diff --git a/src/Vtk/uLibVtkInterface.cxx b/src/Vtk/uLibVtkInterface.cxx index 186a024..46f7b07 100644 --- a/src/Vtk/uLibVtkInterface.cxx +++ b/src/Vtk/uLibVtkInterface.cxx @@ -53,9 +53,12 @@ #include #include #include - +#include +#include +#include #include "uLibVtkInterface.h" +#include "vtkHandlerWidget.h" @@ -78,36 +81,30 @@ namespace Vtk { class PuppetData { public: PuppetData() : - m_Renderers(vtkRendererCollection::New()), - m_Assembly(vtkPropAssembly::New()), - m_OutlineSource(NULL), - m_OutlineActor(NULL), - m_CubeAxesActor(NULL), + m_Renderers(vtkSmartPointer::New()), + m_Assembly(vtkSmartPointer::New()), m_ShowBoundingBox(false), m_ShowScaleMeasures(false), 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; } ~PuppetData() { - m_Renderers->RemoveAllItems(); - 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(); + // No manual Delete needed for smart pointers } // members // - vtkRendererCollection *m_Renderers; - vtkPropAssembly *m_Assembly; + vtkSmartPointer m_Renderers; + vtkSmartPointer m_Assembly; - vtkOutlineSource *m_OutlineSource; - vtkActor *m_OutlineActor; - vtkCubeAxesActor *m_CubeAxesActor; + vtkSmartPointer m_OutlineSource; + vtkSmartPointer m_OutlineActor; + vtkSmartPointer m_CubeAxesActor; + vtkSmartPointer m_HighlightActor; bool m_ShowBoundingBox; bool m_ShowScaleMeasures; @@ -132,6 +129,69 @@ public: actor->GetProperty()->SetOpacity(m_Opacity); } } + + void UpdateHighlight() { + if (m_Selected) { + if (!m_HighlightActor) { + vtkSmartPointer edges = vtkSmartPointer::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::New(); + vtkSmartPointer mapper = vtkSmartPointer::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()); 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; if (show) { if (!d->m_OutlineActor) { - d->m_OutlineSource = vtkOutlineSource::New(); - d->m_OutlineActor = vtkActor::New(); + d->m_OutlineSource = vtkSmartPointer::New(); + d->m_OutlineActor = vtkSmartPointer::New(); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputConnection(d->m_OutlineSource->GetOutputPort()); d->m_OutlineActor->SetMapper(mapper); @@ -282,7 +346,7 @@ void Puppet::ShowScaleMeasures(bool show) d->m_ShowScaleMeasures = show; if (show) { if (!d->m_CubeAxesActor) { - d->m_CubeAxesActor = vtkCubeAxesActor::New(); + d->m_CubeAxesActor = vtkSmartPointer::New(); d->m_CubeAxesActor->SetFlyModeToOuterEdges(); d->m_CubeAxesActor->SetUseTextActor3D(1); 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 -} // uLib +bool Puppet::IsSelected() const +{ + return d->m_Selected; +} + +void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor) +{ +} + +} // namespace Vtk +} // namespace uLib diff --git a/src/Vtk/uLibVtkInterface.h b/src/Vtk/uLibVtkInterface.h index c3127fc..4ae43f1 100644 --- a/src/Vtk/uLibVtkInterface.h +++ b/src/Vtk/uLibVtkInterface.h @@ -80,11 +80,13 @@ public: void SetOpacity(double alpha); + void SetSelectable(bool selectable = true); + bool IsSelectable() const; - - virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor) { - (void)interactor; - } + void SetSelected(bool selected = true); + bool IsSelected() const; + + virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor); protected: void SetProp(vtkProp *prop); @@ -92,6 +94,9 @@ protected: void RemoveProp(vtkProp *prop); private: + Puppet(const Puppet&) = delete; + Puppet& operator=(const Puppet&) = delete; + friend class PuppetData; class PuppetData *d; }; diff --git a/src/Vtk/uLibVtkViewer.cpp b/src/Vtk/uLibVtkViewer.cpp index 510d051..a67365b 100644 --- a/src/Vtk/uLibVtkViewer.cpp +++ b/src/Vtk/uLibVtkViewer.cpp @@ -93,8 +93,9 @@ void Viewer::InstallPipe() { } void Viewer::UninstallPipe() { - m_Renderer->RemoveAllViewProps(); - m_Renderer->Clear(); + if (m_Renderer) { + m_Renderer->RemoveAllViewProps(); + } } void Viewer::Render() { diff --git a/src/Vtk/vtkHandlerWidget.h b/src/Vtk/vtkHandlerWidget.h index 6bc1a28..dfdea55 100644 --- a/src/Vtk/vtkHandlerWidget.h +++ b/src/Vtk/vtkHandlerWidget.h @@ -98,6 +98,8 @@ public: void SetTransform(::vtkTransform *t); void GetTransform(::vtkTransform *t); + ::vtkRenderer *GetOverlayRenderer() { return this->m_OverlayRenderer; } + protected: void CreateGizmos(); void UpdateGizmoPosition(); diff --git a/src/Vtk/vtkViewport.cpp b/src/Vtk/vtkViewport.cpp index 66e7b50..a20f55f 100644 --- a/src/Vtk/vtkViewport.cpp +++ b/src/Vtk/vtkViewport.cpp @@ -1,6 +1,9 @@ #include "vtkViewport.h" - +#include +#include +#include #include +#include #include #include #include @@ -14,6 +17,8 @@ #include #include #include +#include +#include "vtkHandlerWidget.h" namespace uLib { namespace Vtk { @@ -103,9 +108,94 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren) m_CameraWidget->SetInteractor(iren); m_CameraWidget->On(); #endif - m_Renderer->SetBackground(0.15, 0.15, 0.15); 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::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::New(); + vtkNew clickCallback; + clickCallback->SetClientData(this); + clickCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){ + auto* iren = static_cast(caller); + auto* self = static_cast(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; iGetNumberOfItems(); ++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 keyCallback; + keyCallback->SetClientData(this); + keyCallback->SetCallback([](vtkObject* caller, unsigned long, void* clientdata, void*){ + auto* iren = static_cast(caller); + auto* self = static_cast(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() @@ -124,17 +214,43 @@ void Viewport::ZoomAuto() void Viewport::AddPuppet(Puppet& prop) { + m_Puppets.push_back(&prop); prop.ConnectRenderer(m_Renderer); - prop.ConnectInteractor(GetInteractor()); Render(); } 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); 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) { if (m_Renderer) { diff --git a/src/Vtk/vtkViewport.h b/src/Vtk/vtkViewport.h index 648720d..2d8424a 100644 --- a/src/Vtk/vtkViewport.h +++ b/src/Vtk/vtkViewport.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include class vtkProp; class vtk3DWidget; @@ -26,6 +28,8 @@ class vtkCamera; namespace uLib { namespace Vtk { +class vtkHandlerWidget; + /** * @class Viewport * @brief Base class for VTK viewports, providing core rendering and prop management. @@ -43,6 +47,7 @@ public: // Puppet / prop management void AddPuppet(Puppet &prop); void RemovePuppet(Puppet &prop); + void SelectPuppet(Puppet *prop); void addProp(vtkProp *prop); void RemoveProp(vtkProp *prop); @@ -72,6 +77,10 @@ protected: vtkSmartPointer m_OriginAxesActor; vtkSmartPointer m_Colors; + + vtkSmartPointer m_HandlerWidget; + std::vector m_Puppets; + vtkSmartPointer m_Picker; }; } // namespace Vtk