add cylinder

This commit is contained in:
AndreaRigoni
2026-03-24 15:22:50 +00:00
parent b45cde0bad
commit 51e6dbb4f5
11 changed files with 523 additions and 121 deletions

View File

@@ -75,7 +75,8 @@ void Object::RegisterProperty(PropertyBase* prop) {
void Object::RegisterDynamicProperty(PropertyBase* prop) {
if (prop) {
d->m_DynamicProperties.push_back(prop);
d->m_Properties.push_back(prop);
// Note: prop already added itself to m_Properties
// during its own constructor call to owner->RegisterProperty()
}
}
@@ -114,8 +115,9 @@ template void Object::serialize(Archive::log_archive &, const unsigned int);
// OBJECT IMPLEMENTATION
Object::Object() : d(new ObjectPrivate) {}
Object::Object(const Object &copy) : d(new ObjectPrivate(*copy.d)) {}
Object::Object(const Object &copy) : d(new ObjectPrivate) {
if (copy.d) d->m_InstanceName = copy.d->m_InstanceName;
}
Object::~Object() {
for (auto* p : d->m_DynamicProperties) {
@@ -125,9 +127,11 @@ Object::~Object() {
}
void Object::DeepCopy(const Object &copy) {
// should lock to be tread safe //
memcpy(d, copy.d, sizeof(ObjectPrivate));
// ERROR! does not copy parameters ... <<<< FIXXXXX
if (this == &copy) return;
if (copy.d) d->m_InstanceName = copy.d->m_InstanceName;
// Note: signals, slots and properties are intentionally not copied
// to maintain instance uniquely and avoid duplicate registrations.
this->Updated();
}
void Object::SaveXml(std::ostream &os, Object &ob) {

View File

@@ -126,6 +126,12 @@ private:
G4Box *m_Solid;
};
} // namespace Geant
} // namespace uLib

View File

@@ -1,5 +1,6 @@
set(HEADERS ContainerBox.h
Cylinder.h
Dense.h
Geometry.h
Transform.h
@@ -70,4 +71,3 @@ if(BUILD_TESTING)
include(uLibTargetMacros)
add_subdirectory(testing)
endif()

View File

@@ -51,10 +51,8 @@ class ContainerBox : public AffineTransform, public Object {
public:
////////////////////////////////////////////////////////////////////////////
// PROPERTIES //
Property<float> Width;
Property<float> Height;
Property<float> Depth;
Property<Vector3f> p_Size;
Property<Vector3f> p_Origin;
virtual const char * GetClassName() const { return "ContainerBox"; }
/**
@@ -63,12 +61,10 @@ public:
*/
ContainerBox()
: m_LocalT(this), // BaseClass is Parent of m_LocalTransform
Width(this, "Width", 1.0f),
Height(this, "Height", 1.0f),
Depth(this, "Depth", 1.0f) {
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
p_Size(this, "Size", Vector3f(1.0f, 1.0f, 1.0f)),
p_Origin(this, "Origin", Vector3f(0.0f, 0.0f, 0.0f)) {
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
}
/**
@@ -77,12 +73,11 @@ public:
*/
ContainerBox(const Vector3f &size)
: m_LocalT(this),
Width(this, "Width", size(0)),
Height(this, "Height", size(1)),
Depth(this, "Depth", size(2)) {
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
p_Size(this, "Size", size),
p_Origin(this, "Origin", Vector3f(0.0f, 0.0f, 0.0f)) {
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
this->SetSize(size);
}
/**
@@ -92,20 +87,20 @@ public:
ContainerBox(const ContainerBox &copy)
: m_LocalT(this), // BaseClass is Parent of m_LocalTransform
AffineTransform(copy),
Width(this, "Width", copy.Width),
Height(this, "Height", copy.Height),
Depth(this, "Depth", copy.Depth) {
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
this->SetOrigin(copy.GetOrigin());
p_Size(this, "Size", copy.p_Size),
p_Origin(this, "Origin", copy.p_Origin) {
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
}
/**
* @brief Sets the box origin relative to its coordinate system.
* @param v The origin position vector.
*/
void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
void SetOrigin(const Vector3f &v) {
p_Origin = v;
m_LocalT.SetPosition(v);
}
/**
* @brief Gets the box origin relative to its coordinate system.
@@ -119,9 +114,7 @@ public:
* @param v The size vector (width, height, depth).
*/
void SetSize(const Vector3f &v) {
Width = v(0);
Height = v(1);
Depth = v(2);
p_Size = v;
Vector3f pos = this->GetOrigin();
m_LocalT = AffineTransform(this); // regenerate local transform
m_LocalT.Scale(v);
@@ -213,11 +206,17 @@ signals:
// signal to emit when the box is updated //
virtual void Updated() override { ULIB_SIGNAL_EMIT(ContainerBox::Updated); }
private:
private slots:
void SyncSize() {
this->SetSize(Vector3f(Width, Height, Depth));
this->SetSize(p_Size);
}
void SyncOrigin() {
this->SetOrigin(p_Origin);
}
private:
AffineTransform m_LocalT;
};

View File

@@ -34,32 +34,32 @@
namespace uLib {
/**
* @brief Represents a cylindrical volume centered in the base circle.
* @brief Represents a cylindrical volume.
*
* Cylinder inherits from AffineTransform, which defines its parent
* coordinate system. It contains an internal local transformation (m_LocalT)
* that defines the cylinder's actual volume (radius and height)
* relative to the emitter's origin (base circle center).
* The cylinder orientation is defined by the Axis property (0=X, 1=Y, 2=Z).
* By default, it is aligned with the Y axis (Axis=1).
*/
class Cylinder : public AffineTransform, public Object {
typedef AffineTransform BaseClass;
public:
uLibTypeMacro(Cylinder, Object)
virtual const char * GetClassName() const { return "Cylinder"; }
virtual const char * GetClassName() const override { return "Cylinder"; }
/**
* @brief Default constructor.
* Initializes with radius 1 and height 1.
* @brief Default constructor. Aligns with Y by default.
*/
Cylinder() : m_LocalT(this), m_Radius(1.0), m_Height(1.0) {
Cylinder() : m_LocalT(this), Radius(1.0), Height(1.0), Axis(1) {
ULIB_ACTIVATE_PROPERTIES(*this);
UpdateLocalMatrix();
}
/**
* @brief Constructor with radius and height.
*/
Cylinder(float radius, float height) : m_LocalT(this), m_Radius(radius), m_Height(height) {
Cylinder(float radius, float height, int axis = 1)
: m_LocalT(this), Radius(radius), Height(height), Axis(axis) {
ULIB_ACTIVATE_PROPERTIES(*this);
UpdateLocalMatrix();
}
@@ -67,75 +67,115 @@ public:
* @brief Copy constructor.
*/
Cylinder(const Cylinder &copy)
: m_LocalT(this), AffineTransform(copy) {
this->SetRadius(copy.GetRadius());
this->SetHeight(copy.GetHeight());
: m_LocalT(this), AffineTransform(copy), Radius(copy.Radius), Height(copy.Height), Axis(copy.Axis) {
ULIB_ACTIVATE_PROPERTIES(*this);
this->UpdateLocalMatrix();
}
/**
* @brief Serialization template for property registration and persistence.
*/
template <class ArchiveT>
void serialize(ArchiveT & ar, const unsigned int version) {
ar & HRP(Radius);
ar & HRP(Height);
ar & HRP(Axis);
}
/** Sets the radius of the cylinder */
inline void SetRadius(float r) {
m_Radius = r;
Radius = r;
UpdateLocalMatrix();
}
/** Gets the radius of the cylinder */
inline float GetRadius() const { return m_Radius; }
inline float GetRadius() const { return Radius; }
/** Sets the height of the cylinder */
inline void SetHeight(float h) {
m_Height = h;
Height = h;
UpdateLocalMatrix();
}
/** Gets the height of the cylinder */
inline float GetHeight() const { return m_Height; }
inline float GetHeight() const { return Height; }
/** Sets the main axis (0=X, 1=Y, 2=Z) */
inline void SetAxis(int axis) {
Axis = axis;
UpdateLocalMatrix();
}
/** Gets the main axis */
inline int GetAxis() const { return Axis; }
/**
* @brief Returns the world transformation matrix of the cylinder's volume.
* @brief Returns the world transformation matrix.
*/
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
/**
* @brief Returns the local transformation matrix of the cylinder's volume.
* @brief Returns the local transformation matrix.
*/
Matrix4f GetLocalMatrix() const { return m_LocalT.GetMatrix(); }
/**
* @brief Transforms local cylindrical coordinates to world space.
* @param r Local radius (absolute).
* @param theta Local angle in radians.
* @param z Local height (absolute, relative to base circle).
* @return Transformed point in world space.
* @param r Local radius.
* @param theta Local angle in radians (around main axis).
* @param h Local height along main axis.
*/
inline Vector4f GetWorldPoint(float r, float theta, float z) const {
return BaseClass::GetWorldMatrix() * Vector4f(r * std::cos(theta), r * std::sin(theta), z, 1.0f);
inline Vector4f GetWorldPoint(float r, float theta, float h) const {
Vector3f p;
if (Axis == 0) p = Vector3f(h, r * std::cos(theta), r * std::sin(theta));
else if (Axis == 1) p = Vector3f(r * std::cos(theta), h, r * std::sin(theta));
else p = Vector3f(r * std::cos(theta), r * std::sin(theta), h);
return AffineTransform::GetWorldMatrix() * Vector4f(p.x(), p.y(), p.z(), 1.0f);
}
/**
* @brief Transforms a world point to cylindrical local space.
* @return Vector3f(r, theta, z)
* @return Vector3f(r, theta, h)
*/
inline Vector3f GetCylindricalLocal(const Vector4f &world_v) const {
Vector4f local_v = BaseClass::GetWorldMatrix().inverse() * world_v;
float r = std::sqrt(local_v.x() * local_v.x() + local_v.y() * local_v.y());
float theta = std::atan2(local_v.y(), local_v.x());
return Vector3f(r, theta, local_v.z());
Vector4f local_v = AffineTransform::GetWorldMatrix().inverse() * world_v;
float r, theta, h;
if (Axis == 0) {
h = local_v.x();
r = std::sqrt(local_v.y() * local_v.y() + local_v.z() * local_v.z());
theta = std::atan2(local_v.z(), local_v.y());
} else if (Axis == 1) {
h = local_v.y();
r = std::sqrt(local_v.x() * local_v.x() + local_v.z() * local_v.z());
theta = std::atan2(local_v.z(), local_v.x());
} else {
h = local_v.z();
r = std::sqrt(local_v.x() * local_v.x() + local_v.y() * local_v.y());
theta = std::atan2(local_v.y(), local_v.x());
}
return Vector3f(r, theta, h);
}
signals:
/** Signal emitted when the cylinder geometry or transform is updated */
virtual void Updated() override { ULIB_SIGNAL_EMIT(Cylinder::Updated); }
private:
/** Recalculates the internal local matrix based on radius and height */
void UpdateLocalMatrix() {
m_LocalT = AffineTransform(this); // BaseClass is parent
m_LocalT.Scale(Vector3f(m_Radius, m_Radius, m_Height));
this->Updated();
/** Signal emitted when properties change */
virtual void Updated() override {
this->UpdateLocalMatrix();
ULIB_SIGNAL_EMIT(Cylinder::Updated);
}
float m_Radius;
float m_Height;
private:
/** Recalculates the internal local matrix based on dimensions and axis */
void UpdateLocalMatrix() {
m_LocalT = AffineTransform(this);
if (Axis == 0) m_LocalT.Scale(Vector3f(Height, Radius, Radius));
else if (Axis == 1) m_LocalT.Scale(Vector3f(Radius, Height, Radius));
else m_LocalT.Scale(Vector3f(Radius, Radius, Height));
}
float Radius;
float Height;
int Axis;
AffineTransform m_LocalT;
};

View File

@@ -8,6 +8,7 @@ set(MATH_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/vtkTriangleMesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/vtkQuadMesh.cpp
${CMAKE_CURRENT_SOURCE_DIR}/vtkVoxImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/vtkCylinder.cpp
PARENT_SCOPE)
set(MATH_HEADERS
@@ -16,6 +17,7 @@ set(MATH_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/vtkTriangleMesh.h
${CMAKE_CURRENT_SOURCE_DIR}/vtkQuadMesh.h
${CMAKE_CURRENT_SOURCE_DIR}/vtkVoxImage.h
${CMAKE_CURRENT_SOURCE_DIR}/vtkCylinder.h
PARENT_SCOPE)
if(BUILD_TESTING)

View File

@@ -0,0 +1,144 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include "Vtk/Math/vtkCylinder.h"
#include <vtkActor.h>
#include <vtkCylinderSource.h>
#include <vtkMatrix4x4.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkSmartPointer.h>
#include <vtkTransform.h>
#include "Math/vtkDense.h"
namespace uLib {
namespace Vtk {
vtkCylinder::vtkCylinder(vtkCylinder::Content *content)
: m_Actor(vtkActor::New()), m_Content(content) {
this->InstallPipe();
Object::connect(m_Content, &Content::Updated, this, &vtkCylinder::contentUpdate);
}
vtkCylinder::~vtkCylinder() {
m_Actor->Delete();
}
void vtkCylinder::contentUpdate() {
if (!m_Content)
return;
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (!root) return;
vtkMatrix4x4* vmat = root->GetUserMatrix();
if (!vmat) {
vtkNew<vtkMatrix4x4> mat;
root->SetUserMatrix(mat);
vmat = mat;
}
// Multiply the placement matrix by the volume scaling (Radius, Radius, Height)
Matrix4f transform = m_Content->GetMatrix() * m_Content->GetLocalMatrix();
Matrix4fToVtk(transform, vmat);
// Update internal alignment based on active axis
vtkTransform* alignment = vtkTransform::SafeDownCast(m_Actor->GetUserTransform());
if (alignment) {
alignment->Identity();
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
// We keep it centered as per latest user preference in Step 677
// alignment->Translate(0, 0, 0); // Implicit
}
root->Modified();
Puppet::Update();
}
void vtkCylinder::Update() {
if (!m_Content) return;
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (!root) return;
vtkMatrix4x4* vmat = root->GetUserMatrix();
if (!vmat) return;
Matrix4f fullTransform = VtkToMatrix4f(vmat);
Matrix4f placementScale = m_Content->GetLocalMatrix().inverse();
Matrix4f transform = fullTransform * placementScale;
if (m_Content->GetParent()) {
Matrix4f localT = m_Content->GetParent()->GetWorldMatrix().inverse() * transform;
m_Content->SetMatrix(localT);
} else {
m_Content->SetMatrix(transform);
}
m_Content->Updated();
}
void vtkCylinder::InstallPipe() {
if (!m_Content)
return;
vtkNew<vtkCylinderSource> cylinder;
cylinder->SetRadius(1.0);
cylinder->SetHeight(1.0);
cylinder->SetResolution(32);
vtkNew<vtkTransform> alignment;
alignment->Identity();
alignment->Translate(0, 0, -0.5);
// Default to Y alignment (Identity) as per latest request
int axis = m_Content->GetAxis();
if (axis == 0) alignment->RotateZ(-90);
else if (axis == 2) alignment->RotateX(90);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(cylinder->GetOutputPort());
m_Actor->SetMapper(mapper);
m_Actor->SetUserTransform(alignment);
m_Actor->GetProperty()->SetRepresentationToWireframe();
m_Actor->GetProperty()->SetAmbient(0.6);
this->SetProp(m_Actor);
vtkProp3D* root = vtkProp3D::SafeDownCast(this->GetProp());
if (root) {
vtkNew<vtkMatrix4x4> vmat;
Matrix4fToVtk(m_Content->GetMatrix() * m_Content->GetLocalMatrix(), vmat);
root->SetUserMatrix(vmat);
}
}
} // namespace Vtk
} // namespace uLib

View File

@@ -0,0 +1,67 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef U_VTKCYLINDER_H
#define U_VTKCYLINDER_H
#include "Math/Cylinder.h"
#include "Vtk/uLibVtkInterface.h"
#include <vtkActor.h>
namespace uLib {
namespace Vtk {
/**
* @brief VTK representation of the uLib::Cylinder object.
*
* This class wraps a vtkCylinderSource and synchronizes it with the
* mathematical state of a Cylinder object. It manages the alignment
* between VTK's Y-centered cylinder and uLib's Z-based coordinate system.
*/
class vtkCylinder : public Puppet {
typedef Cylinder Content;
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) */
virtual void Update();
protected:
/** Sets up the VTK visualization pipeline */
virtual void InstallPipe();
vtkActor *m_Actor;
Content *m_Content;
};
} // namespace Vtk
} // namespace uLib
#endif // U_VTKCYLINDER_H

View File

@@ -1,5 +1,6 @@
#include "vtkObjectsContext.h"
#include "vtkContainerBox.h"
#include "Vtk/vtkContainerBox.h"
#include "Vtk/Math/vtkCylinder.h"
#include "HEP/Detectors/vtkDetectorChamber.h"
#include <vtkAssembly.h>
@@ -128,6 +129,8 @@ Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) {
return new vtkContainerBox(static_cast<uLib::ContainerBox*>(obj));
} else if (std::strcmp(className, "DetectorChamber") == 0) {
return new vtkDetectorChamber(static_cast<uLib::DetectorChamber*>(obj));
} else if (std::strcmp(className, "Cylinder") == 0) {
return new vtkCylinder(static_cast<uLib::Cylinder*>(obj));
}
// Fallback if we don't know the exact class but it might be a context itself