From e6e0bccffb6a5cab6c2df44c1bbfeb8e0de4db82 Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Tue, 17 Mar 2026 15:29:07 +0000 Subject: [PATCH] split viewport for Qt and VtkViewer --- src/Vtk/CMakeLists.txt | 2 + src/Vtk/uLibVtkViewer.cpp | 83 ++------------ src/Vtk/uLibVtkViewer.h | 67 +++-------- src/Vtk/vtkQViewport.cpp | 94 +--------------- src/Vtk/vtkQViewport.h | 28 +---- src/Vtk/vtkViewport.cpp | 228 ++++++++++++++++++++++++++++++++++++++ src/Vtk/vtkViewport.h | 76 +++++++++++++ 7 files changed, 345 insertions(+), 233 deletions(-) create mode 100644 src/Vtk/vtkViewport.cpp create mode 100644 src/Vtk/vtkViewport.h diff --git a/src/Vtk/CMakeLists.txt b/src/Vtk/CMakeLists.txt index 2254b96..1a2121e 100644 --- a/src/Vtk/CMakeLists.txt +++ b/src/Vtk/CMakeLists.txt @@ -3,6 +3,7 @@ set(HEADERS uLibVtkInterface.h vtkContainerBox.h vtkHandlerWidget.h vtkQViewport.h + vtkViewport.h vtkPolydata.h ) @@ -11,6 +12,7 @@ set(SOURCES uLibVtkInterface.cxx vtkContainerBox.cpp vtkHandlerWidget.cpp vtkQViewport.cpp + vtkViewport.cpp vtkPolydata.cpp ) diff --git a/src/Vtk/uLibVtkViewer.cpp b/src/Vtk/uLibVtkViewer.cpp index e25318d..510d051 100644 --- a/src/Vtk/uLibVtkViewer.cpp +++ b/src/Vtk/uLibVtkViewer.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "uLibVtkViewer.h" @@ -56,8 +57,6 @@ public: }; vtkStandardNewMacro(vtkInteractorStyleNoSpin); - - namespace uLib { namespace Vtk { @@ -65,24 +64,12 @@ namespace Vtk { ///// VTK VIEWER ////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Viewer::Viewer() - : m_RenderWindow(vtkRenderWindow::New()), m_Renderer(vtkRenderer::New()), - m_Annotation(vtkCornerAnnotation::New()), - m_Marker(vtkOrientationMarkerWidget::New()), m_CameraWidget(nullptr) { -#if VTK_MAJOR_VERSION >= 9 || \ - (VTK_MAJOR_VERSION == 8 && VTK_MINOR_VERSION >= 90) - m_CameraWidget = vtkCameraOrientationWidget::New(); -#endif + : Viewport(), m_RenderWindow(vtkRenderWindow::New()) { InstallPipe(); } Viewer::~Viewer() { UninstallPipe(); - - m_Annotation->Delete(); - m_Marker->Delete(); - if (m_CameraWidget) - m_CameraWidget->Delete(); - m_Renderer->Delete(); m_RenderWindow->Delete(); } @@ -93,34 +80,14 @@ void Viewer::InstallPipe() { vtkSmartPointer::New(); renderWindowInteractor->SetRenderWindow(m_RenderWindow); - // Use a custom style with no spin/inertia + // Common setup + Viewport::SetupPipeline(renderWindowInteractor); + + // BUT we want to override the style with our custom NoSpin version vtkSmartPointer style = vtkSmartPointer::New(); renderWindowInteractor->SetInteractorStyle(style); - // annotation // - m_Annotation->GetTextProperty()->SetColor(1, 1, 1); - m_Annotation->GetTextProperty()->SetFontFamilyToArial(); - m_Annotation->GetTextProperty()->SetOpacity(0.5); - m_Annotation->SetMaximumFontSize(10); - m_Annotation->SetText(0, "uLib VTK Viewer - OpenCMT all right reserved."); - m_Renderer->AddViewProp(m_Annotation); - - // orientation marker // - vtkSmartPointer axes = vtkSmartPointer::New(); - m_Marker->SetInteractor(renderWindowInteractor); - m_Marker->SetOrientationMarker(axes); - m_Marker->SetViewport(0.0, 0.0, 0.2, 0.2); - m_Marker->SetEnabled(true); - m_Marker->InteractiveOff(); - - // camera orientation widget // - if (m_CameraWidget) { - m_CameraWidget->SetParentRenderer(m_Renderer); - m_CameraWidget->SetInteractor(renderWindowInteractor); - m_CameraWidget->On(); - } - // Must be rendered here in Vtk-6.0 or seg-fault // m_RenderWindow->Render(); } @@ -130,25 +97,9 @@ void Viewer::UninstallPipe() { m_Renderer->Clear(); } -void Viewer::addProp(vtkProp *prop) { - m_Renderer->AddActor(prop); - m_Renderer->Render(); -} - -void Viewer::RemoveProp(vtkProp *prop) { - m_Renderer->RemoveViewProp(prop); - m_Renderer->Render(); -} - -void Viewer::AddPuppet(Puppet &prop) { - prop.ConnectRenderer(m_Renderer); - prop.ConnectInteractor(this->GetInteractor()); - m_Renderer->Render(); -} - -void Viewer::RemovePuppet(Puppet &prop) { - prop.DisconnectRenderer(m_Renderer); - m_Renderer->Render(); +void Viewer::Render() { + if (m_RenderWindow) + m_RenderWindow->Render(); } vtkSmartPointer @@ -164,27 +115,11 @@ Viewer::MakeCameraOrientationWidget(vtkRenderWindowInteractor *interactor, void Viewer::Start() { m_RenderWindow->GetInteractor()->Start(); } -vtkCornerAnnotation *Viewer::GetAnnotation() { return m_Annotation; } - -vtkRenderer *Viewer::GetRenderer() { return m_Renderer; } - vtkRenderWindow *Viewer::GetRenderWindow() { return m_RenderWindow; } vtkRenderWindowInteractor *Viewer::GetInteractor() { return m_RenderWindow->GetInteractor(); } -vtkCameraOrientationWidget *Viewer::GetCameraWidget() { return m_CameraWidget; } - -void Viewer::Reset() { - this->ZoomAuto(); - m_Renderer->Render(); -} - -void Viewer::ZoomAuto() { - m_Renderer->ResetCameraClippingRange(); - m_Renderer->ResetCamera(); -} - } // namespace Vtk } // namespace uLib diff --git a/src/Vtk/uLibVtkViewer.h b/src/Vtk/uLibVtkViewer.h index ff304ec..ad4ba58 100644 --- a/src/Vtk/uLibVtkViewer.h +++ b/src/Vtk/uLibVtkViewer.h @@ -26,80 +26,49 @@ #ifndef ULIBVTKVIEWER_H #define ULIBVTKVIEWER_H -#include "uLibVtkInterface.h" #include -#include -#include -#include -#include -#include - -class vtkProp; -class vtk3DWidget; -class vtkCornerAnnotation; -class vtkRenderWindowInteractor; -class vtkRenderer; -class vtkCameraOrientationWidget; +#include "vtkViewport.h" namespace uLib { namespace Vtk { -template class Tie { -public: - void DoAction() { - std::cout << "Tie::DoAction -> generic Tie does nothing\n"; - } -}; +// template class Tie { +// public: +// void DoAction() { +// std::cout << "Tie::DoAction -> generic Tie does nothing\n"; +// } +// }; -class Viewer { +class Viewer : public Viewport { public: Viewer(); - ~Viewer(); + virtual ~Viewer(); - void AddPuppet(Puppet &prop); - - void RemovePuppet(Puppet &prop); - - void AddWidget(vtk3DWidget *widget); + // Render scene + virtual void Render() override; static vtkSmartPointer MakeCameraOrientationWidget(vtkRenderWindowInteractor *interactor, - vtkRenderer *renderer); - - void Reset(); - void ZoomAuto(); + vtkRenderer *renderer); void Start(); - vtkCornerAnnotation *GetAnnotation(); - - vtkRenderer *GetRenderer(); - vtkRenderWindow *GetRenderWindow(); - vtkRenderWindowInteractor *GetInteractor(); - - vtkCameraOrientationWidget *GetCameraWidget(); - - void addProp(vtkProp *prop); - - void RemoveProp(vtkProp *prop); + virtual vtkRenderWindow *GetRenderWindow() override; + virtual vtkRenderWindowInteractor *GetInteractor() override; private: void InstallPipe(); void UninstallPipe(); - vtkRenderer *m_Renderer; vtkRenderWindow *m_RenderWindow; - vtkCornerAnnotation *m_Annotation; - vtkOrientationMarkerWidget *m_Marker; - vtkCameraOrientationWidget *m_CameraWidget; }; -template <> class Tie { -public: - void DoAction() { std::cout << " VIEWER TIE !!! \n"; } -}; +// template <> class Tie { +// public: +// void DoAction() { std::cout << " VIEWER TIE !!! \n"; } +// }; } // namespace Vtk } // namespace uLib diff --git a/src/Vtk/vtkQViewport.cpp b/src/Vtk/vtkQViewport.cpp index 0e31bde..567b7cb 100644 --- a/src/Vtk/vtkQViewport.cpp +++ b/src/Vtk/vtkQViewport.cpp @@ -15,11 +15,8 @@ namespace Vtk { QViewport::QViewport(QWidget* parent) : QWidget(parent) + , Viewport() , m_VtkWidget(nullptr) - , m_Renderer(vtkRenderer::New()) - , m_Annotation(vtkCornerAnnotation::New()) - , m_Marker(vtkOrientationMarkerWidget::New()) - , m_CameraWidget(nullptr) { // Build the layout – zero margins so VTK fills the entire widget auto* layout = new QVBoxLayout(this); @@ -36,14 +33,6 @@ QViewport::QViewport(QWidget* parent) QViewport::~QViewport() { - m_Renderer->RemoveAllViewProps(); - m_Renderer->Clear(); - - m_Annotation->Delete(); - m_Marker->Delete(); - if (m_CameraWidget) - m_CameraWidget->Delete(); - m_Renderer->Delete(); } void QViewport::SetupPipeline() @@ -52,88 +41,17 @@ void QViewport::SetupPipeline() vtkRenderWindow* rw = m_VtkWidget->renderWindow(); rw->AddRenderer(m_Renderer); - // Qt will supply its own interactor – grab it - vtkRenderWindowInteractor* iren = rw->GetInteractor(); - - // Trackball-camera interaction style - vtkNew style; - iren->SetInteractorStyle(style); - - // Corner annotation - m_Annotation->GetTextProperty()->SetColor(1, 1, 1); - m_Annotation->GetTextProperty()->SetFontFamilyToArial(); - m_Annotation->GetTextProperty()->SetOpacity(0.5); - m_Annotation->SetMaximumFontSize(10); - m_Annotation->SetText(0, "uLib VTK Viewer - OpenCMT all right reserved."); - m_Renderer->AddViewProp(m_Annotation); - - // Orientation axes marker (bottom-left corner) - vtkNew axes; - m_Marker->SetInteractor(iren); - m_Marker->SetOrientationMarker(axes); - m_Marker->SetViewport(0.0, 0.0, 0.2, 0.2); - m_Marker->SetEnabled(true); - m_Marker->InteractiveOff(); - - // Camera-orientation widget (VTK ≥ 9) -#if VTK_MAJOR_VERSION >= 9 - m_CameraWidget = vtkCameraOrientationWidget::New(); - m_CameraWidget->SetParentRenderer(m_Renderer); - m_CameraWidget->SetInteractor(iren); - m_CameraWidget->On(); -#endif - - m_Renderer->SetBackground(0.15, 0.15, 0.15); - m_Renderer->ResetCamera(); + // Common setup + Viewport::SetupPipeline(rw->GetInteractor()); } // ── Public API ──────────────────────────────────────────────────────────────── void QViewport::Render() { - m_VtkWidget->renderWindow()->Render(); -} - -void QViewport::Reset() -{ - ZoomAuto(); - Render(); -} - -void QViewport::ZoomAuto() -{ - m_Renderer->ResetCameraClippingRange(); - m_Renderer->ResetCamera(); -} - -void QViewport::AddPuppet(Puppet& prop) -{ - prop.ConnectRenderer(m_Renderer); - prop.ConnectInteractor(GetInteractor()); - Render(); -} - -void QViewport::RemovePuppet(Puppet& prop) -{ - prop.DisconnectRenderer(m_Renderer); - Render(); -} - -void QViewport::addProp(vtkProp* prop) -{ - m_Renderer->AddActor(prop); - Render(); -} - -void QViewport::RemoveProp(vtkProp* prop) -{ - m_Renderer->RemoveViewProp(prop); - Render(); -} - -void QViewport::AddWidget(vtk3DWidget* /*widget*/) -{ - // vtk3DWidget integration can be added here if needed + UpdateGrid(); + if (m_VtkWidget && m_VtkWidget->renderWindow()) + m_VtkWidget->renderWindow()->Render(); } vtkRenderWindow* QViewport::GetRenderWindow() diff --git a/src/Vtk/vtkQViewport.h b/src/Vtk/vtkQViewport.h index 62666e1..83469b7 100644 --- a/src/Vtk/vtkQViewport.h +++ b/src/Vtk/vtkQViewport.h @@ -12,6 +12,8 @@ #include "uLibVtkInterface.h" +#include "vtkViewport.h" + class vtkProp; class vtk3DWidget; class vtkRenderWindowInteractor; @@ -25,42 +27,24 @@ namespace Vtk { * directly into the Qt application (no separate VTK window). * Puppets and actors are added to the embedded renderer. */ -class QViewport : public QWidget { +class QViewport : public QWidget, public Viewport { Q_OBJECT public: explicit QViewport(QWidget* parent = nullptr); virtual ~QViewport(); // Render scene - void Render(); - void Reset(); - void ZoomAuto(); - - // Puppet / prop management - void AddPuppet(Puppet &prop); - void RemovePuppet(Puppet &prop); - void addProp(vtkProp *prop); - void RemoveProp(vtkProp *prop); - - // Widget integration - void AddWidget(vtk3DWidget *widget); + virtual void Render() override; // Direct access to VTK internals - vtkRenderer* GetRenderer() { return m_Renderer; } - vtkRenderWindow* GetRenderWindow(); - vtkRenderWindowInteractor* GetInteractor(); - vtkCornerAnnotation* GetAnnotation() { return m_Annotation; } - vtkCameraOrientationWidget* GetCameraWidget(){ return m_CameraWidget; } + virtual vtkRenderWindow* GetRenderWindow() override; + virtual vtkRenderWindowInteractor* GetInteractor() override; QVTKOpenGLNativeWidget* GetWidget() { return m_VtkWidget; } private: void SetupPipeline(); QVTKOpenGLNativeWidget* m_VtkWidget; - vtkRenderer* m_Renderer; - vtkCornerAnnotation* m_Annotation; - vtkOrientationMarkerWidget* m_Marker; - vtkCameraOrientationWidget* m_CameraWidget; }; } // namespace Vtk diff --git a/src/Vtk/vtkViewport.cpp b/src/Vtk/vtkViewport.cpp new file mode 100644 index 0000000..8dc587d --- /dev/null +++ b/src/Vtk/vtkViewport.cpp @@ -0,0 +1,228 @@ +#include "vtkViewport.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace uLib { +namespace Vtk { + +Viewport::Viewport() + : m_Renderer(vtkRenderer::New()) + , m_Annotation(vtkCornerAnnotation::New()) + , m_Marker(vtkOrientationMarkerWidget::New()) + , m_CameraWidget(nullptr) + , m_Colors(vtkNamedColors::New()) +{ +} + +Viewport::~Viewport() +{ + if (m_Renderer) { + m_Renderer->RemoveAllViewProps(); + m_Renderer->Clear(); + } + + if (m_Annotation) m_Annotation->Delete(); + if (m_Marker) m_Marker->Delete(); + if (m_CameraWidget) m_CameraWidget->Delete(); + if (m_Renderer) m_Renderer->Delete(); + if (m_Colors) m_Colors->Delete(); +} + +void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren) +{ + if (!iren) return; + + // Trackball-camera interaction style + vtkNew style; + iren->SetInteractorStyle(style); + + // Corner annotation + m_Annotation->GetTextProperty()->SetColor(1, 1, 1); + m_Annotation->GetTextProperty()->SetFontFamilyToArial(); + m_Annotation->GetTextProperty()->SetOpacity(0.5); + m_Annotation->SetMaximumFontSize(10); + m_Annotation->SetText(0, "uLib VTK Viewer - OpenCMT all right reserved."); + m_Renderer->AddViewProp(m_Annotation); + + // Orientation axes marker (bottom-left corner) + vtkNew axes; + m_Marker->SetInteractor(iren); + m_Marker->SetOrientationMarker(axes); + m_Marker->SetViewport(0.0, 0.0, 0.2, 0.2); + m_Marker->SetEnabled(true); + m_Marker->InteractiveOff(); + + // Grid Plane centered at (0,0,0) + m_GridSource = vtkSmartPointer::New(); + m_GridActor = vtkSmartPointer::New(); + vtkNew gridMapper; + gridMapper->SetInputConnection(m_GridSource->GetOutputPort()); + + m_GridActor->SetMapper(gridMapper); + m_GridActor->GetProperty()->SetRepresentationToWireframe(); + m_GridActor->GetProperty()->SetColor(0.4, 0.4, 0.4); + m_GridActor->GetProperty()->SetLighting(0); + m_GridActor->GetProperty()->SetOpacity(0.5); + m_GridActor->PickableOff(); + m_Renderer->AddActor(m_GridActor); + + // Global Origin Axes + m_OriginAxes = vtkSmartPointer::New(); + m_OriginAxes->SetScaleFactor(1.0); // will be updated + + vtkNew axesMapper; + axesMapper->SetInputConnection(m_OriginAxes->GetOutputPort()); + + m_OriginAxesActor = vtkSmartPointer::New(); + m_OriginAxesActor->SetMapper(axesMapper); + m_OriginAxesActor->PickableOff(); + m_Renderer->AddActor(m_OriginAxesActor); + + UpdateGrid(); + + // Observe interactor to update grid during interaction + vtkNew interactionCallback; + interactionCallback->SetClientData(this); + interactionCallback->SetCallback([](vtkObject*, unsigned long, void* clientdata, void*){ + static_cast(clientdata)->UpdateGrid(); + }); + iren->AddObserver(vtkCommand::InteractionEvent, interactionCallback); + m_Renderer->GetActiveCamera()->AddObserver(vtkCommand::ModifiedEvent, interactionCallback); + + + // Camera-orientation widget (VTK >= 9) +#if VTK_MAJOR_VERSION >= 9 + m_CameraWidget = vtkCameraOrientationWidget::New(); + m_CameraWidget->SetParentRenderer(m_Renderer); + m_CameraWidget->SetInteractor(iren); + m_CameraWidget->On(); +#endif + + m_Renderer->SetBackground(0.15, 0.15, 0.15); + m_Renderer->ResetCamera(); +} + +void Viewport::Reset() +{ + ZoomAuto(); + Render(); +} + +void Viewport::ZoomAuto() +{ + if (m_Renderer) { + m_Renderer->ResetCameraClippingRange(); + m_Renderer->ResetCamera(); + } +} + +void Viewport::AddPuppet(Puppet& prop) +{ + prop.ConnectRenderer(m_Renderer); + prop.ConnectInteractor(GetInteractor()); + Render(); +} + +void Viewport::RemovePuppet(Puppet& prop) +{ + prop.DisconnectRenderer(m_Renderer); + Render(); +} + +void Viewport::addProp(vtkProp* prop) +{ + if (m_Renderer) { + m_Renderer->AddActor(prop); + Render(); + } +} + +void Viewport::RemoveProp(vtkProp* prop) +{ + if (m_Renderer) { + m_Renderer->RemoveViewProp(prop); + Render(); + } +} + +void Viewport::UpdateGrid() +{ + if (!m_Renderer || !m_GridSource) return; + + vtkCamera* camera = m_Renderer->GetActiveCamera(); + if (!camera) return; + + // Determine the "scale" of the view (how many units are visible vertically) + double viewHeight; + if (camera->GetParallelProjection()) { + viewHeight = 2.0 * camera->GetParallelScale(); + } else { + double distance = camera->GetDistance(); + // ViewAngle is height angle in degrees + double angleRad = camera->GetViewAngle() * vtkMath::Pi() / 180.0; + viewHeight = 2.0 * distance * std::tan(angleRad / 2.0); + } + + if (viewHeight <= 0) viewHeight = 1.0; + + // We want roughly 5-15 grid divisions visible. + // Spacing should be a power of 10 (1mm, 1cm, 10cm, 1m, 10m...) + double log10Spacing = std::floor(std::log10(viewHeight / 5.0)); + double spacing = std::pow(10.0, log10Spacing); + + // Get current focal point to center the grid near what we're looking at + double focalPoint[3]; + camera->GetFocalPoint(focalPoint); + + // Align center to spacing + double centerX = std::round(focalPoint[0] / spacing) * spacing; + double centerY = std::round(focalPoint[1] / spacing) * spacing; + + // Number of lines: enough to cover the screen even if aspect ratio is wide + // or if we rotate. 20x20 is usually plenty. + int numLines = 20; + double halfSize = (numLines / 2.0) * spacing; + + double xmin = centerX - halfSize; + double xmax = centerX + halfSize; + double ymin = centerY - halfSize; + double ymax = centerY + halfSize; + + // Update Plane Source + m_GridSource->SetOrigin(xmin, ymin, 0.0); + m_GridSource->SetPoint1(xmax, ymin, 0.0); + m_GridSource->SetPoint2(xmin, ymax, 0.0); + m_GridSource->SetXResolution(numLines); + m_GridSource->SetYResolution(numLines); + m_GridSource->Update(); + + if (m_OriginAxes) { + m_OriginAxes->SetScaleFactor(spacing); + } +} + + + + +} // namespace Vtk +} // namespace uLib diff --git a/src/Vtk/vtkViewport.h b/src/Vtk/vtkViewport.h new file mode 100644 index 0000000..9b57a81 --- /dev/null +++ b/src/Vtk/vtkViewport.h @@ -0,0 +1,76 @@ +#ifndef ULIB_VTK_VIEWPORT_H +#define ULIB_VTK_VIEWPORT_H + +#include "uLibVtkInterface.h" +#include +#include +#include +#include +#include + +class vtkAxes; +class vtkProp; +class vtk3DWidget; +class vtkRenderWindow; +class vtkRenderWindowInteractor; +class vtkCameraOrientationWidget; +class vtkPlaneSource; +class vtkActor; +class vtkNamedColors; +class vtkCamera; + +namespace uLib { +namespace Vtk { + +/** + * @class Viewport + * @brief Base class for VTK viewports, providing core rendering and prop management. + */ +class Viewport { +public: + Viewport(); + virtual ~Viewport(); + + // Render scene + virtual void Render() = 0; + void Reset(); + void ZoomAuto(); + + // Puppet / prop management + void AddPuppet(Puppet &prop); + void RemovePuppet(Puppet &prop); + void addProp(vtkProp *prop); + void RemoveProp(vtkProp *prop); + + // Widget integration + void AddWidget(vtk3DWidget *widget); + + // Direct access to VTK internals + vtkRenderer* GetRenderer() { return m_Renderer; } + virtual vtkRenderWindow* GetRenderWindow() = 0; + virtual vtkRenderWindowInteractor* GetInteractor() = 0; + vtkCornerAnnotation* GetAnnotation() { return m_Annotation; } + vtkCameraOrientationWidget* GetCameraWidget(){ return m_CameraWidget; } + +protected: + void SetupPipeline(vtkRenderWindowInteractor* iren); + + void UpdateGrid(); + + vtkRenderer* m_Renderer; + vtkCornerAnnotation* m_Annotation; + vtkOrientationMarkerWidget* m_Marker; + vtkCameraOrientationWidget* m_CameraWidget; + + vtkSmartPointer m_GridSource; + vtkSmartPointer m_GridActor; + vtkSmartPointer m_OriginAxes; + vtkSmartPointer m_OriginAxesActor; + + vtkNamedColors* m_Colors; +}; + +} // namespace Vtk +} // namespace uLib + +#endif // ULIB_VTK_VIEWPORT_H