feat/vtk-3d-view #7
|
|
@ -59,6 +59,11 @@ void InteractionManager::safeRender() {
|
||||||
if (renderWindow_ && !destroying_) renderWindow_->Render();
|
if (renderWindow_ && !destroying_) renderWindow_->Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InteractionManager::updateSelectionVisual() {
|
||||||
|
for (std::size_t i = 0; i < slices_.size(); ++i)
|
||||||
|
slices_[i]->setSelected(static_cast<int>(i) == selected_);
|
||||||
|
}
|
||||||
|
|
||||||
void InteractionManager::setVolumeImage(vtkImageData* image, const geopro::core::ColorScale& cs,
|
void InteractionManager::setVolumeImage(vtkImageData* image, const geopro::core::ColorScale& cs,
|
||||||
double vmin, double vmax) {
|
double vmin, double vmax) {
|
||||||
// 体素重建/变更:先释放旧切片(旧 image 即将失效),再附着新 image。
|
// 体素重建/变更:先释放旧切片(旧 image 即将失效),再附着新 image。
|
||||||
|
|
@ -74,6 +79,7 @@ void InteractionManager::addSlice(SliceAxis axis) {
|
||||||
auto tool = std::make_unique<SliceTool>(image_, interactor_, axis, colorScale_, vmin_, vmax_);
|
auto tool = std::make_unique<SliceTool>(image_, interactor_, axis, colorScale_, vmin_, vmax_);
|
||||||
slices_.push_back(std::move(tool));
|
slices_.push_back(std::move(tool));
|
||||||
selected_ = static_cast<int>(slices_.size()) - 1; // 新切片选中
|
selected_ = static_cast<int>(slices_.size()) - 1; // 新切片选中
|
||||||
|
updateSelectionVisual();
|
||||||
safeRender();
|
safeRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,6 +90,7 @@ void InteractionManager::closeSelected() {
|
||||||
// 选中停在原位就近(删后该位变成下一张;删的是末张则退一张),不跳回 0(评审 M2)。
|
// 选中停在原位就近(删后该位变成下一张;删的是末张则退一张),不跳回 0(评审 M2)。
|
||||||
selected_ = slices_.empty() ? -1
|
selected_ = slices_.empty() ? -1
|
||||||
: std::min(selected_, static_cast<int>(slices_.size()) - 1);
|
: std::min(selected_, static_cast<int>(slices_.size()) - 1);
|
||||||
|
updateSelectionVisual();
|
||||||
safeRender();
|
safeRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,22 +131,13 @@ int InteractionManager::nearestSlice(const Vec3& worldPoint) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InteractionManager::onPicked(const Vec3& worldPoint) {
|
void InteractionManager::onPicked(const Vec3& worldPoint) {
|
||||||
// 旋转中心设到命中点(spec C38/D39):焦点与相机位置**同步平移同一 delta**,
|
// 单击 = 仅选中命中的切片(不动相机 → 绝不跳;拖动旋转交给默认 TrackballCamera)。
|
||||||
// 使渲染图像不变(视向/距离不变)、只把旋转中心移到命中点——否则只改焦点会让视图突然跳变(评审)。
|
// 原"把焦点移到命中点以绕其旋转"实测会让视图跳变,去掉。选中切片高亮 + 供滚轮推进/关闭。
|
||||||
if (renderer_) {
|
|
||||||
if (auto* cam = renderer_->GetActiveCamera()) {
|
|
||||||
double f[3], p[3];
|
|
||||||
cam->GetFocalPoint(f);
|
|
||||||
cam->GetPosition(p);
|
|
||||||
const double d[3] = {worldPoint[0] - f[0], worldPoint[1] - f[1], worldPoint[2] - f[2]};
|
|
||||||
cam->SetFocalPoint(worldPoint[0], worldPoint[1], worldPoint[2]);
|
|
||||||
cam->SetPosition(p[0] + d[0], p[1] + d[1], p[2] + d[2]);
|
|
||||||
renderer_->ResetCameraClippingRange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 若命中点落在某切片上(阈值内),选中之(供滚轮推进/关闭)。
|
|
||||||
const int idx = nearestSlice(worldPoint);
|
const int idx = nearestSlice(worldPoint);
|
||||||
if (idx >= 0) selected_ = idx;
|
if (idx >= 0) {
|
||||||
|
selected_ = idx;
|
||||||
|
updateSelectionVisual();
|
||||||
|
}
|
||||||
safeRender();
|
safeRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,6 +147,7 @@ void InteractionManager::onDoubleClicked(const Vec3& worldPoint) {
|
||||||
auto* cam = renderer_->GetActiveCamera();
|
auto* cam = renderer_->GetActiveCamera();
|
||||||
if (!cam) return;
|
if (!cam) return;
|
||||||
selected_ = idx;
|
selected_ = idx;
|
||||||
|
updateSelectionVisual();
|
||||||
const Vec3 focal = slices_[static_cast<std::size_t>(idx)]->center();
|
const Vec3 focal = slices_[static_cast<std::size_t>(idx)]->center();
|
||||||
const Vec3 normal = slices_[static_cast<std::size_t>(idx)]->normal();
|
const Vec3 normal = slices_[static_cast<std::size_t>(idx)]->normal();
|
||||||
const double dist = cam->GetDistance(); // 保持当前观察距离
|
const double dist = cam->GetDistance(); // 保持当前观察距离
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,9 @@ private:
|
||||||
// 统一重绘:析构进行中(destroying_)跳过,避免 Qt 拆台时对半析构窗口 Render 崩溃(评审 H3)。
|
// 统一重绘:析构进行中(destroying_)跳过,避免 Qt 拆台时对半析构窗口 Render 崩溃(评审 H3)。
|
||||||
void safeRender();
|
void safeRender();
|
||||||
|
|
||||||
|
// 按 selected_ 刷新各切片高亮(选中亮黄、其余暗淡)。
|
||||||
|
void updateSelectionVisual();
|
||||||
|
|
||||||
vtkRenderWindowInteractor* interactor_;
|
vtkRenderWindowInteractor* interactor_;
|
||||||
vtkRenderWindow* renderWindow_;
|
vtkRenderWindow* renderWindow_;
|
||||||
vtkRenderer* renderer_;
|
vtkRenderer* renderer_;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <vtkImageData.h>
|
#include <vtkImageData.h>
|
||||||
#include <vtkImagePlaneWidget.h>
|
#include <vtkImagePlaneWidget.h>
|
||||||
#include <vtkLookupTable.h>
|
#include <vtkLookupTable.h>
|
||||||
|
#include <vtkProperty.h>
|
||||||
#include <vtkRenderWindowInteractor.h>
|
#include <vtkRenderWindowInteractor.h>
|
||||||
#include <vtkTrivialProducer.h>
|
#include <vtkTrivialProducer.h>
|
||||||
|
|
||||||
|
|
@ -129,6 +130,20 @@ double SliceTool::distanceToPlane(const Vec3& p) const {
|
||||||
return std::abs(dot({p[0] - c[0], p[1] - c[1], p[2] - c[2]}, n));
|
return std::abs(dot({p[0] - c[0], p[1] - c[1], p[2] - c[2]}, n));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SliceTool::setSelected(bool sel) {
|
||||||
|
if (!widget_) return;
|
||||||
|
// 切片边框 = widget 的 PlaneProperty:选中→亮黄粗线,未选中→暗灰细线。
|
||||||
|
if (auto* prop = widget_->GetPlaneProperty()) {
|
||||||
|
if (sel) {
|
||||||
|
prop->SetColor(1.0, 0.9, 0.1);
|
||||||
|
prop->SetLineWidth(3.0);
|
||||||
|
} else {
|
||||||
|
prop->SetColor(0.5, 0.5, 0.5);
|
||||||
|
prop->SetLineWidth(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SliceTool::close() {
|
void SliceTool::close() {
|
||||||
if (!widget_) return;
|
if (!widget_) return;
|
||||||
widget_->Off();
|
widget_->Off();
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,9 @@ public:
|
||||||
// 沿法向推进切面(滚轮,D46):origin += normal*step,夹在 image 包围盒内。
|
// 沿法向推进切面(滚轮,D46):origin += normal*step,夹在 image 包围盒内。
|
||||||
void advance(double step);
|
void advance(double step);
|
||||||
|
|
||||||
|
// 选中视觉反馈:选中→高亮边框(亮黄+粗线),未选中→暗淡细线。
|
||||||
|
void setSelected(bool sel);
|
||||||
|
|
||||||
// 世界点到本切面(无限平面)的垂直距离绝对值。供 picker 命中判定"点在哪张切片上"。
|
// 世界点到本切面(无限平面)的垂直距离绝对值。供 picker 命中判定"点在哪张切片上"。
|
||||||
double distanceToPlane(const Vec3& worldPoint) const;
|
double distanceToPlane(const Vec3& worldPoint) const;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue