#include "library/EntityJson.h" #include #include #include namespace core::library { namespace { QJsonArray pointToJson(const QPointF& p) { return QJsonArray{p.x(), p.y()}; } bool pointFromJson(const QJsonValue& v, QPointF& out) { if (!v.isArray()) { return false; } const QJsonArray a = v.toArray(); if (a.size() < 2) { return false; } out = QPointF(a.at(0).toDouble(), a.at(1).toDouble()); return true; } QJsonArray pointsToJson(const QVector& pts) { QJsonArray a; for (const auto& p : pts) { a.append(pointToJson(p)); } return a; } bool pointsFromJson(const QJsonValue& v, QVector& out) { out.clear(); if (!v.isArray()) { return false; } const QJsonArray a = v.toArray(); out.reserve(a.size()); for (const auto& it : a) { QPointF p; if (!pointFromJson(it, p)) { return false; } out.push_back(p); } return true; } template QJsonArray vecToJson(const QVector& v, const std::function& fn) { QJsonArray a; for (const auto& x : v) { a.append(fn(x)); } return a; } } // namespace QJsonObject entityToJson(const core::Project::Entity& e) { QJsonObject o; o.insert(QStringLiteral("id"), e.id); o.insert(QStringLiteral("displayName"), e.displayName); o.insert(QStringLiteral("visible"), e.visible); o.insert(QStringLiteral("polygonLocal"), pointsToJson(e.polygonLocal)); o.insert(QStringLiteral("cutoutPolygonWorld"), pointsToJson(e.cutoutPolygonWorld)); o.insert(QStringLiteral("blackholeId"), e.blackholeId); o.insert(QStringLiteral("blackholeVisible"), e.blackholeVisible); o.insert(QStringLiteral("blackholeResolvedBy"), e.blackholeResolvedBy); o.insert(QStringLiteral("originWorld"), pointToJson(e.originWorld)); o.insert(QStringLiteral("depth"), e.depth); o.insert(QStringLiteral("imagePath"), e.imagePath); o.insert(QStringLiteral("imageTopLeftWorld"), pointToJson(e.imageTopLeftWorld)); o.insert(QStringLiteral("userScale"), e.userScale); o.insert(QStringLiteral("distanceScaleCalibMult"), e.distanceScaleCalibMult); o.insert(QStringLiteral("ignoreDistanceScale"), e.ignoreDistanceScale); o.insert(QStringLiteral("parentId"), e.parentId); o.insert(QStringLiteral("parentOffsetWorld"), pointToJson(e.parentOffsetWorld)); o.insert(QStringLiteral("entityPayloadPath"), e.entityPayloadPath); o.insert(QStringLiteral("legacyAnimSidecarPath"), e.legacyAnimSidecarPath); o.insert(QStringLiteral("locationKeys"), vecToJson( e.locationKeys, [](const core::Project::Entity::KeyframeVec2& k) { QJsonObject ko; ko.insert(QStringLiteral("frame"), k.frame); ko.insert(QStringLiteral("value"), pointToJson(k.value)); return ko; })); o.insert(QStringLiteral("depthScaleKeys"), vecToJson( e.depthScaleKeys, [](const core::Project::Entity::KeyframeFloat01& k) { QJsonObject ko; ko.insert(QStringLiteral("frame"), k.frame); ko.insert(QStringLiteral("value"), k.value); return ko; })); o.insert(QStringLiteral("userScaleKeys"), vecToJson( e.userScaleKeys, [](const core::Project::Entity::KeyframeDouble& k) { QJsonObject ko; ko.insert(QStringLiteral("frame"), k.frame); ko.insert(QStringLiteral("value"), k.value); return ko; })); o.insert(QStringLiteral("imageFrames"), vecToJson( e.imageFrames, [](const core::Project::Entity::ImageFrame& k) { QJsonObject ko; ko.insert(QStringLiteral("frame"), k.frame); ko.insert(QStringLiteral("imagePath"), k.imagePath); return ko; })); o.insert(QStringLiteral("visibilityKeys"), vecToJson( e.visibilityKeys, [](const core::Project::ToolKeyframeBool& k) { QJsonObject ko; ko.insert(QStringLiteral("frame"), k.frame); ko.insert(QStringLiteral("value"), k.value); return ko; })); { QJsonObject intro; intro.insert(QStringLiteral("title"), e.intro.title); intro.insert(QStringLiteral("bodyText"), e.intro.bodyText); QJsonArray imgs; for (const auto& p : e.intro.imagePathsRelative) { imgs.append(p); } intro.insert(QStringLiteral("imagePathsRelative"), imgs); intro.insert(QStringLiteral("videoPathRelative"), e.intro.videoPathRelative); o.insert(QStringLiteral("intro"), intro); } return o; } bool entityFromJson(const QJsonObject& o, core::Project::Entity& out) { core::Project::Entity e; e.id = o.value(QStringLiteral("id")).toString(); e.displayName = o.value(QStringLiteral("displayName")).toString(); e.visible = o.value(QStringLiteral("visible")).toBool(true); if (!pointsFromJson(o.value(QStringLiteral("polygonLocal")), e.polygonLocal)) { return false; } if (!pointsFromJson(o.value(QStringLiteral("cutoutPolygonWorld")), e.cutoutPolygonWorld)) { // cutout 允许不存在:按空处理 e.cutoutPolygonWorld.clear(); } e.blackholeId = o.value(QStringLiteral("blackholeId")).toString(); if (e.blackholeId.isEmpty() && !e.id.isEmpty()) { e.blackholeId = QStringLiteral("blackhole-%1").arg(e.id); } e.blackholeVisible = o.value(QStringLiteral("blackholeVisible")).toBool(true); e.blackholeResolvedBy = o.value(QStringLiteral("blackholeResolvedBy")).toString(); { QPointF p; if (!pointFromJson(o.value(QStringLiteral("originWorld")), p)) { p = QPointF(); } e.originWorld = p; } e.depth = o.value(QStringLiteral("depth")).toInt(0); e.imagePath = o.value(QStringLiteral("imagePath")).toString(); { QPointF p; if (!pointFromJson(o.value(QStringLiteral("imageTopLeftWorld")), p)) { p = QPointF(); } e.imageTopLeftWorld = p; } e.userScale = o.value(QStringLiteral("userScale")).toDouble(1.0); e.distanceScaleCalibMult = o.value(QStringLiteral("distanceScaleCalibMult")).toDouble(0.0); e.ignoreDistanceScale = o.value(QStringLiteral("ignoreDistanceScale")).toBool(false); e.parentId = o.value(QStringLiteral("parentId")).toString(); { QPointF p; if (!pointFromJson(o.value(QStringLiteral("parentOffsetWorld")), p)) { p = QPointF(); } e.parentOffsetWorld = p; } e.entityPayloadPath = o.value(QStringLiteral("entityPayloadPath")).toString(); e.legacyAnimSidecarPath = o.value(QStringLiteral("legacyAnimSidecarPath")).toString(); auto parseKeyframesVec2 = [&](const QString& key, QVector& dst) -> bool { dst.clear(); const QJsonValue v = o.value(key); if (!v.isArray()) { return true; } const QJsonArray a = v.toArray(); dst.reserve(a.size()); for (const auto& it : a) { if (!it.isObject()) return false; const QJsonObject ko = it.toObject(); core::Project::Entity::KeyframeVec2 k; k.frame = ko.value(QStringLiteral("frame")).toInt(0); QPointF pv; if (!pointFromJson(ko.value(QStringLiteral("value")), pv)) { return false; } k.value = pv; dst.push_back(k); } return true; }; auto parseKeyframesFloat01 = [&](const QString& key, QVector& dst) -> bool { dst.clear(); const QJsonValue v = o.value(key); if (!v.isArray()) { return true; } const QJsonArray a = v.toArray(); dst.reserve(a.size()); for (const auto& it : a) { if (!it.isObject()) return false; const QJsonObject ko = it.toObject(); core::Project::Entity::KeyframeFloat01 k; k.frame = ko.value(QStringLiteral("frame")).toInt(0); k.value = ko.value(QStringLiteral("value")).toDouble(0.5); dst.push_back(k); } return true; }; auto parseKeyframesDouble = [&](const QString& key, QVector& dst) -> bool { dst.clear(); const QJsonValue v = o.value(key); if (!v.isArray()) { return true; } const QJsonArray a = v.toArray(); dst.reserve(a.size()); for (const auto& it : a) { if (!it.isObject()) return false; const QJsonObject ko = it.toObject(); core::Project::Entity::KeyframeDouble k; k.frame = ko.value(QStringLiteral("frame")).toInt(0); k.value = ko.value(QStringLiteral("value")).toDouble(1.0); dst.push_back(k); } return true; }; auto parseImageFrames = [&](const QString& key, QVector& dst) -> bool { dst.clear(); const QJsonValue v = o.value(key); if (!v.isArray()) { return true; } const QJsonArray a = v.toArray(); dst.reserve(a.size()); for (const auto& it : a) { if (!it.isObject()) return false; const QJsonObject ko = it.toObject(); core::Project::Entity::ImageFrame k; k.frame = ko.value(QStringLiteral("frame")).toInt(0); k.imagePath = ko.value(QStringLiteral("imagePath")).toString(); dst.push_back(k); } return true; }; if (!parseKeyframesVec2(QStringLiteral("locationKeys"), e.locationKeys)) return false; if (!parseKeyframesFloat01(QStringLiteral("depthScaleKeys"), e.depthScaleKeys)) return false; if (!parseKeyframesDouble(QStringLiteral("userScaleKeys"), e.userScaleKeys)) return false; if (!parseImageFrames(QStringLiteral("imageFrames"), e.imageFrames)) return false; // visibilityKeys:可缺省(默认永远可见) e.visibilityKeys.clear(); if (o.value(QStringLiteral("visibilityKeys")).isArray()) { const QJsonArray a = o.value(QStringLiteral("visibilityKeys")).toArray(); e.visibilityKeys.reserve(a.size()); for (const auto& it : a) { if (!it.isObject()) return false; const QJsonObject ko = it.toObject(); core::Project::ToolKeyframeBool k; k.frame = ko.value(QStringLiteral("frame")).toInt(0); k.value = ko.value(QStringLiteral("value")).toBool(true); e.visibilityKeys.push_back(k); } } if (o.contains(QStringLiteral("intro")) && o.value(QStringLiteral("intro")).isObject()) { const QJsonObject intro = o.value(QStringLiteral("intro")).toObject(); e.intro.title = intro.value(QStringLiteral("title")).toString(); e.intro.bodyText = intro.value(QStringLiteral("bodyText")).toString(); e.intro.videoPathRelative = intro.value(QStringLiteral("videoPathRelative")).toString(); e.intro.imagePathsRelative.clear(); if (intro.value(QStringLiteral("imagePathsRelative")).isArray()) { const QJsonArray imgs = intro.value(QStringLiteral("imagePathsRelative")).toArray(); e.intro.imagePathsRelative.reserve(imgs.size()); for (const auto& iv : imgs) { e.intro.imagePathsRelative.push_back(iv.toString()); } } } out = e; return true; } } // namespace core::library