|
|
/**************************************************************************\
* * Copyright (c) 1999 Microsoft Corporation * * Abstract: * * Implementation of XBezier class and its DDA. * * History: * * 11/05/1999 ikkof * Created it. * \**************************************************************************/
#include "precomp.hpp"
//==========================================================================
// GpXBezier class
//==========================================================================
GpXBezier::~GpXBezier() { if(Data) GpFree(Data); }
GpStatus GpXBezier::SetBeziers(INT order, const GpPointF* points, INT count) { ASSERT(points && order > 1 && count > order && count % order == 1);
if(!points || order <= 1 || count <= order || count % order != 1) return InvalidParameter;
GpStatus status = Ok; INT totalSize = 2*count*sizeof(REALD);
REALD* data = (REALD*) GpRealloc(Data, totalSize); if(data) { REALD* dataPtr = data; GpPointF* ptr = (GpPointF*) points;
for(INT i = 0; i < count; i++) { *dataPtr++ = ptr->X; *dataPtr++ = ptr->Y; ptr++; }
NthOrder = order; Dimension = 2; Count = count; Data = data; status = Ok; } else status = OutOfMemory;
return status; }
GpStatus GpXBezier::SetBeziers(INT order, const GpXPoints& xpoints) { ASSERT(xpoints.Count % order == 1); if(xpoints.Count % order != 1) return InvalidParameter;
INT totalSize = xpoints.Dimension*xpoints.Count*sizeof(REALD);
REALD* data = (REALD*) GpRealloc(Data, totalSize); if(data) { NthOrder = order; Dimension = xpoints.Dimension; Count = xpoints.Count; GpMemcpy(data, xpoints.Data, totalSize); Data = data; }
return Ok; }
/**************************************************************************\
* * Function Description: * * Flattens the series of Bezier control points and stores * the results to the arrays of the flatten points. * * Arguments: * * [OUT] flattenPts - the returned flattend points. * [IN] matrix - Specifies the transform * * Return Value: * * NONE * * Created: * * 11/05/1999 ikkof * Created it. * \**************************************************************************/ GpStatus GpXBezier::Flatten( DynPointFArray* flattenPts, const GpMatrix *matrix ) { if(flattenPts == NULL) return InvalidParameter;
GpXBezierDDA dda; REALD* bezierData = Data; INT bezierDataStep = Dimension*NthOrder; BOOL isFirstBezier = TRUE;
INT count = Count;
flattenPts->Reset(FALSE);
while(count > 1) { FlattenEachBezier( flattenPts, dda, isFirstBezier, matrix, bezierData);
count -= NthOrder; bezierData += bezierDataStep; isFirstBezier = FALSE; }
return Ok; }
/**************************************************************************\
* * Function Description: * * Transforms the control points. * * Arguments: * * [IN] matrix - Specifies the transform * * Return Value: * * NONE * * Created: * * 11/05/1999 ikkof * Created it. * \**************************************************************************/ VOID GpXBezier::Transform( GpMatrix *matrix ) { FPUStateSaver fpuState;
if(matrix == NULL || !Data || Count <= 0) return;
// Since this is the 2D transform, we transform only
// the first two component.
GpPointF pt; INT j = 0;
for(INT i = 0; i < Count; i++) { pt.X = TOREAL(Data[j]); pt.Y = TOREAL(Data[j+1]); matrix->Transform(&pt, 1); Data[j] = pt.X; Data[j + 1] = pt.Y; j += Dimension; } return; }
/**************************************************************************\
* * Function Description: * * Returns the bounds in the specified transform. * This first calculates the bounds of the control points * in the world coordinates. * Then it converts the bounds in the given transform. * Therefore, this is bigger than the real bounds. * * Arguments: * * [IN] matrix - Specifies the transform * [OUT] bounds - Returns the bounding rectangle * * Return Value: * * NONE * * Created: * * 12/16/1998 ikkof * Created it. * \**************************************************************************/
VOID GpXBezier::GetBounds( GpMatrix* matrix, GpRect* bounds ) { ASSERT(IsValid());
// Currently only Dimension = 2 case is implemented.
if(Dimension != 2) return;
INT count = Count;
if (count == 0) { bounds->X = 0; bounds->Y = 0; bounds->Width = 0; bounds->Height = 0; } else { FPUStateSaver fpuState;
REALD* data = Data;
REALD left = *data; REALD right = left; REALD top = *data++; REALD bottom = top; count--; REALD x, y;
while(count > 0) { x = *data++; y = *data++;
if (x < left) left = x; if (y < top) top = y; if (x > right) right = x; if (y > bottom) bottom = y; count--; }
GpRectF boundsF; TransformBounds( matrix, TOREAL(left), TOREAL(top), TOREAL(right), TOREAL(bottom), &boundsF); BoundsFToRect(&boundsF, bounds); } }
GpStatus GpXBezier::Get2DPoints( GpPointF* points, INT count, const REALD* dataPoints, const GpMatrix* matrix) { ASSERT(points && dataPoints && count > 0);
if(points && dataPoints && count > 0) { FPUStateSaver fpuState;
GpPointF* ptr = points; const REALD* dataPtr = dataPoints; INT i, j = 0; GpMatrix identityMatrix; const GpMatrix* mat = matrix; if(!mat) mat = &identityMatrix;
switch(Dimension) { case 2: for(i = 0; i < count; i++) { ptr->X = TOREAL(*dataPtr++); ptr->Y = TOREAL(*dataPtr++); ptr++; } mat->Transform(points, count); break;
case 3: for(i = 0; i < count; i++) { REALD x, y, w; x = *dataPtr++; y = *dataPtr++; w = *dataPtr++;
// Do the perspective projection.
ptr->X = TOREAL(x/w); ptr->Y = TOREAL(y/w); ptr++; } mat->Transform(points, count);
default: // Not implemented yet.
break; }
return Ok; }
return InvalidParameter; }
/**************************************************************************\
* * Function Description: * * Flattens a given cubic Bezier curve. * * Arguments: * * [OUT] flattenPts - the returned flattend points. * [IN] points - the four control points for a Cubic Bezier * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
GpStatus GpXBezier::FlattenEachBezier( DynPointFArray* flattenPts, GpXBezierDDA& dda, BOOL isFirstBezier, const GpMatrix *matrix, const REALD* bezierData ) { GpPointF pts[7];
if(Get2DPoints(&pts[0], NthOrder + 1, bezierData, matrix) != Ok) return GenericError;
// Use DDA to flatten a Bezier.
GpPointF nextPt;
GpXPoints xpoints(&pts[0], NthOrder + 1);
if(xpoints.Data == NULL) return OutOfMemory;
dda.SetBezier(xpoints, FlatnessLimit, DistanceLimit); dda.InitDDA(&nextPt);
GpPointF buffer[BZ_BUFF_SIZE]; INT count = 0;
// If this is the first Bezier curve, add the first point.
if(isFirstBezier) { buffer[count++] = nextPt; }
while(dda.GetNextPoint(&nextPt)) { if(count < BZ_BUFF_SIZE) buffer[count++] = nextPt; else { flattenPts->AddMultiple(&buffer[0], count); buffer[0] = nextPt; count = 1; } dda.MoveForward(); } // Add the last point.
if(count < BZ_BUFF_SIZE) buffer[count++] = nextPt; else { flattenPts->AddMultiple(&buffer[0], count); buffer[0] = nextPt; count = 1; } flattenPts->AddMultiple(&buffer[0], count);
return Ok; }
/**************************************************************************\
* * Function Description: * * Initialized constants needed for DDA of General Bezier. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
GpXBezierConstants::GpXBezierConstants() { INT i, j;
for(i = 0; i <= 6; i++) { GpMemset(&H[i][0], 0, 7*sizeof(REAL)); GpMemset(&D[i][0], 0, 7*sizeof(REAL)); GpMemset(&S[i][0], 0, 7*sizeof(REAL)); }
// Matrix for half step
H[0][0] = 1; H[1][0] = 0.5; // = 1/2
H[1][1] = 0.5; // = 1/2
H[2][0] = 0.25; // = 1/4
H[2][1] = 0.5; // = 1/2
H[2][2] = 0.25; // = 1/4
H[3][0] = 0.125; // = 1/8
H[3][1] = 0.375; // = 3/8
H[3][2] = 0.375; // = 3/8
H[3][3] = 0.125; // = 1/8
H[4][0] = 0.0625; // = 1/16
H[4][1] = 0.25; // = 1/4
H[4][2] = 0.375; // = 3/8
H[4][3] = 0.25; // = 1/4
H[4][4] = 0.0625; // = 1/16
H[5][0] = 0.03125; // = 1/32
H[5][1] = 0.15625; // = 5/32
H[5][2] = 0.3125; // = 5/16
H[5][3] = 0.3125; // = 5/16
H[5][4] = 0.15625; // = 5/32
H[5][5] = 0.03125; // = 1/32
H[6][0] = 0.015625; // = 1/64
H[6][1] = 0.09375; // = 3/32
H[6][2] = 0.234375; // = 15/64
H[6][3] = 0.3125; // = 5/16
H[6][4] = 0.234375; // = 15/64
H[6][5] = 0.09375; // = 3/32
H[6][6] = 0.015625; // = 1/64
// Matrix for double step
D[0][0] = 1; D[1][0] = -1; D[1][1] = 2; D[2][0] = 1; D[2][1] = -4; D[2][2] = 4; D[3][0] = -1; D[3][1] = 6; D[3][2] = -12; D[3][3] = 8; D[4][0] = 1; D[4][1] = -8; D[4][2] = 24; D[4][3] = -32; D[4][4] = 16; D[5][0] = -1; D[5][1] = 10; D[5][2] = -40; D[5][3] = 80; D[5][4] = -80; D[5][5] = 32; D[6][0] = 1; D[6][1] = -12; D[6][2] = 60; D[6][3] = -160; D[6][4] = 240; D[6][5] = -192; D[6][6] = 64;
// Matrix for one step
S[0][0] = 1; S[1][0] = 2; S[1][1] = -1; S[2][0] = 4; S[2][1] = -4; S[2][2] = 1; S[3][0] = 8; S[3][1] = -12; S[3][2] = 6; S[3][3] = -1; S[4][0] = 16; S[4][1] = -32; S[4][2] = 24; S[4][3] = -8; S[4][4] = 1; S[5][0] = 32; S[5][1] = -80; S[5][2] = 80; S[5][3] = -40; S[5][4] = 10; S[5][5] = -1; S[6][0] = 64; S[6][1] = -192; S[6][2] = 240; S[6][3] = -160; S[6][4] = 60; S[6][5] = -12; S[6][6] = 1;
F[0][0] = 1; F[0][1] = 1; F[0][2] = 1; F[0][3] = 1; F[0][4] = 1; F[0][5] = 1; F[0][6] = 1; F[1][1] = 1; F[1][2] = 2; F[1][3] = 3; F[1][4] = 4; F[1][5] = 5; F[1][6] = 6; F[2][2] = 1; F[2][3] = 3; F[2][4] = 6; F[2][5] = 10; F[2][6] = 15; F[3][3] = 1; F[3][4] = 4; F[3][5] = 10; F[3][6] = 20; F[4][4] = 1; F[4][5] = 5; F[4][6] = 15; F[5][5] = 1; F[5][6] = 6; F[6][6] = 1;
H6[0][0] = 1; H6[1][0] = 1; H6[1][1] = 1.0/6; H6[2][0] = 1; H6[2][1] = 1.0/3; H6[2][2] = 1.0/15; H6[3][0] = 1; H6[3][1] = 1.0/2; H6[3][2] = 1.0/5; H6[3][3] = 1.0/20; H6[4][0] = 1; H6[4][1] = 2.0/3; H6[4][2] = 2.0/5; H6[4][3] = 1.0/5; H6[4][4] = 1.0/15; H6[5][0] = 1; H6[5][1] = 5.0/6; H6[5][2] = 2.0/3; H6[5][3] = 1.0/2; H6[5][4] = 1.0/3; H6[5][5] = 1.0/6; H6[6][0] = 1; H6[6][1] = 1; H6[6][2] = 1; H6[6][3] = 1; H6[6][4] = 1; H6[6][5] = 1; H6[6][6] = 1;
G6[0][0] = 1; G6[1][0] = -6; G6[1][1] = 6; G6[2][0] = 15; G6[2][1] = -30; G6[2][2] = 15; G6[3][0] = -20; G6[3][1] = 60; G6[3][2] = -60; G6[3][3] = 20; G6[4][0] = 15; G6[4][1] = -60; G6[4][2] = 90; G6[4][3] = -60; G6[4][4] = 15; G6[5][0] = -6; G6[5][1] = 30; G6[5][2] = -60; G6[5][3] = 60; G6[5][4] = -30; G6[5][5] = 6; G6[6][0] = 1; G6[6][1] = -6; G6[6][2] = 15; G6[6][3] = -20; G6[6][4] = 15; G6[6][5] = -6; G6[6][6] = 1; }
GpStatus GpXPoints::Transform(const GpMatrix* matrix) { return TransformPoints(matrix, Data, Dimension, Count); }
GpStatus GpXPoints::TransformPoints( const GpMatrix* matrix, REALD* data, INT dimension, INT count ) { if(matrix == NULL || data == NULL || dimension == 0 || count == 0) return Ok;
// !! This code should consider using Matrix->Transform.
if(dimension >= 2) { FPUStateSaver fpuState;
INT j = 0;
// Transform only the first two axis.
for(INT i = 0; i < count; i++) { GpPointF pt;
pt.X = TOREAL(data[j]); pt.Y = TOREAL(data[j + 1]); matrix->Transform(&pt); data[j] = pt.X; data[j + 1] = pt.Y; j += dimension; }
return Ok; } else return GenericError; }
//==========================================================================
// Cubic Bezier class
//
// GpXBezierDDA class
//
// This is based on GDI's flatten path methods written
// by Paul Butzi and J. Andrew Gossen.
// Ikko Fushiki wrote this with different parameters
// and different flatness tests.
//==========================================================================
VOID GpXBezierDDA::Initialize( VOID ) { INT i;
T = 0; Dt = 1; NthOrder = 0; GpMemset(&P[0], 0, 16*sizeof(REALD)); GpMemset(&Q[0], 0, 16*sizeof(REALD));
NSteps = 1; // In order to avoid the later multiplication, we pre-multiply
// the flatness limit by 3.
FlatnessLimit = 3*FLATNESS_LIMIT; DistanceLimit = DISTANCE_LIMIT; }
/**************************************************************************\
* * Function Description: * * Set the control points of a CubicBezier. * * Arguments: * * [IN] points - the four control points for a Cubic Bezier * [IN] flatnessLimit - used for flattening * [IN] distanceLimit - used for flattening * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::SetBezier( const GpXPoints& xpoints, REAL flatnessLimit, REAL distanceLimit ) { if(xpoints.Data == NULL) return;
T = 0; Dt = 1; NthOrder = 0; INT totalCount = xpoints.Count*xpoints.Dimension;
// This can handle the two dimensional Bezier of 6-th order
// and the three and four dimensional Bezier of 3rd order.
ASSERT(totalCount < 16);
NthOrder = xpoints.Count - 1; Dimension = xpoints.Dimension;
GpMemcpy(&Q[0], xpoints.Data, totalCount*sizeof(REALD));
SetPolynomicalCoefficients();
NSteps = 1; // In order to avoid the later multiplication, we pre-multiply
// the flatness limit by 3.
FlatnessLimit = 3*flatnessLimit; DistanceLimit = distanceLimit; }
VOID GpXBezierDDA::SetPolynomicalCoefficients( VOID ) { if(NthOrder == 6) { for(INT i = 0; i <= 6; i++) { REALD x[4]; GpMemset(&x[0], 0, Dimension*sizeof(REALD)); INT k, k0;
for(INT j = 0; j <= i; j++) { k0 = Dimension*j; k = 0;
while(k < Dimension) { x[k] += C.G6[i][j]*Q[k0 + k]; k++; } }
k0 = Dimension*i; GpMemcpy(&P[k0], &x[0], Dimension*sizeof(REALD)); } } }
/**************************************************************************\
* * Function Description: * * Initializes DDA for CubicBezier and make one step forward. * This must be called before GetNextPoint() is called. * * Arguments: * * [OUT] pt - Returns the start point * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::InitDDA( GpPointF* pt ) { switch(Dimension) { case 2: pt->X = (REAL) Q[0]; pt->Y = (REAL) Q[1]; break;
case 3: // Do something
break;
default: // Do something
break; }
INT shift = 2;
// Subdivide fast until it is flat enough
while(NeedsSubdivide(FlatnessLimit)) { HalveStepSize(); // FastShrinkStepSize(shift);
}
// If it is subdivided too much, expand it.
if((NSteps & 1) == 0) { // If the current subdivide is too small,
// double it up.
while(NSteps > 1 && !NeedsSubdivide(FlatnessLimit/4)) { DoubleStepSize(); } }
// Take the first step forward.
TakeStep(); }
/**************************************************************************\
* * Function Description: * * Shrinks the current Bezier segment to half. * The section of t = 0 -> 1/2 of the current * Beizer segment becomes the new current segment. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::HalveStepSize( VOID ) { INT i, j; REALD x[4]; i = NthOrder;
while(i >= 0) { j = i;
GpMemset(&x[0], 0, Dimension*sizeof(REALD)); INT k0, k;
while(j >= 0) { k0 = Dimension*j; k = 0; while(k < Dimension) { x[k] += C.H[i][j]*Q[k0 + k]; k++; } j--; }
k0 = Dimension*i; GpMemcpy(&Q[k0], &x[0], Dimension*sizeof(REALD)); i--; }
NSteps <<= 1; // The number of steps needed is doubled.
Dt *= 0.5; }
/**************************************************************************\
* * Function Description: * * Doubles the current Bezier segment. * The section of t = 0 -> 2 of the current * Bezier segment becomes the new current segment. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::DoubleStepSize( VOID ) { INT i, j; REALD x[4]; i = NthOrder;
while(i >= 0) { j = i;
GpMemset(&x[0], 0, Dimension*sizeof(REALD));
INT k, k0;
while(j >= 0) { k0 = Dimension*j; k = 0;
while(k < Dimension) { x[k] += C.D[i][j]*Q[k0 + k]; k++; } j--; }
k0 = Dimension*i; GpMemcpy(&Q[k0], &x[0], Dimension*sizeof(REALD)); i--; }
NSteps >>= 1; // The number of steps needed is halved.
Dt *= 2; }
/**************************************************************************\
* * Function Description: * * Shrinks the current Bezier segment by (2^shift). * The section of t = 0 -> 1/(2^shift) of the current * Beizer segment becomes the new current segment. * If shift > 0, this shrinks the step size. * If shift < 0, this enlarge the step size. * * halfStepSize() is equal to fastShrinkStepSize(1). * doubleStepSize() is equal to fastShrinkStepSize(-1). * * Arguments: * * [INT] shift - the bits to shift * * Return Value: * * NONE * * Created: * * 02/10/1998 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::FastShrinkStepSize( INT shift ) { /*
INT n = 1; if(shift > 0) { n <<= shift; Cx /= n; Cy /= n; n <<= shift; Bx /= n; By /= n; n <<= shift; Ax /= n; Ay /= n;
NSteps <<= shift; // Increase the number of steps.
} else if(shift < 0) { n <<= - shift; Cx *= n; Cy *= n; n <<= - shift; Bx *= n; By *= n; n <<= - shift; Ax *= n; Ay *= n;
NSteps >>= - shift; // Reduce the number of steps.
}
// Dx and Dy remain the same.
*/ }
/**************************************************************************\
* * Function Description: * * Advances the current Bezeir segment to the next one. * The section of t = 1 -> 2 of the current Bezier segment * becoms the new current segment. * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 12/16/1998 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::TakeStep( VOID ) { REALD p[16];
INT i, j; REALD x[4]; i = NthOrder;
if(NthOrder != 6) { while(i >= 0) { j = i;
GpMemset(&x[0], 0, Dimension*sizeof(REALD));
INT k0, k;
while(j >= 0) { k0 = Dimension*(NthOrder - j); k = 0;
while(k < Dimension) { x[k] += C.S[i][j]*Q[k0 + k]; k++; } j--; }
k0 = Dimension*i; k = 0;
while(k < Dimension) { p[k0 + k] = x[k]; k++; } i--; }
GpMemcpy(&Q[0], &p[0], Dimension*(NthOrder + 1)*sizeof(REALD)); } else TakeConvergentStep();
NSteps--; // Reduce one step.
T += Dt; }
VOID GpXBezierDDA::TakeConvergentStep( VOID ) { REALD t[7], dt[7]; INT i, j;
t[0] = dt[0] = 1; for(i = 1; i <= 6; i++) { t[i] = t[i-1]*T; dt[i] = dt[i-1]*Dt; }
REALD c[16]; REALD x[4]; INT k0, k;
for(i = 0; i <= 6; i++) { GpMemset(&x[0], 0, Dimension*sizeof(REALD));
for(j = i; j <= 6; j++) { k0 = Dimension*j;
for(k = 0; k < Dimension; k++) { x[k] += C.F[i][j]*t[j-i]*dt[i]*P[k0 + k]; } }
k0 = Dimension*i; GpMemcpy(&c[k0], &x[0], Dimension*sizeof(REALD)); }
for(i = 0; i <= 6; i++) { GpMemset(&x[0], 0, Dimension*sizeof(REALD));
for(j = 0; j <= i; j++) { k0 = Dimension*j; for(k = 0; k < Dimension; k++) { x[k] += C.H6[i][j]*c[k0 + k]; } } k0 = Dimension*i; GpMemcpy(&Q[k0], &x[0], Dimension*sizeof(REALD)); } } BOOL GpXBezierDDA::Get2DDistanceVector( REALD* dx, REALD* dy, INT from, INT to ) { REALD p0[16], p1[16]; INT k0, k;
if(from < 0 || from > NthOrder || to < 0 || to > NthOrder) return FALSE;
k0 = from*Dimension; GpMemcpy(&p0[0], &Q[k0], Dimension*sizeof(REALD));
k0 = to*Dimension; GpMemcpy(&p1[0], &Q[k0], Dimension*sizeof(REALD));
*dx = p1[0] - p0[0]; *dy = p1[1] - p0[1];
return TRUE; }
/**************************************************************************\
* * Function Description: * * Reurns true if more subdivision is necessary. * * Arguments: * * [IN] flatnessLimit - flatness parameter * * Return Value: * * Returns true if subdivision is necessary. Otherwise this returns false. * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
BOOL GpXBezierDDA::NeedsSubdivide( REAL flatnessLimit ) { REALD mx, my; REALD baseLen; REALD dx, dy;
// Get the base line vector.
if(!Get2DDistanceVector(&dx, &dy, 0, NthOrder)) return FALSE; // Get the perpendicular vector to the base line
mx = - dy; my = dx;
// Approximate the distance by absolute values of x and y components.
baseLen = fabs(mx) + fabs(my);
BOOL needsSubdivide = FALSE;
// First check if the base length is larger than the distance limit.
if(baseLen > DistanceLimit) { // Pre-multiply baseLen by flatness limit for convenience.
baseLen *= flatnessLimit; INT i = 1;
while(i < NthOrder && !needsSubdivide) { Get2DDistanceVector(&dx, &dy, 0, i);
if(fabs(dx*mx + dy*my) > baseLen) needsSubdivide = TRUE; i++; } }
return needsSubdivide; }
/**************************************************************************\
* * Function Description: * * Returns the current start point. * If this has reached the end point, this returns false. * * Arguments: * * [OUT] pt - the current start point. * * Return Value: * * Returns true if there is a next point. * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
BOOL GpXBezierDDA::GetNextPoint( GpPointF* pt ) { // Copy the current start point;
FPUStateSaver fpuState;
switch(Dimension) { case 2: pt->X = TOREAL(Q[0]); pt->Y = TOREAL(Q[1]); break;
case 3: default: // Do something for projection.
return FALSE; }
if(NSteps != 0) return TRUE; else return FALSE; // Congratulations! You have reached the end.
}
/**************************************************************************\
* * Function Description: * * Moves to the next Bezier segment * * Arguments: * * NONE * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
VOID GpXBezierDDA::MoveForward( VOID ) { // If the current subdivide is too big,
// subdivide it.
while(NeedsSubdivide(FlatnessLimit)) { HalveStepSize(); }
if((NSteps & 1) == 0) { // If the current subdivide is too small,
// double it up.
while(NSteps > 1 && !NeedsSubdivide(FlatnessLimit/4)) { DoubleStepSize(); } }
// Move to the next Bezier segment.
TakeStep(); }
/**************************************************************************\
* * Function Description: * * Returns the control points of the last Bezier segment * which ends at the current point. * The current point is given by calling getNextPoint(). * pts[] must have the dimension of 4. * * Arguments: * * [OUT] pts - the Bezier control points * * Return Value: * * NONE * * Created: * * 02/10/1999 ikkof * Created it. * \**************************************************************************/
INT GpXBezierDDA::GetControlPoints( GpXPoints* xpoints ) { ASSERT(xpoints);
if(xpoints == NULL) return 0;
INT totalCount = Dimension*(NthOrder + 1); REALD* buff = (REALD*) GpRealloc(xpoints->Data, totalCount*sizeof(REALD)); if(buff) { GpMemcpy(buff, &Q[0], Dimension*(NthOrder + 1)*sizeof(REALD)); xpoints->Count = NthOrder + 1; xpoints->Dimension = Dimension; xpoints->Data = buff;
return NthOrder + 1; } else return 0; }
|