fix(ui): 等值线用vtkStripper连成长折线(使沿线数值标注可落上) + 网格页(图+异常)分割器放入页签内QScrollArea(滚动限页签内) + 等值线测试
This commit is contained in:
parent
53dbcee052
commit
4fbab033f0
|
|
@ -183,7 +183,7 @@ void ContourPlotItem::draw(QPainter* painter, const QwtScaleMap& xMap, const Qwt
|
|||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
QPen pen(QColor(0, 0, 0));
|
||||
pen.setWidthF(0.0); // cosmetic:恒 1px,不随缩放变粗
|
||||
pen.setWidthF(1.0); // 1px 黑色等值线
|
||||
painter->setPen(pen);
|
||||
for (const auto& ln : lines_) {
|
||||
if (ln.pts.size() < 2) continue;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include <qwt_plot.h>
|
||||
#include <qwt_plot_grid.h>
|
||||
#include <QSplitter>
|
||||
#include <QScrollArea>
|
||||
#include <QFrame>
|
||||
#include <qwt_plot_rescaler.h>
|
||||
|
||||
#include "PanelHeader.hpp"
|
||||
|
|
@ -141,15 +143,24 @@ GridDataChartView::GridDataChartView(QWidget* parent) : QWidget(parent) {
|
|||
};
|
||||
auto tabbedBottom = buildTabbedPanel(bottomTabs, {});
|
||||
|
||||
// 图表区 | 底部表 用竖直 QSplitter:自适应 dock 高度、可拖拽、不强制固定 200px
|
||||
//(避免内容溢出滚动条;也使本页与「原数据」页高度互不耦合)。
|
||||
auto* splitter = new QSplitter(Qt::Vertical, this);
|
||||
// 图表区 | 底部表 竖直 QSplitter(可拖拽调整比例)。给定最小高度→分割器有最小尺寸。
|
||||
chartArea->setMinimumHeight(280);
|
||||
tabbedBottom.container->setMinimumHeight(160);
|
||||
auto* splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->addWidget(chartArea);
|
||||
splitter->addWidget(tabbedBottom.container);
|
||||
splitter->setStretchFactor(0, 3);
|
||||
splitter->setStretchFactor(1, 1);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
lay->addWidget(splitter, 1);
|
||||
|
||||
// 页签内滚动:把(图表+异常)分割器放进 QScrollArea。dock 够高→分割器填满(可拖动调整);
|
||||
// dock 太矮→在页签内部出现竖滚动条(工具条/页签/标题固定),而非整个面板滚动。
|
||||
auto* scroll = new QScrollArea(this);
|
||||
scroll->setWidgetResizable(true);
|
||||
scroll->setFrameShape(QFrame::NoFrame);
|
||||
scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
scroll->setWidget(splitter);
|
||||
lay->addWidget(scroll, 1);
|
||||
|
||||
// ---- 工具条开关 → 重建/重绘 ----
|
||||
connect(chkShowAnom, &QCheckBox::toggled, this, [this](bool on) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <vtkPointData.h>
|
||||
#include <vtkPoints.h>
|
||||
#include <vtkPolyData.h>
|
||||
#include <vtkStripper.h>
|
||||
#include <vtkUnstructuredGrid.h>
|
||||
|
||||
#include <cmath>
|
||||
|
|
@ -152,14 +153,20 @@ ContourBandsResult buildContourBands(const Grid& g, const ColorScale& cs, const
|
|||
out.bands.push_back(std::move(bp));
|
||||
}
|
||||
|
||||
// port1:等值线(polylines)+ DP 简化。仅 makeLines=true 时生成(否则不提取)。
|
||||
// port1:等值线。banded 边输出是大量 2 点短线段——用 vtkStripper 连成长折线,
|
||||
// 便于沿线标注与减少绘制调用。仅 makeLines=true 时生成。
|
||||
if (opt.makeLines) {
|
||||
vtkPolyData* edges = banded->GetContourEdgesOutput();
|
||||
if (edges) {
|
||||
edges->BuildCells();
|
||||
const vtkIdType nLines = edges->GetNumberOfCells();
|
||||
vtkNew<vtkStripper> stripper;
|
||||
stripper->SetInputData(edges);
|
||||
stripper->JoinContiguousSegmentsOn();
|
||||
stripper->Update();
|
||||
vtkPolyData* joined = stripper->GetOutput();
|
||||
joined->BuildCells();
|
||||
const vtkIdType nLines = joined->GetNumberOfCells();
|
||||
for (vtkIdType c = 0; c < nLines; ++c) {
|
||||
vtkCell* cell = edges->GetCell(c);
|
||||
vtkCell* cell = joined->GetCell(c);
|
||||
vtkPoints* cp = cell->GetPoints();
|
||||
ContourLine cl; cl.level = 0.0;
|
||||
for (vtkIdType p = 0; p < cp->GetNumberOfPoints(); ++p) {
|
||||
|
|
|
|||
|
|
@ -53,3 +53,38 @@ TEST(ContourBands, ProducesNonEmptyBands) {
|
|||
ASSERT_FALSE(r.bands.empty());
|
||||
for (const auto& b : r.bands) EXPECT_GE(b.ring.size(), 3u);
|
||||
}
|
||||
|
||||
// 诊断:等值线(lines)应非空——网格跨越多个色阶级时应产生等值线。
|
||||
TEST(ContourBands, ProducesContourLines) {
|
||||
Grid g(10, 10);
|
||||
g.x.resize(10); g.y.resize(10);
|
||||
for (int i = 0; i < 10; ++i) { g.x[i] = i; g.y[i] = i; }
|
||||
for (int j = 0; j < 10; ++j)
|
||||
for (int i = 0; i < 10; ++i) g.valueAt(i, j) = i + j; // 0..18
|
||||
g.vmin = 0; g.vmax = 18;
|
||||
ColorScale cs;
|
||||
cs.addStop(0, Rgba{0,0,255,255}); cs.addStop(6, Rgba{0,255,0,255});
|
||||
cs.addStop(12, Rgba{255,255,0,255}); cs.addStop(18, Rgba{255,0,0,255});
|
||||
ContourOptions opt; opt.upsample = 1; opt.smooth = 0; opt.simplifyTol = 0; opt.makeLines = true;
|
||||
auto r = buildContourBands(g, cs, opt);
|
||||
EXPECT_FALSE(r.lines.empty());
|
||||
}
|
||||
|
||||
// 诊断(真实参数复现):大网格 + 默认 opt(upsample=2,smooth=0.3,simplifyTol=0.5) + 17 级色阶。
|
||||
TEST(ContourBands, ProducesLinesRealisticOptions) {
|
||||
Grid g(60, 40);
|
||||
g.x.resize(60); g.y.resize(40);
|
||||
for (int i = 0; i < 60; ++i) g.x[i] = i * 3.0;
|
||||
for (int j = 0; j < 40; ++j) g.y[j] = -33.0 + j * 1.2;
|
||||
for (int j = 0; j < 40; ++j)
|
||||
for (int i = 0; i < 60; ++i)
|
||||
g.valueAt(i, j) = 2.0 + 340.0 * (0.5 + 0.5 * std::sin(i * 0.2) * std::cos(j * 0.25));
|
||||
g.vmin = 2; g.vmax = 348;
|
||||
ColorScale cs;
|
||||
const double bars[17] = {2.08,4.59,7.81,11.02,14.26,17.25,20.56,23.55,27.05,
|
||||
31.16,36.83,43.64,51.98,62.09,81.14,108.24,348.52};
|
||||
for (int i = 0; i < 17; ++i) cs.addStop(bars[i], Rgba{(unsigned char)(i*15),0,200,255});
|
||||
ContourOptions opt; // 默认 upsample=2, smooth=0.3, simplifyTol=0.5, makeLines=true
|
||||
auto r = buildContourBands(g, cs, opt);
|
||||
EXPECT_FALSE(r.lines.empty());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue