feat(vtk): 2D 平面z值直接平移足迹+底图(去重渲染与防抖)
拖 z 值滑块时足迹与平面底图直接沿 Z 平移(改 actor position),取代 移除+异步重载足迹与底图销毁+重建 → 实时跟随、无闪烁。 - 足迹几何建于 Z=0,平面高程经 addMapLine SetPosition 施加;新增 VtkSceneView::setMapLinesZ 直接平移足迹 actor(接口默认空实现) - TileBasemap 瓦片几何仅留逐层级 z-fighting 偏移,平面高程经 placeActor SetPosition(groundZ_)施加(LOD 新增瓦片同面);新增 setGroundZ 重设所有已贴瓦片 position,无重下载/重建 - IPlaneBasemap 新增 setGroundZ,适配器转发;Plane2DRenderStrategy setPlaneZ 改为直接平移足迹+底图(创建/销毁生命周期不变) - 移除 z 滑块 150ms 防抖(直接平移瞬时同步),底图透明度防抖保留 build.bat app 链接干净;build.bat test 467/467 通过
This commit is contained in:
parent
a6ade4939f
commit
94d0ac9c3b
|
|
@ -218,6 +218,16 @@ void TileBasemap::setOpacity(double o) {
|
||||||
if (kind_ != Hidden) show(kind_); // 重建套用新透明度
|
if (kind_ != Hidden) show(kind_); // 重建套用新透明度
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TileBasemap::setGroundZ(double z) {
|
||||||
|
// 直接平移平面高程:瓦片几何建于相对 z(仅含逐层级 z-fighting 偏移),平面高程 groundZ_ 经 actor position 施加。
|
||||||
|
// 拖 z 值滑块时只改所有已贴瓦片的 SetPosition,无需重下载/重建;后续 refresh() 新瓦片经 placeActor 自动取新 groundZ_。
|
||||||
|
if (z == groundZ_) return;
|
||||||
|
groundZ_ = z;
|
||||||
|
for (auto& kv : placed_)
|
||||||
|
if (kv.second) kv.second->SetPosition(0.0, 0.0, groundZ_);
|
||||||
|
requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
void TileBasemap::refineTile(int z, int x, int y, std::set<long long>& out, int& count) {
|
void TileBasemap::refineTile(int z, int x, int y, std::set<long long>& out, int& count) {
|
||||||
if (count >= kMaxLeaves) { out.insert(tileKey(z, x, y)); return; } // 安全上限:停止细分
|
if (count >= kMaxLeaves) { out.insert(tileKey(z, x, y)); return; } // 安全上限:停止细分
|
||||||
const int n = 1 << z;
|
const int n = 1 << z;
|
||||||
|
|
@ -439,6 +449,7 @@ void TileBasemap::fetchTile(int z, int x, int y, long long key) {
|
||||||
|
|
||||||
void TileBasemap::placeActor(long long key, vtkSmartPointer<vtkActor> actor) {
|
void TileBasemap::placeActor(long long key, vtkSmartPointer<vtkActor> actor) {
|
||||||
if (!actor) return;
|
if (!actor) return;
|
||||||
|
actor->SetPosition(0.0, 0.0, groundZ_); // 平面高程经 position 施加:几何建于相对 z,此处抬到当前 groundZ_
|
||||||
scene_.addActor(actor);
|
scene_.addActor(actor);
|
||||||
placed_[key] = actor;
|
placed_[key] = actor;
|
||||||
}
|
}
|
||||||
|
|
@ -449,7 +460,7 @@ vtkSmartPointer<vtkActor> TileBasemap::buildFlat(int z, int x, int y,
|
||||||
const auto sw = frame_->toLocal(b.south, b.west);
|
const auto sw = frame_->toLocal(b.south, b.west);
|
||||||
const auto se = frame_->toLocal(b.south, b.east);
|
const auto se = frame_->toLocal(b.south, b.east);
|
||||||
const auto nw = frame_->toLocal(b.north, b.west);
|
const auto nw = frame_->toLocal(b.north, b.west);
|
||||||
const double gz = groundZ_ + (z - kMinZoom) * kZEps; // 高层级略抬高,压在旧层之上防共面闪烁
|
const double gz = (z - kMinZoom) * kZEps; // 仅逐层级 z-fighting 偏移(相对 z);平面高程由 actor position 施加
|
||||||
|
|
||||||
// PlaneSource 自动 tcoord:origin=SW→u 西0东1、v 南0北1(与翻转后纹理对齐)。
|
// PlaneSource 自动 tcoord:origin=SW→u 西0东1、v 南0北1(与翻转后纹理对齐)。
|
||||||
vtkNew<vtkPlaneSource> plane;
|
vtkNew<vtkPlaneSource> plane;
|
||||||
|
|
@ -538,7 +549,7 @@ vtkSmartPointer<vtkActor> TileBasemap::buildWarped(int sz, int sx, int sy, int d
|
||||||
const auto sw = frame_->toLocal(sb.south, sb.west);
|
const auto sw = frame_->toLocal(sb.south, sb.west);
|
||||||
const auto se = frame_->toLocal(sb.south, sb.east);
|
const auto se = frame_->toLocal(sb.south, sb.east);
|
||||||
const auto nw = frame_->toLocal(sb.north, sb.west);
|
const auto nw = frame_->toLocal(sb.north, sb.west);
|
||||||
const double base = groundZ_ + (sz - kMinZoom) * kZEps;
|
const double base = (sz - kMinZoom) * kZEps; // 仅逐层级 z-fighting 偏移(相对 z);平面高程由 actor position 施加
|
||||||
|
|
||||||
// PlaneSource(等距圆柱下平面插值即正确 x/y) + 自动 tcoord;再按各点真实经纬采 DEM 位移 Z。
|
// PlaneSource(等距圆柱下平面插值即正确 x/y) + 自动 tcoord;再按各点真实经纬采 DEM 位移 Z。
|
||||||
vtkNew<vtkPlaneSource> plane;
|
vtkNew<vtkPlaneSource> plane;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public:
|
||||||
void refresh(); // 按当前相机重算层级+覆盖,增量更新瓦片(交互结束回调)
|
void refresh(); // 按当前相机重算层级+覆盖,增量更新瓦片(交互结束回调)
|
||||||
void setVerticalExaggeration(double ve); // 地形垂向夸张(须与剖面 VE 一致才对齐)
|
void setVerticalExaggeration(double ve); // 地形垂向夸张(须与剖面 VE 一致才对齐)
|
||||||
void setOpacity(double o); // 底图半透明度[0,1],供渲染工具栏底图弹窗调节
|
void setOpacity(double o); // 底图半透明度[0,1],供渲染工具栏底图弹窗调节
|
||||||
|
void setGroundZ(double z); // 直接平移底图平面 z(拖 z 值滑块):改所有已贴瓦片 actor 的 position,无重铺
|
||||||
// 数据半径提供者:刷新时查询当前所有勾选剖面的合并范围(半径,米),据此动态定底图最大范围。
|
// 数据半径提供者:刷新时查询当前所有勾选剖面的合并范围(半径,米),据此动态定底图最大范围。
|
||||||
void setDataRadiusProvider(std::function<double()> fn) { dataRadiusProvider_ = std::move(fn); }
|
void setDataRadiusProvider(std::function<double()> fn) { dataRadiusProvider_ = std::move(fn); }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ public:
|
||||||
}
|
}
|
||||||
void hide() override { bm_.hide(); }
|
void hide() override { bm_.hide(); }
|
||||||
void setOpacity(double o) override { bm_.setOpacity(o); }
|
void setOpacity(double o) override { bm_.setOpacity(o); }
|
||||||
|
void setGroundZ(double z) override { bm_.setGroundZ(z); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TileBasemap bm_;
|
TileBasemap bm_;
|
||||||
|
|
|
||||||
|
|
@ -253,14 +253,29 @@ void VtkSceneView::addMapLine(const std::string& dsId, const geopro::data::MapLi
|
||||||
// worldZ 已是最终世界高程(含摆放语义),不再施加 VE(足迹是水平线,非随深度的竖直图元)。
|
// worldZ 已是最终世界高程(含摆放语义),不再施加 VE(足迹是水平线,非随深度的竖直图元)。
|
||||||
// 足迹可能是首个(且唯一)带经纬的数据 → 与帘面同样重锚原点,否则按样本默认原点投到数百公里外不可见。
|
// 足迹可能是首个(且唯一)带经纬的数据 → 与帘面同样重锚原点,否则按样本默认原点投到数百公里外不可见。
|
||||||
anchorFrameIfNeeded(line.lat, line.lon, static_cast<int>(line.lat.size()));
|
anchorFrameIfNeeded(line.lat, line.lon, static_cast<int>(line.lat.size()));
|
||||||
auto actor = geopro::render::buildMapLine(line.lat, line.lon, worldZ, *frame_);
|
// 折线几何建于 Z=0,平面高程 worldZ 经 actor SetPosition 施加 → 后续拖 z 值滑块只改 position 即直接平移,
|
||||||
|
// 无需移除+异步重载几何(setMapLinesZ 走此)。首勾/后续勾选在当前平面 z 加入者立即摆到该 z。
|
||||||
|
auto actor = geopro::render::buildMapLine(line.lat, line.lon, 0.0, *frame_);
|
||||||
if (actor) {
|
if (actor) {
|
||||||
|
actor->SetPosition(0.0, 0.0, worldZ);
|
||||||
scene_.addActor(actor);
|
scene_.addActor(actor);
|
||||||
dsProps_[dsId].push_back(actor);
|
dsProps_[dsId].push_back(actor);
|
||||||
mapLineDs_.insert(dsId); // 记录此 ds 为 2D 足迹(供足迹归属识别)
|
mapLineDs_.insert(dsId); // 记录此 ds 为 2D 足迹(供足迹归属识别)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VtkSceneView::setMapLinesZ(const std::vector<std::string>& dsIds, double z) {
|
||||||
|
// 直接平移足迹:仅对属于足迹的 dsId 改其 actor 的 SetPosition(0,0,z),即时渲染,无移除+重载。
|
||||||
|
for (const auto& dsId : dsIds) {
|
||||||
|
if (!mapLineDs_.count(dsId)) continue;
|
||||||
|
auto it = dsProps_.find(dsId);
|
||||||
|
if (it == dsProps_.end()) continue;
|
||||||
|
for (auto& prop : it->second)
|
||||||
|
if (auto* a = vtkActor::SafeDownCast(prop)) a->SetPosition(0.0, 0.0, z);
|
||||||
|
}
|
||||||
|
if (renderWindow_) renderWindow_->Render();
|
||||||
|
}
|
||||||
|
|
||||||
void VtkSceneView::addTerrain(const geopro::data::TerrainPaths& paths) {
|
void VtkSceneView::addTerrain(const geopro::data::TerrainPaths& paths) {
|
||||||
auto terrain = geopro::render::buildTerrain(paths.demPath, paths.imagePath, *frame_, zRefElev_,
|
auto terrain = geopro::render::buildTerrain(paths.demPath, paths.imagePath, *frame_, zRefElev_,
|
||||||
verticalExaggeration_);
|
verticalExaggeration_);
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ public:
|
||||||
const geopro::core::ColorScale& cs) override;
|
const geopro::core::ColorScale& cs) override;
|
||||||
void addMapLine(const std::string& dsId, const geopro::data::MapLine& line,
|
void addMapLine(const std::string& dsId, const geopro::data::MapLine& line,
|
||||||
double worldZ) override;
|
double worldZ) override;
|
||||||
|
void setMapLinesZ(const std::vector<std::string>& dsIds, double z) override;
|
||||||
void addTerrain(const geopro::data::TerrainPaths& paths) override;
|
void addTerrain(const geopro::data::TerrainPaths& paths) override;
|
||||||
void removeDataset(const std::string& dsId) override;
|
void removeDataset(const std::string& dsId) override;
|
||||||
void addAnomaly(const geopro::core::Anomaly& a) override;
|
void addAnomaly(const geopro::core::Anomaly& a) override;
|
||||||
|
|
|
||||||
|
|
@ -484,23 +484,12 @@ void CategorySection::showPlaneZPopup(QToolButton* host) {
|
||||||
sld->setToolTip(QStringLiteral("平面高程 z(米)"));
|
sld->setToolTip(QStringLiteral("平面高程 z(米)"));
|
||||||
auto syncLabel = [lab](int v) { lab->setText(QStringLiteral("平面 z:%1 米").arg(v)); };
|
auto syncLabel = [lab](int v) { lab->setText(QStringLiteral("平面 z:%1 米").arg(v)); };
|
||||||
syncLabel(sld->value());
|
syncLabel(sld->value());
|
||||||
// 发射防抖:valueChanged 在拖动期逐整数步触发,每步直发 planeZChanged 会引发数十次
|
// 直接平移平面/足迹/底图(改 actor position,即时同步)→ 无需防抖:valueChanged 每步直发 planeZChanged,
|
||||||
// removeDataset+add2DDatasetAsync 抖动重摆(生产异步路径 loadingDs_ 守护会丢中间帧→足迹滞后于陈旧 z)。
|
// 拖动即实时跟随、无移除+异步重载。lastPlaneZ_ 记住终值供重开 popup 回显。
|
||||||
// 故 label/lastPlaneZ_ 即时回显,但 planeZChanged 经单发 QTimer(150ms)合并——每次变更重启,停手后只发一次终值。
|
|
||||||
// 定时器 parent=this(CategorySection),存活于 modal popup(menu.exec)之外,即便 popup 已销毁也能安全发射终值。
|
|
||||||
if (!planeZTimer_) {
|
|
||||||
planeZTimer_ = new QTimer(this);
|
|
||||||
planeZTimer_->setSingleShot(true);
|
|
||||||
planeZTimer_->setInterval(150);
|
|
||||||
connect(planeZTimer_, &QTimer::timeout, this, [this]() {
|
|
||||||
emit planeZChanged(QString::fromStdString(desc_.id), pendingPlaneZ_);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
connect(sld, &QSlider::valueChanged, this, [this, syncLabel](int v) {
|
connect(sld, &QSlider::valueChanged, this, [this, syncLabel](int v) {
|
||||||
lastPlaneZ_ = v;
|
lastPlaneZ_ = v;
|
||||||
syncLabel(v);
|
syncLabel(v);
|
||||||
pendingPlaneZ_ = static_cast<double>(v);
|
emit planeZChanged(QString::fromStdString(desc_.id), static_cast<double>(v));
|
||||||
planeZTimer_->start(); // 重启防抖窗口:覆盖拖动、键盘、点轨——停手后一次性发射
|
|
||||||
});
|
});
|
||||||
lay->addWidget(lab);
|
lay->addWidget(lab);
|
||||||
lay->addWidget(sld);
|
lay->addWidget(sld);
|
||||||
|
|
|
||||||
|
|
@ -93,12 +93,10 @@ private:
|
||||||
QTreeWidget* list_ = nullptr;
|
QTreeWidget* list_ = nullptr;
|
||||||
QTimer* spinTimer_ = nullptr; // 驱动 busy 行 spinner 旋转(有 busy 行时运行)
|
QTimer* spinTimer_ = nullptr; // 驱动 busy 行 spinner 旋转(有 busy 行时运行)
|
||||||
int spinAngle_ = 0; // 当前 spinner 角度(度)
|
int spinAngle_ = 0; // 当前 spinner 角度(度)
|
||||||
double lastPlaneZ_ = 0.0; // 上次 z 值滑块设定的平面高程(重开 popup 时回显,无则 0)
|
double lastPlaneZ_ = 0.0; // 上次 z 值滑块设定的平面高程(重开 popup 时回显,无则 0;直接平移故无防抖)
|
||||||
QTimer* planeZTimer_ = nullptr; // z 值滑块发射防抖:拖动/键盘/点轨期合并为停手后一次重摆(owned by this,存活于 popup 之外)
|
|
||||||
double pendingPlaneZ_ = 0.0; // 防抖待发的平面 z(定时器到点时取此值发射 planeZChanged)
|
|
||||||
int lastBasemapKind_ = 0; // 上次底图选择(0=矢量平面/1=无),重开 popup 时回显
|
int lastBasemapKind_ = 0; // 上次底图选择(0=矢量平面/1=无),重开 popup 时回显
|
||||||
int lastBasemapOpacity_ = 50; // 上次底图透明度(0–100,重开 popup 回显;默认 50)
|
int lastBasemapOpacity_ = 50; // 上次底图透明度(0–100,重开 popup 回显;默认 50)
|
||||||
QTimer* basemapOpacityTimer_ = nullptr; // 底图透明度滑块发射防抖(同 planeZ:停手后一次发射,免抖动重铺瓦片)
|
QTimer* basemapOpacityTimer_ = nullptr; // 底图透明度滑块发射防抖(透明度改动会触发瓦片重铺,仍需防抖)
|
||||||
double pendingBasemapOpacity_ = 0.5; // 防抖待发的底图透明度[0,1](定时器到点发射 basemapOpacityChanged)
|
double pendingBasemapOpacity_ = 0.5; // 防抖待发的底图透明度[0,1](定时器到点发射 basemapOpacityChanged)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
#include "controller/DatasetRenderStrategy.hpp"
|
#include "controller/DatasetRenderStrategy.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "controller/VtkSceneController.hpp" // 完整类型 + 含 I3dSceneView(view_.removeDataset)
|
#include "controller/VtkSceneController.hpp" // 完整类型 + 含 I3dSceneView(view_.removeDataset)
|
||||||
|
|
||||||
namespace geopro::controller {
|
namespace geopro::controller {
|
||||||
|
|
@ -52,15 +55,14 @@ void Plane2DRenderStrategy::onTypeDeactivated(const std::string& typeId) {
|
||||||
|
|
||||||
void Plane2DRenderStrategy::setPlaneZ(const std::string& typeId, double z) {
|
void Plane2DRenderStrategy::setPlaneZ(const std::string& typeId, double z) {
|
||||||
planeReg_.setPlaneZ(typeId, z); // 平面 z 真源更新(类型不存在则无操作)
|
planeReg_.setPlaneZ(typeId, z); // 平面 z 真源更新(类型不存在则无操作)
|
||||||
// 重摆该类型全部已勾选足迹:移除旧 actor,按既有 add 路径在新 z 重新加载摆放。
|
// 直接平移该类型全部已勾选足迹:只改足迹 actor 的 position(无移除+异步重载)→ 拖滑块即时跟随、无闪烁。
|
||||||
// add2DDatasetAsync 复用 rebuildGeneration_ + loadingDs_/is2DChecked 防迟到/防重复(与 add 同护栏)。
|
std::vector<std::string> dsIds;
|
||||||
for (const auto& [dsId, t] : dsToType_) {
|
for (const auto& [dsId, t] : dsToType_)
|
||||||
if (t != typeId) continue;
|
if (t == typeId) dsIds.push_back(dsId);
|
||||||
ctrl_.view_.removeDataset(dsId);
|
if (!dsIds.empty()) ctrl_.view_.setMapLinesZ(dsIds, z);
|
||||||
ctrl_.add2DDatasetAsync(dsId, ctrl_.rebuildGeneration_, z);
|
// 底图同步平移:直接改瓦片 position(无销毁+重建、无重下载)→ 底图与足迹一同实时跟随滑块。
|
||||||
}
|
auto it = bms_.find(typeId);
|
||||||
// 底图同步升降:销毁旧实例、按新平面 z 重建(复用当前 kind/opacity),简单可靠(无 setGroundZ 增量重铺)。
|
if (it != bms_.end()) it->second->setGroundZ(z);
|
||||||
if (bms_.count(typeId)) createBasemap(typeId);
|
|
||||||
ctrl_.view_.renderIncremental();
|
ctrl_.view_.renderIncremental();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ public:
|
||||||
virtual void show(int kind) = 0; // 0=矢量平面(Street)/其它=无(hide)
|
virtual void show(int kind) = 0; // 0=矢量平面(Street)/其它=无(hide)
|
||||||
virtual void hide() = 0;
|
virtual void hide() = 0;
|
||||||
virtual void setOpacity(double o) = 0; // 半透明度[0,1]
|
virtual void setOpacity(double o) = 0; // 半透明度[0,1]
|
||||||
|
virtual void setGroundZ(double z) = 0; // 直接平移平面高程 z(拖 z 值滑块):改瓦片 position,无重铺
|
||||||
};
|
};
|
||||||
// 工厂:按平面 z 造一份平面底图(底图所需 scene/渲染窗/frame/数据半径规则由 app 闭包捕获)。
|
// 工厂:按平面 z 造一份平面底图(底图所需 scene/渲染窗/frame/数据半径规则由 app 闭包捕获)。
|
||||||
// 未注入(空)则不建底图——便于无 VTK 的纯逻辑单测。
|
// 未注入(空)则不建底图——便于无 VTK 的纯逻辑单测。
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "model/ColorScale.hpp"
|
#include "model/ColorScale.hpp"
|
||||||
#include "model/Field.hpp"
|
#include "model/Field.hpp"
|
||||||
|
|
@ -51,6 +52,9 @@ public:
|
||||||
// 2D 足迹:把测线/轨迹经纬折线平铺进 3D 地图(worldZ=摆放高程);按 dsId 跟踪以支持增量移除。
|
// 2D 足迹:把测线/轨迹经纬折线平铺进 3D 地图(worldZ=摆放高程);按 dsId 跟踪以支持增量移除。
|
||||||
virtual void addMapLine(const std::string& dsId, const geopro::data::MapLine& line,
|
virtual void addMapLine(const std::string& dsId, const geopro::data::MapLine& line,
|
||||||
double worldZ) = 0;
|
double worldZ) = 0;
|
||||||
|
// 直接平移一组 2D 足迹到新平面 z(拖 z 值滑块用):改足迹 actor 的 SetPosition,无移除+异步重载。
|
||||||
|
// 仅对属于足迹的 dsId 生效;即时渲染。默认空实现,测试 mock 无需覆盖。
|
||||||
|
virtual void setMapLinesZ(const std::vector<std::string>& /*dsIds*/, double /*z*/) {}
|
||||||
// 3D:DEM 地形 + 影像纹理。
|
// 3D:DEM 地形 + 影像纹理。
|
||||||
virtual void addTerrain(const geopro::data::TerrainPaths& paths) = 0;
|
virtual void addTerrain(const geopro::data::TerrainPaths& paths) = 0;
|
||||||
// 增量移除某数据集的全部图元(取消勾选时调,不影响其余 ds 与底图)。
|
// 增量移除某数据集的全部图元(取消勾选时调,不影响其余 ds 与底图)。
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue