bezier_2d_isect.c

Go to the documentation of this file.
00001 /*               B E Z I E R _ 2 D _ I S E C T . 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 ray */
00023 /*@{*/
00024 /** @file bezier_2d_isect.c
00025  *      The following routines are for 2D Bezier curves
00026  *
00027  *      The following routines are borrowed from Graphics Gems I,
00028  *      Academic Press, Inc, 1990, Andrew S. Glassner (editor),
00029  *      "A Bezier Curve-based Root-finder", Philip J. Schneider.
00030  *
00031  *      JRA 4/2001:
00032  *              Modifications have been made for inclusion in BRL-CAD and
00033  *              to generalize the codes for finding intersections with any 2D line
00034  *              rather than just the X-axis.
00035  */
00036 
00037 #include "common.h"
00038 
00039 
00040 
00041 #include <stdio.h>
00042 #include "machine.h"
00043 #include "vmath.h"
00044 #include "nmg.h"
00045 #include "raytrace.h"
00046 #include "nurb.h"
00047 #include "../librt/debug.h"
00048 
00049 #define SGN(_x) (((_x)<0) ? -1 : 1)
00050 #define MAXDEPTH        64
00051 
00052 /*
00053  * CrossingCount :
00054  *      Count the number of times a Bezier control polygon
00055  *      crosses the ray. This number is >= the number of roots.
00056  *
00057  */
00058 int CrossingCount(
00059     point2d_t      *V,          /*  2D Control pts of Bezier curve */
00060     int         degree,         /*  Degreee of Bezier curve     */
00061     point2d_t   ray_start,      /*  starting point for ray      */
00062     point2d_t   ray_dir,        /*  unit ray direction (actually a vector, not a point) */
00063     point2d_t   ray_perp)       /*  unit vector perpendicular to ray direction */
00064 {
00065     int         i;
00066     int         n_crossings = 0;        /*  Number of crossings    */
00067     int         sign, old_sign;         /*  Sign of coefficients        */
00068     point2d_t   to_pt;                  /* vector from ray start to a control point */
00069 
00070     V2SUB2( to_pt, ray_start, V[0] );
00071     sign = old_sign = SGN( V2DOT( to_pt, ray_perp ) );
00072     for (i = 1; i <= degree; i++) {
00073             V2SUB2( to_pt, ray_start, V[i] );
00074             sign = SGN( V2DOT( to_pt, ray_perp ) );
00075             if (sign != old_sign) n_crossings++;
00076             old_sign = sign;
00077     }
00078 
00079     return n_crossings;
00080 }
00081 
00082 
00083 /*
00084  *  ControlPolygonFlatEnough :
00085  *      Check if the control polygon of a Bezier curve is flat enough
00086  *      for recursive subdivision to bottom out.
00087  *
00088  */
00089 int
00090 ControlPolygonFlatEnough(
00091     point2d_t   *V,             /* Control points       */
00092     int         degree,         /* Degree of polynomial */
00093     fastf_t     epsilon)        /* Maximum allowable error */
00094 {
00095     int         i;                      /* Index variable               */
00096     double      *distance;              /* Distances from pts to line   */
00097     double      max_distance_above;     /* maximum of these             */
00098     double      max_distance_below;
00099     double      error;                  /* Precision of root            */
00100     double      intercept_1,
00101                 intercept_2,
00102                         left_intercept,
00103                         right_intercept;
00104     double      a, b, c;                /* Coefficients of implicit     */
00105                                         /* eqn for line from V[0]-V[deg]*/
00106 
00107     /* Find the  perpendicular distance         */
00108     /* from each interior control point to      */
00109     /* line connecting V[0] and V[degree]       */
00110     distance = (double *)bu_malloc((unsigned)(degree + 1) * sizeof(double), "ControlPolygonFlatEnough");
00111     {
00112         double  abSquared;
00113 
00114         /* Derive the implicit equation for line connecting first */
00115         /*  and last control points */
00116         a = V[0][Y] - V[degree][Y];
00117         b = V[degree][X] - V[0][X];
00118         c = V[0][X] * V[degree][Y] - V[degree][X] * V[0][Y];
00119 
00120         abSquared = (a * a) + (b * b);
00121 
00122         for (i = 1; i < degree; i++) {
00123             /* Compute distance from each of the points to that line    */
00124                 distance[i] = a * V[i][X] + b * V[i][Y] + c;
00125                 if (distance[i] > 0.0) {
00126                                 distance[i] = (distance[i] * distance[i]) / abSquared;
00127                 }
00128                 if (distance[i] < 0.0) {
00129                                 distance[i] = -((distance[i] * distance[i]) / abSquared);
00130                 }
00131         }
00132     }
00133 
00134 
00135     /* Find the largest distance        */
00136     max_distance_above = 0.0;
00137     max_distance_below = 0.0;
00138     for (i = 1; i < degree; i++) {
00139                 if (distance[i] < 0.0) {
00140                         max_distance_below = MIN(max_distance_below, distance[i]);
00141                 };
00142                 if (distance[i] > 0.0) {
00143                         max_distance_above = MAX(max_distance_above, distance[i]);
00144                 }
00145     }
00146     bu_free((char *)distance,"ControlPolygonFlatEnough" );
00147 
00148     {
00149         double  det, dInv;
00150         double  a1, b1, c1, a2, b2, c2;
00151 
00152         /*  Implicit equation for zero line */
00153         a1 = 0.0;
00154         b1 = 1.0;
00155         c1 = 0.0;
00156 
00157         /*  Implicit equation for "above" line */
00158         a2 = a;
00159         b2 = b;
00160         c2 = c + max_distance_above;
00161 
00162         det = a1 * b2 - a2 * b1;
00163         dInv = 1.0/det;
00164 
00165         intercept_1 = (b1 * c2 - b2 * c1) * dInv;
00166 
00167         /*  Implicit equation for "below" line */
00168         a2 = a;
00169         b2 = b;
00170         c2 = c + max_distance_below;
00171 
00172         det = a1 * b2 - a2 * b1;
00173         dInv = 1.0/det;
00174 
00175         intercept_2 = (b1 * c2 - b2 * c1) * dInv;
00176     }
00177 
00178     /* Compute intercepts of bounding box       */
00179     left_intercept = MIN(intercept_1, intercept_2);
00180     right_intercept = MAX(intercept_1, intercept_2);
00181 
00182     error = 0.5 * (right_intercept-left_intercept);
00183 
00184     if (error < epsilon) {
00185                 return 1;
00186     }
00187     else {
00188                 return 0;
00189     }
00190 }
00191 
00192 
00193 /*
00194  *  Bezier :
00195  *      Evaluate a Bezier curve at a particular parameter value
00196  *      Fill in control points for resulting sub-curves if "Left" and
00197  *      "Right" are non-null.
00198  *
00199  */
00200 void
00201 Bezier(
00202     point2d_t   *V,                     /* Control pts                  */
00203     int         degree,                 /* Degree of bezier curve       */
00204     double      t,                      /* Parameter value [0..1]       */
00205     point2d_t   *Left,                  /* RETURN left half ctl pts     */
00206     point2d_t   *Right,                 /* RETURN right half ctl pts    */
00207     point2d_t   eval_pt,                /* RETURN evaluated point       */
00208     point2d_t   normal )                /* RETURN unit normal at evaluated pt (may be NULL) */
00209 {
00210     int         i, j;           /* Index variables      */
00211     fastf_t     len;
00212     point2d_t   tangent;
00213     point2d_t   **Vtemp;
00214 
00215 
00216     Vtemp = (point2d_t **)bu_calloc( degree+1, sizeof(point2d_t *), "Bezier: Vtemp array" );
00217     for( i=0 ; i<=degree ; i++ )
00218             Vtemp[i] = (point2d_t *)bu_calloc( degree+1, sizeof( point2d_t ),
00219                                                "Bezier: Vtemp[i] array" );
00220     /* Copy control points      */
00221     for (j =0; j <= degree; j++) {
00222                 V2MOVE( Vtemp[0][j], V[j]);
00223     }
00224 
00225     /* Triangle computation     */
00226     for (i = 1; i <= degree; i++) {
00227                 for (j =0 ; j <= degree - i; j++) {
00228                 Vtemp[i][j][X] =
00229                         (1.0 - t) * Vtemp[i-1][j][X] + t * Vtemp[i-1][j+1][X];
00230                 Vtemp[i][j][Y] =
00231                         (1.0 - t) * Vtemp[i-1][j][Y] + t * Vtemp[i-1][j+1][Y];
00232                 }
00233     }
00234 
00235     if (Left != NULL) {
00236                 for (j = 0; j <= degree; j++) {
00237                 V2MOVE( Left[j], Vtemp[j][0]);
00238                 }
00239     }
00240     if (Right != NULL) {
00241                 for (j = 0; j <= degree; j++) {
00242                 V2MOVE( Right[j], Vtemp[degree-j][j]);
00243                 }
00244     }
00245 
00246     V2MOVE( eval_pt, Vtemp[degree][0] );
00247 
00248     if( normal ) {
00249             V2SUB2( tangent, Vtemp[degree-1][1], Vtemp[degree-1][0] );
00250             normal[X] = tangent[Y];
00251             normal[Y] = -tangent[X];
00252             len = sqrt( MAG2SQ( normal ) );
00253             normal[X] /= len;
00254             normal[Y] /= len;
00255     }
00256 
00257     for( i=0 ; i<=degree ; i++ )
00258             bu_free( (char *)Vtemp[i], "Bezier: Vtemp[i]" );
00259     bu_free( (char *)Vtemp, "Bezier: Vtemp" );
00260 
00261     return;
00262 }
00263 
00264 /*
00265  *  ComputeXIntercept :
00266  *      Compute intersection of chord from first control point to last
00267  *      with ray.
00268  *
00269  *  Returns :
00270  *
00271  *      0 - no intersection
00272  *      1 - found an intersection
00273  *
00274  *      intercept - contains calculated intercept
00275  *
00276  */
00277 static int
00278 ComputeXIntercept(
00279     point2d_t   *V,                     /*  Control points      */
00280     int         degree,                 /*  Degree of curve     */
00281     point2d_t   ray_start,              /*  starting point of ray */
00282     point2d_t   ray_dir,                /* unit ray direction   */
00283     double      epsilon,                /* maximum allowable error */
00284     point2d_t   intercept,              /* calculated intercept point */
00285     point2d_t   normal )                /* calculated unit normal at intercept */
00286 {
00287         fastf_t beta;
00288         fastf_t denom;
00289         fastf_t len;
00290         point2d_t seg_line;
00291 
00292         denom = (V[degree][X] - V[0][X]) * ray_dir[Y] -
00293                 (V[degree][Y] - V[0][Y]) * ray_dir[X];
00294 
00295         if( NEAR_ZERO( denom, SMALL_FASTF ) )
00296                 return 0;
00297 
00298         beta = (V[0][Y] * ray_dir[X] - V[0][X] * ray_dir[Y] +
00299                 ray_start[X] * ray_dir[Y] - ray_start[Y] * ray_dir[X] ) / denom;
00300 
00301         if( beta < 0.0 || beta > 1.0 )
00302                 return 0;
00303 
00304         V2SUB2( seg_line, V[degree], V[0] );
00305         V2JOIN1( intercept, V[0], beta, seg_line );
00306 
00307         /* calculate normal */
00308         normal[X] = seg_line[Y];
00309         normal[Y] = -seg_line[X];
00310         len = sqrt( MAG2SQ( seg_line ) );
00311         normal[X] /= len;
00312         normal[Y] /= len;
00313 
00314         return 1;
00315 #if 0
00316         V2MOVE( p, ray_start );
00317         p[Z] = 0.0;
00318         V2MOVE( d, ray_dir );
00319         d[Z] = 0.0;
00320         V2MOVE( a, V[0] );
00321         a[Z] = 0.0;
00322         V2SUB2( c, V[degree], V[0] );
00323         c[Z] = 0.0;
00324 
00325         /* calculate intercept */
00326         ret = bn_isect_line2_lseg2( dist, p, d, a, c, &tol );
00327 
00328         bu_log( "\tbn_isect_line2_lseg2() returned %d\n", ret );
00329 
00330         if( ret <= 0 )
00331                 return 0;
00332 
00333         switch( ret ) {
00334                 case 1:
00335                         /* intercept at V[0] */
00336                         V2MOVE( intercept, V[0] );
00337                         break;
00338                 case 2:
00339                         /* intercept at V[degree] */
00340                         V2MOVE( intercept, V[degree] );
00341                         break;
00342                 case 3:
00343                         /* intercept between endpoints */
00344                         V2JOIN1( intercept, ray_start, dist[0], ray_dir );
00345                         break;
00346         }
00347 
00348         /* calculate normal */
00349         normal[X] = c[Y];
00350         normal[Y] = -c[X];
00351         len = sqrt( MAG2SQ( c ) );
00352         normal[X] /= len;
00353         normal[Y] /= len;
00354 
00355         return 1;
00356 #endif
00357 }
00358 
00359 
00360 /*
00361  *  FindRoots :
00362  *      Given an equation in Bernstein-Bezier form, find
00363  *      all of the roots in the interval [0, 1].  Return the number
00364  *      of roots found.
00365  */
00366 int
00367 FindRoots(
00368     point2d_t      *w,                     /* The control points           */
00369     int         degree,         /* The degree of the polynomial */
00370     point2d_t   **intercept,    /* list of intersections found  */
00371     point2d_t   **normal,       /* corresponding normals        */
00372     point2d_t   ray_start,      /* starting point of ray */
00373     point2d_t   ray_dir,        /* Unit direction for ray */
00374     point2d_t   ray_perp,       /* Unit vector normal to ray_dir */
00375     int         depth,          /* The depth of the recursion   */
00376     fastf_t     epsilon)        /* maximum allowable error */
00377 {
00378     int         i;
00379     point2d_t   *Left,                  /* New left and right           */
00380                 *Right;                 /* control polygons             */
00381     int         left_count,             /* Solution count from          */
00382                 right_count;            /* children                     */
00383     point2d_t   *left_t,                /* Solutions from kids          */
00384                 *right_t;
00385     point2d_t   *left_n,                /* normals from kids            */
00386                 *right_n;
00387     int         total_count;
00388     point2d_t   eval_pt;
00389 
00390     switch (CrossingCount(w, degree, ray_start, ray_dir, ray_perp)) {
00391         case 0 : {      /* No solutions here    */
00392              return 0;
00393         }
00394         case 1 : {      /* Unique solution      */
00395             /* Stop recursion when the tree is deep enough      */
00396             /* if deep enough, return 1 solution at midpoint    */
00397             if (depth >= MAXDEPTH) {
00398                     *intercept = (point2d_t *)bu_malloc( sizeof( point2d_t ), "FindRoots: unique solution (intercept)" );
00399                     *normal = (point2d_t *)bu_malloc( sizeof( point2d_t ), "FindRoots: unique solution (normal)" );
00400                     Bezier( w, degree, 0.5, NULL, NULL, *intercept[0], *normal[0] );
00401                     return 1;
00402             }
00403             if (ControlPolygonFlatEnough(w, degree, epsilon)) {
00404                     *intercept = (point2d_t *)bu_malloc( sizeof( point2d_t ), "FindRoots: unique solution (intercept)" );
00405                     *normal = (point2d_t *)bu_malloc( sizeof( point2d_t ), "FindRoots: unique solution (normal)" );
00406                     if( !ComputeXIntercept( w, degree, ray_start, ray_dir, epsilon, *intercept[0], *normal[0] ) ){
00407                             bu_free( (char *)(*intercept), "FindRoots: no solution" );
00408                             bu_free( (char *)(*normal), "FindRoots: no solution" );
00409                             return 0;
00410                     }
00411                     return 1;
00412             }
00413             break;
00414         }
00415     }
00416 
00417     /* Otherwise, solve recursively after       */
00418     /* subdividing control polygon              */
00419     Left = (point2d_t *)bu_calloc( degree+1, sizeof( point2d_t ), "FindRoots: Left" );
00420     Right = (point2d_t *)bu_calloc( degree+1, sizeof( point2d_t ), "FindRoots: Right" );
00421     Bezier(w, degree, 0.5, Left, Right, eval_pt, NULL);
00422 
00423     left_count  = FindRoots(Left,  degree, &left_t, &left_n, ray_start, ray_dir, ray_perp, depth+1, epsilon);
00424     right_count = FindRoots(Right, degree, &right_t, &right_n, ray_start, ray_dir, ray_perp, depth+1, epsilon);
00425 
00426     total_count = left_count + right_count;
00427 
00428     bu_free( (char *)Left, "FindRoots: Left" );
00429     bu_free( (char *)Right, "FindRoots: Right" );
00430     if( total_count ) {
00431             *intercept = (point2d_t *)bu_calloc( total_count, sizeof( point2d_t ),
00432                                        "FindRoots: roots compilation" );
00433             *normal = (point2d_t *)bu_calloc( total_count, sizeof( point2d_t ),
00434                                           "FindRoots: normal compilation" );
00435     }
00436 
00437     /* Gather solutions together        */
00438     for (i = 0; i < left_count; i++) {
00439             V2MOVE( (*intercept)[i], left_t[i] );
00440             V2MOVE( (*normal)[i], left_n[i] );
00441     }
00442     for (i = 0; i < right_count; i++) {
00443             V2MOVE( (*intercept)[i+left_count], right_t[i] );
00444             V2MOVE( (*normal)[i+left_count], right_n[i] );
00445     }
00446 
00447     if( left_count ) {
00448             bu_free( (char *)left_t, "Left roots" );
00449             bu_free( (char *)left_n, "Left normals" );
00450     }
00451     if( right_count ) {
00452             bu_free( (char *)right_t, "Right roots" );
00453             bu_free( (char *)right_n, "Right normals" );
00454     }
00455 
00456     /* Send back total number of solutions      */
00457     return (total_count);
00458 }
00459 
00460 struct bezier_2d_list *
00461 subdivide_bezier( struct bezier_2d_list *bezier_in, int degree, fastf_t epsilon, int depth )
00462 {
00463         struct bezier_2d_list *bz_l, *bz_r, *new_head;
00464         struct bezier_2d_list *left_rtrn, *rt_rtrn;
00465         point2d_t pt;
00466 
00467         /* create a new head */
00468         new_head = (struct bezier_2d_list *)bu_malloc( sizeof( struct bezier_2d_list ),
00469                                                        "subdivide_bezier: new_head" );
00470         BU_LIST_INIT( &new_head->l );
00471         if( depth >= MAXDEPTH ){
00472                 BU_LIST_APPEND( &new_head->l, &bezier_in->l );
00473                 return( new_head );
00474         }
00475 
00476         if( ControlPolygonFlatEnough( bezier_in->ctl, degree, epsilon ) ) {
00477                 BU_LIST_APPEND( &new_head->l, &bezier_in->l );
00478                 return( new_head );
00479         }
00480 
00481         /* allocate memory for left and right curves */
00482         bz_l = (struct bezier_2d_list *)bu_malloc( sizeof( struct bezier_2d_list ), "subdivide_bezier: bz_l" );
00483         BU_LIST_INIT( &bz_l->l );
00484         bz_r = (struct bezier_2d_list *)bu_malloc( sizeof( struct bezier_2d_list ), "subdivide_bezier: bz_r" );
00485         BU_LIST_INIT( &bz_r->l );
00486         bz_l->ctl = (point2d_t *)bu_calloc( degree + 1, sizeof( point2d_t ),
00487                                             "subdivide_bezier: bz_l->ctl" );
00488         bz_r->ctl = (point2d_t *)bu_calloc( degree + 1, sizeof( point2d_t ),
00489                                             "subdivide_bezier: bz_r->ctl" );
00490 
00491         /* subdivide at t = 0.5 */
00492         Bezier( bezier_in->ctl, degree, 0.5, bz_l->ctl, bz_r->ctl, pt, NULL );
00493 
00494         /* eliminate original */
00495         BU_LIST_DEQUEUE( &bezier_in->l );
00496         bu_free( (char *)bezier_in->ctl, "subdivide_bezier: bezier_in->ctl" );
00497         bu_free( (char *)bezier_in, "subdivide_bezier: bezier_in" );
00498 
00499         /* recurse on left curve */
00500         left_rtrn = subdivide_bezier( bz_l, degree, epsilon, depth+1 );
00501 
00502         /* recurse on right curve */
00503         rt_rtrn = subdivide_bezier( bz_r, degree, epsilon, depth+1 );
00504 
00505         BU_LIST_APPEND_LIST( &new_head->l, &left_rtrn->l );
00506         BU_LIST_APPEND_LIST( &new_head->l, &rt_rtrn->l );
00507         bu_free( (char *)left_rtrn, "subdivide_bezier: left_rtrn (head)" );
00508         bu_free( (char *)rt_rtrn, "subdivide_bezier: rt_rtrn (head)" );
00509 
00510         return( new_head );
00511 }
00512 
00513 /*@}*/
00514 /*
00515  * Local Variables:
00516  * mode: C
00517  * tab-width: 8
00518  * c-basic-offset: 4
00519  * indent-tabs-mode: t
00520  * End:
00521  * ex: shiftwidth=4 tabstop=8
00522  */

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