Visualization for Geant4 examples

Under http://inexlib.lal.in2p3.fr/download/exp/geant4 can be found the g4inex.zip distribution. It contains modified geant4 examples (for example the A01 one) to demonstrate how to use the inlib/exlib histogramming and graphics to work with Geant4. The way to "exlib instrument a Geant4 example" is sufficiently simple to be used also on your own Geant4 application. The look and feel of an instrumented Geant4 example is similar to the ioda application. In particular the menu system is done with the same tools (exlib/sg) than the ioda one (and then done on OpenGL-ES). The "straight C++ way of doing" applies here : it is sufficient to include one file (exlib/geant4/session), instanciate one class, change the Geant4 example GNUmakefile (or your own Geant4 application build system) to have the "-I" toward inlib/exlib, some "-L -l" toward some standard libraries and then build and run. Here too, it is quite simple and straightforward.

instrumented A01

To build and run the modifed A01 :

     UNIX> <get g4inex.zip>
     UNIX> <unzip g4inex.zip>
     UNIX> cd g4inex/A01
     UNIX> <modify setup.csh to say where is Geant4>
     UNIX> tcsh
     csh> source setup.csh
     csh> make
     csh> ${G4BIN}/${G4SYSTEM}/A01app

At startup you should see :

exlib_g4_0.png

click on init and then geom :

exlib_g4_1.png

As for ioda, you can play with the key controls (or the camera panel) to move around. Then go back to the main menu by clicking in the bottom "meta zone" and then click on "beam on" :

exlib_g4_2.png

Select a number of event and then "Ok". You should see events flashing :

exlib_g4_3.png

By default the g4inexa/A01 has histogramming code done with inlib. You can see plots by clicking "plot" and then "beam on", you should see the filling of the histograms and clouds :

g4inex_plot.png

Geant4 on an Android or an iDevice ?

It is clear that with the inlib/exlib we have all the material at hand to handle interactivity for a Geant4 application on an Android or an iDevice. But for that you shall need CLHEP and Geant4 ported on this kind of platform...

Darwin (Mac desktop) notes

On Darwin it is expected to have jpeg installed with MacPort and then found under :

     /opt/local/include and /opt/local/lib

If not (for example because installed with Fink under /sw), you have to change in inexlib.gmk the line CPPFLAGS and LDLIBS about jpeg :

ifeq ($(G4SYSTEM),Darwin-g++)
  # freetype 2 :
  CPPFLAGS += -I/usr/X11/include/freetype2
  LDLIBS += -L/usr/X11R6/lib -lGLU -lfreetype
  # jpeg :
  CPPFLAGS += -I/opt/local/include
  LDLIBS += -L/opt/local/lib -ljpeg
endif

modified A01app.cc

// NOTE : in A01EventAction.hh, have the AIDA objects (IHistogram1D, etc...)
//        fields made public :
//          public:
//            IHistogram1D* dc1Hits;
//            ...

#include "G4RunManager.hh"
#include "G4UImanager.hh"

#include "A01DetectorConstruction.hh"
#include "A01PhysicsList.hh"
#include "A01PrimaryGeneratorAction.hh"

#include "A01EventAction.hh"

#define WALL_DATA_CLIENT

#include <exlib/geant4/session>

#ifdef G4ANALYSIS_USE
#include <exlib/AIDA/h2plot>
#include <exlib/AIDA/cloud2plot>
#endif

#ifdef G4ANALYSIS_INEX
#include <inlib/sg/h2plot>
#include <inlib/sg/cloud2plot>
#endif

namespace A01 {
class session : public exlib::geant4::session {
protected:
  virtual exlib::sg::list* create_home_menu() {
    exlib::sg::list* list = exlib::geant4::session::create_home_menu();
#if defined(G4ANALYSIS_USE) || defined(G4ANALYSIS_INEX)
    list->add_before("params","rebook",new book_cbk(*this));
    list->add_before("params","plot",new plot_cbk(*this));
#endif
    return list;
  }
  virtual void event_end() {
    if(m_eva.m_build_rep){
      plot_cbk cbk(*this);
      cbk.action();
    }
  }
public:
  // pass the A01EventAction to get the histos.
  session(A01EventAction& a_eva,int a_argc = 0,char** a_argv = NULL)
  : exlib::geant4::session(a_argc,a_argv)
  ,m_eva(a_eva) //to get histos.
  {
    push_home(); //to enforce the upper create_home_menu().
  }
private:
  session(const session& a_from)
  : exlib::geant4::session(a_from)
  ,m_eva(a_from.m_eva)
  {}
  session& operator=(const session&){return *this;}

private:
  class book_cbk : public inlib::sg::cbk {
  public:
    virtual return_action action() {
      m_sess.plots().clear();
      m_sess.m_eva.m_cloud_auto_cnv_limit = m_sess.m_cloud_auto_cnv_limit;
      m_sess.m_eva.Book();
      return return_to_render;     
    }
    virtual inlib::sg::cbk* copy() const {return new book_cbk(*this);}
  public:
    book_cbk(session& a_gv): inlib::sg::cbk(),m_sess(a_gv){}
    virtual ~book_cbk(){}
  private:
    book_cbk(const book_cbk& a_from)
    :inlib::sg::cbk(a_from),m_sess(a_from.m_sess){}
    book_cbk& operator=(const book_cbk& a_from){
      inlib::sg::cbk::operator=(a_from);
      return *this;
    }
  private:
    session& m_sess;
  };

