WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/xbezier.cpp

1446 lines
29 KiB
C++
Raw Permalink Normal View History

2001-01-01 00:00:00 +01:00
/**************************************************************************\
*
* 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;
REALD dx, dy;
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;
}