Bug 349178 - Implement getPathSegAtLength.

Patch by amenzie@us.ibm.com, r=tor, sr=roc
This commit is contained in:
tor%cs.brown.edu 2006-09-11 15:32:28 +00:00
Родитель 18d12280d2
Коммит c3caf33717
5 изменённых файлов: 517 добавлений и 105 удалений

Просмотреть файл

@ -1075,104 +1075,27 @@ nsSVGPathDataParserToInternal::ConvertArcToCurves(float x2, float y2,
PRBool largeArcFlag,
PRBool sweepFlag)
{
const double radPerDeg = M_PI/180.0;
double x1=mPx, y1=mPy;
// 1. Treat out-of-range parameters as described in
float x1=mPx, y1=mPy, x3, y3;
// Treat out-of-range parameters as described in
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
// If the endpoints (x1, y1) and (x2, y2) are identical, then this
// is equivalent to omitting the elliptical arc segment entirely
if (x1 == x2 && y1 == y2)
if (x1 == x2 && y1 == y2) {
return NS_OK;
}
// If rX = 0 or rY = 0 then this arc is treated as a straight line
// segment (a "lineto") joining the endpoints.
if (rx == 0.0f || ry == 0.0f) {
return PathLineTo(x2, y2);
}
// If rX or rY have negative signs, these are dropped; the absolute
// value is used instead.
if (rx<0.0) rx = -rx;
if (ry<0.0) ry = -ry;
nsSVGArcConverter converter(x1, y1, x2, y2, rx, ry, angle,
largeArcFlag, sweepFlag);
// 2. convert to center parameterization as shown in
// http://www.w3.org/TR/SVG/implnote.html
double sinPhi = sin(angle*radPerDeg);
double cosPhi = cos(angle*radPerDeg);
double x1dash = cosPhi * (x1-x2)/2.0 + sinPhi * (y1-y2)/2.0;
double y1dash = -sinPhi * (x1-x2)/2.0 + cosPhi * (y1-y2)/2.0;
double root;
double numerator = rx*rx*ry*ry - rx*rx*y1dash*y1dash - ry*ry*x1dash*x1dash;
if (numerator < 0.0) {
// If rX , rY and are such that there is no solution (basically,
// the ellipse is not big enough to reach from (x1, y1) to (x2,
// y2)) then the ellipse is scaled up uniformly until there is
// exactly one solution (until the ellipse is just big enough).
// -> find factor s, such that numerator' with rx'=s*rx and
// ry'=s*ry becomes 0 :
float s = (float)sqrt(1.0 - numerator/(rx*rx*ry*ry));
rx *= s;
ry *= s;
root = 0.0;
}
else {
root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
sqrt( numerator/(rx*rx*y1dash*y1dash + ry*ry*x1dash*x1dash) );
}
double cxdash = root*rx*y1dash/ry;
double cydash = -root*ry*x1dash/rx;
double cx = cosPhi * cxdash - sinPhi * cydash + (x1+x2)/2.0;
double cy = sinPhi * cxdash + cosPhi * cydash + (y1+y2)/2.0;
double theta1 = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/rx, (y1dash-cydash)/ry);
double dtheta = CalcVectorAngle((x1dash-cxdash)/rx, (y1dash-cydash)/ry,
(-x1dash-cxdash)/rx, (-y1dash-cydash)/ry);
if (!sweepFlag && dtheta>0)
dtheta -= 2.0*M_PI;
else if (sweepFlag && dtheta<0)
dtheta += 2.0*M_PI;
// 3. convert into cubic bezier segments <= 90deg
int segments = (int)ceil(fabs(dtheta/(M_PI/2.0)));
double delta = dtheta/segments;
double t = 8.0/3.0 * sin(delta/4.0) * sin(delta/4.0) / sin(delta/2.0);
for (int i = 0; i < segments; ++i) {
double cosTheta1 = cos(theta1);
double sinTheta1 = sin(theta1);
double theta2 = theta1 + delta;
double cosTheta2 = cos(theta2);
double sinTheta2 = sin(theta2);
// a) calculate endpoint of the segment:
double xe = cosPhi * rx*cosTheta2 - sinPhi * ry*sinTheta2 + cx;
double ye = sinPhi * rx*cosTheta2 + cosPhi * ry*sinTheta2 + cy;
// b) calculate gradients at start/end points of segment:
double dx1 = t * ( - cosPhi * rx*sinTheta1 - sinPhi * ry*cosTheta1);
double dy1 = t * ( - sinPhi * rx*sinTheta1 + cosPhi * ry*cosTheta1);
double dxe = t * ( cosPhi * rx*sinTheta2 + sinPhi * ry*cosTheta2);
double dye = t * ( sinPhi * rx*sinTheta2 - cosPhi * ry*cosTheta2);
while (converter.GetNextSegment(&x1, &y1, &x2, &y2, &x3, &y3)) {
// c) draw the cubic bezier:
nsresult rv = PathCurveTo(x1+dx1, y1+dy1, xe+dxe, ye+dye, xe, ye);
nsresult rv = PathCurveTo(x1, y1, x2, y2, x3, y3);
NS_ENSURE_SUCCESS(rv, rv);
// do next segment
theta1 = theta2;
x1 = (float)xe;
y1 = (float)ye;
}
return NS_OK;
@ -1420,3 +1343,107 @@ nsSVGPathDataParserToDOM::StoreEllipticalArc(PRBool absCoords,
largeArcFlag, sweepFlag));
}
nsSVGArcConverter::nsSVGArcConverter(float x1, float y1,
float x2, float y2,
float rx, float ry,
float angle,
PRBool largeArcFlag,
PRBool sweepFlag)
{
const double radPerDeg = M_PI/180.0;
// If rX or rY have negative signs, these are dropped; the absolute
// value is used instead.
mRx = fabs(rx);
mRy = fabs(ry);
// Convert to center parameterization as shown in
// http://www.w3.org/TR/SVG/implnote.html
mSinPhi = sin(angle*radPerDeg);
mCosPhi = cos(angle*radPerDeg);
double x1dash = mCosPhi * (x1-x2)/2.0 + mSinPhi * (y1-y2)/2.0;
double y1dash = -mSinPhi * (x1-x2)/2.0 + mCosPhi * (y1-y2)/2.0;
double root;
double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash -
mRy*mRy*x1dash*x1dash;
if (numerator < 0.0) {
// If mRx , mRy and are such that there is no solution (basically,
// the ellipse is not big enough to reach from (x1, y1) to (x2,
// y2)) then the ellipse is scaled up uniformly until there is
// exactly one solution (until the ellipse is just big enough).
// -> find factor s, such that numerator' with mRx'=s*mRx and
// mRy'=s*mRy becomes 0 :
float s = (float)sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy));
mRx *= s;
mRy *= s;
root = 0.0;
}
else {
root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) );
}
double cxdash = root*mRx*y1dash/mRy;
double cydash = -root*mRy*x1dash/mRx;
mCx = mCosPhi * cxdash - mSinPhi * cydash + (x1+x2)/2.0;
mCy = mSinPhi * cxdash + mCosPhi * cydash + (y1+y2)/2.0;
mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy);
double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy,
(-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy);
if (!sweepFlag && dtheta>0)
dtheta -= 2.0*M_PI;
else if (sweepFlag && dtheta<0)
dtheta += 2.0*M_PI;
// Convert into cubic bezier segments <= 90deg
mNumSegs = NS_STATIC_CAST(int, ceil(fabs(dtheta/(M_PI/2.0))));
mDelta = dtheta/mNumSegs;
mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0);
mX1 = x1;
mY1 = y1;
mSegIndex = 0;
}
PRBool
nsSVGArcConverter::GetNextSegment(float *x1, float *y1,
float *x2, float *y2,
float *x3, float *y3)
{
if (mSegIndex == mNumSegs) {
return PR_FALSE;
}
float cosTheta1 = cos(mTheta);
float sinTheta1 = sin(mTheta);
float theta2 = mTheta + mDelta;
float cosTheta2 = cos(theta2);
float sinTheta2 = sin(theta2);
// a) calculate endpoint of the segment:
*x3 = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mCx;
*y3 = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mCy;
// b) calculate gradients at start/end points of segment:
*x1 = mX1 + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1);
*y1 = mY1 + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1);
*x2 = *x3 + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2);
*y2 = *y3 + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2);
// do next segment
mTheta = theta2;
mX1 = *x3;
mY1 = *y3;
++mSegIndex;
return PR_TRUE;
}

