#pragma once #include "core/domain/Project.h" #include #include #include #include #include #include #include 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 setEntities(const QVector& 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); 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; private: void ensurePixmapLoaded() const; void invalidatePixmap(); void updateCursor(); QPointF viewToWorld(const QPointF& v) const; QPointF worldToView(const QPointF& w) const; QRectF worldRectOfBackground() const; private: struct Entity { QString id; QRectF rect; // world 坐标(用于拖拽与约束) QVector polygonWorld; // 非空则使用 polygon QPainterPath pathWorld; // polygonWorld 对应的 world 路径(缓存,避免每帧重建) QVector cutoutPolygonWorld; QColor color; // 实体独立信息: int depth = 0; // 0..255,来自划分区域平均深度 QImage image; // 抠图后的实体图像(带透明) QPointF imageTopLeft; // image 对应的 world 左上角 double visualScale = 1.0; // 实体在 world 坐标下的缩放(用于贴图绘制) double userScale = 1.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 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 m_entities; QVector m_strokeWorld; int m_currentFrame = 0; };