source: trunk/source/geometry/solids/CSG/src/G4Trd.cc @ 1058

Last change on this file since 1058 was 1058, checked in by garnier, 15 years ago

file release beta

File size: 37.5 KB
Line 
1//
2// ********************************************************************
3// * License and Disclaimer                                           *
4// *                                                                  *
5// * The  Geant4 software  is  copyright of the Copyright Holders  of *
6// * the Geant4 Collaboration.  It is provided  under  the terms  and *
7// * conditions of the Geant4 Software License,  included in the file *
8// * LICENSE and available at  http://cern.ch/geant4/license .  These *
9// * include a list of copyright holders.                             *
10// *                                                                  *
11// * Neither the authors of this software system, nor their employing *
12// * institutes,nor the agencies providing financial support for this *
13// * work  make  any representation or  warranty, express or implied, *
14// * regarding  this  software system or assume any liability for its *
15// * use.  Please see the license in the file  LICENSE  and URL above *
16// * for the full disclaimer and the limitation of liability.         *
17// *                                                                  *
18// * This  code  implementation is the result of  the  scientific and *
19// * technical work of the GEANT4 collaboration.                      *
20// * By using,  copying,  modifying or  distributing the software (or *
21// * any work based  on the software)  you  agree  to acknowledge its *
22// * use  in  resulting  scientific  publications,  and indicate your *
23// * acceptance of all terms of the Geant4 Software license.          *
24// ********************************************************************
25//
26//
27// $Id: G4Trd.cc,v 1.34 2006/10/19 15:33:38 gcosmo Exp $
28// GEANT4 tag $Name: geant4-09-02-ref-02 $
29//
30//
31// Implementation for G4Trd class
32//
33// History:
34//
35// 28.04.05 V.Grichine: new SurfaceNormal according to J. Apostolakis proposal
36// 26.04.05, V.Grichine, new SurfaceNoramal is default
37// 07.12.04, V.Grichine, SurfaceNoramal with edges/vertices.
38// 07.05.00, V.Grichine, in d = DistanceToIn(p,v), if d<0.5*kCarTolerance, d=0
39//    ~1996, V.Grichine, 1st implementation based on old code of P.Kent
40//
41//////////////////////////////////////////////////////////////////////////////
42
43#include "G4Trd.hh"
44
45#include "G4VPVParameterisation.hh"
46#include "G4VoxelLimits.hh"
47#include "G4AffineTransform.hh"
48#include "Randomize.hh"
49
50#include "G4VGraphicsScene.hh"
51#include "G4Polyhedron.hh"
52#include "G4NURBS.hh"
53#include "G4NURBSbox.hh"
54
55using namespace CLHEP;
56
57/////////////////////////////////////////////////////////////////////////
58//
59// Constructor - check & set half widths
60
61G4Trd::G4Trd( const G4String& pName,
62                    G4double pdx1,  G4double pdx2,
63                    G4double pdy1,  G4double pdy2,
64                    G4double pdz )
65  : G4CSGSolid(pName)
66{
67  CheckAndSetAllParameters (pdx1, pdx2, pdy1, pdy2, pdz);
68}
69
70/////////////////////////////////////////////////////////////////////////
71//
72// Set and check (coplanarity) of trd parameters
73
74void G4Trd::CheckAndSetAllParameters ( G4double pdx1,  G4double pdx2,
75                                       G4double pdy1,  G4double pdy2,
76                                       G4double pdz ) 
77{
78  if ( pdx1>0&&pdx2>0&&pdy1>0&&pdy2>0&&pdz>0 )
79  {
80    fDx1=pdx1; fDx2=pdx2;
81    fDy1=pdy1; fDy2=pdy2;
82    fDz=pdz;
83  }
84  else
85  {
86    if ( pdx1>=0 && pdx2>=0 && pdy1>=0 && pdy2>=0 && pdz>=0 )
87    {
88      // G4double  Minimum_length= (1+per_thousand) * kCarTolerance/2.;
89      // FIX-ME : temporary solution for ZERO or very-small parameters
90      //
91      G4double  Minimum_length= kCarTolerance/2.;
92      fDx1=std::max(pdx1,Minimum_length); 
93      fDx2=std::max(pdx2,Minimum_length); 
94      fDy1=std::max(pdy1,Minimum_length); 
95      fDy2=std::max(pdy2,Minimum_length); 
96      fDz=std::max(pdz,Minimum_length);
97    }
98    else
99    {
100      G4cerr << "ERROR - G4Trd()::CheckAndSetAllParameters(): " << GetName()
101             << G4endl
102             << "        Invalid dimensions, some are < 0 !" << G4endl
103             << "          X - " << pdx1 << ", " << pdx2 << G4endl
104             << "          Y - " << pdy1 << ", " << pdy2 << G4endl
105             << "          Z - " << pdz << G4endl;
106      G4Exception("G4Trd::CheckAndSetAllParameters()",
107                  "InvalidSetup", FatalException,
108                  "Invalid parameters.");
109    }
110  }
111  fCubicVolume= 0.;
112  fSurfaceArea= 0.;
113  fpPolyhedron = 0;
114}
115
116///////////////////////////////////////////////////////////////////////
117//
118// Fake default constructor - sets only member data and allocates memory
119//                            for usage restricted to object persistency.
120//
121G4Trd::G4Trd( __void__& a )
122  : G4CSGSolid(a)
123{
124}
125
126//////////////////////////////////////////////////////////////////////////
127//
128// Destructor
129
130G4Trd::~G4Trd()
131{
132}
133
134////////////////////////////////////////////////////////////////////////////
135//
136//
137
138void G4Trd::SetAllParameters ( G4double pdx1, G4double pdx2, G4double pdy1, 
139                               G4double pdy2, G4double pdz ) 
140{
141  CheckAndSetAllParameters (pdx1, pdx2, pdy1, pdy2, pdz);
142}
143
144
145/////////////////////////////////////////////////////////////////////////
146//
147// Dispatch to parameterisation for replication mechanism dimension
148// computation & modification.
149
150void G4Trd::ComputeDimensions(       G4VPVParameterisation* p,
151                               const G4int n,
152                               const G4VPhysicalVolume* pRep )
153{
154  p->ComputeDimensions(*this,n,pRep);
155}
156
157
158///////////////////////////////////////////////////////////////////////////
159//
160// Calculate extent under transform and specified limit
161
162G4bool G4Trd::CalculateExtent( const EAxis pAxis,
163                               const G4VoxelLimits& pVoxelLimit,
164                               const G4AffineTransform& pTransform,
165                                     G4double& pMin, G4double& pMax ) const
166{
167  if (!pTransform.IsRotated())
168  {
169    // Special case handling for unrotated solids
170    // Compute x/y/z mins and maxs respecting limits, with early returns
171    // if outside limits. Then switch() on pAxis
172
173    G4double xoffset,xMin,xMax;
174    G4double yoffset,yMin,yMax;
175    G4double zoffset,zMin,zMax;
176
177    zoffset=pTransform.NetTranslation().z();
178    zMin=zoffset-fDz;
179    zMax=zoffset+fDz;
180    if (pVoxelLimit.IsZLimited())
181    {
182      if ( (zMin>pVoxelLimit.GetMaxZExtent()+kCarTolerance)
183        || (zMax<pVoxelLimit.GetMinZExtent()-kCarTolerance) )
184      {
185        return false;
186      }
187        else
188      {
189        if (zMin<pVoxelLimit.GetMinZExtent())
190        {
191          zMin=pVoxelLimit.GetMinZExtent();
192        }
193        if (zMax>pVoxelLimit.GetMaxZExtent())
194        {
195          zMax=pVoxelLimit.GetMaxZExtent();
196        }
197      }
198    }
199    xoffset=pTransform.NetTranslation().x();
200    if (fDx2 >= fDx1)
201    { 
202      xMax =  xoffset+(fDx1+fDx2)/2+(zMax-zoffset)*(fDx2-fDx1)/(2*fDz) ;
203      xMin = 2*xoffset - xMax ;
204    }
205    else
206    {
207      xMax =  xoffset+(fDx1+fDx2)/2+(zMin-zoffset)*(fDx2-fDx1)/(2*fDz) ;
208      xMin =  2*xoffset - xMax ;
209    }   
210    if (pVoxelLimit.IsXLimited())
211    {
212      if ( (xMin>pVoxelLimit.GetMaxXExtent()+kCarTolerance)
213        || (xMax<pVoxelLimit.GetMinXExtent()-kCarTolerance) )
214      {
215        return false;
216      }
217      else
218      {
219        if (xMin<pVoxelLimit.GetMinXExtent())
220        {
221          xMin=pVoxelLimit.GetMinXExtent();
222        }
223        if (xMax>pVoxelLimit.GetMaxXExtent())
224        {
225          xMax=pVoxelLimit.GetMaxXExtent();
226        }
227      }
228    }
229    yoffset= pTransform.NetTranslation().y() ;
230    if(fDy2 >= fDy1)
231    {
232      yMax = yoffset+(fDy2+fDy1)/2+(zMax-zoffset)*(fDy2-fDy1)/(2*fDz) ;
233      yMin = 2*yoffset - yMax ;
234    }
235    else
236    {
237      yMax = yoffset+(fDy2+fDy1)/2+(zMin-zoffset)*(fDy2-fDy1)/(2*fDz) ;
238      yMin = 2*yoffset - yMax ; 
239    } 
240    if (pVoxelLimit.IsYLimited())
241    {
242      if ( (yMin>pVoxelLimit.GetMaxYExtent()+kCarTolerance)
243        || (yMax<pVoxelLimit.GetMinYExtent()-kCarTolerance) )
244      {
245        return false;
246      }
247      else
248      {
249        if (yMin<pVoxelLimit.GetMinYExtent())
250        {
251          yMin=pVoxelLimit.GetMinYExtent();
252        }
253        if (yMax>pVoxelLimit.GetMaxYExtent())
254        {
255          yMax=pVoxelLimit.GetMaxYExtent();
256        }
257      }
258    }
259
260    switch (pAxis)
261    {
262      case kXAxis:
263        pMin=xMin;
264        pMax=xMax;
265        break;
266      case kYAxis:
267        pMin=yMin;
268        pMax=yMax;
269        break;
270      case kZAxis:
271        pMin=zMin;
272        pMax=zMax;
273        break;
274      default:
275        break;
276    }
277
278    // Add 2*Tolerance to avoid precision troubles ?
279    //
280    pMin-=kCarTolerance;
281    pMax+=kCarTolerance;
282
283    return true;
284  }
285  else
286  {
287    // General rotated case - create and clip mesh to boundaries
288
289    G4bool existsAfterClip=false;
290    G4ThreeVectorList *vertices;
291
292    pMin=+kInfinity;
293    pMax=-kInfinity;
294
295    // Calculate rotated vertex coordinates
296    //
297    vertices=CreateRotatedVertices(pTransform);
298    ClipCrossSection(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
299    ClipCrossSection(vertices,4,pVoxelLimit,pAxis,pMin,pMax);
300    ClipBetweenSections(vertices,0,pVoxelLimit,pAxis,pMin,pMax);
301     
302    if (pMin!=kInfinity||pMax!=-kInfinity)
303    {
304      existsAfterClip=true;
305
306      // Add 2*tolerance to avoid precision troubles
307      //
308      pMin-=kCarTolerance;
309      pMax+=kCarTolerance;
310       
311    }
312    else
313    {
314      // Check for case where completely enveloping clipping volume
315      // If point inside then we are confident that the solid completely
316      // envelopes the clipping volume. Hence set min/max extents according
317      // to clipping volume extents along the specified axis.
318
319      G4ThreeVector clipCentre(
320         (pVoxelLimit.GetMinXExtent()+pVoxelLimit.GetMaxXExtent())*0.5,
321         (pVoxelLimit.GetMinYExtent()+pVoxelLimit.GetMaxYExtent())*0.5,
322         (pVoxelLimit.GetMinZExtent()+pVoxelLimit.GetMaxZExtent())*0.5);
323       
324      if (Inside(pTransform.Inverse().TransformPoint(clipCentre))!=kOutside)
325      {
326        existsAfterClip=true;
327        pMin=pVoxelLimit.GetMinExtent(pAxis);
328        pMax=pVoxelLimit.GetMaxExtent(pAxis);
329      }
330    }
331    delete vertices;
332    return existsAfterClip;
333  }
334}
335
336///////////////////////////////////////////////////////////////////
337//
338// Return whether point inside/outside/on surface, using tolerance
339
340EInside G4Trd::Inside( const G4ThreeVector& p ) const
341{ 
342  EInside in=kOutside;
343  G4double x,y,zbase1,zbase2;
344   
345  if (std::fabs(p.z())<=fDz-kCarTolerance/2)
346  {
347    zbase1=p.z()+fDz;  // Dist from -ve z plane
348    zbase2=fDz-p.z();  // Dist from +ve z plane
349
350    // Check whether inside x tolerance
351    //
352    x=0.5*(fDx2*zbase1+fDx1*zbase2)/fDz - kCarTolerance/2;
353    if (std::fabs(p.x())<=x)
354    {
355      y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz - kCarTolerance/2;
356      if (std::fabs(p.y())<=y)
357      {
358        in=kInside;
359      }
360      else if (std::fabs(p.y())<=y+kCarTolerance)
361      {
362        in=kSurface;
363      }
364    }
365    else if (std::fabs(p.x())<=x+kCarTolerance)
366    {
367      // y = y half width of shape at z of point + tolerant boundary
368      //
369      y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz + kCarTolerance/2;
370      if (std::fabs(p.y())<=y)
371      {
372        in=kSurface;
373      }
374    }
375  }
376  else if (std::fabs(p.z())<=fDz+kCarTolerance/2)
377  {
378    // Only need to check outer tolerant boundaries
379    //
380    zbase1=p.z()+fDz;  // Dist from -ve z plane
381    zbase2=fDz-p.z();   // Dist from +ve z plane
382
383    // x = x half width of shape at z of point plus tolerance
384    //
385    x=0.5*(fDx2*zbase1+fDx1*zbase2)/fDz + kCarTolerance/2;
386    if (std::fabs(p.x())<=x)
387    {
388      // y = y half width of shape at z of point
389      //
390      y=0.5*((fDy2*zbase1+fDy1*zbase2))/fDz + kCarTolerance/2;
391      if (std::fabs(p.y())<=y) in=kSurface;
392    }
393  } 
394  return in;
395}
396
397//////////////////////////////////////////////////////////////////////////
398//
399// Calculate side nearest to p, and return normal
400// If two sides are equidistant, normal of first side (x/y/z)
401// encountered returned
402
403G4ThreeVector G4Trd::SurfaceNormal( const G4ThreeVector& p ) const
404{
405  G4ThreeVector norm, sumnorm(0.,0.,0.);
406  G4int noSurfaces = 0; 
407  G4double z = 2.0*fDz, tanx, secx, newpx, widx;
408  G4double tany, secy, newpy, widy;
409  G4double distx, disty, distz, fcos;
410  G4double delta = 0.5*kCarTolerance;
411
412  tanx  = (fDx2 - fDx1)/z;
413  secx  = std::sqrt(1.0+tanx*tanx);
414  newpx = std::fabs(p.x())-p.z()*tanx;
415  widx  = fDx2 - fDz*tanx;
416
417  tany  = (fDy2 - fDy1)/z;
418  secy  = std::sqrt(1.0+tany*tany);
419  newpy = std::fabs(p.y())-p.z()*tany;
420  widy  = fDy2 - fDz*tany;
421
422  distx = std::fabs(newpx-widx)/secx;       // perp. distance to x side
423  disty = std::fabs(newpy-widy)/secy;       //                to y side
424  distz = std::fabs(std::fabs(p.z())-fDz);  //                to z side
425
426  fcos              = 1.0/secx;
427  G4ThreeVector nX  = G4ThreeVector( fcos,0,-tanx*fcos);
428  G4ThreeVector nmX = G4ThreeVector(-fcos,0,-tanx*fcos);
429
430  fcos              = 1.0/secy;
431  G4ThreeVector nY  = G4ThreeVector(0, fcos,-tany*fcos);
432  G4ThreeVector nmY = G4ThreeVector(0,-fcos,-tany*fcos);
433  G4ThreeVector nZ  = G4ThreeVector( 0, 0,  1.0);
434 
435  if (distx <= delta)     
436  {
437    noSurfaces ++;
438    if ( p.x() >= 0.) sumnorm += nX;
439    else              sumnorm += nmX;   
440  }
441  if (disty <= delta)
442  {
443    noSurfaces ++;
444    if ( p.y() >= 0.) sumnorm += nY;
445    else              sumnorm += nmY;   
446  }
447  if (distz <= delta) 
448  {
449    noSurfaces ++;
450    if ( p.z() >= 0.) sumnorm += nZ;
451    else              sumnorm -= nZ; 
452  }
453  if ( noSurfaces == 0 )
454  {
455#ifdef G4CSGDEBUG
456    G4Exception("G4Trd::SurfaceNormal(p)", "Notification", JustWarning, 
457                "Point p is not on surface !?" );
458#endif
459     norm = ApproxSurfaceNormal(p);
460  }
461  else if ( noSurfaces == 1 ) norm = sumnorm;
462  else                        norm = sumnorm.unit();
463  return norm;   
464}
465
466
467/////////////////////////////////////////////////////////////////////////////
468//
469// Algorithm for SurfaceNormal() following the original specification
470// for points not on the surface
471
472G4ThreeVector G4Trd::ApproxSurfaceNormal( const G4ThreeVector& p ) const
473{
474  G4ThreeVector norm;
475  G4double z,tanx,secx,newpx,widx;
476  G4double tany,secy,newpy,widy;
477  G4double distx,disty,distz,fcos;
478
479  z=2.0*fDz;
480
481  tanx=(fDx2-fDx1)/z;
482  secx=std::sqrt(1.0+tanx*tanx);
483  newpx=std::fabs(p.x())-p.z()*tanx;
484  widx=fDx2-fDz*tanx;
485
486  tany=(fDy2-fDy1)/z;
487  secy=std::sqrt(1.0+tany*tany);
488  newpy=std::fabs(p.y())-p.z()*tany;
489  widy=fDy2-fDz*tany;
490
491  distx=std::fabs(newpx-widx)/secx;  // perpendicular distance to x side
492  disty=std::fabs(newpy-widy)/secy;  //                        to y side
493  distz=std::fabs(std::fabs(p.z())-fDz);  //                        to z side
494
495  // find closest side
496  //
497  if (distx<=disty)
498  { 
499    if (distx<=distz) 
500    {
501      // Closest to X
502      //
503      fcos=1.0/secx;
504      // normal=(+/-std::cos(ang),0,-std::sin(ang))
505      if (p.x()>=0)
506        norm=G4ThreeVector(fcos,0,-tanx*fcos);
507      else
508        norm=G4ThreeVector(-fcos,0,-tanx*fcos);
509    }
510    else
511    {
512      // Closest to Z
513      //
514      if (p.z()>=0)
515        norm=G4ThreeVector(0,0,1);
516      else
517        norm=G4ThreeVector(0,0,-1);
518    }
519  }
520  else
521  { 
522    if (disty<=distz)
523    {
524      // Closest to Y
525      //
526      fcos=1.0/secy;
527      if (p.y()>=0)
528        norm=G4ThreeVector(0,fcos,-tany*fcos);
529      else
530        norm=G4ThreeVector(0,-fcos,-tany*fcos);
531    }
532    else 
533    {
534      // Closest to Z
535      //
536      if (p.z()>=0)
537        norm=G4ThreeVector(0,0,1);
538      else
539        norm=G4ThreeVector(0,0,-1);
540    }
541  }
542  return norm;   
543}
544
545////////////////////////////////////////////////////////////////////////////
546//
547// Calculate distance to shape from outside
548// - return kInfinity if no intersection
549//
550// ALGORITHM:
551// For each component, calculate pair of minimum and maximum intersection
552// values for which the particle is in the extent of the shape
553// - The smallest (MAX minimum) allowed distance of the pairs is intersect
554// - Z plane intersectin uses tolerance
555// - XZ YZ planes use logic & *SLIGHTLY INCORRECT* tolerance
556//   (this saves at least 1 sqrt, 1 multiply and 1 divide... in applicable
557//    cases)
558// - Note: XZ and YZ planes each divide space into four regions,
559//   characterised by ss1 ss2
560// NOTE:
561//
562// `Inside' safe - meaningful answers given if point is inside the exact
563// shape.
564
565G4double G4Trd::DistanceToIn( const G4ThreeVector& p,
566                              const G4ThreeVector& v ) const
567{ 
568  G4double snxt = kInfinity ;    // snxt = default return value
569  G4double smin,smax;
570  G4double s1,s2,tanxz,tanyz,ds1,ds2;
571  G4double ss1,ss2,sn1=0.,sn2=0.,Dist;
572
573  if ( v.z() )  // Calculate valid z intersect range
574  {
575    if ( v.z() > 0 )   // Calculate smax: must be +ve or no intersection.
576    {
577      Dist = fDz - p.z() ;  // to plane at +dz
578
579      if (Dist >= 0.5*kCarTolerance)
580      {
581        smax = Dist/v.z() ;
582        smin = -(fDz + p.z())/v.z() ;
583      }
584      else  return snxt ;
585    }
586    else // v.z <0
587    {
588      Dist=fDz+p.z();  // plane at -dz
589
590      if ( Dist >= 0.5*kCarTolerance )
591      {
592        smax = -Dist/v.z() ;
593        smin = (fDz - p.z())/v.z() ;
594      }
595      else return snxt ; 
596    }
597    if (smin < 0 ) smin = 0 ;
598  }
599  else // v.z=0
600  {
601    if (std::fabs(p.z()) >= fDz ) return snxt ;     // Outside & no intersect
602    else
603    {
604      smin = 0 ;    // Always inside z range
605      smax = kInfinity;
606    }
607  }
608
609  // Calculate x intersection range
610  //
611  // Calc half width at p.z, and components towards planes
612
613  tanxz = (fDx2 - fDx1)*0.5/fDz ;
614  s1    = 0.5*(fDx1+fDx2) + tanxz*p.z() ;  // x half width at p.z
615  ds1   = v.x() - tanxz*v.z() ;       // Components of v towards faces at +-x
616  ds2   = v.x() + tanxz*v.z() ;
617  ss1   = s1 - p.x() ;         // -delta x to +ve plane
618                               // -ve when outside
619  ss2   = -s1 - p.x() ;        // -delta x to -ve plane
620                               // +ve when outside
621   
622  if (ss1 < 0 && ss2 <= 0 )
623  {
624    if (ds1 < 0)   // In +ve coord Area
625    {
626      sn1 = ss1/ds1 ;
627
628      if ( ds2 < 0 ) sn2 = ss2/ds2 ;           
629      else           sn2 = kInfinity ;
630    }
631    else return snxt ;
632  }
633  else if ( ss1 >= 0 && ss2 > 0 )
634  {
635    if ( ds2 > 0 )  // In -ve coord Area
636    {
637      sn1 = ss2/ds2 ;
638
639      if (ds1 > 0)  sn2 = ss1/ds1 ;     
640      else          sn2 = kInfinity;     
641       
642    }
643    else   return snxt ;
644  }
645  else if (ss1 >= 0 && ss2 <= 0 )
646  {
647    // Inside Area - calculate leaving distance
648    // *Don't* use exact distance to side for tolerance
649    //                                             = ss1*std::cos(ang xz)
650    //                                             = ss1/std::sqrt(1.0+tanxz*tanxz)
651    sn1 = 0 ;
652
653    if ( ds1 > 0 )
654    {
655      if (ss1 > 0.5*kCarTolerance) sn2 = ss1/ds1 ; // Leave +ve side extent
656      else                         return snxt ;   // Leave immediately by +ve
657    }
658    else  sn2 = kInfinity ;
659     
660    if ( ds2 < 0 )
661    {
662      if ( ss2 < -0.5*kCarTolerance )
663      {
664        Dist = ss2/ds2 ;            // Leave -ve side extent
665        if ( Dist < sn2 ) sn2 = Dist ;
666      }
667      else  return snxt ;
668    }   
669  }
670  else if (ss1 < 0 && ss2 > 0 )
671  {
672    // Within +/- plane cross-over areas (not on boundaries ss1||ss2==0)
673
674    if ( ds1 >= 0 || ds2 <= 0 )
675    {   
676      return snxt ;
677    }
678    else  // Will intersect & stay inside
679    {
680      sn1  = ss1/ds1 ;
681      Dist = ss2/ds2 ;
682      if (Dist > sn1 ) sn1 = Dist ;
683      sn2 = kInfinity ;
684    }
685  }
686
687  // Reduce allowed range of distances as appropriate
688
689  if ( sn1 > smin ) smin = sn1 ;
690  if ( sn2 < smax ) smax = sn2 ;
691
692  // Check for incompatible ranges (eg z intersects between 50 ->100 and x
693  // only 10-40 -> no intersection)
694
695  if ( smax < smin ) return snxt ;
696
697  // Calculate valid y intersection range
698  // (repeat of x intersection code)
699
700  tanyz = (fDy2-fDy1)*0.5/fDz ;
701  s2    = 0.5*(fDy1+fDy2) + tanyz*p.z() ;  // y half width at p.z
702  ds1   = v.y() - tanyz*v.z() ;       // Components of v towards faces at +-y
703  ds2   = v.y() + tanyz*v.z() ;
704  ss1   = s2 - p.y() ;         // -delta y to +ve plane
705  ss2   = -s2 - p.y() ;        // -delta y to -ve plane
706   
707  if ( ss1 < 0 && ss2 <= 0 )
708  {
709    if (ds1 < 0 ) // In +ve coord Area
710    {
711      sn1 = ss1/ds1 ;
712      if ( ds2 < 0 )  sn2 = ss2/ds2 ;
713      else            sn2 = kInfinity ;
714    }
715    else   return snxt ;
716  }
717  else if ( ss1 >= 0 && ss2 > 0 )
718  {
719    if ( ds2 > 0 )  // In -ve coord Area
720    {
721      sn1 = ss2/ds2 ;
722      if ( ds1 > 0 )  sn2 = ss1/ds1 ;
723      else            sn2 = kInfinity ;     
724    }
725    else   return snxt ;
726  }
727  else if (ss1 >= 0 && ss2 <= 0 )
728  {
729    // Inside Area - calculate leaving distance
730    // *Don't* use exact distance to side for tolerance
731    //                                          = ss1*std::cos(ang yz)
732    //                                          = ss1/std::sqrt(1.0+tanyz*tanyz)
733    sn1 = 0 ;
734
735    if ( ds1 > 0 )
736    {
737      if (ss1 > 0.5*kCarTolerance) sn2 = ss1/ds1 ; // Leave +ve side extent
738      else                         return snxt ;   // Leave immediately by +ve
739    }
740    else  sn2 = kInfinity ;
741     
742    if ( ds2 < 0 )
743    {
744      if ( ss2 < -0.5*kCarTolerance )
745      {
746        Dist = ss2/ds2 ; // Leave -ve side extent
747        if (Dist < sn2) sn2=Dist;
748      }
749      else  return snxt ;
750    }   
751  }
752  else if (ss1 < 0 && ss2 > 0 )
753  {
754    // Within +/- plane cross-over areas (not on boundaries ss1||ss2==0)
755
756    if (ds1 >= 0 || ds2 <= 0 ) 
757    {
758      return snxt ;
759    }
760    else  // Will intersect & stay inside
761    {
762      sn1 = ss1/ds1 ;
763      Dist = ss2/ds2 ;
764      if (Dist > sn1 ) sn1 = Dist ;
765      sn2 = kInfinity ;
766    }
767  }
768 
769  // Reduce allowed range of distances as appropriate
770
771  if ( sn1 > smin) smin = sn1 ;
772  if ( sn2 < smax) smax = sn2 ;
773
774  // Check for incompatible ranges (eg x intersects between 50 ->100 and y
775  // only 10-40 -> no intersection). Set snxt if ok
776
777  if ( smax > smin ) snxt = smin ;
778  if (snxt < 0.5*kCarTolerance ) snxt = 0.0 ;
779
780  return snxt ;
781}
782
783/////////////////////////////////////////////////////////////////////////
784//
785// Approximate distance to shape
786// Calculate perpendicular distances to z/x/y surfaces, return largest
787// which is the most fast estimation of shortest distance to Trd
788//  - Safe underestimate
789//  - If point within exact shape, return 0
790
791G4double G4Trd::DistanceToIn( const G4ThreeVector& p ) const
792{
793  G4double safe=0.0;
794  G4double tanxz,distx,safx;
795  G4double tanyz,disty,safy;
796  G4double zbase;
797
798  safe=std::fabs(p.z())-fDz;
799  if (safe<0) safe=0;      // Also used to ensure x/y distances
800                           // POSITIVE
801
802  zbase=fDz+p.z();
803
804  // Find distance along x direction to closest x plane
805  //
806  tanxz=(fDx2-fDx1)*0.5/fDz;
807  //    widx=fDx1+tanxz*(fDz+p.z()); // x width at p.z
808  //    distx=std::fabs(p.x())-widx;      // distance to plane
809  distx=std::fabs(p.x())-(fDx1+tanxz*zbase);
810  if (distx>safe)
811  {
812    safx=distx/std::sqrt(1.0+tanxz*tanxz); // vector Dist=Dist*std::cos(ang)
813    if (safx>safe) safe=safx;
814  }
815
816  // Find distance along y direction to slanted wall
817  tanyz=(fDy2-fDy1)*0.5/fDz;
818  //    widy=fDy1+tanyz*(fDz+p.z()); // y width at p.z
819  //    disty=std::fabs(p.y())-widy;      // distance to plane
820  disty=std::fabs(p.y())-(fDy1+tanyz*zbase);
821  if (disty>safe)   
822  {
823    safy=disty/std::sqrt(1.0+tanyz*tanyz); // distance along vector
824    if (safy>safe) safe=safy;
825  }
826  return safe;
827}
828
829////////////////////////////////////////////////////////////////////////
830//
831// Calcluate distance to surface of shape from inside
832// Calculate distance to x/y/z planes - smallest is exiting distance
833// - z planes have std. check for tolerance
834// - xz yz planes have check based on distance || to x or y axis
835//   (not corrected for slope of planes)
836// ?BUG? If v.z==0 are there cases when snside not set????
837
838G4double G4Trd::DistanceToOut( const G4ThreeVector& p,
839                               const G4ThreeVector& v,
840                               const G4bool calcNorm,
841                                     G4bool *validNorm,
842                                     G4ThreeVector *n ) const
843{
844  ESide side = kUndefined, snside = kUndefined;
845  G4double snxt,pdist;
846  G4double central,ss1,ss2,ds1,ds2,sn=0.,sn2=0.;
847  G4double tanxz=0.,cosxz=0.,tanyz=0.,cosyz=0.;
848
849  if (calcNorm) *validNorm=true; // All normals are valid
850
851  // Calculate z plane intersection
852  if (v.z()>0)
853  {
854    pdist=fDz-p.z();
855    if (pdist>kCarTolerance/2)
856    {
857      snxt=pdist/v.z();
858      side=kPZ;
859    }
860    else
861    {
862      if (calcNorm)
863      {
864        *n=G4ThreeVector(0,0,1);
865      }
866      return snxt=0;
867    }
868  }
869  else if (v.z()<0) 
870  {
871    pdist=fDz+p.z();
872    if (pdist>kCarTolerance/2)
873    {
874      snxt=-pdist/v.z();
875      side=kMZ;
876    }
877    else
878    {
879      if (calcNorm)
880      {
881        *n=G4ThreeVector(0,0,-1);
882      }
883      return snxt=0;
884    }
885  }
886  else
887  {
888    snxt=kInfinity;
889  }
890
891  //
892  // Calculate x intersection
893  //
894  tanxz=(fDx2-fDx1)*0.5/fDz;
895  central=0.5*(fDx1+fDx2);
896
897  // +ve plane (1)
898  //
899  ss1=central+tanxz*p.z()-p.x();  // distance || x axis to plane
900                                  // (+ve if point inside)
901  ds1=v.x()-tanxz*v.z();    // component towards plane at +x
902                            // (-ve if +ve -> -ve direction)
903  // -ve plane (2)
904  //
905  ss2=-tanxz*p.z()-p.x()-central;  //distance || x axis to plane
906                                   // (-ve if point inside)
907  ds2=tanxz*v.z()+v.x();    // component towards plane at -x
908
909  if (ss1>0&&ss2<0)
910  {
911    // Normal case - entirely inside region
912    if (ds1<=0&&ds2<0)
913    {   
914      if (ss2<-kCarTolerance/2)
915      {
916        sn=ss2/ds2;  // Leave by -ve side
917        snside=kMX;
918      }
919      else
920      {
921        sn=0; // Leave immediately by -ve side
922        snside=kMX;
923      }
924    }
925    else if (ds1>0&&ds2>=0)
926    {
927      if (ss1>kCarTolerance/2)
928      {
929        sn=ss1/ds1;  // Leave by +ve side
930        snside=kPX;
931      }
932      else
933      {
934        sn=0; // Leave immediately by +ve side
935        snside=kPX;
936      }
937    }
938    else if (ds1>0&&ds2<0)
939    {
940      if (ss1>kCarTolerance/2)
941      {
942        // sn=ss1/ds1;  // Leave by +ve side
943        if (ss2<-kCarTolerance/2)
944        {
945          sn=ss1/ds1;  // Leave by +ve side
946          sn2=ss2/ds2;
947          if (sn2<sn)
948          {
949            sn=sn2;
950            snside=kMX;
951          }
952          else
953          {
954            snside=kPX;
955          }
956        }
957        else
958        {
959          sn=0; // Leave immediately by -ve
960          snside=kMX;
961        }     
962      }
963      else
964      {
965        sn=0; // Leave immediately by +ve side
966        snside=kPX;
967      }
968    }
969    else
970    {
971      // Must be || to both
972      //
973      sn=kInfinity;    // Don't leave by either side
974    }
975  }
976  else if (ss1<=0&&ss2<0)
977  {
978    // Outside, in +ve Area
979   
980    if (ds1>0)
981    {
982      sn=0;       // Away from shape
983                  // Left by +ve side
984      snside=kPX;
985    }
986    else
987    {
988      if (ds2<0)
989      {
990        // Ignore +ve plane and use -ve plane intersect
991        //
992        sn=ss2/ds2; // Leave by -ve side
993        snside=kMX;
994      }
995      else
996      {
997        // Must be || to both -> exit determined by other axes
998        //
999        sn=kInfinity; // Don't leave by either side
1000      }
1001    }
1002  }
1003  else if (ss1>0&&ss2>=0)
1004  {
1005    // Outside, in -ve Area
1006
1007    if (ds2<0)
1008    {
1009      sn=0;       // away from shape
1010                  // Left by -ve side
1011      snside=kMX;
1012    }
1013    else
1014    {
1015      if (ds1>0)
1016      {
1017        // Ignore +ve plane and use -ve plane intersect
1018        //
1019        sn=ss1/ds1; // Leave by +ve side
1020        snside=kPX;
1021      }
1022      else
1023      {
1024        // Must be || to both -> exit determined by other axes
1025        //
1026        sn=kInfinity; // Don't leave by either side
1027      }
1028    }
1029  }
1030
1031  // Update minimum exit distance
1032
1033  if (sn<snxt)
1034  {
1035    snxt=sn;
1036    side=snside;
1037  }
1038  if (snxt>0)
1039  {
1040    // Calculate y intersection
1041
1042    tanyz=(fDy2-fDy1)*0.5/fDz;
1043    central=0.5*(fDy1+fDy2);
1044
1045    // +ve plane (1)
1046    //
1047    ss1=central+tanyz*p.z()-p.y(); // distance || y axis to plane
1048                                   // (+ve if point inside)
1049    ds1=v.y()-tanyz*v.z();  // component towards +ve plane
1050                            // (-ve if +ve -> -ve direction)
1051    // -ve plane (2)
1052    //
1053    ss2=-tanyz*p.z()-p.y()-central; // distance || y axis to plane
1054                                    // (-ve if point inside)
1055    ds2=tanyz*v.z()+v.y();  // component towards -ve plane
1056
1057    if (ss1>0&&ss2<0)
1058    {
1059      // Normal case - entirely inside region
1060
1061      if (ds1<=0&&ds2<0)
1062      {   
1063        if (ss2<-kCarTolerance/2)
1064        {
1065          sn=ss2/ds2;  // Leave by -ve side
1066          snside=kMY;
1067        }
1068        else
1069        {
1070          sn=0; // Leave immediately by -ve side
1071          snside=kMY;
1072        }
1073      }
1074      else if (ds1>0&&ds2>=0)
1075      {
1076        if (ss1>kCarTolerance/2)
1077        {
1078          sn=ss1/ds1;  // Leave by +ve side
1079          snside=kPY;
1080        }
1081        else
1082        {
1083          sn=0; // Leave immediately by +ve side
1084          snside=kPY;
1085        }
1086      }
1087      else if (ds1>0&&ds2<0)
1088      {
1089        if (ss1>kCarTolerance/2)
1090        {
1091          // sn=ss1/ds1;  // Leave by +ve side
1092          if (ss2<-kCarTolerance/2)
1093          {
1094            sn=ss1/ds1;  // Leave by +ve side
1095            sn2=ss2/ds2;
1096            if (sn2<sn)
1097            {
1098              sn=sn2;
1099              snside=kMY;
1100            }
1101            else
1102            {
1103              snside=kPY;
1104            }
1105          }
1106          else
1107          {
1108            sn=0; // Leave immediately by -ve
1109            snside=kMY;
1110          }
1111        }
1112        else
1113        {
1114          sn=0; // Leave immediately by +ve side
1115          snside=kPY;
1116        }
1117      }
1118      else
1119      {
1120        // Must be || to both
1121        //
1122        sn=kInfinity;    // Don't leave by either side
1123      }
1124    }
1125    else if (ss1<=0&&ss2<0)
1126    {
1127      // Outside, in +ve Area
1128
1129      if (ds1>0)
1130      {
1131        sn=0;       // Away from shape
1132                    // Left by +ve side
1133        snside=kPY;
1134      }
1135      else
1136      {
1137        if (ds2<0)
1138        {
1139          // Ignore +ve plane and use -ve plane intersect
1140          //
1141          sn=ss2/ds2; // Leave by -ve side
1142          snside=kMY;
1143        }
1144        else
1145        {
1146          // Must be || to both -> exit determined by other axes
1147          //
1148          sn=kInfinity; // Don't leave by either side
1149        }
1150      }
1151    }
1152    else if (ss1>0&&ss2>=0)
1153    {
1154      // Outside, in -ve Area
1155      if (ds2<0)
1156      {
1157        sn=0;       // away from shape
1158                    // Left by -ve side
1159        snside=kMY;
1160      }
1161      else
1162      {
1163        if (ds1>0)
1164        {
1165          // Ignore +ve plane and use -ve plane intersect
1166          //
1167          sn=ss1/ds1; // Leave by +ve side
1168          snside=kPY;
1169        }
1170        else
1171        {
1172          // Must be || to both -> exit determined by other axes
1173          //
1174          sn=kInfinity; // Don't leave by either side
1175        }
1176      }
1177    }
1178
1179    // Update minimum exit distance
1180
1181    if (sn<snxt)
1182    {
1183      snxt=sn;
1184      side=snside;
1185    }
1186  }
1187
1188  if (calcNorm)
1189  {
1190    switch (side)
1191    {
1192      case kPX:
1193        cosxz=1.0/std::sqrt(1.0+tanxz*tanxz);
1194        *n=G4ThreeVector(cosxz,0,-tanxz*cosxz);
1195        break;
1196      case kMX:
1197        cosxz=-1.0/std::sqrt(1.0+tanxz*tanxz);
1198        *n=G4ThreeVector(cosxz,0,tanxz*cosxz);
1199        break;
1200      case kPY:
1201        cosyz=1.0/std::sqrt(1.0+tanyz*tanyz);
1202        *n=G4ThreeVector(0,cosyz,-tanyz*cosyz);
1203        break;
1204      case kMY:
1205        cosyz=-1.0/std::sqrt(1.0+tanyz*tanyz);
1206        *n=G4ThreeVector(0,cosyz,tanyz*cosyz);
1207        break;
1208      case kPZ:
1209        *n=G4ThreeVector(0,0,1);
1210        break;
1211      case kMZ:
1212        *n=G4ThreeVector(0,0,-1);
1213        break;
1214      default:
1215        DumpInfo();
1216        G4Exception("G4Trd::DistanceToOut(p,v,..)","Notification",JustWarning, 
1217                    "Undefined side for valid surface normal to solid.");
1218        break;
1219    }
1220  }
1221  return snxt; 
1222}
1223
1224///////////////////////////////////////////////////////////////////////////
1225//
1226// Calculate exact shortest distance to any boundary from inside
1227// - Returns 0 is point outside
1228
1229G4double G4Trd::DistanceToOut( const G4ThreeVector& p ) const
1230{
1231  G4double safe=0.0;
1232  G4double tanxz,xdist,saf1;
1233  G4double tanyz,ydist,saf2;
1234  G4double zbase;
1235
1236#ifdef G4CSGDEBUG
1237  if( Inside(p) == kOutside )
1238  {
1239     G4cout.precision(16) ;
1240     G4cout << G4endl ;
1241     DumpInfo();
1242     G4cout << "Position:"  << G4endl << G4endl ;
1243     G4cout << "p.x() = "   << p.x()/mm << " mm" << G4endl ;
1244     G4cout << "p.y() = "   << p.y()/mm << " mm" << G4endl ;
1245     G4cout << "p.z() = "   << p.z()/mm << " mm" << G4endl << G4endl ;
1246     G4Exception("G4Trd::DistanceToOut(p)", "Notification", JustWarning, 
1247                 "Point p is outside !?" );
1248  }
1249#endif
1250
1251  safe=fDz-std::fabs(p.z());  // z perpendicular Dist
1252
1253  zbase=fDz+p.z();
1254
1255  // xdist = distance perpendicular to z axis to closest x plane from p
1256  //       = (x half width of shape at p.z) - std::fabs(p.x)
1257  //
1258  tanxz=(fDx2-fDx1)*0.5/fDz;
1259  xdist=fDx1+tanxz*zbase-std::fabs(p.x());
1260  saf1=xdist/std::sqrt(1.0+tanxz*tanxz); // x*std::cos(ang_xz) =
1261                                    // shortest (perpendicular)
1262                                    // distance to plane
1263  tanyz=(fDy2-fDy1)*0.5/fDz;
1264  ydist=fDy1+tanyz*zbase-std::fabs(p.y());
1265  saf2=ydist/std::sqrt(1.0+tanyz*tanyz);
1266
1267  // Return minimum x/y/z distance
1268  //
1269  if (safe>saf1) safe=saf1;
1270  if (safe>saf2) safe=saf2;
1271
1272  if (safe<0) safe=0;
1273  return safe;     
1274}
1275
1276////////////////////////////////////////////////////////////////////////////
1277//
1278// Create a List containing the transformed vertices
1279// Ordering [0-3] -fDz cross section
1280//          [4-7] +fDz cross section such that [0] is below [4],
1281//                                             [1] below [5] etc.
1282// Note:
1283//  Caller has deletion resposibility
1284
1285G4ThreeVectorList*
1286G4Trd::CreateRotatedVertices( const G4AffineTransform& pTransform ) const
1287{
1288  G4ThreeVectorList *vertices;
1289  vertices=new G4ThreeVectorList();
1290  vertices->reserve(8);
1291  if (vertices)
1292  {
1293    G4ThreeVector vertex0(-fDx1,-fDy1,-fDz);
1294    G4ThreeVector vertex1(fDx1,-fDy1,-fDz);
1295    G4ThreeVector vertex2(fDx1,fDy1,-fDz);
1296    G4ThreeVector vertex3(-fDx1,fDy1,-fDz);
1297    G4ThreeVector vertex4(-fDx2,-fDy2,fDz);
1298    G4ThreeVector vertex5(fDx2,-fDy2,fDz);
1299    G4ThreeVector vertex6(fDx2,fDy2,fDz);
1300    G4ThreeVector vertex7(-fDx2,fDy2,fDz);
1301
1302    vertices->push_back(pTransform.TransformPoint(vertex0));
1303    vertices->push_back(pTransform.TransformPoint(vertex1));
1304    vertices->push_back(pTransform.TransformPoint(vertex2));
1305    vertices->push_back(pTransform.TransformPoint(vertex3));
1306    vertices->push_back(pTransform.TransformPoint(vertex4));
1307    vertices->push_back(pTransform.TransformPoint(vertex5));
1308    vertices->push_back(pTransform.TransformPoint(vertex6));
1309    vertices->push_back(pTransform.TransformPoint(vertex7));
1310  }
1311  else
1312  {
1313    DumpInfo();
1314    G4Exception("G4Trd::CreateRotatedVertices()",
1315                "FatalError", FatalException,
1316                "Error in allocation of vertices. Out of memory !");
1317  }
1318  return vertices;
1319}
1320
1321//////////////////////////////////////////////////////////////////////////
1322//
1323// GetEntityType
1324
1325G4GeometryType G4Trd::GetEntityType() const
1326{
1327  return G4String("G4Trd");
1328}
1329
1330//////////////////////////////////////////////////////////////////////////
1331//
1332// Stream object contents to an output stream
1333
1334std::ostream& G4Trd::StreamInfo( std::ostream& os ) const
1335{
1336  os << "-----------------------------------------------------------\n"
1337     << "    *** Dump for solid - " << GetName() << " ***\n"
1338     << "    ===================================================\n"
1339     << " Solid type: G4Trd\n"
1340     << " Parameters: \n"
1341     << "    half length X, surface -dZ: " << fDx1/mm << " mm \n"
1342     << "    half length X, surface +dZ: " << fDx2/mm << " mm \n"
1343     << "    half length Y, surface -dZ: " << fDy1/mm << " mm \n"
1344     << "    half length Y, surface +dZ: " << fDy2/mm << " mm \n"
1345     << "    half length Z             : " << fDz/mm << " mm \n"
1346     << "-----------------------------------------------------------\n";
1347
1348  return os;
1349}
1350
1351
1352////////////////////////////////////////////////////////////////////////
1353//
1354// GetPointOnSurface
1355//
1356// Return a point (G4ThreeVector) randomly and uniformly
1357// selected on the solid surface
1358
1359G4ThreeVector G4Trd::GetPointOnSurface() const
1360{
1361  G4double px, py, pz, tgX, tgY, secX, secY, select, sumS, tmp;
1362  G4double Sxy1, Sxy2, Sxy, Sxz, Syz;
1363
1364  tgX  = 0.5*(fDx2-fDx1)/fDz;
1365  secX = std::sqrt(1+tgX*tgX);
1366  tgY  = 0.5*(fDy2-fDy1)/fDz;
1367  secY = std::sqrt(1+tgY*tgY);
1368
1369  // calculate 0.25 of side surfaces, sumS is 0.25 of total surface
1370
1371  Sxy1 = fDx1*fDy1; 
1372  Sxy2 = fDx2*fDy2;
1373  Sxy  = Sxy1 + Sxy2; 
1374  Sxz  = (fDx1 + fDx2)*fDz*secY; 
1375  Syz  = (fDy1 + fDy2)*fDz*secX;
1376  sumS = Sxy + Sxz + Syz;
1377
1378  select = sumS*G4UniformRand();
1379 
1380  if( select < Sxy )                  // Sxy1 or Sxy2
1381  {
1382    if( select < Sxy1 ) 
1383    {
1384      pz = -fDz;
1385      px = -fDx1 + 2*fDx1*G4UniformRand();
1386      py = -fDy1 + 2*fDy1*G4UniformRand();
1387    }
1388    else     
1389    {
1390      pz =  fDz;
1391      px = -fDx2 + 2*fDx2*G4UniformRand();
1392      py = -fDy2 + 2*fDy2*G4UniformRand();
1393    }
1394  }
1395  else if ( ( select - Sxy ) < Sxz )    // Sxz
1396  {
1397    pz  = -fDz  + 2*fDz*G4UniformRand();
1398    tmp =  fDx1 + (pz + fDz)*tgX;
1399    px  = -tmp  + 2*tmp*G4UniformRand();
1400    tmp =  fDy1 + (pz + fDz)*tgY;
1401
1402    if(G4UniformRand() > 0.5) { py =  tmp; }
1403    else                      { py = -tmp; }
1404  }
1405  else                                   // Syz
1406  {
1407    pz  = -fDz  + 2*fDz*G4UniformRand();
1408    tmp =  fDy1 + (pz + fDz)*tgY;
1409    py  = -tmp  + 2*tmp*G4UniformRand();
1410    tmp =  fDx1 + (pz + fDz)*tgX;
1411
1412    if(G4UniformRand() > 0.5) { px =  tmp; }
1413    else                      { px = -tmp; }
1414  } 
1415  return G4ThreeVector(px,py,pz);
1416}
1417
1418///////////////////////////////////////////////////////////////////////
1419//
1420// Methods for visualisation
1421
1422void G4Trd::DescribeYourselfTo ( G4VGraphicsScene& scene ) const
1423{
1424  scene.AddSolid (*this);
1425}
1426
1427G4Polyhedron* G4Trd::CreatePolyhedron () const
1428{
1429  return new G4PolyhedronTrd2 (fDx1, fDx2, fDy1, fDy2, fDz);
1430}
1431
1432G4NURBS* G4Trd::CreateNURBS () const
1433{
1434  //  return new G4NURBSbox (fDx, fDy, fDz);
1435  return 0;
1436}
Note: See TracBrowser for help on using the repository browser.