Files
hfut-bishe/client/core/workspace/ProjectWorkspace.h

155 lines
7.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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.jsondepthComputed/depthMapPath
bool computeFakeDepthForProject();
// 从后端计算深度并落盘assets/depth.png同时更新 project.jsondepthComputed/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);
/// keyframeAtFrame >= 0 时同时写入该帧的 userScale 关键帧(与画布 sampleUserScale 一致)
bool setEntityUserScale(const QString& id, double userScale, int keyframeAtFrame = -1);
// 将多边形质心平移到 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);
bool setEntityIntroContent(const QString& id, const EntityIntroContent& intro);
/// 将外部图片拷贝到 assets/entities/ 并返回相对项目根的路径
bool importEntityIntroImageFromFile(const QString& id, const QString& absoluteImagePath,
QString* outRelativePath = nullptr);
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