Files
hfut-bishe/client/gui/editor/EditorCanvas.h

214 lines
8.2 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 "core/domain/Project.h"
#include <QPixmap>
#include <QPointF>
#include <QImage>
#include <QPainterPath>
#include <QVector>
#include <QWidget>
#include <QElapsedTimer>
#include <QTimer>
class QKeyEvent;
class EditorCanvas final : public QWidget {
Q_OBJECT
public:
enum class Tool { Move, Zoom, CreateEntity };
Q_ENUM(Tool)
explicit EditorCanvas(QWidget* parent = nullptr);
void setBackgroundImagePath(const QString& absolutePath);
QString backgroundImagePath() const { return m_bgAbsPath; }
void setBackgroundVisible(bool on);
bool backgroundVisible() const { return m_backgroundVisible; }
void setDepthMapPath(const QString& absolutePath);
void setDepthOverlayEnabled(bool on);
bool depthOverlayEnabled() const { return m_depthOverlayEnabled; }
void setTool(Tool tool);
Tool tool() const { return m_tool; }
void resetView();
void zoomToFit();
void setWorldAxesVisible(bool on);
bool worldAxesVisible() const { return m_worldAxesVisible; }
void setAxisLabelsVisible(bool on);
bool axisLabelsVisible() const { return m_axisLabelsVisible; }
void setGizmoLabelsVisible(bool on);
bool gizmoLabelsVisible() const { return m_gizmoLabelsVisible; }
void setGridVisible(bool on);
bool gridVisible() const { return m_gridVisible; }
void setCheckerboardVisible(bool on);
bool checkerboardVisible() const { return m_checkerboardVisible; }
// 预览呈现:完整背景 + 全部实体(忽略显隐开关),隐藏编辑辅助元素,仅可平移/缩放查看
void setPresentationPreviewMode(bool on);
bool presentationPreviewMode() const { return m_presentationPreviewMode; }
/// 退出「点击实体放大」状态并平滑回到进入前的视图(预览模式)
void clearPresentationEntityFocus();
void setEntities(const QVector<core::Project::Entity>& entities, const QString& projectDirAbs);
void setCurrentFrame(int frame);
int currentFrame() const { return m_currentFrame; }
bool isDraggingEntity() const { return m_draggingEntity; }
void selectEntityById(const QString& id);
void clearEntitySelection();
// 与动画求值一致的原点/缩放(用于 K 帧与自动关键帧)
QPointF selectedAnimatedOriginWorld() const;
double selectedDepthScale01() const;
QPointF selectedEntityCentroidWorld() const;
double selectedDistanceScaleMultiplier() const;
double selectedUserScale() const;
double selectedCombinedScale() const;
enum class DragMode { None, Free, AxisX, AxisY };
signals:
void hoveredWorldPosChanged(const QPointF& worldPos);
void hoveredWorldPosDepthChanged(const QPointF& worldPos, int depthZ);
void selectedEntityChanged(bool hasSelection, const QString& id, int depth, const QPointF& originWorld);
void requestAddEntity(const core::Project::Entity& entity, const QImage& image);
void requestMoveEntity(const QString& id, const QPointF& delta);
void entityDragActiveChanged(bool on);
void selectedEntityPreviewChanged(const QString& id, int depth, const QPointF& originWorld);
/// 预览模式下点击实体anchorView 为实体质心在视图中的位置,用于摆放介绍浮层
void presentationEntityIntroRequested(const QString& entityId, QPointF anchorViewPoint);
/// 预览模式下应关闭介绍层空白处轻点、Esc、开始还原缩放时由画布侧触发
void presentationInteractionDismissed();
protected:
void paintEvent(QPaintEvent* e) override;
void resizeEvent(QResizeEvent* e) override;
void mousePressEvent(QMouseEvent* e) override;
void mouseMoveEvent(QMouseEvent* e) override;
void mouseReleaseEvent(QMouseEvent* e) override;
void wheelEvent(QWheelEvent* e) override;
void keyPressEvent(QKeyEvent* e) override;
private:
void ensurePixmapLoaded() const;
void invalidatePixmap();
void updateCursor();
QPointF viewToWorld(const QPointF& v) const;
QPointF worldToView(const QPointF& w) const;
QRectF worldRectOfBackground() const;
void tickPresentationZoomAnimation();
void tickPresentationHoverAnimation();
void beginPresentationZoomTowardEntity(int entityIndex);
void beginPresentationZoomRestore();
void presentationComputeZoomTarget(int entityIndex, QPointF* outPan, qreal* outScale) const;
private:
struct Entity {
QString id;
QRectF rect; // world 坐标(用于拖拽与约束)
QVector<QPointF> polygonWorld; // 非空则使用 polygon
QPainterPath pathWorld; // polygonWorld 对应的 world 路径(缓存,避免每帧重建)
QVector<QPointF> cutoutPolygonWorld;
QColor color;
// 实体独立信息:
int depth = 0; // 0..255,来自划分区域平均深度
QImage image; // 抠图后的实体图像(带透明)
QPointF imageTopLeft; // image 对应的 world 左上角
double visualScale = 1.0; // 实体在 world 坐标下的缩放(用于贴图绘制)
double userScale = 1.0; // 与深度距离缩放相乘
double distanceScaleCalibMult = 0.0; // 与 Project::Entity 一致0=未校准
QPointF animatedOriginWorld;
double animatedDepthScale01 = 0.5;
// 编辑模式下实体被设为隐藏时:不响应点选且不绘制,除非当前选中(便于树选隐藏实体)
bool hiddenInEditMode = false;
};
int hitTestEntity(const QPointF& worldPos) const;
private:
QString m_bgAbsPath;
bool m_backgroundVisible = true;
mutable QPixmap m_bgPixmap;
mutable bool m_pixmapDirty = true;
mutable QImage m_bgImage; // 原背景(用于抠图/填充)
mutable QImage m_bgImageCutout; // 抠图后的背景(实体区域填黑)
mutable bool m_bgImageDirty = true;
mutable bool m_bgCutoutDirty = true;
QString m_depthAbsPath;
mutable QImage m_depthImage8;
mutable bool m_depthDirty = true;
bool m_depthOverlayEnabled = false;
int m_depthOverlayAlpha = 110;
bool m_worldAxesVisible = true;
bool m_axisLabelsVisible = true;
bool m_gizmoLabelsVisible = true;
bool m_gridVisible = true;
bool m_checkerboardVisible = true;
bool m_presentationPreviewMode = false;
Tool m_tool = Tool::Move;
qreal m_scale = 1.0;
QPointF m_pan; // world 原点对应的 view 坐标偏移view = world*scale + pan
bool m_dragging = false;
bool m_draggingEntity = false;
bool m_drawingEntity = false;
QPointF m_lastMouseView;
// 拖动以“实体原点 animatedOriginWorld”为基准避免因缩放导致 rect/topLeft 抖动
QPointF m_entityDragOffsetOriginWorld;
QPointF m_entityDragStartAnimatedOrigin;
// 拖动性能优化:拖动过程中不逐点修改 polygonWorld而是保留基准形状+增量参数,在 paint 时做变换预览
bool m_dragPreviewActive = false;
QVector<QPointF> m_dragPolyBase;
QPainterPath m_dragPathBase;
QPointF m_dragImageTopLeftBase;
QRectF m_dragRectBase;
QPointF m_dragOriginBase;
QPointF m_dragDelta; // 纯平移
QPointF m_dragCentroidBase;
double m_dragScaleBase = 1.0; // 拖动开始时的 visualScale
double m_dragScaleRatio = 1.0; // 相对 m_dragScaleBase 的缩放比(由深度重算驱动)
QElapsedTimer m_previewEmitTimer;
qint64 m_lastPreviewEmitMs = 0;
qint64 m_lastDepthScaleRecalcMs = 0;
int m_selectedEntity = -1;
DragMode m_dragMode = DragMode::None;
QPointF m_dragStartMouseWorld;
QVector<Entity> m_entities;
QVector<QPointF> m_strokeWorld;
int m_currentFrame = 0;
// —— 预览展示:实体悬停动效、点击聚焦缩放 ——
QTimer* m_presZoomTimer = nullptr;
QTimer* m_presHoverTimer = nullptr;
int m_presHoverEntityIndex = -1;
int m_presFocusedEntityIndex = -1;
qreal m_presHoverPhase = 0.0;
QPointF m_presRestorePan;
qreal m_presRestoreScale = 1.0;
QPointF m_presZoomFromPan;
QPointF m_presZoomToPan;
qreal m_presZoomFromScale = 1.0;
qreal m_presZoomToScale = 1.0;
qreal m_presZoomAnimT = 0.0;
bool m_presZoomFinishingRestore = false;
bool m_presBgPanSession = false;
qreal m_presBgDragDist = 0.0;
};