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/nmPebiResultSceneController...

1489 lines
54 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 "nmPebiResultSceneController.h"
#include "nmPebiResultPropertyCatalog.h"
#include "nmPebiResultPropertyDisplayText.h"
#include "nmPebiResultSnapshot.h"
#include "nmPebiResultSnapshotRenderAdapter.h"
#include "nmPebiResultWellAnnotationLayer.h"
#include "nmVTKColorMapCatalog.h"
#include "nmVTKMeshLayer.h"
#include "nmVTKPlanarScaleGridLayer.h"
#include "nmVTKScalarBarLayer.h"
#include "nmVTKScene.h"
#include "nmWxVTKRenderContainerWidget.h"
#include <QCoreApplication>
#include <QMap>
#include <QThread>
#include <QtGlobal>
#include <vtkActor.h>
#include <vtkAlgorithm.h>
#include <vtkAlgorithmOutput.h>
#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDataSet.h>
#include <vtkLookupTable.h>
#include <vtkMath.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <math.h>
namespace {
const char* const RESULT_MESH_LAYER_ID = "pebi.result.mesh";
const char* const RESULT_SCALAR_BAR_LAYER_ID = "pebi.result.scalar_bar";
const char* const RESULT_SCALE_GRID_LAYER_ID = "pebi.result.scale_grid";
const char* const RESULT_WELLS_LAYER_ID = "pebi.result.wells";
/* Qt 4.8 的 lupdate 无法从变量参数中推导翻译原文,必须显式登记。 */
static const char* const s_pResultSceneTranslationMarkers[] =
{
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The result snapshot is invalid."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The result snapshot has no renderable property."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to initialize the result render adapter."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The result render data failed validation."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to create the result render layers."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to commit the result render data."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The requested result property or frame is invalid."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The result property range is invalid."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to apply the result property or frame."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The color settings no longer match the current result."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The color settings are invalid."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The selected color map is unavailable."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"The manual range must contain two finite increasing values."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Logarithmic scale requires positive property and display ranges."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to construct the color mapping."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController",
"Unable to apply the color settings."),
QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Below min"),
QT_TRANSLATE_NOOP("nmPebiResultSceneController", "Above max")
};
bool isResultSceneGuiThread()
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == nullptr ||
pApplication->thread() == QThread::currentThread();
}
QString controllerText(const char* pText)
{
Q_UNUSED(s_pResultSceneTranslationMarkers);
return QCoreApplication::translate(
"nmPebiResultSceneController", pText);
}
void copyBounds(const double dSource[6], double dTarget[6])
{
for(int nIndex = 0; nIndex < 6; ++nIndex) {
dTarget[nIndex] = dSource[nIndex];
}
}
bool getFiniteBounds(vtkDataSet* pDataSet, double dBounds[6])
{
if(pDataSet == nullptr || dBounds == nullptr) {
return false;
}
pDataSet->GetBounds(dBounds);
if(!vtkMath::AreBoundsInitialized(dBounds)) {
return false;
}
for(int nIndex = 0; nIndex < 6; ++nIndex) {
if(!vtkMath::IsFinite(dBounds[nIndex])) {
return false;
}
}
return dBounds[0] < dBounds[1] &&
dBounds[2] < dBounds[3] &&
dBounds[4] <= dBounds[5];
}
bool getInternalDisplayRange(const double dVisibleRange[2],
bool bLogarithmic,
double dInternalRange[2])
{
if(dVisibleRange == nullptr || dInternalRange == nullptr ||
!vtkMath::IsFinite(dVisibleRange[0]) ||
!vtkMath::IsFinite(dVisibleRange[1]) ||
dVisibleRange[0] > dVisibleRange[1] ||
(bLogarithmic && dVisibleRange[0] <= 0.0)) {
return false;
}
dInternalRange[0] = dVisibleRange[0];
dInternalRange[1] = dVisibleRange[1];
if(dVisibleRange[0] < dVisibleRange[1]) {
return true;
}
// VTK 不接受零宽范围。常量值只扩展内部 LUT,业务和可见范围仍为 [c,c]。
const double dValue = dVisibleRange[0];
double dMinimum = 0.0;
double dMaximum = 0.0;
if(bLogarithmic) {
dMinimum = dValue * (1.0 - 1.0e-6);
dMaximum = dValue * (1.0 + 1.0e-6);
if(!vtkMath::IsFinite(dMinimum) || dMinimum <= 0.0 ||
dMinimum >= dValue) {
const double dHalfValue = dValue / 2.0;
dMinimum = dHalfValue > 0.0 && dHalfValue < dValue
? dHalfValue : dValue;
}
if(!vtkMath::IsFinite(dMaximum) || dMaximum <= dValue) {
const double dTwiceValue = dValue * 2.0;
dMaximum = vtkMath::IsFinite(dTwiceValue) &&
dTwiceValue > dValue ? dTwiceValue : dValue;
}
} else if(dValue == 0.0) {
dMinimum = -1.0e-6;
dMaximum = 1.0e-6;
} else {
const double dEpsilon = qMax(1.0, fabs(dValue)) * 1.0e-6;
dMinimum = dValue - dEpsilon;
dMaximum = dValue + dEpsilon;
if(!vtkMath::IsFinite(dMinimum) || dMinimum >= dValue) {
dMinimum = dValue > 0.0 ? dValue / 2.0 : dValue;
}
if(!vtkMath::IsFinite(dMaximum) || dMaximum <= dValue) {
dMaximum = dValue < 0.0 ? dValue / 2.0 : dValue;
}
}
if(!vtkMath::IsFinite(dMinimum) || !vtkMath::IsFinite(dMaximum) ||
dMinimum >= dMaximum || (bLogarithmic && dMinimum <= 0.0)) {
return false;
}
dInternalRange[0] = dMinimum;
dInternalRange[1] = dMaximum;
return true;
}
struct ResultDisplayCandidate
{
ResultDisplayCandidate()
: m_pLookupTable(nullptr),
m_bConstantValueLabel(false)
{
m_dBusinessRange[0] = m_dBusinessRange[1] = 0.0;
m_dVisibleRange[0] = m_dVisibleRange[1] = 0.0;
m_dInternalRange[0] = 0.0;
m_dInternalRange[1] = 1.0;
}
nmPebiResultDisplaySettings m_oSettings;
double m_dBusinessRange[2];
double m_dVisibleRange[2];
double m_dInternalRange[2];
vtkSmartPointer<vtkLookupTable> m_pLookupTable;
QString m_sScalarBarTitle;
bool m_bConstantValueLabel;
};
bool prepareDisplayCandidate(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
const QString& sPropertyId,
const nmPebiResultDisplaySettings& oRequestedSettings,
ResultDisplayCandidate& oCandidate,
QString* pError)
{
nmPebiResultDisplaySettings oSettings = oRequestedSettings.normalized();
if(pSnapshot.isNull() || !pSnapshot->hasProperty(sPropertyId) ||
!oSettings.isStructurallyValid()) {
if(pError != nullptr) {
*pError = controllerText("The color settings are invalid.");
}
return false;
}
if(!nmVTKColorMapCatalog::contains(oSettings.m_sColorMapId)) {
if(pError != nullptr) {
*pError = controllerText("The selected color map is unavailable.");
}
return false;
}
double dBusinessRange[2] = { 0.0, 0.0 };
if(!pSnapshot->getPropertyRange(sPropertyId, dBusinessRange) ||
!vtkMath::IsFinite(dBusinessRange[0]) ||
!vtkMath::IsFinite(dBusinessRange[1]) ||
dBusinessRange[0] > dBusinessRange[1]) {
if(pError != nullptr) {
*pError = controllerText("The result property range is invalid.");
}
return false;
}
double dVisibleRange[2] = {
dBusinessRange[0], dBusinessRange[1]
};
if(oSettings.m_bHasManualRange) {
if(!vtkMath::IsFinite(oSettings.m_dManualMinimum) ||
!vtkMath::IsFinite(oSettings.m_dManualMaximum) ||
oSettings.m_dManualMinimum >= oSettings.m_dManualMaximum) {
if(pError != nullptr) {
*pError = controllerText(
"The manual range must contain two finite increasing values.");
}
return false;
}
dVisibleRange[0] = oSettings.m_dManualMinimum;
dVisibleRange[1] = oSettings.m_dManualMaximum;
}
const bool bLogarithmic =
oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic;
if(bLogarithmic &&
(dBusinessRange[0] <= 0.0 || dVisibleRange[0] <= 0.0)) {
if(pError != nullptr) {
*pError = controllerText(
"Logarithmic scale requires positive property and display ranges.");
}
return false;
}
double dInternalRange[2] = { 0.0, 1.0 };
vtkSmartPointer<vtkLookupTable> pLookupTable =
nmVTKColorMapCatalog::createLookupTable(
oSettings.m_sColorMapId);
if(pLookupTable == nullptr ||
!getInternalDisplayRange(dVisibleRange, bLogarithmic,
dInternalRange)) {
if(pError != nullptr) {
*pError = controllerText("Unable to construct the color mapping.");
}
return false;
}
pLookupTable->SetTableRange(dInternalRange);
if(bLogarithmic) {
pLookupTable->SetScaleToLog10();
} else {
pLookupTable->SetScaleToLinear();
}
const QColor oBelowColor = oSettings.m_oBelowRangeColor;
const QColor oAboveColor = oSettings.m_oAboveRangeColor;
pLookupTable->SetBelowRangeColor(
oBelowColor.redF(), oBelowColor.greenF(),
oBelowColor.blueF(), 1.0);
pLookupTable->SetAboveRangeColor(
oAboveColor.redF(), oAboveColor.greenF(),
oAboveColor.blueF(), 1.0);
pLookupTable->SetUseBelowRangeColor(
oSettings.m_bUseBelowRangeColor ? 1 : 0);
pLookupTable->SetUseAboveRangeColor(
oSettings.m_bUseAboveRangeColor ? 1 : 0);
// VTK 7.1 的上下越界颜色位于特殊表项中,Setter 后必须显式同步。
// 不能使用 ForceBuild,否则会覆盖色带目录手工写入的 256 个颜色表项。
pLookupTable->BuildSpecialColors();
pLookupTable->IndexedLookupOff();
pLookupTable->ResetAnnotations();
const bool bConstantValueLabel =
!oSettings.m_bHasManualRange &&
dBusinessRange[0] == dBusinessRange[1];
QString sTitle =
nmPebiResultPropertyDisplayText::getDisplayName(sPropertyId);
const QString sUnit =
nmPebiResultPropertyDisplayText::getDefaultDisplayUnit(sPropertyId);
if(!sUnit.isEmpty()) {
sTitle += QString::fromLatin1(" (") + sUnit +
QString::fromLatin1(")");
}
oCandidate.m_oSettings = oSettings;
oCandidate.m_dBusinessRange[0] = dBusinessRange[0];
oCandidate.m_dBusinessRange[1] = dBusinessRange[1];
oCandidate.m_dVisibleRange[0] = dVisibleRange[0];
oCandidate.m_dVisibleRange[1] = dVisibleRange[1];
oCandidate.m_dInternalRange[0] = dInternalRange[0];
oCandidate.m_dInternalRange[1] = dInternalRange[1];
oCandidate.m_pLookupTable = pLookupTable;
oCandidate.m_sScalarBarTitle = sTitle;
oCandidate.m_bConstantValueLabel = bConstantValueLabel;
return true;
}
QString chooseInitialProperty(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot)
{
const QString sPressure = QString::fromLatin1("pressure");
if(!pSnapshot.isNull() && pSnapshot->hasProperty(sPressure) &&
pSnapshot->getPropertyFrameCount(sPressure) > 0) {
return sPressure;
}
// 无压力仅是防御分支;按 Catalog 顺序选择第一个可用静态属性。
const QList<nmPebiResultPropertyDescriptor> listDescriptors =
nmPebiResultPropertyCatalog::descriptors();
for(int nIndex = 0; nIndex < listDescriptors.size(); ++nIndex) {
const nmPebiResultPropertyDescriptor& oDescriptor =
listDescriptors[nIndex];
if(oDescriptor.m_eTimeType == NM_PebiResultProperty_Static &&
pSnapshot->hasProperty(oDescriptor.m_sPropertyId)) {
return oDescriptor.m_sPropertyId;
}
}
return QString();
}
bool validateAdapterOutput(
nmPebiResultSnapshotRenderAdapter* pAdapter,
vtkIdType nExpectedCellCount,
double dBounds[6])
{
if(pAdapter == nullptr || dBounds == nullptr ||
pAdapter->getCellCount() != nExpectedCellCount ||
nExpectedCellCount <= 0) {
return false;
}
vtkAlgorithmOutput* pOutputPort = pAdapter->getOutputPort();
if(pOutputPort == nullptr || pOutputPort->GetProducer() == nullptr ||
pOutputPort->GetIndex() < 0) {
return false;
}
vtkAlgorithm* pProducer = pOutputPort->GetProducer();
pProducer->Update(pOutputPort->GetIndex());
vtkDataSet* pDataSet = vtkDataSet::SafeDownCast(
pProducer->GetOutputDataObject(pOutputPort->GetIndex()));
if(pDataSet == nullptr ||
pDataSet->GetNumberOfCells() != nExpectedCellCount ||
!getFiniteBounds(pDataSet, dBounds)) {
return false;
}
const QByteArray aScalarName =
pAdapter->getCurrentScalarArrayName().toUtf8();
vtkDataArray* pScalars = aScalarName.isEmpty() ? nullptr :
pDataSet->GetCellData()->GetArray(aScalarName.constData());
if(pScalars == nullptr || pScalars->GetNumberOfComponents() != 1 ||
pScalars->GetNumberOfTuples() != nExpectedCellCount) {
return false;
}
double dActualRange[2] = { 0.0, 0.0 };
pScalars->GetRange(dActualRange);
return vtkMath::IsFinite(dActualRange[0]) &&
vtkMath::IsFinite(dActualRange[1]) &&
dActualRange[0] <= dActualRange[1];
}
}
class nmPebiResultSceneControllerPrivate
{
public:
explicit nmPebiResultSceneControllerPrivate(
nmWxVTKRenderContainerWidget* pContainer)
: m_pContainer(pContainer),
m_pMeshLayer(nullptr),
m_pScalarBarLayer(nullptr),
m_pScaleGridLayer(nullptr),
m_pWellLayer(nullptr),
m_pAdapter(nullptr),
m_nCurrentFrameIndex(-1),
m_nSnapshotBindingRevision(0),
m_bHasPropertyRange(false),
m_bHasBounds(false),
m_bScalarBarVisible(true),
m_bScaleGridVisible(false),
m_bWellNamesVisible(true)
{
m_dBusinessRange[0] = 0.0;
m_dBusinessRange[1] = 0.0;
m_dDisplayRange[0] = 0.0;
m_dDisplayRange[1] = 1.0;
for(int nIndex = 0; nIndex < 6; ++nIndex) {
m_dBounds[nIndex] = 0.0;
}
}
nmVTKScene* getScene() const
{
return m_pContainer != nullptr ? m_pContainer->getScene() : nullptr;
}
bool synchronizeLayerPointers()
{
nmVTKScene* pScene = getScene();
bool bPointersIntact = pScene != nullptr;
if(pScene == nullptr) {
bPointersIntact = m_pMeshLayer == nullptr &&
m_pScalarBarLayer == nullptr &&
m_pScaleGridLayer == nullptr &&
m_pWellLayer == nullptr;
m_pMeshLayer = nullptr;
m_pScalarBarLayer = nullptr;
m_pScaleGridLayer = nullptr;
m_pWellLayer = nullptr;
return bPointersIntact;
}
// Scene 是图层唯一所有者。这里只比较指针身份,不解引用可能已经由
// 外部 Scene::clear() 删除的旧地址;不一致时立即丢弃观察指针。
if(m_pMeshLayer != nullptr &&
pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) !=
m_pMeshLayer) {
m_pMeshLayer = nullptr;
bPointersIntact = false;
}
if(m_pScalarBarLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) !=
m_pScalarBarLayer) {
m_pScalarBarLayer = nullptr;
bPointersIntact = false;
}
if(m_pScaleGridLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) !=
m_pScaleGridLayer) {
m_pScaleGridLayer = nullptr;
bPointersIntact = false;
}
if(m_pWellLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID)) !=
m_pWellLayer) {
m_pWellLayer = nullptr;
bPointersIntact = false;
}
return bPointersIntact;
}
bool hasOwnedBaseLayers() const
{
if(!isResultSceneGuiThread()) {
return false;
}
nmVTKScene* pScene = getScene();
return pScene != nullptr &&
m_pMeshLayer != nullptr &&
m_pScalarBarLayer != nullptr &&
m_pScaleGridLayer != nullptr &&
m_pWellLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_MESH_LAYER_ID)) ==
m_pMeshLayer &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) ==
m_pScalarBarLayer &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) ==
m_pScaleGridLayer &&
pScene->getLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID)) ==
m_pWellLayer;
}
void discardActiveDataIfLayersLost()
{
const bool bPointersIntact = synchronizeLayerPointers();
const bool bHasActiveState = m_pAdapter != nullptr ||
!m_pSnapshot.isNull();
if(bHasActiveState &&
(!bPointersIntact || !hasOwnedBaseLayers())) {
// 外部已经清理业务 Scene 时无法继续兑现“当前快照可见”的状态,
// 因此采用 fail-closed:释放逻辑状态,但绝不访问已删除的 Layer。
releaseActiveData();
}
}
bool ensureBaseLayers(bool& bCreatedMesh,
bool& bCreatedScalarBar,
bool& bCreatedScaleGrid,
bool& bCreatedWells)
{
bCreatedMesh = false;
bCreatedScalarBar = false;
bCreatedScaleGrid = false;
bCreatedWells = false;
const bool bPointersIntact = synchronizeLayerPointers();
nmVTKScene* pScene = getScene();
vtkRenderer* pRenderer = m_pContainer != nullptr
? m_pContainer->getRenderer() : nullptr;
if(pScene == nullptr || pRenderer == nullptr ||
(!bPointersIntact && m_pAdapter != nullptr)) {
return false;
}
if(m_pMeshLayer == nullptr) {
if(pScene->getLayer(
QString::fromLatin1(RESULT_MESH_LAYER_ID)) != nullptr) {
return false;
}
nmVTKMeshLayer* pLayer = new nmVTKMeshLayer(
QString::fromLatin1(RESULT_MESH_LAYER_ID));
pLayer->setVisible(false);
if(!pScene->addLayer(pLayer)) {
delete pLayer;
return false;
}
m_pMeshLayer = pLayer;
bCreatedMesh = true;
m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor(
0.0, 0.0, 0.0);
m_pMeshLayer->getActor()->GetProperty()->LightingOff();
}
if(m_pScalarBarLayer == nullptr) {
if(pScene->getLayer(QString::fromLatin1(
RESULT_SCALAR_BAR_LAYER_ID)) != nullptr) {
rollbackCreatedLayers(
bCreatedMesh, false, false, false);
return false;
}
nmVTKScalarBarLayer* pLayer = new nmVTKScalarBarLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID));
pLayer->setVisible(false);
pLayer->setPosition(0.05, 0.1);
pLayer->setSize(0.1, 0.8);
if(!pScene->addLayer(pLayer)) {
delete pLayer;
rollbackCreatedLayers(
bCreatedMesh, false, false, false);
return false;
}
m_pScalarBarLayer = pLayer;
bCreatedScalarBar = true;
}
if(m_pScaleGridLayer == nullptr) {
if(pScene->getLayer(QString::fromLatin1(
RESULT_SCALE_GRID_LAYER_ID)) != nullptr) {
rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar, false, false);
return false;
}
nmVTKPlanarScaleGridLayer* pLayer =
new nmVTKPlanarScaleGridLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID));
pLayer->setVisible(false);
if(!pScene->addLayer(pLayer)) {
delete pLayer;
rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar, false, false);
return false;
}
m_pScaleGridLayer = pLayer;
bCreatedScaleGrid = true;
}
if(m_pWellLayer == nullptr) {
if(pScene->getLayer(QString::fromLatin1(
RESULT_WELLS_LAYER_ID)) != nullptr) {
rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar,
bCreatedScaleGrid, false);
return false;
}
nmPebiResultWellAnnotationLayer* pLayer =
new nmPebiResultWellAnnotationLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID));
pLayer->setVisible(false);
if(!pScene->addLayer(pLayer)) {
delete pLayer;
rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar,
bCreatedScaleGrid, false);
return false;
}
m_pWellLayer = pLayer;
bCreatedWells = true;
}
return true;
}
void rollbackCreatedLayers(bool bCreatedMesh,
bool bCreatedScalarBar,
bool bCreatedScaleGrid,
bool bCreatedWells)
{
synchronizeLayerPointers();
nmVTKScene* pScene = getScene();
if(pScene == nullptr) {
return;
}
if(bCreatedWells && m_pWellLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID)) ==
m_pWellLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID));
}
if(bCreatedWells) {
m_pWellLayer = nullptr;
}
if(bCreatedScaleGrid && m_pScaleGridLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) ==
m_pScaleGridLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID));
}
if(bCreatedScaleGrid) {
m_pScaleGridLayer = nullptr;
}
if(bCreatedScalarBar && m_pScalarBarLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) ==
m_pScalarBarLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID));
}
if(bCreatedScalarBar) {
m_pScalarBarLayer = nullptr;
}
if(bCreatedMesh && m_pMeshLayer != nullptr &&
pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) ==
m_pMeshLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_MESH_LAYER_ID));
}
if(bCreatedMesh) {
m_pMeshLayer = nullptr;
}
}
bool bindMeshAdapter(nmPebiResultSnapshotRenderAdapter* pAdapter,
vtkLookupTable* pLookupTable)
{
if(m_pMeshLayer == nullptr || pAdapter == nullptr ||
pLookupTable == nullptr) {
return false;
}
// MeshLayer 在触碰旧 Mapper 前完成全部校验,失败不会破坏旧画面。
return m_pMeshLayer->setInputConnection(
pAdapter->getOutputPort(),
pAdapter->getCurrentScalarArrayName(),
nmVTKMeshLayer::ScalarAssociation_CellData,
pLookupTable);
}
void applyScalarBarState(const ResultDisplayCandidate& oDisplay)
{
if(m_pScalarBarLayer == nullptr) {
return;
}
m_pScalarBarLayer->setLookupTable(oDisplay.m_pLookupTable);
m_pScalarBarLayer->setTitle(oDisplay.m_sScalarBarTitle);
m_pScalarBarLayer->setLabelFormat(QString::fromLatin1("%.6g"));
m_pScalarBarLayer->setNumberOfLabels(
oDisplay.m_bConstantValueLabel ? 0 : 5);
m_pScalarBarLayer->setConstantValueLabel(
oDisplay.m_bConstantValueLabel,
oDisplay.m_bConstantValueLabel
? QString::number(oDisplay.m_dBusinessRange[0], 'g', 6)
: QString());
m_pScalarBarLayer->setBelowRangeSwatch(
oDisplay.m_oSettings.m_bUseBelowRangeColor,
controllerText("Below min"));
m_pScalarBarLayer->setAboveRangeSwatch(
oDisplay.m_oSettings.m_bUseAboveRangeColor,
controllerText("Above max"));
}
void applyLayerVisibility(bool bHasData)
{
synchronizeLayerPointers();
if(m_pMeshLayer != nullptr) {
// 结果网格固定显示彩色表面和黑色单元边线,不保存可关闭的偏好。
m_pMeshLayer->setDisplayMode(
nmVTKMeshLayer::DisplayMode_SurfaceWithEdges);
m_pMeshLayer->setScalarVisibility(bHasData);
// 彩色结果网格是有效快照的主内容,不提供独立隐藏状态。
m_pMeshLayer->setVisible(bHasData);
}
if(m_pScalarBarLayer != nullptr) {
m_pScalarBarLayer->setVisible(
bHasData && m_bScalarBarVisible);
}
if(m_pScaleGridLayer != nullptr) {
m_pScaleGridLayer->setVisible(
bHasData && m_bScaleGridVisible);
}
if(m_pWellLayer != nullptr) {
// 井图层只包含文字和引线,由 SceneController 统一控制顶层 Prop。
m_pWellLayer->setVisible(
bHasData && m_bWellNamesVisible);
}
if(m_pContainer != nullptr) {
m_pContainer->setRotationCenterPickTarget(
bHasData && m_pMeshLayer != nullptr
? m_pMeshLayer->getActor() : nullptr);
}
}
bool restorePreviousScene()
{
synchronizeLayerPointers();
if(!hasOwnedBaseLayers() || m_pContainer == nullptr) {
return false;
}
if(m_pAdapter == nullptr || m_pSnapshot.isNull()) {
m_pMeshLayer->clearInput();
m_pContainer->clearPrimaryViewBounds();
if(m_pScalarBarLayer != nullptr) {
m_pScalarBarLayer->setLookupTable(nullptr);
m_pScalarBarLayer->setTitle(QString());
m_pScalarBarLayer->setNumberOfLabels(5);
m_pScalarBarLayer->setBelowRangeSwatch(false, QString());
m_pScalarBarLayer->setAboveRangeSwatch(false, QString());
m_pScalarBarLayer->setConstantValueLabel(false, QString());
}
applyLayerVisibility(false);
return true;
}
const bool bMeshRestored = m_pMeshLayer->getLookupTable() != nullptr &&
bindMeshAdapter(m_pAdapter, m_pMeshLayer->getLookupTable());
bool bGridRestored = !m_bHasBounds;
bool bBoundsRestored = !m_bHasBounds;
if(m_bHasBounds && m_pScaleGridLayer != nullptr) {
bGridRestored = m_pScaleGridLayer->setBounds(
m_dBounds[0], m_dBounds[1],
m_dBounds[2], m_dBounds[3], m_dBounds[4]);
bBoundsRestored = m_pContainer->setPrimaryViewBounds(m_dBounds);
}
if(m_pScalarBarLayer != nullptr) {
m_pScalarBarLayer->setLookupTable(
m_pMeshLayer->getLookupTable());
}
applyLayerVisibility(true);
return bMeshRestored && bGridRestored && bBoundsRestored;
}
void releaseActiveData()
{
// Scene 可能已由外部清空;先按稳定 ID 解除所有悬空观察指针。
synchronizeLayerPointers();
if(m_pContainer != nullptr) {
m_pContainer->setRotationCenterPickTarget(nullptr);
m_pContainer->clearPrimaryViewBounds();
}
if(m_pMeshLayer != nullptr) {
m_pMeshLayer->setScalarVisibility(false);
m_pMeshLayer->clearInput();
m_pMeshLayer->setVisible(false);
}
if(m_pScalarBarLayer != nullptr) {
m_pScalarBarLayer->setVisible(false);
m_pScalarBarLayer->setLookupTable(nullptr);
m_pScalarBarLayer->setTitle(QString());
m_pScalarBarLayer->setNumberOfLabels(5);
m_pScalarBarLayer->setBelowRangeSwatch(false, QString());
m_pScalarBarLayer->setAboveRangeSwatch(false, QString());
m_pScalarBarLayer->setConstantValueLabel(false, QString());
}
if(m_pScaleGridLayer != nullptr) {
m_pScaleGridLayer->setVisible(false);
}
if(m_pWellLayer != nullptr) {
m_pWellLayer->setVisible(false);
m_pWellLayer->clear();
}
delete m_pAdapter;
m_pAdapter = nullptr;
m_pSnapshot.clear();
m_listAvailablePropertyIds.clear();
m_sCurrentPropertyId.clear();
m_nCurrentFrameIndex = -1;
m_bHasPropertyRange = false;
m_bHasBounds = false;
// clear()保留窗口样式,但任何活动快照相关的手工范围都必须失效。
QMap<QString, nmPebiResultDisplaySettings>::iterator oIterator =
m_mapDisplaySettings.begin();
for(; oIterator != m_mapDisplaySettings.end(); ++oIterator) {
oIterator.value().m_bHasManualRange = false;
oIterator.value().m_dManualMinimum = 0.0;
oIterator.value().m_dManualMaximum = 0.0;
}
}
void removeOwnedLayers()
{
synchronizeLayerPointers();
nmVTKScene* pScene = getScene();
if(pScene == nullptr) {
return;
}
// 只删除仍与本控制器记录身份一致的图层,绝不误删同 ID 的外部替代层。
if(m_pWellLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID)) ==
m_pWellLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_WELLS_LAYER_ID));
}
m_pWellLayer = nullptr;
if(m_pScaleGridLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID)) ==
m_pScaleGridLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_SCALE_GRID_LAYER_ID));
}
m_pScaleGridLayer = nullptr;
if(m_pScalarBarLayer != nullptr &&
pScene->getLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID)) ==
m_pScalarBarLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_SCALAR_BAR_LAYER_ID));
}
m_pScalarBarLayer = nullptr;
if(m_pMeshLayer != nullptr &&
pScene->getLayer(QString::fromLatin1(RESULT_MESH_LAYER_ID)) ==
m_pMeshLayer) {
pScene->removeLayer(
QString::fromLatin1(RESULT_MESH_LAYER_ID));
}
m_pMeshLayer = nullptr;
}
nmWxVTKRenderContainerWidget* m_pContainer; ///< 非所有权指针。
nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有。
nmVTKScalarBarLayer* m_pScalarBarLayer; ///< Scene 所有。
nmVTKPlanarScaleGridLayer* m_pScaleGridLayer; ///< Scene 所有。
nmPebiResultWellAnnotationLayer* m_pWellLayer; ///< Scene 所有。
nmPebiResultSnapshotRenderAdapter* m_pAdapter; ///< 控制器独占。
QSharedPointer<const nmPebiResultSnapshot> m_pSnapshot;
QStringList m_listAvailablePropertyIds;
QString m_sCurrentPropertyId;
/** @brief 按稳定属性 ID 保存的窗口级样式;clear 不删除。 */
QMap<QString, nmPebiResultDisplaySettings> m_mapDisplaySettings;
int m_nCurrentFrameIndex;
/** @brief 不同快照对象完整提交成功后的单调序号,clear 和失败均不回退。 */
quint64 m_nSnapshotBindingRevision;
double m_dBusinessRange[2];
double m_dDisplayRange[2];
double m_dBounds[6];
bool m_bHasPropertyRange;
bool m_bHasBounds;
bool m_bScalarBarVisible;
bool m_bScaleGridVisible;
bool m_bWellNamesVisible;
};
nmPebiResultSceneController::nmPebiResultSceneController(
nmWxVTKRenderContainerWidget* pContainer)
: m_pPrivate(new nmPebiResultSceneControllerPrivate(pContainer))
{
Q_ASSERT(isResultSceneGuiThread());
}
nmPebiResultSceneController::~nmPebiResultSceneController()
{
Q_ASSERT(isResultSceneGuiThread());
if(m_pPrivate != nullptr) {
m_pPrivate->releaseActiveData();
// Controller 结束后立即移除自己的业务图层,使同一容器可安全创建新实例。
m_pPrivate->removeOwnedLayers();
m_pPrivate->m_pContainer = nullptr;
delete m_pPrivate;
m_pPrivate = nullptr;
}
}
bool nmPebiResultSceneController::setSnapshot(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
QString* pError)
{
Q_ASSERT(isResultSceneGuiThread());
if(pError != nullptr) {
pError->clear();
}
if(!isResultSceneGuiThread() || m_pPrivate == nullptr ||
m_pPrivate->m_pContainer == nullptr || pSnapshot.isNull() ||
!pSnapshot->isComplete()) {
if(pError != nullptr) {
*pError = controllerText("The result snapshot is invalid.");
}
return false;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_pSnapshot == pSnapshot &&
m_pPrivate->m_pSnapshot->getSnapshotId() ==
pSnapshot->getSnapshotId()) {
return true;
}
const QString sInitialProperty = chooseInitialProperty(pSnapshot);
nmPebiResultPropertyDescriptor oInitialDescriptor;
if(sInitialProperty.isEmpty() ||
!nmPebiResultPropertyCatalog::findDescriptor(
sInitialProperty, oInitialDescriptor) ||
oInitialDescriptor.m_eAssociation !=
NM_PebiResultPropertyAssociation_CellData) {
if(pError != nullptr) {
*pError = controllerText(
"The result snapshot has no renderable property.");
}
return false;
}
nmPebiResultSnapshotRenderAdapter* pCandidateAdapter =
new nmPebiResultSnapshotRenderAdapter();
if(!pCandidateAdapter->initialize(
pSnapshot,
nmPebiResultSnapshotRenderAdapter::SharedPropertyArray) ||
!pCandidateAdapter->setPropertyFrame(sInitialProperty, 0)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"Unable to initialize the result render adapter.");
}
return false;
}
// 新快照只继承样式;所有属性的手工范围都在候选表中先清除。
QMap<QString, nmPebiResultDisplaySettings> mapCandidateSettings =
m_pPrivate->m_mapDisplaySettings;
QMap<QString, nmPebiResultDisplaySettings>::iterator oSettingsIterator =
mapCandidateSettings.begin();
for(; oSettingsIterator != mapCandidateSettings.end();
++oSettingsIterator) {
oSettingsIterator.value().m_bHasManualRange = false;
oSettingsIterator.value().m_dManualMinimum = 0.0;
oSettingsIterator.value().m_dManualMaximum = 0.0;
oSettingsIterator.value() = oSettingsIterator.value().normalized();
}
const QStringList listCandidatePropertyIds =
pSnapshot->getAvailablePropertyIds();
QString sDisplayError;
for(int nPropertyIndex = 0;
nPropertyIndex < listCandidatePropertyIds.size();
++nPropertyIndex) {
const QString& sPropertyId =
listCandidatePropertyIds[nPropertyIndex];
nmPebiResultDisplaySettings oSettings =
mapCandidateSettings.value(
sPropertyId, nmPebiResultDisplaySettings()).normalized();
double dPropertyRange[2] = { 0.0, 0.0 };
if(!pSnapshot->getPropertyRange(sPropertyId, dPropertyRange)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"The result property range is invalid.");
}
return false;
}
if(oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic &&
dPropertyRange[0] <= 0.0) {
// 当前快照不支持原 Log 时永久降级,避免保留不可见的隐藏偏好。
oSettings.m_eScaleMode = NM_PebiColorScale_Linear;
}
mapCandidateSettings.insert(sPropertyId, oSettings);
// 在触碰活动 Scene 前预检每个可用属性,避免隐藏的无效偏好延迟到切换时。
ResultDisplayCandidate oPropertyDisplayCandidate;
if(!prepareDisplayCandidate(
pSnapshot, sPropertyId, oSettings,
oPropertyDisplayCandidate, &sDisplayError)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = sDisplayError;
}
return false;
}
}
ResultDisplayCandidate oDisplayCandidate;
if(!prepareDisplayCandidate(
pSnapshot, sInitialProperty,
mapCandidateSettings.value(sInitialProperty),
oDisplayCandidate, &sDisplayError)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = sDisplayError;
}
return false;
}
double dBounds[6];
if(pCandidateAdapter->getCurrentScalarArrayName().isEmpty() ||
!validateAdapterOutput(pCandidateAdapter,
pSnapshot->getResultCellCount(), dBounds)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"The result render data failed validation.");
}
return false;
}
bool bCreatedMesh = false;
bool bCreatedScalarBar = false;
bool bCreatedScaleGrid = false;
bool bCreatedWells = false;
if(!m_pPrivate->ensureBaseLayers(
bCreatedMesh, bCreatedScalarBar,
bCreatedScaleGrid, bCreatedWells)) {
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"Unable to create the result render layers.");
}
return false;
}
if(!m_pPrivate->m_pWellLayer->prepareCandidate(pSnapshot)) {
m_pPrivate->rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar,
bCreatedScaleGrid, bCreatedWells);
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"The result render data failed validation.");
}
return false;
}
bool bCommitted = m_pPrivate->m_pScaleGridLayer->setBounds(
dBounds[0], dBounds[1], dBounds[2],
dBounds[3], dBounds[4]) &&
m_pPrivate->m_pContainer->setPrimaryViewBounds(dBounds) &&
m_pPrivate->bindMeshAdapter(
pCandidateAdapter, oDisplayCandidate.m_pLookupTable);
if(!bCommitted) {
m_pPrivate->m_pWellLayer->discardCandidate();
const bool bRestored = m_pPrivate->restorePreviousScene();
if(!bRestored) {
// 不允许 Release 构建留下“逻辑有快照、实际无输入”的半恢复状态。
m_pPrivate->releaseActiveData();
}
m_pPrivate->rollbackCreatedLayers(
bCreatedMesh, bCreatedScalarBar,
bCreatedScaleGrid, bCreatedWells);
delete pCandidateAdapter;
if(pError != nullptr) {
*pError = controllerText(
"Unable to commit the result render data.");
}
return false;
}
// prepareCandidate 成功后,commitCandidate 仅交换 Assembly 子项且没有失败分支。
m_pPrivate->m_pWellLayer->commitCandidate();
nmPebiResultSnapshotRenderAdapter* pPreviousAdapter =
m_pPrivate->m_pAdapter;
m_pPrivate->m_pAdapter = pCandidateAdapter;
m_pPrivate->m_pSnapshot = pSnapshot;
m_pPrivate->m_listAvailablePropertyIds =
listCandidatePropertyIds;
m_pPrivate->m_sCurrentPropertyId = sInitialProperty;
m_pPrivate->m_mapDisplaySettings = mapCandidateSettings;
m_pPrivate->m_nCurrentFrameIndex = 0;
m_pPrivate->m_dBusinessRange[0] =
oDisplayCandidate.m_dBusinessRange[0];
m_pPrivate->m_dBusinessRange[1] =
oDisplayCandidate.m_dBusinessRange[1];
m_pPrivate->m_dDisplayRange[0] =
oDisplayCandidate.m_dVisibleRange[0];
m_pPrivate->m_dDisplayRange[1] =
oDisplayCandidate.m_dVisibleRange[1];
copyBounds(dBounds, m_pPrivate->m_dBounds);
m_pPrivate->m_bHasPropertyRange = true;
m_pPrivate->m_bHasBounds = true;
m_pPrivate->applyScalarBarState(oDisplayCandidate);
m_pPrivate->applyLayerVisibility(true);
// Mapper 已不再引用旧 Producer,此时才允许释放旧 Adapter。
delete pPreviousAdapter;
const bool bViewFitted =
m_pPrivate->m_pContainer->fitTopViewWithoutRender();
const bool bCenterReset =
m_pPrivate->m_pContainer->resetRotationCenterWithoutRender();
Q_ASSERT(bViewFitted && bCenterReset);
Q_UNUSED(bViewFitted);
Q_UNUSED(bCenterReset);
// showReadyState 是本提交路径唯一的 Render 入口。
m_pPrivate->m_pContainer->showReadyState();
// 候选管线、相机和首次 Render 已完整提交,最后才发布新绑定序号。
++m_pPrivate->m_nSnapshotBindingRevision;
return true;
}
void nmPebiResultSceneController::clear()
{
Q_ASSERT(isResultSceneGuiThread());
if(isResultSceneGuiThread() && m_pPrivate != nullptr) {
m_pPrivate->releaseActiveData();
}
}
bool nmPebiResultSceneController::applyPropertyFrame(
const QString& sPropertyId,
int nFrameIndex,
QString* pError)
{
Q_ASSERT(isResultSceneGuiThread());
if(pError != nullptr) {
pError->clear();
}
if(isResultSceneGuiThread() && m_pPrivate != nullptr) {
m_pPrivate->discardActiveDataIfLayersLost();
}
if(!isResultSceneGuiThread() || m_pPrivate == nullptr ||
m_pPrivate->m_pAdapter == nullptr ||
m_pPrivate->m_pSnapshot.isNull() ||
!m_pPrivate->hasOwnedBaseLayers() ||
!m_pPrivate->m_pSnapshot->hasProperty(sPropertyId) ||
nFrameIndex < 0 ||
nFrameIndex >= m_pPrivate->m_pSnapshot->getPropertyFrameCount(
sPropertyId)) {
if(pError != nullptr) {
*pError = controllerText(
"The requested result property or frame is invalid.");
}
return false;
}
nmPebiResultPropertyDescriptor oDescriptor;
if(!nmPebiResultPropertyCatalog::findDescriptor(
sPropertyId, oDescriptor) ||
oDescriptor.m_eAssociation !=
NM_PebiResultPropertyAssociation_CellData ||
(oDescriptor.m_eTimeType == NM_PebiResultProperty_Static &&
nFrameIndex != 0)) {
if(pError != nullptr) {
*pError = controllerText(
"The requested result property or frame is invalid.");
}
return false;
}
if(sPropertyId == m_pPrivate->m_sCurrentPropertyId &&
nFrameIndex == m_pPrivate->m_nCurrentFrameIndex) {
return true;
}
const nmPebiResultDisplaySettings oTargetSettings =
m_pPrivate->m_mapDisplaySettings.value(
sPropertyId, nmPebiResultDisplaySettings());
ResultDisplayCandidate oDisplayCandidate;
if(!prepareDisplayCandidate(
m_pPrivate->m_pSnapshot, sPropertyId, oTargetSettings,
oDisplayCandidate, pError)) {
return false;
}
const QString sPreviousProperty =
m_pPrivate->m_sCurrentPropertyId;
const int nPreviousFrame = m_pPrivate->m_nCurrentFrameIndex;
bool bApplied = m_pPrivate->m_pAdapter->setPropertyFrame(
sPropertyId, nFrameIndex);
double dCurrentBounds[6];
if(bApplied) {
bApplied = validateAdapterOutput(
m_pPrivate->m_pAdapter,
m_pPrivate->m_pSnapshot->getResultCellCount(),
dCurrentBounds) &&
m_pPrivate->m_pMeshLayer->setScalarArray(
m_pPrivate->m_pAdapter->getCurrentScalarArrayName(),
nmVTKMeshLayer::ScalarAssociation_CellData,
oDisplayCandidate.m_pLookupTable);
}
if(!bApplied) {
const bool bAdapterRestored =
m_pPrivate->m_pAdapter->setPropertyFrame(
sPreviousProperty, nPreviousFrame);
if(!bAdapterRestored) {
// 极端恢复失败时清空逻辑和 VTK 状态,保证查询结果与实际画面一致。
m_pPrivate->releaseActiveData();
}
if(pError != nullptr) {
*pError = controllerText(
"Unable to apply the result property or frame.");
}
return false;
}
m_pPrivate->m_sCurrentPropertyId = sPropertyId;
m_pPrivate->m_nCurrentFrameIndex = nFrameIndex;
m_pPrivate->m_mapDisplaySettings.insert(
sPropertyId, oDisplayCandidate.m_oSettings);
m_pPrivate->m_dBusinessRange[0] =
oDisplayCandidate.m_dBusinessRange[0];
m_pPrivate->m_dBusinessRange[1] =
oDisplayCandidate.m_dBusinessRange[1];
m_pPrivate->m_dDisplayRange[0] =
oDisplayCandidate.m_dVisibleRange[0];
m_pPrivate->m_dDisplayRange[1] =
oDisplayCandidate.m_dVisibleRange[1];
m_pPrivate->m_bHasPropertyRange = true;
m_pPrivate->applyScalarBarState(oDisplayCandidate);
m_pPrivate->m_pContainer->renderView();
return true;
}
bool nmPebiResultSceneController::applyCurrentDisplaySettings(
quint64 nExpectedBindingRevision,
const QString& sExpectedSnapshotId,
const QString& sExpectedPropertyId,
const nmPebiResultDisplaySettings& oSettings,
QString* pError)
{
Q_ASSERT(isResultSceneGuiThread());
if(pError != nullptr) {
pError->clear();
}
if(isResultSceneGuiThread() && m_pPrivate != nullptr) {
m_pPrivate->discardActiveDataIfLayersLost();
}
// 身份核对和实际提交处在同一个 GUI 线程调用中,旧对话框不能污染新快照。
if(!isResultSceneGuiThread() || !hasSnapshot() ||
nExpectedBindingRevision == 0 ||
nExpectedBindingRevision != getSnapshotBindingRevision() ||
sExpectedSnapshotId.isEmpty() ||
sExpectedPropertyId.isEmpty() ||
sExpectedSnapshotId != getCurrentSnapshotId() ||
sExpectedPropertyId != m_pPrivate->m_sCurrentPropertyId) {
if(pError != nullptr) {
*pError = controllerText(
"The color settings no longer match the current result.");
}
return false;
}
ResultDisplayCandidate oDisplayCandidate;
if(!prepareDisplayCandidate(
m_pPrivate->m_pSnapshot,
sExpectedPropertyId,
oSettings,
oDisplayCandidate,
pError)) {
return false;
}
const nmPebiResultDisplaySettings oCurrentSettings =
m_pPrivate->m_mapDisplaySettings.value(
sExpectedPropertyId, nmPebiResultDisplaySettings());
if(oDisplayCandidate.m_oSettings == oCurrentSettings.normalized()) {
return true;
}
// MeshLayer 先完整验证候选 LUT;失败时旧 Mapper 和 ScalarBar 仍共享旧 LUT。
if(!m_pPrivate->m_pMeshLayer->setLookupTable(
oDisplayCandidate.m_pLookupTable)) {
if(pError != nullptr) {
*pError = controllerText("Unable to apply the color settings.");
}
return false;
}
m_pPrivate->m_mapDisplaySettings.insert(
sExpectedPropertyId, oDisplayCandidate.m_oSettings);
m_pPrivate->m_dBusinessRange[0] =
oDisplayCandidate.m_dBusinessRange[0];
m_pPrivate->m_dBusinessRange[1] =
oDisplayCandidate.m_dBusinessRange[1];
m_pPrivate->m_dDisplayRange[0] =
oDisplayCandidate.m_dVisibleRange[0];
m_pPrivate->m_dDisplayRange[1] =
oDisplayCandidate.m_dVisibleRange[1];
m_pPrivate->m_bHasPropertyRange = true;
m_pPrivate->applyScalarBarState(oDisplayCandidate);
m_pPrivate->m_pContainer->renderView();
return true;
}
void nmPebiResultSceneController::setScalarBarVisible(bool bVisible)
{
Q_ASSERT(isResultSceneGuiThread());
if(!isResultSceneGuiThread() || m_pPrivate == nullptr) {
return;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_bScalarBarVisible == bVisible) {
return;
}
m_pPrivate->m_bScalarBarVisible = bVisible;
const bool bHasData = hasSnapshot();
m_pPrivate->applyLayerVisibility(bHasData);
if(bHasData) {
m_pPrivate->m_pContainer->renderView();
}
}
void nmPebiResultSceneController::setScaleGridVisible(bool bVisible)
{
Q_ASSERT(isResultSceneGuiThread());
if(!isResultSceneGuiThread() || m_pPrivate == nullptr) {
return;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_bScaleGridVisible == bVisible) {
return;
}
m_pPrivate->m_bScaleGridVisible = bVisible;
const bool bHasData = hasSnapshot();
m_pPrivate->applyLayerVisibility(bHasData);
if(bHasData) {
m_pPrivate->m_pContainer->renderView();
}
}
void nmPebiResultSceneController::setWellNamesVisible(bool bVisible)
{
Q_ASSERT(isResultSceneGuiThread());
if(!isResultSceneGuiThread() || m_pPrivate == nullptr) {
return;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_bWellNamesVisible == bVisible) {
return;
}
m_pPrivate->m_bWellNamesVisible = bVisible;
const bool bHasData = hasSnapshot();
m_pPrivate->applyLayerVisibility(bHasData);
if(bHasData) {
m_pPrivate->m_pContainer->renderView();
}
}
bool nmPebiResultSceneController::hasSnapshot() const
{
return isResultSceneGuiThread() &&
m_pPrivate != nullptr &&
m_pPrivate->m_pAdapter != nullptr &&
!m_pPrivate->m_pSnapshot.isNull() &&
m_pPrivate->hasOwnedBaseLayers() &&
m_pPrivate->m_pWellLayer->hasActiveAnnotations();
}
quint64 nmPebiResultSceneController::getSnapshotBindingRevision() const
{
return m_pPrivate != nullptr
? m_pPrivate->m_nSnapshotBindingRevision : 0;
}
QString nmPebiResultSceneController::getCurrentSnapshotId() const
{
return hasSnapshot()
? m_pPrivate->m_pSnapshot->getSnapshotId() : QString();
}
QStringList nmPebiResultSceneController::getAvailablePropertyIds() const
{
return hasSnapshot()
? m_pPrivate->m_listAvailablePropertyIds : QStringList();
}
QString nmPebiResultSceneController::getCurrentPropertyId() const
{
return hasSnapshot()
? m_pPrivate->m_sCurrentPropertyId : QString();
}
int nmPebiResultSceneController::getCurrentFrameIndex() const
{
return hasSnapshot()
? m_pPrivate->m_nCurrentFrameIndex : -1;
}
bool nmPebiResultSceneController::isPropertyTimeDependent(
const QString& sPropertyId) const
{
if(!hasSnapshot() ||
!m_pPrivate->m_pSnapshot->hasProperty(sPropertyId)) {
return false;
}
nmPebiResultPropertyDescriptor oDescriptor;
return nmPebiResultPropertyCatalog::findDescriptor(
sPropertyId, oDescriptor) &&
oDescriptor.m_eTimeType == NM_PebiResultProperty_Dynamic;
}
int nmPebiResultSceneController::getPropertyFrameCount(
const QString& sPropertyId) const
{
return hasSnapshot()
? m_pPrivate->m_pSnapshot->getPropertyFrameCount(sPropertyId)
: 0;
}
bool nmPebiResultSceneController::getPropertyTimeAt(
const QString& sPropertyId,
int nFrameIndex,
double& dTime) const
{
// 快照接口保证失败时不写输出参数,这里直接保持该事务语义。
return hasSnapshot() &&
m_pPrivate->m_pSnapshot->getPropertyTimeAt(
sPropertyId, nFrameIndex, dTime);
}
bool nmPebiResultSceneController::getCurrentPropertyRange(
double dRange[2]) const
{
if(!hasSnapshot() || dRange == nullptr ||
!m_pPrivate->m_bHasPropertyRange) {
return false;
}
dRange[0] = m_pPrivate->m_dBusinessRange[0];
dRange[1] = m_pPrivate->m_dBusinessRange[1];
return true;
}
bool nmPebiResultSceneController::getCurrentDisplayRange(
double dRange[2]) const
{
if(!hasSnapshot() || dRange == nullptr ||
!m_pPrivate->m_bHasPropertyRange) {
return false;
}
dRange[0] = m_pPrivate->m_dDisplayRange[0];
dRange[1] = m_pPrivate->m_dDisplayRange[1];
return true;
}
bool nmPebiResultSceneController::getCurrentDisplaySettings(
nmPebiResultDisplaySettings& oSettings) const
{
if(!hasSnapshot() ||
!m_pPrivate->m_mapDisplaySettings.contains(
m_pPrivate->m_sCurrentPropertyId)) {
return false;
}
oSettings = m_pPrivate->m_mapDisplaySettings.value(
m_pPrivate->m_sCurrentPropertyId);
return true;
}
bool nmPebiResultSceneController::isScalarBarVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bScalarBarVisible;
}
bool nmPebiResultSceneController::isScaleGridVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bScaleGridVisible;
}
bool nmPebiResultSceneController::areWellNamesVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bWellNamesVisible;
}