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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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