|
|
#include "nmPebiResultDisplayStateBuilder.h"
|
|
|
|
|
|
#include "nmPebiResultPropertyDisplayText.h"
|
|
|
#include "nmPebiResultSnapshot.h"
|
|
|
#include "nmVTKColorMapCatalog.h"
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
#include <QtGlobal>
|
|
|
|
|
|
#include <vtkLookupTable.h>
|
|
|
#include <vtkMath.h>
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
QString displayStateText(const char* pText)
|
|
|
{
|
|
|
// 沿用原 SceneController 翻译上下文,避免迁移既有 TS 条目。
|
|
|
return QCoreApplication::translate(
|
|
|
"nmPebiResultSceneController", pText);
|
|
|
}
|
|
|
|
|
|
bool getInternalDisplayRange(const double dVisibleRange[2],
|
|
|
bool bLogarithmic,
|
|
|
bool bSingleValueMode,
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
if(dVisibleRange[0] < dVisibleRange[1]) {
|
|
|
dInternalRange[0] = dVisibleRange[0];
|
|
|
dInternalRange[1] = dVisibleRange[1];
|
|
|
return true;
|
|
|
}
|
|
|
if(!bSingleValueMode || dVisibleRange[0] != dVisibleRange[1]) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// VTK要求映射范围严格递增;微量扩展只供内部映射,不改变业务值。
|
|
|
const double dValue = dVisibleRange[0];
|
|
|
double dMaximum = 0.0;
|
|
|
if(bLogarithmic) {
|
|
|
dMaximum = dValue * (1.0 + 1.0e-6);
|
|
|
if(!vtkMath::IsFinite(dMaximum) || dMaximum <= dValue) {
|
|
|
dMaximum = dValue * 2.0;
|
|
|
}
|
|
|
} else {
|
|
|
const double dEpsilon = qMax(1.0, fabs(dValue)) * 1.0e-6;
|
|
|
dMaximum = dValue + dEpsilon;
|
|
|
}
|
|
|
if(!vtkMath::IsFinite(dMaximum) || dValue >= dMaximum ||
|
|
|
(bLogarithmic && dValue <= 0.0)) {
|
|
|
return false;
|
|
|
}
|
|
|
dInternalRange[0] = dValue;
|
|
|
dInternalRange[1] = dMaximum;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool fillLookupTableWithFirstColor(vtkLookupTable* pLookupTable)
|
|
|
{
|
|
|
if(pLookupTable == nullptr ||
|
|
|
pLookupTable->GetNumberOfTableValues() <= 0) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
double dFirstColor[4] = { 0.0, 0.0, 0.0, 1.0 };
|
|
|
pLookupTable->GetTableValue(0, dFirstColor);
|
|
|
for(int nComponent = 0; nComponent < 4; ++nComponent) {
|
|
|
if(!vtkMath::IsFinite(dFirstColor[nComponent])) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const int nTableValueCount = static_cast<int>(
|
|
|
pLookupTable->GetNumberOfTableValues());
|
|
|
for(int nIndex = 0; nIndex < nTableValueCount; ++nIndex) {
|
|
|
pLookupTable->SetTableValue(nIndex, dFirstColor);
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
nmPebiResultDisplayState::nmPebiResultDisplayState()
|
|
|
: m_pLookupTable(nullptr),
|
|
|
m_bSingleValueMode(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;
|
|
|
}
|
|
|
|
|
|
bool nmPebiResultDisplayStateBuilder::build(
|
|
|
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
|
|
|
const QString& sPropertyId,
|
|
|
const nmPebiResultDisplaySettings& oRequestedSettings,
|
|
|
nmPebiResultDisplayState& oState,
|
|
|
QString* pError)
|
|
|
{
|
|
|
if(pError != nullptr) {
|
|
|
pError->clear();
|
|
|
}
|
|
|
nmPebiResultDisplaySettings oSettings =
|
|
|
oRequestedSettings.normalized();
|
|
|
if(pSnapshot.isNull() || !pSnapshot->hasProperty(sPropertyId) ||
|
|
|
!oSettings.isStructurallyValid()) {
|
|
|
if(pError != nullptr) {
|
|
|
*pError = displayStateText("The color settings are invalid.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
if(!nmVTKColorMapCatalog::contains(oSettings.m_sColorMapId)) {
|
|
|
if(pError != nullptr) {
|
|
|
*pError = displayStateText(
|
|
|
"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 = displayStateText(
|
|
|
"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 = displayStateText(
|
|
|
"The manual range must contain two finite increasing values.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
dVisibleRange[0] = oSettings.m_dManualMinimum;
|
|
|
dVisibleRange[1] = oSettings.m_dManualMaximum;
|
|
|
}
|
|
|
|
|
|
const bool bSingleValueMode =
|
|
|
!oSettings.m_bHasManualRange &&
|
|
|
dBusinessRange[0] == dBusinessRange[1];
|
|
|
const bool bLogarithmic =
|
|
|
oSettings.m_eScaleMode == NM_PebiColorScale_Logarithmic;
|
|
|
if(bLogarithmic &&
|
|
|
(dBusinessRange[0] <= 0.0 || dVisibleRange[0] <= 0.0)) {
|
|
|
if(pError != nullptr) {
|
|
|
*pError = displayStateText(
|
|
|
"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,
|
|
|
bSingleValueMode, dInternalRange)) {
|
|
|
if(pError != nullptr) {
|
|
|
*pError = displayStateText(
|
|
|
"Unable to construct the color mapping.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
pLookupTable->SetTableRange(dInternalRange);
|
|
|
if(bLogarithmic) {
|
|
|
pLookupTable->SetScaleToLog10();
|
|
|
} else {
|
|
|
pLookupTable->SetScaleToLinear();
|
|
|
}
|
|
|
// 单值自动范围固定到色带第0色,特殊越界色随后单独构造。
|
|
|
if(bSingleValueMode &&
|
|
|
!fillLookupTableWithFirstColor(pLookupTable)) {
|
|
|
if(pError != nullptr) {
|
|
|
*pError = displayStateText(
|
|
|
"Unable to construct the color mapping.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
pLookupTable->BuildSpecialColors();
|
|
|
pLookupTable->IndexedLookupOff();
|
|
|
pLookupTable->ResetAnnotations();
|
|
|
|
|
|
oState.m_oSettings = oSettings;
|
|
|
oState.m_dBusinessRange[0] = dBusinessRange[0];
|
|
|
oState.m_dBusinessRange[1] = dBusinessRange[1];
|
|
|
oState.m_dVisibleRange[0] = dVisibleRange[0];
|
|
|
oState.m_dVisibleRange[1] = dVisibleRange[1];
|
|
|
oState.m_dInternalRange[0] = dInternalRange[0];
|
|
|
oState.m_dInternalRange[1] = dInternalRange[1];
|
|
|
oState.m_pLookupTable = pLookupTable;
|
|
|
oState.m_sScalarBarTitle =
|
|
|
nmPebiResultPropertyDisplayText::getScalarBarTitle(
|
|
|
sPropertyId, bLogarithmic);
|
|
|
oState.m_bSingleValueMode = bSingleValueMode;
|
|
|
return true;
|
|
|
}
|