#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) { if(renderer == NULL || renderWindow == NULL) { return; } vtkSmartPointer messageActor = vtkSmartPointer::New(); messageActor->SetInput(previewText("Preview Failed").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, bool showMeasurementPoints, bool showMeasurementLabels, QWidget* parent) : iDlgBase(parent), m_pVtkWidget(NULL) { setWindowTitle(previewText("Interpolation Preview") + " - " + dataSetName); setWindowModality(Qt::WindowModal); 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())); initializeRenderer(scalarTitle, bounds, columnCount, rowCount, interpolationValues, measurementPoints, measurementValues, outlinePoints, showMeasurementPoints, showMeasurementLabels); } 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, 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); return; } // 标量按行优先顺序保存,与对话框生成规则待插值点的顺序一致. vtkSmartPointer scalarArray = vtkSmartPointer::New(); scalarArray->SetName("InterpolatedValue"); scalarArray->SetNumberOfComponents(1); scalarArray->SetNumberOfTuples(interpolationValues.size()); for(int i = 0; i < interpolationValues.size(); ++i) { scalarArray->SetValue(i, interpolationValues[i]); } vtkSmartPointer imageData = vtkSmartPointer::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 geometryFilter = vtkSmartPointer::New(); geometryFilter->SetInputData(imageData); geometryFilter->Update(); // 默认使用完整规则网格;仅在裁剪结果有效且保留标量时切换. vtkPolyData* visibleSurface = geometryFilter->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(geometryFilter->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); return; } vtkDataArray* interpolatedScalars = visibleSurface->GetPointData()->GetScalars(); visibleSurface->GetPointData()->SetScalars(interpolatedScalars); double displayRange[2] = { 0.0, 0.0 }; visibleSurface->GetPointData()->GetScalars()->GetRange(displayRange); double displayMinimum = displayRange[0]; double 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); // 将连续点标量划分为10级色带,Mapper按CellData进行分级着色. const int colorBandCount = 10; 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 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(8.0); renderer->AddActor(measurementActor); } if(showMeasurementLabels) { vtkSmartPointer labelMapper = vtkSmartPointer::New(); labelMapper->SetInputData(measurementData); 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(); vtkSmartPointer labelActor = vtkSmartPointer::New(); labelActor->SetMapper(labelMapper); labelActor->SetPosition(0.0, 10.0); renderer->AddActor2D(labelActor); } } 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(); }