feat(vtk): 坐标轴设置真正生效-单位(米/英尺)换算+每轴显示开关+自定义刻度范围 打通 render→controller→view→面板

This commit is contained in:
gaozheng 2026-06-25 14:16:27 +08:00
parent 0cfa1ad352
commit 6c8069b87d
8 changed files with 74 additions and 8 deletions

View File

@ -253,6 +253,14 @@ void VtkSceneView::setAxes(geopro::controller::AxesMode mode, geopro::controller
axesFontSize_ = fontSize; 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) { void VtkSceneView::applyCameraView(geopro::controller::ViewDir dir) {
geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir)); // 设朝向(内部 ResetCamera 含底图) geopro::render::applyView(scene_.renderer(), toRenderViewDir(dir)); // 设朝向(内部 ResetCamera 含底图)
double bounds[6]; double bounds[6];
@ -296,6 +304,12 @@ void VtkSceneView::rebuildAxes() {
opts.unit = toRenderUnit(axesUnit_); opts.unit = toRenderUnit(axesUnit_);
opts.fontSize = axesFontSize_; opts.fontSize = axesFontSize_;
opts.frame = frame_.get(); 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()); auto axes = geopro::render::buildAxes(bounds, opts, scene_.renderer());
if (axes) { if (axes) {
scene_.addViewProp(axes); scene_.addViewProp(axes);

View File

@ -50,6 +50,9 @@ public:
void setSelectedAnomaly(const std::string& anomalyId) override; void setSelectedAnomaly(const std::string& anomalyId) override;
void setAxes(geopro::controller::AxesMode mode, geopro::controller::AxesUnit unit, void setAxes(geopro::controller::AxesMode mode, geopro::controller::AxesUnit unit,
int fontSize) override; 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 applyCameraView(geopro::controller::ViewDir dir) override;
void zoom(double factor) override; void zoom(double factor) override;
void fitView() override; void fitView() override;
@ -108,6 +111,7 @@ private:
geopro::controller::AxesMode axesMode_ = geopro::controller::AxesMode::Standard; geopro::controller::AxesMode axesMode_ = geopro::controller::AxesMode::Standard;
geopro::controller::AxesUnit axesUnit_ = geopro::controller::AxesUnit::Meter; geopro::controller::AxesUnit axesUnit_ = geopro::controller::AxesUnit::Meter;
int axesFontSize_ = 12; int axesFontSize_ = 12;
geopro::controller::AxisRangeCfg axisX_, axisY_, axisZ_; // per-axis 可见性 + 自定义范围
// 当前坐标轴 proprender 可能多次调用 rebuildAxesrebuild 末尾 + 异步回灌), // 当前坐标轴 proprender 可能多次调用 rebuildAxesrebuild 末尾 + 异步回灌),
// 持引用以便重建前移除旧 prop避免叠加评审 HIGH // 持引用以便重建前移除旧 prop避免叠加评审 HIGH
vtkSmartPointer<vtkCubeAxesActor> currentAxes_; vtkSmartPointer<vtkCubeAxesActor> currentAxes_;

View File

@ -1083,12 +1083,19 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
// 坐标轴设置抽屉「应用」:轴显示开关 + 放大系数(=垂直夸张,下发控制器+底图);范围/单位待控制器 API。 // 坐标轴设置抽屉「应用」:轴显示开关 + 放大系数(=垂直夸张,下发控制器+底图);范围/单位待控制器 API。
QObject::connect(axesPanel, &geopro::app::AxesSettingsPanel::applied, &window, QObject::connect(axesPanel, &geopro::app::AxesSettingsPanel::applied, &window,
[axesPanel, sceneCtrl, basemap](geopro::app::AxisRange x, geopro::app::AxisRange y, [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) { double scale) {
const bool anyShow = x.show || y.show || z.show; const bool anyShow = x.show || y.show || z.show;
sceneCtrl->setAxesMode(anyShow ? geopro::controller::AxesMode::Standard // 每轴:可见性=显示开关;自定义范围=面板 min/max按当前单位真正改刻度。
: geopro::controller::AxesMode::None); auto cfg = [](const geopro::app::AxisRange& a) {
if (scale > 0) { 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); sceneCtrl->setVerticalExaggeration(scale);
basemap->setVerticalExaggeration(scale); basemap->setVerticalExaggeration(scale);
} }

View File

@ -14,6 +14,13 @@ enum class AxesUnit { None, Meter, Feet, LatLon };
// 快捷视图方向spec §4 C7前/后/左/右/上/下。 // 快捷视图方向spec §4 C7前/后/左/右/上/下。
enum class ViewDir { Front, Back, Left, Right, Top, Bottom }; 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 渲染解耦的缝): // 三维场景视图抽象(编排层与 VTK 渲染解耦的缝):
// VtkSceneController 只发出"清场 / 加某类图元 / 提交渲染"指令,不认 vtkActor/vtkVolume // VtkSceneController 只发出"清场 / 加某类图元 / 提交渲染"指令,不认 vtkActor/vtkVolume
// 真实实现VtkSceneView调 render actor + Scene测试用 fake 记录调用断言编排。 // 真实实现VtkSceneView调 render actor + Scene测试用 fake 记录调用断言编排。
@ -56,6 +63,8 @@ public:
// 坐标轴设置P2显示方式 + 刻度单位 + 字号。视图据当前场景包围盒重建坐标轴 prop。 // 坐标轴设置P2显示方式 + 刻度单位 + 字号。视图据当前场景包围盒重建坐标轴 prop。
// None 模式 = 移除坐标轴rebuild 时由控制器在 clear 后重新下发当前坐标轴设置。 // None 模式 = 移除坐标轴rebuild 时由控制器在 clear 后重新下发当前坐标轴设置。
virtual void setAxes(AxesMode mode, AxesUnit unit, int fontSize) = 0; virtual void setAxes(AxesMode mode, AxesUnit unit, int fontSize) = 0;
// per-axis 可见性 + 自定义范围(坐标轴设置面板「应用」)。默认空实现,测试 mock 无需覆盖。
virtual void setAxesRanges(const AxisRangeCfg&, const AxisRangeCfg&, const AxisRangeCfg&) {}
// 快捷视图P2应用 6 向相机预设并提交渲染。 // 快捷视图P2应用 6 向相机预设并提交渲染。
virtual void applyCameraView(ViewDir dir) = 0; virtual void applyCameraView(ViewDir dir) = 0;

View File

@ -249,6 +249,16 @@ void VtkSceneController::setAxesUnit(AxesUnit unit) {
rebuildInternal(); 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::applyView(ViewDir dir) { view_.applyCameraView(dir); }
void VtkSceneController::zoomIn() { view_.zoom(1.2); } void VtkSceneController::zoomIn() { view_.zoom(1.2); }
@ -277,6 +287,7 @@ void VtkSceneController::rebuildInternal() {
view_.setVerticalExaggeration(verticalExaggeration_); view_.setVerticalExaggeration(verticalExaggeration_);
// 坐标轴设置在 clear 后下发render 末尾据当前场景包围盒重建坐标轴 prop。 // 坐标轴设置在 clear 后下发render 末尾据当前场景包围盒重建坐标轴 prop。
view_.setAxes(axesMode_, axesUnit_, kAxesFontSize); view_.setAxes(axesMode_, axesUnit_, kAxesFontSize);
view_.setAxesRanges(axisX_, axisY_, axisZ_);
fitOnArrival_ = true; // 全量重建:到场数据自动取景 fitOnArrival_ = true; // 全量重建:到场数据自动取景
// 坏 dsIdloadGrid/loadColorScale 抛异常)= best-effort 跳过emit loadFailed 但不中断。 // 坏 dsIdloadGrid/loadColorScale 抛异常)= best-effort 跳过emit loadFailed 但不中断。

View File

@ -53,6 +53,9 @@ public slots:
// ── P2 三维数据集栏 ── // ── P2 三维数据集栏 ──
void setAxesMode(AxesMode mode); void setAxesMode(AxesMode mode);
void setAxesUnit(AxesUnit unit); 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 applyView(ViewDir dir); // 6 向快捷视图
void zoomIn(); // Zoom In (×1.2) void zoomIn(); // Zoom In (×1.2)
void zoomOut(); // Zoom Out (×1/1.2) void zoomOut(); // Zoom Out (×1/1.2)
@ -95,6 +98,7 @@ private:
// 坐标轴设置P2默认标准 + 米;字号固定 12字体设置待 1.0 确认)。 // 坐标轴设置P2默认标准 + 米;字号固定 12字体设置待 1.0 确认)。
AxesMode axesMode_ = AxesMode::Standard; AxesMode axesMode_ = AxesMode::Standard;
AxesUnit axesUnit_ = AxesUnit::Meter; AxesUnit axesUnit_ = AxesUnit::Meter;
AxisRangeCfg axisX_, axisY_, axisZ_; // 坐标轴设置面板的 per-axis 可见性 + 自定义范围
static constexpr int kAxesFontSize = 12; static constexpr int kAxesFontSize = 12;
// 缓存(按 dsId避免重复读盘/插值。 // 缓存(按 dsId避免重复读盘/插值。

View File

@ -77,11 +77,14 @@ vtkSmartPointer<vtkCubeAxesActor> buildAxes(const double bounds[6], const AxesOp
ax->SetXLabelFormat("%.5f"); ax->SetXLabelFormat("%.5f");
ax->SetYLabelFormat("%.5f"); ax->SetYLabelFormat("%.5f");
} else { } else {
// 米 / 英尺:显示范围 = 几何范围 × 系数。 // 米 / 英尺:显示范围 = 自定义范围(用户按当前单位输入)或几何范围 × 系数。
const double s = unitScaleFactor(opts.unit); const double s = unitScaleFactor(opts.unit);
ax->SetXAxisRange(b[0] * s, b[1] * s); ax->SetXAxisRange(opts.x.customRange ? opts.x.min : b[0] * s,
ax->SetYAxisRange(b[2] * s, b[3] * s); opts.x.customRange ? opts.x.max : b[1] * s);
ax->SetZAxisRange(b[4] * s, b[5] * 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"; const char* u = (opts.unit == AxesUnit::Feet) ? "ft" : "m";
ax->SetXTitle("X"); ax->SetXTitle("X");
ax->SetYTitle("Y"); ax->SetYTitle("Y");
@ -91,6 +94,11 @@ vtkSmartPointer<vtkCubeAxesActor> buildAxes(const double bounds[6], const AxesOp
ax->SetZUnits(u); ax->SetZUnits(u);
} }
// per-axis 显示开关(整轴:线 + 刻度 + 标签 + 标题)。
ax->SetXAxisVisibility(opts.x.visible);
ax->SetYAxisVisibility(opts.y.visible);
ax->SetZAxisVisibility(opts.z.visible);
applyFont(ax, opts.fontSize); applyFont(ax, opts.fontSize);
return ax; return ax;
} }

View File

@ -21,6 +21,14 @@ enum class AxesMode { Standard, Stereo, None };
// LatLon 经纬度 = 经 GeoLocalFrame 反算 X→经度、Y→纬度Z 退化为米深度)。 // LatLon 经纬度 = 经 GeoLocalFrame 反算 X→经度、Y→纬度Z 退化为米深度)。
enum class AxesUnit { None, Meter, Feet, LatLon }; 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 { struct AxesOptions {
AxesMode mode = AxesMode::Standard; AxesMode mode = AxesMode::Standard;
@ -28,6 +36,7 @@ struct AxesOptions {
int fontSize = 12; // 标题/标签字号 int fontSize = 12; // 标题/标签字号
// 经纬度刻度需 frame 反算;为空则 LatLon 退化为米。 // 经纬度刻度需 frame 反算;为空则 LatLon 退化为米。
const geopro::core::GeoLocalFrame* frame = nullptr; const geopro::core::GeoLocalFrame* frame = nullptr;
AxisDisplay x, y, z; // per-axis 可见性 + 自定义范围
}; };
// 由数据包围盒 bounds[6]={xmin,xmax,ymin,ymax,zmin,zmax} + 选项构建坐标轴 prop。 // 由数据包围盒 bounds[6]={xmin,xmax,ymin,ymax,zmin,zmax} + 选项构建坐标轴 prop。