Compare commits
57 Commits
main
...
5d0efb3078
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d0efb3078 | ||
|
|
94843de711 | ||
|
|
b52ae808b8 | ||
|
|
d87f3a984e | ||
|
|
324aaa91b7 | ||
|
|
a8f786d8d1 | ||
|
|
add9d37aea | ||
|
|
0bff36f8ba | ||
|
|
cd95f16221 | ||
|
|
bbd7493d9f | ||
|
|
033fb598c7 | ||
|
|
c44a7738c0 | ||
|
|
85e1f1448f | ||
|
|
6234dffaa7 | ||
|
|
80952cc706 | ||
|
|
ae27e9d46d | ||
|
|
a8a313e5cf | ||
|
|
7c8c7beae4 | ||
|
|
a8c0d5edc2 | ||
|
|
dbb5f24933 | ||
|
|
1e6e3ae4f4 | ||
|
|
ca5f576b99 | ||
|
|
4cb4560921 | ||
|
|
887b3b36f0 | ||
|
|
2f163a762c | ||
|
|
12657167f1 | ||
|
|
c265adadfc | ||
|
|
c8eec163a6 | ||
|
|
ca2223e04c | ||
|
|
176a82f108 | ||
|
|
3b02bb26ac | ||
|
|
a9b66a4e12 | ||
|
|
cca29ef837 | ||
|
|
0e8ac47fcf | ||
|
|
92a06f6274 | ||
|
|
553bb7fd61 | ||
|
|
bc437a3913 | ||
|
|
e6e0bccffb | ||
|
|
4569407d18 | ||
|
|
d8ef413216 | ||
|
|
c63a1ae047 | ||
|
|
692cdf7ae3 | ||
|
|
e5dfb75262 | ||
|
|
35e4fb949d | ||
|
|
20d4967356 | ||
|
|
6bf9eaf309 | ||
|
|
a142c5d060 | ||
|
|
61052f80bc | ||
|
|
f2133c31d5 | ||
|
|
00275ac56d | ||
|
|
1374821344 | ||
|
|
2548582036 | ||
|
|
32a1104769 | ||
|
|
3be7ec2274 | ||
|
|
38dd416ced | ||
|
|
e8f8e96521 | ||
|
|
49cf0aeedd |
@@ -34,7 +34,9 @@
|
||||
#cmakedefine HAVE_FLOOR
|
||||
|
||||
/* Having Geant4 installed */
|
||||
#ifndef HAVE_GEANT4
|
||||
#cmakedefine HAVE_GEANT4
|
||||
#endif
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#cmakedefine HAVE_INTTYPES_H
|
||||
|
||||
@@ -3,7 +3,14 @@
|
||||
##### CMAKE LISTS ##############################################################
|
||||
################################################################################
|
||||
|
||||
if(EXISTS "${CMAKE_BINARY_DIR}/conan_toolchain.cmake")
|
||||
include("${CMAKE_BINARY_DIR}/conan_toolchain.cmake")
|
||||
endif()
|
||||
|
||||
cmake_minimum_required (VERSION 3.26)
|
||||
|
||||
set(QT_NO_VERSION_CHECK TRUE)
|
||||
|
||||
if(POLICY CMP0167)
|
||||
cmake_policy(SET CMP0167 NEW)
|
||||
endif()
|
||||
@@ -107,6 +114,8 @@ set(Boost_USE_MULTITHREADED ON)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
message(STATUS "CMAKE_PREFIX_PATH is ${CMAKE_PREFIX_PATH}")
|
||||
|
||||
find_package(HDF5 REQUIRED CONFIG)
|
||||
|
||||
find_package(Boost 1.45.0 COMPONENTS program_options serialization unit_test_framework REQUIRED)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
@@ -118,15 +127,12 @@ find_package(ROOT CONFIG REQUIRED)
|
||||
include(${ROOT_USE_FILE})
|
||||
|
||||
find_package(VTK REQUIRED)
|
||||
# include(${VTK_USE_FILE})
|
||||
|
||||
find_package(pybind11 REQUIRED)
|
||||
|
||||
|
||||
option(CENTOS_SUPPORT "VTK definitions for CentOS" OFF)
|
||||
if(CENTOS_SUPPORT)
|
||||
find_package(VTK CONFIG REQUIRED)
|
||||
include(${VTK_USE_FILE})
|
||||
# include(${VTK_USE_FILE})
|
||||
else()
|
||||
find_package(VTK REQUIRED
|
||||
COMPONENTS CommonColor
|
||||
@@ -146,7 +152,36 @@ else()
|
||||
RenderingFreeType
|
||||
RenderingGL2PSOpenGL2
|
||||
RenderingOpenGL2
|
||||
RenderingVolumeOpenGL2)
|
||||
RenderingVolumeOpenGL2
|
||||
IOGeometry
|
||||
GUISupportQt)
|
||||
endif()
|
||||
|
||||
find_package(Qt6 COMPONENTS Widgets)
|
||||
if(Qt6_FOUND)
|
||||
add_compile_definitions(HAVE_QT)
|
||||
endif()
|
||||
|
||||
find_package(Geant4)
|
||||
if(Geant4_FOUND)
|
||||
message(STATUS "Geant4 libs: ${Geant4_LIBRARIES}")
|
||||
add_compile_definitions(HAVE_GEANT4)
|
||||
set(HAVE_GEANT4 1)
|
||||
|
||||
# Sanitize Geant4 targets to remove Qt5 dependencies that conflict with VTK/Qt6
|
||||
if(TARGET Geant4::G4interfaces)
|
||||
set_target_properties(Geant4::G4interfaces PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "Geant4::G4global;Geant4::G4graphics_reps;Geant4::G4intercoms"
|
||||
)
|
||||
endif()
|
||||
if(TARGET Geant4::G4OpenGL)
|
||||
set_target_properties(Geant4::G4OpenGL PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "Geant4::G4vis_management;Geant4::G4graphics_reps;Geant4::G4geometry;Geant4::G4materials;Geant4::G4intercoms;Geant4::G4global;OpenGL::GL;OpenGL::GLU"
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Geant4 NOT found - optional features will be disabled")
|
||||
set(HAVE_GEANT4 0)
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_INCLUDES CMAKE_REQUIRED_INCLUDES math.h)
|
||||
@@ -204,8 +239,8 @@ add_subdirectory(${SRC_DIR}/Core)
|
||||
include_directories(${SRC_DIR}/Math)
|
||||
add_subdirectory(${SRC_DIR}/Math)
|
||||
|
||||
include_directories(${SRC_DIR}/Detectors)
|
||||
add_subdirectory(${SRC_DIR}/Detectors)
|
||||
include_directories(${SRC_DIR}/HEP)
|
||||
add_subdirectory(${SRC_DIR}/HEP)
|
||||
|
||||
include_directories(${SRC_DIR}/Root)
|
||||
add_subdirectory(${SRC_DIR}/Root)
|
||||
@@ -215,7 +250,7 @@ add_subdirectory(${SRC_DIR}/Vtk)
|
||||
|
||||
add_subdirectory(${SRC_DIR}/Python)
|
||||
|
||||
#add_subdirectory("${SRC_DIR}/utils/make_recipe")
|
||||
add_subdirectory(app)
|
||||
|
||||
## Documentation and packages
|
||||
|
||||
|
||||
3
app/CMakeLists.txt
Normal file
3
app/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
if(HAVE_GEANT4)
|
||||
add_subdirectory(gcompose)
|
||||
endif()
|
||||
49
app/gcompose/CMakeLists.txt
Normal file
49
app/gcompose/CMakeLists.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
add_executable(gcompose
|
||||
src/main.cpp
|
||||
src/MainWindow.h
|
||||
src/MainWindow.cpp
|
||||
src/ViewportPane.h
|
||||
src/ViewportPane.cpp
|
||||
src/MainPanel.h
|
||||
src/MainPanel.cpp
|
||||
src/ContextPanel.h
|
||||
src/ContextPanel.cpp
|
||||
src/ContextModel.h
|
||||
src/ContextModel.cpp
|
||||
src/StyleManager.h
|
||||
src/StyleManager.cpp
|
||||
)
|
||||
|
||||
set_target_properties(gcompose PROPERTIES
|
||||
AUTOMOC ON
|
||||
AUTOUIC ON
|
||||
AUTORCC ON
|
||||
)
|
||||
|
||||
target_include_directories(gcompose PRIVATE
|
||||
${SRC_DIR}
|
||||
${PROJECT_BINARY_DIR}
|
||||
${Geant4_INCLUDE_DIRS}
|
||||
${VTK_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Filter Geant4 libraries to remove Qt-dependent ones
|
||||
set(Geant4_LIBS_FILTERED ${Geant4_LIBRARIES})
|
||||
if(Geant4_LIBS_FILTERED)
|
||||
list(REMOVE_ITEM Geant4_LIBS_FILTERED Geant4::G4interfaces Geant4::G4OpenGL Geant4::G4visQt3D)
|
||||
endif()
|
||||
|
||||
target_link_libraries(gcompose
|
||||
mutomCore
|
||||
mutomMath
|
||||
mutomGeant
|
||||
mutomVtk
|
||||
mutomRoot
|
||||
${Geant4_LIBS_FILTERED}
|
||||
${VTK_LIBRARIES}
|
||||
Qt6::Widgets
|
||||
VTK::GUISupportQt
|
||||
)
|
||||
|
||||
install(TARGETS gcompose RUNTIME DESTINATION bin)
|
||||
161
app/gcompose/src/ContextModel.cpp
Normal file
161
app/gcompose/src/ContextModel.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "ContextModel.h"
|
||||
#include <QString>
|
||||
#include <typeinfo>
|
||||
#include <cxxabi.h>
|
||||
#include <functional>
|
||||
#include "Core/Object.h"
|
||||
|
||||
ContextModel::ContextModel(QObject* parent)
|
||||
: QAbstractItemModel(parent), m_rootContext(nullptr) {}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
QModelIndex ContextModel::index(int row, int column, const QModelIndex& parent) const {
|
||||
if (!hasIndex(row, column, parent) || !m_rootContext) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
if (!parent.isValid()) {
|
||||
if (row < m_rootContext->GetCount()) {
|
||||
return createIndex(row, column, m_rootContext->GetObject(row));
|
||||
}
|
||||
} else {
|
||||
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||
uLib::ObjectsContext* parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj);
|
||||
if (parentCtx && row < parentCtx->GetCount()) {
|
||||
return createIndex(row, column, parentCtx->GetObject(row));
|
||||
}
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex ContextModel::parent(const QModelIndex& child) const {
|
||||
if (!child.isValid() || !m_rootContext) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
uLib::Object* childObj = static_cast<uLib::Object*>(child.internalPointer());
|
||||
|
||||
// Finding the parent of childObj is O(N) since there is no parent pointer.
|
||||
// We just do a recursive search starting from root context.
|
||||
std::function<uLib::ObjectsContext*(uLib::ObjectsContext*, uLib::Object*)> findParent =
|
||||
[&findParent](uLib::ObjectsContext* ctx, uLib::Object* target) -> uLib::ObjectsContext* {
|
||||
for (const auto& obj : ctx->GetObjects()) {
|
||||
if (obj == target) return ctx;
|
||||
if (auto subCtx = dynamic_cast<uLib::ObjectsContext*>(obj)) {
|
||||
if (auto p = findParent(subCtx, target)) return p;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
uLib::ObjectsContext* parentCtx = findParent(m_rootContext, childObj);
|
||||
if (!parentCtx || parentCtx == m_rootContext) {
|
||||
return QModelIndex(); // Root items have invalid parent index
|
||||
}
|
||||
|
||||
// Now need to find the row of parentCtx in its own parent Context.
|
||||
uLib::ObjectsContext* grandParentCtx = findParent(m_rootContext, parentCtx);
|
||||
if (!grandParentCtx) grandParentCtx = m_rootContext;
|
||||
|
||||
int row = -1;
|
||||
for (size_t i = 0; i < grandParentCtx->GetCount(); ++i) {
|
||||
if (grandParentCtx->GetObject(i) == parentCtx) {
|
||||
row = (int)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (row != -1) {
|
||||
return createIndex(row, 0, parentCtx);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int ContextModel::rowCount(const QModelIndex& parent) const {
|
||||
if (!m_rootContext) return 0;
|
||||
|
||||
if (!parent.isValid()) {
|
||||
return m_rootContext->GetCount();
|
||||
}
|
||||
|
||||
uLib::Object* parentObj = static_cast<uLib::Object*>(parent.internalPointer());
|
||||
if (auto parentCtx = dynamic_cast<uLib::ObjectsContext*>(parentObj)) {
|
||||
return parentCtx->GetCount();
|
||||
}
|
||||
return 0; // leaf node
|
||||
}
|
||||
|
||||
int ContextModel::columnCount(const QModelIndex& parent) const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static QString getDemangledName(const std::type_info& info) {
|
||||
int status = -4;
|
||||
char* demangled = abi::__cxa_demangle(info.name(), nullptr, nullptr, &status);
|
||||
QString res = (status == 0 && demangled) ? QString::fromUtf8(demangled) : QString::fromUtf8(info.name());
|
||||
if (demangled) free(demangled);
|
||||
|
||||
// Remove namespaces
|
||||
int lastColon = res.lastIndexOf("::");
|
||||
if (lastColon != -1) {
|
||||
res = res.mid(lastColon + 2);
|
||||
}
|
||||
|
||||
// Remove "class " prefix if any
|
||||
if (res.startsWith("class ")) res = res.mid(6);
|
||||
return res;
|
||||
}
|
||||
|
||||
QVariant ContextModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid()) return QVariant();
|
||||
|
||||
uLib::Object* obj = static_cast<uLib::Object*>(index.internalPointer());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
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();
|
||||
}
|
||||
|
||||
QVariant ContextModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0) {
|
||||
return "Object Context";
|
||||
}
|
||||
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<uLib::Object*>(index.internalPointer());
|
||||
obj->SetInstanceName(value.toString().toStdString());
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
28
app/gcompose/src/ContextModel.h
Normal file
28
app/gcompose/src/ContextModel.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef CONTEXT_MODEL_H
|
||||
#define CONTEXT_MODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include "Core/ObjectsContext.h"
|
||||
|
||||
class ContextModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ContextModel(QObject* parent = nullptr);
|
||||
virtual ~ContextModel();
|
||||
|
||||
void setContext(uLib::ObjectsContext* context);
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
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;
|
||||
};
|
||||
|
||||
#endif // CONTEXT_MODEL_H
|
||||
138
app/gcompose/src/ContextPanel.cpp
Normal file
138
app/gcompose/src/ContextPanel.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "ContextPanel.h"
|
||||
#include "ContextModel.h"
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QTreeView>
|
||||
#include <QSplitter>
|
||||
#include <QList>
|
||||
#include <QShortcut>
|
||||
#include <QItemSelectionModel>
|
||||
#include <Vtk/vtkQViewport.h>
|
||||
#include <Vtk/vtkObjectsContext.h>
|
||||
|
||||
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);
|
||||
|
||||
// Title bar setup
|
||||
m_titleBar = new QWidget(this);
|
||||
m_titleBar->setObjectName("PaneTitleBar");
|
||||
m_titleBar->setFixedHeight(22);
|
||||
|
||||
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||
|
||||
m_titleLabel = new QLabel("Context Panel", m_titleBar);
|
||||
m_titleLabel->setObjectName("TitleLabel");
|
||||
titleLayout->addWidget(m_titleLabel);
|
||||
titleLayout->addStretch();
|
||||
|
||||
m_layout->addWidget(m_titleBar);
|
||||
|
||||
m_treeView = new QTreeView(this);
|
||||
m_treeView->setObjectName("ContextTree");
|
||||
m_treeView->setHeaderHidden(false);
|
||||
|
||||
m_model = new ContextModel(this);
|
||||
m_treeView->setModel(m_model);
|
||||
|
||||
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<int> 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<uLib::Object*> toRemove;
|
||||
for (const auto& idx : selectedIndexes) {
|
||||
if (idx.column() == 0) {
|
||||
toRemove.push_back(static_cast<uLib::Object*>(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); // redundant: child puppets are added individually
|
||||
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
|
||||
uLib::Object::connect(m_vtkContext, &uLib::Vtk::vtkObjectsContext::PuppetRemoved, [this](uLib::Vtk::Puppet* p) {
|
||||
if (this->m_vtkView && p) {
|
||||
this->m_vtkView->RemovePuppet(*p);
|
||||
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<uLib::Object*>(selected.indexes().first().internalPointer());
|
||||
}
|
||||
|
||||
emit objectSelected(target);
|
||||
|
||||
if (m_vtkContext) {
|
||||
auto* puppet = m_vtkContext->GetPuppet(target);
|
||||
m_vtkView->SelectPuppet(puppet);
|
||||
}
|
||||
}
|
||||
50
app/gcompose/src/ContextPanel.h
Normal file
50
app/gcompose/src/ContextPanel.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef CONTEXT_PANEL_H
|
||||
#define CONTEXT_PANEL_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QItemSelection>
|
||||
|
||||
class QTreeView;
|
||||
class QVBoxLayout;
|
||||
class QLabel;
|
||||
class ContextModel;
|
||||
class QSplitter;
|
||||
|
||||
|
||||
namespace uLib {
|
||||
class Object;
|
||||
class ObjectsContext;
|
||||
namespace Vtk {
|
||||
class QViewport;
|
||||
class vtkObjectsContext;
|
||||
}
|
||||
}
|
||||
|
||||
class ContextPanel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ContextPanel(QWidget* parent = nullptr);
|
||||
virtual ~ContextPanel();
|
||||
|
||||
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
|
||||
186
app/gcompose/src/MainPanel.cpp
Normal file
186
app/gcompose/src/MainPanel.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#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 <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QSplitter>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include "StyleManager.h"
|
||||
|
||||
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);
|
||||
|
||||
// 1. Top Menu Panel
|
||||
auto* menuPanel = new QWidget(this);
|
||||
menuPanel->setObjectName("MenuPanel");
|
||||
menuPanel->setFixedHeight(36);
|
||||
|
||||
auto* menuLayout = new QHBoxLayout(menuPanel);
|
||||
menuLayout->setContentsMargins(10, 0, 10, 0);
|
||||
menuLayout->setSpacing(15);
|
||||
|
||||
auto* logo = new QLabel("G-COMPOSE", menuPanel);
|
||||
logo->setObjectName("LogoLabel");
|
||||
|
||||
// File Menu Button
|
||||
auto* btnFile = new QPushButton("File", menuPanel);
|
||||
btnFile->setObjectName("MenuButton");
|
||||
auto* fileMenu = new QMenu(btnFile);
|
||||
fileMenu->addAction("Open", this, &MainPanel::onOpen);
|
||||
fileMenu->addAction("Save", this, &MainPanel::onSave);
|
||||
btnFile->setMenu(fileMenu);
|
||||
|
||||
// Theme Menu Button
|
||||
auto* btnTheme = new QPushButton("Theme", menuPanel);
|
||||
btnTheme->setObjectName("MenuButton");
|
||||
auto* themeMenu = new QMenu(btnTheme);
|
||||
themeMenu->addAction("Dark", this, &MainPanel::onDarkTheme);
|
||||
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();
|
||||
|
||||
mainLayout->addWidget(menuPanel);
|
||||
|
||||
// 2. Central Splitter Area
|
||||
m_rootSplitter = new QSplitter(Qt::Horizontal, this);
|
||||
m_contextPanel = new ContextPanel(m_rootSplitter);
|
||||
m_rootSplitter->addWidget(m_contextPanel);
|
||||
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<uLib::Vtk::QViewport*>(m_firstPane->currentViewport())) {
|
||||
uLib::Vtk::Puppet* puppet = nullptr;
|
||||
if (m_mainVtkContext) {
|
||||
puppet = m_mainVtkContext->GetPuppet(obj);
|
||||
}
|
||||
viewport->SelectPuppet(puppet);
|
||||
}
|
||||
});
|
||||
|
||||
// Set initial sizes
|
||||
QList<int> sizes;
|
||||
sizes << 200 << 1000;
|
||||
m_rootSplitter->setSizes(sizes);
|
||||
|
||||
mainLayout->addWidget(m_rootSplitter, 1);
|
||||
}
|
||||
|
||||
void MainPanel::setContext(uLib::ObjectsContext* context) {
|
||||
m_context = context;
|
||||
m_contextPanel->setContext(context);
|
||||
|
||||
if (m_mainVtkContext) {
|
||||
if (auto* viewport = qobject_cast<uLib::Vtk::QViewport*>(m_firstPane->currentViewport())) {
|
||||
viewport->RemovePuppet(*m_mainVtkContext);
|
||||
}
|
||||
delete m_mainVtkContext;
|
||||
m_mainVtkContext = nullptr;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
if (auto* viewport = qobject_cast<uLib::Vtk::QViewport*>(m_firstPane->currentViewport())) {
|
||||
m_mainVtkContext = new uLib::Vtk::vtkObjectsContext(context);
|
||||
// viewport->AddPuppet(*m_mainVtkContext); // redundant
|
||||
|
||||
uLib::Object::connect(m_mainVtkContext, &uLib::Vtk::vtkObjectsContext::PuppetAdded, [this](uLib::Vtk::Puppet* p) {
|
||||
if (p) {
|
||||
auto panes = this->findChildren<ViewportPane*>();
|
||||
for (auto* pane : panes) {
|
||||
if (auto* vp = qobject_cast<uLib::Vtk::QViewport*>(pane->currentViewport())) {
|
||||
vp->AddPuppet(*p);
|
||||
vp->ZoomAuto();
|
||||
vp->Render();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uLib::Object::connect(m_mainVtkContext, &uLib::Vtk::vtkObjectsContext::PuppetRemoved, [this](uLib::Vtk::Puppet* p) {
|
||||
if (p) {
|
||||
auto panes = this->findChildren<ViewportPane*>();
|
||||
for (auto* pane : panes) {
|
||||
if (auto* vp = qobject_cast<uLib::Vtk::QViewport*>(pane->currentViewport())) {
|
||||
vp->RemovePuppet(*p);
|
||||
vp->Render();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add any puppets that were created during m_mainVtkContext's construction to all panes
|
||||
auto panes = this->findChildren<ViewportPane*>();
|
||||
for (auto* obj : context->GetObjects()) {
|
||||
if (auto* p = m_mainVtkContext->GetPuppet(obj)) {
|
||||
for (auto* pane : panes) {
|
||||
if (auto* vp = qobject_cast<uLib::Vtk::QViewport*>(pane->currentViewport())) {
|
||||
vp->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() {
|
||||
// Placeholder for open logic
|
||||
}
|
||||
|
||||
void MainPanel::onSave() {
|
||||
// Placeholder for save logic
|
||||
}
|
||||
|
||||
void MainPanel::onDarkTheme() {
|
||||
StyleManager::applyStyle(qApp, "dark");
|
||||
}
|
||||
|
||||
void MainPanel::onBrightTheme() {
|
||||
StyleManager::applyStyle(qApp, "bright");
|
||||
}
|
||||
|
||||
MainPanel::~MainPanel() {}
|
||||
42
app/gcompose/src/MainPanel.h
Normal file
42
app/gcompose/src/MainPanel.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef MAINPANEL_H
|
||||
#define MAINPANEL_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QSplitter;
|
||||
class ViewportPane;
|
||||
class ContextPanel;
|
||||
|
||||
namespace uLib {
|
||||
class ObjectsContext;
|
||||
namespace Vtk {
|
||||
class vtkObjectsContext;
|
||||
}
|
||||
}
|
||||
|
||||
class MainPanel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MainPanel(QWidget* parent = nullptr);
|
||||
virtual ~MainPanel();
|
||||
|
||||
void setContext(uLib::ObjectsContext* context);
|
||||
ViewportPane* getFirstPane() const { return m_firstPane; }
|
||||
|
||||
private slots:
|
||||
void onOpen();
|
||||
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
|
||||
21
app/gcompose/src/MainWindow.cpp
Normal file
21
app/gcompose/src/MainWindow.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "MainWindow.h"
|
||||
#include <QSplitter>
|
||||
#include "MainPanel.h"
|
||||
#include "Core/ObjectsContext.h"
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
|
||||
m_panel = new MainPanel(this);
|
||||
setCentralWidget(m_panel);
|
||||
|
||||
setWindowTitle("gcompose - Qt VTK Interface");
|
||||
resize(1200, 800);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
}
|
||||
|
||||
void MainWindow::setContext(uLib::ObjectsContext* context) {
|
||||
m_panel->setContext(context);
|
||||
}
|
||||
29
app/gcompose/src/MainWindow.h
Normal file
29
app/gcompose/src/MainWindow.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QVTKOpenGLNativeWidget.h>
|
||||
|
||||
class MainPanel;
|
||||
class ViewportPane;
|
||||
|
||||
namespace uLib {
|
||||
namespace Vtk {
|
||||
}
|
||||
class ObjectsContext;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MainWindow(QWidget* parent = nullptr);
|
||||
virtual ~MainWindow();
|
||||
|
||||
void setContext(uLib::ObjectsContext* context);
|
||||
MainPanel* getPanel() { return m_panel; }
|
||||
|
||||
private:
|
||||
MainPanel* m_panel;
|
||||
};
|
||||
|
||||
#endif
|
||||
169
app/gcompose/src/QViewportPane.cpp
Normal file
169
app/gcompose/src/QViewportPane.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "QViewportPane.h"
|
||||
#include <Vtk/vtkQViewport.h>
|
||||
#include <Root/QCanvas.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QToolButton>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QSplitter>
|
||||
#include <vtkCamera.h>
|
||||
|
||||
QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) {
|
||||
m_layout = new QVBoxLayout(this);
|
||||
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_layout->setSpacing(0);
|
||||
|
||||
// Title bar setup
|
||||
m_titleBar = new QWidget(this);
|
||||
m_titleBar->setObjectName("PaneTitleBar");
|
||||
m_titleBar->setFixedHeight(22);
|
||||
|
||||
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||
|
||||
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
||||
m_titleLabel->setObjectName("TitleLabel");
|
||||
|
||||
auto* closeBtn = new QToolButton(m_titleBar);
|
||||
closeBtn->setObjectName("PaneCloseButton");
|
||||
closeBtn->setText("X");
|
||||
closeBtn->setFixedSize(18, 18);
|
||||
|
||||
titleLayout->addWidget(m_titleLabel);
|
||||
titleLayout->addStretch();
|
||||
titleLayout->addWidget(closeBtn);
|
||||
|
||||
m_layout->addWidget(m_titleBar);
|
||||
|
||||
m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_titleBar, &QWidget::customContextMenuRequested, this, &QViewportPane::showContextMenu);
|
||||
connect(closeBtn, &QToolButton::clicked, this, &QViewportPane::onCloseRequested);
|
||||
|
||||
addVtkViewport(); // Initialize with a default VTK viewport
|
||||
}
|
||||
|
||||
QViewportPane::~QViewportPane() {}
|
||||
|
||||
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
if (m_viewport) {
|
||||
m_layout->removeWidget(m_viewport);
|
||||
delete m_viewport;
|
||||
}
|
||||
m_viewport = viewport;
|
||||
m_titleLabel->setText(title);
|
||||
|
||||
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_layout->addWidget(m_viewport);
|
||||
}
|
||||
|
||||
void QViewportPane::addVtkViewport() {
|
||||
auto* viewport = new uLib::Vtk::QViewport(this);
|
||||
setViewport(viewport, "VTK Viewport");
|
||||
}
|
||||
|
||||
void QViewportPane::addRootCanvas() {
|
||||
auto* canvas = new uLib::Root::QCanvas(this);
|
||||
setViewport(canvas, "ROOT Canvas");
|
||||
}
|
||||
|
||||
void QViewportPane::onCloseRequested() {
|
||||
QSplitter* parentSplitter = qobject_cast<QSplitter*>(parentWidget());
|
||||
if (parentSplitter && parentSplitter->count() > 1) {
|
||||
deleteLater();
|
||||
} else {
|
||||
// Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas.
|
||||
addVtkViewport();
|
||||
}
|
||||
}
|
||||
|
||||
void QViewportPane::showContextMenu(const QPoint& pos) {
|
||||
QMenu menu(this);
|
||||
QAction* hSplit = menu.addAction("H split");
|
||||
QAction* vSplit = menu.addAction("V split");
|
||||
menu.addSeparator();
|
||||
|
||||
bool isVtk = (qobject_cast<uLib::Vtk::QViewport*>(m_viewport) != nullptr);
|
||||
|
||||
QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport");
|
||||
|
||||
QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos));
|
||||
|
||||
if (selected == hSplit) {
|
||||
AttemptSplit(Qt::Horizontal);
|
||||
} else if (selected == vSplit) {
|
||||
AttemptSplit(Qt::Vertical);
|
||||
} else if (selected == changeType) {
|
||||
if (isVtk) {
|
||||
addRootCanvas();
|
||||
} else {
|
||||
addVtkViewport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QViewportPane::AttemptSplit(Qt::Orientation orientation) {
|
||||
QWidget* p = parentWidget();
|
||||
if (!p) return;
|
||||
|
||||
QSplitter* parentSplitter = qobject_cast<QSplitter*>(p);
|
||||
if (!parentSplitter) return;
|
||||
|
||||
QViewportPane* newPane = new QViewportPane();
|
||||
|
||||
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
|
||||
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
|
||||
if (currentVtk) {
|
||||
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(newPane->currentViewport());
|
||||
if (newVtk) {
|
||||
// Copy puppets
|
||||
for (auto* puppet : currentVtk->getPuppets()) {
|
||||
newVtk->AddPuppet(*puppet);
|
||||
}
|
||||
// Copy camera
|
||||
if (currentVtk->GetRenderer() && newVtk->GetRenderer()) {
|
||||
vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera();
|
||||
vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera();
|
||||
if (currentCam && newCam) {
|
||||
newCam->DeepCopy(currentCam);
|
||||
}
|
||||
}
|
||||
// Sync grid visible and axis
|
||||
newVtk->SetGridVisible(currentVtk->GetGridVisible());
|
||||
newVtk->SetGridAxis(currentVtk->GetGridAxis());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Adjust for ROOT Canvas if that was the active view
|
||||
bool isRoot = (qobject_cast<uLib::Root::QCanvas*>(m_viewport) != nullptr);
|
||||
if (isRoot) {
|
||||
newPane->addRootCanvas();
|
||||
}
|
||||
|
||||
if (parentSplitter->orientation() == orientation) {
|
||||
int index = parentSplitter->indexOf(this);
|
||||
QList<int> sizes = parentSplitter->sizes();
|
||||
int currentSize = sizes.value(index, 0);
|
||||
int half = currentSize / 2;
|
||||
sizes[index] = half;
|
||||
sizes.insert(index + 1, currentSize - half);
|
||||
|
||||
parentSplitter->insertWidget(index + 1, newPane);
|
||||
parentSplitter->setSizes(sizes);
|
||||
} else {
|
||||
int index = parentSplitter->indexOf(this);
|
||||
QList<int> parentSizes = parentSplitter->sizes();
|
||||
|
||||
QSplitter* newSplitter = new QSplitter(orientation);
|
||||
newSplitter->addWidget(this);
|
||||
newSplitter->addWidget(newPane);
|
||||
|
||||
QList<int> subSizes;
|
||||
subSizes << 500 << 500;
|
||||
newSplitter->setSizes(subSizes);
|
||||
|
||||
parentSplitter->insertWidget(index, newSplitter);
|
||||
parentSplitter->setSizes(parentSizes);
|
||||
}
|
||||
}
|
||||
34
app/gcompose/src/QViewportPane.h
Normal file
34
app/gcompose/src/QViewportPane.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef QVIEWPORTPANE_H
|
||||
#define QVIEWPORTPANE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QVBoxLayout;
|
||||
class QLabel;
|
||||
|
||||
class QViewportPane : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QViewportPane(QWidget* parent = nullptr);
|
||||
virtual ~QViewportPane();
|
||||
|
||||
void addVtkViewport();
|
||||
void addRootCanvas();
|
||||
|
||||
QWidget* currentViewport() const { return m_viewport; }
|
||||
|
||||
private slots:
|
||||
void onCloseRequested();
|
||||
void showContextMenu(const QPoint& pos);
|
||||
|
||||
private:
|
||||
void AttemptSplit(Qt::Orientation orientation);
|
||||
void setViewport(QWidget* viewport, const QString& title);
|
||||
|
||||
QVBoxLayout* m_layout;
|
||||
QWidget* m_titleBar;
|
||||
QLabel* m_titleLabel;
|
||||
QWidget* m_viewport;
|
||||
};
|
||||
|
||||
#endif // QVIEWPORTPANE_H
|
||||
44
app/gcompose/src/StyleManager.cpp
Normal file
44
app/gcompose/src/StyleManager.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "StyleManager.h"
|
||||
#include <QApplication>
|
||||
|
||||
static const QString DARK_THEME = R"(
|
||||
QWidget#MenuPanel { background-color: #2b2b2b; border-bottom: 1px solid #111; }
|
||||
QLabel#LogoLabel { font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px; }
|
||||
QPushButton#MenuButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }
|
||||
QPushButton#MenuButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }
|
||||
QWidget#PaneTitleBar { background-color: #333; color: white; }
|
||||
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #ccc; }
|
||||
QToolButton#PaneCloseButton:hover { color: white; background: red; }
|
||||
QMenu { background-color: #2b2b2b; color: white; border: 1px solid #111; }
|
||||
QMenu::item:selected { background-color: #3c3c3c; }
|
||||
QTreeView#ContextTree { background-color: #1e1e1e; color: #ccc; border: none; }
|
||||
QTreeView#ContextTree::item:hover { background-color: #2a2d2e; }
|
||||
QTreeView#ContextTree::item:selected { background-color: #094771; color: white; }
|
||||
QHeaderView::section { background-color: #252526; color: #ccc; border: 1px solid #323233; padding: 4px; }
|
||||
)";
|
||||
|
||||
static const QString BRIGHT_THEME = R"(
|
||||
QWidget#MenuPanel { background-color: #f0f0f0; border-bottom: 1px solid #ccc; }
|
||||
QLabel#LogoLabel { font-weight: bold; color: #005a9e; font-size: 14px; letter-spacing: 1px; }
|
||||
QPushButton#MenuButton { background: transparent; color: #333; border: none; padding: 5px 10px; }
|
||||
QPushButton#MenuButton:hover { background: #d0d0d0; color: black; border-radius: 4px; }
|
||||
QWidget#PaneTitleBar { background-color: #e0e0e0; color: black; }
|
||||
QToolButton#PaneCloseButton { border: none; font-weight: bold; background: transparent; color: #666; }
|
||||
QToolButton#PaneCloseButton:hover { color: white; background: #e81123; }
|
||||
QMenu { background-color: #f0f0f0; color: black; border: 1px solid #ccc; }
|
||||
QMenu::item:selected { background-color: #d0d0d0; }
|
||||
QTreeView#ContextTree { background-color: #ffffff; color: #333; border: none; }
|
||||
QTreeView#ContextTree::item:hover { background-color: #f2f2f2; }
|
||||
QTreeView#ContextTree::item:selected { background-color: #0078d7; color: white; }
|
||||
QHeaderView::section { background-color: #f3f3f3; color: #333; border: 1px solid #ccc; padding: 4px; }
|
||||
)";
|
||||
|
||||
void StyleManager::applyStyle(QApplication* app, const QString& themeName) {
|
||||
if (!app) return;
|
||||
|
||||
if (themeName == "bright") {
|
||||
app->setStyleSheet(BRIGHT_THEME);
|
||||
} else {
|
||||
app->setStyleSheet(DARK_THEME); // default
|
||||
}
|
||||
}
|
||||
13
app/gcompose/src/StyleManager.h
Normal file
13
app/gcompose/src/StyleManager.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef STYLEMANAGER_H
|
||||
#define STYLEMANAGER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class QApplication;
|
||||
|
||||
class StyleManager {
|
||||
public:
|
||||
static void applyStyle(QApplication* app, const QString& themeName);
|
||||
};
|
||||
|
||||
#endif // STYLEMANAGER_H
|
||||
169
app/gcompose/src/ViewportPane.cpp
Normal file
169
app/gcompose/src/ViewportPane.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
#include "ViewportPane.h"
|
||||
#include <Vtk/vtkQViewport.h>
|
||||
#include <Root/QCanvas.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QToolButton>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QSplitter>
|
||||
#include <vtkCamera.h>
|
||||
|
||||
ViewportPane::ViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) {
|
||||
m_layout = new QVBoxLayout(this);
|
||||
m_layout->setContentsMargins(0, 0, 0, 0);
|
||||
m_layout->setSpacing(0);
|
||||
|
||||
// Title bar setup
|
||||
m_titleBar = new QWidget(this);
|
||||
m_titleBar->setObjectName("PaneTitleBar");
|
||||
m_titleBar->setFixedHeight(22);
|
||||
|
||||
auto* titleLayout = new QHBoxLayout(m_titleBar);
|
||||
titleLayout->setContentsMargins(5, 0, 5, 0);
|
||||
|
||||
m_titleLabel = new QLabel("Viewport", m_titleBar);
|
||||
m_titleLabel->setObjectName("TitleLabel");
|
||||
|
||||
auto* closeBtn = new QToolButton(m_titleBar);
|
||||
closeBtn->setObjectName("PaneCloseButton");
|
||||
closeBtn->setText("X");
|
||||
closeBtn->setFixedSize(18, 18);
|
||||
|
||||
titleLayout->addWidget(m_titleLabel);
|
||||
titleLayout->addStretch();
|
||||
titleLayout->addWidget(closeBtn);
|
||||
|
||||
m_layout->addWidget(m_titleBar);
|
||||
|
||||
m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(m_titleBar, &QWidget::customContextMenuRequested, this, &ViewportPane::showContextMenu);
|
||||
connect(closeBtn, &QToolButton::clicked, this, &ViewportPane::onCloseRequested);
|
||||
|
||||
addVtkViewport(); // Initialize with a default VTK viewport
|
||||
}
|
||||
|
||||
ViewportPane::~ViewportPane() {}
|
||||
|
||||
void ViewportPane::setViewport(QWidget* viewport, const QString& title) {
|
||||
if (m_viewport) {
|
||||
m_layout->removeWidget(m_viewport);
|
||||
delete m_viewport;
|
||||
}
|
||||
m_viewport = viewport;
|
||||
m_titleLabel->setText(title);
|
||||
|
||||
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_layout->addWidget(m_viewport);
|
||||
}
|
||||
|
||||
void ViewportPane::addVtkViewport() {
|
||||
auto* viewport = new uLib::Vtk::QViewport(this);
|
||||
setViewport(viewport, "VTK Viewport");
|
||||
}
|
||||
|
||||
void ViewportPane::addRootCanvas() {
|
||||
auto* canvas = new uLib::Root::QCanvas(this);
|
||||
setViewport(canvas, "ROOT Canvas");
|
||||
}
|
||||
|
||||
void ViewportPane::onCloseRequested() {
|
||||
QSplitter* parentSplitter = qobject_cast<QSplitter*>(parentWidget());
|
||||
if (parentSplitter && parentSplitter->count() > 1) {
|
||||
deleteLater();
|
||||
} else {
|
||||
// Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas.
|
||||
addVtkViewport();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportPane::showContextMenu(const QPoint& pos) {
|
||||
QMenu menu(this);
|
||||
QAction* hSplit = menu.addAction("H split");
|
||||
QAction* vSplit = menu.addAction("V split");
|
||||
menu.addSeparator();
|
||||
|
||||
bool isVtk = (qobject_cast<uLib::Vtk::QViewport*>(m_viewport) != nullptr);
|
||||
|
||||
QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport");
|
||||
|
||||
QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos));
|
||||
|
||||
if (selected == hSplit) {
|
||||
AttemptSplit(Qt::Horizontal);
|
||||
} else if (selected == vSplit) {
|
||||
AttemptSplit(Qt::Vertical);
|
||||
} else if (selected == changeType) {
|
||||
if (isVtk) {
|
||||
addRootCanvas();
|
||||
} else {
|
||||
addVtkViewport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportPane::AttemptSplit(Qt::Orientation orientation) {
|
||||
QWidget* p = parentWidget();
|
||||
if (!p) return;
|
||||
|
||||
QSplitter* parentSplitter = qobject_cast<QSplitter*>(p);
|
||||
if (!parentSplitter) return;
|
||||
|
||||
ViewportPane* newPane = new ViewportPane();
|
||||
|
||||
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
|
||||
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
|
||||
if (currentVtk) {
|
||||
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(newPane->currentViewport());
|
||||
if (newVtk) {
|
||||
// Copy puppets
|
||||
for (auto* puppet : currentVtk->getPuppets()) {
|
||||
newVtk->AddPuppet(*puppet);
|
||||
}
|
||||
// Copy camera
|
||||
if (currentVtk->GetRenderer() && newVtk->GetRenderer()) {
|
||||
vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera();
|
||||
vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera();
|
||||
if (currentCam && newCam) {
|
||||
newCam->DeepCopy(currentCam);
|
||||
}
|
||||
}
|
||||
// Sync grid visible and axis
|
||||
newVtk->SetGridVisible(currentVtk->GetGridVisible());
|
||||
newVtk->SetGridAxis(currentVtk->GetGridAxis());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Adjust for ROOT Canvas if that was the active view
|
||||
bool isRoot = (qobject_cast<uLib::Root::QCanvas*>(m_viewport) != nullptr);
|
||||
if (isRoot) {
|
||||
newPane->addRootCanvas();
|
||||
}
|
||||
|
||||
if (parentSplitter->orientation() == orientation) {
|
||||
int index = parentSplitter->indexOf(this);
|
||||
QList<int> sizes = parentSplitter->sizes();
|
||||
int currentSize = sizes.value(index, 0);
|
||||
int half = currentSize / 2;
|
||||
sizes[index] = half;
|
||||
sizes.insert(index + 1, currentSize - half);
|
||||
|
||||
parentSplitter->insertWidget(index + 1, newPane);
|
||||
parentSplitter->setSizes(sizes);
|
||||
} else {
|
||||
int index = parentSplitter->indexOf(this);
|
||||
QList<int> parentSizes = parentSplitter->sizes();
|
||||
|
||||
QSplitter* newSplitter = new QSplitter(orientation);
|
||||
newSplitter->addWidget(this);
|
||||
newSplitter->addWidget(newPane);
|
||||
|
||||
QList<int> subSizes;
|
||||
subSizes << 500 << 500;
|
||||
newSplitter->setSizes(subSizes);
|
||||
|
||||
parentSplitter->insertWidget(index, newSplitter);
|
||||
parentSplitter->setSizes(parentSizes);
|
||||
}
|
||||
}
|
||||
34
app/gcompose/src/ViewportPane.h
Normal file
34
app/gcompose/src/ViewportPane.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef VIEWPORTPANE_H
|
||||
#define VIEWPORTPANE_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QVBoxLayout;
|
||||
class QLabel;
|
||||
|
||||
class ViewportPane : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ViewportPane(QWidget* parent = nullptr);
|
||||
virtual ~ViewportPane();
|
||||
|
||||
void addVtkViewport();
|
||||
void addRootCanvas();
|
||||
|
||||
QWidget* currentViewport() const { return m_viewport; }
|
||||
|
||||
private slots:
|
||||
void onCloseRequested();
|
||||
void showContextMenu(const QPoint& pos);
|
||||
|
||||
private:
|
||||
void AttemptSplit(Qt::Orientation orientation);
|
||||
void setViewport(QWidget* viewport, const QString& title);
|
||||
|
||||
QVBoxLayout* m_layout;
|
||||
QWidget* m_titleBar;
|
||||
QLabel* m_titleLabel;
|
||||
QWidget* m_viewport;
|
||||
};
|
||||
|
||||
#endif // VIEWPORTPANE_H
|
||||
55
app/gcompose/src/main.cpp
Normal file
55
app/gcompose/src/main.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <QApplication>
|
||||
#include "MainWindow.h"
|
||||
#include "MainPanel.h"
|
||||
#include "ViewportPane.h"
|
||||
#include "StyleManager.h"
|
||||
|
||||
#include "Math/ContainerBox.h"
|
||||
#include <HEP/Geant/Scene.h>
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
|
||||
|
||||
#include <Vtk/vtkContainerBox.h>
|
||||
#include <Vtk/vtkQViewport.h>
|
||||
|
||||
#include "Core/ObjectsContext.h"
|
||||
|
||||
#include <vtkSmartPointer.h>
|
||||
#include <vtkCubeSource.h>
|
||||
#include <vtkPolyDataMapper.h>
|
||||
#include <vtkActor.h>
|
||||
#include <vtkRenderer.h>
|
||||
|
||||
|
||||
#include "Math/Units.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace uLib;
|
||||
using namespace uLib::literals;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
QApplication app(argc, argv);
|
||||
StyleManager::applyStyle(&app, "dark");
|
||||
std::cout << "Starting gcompose Qt application..." << std::endl;
|
||||
|
||||
// ContainerBox world_box(Vector3f(1, 1, 1));
|
||||
// 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");
|
||||
// scene.Initialize();
|
||||
|
||||
uLib::ObjectsContext globalContext;
|
||||
// globalContext.AddObject(&world_box);
|
||||
// globalContext.AddObject(&scene);
|
||||
|
||||
// 2. Initialize MainWindow (contains embedded VTK QViewport)
|
||||
MainWindow window;
|
||||
window.setContext(&globalContext);
|
||||
|
||||
std::cout << "Geant4 and VTK scenes are ready." << std::endl;
|
||||
|
||||
window.show();
|
||||
return app.exec();
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
[requires]
|
||||
eigen/3.4.0
|
||||
boost/1.83.0
|
||||
pybind11/3.0.2
|
||||
# pybind11/3.0.2
|
||||
hdf5/1.14.3
|
||||
|
||||
[generators]
|
||||
CMakeDeps
|
||||
CMakeToolchain
|
||||
|
||||
[options]
|
||||
hdf5/*:threadsafe=True
|
||||
hdf5/*:enable_unsupported=True
|
||||
|
||||
@@ -198,7 +198,7 @@ public:
|
||||
return &bpos;
|
||||
}
|
||||
|
||||
template <class T> Archive &operator<<(T &t) {
|
||||
template <class T> Archive &operator<<(const T &t) {
|
||||
// to get access you must redefine save_override by typing
|
||||
// "using save_override" in archive impl
|
||||
this->This()->save_override(t);
|
||||
@@ -206,12 +206,8 @@ public:
|
||||
}
|
||||
|
||||
// the & operator
|
||||
template <class T> Archive &operator&(T &t) {
|
||||
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
|
||||
return *this->This() << const_cast<const T &>(t);
|
||||
#else
|
||||
template <class T> Archive &operator&(const T &t) {
|
||||
return *this->This() << t;
|
||||
#endif
|
||||
}
|
||||
|
||||
// the == operator
|
||||
@@ -364,7 +360,6 @@ public:
|
||||
boost::serialization::hrp<T> &t) {
|
||||
this->This()->load_start(t.name());
|
||||
this->detail_common_iarchive::load_override(t.value());
|
||||
// t.stov();
|
||||
this->This()->load_end(t.name());
|
||||
}
|
||||
|
||||
@@ -432,8 +427,7 @@ public:
|
||||
#endif
|
||||
::boost::serialization::hrp<T> &t) {
|
||||
this->This()->save_start(t.name());
|
||||
// t.vtos();
|
||||
// this->detail_common_oarchive::save_override(t.const_value());
|
||||
this->detail_common_oarchive::save_override(t.const_value());
|
||||
this->This()->save_end(t.name());
|
||||
}
|
||||
|
||||
@@ -467,14 +461,10 @@ public:
|
||||
text_iarchive(std::istream &is, unsigned int flags = 0)
|
||||
: text_iarchive_impl<Archive>(is, flags) {}
|
||||
|
||||
using basic_text_iarchive::load_override;
|
||||
using base::load_override;
|
||||
|
||||
void load_override(boost::archive::object_id_type &t) {}
|
||||
|
||||
// class_name_type can't be handled here as it depends upon the
|
||||
// char type used by the stream. So require the derived implementation.
|
||||
// derived in this case is xml_iarchive_impl or base ..
|
||||
using base::load_override;
|
||||
|
||||
void load_override(const char *str) {
|
||||
StringReader sr(basic_text_iprimitive::is);
|
||||
@@ -532,7 +522,7 @@ public:
|
||||
hrt_iarchive(std::istream &is, unsigned int flags = 0)
|
||||
: base(is, flags | boost::archive::no_header) {}
|
||||
|
||||
using basic_text_iarchive::load_override;
|
||||
using base::load_override;
|
||||
|
||||
// hide all archive props //
|
||||
void load_override(boost::archive::object_id_type &t) {}
|
||||
@@ -544,10 +534,6 @@ public:
|
||||
void load_override(boost::archive::class_name_type &t) {}
|
||||
void load_override(boost::archive::tracking_type &t) {}
|
||||
|
||||
// class_name_type can't be handled here as it depends upon the
|
||||
// char type used by the stream. So require the derived implementation.
|
||||
// derived in this case is xml_iarchive_impl or base ..
|
||||
using base::load_override;
|
||||
|
||||
void load_override(const char *str) {
|
||||
StringReader sr(basic_text_iprimitive::is);
|
||||
@@ -583,6 +569,13 @@ public:
|
||||
|
||||
void save_override(const char *str) { basic_text_oprimitive::save(str); }
|
||||
|
||||
template <class T>
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
*this << t.name() << ": ";
|
||||
*this << t.const_value();
|
||||
*this << "\n";
|
||||
}
|
||||
|
||||
~hrt_oarchive() {}
|
||||
};
|
||||
|
||||
@@ -611,7 +604,7 @@ public:
|
||||
// basic_text_oprimitive::save(str);
|
||||
}
|
||||
|
||||
template <class T> void save_override(T &t) {
|
||||
template <class T> void save_override(const T &t) {
|
||||
base::save_override(boost::serialization::make_nvp(NULL, t));
|
||||
}
|
||||
|
||||
@@ -627,6 +620,10 @@ public:
|
||||
base::save_override(t);
|
||||
}
|
||||
|
||||
template <class T> void save_override(const boost::serialization::hrp<T> &t) {
|
||||
base::save_override(boost::serialization::make_nvp(t.name(), t.const_value()));
|
||||
}
|
||||
|
||||
// specific overrides for attributes - not name value pairs so we
|
||||
// want to trap them before the above "fall through"
|
||||
// since we don't want to see these in the output - make them no-ops.
|
||||
|
||||
@@ -10,6 +10,8 @@ set(HEADERS
|
||||
Macros.h
|
||||
Mpl.h
|
||||
Object.h
|
||||
ObjectFactory.h
|
||||
ObjectsContext.h
|
||||
Options.h
|
||||
Serializable.h
|
||||
Signal.h
|
||||
@@ -26,6 +28,8 @@ set(SOURCES
|
||||
Archives.cpp
|
||||
Debug.cpp
|
||||
Object.cpp
|
||||
ObjectFactory.cpp
|
||||
ObjectsContext.cpp
|
||||
Options.cpp
|
||||
Serializable.cpp
|
||||
Signal.cpp
|
||||
@@ -49,7 +53,7 @@ endif()
|
||||
target_link_libraries(${libname} ${LIBRARIES})
|
||||
|
||||
install(TARGETS ${libname}
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
EXPORT "uLibTargets"
|
||||
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
|
||||
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
#include "SmartPointer.h"
|
||||
|
||||
#include <boost/any.hpp>
|
||||
#include <TObject.h>
|
||||
|
||||
|
||||
namespace uLib {
|
||||
|
||||
@@ -119,8 +119,8 @@ public:
|
||||
void AddAdapter(AdapterInterface &ad) { m_a.push_back(Adapter(ad)); }
|
||||
|
||||
void Update() {
|
||||
foreach(Adapter &ad, m_a) {
|
||||
foreach(DItem &item, m_v) {
|
||||
for(Adapter &ad : m_a) {
|
||||
for(DItem &item : m_v) {
|
||||
item.m_adapter->operator()(ad, item.m_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
#include "boost/archive/polymorphic_xml_iarchive.hpp"
|
||||
#include "boost/archive/polymorphic_xml_oarchive.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include "Property.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
const char *Version::PackageName = PACKAGE_NAME;
|
||||
@@ -57,10 +60,54 @@ public:
|
||||
std::string slostr;
|
||||
};
|
||||
|
||||
std::string m_InstanceName;
|
||||
Vector<Signal> sigv;
|
||||
Vector<Slot> slov;
|
||||
std::vector<PropertyBase*> m_Properties;
|
||||
std::vector<PropertyBase*> m_DynamicProperties;
|
||||
};
|
||||
|
||||
// Implementations of Property methods
|
||||
void Object::RegisterProperty(PropertyBase* prop) {
|
||||
if (prop) d->m_Properties.push_back(prop);
|
||||
}
|
||||
|
||||
void Object::RegisterDynamicProperty(PropertyBase* prop) {
|
||||
if (prop) {
|
||||
d->m_DynamicProperties.push_back(prop);
|
||||
d->m_Properties.push_back(prop);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<PropertyBase*>& Object::GetProperties() const {
|
||||
return d->m_Properties;
|
||||
}
|
||||
|
||||
// In Object.h, the template serialize needs to be updated to call property serialization.
|
||||
// However, since Object::serialize is a template in the header, we might need a helper here.
|
||||
|
||||
template <class ArchiveT>
|
||||
void Object::serialize(ArchiveT &ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("InstanceName", d->m_InstanceName);
|
||||
for (auto* prop : d->m_Properties) {
|
||||
prop->serialize(ar, version);
|
||||
}
|
||||
}
|
||||
|
||||
void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); }
|
||||
|
||||
template <class ArchiveT>
|
||||
void Object::save_override(ArchiveT &ar, const unsigned int version) {}
|
||||
|
||||
// Explicitly instantiate for all uLib archives
|
||||
template void Object::serialize(Archive::xml_oarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::xml_iarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::text_oarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::text_iarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::hrt_oarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::hrt_iarchive &, const unsigned int);
|
||||
template void Object::serialize(Archive::log_archive &, const unsigned int);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -70,7 +117,12 @@ Object::Object() : d(new ObjectPrivate) {}
|
||||
|
||||
Object::Object(const Object ©) : d(new ObjectPrivate(*copy.d)) {}
|
||||
|
||||
Object::~Object() { delete d; }
|
||||
Object::~Object() {
|
||||
for (auto* p : d->m_DynamicProperties) {
|
||||
delete p;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Object::DeepCopy(const Object ©) {
|
||||
// should lock to be tread safe //
|
||||
@@ -145,6 +197,15 @@ GenericMFPtr *Object::findSlotImpl(const char *name) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
// {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define U_CORE_OBJECT_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
// WARNING: COPILE ERROR if this goes after mpl/vector //
|
||||
// #include "Core/Vector.h"
|
||||
@@ -50,6 +51,8 @@ class polymorphic_oarchive;
|
||||
|
||||
namespace uLib {
|
||||
|
||||
class PropertyBase;
|
||||
|
||||
class Version {
|
||||
public:
|
||||
static const char *PackageName;
|
||||
@@ -74,6 +77,17 @@ public:
|
||||
Object(const Object ©);
|
||||
~Object();
|
||||
|
||||
virtual const char * GetClassName() const { return "Object"; }
|
||||
|
||||
const std::string& GetInstanceName() const;
|
||||
void SetInstanceName(const std::string& name);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PROPERTIES //
|
||||
void RegisterProperty(PropertyBase* prop);
|
||||
void RegisterDynamicProperty(PropertyBase* prop);
|
||||
const std::vector<PropertyBase*>& GetProperties() const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PARAMETERS //
|
||||
|
||||
@@ -84,9 +98,9 @@ public:
|
||||
// SERIALIZATION //
|
||||
|
||||
template <class ArchiveT>
|
||||
void serialize(ArchiveT &ar, const unsigned int version) {}
|
||||
void serialize(ArchiveT &ar, const unsigned int version);
|
||||
template <class ArchiveT>
|
||||
void save_override(ArchiveT &ar, const unsigned int version) {}
|
||||
void save_override(ArchiveT &ar, const unsigned int version);
|
||||
|
||||
void SaveConfig(std::ostream &os, int version = 0);
|
||||
void LoadConfig(std::istream &is, int version = 0);
|
||||
@@ -97,6 +111,9 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// SIGNALS //
|
||||
|
||||
signals:
|
||||
virtual void Updated();
|
||||
|
||||
// Qt4 style connector //
|
||||
static bool connect(const Object *ob1, const char *signal_name,
|
||||
const Object *receiver, const char *slot_name) {
|
||||
@@ -121,6 +138,25 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Lambda/Function object connector //
|
||||
template <typename Func1, typename SlotT>
|
||||
static bool connect(typename FunctionPointer<Func1>::Object *sender,
|
||||
Func1 sigf, SlotT slof) {
|
||||
SignalBase *sigb = sender->findOrAddSignal(sigf);
|
||||
typedef typename FunctionPointer<Func1>::SignalSignature SigSignature;
|
||||
typedef typename Signal<SigSignature>::type SigT;
|
||||
reinterpret_cast<SigT *>(sigb)->connect(slof);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Func1, typename Func2>
|
||||
static bool
|
||||
disconnect(typename FunctionPointer<Func1>::Object *sender, Func1 sigf,
|
||||
typename FunctionPointer<Func2>::Object *receiver, Func2 slof) {
|
||||
// TODO: implement actual disconnect in Signal.h //
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename FuncT>
|
||||
static inline bool connect(SignalBase *sigb, FuncT slof, Object *receiver) {
|
||||
ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(sigb, slof,
|
||||
|
||||
32
src/Core/ObjectFactory.cpp
Normal file
32
src/Core/ObjectFactory.cpp
Normal file
@@ -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<std::string> ObjectFactory::GetRegisteredClasses() const {
|
||||
std::vector<std::string> classes;
|
||||
for (auto const& [name, func] : m_factoryMap) {
|
||||
classes.push_back(name);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
} // namespace uLib
|
||||
68
src/Core/ObjectFactory.h
Normal file
68
src/Core/ObjectFactory.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef U_CORE_OBJECTFACTORY_H
|
||||
#define U_CORE_OBJECTFACTORY_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "Core/Object.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Singleton factory for dynamic Object instantiation based on class name.
|
||||
*/
|
||||
class ObjectFactory {
|
||||
public:
|
||||
typedef std::function<Object*()> 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<std::string> GetRegisteredClasses() const;
|
||||
|
||||
private:
|
||||
ObjectFactory() = default;
|
||||
~ObjectFactory() = default;
|
||||
|
||||
// Prevent copy and assignment
|
||||
ObjectFactory(const ObjectFactory&) = delete;
|
||||
ObjectFactory& operator=(const ObjectFactory&) = delete;
|
||||
|
||||
std::map<std::string, FactoryFunction> m_factoryMap;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper class to statically register a factory function.
|
||||
*/
|
||||
template <typename T>
|
||||
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<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);
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_OBJECTFACTORY_H
|
||||
61
src/Core/ObjectsContext.cpp
Normal file
61
src/Core/ObjectsContext.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Core/ObjectsContext.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
ObjectsContext::ObjectsContext() : Object() {}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Object*>& ObjectsContext::GetObjects() const {
|
||||
return m_objects;
|
||||
}
|
||||
|
||||
size_t ObjectsContext::GetCount() const {
|
||||
return m_objects.size();
|
||||
}
|
||||
|
||||
Object* ObjectsContext::GetObject(size_t index) const {
|
||||
if (index < m_objects.size()) {
|
||||
return m_objects[index];
|
||||
}
|
||||
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
|
||||
67
src/Core/ObjectsContext.h
Normal file
67
src/Core/ObjectsContext.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef U_CORE_OBJECTS_CONTEXT_H
|
||||
#define U_CORE_OBJECTS_CONTEXT_H
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include <vector>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief ObjectsContext represents a collection of Object instances.
|
||||
*/
|
||||
class ObjectsContext : public Object {
|
||||
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.
|
||||
*/
|
||||
void AddObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Removes an object from the context.
|
||||
* @param obj Pointer to the object to remove.
|
||||
*/
|
||||
void RemoveObject(Object* obj);
|
||||
|
||||
/**
|
||||
* @brief Clears all objects from the context.
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
/**
|
||||
* @brief Returns the collection of objects.
|
||||
* @return Const reference to the vector of object pointers.
|
||||
*/
|
||||
const std::vector<Object*>& 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.
|
||||
* @return Size of the collection.
|
||||
*/
|
||||
size_t GetCount() const;
|
||||
|
||||
/**
|
||||
* @brief Returns an object by index.
|
||||
* @param index The index of the object.
|
||||
* @return Pointer to the object or nullptr if index is out of bounds.
|
||||
*/
|
||||
Object* GetObject(size_t index) const;
|
||||
|
||||
private:
|
||||
std::vector<Object*> m_objects;
|
||||
};
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_OBJECTS_CONTEXT_H
|
||||
221
src/Core/Property.h
Normal file
221
src/Core/Property.h
Normal file
@@ -0,0 +1,221 @@
|
||||
#ifndef U_CORE_PROPERTY_H
|
||||
#define U_CORE_PROPERTY_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Core/Archives.h"
|
||||
#include "Core/Signal.h"
|
||||
#include "Core/Object.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Base class for properties to allow runtime listing and identification.
|
||||
*/
|
||||
class PropertyBase : public Object {
|
||||
public:
|
||||
virtual ~PropertyBase() {}
|
||||
virtual const std::string& GetName() const = 0;
|
||||
virtual const char* GetTypeName() const = 0;
|
||||
virtual std::string GetValueAsString() const = 0;
|
||||
|
||||
// Signal support
|
||||
signals:
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(PropertyBase::Updated); }
|
||||
|
||||
// Serialization support for different uLib archives
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) = 0;
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int version) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Template class for typed properties.
|
||||
*/
|
||||
template <typename T>
|
||||
class Property : public PropertyBase {
|
||||
public:
|
||||
// PROXY: Use an existing variable as back-end storage
|
||||
Property(Object* owner, const std::string& name, T* valuePtr)
|
||||
: m_owner(owner), m_name(name), m_value(valuePtr), m_own(false) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
// MANAGED: Create and own internal storage
|
||||
Property(Object* owner, const std::string& name, const T& defaultValue = T())
|
||||
: m_owner(owner), m_name(name), m_value(new T(defaultValue)), m_own(true) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~Property() {
|
||||
if (m_own) delete m_value;
|
||||
}
|
||||
|
||||
const std::string& GetName() const override { return m_name; }
|
||||
const char* GetTypeName() const override { return typeid(T).name(); }
|
||||
|
||||
std::string GetValueAsString() const override {
|
||||
try {
|
||||
return boost::lexical_cast<std::string>(*m_value);
|
||||
} catch (const boost::bad_lexical_cast&) {
|
||||
std::stringstream ss;
|
||||
ss << *m_value;
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
|
||||
// Accessors
|
||||
const T& Get() const { return *m_value; }
|
||||
void Set(const T& value) {
|
||||
if (*m_value != value) {
|
||||
*m_value = value;
|
||||
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
|
||||
}
|
||||
}
|
||||
|
||||
// Operators for seamless usage
|
||||
operator const T&() const { return *m_value; }
|
||||
Property& operator=(const T& value) {
|
||||
Set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Signals
|
||||
signals:
|
||||
virtual void PropertyChanged() { ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged); }
|
||||
|
||||
// Serialization
|
||||
template <class ArchiveT>
|
||||
void serialize_impl(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp(m_name.c_str(), *m_value);
|
||||
}
|
||||
|
||||
void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_impl(ar, v); }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
T* m_value;
|
||||
bool m_own;
|
||||
Object* m_owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Conveninent typedefs for common property types.
|
||||
*/
|
||||
typedef Property<std::string> StringProperty;
|
||||
typedef Property<int> IntProperty;
|
||||
typedef Property<unsigned int> UIntProperty;
|
||||
typedef Property<long> LongProperty;
|
||||
typedef Property<unsigned long> ULongProperty;
|
||||
typedef Property<float> FloatProperty;
|
||||
typedef Property<double> DoubleProperty;
|
||||
typedef Property<Bool_t> BoolProperty;
|
||||
|
||||
/**
|
||||
* @brief Macro to simplify property declaration within a class.
|
||||
* Usage: ULIB_PROPERTY(float, Width, 1.0f)
|
||||
* It creates a raw member m_Width and a Property proxy Width.
|
||||
*/
|
||||
#define ULIB_PROPERTY(type, name, defaultValue) \
|
||||
type m_##name = defaultValue; \
|
||||
Property<type> name = Property<type>(this, #name, &m_##name);
|
||||
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
namespace Archive {
|
||||
class property_register_archive;
|
||||
} // namespace Archive
|
||||
} // namespace uLib
|
||||
|
||||
namespace boost {
|
||||
namespace archive {
|
||||
namespace detail {
|
||||
template <>
|
||||
class interface_oarchive<uLib::Archive::property_register_archive>
|
||||
: public uLib_interface_oarchive<uLib::Archive::property_register_archive> {};
|
||||
} // namespace detail
|
||||
} // namespace archive
|
||||
} // namespace boost
|
||||
|
||||
namespace uLib {
|
||||
namespace Archive {
|
||||
|
||||
/**
|
||||
* @brief A special archive that creates and registers Property proxies
|
||||
* for any member it encounters wrapped in HRP().
|
||||
*/
|
||||
class property_register_archive :
|
||||
public boost::archive::detail::common_oarchive<property_register_archive>
|
||||
{
|
||||
Object* m_Object;
|
||||
public:
|
||||
friend class boost::archive::detail::interface_oarchive<property_register_archive>;
|
||||
friend class boost::archive::save_access;
|
||||
|
||||
typedef boost::archive::detail::common_oarchive<property_register_archive> detail_common_oarchive;
|
||||
|
||||
property_register_archive(Object* obj) :
|
||||
boost::archive::detail::common_oarchive<property_register_archive>(boost::archive::no_header),
|
||||
m_Object(obj) {}
|
||||
|
||||
// Core logic: encounter HRP -> Create Dynamic Property
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
if (m_Object) {
|
||||
// We use const_cast because we are just creating a proxy to the member
|
||||
m_Object->RegisterDynamicProperty(
|
||||
new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle standard NVPs by recursing (important for base classes)
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::nvp<T> &t) {
|
||||
this->detail_common_oarchive::save_override(t.const_value());
|
||||
}
|
||||
|
||||
// Ignore everything else
|
||||
template<class T> void save_override(const T &t) {}
|
||||
|
||||
// Required attribute overrides for common_oarchive
|
||||
void save_override(const boost::archive::object_id_type & t) {}
|
||||
void save_override(const boost::archive::object_reference_type & t) {}
|
||||
void save_override(const boost::archive::version_type & t) {}
|
||||
void save_override(const boost::archive::class_id_type & t) {}
|
||||
void save_override(const boost::archive::class_id_optional_type & t) {}
|
||||
void save_override(const boost::archive::class_id_reference_type & t) {}
|
||||
void save_override(const boost::archive::class_name_type & t) {}
|
||||
void save_override(const boost::archive::tracking_type & t) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Convenience macro to automatically activate and register all HRP members
|
||||
* as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj)
|
||||
*/
|
||||
#define ULIB_ACTIVATE_PROPERTIES(obj) \
|
||||
{ uLib::Archive::property_register_archive _ar_tmp(&(obj)); (obj).serialize(_ar_tmp, 0); }
|
||||
|
||||
} // namespace Archive
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_PROPERTY_H
|
||||
@@ -72,39 +72,33 @@ namespace serialization {
|
||||
template <class T> struct access2 {};
|
||||
|
||||
// NON FUNZIONA ... SISTEMARE !!!! // ------------------------------------------
|
||||
template <class T> class hrp : public wrapper_traits<const hrp<T>> {
|
||||
template <class T>
|
||||
class hrp : public boost::serialization::wrapper_traits<hrp<T>> {
|
||||
const char *m_name;
|
||||
T *m_value;
|
||||
std::string *m_str;
|
||||
T &m_value;
|
||||
|
||||
public:
|
||||
explicit hrp(const char *name_, T &t)
|
||||
: m_str(new std::string), m_name(name_), m_value(&t) {}
|
||||
explicit hrp(const char *name_, T &t) : m_name(name_), m_value(t) {}
|
||||
|
||||
const char *name() const { return this->m_name; }
|
||||
T &value() { return this->m_value; }
|
||||
const T &const_value() const { return this->m_value; }
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
|
||||
template <class Archivex>
|
||||
void save(Archivex &ar, const unsigned int /* file_version */) const {
|
||||
//// ar.operator<<(const_value());
|
||||
// std::stringstream ss;
|
||||
// uLib::Archive::hrt_oarchive har(ss);
|
||||
// har << make_nvp(m_name,*m_value);
|
||||
// // (*m_str) = ss.str();
|
||||
//// ar.operator << (make_nvp(m_name, ss.str());
|
||||
void save(Archivex &ar, const unsigned int /* version */) const {
|
||||
ar << boost::serialization::make_nvp(m_name, m_value);
|
||||
}
|
||||
|
||||
template <class Archivex>
|
||||
void load(Archivex &ar, const unsigned int /* file_version */) {
|
||||
// ar.operator>>(value());
|
||||
void load(Archivex &ar, const unsigned int /* version */) {
|
||||
ar >> boost::serialization::make_nvp(m_name, m_value);
|
||||
}
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER()
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
#ifndef BOOST_NO_FUNCTION_TEMPLATE_ORDERING
|
||||
const
|
||||
#endif
|
||||
hrp<T> make_hrp(const char *name, T &t) {
|
||||
inline hrp<T> make_hrp(const char *name, T &t) {
|
||||
return hrp<T>(name, t);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { \
|
||||
|
||||
@@ -139,6 +139,7 @@ typedef id_t Id_t;
|
||||
typedef void *Pointer_t;
|
||||
typedef bool Bool_t; // Boolean (0=false, 1=true) (bool)
|
||||
|
||||
|
||||
//--- bit manipulation ---------------------------------------------------------
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1ULL << (n))
|
||||
|
||||
@@ -21,6 +21,8 @@ set( TESTS
|
||||
OptionsTest
|
||||
PingPongTest
|
||||
VectorMetaAllocatorTest
|
||||
PropertyTypesTest
|
||||
HRPTest
|
||||
)
|
||||
|
||||
set(LIBRARIES
|
||||
|
||||
83
src/Core/testing/HRPTest.cpp
Normal file
83
src/Core/testing/HRPTest.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Core/Serializable.h"
|
||||
#include "Core/Archives.h"
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
struct SimpleObject {
|
||||
int value;
|
||||
std::string name;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version) {
|
||||
ar & HRP(value);
|
||||
ar & HRP(name);
|
||||
}
|
||||
};
|
||||
|
||||
struct DynamicObject : public Object {
|
||||
float width;
|
||||
int height;
|
||||
|
||||
DynamicObject() : width(10.0f), height(20) {
|
||||
// Automatic registration of properties based on serialize/HRP
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
}
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int version) {
|
||||
ar & HRP(width);
|
||||
ar & HRP(height);
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
SimpleObject obj;
|
||||
obj.value = 42;
|
||||
obj.name = "TestObject";
|
||||
|
||||
std::cout << "Testing HRP Serialization to Log..." << std::endl;
|
||||
std::stringstream ss;
|
||||
{
|
||||
uLib::Archive::log_archive ar(ss);
|
||||
ar << boost::serialization::make_nvp("Object", obj);
|
||||
}
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
std::cout << "Testing HRP Serialization to HRT..." << std::endl;
|
||||
ss.str("");
|
||||
{
|
||||
uLib::Archive::hrt_oarchive ar(ss);
|
||||
ar << obj;
|
||||
}
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
std::cout << "Testing HRP Serialization to XML..." << std::endl;
|
||||
ss.str("");
|
||||
{
|
||||
uLib::Archive::xml_oarchive ar(ss);
|
||||
ar << boost::serialization::make_nvp("Object", obj);
|
||||
}
|
||||
std::cout << ss.str() << std::endl;
|
||||
|
||||
std::cout << "Testing Dynamic Property Creation via ULIB_ACTIVATE_PROPERTIES macro..." << std::endl;
|
||||
DynamicObject dynObj;
|
||||
// (properties were already created in DynamicObject constructor via macro)
|
||||
|
||||
std::cout << "Registered Properties in dynObj:" << std::endl;
|
||||
const auto& props = dynObj.GetProperties();
|
||||
for (auto* p : props) {
|
||||
std::cout << " - [" << p->GetTypeName() << "] " << p->GetName() << " = " << p->GetValueAsString() << std::endl;
|
||||
}
|
||||
|
||||
if (props.size() == 2) {
|
||||
std::cout << "Dynamic Property Creation SUCCESS!" << std::endl;
|
||||
} else {
|
||||
std::cout << "Dynamic Property Creation FAILED (Expected 2, got " << props.size() << ")" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
src/Core/testing/PropertySystemTest.cpp
Normal file
64
src/Core/testing/PropertySystemTest.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Property.h"
|
||||
#include <cassert>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
class TestObject : public Object {
|
||||
public:
|
||||
TestObject() : Object(),
|
||||
IntProp(this, "IntProp", 10),
|
||||
StringProp(this, "StringProp", "Initial")
|
||||
{}
|
||||
|
||||
virtual const char* GetClassName() const override { return "TestObject"; }
|
||||
|
||||
Property<int> IntProp;
|
||||
Property<std::string> StringProp;
|
||||
};
|
||||
|
||||
int main() {
|
||||
TestObject obj;
|
||||
|
||||
std::cout << "Testing Properties..." << std::endl;
|
||||
|
||||
// 1. Check registration
|
||||
const auto& props = obj.GetProperties();
|
||||
assert(props.size() == 2);
|
||||
assert(props[0]->GetName() == "IntProp");
|
||||
assert(props[1]->GetName() == "StringProp");
|
||||
|
||||
// 2. Check value access and signals
|
||||
bool signalCalled = false;
|
||||
uLib::Object::connect(&obj.IntProp, &Property<int>::PropertyChanged, [&signalCalled]() {
|
||||
signalCalled = true;
|
||||
});
|
||||
|
||||
assert(obj.IntProp.Get() == 10);
|
||||
obj.IntProp = 20;
|
||||
assert(obj.IntProp.Get() == 20);
|
||||
assert(signalCalled == true);
|
||||
|
||||
// 3. Check serialization
|
||||
std::stringstream ss;
|
||||
Object::SaveXml(ss, obj);
|
||||
std::string xml = ss.str();
|
||||
std::cout << "Serialized XML: \n" << xml << std::endl;
|
||||
|
||||
assert(xml.find("<IntProp>20</IntProp>") != std::string::npos);
|
||||
assert(xml.find("<StringProp>Initial</StringProp>") != std::string::npos);
|
||||
|
||||
// 4. Check deserialization
|
||||
TestObject obj2;
|
||||
std::stringstream ss2(xml);
|
||||
Object::LoadXml(ss2, obj2);
|
||||
|
||||
assert(obj2.IntProp.Get() == 20);
|
||||
assert(obj2.StringProp.Get() == "Initial");
|
||||
|
||||
std::cout << "All Property Tests PASSED!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
68
src/Core/testing/PropertyTypesTest.cpp
Normal file
68
src/Core/testing/PropertyTypesTest.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Math/Dense.h"
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
class TestObject : public Object {
|
||||
public:
|
||||
TestObject() : Object() {}
|
||||
|
||||
virtual const char* GetClassName() const override { return "TestObject"; }
|
||||
|
||||
// Use new typedefs
|
||||
StringProperty StringProp = StringProperty(this, "StringProp", "Initial");
|
||||
IntProperty IntProp = IntProperty(this, "IntProp", 42);
|
||||
FloatProperty FloatProp = FloatProperty(this, "FloatProp", 3.14f);
|
||||
BoolProperty BoolProp = BoolProperty(this, "BoolProp", true);
|
||||
|
||||
// Use new macro
|
||||
ULIB_PROPERTY(Matrix3f, MatrixProp, Matrix3f::Identity())
|
||||
|
||||
// Use new Dense typedefs
|
||||
Vector3fProperty Vector3fProp = Vector3fProperty(this, "Vector3fProp", Vector3f(1.1f, 2.2f, 3.3f));
|
||||
Matrix4fProperty Matrix4fProp = Matrix4fProperty(this, "Matrix4fProp", Matrix4f::Identity());
|
||||
};
|
||||
|
||||
int main() {
|
||||
TestObject obj;
|
||||
|
||||
std::cout << "Testing Property Types..." << std::endl;
|
||||
|
||||
// 1. Verify string representation
|
||||
std::cout << "StringProp: " << obj.StringProp.GetValueAsString() << std::endl;
|
||||
assert(obj.StringProp.GetValueAsString() == "Initial");
|
||||
|
||||
std::cout << "IntProp: " << obj.IntProp.GetValueAsString() << std::endl;
|
||||
assert(obj.IntProp.GetValueAsString() == "42");
|
||||
|
||||
std::cout << "FloatProp: " << obj.FloatProp.GetValueAsString() << std::endl;
|
||||
// boost::lexical_cast might have different precision, but for 3.14 it should be okay or we check find
|
||||
assert(obj.FloatProp.GetValueAsString().find("3.14") != std::string::npos);
|
||||
|
||||
std::cout << "BoolProp: " << obj.BoolProp.GetValueAsString() << std::endl;
|
||||
// Bool might be "1" or "true" depending on lexical_cast/stringstream
|
||||
assert(obj.BoolProp.GetValueAsString() == "1" || obj.BoolProp.GetValueAsString() == "true");
|
||||
|
||||
// 2. Verify Matrix/Vector string representation (uses operator<<)
|
||||
std::cout << "MatrixProp: \n" << obj.MatrixProp.GetValueAsString() << std::endl;
|
||||
assert(obj.MatrixProp.GetValueAsString().find("1 0 0") != std::string::npos);
|
||||
|
||||
std::cout << "Vector3fProp: " << obj.Vector3fProp.GetValueAsString() << std::endl;
|
||||
assert(obj.Vector3fProp.GetValueAsString().find("1.1 2.2 3.3") != std::string::npos);
|
||||
|
||||
std::cout << "Matrix4fProp: \n" << obj.Matrix4fProp.GetValueAsString() << std::endl;
|
||||
assert(obj.Matrix4fProp.GetValueAsString().find("1 0 0 0") != std::string::npos);
|
||||
|
||||
// 3. Verify updates and signals
|
||||
obj.IntProp = 100;
|
||||
assert(obj.IntProp.Get() == 100);
|
||||
assert(obj.IntProp.GetValueAsString() == "100");
|
||||
|
||||
std::cout << "All Property Type Tests PASSED!" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
set(HEADERS MuonScatter.h MuonError.h MuonEvent.h)
|
||||
|
||||
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Detectors PARENT_SCOPE)
|
||||
|
||||
|
||||
install(FILES ${HEADERS}
|
||||
DESTINATION ${INSTALL_INC_DIR}/Detectors)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
include(uLibTargetMacros)
|
||||
add_subdirectory(testing)
|
||||
endif()
|
||||
@@ -1,114 +0,0 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
// G4 Solid //
|
||||
#include <Geant4/G4Material.hh>
|
||||
#include <Geant4/G4NistManager.hh>
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
|
||||
// Tessellated solid //
|
||||
#include <Geant4/G4TessellatedSolid.hh>
|
||||
#include <Geant4/G4TriangularFacet.hh>
|
||||
#include <Geant4/G4ThreeVector.hh>
|
||||
|
||||
|
||||
#include "Math/Dense.h"
|
||||
|
||||
#include "Solid.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
class DetectorsSolidPimpl {
|
||||
public:
|
||||
static G4ThreeVector getG4Vector3f(const Vector3f &vector) {
|
||||
return G4ThreeVector( vector(0), vector(1), vector(2) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Solid::Solid() :
|
||||
m_Logical (new G4LogicalVolume(NULL,NULL,"unnamed_solid")),
|
||||
m_Material(NULL)
|
||||
{}
|
||||
|
||||
Solid::Solid(const char *name) :
|
||||
m_Logical(new G4LogicalVolume(NULL,NULL,name)),
|
||||
m_Material(NULL)
|
||||
{}
|
||||
|
||||
|
||||
|
||||
void Solid::SetNistMaterial(const char *name)
|
||||
{
|
||||
G4NistManager *nist = G4NistManager::Instance();
|
||||
if (m_Material) delete m_Material;
|
||||
m_Material = nist->FindOrBuildMaterial(name);
|
||||
m_Logical->SetMaterial(m_Material);
|
||||
}
|
||||
|
||||
void Solid::SetMaterial(G4Material *material)
|
||||
{
|
||||
if(material)
|
||||
{
|
||||
m_Material = material;
|
||||
m_Logical->SetMaterial(material);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TessellatedSolid::TessellatedSolid(const char *name) :
|
||||
BaseClass(name),
|
||||
m_Solid(new G4TessellatedSolid(name))
|
||||
{}
|
||||
|
||||
|
||||
void TessellatedSolid::SetMesh(TriangleMesh &mesh)
|
||||
{
|
||||
G4TessellatedSolid *ts = this->m_Solid;
|
||||
for (int i=0; i<mesh.Triangles().size(); ++i) {
|
||||
const Vector3i &trg = mesh.Triangles().at(i);
|
||||
G4TriangularFacet *facet = new G4TriangularFacet(
|
||||
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(0))),
|
||||
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(1))),
|
||||
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(2))),
|
||||
ABSOLUTE);
|
||||
ts->AddFacet((G4VFacet *)facet);
|
||||
}
|
||||
this->m_Logical->SetSolid(ts);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
include $(top_srcdir)/Common.am
|
||||
|
||||
#AM_DEFAULT_SOURCE_EXT = .cpp
|
||||
|
||||
# if HAVE_CHECK
|
||||
TESTS = GDMLSolidTest
|
||||
|
||||
# else
|
||||
# TEST =
|
||||
# endif
|
||||
|
||||
LDADD = $(top_srcdir)/libmutom-${PACKAGE_VERSION}.la
|
||||
|
||||
check_PROGRAMS = $(TESTS)
|
||||
|
||||
13
src/HEP/CMakeLists.txt
Normal file
13
src/HEP/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
################################################################################
|
||||
##### HEP - High Energy Physics modules ########################################
|
||||
################################################################################
|
||||
|
||||
include_directories(${SRC_DIR}/HEP)
|
||||
|
||||
add_subdirectory(Detectors)
|
||||
add_subdirectory(Geant)
|
||||
# add_subdirectory(MuonTomography)
|
||||
|
||||
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} PARENT_SCOPE)
|
||||
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} PARENT_SCOPE)
|
||||
46
src/HEP/Detectors/CMakeLists.txt
Normal file
46
src/HEP/Detectors/CMakeLists.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
set(HEADERS
|
||||
ChamberHitEvent.h
|
||||
DetectorChamber.h
|
||||
ExperimentFitEvent.h
|
||||
HierarchicalEncoding.h
|
||||
Hit.h
|
||||
HitMC.h
|
||||
LinearFit.h
|
||||
MuonError.h
|
||||
MuonEvent.h
|
||||
MuonScatter.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
DetectorChamber.cpp
|
||||
)
|
||||
|
||||
set(LIBRARIES
|
||||
${PACKAGE_LIBPREFIX}Core
|
||||
${PACKAGE_LIBPREFIX}Math
|
||||
)
|
||||
|
||||
set(libname ${PACKAGE_LIBPREFIX}Detectors)
|
||||
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} ${libname} PARENT_SCOPE)
|
||||
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Detectors PARENT_SCOPE)
|
||||
|
||||
## SHARED library
|
||||
add_library(${libname} SHARED ${SOURCES})
|
||||
set_target_properties(${libname} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_SOVERSION}
|
||||
CXX_STANDARD 17)
|
||||
target_link_libraries(${libname} PRIVATE ${LIBRARIES})
|
||||
|
||||
install(TARGETS ${libname}
|
||||
EXPORT "uLibTargets"
|
||||
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
|
||||
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
|
||||
|
||||
install(FILES ${HEADERS}
|
||||
DESTINATION ${INSTALL_INC_DIR}/HEP/Detectors)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
include(uLibTargetMacros)
|
||||
add_subdirectory(testing)
|
||||
endif()
|
||||
@@ -29,8 +29,8 @@
|
||||
#define U_CHAMBERHITEVENT_H
|
||||
|
||||
#include "Core/Vector.h"
|
||||
#include "Hit.h"
|
||||
#include "ChamberDetector.h"
|
||||
#include "Detectors/HitMC.h"
|
||||
#include "Detectors/DetectorChamber.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
@@ -38,17 +38,17 @@ class ChamberHitEventData
|
||||
{
|
||||
public:
|
||||
uLibConstRefMacro (Hits, Vector<HitData> )
|
||||
uLibGetMacro (Idv, ChamberDetector::ID)
|
||||
uLibGetMacro (Idv, uint)
|
||||
private:
|
||||
friend class ChamberHitEvent;
|
||||
Vector<HitData> m_Hits;
|
||||
DetectorChamber::ID m_Idv; // -> chamber/view
|
||||
uint m_Idv; // -> chamber/view
|
||||
};
|
||||
|
||||
class ChamberHitEvent : public ChamberHitEventData {
|
||||
public:
|
||||
uLibRefMacro (Hits, Vector<HitData> )
|
||||
uLibSetMacro (Idv, ChamberDetector::ID)
|
||||
uLibSetMacro (Idv, uint)
|
||||
};
|
||||
|
||||
}
|
||||
49
src/HEP/Detectors/DetectorChamber.cpp
Normal file
49
src/HEP/Detectors/DetectorChamber.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
#include "Core/ObjectFactory.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
MuonEvent DetectorChamber::ProjectMuonEvent(const MuonEvent &muon) const {
|
||||
MuonEvent projectedMuon = muon;
|
||||
|
||||
// Transform the local projection plane to world coordinates
|
||||
HLine3f worldPlane = this->GetWorldProjectionPlane();
|
||||
HPoint3f P = worldPlane.origin;
|
||||
HVector3f N = worldPlane.direction;
|
||||
|
||||
HPoint3f X_in = muon.LineIn().origin;
|
||||
HPoint3f X_out = muon.LineOut().origin;
|
||||
|
||||
// Let's use distance to the plane for the first part and keep the logic consistent.
|
||||
float dist_in = std::abs((X_in - P).dot(N));
|
||||
float dist_out = std::abs((X_out - P).dot(N));
|
||||
|
||||
const HLine3f &chosenLine = (dist_in <= dist_out) ? muon.LineIn() : muon.LineOut();
|
||||
HPoint3f X_chosen = chosenLine.origin;
|
||||
|
||||
// Project X_chosen into the plane defined by P and normal N
|
||||
// X_proj = X_chosen - ((X_chosen - P) . N / (N . N)) * N
|
||||
float dot = (X_chosen - P).dot(N);
|
||||
float n_sq = N.dot(N);
|
||||
|
||||
HPoint3f X_proj = X_chosen;
|
||||
if (n_sq > 0) {
|
||||
X_proj = X_chosen - (dot / n_sq) * N;
|
||||
}
|
||||
|
||||
// Define the projected line with projected origin and original direction
|
||||
HLine3f projectedLine;
|
||||
projectedLine.origin = X_proj;
|
||||
projectedLine.direction = chosenLine.direction;
|
||||
|
||||
// Set both input and output lines of the projected muon to the same projected line
|
||||
projectedMuon.LineIn() = projectedLine;
|
||||
projectedMuon.LineOut() = projectedLine;
|
||||
|
||||
return projectedMuon;
|
||||
}
|
||||
|
||||
ULIB_REGISTER_OBJECT(DetectorChamber)
|
||||
|
||||
} // namespace uLib
|
||||
86
src/HEP/Detectors/DetectorChamber.h
Normal file
86
src/HEP/Detectors/DetectorChamber.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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_CHAMBERDETECTOR_H
|
||||
#define U_CHAMBERDETECTOR_H
|
||||
|
||||
#include "Core/Types.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
|
||||
#include "HEP/Detectors/Hit.h"
|
||||
#include "HEP/Detectors/HitMC.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
|
||||
class DetectorChamber : public ContainerBox {
|
||||
|
||||
typedef ContainerBox BaseClass;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
virtual const char * GetClassName() const { return "DetectorChamber"; }
|
||||
|
||||
DetectorChamber() : BaseClass() {
|
||||
m_ProjectionPlane.origin = HPoint3f(0, 0, 0);
|
||||
m_ProjectionPlane.direction = HVector3f(0, 0, 1);
|
||||
}
|
||||
|
||||
DetectorChamber(const Vector3f &size) : BaseClass(size) {
|
||||
m_ProjectionPlane.origin = HPoint3f(0, 0, 0);
|
||||
m_ProjectionPlane.direction = HVector3f(0, 0, 1);
|
||||
}
|
||||
|
||||
// set the plane where muons hit is projected
|
||||
// coordinates are local to the container box
|
||||
void SetProjectionPlane(const HLine3f &normal) { m_ProjectionPlane = normal; }
|
||||
|
||||
const HLine3f &GetProjectionPlane() const { return m_ProjectionPlane; }
|
||||
|
||||
HLine3f GetWorldProjectionPlane() const {
|
||||
HLine3f worldPlane;
|
||||
Matrix4f M = this->GetWorldMatrix();
|
||||
worldPlane.origin = M * m_ProjectionPlane.origin;
|
||||
HVector3f dirNorm = M * m_ProjectionPlane.direction;
|
||||
dirNorm.normalize(); // Normalize for consistent dot products
|
||||
worldPlane.direction = dirNorm;
|
||||
return worldPlane;
|
||||
}
|
||||
|
||||
MuonEvent ProjectMuonEvent(const MuonEvent &muon) const;
|
||||
|
||||
private:
|
||||
HLine3f m_ProjectionPlane;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // CHAMBERDETECTOR_H
|
||||
@@ -33,9 +33,6 @@
|
||||
namespace uLib {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class HitRawCode_CMSDrift :
|
||||
public BitCode4<unsigned short,6,3,2,5>
|
||||
{
|
||||
@@ -59,8 +56,14 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class HitData {
|
||||
public:
|
||||
HitData() {}
|
||||
~HitData() {}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ protected:
|
||||
|
||||
class MuonEvent : public MuonEventData {
|
||||
public:
|
||||
using MuonEventData::LineIn;
|
||||
using MuonEventData::LineOut;
|
||||
using MuonEventData::GetMomentum;
|
||||
|
||||
inline HLine3f & LineIn() { return this->m_LineIn; }
|
||||
inline HLine3f & LineOut() { return this->m_LineOut; }
|
||||
inline Scalarf & Momentum() { return this->m_Momentum; }
|
||||
@@ -1,12 +1,13 @@
|
||||
# TESTS
|
||||
set( TESTS
|
||||
# GDMLSolidTest
|
||||
HierarchicalEncodingTest
|
||||
DetectorChamberTest
|
||||
)
|
||||
|
||||
set(LIBRARIES
|
||||
${PACKAGE_LIBPREFIX}Core
|
||||
${PACKAGE_LIBPREFIX}Math
|
||||
${PACKAGE_LIBPREFIX}Detectors
|
||||
Boost::serialization
|
||||
Boost::program_options
|
||||
Eigen3::Eigen
|
||||
139
src/HEP/Detectors/testing/DetectorChamberTest.cpp
Normal file
139
src/HEP/Detectors/testing/DetectorChamberTest.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "HEP/Detectors/DetectorChamber.h"
|
||||
#include "testing-prototype.h"
|
||||
#include <iostream>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
int main() {
|
||||
BEGIN_TESTING(DetectorChamber Projection);
|
||||
|
||||
DetectorChamber chamber;
|
||||
|
||||
// Define a horizontal plane at z = 100
|
||||
HLine3f plane;
|
||||
plane.origin = HPoint3f(0, 0, 100);
|
||||
plane.direction = HVector3f(0, 0, 1); // Normal to the plane
|
||||
chamber.SetProjectionPlane(plane);
|
||||
|
||||
// Create a muon with two segments
|
||||
MuonEvent muon;
|
||||
|
||||
// Segment 1 (muon.LineIn()): origin is at (10, 20, 50), direction along Z
|
||||
muon.LineIn().origin = HPoint3f(10, 20, 50);
|
||||
muon.LineIn().direction = HVector3f(0, 0, 1);
|
||||
|
||||
// Segment 2 (muon.LineOut()): origin is at (10, 20, 200), direction along Z
|
||||
muon.LineOut().origin = HPoint3f(10, 20, 200);
|
||||
muon.LineOut().direction = HVector3f(0, 0, 1);
|
||||
|
||||
// distance_in = |50 - 100| = 50
|
||||
// distance_out = |200 - 100| = 100
|
||||
// LineIn is closer to the plane (z=100)
|
||||
|
||||
MuonEvent projected = chamber.ProjectMuonEvent(muon);
|
||||
|
||||
// Expected:
|
||||
// chosenLine = LineIn (it is closer)
|
||||
// X_chosen = (10, 20, 50)
|
||||
// X_proj = (10, 20, 50) - (( (10, 20, 50) - (0, 0, 100) ) . (0, 0, 1)) * (0, 0, 1)
|
||||
// X_proj = (10, 20, 50) - (-50) * (0, 0, 1) = (10, 20, 100)
|
||||
|
||||
HPoint3f expectedPos(10, 20, 100);
|
||||
|
||||
std::cout << "Test Case 1: LineIn is closer" << std::endl;
|
||||
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
|
||||
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
|
||||
|
||||
// Check if the projected position is correct
|
||||
// norm() includes the 4th component, which for HVector3f (diff of points) should be 0.
|
||||
bool posOk1 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
|
||||
TEST1(posOk1);
|
||||
|
||||
// Check if LineIn and LineOut are the same
|
||||
bool linesMatch1 = (projected.LineIn().origin == projected.LineOut().origin) &&
|
||||
(projected.LineIn().direction == projected.LineOut().direction);
|
||||
TEST1(linesMatch1);
|
||||
|
||||
// Test Case 2: LineOut is closer
|
||||
muon.LineIn().origin = HPoint3f(30, 40, 0); // dist = 100
|
||||
muon.LineOut().origin = HPoint3f(30, 40, 110); // dist = 10
|
||||
|
||||
projected = chamber.ProjectMuonEvent(muon);
|
||||
expectedPos = HPoint3f(30, 40, 100); // projection of (30,40,110) onto z=100
|
||||
|
||||
std::cout << "\nTest Case 2: LineOut is closer" << std::endl;
|
||||
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
|
||||
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
|
||||
|
||||
bool posOk2 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
|
||||
TEST1(posOk2);
|
||||
|
||||
// Test Case 3: Oblique plane
|
||||
// Plane through (0,0,0) with normal (1,1,1) (normalized)
|
||||
plane.origin = HPoint3f(0, 0, 0);
|
||||
plane.direction = HVector3f(1, 1, 1).normalized();
|
||||
chamber.SetProjectionPlane(plane);
|
||||
|
||||
muon.LineIn().origin = HPoint3f(1, 1, 1); // dist = (1,1,1) . (1,1,1).norm()
|
||||
muon.LineIn().direction = HVector3f(0, 0, 1);
|
||||
muon.LineOut().origin = HPoint3f(10, 10, 10); // dist = 10 * sqrt(3)
|
||||
|
||||
projected = chamber.ProjectMuonEvent(muon);
|
||||
// X_chosen = (1,1,1)
|
||||
// X_proj = (1,1,1) - ((1,1,1) . N) * N where N = (1,1,1)/sqrt(3)
|
||||
// X_proj = (1,1,1) - (sqrt(3)) * (1,1,1)/sqrt(3) = (1,1,1) - (1,1,1) = (0,0,0)
|
||||
expectedPos = HPoint3f(0, 0, 0);
|
||||
|
||||
std::cout << "\nTest Case 3: Oblique plane" << std::endl;
|
||||
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
|
||||
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
|
||||
|
||||
bool posOk3 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
|
||||
TEST1(posOk3);
|
||||
|
||||
// Test Case 4: Transformed DetectorChamber
|
||||
DetectorChamber chamber2;
|
||||
chamber2.SetPosition(Vector3f(0, 0, 100)); // Move chamber to z=100
|
||||
// chamber2.GetProjectionPlane has default origin (0,0,0) and direction (0,0,1)
|
||||
// In world coordinates, this plane is at z = 100 + 0 = 100.
|
||||
|
||||
muon.LineIn().origin = HPoint3f(50, 60, 50); // dist to world plane (z=100) is 50
|
||||
muon.LineOut().origin = HPoint3f(50, 60, 200); // dist to world plane (z=100) is 100
|
||||
|
||||
projected = chamber2.ProjectMuonEvent(muon);
|
||||
expectedPos = HPoint3f(50, 60, 100);
|
||||
|
||||
std::cout << "\nTest Case 4: Transformed DetectorChamber (active world matrix)" << std::endl;
|
||||
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
|
||||
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
|
||||
|
||||
bool posOk4 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
|
||||
TEST1(posOk4);
|
||||
|
||||
END_TESTING;
|
||||
}
|
||||
31
src/HEP/Geant/ActionInitialization.cpp
Normal file
31
src/HEP/Geant/ActionInitialization.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "ActionInitialization.hh"
|
||||
#include "EmitterPrimary.hh"
|
||||
#include "SteppingAction.hh"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
ActionInitialization::ActionInitialization(EmitterPrimary *emitter, SimulationContext *context)
|
||||
: G4VUserActionInitialization(),
|
||||
m_Emitter(emitter),
|
||||
m_Context(context)
|
||||
{}
|
||||
|
||||
ActionInitialization::~ActionInitialization() {}
|
||||
|
||||
void ActionInitialization::BuildForMaster() const {}
|
||||
|
||||
void ActionInitialization::Build() const {
|
||||
if (m_Emitter) {
|
||||
SetUserAction(m_Emitter->Clone());
|
||||
} else {
|
||||
SetUserAction(new EmitterPrimary());
|
||||
}
|
||||
|
||||
SteppingAction *sa = new SteppingAction(m_Context);
|
||||
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
|
||||
SetUserAction(static_cast<G4UserEventAction*>(sa));
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
28
src/HEP/Geant/ActionInitialization.hh
Normal file
28
src/HEP/Geant/ActionInitialization.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef ActionInitialization_h
|
||||
#define ActionInitialization_h
|
||||
|
||||
#include "G4VUserActionInitialization.hh"
|
||||
#include "SimulationContext.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class EmitterPrimary;
|
||||
|
||||
class ActionInitialization : public G4VUserActionInitialization {
|
||||
public:
|
||||
ActionInitialization(EmitterPrimary *emitter, SimulationContext *context);
|
||||
~ActionInitialization();
|
||||
|
||||
virtual void BuildForMaster() const override;
|
||||
virtual void Build() const override;
|
||||
|
||||
private:
|
||||
EmitterPrimary *m_Emitter;
|
||||
SimulationContext *m_Context;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
74
src/HEP/Geant/CMakeLists.txt
Normal file
74
src/HEP/Geant/CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
|
||||
################################################################################
|
||||
##### HEP/Geant - Geant4 integration library ###################################
|
||||
################################################################################
|
||||
|
||||
if(NOT Geant4_FOUND)
|
||||
message(STATUS "Geant4 not found - skipping mutomGeant library")
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "Geant4 found: ${Geant4_VERSION}")
|
||||
include(${Geant4_USE_FILE})
|
||||
|
||||
set(HEADERS
|
||||
GeantEvent.h
|
||||
Matter.h
|
||||
Scene.h
|
||||
Solid.h
|
||||
DetectorConstruction.hh
|
||||
PhysicsList.hh
|
||||
ActionInitialization.hh
|
||||
SteppingAction.hh
|
||||
SimulationContext.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
Scene.cpp
|
||||
Solid.cpp
|
||||
EmitterPrimary.cpp
|
||||
DetectorConstruction.cpp
|
||||
PhysicsList.cpp
|
||||
ActionInitialization.cpp
|
||||
SteppingAction.cpp
|
||||
)
|
||||
|
||||
set(libname ${PACKAGE_LIBPREFIX}Geant)
|
||||
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} ${libname} PARENT_SCOPE)
|
||||
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Geant PARENT_SCOPE)
|
||||
|
||||
add_library(${libname} SHARED ${SOURCES})
|
||||
set_target_properties(${libname} PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_SOVERSION})
|
||||
|
||||
target_include_directories(${libname} PRIVATE ${Geant4_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(${libname}
|
||||
${PACKAGE_LIBPREFIX}Core
|
||||
${PACKAGE_LIBPREFIX}Math
|
||||
${PACKAGE_LIBPREFIX}Detectors
|
||||
)
|
||||
|
||||
# Filter Geant4 libraries to remove Qt-dependent ones
|
||||
set(Geant4_LIBS_FILTERED ${Geant4_LIBRARIES})
|
||||
if(Geant4_LIBS_FILTERED)
|
||||
list(REMOVE_ITEM Geant4_LIBS_FILTERED Geant4::G4interfaces Geant4::G4OpenGL Geant4::G4visQt3D)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${libname}
|
||||
${Geant4_LIBS_FILTERED}
|
||||
)
|
||||
|
||||
install(TARGETS ${libname}
|
||||
EXPORT "uLibTargets"
|
||||
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
|
||||
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
|
||||
|
||||
install(FILES ${HEADERS}
|
||||
DESTINATION ${INSTALL_INC_DIR}/HEP/Geant)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
include(uLibTargetMacros)
|
||||
add_subdirectory(testing)
|
||||
endif()
|
||||
43
src/HEP/Geant/DetectorActionInitialization.cpp
Normal file
43
src/HEP/Geant/DetectorActionInitialization.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "DetectorActionInitialization.hh"
|
||||
#include "EmitterPrimary.hh"
|
||||
#include "DetectorSteppingAction.hh"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
DetectorActionInitialization::DetectorActionInitialization(EmitterPrimary *emitter,
|
||||
Vector<MuonEvent> *output,
|
||||
const Vector<HLine3f> &planes,
|
||||
int verbosity)
|
||||
: G4VUserActionInitialization(),
|
||||
m_Emitter(emitter),
|
||||
m_Output(output),
|
||||
m_Planes(planes),
|
||||
m_Verbosity(verbosity)
|
||||
{}
|
||||
|
||||
DetectorActionInitialization::~DetectorActionInitialization() {}
|
||||
|
||||
void DetectorActionInitialization::BuildForMaster() const {}
|
||||
|
||||
void DetectorActionInitialization::Build() const {
|
||||
if (m_Verbosity > 0) {
|
||||
std::cout << "[Geant] Worker thread Building actions... Output ptr: " << m_Output
|
||||
<< ", Planes count: " << m_Planes.size() << std::endl;
|
||||
}
|
||||
if (m_Emitter) {
|
||||
SetUserAction(m_Emitter->Clone());
|
||||
} else {
|
||||
SetUserAction(new EmitterPrimary());
|
||||
}
|
||||
|
||||
if (m_Output) {
|
||||
DetectorSteppingAction *sa = new DetectorSteppingAction(m_Output, m_Planes);
|
||||
sa->SetVerbosity(m_Verbosity);
|
||||
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
|
||||
SetUserAction(static_cast<G4UserEventAction*>(sa));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
35
src/HEP/Geant/DetectorActionInitialization.hh
Normal file
35
src/HEP/Geant/DetectorActionInitialization.hh
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef U_GEANT_DETECTORACTIONINITIALIZATION_HH
|
||||
#define U_GEANT_DETECTORACTIONINITIALIZATION_HH
|
||||
|
||||
#include "G4VUserActionInitialization.hh"
|
||||
#include "Core/Vector.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
#include "Math/Dense.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class EmitterPrimary;
|
||||
|
||||
class DetectorActionInitialization : public G4VUserActionInitialization {
|
||||
public:
|
||||
DetectorActionInitialization(EmitterPrimary *emitter,
|
||||
Vector<MuonEvent> *output,
|
||||
const Vector<HLine3f> &planes,
|
||||
int verbosity = 0);
|
||||
~DetectorActionInitialization();
|
||||
|
||||
virtual void BuildForMaster() const override;
|
||||
virtual void Build() const override;
|
||||
|
||||
private:
|
||||
EmitterPrimary *m_Emitter;
|
||||
Vector<MuonEvent> *m_Output;
|
||||
Vector<HLine3f> m_Planes;
|
||||
int m_Verbosity;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
26
src/HEP/Geant/DetectorConstruction.cpp
Normal file
26
src/HEP/Geant/DetectorConstruction.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
#include "DetectorConstruction.hh"
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
|
||||
#include "G4Box.hh"
|
||||
#include "G4LogicalVolume.hh"
|
||||
#include "G4NistManager.hh"
|
||||
#include "G4PVPlacement.hh"
|
||||
#include "G4RunManager.hh"
|
||||
#include "G4SystemOfUnits.hh"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
DetectorConstruction::DetectorConstruction(const char *name) : G4VUserDetectorConstruction() {}
|
||||
|
||||
DetectorConstruction::~DetectorConstruction() {}
|
||||
|
||||
G4VPhysicalVolume *DetectorConstruction::Construct() { return nullptr; }
|
||||
|
||||
void DetectorConstruction::ConstructSDandField() {}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
30
src/HEP/Geant/DetectorConstruction.hh
Normal file
30
src/HEP/Geant/DetectorConstruction.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef DetectorConstruction_h
|
||||
#define DetectorConstruction_h
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "G4VUserDetectorConstruction.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
class G4VPhysicalVolume;
|
||||
class G4LogicalVolume;
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class DetectorConstruction : public G4VUserDetectorConstruction {
|
||||
public:
|
||||
DetectorConstruction(const char *name);
|
||||
virtual ~DetectorConstruction();
|
||||
|
||||
virtual G4VPhysicalVolume *Construct();
|
||||
|
||||
virtual void ConstructSDandField();
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
110
src/HEP/Geant/DetectorSteppingAction.cpp
Normal file
110
src/HEP/Geant/DetectorSteppingAction.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "DetectorSteppingAction.hh"
|
||||
#include <Geant4/G4Step.hh>
|
||||
#include <Geant4/G4Track.hh>
|
||||
#include <Geant4/G4Event.hh>
|
||||
#include <Geant4/G4SystemOfUnits.hh>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
|
||||
static std::mutex g_DetectorOutputMutex;
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
DetectorSteppingAction::DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes)
|
||||
: G4UserSteppingAction(),
|
||||
G4UserEventAction(),
|
||||
m_Output(output),
|
||||
m_Planes(planes),
|
||||
m_CrossCount(0),
|
||||
m_Verbosity(1)
|
||||
{
|
||||
// std::cout << "[Geant] SteppingAction created with " << m_Planes.size() << " planes." << std::endl;
|
||||
}
|
||||
|
||||
DetectorSteppingAction::~DetectorSteppingAction() {}
|
||||
|
||||
void DetectorSteppingAction::BeginOfEventAction(const G4Event* /*event*/) {
|
||||
m_CrossCount = 0;
|
||||
|
||||
// Initialize with NaN
|
||||
float nan = std::numeric_limits<float>::quiet_NaN();
|
||||
m_Current.LineIn().origin = HPoint3f(nan, nan, nan);
|
||||
m_Current.LineIn().direction = HVector3f(nan, nan, nan);
|
||||
m_Current.LineOut().origin = HPoint3f(nan, nan, nan);
|
||||
m_Current.LineOut().direction = HVector3f(nan, nan, nan);
|
||||
m_Current.Momentum() = nan;
|
||||
}
|
||||
|
||||
void DetectorSteppingAction::EndOfEventAction(const G4Event* /*event*/) {
|
||||
if (m_Output) {
|
||||
std::lock_guard<std::mutex> lock(g_DetectorOutputMutex);
|
||||
m_Output->push_back(m_Current);
|
||||
}
|
||||
}
|
||||
|
||||
void DetectorSteppingAction::UserSteppingAction(const G4Step *step) {
|
||||
if (!step) return;
|
||||
if (!m_Output) {
|
||||
return;
|
||||
}
|
||||
|
||||
const G4Track *track = step->GetTrack();
|
||||
if (!track) return;
|
||||
|
||||
static size_t step_count = 0;
|
||||
if (++step_count % 1000 == 0 && m_Verbosity > 0) {
|
||||
std::cout << "[GeantMT] Processed " << step_count << " total steps across events." << std::endl;
|
||||
}
|
||||
|
||||
// Only consider primary muons
|
||||
if (track->GetParentID() != 0) return;
|
||||
|
||||
// Track the momentum at generation/first step if not set
|
||||
if (std::isnan(m_Current.Momentum())) {
|
||||
m_Current.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
|
||||
}
|
||||
|
||||
G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
|
||||
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
|
||||
G4ThreeVector dir_g4 = track->GetMomentumDirection();
|
||||
|
||||
HPoint3f p1f(p1.x(), p1.y(), p1.z());
|
||||
HPoint3f p2f(p2.x(), p2.y(), p2.z());
|
||||
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
|
||||
|
||||
// Check intersection with each detector plane
|
||||
for (const auto& plane : m_Planes) {
|
||||
// Plane: origin=O, direction=N (normal)
|
||||
HPoint3f O = plane.origin;
|
||||
HVector3f N = plane.direction;
|
||||
|
||||
float d1 = (p1f - O).dot(N);
|
||||
float d2 = (p2f - O).dot(N);
|
||||
|
||||
// Check if the step crossed the plane
|
||||
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
|
||||
// Intersection point t = d1 / (d1 - d2)
|
||||
float t = d1 / (d1 - d2);
|
||||
HPoint3f intersection = p1f + t * (p2f - p1f);
|
||||
|
||||
if (m_CrossCount == 0) {
|
||||
m_Current.LineIn().origin = intersection;
|
||||
m_Current.LineIn().direction = dirf;
|
||||
m_CrossCount++;
|
||||
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit first plane at " << intersection.transpose() << std::endl;
|
||||
} else if (m_CrossCount == 1) {
|
||||
m_Current.LineOut().origin = intersection;
|
||||
m_Current.LineOut().direction = dirf;
|
||||
m_CrossCount++;
|
||||
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit second plane at " << intersection.transpose() << std::endl;
|
||||
}
|
||||
// We break to avoid crossing multiple planes in one infinitesimal step (unlikely but possible)
|
||||
// Actually, we should check ALL planes.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
36
src/HEP/Geant/DetectorSteppingAction.hh
Normal file
36
src/HEP/Geant/DetectorSteppingAction.hh
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef U_GEANT_DETECTORSTEPPINGACTION_HH
|
||||
#define U_GEANT_DETECTORSTEPPINGACTION_HH
|
||||
|
||||
#include "G4UserSteppingAction.hh"
|
||||
#include "G4UserEventAction.hh"
|
||||
#include "Core/Vector.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class DetectorSteppingAction : public G4UserSteppingAction, public G4UserEventAction {
|
||||
public:
|
||||
DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes);
|
||||
virtual ~DetectorSteppingAction();
|
||||
|
||||
virtual void UserSteppingAction(const G4Step *step) override;
|
||||
virtual void BeginOfEventAction(const G4Event *event) override;
|
||||
virtual void EndOfEventAction(const G4Event *event) override;
|
||||
|
||||
void SetVerbosity(int level) { m_Verbosity = level; }
|
||||
|
||||
private:
|
||||
Vector<MuonEvent> *m_Output;
|
||||
Vector<HLine3f> m_Planes; // World projection planes
|
||||
MuonEvent m_Current;
|
||||
int m_CrossCount = 0;
|
||||
int m_Verbosity = 0;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
1203
src/HEP/Geant/EcoMug.hh
Normal file
1203
src/HEP/Geant/EcoMug.hh
Normal file
File diff suppressed because it is too large
Load Diff
330
src/HEP/Geant/EmitterPrimary.cpp
Normal file
330
src/HEP/Geant/EmitterPrimary.cpp
Normal file
@@ -0,0 +1,330 @@
|
||||
#include "EmitterPrimary.hh"
|
||||
|
||||
#include "G4Box.hh"
|
||||
#include "G4LogicalVolume.hh"
|
||||
#include "G4LogicalVolumeStore.hh"
|
||||
#include "G4ParticleDefinition.hh"
|
||||
#include "G4ParticleGun.hh"
|
||||
#include "G4ParticleTable.hh"
|
||||
#include "G4RunManager.hh"
|
||||
#include "G4SystemOfUnits.hh"
|
||||
#include "Randomize.hh"
|
||||
|
||||
#include "EcoMug.hh"
|
||||
#include "Math/Cylinder.h"
|
||||
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
EmitterPrimary::EmitterPrimary()
|
||||
: G4VUserPrimaryGeneratorAction(), fParticleGun(nullptr) {
|
||||
// Creiamo il ParticleGun impostandolo per sparare 1 particella alla volta
|
||||
G4int n_particle = 1;
|
||||
fParticleGun = new G4ParticleGun(n_particle);
|
||||
|
||||
// Otteniamo la tabella delle particelle di Geant4
|
||||
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
|
||||
|
||||
// Cerchiamo il muone negativo (usa "mu+" per l'antimuone)
|
||||
G4String particleName = "mu-";
|
||||
G4ParticleDefinition *particle = particleTable->FindParticle(particleName);
|
||||
|
||||
// Configuriamo le proprietà iniziali della particella
|
||||
fParticleGun->SetParticleDefinition(particle);
|
||||
|
||||
// Impostiamo l'energia cinetica a 1 GeV
|
||||
fParticleGun->SetParticleEnergy(1.0 * GeV);
|
||||
|
||||
// Initial position and direction through AffineTransform
|
||||
// 10m on Z axis, pointing towards origin
|
||||
this->SetPosition(Vector3f(0, 0, 10000.0));
|
||||
// Default orientation is identity (pointing along -Z if we rotate the puppet accordingly)
|
||||
// But fParticleGun defaults are set here and overridden in GeneratePrimaries
|
||||
}
|
||||
|
||||
EmitterPrimary::~EmitterPrimary() {
|
||||
// Importante: liberare la memoria
|
||||
delete fParticleGun;
|
||||
}
|
||||
|
||||
void EmitterPrimary::GeneratePrimaries(G4Event *anEvent) {
|
||||
// Use position and direction from AffineTransform
|
||||
Vector3f pos = this->GetPosition();
|
||||
// Assume default direction is along the -Z axis of the local frame
|
||||
Vector4f dir4 = this->GetWorldMatrix() * Vector4f(0, 0, -1, 0);
|
||||
Vector3f dir = dir4.head<3>().normalized();
|
||||
|
||||
fParticleGun->SetParticlePosition(G4ThreeVector(pos(0), pos(1), pos(2)));
|
||||
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(dir(0), dir(1), dir(2)));
|
||||
|
||||
fParticleGun->GeneratePrimaryVertex(anEvent);
|
||||
}
|
||||
|
||||
EmitterPrimary* EmitterPrimary::Clone() const {
|
||||
auto* clone = new EmitterPrimary();
|
||||
clone->SetMatrix(this->GetMatrix());
|
||||
return clone;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// SkyPlaneEmitterPrimary using EcoMug
|
||||
|
||||
SkyPlaneEmitterPrimary::SkyPlaneEmitterPrimary()
|
||||
: EmitterPrimary(), m_EcoMug(new EcoMug()), m_Size(1000.0, 1000.0) {
|
||||
// Initial configuration for EcoMug in sky mode
|
||||
m_EcoMug->SetUseSky();
|
||||
m_EcoMug->SetSkySize({m_Size(0)/1000.0, m_Size(1)/1000.0});
|
||||
}
|
||||
|
||||
SkyPlaneEmitterPrimary::~SkyPlaneEmitterPrimary() {
|
||||
delete m_EcoMug;
|
||||
}
|
||||
|
||||
void SkyPlaneEmitterPrimary::SetSkySize(const uLib::Vector2f& size) {
|
||||
m_Size = size;
|
||||
// EcoMug units are in meters (m=1), Geant4 units are in mm.
|
||||
m_EcoMug->SetSkySize({m_Size(0)/1000.0, m_Size(1)/1000.0});
|
||||
this->Updated();
|
||||
}
|
||||
|
||||
void SkyPlaneEmitterPrimary::SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal) {
|
||||
this->SetPosition(p0);
|
||||
// Orient the emitter so that local Z is the normal.
|
||||
// This is useful for unconventional planes, though EcoMug sky is usually horizontal.
|
||||
// If we want a truly 'sky' plane, it usually stays horizontal.
|
||||
this->Updated();
|
||||
}
|
||||
|
||||
void SkyPlaneEmitterPrimary::GeneratePrimaries(G4Event* anEvent) {
|
||||
if (!m_EcoMug) return;
|
||||
m_EcoMug->Generate();
|
||||
|
||||
// EcoMug position is relative to its internal sky center in meters.
|
||||
// Our wrapper uses the AffineTransform for the overall positioning.
|
||||
std::array<double, 3> pos_m = m_EcoMug->GetGenerationPosition();
|
||||
G4ThreeVector local_pos(pos_m[0]*1000.0, pos_m[1]*1000.0, pos_m[2]*1000.0);
|
||||
|
||||
// EcoMug momentum (direction and magnitude in GeV/c)
|
||||
double p_mag = m_EcoMug->GetGenerationMomentum();
|
||||
double theta = m_EcoMug->GetGenerationTheta();
|
||||
double phi = m_EcoMug->GetGenerationPhi();
|
||||
|
||||
// EcoMug theta is generated in a way that PI means pointing down (-Z)
|
||||
G4ThreeVector local_dir(
|
||||
sin(theta) * cos(phi),
|
||||
sin(theta) * sin(phi),
|
||||
cos(theta)
|
||||
);
|
||||
|
||||
// Transform local coordinates to world
|
||||
Matrix4f world_mat = this->GetWorldMatrix();
|
||||
Vector3f world_pos = (world_mat * Vector4f(local_pos.x(), local_pos.y(), local_pos.z(), 1.0)).head<3>();
|
||||
Vector3f world_dir = (world_mat * Vector4f(local_dir.x(), local_dir.y(), local_dir.z(), 0.0)).head<3>().normalized();
|
||||
|
||||
// Set particle charge
|
||||
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
|
||||
G4String particleName = (m_EcoMug->GetCharge() > 0) ? "mu+" : "mu-";
|
||||
fParticleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
|
||||
|
||||
fParticleGun->SetParticlePosition(G4ThreeVector(world_pos(0), world_pos(1), world_pos(2)));
|
||||
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(world_dir(0), world_dir(1), world_dir(2)));
|
||||
fParticleGun->SetParticleEnergy(p_mag * GeV);
|
||||
|
||||
fParticleGun->GeneratePrimaryVertex(anEvent);
|
||||
}
|
||||
|
||||
EmitterPrimary* SkyPlaneEmitterPrimary::Clone() const {
|
||||
auto* clone = new SkyPlaneEmitterPrimary();
|
||||
clone->SetSkySize(this->m_Size);
|
||||
clone->SetMatrix(this->GetMatrix());
|
||||
return clone;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
// CylinderEmitterPrimary using EcoMug
|
||||
|
||||
CylinderEmitterPrimary::CylinderEmitterPrimary()
|
||||
: EmitterPrimary(), m_EcoMug(new EcoMug()), m_Radius(1000.0), m_Height(1000.0) {
|
||||
m_EcoMug->SetUseCylinder();
|
||||
m_EcoMug->SetCylinderRadius(m_Radius/1000.0);
|
||||
m_EcoMug->SetCylinderHeight(m_Height/1000.0);
|
||||
m_EcoMug->SetCylinderCenterPosition({0.0, 0.0, m_Height/2000.0});
|
||||
}
|
||||
|
||||
CylinderEmitterPrimary::~CylinderEmitterPrimary() {
|
||||
delete m_EcoMug;
|
||||
}
|
||||
|
||||
void CylinderEmitterPrimary::SetRadius(float r) {
|
||||
m_Radius = r;
|
||||
m_EcoMug->SetCylinderRadius(m_Radius/1000.0);
|
||||
this->Updated();
|
||||
}
|
||||
|
||||
void CylinderEmitterPrimary::SetHeight(float h) {
|
||||
m_Height = h;
|
||||
m_EcoMug->SetCylinderHeight(m_Height/1000.0);
|
||||
m_EcoMug->SetCylinderCenterPosition({0.0, 0.0, m_Height/2000.0});
|
||||
this->Updated();
|
||||
}
|
||||
|
||||
void CylinderEmitterPrimary::GeneratePrimaries(G4Event* anEvent) {
|
||||
if (!m_EcoMug) return;
|
||||
m_EcoMug->Generate();
|
||||
|
||||
std::array<double, 3> pos_m = m_EcoMug->GetGenerationPosition();
|
||||
G4ThreeVector local_pos(pos_m[0]*1000.0, pos_m[1]*1000.0, pos_m[2]*1000.0);
|
||||
|
||||
double p_mag = m_EcoMug->GetGenerationMomentum();
|
||||
double theta = m_EcoMug->GetGenerationTheta();
|
||||
double phi = m_EcoMug->GetGenerationPhi();
|
||||
|
||||
G4ThreeVector local_dir(
|
||||
sin(theta) * cos(phi),
|
||||
sin(theta) * sin(phi),
|
||||
cos(theta)
|
||||
);
|
||||
|
||||
Matrix4f world_mat = this->GetWorldMatrix();
|
||||
Vector3f world_pos = (world_mat * Vector4f(local_pos.x(), local_pos.y(), local_pos.z(), 1.0)).head<3>();
|
||||
Vector3f world_dir = (world_mat * Vector4f(local_dir.x(), local_dir.y(), local_dir.z(), 0.0)).head<3>().normalized();
|
||||
|
||||
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
|
||||
G4String particleName = (m_EcoMug->GetCharge() > 0) ? "mu+" : "mu-";
|
||||
fParticleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
|
||||
|
||||
fParticleGun->SetParticlePosition(G4ThreeVector(world_pos(0), world_pos(1), world_pos(2)));
|
||||
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(world_dir(0), world_dir(1), world_dir(2)));
|
||||
fParticleGun->SetParticleEnergy(p_mag * GeV);
|
||||
|
||||
fParticleGun->GeneratePrimaryVertex(anEvent);
|
||||
}
|
||||
|
||||
EmitterPrimary* CylinderEmitterPrimary::Clone() const {
|
||||
auto* clone = new CylinderEmitterPrimary();
|
||||
clone->SetRadius(this->m_Radius);
|
||||
clone->SetHeight(this->m_Height);
|
||||
clone->SetMatrix(this->GetMatrix());
|
||||
return clone;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------- //
|
||||
|
||||
|
||||
QuadMeshEmitterPrimary::QuadMeshEmitterPrimary()
|
||||
: EmitterPrimary(), m_Mesh(nullptr), m_TotalArea(0.0) {
|
||||
}
|
||||
|
||||
QuadMeshEmitterPrimary::~QuadMeshEmitterPrimary() {
|
||||
}
|
||||
|
||||
void QuadMeshEmitterPrimary::SetMesh(uLib::QuadMesh *mesh) {
|
||||
m_Mesh = mesh;
|
||||
CalculateAreas();
|
||||
}
|
||||
|
||||
void QuadMeshEmitterPrimary::CalculateAreas() {
|
||||
if (!m_Mesh) return;
|
||||
m_CumulativeAreas.clear();
|
||||
m_TotalArea = 0.0;
|
||||
|
||||
const auto &quads = m_Mesh->Quads();
|
||||
|
||||
for (const auto &q : quads) {
|
||||
uLib::Vector3f v0 = m_Mesh->GetPoint(q(0));
|
||||
uLib::Vector3f v1 = m_Mesh->GetPoint(q(1));
|
||||
uLib::Vector3f v2 = m_Mesh->GetPoint(q(2));
|
||||
uLib::Vector3f v3 = m_Mesh->GetPoint(q(3));
|
||||
|
||||
double a1 = 0.5 * (v1 - v0).cross(v2 - v0).norm();
|
||||
double a2 = 0.5 * (v2 - v0).cross(v3 - v0).norm();
|
||||
m_TotalArea += (a1 + a2);
|
||||
m_CumulativeAreas.push_back(m_TotalArea);
|
||||
}
|
||||
}
|
||||
|
||||
void QuadMeshEmitterPrimary::GeneratePrimaries(G4Event *anEvent) {
|
||||
if (!m_Mesh || m_TotalArea <= 0.0) return;
|
||||
|
||||
// 1. Choose a quad
|
||||
double r = G4UniformRand() * m_TotalArea;
|
||||
auto it = std::lower_bound(m_CumulativeAreas.begin(), m_CumulativeAreas.end(), r);
|
||||
int quadIdx = std::distance(m_CumulativeAreas.begin(), it);
|
||||
|
||||
const auto &q = m_Mesh->Quads()[quadIdx];
|
||||
uLib::Vector3f v0 = m_Mesh->GetPoint(q(0));
|
||||
uLib::Vector3f v1 = m_Mesh->GetPoint(q(1));
|
||||
uLib::Vector3f v2 = m_Mesh->GetPoint(q(2));
|
||||
uLib::Vector3f v3 = m_Mesh->GetPoint(q(3));
|
||||
|
||||
// 2. Choose a point on the quad
|
||||
double a1 = 0.5 * (v1 - v0).cross(v2 - v0).norm();
|
||||
double a2 = 0.5 * (v2 - v0).cross(v3 - v0).norm();
|
||||
|
||||
G4ThreeVector pos;
|
||||
uLib::Vector3f normal = m_Mesh->GetNormal(quadIdx);
|
||||
|
||||
if (G4UniformRand() < a1 / (a1 + a2)) {
|
||||
double u = G4UniformRand();
|
||||
double v = G4UniformRand();
|
||||
if (u + v > 1.0) { u = 1.0 - u; v = 1.0 - v; }
|
||||
uLib::Vector3f p = v0 + u * (v1 - v0) + v * (v2 - v0);
|
||||
pos.set(p(0), p(1), p(2));
|
||||
} else {
|
||||
double u = G4UniformRand();
|
||||
double v = G4UniformRand();
|
||||
if (u + v > 1.0) { u = 1.0 - u; v = 1.0 - v; }
|
||||
uLib::Vector3f p = v0 + u * (v2 - v0) + v * (v3 - v0);
|
||||
pos.set(p(0), p(1), p(2));
|
||||
}
|
||||
|
||||
// 3. Choose a direction (Cosmic Muon: cos^2(theta))
|
||||
G4ThreeVector dir;
|
||||
bool accepted = false;
|
||||
int tries = 0;
|
||||
|
||||
while (!accepted && tries < 1000) {
|
||||
tries++;
|
||||
double cosTheta = std::pow(G4UniformRand(), 1.0/3.0);
|
||||
double sinTheta = std::sqrt(1.0 - cosTheta * cosTheta);
|
||||
double phi = 2.0 * M_PI * G4UniformRand();
|
||||
|
||||
// Incoming from above (+Z towards -Z)
|
||||
dir.set(sinTheta * std::cos(phi), sinTheta * std::sin(phi), -cosTheta);
|
||||
|
||||
// Filtering: pointing on the same side of the face normal
|
||||
if (dir.x() * normal(0) + dir.y() * normal(1) + dir.z() * normal(2) > 0) {
|
||||
accepted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (accepted) {
|
||||
fParticleGun->SetParticlePosition(pos);
|
||||
fParticleGun->SetParticleMomentumDirection(dir);
|
||||
// Keep energy from base class or set here if needed
|
||||
fParticleGun->GeneratePrimaryVertex(anEvent);
|
||||
}
|
||||
}
|
||||
EmitterPrimary* QuadMeshEmitterPrimary::Clone() const {
|
||||
auto* clone = new QuadMeshEmitterPrimary();
|
||||
if (m_Mesh) clone->SetMesh(m_Mesh);
|
||||
clone->SetMatrix(this->GetMatrix());
|
||||
return clone;
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
127
src/HEP/Geant/EmitterPrimary.hh
Normal file
127
src/HEP/Geant/EmitterPrimary.hh
Normal file
@@ -0,0 +1,127 @@
|
||||
#ifndef U_GEANT_EMITTERPRIMARY_HH
|
||||
#define U_GEANT_EMITTERPRIMARY_HH 1
|
||||
|
||||
#include "G4VUserPrimaryGeneratorAction.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
|
||||
namespace uLib {
|
||||
class QuadMesh;
|
||||
}
|
||||
|
||||
class EcoMug;
|
||||
|
||||
#include "Math/QuadMesh.h"
|
||||
#include "Core/Object.h"
|
||||
#include "Math/Transform.h"
|
||||
#include <vector> // Added for std::vector
|
||||
|
||||
class G4ParticleGun;
|
||||
class G4Event;
|
||||
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public Object, public AffineTransform
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.EmitterPrimary"; }
|
||||
|
||||
EmitterPrimary();
|
||||
virtual ~EmitterPrimary();
|
||||
|
||||
// Metodo principale chiamato all'inizio di ogni evento
|
||||
virtual void GeneratePrimaries(G4Event*);
|
||||
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(EmitterPrimary::Updated); }
|
||||
|
||||
/// Create a clone of this emitter for multi-threading
|
||||
virtual EmitterPrimary* Clone() const;
|
||||
|
||||
protected:
|
||||
G4ParticleGun* fParticleGun; // Puntatore al cannone di particelle
|
||||
};
|
||||
|
||||
class SkyPlaneEmitterPrimary : public EmitterPrimary
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.SkyPlaneEmitterPrimary"; }
|
||||
|
||||
SkyPlaneEmitterPrimary();
|
||||
virtual ~SkyPlaneEmitterPrimary();
|
||||
|
||||
virtual void GeneratePrimaries(G4Event*);
|
||||
|
||||
void SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal);
|
||||
void SetSkySize(const uLib::Vector2f& size);
|
||||
uLib::Vector2f GetSkySize() const { return m_Size; }
|
||||
|
||||
virtual EmitterPrimary* Clone() const override;
|
||||
|
||||
private:
|
||||
EcoMug* m_EcoMug;
|
||||
uLib::Vector2f m_Size;
|
||||
};
|
||||
|
||||
class CylinderEmitterPrimary : public EmitterPrimary
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.CylinderEmitterPrimary"; }
|
||||
|
||||
CylinderEmitterPrimary();
|
||||
virtual ~CylinderEmitterPrimary();
|
||||
|
||||
virtual void GeneratePrimaries(G4Event*);
|
||||
|
||||
void SetRadius(float r);
|
||||
float GetRadius() const { return m_Radius; }
|
||||
void SetHeight(float h);
|
||||
float GetHeight() const { return m_Height; }
|
||||
|
||||
virtual EmitterPrimary* Clone() const override;
|
||||
|
||||
private:
|
||||
EcoMug* m_EcoMug;
|
||||
float m_Radius;
|
||||
float m_Height;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class QuadMeshEmitterPrimary : public EmitterPrimary
|
||||
{
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.QuadMeshEmitterPrimary"; }
|
||||
|
||||
QuadMeshEmitterPrimary();
|
||||
virtual ~QuadMeshEmitterPrimary();
|
||||
|
||||
// Metodo principale chiamato all'inizio di ogni evento
|
||||
virtual void GeneratePrimaries(G4Event*);
|
||||
|
||||
void SetMesh(uLib::QuadMesh* mesh);
|
||||
|
||||
virtual EmitterPrimary* Clone() const override;
|
||||
|
||||
private:
|
||||
uLib::QuadMesh* m_Mesh;
|
||||
std::vector<double> m_CumulativeAreas;
|
||||
double m_TotalArea;
|
||||
|
||||
void CalculateAreas();
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
25
src/HEP/Geant/GeantEvent.cpp
Normal file
25
src/HEP/Geant/GeantEvent.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "HEP/Geant/GeantEvent.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
void GeantEvent::Print(const size_t size) const {
|
||||
std::cout << "Event " << m_EventID << ":" << std::endl;
|
||||
std::cout << " Momentum: " << m_Momentum << std::endl;
|
||||
std::cout << " GenVector: " << m_GenVector << std::endl;
|
||||
std::cout << " Path: " << std::endl;
|
||||
|
||||
size_t limit = m_Path.size();
|
||||
if(size > 0 && size < m_Path.size()) {
|
||||
limit = size;
|
||||
}
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
std::cout << " " << i << ": " << m_Path[i].m_Length << " " << m_Path[i].m_Momentum << " " << m_Path[i].m_Direction << " " << m_Path[i].m_SolidName << std::endl;
|
||||
}
|
||||
if (limit < m_Path.size()) {
|
||||
std::cout << " ... (" << m_Path.size() - limit << " more deltas)" << std::endl;
|
||||
}
|
||||
}
|
||||
96
src/HEP/Geant/GeantEvent.h
Normal file
96
src/HEP/Geant/GeantEvent.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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_GEANTEVENT_H
|
||||
#define U_GEANTEVENT_H
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Types.h"
|
||||
#include "Core/Vector.h"
|
||||
#include "Math/Dense.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
// Forward declaration for friend access
|
||||
class SteppingAction;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// GeantEvent — output of a Geant4 simulation run.
|
||||
///
|
||||
/// m_Momentum and m_GenVector are set from the EmitterPrimary at generation.
|
||||
/// During simulation, each scattering interaction deposits a Delta in m_Path,
|
||||
/// recording the change of momentum and direction at each step boundary.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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
|
||||
Scalarf m_Momentum; ///< momentum magnitude at this step
|
||||
HVector3f m_Direction; ///< direction after scattering
|
||||
std::string m_SolidName; ///< name of the solid where interaction occurred
|
||||
|
||||
uLibGetMacro(Length, Scalarf)
|
||||
uLibGetMacro(Momentum, Scalarf)
|
||||
uLibConstRefMacro(Direction, HVector3f)
|
||||
uLibConstRefMacro(SolidName, std::string)
|
||||
|
||||
Delta() : m_Length(0), m_Momentum(0), m_Direction(HVector3f::Zero()) {}
|
||||
};
|
||||
|
||||
// --- Read-only accessors (public) --- //
|
||||
uLibGetMacro(EventID, Id_t)
|
||||
uLibGetMacro(Momentum, Scalarf)
|
||||
uLibConstRefMacro(GenVector, HLine3f)
|
||||
uLibConstRefMacro(Path, Vector<Delta>)
|
||||
|
||||
void Print(const size_t size = 10) const;
|
||||
|
||||
|
||||
private:
|
||||
Id_t m_EventID;
|
||||
Scalarf m_Momentum;
|
||||
HLine3f m_GenVector;
|
||||
Vector<Delta> m_Path;
|
||||
|
||||
public:
|
||||
GeantEvent() : m_EventID(0), m_Momentum(0), m_GenVector(HLine3f()), m_Path() {}
|
||||
|
||||
// SteppingAction can populate the private fields during simulation
|
||||
friend class SteppingAction;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_GEANTEVENT_H
|
||||
@@ -34,6 +34,7 @@ class G4Element;
|
||||
class G4Material;
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -58,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;
|
||||
@@ -65,6 +68,7 @@ private:
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
src/HEP/Geant/PhysicsList.cpp
Normal file
28
src/HEP/Geant/PhysicsList.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "PhysicsList.hh"
|
||||
|
||||
#include "G4DecayPhysics.hh"
|
||||
#include "G4EmStandardPhysics.hh"
|
||||
#include "G4RadioactiveDecayPhysics.hh"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
PhysicsList::PhysicsList() : G4VModularPhysicsList() {
|
||||
SetVerboseLevel(1);
|
||||
|
||||
// Default physics
|
||||
RegisterPhysics(new G4DecayPhysics());
|
||||
|
||||
// EM physics
|
||||
RegisterPhysics(new G4EmStandardPhysics());
|
||||
|
||||
// Radioactive decay
|
||||
RegisterPhysics(new G4RadioactiveDecayPhysics());
|
||||
}
|
||||
|
||||
PhysicsList::~PhysicsList() {}
|
||||
|
||||
void PhysicsList::SetCuts() { G4VModularPhysicsList::SetCuts(); }
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
20
src/HEP/Geant/PhysicsList.hh
Normal file
20
src/HEP/Geant/PhysicsList.hh
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef PhysicsList_h
|
||||
#define PhysicsList_h
|
||||
|
||||
#include "G4VModularPhysicsList.hh"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class PhysicsList : public G4VModularPhysicsList {
|
||||
public:
|
||||
PhysicsList();
|
||||
virtual ~PhysicsList();
|
||||
|
||||
virtual void SetCuts();
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
160
src/HEP/Geant/Scene.cpp
Normal file
160
src/HEP/Geant/Scene.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
#include <Geant4/G4Box.hh>
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
#include <Geant4/G4Material.hh>
|
||||
#include <Geant4/G4NistManager.hh>
|
||||
#include <Geant4/G4PVPlacement.hh>
|
||||
#include <Geant4/G4RunManager.hh>
|
||||
#include <Geant4/G4RunManagerFactory.hh>
|
||||
#include <Geant4/G4SystemOfUnits.hh>
|
||||
#include <Geant4/G4VPhysicalVolume.hh>
|
||||
|
||||
#include "Core/Vector.h"
|
||||
#include "HEP/Geant/DetectorConstruction.hh"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Solid.h"
|
||||
|
||||
#include "Scene.h"
|
||||
#include "PhysicsList.hh"
|
||||
#include "ActionInitialization.hh"
|
||||
#include "SimulationContext.h"
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class SceneDetectorConstruction : public DetectorConstruction {
|
||||
public:
|
||||
SceneDetectorConstruction(class SceneImpl *owner) : DetectorConstruction("Scene"), m_Owner(owner) {}
|
||||
G4VPhysicalVolume *Construct() override;
|
||||
private:
|
||||
class SceneImpl *m_Owner;
|
||||
};
|
||||
|
||||
static void CheckGeant4Environment() {
|
||||
static bool checked = false;
|
||||
if (checked) return;
|
||||
checked = true;
|
||||
if (!std::getenv("G4ENSDFSTATEDATA")) {
|
||||
std::cerr << "********************************************************" << std::endl;
|
||||
std::cerr << " WARNING: Geant4 environment variables are not set!" << std::endl;
|
||||
std::cerr << " Please activate the environment before running:" << std::endl;
|
||||
std::cerr << " micromamba activate mutom" << std::endl;
|
||||
std::cerr << "********************************************************" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
class SceneImpl {
|
||||
public:
|
||||
SceneImpl() : m_RunManager(G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default)),
|
||||
m_Emitter(nullptr),
|
||||
m_InitCalled(false) {
|
||||
m_RunManager->SetUserInitialization(new PhysicsList);
|
||||
}
|
||||
|
||||
~SceneImpl() {
|
||||
if (m_RunManager) delete m_RunManager;
|
||||
// m_World deletion is handled in Scene destructor or here
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
if (m_InitCalled) return;
|
||||
|
||||
m_RunManager->SetUserInitialization(new SceneDetectorConstruction(this));
|
||||
m_RunManager->SetUserInitialization(new ActionInitialization(m_Emitter, &m_Context));
|
||||
|
||||
m_RunManager->Initialize();
|
||||
m_InitCalled = true;
|
||||
}
|
||||
|
||||
Vector<Solid *> m_Solids;
|
||||
Solid *m_World = nullptr;
|
||||
ContainerBox m_WorldBox;
|
||||
G4RunManager *m_RunManager;
|
||||
EmitterPrimary *m_Emitter;
|
||||
SimulationContext m_Context;
|
||||
bool m_InitCalled;
|
||||
};
|
||||
|
||||
G4VPhysicalVolume *SceneDetectorConstruction::Construct() {
|
||||
return m_Owner->m_World->GetPhysical();
|
||||
}
|
||||
|
||||
Scene::Scene() {
|
||||
CheckGeant4Environment();
|
||||
d = new SceneImpl();
|
||||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
// Delete solids
|
||||
for(auto s : d->m_Solids) delete s;
|
||||
delete d;
|
||||
}
|
||||
|
||||
void Scene::AddSolid(Solid *solid, Solid *parent) {
|
||||
d->m_Solids.push_back(solid);
|
||||
if (!d->m_World) {
|
||||
d->m_World = solid;
|
||||
} else {
|
||||
solid->SetParent(parent ? parent : d->m_World);
|
||||
}
|
||||
}
|
||||
|
||||
const Solid* Scene::GetWorld() const { return d->m_World; }
|
||||
ContainerBox* Scene::GetWorldBox() const { return &d->m_WorldBox; }
|
||||
|
||||
void Scene::ConstructWorldBox(const Vector3f &size, const char *material) {
|
||||
d->m_WorldBox.Scale(size);
|
||||
d->m_WorldBox.SetPosition(-size/2.0f);
|
||||
|
||||
if (!d->m_World) {
|
||||
d->m_World = new Solid("World");
|
||||
d->m_World->SetNistMaterial(material);
|
||||
AddSolid(d->m_World);
|
||||
}
|
||||
|
||||
G4Box *solidWorld = new G4Box("World", 0.5 * size(0), 0.5 * size(1), 0.5 * size(2));
|
||||
G4LogicalVolume *logicWorld = new G4LogicalVolume(solidWorld, d->m_World->GetMaterial(), d->m_World->GetName());
|
||||
d->m_World->SetLogical(logicWorld);
|
||||
|
||||
G4PVPlacement *physWorld = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0), logicWorld, d->m_World->GetName(), 0, false, 0, true);
|
||||
d->m_World->SetPhysical(physWorld);
|
||||
}
|
||||
|
||||
void Scene::SetEmitter(EmitterPrimary *emitter) { d->m_Emitter = emitter; }
|
||||
void Scene::Initialize() { d->Initialize(); }
|
||||
void Scene::SetVerbosity(int level) {
|
||||
d->m_Context.verbosity = level;
|
||||
if (d->m_RunManager) d->m_RunManager->SetVerboseLevel(level);
|
||||
}
|
||||
|
||||
void Scene::RunSimulation(int nEvents, Vector<GeantEvent> &results) {
|
||||
d->Initialize(); // Ensure initialized
|
||||
d->m_Context.mode = SimulationMode::DETAILED;
|
||||
d->m_Context.outputGeant = &results;
|
||||
d->m_Context.outputMuon = nullptr;
|
||||
|
||||
d->m_RunManager->BeamOn(nEvents);
|
||||
}
|
||||
|
||||
void Scene::RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results) {
|
||||
d->Initialize(); // Ensure initialized
|
||||
d->m_Context.mode = SimulationMode::DETECTOR;
|
||||
d->m_Context.outputGeant = nullptr;
|
||||
d->m_Context.outputMuon = &results;
|
||||
|
||||
// Find detector planes
|
||||
d->m_Context.detectorPlanes.clear();
|
||||
for (Solid* s : d->m_Solids) {
|
||||
if (BoxSolid* bs = dynamic_cast<BoxSolid*>(s)) {
|
||||
if (DetectorChamber* dc = dynamic_cast<DetectorChamber*>(bs->GetObject())) {
|
||||
d->m_Context.detectorPlanes.push_back(dc->GetWorldProjectionPlane());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d->m_RunManager->BeamOn(nEvents);
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
84
src/HEP/Geant/Scene.h
Normal file
84
src/HEP/Geant/Scene.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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 SCENE_H
|
||||
#define SCENE_H
|
||||
|
||||
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Vector.h"
|
||||
#include "Solid.h"
|
||||
#include "GeantEvent.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
|
||||
class G4VPhysicalVolume;
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class EmitterPrimary;
|
||||
|
||||
class Scene : public Object {
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.Scene"; }
|
||||
|
||||
Scene();
|
||||
~Scene();
|
||||
|
||||
void AddSolid(Solid *solid, Solid *parent = nullptr);
|
||||
|
||||
void ConstructWorldBox(const Vector3f &size, const char *material);
|
||||
|
||||
/// Get the world box
|
||||
const Solid* GetWorld() const;
|
||||
|
||||
ContainerBox* GetWorldBox() const;
|
||||
|
||||
/// Set the primary generator (emitter) for the simulation.
|
||||
/// The Scene does NOT take ownership of the emitter.
|
||||
void SetEmitter(EmitterPrimary *emitter);
|
||||
|
||||
/// Initialize the Geant4 run manager with detector, physics, and action.
|
||||
void Initialize();
|
||||
|
||||
/// Set the verbosity level for console output (default 0)
|
||||
void SetVerbosity(int level);
|
||||
|
||||
/// Run the simulation for nEvents muons.
|
||||
/// Results are appended to the provided vector.
|
||||
void RunSimulation(int nEvents, Vector<GeantEvent> &results);
|
||||
|
||||
/// Specialized detector simulation trackingMuonEvent line crossings.
|
||||
void RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results);
|
||||
|
||||
private:
|
||||
class SceneImpl *d;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
#endif // SCENE_H
|
||||
30
src/HEP/Geant/SimulationContext.h
Normal file
30
src/HEP/Geant/SimulationContext.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef U_GEANT_SIMULATIONCONTEXT_H
|
||||
#define U_GEANT_SIMULATIONCONTEXT_H
|
||||
|
||||
#include "Core/Vector.h"
|
||||
#include "GeantEvent.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
#include "Math/Dense.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
enum class SimulationMode {
|
||||
DETAILED,
|
||||
DETECTOR
|
||||
};
|
||||
|
||||
struct SimulationContext {
|
||||
SimulationMode mode = SimulationMode::DETAILED;
|
||||
Vector<GeantEvent> *outputGeant = nullptr;
|
||||
Vector<MuonEvent> *outputMuon = nullptr;
|
||||
Vector<HLine3f> detectorPlanes;
|
||||
int verbosity = 0;
|
||||
std::mutex outputMutex;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
208
src/HEP/Geant/Solid.cpp
Normal file
208
src/HEP/Geant/Solid.cpp
Normal file
@@ -0,0 +1,208 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
// G4 Solid //
|
||||
#include <CLHEP/Units/SystemOfUnits.h>
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
#include <Geant4/G4Material.hh>
|
||||
#include <Geant4/G4NistManager.hh>
|
||||
|
||||
// Tessellated solid //
|
||||
#include <Geant4/G4TessellatedSolid.hh>
|
||||
#include <Geant4/G4ThreeVector.hh>
|
||||
#include <Geant4/G4TriangularFacet.hh>
|
||||
#include <Geant4/G4Box.hh>
|
||||
#include <Geant4/G4PVPlacement.hh>
|
||||
|
||||
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Transform.h"
|
||||
|
||||
#include "Solid.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class DetectorsSolidImpl {
|
||||
public:
|
||||
static G4ThreeVector getG4Vector3f(const Vector3f &vector) {
|
||||
return G4ThreeVector(vector(0), vector(1), vector(2));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
Solid::Solid()
|
||||
: m_Name("unnamed_solid"), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
|
||||
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
|
||||
|
||||
Solid::Solid(const char *name)
|
||||
: m_Name(name), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
|
||||
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
|
||||
|
||||
Solid::~Solid() {
|
||||
if (m_Position) delete m_Position;
|
||||
if (m_Rotation) delete m_Rotation;
|
||||
}
|
||||
|
||||
void Solid::SetNistMaterial(const char *name) {
|
||||
G4NistManager *nist = G4NistManager::Instance();
|
||||
G4Material *mat = nist->FindOrBuildMaterial(name);
|
||||
if (mat) SetMaterial(mat);
|
||||
}
|
||||
|
||||
void Solid::SetMaterial(G4Material *material) {
|
||||
if (material) {
|
||||
m_Material = material;
|
||||
if (m_Logical) {
|
||||
m_Logical->SetMaterial(material);
|
||||
} else if (GetG4Solid()) {
|
||||
m_Logical = new G4LogicalVolume(GetG4Solid(), m_Material, GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Solid::SetTransform(Matrix4f transform) {
|
||||
uLib::AffineTransform t;
|
||||
t.SetMatrix(transform);
|
||||
|
||||
// 2. Extract position and rotation for Geant4
|
||||
Vector3f pos = t.GetPosition();
|
||||
if (!m_Position) m_Position = new G4ThreeVector();
|
||||
*m_Position = G4ThreeVector(pos(0), pos(1), pos(2));
|
||||
|
||||
// Create a G4 rotation matrix from the 4x4 matrix
|
||||
Matrix3f m = t.GetRotation();
|
||||
if (!m_Rotation) m_Rotation = new G4RotationMatrix();
|
||||
m_Rotation->set(G4ThreeVector(m(0,0), m(1,0), m(2,0)),
|
||||
G4ThreeVector(m(0,1), m(1,1), m(2,1)),
|
||||
G4ThreeVector(m(0,2), m(1,2), m(2,2)));
|
||||
|
||||
// 3. If object is already placed, update its transformation
|
||||
if (m_Physical) {
|
||||
m_Physical->SetTranslation(*m_Position);
|
||||
m_Physical->SetRotation(m_Rotation);
|
||||
}
|
||||
}
|
||||
|
||||
void Solid::SetParent(Solid *parent) {
|
||||
if (!m_Logical) {
|
||||
std::cerr << "logical volume not created for solid " << GetName() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_Physical) {
|
||||
std::cerr << "physical volume already created for solid " << GetName() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
G4LogicalVolume* parentLogical = nullptr;
|
||||
if (parent) {
|
||||
parentLogical = parent->GetLogical();
|
||||
if (!parentLogical) {
|
||||
std::cerr << "parent logical volume not created for solid " << parent->GetName() << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// G4PVPlacement
|
||||
m_Physical = new G4PVPlacement(
|
||||
m_Rotation, // Rotation
|
||||
*m_Position, // Position (translation) inside the parent
|
||||
m_Logical, // The logical volume of this solid (the child)
|
||||
GetName(), // Name of the physical volume
|
||||
parentLogical, // The logical volume of the parent (nullptr if it's the World volume)
|
||||
false, // Boolean operations (usually false)
|
||||
0, // Copy number
|
||||
true // Check overlaps (useful to enable in debug phase)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TessellatedSolid::TessellatedSolid(const char *name)
|
||||
: BaseClass(name), m_Solid(new G4TessellatedSolid(name)) {
|
||||
}
|
||||
|
||||
void TessellatedSolid::SetMesh(TriangleMesh &mesh) {
|
||||
G4TessellatedSolid *ts = this->m_Solid;
|
||||
for (int i = 0; i < mesh.Triangles().size(); ++i) {
|
||||
const Vector3i &trg = mesh.Triangles().at(i);
|
||||
G4TriangularFacet *facet = new G4TriangularFacet(
|
||||
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(0))),
|
||||
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(1))),
|
||||
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(2))), ABSOLUTE);
|
||||
ts->AddFacet((G4VFacet *)facet);
|
||||
}
|
||||
if (this->m_Logical) {
|
||||
this->m_Logical->SetSolid(ts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BoxSolid::BoxSolid(const char *name, ContainerBox *box) : BaseClass(name) {
|
||||
m_Solid = new G4Box(name, 1,1,1);
|
||||
m_Object = box;
|
||||
Object::connect(box, &ContainerBox::Updated, this, &BoxSolid::Update);
|
||||
if (m_Logical) {
|
||||
m_Logical->SetSolid(m_Solid);
|
||||
}
|
||||
Update();
|
||||
}
|
||||
|
||||
void BoxSolid::Update() {
|
||||
if (m_Object) {
|
||||
Vector3f size = m_Object->GetSize();
|
||||
m_Solid->SetXHalfLength(size(0) * 0.5);
|
||||
m_Solid->SetYHalfLength(size(1) * 0.5);
|
||||
m_Solid->SetZHalfLength(size(2) * 0.5);
|
||||
|
||||
// Geant4 placement is relative to center. uLib Box is anchored at corner.
|
||||
// 1. Get position and rotation (clean, without scale)
|
||||
Vector3f pos = m_Object->GetPosition();
|
||||
Matrix3f rot = m_Object->GetRotation();
|
||||
|
||||
// 2. Center = Corner + Rotation * (Half-Size)
|
||||
// We must rotate the offset vector because uLib box can be rotated.
|
||||
Vector3f center = pos + rot * (size * 0.5);
|
||||
|
||||
uLib::AffineTransform t;
|
||||
t.SetPosition(center);
|
||||
t.SetRotation(rot);
|
||||
|
||||
this->SetTransform(t.GetMatrix());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
132
src/HEP/Geant/Solid.h
Normal file
132
src/HEP/Geant/Solid.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*//////////////////////////////////////////////////////////////////////////////
|
||||
// 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 SOLID_H
|
||||
#define SOLID_H
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Geant/Matter.h"
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
|
||||
class G4Material;
|
||||
class G4LogicalVolume;
|
||||
class G4TessellatedSolid;
|
||||
class G4Box;
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
class Solid : public Object {
|
||||
public:
|
||||
|
||||
virtual const char* GetClassName() const override { return "Geant.Solid"; }
|
||||
|
||||
Solid();
|
||||
Solid(const char *name);
|
||||
virtual ~Solid();
|
||||
|
||||
void SetNistMaterial(const char *name);
|
||||
void SetMaterial(G4Material *material);
|
||||
void SetSizeUnit(const char *unit);
|
||||
// Implementiamo SetParent qui, per tutti.
|
||||
virtual void SetParent(Solid *parent);
|
||||
|
||||
// Setters per la posizione (necessari per il piazzamento)
|
||||
void SetTransform(Matrix4f transform);
|
||||
|
||||
uLibGetMacro(Material, G4Material *)
|
||||
uLibGetSetMacro(Logical, G4LogicalVolume *)
|
||||
uLibGetSetMacro(Physical, G4VPhysicalVolume *)
|
||||
|
||||
virtual G4VSolid* GetG4Solid() const { return nullptr; }
|
||||
|
||||
inline const char *GetName() const {
|
||||
return m_Logical ? m_Logical->GetName().c_str() : m_Name.c_str();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
std::string m_Name;
|
||||
G4Material *m_Material;
|
||||
G4LogicalVolume *m_Logical;
|
||||
G4VPhysicalVolume *m_Physical; // <-- Memorizza l'oggetto posizionato
|
||||
G4ThreeVector *m_Position; // <-- Offset rispetto al centro del padre
|
||||
G4RotationMatrix* m_Rotation; // <-- Rotazione rispetto al padre
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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 *)
|
||||
virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; }
|
||||
|
||||
public slots:
|
||||
void Update();
|
||||
|
||||
private :
|
||||
G4TessellatedSolid *m_Solid;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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; }
|
||||
|
||||
ContainerBox* GetObject() const { return m_Object; }
|
||||
|
||||
public slots:
|
||||
void Update();
|
||||
|
||||
private:
|
||||
ContainerBox *m_Object;
|
||||
G4Box *m_Solid;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif // SOLID_H
|
||||
125
src/HEP/Geant/SteppingAction.cpp
Normal file
125
src/HEP/Geant/SteppingAction.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include "SteppingAction.hh"
|
||||
#include <Geant4/G4Step.hh>
|
||||
#include <Geant4/G4Track.hh>
|
||||
#include <Geant4/G4Event.hh>
|
||||
#include <Geant4/G4SystemOfUnits.hh>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <cmath>
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
SteppingAction::SteppingAction(SimulationContext *context)
|
||||
: G4UserSteppingAction(),
|
||||
G4UserEventAction(),
|
||||
m_Context(context),
|
||||
m_Verbosity(0)
|
||||
{}
|
||||
|
||||
SteppingAction::~SteppingAction() {}
|
||||
|
||||
void SteppingAction::BeginOfEventAction(const G4Event *event) {
|
||||
if (!event || !m_Context) return;
|
||||
|
||||
if (m_Context->mode == SimulationMode::DETAILED) {
|
||||
m_CurrentGeant = GeantEvent();
|
||||
m_CurrentGeant.m_EventID = static_cast<Id_t>(event->GetEventID());
|
||||
|
||||
if (event->GetNumberOfPrimaryVertex() > 0) {
|
||||
G4PrimaryVertex *vtx = event->GetPrimaryVertex(0);
|
||||
G4ThreeVector pos = vtx->GetPosition();
|
||||
m_CurrentGeant.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z());
|
||||
|
||||
if (vtx->GetNumberOfParticle() > 0) {
|
||||
G4PrimaryParticle *prim = vtx->GetPrimary(0);
|
||||
G4ThreeVector mom = prim->GetMomentumDirection();
|
||||
m_CurrentGeant.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z());
|
||||
m_CurrentGeant.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Detector mode
|
||||
m_MuonCrossCount = 0;
|
||||
float nan = std::numeric_limits<float>::quiet_NaN();
|
||||
m_CurrentMuon.LineIn().origin = HPoint3f(nan, nan, nan);
|
||||
m_CurrentMuon.LineIn().direction = HVector3f(nan, nan, nan);
|
||||
m_CurrentMuon.LineOut().origin = HPoint3f(nan, nan, nan);
|
||||
m_CurrentMuon.LineOut().direction = HVector3f(nan, nan, nan);
|
||||
m_CurrentMuon.Momentum() = nan;
|
||||
}
|
||||
}
|
||||
|
||||
void SteppingAction::EndOfEventAction(const G4Event *event) {
|
||||
if (!m_Context) return;
|
||||
|
||||
if (m_Context->mode == SimulationMode::DETAILED && m_Context->outputGeant) {
|
||||
if (!m_CurrentGeant.m_Path.empty()) {
|
||||
std::lock_guard<std::mutex> lock(m_Context->outputMutex);
|
||||
m_Context->outputGeant->push_back(m_CurrentGeant);
|
||||
}
|
||||
} else if (m_Context->mode == SimulationMode::DETECTOR && m_Context->outputMuon) {
|
||||
// In detector mode, we always push the event (to keep indexing consistent)
|
||||
// or only if hit? User requested "all muon event line in and out ar at first set to nan".
|
||||
// So we push everything.
|
||||
std::lock_guard<std::mutex> lock(m_Context->outputMutex);
|
||||
m_Context->outputMuon->push_back(m_CurrentMuon);
|
||||
}
|
||||
}
|
||||
|
||||
void SteppingAction::UserSteppingAction(const G4Step *step) {
|
||||
if (!step || !m_Context) return;
|
||||
|
||||
const G4Track *track = step->GetTrack();
|
||||
if (!track || track->GetParentID() != 0) return;
|
||||
|
||||
if (m_Context->mode == SimulationMode::DETAILED) {
|
||||
GeantEvent::Delta delta;
|
||||
delta.m_Length = static_cast<Scalarf>(step->GetStepLength() / mm);
|
||||
delta.m_Momentum = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
|
||||
G4ThreeVector dir = track->GetMomentumDirection();
|
||||
delta.m_Direction = HVector3f(dir.x(), dir.y(), dir.z());
|
||||
|
||||
if (track->GetVolume()) {
|
||||
delta.m_SolidName = track->GetVolume()->GetName();
|
||||
}
|
||||
m_CurrentGeant.m_Path.push_back(delta);
|
||||
} else {
|
||||
// Detector Mode
|
||||
if (std::isnan(m_CurrentMuon.Momentum())) {
|
||||
m_CurrentMuon.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
|
||||
}
|
||||
|
||||
G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
|
||||
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
|
||||
G4ThreeVector dir_g4 = track->GetMomentumDirection();
|
||||
|
||||
HPoint3f p1f(p1.x(), p1.y(), p1.z());
|
||||
HPoint3f p2f(p2.x(), p2.y(), p2.z());
|
||||
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
|
||||
|
||||
for (const auto& plane : m_Context->detectorPlanes) {
|
||||
float d1 = (p1f - plane.origin).dot(plane.direction);
|
||||
float d2 = (p2f - plane.origin).dot(plane.direction);
|
||||
|
||||
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
|
||||
float t = d1 / (d1 - d2);
|
||||
HPoint3f intersection = p1f + t * (p2f - p1f);
|
||||
|
||||
if (m_MuonCrossCount == 0) {
|
||||
m_CurrentMuon.LineIn().origin = intersection;
|
||||
m_CurrentMuon.LineIn().direction = dirf;
|
||||
m_MuonCrossCount++;
|
||||
} else if (m_MuonCrossCount == 1) {
|
||||
m_CurrentMuon.LineOut().origin = intersection;
|
||||
m_CurrentMuon.LineOut().direction = dirf;
|
||||
m_MuonCrossCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
37
src/HEP/Geant/SteppingAction.hh
Normal file
37
src/HEP/Geant/SteppingAction.hh
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef U_GEANT_STEPPINGACTION_HH
|
||||
#define U_GEANT_STEPPINGACTION_HH
|
||||
|
||||
#include "G4UserSteppingAction.hh"
|
||||
#include "G4UserEventAction.hh"
|
||||
#include "Core/Vector.h"
|
||||
#include "GeantEvent.h"
|
||||
#include "HEP/Detectors/MuonEvent.h"
|
||||
#include "SimulationContext.h"
|
||||
|
||||
namespace uLib {
|
||||
namespace Geant {
|
||||
|
||||
/// SteppingAction collects scattering data at each Geant4 step.
|
||||
class SteppingAction : public G4UserSteppingAction, public G4UserEventAction {
|
||||
public:
|
||||
SteppingAction(SimulationContext *context);
|
||||
virtual ~SteppingAction();
|
||||
|
||||
virtual void UserSteppingAction(const G4Step *step) override;
|
||||
virtual void BeginOfEventAction(const G4Event *event) override;
|
||||
virtual void EndOfEventAction(const G4Event *event) override;
|
||||
|
||||
void SetVerbosity(int level) { m_Verbosity = level; }
|
||||
|
||||
private:
|
||||
SimulationContext *m_Context;
|
||||
GeantEvent m_CurrentGeant;
|
||||
MuonEvent m_CurrentMuon;
|
||||
int m_MuonCrossCount = 0;
|
||||
int m_Verbosity = 0;
|
||||
};
|
||||
|
||||
} // namespace Geant
|
||||
} // namespace uLib
|
||||
|
||||
#endif
|
||||
23
src/HEP/Geant/testing/ActionInitialization.cpp
Normal file
23
src/HEP/Geant/testing/ActionInitialization.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "HEP/Geant/ActionInitialization.hh" // Il file appena creato
|
||||
#include "G4RunManagerFactory.hh" // Per il RunManager moderno
|
||||
// ... altri include (DetectorConstruction, PhysicsList, ecc.)
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Creazione del Run Manager
|
||||
auto *runManager = G4RunManagerFactory::CreateRunManager();
|
||||
|
||||
// 1. Inizializzazione della Geometria
|
||||
// runManager->SetUserInitialization(new DetectorConstruction());
|
||||
|
||||
// 2. Inizializzazione della Fisica
|
||||
// runManager->SetUserInitialization(new PhysicsList());
|
||||
|
||||
// 3. INIZIALIZZAZIONE DELLE AZIONI (Il nostro generatore!)
|
||||
runManager->SetUserInitialization(new uLib::Geant::ActionInitialization(nullptr, nullptr));
|
||||
|
||||
// ... Inizializzazione del kernel ( runManager->Initialize(); ), UI manager,
|
||||
// vis manager, ecc.
|
||||
|
||||
delete runManager;
|
||||
return 0;
|
||||
}
|
||||
16
src/HEP/Geant/testing/CMakeLists.txt
Normal file
16
src/HEP/Geant/testing/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
# TESTS
|
||||
set(TESTS
|
||||
SolidTest
|
||||
EventTest
|
||||
GeantApp
|
||||
ActionInitialization
|
||||
SkyPlaneEmitterTest
|
||||
)
|
||||
|
||||
set(LIBRARIES
|
||||
${PACKAGE_LIBPREFIX}Core
|
||||
${PACKAGE_LIBPREFIX}Math
|
||||
${PACKAGE_LIBPREFIX}Geant
|
||||
Eigen3::Eigen
|
||||
)
|
||||
uLib_add_tests(Geant)
|
||||
90
src/HEP/Geant/testing/EventTest.cpp
Normal file
90
src/HEP/Geant/testing/EventTest.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "Geant/Solid.h"
|
||||
#include "HEP/Geant/GeantEvent.h"
|
||||
#include "HEP/Geant/Scene.h"
|
||||
#include "HEP/Geant/EmitterPrimary.hh"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Units.h"
|
||||
#include "testing-prototype.h"
|
||||
#include <Geant4/G4Material.hh>
|
||||
#include <Geant4/G4NistManager.hh>
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
#include <Geant4/G4TessellatedSolid.hh>
|
||||
#include <string.h>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
int main() {
|
||||
BEGIN_TESTING(Geant Event);
|
||||
|
||||
// Test: Scene with iron cube in air, launch muons, collect events //
|
||||
{
|
||||
// 1. Create world box (air, 30m x 30m x 30m)
|
||||
Geant::Scene scene;
|
||||
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
|
||||
|
||||
// 2. Create iron cube (1m x 1m x 1m) at center
|
||||
ContainerBox iron_box(Vector3f(1000, 1000, 1000)); // mm
|
||||
Geant::BoxSolid *iron_cube = new Geant::BoxSolid("IronCube", &iron_box);
|
||||
iron_cube->SetNistMaterial("G4_Fe");
|
||||
iron_cube->Update(); // apply dimensions
|
||||
scene.AddSolid(iron_cube);
|
||||
|
||||
// 3. Set up emitter (default: mu- at 1 GeV, from z=+10m downward)
|
||||
Geant::EmitterPrimary *emitter = new Geant::EmitterPrimary();
|
||||
scene.SetEmitter(emitter);
|
||||
|
||||
// 4. Initialize Geant4
|
||||
scene.Initialize();
|
||||
|
||||
// 5. Run simulation: 10 muons
|
||||
int nEvents = 10;
|
||||
Vector<Geant::GeantEvent> results;
|
||||
scene.RunSimulation(nEvents, results);
|
||||
|
||||
// 6. Check results
|
||||
printf(" Collected %zu events\n", results.size());
|
||||
TEST1(results.size() > 0);
|
||||
|
||||
for (size_t i = 0; i < results.size(); ++i) {
|
||||
const Geant::GeantEvent &ev = results[i];
|
||||
bool hitIron = false;
|
||||
for (const auto &d : ev.Path()) {
|
||||
if (d.SolidName() == "IronCube") {
|
||||
hitIron = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf(" Event %d: momentum=%.1f MeV, path steps=%zu, hitIron=%s\n",
|
||||
ev.GetEventID(),
|
||||
ev.GetMomentum(),
|
||||
ev.Path().size(),
|
||||
hitIron ? "YES" : "NO");
|
||||
|
||||
// Each event should have at least one step
|
||||
TEST1(ev.Path().size() > 0);
|
||||
|
||||
// Print first few deltas
|
||||
const auto &path = ev.Path();
|
||||
for (size_t j = 0; j < path.size() && j < 5; ++j) {
|
||||
const auto &d = path[j];
|
||||
printf(" Delta[%zu]: solid=%s len=%.2f mm, p=%.1f MeV, "
|
||||
"dir=(%.3f, %.3f, %.3f)\n",
|
||||
j,
|
||||
d.SolidName().c_str(),
|
||||
d.GetLength(),
|
||||
d.GetMomentum(),
|
||||
d.Direction()(0),
|
||||
d.Direction()(1),
|
||||
d.Direction()(2));
|
||||
}
|
||||
if (path.size() > 5) {
|
||||
printf(" ... (%zu more deltas)\n", path.size() - 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
END_TESTING
|
||||
}
|
||||
16
src/HEP/Geant/testing/GeantApp.cpp
Normal file
16
src/HEP/Geant/testing/GeantApp.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "HEP/Geant/Scene.h"
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
int main() {
|
||||
|
||||
uLib::Geant::Scene scene;
|
||||
scene.ConstructWorldBox(Vector3f(100, 100, 100), "G4_AIR");
|
||||
scene.Initialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
104
src/HEP/Geant/testing/SkyPlaneEmitterTest.cpp
Normal file
104
src/HEP/Geant/testing/SkyPlaneEmitterTest.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "Geant/Solid.h"
|
||||
#include "HEP/Geant/GeantEvent.h"
|
||||
#include "HEP/Geant/Scene.h"
|
||||
#include "HEP/Geant/EmitterPrimary.hh"
|
||||
#include "Math/ContainerBox.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Units.h"
|
||||
#include "HEP/Detectors/DetectorChamber.h"
|
||||
|
||||
#include <Geant4/G4SystemOfUnits.hh>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
int nEvents = 10000;
|
||||
if (argc > 1) {
|
||||
nEvents = std::stoi(argv[1]);
|
||||
}
|
||||
|
||||
// 1. Setup Geant4 Scene
|
||||
Geant::Scene scene;
|
||||
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
|
||||
|
||||
ContainerBox iron_box;
|
||||
iron_box.Scale(Vector3f(18_m, 10_cm, 18_m));
|
||||
iron_box.SetPosition(Vector3f(-9_m, -5_cm, -9_m));
|
||||
Geant::BoxSolid* iron_cube = new Geant::BoxSolid("IronCube", &iron_box);
|
||||
iron_cube->SetNistMaterial("G4_Fe");
|
||||
iron_cube->Update();
|
||||
scene.AddSolid(iron_cube);
|
||||
|
||||
// Top Detector Chamber (along Y axis)
|
||||
DetectorChamber* top_chamber_box = new DetectorChamber();
|
||||
top_chamber_box->Scale(Vector3f(20_m, 40_cm, 20_m));
|
||||
top_chamber_box->Rotate(90_deg, Vector3f(1, 0, 0));
|
||||
top_chamber_box->SetPosition(Vector3f(-10_m, 12_m, -10_m));
|
||||
Geant::BoxSolid* top_chamber = new Geant::BoxSolid("TopChamber", top_chamber_box);
|
||||
top_chamber->SetNistMaterial("G4_AIR");
|
||||
top_chamber->Update();
|
||||
scene.AddSolid(top_chamber);
|
||||
|
||||
// Bottom Detector Chamber (along Y axis)
|
||||
DetectorChamber* bottom_chamber_box = new DetectorChamber();
|
||||
bottom_chamber_box->Scale(Vector3f(20_m, 40_cm, 20_m));
|
||||
bottom_chamber_box->Rotate(90_deg, Vector3f(1, 0, 0));
|
||||
bottom_chamber_box->SetPosition(Vector3f(-10_m, -12_m, -10_m));
|
||||
Geant::BoxSolid* bottom_chamber = new Geant::BoxSolid("BottomChamber", bottom_chamber_box);
|
||||
bottom_chamber->SetNistMaterial("G4_AIR");
|
||||
bottom_chamber->Update();
|
||||
scene.AddSolid(bottom_chamber);
|
||||
|
||||
// Setup SkyPlaneEmitterPrimary
|
||||
Geant::SkyPlaneEmitterPrimary* emitter = new Geant::SkyPlaneEmitterPrimary();
|
||||
emitter->SetPosition(Vector3f(0, 14.9_m, 0));
|
||||
emitter->Rotate(-90_deg, Vector3f(1, 0, 0));
|
||||
emitter->SetSkySize(Vector2f(20_m, 20_m));
|
||||
|
||||
scene.SetEmitter(emitter);
|
||||
scene.SetVerbosity(1);
|
||||
// scene.Initialize(); // Removed to avoid premature initialization
|
||||
|
||||
|
||||
|
||||
std::cout << "Starting simulation of " << nEvents << " events..." << std::endl;
|
||||
Vector<Geant::GeantEvent> results;
|
||||
scene.RunSimulation(nEvents, results);
|
||||
|
||||
std::cout << "Simulation finished. Collected " << results.size() << " events." << std::endl;
|
||||
|
||||
// Sample output to verify data collection
|
||||
if (!results.empty()) {
|
||||
std::cout << "Summary: " << std::endl;
|
||||
std::cout << " Total events generated: " << results.size() << std::endl;
|
||||
size_t total_steps = 0;
|
||||
for (const auto& event : results) {
|
||||
total_steps += event.Path().size();
|
||||
}
|
||||
std::cout << " Total simulation steps: " << total_steps << std::endl;
|
||||
std::cout << " Average steps per event: " << static_cast<double>(total_steps) / results.size() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\nStarting Detector Simulation of " << nEvents << " events..." << std::endl;
|
||||
Vector<MuonEvent> detectorResults;
|
||||
scene.RunDetectorSimulation(nEvents, detectorResults);
|
||||
|
||||
|
||||
|
||||
std::cout << "Detector Simulation finished." << std::endl;
|
||||
size_t hit_count = 0;
|
||||
for (const auto& ev : detectorResults) {
|
||||
if (!std::isnan(ev.LineIn().origin.x())) {
|
||||
hit_count++;
|
||||
}
|
||||
}
|
||||
std::cout << " Muons crossing at least one detector: " << hit_count << std::endl;
|
||||
if (nEvents > 0) {
|
||||
std::cout << " Efficiency: " << (100.0 * hit_count / nEvents) << "%" << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
src/HEP/Geant/testing/SolidTest.cpp
Normal file
73
src/HEP/Geant/testing/SolidTest.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "Geant/Solid.h"
|
||||
#include "Math/TriangleMesh.h"
|
||||
#include "testing-prototype.h"
|
||||
#include <Geant4/G4Material.hh>
|
||||
#include <Geant4/G4NistManager.hh>
|
||||
#include <Geant4/G4LogicalVolume.hh>
|
||||
#include <Geant4/G4TessellatedSolid.hh>
|
||||
#include <string.h>
|
||||
|
||||
using namespace uLib;
|
||||
|
||||
int main() {
|
||||
BEGIN_TESTING(Geant Solid);
|
||||
|
||||
// Test Solid initialization and NIST material //
|
||||
{
|
||||
Geant::Solid solid("test_solid");
|
||||
// Logical volume is not created until material and solid are set
|
||||
TEST1(solid.GetLogical() == nullptr);
|
||||
|
||||
solid.SetNistMaterial("G4_AIR");
|
||||
// Still null because base Solid has no GetG4Solid()
|
||||
TEST1(solid.GetLogical() == nullptr);
|
||||
TEST1(solid.GetMaterial() != nullptr);
|
||||
TEST1(solid.GetMaterial()->GetName() == "G4_AIR");
|
||||
}
|
||||
|
||||
// Test TessellatedSolid with a simple mesh //
|
||||
{
|
||||
Geant::TessellatedSolid tsolid("test_tessellated");
|
||||
tsolid.SetNistMaterial("G4_AIR");
|
||||
TEST1(tsolid.GetLogical() != nullptr);
|
||||
TEST1(tsolid.GetSolid() != nullptr);
|
||||
|
||||
// cube mesh //
|
||||
TriangleMesh mesh;
|
||||
mesh.AddPoint(Vector3f(0,0,0));
|
||||
mesh.AddPoint(Vector3f(1,0,0));
|
||||
mesh.AddPoint(Vector3f(0,1,0));
|
||||
mesh.AddPoint(Vector3f(1,1,0));
|
||||
mesh.AddPoint(Vector3f(0,0,1));
|
||||
mesh.AddPoint(Vector3f(1,0,1));
|
||||
mesh.AddPoint(Vector3f(0,1,1));
|
||||
mesh.AddPoint(Vector3f(1,1,1));
|
||||
|
||||
// create triangles (consistent outward winding) //
|
||||
// bottom (z=0)
|
||||
mesh.AddTriangle(Vector3i(0,2,3));
|
||||
mesh.AddTriangle(Vector3i(0,3,1));
|
||||
// top (z=1)
|
||||
mesh.AddTriangle(Vector3i(4,5,7));
|
||||
mesh.AddTriangle(Vector3i(4,7,6));
|
||||
// left (x=0)
|
||||
mesh.AddTriangle(Vector3i(0,4,6));
|
||||
mesh.AddTriangle(Vector3i(0,6,2));
|
||||
// right (x=1)
|
||||
mesh.AddTriangle(Vector3i(1,3,7));
|
||||
mesh.AddTriangle(Vector3i(1,7,5));
|
||||
// front (y=0)
|
||||
mesh.AddTriangle(Vector3i(0,1,5));
|
||||
mesh.AddTriangle(Vector3i(0,5,4));
|
||||
// back (y=1)
|
||||
mesh.AddTriangle(Vector3i(2,6,7));
|
||||
mesh.AddTriangle(Vector3i(2,7,3));
|
||||
|
||||
|
||||
|
||||
tsolid.SetMesh(mesh);
|
||||
TEST1(tsolid.GetSolid()->GetNumberOfFacets() == 12);
|
||||
}
|
||||
|
||||
END_TESTING
|
||||
}
|
||||
@@ -25,24 +25,13 @@
|
||||
|
||||
|
||||
|
||||
#ifndef U_CHAMBERDETECTOR_H
|
||||
#define U_CHAMBERDETECTOR_H
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Core/Types.h"
|
||||
#include "Math/ContainerBox.h"
|
||||
#define BEGIN_TESTING(name) \
|
||||
static int _fail = 0; \
|
||||
printf("..:: Testing " #name " ::..\n");
|
||||
|
||||
namespace uLib {
|
||||
#define TEST1(val) _fail += (val)==0
|
||||
#define TEST0(val) _fail += (val)!=0
|
||||
#define END_TESTING return _fail;
|
||||
|
||||
|
||||
class DetectorChamber : public ContainerBox {
|
||||
|
||||
public:
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // CHAMBERDETECTOR_H
|
||||
@@ -39,12 +39,6 @@ class Hit {
|
||||
Type m_DriftTime;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class HitMC {
|
||||
public:
|
||||
virtual Id_t GetChamber() = 0;
|
||||
373
src/Math/CLHEP_defs.hh
Normal file
373
src/Math/CLHEP_defs.hh
Normal file
@@ -0,0 +1,373 @@
|
||||
#if !defined(HAVE_GEANT4) && !defined(HEP_SYSTEM_OF_UNITS_H) && !defined(HEP_PHYSICAL_CONSTANTS_H)
|
||||
#ifndef CLHEP_defs_h
|
||||
#define CLHEP_defs_h
|
||||
|
||||
|
||||
|
||||
namespace CLHEP {
|
||||
|
||||
|
||||
// UNITS //
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double pi = 3.14159265358979323846;
|
||||
static constexpr double twopi = 2*pi;
|
||||
static constexpr double halfpi = pi/2;
|
||||
static constexpr double pi2 = pi*pi;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double millimeter = 1.;
|
||||
static constexpr double millimeter2 = millimeter*millimeter;
|
||||
static constexpr double millimeter3 = millimeter*millimeter*millimeter;
|
||||
|
||||
static constexpr double centimeter = 10.*millimeter;
|
||||
static constexpr double centimeter2 = centimeter*centimeter;
|
||||
static constexpr double centimeter3 = centimeter*centimeter*centimeter;
|
||||
|
||||
static constexpr double meter = 1000.*millimeter;
|
||||
static constexpr double meter2 = meter*meter;
|
||||
static constexpr double meter3 = meter*meter*meter;
|
||||
|
||||
static constexpr double kilometer = 1000.*meter;
|
||||
static constexpr double kilometer2 = kilometer*kilometer;
|
||||
static constexpr double kilometer3 = kilometer*kilometer*kilometer;
|
||||
|
||||
static constexpr double parsec = 3.0856775807e+16*meter;
|
||||
|
||||
static constexpr double micrometer = 1.e-6 *meter;
|
||||
static constexpr double nanometer = 1.e-9 *meter;
|
||||
static constexpr double angstrom = 1.e-10*meter;
|
||||
static constexpr double fermi = 1.e-15*meter;
|
||||
|
||||
static constexpr double barn = 1.e-28*meter2;
|
||||
static constexpr double millibarn = 1.e-3 *barn;
|
||||
static constexpr double microbarn = 1.e-6 *barn;
|
||||
static constexpr double nanobarn = 1.e-9 *barn;
|
||||
static constexpr double picobarn = 1.e-12*barn;
|
||||
|
||||
// symbols
|
||||
static constexpr double nm = nanometer;
|
||||
static constexpr double um = micrometer;
|
||||
|
||||
static constexpr double mm = millimeter;
|
||||
static constexpr double mm2 = millimeter2;
|
||||
static constexpr double mm3 = millimeter3;
|
||||
|
||||
static constexpr double cm = centimeter;
|
||||
static constexpr double cm2 = centimeter2;
|
||||
static constexpr double cm3 = centimeter3;
|
||||
|
||||
static constexpr double liter = 1.e+3*cm3;
|
||||
static constexpr double L = liter;
|
||||
static constexpr double dL = 1.e-1*liter;
|
||||
static constexpr double cL = 1.e-2*liter;
|
||||
static constexpr double mL = 1.e-3*liter;
|
||||
|
||||
static constexpr double m = meter;
|
||||
static constexpr double m2 = meter2;
|
||||
static constexpr double m3 = meter3;
|
||||
|
||||
static constexpr double km = kilometer;
|
||||
static constexpr double km2 = kilometer2;
|
||||
static constexpr double km3 = kilometer3;
|
||||
|
||||
static constexpr double pc = parsec;
|
||||
|
||||
//
|
||||
// Angle
|
||||
//
|
||||
static constexpr double radian = 1.;
|
||||
static constexpr double milliradian = 1.e-3*radian;
|
||||
static constexpr double degree = (pi/180.0)*radian;
|
||||
|
||||
static constexpr double steradian = 1.;
|
||||
|
||||
// symbols
|
||||
static constexpr double rad = radian;
|
||||
static constexpr double mrad = milliradian;
|
||||
static constexpr double sr = steradian;
|
||||
static constexpr double deg = degree;
|
||||
|
||||
//
|
||||
// Time [T]
|
||||
//
|
||||
static constexpr double nanosecond = 1.;
|
||||
static constexpr double second = 1.e+9 *nanosecond;
|
||||
static constexpr double millisecond = 1.e-3 *second;
|
||||
static constexpr double microsecond = 1.e-6 *second;
|
||||
static constexpr double picosecond = 1.e-12*second;
|
||||
|
||||
static constexpr double minute = 60*second;
|
||||
static constexpr double hour = 60*minute;
|
||||
static constexpr double day = 24*hour;
|
||||
static constexpr double year = 365*day;
|
||||
|
||||
static constexpr double hertz = 1./second;
|
||||
static constexpr double kilohertz = 1.e+3*hertz;
|
||||
static constexpr double megahertz = 1.e+6*hertz;
|
||||
|
||||
// symbols
|
||||
static constexpr double ns = nanosecond;
|
||||
static constexpr double s = second;
|
||||
static constexpr double ms = millisecond;
|
||||
static constexpr double us = microsecond;
|
||||
static constexpr double ps = picosecond;
|
||||
|
||||
//
|
||||
// Electric charge [Q]
|
||||
//
|
||||
static constexpr double eplus = 1. ;// positron charge
|
||||
static constexpr double e_SI = 1.602176634e-19;// positron charge in coulomb
|
||||
static constexpr double coulomb = eplus/e_SI;// coulomb = 6.24150 e+18 * eplus
|
||||
|
||||
//
|
||||
// Energy [E]
|
||||
//
|
||||
static constexpr double megaelectronvolt = 1. ;
|
||||
static constexpr double electronvolt = 1.e-6*megaelectronvolt;
|
||||
static constexpr double kiloelectronvolt = 1.e-3*megaelectronvolt;
|
||||
static constexpr double gigaelectronvolt = 1.e+3*megaelectronvolt;
|
||||
static constexpr double teraelectronvolt = 1.e+6*megaelectronvolt;
|
||||
static constexpr double petaelectronvolt = 1.e+9*megaelectronvolt;
|
||||
static constexpr double millielectronvolt = 1.e-9*megaelectronvolt;
|
||||
|
||||
static constexpr double joule = electronvolt/e_SI;// joule = 6.24150 e+12 * MeV
|
||||
|
||||
// symbols
|
||||
static constexpr double MeV = megaelectronvolt;
|
||||
static constexpr double eV = electronvolt;
|
||||
static constexpr double keV = kiloelectronvolt;
|
||||
static constexpr double GeV = gigaelectronvolt;
|
||||
static constexpr double TeV = teraelectronvolt;
|
||||
static constexpr double PeV = petaelectronvolt;
|
||||
|
||||
//
|
||||
// Mass [E][T^2][L^-2]
|
||||
//
|
||||
static constexpr double kilogram = joule*second*second/(meter*meter);
|
||||
static constexpr double gram = 1.e-3*kilogram;
|
||||
static constexpr double milligram = 1.e-3*gram;
|
||||
|
||||
// symbols
|
||||
static constexpr double kg = kilogram;
|
||||
static constexpr double g = gram;
|
||||
static constexpr double mg = milligram;
|
||||
|
||||
//
|
||||
// Power [E][T^-1]
|
||||
//
|
||||
static constexpr double watt = joule/second;// watt = 6.24150 e+3 * MeV/ns
|
||||
|
||||
//
|
||||
// Force [E][L^-1]
|
||||
//
|
||||
static constexpr double newton = joule/meter;// newton = 6.24150 e+9 * MeV/mm
|
||||
|
||||
//
|
||||
// Pressure [E][L^-3]
|
||||
//
|
||||
#define pascal hep_pascal // a trick to avoid warnings
|
||||
static constexpr double hep_pascal = newton/m2; // pascal = 6.24150 e+3 * MeV/mm3
|
||||
static constexpr double bar = 100000*pascal; // bar = 6.24150 e+8 * MeV/mm3
|
||||
static constexpr double atmosphere = 101325*pascal; // atm = 6.32420 e+8 * MeV/mm3
|
||||
|
||||
//
|
||||
// Electric current [Q][T^-1]
|
||||
//
|
||||
static constexpr double ampere = coulomb/second; // ampere = 6.24150 e+9 * eplus/ns
|
||||
static constexpr double milliampere = 1.e-3*ampere;
|
||||
static constexpr double microampere = 1.e-6*ampere;
|
||||
static constexpr double nanoampere = 1.e-9*ampere;
|
||||
|
||||
//
|
||||
// Electric potential [E][Q^-1]
|
||||
//
|
||||
static constexpr double megavolt = megaelectronvolt/eplus;
|
||||
static constexpr double kilovolt = 1.e-3*megavolt;
|
||||
static constexpr double volt = 1.e-6*megavolt;
|
||||
|
||||
//
|
||||
// Electric resistance [E][T][Q^-2]
|
||||
//
|
||||
static constexpr double ohm = volt/ampere;// ohm = 1.60217e-16*(MeV/eplus)/(eplus/ns)
|
||||
|
||||
//
|
||||
// Electric capacitance [Q^2][E^-1]
|
||||
//
|
||||
static constexpr double farad = coulomb/volt;// farad = 6.24150e+24 * eplus/Megavolt
|
||||
static constexpr double millifarad = 1.e-3*farad;
|
||||
static constexpr double microfarad = 1.e-6*farad;
|
||||
static constexpr double nanofarad = 1.e-9*farad;
|
||||
static constexpr double picofarad = 1.e-12*farad;
|
||||
|
||||
//
|
||||
// Magnetic Flux [T][E][Q^-1]
|
||||
//
|
||||
static constexpr double weber = volt*second;// weber = 1000*megavolt*ns
|
||||
|
||||
//
|
||||
// Magnetic Field [T][E][Q^-1][L^-2]
|
||||
//
|
||||
static constexpr double tesla = volt*second/meter2;// tesla =0.001*megavolt*ns/mm2
|
||||
|
||||
static constexpr double gauss = 1.e-4*tesla;
|
||||
static constexpr double kilogauss = 1.e-1*tesla;
|
||||
|
||||
//
|
||||
// Inductance [T^2][E][Q^-2]
|
||||
//
|
||||
static constexpr double henry = weber/ampere;// henry = 1.60217e-7*MeV*(ns/eplus)**2
|
||||
|
||||
//
|
||||
// Temperature
|
||||
//
|
||||
static constexpr double kelvin = 1.;
|
||||
|
||||
//
|
||||
// Amount of substance
|
||||
//
|
||||
static constexpr double mole = 1.;
|
||||
|
||||
//
|
||||
// Activity [T^-1]
|
||||
//
|
||||
static constexpr double becquerel = 1./second ;
|
||||
static constexpr double curie = 3.7e+10 * becquerel;
|
||||
static constexpr double kilobecquerel = 1.e+3*becquerel;
|
||||
static constexpr double megabecquerel = 1.e+6*becquerel;
|
||||
static constexpr double gigabecquerel = 1.e+9*becquerel;
|
||||
static constexpr double millicurie = 1.e-3*curie;
|
||||
static constexpr double microcurie = 1.e-6*curie;
|
||||
static constexpr double Bq = becquerel;
|
||||
static constexpr double kBq = kilobecquerel;
|
||||
static constexpr double MBq = megabecquerel;
|
||||
static constexpr double GBq = gigabecquerel;
|
||||
static constexpr double Ci = curie;
|
||||
static constexpr double mCi = millicurie;
|
||||
static constexpr double uCi = microcurie;
|
||||
|
||||
//
|
||||
// Absorbed dose [L^2][T^-2]
|
||||
//
|
||||
static constexpr double gray = joule/kilogram ;
|
||||
static constexpr double kilogray = 1.e+3*gray;
|
||||
static constexpr double milligray = 1.e-3*gray;
|
||||
static constexpr double microgray = 1.e-6*gray;
|
||||
|
||||
//
|
||||
// Luminous intensity [I]
|
||||
//
|
||||
static constexpr double candela = 1.;
|
||||
|
||||
//
|
||||
// Luminous flux [I]
|
||||
//
|
||||
static constexpr double lumen = candela*steradian;
|
||||
|
||||
//
|
||||
// Illuminance [I][L^-2]
|
||||
//
|
||||
static constexpr double lux = lumen/meter2;
|
||||
|
||||
//
|
||||
// Miscellaneous
|
||||
//
|
||||
static constexpr double perCent = 0.01 ;
|
||||
static constexpr double perThousand = 0.001;
|
||||
static constexpr double perMillion = 0.000001;
|
||||
|
||||
|
||||
|
||||
// CONSTANTS //
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double Avogadro = 6.02214076e+23/mole;
|
||||
|
||||
//
|
||||
// c = 299.792458 mm/ns
|
||||
// c^2 = 898.7404 (mm/ns)^2
|
||||
//
|
||||
static constexpr double c_light = 2.99792458e+8 * m/s;
|
||||
static constexpr double c_squared = c_light * c_light;
|
||||
|
||||
//
|
||||
// h = 4.13566e-12 MeV*ns
|
||||
// hbar = 6.58212e-13 MeV*ns
|
||||
// hbarc = 197.32705e-12 MeV*mm
|
||||
//
|
||||
static constexpr double h_Planck = 6.62607015e-34 * joule*s;
|
||||
static constexpr double hbar_Planck = h_Planck/twopi;
|
||||
static constexpr double hbarc = hbar_Planck * c_light;
|
||||
static constexpr double hbarc_squared = hbarc * hbarc;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double electron_charge = - eplus; // see SystemOfUnits.h
|
||||
static constexpr double e_squared = eplus * eplus;
|
||||
|
||||
//
|
||||
// amu_c2 - atomic equivalent mass unit
|
||||
// - AKA, unified atomic mass unit (u)
|
||||
// amu - atomic mass unit
|
||||
//
|
||||
static constexpr double electron_mass_c2 = 0.510998910 * MeV;
|
||||
static constexpr double proton_mass_c2 = 938.272013 * MeV;
|
||||
static constexpr double neutron_mass_c2 = 939.56536 * MeV;
|
||||
static constexpr double amu_c2 = 931.494028 * MeV;
|
||||
static constexpr double amu = amu_c2/c_squared;
|
||||
|
||||
//
|
||||
// permeability of free space mu0 = 2.01334e-16 Mev*(ns*eplus)^2/mm
|
||||
// permittivity of free space epsil0 = 5.52636e+10 eplus^2/(MeV*mm)
|
||||
//
|
||||
static constexpr double mu0 = 4*pi*1.e-7 * henry/m;
|
||||
static constexpr double epsilon0 = 1./(c_squared*mu0);
|
||||
|
||||
//
|
||||
// electromagnetic coupling = 1.43996e-12 MeV*mm/(eplus^2)
|
||||
//
|
||||
static constexpr double elm_coupling = e_squared/(4*pi*epsilon0);
|
||||
static constexpr double fine_structure_const = elm_coupling/hbarc;
|
||||
static constexpr double classic_electr_radius = elm_coupling/electron_mass_c2;
|
||||
static constexpr double electron_Compton_length = hbarc/electron_mass_c2;
|
||||
static constexpr double Bohr_radius = electron_Compton_length/fine_structure_const;
|
||||
|
||||
static constexpr double alpha_rcl2 = fine_structure_const
|
||||
*classic_electr_radius
|
||||
*classic_electr_radius;
|
||||
|
||||
static constexpr double twopi_mc2_rcl2 = twopi*electron_mass_c2
|
||||
*classic_electr_radius
|
||||
*classic_electr_radius;
|
||||
|
||||
static constexpr double Bohr_magneton = (eplus*hbarc*c_light)/(2*electron_mass_c2);
|
||||
static constexpr double nuclear_magneton = (eplus*hbarc*c_light)/(2*proton_mass_c2);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double k_Boltzmann = 8.617333e-11 * MeV/kelvin;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double STP_Temperature = 273.15*kelvin;
|
||||
static constexpr double STP_Pressure = 1.*atmosphere;
|
||||
static constexpr double kGasThreshold = 10.*mg/cm3;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
static constexpr double universe_mean_density = 1.e-25*g/cm3;
|
||||
|
||||
} // namespace CLHEP
|
||||
|
||||
#endif // CLHEP_defs_h
|
||||
#endif // HAVE_GEANT4 / CLHEP checks
|
||||
|
||||
@@ -19,20 +19,26 @@ set(HEADERS ContainerBox.h
|
||||
VoxImageFilterCustom.hpp
|
||||
Accumulator.h
|
||||
TriangleMesh.h
|
||||
QuadMesh.h
|
||||
BitCode.h
|
||||
Structured2DGrid.h
|
||||
Structured4DGrid.h)
|
||||
Structured4DGrid.h
|
||||
Units.h
|
||||
CLHEP_defs.hh)
|
||||
|
||||
set(SOURCES VoxRaytracer.cpp
|
||||
StructuredData.cpp
|
||||
StructuredGrid.cpp
|
||||
VoxImage.cpp
|
||||
TriangleMesh.cpp
|
||||
QuadMesh.cpp
|
||||
Dense.cpp
|
||||
Structured2DGrid.cpp
|
||||
Structured4DGrid.cpp)
|
||||
Structured4DGrid.cpp
|
||||
MathRegistrations.cpp)
|
||||
|
||||
set(LIBRARIES Eigen3::Eigen
|
||||
set(LIBRARIES ${PACKAGE_LIBPREFIX}Core
|
||||
Eigen3::Eigen
|
||||
${ROOT_LIBRARIES}
|
||||
${VTK_LIBRARIES})
|
||||
|
||||
@@ -54,7 +60,7 @@ endif()
|
||||
|
||||
|
||||
install(TARGETS ${libname}
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
EXPORT "uLibTargets"
|
||||
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
|
||||
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
|
||||
|
||||
|
||||
@@ -23,76 +23,204 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
|
||||
|
||||
#ifndef U_CONTAINERBOX_H
|
||||
#define U_CONTAINERBOX_H
|
||||
|
||||
#include "Geometry.h"
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Core/Property.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Transform.h"
|
||||
#include <utility>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Represents an oriented bounding box (OBB) within a hierarchical
|
||||
* transformation system.
|
||||
*
|
||||
* ContainerBox inherits from AffineTransform, which defines its parent
|
||||
* coordinate system. It contains an internal local transformation (m_LocalT)
|
||||
* that defines the box's specific origin and size relative to its own
|
||||
* coordinate system.
|
||||
*/
|
||||
class ContainerBox : public AffineTransform, public Object {
|
||||
|
||||
typedef AffineTransform BaseClass;
|
||||
|
||||
class ContainerBox : public AffineTransform {
|
||||
public:
|
||||
ContainerBox() : m_LocalT(this) {}
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PROPERTIES //
|
||||
Property<float> Width;
|
||||
Property<float> Height;
|
||||
Property<float> Depth;
|
||||
|
||||
ContainerBox(const ContainerBox ©) :
|
||||
m_LocalT(this),
|
||||
AffineTransform(copy)
|
||||
{
|
||||
// FIX for performance //
|
||||
this->SetOrigin(copy.GetOrigin());
|
||||
this->SetSize(copy.GetSize());
|
||||
virtual const char * GetClassName() const { return "ContainerBox"; }
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
* Initializes the local transformation with this instance as its parent.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
inline void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
|
||||
/**
|
||||
* @brief Constructor with size.
|
||||
* @param size The size vector.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
inline Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param copy The ContainerBox instance to copy from.
|
||||
*/
|
||||
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<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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @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); }
|
||||
|
||||
/**
|
||||
* @brief Gets the box origin relative to its coordinate system.
|
||||
* @return The origin position vector.
|
||||
*/
|
||||
Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
|
||||
|
||||
/**
|
||||
* @brief Sets the size of the box.
|
||||
* Re-initializes the local transformation and applies the new scale.
|
||||
* @param v The size vector (width, height, depth).
|
||||
*/
|
||||
void SetSize(const Vector3f &v) {
|
||||
Width = v(0);
|
||||
Height = v(1);
|
||||
Depth = v(2);
|
||||
Vector3f pos = this->GetOrigin();
|
||||
m_LocalT = AffineTransform(this);
|
||||
m_LocalT = AffineTransform(this); // regenerate local transform
|
||||
m_LocalT.Scale(v);
|
||||
m_LocalT.SetPosition(pos);
|
||||
}
|
||||
|
||||
inline Vector3f GetSize() const { return m_LocalT.GetScale(); }
|
||||
/**
|
||||
* @brief Gets the current size (scale) of the box.
|
||||
* @return The size vector.
|
||||
*/
|
||||
Vector3f GetSize() const {
|
||||
Vector3f s = this->GetScale();
|
||||
Vector3f ls = m_LocalT.GetScale();
|
||||
return Vector3f(s(0) * ls(0), s(1) * ls(1), s(2) * ls(2));
|
||||
}
|
||||
|
||||
// FIX... //
|
||||
inline void FlipLocalAxes(int first, int second)
|
||||
{ m_LocalT.FlipAxes(first,second); }
|
||||
/**
|
||||
* @brief Swaps two local axes of the box.
|
||||
* @param first Index of the first axis (0=X, 1=Y, 2=Z).
|
||||
* @param second Index of the second axis (0=X, 1=Y, 2=Z).
|
||||
*/
|
||||
void FlipLocalAxes(int first, int second) {
|
||||
m_LocalT.FlipAxes(first, second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the world transformation matrix of the box's volume.
|
||||
* @return A 4x4 transformation matrix.
|
||||
*/
|
||||
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
|
||||
|
||||
inline Vector4f GetWorldPoint(const Vector4f &v) const {
|
||||
/**
|
||||
* @brief Returns the local transformation matrix of the box's volume.
|
||||
* @return A 4x4 transformation matrix.
|
||||
*/
|
||||
Matrix4f GetLocalMatrix() const { return m_LocalT.GetMatrix(); }
|
||||
|
||||
/**
|
||||
* @brief Transforms a point from box-local space to world space.
|
||||
* @param v The local point (4D homogeneous vector).
|
||||
* @return The transformed point in world space.
|
||||
*/
|
||||
Vector4f GetWorldPoint(const Vector4f &v) const {
|
||||
return m_LocalT.GetWorldMatrix() * v;
|
||||
}
|
||||
|
||||
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
||||
/**
|
||||
* @brief Transforms a point from box-local space coordinates to world space.
|
||||
* @param x X coordinate in local space.
|
||||
* @param y Y coordinate in local space.
|
||||
* @param z Z coordinate in local space.
|
||||
* @return The transformed point in world space.
|
||||
*/
|
||||
Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
||||
return this->GetWorldPoint(Vector4f(x, y, z, 1));
|
||||
}
|
||||
|
||||
inline Vector4f GetLocalPoint(const Vector4f &v) const {
|
||||
/**
|
||||
* @brief Transforms a point from world space to box-local space.
|
||||
* @param v The world point (4D homogeneous vector).
|
||||
* @return The transformed point in box-local space.
|
||||
*/
|
||||
Vector4f GetLocalPoint(const Vector4f &v) const {
|
||||
return m_LocalT.GetWorldMatrix().inverse() * v;
|
||||
}
|
||||
|
||||
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
||||
/**
|
||||
* @brief Transforms a point from world space coordinates to box-local space.
|
||||
* @param x X coordinate in world space.
|
||||
* @param y Y coordinate in world space.
|
||||
* @param z Z coordinate in world space.
|
||||
* @return The transformed point in box-local space.
|
||||
*/
|
||||
Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
||||
return this->GetLocalPoint(Vector4f(x, y, z, 1));
|
||||
}
|
||||
|
||||
/** Translate using transformation chain */
|
||||
using BaseClass::Translate;
|
||||
|
||||
protected:
|
||||
/** Rotate using transformation chain */
|
||||
using BaseClass::Rotate;
|
||||
|
||||
/** Scale using transformation chain */
|
||||
using BaseClass::Scale;
|
||||
|
||||
signals:
|
||||
|
||||
// signal to emit when the box is updated //
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(ContainerBox::Updated); }
|
||||
|
||||
private:
|
||||
void SyncSize() {
|
||||
this->SetSize(Vector3f(Width, Height, Depth));
|
||||
}
|
||||
|
||||
AffineTransform m_LocalT;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // CONTAINERBOX_H
|
||||
|
||||
144
src/Math/Cylinder.h
Normal file
144
src/Math/Cylinder.h
Normal 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.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
#ifndef U_CYLINDER_H
|
||||
#define U_CYLINDER_H
|
||||
|
||||
#include "Geometry.h"
|
||||
#include "Core/Object.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Transform.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Represents a cylindrical volume centered in the base circle.
|
||||
*
|
||||
* 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).
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
Cylinder() : m_LocalT(this), m_Radius(1.0), m_Height(1.0) {
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with radius and height.
|
||||
*/
|
||||
Cylinder(float radius, float height) : m_LocalT(this), m_Radius(radius), m_Height(height) {
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
Cylinder(const Cylinder ©)
|
||||
: m_LocalT(this), AffineTransform(copy) {
|
||||
this->SetRadius(copy.GetRadius());
|
||||
this->SetHeight(copy.GetHeight());
|
||||
}
|
||||
|
||||
/** Sets the radius of the cylinder */
|
||||
inline void SetRadius(float r) {
|
||||
m_Radius = r;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/** Gets the radius of the cylinder */
|
||||
inline float GetRadius() const { return m_Radius; }
|
||||
|
||||
/** Sets the height of the cylinder */
|
||||
inline void SetHeight(float h) {
|
||||
m_Height = h;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/** Gets the height of the cylinder */
|
||||
inline float GetHeight() const { return m_Height; }
|
||||
|
||||
/**
|
||||
* @brief Returns the world transformation matrix of the cylinder's volume.
|
||||
*/
|
||||
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the local transformation matrix of the cylinder's volume.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transforms a world point to cylindrical local space.
|
||||
* @return Vector3f(r, theta, z)
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
float m_Radius;
|
||||
float m_Height;
|
||||
AffineTransform m_LocalT;
|
||||
};
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CYLINDER_H
|
||||
@@ -51,6 +51,8 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include "Core/Types.h"
|
||||
#include "Core/Property.h"
|
||||
|
||||
//// BOOST SERIALIZATION ///////////////////////////////////////////////////////
|
||||
|
||||
@@ -107,7 +109,6 @@ std::ostream &operator<<(std::ostream &os,
|
||||
namespace uLib {
|
||||
|
||||
typedef id_t Id_t;
|
||||
|
||||
typedef int Scalari;
|
||||
typedef unsigned int Scalarui;
|
||||
typedef long Scalarl;
|
||||
@@ -257,6 +258,44 @@ inline std::ostream &operator<<(std::ostream &stream, const HError3f &err) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
typedef Property<Scalari> ScalariProperty;
|
||||
typedef Property<Scalarui> ScalaruiProperty;
|
||||
typedef Property<Scalarl> ScalarlProperty;
|
||||
typedef Property<Scalarul> ScalarulProperty;
|
||||
typedef Property<Scalarf> ScalarfProperty;
|
||||
typedef Property<Scalard> ScalardProperty;
|
||||
|
||||
typedef Property<Vector1i> Vector1iProperty;
|
||||
typedef Property<Vector1f> Vector1fProperty;
|
||||
typedef Property<Vector1d> Vector1dProperty;
|
||||
|
||||
typedef Property<Vector2i> Vector2iProperty;
|
||||
typedef Property<Vector3i> Vector3iProperty;
|
||||
typedef Property<Vector4i> Vector4iProperty;
|
||||
|
||||
typedef Property<Vector2f> Vector2fProperty;
|
||||
typedef Property<Vector3f> Vector3fProperty;
|
||||
typedef Property<Vector4f> Vector4fProperty;
|
||||
|
||||
typedef Property<Vector2d> Vector2dProperty;
|
||||
typedef Property<Vector3d> Vector3dProperty;
|
||||
typedef Property<Vector4d> Vector4dProperty;
|
||||
|
||||
typedef Property<Matrix2i> Matrix2iProperty;
|
||||
typedef Property<Matrix3i> Matrix3iProperty;
|
||||
typedef Property<Matrix4i> Matrix4iProperty;
|
||||
|
||||
typedef Property<Matrix2f> Matrix2fProperty;
|
||||
typedef Property<Matrix3f> Matrix3fProperty;
|
||||
typedef Property<Matrix4f> Matrix4fProperty;
|
||||
|
||||
typedef Property<Matrix2d> Matrix2dProperty;
|
||||
typedef Property<Matrix3d> Matrix3dProperty;
|
||||
typedef Property<Matrix4d> Matrix4dProperty;
|
||||
|
||||
typedef Property<HVector3f> HVector3fProperty;
|
||||
typedef Property<HPoint3f> HPoint3fProperty;
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -28,24 +28,39 @@
|
||||
#ifndef U_GEOMETRY_H
|
||||
#define U_GEOMETRY_H
|
||||
|
||||
#include "Core/Object.h"
|
||||
#include "Math/Dense.h"
|
||||
#include "Math/Transform.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace uLib {
|
||||
|
||||
class Geometry : public AffineTransform {
|
||||
class Geometry : public AffineTransform, public Object {
|
||||
public:
|
||||
|
||||
inline Vector4f GetWorldPoint(const Vector4f &v) const {
|
||||
return this->GetWorldMatrix() * v;
|
||||
virtual const char * GetClassName() const { return "Geometry"; }
|
||||
|
||||
virtual Vector3f ToLinear(const Vector3f& curved_space) const {
|
||||
return curved_space;
|
||||
}
|
||||
|
||||
virtual Vector3f FromLinear(const Vector3f& cartesian_space) const {
|
||||
return cartesian_space;
|
||||
}
|
||||
|
||||
inline Vector4f GetWorldPoint(const Vector4f v) const {
|
||||
Vector3f lin = ToLinear(Vector3f(v.x(), v.y(), v.z()));
|
||||
return this->GetWorldMatrix() * Vector4f(lin.x(), lin.y(), lin.z(), v.w());
|
||||
}
|
||||
|
||||
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
|
||||
return this->GetWorldPoint(Vector4f(x,y,z,1));
|
||||
}
|
||||
|
||||
inline Vector4f GetLocalPoint(const Vector4f &v) const {
|
||||
return this->GetWorldMatrix().inverse() * v;
|
||||
inline Vector4f GetLocalPoint(const Vector4f v) const {
|
||||
Vector4f loc_lin = this->GetWorldMatrix().inverse() * v;
|
||||
Vector3f curv = FromLinear(Vector3f(loc_lin.x(), loc_lin.y(), loc_lin.z()));
|
||||
return Vector4f(curv.x(), curv.y(), curv.z(), loc_lin.w());
|
||||
}
|
||||
|
||||
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
|
||||
@@ -53,6 +68,77 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class CylindricalGeometry : public Geometry {
|
||||
public:
|
||||
CylindricalGeometry() {}
|
||||
|
||||
Vector3f ToLinear(const Vector3f& cylindrical) const {
|
||||
return Vector3f(cylindrical.x() * std::cos(cylindrical.y()),
|
||||
cylindrical.x() * std::sin(cylindrical.y()),
|
||||
cylindrical.z());
|
||||
}
|
||||
|
||||
Vector3f FromLinear(const Vector3f& linear) const {
|
||||
float r = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y());
|
||||
float phi = std::atan2(linear.y(), linear.x());
|
||||
return Vector3f(r, phi, linear.z());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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();
|
||||
float phi = spherical.z();
|
||||
return Vector3f(r * std::sin(theta) * std::cos(phi),
|
||||
r * std::sin(theta) * std::sin(phi),
|
||||
r * std::cos(theta));
|
||||
}
|
||||
|
||||
Vector3f FromLinear(const Vector3f& linear) const {
|
||||
float r = linear.norm();
|
||||
float theta = (r == 0.0f) ? 0.0f : std::acos(linear.z() / r);
|
||||
float phi = std::atan2(linear.y(), linear.x());
|
||||
return Vector3f(r, theta, phi);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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();
|
||||
float phi = toroidal.z();
|
||||
return Vector3f((m_Rtor + r * std::cos(theta)) * std::cos(phi),
|
||||
(m_Rtor + r * std::cos(theta)) * std::sin(phi),
|
||||
r * std::sin(theta));
|
||||
}
|
||||
|
||||
Vector3f FromLinear(const Vector3f& linear) const {
|
||||
float phi = std::atan2(linear.y(), linear.x());
|
||||
float r_xy = std::sqrt(linear.x() * linear.x() + linear.y() * linear.y());
|
||||
float delta_r = r_xy - m_Rtor;
|
||||
float z = linear.z();
|
||||
float r = std::sqrt(delta_r * delta_r + z * z);
|
||||
float theta = std::atan2(z, delta_r);
|
||||
return Vector3f(r, theta, phi);
|
||||
}
|
||||
|
||||
private:
|
||||
float m_Rtor;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user