|
|
|
|
|
#include "nmVTKCameraController.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
|
#include <QThread>
|
|
|
|
|
|
|
|
|
|
|
|
#include <vtkCamera.h>
|
|
|
|
|
|
#include <vtkMath.h>
|
|
|
|
|
|
#include <vtkRenderer.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
// 相机和 Renderer 都是界面对象;Release 中也用返回值保护线程边界。
|
|
|
|
|
|
bool isCameraGuiThread()
|
|
|
|
|
|
{
|
|
|
|
|
|
QCoreApplication* pApplication = QCoreApplication::instance();
|
|
|
|
|
|
return pApplication == NULL ||
|
|
|
|
|
|
pApplication->thread() == QThread::currentThread();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKCameraController::nmVTKCameraController(vtkRenderer* pRenderer)
|
|
|
|
|
|
: m_pRenderer(pRenderer)
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isCameraGuiThread());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmVTKCameraController::~nmVTKCameraController()
|
|
|
|
|
|
{
|
|
|
|
|
|
Q_ASSERT(isCameraGuiThread());
|
|
|
|
|
|
// Renderer 为非所有权引用,析构只解除关系,不修改相机或场景。
|
|
|
|
|
|
m_pRenderer = NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 == NULL) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 重置入口恢复默认三维透视投影,再按当前可见边界适配。
|
|
|
|
|
|
pCamera->ParallelProjectionOff();
|
|
|
|
|
|
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 == NULL ||
|
|
|
|
|
|
!vtkMath::IsFinite(dX) || !vtkMath::IsFinite(dY) ||
|
|
|
|
|
|
!vtkMath::IsFinite(dZ)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
vtkCamera* pCamera = m_pRenderer->GetActiveCamera();
|
|
|
|
|
|
if(pCamera == NULL) {
|
|
|
|
|
|
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 == NULL || dBounds == NULL) {
|
|
|
|
|
|
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 == NULL) {
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|