|
|
|
|
@ -5,8 +5,11 @@
|
|
|
|
|
#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>
|
|
|
|
|
@ -29,6 +32,7 @@ bool isMeshLayerGuiThread()
|
|
|
|
|
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()),
|
|
|
|
|
@ -56,7 +60,9 @@ nmVTKMeshLayer::~nmVTKMeshLayer()
|
|
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
|
|
// 派生成员释放前先解除 Renderer,基类析构中的 detach 仍保持幂等。
|
|
|
|
|
detach();
|
|
|
|
|
clearInput();
|
|
|
|
|
m_pDataSet = nullptr;
|
|
|
|
|
m_pInputConnection = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmVTKMeshLayer::setDataSet(vtkDataSet* pDataSet)
|
|
|
|
|
@ -65,41 +71,105 @@ bool nmVTKMeshLayer::setDataSet(vtkDataSet* pDataSet)
|
|
|
|
|
if(!isMeshLayerGuiThread()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(pDataSet == nullptr) {
|
|
|
|
|
clearInput();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// vtkSmartPointer 仅增加引用计数,不复制点、单元或属性数组。
|
|
|
|
|
m_pDataSet = pDataSet;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(pDataSet == nullptr) {
|
|
|
|
|
clearInvalidScalarState();
|
|
|
|
|
bool nmVTKMeshLayer::setInputConnection(
|
|
|
|
|
vtkAlgorithmOutput* pInputConnection)
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
|
|
if(!isMeshLayerGuiThread() || pInputConnection == nullptr ||
|
|
|
|
|
pInputConnection->GetProducer() == nullptr ||
|
|
|
|
|
pInputConnection->GetIndex() < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更换数据集时保留调用方此前的数组选择;若同名数组存在则恢复其映射方式。
|
|
|
|
|
if(!m_aScalarArrayName.isEmpty()) {
|
|
|
|
|
if(getSelectedScalarArray() == nullptr) {
|
|
|
|
|
clearInvalidScalarState();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(m_eScalarAssociation == ScalarAssociation_CellData) {
|
|
|
|
|
m_pMapper->SetScalarModeToUseCellFieldData();
|
|
|
|
|
} else {
|
|
|
|
|
m_pMapper->SetScalarModeToUsePointFieldData();
|
|
|
|
|
}
|
|
|
|
|
m_pMapper->SelectColorArray(m_aScalarArrayName.constData());
|
|
|
|
|
if(!m_bUsingExplicitScalarRange) {
|
|
|
|
|
if(!applyDataScalarRange()) {
|
|
|
|
|
clearInvalidScalarState();
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 m_pDataSet;
|
|
|
|
|
return getInputDataSet(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmVTKMeshLayer::setDisplayMode(DisplayMode eMode)
|
|
|
|
|
@ -170,32 +240,20 @@ bool nmVTKMeshLayer::setScalarArray(
|
|
|
|
|
ScalarAssociation eAssociation)
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
|
|
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
|
|
|
|
|
m_pDataSet == nullptr || m_pMapper == nullptr ||
|
|
|
|
|
(eAssociation != ScalarAssociation_CellData &&
|
|
|
|
|
eAssociation != ScalarAssociation_PointData)) {
|
|
|
|
|
pDataSet == nullptr || m_pMapper == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QByteArray aArrayName = sArrayName.toUtf8();
|
|
|
|
|
vtkDataArray* pArray = eAssociation == ScalarAssociation_CellData
|
|
|
|
|
? m_pDataSet->GetCellData()->GetArray(aArrayName.constData())
|
|
|
|
|
: m_pDataSet->GetPointData()->GetArray(aArrayName.constData());
|
|
|
|
|
if(pArray == nullptr) {
|
|
|
|
|
double dRange[2] = { 0.0, 0.0 };
|
|
|
|
|
if(validateScalarArray(pDataSet, aArrayName,
|
|
|
|
|
eAssociation, dRange) == nullptr) {
|
|
|
|
|
// 失败时保留原数组选择和原 Mapper 状态。
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double dRange[2] = { 0.0, 0.0 };
|
|
|
|
|
if(!m_bUsingExplicitScalarRange) {
|
|
|
|
|
pArray->GetRange(dRange);
|
|
|
|
|
if(!vtkMath::IsFinite(dRange[0]) ||
|
|
|
|
|
!vtkMath::IsFinite(dRange[1]) || dRange[0] > dRange[1]) {
|
|
|
|
|
// 自动范围无效时不提交新数组选择,避免形成半更新状态。
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_aScalarArrayName = aArrayName;
|
|
|
|
|
m_eScalarAssociation = eAssociation;
|
|
|
|
|
if(eAssociation == ScalarAssociation_CellData) {
|
|
|
|
|
@ -214,6 +272,34 @@ bool nmVTKMeshLayer::setScalarArray(
|
|
|
|
|
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());
|
|
|
|
|
@ -273,15 +359,117 @@ 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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::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
|
|
|
|
|
{
|
|
|
|
|
if(m_pDataSet == nullptr || m_aScalarArrayName.isEmpty()) {
|
|
|
|
|
vtkDataSet* pDataSet = getInputDataSet(true);
|
|
|
|
|
if(pDataSet == nullptr || m_aScalarArrayName.isEmpty()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return m_eScalarAssociation == ScalarAssociation_CellData
|
|
|
|
|
? m_pDataSet->GetCellData()->GetArray(
|
|
|
|
|
? pDataSet->GetCellData()->GetArray(
|
|
|
|
|
m_aScalarArrayName.constData())
|
|
|
|
|
: m_pDataSet->GetPointData()->GetArray(
|
|
|
|
|
: pDataSet->GetPointData()->GetArray(
|
|
|
|
|
m_aScalarArrayName.constData());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|