Merge remote-tracking branch 'origin/feature/Interpolation-20260803' into develop

# Conflicts:
#	Bin/Config/Lang/cn/nmNum_cn.qm   resolved by origin/feature/Interpolation-20260803(远端) version
#	Src/nmNum/nmSubWxs/nmWxParaPropertyPebi.cpp
feature/PebiSolver-Integration-20260818
lh 2 weeks ago
commit a4366fb711

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

@ -6556,4 +6556,229 @@ Average pressure in contour: %2 MPa</source>
<translation>3D</translation>
</message>
</context>
<context>
<name>nmWxPropertyInterpolationDlg</name>
<message>
<source>Property Interpolation</source>
<translation></translation>
</message>
<message>
<source>Dataset:</source>
<translation></translation>
</message>
<message>
<source>Add dataset</source>
<translation></translation>
</message>
<message>
<source>Delete dataset</source>
<translation></translation>
</message>
<message>
<source>Dataset #%1</source>
<translation> #%1</translation>
</message>
<message>
<source>Property:</source>
<translation></translation>
</message>
<message>
<source>Permeability k</source>
<translation> k</translation>
</message>
<message>
<source>Permeability k (mD)</source>
<translation> kmD</translation>
</message>
<message>
<source>Porosity phi</source>
<translation> phi</translation>
</message>
<message>
<source>Reservoir thickness h</source>
<translation> h</translation>
</message>
<message>
<source>Reservoir thickness h (m)</source>
<translation> hm</translation>
</message>
<message>
<source>Name:</source>
<translation></translation>
</message>
<message>
<source>Permeability</source>
<translation></translation>
</message>
<message>
<source>Measurement Points</source>
<translation></translation>
</message>
<message>
<source>New value:</source>
<translation></translation>
</message>
<message>
<source>Select on Map</source>
<translation></translation>
</message>
<message>
<source>X</source>
<translation>X</translation>
</message>
<message>
<source>Y</source>
<translation>Y</translation>
</message>
<message>
<source>Value</source>
<translation></translation>
</message>
<message>
<source>Delete Selected</source>
<translation></translation>
</message>
<message>
<source>Clear</source>
<translation></translation>
</message>
<message>
<source>Show measurement points</source>
<translation></translation>
</message>
<message>
<source>Show labels</source>
<translation></translation>
</message>
<message>
<source>Clear measurement points before changing the property.</source>
<translation></translation>
</message>
<message>
<source>Kriging Parameters</source>
<translation></translation>
</message>
<message>
<source>Nugget:</source>
<translation></translation>
</message>
<message>
<source>Sill:</source>
<translation></translation>
</message>
<message>
<source>Range:</source>
<translation></translation>
</message>
<message>
<source>Model:</source>
<translation></translation>
</message>
<message>
<source>Spherical (0)</source>
<translation>0</translation>
</message>
<message>
<source>Exponential (1)</source>
<translation>1</translation>
</message>
<message>
<source>Gaussian (2)</source>
<translation>2</translation>
</message>
<message>
<source>Use Interpolation in Solver</source>
<translation>使</translation>
</message>
<message>
<source>Preview</source>
<translation></translation>
</message>
<message>
<source>Close</source>
<translation></translation>
</message>
<message>
<source>Preview Failed</source>
<translation></translation>
</message>
<message>
<source>Please create a valid reservoir boundary before previewing.</source>
<translation></translation>
</message>
</context>
<context>
<name>nmWxPropertyInterpolationPreviewDlg</name>
<message>
<source>Interpolation Preview</source>
<translation></translation>
</message>
<message>
<source>Close</source>
<translation></translation>
</message>
</context>
<context>
<name>nmCalculationUtils</name>
<message>
<source>No interpolation points are available.</source>
<translation></translation>
</message>
<message>
<source>At least two measurement points are required.</source>
<translation></translation>
</message>
<message>
<source>Measurement point coordinates and values do not match.</source>
<translation></translation>
</message>
<message>
<source>Kriging parameters are invalid.</source>
<translation>Kriging </translation>
</message>
<message>
<source>The Kriging model is invalid.</source>
<translation>Kriging </translation>
</message>
<message>
<source>An interpolation point contains an invalid coordinate.</source>
<translation></translation>
</message>
<message>
<source>A measurement point contains invalid data.</source>
<translation></translation>
</message>
<message>
<source>Measurement point coordinates cannot be duplicated.</source>
<translation></translation>
</message>
<message>
<source>The solver license file was not found.</source>
<translation></translation>
</message>
<message>
<source>Failed to load HX_NWTM.dll.</source>
<translation> HX_NWTM.dll </translation>
</message>
<message>
<source>The Kriging interface was not found in HX_NWTM.dll.</source>
<translation>HX_NWTM.dll Kriging </translation>
</message>
<message>
<source>Kriging calculation failed: %1</source>
<translation>Kriging %1</translation>
</message>
<message>
<source>Kriging calculation failed.</source>
<translation>Kriging </translation>
</message>
<message>
<source>The Kriging result count does not match the interpolation points.</source>
<translation>Kriging </translation>
</message>
<message>
<source>The Kriging result contains an invalid value.</source>
<translation>Kriging </translation>
</message>
</context>
</TS>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -248,6 +248,7 @@
<Cmd ID="5112" Bounds="0,0,2,1" Name="NmGeometry" Alias="分层" Icon="NmGeometry" />
<Cmd ID="5113" Bounds="0,1,2,1" Name="NmProperties" Alias="储层特性" Icon="NmProperties" />
<Cmd ID="5122" Bounds="0,2,2,1" Name="NmDFN" Alias="DFN" Icon="NmDFN" />
<Cmd ID="5126" Bounds="0,3,2,1" Name="NmInterpolation" Alias="属性插值" Icon="NmInterpolation" />
</Group>
<Group Name="NtModel" Alias="模型生成">

@ -246,6 +246,7 @@
<Cmd ID="5112" Bounds="0,0,2,1" Name="NmGeometry" Alias="Layer Division" Icon="NmGeometry" />
<Cmd ID="5113" Bounds="0,1,2,1" Name="NmProperties" Alias="Reservoir Properties" Icon="NmProperties" />
<Cmd ID="5122" Bounds="0,2,2,1" Name="NmDFN" Alias="DFN" Icon="NmDFN" />
<Cmd ID="5126" Bounds="0,3,2,1" Name="NmInterpolation" Alias="Property Interpolation" Icon="NmInterpolation" />
</Group>
<Group Name="NtModel" Alias="Model Generation">

