refactor: update transformation system, improve template readability, and reorganize VTK assembly management

This commit is contained in:
AndreaRigoni
2026-03-31 16:04:03 +00:00
parent 22d0041942
commit d4fd2d3914
30 changed files with 568 additions and 501 deletions

View File

@@ -64,7 +64,7 @@ vtkDetectorChamber::vtkDetectorChamber(DetectorChamber *content)
this->SetProp(m_PlaneActor);
this->contentUpdate();
this->Update();
}
vtkDetectorChamber::~vtkDetectorChamber() {
@@ -76,8 +76,8 @@ DetectorChamber *vtkDetectorChamber::GetContent() {
return static_cast<DetectorChamber *>(m_Content);
}
void vtkDetectorChamber::contentUpdate() {
this->BaseClass::contentUpdate();
void vtkDetectorChamber::Update() {
this->BaseClass::Update();
if (!m_Content) return;
DetectorChamber *c = this->GetContent();

View File

@@ -56,7 +56,7 @@ public:
Content *GetContent();
virtual void contentUpdate() override;
virtual void Update() override;
protected:
vtkActor *m_PlaneActor;

View File

@@ -40,20 +40,21 @@ namespace Vtk {
vtkMuonScatter::vtkMuonScatter(MuonScatter &content)
: m_Content(&content), m_LineIn(vtkLineSource::New()),
m_LineOut(vtkLineSource::New()), m_PolyData(vtkPolyData::New()),
m_SpherePoca(NULL) {
m_SpherePoca(NULL), m_Asm(vtkAssembly::New()) {
InstallPipe();
}
vtkMuonScatter::vtkMuonScatter(const MuonScatter &content)
: m_Content(const_cast<MuonScatter *>(&content)),
m_LineIn(vtkLineSource::New()), m_LineOut(vtkLineSource::New()),
m_PolyData(vtkPolyData::New()), m_SpherePoca(NULL) {
m_PolyData(vtkPolyData::New()), m_SpherePoca(NULL), m_Asm(vtkAssembly::New()) {
InstallPipe();
}
vtkMuonScatter::~vtkMuonScatter() {
m_LineIn->Delete();
m_LineOut->Delete();
m_Asm->Delete();
if (m_SpherePoca)
m_SpherePoca->Delete();
}
@@ -87,13 +88,15 @@ void vtkMuonScatter::InstallPipe() {
mapper->SetInputConnection(m_LineIn->GetOutputPort());
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
this->SetProp(actor);
m_Asm->AddPart(actor);
mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(m_LineOut->GetOutputPort());
actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
this->SetProp(actor);
m_Asm->AddPart(actor);
this->SetProp(m_Asm);
}
vtkPolyData *vtkMuonScatter::GetPolyData() const {
@@ -123,7 +126,7 @@ void vtkMuonScatter::AddPocaPoint(HPoint3f poca) {
mapper->SetInputConnection(m_SpherePoca->GetOutputPort());
vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
this->SetProp(actor);
m_Asm->AddPart(actor);
}
HPoint3f vtkMuonScatter::GetPocaPoint() {

View File

@@ -27,6 +27,7 @@
#define VTKMUONSCATTER_H
#include <vtkActor.h>
#include <vtkAssembly.h>
#include <vtkAppendPolyData.h>
#include <vtkLineSource.h>
#include <vtkPolyDataMapper.h>
@@ -85,6 +86,7 @@ private:
vtkLineSource *m_LineOut;
vtkSphereSource *m_SpherePoca;
vtkPolyData *m_PolyData;
vtkAssembly *m_Asm;
};
} // namespace Vtk

View File

@@ -50,6 +50,7 @@ vtkVoxRaytracerRepresentation::vtkVoxRaytracerRepresentation(Content &content)
m_RayRepresentation(vtkAppendPolyData::New()),
m_RayRepresentationActor(vtkActor::New()),
m_Transform(vtkTransform::New()),
m_Asm(vtkAssembly::New()),
m_HasMuon(false), m_HasPoca(false) {
default_radius = content.GetImage()->GetSpacing()(0) / 4;
m_Sphere1->SetRadius(default_radius);
@@ -313,20 +314,19 @@ void vtkVoxRaytracerRepresentation::InstallPipe() {
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(append->GetOutputPort());
vtkSmartPointer<vtkActor> actor = vtkActor::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(0.6, 0.6, 1);
this->SetProp(actor);
m_Asm->AddPart(actor);
mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(m_RayLine->GetOutputPort());
m_RayLineActor->SetMapper(mapper);
m_RayLineActor->GetProperty()->SetColor(1, 0, 0);
this->SetProp(m_RayLineActor);
m_Asm->AddPart(m_RayLineActor);
vtkSmartPointer<vtkTransformPolyDataFilter> polyfilter =
vtkSmartPointer<vtkTransformPolyDataFilter>::New();
@@ -343,7 +343,9 @@ void vtkVoxRaytracerRepresentation::InstallPipe() {
vra->GetProperty()->SetEdgeVisibility(true);
vra->GetProperty()->SetColor(0.5, 0.5, 0.5);
this->SetProp(vra);
m_Asm->AddPart(vra);
this->SetProp(m_Asm);
}
} // namespace Vtk

View File

@@ -107,6 +107,7 @@ private:
bool m_HasPoca;
Scalarf default_radius;
vtkSmartPointer<vtkAssembly> m_Asm;
vtkAppendPolyData *m_RayLine;
vtkActor *m_RayLineActor;
vtkActor *m_RayRepresentationActor;

View File

@@ -39,7 +39,7 @@ Assembly::Assembly(uLib::Assembly *content)
this->InstallPipe();
if (m_Content) {
Object::connect(m_Content, &uLib::Assembly::Updated,
this, &Assembly::contentUpdate);
this, &Assembly::Update);
}
}
@@ -84,29 +84,27 @@ void Assembly::InstallPipe() {
}
}
// 4. Apply initial transform
this->UpdateTransform();
this->UpdateBoundingBox();
}
// ------------------------------------------------------------------ //
void Assembly::contentUpdate() {
if (m_InUpdate) return;
m_InUpdate = true;
this->UpdateTransform();
this->UpdateBoundingBox();
if (m_ChildContext)
m_ChildContext->Update();
Puppet::Update();
m_InUpdate = false;
// 4. Force initial visual sync
this->Update();
}
// ------------------------------------------------------------------ //
void Assembly::Update() {
if (m_InUpdate) return;
m_InUpdate = true;
this->contentUpdate();
if (m_Content && m_VtkAsm) {
// Apply world matrix from the assembly content
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m);
m_VtkAsm->SetUserMatrix(m);
m_VtkAsm->Modified();
}
this->Puppet::Update();
this->UpdateBoundingBox();
if (m_ChildContext)
m_ChildContext->Update();
m_InUpdate = false;
}
@@ -116,14 +114,11 @@ void Assembly::SyncFromVtk() {
m_InUpdate = true;
double pos[3], ori[3], scale[3];
m_VtkAsm->GetPosition(pos);
m_VtkAsm->GetOrientation(ori);
m_VtkAsm->GetScale(scale);
m_Content->SetPosition(Vector3f(pos[0], pos[1], pos[2]));
m_Content->SetOrientation(Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree);
m_Content->SetScale(Vector3f(scale[0], scale[1], scale[2]));
// VTK -> Model: Update world matrix (accounting for model parents)
if (vtkProp3D* proxy = this->GetProxyProp()) {
m_Content->SetWorldMatrix(VtkToMatrix4f(proxy->GetUserMatrix()));
m_Content->FromMatrix(m_Content->GetMatrix());
}
this->UpdateBoundingBox();
if (m_ChildContext)
@@ -134,14 +129,6 @@ void Assembly::SyncFromVtk() {
m_InUpdate = false;
}
// ------------------------------------------------------------------ //
void Assembly::UpdateTransform() {
if (!m_Content || !m_VtkAsm) return;
this->ApplyTransform(m_VtkAsm);
m_VtkAsm->Modified();
}
// ------------------------------------------------------------------ //
void Assembly::UpdateBoundingBox() {
if (!m_Content || !m_BBoxActor) return;

View File

@@ -50,14 +50,14 @@ public:
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();
/**
* @brief Returns the puppet managing child objects.
*/
/** @brief Returns the puppet managing child objects. */
vtkObjectsContext *GetChildrenContext() const;
private:
void UpdateTransform();
void UpdateBoundingBox();
void InstallPipe();

View File

@@ -48,18 +48,28 @@ namespace uLib {
namespace Vtk {
struct ContainerBoxData {
vtkSmartPointer<vtkActor> m_Cube;
vtkSmartPointer<vtkActor> m_Axes;
vtkSmartPointer<vtkActor> m_Cube;
vtkSmartPointer<vtkActor> m_Axes;
vtkSmartPointer<vtkAssembly> m_VtkAsm;
vtkSmartPointer<vtkMatrix4x4> m_Affine;
uLib::Connection m_UpdateSignal;
ContainerBoxData() : m_Cube(vtkSmartPointer<vtkActor>::New()), m_Axes(vtkSmartPointer<vtkActor>::New()) {}
ContainerBoxData() : m_Cube(vtkSmartPointer<vtkActor>::New()),
m_Axes(vtkSmartPointer<vtkActor>::New()),
m_VtkAsm(vtkSmartPointer<vtkAssembly>::New()),
m_Affine(vtkSmartPointer<vtkMatrix4x4>::New()) {}
~ContainerBoxData() {
}
};
vtkContainerBox::vtkContainerBox(vtkContainerBox::Content *content)
: d(new ContainerBoxData()), m_Content(content) {
this->InstallPipe();
Object::connect(m_Content, &Content::Updated, this, &vtkContainerBox::contentUpdate);
d->m_UpdateSignal = Object::connect(m_Content, &uLib::Object::Updated, this, &vtkContainerBox::Update);
}
vtkContainerBox::~vtkContainerBox() {
@@ -72,47 +82,50 @@ vtkPolyData *vtkContainerBox::GetPolyData() const {
}
void vtkContainerBox::contentUpdate() {
void vtkContainerBox::Update() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content)
return;
if (!m_Content) return;
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (!root) return;
if (root) {
// Apply local full matrix (TRS * LocalBox) so that nested assemblies work correctly
Matrix4f fullLocal = m_Content->GetMatrix() * m_Content->GetLocalMatrix();
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(fullLocal, m);
root->SetUserMatrix(m);
root->Modified();
}
d->m_Cube->SetUserMatrix(nullptr);
d->m_Axes->SetUserMatrix(nullptr);
TRS trs(*m_Content);
this->ApplyTransform(root);
root->Modified();
m_BlockUpdate = false;
Puppet::Update();
// Delegate rest of update (appearance, render, etc)
ConnectionBlock blocker(d->m_UpdateSignal);
this->Puppet::Update();
}
void vtkContainerBox::Update() {
this->contentUpdate();
}
void vtkContainerBox::SyncFromVtk() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content) return;
vtkProp3D* assembly = vtkProp3D::SafeDownCast(this->GetProp());
if (!assembly) return;
double pos[3], ori[3], scale[3];
assembly->GetPosition(pos);
assembly->GetOrientation(ori);
assembly->GetScale(scale);
vtkProp3D* root = this->GetProxyProp();
if (!root) return;
// VTK -> Model: Extract new world TRS from proxy, which matches the model's TRS center
vtkMatrix4x4* rootMat = root->GetUserMatrix();
if (rootMat) {
std::cout << "[vtkContainerBox::SyncFromVtk] Read Proxy UserMatrix:" << std::endl;
rootMat->Print(std::cout);
}
m_Content->SetPosition(Vector3f(pos[0], pos[1], pos[2]));
m_Content->SetOrientation(Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree);
m_Content->SetScale(Vector3f(scale[0], scale[1], scale[2]));
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
m_Content->Updated(); // Notify change
// Synchronize TRS property members from the updated local matrix
m_Content->FromMatrix(vtkWorld);
std::cout << "[vtkContainerBox::SyncFromVtk] New Model WorldMatrix:" << std::endl << m_Content->GetWorldMatrix() << std::endl;
// Since we modified the model, notify observers, but block the loop back to VTK
// ConnectionBlock blocker(d->m_UpdateSignal);
m_Content->Updated();
}
@@ -154,17 +167,16 @@ void vtkContainerBox::InstallPipe() {
mapper->SetInputConnection(axes->GetOutputPort());
mapper->Update();
this->SetProp(d->m_Cube);
this->SetProp(d->m_Axes);
d->m_VtkAsm->AddPart(d->m_Cube);
d->m_VtkAsm->AddPart(d->m_Axes);
this->SetProp(d->m_VtkAsm);
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
vtkProp3D* root = d->m_VtkAsm;
if (root) {
TRS trs(*c);
root->SetPosition(trs.position.x(), trs.position.y(), trs.position.z());
root->SetOrientation(trs.rotation.x(), trs.rotation.y(), trs.rotation.z());
root->SetScale(trs.scaling.x(), trs.scaling.y(), trs.scaling.z());
root->SetUserMatrix(nullptr);
d->m_Affine = Matrix4fToVtk(m_Content->GetMatrix());
root->SetUserMatrix(d->m_Affine);
}
this->Update();
}
} // namespace Vtk

View File

@@ -46,9 +46,14 @@ public:
virtual class vtkPolyData *GetPolyData() const;
virtual void contentUpdate();
/**
* @brief Updates the VTK representation from the internal state.
*/
virtual void Update() override;
/**
* @brief Synchronizes the model from the VTK representation (VTK→model).
*/
virtual void SyncFromVtk() override;
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
@@ -59,6 +64,7 @@ protected:
struct ContainerBoxData *d;
Content *m_Content;
bool m_BlockUpdate = false;
};
} // namespace Vtk

View File

@@ -40,7 +40,7 @@ namespace Vtk {
vtkCylinder::vtkCylinder(vtkCylinder::Content *content)
: m_Content(content), m_Actor(nullptr), m_VtkAsm(nullptr) {
this->InstallPipe();
Object::connect(m_Content, &Content::Updated, this, &vtkCylinder::contentUpdate);
Object::connect(m_Content, &uLib::Object::Updated, this, &vtkCylinder::Update);
}
vtkCylinder::~vtkCylinder() {
@@ -48,59 +48,56 @@ vtkCylinder::~vtkCylinder() {
if (m_VtkAsm) m_VtkAsm->Delete();
}
void vtkCylinder::contentUpdate() {
void vtkCylinder::Update() {
if (!m_Content)
return;
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (!root) return;
if (root) {
// 1. Placement handled specifically from content (use TRS GetMatrix, not World)
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m);
root->SetUserMatrix(m);
// 1. Placement handled by base Puppet class via Sync / Update logic
// Update internal pd->m_Transform from content
Puppet::Update();
// 2. Shape-local properties (Radius, Height, Axis alignment) go to the internal actor
// These are relative to the root assembly
vtkTransform* alignment = vtkTransform::SafeDownCast(m_Actor->GetUserTransform());
if (alignment) {
alignment->Identity();
alignment->PostMultiply();
// Initial source is centered Y-cylinder (Radial XZ [-1,1], Height Y [-0.5, 0.5])
// Apply Radius and Height scaling
alignment->Scale(m_Content->GetRadius(), m_Content->GetHeight(), m_Content->GetRadius());
// 2. Shape-local properties (Radius, Height, Axis alignment) go to the internal actor
// These are relative to the root assembly
vtkTransform* alignment = vtkTransform::SafeDownCast(m_Actor->GetUserTransform());
if (alignment) {
alignment->Identity();
alignment->PostMultiply();
// Initial source is centered Y-cylinder (Radial XZ [-1,1], Height Y [-0.5, 0.5])
// Apply Radius and Height scaling
alignment->Scale(m_Content->GetRadius(), m_Content->GetHeight(), m_Content->GetRadius());
// Apply Axis alignment
int axis = m_Content->GetAxis();
if (axis == 0) alignment->RotateZ(-90); // Y -> X
else if (axis == 1) ; // Y -> Y (identity)
else if (axis == 2) alignment->RotateX(90); // Y -> Z
}
// Apply Axis alignment
int axis = m_Content->GetAxis();
if (axis == 0) alignment->RotateZ(-90); // Y -> X
else if (axis == 1) ; // Y -> Y (identity)
else if (axis == 2) alignment->RotateX(90); // Y -> Z
root->Modified();
}
root->Modified();
}
void vtkCylinder::Update() {
this->contentUpdate();
// Use base class sync, which handles appearance and children
this->Puppet::Update();
}
void vtkCylinder::SyncFromVtk() {
if (!m_Content) return;
vtkProp3D* assembly = vtkProp3D::SafeDownCast(this->GetProp());
vtkProp3D* assembly = this->GetProxyProp();
if (!assembly) return;
double pos[3], ori[3], scale[3];
assembly->GetPosition(pos);
assembly->GetOrientation(ori);
assembly->GetScale(scale);
// VTK -> Model: Update TRS properties from VTK matrix via world transform
m_Content->SetWorldMatrix(VtkToMatrix4f(assembly->GetUserMatrix()));
m_Content->SetPosition(Vector3f(pos[0], pos[1], pos[2]));
// Convert VTK degrees to model radians
m_Content->SetOrientation(Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree);
m_Content->SetScale(Vector3f(scale[0], scale[1], scale[2]));
// Resync TRS property members (pos/rot/scale) from the newly set local matrix
m_Content->FromMatrix(m_Content->GetMatrix());
m_Content->Updated(); // Notify change
// Since we modified the model, notify observers, but block the loop back to VTK
m_Content->Updated();
}
void vtkCylinder::InstallPipe() {
@@ -127,7 +124,7 @@ void vtkCylinder::InstallPipe() {
m_VtkAsm->AddPart(m_Actor);
this->contentUpdate();
this->Update();
}
} // namespace Vtk

View File

@@ -48,10 +48,7 @@ public:
vtkCylinder(Content *content);
virtual ~vtkCylinder();
/** Synchronizes the VTK actor with the uLib model matrix */
virtual void contentUpdate();
/** Synchronizes the uLib model matrix with the VTK actor (e.g., after UI manipulation) */
/** Synchronizes the VTK actor with the uLib model matrix and vice-versa */
virtual void Update() override;
/** Synchronizes the uLib model matrix with the VTK actor specifically for gizmo interactions */

View File

@@ -124,6 +124,7 @@ void vtkVoxImage::SetContent() {
vtkVoxImage::vtkVoxImage(Content &content)
: m_Content(content), m_Actor(vtkVolume::New()),
m_Asm(vtkAssembly::New()),
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
m_OutlineActor(vtkActor::New()),
m_Reader(NULL), m_Writer(NULL), writer_factor(1.E6),
@@ -136,6 +137,7 @@ vtkVoxImage::vtkVoxImage(Content &content)
vtkVoxImage::~vtkVoxImage() {
m_Image->Delete();
m_Actor->Delete();
m_Asm->Delete();
m_Outline->Delete();
m_OutlineActor->Delete();
}
@@ -330,8 +332,9 @@ void vtkVoxImage::InstallPipe() {
m_OutlineActor->GetProperty()->SetRepresentationToWireframe();
m_OutlineActor->GetProperty()->SetAmbient(0.7);
this->SetProp(m_Actor);
this->SetProp(m_OutlineActor);
m_Asm->AddPart(m_Actor);
m_Asm->AddPart(m_OutlineActor);
this->SetProp(m_Asm);
// Default look
this->SetRepresentation(Surface);

View File

@@ -31,6 +31,7 @@
#include <vtkVolume.h>
#include <vtkXMLImageDataReader.h>
#include <vtkXMLImageDataWriter.h>
#include <vtkAssembly.h>
#include <Math/VoxImage.h>
@@ -77,6 +78,7 @@ private:
vtkImageData *m_Image;
vtkCubeSource *m_Outline;
vtkActor *m_OutlineActor;
vtkAssembly *m_Asm;
vtkXMLImageDataReader *m_Reader;
vtkXMLImageDataWriter *m_Writer;

View File

@@ -58,6 +58,7 @@
#include <vtkPolyData.h>
#include <vtkFeatureEdges.h>
#include <vtkTransform.h>
#include <vtkCubeSource.h>
#include <vtkRenderWindow.h>
#include "uLibVtkInterface.h"
@@ -65,6 +66,7 @@
#include "Math/Dense.h"
#include "Vtk/Math/vtkDense.h"
#include "Core/Property.h"
#include "Math/Transform.h"
@@ -83,7 +85,7 @@ public:
PuppetData(Puppet* owner) :
m_Puppet(owner),
m_Renderers(vtkSmartPointer<vtkRendererCollection>::New()),
m_Assembly(vtkSmartPointer<vtkAssembly>::New()),
m_Prop(nullptr),
m_ShowBoundingBox(false),
m_ShowScaleMeasures(false),
m_Representation(Puppet::Surface),
@@ -103,7 +105,7 @@ public:
Puppet *m_Puppet;
// members //
vtkSmartPointer<vtkRendererCollection> m_Renderers;
vtkSmartPointer<vtkAssembly> m_Assembly;
vtkSmartPointer<vtkProp3D> m_Prop;
vtkSmartPointer<vtkOutlineSource> m_OutlineSource;
vtkSmartPointer<vtkActor> m_OutlineActor;
@@ -152,30 +154,26 @@ public:
} else if (vtkAssembly *asm_p = vtkAssembly::SafeDownCast(p)) {
// Recursively apply to parts of the assembly
vtkProp3DCollection *parts = asm_p->GetParts();
parts->InitTraversal();
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
this->ApplyAppearance(parts->GetNextProp3D());
if (parts) {
parts->InitTraversal();
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
this->ApplyAppearance(parts->GetNextProp3D());
}
}
}
}
void ApplyTransform(vtkProp3D* p3d) {
if (p3d) {
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());
}
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());
}
}
@@ -183,13 +181,21 @@ public:
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()) {
if (vtkActor *actor = vtkActor::SafeDownCast(m_Prop)) {
if (actor->GetMapper()) {
polydata = vtkPolyData::SafeDownCast(actor->GetMapper()->GetDataSetInput());
if (polydata) break;
}
} else if (vtkAssembly *asm_p = vtkAssembly::SafeDownCast(m_Prop)) {
vtkPropCollection *parts = asm_p->GetParts();
if (parts) {
parts->InitTraversal();
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
vtkActor *a = vtkActor::SafeDownCast(parts->GetNextProp());
if (a && a->GetMapper()) {
polydata = vtkPolyData::SafeDownCast(a->GetMapper()->GetDataSetInput());
if (polydata) break;
}
}
}
}
@@ -205,40 +211,50 @@ public:
}
if (!m_HighlightActor) {
vtkSmartPointer<vtkFeatureEdges> edges = vtkSmartPointer<vtkFeatureEdges>::New();
edges->BoundaryEdgesOn();
edges->FeatureEdgesOn();
edges->SetFeatureAngle(30);
edges->NonManifoldEdgesOn();
edges->ManifoldEdgesOff();
edges->SetInputData(polydata);
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
double bounds[6];
polydata->GetBounds(bounds);
// Add a small padding to prevent z-fighting
double maxDim = std::max({bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]});
double pad = maxDim * 0.02;
if(pad < 1e-4) pad = 0.05;
cube->SetBounds(bounds[0]-pad, bounds[1]+pad,
bounds[2]-pad, bounds[3]+pad,
bounds[4]-pad, bounds[5]+pad);
m_HighlightActor = vtkSmartPointer<vtkActor>::New();
vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(edges->GetOutputPort());
mapper->SetInputConnection(cube->GetOutputPort());
m_HighlightActor->SetMapper(mapper);
m_HighlightActor->GetProperty()->SetColor(1.0, 0.5, 0.0); // Orange
m_HighlightActor->GetProperty()->SetRepresentationToWireframe();
m_HighlightActor->GetProperty()->SetColor(1.0, 0.0, 0.0); // Red
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);
if (auto* cube = vtkCubeSource::SafeDownCast(mapper->GetInputAlgorithm())) {
double bounds[6];
polydata->GetBounds(bounds);
double maxDim = std::max({bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4]});
double pad = maxDim * 0.02;
if(pad < 1e-4) pad = 0.05;
cube->SetBounds(bounds[0]-pad, bounds[1]+pad,
bounds[2]-pad, bounds[3]+pad,
bounds[4]-pad, bounds[5]+pad);
cube->Modified();
}
}
}
// Update highlight matrix from the root prop
vtkProp3D* root = nullptr;
if (m_Assembly->GetParts()->GetNumberOfItems() == 1) {
root = vtkProp3D::SafeDownCast(m_Assembly->GetParts()->GetLastProp());
} else {
root = m_Assembly;
}
if (root) {
m_HighlightActor->SetUserMatrix(root->GetMatrix());
// Update highlight matrix from the model world matrix
if (m_Puppet) {
if (auto* content = m_Puppet->GetContent()) {
if (auto* tr = dynamic_cast<uLib::TRS*>(content)) {
vtkNew<vtkMatrix4x4> vwm;
Matrix4fToVtk(tr->GetWorldMatrix(), vwm);
m_HighlightActor->SetUserMatrix(vwm);
}
}
}
m_Renderers->InitTraversal();
@@ -285,36 +301,38 @@ Puppet::~Puppet()
vtkProp *Puppet::GetProp()
{
if (pd->m_Assembly->GetParts()->GetNumberOfItems() == 1)
return pd->m_Assembly->GetParts()->GetLastProp();
else
return pd->m_Assembly;
return pd->m_Prop;
}
vtkProp3D *Puppet::GetProxyProp()
{
// The handler should manipulate the highlight actor if it exists
if (pd->m_HighlightActor) {
return pd->m_HighlightActor;
}
return vtkProp3D::SafeDownCast(this->GetProp());
}
void Puppet::SetProp(vtkProp *prop)
{
if(prop) {
prop->SetPickable(pd->m_Selectable);
if (auto* p3d = vtkProp3D::SafeDownCast(prop)) {
pd->m_Assembly->AddPart(p3d);
}
pd->m_Prop = vtkProp3D::SafeDownCast(prop);
pd->ApplyAppearance(prop);
// For the first actor added, seed the tracked display values from the VTK
// actor's current state so the display properties panel shows meaningful
// initial values instead of the -1 "not-overriding" sentinels.
if (pd->m_Assembly->GetParts()->GetNumberOfItems() == 1) {
if (auto* actor = vtkActor::SafeDownCast(prop)) {
vtkProperty* vp = actor->GetProperty();
if (pd->m_Representation < 0)
pd->m_Representation = vp->GetRepresentation();
if (pd->m_Opacity < 0)
pd->m_Opacity = vp->GetOpacity();
if (pd->m_Color.x() < 0) {
double c[3];
vp->GetColor(c);
pd->m_Color = Vector3d(c[0], c[1], c[2]);
}
if (auto* actor = vtkActor::SafeDownCast(prop)) {
vtkProperty* vp = actor->GetProperty();
if (pd->m_Representation < 0)
pd->m_Representation = vp->GetRepresentation();
if (pd->m_Opacity < 0)
pd->m_Opacity = vp->GetOpacity();
if (pd->m_Color.x() < 0) {
double c[3];
vp->GetColor(c);
pd->m_Color = Vector3d(c[0], c[1], c[2]);
}
}
}
@@ -338,12 +356,18 @@ void Puppet::ApplyTransform(vtkProp3D* p3d)
vtkPropCollection *Puppet::GetParts()
{
return pd->m_Assembly->GetParts();
if (auto* asm_p = vtkAssembly::SafeDownCast(pd->m_Prop)) {
return asm_p->GetParts();
}
return nullptr;
}
vtkPropCollection *Puppet::GetProps()
{
return pd->m_Assembly->GetParts();
if (auto* asm_p = vtkAssembly::SafeDownCast(pd->m_Prop)) {
return asm_p->GetParts();
}
return nullptr;
}
void Puppet::ConnectRenderer(vtkRenderer *renderer)
@@ -397,7 +421,8 @@ vtkRendererCollection *Puppet::GetRenderers() const
void Puppet::PrintSelf(std::ostream &o) const
{
o << "Props Assembly: \n";
pd->m_Assembly->PrintSelf(o,vtkIndent(1));
if (pd->m_Prop)
pd->m_Prop->PrintSelf(o,vtkIndent(1));
o << "Connected Renderers: \n";
pd->m_Renderers->PrintSelf(o,vtkIndent(1));
@@ -417,9 +442,11 @@ void Puppet::ShowBoundingBox(bool show)
pd->m_OutlineActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
}
double* bounds = pd->m_Assembly->GetBounds();
pd->m_OutlineSource->SetBounds(bounds);
pd->m_OutlineSource->Update();
if (pd->m_Prop) {
double* bounds = pd->m_Prop->GetBounds();
pd->m_OutlineSource->SetBounds(bounds);
pd->m_OutlineSource->Update();
}
pd->m_Renderers->InitTraversal();
for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) {
@@ -449,8 +476,10 @@ void Puppet::ShowScaleMeasures(bool show)
pd->m_CubeAxesActor->GetProperty()->SetColor(1.0, 1.0, 1.0);
}
double* bounds = pd->m_Assembly->GetBounds();
pd->m_CubeAxesActor->SetBounds(bounds);
if (pd->m_Prop) {
double* bounds = pd->m_Prop->GetBounds();
pd->m_CubeAxesActor->SetBounds(bounds);
}
pd->m_Renderers->InitTraversal();
for (int i = 0; i < pd->m_Renderers->GetNumberOfItems(); ++i) {
@@ -472,12 +501,7 @@ void Puppet::ShowScaleMeasures(bool show)
void Puppet::SetRepresentation(Representation mode)
{
pd->m_Representation = static_cast<int>(mode);
vtkProp3DCollection *props = pd->m_Assembly->GetParts();
props->InitTraversal();
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
pd->ApplyAppearance(props->GetNextProp3D());
}
pd->ApplyAppearance(pd->m_Prop);
}
void Puppet::SetRepresentation(const char *mode)
@@ -497,23 +521,13 @@ void Puppet::SetColor(double r, double g, double b)
pd->m_Color[0] = r;
pd->m_Color[1] = g;
pd->m_Color[2] = b;
vtkProp3DCollection *props = pd->m_Assembly->GetParts();
props->InitTraversal();
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
pd->ApplyAppearance(props->GetNextProp3D());
}
pd->ApplyAppearance(pd->m_Prop);
}
void Puppet::SetOpacity(double alpha)
{
pd->m_Opacity = alpha;
vtkProp3DCollection *props = pd->m_Assembly->GetParts();
props->InitTraversal();
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
pd->ApplyAppearance(props->GetNextProp3D());
}
pd->ApplyAppearance(pd->m_Prop);
}
@@ -525,11 +539,7 @@ void Puppet::SetOpacity(double alpha)
void Puppet::SetSelectable(bool selectable)
{
pd->m_Selectable = selectable;
vtkProp3DCollection *props = pd->m_Assembly->GetParts();
props->InitTraversal();
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
props->GetNextProp3D()->SetPickable(selectable);
}
pd->ApplyAppearance(pd->m_Prop);
}
bool Puppet::IsSelectable() const
@@ -552,38 +562,27 @@ bool Puppet::IsSelected() const
void Puppet::Update()
{
vtkProp* root = this->GetProp();
if (root) {
// Handle transformation synchronization from content
if (auto* content = dynamic_cast<uLib::AffineTransform*>(GetContent())) {
pd->m_Transform = *content; // Uses TRS(const AffineTransform&)
}
// Derived classes should have updated the transform if they override Update()
// or we can apply base transform if it's default:
// pd->ApplyTransform(pd->m_Prop);
if (auto* p3d = vtkProp3D::SafeDownCast(root)) {
pd->ApplyTransform(p3d);
}
pd->ApplyAppearance(root);
}
vtkProp3DCollection *props = pd->m_Assembly->GetParts();
props->InitTraversal();
for (int i = 0; i < props->GetNumberOfItems(); ++i) {
pd->ApplyAppearance(props->GetNextProp3D());
}
pd->ApplyAppearance(pd->m_Prop);
if (pd->m_Selected) {
pd->UpdateHighlight();
}
if (pd->m_ShowBoundingBox) {
double* bounds = pd->m_Assembly->GetBounds();
pd->m_OutlineSource->SetBounds(bounds);
pd->m_OutlineSource->Update();
}
if (pd->m_ShowScaleMeasures) {
double* bounds = pd->m_Assembly->GetBounds();
pd->m_CubeAxesActor->SetBounds(bounds);
if (pd->m_Prop) {
if (pd->m_ShowBoundingBox) {
double* bounds = pd->m_Prop->GetBounds();
pd->m_OutlineSource->SetBounds(bounds);
pd->m_OutlineSource->Update();
}
if (pd->m_ShowScaleMeasures) {
double* bounds = pd->m_Prop->GetBounds();
pd->m_CubeAxesActor->SetBounds(bounds);
}
}
// Notify that the object has been updated (important for UI refresh)
@@ -599,37 +598,6 @@ void Puppet::Update()
}
void Puppet::SyncFromVtk()
{
vtkProp* root = this->GetProp();
if (auto* p3d = vtkProp3D::SafeDownCast(root)) {
// Handle content synchronization if it's an AffineTransform
if (auto* content = dynamic_cast<uLib::AffineTransform*>(GetContent())) {
// 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));
// Sync local TRS from the newly updated model
pd->m_Transform = *content;
}
else {
// 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]);
pd->m_Transform.rotation = Vector3f(ori[0], ori[1], ori[2]) * CLHEP::degree;
pd->m_Transform.scaling = Vector3f(scale[0], scale[1], scale[2]);
}
this->Object::Updated();
}
}
void Puppet::ConnectInteractor(vtkRenderWindowInteractor *interactor)
{
}
@@ -653,7 +621,8 @@ struct AppearanceProxy {
void serialize(Archive & ar, const unsigned int version) {
ar & boost::serialization::make_hrp("Color", pd->m_Color, "color");
ar & boost::serialization::make_hrp("Opacity", pd->m_Opacity).range(0.0, 1.0).set_default(1.0);
ar & boost::serialization::make_hrp_enum("Representation", pd->m_Representation, {"Points", "Wireframe", "Surface", "SurfaceWithEdges", "Volume", "Outline", "Slice"});
ar & boost::serialization::make_hrp_enum("Representation",
pd->m_Representation, {"Points", "Wireframe", "Surface", "SurfaceWithEdges", "Volume", "Outline", "Slice"});
ar & boost::serialization::make_hrp("Visibility", pd->m_Visibility);
ar & boost::serialization::make_hrp("Pickable", pd->m_Selectable);
ar & boost::serialization::make_hrp("Dragable", pd->m_Dragable);

View File

@@ -26,15 +26,15 @@
#ifndef ULIBVTKINTERFACE_H
#define ULIBVTKINTERFACE_H
#include "Core/Monitor.h"
#include "Core/Object.h"
#include "Core/Property.h"
#include <boost/mpl/bool.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/type_traits/is_class.hpp>
#include <iomanip>
#include <ostream>
#include <vector>
#include <boost/type_traits/is_class.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/serialization/serialization.hpp>
#include "Core/Object.h"
#include "Core/Property.h"
#include "Core/Monitor.h"
// vtk classes forward declaration //
class vtkProp;
@@ -46,22 +46,27 @@ class vtkRendererCollection;
class vtkRenderWindowInteractor;
namespace uLib {
namespace Archive { class display_properties_archive; }
namespace Vtk { class Puppet; class Viewer; }
namespace Archive {
class display_properties_archive;
}
namespace Vtk {
class Puppet;
class Viewer;
} // namespace Vtk
} // namespace uLib
namespace uLib {
namespace Vtk {
class Puppet : public uLib::Object {
uLibTypeMacro(Puppet, uLib::Object)
uLibTypeMacro(Puppet, uLib::Object)
public:
Puppet();
public : Puppet();
virtual ~Puppet();
virtual vtkProp *GetProp();
virtual vtkProp3D *GetProxyProp();
virtual vtkPropCollection *GetParts();
@@ -87,10 +92,31 @@ public:
void SetSelected(bool selected = true);
bool IsSelected() const;
/**
* @brief Synchronizes the VTK representation with the internal state and properties.
*
* This method should be called whenever the underlying model or display properties
* are modified to ensure the visual representation in VTK is consistent.
*/
virtual void Update();
virtual void SyncFromVtk();
enum Representation { Points = 0, Wireframe = 1, Surface = 2, SurfaceWithEdges = 3, Volume = 4, Outline = 5, Slice = 6 };
/**
* @brief Synchronizes the internal state and properties from the VTK representation.
*
* This method should be called when the VTK representation has been modified
* (e.g., via a gizmo) and the changes need to be pushed back to the model.
*/
virtual void SyncFromVtk() {}
enum Representation {
Points = 0,
Wireframe = 1,
Surface = 2,
SurfaceWithEdges = 3,
Volume = 4,
Outline = 5,
Slice = 6
};
void SetRepresentation(Representation mode);
void SetRepresentation(const char *mode);
@@ -101,10 +127,15 @@ public:
vtkRendererCollection *GetRenderers() const;
const std::vector<uLib::PropertyBase*>& GetDisplayProperties() const { return m_DisplayProperties; }
void RegisterDisplayProperty(uLib::PropertyBase* prop) { m_DisplayProperties.push_back(prop); }
const std::vector<uLib::PropertyBase *> &GetDisplayProperties() const {
return m_DisplayProperties;
}
void RegisterDisplayProperty(uLib::PropertyBase *prop) {
m_DisplayProperties.push_back(prop);
}
virtual void serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version = 0);
virtual void serialize_display(uLib::Archive::display_properties_archive &ar,
const unsigned int version = 0);
virtual void ConnectInteractor(class vtkRenderWindowInteractor *interactor);
@@ -116,15 +147,15 @@ protected:
void RemoveProp(vtkProp *prop);
void ApplyAppearance(vtkProp* prop);
void ApplyTransform(vtkProp3D* p3d);
void ApplyAppearance(vtkProp *prop);
void ApplyTransform(vtkProp3D *p3d);
std::vector<uLib::PropertyBase*> m_DisplayProperties;
std::vector<uLib::PropertyBase *> m_DisplayProperties;
mutable uLib::RecursiveMutex m_UpdateMutex;
private:
Puppet(const Puppet&) = delete;
Puppet& operator=(const Puppet&) = delete;
Puppet(const Puppet &) = delete;
Puppet &operator=(const Puppet &) = delete;
friend class PuppetData;
class PuppetData *pd;
@@ -133,104 +164,110 @@ private:
} // namespace Vtk
} // namespace uLib
// -------------------------------------------------------------------------- //
// DISPLAY PROPERTIES SERIALIZE
// DISPLAY PROPERTIES SERIALIZE
// -------------------------------------------------------------------------- //
namespace uLib {
namespace Archive {
/**
* @brief Specialized archive for registering display-only properties in Puppets.
* @brief Specialized archive for registering display-only properties in
* Puppets.
*/
class display_properties_archive : public boost::archive::detail::common_oarchive<display_properties_archive> {
class display_properties_archive
: public boost::archive::detail::common_oarchive<
display_properties_archive> {
public:
display_properties_archive(Vtk::Puppet* puppet) :
boost::archive::detail::common_oarchive<display_properties_archive>(boost::archive::no_header),
display_properties_archive(Vtk::Puppet *puppet)
: boost::archive::detail::common_oarchive<display_properties_archive>(
boost::archive::no_header),
m_Puppet(puppet) {}
std::string GetCurrentGroup() const {
std::string group;
for (const auto& g : m_GroupStack) {
if (!group.empty()) group += ".";
group += g;
}
return group;
std::string GetCurrentGroup() const {
std::string group;
for (const auto &g : m_GroupStack) {
if (!group.empty())
group += ".";
group += g;
}
return group;
}
template<class T>
void save_override(const boost::serialization::hrp<T> &t) {
if (m_Puppet) {
uLib::Property<T>* p = new uLib::Property<T>(m_Puppet, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
if (t.has_default()) p->SetDefault(t.default_val());
m_Puppet->RegisterDisplayProperty(p);
Vtk::Puppet* puppet = m_Puppet;
uLib::Object::connect(p, &uLib::PropertyBase::Updated, [puppet](){ puppet->Update(); });
}
template <class T> void save_override(const boost::serialization::hrp<T> &t) {
if (m_Puppet) {
uLib::Property<T> *p = new uLib::Property<T>(
m_Puppet, t.name(),
&const_cast<boost::serialization::hrp<T> &>(t).value(),
t.units() ? t.units() : "", GetCurrentGroup());
if (t.has_range())
p->SetRange(t.min_val(), t.max_val());
if (t.has_default())
p->SetDefault(t.default_val());
m_Puppet->RegisterDisplayProperty(p);
Vtk::Puppet *puppet = m_Puppet;
uLib::Object::connect(p, &uLib::PropertyBase::Updated,
[puppet]() { puppet->Update(); });
}
}
template<class T>
void save_override(const boost::serialization::hrp_enum<T> &t) {
if (m_Puppet) {
uLib::EnumProperty* p = new uLib::EnumProperty(m_Puppet, t.name(), (int*)&const_cast<boost::serialization::hrp_enum<T>&>(t).value(), t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
m_Puppet->RegisterDisplayProperty(p);
Vtk::Puppet* puppet = m_Puppet;
uLib::Object::connect(p, &uLib::PropertyBase::Updated, [puppet](){ puppet->Update(); });
}
template <class T>
void save_override(const boost::serialization::hrp_enum<T> &t) {
if (m_Puppet) {
uLib::EnumProperty *p = new uLib::EnumProperty(
m_Puppet, t.name(),
(int *)&const_cast<boost::serialization::hrp_enum<T> &>(t).value(),
t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
m_Puppet->RegisterDisplayProperty(p);
Vtk::Puppet *puppet = m_Puppet;
uLib::Object::connect(p, &uLib::PropertyBase::Updated,
[puppet]() { puppet->Update(); });
}
}
template<class T> void save_override(const boost::serialization::nvp<T> &t) {
if (t.name()) m_GroupStack.push_back(t.name());
this->save_helper(t.const_value(), typename boost::is_class<T>::type());
if (t.name()) m_GroupStack.pop_back();
}
template <class T> void save_override(const boost::serialization::nvp<T> &t) {
if (t.name())
m_GroupStack.push_back(t.name());
this->save_helper(t.const_value(), typename boost::is_class<T>::type());
if (t.name())
m_GroupStack.pop_back();
}
// Recursion for nested classes, ignore primitives
template<class T> void save_override(const T &t) {
this->save_helper(t, typename boost::is_class<T>::type());
}
// Recursion for nested classes, ignore primitives
template <class T> void save_override(const T &t) {
this->save_helper(t, typename boost::is_class<T>::type());
}
template<class T>
void save_helper(const T &t, boost::mpl::true_) {
boost::serialization::serialize_adl(*this, const_cast<T&>(t), 0);
}
template <class T> void save_helper(const T &t, boost::mpl::true_) {
boost::serialization::serialize_adl(*this, const_cast<T &>(t), 0);
}
template<class T>
void save_helper(const T &t, boost::mpl::false_) {}
template <class T> void save_helper(const T &t, boost::mpl::false_) {}
void save_override(const boost::archive::object_id_type & t) {}
void save_override(const boost::archive::object_reference_type & t) {}
void save_override(const boost::archive::version_type & t) {}
void save_override(const boost::archive::class_id_type & t) {}
void save_override(const boost::archive::class_id_optional_type & t) {}
void save_override(const boost::archive::class_id_reference_type & t) {}
void save_override(const boost::archive::class_name_type & t) {}
void save_override(const boost::archive::tracking_type & t) {}
void save_override(const boost::archive::object_id_type &t) {}
void save_override(const boost::archive::object_reference_type &t) {}
void save_override(const boost::archive::version_type &t) {}
void save_override(const boost::archive::class_id_type &t) {}
void save_override(const boost::archive::class_id_optional_type &t) {}
void save_override(const boost::archive::class_id_reference_type &t) {}
void save_override(const boost::archive::class_name_type &t) {}
void save_override(const boost::archive::tracking_type &t) {}
private:
Vtk::Puppet* m_Puppet;
std::vector<std::string> m_GroupStack;
Vtk::Puppet *m_Puppet;
std::vector<std::string> m_GroupStack;
};
} // namespace Archive
// This macro MUST be defined after both Puppet and display_properties_archive are fully defined.
#define ULIB_ACTIVATE_DISPLAY_PROPERTIES \
{ \
uLib::Archive::display_properties_archive dar(this); \
this->serialize_display(dar, 0); \
}
// This macro MUST be defined after both Puppet and display_properties_archive
// are fully defined.
#define ULIB_ACTIVATE_DISPLAY_PROPERTIES \
{ \
uLib::Archive::display_properties_archive dar(this); \
this->serialize_display(dar, 0); \
}
} // namespace uLib

View File

@@ -547,16 +547,14 @@ void vtkHandlerWidget::OnMouseMove() {
total->Concatenate(op);
if (this->Prop3D) {
double p[3], r[3], s[3];
total->GetPosition(p);
total->GetOrientation(r);
total->GetScale(s);
this->Prop3D->SetPosition(p);
// VTK GetOrientation already returned degrees, so r is in degrees.
// We apply it directly back to VTK.
this->Prop3D->SetOrientation(r);
this->Prop3D->SetScale(s);
this->Prop3D->SetUserMatrix(nullptr);
vtkNew<vtkMatrix4x4> result;
total->GetMatrix(result);
this->Prop3D->SetUserMatrix(result);
// Reset individual TRS components so UserMatrix is the single source of truth
this->Prop3D->SetPosition(0, 0, 0);
this->Prop3D->SetOrientation(0, 0, 0);
this->Prop3D->SetScale(1, 1, 1);
}
this->Prop3D->Modified();

View File

@@ -107,6 +107,12 @@ void vtkObjectsContext::Update() {
}
}
void vtkObjectsContext::SyncFromVtk() {
for (auto const& [obj, puppet] : m_Puppets) {
puppet->SyncFromVtk();
}
}
Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) {
if (!obj) return nullptr;

View File

@@ -30,6 +30,9 @@ public:
/** @brief Updates all managed puppets. */
virtual void Update() override;
/** @brief Synchronizes all managed puppets back to their models. */
virtual void SyncFromVtk() override;
public:
virtual void PuppetAdded(Puppet* puppet);
virtual void PuppetRemoved(Puppet* puppet);

View File

@@ -505,11 +505,11 @@ void Viewport::SelectPuppet(Puppet* prop)
if (pv->m_HandlerWidget) {
if (prop) {
vtkProp3D* prop3d = vtkProp3D::SafeDownCast(prop->GetProp());
vtkProp3D* prop3d = prop->GetProxyProp();
if (prop3d) {
pv->m_HandlerWidget->SetProp3D(prop3d);
pv->m_HandlerWidget->SetEnabled(1);
pv->m_HandlerWidget->PlaceWidget(prop3d->GetBounds());
pv->m_HandlerWidget->PlaceWidget(prop3d->GetBounds()); //TODO: FIX !
}
} else {
pv->m_HandlerWidget->SetEnabled(0);