From 22d0041942d10c336c40bafae55197c33518f1af Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Mon, 30 Mar 2026 15:24:37 +0000 Subject: [PATCH] refactor: update Puppet transform logic to support AffineTransform world matrices and improve selection highlighting --- .clangd | 4 +- .vscode/settings.json | 5 +- src/Core/testing/testing-prototype.h | 2 + src/HEP/Geant/Solid.cpp | 4 +- src/Math/Assembly.cpp | 8 +- src/Math/Assembly.h | 6 +- src/Math/ContainerBox.h | 10 +- src/Math/Cylinder.h | 7 +- src/Math/Geometry.h | 146 +++++++-- src/Math/MathRegistrations.cpp | 1 + src/Math/QuadMesh.h | 4 +- src/Math/Transform.h | 366 +++++++++++------------ src/Math/TriangleMesh.h | 4 +- src/Math/testing/GeometryTest.cpp | 25 +- src/Math/testing/testing-prototype.h | 6 +- src/Python/math_bindings.cpp | 57 +++- src/Vtk/Math/testing/testing-prototype.h | 2 + src/Vtk/Math/vtkAssembly.cpp | 11 +- src/Vtk/Math/vtkAssembly.h | 1 - src/Vtk/Math/vtkContainerBox.h | 1 - src/Vtk/testing/PuppetParentingTest.cpp | 2 +- src/Vtk/testing/testing-prototype.h | 2 + src/Vtk/uLibVtkInterface.cxx | 116 ++++--- src/Vtk/uLibVtkInterface.h | 10 +- 24 files changed, 469 insertions(+), 331 deletions(-) diff --git a/.clangd b/.clangd index 432ea6a..7dfeedf 100644 --- a/.clangd +++ b/.clangd @@ -1,7 +1,7 @@ CompileFlags: CompilationDatabase: build Add: - - -I/home/rigoni/devel/cmt/ulib/src + - -I/home/rigoni/devel/cmt/uLib/src - -isystem/home/share/micromamba/envs/mutom/include - -isystem/home/share/micromamba/envs/mutom/include/eigen3 - -isystem/home/share/micromamba/envs/mutom/targets/x86_64-linux/include @@ -27,7 +27,7 @@ Diagnostics: --- If: - PathExclude: [/home/rigoni/devel/cmt/ulib/src/.*] + PathExclude: [/home/rigoni/devel/cmt/uLib/src/.*] Diagnostics: Suppress: ["*"] diff --git a/.vscode/settings.json b/.vscode/settings.json index ff5c426..598425a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "clangd.fallbackFlags": [ - "-I/home/rigoni/devel/cmt/ulib/src", + "-I/home/rigoni/devel/cmt/uLib/src", "-isystem/home/share/micromamba/envs/mutom/include", "-isystem/home/share/micromamba/envs/mutom/include/eigen3", "-isystem/home/share/micromamba/envs/mutom/targets/x86_64-linux/include", @@ -19,8 +19,7 @@ "clangd.semanticHighlighting.enable": true, "clangd.arguments": [ "--compile-commands-dir=build", - "--query-driver=/home/share/micromamba/envs/mutom/bin/g++,/home/share/micromamba/envs/mutom/bin/gcc,/home/share/micromamba/envs/mutom/bin/nvcc", - "--suppress-system-warnings", + "--query-driver=/home/share/micromamba/envs/mutom/bin/*", "--all-scopes-completion", "--completion-style=detailed", "--header-insertion=never", diff --git a/src/Core/testing/testing-prototype.h b/src/Core/testing/testing-prototype.h index 39f4e6c..f25accf 100644 --- a/src/Core/testing/testing-prototype.h +++ b/src/Core/testing/testing-prototype.h @@ -33,5 +33,7 @@ printf("..:: Testing " #name " ::..\n"); #define TEST1(val) _fail += (val)==0 #define TEST0(val) _fail += (val)!=0 +#define ASSERT_EQUAL(a,b) if((a)!=(b)) { printf("Assertion failed: " #a " != " #b " at line %d\n", __LINE__); _fail++; } +#define ASSERT_NOT_NULL(ptr) if((ptr)==NULL) { printf("Assertion failed: " #ptr " is NULL at line %d\n", __LINE__); _fail++; } #define END_TESTING return _fail; diff --git a/src/HEP/Geant/Solid.cpp b/src/HEP/Geant/Solid.cpp index e5f5ab2..54456a3 100644 --- a/src/HEP/Geant/Solid.cpp +++ b/src/HEP/Geant/Solid.cpp @@ -85,7 +85,7 @@ void Solid::SetMaterial(G4Material *material) { } void Solid::SetTransform(Matrix4f transform) { - uLib::AffineTransform t; + uLib::AffineTransform t; t.SetMatrix(transform); // 2. Extract position and rotation for Geant4 @@ -199,7 +199,7 @@ void BoxSolid::Update() { // We must rotate the offset vector because uLib box can be rotated. Vector3f center = pos + rot * (size * 0.5); - uLib::AffineTransform t; + uLib::AffineTransform t; t.SetPosition(center); t.SetRotation(rot); diff --git a/src/Math/Assembly.cpp b/src/Math/Assembly.cpp index ed24f2b..e232e3e 100644 --- a/src/Math/Assembly.cpp +++ b/src/Math/Assembly.cpp @@ -21,7 +21,7 @@ namespace uLib { Assembly::Assembly() : ObjectsContext(), - AffineTransform(), + TRS(), m_BBoxMin(Vector3f::Zero()), m_BBoxMax(Vector3f::Zero()), m_ShowBoundingBox(false), @@ -31,7 +31,7 @@ Assembly::Assembly() Assembly::Assembly(const Assembly ©) : ObjectsContext(copy), - AffineTransform(copy), + TRS(copy), m_BBoxMin(copy.m_BBoxMin), m_BBoxMax(copy.m_BBoxMax), m_ShowBoundingBox(copy.m_ShowBoundingBox), @@ -55,6 +55,10 @@ void Assembly::AddObject(Object *obj) { 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(); } diff --git a/src/Math/Assembly.h b/src/Math/Assembly.h index a96105c..4bca2d6 100644 --- a/src/Math/Assembly.h +++ b/src/Math/Assembly.h @@ -43,9 +43,9 @@ namespace uLib { * A bounding box is automatically computed from all contained objects and * can be queried or shown/hidden through the VTK puppet. */ -class Assembly : public ObjectsContext, public AffineTransform { +class Assembly : public ObjectsContext, public TRS { public: - uLibTypeMacro(Assembly, ObjectsContext, AffineTransform) + uLibTypeMacro(Assembly, ObjectsContext, TRS) virtual const char *GetClassName() const override { return "Assembly"; } Assembly(); @@ -54,7 +54,7 @@ public: template void serialize(ArchiveT & ar, const unsigned int version) { - ar & boost::serialization::make_nvp("AffineTransform", boost::serialization::base_object(*this)); + ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object(*this)); ar & boost::serialization::make_hrp("GroupSelection", m_GroupSelection); } diff --git a/src/Math/ContainerBox.h b/src/Math/ContainerBox.h index 0e724cf..eb782fa 100644 --- a/src/Math/ContainerBox.h +++ b/src/Math/ContainerBox.h @@ -39,20 +39,21 @@ namespace uLib { * @brief Represents an oriented bounding box (OBB) within a hierarchical * transformation system. * - * ContainerBox inherits from AffineTransform, which defines its parent + * ContainerBox inherits from TRS, which defines its parent * coordinate system. It contains an internal local transformation (m_LocalT) * that defines the box's specific origin and size relative to its own * coordinate system. */ -class ContainerBox : public AffineTransform { +class ContainerBox : public TRS { public: - uLibTypeMacro(ContainerBox, AffineTransform) + uLibTypeMacro(ContainerBox, TRS) virtual const char * GetClassName() const override { return "ContainerBox"; } //////////////////////////////////////////////////////////////////////////// // PROPERTIES // + Vector3f Size; Vector3f Origin; @@ -86,7 +87,7 @@ public: */ ContainerBox(const ContainerBox ©) : m_LocalT(this), // Reset parent to the new object - AffineTransform(copy), + TRS(copy), Size(copy.Size), Origin(copy.Origin) { ULIB_ACTIVATE_PROPERTIES(*this); @@ -98,6 +99,7 @@ public: */ template void serialize(ArchiveT & ar, const unsigned int version) { + ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object(*this)); ar & HRP(Size); ar & HRP(Origin); } diff --git a/src/Math/Cylinder.h b/src/Math/Cylinder.h index 7ba88fa..aa1e7c7 100644 --- a/src/Math/Cylinder.h +++ b/src/Math/Cylinder.h @@ -39,10 +39,10 @@ namespace uLib { * The cylinder orientation is defined by the Axis property (0=X, 1=Y, 2=Z). * By default, it is aligned with the Y axis (Axis=1). */ -class Cylinder : public AffineTransform { +class Cylinder : public TRS { public: - uLibTypeMacro(Cylinder, AffineTransform) + uLibTypeMacro(Cylinder, TRS) /** * @brief PROPERTIES @@ -74,7 +74,7 @@ public: * @brief Copy constructor. */ Cylinder(const Cylinder ©) - : m_LocalT(this), AffineTransform(copy), Radius(copy.Radius), Height(copy.Height), Axis(copy.Axis) { + : m_LocalT(this), TRS(copy), Radius(copy.Radius), Height(copy.Height), Axis(copy.Axis) { ULIB_ACTIVATE_PROPERTIES(*this); this->Sync(); } @@ -84,6 +84,7 @@ public: */ template void serialize(ArchiveT & ar, const unsigned int version) { + ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object(*this)); ar & HRP(Radius); ar & HRP(Height); ar & HRP(Axis); diff --git a/src/Math/Geometry.h b/src/Math/Geometry.h index 5322b68..7d29ec7 100644 --- a/src/Math/Geometry.h +++ b/src/Math/Geometry.h @@ -35,12 +35,22 @@ namespace uLib { -class Geometry : public AffineTransform { + +class Geometry : virtual public Object { +protected: + Geometry* m_Parent = nullptr; + public: - uLibTypeMacro(Geometry, AffineTransform) + uLibTypeMacro(Geometry, Object) virtual const char * GetClassName() const override { return "Geometry"; } + virtual void SetParent(Geometry* p) { m_Parent = p; } + virtual Geometry* GetParent() const { return m_Parent; } + + virtual bool IsLinear() const { return false; } + virtual bool IsPure() const { return false; } + virtual Vector3f ToLinear(const Vector3f& curved_space) const { return curved_space; } @@ -49,38 +59,120 @@ public: return cartesian_space; } - inline Vector4f GetWorldPoint(const Vector4f v) const { - Vector3f lin = ToLinear(Vector3f(v.x(), v.y(), v.z())); - return this->GetWorldMatrix() * Vector4f(lin.x(), lin.y(), lin.z(), v.w()); + virtual Vector4f GetWorldPoint(const Vector4f v) const = 0; + virtual Vector4f GetLocalPoint(const Vector4f v) const = 0; + + virtual Vector4f GetWorldPoint(const float x, const float y, const float z) const { + return GetWorldPoint(Vector4f(x,y,z,1)); } - inline Vector4f GetWorldPoint(const float x, const float y, const float z) { - return this->GetWorldPoint(Vector4f(x,y,z,1)); + virtual Vector4f GetLocalPoint(const float x, const float y, const float z) const { + return GetLocalPoint(Vector4f(x,y,z,1)); } - inline Vector4f GetLocalPoint(const Vector4f v) const { - Vector4f loc_lin = this->GetWorldMatrix().inverse() * v; - Vector3f curv = FromLinear(Vector3f(loc_lin.x(), loc_lin.y(), loc_lin.z())); - return Vector4f(curv.x(), curv.y(), curv.z(), loc_lin.w()); + virtual Vector4f GetWorldPoint(const Vector3f v) const { + return GetWorldPoint(Vector4f(v.x(), v.y(), v.z(), 1.0f)); } - inline Vector4f GetLocalPoint(const float x, const float y, const float z) { - return this->GetLocalPoint(Vector4f(x,y,z,1)); + virtual Vector4f GetLocalPoint(const Vector3f v) const { + return GetLocalPoint(Vector4f(v.x(), v.y(), v.z(), 1.0f)); } + + virtual void Translate(Vector3f t) = 0; + virtual void Rotate(Vector3f r) = 0; + virtual void Scale(Vector3f s) = 0; + }; -class CylindricalGeometry : public Geometry { + + +class LinearGeometry : public Geometry { +protected: + Affine3f m_T = Affine3f::Identity(); + public: - uLibTypeMacro(CylindricalGeometry, Geometry) + uLibTypeMacro(LinearGeometry, Geometry) + + virtual const char * GetClassName() const override { return "LinearGeometry"; } + + virtual bool IsLinear() const override { return true; } + virtual bool IsPure() const override { return true; } + + virtual Vector4f GetWorldPoint(const Vector4f v) const override { + Vector3f lin_v = ToLinear(v.head<3>()); + Vector4f v_lin(lin_v.x(), lin_v.y(), lin_v.z(), v.w()); + + Affine3f combined = m_T; + const Geometry* curr = m_Parent; + while (curr && curr->IsLinear() && curr->IsPure()) { + combined = static_cast(curr)->m_T * combined; + curr = curr->GetParent(); + } + + Vector4f v_res = combined.matrix() * v_lin; + if (curr) return curr->GetWorldPoint(v_res); + return v_res; + } + + virtual Vector4f GetLocalPoint(const Vector4f v) const override { + Vector4f v_parent = m_Parent ? m_Parent->GetLocalPoint(v) : v; + Vector4f v_loc_lin = m_T.inverse().matrix() * v_parent; + Vector3f v_curv = FromLinear(v_loc_lin.head<3>()); + return Vector4f(v_curv.x(), v_curv.y(), v_curv.z(), v_loc_lin.w()); + } + + virtual void Translate(Vector3f t) override { + m_T.translate(t); + } + + virtual void Rotate(Vector3f r) override { + this->EulerYZYRotate(r); + } + + virtual void Scale(Vector3f s) override { + m_T.scale(s); + } + + void SetPosition(const Vector3f& v) { m_T.translation() = v; } + Vector3f GetPosition() const { return m_T.translation(); } + + void EulerYZYRotate(const Vector3f& e) { + Matrix3f mat; + mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY()) + * Eigen::AngleAxisf(e.y(), Vector3f::UnitZ()) + * Eigen::AngleAxisf(e.z(), Vector3f::UnitY()); + m_T.rotate(mat); + } + + void FlipAxes(int first, int second) { + Matrix3f mat = Matrix3f::Identity(); + mat.col(first).swap(mat.col(second)); + m_T.rotate(mat); + } + + const Affine3f& GetTransform() const { return m_T; } + void SetTransform(const Affine3f& t) { m_T = t; } +}; + + + + +class CylindricalGeometry : public LinearGeometry { +public: + uLibTypeMacro(CylindricalGeometry, LinearGeometry) CylindricalGeometry() {} - Vector3f ToLinear(const Vector3f& cylindrical) const { + virtual const char * GetClassName() const override { return "CylindricalGeometry"; } + + virtual bool IsPure() const override { return false; } + + Vector3f ToLinear(const Vector3f& cylindrical) const override { return Vector3f(cylindrical.x() * std::cos(cylindrical.y()), cylindrical.x() * std::sin(cylindrical.y()), cylindrical.z()); } - Vector3f FromLinear(const Vector3f& linear) const { + Vector3f FromLinear(const Vector3f& linear) const override { float r = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y()); float phi = std::atan2(linear.y(), linear.x()); return Vector3f(r, phi, linear.z()); @@ -88,14 +180,16 @@ public: }; -class SphericalGeometry : public Geometry { +class SphericalGeometry : public LinearGeometry { public: - uLibTypeMacro(SphericalGeometry, Geometry) + uLibTypeMacro(SphericalGeometry, LinearGeometry) SphericalGeometry() {} virtual const char * GetClassName() const override { return "SphericalGeometry"; } - Vector3f ToLinear(const Vector3f& spherical) const { + virtual bool IsPure() const override { return false; } + + Vector3f ToLinear(const Vector3f& spherical) const override { float r = spherical.x(); float theta = spherical.y(); float phi = spherical.z(); @@ -104,7 +198,7 @@ public: r * std::cos(theta)); } - Vector3f FromLinear(const Vector3f& linear) const { + Vector3f FromLinear(const Vector3f& linear) const override { float r = linear.norm(); float theta = (r == 0.0f) ? 0.0f : std::acos(linear.z() / r); float phi = std::atan2(linear.y(), linear.x()); @@ -113,14 +207,16 @@ public: }; -class ToroidalGeometry : public Geometry { +class ToroidalGeometry : public LinearGeometry { public: - uLibTypeMacro(ToroidalGeometry, Geometry) + uLibTypeMacro(ToroidalGeometry, LinearGeometry) ToroidalGeometry(float Rtor) : m_Rtor(Rtor) {} virtual const char * GetClassName() const override { return "ToroidalGeometry"; } - Vector3f ToLinear(const Vector3f& toroidal) const { + virtual bool IsPure() const override { return false; } + + Vector3f ToLinear(const Vector3f& toroidal) const override { float r = toroidal.x(); float theta = toroidal.y(); float phi = toroidal.z(); @@ -129,7 +225,7 @@ public: r * std::sin(theta)); } - Vector3f FromLinear(const Vector3f& linear) const { + Vector3f FromLinear(const Vector3f& linear) const override { float phi = std::atan2(linear.y(), linear.x()); float r_xy = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y()); float delta_r = r_xy - m_Rtor; diff --git a/src/Math/MathRegistrations.cpp b/src/Math/MathRegistrations.cpp index 53d0903..86056fc 100644 --- a/src/Math/MathRegistrations.cpp +++ b/src/Math/MathRegistrations.cpp @@ -10,6 +10,7 @@ namespace uLib { +ULIB_REGISTER_OBJECT(TRS) ULIB_REGISTER_OBJECT(ContainerBox) ULIB_REGISTER_OBJECT(Cylinder) ULIB_REGISTER_OBJECT(Assembly) diff --git a/src/Math/QuadMesh.h b/src/Math/QuadMesh.h index 7652c0f..8361f70 100644 --- a/src/Math/QuadMesh.h +++ b/src/Math/QuadMesh.h @@ -34,10 +34,10 @@ namespace uLib { -class QuadMesh : public AffineTransform +class QuadMesh : public TRS { public: - uLibTypeMacro(QuadMesh, AffineTransform) + uLibTypeMacro(QuadMesh, TRS) virtual const char * GetClassName() const override { return "QuadMesh"; } diff --git a/src/Math/Transform.h b/src/Math/Transform.h index bf92041..9f66e43 100644 --- a/src/Math/Transform.h +++ b/src/Math/Transform.h @@ -57,21 +57,150 @@ namespace uLib { + +using Eigen::Isometry3f; +using Eigen::Isometry3d; + +using Eigen::Affine3f; +using Eigen::Affine3d; + +using Eigen::Projective3f; +using Eigen::Projective3d; + + + + + + + +//////////////////////////////////////////////////////////////////////////////// +///////// AFFINE TRANSFORM WRAPPER ////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + + +class AffineTransform : virtual public Object { +public: + uLibTypeMacro(AffineTransform, Object) +protected: + + Affine3f m_T; + AffineTransform *m_Parent; + +public: + AffineTransform() : + m_T(Matrix4f::Identity()), + m_Parent(NULL) + {} + + AffineTransform(AffineTransform *parent) : + m_T(Matrix4f::Identity()), + m_Parent(parent) + {} + + AffineTransform(const AffineTransform ©) : + m_T(copy.m_T), + m_Parent(copy.m_Parent) + {} + + Affine3f& GetTransform() { return m_T; } + + AffineTransform *GetParent() const { return this->m_Parent; } + + void SetParent(AffineTransform *name) { this->m_Parent = name; } + + void SetMatrix (const Matrix4f &mat) { m_T.matrix() = mat; } + Matrix4f& GetMatrix () { return m_T.matrix(); } + const Matrix4f& GetMatrix () const { return m_T.matrix(); } + + Matrix4f GetWorldMatrix() const + { + if(!m_Parent) return m_T.matrix(); + else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A // + } + + void SetWorldMatrix(const Matrix4f &mat) + { + if(!m_Parent) m_T.matrix() = mat; + else m_T.matrix() = m_Parent->GetWorldMatrix().inverse() * mat; + } + + void SetPosition(const Vector3f &v) { this->m_T.translation() = v; } + + Vector3f GetPosition() const { return this->m_T.translation(); } + + void SetRotation(const Matrix3f &m) { this->m_T.linear() = m; } + + Matrix3f GetRotation() const { return this->m_T.rotation(); } + + void Translate(const Vector3f &v) { this->m_T.translate(v); } + + void Scale(const Vector3f &v) { this->m_T.scale(v); } + + Vector3f GetScale() const { + return Vector3f(this->m_T.linear().col(0).norm(), + this->m_T.linear().col(1).norm(), + this->m_T.linear().col(2).norm()); + } + + + void Rotate(const Matrix3f &m) { this->m_T.rotate(m); } + + void Rotate(const float angle, Vector3f axis) + { + axis.normalize(); // prehaps not necessary ( see eigens ) + Eigen::AngleAxisf ax(angle,axis); + this->m_T.rotate(Eigen::Quaternion(ax)); + } + + void Rotate(const Vector3f euler_axis) { + float angle = euler_axis.norm(); + Rotate(angle,euler_axis); + } + + void PreRotate(const Matrix3f &m) { this->m_T.prerotate(m); } + + void QuaternionRotate(const Vector4f &q) + { this->m_T.rotate(Eigen::Quaternion(q)); } + + void EulerYZYRotate(const Vector3f &e) { + Matrix3f mat; + mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY()) + * Eigen::AngleAxisf(e.y(), Vector3f::UnitZ()) + * Eigen::AngleAxisf(e.z(), Vector3f::UnitY()); + m_T.rotate(mat); + } + + void FlipAxes(int first, int second) + { + Matrix3f mat = Matrix3f::Identity(); + mat.col(first).swap(mat.col(second)); + m_T.rotate(mat); + } +}; + + + //////////////////////////////////////////////////////////////////////////////// ///////// TRS PARAMETERS ///////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// typedef Eigen::Affine3f AffineMatrix; -class TRS { +class TRS : public AffineTransform { + public: + uLibTypeMacro(TRS, AffineTransform) + Vector3f position = Vector3f::Zero(); Vector3f rotation = Vector3f::Zero(); Vector3f scaling = Vector3f::Ones(); TRS() = default; - TRS(const class AffineTransform& at); + TRS(const class AffineTransform& at) { + this->FromMatrix(at.GetMatrix()); + } TRS(const Matrix4f& mat) { this->FromMatrix(mat); @@ -90,221 +219,60 @@ public: if (this->scaling(1) > 1e-6) rot.col(1) /= this->scaling(1); if (this->scaling(2) > 1e-6) rot.col(2) /= this->scaling(2); - // Decompose to Euler angles matching VTK (M = Rz * Ry * Rx) - // Store internally as RADIANS (standard for uLib properties) Vector3f euler = rot.eulerAngles(2, 1, 0); this->rotation = Vector3f(euler(2), euler(1), euler(0)); + + this->SetMatrix(mat); + } + + void SetPosition(const Vector3f &v) { + position = v; + this->AffineTransform::SetPosition(v); + } + + void SetRotation(const Vector3f &v) { + rotation = v; + this->SyncMatrix(); + } + + void SetOrientation(const Vector3f &v) { SetRotation(v); } + + void SetScale(const Vector3f &v) { + scaling = v; + this->SyncMatrix(); + } + + void SyncMatrix() { + this->GetTransform() = GetAffineMatrix(); } template void serialize(ArchiveT & ar, const unsigned int version) { ar & HRPU(position, "mm"); - ar & HRPU(rotation, "deg"); // Metadata informs UI to convert to/from degrees + ar & HRPU(rotation, "rad"); ar & HRP(scaling); } AffineMatrix GetAffineMatrix() const { - AffineMatrix t = AffineMatrix::Identity(); - t.translate(position); - - // rotation is in Radians here - t.rotate(Eigen::AngleAxisf(rotation.z(), Vector3f::UnitZ())); - t.rotate(Eigen::AngleAxisf(rotation.y(), Vector3f::UnitY())); - t.rotate(Eigen::AngleAxisf(rotation.x(), Vector3f::UnitX())); - - t.scale(scaling); - return t; + AffineMatrix m = AffineMatrix::Identity(); + m.translate(position); + m.rotate(Eigen::AngleAxisf(rotation.z(), Vector3f::UnitZ())); + m.rotate(Eigen::AngleAxisf(rotation.y(), Vector3f::UnitY())); + m.rotate(Eigen::AngleAxisf(rotation.x(), Vector3f::UnitX())); + m.scale(scaling); + return m; + } + + Matrix4f GetMatrix() const { + return this->GetAffineMatrix().matrix(); } }; -//////////////////////////////////////////////////////////////////////////////// -///////// AFFINE TRANSFORM WRAPPER ////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -class AffineTransform : virtual public Object { -public: - uLibTypeMacro(AffineTransform, Object) - - TRS Transform; - -private: - void NotifyProperties() { - PropertyBase *p; - if ((p = this->GetProperty("Transform.position"))) p->Updated(); - if ((p = this->GetProperty("Transform.rotation"))) p->Updated(); - if ((p = this->GetProperty("Transform.scaling"))) p->Updated(); - } - -protected: - Eigen::Affine3f m_T; - AffineTransform *m_Parent; - -public: - AffineTransform() : - m_T(Matrix4f::Identity()), - m_Parent(NULL) - { - ULIB_ACTIVATE_PROPERTIES(*this); - this->Sync(); - } - - virtual ~AffineTransform() {} - - AffineTransform(AffineTransform *parent) : - m_T(Matrix4f::Identity()), - m_Parent(parent) - { - ULIB_ACTIVATE_PROPERTIES(*this); - this->Sync(); - } - - AffineTransform(const AffineTransform ©) : - m_T(copy.m_T), - m_Parent(copy.m_Parent), - Transform(copy.Transform) - { - ULIB_ACTIVATE_PROPERTIES(*this); - this->Sync(); - } - - /** - * @brief Registration of properties in groups. - */ - template - void serialize(ArchiveT & ar, const unsigned int version) { - ar & boost::serialization::make_nvp("Transform", Transform); - } - - Eigen::Affine3f& GetTransform() { return m_T; } - - AffineTransform *GetParent() const { return this->m_Parent; } - - void SetParent(AffineTransform *name) { this->m_Parent = name; } - - void SetMatrix (Matrix4f mat) { - m_T.matrix() = mat; - this->UpdatePropertiesFromMatrix(); - } - - Matrix4f GetMatrix() const { return m_T.matrix(); } - - Matrix4f GetWorldMatrix() const - { - if(!m_Parent) return m_T.matrix(); - else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A // - } - - void SetPosition(const Vector3f v) { - this->Transform.position = v; - this->Updated(); - this->NotifyProperties(); - } - Vector3f GetPosition() const { return this->Transform.position; } - - void SetOrientation(const Vector3f v) { - this->Transform.rotation = v; - this->Updated(); - this->NotifyProperties(); - } - Vector3f GetOrientation() const { return this->Transform.rotation; } - - void SetScale(const Vector3f v) { - this->Transform.scaling = v; - this->Updated(); - this->NotifyProperties(); - } - Vector3f GetScale() const { return this->Transform.scaling; } - - void SetRotation(const Matrix3f m) { - this->m_T.linear() = m; - this->UpdatePropertiesFromMatrix(); - } - - Matrix3f GetRotation() const { return this->m_T.rotation(); } - - void Translate(const Vector3f v) { - this->Transform.position += v; - this->Sync(); - } - - void Scale(const Vector3f v) { - this->Transform.scaling = this->Transform.scaling.cwiseProduct(v); - this->Sync(); - } - - - void Rotate(const Matrix3f m) { - this->m_T.rotate(m); - this->UpdatePropertiesFromMatrix(); - } - - void Rotate(const float angle, Vector3f axis) - { - axis.normalize(); - Eigen::AngleAxisf ax(angle,axis); - this->m_T.rotate(Eigen::Quaternion(ax)); - this->UpdatePropertiesFromMatrix(); - } - - void Rotate(const Vector3f euler_axis) { - float angle = euler_axis.norm(); - Rotate(angle,euler_axis); - } - - void PreRotate(const Matrix3f m) { this->m_T.prerotate(m); this->UpdatePropertiesFromMatrix(); } - - void QuaternionRotate(const Vector4f q) - { this->m_T.rotate(Eigen::Quaternion(q)); this->UpdatePropertiesFromMatrix(); } - - void EulerYZYRotate(const Vector3f e) { - this->Transform.rotation = e; - this->Sync(); - } - - void FlipAxes(int first, int second) - { - Matrix3f mat = Matrix3f::Identity(); - mat.col(first).swap(mat.col(second)); - m_T.rotate(mat); - this->UpdatePropertiesFromMatrix(); - } - - /** - * @brief Decomposes the internal matrix m_T back into Position, Orientation, and Scale properties. - */ - void UpdatePropertiesFromMatrix() { - this->Transform.FromMatrix(this->GetMatrix()); - - PropertyBase *p; - if ((p = this->GetProperty("Transform.position"))) p->Updated(); - if ((p = this->GetProperty("Transform.rotation"))) p->Updated(); - if ((p = this->GetProperty("Transform.scaling"))) p->Updated(); - } - -signals: - /** Signal emitted when properties change */ - virtual void Updated() override { - this->Sync(); - ULIB_SIGNAL_EMIT(Object::Updated); - } - -private: - void Sync() { - m_T.matrix() = this->Transform.GetAffineMatrix().matrix(); - } -}; - -inline TRS::TRS(const AffineTransform& at) { - this->position = at.GetPosition(); - this->rotation = at.GetOrientation(); - this->scaling = at.GetScale(); -} - -} +} // uLib diff --git a/src/Math/TriangleMesh.h b/src/Math/TriangleMesh.h index 94f5582..590fdf3 100644 --- a/src/Math/TriangleMesh.h +++ b/src/Math/TriangleMesh.h @@ -37,10 +37,10 @@ namespace uLib { -class TriangleMesh : public AffineTransform +class TriangleMesh : public TRS { public: - uLibTypeMacro(TriangleMesh, AffineTransform) + uLibTypeMacro(TriangleMesh, TRS) virtual const char * GetClassName() const override { return "TriangleMesh"; } diff --git a/src/Math/testing/GeometryTest.cpp b/src/Math/testing/GeometryTest.cpp index 075e750..fc0c371 100644 --- a/src/Math/testing/GeometryTest.cpp +++ b/src/Math/testing/GeometryTest.cpp @@ -53,7 +53,7 @@ int main() ///////////////// GEOMETRY TESTING /////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// - Geometry Geo; + LinearGeometry Geo; Geo.SetPosition(Vector3f(1,1,1)); Geo.EulerYZYRotate(Vector3f(0,0,0)); @@ -77,7 +77,7 @@ int main() Geo.EulerYZYRotate(Vector3f(0,0,M_PI_2)); wp = Geo.GetWorldPoint(HPoint3f(1,1,1)); - // std::cout << "Geometry matrix\n" << Geo.GetTransform() << "\n"; + // std::cout << "Geometry matrix\n" << Geo.GetTransform().matrix() << "\n"; // std::cout << "World 1,1,1 coords\n" << wp << "\n"; TEST0( Vector4f0(wp - HPoint3f(0,2,2)) ); @@ -122,6 +122,27 @@ int main() TEST0( Vector4f0(recovered.homogeneous() - tor_pt.homogeneous()) ); } + // PARENT GEOMETRY TESTING + { + LinearGeometry parent; + parent.Translate(Vector3f(10, 0, 0)); + + LinearGeometry child; + child.SetParent(&parent); + child.Translate(Vector3f(0, 5, 0)); + + HPoint3f wp = child.GetWorldPoint(HPoint3f(1, 1, 1)); + TEST0( Vector4f0(wp - HPoint3f(11, 6, 1)) ); + + CylindricalGeometry cparent; + LinearGeometry grandchild; + grandchild.SetParent(&cparent); + grandchild.Translate(Vector3f(1, 0, 0)); + + HPoint3f gp = grandchild.GetWorldPoint(HPoint3f(1, M_PI_2, 0)); + TEST0( Vector4f0(gp - HPoint3f(0, 2, 0)) ); + } + END_TESTING; } diff --git a/src/Math/testing/testing-prototype.h b/src/Math/testing/testing-prototype.h index 2e46ed8..1d30f7b 100644 --- a/src/Math/testing/testing-prototype.h +++ b/src/Math/testing/testing-prototype.h @@ -31,8 +31,10 @@ static int _fail = 0; \ printf("..:: Testing " #name " ::..\n"); -#define TEST1(val) if ((val)==0) { printf("Assertion failed: %s != 0\n", #val); _fail++; } -#define TEST0(val) if ((val)!=0) { printf("Assertion failed: %s != 0\n", #val); _fail++; } +#define TEST1(val) _fail += (val)==0 +#define TEST0(val) _fail += (val)!=0 +#define ASSERT_EQUAL(a,b) if((a)!=(b)) { printf("Assertion failed: " #a " != " #b " at line %d\n", __LINE__); _fail++; } +#define ASSERT_NOT_NULL(ptr) if((ptr)==NULL) { printf("Assertion failed: " #ptr " is NULL at line %d\n", __LINE__); _fail++; } #define END_TESTING return _fail; #define ASSERT_EQ(a, b) if ((a) != (b)) { printf("Assertion failed: %s != %s\n", #a, #b); _fail++; } \ No newline at end of file diff --git a/src/Python/math_bindings.cpp b/src/Python/math_bindings.cpp index 9c6e6fe..2d08398 100644 --- a/src/Python/math_bindings.cpp +++ b/src/Python/math_bindings.cpp @@ -162,6 +162,12 @@ void init_math(py::module_ &m) { .def_readwrite("direction_error", &HError3f::direction_error); // 3. Dynamic Vectors (uLib::Vector) + py::class_>(m, "TRS") + .def(py::init<>()) + .def_readwrite("position", &TRS::position) + .def_readwrite("rotation", &TRS::rotation) + .def_readwrite("scaling", &TRS::scaling); + py::bind_vector>(m, "Vector_i") .def("MoveToVRAM", &uLib::Vector::MoveToVRAM) .def("MoveToRAM", &uLib::Vector::MoveToRAM); @@ -268,7 +274,7 @@ void init_math(py::module_ &m) { // 5. Core Math Structures py::class_>(m, - "AffineTransform") + "AffineTransform") .def(py::init<>()) .def("GetWorldMatrix", &AffineTransform::GetWorldMatrix) .def("SetPosition", &AffineTransform::SetPosition) @@ -278,23 +284,46 @@ void init_math(py::module_ &m) { .def("SetRotation", &AffineTransform::SetRotation) .def("GetRotation", &AffineTransform::GetRotation) .def("Rotate", - py::overload_cast(&AffineTransform::Rotate)) + py::overload_cast(&AffineTransform::Rotate)) .def("Rotate", py::overload_cast(&AffineTransform::Rotate)) .def("Rotate", py::overload_cast(&AffineTransform::Rotate)) .def("EulerYZYRotate", &AffineTransform::EulerYZYRotate) - .def("FlipAxes", &AffineTransform::FlipAxes); + .def("FlipAxes", &AffineTransform::FlipAxes) + .def("SetWorldMatrix", &AffineTransform::SetWorldMatrix); - py::class_>(m, "Geometry") + py::class_>(m, "TRS") .def(py::init<>()) - .def("GetWorldPoint", py::overload_cast( - &Geometry::GetWorldPoint, py::const_)) - .def("GetWorldPoint", - py::overload_cast(&Geometry::GetWorldPoint)) - .def("GetLocalPoint", py::overload_cast( - &Geometry::GetLocalPoint, py::const_)) - .def("GetLocalPoint", - py::overload_cast(&Geometry::GetLocalPoint)); + .def(py::init()) + .def_readwrite("position", &TRS::position) + .def_readwrite("rotation", &TRS::rotation) + .def_readwrite("scaling", &TRS::scaling) + .def("SetPosition", &TRS::SetPosition) + .def("SetRotation", &TRS::SetRotation) + .def("SetOrientation", &TRS::SetOrientation) + .def("SetScale", &TRS::SetScale) + .def("FromMatrix", &TRS::FromMatrix) + .def("GetMatrix", &TRS::GetMatrix); + + py::class_>(m, "Geometry") + .def("GetParent", &Geometry::GetParent) + .def("SetParent", &Geometry::SetParent) + .def("GetWorldPoint", py::overload_cast(&Geometry::GetWorldPoint, py::const_)) + .def("GetWorldPoint", py::overload_cast(&Geometry::GetWorldPoint, py::const_)) + .def("GetLocalPoint", py::overload_cast(&Geometry::GetLocalPoint, py::const_)) + .def("GetLocalPoint", py::overload_cast(&Geometry::GetLocalPoint, py::const_)); + + py::class_>(m, "LinearGeometry") + .def(py::init<>()) + .def("Translate", &LinearGeometry::Translate) + .def("Rotate", &LinearGeometry::Rotate) + .def("Scale", &LinearGeometry::Scale) + .def("SetPosition", &LinearGeometry::SetPosition) + .def("GetPosition", &LinearGeometry::GetPosition) + .def("EulerYZYRotate", &LinearGeometry::EulerYZYRotate) + .def("FlipAxes", &LinearGeometry::FlipAxes) + .def("GetTransform", &LinearGeometry::GetTransform) + .def("SetTransform", &LinearGeometry::SetTransform); py::class_>( m, "ContainerBox") @@ -427,7 +456,7 @@ void init_math(py::module_ &m) { .def("__getitem__", py::overload_cast(&VoxImage::operator[])); - py::class_(m, "TriangleMesh") + py::class_>(m, "TriangleMesh") .def(py::init<>()) .def("AddPoint", &TriangleMesh::AddPoint) .def("AddTriangle", @@ -439,7 +468,7 @@ void init_math(py::module_ &m) { .def("GetTriangle", &TriangleMesh::GetTriangle) .def("GetNormal", &TriangleMesh::GetNormal); - py::class_(m, "QuadMesh") + py::class_>(m, "QuadMesh") .def(py::init<>()) .def("AddPoint", &QuadMesh::AddPoint) .def("AddQuad", diff --git a/src/Vtk/Math/testing/testing-prototype.h b/src/Vtk/Math/testing/testing-prototype.h index c801261..b3eb885 100644 --- a/src/Vtk/Math/testing/testing-prototype.h +++ b/src/Vtk/Math/testing/testing-prototype.h @@ -36,6 +36,8 @@ printf("..:: Testing " #name " ::..\n"); #define TEST1(val) _fail += (val)==0 #define TEST0(val) _fail += (val)!=0 +#define ASSERT_EQUAL(a,b) if((a)!=(b)) { printf("Assertion failed: " #a " != " #b " at line %d\n", __LINE__); _fail++; } +#define ASSERT_NOT_NULL(ptr) if((ptr)==NULL) { printf("Assertion failed: " #ptr " is NULL at line %d\n", __LINE__); _fail++; } #define END_TESTING return _fail; diff --git a/src/Vtk/Math/vtkAssembly.cpp b/src/Vtk/Math/vtkAssembly.cpp index 55c7b71..a699c2f 100644 --- a/src/Vtk/Math/vtkAssembly.cpp +++ b/src/Vtk/Math/vtkAssembly.cpp @@ -35,8 +35,7 @@ Assembly::Assembly(uLib::Assembly *content) m_ChildContext(nullptr), m_BBoxActor(nullptr), m_VtkAsm(nullptr), - m_InUpdate(false), - m_BlockUpdate(false) { + m_InUpdate(false) { this->InstallPipe(); if (m_Content) { Object::connect(m_Content, &uLib::Assembly::Updated, @@ -54,6 +53,7 @@ Assembly::~Assembly() { void Assembly::InstallPipe() { // 1. Create the VTK library assembly that groups everything m_VtkAsm = ::vtkAssembly::New(); + m_VtkAsm->PickableOff(); this->SetProp(m_VtkAsm); // 2. Create the bounding-box wireframe actor @@ -78,10 +78,10 @@ void Assembly::InstallPipe() { // 3. Build a child-objects context (auto-creates puppets for each child) if (m_Content) { m_ChildContext = new vtkObjectsContext(m_Content); - // The vtkObjectsContext's own prop is already a ::vtkAssembly; - // nest it inside ours so everything moves together. - if (auto *childProp = vtkProp3D::SafeDownCast(m_ChildContext->GetProp())) + // Link the children context's assembly into our group assembly + if (auto* childProp = vtkProp3D::SafeDownCast(m_ChildContext->GetProp())) { m_VtkAsm->AddPart(childProp); + } } // 4. Apply initial transform @@ -93,7 +93,6 @@ void Assembly::InstallPipe() { void Assembly::contentUpdate() { if (m_InUpdate) return; m_InUpdate = true; - m_BlockUpdate = false; this->UpdateTransform(); this->UpdateBoundingBox(); if (m_ChildContext) diff --git a/src/Vtk/Math/vtkAssembly.h b/src/Vtk/Math/vtkAssembly.h index 17defa9..5ad2960 100644 --- a/src/Vtk/Math/vtkAssembly.h +++ b/src/Vtk/Math/vtkAssembly.h @@ -66,7 +66,6 @@ private: vtkActor *m_BBoxActor; ::vtkAssembly *m_VtkAsm; // VTK library assembly — NOT this class bool m_InUpdate; // re-entrancy guard - bool m_BlockUpdate; // block feedback from contentUpdate→Update }; } // namespace Vtk diff --git a/src/Vtk/Math/vtkContainerBox.h b/src/Vtk/Math/vtkContainerBox.h index 6fe0873..3dc2d68 100644 --- a/src/Vtk/Math/vtkContainerBox.h +++ b/src/Vtk/Math/vtkContainerBox.h @@ -29,7 +29,6 @@ #include "Math/ContainerBox.h" #include "uLibVtkInterface.h" #include "vtkPolydata.h" -#include class vtkActor; diff --git a/src/Vtk/testing/PuppetParentingTest.cpp b/src/Vtk/testing/PuppetParentingTest.cpp index b9136df..ca84e7c 100644 --- a/src/Vtk/testing/PuppetParentingTest.cpp +++ b/src/Vtk/testing/PuppetParentingTest.cpp @@ -64,7 +64,7 @@ int main() { // 3. Move the parent and verify the child follows assembly->SetPosition(Vector3f(100, 0, 0)); - assembly->Update(); + assembly->Updated(); // In VTK assemblies, the child's absolute matrix should reflect the parent's transform vtkProp3D* box1Prop = vtkProp3D::SafeDownCast(box1Puppet->GetProp()); diff --git a/src/Vtk/testing/testing-prototype.h b/src/Vtk/testing/testing-prototype.h index c801261..b3eb885 100644 --- a/src/Vtk/testing/testing-prototype.h +++ b/src/Vtk/testing/testing-prototype.h @@ -36,6 +36,8 @@ printf("..:: Testing " #name " ::..\n"); #define TEST1(val) _fail += (val)==0 #define TEST0(val) _fail += (val)!=0 +#define ASSERT_EQUAL(a,b) if((a)!=(b)) { printf("Assertion failed: " #a " != " #b " at line %d\n", __LINE__); _fail++; } +#define ASSERT_NOT_NULL(ptr) if((ptr)==NULL) { printf("Assertion failed: " #ptr " is NULL at line %d\n", __LINE__); _fail++; } #define END_TESTING return _fail; diff --git a/src/Vtk/uLibVtkInterface.cxx b/src/Vtk/uLibVtkInterface.cxx index b664240..cbf85a7 100644 --- a/src/Vtk/uLibVtkInterface.cxx +++ b/src/Vtk/uLibVtkInterface.cxx @@ -80,7 +80,8 @@ namespace Vtk { class PuppetData { public: - PuppetData() : + PuppetData(Puppet* owner) : + m_Puppet(owner), m_Renderers(vtkSmartPointer::New()), m_Assembly(vtkSmartPointer::New()), m_ShowBoundingBox(false), @@ -99,9 +100,10 @@ public: // No manual Delete needed for smart pointers } + Puppet *m_Puppet; // members // vtkSmartPointer m_Renderers; - vtkSmartPointer m_Assembly; + vtkSmartPointer m_Assembly; vtkSmartPointer m_OutlineSource; vtkSmartPointer m_OutlineActor; @@ -118,6 +120,8 @@ public: bool m_Selected; bool m_Visibility; bool m_Dragable; + + // TRS m_Transform; void ApplyAppearance(vtkProp *p) { @@ -157,20 +161,49 @@ public: void ApplyTransform(vtkProp3D* p3d) { if (p3d) { - p3d->SetPosition(m_Transform.position.x(), m_Transform.position.y(), m_Transform.position.z()); - - // Convert Model Radians to VTK Degrees - p3d->SetOrientation(m_Transform.rotation.x() / CLHEP::degree, - m_Transform.rotation.y() / CLHEP::degree, - m_Transform.rotation.z() / CLHEP::degree); - - p3d->SetScale(m_Transform.scaling.x(), m_Transform.scaling.y(), m_Transform.scaling.z()); - p3d->SetUserMatrix(nullptr); + if (auto* content = dynamic_cast(m_Puppet->GetContent())) { + vtkNew m; + Matrix4fToVtk(content->GetWorldMatrix(), m); + p3d->SetUserMatrix(m); + } else { + p3d->SetUserMatrix(nullptr); + p3d->SetPosition(m_Transform.position.x(), m_Transform.position.y(), m_Transform.position.z()); + + // Convert Model Radians to VTK Degrees + p3d->SetOrientation(m_Transform.rotation.x() / CLHEP::degree, + m_Transform.rotation.y() / CLHEP::degree, + m_Transform.rotation.z() / CLHEP::degree); + + p3d->SetScale(m_Transform.scaling.x(), m_Transform.scaling.y(), m_Transform.scaling.z()); + } } } void UpdateHighlight() { if (m_Selected) { + // Find first polydata in assembly to highlight + vtkPolyData* polydata = nullptr; + 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()) { + polydata = vtkPolyData::SafeDownCast(actor->GetMapper()->GetDataSetInput()); + if (polydata) break; + } + } + + if (!polydata) { + if (m_HighlightActor) { + m_Renderers->InitTraversal(); + for (int i = 0; i < m_Renderers->GetNumberOfItems(); ++i) { + m_Renderers->GetNextItem()->RemoveActor(m_HighlightActor); + } + m_HighlightActor = nullptr; + } + return; + } + if (!m_HighlightActor) { vtkSmartPointer edges = vtkSmartPointer::New(); edges->BoundaryEdgesOn(); @@ -178,17 +211,7 @@ public: 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; - } - } + edges->SetInputData(polydata); m_HighlightActor = vtkSmartPointer::New(); vtkSmartPointer mapper = vtkSmartPointer::New(); @@ -197,6 +220,13 @@ public: m_HighlightActor->GetProperty()->SetColor(1.0, 0.5, 0.0); // Orange m_HighlightActor->GetProperty()->SetLineWidth(2.0); m_HighlightActor->GetProperty()->SetLighting(0); + } else { + // Update input data (safe even if same) + if (auto* mapper = vtkPolyDataMapper::SafeDownCast(m_HighlightActor->GetMapper())) { + if (auto* edges = vtkFeatureEdges::SafeDownCast(mapper->GetInputAlgorithm())) { + edges->SetInputData(polydata); + } + } } // Update highlight matrix from the root prop @@ -208,7 +238,6 @@ public: } if (root) { - // Now that we use internal TRS, the prop's total matrix is GetMatrix() m_HighlightActor->SetUserMatrix(root->GetMatrix()); } @@ -242,7 +271,7 @@ public: -Puppet::Puppet() : Object(), pd(new PuppetData) { +Puppet::Puppet() : Object(), pd(new PuppetData(this)) { ULIB_ACTIVATE_DISPLAY_PROPERTIES; for (auto* p : this->GetDisplayProperties()) { uLib::Object::connect(p, &uLib::PropertyBase::Updated, this, &Puppet::Update); @@ -576,36 +605,27 @@ void Puppet::SyncFromVtk() if (auto* p3d = vtkProp3D::SafeDownCast(root)) { // Handle content synchronization if it's an AffineTransform if (auto* content = dynamic_cast(GetContent())) { - double pos[3], ori[3], scale[3]; - p3d->GetPosition(pos); - p3d->GetOrientation(ori); - p3d->GetScale(scale); + // Apply current VTK world transform to model. + // When 'sinking the chain', p3d's matrix represents the world matrix + // because it has no parent in VTK (nesting was removed). + vtkNew m; + p3d->GetMatrix(m); + content->SetWorldMatrix(VtkToMatrix4f(m)); - // Convert VTK Degrees to Model Radians - content->SetPosition(Vector3f(pos[0], pos[1], pos[2])); - content->SetOrientation(Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree); - content->SetScale(Vector3f(scale[0], scale[1], scale[2])); - - // Re-sync internal puppet properties from the now-updated content + // Sync local TRS from the newly updated model pd->m_Transform = *content; } else { - // Update internal puppet TRS directly from VTK components + // Fallback for simple props double pos[3], ori[3], scale[3]; p3d->GetPosition(pos); p3d->GetOrientation(ori); p3d->GetScale(scale); pd->m_Transform.position = Vector3f(pos[0], pos[1], pos[2]); - // Convert VTK Degrees to internal Radians pd->m_Transform.rotation = Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree; - pd->m_Transform.scaling = Vector3f(scale[0], scale[1], scale[2]); + pd->m_Transform.scaling = Vector3f(scale[0], scale[1], scale[2]); } - // 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(); } } @@ -614,6 +634,11 @@ void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor) { } + +// ------------------------------------------------------ // +// SERIALIZE DISPLAY PROPERTIES + + struct TransformProxy { PuppetData* pd; template @@ -643,13 +668,6 @@ void Puppet::serialize_display(Archive::display_properties_archive & ar, const u 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 diff --git a/src/Vtk/uLibVtkInterface.h b/src/Vtk/uLibVtkInterface.h index b0577f0..3b0f806 100644 --- a/src/Vtk/uLibVtkInterface.h +++ b/src/Vtk/uLibVtkInterface.h @@ -54,7 +54,9 @@ namespace uLib { namespace Vtk { class Puppet : public uLib::Object { + uLibTypeMacro(Puppet, uLib::Object) + public: Puppet(); virtual ~Puppet(); @@ -99,14 +101,6 @@ public: vtkRendererCollection *GetRenderers() const; - virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) override; - virtual void serialize(Archive::log_archive & ar, const unsigned int version) override; - const std::vector& GetDisplayProperties() const { return m_DisplayProperties; } void RegisterDisplayProperty(uLib::PropertyBase* prop) { m_DisplayProperties.push_back(prop); }