@ -2,6 +2,8 @@
#define NMCALCULATIONUTILS_H
#include <QString>
#include <QPointF>
#include <QVector>
#include "nmCalculation_global.h"
#include "nmCalculationDefine.h"
#include <iostream>
@ -16,6 +18,20 @@ class NMCALCULATION_EXPORT nmCalculationUtils {
static bool removeDirectory(const QString &dirPath);
static bool writeFile(const QStringList& content, const QString &filePath);
static QStringList readFile(const QString & filePath);
// 调用求解器Kriging接口对targetPoints中的坐标依次进行属性插值.
// measurementPoints与measurementValues按下标一一对应.
// 成功时outputValues与targetPoints数量相同且顺序一致失败时清空outputValues.
static bool calculateKriging(const QVector<QPointF>& targetPoints,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
double nugget,
double sill,
double range,
int model,
const QString& licensePath,
QVector<double>& outputValues,
QString* errorMessage = 0);
};
#endif // NMCALCULATIONUTILS_H

@ -51,6 +51,58 @@ struct BackgroundImageInfo {
QImage objImage; // 具体的QImage对象
};
/* 插值测点数据 */
struct NM_DATA_EXPORT nmPropertyInterpolationPointData {
nmPropertyInterpolationPointData()
: x(0.0), y(0.0), value(0.0)
{
}
nmPropertyInterpolationPointData(double xValue, double yValue, double propertyValue)
: x(xValue), y(yValue), value(propertyValue)
{
}
double x;
double y;
double value;
};
/* 一组属性插值数据及其Kriging参数 */
struct NM_DATA_EXPORT nmPropertyInterpolationDataSet {
nmPropertyInterpolationDataSet()
: property("k"),
xDisplayUnit("m"),
yDisplayUnit("m"),
valueDisplayUnit("mD"),
rangeDisplayUnit("m"),
useForCalculation(false),
showPoints(true),
showLabels(true),
nugget(0.01),
sill(100.0),
range(1000.0),
model(0)
{
}
QString name;
QString property;
// 仅记录界面显示单位坐标、Range及属性值始终按基准单位保存.
QString xDisplayUnit;
QString yDisplayUnit;
QString valueDisplayUnit;
QString rangeDisplayUnit;
bool useForCalculation;
QVector<nmPropertyInterpolationPointData> points;
bool showPoints;
bool showLabels;
double nugget;
double sill;
double range;
int model;
};
enum NM_SOLVER_MODEL_TYPE {
SMT_Oil_ConstPvt = 1,
SMT_Oil_VariablePvt = 2,
@ -433,6 +485,10 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin
// 获取背景图片信息
const BackgroundImageInfo& getBackgroundImageInfo() const;
/* 获取及设置属性插值数据组 */
QVector<nmPropertyInterpolationDataSet> getPropertyInterpolationDataSets() const;
void setPropertyInterpolationDataSets(const QVector<nmPropertyInterpolationDataSet>& dataSets);
// 统一的读写整个项目数据的方法
// @param filePath JSON文件的路径
// @return 读取成功返回 true否则返回 false
@ -647,6 +703,9 @@ class NM_DATA_EXPORT nmDataAnalyzeManager : public ZxDataObjectBin
// 显示设置信息
QVector<DisplaySetting> m_vecDisplaySettings;
/* 属性插值数据组 */
QVector<nmPropertyInterpolationDataSet> m_vecPropertyInterpolationDataSets;
// 背景图片信息
BackgroundImageInfo m_backgroundImageInfo;

@ -42,6 +42,9 @@ class NM_PLOT_EXPORT nmGuiPlot : public iGuiPlot {
// Action命令执行
virtual bool runAction(QString sAction) override;
// 取消当前地图绘图操作.
void cancelActiveTools();
// 创建添加一个Obj
nmObjBase* appendOneObj(NM_Obj_Type o, QString& sName, QVector<QPointF>& vec);
nmObjBase* _createOneObj(NM_Obj_Type o, QString& sName);

@ -19,6 +19,9 @@ public:
virtual bool runAction(QString sAction) override;
// 取消当前地图绘图操作,并恢复默认操作状态.
void cancelActiveTools();
// 插入背景
void insertBkImg();

@ -205,6 +205,8 @@ class NM_SUB_WND_EXPORT nmSubWndMain : public iSubWndBaseFit {
QWidget* showMeasureDialog();
// 显示模型位置对话框
QWidget* showPointerPosDialog();
// 显示属性插值对话框
QWidget* showPropertyInterpolationDialog();
void bindDialogCmdDestroyed(QWidget* dialog, int nId);
void bindMeasureDialog(QWidget* measureDlg);
bool isDialogCmdOpened(int nId) const;

@ -0,0 +1,167 @@
#ifndef NMWXPROPERTYINTERPOLATIONDLG_H
#define NMWXPROPERTYINTERPOLATIONDLG_H
#include "iDlgBase.h"
#include "nmDataAnalyzeManager.h"
#include "nmSubWxs_global.h"
#include <QPointer>
#include <QStringList>
#include <QVector>
class QCheckBox;
class QComboBox;
class QDoubleSpinBox;
class QEvent;
class QLineEdit;
class QPointF;
class QPushButton;
class QTableWidget;
class QTableWidgetItem;
class QTimer;
class nmGuiPlot;
class nmObjPoint;
class nmWxPropertyInterpolationPreviewDlg;
class NM_SUB_WXS_EXPORT nmWxPropertyInterpolationDlg : public iDlgBase
{
Q_OBJECT
public:
explicit nmWxPropertyInterpolationDlg(nmGuiPlot* plot,
nmDataAnalyzeManager* dataManager,
QWidget* parent = 0);
~nmWxPropertyInterpolationDlg();
protected:
bool eventFilter(QObject* watched, QEvent* event);
private slots:
void onDataSetChanged(int index);
void onAddDataSet();
void onDeleteDataSet();
void onDataSetNameChanged(const QString& name);
// 当前数据组属性及显示单位变化.
void onCurrentDataSetPropertyChanged();
void onCurrentDataSetEdited();
void onXDisplayUnitChanged(const QString& unit);
void onYDisplayUnitChanged(const QString& unit);
void onValueDisplayUnitChanged(const QString& unit);
void onRangeDisplayUnitChanged(const QString& unit);
// 正式求解使用的数据组变化.
void onCalculationSelectionChanged();
void onMapPickToggled(bool checked);
void onMapClicked(const QPointF& position);
void onPointSelectionChanged();
void onPointItemChanged(QTableWidgetItem* item);
void onDeleteSelectedPoint();
void onClearPoints();
void onShowPointsToggled(bool checked);
void onShowLabelsToggled(bool checked);
void onPreview();
void refreshPreview();
private:
// 初始化界面及持久化数据组.
void initInterpolationUI();
void initializeDataSets();
nmPropertyInterpolationDataSet createDefaultDataSet();
QString nextDataSetName();
void loadDataSet(int index);
void clearCurrentDataSet();
void saveCurrentDataSet();
// 单位控件只维护界面显示状态;插值数据始终按求解器基准单位保存.
void updateUnitControls(nmPropertyInterpolationDataSet& dataSet);
void updatePointDisplayValues();
void clearPointRows();
void updatePointRowNumbers();
void syncDataSetsToManager(bool markModified);
void updateDataSetButtons();
// 管理正式求解时k、phi和h各自使用的插值数据组.
void updateCalculationDataSetControls();
void updateCalculationDataSetControl(const QString& property,
QCheckBox* checkBox,
QComboBox* comboBox);
void saveCalculationSelection(const QString& property,
QCheckBox* checkBox,
QComboBox* comboBox);
// 管理测点表格和地图上的临时标记.
void appendPoint(const QPointF& valuePoint);
void appendPointRow(const nmPropertyInterpolationPointData& point,
const QString& markerName);
QString createMarker(const QPointF& valuePosition,
double value,
bool showPoints,
bool showLabels);
void createMarkersForAllDataSets();
void removeDataSetMarkers(int dataSetIndex);
void removeAllMarkers();
QString markerNameAt(int row) const;
nmObjPoint* markerByName(const QString& markerName) const;
nmObjPoint* markerAt(int row) const;
void removeMarkerByName(const QString& markerName);
void clearTableSelection();
void updatePointButtons();
void updateCurrentMarkerVisibility();
void schedulePreviewRefresh();
void updatePreviewWindow(bool activateWindow);
void showPreviewError(const QString& message, bool activateWindow);
private:
QPointer<nmGuiPlot> m_pPlot;
QPointer<nmDataAnalyzeManager> m_pDataManager;
// 多组插值数据.
QComboBox* m_pDataSetCombo;
QPushButton* m_pAddDataSetButton;
QPushButton* m_pDeleteDataSetButton;
QVector<nmPropertyInterpolationDataSet> m_vecDataSets;
// 与数据组、测点下标一一对应,仅保存地图临时标记的对象名称.
QVector<QStringList> m_vecMarkerNames;
int m_nCurrentDataSetIndex;
int m_nNextPointId;
// 加载界面数据时阻止控件信号反向修改当前数据组.
bool m_bUpdatingUi;
// 当前数据组的属性及名称.
QComboBox* m_pPropertyCombo;
QLineEdit* m_pNameEdit;
// 测点数据录入.
QDoubleSpinBox* m_pNewValueSpin;
// 新值单位与表格Value列单位保持同步.
QComboBox* m_pNewValueUnitCombo;
QPushButton* m_pMapPickButton;
QTableWidget* m_pPointTable;
// 表格第0行中的X、Y和Value显示单位.
QComboBox* m_pXUnitCombo;
QComboBox* m_pYUnitCombo;
QComboBox* m_pValueUnitCombo;
QPushButton* m_pDeletePointButton;
QPushButton* m_pClearPointsButton;
QCheckBox* m_pShowPointsCheck;
QCheckBox* m_pShowLabelsCheck;
// Kriging插值参数.
QDoubleSpinBox* m_pNuggetSpin;
QDoubleSpinBox* m_pSillSpin;
QDoubleSpinBox* m_pRangeSpin;
QComboBox* m_pRangeUnitCombo;
QComboBox* m_pModelCombo;
// 正式求解时各属性使用的数据组.
QCheckBox* m_pUseKCheck;
QComboBox* m_pKCalculationDataSetCombo;
QCheckBox* m_pUsePhiCheck;
QComboBox* m_pPhiCalculationDataSetCombo;
QCheckBox* m_pUseHCheck;
QComboBox* m_pHCalculationDataSetCombo;
// 插值结果预览.
QPushButton* m_pPreviewButton;
QPointer<nmWxPropertyInterpolationPreviewDlg> m_pPreviewDialog;
QTimer* m_pPreviewRefreshTimer;
};
#endif // NMWXPROPERTYINTERPOLATIONDLG_H

@ -0,0 +1,71 @@
#ifndef NMWXPROPERTYINTERPOLATIONPREVIEWDLG_H
#define NMWXPROPERTYINTERPOLATIONPREVIEWDLG_H
#include "iDlgBase.h"
#include "nmSubWxs_global.h"
#include <QPointF>
#include <QRectF>
#include <QSize>
#include <QString>
#include <QStringList>
#include <QVector>
class QVTKWidget;
// 网格属性插值结果的独立VTK预览对话框.
class NM_SUB_WXS_EXPORT nmWxPropertyInterpolationPreviewDlg : public iDlgBase
{
public:
explicit 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,
const QVector<QPointF>& wellPoints,
const QStringList& wellNames,
bool showMeasurementPoints,
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,
const QVector<QPointF>& wellPoints,
const QStringList& wellNames,
bool showMeasurementPoints,
bool showMeasurementLabels);
void showMessage(const QString& dataSetName, const QString& message);
virtual QSize sizeHint() const;
private:
void 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,
const QVector<QPointF>& wellPoints,
const QStringList& wellNames,
bool showMeasurementPoints,
bool showMeasurementLabels);
private:
QVTKWidget* m_pVtkWidget;
};
#endif // NMWXPROPERTYINTERPOLATIONPREVIEWDLG_H

@ -16,6 +16,7 @@
#include "nmDataTimeStepSetting.h"
#include "nmCalculationPebiGrid.h"
#include "nmCalculationUtils.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataPvtParaForPebi.h"
@ -52,6 +53,111 @@ bool isFiniteSolverNumber(double value)
#endif
}
// 将启用的数据组插值到全部网格单元中心,并覆盖对应的求解器属性数组.
bool applyPropertyInterpolation(
HX_NWTM_MODEL_INPUT& modelInput,
const QVector<nmPropertyInterpolationDataSet>& dataSets,
const QString& licensePath,
QString& errorMessage)
{
bool hasEnabledDataSet = false;
for(int i = 0; i < dataSets.size(); ++i) {
if(dataSets[i].useForCalculation) {
hasEnabledDataSet = true;
break;
}
}
if(!hasEnabledDataSet) {
return true;
}
QVector<QPointF> targetPoints;
targetPoints.reserve(static_cast<int>(modelInput.GRID.Trinodexy.size()));
for(size_t cellIndex = 0;
cellIndex < modelInput.GRID.Trinodexy.size(); ++cellIndex) {
const dVec1& cellPosition = modelInput.GRID.Trinodexy[cellIndex];
if(cellPosition.size() < 2) {
errorMessage = QString("Grid cell %1 has no valid center coordinate.")
.arg(static_cast<qulonglong>(cellIndex));
return false;
}
targetPoints.append(QPointF(cellPosition[0], cellPosition[1]));
}
bool kApplied = false;
bool phiApplied = false;
bool hApplied = false;
for(int dataSetIndex = 0; dataSetIndex < dataSets.size(); ++dataSetIndex) {
const nmPropertyInterpolationDataSet& dataSet = dataSets[dataSetIndex];
if(!dataSet.useForCalculation) {
continue;
}
dVec1* solverValues = NULL;
bool* propertyApplied = NULL;
if(dataSet.property == "k") {
solverValues = &modelInput.Base.k;
propertyApplied = &kApplied;
} else if(dataSet.property == "phi") {
solverValues = &modelInput.Base.phi;
propertyApplied = &phiApplied;
} else if(dataSet.property == "h") {
solverValues = &modelInput.Base.h;
propertyApplied = &hApplied;
} else {
errorMessage = QString("Dataset '%1' has an unknown property.")
.arg(dataSet.name);
return false;
}
if(*propertyApplied) {
errorMessage = QString(
"More than one dataset is enabled for property %1.")
.arg(dataSet.property);
return false;
}
QVector<QPointF> measurementPoints;
QVector<double> measurementValues;
measurementPoints.reserve(dataSet.points.size());
measurementValues.reserve(dataSet.points.size());
for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) {
const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex];
measurementPoints.append(QPointF(point.x, point.y));
measurementValues.append(point.value);
}
QVector<double> interpolationValues;
QString calculationError;
if(!nmCalculationUtils::calculateKriging(
targetPoints,
measurementPoints,
measurementValues,
dataSet.nugget,
dataSet.sill,
dataSet.range,
dataSet.model,
licensePath,
interpolationValues,
&calculationError)) {
errorMessage = QString("Dataset '%1': %2")
.arg(dataSet.name)
.arg(calculationError);
return false;
}
solverValues->resize(interpolationValues.size());
for(int valueIndex = 0; valueIndex < interpolationValues.size(); ++valueIndex) {
// 插值数据已经使用求解器基准单位,结果可直接按网格顺序写入.
(*solverValues)[valueIndex] = interpolationValues[valueIndex];
}
*propertyApplied = true;
}
return true;
}
bool isReasonableLogLogValue(double value)
{
const double maxReasonableAbsValue = 1.0e12;
@ -485,6 +591,21 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
p0.Base.h = dVec1(cellCount, pReservoirData->getThickness().getValue().toDouble());
}
// 未启用的属性保留上面的单值数组;启用后才使用所选数据组覆盖.
QString interpolationError;
if(!applyPropertyInterpolation(
p0,
pDataInstance->getPropertyInterpolationDataSets(),
pDataInstance->getLicensePath(),
interpolationError)) {
QString logMessage = QString("Property interpolation failed: %1")
.arg(interpolationError);
qWarning() << logMessage;
zxLogInstance::getInstance()->writeLogF(logMessage);
FreeLibrary(dll);
return false;
}
// 获取时间步长
nmDataTimeStepSetting* pTimeStepSetting = pDataInstance->getTimeStep();
if(pTimeStepSetting) {

@ -4,7 +4,35 @@
#include <QFileInfo>
#include <QDir>
#include <QDebug>
#include <QCoreApplication>
#include <fstream>
#include <float.h>
#include "pch.h"
namespace
{
QString krigingText(const char* sourceText)
{
return QCoreApplication::translate("nmCalculationUtils", sourceText);
}
bool isFiniteValue(double value)
{
#ifdef _MSC_VER
return _finite(value) != 0;
#else
return std::isfinite(value);
#endif
}
void setKrigingError(QString* errorMessage, const QString& message)
{
if(errorMessage != NULL) {
*errorMessage = message;
}
}
}
nmCalculationUtils::nmCalculationUtils() {
}
@ -159,3 +187,165 @@ QStringList nmCalculationUtils::readFile(const QString &filePath) {
return content;
}
bool nmCalculationUtils::calculateKriging(
const QVector<QPointF>& targetPoints,
const QVector<QPointF>& measurementPoints,
const QVector<double>& measurementValues,
double nugget,
double sill,
double range,
int model,
const QString& licensePath,
QVector<double>& outputValues,
QString* errorMessage)
{
outputValues.clear();
setKrigingError(errorMessage, QString());
if(targetPoints.isEmpty()) {
setKrigingError(errorMessage, krigingText("No interpolation points are available."));
return false;
}
if(measurementPoints.size() < 2) {
setKrigingError(errorMessage,
krigingText("At least two measurement points are required."));
return false;
}
if(measurementPoints.size() != measurementValues.size()) {
setKrigingError(errorMessage,
krigingText("Measurement point coordinates and values do not match."));
return false;
}
if(!isFiniteValue(nugget) || !isFiniteValue(sill) ||
!isFiniteValue(range) || nugget < 0.0 || sill <= 0.0 || range <= 0.0) {
setKrigingError(errorMessage, krigingText("Kriging parameters are invalid."));
return false;
}
if(model < 0 || model > 2) {
setKrigingError(errorMessage, krigingText("The Kriging model is invalid."));
return false;
}
for(int i = 0; i < targetPoints.size(); ++i) {
if(!isFiniteValue(targetPoints[i].x()) || !isFiniteValue(targetPoints[i].y())) {
setKrigingError(errorMessage,
krigingText("An interpolation point contains an invalid coordinate."));
return false;
}
}
for(int i = 0; i < measurementPoints.size(); ++i) {
if(!isFiniteValue(measurementPoints[i].x()) ||
!isFiniteValue(measurementPoints[i].y()) ||
!isFiniteValue(measurementValues[i])) {
setKrigingError(errorMessage,
krigingText("A measurement point contains invalid data."));
return false;
}
for(int j = 0; j < i; ++j) {
if(measurementPoints[i] == measurementPoints[j]) {
setKrigingError(errorMessage,
krigingText("Measurement point coordinates cannot be duplicated."));
return false;
}
}
}
if(licensePath.isEmpty() || !QFileInfo(licensePath).exists()) {
setKrigingError(errorMessage, krigingText("The solver license file was not found."));
return false;
}
HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
if(dll == NULL) {
setKrigingError(errorMessage, krigingText("Failed to load HX_NWTM.dll."));
return false;
}
typedef void (*HX_NWTM_KRINGING_Func)(HX_KRING_OUTPUT&,
const HX_KRING_INPUT,
std::string);
HX_NWTM_KRINGING_Func krigingFunction =
(HX_NWTM_KRINGING_Func)GetProcAddress(dll, "HX_NWTM_KRINGING");
if(krigingFunction == NULL) {
FreeLibrary(dll);
setKrigingError(errorMessage,
krigingText("The Kriging interface was not found in HX_NWTM.dll."));
return false;
}
// 求解器p参数每行保存{x, y}并保持targetPoints的原始顺序.
dVec2 targetData;
targetData.reserve(targetPoints.size());
for(int i = 0; i < targetPoints.size(); ++i) {
dVec1 point(2, 0.0);
point[0] = targetPoints[i].x();
point[1] = targetPoints[i].y();
targetData.push_back(point);
}
// 求解器v参数每行保存{x, y, value},坐标和值按相同下标配对.
dVec2 measurementData;
measurementData.reserve(measurementPoints.size());
for(int i = 0; i < measurementPoints.size(); ++i) {
dVec1 point(3, 0.0);
point[0] = measurementPoints[i].x();
point[1] = measurementPoints[i].y();
point[2] = measurementValues[i];
measurementData.push_back(point);
}
bool calculationSucceeded = true;
QString calculationError;
// 局部作用域保证求解器输入、输出对象在FreeLibrary前析构.
{
HX_KRING_INPUT input(nugget, sill, range, model,
targetData, measurementData);
HX_KRING_OUTPUT output;
try {
krigingFunction(output, input, licensePath.toStdString());
} catch(const std::exception& exception) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation failed: %1")
.arg(QString::fromLocal8Bit(exception.what()));
} catch(...) {
calculationSucceeded = false;
calculationError = krigingText("Kriging calculation failed.");
}
if(calculationSucceeded &&
output.v.size() != static_cast<size_t>(targetPoints.size())) {
calculationSucceeded = false;
calculationError = krigingText(
"The Kriging result count does not match the interpolation points.");
}
if(calculationSucceeded) {
outputValues.reserve(targetPoints.size());
for(size_t i = 0; i < output.v.size(); ++i) {
if(!isFiniteValue(output.v[i])) {
calculationSucceeded = false;
calculationError = krigingText(
"The Kriging result contains an invalid value.");
break;
}
outputValues.append(output.v[i]);
}
}
}
FreeLibrary(dll);
if(!calculationSucceeded) {
outputValues.clear();
setKrigingError(errorMessage, calculationError);
}
return calculationSucceeded;
}

