From 51e6dbb4f5829a4ce08256431221db5b1a1b2d15 Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Tue, 24 Mar 2026 15:22:50 +0000 Subject: [PATCH] add cylinder --- app/gcompose/src/PropertyWidgets.cpp | 150 +++++++++++++++++++++------ app/gcompose/src/PropertyWidgets.h | 71 +++++++++++-- src/Core/Object.cpp | 16 +-- src/HEP/Geant/Solid.h | 6 ++ src/Math/CMakeLists.txt | 2 +- src/Math/ContainerBox.h | 57 +++++----- src/Math/Cylinder.h | 124 ++++++++++++++-------- src/Vtk/Math/CMakeLists.txt | 2 + src/Vtk/Math/vtkCylinder.cpp | 144 +++++++++++++++++++++++++ src/Vtk/Math/vtkCylinder.h | 67 ++++++++++++ src/Vtk/vtkObjectsContext.cpp | 5 +- 11 files changed, 523 insertions(+), 121 deletions(-) create mode 100644 src/Vtk/Math/vtkCylinder.cpp create mode 100644 src/Vtk/Math/vtkCylinder.h diff --git a/app/gcompose/src/PropertyWidgets.cpp b/app/gcompose/src/PropertyWidgets.cpp index 0a14c0a..7542585 100644 --- a/app/gcompose/src/PropertyWidgets.cpp +++ b/app/gcompose/src/PropertyWidgets.cpp @@ -1,6 +1,10 @@ #include "PropertyWidgets.h" #include +#include +#include #include "Vtk/uLibVtkInterface.h" +#include "Math/Units.h" +#include "Math/Dense.h" namespace uLib { namespace Qt { @@ -15,57 +19,124 @@ PropertyWidgetBase::PropertyWidgetBase(PropertyBase* prop, QWidget* parent) } PropertyWidgetBase::~PropertyWidgetBase() {} +// Helper for unit parsing +static double parseWithUnits(const QString& text, double* factorOut = nullptr, QString* suffixOut = nullptr) { + static QRegularExpression re("^\\s*([-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?)\\s*(_?[a-zA-Z]+)?\\s*$"); + QRegularExpressionMatch match = re.match(text); + if (!match.hasMatch()) return 0.0; + + double num = match.captured(1).toDouble(); + QString unit = match.captured(3); + double factor = 1.0; + + if (!unit.isEmpty()) { + QString u = unit.startsWith('_') ? unit.mid(1) : unit; + if (u == "m") factor = CLHEP::meter; + else if (u == "cm") factor = CLHEP::centimeter; + else if (u == "mm") factor = CLHEP::millimeter; + else if (u == "um") factor = CLHEP::micrometer; + else if (u == "nm") factor = CLHEP::nanometer; + else if (u == "km") factor = CLHEP::kilometer; + else if (u == "deg") factor = CLHEP::degree; + else if (u == "rad") factor = CLHEP::radian; + else if (u == "ns") factor = CLHEP::nanosecond; + else if (u == "s") factor = CLHEP::second; + else if (u == "ms") factor = CLHEP::millisecond; + else if (u == "MeV") factor = CLHEP::megaelectronvolt; + else if (u == "eV") factor = CLHEP::electronvolt; + else if (u == "keV") factor = CLHEP::kiloelectronvolt; + else if (u == "GeV") factor = CLHEP::gigaelectronvolt; + else if (u == "TeV") factor = CLHEP::teraelectronvolt; + if (suffixOut) *suffixOut = u; + } else if (suffixOut) { + // Reuse previous suffix if none provided, or empty + } + + if (factorOut) *factorOut = factor; + return num * factor; +} + +// UnitLineEdit implementation +UnitLineEdit::UnitLineEdit(QWidget* parent) : QLineEdit(parent), m_Value(0), m_Factor(1.0), m_Suffix("mm"), m_IsInteger(false) { + connect(this, &QLineEdit::editingFinished, this, &UnitLineEdit::onEditingFinished); +} + +void UnitLineEdit::setValue(double val) { + if (m_Value != val) { + m_Value = val; + // Initial heuristic for unit if it was mm and value becomes large + if (!m_IsInteger && m_Suffix == "mm" && std::abs(val) >= 1000.0) { m_Suffix = "m"; m_Factor = CLHEP::meter; } + updateText(); + } +} + +void UnitLineEdit::onEditingFinished() { + double factor = m_Factor; + QString suffix = m_Suffix; + double parsedVal = parseWithUnits(text(), &factor, &suffix); + if (!suffix.isEmpty()) { + m_Suffix = suffix; + m_Factor = factor; + } + if (m_IsInteger) { + parsedVal = std::round(parsedVal); + } + + if (m_Value != parsedVal) { + m_Value = parsedVal; + emit valueManualChanged(m_Value); + } + updateText(); +} + +void UnitLineEdit::updateText() { + QSignalBlocker blocker(this); + if (m_IsInteger) { + setText(QString::number((int)m_Value)); + } else { + double displayVal = m_Value / m_Factor; + setText(QString::number(displayVal, 'g', 6) + " " + m_Suffix); + } +} + +void UnitLineEdit::setIntegerOnly(bool integerOnly) { + m_IsInteger = integerOnly; + updateText(); +} + DoublePropertyWidget::DoublePropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { - m_SpinBox = new QDoubleSpinBox(this); - m_SpinBox->setRange(-1000000.0, 1000000.0); - m_SpinBox->setValue(prop->Get()); - m_Layout->addWidget(m_SpinBox, 1); - connect(m_SpinBox, static_cast(&QDoubleSpinBox::valueChanged), - [this](double val){ if (m_Prop->Get() != val) m_Prop->Set(val); }); + m_Edit = new UnitLineEdit(this); + m_Layout->addWidget(m_Edit, 1); + m_Edit->setValue(prop->Get()); + connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set(val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ - if (m_SpinBox->value() != m_Prop->Get()) { - QSignalBlocker blocker(m_SpinBox); - m_SpinBox->setValue(m_Prop->Get()); - } + m_Edit->setValue(m_Prop->Get()); }); } -DoublePropertyWidget::~DoublePropertyWidget() {} FloatPropertyWidget::FloatPropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { - m_SpinBox = new QDoubleSpinBox(this); - m_SpinBox->setRange(-1000000.0, 1000000.0); - m_SpinBox->setDecimals(3); - m_SpinBox->setValue(prop->Get()); - m_Layout->addWidget(m_SpinBox, 1); - connect(m_SpinBox, static_cast(&QDoubleSpinBox::valueChanged), - [this](double val){ if (m_Prop->Get() != (float)val) m_Prop->Set((float)val); }); + m_Edit = new UnitLineEdit(this); + m_Layout->addWidget(m_Edit, 1); + m_Edit->setValue(prop->Get()); + connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set((float)val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ - if (m_SpinBox->value() != (double)m_Prop->Get()) { - QSignalBlocker blocker(m_SpinBox); - m_SpinBox->setValue((double)m_Prop->Get()); - } + m_Edit->setValue((double)m_Prop->Get()); }); } -FloatPropertyWidget::~FloatPropertyWidget() {} IntPropertyWidget::IntPropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { - m_SpinBox = new QSpinBox(this); - m_SpinBox->setRange(-1000000, 1000000); - m_SpinBox->setValue(prop->Get()); - m_Layout->addWidget(m_SpinBox, 1); - connect(m_SpinBox, static_cast(&QSpinBox::valueChanged), - [this](int val){ if (m_Prop->Get() != val) m_Prop->Set(val); }); + m_Edit = new UnitLineEdit(this); + m_Edit->setIntegerOnly(true); + m_Layout->addWidget(m_Edit, 1); + m_Edit->setValue(prop->Get()); + connect(m_Edit, &UnitLineEdit::valueManualChanged, [this](double val){ m_Prop->Set((int)val); }); uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ - if (m_SpinBox->value() != m_Prop->Get()) { - QSignalBlocker blocker(m_SpinBox); - m_SpinBox->setValue(m_Prop->Get()); - } + m_Edit->setValue((double)m_Prop->Get()); }); } -IntPropertyWidget::~IntPropertyWidget() {} BoolPropertyWidget::BoolPropertyWidget(Property* prop, QWidget* parent) : PropertyWidgetBase(prop, parent), m_Prop(prop) { @@ -126,6 +197,17 @@ PropertyEditor::PropertyEditor(QWidget* parent) : QWidget(parent), m_Object(null registerFactory([](PropertyBase* p, QWidget* parent){ return new StringPropertyWidget(static_cast*>(p), parent); }); + + // Vector Registration + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); + registerFactory([](PropertyBase* p, QWidget* parent){ return new VectorPropertyWidget(static_cast*>(p), parent); }); } PropertyEditor::~PropertyEditor() {} diff --git a/app/gcompose/src/PropertyWidgets.h b/app/gcompose/src/PropertyWidgets.h index 47f17dc..15f5046 100644 --- a/app/gcompose/src/PropertyWidgets.h +++ b/app/gcompose/src/PropertyWidgets.h @@ -5,8 +5,6 @@ #include #include #include -#include -#include #include #include #include @@ -16,6 +14,7 @@ #include "Core/Property.h" #include "Core/Object.h" +#include "Math/Dense.h" namespace uLib { namespace Qt { @@ -33,34 +32,90 @@ protected: QLabel* m_Label; }; +class UnitLineEdit : public QLineEdit { + Q_OBJECT +public: + UnitLineEdit(QWidget* parent = nullptr); + void setValue(double val); + double getValue() const { return m_Value; } + void setIntegerOnly(bool b); + +signals: + void valueManualChanged(double val); + +private slots: + void onEditingFinished(); + +private: + void updateText(); + double m_Value; + double m_Factor; + QString m_Suffix; + bool m_IsInteger; +}; + class DoublePropertyWidget : public PropertyWidgetBase { Q_OBJECT public: DoublePropertyWidget(Property* prop, QWidget* parent = nullptr); - virtual ~DoublePropertyWidget(); private: Property* m_Prop; - QDoubleSpinBox* m_SpinBox; + UnitLineEdit* m_Edit; }; class FloatPropertyWidget : public PropertyWidgetBase { Q_OBJECT public: FloatPropertyWidget(Property* prop, QWidget* parent = nullptr); - virtual ~FloatPropertyWidget(); private: Property* m_Prop; - QDoubleSpinBox* m_SpinBox; + UnitLineEdit* m_Edit; }; class IntPropertyWidget : public PropertyWidgetBase { Q_OBJECT public: IntPropertyWidget(Property* prop, QWidget* parent = nullptr); - virtual ~IntPropertyWidget(); private: Property* m_Prop; - QSpinBox* m_SpinBox; + UnitLineEdit* m_Edit; +}; + +template +class VectorPropertyWidget : public PropertyWidgetBase { +public: + VectorPropertyWidget(Property* prop, QWidget* parent = nullptr) + : PropertyWidgetBase(prop, parent), m_Prop(prop) { + for (int i = 0; i < Size; ++i) { + m_Edits[i] = new UnitLineEdit(this); + if (std::is_integral::value) { + m_Edits[i]->setIntegerOnly(true); + } + m_Layout->addWidget(m_Edits[i], 1); + + connect(m_Edits[i], &UnitLineEdit::valueManualChanged, [this, i](double val){ + VecT v = m_Prop->Get(); + v(i) = (typename VecT::Scalar)val; + if (m_Prop->Get() != v) m_Prop->Set(v); + }); + } + updateEdits(); + uLib::Object::connect(m_Prop, &Property::PropertyChanged, [this](){ + updateEdits(); + }); + } + +private: + void updateEdits() { + VecT v = m_Prop->Get(); + for (int i = 0; i < Size; ++i) { + if (!m_Edits[i]->hasFocus()) { + m_Edits[i]->setValue((double)v(i)); + } + } + } + Property* m_Prop; + UnitLineEdit* m_Edits[Size]; }; class BoolPropertyWidget : public PropertyWidgetBase { diff --git a/src/Core/Object.cpp b/src/Core/Object.cpp index eb1d666..2b1ff91 100644 --- a/src/Core/Object.cpp +++ b/src/Core/Object.cpp @@ -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 ©) : d(new ObjectPrivate(*copy.d)) {} +Object::Object(const Object ©) : 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 ©) { - // should lock to be tread safe // - memcpy(d, copy.d, sizeof(ObjectPrivate)); - // ERROR! does not copy parameters ... <<<< FIXXXXX + if (this == ©) 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) { diff --git a/src/HEP/Geant/Solid.h b/src/HEP/Geant/Solid.h index 6bf45df..4837163 100644 --- a/src/HEP/Geant/Solid.h +++ b/src/HEP/Geant/Solid.h @@ -126,6 +126,12 @@ private: G4Box *m_Solid; }; + + + + + + } // namespace Geant } // namespace uLib diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index 21a19c9..3121b33 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -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() - diff --git a/src/Math/ContainerBox.h b/src/Math/ContainerBox.h index 2255639..90146a7 100644 --- a/src/Math/ContainerBox.h +++ b/src/Math/ContainerBox.h @@ -51,10 +51,8 @@ class ContainerBox : public AffineTransform, public Object { public: //////////////////////////////////////////////////////////////////////////// // PROPERTIES // - Property Width; - Property Height; - Property Depth; - + Property p_Size; + Property 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::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Height, &Property::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Depth, &Property::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::PropertyChanged, this, &ContainerBox::SyncSize); + Object::connect(&p_Origin, &Property::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::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Height, &Property::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Depth, &Property::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::PropertyChanged, this, &ContainerBox::SyncSize); + Object::connect(&p_Origin, &Property::PropertyChanged, this, &ContainerBox::SyncOrigin); + this->SetSize(size); } /** @@ -92,20 +87,20 @@ public: ContainerBox(const ContainerBox ©) : 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::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Height, &Property::PropertyChanged, this, &ContainerBox::SyncSize); - Object::connect(&Depth, &Property::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::PropertyChanged, this, &ContainerBox::SyncSize); + Object::connect(&p_Origin, &Property::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; }; diff --git a/src/Math/Cylinder.h b/src/Math/Cylinder.h index 2cdeb2f..b7f2505 100644 --- a/src/Math/Cylinder.h +++ b/src/Math/Cylinder.h @@ -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 ©) - : 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 + 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; }; diff --git a/src/Vtk/Math/CMakeLists.txt b/src/Vtk/Math/CMakeLists.txt index ca56349..c76d465 100644 --- a/src/Vtk/Math/CMakeLists.txt +++ b/src/Vtk/Math/CMakeLists.txt @@ -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) diff --git a/src/Vtk/Math/vtkCylinder.cpp b/src/Vtk/Math/vtkCylinder.cpp new file mode 100644 index 0000000..e3e1e8a --- /dev/null +++ b/src/Vtk/Math/vtkCylinder.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#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 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 cylinder; + cylinder->SetRadius(1.0); + cylinder->SetHeight(1.0); + cylinder->SetResolution(32); + + vtkNew 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 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 vmat; + Matrix4fToVtk(m_Content->GetMatrix() * m_Content->GetLocalMatrix(), vmat); + root->SetUserMatrix(vmat); + } +} + +} // namespace Vtk +} // namespace uLib diff --git a/src/Vtk/Math/vtkCylinder.h b/src/Vtk/Math/vtkCylinder.h new file mode 100644 index 0000000..20ce7a5 --- /dev/null +++ b/src/Vtk/Math/vtkCylinder.h @@ -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 + +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 diff --git a/src/Vtk/vtkObjectsContext.cpp b/src/Vtk/vtkObjectsContext.cpp index 20ccb94..578d07e 100644 --- a/src/Vtk/vtkObjectsContext.cpp +++ b/src/Vtk/vtkObjectsContext.cpp @@ -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 @@ -128,6 +129,8 @@ Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) { return new vtkContainerBox(static_cast(obj)); } else if (std::strcmp(className, "DetectorChamber") == 0) { return new vtkDetectorChamber(static_cast(obj)); + } else if (std::strcmp(className, "Cylinder") == 0) { + return new vtkCylinder(static_cast(obj)); } // Fallback if we don't know the exact class but it might be a context itself