  class plot_cbk : public inlib::sg::cbk {
  public:
    virtual return_action action() {
#ifdef G4ANALYSIS_USE
#ifdef WALL_DATA_CLIENT
      m_sess.create_plotter(1,1);
     {m_sess.plots().set_current_plotter(0);
      AIDA::IHistogram1D* dc1Hits = m_sess.m_eva.dc1Hits;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      plotter.background_style().visible = true;
      if(dc1Hits) {
        plotter.add_plottable(new exlib::AIDA::h1d2plot(*dc1Hits));
        //ui.style_from_res("A01",*plotter);
      }}
#else      
      m_sess.create_plotter(2,2);
     {m_sess.plots().set_current_plotter(0);
      AIDA::IHistogram1D* dc1Hits = m_sess.m_eva.dc1Hits;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      if(dc1Hits) {
        plotter.add_plottable(new exlib::AIDA::h1d2plot(*dc1Hits));
      }}

     {m_sess.plots().next();
      AIDA::ICloud2D* dc1XY = m_sess.m_eva.dc1XY;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      if(dc1XY) {
        plotter.add_plottable(new exlib::AIDA::cloud2D_2plot(*dc1XY));
      }}

     {m_sess.plots().next();
      AIDA::ICloud2D* dc2XY = m_sess.m_eva.dc2XY;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      if(dc2XY) {
        plotter.add_plottable(new exlib::AIDA::cloud2D_2plot(*dc2XY));
      }}

     {m_sess.plots().next();
      AIDA::ICloud2D* evstof = m_sess.m_eva.evstof;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      if(evstof) {
        plotter.add_plottable(new exlib::AIDA::cloud2D_2plot(*evstof));
      }}
#endif
#endif


#ifdef G4ANALYSIS_INEX
      m_sess.create_plotter(2,2);
     {m_sess.plots().set_current_plotter(0);
      inlib::histo::h1d* dc1Hits = m_sess.m_eva.dc1Hits;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      plotter.background_style().visible = true;
      if(dc1Hits) {
        plotter.add_plottable(new inlib::sg::h1d2plot(*dc1Hits));
      }}

     {m_sess.plots().next();
      inlib::histo::c2d* dc1XY = m_sess.m_eva.dc1XY;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      plotter.background_style().visible = true;
      if(dc1XY) {
        //NOTE : we should find a way to automatically switch the rep
        //       when the c2d is auto converted to an histogram.
        if(dc1XY->is_converted()) {
          plotter.add_plottable(new inlib::sg::h2d2plot(dc1XY->histogram()));
        } else {
          plotter.add_plottable(new inlib::sg::c2d2plot(*dc1XY));
        }
      }}

     {m_sess.plots().next();
      inlib::histo::c2d* dc2XY = m_sess.m_eva.dc2XY;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      plotter.background_style().visible = true;
      if(dc2XY) {
        if(dc2XY->is_converted()) {
          plotter.add_plottable(new inlib::sg::h2d2plot(dc2XY->histogram()));
        } else {
          plotter.add_plottable(new inlib::sg::c2d2plot(*dc2XY));
        }
      }}

     {m_sess.plots().next();
      inlib::histo::c2d* evstof = m_sess.m_eva.evstof;
      exlib::sg::plotter& plotter = m_sess.plots().current_plotter();
      plotter.background_style().visible = true;
      if(evstof) {
        if(evstof->is_converted()) {
          plotter.add_plottable(new inlib::sg::h2d2plot(evstof->histogram()));
        } else {
          plotter.add_plottable(new inlib::sg::c2d2plot(*evstof));
        }
      }}
#endif

      return return_to_render;     
    }
    virtual inlib::sg::cbk* copy() const {return new plot_cbk(*this);}
  public:
    plot_cbk(session& a_gv): inlib::sg::cbk(),m_sess(a_gv){}
    virtual ~plot_cbk(){}
  private:
    plot_cbk(const plot_cbk& a_from)
    :inlib::sg::cbk(a_from),m_sess(a_from.m_sess){}
    plot_cbk& operator=(const plot_cbk& a_from){
      inlib::sg::cbk::operator=(a_from);
      return *this;
    }
  private:
    session& m_sess;
  };

private:
  A01EventAction& m_eva;
};
}

int main(int argc,char** argv)
{
  // RunManager construction
  G4RunManager* runManager = new G4RunManager;

  // mandatory user initialization classes
  runManager->SetUserInitialization(new A01DetectorConstruction);
  runManager->SetUserInitialization(new A01PhysicsList);

  // initialize Geant4 kernel
  runManager->Initialize();

  // mandatory user action class
  runManager->SetUserAction(new A01PrimaryGeneratorAction);

  // optional user action classes
  A01EventAction* eventAction = new A01EventAction();
  runManager->SetUserAction(eventAction);
#ifdef WALL_DATA_CLIENT
  eventAction->m_cloud_auto_cnv_limit = 1000;
#endif

 {//exlib gui and graphics :
  A01::session ui(*eventAction,argc,argv);
  ui.set_background(inlib::colorf::white());
  ui.set_cloud_auto_cnv_limit(eventAction->m_cloud_auto_cnv_limit);

  exlib::sg::ortho* camera = new exlib::sg::ortho();
  camera->height = 10.0*m;
  float z = 10.*m;
  camera->znear = 0.01f*z;
  camera->zfar = 100*z;
  camera->position = inlib::vec3f(0,0,z);
  camera->dx = z*0.003f;
  camera->da = camera->da.value()/2;

  ui.replace_camera(camera); //it takes ownership of camera.

  ui.steer();}

  delete runManager;

  std::cout << "main : exit..." << std::endl;

  return 0;
}