plane.c

Go to the documentation of this file.
00001 /*                         P L A N E . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** \addtogroup plane */
00023 /*@{*/
00024 /** @file plane.c
00025  * @brief
00026  *  Some useful routines for dealing with planes and lines.
00027  *
00028  *  @author
00029  *      Michael John Muuss
00030  *
00031  *  @par Source
00032  *      SECAD/VLD Computing Consortium, Bldg 394
00033  *@n    The U. S. Army Ballistic Research Laboratory
00034  *@n    Aberdeen Proving Ground, Maryland  21005
00035  *
00036  */
00037 
00038 #ifndef lint
00039 static const char RCSplane[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/libbn/plane.c,v 14.10 2006/09/04 04:42:40 lbutler Exp $ (BRL)";
00040 #endif
00041 
00042 #include "common.h"
00043 
00044 #include <stdio.h>
00045 #include <math.h>
00046 #ifdef HAVE_STRING_H
00047 #include <string.h>
00048 #else
00049 #include <strings.h>
00050 #endif
00051 
00052 #include "machine.h"
00053 #include "bu.h"
00054 #include "vmath.h"
00055 #include "bn.h"
00056 
00057 #define         UNIT_SQ_TOL     1.0e-13
00058 
00059 /**
00060  *                      B N _ D I S T _ P T 3 _ P T 3
00061  * @brief
00062  *  Returns distance between two points.
00063  */
00064 double
00065 bn_dist_pt3_pt3(const fastf_t *a, const fastf_t *b)
00066 {
00067         vect_t  diff;
00068 
00069         VSUB2( diff, a, b );
00070         return MAGNITUDE( diff );
00071 }
00072 
00073 /**
00074  *                      B N _ P T 3 _ P T 3 _ E Q U A L
00075  *
00076  *  
00077  *  @return     1       if the two points are equal, within the tolerance
00078  *  @return     0       if the two points are not "the same"
00079  */
00080 int
00081 bn_pt3_pt3_equal(const fastf_t *a, const fastf_t *b, const struct bn_tol *tol)
00082 {
00083         vect_t  diff;
00084 
00085         BN_CK_TOL(tol);
00086         VSUB2( diff, b, a );
00087         if( MAGSQ( diff ) < tol->dist_sq )  return 1;
00088         return 0;
00089 }
00090 
00091 /**
00092  *                      B N _ P T 2 _ P T 2 _ E Q U A L
00093  *
00094  *
00095  *  @return     1       if the two points are equal, within the tolerance
00096  *  @return     0       if the two points are not "the same"
00097  */
00098 int
00099 bn_pt2_pt2_equal(const fastf_t *a, const fastf_t *b, const struct bn_tol *tol)
00100 {
00101         vect_t  diff;
00102 
00103         BN_CK_TOL(tol);
00104         VSUB2_2D( diff, b, a );
00105         if( MAGSQ_2D( diff ) < tol->dist_sq )  return 1;
00106         return 0;
00107 }
00108 
00109 /**
00110  *                      B N _ 3 P T S _ C O L L I N E A R
00111  * @brief
00112  *  Check to see if three points are collinear.
00113  *
00114  *  The algorithm is designed to work properly regardless of the
00115  *  order in which the points are provided.
00116  *
00117  *  @return     1       If 3 points are collinear
00118  *  @return     0       If they are not
00119  */
00120 int
00121 bn_3pts_collinear(fastf_t *a, fastf_t *b, fastf_t *c, const struct bn_tol *tol)
00122 {
00123         fastf_t mag_ab, mag_bc, mag_ca, max_len, dist_sq;
00124         fastf_t cos_a, cos_b, cos_c;
00125         vect_t  ab, bc, ca;
00126         int max_edge_no;
00127 
00128         VSUB2(ab, b, a);
00129         VSUB2(bc, c, b);
00130         VSUB2(ca, a, c);
00131         mag_ab = MAGNITUDE(ab);
00132         mag_bc = MAGNITUDE(bc);
00133         mag_ca = MAGNITUDE(ca);
00134 
00135         /* find longest edge */
00136         max_len = mag_ab;
00137         max_edge_no = 1;
00138 
00139         if( mag_bc > max_len )
00140         {
00141                 max_len = mag_bc;
00142                 max_edge_no = 2;
00143         }
00144 
00145         if( mag_ca > max_len )
00146         {
00147                 max_len = mag_ca;
00148                 max_edge_no = 3;
00149         }
00150 
00151         switch( max_edge_no )
00152         {
00153                 default:
00154                 case 1:
00155                         cos_b = (-VDOT( ab , bc ))/(mag_ab * mag_bc );
00156                         dist_sq = mag_bc*mag_bc*( 1.0 - cos_b*cos_b);
00157                         break;
00158                 case 2:
00159                         cos_c = (-VDOT( bc , ca ))/(mag_bc * mag_ca );
00160                         dist_sq = mag_ca*mag_ca*(1.0 - cos_c*cos_c);
00161                         break;
00162                 case 3:
00163                         cos_a = (-VDOT( ca , ab ))/(mag_ca * mag_ab );
00164                         dist_sq = mag_ab*mag_ab*(1.0 - cos_a*cos_a);
00165                         break;
00166         }
00167 
00168         if( dist_sq <= tol->dist_sq )
00169                 return( 1 );
00170         else
00171                 return( 0 );
00172 }
00173 
00174 
00175 /**
00176  *                      B N _ 3 P T S _ D I S T I N C T
00177  *
00178  *  Check to see if three points are all distinct, i.e.,
00179  *  ensure that there is at least sqrt(dist_tol_sq) distance
00180  *  between every pair of points.
00181  *
00182  *
00183  * @return      1   If all three points are distinct
00184  * @return      0   If two or more points are closer together than dist_tol_sq
00185  */
00186 int
00187 bn_3pts_distinct(const fastf_t *a, const fastf_t *b, const fastf_t *c, const struct bn_tol *tol)
00188 {
00189         vect_t  B_A;
00190         vect_t  C_A;
00191         vect_t  C_B;
00192 
00193         BN_CK_TOL(tol);
00194         VSUB2( B_A, b, a );
00195         if( MAGSQ( B_A ) <= tol->dist_sq )  return(0);
00196         VSUB2( C_A, c, a );
00197         if( MAGSQ( C_A ) <= tol->dist_sq )  return(0);
00198         VSUB2( C_B, c, b );
00199         if( MAGSQ( C_B ) <= tol->dist_sq )  return(0);
00200         return(1);
00201 }
00202 
00203 /**
00204  *                      B N _ M K _ P L A N E _ 3 P T S
00205  *
00206  *  Find the equation of a plane that contains three points.
00207  *  Note that normal vector created is expected to point out (see vmath.h),
00208  *  so the vector from A to C had better be counter-clockwise
00209  *  (about the point A) from the vector from A to B.
00210  *  This follows the BRL-CAD outward-pointing normal convention, and the
00211  *  right-hand rule for cross products.
00212  *
00213 @verbatim
00214  *
00215  *                      C
00216  *                      *
00217  *                      |\
00218  *                      | \
00219  *         ^     N      |  \
00220  *         |      \     |   \
00221  *         |       \    |    \
00222  *         |C-A     \   |     \
00223  *         |         \  |      \
00224  *         |          \ |       \
00225  *                     \|        \
00226  *                      *---------*
00227  *                      A         B
00228  *                         ----->
00229  *                          B-A
00230 @endverbatim
00231  *
00232  *  If the points are given in the order A B C (eg, *counter*-clockwise),
00233  *  then the outward pointing surface normal N = (B-A) x (C-A).
00234  *
00235  *
00236  *  @return      0      OK
00237  *  @return     -1      Failure.  At least two of the points were not distinct,
00238  *              or all three were colinear.
00239  *
00240  * @param[out]  plane   The plane equation is stored here.
00241  * @param[in]   a       point 1
00242  * @param[in]   b       point 2
00243  * @param[in]   c       point 3
00244  * @param[in]   tol     Tolerance values for doing calcualtion
00245  */
00246 int
00247 bn_mk_plane_3pts(fastf_t *plane,
00248                  const fastf_t *a,
00249                  const fastf_t *b,
00250                  const fastf_t *c,
00251                  const struct bn_tol *tol)
00252 {
00253         vect_t  B_A;
00254         vect_t  C_A;
00255         vect_t  C_B;
00256         register fastf_t mag;
00257 
00258         BN_CK_TOL(tol);
00259 
00260         VSUB2( B_A, b, a );
00261         if( MAGSQ( B_A ) <= tol->dist_sq )  return(-1);
00262         VSUB2( C_A, c, a );
00263         if( MAGSQ( C_A ) <= tol->dist_sq )  return(-1);
00264         VSUB2( C_B, c, b );
00265         if( MAGSQ( C_B ) <= tol->dist_sq )  return(-1);
00266 
00267         VCROSS( plane, B_A, C_A );
00268 
00269         /* Ensure unit length normal */
00270         if( (mag = MAGNITUDE(plane)) <= SMALL_FASTF )
00271                 return(-1);     /* FAIL */
00272         mag = 1/mag;
00273         VSCALE( plane, plane, mag );
00274 
00275         /* Find distance from the origin to the plane */
00276         /* XXX Should do with pt that has smallest magnitude (closest to origin) */
00277         plane[3] = VDOT( plane, a );
00278 
00279 #if 0
00280         /* Check to be sure that angle between A-Origin and N vect < 89 degrees */
00281         /* XXX Could complain for pts on axis-aligned plane, far from origin */
00282         mag = MAGSQ(a);
00283         if( mag > tol->dist_sq )  {
00284                 /* cos(89 degrees) = 0.017452406, reciprocal is 57.29 */
00285                 if( plane[3]/sqrt(mag) < 0.017452406 )  {
00286                         bu_log("bn_mk_plane_3pts() WARNING: plane[3] value is suspect\n");
00287                 }
00288         }
00289 #endif
00290         return(0);              /* OK */
00291 }
00292 
00293 /**
00294  *                      B N _ M K P O I N T _ 3 P L A N E S
00295  *@brief
00296  *  Given the description of three planes, compute the point of intersection,
00297  *  if any.
00298  *
00299  *  Find the solution to a system of three equations in three unknowns:
00300 @verbatim
00301  *      Px * Ax + Py * Ay + Pz * Az = -A3;
00302  *      Px * Bx + Py * By + Pz * Bz = -B3;
00303  *      Px * Cx + Py * Cy + Pz * Cz = -C3;
00304  *
00305  *  or
00306  *
00307  *      [ Ax  Ay  Az ]   [ Px ]   [ -A3 ]
00308  *      [ Bx  By  Bz ] * [ Py ] = [ -B3 ]
00309  *      [ Cx  Cy  Cz ]   [ Pz ]   [ -C3 ]
00310  *
00311 @endverbatim
00312  *
00313  *  
00314  * @return       0      OK
00315  * @return      -1      Failure.  Intersection is a line or plane.
00316  *
00317  * @param[out] pt       The point of intersection is stored here.
00318  * @param       a       plane 1
00319  * @param       b       plane 2
00320  * @param       c       plane 3
00321  */
00322 int
00323 bn_mkpoint_3planes(fastf_t *pt, const fastf_t *a, const fastf_t *b, const fastf_t *c)
00324 {
00325         vect_t  v1, v2, v3;
00326         register fastf_t det;
00327 
00328         /* Find a vector perpendicular to both planes B and C */
00329         VCROSS( v1, b, c );
00330 
00331         /*  If that vector is perpendicular to A,
00332          *  then A is parallel to either B or C, and no intersection exists.
00333          *  This dot&cross product is the determinant of the matrix M.
00334          *  (I suspect there is some deep significance to this!)
00335          */
00336         det = VDOT( a, v1 );
00337         if( NEAR_ZERO( det, SMALL_FASTF ) )  return(-1);
00338 
00339         VCROSS( v2, a, c );
00340         VCROSS( v3, a, b );
00341 
00342         det = 1/det;
00343         pt[X] = det*(a[3]*v1[X] - b[3]*v2[X] + c[3]*v3[X]);
00344         pt[Y] = det*(a[3]*v1[Y] - b[3]*v2[Y] + c[3]*v3[Y]);
00345         pt[Z] = det*(a[3]*v1[Z] - b[3]*v2[Z] + c[3]*v3[Z]);
00346         return(0);
00347 }
00348 
00349 /**
00350  *                      B N _ 2 L I N E 3 _ C O L I N E A R
00351  * @brief
00352  *  Returns non-zero if the 3 lines are colinear to within tol->dist
00353  *  over the given distance range.
00354  *
00355  *  Range should be at least one model diameter for most applications.
00356  *  1e5 might be OK for a default for "vehicle sized" models.
00357  *
00358  *  The direction vectors do not need to be unit length.
00359  */
00360 int
00361 bn_2line3_colinear(const fastf_t *p1,
00362                    const fastf_t *d1,
00363                    const fastf_t *p2,
00364                    const fastf_t *d2,
00365                    double range,
00366                    const struct bn_tol *tol)
00367 {
00368         fastf_t         mag1;
00369         fastf_t         mag2;
00370         point_t         tail;
00371 
00372         BN_CK_TOL(tol);
00373 
00374         if( (mag1 = MAGNITUDE(d1)) < SMALL_FASTF )  bu_bomb("bn_2line3_colinear() mag1 zero\n");
00375         if( (mag2 = MAGNITUDE(d2)) < SMALL_FASTF )  bu_bomb("bn_2line3_colinear() mag2 zero\n");
00376 
00377         /* Impose a general angular tolerance to reject "obviously" non-parallel lines */
00378         /* tol->para and RT_DOT_TOL are too tight a tolerance.  0.1 is 5 degrees */
00379         if( fabs(VDOT(d1, d2)) < 0.9 * mag1 * mag2  )  goto fail;
00380 
00381         /* See if start points are within tolerance of other line */
00382         if( bn_distsq_line3_pt3( p1, d1, p2 ) > tol->dist_sq )  goto fail;
00383         if( bn_distsq_line3_pt3( p2, d2, p1 ) > tol->dist_sq )  goto fail;
00384 
00385         VJOIN1( tail, p1, range/mag1, d1 );
00386         if( bn_distsq_line3_pt3( p2, d2, tail ) > tol->dist_sq )  goto fail;
00387 
00388         VJOIN1( tail, p2, range/mag2, d2 );
00389         if( bn_distsq_line3_pt3( p1, d1, tail ) > tol->dist_sq )  goto fail;
00390 
00391         if( bu_debug & BU_DEBUG_MATH )  {
00392                 bu_log("bn_2line3colinear(range=%g) ret=1\n",range);
00393         }
00394         return 1;
00395 fail:
00396         if( bu_debug & BU_DEBUG_MATH )  {
00397                 bu_log("bn_2line3colinear(range=%g) ret=0\n",range);
00398         }
00399         return 0;
00400 }
00401 
00402 /**
00403  *                      B N _ I S E C T _ L I N E 3 _ P L A N E
00404  *
00405  *  Intersect an infinite line (specified in point and direction vector form)
00406  *  with a plane that has an outward pointing normal.
00407  *  The direction vector need not have unit length.
00408  *  The first three elements of the plane equation must form
00409  *      a unit lengh vector.
00410  *
00411  *
00412  * @return      -2      missed (ray is outside halfspace)
00413  * @return      -1      missed (ray is inside)
00414  * @return       0      line lies on plane
00415  * @return       1      hit (ray is entering halfspace)
00416  * @return       2      hit (ray is leaving)
00417  *
00418  * @param[out]  dist    set to the parametric distance of the intercept
00419  * @param[in]   pt      origin of ray
00420  * @param[in]   dir     direction of ray
00421  * @param[in]   plane   equation of plane
00422  * @param[in]   tol     tolerance values
00423  */
00424 int
00425 bn_isect_line3_plane(fastf_t *dist,
00426                      const fastf_t *pt,
00427                      const fastf_t *dir,
00428                      const fastf_t *plane,
00429                      const struct bn_tol *tol)
00430 {
00431         register fastf_t        slant_factor;
00432         register fastf_t        norm_dist;
00433         register fastf_t        dot;
00434         vect_t                  local_dir;
00435 
00436         BN_CK_TOL(tol);
00437 
00438         norm_dist = plane[3] - VDOT( plane, pt );
00439         slant_factor = VDOT( plane, dir );
00440         VMOVE( local_dir, dir )
00441         VUNITIZE( local_dir )
00442         dot = VDOT( plane, local_dir );
00443 
00444         if( slant_factor < -SMALL_FASTF && dot < -tol->perp  )  {
00445                 *dist = norm_dist/slant_factor;
00446                 return 1;                       /* HIT, entering */
00447         } else if( slant_factor > SMALL_FASTF && dot > tol->perp )  {
00448                 *dist = norm_dist/slant_factor;
00449                 return 2;                       /* HIT, leaving */
00450         }
00451 
00452         /*
00453          *  Ray is parallel to plane when dir.N == 0.
00454          */
00455         *dist = 0;              /* sanity */
00456         if( norm_dist < -tol->dist )
00457                 return -2;      /* missed, outside */
00458         if( norm_dist > tol->dist )
00459                 return -1;      /* missed, inside */
00460         return 0;               /* Ray lies in the plane */
00461 }
00462 
00463 /**
00464  *                      B N _ I S E C T _ 2 P L A N E S
00465  *@brief
00466  *  Given two planes, find the line of intersection between them,
00467  *  if one exists.
00468  *  The line of intersection is returned in parametric line
00469  *  (point & direction vector) form.
00470  *
00471  *  In order that all the geometry under consideration be in "front"
00472  *  of the ray, it is necessary to pass the minimum point of the model
00473  *  RPP.  If this convention is unnecessary, just pass (0,0,0) as rpp_min.
00474  *
00475  *
00476  * @return       0      OK, line of intersection stored in `pt' and `dir'.
00477  * @return      -1      FAIL, planes are identical (co-planar)
00478  * @return      -2      FAIL, planes are parallel and distinct
00479  * @return      -3      FAIL, unable to find line of intersection
00480  *
00481  * 
00482  * @param[out]  pt      Starting point of line of intersection
00483  * @param[out]  dir     Direction vector of line of intersection (unit length)
00484  * @param[in]   a       plane 1
00485  * @param[in]   b       plane 2
00486  * @param[in]   rpp_min minimum poit of model RPP
00487  * @param[in]   tol     tolerance values
00488  */
00489 int
00490 bn_isect_2planes(fastf_t *pt,
00491                  fastf_t *dir,
00492                  const fastf_t *a,
00493                  const fastf_t *b,
00494                  const fastf_t *rpp_min,
00495                  const struct bn_tol *tol)
00496 {
00497         LOCAL vect_t            abs_dir;
00498         LOCAL plane_t           pl;
00499         int                     i;
00500 
00501         if( (i = bn_coplanar( a, b, tol )) != 0 )  {
00502                 if( i > 0 )
00503                         return(-1);     /* FAIL -- coplanar */
00504                 return(-2);             /* FAIL -- parallel & distinct */
00505         }
00506 
00507         /* Direction vector for ray is perpendicular to both plane normals */
00508         VCROSS( dir, a, b );
00509         VUNITIZE( dir );                /* safety */
00510 
00511         /*
00512          *  Select an axis-aligned plane which has it's normal pointing
00513          *  along the same axis as the largest magnitude component of
00514          *  the direction vector.
00515          *  If the largest magnitude component is negative, reverse the
00516          *  direction vector, so that model is "in front" of start point.
00517          */
00518         abs_dir[X] = (dir[X] >= 0) ? dir[X] : (-dir[X]);
00519         abs_dir[Y] = (dir[Y] >= 0) ? dir[Y] : (-dir[Y]);
00520         abs_dir[Z] = (dir[Z] >= 0) ? dir[Z] : (-dir[Z]);
00521 
00522         if( abs_dir[X] >= abs_dir[Y] )  {
00523                 if( abs_dir[X] >= abs_dir[Z] )  {
00524                         VSET( pl, 1, 0, 0 );    /* X */
00525                         pl[3] = rpp_min[X];
00526                         if( dir[X] < 0 )  {
00527                                 VREVERSE( dir, dir );
00528                         }
00529                 } else {
00530                         VSET( pl, 0, 0, 1 );    /* Z */
00531                         pl[3] = rpp_min[Z];
00532                         if( dir[Z] < 0 )  {
00533                                 VREVERSE( dir, dir );
00534                         }
00535                 }
00536         } else {
00537                 if( abs_dir[Y] >= abs_dir[Z] )  {
00538                         VSET( pl, 0, 1, 0 );    /* Y */
00539                         pl[3] = rpp_min[Y];
00540                         if( dir[Y] < 0 )  {
00541                                 VREVERSE( dir, dir );
00542                         }
00543                 } else {
00544                         VSET( pl, 0, 0, 1 );    /* Z */
00545                         pl[3] = rpp_min[Z];
00546                         if( dir[Z] < 0 )  {
00547                                 VREVERSE( dir, dir );
00548                         }
00549                 }
00550         }
00551 
00552         /* Intersection of the 3 planes defines ray start point */
00553         if( bn_mkpoint_3planes( pt, pl, a, b ) < 0 )
00554                 return(-3);     /* FAIL -- no intersection */
00555 
00556         return(0);              /* OK */
00557 }
00558 
00559 /**
00560  *                      B N _ I S E C T _ L I N E 2 _ L I N E 2
00561  *
00562  *  Intersect two lines, each in given in parametric form:
00563 @verbatim
00564 
00565         X = P + t * D
00566    and
00567         X = A + u * C
00568 
00569 @endverbatim
00570  *  While the parametric form is usually used to denote a ray
00571  *  (ie, positive values of the parameter only), in this case
00572  *  the full line is considered.
00573  *
00574  *  The direction vectors C and D need not have unit length.
00575  *
00576  *  
00577  * @return      -1      no intersection, lines are parallel.
00578  * @return       0      lines are co-linear
00579  *@n                    dist[0] gives distance from P to A,
00580  *@n                    dist[1] gives distance from P to (A+C) [not same as below]
00581  * @return       1      intersection found (t and u returned)
00582  *@n                    dist[0] gives distance from P to isect,
00583  *@n                    dist[1] gives distance from A to isect.
00584  *
00585  * @param dist  When explicit return > 0, dist[0] and dist[1] are the
00586  *      line parameters of the intersection point on the 2 rays.
00587  *      The actual intersection coordinates can be found by
00588  *      substituting either of these into the original ray equations.
00589  *
00590  * @param p     point of first line
00591  * @param d     direction of first line
00592  * @param a     point of second line
00593  * @param c     direction of second line
00594  * @param tol   tolerance values
00595  *
00596  *  Note that for lines which are very nearly parallel, but not
00597  *  quite parallel enough to have the determinant go to "zero",
00598  *  the intersection can turn up in surprising places.
00599  *  (e.g. when det=1e-15 and det1=5.5e-17, t=0.5)
00600  */
00601 int
00602 bn_isect_line2_line2(fastf_t *dist, const fastf_t *p, const fastf_t *d, const fastf_t *a, const fastf_t *c, const struct bn_tol *tol)
00603                                                 /* dist[2] */
00604 
00605 
00606 
00607 
00608 
00609 {
00610         fastf_t                 hx, hy;         /* A - P */
00611         register fastf_t        det;
00612         register fastf_t        det1;
00613         vect_t                  unit_d;
00614         vect_t                  unit_c;
00615         vect_t                  unit_h;
00616         int                     parallel;
00617         int                     parallel1;
00618 
00619         BN_CK_TOL(tol);
00620         if( bu_debug & BU_DEBUG_MATH )  {
00621                 bu_log("bn_isect_line2_line2() p=(%g,%g), d=(%g,%g)\n\t\t\ta=(%g,%g), c=(%g,%g)\n",
00622                         V2ARGS(p), V2ARGS(d), V2ARGS(a), V2ARGS(c) );
00623         }
00624 
00625         /*
00626          *  From the two components q and r, form a system
00627          *  of 2 equations in 2 unknowns.
00628          *  Solve for t and u in the system:
00629          *
00630          *      Px + t * Dx = Ax + u * Cx
00631          *      Py + t * Dy = Ay + u * Cy
00632          *  or
00633          *      t * Dx - u * Cx = Ax - Px
00634          *      t * Dy - u * Cy = Ay - Py
00635          *
00636          *  Let H = A - P, resulting in:
00637          *
00638          *      t * Dx - u * Cx = Hx
00639          *      t * Dy - u * Cy = Hy
00640          *
00641          *  or
00642          *
00643          *      [ Dx  -Cx ]   [ t ]   [ Hx ]
00644          *      [         ] * [   ] = [    ]
00645          *      [ Dy  -Cy ]   [ u ]   [ Hy ]
00646          *
00647          *  This system can be solved by direct substitution, or by
00648          *  finding the determinants by Cramers rule:
00649          *
00650          *                   [ Dx  -Cx ]
00651          *      det(M) = det [         ] = -Dx * Cy + Cx * Dy
00652          *                   [ Dy  -Cy ]
00653          *
00654          *  If det(M) is zero, then the lines are parallel (perhaps colinear).
00655          *  Otherwise, exactly one solution exists.
00656          */
00657         det = c[X] * d[Y] - d[X] * c[Y];
00658 
00659         /*
00660          *  det(M) is non-zero, so there is exactly one solution.
00661          *  Using Cramer's rule, det1(M) replaces the first column
00662          *  of M with the constant column vector, in this case H.
00663          *  Similarly, det2(M) replaces the second column.
00664          *  Computation of the determinant is done as before.
00665          *
00666          *  Now,
00667          *
00668          *                        [ Hx  -Cx ]
00669          *                    det [         ]
00670          *          det1(M)       [ Hy  -Cy ]   -Hx * Cy + Cx * Hy
00671          *      t = ------- = --------------- = ------------------
00672          *           det(M)       det(M)        -Dx * Cy + Cx * Dy
00673          *
00674          *  and
00675          *
00676          *                        [ Dx   Hx ]
00677          *                    det [         ]
00678          *          det2(M)       [ Dy   Hy ]    Dx * Hy - Hx * Dy
00679          *      u = ------- = --------------- = ------------------
00680          *           det(M)       det(M)        -Dx * Cy + Cx * Dy
00681          */
00682         hx = a[X] - p[X];
00683         hy = a[Y] - p[Y];
00684         det1 = (c[X] * hy - hx * c[Y]);
00685 
00686         unit_d[0] = d[0];
00687         unit_d[1] = d[1];
00688         unit_d[2] = 0.0;
00689         VUNITIZE( unit_d );
00690         unit_c[0] = c[0];
00691         unit_c[1] = c[1];
00692         unit_c[2] = 0.0;
00693         VUNITIZE( unit_c );
00694         unit_h[0] = hx;
00695         unit_h[1] = hy;
00696         unit_h[2] = 0.0;
00697         VUNITIZE( unit_h );
00698 
00699         if( fabs( VDOT( unit_d, unit_c ) ) >= tol->para )
00700                 parallel = 1;
00701         else
00702                 parallel = 0;
00703 
00704         if( fabs( VDOT( unit_h, unit_c ) ) >= tol->para )
00705                 parallel1 = 1;
00706         else
00707                 parallel1 = 0;
00708 
00709         /* XXX This zero tolerance here should actually be
00710          * XXX determined by something like
00711          * XXX max(c[X], c[Y], d[X], d[Y]) / MAX_FASTF_DYNAMIC_RANGE
00712          * XXX In any case, nothing smaller than 1e-16
00713          */
00714 #define DETERMINANT_TOL         1.0e-14         /* XXX caution on non-IEEE machines */
00715         if( parallel || NEAR_ZERO( det, DETERMINANT_TOL ) )  {
00716                 /* Lines are parallel */
00717                 if( !parallel1 && !NEAR_ZERO( det1, DETERMINANT_TOL ) )  {
00718                         /* Lines are NOT co-linear, just parallel */
00719                         if( bu_debug & BU_DEBUG_MATH )  {
00720                                 bu_log("\tparallel, not co-linear.  det=%e, det1=%g\n", det, det1);
00721                         }
00722                         return -1;      /* parallel, no intersection */
00723                 }
00724 
00725                 /*
00726                  *  Lines are co-linear.
00727                  *  Determine t as distance from P to A.
00728                  *  Determine u as distance from P to (A+C).  [special!]
00729                  *  Use largest direction component, for numeric stability
00730                  *  (and avoiding division by zero).
00731                  */
00732                 if( fabs(d[X]) >= fabs(d[Y]) )  {
00733                         dist[0] = hx/d[X];
00734                         dist[1] = (hx + c[X]) / d[X];
00735                 } else {
00736                         dist[0] = hy/d[Y];
00737                         dist[1] = (hy + c[Y]) / d[Y];
00738                 }
00739                 if( bu_debug & BU_DEBUG_MATH )  {
00740                         bu_log("\tcolinear, t = %g, u = %g\n", dist[0], dist[1] );
00741                 }
00742                 return 0;       /* Lines co-linear */
00743         }
00744         if( bu_debug & BU_DEBUG_MATH )  {
00745                 /* XXX This print is temporary */
00746 bu_log("\thx=%g, hy=%g, det=%g, det1=%g, det2=%g\n", hx, hy, det, det1, (d[X] * hy - hx * d[Y]) );
00747         }
00748         det = 1/det;
00749         dist[0] = det * det1;
00750         dist[1] = det * (d[X] * hy - hx * d[Y]);
00751         if( bu_debug & BU_DEBUG_MATH )  {
00752                 bu_log("\tintersection, t = %g, u = %g\n", dist[0], dist[1] );
00753         }
00754 
00755 #if 0
00756         /* XXX This isn't any good.
00757          * 1)  Sometimes, dist[0] is very large.  Only caller can tell whether
00758          *     that is useful to him or not.
00759          * 2)  Sometimes, the difference between the two hit points is
00760          *     not much more than tol->dist.  Either hit point is perfectly
00761          *     good;  the caller just needs to be careful and not use *both*.
00762          */
00763         {
00764                 point_t         hit1, hit2;
00765                 vect_t          diff;
00766                 fastf_t         dist_sq;
00767 
00768                 VJOIN1_2D( hit1, p, dist[0], d );
00769                 VJOIN1_2D( hit2, a, dist[1], c );
00770                 VSUB2_2D( diff, hit1, hit2 );
00771                 dist_sq = MAGSQ_2D( diff );
00772                 if( dist_sq >= tol->dist_sq )  {
00773                         if( bu_debug & BU_DEBUG_MATH || dist_sq < 100*tol->dist_sq )  {
00774                                 bu_log("bn_isect_line2_line2(): dist=%g >%g, inconsistent solution, hit1=(%g,%g), hit2=(%g,%g)\n",
00775                                         sqrt(dist_sq), tol->dist,
00776                                         hit1[X], hit1[Y], hit2[X], hit2[Y]);
00777                         }
00778                         return -2;      /* s/b -1? */
00779                 }
00780         }
00781 #endif
00782 
00783         return 1;               /* Intersection found */
00784 }
00785 
00786 /**
00787  *                      B N _ I S E C T _ L I N E 2 _ L S E G 2
00788  *@brief
00789  *  Intersect a line in parametric form:
00790  *
00791  *      X = P + t * D
00792  *
00793  *  with a line segment defined by two distinct points A and B=(A+C).
00794  *
00795  *  XXX probably should take point B, not vector C.  Sigh.
00796  *
00797  *
00798  * @return      -4      A and B are not distinct points
00799  * @return      -3      Lines do not intersect
00800  * @return      -2      Intersection exists, but outside segemnt, < A
00801  * @return      -1      Intersection exists, but outside segment, > B
00802  * @return       0      Lines are co-linear (special meaning of dist[1])
00803  * @return       1      Intersection at vertex A
00804  * @return       2      Intersection at vertex B (A+C)
00805  * @return       3      Intersection between A and B
00806  *
00807  *  Implicit Returns -
00808  * @param dist  When explicit return >= 0, t is the parameter that describes
00809  *              the intersection of the line and the line segment.
00810  *              The actual intersection coordinates can be found by
00811  *              solving P + t * D.  However, note that for return codes
00812  *              1 and 2 (intersection exactly at a vertex), it is
00813  *              strongly recommended that the original values passed in
00814  *              A or B are used instead of solving P + t * D, to prevent
00815  *              numeric error from creeping into the position of
00816  *              the endpoints.
00817  *
00818  * @param p     point of first line
00819  * @param d     direction of first line
00820  * @param a     point of second line
00821  * @param c     direction of second line
00822  * @param tol   tolerance values
00823  */
00824 int
00825 bn_isect_line2_lseg2(fastf_t *dist,
00826                      const fastf_t *p,
00827                      const fastf_t *d,
00828                      const fastf_t *a,
00829                      const fastf_t *c,
00830                      const struct bn_tol *tol)
00831 {
00832         register fastf_t f;
00833         fastf_t         ctol;
00834         int             ret;
00835         point_t         b;
00836 
00837         BN_CK_TOL(tol);
00838         if( bu_debug & BU_DEBUG_MATH )  {
00839                 bu_log("bn_isect_line2_lseg2() p=(%g,%g), pdir=(%g,%g)\n\t\t\ta=(%g,%g), adir=(%g,%g)\n",
00840                         V2ARGS(p), V2ARGS(d), V2ARGS(a), V2ARGS(c) );
00841         }
00842 
00843         /*
00844          *  To keep the values of u between 0 and 1,
00845          *  C should NOT be scaled to have unit length.
00846          *  However, it is a good idea to make sure that
00847          *  C is a non-zero vector, (ie, that A and B are distinct).
00848          */
00849         if( (ctol = MAGSQ_2D(c)) <= tol->dist_sq )  {
00850                 ret = -4;               /* points A and B are not distinct */
00851                 goto out;
00852         }
00853 
00854         /*
00855          *  Detecting colinearity is difficult, and very very important.
00856          *  As a first step, check to see if both points A and B lie
00857          *  within tolerance of the line.  If so, then the line segment AC
00858          *  is ON the line.
00859          */
00860         VADD2_2D( b, a, c );
00861         if( bn_distsq_line2_point2( p, d, a ) <= tol->dist_sq  &&
00862             (ctol=bn_distsq_line2_point2( p, d, b )) <= tol->dist_sq )  {
00863                 if( bu_debug & BU_DEBUG_MATH )  {
00864 bu_log("b=(%g, %g), b_dist_sq=%g\n", V2ARGS(b), ctol);
00865                         bu_log("bn_isect_line2_lseg2() pts A and B within tol of line\n");
00866                 }
00867                 /* Find the parametric distance along the ray */
00868                 dist[0] = bn_dist_pt2_along_line2( p, d, a );
00869                 dist[1] = bn_dist_pt2_along_line2( p, d, b );
00870                 ret = 0;                /* Colinear */
00871                 goto out;
00872         }
00873 
00874         if( (ret = bn_isect_line2_line2( dist, p, d, a, c, tol )) < 0 )  {
00875                 /* Lines are parallel, non-colinear */
00876                 ret = -3;               /* No intersection found */
00877                 goto out;
00878         }
00879         if( ret == 0 )  {
00880                 fastf_t dtol;
00881                 /*  Lines are colinear */
00882                 /*  If P within tol of either endpoint (0, 1), make exact. */
00883                 dtol = tol->dist / sqrt(MAGSQ_2D(d));
00884                 if( bu_debug & BU_DEBUG_MATH )  {
00885                         bu_log("bn_isect_line2_lseg2() dtol=%g, dist[0]=%g, dist[1]=%g\n",
00886                                 dtol, dist[0], dist[1]);
00887                 }
00888                 if( dist[0] > -dtol && dist[0] < dtol )  dist[0] = 0;
00889                 else if( dist[0] > 1-dtol && dist[0] < 1+dtol ) dist[0] = 1;
00890 
00891                 if( dist[1] > -dtol && dist[1] < dtol )  dist[1] = 0;
00892                 else if( dist[1] > 1-dtol && dist[1] < 1+dtol ) dist[1] = 1;
00893                 ret = 0;                /* Colinear */
00894                 goto out;
00895         }
00896 
00897         /*
00898          *  The two lines are claimed to intersect at a point.
00899          *  First, validate that hit point represented by dist[0]
00900          *  is in fact on and between A--B.
00901          *  (Nearly parallel lines can result in odd situations here).
00902          *  The performance hit of doing this is vastly preferable
00903          *  to returning wrong answers.  Know a faster algorithm?
00904          */
00905         {
00906                 fastf_t         ab_dist = 0;
00907                 point_t         hit_pt;
00908                 point_t         hit2;
00909 
00910                 VJOIN1_2D( hit_pt, p, dist[0], d );
00911                 VJOIN1_2D( hit2, a, dist[1], c );
00912                 /* Check both hit point value calculations */
00913                 if( bn_pt2_pt2_equal( a, hit_pt, tol ) ||
00914                     bn_pt2_pt2_equal( a, hit2, tol ) )  {
00915                         dist[1] = 0;
00916                         ret = 1;        /* Intersect is at A */
00917                 }
00918                 if( bn_pt2_pt2_equal( b, hit_pt, tol ) ||
00919                     bn_pt2_pt2_equal( b, hit_pt, tol ) )  {
00920                         dist[1] = 1;
00921                         ret = 2;        /* Intersect is at B */
00922                 }
00923 
00924                 ret = bn_isect_pt2_lseg2( &ab_dist, a, b, hit_pt, tol );
00925                 if( bu_debug & BU_DEBUG_MATH )  {
00926                         /* XXX This is temporary */
00927                         V2PRINT("a", a);
00928                         V2PRINT("hit", hit_pt);
00929                         V2PRINT("b", b);
00930 bu_log("bn_isect_pt2_lseg2() hit2d=(%g,%g) ab_dist=%g, ret=%d\n", hit_pt[X], hit_pt[Y], ab_dist, ret);
00931 bu_log("\tother hit2d=(%g,%g)\n", hit2[X], hit2[Y] );
00932                 }
00933                 if( ret <= 0 )  {
00934                         if( ab_dist < 0 )  {
00935                                 ret = -2;       /* Intersection < A */
00936                         } else {
00937                                 ret = -1;       /* Intersection >B */
00938                         }
00939                         goto out;
00940                 }
00941                 if( ret == 1 )  {
00942                         dist[1] = 0;
00943                         ret = 1;        /* Intersect is at A */
00944                         goto out;
00945                 }
00946                 if( ret == 2 )  {
00947                         dist[1] = 1;
00948                         ret = 2;        /* Intersect is at B */
00949                         goto out;
00950                 }
00951                 /* ret == 3, hit_pt is between A and B */
00952 
00953                 if( !bn_between( a[X], hit_pt[X], b[X], tol ) ||
00954                     !bn_between( a[Y], hit_pt[Y], b[Y], tol ) ) {
00955                         bu_bomb("bn_isect_line2_lseg2() hit_pt not between A and B!\n");
00956                 }
00957         }
00958 
00959         /*
00960          *  If the dist[1] parameter is outside the range (0..1),
00961          *  reject the intersection, because it falls outside
00962          *  the line segment A--B.
00963          *
00964          *  Convert the tol->dist into allowable deviation in terms of
00965          *  (0..1) range of the parameters.
00966          */
00967         ctol = tol->dist / sqrt(ctol);
00968         if( bu_debug & BU_DEBUG_MATH )  {
00969                 bu_log("bn_isect_line2_lseg2() ctol=%g, dist[1]=%g\n", ctol, dist[1]);
00970         }
00971         if( dist[1] < -ctol )  {
00972                 ret = -2;               /* Intersection < A */
00973                 goto out;
00974         }
00975         if( (f=(dist[1]-1)) > ctol )  {
00976                 ret = -1;               /* Intersection > B */
00977                 goto out;
00978         }
00979 
00980         /* Check for ctoly intersection with one of the verticies */
00981         if( dist[1] < ctol )  {
00982                 dist[1] = 0;
00983                 ret = 1;                /* Intersection at A */
00984                 goto out;
00985         }
00986         if( f >= -ctol )  {
00987                 dist[1] = 1;
00988                 ret = 2;                /* Intersection at B */
00989                 goto out;
00990         }
00991         ret = 3;                        /* Intersection between A and B */
00992 out:
00993         if( bu_debug & BU_DEBUG_MATH )  {
00994                 bu_log("bn_isect_line2_lseg2() dist[0]=%g, dist[1]=%g, ret=%d\n",
00995                         dist[0], dist[1], ret);
00996         }
00997         return ret;
00998 }
00999 
01000 /**
01001  *                      B N _ I S E C T _ L S E G 2  _ L S E G 2
01002  *@brief
01003  *  Intersect two 2D line segments, defined by two points and two vectors.
01004  *  The vectors are unlikely to be unit length.
01005  *
01006  *
01007  * @return      -2      missed (line segments are parallel)
01008  * @return      -1      missed (colinear and non-overlapping)
01009  * @return       0      hit (line segments colinear and overlapping)
01010  * @return       1      hit (normal intersection)
01011  *
01012 
01013  * @param dist  The value at dist[] is set to the parametric distance of the 
01014  *              intercept.
01015  *@n    dist[0] is parameter along p, range 0 to 1, to intercept.
01016  *@n    dist[1] is parameter along q, range 0 to 1, to intercept.
01017  *@n    If within distance tolerance of the endpoints, these will be
01018  *      exactly 0.0 or 1.0, to ease the job of caller.
01019  *
01020  *  Special note:  when return code is "0" for co-linearity, dist[1] has
01021  *  an alternate interpretation:  it's the parameter along p (not q)
01022  *  which takes you from point p to the point (q + qdir), i.e., it's
01023  *  the endpoint of the q linesegment, since in this case there may be
01024  *  *two* intersections, if q is contained within span p to (p + pdir).
01025  *  And either may be -10 if the point is outside the span.
01026  *
01027  *
01028  * @param p     point 1
01029  * @param pdir  direction1
01030  * @param q     point 2
01031  * @param qdir  direction2
01032  * @param tol   tolerance values
01033  *
01034  */
01035 int
01036 bn_isect_lseg2_lseg2(fastf_t *dist,
01037                      const fastf_t *p,
01038                      const fastf_t *pdir,
01039                      const fastf_t *q,
01040                      const fastf_t *qdir,
01041                      const struct bn_tol *tol)
01042 {
01043         fastf_t ptol, qtol;     /* length in parameter space == tol->dist */
01044         int     status;
01045 
01046         BN_CK_TOL(tol);
01047         if( bu_debug & BU_DEBUG_MATH )  {
01048                 bu_log("bn_isect_lseg2_lseg2() p=(%g,%g), pdir=(%g,%g)\n\t\tq=(%g,%g), qdir=(%g,%g)\n",
01049                         V2ARGS(p), V2ARGS(pdir), V2ARGS(q), V2ARGS(qdir) );
01050         }
01051 
01052         status = bn_isect_line2_line2( dist, p, pdir, q, qdir, tol );
01053         if( status < 0 )  {
01054                 /* Lines are parallel, non-colinear */
01055                 return -1;      /* No intersection */
01056         }
01057         if( status == 0 )  {
01058                 int     nogood = 0;
01059                 /* Lines are colinear */
01060                 /*  If P within tol of either endpoint (0, 1), make exact. */
01061                 ptol = tol->dist / sqrt( MAGSQ_2D(pdir) );
01062                 if( bu_debug & BU_DEBUG_MATH )  {
01063                         bu_log("ptol=%g\n", ptol);
01064                 }
01065                 if( dist[0] > -ptol && dist[0] < ptol )  dist[0] = 0;
01066                 else if( dist[0] > 1-ptol && dist[0] < 1+ptol ) dist[0] = 1;
01067 
01068                 if( dist[1] > -ptol && dist[1] < ptol )  dist[1] = 0;
01069                 else if( dist[1] > 1-ptol && dist[1] < 1+ptol ) dist[1] = 1;
01070 
01071                 if( dist[1] < 0 || dist[1] > 1 )  nogood = 1;
01072                 if( dist[0] < 0 || dist[0] > 1 )  nogood++;
01073                 if( nogood >= 2 )
01074                         return -1;      /* colinear, but not overlapping */
01075                 if( bu_debug & BU_DEBUG_MATH )  {
01076                         bu_log("  HIT colinear!\n");
01077                 }
01078                 return 0;               /* colinear and overlapping */
01079         }
01080         /* Lines intersect */
01081         /*  If within tolerance of an endpoint (0, 1), make exact. */
01082         ptol = tol->dist / sqrt( MAGSQ_2D(pdir) );
01083         if( dist[0] > -ptol && dist[0] < ptol )  dist[0] = 0;
01084         else if( dist[0] > 1-ptol && dist[0] < 1+ptol ) dist[0] = 1;
01085 
01086         qtol = tol->dist / sqrt( MAGSQ_2D(qdir) );
01087         if( dist[1] > -qtol && dist[1] < qtol )  dist[1] = 0;
01088         else if( dist[1] > 1-qtol && dist[1] < 1+qtol ) dist[1] = 1;
01089 
01090         if( bu_debug & BU_DEBUG_MATH )  {
01091                 bu_log("ptol=%g, qtol=%g\n", ptol, qtol);
01092         }
01093         if( dist[0] < 0 || dist[0] > 1 || dist[1] < 0 || dist[1] > 1 ) {
01094                 if( bu_debug & BU_DEBUG_MATH )  {
01095                         bu_log("  MISS\n");
01096                 }
01097                 return -1;              /* missed */
01098         }
01099         if( bu_debug & BU_DEBUG_MATH )  {
01100                 bu_log("  HIT!\n");
01101         }
01102         return 1;                       /* hit, normal intersection */
01103 }
01104 
01105 /**
01106  *                      B N _ I S E C T _ L S E G 3  _ L S E G 3
01107  *@brief
01108  *  Intersect two 3D line segments, defined by two points and two vectors.
01109  *  The vectors are unlikely to be unit length.
01110  *
01111  *
01112  * @return      -2      missed (line segments are parallel)
01113  * @return      -1      missed (colinear and non-overlapping)
01114  * @return       0      hit (line segments colinear and overlapping)
01115  * @return       1      hit (normal intersection)
01116  *
01117  * @param[out] dist
01118  *      The value at dist[] is set to the parametric distance of the intercept
01119  *      dist[0] is parameter along p, range 0 to 1, to intercept.
01120  *      dist[1] is parameter along q, range 0 to 1, to intercept.
01121  *      If within distance tolerance of the endpoints, these will be
01122  *      exactly 0.0 or 1.0, to ease the job of caller.
01123  *
01124  *  Special note:  when return code is "0" for co-linearity, dist[1] has
01125  *  an alternate interpretation:  it's the parameter along p (not q)
01126  *  which takes you from point p to the point (q + qdir), i.e., it's
01127  *  the endpoint of the q linesegment, since in this case there may be
01128  *  *two* intersections, if q is contained within span p to (p + pdir).
01129  *  And either may be -10 if the point is outside the span.
01130  *
01131  * @param p     point 1
01132  * @param pdir  direction-1
01133  * @param q     point 2
01134  * @param qdir  direction-2
01135  * @param tol   tolerance values
01136  */
01137 int
01138 bn_isect_lseg3_lseg3(fastf_t *dist,
01139                      const fastf_t *p,
01140                      const fastf_t *pdir,
01141                      const fastf_t *q,
01142                      const fastf_t *qdir,
01143                      const struct bn_tol *tol)
01144 {
01145         fastf_t ptol, qtol;     /* length in parameter space == tol->dist */
01146         fastf_t pmag, qmag;
01147         int     status;
01148 
01149         BN_CK_TOL(tol);
01150         if( bu_debug & BU_DEBUG_MATH )  {
01151                 bu_log("bn_isect_lseg3_lseg3() p=(%g,%g), pdir=(%g,%g)\n\t\tq=(%g,%g), qdir=(%g,%g)\n",
01152                         V2ARGS(p), V2ARGS(pdir), V2ARGS(q), V2ARGS(qdir) );
01153         }
01154 
01155         status = bn_isect_line3_line3( &dist[0], &dist[1], p, pdir, q, qdir, tol );
01156         if( status < 0 )  {
01157                 /* Lines are parallel, non-colinear */
01158                 return -1;      /* No intersection */
01159         }
01160         pmag = MAGNITUDE(pdir);
01161         if( pmag < SMALL_FASTF )
01162                 bu_bomb("bn_isect_lseg3_lseg3: |p|=0\n");
01163         if( status == 0 )  {
01164                 int     nogood = 0;
01165                 /* Lines are colinear */
01166                 /*  If P within tol of either endpoint (0, 1), make exact. */
01167                 ptol = tol->dist / pmag;
01168                 if( bu_debug & BU_DEBUG_MATH )  {
01169                         bu_log("ptol=%g\n", ptol);
01170                 }
01171                 if( dist[0] > -ptol && dist[0] < ptol )  dist[0] = 0;
01172                 else if( dist[0] > 1-ptol && dist[0] < 1+ptol ) dist[0] = 1;
01173 
01174                 if( dist[1] > -ptol && dist[1] < ptol )  dist[1] = 0;
01175                 else if( dist[1] > 1-ptol && dist[1] < 1+ptol ) dist[1] = 1;
01176 
01177                 if( dist[1] < 0 || dist[1] > 1 )  nogood = 1;
01178                 if( dist[0] < 0 || dist[0] > 1 )  nogood++;
01179                 if( nogood >= 2 )
01180                         return -1;      /* colinear, but not overlapping */
01181                 if( bu_debug & BU_DEBUG_MATH )  {
01182                         bu_log("  HIT colinear!\n");
01183                 }
01184                 return 0;               /* colinear and overlapping */
01185         }
01186         /* Lines intersect */
01187         /*  If within tolerance of an endpoint (0, 1), make exact. */
01188         ptol = tol->dist / pmag;
01189         if( dist[0] > -ptol && dist[0] < ptol )  dist[0] = 0;
01190         else if( dist[0] > 1-ptol && dist[0] < 1+ptol ) dist[0] = 1;
01191 
01192         qmag = MAGNITUDE(qdir);
01193         if( qmag < SMALL_FASTF )
01194                 bu_bomb("bn_isect_lseg3_lseg3: |q|=0\n");
01195         qtol = tol->dist / qmag;
01196         if( dist[1] > -qtol && dist[1] < qtol )  dist[1] = 0;
01197         else if( dist[1] > 1-qtol && dist[1] < 1+qtol ) dist[1] = 1;
01198 
01199         if( bu_debug & BU_DEBUG_MATH )  {
01200                 bu_log("ptol=%g, qtol=%g\n", ptol, qtol);
01201         }
01202         if( dist[0] < 0 || dist[0] > 1 || dist[1] < 0 || dist[1] > 1 ) {
01203                 if( bu_debug & BU_DEBUG_MATH )  {
01204                         bu_log("  MISS\n");
01205                 }
01206                 return -1;              /* missed */
01207         }
01208         if( bu_debug & BU_DEBUG_MATH )  {
01209                 bu_log("  HIT!\n");
01210         }
01211         return 1;                       /* hit, normal intersection */
01212 }
01213 
01214 /**
01215  *                      B N _ I S E C T _ L I N E 3 _ L I N E 3
01216  *
01217  *  Intersect two lines, each in given in parametric form:
01218  *
01219  *      X = P + t * D
01220  *  and
01221  *      X = A + u * C
01222  *
01223  *  While the parametric form is usually used to denote a ray
01224  *  (ie, positive values of the parameter only), in this case
01225  *  the full line is considered.
01226  *
01227  *  The direction vectors C and D need not have unit length.
01228  *
01229  *
01230  * @return  -2  no intersection, lines are parallel.
01231  * @return  -1  no intersection
01232  * @return   0  lines are co-linear (t returned for u=0 to give distance to A)
01233  * @return   1  intersection found (t and u returned)
01234  *
01235  * @param[out]  t,u     line parameter of interseciton
01236  *              When explicit return >= 0, t and u are the
01237  *              line parameters of the intersection point on the 2 rays.
01238  *              The actual intersection coordinates can be found by
01239  *              substituting either of these into the original ray equations.
01240 
01241  * @param       p       point 1
01242  * @param       d       direction 1
01243  * @param       a       point 2
01244  * @param       c       direction 2
01245  * @param tol   tolerance values
01246  *
01247  *      t,u     When explicit return >= 0, t and u are the
01248  *              line parameters of the intersection point on the 2 rays.
01249  *              The actual intersection coordinates can be found by
01250  *              substituting either of these into the original ray equations.
01251  *
01252  * XXX It would be sensible to change the t,u pair to dist[2].
01253  *
01254  *
01255  */
01256 int
01257 bn_isect_line3_line3(fastf_t *t,
01258                      fastf_t *u,
01259                      const fastf_t *p,
01260                      const fastf_t *d,
01261                      const fastf_t *a,
01262                      const fastf_t *c,
01263                      const struct bn_tol *tol)
01264 {
01265         LOCAL vect_t            n;
01266         LOCAL vect_t            abs_n;
01267         LOCAL vect_t            h;
01268         register fastf_t        det;
01269         register fastf_t        det1;
01270         register short int      q,r,s;
01271 
01272         BN_CK_TOL(tol);
01273 
01274         /*
01275          *  Any intersection will occur in the plane with surface
01276          *  normal D cross C, which may not have unit length.
01277          *  The plane containing the two lines will be a constant
01278          *  distance from a plane with the same normal that contains
01279          *  the origin.  Therfore, the projection of any point on the
01280          *  plane along N has the same length.
01281          *  Verify that this holds for P and A.
01282          *  If N dot P != N dot A, there is no intersection, because
01283          *  P and A must lie on parallel planes that are different
01284          *  distances from the origin.
01285          */
01286         VCROSS( n, d, c );
01287         det = VDOT( n, p ) - VDOT( n, a );
01288         if( !NEAR_ZERO( det, tol->dist ) )  {
01289                 return(-1);             /* No intersection */
01290         }
01291 
01292         if( NEAR_ZERO( MAGSQ( n ) , SMALL_FASTF ) )
01293         {
01294                 vect_t a_to_p;
01295 
01296                 /* lines are parallel, must find another way to get normal vector */
01297                 VSUB2( a_to_p , p , a );
01298                 VCROSS( n , a_to_p , d );
01299 
01300                 if( NEAR_ZERO( MAGSQ( n ) , SMALL_FASTF ) )
01301                         bn_vec_ortho( n, d );
01302         }
01303 
01304         /*
01305          *  Solve for t and u in the system:
01306          *
01307          *      Px + t * Dx = Ax + u * Cx
01308          *      Py + t * Dy = Ay + u * Cy
01309          *      Pz + t * Dz = Az + u * Cz
01310          *
01311          *  This system is over-determined, having 3 equations in 2 unknowns.
01312          *  However, the intersection problem is really only a 2-dimensional
01313          *  problem, being located in the surface of a plane.
01314          *  Therefore, the "least important" of these equations can
01315          *  be initially ignored, leaving a system of 2 equations in
01316          *  2 unknowns.
01317          *
01318          *  Find the component of N with the largest magnitude.
01319          *  This component will have the least effect on the parameters
01320          *  in the system, being most nearly perpendicular to the plane.
01321          *  Denote the two remaining components by the
01322          *  subscripts q and r, rather than x,y,z.
01323          *  Subscript s is the smallest component, used for checking later.
01324          */
01325         abs_n[X] = (n[X] >= 0) ? n[X] : (-n[X]);
01326         abs_n[Y] = (n[Y] >= 0) ? n[Y] : (-n[Y]);
01327         abs_n[Z] = (n[Z] >= 0) ? n[Z] : (-n[Z]);
01328         if( abs_n[X] >= abs_n[Y] )  {
01329                 if( abs_n[X] >= abs_n[Z] )  {
01330                         /* X is largest in magnitude */
01331                         q = Y;
01332                         r = Z;
01333                         s = X;
01334                 } else {
01335                         /* Z is largest in magnitude */
01336                         q = X;
01337                         r = Y;
01338                         s = Z;
01339                 }
01340         } else {
01341                 if( abs_n[Y] >= abs_n[Z] )  {
01342                         /* Y is largest in magnitude */
01343                         q = X;
01344                         r = Z;
01345                         s = Y;
01346                 } else {
01347                         /* Z is largest in magnitude */
01348                         q = X;
01349                         r = Y;
01350                         s = Z;
01351                 }
01352         }
01353 
01354 #if 0
01355         /* XXX Use bn_isect_line2_line2() here */
01356         /* move the 2d vectors around */
01357         bn_isect_line2_line2( &dist, p, d, a, c, tol );
01358 #endif
01359 
01360         /*
01361          *  From the two components q and r, form a system
01362          *  of 2 equations in 2 unknowns:
01363          *
01364          *      Pq + t * Dq = Aq + u * Cq
01365          *      Pr + t * Dr = Ar + u * Cr
01366          *  or
01367          *      t * Dq - u * Cq = Aq - Pq
01368          *      t * Dr - u * Cr = Ar - Pr
01369          *
01370          *  Let H = A - P, resulting in:
01371          *
01372          *      t * Dq - u * Cq = Hq
01373          *      t * Dr - u * Cr = Hr
01374          *
01375          *  or
01376          *
01377          *      [ Dq  -Cq ]   [ t ]   [ Hq ]
01378          *      [         ] * [   ] = [    ]
01379          *      [ Dr  -Cr ]   [ u ]   [ Hr ]
01380          *
01381          *  This system can be solved by direct substitution, or by
01382          *  finding the determinants by Cramers rule:
01383          *
01384          *                   [ Dq  -Cq ]
01385          *      det(M) = det [         ] = -Dq * Cr + Cq * Dr
01386          *                   [ Dr  -Cr ]
01387          *
01388          *  If det(M) is zero, then the lines are parallel (perhaps colinear).
01389          *  Otherwise, exactly one solution exists.
01390          */
01391         VSUB2( h, a, p );
01392         det = c[q] * d[r] - d[q] * c[r];
01393         det1 = (c[q] * h[r] - h[q] * c[r]);             /* see below */
01394         /* XXX This should be no smaller than 1e-16.  See bn_isect_line2_line2 for details */
01395         if( NEAR_ZERO( det, DETERMINANT_TOL ) )  {
01396                 /* Lines are parallel */
01397                 if( !NEAR_ZERO( det1, DETERMINANT_TOL ) )  {
01398                         /* Lines are NOT co-linear, just parallel */
01399                         return -2;      /* parallel, no intersection */
01400                 }
01401 
01402                 /* Lines are co-linear */
01403                 /* Compute t for u=0 as a convenience to caller */
01404                 *u = 0;
01405                 /* Use largest direction component */
01406                 if( fabs(d[q]) >= fabs(d[r]) )  {
01407                         *t = h[q]/d[q];
01408                 } else {
01409                         *t = h[r]/d[r];
01410                 }
01411                 return(0);      /* Lines co-linear */
01412         }
01413 
01414         /*
01415          *  det(M) is non-zero, so there is exactly one solution.
01416          *  Using Cramer's rule, det1(M) replaces the first column
01417          *  of M with the constant column vector, in this case H.
01418          *  Similarly, det2(M) replaces the second column.
01419          *  Computation of the determinant is done as before.
01420          *
01421          *  Now,
01422          *
01423          *                        [ Hq  -Cq ]
01424          *                    det [         ]
01425          *          det1(M)       [ Hr  -Cr ]   -Hq * Cr + Cq * Hr
01426          *      t = ------- = --------------- = ------------------
01427          *           det(M)       det(M)        -Dq * Cr + Cq * Dr
01428          *
01429          *  and
01430          *
01431          *                        [ Dq   Hq ]
01432          *                    det [         ]
01433          *          det2(M)       [ Dr   Hr ]    Dq * Hr - Hq * Dr
01434          *      u = ------- = --------------- = ------------------
01435          *           det(M)       det(M)        -Dq * Cr + Cq * Dr
01436          */
01437         det = 1/det;
01438         *t = det * det1;
01439         *u = det * (d[q] * h[r] - h[q] * d[r]);
01440 
01441         /*
01442          *  Check that these values of t and u satisfy the 3rd equation
01443          *  as well!
01444          *  XXX It isn't clear that "det" is exactly a model-space distance.
01445          */
01446         det = *t * d[s] - *u * c[s] - h[s];
01447         if( !NEAR_ZERO( det, tol->dist ) )  {
01448                 /* XXX This tolerance needs to be much less loose than
01449                  * XXX SQRT_SMALL_FASTF.  What about DETERMINANT_TOL?
01450                  */
01451                 /* Inconsistent solution, lines miss each other */
01452                 return(-1);
01453         }
01454 
01455         /*  To prevent errors, check the answer.
01456          *  Not returning bogus results to our caller is worth the extra time.
01457          */
01458         {
01459                 point_t         hit1, hit2;
01460 
01461                 VJOIN1( hit1, p, *t, d );
01462                 VJOIN1( hit2, a, *u, c );
01463                 if( !bn_pt3_pt3_equal( hit1, hit2, tol ) )  {
01464 /*                      bu_log("bn_isect_line3_line3(): BOGUS RESULT, hit1=(%g,%g,%g), hit2=(%g,%g,%g)\n",
01465                                 hit1[X], hit1[Y], hit1[Z], hit2[X], hit2[Y], hit2[Z]); */
01466                         return -1;
01467                 }
01468         }
01469 
01470         return(1);              /* Intersection found */
01471 }
01472 
01473 /**
01474  *                      B N _ I S E C T _ L I N E _ L S E G
01475  *@brief
01476  *  Intersect a line in parametric form:
01477  *
01478  *      X = P + t * D
01479  *
01480  *  with a line segment defined by two distinct points A and B.
01481  *
01482  *  
01483  * @return      -4      A and B are not distinct points
01484  * @return      -3      Intersection exists, < A (t is returned)
01485  * @return      -2      Intersection exists, > B (t is returned)
01486  * @return      -1      Lines do not intersect
01487  * @return       0      Lines are co-linear (t for A is returned)
01488  * @return       1      Intersection at vertex A
01489  * @return       2      Intersection at vertex B
01490  * @return       3      Intersection between A and B
01491  *
01492  *  @par Implicit Returns -
01493  *      t       When explicit return >= 0, t is the parameter that describes
01494  *              the intersection of the line and the line segment.
01495  *              The actual intersection coordinates can be found by
01496  *              solving P + t * D.  However, note that for return codes
01497  *              1 and 2 (intersection exactly at a vertex), it is
01498  *              strongly recommended that the original values passed in
01499  *              A or B are used instead of solving P + t * D, to prevent
01500  *              numeric error from creeping into the position of
01501  *              the endpoints.
01502  *
01503  * XXX should probably be called bn_isect_line3_lseg3()
01504  * XXX should probably be changed to return dist[2] 
01505  */
01506 int
01507 bn_isect_line_lseg(fastf_t *t, const fastf_t *p, const fastf_t *d, const fastf_t *a, const fastf_t *b, const struct bn_tol *tol)
01508 {
01509         LOCAL vect_t    c;              /* Direction vector from A to B */
01510         auto fastf_t    u;              /* As in, A + u * C = X */
01511         register fastf_t f;
01512         register int    ret;
01513         fastf_t         fuzz;
01514 
01515         BN_CK_TOL(tol);
01516 
01517         VSUB2( c, b, a );
01518         /*
01519          *  To keep the values of u between 0 and 1,
01520          *  C should NOT be scaled to have unit length.
01521          *  However, it is a good idea to make sure that
01522          *  C is a non-zero vector, (ie, that A and B are distinct).
01523          */
01524         if( (fuzz = MAGSQ(c)) < tol->dist_sq )  {
01525                 return(-4);             /* points A and B are not distinct */
01526         }
01527 
01528         /*
01529          *  Detecting colinearity is difficult, and very very important.
01530          *  As a first step, check to see if both points A and B lie
01531          *  within tolerance of the line.  If so, then the line segment AC
01532          *  is ON the line.
01533          */
01534         if( bn_distsq_line3_pt3( p, d, a ) <= tol->dist_sq  &&
01535             bn_distsq_line3_pt3( p, d, b ) <= tol->dist_sq )  {
01536                 if( bu_debug & BU_DEBUG_MATH )  {
01537                         bu_log("bn_isect_line3_lseg3() pts A and B within tol of line\n");
01538                 }
01539                 /* Find the parametric distance along the ray */
01540                 *t = bn_dist_pt3_along_line3( p, d, a );
01541                 /*** dist[1] = bn_dist_pt3_along_line3( p, d, b ); ***/
01542                 return 0;               /* Colinear */
01543         }
01544 
01545         if( (ret = bn_isect_line3_line3( t, &u, p, d, a, c, tol )) < 0 )  {
01546                 /* No intersection found */
01547                 return( -1 );
01548         }
01549         if( ret == 0 )  {
01550                 /* co-linear (t was computed for point A, u=0) */
01551                 return( 0 );
01552         }
01553 
01554         /*
01555          *  The two lines intersect at a point.
01556          *  If the u parameter is outside the range (0..1),
01557          *  reject the intersection, because it falls outside
01558          *  the line segment A--B.
01559          *
01560          *  Convert the tol->dist into allowable deviation in terms of
01561          *  (0..1) range of the parameters.
01562          */
01563         fuzz = tol->dist / sqrt(fuzz);
01564         if( u < -fuzz )
01565                 return(-3);             /* Intersection < A */
01566         if( (f=(u-1)) > fuzz )
01567                 return(-2);             /* Intersection > B */
01568 
01569         /* Check for fuzzy intersection with one of the verticies */
01570         if( u < fuzz )
01571                 return( 1 );            /* Intersection at A */
01572         if( f >= -fuzz )
01573                 return( 2 );            /* Intersection at B */
01574 
01575         return(3);                      /* Intersection between A and B */
01576 }
01577 
01578 /**
01579  *                      B N _ D I S T _ L I N E 3_ P T 3
01580  *@brief
01581  *  Given a parametric line defined by PT + t * DIR and a point A,
01582  *  return the closest distance between the line and the point.
01583  *
01584  *  'dir' need not have unit length.
01585  *
01586  *  Find parameter for PCA along line with unitized DIR:
01587  *      d = VDOT(f, dir) / MAGNITUDE(dir);
01588  *  Find distance g from PCA to A using Pythagoras:
01589  *      g = sqrt( MAGSQ(f) - d**2 )
01590  *
01591  *  Return -
01592  *      Distance
01593  */
01594 double
01595 bn_dist_line3_pt3(const fastf_t *pt, const fastf_t *dir, const fastf_t *a)
01596 {
01597         LOCAL vect_t            f;
01598         register fastf_t        FdotD;
01599 
01600         if( (FdotD = MAGNITUDE(dir)) <= SMALL_FASTF )  {
01601                 FdotD = 0.0;
01602                 goto out;
01603         }
01604         VSUB2( f, a, pt );
01605         FdotD = VDOT( f, dir ) / FdotD;
01606         FdotD = MAGSQ( f ) - FdotD * FdotD;
01607         if( FdotD <= SMALL_FASTF )  {
01608                 FdotD = 0.0;
01609                 goto out;
01610         }
01611         FdotD = sqrt(FdotD);
01612 out:
01613         if( bu_debug & BU_DEBUG_MATH )  {
01614                 bu_log("bn_dist_line3_pt3() ret=%g\n", FdotD);
01615         }
01616         return FdotD;
01617 }
01618 
01619 /**
01620  *                      B N _ D I S T S Q _ L I N E 3 _ P T 3
01621  *
01622  *  Given a parametric line defined by PT + t * DIR and a point A,
01623  *  return the square of the closest distance between the line and the point.
01624  *
01625  *  'dir' need not have unit length.
01626  *
01627  *  Return -
01628  *      Distance squared
01629  */
01630 double
01631 bn_distsq_line3_pt3(const fastf_t *pt, const fastf_t *dir, const fastf_t *a)
01632 {
01633         LOCAL vect_t            f;
01634         register fastf_t        FdotD;
01635 
01636         VSUB2( f, pt, a );
01637         if( (FdotD = MAGNITUDE(dir)) <= SMALL_FASTF )  {
01638                 FdotD = 0.0;
01639                 goto out;
01640         }
01641         FdotD = VDOT( f, dir ) / FdotD;
01642         if( (FdotD = VDOT( f, f ) - FdotD * FdotD ) <= SMALL_FASTF )  {
01643                 FdotD = 0.0;
01644         }
01645 out:
01646         if( bu_debug & BU_DEBUG_MATH )  {
01647                 bu_log("bn_distsq_line3_pt3() ret=%g\n", FdotD);
01648         }
01649         return FdotD;
01650 }
01651 
01652 /**
01653  *                      B N _ D I S T _ L I N E _ O R I G I N
01654  *@brief
01655  *  Given a parametric line defined by PT + t * DIR,
01656  *  return the closest distance between the line and the origin.
01657  *
01658  *  'dir' need not have unit length.
01659  *
01660  *  @return     Distance
01661  */
01662 double
01663 bn_dist_line_origin(const fastf_t *pt, const fastf_t *dir)
01664 {
01665         register fastf_t        PTdotD;
01666 
01667         if( (PTdotD = MAGNITUDE(dir)) <= SMALL_FASTF )
01668                 return 0.0;
01669         PTdotD = VDOT( pt, dir ) / PTdotD;
01670         if( (PTdotD = VDOT( pt, pt ) - PTdotD * PTdotD ) <= SMALL_FASTF )
01671                 return(0.0);
01672         return( sqrt(PTdotD) );
01673 }
01674 /**
01675  *                      B N _ D I S T _ L I N E 2 _ P O I N T 2
01676  *@brief
01677  *  Given a parametric line defined by PT + t * DIR and a point A,
01678  *  return the closest distance between the line and the point.
01679  *
01680  *  'dir' need not have unit length.
01681  *
01682  *  @return Distance
01683  */
01684 double
01685 bn_dist_line2_point2(const fastf_t *pt, const fastf_t *dir, const fastf_t *a)
01686 {
01687         LOCAL vect_t            f;
01688         register fastf_t        FdotD;
01689 
01690         VSUB2_2D( f, pt, a );
01691         if( (FdotD = sqrt(MAGSQ_2D(dir))) <= SMALL_FASTF )
01692                 return 0.0;
01693         FdotD = VDOT_2D( f, dir ) / FdotD;
01694         if( (FdotD = VDOT_2D( f, f ) - FdotD * FdotD ) <= SMALL_FASTF )
01695                 return(0.0);
01696         return( sqrt(FdotD) );
01697 }
01698 
01699 /**
01700  *                      B N _ D I S T S Q _ L I N E 2 _ P O I N T 2
01701  *@brief
01702  *  Given a parametric line defined by PT + t * DIR and a point A,
01703  *  return the closest distance between the line and the point, squared.
01704  *
01705  *  'dir' need not have unit length.
01706  *
01707  *  @return
01708  *      Distance squared
01709  */
01710 double
01711 bn_distsq_line2_point2(const fastf_t *pt, const fastf_t *dir, const fastf_t *a)
01712 {
01713         LOCAL vect_t            f;
01714         register fastf_t        FdotD;
01715 
01716         VSUB2_2D( f, pt, a );
01717         if( (FdotD = sqrt(MAGSQ_2D(dir))) <= SMALL_FASTF )
01718                 return 0.0;
01719         FdotD = VDOT_2D( f, dir ) / FdotD;
01720         if( (FdotD = VDOT_2D( f, f ) - FdotD * FdotD ) <= SMALL_FASTF )
01721                 return(0.0);
01722         return( FdotD );
01723 }
01724 
01725 /**
01726  *                      B N _ A R E A _ O F _ T R I A N G L E
01727  *@brief
01728  *  Returns the area of a triangle.
01729  *  Algorithm by Jon Leech 3/24/89.
01730  */
01731 double
01732 bn_area_of_triangle(register const fastf_t *a, register const fastf_t *b, register const fastf_t *c)
01733 {
01734         register double t;
01735         register double area;
01736 
01737         t =     a[Y] * (b[Z] - c[Z]) -
01738                 b[Y] * (a[Z] - c[Z]) +
01739                 c[Y] * (a[Z] - b[Z]);
01740         area  = t*t;
01741         t =     a[Z] * (b[X] - c[X]) -
01742                 b[Z] * (a[X] - c[X]) +
01743                 c[Z] * (a[X] - b[X]);
01744         area += t*t;
01745         t =     a[X] * (b[Y] - c[Y]) -
01746                 b[X] * (a[Y] - c[Y]) +
01747                 c[X] * (a[Y] - b[Y]);
01748         area += t*t;
01749 
01750         return( 0.5 * sqrt(area) );
01751 }
01752 
01753 
01754 /**
01755  *                      B N _ I S E C T _ P T _ L S E G
01756  *@brief
01757  * Intersect a point P with the line segment defined by two distinct
01758  * points A and B.
01759  *
01760  *
01761  * @return      -2      P on line AB but outside range of AB,
01762  *                      dist = distance from A to P on line.
01763  * @return      -1      P not on line of AB within tolerance
01764  * @return      1       P is at A
01765  * @return      2       P is at B
01766  * @return      3       P is on AB, dist = distance from A to P on line.
01767 @verbatim
01768      B *
01769         |
01770      P'*-tol-*P
01771         |    /  _
01772      dist  /   /|
01773         |  /   /
01774         | /   / AtoP
01775         |/   /
01776      A *   /
01777  
01778         tol = distance limit from line to pt P;
01779         dist = parametric distance from A to P' (in terms of A to B)
01780 @endverbatim
01781  *
01782  * @param p     point
01783  * @param a     start of lseg
01784  * @param b     end of lseg
01785  * @param tol   tolerance values
01786  * @param[out] dist     parametric distance from A to P' (in terms of A to B)
01787  */
01788 int bn_isect_pt_lseg(fastf_t *dist,
01789                      const fastf_t *a,
01790                      const fastf_t *b,
01791                      const fastf_t *p,
01792                      const struct bn_tol *tol)
01793                                         /* distance along line from A to P */
01794                                         /* points for line and intersect */
01795 
01796 {
01797         vect_t  AtoP,
01798                 BtoP,
01799                 AtoB,
01800                 ABunit; /* unit vector from A to B */
01801         fastf_t APprABunit;     /* Mag of projection of AtoP onto ABunit */
01802         fastf_t distsq;
01803 
01804         BN_CK_TOL(tol);
01805 
01806         VSUB2(AtoP, p, a);
01807         if (MAGSQ(AtoP) < tol->dist_sq)
01808                 return(1);      /* P at A */
01809 
01810         VSUB2(BtoP, p, b);
01811         if (MAGSQ(BtoP) < tol->dist_sq)
01812                 return(2);      /* P at B */
01813 
01814         VSUB2(AtoB, b, a);
01815         VMOVE(ABunit, AtoB);
01816         distsq = MAGSQ(ABunit);
01817         if( distsq < tol->dist_sq )
01818                 return -1;      /* A equals B, and P isn't there */
01819         distsq = 1/sqrt(distsq);
01820         VSCALE( ABunit, ABunit, distsq );
01821 
01822         /* Similar to bn_dist_line_pt, except we
01823          * never actually have to do the sqrt that the other routine does.
01824          */
01825 
01826         /* find dist as a function of ABunit, actually the projection
01827          * of AtoP onto ABunit
01828          */
01829         APprABunit = VDOT(AtoP, ABunit);
01830 
01831         /* because of pythgorean theorem ... */
01832         distsq = MAGSQ(AtoP) - APprABunit * APprABunit;
01833         if (distsq > tol->dist_sq)
01834                 return(-1);     /* dist pt to line too large */
01835 
01836         /* Distance from the point to the line is within tolerance. */
01837         *dist = VDOT(AtoP, AtoB) / MAGSQ(AtoB);
01838 
01839         if (*dist > 1.0 || *dist < 0.0) /* P outside AtoB */
01840                 return(-2);
01841 
01842         return(3);      /* P on AtoB */
01843 }
01844 
01845 /**
01846  *                      B N _ I S E C T _ P T 2 _ L S E G 2
01847  * @brief
01848  * Intersect a point P with the line segment defined by two distinct
01849  * points A and B.
01850  *
01851  * 
01852  * @return      -2      P on line AB but outside range of AB,
01853  *                      dist = distance from A to P on line.
01854  * @return      -1      P not on line of AB within tolerance
01855  * @return      1       P is at A
01856  * @return      2       P is at B
01857  * @return      3       P is on AB, dist = distance from A to P on line.
01858 @verbatim
01859      B *
01860         |
01861      P'*-tol-*P
01862         |    /  _
01863      dist  /   /|
01864         |  /   /
01865         | /   / AtoP
01866         |/   /
01867      A *   /
01868  
01869         tol = distance limit from line to pt P;
01870         dist = distance from A to P'
01871 @endverbatim
01872  */
01873 int
01874 bn_isect_pt2_lseg2(fastf_t *dist, const fastf_t *a, const fastf_t *b, const fastf_t *p, const struct bn_tol *tol)
01875                                         /* distance along line from A to P */
01876                                         /* points for line and intersect */
01877 
01878 {
01879         vect_t  AtoP,
01880                 BtoP,
01881                 AtoB,
01882                 ABunit; /* unit vector from A to B */
01883         fastf_t APprABunit;     /* Mag of projection of AtoP onto ABunit */
01884         fastf_t distsq;
01885 
01886         BN_CK_TOL(tol);
01887 
01888         VSUB2_2D(AtoP, p, a);
01889         if (MAGSQ_2D(AtoP) < tol->dist_sq)
01890                 return(1);      /* P at A */
01891 
01892         VSUB2_2D(BtoP, p, b);
01893         if (MAGSQ_2D(BtoP) < tol->dist_sq)
01894                 return(2);      /* P at B */
01895 
01896         VSUB2_2D(AtoB, b, a);
01897         VMOVE_2D(ABunit, AtoB);
01898         distsq = MAGSQ_2D(ABunit);
01899         if( distsq < tol->dist_sq )  {
01900                 if( bu_debug & BU_DEBUG_MATH )  {
01901                         bu_log("distsq A=%g\n", distsq);
01902                 }
01903                 return -1;      /* A equals B, and P isn't there */
01904         }
01905         distsq = 1/sqrt(distsq);
01906         VSCALE_2D( ABunit, ABunit, distsq );
01907 
01908         /* Similar to bn_dist_line_pt, except we
01909          * never actually have to do the sqrt that the other routine does.
01910          */
01911 
01912         /* find dist as a function of ABunit, actually the projection
01913          * of AtoP onto ABunit
01914          */
01915         APprABunit = VDOT_2D(AtoP, ABunit);
01916 
01917         /* because of pythgorean theorem ... */
01918         distsq = MAGSQ_2D(AtoP) - APprABunit * APprABunit;
01919         if (distsq > tol->dist_sq) {
01920                 if( bu_debug & BU_DEBUG_MATH )  {
01921                         V2PRINT("ABunit", ABunit);
01922                         bu_log("distsq B=%g\n", distsq);
01923                 }
01924                 return(-1);     /* dist pt to line too large */
01925         }
01926 
01927         /* Distance from the point to the line is within tolerance. */
01928         *dist = VDOT_2D(AtoP, AtoB) / MAGSQ_2D(AtoB);
01929 
01930         if (*dist > 1.0 || *dist < 0.0) /* P outside AtoB */
01931                 return(-2);
01932 
01933         return(3);      /* P on AtoB */
01934 }
01935 
01936 /**
01937  *                      B N _ D I S T _ P T 3 _ L S E G 3
01938  *@brief
01939  *  Find the distance from a point P to a line segment described
01940  *  by the two endpoints A and B, and the point of closest approach (PCA).
01941 @verbatim
01942  *
01943  *                      P
01944  *                     *
01945  *                    /.
01946  *                   / .
01947  *                  /  .
01948  *                 /   . (dist)
01949  *                /    .
01950  *               /     .
01951  *              *------*--------*
01952  *              A      PCA      B
01953 @endverbatim
01954  *  
01955  * @return      0       P is within tolerance of lseg AB.  *dist isn't 0: (SPECIAL!!!)
01956  *                *dist = parametric dist = |PCA-A| / |B-A|.  pca=computed.
01957  * @return      1       P is within tolerance of point A.  *dist = 0, pca=A.
01958  * @return      2       P is within tolerance of point B.  *dist = 0, pca=B.
01959  * @return      3       P is to the "left" of point A.  *dist=|P-A|, pca=A.
01960  * @return      4       P is to the "right" of point B.  *dist=|P-B|, pca=B.
01961  * @return      5       P is "above/below" lseg AB.  *dist=|PCA-P|, pca=computed.
01962  *
01963  * This routine was formerly called bn_dist_pt_lseg().
01964  *
01965  * XXX For efficiency, a version of this routine that provides the
01966  * XXX distance squared would be faster.
01967  */
01968 int
01969 bn_dist_pt3_lseg3(fastf_t *dist,
01970                   fastf_t *pca,
01971                   const fastf_t *a,
01972                   const fastf_t *b,
01973                   const fastf_t *p,
01974                   const struct bn_tol *tol)
01975 {
01976         vect_t  PtoA;           /* P-A */
01977         vect_t  PtoB;           /* P-B */
01978         vect_t  AtoB;           /* B-A */
01979         fastf_t P_A_sq;         /* |P-A|**2 */
01980         fastf_t P_B_sq;         /* |P-B|**2 */
01981         fastf_t B_A;            /* |B-A| */
01982         fastf_t t;              /* distance along ray of projection of P */
01983 
01984         BN_CK_TOL(tol);
01985 
01986         if( bu_debug & BU_DEBUG_MATH )  {
01987                 bu_log("bn_dist_pt3_lseg3() a=(%g,%g,%g) b=(%g,%g,%g)\n\tp=(%g,%g,%g), tol->dist=%g sq=%g\n",
01988                         V3ARGS(a),
01989                         V3ARGS(b),
01990                         V3ARGS(p),
01991                         tol->dist, tol->dist_sq );
01992         }
01993 
01994         /* Check proximity to endpoint A */
01995         VSUB2(PtoA, p, a);
01996         if( (P_A_sq = MAGSQ(PtoA)) < tol->dist_sq )  {
01997                 /* P is within the tol->dist radius circle around A */
01998                 VMOVE( pca, a );
01999                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  at A\n");
02000                 *dist = 0.0;
02001                 return 1;
02002         }
02003 
02004         /* Check proximity to endpoint B */
02005         VSUB2(PtoB, p, b);
02006         if( (P_B_sq = MAGSQ(PtoB)) < tol->dist_sq )  {
02007                 /* P is within the tol->dist radius circle around B */
02008                 VMOVE( pca, b );
02009                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  at B\n");
02010                 *dist = 0.0;
02011                 return 2;
02012         }
02013 
02014         VSUB2(AtoB, b, a);
02015         B_A = sqrt( MAGSQ(AtoB) );
02016 
02017         /* compute distance (in actual units) along line to PROJECTION of
02018          * point p onto the line: point pca
02019          */
02020         t = VDOT(PtoA, AtoB) / B_A;
02021         if( bu_debug & BU_DEBUG_MATH )  {
02022                 bu_log("bn_dist_pt3_lseg3() B_A=%g, t=%g\n",
02023                         B_A, t );
02024         }
02025 
02026         if( t <= 0 )  {
02027                 /* P is "left" of A */
02028                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  left of A\n");
02029                 VMOVE( pca, a );
02030                 *dist = sqrt(P_A_sq);
02031                 return 3;
02032         }
02033         if( t < B_A )  {
02034                 /* PCA falls between A and B */
02035                 register fastf_t        dsq;
02036                 fastf_t                 param_dist;     /* parametric dist */
02037 
02038                 /* Find PCA */
02039                 param_dist = t / B_A;           /* Range 0..1 */
02040                 VJOIN1(pca, a, param_dist, AtoB);
02041 
02042                 /* Find distance from PCA to line segment (Pythagorus) */
02043                 if( (dsq = P_A_sq - t * t ) <= tol->dist_sq )  {
02044                         if( bu_debug & BU_DEBUG_MATH )  bu_log("  ON lseg\n");
02045                         /* Distance from PCA to lseg is zero, give param instead */
02046                         *dist = param_dist;     /* special! */
02047                         return 0;
02048                 }
02049                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  closest to lseg\n");
02050                 *dist = sqrt(dsq);
02051                 return 5;
02052         }
02053         /* P is "right" of B */
02054         if( bu_debug & BU_DEBUG_MATH )  bu_log("  right of B\n");
02055         VMOVE(pca, b);
02056         *dist = sqrt(P_B_sq);
02057         return 4;
02058 }
02059 
02060 /**
02061  *                      B N _ D I S T _ P T 2 _ L S E G 2
02062  *@brief
02063  *  Find the distance from a point P to a line segment described
02064  *  by the two endpoints A and B, and the point of closest approach (PCA).
02065 @verbatim
02066  *                      P
02067  *                     *
02068  *                    /.
02069  *                   / .
02070  *                  /  .
02071  *                 /   . (dist)
02072  *                /    .
02073  *               /     .
02074  *              *------*--------*
02075  *              A      PCA      B
02076 @endverbatim
02077  *  There are six distinct cases, with these return codes -
02078  * @return      0       P is within tolerance of lseg AB.  *dist isn't 0: (SPECIAL!!!)
02079  *                *dist = parametric dist = |PCA-A| / |B-A|.  pca=computed.
02080  * @return      1       P is within tolerance of point A.  *dist = 0, pca=A.
02081  * @return      2       P is within tolerance of point B.  *dist = 0, pca=B.
02082  * @return      3       P is to the "left" of point A.  *dist=|P-A|**2, pca=A.
02083  * @return      4       P is to the "right" of point B.  *dist=|P-B|**2, pca=B.
02084  * @return      5       P is "above/below" lseg AB.  *dist=|PCA-P|**2, pca=computed.
02085  *
02086  *
02087  *  Patterned after bn_dist_pt3_lseg3().
02088  */
02089 int
02090 bn_dist_pt2_lseg2(fastf_t *dist_sq, fastf_t *pca, const fastf_t *a, const fastf_t *b, const fastf_t *p, const struct bn_tol *tol)
02091 {
02092         vect_t  PtoA;           /* P-A */
02093         vect_t  PtoB;           /* P-B */
02094         vect_t  AtoB;           /* B-A */
02095         fastf_t P_A_sq;         /* |P-A|**2 */
02096         fastf_t P_B_sq;         /* |P-B|**2 */
02097         fastf_t B_A;            /* |B-A| */
02098         fastf_t t;              /* distance along ray of projection of P */
02099 
02100         BN_CK_TOL(tol);
02101 
02102         if( bu_debug & BU_DEBUG_MATH )  {
02103                 bu_log("bn_dist_pt3_lseg3() a=(%g,%g,%g) b=(%g,%g,%g)\n\tp=(%g,%g,%g), tol->dist=%g sq=%g\n",
02104                         V3ARGS(a),
02105                         V3ARGS(b),
02106                         V3ARGS(p),
02107                         tol->dist, tol->dist_sq );
02108         }
02109 
02110 
02111         /* Check proximity to endpoint A */
02112         VSUB2_2D(PtoA, p, a);
02113         if( (P_A_sq = MAGSQ_2D(PtoA)) < tol->dist_sq )  {
02114                 /* P is within the tol->dist radius circle around A */
02115                 V2MOVE( pca, a );
02116                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  at A\n");
02117                 *dist_sq = 0.0;
02118                 return 1;
02119         }
02120 
02121         /* Check proximity to endpoint B */
02122         VSUB2_2D(PtoB, p, b);
02123         if( (P_B_sq = MAGSQ_2D(PtoB)) < tol->dist_sq )  {
02124                 /* P is within the tol->dist radius circle around B */
02125                 V2MOVE( pca, b );
02126                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  at B\n");
02127                 *dist_sq = 0.0;
02128                 return 2;
02129         }
02130 
02131         VSUB2_2D(AtoB, b, a);
02132         B_A = sqrt( MAGSQ_2D(AtoB) );
02133 
02134         /* compute distance (in actual units) along line to PROJECTION of
02135          * point p onto the line: point pca
02136          */
02137         t = VDOT_2D(PtoA, AtoB) / B_A;
02138         if( bu_debug & BU_DEBUG_MATH )  {
02139                 bu_log("bn_dist_pt3_lseg3() B_A=%g, t=%g\n",
02140                         B_A, t );
02141         }
02142 
02143         if( t <= 0 )  {
02144                 /* P is "left" of A */
02145                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  left of A\n");
02146                 V2MOVE( pca, a );
02147                 *dist_sq = P_A_sq;
02148                 return 3;
02149         }
02150         if( t < B_A )  {
02151                 /* PCA falls between A and B */
02152                 register fastf_t        dsq;
02153                 fastf_t                 param_dist;     /* parametric dist */
02154 
02155                 /* Find PCA */
02156                 param_dist = t / B_A;           /* Range 0..1 */
02157                 V2JOIN1(pca, a, param_dist, AtoB);
02158 
02159                 /* Find distance from PCA to line segment (Pythagorus) */
02160                 if( (dsq = P_A_sq - t * t ) <= tol->dist_sq )  {
02161                         if( bu_debug & BU_DEBUG_MATH )  bu_log("  ON lseg\n");
02162                         /* Distance from PCA to lseg is zero, give param instead */
02163                         *dist_sq = param_dist;  /* special! Not squared. */
02164                         return 0;
02165                 }
02166                 if( bu_debug & BU_DEBUG_MATH )  bu_log("  closest to lseg\n");
02167                 *dist_sq = dsq;
02168                 return 5;
02169         }
02170         /* P is "right" of B */
02171         if( bu_debug & BU_DEBUG_MATH )  bu_log("  right of B\n");
02172         V2MOVE(pca, b);
02173         *dist_sq = P_B_sq;
02174         return 4;
02175 }
02176 
02177 /**
02178  *                      B N _ R O T A T E _ B B O X
02179  *@brief
02180  *  Transform a bounding box (RPP) by the given 4x4 matrix.
02181  *  There are 8 corners to the bounding RPP.
02182  *  Each one needs to be transformed and min/max'ed.
02183  *  This is not minimal, but does fully contain any internal object,
02184  *  using an axis-aligned RPP.
02185  */
02186 void
02187 bn_rotate_bbox(fastf_t *omin, fastf_t *omax, const fastf_t *mat, const fastf_t *imin, const fastf_t *imax)
02188 {
02189         point_t local;          /* vertex point in local coordinates */
02190         point_t model;          /* vertex point in model coordinates */
02191 
02192 #define ROT_VERT( a, b, c )  \
02193         VSET( local, a[X], b[Y], c[Z] ); \
02194         MAT4X3PNT( model, mat, local ); \
02195         VMINMAX( omin, omax, model ) \
02196 
02197         ROT_VERT( imin, imin, imin );
02198         ROT_VERT( imin, imin, imax );
02199         ROT_VERT( imin, imax, imin );
02200         ROT_VERT( imin, imax, imax );
02201         ROT_VERT( imax, imin, imin );
02202         ROT_VERT( imax, imin, imax );
02203         ROT_VERT( imax, imax, imin );
02204         ROT_VERT( imax, imax, imax );
02205 #undef ROT_VERT
02206 }
02207 
02208 /**
02209  *                      B N _ R O T A T E _ P L A N E
02210  *@brief
02211  *  Transform a plane equation by the given 4x4 matrix.
02212  */
02213 void
02214 bn_rotate_plane(fastf_t *oplane, const fastf_t *mat, const fastf_t *iplane)
02215 {
02216         point_t         orig_pt;
02217         point_t         new_pt;
02218 
02219         /* First, pick a point that lies on the original halfspace */
02220         VSCALE( orig_pt, iplane, iplane[3] );
02221 
02222         /* Transform the surface normal */
02223         MAT4X3VEC( oplane, mat, iplane );
02224 
02225         /* Transform the point from original to new halfspace */
02226         MAT4X3PNT( new_pt, mat, orig_pt );
02227 
02228         /*
02229          *  The transformed normal is all that is required.
02230          *  The new distance is found from the transformed point on the plane.
02231          */
02232         oplane[3] = VDOT( new_pt, oplane );
02233 }
02234 
02235 /**
02236  *                      B N _ C O P L A N A R
02237  *@brief
02238  *  Test if two planes are identical.  If so, their dot products will be
02239  *  either +1 or -1, with the distance from the origin equal in magnitude.
02240  *
02241  *
02242  * @return      -1      not coplanar, parallel but distinct
02243  * @return       0      not coplanar, not parallel.  Planes intersect.
02244  * @return      +1      coplanar, same normal direction
02245  * @return      +2      coplanar, opposite normal direction
02246  */
02247 int
02248 bn_coplanar(const fastf_t *a, const fastf_t *b, const struct bn_tol *tol)
02249 {
02250         register fastf_t        f;
02251         register fastf_t        dot;
02252 
02253         BN_CK_TOL(tol);
02254 
02255         /* Check to see if the planes are parallel */
02256         dot = VDOT( a, b );
02257         if( dot >= 0 )  {
02258                 /* Normals head in generally the same directions */
02259                 if( dot < tol->para )
02260                         return(0);      /* Planes intersect */
02261 
02262                 /* Planes have "exactly" the same normal vector */
02263                 f = a[3] - b[3];
02264                 if( NEAR_ZERO( f, tol->dist ) )  {
02265                         return(1);      /* Coplanar, same direction */
02266                 }
02267                 return(-1);             /* Parallel but distinct */
02268         }
02269         /* Normals head in generally opposite directions */
02270         if( -dot < tol->para )
02271                 return(0);              /* Planes intersect */
02272 
02273         /* Planes have "exactly" opposite normal vectors */
02274         f = a[3] + b[3];
02275         if( NEAR_ZERO( f, tol->dist ) )  {
02276                 return(2);              /* Coplanar, opposite directions */
02277         }
02278         return(-1);                     /* Parallel but distinct */
02279 }
02280 
02281 /**
02282  *                      B N _ A N G L E _ M E A S U R E
02283  *
02284  *  Using two perpendicular vectors (x_dir and y_dir) which lie
02285  *  in the same plane as 'vec', return the angle (in radians) of 'vec'
02286  *  from x_dir, going CCW around the perpendicular x_dir CROSS y_dir.
02287  *
02288  *  Trig note -
02289  *
02290  *  theta = atan2(x,y) returns an angle in the range -pi to +pi.
02291  *  Here, we need an angle in the range of 0 to 2pi.
02292  *  This could be implemented by adding 2pi to theta when theta is negative,
02293  *  but this could have nasty numeric ambiguity right in the vicinity
02294  *  of theta = +pi, which is a very critical angle for the applications using
02295  *  this routine.
02296  *  So, an alternative formulation is to compute gamma = atan2(-x,-y),
02297  *  and then theta = gamma + pi.  Now, any error will occur in the
02298  *  vicinity of theta = 0, which can be handled much more readily.
02299  *
02300  *  If theta is negative, or greater than two pi,
02301  *  wrap it around.
02302  *  These conditions only occur if there are problems in atan2().
02303  *
02304  * 
02305  * @return      vec == x_dir returns 0,
02306  * @return      vec == y_dir returns pi/2,
02307  * @return      vec == -x_dir returns pi,
02308  * @return      vec == -y_dir returns 3*pi/2.
02309  *
02310  *  In all cases, the returned value is between 0 and bn_twopi.
02311  */
02312 double
02313 bn_angle_measure(fastf_t *vec, const fastf_t *x_dir, const fastf_t *y_dir)
02314 {
02315         fastf_t         xproj, yproj;
02316         fastf_t         gamma;
02317         fastf_t         ang;
02318 
02319         xproj = -VDOT( vec, x_dir );
02320         yproj = -VDOT( vec, y_dir );
02321         gamma = atan2( yproj, xproj );  /* -pi..+pi */
02322         ang = bn_pi + gamma;            /* 0..+2pi */
02323         if( ang < 0 )  {
02324                 do {
02325                         ang += bn_twopi;
02326                 } while( ang < 0 );
02327         } else if( ang > bn_twopi )  {
02328                 do {
02329                         ang -= bn_twopi;
02330                 } while( ang > bn_twopi );
02331         }
02332         if( ang < 0 || ang > bn_twopi )  bu_bomb("bn_angle_measure() angle out of range\n");
02333         return ang;
02334 }
02335 
02336 /**
02337  *                      B N _ D I S T _ P T 3 _ A L O N G _ L I N E 3
02338  *@brief
02339  *  Return the parametric distance t of a point X along a line defined
02340  *  as a ray, i.e. solve X = P + t * D.
02341  *  If the point X does not lie on the line, then t is the distance of
02342  *  the perpendicular projection of point X onto the line.
02343  */
02344 double
02345 bn_dist_pt3_along_line3(const fastf_t *p, const fastf_t *d, const fastf_t *x)
02346 {
02347         vect_t  x_p;
02348 
02349         VSUB2( x_p, x, p );
02350         return VDOT( x_p, d );
02351 }
02352 
02353 
02354 /**
02355  *                      B N _ D I S T _ P T 2 _ A L O N G _ L I N E 2
02356  *@brief
02357  *  Return the parametric distance t of a point X along a line defined
02358  *  as a ray, i.e. solve X = P + t * D.
02359  *  If the point X does not lie on the line, then t is the distance of
02360  *  the perpendicular projection of point X onto the line.
02361  */
02362 double
02363 bn_dist_pt2_along_line2(const fastf_t *p, const fastf_t *d, const fastf_t *x)
02364 {
02365         vect_t  x_p;
02366         double  ret;
02367 
02368         VSUB2_2D( x_p, x, p );
02369         ret = VDOT_2D( x_p, d );
02370         if( bu_debug & BU_DEBUG_MATH )  {
02371                 bu_log("bn_dist_pt2_along_line2() p=(%g, %g), d=(%g, %g), x=(%g, %g) ret=%g\n",
02372                         V2ARGS(p),
02373                         V2ARGS(d),
02374                         V2ARGS(x),
02375                         ret );
02376         }
02377         return ret;
02378 }
02379 
02380 /**
02381  *
02382  * @return      1       if left <= mid <= right
02383  * @return      0       if mid is not in the range.
02384  */
02385 int
02386 bn_between(double left, double mid, double right, const struct bn_tol *tol)
02387 {
02388         BN_CK_TOL(tol);
02389 
02390         if( left < right )  {
02391                 if( NEAR_ZERO(left-right, tol->dist*0.1) )  {
02392                         left -= tol->dist*0.1;
02393                         right += tol->dist*0.1;
02394                 }
02395                 if( mid < left || mid > right )  goto fail;
02396                 return 1;
02397         }
02398         /* The 'right' value is lowest */
02399         if( NEAR_ZERO(left-right, tol->dist*0.1) )  {
02400                 right -= tol->dist*0.1;
02401                 left += tol->dist*0.1;
02402         }
02403         if( mid < right || mid > left )  goto fail;
02404         return 1;
02405 fail:
02406         if( bu_debug & BU_DEBUG_MATH )  {
02407                 bu_log("bn_between( %.17e, %.17e, %.17e ) ret=0 FAIL\n",
02408                         left, mid, right);
02409         }
02410         return 0;
02411 }
02412 
02413 /**
02414  *                      B N _ D O E S _ R A Y _ I S E C T _ T R I
02415  *
02416  * 
02417  * @return      0       No intersection
02418  * @return      1       Intersection, 'inter' has intersect point.
02419  */
02420 int
02421 bn_does_ray_isect_tri(
02422         const point_t pt,
02423         const vect_t dir,
02424         const point_t V,
02425         const point_t A,
02426         const point_t B,
02427         point_t inter)                  /* output variable */
02428 {
02429         vect_t VP, VA, VB, AB, AP, N;
02430         fastf_t NdotDir;
02431         plane_t pl;
02432         fastf_t dist;
02433 
02434         /* insersect with plane */
02435 
02436         VSUB2( VA, A, V );
02437         VSUB2( VB, B, V );
02438         VCROSS( pl, VA, VB );
02439         VUNITIZE( pl );
02440 
02441         NdotDir = VDOT( pl, dir );
02442         if( NEAR_ZERO( NdotDir, SMALL_FASTF ) )
02443                 return( 0 );
02444 
02445         pl[3] = VDOT( pl, V );
02446 
02447         dist = (pl[3] - VDOT( pl, pt ))/NdotDir;
02448         VJOIN1( inter, pt, dist, dir );
02449 
02450         /* determine if point is within triangle */
02451         VSUB2( VP, inter, V );
02452         VCROSS( N, VA, VP );
02453         if( VDOT( N, pl ) < 0.0 )
02454                 return( 0 );
02455 
02456         VCROSS( N, VP, VB );
02457         if( VDOT( N, pl ) < 0.0 )
02458                 return( 0 );
02459 
02460         VSUB2( AB, B, A );
02461         VSUB2( AP, inter, A );
02462         VCROSS( N, AB, AP );
02463         if( VDOT( N, pl ) < 0.0 )
02464                 return( 0 );
02465 
02466         return( 1 );
02467 }
02468 
02469 #if 0
02470 /*
02471  *                      B N _ I S E C T _ R A Y _ T R I
02472  *
02473  *  Intersect a infinite ray with a triangle specified by 3 points.
02474  *  From: "Graphics Gems" ed Andrew S. Glassner:
02475  *              "An Efficient Ray-Polygon Intersection"  P 390
02476  *
02477  *            o A
02478  *            |\
02479  *            | \
02480  *            |  \
02481  *            |   \
02482  *         _  |  o \
02483  *   alpha|   |  P  \
02484  *        |   |      \
02485  *        |_  o-------o
02486  *             V       B
02487  *
02488  *            |__|
02489  *              beta
02490  *
02491  *  The intersection point P is computed by determining 2 quantities,
02492  *  (alpha,beta) which indicate the parametric distance along VA and VB
02493  *  respectively.  The intersection point is thus:
02494  *
02495  *      P = V + (alpha * VA) + (beta * VB)
02496  *
02497  *  Return:
02498  *      0       Miss
02499  *      1       Hit, incoming
02500  *      -1      Hit, outgoing
02501  *
02502  *    If Hit, the following parameters are also set:
02503  *      dist    parametric distance to the triangle intercept.
02504  *      N       If non-null, surface normal of triangle
02505  *      Alpha   If non-null, parametric distance along VA
02506  *      Beta    If non-null, parametric distance along VB
02507 
02508  *  The parameters Alpha and Beta may be null, otherwise they will be
02509  *      set.
02510  */
02511 int
02512 bn_isect_ray_tri(dist_p, N_p, Alpha_p, Beta_p, pt, dir, V, A, B)
02513 fastf_t *dist_p;
02514 fastf_t *N_p;
02515 fastf_t *Alpha_p;
02516 fastf_t *Beta_p;
02517 const point_t pt;
02518 const vect_t dir;
02519 const point_t V;
02520 const point_t A;
02521 const point_t B;
02522 {
02523         vect_t VA;      /* V -> A vector */
02524         vect_t VB;      /* V -> B vector */
02525         vect_t pt_V;    /* Ray_origin -> V  vector */
02526         vect_t N;       /* Triangle Normal */
02527         vect_t VPP;     /* perpendicular to vector V -> P */
02528         fastf_t alpha;  /* parametric distance along VA */
02529         fastf_t beta;   /* parametric distance along VB */
02530         fastf_t NdotDir;
02531         fastf_t entleave;
02532         fastf_t k;
02533 
02534         /* form edge vectors of triangle */
02535         VSUB2(VB, B, V);
02536         VSUB2(VA, A, V);
02537 
02538         /* form triangle normal */
02539         VCROSS( N, VA, VB );
02540 
02541         NdotDir = VDOT( N, dir );
02542         if (fabs(NdotDir) < SQRT_SMALL_FASTF)
02543                 return 0;       /* ray parallel to triangle */
02544 
02545         if (NdotDir >= 0.0) entleave = -1;      /* leaving */
02546         else entleave = 1;                      /* entering */
02547 
02548         VSUB2( pt_V, V, pt );
02549         VCROSS( VPP, pt_V, dir );
02550 
02551         /* alpha is projection of VPP onto VA (not necessaily in plane)
02552          * If alpha < 0.0 then p is "before" point V on line V->A
02553          * No-one can figure out why alpha > NdotDir is important.
02554          */
02555         alpha = VDOT( VA, VPP ) * entleave;
02556         if (alpha < 0.0 || alpha > fabs(NdotDir) ) return 0;
02557 
02558 
02559         /* beta is projection of VPP onto VB (not necessaily in plane) */
02560         beta = VDOT(VB, VPP ) * (-1 * entleave);
02561         if (beta < 0.0 || beta > fabs(NdotDir)) return 0;
02562 
02563         k = VDOT(VPP, N) / NdotDir;
02564 
02565         if (dist_p) *dist_p = k;
02566         if (N_p) {
02567                 VUNITIZE(N);
02568                 VMOVE(N_p, N);
02569         }
02570         if (Alpha_p) *Alpha_p = alpha;
02571         if (Beta_p) *Beta_p = beta;
02572 
02573         return entleave;
02574 }
02575 #endif
02576 
02577 /**
02578  *                      B N _ H L F _ C L A S S
02579  *@brief
02580  *  Classify a halfspace, specified by its plane equation,
02581  *  against a bounding RPP.
02582  *
02583  *
02584  * @return      BN_CLASSIFY_INSIDE
02585  * @return      BN_CLASSIFY_OVERLAPPING
02586  * @return      BN_CLASSIFY_OUTSIDE
02587  */
02588 int
02589 bn_hlf_class(const fastf_t *half_eqn, const fastf_t *min, const fastf_t *max, const struct bn_tol *tol)
02590 {
02591         int     class;  /* current classification */
02592         fastf_t d;
02593 
02594 #define CHECK_PT( x, y, z ) \
02595         d = (x)*half_eqn[0] + (y)*half_eqn[1] + (z)*half_eqn[2] - half_eqn[3];\
02596         if( d < -tol->dist ) { \
02597                 if( class == BN_CLASSIFY_OUTSIDE ) \
02598                         return BN_CLASSIFY_OVERLAPPING; \
02599                 else class = BN_CLASSIFY_INSIDE; \
02600         } else if( d > tol->dist ) { \
02601                 if( class == BN_CLASSIFY_INSIDE ) \
02602                         return BN_CLASSIFY_OVERLAPPING; \
02603                 else class = BN_CLASSIFY_OUTSIDE; \
02604         } else return BN_CLASSIFY_OVERLAPPING
02605 
02606         class = BN_CLASSIFY_UNIMPLEMENTED;
02607         CHECK_PT( min[X], min[Y], min[Z] );
02608         CHECK_PT( min[X], min[Y], max[Z] );
02609         CHECK_PT( min[X], max[Y], min[Z] );
02610         CHECK_PT( min[X], max[Y], max[Z] );
02611         CHECK_PT( max[X], min[Y], min[Z] );
02612         CHECK_PT( max[X], min[Y], max[Z] );
02613         CHECK_PT( max[X], max[Y], min[Z] );
02614         CHECK_PT( max[X], max[Y], max[Z] );
02615         if( class == BN_CLASSIFY_UNIMPLEMENTED )
02616                 bu_log( "bn_hlf_class: error in implementation\
02617 min = (%g, %g, %g), max = (%g, %g, %g), half_eqn = (%d, %d, %d, %d)\n",
02618                         V3ARGS(min), V3ARGS(max), V3ARGS(half_eqn),
02619                         half_eqn[3]);
02620         return class;
02621 }
02622 
02623 /**             B N _ D I S T S Q _ L I N E 3 _ L I N E 3
02624  *@brief
02625  * Calculate the square of the distance of closest approach for two lines.
02626  *
02627  * The lines are specifed as a point and a vector each. 
02628  * The vectors need not be unit length.
02629  * P and d define one line; Q and e define the other.
02630  *
02631  * 
02632  * @return      0 - normal return
02633  * @return      1 - lines are parallel, dist[0] is set to 0.0
02634  *
02635  * Output values:
02636  * dist[0] is the parametric distance along the first line P + dist[0] * d of the PCA
02637  * dist[1] is the parametric distance along the second line Q + dist[1] * e of the PCA
02638  * dist[3] is the square of the distance between the points of closest approach
02639  * pt1 is the point of closest approach on the first line
02640  * pt2 is the point of closest approach on the second line
02641  *
02642  * This algoritm is based on expressing the distance sqaured, taking partials with respect
02643  * to the two unknown parameters (dist[0] and dist[1]), seeting the two partails equal to 0,
02644  * and solving the two simutaneous equations
02645  */
02646 int
02647 bn_distsq_line3_line3(fastf_t *dist, fastf_t *P, fastf_t *d_in, fastf_t *Q, fastf_t *e_in, fastf_t *pt1, fastf_t *pt2)
02648 {
02649         fastf_t de, denom;
02650         vect_t diff, PmQ, tmp;
02651         vect_t d, e;
02652         fastf_t len_e, inv_len_e, len_d, inv_len_d;
02653         int ret=0;
02654 
02655         len_e = MAGNITUDE( e_in );
02656         if( NEAR_ZERO( len_e, SMALL_FASTF ) )
02657                 bu_bomb( "bn_distsq_line3_line3() called with zero length vector!!!\n");
02658         inv_len_e = 1.0 / len_e;
02659 
02660         len_d = MAGNITUDE( d_in );
02661         if( NEAR_ZERO( len_d, SMALL_FASTF ) )
02662                 bu_bomb( "bn_distsq_line3_line3() called with zero length vector!!!\n");
02663         inv_len_d = 1.0 / len_d;
02664 
02665         VSCALE( e, e_in, inv_len_e );
02666         VSCALE( d, d_in, inv_len_d );
02667         de = VDOT( d, e );
02668 
02669         if( NEAR_ZERO( de, SMALL_FASTF ) )
02670         {
02671                 /* lines are perpendicular */
02672                 dist[0] = VDOT( Q, d ) - VDOT( P, d );
02673                 dist[1] = VDOT( P, e ) - VDOT( Q, e );
02674         }
02675         else
02676         {
02677                 VSUB2( PmQ, P, Q );
02678                 denom = 1.0 - de*de;
02679                 if( NEAR_ZERO( denom, SMALL_FASTF ) )
02680                 {
02681                         /* lines are parallel */
02682                         dist[0] = 0.0;
02683                         dist[1] = VDOT( PmQ, d );
02684                         ret = 1;
02685                 }
02686                 else
02687                 {
02688                         VBLEND2( tmp, 1.0, e, -de, d );
02689                         dist[1] = VDOT( PmQ, tmp )/denom;
02690                         dist[0] = dist[1] * de - VDOT( PmQ, d );
02691                 }
02692         }
02693         VJOIN1( pt1, P, dist[0], d );
02694         VJOIN1( pt2, Q, dist[1], e );
02695         VSUB2( diff, pt1, pt2 );
02696         dist[0] *= inv_len_d;
02697         dist[1] *= inv_len_e;
02698         dist[2] =  MAGSQ( diff );
02699         return( ret );
02700 }
02701 
02702 /**
02703  *                      B N _ I S E C T _ P L A N E S
02704  *@brief
02705  * Calculates the point that is the minimum distance from all the
02706  * planes in the "planes" array.  If the planes intersect at a single point,
02707  * that point is the solution.
02708  *
02709  * The method used here is based on:
02710  *      An expression for the distance from a point to a plane is VDOT(pt,plane)-plane[H].
02711  *      Square that distance and sum for all planes to get the "total" distance.
02712  *      For minimum total distance, the partial derivatives of this expression (with
02713  *      respect to x, y, and z) must all be zero.
02714  *      This produces a set of three equations in three unknowns (x, y, and z).
02715  *      This routine sets up the three equations as [matrix][pt] = [hpq]
02716  *      and solves by inverting "matrix" into "inverse" and
02717  *      [pt] = [inverse][hpq].
02718  *
02719  * There is likely a more economical solution rather than matrix inversion, but
02720  * bn_mat_inv was handy at the time.
02721  *
02722  * Checks if these planes form a singular matrix and returns.
02723  *
02724  * @return      0 - all is well
02725  * @return      1 - planes form a singular matrix (no solution)
02726  */
02727 int
02728 bn_isect_planes(fastf_t *pt, const fastf_t (*planes)[4], const int pl_count)
02729 {
02730         mat_t matrix;
02731         mat_t inverse;
02732         vect_t hpq;
02733         fastf_t det;
02734         int i;
02735 
02736         if( bu_debug & BU_DEBUG_MATH )  {
02737                 bu_log( "bn_isect_planes:\n" );
02738                 for( i=0 ; i<pl_count ; i++ )
02739                 {
02740                         bu_log( "Plane #%d (%f %f %f %f)\n" , i , V4ARGS( planes[i] ) );
02741                 }
02742         }
02743 
02744         MAT_ZERO( matrix );
02745         VSET( hpq , 0.0 , 0.0 , 0.0 );
02746 
02747         for( i=0 ; i<pl_count ; i++ )
02748         {
02749                 matrix[0] += planes[i][X] * planes[i][X];
02750                 matrix[5] += planes[i][Y] * planes[i][Y];
02751                 matrix[10] += planes[i][Z] * planes[i][Z];
02752                 matrix[1] += planes[i][X] * planes[i][Y];
02753                 matrix[2] += planes[i][X] * planes[i][Z];
02754                 matrix[6] += planes[i][Y] * planes[i][Z];
02755                 hpq[X] += planes[i][X] * planes[i][H];
02756                 hpq[Y] += planes[i][Y] * planes[i][H];
02757                 hpq[Z] += planes[i][Z] * planes[i][H];
02758         }
02759 
02760         matrix[4] = matrix[1];
02761         matrix[8] = matrix[2];
02762         matrix[9] = matrix[6];
02763         matrix[15] = 1.0;
02764 
02765         /* Check that we don't have a singular matrix */
02766         det = bn_mat_determinant( matrix );
02767         if( NEAR_ZERO( det , SMALL_FASTF ) )
02768                 return( 1 );
02769 
02770         bn_mat_inv( inverse , matrix );
02771 
02772         MAT4X3PNT( pt , inverse , hpq );
02773 
02774         return( 0 );
02775 
02776 }
02777 
02778 /**
02779  *                      B N _ I S E C T _ L S E G _ R P P
02780  *@brief
02781  *  Intersect a line segment with a rectangular parallelpiped (RPP)
02782  *  that has faces parallel to the coordinate planes (a clipping RPP).
02783  *  The RPP is defined by a minimum point and a maximum point.
02784  *  This is a very close relative to rt_in_rpp() from librt/shoot.c
02785  *
02786  *
02787  * @return       0  if ray does not hit RPP,
02788  * @return      !0  if ray hits RPP.
02789  *
02790  *
02791  * @param[in,out] a     Start point of lseg
02792  * @param[in,out] b     End point of lseg
02793  * @param[in] min       min point of RPP
02794  * @param[in] max       amx point of RPP
02795  *
02796  *      if !0 was returned, "a" and "b" have been clipped to the RPP.
02797  */
02798 int
02799 bn_isect_lseg_rpp(fastf_t *a,
02800                   fastf_t *b,
02801                   register fastf_t *min,
02802                   register fastf_t *max)
02803 {
02804         auto vect_t     diff;
02805         register fastf_t *pt = &a[0];
02806         register fastf_t *dir = &diff[0];
02807         register int i;
02808         register double sv;
02809         register double st;
02810         register double mindist, maxdist;
02811 
02812         mindist = -MAX_FASTF;
02813         maxdist = MAX_FASTF;
02814         VSUB2( diff, b, a );
02815 
02816         for( i=0; i < 3; i++, pt++, dir++, max++, min++ )  {
02817                 if( *dir < -SQRT_SMALL_FASTF )  {
02818                         if( (sv = (*min - *pt) / *dir) < 0.0 )
02819                                 return(0);      /* MISS */
02820                         if(maxdist > sv)
02821                                 maxdist = sv;
02822                         if( mindist < (st = (*max - *pt) / *dir) )
02823                                 mindist = st;
02824                 }  else if( *dir > SQRT_SMALL_FASTF )  {
02825                         if( (st = (*max - *pt) / *dir) < 0.0 )
02826                                 return(0);      /* MISS */
02827                         if(maxdist > st)
02828                                 maxdist = st;
02829                         if( mindist < ((sv = (*min - *pt) / *dir)) )
02830                                 mindist = sv;
02831                 }  else  {
02832                         /*
02833                          *  If direction component along this axis is NEAR 0,
02834                          *  (ie, this ray is aligned with this axis),
02835                          *  merely check against the boundaries.
02836                          */
02837                         if( (*min > *pt) || (*max < *pt) )
02838                                 return(0);      /* MISS */;
02839                 }
02840         }
02841         if( mindist >= maxdist )
02842                 return(0);      /* MISS */
02843 
02844         if( mindist > 1 || maxdist < 0 )
02845                 return(0);      /* MISS */
02846 
02847         if( mindist >= 0 && maxdist <= 1 )
02848                 return(1);      /* HIT within box, no clipping needed */
02849 
02850         /* Don't grow one end of a contained segment */
02851         if( mindist < 0 )
02852                 mindist = 0;
02853         if( maxdist > 1 )
02854                 maxdist = 1;
02855 
02856         /* Compute actual intercept points */
02857         VJOIN1( b, a, maxdist, diff );          /* b must go first */
02858         VJOIN1( a, a, mindist, diff );
02859         return(1);              /* HIT */
02860 }
02861 
02862 /*@}*/
02863 /*
02864  * Local Variables:
02865  * mode: C
02866  * tab-width: 8
02867  * c-basic-offset: 4
02868  * indent-tabs-mode: t
02869  * End:
02870  * ex: shiftwidth=4 tabstop=8
02871  */

Generated on Mon Sep 18 01:24:47 2006 for BRL-CAD by  doxygen 1.4.6