refactor: migrate vtk classes to use ObjectWrapper for model management and update registration logic

This commit is contained in:
AndreaRigoni
2026-04-09 10:38:45 +00:00
parent 64a87e97e3
commit db76513e79
27 changed files with 479 additions and 349 deletions

View File

@@ -1,53 +1,55 @@
#ifndef U_CORE_OBJECTFACTORY_H
#define U_CORE_OBJECTFACTORY_H
#include <string>
#include <map>
#include <vector>
#include <functional>
#include "Core/Object.h"
#include <functional>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
namespace uLib {
/**
* @brief Singleton factory for dynamic Object instantiation based on class name.
* @brief Singleton factory for dynamic Object instantiation based on class
* name.
*/
class ObjectFactory {
public:
typedef std::function<Object*()> FactoryFunction;
typedef std::function<Object *()> FactoryFunction;
/** @brief Get the singleton instance. */
static ObjectFactory& Instance();
/** @brief Get the singleton instance. */
static ObjectFactory &Instance();
/** @brief Register a factory function for a given class name. */
void Register(const std::string& className, FactoryFunction func);
/** @brief Register a factory function for a given class name. */
void Register(const std::string &className, FactoryFunction func);
/** @brief Create a new instance of the specified class. */
Object* Create(const std::string& className);
/** @brief Create a new instance of the specified class. */
Object *Create(const std::string &className);
/** @brief Get the names of all registered classes. */
std::vector<std::string> GetRegisteredClasses() const;
/** @brief Get the names of all registered classes. */
std::vector<std::string> GetRegisteredClasses() const;
private:
ObjectFactory() = default;
~ObjectFactory() = default;
ObjectFactory() = default;
~ObjectFactory() = default;
// Prevent copy and assignment
ObjectFactory(const ObjectFactory&) = delete;
ObjectFactory& operator=(const ObjectFactory&) = delete;
// Prevent copy and assignment
ObjectFactory(const ObjectFactory &) = delete;
ObjectFactory &operator=(const ObjectFactory &) = delete;
std::map<std::string, FactoryFunction> m_factoryMap;
std::map<std::string, FactoryFunction> m_factoryMap;
};
/**
* @brief Helper class to statically register a factory function.
*/
template <typename T>
class ObjectRegistrar {
template <typename T> class ObjectRegistrar {
public:
ObjectRegistrar(const std::string& className) {
ObjectFactory::Instance().Register(className, []() -> Object* { return new T(); });
}
ObjectRegistrar(const std::string &className) {
ObjectFactory::Instance().Register(className,
[]() -> Object * { return new T(); });
}
};
#define ULIB_REG_CONCAT_IMPL(a, b) a##b
@@ -57,11 +59,63 @@ public:
* @brief Macro to register a class to the factory.
* Put this in the .cpp file of the class.
*/
#define ULIB_REGISTER_OBJECT(className) \
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(#className);
#define ULIB_REGISTER_OBJECT(className) \
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT( \
g_ObjectRegistrar_, __LINE__)(#className);
#define ULIB_REGISTER_OBJECT_NAME(className, registeredName) \
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(registeredName);
#define ULIB_REGISTER_OBJECT_NAME(className, registeredName) \
static uLib::ObjectRegistrar<className> ULIB_REG_CONCAT( \
g_ObjectRegistrar_, __LINE__)(registeredName);
template <typename T> class ObjectWrapper {
public:
ObjectWrapper(const std::string &className) {
ObjectFactory::Instance().Register(className,
[]() -> Object * { return new T(); });
}
ObjectWrapper(T *model) : m_model(model) {}
template <typename U = T,
typename = std::enable_if_t<std::is_default_constructible_v<U>>>
ObjectWrapper() : m_model(new T()) {}
ObjectWrapper(const ObjectWrapper &other) : m_model(other.m_model) {}
ObjectWrapper &operator=(const ObjectWrapper &other) {
m_model = other.m_model;
return *this;
}
ObjectWrapper(ObjectWrapper &&other) noexcept
: m_model(std::move(other.m_model)) {}
ObjectWrapper &operator=(ObjectWrapper &&other) noexcept {
m_model = std::move(other.m_model);
return *this;
}
~ObjectWrapper() = default;
T *operator->() const { return m_model.get(); }
T &operator*() const { return *m_model; }
T *get() const { return m_model.get(); }
bool operator==(const ObjectWrapper &other) const {
return m_model == other.m_model;
}
bool operator!=(const ObjectWrapper &other) const {
return m_model != other.m_model;
}
explicit operator bool() const { return m_model != nullptr; }
protected:
SmartPointer<T> m_model;
};
} // namespace uLib

View File

@@ -28,6 +28,7 @@
#include <atomic>
#include <functional>
#include <type_traits>
#include <utility>
namespace uLib {
@@ -50,7 +51,11 @@ public:
* If ptr is nullptr, a new T is allocated (legacy behavior).
*/
explicit SmartPointer(T* ptr = nullptr) : m_counter(nullptr) {
if (!ptr) ptr = new T();
if (!ptr) {
if constexpr (std::is_default_constructible_v<T>) {
ptr = new T();
}
}
if (ptr) m_counter = new ReferenceCounter(ptr);
}
@@ -156,6 +161,11 @@ public:
*/
T* get() const noexcept { return m_counter ? m_counter->ptr : nullptr; }
/**
* @brief Implicit conversion to raw pointer (legacy compatibility).
*/
operator T*() const noexcept { return get(); }
/**
* @brief Returns the number of SmartPointers sharing ownership.
*/

View File

@@ -3,6 +3,7 @@
set( TESTS
SmartVectorTest
SmartPointerTest
ObjectWrapperTest
VectorTest
ObjectFlagsTest
ObjectParametersTest

View File

@@ -0,0 +1,26 @@
#include "Core/ObjectFactory.h"
#include <iostream>
struct NonDefault {
NonDefault(int) {}
};
struct Default {
Default() : value(42) {}
int value;
};
int main() {
std::cout << "Testing ObjectWrapper with Default Constructible type..." << std::endl;
uLib::ObjectWrapper<Default> w1;
std::cout << "Testing ObjectWrapper with Non-Default Constructible type..." << std::endl;
NonDefault nd(10);
uLib::ObjectWrapper<NonDefault> w2(&nd);
// The following would NOT compile without SFINAE:
// uLib::ObjectWrapper<NonDefault> w3;
std::cout << "Tests passed (compilation and manual instantiation)!" << std::endl;
return 0;
}

View File

@@ -1,12 +1,12 @@
#include "Core/ObjectFactory.h"
#include "Math/Assembly.h"
#include "Math/ContainerBox.h"
#include "Math/Cylinder.h"
#include "Math/Geometry.h"
#include "Math/TriangleMesh.h"
#include "Math/QuadMesh.h"
#include "Math/VoxImage.h"
#include "Math/Assembly.h"
#include "Math/StructuredData.h"
#include "Math/TriangleMesh.h"
#include "Math/VoxImage.h"
namespace uLib {
@@ -14,8 +14,6 @@ ULIB_REGISTER_OBJECT(TRS)
ULIB_REGISTER_OBJECT(ContainerBox)
ULIB_REGISTER_OBJECT(Cylinder)
ULIB_REGISTER_OBJECT(Assembly)
ULIB_REGISTER_OBJECT(CylindricalGeometry)
ULIB_REGISTER_OBJECT(SphericalGeometry)
ULIB_REGISTER_OBJECT(TriangleMesh)
ULIB_REGISTER_OBJECT(QuadMesh)
ULIB_REGISTER_OBJECT_NAME(VoxImage<Voxel>, "VoxImage")

View File

@@ -73,13 +73,13 @@ DetectorChamber::~DetectorChamber() {
}
DetectorChamber::Content *DetectorChamber::GetContent() const {
return static_cast<Content *>(m_Content);
return static_cast<Content *>(this->m_model.get());
}
void DetectorChamber::Update() {
this->BaseClass::Update();
if (!m_Content) return;
if (!this->m_model) return;
Content *c = this->GetContent();
Vector3f size = c->GetSize();
HLine3f plane = c->GetProjectionPlane();

View File

@@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(vtkVoxRaytracerRepresentationTest) {
grid.SetSpacing(Vector3f(1, 1, 1));
grid.SetPosition(Vector3f(0, 0, 0));
Vtk::StructuredGrid v_grid(grid);
Vtk::StructuredGrid v_grid(&grid);
// voxraytracer //
VoxRaytracer rt(grid);

View File

@@ -23,7 +23,6 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkQuadMesh.h"
#include "Vtk/Math/vtkQuadMesh.h"
#include "Vtk/uLibVtkViewer.h"
@@ -39,10 +38,10 @@ BOOST_AUTO_TEST_CASE(vtkQuadMeshConstruction) {
mesh.AddPoint(Vector3f(1, 0, 0));
mesh.AddPoint(Vector3f(1, 1, 0));
mesh.AddPoint(Vector3f(0, 1, 0));
mesh.AddQuad(Vector4i(0, 1, 2, 3));
Vtk::QuadMesh v_mesh(mesh);
Vtk::QuadMesh v_mesh(&mesh);
Object::connect(&mesh, &QuadMesh::Updated, [&mesh]() {
Vector3f points[4];
@@ -50,8 +49,8 @@ BOOST_AUTO_TEST_CASE(vtkQuadMeshConstruction) {
points[1] = mesh.GetPoint(1);
points[2] = mesh.GetPoint(2);
points[3] = mesh.GetPoint(3);
std::cout << "mesh updated: " << points[0] << " " << points[1]
<< " " << points[2] << " " << points[3] << std::endl;
std::cout << "mesh updated: " << points[0] << " " << points[1] << " "
<< points[2] << " " << points[3] << std::endl;
});
v_mesh.Update();

View File

@@ -23,7 +23,6 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkStructuredGrid.h"
#include "Vtk/Math/vtkStructuredGrid.h"
#include "Vtk/uLibVtkViewer.h"
@@ -36,7 +35,7 @@ BOOST_AUTO_TEST_CASE(vtkStructuredGridTest) {
StructuredGrid grid(Vector3i(10, 10, 100));
grid.SetSpacing(Vector3f(3, 1, 1));
Vtk::StructuredGrid grid_viewer(grid);
Vtk::StructuredGrid grid_viewer(&grid);
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {
Vtk::Viewer viewer;

View File

@@ -23,7 +23,6 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkTriangleMesh.h"
#include "Vtk/Math/vtkTriangleMesh.h"
#include "Vtk/uLibVtkViewer.h"
@@ -40,15 +39,16 @@ BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction) {
mesh.AddPoint(Vector3f(1, 0, 0));
mesh.AddTriangle(Vector3i(0, 1, 2));
Vtk::TriangleMesh v_mesh(mesh);
Vtk::TriangleMesh v_mesh(&mesh);
Object::connect(&mesh, &TriangleMesh::Updated, [&mesh]() {
Vector3f points[3];
points[0] = mesh.GetPoint(0);
points[1] = mesh.GetPoint(1);
points[2] = mesh.GetPoint(2);
std::cout << "mesh updated: " << points[0].transpose() << " " << points[1].transpose()
<< " " << points[2].transpose() << std::endl;
std::cout << "mesh updated: " << points[0].transpose() << " "
<< points[1].transpose() << " " << points[2].transpose()
<< std::endl;
});
v_mesh.Update();
@@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction) {
BOOST_AUTO_TEST_CASE(vtkTriangleMeshConstruction2) {
TriangleMesh mesh;
Vtk::TriangleMesh v_mesh(mesh);
Vtk::TriangleMesh v_mesh(&mesh);
v_mesh.ReadFromStlFile("capelluzzo.stl");
v_mesh.Update();

View File

@@ -9,134 +9,140 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkVoxImage.h"
#include "Vtk/Math/vtkVoxImage.h"
#include "Vtk/uLibVtkViewer.h"
#include <vtkSmartPointer.h>
#include <vtkCallbackCommand.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <iostream>
#include <cmath>
#include <iostream>
#include <vtkCallbackCommand.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
using namespace uLib;
struct AppState {
std::vector<Vtk::VoxImage*> images;
Vtk::Viewer* viewer;
std::vector<Vtk::VoxImage *> images;
Vtk::Viewer *viewer;
};
void KeyPressCallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData) {
auto* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
auto* state = static_cast<AppState*>(clientData);
std::string key = interactor->GetKeySym();
if (key == "w") {
std::cout << "--> Switching all images to Wireframe Box" << std::endl;
for (auto* img : state->images) img->SetRepresentation(Vtk::Prop3D::Wireframe);
state->viewer->GetRenderWindow()->Render();
}
else if (key == "s") {
std::cout << "--> Switching all images to Surface (Volume Rendering)" << std::endl;
for (auto* img : state->images) img->SetRepresentation(Vtk::Prop3D::Surface);
state->viewer->GetRenderWindow()->Render();
}
else if (key >= "0" && key <= "5") {
int preset = key[0] - '0';
std::cout << "--> Switching all images to Rendering Preset " << preset << std::endl;
for (auto* img : state->images) img->setShadingPreset(preset);
state->viewer->GetRenderWindow()->Render();
}
void KeyPressCallbackFunction(vtkObject *caller, long unsigned int eventId,
void *clientData, void *callData) {
auto *interactor = static_cast<vtkRenderWindowInteractor *>(caller);
auto *state = static_cast<AppState *>(clientData);
std::string key = interactor->GetKeySym();
if (key == "w") {
std::cout << "--> Switching all images to Wireframe Box" << std::endl;
for (auto *img : state->images)
img->SetRepresentation(Vtk::Prop3D::Wireframe);
state->viewer->GetRenderWindow()->Render();
} else if (key == "s") {
std::cout << "--> Switching all images to Surface (Volume Rendering)"
<< std::endl;
for (auto *img : state->images)
img->SetRepresentation(Vtk::Prop3D::Surface);
state->viewer->GetRenderWindow()->Render();
} else if (key >= "0" && key <= "5") {
int preset = key[0] - '0';
std::cout << "--> Switching all images to Rendering Preset " << preset
<< std::endl;
for (auto *img : state->images)
img->setShadingPreset(preset);
state->viewer->GetRenderWindow()->Render();
}
}
int main(int argc, char** argv) {
float factor = 1.0e6f;
int main(int argc, char **argv) {
float factor = 1.0e6f;
// --- Image 1: Spherical Shell ---
Vector3i dims1(64, 64, 64);
VoxImage<Voxel> img1(dims1);
img1.SetSpacing(Vector3f(1.0, 1.0, 1.0));
img1.SetPosition(Vector3f(-40, -32, -32));
// --- Image 1: Spherical Shell ---
Vector3i dims1(64, 64, 64);
VoxImage<Voxel> img1(dims1);
img1.SetSpacing(Vector3f(1.0, 1.0, 1.0));
img1.SetPosition(Vector3f(-40, -32, -32));
for (int z = 0; z < dims1(2); ++z) {
for (int y = 0; y < dims1(1); ++y) {
for (int x = 0; x < dims1(0); ++x) {
float dx = x - 32.0f;
float dy = y - 32.0f;
float dz = z - 32.0f;
float dist = std::sqrt(dx*dx + dy*dy + dz*dz);
Voxel v;
if (dist < 25.0f && dist > 10.0f) {
v.Value = (40.0f * (25.0f - dist) / 15.0f) / factor;
} else {
v.Value = 0.0f;
}
img1[Vector3i(x, y, z)] = v;
}
for (int z = 0; z < dims1(2); ++z) {
for (int y = 0; y < dims1(1); ++y) {
for (int x = 0; x < dims1(0); ++x) {
float dx = x - 32.0f;
float dy = y - 32.0f;
float dz = z - 32.0f;
float dist = std::sqrt(dx * dx + dy * dy + dz * dz);
Voxel v;
if (dist < 25.0f && dist > 10.0f) {
v.Value = (40.0f * (25.0f - dist) / 15.0f) / factor;
} else {
v.Value = 0.0f;
}
img1[Vector3i(x, y, z)] = v;
}
}
}
// --- Image 2: Axes Gradient ---
Vector3i dims2(64, 64, 64);
VoxImage<Voxel> img2(dims2);
img2.SetSpacing(Vector3f(1.0, 1.0, 1.0));
img2.SetPosition(Vector3f(40, -32, -32));
// --- Image 2: Axes Gradient ---
Vector3i dims2(64, 64, 64);
VoxImage<Voxel> img2(dims2);
img2.SetSpacing(Vector3f(1.0, 1.0, 1.0));
img2.SetPosition(Vector3f(40, -32, -32));
for (int z = 0; z < dims2(2); ++z) {
for (int y = 0; y < dims2(1); ++y) {
for (int x = 0; x < dims2(0); ++x) {
Voxel v;
// Linear gradient along X, Y, Z
float val = (float(x)/dims2(0) + float(y)/dims2(1) + float(z)/dims2(2)) / 3.0f;
v.Value = (40.0f * val) / factor;
img2[Vector3i(x, y, z)] = v;
}
}
for (int z = 0; z < dims2(2); ++z) {
for (int y = 0; y < dims2(1); ++y) {
for (int x = 0; x < dims2(0); ++x) {
Voxel v;
// Linear gradient along X, Y, Z
float val =
(float(x) / dims2(0) + float(y) / dims2(1) + float(z) / dims2(2)) /
3.0f;
v.Value = (40.0f * val) / factor;
img2[Vector3i(x, y, z)] = v;
}
}
}
Vtk::VoxImage vtk_img1(img1);
vtk_img1.setShadingPreset(0);
Vtk::VoxImage vtk_img1(&img1);
vtk_img1.setShadingPreset(0);
Vtk::VoxImage vtk_img2(img2);
vtk_img2.setShadingPreset(1); // Use Composite without MIP for variety
Vtk::VoxImage vtk_img2(&img2);
vtk_img2.setShadingPreset(1); // Use Composite without MIP for variety
Vtk::Viewer viewer;
viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1);
viewer.AddProp3D(vtk_img1);
viewer.AddProp3D(vtk_img2);
// Setup KeyPress Callback
AppState state;
state.images.push_back(&vtk_img1);
state.images.push_back(&vtk_img2);
state.viewer = &viewer;
Vtk::Viewer viewer;
viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1);
viewer.AddProp3D(vtk_img1);
viewer.AddProp3D(vtk_img2);
vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
keyCallback->SetCallback(KeyPressCallbackFunction);
keyCallback->SetClientData(&state);
viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
// Setup KeyPress Callback
AppState state;
state.images.push_back(&vtk_img1);
state.images.push_back(&vtk_img2);
state.viewer = &viewer;
std::cout << "=========================================" << std::endl;
std::cout << " VoxImage Interactive Viewer Test" << std::endl;
std::cout << " [LEFT] Spherical Shell (MIP)" << std::endl;
std::cout << " [RIGHT] Axes Gradient (Composite)" << std::endl;
std::cout << "-----------------------------------------" << std::endl;
std::cout << " Press [w] to show Wireframe Bounding Boxes" << std::endl;
std::cout << " Press [s] to show Volume Rendering" << std::endl;
std::cout << " Press [0..5] to switch Rendering Presets:" << std::endl;
std::cout << " 0: MIP (Grayscale)" << std::endl;
std::cout << " 1: Composite (Grayscale)" << std::endl;
std::cout << " 2: Composite (Shaded)" << std::endl;
std::cout << " 3: CT Bone/Tissue (Bone colors)" << std::endl;
std::cout << " 4: MIP (Rainbow)" << std::endl;
std::cout << " 5: Additive (Total path sum)" << std::endl;
std::cout << " Press [q] to quit" << std::endl;
std::cout << "=========================================" << std::endl;
vtkSmartPointer<vtkCallbackCommand> keyCallback =
vtkSmartPointer<vtkCallbackCommand>::New();
keyCallback->SetCallback(KeyPressCallbackFunction);
keyCallback->SetClientData(&state);
viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
viewer.ZoomAuto();
viewer.Start();
std::cout << "=========================================" << std::endl;
std::cout << " VoxImage Interactive Viewer Test" << std::endl;
std::cout << " [LEFT] Spherical Shell (MIP)" << std::endl;
std::cout << " [RIGHT] Axes Gradient (Composite)" << std::endl;
std::cout << "-----------------------------------------" << std::endl;
std::cout << " Press [w] to show Wireframe Bounding Boxes" << std::endl;
std::cout << " Press [s] to show Volume Rendering" << std::endl;
std::cout << " Press [0..5] to switch Rendering Presets:" << std::endl;
std::cout << " 0: MIP (Grayscale)" << std::endl;
std::cout << " 1: Composite (Grayscale)" << std::endl;
std::cout << " 2: Composite (Shaded)" << std::endl;
std::cout << " 3: CT Bone/Tissue (Bone colors)" << std::endl;
std::cout << " 4: MIP (Rainbow)" << std::endl;
std::cout << " 5: Additive (Total path sum)" << std::endl;
std::cout << " Press [q] to quit" << std::endl;
std::cout << "=========================================" << std::endl;
return 0;
viewer.ZoomAuto();
viewer.Start();
return 0;
}

View File

@@ -23,7 +23,6 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkVoxImage.h"
#include "Vtk/Math/vtkVoxImage.h"
#include "Vtk/uLibVtkViewer.h"
@@ -46,7 +45,7 @@ BOOST_AUTO_TEST_CASE(vtkVoxImageConstruction) {
img.InitVoxels(zero);
img[Vector3i(3, 3, 3)] = nonzero;
Vtk::VoxImage vtk_img(img);
Vtk::VoxImage vtk_img(&img);
vtk_img.SaveToXMLFile("test_vtkvoximage.vti");
if (std::getenv("CTEST_PROJECT_NAME") == nullptr) {

View File

@@ -31,14 +31,14 @@ namespace Vtk {
// ------------------------------------------------------------------ //
Assembly::Assembly(uLib::Assembly *content)
: m_Content(content),
: ObjectWrapper(content),
m_ChildContext(nullptr),
m_BBoxActor(nullptr),
m_VtkAsm(nullptr),
m_InUpdate(false) {
this->InstallPipe();
if (m_Content) {
Object::connect(m_Content, &uLib::Assembly::Updated,
if (this->m_model) {
Object::connect(this->m_model.get(), &uLib::Assembly::Updated,
this, &Assembly::Update);
}
}
@@ -71,13 +71,13 @@ void Assembly::InstallPipe() {
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_BBoxActor->SetVisibility(this->m_model ? this->m_model->GetShowBoundingBox() : false);
m_VtkAsm->AddPart(m_BBoxActor);
// 3. Build a child-objects context (auto-creates prop3ds for each child)
if (m_Content) {
m_ChildContext = new ObjectsContext(m_Content);
if (this->m_model) {
m_ChildContext = new ObjectsContext(this->m_model);
// Link the children context's assembly into our group assembly
if (auto* childProp = vtkProp3D::SafeDownCast(m_ChildContext->GetProp())) {
m_VtkAsm->AddPart(childProp);
@@ -93,10 +93,10 @@ void Assembly::Update() {
if (m_InUpdate) return;
m_InUpdate = true;
if (m_Content && m_VtkAsm) {
if (this->m_model && m_VtkAsm) {
// Apply world matrix from the assembly content
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m);
Matrix4fToVtk(this->m_model->GetMatrix(), m);
m_VtkAsm->SetUserMatrix(m);
m_VtkAsm->Modified();
}
@@ -110,33 +110,33 @@ void Assembly::Update() {
void Assembly::SyncFromVtk() {
if (m_InUpdate) return;
if (!m_Content || !m_VtkAsm) return;
if (!this->m_model || !m_VtkAsm) return;
m_InUpdate = true;
// 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->m_model->SetWorldMatrix(VtkToMatrix4f(proxy->GetUserMatrix()));
this->m_model->FromMatrix(this->m_model->GetMatrix());
}
this->UpdateBoundingBox();
if (m_ChildContext)
m_ChildContext->SyncFromVtk();
m_Content->Updated(); // Notify change in model
this->m_model->Updated(); // Notify change in model
m_InUpdate = false;
}
// ------------------------------------------------------------------ //
void Assembly::UpdateBoundingBox() {
if (!m_Content || !m_BBoxActor) return;
if (!this->m_model || !m_BBoxActor) return;
m_BBoxActor->SetVisibility(m_Content->GetShowBoundingBox());
m_BBoxActor->SetVisibility(this->m_model->GetShowBoundingBox());
Vector3f bbMin, bbMax;
m_Content->GetBoundingBox(bbMin, bbMax);
this->m_model->GetBoundingBox(bbMin, bbMax);
// Avoid degenerate boxes
Vector3f size = bbMax - bbMin;

View File

@@ -12,8 +12,9 @@
#ifndef U_VTK_ASSEMBLY_H
#define U_VTK_ASSEMBLY_H
#include "Core/ObjectFactory.h"
#include "Math/Assembly.h"
#include "uLibVtkInterface.h"
#include "Vtk/uLibVtkInterface.h"
class vtkActor;
class vtkAssembly; // VTK library forward declaration (must be before namespace)
@@ -34,7 +35,7 @@ class ObjectsContext; // forward
* the VTK library class vtkAssembly for grouping, but the two
* are distinct.
*/
class Assembly : public Prop3D {
class Assembly : public Prop3D, public uLib::ObjectWrapper<uLib::Assembly> {
public:
uLibTypeMacro(Assembly, Prop3D)
@@ -47,8 +48,8 @@ public:
/** @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; }
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_Content; }
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_model.get(); }
virtual uLib::ObjectsContext* GetChildren() override { return (uLib::ObjectsContext*)m_model.get(); }
/**
* @brief Returns the prop3d managing child objects.
@@ -61,7 +62,6 @@ private:
void UpdateBoundingBox();
void InstallPipe();
uLib::Assembly *m_Content;
ObjectsContext *m_ChildContext;
vtkActor *m_BBoxActor;
::vtkAssembly *m_VtkAsm; // VTK library assembly — NOT this class

View File

@@ -66,9 +66,10 @@ struct ContainerBoxData {
ContainerBox::ContainerBox(ContainerBox::Content *content)
: d(new ContainerBoxData()), m_Content(content) {
: d(new ContainerBoxData()), ObjectWrapper(content) {
this->InstallPipe();
d->m_UpdateSignal = Object::connect(m_Content, &uLib::Object::Updated, this, &ContainerBox::Update);
d->m_UpdateSignal =
Object::connect(this->m_model.get(), &uLib::Object::Updated, this, &ContainerBox::Update);
}
ContainerBox::~ContainerBox() {
@@ -83,13 +84,13 @@ vtkPolyData *ContainerBox::GetPolyData() const {
void ContainerBox::Update() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content) return;
if (!this->m_model) return;
vtkProp3D* prop = vtkProp3D::SafeDownCast(this->GetProp());
if (prop) {
// Apply the full volume matrix (TRS * m_LocalT)
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m);
Matrix4fToVtk(this->m_model->GetMatrix(), m);
prop->SetUserMatrix(m);
prop->Modified();
}
@@ -104,7 +105,7 @@ void ContainerBox::Update() {
void ContainerBox::SyncFromVtk() {
RecursiveMutex::ScopedLock lock(this->m_UpdateMutex);
if (!m_Content) return;
if (!this->m_model) return;
vtkProp3D* root = this->GetProxyProp();
if (!root) return;
@@ -114,11 +115,11 @@ void ContainerBox::SyncFromVtk() {
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
// Synchronize TRS property members from the updated local matrix
m_Content->FromMatrix(vtkWorld);
this->m_model->FromMatrix(vtkWorld);
// Since we modified the model, notify observers, but block the loop back to VTK
// ConnectionBlock blocker(d->m_UpdateSignal);
m_Content->Updated();
this->m_model->Updated();
}
@@ -126,9 +127,9 @@ void ContainerBox::SyncFromVtk() {
void ContainerBox::InstallPipe() {
if (!m_Content)
if (!this->m_model)
return;
Content *c = m_Content;
Content *c = this->m_model;
// CUBE
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();

View File

@@ -26,6 +26,7 @@
#ifndef U_VTKCONTAINERBOX_H
#define U_VTKCONTAINERBOX_H
#include "Core/ObjectFactory.h"
#include "Math/ContainerBox.h"
#include "uLibVtkInterface.h"
#include "vtkPolydata.h"
@@ -37,11 +38,13 @@ namespace Vtk {
struct ContainerBoxData;
class ContainerBox : public Prop3D, public Polydata {
class ContainerBox : public Prop3D,
public Polydata,
public uLib::ObjectWrapper<uLib::ContainerBox> {
uLibTypeMacro(ContainerBox, Prop3D, Polydata)
typedef uLib::ContainerBox Content;
public:
ContainerBox(Content *content);
~ContainerBox();
@@ -58,14 +61,15 @@ public:
*/
virtual void SyncFromVtk() override;
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
virtual uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
protected:
virtual void InstallPipe();
struct ContainerBoxData *d;
uLib::ContainerBox *m_Content;
ULIB_DECLARE_PROPERTIES(ContainerBox)
};

View File

@@ -38,9 +38,9 @@ namespace uLib {
namespace Vtk {
Cylinder::Cylinder(Cylinder::Content *content)
: m_Content(content), m_Actor(nullptr), m_VtkAsm(nullptr) {
: ObjectWrapper(content), m_Actor(nullptr), m_VtkAsm(nullptr) {
this->InstallPipe();
m_UpdateSignal = Object::connect(m_Content, &uLib::Object::Updated, this, &Cylinder::Update);
m_UpdateSignal = Object::connect(this->m_model.get(), &uLib::Object::Updated, this, &Cylinder::Update);
}
Cylinder::~Cylinder() {
@@ -49,14 +49,14 @@ Cylinder::~Cylinder() {
}
void Cylinder::Update() {
if (!m_Content)
if (!this->m_model)
return;
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (root) {
// 1. Placement handled specifically from content (use TRS GetMatrix, not World)
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content->GetMatrix(), m);
Matrix4fToVtk(this->m_model->GetMatrix(), m);
root->SetUserMatrix(m);
// 2. Shape-local properties (Radius, Height, Axis alignment) go to the internal actor
@@ -68,10 +68,10 @@ void Cylinder::Update() {
// 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());
alignment->Scale(this->m_model->GetRadius(), this->m_model->GetHeight(), this->m_model->GetRadius());
// Apply Axis alignment
int axis = m_Content->GetAxis();
int axis = this->m_model->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
@@ -86,7 +86,7 @@ void Cylinder::Update() {
}
void Cylinder::SyncFromVtk() {
if (!m_Content) return;
if (!this->m_model) return;
vtkProp3D* root = this->GetProxyProp();
if (!root) return;
@@ -96,12 +96,12 @@ void Cylinder::SyncFromVtk() {
Matrix4f vtkWorld = VtkToMatrix4f(rootMat);
// Directly sync model from the world matrix
m_Content->FromMatrix(vtkWorld);
m_Content->Updated();
this->m_model->FromMatrix(vtkWorld);
this->m_model->Updated();
}
void Cylinder::InstallPipe() {
if (!m_Content)
if (!this->m_model)
return;
m_VtkAsm = ::vtkAssembly::New();

View File

@@ -26,6 +26,7 @@
#ifndef U_VTKCYLINDER_H
#define U_VTKCYLINDER_H
#include "Core/ObjectFactory.h"
#include "Math/Cylinder.h"
#include "Vtk/uLibVtkInterface.h"
#include <vtkActor.h>
@@ -41,7 +42,7 @@ namespace Vtk {
* mathematical state of a Cylinder object. It manages the alignment
* between VTK's Y-centered cylinder and uLib's Z-based coordinate system.
*/
class Cylinder : public Prop3D {
class Cylinder : public Prop3D, public uLib::ObjectWrapper<uLib::Cylinder> {
typedef uLib::Cylinder Content;
public:
@@ -54,7 +55,9 @@ public:
/** Synchronizes the uLib model matrix with the VTK actor specifically for gizmo interactions */
virtual void SyncFromVtk() override;
virtual uLib::Object* GetContent() const override { return (uLib::Object*)m_Content; }
virtual uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
protected:
/** Sets up the VTK visualization pipeline */
@@ -62,7 +65,6 @@ protected:
vtkActor *m_Actor;
::vtkAssembly *m_VtkAsm;
Content *m_Content;
uLib::Connection m_UpdateSignal;
};

View File

@@ -58,22 +58,22 @@ void QuadMesh::vtk2uLib_update() {
<< "number of quads = " << number_of_quads << "\n"
<< "//////\n";
m_content.Points().clear();
this->m_model->Points().clear();
for (int i = 0; i < number_of_points; ++i) {
double *point = m_Poly->GetPoint(i);
m_content.Points().push_back(Vector3f(point[0], point[1], point[2]));
this->m_model->Points().push_back(Vector3f(point[0], point[1], point[2]));
}
m_content.Quads().resize(number_of_quads);
this->m_model->Quads().resize(number_of_quads);
m_Poly->GetPolys()->InitTraversal();
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New();
for (int i = 0; i < number_of_quads; ++i) {
m_Poly->GetPolys()->GetNextCell(idList);
if (idList->GetNumberOfIds() == 4) {
m_content.Quads()[i](0) = idList->GetId(0);
m_content.Quads()[i](1) = idList->GetId(1);
m_content.Quads()[i](2) = idList->GetId(2);
m_content.Quads()[i](3) = idList->GetId(3);
this->m_model->Quads()[i](0) = idList->GetId(0);
this->m_model->Quads()[i](1) = idList->GetId(1);
this->m_model->Quads()[i](2) = idList->GetId(2);
this->m_model->Quads()[i](3) = idList->GetId(3);
}
}
m_Poly->Modified();
@@ -81,23 +81,23 @@ void QuadMesh::vtk2uLib_update() {
}
void QuadMesh::uLib2vtk_update() {
vtkIdType number_of_points = m_content.Points().size();
vtkIdType number_of_quads = m_content.Quads().size();
vtkIdType number_of_points = this->m_model->Points().size();
vtkIdType number_of_quads = this->m_model->Quads().size();
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(number_of_points);
for (vtkIdType i = 0; i < number_of_points; i++) {
Vector3f p = m_content.Points().at(i);
Vector3f p = this->m_model->Points().at(i);
points->SetPoint(i, p(0), p(1), p(2));
}
vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
for (vtkIdType i = 0; i < number_of_quads; i++) {
vtkIdType a, b, c, d;
a = m_content.Quads().at(i)(0);
b = m_content.Quads().at(i)(1);
c = m_content.Quads().at(i)(2);
d = m_content.Quads().at(i)(3);
a = this->m_model->Quads().at(i)(0);
b = this->m_model->Quads().at(i)(1);
c = this->m_model->Quads().at(i)(2);
d = this->m_model->Quads().at(i)(3);
polys->InsertNextCell(4);
polys->InsertCellPoint(a);
polys->InsertCellPoint(b);
@@ -118,7 +118,7 @@ void QuadMesh::contentUpdate() {
vmat = mat;
}
Matrix4f transform = m_content.GetWorldMatrix();
Matrix4f transform = this->m_model->GetWorldMatrix();
Matrix4fToVtk(transform, vmat);
uLib2vtk_update();
@@ -133,30 +133,30 @@ void QuadMesh::Update() {
if (!vmat) return;
Matrix4f transform = VtkToMatrix4f(vmat);
m_content.SetMatrix(transform);
m_content.Updated();
this->m_model->SetMatrix(transform);
this->m_model->Updated();
}
// -------------------------------------------------------------------------- //
QuadMesh::QuadMesh(QuadMesh::Content &content)
: m_content(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
QuadMesh::QuadMesh(QuadMesh::Content *content)
: ObjectWrapper(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(m_Poly);
m_Actor->SetMapper(mapper);
vtkNew<vtkMatrix4x4> vmat;
Matrix4fToVtk(m_content.GetWorldMatrix(), vmat);
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
m_Actor->SetUserMatrix(vmat);
this->SetProp(m_Actor);
Object::connect(&m_content, &Content::Updated, this, &QuadMesh::contentUpdate);
Object::connect(this->m_model.get(), &Content::Updated, this, &QuadMesh::contentUpdate);
this->contentUpdate();
}
QuadMesh::~QuadMesh() {
Object::disconnect(&m_content, &Content::Updated, this, &QuadMesh::contentUpdate);
Object::disconnect(this->m_model.get(), &Content::Updated, this, &QuadMesh::contentUpdate);
m_Poly->Delete();
m_Actor->Delete();
}

View File

@@ -26,9 +26,10 @@
#ifndef VTKQUADMESH_H
#define VTKQUADMESH_H
#include "Core/ObjectFactory.h"
#include "Math/QuadMesh.h"
#include "Vtk/uLibVtkInterface.h"
#include "Vtk/Math/vtkPolydata.h"
#include "Vtk/uLibVtkInterface.h"
class vtkPolyData;
class vtkActor;
@@ -36,11 +37,13 @@ class vtkActor;
namespace uLib {
namespace Vtk {
class QuadMesh : public Prop3D, public Polydata {
class QuadMesh : public Prop3D,
public Polydata,
public uLib::ObjectWrapper<uLib::QuadMesh> {
typedef uLib::QuadMesh Content;
public:
QuadMesh(Content &content);
QuadMesh(Content *content);
~QuadMesh();
void ReadFromFile(const char *filename);
@@ -56,12 +59,15 @@ public:
virtual void contentUpdate();
virtual void Update();
uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
private:
void vtk2uLib_update();
void uLib2vtk_update();
uLib::QuadMesh &m_content;
// ObjectWrapper provides m_model
vtkPolyData *m_Poly;
vtkActor *m_Actor;
};

View File

@@ -39,8 +39,8 @@ namespace Vtk {
////// VTK STRUCTURED GRID /////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
StructuredGrid::StructuredGrid(Content &content)
: m_Content(&content), m_Actor(vtkActor::New()),
StructuredGrid::StructuredGrid(Content *content)
: ObjectWrapper(content), m_Actor(vtkActor::New()),
m_Transform(vtkTransform::New()) {
this->InstallPipe();
@@ -54,10 +54,10 @@ StructuredGrid::~StructuredGrid() {
void StructuredGrid::SetTransform(vtkTransform *t) {
vtkMatrix4x4 *vmat = t->GetMatrix();
Matrix4f mat = VtkToMatrix4f(vmat);
m_Content->SetMatrix(mat);
this->m_model->SetMatrix(mat);
vtkSmartPointer<vtkMatrix4x4> vmat2 = vtkSmartPointer<vtkMatrix4x4>::New();
mat = m_Content->GetWorldMatrix();
mat = this->m_model->GetWorldMatrix();
Matrix4fToVtk(mat, vmat2);
m_Transform->SetMatrix(vmat2);
m_Transform->Update();
@@ -65,7 +65,7 @@ void StructuredGrid::SetTransform(vtkTransform *t) {
}
void StructuredGrid::Update() {
if (!m_Content) return;
if (!this->m_model) return;
vtkProp3D* actor = vtkProp3D::SafeDownCast(this->GetProp());
if (!actor) return;
@@ -76,20 +76,20 @@ void StructuredGrid::Update() {
Matrix4f transform = VtkToMatrix4f(vmat);
// Update uLib model's affine transform
if (m_Content->GetParent()) {
Matrix4f localT = m_Content->GetParent()->GetWorldMatrix().inverse() * transform;
m_Content->SetMatrix(localT);
if (this->m_model->GetParent()) {
Matrix4f localT = this->m_model->GetParent()->GetWorldMatrix().inverse() * transform;
this->m_model->SetMatrix(localT);
} else {
m_Content->SetMatrix(transform);
this->m_model->SetMatrix(transform);
}
m_Content->Updated(); // Notify others (like raytracer)
this->m_model->Updated(); // Notify others (like raytracer)
}
void StructuredGrid::InstallPipe() {
vtkSmartPointer<vtkCubeSource> cube = vtkSmartPointer<vtkCubeSource>::New();
Vector3i dims = m_Content->GetDims();
Vector3i dims = this->m_model->GetDims();
cube->SetBounds(0, dims(0), 0, dims(1), 0, dims(2));
cube->Update();
@@ -104,7 +104,7 @@ void StructuredGrid::InstallPipe() {
m_Actor->GetProperty()->SetAmbient(0.7);
vtkNew<vtkMatrix4x4> vmat;
Matrix4fToVtk(m_Content->GetWorldMatrix(), vmat);
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
m_Actor->SetUserMatrix(vmat);
this->SetProp(m_Actor);

View File

@@ -41,28 +41,32 @@
#include "Vtk/Math/vtkDense.h"
#include "Core/ObjectFactory.h"
#include "Math/StructuredGrid.h"
#include "Vtk/uLibVtkInterface.h"
namespace uLib {
namespace Vtk {
class StructuredGrid : public Prop3D {
class StructuredGrid : public Prop3D,
public uLib::ObjectWrapper<uLib::StructuredGrid> {
typedef uLib::StructuredGrid Content;
public:
StructuredGrid(Content &content);
StructuredGrid(Content *content);
~StructuredGrid();
void SetTransform(class vtkTransform *t);
virtual void Update() override;
uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
private:
void InstallPipe();
vtkActor *m_Actor;
uLib::StructuredGrid *m_Content;
vtkTransform *m_Transform;
};

View File

@@ -58,42 +58,42 @@ void TriangleMesh::vtk2uLib_update() {
<< "number of polys = " << number_of_triangles << "\n"
<< "//////\n";
m_content.Points().clear();
this->m_model->Points().clear();
for (int i = 0; i < number_of_points; ++i) {
double *point = m_Poly->GetPoint(i);
m_content.Points().push_back(Vector3f(point[0], point[1], point[2]));
this->m_model->Points().push_back(Vector3f(point[0], point[1], point[2]));
}
m_content.Triangles().resize(number_of_triangles);
this->m_model->Triangles().resize(number_of_triangles);
m_Poly->GetPolys()->InitTraversal();
vtkSmartPointer<vtkIdList> idList = vtkSmartPointer<vtkIdList>::New();
for (int i = 0; i < number_of_triangles; ++i) {
m_Poly->GetPolys()->GetNextCell(idList);
m_content.Triangles()[i](0) = idList->GetId(0);
m_content.Triangles()[i](1) = idList->GetId(1);
m_content.Triangles()[i](2) = idList->GetId(2);
this->m_model->Triangles()[i](0) = idList->GetId(0);
this->m_model->Triangles()[i](1) = idList->GetId(1);
this->m_model->Triangles()[i](2) = idList->GetId(2);
}
m_Poly->Modified();
m_Actor->GetMapper()->Update();
}
void TriangleMesh::uLib2vtk_update() {
vtkIdType number_of_points = m_content.Points().size();
vtkIdType number_of_triangles = m_content.Triangles().size();
vtkIdType number_of_points = this->m_model->Points().size();
vtkIdType number_of_triangles = this->m_model->Triangles().size();
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
points->SetNumberOfPoints(number_of_points);
for (vtkIdType i = 0; i < number_of_points; i++) {
Vector3f p = m_content.Points().at(i);
Vector3f p = this->m_model->Points().at(i);
points->SetPoint(i, p(0), p(1), p(2));
}
vtkSmartPointer<vtkCellArray> polys = vtkSmartPointer<vtkCellArray>::New();
for (vtkIdType i = 0; i < number_of_triangles; i++) {
vtkIdType a, b, c;
a = m_content.Triangles().at(i)(0);
b = m_content.Triangles().at(i)(1);
c = m_content.Triangles().at(i)(2);
a = this->m_model->Triangles().at(i)(0);
b = this->m_model->Triangles().at(i)(1);
c = this->m_model->Triangles().at(i)(2);
polys->InsertNextCell(3);
polys->InsertCellPoint(a);
polys->InsertCellPoint(b);
@@ -113,7 +113,7 @@ void TriangleMesh::contentUpdate() {
vmat = mat;
}
Matrix4f transform = m_content.GetWorldMatrix();
Matrix4f transform = this->m_model->GetWorldMatrix();
Matrix4fToVtk(transform, vmat);
uLib2vtk_update();
@@ -128,30 +128,30 @@ void TriangleMesh::Update() {
if (!vmat) return;
Matrix4f transform = VtkToMatrix4f(vmat);
m_content.SetMatrix(transform);
m_content.Updated();
this->m_model->SetMatrix(transform);
this->m_model->Updated();
}
// -------------------------------------------------------------------------- //
TriangleMesh::TriangleMesh(TriangleMesh::Content &content)
: m_content(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
TriangleMesh::TriangleMesh(TriangleMesh::Content *content)
: ObjectWrapper(content), m_Poly(vtkPolyData::New()), m_Actor(vtkActor::New()) {
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(m_Poly);
m_Actor->SetMapper(mapper);
vtkNew<vtkMatrix4x4> vmat;
Matrix4fToVtk(m_content.GetWorldMatrix(), vmat);
Matrix4fToVtk(this->m_model->GetWorldMatrix(), vmat);
m_Actor->SetUserMatrix(vmat);
this->SetProp(m_Actor);
Object::connect(&m_content, &Content::Updated, this, &TriangleMesh::contentUpdate);
Object::connect(this->m_model.get(), &Content::Updated, this, &TriangleMesh::contentUpdate);
this->contentUpdate();
}
TriangleMesh::~TriangleMesh() {
Object::disconnect(&m_content, &Content::Updated, this, &TriangleMesh::contentUpdate);
Object::disconnect(this->m_model.get(), &Content::Updated, this, &TriangleMesh::contentUpdate);
m_Poly->Delete();
m_Actor->Delete();
}

View File

@@ -26,9 +26,10 @@
#ifndef VTKTRIANGLEMESH_H
#define VTKTRIANGLEMESH_H
#include "Core/ObjectFactory.h"
#include "Math/TriangleMesh.h"
#include "Vtk/uLibVtkInterface.h"
#include "Vtk/Math/vtkPolydata.h"
#include "Vtk/uLibVtkInterface.h"
class vtkPolyData;
class vtkActor;
@@ -36,11 +37,13 @@ class vtkActor;
namespace uLib {
namespace Vtk {
class TriangleMesh : public Prop3D, public Polydata {
class TriangleMesh : public Prop3D,
public Polydata,
public uLib::ObjectWrapper<uLib::TriangleMesh> {
typedef uLib::TriangleMesh Content;
public:
TriangleMesh(Content &content);
TriangleMesh(Content *content);
~TriangleMesh();
void ReadFromFile(const char *filename);
@@ -56,12 +59,15 @@ public:
virtual void contentUpdate();
virtual void Update();
uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
private:
void vtk2uLib_update();
void uLib2vtk_update();
uLib::TriangleMesh &m_content;
// ObjectWrapper provides m_model
vtkPolyData *m_Poly;
vtkActor *m_Actor;
};

View File

@@ -61,13 +61,13 @@ namespace uLib {
namespace Vtk {
void VoxImage::UpdateFromContent() {
Vector3i ev_dims = m_Content.GetDims();
Vector3i ev_dims = this->m_model->GetDims();
m_Image->SetDimensions(ev_dims.data());
Vector3f ev_spacing = m_Content.GetSpacing();
Vector3f ev_spacing = this->m_model->GetSpacing();
m_Image->SetSpacing(ev_spacing(0), ev_spacing(1), ev_spacing(2));
Vector3f ev_pos = m_Content.GetPosition();
Vector3f ev_pos = this->m_model->GetPosition();
m_Image->SetOrigin(ev_pos(0), ev_pos(1), ev_pos(2));
vtkFloatArray *array =
@@ -78,14 +78,14 @@ void VoxImage::UpdateFromContent() {
array->Delete();
}
array->SetNumberOfTuples(m_Content.GetDims().prod());
array->SetNumberOfTuples(this->m_model->GetDims().prod());
Vector3i index(0, 0, 0);
int i = 0;
for (int zv = 0; zv < ev_dims(2); ++zv) {
for (int yv = 0; yv < ev_dims(1); ++yv) {
for (int xv = 0; xv < ev_dims(0); ++xv) {
index << xv, yv, zv;
array->SetValue(i++, m_Content.GetValue(index));
array->SetValue(i++, this->m_model->GetValue(index));
}
}
}
@@ -94,13 +94,13 @@ void VoxImage::UpdateFromContent() {
void VoxImage::UpdateToContent() {
int *ext = m_Image->GetExtent();
int dims[3] = {ext[1] - ext[0] + 1, ext[3] - ext[2] + 1, ext[5] - ext[4] + 1};
m_Content.SetDims(Vector3i(dims[0], dims[1], dims[2]));
this->m_model->SetDims(Vector3i(dims[0], dims[1], dims[2]));
double *spacing = m_Image->GetSpacing();
m_Content.SetSpacing(Vector3f(spacing[0], spacing[1], spacing[2]));
this->m_model->SetSpacing(Vector3f(spacing[0], spacing[1], spacing[2]));
double *pos = m_Image->GetOrigin();
m_Content.SetPosition(Vector3f(pos[0], pos[1], pos[2]));
this->m_model->SetPosition(Vector3f(pos[0], pos[1], pos[2]));
vtkFloatArray *array =
vtkFloatArray::SafeDownCast(m_Image->GetPointData()->GetScalars());
@@ -111,7 +111,7 @@ void VoxImage::UpdateToContent() {
for (int yv = 0; yv < dims[1]; ++yv) {
for (int xv = 0; xv < dims[0]; ++xv) {
index << xv, yv, zv;
m_Content.SetValue(index, array->GetValue(i++));
this->m_model->SetValue(index, array->GetValue(i++));
}
}
}
@@ -124,8 +124,8 @@ void VoxImage::UpdateToContent() {
////////////////////////////////////////////////////////////////////////////////
// VTK VOXIMAGE
VoxImage::VoxImage(Content &content)
: m_Content(content), m_Actor(vtkVolume::New()),
VoxImage::VoxImage(Content *content)
: ObjectWrapper(content), m_Actor(vtkVolume::New()),
m_Asm(vtkAssembly::New()),
m_Image(vtkImageData::New()), m_Outline(vtkCubeSource::New()),
m_OutlineActor(vtkActor::New()),
@@ -134,7 +134,7 @@ VoxImage::VoxImage(Content &content)
// Transfer functions
m_ColorFun = vtkColorTransferFunction::New();
m_OpacityFun = vtkPiecewiseFunction::New();
m_UpdateConnection = Object::connect(&m_Content, &uLib::Object::Updated, this, &VoxImage::Update);
m_UpdateConnection = Object::connect(this->m_model.get(), &uLib::Object::Updated, this, &VoxImage::Update);
UpdateFromContent();
InstallPipe();
@@ -314,8 +314,8 @@ void VoxImage::SyncFromVtk() {
if (rootMat) {
Matrix4f vtkLocal = VtkToMatrix4f(rootMat);
// Synchronize TRS from VTK, compensating for local volume offset
m_Content.FromMatrix(vtkLocal); // * m_Content.GetLocalMatrix().inverse());
m_Content.Updated();
this->m_model->FromMatrix(vtkLocal); // * this->m_model->GetLocalMatrix().inverse());
this->m_model->Updated();
}
}
}
@@ -323,11 +323,11 @@ void VoxImage::SyncFromVtk() {
void VoxImage::Update() {
if (auto *root = vtkProp3D::SafeDownCast(this->GetProp())) {
vtkNew<vtkMatrix4x4> m;
Matrix4fToVtk(m_Content.GetMatrix(), m); // * m_Content.GetLocalMatrix(), m);
Matrix4fToVtk(this->m_model->GetMatrix(), m); // * this->m_model->GetLocalMatrix(), m);
root->SetUserMatrix(m);
root->Modified();
// std::cout << "[VoxImage::Update] Set Proxy UserMatrix:" << std::endl;
// std::cout << m_Content.GetMatrix() << std::endl;
// std::cout << this->m_model->GetMatrix() << std::endl;
}
setShadingPreset(m_ShadingPreset);
m_Actor->Update();

View File

@@ -26,15 +26,16 @@
#ifndef U_VTKVOXIMAGE_H
#define U_VTKVOXIMAGE_H
#include <vtkAssembly.h>
#include <vtkCubeSource.h>
#include <vtkImageData.h>
#include <vtkVolume.h>
#include <vtkXMLImageDataReader.h>
#include <vtkXMLImageDataWriter.h>
#include <vtkAssembly.h>
#include <Math/VoxImage.h>
#include "Core/ObjectFactory.h"
#include "Vtk/uLibVtkInterface.h"
class vtkImageData;
@@ -45,18 +46,21 @@ class vtkPiecewiseFunction;
namespace uLib {
namespace Vtk {
class VoxImage : public Prop3D {
class VoxImage : public Prop3D,
public uLib::ObjectWrapper<uLib::Abstract::VoxImage> {
public:
typedef Abstract::VoxImage Content;
VoxImage(Content &content);
VoxImage(Content *content);
~VoxImage();
void UpdateFromContent();
void UpdateToContent();
uLib::Object* GetContent() const override { return (uLib::Object*)&m_Content; }
uLib::Object *GetContent() const override {
return (uLib::Object *)m_model.get();
}
vtkProp3D *GetProp() override { return m_Asm; }
@@ -74,7 +78,8 @@ public:
void Update() override;
void SyncFromVtk() override;
void serialize_display(uLib::Archive::display_properties_archive & ar, const unsigned int version = 0) override;
void serialize_display(uLib::Archive::display_properties_archive &ar,
const unsigned int version = 0) override;
protected:
void InstallPipe();
@@ -90,7 +95,7 @@ private:
vtkXMLImageDataReader *m_Reader;
vtkXMLImageDataWriter *m_Writer;
VoxImage::Content &m_Content;
// ObjectWrapper provides m_model
float m_Window;
float m_Level;

View File

@@ -1,24 +1,23 @@
#include "vtkObjectsContext.h"
#include "Vtk/Math/vtkAssembly.h"
#include "Vtk/Math/vtkContainerBox.h"
#include "Vtk/Math/vtkCylinder.h"
#include "Vtk/Math/vtkAssembly.h"
#include "Vtk/Math/vtkVoxImage.h"
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
#include "Vtk/HEP/Geant/vtkBoxSolid.h"
#include <cstring>
#include <iostream>
#include <vtkAssembly.h>
#include <vtkPropCollection.h>
#include <iostream>
#include <cstring>
#include "Math/ContainerBox.h"
#include "Math/Cylinder.h"
#include "Math/Assembly.h"
#include "Math/VoxImage.h"
#include "HEP/Detectors/DetectorChamber.h"
#include "HEP/Geant/Solid.h"
#include "Math/Assembly.h"
#include "Math/ContainerBox.h"
#include "Math/Cylinder.h"
#include "Math/VoxImage.h"
namespace uLib {
namespace Vtk {
@@ -27,35 +26,41 @@ ObjectsContext::ObjectsContext(uLib::ObjectsContext *context)
: m_Context(context), m_Assembly(::vtkAssembly::New()) {
this->SetProp(m_Assembly);
if (m_Context) {
Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this, &ObjectsContext::OnObjectAdded);
Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this, &ObjectsContext::OnObjectRemoved);
Object::connect(m_Context, &uLib::ObjectsContext::ObjectAdded, this,
&ObjectsContext::OnObjectAdded);
Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this,
&ObjectsContext::OnObjectRemoved);
this->Synchronize();
}
}
ObjectsContext::~ObjectsContext() {
for (auto const& [obj, prop3d] : m_Prop3Ds) {
for (auto const &[obj, prop3d] : m_Prop3Ds) {
delete prop3d;
}
m_Assembly->Delete();
}
void ObjectsContext::Synchronize() {
if (!m_Context) return;
if (!m_Context)
return;
// 1. Identify objects to add and remove
const auto& objects = m_Context->GetObjects();
std::map<uLib::Object*, bool> currentObjects;
for (auto obj : objects) currentObjects[obj] = true;
const auto &objects = m_Context->GetObjects();
std::map<uLib::Object *, bool> currentObjects;
for (auto obj : objects)
currentObjects[obj] = true;
// Remove Prop3Ds for objects no longer in context
for (auto it = m_Prop3Ds.begin(); it != m_Prop3Ds.end(); ) {
for (auto it = m_Prop3Ds.begin(); it != m_Prop3Ds.end();) {
if (currentObjects.find(it->first) == currentObjects.end()) {
it->second->DisconnectRenderer(nullptr); // If we have a ref to a renderer we should disconnect but Prop3D doesn't store it easily
it->second->DisconnectRenderer(
nullptr); // If we have a ref to a renderer we should disconnect but
// Prop3D doesn't store it easily
// Actually Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer.
// For now we just remove from assembly
if (auto* p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
m_Assembly->RemovePart(p3d);
if (auto *p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
m_Assembly->RemovePart(p3d);
this->Prop3DRemoved(it->second);
delete it->second;
it = m_Prop3Ds.erase(it);
@@ -67,92 +72,97 @@ void ObjectsContext::Synchronize() {
// Add Prop3Ds for new objects
for (auto obj : objects) {
if (m_Prop3Ds.find(obj) == m_Prop3Ds.end()) {
Prop3D* prop3d = this->CreateProp3D(obj);
Prop3D *prop3d = this->CreateProp3D(obj);
if (prop3d) {
m_Prop3Ds[obj] = prop3d;
if (auto* p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
m_Assembly->AddPart(p3d);
if (auto *p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
m_Assembly->AddPart(p3d);
this->Prop3DAdded(prop3d);
}
}
}
}
void ObjectsContext::OnObjectAdded(uLib::Object* obj) {
if (!obj) return;
void ObjectsContext::OnObjectAdded(uLib::Object *obj) {
if (!obj)
return;
if (m_Prop3Ds.find(obj) == m_Prop3Ds.end()) {
Prop3D* prop3d = this->CreateProp3D(obj);
Prop3D *prop3d = this->CreateProp3D(obj);
if (prop3d) {
m_Prop3Ds[obj] = prop3d;
if (auto* p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
m_Assembly->AddPart(p3d);
if (auto *p3d = vtkProp3D::SafeDownCast(prop3d->GetProp()))
m_Assembly->AddPart(p3d);
this->Prop3DAdded(prop3d);
}
}
}
void ObjectsContext::OnObjectRemoved(uLib::Object* obj) {
if (!obj) return;
auto it = m_Prop3Ds.find(obj);
if (it != m_Prop3Ds.end()) {
// For now we just remove from assembly.
// Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer, but we don't have it here easily.
if (auto* p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
m_Assembly->RemovePart(p3d);
this->Prop3DRemoved(it->second);
delete it->second;
m_Prop3Ds.erase(it);
}
void ObjectsContext::OnObjectRemoved(uLib::Object *obj) {
if (!obj)
return;
auto it = m_Prop3Ds.find(obj);
if (it != m_Prop3Ds.end()) {
// For now we just remove from assembly.
// Prop3D::DisconnectRenderer(vtkRenderer*) needs the renderer, but we don't
// have it here easily.
if (auto *p3d = vtkProp3D::SafeDownCast(it->second->GetProp()))
m_Assembly->RemovePart(p3d);
this->Prop3DRemoved(it->second);
delete it->second;
m_Prop3Ds.erase(it);
}
}
Prop3D* ObjectsContext::GetProp3D(uLib::Object* obj) {
Prop3D *ObjectsContext::GetProp3D(uLib::Object *obj) {
auto it = m_Prop3Ds.find(obj);
if (it != m_Prop3Ds.end()) return it->second;
if (it != m_Prop3Ds.end())
return it->second;
return nullptr;
}
void ObjectsContext::Update() {
for (auto const& [obj, prop3d] : m_Prop3Ds) {
for (auto const &[obj, prop3d] : m_Prop3Ds) {
prop3d->Update();
}
}
void ObjectsContext::SyncFromVtk() {
for (auto const& [obj, prop3d] : m_Prop3Ds) {
for (auto const &[obj, prop3d] : m_Prop3Ds) {
prop3d->SyncFromVtk();
}
}
Prop3D* ObjectsContext::CreateProp3D(uLib::Object* obj) {
if (!obj) return nullptr;
Prop3D *ObjectsContext::CreateProp3D(uLib::Object *obj) {
if (!obj)
return nullptr;
if (auto* vox = dynamic_cast<uLib::Abstract::VoxImage*>(obj)) {
return new VoxImage(*vox);
} else if (auto* box = dynamic_cast<uLib::ContainerBox*>(obj)) {
if (auto *vox = dynamic_cast<uLib::Abstract::VoxImage *>(obj)) {
return new VoxImage(vox);
} else if (auto *box = dynamic_cast<uLib::ContainerBox *>(obj)) {
return new ContainerBox(box);
} else if (auto* chamber = dynamic_cast<uLib::DetectorChamber*>(obj)) {
} else if (auto *chamber = dynamic_cast<uLib::DetectorChamber *>(obj)) {
return new DetectorChamber(chamber);
} else if (auto* cylinder = dynamic_cast<uLib::Cylinder*>(obj)) {
} else if (auto *cylinder = dynamic_cast<uLib::Cylinder *>(obj)) {
return new Cylinder(cylinder);
} else if (auto* assembly = dynamic_cast<uLib::Assembly*>(obj)) {
} else if (auto *assembly = dynamic_cast<uLib::Assembly *>(obj)) {
return new Assembly(assembly);
} else if (auto* box = dynamic_cast<uLib::Geant::BoxSolid*>(obj)) {
} else if (auto *box = dynamic_cast<uLib::Geant::BoxSolid *>(obj)) {
return new BoxSolid(box);
}
// Fallback if we don't know the exact class but it might be a context itself
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
return new ObjectsContext(subCtx);
if (auto subCtx = dynamic_cast<uLib::ObjectsContext *>(obj)) {
return new ObjectsContext(subCtx);
}
return nullptr;
}
void ObjectsContext::Prop3DAdded(Prop3D* prop3d) {
void ObjectsContext::Prop3DAdded(Prop3D *prop3d) {
ULIB_SIGNAL_EMIT(ObjectsContext::Prop3DAdded, prop3d);
}
void ObjectsContext::Prop3DRemoved(Prop3D* prop3d) {
void ObjectsContext::Prop3DRemoved(Prop3D *prop3d) {
ULIB_SIGNAL_EMIT(ObjectsContext::Prop3DRemoved, prop3d);
}