2 Commits

Author SHA1 Message Date
AndreaRigoni
553bb7fd61 widget in viewport 2026-03-17 17:28:26 +00:00
AndreaRigoni
bc437a3913 fix segfault 2026-03-17 15:47:27 +00:00
6 changed files with 273 additions and 62 deletions

View File

@@ -53,9 +53,12 @@
#include <vtkRenderer.h> #include <vtkRenderer.h>
#include <vtkProperty.h> #include <vtkProperty.h>
#include <vtkCamera.h> #include <vtkCamera.h>
#include <vtkPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkTransform.h>
#include "uLibVtkInterface.h" #include "uLibVtkInterface.h"
#include "vtkHandlerWidget.h"
@@ -78,36 +81,30 @@ namespace Vtk {
class PuppetData { class PuppetData {
public: public:
PuppetData() : PuppetData() :
m_Renderers(vtkRendererCollection::New()), m_Renderers(vtkSmartPointer<vtkRendererCollection>::New()),
m_Assembly(vtkPropAssembly::New()), m_Assembly(vtkSmartPointer<vtkPropAssembly>::New()),
m_OutlineSource(NULL),
m_OutlineActor(NULL),
m_CubeAxesActor(NULL),
m_ShowBoundingBox(false), m_ShowBoundingBox(false),
m_ShowScaleMeasures(false), m_ShowScaleMeasures(false),
m_Representation(-1), m_Representation(-1),
m_Opacity(-1.0) m_Opacity(-1.0),
m_Selectable(true),
m_Selected(false)
{ {
m_Color[0] = m_Color[1] = m_Color[2] = -1.0; m_Color[0] = m_Color[1] = m_Color[2] = -1.0;
} }
~PuppetData() { ~PuppetData() {
m_Renderers->RemoveAllItems(); // No manual Delete needed for smart pointers
m_Assembly->GetParts()->RemoveAllItems();
m_Renderers->Delete();
m_Assembly->Delete();
if (m_OutlineSource) m_OutlineSource->Delete();
if (m_OutlineActor) m_OutlineActor->Delete();
if (m_CubeAxesActor) m_CubeAxesActor->Delete();
} }
// members // // members //
vtkRendererCollection *m_Renderers; vtkSmartPointer<vtkRendererCollection> m_Renderers;
vtkPropAssembly *m_Assembly; vtkSmartPointer<vtkPropAssembly> m_Assembly;
vtkOutlineSource *m_OutlineSource; vtkSmartPointer<vtkOutlineSource> m_OutlineSource;
vtkActor *m_OutlineActor; vtkSmartPointer<vtkActor> m_OutlineActor;
vtkCubeAxesActor *m_CubeAxesActor; vtkSmartPointer<vtkCubeAxesActor> m_CubeAxesActor;
vtkSmartPointer<vtkActor> m_HighlightActor;
bool m_ShowBoundingBox; bool m_ShowBoundingBox;
bool m_ShowScaleMeasures; bool m_ShowScaleMeasures;
@@ -132,6 +129,69 @@ public:
actor->GetProperty()->SetOpacity(m_Opacity); actor->GetProperty()->SetOpacity(m_Opacity);
} }
} }
void UpdateHighlight() {
if (m_Selected) {
if (!m_HighlightActor) {
vtkSmartPointer<vtkFeatureEdges> edges = vtkSmartPointer<vtkFeatureEdges>::New();
edges->BoundaryEdgesOn();
edges->FeatureEdgesOn();
edges->SetFeatureAngle(30);
edges->NonManifoldEdgesOn();
edges->ManifoldEdgesOff();
// Find first polydata in assembly to highlight
vtkPropCollection *parts = m_Assembly->GetParts();
parts->InitTraversal();
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
vtkActor *actor = vtkActor::SafeDownCast(parts->GetNextProp());
if (actor && actor->GetMapper() && actor->GetMapper()->GetDataSetInput()) {
edges->SetInputData(vtkPolyData::SafeDownCast(actor->GetMapper()->GetDataSetInput()));
break;
}
}
m_HighlightActor = vtkSmartPointer<vtkActor>::New();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(edges->GetOutputPort());
m_HighlightActor->SetMapper(mapper);
m_HighlightActor->GetProperty()->SetColor(1.0, 0.5, 0.0); // Orange
m_HighlightActor->GetProperty()->SetLineWidth(2.0);
m_HighlightActor->GetProperty()->SetLighting(0);
}
// Update highlight data and transform from first actor
vtkPropCollection *parts = m_Assembly->GetParts();
parts->InitTraversal();
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
vtkActor *actor = vtkActor::SafeDownCast(parts->GetNextProp());
if (actor) {
// Sync transform
m_HighlightActor->SetUserTransform(actor->GetUserTransform());
m_HighlightActor->SetPosition(actor->GetPosition());
m_HighlightActor->SetOrientation(actor->GetOrientation());
m_HighlightActor->SetScale(actor->GetScale());
break;
}
}
m_Renderers->InitTraversal();
for (int i = 0; i < m_Renderers->GetNumberOfItems(); ++i) {
vtkRenderer *ren = m_Renderers->GetNextItem();
ren->AddActor(m_HighlightActor);
}
} else {
if (m_HighlightActor) {
m_Renderers->InitTraversal();
for (int i = 0; i < m_Renderers->GetNumberOfItems(); ++i) {
m_Renderers->GetNextItem()->RemoveActor(m_HighlightActor);
}
}
}
}
bool m_Selectable;
bool m_Selected;
}; };
// -------------------------------------------------------------------------- // // -------------------------------------------------------------------------- //
@@ -195,6 +255,10 @@ void Puppet::ConnectRenderer(vtkRenderer *renderer)
d->m_CubeAxesActor->SetCamera(renderer->GetActiveCamera()); d->m_CubeAxesActor->SetCamera(renderer->GetActiveCamera());
renderer->AddActor(d->m_CubeAxesActor); renderer->AddActor(d->m_CubeAxesActor);
} }
if (d->m_Selected && d->m_HighlightActor) {
renderer->AddActor(d->m_HighlightActor);
}
} }
} }
@@ -248,8 +312,8 @@ void Puppet::ShowBoundingBox(bool show)
d->m_ShowBoundingBox = show; d->m_ShowBoundingBox = show;
if (show) { if (show) {
if (!d->m_OutlineActor) { if (!d->m_OutlineActor) {
d->m_OutlineSource = vtkOutlineSource::New(); d->m_OutlineSource = vtkSmartPointer<vtkOutlineSource>::New();
d->m_OutlineActor = vtkActor::New(); d->m_OutlineActor = vtkSmartPointer<vtkActor>::New();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(d->m_OutlineSource->GetOutputPort()); mapper->SetInputConnection(d->m_OutlineSource->GetOutputPort());
d->m_OutlineActor->SetMapper(mapper); d->m_OutlineActor->SetMapper(mapper);
@@ -282,7 +346,7 @@ void Puppet::ShowScaleMeasures(bool show)
d->m_ShowScaleMeasures = show; d->m_ShowScaleMeasures = show;
if (show) { if (show) {
if (!d->m_CubeAxesActor) { if (!d->m_CubeAxesActor) {
d->m_CubeAxesActor = vtkCubeAxesActor::New(); d->m_CubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
d->m_CubeAxesActor->SetFlyModeToOuterEdges(); d->m_CubeAxesActor->SetFlyModeToOuterEdges();
d->m_CubeAxesActor->SetUseTextActor3D(1); d->m_CubeAxesActor->SetUseTextActor3D(1);
d->m_CubeAxesActor->GetProperty()->SetColor(1.0, 1.0, 1.0); d->m_CubeAxesActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
@@ -363,8 +427,32 @@ void Puppet::SetOpacity(double alpha)
void Puppet::SetSelectable(bool selectable)
{
d->m_Selectable = selectable;
}
bool Puppet::IsSelectable() const
{
return d->m_Selectable;
}
void Puppet::SetSelected(bool selected)
{
if (!d->m_Selectable) return;
if (d->m_Selected == selected) return;
d->m_Selected = selected;
d->UpdateHighlight();
}
} // Vtk bool Puppet::IsSelected() const
} // uLib {
return d->m_Selected;
}
void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor)
{
}
} // namespace Vtk
} // namespace uLib

View File

@@ -80,11 +80,13 @@ public:
void SetOpacity(double alpha); void SetOpacity(double alpha);
void SetSelectable(bool selectable = true);
bool IsSelectable() const;
void SetSelected(bool selected = true);
bool IsSelected() const;
virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor) { virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor);
(void)interactor;
}
protected: protected:
void SetProp(vtkProp *prop); void SetProp(vtkProp *prop);
@@ -92,6 +94,9 @@ protected:
void RemoveProp(vtkProp *prop); void RemoveProp(vtkProp *prop);
private: private:
Puppet(const Puppet&) = delete;
Puppet& operator=(const Puppet&) = delete;
friend class PuppetData; friend class PuppetData;
class PuppetData *d; class PuppetData *d;
}; };

