add assembly to gcompose, not working yet

This commit is contained in:
AndreaRigoni
2026-03-27 16:55:26 +00:00
parent 171a07eb79
commit 46c39bc26e
15 changed files with 287 additions and 32 deletions

View File

@@ -52,6 +52,7 @@ class polymorphic_oarchive;
namespace uLib {
class PropertyBase;
class ObjectsContext;
class Version {
public:
@@ -101,6 +102,9 @@ public:
// FIXX !!!
virtual void DeepCopy(const Object &copy);
/** @brief Returns a nested context for children objects, if any. */
virtual ObjectsContext* GetChildren() { return nullptr; }
////////////////////////////////////////////////////////////////////////////
// SERIALIZATION //

View File

@@ -15,6 +15,7 @@ public:
virtual ~ObjectsContext();
virtual const char * GetClassName() const { return "ObjectsContext"; }
virtual ObjectsContext* GetChildren() override { return this; }
/**
* @brief Adds an object to the context.

View File

@@ -25,7 +25,9 @@ Assembly::Assembly()
m_BBoxMin(Vector3f::Zero()),
m_BBoxMax(Vector3f::Zero()),
m_ShowBoundingBox(false),
m_GroupSelection(true) {}
m_GroupSelection(true) {
ULIB_ACTIVATE_PROPERTIES(*this);
}
Assembly::Assembly(const Assembly &copy)
: ObjectsContext(copy),
@@ -35,13 +37,25 @@ Assembly::Assembly(const Assembly &copy)
m_ShowBoundingBox(copy.m_ShowBoundingBox),
m_GroupSelection(copy.m_GroupSelection) {}
Assembly::~Assembly() {}
Assembly::~Assembly() {
for (auto const& [obj, conn] : m_ChildConnections) {
conn.disconnect();
}
m_ChildConnections.clear();
}
void Assembly::AddObject(Object *obj) {
if (auto *at = dynamic_cast<AffineTransform *>(obj)) {
at->SetParent(this);
}
ObjectsContext::AddObject(obj);
// Connect to child updates to recompute AABB
m_ChildConnections[obj] = Object::connect(obj, &Object::Updated, [this](){
this->ComputeBoundingBox();
this->Updated(); // Signal that assembly itself changed (AABB-wise)
});
this->ComputeBoundingBox();
}
void Assembly::RemoveObject(Object *obj) {
@@ -49,7 +63,15 @@ void Assembly::RemoveObject(Object *obj) {
if (at->GetParent() == this)
at->SetParent(nullptr);
}
auto itConn = m_ChildConnections.find(obj);
if (itConn != m_ChildConnections.end()) {
itConn->second.disconnect();
m_ChildConnections.erase(itConn);
}
ObjectsContext::RemoveObject(obj);
this->ComputeBoundingBox();
}
void Assembly::ComputeBoundingBox() {
@@ -64,12 +86,11 @@ void Assembly::ComputeBoundingBox() {
m_BBoxMin = Vector3f(inf, inf, inf);
m_BBoxMax = Vector3f(-inf, -inf, -inf);
Matrix4f invAsm = this->GetWorldMatrix().inverse();
for (Object *obj : objects) {
if (auto *box = dynamic_cast<ContainerBox *>(obj)) {
// ContainerBox: wm is matrix from unit cube [0,1] to assembly base
Matrix4f m = invAsm * box->GetWorldMatrix();
// ContainerBox: wm is matrix from unit cube [0,1] to local space
// Since it is parented to 'this', GetMatrix() is sufficient.
Matrix4f m = box->GetMatrix();
for (int i = 0; i < 8; ++i) {
float x = (i & 1) ? 1.0f : 0.0f;
float y = (i & 2) ? 1.0f : 0.0f;
@@ -82,7 +103,7 @@ void Assembly::ComputeBoundingBox() {
}
} else if (auto *cyl = dynamic_cast<Cylinder *>(obj)) {
// Cylinder: centered [-1, 1] radial, [-0.5, 0.5] height
Matrix4f m = invAsm * cyl->GetWorldMatrix();
Matrix4f m = cyl->GetMatrix();
for (int i = 0; i < 8; ++i) {
float x = (i & 1) ? 1.0f : -1.0f;
float y = (i & 2) ? 0.5f : -0.5f;
@@ -98,7 +119,7 @@ void Assembly::ComputeBoundingBox() {
subAsm->ComputeBoundingBox();
Vector3f subMin, subMax;
subAsm->GetBoundingBox(subMin, subMax);
Matrix4f m = invAsm * subAsm->GetWorldMatrix();
Matrix4f m = subAsm->GetMatrix();
for (int i = 0; i < 8; ++i) {
float x = (i & 1) ? subMax(0) : subMin(0);
float y = (i & 2) ? subMax(1) : subMin(1);

View File

@@ -52,6 +52,12 @@ public:
Assembly(const Assembly &copy);
virtual ~Assembly();
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_hrp("GroupSelection", m_GroupSelection);
}
virtual void AddObject(Object* obj) override;
virtual void RemoveObject(Object* obj) override;
@@ -93,7 +99,7 @@ signals:
if (m_InUpdated) return; // break signal recursion
m_InUpdated = true;
this->ComputeBoundingBox();
ULIB_SIGNAL_EMIT(Assembly::Updated);
ULIB_SIGNAL_EMIT(Object::Updated);
m_InUpdated = false;
}
@@ -103,6 +109,7 @@ private:
bool m_ShowBoundingBox;
bool m_GroupSelection;
bool m_InUpdated = false;
std::map<Object*, Connection> m_ChildConnections;
};
} // namespace uLib

View File

@@ -215,7 +215,7 @@ signals:
/** Signal emitted when properties change */
virtual void Updated() override {
this->Sync();
ULIB_SIGNAL_EMIT(ContainerBox::Updated);
ULIB_SIGNAL_EMIT(Object::Updated);
}
private:

View File

@@ -177,7 +177,7 @@ signals:
/** Signal emitted when properties change */
virtual void Updated() override {
this->Sync();
ULIB_SIGNAL_EMIT(Cylinder::Updated);
ULIB_SIGNAL_EMIT(Object::Updated);
}
private:

View File

@@ -5,12 +5,14 @@
#include "Math/TriangleMesh.h"
#include "Math/QuadMesh.h"
#include "Math/VoxImage.h"
#include "Math/Assembly.h"
#include "Math/StructuredData.h"
namespace uLib {
ULIB_REGISTER_OBJECT(ContainerBox)
ULIB_REGISTER_OBJECT(Cylinder)
ULIB_REGISTER_OBJECT(Assembly)
ULIB_REGISTER_OBJECT(CylindricalGeometry)
ULIB_REGISTER_OBJECT(SphericalGeometry)
ULIB_REGISTER_OBJECT(TriangleMesh)

View File

@@ -285,7 +285,7 @@ signals:
/** Signal emitted when properties change */
virtual void Updated() override {
this->Sync();
ULIB_SIGNAL_EMIT(AffineTransform::Updated);
ULIB_SIGNAL_EMIT(Object::Updated);
}
private:

View File

@@ -70,6 +70,7 @@ void Assembly::InstallPipe() {
m_BBoxActor->GetProperty()->SetColor(1.0, 0.85, 0.0); // gold wireframe
m_BBoxActor->GetProperty()->SetLineWidth(1.5);
m_BBoxActor->GetProperty()->SetOpacity(0.6);
m_BBoxActor->PickableOff();
m_BBoxActor->SetVisibility(m_Content ? m_Content->GetShowBoundingBox() : false);
m_VtkAsm->AddPart(m_BBoxActor);

View File

@@ -48,6 +48,7 @@ public:
virtual void SyncFromVtk() override;
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_Content; }
/** @brief Called when the model signals an update (model→VTK push). */
void contentUpdate();

View File

@@ -3,6 +3,7 @@ set(TESTS
vtkViewerTest
vtkHandlerWidget
PuppetPropertyTest
PuppetParentingTest
# vtkVoxImageTest
# vtkTriangleMeshTest
)

View File

@@ -0,0 +1,106 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
// All rights reserved
//
// Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
//
//////////////////////////////////////////////////////////////////////////////*/
#include <cstdlib>
#include <Core/ObjectsContext.h>
#include <Math/Assembly.h>
#include <Math/ContainerBox.h>
#include <Vtk/uLibVtkViewer.h>
#include <Vtk/vtkObjectsContext.h>
#include <Vtk/Math/vtkAssembly.h>
#include <Vtk/Math/vtkContainerBox.h>
#include <vtkAssembly.h>
#include <vtkProp3D.h>
#include <vtkRenderer.h>
#include <vtkMatrix4x4.h>
#include "testing-prototype.h"
using namespace uLib;
int main() {
BEGIN_TESTING(Puppet Parenting Test);
ObjectsContext globalContext;
Vtk::Viewer viewer;
// Create the display context, linked to the model context.
// It will automatically create visual puppets for each model object.
Vtk::vtkObjectsContext viewerContext(&globalContext);
viewerContext.ConnectRenderer(viewer.GetRenderer());
// 1. Create a model Assembly
auto* assembly = new Assembly();
assembly->SetInstanceName("ParentAssembly");
globalContext.AddObject(assembly);
// Verify assembly puppet exists in the viewer context
Vtk::Puppet* assemblyPuppet = viewerContext.GetPuppet(assembly);
ASSERT_NOT_NULL(assemblyPuppet);
// cast to Vtk::Assembly to access child context
auto* vtkAss = dynamic_cast<Vtk::Assembly*>(assemblyPuppet);
ASSERT_NOT_NULL(vtkAss);
// 2. Create a child Box and add it to the Assembly
auto* box1 = new ContainerBox(Vector3f(10, 10, 10));
box1->SetInstanceName("ChildBox1");
box1->SetPosition(Vector3f(20, 0, 0));
assembly->AddObject(box1);
// Verify child puppet was created in the assembly's child context
Vtk::vtkObjectsContext* childVtkCtx = vtkAss->GetChildrenContext();
ASSERT_NOT_NULL(childVtkCtx);
Vtk::Puppet* box1Puppet = childVtkCtx->GetPuppet(box1);
ASSERT_NOT_NULL(box1Puppet);
// 3. Move the parent and verify the child follows
assembly->SetPosition(Vector3f(100, 0, 0));
assembly->Update();
// In VTK assemblies, the child's absolute matrix should reflect the parent's transform
vtkProp3D* box1Prop = vtkProp3D::SafeDownCast(box1Puppet->GetProp());
ASSERT_NOT_NULL(box1Prop);
vtkMatrix4x4* boxMatrix = box1Prop->GetMatrix();
// Origin (0,0,0) + local(20,0,0) + assembly(100,0,0) = world(120,0,0) ?
// Actually, box1->GetPosition() is (20,0,0).
// The puppet ApplyTransform sets the prop orientation and position.
std::cout << "Checking transformation chain..." << std::endl;
// std::cout << *boxMatrix << std::endl;
// Verify relative positioning
double* pos = box1Prop->GetPosition();
ASSERT_EQUAL(pos[0], 20.0);
// The absolute world position can be checked via GetMatrix elements
// boxMatrix->GetElement(0, 3) should be 120.0 if the vtkAssembly nesting is working
// but vtkAssembly::GetMatrix() usually returns the LOCAL matrix unless called on the top property context?
// Actually vtkProp3D::GetMatrix() is the local matrix.
// 4. Add another child
auto* box2 = new ContainerBox(Vector3f(5, 5, 5));
box2->SetInstanceName("ChildBox2");
box2->SetPosition(Vector3f(-20, 0, 0));
assembly->AddObject(box2);
Vtk::Puppet* box2Puppet = childVtkCtx->GetPuppet(box2);
ASSERT_NOT_NULL(box2Puppet);
// Render if not in batch environment
if (!std::getenv("CTEST_PROJECT_NAME")) {
viewer.GetRenderer()->ResetCamera();
viewer.Start();
}
END_TESTING;
}