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
5 changed files with 50 additions and 34 deletions
Showing only changes of commit beb398d478 - Show all commits

View File

@ -473,13 +473,10 @@ void buildWorkbench(QMainWindow& window, geopro::data::LocalSampleRepository& re
const auto vols = scene3dRepo->volumeRows(); const auto vols = scene3dRepo->volumeRows();
const auto slices = scene3dRepo->sliceRows(); const auto slices = scene3dRepo->sliceRows();
const auto anomalies = scene3dRepo->anomalyRows(); const auto anomalies = scene3dRepo->anomalyRows();
// 电阻率/视/瞬变段=对象树反演 dsvoxel/slice 段先由 splitByCategory 按 ddCode 填体/切片。 // 电阻率/视/瞬变段=对象树反演 dssplitByCategory 按 dsTypeCode 分voxel 段下方单独喂三级树)。
std::vector<geopro::data::DsRow> forCat = *lastSourceRows; drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(*lastSourceRows));
for (const auto& v : vols) forCat.push_back(v); // 三维体段=「体→切片/异常」三级树:体(根)+切片(parentId=体)+异常(parentId=体/切片)
for (const auto& s : slices) forCat.push_back(s); // 切片不单列段只在此树spec §8 修订。populateDatasetList 按 parentId 自动建树。
drawer->analysisTab()->setBuckets(geopro::app::splitByCategory(forCat));
// 三维体段重置为「体→切片/异常」三级树:体(根)+切片(parentId=体)+异常(parentId=体/切片)
// populateDatasetList 按 parentId 自动建三级树spec §8
std::vector<geopro::data::DsRow> voxelTree = vols; std::vector<geopro::data::DsRow> voxelTree = vols;
for (const auto& s : slices) voxelTree.push_back(s); for (const auto& s : slices) voxelTree.push_back(s);
for (const auto& a : anomalies) voxelTree.push_back(a); for (const auto& a : anomalies) voxelTree.push_back(a);

View File

