add cylinder emitter

This commit is contained in:
AndreaRigoni
2026-03-19 16:19:36 +00:00
parent ae27e9d46d
commit 80952cc706
6 changed files with 286 additions and 0 deletions

View File

@@ -3,6 +3,7 @@ set(TESTS
vtkGeantEventTest
vtkEmitterPrimaryTest
vtkSkyPlaneEmitterPrimaryTest
vtkCylinderEmitterPrimaryTest
)
set(LIBRARIES

View File

@@ -0,0 +1,136 @@
/*//////////////////////////////////////////////////////////////////////////////
// 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 >
//////////////////////////////////////////////////////////////////////////////*/
#include "Geant/Solid.h"
#include "HEP/Geant/GeantEvent.h"
#include "HEP/Geant/Scene.h"
#include "HEP/Geant/EmitterPrimary.hh"
#include "Math/Cylinder.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 <vtkSmartPointer.h>
#include <vtkCallbackCommand.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <Geant4/Randomize.hh>
#include <Geant4/G4ParticleGun.hh>
#include <Geant4/G4SystemOfUnits.hh>
#include <iostream>
using namespace uLib;
struct AppState {
Geant::Scene* scene;
Vtk::Viewer* viewer;
Vector<Geant::GeantEvent> results;
};
void KeyPressCallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData) {
auto* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
auto* state = static_cast<AppState*>(clientData);
std::string key = interactor->GetKeySym();
if (key == "Return") {
std::cout << "--> Firing muon from cylinder emitter..." << std::endl;
// Run one event
state->scene->RunSimulation(1, state->results);
if (!state->results.empty()) {
Geant::GeantEvent* lastEvent = &state->results.back();
std::cout << " Collected event " << lastEvent->GetEventID()
<< " with " << lastEvent->Path().size() << " steps." << std::endl;
Vtk::vtkGeantEvent* vtkEvent = new Vtk::vtkGeantEvent(lastEvent);
state->viewer->AddPuppet(*vtkEvent);
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);
// Use CylinderEmitterPrimary
Geant::CylinderEmitterPrimary* emitter = new Geant::CylinderEmitterPrimary();
emitter->SetPosition(Vector3f(0, -15_m, 0)); // Vertically offset so base is at -10m
emitter->Rotate(-90_deg, Vector3f(1, 0, 0));
emitter->SetRadius(15_m);
emitter->SetHeight(30_m);
scene.SetEmitter(emitter);
scene.Initialize();
// 2. Setup VTK Viewer
Vtk::Viewer viewer;
viewer.GetRenderer()->SetBackground(0.05, 0.05, 0.1);
Vtk::vtkContainerBox* vtkWorld = new Vtk::vtkContainerBox(scene.GetWorldBox());
vtkWorld->ShowScaleMeasures(true);
vtkWorld->SetRepresentation(Vtk::Puppet::Wireframe);
vtkWorld->SetSelectable(false);
viewer.AddPuppet(*vtkWorld);
Vtk::vtkContainerBox* vtkIron = new Vtk::vtkContainerBox(&iron_box);
vtkIron->SetOpacity(0.2);
vtkIron->SetRepresentation(Vtk::Puppet::Surface);
viewer.AddPuppet(*vtkIron);
// Use vtkCylinderEmitterPrimary
Vtk::vtkCylinderEmitterPrimary* vtkEmitter = new Vtk::vtkCylinderEmitterPrimary(*emitter);
vtkEmitter->SetSelectable(false);
viewer.AddPuppet(*vtkEmitter);
// 3. Event Handling
AppState state = { &scene, &viewer, {} };
vtkSmartPointer<vtkCallbackCommand> keyCallback = vtkSmartPointer<vtkCallbackCommand>::New();
keyCallback->SetCallback(KeyPressCallbackFunction);
keyCallback->SetClientData(&state);
viewer.GetInteractor()->AddObserver(vtkCommand::KeyPressEvent, keyCallback);
std::cout << "=================================================" << std::endl;
std::cout << " Cylinder Emitter Interactive Test" << std::endl;
std::cout << " Visualize the generation volume (blue cylinder)" << std::endl;
std::cout << " Press [ENTER] to fire a muon using EcoMug" << std::endl;
std::cout << " Press [q] to exit" << std::endl;
std::cout << "=================================================" << std::endl;
viewer.ZoomAuto();
viewer.Start();
return 0;
}

View File

@@ -10,6 +10,7 @@
#include <vtkAppendPolyData.h>
#include <vtkPolyData.h>
#include <vtkProperty.h>
#include <vtkCylinderSource.h>
#include "Math/vtkDense.h"
namespace uLib {
@@ -121,6 +122,56 @@ void vtkQuadMeshEmitterPrimary::contentUpdate() {
// For now stick with the arrow. In the future visualize the mesh if useful.
vtkEmitterPrimary::contentUpdate();
}
// -------------------------------------------------------------------------- //
// vtkCylinderEmitterPrimary
vtkCylinderEmitterPrimary::vtkCylinderEmitterPrimary(Geant::CylinderEmitterPrimary &emitter)
: vtkEmitterPrimary(emitter), m_cylinderEmitter(emitter), m_CylinderSource(vtkCylinderSource::New()) {
// vtkCylinderSource is along Y by default.
// We will update its actual dimensions in contentUpdate().
m_CylinderSource->SetResolution(64);
// Rotate it to be along local Z
vtkNew<vtkTransform> tcyl;
tcyl->RotateX(90.0);
vtkNew<vtkTransformPolyDataFilter> cylFilter;
cylFilter->SetTransform(tcyl);
cylFilter->SetInputConnection(m_CylinderSource->GetOutputPort());
vtkNew<vtkAppendPolyData> append;
// Keep the directional arrow from base class
append->AddInputData(vtkPolyData::SafeDownCast(m_Actor->GetMapper()->GetInput()));
append->AddInputConnection(cylFilter->GetOutputPort());
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(append->GetOutputPort());
m_Actor->SetMapper(mapper);
m_Actor->GetProperty()->SetOpacity(0.5);
m_Actor->GetProperty()->SetColor(0.2, 0.6, 0.9);
this->contentUpdate();
}
vtkCylinderEmitterPrimary::~vtkCylinderEmitterPrimary() {
m_CylinderSource->Delete();
}
void vtkCylinderEmitterPrimary::contentUpdate() {
float r = m_cylinderEmitter.GetRadius();
float h = m_cylinderEmitter.GetHeight();
m_CylinderSource->SetRadius(r);
m_CylinderSource->SetHeight(h);
// vtkCylinderSource is along Y. To have the base at Y=0, we center it at h/2.
m_CylinderSource->SetCenter(0, h/2.0, 0);
m_CylinderSource->Update();
vtkEmitterPrimary::contentUpdate();
}
} // namespace Vtk
} // namespace uLib

View File

@@ -9,6 +9,7 @@ class vtkLineSource;
class vtkPolyData;
class vtkActor;
class vtkPlaneSource;
class vtkCylinderSource;
namespace uLib {
namespace Vtk {
@@ -38,6 +39,8 @@ private:
vtkPlaneSource *m_PlaneSource;
};
class vtkQuadMeshEmitterPrimary : public vtkEmitterPrimary {
public:
vtkQuadMeshEmitterPrimary(Geant::QuadMeshEmitterPrimary &emitter);
@@ -47,6 +50,18 @@ private:
Geant::QuadMeshEmitterPrimary &m_meshEmitter;
};
class vtkCylinderEmitterPrimary : public vtkEmitterPrimary {
public:
vtkCylinderEmitterPrimary(Geant::CylinderEmitterPrimary &emitter);
virtual ~vtkCylinderEmitterPrimary();
virtual void contentUpdate();
private:
Geant::CylinderEmitterPrimary &m_cylinderEmitter;
vtkCylinderSource *m_CylinderSource;
};