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/nmSubWxs/nmPebiGridSceneController.cpp

431 lines
14 KiB
C++

#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 == nullptr || dBounds == nullptr) {
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(nullptr),
m_pScalarBarLayer(nullptr),
m_pBoundsGridLayer(nullptr),
m_bGridLinesVisible(true),
m_bScalarBarVisible(true),
m_bBoundsGridVisible(false),
m_bPropertySelectionExplicit(false),
m_bHasData(false)
{
}
bool ensureBaseLayers()
{
nmVTKScene* pScene = m_pContainer != nullptr
? m_pContainer->getScene() : nullptr;
vtkRenderer* pRenderer = m_pContainer != nullptr
? m_pContainer->getRenderer() : nullptr;
if(pScene == nullptr || pRenderer == nullptr) {
return false;
}
if(m_pMeshLayer == nullptr) {
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 == nullptr) {
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 != nullptr) {
return true;
}
nmVTKScene* pScene = m_pContainer != nullptr
? m_pContainer->getScene() : nullptr;
if(pScene == nullptr || m_pMeshLayer == nullptr) {
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 == nullptr) {
return false;
}
if(sPropertyName.isEmpty()) {
m_sPropertyName.clear();
m_pMeshLayer->setScalarVisibility(false);
if(m_pScalarBarLayer != nullptr) {
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 != nullptr) {
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 == nullptr || m_pBoundsGridLayer == nullptr ||
dBounds == nullptr) {
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(nullptr);
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 != nullptr) {
m_pMeshLayer->setScalarVisibility(false);
m_pMeshLayer->setDataSet(nullptr);
m_pMeshLayer->setVisible(false);
}
if(m_pScalarBarLayer != nullptr) {
m_pScalarBarLayer->setVisible(false);
}
if(m_pBoundsGridLayer != nullptr) {
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 != nullptr) {
// Layer 已归 Scene 所有;这里只解除数据和非所有权指针。
m_pPrivate->releaseGridData();
m_pPrivate->m_pContainer = nullptr;
m_pPrivate->m_pMeshLayer = nullptr;
m_pPrivate->m_pScalarBarLayer = nullptr;
m_pPrivate->m_pBoundsGridLayer = nullptr;
delete m_pPrivate;
m_pPrivate = nullptr;
}
}
bool nmPebiGridSceneController::setViewData(
const nmPebiGridViewData& oViewData,
QString* pError)
{
if(pError != nullptr) {
pError->clear();
}
if(m_pPrivate == nullptr || m_pPrivate->m_pContainer == nullptr ||
!oViewData.isValid()) {
if(pError != nullptr) {
*pError = controllerText("The grid preview data is invalid.");
}
return false;
}
double dBounds[6];
if(!getFiniteGridBounds(oViewData.getGrid(), dBounds)) {
if(pError != nullptr) {
*pError = controllerText("The grid bounds are invalid.");
}
return false;
}
if(!m_pPrivate->ensureBaseLayers()) {
if(pError != nullptr) {
*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 != nullptr) {
*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 == nullptr) {
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 == nullptr) {
return;
}
m_pPrivate->m_bGridLinesVisible = bVisible;
if(m_pPrivate->m_pMeshLayer != nullptr) {
m_pPrivate->m_pMeshLayer->setEdgeVisibility(bVisible);
}
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) {
m_pPrivate->m_pContainer->renderView();
}
}
bool nmPebiGridSceneController::setProperty(const QString& sPropertyName)
{
if(m_pPrivate == nullptr || !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 != nullptr) {
m_pPrivate->m_pContainer->renderView();
}
return bSucceeded;
}
void nmPebiGridSceneController::setScalarBarVisible(bool bVisible)
{
if(m_pPrivate == nullptr) {
return;
}
// 无属性时只保存偏好;重新选择 Material 后按该偏好恢复色标。
m_pPrivate->m_bScalarBarVisible = bVisible;
if(m_pPrivate->m_pScalarBarLayer != nullptr) {
m_pPrivate->m_pScalarBarLayer->setVisible(
bVisible && m_pPrivate->m_bHasData &&
!m_pPrivate->m_sPropertyName.isEmpty());
}
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) {
m_pPrivate->m_pContainer->renderView();
}
}
void nmPebiGridSceneController::setBoundsGridVisible(bool bVisible)
{
if(m_pPrivate == nullptr) {
return;
}
m_pPrivate->m_bBoundsGridVisible = bVisible;
if(m_pPrivate->m_pBoundsGridLayer != nullptr) {
m_pPrivate->m_pBoundsGridLayer->setVisible(
bVisible && m_pPrivate->m_bHasData);
}
if(m_pPrivate->m_bHasData && m_pPrivate->m_pContainer != nullptr) {
m_pPrivate->m_pContainer->renderView();
}
}
bool nmPebiGridSceneController::hasViewData() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bHasData;
}
QString nmPebiGridSceneController::getProperty() const
{
return m_pPrivate != nullptr
? m_pPrivate->m_sPropertyName : QString();
}
bool nmPebiGridSceneController::isGridLinesVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bGridLinesVisible;
}
bool nmPebiGridSceneController::isScalarBarVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bScalarBarVisible;
}
bool nmPebiGridSceneController::isBoundsGridVisible() const
{
return m_pPrivate != nullptr && m_pPrivate->m_bBoundsGridVisible;
}