@ -29,23 +29,37 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
root->setContentsMargins(0, 0, 0, 0); root->setContentsMargins(0, 0, 0, 0);
root->setSpacing(0); root->setSpacing(0);
// 折叠头:标题 + 箭头(点击展开/收起段体)。 // 数据类型标题行spec §7折叠箭头+标题(左) | 「+新增三维体」(右,仅反演类,在标题行而非筛选行)。
header_ = new QToolButton(this); auto* headerRow = new QWidget(this);
auto* hl = new QHBoxLayout(headerRow);
hl->setContentsMargins(space::kSm, 0, space::kSm, 0);
hl->setSpacing(space::kSm);
header_ = new QToolButton(headerRow);
header_->setText(QString::fromStdString(spec_.title)); header_->setText(QString::fromStdString(spec_.title));
header_->setCheckable(true); header_->setCheckable(true);
header_->setChecked(true); header_->setChecked(true);
header_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); header_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
header_->setArrowType(Qt::DownArrow); header_->setArrowType(Qt::DownArrow);
header_->setAutoRaise(true); header_->setAutoRaise(true);
header_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); hl->addWidget(header_);
root->addWidget(header_); hl->addStretch(1);
if (spec_.canGenerateVolume) {
auto* gen = new QToolButton(headerRow);
gen->setText(QStringLiteral("+ 新增三维体"));
gen->setAutoRaise(true);
connect(gen, &QToolButton::clicked, this, [this] {
emit generateVolumeRequested(QString::fromStdString(spec_.dsTypeCode), checkedDsIds());
});
hl->addWidget(gen);
}
root->addWidget(headerRow);
body_ = new QWidget(this); body_ = new QWidget(this);
auto* body = new QVBoxLayout(body_); auto* body = new QVBoxLayout(body_);
body->setContentsMargins(space::kMd, space::kSm, space::kMd, space::kMd); body->setContentsMargins(space::kMd, space::kSm, space::kMd, space::kMd);
body->setSpacing(space::kSm); body->setSpacing(space::kSm);
// 段头筛选行:装置类型(仅 ERT 类)+ 采集时间下限 +「+新增三维体」(仅反演类)。 // 筛选行:装置类型(仅 ERT 类)+ 采集时间段(起止;最小日期显示"不限",不再露 1752)。
auto* filterRow = new QHBoxLayout(); auto* filterRow = new QHBoxLayout();
if (spec_.hasArrayTypeFilter) { if (spec_.hasArrayTypeFilter) {
arrayCombo_ = new QComboBox(body_); arrayCombo_ = new QComboBox(body_);
@ -54,21 +68,21 @@ CategorySection::CategorySection(const CategorySpec& spec, geopro::data::Dataset
[this](int) { rebuildList(); }); [this](int) { rebuildList(); });
filterRow->addWidget(arrayCombo_); filterRow->addWidget(arrayCombo_);
} }
fromDate_ = new QDateEdit(body_); auto makeDate = [this]() {
fromDate_->setCalendarPopup(true); auto* d = new QDateEdit(body_);
fromDate_->setDisplayFormat(QStringLiteral("yyyy-MM-dd")); d->setCalendarPopup(true);
fromDate_->setDate(fromDate_->minimumDate()); // 最小日期=不限(哨兵) d->setDisplayFormat(QStringLiteral("yyyy-MM-dd"));
fromDate_->setToolTip(QStringLiteral("采集时间下限(设为最小日期=不限)")); d->setSpecialValueText(QStringLiteral("不限")); // 最小日期→显示"不限"
connect(fromDate_, &QDateEdit::dateChanged, this, [this](const QDate&) { rebuildList(); }); d->setDate(d->minimumDate());
filterRow->addWidget(new QLabel(QStringLiteral("起始日期"), body_)); connect(d, &QDateEdit::dateChanged, this, [this](const QDate&) { rebuildList(); });
return d;
};
fromDate_ = makeDate();
toDate_ = makeDate();
filterRow->addWidget(new QLabel(QStringLiteral("采集"), body_));
filterRow->addWidget(fromDate_, 1); filterRow->addWidget(fromDate_, 1);
if (spec_.canGenerateVolume) { filterRow->addWidget(new QLabel(QStringLiteral(""), body_));
auto* gen = new QPushButton(QStringLiteral("+ 新增三维体"), body_); filterRow->addWidget(toDate_, 1);
connect(gen, &QPushButton::clicked, this, [this] {
emit generateVolumeRequested(QString::fromStdString(spec_.dsTypeCode), checkedDsIds());
});
filterRow->addWidget(gen);
}
body->addLayout(filterRow); body->addLayout(filterRow);
// 段体:可勾选数据树(勾选=渲染)。复用数据列表卡片委托与 populateDatasetList。 // 段体:可勾选数据树(勾选=渲染)。复用数据列表卡片委托与 populateDatasetList。
@ -138,13 +152,18 @@ bool CategorySection::passesFilters(const DsRow& row) const {
if (!hit) return false; if (!hit) return false;
} }
} }
// 采集时间下限collectTime 经 dict 取;缺则回退 createTime最小日期=不限)。 // 采集时间段collectTime 经 dict 取;缺则回退 createTime最小日期="不限"不约束)。
if (fromDate_ && fromDate_->date() > fromDate_->minimumDate()) { const bool hasFrom = fromDate_ && fromDate_->date() > fromDate_->minimumDate();
const bool hasTo = toDate_ && toDate_->date() > toDate_->minimumDate();
if (hasFrom || hasTo) {
const DsTypeFields* f = dict_ ? dict_->fields(spec_.dsTypeCode) : nullptr; const DsTypeFields* f = dict_ ? dict_->fields(spec_.dsTypeCode) : nullptr;
std::string ts = f ? collectTimeOf(row, *f) : std::string(); std::string ts = f ? collectTimeOf(row, *f) : std::string();
if (ts.empty()) ts = row.createTime; if (ts.empty()) ts = row.createTime;
const QDate d = QDate::fromString(QString::fromStdString(ts).left(10), QStringLiteral("yyyy-MM-dd")); const QDate d = QDate::fromString(QString::fromStdString(ts).left(10), QStringLiteral("yyyy-MM-dd"));
if (d.isValid() && d < fromDate_->date()) return false; if (d.isValid()) {
if (hasFrom && d < fromDate_->date()) return false;
if (hasTo && d > toDate_->date()) return false;
}
} }
return true; return true;
} }

