CMS 3D CMS Logo

FWEveView.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: Core
4 // Class : FWEveView
5 //
6 // Implementation:
7 // [Notes on implementation]
8 //
9 // Original Author: Alja Mrak-Tadel
10 // Created: Thu Mar 16 14:11:32 CET 2010
11 //
12 
13 #include <RVersion.h>
14 #include <functional>
15 #include <stdexcept>
16 #include <string>
17 
18 // user include files
19 
20 #include "TGLOrthoCamera.h"
21 #include "TGLPerspectiveCamera.h"
22 #include "TGLCameraGuide.h"
23 
24 #include "TGLEmbeddedViewer.h"
25 #include "TGLScenePad.h"
26 #include "TEveManager.h"
27 #include "TEveElement.h"
28 #include "TEveWindow.h"
29 #include "TEveScene.h"
30 #include "TEveCalo.h"
31 #include "TGLOverlay.h"
32 
35 
51 
52 namespace fireworks {
53  class Context;
54 }
55 
56 /* This class is temporary workaround for missing in TGLAnnotation functionality */
57 class ScaleAnnotation : public TGLAnnotation {
58 public:
59  ScaleAnnotation(TGLViewerBase* parent, const char* text, Float_t posx, Float_t posy)
60  : TGLAnnotation(parent, text, posx, posy) {}
61  ~ScaleAnnotation() override {}
62 
63  void setText(const char* txt) { fText = txt; }
64 };
65 
66 //
67 // constructors and destructor
68 //
69 
70 FWEveView::FWEveView(TEveWindowSlot* iParent, FWViewType::EType type, unsigned int version)
72  m_context(nullptr),
73  m_viewer(nullptr),
74  m_eventScene(nullptr),
75  m_ownedProducts(nullptr),
76  m_geoScene(nullptr),
77  m_overlayEventInfo(nullptr),
78  m_overlayLogo(nullptr),
79  m_energyMaxValAnnotation(nullptr),
80  m_cameraGuide(nullptr),
81 // style
82 #if ROOT_VERSION_CODE >= ROOT_VERSION(5, 26, 0)
83  m_imageScale(this, "Image Scale", 1.0, 1.0, 6.0),
84 #endif
85  m_eventInfoLevel(this, "Overlay Event Info", 0l, 0l, 2l),
86  m_drawCMSLogo(this, "Show Logo", false),
87  m_pointSmooth(this, "Smooth points", false),
88  m_pointSize(this, "Point size", 1.0, 1.0, 10.0),
89  m_lineSmooth(this, "Smooth lines", false),
90  m_lineWidth(this, "Line width", 1.0, 1.0, 10.0),
91  m_lineOutlineScale(this, "Outline width scale", 1.0, 0.01, 10.0),
92  m_lineWireframeScale(this, "Wireframe width scale", 1.0, 0.01, 10.0),
93  m_showCameraGuide(this, "Show Camera Guide", false),
94  m_useGlobalEnergyScale(this, "UseGlobalEnergyScale", true),
95  m_viewContext(new FWViewContext()),
96  m_localEnergyScale(new FWViewEnergyScale(FWViewType::idToName(type), version)),
97  m_viewEnergyScaleEditor(nullptr) {
98  m_viewer = new FWTEveViewer(typeName().c_str());
99 
100  FWTGLViewer* embeddedViewer = m_viewer->SpawnFWTGLViewer();
101  iParent->ReplaceWindow(m_viewer);
102  gEve->GetViewers()->AddElement(m_viewer);
103 
104  m_eventScene = gEve->SpawnNewScene(Form("EventScene %s", typeName().c_str()));
105  m_ownedProducts = new TEveElementList("ViewSpecificProducts");
106  m_eventScene->AddElement(m_ownedProducts);
107 
108  m_viewer->AddScene(m_eventScene);
109 
110  // spawn geo scene
111  m_geoScene = gEve->SpawnNewScene(Form("GeoScene %s", typeName().c_str()));
112  m_geoScene->GetGLScene()->SetSelectable(kFALSE);
113  m_viewer->AddScene(m_geoScene);
114 
115  FWGLEventHandler* eh = new FWGLEventHandler((TGWindow*)embeddedViewer->GetGLWidget(), (TObject*)embeddedViewer);
116  embeddedViewer->SetEventHandler(eh);
117  eh->setViewer(this);
119  eh->SetDoInternalSelection(kFALSE);
121  // ctxHand->setPickCameraCenter(true);
122  m_viewContextMenu.reset(ctxHand);
123 
124  m_energyMaxValAnnotation = new ScaleAnnotation(viewerGL(), "empty", 0.1, 0.9);
125  m_energyMaxValAnnotation->SetRole(TGLOverlayElement::kViewer);
126  m_energyMaxValAnnotation->SetState(TGLOverlayElement::kInvisible);
127  m_energyMaxValAnnotation->SetUseColorSet(false);
128  m_energyMaxValAnnotation->SetTextSize(0.05);
129  m_energyMaxValAnnotation->SetTextColor(kMagenta);
130 
131  // style params
132 
133  m_overlayEventInfo = new FWEventAnnotation(embeddedViewer);
135 
136  m_eventInfoLevel.addEntry(0, "Nothing");
137  m_eventInfoLevel.addEntry(1, "Run / event");
138  m_eventInfoLevel.addEntry(2, "Run / event / lumi");
139  m_eventInfoLevel.addEntry(3, "Full");
140  m_eventInfoLevel.changed_.connect(std::bind(&FWEventAnnotation::setLevel, m_overlayEventInfo, std::placeholders::_1));
141 
142  m_overlayLogo = new CmsAnnotation(embeddedViewer, 0.02, 0.98);
143  m_overlayLogo->setVisible(false);
144  m_drawCMSLogo.changed_.connect(std::bind(&CmsAnnotation::setVisible, m_overlayLogo, std::placeholders::_1));
145 
146  m_cameraGuide = new TGLCameraGuide(0.9, 0.1, 0.08);
147  m_cameraGuide->SetState(TGLOverlayElement::kInvisible);
148  embeddedViewer->AddOverlayElement(m_cameraGuide);
149  m_showCameraGuide.changed_.connect(std::bind(&FWEveView::cameraGuideChanged, this));
150 
151  m_pointSmooth.changed_.connect(std::bind(&FWEveView::pointLineScalesChanged, this));
152  m_pointSize.changed_.connect(std::bind(&FWEveView::pointLineScalesChanged, this));
153  m_lineSmooth.changed_.connect(std::bind(&FWEveView::pointLineScalesChanged, this));
154  m_lineWidth.changed_.connect(std::bind(&FWEveView::pointLineScalesChanged, this));
157 
158  // create scale for view ..
159  m_viewContext->setEnergyScale(m_localEnergyScale.get());
161  m_localEnergyScale->parameterChanged_.connect(std::bind(&FWEveView::setupEnergyScale, this));
162 }
163 
165  m_geoScene->RemoveElements();
166  m_eventScene->RemoveElements();
167  m_viewer->DestroyWindowAndSlot();
168 }
169 
170 //______________________________________________________________________________
171 // const member functions
172 
174  return dynamic_cast<FWViewContextMenuHandlerBase*>(m_viewContextMenu.get());
175 }
176 
177 TGLViewer* FWEveView::viewerGL() const { return m_viewer->GetGLViewer(); }
178 
179 TEveViewer* FWEveView::viewer() { return m_viewer; }
180 
182 
183 void FWEveView::saveImageTo(const std::string& iName) const {
184  bool succeeded = false;
185 #if ROOT_VERSION_CODE >= ROOT_VERSION(5, 26, 0)
186  succeeded = viewerGL()->SavePictureScale(iName, m_imageScale.value());
187 #else
188  succeeded = viewerGL()->SavePicture(iName.c_str());
189 #endif
190 
191  if (!succeeded) {
192  throw std::runtime_error("Unable to save picture");
193  }
194  fwLog(fwlog::kInfo) << "Saved image " << iName << std::endl;
195 }
196 
197 //-------------------------------------------------------------------------------
199  viewerGL()->SetSmoothPoints(m_pointSmooth.value());
200  viewerGL()->SetPointScale(m_pointSize.value());
201  viewerGL()->SetSmoothLines(m_lineSmooth.value());
202  viewerGL()->SetLineScale(m_lineWidth.value());
203  viewerGL()->SetOLLineW(m_lineOutlineScale.value());
204  viewerGL()->SetWFLineW(m_lineWireframeScale.value());
205  viewerGL()->Changed();
206  gEve->Redraw3D();
207 }
208 
210  m_cameraGuide->SetBinaryState(m_showCameraGuide.value());
211  viewerGL()->Changed();
212  gEve->Redraw3D();
213 }
214 
216 
220 }
221 
223 
224 void FWEveView::resetCamera() { viewerGL()->ResetCurrentCamera(); }
225 
226 //______________________________________________________________________________
228  m_context = &x;
229 
230  // in constructor view context has local scale
232  m_viewContext->setEnergyScale(context().commonPrefs()->getEnergyScale());
233 }
234 
236 
239  : m_localEnergyScale.get());
243 }
244 
246  TEveCaloViz* calo = getEveCalo();
247  if (calo)
248  context().voteMaxEtAndEnergy(calo->GetData()->GetMaxVal(true), calo->GetData()->GetMaxVal(false));
249 }
250 
252  // Called at end of event OR if scale parameters changed.
253 
254  FWViewEnergyScale* energyScale = viewContext()->getEnergyScale();
255  // printf("setupEnergyScale %s >> scale name %s\n", typeName().c_str(), energyScale->name().c_str());
256  voteCaloMaxVal();
257 
258  // set cache for energy to lenght conversion
259  float maxVal = context().getMaxEnergyInEvent(energyScale->getPlotEt());
260  energyScale->updateScaleFactors(maxVal);
261  // printf("max event val %f \n", maxVal);
262  // printf("scales lego %f \n", energyScale->getScaleFactorLego());
263 
264  // configure TEveCaloViz
265  TEveCaloViz* calo = getEveCalo();
266  if (calo) {
267  calo->SetPlotEt(energyScale->getPlotEt());
268  if (FWViewType::isLego(typeId())) {
269  float f = energyScale->getScaleFactorLego();
270  calo->SetMaxValAbs(TMath::Pi() / f);
271  } else {
272  float f = energyScale->getScaleFactor3D();
273  calo->SetMaxValAbs(100 / f);
274  }
275  calo->ElementChanged();
276  }
277 
278  // emit signal to proxy builders
280  gEve->Redraw3D();
281 }
282 
284  // piggyback on scales signal connections to redraw jets
285  // can add new signal in future if necessary
287  gEve->Redraw3D();
288 }
289 
290 //-------------------------------------------------------------------------------
292  // take care of parameters
294 
295  {
298  }
299  {
301  m_overlayLogo->addTo(iTo);
302  }
303 
304  m_viewContext->getEnergyScale()->addTo(iTo);
305 }
306 
308  // Make sure you change the version ranges here
309  // whenever you update the logic.
310  // The rationale should be:
311  // (version range supported by the next block) && (version range in the configuration file)
312  //
313  // This is not "forward" compatible, but I don't think
314  // we care.
315  if (version() >= 2 && iFrom.version() >= 1) {
316  for (const_iterator it = begin(), itEnd = end(); it != itEnd; ++it) {
317  (*it)->setFrom(iFrom);
318  }
319  }
320  if (iFrom.version() > 1) {
322  m_overlayEventInfo->setFrom(iFrom);
323  }
324  {
326  m_overlayLogo->setFrom(iFrom);
327  }
328 
329  if (iFrom.version() > 4) {
330  m_localEnergyScale->setFrom(iFrom);
331  }
332 
333  // selection clors
334  {
335  const TGLColorSet& lcs = context().commonPrefs()->getLightColorSet();
336  const TGLColorSet& dcs = context().commonPrefs()->getDarkColorSet();
337  const UChar_t* ca = nullptr;
338 
339  ca = lcs.Selection(1).CArr();
340  viewerGL()->RefLightColorSet().Selection(1).SetColor(ca[0], ca[1], ca[2]);
341  ca = lcs.Selection(3).CArr();
342  viewerGL()->RefLightColorSet().Selection(3).SetColor(ca[0], ca[1], ca[2]);
343  ca = dcs.Selection(1).CArr();
344  viewerGL()->RefDarkColorSet().Selection(1).SetColor(ca[0], ca[1], ca[2]);
345  ca = dcs.Selection(3).CArr();
346  viewerGL()->RefDarkColorSet().Selection(3).SetColor(ca[0], ca[1], ca[2]);
347  }
348 }
349 
350 //______________________________________________________________________________
351 
352 void FWEveView::addToOrthoCamera(TGLOrthoCamera* camera, FWConfiguration& iTo) const {
353  // zoom
354  std::string name("cameraZoom");
355  iTo.addKeyValue(name + typeName(), FWConfiguration(std::to_string(camera->GetZoom())));
356 
357  // transformation matrix
358  std::string matrixName("cameraMatrix");
359  for (unsigned int i = 0; i < 16; ++i) {
360  std::ostringstream osIndex;
361  osIndex << i;
362  std::ostringstream osValue;
363  osValue << camera->GetCamTrans()[i];
364  iTo.addKeyValue(matrixName + osIndex.str() + typeName(), FWConfiguration(osValue.str()));
365  }
366 }
367 
368 void FWEveView::setFromOrthoCamera(TGLOrthoCamera* camera, const FWConfiguration& iFrom) {
369  try {
370  // zoom
371  std::string zoomName("cameraZoom");
372  zoomName += typeName();
373  if (iFrom.valueForKey(zoomName) == nullptr) {
374  throw std::runtime_error("can't restore parameter cameraZoom");
375  }
376  camera->SetZoom(std::stod(iFrom.valueForKey(zoomName)->value()));
377 
378  // transformation matrix
379  std::string matrixName("cameraMatrix");
380  for (unsigned int i = 0; i < 16; ++i) {
381  std::ostringstream os;
382  os << i;
383  const FWConfiguration* value = iFrom.valueForKey(matrixName + os.str() + typeName());
384  if (value == nullptr) {
385  throw std::runtime_error("can't restore parameter cameraMatrix.");
386  }
387  std::istringstream s(value->value());
388  s >> (camera->RefCamTrans()[i]);
389  }
390  } catch (const std::runtime_error& iException) {
391  fwLog(fwlog::kInfo) << "Caught exception while restoring camera parameters in view " << typeName() << "\n.";
392  viewerGL()->ResetCamerasAfterNextUpdate();
393  }
394  camera->IncTimeStamp();
395 }
396 
397 void FWEveView::addToPerspectiveCamera(TGLPerspectiveCamera* cam, const std::string& name, FWConfiguration& iTo) const {
398  // transformation matrix
399  std::string matrixName("cameraMatrix");
400  for (unsigned int i = 0; i < 16; ++i) {
401  std::ostringstream osIndex;
402  osIndex << i;
403  std::ostringstream osValue;
404  osValue << (cam->GetCamTrans())[i];
405  iTo.addKeyValue(matrixName + osIndex.str() + name, FWConfiguration(osValue.str()));
406  }
407 
408  // transformation matrix base
409  matrixName = "cameraMatrixBase";
410  for (unsigned int i = 0; i < 16; ++i) {
411  std::ostringstream osIndex;
412  osIndex << i;
413  std::ostringstream osValue;
414  osValue << (cam->GetCamBase())[i];
415  iTo.addKeyValue(matrixName + osIndex.str() + name, FWConfiguration(osValue.str()));
416  }
417  { iTo.addKeyValue(name + " FOV", FWConfiguration(std::to_string(cam->GetFOV()))); }
418 }
419 
420 void FWEveView::setFromPerspectiveCamera(TGLPerspectiveCamera* cam,
421  const std::string& name,
422  const FWConfiguration& iFrom) {
423  try {
424  std::string matrixName("cameraMatrix");
425  for (unsigned int i = 0; i < 16; ++i) {
426  std::ostringstream os;
427  os << i;
428  const FWConfiguration* value = iFrom.valueForKey(matrixName + os.str() + name);
429  if (value == nullptr) {
430  throw std::runtime_error("can't restore parameter cameraMatrix.");
431  }
432  std::istringstream s(value->value());
433  s >> ((cam->RefCamTrans())[i]);
434  }
435 
436  // transformation matrix base
437  matrixName = "cameraMatrixBase";
438  for (unsigned int i = 0; i < 16; ++i) {
439  std::ostringstream os;
440  os << i;
441  const FWConfiguration* value = iFrom.valueForKey(matrixName + os.str() + name);
442  if (value == nullptr) {
443  throw std::runtime_error("can't restore parameter cameraMatrixBase.");
444  }
445 
446  std::istringstream s(value->value());
447  s >> ((cam->RefCamBase())[i]);
448  }
449 
450  {
451  const FWConfiguration* value = iFrom.valueForKey(name + " FOV");
452  if (value == nullptr) {
453  throw std::runtime_error("can't restore parameter cameraMatrixBase.");
454  }
455  cam->SetFOV(std::stod(value->value()));
456  }
457 
458  cam->IncTimeStamp();
459  } catch (const std::runtime_error& iException) {
460  fwLog(fwlog::kInfo) << "Caught exception while restoring camera parameters in view " << typeName() << "\n.";
461  viewerGL()->ResetCamerasAfterNextUpdate();
462  fwLog(fwlog::kDebug) << "Reset camera fo view " << typeName() << "\n.";
463  }
464 }
465 
467  gui.requestTab("Style")
471  .separator()
472  .
473 #if ROOT_VERSION_CODE >= ROOT_VERSION(5, 26, 0)
474  addParam(&m_imageScale)
475  .
476 #endif
477  addParam(&m_pointSize)
483 
485 
490 }
void scaleChanged()
const double Pi
FWEventAnnotation * m_overlayEventInfo
Definition: FWEveView.h:123
CmsShowCommon * commonPrefs() const
Definition: Context.cc:167
virtual void useGlobalEnergyScaleChanged()
Definition: FWEveView.cc:237
virtual void addTo(FWConfiguration &) const
const_iterator begin() const
std::shared_ptr< FWViewContextMenuHandlerGL > m_viewContextMenu
Definition: FWEveView.h:146
virtual void pointLineScalesChanged()
Definition: FWEveView.cc:198
ScaleAnnotation(TGLViewerBase *parent, const char *text, Float_t posx, Float_t posy)
Definition: FWEveView.cc:59
ViewerParameterGUI & requestTab(const char *)
float getMaxEnergyInEvent(bool isEt) const
Definition: Context.cc:181
FWTGLViewer * fwViewerGL() const
Definition: FWEveView.cc:181
ViewerParameterGUI & separator()
virtual void setFrom(const FWConfiguration &)
std::string to_string(const V &value)
Definition: OMSAccess.h:71
TEveScene * m_geoScene
Definition: FWEveView.h:121
virtual void setupEventCenter()
Definition: FWEveView.cc:283
FWViewEnergyScaleEditor * m_viewEnergyScaleEditor
Definition: FWEveView.h:150
virtual void setupEnergyScale()
Definition: FWEveView.cc:251
void addFrameToContainer(TGCompositeFrame *)
FWDoubleParameter m_pointSize
Definition: FWEveView.h:137
void updateScaleFactors(float iMaxVal)
std::vector< FWParameterBase * >::const_iterator const_iterator
assert(be >=bs)
const TGLColorSet & getDarkColorSet() const
Definition: CmsShowCommon.h:73
const fireworks::Context * m_context
Definition: FWEveView.h:109
virtual void addTo(FWConfiguration &) const
TEveElement * m_ownedProducts
Definition: FWEveView.h:120
FWBoolParameter m_lineSmooth
Definition: FWEveView.h:138
virtual bool isEnergyScaleGlobal() const
Definition: FWEveView.cc:235
sigc::signal< void(Int_t, Int_t)> openSelectedModelContextMenu_
Definition: FWViewBase.h:57
bool getPlotEt() const
virtual void eventEnd()
Definition: FWEveView.cc:217
const_iterator end() const
float getScaleFactorLego() const
FWBoolParameter m_showCameraGuide
Definition: FWEveView.h:143
FWViewEnergyScale * getEnergyScale() const
FWBoolParameter m_useGlobalEnergyScale
Definition: FWEveView.h:144
~ScaleAnnotation() override
Definition: FWEveView.cc:61
virtual void cameraGuideChanged()
Definition: FWEveView.cc:209
virtual void voteCaloMaxVal()
Definition: FWEveView.cc:245
const std::string & value(unsigned int iIndex=0) const
virtual void setFrom(const FWConfiguration &)
ScaleAnnotation * m_energyMaxValAnnotation
Definition: FWEveView.h:125
FWTGLViewer * fwGlViewer()
Definition: FWTEveViewer.h:50
void setText(const char *txt)
Definition: FWEveView.cc:63
virtual TEveCaloViz * getEveCalo() const
Definition: FWEveView.h:100
void voteMaxEtAndEnergy(float Et, float energy) const
Definition: Context.cc:169
void saveImageTo(const std::string &iName) const override
Definition: FWEveView.cc:183
FWTEveViewer * m_viewer
Definition: FWEveView.h:118
TEveViewer * viewer()
Definition: FWEveView.cc:179
bool addEntry(Long_t id, const std::string &txt)
double f[11][100]
FWViewContextMenuHandlerBase * contextMenuHandler() const override
Definition: FWEveView.cc:173
Definition: value.py:1
virtual void resetCamera()
Definition: FWEveView.cc:224
void addToOrthoCamera(TGLOrthoCamera *, FWConfiguration &) const
Definition: FWEveView.cc:352
FWBoolParameter m_drawCMSLogo
Definition: FWEveView.h:134
void setViewer(FWEveView *ev)
FWConfiguration & addKeyValue(const std::string &, const FWConfiguration &)
std::unique_ptr< FWViewEnergyScale > m_localEnergyScale
Definition: FWEveView.h:148
virtual void eventBegin()
Definition: FWEveView.cc:215
FWViewEnergyScale * getEnergyScale() const
Definition: CmsShowCommon.h:70
const TGLColorSet & getLightColorSet() const
Definition: CmsShowCommon.h:72
const fireworks::Context & context()
Definition: FWEveView.h:64
FWBoolParameter m_pointSmooth
Definition: FWEveView.h:136
float getScaleFactor3D() const
FWDoubleParameter m_imageScale
Definition: FWEveView.h:131
void populateController(ViewerParameterGUI &) const override
Definition: FWEveView.cc:466
void setFrom(const FWConfiguration &) override
Definition: FWEveView.cc:307
sigc::signal< void(Int_t, Int_t)> openSelectedModelContextMenu_
void setFromPerspectiveCamera(TGLPerspectiveCamera *, const std::string &, const FWConfiguration &)
Definition: FWEveView.cc:420
#define fwLog(_level_)
Definition: fwLog.h:45
FWViewContext * viewContext()
Definition: FWEveView.h:83
virtual void setBackgroundColor(Color_t)
Definition: FWEveView.cc:222
FWEveView(TEveWindowSlot *, FWViewType::EType, unsigned int version=7)
Definition: FWEveView.cc:70
FWDoubleParameter m_lineOutlineScale
Definition: FWEveView.h:140
ViewerParameterGUI & addParam(const FWParameterBase *)
TGCompositeFrame * getTabContainer()
const FWConfiguration * valueForKey(const std::string &iKey) const
TEveScene * m_eventScene
Definition: FWEveView.h:119
FWEnumParameter m_eventInfoLevel
Definition: FWEveView.h:133
static bool isLego(int)
Definition: FWViewType.cc:102
std::unique_ptr< FWViewContext > m_viewContext
Definition: FWEveView.h:147
static Bool_t setColorSetViewer(TGLViewer *, Color_t)
float x
sigc::signal< void(T)> changed_
~FWEveView() override
Definition: FWEveView.cc:164
FWViewType::EType typeId() const
Definition: FWViewBase.h:42
void addToPerspectiveCamera(TGLPerspectiveCamera *, const std::string &, FWConfiguration &) const
Definition: FWEveView.cc:397
FWDoubleParameter m_lineWidth
Definition: FWEveView.h:139
void addTo(FWConfiguration &) const override
Definition: FWEveView.cc:291
FWDoubleParameter m_lineWireframeScale
Definition: FWEveView.h:141
TGLViewer * viewerGL() const
Definition: FWEveView.cc:177
void addTo(FWConfiguration &) const override
void setFromOrthoCamera(TGLOrthoCamera *, const FWConfiguration &)
Definition: FWEveView.cc:368
void setVisible(bool x)
TGLCameraGuide * m_cameraGuide
Definition: FWEveView.h:126
virtual void setContext(const fireworks::Context &x)
Definition: FWEveView.cc:227
FWTGLViewer * SpawnFWTGLViewer()
const std::string & typeName() const
Definition: FWViewBase.cc:107
Definition: Common.h:9
unsigned int version() const
CmsAnnotation * m_overlayLogo
Definition: FWEveView.h:124