|
|
#include "nmPebiGridSceneController.h"
|
|
|
|
|
|
#include "nmPebiGridViewData.h"
|
|
|
|
|
|
#include "nmVTKBoundsGridLayer.h"
|
|
|
#include "nmVTKMeshLayer.h"
|
|
|
#include "nmVTKScalarBarLayer.h"
|
|
|
#include "nmVTKScene.h"
|
|
|
#include "nmWxVTKRenderContainerWidget.h"
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
#include <vtkActor.h>
|
|
|
#include <vtkCamera.h>
|
|
|
#include <vtkMath.h>
|
|
|
#include <vtkProperty.h>
|
|
|
#include <vtkRenderer.h>
|
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
QString controllerText(const char* pText)
|
|
|
{
|
|
|
return QCoreApplication::translate(
|
|
|
"nmPebiGridSceneController", pText);
|
|
|
}
|
|
|
|
|
|
bool getFiniteGridBounds(vtkUnstructuredGrid* pGrid, double dBounds[6])
|
|
|
{
|
|
|
if(pGrid == NULL || dBounds == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
pGrid->GetBounds(dBounds);
|
|
|
if(!vtkMath::AreBoundsInitialized(dBounds)) {
|
|
|
return false;
|
|
|
}
|
|
|
for(int nIndex = 0; nIndex < 6; ++nIndex) {
|
|
|
if(!vtkMath::IsFinite(dBounds[nIndex])) {
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
return dBounds[0] <= dBounds[1] &&
|
|
|
dBounds[2] <= dBounds[3] &&
|
|
|
dBounds[4] <= dBounds[5];
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
class nmPebiGridSceneControllerPrivate
|
|
|
{
|
|
|
public:
|
|
|
explicit nmPebiGridSceneControllerPrivate(
|
|
|
nmWxVTKRenderContainerWidget* pRenderContainer)
|
|
|
: m_pContainer(pRenderContainer),
|
|
|
m_pMeshLayer(NULL),
|
|
|
m_pScalarBarLayer(NULL),
|
|
|
m_pBoundsGridLayer(NULL),
|
|
|
m_bGridLinesVisible(true),
|
|
|
m_bScalarBarVisible(true),
|
|
|
m_bBoundsGridVisible(false),
|
|
|
m_bPropertySelectionExplicit(false),
|
|
|
m_bHasData(false)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
bool ensureBaseLayers()
|
|
|
{
|
|
|
nmVTKScene* pScene = m_pContainer != NULL
|
|
|
? m_pContainer->getScene() : NULL;
|
|
|
vtkRenderer* pRenderer = m_pContainer != NULL
|
|
|
? m_pContainer->getRenderer() : NULL;
|
|
|
if(pScene == NULL || pRenderer == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(m_pMeshLayer == NULL) {
|
|
|
nmVTKMeshLayer* pLayer =
|
|
|
new nmVTKMeshLayer("pebi.grid.mesh");
|
|
|
pLayer->setVisible(false);
|
|
|
if(!pScene->addLayer(pLayer)) {
|
|
|
delete pLayer;
|
|
|
return false;
|
|
|
}
|
|
|
m_pMeshLayer = pLayer;
|
|
|
// 固定面色、边线颜色和完全不透明外观沿用旧 PEBI 预览。
|
|
|
m_pMeshLayer->getActor()->GetProperty()->SetColor(
|
|
|
0.0, 120.0 / 255.0, 215.0 / 255.0);
|
|
|
m_pMeshLayer->getActor()->GetProperty()->SetEdgeColor(
|
|
|
0.0, 0.0, 0.0);
|
|
|
}
|
|
|
|
|
|
if(m_pBoundsGridLayer == NULL) {
|
|
|
nmVTKBoundsGridLayer* pLayer =
|
|
|
new nmVTKBoundsGridLayer("pebi.grid.bounds_grid");
|
|
|
pLayer->setMode(nmVTKBoundsGridLayer::Mode_PlanarXY);
|
|
|
pLayer->setCamera(pRenderer->GetActiveCamera());
|
|
|
pLayer->setVisible(false);
|
|
|
if(!pScene->addLayer(pLayer)) {
|
|
|
delete pLayer;
|
|
|
return false;
|
|
|
}
|
|
|
m_pBoundsGridLayer = pLayer;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool ensureScalarBarLayer()
|
|
|
{
|
|
|
if(m_pScalarBarLayer != NULL) {
|
|
|
return true;
|
|
|
}
|
|
|
nmVTKScene* pScene = m_pContainer != NULL
|
|
|
? m_pContainer->getScene() : NULL;
|
|
|
if(pScene == NULL || m_pMeshLayer == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 色标只在第一次选择有效标量时创建,并共享 MeshLayer 的 LUT。
|
|
|
nmVTKScalarBarLayer* pLayer =
|
|
|
new nmVTKScalarBarLayer("pebi.grid.scalar_bar");
|
|
|
pLayer->setLookupTable(m_pMeshLayer->getLookupTable());
|
|
|
pLayer->setPosition(0.86, 0.12);
|
|
|
pLayer->setSize(0.10, 0.76);
|
|
|
pLayer->setVisible(false);
|
|
|
if(!pScene->addLayer(pLayer)) {
|
|
|
delete pLayer;
|
|
|
return false;
|
|
|
}
|
|
|
m_pScalarBarLayer = pLayer;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool applyProperty(const QString& sPropertyName)
|
|
|
{
|
|
|
if(m_pMeshLayer == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if(sPropertyName.isEmpty()) {
|
|
|
m_sPropertyName.clear();
|
|
|
m_pMeshLayer->setScalarVisibility(false);
|
|
|
if(m_pScalarBarLayer != NULL) {
|
|
|
m_pScalarBarLayer->setVisible(false);
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
if(sPropertyName != "Material" || !m_oViewData.hasMaterial() ||
|
|
|
!m_pMeshLayer->setScalarArray(
|
|
|
sPropertyName, nmVTKMeshLayer::ScalarAssociation_CellData)) {
|
|
|
// 无效属性不能沿用上一网格的标量着色或色标范围。
|
|
|
m_sPropertyName.clear();
|
|
|
m_pMeshLayer->setScalarVisibility(false);
|
|
|
if(m_pScalarBarLayer != NULL) {
|
|
|
m_pScalarBarLayer->setVisible(false);
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
if(!ensureScalarBarLayer()) {
|
|
|
m_sPropertyName.clear();
|
|
|
m_pMeshLayer->setScalarVisibility(false);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
m_sPropertyName = sPropertyName;
|
|
|
m_pMeshLayer->setScalarVisibility(true);
|
|
|
m_pScalarBarLayer->setLookupTable(
|
|
|
m_pMeshLayer->getLookupTable());
|
|
|
m_pScalarBarLayer->setTitle(sPropertyName);
|
|
|
m_pScalarBarLayer->setVisible(m_bScalarBarVisible);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool bindViewData(const nmPebiGridViewData& oViewData,
|
|
|
const QString& sRequestedProperty,
|
|
|
const double dBounds[6])
|
|
|
{
|
|
|
if(m_pMeshLayer == NULL || m_pBoundsGridLayer == NULL ||
|
|
|
dBounds == NULL) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 本函数只提交已在外层验证过的数据,不渲染也不改变相机。
|
|
|
m_oViewData = oViewData;
|
|
|
if(!m_pMeshLayer->setDataSet(oViewData.getGrid())) {
|
|
|
m_oViewData.clear();
|
|
|
return false;
|
|
|
}
|
|
|
m_pMeshLayer->setEdgeVisibility(m_bGridLinesVisible);
|
|
|
m_pMeshLayer->setVisible(true);
|
|
|
|
|
|
if(!m_pBoundsGridLayer->setBounds(
|
|
|
dBounds[0], dBounds[1], dBounds[2],
|
|
|
dBounds[3], dBounds[4], dBounds[5])) {
|
|
|
m_pMeshLayer->setDataSet(NULL);
|
|
|
m_pMeshLayer->setVisible(false);
|
|
|
m_oViewData.clear();
|
|
|
return false;
|
|
|
}
|
|
|
m_pBoundsGridLayer->setVisible(m_bBoundsGridVisible);
|
|
|
m_bHasData = true;
|
|
|
|
|
|
// 属性不可用只回退固定面色,不影响已经验证成功的网格几何提交。
|
|
|
applyProperty(sRequestedProperty);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void releaseGridData()
|
|
|
{
|
|
|
// 先断开 Mapper 对旧网格的引用,再释放 ViewData 的共享引用。
|
|
|
if(m_pMeshLayer != NULL) {
|
|
|
m_pMeshLayer->setScalarVisibility(false);
|
|
|
m_pMeshLayer->setDataSet(NULL);
|
|
|
m_pMeshLayer->setVisible(false);
|
|
|
}
|
|
|
if(m_pScalarBarLayer != NULL) {
|
|
|
m_pScalarBarLayer->setVisible(false);
|
|
|
}
|
|
|
if(m_pBoundsGridLayer != NULL) {
|
|
|
m_pBoundsGridLayer->setVisible(false);
|
|
|
}
|
|
|
m_oViewData.clear();
|
|
|
m_sPropertyName.clear();
|
|
|
m_bHasData = false;
|
|
|
}
|
|
|
|
|
|
nmWxVTKRenderContainerWidget* m_pContainer; ///< 非所有权指针。
|
|
|
nmVTKMeshLayer* m_pMeshLayer; ///< Scene 所有,控制器只观察。
|
|
|
nmVTKScalarBarLayer* m_pScalarBarLayer; ///< Scene 所有,按需创建。
|
|
|
nmVTKBoundsGridLayer* m_pBoundsGridLayer; ///< Scene 所有。
|
|
|
nmPebiGridViewData m_oViewData;
|
|
|
bool m_bGridLinesVisible;
|
|
|
bool m_bScalarBarVisible; ///< 用户偏好,无属性时仍保留。
|
|
|
bool m_bBoundsGridVisible;
|
|
|
bool m_bPropertySelectionExplicit; ///< 是否已由业务界面明确选择属性。
|
|
|
bool m_bHasData;
|
|
|
QString m_sRequestedPropertyName; ///< 显式选择偏好,可暂时不可用。
|
|
|
QString m_sPropertyName;
|
|
|
};
|
|
|
|
|
|
nmPebiGridSceneController::nmPebiGridSceneController(
|
|
|
nmWxVTKRenderContainerWidget* pContainer)
|
|
|
: m_pPrivate(new nmPebiGridSceneControllerPrivate(pContainer))
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmPebiGridSceneController::~nmPebiGridSceneController()
|
|
|
{
|
|
|
if(m_pPrivate != NULL) {
|
|
|
// Layer 已归 Scene 所有;这里只解除数据和非所有权指针。
|
|
|
m_pPrivate->releaseGridData();
|
|
|
m_pPrivate->m_pContainer = NULL;
|
|
|
m_pPrivate->m_pMeshLayer = NULL;
|
|
|
m_pPrivate->m_pScalarBarLayer = NULL;
|
|
|
m_pPrivate->m_pBoundsGridLayer = NULL;
|
|
|
delete m_pPrivate;
|
|
|
m_pPrivate = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::setViewData(
|
|
|
const nmPebiGridViewData& oViewData,
|
|
|
QString* pError)
|
|
|
{
|
|
|
if(pError != NULL) {
|
|
|
pError->clear();
|
|
|
}
|
|
|
if(m_pPrivate == NULL || m_pPrivate->m_pContainer == NULL ||
|
|
|
!oViewData.isValid()) {
|
|
|
if(pError != NULL) {
|
|
|
*pError = controllerText("The grid preview data is invalid.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
double dBounds[6];
|
|
|
if(!getFiniteGridBounds(oViewData.getGrid(), dBounds)) {
|
|
|
if(pError != NULL) {
|
|
|
*pError = controllerText("The grid bounds are invalid.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
if(!m_pPrivate->ensureBaseLayers()) {
|
|
|
if(pError != NULL) {
|
|
|
*pError = controllerText("Unable to create the grid render layers.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
QString sRequestedProperty;
|
|
|
if(m_pPrivate->m_bPropertySelectionExplicit) {
|
|
|
// 用户明确选择优先;即使属性暂时缺失,也保留偏好供后续网格恢复。
|
|
|
sRequestedProperty = m_pPrivate->m_sRequestedPropertyName;
|
|
|
} else if(oViewData.hasMaterial()) {
|
|
|
// 默认选择与“是否首次装入网格”无关,首次出现 Material 时同样生效。
|
|
|
sRequestedProperty = "Material";
|
|
|
}
|
|
|
|
|
|
// 强引用保留上一份成功数据,提交任一步失败时可以完整恢复旧画面。
|
|
|
const bool bHadPreviousData = m_pPrivate->m_bHasData;
|
|
|
const nmPebiGridViewData oPreviousViewData = m_pPrivate->m_oViewData;
|
|
|
const QString sPreviousProperty = m_pPrivate->m_sPropertyName;
|
|
|
double dPreviousBounds[6];
|
|
|
const bool bHasPreviousBounds = bHadPreviousData &&
|
|
|
getFiniteGridBounds(oPreviousViewData.getGrid(), dPreviousBounds);
|
|
|
|
|
|
// 几何替换先解除旧输入,再共享新网格;Renderer 和图层对象保持不变。
|
|
|
m_pPrivate->releaseGridData();
|
|
|
if(!m_pPrivate->bindViewData(oViewData, sRequestedProperty, dBounds)) {
|
|
|
// 回滚同样不渲染;外层失败状态会决定恢复旧画面还是显示错误页。
|
|
|
m_pPrivate->releaseGridData();
|
|
|
if(bHadPreviousData && bHasPreviousBounds) {
|
|
|
m_pPrivate->bindViewData(
|
|
|
oPreviousViewData,
|
|
|
sPreviousProperty,
|
|
|
dPreviousBounds);
|
|
|
}
|
|
|
if(pError != NULL) {
|
|
|
*pError = controllerText("Unable to bind the grid render data.");
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 几何变化始终恢复正交顶视图并适配;整批更新最终只在就绪入口渲染一次。
|
|
|
m_pPrivate->m_pContainer->fitTopViewWithoutRender();
|
|
|
m_pPrivate->m_pContainer->showReadyState();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmPebiGridSceneController::clear()
|
|
|
{
|
|
|
if(m_pPrivate == NULL) {
|
|
|
return;
|
|
|
}
|
|
|
m_pPrivate->releaseGridData();
|
|
|
m_pPrivate->m_sPropertyName.clear();
|
|
|
m_pPrivate->m_sRequestedPropertyName.clear();
|
|
|
m_pPrivate->m_bGridLinesVisible = true;
|
|
|
m_pPrivate->m_bScalarBarVisible = true;
|
|
|
m_pPrivate->m_bBoundsGridVisible = false;
|
|
|
m_pPrivate->m_bPropertySelectionExplicit = false;
|
|
|
}
|
|
|
|
|
|
void nmPebiGridSceneController::setGridLinesVisible(bool bVisible)
|
|
|
{
|
|
|
if(m_pPrivate == NULL) {
|
|
|
return;
|
|
|
}
|
|
|
m_pPrivate->m_bGridLinesVisible = bVisible;
|
|
|
if(m_pPrivate->m_pMeshLayer != NULL) {
|
|
|
m_pPrivate->m_pMeshLayer->setEdgeVisibility(bVisible);
|
|
|
}
|
|
|
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != NULL) {
|
|
|
m_pPrivate->m_pContainer->renderView();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::setProperty(const QString& sPropertyName)
|
|
|
{
|
|
|
if(m_pPrivate == NULL || !m_pPrivate->m_bHasData) {
|
|
|
return false;
|
|
|
}
|
|
|
const bool bSucceeded = m_pPrivate->applyProperty(sPropertyName);
|
|
|
// 只有公开入口代表用户/业务明确选择,内部默认绑定不会污染该状态。
|
|
|
m_pPrivate->m_bPropertySelectionExplicit = true;
|
|
|
m_pPrivate->m_sRequestedPropertyName = bSucceeded
|
|
|
? sPropertyName : QString();
|
|
|
if(m_pPrivate->m_pContainer != NULL) {
|
|
|
m_pPrivate->m_pContainer->renderView();
|
|
|
}
|
|
|
return bSucceeded;
|
|
|
}
|
|
|
|
|
|
void nmPebiGridSceneController::setScalarBarVisible(bool bVisible)
|
|
|
{
|
|
|
if(m_pPrivate == NULL) {
|
|
|
return;
|
|
|
}
|
|
|
// 无属性时只保存偏好;重新选择 Material 后按该偏好恢复色标。
|
|
|
m_pPrivate->m_bScalarBarVisible = bVisible;
|
|
|
if(m_pPrivate->m_pScalarBarLayer != NULL) {
|
|
|
m_pPrivate->m_pScalarBarLayer->setVisible(
|
|
|
bVisible && m_pPrivate->m_bHasData &&
|
|
|
!m_pPrivate->m_sPropertyName.isEmpty());
|
|
|
}
|
|
|
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != NULL) {
|
|
|
m_pPrivate->m_pContainer->renderView();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmPebiGridSceneController::setBoundsGridVisible(bool bVisible)
|
|
|
{
|
|
|
if(m_pPrivate == NULL) {
|
|
|
return;
|
|
|
}
|
|
|
m_pPrivate->m_bBoundsGridVisible = bVisible;
|
|
|
if(m_pPrivate->m_pBoundsGridLayer != NULL) {
|
|
|
m_pPrivate->m_pBoundsGridLayer->setVisible(
|
|
|
bVisible && m_pPrivate->m_bHasData);
|
|
|
}
|
|
|
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != NULL) {
|
|
|
m_pPrivate->m_pContainer->renderView();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::hasViewData() const
|
|
|
{
|
|
|
return m_pPrivate != NULL && m_pPrivate->m_bHasData;
|
|
|
}
|
|
|
|
|
|
QString nmPebiGridSceneController::getProperty() const
|
|
|
{
|
|
|
return m_pPrivate != NULL
|
|
|
? m_pPrivate->m_sPropertyName : QString();
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::isGridLinesVisible() const
|
|
|
{
|
|
|
return m_pPrivate != NULL && m_pPrivate->m_bGridLinesVisible;
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::isScalarBarVisible() const
|
|
|
{
|
|
|
return m_pPrivate != NULL && m_pPrivate->m_bScalarBarVisible;
|
|
|
}
|
|
|
|
|
|
bool nmPebiGridSceneController::isBoundsGridVisible() const
|
|
|
{
|
|
|
return m_pPrivate != NULL && m_pPrivate->m_bBoundsGridVisible;
|
|
|
}
|