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

1543 lines
56 KiB
C++

#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_bCellEdgesVisible(false),
m_bWellMarkersVisible(true),
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);
pLayer->setWellMarkersVisible(m_bWellMarkersVisible);
pLayer->setWellNamesVisible(m_bWellNamesVisible);
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(m_bCellEdgesVisible
? nmVTKMeshLayer::DisplayMode_SurfaceWithEdges
: nmVTKMeshLayer::DisplayMode_Surface);
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) {
m_pWellLayer->setWellMarkersVisible(m_bWellMarkersVisible);
m_pWellLayer->setWellNamesVisible(m_bWellNamesVisible);
m_pWellLayer->setVisible(bHasData);
}
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_bCellEdgesVisible;
bool m_bWellMarkersVisible;
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::setCellEdgesVisible(bool bVisible)
{
Q_ASSERT(isResultSceneGuiThread());
if(!isResultSceneGuiThread() || m_pPrivate == nullptr) {
return;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_bCellEdgesVisible == bVisible) {
return;
}
m_pPrivate->m_bCellEdgesVisible = bVisible;
const bool bHasData = hasSnapshot();
m_pPrivate->applyLayerVisibility(bHasData);
if(bHasData) {
m_pPrivate->m_pContainer->renderView();
}
}
void nmPebiResultSceneController::setWellMarkersVisible(bool bVisible)
{
Q_ASSERT(isResultSceneGuiThread());
if(!isResultSceneGuiThread() || m_pPrivate == nullptr) {
return;
}
m_pPrivate->discardActiveDataIfLayersLost();
if(m_pPrivate->m_bWellMarkersVisible == bVisible) {
return;
}
m_pPrivate->m_bWellMarkersVisible = bVisible;
if(m_pPrivate->m_pWellLayer != nullptr) {
m_pPrivate->m_pWellLayer->setWellMarkersVisible(bVisible);
}
if(hasSnapshot()) {
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;
if(m_pPrivate->m_pWellLayer != nullptr) {
m_pPrivate->m_pWellLayer->setWellNamesVisible(bVisible);
}
if(hasSnapshot()) {
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::areCellEdgesVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bCellEdgesVisible;
}
bool nmPebiResultSceneController::areWellMarkersVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bWellMarkersVisible;
}
bool nmPebiResultSceneController::areWellNamesVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bWellNamesVisible;
}