00001 // 00002 // ******************************************************************** 00003 // * License and Disclaimer * 00004 // * * 00005 // * The Geant4 software is copyright of the Copyright Holders of * 00006 // * the Geant4 Collaboration. It is provided under the terms and * 00007 // * conditions of the Geant4 Software License, included in the file * 00008 // * LICENSE and available at http://cern.ch/geant4/license . These * 00009 // * include a list of copyright holders. * 00010 // * * 00011 // * Neither the authors of this software system, nor their employing * 00012 // * institutes,nor the agencies providing financial support for this * 00013 // * work make any representation or warranty, express or implied, * 00014 // * regarding this software system or assume any liability for its * 00015 // * use. Please see the license in the file LICENSE and URL above * 00016 // * for the full disclaimer and the limitation of liability. * 00017 // * * 00018 // * This code implementation is the result of the scientific and * 00019 // * technical work of the GEANT4 collaboration. * 00020 // * By using, copying, modifying or distributing the software (or * 00021 // * any work based on the software) you agree to acknowledge its * 00022 // * use in resulting scientific publications, and indicate your * 00023 // * acceptance of all terms of the Geant4 Software license. * 00024 // ******************************************************************** 00025 // 00026 // 00027 // $Id$ 00028 // 00029 // ---------------------------------------------------------------------- 00030 // GEANT 4 class source file 00031 // 00032 // G4ConicalSurface.cc 00033 // 00034 // ---------------------------------------------------------------------- 00035 00036 #include "G4ConicalSurface.hh" 00037 #include "G4PhysicalConstants.hh" 00038 #include "G4Sort.hh" 00039 #include "G4Globals.hh" 00040 00041 00042 G4ConicalSurface::G4ConicalSurface() 00043 : G4Surface(), axis(G4Vector3D(1.,0.,0.)), angle(1.) 00044 { 00045 } 00046 00047 G4ConicalSurface::G4ConicalSurface( const G4Point3D&, 00048 const G4Vector3D& a, 00049 G4double e ) 00050 : G4Surface() 00051 { 00052 // Normal constructor 00053 // require axis to be a unit vector 00054 00055 G4double amag = a.mag2(); 00056 00057 if ( amag != 0.0 ) 00058 { 00059 axis = a*(1/amag); 00060 } 00061 else 00062 { 00063 std::ostringstream message; 00064 message << "Axis has zero length." << G4endl 00065 << "Default axis ( 1.0, 0.0, 0.0 ) is used."; 00066 G4Exception("G4ConicalSurface::G4ConicalSurface()", "GeomSolids1001", 00067 JustWarning, message); 00068 00069 axis = G4Vector3D( 1.0, 0.0, 0.0 ); 00070 } 00071 00072 // Require angle to range from 0 to PI/2 00073 // 00074 if ( ( e > 0.0 ) && ( e < ( 0.5 * pi ) ) ) 00075 { 00076 angle = e; 00077 } 00078 else 00079 { 00080 std::ostringstream message; 00081 message << "Angle out of range." << G4endl 00082 << "Asked for angle out of allowed range of 0 to " 00083 << 0.5*pi << " (PI/2): " << e << G4endl 00084 << "Default angle of 1.0 is used."; 00085 G4Exception("G4ConicalSurface::G4ConicalSurface()", "GeomSolids1001", 00086 JustWarning, message); 00087 angle = 1.0; 00088 } 00089 } 00090 00091 00092 G4ConicalSurface::~G4ConicalSurface() 00093 { 00094 } 00095 00096 00097 G4ConicalSurface::G4ConicalSurface( const G4ConicalSurface& c ) 00098 : G4Surface(), axis(c.axis), angle(c.angle) 00099 { 00100 } 00101 00102 00103 G4ConicalSurface& 00104 G4ConicalSurface::operator=( const G4ConicalSurface& c ) 00105 { 00106 if (&c == this) { return *this; } 00107 axis = c.axis; 00108 angle = c.angle; 00109 00110 return *this; 00111 } 00112 00113 00114 const char* G4ConicalSurface::NameOf() const 00115 { 00116 return "G4ConicalSurface"; 00117 } 00118 00119 void G4ConicalSurface::CalcBBox() 00120 { 00121 bbox= new G4BoundingBox3D(surfaceBoundary.BBox().GetBoxMin(), 00122 surfaceBoundary.BBox().GetBoxMax()); 00123 } 00124 00125 void G4ConicalSurface::PrintOn( std::ostream& os ) const 00126 { 00127 // printing function using C++ std::ostream class 00128 00129 os << "G4ConicalSurface surface with origin: " << origin << "\t" 00130 << "angle: " << angle << " radians \tand axis " << axis << "\n"; 00131 } 00132 00133 00134 G4double G4ConicalSurface::HowNear( const G4Vector3D& x ) const 00135 { 00136 // Distance from the point x to the semi-infinite G4ConicalSurface. 00137 // The distance will be positive if the point is Inside the G4ConicalSurface, 00138 // negative if the point is outside. 00139 // Note that this may not be correct for a bounded conical object 00140 // subclassed to G4ConicalSurface. 00141 00142 G4Vector3D d = G4Vector3D( x - origin ); 00143 G4double l = d * axis; 00144 G4Vector3D q = G4Vector3D( origin + l * axis ); 00145 G4Vector3D v = G4Vector3D( x - q ); 00146 00147 G4double Dist = ( l*std::tan(angle) - v.mag2() ) * std::cos(angle); 00148 00149 return Dist; 00150 } 00151 00152 00153 G4int G4ConicalSurface::Intersect( const G4Ray& ry ) 00154 { 00155 // Distance along a Ray (straight line with G4Vector3D) to leave or enter 00156 // a G4ConicalSurface. The input variable which_way should be set to +1 to 00157 // indicate leaving a G4ConicalSurface, -1 to indicate entering a 00158 // G4ConicalSurface. 00159 // p is the point of intersection of the Ray with the G4ConicalSurface. 00160 // If the G4Vector3D of the Ray is opposite to that of the Normal to 00161 // the G4ConicalSurface at the intersection point, it will not leave the 00162 // G4ConicalSurface. 00163 // Similarly, if the G4Vector3D of the Ray is along that of the Normal 00164 // to the G4ConicalSurface at the intersection point, it will not enter the 00165 // G4ConicalSurface. 00166 // This method is called by all finite shapes sub-classed to 00167 // G4ConicalSurface. 00168 // Use the virtual function table to check if the intersection point 00169 // is within the boundary of the finite shape. 00170 // A negative result means no intersection. 00171 // If no valid intersection point is found, set the distance 00172 // and intersection point to large numbers. 00173 00174 G4int which_way = -1; // Originally a parameter.Read explanation above. 00175 00176 distance = kInfinity; 00177 00178 G4Vector3D lv ( kInfinity, kInfinity, kInfinity ); 00179 closest_hit = lv; 00180 00181 // Origin and G4Vector3D unit vector of Ray. 00182 // 00183 G4Vector3D x = G4Vector3D( ry.GetStart() ); 00184 G4Vector3D dhat = ry.GetDir(); 00185 00186 00187 // Cone angle and axis unit vector. 00188 // 00189 G4double ta = std::tan( GetAngle() ); 00190 G4Vector3D ahat = GetAxis(); 00191 G4int isoln = 0, 00192 maxsoln = 2; 00193 00194 // array of solutions in distance along the Ray 00195 // 00196 G4double sol[2]; 00197 sol[0] = -1.0; 00198 sol[1] = -1.0 ; 00199 00200 // calculate the two solutions (quadratic equation) 00201 // 00202 G4Vector3D gamma = G4Vector3D( x - GetOrigin() ); 00203 G4double T = 1.0 + ta * ta; 00204 G4double ga = gamma * ahat; 00205 G4double da = dhat * ahat; 00206 G4double A = 1.0 - T * da * da; 00207 G4double B = 2.0 * ( gamma * dhat - T * ga * da ); 00208 G4double C = gamma * gamma - T * ga * ga; 00209 00210 // if quadratic term vanishes, just do the simple solution 00211 // 00212 if ( std::fabs( A ) < kCarTolerance ) 00213 { 00214 if ( B == 0.0 ) 00215 { return 1; } 00216 else 00217 { sol[0] = -C / B; } 00218 } 00219 00220 // Normal quadratic case, no intersection if radical is less than zero 00221 // 00222 else 00223 { 00224 G4double radical = B * B - 4.0 * A * C; 00225 if ( radical < 0.0 ) 00226 { 00227 return 1; 00228 } 00229 else 00230 { 00231 G4double root = std::sqrt( radical ); 00232 sol[0] = ( - B + root ) / ( 2. * A ); 00233 sol[1] = ( - B - root ) / ( 2. * A ); 00234 } 00235 } 00236 00237 // order the possible solutions by increasing distance along the Ray 00238 // 00239 sort_double( sol, isoln, maxsoln-1 ); 00240 00241 // now loop over each positive solution, keeping the first one (smallest 00242 // distance along the Ray) which is within the boundary of the sub-shape 00243 // and which also has the correct G4Vector3D with respect to the Normal to 00244 // the G4ConicalSurface at the intersection point 00245 // 00246 for ( isoln = 0; isoln < maxsoln; isoln++ ) 00247 { 00248 if ( sol[isoln] >= 0.0 ) 00249 { 00250 if ( sol[isoln] >= kInfinity ) // quit if too large 00251 { 00252 return 1; 00253 } 00254 00255 distance = sol[isoln]; 00256 closest_hit = ry.GetPoint( distance ); 00257 00258 // Following line necessary to select non-reflective solutions. 00259 // 00260 if (( ahat * ( closest_hit - GetOrigin() ) > 0.0 ) && 00261 ((( dhat * SurfaceNormal( closest_hit ) * which_way )) >= 0.0 ) && 00262 ( std::fabs(HowNear( closest_hit )) < 0.1) ) 00263 { 00264 return 1; 00265 } 00266 } 00267 } 00268 00269 // get here only if there was no solution within the boundary, Reset 00270 // distance and intersection point to large numbers 00271 // 00272 distance = kInfinity; 00273 closest_hit = lv; 00274 00275 return 0; 00276 } 00277 00278 00279 /* 00280 G4double G4ConicalSurface::distanceAlongHelix(G4int which_way, const Helix* hx, 00281 G4Vector3D& p ) const 00282 { // Distance along a Helix to leave or enter a G4ConicalSurface. 00283 // The input variable which_way should be set to +1 to 00284 // indicate leaving a G4ConicalSurface, -1 to indicate entering a 00285 // G4ConicalSurface. 00286 // p is the point of intersection of the Helix with the G4ConicalSurface. 00287 // If the G4Vector3D of the Helix is opposite to that of the Normal to 00288 // the G4ConicalSurface at the intersection point, it will not leave the 00289 // G4ConicalSurface. 00290 // Similarly, if the G4Vector3D of the Helix is along that of the Normal 00291 // to the G4ConicalSurface at the intersection point, it will not enter the 00292 // G4ConicalSurface. 00293 // This method is called by all finite shapes sub-classed to 00294 // G4ConicalSurface. 00295 // Use the virtual function table to check if the intersection point 00296 // is within the boundary of the finite shape. 00297 // If no valid intersection point is found, set the distance 00298 // and intersection point to large numbers. 00299 // Possible negative distance solutions are discarded. 00300 G4double Dist = kInfinity; 00301 G4Vector3D lv ( kInfinity, kInfinity, kInfinity ); 00302 p = lv; 00303 G4int isoln = 0, maxsoln = 4; 00304 00305 // Array of solutions in turning angle 00306 // G4double s[4] = { -1.0, -1.0, -1.0, -1.0 }; 00307 G4double s[4];s[0] = -1.0; s[1]= -1.0 ;s[2] = -1.0; s[3]= -1.0 ; 00308 00309 // Flag set to 1 if exact solution is found 00310 G4int exact = 0; 00311 00312 // Helix parameters 00313 G4double rh = hx->GetRadius(); // radius of Helix 00314 G4Vector3D oh = hx->position(); // origin of Helix 00315 G4Vector3D dh = hx->direction( 0.0 ); // initial G4Vector3D of Helix 00316 G4Vector3D prp = hx->getPerp(); // perpendicular vector 00317 G4double prpmag = prp.Magnitude(); 00318 G4double rhp = rh / prpmag; 00319 00320 // G4ConicalSurface parameters 00321 G4double ta = std::tan( GetAngle() ); // tangent of angle of G4ConicalSurface 00322 G4Vector3D oc = GetOrigin(); // origin of G4ConicalSurface 00323 G4Vector3D ac = GetAxis(); // axis of G4ConicalSurface 00324 00325 // Calculate quantities of use later on 00326 G4Vector3D alpha = rhp * prp; 00327 G4Vector3D beta = rhp * dh; 00328 G4Vector3D gamma = oh - oc; 00329 G4double T = 1.0 + ta * ta; 00330 G4double gc = gamma * ac; 00331 G4double bc = beta * ac; 00332 00333 // General approximate solution for std::sin(s)-->s and std::cos(s)-->1-s**2/2, 00334 // keeping only terms to second order in s 00335 G4double A = gamma * alpha - T * ( gc * alpha * ac - bc * bc ) + 00336 beta * beta; 00337 G4double B = 2.0 * ( gamma * beta - gc * bc * T ); 00338 G4double C = gamma * gamma - gc * gc * T; 00339 00340 // Solution for no quadratic term 00341 if ( std::fabs( A ) < kCarTolerance ) 00342 { 00343 if ( B == 0.0 ) 00344 return Dist; 00345 else 00346 s[0] = -C / B; 00347 } 00348 00349 // General quadratic solutions 00350 else { 00351 G4double radical = B * B - 4.0 * A * C; 00352 if ( radical < 0.0 ) 00353 // Radical is less than zero, either there is no intersection, or the 00354 // approximation doesn't hold, so try a cruder technique to find a 00355 // possible intersection point using the gropeAlongHelix function. 00356 s[0] = gropeAlongHelix( hx ); 00357 // Normal non-negative radical solutions 00358 else { 00359 G4double root = std::sqrt( radical ); 00360 s[0] = ( -B + root ) / ( 2.0 * A ); 00361 s[1] = ( -B - root ) / ( 2.0 * A ); 00362 if ( rh < 0.0 ) { 00363 s[0] = -s[0]; 00364 s[1] = -s[1]; 00365 } 00366 s[2] = s[0] + 2.0 * pi; 00367 s[3] = s[1] + 2.0 * pi; 00368 } 00369 } 00370 // 00371 // Order the possible solutions by increasing turning angle 00372 // (G4Sorting routines are in support/G4Sort.h). 00373 G4Sort_double( s, isoln, maxsoln-1 ); 00374 // 00375 // Now loop over each positive solution, keeping the first one (smallest 00376 // distance along the Helix) which is within the boundary of the sub-shape. 00377 for ( isoln = 0; isoln < maxsoln; isoln++ ) { 00378 if ( s[isoln] >= 0.0 ) { 00379 // Calculate distance along Helix and position and G4Vector3D vectors. 00380 Dist = s[isoln] * std::fabs( rhp ); 00381 p = hx->position( Dist ); 00382 G4Vector3D d = hx->direction( Dist ); 00383 if ( exact == 0 ) { // only for approximate solns 00384 // Now do approximation to get remaining distance to correct this solution. 00385 // Iterate it until the accuracy is below the user-set surface precision. 00386 G4double delta = 0.; 00387 G4double delta0 = kInfinity; 00388 G4int dummy = 1; 00389 G4int iter = 0; 00390 G4int in0 = Inside( hx->position() ); 00391 G4int in1 = Inside( p ); 00392 G4double sc = Scale(); 00393 while ( dummy ) { 00394 iter++; 00395 // Terminate loop after 50 iterations and Reset distance to large number, 00396 // indicating no intersection with G4ConicalSurface. 00397 // This generally occurs if the Helix curls too tightly to Intersect it. 00398 if ( iter > 50 ) { 00399 Dist = kInfinity; 00400 p = lv; 00401 break; 00402 } 00403 // Find distance from the current point along the above-calculated 00404 // G4Vector3D using a Ray. 00405 // The G4Vector3D of the Ray and the Sign of the distance are determined 00406 // by whether the starting point of the Helix is Inside or outside of 00407 // the G4ConicalSurface. 00408 in1 = Inside( p ); 00409 if ( in1 ) { // current point Inside 00410 if ( in0 ) { // starting point Inside 00411 Ray* r = new Ray( p, d ); 00412 delta = 00413 distanceAlongRay( 1, r, p ); 00414 delete r; 00415 } 00416 else { // starting point outside 00417 Ray* r = new Ray( p, -d ); 00418 delta = 00419 -distanceAlongRay( 1, r, p ); 00420 delete r; 00421 } 00422 } 00423 else { // current point outside 00424 if ( in0 ) { // starting point Inside 00425 Ray* r = new Ray( p, -d ); 00426 delta = 00427 -distanceAlongRay( -1, r, p ); 00428 delete r; 00429 } 00430 else { // starting point outside 00431 Ray* r = new Ray( p, d ); 00432 delta = 00433 distanceAlongRay( -1, r, p ); 00434 delete r; 00435 } 00436 } 00437 // Test if distance is less than the surface precision, if so Terminate loop. 00438 if ( std::fabs( delta / sc ) <= SURFACE_PRECISION ) 00439 break; 00440 // If delta has not changed sufficiently from the previous iteration, 00441 // skip out of this loop. 00442 if ( std::fabs( ( delta - delta0 ) / sc ) <= 00443 SURFACE_PRECISION ) 00444 break; 00445 // If delta has increased in absolute value from the previous iteration 00446 // either the Helix doesn't Intersect the G4ConicalSurface or the approximate solution 00447 // is too far from the real solution. Try groping for a solution. If not 00448 // found, Reset distance to large number, indicating no intersection with 00449 // the G4ConicalSurface. 00450 if ( std::fabs( delta ) > std::fabs( delta0 ) ) { 00451 Dist = std::fabs( rhp ) * 00452 gropeAlongHelix( hx ); 00453 if ( Dist < 0.0 ) { 00454 Dist = kInfinity; 00455 p = lv; 00456 } 00457 else 00458 p = hx->position( Dist ); 00459 break; 00460 } 00461 // Set old delta to new one. 00462 delta0 = delta; 00463 // Add distance to G4ConicalSurface to distance along Helix. 00464 Dist += delta; 00465 // Negative distance along Helix means Helix doesn't Intersect G4ConicalSurface. 00466 // Reset distance to large number, indicating no intersection with G4ConicalSurface. 00467 if ( Dist < 0.0 ) { 00468 Dist = kInfinity; 00469 p = lv; 00470 break; 00471 } 00472 // Recalculate point along Helix and the G4Vector3D. 00473 p = hx->position( Dist ); 00474 d = hx->direction( Dist ); 00475 } // end of while loop 00476 } // end of exact == 0 condition 00477 // Now have best value of distance along Helix and position for this 00478 // solution, so test if it is within the boundary of the sub-shape 00479 // and require that it point in the correct G4Vector3D with respect to 00480 // the Normal to the G4ConicalSurface. 00481 if ( ( Dist < kInfinity ) && 00482 ( ( hx->direction( Dist ) * Normal( p ) * 00483 which_way ) >= 0.0 ) && 00484 ( WithinBoundary( p ) == 1 ) ) 00485 return Dist; 00486 } // end of if s[isoln] >= 0.0 condition 00487 } // end of for loop over solutions 00488 // If one gets here, there is no solution, so set distance along Helix 00489 // and position to large numbers. 00490 Dist = kInfinity; 00491 p = lv; 00492 return Dist; 00493 } 00494 */ 00495 00496 00497 G4Vector3D G4ConicalSurface::SurfaceNormal( const G4Point3D& p ) const 00498 { 00499 // return the Normal unit vector to the G4ConicalSurface at a point p 00500 // on (or nearly on) the G4ConicalSurface 00501 00502 G4Vector3D ss = G4Vector3D( p - origin ); 00503 G4double smag = ss.mag2(); 00504 00505 // if the point happens to be at the origin, calculate a unit vector Normal 00506 // to the axis, with zero z component 00507 // 00508 if ( smag == 0.0 ) 00509 { 00510 G4double ax = axis.x(); 00511 G4double ay = axis.y(); 00512 G4double ap = std::sqrt( ax * ax + ay * ay ); 00513 00514 if ( ap == 0.0 ) 00515 { return G4Vector3D( 1.0, 0.0, 0.0 ); } 00516 else 00517 { return G4Vector3D( ay / ap, -ax / ap, 0.0 ); } 00518 } 00519 else // otherwise do the calculation of the Normal to the conical surface 00520 { 00521 G4double l = ss * axis; 00522 ss = ss*(1/smag); 00523 G4Vector3D q = G4Vector3D( origin + l * axis ); 00524 G4Vector3D v = G4Vector3D( p - q ); 00525 G4double sl = v.mag2() * std::sin( angle ); 00526 G4Vector3D n = G4Vector3D( v - sl * ss ); 00527 G4double nmag = n.mag2(); 00528 00529 if ( nmag != 0.0 ) 00530 { 00531 n=n*(1/nmag); 00532 } 00533 return n; 00534 } 00535 } 00536 00537 00538 G4int G4ConicalSurface::Inside ( const G4Vector3D& x ) const 00539 { 00540 // Return 0 if point x is outside G4ConicalSurface, 1 if Inside. 00541 // Outside means that the distance to the G4ConicalSurface would be negative. 00542 // Use the HowNear function to calculate this distance. 00543 00544 if ( HowNear( x ) >= -0.5*kCarTolerance ) 00545 { return 1; } 00546 else 00547 { return 0; } 00548 } 00549 00550 00551 G4int G4ConicalSurface::WithinBoundary( const G4Vector3D& x ) const 00552 { 00553 // return 1 if point x is on the G4ConicalSurface, otherwise return zero 00554 // base this on the surface precision factor 00555 00556 if ( std::fabs( HowNear( x ) / Scale() ) <= SURFACE_PRECISION ) 00557 { return 1; } 00558 else 00559 { return 0; } 00560 } 00561 00562 G4double G4ConicalSurface::Scale() const 00563 { 00564 return 1.0; 00565 } 00566 00567 void G4ConicalSurface::SetAngle( G4double e ) 00568 { 00569 // Reset the angle of the G4ConicalSurface 00570 // Require angle to range from 0 to PI/2 00571 00572 if ( (e > 0.0) && (e <= ( 0.5 * pi )) ) 00573 { 00574 angle = e; 00575 } 00576 else // use old value (do not change angle) if out of the range, 00577 { // but print warning message 00578 std::ostringstream message; 00579 message << "Angle out of range." << G4endl 00580 << "Asked for angle out of allowed range of 0 to " 00581 << 0.5*pi << " (PI/2): " << e << G4endl 00582 << "Default angle of " << angle << " is used."; 00583 G4Exception("G4ConicalSurface::SetAngle()", "GeomSolids1001", 00584 JustWarning, message); 00585 } 00586 } 00587