Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TGeoCompositeShape.cxx
Go to the documentation of this file.
1 // @(#)root/geom:$Id$
2 // Author: Andrei Gheata 31/01/02
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TGeoCompositeShape
13 \ingroup Geometry_classes
14 
15 Class handling Boolean composition of shapes
16 
17  Composite shapes are Boolean combination of two or more shape
18 components. The supported boolean operations are union (+), intersection (*)
19 and subtraction. Composite shapes derive from the base TGeoShape class,
20 therefore providing all shape features : computation of bounding box, finding
21 if a given point is inside or outside the combination, as well as computing the
22 distance to entering/exiting. It can be directly used for creating volumes or
23 used in the definition of other composite shapes.
24 
25  Composite shapes are provided in order to complement and extend the set of
26 basic shape primitives. They have a binary tree internal structure, therefore
27 all shape-related geometry queries are signals propagated from top level down
28 to the final leaves, while the provided answers are assembled and interpreted
29 back at top. This CSG hierarchy is effective for small number of components,
30 while performance drops dramatically for large structures. Building a complete
31 geometry in this style is virtually possible but highly not recommended.
32 
33 ### Structure of composite shapes
34 
35  A composite shape can always be regarded as the result of a Boolean operation
36 between only two shape components. All information identifying these two
37 components as well as their positions with respect to the frame of the composite
38 is represented by an object called Boolean node. A composite shape just have
39 a pointer to such a Boolean node. Since the shape components may also be
40 composites, they will also contain binary Boolean nodes branching other two
41 shapes in the hierarchy. Any such branch ends-up when the final leaves are no
42 longer composite shapes, but basic primitives.
43 
44 \image html geom_booltree.png
45 
46  Suppose that A, B, C and D represent basic shapes, we will illustrate
47 how the internal representation of few combinations look like. We do this
48 only for the sake of understanding how to create them in a proper way, since
49 the user interface for this purpose is in fact very simple. We will ignore
50 for the time being the positioning of components. The definition of a composite
51 shape takes an expression where the identifiers are shape names. The
52 expression is parsed and decomposed in 2 sub-expressions and the top-level
53 Boolean operator.
54 
55 #### A+B+C
56 
57  This represent the union of A, B and C. Both union operators are at the
58 same level. Since:
59 
60 ~~~ {.cpp}
61  A+B+C = (A+B)+C = A+(B+C)
62 ~~~
63 
64 the first (+) is taken as separator, hence the expression split:
65 
66 ~~~ {.cpp}
67  A and B+C
68 ~~~
69 
70 A Boolean node of type TGeoUnion("A", "B+C") is created. This tries to replace
71 the 2 expressions by actual pointers to corresponding shapes.
72 The first expression (A) contains no operators therefore is interpreted as
73 representing a shape. The shape named "A" is searched into the list of shapes
74 handled by the manager class and stored as the "left" shape in the Boolean
75 union node. Since the second expression is not yet fully decomposed, the "right"
76 shape in the combination is created as a new composite shape. This will split
77 at its turn B+C into B and C and create a TGeoUnion("B","C"). The B and C
78 identifiers will be looked for and replaced by the pointers to the actual shapes
79 into the new node. Finally, the composite "A+B+C" will be represented as:
80 
81 ~~~ {.cpp}
82  A
83  |
84  [A+B+C] = (+) B
85  | |
86  [B+C] = (+)
87  |
88  C
89 ~~~
90 
91 where [] is a composite shape, (+) is a Boolean node of type union and A, B,
92 C are pointers to the corresponding shapes.
93  Building this composite shapes takes the following line :
94 
95 ~~~ {.cpp}
96  TGeoCompositeShape *cs1 = new TGeoCompositeShape("CS1", "A+B+C");
97 ~~~
98 
99 #### (A+B)-(C+D)
100  This expression means: subtract the union of C and D from the union of A and
101 B. The usage of parenthesis to force operator precedence is always recommended.
102 The representation of the corresponding composite shape looks like:
103 
104 ~~~ {.cpp}
105  A
106  |
107  [A+B] = (+)
108  | |
109  [(A+B)-(C+D)] = (-) C B
110  | |
111  [C+D]=(+)
112  |
113  D
114 ~~~
115 
116 ~~~ {.cpp}
117  TGeoCompositeShape *cs2 = new TGeoCompositeShape("CS2", "(A+B)-(C+D)");
118 ~~~
119 
120  Building composite shapes as in the 2 examples above is not always quite
121 useful since we were using un-positioned shapes. When supplying just shape
122 names as identifiers, the created boolean nodes will assume that the shapes
123 are positioned with an identity transformation with respect to the frame of
124 the created composite. In order to provide some positioning of the combination
125 components, we have to attach after each shape identifier the name of an
126 existing transformation, separated by a colon. Obviously all transformations
127 created for this purpose have to be objects with unique names in order to be
128 properly substituted during parsing.
129  Let's look at the code implementing the second example :
130 
131 ~~~ {.cpp}
132  TGeoTranslation *t1 = new TGeoTranslation("t1",0,0,-20);
133  TGeoTranslation *t2 = new TGeoTranslation("t2",0,0, 20);
134  TGeoRotation *r1 = new TGeoRotation("r1"); // transformations need names
135  r1->SetAngles(90,30,90,120,0,0); // rotation with 30 degrees about Z
136  TGeoTube *a = new TGeoTube(0, 10,20);
137  a->SetName("A"); // shapes need names too
138  TGeoTube *b = new TGeoTube(0, 20,20);
139  b->SetName("B");
140  TGeoBBox *c = new TGeoBBox(10,10,50);
141  c->SetName("C");
142  TGeoBBox *d = new TGeoBBox(50,10,10);
143  d->SetName("D");
144 
145  TGeoCompositeShape *cs;
146  cs = new TGeoCompositeShape("CS", "(A:t1+B:t2)-(C+D:r1)");
147 ~~~
148 
149  The newly created composite looks like 2 cylinders of different radii sitting
150 one on top of the other and having 2 rectangular holes : a longitudinal one
151 along Z axis corresponding to C and an other one in the XY plane due to D.
152  One should have in mind that the same shape or matrix identifier can be
153 used many times in the same expression. For instance:
154 
155 ~~~ {.cpp}
156  (A:t1-A:t2)*B:t1
157 ~~~
158 
159 is a valid expression. Expressions that cannot be parsed or identifiers that
160 cannot be substituted by existing objects generate error messages.
161  Composite shapes can be subsequently used for defining volumes. Moreover,
162 these volumes may have daughters but these have to obey overlapping/extruding
163 rules (see TGeoVolume). Volumes created based on composite shapes cannot be
164 divided. Visualization of such volumes is currently not implemented.
165 */
166 
167 #include "Riostream.h"
168 #include "TRandom3.h"
169 
170 #include "TGeoManager.h"
171 #include "TGeoMatrix.h"
172 #include "TGeoBoolNode.h"
173 #include "TVirtualGeoPainter.h"
174 
175 #include "TVirtualPad.h"
176 #include "TVirtualViewer3D.h"
177 #include "TBuffer3D.h"
178 #include "TBuffer3DTypes.h"
179 
180 #include "TGeoCompositeShape.h"
181 ClassImp(TGeoCompositeShape);
182 
183 ////////////////////////////////////////////////////////////////////////////////
184 /// Needed just for cleanup.
185 
186 void TGeoCompositeShape::ClearThreadData() const
187 {
188  if (fNode) fNode->ClearThreadData();
189 }
190 
191 ////////////////////////////////////////////////////////////////////////////////
192 /// Needed just for cleanup.
193 
194 void TGeoCompositeShape::CreateThreadData(Int_t nthreads)
195 {
196  if (fNode) fNode->CreateThreadData(nthreads);
197 }
198 
199 ////////////////////////////////////////////////////////////////////////////////
200 /// Default constructor
201 
202 TGeoCompositeShape::TGeoCompositeShape()
203  :TGeoBBox(0, 0, 0)
204 {
205  SetShapeBit(TGeoShape::kGeoComb);
206  fNode = 0;
207 }
208 
209 ////////////////////////////////////////////////////////////////////////////////
210 /// Default constructor
211 
212 TGeoCompositeShape::TGeoCompositeShape(const char *name, const char *expression)
213  :TGeoBBox(0, 0, 0)
214 {
215  SetShapeBit(TGeoShape::kGeoComb);
216  SetName(name);
217  fNode = 0;
218  MakeNode(expression);
219  if (!fNode) {
220  Error("ctor", "Composite %s: cannot parse expression: %s", name, expression);
221  return;
222  }
223  ComputeBBox();
224 }
225 
226 ////////////////////////////////////////////////////////////////////////////////
227 /// Default constructor
228 
229 TGeoCompositeShape::TGeoCompositeShape(const char *expression)
230  :TGeoBBox(0, 0, 0)
231 {
232  SetShapeBit(TGeoShape::kGeoComb);
233  fNode = 0;
234  MakeNode(expression);
235  if (!fNode) {
236  TString message = TString::Format("Composite (no name) could not parse expression %s", expression);
237  Error("ctor", "%s", message.Data());
238  return;
239  }
240  ComputeBBox();
241 }
242 
243 ////////////////////////////////////////////////////////////////////////////////
244 /// Constructor with a Boolean node
245 
246 TGeoCompositeShape::TGeoCompositeShape(const char *name, TGeoBoolNode *node)
247  :TGeoBBox(0,0,0)
248 {
249  SetName(name);
250  fNode = node;
251  if (!fNode) {
252  Error("ctor", "Composite shape %s has null node", name);
253  return;
254  }
255  ComputeBBox();
256 }
257 
258 ////////////////////////////////////////////////////////////////////////////////
259 /// destructor
260 
261 TGeoCompositeShape::~TGeoCompositeShape()
262 {
263  if (fNode) delete fNode;
264 }
265 
266 ////////////////////////////////////////////////////////////////////////////////
267 /// Computes capacity of this shape [length^3] by sampling with 1% error.
268 
269 Double_t TGeoCompositeShape::Capacity() const
270 {
271  Double_t pt[3];
272  if (!gRandom) gRandom = new TRandom3();
273  Double_t vbox = 8*fDX*fDY*fDZ; // cm3
274  Int_t igen=0;
275  Int_t iin = 0;
276  while (iin<10000) {
277  pt[0] = fOrigin[0]-fDX+2*fDX*gRandom->Rndm();
278  pt[1] = fOrigin[1]-fDY+2*fDY*gRandom->Rndm();
279  pt[2] = fOrigin[2]-fDZ+2*fDZ*gRandom->Rndm();
280  igen++;
281  if (Contains(pt)) iin++;
282  }
283  Double_t capacity = iin*vbox/igen;
284  return capacity;
285 }
286 
287 ////////////////////////////////////////////////////////////////////////////////
288 /// compute bounding box of the sphere
289 
290 void TGeoCompositeShape::ComputeBBox()
291 {
292  if(fNode) fNode->ComputeBBox(fDX, fDY, fDZ, fOrigin);
293 }
294 
295 ////////////////////////////////////////////////////////////////////////////////
296 /// Computes normal vector in POINT to the composite shape.
297 
298 void TGeoCompositeShape::ComputeNormal(const Double_t *point, const Double_t *dir, Double_t *norm)
299 {
300  if (fNode) fNode->ComputeNormal(point,dir,norm);
301 }
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 /// Tests if point is inside the shape.
305 
306 Bool_t TGeoCompositeShape::Contains(const Double_t *point) const
307 {
308  if (fNode) return fNode->Contains(point);
309  return kFALSE;
310 }
311 
312 ////////////////////////////////////////////////////////////////////////////////
313 /// Compute closest distance from point px,py to each corner.
314 
315 Int_t TGeoCompositeShape::DistancetoPrimitive(Int_t px, Int_t py)
316 {
317  const Int_t numPoints = GetNmeshVertices();
318  return ShapeDistancetoPrimitive(numPoints, px, py);
319 }
320 
321 ////////////////////////////////////////////////////////////////////////////////
322 /// Compute distance from outside point to this composite shape.
323 /// Check if the bounding box is crossed within the requested distance
324 
325 Double_t TGeoCompositeShape::DistFromOutside(const Double_t *point, const Double_t *dir, Int_t iact,
326  Double_t step, Double_t *safe) const
327 {
328  Double_t sdist = TGeoBBox::DistFromOutside(point,dir, fDX, fDY, fDZ, fOrigin, step);
329  if (sdist>=step) return TGeoShape::Big();
330  if (fNode) return fNode->DistFromOutside(point, dir, iact, step, safe);
331  return TGeoShape::Big();
332 }
333 
334 ////////////////////////////////////////////////////////////////////////////////
335 /// Compute distance from inside point to outside of this composite shape.
336 
337 Double_t TGeoCompositeShape::DistFromInside(const Double_t *point, const Double_t *dir, Int_t iact,
338  Double_t step, Double_t *safe) const
339 {
340  if (fNode) return fNode->DistFromInside(point, dir, iact, step, safe);
341  return TGeoShape::Big();
342 }
343 
344 ////////////////////////////////////////////////////////////////////////////////
345 /// Divide all range of iaxis in range/step cells
346 
347 TGeoVolume *TGeoCompositeShape::Divide(TGeoVolume * /*voldiv*/, const char * /*divname*/, Int_t /*iaxis*/,
348  Int_t /*ndiv*/, Double_t /*start*/, Double_t /*step*/)
349 {
350  Error("Divide", "Composite shapes cannot be divided");
351  return 0;
352 }
353 
354 ////////////////////////////////////////////////////////////////////////////////
355 /// Returns numbers of vertices, segments and polygons composing the shape mesh.
356 
357 void TGeoCompositeShape::GetMeshNumbers(Int_t &nvert, Int_t &nsegs, Int_t &npols) const
358 {
359  nvert = GetNmeshVertices();
360  nsegs = 0;
361  npols = 0;
362 }
363 
364 ////////////////////////////////////////////////////////////////////////////////
365 /// print shape parameters
366 
367 void TGeoCompositeShape::InspectShape() const
368 {
369  printf("*** TGeoCompositeShape : %s = %s\n", GetName(), GetTitle());
370  printf(" Bounding box:\n");
371  TGeoBBox::InspectShape();
372 }
373 
374 ////////////////////////////////////////////////////////////////////////////////
375 /// Make a boolean node according to the top level boolean operation of expression.
376 /// Propagates signal to branches until expression is fully decomposed.
377 /// printf("Making node for : %s\n", expression);
378 
379 void TGeoCompositeShape::MakeNode(const char *expression)
380 {
381  if (fNode) delete fNode;
382  fNode = 0;
383  SetTitle(expression);
384  TString sleft, sright, smat;
385  Int_t boolop;
386  boolop = TGeoManager::Parse(expression, sleft, sright, smat);
387  if (boolop<0) {
388  // fail
389  Error("MakeNode", "parser error");
390  return;
391  }
392  if (smat.Length())
393  Warning("MakeNode", "no geometrical transformation allowed at this level");
394  switch (boolop) {
395  case 0:
396  Error("MakeNode", "Expression has no boolean operation");
397  return;
398  case 1:
399  fNode = new TGeoUnion(sleft.Data(), sright.Data());
400  return;
401  case 2:
402  fNode = new TGeoSubtraction(sleft.Data(), sright.Data());
403  return;
404  case 3:
405  fNode = new TGeoIntersection(sleft.Data(), sright.Data());
406  }
407 }
408 
409 ////////////////////////////////////////////////////////////////////////////////
410 /// Paint this composite shape into the current 3D viewer
411 /// Returns bool flag indicating if the caller should continue to
412 /// paint child objects
413 
414 Bool_t TGeoCompositeShape::PaintComposite(Option_t *option) const
415 {
416  Bool_t addChildren = kTRUE;
417 
418  TVirtualGeoPainter *painter = gGeoManager->GetGeomPainter();
419  TVirtualViewer3D * viewer = gPad->GetViewer3D();
420  if (!painter || !viewer) return kFALSE;
421 
422  if (fNode) {
423  // Fill out the buffer for the composite shape - nothing extra
424  // over TGeoBBox
425  Bool_t preferLocal = viewer->PreferLocalFrame();
426  if (TBuffer3D::GetCSLevel()) preferLocal = kFALSE;
427  static TBuffer3D buffer(TBuffer3DTypes::kComposite);
428  FillBuffer3D(buffer, TBuffer3D::kCore|TBuffer3D::kBoundingBox,
429  preferLocal);
430 
431  Bool_t paintComponents = kTRUE;
432 
433  // Start a composite shape, identified by this buffer
434  if (!TBuffer3D::GetCSLevel())
435  paintComponents = viewer->OpenComposite(buffer, &addChildren);
436 
437  TBuffer3D::IncCSLevel();
438 
439  // Paint the boolean node - will add more buffers to viewer
440  TGeoHMatrix *matrix = (TGeoHMatrix*)TGeoShape::GetTransform();
441  TGeoHMatrix backup(*matrix);
442  if (preferLocal) matrix->Clear();
443  if (paintComponents) fNode->Paint(option);
444  if (preferLocal) *matrix = backup;
445  // Close the composite shape
446  if (!TBuffer3D::DecCSLevel())
447  viewer->CloseComposite();
448  }
449 
450  return addChildren;
451 }
452 
453 ////////////////////////////////////////////////////////////////////////////////
454 /// Register the shape and all components to TGeoManager class.
455 
456 void TGeoCompositeShape::RegisterYourself()
457 {
458  if (gGeoManager->GetListOfShapes()->FindObject(this)) return;
459  gGeoManager->AddShape(this);
460  TGeoMatrix *matrix;
461  TGeoShape *shape;
462  TGeoCompositeShape *comp;
463  if (fNode) {
464  matrix = fNode->GetLeftMatrix();
465  if (!matrix->IsRegistered()) matrix->RegisterYourself();
466  else if (!gGeoManager->GetListOfMatrices()->FindObject(matrix)) {
467  gGeoManager->GetListOfMatrices()->Add(matrix);
468  }
469  matrix = fNode->GetRightMatrix();
470  if (!matrix->IsRegistered()) matrix->RegisterYourself();
471  else if (!gGeoManager->GetListOfMatrices()->FindObject(matrix)) {
472  gGeoManager->GetListOfMatrices()->Add(matrix);
473  }
474  shape = fNode->GetLeftShape();
475  if (!gGeoManager->GetListOfShapes()->FindObject(shape)) {
476  if (shape->IsComposite()) {
477  comp = (TGeoCompositeShape*)shape;
478  comp->RegisterYourself();
479  } else {
480  gGeoManager->AddShape(shape);
481  }
482  }
483  shape = fNode->GetRightShape();
484  if (!gGeoManager->GetListOfShapes()->FindObject(shape)) {
485  if (shape->IsComposite()) {
486  comp = (TGeoCompositeShape*)shape;
487  comp->RegisterYourself();
488  } else {
489  gGeoManager->AddShape(shape);
490  }
491  }
492  }
493 }
494 
495 ////////////////////////////////////////////////////////////////////////////////
496 /// computes the closest distance from given point to this shape, according
497 /// to option. The matching point on the shape is stored in spoint.
498 
499 Double_t TGeoCompositeShape::Safety(const Double_t *point, Bool_t in) const
500 {
501  if (fNode) return fNode->Safety(point,in);
502  return 0.;
503 }
504 
505 ////////////////////////////////////////////////////////////////////////////////
506 /// Save a primitive as a C++ statement(s) on output stream "out".
507 
508 void TGeoCompositeShape::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
509 {
510  if (TObject::TestBit(kGeoSavePrimitive)) return;
511  if (fNode) fNode->SavePrimitive(out,option);
512  out << " // Shape: " << GetName() << " type: " << ClassName() << std::endl;
513  out << " TGeoShape *" << GetPointerName() << " = new TGeoCompositeShape(\"" << GetName() << "\", pBoolNode);" << std::endl;
514  if (strlen(GetTitle())) out << " " << GetPointerName() << "->SetTitle(\"" << GetTitle() << "\");" << std::endl;
515  TObject::SetBit(TGeoShape::kGeoSavePrimitive);
516 }
517 
518 ////////////////////////////////////////////////////////////////////////////////
519 /// create points for a composite shape
520 
521 void TGeoCompositeShape::SetPoints(Double_t *points) const
522 {
523  if (fNode) fNode->SetPoints(points);
524 }
525 
526 ////////////////////////////////////////////////////////////////////////////////
527 /// create points for a composite shape
528 
529 void TGeoCompositeShape::SetPoints(Float_t *points) const
530 {
531  if (fNode) fNode->SetPoints(points);
532 }
533 
534 ////////////////////////////////////////////////////////////////////////////////
535 /// compute size of this 3D object
536 
537 void TGeoCompositeShape::Sizeof3D() const
538 {
539  if (fNode) fNode->Sizeof3D();
540 }
541 
542 ////////////////////////////////////////////////////////////////////////////////
543 /// Return number of vertices of the mesh representation
544 
545 Int_t TGeoCompositeShape::GetNmeshVertices() const
546 {
547  if (!fNode) return 0;
548  return fNode->GetNpoints();
549 }
550 
551 ////////////////////////////////////////////////////////////////////////////////
552 /// Check the inside status for each of the points in the array.
553 /// Input: Array of point coordinates + vector size
554 /// Output: Array of Booleans for the inside of each point
555 
556 void TGeoCompositeShape::Contains_v(const Double_t *points, Bool_t *inside, Int_t vecsize) const
557 {
558  for (Int_t i=0; i<vecsize; i++) inside[i] = Contains(&points[3*i]);
559 }
560 
561 ////////////////////////////////////////////////////////////////////////////////
562 /// Compute the normal for an array o points so that norm.dot.dir is positive
563 /// Input: Arrays of point coordinates and directions + vector size
564 /// Output: Array of normal directions
565 
566 void TGeoCompositeShape::ComputeNormal_v(const Double_t *points, const Double_t *dirs, Double_t *norms, Int_t vecsize)
567 {
568  for (Int_t i=0; i<vecsize; i++) ComputeNormal(&points[3*i], &dirs[3*i], &norms[3*i]);
569 }
570 
571 ////////////////////////////////////////////////////////////////////////////////
572 /// Compute distance from array of input points having directions specified by dirs. Store output in dists
573 
574 void TGeoCompositeShape::DistFromInside_v(const Double_t *points, const Double_t *dirs, Double_t *dists, Int_t vecsize, Double_t* step) const
575 {
576  for (Int_t i=0; i<vecsize; i++) dists[i] = DistFromInside(&points[3*i], &dirs[3*i], 3, step[i]);
577 }
578 
579 ////////////////////////////////////////////////////////////////////////////////
580 /// Compute distance from array of input points having directions specified by dirs. Store output in dists
581 
582 void TGeoCompositeShape::DistFromOutside_v(const Double_t *points, const Double_t *dirs, Double_t *dists, Int_t vecsize, Double_t* step) const
583 {
584  for (Int_t i=0; i<vecsize; i++) dists[i] = DistFromOutside(&points[3*i], &dirs[3*i], 3, step[i]);
585 }
586 
587 ////////////////////////////////////////////////////////////////////////////////
588 /// Compute safe distance from each of the points in the input array.
589 /// Input: Array of point coordinates, array of statuses for these points, size of the arrays
590 /// Output: Safety values
591 
592 void TGeoCompositeShape::Safety_v(const Double_t *points, const Bool_t *inside, Double_t *safe, Int_t vecsize) const
593 {
594  for (Int_t i=0; i<vecsize; i++) safe[i] = Safety(&points[3*i], inside[i]);
595 }