Mozilla/mozilla/layout/svg/base/src/nsSVGPathFrame.cpp
jwatt%jwatt.org 61030d9012 Fixing bug 288165. SVG path: marker-start/-end (auto orient): wrong direction. r=tor@acm.org, a=benjamin@smedbergs.us
git-svn-id: svn://10.0.0.236/trunk@175423 18797224-902f-48f8-a5cc-f745e15eee43
2005-06-30 21:47:41 +00:00

970 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
* Daniele Nicolodi <daniele@grinta.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <math.h>
#include "nsSVGPathGeometryFrame.h"
#include "nsIDOMSVGAnimatedPathData.h"
#include "nsIDOMSVGPathSegList.h"
#include "nsIDOMSVGPathSeg.h"
#include "nsIDOMSVGMatrix.h"
#include "nsISVGRendererPathBuilder.h"
#include "nsISVGMarkable.h"
#include "nsSVGMarkerFrame.h"
#include "nsISupports.h"
#include "nsLayoutAtoms.h"
class nsSVGPathFrame : public nsSVGPathGeometryFrame,
public nsISVGMarkable
{
protected:
friend nsresult
NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame);
~nsSVGPathFrame();
NS_IMETHOD InitSVG();
public:
// nsISVGValueObserver interface:
NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsISVGPathGeometrySource interface:
NS_IMETHOD ConstructPath(nsISVGRendererPathBuilder *pathBuilder);
// nsISVGMarkable interface
void GetMarkPoints(nsVoidArray *aMarks);
// nsISupports interface:
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
/**
* Get the "type" of the frame
*
* @see nsLayoutAtoms::svgPathFrame
*/
virtual nsIAtom* GetType() const;
#ifdef DEBUG
NS_IMETHOD GetFrameName(nsAString& aResult) const
{
return MakeFrameName(NS_LITERAL_STRING("SVGPath"), aResult);
}
#endif
private:
NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; }
NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; }
nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
};
NS_INTERFACE_MAP_BEGIN(nsSVGPathFrame)
NS_INTERFACE_MAP_ENTRY(nsISVGMarkable)
NS_INTERFACE_MAP_END_INHERITING(nsSVGPathGeometryFrame)
//----------------------------------------------------------------------
// Implementation
nsresult
NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame)
{
*aNewFrame = nsnull;
nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(aContent);
if (!anim_data) {
#ifdef DEBUG
printf("warning: trying to construct an SVGPathFrame for a content element that doesn't support the right interfaces\n");
#endif
return NS_ERROR_FAILURE;
}
nsSVGPathFrame* it = new (aPresShell) nsSVGPathFrame;
if (nsnull == it)
return NS_ERROR_OUT_OF_MEMORY;
*aNewFrame = it;
return NS_OK;
}
nsSVGPathFrame::~nsSVGPathFrame()
{
nsCOMPtr<nsISVGValue> value;
if (mSegments && (value = do_QueryInterface(mSegments)))
value->RemoveObserver(this);
}
NS_IMETHODIMP
nsSVGPathFrame::InitSVG()
{
nsresult rv = nsSVGPathGeometryFrame::InitSVG();
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(mContent);
NS_ASSERTION(anim_data,"wrong content element");
anim_data->GetAnimatedPathSegList(getter_AddRefs(mSegments));
NS_ASSERTION(mSegments, "no pathseglist");
if (!mSegments) return NS_ERROR_FAILURE;
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
if (value)
value->AddObserver(this);
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGPathFrame::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
nsCOMPtr<nsIDOMSVGPathSegList> l = do_QueryInterface(observable);
if (l && mSegments==l) {
UpdateGraphic(nsISVGPathGeometrySource::UPDATEMASK_PATH);
return NS_OK;
}
// else
return nsSVGPathGeometryFrame::DidModifySVGObservable(observable, aModType);
}
//----------------------------------------------------------------------
// nsISVGPathGeometrySource methods:
/* void constructPath (in nsISVGRendererPathBuilder pathBuilder); */
NS_IMETHODIMP nsSVGPathFrame::ConstructPath(nsISVGRendererPathBuilder* pathBuilder)
{
PRUint32 count;
mSegments->GetNumberOfItems(&count);
if (count == 0) return NS_OK;
float cx = 0.0f; // current point
float cy = 0.0f;
float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
float cy1 = 0.0f;
PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
PRUint32 i;
for (i = 0; i < count; ++i) {
nsCOMPtr<nsIDOMSVGPathSeg> segment;
mSegments->GetItem(i, getter_AddRefs(segment));
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
segment->GetPathSegType(&type);
PRBool absCoords = PR_FALSE;
switch (type) {
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
pathBuilder->ClosePath(&cx,&cy);
break;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
{
float x, y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
}
cx = x;
cy = y;
pathBuilder->Moveto(x,y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
{
float x, y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
}
cx = x;
cy = y;
pathBuilder->Lineto(x,y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
{
float x, y, x1, y1, x2, y2;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx = x;
cy = y;
cx1 = x2;
cy1 = y2;
pathBuilder->Curveto(x, y, x1, y1, x2, y2);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
{
float x, y, x1, y1, x31, y31, x32, y32;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
}
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = cx + (x1 - cx) * 2 / 3;
y31 = cy + (y1 - cy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
cx = x;
cy = y;
cx1 = x1;
cy1 = y1;
pathBuilder->Curveto(x, y, x31, y31, x32, y32);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
{
float x0, y0, x, y, r1, r2, angle;
PRBool largeArcFlag, sweepFlag;
x0 = cx;
y0 = cy;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
}
cx = x;
cy = y;
pathBuilder->Arcto(x, y, r1, r2, angle, largeArcFlag, sweepFlag);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
{
float x;
float y = cy;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
x += cx;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
}
cx = x;
pathBuilder->Lineto(x,y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
{
float x = cx;
float y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
}
cy = y;
pathBuilder->Lineto(x,y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
{
float x, y, x1, y1, x2, y2;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx = x;
cy = y;
cx1 = x2;
cy1 = y2;
pathBuilder->Curveto(x, y, x1, y1, x2, y2);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
{
float x, y, x1, y1, x31, y31, x32, y32;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
}
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = cx + (x1 - cx) * 2 / 3;
y31 = cy + (y1 - cy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
cx = x;
cy = y;
cx1 = x1;
cy1 = y1;
pathBuilder->Curveto(x, y, x31, y31, x32, y32);
}
break;
default:
NS_ASSERTION(1==0, "unknown path segment");
break;
}
lastSegmentType = type;
}
return NS_OK;
}
static float
calcAngle(float ux, float uy, float vx, float vy)
{
float ta = atan2(uy, ux);
float tb = atan2(vy, vx);
if (tb >= ta)
return tb-ta;
return 2*M_PI - (ta-tb);
}
//----------------------------------------------------------------------
// nsISVGMarkable methods:
void
nsSVGPathFrame::GetMarkPoints(nsVoidArray *aMarks) {
PRUint32 count;
mSegments->GetNumberOfItems(&count);
nsCOMPtr<nsIDOMSVGPathSeg> segment;
float cx = 0.0f; // current point
float cy = 0.0f;
float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
float cy1 = 0.0f;
PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
float px, py; // subpath initial point
float pathAngle, pathIndex;
float prevAngle = 0, startAngle, endAngle;
PRBool newSegment = PR_FALSE;
PRUint32 i;
for (i = 0; i < count; ++i) {
nsCOMPtr<nsIDOMSVGPathSeg> segment;
mSegments->GetItem(i, getter_AddRefs(segment));
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
segment->GetPathSegType(&type);
float x, y;
PRBool absCoords = PR_FALSE;
switch (type) {
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
{
x = px;
y = py;
startAngle = endAngle = atan2(y - cy, x - cx);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
{
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
}
px = x;
py = y;
startAngle = endAngle = prevAngle;
newSegment = PR_TRUE;
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
{
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
}
startAngle = endAngle = atan2(y - cy, x - cx);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
{
float x1, y1, x2, y2;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx1 = x2;
cy1 = y2;
if (x1 == cx && y1 == cy) {
x1 = x2;
y1 = y2;
}
if (x2 == x && y2 == y) {
x2 = x1;
y2 = y1;
}
startAngle = atan2(y1 - cy, x1 - cx);
endAngle = atan2(y - y2, x - x2);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
{
float x1, y1;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
}
cx1 = x1;
cy1 = y1;
startAngle = atan2(y1 - cy, x1 - cx);
endAngle = atan2(y - y1, x - x1);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
{
float r1, r2, angle;
PRBool largeArcFlag, sweepFlag;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
}
/* check for degenerate ellipse */
if (r1 == 0.0 || r2 == 0.0) {
startAngle = endAngle = atan2(y - cy, x - cx);
break;
}
r1 = fabs(r1); r2 = fabs(r2);
float xp, yp, cxp, cyp;
/* slope fun&games ... see SVG spec, section F.6 */
angle = angle*M_PI/180.0;
xp = cos(angle)*(cx-x)/2.0 + sin(angle)*(cy-y)/2.0;
yp = -sin(angle)*(cx-x)/2.0 + cos(angle)*(cy-y)/2.0;
/* make sure radii are large enough */
float root, numerator = r1*r1*r2*r2 - r1*r1*yp*yp - r2*r2*xp*xp;
if (numerator < 0.0) {
float s = sqrt(1.0 - numerator/(r1*r1*r2*r2));
r1 *= s;
r2 *= s;
root = 0.0;
} else {
root = sqrt(numerator/(r1*r1*yp*yp + r2*r2*xp*xp));
if (largeArcFlag == sweepFlag)
root = -root;
}
cxp = root*r1*yp/r2;
cyp = -root*r2*xp/r1;
float theta, delta;
theta = calcAngle(1.0, 0.0, (xp-cxp)/r1, (yp-cyp)/r2);
delta = calcAngle((xp-cxp)/r1, (yp-cyp)/r2, (-xp-cxp)/r1, (-yp-cyp)/r2);
if (!sweepFlag && delta > 0)
delta -= 2.0*M_PI;
else if (sweepFlag && delta < 0)
delta += 2.0*M_PI;
float tx1, ty1, tx2, ty2;
tx1 = -cos(angle)*r1*sin(theta) - sin(angle)*r2*cos(theta);
ty1 = -sin(angle)*r1*sin(theta) + cos(angle)*r2*cos(theta);
tx2 = -cos(angle)*r1*sin(theta+delta) - sin(angle)*r2*cos(theta+delta);
ty2 = -sin(angle)*r1*sin(theta+delta) + cos(angle)*r2*cos(theta+delta);
if (delta < 0.0f) {
tx1 = -tx1;
ty1 = -ty1;
tx2 = -tx2;
ty2 = -ty2;
}
startAngle = atan2(ty1, tx1);
endAngle = atan2(ty2, tx2);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
{
y = cy;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
x += cx;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
}
startAngle = endAngle = atan2(0, x - cx);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
{
x = cx;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
}
startAngle = endAngle = atan2(y - cy, 0);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
{
float x1, y1, x2, y2;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx1 = x2;
cy1 = y2;
if (x1 == cx && y1 == cy) {
x1 = x2;
y1 = y2;
}
if (x2 == x && y2 == y) {
x2 = x1;
y2 = y1;
}
startAngle = atan2(y1 - cy, x1 - cx);
endAngle = atan2(y - y2, x - x2);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
{
float x1, y1;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
}
cx1 = x1;
cy1 = y1;
startAngle = atan2(y1 - cy, x1 - cx);
endAngle = atan2(y - y1, x - x1);
}
break;
default:
NS_ASSERTION(1==0, "unknown path segment");
break;
}
lastSegmentType = type;
if (newSegment &&
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
pathIndex = aMarks->Count() - 1;
pathAngle = startAngle;
((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = pathAngle;
newSegment = PR_FALSE;
prevAngle = endAngle;
} else if (type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
if (aMarks->Count())
((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = prevAngle;
} else {
((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle =
nsSVGMarkerFrame::bisect(prevAngle, startAngle);
prevAngle = endAngle;
}
nsSVGMark *mark = new nsSVGMark;
mark->x = x;
mark->y = y;
aMarks->AppendElement(mark);
if (type == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
prevAngle = nsSVGMarkerFrame::bisect(endAngle, pathAngle);
((nsSVGMark *)aMarks->ElementAt(pathIndex))->angle = prevAngle;
}
cx = x;
cy = y;
}
if (aMarks->Count())
((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = prevAngle;
}
nsIAtom *
nsSVGPathFrame::GetType() const
{
return nsLayoutAtoms::svgPathFrame;
}