feat/vtk-3d-view #7

Merged
gaozheng merged 301 commits from feat/vtk-3d-view into main 2026-06-27 18:43:52 +08:00
3 changed files with 94 additions and 12 deletions
Showing only changes of commit 0212fb5d2e - Show all commits

View File

@ -22,8 +22,13 @@
#include <QToolButton>
#include <QVBoxLayout>
#include <qwt_plot.h>
#include "FormKit.hpp"
#include "Theme.hpp" // scaledPx
#include "dto/DatasetChartDto.hpp" // parseDatasetAnomaliesJSON→Anomaly
#include "panels/chart/ColorMapService.hpp"
#include "panels/chart/ContourPlotItem.hpp"
#include "repo/IDatasetCommandRepository.hpp"
namespace geopro::app {
@ -61,12 +66,14 @@ QString fmt2(bool valid, double x) {
AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandRepository* repo,
QString dsObjectId, QString projectId,
std::vector<double> gridValues, QWidget* parent)
const geopro::core::Grid& grid,
const geopro::core::ColorScale& scale, QWidget* parent)
: QDialog(parent),
repo_(repo),
dsObjectId_(std::move(dsObjectId)),
projectId_(std::move(projectId)),
gridValues_(std::move(gridValues)) {
grid_(grid),
scale_(scale) {
setWindowTitle(QStringLiteral("自动标注"));
setModal(true);
resize(geopro::app::scaledPx(1400), geopro::app::scaledPx(600));
@ -89,9 +96,11 @@ AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandReposito
leftWrap->setLayout(leftCol);
split->addWidget(leftWrap, 35);
// ── 右:上统计 + 下预览表 ───────────────────────────────────────────────
// ── 右:上(统计条 + 预览图) + 下预览表 ──────────────────────────────────
// 对照原版右上 <ContourPreview>:等值面预览图为主、数据统计在上。
auto* rightCol = new QVBoxLayout();
buildStatsBar(rightCol);
buildPreviewPlot(rightCol);
detectedLabel_ = new QLabel(QStringLiteral("自动标注结果"), this);
rightCol->addWidget(detectedLabel_);
@ -128,10 +137,19 @@ AutoAnnotationDialog::AutoAnnotationDialog(geopro::data::IDatasetCommandReposito
loadExceptionTypes(); // 拉面异常类型
}
AutoAnnotationDialog::~AutoAnnotationDialog() {
// previewItem_ 由 QwtPlot autoDelete=true 处理,但析构顺序不确定:先 detach 再交还。
if (previewItem_) {
previewItem_->detach();
delete previewItem_;
previewItem_ = nullptr;
}
// colorSvc_ 为 unique_ptr自动释放。
}
void AutoAnnotationDialog::buildStatsBar(QVBoxLayout* into) {
// 数据统计max/min/mean/median从网格标量算。预览图后置当前以统计条替代
// 后续如复用 ContourPlotItem 渲染异常预览图可在此区追加。
const Stats s = computeStats(gridValues_);
// 数据统计max/min/mean/median从网格标量算
const Stats s = computeStats(grid_.values());
auto* bar = new QFrame(this);
bar->setFrameShape(QFrame::StyledPanel);
auto* lay = new QHBoxLayout(bar);
@ -146,6 +164,49 @@ void AutoAnnotationDialog::buildStatsBar(QVBoxLayout* into) {
into->addWidget(bar);
}
void AutoAnnotationDialog::buildPreviewPlot(QVBoxLayout* into) {
// 复刻原版 <ContourPreview>:用 GridDataChartView 同款 ContourPlotItem 渲染当前网格等值面,
// 预览图为主区域;执行/删除时把预演异常实时叠加refreshPreviewAnomalies
previewPlot_ = new QwtPlot(this);
previewPlot_->setObjectName(QStringLiteral("autoAnnotPreview"));
previewPlot_->enableAxis(QwtPlot::xBottom, true);
previewPlot_->enableAxis(QwtPlot::xTop, false);
previewPlot_->enableAxis(QwtPlot::yLeft, true);
previewPlot_->setMinimumHeight(geopro::app::scaledPx(220));
// 网格 < 2×2 视为无可渲染数据:仅占位,不建等值面项。
if (grid_.nx() < 2 || grid_.ny() < 2) {
into->addWidget(previewPlot_, 1);
return;
}
colorSvc_ = std::make_unique<ColorMapService>(scale_);
previewItem_ = new ContourPlotItem();
// 轻量预览:渲染等值面 + 等值线,关标注(小图标注过密);初始无异常。
previewItem_->setData(grid_, colorSvc_.get(), {}, /*showLines=*/true, /*showLabels=*/false);
previewItem_->setShowAnomalies(true);
previewItem_->attach(previewPlot_);
// 轴范围 = 数据范围y 深度向下:上沿 ymax、下沿 ymin与 GridDataChartView 一致)。
const QRectF bbox = previewItem_->boundingRect();
if (!bbox.isNull()) {
previewPlot_->setAxisScale(QwtPlot::xBottom, bbox.left(), bbox.right());
previewPlot_->setAxisScale(QwtPlot::yLeft, bbox.top(), bbox.bottom());
}
previewPlot_->replot();
into->addWidget(previewPlot_, 1);
}
void AutoAnnotationDialog::refreshPreviewAnomalies() {
// 把当前 previewExceptions_execute 返回 / 删除后剩余)映射成 Anomaly 叠加到预览图。
// 复用 dto::parseDatasetAnomalies与正式异常同一 JSON 形态location.coordinate + legend
if (!previewItem_ || !colorSvc_) return;
const auto anoms = geopro::data::dto::parseDatasetAnomalies(previewExceptions_);
previewItem_->setData(grid_, colorSvc_.get(), anoms, /*showLines=*/true, /*showLabels=*/false);
previewItem_->setShowAnomalies(true);
if (previewPlot_) previewPlot_->replot();
}
void AutoAnnotationDialog::loadExceptionTypes() {
if (!repo_) return;
QPointer<AutoAnnotationDialog> self(this);
@ -362,6 +423,7 @@ void AutoAnnotationDialog::onExecute() {
self->previewTable_->setCellWidget(i, 5, delBtn);
}
self->saveBtn_->setEnabled(!list.isEmpty());
self->refreshPreviewAnomalies(); // 实时叠加预演异常到预览图
if (list.isEmpty())
QMessageBox::information(self, self->windowTitle(), QStringLiteral("暂未识别到异常"));
});
@ -397,6 +459,7 @@ void AutoAnnotationDialog::deletePreviewRow(int row) {
detectedLabel_->setText(
QStringLiteral("自动标注结果(共识别到 %1 个异常)").arg(previewExceptions_.size()));
saveBtn_->setEnabled(!previewExceptions_.isEmpty());
refreshPreviewAnomalies(); // 同步从预览图移除该异常
}
void AutoAnnotationDialog::onSave() {

View File

@ -2,8 +2,12 @@
#include <QDialog>
#include <QJsonArray>
#include <QString>
#include <memory>
#include <vector>
#include "model/Field.hpp" // core::Grid预览图等值面
#include "model/ColorScale.hpp" // core::ColorScale预览图色阶
class QButtonGroup;
class QComboBox;
class QLineEdit;
@ -14,6 +18,7 @@ class QToolButton;
class QLabel;
class QVBoxLayout;
class QWidget;
class QwtPlot;
namespace geopro::data {
class IDatasetCommandRepository;
@ -21,6 +26,9 @@ class IDatasetCommandRepository;
namespace geopro::app {
class ColorMapService;
class ContourPlotItem;
// 自动标注对话框I13复刻原版 AutoAnnotationDialog1400×600
// 左规则卡片列表标题「规则N」+ 折叠 + 删除;阈值模式 radio-button 数值/百分位、min/max、
// 最小点数、异常类型)+「添加规则」。
@ -31,10 +39,12 @@ namespace geopro::app {
class AutoAnnotationDialog : public QDialog {
Q_OBJECT
public:
// gridValues网格标量用于右上数据统计 max/min/mean/median可空统计显示 '-')。
// grid/scale当前反演网格 + 色阶用于右上预览图ContourPlotItem 渲染等值面)
// 及数据统计max/min/mean/median 从 grid.values() 算)。
AutoAnnotationDialog(geopro::data::IDatasetCommandRepository* repo, QString dsObjectId,
QString projectId, std::vector<double> gridValues,
QWidget* parent = nullptr);
QString projectId, const geopro::core::Grid& grid,
const geopro::core::ColorScale& scale, QWidget* parent = nullptr);
~AutoAnnotationDialog() override;
private:
// 一条规则卡片的控件集合(卡片标题/折叠/删除 + 模式 radio + min/max + 最小点数 + 类型)。
@ -50,6 +60,8 @@ private:
};
void buildStatsBar(QVBoxLayout* into); // 右上统计条max/min/mean/median
void buildPreviewPlot(QVBoxLayout* into); // 右上预览图QwtPlot + ContourPlotItem 等值面)
void refreshPreviewAnomalies(); // 把 previewExceptions_ 映射成 Anomaly 叠加到预览图并重绘
void loadExceptionTypes(); // 拉面异常类型(填充所有规则卡片下拉)
void addRule(); // 加一条规则卡片
void removeRule(QWidget* frame); // 删除指定卡片(至少保留一条)
@ -63,7 +75,14 @@ private:
geopro::data::IDatasetCommandRepository* repo_ = nullptr;
QString dsObjectId_;
QString projectId_;
std::vector<double> gridValues_;
geopro::core::Grid grid_; // 反演网格(预览图等值面 + 统计)
geopro::core::ColorScale scale_; // 网格色阶(预览图取色)
// 预览图(复用 GridDataChartView 的 ContourPlotItem 渲染等值面 + 异常叠加)。
// colorSvc_ 非 QObject 手动持有previewItem_ 由 QwtPlot autoDelete析构前 detach。
QwtPlot* previewPlot_ = nullptr;
std::unique_ptr<ColorMapService> colorSvc_;
ContourPlotItem* previewItem_ = nullptr;
QVBoxLayout* ruleHost_ = nullptr;
std::vector<RuleCard> rules_;

View File

@ -499,8 +499,8 @@ void GridDataChartView::openAutoAnnotation() {
const QString dsId = dsIdGetter_ ? dsIdGetter_() : QString();
const QString projectId = projectIdGetter_ ? projectIdGetter_() : QString();
if (!cmdRepo_ || dsId.isEmpty()) { showNotImplemented(nullptr); return; }
// 透传网格标量用于右上数据统计max/min/mean/median
AutoAnnotationDialog dlg(cmdRepo_, dsId, projectId, grid_.values(), this);
// 透传网格 + 色阶右上预览图ContourPlotItem 等值面)+ 数据统计max/min/mean/median
AutoAnnotationDialog dlg(cmdRepo_, dsId, projectId, grid_, gridScale_, this);
if (dlg.exec() == QDialog::Accepted) reloadGrid();
}