00001 // @(#)root/geom:$Id: TGeoCompositeShape.cxx 36535 2010-11-08 14:41:54Z agheata $ 00002 // Author: Andrei Gheata 31/01/02 00003 00004 /************************************************************************* 00005 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * 00006 * All rights reserved. * 00007 * * 00008 * For the licensing terms see $ROOTSYS/LICENSE. * 00009 * For the list of contributors see $ROOTSYS/README/CREDITS. * 00010 *************************************************************************/ 00011 00012 00013 /////////////////////////////////////////////////////////////////////////////// 00014 // TGeoCompositeShape - class handling Boolean composition of shapes 00015 // 00016 // Composite shapes are Boolean combination of two or more shape 00017 // components. The supported boolean operations are union (+), intersection (*) 00018 // and subtraction. Composite shapes derive from the base TGeoShape class, 00019 // therefore providing all shape features : computation of bounding box, finding 00020 // if a given point is inside or outside the combination, as well as computing the 00021 // distance to entering/exiting. It can be directly used for creating volumes or 00022 // used in the definition of other composite shapes. 00023 // Composite shapes are provided in order to complement and extend the set of 00024 // basic shape primitives. They have a binary tree internal structure, therefore 00025 // all shape-related geometry queries are signals propagated from top level down 00026 // to the final leaves, while the provided answers are assembled and interpreted 00027 // back at top. This CSG hierarchy is effective for small number of components, 00028 // while performance drops dramatically for large structures. Building a complete 00029 // geometry in this style is virtually possible but highly not recommended. 00030 // 00031 // Structure of composite shapes 00032 // 00033 // A composite shape can always be regarded as the result of a Boolean operation 00034 // between only two shape components. All information identifying these two 00035 // components as well as their positions with respect to the frame of the composite 00036 // is represented by an object called Boolean node. A composite shape just have 00037 // a pointer to such a Boolean node. Since the shape components may also be 00038 // composites, they will also contain binary Boolean nodes branching other two 00039 // shapes in the hierarcy. Any such branch ends-up when the final leaves are no 00040 // longer composite shapes, but basic primitives. 00041 // 00042 //Begin_Html 00043 /* 00044 <img src="gif/t_booltree.jpg"> 00045 */ 00046 //End_Html 00047 // 00048 // Suppose that A, B, C and D represent basic shapes, we will illustrate 00049 // how the internal representation of few combinations look like. We do this 00050 // only for the sake of understanding how to create them in a proper way, since 00051 // the user interface for this purpose is in fact very simple. We will ignore 00052 // for the time being the positioning of components. The definition of a composite 00053 // shape takes an expression where the identifiers are shape names. The 00054 // expression is parsed and decomposed in 2 sub-expressions and the top-level 00055 // Boolean operator. 00056 // 00057 // 1. A+B+C 00058 // This represent the union of A, B and C. Both union operators are at the 00059 // same level. Since: 00060 // A+B+C = (A+B)+C = A+(B+C) 00061 // the first (+) is taken as separator, hence the expression splitted: 00062 // A and B+C 00063 // A Boolean node of type TGeoUnion("A", "B+C") is created. This tries to replace 00064 // the 2 expressions by actual pointers to corresponding shapes. 00065 // The first expression (A) contains no operators therefore is interpreted as 00066 // representing a shape. The shape named "A" is searched into the list of shapes 00067 // handled by the manager class and stored as the "left" shape in the Boolean 00068 // union node. Since the second expression is not yet fully decomposed, the "right" 00069 // shape in the combination is created as a new composite shape. This will split 00070 // at its turn B+C into B and C and create a TGeoUnion("B","C"). The B and C 00071 // identifiers will be looked for and replaced by the pointers to the actual shapes 00072 // into the new node. Finally, the composite "A+B+C" will be represented as: 00073 // 00074 // A 00075 // | 00076 // [A+B+C] = (+) B 00077 // | | 00078 // [B+C] = (+) 00079 // | 00080 // C 00081 // 00082 // where [] is a composite shape, (+) is a Boolean node of type union and A, B, 00083 // C are pointers to the corresponding shapes. 00084 // Building this composite shapes takes the following line : 00085 // TGeoCompositeShape *cs1 = new TGeoCompositeShape("CS1", "A+B+C"); 00086 // 00087 // 2. (A+B)\(C+D) 00088 // This expression means: subtract the union of C and D from the union of A and 00089 // B. The usage of paranthesys to force operator precedence is always recommended. 00090 // The representation of the corresponding composite shape looks like: 00091 // 00092 // A 00093 // | 00094 // [A+B] = (+) 00095 // | | 00096 // [(A+B)\(C+D)] = (\) C B 00097 // | | 00098 // [C+D]=(+) 00099 // | 00100 // D 00101 // 00102 // TGeoCompositeShape *cs2 = new TGeoCompositeShape("CS2", "(A+B)\(C+D)"); 00103 // 00104 // Building composite shapes as in the 2 examples above is not always quite 00105 // usefull since we were using unpositioned shapes. When suplying just shape 00106 // names as identifiers, the created boolean nodes will assume that the shapes 00107 // are positioned with an identity transformation with respect to the frame of 00108 // the created composite. In order to provide some positioning of the combination 00109 // components, we have to attach after each shape identifier the name of an 00110 // existing transformation, separated by a colon. Obviously all transformations 00111 // created for this purpose have to be objects with unique names in order to be 00112 // properly substituted during parsing. 00113 // Let's look at the code implementing the second example : 00114 // 00115 // TGeoTranslation *t1 = new TGeoTranslation("T1",0,0,-20); 00116 // TGeoTranslation *t2 = new TGeoTranslation("T2",0,0, 20); 00117 // TGeoRotation *r1 = new TGeoRotation("R1"); // transformations need names 00118 // r1->SetAngles(90,30,90,120,0,0); // rotation with 30 degrees about Z 00119 // TGeoTube *a = new TGeoTube(0, 10,20); 00120 // a->SetName("A"); // shapes need names too 00121 // TGeoTube *b = new TGeoTube(0, 20,20); 00122 // b->SetName("B"); 00123 // TGeoBBox *c = new TGeoBBox(10,10,50); 00124 // c->SetName("C"); 00125 // TGeoBBox *d = new TGeoBBox(50,10,10); 00126 // d->SetName("D"); 00127 // 00128 // TGeoCompositeShape *cs; 00129 // cs = new TGeoCompositeShape("CS", "(A:t1+B:t2)\(C+D:r1)"); 00130 // 00131 // The newly created composite looks like 2 cylinders of different radii sitting 00132 // one on top of the other and having 2 rectangular holes : a longitudinal one 00133 // along Z axis corresponding to C and an other one in the XY plane due to D. 00134 // One should have in mind that the same shape or matrix identifier can be 00135 // used many times in the same expression. For instance: 00136 // 00137 // (A:t1-A:t2)*B:t1 00138 // 00139 // is a valid expression. Expressions that cannot be parsed or identifiers that 00140 // cannot be substituted by existing objects generate error messages. 00141 // Composite shapes can be subsequently used for defining volumes. Moreover, 00142 // these volumes may have daughters but these have to obbey overlapping/extruding 00143 // rules (see TGeoVolume). Volumes created based on composite shapes cannot be 00144 // divided. Visualization of such volumes is currently not implemented. 00145 00146 #include "Riostream.h" 00147 #include "TRandom3.h" 00148 00149 #include "TGeoManager.h" 00150 #include "TGeoMatrix.h" 00151 #include "TGeoBoolNode.h" 00152 #include "TVirtualGeoPainter.h" 00153 00154 #include "TVirtualPad.h" 00155 #include "TVirtualViewer3D.h" 00156 #include "TBuffer3D.h" 00157 #include "TBuffer3DTypes.h" 00158 00159 #include "TGeoCompositeShape.h" 00160 ClassImp(TGeoCompositeShape) 00161 00162 //_____________________________________________________________________________ 00163 TGeoCompositeShape::TGeoCompositeShape() 00164 :TGeoBBox(0, 0, 0) 00165 { 00166 // Default constructor 00167 SetShapeBit(TGeoShape::kGeoComb); 00168 fNode = 0; 00169 } 00170 00171 //_____________________________________________________________________________ 00172 TGeoCompositeShape::TGeoCompositeShape(const char *name, const char *expression) 00173 :TGeoBBox(0, 0, 0) 00174 { 00175 // Default constructor 00176 SetShapeBit(TGeoShape::kGeoComb); 00177 SetName(name); 00178 fNode = 0; 00179 MakeNode(expression); 00180 if (!fNode) { 00181 Error("ctor", "Composite %s: cannot parse expression: %s", name, expression); 00182 return; 00183 } 00184 ComputeBBox(); 00185 } 00186 00187 //_____________________________________________________________________________ 00188 TGeoCompositeShape::TGeoCompositeShape(const char *expression) 00189 :TGeoBBox(0, 0, 0) 00190 { 00191 // Default constructor 00192 SetShapeBit(TGeoShape::kGeoComb); 00193 fNode = 0; 00194 MakeNode(expression); 00195 if (!fNode) { 00196 TString message = TString::Format("Composite (no name) could not parse expression %s", expression); 00197 Error("ctor", "%s", message.Data()); 00198 return; 00199 } 00200 ComputeBBox(); 00201 } 00202 00203 //_____________________________________________________________________________ 00204 TGeoCompositeShape::TGeoCompositeShape(const char *name, TGeoBoolNode *node) 00205 :TGeoBBox(0,0,0) 00206 { 00207 // Constructor with a Boolean node 00208 SetName(name); 00209 fNode = node; 00210 if (!fNode) { 00211 Error("ctor", "Composite shape %s has null node", name); 00212 return; 00213 } 00214 ComputeBBox(); 00215 } 00216 00217 //_____________________________________________________________________________ 00218 TGeoCompositeShape::~TGeoCompositeShape() 00219 { 00220 // destructor 00221 if (fNode) delete fNode; 00222 } 00223 00224 //_____________________________________________________________________________ 00225 Double_t TGeoCompositeShape::Capacity() const 00226 { 00227 // Computes capacity of this shape [length^3] by sampling with 1% error. 00228 Double_t pt[3]; 00229 if (!gRandom) gRandom = new TRandom3(); 00230 Double_t vbox = 8*fDX*fDY*fDZ; // cm3 00231 Int_t igen=0; 00232 Int_t iin = 0; 00233 while (iin<10000) { 00234 pt[0] = fOrigin[0]-fDX+2*fDX*gRandom->Rndm(); 00235 pt[1] = fOrigin[1]-fDY+2*fDY*gRandom->Rndm(); 00236 pt[2] = fOrigin[2]-fDZ+2*fDZ*gRandom->Rndm(); 00237 igen++; 00238 if (Contains(pt)) iin++; 00239 } 00240 Double_t capacity = iin*vbox/igen; 00241 return capacity; 00242 } 00243 00244 //_____________________________________________________________________________ 00245 void TGeoCompositeShape::ComputeBBox() 00246 { 00247 // compute bounding box of the sphere 00248 if(fNode) fNode->ComputeBBox(fDX, fDY, fDZ, fOrigin); 00249 } 00250 00251 //_____________________________________________________________________________ 00252 void TGeoCompositeShape::ComputeNormal(Double_t *point, Double_t *dir, Double_t *norm) 00253 { 00254 // Computes normal vector in POINT to the composite shape. 00255 if (fNode) fNode->ComputeNormal(point,dir,norm); 00256 } 00257 00258 //_____________________________________________________________________________ 00259 Bool_t TGeoCompositeShape::Contains(Double_t *point) const 00260 { 00261 // Tests if point is inside the shape. 00262 if (fNode) return fNode->Contains(point); 00263 return kFALSE; 00264 } 00265 00266 //_____________________________________________________________________________ 00267 Int_t TGeoCompositeShape::DistancetoPrimitive(Int_t px, Int_t py) 00268 { 00269 // Compute closest distance from point px,py to each corner. 00270 const Int_t numPoints = GetNmeshVertices(); 00271 return ShapeDistancetoPrimitive(numPoints, px, py); 00272 } 00273 00274 //_____________________________________________________________________________ 00275 Double_t TGeoCompositeShape::DistFromOutside(Double_t *point, Double_t *dir, Int_t iact, 00276 Double_t step, Double_t *safe) const 00277 { 00278 // Compute distance from outside point to this composite shape. 00279 // Check if the bounding box is crossed within the requested distance 00280 Double_t sdist = TGeoBBox::DistFromOutside(point,dir, fDX, fDY, fDZ, fOrigin, step); 00281 if (sdist>=step) return TGeoShape::Big(); 00282 if (fNode) return fNode->DistFromOutside(point, dir, iact, step, safe); 00283 return TGeoShape::Big(); 00284 } 00285 00286 //_____________________________________________________________________________ 00287 Double_t TGeoCompositeShape::DistFromInside(Double_t *point, Double_t *dir, Int_t iact, 00288 Double_t step, Double_t *safe) const 00289 { 00290 // Compute distance from inside point to outside of this composite shape. 00291 if (fNode) return fNode->DistFromInside(point, dir, iact, step, safe); 00292 return TGeoShape::Big(); 00293 } 00294 00295 //_____________________________________________________________________________ 00296 TGeoVolume *TGeoCompositeShape::Divide(TGeoVolume * /*voldiv*/, const char * /*divname*/, Int_t /*iaxis*/, 00297 Int_t /*ndiv*/, Double_t /*start*/, Double_t /*step*/) 00298 { 00299 // Divide all range of iaxis in range/step cells 00300 Error("Divide", "Composite shapes cannot be divided"); 00301 return 0; 00302 } 00303 00304 //_____________________________________________________________________________ 00305 void TGeoCompositeShape::GetMeshNumbers(Int_t &nvert, Int_t &nsegs, Int_t &npols) const 00306 { 00307 // Returns numbers of vertices, segments and polygons composing the shape mesh. 00308 nvert = GetNmeshVertices(); 00309 nsegs = 0; 00310 npols = 0; 00311 } 00312 00313 //_____________________________________________________________________________ 00314 void TGeoCompositeShape::InspectShape() const 00315 { 00316 // print shape parameters 00317 printf("*** TGeoCompositeShape : %s = %s\n", GetName(), GetTitle()); 00318 printf(" Bounding box:\n"); 00319 TGeoBBox::InspectShape(); 00320 } 00321 00322 //_____________________________________________________________________________ 00323 void TGeoCompositeShape::MakeNode(const char *expression) 00324 { 00325 // Make a booleann node according to the top level boolean operation of expression. 00326 // Propagates signal to branches until expression is fully decomposed. 00327 // printf("Making node for : %s\n", expression); 00328 if (fNode) delete fNode; 00329 fNode = 0; 00330 SetTitle(expression); 00331 TString sleft, sright, smat; 00332 Int_t boolop; 00333 boolop = TGeoManager::Parse(expression, sleft, sright, smat); 00334 if (boolop<0) { 00335 // fail 00336 Error("MakeNode", "parser error"); 00337 return; 00338 } 00339 if (smat.Length()) 00340 Warning("MakeNode", "no geometrical transformation allowed at this level"); 00341 switch (boolop) { 00342 case 0: 00343 Error("MakeNode", "Expression has no boolean operation"); 00344 return; 00345 case 1: 00346 fNode = new TGeoUnion(sleft.Data(), sright.Data()); 00347 return; 00348 case 2: 00349 fNode = new TGeoSubtraction(sleft.Data(), sright.Data()); 00350 return; 00351 case 3: 00352 fNode = new TGeoIntersection(sleft.Data(), sright.Data()); 00353 } 00354 } 00355 00356 //_____________________________________________________________________________ 00357 Bool_t TGeoCompositeShape::PaintComposite(Option_t *option) const 00358 { 00359 // Paint this composite shape into the current 3D viewer 00360 // Returns bool flag indicating if the caller should continue to 00361 // paint child objects 00362 00363 Bool_t addChildren = kTRUE; 00364 00365 TVirtualGeoPainter *painter = gGeoManager->GetGeomPainter(); 00366 TVirtualViewer3D * viewer = gPad->GetViewer3D(); 00367 if (!painter || !viewer) return kFALSE; 00368 00369 if (fNode) { 00370 // Fill out the buffer for the composite shape - nothing extra 00371 // over TGeoBBox 00372 Bool_t preferLocal = viewer->PreferLocalFrame(); 00373 if (TBuffer3D::GetCSLevel()) preferLocal = kFALSE; 00374 static TBuffer3D buffer(TBuffer3DTypes::kComposite); 00375 FillBuffer3D(buffer, TBuffer3D::kCore|TBuffer3D::kBoundingBox, 00376 preferLocal); 00377 00378 Bool_t paintComponents = kTRUE; 00379 00380 // Start a composite shape, identified by this buffer 00381 if (!TBuffer3D::GetCSLevel()) 00382 paintComponents = viewer->OpenComposite(buffer, &addChildren); 00383 00384 TBuffer3D::IncCSLevel(); 00385 00386 // Paint the boolean node - will add more buffers to viewer 00387 TGeoHMatrix *matrix = (TGeoHMatrix*)TGeoShape::GetTransform(); 00388 TGeoHMatrix backup(*matrix); 00389 if (preferLocal) matrix->Clear(); 00390 if (paintComponents) fNode->Paint(option); 00391 if (preferLocal) *matrix = backup; 00392 // Close the composite shape 00393 if (!TBuffer3D::DecCSLevel()) 00394 viewer->CloseComposite(); 00395 } 00396 00397 return addChildren; 00398 } 00399 00400 //_____________________________________________________________________________ 00401 void TGeoCompositeShape::RegisterYourself() 00402 { 00403 // Register the shape and all components to TGeoManager class. 00404 if (gGeoManager->GetListOfShapes()->FindObject(this)) return; 00405 gGeoManager->AddShape(this); 00406 TGeoMatrix *matrix; 00407 TGeoShape *shape; 00408 TGeoCompositeShape *comp; 00409 if (fNode) { 00410 matrix = fNode->GetLeftMatrix(); 00411 if (!matrix->IsRegistered()) matrix->RegisterYourself(); 00412 else if (!gGeoManager->GetListOfMatrices()->FindObject(matrix)) { 00413 gGeoManager->GetListOfMatrices()->Add(matrix); 00414 } 00415 matrix = fNode->GetRightMatrix(); 00416 if (!matrix->IsRegistered()) matrix->RegisterYourself(); 00417 else if (!gGeoManager->GetListOfMatrices()->FindObject(matrix)) { 00418 gGeoManager->GetListOfMatrices()->Add(matrix); 00419 } 00420 shape = fNode->GetLeftShape(); 00421 if (!gGeoManager->GetListOfShapes()->FindObject(shape)) { 00422 if (shape->IsComposite()) { 00423 comp = (TGeoCompositeShape*)shape; 00424 comp->RegisterYourself(); 00425 } else { 00426 gGeoManager->AddShape(shape); 00427 } 00428 } 00429 shape = fNode->GetRightShape(); 00430 if (!gGeoManager->GetListOfShapes()->FindObject(shape)) { 00431 if (shape->IsComposite()) { 00432 comp = (TGeoCompositeShape*)shape; 00433 comp->RegisterYourself(); 00434 } else { 00435 gGeoManager->AddShape(shape); 00436 } 00437 } 00438 } 00439 } 00440 00441 //_____________________________________________________________________________ 00442 Double_t TGeoCompositeShape::Safety(Double_t *point, Bool_t in) const 00443 { 00444 // computes the closest distance from given point to this shape, according 00445 // to option. The matching point on the shape is stored in spoint. 00446 if (fNode) return fNode->Safety(point,in); 00447 return 0.; 00448 } 00449 00450 //_____________________________________________________________________________ 00451 void TGeoCompositeShape::SavePrimitive(ostream &out, Option_t *option /*= ""*/) 00452 { 00453 // Save a primitive as a C++ statement(s) on output stream "out". 00454 if (TObject::TestBit(kGeoSavePrimitive)) return; 00455 if (fNode) fNode->SavePrimitive(out,option); 00456 out << " // Shape: " << GetName() << " type: " << ClassName() << endl; 00457 out << " TGeoShape *" << GetPointerName() << " = new TGeoCompositeShape(\"" << GetName() << "\", pBoolNode);" << endl; 00458 if (strlen(GetTitle())) out << " " << GetPointerName() << "->SetTitle(\"" << GetTitle() << "\");" << endl; 00459 TObject::SetBit(TGeoShape::kGeoSavePrimitive); 00460 } 00461 00462 //_____________________________________________________________________________ 00463 void TGeoCompositeShape::SetPoints(Double_t *points) const 00464 { 00465 // create points for a composite shape 00466 if (fNode) fNode->SetPoints(points); 00467 } 00468 00469 //_____________________________________________________________________________ 00470 void TGeoCompositeShape::SetPoints(Float_t *points) const 00471 { 00472 // create points for a composite shape 00473 if (fNode) fNode->SetPoints(points); 00474 } 00475 00476 //_____________________________________________________________________________ 00477 void TGeoCompositeShape::Sizeof3D() const 00478 { 00479 // compute size of this 3D object 00480 if (fNode) fNode->Sizeof3D(); 00481 } 00482 00483 //_____________________________________________________________________________ 00484 Int_t TGeoCompositeShape::GetNmeshVertices() const 00485 { 00486 // Return number of vertices of the mesh representation 00487 if (!fNode) return 0; 00488 return fNode->GetNpoints(); 00489 }