#include "nmVTKCameraController.h" #include #include #include #include #include namespace { // 相机和 Renderer 都是界面对象;Release 中也用返回值保护线程边界。 bool isCameraGuiThread() { QCoreApplication* pApplication = QCoreApplication::instance(); return pApplication == nullptr || pApplication->thread() == QThread::currentThread(); } } nmVTKCameraController::nmVTKCameraController(vtkRenderer* pRenderer) : m_pRenderer(pRenderer) { Q_ASSERT(isCameraGuiThread()); } nmVTKCameraController::~nmVTKCameraController() { Q_ASSERT(isCameraGuiThread()); // Renderer 为非所有权引用,析构只解除关系,不修改相机或场景。 m_pRenderer = nullptr; } void nmVTKCameraController::setRenderer(vtkRenderer* pRenderer) { Q_ASSERT(isCameraGuiThread()); if(isCameraGuiThread()) { m_pRenderer = pRenderer; } } vtkRenderer* nmVTKCameraController::getRenderer() const { return m_pRenderer; } bool nmVTKCameraController::resetCamera() { Q_ASSERT(isCameraGuiThread()); double dBounds[6]; if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) { return false; } vtkCamera* pCamera = m_pRenderer->GetActiveCamera(); if(pCamera == nullptr) { return false; } // 重置入口恢复默认三维透视投影,再按当前可见边界适配。 pCamera->ParallelProjectionOff(); m_pRenderer->ResetCamera(dBounds); m_pRenderer->ResetCameraClippingRange(dBounds); return true; } bool nmVTKCameraController::zoomIn() { return zoom(1.2); } bool nmVTKCameraController::zoomOut() { return zoom(1.0 / 1.2); } bool nmVTKCameraController::fitView() { Q_ASSERT(isCameraGuiThread()); double dBounds[6]; if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) { return false; } vtkCamera* pCamera = m_pRenderer->GetActiveCamera(); if(pCamera == nullptr) { return false; } // ResetCamera 只重新适配边界,不改写调用方选定的观察方向和投影方式。 m_pRenderer->ResetCamera(dBounds); m_pRenderer->ResetCameraClippingRange(dBounds); return true; } bool nmVTKCameraController::setTopView() { return applyStandardView(0.0, 0.0, 1.0, 0.0, 1.0, 0.0, true); } bool nmVTKCameraController::setFrontView() { return applyStandardView(0.0, -1.0, 0.0, 0.0, 0.0, 1.0, true); } bool nmVTKCameraController::setRightView() { return applyStandardView(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, true); } bool nmVTKCameraController::setIsometricView() { return applyStandardView(1.0, -1.0, 1.0, 0.0, 0.0, 1.0, false); } bool nmVTKCameraController::setRotationCenter( double dX, double dY, double dZ) { Q_ASSERT(isCameraGuiThread()); if(!isCameraGuiThread() || m_pRenderer == nullptr || !vtkMath::IsFinite(dX) || !vtkMath::IsFinite(dY) || !vtkMath::IsFinite(dZ)) { return false; } vtkCamera* pCamera = m_pRenderer->GetActiveCamera(); if(pCamera == nullptr) { return false; } double dOldFocalPoint[3]; double dOldPosition[3]; pCamera->GetFocalPoint(dOldFocalPoint); pCamera->GetPosition(dOldPosition); pCamera->SetFocalPoint(dX, dY, dZ); // 同步平移相机位置,保持原观察方向和相机到焦点的距离。 pCamera->SetPosition( dOldPosition[0] + dX - dOldFocalPoint[0], dOldPosition[1] + dY - dOldFocalPoint[1], dOldPosition[2] + dZ - dOldFocalPoint[2]); m_pRenderer->ResetCameraClippingRange(); return true; } bool nmVTKCameraController::resetRotationCenter() { Q_ASSERT(isCameraGuiThread()); double dBounds[6]; if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) { return false; } // 复用平移逻辑,保持当前观察方向、投影方式和观察距离不变。 return setRotationCenter((dBounds[0] + dBounds[1]) * 0.5, (dBounds[2] + dBounds[3]) * 0.5, (dBounds[4] + dBounds[5]) * 0.5); } bool nmVTKCameraController::getVisibleBounds(double dBounds[6]) const { if(m_pRenderer == nullptr || dBounds == nullptr) { return false; } // 只使用可见 Prop;隐藏图层不会影响标准视角和旋转中心。 m_pRenderer->ComputeVisiblePropBounds(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]; } bool nmVTKCameraController::applyStandardView( double dDirectionX, double dDirectionY, double dDirectionZ, double dViewUpX, double dViewUpY, double dViewUpZ, bool bParallelProjection) { Q_ASSERT(isCameraGuiThread()); double dBounds[6]; if(!isCameraGuiThread() || !getVisibleBounds(dBounds)) { return false; } vtkCamera* pCamera = m_pRenderer->GetActiveCamera(); if(pCamera == nullptr) { return false; } double dDirection[3] = { dDirectionX, dDirectionY, dDirectionZ }; if(vtkMath::Normalize(dDirection) == 0.0) { return false; } const double dCenter[3] = { (dBounds[0] + dBounds[1]) * 0.5, (dBounds[2] + dBounds[3]) * 0.5, (dBounds[4] + dBounds[5]) * 0.5 }; // 先给相机一个与最大跨度成比例的稳定距离,再由 ResetCamera 精确适配。 const double dExtent = qMax( qMax(dBounds[1] - dBounds[0], dBounds[3] - dBounds[2]), dBounds[5] - dBounds[4]); const double dDistance = dExtent > 0.0 ? dExtent * 2.0 : 1.0; // 顶、前、右视图分别位于 +Z、-Y、+X 一侧观察包围盒中心。 // 等轴视图使用透视投影,三个标准正视图使用正交投影。 pCamera->SetFocalPoint(dCenter); pCamera->SetPosition(dCenter[0] + dDirection[0] * dDistance, dCenter[1] + dDirection[1] * dDistance, dCenter[2] + dDirection[2] * dDistance); pCamera->SetViewUp(dViewUpX, dViewUpY, dViewUpZ); pCamera->SetParallelProjection(bParallelProjection ? 1 : 0); pCamera->OrthogonalizeViewUp(); m_pRenderer->ResetCamera(dBounds); m_pRenderer->ResetCameraClippingRange(dBounds); return true; } bool nmVTKCameraController::zoom(double dFactor) { Q_ASSERT(isCameraGuiThread()); double dBounds[6]; if(!isCameraGuiThread() || !vtkMath::IsFinite(dFactor) || dFactor <= 0.0 || !getVisibleBounds(dBounds)) { return false; } vtkCamera* pCamera = m_pRenderer->GetActiveCamera(); if(pCamera == nullptr) { return false; } // 正交投影必须调整 ParallelScale;Dolly 只对透视投影产生可见缩放。 if(pCamera->GetParallelProjection() != 0) { const double dParallelScale = pCamera->GetParallelScale(); if(!vtkMath::IsFinite(dParallelScale) || dParallelScale <= 0.0) { return false; } pCamera->SetParallelScale(dParallelScale / dFactor); } else { pCamera->Dolly(dFactor); } m_pRenderer->ResetCameraClippingRange(dBounds); return true; }