/**************************************************************************\ * * 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; }