#include "nmWxPropertyInterpolationPreviewDlg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { QString previewText(const char* sourceText) { return QCoreApplication::translate("nmWxPropertyInterpolationPreviewDlg", sourceText); } vtkSmartPointer createPreviewLookupTable(double minimum, double maximum) { vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetNumberOfTableValues(256); lookupTable->SetRange(minimum, maximum); lookupTable->SetHueRange(0.60, 0.02); lookupTable->SetSaturationRange(0.50, 0.50); lookupTable->SetValueRange(0.96, 0.96); lookupTable->Build(); return lookupTable; } void showPreviewFailure(vtkRenderer* renderer, vtkRenderWindow* renderWindow, const QString& message) { if(renderer == NULL || renderWindow == NULL) { return; } vtkSmartPointer messageActor = vtkSmartPointer::New(); 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); renderer->AddActor2D(messageActor); renderWindow->Render(); } } nmWxPropertyInterpolationPreviewDlg::nmWxPropertyInterpolationPreviewDlg( const QString& dataSetName, const QString& scalarTitle, const QRectF& bounds, int columnCount, int rowCount, const QVector& interpolationValues, const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, const QVector& wellPoints, const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels, QWidget* parent) : iDlgBase(parent), m_pVtkWidget(NULL) { setWindowModality(Qt::NonModal); setModal(false); setMinimumSize(900, 700); resize(1200, 850); QVBoxLayout* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(8, 8, 8, 8); mainLayout->setSpacing(8); m_pVtkWidget = new QVTKWidget(this); m_pVtkWidget->setMinimumSize(860, 640); mainLayout->addWidget(m_pVtkWidget, 1); QHBoxLayout* buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); QPushButton* closeButton = new QPushButton(previewText("Close"), this); buttonLayout->addWidget(closeButton); mainLayout->addLayout(buttonLayout); connect(closeButton, SIGNAL(clicked()), this, SLOT(close())); updatePreview(dataSetName, scalarTitle, bounds, columnCount, rowCount, interpolationValues, measurementPoints, measurementValues, outlinePoints, wellPoints, wellNames, showMeasurementPoints, showMeasurementLabels); } void nmWxPropertyInterpolationPreviewDlg::updatePreview( const QString& dataSetName, const QString& scalarTitle, const QRectF& bounds, int columnCount, int rowCount, const QVector& interpolationValues, const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, const QVector& wellPoints, const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels) { setWindowTitle(previewText("Interpolation Preview") + " - " + dataSetName); initializeRenderer(scalarTitle, bounds, columnCount, rowCount, interpolationValues, measurementPoints, measurementValues, outlinePoints, wellPoints, wellNames, 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 renderer = vtkSmartPointer::New(); renderer->SetBackground(0.88, 0.88, 0.88); vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->SetMultiSamples(0); renderWindow->AddRenderer(renderer); m_pVtkWidget->SetRenderWindow(renderWindow); showPreviewFailure(renderer, renderWindow, message); } void nmWxPropertyInterpolationPreviewDlg::initializeRenderer( const QString& scalarTitle, const QRectF& bounds, int columnCount, int rowCount, const QVector& interpolationValues, const QVector& measurementPoints, const QVector& measurementValues, const QVector& outlinePoints, const QVector& wellPoints, const QStringList& wellNames, bool showMeasurementPoints, bool showMeasurementLabels) { if(m_pVtkWidget == NULL) { return; } // 显式创建渲染窗口,避免使用尚未初始化的QVTKWidget默认窗口. vtkSmartPointer renderer = vtkSmartPointer::New(); renderer->SetBackground(0.88, 0.88, 0.88); vtkSmartPointer renderWindow = vtkSmartPointer::New(); renderWindow->SetMultiSamples(0); renderWindow->AddRenderer(renderer); m_pVtkWidget->SetRenderWindow(renderWindow); if(columnCount < 2 || rowCount < 2 || bounds.width() <= 0.0 || bounds.height() <= 0.0 || interpolationValues.size() != columnCount * rowCount) { showPreviewFailure(renderer, renderWindow, previewText("Preview Failed")); return; } // 规则采样点使用Kriging结果,测点作为额外顶点直接使用已知值. vtkSmartPointer surfacePoints = vtkSmartPointer::New(); vtkSmartPointer scalarArray = vtkSmartPointer::New(); scalarArray->SetName("InterpolatedValue"); scalarArray->SetNumberOfComponents(1); 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; } } if(matchingPointId >= 0) { scalarArray->SetValue(matchingPointId, measurementValues[i]); } else { surfacePoints->InsertNextPoint(measurementPoints[i].x(), measurementPoints[i].y(), 0.0); scalarArray->InsertNextValue(measurementValues[i]); } } } vtkSmartPointer surfaceData = vtkSmartPointer::New(); surfaceData->SetPoints(surfacePoints); surfaceData->GetPointData()->SetScalars(scalarArray); vtkSmartPointer triangulationFilter = vtkSmartPointer::New(); triangulationFilter->SetInputData(surfaceData); triangulationFilter->SetProjectionPlaneMode(VTK_DELAUNAY_XY_PLANE); triangulationFilter->SetTolerance(0.0); triangulationFilter->BoundingTriangulationOff(); triangulationFilter->Update(); // 默认使用完整三角网格;仅在裁剪结果有效且保留标量时切换. vtkPolyData* visibleSurface = triangulationFilter->GetOutput(); vtkSmartPointer extractFilter; vtkSmartPointer selectionLoop; vtkSmartPointer selectionPoints; if(outlinePoints.size() >= 3) { selectionPoints = vtkSmartPointer::New(); for(int i = 0; i < outlinePoints.size(); ++i) { selectionPoints->InsertNextPoint(outlinePoints[i].x(), outlinePoints[i].y(), 0.0); } selectionLoop = vtkSmartPointer::New(); selectionLoop->SetLoop(selectionPoints); selectionLoop->AutomaticNormalGenerationOff(); selectionLoop->SetNormal(0.0, 0.0, 1.0); extractFilter = vtkSmartPointer::New(); extractFilter->SetInputConnection(triangulationFilter->GetOutputPort()); extractFilter->SetImplicitFunction(selectionLoop); extractFilter->ExtractInsideOn(); extractFilter->ExtractBoundaryCellsOn(); extractFilter->Update(); vtkPolyData* extractedSurface = extractFilter->GetOutput(); if(extractedSurface != NULL && extractedSurface->GetNumberOfPoints() > 0 && extractedSurface->GetPointData() != NULL && extractedSurface->GetPointData()->GetScalars() != NULL) { visibleSurface = extractedSurface; } } if(visibleSurface == NULL || visibleSurface->GetNumberOfPoints() == 0 || visibleSurface->GetPointData() == NULL || visibleSurface->GetPointData()->GetScalars() == NULL) { showPreviewFailure(renderer, renderWindow, previewText("Preview Failed")); return; } vtkDataArray* interpolatedScalars = 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); 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; displayMaximum += padding; } vtkSmartPointer lookupTable = createPreviewLookupTable(displayMinimum, displayMaximum); // 将连续点标量划分为8级色带,Mapper按CellData进行分级着色. const int colorBandCount = 8; vtkSmartPointer bandFilter = vtkSmartPointer::New(); bandFilter->SetInputData(visibleSurface); bandFilter->GenerateValues(colorBandCount + 1, displayMinimum, displayMaximum); bandFilter->SetScalarModeToValue(); bandFilter->GenerateContourEdgesOff(); vtkSmartPointer surfaceMapper = vtkSmartPointer::New(); surfaceMapper->SetInputConnection(bandFilter->GetOutputPort()); surfaceMapper->SetScalarModeToUseCellData(); surfaceMapper->SetLookupTable(lookupTable); surfaceMapper->SetScalarRange(displayMinimum, displayMaximum); surfaceMapper->ScalarVisibilityOn(); vtkSmartPointer surfaceActor = vtkSmartPointer::New(); surfaceActor->SetMapper(surfaceMapper); surfaceActor->GetProperty()->EdgeVisibilityOff(); renderer->AddActor(surfaceActor); // 叠加储层边界,并通过轻微Z偏移避免与色带表面重合. if(outlinePoints.size() >= 3) { vtkSmartPointer outlineVtkPoints = vtkSmartPointer::New(); for(int i = 0; i < outlinePoints.size(); ++i) { outlineVtkPoints->InsertNextPoint(outlinePoints[i].x(), outlinePoints[i].y(), 0.01); } vtkSmartPointer outlineLines = vtkSmartPointer::New(); outlineLines->InsertNextCell(outlinePoints.size() + 1); for(int i = 0; i < outlinePoints.size(); ++i) { outlineLines->InsertCellPoint(i); } outlineLines->InsertCellPoint(0); vtkSmartPointer outlineData = vtkSmartPointer::New(); outlineData->SetPoints(outlineVtkPoints); outlineData->SetLines(outlineLines); vtkSmartPointer outlineMapper = vtkSmartPointer::New(); outlineMapper->SetInputData(outlineData); outlineMapper->ScalarVisibilityOff(); vtkSmartPointer outlineActor = vtkSmartPointer::New(); outlineActor->SetMapper(outlineMapper); outlineActor->GetProperty()->SetColor(0.55, 0.38, 0.22); outlineActor->GetProperty()->SetLineWidth(1.2); renderer->AddActor(outlineActor); } // 测点和数值标签复用同一组坐标和值,叠加显示在插值面上. if((showMeasurementPoints || showMeasurementLabels) && !measurementPoints.isEmpty() && measurementPoints.size() == measurementValues.size()) { vtkSmartPointer measurementVtkPoints = vtkSmartPointer::New(); vtkSmartPointer measurementScalars = vtkSmartPointer::New(); measurementScalars->SetName("MeasurementValue"); measurementScalars->SetNumberOfComponents(1); for(int i = 0; i < measurementPoints.size(); ++i) { measurementVtkPoints->InsertNextPoint(measurementPoints[i].x(), measurementPoints[i].y(), 0.02); measurementScalars->InsertNextValue(measurementValues[i]); } vtkSmartPointer measurementData = vtkSmartPointer::New(); measurementData->SetPoints(measurementVtkPoints); measurementData->GetPointData()->SetScalars(measurementScalars); if(showMeasurementPoints) { vtkSmartPointer glyphFilter = vtkSmartPointer::New(); glyphFilter->SetInputData(measurementData); // 深色外层形成清晰描边,避免测点颜色与附近插值色带融为一体. vtkSmartPointer borderMapper = vtkSmartPointer::New(); borderMapper->SetInputConnection(glyphFilter->GetOutputPort()); borderMapper->ScalarVisibilityOff(); vtkSmartPointer borderActor = vtkSmartPointer::New(); borderActor->SetMapper(borderMapper); borderActor->GetProperty()->SetColor(0.10, 0.10, 0.10); borderActor->GetProperty()->SetPointSize(9.0); renderer->AddActor(borderActor); vtkSmartPointer measurementMapper = vtkSmartPointer::New(); measurementMapper->SetInputConnection(glyphFilter->GetOutputPort()); measurementMapper->SetLookupTable(lookupTable); measurementMapper->SetScalarRange(displayMinimum, displayMaximum); measurementMapper->SetScalarModeToUsePointData(); vtkSmartPointer measurementActor = vtkSmartPointer::New(); measurementActor->SetMapper(measurementMapper); measurementActor->GetProperty()->SetPointSize(5.0); measurementActor->SetPosition(0.0, 0.0, 0.001); renderer->AddActor(measurementActor); } if(showMeasurementLabels) { // 标签使用独立坐标上移,测点方框仍保持在原始测量位置. const double labelOffset = qMax(bounds.width(), bounds.height()) * 0.018; vtkSmartPointer measurementLabelPoints = vtkSmartPointer::New(); for(int i = 0; i < measurementPoints.size(); ++i) { measurementLabelPoints->InsertNextPoint( measurementPoints[i].x(), measurementPoints[i].y() + labelOffset, 0.03); } vtkSmartPointer measurementLabelData = vtkSmartPointer::New(); measurementLabelData->SetPoints(measurementLabelPoints); measurementLabelData->GetPointData()->SetScalars( measurementScalars); vtkSmartPointer labelMapper = vtkSmartPointer::New(); labelMapper->SetInputData(measurementLabelData); labelMapper->SetLabelModeToLabelScalars(); labelMapper->SetLabelFormat("%.6g"); labelMapper->GetLabelTextProperty()->SetColor(0.12, 0.12, 0.12); labelMapper->GetLabelTextProperty()->SetFontSize(12); labelMapper->GetLabelTextProperty()->BoldOff(); labelMapper->GetLabelTextProperty()->ItalicOff(); labelMapper->GetLabelTextProperty()->ShadowOff(); labelMapper->GetLabelTextProperty()->SetJustificationToCentered(); labelMapper->GetLabelTextProperty()->SetVerticalJustificationToBottom(); vtkSmartPointer labelActor = vtkSmartPointer::New(); labelActor->SetMapper(labelMapper); renderer->AddActor2D(labelActor); } } // Map中显示的井使用独立红色标记和名称叠加,不参与插值表面计算. if(!wellPoints.isEmpty() && wellPoints.size() == wellNames.size()) { vtkSmartPointer wellVtkPoints = vtkSmartPointer::New(); vtkSmartPointer wellNameArray = vtkSmartPointer::New(); wellNameArray->SetName("WellName"); for(int i = 0; i < wellPoints.size(); ++i) { wellVtkPoints->InsertNextPoint(wellPoints[i].x(), wellPoints[i].y(), 0.04); wellNameArray->InsertNextValue(wellNames[i].toUtf8().constData()); } vtkSmartPointer wellData = vtkSmartPointer::New(); wellData->SetPoints(wellVtkPoints); wellData->GetPointData()->AddArray(wellNameArray); vtkSmartPointer wellGlyphFilter = vtkSmartPointer::New(); wellGlyphFilter->SetInputData(wellData); vtkSmartPointer wellBorderMapper = vtkSmartPointer::New(); wellBorderMapper->SetInputConnection(wellGlyphFilter->GetOutputPort()); wellBorderMapper->ScalarVisibilityOff(); vtkSmartPointer wellBorderActor = vtkSmartPointer::New(); wellBorderActor->SetMapper(wellBorderMapper); wellBorderActor->GetProperty()->SetColor(0.12, 0.12, 0.12); wellBorderActor->GetProperty()->SetPointSize(9.0); renderer->AddActor(wellBorderActor); vtkSmartPointer wellMapper = vtkSmartPointer::New(); wellMapper->SetInputConnection(wellGlyphFilter->GetOutputPort()); wellMapper->ScalarVisibilityOff(); vtkSmartPointer wellActor = vtkSmartPointer::New(); wellActor->SetMapper(wellMapper); wellActor->GetProperty()->SetColor(0.90, 0.12, 0.10); wellActor->GetProperty()->SetPointSize(5.0); wellActor->SetPosition(0.0, 0.0, 0.001); renderer->AddActor(wellActor); // 井名同样使用独立坐标,避免文字覆盖井位标记. const double wellLabelOffset = qMax(bounds.width(), bounds.height()) * 0.022; vtkSmartPointer wellLabelPoints = vtkSmartPointer::New(); for(int i = 0; i < wellPoints.size(); ++i) { wellLabelPoints->InsertNextPoint( wellPoints[i].x(), wellPoints[i].y() + wellLabelOffset, 0.05); } vtkSmartPointer wellLabelData = vtkSmartPointer::New(); wellLabelData->SetPoints(wellLabelPoints); wellLabelData->GetPointData()->AddArray(wellNameArray); vtkSmartPointer wellLabelMapper = vtkSmartPointer::New(); wellLabelMapper->SetInputData(wellLabelData); wellLabelMapper->SetLabelModeToLabelFieldData(); wellLabelMapper->SetFieldDataName("WellName"); wellLabelMapper->SetLabelFormat("%s"); wellLabelMapper->GetLabelTextProperty()->SetColor(0.10, 0.10, 0.10); wellLabelMapper->GetLabelTextProperty()->SetFontSize(13); wellLabelMapper->GetLabelTextProperty()->BoldOff(); wellLabelMapper->GetLabelTextProperty()->ItalicOff(); wellLabelMapper->GetLabelTextProperty()->ShadowOff(); wellLabelMapper->GetLabelTextProperty()->SetJustificationToCentered(); wellLabelMapper->GetLabelTextProperty()->SetVerticalJustificationToBottom(); vtkSmartPointer wellLabelActor = vtkSmartPointer::New(); wellLabelActor->SetMapper(wellLabelMapper); renderer->AddActor2D(wellLabelActor); } vtkSmartPointer scalarBar = vtkSmartPointer::New(); scalarBar->SetLookupTable(lookupTable); scalarBar->SetTitle(scalarTitle.toUtf8().constData()); scalarBar->SetNumberOfLabels(6); scalarBar->SetPosition(0.88, 0.12); scalarBar->SetWidth(0.08); scalarBar->SetHeight(0.76); scalarBar->UnconstrainedFontSizeOn(); scalarBar->GetLabelTextProperty()->SetColor(0.15, 0.15, 0.15); scalarBar->GetLabelTextProperty()->SetFontSize(11); scalarBar->GetLabelTextProperty()->BoldOff(); scalarBar->GetLabelTextProperty()->ItalicOff(); scalarBar->GetLabelTextProperty()->ShadowOff(); scalarBar->GetTitleTextProperty()->SetColor(0.15, 0.15, 0.15); scalarBar->GetTitleTextProperty()->SetFontSize(13); scalarBar->GetTitleTextProperty()->BoldOff(); scalarBar->GetTitleTextProperty()->ItalicOff(); scalarBar->GetTitleTextProperty()->ShadowOff(); renderer->AddActor2D(scalarBar); // 使用正交相机按模型边界居中显示,保持平面比例并避免透视变形. vtkCamera* camera = renderer->GetActiveCamera(); const double centerX = bounds.left() + bounds.width() * 0.5; const double centerY = bounds.top() + bounds.height() * 0.5; camera->SetPosition(centerX, centerY, 1.0); camera->SetFocalPoint(centerX, centerY, 0.0); camera->SetViewUp(0.0, 1.0, 0.0); camera->ParallelProjectionOn(); double cameraBounds[6] = { bounds.left(), bounds.right(), bounds.top(), bounds.bottom(), -0.1, 0.1 }; renderer->ResetCamera(cameraBounds); camera->SetParallelScale(qMax(bounds.height(), bounds.width()) * 0.52); vtkSmartPointer interactorStyle = vtkSmartPointer::New(); renderWindow->GetInteractor()->SetInteractorStyle(interactorStyle); renderWindow->Render(); }