导出数据转spine
This commit is contained in:
1 parent
d258d4f435
commit
3a7c0b4092
65 files changed
+6000
No files matched your search
@@ -0,0 +1,173 @@
|
||||
#include "atlas/AtlasParser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace dts {
|
||||
namespace {
|
||||
|
||||
std::string trim(std::string value) {
|
||||
const auto isSpace = [](unsigned char character) { return std::isspace(character) != 0; };
|
||||
value.erase(value.begin(), std::find_if_not(value.begin(), value.end(), isSpace));
|
||||
value.erase(std::find_if_not(value.rbegin(), value.rend(), isSpace).base(), value.end());
|
||||
return value;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitValues(const std::string& value) {
|
||||
std::vector<std::string> values;
|
||||
std::stringstream stream(value);
|
||||
std::string item;
|
||||
while (std::getline(stream, item, ',')) {
|
||||
values.push_back(trim(item));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
std::optional<std::vector<int>> parseIntegers(const std::string& value) {
|
||||
std::vector<int> integers;
|
||||
for (const auto& item : splitValues(value)) {
|
||||
int number = 0;
|
||||
const auto result = std::from_chars(item.data(), item.data() + item.size(), number);
|
||||
if (result.ec != std::errc{} || result.ptr != item.data() + item.size()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
integers.push_back(number);
|
||||
}
|
||||
return integers;
|
||||
}
|
||||
|
||||
bool isPageKey(const std::string& key) {
|
||||
return key == "size" || key == "format" || key == "filter" || key == "repeat"
|
||||
|| key == "pma" || key == "scale";
|
||||
}
|
||||
|
||||
bool parseBoolean(const std::string& value) {
|
||||
return value == "true" || value == "yes" || value == "1";
|
||||
}
|
||||
|
||||
void applyPageProperty(AtlasPage& page, const std::string& key, const std::string& value) {
|
||||
if (key == "size") {
|
||||
page.size = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "format") {
|
||||
page.format = value;
|
||||
} else if (key == "filter") {
|
||||
page.filter = value;
|
||||
} else if (key == "repeat") {
|
||||
page.repeat = value;
|
||||
} else if (key == "pma") {
|
||||
page.premultipliedAlpha = parseBoolean(value);
|
||||
} else if (key == "scale") {
|
||||
try {
|
||||
page.scale = std::stod(value);
|
||||
} catch (...) {
|
||||
page.scale.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void applyRegionProperty(AtlasRegion& region, const std::string& key, const std::string& value) {
|
||||
if (key == "rotate") {
|
||||
region.rotated = parseBoolean(value) || value == "90" || value == "270";
|
||||
} else if (key == "bounds") {
|
||||
region.bounds = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "xy") {
|
||||
region.position = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "size") {
|
||||
region.size = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "orig") {
|
||||
region.originalSize = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "offset" || key == "offsets") {
|
||||
region.offsets = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "split") {
|
||||
region.split = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "pad") {
|
||||
region.pad = parseIntegers(value).value_or(std::vector<int>{});
|
||||
} else if (key == "index") {
|
||||
const auto values = parseIntegers(value);
|
||||
if (values && !values->empty()) {
|
||||
region.index = values->front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtlasParseResult AtlasParser::parseFile(const std::filesystem::path& path) const {
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
if (!input) {
|
||||
return {std::nullopt, "Atlas file could not be opened.", 0};
|
||||
}
|
||||
std::ostringstream contents;
|
||||
contents << input.rdbuf();
|
||||
return parseText(contents.str());
|
||||
}
|
||||
|
||||
AtlasParseResult AtlasParser::parseText(const std::string& text) const {
|
||||
AtlasDocument document;
|
||||
AtlasPage* page = nullptr;
|
||||
AtlasRegion* region = nullptr;
|
||||
bool expectPage = true;
|
||||
std::istringstream input(text);
|
||||
std::string rawLine;
|
||||
std::size_t lineNumber = 0;
|
||||
|
||||
while (std::getline(input, rawLine)) {
|
||||
++lineNumber;
|
||||
if (!rawLine.empty() && rawLine.back() == '\r') {
|
||||
rawLine.pop_back();
|
||||
}
|
||||
const auto line = trim(rawLine);
|
||||
if (line.empty()) {
|
||||
expectPage = true;
|
||||
page = nullptr;
|
||||
region = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto colon = line.find(':');
|
||||
if (colon == std::string::npos) {
|
||||
if (expectPage || page == nullptr) {
|
||||
AtlasPage nextPage;
|
||||
nextPage.imagePath = std::filesystem::path(line);
|
||||
document.pages.push_back(std::move(nextPage));
|
||||
page = &document.pages.back();
|
||||
region = nullptr;
|
||||
expectPage = false;
|
||||
} else {
|
||||
AtlasRegion nextRegion;
|
||||
nextRegion.name = line;
|
||||
page->regions.push_back(std::move(nextRegion));
|
||||
region = &page->regions.back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
return {std::nullopt, "Atlas property appears before a page name.", lineNumber};
|
||||
}
|
||||
const auto key = trim(line.substr(0, colon));
|
||||
const auto value = trim(line.substr(colon + 1));
|
||||
if (!region && isPageKey(key)) {
|
||||
applyPageProperty(*page, key, value);
|
||||
} else if (region) {
|
||||
applyRegionProperty(*region, key, value);
|
||||
} else {
|
||||
document.warnings.push_back("Unknown page property at line " + std::to_string(lineNumber));
|
||||
}
|
||||
}
|
||||
|
||||
if (document.pages.empty()) {
|
||||
return {std::nullopt, "Atlas does not contain any pages.", 0};
|
||||
}
|
||||
for (const auto& atlasPage : document.pages) {
|
||||
if (atlasPage.imagePath.empty()) {
|
||||
return {std::nullopt, "Atlas contains an empty page name.", 0};
|
||||
}
|
||||
}
|
||||
return {std::move(document), {}, 0};
|
||||
}
|
||||
|
||||
} // namespace dts
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace dts {
|
||||
|
||||
struct AtlasRegion {
|
||||
std::string name;
|
||||
bool rotated = false;
|
||||
std::vector<int> bounds;
|
||||
std::vector<int> position;
|
||||
std::vector<int> size;
|
||||
std::vector<int> originalSize;
|
||||
std::vector<int> offsets;
|
||||
std::vector<int> split;
|
||||
std::vector<int> pad;
|
||||
std::optional<int> index;
|
||||
};
|
||||
|
||||
struct AtlasPage {
|
||||
std::filesystem::path imagePath;
|
||||
std::vector<int> size;
|
||||
std::string format;
|
||||
std::string filter;
|
||||
std::string repeat;
|
||||
std::optional<bool> premultipliedAlpha;
|
||||
std::optional<double> scale;
|
||||
std::vector<AtlasRegion> regions;
|
||||
};
|
||||
|
||||
struct AtlasDocument {
|
||||
std::vector<AtlasPage> pages;
|
||||
std::vector<std::string> warnings;
|
||||
};
|
||||
|
||||
struct AtlasParseResult {
|
||||
std::optional<AtlasDocument> document;
|
||||
std::string error;
|
||||
std::size_t errorLine = 0;
|
||||
};
|
||||
|
||||
class AtlasParser {
|
||||
public:
|
||||
[[nodiscard]] AtlasParseResult parseFile(const std::filesystem::path& path) const;
|
||||
[[nodiscard]] AtlasParseResult parseText(const std::string& text) const;
|
||||
};
|
||||
|
||||
} // namespace dts
|
||||
Reference in new issue
Block a user