|
|
#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 == NULL ||
|
|
|
pApplication->thread() == QThread::currentThread();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
nmVTKMeshLayer::nmVTKMeshLayer(const QString& sLayerId)
|
|
|
: nmVTKRenderLayer(sLayerId),
|
|
|
m_pDataSet(NULL),
|
|
|
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 = NULL;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setDataSet(vtkDataSet* pDataSet)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// vtkSmartPointer 仅增加引用计数,不复制点、单元或属性数组。
|
|
|
m_pDataSet = pDataSet;
|
|
|
m_pMapper->SetInputData(pDataSet);
|
|
|
|
|
|
if(pDataSet == NULL) {
|
|
|
clearInvalidScalarState();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 更换数据集时保留调用方此前的数组选择;若同名数组存在则恢复其映射方式。
|
|
|
if(!m_aScalarArrayName.isEmpty()) {
|
|
|
if(getSelectedScalarArray() == NULL) {
|
|
|
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 == NULL) {
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setOpacity(double dOpacity)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || !vtkMath::IsFinite(dOpacity) ||
|
|
|
m_pActor == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
m_pActor->GetProperty()->SetOpacity(qBound(0.0, dOpacity, 1.0));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
double nmVTKMeshLayer::getOpacity() const
|
|
|
{
|
|
|
return m_pActor != NULL ? m_pActor->GetProperty()->GetOpacity() : 0.0;
|
|
|
}
|
|
|
|
|
|
void nmVTKMeshLayer::setScalarVisibility(bool bVisible)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || m_pMapper == NULL) {
|
|
|
return;
|
|
|
}
|
|
|
m_pMapper->SetScalarVisibility(bVisible ? 1 : 0);
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::isScalarVisible() const
|
|
|
{
|
|
|
return m_pMapper != NULL && m_pMapper->GetScalarVisibility() != 0;
|
|
|
}
|
|
|
|
|
|
bool nmVTKMeshLayer::setScalarArray(
|
|
|
const QString& sArrayName,
|
|
|
ScalarAssociation eAssociation)
|
|
|
{
|
|
|
Q_ASSERT(isMeshLayerGuiThread());
|
|
|
if(!isMeshLayerGuiThread() || sArrayName.isEmpty() ||
|
|
|
m_pDataSet == NULL || m_pMapper == NULL ||
|
|
|
(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 == NULL) {
|
|
|
// 失败时保留原数组选择和原 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 == NULL || m_pLookupTable == NULL) {
|
|
|
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 == NULL || m_aScalarArrayName.isEmpty()) {
|
|
|
return NULL;
|
|
|
}
|
|
|
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 == NULL || m_pMapper == NULL || m_pLookupTable == NULL) {
|
|
|
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();
|
|
|
}
|