@ -65,6 +65,7 @@
#include "nmDataTimeStepSetting.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
@ -3353,6 +3354,17 @@ const BackgroundImageInfo& nmDataAnalyzeManager::getBackgroundImageInfo() const
return m_backgroundImageInfo;
}
QVector<nmPropertyInterpolationDataSet> nmDataAnalyzeManager::getPropertyInterpolationDataSets() const
{
return m_vecPropertyInterpolationDataSets;
}
void nmDataAnalyzeManager::setPropertyInterpolationDataSets(
const QVector<nmPropertyInterpolationDataSet>& dataSets)
{
m_vecPropertyInterpolationDataSets = dataSets;
}
// 从 JSON 文件读取数据到 C++ 对象
bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
{
@ -3400,6 +3412,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
}
m_vecLayers.clear();
m_vecPropertyInterpolationDataSets.clear();
if(m_reservoirData) {
delete m_reservoirData;
@ -3517,7 +3530,96 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
}
}
// 解析 "Reservoir" 对象
/* 读取属性插值数据组 */
if(doc.HasMember("PropertyInterpolationDataSets") &&
doc["PropertyInterpolationDataSets"].IsArray()) {
const rapidjson::Value& dataSetsJson = doc["PropertyInterpolationDataSets"];
for(rapidjson::SizeType i = 0; i < dataSetsJson.Size(); ++i) {
const rapidjson::Value& dataSetJson = dataSetsJson[i];
if(!dataSetJson.IsObject()) {
continue;
}
nmPropertyInterpolationDataSet dataSet;
if(dataSetJson.HasMember("Name") && dataSetJson["Name"].IsString()) {
dataSet.name = QString::fromUtf8(dataSetJson["Name"].GetString());
}
if(dataSetJson.HasMember("Property") && dataSetJson["Property"].IsString()) {
dataSet.property = QString::fromUtf8(dataSetJson["Property"].GetString());
}
if(dataSet.property == "h") {
dataSet.valueDisplayUnit = "m";
}
else if(dataSet.property == "phi") {
dataSet.valueDisplayUnit.clear();
}
if(dataSetJson.HasMember("XDisplayUnit") &&
dataSetJson["XDisplayUnit"].IsString()) {
dataSet.xDisplayUnit = QString::fromUtf8(
dataSetJson["XDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("YDisplayUnit") &&
dataSetJson["YDisplayUnit"].IsString()) {
dataSet.yDisplayUnit = QString::fromUtf8(
dataSetJson["YDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("ValueDisplayUnit") &&
dataSetJson["ValueDisplayUnit"].IsString()) {
dataSet.valueDisplayUnit = QString::fromUtf8(
dataSetJson["ValueDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("RangeDisplayUnit") &&
dataSetJson["RangeDisplayUnit"].IsString()) {
dataSet.rangeDisplayUnit = QString::fromUtf8(
dataSetJson["RangeDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("UseForCalculation") &&
dataSetJson["UseForCalculation"].IsBool()) {
dataSet.useForCalculation = dataSetJson["UseForCalculation"].GetBool();
}
if(dataSetJson.HasMember("ShowPoints") && dataSetJson["ShowPoints"].IsBool()) {
dataSet.showPoints = dataSetJson["ShowPoints"].GetBool();
}
if(dataSetJson.HasMember("ShowLabels") && dataSetJson["ShowLabels"].IsBool()) {
dataSet.showLabels = dataSetJson["ShowLabels"].GetBool();
}
if(dataSetJson.HasMember("Nugget") && dataSetJson["Nugget"].IsNumber()) {
dataSet.nugget = dataSetJson["Nugget"].GetDouble();
}
if(dataSetJson.HasMember("Sill") && dataSetJson["Sill"].IsNumber()) {
dataSet.sill = dataSetJson["Sill"].GetDouble();
}
if(dataSetJson.HasMember("Range") && dataSetJson["Range"].IsNumber()) {
dataSet.range = dataSetJson["Range"].GetDouble();
}
if(dataSetJson.HasMember("Model") && dataSetJson["Model"].IsInt()) {
dataSet.model = dataSetJson["Model"].GetInt();
}
if(dataSetJson.HasMember("Points") && dataSetJson["Points"].IsArray()) {
const rapidjson::Value& pointsJson = dataSetJson["Points"];
for(rapidjson::SizeType pointIndex = 0; pointIndex < pointsJson.Size(); ++pointIndex) {
const rapidjson::Value& pointJson = pointsJson[pointIndex];
if(!pointJson.IsObject() ||
!pointJson.HasMember("X") || !pointJson["X"].IsNumber() ||
!pointJson.HasMember("Y") || !pointJson["Y"].IsNumber() ||
!pointJson.HasMember("Value") || !pointJson["Value"].IsNumber()) {
continue;
}
dataSet.points.append(nmPropertyInterpolationPointData(
pointJson["X"].GetDouble(),
pointJson["Y"].GetDouble(),
pointJson["Value"].GetDouble()));
}
}
m_vecPropertyInterpolationDataSets.append(dataSet);
}
}
/* 解析 "Reservoir" 对象 */
if(doc.HasMember("Reservoir") && doc["Reservoir"].IsObject()) {
m_reservoirData = new nmDataReservoir;
m_reservoirData->FromJsonValue(doc["Reservoir"]);
@ -3736,6 +3838,55 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
doc.AddMember("Layers", layersJsonArray, allocator);
/* 保存属性插值数据组数值统一使用m、D等求解器基准单位 */
rapidjson::Value interpolationDataSetsJson(rapidjson::kArrayType);
for(int dataSetIndex = 0;
dataSetIndex < m_vecPropertyInterpolationDataSets.size();
++dataSetIndex) {
const nmPropertyInterpolationDataSet& dataSet =
m_vecPropertyInterpolationDataSets[dataSetIndex];
rapidjson::Value dataSetJson(rapidjson::kObjectType);
QByteArray nameUtf8 = dataSet.name.toUtf8();
QByteArray propertyUtf8 = dataSet.property.toUtf8();
dataSetJson.AddMember("Name",
rapidjson::Value(nameUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("Property",
rapidjson::Value(propertyUtf8.constData(), allocator).Move(), allocator);
QByteArray xDisplayUnitUtf8 = dataSet.xDisplayUnit.toUtf8();
QByteArray yDisplayUnitUtf8 = dataSet.yDisplayUnit.toUtf8();
QByteArray valueDisplayUnitUtf8 = dataSet.valueDisplayUnit.toUtf8();
QByteArray rangeDisplayUnitUtf8 = dataSet.rangeDisplayUnit.toUtf8();
dataSetJson.AddMember("XDisplayUnit",
rapidjson::Value(xDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("YDisplayUnit",
rapidjson::Value(yDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("ValueDisplayUnit",
rapidjson::Value(valueDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("RangeDisplayUnit",
rapidjson::Value(rangeDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("UseForCalculation", dataSet.useForCalculation, allocator);
dataSetJson.AddMember("ShowPoints", dataSet.showPoints, allocator);
dataSetJson.AddMember("ShowLabels", dataSet.showLabels, allocator);
dataSetJson.AddMember("Nugget", dataSet.nugget, allocator);
dataSetJson.AddMember("Sill", dataSet.sill, allocator);
dataSetJson.AddMember("Range", dataSet.range, allocator);
dataSetJson.AddMember("Model", dataSet.model, allocator);
rapidjson::Value pointsJson(rapidjson::kArrayType);
for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) {
const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex];
rapidjson::Value pointJson(rapidjson::kObjectType);
pointJson.AddMember("X", point.x, allocator);
pointJson.AddMember("Y", point.y, allocator);
pointJson.AddMember("Value", point.value, allocator);
pointsJson.PushBack(pointJson, allocator);
}
dataSetJson.AddMember("Points", pointsJson, allocator);
interpolationDataSetsJson.PushBack(dataSetJson, allocator);
}
doc.AddMember("PropertyInterpolationDataSets", interpolationDataSetsJson, allocator);
// 序列化 "Reservoir"
if(m_reservoirData) {
rapidjson::Value reservoirJson(rapidjson::kObjectType);

@ -7,7 +7,6 @@
// RapidJSON 内部使用 char*,这里需要转换为 std::string
#include <string>
#include <QFile>
#include <QTextStream>
#include <QString>
#include "rapidjson/document.h"
@ -211,39 +210,34 @@ bool nmDataJsonTools::WriteDomToFile(const rapidjson::Document& doc, const QStri
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(buffer); // 使用 PrettyWriter 进行美化输出
doc.Accept(writer);
// 将字符串写入文件
/* RapidJSON输出为UTF-8字节直接写入可避免中文被本地编码再次转换 */
QFile file(filePath);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qDebug() << "Error: Could not open file for writing:" << filePath;
return false;
}
QTextStream out(&file);
out << buffer.GetString();
const qint64 bufferSize = static_cast<qint64>(buffer.GetSize());
const qint64 writtenSize = file.write(buffer.GetString(), bufferSize);
file.close();
return true;
return writtenSize == bufferSize;
}
// 从文件读取 JSON 并解析到 RapidJSON Document
bool nmDataJsonTools::ReadDomFromFile(const QString& filePath, rapidjson::Document& doc)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Error: Could not open file for reading:" << filePath;
return false;
}
QTextStream in(&file);
QString jsonString = in.readAll();
const QByteArray jsonData = file.readAll();
file.close();
// 将 QString 转换为 std::string
QByteArray byteArray = jsonString.toUtf8();
std::string stdJsonString = byteArray.constData();
// 解析 JSON 字符串到 RapidJSON Document
doc.Parse(stdJsonString.c_str());
/* 文件内容保持RapidJSON所需的UTF-8编码 */
doc.Parse(jsonData.constData());
if (doc.HasParseError()) {
qDebug() << "Error parsing JSON from file:" << filePath << "Error:" << doc.GetParseError();
@ -251,4 +245,4 @@ bool nmDataJsonTools::ReadDomFromFile(const QString& filePath, rapidjson::Docume
}
return true;
}
}

@ -167,6 +167,14 @@ bool nmGuiPlot::runAction(QString sAction)
return m_pCmdHelper->runAction(sAction);
}
void nmGuiPlot::cancelActiveTools()
{
nmGuiPlotCmdHelper* helper = dynamic_cast<nmGuiPlotCmdHelper*>(m_pCmdHelper);
if(helper != NULL) {
helper->cancelActiveTools();
}
}
void nmGuiPlot::initDefultGeoObj()
{

@ -161,6 +161,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjPointWell")) {
nmObjToolBase* pObjTool = new nmObjPointWellTool();
@ -169,6 +170,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjLine")) {
nmObjToolBase* pObjTool = new nmObjLineTool();
@ -176,6 +178,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjPolygon")) {
nmObjToolBase* pObjTool = new nmObjPolygonTool();
@ -183,6 +186,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjPolygonOutline")) {
nmObjToolBase* pObjTool = new nmObjPolygonOutlineTool();
@ -190,6 +194,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjRect")) {
nmObjToolBase* pObjTool = new nmObjRectTool(); //矩形对象工具类
@ -197,6 +202,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjRectOutline")) {
nmObjToolBase* pObjTool = new nmObjRectOutlineTool(); //矩形边界工具类
@ -204,6 +210,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjRound")) {
nmObjToolBase* pObjTool = new nmObjRoundTool(); //圆形对象工具类
@ -211,6 +218,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjRoundOutline")) {
nmObjToolBase* pObjTool = new nmObjRoundOutlineTool(); //圆形边界工具类
@ -218,6 +226,7 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
pPlot->appendTools(pObjTool);
connect(pObjTool, SIGNAL(sigPtsFinished(QVector<QPointF>&)), \
this, SLOT(slotObjPtsFinished(QVector<QPointF>&)));
m_activeTools.insert(pObjTool->getNOT(), pObjTool);
return true;
} else if (_isSame(sAction, "nmInsertObjLineCrack")) {
nmObjToolBase* pObjTool = new nmObjLineCrackTool();
@ -298,6 +307,21 @@ bool nmGuiPlotCmdHelper::runAction(QString sAction) {
return iGuiPlotCmdHelper::runAction(sAction);
}
void nmGuiPlotCmdHelper::cancelActiveTools()
{
nmGuiPlot* pWxPlot = dynamic_cast<nmGuiPlot*>(m_pGuiPlot);
if(pWxPlot == nullptr || pWxPlot->m_pPlot == nullptr ||
pWxPlot->m_pPlotView == nullptr) {
return;
}
foreach (nmObjToolBase* activeTool, m_activeTools.values()) {
pWxPlot->m_pPlot->removeTools(activeTool);
}
m_activeTools.clear();
finalWithAcm(true, true);
}
void nmGuiPlotCmdHelper::slotObjPtsFinished(QVector<QPointF>& vec) {
nmObjToolBase* pTool = dynamic_cast<nmObjToolBase*>(sender());
if (NULL == pTool) {

@ -57,6 +57,7 @@
#include "nmWxWellLayerDlg.h"
#include "nmWxGeometryLayerDlg.h"
#include "nmWxReservoirPropertiesDlg.h"
#include "nmWxPropertyInterpolationDlg.h"
#include "nmWxPostprocessingAnimationWidget.h"
#include "nmWxGeoRefDlg.h"
@ -512,6 +513,13 @@ bool nmSubWndMain::runCmdBy(QString sName, QString sID)
}
this->drawPointerPos();
return true;
case 5126:
if(isDialogCmdOpened(5126)) {
return true;
}
this->showPropertyInterpolationDialog();
return true;
}
if(_isSame(s, "RunGrid")) {
@ -557,6 +565,7 @@ bool nmSubWndMain::checkCmdEnable(bool &b, \
if(sName == "NmMap" || sName == "NmUnlocked" || sName == "NmGeometry" ||
sName == "NmProperties" || sName == "NmDFN" || sName == "NmCaculation" ||
sName == "NmBack" || sName == "NmResult" || sName == "NmGrid" ||
sName == "NmInterpolation" ||
sName == "NmShow" || sName == "NmPrintPreview" || sName == "NmPrint" ||
sName == "VisibleAnalBasic" || sName == "VisibleAnalPVT" || sName == "VisibleAnalDiff" ||
sName == "VisibleAnalPseu" || sName == "nmFitModel" || sName == "NmAnalChange") {
@ -1435,6 +1444,21 @@ QWidget* nmSubWndMain::showPointerPosDialog()
return pointerPosDlg;
}
QWidget* nmSubWndMain::showPropertyInterpolationDialog()
{
nmWxPropertyInterpolationDlg* interpolationDlg = new nmWxPropertyInterpolationDlg(
m_pWxPlot, nmDataAnalyzeManager::getCurrentInstance(), this);
markDialogCmdOpened(5126);
interpolationDlg->setObjectName("nmPropertyInterpolationDialog");
// 关闭窗口时自动释放,并恢复菜单命令的可用状态
interpolationDlg->setAttribute(Qt::WA_DeleteOnClose, true);
bindDialogCmdDestroyed(interpolationDlg, 5126);
interpolationDlg->show();
interpolationDlg->raise();
interpolationDlg->activateWindow();
return interpolationDlg;
}
void nmSubWndMain::generationMesh()
{
// // TODO还不支持原型边界

File diff suppressed because it is too large Load Diff

@ -0,0 +1,620 @@
#include "nmWxPropertyInterpolationPreviewDlg.h"
#include <QApplication>
#include <QCoreApplication>
#include <QDesktopWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSizePolicy>
#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 <vtkStringArray.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,
const QVector<QPointF>& wellPoints,
const QStringList& wellNames,
bool showMeasurementPoints,
bool showMeasurementLabels,
QWidget* parent)
: iDlgBase(parent),
m_pVtkWidget(NULL)
{
setWindowModality(Qt::NonModal);
setModal(false);
QVBoxLayout* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(8, 8, 8, 8);
mainLayout->setSpacing(8);
m_pVtkWidget = new QVTKWidget(this);
m_pVtkWidget->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
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);
}
QSize nmWxPropertyInterpolationPreviewDlg::sizeHint() const
{
QDesktopWidget* desktop = QApplication::desktop();
if(desktop == NULL) {
return iDlgBase::sizeHint();
}
// 预览窗口随当前屏幕可用区域调整,避免固定像素尺寸在不同分辨率下失衡.
const QRect availableRect = desktop->availableGeometry(this);
return QSize(availableRect.width() * 1 / 2,
availableRect.height() * 4 / 5);
}
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,
const QVector<QPointF>& 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<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(1.0, 1.0, 1.0);
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,
const QVector<QPointF>& wellPoints,
const QStringList& wellNames,
bool showMeasurementPoints,
bool showMeasurementLabels)
{
if(m_pVtkWidget == NULL) {
return;
}
// 显式创建渲染窗口避免使用尚未初始化的QVTKWidget默认窗口.
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->SetBackground(1.0, 1.0, 1.0);
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);
// 将连续点标量划分为8级色带Mapper按CellData进行分级着色.
const int colorBandCount = 8;
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) {
// 标签使用独立坐标上移,测点方框仍保持在原始测量位置.
const double labelOffset =
qMax(bounds.width(), bounds.height()) * 0.018;
vtkSmartPointer<vtkPoints> measurementLabelPoints =
vtkSmartPointer<vtkPoints>::New();
for(int i = 0; i < measurementPoints.size(); ++i) {
measurementLabelPoints->InsertNextPoint(
measurementPoints[i].x(),
measurementPoints[i].y() + labelOffset, 0.03);
}
vtkSmartPointer<vtkPolyData> measurementLabelData =
vtkSmartPointer<vtkPolyData>::New();
measurementLabelData->SetPoints(measurementLabelPoints);
measurementLabelData->GetPointData()->SetScalars(
measurementScalars);
vtkSmartPointer<vtkLabeledDataMapper> labelMapper =
vtkSmartPointer<vtkLabeledDataMapper>::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<vtkActor2D> labelActor =
vtkSmartPointer<vtkActor2D>::New();
labelActor->SetMapper(labelMapper);
renderer->AddActor2D(labelActor);
}
}
// Map中显示的井使用独立红色标记和名称叠加不参与插值表面计算.
if(!wellPoints.isEmpty() && wellPoints.size() == wellNames.size()) {
vtkSmartPointer<vtkPoints> wellVtkPoints =
vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkStringArray> wellNameArray =
vtkSmartPointer<vtkStringArray>::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<vtkPolyData> wellData =
vtkSmartPointer<vtkPolyData>::New();
wellData->SetPoints(wellVtkPoints);
wellData->GetPointData()->AddArray(wellNameArray);
vtkSmartPointer<vtkVertexGlyphFilter> wellGlyphFilter =
vtkSmartPointer<vtkVertexGlyphFilter>::New();
wellGlyphFilter->SetInputData(wellData);
vtkSmartPointer<vtkPolyDataMapper> wellBorderMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
wellBorderMapper->SetInputConnection(wellGlyphFilter->GetOutputPort());
wellBorderMapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> wellBorderActor =
vtkSmartPointer<vtkActor>::New();
wellBorderActor->SetMapper(wellBorderMapper);
wellBorderActor->GetProperty()->SetColor(0.12, 0.12, 0.12);
wellBorderActor->GetProperty()->SetPointSize(9.0);
renderer->AddActor(wellBorderActor);
vtkSmartPointer<vtkPolyDataMapper> wellMapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
wellMapper->SetInputConnection(wellGlyphFilter->GetOutputPort());
wellMapper->ScalarVisibilityOff();
vtkSmartPointer<vtkActor> wellActor = vtkSmartPointer<vtkActor>::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<vtkPoints> wellLabelPoints =
vtkSmartPointer<vtkPoints>::New();
for(int i = 0; i < wellPoints.size(); ++i) {
wellLabelPoints->InsertNextPoint(
wellPoints[i].x(),
wellPoints[i].y() + wellLabelOffset, 0.05);
}
vtkSmartPointer<vtkPolyData> wellLabelData =
vtkSmartPointer<vtkPolyData>::New();
wellLabelData->SetPoints(wellLabelPoints);
wellLabelData->GetPointData()->AddArray(wellNameArray);
vtkSmartPointer<vtkLabeledDataMapper> wellLabelMapper =
vtkSmartPointer<vtkLabeledDataMapper>::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<vtkActor2D> wellLabelActor =
vtkSmartPointer<vtkActor2D>::New();
wellLabelActor->SetMapper(wellLabelMapper);
renderer->AddActor2D(wellLabelActor);
}
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();
}
Loading…
Cancel
Save