实现插值结果实时预览

feature/Interpolation-20260803
lvjunjie 2 weeks ago
parent fde6316a37
commit d3bb18b24f

@ -18,8 +18,10 @@ class QPointF;
class QPushButton;
class QTableWidget;
class QTableWidgetItem;
class QTimer;
class nmGuiPlot;
class nmObjPoint;
class nmWxPropertyInterpolationPreviewDlg;
class NM_SUB_WXS_EXPORT nmWxPropertyInterpolationDlg : public iDlgBase
{
@ -49,6 +51,7 @@ private slots:
void onShowPointsToggled(bool checked);
void onShowLabelsToggled(bool checked);
void onPreview();
void refreshPreview();
private:
// 初始化界面及持久化数据组.
@ -80,6 +83,9 @@ private:
void clearTableSelection();
void updatePointButtons();
void updateCurrentMarkerVisibility();
void schedulePreviewRefresh();
void updatePreviewWindow(bool activateWindow);
void showPreviewError(const QString& message, bool activateWindow);
private:
QPointer<nmGuiPlot> m_pPlot;
@ -119,6 +125,8 @@ private:
// 结果预览及应用.
QPushButton* m_pPreviewButton;
QPushButton* m_pApplyButton;
QPointer<nmWxPropertyInterpolationPreviewDlg> m_pPreviewDialog;
QTimer* m_pPreviewRefreshTimer;
};
#endif // NMWXPROPERTYINTERPOLATIONDLG_H

