Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:54

0001 #pragma once
0002 
0003 /**
0004 distance_leaf_phicut
0005 -----------------------
0006 
0007 ::
0008 
0009       . . . . . . phi0=0.5
0010       . . . . . . |
0011       . . . . . . |
0012       . . . . . . |--> [sinPhi0,-cosPhi0] = [1,0]
0013       . . . . . . |
0014       . . . . . . |
0015       . . . . . . |
0016       . . . . . . | sd0   (x,y)
0017       . . . . . . +. . . +
0018       . . . . . . |      :
0019       . . . . . . |      sd1
0020       . . . . . . |      :           [ -sinPhi1, cosPhi1] = [0, 1]
0021       . . . . . . |      :          ^
0022       . . . . . . |      :          |
0023       . . . . . . +------+----------+--- phi1=2
0024       . . . . . . . . . . . . . . . . . . . . .
0025       . . . . . . . . . . . . . . . . . . . . .
0026       . . . . . . . . . . . . . . . . . . . . .
0027       . . . . . . . . . . . . . . . . . . . . .
0028       . . . . . . . . . . . . . . . . . . . . .
0029       . . . . . . . . . . . . . . . . . . . . .
0030 
0031 **/
0032 
0033 LEAF_FUNC
0034 float distance_leaf_phicut( const float3& pos, const quad& q0 )
0035 {
0036     const float& cosPhi0 = q0.f.x ;
0037     const float& sinPhi0 = q0.f.y ;
0038     const float& cosPhi1 = q0.f.z ;
0039     const float& sinPhi1 = q0.f.w ;
0040 
0041     // dot products with normal0  [  sinPhi0, -cosPhi0, 0.f ]
0042     float sd0 = sinPhi0*pos.x - cosPhi0*pos.y ;
0043 
0044     // dot products with normal1  [ -sinPhi1,  cosPhi1, 0.f ]
0045     float sd1 = -sinPhi1*pos.x + cosPhi1*pos.y ;
0046 
0047     return fminf( sd0, sd1 );
0048 }
0049 
0050 
0051 /**
0052 intersect_leaf_phicut
0053 ------------------------
0054 
0055 Unbounded shape that cuts phi via two half-planes "attached" to the z-axis.
0056 See phicut.py for development and testing of intersect_node_phicut
0057 The phicut planes go through the origin so the equation of the plane is::
0058 
0059     p.n = 0
0060 
0061 Intersecting a ray with the plane (with normal vector n)::
0062 
0063     p = o + t d       parametric ray equation, o:ray_origin d:ray_direction t:distance
0064 
0065    (o + t d).n = 0
0066 
0067       o.n  = - t d.n
0068 
0069               -o.n
0070        t  = -----------
0071                d.n
0072 
0073 Outwards normals of the two planes::
0074 
0075     n0   (  sin(phi0), -cos(phi0), 0 )   =  ( 0, -1,  0)    phi0 = 0
0076     n1   ( -sin(phi1),  cos(phi1), 0 )   =  ( -1, 0,  0)    phi1 = 0.5
0077 
0078 Vectors within the two planes::
0079 
0080     w0  ( cos(phi0), sin(phi0), 0 )     =  (1, 0, 0 )       phi0 = 0
0081     w1  ( cos(phi1), sin(phi1), 0 )     =  (0, 1, 0)        phi1 = 0.5
0082 
0083 
0084                 phi1=0.5
0085                   Y
0086                   | . . . . . . .
0087                   | . . . . . . .
0088                   | . . . . . . .
0089               n1--+ . . . . . . .
0090                   | . . . . . . .
0091                   | . . . . . . .
0092    phi=1.0 . . . .O----+-------- X    phi0=0
0093                   .    |
0094                   .    n0
0095                   .
0096                   .
0097                   .
0098                 phi=1.5
0099 
0100 
0101 Normal incidence is no problem::
0102 
0103           Y
0104           |
0105           |
0106           |
0107           |     (10,0,0)
0108           O------+---------- X
0109                  |
0110                  |
0111                  |
0112                  *
0113                  normal        ( 0,  -1, 0)
0114                  ray_origin    (10, -10, 0)  .normal =  10
0115                  ray_direction ( 0,   1, 0)  .normal  = -1
0116 
0117                  t = -10/(-1) = 10
0118 
0119 Disqualify rays with direction within the plane::
0120 
0121        normal        (  0, -1, 0)
0122        ray_origin    ( 20, 0,  0)   .normal     0
0123        ray_direction ( -1, 0,  0)   .normal     0
0124 
0125 
0126 Edge vectors P, Q and radial direction vector R::
0127 
0128    P  ( cosPhi0, sinPhi0, 0 )   vector within phi0 plane eg:     phi0=0     (1, 0, 0)      phi0=1  (-1, 0, 0)
0129    Q  ( cosPhi1, sinPhi1, 0 )   vector within phi1 plane eg:     phi1=0.5   (0, 1, 0)
0130    R  (  d.x   ,   d.y  , 0 )        d.z is ignored, only intersected in radial direction within XY plane
0131 
0132 Use conventional cross product sign convention  A ^ B =  (Ax By - Bx Ay ) k the products are::
0133 
0134    PQ = P ^ Q = cosPhi0*sinPhi1 - cosPhi1*sinPhi0
0135    PR = P ^ R = cosPhi0*d.y - d.x*sinPhi0
0136    QR = Q ^ R = cosPhi1*d.y - d.x*sinPhi1
0137 
0138 Note that as R is not normalized the PR and QR cross products are only proportional to the sine of the angles.
0139 See CSG/tests/cross2D_angle_range_without_trig.py for exploration of 2D cross product
0140 
0141 Alternative way of looking at this is with the sine of angle subtraction identity::
0142 
0143     sin(a-b) = sin(a)cos(b)-cos(a)sin(b)
0144 
0145 When rays have non-zero x,y direction components the 2D cross products
0146 between the direction vectors and the phi edge vectors determines
0147 if the rays will exit the unbounded shape at infinity.
0148 
0149 When rays are purely in Z  direction this approach does not work as PR and QR
0150 will always be zero because d.x and d.y are zero.
0151 So in this "zpure" case the (o.x, o.y) are used instead of (d.x, d.y)
0152 Can think of vectors from origin to the (o.x, o.y) ray origin which can
0153 serve the same purpose of determining whether the ray will exit the
0154 shape at infinity or not.
0155 
0156                                                         NO
0157             . . . . . . . . . . . .|                   /
0158             . . . . . . . . . . . .|                  /
0159             . . . . . . . . . . . .|                 /
0160             . . . . . . . . . . . .|                /
0161             . . . . . . . . . . phi0=0.5           /
0162             . . . . . . . . . . . .|              /
0163             . . . . . . . . . . . .|             0  sd > 0.f
0164             . . . . . . . . . . . .|
0165             . . . . . 0 . . . . . .|
0166             . . . . ./. . . . . . .|
0167             . . . . / . . . . . . .+------------ phi1=2.0 ---  X
0168             . . . ./. . . . . . . . . . . . . . . . . . . . . . .
0169             . . . / . . . . . . . . . . . . . . . . . . . . . . .
0170             . . ./. . . . . . . . . . . . . . . . . . . . . . . .
0171             . . / . . . . . . . . . . . . . . . .0------------------ YES UNBOUNDED_EXIT AT INFINITY
0172             . ./. . . . . . . . . . . . . . . .sd < 0.f . . . . .
0173             . / . . . . . . . . . . . . . . . . . . . . . . . . .
0174             ./. . . . . . . . . . . . . . . . . . . . . . . . . .
0175             / . . . . . . . . . . . . . . . . . . . . . . . . . .
0176            /
0177          YES
0178 
0179 
0180 
0181 Clockwise rotation by phi (flipping sign)::
0182 
0183   | x' |    |  cosPhi   sinPhi |   | x  |
0184   |    | =  |                  |   |    |
0185   | y' |    | -sinPhi   cosPhi |   | y  |
0186 
0187 
0188     x' = cosPhi x + sinPhi y
0189 
0190     y' = -sinPhi x + cosPhi y
0191 
0192 
0193 Within edgezone want to avoid two separate decisions as to which face gets hit
0194 otherwise numerical imprecision will result in inconsistent decisions causing misses (white seams) along the edge.
0195 
0196 
0197 
0198 Thoughts on implementing phicut by CSG combination of two halfspaces
0199 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0200 
0201 phi1 - phi0 > 1.0
0202     A+B      : union of two halfspaces
0203     !(!A.!B) : complement of intersection of the two halfspaces complemented
0204 
0205 ::
0206 
0207 
0208             A | !A
0209           phi0=0.5
0210        . . . .|
0211        . . . .|
0212        . . . .|
0213        . . . .|
0214        . . . .|            !B
0215        ~~~~~~~+--------- phi1=2.0
0216        . . . .:. . . . .    B
0217        . . . .:. . . . .
0218        . . . .:. . . . .
0219        . . . .:. . . . .
0220 
0221 phi1 - phi0 < 1.0 :
0222 
0223     A.B      :  intersection of two halfspaces
0224     !(!A+!B) : complement of union of two halfspaces complemented
0225 
0226            !A | A
0227            phi1=0.5
0228               | . . . . .
0229               | . . . . .
0230               | . . . . .
0231               | . . . . .  B
0232        ~~~~~~~+--------- phi0=0.0
0233               :            !B
0234               :
0235               :
0236               :
0237 **/
0238 
0239 
0240 LEAF_FUNC
0241 void intersect_leaf_phicut( bool& valid_isect, float4& isect, const quad& q0, const float t_min, const float3& o, const float3& d )
0242 {
0243     const float& cosPhi0 = q0.f.x ;
0244     const float& sinPhi0 = q0.f.y ;
0245     const float& cosPhi1 = q0.f.z ;
0246     const float& sinPhi1 = q0.f.w ;
0247 
0248 #ifdef DEBUG
0249     printf("//intersect_leaf_phicut  q0.f (%10.4f %10.4f   %10.4f %10.4f )   cosPhi0 sinPhi0   cosPhi1 sinPhi1      t_min  %10.4f \n", cosPhi0, sinPhi0, cosPhi1, sinPhi1, t_min );
0250 #endif
0251 
0252     const float sd0 =  sinPhi0*(o.x+t_min*d.x) - cosPhi0*(o.y+t_min*d.y) ;
0253     const float sd1 = -sinPhi1*(o.x+t_min*d.x) + cosPhi1*(o.y+t_min*d.y) ;
0254     float sd = fminf( sd0, sd1 );   // signed distance at t_min, sd < 0.f means are within the phicut shape : ie between the planes
0255 
0256 #ifdef DEBUG
0257     printf("//intersect_leaf_phicut  sd0 %10.4f sd1 %10.4f sd  %10.4f \n", sd0, sd1, sd );
0258 #endif
0259 
0260     const float PQ = cosPhi0*sinPhi1 - cosPhi1*sinPhi0  ;  // PQ +ve => angle < pi,   PQ -ve => angle > pi
0261     const bool zpure = d.x == 0.f && d.y == 0.f ;
0262     const float PR = cosPhi0*(zpure ? o.y : d.y) - (zpure ? o.x : d.x)*sinPhi0  ;          // PR and QR +ve/-ve selects the "side of the line"
0263     const float QR = cosPhi1*(zpure ? o.y : d.y) - (zpure ? o.x : d.x)*sinPhi1  ;
0264     const bool unbounded_exit = sd < 0.f &&  ( PQ >= 0.f ? ( PR >= 0.f && QR <= 0.f ) : ( PR >= 0.f || QR <= 0.f ))  ;
0265 
0266 #ifdef DEBUG
0267     printf("//intersect_leaf_phicut  PQ %10.4f zpure %d \n", PQ, zpure );
0268     printf("//intersect_leaf_phicut  PR %10.4f  QR %10.4f unbounded_exit %d \n", PR,QR, unbounded_exit );
0269 #endif
0270 
0271     // dotproduct with outward normal [ sinPhi0, -cosPhi0, 0 ]  of phi0 plane    eg [1,0,0] for pacmanpp
0272     const float t0 = -( o.x*sinPhi0 + o.y*(-cosPhi0) )/ ( d.x*sinPhi0 + d.y*(-cosPhi0) ) ;  // -o.x/d.x
0273     const float x0 = o.x+t0*d.x ;
0274     const float y0 = o.y+t0*d.y ;
0275     const float s0x =  cosPhi0*x0 + sinPhi0*y0 ;
0276 
0277 #ifdef DEBUG
0278     const float s0y = -sinPhi0*x0 + cosPhi0*y0 ;
0279     printf("//intersect_leaf_phicut  t0 %10.4f  (x0,y0) (%10.4f, %10.4f)   (s0x,s0y) (%10.4g, %10.4g) \n", t0, x0,y0, s0x,s0y );
0280 #endif
0281 
0282     // dotproduct with outward normal [ -sinPhi1, cosPhi1, 0 ]  of phi1 plane    eg [0,1,0] for pacmanpp
0283     const float t1 = -( o.x*(-sinPhi1) + o.y*cosPhi1 )/( d.x*(-sinPhi1) + d.y*cosPhi1 ) ;
0284     const float x1 = o.x+t1*d.x ;
0285     const float y1 = o.y+t1*d.y ;
0286     const float s1x =  cosPhi1*x1 + sinPhi1*y1 ;
0287 
0288 #ifdef DEBUG
0289     const float s1y = -sinPhi1*x1 + cosPhi1*y1 ;
0290     printf("//intersect_leaf_phicut  t1 %10.4f  (x1,y1) (%10.4f, %10.4f)   (s1x,s1y) (%10.4f, %10.4f) \n", t1, x1,y1, s1x,s1y );
0291 #endif
0292 
0293     const float epsilon = 1e-4f ;
0294     bool safezone = ( fabsf(s0x) > epsilon && fabsf(s1x) > epsilon ) ;
0295     const float t0c = ( s0x >= 0.f && t0 > t_min) ? t0 : RT_DEFAULT_MAX ;
0296     const float t1c = ( s1x >= 0.f && t1 > t_min) ? t1 : RT_DEFAULT_MAX ;
0297     const float t_cand = safezone ? fminf( t0c, t1c ) : ( s0x >= 0.f ? t0c : t1c ) ;
0298 
0299     valid_isect = t_cand > t_min && t_cand <  RT_DEFAULT_MAX ;
0300 
0301 
0302     /*
0303        0. s0x s1x are -phi0 and -phi1 rotated xprime coordinates of intersect positions
0304           it might be tempting to simply match signs of intersect x or y
0305           but that does not work in general for any phi0 phi1 angle
0306 
0307           * eg pacmanpp +x   x1:-inf y1:nan s1x:nan s1y:nan
0308 
0309        1. s0x s1x can become nan (from t0 or t1 inf or -inf and inf*0.f->nan or -inf*0.f->nan )
0310           and comparisons with nan always give false. So arrange direction of the comparisons
0311           such that true yields the root in order that a false from nan comparison does what
0312           is needed and disqualifies the root.
0313 
0314        2. t0, t1 can be -inf/inf/nan so must check them against t_min individually
0315 
0316           * when t0 OR t1 is inf the s0x OR s1x will be nan
0317 
0318        3. must prevent nan from one root infecting the other, by quashing it before root comparison
0319 
0320        4. close to knife edge numerical imprecision makes inconsistencies between sign comparisons on s0x and s1x likely
0321           (such inconsistencies cause seams lines of MISS-es along the knife edge between the phi planes)
0322 
0323           * having unexpected holes in geometry is more of a problem than having normals at edge swap between planes
0324           * hence : prevent inconsistent decisions by making just one decision over which plane is hit when at knife edge
0325 
0326           * TODO: current approach makes the knife edge decision based on s0x (an arbitrary choice)
0327             it would be better to choose based on the s0x or s1x with the larger absolute value,
0328             but need to fish for which is which to pick t0c or t1c::
0329 
0330                  fmaxf(abs(s0x), abs(s1x)) == abs(s0x) ? t0c : t1c
0331 
0332     */
0333 
0334 
0335 #ifdef DEBUG
0336     printf("//intersect_leaf_phicut t0c %10.4f t1c %10.4f safezone %d t_cand %10.4f valid_isect %d  unbounded_exit %d \n", t0c, t1c, safezone, t_cand, valid_isect, unbounded_exit );
0337 #endif
0338 
0339     if( valid_isect )
0340     {
0341         isect.x = t_cand == t1 ? -sinPhi1 :  sinPhi0 ;
0342         isect.y = t_cand == t1 ?  cosPhi1 : -cosPhi0 ;
0343         isect.z = 0.f ;
0344         isect.w = t_cand ;
0345     }
0346     else if( unbounded_exit )
0347     {
0348         isect.y = -isect.y ;  // -0.f signflip signalling that can promote MISS to EXIT at infinity
0349     }
0350 }
0351 
0352 
0353 
0354 
0355 
0356 
0357 LEAF_FUNC
0358 void intersect_leaf_phicut_dev( bool& valid_isect, float4& isect, const quad& q0, const float t_min, const float3& o, const float3& d )
0359 {
0360     const float& cosPhi0 = q0.f.x ;
0361     const float& sinPhi0 = q0.f.y ;
0362     const float& cosPhi1 = q0.f.z ;
0363     const float& sinPhi1 = q0.f.w ;
0364 
0365     const float PQ = cosPhi0*sinPhi1 - cosPhi1*sinPhi0  ;  // PQ +ve => angle < pi,   PQ -ve => angle > pi
0366     bool zpure = d.x == 0.f && d.y == 0.f ;
0367     const float PR = cosPhi0*(zpure ? o.y : d.y) - (zpure ? o.x : d.x)*sinPhi0  ;          // PR and QR +ve/-ve selects the "side of the line"
0368     const float QR = cosPhi1*(zpure ? o.y : d.y) - (zpure ? o.x : d.x)*sinPhi1  ;
0369     bool unbounded_exit = PQ >= 0.f ? ( PR >= 0.f && QR <= 0.f ) : ( PR >= 0.f || QR <= 0.f )  ;
0370 
0371 #ifdef DEBUG
0372     printf("//intersect_leaf_phicut q0.f  (%10.4f %10.4f %10.4f %10.4f) %s t_min %10.4f \n" , q0.f.x, q0.f.y, q0.f.z, q0.f.w, "cosPhi0/sinPhi0/cosPhi1/sinPhi1", t_min  ) ;
0373     printf("//intersect_leaf_phicut d.xyz ( %10.4f %10.4f %10.4f ) zpure %d \n", d.x, d.y, d.z, zpure  );
0374     printf("//intersect_leaf_phicut PQ %10.4f cosPhi0*sinPhi1 - cosPhi1*sinPhi0 : +ve:angle less than pi, -ve:angle greater than pi \n", PQ );
0375     printf("//intersect_leaf_phicut PR %10.4f cosPhi0*d.y - d.x*sinPhi0 \n", PR );
0376     printf("//intersect_leaf_phicut QR %10.4f cosPhi1*d.y - d.x*sinPhi1 \n", QR );
0377     printf("//intersect_leaf_phicut unbounded_exit %d \n", unbounded_exit );
0378 #endif
0379 
0380     // setting t values to t_min disqualifies that intersect
0381     // dot products with normal0  [ sinPhi0, -cosPhi0, 0.f ]
0382 
0383     /*
0384     // Old careful approach works, but unneeded rotation flops for checking the side,
0385     // can just check signbit of x intersect matches the sigbit of the cosPhi
0386 
0387     float d_n0 = d.x*sinPhi0 + d.y*(-cosPhi0) ;
0388     float o_n0 = o.x*sinPhi0 + o.y*(-cosPhi0) ;
0389     float t0 = d_n0 == 0.f ? t_min : -o_n0/d_n0 ;                 // perhaps could avoid the check if side0 became -inf ?
0390 
0391     float side0 = o.x*cosPhi0 + o.y*sinPhi0 + ( d.x*cosPhi0 + d.y*sinPhi0 )*t0 ;
0392     if(side0 < 0.f) t0 = t_min ;
0393 
0394     float d_n1 = d.x*(-sinPhi1) + d.y*cosPhi1 ;
0395     float o_n1 = o.x*(-sinPhi1) + o.y*cosPhi1 ;
0396     float t1 = d_n1 == 0.f ? t_min : -o_n1/d_n1 ;
0397 
0398     float side1 = o.x*cosPhi1 + o.y*sinPhi1 + ( d.x*cosPhi1 + d.y*sinPhi1 )*t1 ;
0399     if(side1 < 0.f) t1 = t_min ;
0400     */
0401 
0402 
0403     /*
0404 
0405     // Medium careful approach works, accepting t0 and t1 becoming infinity for some ray directions
0406     // but still carefully ordering the roots.
0407     //
0408     float t0 = -(o.x*sinPhi0 + o.y*(-cosPhi0))/ ( d.x*sinPhi0 + d.y*(-cosPhi0) ) ;
0409     if(signbit(o.x+t0*d.x) != signbit(cosPhi0)) t0 = t_min ;
0410 
0411     float t1 = -(o.x*(-sinPhi1) + o.y*cosPhi1)/(d.x*(-sinPhi1) + d.y*cosPhi1 ) ;
0412     if(signbit(o.x+t1*d.x) != signbit(cosPhi1)) t1 = t_min ;
0413 
0414     float t_near = fminf(t0,t1);  // order the intersects
0415     float t_far  = fmaxf(t0,t1);
0416     float t_cand = t_near > t_min  ?  t_near : ( t_far > t_min ? t_far : t_min ) ;
0417     bool valid_intersect = t_cand > t_min ;
0418 
0419    */
0420 
0421 
0422     //  Lucas wild abandon approach gives bad intersects
0423     //  Select isect in the bad region::
0424     //
0425     //      SPHI=0.24,1.76 ./csg_geochain.sh ana
0426     //      SPHI=0.24,1.76 IXYZ=4,4,0 ./csg_geochain.sh ana
0427     //
0428     //   Q: why ? its little different to the above which has no problem
0429     //   A: bug t_cand < t_min which should be t_cand <= t_min
0430     //
0431     //   https://tavianator.com/2011/ray_box.html
0432     //    0*inf = nan
0433     //
0434 
0435 
0436     float t_cand = -( o.x*sinPhi0 + o.y*(-cosPhi0) )/ ( d.x*sinPhi0 + d.y*(-cosPhi0) ) ;
0437     // o on phi0 line => t_cand.0  -0.f
0438     // o on phi0 line, d along line => t_cand.0  0/0 = nan
0439 
0440 #ifdef DEBUG
0441     //printf("//intersect_leaf_phicut ( o.x*sinPhi0 + o.y*(-cosPhi0)    %10.4f \n", ( o.x*sinPhi0 + o.y*(-cosPhi0)) );
0442     //printf("//intersect_leaf_phicut ( d.x*sinPhi0 + d.y*(-cosPhi0)    %10.4f \n", ( d.x*sinPhi0 + d.y*(-cosPhi0)) );
0443     printf("//intersect_leaf_phicut t_cand.0 %10.4f t_min %10.4f \n", t_cand, t_min  );
0444     //printf("//intersect_leaf_phicut o.x+t_cand*d.x  %10.4f \n", o.x+t_cand*d.x );
0445     //printf("//intersect_leaf_phicut signbit(o.x+t_cand*d.x)  %d \n", signbit(o.x+t_cand*d.x) );
0446     //printf("//intersect_leaf_phicut signbit(o.x+t_cand*d.x) != signbit(cosPhi0)  %d \n", signbit(o.x+t_cand*d.x) != signbit(cosPhi0) );
0447     //printf("//intersect_leaf_phicut t_cand.0 < t_min : %d \n", t_cand < t_min );
0448     //printf("//intersect_leaf_phicut t_cand.0 > t_min : %d \n", t_cand > t_min );
0449     //printf("//intersect_leaf_phicut t_cand.0 == t_min : %d \n", t_cand == t_min );
0450     //printf("//intersect_leaf_phicut signbit(o.x+t_cand*d.x) != signbit(cosPhi0) || t_cand.0 < t_min : %d \n",  signbit(o.x+t_cand*d.x) != signbit(cosPhi0) || t_cand < t_min );
0451 
0452     {
0453         /*
0454         in XZ projection testing deciding based on ipos_x yields spurious due to ipos_x going slightly wrong sign
0455         switching to ipos_y avoids the problem : WHY ?
0456         */
0457         float ipos_x = (o.x+t_cand*d.x) ;
0458         float ipos_y = (o.y+t_cand*d.y) ;
0459         float ipos_z = (o.z+t_cand*d.z) ;
0460         bool x_wrong_side = ipos_x*cosPhi0 < 0.f ;
0461         bool y_wrong_side = ipos_y*sinPhi0 < 0.f ;
0462         bool too_close =  t_cand <= t_min ;
0463         printf("//intersect_leaf_phicut ipos_x %10.4f ipos_x*1e6f %10.4f  cosPhi0 %10.4f  x_wrong_side %d \n", ipos_x, ipos_x*1e6f, cosPhi0, x_wrong_side );
0464         printf("//intersect_leaf_phicut ipos_y %10.4f ipos_y*1e6f %10.4f  sinPhi0 %10.4f  y_wrong_side %d \n", ipos_y, ipos_y*1e6f, sinPhi0, y_wrong_side );
0465         printf("//intersect_leaf_phicut ipos_z %10.4f ipos_z*1e6f %10.4f                                  \n", ipos_z, ipos_z*1e6f );
0466         printf("//intersect_leaf_phicut t_cand %10.4f t_min %10.4f too_close %d \n", t_cand, t_min, too_close );
0467     }
0468 
0469 
0470 /*
0471 rays along the phi0 line get t_cand nan from 0/0
0472 BUT as all comparisons with nan yield false the below
0473 also note that signbit(nan) == true which would also invalidate +ve cosPhi0
0474 */
0475 #endif
0476 
0477     if((o.y+t_cand*d.y)*sinPhi0 < 0.f || t_cand <= t_min ) t_cand = RT_DEFAULT_MAX ;
0478 
0479     //if((o.x+t_cand*d.x+1e-4)*cosPhi0 < 0.f || t_cand <= t_min ) t_cand = RT_DEFAULT_MAX ;
0480     //if((o.x+t_cand*d.x)*cosPhi0 < 0.f || t_cand < t_min ) t_cand = RT_DEFAULT_MAX ;         // t_cand < t_min YIELDS SPURIOUS INTERSECTS
0481 /*
0482 Disqualify wrong side or too close
0483                                         ^^^^^^^^^^^^^^^^^^
0484 
0485 1. must be  t_cand <= t_min to avoid spurious intersects in CSG combination
0486    for rays starting on phi0 line which have t_cand -0.f
0487 
0488    * this is because NOT("t_cand > t_min") -> "t_cand <= t_min"
0489 
0490 2. At first glance using signbit seems better, but signbit(nan) == true
0491    which means that the t_cand nan does not propagate to the comparison
0492    so the invalidation will only happen for +ve cosPhi0
0493 
0494    signbit(o.x+t_cand*d.x) != signbit(cosPhi0)
0495 
0496    * actually not invalidating here doesnt matter as the t_cand nan will
0497      simply end up giving a false for valid_intersect
0498 
0499    * product (o.x+t_cand*d.x)*cosPhi0  will be nan for t_cand nan and
0500      all comparisons with nan give false so t_cand will stay nan here
0501 
0502    * all comparisons with nan always returns false
0503      but that does not kill the entire short circuit OR
0504      (see sysrap/tests/signbitTest.cc)
0505 
0506 3. at first glance may think could make the disqualification based only
0507    x or y of intersect BUT that will nor work for all phi0 phi1 angles.
0508    So in general it is necessary to rotate the intersect (x,y) positions
0509    by -phi0 and -phi1 to place it back on the x line.
0510 
0511 
0512 */
0513 
0514 #ifdef DEBUG
0515     printf("//intersect_leaf_phicut t_cand.1 %10.4f \n", t_cand );
0516 #endif
0517 
0518     const float t1 = -( o.x*(-sinPhi1) + o.y*cosPhi1 )/( d.x*(-sinPhi1) + d.y*cosPhi1 ) ;
0519     if((o.x + t1*d.x)*cosPhi1 > 0.f && t1 > t_min ) t_cand = fminf( t1, t_cand );
0520 
0521     valid_isect = t_cand > t_min && t_cand < RT_DEFAULT_MAX ;
0522 
0523 #ifdef DEBUG
0524     //printf("//intersect_leaf_phicut t1_num  ( o.x*(-sinPhi1) + o.y*cosPhi1 )  : %10.4f \n", ( o.x*(-sinPhi1) + o.y*cosPhi1 ) );
0525     //printf("//intersect_leaf_phicut t1_den  ( d.x*(-sinPhi1) + d.y*cosPhi1 )  : %10.4f \n", ( d.x*(-sinPhi1) + d.y*cosPhi1 ) );
0526     //printf("//intersect_leaf_phicut t1      %10.4f \n", t1 );
0527     //printf("//intersect_leaf_phicut  signbit(o.x + t1*d.x) == signbit(cosPhi1) && t1 > t_min  : %d \n", signbit(o.x + t1*d.x) == signbit(cosPhi1) && t1 > t_min );
0528     printf("//intersect_leaf_phicut t_cand.2 %10.4f valid_isect %d \n", t_cand, valid_isect  );
0529 #endif
0530 
0531     if( valid_isect )
0532     {
0533         isect.x = t_cand == t1 ? -sinPhi1 :  sinPhi0 ;
0534         isect.y = t_cand == t1 ?  cosPhi1 : -cosPhi0 ;
0535         isect.z = 0.f ;
0536         isect.w = t_cand ;
0537     }
0538     else if( unbounded_exit )
0539     {
0540         isect.y = -isect.y ;  // -0.f signflip signalling that can promote MISS to EXIT at infinity
0541     }
0542 }
0543 
0544 /**
0545 intersect_leaf_phicut_simple
0546 -----------------------------
0547 
0548 Translation of phicut.py
0549 
0550 The solid lines below denote the valid half planes.
0551 For the infinite plane intersection points [0] [1] (0) (1)
0552 
0553 
0554                                p1 [cosPhi1, sinPhi1]
0555                                 /
0556                                /
0557                              (1)
0558                              /:
0559                             / :
0560                            /  :
0561            . . .{0}. . .  +--(0)--------- p0   [ cosPhi0, sinPhi0 ]
0562                  :       .    :
0563                  :      .     :
0564                  :     .      :
0565                  :    .       :
0566                  :   .        :
0567                  :  .         :
0568                  : .          :
0569                  :.           :
0570                 {1}           :
0571                 .:            :
0572                . :            :
0573               .  :            :
0574                  o            o
0575 
0576 **/
0577 
0578 
0579 
0580 LEAF_FUNC
0581 void intersect_leaf_phicut_simple( bool& valid_isect, float4& isect, const quad& q0, const float t_min, const float3& o, const float3& d )
0582 {
0583     const float& cosPhi0 = q0.f.x ;
0584     const float& sinPhi0 = q0.f.y ;
0585     const float& cosPhi1 = q0.f.z ;
0586     const float& sinPhi1 = q0.f.w ;
0587 
0588     // dot products for direction and origin with::
0589     // normal0 [  sinPhi0, -cosPhi0, 0. ]
0590     // normal1 [ -sinPhi1,  cosPhi1, 0. ]
0591 
0592     float d_n0 = d.x*sinPhi0    + d.y*(-cosPhi0) ;
0593     float d_n1 = d.x*(-sinPhi1) + d.y*(cosPhi1) ;
0594     float o_n0 = o.x*sinPhi0    + o.y*(-cosPhi0) ;
0595     float o_n1 = o.x*(-sinPhi1) + o.y*(cosPhi1) ;
0596     float t0 = d_n0 == 0.f ? t_min : -o_n0/d_n0  ; // intersect distance to Plane0
0597     float t1 = d_n1 == 0.f ? t_min : -o_n1/d_n1  ; // intersect distance to Plane1
0598 
0599     const float PR = d_n0 == 0.f ? -o_n0 : -d_n0 ;
0600     const float QR = d_n1 == 0.f ? -o_n1 : -d_n1 ;
0601     const float PQ = cosPhi0*sinPhi1 - cosPhi1*sinPhi0  ;  // PQ +ve => angle < pi : wedge,   PQ -ve => angle > pi : pacman
0602     bool unbounded_exit = PQ >= 0.f ? ( PR >= 0.f && QR <= 0.f ) : ( PR >= 0.f || QR <= 0.f )  ;
0603 
0604     // Disqualify intersects with the wrong halves of the planes.
0605     // When the dot product of the hit point with the unit vector
0606     // within the plane (in valid direction) is positive it means the
0607     // intersect is with the valid half of the plane.  When negative
0608     // are intersecting with the non-allowed half of the plane.
0609     //
0610     // sideI = (ray_origin + ray_direction * tI) ยท (cosPhiI, sinPhiI)    I={0,1}
0611 
0612     float side0 = o.x*cosPhi0 + o.y*sinPhi0 + ( d.x*cosPhi0 + d.y*sinPhi0 )*t0 ;
0613     float side1 = o.x*cosPhi1 + o.y*sinPhi1 + ( d.x*cosPhi1 + d.y*sinPhi1 )*t1 ;
0614     if(side0 < 0.f) t0 = t_min ;
0615     if(side1 < 0.f) t1 = t_min ;
0616     float t_near = fminf(t0,t1);
0617     float t_far  = fmaxf(t0,t1);
0618     float t_cand = t_near > t_min  ?  t_near : ( t_far > t_min ? t_far : t_min ) ;
0619 
0620 
0621     valid_isect = t_cand > t_min ;
0622     if(valid_isect)
0623     {
0624         isect.x = t_cand == t1 ? -sinPhi1 : sinPhi0 ;
0625         isect.y = t_cand == t1 ?  cosPhi1 : -cosPhi0  ;
0626         isect.z = 0.f ;
0627         isect.w = t_cand ;
0628     }
0629     else if( unbounded_exit )
0630     {
0631         isect.y = -isect.y ;  // -0.f signflip signalling that can promote MISS to EXIT at infinity
0632     }
0633 
0634 
0635 }
0636 
0637 
0638 
0639 
0640 
0641 
0642 
0643 
0644 
0645 
0646 
0647 
0648 
0649 
0650 
0651 
0652 
0653 
0654 
0655 
0656 
0657 
0658 
0659 
0660 
0661 
0662 
0663 
0664 
0665 
0666 
0667 
0668 
0669 
0670 
0671 
0672 
0673 
0674 /**
0675 intersect_leaf_phicut_lucas
0676 ----------------------------
0677 
0678 Simon's comments on intersect_leaf_phicut_lucas
0679 
0680 1. code must stay readable
0681 
0682 2. scrunching up code to make it shorter, does not make it faster.
0683    The only way to tell if its faster is to measure it in the context
0684    in which you want to use it.
0685 
0686 3. code you write is not what gets run : the compiler performs lots of optimizations on it first
0687 
0688    * you should of course help the compiler to know what your intentions are
0689      using const and references to avoid needless copies while still retaining readability
0690 
0691 4. for rays starting inside the planes your t_cand and t1 will get divisions by zero and infinities
0692 
0693    * that is not necessarily a problem : but you have to think thru what will happen and test the cases
0694 
0695 5. disqualification of candidates needs to use t_cand > t_min as for this shape to work
0696    in CSG combination it must perform as expected as t_min is varied
0697 
0698    * using the below commandline with a test geometry using phicut will
0699      produce renders that you can use to see how it performs as t_min is varied::
0700 
0701          EYE=1,1,1 TMIN=0.5 ./cxr_geochain.sh
0702 
0703    * the required behaviour is for the t_min to cut away the solid in an expanding sphere
0704      shape when using perspective projection (plane when using parallel projection)
0705 
0706 
0707             |        /
0708             |       /
0709             |      +
0710             |     /:
0711             |    / :
0712             |   /  :
0713             | [1]--:- - - - - 0
0714             | /:   :    accepted sign(xi) == sign(cosPhi1)
0715             |/ :   :
0716    . . . . .O--:-- +-----+  X
0717            .:  :xi :cosPhi1
0718           . :
0719         *1*- - - - - - - - - 0     disqualified "other half" intersect
0720         .   :      (-ve x)*(+ve cosPhi1 ) => -ve => disqualified
0721        .    :
0722       .     :
0723      .      :
0724 
0725 **/
0726 
0727 LEAF_FUNC
0728 void intersect_leaf_phicut_lucas(bool& valid_isect, float4& isect, const quad& angles, const float& t_min, const float3& ray_origin, const float3& ray_direction)
0729 {   //for future reference: angles.f has: .x = cos0, .y = sin0, .z = cos1, .w = sin1
0730 
0731     float t_cand = -(angles.f.y * ray_origin.x - angles.f.x * ray_origin.y) / (angles.f.y * ray_direction.x - angles.f.x * ray_direction.y);
0732     //using t_cand saves unnecessary definition (t_cand = -( Norm0 * Or ) / ( Norm0 * Dir ), ratio of magnitudes in the direction of plane)
0733 
0734     if (t_cand * angles.f.x * ray_direction.x  + angles.f.x * ray_origin.x < 0.f || t_cand <= t_min)
0735         t_cand = RT_DEFAULT_MAX; // disqualify t_cand for wrong side
0736 
0737     const float t1 = -(-angles.f.w * ray_origin.x + angles.f.z * ray_origin.y) / (-angles.f.w * ray_direction.x + angles.f.z * ray_direction.y);
0738     if (t1 * angles.f.z * ray_direction.x + angles.f.z * ray_origin.x > 0.f && t1 > t_min)
0739         t_cand = fminf(t1, t_cand);
0740 
0741      //   angles.f.z * ( t1 * ray_direction.x + ray_origin.x )
0742      //   cosPhi1 * (intersection_x) > 0
0743 
0744 
0745     valid_isect = t_cand < RT_DEFAULT_MAX;
0746     if (valid_isect)
0747     {
0748         isect.x = t_cand == t1 ? -angles.f.w: angles.f.y;
0749         isect.y = t_cand == t1 ? angles.f.z : -angles.f.x;
0750         isect.z = 0.f;
0751         isect.w = t_cand;
0752     }
0753 }
0754 
0755 
0756