initial commit
This commit is contained in:
1629
client/core/workspace/ProjectWorkspace.cpp
Normal file
1629
client/core/workspace/ProjectWorkspace.cpp
Normal file
File diff suppressed because it is too large
Load Diff
148
client/core/workspace/ProjectWorkspace.h
Normal file
148
client/core/workspace/ProjectWorkspace.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include "domain/Project.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QJsonObject>
|
||||
#include <QRect>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVector>
|
||||
|
||||
namespace core {
|
||||
|
||||
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 = 2;
|
||||
|
||||
ProjectWorkspace() = default;
|
||||
|
||||
// 新建项目:
|
||||
// - 传入的 parentDir 是“父目录”(你在文件对话框中选择的目录)
|
||||
// - 会在 parentDir 下创建一个新的项目目录(默认使用项目名做文件夹名;若重名会自动加后缀)
|
||||
// - 项目结构为(v2):
|
||||
// <projectDir>/project.json (索引:背景/深度路径 + 实体 id 与 .hfe 路径)
|
||||
// <projectDir>/assets/background.png
|
||||
// <projectDir>/assets/entities/*.png / *.hfe
|
||||
bool createNew(const QString& parentDir, const QString& name, const QString& backgroundImageSourcePath);
|
||||
bool createNew(const QString& parentDir, const QString& name, const QString& backgroundImageSourcePath,
|
||||
const QRect& cropRectInSourceImage);
|
||||
bool openExisting(const QString& projectDir);
|
||||
void close();
|
||||
|
||||
bool isOpen() const { return !m_projectDir.isEmpty(); }
|
||||
const QString& projectDir() const { return m_projectDir; }
|
||||
QString indexFilePath() const;
|
||||
QString assetsDirPath() const;
|
||||
bool hasBackground() const { return !m_project.backgroundImagePath().isEmpty(); }
|
||||
QString backgroundAbsolutePath() const;
|
||||
bool backgroundVisible() const { return m_project.backgroundVisible(); }
|
||||
bool setBackgroundVisible(bool on);
|
||||
|
||||
bool hasDepth() const;
|
||||
QString depthAbsolutePath() const;
|
||||
|
||||
// 写入 project.json 的 name 字段(可 undo)
|
||||
bool setProjectTitle(const QString& title);
|
||||
|
||||
Project& project() { return m_project; }
|
||||
const Project& project() const { return m_project; }
|
||||
|
||||
// 历史操作(最多 30 步),类似 Blender:维护 undo/redo 栈
|
||||
bool canUndo() const;
|
||||
bool canRedo() const;
|
||||
bool undo();
|
||||
bool redo();
|
||||
QStringList historyLabelsNewestFirst() const;
|
||||
|
||||
// 追加一次“导入并设置背景图”操作:把图片拷贝进 assets/,并作为背景写入项目(会进入历史)。
|
||||
bool importBackgroundImage(const QString& backgroundImageSourcePath);
|
||||
bool importBackgroundImage(const QString& backgroundImageSourcePath, const QRect& cropRectInSourceImage);
|
||||
|
||||
// 计算并写入假深度图:assets/depth.png,同时更新 project.json(depthComputed/depthMapPath)。
|
||||
bool computeFakeDepthForProject();
|
||||
|
||||
// 从后端计算深度并落盘:assets/depth.png,同时更新 project.json(depthComputed/depthMapPath)。
|
||||
// - serverBaseUrl 为空时:优先读环境变量 MODEL_SERVER_URL,否则默认 http://127.0.0.1:8000
|
||||
// - outError 可选:返回失败原因
|
||||
bool computeDepthForProjectFromServer(const QString& serverBaseUrl, QString* outError = nullptr, int timeoutMs = 30000);
|
||||
|
||||
// 直接保存深度图(PNG bytes)到 assets/depth.png,并更新 project.json。
|
||||
bool saveDepthMapPngBytes(const QByteArray& pngBytes, QString* outError = nullptr);
|
||||
|
||||
const QVector<Project::Entity>& entities() const { return m_project.entities(); }
|
||||
bool addEntity(const Project::Entity& entity, const QImage& image);
|
||||
bool setEntityVisible(const QString& id, bool on);
|
||||
bool setEntityDisplayName(const QString& id, const QString& displayName);
|
||||
bool setEntityUserScale(const QString& id, double userScale);
|
||||
// 将多边形质心平移到 targetCentroidWorld(整体平移);sTotal 须与画布一致
|
||||
bool moveEntityCentroidTo(const QString& id, int frame, const QPointF& targetCentroidWorld, double sTotal,
|
||||
bool autoKeyLocation);
|
||||
// 在保持外形不变的前提下移动枢轴点;sTotal 须与画布一致(距离缩放×整体缩放)
|
||||
bool reanchorEntityPivot(const QString& id, int frame, const QPointF& newPivotWorld, double sTotal);
|
||||
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 setEntityImageFrame(const QString& id, int frame, const QImage& image, QString* outRelPath = nullptr);
|
||||
bool removeEntityLocationKey(const QString& id, int frame);
|
||||
bool removeEntityDepthScaleKey(const QString& id, int frame);
|
||||
bool removeEntityUserScaleKey(const QString& id, int frame);
|
||||
bool removeEntityImageFrame(const QString& id, int frame);
|
||||
|
||||
private:
|
||||
bool writeIndexJson();
|
||||
bool readIndexJson(const QString& indexPath);
|
||||
|
||||
bool syncEntityPayloadsToDisk();
|
||||
bool hydrateEntityPayloadsFromDisk();
|
||||
void loadV1LegacyAnimationSidecars();
|
||||
|
||||
static QJsonObject projectToJson(const Project& project);
|
||||
static bool projectFromJson(const QJsonObject& root, Project& outProject, int* outFileVersion);
|
||||
static QString asRelativeUnderProject(const QString& relativePath);
|
||||
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);
|
||||
|
||||
struct Operation {
|
||||
enum class Type { ImportBackground, SetEntities, SetProjectTitle };
|
||||
Type type {Type::ImportBackground};
|
||||
QString label;
|
||||
QString beforeBackgroundPath;
|
||||
QString afterBackgroundPath;
|
||||
QVector<Project::Entity> beforeEntities;
|
||||
QVector<Project::Entity> afterEntities;
|
||||
QString beforeProjectTitle;
|
||||
QString afterProjectTitle;
|
||||
};
|
||||
|
||||
static constexpr int kMaxHistorySteps = 30;
|
||||
void pushOperation(const Operation& op);
|
||||
|
||||
bool applyBackgroundPath(const QString& relativePath, bool recordHistory, const QString& label);
|
||||
bool applyEntities(const QVector<Project::Entity>& entities, 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;
|
||||
bool writeEntityImage(const QString& entityId, const QImage& image, QString& outRelPath);
|
||||
bool writeEntityFrameImage(const QString& entityId, int frame, const QImage& image, QString& outRelPath);
|
||||
|
||||
private:
|
||||
QString m_projectDir;
|
||||
Project m_project;
|
||||
|
||||
QVector<Operation> m_undoStack;
|
||||
QVector<Operation> m_redoStack;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
Reference in New Issue
Block a user