feat/vtk-3d-view #7
|
|
@ -398,6 +398,17 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
viewToolbar->move(12, 12);
|
viewToolbar->move(12, 12);
|
||||||
viewToolbar->raise();
|
viewToolbar->raise();
|
||||||
viewToolbar->show();
|
viewToolbar->show();
|
||||||
|
// 异常绘制操作提示:右上角 QLabel 浮层(VTK 内置字体不含中文字形,故用 Qt 渲染中文 + QSS 美化)。
|
||||||
|
// 深底 + accent 描边;不挡画布鼠标事件;绘制开始显示、结束/取消隐藏(见 onSliceContextMenuRequested)。
|
||||||
|
auto* anomalyHint = new QLabel(vtkWidget);
|
||||||
|
anomalyHint->setObjectName(QStringLiteral("anomalyHint"));
|
||||||
|
anomalyHint->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
geopro::app::applyTokenizedStyleSheet(
|
||||||
|
anomalyHint,
|
||||||
|
QStringLiteral("QLabel#anomalyHint{background:rgba(10,18,30,0.85);color:#E6ECF5;"
|
||||||
|
"border:1px solid {{accent/primary}};border-radius:8px;padding:8px 12px;}"));
|
||||||
|
anomalyHint->hide();
|
||||||
|
|
||||||
// 坐标轴设置抽屉面板:叠加 vtkWidget、工具条右侧滑出,默认隐藏(点设置 toggle)。
|
// 坐标轴设置抽屉面板:叠加 vtkWidget、工具条右侧滑出,默认隐藏(点设置 toggle)。
|
||||||
auto* axesPanel = new geopro::app::AxesSettingsPanel(vtkWidget);
|
auto* axesPanel = new geopro::app::AxesSettingsPanel(vtkWidget);
|
||||||
axesPanel->hide();
|
axesPanel->hide();
|
||||||
|
|
@ -506,7 +517,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
// 正视/翻转/关闭=接现有交互(关闭已保存切片→onSliceClosed 取消列表勾选);创建异常=占位(#4)。
|
// 正视/翻转/关闭=接现有交互(关闭已保存切片→onSliceClosed 取消列表勾选);创建异常=占位(#4)。
|
||||||
interactionMgr->onSliceContextMenuRequested =
|
interactionMgr->onSliceContextMenuRequested =
|
||||||
[&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, refreshAnalysis,
|
[&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, refreshAnalysis,
|
||||||
refreshAnomalies, drawer, anomalyDrawTool, renderWindowPtr]() {
|
refreshAnomalies, drawer, anomalyDrawTool, renderWindowPtr, anomalyHint, vtkWidget]() {
|
||||||
QMenu menu(&window);
|
QMenu menu(&window);
|
||||||
QMenu* anomMenu = menu.addMenu(QStringLiteral("创建异常")); // → 点/线/面 子菜单
|
QMenu* anomMenu = menu.addMenu(QStringLiteral("创建异常")); // → 点/线/面 子菜单
|
||||||
QAction* aAnoPoint = anomMenu->addAction(QStringLiteral("点"));
|
QAction* aAnoPoint = anomMenu->addAction(QStringLiteral("点"));
|
||||||
|
|
@ -543,6 +554,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
const ri::Vec3 e1{{p1[0] - o[0], p1[1] - o[1], p1[2] - o[2]}};
|
const ri::Vec3 e1{{p1[0] - o[0], p1[1] - o[1], p1[2] - o[2]}};
|
||||||
const ri::Vec3 e2{{p2[0] - o[0], p2[1] - o[1], p2[2] - o[2]}};
|
const ri::Vec3 e2{{p2[0] - o[0], p2[1] - o[1], p2[2] - o[2]}};
|
||||||
const ri::Vec3 normal = ri::normalize(ri::cross(e1, e2));
|
const ri::Vec3 normal = ri::normalize(ri::cross(e1, e2));
|
||||||
|
// 操作提示浮层(右上角):按形态显示结束方式;绘制结束/取消隐藏。
|
||||||
|
anomalyHint->setText(
|
||||||
|
shape == 1 ? QStringLiteral("标注点\n左键单击落点即完成\nEsc 取消")
|
||||||
|
: shape == 2
|
||||||
|
? QStringLiteral("标注线\n左键逐点 · 双击结束\nBackspace 撤点 · Esc 取消")
|
||||||
|
: QStringLiteral("标注面\n左键逐点 · 点回起点闭合\nBackspace 撤点 · Esc 取消"));
|
||||||
|
anomalyHint->adjustSize();
|
||||||
|
anomalyHint->move(vtkWidget->width() - anomalyHint->width() - 12, 12); // 右上角
|
||||||
|
anomalyHint->show();
|
||||||
|
anomalyHint->raise();
|
||||||
// 多体并发:异常挂到"选中切片所属体"(非 currentVolume),无选中切片回退当前体。
|
// 多体并发:异常挂到"选中切片所属体"(非 currentVolume),无选中切片回退当前体。
|
||||||
std::string volId = interactionMgr->selectedSliceVolumeDsId();
|
std::string volId = interactionMgr->selectedSliceVolumeDsId();
|
||||||
if (volId.empty()) volId = sceneView->currentVolumeDsId();
|
if (volId.empty()) volId = sceneView->currentVolumeDsId();
|
||||||
|
|
@ -551,8 +572,9 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
anomalyDrawTool->start(
|
anomalyDrawTool->start(
|
||||||
mode, o, normal,
|
mode, o, normal,
|
||||||
[&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, renderWindowPtr,
|
[&window, &cmdRepo, &nav, interactionMgr, sceneView, scene3dRepo, renderWindowPtr,
|
||||||
refreshAnomalies, refreshAnalysis, volId, savedSliceId, normal, o, p1, p2,
|
refreshAnomalies, refreshAnalysis, volId, savedSliceId, normal, o, p1, p2, shape,
|
||||||
shape](const std::vector<ri::Vec3>& worldPts) {
|
anomalyHint](const std::vector<ri::Vec3>& worldPts) {
|
||||||
|
anomalyHint->hide(); // 绘制结束 → 隐藏操作提示
|
||||||
// 草稿异常:先临时渲染(让用户在对话框前看到所画,且截图含异常)。
|
// 草稿异常:先临时渲染(让用户在对话框前看到所画,且截图含异常)。
|
||||||
geopro::core::Anomaly a;
|
geopro::core::Anomaly a;
|
||||||
a.markType = static_cast<geopro::core::AnomalyMarkType>(shape);
|
a.markType = static_cast<geopro::core::AnomalyMarkType>(shape);
|
||||||
|
|
@ -632,7 +654,7 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
QString::fromStdString(m));
|
QString::fromStdString(m));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[]() { /* onCancel:放弃,无需处理 */ });
|
[anomalyHint]() { anomalyHint->hide(); }); // 取消(Esc)→隐藏提示
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (aSave != nullptr && chosen == aSave) {
|
if (aSave != nullptr && chosen == aSave) {
|
||||||
|
|
@ -991,12 +1013,16 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
|
||||||
[sceneView, interactionMgr, renderWindowPtr](const QString& dsId,
|
[sceneView, interactionMgr, renderWindowPtr](const QString& dsId,
|
||||||
const QString& ddCode) {
|
const QString& ddCode) {
|
||||||
const std::string id = dsId.toStdString();
|
const std::string id = dsId.toStdString();
|
||||||
|
// 选中项决定高亮:异常↔切片互斥,选其它对象两者都清(否则切到别的对象后切片/异常仍高亮)。
|
||||||
if (ddCode == QStringLiteral("dd_anomaly")) {
|
if (ddCode == QStringLiteral("dd_anomaly")) {
|
||||||
sceneView->setSelectedAnomaly(id);
|
sceneView->setSelectedAnomaly(id);
|
||||||
} else {
|
interactionMgr->deselectSlice();
|
||||||
sceneView->setSelectedAnomaly(std::string{}); // 选中非异常对象→清异常高亮
|
} else if (ddCode == QStringLiteral("dd_slice")) {
|
||||||
if (ddCode == QStringLiteral("dd_slice"))
|
sceneView->setSelectedAnomaly(std::string{});
|
||||||
interactionMgr->selectSavedSlice(id); // 选中已渲染的该切片(高亮)
|
interactionMgr->selectSavedSlice(id); // 选中已渲染的该切片(高亮)
|
||||||
|
} else {
|
||||||
|
sceneView->setSelectedAnomaly(std::string{});
|
||||||
|
interactionMgr->deselectSlice();
|
||||||
}
|
}
|
||||||
renderWindowPtr->Render();
|
renderWindowPtr->Render();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
#include <vtkProperty.h>
|
#include <vtkProperty.h>
|
||||||
#include <vtkRenderWindowInteractor.h>
|
#include <vtkRenderWindowInteractor.h>
|
||||||
#include <vtkRenderer.h>
|
#include <vtkRenderer.h>
|
||||||
#include <vtkTextActor.h>
|
|
||||||
#include <vtkTextProperty.h>
|
|
||||||
|
|
||||||
namespace geopro::render::interact {
|
namespace geopro::render::interact {
|
||||||
|
|
||||||
|
|
@ -55,33 +53,8 @@ void AnomalyDrawTool::start(DrawMode mode, const Vec3& planeOrigin, const Vec3&
|
||||||
hasCursor_ = false;
|
hasCursor_ = false;
|
||||||
active_ = true;
|
active_ = true;
|
||||||
installObservers();
|
installObservers();
|
||||||
|
// 操作提示由 app 层 QLabel 浮层承担(VTK 内置字体不含中文字形 → vtkTextActor 渲染不出中文)。
|
||||||
// 屏幕操作提示(右上角,避开左侧工具条):标题 + 当前形态的结束方式 + 取消,分行排版。
|
|
||||||
if (renderer_) {
|
|
||||||
const char* tip =
|
|
||||||
mode_ == DrawMode::Point
|
|
||||||
? "标注点\n左键单击落点即完成\nEsc 取消"
|
|
||||||
: (mode_ == DrawMode::Line
|
|
||||||
? "标注线\n左键逐点 · 双击结束\nBackspace 撤点 · Esc 取消"
|
|
||||||
: "标注面\n左键逐点 · 点回起点闭合\nBackspace 撤点 · Esc 取消");
|
|
||||||
hint_ = vtkSmartPointer<vtkTextActor>::New();
|
|
||||||
hint_->SetInput(tip);
|
|
||||||
auto* tp = hint_->GetTextProperty();
|
|
||||||
tp->SetFontSize(15);
|
|
||||||
tp->SetLineSpacing(1.3);
|
|
||||||
tp->SetColor(0.90, 0.94, 1.0); // 近白(canvas-text)
|
|
||||||
tp->SetJustificationToRight(); // 右对齐,贴右上
|
|
||||||
tp->SetVerticalJustificationToTop();
|
|
||||||
tp->SetBackgroundColor(0.04, 0.07, 0.12); // 深底
|
|
||||||
tp->SetBackgroundOpacity(0.66);
|
|
||||||
tp->SetFrame(1);
|
|
||||||
tp->SetFrameColor(0.37, 0.55, 0.96); // accent 描边
|
|
||||||
tp->SetFrameWidth(1);
|
|
||||||
hint_->GetPositionCoordinate()->SetCoordinateSystemToNormalizedViewport();
|
|
||||||
hint_->GetPositionCoordinate()->SetValue(0.985, 0.975); // 右上角
|
|
||||||
renderer_->AddViewProp(hint_);
|
|
||||||
if (interactor_) interactor_->Render();
|
if (interactor_) interactor_->Render();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnomalyDrawTool::cancel() {
|
void AnomalyDrawTool::cancel() {
|
||||||
|
|
@ -97,11 +70,9 @@ void AnomalyDrawTool::teardownActive() {
|
||||||
if (renderer_) {
|
if (renderer_) {
|
||||||
if (preview_) renderer_->RemoveViewProp(preview_);
|
if (preview_) renderer_->RemoveViewProp(preview_);
|
||||||
if (rubber_) renderer_->RemoveViewProp(rubber_);
|
if (rubber_) renderer_->RemoveViewProp(rubber_);
|
||||||
if (hint_) renderer_->RemoveViewProp(hint_);
|
|
||||||
}
|
}
|
||||||
preview_ = nullptr;
|
preview_ = nullptr;
|
||||||
rubber_ = nullptr;
|
rubber_ = nullptr;
|
||||||
hint_ = nullptr;
|
|
||||||
active_ = false;
|
active_ = false;
|
||||||
hasCursor_ = false;
|
hasCursor_ = false;
|
||||||
pts_.clear();
|
pts_.clear();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
class vtkRenderWindowInteractor;
|
class vtkRenderWindowInteractor;
|
||||||
class vtkRenderer;
|
class vtkRenderer;
|
||||||
class vtkActor;
|
class vtkActor;
|
||||||
class vtkTextActor;
|
|
||||||
class vtkCallbackCommand;
|
class vtkCallbackCommand;
|
||||||
|
|
||||||
namespace geopro::render::interact {
|
namespace geopro::render::interact {
|
||||||
|
|
@ -61,7 +60,6 @@ private:
|
||||||
|
|
||||||
vtkSmartPointer<vtkActor> preview_; // 已点几何(顶点圆点 + 实线折线)
|
vtkSmartPointer<vtkActor> preview_; // 已点几何(顶点圆点 + 实线折线)
|
||||||
vtkSmartPointer<vtkActor> rubber_; // 末点→光标 虚线橡皮筋
|
vtkSmartPointer<vtkActor> rubber_; // 末点→光标 虚线橡皮筋
|
||||||
vtkSmartPointer<vtkTextActor> hint_; // 屏幕操作提示
|
|
||||||
Vec3 cursorPt_{{0, 0, 0}}; // 当前鼠标在切面上的投影点
|
Vec3 cursorPt_{{0, 0, 0}}; // 当前鼠标在切面上的投影点
|
||||||
bool hasCursor_ = false;
|
bool hasCursor_ = false;
|
||||||
bool cursorNearStart_ = false; // 面模式光标邻近起点 → 橡皮筋指向起点预览闭合
|
bool cursorNearStart_ = false; // 面模式光标邻近起点 → 橡皮筋指向起点预览闭合
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,13 @@ bool InteractionManager::selectSavedSlice(const std::string& dsId) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InteractionManager::deselectSlice() {
|
||||||
|
if (selected_ < 0) return;
|
||||||
|
selected_ = -1;
|
||||||
|
updateSelectionVisual(); // 清高亮(无选中切片)
|
||||||
|
safeRender();
|
||||||
|
}
|
||||||
|
|
||||||
void InteractionManager::selectByTool(const SliceTool* tool) {
|
void InteractionManager::selectByTool(const SliceTool* tool) {
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
for (std::size_t i = 0; i < slices_.size(); ++i)
|
for (std::size_t i = 0; i < slices_.size(); ++i)
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ public:
|
||||||
std::vector<std::string> shownSavedSliceIds() const; // 当前已显示的已保存切片 dsId 列表
|
std::vector<std::string> shownSavedSliceIds() const; // 当前已显示的已保存切片 dsId 列表
|
||||||
// 选中已显示的某 dsId 切片(列表操作定位到对应渲染切片);找到返回 true。
|
// 选中已显示的某 dsId 切片(列表操作定位到对应渲染切片);找到返回 true。
|
||||||
bool selectSavedSlice(const std::string& dsId);
|
bool selectSavedSlice(const std::string& dsId);
|
||||||
|
// 清除切片选中(列表选中切到别的对象/异常时调用,否则 VTK 切片仍高亮,用户反馈)。
|
||||||
|
void deselectSlice();
|
||||||
|
|
||||||
// 关闭选中切片(E56)。无选中则忽略。
|
// 关闭选中切片(E56)。无选中则忽略。
|
||||||
void closeSelected();
|
void closeSelected();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue