You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmSubWxs/nmWxPropertyInterpolationPr...

501 lines
20 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmWxPropertyInterpolationPreviewDlg.h"
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QVBoxLayout>
#include <QVTKWidget.h>
#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkBandedPolyDataContourFilter.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkDataArray.h>
#include <vtkDelaunay2D.h>
#include <vtkDoubleArray.h>
#include <vtkExtractPolyDataGeometry.h>
#include <vtkImplicitSelectionLoop.h>
#include <vtkInteractorStyleImage.h>
#include <vtkLabeledDataMapper.h>
#include <vtkLookupTable.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkScalarBarActor.h>
#include <vtkSmartPointer.h>
#include <vtkTextActor.h>
#include <vtkTextProperty.h>
#include <vtkVertexGlyphFilter.h>
namespace
{
QString previewText(const char* sourceText)
{
return QCoreApplication::translate("nmWxPropertyInterpolationPreviewDlg", sourceText);
}
vtkSmartPointer<vtkLookupTable> createPreviewLookupTable(double minimum,
double maximum)
{
vtkSmartPointer<vtkLookupTable> lookupTable =
vtkSmartPointer<vtkLookupTable>::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<vtkTextActor> messageActor =
vtkSmartPointer<vtkTextActor>::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<double>& interpolationValues,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
const QVector<QPointF>& outlinePoints,
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,
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,
int columnCount,
int rowCount,
const QVector<double>& interpolationValues,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
const QVector<QPointF>& outlinePoints,
bool showMeasurementPoints,
bool showMeasurementLabels)
{
if(m_pVtkWidget == NULL) {
return;
}
// 显式创建渲染窗口避免使用尚未初始化的QVTKWidget默认窗口.
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);
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<vtkPoints> surfacePoints =
vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkDoubleArray> scalarArray =
vtkSmartPointer<vtkDoubleArray>::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<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;
if(outlinePoints.size() >= 3) {
selectionPoints = vtkSmartPointer<vtkPoints>::New();
for(int i = 0; i < outlinePoints.size(); ++i) {
selectionPoints->InsertNextPoint(outlinePoints[i].x(),
outlinePoints[i].y(), 0.0);
}
selectionLoop = vtkSmartPointer<vtkImplicitSelectionLoop>::New();
selectionLoop->SetLoop(selectionPoints);
selectionLoop->AutomaticNormalGenerationOff();
selectionLoop->SetNormal(0.0, 0.0, 1.0);
extractFilter = vtkSmartPointer<vtkExtractPolyDataGeometry>::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<vtkLookupTable> lookupTable =
createPreviewLookupTable(displayMinimum, displayMaximum);
// 将连续点标量划分为10级色带Mapper按CellData进行分级着色.
const int colorBandCount = 10;
vtkSmartPointer<vtkBandedPolyDataContourFilter> bandFilter =
vtkSmartPointer<vtkBandedPolyDataContourFilter>::New();
bandFilter->SetInputData(visibleSurface);
bandFilter->GenerateValues(colorBandCount + 1,
displayMinimum, displayMaximum);
bandFilter->SetScalarModeToValue();
bandFilter->GenerateContourEdgesOff();
vtkSmartPointer<vtkPolyDataMapper> surfaceMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
surfaceMapper->SetInputConnection(bandFilter->GetOutputPort());
surfaceMapper->SetScalarModeToUseCellData();
surfaceMapper->SetLookupTable(lookupTable);
surfaceMapper->SetScalarRange(displayMinimum, displayMaximum);
surfaceMapper->ScalarVisibilityOn();
vtkSmartPointer<vtkActor> surfaceActor = vtkSmartPointer<vtkActor>::New();
surfaceActor->SetMapper(surfaceMapper);
surfaceActor->GetProperty()->EdgeVisibilityOff();
renderer->AddActor(surfaceActor);
// 叠加储层边界并通过轻微Z偏移避免与色带表面重合.
if(outlinePoints.size() >= 3) {
vtkSmartPointer<vtkPoints> outlineVtkPoints =
vtkSmartPointer<vtkPoints>::New();
for(int i = 0; i < outlinePoints.size(); ++i) {
outlineVtkPoints->InsertNextPoint(outlinePoints[i].x(),
outlinePoints[i].y(), 0.01);
}
vtkSmartPointer<vtkCellArray> outlineLines =
vtkSmartPointer<vtkCellArray>::New();
outlineLines->InsertNextCell(outlinePoints.size() + 1);
for(int i = 0; i < outlinePoints.size(); ++i) {
outlineLines->InsertCellPoint(i);
}
outlineLines->InsertCellPoint(0);
vtkSmartPointer<vtkPolyData> outlineData =
vtkSmartPointer<vtkPolyData>::New();
outlineData->SetPoints(outlineVtkPoints);
outlineData->SetLines(outlineLines);
vtkSmartPointer<vtkPolyDataMapper> outlineMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
outlineMapper->SetInputData(outlineData);
outlineMapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> outlineActor = vtkSmartPointer<vtkActor>::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<vtkPoints> measurementVtkPoints =
vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkDoubleArray> measurementScalars =
vtkSmartPointer<vtkDoubleArray>::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<vtkPolyData> measurementData =
vtkSmartPointer<vtkPolyData>::New();
measurementData->SetPoints(measurementVtkPoints);
measurementData->GetPointData()->SetScalars(measurementScalars);
if(showMeasurementPoints) {
vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter =
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());
measurementMapper->SetLookupTable(lookupTable);
measurementMapper->SetScalarRange(displayMinimum, displayMaximum);
measurementMapper->SetScalarModeToUsePointData();
vtkSmartPointer<vtkActor> measurementActor =
vtkSmartPointer<vtkActor>::New();
measurementActor->SetMapper(measurementMapper);
measurementActor->GetProperty()->SetPointSize(5.0);
measurementActor->SetPosition(0.0, 0.0, 0.001);
renderer->AddActor(measurementActor);
}
if(showMeasurementLabels) {
vtkSmartPointer<vtkLabeledDataMapper> labelMapper =
vtkSmartPointer<vtkLabeledDataMapper>::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<vtkActor2D> labelActor =
vtkSmartPointer<vtkActor2D>::New();
labelActor->SetMapper(labelMapper);
labelActor->SetPosition(0.0, 10.0);
renderer->AddActor2D(labelActor);
}
}
vtkSmartPointer<vtkScalarBarActor> scalarBar =
vtkSmartPointer<vtkScalarBarActor>::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<vtkInteractorStyleImage> interactorStyle =
vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindow->GetInteractor()->SetInteractorStyle(interactorStyle);
renderWindow->Render();
}