导出数据转spine

This commit is contained in:
tianmo committed 2026-09-07 21:29:15 +08:00
1 parent d258d4f435
commit 3a7c0b4092
65 files changed
+6000

No files matched your search

+173
View File
@@ -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
+52
View File
@@ -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