View File

@@ -93,8 +93,9 @@ void Viewer::InstallPipe() {
} }
void Viewer::UninstallPipe() { void Viewer::UninstallPipe() {
m_Renderer->RemoveAllViewProps(); if (m_Renderer) {
m_Renderer->Clear(); m_Renderer->RemoveAllViewProps();
}
} }
void Viewer::Render() { void Viewer::Render() {

View File

@@ -98,6 +98,8 @@ public:
void SetTransform(::vtkTransform *t); void SetTransform(::vtkTransform *t);
void GetTransform(::vtkTransform *t); void GetTransform(::vtkTransform *t);
::vtkRenderer *GetOverlayRenderer() { return this->m_OverlayRenderer; }
protected: protected:
void CreateGizmos(); void CreateGizmos();
void UpdateGizmoPosition(); void UpdateGizmoPosition();

View File

@@ -1,36 +1,34 @@
#include "vtkViewport.h" #include "vtkViewport.h"
#include <vtkPropAssembly.h>
#include <vtkAxes.h> #include <vtkPropCollection.h>
#include <vtkAxesActor.h> #include <vtkProp3D.h>
#include <vtkCamera.h> #include <vtkCamera.h>
#include <vtkCameraOrientationWidget.h> #include <algorithm>
#include <vtkInteractorStyleTrackballCamera.h> #include <vtkInteractorStyleTrackballCamera.h>
#include <vtkObjectFactory.h> #include <vtkObjectFactory.h>
#include <vtkPlaneSource.h>
#include <vtkRenderWindow.h> #include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h> #include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkNew.h> #include <vtkNew.h>
#include <vtkTextProperty.h> #include <vtkTextProperty.h>
#include <vtkPolyDataMapper.h> #include <vtkPolyDataMapper.h>
#include <vtkNamedColors.h>
#include <vtkProperty.h> #include <vtkProperty.h>
#include <vtkCallbackCommand.h> #include <vtkCallbackCommand.h>
#include <vtkMath.h> #include <vtkMath.h>
#include <vtkActor.h>
#include <cmath> #include <cmath>
#include <iostream>
#include "vtkHandlerWidget.h"
namespace uLib { namespace uLib {
namespace Vtk { namespace Vtk {
Viewport::Viewport() Viewport::Viewport()
: m_Renderer(vtkRenderer::New()) : m_Renderer(vtkSmartPointer<vtkRenderer>::New())
, m_Annotation(vtkCornerAnnotation::New()) , m_Annotation(vtkSmartPointer<vtkCornerAnnotation>::New())
, m_Marker(vtkOrientationMarkerWidget::New()) , m_Marker(vtkSmartPointer<vtkOrientationMarkerWidget>::New())
, m_CameraWidget(nullptr) , m_CameraWidget(nullptr)
, m_Colors(vtkNamedColors::New()) , m_Colors(vtkSmartPointer<vtkNamedColors>::New())
{ {
} }
@@ -38,14 +36,7 @@ Viewport::~Viewport()
{ {
if (m_Renderer) { if (m_Renderer) {
m_Renderer->RemoveAllViewProps(); 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) void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
@@ -112,14 +103,99 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
// Camera-orientation widget (VTK >= 9) // Camera-orientation widget (VTK >= 9)
#if VTK_MAJOR_VERSION >= 9 #if VTK_MAJOR_VERSION >= 9
m_CameraWidget = vtkCameraOrientationWidget::New(); m_CameraWidget = vtkSmartPointer<vtkCameraOrientationWidget>::New();
m_CameraWidget->SetParentRenderer(m_Renderer); m_CameraWidget->SetParentRenderer(m_Renderer);
m_CameraWidget->SetInteractor(iren); m_CameraWidget->SetInteractor(iren);
m_CameraWidget->On(); m_CameraWidget->On();
#endif #endif
m_Renderer->SetBackground(0.15, 0.15, 0.15); m_Renderer->SetBackground(0.15, 0.15, 0.15);
m_Renderer->ResetCamera(); m_Renderer->ResetCamera();
// Setup layering for overimposed rendering
if (iren->GetRenderWindow()) {
iren->GetRenderWindow()->SetNumberOfLayers(2);
m_Renderer->SetLayer(0);
}
// Setup Handler Widget
m_HandlerWidget = vtkSmartPointer<vtkHandlerWidget>::New();
m_HandlerWidget->SetInteractor(iren);
m_HandlerWidget->SetCurrentRenderer(m_Renderer);
if (m_HandlerWidget->GetOverlayRenderer()) {
m_HandlerWidget->GetOverlayRenderer()->SetLayer(1);
}
// Picking for selection
m_Picker = vtkSmartPointer<vtkCellPicker>::New();
vtkNew<vtkCallbackCommand> clickCallback;
clickCallback->SetClientData(this);
clickCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
auto* iren = static_cast<vtkRenderWindowInteractor*>(caller);
auto* self = static_cast<Viewport*>(clientdata);
int* pos = iren->GetEventPosition();
self->m_Picker->Pick(pos[0], pos[1], 0, self->m_Renderer);
vtkProp* picked = self->m_Picker->GetViewProp();
Puppet* target = nullptr;
if (picked) {
for (auto* p : self->m_Puppets) {
if (p->GetProp() == picked) {
target = p;
break;
}
auto* assembly = vtkPropAssembly::SafeDownCast(p->GetProp());
if (assembly) {
bool found = false;
auto* parts = assembly->GetParts();
parts->InitTraversal();
for (int i=0; i<parts->GetNumberOfItems(); ++i) {
if (parts->GetNextProp() == picked) {
found = true;
break;
}
}
if (found) {
target = p;
break;
}
}
}
}
self->SelectPuppet(target);
});
iren->AddObserver(vtkCommand::LeftButtonPressEvent, clickCallback);
// Keyboard events for widget coordinate frame
vtkNew<vtkCallbackCommand> keyCallback;
keyCallback->SetClientData(this);
keyCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){
auto* iren = static_cast<vtkRenderWindowInteractor*>(caller);
auto* self = static_cast<Viewport*>(clientdata);
if (!self->m_HandlerWidget || !self->m_HandlerWidget->GetEnabled()) return;
std::string key = iren->GetKeySym();
if (key == "l") {
self->m_HandlerWidget->SetReferenceFrame(vtkHandlerWidget::LOCAL);
std::cout << "Widget Frame: LOCAL" << std::endl;
}
else if (key == "g") {
self->m_HandlerWidget->SetReferenceFrame(vtkHandlerWidget::GLOBAL);
std::cout << "Widget Frame: GLOBAL" << std::endl;
}
else if (key == "c") {
self->m_HandlerWidget->SetReferenceFrame(vtkHandlerWidget::CENTER);
std::cout << "Widget Frame: CENTER" << std::endl;
}
else if (key == "k") {
self->m_HandlerWidget->SetReferenceFrame(vtkHandlerWidget::CENTER_LOCAL);
std::cout << "Widget Frame: CENTER_LOCAL" << std::endl;
}
iren->Render();
});
iren->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
} }
void Viewport::Reset() void Viewport::Reset()
@@ -138,17 +214,43 @@ void Viewport::ZoomAuto()
void Viewport::AddPuppet(Puppet& prop) void Viewport::AddPuppet(Puppet& prop)
{ {
m_Puppets.push_back(&prop);
prop.ConnectRenderer(m_Renderer); prop.ConnectRenderer(m_Renderer);
prop.ConnectInteractor(GetInteractor());
Render(); Render();
} }
void Viewport::RemovePuppet(Puppet& prop) void Viewport::RemovePuppet(Puppet& prop)
{ {
if (prop.IsSelected()) SelectPuppet(nullptr);
auto it = std::find(m_Puppets.begin(), m_Puppets.end(), &prop);
if (it != m_Puppets.end()) m_Puppets.erase(it);
prop.DisconnectRenderer(m_Renderer); prop.DisconnectRenderer(m_Renderer);
Render(); Render();
} }
void Viewport::SelectPuppet(Puppet* prop)
{
for (auto* p : m_Puppets) {
p->SetSelected(p == prop);
}
if (m_HandlerWidget) {
if (prop) {
vtkProp3D* prop3d = vtkProp3D::SafeDownCast(prop->GetProp());
if (prop3d) {
m_HandlerWidget->SetProp3D(prop3d);
m_HandlerWidget->SetEnabled(1);
m_HandlerWidget->PlaceWidget(prop3d->GetBounds());
}
} else {
m_HandlerWidget->SetEnabled(0);
m_HandlerWidget->SetProp3D(nullptr);
}
}
Render();
}
void Viewport::addProp(vtkProp* prop) void Viewport::addProp(vtkProp* prop)
{ {
if (m_Renderer) { if (m_Renderer) {

View File

@@ -7,21 +7,29 @@
#include <vtkRenderer.h> #include <vtkRenderer.h>
#include <vtkSmartPointer.h> #include <vtkSmartPointer.h>
#include <vtkVersion.h> #include <vtkVersion.h>
#include <vtkRenderer.h>
#include <vtkCornerAnnotation.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkCameraOrientationWidget.h>
#include <vtkNamedColors.h>
#include <vtkAxes.h>
#include <vtkAxesActor.h>
#include <vtkPlaneSource.h>
#include <vtkActor.h>
#include <vtkCellPicker.h>
#include <vector>
class vtkAxes;
class vtkProp; class vtkProp;
class vtk3DWidget; class vtk3DWidget;
class vtkRenderWindow; class vtkRenderWindow;
class vtkRenderWindowInteractor; class vtkRenderWindowInteractor;
class vtkCameraOrientationWidget;
class vtkPlaneSource;
class vtkActor;
class vtkNamedColors;
class vtkCamera; class vtkCamera;
namespace uLib { namespace uLib {
namespace Vtk { namespace Vtk {
class vtkHandlerWidget;
/** /**
* @class Viewport * @class Viewport
* @brief Base class for VTK viewports, providing core rendering and prop management. * @brief Base class for VTK viewports, providing core rendering and prop management.
@@ -39,6 +47,7 @@ public:
// Puppet / prop management // Puppet / prop management
void AddPuppet(Puppet &prop); void AddPuppet(Puppet &prop);
void RemovePuppet(Puppet &prop); void RemovePuppet(Puppet &prop);
void SelectPuppet(Puppet *prop);
void addProp(vtkProp *prop); void addProp(vtkProp *prop);
void RemoveProp(vtkProp *prop); void RemoveProp(vtkProp *prop);
@@ -57,17 +66,21 @@ protected:
void UpdateGrid(); void UpdateGrid();
vtkRenderer* m_Renderer; vtkSmartPointer<vtkRenderer> m_Renderer;
vtkCornerAnnotation* m_Annotation; vtkSmartPointer<vtkCornerAnnotation> m_Annotation;
vtkOrientationMarkerWidget* m_Marker; vtkSmartPointer<vtkOrientationMarkerWidget> m_Marker;
vtkCameraOrientationWidget* m_CameraWidget; vtkSmartPointer<vtkCameraOrientationWidget> m_CameraWidget;
vtkSmartPointer<vtkPlaneSource> m_GridSource; vtkSmartPointer<vtkPlaneSource> m_GridSource;
vtkSmartPointer<vtkActor> m_GridActor; vtkSmartPointer<vtkActor> m_GridActor;
vtkSmartPointer<vtkAxes> m_OriginAxes; vtkSmartPointer<vtkAxes> m_OriginAxes;
vtkSmartPointer<vtkActor> m_OriginAxesActor; vtkSmartPointer<vtkActor> m_OriginAxesActor;
vtkNamedColors* m_Colors; vtkSmartPointer<vtkNamedColors> m_Colors;
vtkSmartPointer<vtkHandlerWidget> m_HandlerWidget;
std::vector<Puppet*> m_Puppets;
vtkSmartPointer<vtkCellPicker> m_Picker;
}; };
} // namespace Vtk } // namespace Vtk