⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bezier.cc

📁 [Game.Programming].Academic - Graphics Gems (6 books source code)
💻 CC
字号:
#include <stddef.h> // for size_t// #include <sys/stdtypes.h> // for size_t, on some systems#include "Bezier.h"#include <math.h>/* The value of 1.0 / (1L<<23) is float machine epsilon. */#ifdef FLOAT_ACCURACY#define INV_EPS (1L<<23)#else/* The value of 1.0 / (1L<<14) is enough for most applications */#define INV_EPS (1L<<14)#endif#define log2(x) (log(x)/log(2.))extern "C" void qsort( char *base, int nel, size_t width, int (*compar)(void *, void *));int compare_doubles( void *a, void *b )    {    register double *A = (double *)a, *B = (double *)b;    return( *A > *B )?1:(*A < *B ? -1 : 0 );    }void Sort( double *array, int length )    {    qsort( (char *)array, length, sizeof( double ), compare_doubles );    }    /* * Split the curve at the midpoint, returning an array with the two parts * Temporary storage is minimized by using part of the storage for the result * to hold an intermediate value until it is no longer needed. */#define left r[0]#define right r[1]Bezier *Bezier::Split()    {    Bezier *r = new Bezier[2];    (left.p0 = p0)->refcount++;    (right.p3 = p3)->refcount++;    left.p1 = new point(  p0, p1, 1);    right.p2 = new point( p2, p3, 1);    right.p1 = new point( p1, p2, 1); // temporary holding spot    left.p2 = new point ( left.p1, right.p1, 1);    *right.p1 = mid( right.p1, right.p2 ); // Real value this time    left.p3 = right.p0 = new point( left.p2, right.p1, 2 );    return r;    }#undef left#undef right    /** Test the bounding boxes of two Bezier curves for interference.* Several observations:*	First, it is cheaper to compute the bounding box of the second curve*	and test its bounding box for interference than to use a more direct*	approach of comparing all control points of the second curve with *	the various edges of the bounding box of the first curve to test* 	for interference.*	Second, after a few subdivisions it is highly probable that two corners*	of the bounding box of a given Bezier curve are the first and last *	control point.  Once this happens once, it happens for all subsequent*	subcurves.  It might be worth putting in a test and then short-circuit*	code for further subdivision levels.*	Third, in the final comparison (the interference test) the comparisons*	should both permit equality.  We want to find intersections even if they*	occur at the ends of segments.*	Finally, there are tighter bounding boxes that can be derived. It isn't*	clear whether the higher probability of rejection (and hence fewer*	subdivisions and tests) is worth the extra work.*/int IntersectBB( Bezier a, Bezier b )    {    // Compute bounding box for a    double minax, maxax, minay, maxay;    if( a.p0->x > a.p3->x )	 // These are the most likely to be extremal	minax = a.p3->x, maxax = a.p0->x;    else	minax = a.p0->x, maxax = a.p3->x;    if( a.p2->x < minax )	minax = a.p2->x;    else if( a.p2->x > maxax )	maxax = a.p2->x;    if( a.p1->x < minax )	minax = a.p1->x;    else if( a.p1->x > maxax )	maxax = a.p1->x;    if( a.p0->y > a.p3->y ) 			minay = a.p3->y, maxay = a.p0->y;    else	minay = a.p0->y, maxay = a.p3->y;    if( a.p2->y < minay )	minay = a.p2->y;    else if( a.p2->y > maxay )	maxay = a.p2->y;    if( a.p1->y < minay )	minay = a.p1->y;    else if( a.p1->y > maxay )	maxay = a.p1->y;    // Compute bounding box for b    double minbx, maxbx, minby, maxby;    if( b.p0->x > b.p3->x ) 			minbx = b.p3->x, maxbx = b.p0->x;    else	minbx = b.p0->x, maxbx = b.p3->x;    if( b.p2->x < minbx )	minbx = b.p2->x;    else if( b.p2->x > maxbx )	maxbx = b.p2->x;    if( b.p1->x < minbx )	minbx = b.p1->x;    else if( b.p1->x > maxbx )	maxbx = b.p1->x;    if( b.p0->y > b.p3->y ) 			minby = b.p3->y, maxby = b.p0->y;    else	minby = b.p0->y, maxby = b.p3->y;    if( b.p2->y < minby )	minby = b.p2->y;    else if( b.p2->y > maxby )	maxby = b.p2->y;    if( b.p1->y < minby )	minby = b.p1->y;    else if( b.p1->y > maxby )	maxby = b.p1->y;    // Test bounding box of b against bounding box of a    if( ( minax > maxbx ) || ( minay > maxby )  // Not >= : need boundary case	|| ( minbx > maxax ) || ( minby > maxay ) )	return 0; // they don't intersect    else	return 1; // they intersect    }	/* * Recursively intersect two curves keeping track of their real parameters * and depths of intersection.* The results are returned in a 2-D array of doubles indicating the parameters* for which intersections are found.  The parameters are in the order the* intersections were found, which is probably not in sorted order.* When an intersection is found, the parameter value for each of the two * is stored in the index elements array, and the index is incremented.* * If either of the curves has subdivisions left before it is straight*	(depth > 0)* that curve (possibly both) is (are) subdivided at its (their) midpoint(s).* the depth(s) is (are) decremented, and the parameter value(s) corresponding* to the midpoints(s) is (are) computed.* Then each of the subcurves of one curve is intersected with each of the * subcurves of the other curve, first by testing the bounding boxes for* interference.  If there is any bounding box interference, the corresponding* subcurves are recursively intersected.* * If neither curve has subdivisions left, the line segments from the first* to last control point of each segment are intersected.  (Actually the * only the parameter value corresponding to the intersection point is found).** The apriori flatness test is probably more efficient than testing at each* level of recursion, although a test after three or four levels would* probably be worthwhile, since many curves become flat faster than their * asymptotic rate for the first few levels of recursion.** The bounding box test fails much more frequently than it succeeds, providing* substantial pruning of the search space.** Each (sub)curve is subdivided only once, hence it is not possible that for * one final line intersection test the subdivision was at one level, while* for another final line intersection test the subdivision (of the same curve)* was at another.  Since the line segments share endpoints, the intersection* is robust: a near-tangential intersection will yield zero or two* intersections.*/void RecursivelyIntersect( Bezier a, double t0, double t1, int deptha,			   Bezier b, double u0, double u1, int depthb,			   double **parameters, int &index )    {    if( deptha > 0 )	{	Bezier *A = a.Split();	double tmid = (t0+t1)*0.5;	deptha--;	if( depthb > 0 )	    {	    Bezier *B = b.Split();	    double umid = (u0+u1)*0.5;	    depthb--;	    if( IntersectBB( A[0], B[0] ) )		RecursivelyIntersect( A[0], t0, tmid, deptha,				      B[0], u0, umid, depthb,				      parameters, index );	    if( IntersectBB( A[1], B[0] ) )		RecursivelyIntersect( A[1], tmid, t1, deptha,				      B[0], u0, umid, depthb,				      parameters, index );	    if( IntersectBB( A[0], B[1] ) )		RecursivelyIntersect( A[0], t0, tmid, deptha,				      B[1], umid, u1, depthb,				      parameters, index );	    if( IntersectBB( A[1], B[1] ) )		RecursivelyIntersect( A[1], tmid, t1, deptha,				      B[1], umid, u1, depthb,				      parameters, index );	    }	else	    {	    if( IntersectBB( A[0], b ) )		RecursivelyIntersect( A[0], t0, tmid, deptha,				      b, u0, u1, depthb,				      parameters, index );	    if( IntersectBB( A[1], b ) )		RecursivelyIntersect( A[1], tmid, t1, deptha,				      b, u0, u1, depthb,				      parameters, index );	    }	}    else	if( depthb > 0 )	    {	    Bezier *B = b.Split();	    double umid = (u0 + u1)*0.5;	    depthb--;	    if( IntersectBB( a, B[0] ) )		RecursivelyIntersect( a, t0, t1, deptha,				      B[0], u0, umid, depthb,				      parameters, index );	    if( IntersectBB( a, B[1] ) )		RecursivelyIntersect( a, t0, t1, deptha,				      B[0], umid, u1, depthb,				      parameters, index );	    }	else // Both segments are fully subdivided; now do line segments	    {	    double xlk = a.p3->x - a.p0->x;	    double ylk = a.p3->y - a.p0->y;	    double xnm = b.p3->x - b.p0->x;	    double ynm = b.p3->y - b.p0->y;	    double xmk = b.p0->x - a.p0->x;	    double ymk = b.p0->y - a.p0->y;	    double det = xnm * ylk - ynm * xlk;	    if( 1.0 + det == 1.0 )		return;	    else		{		double detinv = 1.0 / det;		double s = ( xnm * ymk - ynm *xmk ) * detinv;		double t = ( xlk * ymk - ylk * xmk ) * detinv;		if( ( s < 0.0 ) || ( s > 1.0 ) || ( t < 0.0 ) || ( t > 1.0 ) )		    return;		parameters[0][index] = t0 + s * ( t1 - t0 );		parameters[1][index] = u0 + t * ( u1 - u0 );		index++;		}	    }    }inline double log4( double x ) { return 0.5 * log2( x ); }    /* * Wang's theorem is used to estimate the level of subdivision required, * but only if the bounding boxes interfere at the top level. * Assuming there is a possible intersection, RecursivelyIntersect is * used to find all the parameters corresponding to intersection points. * these are then sorted and returned in an array. */double ** FindIntersections( Bezier a, Bezier b )    {    double **parameters = new double *[2];    parameters[0] = new double[9];    parameters[1] = new double[9];    int index = 0;    if( IntersectBB( a, b ) )	{	vector la1 = vabs( ( *(a.p2) - *(a.p1) ) - ( *(a.p1) - *(a.p0) ) );	vector la2 = vabs( ( *(a.p3) - *(a.p2) ) - ( *(a.p2) - *(a.p1) ) );	vector la;	if( la1.x > la2.x ) la.x = la1.x; else la.x = la2.x;	if( la1.y > la2.y ) la.y = la1.y; else la.y = la2.y;	vector lb1 = vabs( ( *(b.p2) - *(b.p1) ) - ( *(b.p1) - *(b.p0) ) );	vector lb2 = vabs( ( *(b.p3) - *(b.p2) ) - ( *(b.p2) - *(b.p1) ) );	vector lb;	if( lb1.x > lb2.x ) lb.x = lb1.x; else lb.x = lb2.x;	if( lb1.y > lb2.y ) lb.y = lb1.y; else lb.y = lb2.y;	double l0;	if( la.x > la.y ) 	    l0 = la.x;	else 	    l0 = la.y;	int ra;	if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) 	    ra = 0;	else	    ra = (int)ceil( log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );	if( lb.x > lb.y ) 	    l0 = lb.x;	else 	    l0 = lb.y;	int rb;	if( l0 * 0.75 * M_SQRT2 + 1.0 == 1.0 ) 	    rb = 0;	else	    rb = (int)ceil(log4( M_SQRT2 * 6.0 / 8.0 * INV_EPS * l0 ) );	RecursivelyIntersect( a, 0., 1., ra, b, 0., 1., rb, parameters, index );	}    if( index < 9 )	parameters[0][index] = parameters[1][index] = -1.0;    Sort( parameters[0], index );    Sort( parameters[1], index );    return parameters;    }voidBezier::ParameterSplitLeft( double t, Bezier &left )    {    left.p0 = p0;    left.p1 = new point( *p0 + t * ( *p1 - *p0 ) );    left.p2 = new point( *p1 + t * ( *p2 - *p1 ) ); // temporary holding spot    p2->refcount--;    p2 = new point( *p2 + t * ( *p3 - *p2 ) );    p1->refcount--;    p1 = new point( *(left.p2) + t * ( *p2 - *(left.p2) ) );    *(left.p2) = ( *(left.p1) + t * ( *(left.p2) - *(left.p1) ) );    (left.p3 = p0 = new point(*(left.p2) + t * (*(p1)-*(left.p2))))->refcount++;    left.p0->refcount++; left.p1->refcount++;     left.p2->refcount++; left.p3->refcount++;    }    /* * Intersect two curves, returning an array of two arrays of curves. * The first array of curves corresponds to `this' curve, the second  * corresponds to curve B, passed in. * The intersection parameter values are computed by FindIntersections, * and they come back in the range 0..1, using the original parameterization. * Once one segment has been removed, ie the curve is split at splitT, the * parameterization of the second half is from 0..1, so the parameter for * the next split point, if any, must be adjusted. * If we split at t[i], the split point at t[i+1] is  * ( t[i+1] - t[i] ) / ( t - t[i] ) of the way to the end from the new * start point. */Bezier **Bezier::Intersect( Bezier B )    {    // The return from FindIntersections will decrement all refcounts.    // (a c++-ism)    B.p0->refcount++; B.p1->refcount++; B.p2->refcount++; B.p3->refcount++;     Bezier **rvalue = new Bezier *[2];    rvalue[0] = new Bezier[10];    rvalue[1] = new Bezier[10];    double **t = FindIntersections( *this, B );    int index = 0;    if( t[0][0] > -0.5 )	{	ParameterSplitLeft( t[0][0], rvalue[0][0] );	B.ParameterSplitLeft( t[1][0], rvalue[1][0] );	index++;	while( t[0][index] > -0.5 && index < 9 )	    {	    double splitT = (t[0][index] - t[0][index-1])/(1.0 - t[0][index-1]);	    ParameterSplitLeft( splitT, rvalue[0][index] );	    splitT = (t[1][index] - t[1][index-1])/(1.0 - t[1][index-1]);	    B.ParameterSplitLeft( splitT, rvalue[1][index] );	    index++;	    }	}    rvalue[0][index] = *this;    rvalue[1][index] = B;    return rvalue;    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -