update
This commit is contained in:
@@ -5,19 +5,24 @@
|
||||
#include <QImage>
|
||||
#include <QJsonObject>
|
||||
#include <QRect>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
#include <QPoint>
|
||||
|
||||
namespace core {
|
||||
namespace persistence {
|
||||
class AsyncProjectWriter;
|
||||
}
|
||||
|
||||
class ProjectWorkspace {
|
||||
public:
|
||||
static constexpr const char* kProjectIndexFileName = "project.json";
|
||||
static constexpr const char* kAssetsDirName = "assets";
|
||||
// 写入 project.json 的 version 字段;仍可读 version 1(内嵌实体 + 可选 .anim)。
|
||||
static constexpr int kProjectIndexFormatVersion = 4;
|
||||
static constexpr int kProjectIndexFormatVersion = 7;
|
||||
|
||||
ProjectWorkspace() = default;
|
||||
|
||||
@@ -58,6 +63,7 @@ public:
|
||||
|
||||
// 仅写盘(project.json + payload 同步)。动画 UI 直接编辑 Project 后可调用此函数持久化。
|
||||
bool save();
|
||||
bool saveAll();
|
||||
|
||||
// 历史操作(最多 30 步),类似 Blender:维护 undo/redo 栈
|
||||
bool canUndo() const;
|
||||
@@ -92,6 +98,7 @@ public:
|
||||
bool setToolFontPx(const QString& id, int fontPx);
|
||||
bool setToolAlign(const QString& id, core::Project::Tool::TextAlign align);
|
||||
bool setToolVisibilityKey(const QString& id, int frame, bool visible);
|
||||
bool setToolPriorityKey(const QString& id, int frame, int priority);
|
||||
bool removeToolVisibilityKey(const QString& id, int frame);
|
||||
bool setToolParent(const QString& id, const QString& parentId, const QPointF& parentOffsetWorld);
|
||||
bool moveToolBy(const QString& id, const QPointF& delta, int currentFrame, bool autoKeyLocation);
|
||||
@@ -113,9 +120,9 @@ public:
|
||||
// 复制背景其他区域填充黑洞(sourceOffsetPx 以黑洞包围盒左上角为基准偏移)
|
||||
bool resolveBlackholeByCopyBackground(const QString& id, const QPoint& sourceOffsetPx,
|
||||
bool hideBlackholeAfterFill);
|
||||
// 使用模型补全后的结果写回背景(patchedBackground 已包含补全贴合后的完整背景图像)
|
||||
bool resolveBlackholeByModelInpaint(const QString& id, const QImage& patchedBackground,
|
||||
bool hideBlackholeAfterFill);
|
||||
// 将模型补全的裁剪块保存为覆盖层并叠加显示,不修改 background 原图
|
||||
bool resolveBlackholeByModelInpaint(const QString& id, const QImage& overlayPatch,
|
||||
const QRect& overlayRectInBackground, bool hideBlackholeAfterFill);
|
||||
bool setEntityVisibilityKey(const QString& id, int frame, bool visible);
|
||||
bool removeEntityVisibilityKey(const QString& id, int frame);
|
||||
bool setEntityDisplayName(const QString& id, const QString& displayName);
|
||||
@@ -126,17 +133,42 @@ public:
|
||||
// 将多边形质心平移到 targetCentroidWorld(整体平移);sTotal 须与画布一致
|
||||
bool moveEntityCentroidTo(const QString& id, int frame, const QPointF& targetCentroidWorld, double sTotal,
|
||||
bool autoKeyLocation);
|
||||
/// 仅适用于有父实体:设置相对父形心的偏移(静态 parentOffsetWorld)。
|
||||
bool setEntityParentOffsetWorld(const QString& id, const QPointF& offsetWorld);
|
||||
// 在保持外形不变的前提下移动枢轴点;sTotal 须与画布一致(距离缩放×整体缩放)
|
||||
bool reanchorEntityPivot(const QString& id, int frame, const QPointF& newPivotWorld, double sTotal);
|
||||
/// 将各实体 origin 与多边形形心对齐(外形不变);打开工程时迁移旧数据。
|
||||
bool normalizeAllEntityOriginsToCentroids(int frame);
|
||||
/// 当前帧下实体多边形形心的世界坐标(与画布深度/距离缩放采样点一致);工具 id 则返回其原点。
|
||||
[[nodiscard]] QPointF entityCentroidWorldAtFrame(const QString& id, int frame) const;
|
||||
bool reorderEntitiesById(const QStringList& idsInOrder);
|
||||
// currentFrame:自动关键帧时写入位置曲线;autoKeyLocation 为 false 时忽略。
|
||||
bool moveEntityBy(const QString& id, const QPointF& delta, int currentFrame, bool autoKeyLocation);
|
||||
bool setEntityLocationKey(const QString& id, int frame, const QPointF& originWorld);
|
||||
bool setEntityDepthScaleKey(const QString& id, int frame, double value01);
|
||||
bool setEntityUserScaleKey(const QString& id, int frame, double userScale);
|
||||
bool setEntityPriorityKey(const QString& id, int frame, int priority);
|
||||
bool setEntityDefaultImage(const QString& id, const QImage& image, QString* outRelPath = nullptr);
|
||||
bool setEntityImageFrame(const QString& id, int frame, const QImage& image, QString* outRelPath = nullptr);
|
||||
// 仅更新 imageFrames 中某帧的图像路径(不读图、不写盘),用于高性能地“切断”Hold 区间
|
||||
bool setEntityImageFramePath(const QString& id, int frame, const QString& relativePath);
|
||||
QVector<Project::Entity::ImageFrame> entitySpriteFrames(const QString& id) const;
|
||||
QString entityImagePathAt(const QString& id, int frame) const;
|
||||
QByteArray entityImagePngAt(const QString& id, int frame) const;
|
||||
bool clearEntityImageFramesInRange(const QString& id, int startFrame, int endFrame);
|
||||
int activeAnimationLengthFrames() const;
|
||||
int activeAnimationFps() const;
|
||||
int animationLengthFramesForId(const QString& animationId) const;
|
||||
int animationFpsForId(const QString& animationId) const;
|
||||
bool animationLoopsForId(const QString& animationId) const;
|
||||
|
||||
bool createAnimationScheme(const QString& displayName, int lengthFrames, bool loop, int fps,
|
||||
QString* outNewId = nullptr);
|
||||
bool updateAnimationSchemeSettings(const QString& id, const QString& displayName, int lengthFrames, bool loop,
|
||||
int fps);
|
||||
|
||||
bool applyPresentationHotspots(const QVector<Project::PresentationHotspot>& hotspots, bool recordHistory,
|
||||
const QString& label);
|
||||
bool removeEntityLocationKey(const QString& id, int frame);
|
||||
bool removeEntityDepthScaleKey(const QString& id, int frame);
|
||||
bool removeEntityUserScaleKey(const QString& id, int frame);
|
||||
@@ -152,8 +184,9 @@ private:
|
||||
bool readIndexJson(const QString& indexPath);
|
||||
|
||||
bool syncEntityPayloadsToDisk();
|
||||
bool syncAnimationPayloadsToDisk();
|
||||
bool hydrateEntityPayloadsFromDisk();
|
||||
void loadV1LegacyAnimationSidecars();
|
||||
bool hydrateAnimationPayloadsFromDisk();
|
||||
bool writeIndexJsonWithoutPayloadSync();
|
||||
bool saveSingleEntityPayload(Project::Entity& entity);
|
||||
|
||||
@@ -163,7 +196,6 @@ private:
|
||||
static QString fileSuffixWithDot(const QString& path);
|
||||
static QString asOptionalRelativeUnderProject(const QString& relativePath);
|
||||
static QJsonObject entityToJson(const Project::Entity& e);
|
||||
static bool entityFromJsonV1(const QJsonObject& o, Project::Entity& out);
|
||||
static bool entityStubFromJsonV2(const QJsonObject& o, Project::Entity& out);
|
||||
static QJsonObject toolToJson(const Project::Tool& t);
|
||||
static bool toolFromJsonV2(const QJsonObject& o, Project::Tool& out);
|
||||
@@ -174,7 +206,16 @@ private:
|
||||
bool ensureDefaultCameraIfMissing(bool* outAdded = nullptr);
|
||||
|
||||
struct Operation {
|
||||
enum class Type { ImportBackground, SetEntities, SetTools, SetCameras, SetProjectTitle, SetProjectFrameRange };
|
||||
enum class Type {
|
||||
ImportBackground,
|
||||
SetEntities,
|
||||
SetTools,
|
||||
SetCameras,
|
||||
SetAnimations,
|
||||
SetPresentationHotspots,
|
||||
SetProjectTitle,
|
||||
SetProjectFrameRange
|
||||
};
|
||||
Type type {Type::ImportBackground};
|
||||
QString label;
|
||||
QString beforeBackgroundPath;
|
||||
@@ -185,6 +226,14 @@ private:
|
||||
QVector<Project::Tool> afterTools;
|
||||
QVector<Project::Camera> beforeCameras;
|
||||
QVector<Project::Camera> afterCameras;
|
||||
QVector<Project::Animation> beforeAnimations;
|
||||
QVector<Project::Animation> afterAnimations;
|
||||
bool animBundlesPresentationHotspots = false;
|
||||
QVector<Project::PresentationHotspot> animSnapshotBeforePresentationHotspots;
|
||||
QVector<Project::PresentationHotspot> animSnapshotAfterPresentationHotspots;
|
||||
|
||||
QVector<Project::PresentationHotspot> beforePresentationHotspots;
|
||||
QVector<Project::PresentationHotspot> afterPresentationHotspots;
|
||||
QString beforeProjectTitle;
|
||||
QString afterProjectTitle;
|
||||
int beforeFrameStart = 0;
|
||||
@@ -200,19 +249,36 @@ private:
|
||||
bool applyEntities(const QVector<Project::Entity>& entities, bool recordHistory, const QString& label);
|
||||
bool applyTools(const QVector<Project::Tool>& tools, bool recordHistory, const QString& label);
|
||||
bool applyCameras(const QVector<Project::Camera>& cameras, bool recordHistory, const QString& label);
|
||||
bool applyAnimations(const QVector<Project::Animation>& animations, bool recordHistory, const QString& label);
|
||||
QString copyIntoAssetsAsBackground(const QString& sourceFilePath, const QRect& cropRectInSourceImage);
|
||||
bool writeDepthMap(const QImage& depth8);
|
||||
bool writeDepthMapBytes(const QByteArray& pngBytes);
|
||||
QString ensureEntitiesDir() const;
|
||||
QString ensureAnimationsDir() const;
|
||||
bool writeEntityImage(const QString& entityId, const QImage& image, QString& outRelPath);
|
||||
bool writeEntityFrameImage(const QString& entityId, int frame, const QImage& image, QString& outRelPath);
|
||||
QString defaultAnimationPayloadPath(const QString& animationId) const;
|
||||
void markIndexDirty();
|
||||
/// snapshot:在尚未写入 m_project 前(例如新实体)必须传入,否则异步 bundle 写会漏队列。
|
||||
void markEntityDirty(const QString& id, const Project::Entity* snapshot = nullptr);
|
||||
void markAnimationDirty(const QString& id);
|
||||
void markAllEntitiesDirty();
|
||||
void markAllAnimationsDirty();
|
||||
void schedulePayloadSync();
|
||||
bool syncPayloadsToDiskOnce();
|
||||
|
||||
private:
|
||||
QString m_projectDir;
|
||||
Project m_project;
|
||||
|
||||
persistence::AsyncProjectWriter* m_asyncWriter = nullptr;
|
||||
|
||||
QVector<Operation> m_undoStack;
|
||||
QVector<Operation> m_redoStack;
|
||||
QSet<QString> m_dirtyEntityIds;
|
||||
QSet<QString> m_dirtyAnimationIds;
|
||||
bool m_indexDirty = false;
|
||||
QTimer* m_payloadSyncTimer = nullptr;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
Reference in New Issue
Block a user