/*////////////////////////////////////////////////////////////////////////////// // CMT Cosmic Muon Tomography project ////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova All rights reserved Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it > ------------------------------------------------------------------ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. //////////////////////////////////////////////////////////////////////////////*/ #if VTK_MAJOR_VERSION <= 5 # #else # include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "vtkViewport.h" #include "uLibVtkInterface.h" #include "Math/Transform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uLibVtkInterface.h" #include "vtkHandlerWidget.h" #include "Math/Dense.h" #include "Core/Property.h" namespace uLib { namespace Vtk { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // PUPPET // // PIMPL -------------------------------------------------------------------- // class PuppetData { public: PuppetData() : m_Renderers(vtkSmartPointer::New()), m_Assembly(vtkSmartPointer::New()), m_ShowBoundingBox(false), m_ShowScaleMeasures(false), m_Representation(Puppet::Surface), m_Opacity(-1.0), m_Selectable(true), m_Selected(false), m_Visibility(true), m_Dragable(true) { m_Color[0] = m_Color[1] = m_Color[2] = -1.0; m_Position = Vector3d::Zero(); m_Orientation = Vector3d::Zero(); m_Scale = Vector3d::Ones(); } ~PuppetData() { // No manual Delete needed for smart pointers } // members // vtkSmartPointer m_Renderers; vtkSmartPointer m_Assembly; vtkSmartPointer m_OutlineSource; vtkSmartPointer m_OutlineActor; vtkSmartPointer m_CubeAxesActor; vtkSmartPointer m_HighlightActor; bool m_ShowBoundingBox; bool m_ShowScaleMeasures; int m_Representation; double m_Color[3]; double m_Opacity; bool m_Selectable; bool m_Selected; bool m_Visibility; bool m_Dragable; Vector3d m_Position; Vector3d m_Orientation; Vector3d m_Scale; void ApplyAppearance(vtkProp *p) { p->SetVisibility(m_Visibility); p->SetPickable(m_Selectable); p->SetDragable(m_Dragable); vtkActor *actor = vtkActor::SafeDownCast(p); if (actor) { if (m_Representation != -1) { if (m_Representation == Puppet::SurfaceWithEdges) { actor->GetProperty()->SetRepresentation(VTK_SURFACE); actor->GetProperty()->SetEdgeVisibility(1); } else { actor->GetProperty()->SetRepresentation(m_Representation); actor->GetProperty()->SetEdgeVisibility(0); } } if (m_Color[0] != -1.0) { actor->GetProperty()->SetColor(m_Color); } if (m_Opacity != -1.0) { actor->GetProperty()->SetOpacity(m_Opacity); } } // Handle transformation if it's a Prop3D if (auto* p3d = vtkProp3D::SafeDownCast(p)) { // NOTE: Usually managed by Puppet::Update from model, but here for direct prop manipulation // p3d->SetPosition(m_Position.data()); // p3d->SetOrientation(m_Orientation.data()); // p3d->SetScale(m_Scale.data()); } } 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 matrix from the root prop vtkProp3D* root = nullptr; if (m_Assembly->GetParts()->GetNumberOfItems() == 1) { root = vtkProp3D::SafeDownCast(m_Assembly->GetParts()->GetLastProp()); } else { root = m_Assembly; } if (root) { m_HighlightActor->SetUserMatrix(root->GetMatrix()); } 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); } } } } }; // -------------------------------------------------------------------------- // Puppet::Puppet() : Object(), pd(new PuppetData) { ULIB_ACTIVATE_DISPLAY_PROPERTIES; for (auto* p : this->GetDisplayProperties()) { uLib::Object::connect(p, &uLib::PropertyBase::Updated, this, &Puppet::Update); } } Puppet::~Puppet() { delete pd; } vtkProp *Puppet::GetProp() { if (pd->m_Assembly->GetParts()->GetNumberOfItems() == 1) return pd->m_Assembly->GetParts()->GetLastProp(); else return pd->m_Assembly; } void Puppet::SetProp(vtkProp *prop) { if(prop) { prop->SetPickable(pd->m_Selectable); if (auto* p3d = vtkProp3D::SafeDownCast(prop)) { pd->m_Assembly->AddPart(p3d); } pd->ApplyAppearance(prop); // For the first actor added, seed the tracked display values from the VTK // actor's current state so the display properties panel shows meaningful // initial values instead of the -1 "not-overriding" sentinels. if (pd->m_Assembly->GetParts()->GetNumberOfItems() == 1) { if (auto* actor = vtkActor::SafeDownCast(prop)) { vtkProperty* vp = actor->GetProperty(); if (pd->m_Representation < 0) pd->m_Representation = vp->GetRepresentation(); if (pd->m_Opacity < 0) pd->m_Opacity = vp->GetOpacity(); if (pd->m_Color[0] < 0) vp->GetColor(pd->m_Color); } } } } void Puppet::RemoveProp(vtkProp *prop) { // TODO } vtkPropCollection *Puppet::GetParts() { return pd->m_Assembly->GetParts(); } vtkPropCollection *Puppet::GetProps() { return pd->m_Assembly->GetParts(); } void Puppet::ConnectRenderer(vtkRenderer *renderer) { if(renderer) { this->GetRenderers()->AddItem(renderer); if(vtkProp* prop = this->GetProp()) { renderer->AddViewProp(prop); } if (pd->m_ShowBoundingBox && pd->m_OutlineActor) renderer->AddActor(pd->m_OutlineActor); if (pd->m_ShowScaleMeasures && pd->m_CubeAxesActor) { pd->m_CubeAxesActor->SetCamera(renderer->GetActiveCamera()); renderer->AddActor(pd->m_CubeAxesActor); } if (pd->m_Selected && pd->m_HighlightActor) { renderer->AddActor(pd->m_HighlightActor); } } } void Puppet::DisconnectRenderer(vtkRenderer *renderer) { if(renderer) { if(vtkProp* prop = this->GetProp()) renderer->RemoveViewProp(prop); if (pd->m_ShowBoundingBox && pd->m_OutlineActor) renderer->RemoveActor(pd->m_OutlineActor); if (pd->m_ShowScaleMeasures && pd->m_CubeAxesActor) renderer->RemoveActor(pd->m_CubeAxesActor); this->GetRenderers()->RemoveItem(renderer); } } void Puppet::AddToViewer(Viewport &viewer) { viewer.AddPuppet(*this); } void Puppet::RemoveFromViewer(Viewport &viewer) { viewer.RemovePuppet(*this); } vtkRendererCollection *Puppet::GetRenderers() const { return pd->m_Renderers; } void Puppet::PrintSelf(std::ostream &o) const { o << "Props Assembly: \n"; pd->m_Assembly->PrintSelf(o,vtkIndent(1)); o << "Connected Renderers: \n"; pd->m_Renderers->PrintSelf(o,vtkIndent(1)); } void Puppet::ShowBoundingBox(bool show) { if (pd->m_ShowBoundingBox == show) return; pd->m_ShowBoundingBox = show; if (show) { if (!pd->m_OutlineActor) { pd->m_OutlineSource = vtkSmartPointer::New(); pd->m_OutlineActor = vtkSmartPointer::New(); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInputConnection(pd->m_OutlineSource->GetOutputPort()); pd->m_OutlineActor->SetMapper(mapper); pd->m_OutlineActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } double* bounds = pd->m_Assembly->GetBounds(); pd->m_OutlineSource->SetBounds(bounds); pd->m_OutlineSource->Update(); pd->m_Renderers->InitTraversal(); for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) { vtkRenderer *renderer = pd->m_Renderers->GetNextItem(); renderer->AddActor(pd->m_OutlineActor); } } else { if (pd->m_OutlineActor) { pd->m_Renderers->InitTraversal(); for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) { vtkRenderer *renderer = pd->m_Renderers->GetNextItem(); renderer->RemoveActor(pd->m_OutlineActor); } } } } void Puppet::ShowScaleMeasures(bool show) { if (pd->m_ShowScaleMeasures == show) return; pd->m_ShowScaleMeasures = show; if (show) { if (!pd->m_CubeAxesActor) { pd->m_CubeAxesActor = vtkSmartPointer::New(); pd->m_CubeAxesActor->SetFlyModeToOuterEdges(); pd->m_CubeAxesActor->SetUseTextActor3D(1); pd->m_CubeAxesActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } double* bounds = pd->m_Assembly->GetBounds(); pd->m_CubeAxesActor->SetBounds(bounds); pd->m_Renderers->InitTraversal(); for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) { vtkRenderer *renderer = pd->m_Renderers->GetNextItem(); pd->m_CubeAxesActor->SetCamera(renderer->GetActiveCamera()); renderer->AddActor(pd->m_CubeAxesActor); } } else { if (pd->m_CubeAxesActor) { pd->m_Renderers->InitTraversal(); for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) { vtkRenderer *renderer = pd->m_Renderers->GetNextItem(); renderer->RemoveActor(pd->m_CubeAxesActor); } } } } void Puppet::SetRepresentation(Representation mode) { pd->m_Representation = static_cast(mode); vtkProp3DCollection *props = pd->m_Assembly->GetParts(); props->InitTraversal(); for (int i = 0; i < props->GetNumberOfItems(); ++i) { pd->ApplyAppearance(props->GetNextProp3D()); } } void Puppet::SetRepresentation(const char *mode) { std::string s(mode); if (s == "points") SetRepresentation(Points); else if (s == "wireframe") SetRepresentation(Wireframe); else if (s == "shaded" || s == "surface") SetRepresentation(Surface); else if (s == "edges" || s == "surface+edges" || s == "surfacewithedges") SetRepresentation(SurfaceWithEdges); else if (s == "volume") SetRepresentation(Volume); else if (s == "outline") SetRepresentation(Outline); else if (s == "slice") SetRepresentation(Slice); } void Puppet::SetColor(double r, double g, double b) { pd->m_Color[0] = r; pd->m_Color[1] = g; pd->m_Color[2] = b; vtkProp3DCollection *props = pd->m_Assembly->GetParts(); props->InitTraversal(); for (int i = 0; i < props->GetNumberOfItems(); ++i) { pd->ApplyAppearance(props->GetNextProp3D()); } } void Puppet::SetOpacity(double alpha) { pd->m_Opacity = alpha; vtkProp3DCollection *props = pd->m_Assembly->GetParts(); props->InitTraversal(); for (int i = 0; i < props->GetNumberOfItems(); ++i) { pd->ApplyAppearance(props->GetNextProp3D()); } } void Puppet::SetSelectable(bool selectable) { pd->m_Selectable = selectable; vtkProp3DCollection *props = pd->m_Assembly->GetParts(); props->InitTraversal(); for (int i = 0; i < props->GetNumberOfItems(); ++i) { props->GetNextProp3D()->SetPickable(selectable); } } bool Puppet::IsSelectable() const { return pd->m_Selectable; } void Puppet::SetSelected(bool selected) { if (!pd->m_Selectable) return; if (pd->m_Selected == selected) return; pd->m_Selected = selected; pd->UpdateHighlight();0 } bool Puppet::IsSelected() const { return pd->m_Selected; } void Puppet::Update() { vtkProp* root = this->GetProp(); if (root) { pd->ApplyAppearance(root); // Handle transformation synchronization from content if (auto* content = dynamic_cast(GetContent())) { pd->m_Position = content->GetPosition().cast(); pd->m_Orientation = content->GetOrientation().cast(); pd->m_Scale = content->GetScale().cast(); if (auto* p3d = vtkProp3D::SafeDownCast(root)) { vtkNew vmat; const Matrix4f& emat = content->GetMatrix(); for(int i=0; i<4; ++i) for(int j=0; j<4; ++j) vmat->SetElement(i, j, emat(i,j)); p3d->SetUserMatrix(vmat); // Clear base transform to avoid double-application p3d->SetPosition(0,0,0); p3d->SetOrientation(0,0,0); p3d->SetScale(1,1,1); } } else if (auto* p3d = vtkProp3D::SafeDownCast(root)) { p3d->SetPosition(pd->m_Position.data()); p3d->SetOrientation(pd->m_Orientation.data()); p3d->SetScale(pd->m_Scale.data()); } } vtkProp3DCollection *props = pd->m_Assembly->GetParts(); props->InitTraversal(); for (int i = 0; i < props->GetNumberOfItems(); ++i) { pd->ApplyAppearance(props->GetNextProp3D()); } if (pd->m_Selected) { pd->UpdateHighlight(); } if (pd->m_ShowBoundingBox) { double* bounds = pd->m_Assembly->GetBounds(); pd->m_OutlineSource->SetBounds(bounds); pd->m_OutlineSource->Update(); } if (pd->m_ShowScaleMeasures) { double* bounds = pd->m_Assembly->GetBounds(); pd->m_CubeAxesActor->SetBounds(bounds); } // Notify that the object has been updated (important for UI refresh) this->Object::Updated(); // Trigger immediate re-render of all connected viewports pd->m_Renderers->InitTraversal(); for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) { if (auto* ren = pd->m_Renderers->GetNextItem()) { if (ren->GetRenderWindow()) ren->GetRenderWindow()->Render(); } } } void Puppet::SyncFromVtk() { vtkProp* root = this->GetProp(); if (auto* p3d = vtkProp3D::SafeDownCast(root)) { // Handle content synchronization if it's an AffineTransform if (auto* content = dynamic_cast(GetContent())) { vtkMatrix4x4* vmat = p3d->GetUserMatrix(); if (vmat) { Matrix4f emat; for (int i=0; i<4; ++i) for (int j=0; j<4; ++j) emat(i, j) = vmat->GetElement(i, j); content->SetMatrix(emat); // Re-sync internal puppet properties from the now-updated content pd->m_Position = content->GetPosition().cast(); pd->m_Orientation = content->GetOrientation().cast(); pd->m_Scale = content->GetScale().cast(); } } else { // Update internal puppet properties directly from base components // only if no content exists (old behavior) double pos[3], ori[3], scale[3]; p3d->GetPosition(pos); p3d->GetOrientation(ori); p3d->GetScale(scale); for (int i=0; i<3; ++i) { pd->m_Position(i) = pos[i]; pd->m_Orientation(i) = ori[i]; pd->m_Scale(i) = scale[i]; } } // Notify puppet properties updated if (auto* propPos = this->GetProperty("Position")) propPos->Updated(); if (auto* propOri = this->GetProperty("Orientation")) propOri->Updated(); if (auto* propScale = this->GetProperty("Scale")) propScale->Updated(); this->Object::Updated(); } } void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor) { } struct TransformProxy { PuppetData* pd; template void serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_hrp("Position", pd->m_Position, "mm"); ar & boost::serialization::make_hrp("Orientation", pd->m_Orientation, "deg"); ar & boost::serialization::make_hrp("Scale", pd->m_Scale, ""); } }; struct AppearanceProxy { PuppetData* pd; template void serialize(Archive & ar, const unsigned int version) { ar & boost::serialization::make_hrp("ColorR", pd->m_Color[0]); ar & boost::serialization::make_hrp("ColorG", pd->m_Color[1]); ar & boost::serialization::make_hrp("ColorB", pd->m_Color[2]); ar & boost::serialization::make_hrp("Opacity", pd->m_Opacity); ar & boost::serialization::make_hrp_enum("Representation", pd->m_Representation, {"Points", "Wireframe", "Surface", "SurfaceWithEdges", "Volume", "Outline", "Slice"}); ar & boost::serialization::make_hrp("Visibility", pd->m_Visibility); ar & boost::serialization::make_hrp("Pickable", pd->m_Selectable); ar & boost::serialization::make_hrp("Dragable", pd->m_Dragable); } }; void Puppet::serialize_display(Archive::display_properties_archive & ar, const unsigned int version) { AppearanceProxy appearance{pd}; ar & boost::serialization::make_nvp("Appearance", appearance); TransformProxy transform{pd}; ar & boost::serialization::make_nvp("Transform", transform); } void Puppet::serialize(Archive::xml_oarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::xml_iarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::text_oarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::text_iarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::hrt_oarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::hrt_iarchive & ar, const unsigned int v) { } void Puppet::serialize(Archive::log_archive & ar, const unsigned int v) { } } // namespace Vtk } // namespace uLib