Просмотреть файл

@ -221,4 +221,24 @@ private:
nsCOMArray<nsIDOMSVGPathSeg>* mData;
};
class nsSVGArcConverter
{
public:
nsSVGArcConverter(float x1, float y1,
float x2, float y2,
float rx, float ry,
float angle,
PRBool largeArcFlag,
PRBool sweepFlag);
PRBool GetNextSegment(float *x1, float *y1,
float *x2, float *y2,
float *x3, float *y3);
protected:
PRInt32 mNumSegs, mSegIndex;
float mTheta, mDelta, mT;
float mSinPhi, mCosPhi;
float mX1, mY1, mRx, mRy, mCx, mCy;
};
#endif // __NS_SVGPATHDATAPARSER_H__

Просмотреть файл

@ -139,7 +139,38 @@ nsSVGPathElement::GetPointAtLength(float distance, nsIDOMSVGPoint **_retval)
NS_IMETHODIMP
nsSVGPathElement::GetPathSegAtLength(float distance, PRUint32 *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
//Check if mSegments is null
nsresult rv = CreatePathSegList();
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 i = 0, numSegments;
float distCovered = 0;
nsSVGPathSegTraversalState ts;
mSegments->GetNumberOfItems(&numSegments);
// There is no need to check to see if distance falls within the last segment
// because if distance is longer than the total length of the path we return
// the index of the final segment anyway.
while (distCovered < distance && i < numSegments - 1) {
nsIDOMSVGPathSeg *iSeg;
mSegments->GetItem(i, &iSeg);
nsSVGPathSeg* curSeg = NS_STATIC_CAST(nsSVGPathSeg*, iSeg);
if (i == 0) {
curSeg->GetLength(&ts);
} else {
distCovered += curSeg->GetLength(&ts);
}
if (distCovered >= distance) {
break;
}
++i;
}
*_retval = i;
return NS_OK;
}
/* nsIDOMSVGPathSegClosePath createSVGPathSegClosePath (); */

