add assembly
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <vtkProp3DCollection.h>
|
||||
#include <vtkCamera.h>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vtkInteractorStyleTrackballCamera.h>
|
||||
#include <vtkObjectFactory.h>
|
||||
#include <vtkAxesActor.h>
|
||||
@@ -29,6 +30,12 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include "vtkHandlerWidget.h"
|
||||
#include "vtkObjectsContext.h"
|
||||
#include "Math/Assembly.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Cylinder.h"
|
||||
#include "Math/Transform.h"
|
||||
#include "Vtk/Math/vtkAssembly.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
@@ -220,32 +227,67 @@ void Viewport::SetupPipeline(vtkRenderWindowInteractor* iren)
|
||||
self->pv->m_Picker->Pick(pos[0], pos[1], 0, self->pv->m_Renderer);
|
||||
vtkProp* picked = self->pv->m_Picker->GetViewProp();
|
||||
|
||||
// 1. Recursive helper to check if a container prop contains a target prop
|
||||
std::function<bool(vtkProp*, vtkProp*)> containsProp;
|
||||
containsProp = [&containsProp](vtkProp* container, vtkProp* target) -> bool {
|
||||
if (container == target) return true;
|
||||
vtkPropCollection* parts = nullptr;
|
||||
if (auto* pa = vtkPropAssembly::SafeDownCast(container))
|
||||
parts = pa->GetParts();
|
||||
else if (auto* aa = vtkAssembly::SafeDownCast(container))
|
||||
parts = aa->GetParts();
|
||||
if (parts) {
|
||||
parts->InitTraversal();
|
||||
for (int i = 0; i < parts->GetNumberOfItems(); ++i) {
|
||||
if (containsProp(parts->GetNextProp(), target))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Puppet* target = nullptr;
|
||||
if (picked) {
|
||||
// 2. Find the leaf puppet: the one that contains 'picked' and is not a parent of another that also contains it.
|
||||
// Actually, we can just find all matches and pick the one with most 'nested' prop?
|
||||
// A simpler way: we know 'picked' is the LEAF prop from VTK.
|
||||
// Find a puppet that contains it.
|
||||
Puppet* leafPuppet = nullptr;
|
||||
for (auto* p : self->m_Puppets) {
|
||||
if (p->GetProp() == picked) {
|
||||
target = p;
|
||||
break;
|
||||
if (containsProp(p->GetProp(), picked)) {
|
||||
// If we already have a candidate, check if this one is smaller (nested)
|
||||
if (!leafPuppet || containsProp(leafPuppet->GetProp(), p->GetProp())) {
|
||||
leafPuppet = p;
|
||||
}
|
||||
}
|
||||
auto* propAssembly = vtkPropAssembly::SafeDownCast(p->GetProp());
|
||||
auto* actorAssembly = vtkAssembly::SafeDownCast(p->GetProp());
|
||||
vtkPropCollection* parts = nullptr;
|
||||
if (propAssembly) parts = propAssembly->GetParts();
|
||||
else if (actorAssembly) parts = actorAssembly->GetParts();
|
||||
}
|
||||
|
||||
if (parts) {
|
||||
bool found = false;
|
||||
parts->InitTraversal();
|
||||
for (int i=0; i<parts->GetNumberOfItems(); ++i) {
|
||||
if (parts->GetNextProp() == picked) {
|
||||
found = true;
|
||||
break;
|
||||
if (leafPuppet) {
|
||||
target = leafPuppet;
|
||||
|
||||
// 3. Model-driven hierarchy climb:
|
||||
// If the leaf puppet has a uLib object, climb its parents.
|
||||
// If any parent is an Assembly with GroupSelection=true, select the assembly puppet instead.
|
||||
uLib::Object* currentObj = leafPuppet->GetContent();
|
||||
|
||||
while (currentObj) {
|
||||
// Object doesn't have parent, but AffineTransform does
|
||||
uLib::Object* parentObj = nullptr;
|
||||
if (auto* at = dynamic_cast<uLib::AffineTransform*>(currentObj)) {
|
||||
parentObj = dynamic_cast<uLib::Object*>(at->GetParent());
|
||||
}
|
||||
|
||||
if (auto* parentAsm = dynamic_cast<::uLib::Assembly*>(parentObj)) {
|
||||
if (parentAsm->GetGroupSelection()) {
|
||||
// Find the puppet for this parent assembly
|
||||
auto it = self->m_ObjectToPuppet.find(parentAsm);
|
||||
if (it != self->m_ObjectToPuppet.end()) {
|
||||
target = it->second;
|
||||
// Keep climbing to find even larger groups
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
target = p;
|
||||
break;
|
||||
}
|
||||
currentObj = parentObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -387,20 +429,74 @@ void Viewport::ZoomSelected()
|
||||
|
||||
void Viewport::AddPuppet(Puppet& prop)
|
||||
{
|
||||
m_Puppets.push_back(&prop);
|
||||
prop.ConnectRenderer(pv->m_Renderer);
|
||||
this->RegisterPuppet(&prop, false);
|
||||
Render();
|
||||
}
|
||||
|
||||
void Viewport::RemovePuppet(Puppet& prop)
|
||||
{
|
||||
if (prop.IsSelected()) SelectPuppet(nullptr);
|
||||
auto it = std::find(m_Puppets.begin(), m_Puppets.end(), &prop);
|
||||
if (it != m_Puppets.end()) m_Puppets.erase(it);
|
||||
prop.DisconnectRenderer(pv->m_Renderer);
|
||||
this->UnregisterPuppet(&prop);
|
||||
Render();
|
||||
}
|
||||
|
||||
void Viewport::RegisterPuppet(Puppet* p, bool isPart) {
|
||||
if (!p) return;
|
||||
if (std::find(m_Puppets.begin(), m_Puppets.end(), p) != m_Puppets.end()) return;
|
||||
|
||||
m_Puppets.push_back(p);
|
||||
p->ConnectRenderer(pv->m_Renderer);
|
||||
|
||||
// If it's a part of an assembly, we don't want to draw it twice.
|
||||
// Assembly itself already draws its parts.
|
||||
// But we need ConnectRenderer above to allow highliting and property updates.
|
||||
if (isPart) {
|
||||
pv->m_Renderer->RemoveViewProp(p->GetProp());
|
||||
}
|
||||
|
||||
// Get the object and register in map
|
||||
uLib::Object* obj = p->GetContent();
|
||||
|
||||
// If it's an assembly, we need to observe its children
|
||||
if (auto* as = dynamic_cast<::uLib::Vtk::Assembly*>(p)) {
|
||||
this->ObserveContext(as->GetChildrenContext());
|
||||
}
|
||||
|
||||
if (obj) m_ObjectToPuppet[obj] = p;
|
||||
}
|
||||
|
||||
void Viewport::UnregisterPuppet(Puppet* p) {
|
||||
if (!p) return;
|
||||
if (p->IsSelected()) SelectPuppet(nullptr);
|
||||
|
||||
auto it = std::find(m_Puppets.begin(), m_Puppets.end(), p);
|
||||
if (it != m_Puppets.end()) m_Puppets.erase(it);
|
||||
|
||||
// Remove from map
|
||||
for (auto mapIt = m_ObjectToPuppet.begin(); mapIt != m_ObjectToPuppet.end(); ) {
|
||||
if (mapIt->second == p) mapIt = m_ObjectToPuppet.erase(mapIt);
|
||||
else ++mapIt;
|
||||
}
|
||||
|
||||
p->DisconnectRenderer(pv->m_Renderer);
|
||||
}
|
||||
|
||||
void Viewport::ObserveContext(vtkObjectsContext* ctx) {
|
||||
if (!ctx) return;
|
||||
|
||||
// Process existing puppets
|
||||
for (auto const& [obj, puppet] : ctx->GetPuppets()) {
|
||||
this->RegisterPuppet(puppet, true);
|
||||
}
|
||||
|
||||
// Listen for future puppets
|
||||
uLib::Object::connect(ctx, &vtkObjectsContext::PuppetAdded, [this](Puppet* p){
|
||||
this->RegisterPuppet(p, true);
|
||||
});
|
||||
uLib::Object::connect(ctx, &vtkObjectsContext::PuppetRemoved, [this](Puppet* p){
|
||||
this->UnregisterPuppet(p);
|
||||
});
|
||||
}
|
||||
|
||||
void Viewport::SelectPuppet(Puppet* prop)
|
||||
{
|
||||
for (auto* p : m_Puppets) {
|
||||
|
||||
Reference in New Issue
Block a user