refactor: update Puppet transform logic to support AffineTransform world matrices and improve selection highlighting
This commit is contained in:
4
.clangd
4
.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: ["*"]
|
||||
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <class ArchiveT>
|
||||
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("AffineTransform", boost::serialization::base_object<AffineTransform>(*this));
|
||||
ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object<TRS>(*this));
|
||||
ar & boost::serialization::make_hrp("GroupSelection", m_GroupSelection);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <class ArchiveT>
|
||||
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object<TRS>(*this));
|
||||
ar & HRP(Size);
|
||||
ar & HRP(Origin);
|
||||
}
|
||||
|
||||
@@ -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 <class ArchiveT>
|
||||
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("TRS", boost::serialization::base_object<TRS>(*this));
|
||||
ar & HRP(Radius);
|
||||
ar & HRP(Height);
|
||||
ar & HRP(Axis);
|
||||
|
||||
@@ -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<const LinearGeometry*>(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;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
namespace uLib {
|
||||
|
||||
ULIB_REGISTER_OBJECT(TRS)
|
||||
ULIB_REGISTER_OBJECT(ContainerBox)
|
||||
ULIB_REGISTER_OBJECT(Cylinder)
|
||||
ULIB_REGISTER_OBJECT(Assembly)
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
|
||||
@@ -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<float>(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<float>(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 <class ArchiveT>
|
||||
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 <class ArchiveT>
|
||||
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<float>(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<float>(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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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++; }
|
||||
@@ -162,6 +162,12 @@ void init_math(py::module_ &m) {
|
||||
.def_readwrite("direction_error", &HError3f::direction_error);
|
||||
|
||||
// 3. Dynamic Vectors (uLib::Vector)
|
||||
py::class_<TRS, AffineTransform, std::shared_ptr<TRS>>(m, "TRS")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("position", &TRS::position)
|
||||
.def_readwrite("rotation", &TRS::rotation)
|
||||
.def_readwrite("scaling", &TRS::scaling);
|
||||
|
||||
py::bind_vector<uLib::Vector<Scalari>>(m, "Vector_i")
|
||||
.def("MoveToVRAM", &uLib::Vector<Scalari>::MoveToVRAM)
|
||||
.def("MoveToRAM", &uLib::Vector<Scalari>::MoveToRAM);
|
||||
@@ -268,7 +274,7 @@ void init_math(py::module_ &m) {
|
||||
|
||||
// 5. Core Math Structures
|
||||
py::class_<AffineTransform, std::shared_ptr<AffineTransform>>(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<const Matrix3f>(&AffineTransform::Rotate))
|
||||
py::overload_cast<const Matrix3f&>(&AffineTransform::Rotate))
|
||||
.def("Rotate",
|
||||
py::overload_cast<float, Vector3f>(&AffineTransform::Rotate))
|
||||
.def("Rotate", py::overload_cast<Vector3f>(&AffineTransform::Rotate))
|
||||
.def("EulerYZYRotate", &AffineTransform::EulerYZYRotate)
|
||||
.def("FlipAxes", &AffineTransform::FlipAxes);
|
||||
.def("FlipAxes", &AffineTransform::FlipAxes)
|
||||
.def("SetWorldMatrix", &AffineTransform::SetWorldMatrix);
|
||||
|
||||
py::class_<Geometry, AffineTransform, std::shared_ptr<Geometry>>(m, "Geometry")
|
||||
py::class_<TRS, AffineTransform, std::shared_ptr<TRS>>(m, "TRS")
|
||||
.def(py::init<>())
|
||||
.def("GetWorldPoint", py::overload_cast<const Vector4f>(
|
||||
&Geometry::GetWorldPoint, py::const_))
|
||||
.def("GetWorldPoint",
|
||||
py::overload_cast<float, float, float>(&Geometry::GetWorldPoint))
|
||||
.def("GetLocalPoint", py::overload_cast<const Vector4f>(
|
||||
&Geometry::GetLocalPoint, py::const_))
|
||||
.def("GetLocalPoint",
|
||||
py::overload_cast<float, float, float>(&Geometry::GetLocalPoint));
|
||||
.def(py::init<const Matrix4f &>())
|
||||
.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_<Geometry, Object, std::shared_ptr<Geometry>>(m, "Geometry")
|
||||
.def("GetParent", &Geometry::GetParent)
|
||||
.def("SetParent", &Geometry::SetParent)
|
||||
.def("GetWorldPoint", py::overload_cast<const Vector4f>(&Geometry::GetWorldPoint, py::const_))
|
||||
.def("GetWorldPoint", py::overload_cast<float, float, float>(&Geometry::GetWorldPoint, py::const_))
|
||||
.def("GetLocalPoint", py::overload_cast<const Vector4f>(&Geometry::GetLocalPoint, py::const_))
|
||||
.def("GetLocalPoint", py::overload_cast<float, float, float>(&Geometry::GetLocalPoint, py::const_));
|
||||
|
||||
py::class_<LinearGeometry, Geometry, std::shared_ptr<LinearGeometry>>(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_<ContainerBox, AffineTransform, Object, std::shared_ptr<ContainerBox>>(
|
||||
m, "ContainerBox")
|
||||
@@ -427,7 +456,7 @@ void init_math(py::module_ &m) {
|
||||
.def("__getitem__",
|
||||
py::overload_cast<const Vector3i &>(&VoxImage<Voxel>::operator[]));
|
||||
|
||||
py::class_<TriangleMesh>(m, "TriangleMesh")
|
||||
py::class_<TriangleMesh, TRS, std::shared_ptr<TriangleMesh>>(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_<QuadMesh>(m, "QuadMesh")
|
||||
py::class_<QuadMesh, TRS, std::shared_ptr<QuadMesh>>(m, "QuadMesh")
|
||||
.def(py::init<>())
|
||||
.def("AddPoint", &QuadMesh::AddPoint)
|
||||
.def("AddQuad",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "uLibVtkInterface.h"
|
||||
#include "vtkPolydata.h"
|
||||
#include <boost/signals2/connection.hpp>
|
||||
|
||||
class vtkActor;
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ namespace Vtk {
|
||||
|
||||
class PuppetData {
|
||||
public:
|
||||
PuppetData() :
|
||||
PuppetData(Puppet* owner) :
|
||||
m_Puppet(owner),
|
||||
m_Renderers(vtkSmartPointer<vtkRendererCollection>::New()),
|
||||
m_Assembly(vtkSmartPointer<vtkAssembly>::New()),
|
||||
m_ShowBoundingBox(false),
|
||||
@@ -99,9 +100,10 @@ public:
|
||||
// No manual Delete needed for smart pointers
|
||||
}
|
||||
|
||||
Puppet *m_Puppet;
|
||||
// members //
|
||||
vtkSmartPointer<vtkRendererCollection> m_Renderers;
|
||||
vtkSmartPointer<vtkAssembly> m_Assembly;
|
||||
vtkSmartPointer<vtkAssembly> m_Assembly;
|
||||
|
||||
vtkSmartPointer<vtkOutlineSource> m_OutlineSource;
|
||||
vtkSmartPointer<vtkActor> 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<uLib::AffineTransform*>(m_Puppet->GetContent())) {
|
||||
vtkNew<vtkMatrix4x4> 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<vtkFeatureEdges> edges = vtkSmartPointer<vtkFeatureEdges>::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<vtkActor>::New();
|
||||
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::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<uLib::AffineTransform*>(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<vtkMatrix4x4> 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<class Archive>
|
||||
@@ -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
|
||||
|
||||
@@ -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<uLib::PropertyBase*>& GetDisplayProperties() const { return m_DisplayProperties; }
|
||||
void RegisterDisplayProperty(uLib::PropertyBase* prop) { m_DisplayProperties.push_back(prop); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user