Просмотреть файл

@ -42,6 +42,15 @@
#include "nsTextFormatter.h"
#include "nsContentUtils.h"
struct PathPoint {
float x, y;
};
//----------------------------------------------------------------------
// Error threshold to use when calculating the length of Bezier curves
static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f;
//----------------------------------------------------------------------
// implementation helper macros
@ -54,6 +63,73 @@ NS_INTERFACE_MAP_BEGIN(ns##basename) \
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(basename) \
NS_INTERFACE_MAP_END_INHERITING(nsSVGPathSeg)
//----------------------------------------------------------------------
// GetLength Helper Functions
static float GetDistance(float x1, float x2, float y1, float y2)
{
return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
static void SplitQuadraticBezier(PathPoint *curve,
PathPoint *left,
PathPoint *right)
{
left[0].x = curve[0].x;
left[0].y = curve[0].y;
right[2].x = curve[2].x;
right[2].y = curve[2].y;
left[1].x = (curve[0].x + curve[1].x) / 2;
left[1].y = (curve[0].y + curve[1].y) / 2;
right[1].x = (curve[1].x + curve[2].x) / 2;
right[1].y = (curve[1].y + curve[2].y) / 2;
left[2].x = right[0].x = (left[1].x + right[1].x) / 2;
left[2].y = right[0].y = (left[1].y + right[1].y) / 2;
}
static void SplitCubicBezier(PathPoint *curve,
PathPoint *left,
PathPoint *right)
{
PathPoint tmp;
tmp.x = (curve[1].x + curve[2].x) / 4;
tmp.y = (curve[1].y + curve[2].y) / 4;
left[0].x = curve[0].x;
left[0].y = curve[0].y;
right[3].x = curve[3].x;
right[3].y = curve[3].y;
left[1].x = (curve[0].x + curve[1].x) / 2;
left[1].y = (curve[0].y + curve[1].y) / 2;
right[2].x = (curve[2].x + curve[3].x) / 2;
right[2].y = (curve[2].y + curve[3].y) / 2;
left[2].x = left[1].x / 2 + tmp.x;
left[2].y = left[1].y / 2 + tmp.y;
right[1].x = right[2].x / 2 + tmp.x;
right[1].y = right[2].y / 2 + tmp.y;
left[3].x = right[0].x = (left[2].x + right[1].x) / 2;
left[3].y = right[0].y = (left[2].y + right[1].y) / 2;
}
static float CalcBezLength(PathPoint *curve,PRUint32 numPts,
void (*split)(PathPoint*, PathPoint*, PathPoint*))
{
PathPoint left[4];
PathPoint right[4];
float length = 0, dist;
PRUint32 i;
for (i = 0; i < numPts - 1; i++) {
length += GetDistance(curve[i].x, curve[i+1].x, curve[i].y, curve[i+1].y);
}
dist = GetDistance(curve[0].x, curve[numPts - 1].x,
curve[0].y, curve[numPts - 1].y);
if (length - dist > PATH_SEG_LENGTH_TOLERANCE) {
split(curve, left, right);
return CalcBezLength(left, numPts, split) +
CalcBezLength(right, numPts, split);
}
return length;
}
////////////////////////////////////////////////////////////////////////
// nsSVGPathSeg
@ -127,7 +203,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH; }
};
@ -154,6 +230,16 @@ nsSVGPathSegClosePath::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegClosePath::GetLength(nsSVGPathSegTraversalState *ts)
{
float dist = GetDistance(ts->startPosX, ts->curPosX,
ts->startPosY, ts->curPosY);
ts->quadCPX = ts->cubicCPX = ts->curPosX = ts->startPosX;
ts->quadCPY = ts->cubicCPY = ts->curPosY = ts->startPosY;
return dist;
}
////////////////////////////////////////////////////////////////////////
// nsSVGPathSegMovetoAbs
@ -171,7 +257,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS; }
@ -210,6 +296,14 @@ nsSVGPathSegMovetoAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegMovetoAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->cubicCPX = ts->quadCPX = ts->startPosX = ts->curPosX = mX;
ts->cubicCPY = ts->quadCPY = ts->startPosY = ts->curPosY = mY;
return 0;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegMovetoAbs methods:
@ -257,7 +351,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL; }
@ -295,6 +389,15 @@ nsSVGPathSegMovetoRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegMovetoRel::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->cubicCPX = ts->quadCPX = ts->startPosX = ts->curPosX += mX;
ts->cubicCPY = ts->quadCPY = ts->startPosY = ts->curPosY += mY;
return 0;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegMovetoRel methods:
@ -341,7 +444,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS; }
@ -379,6 +482,17 @@ nsSVGPathSegLinetoAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
float dist = GetDistance(ts->curPosX, mX, ts->curPosY, mY);
ts->cubicCPX = ts->quadCPX = ts->curPosX = mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY = mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoAbs methods:
@ -426,7 +540,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_REL; }
@ -465,6 +579,14 @@ nsSVGPathSegLinetoRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoRel::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->cubicCPX = ts->quadCPX = ts->curPosX += mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY += mY;
return sqrt(mX * mX + mY * mY);
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoRel methods:
@ -514,7 +636,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS; }
@ -558,6 +680,22 @@ nsSVGPathSegCurvetoCubicAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoCubicAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint curve[4] = { {ts->curPosX, ts->curPosY},
{mX1, mY1},
{mX2, mY2},
{mX, mY} };
float dist = CalcBezLength(curve, 4, SplitCubicBezier);
ts->quadCPX = ts->curPosX = mX;
ts->quadCPY = ts->curPosY = mY;
ts->cubicCPX = mX2;
ts->cubicCPY = mY2;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoCubicAbs methods:
@ -659,7 +797,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL; }
@ -703,6 +841,19 @@ nsSVGPathSegCurvetoCubicRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoCubicRel::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint curve[4] = { {0, 0}, {mX1, mY1}, {mX2, mY2}, {mX, mY} };
float dist = CalcBezLength(curve, 4, SplitCubicBezier);
ts->cubicCPX = mX2 + ts->curPosX;
ts->cubicCPY = mY2 + ts->curPosY;
ts->quadCPX = ts->curPosX += mX;
ts->quadCPY = ts->curPosY += mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoCubicRel methods:
@ -802,7 +953,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS; }
@ -845,6 +996,19 @@ nsSVGPathSegCurvetoQuadraticAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoQuadraticAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint curve[3] = { {ts->curPosX, ts->curPosY}, {mX1, mY1}, {mX, mY} };
float dist = CalcBezLength(curve, 3, SplitQuadraticBezier);
ts->quadCPX = mX1;
ts->quadCPY = mY1;
ts->cubicCPX = ts->curPosX = mX;
ts->cubicCPY = ts->curPosY = mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoQuadraticAbs methods:
@ -919,7 +1083,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL; }
@ -963,6 +1127,19 @@ nsSVGPathSegCurvetoQuadraticRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoQuadraticRel::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint curve[3] = { {0, 0}, {mX1, mY1}, {mX, mY} };
float dist = CalcBezLength(curve, 3, SplitQuadraticBezier);
ts->quadCPX = mX1 + ts->curPosX;
ts->quadCPY = mY1 + ts->curPosY;
ts->cubicCPX = ts->curPosX += mX;
ts->cubicCPY = ts->curPosY += mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoQuadraticRel methods:
@ -1038,7 +1215,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_ARC_ABS; }
@ -1086,6 +1263,25 @@ nsSVGPathSegArcAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegArcAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint bez[4] = { {ts->curPosX, ts->curPosY}, {0,0}, {0,0}, {0,0}};
nsSVGArcConverter converter(ts->curPosX, ts->curPosY, mX, mY, mR1,
mR2, mAngle, mLargeArcFlag, mSweepFlag);
float dist = 0;
while (converter.GetNextSegment(&bez[1].x, &bez[1].y, &bez[2].x, &bez[2].y,
&bez[3].x, &bez[3].y))
{
dist += CalcBezLength(bez, 4, SplitCubicBezier);
bez[0].x = bez[3].x;
bez[0].y = bez[3].y;
}
ts->cubicCPX = ts->quadCPX = ts->curPosX = mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY = mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegArcAbs methods:
@ -1200,7 +1396,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_ARC_REL; }
@ -1247,6 +1443,26 @@ nsSVGPathSegArcRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegArcRel::GetLength(nsSVGPathSegTraversalState *ts)
{
PathPoint bez[4] = { {0, 0}, {0,0}, {0,0}, {0,0}};
nsSVGArcConverter converter(0, 0, mX, mY, mR1, mR2, mAngle,
mLargeArcFlag, mSweepFlag);
float dist = 0;
while (converter.GetNextSegment(&bez[1].x, &bez[1].y, &bez[2].x, &bez[2].y,
&bez[3].x, &bez[3].y))
{
dist += CalcBezLength(bez, 4, SplitCubicBezier);
bez[0].x = bez[3].x;
bez[0].y = bez[3].y;
}
ts->cubicCPX = ts->quadCPX = ts->curPosX += mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY += mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegArcRel methods:
@ -1359,7 +1575,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS; }
@ -1399,6 +1615,15 @@ nsSVGPathSegLinetoHorizontalAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoHorizontalAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
float dist = fabs(mX - ts->curPosX);
ts->cubicCPX = ts->quadCPX = ts->curPosX = mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoHorizontalAbs methods:
@ -1432,7 +1657,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL; }
@ -1472,6 +1697,14 @@ nsSVGPathSegLinetoHorizontalRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoHorizontalRel::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->cubicCPX = ts->quadCPX = ts->curPosX += mX;
ts->cubicCPY = ts->quadCPY = ts->curPosY;
return fabs(mX);
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoHorizontalRel methods:
@ -1505,7 +1738,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS; }
@ -1545,6 +1778,15 @@ nsSVGPathSegLinetoVerticalAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoVerticalAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
float dist = fabs(mY - ts->curPosY);
ts->cubicCPY = ts->quadCPY = ts->curPosY = mY;
ts->cubicCPX = ts->quadCPX = ts->curPosX;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoVerticalAbs methods:
@ -1578,7 +1820,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL; }
@ -1619,6 +1861,14 @@ nsSVGPathSegLinetoVerticalRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegLinetoVerticalRel::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->cubicCPY = ts->quadCPY = ts->curPosY += mY;
ts->cubicCPX = ts->quadCPX = ts->curPosX;
return fabs(mY);
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegLinetoVerticalRel methods:
@ -1653,7 +1903,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; }
@ -1696,6 +1946,25 @@ nsSVGPathSegCurvetoCubicSmoothAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoCubicSmoothAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
float x1 = 2 * ts->curPosX - ts->cubicCPX;
float y1 = 2 * ts->curPosY - ts->cubicCPY;
PathPoint curve[4] = { {ts->curPosX, ts->curPosY},
{x1, y1},
{mX2, mY2},
{mX, mY} };
float dist = CalcBezLength(curve, 4, SplitCubicBezier);
ts->cubicCPX = mX2;
ts->cubicCPY = mY2;
ts->quadCPX = ts->curPosX = mX;
ts->quadCPY = ts->curPosY = mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoCubicSmoothAbs methods:
@ -1769,7 +2038,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL; }
@ -1812,6 +2081,23 @@ nsSVGPathSegCurvetoCubicSmoothRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoCubicSmoothRel::GetLength(nsSVGPathSegTraversalState *ts)
{
float x1 = ts->curPosX - ts->cubicCPX;
float y1 = ts->curPosY - ts->cubicCPY;
PathPoint curve[4] = { {0, 0}, {x1, y1}, {mX2, mY2}, {mX, mY} };
float dist = CalcBezLength(curve, 4, SplitCubicBezier);
ts->cubicCPX = mX2 + ts->curPosX;
ts->cubicCPY = mY2 + ts->curPosY;
ts->quadCPX = ts->curPosX += mX;
ts->quadCPY = ts->curPosY += mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoCubicSmoothRel methods:
@ -1884,7 +2170,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; }
@ -1923,6 +2209,22 @@ nsSVGPathSegCurvetoQuadraticSmoothAbs::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoQuadraticSmoothAbs::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->quadCPX = 2 * ts->curPosX - ts->quadCPX;
ts->quadCPY = 2 * ts->curPosY - ts->quadCPY;
PathPoint bez[3] = { {ts->curPosX, ts->curPosY},
{ts->quadCPX, ts->quadCPY},
{mX, mY} };
float dist = CalcBezLength(bez, 3, SplitQuadraticBezier);
ts->cubicCPX = ts->curPosX = mX;
ts->cubicCPY = ts->curPosY = mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs methods:
@ -1969,7 +2271,7 @@ public:
// nsSVGPathSeg methods:
NS_IMETHOD GetValueString(nsAString& aValue);
virtual float GetLength(nsSVGPathSegTraversalState *ts);
virtual PRUint16 GetSegType()
{ return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; }
@ -2010,6 +2312,23 @@ nsSVGPathSegCurvetoQuadraticSmoothRel::GetValueString(nsAString& aValue)
return NS_OK;
}
float
nsSVGPathSegCurvetoQuadraticSmoothRel::GetLength(nsSVGPathSegTraversalState *ts)
{
ts->quadCPX = ts->curPosX - ts->quadCPX;
ts->quadCPY = ts->curPosY - ts->quadCPY;
PathPoint bez[3] = { {0, 0}, {ts->quadCPX, ts->quadCPY}, {mX, mY} };
float dist = CalcBezLength(bez, 3, SplitQuadraticBezier);
ts->quadCPX += ts->curPosX;
ts->quadCPY += ts->curPosY;
ts->cubicCPX = ts->curPosX += mX;
ts->cubicCPY = ts->curPosY += mY;
return dist;
}
//----------------------------------------------------------------------
// nsIDOMSVGPathSegCurvetoQuadraticSmoothRel methods:

Просмотреть файл

@ -41,10 +41,22 @@
#include "nsIDOMSVGPathSeg.h"
#include "nsIWeakReference.h"
#include "nsSVGUtils.h"
#include "nsSVGPathDataParser.h"
class nsSVGPathSegList;
class nsISVGValue;
struct nsSVGPathSegTraversalState {
float curPosX, startPosX, quadCPX, cubicCPX;
float curPosY, startPosY, quadCPY, cubicCPY;
nsSVGPathSegTraversalState()
{
curPosX = startPosX = quadCPX = cubicCPX = 0;
curPosY = startPosY = quadCPY = cubicCPY = 0;
}
};
class nsSVGPathSeg : public nsIDOMSVGPathSeg
{
public:
@ -58,6 +70,9 @@ public:
NS_DECL_NSIDOMSVGPATHSEG
NS_IMETHOD GetValueString(nsAString& aValue) = 0;
// nsSVGPathSeg methods:
virtual float GetLength(nsSVGPathSegTraversalState *ts) = 0;
protected:
virtual PRUint16 GetSegType() = 0;
void DidModify();