diff --git a/app/gcompose/src/ContextModel.cpp b/app/gcompose/src/ContextModel.cpp index 8a410c4..2fd469f 100644 --- a/app/gcompose/src/ContextModel.cpp +++ b/app/gcompose/src/ContextModel.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "Core/Object.h" ContextModel::ContextModel(QObject* parent) : QAbstractItemModel(parent), m_rootContext(nullptr) {} @@ -12,6 +13,12 @@ ContextModel::~ContextModel() {} void ContextModel::setContext(uLib::ObjectsContext* context) { beginResetModel(); m_rootContext = context; + if (m_rootContext) { + uLib::Object::connect(m_rootContext, &uLib::Object::Updated, [this]() { + this->beginResetModel(); + this->endResetModel(); + }); + } endResetModel(); } @@ -118,7 +125,14 @@ QVariant ContextModel::data(const QModelIndex& index, int role) const { uLib::Object* obj = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) { - return getDemangledName(typeid(*obj)); + QString typeName = getDemangledName(typeid(*obj)); + std::string instName = obj->GetInstanceName(); + if (instName.empty()) return typeName; + return QString("%1 (%2)").arg(QString::fromStdString(instName)).arg(typeName); + } + + if (role == Qt::EditRole) { + return QString::fromStdString(obj->GetInstanceName()); } return QVariant(); @@ -130,3 +144,18 @@ QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int } return QVariant(); } + +Qt::ItemFlags ContextModel::flags(const QModelIndex& index) const { + if (!index.isValid()) return Qt::NoItemFlags; + return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled; +} + +bool ContextModel::setData(const QModelIndex& index, const QVariant& value, int role) { + if (index.isValid() && role == Qt::EditRole) { + uLib::Object* obj = static_cast(index.internalPointer()); + obj->SetInstanceName(value.toString().toStdString()); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + return true; + } + return false; +} diff --git a/app/gcompose/src/ContextModel.h b/app/gcompose/src/ContextModel.h index 759255e..de6d5ea 100644 --- a/app/gcompose/src/ContextModel.h +++ b/app/gcompose/src/ContextModel.h @@ -18,6 +18,8 @@ public: int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; private: uLib::ObjectsContext* m_rootContext; diff --git a/app/gcompose/src/ContextPanel.cpp b/app/gcompose/src/ContextPanel.cpp index 718faab..5cc1d3f 100644 --- a/app/gcompose/src/ContextPanel.cpp +++ b/app/gcompose/src/ContextPanel.cpp @@ -4,8 +4,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include -ContextPanel::ContextPanel(QWidget* parent) : QWidget(parent) { +ContextPanel::ContextPanel(QWidget* parent) + : QWidget(parent) + , m_vtkContext(nullptr) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); @@ -32,12 +40,92 @@ ContextPanel::ContextPanel(QWidget* parent) : QWidget(parent) { m_model = new ContextModel(this); m_treeView->setModel(m_model); - m_layout->addWidget(m_treeView); + m_splitter = new QSplitter(Qt::Vertical, this); + m_splitter->addWidget(m_treeView); + + m_vtkView = new uLib::Vtk::QViewport(m_splitter); + m_splitter->addWidget(m_vtkView); + + QList sizes; + sizes << 400 << 200; + m_splitter->setSizes(sizes); + + m_layout->addWidget(m_splitter); + + connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &ContextPanel::onSelectionChanged); + + auto* deleteShortcut = new QShortcut(QKeySequence::Delete, this); + connect(deleteShortcut, &QShortcut::activated, [this]() { + auto selectedIndexes = m_treeView->selectionModel()->selectedIndexes(); + if (selectedIndexes.isEmpty() || !m_context) return; + + // Collect objects to remove to avoid iterator invalidation issues if context signal emits during removal + std::vector toRemove; + for (const auto& idx : selectedIndexes) { + if (idx.column() == 0) { + toRemove.push_back(static_cast(idx.internalPointer())); + } + } + + for (auto* obj : toRemove) { + m_context->RemoveObject(obj); + } + }); } ContextPanel::~ContextPanel() {} void ContextPanel::setContext(uLib::ObjectsContext* context) { + m_context = context; m_model->setContext(context); m_treeView->expandAll(); + + if (m_vtkContext) { + m_vtkView->RemovePuppet(*m_vtkContext); + delete m_vtkContext; + } + m_vtkContext = new uLib::Vtk::vtkObjectsContext(context); + m_vtkView->AddPuppet(*m_vtkContext); + + // Render viewport and add child puppets when context is updated + if (context) { + uLib::Object::connect(m_vtkContext, &uLib::Vtk::vtkObjectsContext::PuppetAdded, [this](uLib::Vtk::Puppet* p) { + if (this->m_vtkView && p) { + this->m_vtkView->AddPuppet(*p); + this->m_vtkView->ZoomAuto(); + this->m_vtkView->Render(); + } + }); + + // Add any puppets that were created during m_vtkContext's construction + for (auto* obj : context->GetObjects()) { + if (auto* p = m_vtkContext->GetPuppet(obj)) { + this->m_vtkView->AddPuppet(*p); + } + } + + uLib::Object::connect(context, &uLib::Object::Updated, [this]() { + if (this->m_vtkView) { + this->m_vtkView->ZoomAuto(); + this->m_vtkView->Render(); + } + }); + } + + m_vtkView->Render(); +} + +void ContextPanel::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { + uLib::Object* target = nullptr; + if (!selected.indexes().isEmpty()) { + target = static_cast(selected.indexes().first().internalPointer()); + } + + emit objectSelected(target); + + if (m_vtkContext) { + auto* puppet = m_vtkContext->GetPuppet(target); + m_vtkView->SelectPuppet(puppet); + } } diff --git a/app/gcompose/src/ContextPanel.h b/app/gcompose/src/ContextPanel.h index 76d991c..f11857b 100644 --- a/app/gcompose/src/ContextPanel.h +++ b/app/gcompose/src/ContextPanel.h @@ -2,6 +2,7 @@ #define CONTEXT_PANEL_H #include +#include class QTreeView; class QVBoxLayout; @@ -9,9 +10,16 @@ class QLabel; class ContextModel; namespace uLib { + class Object; class ObjectsContext; + namespace Vtk { + class QViewport; + class vtkObjectsContext; + } } +class QSplitter; + class ContextPanel : public QWidget { Q_OBJECT public: @@ -20,12 +28,22 @@ public: void setContext(uLib::ObjectsContext* context); +Q_SIGNALS: + void objectSelected(uLib::Object* obj); + +private Q_SLOTS: + void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + private: QVBoxLayout* m_layout; QWidget* m_titleBar; QLabel* m_titleLabel; QTreeView* m_treeView; ContextModel* m_model; + QSplitter* m_splitter; + uLib::Vtk::QViewport* m_vtkView; + uLib::Vtk::vtkObjectsContext* m_vtkContext; + uLib::ObjectsContext* m_context; }; #endif // CONTEXT_PANEL_H diff --git a/app/gcompose/src/MainPanel.cpp b/app/gcompose/src/MainPanel.cpp index ca2c093..89a7e47 100644 --- a/app/gcompose/src/MainPanel.cpp +++ b/app/gcompose/src/MainPanel.cpp @@ -1,6 +1,10 @@ #include "MainPanel.h" #include "ViewportPane.h" #include "ContextPanel.h" +#include "Core/ObjectFactory.h" +#include "Core/ObjectsContext.h" +#include "Vtk/vtkObjectsContext.h" +#include "Vtk/vtkQViewport.h" #include #include #include @@ -11,7 +15,7 @@ #include #include "StyleManager.h" -MainPanel::MainPanel(QWidget* parent) : QWidget(parent) { +MainPanel::MainPanel(QWidget* parent) : QWidget(parent), m_context(nullptr), m_mainVtkContext(nullptr) { auto* mainLayout = new QVBoxLayout(this); mainLayout->setContentsMargins(0, 0, 0, 0); mainLayout->setSpacing(0); @@ -44,8 +48,23 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent) { themeMenu->addAction("Bright", this, &MainPanel::onBrightTheme); btnTheme->setMenu(themeMenu); + // New Menu Button + auto* btnNew = new QPushButton("New", menuPanel); + btnNew->setObjectName("MenuButton"); + auto* newMenu = new QMenu(btnNew); + + auto classes = uLib::ObjectFactory::Instance().GetRegisteredClasses(); + for (const auto& className : classes) { + auto* action = newMenu->addAction(QString::fromStdString(className)); + connect(action, &QAction::triggered, [this, className]() { + this->onCreateObject(className); + }); + } + btnNew->setMenu(newMenu); + menuLayout->addWidget(logo); menuLayout->addWidget(btnFile); + menuLayout->addWidget(btnNew); menuLayout->addWidget(btnTheme); menuLayout->addStretch(); @@ -58,6 +77,16 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent) { m_firstPane = new ViewportPane(m_rootSplitter); m_rootSplitter->addWidget(m_firstPane); + connect(m_contextPanel, &ContextPanel::objectSelected, [this](uLib::Object* obj) { + if (auto* viewport = qobject_cast(m_firstPane->currentViewport())) { + uLib::Vtk::Puppet* puppet = nullptr; + if (m_mainVtkContext) { + puppet = m_mainVtkContext->GetPuppet(obj); + } + viewport->SelectPuppet(puppet); + } + }); + // Set initial sizes QList sizes; sizes << 200 << 1000; @@ -67,7 +96,52 @@ MainPanel::MainPanel(QWidget* parent) : QWidget(parent) { } void MainPanel::setContext(uLib::ObjectsContext* context) { + m_context = context; m_contextPanel->setContext(context); + + if (m_mainVtkContext) { + if (auto* viewport = qobject_cast(m_firstPane->currentViewport())) { + viewport->RemovePuppet(*m_mainVtkContext); + } + delete m_mainVtkContext; + m_mainVtkContext = nullptr; + } + + if (context) { + if (auto* viewport = qobject_cast(m_firstPane->currentViewport())) { + m_mainVtkContext = new uLib::Vtk::vtkObjectsContext(context); + viewport->AddPuppet(*m_mainVtkContext); + uLib::Object::connect(m_mainVtkContext, &uLib::Vtk::vtkObjectsContext::PuppetAdded, [viewport](uLib::Vtk::Puppet* p) { + if (viewport && p) { + viewport->AddPuppet(*p); + viewport->ZoomAuto(); + viewport->Render(); + } + }); + + // Add any puppets that were created during m_mainVtkContext's construction + for (auto* obj : context->GetObjects()) { + if (auto* p = m_mainVtkContext->GetPuppet(obj)) { + viewport->AddPuppet(*p); + } + } + + uLib::Object::connect(context, &uLib::Object::Updated, [viewport]() { + viewport->ZoomAuto(); + viewport->Render(); + }); + viewport->ZoomAuto(); + viewport->Render(); + } + } +} + +void MainPanel::onCreateObject(const std::string& className) { + if (!m_context) return; + auto* obj = uLib::ObjectFactory::Instance().Create(className); + if (obj) { + m_context->AddObject(obj); + } } void MainPanel::onOpen() { diff --git a/app/gcompose/src/MainPanel.h b/app/gcompose/src/MainPanel.h index 5b85f4a..72086ca 100644 --- a/app/gcompose/src/MainPanel.h +++ b/app/gcompose/src/MainPanel.h @@ -9,6 +9,9 @@ class ContextPanel; namespace uLib { class ObjectsContext; + namespace Vtk { + class vtkObjectsContext; + } } class MainPanel : public QWidget { @@ -25,11 +28,15 @@ private slots: void onSave(); void onDarkTheme(); void onBrightTheme(); + + void onCreateObject(const std::string& className); private: QSplitter* m_rootSplitter; ViewportPane* m_firstPane; ContextPanel* m_contextPanel; + uLib::ObjectsContext* m_context; + uLib::Vtk::vtkObjectsContext* m_mainVtkContext; }; #endif // MAINPANEL_H diff --git a/app/gcompose/src/main.cpp b/app/gcompose/src/main.cpp index 737827c..d0adb51 100644 --- a/app/gcompose/src/main.cpp +++ b/app/gcompose/src/main.cpp @@ -33,7 +33,8 @@ int main(int argc, char** argv) { std::cout << "Starting gcompose Qt application..." << std::endl; ContainerBox world_box(Vector3f(1, 1, 1)); - world_box.Scale(Vector3f(20_mm, 20_mm, 20_mm)); + world_box.Scale(Vector3f(2_mm, 2_mm, 2_mm)); + world_box.SetPosition(Vector3f(-1_mm, -1_mm, -1_mm)); Geant::Scene scene; scene.ConstructWorldBox(world_box.GetSize(), "G4_AIR"); @@ -46,14 +47,7 @@ int main(int argc, char** argv) { // 2. Initialize MainWindow (contains embedded VTK QViewport) MainWindow window; window.setContext(&globalContext); - MainPanel* panel = window.getPanel(); - ViewportPane* pane = panel->getFirstPane(); - Vtk::QViewport* viewport = qobject_cast(pane->currentViewport()); - - Vtk::vtkContainerBox vtk_box(&world_box); - viewport->AddPuppet(vtk_box); - viewport->ZoomAuto(); - + std::cout << "Geant4 and VTK scenes are ready." << std::endl; window.show(); diff --git a/src/Core/CMakeLists.txt b/src/Core/CMakeLists.txt index cb18814..5e58787 100644 --- a/src/Core/CMakeLists.txt +++ b/src/Core/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADERS Macros.h Mpl.h Object.h + ObjectFactory.h ObjectsContext.h Options.h Serializable.h @@ -27,6 +28,7 @@ set(SOURCES Archives.cpp Debug.cpp Object.cpp + ObjectFactory.cpp ObjectsContext.cpp Options.cpp Serializable.cpp diff --git a/src/Core/Object.cpp b/src/Core/Object.cpp index d909f4e..0c302d3 100644 --- a/src/Core/Object.cpp +++ b/src/Core/Object.cpp @@ -56,7 +56,8 @@ public: GenericMFPtr sloptr; std::string slostr; }; - + + std::string m_InstanceName; Vector sigv; Vector slov; }; @@ -147,6 +148,15 @@ GenericMFPtr *Object::findSlotImpl(const char *name) const { void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); } +const std::string& Object::GetInstanceName() const { + return d->m_InstanceName; +} + +void Object::SetInstanceName(const std::string& name) { + d->m_InstanceName = name; + this->Updated(); +} + // std::ostream & // operator << (std::ostream &os, uLib::Object &ob) // { diff --git a/src/Core/Object.h b/src/Core/Object.h index 0dfc060..97f199f 100644 --- a/src/Core/Object.h +++ b/src/Core/Object.h @@ -27,6 +27,7 @@ #define U_CORE_OBJECT_H #include +#include // WARNING: COPILE ERROR if this goes after mpl/vector // // #include "Core/Vector.h" @@ -74,6 +75,10 @@ public: Object(const Object ©); ~Object(); + virtual const char * GetClassName() const { return "Object"; } + + const std::string& GetInstanceName() const; + void SetInstanceName(const std::string& name); //////////////////////////////////////////////////////////////////////////// // PARAMETERS // diff --git a/src/Core/ObjectFactory.cpp b/src/Core/ObjectFactory.cpp new file mode 100644 index 0000000..5b99226 --- /dev/null +++ b/src/Core/ObjectFactory.cpp @@ -0,0 +1,32 @@ +#include "ObjectFactory.h" + +namespace uLib { + +ObjectFactory& ObjectFactory::Instance() { + static ObjectFactory instance; + return instance; +} + +void ObjectFactory::Register(const std::string& className, FactoryFunction func) { + if (m_factoryMap.find(className) == m_factoryMap.end()) { + m_factoryMap[className] = func; + } +} + +Object* ObjectFactory::Create(const std::string& className) { + auto it = m_factoryMap.find(className); + if (it != m_factoryMap.end()) { + return (it->second)(); + } + return nullptr; +} + +std::vector ObjectFactory::GetRegisteredClasses() const { + std::vector classes; + for (auto const& [name, func] : m_factoryMap) { + classes.push_back(name); + } + return classes; +} + +} // namespace uLib diff --git a/src/Core/ObjectFactory.h b/src/Core/ObjectFactory.h new file mode 100644 index 0000000..e22ecc3 --- /dev/null +++ b/src/Core/ObjectFactory.h @@ -0,0 +1,68 @@ +#ifndef U_CORE_OBJECTFACTORY_H +#define U_CORE_OBJECTFACTORY_H + +#include +#include +#include +#include +#include "Core/Object.h" + +namespace uLib { + +/** + * @brief Singleton factory for dynamic Object instantiation based on class name. + */ +class ObjectFactory { +public: + typedef std::function FactoryFunction; + + /** @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 Create a new instance of the specified class. */ + Object* Create(const std::string& className); + + /** @brief Get the names of all registered classes. */ + std::vector GetRegisteredClasses() const; + +private: + ObjectFactory() = default; + ~ObjectFactory() = default; + + // Prevent copy and assignment + ObjectFactory(const ObjectFactory&) = delete; + ObjectFactory& operator=(const ObjectFactory&) = delete; + + std::map m_factoryMap; +}; + +/** + * @brief Helper class to statically register a factory function. + */ +template +class ObjectRegistrar { +public: + ObjectRegistrar(const std::string& className) { + ObjectFactory::Instance().Register(className, []() -> Object* { return new T(); }); + } +}; + +#define ULIB_REG_CONCAT_IMPL(a, b) a##b +#define ULIB_REG_CONCAT(a, b) ULIB_REG_CONCAT_IMPL(a, b) + +/** + * @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 ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(#className); + +#define ULIB_REGISTER_OBJECT_NAME(className, registeredName) \ + static uLib::ObjectRegistrar ULIB_REG_CONCAT(g_ObjectRegistrar_, __LINE__)(registeredName); + +} // namespace uLib + +#endif // U_CORE_OBJECTFACTORY_H diff --git a/src/Core/ObjectsContext.cpp b/src/Core/ObjectsContext.cpp index d14f8a6..dcdb26a 100644 --- a/src/Core/ObjectsContext.cpp +++ b/src/Core/ObjectsContext.cpp @@ -10,6 +10,7 @@ ObjectsContext::~ObjectsContext() {} void ObjectsContext::AddObject(Object* obj) { if (obj && std::find(m_objects.begin(), m_objects.end(), obj) == m_objects.end()) { m_objects.push_back(obj); + ULIB_SIGNAL_EMIT(ObjectsContext::ObjectAdded, obj); this->Updated(); // Signal that the context has been updated } } @@ -17,13 +18,18 @@ void ObjectsContext::AddObject(Object* obj) { void ObjectsContext::RemoveObject(Object* obj) { auto it = std::find(m_objects.begin(), m_objects.end(), obj); if (it != m_objects.end()) { + Object* removedObj = *it; m_objects.erase(it); + ULIB_SIGNAL_EMIT(ObjectsContext::ObjectRemoved, removedObj); this->Updated(); // Signal that the context has been updated } } void ObjectsContext::Clear() { if (!m_objects.empty()) { + for (auto obj : m_objects) { + ULIB_SIGNAL_EMIT(ObjectsContext::ObjectRemoved, obj); + } m_objects.clear(); this->Updated(); } @@ -44,4 +50,12 @@ Object* ObjectsContext::GetObject(size_t index) const { return nullptr; } +void ObjectsContext::ObjectAdded(Object* obj) { + ULIB_SIGNAL_EMIT(ObjectsContext::ObjectAdded, obj); +} + +void ObjectsContext::ObjectRemoved(Object* obj) { + ULIB_SIGNAL_EMIT(ObjectsContext::ObjectRemoved, obj); +} + } // namespace uLib diff --git a/src/Core/ObjectsContext.h b/src/Core/ObjectsContext.h index 1dcfe83..a548d42 100644 --- a/src/Core/ObjectsContext.h +++ b/src/Core/ObjectsContext.h @@ -14,6 +14,8 @@ public: ObjectsContext(); virtual ~ObjectsContext(); + virtual const char * GetClassName() const { return "ObjectsContext"; } + /** * @brief Adds an object to the context. * @param obj Pointer to the object to add. @@ -36,6 +38,12 @@ public: * @return Const reference to the vector of object pointers. */ const std::vector& GetObjects() const; + + signals: + /** @brief Signal emitted when an object is added. */ + virtual void ObjectAdded(Object* obj); + /** @brief Signal emitted when an object is removed. */ + virtual void ObjectRemoved(Object* obj); /** * @brief Returns the number of objects in the context. diff --git a/src/Core/Signal.h b/src/Core/Signal.h index 79dbad5..42ef621 100644 --- a/src/Core/Signal.h +++ b/src/Core/Signal.h @@ -43,11 +43,23 @@ using namespace boost::placeholders; // Signals macro // #define default(vlaue) -#define slots +#ifndef Q_MOC_RUN +#ifndef signals #define signals /*virtual void init_signals();*/ public +#endif +#ifndef slots +#define slots +#endif +#ifndef emit #define emit +#endif +#endif +#ifndef SLOT #define SLOT(a) BOOST_STRINGIZE(a) +#endif +#ifndef SIGNAL #define SIGNAL(a) BOOST_STRINGIZE(a) +#endif #define _ULIB_DETAIL_SIGNAL_EMIT(_name, ...) \ do { \ diff --git a/src/HEP/Detectors/DetectorChamber.cpp b/src/HEP/Detectors/DetectorChamber.cpp index 240a4fd..514cf94 100644 --- a/src/HEP/Detectors/DetectorChamber.cpp +++ b/src/HEP/Detectors/DetectorChamber.cpp @@ -1,4 +1,5 @@ #include "HEP/Detectors/DetectorChamber.h" +#include "Core/ObjectFactory.h" #include namespace uLib { @@ -43,4 +44,6 @@ MuonEvent DetectorChamber::ProjectMuonEvent(const MuonEvent &muon) const { return projectedMuon; } +ULIB_REGISTER_OBJECT(DetectorChamber) + } // namespace uLib \ No newline at end of file diff --git a/src/HEP/Detectors/DetectorChamber.h b/src/HEP/Detectors/DetectorChamber.h index 89686a5..3878f08 100644 --- a/src/HEP/Detectors/DetectorChamber.h +++ b/src/HEP/Detectors/DetectorChamber.h @@ -45,6 +45,8 @@ class DetectorChamber : public ContainerBox { public: + virtual const char * GetClassName() const { return "DetectorChamber"; } + DetectorChamber() : BaseClass() { m_ProjectionPlane.origin = HPoint3f(0, 0, 0); m_ProjectionPlane.direction = HVector3f(0, 0, 1); diff --git a/src/HEP/Geant/EmitterPrimary.hh b/src/HEP/Geant/EmitterPrimary.hh index 858cad3..67401f6 100644 --- a/src/HEP/Geant/EmitterPrimary.hh +++ b/src/HEP/Geant/EmitterPrimary.hh @@ -26,6 +26,9 @@ namespace Geant { class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public Object, public AffineTransform { public: + + virtual const char* GetClassName() const override { return "Geant.EmitterPrimary"; } + EmitterPrimary(); virtual ~EmitterPrimary(); @@ -44,6 +47,9 @@ class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public Object, publ class SkyPlaneEmitterPrimary : public EmitterPrimary { public: + + virtual const char* GetClassName() const override { return "Geant.SkyPlaneEmitterPrimary"; } + SkyPlaneEmitterPrimary(); virtual ~SkyPlaneEmitterPrimary(); @@ -63,6 +69,9 @@ class SkyPlaneEmitterPrimary : public EmitterPrimary class CylinderEmitterPrimary : public EmitterPrimary { public: + + virtual const char* GetClassName() const override { return "Geant.CylinderEmitterPrimary"; } + CylinderEmitterPrimary(); virtual ~CylinderEmitterPrimary(); @@ -89,6 +98,9 @@ class CylinderEmitterPrimary : public EmitterPrimary class QuadMeshEmitterPrimary : public EmitterPrimary { public: + + virtual const char* GetClassName() const override { return "Geant.QuadMeshEmitterPrimary"; } + QuadMeshEmitterPrimary(); virtual ~QuadMeshEmitterPrimary(); diff --git a/src/HEP/Geant/GeantEvent.h b/src/HEP/Geant/GeantEvent.h index ecf5389..b0e6dec 100644 --- a/src/HEP/Geant/GeantEvent.h +++ b/src/HEP/Geant/GeantEvent.h @@ -26,6 +26,7 @@ #ifndef U_GEANTEVENT_H #define U_GEANTEVENT_H +#include "Core/Object.h" #include "Core/Types.h" #include "Core/Vector.h" #include "Math/Dense.h" @@ -46,10 +47,12 @@ class SteppingAction; /// recording the change of momentum and direction at each step boundary. /////////////////////////////////////////////////////////////////////////////// -class GeantEvent { +class GeantEvent : public Object { public: + virtual const char* GetClassName() const override { return "Geant.GeantEvent"; } + /// A single interaction step along the muon path. struct Delta { Scalarf m_Length; ///< step length through the solid diff --git a/src/HEP/Geant/Matter.h b/src/HEP/Geant/Matter.h index 87a1069..64e35d0 100644 --- a/src/HEP/Geant/Matter.h +++ b/src/HEP/Geant/Matter.h @@ -59,6 +59,8 @@ private: class Material : public Object { public: + virtual const char* GetClassName() const override { return "Geant.Material"; } + uLibRefMacro(G4Data,G4Material *) private: G4Material *m_G4Data; diff --git a/src/HEP/Geant/Scene.h b/src/HEP/Geant/Scene.h index d483fce..d3c5f8c 100644 --- a/src/HEP/Geant/Scene.h +++ b/src/HEP/Geant/Scene.h @@ -43,6 +43,9 @@ class EmitterPrimary; class Scene : public Object { public: + + virtual const char* GetClassName() const override { return "Geant.Scene"; } + Scene(); ~Scene(); diff --git a/src/HEP/Geant/Solid.h b/src/HEP/Geant/Solid.h index 2ac5374..6bf45df 100644 --- a/src/HEP/Geant/Solid.h +++ b/src/HEP/Geant/Solid.h @@ -43,6 +43,9 @@ namespace Geant { class Solid : public Object { public: + + virtual const char* GetClassName() const override { return "Geant.Solid"; } + Solid(); Solid(const char *name); virtual ~Solid(); @@ -83,6 +86,9 @@ protected: class TessellatedSolid : public Solid { typedef Solid BaseClass; public: + + virtual const char* GetClassName() const override { return "Geant.TessellatedSolid"; } + TessellatedSolid(const char *name); void SetMesh(TriangleMesh &mesh); uLibGetMacro(Solid, G4TessellatedSolid *) @@ -104,6 +110,9 @@ class BoxSolid : public Solid { typedef Solid BaseClass; public: + + virtual const char* GetClassName() const override { return "Geant.BoxSolid"; } + BoxSolid(const char *name, ContainerBox *box); virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; } diff --git a/src/Math/CMakeLists.txt b/src/Math/CMakeLists.txt index feb33a8..21a19c9 100644 --- a/src/Math/CMakeLists.txt +++ b/src/Math/CMakeLists.txt @@ -34,7 +34,8 @@ set(SOURCES VoxRaytracer.cpp QuadMesh.cpp Dense.cpp Structured2DGrid.cpp - Structured4DGrid.cpp) + Structured4DGrid.cpp + MathRegistrations.cpp) set(LIBRARIES ${PACKAGE_LIBPREFIX}Core Eigen3::Eigen diff --git a/src/Math/ContainerBox.h b/src/Math/ContainerBox.h index 09b865f..30086c8 100644 --- a/src/Math/ContainerBox.h +++ b/src/Math/ContainerBox.h @@ -48,6 +48,9 @@ class ContainerBox : public AffineTransform, public Object { typedef AffineTransform BaseClass; public: + + virtual const char * GetClassName() const { return "ContainerBox"; } + /** * @brief Default constructor. * Initializes the local transformation with this instance as its parent. diff --git a/src/Math/Cylinder.h b/src/Math/Cylinder.h index 334c31a..2cdeb2f 100644 --- a/src/Math/Cylinder.h +++ b/src/Math/Cylinder.h @@ -45,6 +45,9 @@ class Cylinder : public AffineTransform, public Object { typedef AffineTransform BaseClass; public: + + virtual const char * GetClassName() const { return "Cylinder"; } + /** * @brief Default constructor. * Initializes with radius 1 and height 1. diff --git a/src/Math/Geometry.h b/src/Math/Geometry.h index 532ef88..7c35e2b 100644 --- a/src/Math/Geometry.h +++ b/src/Math/Geometry.h @@ -28,15 +28,18 @@ #ifndef U_GEOMETRY_H #define U_GEOMETRY_H +#include "Core/Object.h" #include "Math/Dense.h" #include "Math/Transform.h" #include namespace uLib { -class Geometry : public AffineTransform { +class Geometry : public AffineTransform, public Object { public: + virtual const char * GetClassName() const { return "Geometry"; } + virtual Vector3f ToLinear(const Vector3f& curved_space) const { return curved_space; } @@ -87,6 +90,8 @@ class SphericalGeometry : public Geometry { public: SphericalGeometry() {} + virtual const char * GetClassName() const { return "SphericalGeometry"; } + Vector3f ToLinear(const Vector3f& spherical) const { float r = spherical.x(); float theta = spherical.y(); @@ -109,6 +114,8 @@ class ToroidalGeometry : public Geometry { public: ToroidalGeometry(float Rtor) : m_Rtor(Rtor) {} + virtual const char * GetClassName() const { return "ToroidalGeometry"; } + Vector3f ToLinear(const Vector3f& toroidal) const { float r = toroidal.x(); float theta = toroidal.y(); diff --git a/src/Math/MathRegistrations.cpp b/src/Math/MathRegistrations.cpp new file mode 100644 index 0000000..445552a --- /dev/null +++ b/src/Math/MathRegistrations.cpp @@ -0,0 +1,20 @@ +#include "Core/ObjectFactory.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/StructuredData.h" + +namespace uLib { + +ULIB_REGISTER_OBJECT(ContainerBox) +ULIB_REGISTER_OBJECT(Cylinder) +ULIB_REGISTER_OBJECT(CylindricalGeometry) +ULIB_REGISTER_OBJECT(SphericalGeometry) +ULIB_REGISTER_OBJECT(TriangleMesh) +ULIB_REGISTER_OBJECT(QuadMesh) +ULIB_REGISTER_OBJECT_NAME(VoxImage, "VoxImage") + +} // namespace uLib diff --git a/src/Math/Polydata.h b/src/Math/Polydata.h index 4cd462f..a28cd77 100644 --- a/src/Math/Polydata.h +++ b/src/Math/Polydata.h @@ -36,6 +36,8 @@ class Polydata : public Object { public: + virtual const char * GetClassName() const { return "Polydata"; } + }; diff --git a/src/Math/QuadMesh.h b/src/Math/QuadMesh.h index 7a103fc..b1a07a6 100644 --- a/src/Math/QuadMesh.h +++ b/src/Math/QuadMesh.h @@ -37,6 +37,9 @@ namespace uLib { class QuadMesh : public AffineTransform, public Object { public: + + virtual const char * GetClassName() const { return "QuadMesh"; } + void PrintSelf(std::ostream &o); /** @brief Adds a point in global coordinates. Stored in local coordinates. */ diff --git a/src/Math/TriangleMesh.h b/src/Math/TriangleMesh.h index 604c645..ecfa2d0 100644 --- a/src/Math/TriangleMesh.h +++ b/src/Math/TriangleMesh.h @@ -40,6 +40,9 @@ namespace uLib { class TriangleMesh : public AffineTransform, public Object { public: + + virtual const char * GetClassName() const { return "TriangleMesh"; } + void PrintSelf(std::ostream &o); /** @brief Adds a point in global coordinates. Stored in local coordinates. */ diff --git a/src/Math/VoxImage.h b/src/Math/VoxImage.h index 66b276a..2b9f227 100644 --- a/src/Math/VoxImage.h +++ b/src/Math/VoxImage.h @@ -46,6 +46,9 @@ namespace Abstract { class VoxImage : public uLib::StructuredGrid { public: + + virtual const char * GetClassName() const { return "VoxImage"; } + typedef uLib::StructuredGrid BaseClass; virtual float GetValue(const Vector3i &id) const = 0; diff --git a/src/Math/VoxImageFilter.h b/src/Math/VoxImageFilter.h index d09952c..e66778a 100644 --- a/src/Math/VoxImageFilter.h +++ b/src/Math/VoxImageFilter.h @@ -57,9 +57,12 @@ protected: } // namespace Abstract template -class VoxImageFilter : public Abstract::VoxImageFilter { +class VoxImageFilter : public Abstract::VoxImageFilter, public Object { public: + + virtual const char * GetClassName() const { return "VoxImageFilter"; } + VoxImageFilter(const Vector3i &size); void Run(); diff --git a/src/Vtk/CMakeLists.txt b/src/Vtk/CMakeLists.txt index 1a2121e..ef1277c 100644 --- a/src/Vtk/CMakeLists.txt +++ b/src/Vtk/CMakeLists.txt @@ -5,6 +5,7 @@ set(HEADERS uLibVtkInterface.h vtkQViewport.h vtkViewport.h vtkPolydata.h + vtkObjectsContext.h ) set(SOURCES uLibVtkInterface.cxx @@ -14,6 +15,7 @@ set(SOURCES uLibVtkInterface.cxx vtkQViewport.cpp vtkViewport.cpp vtkPolydata.cpp + vtkObjectsContext.cpp ) ## Pull in Math VTK wrappers (sets MATH_SOURCES / MATH_HEADERS) diff --git a/src/Vtk/uLibVtkInterface.h b/src/Vtk/uLibVtkInterface.h index 28a37e1..2e37294 100644 --- a/src/Vtk/uLibVtkInterface.h +++ b/src/Vtk/uLibVtkInterface.h @@ -103,6 +103,10 @@ private: class PuppetData *d; }; + + + + } // namespace Vtk } // namespace uLib diff --git a/src/Vtk/vtkObjectsContext.cpp b/src/Vtk/vtkObjectsContext.cpp new file mode 100644 index 0000000..20ccb94 --- /dev/null +++ b/src/Vtk/vtkObjectsContext.cpp @@ -0,0 +1,150 @@ +#include "vtkObjectsContext.h" +#include "vtkContainerBox.h" +#include "HEP/Detectors/vtkDetectorChamber.h" + +#include +#include +#include +#include + +namespace uLib { +namespace Vtk { + +vtkObjectsContext::vtkObjectsContext(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, &vtkObjectsContext::OnObjectAdded); + Object::connect(m_Context, &uLib::ObjectsContext::ObjectRemoved, this, &vtkObjectsContext::OnObjectRemoved); + this->Synchronize(); + } +} + +vtkObjectsContext::~vtkObjectsContext() { + for (auto const& [obj, puppet] : m_Puppets) { + delete puppet; + } + m_Assembly->Delete(); +} + +void vtkObjectsContext::Synchronize() { + if (!m_Context) return; + + // 1. Identify objects to add and remove + const auto& objects = m_Context->GetObjects(); + std::map currentObjects; + for (auto obj : objects) currentObjects[obj] = true; + + // Remove puppets for objects no longer in context + for (auto it = m_Puppets.begin(); it != m_Puppets.end(); ) { + if (currentObjects.find(it->first) == currentObjects.end()) { + it->second->DisconnectRenderer(nullptr); // If we have a ref to a renderer we should disconnect but Puppet doesn't store it easily + // Actually Puppet::DisconnectRenderer(vtkRenderer*) needs the renderer. + // For now we just remove from assembly + vtkPropCollection* props = it->second->GetProps(); + props->InitTraversal(); + while(vtkProp* prop = props->GetNextProp()) { + if (vtkProp3D* p3d = vtkProp3D::SafeDownCast(prop)) + m_Assembly->RemovePart(p3d); + } + this->PuppetRemoved(it->second); + delete it->second; + it = m_Puppets.erase(it); + } else { + ++it; + } + } + + // Add puppets for new objects + for (auto obj : objects) { + if (m_Puppets.find(obj) == m_Puppets.end()) { + Puppet* puppet = this->CreatePuppet(obj); + if (puppet) { + m_Puppets[obj] = puppet; + vtkPropCollection* props = puppet->GetProps(); + props->InitTraversal(); + while(vtkProp* prop = props->GetNextProp()) { + if (vtkProp3D* p3d = vtkProp3D::SafeDownCast(prop)) + m_Assembly->AddPart(p3d); + } + this->PuppetAdded(puppet); + } + } + } +} + +void vtkObjectsContext::OnObjectAdded(uLib::Object* obj) { + if (!obj) return; + if (m_Puppets.find(obj) == m_Puppets.end()) { + Puppet* puppet = this->CreatePuppet(obj); + if (puppet) { + m_Puppets[obj] = puppet; + vtkPropCollection* props = puppet->GetProps(); + props->InitTraversal(); + while(vtkProp* prop = props->GetNextProp()) { + if (vtkProp3D* p3d = vtkProp3D::SafeDownCast(prop)) + m_Assembly->AddPart(p3d); + } + this->PuppetAdded(puppet); + } + } +} + +void vtkObjectsContext::OnObjectRemoved(uLib::Object* obj) { + if (!obj) return; + auto it = m_Puppets.find(obj); + if (it != m_Puppets.end()) { + // For now we just remove from assembly. + // Puppet::DisconnectRenderer(vtkRenderer*) needs the renderer, but we don't have it here easily. + vtkPropCollection* props = it->second->GetProps(); + props->InitTraversal(); + while(vtkProp* prop = props->GetNextProp()) { + if (vtkProp3D* p3d = vtkProp3D::SafeDownCast(prop)) + m_Assembly->RemovePart(p3d); + } + this->PuppetRemoved(it->second); + delete it->second; + m_Puppets.erase(it); + } +} + +Puppet* vtkObjectsContext::GetPuppet(uLib::Object* obj) { + auto it = m_Puppets.find(obj); + if (it != m_Puppets.end()) return it->second; + return nullptr; +} + +void vtkObjectsContext::Update() { + for (auto const& [obj, puppet] : m_Puppets) { + puppet->Update(); + } +} + +Puppet* vtkObjectsContext::CreatePuppet(uLib::Object* obj) { + if (!obj) return nullptr; + + const char* className = obj->GetClassName(); + if (std::strcmp(className, "ContainerBox") == 0) { + return new vtkContainerBox(static_cast(obj)); + } else if (std::strcmp(className, "DetectorChamber") == 0) { + return new vtkDetectorChamber(static_cast(obj)); + } + + // Fallback if we don't know the exact class but it might be a context itself + if (auto subCtx = dynamic_cast(obj)) { + return new vtkObjectsContext(subCtx); + } + + return nullptr; +} + +void vtkObjectsContext::PuppetAdded(Puppet* puppet) { + ULIB_SIGNAL_EMIT(vtkObjectsContext::PuppetAdded, puppet); +} + +void vtkObjectsContext::PuppetRemoved(Puppet* puppet) { + ULIB_SIGNAL_EMIT(vtkObjectsContext::PuppetRemoved, puppet); +} + +} // namespace Vtk +} // namespace uLib diff --git a/src/Vtk/vtkObjectsContext.h b/src/Vtk/vtkObjectsContext.h new file mode 100644 index 0000000..db25902 --- /dev/null +++ b/src/Vtk/vtkObjectsContext.h @@ -0,0 +1,54 @@ +#ifndef U_VTKOBJECTSCONTEXT_H +#define U_VTKOBJECTSCONTEXT_H + +#include +#include "Core/ObjectsContext.h" +#include "uLibVtkInterface.h" + +class vtkAssembly; + +namespace uLib { +namespace Vtk { + +/** + * @brief vtkObjectsContext manages VTK representations (Puppets) for a collection of uLib::Objects. + */ +class vtkObjectsContext : public uLib::Object, public Puppet { +public: + virtual const char* GetClassName() const override { return "vtkObjectsContext"; } + vtkObjectsContext(uLib::ObjectsContext *context); + virtual ~vtkObjectsContext(); + + /** @brief Synchronizes the VTK puppets with the core ObjectsContext. */ + void Synchronize(); + + /** @brief Returns the puppet associated with a specific core object. */ + Puppet* GetPuppet(uLib::Object* obj); + + /** @brief Updates all managed puppets. */ + virtual void Update() override; + + public: + virtual void PuppetAdded(Puppet* puppet); + virtual void PuppetRemoved(Puppet* puppet); + + public: + /** @brief Slot called when an object is added to the core context. */ + void OnObjectAdded(uLib::Object* obj); + /** @brief Slot called when an object is removed from the core context. */ + void OnObjectRemoved(uLib::Object* obj); + +protected: + /** @brief Factory method to create a puppet for a core object. */ + Puppet* CreatePuppet(uLib::Object* obj); + +private: + uLib::ObjectsContext *m_Context; + std::map m_Puppets; + vtkAssembly *m_Assembly; +}; + +} // namespace Vtk +} // namespace uLib + +#endif // U_VTKOBJECTSCONTEXT_H