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

321 lines
9.4 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 <vtkCellData.h>
#include <vtkDataArray.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_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();
m_pDataSet = nullptr;
}
bool nmVTKMeshLayer::setDataSet(vtkDataSet* pDataSet)
{
Q_ASSERT(isMeshLayerGuiThread());
if(!isMeshLayerGuiThread()) {
return false;
}
// vtkSmartPointer 仅增加引用计数,不复制点、单元或属性数组。
m_pDataSet = pDataSet;
m_pMapper->SetInputData(pDataSet);
if(pDataSet == nullptr) {
clearInvalidScalarState();
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;
}
}
}
return true;
}
vtkDataSet* nmVTKMeshLayer::getDataSet() const
{
return m_pDataSet;
}
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());
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
m_pDataSet == nullptr || m_pMapper == nullptr ||
(eAssociation != ScalarAssociation_CellData &&
eAssociation != ScalarAssociation_PointData)) {
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) {
// 失败时保留原数组选择和原 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) {
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;
}
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;
}
vtkDataSetMapper* nmVTKMeshLayer::getMapper() const
{
return m_pMapper;
}
vtkActor* nmVTKMeshLayer::getActor() const
{
return m_pActor;
}
vtkLookupTable* nmVTKMeshLayer::getLookupTable() const
{
return m_pLookupTable;
}
vtkDataArray* nmVTKMeshLayer::getSelectedScalarArray() const
{
if(m_pDataSet == nullptr || m_aScalarArrayName.isEmpty()) {
return nullptr;
}
return m_eScalarAssociation == ScalarAssociation_CellData
? m_pDataSet->GetCellData()->GetArray(
m_aScalarArrayName.constData())
: m_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();
}