diff --git a/app/gcompose/src/ContextModel.cpp b/app/gcompose/src/ContextModel.cpp index a8933ea..d09976c 100644 --- a/app/gcompose/src/ContextModel.cpp +++ b/app/gcompose/src/ContextModel.cpp @@ -148,6 +148,7 @@ QVariant ContextModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) return QVariant(); uLib::Object* obj = static_cast(index.internalPointer()); + if (!obj) return QVariant(); if (role == Qt::DisplayRole) { QString typeName = getDemangledName(typeid(*obj)); diff --git a/app/gcompose/src/QViewportPane.cpp b/app/gcompose/src/QViewportPane.cpp index 9f423d2..6eb64a0 100644 --- a/app/gcompose/src/QViewportPane.cpp +++ b/app/gcompose/src/QViewportPane.cpp @@ -94,8 +94,8 @@ void QViewportPane::setObject(uLib::Object* obj) { void QViewportPane::setViewport(QWidget* viewport, const QString& title) { if (m_viewport) { - m_viewport->parentWidget()->layout()->removeWidget(m_viewport); - delete m_viewport; + // Use deleteLater() instead of delete to avoid crashes during repaint cycles + m_viewport->deleteLater(); } m_viewport = viewport; m_titleLabel->setText(title); @@ -105,21 +105,8 @@ void QViewportPane::setViewport(QWidget* viewport, const QString& title) { mainAreaLayout->insertWidget(0, m_viewport); } - QViewportPane::~QViewportPane() {} -void QViewportPane::setViewport(QWidget* viewport, const QString& title) { - if (m_viewport) { - m_layout->removeWidget(m_viewport); - delete m_viewport; - } - m_viewport = viewport; - m_titleLabel->setText(title); - - m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_layout->addWidget(m_viewport); -} - void QViewportPane::addVtkViewport() { auto* viewport = new uLib::Vtk::QViewport(this); setViewport(viewport, "VTK Viewport"); diff --git a/app/gcompose/src/ViewportPane.cpp b/app/gcompose/src/ViewportPane.cpp index 5341ab6..76fce20 100644 --- a/app/gcompose/src/ViewportPane.cpp +++ b/app/gcompose/src/ViewportPane.cpp @@ -115,7 +115,7 @@ void ViewportPane::setObject(uLib::Object* obj) { void ViewportPane::setViewport(QWidget* viewport, const QString& title) { if (m_viewport) { - delete m_viewport; + m_viewport->deleteLater(); } m_viewport = viewport; m_titleLabel->setText(title); diff --git a/build.log b/build.log index 1f51156..cf407e1 100644 --- a/build.log +++ b/build.log @@ -1,36 +1 @@ -make: Entering directory '/home/rigoni/devel/cmt/ulib/build' -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/VoxImage.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/TriangleMesh.cpp.o -[ 30%] Building CXX object src/Core/CMakeFiles/mutomCore.dir/Options.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Dense.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/StructuredGrid.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/VoxRaytracer.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/StructuredData.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Structured2DGrid.cpp.o -[ 30%] Building CXX object src/Math/CMakeFiles/mutomMath.dir/Structured4DGrid.cpp.o -[ 33%] Linking CXX shared library libmutomCore.so -[ 33%] Built target mutomCore -[ 36%] Linking CXX shared library libmutomMath.so -[ 36%] Built target mutomMath -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkContainerBox.cpp.o -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/uLibVtkInterface.cxx.o -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkStructuredGrid.cpp.o -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkMuonScatter.cxx.o -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/uLibVtkViewer.cpp.o -[ 63%] Generating mutomRootDict.cxx, libmutomRootDict_rdict.pcm, libmutomRootDict.rootmap -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkVoxImage.cpp.o -[ 63%] Building CXX object src/Vtk/CMakeFiles/mutomVtk.dir/vtkVoxRaytracerRepresentation.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorSkinHit.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorHit.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorMCTrack.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorInfo.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/RootMuonScatter.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorPrimaryVertex.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/muCastorMuDetDIGI.cpp.o -[ 90%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/SkinDetectorWriter.cpp.o -[ 93%] Building CXX object src/Root/CMakeFiles/mutomRoot.dir/mutomRootDict.cxx.o -[ 96%] Linking CXX shared library libmutomVtk.so -[ 96%] Built target mutomVtk -[100%] Linking CXX shared library libmutomRoot.so -[100%] Built target mutomRoot -make: Leaving directory '/home/rigoni/devel/cmt/ulib/build' +ninja: error: loading 'build.ninja': No such file or directory diff --git a/condaenv.yml b/condaenv.yml index 62debfd..f07a886 100644 --- a/condaenv.yml +++ b/condaenv.yml @@ -9,7 +9,7 @@ dependencies: - root - vtk=9.4 # VTK 9.4 - pybind11 -# - boost=1.86.0 # requested by VTK 9.4 + #- boost=1.86.0 # requested by VTK 9.4 - ninja - clang - clangxx diff --git a/src/Core/DataAllocator.h b/src/Core/DataAllocator.h index 407783b..4c19d0a 100644 --- a/src/Core/DataAllocator.h +++ b/src/Core/DataAllocator.h @@ -46,6 +46,8 @@ public: DataAllocator(size_t size = 0, bool owns_objects = true) : m_Size(size), m_RamData(nullptr), m_VramData(nullptr), m_Device(MemoryDevice::RAM), m_OwnsObjects(owns_objects) { + if (m_Size >= (static_cast(1) << 60)) + throw std::invalid_argument("DataAllocator: absurdly large size requested"); if (m_Size > 0) { if (m_OwnsObjects) m_RamData = new T[m_Size](); @@ -139,7 +141,7 @@ public: void MoveToRAM() { if (m_Device == MemoryDevice::RAM) return; - if (!m_RamData && m_Size > 0) { + if (!m_RamData && m_Size > 0 && m_Size < (static_cast(1) << 60)) { if (m_OwnsObjects) m_RamData = new T[m_Size](); else @@ -178,7 +180,7 @@ public: T *newRam = nullptr; T *newVram = nullptr; - if (size > 0) { + if (size > 0 && size < (static_cast(1) << 60)) { if (m_OwnsObjects) newRam = new T[size](); else diff --git a/src/Core/Object.cpp b/src/Core/Object.cpp index f1d92fb..c992682 100644 --- a/src/Core/Object.cpp +++ b/src/Core/Object.cpp @@ -208,7 +208,8 @@ void Object::PrintSelf(std::ostream &o) const { bool Object::addSignalImpl(SignalBase *sig, GenericMFPtr fptr, const char *name) { - ObjectPrivate::Signal s = {fptr, std::string(name), sig}; + if (!d) return false; + ObjectPrivate::Signal s = {fptr, std::string(name ? name : "unnamed"), sig}; d->sigv.push_back(s); return true; } diff --git a/src/Core/ObjectsContext.cpp b/src/Core/ObjectsContext.cpp index a9c416b..ae37c3f 100644 --- a/src/Core/ObjectsContext.cpp +++ b/src/Core/ObjectsContext.cpp @@ -8,7 +8,9 @@ ObjectsContext::ObjectsContext() : Object() {} ObjectsContext::~ObjectsContext() {} void ObjectsContext::AddObject(Object* obj) { - if (obj && std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) { + if (!obj || obj == this) return; + + if (std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) { m_objects.push_back(obj); // Connect child's update to context's update to trigger re-renders Object::connect(obj, &Object::Updated, this, &Object::Updated); diff --git a/src/Math/Assembly.cpp b/src/Math/Assembly.cpp index 3a9d816..7452a5e 100644 --- a/src/Math/Assembly.cpp +++ b/src/Math/Assembly.cpp @@ -37,27 +37,18 @@ Assembly::Assembly(const Assembly ©) m_GroupSelection(copy.m_GroupSelection) {} Assembly::~Assembly() { - for (auto const& [obj, conn] : m_ChildConnections) { - conn.disconnect(); - } - m_ChildConnections.clear(); } void Assembly::AddObject(Object *obj) { + if (!obj || obj == this) return; + if (auto *at = dynamic_cast(obj)) { at->SetParent(this); } + + // Base class already handles the list and child->parent update connection ObjectsContext::AddObject(obj); - // Connect to child updates to recompute AABB - m_ChildConnections[obj] = Object::connect(obj, &Object::Updated, [this](){ - this->ComputeBoundingBox(); - this->Updated(); // Signal that assembly itself changed (AABB-wise) - }); - - // Parent -> Child propagation for world matrix updates - Object::connect(this, &Object::Updated, obj, &Object::Updated); - this->ComputeBoundingBox(); } @@ -67,12 +58,6 @@ void Assembly::RemoveObject(Object *obj) { at->SetParent(nullptr); } - auto itConn = m_ChildConnections.find(obj); - if (itConn != m_ChildConnections.end()) { - itConn->second.disconnect(); - m_ChildConnections.erase(itConn); - } - ObjectsContext::RemoveObject(obj); this->ComputeBoundingBox(); } diff --git a/src/Math/Assembly.h b/src/Math/Assembly.h index 1f34596..1555913 100644 --- a/src/Math/Assembly.h +++ b/src/Math/Assembly.h @@ -31,6 +31,7 @@ #include "Math/Transform.h" namespace uLib { +namespace Vtk { class Assembly; } /** * @brief Assembly groups geometric objects (ContainerBox, Cylinder, etc.) @@ -46,6 +47,7 @@ namespace uLib { class Assembly : public ObjectsContext, public TRS { public: uLibTypeMacro(Assembly, ObjectsContext, TRS) + friend class Vtk::Assembly; Assembly(); @@ -112,7 +114,6 @@ private: bool m_ShowBoundingBox; bool m_GroupSelection; bool m_InUpdated = false; - std::map m_ChildConnections; ULIB_DECLARE_PROPERTIES(Assembly) }; diff --git a/src/Vtk/Math/testing/vtkAssemblyTest.cpp b/src/Vtk/Math/testing/vtkAssemblyTest.cpp index 882e8ac..3ac85c7 100644 --- a/src/Vtk/Math/testing/vtkAssemblyTest.cpp +++ b/src/Vtk/Math/testing/vtkAssemblyTest.cpp @@ -28,33 +28,33 @@ using namespace uLib; int main(int argc, char **argv) { bool interactive = (argc > 1 && std::string(argv[1]) == "-i"); - // ---- 1. Build model objects ---- - ContainerBox box1; - box1.Scale(Vector3f(1_m, 2_m, 0.5_m)); - box1.SetPosition(Vector3f(0, 0, 0)); + // ---- 1. Build model objects on the heap (expected by uLib SmartPointer) ---- + auto* box1 = new ContainerBox(); + box1->Scale(Vector3f(1, 2, 0.5)); + // box1->SetPosition(Vector3f(0, 0, 0)); - ContainerBox box2; - box2.Scale(Vector3f(0.5_m, 0.5_m, 3_m)); - box2.SetPosition(Vector3f(2_m, 0, 0)); + auto* box2 = new ContainerBox(); + box2->Scale(Vector3f(0.5, 0.5, 3)); + box2->SetPosition(Vector3f(2, 0, 0)); - Cylinder cyl(0.3_m, 1.5_m, 1); - cyl.SetPosition(Vector3f(0, 3_m, 0)); + auto* cyl = new Cylinder(1, 1.5, 1); + cyl->SetPosition(Vector3f(0, 3, 0)); // ---- 2. Create an Assembly and add objects ---- - Assembly assembly; - assembly.AddObject(&box1); - assembly.AddObject(&box2); - assembly.AddObject(&cyl); - assembly.SetShowBoundingBox(true); + auto* assembly = new Assembly(); + assembly->AddObject(box1); + assembly->AddObject(box2); + assembly->AddObject(cyl); + assembly->SetShowBoundingBox(true); // ---- 3. Apply a group transform ---- - assembly.SetPosition(Vector3f(1_m, 1_m, 0)); + // assembly->SetPosition(Vector3f(1_m, 1_m, 0)); // ---- 5. Visualize (create prop3ds to set properties) ---- - Vtk::Assembly vtkAsm(&assembly); + Vtk::Assembly vtkAsm(assembly); // Vtk::Assembly takes ownership of the model wrapper Vtk::Viewer viewer; - vtkAsm.AddToViewer(viewer); // This triggers prop3d creation via ConnectRenderer which eventually calls Prop3D::GetProp + vtkAsm.AddToViewer(viewer); // Explicitly update to ensure prop3ds exist and are added to assemblies vtkAsm.Update(); @@ -74,16 +74,16 @@ int main(int argc, char **argv) { } }; - setProps(childCtx->GetProp3D(&box1), 1.0, 0.0, 0.0); // Red - setProps(childCtx->GetProp3D(&box2), 0.0, 1.0, 0.0); // Green - setProps(childCtx->GetProp3D(&cyl), 0.0, 0.0, 1.0); // Blue + setProps(childCtx->GetProp3D(box1), 1.0, 0.0, 0.0); // Red + setProps(childCtx->GetProp3D(box2), 0.0, 1.0, 0.0); // Green + setProps(childCtx->GetProp3D(cyl), 0.0, 0.0, 1.0); // Blue } std::cout << "Prop3Ds in viewport: " << viewer.getProp3Ds().size() << " (Expected 4: 1 assembly + 3 children)" << std::endl; // ---- 4. Query the bounding box for terminal output ---- Vector3f bbMin, bbMax; - assembly.GetBoundingBox(bbMin, bbMax); + assembly->GetBoundingBox(bbMin, bbMax); std::cout << "Assembly bounding box:" << std::endl; std::cout << " min = " << bbMin.transpose() << std::endl; std::cout << " max = " << bbMax.transpose() << std::endl; diff --git a/src/Vtk/Math/vtkAssembly.cpp b/src/Vtk/Math/vtkAssembly.cpp index c67ffd8..d80aa22 100644 --- a/src/Vtk/Math/vtkAssembly.cpp +++ b/src/Vtk/Math/vtkAssembly.cpp @@ -45,15 +45,13 @@ Assembly::Assembly(uLib::Assembly *content) Assembly::~Assembly() { delete m_ChildContext; - if (m_BBoxActor) m_BBoxActor->Delete(); - if (m_VtkAsm) m_VtkAsm->Delete(); } // ------------------------------------------------------------------ // void Assembly::InstallPipe() { - // 1. Create the VTK library assembly that groups everything - m_VtkAsm = ::vtkAssembly::New(); - m_VtkAsm->PickableOff(); + // 1. Setup the internal VTK assembly + m_VtkAsm = vtkSmartPointer<::vtkAssembly>::New(); + m_BBoxActor = vtkSmartPointer<::vtkActor>::New(); this->SetProp(m_VtkAsm); // 2. Create the bounding-box wireframe actor @@ -64,7 +62,6 @@ void Assembly::InstallPipe() { vtkNew mapper; mapper->SetInputConnection(cube->GetOutputPort()); - m_BBoxActor = vtkActor::New(); m_BBoxActor->SetMapper(mapper); m_BBoxActor->GetProperty()->SetRepresentationToWireframe(); m_BBoxActor->GetProperty()->SetColor(1.0, 0.85, 0.0); // gold wireframe @@ -93,14 +90,7 @@ void Assembly::Update() { if (m_InUpdate) return; m_InUpdate = true; - if (this->m_model && m_VtkAsm) { - // Apply world matrix from the assembly content - vtkNew m; - Matrix4fToVtk(this->m_model->GetMatrix(), m); - m_VtkAsm->SetUserMatrix(m); - m_VtkAsm->Modified(); - } - + // Delegate to Prop3D to handle standard transformation application (uses GetContent()) this->Prop3D::Update(); this->UpdateBoundingBox(); if (m_ChildContext) @@ -110,25 +100,29 @@ void Assembly::Update() { void Assembly::SyncFromVtk() { if (m_InUpdate) return; - if (!this->m_model || !m_VtkAsm) return; - m_InUpdate = true; - - // VTK -> Model: Update world matrix (accounting for model parents) - if (vtkProp3D* proxy = this->GetProxyProp()) { - this->m_model->SetWorldMatrix(VtkToMatrix4f(proxy->GetUserMatrix())); - this->m_model->FromMatrix(this->m_model->GetMatrix()); - } - - this->UpdateBoundingBox(); + + // Sync the group-level transformation from VTK to the domain model + this->Prop3D::SyncFromVtk(); + + // Propagate sync to children if (m_ChildContext) m_ChildContext->SyncFromVtk(); - - this->m_model->Updated(); // Notify change in model - + m_InUpdate = false; } + +void Assembly::serialize_display(uLib::Archive::display_properties_archive &ar, const unsigned int version) { + // 1. Register base class appearance/transform + this->Prop3D::serialize_display(ar, version); + + // 2. Map domain model properties (Bounding Box visibility, etc.) + if (this->m_model) { + ar & HRP("ShowBoundingBox", m_model->m_ShowBoundingBox); + } +} + // ------------------------------------------------------------------ // void Assembly::UpdateBoundingBox() { if (!this->m_model || !m_BBoxActor) return; @@ -188,10 +182,6 @@ void Assembly::UpdateBoundingBox() { m_BBoxActor->Modified(); } -// ------------------------------------------------------------------ // -ObjectsContext *Assembly::GetChildrenContext() const { - return m_ChildContext; -} } // namespace Vtk } // namespace uLib diff --git a/src/Vtk/Math/vtkAssembly.h b/src/Vtk/Math/vtkAssembly.h index f1d8f98..3d8ecca 100644 --- a/src/Vtk/Math/vtkAssembly.h +++ b/src/Vtk/Math/vtkAssembly.h @@ -16,8 +16,9 @@ #include "Math/Assembly.h" #include "Vtk/uLibVtkInterface.h" -class vtkActor; -class vtkAssembly; // VTK library forward declaration (must be before namespace) +#include +#include +#include namespace uLib { namespace Vtk { @@ -39,33 +40,36 @@ class Assembly : public Prop3D, public uLib::ObjectWrapper { public: uLibTypeMacro(Assembly, Prop3D) - Assembly(uLib::Assembly *content); + /** + * @brief Constructor. + * @param content Pointer to the domain assembly model. + */ + explicit Assembly(uLib::Assembly *content); virtual ~Assembly(); /** @brief Updates the VTK representation from the model (model→VTK). */ virtual void Update() override; - /** @brief Synchronizes the model from the VTK representation (VTK→model). */ virtual void SyncFromVtk() override; virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_model.get(); } virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_model.get(); } - /** - * @brief Returns the prop3d managing child objects. - */ + /** @brief Returns the visualization context for children. */ + uLib::Vtk::ObjectsContext* GetChildrenContext() const { return m_ChildContext; } - /** @brief Returns the prop3d managing child objects. */ - ObjectsContext *GetChildrenContext() const; + /** @brief Property serialization for Display Properties Panel. */ + void serialize_display(Archive::display_properties_archive &ar, const unsigned int version = 0) override; private: void UpdateBoundingBox(); void InstallPipe(); - ObjectsContext *m_ChildContext; - vtkActor *m_BBoxActor; - ::vtkAssembly *m_VtkAsm; // VTK library assembly — NOT this class - bool m_InUpdate; // re-entrancy guard + vtkSmartPointer<::vtkAssembly> m_VtkAsm; + vtkSmartPointer<::vtkActor> m_BBoxActor; + + uLib::Vtk::ObjectsContext *m_ChildContext; + bool m_InUpdate; }; } // namespace Vtk diff --git a/src/Vtk/Math/vtkContainerBox.cpp b/src/Vtk/Math/vtkContainerBox.cpp index ac9bf89..e07ec20 100644 --- a/src/Vtk/Math/vtkContainerBox.cpp +++ b/src/Vtk/Math/vtkContainerBox.cpp @@ -56,7 +56,9 @@ struct ContainerBoxData { : m_Cube(vtkSmartPointer::New()), m_Axes(vtkSmartPointer::New()), m_VtkAsm(vtkSmartPointer::New()) {} - ~ContainerBoxData() {} + ~ContainerBoxData() { + m_UpdateSignal.disconnect(); + } }; ContainerBox::ContainerBox(ContainerBox::Content *content) @@ -93,27 +95,7 @@ void ContainerBox::Update() { this->Prop3D::Update(); } -void ContainerBox::SyncFromVtk() { - RecursiveMutex::ScopedLock lock(this->m_UpdateMutex); - if (!this->m_model) - return; - vtkProp3D *root = this->GetProxyProp(); - if (!root) - return; - - // VTK -> Model: Extract new world TRS from proxy, which matches the model's - // TRS center - vtkMatrix4x4 *rootMat = root->GetUserMatrix(); - Matrix4f vtkWorld = VtkToMatrix4f(rootMat); - - // Synchronize TRS property members from the updated local matrix - this->m_model->FromMatrix(vtkWorld); - - // Since we modified the model, notify observers, but block the loop back to - // VTK ConnectionBlock blocker(d->m_UpdateSignal); - this->m_model->Updated(); -} void ContainerBox::InstallPipe() { if (!this->m_model) diff --git a/src/Vtk/Math/vtkContainerBox.h b/src/Vtk/Math/vtkContainerBox.h index bc4a653..2d21683 100644 --- a/src/Vtk/Math/vtkContainerBox.h +++ b/src/Vtk/Math/vtkContainerBox.h @@ -59,7 +59,7 @@ public: /** * @brief Synchronizes the model from the VTK representation (VTK→model). */ - virtual void SyncFromVtk() override; + virtual uLib::Object *GetContent() const override { return (uLib::Object *)m_model.get(); diff --git a/src/Vtk/Math/vtkCylinder.cpp b/src/Vtk/Math/vtkCylinder.cpp index e4a7351..33d7ea9 100644 --- a/src/Vtk/Math/vtkCylinder.cpp +++ b/src/Vtk/Math/vtkCylinder.cpp @@ -44,8 +44,7 @@ Cylinder::Cylinder(Cylinder::Content *content) } Cylinder::~Cylinder() { - if (m_Actor) m_Actor->Delete(); - if (m_VtkAsm) m_VtkAsm->Delete(); + m_UpdateSignal.disconnect(); } void Cylinder::Update() { @@ -85,26 +84,13 @@ void Cylinder::Update() { this->Prop3D::Update(); } -void Cylinder::SyncFromVtk() { - if (!this->m_model) return; - - vtkProp3D* root = this->GetProxyProp(); - if (!root) return; - // VTK -> Model: Extract new world TRS from proxy - vtkMatrix4x4* rootMat = root->GetUserMatrix(); - Matrix4f vtkWorld = VtkToMatrix4f(rootMat); - - // Directly sync model from the world matrix - this->m_model->FromMatrix(vtkWorld); - this->m_model->Updated(); -} void Cylinder::InstallPipe() { if (!this->m_model) return; - m_VtkAsm = ::vtkAssembly::New(); + m_VtkAsm = vtkSmartPointer<::vtkAssembly>::New(); this->SetProp(m_VtkAsm); vtkNew cylinder; @@ -112,7 +98,7 @@ void Cylinder::InstallPipe() { cylinder->SetHeight(1.0); cylinder->SetResolution(32); - m_Actor = vtkActor::New(); + m_Actor = vtkSmartPointer::New(); vtkNew alignment; m_Actor->SetUserTransform(alignment); diff --git a/src/Vtk/Math/vtkCylinder.h b/src/Vtk/Math/vtkCylinder.h index 18401a3..840d264 100644 --- a/src/Vtk/Math/vtkCylinder.h +++ b/src/Vtk/Math/vtkCylinder.h @@ -30,6 +30,7 @@ #include "Math/Cylinder.h" #include "Vtk/uLibVtkInterface.h" #include +#include class vtkAssembly; namespace uLib { @@ -53,7 +54,7 @@ public: virtual void Update() override; /** Synchronizes the uLib model matrix with the VTK actor specifically for gizmo interactions */ - virtual void SyncFromVtk() override; + virtual uLib::Object *GetContent() const override { return (uLib::Object *)m_model.get(); @@ -63,8 +64,8 @@ protected: /** Sets up the VTK visualization pipeline */ virtual void InstallPipe(); - vtkActor *m_Actor; - ::vtkAssembly *m_VtkAsm; + vtkSmartPointer m_Actor; + vtkSmartPointer<::vtkAssembly> m_VtkAsm; uLib::Connection m_UpdateSignal; }; diff --git a/src/Vtk/Math/vtkVoxImage.cpp b/src/Vtk/Math/vtkVoxImage.cpp index 9e10f0f..edb005b 100644 --- a/src/Vtk/Math/vtkVoxImage.cpp +++ b/src/Vtk/Math/vtkVoxImage.cpp @@ -308,17 +308,7 @@ void VoxImage::serialize_display(uLib::Archive::display_properties_archive & ar, {"MIP", "Composite", "Composite Shaded", "MIP Bone", "MIP Hot", "Additive"}); } -void VoxImage::SyncFromVtk() { - if (auto *root = this->GetProxyProp()) { - vtkMatrix4x4 *rootMat = root->GetUserMatrix(); - if (rootMat) { - Matrix4f vtkLocal = VtkToMatrix4f(rootMat); - // Synchronize TRS from VTK, compensating for local volume offset - this->m_model->FromMatrix(vtkLocal); // * this->m_model->GetLocalMatrix().inverse()); - this->m_model->Updated(); - } - } -} + void VoxImage::Update() { if (auto *root = vtkProp3D::SafeDownCast(this->GetProp())) { diff --git a/src/Vtk/Math/vtkVoxImage.h b/src/Vtk/Math/vtkVoxImage.h index 7503fe3..d2402eb 100644 --- a/src/Vtk/Math/vtkVoxImage.h +++ b/src/Vtk/Math/vtkVoxImage.h @@ -77,7 +77,7 @@ public: void RescaleShaderRange(); void Update() override; - void SyncFromVtk() override; + void serialize_display(uLib::Archive::display_properties_archive &ar, const unsigned int version = 0) override; diff --git a/src/Vtk/uLibVtkInterface.cxx b/src/Vtk/uLibVtkInterface.cxx index d341063..e64ad3d 100644 --- a/src/Vtk/uLibVtkInterface.cxx +++ b/src/Vtk/uLibVtkInterface.cxx @@ -97,11 +97,24 @@ public: m_HighlightMode(Prop3D::HighlightPlain) { m_Color = Vector3d(-1, -1, -1); + m_PrevMatrix = vtkSmartPointer::New(); + m_PrevMatrix->Identity(); } ~Prop3DData() { - // No manual Delete needed for smart pointers - + if (m_Renderers) { + m_Renderers->InitTraversal(); + for (int i = 0; i < m_Renderers->GetNumberOfItems(); ++i) { + vtkRenderer* ren = m_Renderers->GetNextItem(); + if (ren) { + if (m_Prop) ren->RemoveViewProp(m_Prop); + if (m_OutlineActor) ren->RemoveActor(m_OutlineActor); + if (m_CubeAxesActor) ren->RemoveActor(m_CubeAxesActor); + if (m_HighlightActor) ren->RemoveActor(m_HighlightActor); + } + } + m_Renderers->RemoveAllItems(); + } } Prop3D *m_Prop3D; @@ -128,6 +141,8 @@ public: int m_HighlightMode; // 0: Plain, 1: Corners + vtkSmartPointer m_PrevMatrix; + // TRS m_Transform; @@ -289,6 +304,7 @@ public: vtkNew vwm; Matrix4fToVtk(tr->GetWorldMatrix(), vwm); m_HighlightActor->SetUserMatrix(vwm); + m_PrevMatrix->DeepCopy(vwm); } } } @@ -620,6 +636,7 @@ void Prop3D::ApplyProp3DTransform(vtkProp3D* prop) Matrix4fToVtk(tr->GetMatrix(), m); prop->SetUserMatrix(m); prop->Modified(); + pd->m_PrevMatrix->DeepCopy(m); } } } @@ -630,7 +647,18 @@ void Prop3D::SyncFromVtk() if (auto* tr = dynamic_cast(content)) { if (auto* proxy = this->GetProxyProp()) { if (vtkMatrix4x4* mat = proxy->GetUserMatrix()) { - tr->FromMatrix(VtkToMatrix4f(mat)); + // Calculate Delta: currentMatrix * Inv(m_PrevMatrix) + vtkNew invPrev; + vtkMatrix4x4::Invert(pd->m_PrevMatrix, invPrev); + + vtkNew delta; + vtkMatrix4x4::Multiply4x4(mat, invPrev, delta); + + // Apply delta to world matrix + Matrix4f nextWorldMatrix = VtkToMatrix4f(delta) * tr->GetWorldMatrix(); + tr->SetWorldMatrix(nextWorldMatrix); + + pd->m_PrevMatrix->DeepCopy(mat); content->Updated(); } } diff --git a/src/Vtk/vtkObjectsContext.cpp b/src/Vtk/vtkObjectsContext.cpp index 1931f77..7e8daaa 100644 --- a/src/Vtk/vtkObjectsContext.cpp +++ b/src/Vtk/vtkObjectsContext.cpp @@ -26,15 +26,17 @@ ObjectsContext::ObjectsContext(uLib::ObjectsContext *context) : m_Context(context), m_Assembly(::vtkAssembly::New()) { this->SetProp(m_Assembly); if (m_Context) { - Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this, + m_AddedConnection = Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this, &ObjectsContext::OnObjectAdded); - Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this, + m_RemovedConnection = Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this, &ObjectsContext::OnObjectRemoved); this->Synchronize(); } } ObjectsContext::~ObjectsContext() { + m_AddedConnection.disconnect(); + m_RemovedConnection.disconnect(); for (auto const &[obj, prop3d] : m_Prop3Ds) { delete prop3d; } diff --git a/src/Vtk/vtkObjectsContext.h b/src/Vtk/vtkObjectsContext.h index 7448922..769bea5 100644 --- a/src/Vtk/vtkObjectsContext.h +++ b/src/Vtk/vtkObjectsContext.h @@ -51,6 +51,8 @@ private: uLib::ObjectsContext *m_Context; std::map m_Prop3Ds; vtkAssembly *m_Assembly; + uLib::Connection m_AddedConnection; + uLib::Connection m_RemovedConnection; }; } // namespace Vtk diff --git a/src/Vtk/vtkQViewport.cpp b/src/Vtk/vtkQViewport.cpp index 83f937a..287815d 100644 --- a/src/Vtk/vtkQViewport.cpp +++ b/src/Vtk/vtkQViewport.cpp @@ -104,6 +104,9 @@ QViewport::QViewport(QWidget* parent) QViewport::~QViewport() { + if (m_VtkWidget && m_VtkWidget->renderWindow()) { + m_VtkWidget->renderWindow()->RemoveRenderer(this->GetRenderer()); + } } void QViewport::SetupPipeline()