EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EicToyModel.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EicToyModel.cc
1 
2 #include <iostream>
3 #include <fstream>
4 
5 #include <unistd.h>
6 
7 #include <TObjString.h>
8 #include <TStyle.h>
9 #include <TCanvas.h>
10 #include <TColor.h>
11 #include <TLatex.h>
12 #include <TFile.h>
13 #include <TROOT.h>
14 #include <TArrow.h>
15 #include <TPolyLine.h>
16 
17 #ifdef _ETM2GEANT_
18 #include "G4LogicalVolume.hh"
19 #include "G4Box.hh"
20 #include "G4SystemOfUnits.hh"
21 #include "G4NistManager.hh"
22 #include "G4PVPlacement.hh"
23 #endif
24 
25 #include <EicGeoMedia.h>
26 #include <EicToyModel.h>
27 
28 // Side, top & bottom extra space for labels and such;
29 #define _IR_REGION_SMARGIN_ ( 50.0 * etm::cm)
30 #define _IR_REGION_BMARGIN_ ( 30.0 * etm::cm)
31 
32 #define _CANVAS_WIDTH_DEFAULT_ ( 1500)
33 
34 // FIXME: this is dubm, but I did not find a way to calculate text width in pixels
35 // dynamically; somehow TText routines do not work for me;
36 #define _COLOR_LEGEND_HEIGHT_ (20.0 * etm::cm)
37 #define _COLOR_LEGEND_STEP_ (80.0 * etm::cm)
38 #define _COLOR_LEGEND_GAP_ ( 5.0 * etm::cm)
39 
40 // Use STAR drawings with Mark additions as shown at Temple in March 2020;
41 #define _HALL_RADIAL_BOUNDARY_ (350.0 * etm::cm)
42 
44 
45 // Prefer zero default values;
46 #define _SAFETY_CLEARANCE_DEFAULT_ (0.0)
47 #define _VISUAL_CLEARANCE_DEFAULT_ (0.0)
48 
49 #define _CROSSING_ANGLE_DEFAULT_ ( 0.025)
50 
51 #ifdef _OPENCASCADE_
52 #define Printf Printf_opencascade
53 #include <gp_Pnt.hxx>
54 #include <TopoDS_Solid.hxx>
55 #include <BRepPrimAPI_MakeCylinder.hxx>
56 #include <BRep_Builder.hxx>
57 #include <TopoDS_Compound.hxx>
58 #include <TDocStd_Document.hxx>
59 #include <XCAFApp_Application.hxx>
60 #include <XCAFDoc_ShapeTool.hxx>
61 #include <XCAFDoc_ColorTool.hxx>
62 #include <XCAFDoc_DocumentTool.hxx>
63 #include <Quantity_Color.hxx>
64 #include <NCollection_Vector.hxx>
65 #include <STEPCAFControl_Writer.hxx>
66 
67 #include <BRepBuilderAPI_MakePolygon.hxx>
68 #include <BRepBuilderAPI_MakeFace.hxx>
69 #include <BRepPrimAPI_MakeRevol.hxx>
70 #undef Printf
71 #endif
72 
73 // ---------------------------------------------------------------------------------------
74 // ---------------------------------------------------------------------------------------
75 
77  mName("EicToyModel"),
78  mIrRegionLength(length),
79  mIrRegionRadius(radius), mAzimuthalSegmentation(0), mIpOffset(0.0),
80  mXdim(_CANVAS_WIDTH_DEFAULT_), mYdim(0),
81  mXsize(0.0), mYsize(0.0), mX0(0.0), mY0(0.0),
82  mColorLegendEnabled(true), mZoomedView(false), mCanvas(0),
83  mVacuumChamber(0),
84  mCurrentView(EicToyModel::kUndefined),
85  mMirrorImage(false), mOneSideMode(EicToyModel::kOff),
86  //mSafetyClearance(_SAFETY_CLEARANCE_DEFAULT_), mVisualClearance(_VISUAL_CLEARANCE_DEFAULT_),
87  mCrossingAngle(_CROSSING_ANGLE_DEFAULT_), mGeometryLocked(false), mNewCanvasRequired(true),
88  //mEicMedia(0)
89  mG4LogicalWorld(0), mDrawIP6boundaries(false), mUseDetectorHighlighting(false)
90 {
91  // Sanity check;
92  if (mInstance) {
93  printf("\n\n EicToyModel::EicToyModel() -> Singleton instance already exists!\n\n");
94  return;
95  } //if
96 
97  // FIXME: control duplicate invocations;
98  mInstance = this;
99 
100  // FIXME: should this be executed in a default ctor?; FIXME: hardcoded is OK?;
101  acceptance(-4.0, -1.0, 1.0, 4.0, true);
102 } // EicToyModel::EicToyModel()
103 
104 // ---------------------------------------------------------------------------------------
105 
106 EicToyModel *EicToyModel::Import(const char *fname)
107 {
108  // Sanity check;
109  if (mInstance) {
110  printf("\n\n EicToyModel::Import() -> Singleton instance already exists!\n\n");
111  return 0;//-1;
112  } //if
113 
114  // Import the ROOT file with an "EicToyModel" singleton class instance;
115  {
116  TFile fin(fname);
117 
118  if (!dynamic_cast<EicToyModel *>(fin.Get("EicToyModel"))) {
119  printf("Wrong file format (%s): no EicToyModel instance found!\n\n\n", fname);
120  return 0;//-1;
121  } //if
122 
123  //mVacuumChamber->mTGeoModel = 0;
124 
125  fin.Close();
126  }
127 
128  return mInstance;//0;
129 } // EicToyModel:Import()
130 
131 // ---------------------------------------------------------------------------------------
132 
134 {
135  // A bit of consistency checks;
137 
138  return this;
139 } // EicToyModel::DefineVacuumChamber()
140 
141 // ---------------------------------------------------------------------------------------
142 
143 //EicToyModel *EicToyModel::DefineVacuumChamber(const char *fname)
144 //{
145 //
146 //return this;
147 //} // EicToyModel::DefineVacuumChamber()
148 
149 // ---------------------------------------------------------------------------------------
150 
151 G4VPhysicalVolume *EicToyModel::ConstructG4World( void )
152 {
153 #ifdef _ETM2GEANT_
154  auto air = G4NistManager::Instance()->FindOrBuildMaterial("G4_AIR");
155 
156  double cff = cm/etm::cm;
157  // Define the "experimental hall"; units are TGeo [cm], as stored;
158  auto expHall_box = new G4Box("World",
159  cff*GetIrRegionRadius(),
160  cff*GetIrRegionRadius(),
161  cff*GetIrRegionLength()/2);
162  auto expHall_log = new G4LogicalVolume(expHall_box, air, "World", 0, 0, 0);
163 
164  return new G4PVPlacement(0, G4ThreeVector(), expHall_log, "World", 0, false, 0);
165 #else
166  printf("\n\n G4 support is not compiled in; use 'cmake -DGEANT=YES ...' \n\n");
167  return 0;
168 #endif
169 } // EicToyModel::ConstructG4World()
170 
171 // ---------------------------------------------------------------------------------------
172 
173 void EicToyModel::PlaceG4Volumes(G4LogicalVolume *world)
174 {
175  for(auto stack: mStacks)
176  for(auto det: stack->mDetectors)
177  det->PlaceG4Volume(world);
178 } // EicToyModel::PlaceG4Volumes()
179 
180 // ---------------------------------------------------------------------------------------
181 
182 void EicToyModel::PlaceG4Volumes(G4VPhysicalVolume *world)
183 {
184 #ifdef _ETM2GEANT_
185  PlaceG4Volumes(world->GetLogicalVolume());
186 #endif
187 } // EicToyModel::PlaceG4Volumes()
188 
189 // ---------------------------------------------------------------------------------------
190 
191 EicToyModel *EicToyModel::acceptance(double eta0, double eta1, double eta2, double eta3,
192  bool reset_stacks, bool redraw)
193 {
194  // Order the values;
195  std::set<double> buffer;
196  buffer.insert(eta0); buffer.insert(eta1); buffer.insert(eta2); buffer.insert(eta3);
197 
198  if (buffer.size() != 4) {
199  printf("At least two of the 'eta' values given to EicToyModel::acceptance() are the same!\n");
200  return this;
201  } //if
202 
203  for(auto stack: mStacks)
204  for(unsigned bf=0; bf<2; bf++)
205  // Perhaps the same eta will be "removed" more than once; ignore;
206  mEtaLines.erase(stack->EtaBoundary(bf)->GetEta());
207 
208  for(auto eta: buffer)
209  // Request only the label to be printed out by DrawEtaLines(); the crack itself will
210  // be drawn by DrawEtaBoundaries(); FIXME: may want to unify these two functionalities,
211  // since they are anyway calling the same CalculateEtaEndPoint() internally;
212  AddEtaLine(eta, false, true);
213 
214  if (reset_stacks) {
215  mStacks.clear();
216 
217  // vtx(), bck(), mid(), fwd() to the moment;
218  const char *labels[] = {"VTX", "BCK", "MID", "FWD"};
219  for(unsigned st=0; st<4; st++)
220  mStacks.push_back(new EtmDetectorStack(labels[st]));
221  } //if
222 
223  // Sequence is: vtx(), bck(), mid(), fwd(); NB: it is essential that vtx() is earlier
224  // than mid(), since its distance calculation is used to determine mid() offset;
225  vtx()->SetEtaBoundaries(eta1, eta2);
226  bck()->SetEtaBoundaries(eta0, eta1);
227  mid()->SetEtaBoundaries(eta1, eta2);
228  fwd()->SetEtaBoundaries(eta2, eta3);
229 
230  if (redraw && mCanvas) DrawMe();
231 
232  return this;
233 } // EicToyModel::acceptance()
234 
235 // ---------------------------------------------------------------------------------------
236 // ---------------------------------------------------------------------------------------
237 
239 {
240  auto ip = GetIpLocation();
241  TVector2 ip_down(mIpOffset, -1E-6);
242  double dx[2] = {0.0, 0.0};
243 
244  if (!single) {
245  for(unsigned bf=0; bf<2; bf++) {
246  auto boundary = vtx()->mEtaBoundaries[bf];
247  // FIXME: do not care about vh/tb?; should be the same, or?;
248  auto &vtxarr = boundary->mCrackNodes[0][0];
249 
250  dx[bf] = vtxarr.size() > 2 ? vtxarr[vtxarr.size()-2].X() : 0.0;
251  } //for bf
252  } //if
253 
254  auto ll = EtmLine2D(TVector2(dx[0], 0), TVector2(0,1));
255  auto rr = EtmLine2D(TVector2(dx[1], 0), TVector2(0,1));
256 
257  for(auto stack: mStacks) {
258  if (single && stack != single) continue;
259 
260  for(unsigned bf=0; bf<2; bf++) {
261  bool account_crossing_angle = stack == fwd() && bf;
262  auto boundary = stack->mEtaBoundaries[bf];
263 
264  for(unsigned vh=0; vh<2; vh++) {
265  for(unsigned tb=0; tb<2; tb++) {
266  double tbsign = tb ? -1.0 : 1.0;
267  double dyv = tbsign*vtx()->FullLength();
268  double dym = tbsign*mid()->mStartingDistance;
269 
270  auto lcv = EtmLine2D(ip + TVector2( 0,dyv), TVector2(1,0));
271  auto lcm = EtmLine2D(ip + TVector2( 0,dym), TVector2(1,0));
272 
273  TVector2 ip_native = CalculateEtaStartPoint(boundary->GetEta(), vh, tb,
274  account_crossing_angle);
275 
276  auto &vtxarr = boundary->mCrackNodes[vh][tb];
277  vtxarr.clear();
278 
279  if (stack == vtx() || !dyv ||
280  (stack == mid() && bf && !dx[1]) ||
281  (stack == bck() && !bf && !dx[0]) ||
282  (stack == fwd() && bf && !dx[1]))
283  vtxarr.push_back(ip_native);
284  else {
285  auto L = CalculateEtaEndPoint(boundary->GetEta(), vh, tb,
286  account_crossing_angle).first;
287  auto arrow = L - ip, n = arrow.Unit();
288  auto l0 = EtmLine2D(ip, n);
289 
290  vtxarr.push_back(ip_down);
291 
292  // These are mutually exclusive; no 'else if' for better readability;
293  if ((stack == bck() && bf) || stack == mid() || (stack == fwd() && !bf)) {
294  vtxarr.push_back(TVector2(ip.X(), dym));
295  vtxarr.push_back(TVector2(lcm.Cross(l0)));
296  } //if
297  if (stack == bck() && !bf) {
298  vtxarr.push_back(TVector2(ip.X(), dyv));
299  vtxarr.push_back(TVector2(lcv.Cross(ll)));
300  vtxarr.push_back(l0.Cross(ll));
301  }
302  if (stack == fwd() && bf) {
303  vtxarr.push_back(TVector2(ip.X(), dyv));
304  vtxarr.push_back(TVector2(lcv.Cross(rr)));
305  vtxarr.push_back(l0.Cross(rr));
306  }
307  } //if
308 
309  // This is not clean, but should suffice for all practical purposes;
310  // NB: do not want to account for the crossing at eta~1.0, for sure
311  // (integration volume creating will become overcomplicated);
312  vtxarr.push_back(CalculateEtaEndPoint(boundary->GetEta(), vh, tb,
313  account_crossing_angle).first);
314  } //for tb
315  } //for vh
316  } //for bf
317  } //for stack
318 } // EicToyModel::CalculateDefaultEtaBoundaries()
319 
320 // ---------------------------------------------------------------------------------------
321 
322 EicToyModel *EicToyModel::ip(double offset, bool redraw)
323 {
324  if (!mGeometryLocked && offset != mIpOffset) {
325  mIpOffset = offset;
327 
328  if (redraw && mCanvas) DrawMe();
329  } //if
330 
331  return this;
332 } // EicToyModel::ip()
333 
334 // ---------------------------------------------------------------------------------------
335 
336 EicToyModel *EicToyModel::ir(double length, double radius, bool redraw) {
338 
339  home(redraw);
340 
341  return this;
342 } // EicToyModel::ir()
343 
344 // ---------------------------------------------------------------------------------------
345 
347 {
349 
350  if (redraw && mCanvas) DrawMe();
351 
352  return this;
353 } // EicToyModel::RebuildEverything()
354 
355 // ---------------------------------------------------------------------------------------
356 
358 {
359  // Same value -> nothing to do;
360  if (value == mCrossingAngle) return this;
361 
362  // Change is only allowed if vacuum chamber design is consistent with it;
364  mCrossingAngle = value;
365 
366  return RebuildEverything(redraw);
367 } // EicToyModel::SetCrossingAngle()
368 
369 // ---------------------------------------------------------------------------------------
370 // ---------------------------------------------------------------------------------------
371 
373 {
374  mXdim = width; mNewCanvasRequired = true;
375  home();
376 
377  return this;
378 } // EicToyModel::width()
379 
380 // ---------------------------------------------------------------------------------------
381 
382 EicToyModel *EicToyModel::mirror(bool what, bool redraw)
383 {
384  if (mMirrorImage != what) {
385  mMirrorImage = what;
386  mNewCanvasRequired = true;
387  home(redraw);
388  } //if
389 
390  return this;
391 } // EicToyModel::mirror()
392 
393 // ---------------------------------------------------------------------------------------
394 
395 EicToyModel *EicToyModel::legend(bool what, bool redraw)
396 {
397  mColorLegendEnabled = what;
398  home(redraw);
399 
400  return this;
401 } // EicToyModel::legend()
402 
403 // ---------------------------------------------------------------------------------------
404 
405 EicToyModel *EicToyModel::AddEtaLine(double value, bool line, bool label, bool redraw)
406 {
407  mEtaLines[value] = std::make_pair(line, label);
408 
409  if (redraw && mCanvas) DrawMe();
410 
411  return this;
412 } // EicToyModel::AddEtaLine()
413 
414 // ---------------------------------------------------------------------------------------
415 // ---------------------------------------------------------------------------------------
416 
417 void EicToyModel::write(bool everything, bool lock)
418 {
419  Export((GetName() + TString(".root")).Data(), everything, lock);
420 } // EicToyModel::write()
421 
422 // ---------------------------------------------------------------------------------------
423 
424 void EicToyModel::ExportVacuumChamber(const char *fname)
425 {
426  TString str(fname ? TString(fname) : GetName() + TString(".vc.gdml"));
427 
428  if (str.EndsWith(".gdml")) {
429  if (mVacuumChamber)
430  mVacuumChamber->Export(str.Data());
431  else
432  printf("EicToyModel::ExportVacuumChamber() -> vacuum chamber is not defined!\n");
433  }
434  else
435  printf("EicToyModel::ExportVacuumChamber(\"%s\") -> .gdml file extension expected!\n",
436  fname);
437 } // EicToyModel::ExportVacuumChamber()
438 
439 // ---------------------------------------------------------------------------------------
440 
441 void EicToyModel::ExportCADmodelCore(std::vector<std::pair<const TColor*, const TopoDS_Shape*> > shapes,
442  const char *fname)
443 {
444 #ifdef _OPENCASCADE_
445  // Create XCAF document;
446  Handle(TDocStd_Document) outDoc;
447  Handle(XCAFApp_Application) outApp = XCAFApp_Application::GetApplication();
448  outApp->NewDocument("ETM", outDoc);
449 
450  Handle (XCAFDoc_ShapeTool) outAssembly = XCAFDoc_DocumentTool::ShapeTool (outDoc->Main());
451 
452  Handle(XCAFDoc_ColorTool) outColors = XCAFDoc_DocumentTool::ColorTool(outDoc->Main());
453 
454  // Loop through all shapes;
455  for(auto shape: shapes) {
456  auto rcolor = shape.first;
457 
458  // Assign ROOT TGeo color;
459  Quantity_Color ccolor(rcolor->GetRed(), rcolor->GetGreen(), rcolor->GetBlue(), Quantity_TOC_RGB);
460  TDF_Label aLabel = outAssembly->AddShape(*shape.second);
461  outColors->SetColor(aLabel, ccolor, XCAFDoc_ColorSurf);
462  } //for shape
463 
464  // Write the file out;
465  {
466  STEPCAFControl_Writer cWriter;
467 
468  cWriter.Transfer(outDoc, STEPControl_ManifoldSolidBrep);
469  //cWriter.Transfer(outDoc, STEPControl_AsIs);
470  cWriter.Write(fname);
471  }
472 #else
473  printf("EicToyModel::Export(\"%s\") -> OpenCascade support is not compiled in!\n", fname);
474 #endif
475 } // EicToyModel::ExportCADmodelCore()
476 
477 // ---------------------------------------------------------------------------------------
478 
479 void EicToyModel::ExportCADmodel(const char *fname)
480 {
481  std::vector<std::pair<const TColor*, const TopoDS_Shape*> > shapes;
482 
483  // FIXME: admittedly this scheme with memory accumulation is not the most efficient
484  // one; however models are small, then who cares; FIXME: memory leak, dtor;
485  for(auto stack: mStacks)
486  for(auto det: stack->mDetectors) {
487  auto shape = det->BuildCADmodel();
488  shapes.insert(shapes.end(), shape.begin(), shape.end());
489  } //for stack..det
490 
491  ExportCADmodelCore(shapes, fname);
492 } // EicToyModel::ExportCADmodel()
493 
494 // ---------------------------------------------------------------------------------------
495 
496 void EicToyModel::Export(const char *fname, bool everything, bool lock)
497 {
498  if (fname) {
499  TString str(fname);
500 
501  if (str.EndsWith(".root")) {
502  TFile fout(str.Data(), "RECREATE");
503 
504  // Permanently lock the geometry if requested;
505  if (lock) mGeometryLocked = true;
506 
507  if (everything) {
508  if (mVacuumChamber) {
509  // Save vacuum chamber TGeo tree in the same file;
510  mVacuumChamber->GetWorld()->CloseGeometry();
511  mVacuumChamber->GetWorld()->CheckOverlaps(0.0001);
512  mVacuumChamber->GetWorld()->Write();
513 
514  // And also store GDML dump as a separate TObjString; this is kind of redundant
515  // since one can always restore this GDML dump using TGeoManager dump; however
516  // technically there can be a situation where gGeoManager is initialized already,
517  // and it may preferrable not to disturb it; anyway, the whole idea behind this
518  // dump is to provide yeat another option to import the vacuum system geometry
519  // without loading ETM library (the dump is store as a separate object);
521  } //if
522 
523 #if 0//def _OPENCASCADE_
524  {
525  // FIXME: unify with EtmVacuumChamber::StoreGDMLdump();
526  const char *qfname = "/tmp/tmp.stp";
527 
528  ExportCADmodel(qfname);
529  std::ifstream fin(qfname);
530 
531  TString str;
532  str.ReadFile(fin);
533 
534  TObjString ostr; ostr.SetString(str);
535  // FIXME: hardcoded (Central Detector);
536  ostr.Write("CD.STEP");
537 
538  // Remove the temporary file;
539  unlink(qfname);
540  }
541 #endif
542  } //if
543 
544  // Save EicToyModel class instance itself (ROOT serializer);
545  TObject::Write("EicToyModel");
546 
547  fout.Close();
548  } else if (str.EndsWith(".stp")) {
549  ExportCADmodel(fname);
550  } else {
551  printf("EicToyModel::Export(\"%s\") -> unknown file name extension!\n", fname);
552  } //if
553  } else
554  printf("EicToyModel::Export() -> null pointer given as file name!\n");
555 } // EicToyModel::Export()
556 
557 // ---------------------------------------------------------------------------------------
558 // ---------------------------------------------------------------------------------------
559 
561 {
562  return (mStacks.size() >= 1 ? mStacks[0] : 0);
563 } // EicToyModel::vtx()
564 
565 // ---------------------------------------------------------------------------------------
566 
568 {
569  return (mStacks.size() >= 2 ? mStacks[1] : 0);
570 } // EicToyModel::bck()
571 
572 // ---------------------------------------------------------------------------------------
573 
575 {
576  return (mStacks.size() >= 3 ? mStacks[2] : 0);
577 } // EicToyModel::mid()
578 
579 // ---------------------------------------------------------------------------------------
580 
582 {
583  return (mStacks.size() >= 4 ? mStacks[3] : 0);
584 } // EicToyModel::fwd()
585 
586 // ---------------------------------------------------------------------------------------
587 
589 {
590  for(auto stack: mStacks)
591  if (stack != vtx() && stack->etamin() <= eta && eta < stack->etamax()) return stack;
592 
593  // Yet consider a special case;
594  if (eta == fwd()->EtaBoundary(1)->GetEta()) return fwd();
595 
596  return 0;
597 } // EicToyModel::GetStack()
598 
599 // ---------------------------------------------------------------------------------------
600 
602  mFlatFieldLinesEta.insert(eta);
603 
604  return this;
605 } // EicToyModel::DrawFlatFieldLines()
606 
607 // ---------------------------------------------------------------------------------------
608 
609 TVector2 EicToyModel::cnv(const TVector2 &world) const
610 {
611  return TVector2((world.X() - mX0)/mXsize, (world.Y() - mY0)/mYsize);
612 } // EicToyModel::cnv()
613 
614 // ---------------------------------------------------------------------------------------
615 
616 void EicToyModel::home(bool redraw)
617 {
618  // NB: this does not affect the canvas size (will be preserved);
623  switch (mOneSideMode) {
624  case EicToyModel::kOff:
625  mX0 = -mXsize/2;
626  break;
627  case EicToyModel::kLeft:
629  break;
630  case EicToyModel::kRight:
632  break;
633  } //switch
635 
636  // Well, this is not 100% correct, but a new window will almost certainly be needed;
637  mNewCanvasRequired = true;
638 
639  mZoomedView = false;
640 
641  if (redraw && mCanvas) DrawMe();
642 } // EicToyModel::home()
643 
644 // ---------------------------------------------------------------------------------------
645 
646 void EicToyModel::zoom(double blX, double blY, double trX, double trY, bool redraw)
647 {
648  mXsize = fabs(trX - blX);
649  mYsize = fabs(trY - blY);
650  mX0 = blX < trX ? blX : trX;
651  mY0 = blY < trY ? blY : trY;
652 
653  if (ConditionChecker(mXsize && mYsize, "EicToyModel::zoom() call: zero width (height)")) {
654  mZoomedView = true;
655 
656  // Well, this is not 100% correct, but a new window will almost certainly be needed;
657  mNewCanvasRequired = true;
658 
659  if (redraw && mCanvas) DrawMe();
660  } //if
661 } // EicToyModel::zoom()
662 
663 // ---------------------------------------------------------------------------------------
664 
666  double toffset, etm::Stretch how)
667 {
669  push_back(new EtmBoundaryModificationRequest(hostdet, refdet, toffset, how));
670 } // EicToyModel::AddBoundaryModificationRequest()
671 
672 // ---------------------------------------------------------------------------------------
673 
674 std::vector<TVector2> EicToyModel::Band(const std::vector<TVector2> &chain,
675  const EtmLine2D &from, const EtmLine2D &to)
676 {
677  unsigned dim = chain.size();
678  std::vector<TVector2> ret;
679 
680  // FIXME: need to place from->to order check;
681 
682  // 'From' line: go up, away from the IP, and start filling the buffer as soon as
683  // the distance goes positive; from that point on check the 'to' condition;
684  for(unsigned ivtx=0; ivtx<dim; ivtx++) {
685  auto const &vcurr0 = chain[ivtx];
686  double d0 = from.Distance(vcurr0);
687 
688  if (d0 >= 0.0) {
689  // First node; ivtx!=0 since IP must certainly be bad (?);
690  assert(ivtx);
691  auto const &vprev0 = chain[ivtx-1];
692 
693  EtmLine2D ll(vcurr0, (vcurr0-vprev0).Unit());
694  if (from.IsParallel(ll)) continue;
695  //assert(!from.IsParallel(ll));
696  ret.push_back(from.Cross(ll));
697 
698  for(unsigned jvtx=ivtx; jvtx<dim; jvtx++) {
699  auto const &vcurr1 = chain[jvtx];
700  double d1 = to.Distance(vcurr1);
701 
702  if (d1 <= 0.0) {
703  ret.push_back(vcurr1);
704  continue;
705  } else {
706  // For sure ret.size() != 0 here;
707  //assert(jvtx);
708  auto const &vprev1 = chain[jvtx-1];
709 
710  EtmLine2D ll(vcurr1, (vcurr1-vprev1).Unit());
711  ret.push_back(to.Cross(ll));
712  return ret;
713  } //if
714  } //for jvtx
715  } //if
716  } //for ivtx
717 
718  // Make the compiler happy;
719  return ret;
720 } // EicToyModel::Band()
721 
722 // ---------------------------------------------------------------------------------------
723 
724 std::pair<std::vector<TVector2>, std::vector<TVector2> >
725 EicToyModel::GetIntersection(const std::vector<TVector2> &chain,
726  const EtmLine2D &line)
727 {
728  unsigned dim = chain.size();
729  std::pair<std::vector<TVector2>, std::vector<TVector2> > ret;
730  std::vector<TVector2> &before = ret.first, &after = ret.second;
731 
732  for(unsigned ivtx=0; ivtx<dim; ivtx++) {
733  auto const &vcurr = chain[ivtx];
734 
735  //printf("%7.2f %7.2f -> %7.2f\n", vcurr.X(), vcurr.Y(), line.Distance(vcurr));
736  if (line.Distance(vcurr) < 0.0)
737  //if (line.Distance(vcurr) <= 0.0)
738  before.push_back(vcurr);
739  else {
740  auto const &vprev = chain[ivtx-1];
741  EtmLine2D lcross(vcurr, (vcurr-vprev).Unit());
742  //assert(!line.IsParallel(lcross));
743  if (!line.IsParallel(lcross)) {
744  // Yes, account the crossing point in both sub-chains;
745  before.push_back(line.Cross(lcross));
746  after .push_back(line.Cross(lcross));
747  after .push_back(vcurr);
748 
749  for(unsigned jvtx=ivtx+1; jvtx<dim; jvtx++)
750  after.push_back(chain[jvtx]);
751 
752  return ret;
753  } //if
754  } //if
755  } //for ivtx
756 
757  // Make compiler happy;
758  return ret;
759 } // EicToyModel::GetIntersection()
760 
761 // ---------------------------------------------------------------------------------------
762 
763 void EicToyModel::AppendChain(std::vector<TVector2> &chain, const std::vector<TVector2> &add)
764 {
765  // FIXME: use insert();
766  //for(unsigned ivtx=0; ivtx<add.size(); ivtx++)
767  //chain.push_back(add[ivtx]);
768 
769  chain.insert(chain.end(), add.begin(), add.end());
770 } // EicToyModel::AppendChain()
771 
772 // ---------------------------------------------------------------------------------------
773 
774 std::vector<TVector2> EicToyModel::RebuildCrack(const std::vector<TVector2> &chain,
775  const EtmLine2D &am, const EtmLine2D &ap,
776  const EtmLine2D &cut, etm::Stretch how)
777 {
778  std::vector<TVector2> ret;
779  // Prefer clarity rather than efficiency;
780  std::pair<std::vector<TVector2>, std::vector<TVector2> > cm = GetIntersection(chain, am);
781  std::pair<std::vector<TVector2>, std::vector<TVector2> > cp = GetIntersection(chain, ap);
782  std::pair<std::vector<TVector2>, std::vector<TVector2> > cc = GetIntersection(chain, cut);
783  // Relative position of this point with respect to the [am..ap] band defines the
784  // output chain; then there are three options how to modify the output, on top of this;
785  //printf("%d %d\n", cc.first.size(), cc.second.size());
786  const TVector2 &C = cc.second[0];
787  double dcm = am.Distance(C), dcp = ap.Distance(C);
788 
789  // Few more useful 2D points;
790  const TVector2 &L = chain[chain.size()-1];
791  const TVector2 /*&A = cp.second[0], &B = cm.second[0],*/ &P = cut.Cross(am), &Q = cut.Cross(ap);
792  // FIXME: this is not good;
793  bool vcut = fabs(cut.V().X()) < fabs(cut.V().Y());
794  TVector2 D = TVector2(vcut ? Q.X() : L.X(), vcut ? L.Y() : Q.Y());
795 
796  if (dcm <= 0.0 && dcp < 0.0) {
797  switch (how) {
798  case etm::kDent:
799  return chain;
800  case etm::kRecess:
801  AppendChain(ret, cc.first);
802  //ret.push_back(B);
803  //ret.push_back(P);
804  ret.push_back(Q);
805  AppendChain(ret, cp.second);
806  break;
807  case etm::kWall:
808  AppendChain(ret, cc.first);
809  //ret.push_back(B);
810  //ret.push_back(P);
811  ret.push_back(D);
812  ret.push_back(L);
813  break;
814  } //switch
815  } else if (dcm > 0.0 && dcp < 0.0) {
816  switch (how) {
817  case etm::kDent:
818  AppendChain(ret, cm.first);
819  ret.push_back(P);
820  AppendChain(ret, cc.second);
821  break;
822  case etm::kRecess:
823  AppendChain(ret, cm.first);
824  ret.push_back(P);
825  ret.push_back(Q);
826  AppendChain(ret, cp.second);
827  break;
828  case etm::kWall:
829  AppendChain(ret, cm.first);
830  ret.push_back(P);
831  ret.push_back(D);
832  ret.push_back(L);
833  break;
834  } //switch
835  } else {
836  switch (how) {
837  case etm::kDent:
838  //AppendChain(ret, cm.first);
839  //ret.push_back(P);
840  //ret.push_back(Q);
841  //ret.push_back(A);
842  //AppendChain(ret, cp.second);
843  //break;
844  ;
845  case etm::kRecess:
846  AppendChain(ret, cm.first);
847  ret.push_back(P);
848  AppendChain(ret, cc.second);
849  break;
850  case etm::kWall:
851  AppendChain(ret, cm.first);
852  ret.push_back(P);
853  ret.push_back(D);
854  ret.push_back(L);
855  break;
856  } //switch
857  } //if
858 
859  return ret;
860 } // EicToyModel::RebuildCrack()
861 
862 // ---------------------------------------------------------------------------------------
863 
865 {
867 
868  // Loop through all the request *in*the*order*they*were*submitted* in a .C script;
869  for(auto request: mBoundaryModificationRequests) {
870  auto hstack = request->mHostDetector->mStack;
871  if (single && hstack != single) continue;
872 
873  auto rstack = request->mReferenceDetector->mStack;
874  auto host = request->mHostDetector, ref = request->mReferenceDetector;
875 
876  // NB: 'ref' pointer may be to a 'dummy' detector (to be ignored);
877  if (host) {
878  bool order = rstack->EtaBoundary(0)->GetEta() > hstack->EtaBoundary(0)->GetEta();
879  EtmEtaBoundary *boundaries[2] = {
880  hstack->EtaBoundary( order),
881  // NB: in case of the vtx() stack CalculateDefaultEtaBoundaries() did the job
882  // to correct either bck() or fwd() beamline boundaries; RebuildCrack() would
883  // not work anyway because crack lines are not monotonous;
884  hstack == vtx() ? 0 : rstack->EtaBoundary(!order)
885  };
886 
887  auto ip = GetIpLocation();
888 
889  for(unsigned vh=0; vh<2; vh++) {
890  for(unsigned tb=0; tb<2; tb++) {
891  double dthost = host->GetActualDistance();
892  double dtref = (ref && !ref->IsDummy()? ref->GetActualDistance() - ref->length()/2 : 0.0) +
893  request->mTransverseOffset;
894 
895  TVector2 aaxis(hstack->mAlignmentAxis.X(), (tb ? -1.0 : 1.0)*hstack->mAlignmentAxis.Y());
896  TVector2 caxis(rstack->mAlignmentAxis.X(), (tb ? -1.0 : 1.0)*rstack->mAlignmentAxis.Y());
897 
898  // So it seems like once the alignment axes are oriented away from the IP, it is the
899  // azimuthal symmetry used for signed point-to-line distance what matters -> rotate by
900  // -pi/2 in all cases;
901  auto am = EtmLine2D(ip + (dthost - host->length()/2 /*- SafetyClearance()/2*/)*aaxis,
902  aaxis.Rotate(-M_PI/2));
903  auto ap = EtmLine2D(ip + (dthost + host->length()/2 /*+ SafetyClearance()/2*/)*aaxis,
904  aaxis.Rotate(-M_PI/2));
905  auto cut = EtmLine2D(ip + (dtref /*- hcal->length()/2*/ /*- SafetyClearance()/2*/)*caxis,
906  caxis.Rotate(-M_PI/2));
907  // Range check; unify with EtmDetector::Build();
908  {
909  //double dmax = fabs(to.X().X());
910  bool central = host->mStack == vtx() || host->mStack == mid();
911 
912  // Yes, I know they are exclusive;
913  if ( central && fabs(cut.X().X()) > GetIrRegionLength()/2) return;
914  if (!central && fabs(cut.X().Y()) > GetIrRegionRadius()) return;
915  }
916 
917  for(unsigned bf=0; bf<2; bf++) {
918  auto boundary = boundaries[bf];
919  if (!boundary) continue;
920 
921  auto &vtxarr = boundary->mCrackNodes[vh][tb];
922 
923  vtxarr = RebuildCrack(vtxarr, am, ap, cut, request->mStretch);
924  } //for bf
925  } //for tb
926  } //for vh
927  } //if
928  } //for request
929 } // EicToyModel::ExecuteBoundaryModificationRequest()
930 
931 // ---------------------------------------------------------------------------------------
932 
934 {
935  // FIXME: this is a bit cubmersome: modify all edges except for those which stay
936  // at _DEFAULT_TRIM_VALUE_;
937  for(auto det: bck()->mDetectors)
938  det->trim(0.0, _DEFAULT_TRIM_VALUE_, true);
939 
940  for(auto det: mid()->mDetectors)
941  det->trim(_DEFAULT_TRIM_VALUE_, _DEFAULT_TRIM_VALUE_, true);
942 
943  for(auto det: fwd()->mDetectors)
944  det->trim(_DEFAULT_TRIM_VALUE_, 0.0, true);
945 } // EicToyModel::ApplyStandardTrimming()
946 
947 // ---------------------------------------------------------------------------------------
948 
950 {
951 #if _TODAY_
952  for(auto boundary: mEtaBoundaries)
953  for(unsigned vh=0; vh<2; vh++) {
954  auto &wdarr = boundary->mCrackWidths[vh];
955 
956  wdarr.clear();
957 
958  // Per default these will just be safety clearance values;
959  for(unsigned iq=0; iq<boundary->mCrackNodes[vh][0].size()-1; iq++)
960  wdarr.push_back(SafetyClearance());
961  } //for boundary..vh
962 #endif
963 } // EicToyModel::AccountServiceMaterials()
964 
965 // ---------------------------------------------------------------------------------------
966 
968 {
969  if (view != kUndefined && view != kCurrent) mCurrentView = view;
970 
971  // These need to be re-calculated only for the first entry;
972  if (!mCanvas) home(false);
973 
974  // This is now in pixels;
975  mYdim = mXdim * mYsize / mXsize;
976 
977  if (draw) {
978  gStyle->SetCanvasColor(0);
979 
980  if (mNewCanvasRequired) {
981  // If canvas was used already, delete it;
982  if (mCanvas) delete mCanvas;
983 
984  mCanvas = new TCanvas(GetName(), GetName(), 0, 0, mXdim, mYdim);
985 
986  mNewCanvasRequired = false;
987  } else {
988  // If canvas was used already, clean it;
989  if (mCanvas) mCanvas->Clear();
990  } //if
991  } //if
992 
993  // FIXME: is the recalculation needed here?; well, something was crashing ...;
994  if (!mGeometryLocked) {
995  // Calculate locations of all the detectors;
996  for(auto stack: mStacks)
997  stack->CalculateActualDistances();
998 
999  // FIXME: well, this is a bit too muc of a complication: first process all
1000  // the requests, which allow one to define the "size" of the vertex stack;
1001  // then make a second pass, where this information is accounted to build
1002  // beamline boundaries of bck() and fwd() stacks; NB: can not create per-stack
1003  // stretch() call vectors because these requests do not commute for different
1004  // stacks (so the sequence matters);
1007 
1008  //++PurgeBoundaryVertexArrays();
1010 
1011  // And now when all the stack boundaries are calculated, eventually
1012  // build the detectors;
1013  for(auto stack: mStacks)
1014  stack->Build();
1015  } //if
1016 
1017  unsigned vh = GetCurrentView() == EicToyModel::kVertical ? 0 : 1;
1018 
1019  // Loop through all stacks and build the detectors; populate the mirror image
1020  // if needed;
1021  for (unsigned tb=0; tb<2; tb++) {
1022  if (tb && !mMirrorImage) continue;
1023 
1024  for(auto stack: mStacks)
1025  // Draw them; yes, prefer to decouple building and drawing;
1026  for(auto det: stack->mDetectors)
1027  if (!mEnabledColorTags.size() ||
1028  mEnabledColorTags.find(*det->GetLabel()) != mEnabledColorTags.end())
1029  if (det->Polygons().size())
1030  det->DrawPolygon(det->Polygons()[vh*2+tb]);
1031  } //for tb
1032 
1033  if (draw) {
1034  // Start with the area, which describes the beam pipe;
1036 
1037  // Draw all booked acceptance limit lines; this looks a bit artificial, but I want to
1038  // decouple the actual acceptance boundary lines from the additional "drawn-only" ones;
1039  DrawEtaLines();
1041 
1042  // Draw the "beam line";
1043  DrawBeamLine();
1044 
1045  // Draw meter and half a meter ticks;
1046  DrawAxisTicks();
1047 
1048  // Draw regions where the field is supposed to be aligned with the solenoid axis;
1050 
1051  DrawMarkers();
1052 
1053  // Draw color map;
1056  } //if
1057 } // EicToyModel::DrawMe()
1058 
1059 // ---------------------------------------------------------------------------------------
1060 
1061 void EicToyModel::WriteText(const TVector2 &where, const TString &what, int color) const
1062 {
1063  TVector2 coord = cnv(where);
1064 
1065  // FIXME: here and in many other places: a memory leak;
1066  //auto text = new TText(coord.X(), coord.Y(), what);
1067  auto text = new TLatex(coord.X(), coord.Y(), what);
1068 
1069  text->SetTextAlign(22);
1070  text->SetTextColor(color);
1071  text->SetTextFont(43);
1072  text->SetTextSize(20*mXdim*(mOneSideMode == EicToyModel::kOff ? 1 : 2)/_CANVAS_WIDTH_DEFAULT_);
1073  text->Draw();
1074 } // EicToyModel::WriteText()
1075 
1076 // ---------------------------------------------------------------------------------------
1077 #if 1
1078 void EicToyModel::DrawPolygon(unsigned dim, const double xx[], const double yy[], int color,
1079  bool line) const
1080 {
1081  double xxc[dim+1], yyc[dim+1];
1082 
1083  for(unsigned iq=0; iq<dim; iq++) {
1084  auto xyc = cnv(TVector2(xx[iq], yy[iq]));
1085  xxc[iq] = xyc.X(); yyc[iq] = xyc.Y();
1086  } //for iq
1087  xxc[dim] = xxc[0]; yyc[dim] = yyc[0];
1088 
1089  // FIXME: memory leak;
1090  TPolyLine *poly = new TPolyLine(dim+1, xxc, yyc);
1091 
1092  poly->SetLineColor(kBlack);
1093  poly->SetFillColor(color);
1094  poly->SetLineWidth(1);
1095  poly->SetLineStyle(etm::solid);
1096  poly->Draw("F");
1097  if (line) poly->Draw();
1098 } // EicToyModel::DrawPolygon()
1099 
1100 // ---------------------------------------------------------------------------------------
1101 
1102 void EicToyModel::DrawRectangle(const TVector2 &bl, const TVector2 &tr, int color, bool line) const
1103 {
1104  double xx[4] = {bl.X(), tr.X(), tr.X(), bl.X()};
1105  double yy[4] = {bl.Y(), bl.Y(), tr.Y(), tr.Y()};
1106 
1107  DrawPolygon(4, xx, yy, color, line);
1108 } // DrawRectangle()
1109 #endif
1110 // ---------------------------------------------------------------------------------------
1111 
1112 void EicToyModel::DrawSingleLine(const TVector2 &from, const TVector2 &to, int color, int width,
1113  etm::LineStyle style) const
1114 {
1115  TVector2 p1 = cnv(from), p2 = cnv(to);
1116  double xx[2] = {p1.X(), p2.X()}, yy[2] = {p1.Y(), p2.Y()};
1117 
1118  // FIXME: memory leak;
1119  TPolyLine *poly = new TPolyLine(2, xx, yy);
1120 
1121  poly->SetLineColor(color);
1122  poly->SetLineWidth(width);
1123  poly->SetLineStyle(style);
1124  poly->Draw();
1125 } // EicToyModel::DrawSingleLine()
1126 
1127 // ---------------------------------------------------------------------------------------
1128 // ---------------------------------------------------------------------------------------
1129 
1130 TVector2 EicToyModel::CalculateEtaStartPoint(double eta, unsigned vh, unsigned tb,
1131  bool account_crossing_angle)
1132 {
1133  double theta = Eta2Theta(eta), tbsign = tb ? -1.0 : 1.0;
1134 
1135  if (eta > 0.0 && vh && account_crossing_angle) theta += tbsign*mCrossingAngle;
1136 
1137  TVector2 dxy = 1E-6*TVector2(1,0).Rotate(theta);
1138  return GetIpLocation() - TVector2(dxy.X(), tbsign*dxy.Y());
1139 } // EicToyModel::CalculateEtaStartPoint()
1140 
1141 // ---------------------------------------------------------------------------------------
1142 
1143 std::pair<TVector2, bool> EicToyModel::CalculateEtaEndPoint(double eta, unsigned vh, unsigned tb,
1144  bool account_crossing_angle)
1145 {
1146  double theta = Eta2Theta(eta), tbsign = tb ? -1.0 : 1.0;
1147 
1148  if (eta > 0.0 && vh && account_crossing_angle) theta += tbsign*mCrossingAngle;
1149 
1150  {
1151  double dx = mIrRegionLength/2. + 1E-6;
1152  double x0 = (theta < TMath::Pi()/2 ? 1.0 : -1.0)*dx;
1153  double y0 = (x0 - mIpOffset)*tan(theta);
1154 
1155  if (fabs(y0) < mIrRegionRadius)
1156  return std::make_pair(TVector2(x0, tbsign*y0), true);
1157  else
1158  return std::make_pair(TVector2(mIpOffset + mIrRegionRadius*tan(TMath::Pi()/2-theta),
1159  tbsign*mIrRegionRadius), false);
1160  }
1161 } // EicToyModel::CalculateEtaEndPoint()
1162 
1163 // ---------------------------------------------------------------------------------------
1164 
1165 void EicToyModel::DrawSingleEtaLine(double eta, const TVector2 &to,
1166  std::pair<bool, bool> what, bool side) const
1167 {
1168  // Line is drawn anyway;
1169  if (what.first) DrawSingleLine(GetIpLocation(), to, kBlack, 1, etm::dashdotted);
1170 
1171  // Label is optional;
1172  if (what.second) {
1173  TString label; label.Form("#eta %4.2f", eta);
1174 
1175  // FIXME: offsets hardcoded;
1176  double sign = to.Y() > 0.0 ? 1.0 : -1.0;
1177  WriteText(to + TVector2(!side ? 0.0 : (eta < 0.0 ? -22.0 : 22.0),
1178  !side ? sign*7.0*etm::cm : 0.0), label);
1179  } //if
1180 } // EicToyModel::DrawSingleEtaLine()
1181 
1182 // ---------------------------------------------------------------------------------------
1183 
1185 {
1186  // Draw all booked eta lines;
1187  for(unsigned tb=0; tb<2; tb++) {
1188  if (tb && !mMirrorImage) continue;
1189 
1190  for(auto eta: mEtaLines) {
1191  auto pt = CalculateEtaEndPoint(eta.first,
1192  GetCurrentView() == EicToyModel::kHorizontal ? 1 : 0, tb, true);
1193 
1194  DrawSingleEtaLine(eta.first, pt.first, eta.second, pt.second);
1195  } //for eta
1196  } //for tb
1197 } // EicToyModel::DrawEtaLines()
1198 
1199 //bool account_crossing_angle = stack == fwd() && bf;
1200 // if (eta > 0.0 && vh && account_crossing_angle) theta += tbsign*mCrossingAngle;
1201 
1202 // ---------------------------------------------------------------------------------------
1203 
1205 {
1206  unsigned vh = GetCurrentView() == EicToyModel::kHorizontal ? 1 : 0;
1207 
1208  for(unsigned tb=0; tb<2; tb++) {
1209  if (tb && !mMirrorImage) continue;
1210 
1211  for(auto stack: mStacks) {
1212  if (stack == vtx()) continue;
1213 
1214  for(unsigned im=0; im<2; im++) {
1215  auto boundary = stack->EtaBoundary(im);
1216  const auto &vtxarr = boundary->mCrackNodes[vh][tb];
1217 
1218  for(unsigned ivtx=1; ivtx<vtxarr.size(); ivtx++)
1219  DrawSingleLine(vtxarr[ivtx-1], vtxarr[ivtx], kBlack, 1, etm::dashdotted);
1220  } //for im
1221  } //for stack
1222  } //for tb
1223 } // EicToyModel::DrawEtaBoundaries()
1224 
1225 // ---------------------------------------------------------------------------------------
1226 
1228 {
1229  // Beam lines themselves;
1230  {
1231  TVector2 left(-mIrRegionLength/2., 0.0);
1233  tan(mCrossingAngle)*mIrRegionLength/2. : 0.0);
1234  // 25mrad solid line looks ugly, yet with the ticks it seems to be the best choice;
1235  DrawSingleLine(left, GetIpLocation());//, kBlack, 1, etm::dashed);
1236  DrawSingleLine(GetIpLocation(), right);//, kBlack, 1, etm::dashed);
1237  }
1238 
1239  if (mDrawIP6boundaries) {
1240  WriteText(TVector2(-300.0, 365.0), "R = 375 cm", kRed);
1241  //DrawSingleLine(TVector2(-450.0, 375.0), TVector2( 450.0, 375.0), kRed, 3, etm::dashdotted);
1242  DrawSingleLine(TVector2(-450.0, 375.0), TVector2( 500.0, 375.0), kRed, 3, etm::dashdotted);
1243  DrawSingleLine(TVector2(-450.0, 375.0), TVector2(-450.0, 0.0), kRed, 3, etm::dashdotted);
1244  //DrawSingleLine(TVector2( 450.0, 375.0), TVector2( 450.0, 0.0), kRed, 3, etm::dashdotted);
1245  //DrawSingleLine(TVector2( 450.0, 375.0), TVector2( 500.0, 375.0), kRed, 1, etm::dashed);
1246  //DrawSingleLine(TVector2( 500.0, 375.0), TVector2( 500.0, 0.0), kRed, 1, etm::dashed);
1247  DrawSingleLine(TVector2( 500.0, 375.0), TVector2( 500.0, 0.0), kRed, 3, etm::dashdotted);
1248 
1249  WriteText(TVector2(-300.0, 400.0), "R = 410 cm", kBlue);
1250  DrawSingleLine(TVector2(-410.0, 410.0), TVector2( 410.0, 410.0), kBlue, 3, etm::dashdotted);
1251  DrawSingleLine(TVector2(-410.0, 410.0), TVector2(-410.0, 0.0), kBlue, 3, etm::dashdotted);
1252  DrawSingleLine(TVector2( 410.0, 410.0), TVector2( 410.0, 0.0), kBlue, 3, etm::dashdotted);
1253  //DrawSingleLine(TVector2( 410.0, 410.0), TVector2( 470.0, 410.0), kBlue, 1, etm::dashed);
1254  //DrawSingleLine(TVector2( 470.0, 410.0), TVector2( 470.0, 0.0), kBlue, 1, etm::dashed);
1255  } //if
1256 #if _TODAY_
1257  for(unsigned tb=0; tb<2; tb++) {
1258  if (tb && !mMirrorImage) continue;
1259 
1260  // FIXME: hardcoded;
1261  double y0 = (tb ? -1.0 : 1.0)*_HALL_RADIAL_BOUNDARY_;
1262 
1263  DrawSingleLine(TVector2(-mIrRegionLength/2, y0), TVector2(mIrRegionLength/2, y0),
1264  kRed, 2, etm::dashed);
1265  } //for tb
1266 #endif
1267 } // EicToyModel::DrawBeamLine()
1268 
1269 // ---------------------------------------------------------------------------------------
1270 
1272 {
1273  EtmDetectorStack *stacks[2] = {fwd(), bck()};
1274 
1275  for(unsigned fb=0; fb<2; fb++) {
1276  auto stack = stacks[fb];
1277  auto marker = stack->get(_MARKER_);
1278 
1279  if (!marker->IsDummy()) {
1280  TVector2 spot = GetIpLocation() + marker->mActualDistance*stack->AlignmentAxis(), dy(0.0, 5 * etm::cm);
1281  TVector2 from = cnv(spot - TVector2(0.0, 10 * etm::cm)), to = cnv(spot);
1282 
1283  // FIXME: memory leak;
1284  auto arrow = new TArrow(from.X(), from.Y(), to.X(), to.Y(), 0.005, "|>");
1285  arrow->SetLineWidth(1);
1286  arrow->SetLineColor(kRed);
1287  arrow->Draw();
1288  } //if
1289  } //for fb
1290 
1291  {
1292  double alength = 30.0 * etm::cm;
1293 
1294  for(unsigned lr=0; lr<2; lr++) {
1295  // These lines are too short, so a 25mrad angle would look ugly -> 0.0;
1296  //double dr = (!lr && GetCurrentView() == EicToyModel::kHorizontal) ?
1297  //-alength*tan(mVacuumChamber->mCrossingAngle) : 0.0;
1298  TVector2 from = cnv(TVector2(mIpOffset + (lr ? 1.0 : -1.0)*alength, /*dr*/0.0));
1299  TVector2 to = cnv(GetIpLocation());
1300 
1301  auto arrow = new TArrow(from.X(), from.Y(), to.X(), to.Y(), 0.007, "|>");
1302  arrow->SetLineWidth(2);
1303  arrow->SetLineColor(lr ? kBlue : kRed);
1304  arrow->Draw();
1305  } //for lr
1306 
1307  WriteText(TVector2(mIpOffset - 20.0, -10.0), "p", kRed);
1308  WriteText(TVector2(mIpOffset + 20.0, -10.0), "e", kBlue);
1309  }
1310 } // EicToyModel::DrawMarkers()
1311 
1312 // ---------------------------------------------------------------------------------------
1313 
1315 {
1316  for(auto eta: mFlatFieldLinesEta) {
1317  auto stack = GetStack(eta);
1318  if (stack && (stack == fwd() || stack == bck())) {
1319  // First try to get MARKER object location;
1320  auto marker = stack->get(_MARKER_);
1321 
1322  if (!marker->IsDummy()) {
1323  double sign = (eta < 0.0 ? -1 : 1);
1324  double zmarker = marker ? sign*marker->GetActualDistance() + GetIpLocation().X() : 0.0;
1325 
1326  // FIXME: step hardcoded;
1327  double zmax = zmarker, rstep = 5 * etm::cm;
1328  double theta = Eta2Theta(eta), dz = zmax - GetIpLocation().X();
1329  double rmax = fabs(dz*tan(theta));
1330 
1331  for(unsigned tb=0; tb<2; tb++) {
1332  if (tb && !mMirrorImage) continue;
1333 
1334  for(unsigned iq=0; (iq+0)*rstep < rmax; iq++) {
1335  TVector2 from, to;
1336  double y0 = (tb ? -1.0 : 1.0)*iq*rstep;
1337 
1338  from = TVector2(GetIpLocation().X() + iq*rstep/tan(theta), y0), to = TVector2(zmax, y0);
1339  DrawSingleLine(from, to, kRed, 1, etm::dotted);
1340  } //for iq
1341 
1342  // Draw the surrounding triangle;
1343  TVector2 apex(zmax, (tb ? -1.0 : 1.0)*rmax);
1344  DrawSingleLine(apex, GetIpLocation(), kRed, 1, etm::dotted);
1345  DrawSingleLine(apex, TVector2(zmax, 0.0), kRed, 1, etm::dotted);
1346  } //for tb
1347  } //if
1348  } //if
1349  } //for eta
1350 } // EicToyModel::DrawFlatFieldLines()
1351 
1352 // ---------------------------------------------------------------------------------------
1353 
1355 {
1356  // Should just be large enough and odd;
1357  unsigned dim = 99;
1358  double step = 50.0 * etm::cm;
1359  for(unsigned iq=0; iq<dim; iq++) {
1360  double zOffset = step*(iq - (dim-1)/2.);
1361 
1362  if (abs(zOffset) > mIrRegionLength/2) continue;
1363 
1364  // So lift the ticks in case of a horizontal view, but does not lift the meter labels;
1365  double lift = (mMirrorImage ? ((iq%2) ? 5.0 : 2.5) * etm::cm : 0.0);
1366  double dlift = (zOffset > mIpOffset && GetCurrentView() == EicToyModel::kHorizontal) ?
1367  (zOffset - mIpOffset)*tan(mCrossingAngle) : 0.0;
1368  TVector2 from(zOffset, lift + dlift), to(zOffset, ((iq%2) ? -10.0 : -5.0) * etm::cm + lift + dlift);
1369  // '3': mark the nominal (0,0) point in some way;
1370  DrawSingleLine(from, to, kBlack, zOffset ? 1 : 3);
1371 
1372  // 1m labels; do not want to mess up when mirror image is present;
1373  if (!mMirrorImage && iq%2) {
1374  TString label;
1375  if (zOffset)
1376  label.Form("%c%5.1f", zOffset > 0.0 ? '+' : '-', fabs(zOffset));
1377  else
1378  // Can not format it centered otherwise; hmm;
1379  label.Form("0.0");
1380 
1381  WriteText(to - TVector2(0.0, 7.0 * etm::cm + dlift), label);
1382  } //if
1383  } //for iq
1384 
1385  for(unsigned tb=0; tb<2; tb++) {
1386  if (tb && !mMirrorImage) continue;
1387 
1388  double tbsign = tb ? -1.0 : 1.0;
1389 
1390  for(unsigned lr=0; lr<2; lr++) {
1391  double lrsign = lr ? 1.0 : -1.0, z0 = lrsign*mIrRegionLength/2;
1392 
1393  DrawSingleLine(TVector2(z0, 0.0), TVector2(z0, tbsign*mIrRegionRadius));
1394 
1395  for(unsigned iq=0; iq<dim; iq++) {
1396  double yOffset = step*iq;
1397 
1398  if (yOffset > mIrRegionRadius) continue;
1399 
1400  TVector2 from(z0, tbsign*yOffset), to(z0 + lrsign*((iq%2) ? 5.0 : 10.0), tbsign*yOffset);
1401 
1402  DrawSingleLine(from, to);
1403  } //for iq
1404  } //for lr
1405  } //for tb
1406 } // EicToyModel::DrawAxisTicks()
1407 
1408 // ---------------------------------------------------------------------------------------
1409 
1410 void EicToyModel::DrawColorLegend(unsigned rows)
1411 {
1412  if (!rows) rows = 1;
1413 
1414  // FIXME: this was written before EicPalette; merge the two;
1415  std::map<TString, int> legend;
1416  // Loop through all registered objects and create a map;
1417  // No check for duplicates?; well, I call new EtmDetector() myself ...;
1418  for(auto stack: mStacks)
1419  for(auto det: stack->mDetectors)
1420  if (!mEnabledColorTags.size() ||
1421  mEnabledColorTags.find(*det->GetLabel()) != mEnabledColorTags.end())
1422  // No check for duplicates?; well, I call new EtmDetector() myself ...;
1423  det->RegisterFillColor(legend);
1424 
1425  {
1426  unsigned counter = 0;
1427  double step = _COLOR_LEGEND_STEP_, yOffset0 = mY0 + mYsize - _COLOR_LEGEND_HEIGHT_;
1428  double zOffset = mX0 + _COLOR_LEGEND_HEIGHT_ + step/2;
1429  for(auto label: legend) {
1430  int color = label.second;
1431  if (color == _GAP_COLOR_ || label.first == _MARKER_) continue;
1432 
1433  unsigned Xjump = rows == 1 ? 1 : counter%rows, Yjump = rows == 1 ? 0 : counter%rows;
1434  double yOffset = yOffset0 - Yjump*(_COLOR_LEGEND_HEIGHT_+_COLOR_LEGEND_GAP_);
1435 
1436  TVector2 bl(zOffset - step/2 + _COLOR_LEGEND_GAP_/2, yOffset - _COLOR_LEGEND_HEIGHT_/2);
1437  TVector2 tr(zOffset + step/2 - _COLOR_LEGEND_GAP_/2, yOffset + _COLOR_LEGEND_HEIGHT_/2);
1438  DrawRectangle(bl, tr, color);
1439 
1440  WriteText(TVector2(zOffset, yOffset), label.first.Data(),
1441  // FIXME: this is not good, all hardcoded;
1442  (color == kBlack || color == kBlue+1 || color == kAzure+4 || color == kViolet+1) ? kWhite : kBlack);
1443 
1444  zOffset += Xjump*step; counter++;
1445  } //for label
1446  } //for label
1447 } // EicToyModel::DrawColorLegend()
1448 
1449 // ---------------------------------------------------------------------------------------
1450 // ---------------------------------------------------------------------------------------
1451 
1453 
1454 
1455 
1456 
1457 
1458 
1459