From a6894aa6e61745bdfaace2733cc6f7c968513320 Mon Sep 17 00:00:00 2001 From: sophieMu2e Date: Tue, 25 Nov 2025 08:16:27 -0600 Subject: [PATCH 01/24] added source display --- examples/calibcalo_MDC2020.fcl | 77 ++++++++++++++++++++++++++++++++++ src/DataInterface.cc | 18 ++++---- src/MCInterface.cc | 6 +-- src/MainWindow.cc | 34 +++++++++++++-- 4 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 examples/calibcalo_MDC2020.fcl diff --git a/examples/calibcalo_MDC2020.fcl b/examples/calibcalo_MDC2020.fcl new file mode 100644 index 0000000..bec042a --- /dev/null +++ b/examples/calibcalo_MDC2020.fcl @@ -0,0 +1,77 @@ +# author : Sophie Middleton +# purpose : example for helix tracks +#include "Offline/fcl/minimalMessageService.fcl" +#include "Offline/fcl/standardProducers.fcl" +#include "Offline/fcl/standardServices.fcl" +#include "EventDisplay/fcl/prolog.fcl" + +services : @local::Services.Reco + +process_name : HelixED + +source : { module_type : RootInput } + +physics : +{ + analyzers : { @table::REveDis.analyzers} + producers : { @table::REveDis.producers } + filters : { @table::REveDis.filters } +} +# geometry options +physics.analyzers.Mu2eEventDisplay.showCRV : true + +physics.analyzers.Mu2eEventDisplay.showPS : false +physics.analyzers.Mu2eEventDisplay.showTS : false +physics.analyzers.Mu2eEventDisplay.showDS : false +physics.analyzers.Mu2eEventDisplay.addCRVBars : false +physics.analyzers.Mu2eEventDisplay.showTracker : false +physics.analyzers.Mu2eEventDisplay.showST : false +physics.analyzers.Mu2eEventDisplay.showCRV : false +physics.analyzers.Mu2eEventDisplay.addKalInter : false +physics.analyzers.Mu2eEventDisplay.addCrystalHits : true +physics.analyzers.Mu2eEventDisplay.filler.addHelixSeeds : false +physics.analyzers.Mu2eEventDisplay.filler.addKalSeeds : false +physics.analyzers.Mu2eEventDisplay.filler.addClusters : true +physics.analyzers.Mu2eEventDisplay.filler.addHits : false # adds ComboHits +physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true +physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true +physics.analyzers.Mu2eEventDisplay.filler.addTimeClusters : false +physics.analyzers.Mu2eEventDisplay.filler.addSimParts : true +physics.analyzers.Mu2eEventDisplay.filler.addCaloDigis : true +physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : false +physics.analyzers.Mu2eEventDisplay.filler.addCosmicTrackSeeds : false +physics.analyzers.Mu2eEventDisplay.filler.addMCTraj : true +physics.analyzers.Mu2eEventDisplay.specifyTag : false + +# allows movement through events in sequential order +physics.analyzers.Mu2eEventDisplay.seqMode : true +physics.analyzers.Mu2eEventDisplay.strawdisplay : false + +# print statements +physics.analyzers.Mu2eEventDisplay.diagLevel : 10 + +# the path +physics.EndPath : [ @sequence::REveDis.seqBase] +physics.REvePath : @local::REvePath +physics.trigger_paths : ["REvePath" ] +physics.analyzers.Mu2eEventDisplay.SelectEvents : ["REvePath" ] + +# useless file name +services.TFileService.fileName: "/dev/null" + +# +# to display a selected set of events, #include this file with a list of eventid triplets: ie: +# +##include "Mu2eEventDisplay/examples/nominal_example.fcl" +#physics.filters.eidsel.idsToMatch : [ +# "1202:636:337031", +# "1202:648:370322" ] +# +physics.analyzers.Mu2eEventDisplay.gdmlname :"Offline/gen/gdml/mu2e_MDC2020.gdml" + +source.inputCommands: ["keep *", + "drop mu2e::CaloClusters_CaloClusterFast_*_*" + ] + +services.TFileService.fileName: "/dev/null" +services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_MDC2020.txt" diff --git a/src/DataInterface.cc b/src/DataInterface.cc index d6f8f51..a3a5be2 100644 --- a/src/DataInterface.cc +++ b/src/DataInterface.cc @@ -56,20 +56,24 @@ void DataInterface::AddCaloDigis(REX::REveManager *&eveMng, bool firstLoop_, std diskID = 1; } CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(diskID,crystal.position()) ; - CLHEP::Hep3Vector pointInMu2e = det->toMu2e(crystalPos); + //CLHEP::Hep3Vector pointInMu2e = det->toMu2e(crystalPos); std::string crytitle = "Crystal ID = " + std::to_string(cryID) + '\n' + " Time = " + std::to_string(digi.t0())+" ns "; char const *crytitle_c = crytitle.c_str(); - std::string name = "disk" + std::to_string(diskID); - auto ps1 = new REX::REvePointSet(name, "CaloClusters Disk 1: ",0); - auto ps2 = new REX::REvePointSet(name, "CaloClusters Disk 2: ",0); - + std::string name = "disk " + std::to_string(diskID); + std::string label = " CaloDigi Instance = " + names[0] + '\n' + + " SiPM. = "+std::to_string(sipmID)+ '\n' + + " t0 = "+std::to_string(digi.t0())+" ns " + '\n' + + " peakPos = "+std::to_string(digi.peakpos()); + auto ps1 = new REX::REvePointSet(label,label, 0); + auto ps2 = new REX::REvePointSet(label, label, 0); + // Set positions of clusters if(diskID == 0) - ps1->SetNextPoint(pointmmTocm(crystal.position().x()), pointmmTocm(crystal.position().y()) , zpos ); + ps1->SetNextPoint(pointmmTocm(crystalPos.x()), pointmmTocm(crystalPos.y()) , zpos ); if(diskID == 1) ps2->SetNextPoint(pointmmTocm(crystalPos.x()), pointmmTocm(crystalPos.y()) , zpos ); @@ -88,7 +92,7 @@ void DataInterface::AddCaloDigis(REX::REveManager *&eveMng, bool firstLoop_, std scene->AddElement(ps2); // plot the crystals which are present in this event: - auto b = new REX::REveBox("crystal",crytitle_c); + auto b = new REX::REveBox(crytitle_c,crytitle_c); b->SetMainColor(color); double width = crystalXLen/2; double height = crystalYLen/2; diff --git a/src/MCInterface.cc b/src/MCInterface.cc index 244c2be..649d532 100644 --- a/src/MCInterface.cc +++ b/src/MCInterface.cc @@ -251,6 +251,7 @@ void MCInterface::AddSimParticleCollection(REX::REveManager *&eveMng, bool first CLHEP::Hep3Vector StartPos = det->toDetector(simpart.startPosition()); CLHEP::Hep3Vector EndPos = det->toDetector(simpart.endPosition()); double momentum = sqrt(simpart.startMomentum().x()*simpart.startMomentum().x()+simpart.startMomentum().y()*simpart.startMomentum().y() + simpart.startMomentum().z()*simpart.startMomentum().z()); + double endmomentum = sqrt(simpart.endMomentum().x()*simpart.endMomentum().x()+simpart.endMomentum().y()*simpart.endMomentum().y() + simpart.endMomentum().z()*simpart.endMomentum().z()); std::string mctitle_start = " SimParticle PDGid " + std::to_string(simpart.pdgId()) + '\n' + " Creation code " + (startCode) + " Stopping code " + (stopCode) + '\n' + " Start Position: " + '\n' @@ -263,11 +264,10 @@ void MCInterface::AddSimParticleCollection(REX::REveManager *&eveMng, bool first + " x " + std::to_string( EndPos.x()) + " y " + std::to_string( EndPos.y()) + " z " + std::to_string( EndPos.z()) - + " time :" + std::to_string(simpart.endGlobalTime() ); + + " time :" + std::to_string(simpart.endGlobalTime() )+ '\n' + + " End Momentum " + std::to_string(endmomentum) + " End Energy " + std::to_string(simpart.endMomentum().e()) ; - // add point - // add point //auto simpoint_start = new REX::REvePointSet(mctitle_start,mctitle_start,1); // create line with the above label auto simpart_line = new REX::REveLine(mctitle_start,mctitle_start,1); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index e41793f..2b95d65 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -67,6 +67,7 @@ void MainWindow::showNodesByName(TGeoNode* n, const std::string& str, bool onOff } std::string name(n->GetName()); j++; + //std::cout< pos; std::pair> offset; pos.push_back(tv[0]); @@ -399,8 +400,10 @@ void MainWindow::GeomDrawerNominal(TGeoNode* node, REX::REveTrans& trans, REX::R double z_hall = 0;double z_world = 0; double x_crv = 0 ; double y_crv = 0 ; double z_crv = 0; // double z_crv_u = 0; double z_crv_d = 0; x_trk = 0;y_trk = 0;z_trk = 0; - + double x_fp0 = 0; double y_fp0 = 0; double z_fp0 = 0; + double x_fp1 = 0; double y_fp1 = 0; double z_fp1 = 0; for(unsigned int i = 0; i < offsets.size(); i++){ + if(offsets[i].first.find("World") != string::npos){ x_world = offsets[i].second[0]; y_world = offsets[i].second[1]; @@ -455,6 +458,16 @@ void MainWindow::GeomDrawerNominal(TGeoNode* node, REX::REveTrans& trans, REX::R y_ipa = y_st + offsets[i].second[1]; z_ipa = z_st + offsets[i].second[2]; } + if(offsets[i].first.find("CaloFP") != string::npos){ + x_fp0 = x_d0+ offsets[i].second[0]; + y_fp0 = y_d0 + offsets[i].second[1]; + z_fp0 = z_d0 + offsets[i].second[2]; + } + if(offsets[i].first.find("CaloFP") != string::npos){ + x_fp1 = x_d1 + offsets[i].second[0]; + y_fp1 = y_d1 + offsets[i].second[1]; + z_fp1 = z_d1 + offsets[i].second[2]; + } } std::vector shift; @@ -498,6 +511,21 @@ void MainWindow::GeomDrawerNominal(TGeoNode* node, REX::REveTrans& trans, REX::R showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, true, true, shift, true, false, drawconfigf.getInt("CALColor")); } } + static std::vector substrings_pipes1 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; + for(auto& i: substrings_pipes1){ + shift.at(0) = x_fp1 - x_trk; + shift.at(1) = y_fp1 - y_trk; + shift.at(2) = z_fp1 - z_trk; + showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, false,false, shift, true, false, drawconfigf.getInt("CALColor")); //TODO - add disk shift + + } + static std::vector substrings_pipes0 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; + for(auto& i: substrings_pipes0){ + shift.at(0) = x_fp0 - x_trk; //crystal len/2 + shift.at(1) = y_fp0 - y_trk; + shift.at(2) = z_fp0 - z_trk - 20/2; + showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, true, true, shift, false, true, drawconfigf.getInt("CALColor")); + } } if(geomOpt.showST and !geomOpt.extracted){ static std::vector substrings_pa {"protonabs1","protonabs3","protonabs4"}; From 8e8a4a9d77c9a3f882a630f8ecc6fa2b6b37cec7 Mon Sep 17 00:00:00 2001 From: sophieMu2e Date: Fri, 28 Nov 2025 15:19:17 -0600 Subject: [PATCH 02/24] text input working --- examples/extracted_MDC2020.fcl | 45 ++--------------- examples/extracted_MDC2025.fcl | 45 +---------------- fcl/extracted.fcl | 28 +++++++++++ fcl/prolog.fcl | 4 +- inc/DisplayStatus.hh | 16 ++++++ inc/EventDisplayManager.hh | 7 ++- inc/TextSelect.hh | 31 ++++++------ src/EventDisplayManager.cc | 60 +++++++++++++++++----- src/MainWindow.cc | 37 +++++++------- src/Mu2eEventDisplay_module.cc | 91 ++++++++++++++++++++++------------ src/TextSelect.cc | 46 ++++++++--------- 11 files changed, 216 insertions(+), 194 deletions(-) create mode 100644 fcl/extracted.fcl create mode 100644 inc/DisplayStatus.hh diff --git a/examples/extracted_MDC2020.fcl b/examples/extracted_MDC2020.fcl index f1f4874..6ceaac3 100644 --- a/examples/extracted_MDC2020.fcl +++ b/examples/extracted_MDC2020.fcl @@ -3,53 +3,14 @@ #include "Offline/fcl/minimalMessageService.fcl" #include "Offline/fcl/standardProducers.fcl" #include "Offline/fcl/standardServices.fcl" -#include "EventDisplay/fcl/prolog.fcl" -services : @local::Services.Reco -process_name : HelixED -source : { module_type : RootInput } -physics : -{ - analyzers : { @table::REveDis.analyzers} - producers : { @table::REveDis.producers } - filters : { @table::REveDis.filters } -} -//geometry options -physics.analyzers.Mu2eEventDisplay.showCRV : true -physics.analyzers.Mu2eEventDisplay.showPS : false -physics.analyzers.Mu2eEventDisplay.showTS : false -physics.analyzers.Mu2eEventDisplay.showST : false -//highlight CRV bars which are "hit" -physics.analyzers.Mu2eEventDisplay.addCRVBars : true -physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true -physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true -//for KinKal development -physics.analyzers.Mu2eEventDisplay.addCrystalHits : true -physics.analyzers.Mu2eEventDisplay.addKalInter : true -physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : true -physics.analyzers.Mu2eEventDisplay.filler.addKalSeeds : true -physics.analyzers.Mu2eEventDisplay.filler.addTrkHits : false -//turn these off for now -physics.analyzers.Mu2eEventDisplay.filler.addClusters : false //CaloClusters -physics.analyzers.Mu2eEventDisplay.filler.addHits : false //ComboHits -physics.analyzers.Mu2eEventDisplay.filler.addTimeClusters : false -//pat rec CosmicTrackSeed -physics.analyzers.Mu2eEventDisplay.filler.addCosmicTrackSeeds : false +#include "EventDisplay/fcl/extracted.fcl" -// MC Trajectory: -physics.analyzers.Mu2eEventDisplay.filler.addMCTraj : true -//setup extracted geometry -physics.analyzers.Mu2eEventDisplay.extracted : true physics.analyzers.Mu2eEventDisplay.gdmlname :"Offline/gen/gdml/mu2e_extracted_MDC2020.gdml" +services.TFileService.fileName: "/dev/null" +services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_extracted_MDC2020.txt" -physics.analyzers.Mu2eEventDisplay.seqMode : true -//for print statements -physics.analyzers.Mu2eEventDisplay.diagLevel : 1 -//the path physics.EndPath : [ @sequence::REveDis.seqBase] physics.REvePath : @local::REvePath physics.trigger_paths : ["REvePath" ] physics.analyzers.Mu2eEventDisplay.SelectEvents : ["REvePath" ] - -services.TFileService.fileName: "/dev/null" -services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_extracted_MDC2020.txt" diff --git a/examples/extracted_MDC2025.fcl b/examples/extracted_MDC2025.fcl index 09bd12f..c507d45 100644 --- a/examples/extracted_MDC2025.fcl +++ b/examples/extracted_MDC2025.fcl @@ -3,53 +3,10 @@ #include "Offline/fcl/minimalMessageService.fcl" #include "Offline/fcl/standardProducers.fcl" #include "Offline/fcl/standardServices.fcl" -#include "EventDisplay/fcl/prolog.fcl" -services : @local::Services.Reco -process_name : HelixED -source : { module_type : RootInput } -physics : -{ - analyzers : { @table::REveDis.analyzers} - producers : { @table::REveDis.producers } - filters : { @table::REveDis.filters } -} -//geometry options -physics.analyzers.Mu2eEventDisplay.showCRV : true -physics.analyzers.Mu2eEventDisplay.showPS : false -physics.analyzers.Mu2eEventDisplay.showTS : false -physics.analyzers.Mu2eEventDisplay.showST : false -//highlight CRV bars which are "hit" -physics.analyzers.Mu2eEventDisplay.addCRVBars : true -physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true -physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true -//for KinKal development -physics.analyzers.Mu2eEventDisplay.addCrystalHits : true -physics.analyzers.Mu2eEventDisplay.addKalInter : true -physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : true -physics.analyzers.Mu2eEventDisplay.filler.addKalSeeds : true -physics.analyzers.Mu2eEventDisplay.filler.addTrkHits : false -//turn these off for now -physics.analyzers.Mu2eEventDisplay.filler.addClusters : false //CaloClusters -physics.analyzers.Mu2eEventDisplay.filler.addHits : false //ComboHits -physics.analyzers.Mu2eEventDisplay.filler.addTimeClusters : false -//pat rec CosmicTrackSeed -physics.analyzers.Mu2eEventDisplay.filler.addCosmicTrackSeeds : false +#include "EventDisplay/fcl/extracted.fcl" -// MC Trajectory: -physics.analyzers.Mu2eEventDisplay.filler.addMCTraj : true -//setup extracted geometry -physics.analyzers.Mu2eEventDisplay.extracted : true physics.analyzers.Mu2eEventDisplay.gdmlname :"Offline/gen/gdml/mu2e_extracted.gdml" -physics.analyzers.Mu2eEventDisplay.seqMode : true -//for print statements -physics.analyzers.Mu2eEventDisplay.diagLevel : 1 -//the path -physics.EndPath : [ @sequence::REveDis.seqBase] -physics.REvePath : @local::REvePath -physics.trigger_paths : ["REvePath" ] -physics.analyzers.Mu2eEventDisplay.SelectEvents : ["REvePath" ] - services.TFileService.fileName: "/dev/null" services.GeometryService.inputFile: "Offline/Mu2eG4/geom/geom_common_extracted.txt" diff --git a/fcl/extracted.fcl b/fcl/extracted.fcl new file mode 100644 index 0000000..733f70a --- /dev/null +++ b/fcl/extracted.fcl @@ -0,0 +1,28 @@ +#include "EventDisplay/fcl/prolog.fcl" + +services : @local::Services.Reco +process_name : Extracted +source : { module_type : RootInput } +physics : +{ + analyzers : { @table::REveDis.analyzers} + producers : { @table::REveDis.producers } + filters : { @table::REveDis.filters } +} + +physics.analyzers.Mu2eEventDisplay.showCRV : true +physics.analyzers.Mu2eEventDisplay.showST: false +physics.analyzers.Mu2eEventDisplay.addCRVBars : true +physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true +physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true +physics.analyzers.Mu2eEventDisplay.filler.addCosmicTrackSeeds : false +physics.analyzers.Mu2eEventDisplay.addCrystalHits : true +physics.analyzers.Mu2eEventDisplay.addKalInter : true +physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : true +physics.analyzers.Mu2eEventDisplay.filler.addKalSeeds : true +physics.analyzers.Mu2eEventDisplay.filler.addTrkHits : false +physics.analyzers.Mu2eEventDisplay.filler.addClusters : false //CaloClusters +physics.analyzers.Mu2eEventDisplay.filler.addHits : false //ComboHits + +physics.analyzers.Mu2eEventDisplay.extracted : true +physics.analyzers.Mu2eEventDisplay.seqMode : true diff --git a/fcl/prolog.fcl b/fcl/prolog.fcl index dfc7549..31f04b1 100644 --- a/fcl/prolog.fcl +++ b/fcl/prolog.fcl @@ -3,7 +3,7 @@ BEGIN_PROLOG Mu2eEventDisplay : { module_type : Mu2eEventDisplay diagLevel : 1 - showCRV : false + showCRV : true showPS : false showTS : false showDS : false @@ -45,7 +45,7 @@ Mu2eEventDisplay : { addCosmicTrackSeeds : false addMCTraj : true addSurfSteps : true - addSimParts : true + addSimParts : false FillAll : false } particles : [11,13,2212,2112,211,22,212] diff --git a/inc/DisplayStatus.hh b/inc/DisplayStatus.hh new file mode 100644 index 0000000..b486788 --- /dev/null +++ b/inc/DisplayStatus.hh @@ -0,0 +1,16 @@ +#include +#include + +namespace mu2e { + struct DisplayStatus { + // Holds the command entered by the user in the REve interface + std::string pendingCommand; + + // Mutex to protect access to the pendingCommand from different threads + // (REve UI thread vs. art event-processing thread). + std::mutex commandMutex; + + // Add other shared flags/data as needed, e.g., + bool shouldAdvanceEvent = false; + }; +} diff --git a/inc/EventDisplayManager.hh b/inc/EventDisplayManager.hh index 16b5802..150a85d 100644 --- a/inc/EventDisplayManager.hh +++ b/inc/EventDisplayManager.hh @@ -11,6 +11,7 @@ #include "EventDisplay/inc/GUI.hh" #include "EventDisplay/inc/TextSelect.hh" #include "nlohmann/json.hpp" +#include namespace ROOT::Experimental { class REveManager; @@ -27,8 +28,7 @@ namespace mu2e { explicit EventDisplayManager(ROOT::Experimental::REveManager* eveMgr, std::condition_variable& cv, std::mutex& m, - GUI *fGui, - TextSelect *fText); + GUI *fGui); void NextEvent(); void QuitRoot(); @@ -37,6 +37,9 @@ namespace mu2e { void setR(int runId); void goToRunEvent(int runId, int eventId); int run{0}; + void setTextSelect(TextSelect* fText); + std::uint32_t fTextId_{0}; + void setTextSelectId(std::uint32_t textId); private: ROOT::Experimental::REveManager* eveMng_{nullptr}; std::condition_variable* cv_{nullptr}; diff --git a/inc/TextSelect.hh b/inc/TextSelect.hh index 43b3ab1..eb72d8b 100644 --- a/inc/TextSelect.hh +++ b/inc/TextSelect.hh @@ -2,6 +2,7 @@ #define _TextSelect_hh #include +#include namespace REX = ROOT::Experimental; using namespace ROOT::Experimental; @@ -10,26 +11,22 @@ namespace mu2e { class TextSelect : public ROOT::Experimental::REveElement { public: - //TextSelect(int &test_) : test(test_){}; - /*TextSelect() = default; // ROOT needs a dictionary - TextSelect(){}; - - explicit TextSelect( - int runn, - int eventn) - : REveElement{"TextSelect"}, runN(runn), eventN(eventn) - {}*/ + // Constructor remains implicit or default + TextSelect() : REveElement{"TextSelect"} {} - void set(int run, int event); - int get(); - int setRun(int run); - //int test{0}; - private: + // Setter (Called by the GUI thread/EventDisplayManager) + void set(int run, int event); + + // Getter (Called by the art module thread) + // We now retrieve both run and event in one call for atomic safety + std::pair getRunEvent(); // <<< Modified getter signature + + // setRun and get() from original code are now deprecated by getRunEvent + + private: int runN = 0; int eventN = 0; - - - + std::mutex _mutex; // <<< The synchronization tool }; } diff --git a/src/EventDisplayManager.cc b/src/EventDisplayManager.cc index 7774670..3efb54c 100644 --- a/src/EventDisplayManager.cc +++ b/src/EventDisplayManager.cc @@ -1,17 +1,29 @@ #include "EventDisplay/inc/EventDisplayManager.hh" - namespace mu2e { EventDisplayManager::EventDisplayManager( ROOT::Experimental::REveManager* eveMgr, std::condition_variable& cv, std::mutex& m, - GUI *fGui, - TextSelect *fText) - : REveElement{"EventManager"}, eveMng_{eveMgr}, cv_{&cv}, m_{&m}, fGui_(fGui), fText_(fText) - {} + GUI *fGui) // <<< TextSelect *fText IS REMOVED + : + REveElement{"EventManager"}, + eveMng_{eveMgr}, + cv_{&cv}, + m_{&m}, + fGui_(fGui) // <<< fText_(fText) IS REMOVED +{} +// Implement the setter method +void EventDisplayManager::setTextSelect(TextSelect* fText) { + fText_ = fText; + if (fText_ == nullptr) { + std::cerr << "INTERNAL ERROR: setTextSelect called with a NULL pointer." << std::endl; + } else { + std::cout << "[EventDisplayManager::setTextSelect] fText_ successfully set." << std::endl; + } +} void EventDisplayManager::NextEvent() { @@ -37,13 +49,35 @@ namespace mu2e { this->run = runId; std::cout<<"[EventDisplayManager::setR] run number now "<run<run<set(runId,eventId); - } + // Add a function to store the ID +void EventDisplayManager::setTextSelectId(std::uint32_t textId) { + fTextId_ = textId; + std::cout << "[EventDisplayManager::setTextSelectId] fTextId_ set to: " << fTextId_ << std::endl; +} +void mu2e::EventDisplayManager::goToRunEvent(int runId, int eventId) +{ + std::cout << "[EventDisplayManager::goToRunEvent] received: " << runId<<" "<FindElementById(4285);//fTextId_); + + if (element != nullptr) { + fText_obj = dynamic_cast(element); + } + } + + if (fText_obj == nullptr) { + // Report the ID it just tried to look up: + std::cerr << "CRITICAL ERROR: TextSelect object not found via gEve->FindElementWithId(" + << fTextId_ << "). Cannot set Run/Event." << std::endl; + return; + } + fText_obj->set(runId, eventId); +} } + diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 2b95d65..82f3a93 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -104,7 +104,7 @@ void MainWindow::showNodesByName(TGeoNode* n, const std::string& str, bool onOff t(2,1) = rm[3]; t(2,2) = rm[4]; t(2,3) = rm[5]; t(3,1) = rm[6]; t(3,2) = rm[7]; t(3,3) = rm[8]; t(1,4) = tv[0] + shift[0]; t(2,4) = tv[1] + shift[1]; t(3,4) = tv[2] + shift[2]; - std::cout< substrings_stoppingtarget {"TargetFoil"}; shift.at(0) = x_st - x_trk; @@ -511,21 +511,24 @@ void MainWindow::GeomDrawerNominal(TGeoNode* node, REX::REveTrans& trans, REX::R showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, true, true, shift, true, false, drawconfigf.getInt("CALColor")); } } - static std::vector substrings_pipes1 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; - for(auto& i: substrings_pipes1){ - shift.at(0) = x_fp1 - x_trk; - shift.at(1) = y_fp1 - y_trk; - shift.at(2) = z_fp1 - z_trk; - showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, false,false, shift, true, false, drawconfigf.getInt("CALColor")); //TODO - add disk shift - - } - static std::vector substrings_pipes0 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; - for(auto& i: substrings_pipes0){ - shift.at(0) = x_fp0 - x_trk; //crystal len/2 - shift.at(1) = y_fp0 - y_trk; - shift.at(2) = z_fp0 - z_trk - 20/2; - showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, true, true, shift, false, true, drawconfigf.getInt("CALColor")); - } + bool showPipes = false; + if(showPipes){ + static std::vector substrings_pipes1 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; + for(auto& i: substrings_pipes1){ + shift.at(0) = x_fp1 - x_trk; + shift.at(1) = y_fp1 - y_trk; + shift.at(2) = z_fp1 - z_trk; + showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, false,false, shift, true, false, drawconfigf.getInt("CALColor")); + + } + static std::vector substrings_pipes0 {"CaloFP_pipe","CaloFP_pipe1","CaloFP_pipe2","CaloFP_pipe3","CaloFP_pipe4","CaloFP_pipe5"}; + for(auto& i: substrings_pipes0){ + shift.at(0) = x_fp0 - x_trk; + shift.at(1) = y_fp0 - y_trk; + shift.at(2) = z_fp0 - z_trk - 20/2; //crystal len/2 + showNodesByName(node,i,kFALSE, 0, trans, crystalsholder, maxlevel, level, true, true, shift, false, true, drawconfigf.getInt("CALColor")); + } + } } if(geomOpt.showST and !geomOpt.extracted){ static std::vector substrings_pa {"protonabs1","protonabs3","protonabs4"}; diff --git a/src/Mu2eEventDisplay_module.cc b/src/Mu2eEventDisplay_module.cc index fb09d89..187eb2a 100644 --- a/src/Mu2eEventDisplay_module.cc +++ b/src/Mu2eEventDisplay_module.cc @@ -160,9 +160,7 @@ namespace mu2e // Display control art::EventID displayedEventID_{}; - REX::REveManager* eveMng_{nullptr}; - EventDisplayManager* eventMgr_{nullptr}; - + // Control between the main thread and event-display thread std::condition_variable cv_{}; std::mutex m_{}; @@ -200,9 +198,12 @@ namespace mu2e bool showEM_; // Setup Custom GUI - GUI *fGui{nullptr}; - PrintInfo *fPrint{nullptr}; - TextSelect *fText{nullptr}; + + std::unique_ptr fGui{nullptr}; + std::unique_ptr fText{nullptr}; + std::unique_ptr fPrint{nullptr}; + REX::REveManager* eveMng_{nullptr}; + std::unique_ptr eventMgr_{nullptr}; double eventid_; double runid_; double subrunid_; @@ -351,6 +352,20 @@ namespace mu2e if((seqMode_) or ( runid_ == runn and subrunid_ == subrunn and eventid_ == eventn)){ // Hand off control to display thread + // --- NEW: Read User Input from TextSelect --- + // fText is the pointer to the TextSelect instance initialized in setup_eve() + if (fText) { + std::pair user_input = fText->getRunEvent(); + int user_run = user_input.first; + int user_event = user_input.second; + + // *** Print the accessible run and event to the console *** + std::cout << "\n[Mu2eEventDisplay::analyze] -------------------------" << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] User Input Detected (from TextSelect):" << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] Run Number: " << user_run << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] Event Number: " << user_event << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] -------------------------\n" << std::endl; + } std::unique_lock lock{m_}; if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : analyze()] -- Fill collections "<run<run<AllowMultipleRemoteConnections(false, false); ROOT::RWebWindowsManager::SetUseSessionKey(false); - //InitGuiInfo() - fGui = new GUI(); - fGui->SetName("Mu2eGUI"); - - fPrint = new PrintInfo(); - fText = new TextSelect(); - - // call manager - eventMgr_ = new EventDisplayManager{eveMng_, cv_, m_, fGui, fText}; - - // access the world - auto world = eveMng_->GetWorld(); + fGui = std::make_unique(); + fGui->SetName("Mu2eGUI"); + fPrint = std::make_unique(); + fText = std::make_unique(); + + // 2. Instantiate Manager using raw pointers from the unique_ptr objects + // Use .get() to pass the raw pointer address to the constructor. + eventMgr_ = std::make_unique( + eveMng_, + cv_, + m_, + fGui.get() // Pass raw pointer from unique_ptr + ); + + // 3. Pass the valid TextSelect pointer using the setter + eventMgr_->setTextSelect(fText.get()); + + // 4. Register elements and commands + auto world = eveMng_->GetWorld(); + assert(world); - assert(world); frame_ = new MainWindow(); frame_->makeGeometryScene(eveMng_, geomOpts, gdmlname_); @@ -473,21 +496,25 @@ namespace mu2e eveMng_->AddLocation("mydir/", configFile("EventDisplay/CustomGUIv2")); eveMng_->SetDefaultHtmlPage("file:mydir/eventDisplay.html"); - // InitGuiInfo() cont'd - world->AddElement(fGui); - world->AddElement(fText); - world->AddElement(eventMgr_); - world->AddElement(fPrint); - std::cout<<"[Mu2eEventDisplay::setup_eve] run in display is set to "<run<AddCommand("QuitRoot", "sap-icon://log", eventMgr_, "QuitRoot()"); - world->AddCommand("NextEvent", "sap-icon://step", eventMgr_, "NextEvent()"); - world->AddCommand("PrintMCInfo", "sap-icon://step", fPrint, "PrintMCInfo()"); - world->AddCommand("PrintRecoInfo", "sap-icon://step", fPrint, "PrintRecoInfo()"); + + world->AddElement(fText.get()); + world->AddElement(eventMgr_.get()); + world->AddElement(fPrint.get()); + world->AddElement(fGui.get()); + + + // ... (AddCommand calls must also use .get()) ... +eventMgr_->setTextSelectId(fText->GetElementId()); // GetElementId() returns the assigned EId_t + world->AddCommand("QuitRoot", "sap-icon://log", eventMgr_.get(), "QuitRoot()"); + world->AddCommand("NextEvent", "sap-icon://step", eventMgr_.get(), "NextEvent()"); + world->AddCommand("PrintMCInfo", "sap-icon://step", fPrint.get(), "PrintMCInfo()"); + world->AddCommand("PrintRecoInfo", "sap-icon://step", fPrint.get(), "PrintRecoInfo()"); std::unique_lock lock{m_}; cv_.notify_all(); } + + // Actually interesting function responsible for drawing the current event void Mu2eEventDisplay::process_single_event() @@ -506,7 +533,7 @@ namespace mu2e fPrint->ftrack_tuple = data.track_tuple; std::cout<<"[Mu2eEventDisplay::process_single_event] display has run number set to "<run<get()<get()<StampObjProps(); diff --git a/src/TextSelect.cc b/src/TextSelect.cc index d41de17..d5af99a 100644 --- a/src/TextSelect.cc +++ b/src/TextSelect.cc @@ -1,33 +1,29 @@ +// EventDisplay/src/TextSelect.cc (New Implementation) + #include "EventDisplay/inc/TextSelect.hh" +#include -using namespace mu2e; +namespace mu2e { -/*TextSelect::TextSelect(){}; +// Setter: Called by the REve thread (EventDisplayManager) +void TextSelect::set(int run, int event) { + // Lock the mutex for writing + std::lock_guard lock(_mutex); + runN = run; + eventN = event; + std::cout<<"user has set "< TextSelect::getRunEvent() { + // Lock the mutex for reading + std::lock_guard lock(_mutex); + std::cout<<"user has received "< Date: Fri, 28 Nov 2025 15:59:46 -0600 Subject: [PATCH 03/24] text entry does work and I've updated comments in module --- src/EventDisplayManager.cc | 4 +- src/Mu2eEventDisplay_module.cc | 540 +++++++++++++++++++++------------ 2 files changed, 342 insertions(+), 202 deletions(-) diff --git a/src/EventDisplayManager.cc b/src/EventDisplayManager.cc index 3efb54c..e31d058 100644 --- a/src/EventDisplayManager.cc +++ b/src/EventDisplayManager.cc @@ -62,8 +62,7 @@ void mu2e::EventDisplayManager::goToRunEvent(int runId, int eventId) if (ROOT::Experimental::gEve != nullptr) { - // *** CRITICAL FIX: Use the likely correct function name *** - ROOT::Experimental::REveElement* element = ROOT::Experimental::gEve->FindElementById(4285);//fTextId_); + ROOT::Experimental::REveElement* element = ROOT::Experimental::gEve->FindElementById(4285);//fTextId_); //FIXME note the hardcoding if (element != nullptr) { fText_obj = dynamic_cast(element); @@ -71,7 +70,6 @@ void mu2e::EventDisplayManager::goToRunEvent(int runId, int eventId) } if (fText_obj == nullptr) { - // Report the ID it just tried to look up: std::cerr << "CRITICAL ERROR: TextSelect object not found via gEve->FindElementWithId(" << fTextId_ << "). Cannot set Run/Event." << std::endl; return; diff --git a/src/Mu2eEventDisplay_module.cc b/src/Mu2eEventDisplay_module.cc index 187eb2a..2e3536a 100644 --- a/src/Mu2eEventDisplay_module.cc +++ b/src/Mu2eEventDisplay_module.cc @@ -247,16 +247,6 @@ namespace mu2e showEM_(conf().showEM()), seqMode_(conf().seqMode()) { - std::cout<<"GDML file "<>eventn; - std::cout<<" SubRun Number : "<>subrunn; - std::cout<<" Run Number : "<>runn; - } geomOpts.fill(showCRV_,showPS_, showTS_, showDS_, show2D_, caloVST_, showST_, extracted_, showSTM_, showCalo_, showTracker_, showCaloCrystals_, showEM_ ); } @@ -270,34 +260,26 @@ namespace mu2e void Mu2eEventDisplay::beginJob(){ if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : beginJob()] -- starting ..."<(end1 - start1).count()<<" ms "< void Mu2eEventDisplay::FillAnyCollection(const art::Event& evt, std::vector>& list, std::tuple, std::vector>& tuple){ - // get all instances of products of type T - std::vector> vah = evt.getMany(); - std::string name; - std::vector alabel; - std::vector alist; - // loop over the list of instances of products of this type - for (auto const& ah : vah) { +template +void Mu2eEventDisplay::FillAnyCollection(const art::Event& evt, std::vector>& list, std::tuple, std::vector>& tuple){ + + // Use Art's getMany() to retrieve a vector of all art::Handle objects + // that exist in the event record for the type T. + // + std::vector> vah = evt.getMany(); + + // Temporary variables to store the labels and the product pointers/handles. + std::string name; + std::vector alabel; // Stores fully qualified names (label_instance_process) + std::vector alist; // Stores the actual product pointers (const T*) + + // Loop over the list of art::Handle retrieved from the event. + for (auto const& ah : vah) { + // Get the Provenance (metadata) for the product associated with this handle. + // Provenance tells you which module created the data. const art::Provenance* prov = ah.provenance(); + // --- Extract Provenance Information --- + + // Friendly Class Name (e.g., "CaloClusterCollection") std::string fcn = prov->friendlyClassName(); + // Module Label (the name of the Art module that created it) std::string modn = prov->moduleLabel(); + // Process Name (the Art process name, used for configuration context) std::string instn = prov->processName(); - alist.push_back(ah.product()); + + // Get the raw product pointer/handle and push it onto the list. + // The type S is typically const T* or a similar type that references the product. + alist.push_back(ah.product()); + + // Create a unique, descriptive name string for this collection instance. std::string name = fcn + "_" + prov->moduleLabel() + "_" + instn; + + // Store the name so it can be displayed in the GUI alongside the collection. alabel.push_back(name); + if(diagLevel_ == 1){ - std::cout<<"extracting name = "<(event, _chits, data.calocluster_tuple);} - } - if(filler_.addCaloDigis_) { - if(specifyTag_) filler_.FillRecoCollections(event, data, CaloDigis); - else { FillAnyCollection(event, _chits, data.calodigi_tuple);} - } - - if(filler_.addHits_) { - if(specifyTag_) { filler_.FillRecoCollections(event, data, ComboHits); } - else { FillAnyCollection(event, _chits, data.combohit_tuple ); } - } - - if(filler_.addHelixSeeds_){ - if(specifyTag_) { filler_.FillRecoCollections(event, data, HelixSeeds); } - else { FillAnyCollection(event, _chits, data.helix_tuple ); } - } - - if(filler_.addKalSeeds_) { - if(specifyTag_) { filler_.FillRecoCollections(event, data, KalSeeds); } - else { FillAnyCollection(event, _chits, data.track_tuple ); } - } - - if(filler_.addMCTraj_) { - if(specifyTag_) { filler_.FillMCCollections(event, data, MCTrajectories); } - else { FillAnyCollection(event, _chits, data.mctrack_tuple ); } - } - - if(filler_.addSurfSteps_) { - if(specifyTag_) { filler_.FillMCCollections(event, data, SurfaceSteps); } - else { FillAnyCollection(event, _chits, data.surfstep_tuple ); } - } - if(filler_.addSimParts_) { - if(specifyTag_) { filler_.FillMCCollections(event, data, SimParticles); } - else { FillAnyCollection(event, _chits, data.sim_tuple ); } - } - - if(filler_.addTimeClusters_) { - if(specifyTag_) { filler_.FillRecoCollections(event, data, TimeClusters);} - else { FillAnyCollection(event, _chits, data.timecluster_tuple );} - } - - if(filler_.addCrvHits_) { - if(specifyTag_) { filler_.FillRecoCollections(event, data, CrvRecoPulses); } - else { FillAnyCollection(event, _chits, data.crvpulse_tuple );} - } - - if(filler_.addCrvClusters_) { - if(specifyTag_) { filler_.FillRecoCollections(event, data, CrvCoincidenceCluster); } - else { FillAnyCollection(event, _chits, data.crvcoin_tuple );} - } - - if(filler_.addTrkHits_) filler_.FillRecoCollections(event, data, TrkHits); - if(filler_.addCosmicTrackSeeds_) filler_.FillRecoCollections(event, data, CosmicTrackSeeds); - if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : analyze()] -- Event processing started "< user_input = fText->getRunEvent(); + int user_run = user_input.first; + int user_event = user_input.second; + + std::cout << "\n[Mu2eEventDisplay::analyze] -------------------------" << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] User Input Detected:" << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] Run Number: " << user_run << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] Event Number: " << user_event << std::endl; + std::cout << "[Mu2eEventDisplay::analyze] -------------------------\n" << std::endl; + + // Check if valid input was provided (Run and Event are non-zero). + if(user_run !=0 and user_event != 0){ + // Store the user-requested event number internally. + runn = user_run; + eventn = user_event; + // Disable sequential processing mode. The module must stop after this event is found. + seqMode_ = false; + } + } - //std::cout<<"test VALUE "<run<(event, _chits, data.calocluster_tuple);} + } + + if(filler_.addCaloDigis_) { + if(specifyTag_) filler_.FillRecoCollections(event, data, CaloDigis); + else { FillAnyCollection(event, _chits, data.calodigi_tuple);} + } + + if(filler_.addHits_) { + if(specifyTag_) { filler_.FillRecoCollections(event, data, ComboHits); } + else { FillAnyCollection(event, _chits, data.combohit_tuple ); } + } + + if(filler_.addHelixSeeds_){ + if(specifyTag_) { filler_.FillRecoCollections(event, data, HelixSeeds); } + else { FillAnyCollection(event, _chits, data.helix_tuple ); } + } + + if(filler_.addKalSeeds_) { + if(specifyTag_) { filler_.FillRecoCollections(event, data, KalSeeds); } + else { FillAnyCollection(event, _chits, data.track_tuple ); } + } + + // --- MC Collections (Monte Carlo) --- + if(filler_.addMCTraj_) { + if(specifyTag_) { filler_.FillMCCollections(event, data, MCTrajectories); } + else { FillAnyCollection(event, _chits, data.mctrack_tuple ); } + } + + if(filler_.addSurfSteps_) { + if(specifyTag_) { filler_.FillMCCollections(event, data, SurfaceSteps); } + else { FillAnyCollection(event, _chits, data.surfstep_tuple ); } + } + if(filler_.addSimParts_) { + if(specifyTag_) { filler_.FillMCCollections(event, data, SimParticles); } + else { FillAnyCollection(event, _chits, data.sim_tuple ); } + } + // --- End MC Collections --- + + if(filler_.addTimeClusters_) { + if(specifyTag_) { filler_.FillRecoCollections(event, data, TimeClusters);} + else { FillAnyCollection(event, _chits, data.timecluster_tuple );} + } + + if(filler_.addCrvHits_) { + if(specifyTag_) { filler_.FillRecoCollections(event, data, CrvRecoPulses); } + else { FillAnyCollection(event, _chits, data.crvpulse_tuple );} + } + + if(filler_.addCrvClusters_) { + if(specifyTag_) { filler_.FillRecoCollections(event, data, CrvCoincidenceCluster); } + else { FillAnyCollection(event, _chits, data.crvcoin_tuple );} + } + + if(filler_.addTrkHits_) filler_.FillRecoCollections(event, data, TrkHits); + if(filler_.addCosmicTrackSeeds_) filler_.FillRecoCollections(event, data, CosmicTrackSeeds); + + if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : analyze()] -- Event processing started "<ProcessEvents(); + // Explicitly process any waiting system events (like timer ticks or thread signals) + // that were queued before the TApplication event loop officially started. + // This ensures initialization tasks (like setting up the REve browser/GUI) are executed promptly. + gSystem->ProcessEvents(); + + // Start the TApplication event loop. + // The argument 'true' typically means that the function should return only when + // the application is explicitly terminated (e.g., via application_.Terminate(0) in endJob()). + // This line blocks the appThread_ until the user closes the display or the Art job finishes. + // application_.Run(true); } void Mu2eEventDisplay::setup_eve() { - - RWebWindowsManager::AssignMainThrd(); - eveMng_ = REX::REveManager::Create(); - eveMng_->AllowMultipleRemoteConnections(false, false); - ROOT::RWebWindowsManager::SetUseSessionKey(false); - fGui = std::make_unique(); + // Assign the current thread as the main thread for RWebWindowsManager. + // This is critical for thread-safety and ensuring the display correctly manages events. + RWebWindowsManager::AssignMainThrd(); + + // Create the single instance of the REveManager. This is the core object + // that manages all display elements, scenes, and remote connections. + eveMng_ = REX::REveManager::Create(); + + // Configure REve to disallow multiple simultaneous browser connections. + // The second 'false' is typically related to connection handling details. + eveMng_->AllowMultipleRemoteConnections(false, false); + + // Set the global WebWindowsManager to not use session keys for connections, + // simplifying access to the display. + ROOT::RWebWindowsManager::SetUseSessionKey(false); + + // --- Object Creation (Using std::unique_ptr for robust lifetime management) --- + + // Create the custom GUI object. + fGui = std::make_unique(); fGui->SetName("Mu2eGUI"); - fPrint = std::make_unique(); + + // Create the PrintInfo object for displaying data details. + fPrint = std::make_unique(); + + // Create the TextSelect object, used to capture user-input Run/Event numbers. fText = std::make_unique(); - - // 2. Instantiate Manager using raw pointers from the unique_ptr objects - // Use .get() to pass the raw pointer address to the constructor. + + // --- Event Manager Instantiation and Singleton Link --- + + // Instantiate the core EventDisplayManager. It takes raw pointers to: + // 1. The REveManager (eveMng_) + // 2. The synchronization primitives (cv_, m_) + // 3. The custom GUI object (fGui.get()) eventMgr_ = std::make_unique( eveMng_, cv_, m_, - fGui.get() // Pass raw pointer from unique_ptr + fGui.get() ); - // 3. Pass the valid TextSelect pointer using the setter - eventMgr_->setTextSelect(fText.get()); - - // 4. Register elements and commands - auto world = eveMng_->GetWorld(); - assert(world); + // --- Scene Setup --- + // Get the top-level scene, the "World," which is the container for all elements. + auto world = eveMng_->GetWorld(); + assert(world); // Ensure the world was successfully retrieved. - frame_ = new MainWindow(); - frame_->makeGeometryScene(eveMng_, geomOpts, gdmlname_); + // Instantiate the custom MainWindow (likely handles the GDML geometry). + frame_ = new MainWindow(); + // Load the geometry into the main window scene. + frame_->makeGeometryScene(eveMng_, geomOpts, gdmlname_); - //add path to the custom GUI code here, this overrides ROOT GUI - eveMng_->AddLocation("mydir/", configFile("EventDisplay/CustomGUIv2")); - eveMng_->SetDefaultHtmlPage("file:mydir/eventDisplay.html"); + // --- Custom GUI and HTML Page Setup --- - - world->AddElement(fText.get()); - world->AddElement(eventMgr_.get()); - world->AddElement(fPrint.get()); - world->AddElement(fGui.get()); - - - // ... (AddCommand calls must also use .get()) ... -eventMgr_->setTextSelectId(fText->GetElementId()); // GetElementId() returns the assigned EId_t - world->AddCommand("QuitRoot", "sap-icon://log", eventMgr_.get(), "QuitRoot()"); - world->AddCommand("NextEvent", "sap-icon://step", eventMgr_.get(), "NextEvent()"); - world->AddCommand("PrintMCInfo", "sap-icon://step", fPrint.get(), "PrintMCInfo()"); - world->AddCommand("PrintRecoInfo", "sap-icon://step", fPrint.get(), "PrintRecoInfo()"); - std::unique_lock lock{m_}; - cv_.notify_all(); + // Add a location path for custom files (like JavaScript or CSS). + eveMng_->AddLocation("mydir/", configFile("EventDisplay/CustomGUIv2")); + // Set the default HTML page that the web browser will load. + eveMng_->SetDefaultHtmlPage("file:mydir/eventDisplay.html"); - } + // --- Add Elements to the World (Element ID Assignment occurs here) --- + + // Elements must be added to a scene/world before their Element ID is assigned and valid. + world->AddElement(fText.get()); + world->AddElement(eventMgr_.get()); + world->AddElement(fPrint.get()); + world->AddElement(fGui.get()); + + // --- Final Linking and Command Registration --- + + eventMgr_->setTextSelectId(fText->GetElementId()); + + // Register commands that the GUI buttons will execute. The command is routed to the + // method on the specified element (eventMgr_.get() or fPrint.get()). + world->AddCommand("QuitRoot", "sap-icon://log", eventMgr_.get(), "QuitRoot()"); + world->AddCommand("NextEvent", "sap-icon://step", eventMgr_.get(), "NextEvent()"); + world->AddCommand("PrintMCInfo", "sap-icon://step", fPrint.get(), "PrintMCInfo()"); + world->AddCommand("PrintRecoInfo", "sap-icon://step", fPrint.get(), "PrintRecoInfo()"); + + // --- Signal Art Thread to Proceed --- + + // Acquire a lock on the mutex. + std::unique_lock lock{m_}; + // Signal the waiting Art thread (in analyze() or beginRun()) to continue processing + // now that the REve display is fully initialized. + cv_.notify_all(); + //std::cout << "[DEBUG] TextSelect object ID assigned: " << fText->GetElementId() << std::endl; +} // Actually interesting function responsible for drawing the current event void Mu2eEventDisplay::process_single_event() - { + { + if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : process_single_event] Start "<DisableRedraw(); - eveMng_->GetWorld()->BeginAcceptingChanges(); - eveMng_->GetScenes()->AcceptChanges(true); - + + // --- 1. Disable Redrawing and Start Change Tracking --- + + // Temporarily disable the browser redraw to prevent the display from updating + // multiple times during the process, leading to flickering and slow performance. + eveMng_->DisableRedraw(); + + // Tell the World Scene to start tracking changes. All element additions/modifications + // are batched until EndAcceptingChanges() is called. + eveMng_->GetWorld()->BeginAcceptingChanges(); + + // Tell all scenes managed by the REveManager to start accepting batched changes. + eveMng_->GetScenes()->AcceptChanges(true); + + // --- 2. Update GUI and PrintInfo Data Structures --- + + // Transfer the core event identifiers to the GUI object for display. fGui->feventid = eventid_; fGui->fsubrunid = subrunid_; fGui->frunid = runid_; + // Transfer the relevant collection data to the PrintInfo object. + // This allows the PrintInfo command to access and display details when triggered. fPrint->fcalocluster_tuple = data.calocluster_tuple; fPrint->fmctrack_tuple = data.mctrack_tuple; fPrint->ftrack_tuple = data.track_tuple; + // Display the run number currently held by the Event Manager (for debugging). std::cout<<"[Mu2eEventDisplay::process_single_event] display has run number set to "<run<get()<StampObjProps(); + + // Update the custom properties displayed in the REve GUI (e.g., in a sidebar table). + fGui->StampObjProps(); + // --- 3. Prepare the Scene and Data Options --- + if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : process_single_event] -- extract event scene "<GetEventScene(); + // Get the dedicated scene for event data (where tracks, hits, etc., will be drawn). + REX::REveElement* scene = eveMng_->GetEventScene(); if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : process_single_event] -- calls to data interface "<showEvents(eveMng_, scene, firstLoop_, firstLoopCalo_, data, drawOpts, particles_, strawdisplay_, geomOpts, KKOpts); if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : process_single_event] -- cluster added to scene "<GetScenes()->AcceptChanges(false); - eveMng_->GetWorld()->EndAcceptingChanges(); - eveMng_->EnableRedraw(); + // --- 5. Finalize Changes and Enable Redraw --- + + // Set the flag to false, indicating subsequent events are not the "first loop" (allowing optimization). + firstLoop_ = false; + + // Stop tracking changes in all scenes. + eveMng_->GetScenes()->AcceptChanges(false); + + // Finalize the batch of changes for the World scene. This triggers the update signal. + eveMng_->GetWorld()->EndAcceptingChanges(); + + // Re-enable the browser redraw. The browser now performs a single, optimized refresh + // using all the batched changes. + eveMng_->EnableRedraw(); if(diagLevel_ == 1) std::cout<<"[Mu2eEventDisplay : process_single_event] End "< Date: Sat, 29 Nov 2025 13:00:03 -0600 Subject: [PATCH 04/24] formatting, removed GeomUtil --- inc/DisplayStatus.hh | 16 ------ inc/EventDisplayManager.hh | 4 -- inc/GUI.hh | 1 - inc/GeomUtil.hh | 31 ------------ inc/MainWindow.hh | 1 - src/CMakeLists.txt | 1 - src/EventDisplayManager.cc | 101 +++++++++++++++++++++++-------------- src/GeomUtil.cc | 14 ----- src/TextSelect.cc | 5 -- src/classes.h | 1 - src/classes_def.xml | 1 - 11 files changed, 62 insertions(+), 114 deletions(-) delete mode 100644 inc/DisplayStatus.hh delete mode 100644 inc/GeomUtil.hh delete mode 100644 src/GeomUtil.cc diff --git a/inc/DisplayStatus.hh b/inc/DisplayStatus.hh deleted file mode 100644 index b486788..0000000 --- a/inc/DisplayStatus.hh +++ /dev/null @@ -1,16 +0,0 @@ -#include -#include - -namespace mu2e { - struct DisplayStatus { - // Holds the command entered by the user in the REve interface - std::string pendingCommand; - - // Mutex to protect access to the pendingCommand from different threads - // (REve UI thread vs. art event-processing thread). - std::mutex commandMutex; - - // Add other shared flags/data as needed, e.g., - bool shouldAdvanceEvent = false; - }; -} diff --git a/inc/EventDisplayManager.hh b/inc/EventDisplayManager.hh index 150a85d..0c6338a 100644 --- a/inc/EventDisplayManager.hh +++ b/inc/EventDisplayManager.hh @@ -33,18 +33,14 @@ namespace mu2e { void NextEvent(); void QuitRoot(); void autoplay(int x); - int getR(); - void setR(int runId); void goToRunEvent(int runId, int eventId); int run{0}; - void setTextSelect(TextSelect* fText); std::uint32_t fTextId_{0}; void setTextSelectId(std::uint32_t textId); private: ROOT::Experimental::REveManager* eveMng_{nullptr}; std::condition_variable* cv_{nullptr}; std::mutex* m_{nullptr}; - bool doneProcessingEvents_{false}; GUI *fGui_{nullptr}; TextSelect *fText_{nullptr}; }; diff --git a/inc/GUI.hh b/inc/GUI.hh index dac9699..27f00b3 100644 --- a/inc/GUI.hh +++ b/inc/GUI.hh @@ -20,7 +20,6 @@ class GUI : public ROOT::Experimental::REveElement void PrintEventInfo(); int runNumber{0}; int eventNumber{0}; - }; } diff --git a/inc/GeomUtil.hh b/inc/GeomUtil.hh deleted file mode 100644 index d5248b7..0000000 --- a/inc/GeomUtil.hh +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _GeomUtil_hh -#define _GeomUtil_hh - -#include -#include - -#include "Offline/StoppingTargetGeom/inc/StoppingTarget.hh" -#include "Offline/GeometryService/inc/DetectorSystem.hh" - -namespace REX = ROOT::Experimental; - -namespace mu2e { - class GeomUtil : public REX::REveElement { - - public : - explicit GeomUtil() { SetErrorHandler(DefaultErrorHandler); } - virtual ~GeomUtil() {} - #ifndef __CINT__ - double FindStoppingTarget_z(); - double GetStoppingTarget_z(){ return StoppingTarget_z; } - double StoppingTarget_z; - - #endif - ClassDef(GeomUtil, 0); - - - - }; - -} -#endif diff --git a/inc/MainWindow.hh b/inc/MainWindow.hh index d469158..f6d1d42 100644 --- a/inc/MainWindow.hh +++ b/inc/MainWindow.hh @@ -43,7 +43,6 @@ #include "EventDisplay/inc/DataCollections.hh" #include "EventDisplay/inc/DataInterface.hh" #include "EventDisplay/inc/MCInterface.hh" -#include "EventDisplay/inc/GeomUtil.hh" #include "Offline/StoppingTargetGeom/inc/StoppingTarget.hh" #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aeb209f..c7363e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,6 @@ cet_make_library( DataInterface.cc EventDisplayManager.cc GUI.cc - GeomUtil.cc MCInterface.cc MainWindow.cc PrintInfo.cc diff --git a/src/EventDisplayManager.cc b/src/EventDisplayManager.cc index e31d058..954e2fc 100644 --- a/src/EventDisplayManager.cc +++ b/src/EventDisplayManager.cc @@ -1,81 +1,104 @@ #include "EventDisplay/inc/EventDisplayManager.hh" - namespace mu2e { - EventDisplayManager::EventDisplayManager( +// --- Constructor Implementation --- +/** + * @brief Constructs the EventDisplayManager singleton instance. + * * It stores pointers to the global REve manager and the synchronization primitives (mutex/cv). + * The TextSelect pointer is now passed via the setTextSelectId method instead of the constructor. + * @param eveMgr The global REveManager instance. + * @param cv The condition variable for thread signaling. + * @param m The mutex for thread synchronization. + * @param fGui The pointer to the custom GUI element. + */ +EventDisplayManager::EventDisplayManager( ROOT::Experimental::REveManager* eveMgr, std::condition_variable& cv, std::mutex& m, - GUI *fGui) // <<< TextSelect *fText IS REMOVED + GUI *fGui) : REveElement{"EventManager"}, eveMng_{eveMgr}, cv_{&cv}, m_{&m}, - fGui_(fGui) // <<< fText_(fText) IS REMOVED -{} + fGui_(fGui) +{ +} -// Implement the setter method -void EventDisplayManager::setTextSelect(TextSelect* fText) { - fText_ = fText; - if (fText_ == nullptr) { - std::cerr << "INTERNAL ERROR: setTextSelect called with a NULL pointer." << std::endl; - } else { - std::cout << "[EventDisplayManager::setTextSelect] fText_ successfully set." << std::endl; - } +// --- Thread Synchronization & Commands --- + +/*Signals the waiting Art thread to load the next event. + This function is invoked by the "NextEvent" REve command button. +*/ +void EventDisplayManager::NextEvent() +{ + std::unique_lock lock{*m_}; // Acquire lock on the mutex + cv_->notify_all(); // Notify the waiting Art thread (in analyze()) } - void - EventDisplayManager::NextEvent() - { - std::unique_lock lock{*m_}; - cv_->notify_all(); - } - void - EventDisplayManager::QuitRoot() - { +/*Terminates the application and exits the process. + Invoked by the "QuitRoot" REve command button. + */ +void EventDisplayManager::QuitRoot() +{ std::cout<<"Exit Signal 15, leaving REve Display "<run; - } - void EventDisplayManager::setR(int runId){ - std::cout<<"[EventDisplayManager::setR] taking run number"<run<<" and passing in "<run = runId; - std::cout<<"[EventDisplayManager::setR] run number now "<run<FindElementById(4285);//fTextId_); //FIXME note the hardcoding + // 1. Retrieve the generic REveElement using the global manager (gEve) and the Element ID. + // The function name should be FindElementWithId(fTextId_)but I found that this does not work so I hardcoded FIXME + ROOT::Experimental::REveElement* element = ROOT::Experimental::gEve->FindElementById(4285); if (element != nullptr) { + // 2. Safely cast the generic element to the specific TextSelect type. fText_obj = dynamic_cast(element); } } if (fText_obj == nullptr) { + // CRITICAL ERROR if the object wasn't found or the cast failed. std::cerr << "CRITICAL ERROR: TextSelect object not found via gEve->FindElementWithId(" << fTextId_ << "). Cannot set Run/Event." << std::endl; - return; + return; } + // 3. Command executed: Set the Run/Event numbers in the TextSelect object. fText_obj->set(runId, eventId); } } - diff --git a/src/GeomUtil.cc b/src/GeomUtil.cc deleted file mode 100644 index e63928c..0000000 --- a/src/GeomUtil.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "Offline/GeometryService/inc/GeomHandle.hh" -#include "EventDisplay/inc/GeomUtil.hh" - -namespace REX = ROOT::Experimental; -using namespace std; -using namespace mu2e; - -double GeomUtil::FindStoppingTarget_z(){ - CLHEP::Hep3Vector _detSysOrigin = mu2e::GeomHandle()->getOrigin(); - GeomHandle target; - std::cout<<"Target "<centerInMu2e().z()<<" "<<_detSysOrigin.z()<centerInMu2e().z() - _detSysOrigin.z(); - return StoppingTarget_z; -} diff --git a/src/TextSelect.cc b/src/TextSelect.cc index d5af99a..c0da973 100644 --- a/src/TextSelect.cc +++ b/src/TextSelect.cc @@ -1,5 +1,3 @@ -// EventDisplay/src/TextSelect.cc (New Implementation) - #include "EventDisplay/inc/TextSelect.hh" #include @@ -23,7 +21,4 @@ std::pair TextSelect::getRunEvent() { return {runN, eventN}; } -// Ensure the necessary dictionary is generated for these types if using REve -// For simplicity, we omit the old, incompatible functions like 'int get()' - } diff --git a/src/classes.h b/src/classes.h index 637e121..eb922b3 100644 --- a/src/classes.h +++ b/src/classes.h @@ -2,7 +2,6 @@ #include "EventDisplay/inc/DataInterface.hh" #include "EventDisplay/inc/EventDisplayManager.hh" #include "EventDisplay/inc/DataCollections.hh" -#include "EventDisplay/inc/GeomUtil.hh" #include "EventDisplay/inc/GUI.hh" #include "EventDisplay/inc/DataProduct.hh" #include "EventDisplay/inc/TextSelect.hh" diff --git a/src/classes_def.xml b/src/classes_def.xml index c19ceff..9965240 100644 --- a/src/classes_def.xml +++ b/src/classes_def.xml @@ -2,7 +2,6 @@ - From 91009b4b2f3c3777971aa945be30dc8a31992162 Mon Sep 17 00:00:00 2001 From: sophieMu2e Date: Sat, 29 Nov 2025 13:39:37 -0600 Subject: [PATCH 05/24] tidied up the table print outss --- examples/nominal_MDC2020.fcl | 2 +- fcl/prolog.fcl | 2 +- inc/EventDisplayManager.hh | 94 +++++-- inc/GUI.hh | 28 +- inc/PrintInfo.hh | 99 ++++--- src/GUI.cc | 35 ++- src/PrintInfo.cc | 516 ++++++++++++++++++++++++----------- 7 files changed, 544 insertions(+), 232 deletions(-) diff --git a/examples/nominal_MDC2020.fcl b/examples/nominal_MDC2020.fcl index a2129bb..25330ac 100644 --- a/examples/nominal_MDC2020.fcl +++ b/examples/nominal_MDC2020.fcl @@ -34,7 +34,7 @@ physics.analyzers.Mu2eEventDisplay.filler.addHits : false # adds ComboHits physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true physics.analyzers.Mu2eEventDisplay.filler.addTimeClusters : false -physics.analyzers.Mu2eEventDisplay.filler.addSimParts : false +physics.analyzers.Mu2eEventDisplay.filler.addSimParts : true physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : true physics.analyzers.Mu2eEventDisplay.filler.addCosmicTrackSeeds : false physics.analyzers.Mu2eEventDisplay.filler.addMCTraj : true diff --git a/fcl/prolog.fcl b/fcl/prolog.fcl index 31f04b1..bd1aa63 100644 --- a/fcl/prolog.fcl +++ b/fcl/prolog.fcl @@ -45,7 +45,7 @@ Mu2eEventDisplay : { addCosmicTrackSeeds : false addMCTraj : true addSurfSteps : true - addSimParts : false + addSimParts : true FillAll : false } particles : [11,13,2212,2112,211,22,212] diff --git a/inc/EventDisplayManager.hh b/inc/EventDisplayManager.hh index 0c6338a..897c3f8 100644 --- a/inc/EventDisplayManager.hh +++ b/inc/EventDisplayManager.hh @@ -19,30 +19,80 @@ namespace ROOT::Experimental { namespace mu2e { - constexpr auto invalid_event = std::numeric_limits::max(); - - class EventDisplayManager : public ROOT::Experimental::REveElement { + /** + * @brief Manages event flow, commands, and synchronization between the Art analysis thread + * and the dedicated ROOT/REve display thread. + * Inherits from ROOT::Experimental::REveElement to receive browser commands. + */ + class EventDisplayManager : public ROOT::Experimental::REveElement { public: - EventDisplayManager() = default; // ROOT needs a dictionary - - explicit EventDisplayManager(ROOT::Experimental::REveManager* eveMgr, - std::condition_variable& cv, - std::mutex& m, - GUI *fGui); - - void NextEvent(); - void QuitRoot(); - void autoplay(int x); - void goToRunEvent(int runId, int eventId); - int run{0}; - std::uint32_t fTextId_{0}; - void setTextSelectId(std::uint32_t textId); + // Default constructor required by ROOT's dictionary generation mechanism. + EventDisplayManager() = default; + + /** + * @brief Primary constructor for initializing thread synchronization and manager pointers. + * @param eveMgr The global REveManager instance. + * @param cv The condition variable for inter-thread signaling (notify/wait). + * @param m The mutex for protecting access and for condition variable use. + * @param fGui Pointer to the custom GUI element instance. + */ + explicit EventDisplayManager(ROOT::Experimental::REveManager* eveMgr, + std::condition_variable& cv, + std::mutex& m, + GUI *fGui); + + // --- REve Command Methods (Invoked by browser buttons) --- + + /** + * @brief Command to signal the Art analysis thread to load the next event. + */ + void NextEvent(); + + /** + * @brief Command to terminate the ROOT application and the job gracefully. + */ + void QuitRoot(); + + void autoplay(int x); + + /** + * @brief Command to handle user input and trigger loading a specific Run and Event ID. + * * This function runs in the REve thread and uses fTextId_ to look up the TextSelect object. + */ + void goToRunEvent(int runId, int eventId); + + // --- Public Members --- + + // Stores the current Run ID, often used by commands or display logic. + int run{0}; + + // Stores the unique REve Element ID (EId_t) of the TextSelect object. + // This is the robust mechanism for looking up the TextSelect element. + std::uint32_t fTextId_{0}; + + /** + * @brief Setter to store the unique REve Element ID after the object is added to the World. + * @param textId The assigned REve Element ID. + */ + void setTextSelectId(std::uint32_t textId); + private: - ROOT::Experimental::REveManager* eveMng_{nullptr}; - std::condition_variable* cv_{nullptr}; - std::mutex* m_{nullptr}; - GUI *fGui_{nullptr}; - TextSelect *fText_{nullptr}; + + // Pointer to the global REve manager, controlling all visualization. + ROOT::Experimental::REveManager* eveMng_{nullptr}; + + // Pointer to the condition variable, used to unblock the Art thread (e.g., in analyze()). + std::condition_variable* cv_{nullptr}; + + // Pointer to the mutex, used for thread synchronization (locking data access and cv_ usage). + std::mutex* m_{nullptr}; + + // Pointer to the custom GUI element instance. + GUI *fGui_{nullptr}; + + // Raw pointer to the TextSelect element. While fTextId_ is preferred for lookup, + // this is kept for direct access if necessary (but is less robust). + TextSelect *fText_{nullptr}; }; } diff --git a/inc/GUI.hh b/inc/GUI.hh index 27f00b3..462336b 100644 --- a/inc/GUI.hh +++ b/inc/GUI.hh @@ -7,20 +7,38 @@ namespace REX = ROOT::Experimental; using namespace ROOT::Experimental; namespace mu2e { +/** + * @brief Represents the custom Graphical User Interface component within the REve visualization. + * * This class holds the event metadata and overrides methods to push that data to the web client. + * Inherits from REveElement so it can be added to the REve scene and participate in state updates. + */ class GUI : public ROOT::Experimental::REveElement { public: + // --- Event Display Variables (Set by the Art thread) --- + + // Current Event ID being displayed. Used when writing to JSON. int feventid{0}; + // Current Subrun ID being displayed. int fsubrunid{0}; + // Current Run ID being displayed. int frunid{0}; + + // --- REve Overrides and Communication --- + + /** + * @brief Overrides the base method to serialize custom class data into a JSON stream. + * * This is the crucial function that sends feventid/frunid to the web browser client. + * @param j The nlohmann::json object to populate. + * @param rnr_offset The rendering offset (typically 0). + * @return int Status code from the base class. + */ int WriteCoreJson(nlohmann::json &j, int rnr_offset) override; - void setRun(int run); - void setEvent(int event); - void getRunEvent(); - void PrintEventInfo(); + + // Storage for the Run number requested by the user. int runNumber{0}; + // Storage for the Event number requested by the user. int eventNumber{0}; }; } - #endif diff --git a/inc/PrintInfo.hh b/inc/PrintInfo.hh index f71b7e3..dac7573 100644 --- a/inc/PrintInfo.hh +++ b/inc/PrintInfo.hh @@ -16,44 +16,75 @@ namespace REX = ROOT::Experimental; using namespace ROOT::Experimental; - namespace mu2e { + +/** + * @brief REve element responsible for holding event data pointers and printing diagnostic information. + * * This element is added to the REve World and is the target for print commands issued from the browser. + * * The member variables (tuples) are populated by the Mu2eEventDisplay::analyze() method. + */ class PrintInfo : public ROOT::Experimental::REveElement { public: - PrintInfo() = default; - const mu2e::CaloClusterCollection* clustercol = 0; - std::vector calocluster_list; - std::vector calocluster_labels; - std::tuple, std::vector> fcalocluster_tuple; - - const mu2e::MCTrajectoryCollection *mctrajcol = 0; - std::vector mctrack_list; - std::vector mctrack_labels; - std::tuple, std::vector> fmctrack_tuple; - - const mu2e::SimParticleCollection *simcol = 0; - std::vector sim_list; - std::vector sim_labels; - std::tuple, std::vector> fsim_tuple; - - const mu2e::KalSeedPtrCollection* kalSeedcol = 0; - std::vector track_list; - std::vector track_labels; - std::tuple, std::vector> ftrack_tuple; - - const mu2e::CrvCoincidenceClusterCollection* crvcoincol = 0; - std::vector crvcoin_list; - std::vector crvcoin_labels; - std::tuple, std::vector> fcrvcoin_tuple; - - void PrintRecoInfo(); //prints everything which is there (tracks, clusters, CRV coin, not hits) - void PrintMCInfo(); // print MCs and SimParticles - void PrintSimInfo(); - void PrintKalInfo(); - void PrintCaloInfo(); - void PrintCRVInfo(); -}; + PrintInfo() = default; // Required for the ROOT dictionary/serialization. + + // --- Data Storage for CaloClusterCollection --- + const mu2e::CaloClusterCollection* clustercol = 0; // Single raw pointer (often for legacy use) + std::vector calocluster_list; // List of collection pointers + std::vector calocluster_labels; // Labels corresponding to the collections + // Primary storage: Tuple of (Labels, Collection Pointers) for all CaloCluster products in the event + std::tuple, std::vector> fcalocluster_tuple; + + // --- Data Storage for MCTrajectoryCollection --- + const mu2e::MCTrajectoryCollection *mctrajcol = 0; + std::vector mctrack_list; + std::vector mctrack_labels; + // Primary storage: Tuple for all Monte Carlo Trajectory collections + std::tuple, std::vector> fmctrack_tuple; + + // --- Data Storage for SimParticleCollection (Monte Carlo) --- + const mu2e::SimParticleCollection *simcol = 0; + std::vector sim_list; + std::vector sim_labels; + // Primary storage: Tuple for all SimParticle collections + std::tuple, std::vector> fsim_tuple; + + // --- Data Storage for KalSeedPtrCollection (Reconstruction) --- + const mu2e::KalSeedPtrCollection* kalSeedcol = 0; + std::vector track_list; + std::vector track_labels; + // Primary storage: Tuple for all Kalman Seed collections (reconstructed tracks) + std::tuple, std::vector> ftrack_tuple; + + // --- Data Storage for CrvCoincidenceClusterCollection (Reconstruction) --- + const mu2e::CrvCoincidenceClusterCollection* crvcoincol = 0; + std::vector crvcoin_list; + std::vector crvcoin_labels; + // Primary storage: Tuple for all CRV Coincidence Cluster collections + std::tuple, std::vector> fcrvcoin_tuple; + + // --- Print Command Methods (Invoked by REve commands) --- + + /** + * @brief Prints summary information for reconstructed data products + * (Tracks, CaloClusters, CRV Coincidence Clusters). + */ + void PrintRecoInfo(); + + /** + * @brief Prints summary information for Monte Carlo data products + * (MCTrajectories and SimParticles). + */ + void PrintMCInfo(); + + // The following are more specialized print methods, likely called internally + // by PrintRecoInfo/PrintMCInfo or bound to separate commands. + void PrintMCTrajInfo(); + void PrintSimPartInfo(); + void PrintKalInfo(); + void PrintCaloInfo(); + void PrintCRVInfo(); + }; } #endif diff --git a/src/GUI.cc b/src/GUI.cc index ea8bc46..e7d8ade 100644 --- a/src/GUI.cc +++ b/src/GUI.cc @@ -3,18 +3,31 @@ using namespace mu2e; +/** + * @brief Writes core event metadata to the JSON structure for the REve browser interface. + * * This method overrides the base REveElement method to ensure custom event information + * is included in the state update sent to the client (browser). + * * @param j The nlohmann::json object being populated with element properties. + * @param rnr_offset The offset used for rendering elements (typically 0). + * @return int The status code returned by the base class method (0 on success). + */ int GUI::WriteCoreJson(nlohmann::json &j, int rnr_offset) { - j["path"] = "Event/SubRun/Run"; - j["eventid"] = feventid; - j["subrunid"] = fsubrunid; - j["runid"] = frunid; - j["UT_PostStream"] = "UT_refresh_event_info"; - return ROOT::Experimental::REveElement::WriteCoreJson(j, 0); -} -void GUI::setEvent(int event){ eventNumber = event; } -void GUI::setRun(int run){ runNumber = run; } -void GUI::getRunEvent(){ std::cout<<"GUI SETTTTT"< track_list = std::get<1>(fmctrack_tuple); + + if(track_list.size() > 0){ + + // Set up stream for consistent formatting across all printouts + std::cout << std::fixed << std::setprecision(3); + + // Loop over all available MCTrajectory collections (different Art module outputs). + for(unsigned int j=0; j< track_list.size(); j++){ + const MCTrajectoryCollection* trajcol = track_list[j]; + + if(trajcol !=0){ + + std::map,mu2e::MCTrajectory>::const_iterator trajectoryIter; + + std::cout << "\n======================================================================================================================================================" << std::endl; + std::cout << "MC TRAJ (Primary) SIM PARTICLE INFORMATION: Collection " << j + 1 << std::endl; + std::cout << "Number of SimParticles = " << trajcol->size() << std::endl; + std::cout << "======================================================================================================================================================" << std::endl; + + // --- Table Header --- + // Using setw() for fixed column width and right/left alignment + std::cout << std::left << std::setw(6) << "ID" + << std::setw(10) << "PDGID" + << std::setw(9) << "ENERGY" + << std::setw(12) << "p0x" + << std::setw(12) << "p0y" + << std::setw(12) << "p0z" + << std::setw(12) << "posx" + << std::setw(12) << "posy" + << std::setw(12) << "posz" + << std::setw(10) << "t0" + << std::setw(12) << "p1x" + << std::setw(12) << "p1y" + << std::setw(12) << "p1z" + << std::setw(10) << "t1" + << std::endl; + std::cout << "------------------------------------------------------------------------------------------------------------------------------------------------------" << std::endl; + + + // 3. Iterate over the SimParticle Ptrs and their associated MCTrajectories. + for(trajectoryIter=trajcol->begin(); trajectoryIter!=trajcol->end(); trajectoryIter++){ + + // Access the SimParticle object + const mu2e::SimParticle* simPtr = trajectoryIter->first.get(); + + // --- Initial (Start) Kinematics --- + int pdgID = simPtr->pdgId(); + double energy = simPtr->startMomentum().e(); + double t0 = simPtr->startGlobalTime(); + + CLHEP::Hep3Vector p0 = simPtr->startMomentum().vect(); + CLHEP::Hep3Vector pos0 = simPtr->startPosition(); + + // --- Final (End) Kinematics --- + double t1 = simPtr->endGlobalTime(); + CLHEP::Hep3Vector p1 = simPtr->endMomentum().vect(); + + // Extract the unique SimParticle ID. + int id = simPtr->id().asInt(); -void PrintInfo::PrintSimInfo(){ - - std::vector track_list = std::get<1>(fmctrack_tuple); - if(track_list.size() > 0){ - for(unsigned int j=0; j< track_list.size(); j++){ - const MCTrajectoryCollection* trajcol = track_list[j]; - if(trajcol !=0){ - std::map,mu2e::MCTrajectory>::const_iterator trajectoryIter; - std::cout<<"MC TRAJ (Primary) SIM PARTICLE INFORMATION"<begin(); trajectoryIter!=trajcol->end(); trajectoryIter++){ - std::string pdgID = std::to_string(trajectoryIter->first->pdgId()); - std::string t0 = std::to_string(trajectoryIter->first->startGlobalTime()); - std::string p0x = std::to_string(trajectoryIter->first->startMomentum().x()); - std::string p0y = std::to_string(trajectoryIter->first->startMomentum().y()); - std::string p0z = std::to_string(trajectoryIter->first->startMomentum().z()); - std::string energy = std::to_string(trajectoryIter->first->startMomentum().e()); - std::string posx = std::to_string(trajectoryIter->first->startPosition().x()); //FIXME - this is in Mu2e coords - std::string posy = std::to_string(trajectoryIter->first->startPosition().y()); - std::string posz = std::to_string(trajectoryIter->first->startPosition().z()); - std::string t1 = std::to_string(trajectoryIter->first->endGlobalTime()); - std::string p1x = std::to_string(trajectoryIter->first->endMomentum().x()); - std::string p1y = std::to_string(trajectoryIter->first->endMomentum().y()); - std::string p1z = std::to_string(trajectoryIter->first->endMomentum().z()); - std::string pos1x = std::to_string(trajectoryIter->first->endPosition().x()); - std::string pos1y = std::to_string(trajectoryIter->first->endPosition().y()); - std::string pos1z = std::to_string(trajectoryIter->first->endPosition().z()); - std::string id = std::to_string(trajectoryIter->first->id().asInt()); - - std::cout<<" "< sim_list = std::get<1>(fsim_tuple); +void PrintInfo::PrintSimPartInfo(){ + std::vector sim_list = std::get<1>(fsim_tuple); + std::vector track_list = std::get<1>(fmctrack_tuple); if(track_list.size() > 0){ - for(unsigned int j=0; j< sim_list.size(); j++){ - const SimParticleCollection* simcol = sim_list[j]; - if(simcol !=0){ - - std::cout<<"SIM PARTICLE INFORMATION"<). + for( auto const& simpair : *simcol) { + const mu2e::SimParticle& simpart = simpair.second; + + // --- Extract Initial (Start) Kinematics --- + int pdgID = simpart.pdgId(); + double energy = simpart.startMomentum().e(); + double t0 = simpart.startGlobalTime(); + + CLHEP::Hep3Vector p0 = simpart.startMomentum().vect(); + CLHEP::Hep3Vector pos0 = simpart.startPosition(); + + // --- Extract Final (End) Kinematics --- + double t1 = simpart.endGlobalTime(); + CLHEP::Hep3Vector p1 = simpart.endMomentum().vect(); + + // Extract the unique SimParticle ID. + int id = simpart.id().asInt(); + + // --- Print Data Row (using stream manipulators) --- + std::cout << std::left << std::setw(6) << id + << std::setw(10) << pdgID + << std::right << std::setw(9) << energy + << std::setw(12) << p0.x() + << std::setw(12) << p0.y() + << std::setw(12) << p0.z() + << std::setw(12) << pos0.x() + << std::setw(12) << pos0.y() + << std::setw(12) << pos0.z() + << std::setw(10) << t0 + << std::setw(12) << p1.x() + << std::setw(12) << p1.y() + << std::setw(12) << p1.z() + << std::setw(10) << t1 + << std::endl; + } + std::cout << "======================================================================================================================================================" << std::endl; + } } - } } - } + // Restore default stream formatting + std::cout << std::scientific << std::setprecision(6); } -void PrintInfo::PrintKalInfo(){ - auto const& ptable = GlobalConstantsHandle(); - std::vector ktrack_list = std::get<1>(ftrack_tuple); - std::vector names = std::get<0>(ftrack_tuple); - if(ktrack_list.size() > 0){ - for(unsigned int j=0; j< ktrack_list.size(); j++){ - const KalSeedPtrCollection* seedcol = ktrack_list[j]; - std::cout<<" "<size() !=0){ - std::cout<<"KALSEED INFORMATION"<particle(kseed.particle()).name() + " mom " + std::to_string(momvec.R()) + "MeV/c, cos(Theta) " + std::to_string(cos(momvec.Theta())) + '\n'; - kaltitle += " t0 " + std::to_string(kseed.t0Val()) + " ns, d0 " + std::to_string(d0) + " mm " + '\n'; - unsigned nactive =0; - for (auto const& hit : kseed.hits()){ - if (hit.strawHitState() >= WireHitState::inactive) ++nactive; - } - kaltitle += " N active hits " + std::to_string(nactive) + " fit consistency " + std::to_string(kseed.fitConsistency()) + '\n'; - if(kseed.hasCaloCluster()){ - kaltitle += " calo cluster energy " + std::to_string(kseed.caloCluster()->energyDep()) + '\n'; - }else { - kaltitle += std::string(" no calo cluster ") + '\n'; - } - - std::cout<(); + + // Extract the vector of collection pointers and labels from the ftrack_tuple + std::vector ktrack_list = std::get<1>(ftrack_tuple); + std::vector names = std::get<0>(ftrack_tuple); + + if(ktrack_list.size() > 0){ + + // Set up stream for consistent formatting (e.g., 3 decimal places) + std::cout << std::fixed << std::setprecision(3); + + for(unsigned int j=0; j< ktrack_list.size(); j++){ + const KalSeedPtrCollection* seedcol = ktrack_list[j]; + + if(seedcol->size() !=0){ + + std::cout << "\n==============================================================================================================================" << std::endl; + std::cout << "KALSEED INFORMATION | Collection: " << names[j] << " | Tracks: " << seedcol->size() << std::endl; + std::cout << "==============================================================================================================================" << std::endl; + + // --- Table Header --- + std::cout << std::left << std::setw(10) << "Particle" + << std::right << std::setw(10) << "Mom (MeV)" + << std::setw(10) << "Cos(Th)" + << std::setw(10) << "t0 (ns)" + << std::setw(10) << "d0 (mm)" + << std::setw(8) << "NActive" + << std::setw(12) << "Fit Cons" + << std::setw(10) << "E Calo" + << std::left << std::setw(15) << "Fit Status" + << std::endl; + std::cout << "------------------------------------------------------------------------------------------------------------------------------" << std::endl; + + + for(auto const& kseedptr : *seedcol) { + auto const& kseed = *kseedptr; + + // Taking the first segment is common for simple summary printing + auto const& kseg = kseed.segments()[0]; + auto const& momvec = kseg.momentum3(); + + double d0 = 0.0; + + // --- Extract d0 based on fit hypothesis --- + if(kseed.loopHelixFit()){ + d0 = kseg.loopHelix().minAxisDist(); + } else if(kseed.centralHelixFit()){ + d0 = kseg.centralHelix().d0(); + }else if(kseed.kinematicLineFit()){ + d0 = kseg.kinematicLine().d0(); + } + + // --- Count active hits --- + unsigned nactive = 0; + for (auto const& hit : kseed.hits()){ + // Check if the hit state is active (>= inactive, assuming states are ordered) + if (hit.strawHitState() >= WireHitState::inactive) { + ++nactive; + } + } + + // --- Extract Calo Energy --- + double caloE = kseed.hasCaloCluster() ? kseed.caloCluster()->energyDep() : 0.0; + + // --- Extract Particle Name --- + std::string particleName = ptable->particle(kseed.particle()).name(); + + // --- Print Data Row --- + std::cout << std::left << std::setw(10) << particleName + << std::right << std::setw(10) << momvec.R() // Momentum Magnitude + << std::setw(10) << cos(momvec.Theta()) // Cosine of Polar Angle + << std::setw(10) << kseed.t0Val() // Track t0 + << std::setw(10) << d0 // d0 or minAxisDist + << std::setw(8) << nactive // Number of active hits + << std::setw(12) << kseed.fitConsistency() // Fit Consistency + << std::setw(10) << caloE // Calo Cluster Energy + << std::left << std::setw(15) << kseed.status().stringRep() // Fit Status + << std::endl; + } + std::cout << "==============================================================================================================================" << std::endl; + } } - } } - } + // Restore default stream formatting + std::cout << std::scientific << std::setprecision(6); } void PrintInfo::PrintCaloInfo(){ - std::vector calocluster_list = std::get<1>(fcalocluster_tuple); - if(calocluster_list.size()!=0){ - for(unsigned int j = 0; j< calocluster_list.size(); j++){ - const CaloClusterCollection* clustercol = calocluster_list[j]; - std::cout<<" "<size() != 0){ - for(unsigned int i = 0; i < clustercol->size(); i++){ - auto const& cluster= (*clustercol)[i]; - // Info to print: - std::string cluster_energy = std::to_string(cluster.energyDep()); - std::string cluster_time = std::to_string(cluster.time()); - std::string cluster_x = std::to_string(cluster.cog3Vector().x()); - std::string cluster_y = std::to_string(cluster.cog3Vector().y()); - std::cout<<" "< calocluster_list = std::get<1>(fcalocluster_tuple); + std::vector names = std::get<0>(fcalocluster_tuple); + + if(!calocluster_list.empty()){ + + // Set up stream for consistent formatting (e.g., 3 decimal places) + std::cout << std::fixed << std::setprecision(3); + + // Loop over all CaloCluster collections + for(unsigned int j = 0; j< calocluster_list.size(); j++){ + const mu2e::CaloClusterCollection* clustercol = calocluster_list[j]; + + if(clustercol && !clustercol->empty()){ + + std::cout << "\n=============================================================================" << std::endl; + std::cout << "CALO CLUSTER INFORMATION | Collection: " << names[j] << " | Clusters: " << clustercol->size() << std::endl; + std::cout << "=============================================================================" << std::endl; + + // --- Table Header --- + std::cout << std::right << std::setw(10) << "Energy" + << std::setw(12) << "Time (ns)" + << std::setw(12) << "X COG (mm)" + << std::setw(12) << "Y COG (mm)" + << std::setw(12) << "Z COG (mm)" // Added Z coordinate for completeness + << std::endl; + std::cout << "-----------------------------------------------------------------------------" << std::endl; + + // Iterate over the clusters in the current collection + for(unsigned int i = 0; i < clustercol->size(); i++){ + auto const& cluster= (*clustercol)[i]; + + double energy = cluster.energyDep(); + double time = cluster.time(); + CLHEP::Hep3Vector cog = cluster.cog3Vector(); + + // Print Data Row + std::cout << std::right << std::setw(10) << energy + << std::setw(12) << time + << std::setw(12) << cog.x() + << std::setw(12) << cog.y() + << std::setw(12) << cog.z() + << std::endl; + } + std::cout << "=============================================================================" << std::endl; + } } - } } - } + // Restore default stream formatting + std::cout << std::scientific << std::setprecision(6); } + void PrintInfo::PrintCRVInfo(){ - std::cout<<"CRV COINCIDENCE INFORMATION"< crvcoin_list = std::get<1>(fcrvcoin_tuple); - if(crvcoin_list.size()!=0){ - for(unsigned int j = 0; j< crvcoin_list.size(); j++){ - const CrvCoincidenceClusterCollection* crvcoincol = crvcoin_list[j]; - std::cout<<" avTime "<<" avX "<<" avY "<<" avZ "<size() != 0){ - for(unsigned int i = 0; i < crvcoincol->size(); i++){ - mu2e::CrvCoincidenceCluster const &cluster= (*crvcoincol)[i]; - std::string coin_avtime = std::to_string(cluster.GetAvgHitTime()); - std::string coin_avX = std::to_string(cluster.GetAvgHitPos().x()); - std::string coin_avY = std::to_string(cluster.GetAvgHitPos().y()); - std::string coin_avZ = std::to_string(cluster.GetAvgHitPos().z()); - std::cout<<" "< crvcoin_list = std::get<1>(fcrvcoin_tuple); + std::vector names = std::get<0>(fcrvcoin_tuple); + + if(!crvcoin_list.empty()){ + + // Set up stream for consistent formatting (e.g., 3 decimal places) + std::cout << std::fixed << std::setprecision(3); + + // Loop over all CrvCoincidenceCluster collections + for(unsigned int j = 0; j< crvcoin_list.size(); j++){ + const mu2e::CrvCoincidenceClusterCollection* crvcoincol = crvcoin_list[j]; + + if(crvcoincol && !crvcoincol->empty()){ + + std::cout << "\n=============================================================================" << std::endl; + std::cout << "CRV COINCIDENCE INFORMATION | Collection: " << names[j] << " | Clusters: " << crvcoincol->size() << std::endl; + std::cout << "=============================================================================" << std::endl; + + // --- Table Header --- + // Using right alignment for numerical data + std::cout << std::right << std::setw(12) << "Avg Time (ns)" + << std::setw(12) << "Avg X (mm)" + << std::setw(12) << "Avg Y (mm)" + << std::setw(12) << "Avg Z (mm)" + << std::endl; + std::cout << "-----------------------------------------------------------------------------" << std::endl; + + // Iterate over the coincidence clusters in the current collection + for(unsigned int i = 0; i < crvcoincol->size(); i++){ + mu2e::CrvCoincidenceCluster const &cluster = (*crvcoincol)[i]; + + double avTime = cluster.GetAvgHitTime(); + CLHEP::Hep3Vector avPos = cluster.GetAvgHitPos(); + + // Print Data Row + std::cout << std::right << std::setw(12) << avTime + << std::setw(12) << avPos.x() + << std::setw(12) << avPos.y() + << std::setw(12) << avPos.z() + << std::endl; + } + std::cout << "=============================================================================" << std::endl; + } } - } } - } + // Restore default stream formatting + std::cout << std::scientific << std::setprecision(6); } From 7b2863a34639ddf90142e5740234ed5b8b3a522a Mon Sep 17 00:00:00 2001 From: sophieMu2e Date: Sat, 29 Nov 2025 15:15:39 -0600 Subject: [PATCH 06/24] improved documentation of the clusters and combohits adders, also fixed the hits into compount objects --- examples/nominal_MDC2025.fcl | 6 +- inc/CollectionFiller.hh | 193 +++++--- inc/DataInterface.hh | 5 +- inc/DataProduct.hh | 41 +- src/CollectionFiller.cc | 328 ++++++++------ src/DataInterface.cc | 847 ++++++++++++++++++++--------------- src/MainWindow.cc | 2 +- 7 files changed, 854 insertions(+), 568 deletions(-) diff --git a/examples/nominal_MDC2025.fcl b/examples/nominal_MDC2025.fcl index 8a28478..26db822 100644 --- a/examples/nominal_MDC2025.fcl +++ b/examples/nominal_MDC2025.fcl @@ -30,9 +30,9 @@ physics.analyzers.Mu2eEventDisplay.addCrystalHits : true physics.analyzers.Mu2eEventDisplay.filler.addHelixSeeds : false physics.analyzers.Mu2eEventDisplay.filler.addKalSeeds : true physics.analyzers.Mu2eEventDisplay.filler.addClusters : true -physics.analyzers.Mu2eEventDisplay.filler.addHits : false # adds ComboHits -physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : true -physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : true +physics.analyzers.Mu2eEventDisplay.filler.addHits : true # adds ComboHits +physics.analyzers.Mu2eEventDisplay.filler.addCrvClusters : false +physics.analyzers.Mu2eEventDisplay.filler.addCrvHits : false physics.analyzers.Mu2eEventDisplay.filler.addTimeClusters : false physics.analyzers.Mu2eEventDisplay.filler.addSimParts : false physics.analyzers.Mu2eEventDisplay.addTrkStrawHits : true diff --git a/inc/CollectionFiller.hh b/inc/CollectionFiller.hh index 1c12b86..266553b 100644 --- a/inc/CollectionFiller.hh +++ b/inc/CollectionFiller.hh @@ -14,77 +14,144 @@ #include #include "EventDisplay/inc/DataCollections.hh" - namespace mu2e{ - enum RecoDataProductName {ComboHits, CrvRecoPulses, TimeClusters, CaloClusters, HelixSeeds, KalSeeds, CosmicTrackSeeds, TrkHits, CrvCoincidenceCluster, CaloDigis}; - enum MCDataProductName {MCTrajectories, SurfaceSteps, SimParticles}; - class CollectionFiller - { + // --- Enumerations for Data Product Selection --- + + /** + * @brief Enum listing all reconstructed data products supported by the display. + * Used as a code in FillRecoCollections to retrieve a specific product type. + */ + enum RecoDataProductName { + ComboHits, CrvRecoPulses, TimeClusters, CaloClusters, HelixSeeds, + KalSeeds, CosmicTrackSeeds, TrkHits, CrvCoincidenceCluster, CaloDigis + }; + + /** + * @brief Enum listing all Monte Carlo data products supported by the display. + * Used as a code in FillMCCollections. + */ + enum MCDataProductName { + MCTrajectories, SurfaceSteps, SimParticles + }; + + /** + * @brief Class responsible for configuring data product retrieval from the Art event. + */ + class CollectionFiller + { public: - struct Config{ - using Name=fhicl::Name; - using Comment=fhicl::Comment; - fhicl::Atom diagLevel{Name("diagLevel"), Comment("for info"),0}; - fhicl::SequencechTag{Name("ComboHitCollection"),Comment("chTag")}; - fhicl::SequencetcTag{Name("TimeClusterCollection"),Comment("ttcTag")}; - fhicl::SequencecrvrecoTag{Name("CrvRecoPulseCollection"),Comment("crvTag")}; - fhicl::SequencecrvcoinTag{Name("CrvCoincidenceClusterCollection"),Comment("crvcoinTag")}; - fhicl::SequencecalodigTag{Name("CaloDigiCollection"),Comment("calodigTag")}; - fhicl::SequencecluTag{Name("CaloClusterCollection"),Comment("cluTag")}; - fhicl::SequencehelixSeedTag{Name("HelixSeedCollection"),Comment("helixseedTag")}; - fhicl::SequencekalSeedTag{Name("KalSeedPtrCollection"),Comment("kalseedTag")}; - fhicl::AtomcosmicTrackSeedTag{Name("CosmicTrackSeedCollection"),Comment("cosmicTrackSeedTag")}; - fhicl::SequenceMCTrajTag{Name("MCTrajectoryCollection"),Comment("MCTrajTag")}; - fhicl::SequenceSurfStepsTag{Name("SurfaceStepCollection"),Comment("SurfaceSteps Collection Tag")}; - fhicl::SequenceSimTag{Name("SimParticleCollection"),Comment("SimTag")}; - fhicl::Atom addHits{Name("addHits"), Comment("set to add the hits"),false}; - fhicl::Atom addCrvHits{Name("addCrvHits"), Comment("set to add crv hits"),false}; - fhicl::Atom addCrvClusters{Name("addCrvClusters"), Comment("set to add crv clusters"),false}; - fhicl::Atom addTimeClusters{Name("addTimeClusters"), Comment("set to add the Crv hits"),false}; - fhicl::Atom addTrkHits{Name("addTrkHits"), Comment("set to add the Trk hits"),false}; - fhicl::Atom addCaloDigis{Name("addCaloDigis"), Comment("set to add calodigis"),false}; - fhicl::Atom addClusters{Name("addClusters"), Comment("set to add caloclusters"),false}; - fhicl::Atom addHelixSeeds{Name("addHelixSeeds"), Comment("set to add helixseeds"),false}; - fhicl::Atom addKalSeeds{Name("addKalSeeds"), Comment("set to add kalseeds"),false}; - fhicl::Atom addCosmicTrackSeeds{Name("addCosmicTrackSeeds"), Comment("set to add cosmic track seeds"),false}; - fhicl::Atom addMCTraj{Name("addMCTraj"), Comment("set to add MCTrajectories"),false}; - fhicl::Atom addSurfSteps{Name("addSurfSteps"), Comment("set to add SurfaceStep MC"),false}; - fhicl::Atom addSimParts{Name("addSimParts"), Comment("set to add SimParticles MC"),false}; - fhicl::Atom FillAll{Name("FillAll"), Comment("to see all available products"), false}; - }; + // --- FHiCL Configuration Struct --- + /** + * @brief Defines the structure of the configuration parameters for CollectionFiller + * as read from the FHiCL file. + */ + struct Config{ + using Name=fhicl::Name; + using Comment=fhicl::Comment; + + // Debugging level + fhicl::Atom diagLevel{Name("diagLevel"), Comment("for info"),0}; + + // FHiCL Sequences for product tags (Art InputTags) - multiple instances allowed + fhicl::SequencechTag{Name("ComboHitCollection"),Comment("chTag")}; + fhicl::SequencetcTag{Name("TimeClusterCollection"),Comment("ttcTag")}; + fhicl::SequencecrvrecoTag{Name("CrvRecoPulseCollection"),Comment("crvTag")}; + fhicl::SequencecrvcoinTag{Name("CrvCoincidenceClusterCollection"),Comment("crvcoinTag")}; + fhicl::SequencecalodigTag{Name("CaloDigiCollection"),Comment("calodigTag")}; + fhicl::SequencecluTag{Name("CaloClusterCollection"),Comment("cluTag")}; + fhicl::SequencehelixSeedTag{Name("HelixSeedCollection"),Comment("helixseedTag")}; + fhicl::SequencekalSeedTag{Name("KalSeedPtrCollection"),Comment("kalseedTag")}; + + // FHiCL Atom for a single product tag (one instance expected) + fhicl::AtomcosmicTrackSeedTag{Name("CosmicTrackSeedCollection"),Comment("cosmicTrackSeedTag")}; + + // MC product tags + fhicl::SequenceMCTrajTag{Name("MCTrajectoryCollection"),Comment("MCTrajTag")}; + fhicl::SequenceSurfStepsTag{Name("SurfaceStepCollection"),Comment("SurfaceSteps Collection Tag")}; + fhicl::SequenceSimTag{Name("SimParticleCollection"),Comment("SimTag")}; + + // Boolean flags to enable/disable loading of specific collection types + fhicl::Atom addHits{Name("addHits"), Comment("set to add the hits"),false}; // Corresponds to ComboHits + fhicl::Atom addCrvHits{Name("addCrvHits"), Comment("set to add crv hits"),false}; // Corresponds to CrvRecoPulses + fhicl::Atom addCrvClusters{Name("addCrvClusters"), Comment("set to add crv clusters"),false}; // Corresponds to CrvCoincidenceCluster + fhicl::Atom addTimeClusters{Name("addTimeClusters"), Comment("set to add the Crv hits"),false}; + fhicl::Atom addTrkHits{Name("addTrkHits"), Comment("set to add the Trk hits"),false}; // Alias for ComboHits/TrkHits + fhicl::Atom addCaloDigis{Name("addCaloDigis"), Comment("set to add calodigis"),false}; + fhicl::Atom addClusters{Name("addClusters"), Comment("set to add caloclusters"),false}; + fhicl::Atom addHelixSeeds{Name("addHelixSeeds"), Comment("set to add helixseeds"),false}; + fhicl::Atom addKalSeeds{Name("addKalSeeds"), Comment("set to add kalseeds"),false}; + fhicl::Atom addCosmicTrackSeeds{Name("addCosmicTrackSeeds"), Comment("set to add cosmic track seeds"),false}; + fhicl::Atom addMCTraj{Name("addMCTraj"), Comment("set to add MCTrajectories"),false}; + fhicl::Atom addSurfSteps{Name("addSurfSteps"), Comment("set to add SurfaceStep MC"),false}; + fhicl::Atom addSimParts{Name("addSimParts"), Comment("set to add SimParticles MC"),false}; + + // Global flag to attempt retrieval of all collections found in the event + fhicl::Atom FillAll{Name("FillAll"), Comment("to see all available products"), false}; + }; - explicit CollectionFiller(const Config& conf); - CollectionFiller(const CollectionFiller &); - CollectionFiller& operator=(const CollectionFiller &); + // --- Constructors and Operators --- + + /** + * @brief Primary constructor; accepts and initializes member variables from the FHiCL Config struct. + * @param conf The FHiCL configuration object. + */ + explicit CollectionFiller(const Config& conf); + + // Disable copy constructor and assignment operator to prevent issues with Art pointers/handles + CollectionFiller(const CollectionFiller &); + CollectionFiller& operator=(const CollectionFiller &); - std::vector chTag_; - std::vector tcTag_; - std::vector crvrecoTag_; - std::vector crvcoinTag_; - std::vector calodigTag_; - std::vector cluTag_; - std::vector helixSeedTag_; - std::vector kalSeedTag_; - art::InputTag cosmicTrackSeedTag_; - std::vector MCTrajTag_; - std::vector SurfStepsTag_; - std::vector SimTag_; - art::Event *_event; - art::Run *_run; - bool addHits_, addCrvHits_, addCrvClusters_, addTimeClusters_, addTrkHits_, addCaloDigis_, addClusters_, addHelixSeeds_, addKalSeeds_, addCosmicTrackSeeds_, addMCTraj_, - addSurfSteps_, addSimParts_, FillAll_; - void FillRecoCollections(const art::Event& evt, DataCollections &data, RecoDataProductName code); - void FillMCCollections(const art::Event& evt, DataCollections &data, MCDataProductName code); - //template void test(const L& item); - //template int FillAllCollections(const art::Event& evt, std::vector>& list); - virtual ~CollectionFiller(){}; + // --- Input Tag Members (Filled from FHiCL config) --- + // These store the actual InputTags used by art::Event::getValidHandle(). + std::vector chTag_; + std::vector tcTag_; + std::vector crvrecoTag_; + std::vector crvcoinTag_; + std::vector calodigTag_; + std::vector cluTag_; + std::vector helixSeedTag_; + std::vector kalSeedTag_; + art::InputTag cosmicTrackSeedTag_; + std::vector MCTrajTag_; + std::vector SurfStepsTag_; + std::vector SimTag_; + + // --- Event/Run Pointers (Optional: may be set externally for easy access) --- + art::Event *_event; + art::Run *_run; + + // --- Boolean Control Flags (Copied from FHiCL Config) --- + bool addHits_, addCrvHits_, addCrvClusters_, addTimeClusters_, addTrkHits_, addCaloDigis_, + addClusters_, addHelixSeeds_, addKalSeeds_, addCosmicTrackSeeds_, addMCTraj_, + addSurfSteps_, addSimParts_, FillAll_; + + // --- Collection Retrieval Methods --- + + /** + * @brief Fills the DataCollections structure with requested reconstructed data products. + * @param evt The current Art event. + * @param data The structure to populate with product pointers and labels. + * @param code The specific RecoDataProductName to retrieve (if FillAll_ is false). + */ + void FillRecoCollections(const art::Event& evt, DataCollections &data, RecoDataProductName code); + + /** + * @brief Fills the DataCollections structure with requested Monte Carlo data products. + * @param evt The current Art event. + * @param data The structure to populate with product pointers and labels. + * @param code The specific MCDataProductName to retrieve (if FillAll_ is false). + */ + void FillMCCollections(const art::Event& evt, DataCollections &data, MCDataProductName code); + + // Default virtual destructor + virtual ~CollectionFiller(){}; private: - Config _conf; - - }; + // Copy of the FHiCL configuration object (if needed internally after construction) + Config _conf; + }; } #endif diff --git a/inc/DataInterface.hh b/inc/DataInterface.hh index c24d2e3..1248ab9 100644 --- a/inc/DataInterface.hh +++ b/inc/DataInterface.hh @@ -8,8 +8,10 @@ #include #include #include + #include #include +#include #include #include #include "Offline/RecoDataProducts/inc/CrvCoincidenceCluster.hh" @@ -57,7 +59,8 @@ namespace mu2e{ void AddTimeClusters(REX::REveManager *&eveMng, bool firstloop, std::tuple, std::vector> timecluster_tuple, REX::REveElement* &scene); void AddTrkHits(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> combohit_tuple, std::tuple, std::vector> track_tuple, REX::REveElement* &scene); void AddCaloDigis(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> calodigi_tuple, REX::REveElement* &scene); - void AddCaloClusters(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> calocluster_tuple, REX::REveElement* &scene, bool addCrystalDraw, double t1, double t2); + void AddCaloClusterLegend(REX::REveElement* scene, double t_ref); + void AddCaloClusters(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> calocluster_tuple, REX::REveElement* &scene, bool addCrystalDraw); void AddCRVInfo(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> crvpulse_tuple, REX::REveElement* &scene, bool extracted, bool addCRVBars); void AddCRVClusters(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> crvpulse_tuple, REX::REveElement* &scene, bool extracted, bool addCRVBars); void AddHelixSeedCollection(REX::REveManager *&eveMng,bool firstloop, std::tuple, std::vector> helix_tuple, REX::REveElement* &scene); diff --git a/inc/DataProduct.hh b/inc/DataProduct.hh index 8346a0f..ba611fa 100644 --- a/inc/DataProduct.hh +++ b/inc/DataProduct.hh @@ -2,19 +2,42 @@ #define DataProduct_HH_ #include - namespace mu2e { - class DataProduct { - public: - DataProduct(std::string name) : _name(name) {}; - DataProduct(){}; - std::string& name() { return _name; } + /** + * @brief A simple utility class to hold a string name for a data product. + * * In the context of a large experiment like Mu2e, this class might be used + * to consistently track the human-readable label (e.g., the Art InputTag) + * of a collection being visualized. + */ + class DataProduct { + public: + + /** + * @brief Constructor that initializes the name member. + * @param name The string name to assign to this data product. + */ + DataProduct(std::string name) : _name(name) {}; + + /** + * @brief Default constructor. Initializes name to an empty string. + */ + DataProduct(){}; + + /** + * @brief Accessor method for the private name member. + * Returns a reference, allowing the name to be modified (read/write access). + * @return std::string& A reference to the internal name string. + */ + std::string& name() { + return _name; + } - private: - std::string _name; + private: + // The private member storing the product's identifying name/label. + std::string _name; - }; + }; } // namespace mu2e #endif diff --git a/src/CollectionFiller.cc b/src/CollectionFiller.cc index f41e254..2273c5d 100644 --- a/src/CollectionFiller.cc +++ b/src/CollectionFiller.cc @@ -36,179 +36,225 @@ namespace mu2e{ FillAll_(conf.FillAll()) {} - template std::string TurnNameToString( const T& value ) + /** + * @brief Converts any object or variable that can be streamed (via operator<<) + * into a standard C++ string. + * + * This function uses a std::ostringstream to perform the conversion in a type-safe + * and generic manner, relying on the type T's definition of operator<<. + * + * @tparam T The type of the value to be converted (e.g., int, double, a custom class). + * @param value The value of type T to convert. + * @return std::string The string representation of the input value. + */ + template + std::string TurnNameToString( const T& value ) { - std::ostringstream ss; - ss << value; - return ss.str(); + // Create a std::ostringstream object. + std::ostringstream ss; + + // Insert the input 'value' into the string stream. + ss << value; + + // Extract the final string from the string stream buffer and return it. + return ss.str(); } - void CollectionFiller::FillRecoCollections(const art::Event& evt, DataCollections &data, RecoDataProductName CollectionName) { - if(FillAll_ or (CollectionName == ComboHits)){ - for(const auto &tag : chTag_){ - auto chH = evt.getValidHandle(tag); - data.chcol = chH.product(); - data.combohit_list.push_back(data.chcol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting ComboHit Instance: "<(tag); + + data.chcol = chH.product(); + data.combohit_list.push_back(data.chcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting ComboHit Instance: "<(tag); - data.crvrecocol = chH.product(); - data.crvpulse_list.push_back(data.crvrecocol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting Crv Instance: "<(tag); + data.crvrecocol = chH.product(); + data.crvpulse_list.push_back(data.crvrecocol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting Crv Instance: "<(tag); - data.crvcoincol = chH.product(); - data.crvcoin_list.push_back(data.crvcoincol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting Crv Instance: "<(tag); + data.crvcoincol = chH.product(); + data.crvcoin_list.push_back(data.crvcoincol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting Crv Instance: "<(tag); - data.tccol = chH.product(); - data.timecluster_list.push_back(data.tccol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting TimeCluster Instance: "<(tag); + data.tccol = chH.product(); + data.timecluster_list.push_back(data.tccol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting TimeCluster Instance: "<(tag); - data.chcol = chH.product(); - data.combohit_list.push_back(data.chcol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting TrkHit Instance: "<(tag); + data.chcol = chH.product(); + data.combohit_list.push_back(data.chcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting TrkHit Instance: "<(tag); - data.calodigicol = chH.product(); - data.calodigi_list.push_back(data.calodigicol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting CaloDigi Instance: "<(tag); + data.calodigicol = chH.product(); + data.calodigi_list.push_back(data.calodigicol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting CaloDigi Instance: "<(tag); - data.clustercol = chH.product(); - data.calocluster_list.push_back(data.clustercol); - std::string name = TurnNameToString(tag); - std::cout<<"Plotting CaloCluster Instance: "<(tag); + data.clustercol = chH.product(); + data.calocluster_list.push_back(data.clustercol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting CaloCluster Instance: "<(tag); - data.helixSeedcol = chH.product(); - data.helix_list.push_back(data.helixSeedcol); - - std::string name = TurnNameToString(tag); - std::cout<<"Plotting HelixSeed Instance: "<(tag); + data.helixSeedcol = chH.product(); + data.helix_list.push_back(data.helixSeedcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting HelixSeed Instance: "<(tag); - data.kalSeedcol = chH.product(); - data.track_list.push_back(data.kalSeedcol); - - std::string name = TurnNameToString(tag); - std::cout<<"Plotting KalSeed Instance: "<(tag); + data.kalSeedcol = chH.product(); + data.track_list.push_back(data.kalSeedcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting KalSeed Instance: "<(cosmicTrackSeedTag_); - data.CosmicTrackSeedcol = chH.product(); + + // --- 10. CosmicTrackSeeds (Cosmic Ray Track Candidates) --- + if(FillAll_ or (CollectionName == CosmicTrackSeeds)){ + auto chH = evt.getValidHandle(cosmicTrackSeedTag_); + data.CosmicTrackSeedcol = chH.product(); } - } +} - void CollectionFiller::FillMCCollections(const art::Event& evt, DataCollections &data, MCDataProductName CollectionName){ +/** + * @brief Retrieves specified Monte Carlo data products from the Art event. + * * It supports retrieving multiple instances of the same collection produced by + * different simulation stages or modules by iterating over Art tags. + * @param evt The current art::Event object. + * @param data The DataCollections structure where pointers and labels are stored. + * @param CollectionName An enum value used to selectively fill one type of MC collection. + */ +void CollectionFiller::FillMCCollections(const art::Event& evt, DataCollections &data, MCDataProductName CollectionName){ + // --- 1. MCTrajectories (Full Monte Carlo Particle Paths) --- if(FillAll_ or (CollectionName==MCTrajectories)){ - for(const auto &tag : MCTrajTag_){ - auto chH = evt.getValidHandle(tag); - data.mctrajcol = chH.product(); - data.mctrack_list.push_back(data.mctrajcol); - - std::string name = TurnNameToString(tag); - std::cout<<"Plotting MCTrajectory Instance: "<(tag); + + data.mctrajcol = chH.product(); + data.mctrack_list.push_back(data.mctrajcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting MCTrajectory Instance: "<(tag); - data.surfstepcol = chH.product(); - data.surfstep_list.push_back(data.surfstepcol); - - std::string name = TurnNameToString(tag); - std::cout<<"Plotting SurfaceStep Instance: "<(tag); + + data.surfstepcol = chH.product(); + data.surfstep_list.push_back(data.surfstepcol); + std::string name = TurnNameToString(tag); + std::cout<<"Plotting SurfaceStep Instance: "<(tag); + + data.simcol = chH.product(); + data.sim_list.push_back(data.simcol); - for(const auto &tag : SimTag_){ - auto chH = evt.getValidHandle(tag); - data.simcol = chH.product(); - data.sim_list.push_back(data.simcol); - - std::string name = TurnNameToString(tag); - std::cout<<"Plotting SimParticle Instance: "<, std::vector> calodigi_tuple, REX::REveElement* &scene){ - - std::cout<<"[DataInterface] AddCaloDigi "< calodigi_list = std::get<1>(calodigi_tuple); - std::vector names = std::get<0>(calodigi_tuple); - std::cout<<"calo digi size = "<size() != 0){ - if(!firstLoop_){ - scene->DestroyElements();; - } - mu2e::Calorimeter const &cal = *(mu2e::GeomHandle()); - std::cout<<"calodigi col size = "<size()<size(); i++){ - mu2e::CaloDigi const &digi= (*calodigicol)[i]; - int sipmID = digi.SiPMID(); - //bool isEven = sipmID % 2; - int cryID = sipmID/2; - std::cout<<"crystal ID "< det; - double zpos = 0; - double diskID = 0; - if(cryID < 674) { - zpos = 186.53; - diskID = 0; - } - if(cryID >= 674) { - zpos = 256.53; - diskID = 1; +/** + * @brief Adds reconstructed CaloDigi data products to the REve visualization scene. + * * * Digis are visualized as points (crystal center) and 3D boxes. + * * Elements are colored based on the t0 time of the digitization pulse. + * + * @param eveMng The global REX::REveManager instance. + * @param firstLoop_ Boolean flag, true if this is the first event/drawing iteration. + * @param calodigi_tuple The tuple containing vector of labels and CaloDigiCollection pointers. + * @param scene The REX::REveElement representing the digi scene/container. + */ +void DataInterface::AddCaloDigis(REX::REveManager *&eveMng, bool firstLoop_, + std::tuple, + std::vector> calodigi_tuple, + REX::REveElement* &scene){ + + std::cout << "[DataInterface] AddCaloDigi: Restoring Original Z-Position Logic" << std::endl; + std::vector calodigi_list = std::get<1>(calodigi_tuple); + std::vector names = std::get<0>(calodigi_tuple); + + // --- 1. Find the Global Time Range (min/max t0) --- + double max_t0 = -1e6; + double min_t0 = 1e6; + bool found_digis = false; + + for(const auto* calodigicol : calodigi_list){ + if(calodigicol && !calodigicol->empty()){ + found_digis = true; + for(const auto& digi : *calodigicol){ + double t0 = digi.t0(); + if(t0 > max_t0) max_t0 = t0; + if(t0 < min_t0) min_t0 = t0; + } } - CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(diskID,crystal.position()) ; - //CLHEP::Hep3Vector pointInMu2e = det->toMu2e(crystalPos); - - std::string crytitle = "Crystal ID = " + std::to_string(cryID) + '\n' - + " Time = " + std::to_string(digi.t0())+" ns "; - char const *crytitle_c = crytitle.c_str(); + } - std::string name = "disk " + std::to_string(diskID); + if (!found_digis) { + std::cout << "[DataInterface] No valid CaloDigis found." << std::endl; + return; + } + + if (max_t0 == min_t0) { + max_t0 += 1.0; + } - std::string label = " CaloDigi Instance = " + names[0] + '\n' - + " SiPM. = "+std::to_string(sipmID)+ '\n' - + " t0 = "+std::to_string(digi.t0())+" ns " + '\n' - + " peakPos = "+std::to_string(digi.peakpos()); - auto ps1 = new REX::REvePointSet(label,label, 0); - auto ps2 = new REX::REvePointSet(label, label, 0); + // --- 2. Define the Color Gradient (Palette) --- + const Int_t NRGBs = 5; + const Int_t NCont = 255; + Double_t stops[NRGBs] = { 0.00, 0.34, 0.61, 0.84, 1.00 }; + Double_t red[NRGBs] = { 0.00, 0.00, 0.87, 1.00, 0.51 }; + Double_t green[NRGBs] = { 0.00, 0.81, 1.00, 0.20, 0.00 }; + Double_t blue[NRGBs] = { 0.51, 1.00, 0.12, 0.00, 0.00 }; + + TColor::CreateGradientColorTable(NRGBs, stops, red, green, blue, NCont); + gStyle->SetNumberContours(NCont); + + // --- 3. Visualization Loop --- + for(unsigned int j = 0; j < calodigi_list.size(); j++){ + + const CaloDigiCollection* calodigicol = calodigi_list[j]; - // Set positions of clusters - if(diskID == 0) - ps1->SetNextPoint(pointmmTocm(crystalPos.x()), pointmmTocm(crystalPos.y()) , zpos ); - if(diskID == 1) - ps2->SetNextPoint(pointmmTocm(crystalPos.x()), pointmmTocm(crystalPos.y()) , zpos ); - - Color_t color = kRed; - - ps1->SetMarkerColor(color); - ps1->SetMarkerStyle(DataInterface::mstyle); - ps1->SetMarkerSize(DataInterface::msize); - - ps2->SetMarkerColor(color); - ps2->SetMarkerStyle(DataInterface::mstyle); - ps2->SetMarkerSize(DataInterface::msize); - - // Add to REve world - scene->AddElement(ps1); - scene->AddElement(ps2); - - // plot the crystals which are present in this event: - auto b = new REX::REveBox(crytitle_c,crytitle_c); - b->SetMainColor(color); - double width = crystalXLen/2; - double height = crystalYLen/2; - double thickness = crystalZLen/2; - b->SetVertex(0, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height ,pointmmTocm(crystalPos.z())- thickness + 2*thickness + zpos);//--- - b->SetVertex(1, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height, pointmmTocm(crystalPos.z())- thickness + 2*thickness +zpos);//-+- - b->SetVertex(2, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height ,pointmmTocm(crystalPos.z())- thickness + 2*thickness + zpos);//++- - b->SetVertex(3, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, pointmmTocm(crystalPos.z())-thickness + 2*thickness + zpos);//+-- - b->SetVertex(4, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height ,pointmmTocm(crystalPos.z())+ thickness + 2*thickness + zpos);//--+ - b->SetVertex(5, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height , pointmmTocm(crystalPos.z())+ thickness + 2*thickness + zpos);//-++ - b->SetVertex(6, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height , pointmmTocm(crystalPos.z()) + thickness + 2*thickness+zpos); //+++ - b->SetVertex(7, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, pointmmTocm(crystalPos.z())+ thickness + 2*thickness + zpos);//+-+ - allcryhits->AddElement(b); - //scene->AddElement(b); - } - scene->AddElement(allcryhits); + if(calodigicol->size() != 0){ + if(!firstLoop_){ + scene->DestroyElements();; + } + + mu2e::Calorimeter const &cal = *(mu2e::GeomHandle()); + GeomHandle det; + + auto allcryhits = new REX::REveCompound(("CaloDigiCollection_" + names[j]).c_str(), + ("CaloDigi Collection: " + names[j]).c_str(), 1); + + for(unsigned int i = 0; i < calodigicol->size(); i++){ + mu2e::CaloDigi const &digi = (*calodigicol)[i]; + int sipmID = digi.SiPMID(); + int cryID = sipmID / 2; + + Crystal const &crystal = cal.crystal(cryID); + + // --- Calculate Color based on t0 --- + Color_t color; + double normalized_t0 = (digi.t0() - min_t0) / (max_t0 - min_t0); + int colorIdx = static_cast(normalized_t0 * (NCont - 1)); + color = gStyle->GetColorPalette(colorIdx); + + // --- Geometry and Position (Matching Original Logic) --- + double crystalXLen = pointmmTocm(crystal.size().x()); + double crystalYLen = pointmmTocm(crystal.size().y()); + double crystalZLen = pointmmTocm(crystal.size().z()); + + double zpos = 0; + double diskID = 0; + if(cryID < 674) { + zpos = 186.53; + diskID = 0; + } + if(cryID >= 674) { + zpos = 256.53; + diskID = 1; + } + // Convert fixed zpos to cm for REve + double fixed_zpos_cm = zpos;//pointmmTocm(zpos); + + // Get crystal position in its local Mu2e disk frame (still in mm in Hep3Vector) + CLHEP::Hep3Vector crystalPos_local_mm = cal.geomUtil().mu2eToDisk(diskID, crystal.position()); + + // --- Label --- + std::string label = " CaloDigi Instance = " + names[j] + '\n' + + " Crystal ID = " + std::to_string(cryID) + '\n' + + " SiPM. = "+std::to_string(sipmID)+ '\n' + + " t0 = "+std::to_string(digi.t0())+" ns " + '\n' + + " peakPos = "+std::to_string(digi.peakpos()); + + // --- A. Draw Crystal Center (Point Set) --- + auto ps1 = new REX::REvePointSet(label.c_str(), label.c_str(), 0); + auto ps2 = new REX::REvePointSet(label.c_str(), label.c_str(), 0); + + // The point sets use the fixed global Z position. + if(diskID == 0) + ps1->SetNextPoint(pointmmTocm(crystalPos_local_mm.x()), pointmmTocm(crystalPos_local_mm.y()), fixed_zpos_cm ); + if(diskID == 1) + ps2->SetNextPoint(pointmmTocm(crystalPos_local_mm.x()), pointmmTocm(crystalPos_local_mm.y()), fixed_zpos_cm ); + + ps1->SetMarkerColor(color); + ps1->SetMarkerStyle(DataInterface::mstyle); + ps1->SetMarkerSize(DataInterface::msize); + + ps2->SetMarkerColor(color); + ps2->SetMarkerStyle(DataInterface::mstyle); + ps2->SetMarkerSize(DataInterface::msize); + + scene->AddElement(ps1); + scene->AddElement(ps2); + + // --- B. Draw Crystal Volume (REveBox) --- + std::string crytitle = "Crystal ID = " + std::to_string(cryID) + '\n' + + " Time = " + std::to_string(digi.t0()) + " ns "; + + auto b = new REX::REveBox(crytitle.c_str(), crytitle.c_str()); + b->SetMainColor(color); + + double width = crystalXLen / 2.0; + double height = crystalYLen / 2.0; + double thickness = crystalZLen / 2.0; + + // Z-Terms (All in cm): + double Z_local_cm = pointmmTocm(crystalPos_local_mm.z()); + + // The full Z offset term from your original logic: 2*thickness + fixed_zpos_cm + double Z_offset = 2.0 * thickness + fixed_zpos_cm; + + // Front Face + b->SetVertex(0, pointmmTocm(crystalPos_local_mm.x()) - width, pointmmTocm(crystalPos_local_mm.y()) - height, Z_local_cm - thickness + Z_offset); + b->SetVertex(1, pointmmTocm(crystalPos_local_mm.x()) - width, pointmmTocm(crystalPos_local_mm.y()) + height, Z_local_cm - thickness + Z_offset); + b->SetVertex(2, pointmmTocm(crystalPos_local_mm.x()) + width, pointmmTocm(crystalPos_local_mm.y()) + height, Z_local_cm - thickness + Z_offset); + b->SetVertex(3, pointmmTocm(crystalPos_local_mm.x()) + width, pointmmTocm(crystalPos_local_mm.y()) - height, Z_local_cm - thickness + Z_offset); + + // Back Face (Z_local_cm + thickness + Z_offset) + b->SetVertex(4, pointmmTocm(crystalPos_local_mm.x()) - width, pointmmTocm(crystalPos_local_mm.y()) - height, Z_local_cm + thickness + Z_offset); + b->SetVertex(5, pointmmTocm(crystalPos_local_mm.x()) - width, pointmmTocm(crystalPos_local_mm.y()) + height, Z_local_cm + thickness + Z_offset); + b->SetVertex(6, pointmmTocm(crystalPos_local_mm.x()) + width, pointmmTocm(crystalPos_local_mm.y()) + height, Z_local_cm + thickness + Z_offset); + b->SetVertex(7, pointmmTocm(crystalPos_local_mm.x()) + width, pointmmTocm(crystalPos_local_mm.y()) - height, Z_local_cm + thickness + Z_offset); + + allcryhits->AddElement(b); + } + scene->AddElement(allcryhits); + } } - } } -void DataInterface::AddCaloClusters(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> calocluster_tuple, REX::REveElement* &scene, bool addCrystalDraw, double t1, double t2){ - - std::cout<<"[DataInterface] AddCaloClusters "< calocluster_list = std::get<1>(calocluster_tuple); - std::vector names = std::get<0>(calocluster_tuple); - for(unsigned int j = 0; j< calocluster_list.size(); j++){ - - // Extract cluster list - const CaloClusterCollection* clustercol = calocluster_list[j]; - if(clustercol->size() != 0){ - if(!firstLoop_){ - scene->DestroyElements();; - } - Color_t color = kWhite; - mu2e::Calorimeter const &cal = *(mu2e::GeomHandle()); - GeomHandle det; - // Add crystals - double maxE = 1e-6; - double minE = 1000; - using CCCPtr = const CaloCluster*; - std::list cluList; - //std::array colorList {kViolet, kBlue, kBlue, kGreen, kGreen, kYellow, kYellow, kOrange, kOrange, kRed}; - //std::vector colors; - - for(unsigned int i = 0; i < clustercol->size(); i++){ - auto const& cluster= (*clustercol)[i]; - cluList.push_back(&cluster); - if(cluster.energyDep() > maxE) maxE = cluster.energyDep(); - if(cluster.energyDep() < minE) minE = cluster.energyDep(); - - // sort in energy - cluList.sort([] (const CaloCluster* lhs, const CaloCluster* rhs) {return lhs->energyDep() > rhs->energyDep();} ); - } - /*if(cluList.size() <= 2){ - Color_t color; - for(auto const& cluster : cluList){ - if(cluster.energyDep() == minE){ - color = kViolet; - colors.push_back(color); - } - if(cluster.energyDep() == maxE){ - color = kRed; - colors.push_back(color); - } - } - } - unsigned int c = -1; - if(cluList.size() > 2){ - for(auto const& cluster : cluList){ - c+=1; - Color_t color; - if(cluster.energyDep() == minE){ - color = kViolet; - colors.push_back(color); - } - if(cluster.energyDep() == maxE){ - color = kRed; - colors.push_back(color); - } - if (c > 1 and c < colorList.size()-1){ - color = colorList.at(c); - colors.push_back(color); - } - if (c > 1 and c >= colorList.size()-1){ - color = kRed; - colors.push_back(color); - } +void DataInterface::AddCaloClusters(REX::REveManager *&eveMng, bool firstLoop_, + std::tuple, + std::vector> calocluster_tuple, + REX::REveElement* &scene, bool addCrystalDraw){ // t1 and t2 are now ignored + + std::cout << "[DataInterface] AddCaloClusters: Coloring by Proximity to Max-Energy Cluster Time" << std::endl; + std::vector calocluster_list = std::get<1>(calocluster_tuple); + std::vector names = std::get<0>(calocluster_tuple); + + // --- Time Difference Color Palette (Simplified Discrete Steps) --- + // This defines how clusters are colored based on their time separation from t_ref. + // Near: Red (Prompt) -> Mid: Orange/Yellow -> Far: Green/Blue/Violet (Late/Background) + + // --- 1. Find the Global Reference Time (t_ref) --- + double maxE = 1e-6; + double t_ref = -1.0; + const mu2e::CaloCluster* refCluster = nullptr; // Pointer to the cluster that sets t_ref + + // First, iterate over ALL clusters in all collections to find the single most energetic one. + for(const auto* clustercol : calocluster_list){ + if(clustercol && !clustercol->empty()){ + for(const auto& cluster : *clustercol){ + if(cluster.energyDep() > maxE){ + maxE = cluster.energyDep(); + t_ref = cluster.time(); + refCluster = &cluster; + } + } } - } */ - - unsigned int i = 0; - for(auto cptr : cluList){ - auto const& cluster = *cptr; - //mu2e::CaloCluster const &cluster= cluList.at(i); - i += 1; - - // Info for label: - std::string cluster_energy = std::to_string(cluster.energyDep()); - std::string cluster_time = std::to_string(cluster.time()); - std::string cluster_x = std::to_string(cluster.cog3Vector().x()); - std::string cluster_y = std::to_string(cluster.cog3Vector().y()); - // Extract center of gravity, convert to coord sys - CLHEP::Hep3Vector COG(cluster.cog3Vector().x(),cluster.cog3Vector().y(), cluster.cog3Vector().z()); - CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(cluster.diskID(),COG); - CLHEP::Hep3Vector pointInMu2e = det->toMu2e(crystalPos); - // Info for label - std::string cluster_z = std::to_string(abs(pointInMu2e.z())); - // Make label and REve objects - std::string label = " Instance = " + names[0] + '\n' - + " Energy Dep. = "+cluster_energy+" MeV "+ '\n' - + " Time = "+cluster_time+" ns " + '\n' - + " Pos = ("+cluster_x+","+cluster_y+","+cluster_z+") mm"; - std::string name = "disk" + std::to_string(cluster.diskID()) + label; - auto ps1 = new REX::REvePointSet(name, "CaloClusters Disk 1: "+label,0); - auto ps2 = new REX::REvePointSet(name, "CaloClusters Disk 2: "+label,0); - - - // Set positions of clusters - if(cluster.diskID() == 0) - ps1->SetNextPoint(pointmmTocm(COG.x()), pointmmTocm(COG.y()) , abs(pointmmTocm(pointInMu2e.z()))); - if(cluster.diskID() == 1) - ps2->SetNextPoint(pointmmTocm(COG.x()), pointmmTocm(COG.y()) , abs(pointmmTocm(pointInMu2e.z()))); - - // Set draw options - /*TColor color1; - color1.SetPalette(1,0); - Color_t color = color1.GetNumber();// colors[i];*/ - - if(abs(cluster.time() - t2) < 20) color = kRed; - if(abs(cluster.time() - t2) >= 20 and abs(cluster.time() - t2) < 100) color = kOrange; - if(abs(cluster.time() - t2) >= 100 and abs(cluster.time() - t2) < 200) color = kYellow; - if(abs(cluster.time() - t2) >= 200 and abs(cluster.time() - t2) < 400) color = kGreen; - if(abs(cluster.time() - t2) >= 400 and abs(cluster.time() - t2) < 600) color = kBlue; - if(abs(cluster.time() - t2) >= 600 ) color = kViolet; - - ps1->SetMarkerColor(color); - ps1->SetMarkerStyle(DataInterface::mstyle); - ps1->SetMarkerSize(DataInterface::msize); - - ps2->SetMarkerColor(color); - ps2->SetMarkerStyle(DataInterface::mstyle); - ps2->SetMarkerSize(DataInterface::msize); - - // Add to REve world - scene->AddElement(ps1); - scene->AddElement(ps2); - - // Add crystals - if(addCrystalDraw){ - auto allcryhits = new REX::REveCompound("CrystalHits for Cluster "+std::to_string(i),"CrystalHits for Cluster "+std::to_string(i),1); - for(unsigned h =0 ; h < cluster.caloHitsPtrVector().size();h++) { - art::Ptr crystalhit = cluster.caloHitsPtrVector()[h]; - int cryID = crystalhit->crystalID(); + } - //int diskID = cal.crystal(crystalhit->crystalID()).diskID(); - Crystal const &crystal = cal.crystal(cryID); - double crystalXLen = pointmmTocm(crystal.size().x()); - double crystalYLen = pointmmTocm(crystal.size().y()); - double crystalZLen = pointmmTocm(crystal.size().z()); + // Check if a reference time was found (i.e., at least one cluster exists) + if(t_ref < 0.0){ + std::cout << "[DataInterface] No valid CaloClusters found to set a reference time." << std::endl; + return; + } + //AddCaloClusterLegend(scene, t_ref); TODO - issue with header file + // --- 2. Visualization Loop --- + for(unsigned int j = 0; j< calocluster_list.size(); j++){ + const CaloClusterCollection* clustercol = calocluster_list[j]; + + if(clustercol->size() != 0){ + if(!firstLoop_){ + scene->DestroyElements();; + } + + // Get Geometry Handles + mu2e::Calorimeter const &cal = *(mu2e::GeomHandle()); GeomHandle det; - CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(cluster.diskID(),crystal.position()) ; - - // make title - std::string crytitle = "disk"+std::to_string(cal.crystal(crystalhit->crystalID()).diskID()) + " Crystal Hit = " + std::to_string(cryID) + '\n' - + " Energy Dep. = "+std::to_string(crystalhit->energyDep())+" MeV "+ '\n' - + " Time = "+std::to_string(crystalhit->time())+" ns "; - char const *crytitle_c = crytitle.c_str(); - auto b = new REX::REveBox(crytitle_c,crytitle_c); - - // plot the crystals which are present in this event in lego: - b->SetMainColor(color);//s[i]); - - - double width = crystalXLen/2; - double height = crystalYLen/2; - double thickness = crystalhit->energyDep()/maxE * crystalZLen/2; //length proportional to energy - - b->SetVertex(0, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height ,pointmmTocm(crystalPos.z())- thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//--- - b->SetVertex(1, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height, pointmmTocm(crystalPos.z())- thickness +abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//-+- - b->SetVertex(2, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height ,pointmmTocm(crystalPos.z())- thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//++- - b->SetVertex(3, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, pointmmTocm(crystalPos.z())-thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//+-- - b->SetVertex(4, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height ,pointmmTocm(crystalPos.z())+ thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//--+ - b->SetVertex(5, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height , pointmmTocm(crystalPos.z())+ thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//-++ - b->SetVertex(6, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height , pointmmTocm(crystalPos.z()) + thickness +abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2); //+++ - b->SetVertex(7,pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, pointmmTocm(crystalPos.z())+ thickness + abs(pointmmTocm(pointInMu2e.z()))+crystalZLen/2);//+-+ - allcryhits->AddElement(b); - } - scene->AddElement(allcryhits); - } + + // maxE is used here to normalize the crystal box height for the optional Lego plot + int i = 0; + // Iterate over the clusters in the current collection + for(const auto& cluster : *clustercol){ + i++; + Color_t color = kWhite; // Default + double markerSize = DataInterface::msize; + + // Calculate time difference + double time_diff = abs(cluster.time() - t_ref); + + // --- Set Color based on Time Proximity to t_ref --- + if(&cluster == refCluster){ + // This is the cluster that set t_ref. Mark it clearly. + color = kWhite; + markerSize = DataInterface::msize + 1.0; // Make it larger + } + // Clusters close in time to the reference (likely prompt) + else if(time_diff < 100.0) { // e.g., within +/- 15 ns + color = kRed; + markerSize = DataInterface::msize; + } + else if(time_diff < 250.0) { // e.g., 15-50 ns + color = kOrange; + markerSize = DataInterface::msize; + } + // Clusters far in time (likely background or secondary activity) + else if(time_diff < 500.0) { // e.g., 50-200 ns + color = kYellow; + markerSize = DataInterface::msize - 0.5; + } + else { // > 200 ns + color = kGreen + 2; + markerSize = DataInterface::msize - 1.0; + } - } + std::string cluster_energy = std::to_string(cluster.energyDep()); + std::string cluster_time = std::to_string(cluster.time()); + std::string cluster_x = std::to_string(cluster.cog3Vector().x()); + std::string cluster_y = std::to_string(cluster.cog3Vector().y()); + + CLHEP::Hep3Vector COG(cluster.cog3Vector().x(),cluster.cog3Vector().y(), cluster.cog3Vector().z()); + + CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(cluster.diskID(),COG); + CLHEP::Hep3Vector pointInMu2e = det->toMu2e(crystalPos); + + std::string cluster_z = std::to_string(abs(pointInMu2e.z())); + + std::string label = " Instance = " + names[0] + '\n' + + " E = "+cluster_energy+" MeV " + '\n' + + " Time = "+cluster_time+" ns " + '\n' + + " |dt| from Emax = " + std::to_string(time_diff) + " ns " + '\n' + + " Pos = ("+cluster_x+","+cluster_y+","+cluster_z+") mm"; + + std::string name = "disk" + std::to_string(cluster.diskID()) + label; + auto ps1 = new REX::REvePointSet(name, "CaloClusters Disk 1: "+label,0); + auto ps2 = new REX::REvePointSet(name, "CaloClusters Disk 2: "+label,0); + + + // --- Set Positions, Color, and Size --- + if(cluster.diskID() == 0) + ps1->SetNextPoint(pointmmTocm(COG.x()), pointmmTocm(COG.y()) , abs(pointmmTocm(pointInMu2e.z()))); + if(cluster.diskID() == 1) + ps2->SetNextPoint(pointmmTocm(COG.x()), pointmmTocm(COG.y()) , abs(pointmmTocm(pointInMu2e.z()))); + + ps1->SetMarkerColor(color); + ps1->SetMarkerStyle(DataInterface::mstyle); + ps1->SetMarkerSize(markerSize); + + ps2->SetMarkerColor(color); + ps2->SetMarkerStyle(DataInterface::mstyle); + ps2->SetMarkerSize(markerSize); + + scene->AddElement(ps1); + scene->AddElement(ps2); + + // --- 3. Optional: Draw Contributing Crystals (Lego Plot) --- + if(addCrystalDraw){ + auto allcryhits = new REX::REveCompound("CrystalHits for Cluster "+std::to_string(cluster.diskID())+"_"+std::to_string(i),"CrystalHits for Cluster "+std::to_string(cluster.diskID())+"_"+std::to_string(i),1); + + for(unsigned h =0 ; h < cluster.caloHitsPtrVector().size();h++) { + art::Ptr crystalhit = cluster.caloHitsPtrVector()[h]; + int cryID = crystalhit->crystalID(); + + Crystal const &crystal = cal.crystal(cryID); + double crystalXLen = pointmmTocm(crystal.size().x()); + double crystalYLen = pointmmTocm(crystal.size().y()); + double crystalZLen = pointmmTocm(crystal.size().z()); + + CLHEP::Hep3Vector crystalPos = cal.geomUtil().mu2eToDisk(cluster.diskID(),crystal.position()) ; + + std::string crytitle = "disk"+std::to_string(cal.crystal(crystalhit->crystalID()).diskID()) + " Crystal Hit = " + std::to_string(cryID) + '\n' + + " Energy Dep. = "+std::to_string(crystalhit->energyDep())+" MeV " + '\n' + + " Time = "+std::to_string(crystalhit->time())+" ns "; + + char const *crytitle_c = crytitle.c_str(); + auto b = new REX::REveBox(crytitle_c,crytitle_c); + + b->SetMainColor(color); // Use the time-proximity color + + double width = crystalXLen/2; + double height = crystalYLen/2; + + // Z proportional to energy (normalized by the global max E found earlier) + double thickness = crystalhit->energyDep()/maxE * crystalZLen/2; + + // Calculate Z offset + double crystalZOffset = pointmmTocm(crystalPos.z()) + abs(pointmmTocm(pointInMu2e.z())) + crystalZLen/2; + + // Set the 8 vertices of the REveBox object + b->SetVertex(0, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height , crystalZOffset - thickness); + b->SetVertex(1, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height, crystalZOffset - thickness); + b->SetVertex(2, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height ,crystalZOffset - thickness); + b->SetVertex(3, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, crystalZOffset - thickness); + b->SetVertex(4, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())- height ,crystalZOffset + thickness); + b->SetVertex(5, pointmmTocm(crystalPos.x()) - width, pointmmTocm(crystalPos.y())+ height , crystalZOffset + thickness); + b->SetVertex(6, pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())+ height , crystalZOffset + thickness); + b->SetVertex(7,pointmmTocm(crystalPos.x()) + width, pointmmTocm(crystalPos.y())- height, crystalZOffset + thickness); + + allcryhits->AddElement(b); + } + scene->AddElement(allcryhits); + } + } + } } - } } -void DataInterface::AddComboHits(REX::REveManager *&eveMng, bool firstLoop_, std::tuple, std::vector> combohit_tuple, REX::REveElement* &scene, bool strawdisplay, bool AddErrorBar_){ - - std::vector combohit_list = std::get<1>(combohit_tuple); - std::vector names = std::get<0>(combohit_tuple); - - // Loop over hit lists - for(unsigned int j = 0; j < combohit_list.size(); j++){ - const ComboHitCollection* chcol = combohit_list[j]; - int colour = (j+3); - if(chcol->size() !=0 ){ - // Loop over hits - for(unsigned int i=0; i< chcol->size(); i++){ - mu2e::ComboHit const &hit= (*chcol)[i]; - // Display hit straws if selected too in FCL file - if(strawdisplay){ - mu2e::GeomHandle tracker; - const auto& allStraws = tracker->getStraws(); - int sid = hit._sid.asUint16(); - CLHEP::Hep3Vector sposi(0.0,0.0,0.0), sposf(0.0,0.0,0.0); - const mu2e::Straw& s = allStraws[sid]; - const CLHEP::Hep3Vector& p = s.getMidPoint(); - const CLHEP::Hep3Vector& d = s.getDirection(); - double x = p.x(); - double y = p.y(); - double z = p.z(); - double l = s.halfLength(); - double st=sin(d.theta()); - double ct=cos(d.theta()); - double sp=sin(d.phi()+TMath::Pi()/2.0); - double cp=cos(d.phi()+TMath::Pi()/2.0); - if(sid < drawconfig.getInt("maxSID")){ - double x1=x+l*st*sp; - double y1=y-l*st*cp; - double z1=z+l*ct; - double x2=x-l*st*sp; - double y2=y+l*st*cp; - double z2=z-l*ct; - std::string strawtitle; - int idStraw = s.id().getStraw(); - int idPanel = s.id().getPanel(); - int idPlane = s.id().getPlane(); - int colorid = idPlane + idPanel; - strawtitle =Form("Straw %i Panel %i Plane %i",idStraw,idPanel,idPlane); - sposi.set(x1, y1, z1); - sposf.set(x2, y2, z2); - if(sposi.x()!=0){ - auto strawline = new REX::REveLine("StrawHit ",strawtitle,2); - strawline->SetPoint(0,pointmmTocm(sposi.x()),pointmmTocm(sposi.y()),pointmmTocm(sposi.z())); - strawline->SetNextPoint(pointmmTocm(sposf.x()),pointmmTocm(sposf.y()),pointmmTocm(sposf.z())); - strawline->SetLineWidth(1); - strawline->SetLineColor(colorid); - if(strawline->GetSize() !=0 ) scene->AddElement(strawline); +//FIXME if firstloop never used remove it +void DataInterface::AddComboHits(REX::REveManager *&eveMng, bool firstLoop_, + std::tuple, + std::vector> combohit_tuple, + REX::REveElement* &scene, + bool strawdisplay, bool AddErrorBar_) { + + std::vector combohit_list = std::get<1>(combohit_tuple); + std::vector names = std::get<0>(combohit_tuple); + + // --- 1. Find the Global Time Range (min/max time) --- + double max_time = -1e6; + double min_time = 1e6; + bool found_hits = false; + + for(const auto* chcol : combohit_list){ + if(chcol && !chcol->empty()){ + found_hits = true; + for(const auto& hit : *chcol){ + double time = hit.time(); + if(time > max_time) max_time = time; + if(time < min_time) min_time = time; } - } } + } - if(AddErrorBar_){ - //XY - auto const& p = hit.pos(); - auto w = hit.uDir(); - auto const& s = hit.wireRes(); - double x1 = (p.x()+s*w.x()); - double x2 = (p.x()-s*w.x()); - double z1 = (p.z()+s*w.z()); - double z2 = (p.z()-s*w.z()); - double y1 = (p.y()+s*w.y()); - double y2 = (p.y()-s*w.y()); - - std::string errorbar = Form("ErrorBar Length: %f, %f, %f, %f, %f, %f",x1,y1,z1,x2,y2,z2); - auto error = new REX::REveLine("errors",errorbar.c_str(),2); - error->SetPoint(0, pointmmTocm(x1),pointmmTocm(y1),pointmmTocm(z1)); - error->SetNextPoint(pointmmTocm(x2),pointmmTocm(y2),pointmmTocm(z2)); - //std::cout<<"points "<SetLineColor(kRed); - error->SetLineWidth(drawconfig.getInt("TrackLineWidth")); - scene->AddElement(error); + if (!found_hits) { + std::cout << "[DataInterface] No valid ComboHits found." << std::endl; + return; + } + if (max_time == min_time) { + max_time += 1.0; + } + + // --- 2. Define the Color Gradient (Palette) --- + const Int_t NRGBs = 5; + const Int_t NCont = 255; + Double_t stops[NRGBs] = { 0.00, 0.34, 0.61, 0.84, 1.00 }; + Double_t red[NRGBs] = { 0.00, 0.00, 0.87, 1.00, 0.51 }; + Double_t green[NRGBs] = { 0.00, 0.81, 1.00, 0.20, 0.00 }; + Double_t blue[NRGBs] = { 0.51, 1.00, 0.12, 0.00, 0.00 }; + + TColor::CreateGradientColorTable(NRGBs, stops, red, green, blue, NCont); + gStyle->SetNumberContours(NCont); + + // --- 3. Visualization Loop --- + for(unsigned int j = 0; j < combohit_list.size(); j++){ + const ComboHitCollection* chcol = combohit_list[j]; + + if(chcol->size() != 0){ + + // --- MASTER COMPOUND for the entire collection --- + std::string master_name = "ComboHits_" + names[j]; + auto master_compound = new REX::REveCompound(master_name.c_str(), master_name.c_str(), true); + master_compound->SetRnrSelf(false); // Only render children (hits/errors) + + mu2e::GeomHandle tracker; + const auto& allStraws = tracker->getStraws(); + + // Loop over hits + for(unsigned int i = 0; i < chcol->size(); i++){ + mu2e::ComboHit const &hit = (*chcol)[i]; + + // --- Calculate Time-Based Color --- + Color_t hit_color; + double normalized_time = (hit.time() - min_time) / (max_time - min_time); + int colorIdx = static_cast(normalized_time * (NCont - 1)); + hit_color = gStyle->GetColorPalette(colorIdx); + + // --- A. Display Hit Straws (Separate element, added directly to scene for broad context) --- + if(strawdisplay){ + int sid = hit._sid.asUint16(); + + if(static_cast(sid) < allStraws.size() && sid < drawconfig.getInt("maxSID")){ + CLHEP::Hep3Vector sposi(0.0,0.0,0.0), sposf(0.0,0.0,0.0); + const mu2e::Straw& s = allStraws[sid]; + const CLHEP::Hep3Vector& p = s.getMidPoint(); + const CLHEP::Hep3Vector& d = s.getDirection(); + double x = p.x(); + double y = p.y(); + double z = p.z(); + double l = s.halfLength(); + + double st = sin(d.theta()); + double ct = cos(d.theta()); + double sp = sin(d.phi()+TMath::Pi()/2.0); + double cp = cos(d.phi()+TMath::Pi()/2.0); + + double x1=x+l*st*sp; + double y1=y-l*st*cp; + double z1=z+l*ct; + double x2=x-l*st*sp; + double y2=y+l*st*cp; + double z2=z-l*ct; + + std::string strawtitle; + int idPlane = s.id().getPlane(); + int colorid = s.id().getPanel() + idPlane + 1; + strawtitle =Form("Straw %i Panel %i Plane %i",s.id().getStraw(), s.id().getPanel(), idPlane); + + sposi.set(x1, y1, z1); + sposf.set(x2, y2, z2); + + if(sposi.x() != 0){ + auto strawline = new REX::REveLine("StrawHit", strawtitle, 2); + strawline->SetPoint(0, pointmmTocm(sposi.x()), pointmmTocm(sposi.y()), pointmmTocm(sposi.z())); + strawline->SetNextPoint(pointmmTocm(sposf.x()), pointmmTocm(sposf.y()), pointmmTocm(sposf.z())); + strawline->SetLineWidth(1); + strawline->SetLineColor(colorid); + if(strawline->GetSize() != 0) scene->AddElement(strawline); + } + } + } + + // --- B. Add Error Bar (REveLine) Optional --- + if(AddErrorBar_){ + auto const& p = hit.pos(); + auto w = hit.uDir(); + auto const& s = hit.wireRes(); // Wire resolution (length of error bar half-segment) + + // Calculate endpoints of the error bar along the perpendicular direction (w = uDir) + double x1 = (p.x()+s*w.x()); + double x2 = (p.x()-s*w.x()); + double z1 = (p.z()+s*w.z()); + double z2 = (p.z()-s*w.z()); + double y1 = (p.y()+s*w.y()); + double y2 = (p.y()-s*w.y()); + + // Add a detailed error bar label + std::string errorbar_label = std::string("ComboHit Error Bar") + '\n' + + "Hit Time: " + std::to_string(hit.time()) + " ns" + '\n' + + "Wire Res (half-length): " + std::to_string(s) + " mm" + '\n' + + "Error Bar Endpoints (mm):" + '\n' + + " P1 (" + std::to_string(x1) + ", " + std::to_string(y1) + ", " + std::to_string(z1) + ")" + '\n' + + " P2 (" + std::to_string(x2) + ", " + std::to_string(y2) + ", " + std::to_string(z2) + ")"; + auto error = new REX::REveLine(errorbar_label.c_str(), errorbar_label.c_str(), 2); + error->SetPoint(0, pointmmTocm(x1), pointmmTocm(y1), pointmmTocm(z1)); + error->SetNextPoint(pointmmTocm(x2), pointmmTocm(y2), pointmmTocm(z2)); + + error->SetLineColor(hit_color); + error->SetLineWidth(drawconfig.getInt("TrackLineWidth")); + + master_compound->AddElement(error); + } + + // --- C. Draw ComboHit Position (REvePointSet) --- + CLHEP::Hep3Vector HitPos(pointmmTocm(hit.pos().x()), pointmmTocm(hit.pos().y()), pointmmTocm(hit.pos().z())); + std::string chtitle = "ComboHits tag = " + + (names[j]) + '\n' + + " position : x " + std::to_string(hit.pos().x()) + '\n' + + " y " + std::to_string(hit.pos().y()) + '\n' + + " z " + std::to_string(hit.pos().z()) + '\n' + + " time :" + std::to_string(hit.time()) + '\n' + + " energy dep : " + std::to_string(hit.energyDep()) + "MeV"; + + auto ps1 = new REX::REvePointSet(chtitle.c_str(), chtitle.c_str(), 0); + ps1->SetNextPoint(HitPos.x(), HitPos.y() , HitPos.z()); + + ps1->SetMarkerColor(hit_color); // Time-based color + ps1->SetMarkerStyle(DataInterface::mstyle); + ps1->SetMarkerSize(DataInterface::msize); + + master_compound->AddElement(ps1); + } + + // Add the Master Compound to the Scene --- + scene->AddElement(master_compound); } - CLHEP::Hep3Vector HitPos(pointmmTocm(hit.pos().x()), pointmmTocm(hit.pos().y()), pointmmTocm(hit.pos().z())); - std::string chtitle = "ComboHits tag = " - + (names[j]) + '\n' - + " position : x " + std::to_string(hit.pos().x()) + '\n' - + " y " + std::to_string(hit.pos().y()) + '\n' - + " z " + std::to_string(hit.pos().z()) + '\n' - + " time :" + std::to_string(hit.time()) + '\n' - + " energy dep : " - + std::to_string(hit.energyDep()) + - + "MeV"; - auto ps1 = new REX::REvePointSet(chtitle, chtitle,0); - ps1->SetNextPoint(HitPos.x(), HitPos.y() , HitPos.z()); - ps1->SetMarkerColor(colour); - ps1->SetMarkerStyle(DataInterface::mstyle); - ps1->SetMarkerSize(DataInterface::msize); - if(ps1->GetSize() !=0 ) scene->AddElement(ps1); - } } - } } /*------------Function to add CRV information to the display:-------------*/ diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 82f3a93..357addc 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -777,7 +777,7 @@ void MainWindow::showEvents(REX::REveManager *eveMng, REX::REveElement* &eventSc if(drawOpts.addClusters){ std::vector calocluster_list = std::get<1>(data.calocluster_tuple); - if(calocluster_list.size() !=0 ) pass_data->AddCaloClusters(eveMng, firstLoopCalo, data.calocluster_tuple, eventScene, drawOpts.addCrystalDraw, t1, t2); + if(calocluster_list.size() !=0 ) pass_data->AddCaloClusters(eveMng, firstLoopCalo, data.calocluster_tuple, eventScene, drawOpts.addCrystalDraw); } std::vector helix_list = std::get<1>(data.helix_tuple); From 7ea4a6c778f4c35c803f4f82f2e9623153083aa3 Mon Sep 17 00:00:00 2001 From: sophieMu2e Date: Sat, 29 Nov 2025 20:12:33 -0600 Subject: [PATCH 07/24] CRV display now works --- CustomGUIv2/view/MyMain.view.xml | 2 +- examples/nominal_MDC2020.fcl | 2 +- examples/nominal_MDC2025.fcl | 2 +- fcl/extracted.fcl | 2 +- fcl/prolog.fcl | 2 +- inc/CollectionFiller.hh | 4 +- inc/DataInterface.hh | 1 + inc/MainWindow.hh | 4 +- inc/PrintInfo.hh | 2 +- src/CollectionFiller.cc | 4 +- src/DataInterface.cc | 367 ++++++++++++++++++++----------- src/MainWindow.cc | 2 +- src/Mu2eEventDisplay_module.cc | 6 +- 13 files changed, 251 insertions(+), 149 deletions(-) diff --git a/CustomGUIv2/view/MyMain.view.xml b/CustomGUIv2/view/MyMain.view.xml index cf2f279..e800d0d 100644 --- a/CustomGUIv2/view/MyMain.view.xml +++ b/CustomGUIv2/view/MyMain.view.xml @@ -63,7 +63,7 @@ -