From 1e6e3ae4f4ddeaa112ed24a68a10f77916963aa5 Mon Sep 17 00:00:00 2001 From: AndreaRigoni Date: Thu, 19 Mar 2026 13:57:10 +0000 Subject: [PATCH] emitter representation --- src/HEP/Geant/EmitterPrimary.cpp | 21 +-- src/HEP/Geant/EmitterPrimary.hh | 11 +- src/Math/Transform.h | 1 + src/Vtk/HEP/Geant/CMakeLists.txt | 2 + src/Vtk/HEP/Geant/testing/CMakeLists.txt | 1 + .../Geant/testing/vtkEmitterPrimaryTest.cpp | 124 ++++++++++++++++++ src/Vtk/HEP/Geant/vtkEmitterPrimary.cpp | 72 ++++++++++ src/Vtk/HEP/Geant/vtkEmitterPrimary.h | 32 +++++ src/Vtk/uLibVtkInterface.cxx | 6 + 9 files changed, 261 insertions(+), 9 deletions(-) create mode 100644 src/Vtk/HEP/Geant/testing/vtkEmitterPrimaryTest.cpp create mode 100644 src/Vtk/HEP/Geant/vtkEmitterPrimary.cpp create mode 100644 src/Vtk/HEP/Geant/vtkEmitterPrimary.h diff --git a/src/HEP/Geant/EmitterPrimary.cpp b/src/HEP/Geant/EmitterPrimary.cpp index db22d62..aca33ad 100644 --- a/src/HEP/Geant/EmitterPrimary.cpp +++ b/src/HEP/Geant/EmitterPrimary.cpp @@ -29,14 +29,14 @@ EmitterPrimary::EmitterPrimary() // Configuriamo le proprietà iniziali della particella fParticleGun->SetParticleDefinition(particle); - // Impostiamo la direzione della quantità di moto (es. lungo l'asse Z) - fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0., 0., -1.)); - // Impostiamo l'energia cinetica a 1 GeV fParticleGun->SetParticleEnergy(1.0 * GeV); - // Impostiamo la posizione di partenza (origine) - fParticleGun->SetParticlePosition(G4ThreeVector(0., 0., 10. * m)); + // 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() { @@ -45,9 +45,14 @@ EmitterPrimary::~EmitterPrimary() { } void EmitterPrimary::GeneratePrimaries(G4Event *anEvent) { - // Questo metodo viene invocato all'inizio di ogni evento. - // Qui potresti anche aggiungere una randomizzazione della posizione o - // dell'energia. + // 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); } diff --git a/src/HEP/Geant/EmitterPrimary.hh b/src/HEP/Geant/EmitterPrimary.hh index 713a21c..f8d1fd3 100644 --- a/src/HEP/Geant/EmitterPrimary.hh +++ b/src/HEP/Geant/EmitterPrimary.hh @@ -4,7 +4,13 @@ #include "G4VUserPrimaryGeneratorAction.hh" #include "globals.hh" +namespace uLib { +class QuadMesh; +} + #include "Math/QuadMesh.h" +#include "Core/Object.h" +#include "Math/Transform.h" #include // Added for std::vector class G4ParticleGun; @@ -13,7 +19,7 @@ class G4Event; namespace uLib { namespace Geant { -class EmitterPrimary : public G4VUserPrimaryGeneratorAction +class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public Object, public AffineTransform { public: EmitterPrimary(); @@ -22,11 +28,14 @@ class EmitterPrimary : public G4VUserPrimaryGeneratorAction // Metodo principale chiamato all'inizio di ogni evento virtual void GeneratePrimaries(G4Event*); + virtual void Updated() override { ULIB_SIGNAL_EMIT(EmitterPrimary::Updated); } + protected: G4ParticleGun* fParticleGun; // Puntatore al cannone di particelle }; + class QuadMeshEmitterPrimary : public EmitterPrimary { public: diff --git a/src/Math/Transform.h b/src/Math/Transform.h index be6cdd9..c267581 100644 --- a/src/Math/Transform.h +++ b/src/Math/Transform.h @@ -50,6 +50,7 @@ #define U_TRANSFORM_H #include +#include "Math/Dense.h" namespace uLib { diff --git a/src/Vtk/HEP/Geant/CMakeLists.txt b/src/Vtk/HEP/Geant/CMakeLists.txt index 48228fa..3685437 100644 --- a/src/Vtk/HEP/Geant/CMakeLists.txt +++ b/src/Vtk/HEP/Geant/CMakeLists.txt @@ -5,10 +5,12 @@ set(HEP_GEANT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/vtkGeantEvent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/vtkEmitterPrimary.cpp PARENT_SCOPE) set(HEP_GEANT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/vtkGeantEvent.h + ${CMAKE_CURRENT_SOURCE_DIR}/vtkEmitterPrimary.h PARENT_SCOPE) if(BUILD_TESTING) diff --git a/src/Vtk/HEP/Geant/testing/CMakeLists.txt b/src/Vtk/HEP/Geant/testing/CMakeLists.txt index 8086ed4..6efcaaa 100644 --- a/src/Vtk/HEP/Geant/testing/CMakeLists.txt +++ b/src/Vtk/HEP/Geant/testing/CMakeLists.txt @@ -1,6 +1,7 @@ # TESTS set(TESTS vtkGeantEventTest + vtkEmitterPrimaryTest ) set(LIBRARIES diff --git a/src/Vtk/HEP/Geant/testing/vtkEmitterPrimaryTest.cpp b/src/Vtk/HEP/Geant/testing/vtkEmitterPrimaryTest.cpp new file mode 100644 index 0000000..f8e0651 --- /dev/null +++ b/src/Vtk/HEP/Geant/testing/vtkEmitterPrimaryTest.cpp @@ -0,0 +1,124 @@ +#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 "Vtk/uLibVtkViewer.h" +#include "Vtk/HEP/Geant/vtkGeantEvent.h" +#include "Vtk/HEP/Geant/vtkEmitterPrimary.h" +#include "Vtk/vtkContainerBox.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace uLib; + +struct AppState { + Geant::Scene* scene; + Vtk::Viewer* viewer; + Vector results; +}; + +void KeyPressCallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData) { + auto* interactor = static_cast(caller); + auto* state = static_cast(clientData); + + std::string key = interactor->GetKeySym(); + if (key == "Return") { + std::cout << "--> Firing muon from current emitter position..." << std::endl; + + // Run one event + state->scene->RunSimulation(1, state->results); + + if (!state->results.empty()) { + // Get the last event + Geant::GeantEvent* lastEvent = &state->results.back(); + std::cout << " Collected event " << lastEvent->GetEventID() + << " with " << lastEvent->Path().size() << " steps." << std::endl; + + // Wrap it for VTK + Vtk::vtkGeantEvent* vtkEvent = new Vtk::vtkGeantEvent(lastEvent); + state->viewer->AddPuppet(*vtkEvent); + + // Re-render + state->viewer->GetRenderer()->Render(); + state->viewer->GetRenderWindow()->Render(); + } + else { + std::cout << " No event collected." << std::endl; + } + } +} + +int main(int argc, char** argv) { + // 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(10_m, 10_m, 10_m)); + iron_box.SetPosition(Vector3f(0, 0, 0)); + Geant::BoxSolid* iron_cube = new Geant::BoxSolid("IronCube", &iron_box); + iron_cube->SetNistMaterial("G4_Fe"); + iron_cube->Update(); + scene.AddSolid(iron_cube); + + Geant::EmitterPrimary* emitter = new Geant::EmitterPrimary(); + emitter->SetPosition(Vector3f(0, 0, 14_m)); + scene.SetEmitter(emitter); + scene.Initialize(); + + // 2. Setup VTK Viewer + Vtk::Viewer viewer; + viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1); + + // Visualize world box + Vtk::vtkContainerBox* vtkWorld = new Vtk::vtkContainerBox(scene.GetWorldBox()); + vtkWorld->ShowScaleMeasures(true); + vtkWorld->SetRepresentation(Vtk::Puppet::Wireframe); + vtkWorld->SetSelectable(false); + viewer.AddPuppet(*vtkWorld); + + // Visualize iron cube + Vtk::vtkContainerBox* vtkIron = new Vtk::vtkContainerBox(&iron_box); + vtkIron->SetOpacity(0.2); + vtkIron->SetRepresentation(Vtk::Puppet::Surface); + viewer.AddPuppet(*vtkIron); + + // Visualize Emitter + Vtk::vtkEmitterPrimary* vtkEmitter = new Vtk::vtkEmitterPrimary(*emitter); + viewer.AddPuppet(*vtkEmitter); + + // 3. Event Handling + AppState state = { &scene, &viewer, {} }; + + vtkSmartPointer keyCallback = vtkSmartPointer::New(); + keyCallback->SetCallback(KeyPressCallbackFunction); + keyCallback->SetClientData(&state); + + viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback); + + std::cout << "=================================================" << std::endl; + std::cout << " Geant Muon Interactive Emitter Test" << std::endl; + std::cout << " Use the Widget to move/rotate the Arrow (Emitter)" << std::endl; + std::cout << " Press [ENTER] to fire a muon" << std::endl; + std::cout << " Press [q] to exit" << std::endl; + std::cout << "=================================================" << std::endl; + + viewer.ZoomAuto(); + viewer.Start(); + + return 0; +} diff --git a/src/Vtk/HEP/Geant/vtkEmitterPrimary.cpp b/src/Vtk/HEP/Geant/vtkEmitterPrimary.cpp new file mode 100644 index 0000000..c7a3492 --- /dev/null +++ b/src/Vtk/HEP/Geant/vtkEmitterPrimary.cpp @@ -0,0 +1,72 @@ +#include "vtkEmitterPrimary.h" +#include +#include +#include +#include +#include +#include +#include +#include "Math/vtkDense.h" + +namespace uLib { +namespace Vtk { + +vtkEmitterPrimary::vtkEmitterPrimary(Geant::EmitterPrimary &emitter) + : m_emitter(emitter), m_Poly(nullptr), m_Actor(vtkActor::New()) { + + vtkNew arrow; + + // Default arrow is along X+. Rotate to point towards Z- relative to the origin + vtkNew transform; + transform->RotateY(90.0); + + vtkNew transformFilter; + transformFilter->SetTransform(transform); + transformFilter->SetInputConnection(arrow->GetOutputPort()); + transformFilter->Update(); + + vtkSmartPointer mapper = vtkSmartPointer::New(); + mapper->SetInputData(transformFilter->GetOutput()); + m_Actor->SetMapper(mapper); + m_Actor->SetScale(1000.0); // 1 meter long + + vtkNew vmat; + Matrix4fToVtk(m_emitter.GetWorldMatrix(), vmat); + m_Actor->SetUserMatrix(vmat); + + this->SetProp(m_Actor); + + Object::connect(&m_emitter, &Object::Updated, this, &vtkEmitterPrimary::contentUpdate); + this->contentUpdate(); +} + +vtkEmitterPrimary::~vtkEmitterPrimary() { + Object::disconnect(&m_emitter, &Object::Updated, this, &vtkEmitterPrimary::contentUpdate); + m_Actor->Delete(); +} + +void vtkEmitterPrimary::contentUpdate() { + vtkMatrix4x4 *vmat = m_Actor->GetUserMatrix(); + if (!vmat) { + vtkNew mat; + m_Actor->SetUserMatrix(mat); + vmat = mat; + } + + Matrix4f transform = m_emitter.GetWorldMatrix(); + Matrix4fToVtk(transform, vmat); + + Puppet::Update(); +} + +void vtkEmitterPrimary::Update() { + vtkMatrix4x4 *vmat = m_Actor->GetUserMatrix(); + if (!vmat) return; + + Matrix4f transform = VtkToMatrix4f(vmat); + m_emitter.SetMatrix(transform); + m_emitter.Updated(); +} + +} // namespace Vtk +} // namespace uLib diff --git a/src/Vtk/HEP/Geant/vtkEmitterPrimary.h b/src/Vtk/HEP/Geant/vtkEmitterPrimary.h new file mode 100644 index 0000000..4780ed3 --- /dev/null +++ b/src/Vtk/HEP/Geant/vtkEmitterPrimary.h @@ -0,0 +1,32 @@ +#ifndef VTK_GEANT_EMITTERPRIMARY_H +#define VTK_GEANT_EMITTERPRIMARY_H + +#include "Vtk/uLibVtkInterface.h" +#include "HEP/Geant/EmitterPrimary.hh" + +class vtkConeSource; +class vtkLineSource; +class vtkPolyData; +class vtkActor; + +namespace uLib { +namespace Vtk { + +class vtkEmitterPrimary : public Puppet { +public: + vtkEmitterPrimary(Geant::EmitterPrimary &emitter); + virtual ~vtkEmitterPrimary(); + + virtual void contentUpdate(); + virtual void Update(); + +private: + Geant::EmitterPrimary &m_emitter; + vtkPolyData *m_Poly; + vtkActor *m_Actor; +}; + +} // namespace Vtk +} // namespace uLib + +#endif // VTK_GEANT_EMITTERPRIMARY_H diff --git a/src/Vtk/uLibVtkInterface.cxx b/src/Vtk/uLibVtkInterface.cxx index a8a14a2..440ce90 100644 --- a/src/Vtk/uLibVtkInterface.cxx +++ b/src/Vtk/uLibVtkInterface.cxx @@ -215,6 +215,7 @@ vtkProp *Puppet::GetProp() void Puppet::SetProp(vtkProp *prop) { if(prop) { + prop->SetPickable(d->m_Selectable); if (auto* p3d = vtkProp3D::SafeDownCast(prop)) { d->m_Assembly->AddPart(p3d); } @@ -414,6 +415,11 @@ void Puppet::SetOpacity(double alpha) void Puppet::SetSelectable(bool selectable) { d->m_Selectable = selectable; + vtkProp3DCollection *props = d->m_Assembly->GetParts(); + props->InitTraversal(); + for (int i = 0; i < props->GetNumberOfItems(); ++i) { + props->GetNextProp3D()->SetPickable(selectable); + } } bool Puppet::IsSelectable() const