|
|
#include "nmVTKMeshLayer.h"
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
#include <QThread>
|
|
|
#include <QtGlobal>
|
|
|
|
|
|
#include <vtkActor.h>
|
|
|
#include <vtkAlgorithm.h>
|
|
|
#include <vtkAlgorithmOutput.h>
|
|
|
#include <vtkCellData.h>
|
|
|
#include <vtkDataArray.h>
|
|
|
#include <vtkDataObject.h>
|
|
|
#include <vtkDataSet.h>
|
|
|
#include <vtkDataSetMapper.h>
|
|
|
#include <vtkLookupTable.h>
|
|
|
#include <vtkMath.h>
|
|
|
#include <vtkPointData.h>
|
|
|
#include <vtkProperty.h>
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
// Mapper、Actor 和输入数据都属于 VTK 状态,只允许 GUI 线程修改。
|
|
|
bool isMeshLayerGuiThread()
|
|
|
{
|
|
|
QCoreApplication* pApplication = QCoreApplication::instance();
|
|
|
return pApplication == nullptr ||
|
|
|
pApplication->thread() == QThread::currentThread();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
nmVTKMeshLayer::nmVTKMeshLayer(const QString& sLayerId)
|
|
|
: nmVTKRenderLayer(sLayerId),
|
|
|
m_pDataSet(nullptr),
|
|
|
m_pInputConnection(nullptr),
|
|
|
m_pMapper(vtkSmartPointer<vtkDataSetMapper>::New()),
|
|
|
m_pActor(vtkSmartPointer<vtkActor>::New()),
|
|
|
m_pLookupTable(vtkSmartPointer<vtkLookupTable>::New()),
|
|
|
m_eDisplayMode(DisplayMode_Surface),
|
|
|
m_eScalarAssociation(ScalarAssociation_CellData),
|
|
|
m_bUsingExplicitScalarRange(false)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
|
|
|
// 图层独占 Mapper、Actor 和 LookupTable;数据集通过智能指针共享。
|
|
|
m_pLookupTable->SetNumberOfTableValues(256);
|
|
|
m_pLookupTable->SetHueRange(0.67, 0.0);
|
|
|
m_pLookupTable->Build();
|
|
|
|
|
|
m_pMapper->SetLookupTable(m_pLookupTable);
|
|
|
m_pMapper->ScalarVisibilityOff();
|
|
|
m_pActor->SetMapper(m_pMapper);
|
|
|
m_pActor->GetProperty()->SetRepresentationToSurface();
|
|
|
m_pActor->GetProperty()->EdgeVisibilityOff();
|
|
|
setRenderProp(m_pActor);
|
|
|
}
|
|
|
|
|
|
nmVTKMeshLayer::~nmVTKMeshLayer()
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
// 派生成员释放前先解除 Renderer,基类析构中的 detach 仍保持幂等。
|
|
|
detach();
|
|
|
clearInput();
|
|
|
m_pDataSet = nullptr;
|
|
|
m_pInputConnection = nullptr;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setDataSet(vtkDataSet* pDataSet)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread()) {
|
|
|
return false;
|
|
|
}
|
|
|
if(pDataSet == nullptr) {
|
|
|
clearInput();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
double dScalarRange[2] = { 0.0, 1.0 };
|
|
|
if(!validateInputDataSet(pDataSet, dScalarRange)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 所有可能失败的校验均在替换 Mapper 输入前完成。
|
|
|
m_pMapper->SetInputData(pDataSet);
|
|
|
m_pDataSet = pDataSet;
|
|
|
m_pInputConnection = nullptr;
|
|
|
applyRetainedScalarState(dScalarRange);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setInputConnection(
|
|
|
vtkAlgorithmOutput* pInputConnection)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || pInputConnection == nullptr ||
|
|
|
pInputConnection->GetProducer() == nullptr ||
|
|
|
pInputConnection->GetIndex() < 0) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
vtkAlgorithm* pProducer = pInputConnection->GetProducer();
|
|
|
pProducer->Update(pInputConnection->GetIndex());
|
|
|
vtkDataSet* pCandidateDataSet = vtkDataSet::SafeDownCast(
|
|
|
pProducer->GetOutputDataObject(
|
|
|
pInputConnection->GetIndex()));
|
|
|
double dScalarRange[2] = { 0.0, 1.0 };
|
|
|
if(!validateInputDataSet(pCandidateDataSet, dScalarRange)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 输出对象、保留数组和自动范围均已验证,此后才替换活动连接。
|
|
|
m_pMapper->SetInputConnection(pInputConnection);
|
|
|
m_pInputConnection = pInputConnection;
|
|
|
m_pDataSet = nullptr;
|
|
|
applyRetainedScalarState(dScalarRange);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setInputConnection(
|
|
|
vtkAlgorithmOutput* pInputConnection,
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
double dMinimum,
|
|
|
double dMaximum)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || pInputConnection == nullptr ||
|
|
|
pInputConnection->GetProducer() == nullptr ||
|
|
|
pInputConnection->GetIndex() < 0 || sArrayName.isEmpty() ||
|
|
|
!vtkMath::IsFinite(dMinimum) || !vtkMath::IsFinite(dMaximum) ||
|
|
|
dMinimum > dMaximum) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
vtkAlgorithm* pProducer = pInputConnection->GetProducer();
|
|
|
const int nOutputPort = pInputConnection->GetIndex();
|
|
|
pProducer->Update(nOutputPort);
|
|
|
vtkDataSet* pCandidateDataSet = vtkDataSet::SafeDownCast(
|
|
|
pProducer->GetOutputDataObject(nOutputPort));
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
double dDataRange[2] = { 0.0, 0.0 };
|
|
|
if(validateScalarArray(pCandidateDataSet, aArrayName,
|
|
|
eAssociation, dDataRange) == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 到这里所有可能失败的条件均已排除。下面只执行 VTK 无返回值 setter,
|
|
|
// 因而输入、数组和范围会作为一个不可分割的 Mapper 状态提交。
|
|
|
m_pMapper->SetInputConnection(pInputConnection);
|
|
|
m_pInputConnection = pInputConnection;
|
|
|
m_pDataSet = nullptr;
|
|
|
applyExplicitScalarState(aArrayName, eAssociation,
|
|
|
dMinimum, dMaximum);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setInputConnection(
|
|
|
vtkAlgorithmOutput* pInputConnection,
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
vtkLookupTable* pCandidateLookupTable)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || pInputConnection == nullptr ||
|
|
|
pInputConnection->GetProducer() == nullptr ||
|
|
|
pInputConnection->GetIndex() < 0 || sArrayName.isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
vtkAlgorithm* pProducer = pInputConnection->GetProducer();
|
|
|
const int nOutputPort = pInputConnection->GetIndex();
|
|
|
pProducer->Update(nOutputPort);
|
|
|
vtkDataSet* pCandidateDataSet = vtkDataSet::SafeDownCast(
|
|
|
pProducer->GetOutputDataObject(nOutputPort));
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
double dDataRange[2] = { 0.0, 0.0 };
|
|
|
double dLookupRange[2] = { 0.0, 0.0 };
|
|
|
if(validateScalarArray(pCandidateDataSet, aArrayName,
|
|
|
eAssociation, dDataRange) == nullptr ||
|
|
|
!validateLookupTable(pCandidateLookupTable, dLookupRange)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 输入、数组和候选 LUT 已全部验证;以下 VTK setter 均无失败返回值。
|
|
|
m_pMapper->SetInputConnection(pInputConnection);
|
|
|
m_pInputConnection = pInputConnection;
|
|
|
m_pDataSet = nullptr;
|
|
|
applyLookupTableScalarState(aArrayName, eAssociation,
|
|
|
pCandidateLookupTable, dLookupRange);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::clearInput()
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || m_pMapper == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
m_pMapper->RemoveAllInputs();
|
|
|
m_pInputConnection = nullptr;
|
|
|
m_pDataSet = nullptr;
|
|
|
clearInvalidScalarState();
|
|
|
}
|
|
|
|
|
|
vtkDataSet* nmVTKMeshLayer::getDataSet() const
|
|
|
{
|
|
|
return getInputDataSet(false);
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::setDisplayMode(DisplayMode eMode)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || m_pActor == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 未识别枚举值按纯表面处理,避免把无效值带入 VTK Property。
|
|
|
m_eDisplayMode = eMode == DisplayMode_SurfaceWithEdges
|
|
|
? DisplayMode_SurfaceWithEdges : DisplayMode_Surface;
|
|
|
vtkProperty* pProperty = m_pActor->GetProperty();
|
|
|
pProperty->SetRepresentationToSurface();
|
|
|
pProperty->SetEdgeVisibility(
|
|
|
m_eDisplayMode == DisplayMode_SurfaceWithEdges ? 1 : 0);
|
|
|
}
|
|
|
|
|
|
nmVTKMeshLayer::DisplayMode nmVTKMeshLayer::getDisplayMode() const
|
|
|
{
|
|
|
return m_eDisplayMode;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::setEdgeVisibility(bool bVisible)
|
|
|
{
|
|
|
setDisplayMode(bVisible
|
|
|
? DisplayMode_SurfaceWithEdges
|
|
|
: DisplayMode_Surface);
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::isEdgeVisible() const
|
|
|
{
|
|
|
return m_eDisplayMode == DisplayMode_SurfaceWithEdges;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setOpacity(double dOpacity)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || !vtkMath::IsFinite(dOpacity) ||
|
|
|
m_pActor == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
m_pActor->GetProperty()->SetOpacity(qBound(0.0, dOpacity, 1.0));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
double nmVTKMeshLayer::getOpacity() const
|
|
|
{
|
|
|
return m_pActor != nullptr ? m_pActor->GetProperty()->GetOpacity() : 0.0;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::setScalarVisibility(bool bVisible)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || m_pMapper == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
m_pMapper->SetScalarVisibility(bVisible ? 1 : 0);
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::isScalarVisible() const
|
|
|
{
|
|
|
return m_pMapper != nullptr && m_pMapper->GetScalarVisibility() != 0;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setScalarArray(
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
|
|
|
pDataSet == nullptr || m_pMapper == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
double dRange[2] = { 0.0, 0.0 };
|
|
|
if(validateScalarArray(pDataSet, aArrayName,
|
|
|
eAssociation, dRange) == nullptr) {
|
|
|
// 失败时保留原数组选择和原 Mapper 状态。
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
m_aScalarArrayName = aArrayName;
|
|
|
m_eScalarAssociation = eAssociation;
|
|
|
if(eAssociation == ScalarAssociation_CellData) {
|
|
|
m_pMapper->SetScalarModeToUseCellFieldData();
|
|
|
} else {
|
|
|
m_pMapper->SetScalarModeToUsePointFieldData();
|
|
|
}
|
|
|
m_pMapper->SelectColorArray(m_aScalarArrayName.constData());
|
|
|
|
|
|
// 已设置显式范围时不覆盖,否则同步提交已经验证的数据范围。
|
|
|
if(!m_bUsingExplicitScalarRange) {
|
|
|
m_pMapper->SetScalarRange(dRange);
|
|
|
m_pLookupTable->SetRange(dRange);
|
|
|
m_pLookupTable->Build();
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setScalarArray(
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
vtkLookupTable* pCandidateLookupTable)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
|
|
|
pDataSet == nullptr || m_pMapper == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
double dDataRange[2] = { 0.0, 0.0 };
|
|
|
double dLookupRange[2] = { 0.0, 0.0 };
|
|
|
if(validateScalarArray(pDataSet, aArrayName,
|
|
|
eAssociation, dDataRange) == nullptr ||
|
|
|
!validateLookupTable(pCandidateLookupTable, dLookupRange)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
applyLookupTableScalarState(aArrayName, eAssociation,
|
|
|
pCandidateLookupTable, dLookupRange);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setScalarArray(
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
double dMinimum,
|
|
|
double dMaximum)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
|
|
|
pDataSet == nullptr || m_pMapper == nullptr ||
|
|
|
!vtkMath::IsFinite(dMinimum) || !vtkMath::IsFinite(dMaximum) ||
|
|
|
dMinimum > dMaximum) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
double dDataRange[2] = { 0.0, 0.0 };
|
|
|
if(validateScalarArray(pDataSet, aArrayName,
|
|
|
eAssociation, dDataRange) == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 数组和范围都已验证,提交阶段不会再产生可观察的半更新状态。
|
|
|
applyExplicitScalarState(aArrayName, eAssociation,
|
|
|
dMinimum, dMaximum);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
QString nmVTKMeshLayer::getScalarArrayName() const
|
|
|
{
|
|
|
return QString::fromUtf8(m_aScalarArrayName.constData());
|
|
|
}
|
|
|
|
|
|
nmVTKMeshLayer::ScalarAssociation
|
|
|
nmVTKMeshLayer::getScalarAssociation() const
|
|
|
{
|
|
|
return m_eScalarAssociation;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setScalarRange(double dMinimum, double dMaximum)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || !vtkMath::IsFinite(dMinimum) ||
|
|
|
!vtkMath::IsFinite(dMaximum) || dMinimum > dMaximum ||
|
|
|
m_pMapper == nullptr || m_pLookupTable == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// Mapper 与 LookupTable 使用同一范围,保证几何着色和外接色标一致。
|
|
|
m_pMapper->SetScalarRange(dMinimum, dMaximum);
|
|
|
m_pLookupTable->SetRange(dMinimum, dMaximum);
|
|
|
m_pLookupTable->Build();
|
|
|
m_bUsingExplicitScalarRange = true;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::restoreDataScalarRange()
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || !applyDataScalarRange()) {
|
|
|
return false;
|
|
|
}
|
|
|
// 只有成功应用数据范围后才退出显式范围模式。
|
|
|
m_bUsingExplicitScalarRange = false;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::isUsingExplicitScalarRange() const
|
|
|
{
|
|
|
return m_bUsingExplicitScalarRange;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setLookupTable(
|
|
|
vtkLookupTable* pCandidateLookupTable)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
double dLookupRange[2] = { 0.0, 0.0 };
|
|
|
if(!isMeshLayerGuiThread() || getSelectedScalarArray() == nullptr ||
|
|
|
!validateLookupTable(pCandidateLookupTable, dLookupRange)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 候选对象由智能指针共享,Mapper 与 MeshLayer 始终引用同一 LUT。
|
|
|
m_pLookupTable = pCandidateLookupTable;
|
|
|
m_pMapper->SetLookupTable(m_pLookupTable);
|
|
|
m_pMapper->SetScalarRange(dLookupRange);
|
|
|
m_bUsingExplicitScalarRange = true;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
vtkDataSetMapper* nmVTKMeshLayer::getMapper() const
|
|
|
{
|
|
|
return m_pMapper;
|
|
|
}
|
|
|
|
|
|
vtkActor* nmVTKMeshLayer::getActor() const
|
|
|
{
|
|
|
return m_pActor;
|
|
|
}
|
|
|
|
|
|
vtkLookupTable* nmVTKMeshLayer::getLookupTable() const
|
|
|
{
|
|
|
return m_pLookupTable;
|
|
|
}
|
|
|
|
|
|
vtkDataSet* nmVTKMeshLayer::getInputDataSet(bool bUpdatePipeline) const
|
|
|
{
|
|
|
if(m_pInputConnection == nullptr) {
|
|
|
return m_pDataSet;
|
|
|
}
|
|
|
|
|
|
vtkAlgorithm* pProducer = m_pInputConnection->GetProducer();
|
|
|
const int nOutputPort = m_pInputConnection->GetIndex();
|
|
|
if(pProducer == nullptr || nOutputPort < 0) {
|
|
|
return nullptr;
|
|
|
}
|
|
|
if(bUpdatePipeline) {
|
|
|
pProducer->Update(nOutputPort);
|
|
|
}
|
|
|
return vtkDataSet::SafeDownCast(
|
|
|
pProducer->GetOutputDataObject(nOutputPort));
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::validateInputDataSet(
|
|
|
vtkDataSet* pDataSet,
|
|
|
double dScalarRange[2]) const
|
|
|
{
|
|
|
if(pDataSet == nullptr || dScalarRange == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
if(m_aScalarArrayName.isEmpty()) {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
return validateScalarArray(pDataSet, m_aScalarArrayName,
|
|
|
m_eScalarAssociation,
|
|
|
dScalarRange) != nullptr;
|
|
|
}
|
|
|
|
|
|
vtkDataArray* nmVTKMeshLayer::validateScalarArray(
|
|
|
vtkDataSet* pDataSet,
|
|
|
const QByteArray& aArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
double dDataRange[2]) const
|
|
|
{
|
|
|
if(pDataSet == nullptr || aArrayName.isEmpty() ||
|
|
|
dDataRange == nullptr ||
|
|
|
(eAssociation != ScalarAssociation_CellData &&
|
|
|
eAssociation != ScalarAssociation_PointData)) {
|
|
|
return nullptr;
|
|
|
}
|
|
|
|
|
|
vtkDataArray* pArray = eAssociation == ScalarAssociation_CellData
|
|
|
? pDataSet->GetCellData()->GetArray(aArrayName.constData())
|
|
|
: pDataSet->GetPointData()->GetArray(aArrayName.constData());
|
|
|
if(pArray == nullptr) {
|
|
|
return nullptr;
|
|
|
}
|
|
|
pArray->GetRange(dDataRange);
|
|
|
return vtkMath::IsFinite(dDataRange[0]) &&
|
|
|
vtkMath::IsFinite(dDataRange[1]) &&
|
|
|
dDataRange[0] <= dDataRange[1]
|
|
|
? pArray : nullptr;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::validateLookupTable(
|
|
|
vtkLookupTable* pLookupTable,
|
|
|
double dRange[2]) const
|
|
|
{
|
|
|
if(pLookupTable == nullptr || dRange == nullptr ||
|
|
|
pLookupTable->GetNumberOfTableValues() <= 0) {
|
|
|
return false;
|
|
|
}
|
|
|
const double* pRange = pLookupTable->GetTableRange();
|
|
|
if(pRange == nullptr || !vtkMath::IsFinite(pRange[0]) ||
|
|
|
!vtkMath::IsFinite(pRange[1]) || pRange[0] >= pRange[1] ||
|
|
|
(pLookupTable->GetScale() == VTK_SCALE_LOG10 && pRange[0] <= 0.0)) {
|
|
|
return false;
|
|
|
}
|
|
|
dRange[0] = pRange[0];
|
|
|
dRange[1] = pRange[1];
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::applyExplicitScalarState(
|
|
|
const QByteArray& aArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
double dMinimum,
|
|
|
double dMaximum)
|
|
|
{
|
|
|
m_aScalarArrayName = aArrayName;
|
|
|
m_eScalarAssociation = eAssociation;
|
|
|
if(eAssociation == ScalarAssociation_CellData) {
|
|
|
m_pMapper->SetScalarModeToUseCellFieldData();
|
|
|
} else {
|
|
|
m_pMapper->SetScalarModeToUsePointFieldData();
|
|
|
}
|
|
|
m_pMapper->SelectColorArray(m_aScalarArrayName.constData());
|
|
|
m_pMapper->SetScalarRange(dMinimum, dMaximum);
|
|
|
m_pLookupTable->SetRange(dMinimum, dMaximum);
|
|
|
m_pLookupTable->Build();
|
|
|
m_bUsingExplicitScalarRange = true;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::applyLookupTableScalarState(
|
|
|
const QByteArray& aArrayName,
|
|
|
ScalarAssociation eAssociation,
|
|
|
vtkLookupTable* pLookupTable,
|
|
|
const double dRange[2])
|
|
|
{
|
|
|
m_aScalarArrayName = aArrayName;
|
|
|
m_eScalarAssociation = eAssociation;
|
|
|
if(eAssociation == ScalarAssociation_CellData) {
|
|
|
m_pMapper->SetScalarModeToUseCellFieldData();
|
|
|
} else {
|
|
|
m_pMapper->SetScalarModeToUsePointFieldData();
|
|
|
}
|
|
|
m_pMapper->SelectColorArray(m_aScalarArrayName.constData());
|
|
|
m_pLookupTable = pLookupTable;
|
|
|
m_pMapper->SetLookupTable(m_pLookupTable);
|
|
|
// VTK 7.1 的数组重载要求可写 double*,这里使用双参数重载保留 const 契约。
|
|
|
m_pMapper->SetScalarRange(dRange[0], dRange[1]);
|
|
|
m_bUsingExplicitScalarRange = true;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::applyRetainedScalarState(
|
|
|
const double dScalarRange[2])
|
|
|
{
|
|
|
if(m_aScalarArrayName.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
if(m_eScalarAssociation == ScalarAssociation_CellData) {
|
|
|
m_pMapper->SetScalarModeToUseCellFieldData();
|
|
|
} else {
|
|
|
m_pMapper->SetScalarModeToUsePointFieldData();
|
|
|
}
|
|
|
m_pMapper->SelectColorArray(m_aScalarArrayName.constData());
|
|
|
if(!m_bUsingExplicitScalarRange && dScalarRange != nullptr) {
|
|
|
m_pMapper->SetScalarRange(
|
|
|
dScalarRange[0], dScalarRange[1]);
|
|
|
m_pLookupTable->SetRange(
|
|
|
dScalarRange[0], dScalarRange[1]);
|
|
|
m_pLookupTable->Build();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
vtkDataArray* nmVTKMeshLayer::getSelectedScalarArray() const
|
|
|
{
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
if(pDataSet == nullptr || m_aScalarArrayName.isEmpty()) {
|
|
|
return nullptr;
|
|
|
}
|
|
|
return m_eScalarAssociation == ScalarAssociation_CellData
|
|
|
? pDataSet->GetCellData()->GetArray(
|
|
|
m_aScalarArrayName.constData())
|
|
|
: pDataSet->GetPointData()->GetArray(
|
|
|
m_aScalarArrayName.constData());
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::applyDataScalarRange()
|
|
|
{
|
|
|
vtkDataArray* pArray = getSelectedScalarArray();
|
|
|
if(pArray == nullptr || m_pMapper == nullptr || m_pLookupTable == nullptr) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// GetRange 只扫描当前选中的数组;无效或非有限范围不会污染现有颜色表。
|
|
|
double dRange[2] = { 0.0, 0.0 };
|
|
|
pArray->GetRange(dRange);
|
|
|
if(!vtkMath::IsFinite(dRange[0]) || !vtkMath::IsFinite(dRange[1]) ||
|
|
|
dRange[0] > dRange[1]) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
m_pMapper->SetScalarRange(dRange);
|
|
|
m_pLookupTable->SetRange(dRange);
|
|
|
m_pLookupTable->Build();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::clearInvalidScalarState()
|
|
|
{
|
|
|
// 失效选择不能继续驱动几何着色,也不能让外接色标沿用上一数据集范围。
|
|
|
m_aScalarArrayName.clear();
|
|
|
m_eScalarAssociation = ScalarAssociation_CellData;
|
|
|
m_bUsingExplicitScalarRange = false;
|
|
|
m_pMapper->ScalarVisibilityOff();
|
|
|
m_pMapper->SetScalarModeToDefault();
|
|
|
m_pMapper->SetScalarRange(0.0, 1.0);
|
|
|
m_pLookupTable->SetRange(0.0, 1.0);
|
|
|
m_pLookupTable->Build();
|
|
|
}
|