View File

@ -51,7 +51,8 @@ private:
QToolButton* header_ = nullptr; // 折叠头(标题 + 箭头) QToolButton* header_ = nullptr; // 折叠头(标题 + 箭头)
QWidget* body_ = nullptr; // 段体容器(折叠时隐藏) QWidget* body_ = nullptr; // 段体容器(折叠时隐藏)
QComboBox* arrayCombo_ = nullptr; // 装置类型筛选(仅 hasArrayTypeFilter QComboBox* arrayCombo_ = nullptr; // 装置类型筛选(仅 hasArrayTypeFilter
QDateEdit* fromDate_ = nullptr; // 采集时间下限 QDateEdit* fromDate_ = nullptr; // 采集时间段·起
QDateEdit* toDate_ = nullptr; // 采集时间段·止
QTreeWidget* list_ = nullptr; QTreeWidget* list_ = nullptr;
}; };

View File

@ -21,7 +21,7 @@ inline const std::vector<CategorySpec>& categoryConfigs() {
{"apparent", "视电阻率数据", "visual resistivity data", "", true, true}, {"apparent", "视电阻率数据", "visual resistivity data", "", true, true},
{"transient", "瞬变电磁数据", "DD TRANSIENT ELECTROMAGNETIC INVERSION", "", true, false}, {"transient", "瞬变电磁数据", "DD TRANSIENT ELECTROMAGNETIC INVERSION", "", true, false},
{"voxel", "三维体", "", "dd_voxel", false, false}, {"voxel", "三维体", "", "dd_voxel", false, false},
{"slice", "切片", "", "dd_slice", false, false}, // 切片不单列段——挂在三维体段「体→切片/异常」三级树下spec §8 修订)。
}; };
return kCfg; return kCfg;
} }

View File

@ -28,11 +28,10 @@ TEST(SplitByCategory, RoutesByDsTypeCodeAndDdCode) {
EXPECT_EQ(b.segments[1].size(), 1u); EXPECT_EQ(b.segments[1][0].id, "b"); EXPECT_EQ(b.segments[1].size(), 1u); EXPECT_EQ(b.segments[1][0].id, "b");
EXPECT_EQ(b.segments[2].size(), 1u); EXPECT_EQ(b.segments[2][0].id, "c"); EXPECT_EQ(b.segments[2].size(), 1u); EXPECT_EQ(b.segments[2][0].id, "c");
EXPECT_EQ(b.segments[3].size(), 1u); EXPECT_EQ(b.segments[3][0].id, "v"); EXPECT_EQ(b.segments[3].size(), 1u); EXPECT_EQ(b.segments[3][0].id, "v");
EXPECT_EQ(b.segments[4].size(), 1u); EXPECT_EQ(b.segments[4][0].id, "s"); // 切片(dd_slice)不单列段——挂三维体树,不进任何段;接地电阻同样丢弃。
// 接地电阻不进任何段。
std::size_t total = 0; std::size_t total = 0;
for (auto& s : b.segments) total += s.size(); for (auto& s : b.segments) total += s.size();
EXPECT_EQ(total, 5u); EXPECT_EQ(total, 4u);
} }
TEST(SplitByCategory, PreservesOrderWithinSegment) { TEST(SplitByCategory, PreservesOrderWithinSegment) {