refactor: update transformation system, improve template readability, and reorganize VTK assembly management
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user