421 lines
9.9 KiB
C++
421 lines
9.9 KiB
C++
|
/**************************************************************************\
|
||
|
*
|
||
|
* Copyright (c) 1998 Microsoft Corporation
|
||
|
*
|
||
|
* Module Name:
|
||
|
*
|
||
|
* XBezier.hpp
|
||
|
*
|
||
|
* Abstract:
|
||
|
*
|
||
|
* Interface of GpXBezier and its DDA classes
|
||
|
*
|
||
|
* Revision History:
|
||
|
*
|
||
|
* 11/05/1999 ikkof
|
||
|
* Created it.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#ifndef _XBEZIER_HPP
|
||
|
#define _XBEZIER_HPP
|
||
|
|
||
|
#define FLATNESS_LIMIT 0.25
|
||
|
#define DISTANCE_LIMIT 2.0
|
||
|
#define BZ_BUFF_SIZE 32
|
||
|
|
||
|
class GpXPoints
|
||
|
{
|
||
|
friend class GpXPath;
|
||
|
friend class GpXBezier;
|
||
|
|
||
|
public:
|
||
|
|
||
|
INT Dimension;
|
||
|
INT Count; // Number of Points
|
||
|
REALD* Data;
|
||
|
|
||
|
protected:
|
||
|
BOOL IsDataAllocated;
|
||
|
|
||
|
public:
|
||
|
|
||
|
GpXPoints()
|
||
|
{
|
||
|
Initialize();
|
||
|
}
|
||
|
|
||
|
// When XPoints is created from a given data with copyData = FALSE,
|
||
|
// the caller is responsible for deleting the data after XPoints is no longer
|
||
|
// used.
|
||
|
|
||
|
GpXPoints(REALD* data, INT dimension, INT count, BOOL copyData = TRUE)
|
||
|
{
|
||
|
Initialize();
|
||
|
SetData(data, dimension, count, copyData);
|
||
|
}
|
||
|
|
||
|
GpXPoints(GpPointF* points, INT count)
|
||
|
{
|
||
|
Initialize();
|
||
|
|
||
|
if(points && count > 0)
|
||
|
{
|
||
|
Data = (REALD*) GpMalloc(2*count*sizeof(REALD));
|
||
|
if(Data)
|
||
|
{
|
||
|
INT i = 0, j = 0;
|
||
|
|
||
|
while(j < count)
|
||
|
{
|
||
|
Data[i++] = points[j].X;
|
||
|
Data[i++] = points[j].Y;
|
||
|
j++;
|
||
|
}
|
||
|
Dimension = 2;
|
||
|
Count = count;
|
||
|
IsDataAllocated = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GpXPoints(GpPointD* points, INT count)
|
||
|
{
|
||
|
Initialize();
|
||
|
|
||
|
if(points && count > 0)
|
||
|
{
|
||
|
Data = (REALD*) GpMalloc(2*count*sizeof(REALD));
|
||
|
if(Data)
|
||
|
{
|
||
|
INT i = 0, j = 0;
|
||
|
|
||
|
while(j < count)
|
||
|
{
|
||
|
Data[i++] = points[j].X;
|
||
|
Data[i++] = points[j].Y;
|
||
|
j++;
|
||
|
}
|
||
|
Dimension = 2;
|
||
|
Count = count;
|
||
|
IsDataAllocated = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
REALD* GetData() {return Data;}
|
||
|
|
||
|
// When XPoints is created from a given data with copyData = FALSE,
|
||
|
// the caller is responsible for deleting the data after XPoints is no longer
|
||
|
// used.
|
||
|
|
||
|
GpStatus
|
||
|
SetData(REALD* data, INT dimension, INT count, BOOL copyData = TRUE)
|
||
|
{
|
||
|
GpStatus status = Ok;
|
||
|
|
||
|
if(data && dimension > 0 || count > 0)
|
||
|
{
|
||
|
REALD* newData = NULL;
|
||
|
|
||
|
if(copyData)
|
||
|
{
|
||
|
INT totalSize = dimension*count*sizeof(REALD);
|
||
|
if(IsDataAllocated)
|
||
|
newData = (REALD*) GpRealloc(Data, totalSize);
|
||
|
else
|
||
|
newData = (REALD*) GpMalloc(totalSize);
|
||
|
|
||
|
if(newData)
|
||
|
{
|
||
|
GpMemcpy(newData, data, totalSize);
|
||
|
IsDataAllocated;
|
||
|
}
|
||
|
else
|
||
|
status = OutOfMemory;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(Data && IsDataAllocated)
|
||
|
GpFree(Data);
|
||
|
newData = data;
|
||
|
IsDataAllocated = FALSE;
|
||
|
}
|
||
|
|
||
|
if(status == Ok)
|
||
|
{
|
||
|
Dimension = dimension;
|
||
|
Count = count;
|
||
|
Data = newData;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
status = InvalidParameter;
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
GpStatus Transform(const GpMatrix* matrix);
|
||
|
|
||
|
BOOL AreEqualPoints(INT index1, INT index2)
|
||
|
{
|
||
|
if(index1 < 0 || index1 >= Count
|
||
|
|| index2 < 0 || index2 >= Count || Data == NULL)
|
||
|
return FALSE; // either index is out of the range or no data.
|
||
|
|
||
|
BOOL areEqual = TRUE;
|
||
|
if(index1 != index2)
|
||
|
{
|
||
|
REALD* data1 = Data + index1*Dimension;
|
||
|
REALD* data2 = Data + index2*Dimension;
|
||
|
INT k = 0;
|
||
|
while(k < Dimension && areEqual)
|
||
|
{
|
||
|
if(*data1++ != *data2++)
|
||
|
areEqual = FALSE;
|
||
|
k++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return areEqual;
|
||
|
}
|
||
|
|
||
|
static GpStatus
|
||
|
GpXPoints::TransformPoints(
|
||
|
const GpMatrix* matrix,
|
||
|
REALD* data,
|
||
|
INT dimension,
|
||
|
INT count
|
||
|
);
|
||
|
|
||
|
~GpXPoints()
|
||
|
{
|
||
|
if(Data && IsDataAllocated)
|
||
|
GpFree(Data);
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
VOID Initialize()
|
||
|
{
|
||
|
Dimension = 0;
|
||
|
Count = 0;
|
||
|
Data = NULL;
|
||
|
IsDataAllocated = FALSE;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//********************************************************
|
||
|
// GpXBezierDDA class
|
||
|
//********************************************************
|
||
|
|
||
|
class GpXBezierConstants
|
||
|
{
|
||
|
friend class GpXBezierDDA;
|
||
|
|
||
|
private:
|
||
|
REALD H[7][7]; // Half step
|
||
|
REALD D[7][7]; // Double step
|
||
|
REALD S[7][7]; // One step
|
||
|
REALD F[7][7]; // Polynomical transform.
|
||
|
REALD H6[7][7]; // Poly to Bez transform in 6th order.
|
||
|
REALD G6[7][7]; // Bez to Poly transform in 6th order.
|
||
|
|
||
|
public:
|
||
|
GpXBezierConstants();
|
||
|
};
|
||
|
|
||
|
class GpXBezierDDA
|
||
|
{
|
||
|
protected:
|
||
|
GpXBezierConstants C;
|
||
|
|
||
|
protected:
|
||
|
REALD T;
|
||
|
REALD Dt;
|
||
|
REALD Q[16];
|
||
|
REALD P[16];
|
||
|
INT NthOrder;
|
||
|
INT Dimension;
|
||
|
INT NSteps;
|
||
|
REAL FlatnessLimit;
|
||
|
REAL DistanceLimit;
|
||
|
|
||
|
public:
|
||
|
|
||
|
public:
|
||
|
|
||
|
GpXBezierDDA() { Initialize(); }
|
||
|
|
||
|
GpXBezierDDA(
|
||
|
const GpXPoints& xpoints,
|
||
|
REAL flatnessLimit = FLATNESS_LIMIT,
|
||
|
REAL distanceLimit = DISTANCE_LIMIT
|
||
|
)
|
||
|
{
|
||
|
Initialize();
|
||
|
SetBezier(xpoints, flatnessLimit, distanceLimit);
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
SetBezier(
|
||
|
const GpXPoints& xpoints,
|
||
|
REAL flatnessLimit = FLATNESS_LIMIT,
|
||
|
REAL distanceLimit = DISTANCE_LIMIT
|
||
|
);
|
||
|
|
||
|
INT GetSteps() { return NSteps; }
|
||
|
|
||
|
VOID InitDDA(GpPointF* pt);
|
||
|
VOID HalveStepSize();
|
||
|
VOID DoubleStepSize();
|
||
|
VOID FastShrinkStepSize(INT shift);
|
||
|
VOID TakeStep();
|
||
|
BOOL NeedsSubdivide(REAL itsFlatnessLimit);
|
||
|
BOOL GetNextPoint(GpPointF* pt);
|
||
|
VOID MoveForward();
|
||
|
INT GetControlPoints(GpXPoints* xpoints);
|
||
|
|
||
|
protected:
|
||
|
|
||
|
VOID Initialize();
|
||
|
VOID SetPolynomicalCoefficients();
|
||
|
VOID TakeConvergentStep();
|
||
|
BOOL Get2DDistanceVector(REALD* dx, REALD* dy, INT from, INT to);
|
||
|
};
|
||
|
|
||
|
//************************************
|
||
|
// XBezier class
|
||
|
//************************************
|
||
|
|
||
|
#define NthOrderMax 6
|
||
|
|
||
|
class GpXBezier
|
||
|
{
|
||
|
private:
|
||
|
// We now use an ObjectTag to determine if the object is valid
|
||
|
// instead of using a BOOL. This is much more robust and helps
|
||
|
// with debugging. It also enables us to version our objects
|
||
|
// more easily with a version number in the ObjectTag.
|
||
|
ObjectTag Tag; // Keep this as the 1st value in the object!
|
||
|
|
||
|
protected:
|
||
|
VOID SetValid(BOOL valid)
|
||
|
{
|
||
|
Tag = valid ? ObjectTagGpBezier : ObjectTagInvalid;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
BOOL IsValid() const
|
||
|
{
|
||
|
ASSERT((Tag == ObjectTagGpBezier) || (Tag == ObjectTagInvalid));
|
||
|
#if DBG
|
||
|
if (Tag == ObjectTagInvalid)
|
||
|
{
|
||
|
WARNING1("Invalid GpBezier");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return (Tag == ObjectTagGpBezier);
|
||
|
}
|
||
|
|
||
|
GpXBezier()
|
||
|
{
|
||
|
Initialize();
|
||
|
}
|
||
|
|
||
|
GpXBezier(INT order, const GpPointF* points, INT count)
|
||
|
{
|
||
|
Initialize();
|
||
|
SetValid(SetBeziers(order, points, count));
|
||
|
}
|
||
|
|
||
|
GpXBezier(INT order, const GpXPoints& xpoints)
|
||
|
{
|
||
|
Initialize();
|
||
|
SetValid(SetBeziers(order, xpoints));
|
||
|
}
|
||
|
|
||
|
~GpXBezier();
|
||
|
|
||
|
GpStatus SetBeziers(INT order, const GpPointF* points, INT count);
|
||
|
|
||
|
GpStatus SetBeziers(INT order, const GpXPoints& xpoints);
|
||
|
|
||
|
virtual INT GetControlCount() {return Count;}
|
||
|
virtual VOID GetBounds(GpMatrix* matrix, GpRect* bounds);
|
||
|
virtual VOID Transform(GpMatrix* matrix);
|
||
|
virtual GpStatus Flatten(
|
||
|
DynPointFArray* flattenPts,
|
||
|
const GpMatrix* matrix);
|
||
|
|
||
|
protected:
|
||
|
|
||
|
VOID Initialize()
|
||
|
{
|
||
|
NthOrder = 0;
|
||
|
Dimension = 0;
|
||
|
Count = 0;
|
||
|
Data = NULL;
|
||
|
FlatnessLimit = FLATNESS_LIMIT;
|
||
|
DistanceLimit = DISTANCE_LIMIT;
|
||
|
SetValid(TRUE);
|
||
|
}
|
||
|
|
||
|
GpStatus
|
||
|
FlattenEachBezier(
|
||
|
DynPointFArray* flattenPts,
|
||
|
GpXBezierDDA& dda,
|
||
|
BOOL isFirstBezier,
|
||
|
const GpMatrix* matrix,
|
||
|
const REALD* bezierData
|
||
|
);
|
||
|
|
||
|
GpStatus
|
||
|
Get2DPoints(
|
||
|
GpPointF* points,
|
||
|
INT count,
|
||
|
const REALD* dataPoints,
|
||
|
const GpMatrix* matrix = NULL);
|
||
|
|
||
|
GpStatus CheckInputData(const GpPointF* points, INT count)
|
||
|
{
|
||
|
GpStatus status = InvalidParameter;
|
||
|
if(NthOrder > 0)
|
||
|
{
|
||
|
if(count > NthOrder)
|
||
|
{
|
||
|
INT reminder = count % NthOrder;
|
||
|
if(reminder == 1 && points !=NULL)
|
||
|
status = Ok;
|
||
|
}
|
||
|
}
|
||
|
else // NthOrder <= 0
|
||
|
{
|
||
|
if(count > 1 && points != NULL)
|
||
|
{
|
||
|
if(count <= NthOrderMax + 1)
|
||
|
{
|
||
|
NthOrder = count - 1;
|
||
|
status = Ok;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
protected: // GDI+ INTERNAL
|
||
|
// Following are the two values to determin the flatness.
|
||
|
REAL FlatnessLimit; // The maximum flateness.
|
||
|
REAL DistanceLimit; // The minimum distance.
|
||
|
|
||
|
private: // GDI+ INTERNAL
|
||
|
INT NthOrder;
|
||
|
INT Dimension;
|
||
|
INT Count;
|
||
|
REALD* Data;
|
||
|
};
|
||
|
|
||
|
|
||
|
#endif
|