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/nmRender/nmVTKRotationCenterMarkerLa...

531 lines
17 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmVTKRotationCenterMarkerLayer.h"
#include <QCoreApplication>
#include <QThread>
#include <vtkActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkCellData.h>
#include <vtkCommand.h>
#include <vtkMath.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkUnsignedCharArray.h>
#include <vtkWeakPointer.h>
#include <cmath>
#include <limits>
namespace {
const double ROTATION_CENTER_HALF_LENGTH_PIXELS = 18.0;
const double ROTATION_CENTER_DEPTH_OFFSET_PIXELS = 1.5;
const double ROTATION_CENTER_HOMOGENEOUS_EPSILON =
std::numeric_limits<double>::epsilon();
bool isRotationCenterMarkerGuiThread()
{
QCoreApplication* pApplication = QCoreApplication::instance();
return pApplication == nullptr ||
pApplication->thread() == QThread::currentThread();
}
bool isFinitePoint3(const double dPoint[3])
{
return dPoint != nullptr &&
vtkMath::IsFinite(dPoint[0]) &&
vtkMath::IsFinite(dPoint[1]) &&
vtkMath::IsFinite(dPoint[2]);
}
bool worldToDisplay(vtkRenderer* pRenderer,
const double dWorld[3],
double dDisplay[3])
{
if(pRenderer == nullptr || dWorld == nullptr || dDisplay == nullptr) {
return false;
}
pRenderer->SetWorldPoint(dWorld[0], dWorld[1], dWorld[2], 1.0);
pRenderer->WorldToDisplay();
const double* pDisplay = pRenderer->GetDisplayPoint();
if(!isFinitePoint3(pDisplay)) {
return false;
}
dDisplay[0] = pDisplay[0];
dDisplay[1] = pDisplay[1];
dDisplay[2] = pDisplay[2];
return true;
}
bool displayToWorld(vtkRenderer* pRenderer,
const double dDisplay[3],
double dWorld[3])
{
if(pRenderer == nullptr || dDisplay == nullptr || dWorld == nullptr ||
!isFinitePoint3(dDisplay)) {
return false;
}
pRenderer->SetDisplayPoint(
dDisplay[0], dDisplay[1], dDisplay[2]);
pRenderer->DisplayToWorld();
const double* pHomogeneousWorld = pRenderer->GetWorldPoint();
if(pHomogeneousWorld == nullptr ||
!vtkMath::IsFinite(pHomogeneousWorld[0]) ||
!vtkMath::IsFinite(pHomogeneousWorld[1]) ||
!vtkMath::IsFinite(pHomogeneousWorld[2]) ||
!vtkMath::IsFinite(pHomogeneousWorld[3]) ||
std::fabs(pHomogeneousWorld[3]) <=
ROTATION_CENTER_HOMOGENEOUS_EPSILON) {
return false;
}
const double dInverseW = 1.0 / pHomogeneousWorld[3];
dWorld[0] = pHomogeneousWorld[0] * dInverseW;
dWorld[1] = pHomogeneousWorld[1] * dInverseW;
dWorld[2] = pHomogeneousWorld[2] * dInverseW;
return isFinitePoint3(dWorld);
}
}
class nmVTKRotationCenterMarkerLayerPrivate
{
public:
nmVTKRotationCenterMarkerLayerPrivate()
: m_pPoints(vtkSmartPointer<vtkPoints>::New()),
m_pLines(vtkSmartPointer<vtkCellArray>::New()),
m_pColors(vtkSmartPointer<vtkUnsignedCharArray>::New()),
m_pPolyData(vtkSmartPointer<vtkPolyData>::New()),
m_pMapper(vtkSmartPointer<vtkPolyDataMapper>::New()),
m_pActor(vtkSmartPointer<vtkActor>::New()),
m_pRendererCallback(vtkSmartPointer<vtkCallbackCommand>::New()),
m_pCameraCallback(vtkSmartPointer<vtkCallbackCommand>::New()),
m_pRenderer(nullptr),
m_pCamera(nullptr),
m_nRendererObserverTag(0),
m_nCameraObserverTag(0),
m_bHasWorldCenter(false)
{
// 必须通过 vtkActor::New() 的对象工厂路径获得实际 OpenGL Actor。
initializeGeometry();
m_dWorldCenter[0] = 0.0;
m_dWorldCenter[1] = 0.0;
m_dWorldCenter[2] = 0.0;
m_pRendererCallback->SetClientData(this);
m_pRendererCallback->SetCallback(
nmVTKRotationCenterMarkerLayerPrivate::rendererStarted);
m_pCameraCallback->SetClientData(this);
m_pCameraCallback->SetCallback(
nmVTKRotationCenterMarkerLayerPrivate::cameraModified);
}
~nmVTKRotationCenterMarkerLayerPrivate()
{
// 先移除 Renderer 和 Camera Observer再阻断裸回调目标。
unbindRenderer();
m_pRendererCallback->SetClientData(nullptr);
m_pRendererCallback->SetCallback(nullptr);
m_pCameraCallback->SetClientData(nullptr);
m_pCameraCallback->SetCallback(nullptr);
}
vtkActor* getActor() const
{
return m_pActor;
}
bool bindRenderer(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr) {
return false;
}
if(m_pRenderer.GetPointer() != pRenderer) {
unbindRenderer();
m_pRenderer = pRenderer;
}
if(!ensureRendererObserver(pRenderer) ||
!ensureCameraObserver(pRenderer->GetActiveCamera())) {
unbindRenderer();
return false;
}
return true;
}
void unbindRenderer()
{
removeCameraObserver();
removeRendererObserver();
m_pRenderer = nullptr;
}
bool synchronize()
{
return synchronizeForRenderer(m_pRenderer.GetPointer());
}
bool synchronizeForRenderer(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr || m_pRenderer.GetPointer() != pRenderer ||
!m_bHasWorldCenter) {
return false;
}
vtkCamera* pCamera = pRenderer->GetActiveCamera();
if(pCamera == nullptr || !ensureCameraObserver(pCamera)) {
return false;
}
int* pViewportSize = pRenderer->GetSize();
if(pViewportSize == nullptr || pViewportSize[0] <= 0 ||
pViewportSize[1] <= 0) {
return false;
}
const double dLogicalCenter[3] = {
m_dWorldCenter[0], m_dWorldCenter[1], m_dWorldCenter[2]
};
double dCameraPosition[3];
pCamera->GetPosition(dCameraPosition);
if(!isFinitePoint3(dLogicalCenter) ||
!isFinitePoint3(dCameraPosition)) {
return false;
}
double dCenterDisplay[3];
if(!worldToDisplay(pRenderer, dLogicalCenter, dCenterDisplay)) {
return false;
}
double dOffsetDisplay[3] = {
dCenterDisplay[0],
dCenterDisplay[1] + ROTATION_CENTER_HALF_LENGTH_PIXELS,
dCenterDisplay[2]
};
double dCenterAtDepth[3];
double dOffsetAtDepth[3];
if(!displayToWorld(pRenderer, dCenterDisplay, dCenterAtDepth) ||
!displayToWorld(pRenderer, dOffsetDisplay, dOffsetAtDepth)) {
return false;
}
const double dScaleX = dOffsetAtDepth[0] - dCenterAtDepth[0];
const double dScaleY = dOffsetAtDepth[1] - dCenterAtDepth[1];
const double dScaleZ = dOffsetAtDepth[2] - dCenterAtDepth[2];
const double dHalfLength = std::sqrt(
dScaleX * dScaleX +
dScaleY * dScaleY +
dScaleZ * dScaleZ);
if(!vtkMath::IsFinite(dHalfLength) || dHalfLength <= 0.0) {
return false;
}
double dTowardCamera[3];
if(pCamera->GetParallelProjection() != 0) {
const double* pDirectionOfProjection =
pCamera->GetDirectionOfProjection();
if(!isFinitePoint3(pDirectionOfProjection)) {
return false;
}
dTowardCamera[0] = -pDirectionOfProjection[0];
dTowardCamera[1] = -pDirectionOfProjection[1];
dTowardCamera[2] = -pDirectionOfProjection[2];
} else {
dTowardCamera[0] = dCameraPosition[0] - dLogicalCenter[0];
dTowardCamera[1] = dCameraPosition[1] - dLogicalCenter[1];
dTowardCamera[2] = dCameraPosition[2] - dLogicalCenter[2];
}
if(vtkMath::Normalize(dTowardCamera) <= 0.0) {
return false;
}
const double dWorldPerPixel =
dHalfLength / ROTATION_CENTER_HALF_LENGTH_PIXELS;
const double dDepthOffset =
dWorldPerPixel * ROTATION_CENTER_DEPTH_OFFSET_PIXELS;
const double dDisplayCenter[3] = {
dLogicalCenter[0] + dTowardCamera[0] * dDepthOffset,
dLogicalCenter[1] + dTowardCamera[1] * dDepthOffset,
dLogicalCenter[2] + dTowardCamera[2] * dDepthOffset
};
const double dAxisPoints[6][3] = {
{ dDisplayCenter[0] - dHalfLength,
dDisplayCenter[1], dDisplayCenter[2] },
{ dDisplayCenter[0] + dHalfLength,
dDisplayCenter[1], dDisplayCenter[2] },
{ dDisplayCenter[0],
dDisplayCenter[1] - dHalfLength, dDisplayCenter[2] },
{ dDisplayCenter[0],
dDisplayCenter[1] + dHalfLength, dDisplayCenter[2] },
{ dDisplayCenter[0], dDisplayCenter[1],
dDisplayCenter[2] - dHalfLength },
{ dDisplayCenter[0], dDisplayCenter[1],
dDisplayCenter[2] + dHalfLength }
};
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
if(!isFinitePoint3(dAxisPoints[nPoint])) {
return false;
}
}
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
m_pPoints->SetPoint(nPoint, dAxisPoints[nPoint]);
}
m_pPoints->Modified();
m_pPolyData->Modified();
return true;
}
bool setWorldCenter(double dX, double dY, double dZ)
{
const double dCenter[3] = { dX, dY, dZ };
if(!isFinitePoint3(dCenter)) {
return false;
}
m_dWorldCenter[0] = dX;
m_dWorldCenter[1] = dY;
m_dWorldCenter[2] = dZ;
m_bHasWorldCenter = true;
synchronize();
return true;
}
bool hasWorldCenter() const
{
return m_bHasWorldCenter;
}
private:
static void rendererStarted(vtkObject* pCaller,
unsigned long,
void* pClientData,
void*)
{
nmVTKRotationCenterMarkerLayerPrivate* pThis =
static_cast<nmVTKRotationCenterMarkerLayerPrivate*>(
pClientData);
if(pThis != nullptr) {
// StartEvent 位于 Prop 遍历之前,可使用最终视口尺寸覆盖 Resize 路径。
pThis->synchronizeForRenderer(
vtkRenderer::SafeDownCast(pCaller));
}
}
static void cameraModified(vtkObject*,
unsigned long,
void* pClientData,
void*)
{
nmVTKRotationCenterMarkerLayerPrivate* pThis =
static_cast<nmVTKRotationCenterMarkerLayerPrivate*>(
pClientData);
if(pThis != nullptr) {
// 相机回调只同步几何,最终 Render 仍由交互器或容器统一提交。
pThis->synchronize();
}
}
void initializeGeometry()
{
m_pPoints->SetDataTypeToDouble();
m_pPoints->SetNumberOfPoints(6);
for(vtkIdType nPoint = 0; nPoint < 6; ++nPoint) {
m_pPoints->SetPoint(nPoint, 0.0, 0.0, 0.0);
}
for(vtkIdType nAxis = 0; nAxis < 3; ++nAxis) {
vtkIdType nPointIds[2] = {
nAxis * 2, nAxis * 2 + 1
};
m_pLines->InsertNextCell(2, nPointIds);
}
m_pColors->SetName("RotationCenterAxisColors");
m_pColors->SetNumberOfComponents(3);
m_pColors->InsertNextTuple3(255.0, 0.0, 0.0);
m_pColors->InsertNextTuple3(254.0, 255.0, 0.0);
m_pColors->InsertNextTuple3(0.0, 255.0, 0.0);
m_pPolyData->SetPoints(m_pPoints);
m_pPolyData->SetLines(m_pLines);
m_pPolyData->GetCellData()->SetScalars(m_pColors);
m_pMapper->SetInputData(m_pPolyData);
m_pMapper->ScalarVisibilityOn();
m_pMapper->SetScalarModeToUseCellData();
m_pMapper->SetColorModeToDirectScalars();
m_pActor->SetMapper(m_pMapper);
m_pActor->GetProperty()->SetLineWidth(2.0f);
m_pActor->GetProperty()->SetOpacity(1.0);
m_pActor->GetProperty()->LightingOff();
m_pActor->PickableOff();
m_pActor->UseBoundsOff();
}
bool ensureRendererObserver(vtkRenderer* pRenderer)
{
if(pRenderer == nullptr || m_pRendererCallback == nullptr) {
return false;
}
if(m_pRenderer.GetPointer() == pRenderer &&
m_nRendererObserverTag != 0) {
return true;
}
removeRendererObserver();
m_nRendererObserverTag = pRenderer->AddObserver(
vtkCommand::StartEvent, m_pRendererCallback);
return m_nRendererObserverTag != 0;
}
void removeRendererObserver()
{
vtkRenderer* pRenderer = m_pRenderer.GetPointer();
if(pRenderer != nullptr && m_nRendererObserverTag != 0) {
pRenderer->RemoveObserver(m_nRendererObserverTag);
}
m_nRendererObserverTag = 0;
}
bool ensureCameraObserver(vtkCamera* pCamera)
{
if(pCamera == nullptr || m_pCameraCallback == nullptr) {
return false;
}
if(m_pCamera.GetPointer() == pCamera &&
m_nCameraObserverTag != 0) {
return true;
}
removeCameraObserver();
m_pCamera = pCamera;
m_nCameraObserverTag = pCamera->AddObserver(
vtkCommand::ModifiedEvent, m_pCameraCallback);
if(m_nCameraObserverTag == 0) {
m_pCamera = nullptr;
return false;
}
return true;
}
void removeCameraObserver()
{
vtkCamera* pCamera = m_pCamera.GetPointer();
if(pCamera != nullptr && m_nCameraObserverTag != 0) {
pCamera->RemoveObserver(m_nCameraObserverTag);
}
m_nCameraObserverTag = 0;
m_pCamera = nullptr;
}
vtkSmartPointer<vtkPoints> m_pPoints;
vtkSmartPointer<vtkCellArray> m_pLines;
vtkSmartPointer<vtkUnsignedCharArray> m_pColors;
vtkSmartPointer<vtkPolyData> m_pPolyData;
vtkSmartPointer<vtkPolyDataMapper> m_pMapper;
vtkSmartPointer<vtkActor> m_pActor;
vtkSmartPointer<vtkCallbackCommand> m_pRendererCallback;
vtkSmartPointer<vtkCallbackCommand> m_pCameraCallback;
vtkWeakPointer<vtkRenderer> m_pRenderer; ///< 非所有权引用。
vtkWeakPointer<vtkCamera> m_pCamera; ///< 非所有权引用。
unsigned long m_nRendererObserverTag; ///< 为零表示尚未注册观察者。
unsigned long m_nCameraObserverTag; ///< 为零表示尚未注册观察者。
double m_dWorldCenter[3]; ///< 独立于相机焦点的逻辑中心。
bool m_bHasWorldCenter;
};
nmVTKRotationCenterMarkerLayer::nmVTKRotationCenterMarkerLayer(
const QString& sLayerId)
: nmVTKRenderLayer(sLayerId),
m_pPrivate(new nmVTKRotationCenterMarkerLayerPrivate)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
setRenderProp(m_pPrivate->getActor());
setVisible(false);
}
nmVTKRotationCenterMarkerLayer::~nmVTKRotationCenterMarkerLayer()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(m_pPrivate != nullptr) {
// Renderer/Camera Observer 必须在 Renderer 和回调目标释放前解除。
m_pPrivate->unbindRenderer();
detach();
delete m_pPrivate;
m_pPrivate = nullptr;
}
}
bool nmVTKRotationCenterMarkerLayer::bindRenderer(vtkRenderer* pRenderer)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(!isRotationCenterMarkerGuiThread() || m_pPrivate == nullptr ||
pRenderer == nullptr || getAttachedRenderer() != pRenderer) {
return false;
}
return m_pPrivate->bindRenderer(pRenderer);
}
void nmVTKRotationCenterMarkerLayer::unbindRenderer()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr) {
m_pPrivate->unbindRenderer();
}
}
bool nmVTKRotationCenterMarkerLayer::synchronize()
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
return isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr &&
m_pPrivate->synchronize();
}
bool nmVTKRotationCenterMarkerLayer::setWorldCenter(
double dX,
double dY,
double dZ)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
return isRotationCenterMarkerGuiThread() && m_pPrivate != nullptr &&
m_pPrivate->setWorldCenter(dX, dY, dZ);
}
bool nmVTKRotationCenterMarkerLayer::hasWorldCenter() const
{
return m_pPrivate != nullptr && m_pPrivate->hasWorldCenter();
}
void nmVTKRotationCenterMarkerLayer::setMarkerVisible(bool bVisible)
{
Q_ASSERT(isRotationCenterMarkerGuiThread());
if(!isRotationCenterMarkerGuiThread()) {
return;
}
const bool bCanShow = bVisible && m_pPrivate != nullptr &&
m_pPrivate->hasWorldCenter();
setVisible(bCanShow);
if(bCanShow) {
// 首次显示先准备几何;视口尚未建立时由绘制前同步再次补齐。
m_pPrivate->synchronize();
}
}
bool nmVTKRotationCenterMarkerLayer::isMarkerVisible() const
{
return isVisible();
}