diff --git a/src/app/VtkSceneView.cpp b/src/app/VtkSceneView.cpp index b158762..425d6ba 100644 --- a/src/app/VtkSceneView.cpp +++ b/src/app/VtkSceneView.cpp @@ -253,6 +253,14 @@ void VtkSceneView::setAxes(geopro::controller::AxesMode mode, geopro::controller axesFontSize_ = fontSize; } +void VtkSceneView::setAxesRanges(const geopro::controller::AxisRangeCfg& x, + const geopro::controller::AxisRangeCfg& y, + const geopro::controller::AxisRangeCfg& z) { + axisX_ = x; + axisY_ = y; + axisZ_ = z; +} + void VtkSceneView::applyCameraView(geopro::controller::ViewDir dir) { geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir)); // 设朝向(内部 ResetCamera 含底图) double bounds[6]; @@ -296,6 +304,12 @@ void VtkSceneView::rebuildAxes() { opts.unit = toRenderUnit(axesUnit_); opts.fontSize = axesFontSize_; opts.frame = frame_.get(); + auto toDisp = [](const geopro::controller::AxisRangeCfg& c) { + return geopro::render::AxisDisplay{c.visible, c.customRange, c.min, c.max}; + }; + opts.x = toDisp(axisX_); + opts.y = toDisp(axisY_); + opts.z = toDisp(axisZ_); auto axes = geopro::render::buildAxes(bounds, opts, scene_.renderer()); if (axes) { scene_.addViewProp(axes); diff --git a/src/app/VtkSceneView.hpp b/src/app/VtkSceneView.hpp index 1560f8f..3794c6a 100644 --- a/src/app/VtkSceneView.hpp +++ b/src/app/VtkSceneView.hpp @@ -50,6 +50,9 @@ public: void setSelectedAnomaly(const std::string& anomalyId) override; void setAxes(geopro::controller::AxesMode mode, geopro::controller::AxesUnit unit, int fontSize) override; + void setAxesRanges(const geopro::controller::AxisRangeCfg& x, + const geopro::controller::AxisRangeCfg& y, + const geopro::controller::AxisRangeCfg& z) override; void applyCameraView(geopro::controller::ViewDir dir) override; void zoom(double factor) override; void fitView() override; @@ -108,6 +111,7 @@ private: geopro::controller::AxesMode axesMode_ = geopro::controller::AxesMode::Standard; geopro::controller::AxesUnit axesUnit_ = geopro::controller::AxesUnit::Meter; int axesFontSize_ = 12; + geopro::controller::AxisRangeCfg axisX_, axisY_, axisZ_; // per-axis 可见性 + 自定义范围 // 当前坐标轴 prop:render 可能多次调用 rebuildAxes(rebuild 末尾 + 异步回灌), // 持引用以便重建前移除旧 prop,避免叠加(评审 HIGH)。 vtkSmartPointer currentAxes_; diff --git a/src/app/main.cpp b/src/app/main.cpp index 677dba5..7f51553 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -1083,12 +1083,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re // 坐标轴设置抽屉「应用」:轴显示开关 + 放大系数(=垂直夸张,下发控制器+底图);范围/单位待控制器 API。 QObject::connect(axesPanel, &geopro::app::AxesSettingsPanel::applied, &window, [axesPanel, sceneCtrl, basemap](geopro::app::AxisRange x, geopro::app::AxisRange y, - geopro::app::AxisRange z, int /*unitIdx*/, + geopro::app::AxisRange z, int unitIdx, double scale) { const bool anyShow = x.show || y.show || z.show; - sceneCtrl->setAxesMode(anyShow ? geopro::controller::AxesMode::Standard - : geopro::controller::AxesMode::None); - if (scale > 0) { + // 每轴:可见性=显示开关;自定义范围=面板 min/max(按当前单位),真正改刻度。 + auto cfg = [](const geopro::app::AxisRange& a) { + return geopro::controller::AxisRangeCfg{a.show, true, a.min, a.max}; + }; + sceneCtrl->setAxesConfig(anyShow ? geopro::controller::AxesMode::Standard + : geopro::controller::AxesMode::None, + unitIdx == 1 ? geopro::controller::AxesUnit::Feet + : geopro::controller::AxesUnit::Meter, + cfg(x), cfg(y), cfg(z)); + if (scale > 0) { // 放大系数=垂直夸张:控制器 + 底图同步 sceneCtrl->setVerticalExaggeration(scale); basemap->setVerticalExaggeration(scale); } diff --git a/src/controller/I3dSceneView.hpp b/src/controller/I3dSceneView.hpp index 2d6daf4..21dff19 100644 --- a/src/controller/I3dSceneView.hpp +++ b/src/controller/I3dSceneView.hpp @@ -14,6 +14,13 @@ enum class AxesUnit { None, Meter, Feet, LatLon }; // 快捷视图方向(spec §4 C7):前/后/左/右/上/下。 enum class ViewDir { Front, Back, Left, Right, Top, Bottom }; +// 单轴显示配置(坐标轴设置面板):是否显示该轴 + 是否用自定义刻度范围(值按当前单位)。 +struct AxisRangeCfg { + bool visible = true; + bool customRange = false; + double min = 0.0, max = 0.0; +}; + // 三维场景视图抽象(编排层与 VTK 渲染解耦的缝): // VtkSceneController 只发出"清场 / 加某类图元 / 提交渲染"指令,不认 vtkActor/vtkVolume; // 真实实现(VtkSceneView)调 render actor + Scene;测试用 fake 记录调用断言编排。 @@ -56,6 +63,8 @@ public: // 坐标轴设置(P2):显示方式 + 刻度单位 + 字号。视图据当前场景包围盒重建坐标轴 prop。 // None 模式 = 移除坐标轴;rebuild 时由控制器在 clear 后重新下发当前坐标轴设置。 virtual void setAxes(AxesMode mode, AxesUnit unit, int fontSize) = 0; + // per-axis 可见性 + 自定义范围(坐标轴设置面板「应用」)。默认空实现,测试 mock 无需覆盖。 + virtual void setAxesRanges(const AxisRangeCfg&, const AxisRangeCfg&, const AxisRangeCfg&) {} // 快捷视图(P2):应用 6 向相机预设并提交渲染。 virtual void applyCameraView(ViewDir dir) = 0; diff --git a/src/controller/VtkSceneController.cpp b/src/controller/VtkSceneController.cpp index 15527d7..b7d9109 100644 --- a/src/controller/VtkSceneController.cpp +++ b/src/controller/VtkSceneController.cpp @@ -249,6 +249,16 @@ void VtkSceneController::setAxesUnit(AxesUnit unit) { rebuildInternal(); } +void VtkSceneController::setAxesConfig(AxesMode mode, AxesUnit unit, const AxisRangeCfg& x, + const AxisRangeCfg& y, const AxisRangeCfg& z) { + axesMode_ = mode; + axesUnit_ = unit; + axisX_ = x; + axisY_ = y; + axisZ_ = z; + rebuildInternal(); // 一次重建应用全部坐标轴设置 +} + // 快捷视图 / 缩放:仅改相机,不重建场景(无须取数/重装图元)。 void VtkSceneController::applyView(ViewDir dir) { view_.applyCameraView(dir); } void VtkSceneController::zoomIn() { view_.zoom(1.2); } @@ -277,6 +287,7 @@ void VtkSceneController::rebuildInternal() { view_.setVerticalExaggeration(verticalExaggeration_); // 坐标轴设置在 clear 后下发:render 末尾据当前场景包围盒重建坐标轴 prop。 view_.setAxes(axesMode_, axesUnit_, kAxesFontSize); + view_.setAxesRanges(axisX_, axisY_, axisZ_); fitOnArrival_ = true; // 全量重建:到场数据自动取景 // 坏 dsId(loadGrid/loadColorScale 抛异常)= best-effort 跳过:emit loadFailed 但不中断。 diff --git a/src/controller/VtkSceneController.hpp b/src/controller/VtkSceneController.hpp index bec591e..0e92f62 100644 --- a/src/controller/VtkSceneController.hpp +++ b/src/controller/VtkSceneController.hpp @@ -53,6 +53,9 @@ public slots: // ── P2 三维数据集栏 ── void setAxesMode(AxesMode mode); void setAxesUnit(AxesUnit unit); + // 坐标轴设置面板「应用」:一次性下发 显示方式 + 单位 + per-axis 可见性/范围(单次重建)。 + void setAxesConfig(AxesMode mode, AxesUnit unit, const AxisRangeCfg& x, const AxisRangeCfg& y, + const AxisRangeCfg& z); void applyView(ViewDir dir); // 6 向快捷视图 void zoomIn(); // Zoom In (×1.2) void zoomOut(); // Zoom Out (×1/1.2) @@ -95,6 +98,7 @@ private: // 坐标轴设置(P2):默认标准 + 米;字号固定 12(字体设置待 1.0 确认)。 AxesMode axesMode_ = AxesMode::Standard; AxesUnit axesUnit_ = AxesUnit::Meter; + AxisRangeCfg axisX_, axisY_, axisZ_; // 坐标轴设置面板的 per-axis 可见性 + 自定义范围 static constexpr int kAxesFontSize = 12; // 缓存(按 dsId):避免重复读盘/插值。 diff --git a/src/render/actors/AxesActor.cpp b/src/render/actors/AxesActor.cpp index 907f58f..eedd5ce 100644 --- a/src/render/actors/AxesActor.cpp +++ b/src/render/actors/AxesActor.cpp @@ -77,11 +77,14 @@ vtkSmartPointer buildAxes(const double bounds[6], const AxesOp ax->SetXLabelFormat("%.5f"); ax->SetYLabelFormat("%.5f"); } else { - // 米 / 英尺:显示范围 = 几何范围 × 系数。 + // 米 / 英尺:显示范围 = 自定义范围(用户按当前单位输入)或几何范围 × 系数。 const double s = unitScaleFactor(opts.unit); - ax->SetXAxisRange(b[0] * s, b[1] * s); - ax->SetYAxisRange(b[2] * s, b[3] * s); - ax->SetZAxisRange(b[4] * s, b[5] * s); + ax->SetXAxisRange(opts.x.customRange ? opts.x.min : b[0] * s, + opts.x.customRange ? opts.x.max : b[1] * s); + ax->SetYAxisRange(opts.y.customRange ? opts.y.min : b[2] * s, + opts.y.customRange ? opts.y.max : b[3] * s); + ax->SetZAxisRange(opts.z.customRange ? opts.z.min : b[4] * s, + opts.z.customRange ? opts.z.max : b[5] * s); const char* u = (opts.unit == AxesUnit::Feet) ? "ft" : "m"; ax->SetXTitle("X"); ax->SetYTitle("Y"); @@ -91,6 +94,11 @@ vtkSmartPointer buildAxes(const double bounds[6], const AxesOp ax->SetZUnits(u); } + // per-axis 显示开关(整轴:线 + 刻度 + 标签 + 标题)。 + ax->SetXAxisVisibility(opts.x.visible); + ax->SetYAxisVisibility(opts.y.visible); + ax->SetZAxisVisibility(opts.z.visible); + applyFont(ax, opts.fontSize); return ax; } diff --git a/src/render/actors/AxesActor.hpp b/src/render/actors/AxesActor.hpp index 4d35e3f..f4842bf 100644 --- a/src/render/actors/AxesActor.hpp +++ b/src/render/actors/AxesActor.hpp @@ -21,6 +21,14 @@ enum class AxesMode { Standard, Stereo, None }; // LatLon 经纬度 = 经 GeoLocalFrame 反算 X→经度、Y→纬度(Z 退化为米深度)。 enum class AxesUnit { None, Meter, Feet, LatLon }; +// 单轴显示(原型坐标轴设置):是否显示该轴 + 是否用自定义刻度范围(覆盖按单位换算的自动范围)。 +struct AxisDisplay { + bool visible = true; + bool customRange = false; // true=用 min/max 作刻度显示范围(值按当前单位) + double min = 0.0; + double max = 0.0; +}; + // 坐标轴构建参数。 struct AxesOptions { AxesMode mode = AxesMode::Standard; @@ -28,6 +36,7 @@ struct AxesOptions { int fontSize = 12; // 标题/标签字号 // 经纬度刻度需 frame 反算;为空则 LatLon 退化为米。 const geopro::core::GeoLocalFrame* frame = nullptr; + AxisDisplay x, y, z; // per-axis 可见性 + 自定义范围 }; // 由数据包围盒 bounds[6]={xmin,xmax,ymin,ymax,zmin,zmax} + 选项构建坐标轴 prop。