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/nmRender/nmVTKMeshLayer.cpp

629 lines
20 KiB
C++

#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();
}