@ -29,6 +29,19 @@ public:
bool showMeasurementLabels,
QWidget* parent = 0);
void updatePreview(const QString& dataSetName,
const QString& scalarTitle,
const QRectF& bounds,
int columnCount,
int rowCount,
const QVector<double>& interpolationValues,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
const QVector<QPointF>& outlinePoints,
bool showMeasurementPoints,
bool showMeasurementLabels);
void showMessage(const QString& dataSetName, const QString& message);
private:
void initializeRenderer(const QString& scalarTitle,
const QRectF& bounds,

@ -22,6 +22,7 @@
#include <QSize>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QTimer>
#include <QTransform>
#include <QVBoxLayout>
#include <QtCore/qmath.h>
@ -185,13 +186,22 @@ nmWxPropertyInterpolationDlg::nmWxPropertyInterpolationDlg(
m_pRangeSpin(NULL),
m_pModelCombo(NULL),
m_pPreviewButton(NULL),
m_pApplyButton(NULL)
m_pApplyButton(NULL),
m_pPreviewDialog(NULL),
m_pPreviewRefreshTimer(NULL)
{
setWindowTitle(interpolationText("Property Interpolation"));
setWindowModality(Qt::NonModal);
setModal(false);
setMinimumSize(520, 620);
resize(560, 680);
m_pPreviewRefreshTimer = new QTimer(this);
m_pPreviewRefreshTimer->setSingleShot(true);
m_pPreviewRefreshTimer->setInterval(200);
connect(m_pPreviewRefreshTimer, SIGNAL(timeout()),
this, SLOT(refreshPreview()));
initInterpolationUI();
initializeDataSets();
@ -554,6 +564,7 @@ void nmWxPropertyInterpolationDlg::onDataSetChanged(int index)
syncDataSetsToManager(false);
m_nCurrentDataSetIndex = index;
loadDataSet(index);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onAddDataSet()
@ -572,6 +583,7 @@ void nmWxPropertyInterpolationDlg::onAddDataSet()
loadDataSet(newIndex);
updateDataSetButtons();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onDeleteDataSet()
@ -597,6 +609,7 @@ void nmWxPropertyInterpolationDlg::onDeleteDataSet()
loadDataSet(newIndex);
updateDataSetButtons();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onDataSetNameChanged(const QString& name)
@ -609,6 +622,7 @@ void nmWxPropertyInterpolationDlg::onDataSetNameChanged(const QString& name)
m_vecDataSets[m_nCurrentDataSetIndex].name = name;
m_pDataSetCombo->setItemText(m_nCurrentDataSetIndex, name);
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onCurrentDataSetEdited()
@ -619,6 +633,7 @@ void nmWxPropertyInterpolationDlg::onCurrentDataSetEdited()
saveCurrentDataSet();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
bool nmWxPropertyInterpolationDlg::eventFilter(QObject* watched, QEvent* event)
@ -706,6 +721,7 @@ void nmWxPropertyInterpolationDlg::appendPoint(const QPointF& valuePoint)
m_pPointTable->scrollToBottom();
updatePointButtons();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::appendPointRow(
@ -871,6 +887,7 @@ void nmWxPropertyInterpolationDlg::onPointItemChanged(QTableWidgetItem* item)
marker->update();
}
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onDeleteSelectedPoint()
@ -892,6 +909,7 @@ void nmWxPropertyInterpolationDlg::onDeleteSelectedPoint()
clearTableSelection();
updatePointButtons();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onClearPoints()
@ -907,6 +925,7 @@ void nmWxPropertyInterpolationDlg::onClearPoints()
clearTableSelection();
updatePointButtons();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onShowPointsToggled(bool checked)
@ -919,6 +938,7 @@ void nmWxPropertyInterpolationDlg::onShowPointsToggled(bool checked)
m_vecDataSets[m_nCurrentDataSetIndex].showPoints = checked;
updateCurrentMarkerVisibility();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::onShowLabelsToggled(bool checked)
@ -931,6 +951,7 @@ void nmWxPropertyInterpolationDlg::onShowLabelsToggled(bool checked)
m_vecDataSets[m_nCurrentDataSetIndex].showLabels = checked;
updateCurrentMarkerVisibility();
syncDataSetsToManager(true);
schedulePreviewRefresh();
}
void nmWxPropertyInterpolationDlg::clearTableSelection()
@ -950,10 +971,64 @@ void nmWxPropertyInterpolationDlg::updatePointButtons()
}
void nmWxPropertyInterpolationDlg::onPreview()
{
if(m_pPreviewRefreshTimer != NULL) {
m_pPreviewRefreshTimer->stop();
}
updatePreviewWindow(true);
}
void nmWxPropertyInterpolationDlg::schedulePreviewRefresh()
{
if(m_pPreviewDialog.isNull() || !m_pPreviewDialog->isVisible() ||
m_pPreviewRefreshTimer == NULL) {
return;
}
// 连续编辑参数时重置计时,仅对最终状态执行一次插值计算.
m_pPreviewRefreshTimer->start();
}
void nmWxPropertyInterpolationDlg::refreshPreview()
{
if(m_pPreviewDialog.isNull() || !m_pPreviewDialog->isVisible()) {
return;
}
updatePreviewWindow(false);
}
void nmWxPropertyInterpolationDlg::showPreviewError(
const QString& message,
bool activateWindow)
{
const QString errorText = message.isEmpty() ?
interpolationText("Preview Failed") : message;
QString dataSetName;
if(m_nCurrentDataSetIndex >= 0 &&
m_nCurrentDataSetIndex < m_vecDataSets.size()) {
dataSetName = m_vecDataSets[m_nCurrentDataSetIndex].name;
}
if(!m_pPreviewDialog.isNull()) {
m_pPreviewDialog->showMessage(dataSetName, errorText);
if(activateWindow) {
m_pPreviewDialog->show();
m_pPreviewDialog->raise();
m_pPreviewDialog->activateWindow();
}
}
else if(activateWindow) {
QMessageBox::warning(this, interpolationText("Preview Failed"), errorText);
}
}
void nmWxPropertyInterpolationDlg::updatePreviewWindow(bool activateWindow)
{
if(m_nCurrentDataSetIndex < 0 ||
m_nCurrentDataSetIndex >= m_vecDataSets.size() ||
m_pDataManager.isNull()) {
showPreviewError(interpolationText("Preview Failed"), activateWindow);
return;
}
@ -966,9 +1041,9 @@ void nmWxPropertyInterpolationDlg::onPreview()
QRectF previewBounds;
if(!createPreviewOutline(m_pDataManager->getOutlineData(),
outlinePoints, previewBounds)) {
QMessageBox::warning(this,
interpolationText("Preview Failed"),
interpolationText("Please create a valid reservoir boundary before previewing."));
showPreviewError(
interpolationText("Please create a valid reservoir boundary before previewing."),
activateWindow);
return;
}
@ -1016,14 +1091,13 @@ void nmWxPropertyInterpolationDlg::onPreview()
QApplication::restoreOverrideCursor();
if(!calculationSucceeded) {
QMessageBox::warning(this,
interpolationText("Preview Failed"),
errorMessage);
showPreviewError(errorMessage, activateWindow);
return;
}
// 预览窗口只负责裁剪和渲染,不修改数据组及正式求解参数.
nmWxPropertyInterpolationPreviewDlg previewDialog(
if(m_pPreviewDialog.isNull()) {
m_pPreviewDialog = new nmWxPropertyInterpolationPreviewDlg(
dataSet.name,
m_pPropertyCombo->currentText(),
previewBounds,
@ -1036,7 +1110,28 @@ void nmWxPropertyInterpolationDlg::onPreview()
dataSet.showPoints,
dataSet.showLabels,
this);
previewDialog.exec();
m_pPreviewDialog->setAttribute(Qt::WA_DeleteOnClose, true);
}
else {
m_pPreviewDialog->updatePreview(
dataSet.name,
m_pPropertyCombo->currentText(),
previewBounds,
columnCount,
rowCount,
interpolationValues,
measurementPoints,
measurementValues,
outlinePoints,
dataSet.showPoints,
dataSet.showLabels);
}
if(activateWindow) {
m_pPreviewDialog->show();
m_pPreviewDialog->raise();
m_pPreviewDialog->activateWindow();
}
}
void nmWxPropertyInterpolationDlg::updateCurrentMarkerVisibility()

@ -12,10 +12,9 @@
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkDataArray.h>
#include <vtkDelaunay2D.h>
#include <vtkDoubleArray.h>
#include <vtkExtractPolyDataGeometry.h>
#include <vtkImageData.h>
#include <vtkImageDataGeometryFilter.h>
#include <vtkImplicitSelectionLoop.h>
#include <vtkInteractorStyleImage.h>
#include <vtkLabeledDataMapper.h>
@ -55,7 +54,9 @@ vtkSmartPointer<vtkLookupTable> createPreviewLookupTable(double minimum,
return lookupTable;
}
void showPreviewFailure(vtkRenderer* renderer, vtkRenderWindow* renderWindow)
void showPreviewFailure(vtkRenderer* renderer,
vtkRenderWindow* renderWindow,
const QString& message)
{
if(renderer == NULL || renderWindow == NULL) {
return;
@ -63,7 +64,9 @@ void showPreviewFailure(vtkRenderer* renderer, vtkRenderWindow* renderWindow)
vtkSmartPointer<vtkTextActor> messageActor =
vtkSmartPointer<vtkTextActor>::New();
messageActor->SetInput(previewText("Preview Failed").toUtf8().constData());
const QString displayMessage = message.isEmpty() ?
previewText("Preview Failed") : message;
messageActor->SetInput(displayMessage.toUtf8().constData());
messageActor->SetDisplayPosition(24, 24);
messageActor->GetTextProperty()->SetColor(0.75, 0.15, 0.12);
messageActor->GetTextProperty()->SetFontSize(16);
@ -88,8 +91,8 @@ nmWxPropertyInterpolationPreviewDlg::nmWxPropertyInterpolationPreviewDlg(
: iDlgBase(parent),
m_pVtkWidget(NULL)
{
setWindowTitle(previewText("Interpolation Preview") + " - " + dataSetName);
setWindowModality(Qt::WindowModal);
setWindowModality(Qt::NonModal);
setModal(false);
setMinimumSize(900, 700);
resize(1200, 850);
@ -108,12 +111,57 @@ nmWxPropertyInterpolationPreviewDlg::nmWxPropertyInterpolationPreviewDlg(
mainLayout->addLayout(buttonLayout);
connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
updatePreview(dataSetName, scalarTitle, bounds, columnCount, rowCount,
interpolationValues, measurementPoints,
measurementValues, outlinePoints,
showMeasurementPoints, showMeasurementLabels);
}
void nmWxPropertyInterpolationPreviewDlg::updatePreview(
const QString& dataSetName,
const QString& scalarTitle,
const QRectF& bounds,
int columnCount,
int rowCount,
const QVector<double>& interpolationValues,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
const QVector<QPointF>& outlinePoints,
bool showMeasurementPoints,
bool showMeasurementLabels)
{
setWindowTitle(previewText("Interpolation Preview") + " - " + dataSetName);
initializeRenderer(scalarTitle, bounds, columnCount, rowCount,
interpolationValues, measurementPoints,
measurementValues, outlinePoints,
showMeasurementPoints, showMeasurementLabels);
}
void nmWxPropertyInterpolationPreviewDlg::showMessage(
const QString& dataSetName,
const QString& message)
{
if(m_pVtkWidget == NULL) {
return;
}
QString windowTitle = previewText("Interpolation Preview");
if(!dataSetName.isEmpty()) {
windowTitle += " - " + dataSetName;
}
setWindowTitle(windowTitle);
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(0.88, 0.88, 0.88);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetMultiSamples(0);
renderWindow->AddRenderer(renderer);
m_pVtkWidget->SetRenderWindow(renderWindow);
showPreviewFailure(renderer, renderWindow, message);
}
void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
const QString& scalarTitle,
const QRectF& bounds,
@ -143,35 +191,73 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
if(columnCount < 2 || rowCount < 2 ||
bounds.width() <= 0.0 || bounds.height() <= 0.0 ||
interpolationValues.size() != columnCount * rowCount) {
showPreviewFailure(renderer, renderWindow);
showPreviewFailure(renderer, renderWindow, previewText("Preview Failed"));
return;
}
// 标量按行优先顺序保存,与对话框生成规则待插值点的顺序一致.
// 规则采样点使用Kriging结果测点作为额外顶点直接使用已知值.
vtkSmartPointer<vtkPoints> surfacePoints =
vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkDoubleArray> scalarArray =
vtkSmartPointer<vtkDoubleArray>::New();
scalarArray->SetName("InterpolatedValue");
scalarArray->SetNumberOfComponents(1);
scalarArray->SetNumberOfTuples(interpolationValues.size());
for(int i = 0; i < interpolationValues.size(); ++i) {
scalarArray->SetValue(i, interpolationValues[i]);
const double spacingX = bounds.width() / (columnCount - 1);
const double spacingY = bounds.height() / (rowCount - 1);
for(int row = 0; row < rowCount; ++row) {
const double y = bounds.top() + spacingY * row;
for(int column = 0; column < columnCount; ++column) {
const int valueIndex = row * columnCount + column;
const double x = bounds.left() + spacingX * column;
surfacePoints->InsertNextPoint(x, y, 0.0);
scalarArray->InsertNextValue(interpolationValues[valueIndex]);
}
}
if(measurementPoints.size() == measurementValues.size()) {
const double coordinateTolerance = qMax(
qMax(bounds.width(), bounds.height()) * 1.0e-10,
1.0e-12);
const int regularPointCount = surfacePoints->GetNumberOfPoints();
for(int i = 0; i < measurementPoints.size(); ++i) {
vtkIdType matchingPointId = -1;
for(int pointIndex = 0; pointIndex < regularPointCount; ++pointIndex) {
double regularPoint[3] = { 0.0, 0.0, 0.0 };
surfacePoints->GetPoint(pointIndex, regularPoint);
if(qAbs(regularPoint[0] - measurementPoints[i].x()) <= coordinateTolerance &&
qAbs(regularPoint[1] - measurementPoints[i].y()) <= coordinateTolerance) {
matchingPointId = pointIndex;
break;
}
}
vtkSmartPointer<vtkImageData> imageData =
vtkSmartPointer<vtkImageData>::New();
imageData->SetDimensions(columnCount, rowCount, 1);
imageData->SetOrigin(bounds.left(), bounds.top(), 0.0);
imageData->SetSpacing(bounds.width() / (columnCount - 1),
bounds.height() / (rowCount - 1), 1.0);
imageData->GetPointData()->SetScalars(scalarArray);
vtkSmartPointer<vtkImageDataGeometryFilter> geometryFilter =
vtkSmartPointer<vtkImageDataGeometryFilter>::New();
geometryFilter->SetInputData(imageData);
geometryFilter->Update();
// 默认使用完整规则网格;仅在裁剪结果有效且保留标量时切换.
vtkPolyData* visibleSurface = geometryFilter->GetOutput();
if(matchingPointId >= 0) {
scalarArray->SetValue(matchingPointId, measurementValues[i]);
}
else {
surfacePoints->InsertNextPoint(measurementPoints[i].x(),
measurementPoints[i].y(), 0.0);
scalarArray->InsertNextValue(measurementValues[i]);
}
}
}
vtkSmartPointer<vtkPolyData> surfaceData =
vtkSmartPointer<vtkPolyData>::New();
surfaceData->SetPoints(surfacePoints);
surfaceData->GetPointData()->SetScalars(scalarArray);
vtkSmartPointer<vtkDelaunay2D> triangulationFilter =
vtkSmartPointer<vtkDelaunay2D>::New();
triangulationFilter->SetInputData(surfaceData);
triangulationFilter->SetProjectionPlaneMode(VTK_DELAUNAY_XY_PLANE);
triangulationFilter->SetTolerance(0.0);
triangulationFilter->BoundingTriangulationOff();
triangulationFilter->Update();
// 默认使用完整三角网格;仅在裁剪结果有效且保留标量时切换.
vtkPolyData* visibleSurface = triangulationFilter->GetOutput();
vtkSmartPointer<vtkExtractPolyDataGeometry> extractFilter;
vtkSmartPointer<vtkImplicitSelectionLoop> selectionLoop;
vtkSmartPointer<vtkPoints> selectionPoints;
@ -188,7 +274,7 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
selectionLoop->SetNormal(0.0, 0.0, 1.0);
extractFilter = vtkSmartPointer<vtkExtractPolyDataGeometry>::New();
extractFilter->SetInputConnection(geometryFilter->GetOutputPort());
extractFilter->SetInputConnection(triangulationFilter->GetOutputPort());
extractFilter->SetImplicitFunction(selectionLoop);
extractFilter->ExtractInsideOn();
extractFilter->ExtractBoundaryCellsOn();
@ -205,7 +291,7 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
if(visibleSurface == NULL || visibleSurface->GetNumberOfPoints() == 0 ||
visibleSurface->GetPointData() == NULL ||
visibleSurface->GetPointData()->GetScalars() == NULL) {
showPreviewFailure(renderer, renderWindow);
showPreviewFailure(renderer, renderWindow, previewText("Preview Failed"));
return;
}
@ -213,10 +299,23 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
visibleSurface->GetPointData()->GetScalars();
visibleSurface->GetPointData()->SetScalars(interpolatedScalars);
// 颜色条使用测点值范围,避免规则采样未命中测点时最大、最小值向内收缩.
double displayMinimum = 0.0;
double displayMaximum = 0.0;
if(!measurementValues.isEmpty()) {
displayMinimum = measurementValues[0];
displayMaximum = measurementValues[0];
for(int i = 1; i < measurementValues.size(); ++i) {
displayMinimum = qMin(displayMinimum, measurementValues[i]);
displayMaximum = qMax(displayMaximum, measurementValues[i]);
}
}
else {
double displayRange[2] = { 0.0, 0.0 };
visibleSurface->GetPointData()->GetScalars()->GetRange(displayRange);
double displayMinimum = displayRange[0];
double displayMaximum = displayRange[1];
displayMinimum = displayRange[0];
displayMaximum = displayRange[1];
}
if(qAbs(displayMaximum - displayMinimum) < 1.0e-12) {
const double padding = qMax(qAbs(displayMinimum) * 0.01, 1.0e-6);
displayMinimum -= padding;
@ -311,6 +410,19 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
vtkSmartPointer<vtkVertexGlyphFilter>::New();
glyphFilter->SetInputData(measurementData);
// 深色外层形成清晰描边,避免测点颜色与附近插值色带融为一体.
vtkSmartPointer<vtkPolyDataMapper> borderMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
borderMapper->SetInputConnection(glyphFilter->GetOutputPort());
borderMapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> borderActor =
vtkSmartPointer<vtkActor>::New();
borderActor->SetMapper(borderMapper);
borderActor->GetProperty()->SetColor(0.10, 0.10, 0.10);
borderActor->GetProperty()->SetPointSize(9.0);
renderer->AddActor(borderActor);
vtkSmartPointer<vtkPolyDataMapper> measurementMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
measurementMapper->SetInputConnection(glyphFilter->GetOutputPort());
@ -321,7 +433,8 @@ void nmWxPropertyInterpolationPreviewDlg::initializeRenderer(
vtkSmartPointer<vtkActor> measurementActor =
vtkSmartPointer<vtkActor>::New();
measurementActor->SetMapper(measurementMapper);
measurementActor->GetProperty()->SetPointSize(8.0);
measurementActor->GetProperty()->SetPointSize(5.0);
measurementActor->SetPosition(0.0, 0.0, 0.001);
renderer->AddActor(measurementActor);
}

Loading…
Cancel
Save