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