/* Copyright (C) 2001-2003 KSVG Team This file is part of the KDE project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <math.h> #include <cfloat> #include <kdebug.h> #include <klocale.h> #include "SVGRectImpl.h" #include "SVGPaintImpl.h" #include "SVGPointImpl.h" #include "SVGAngleImpl.h" #include "SVGDocumentImpl.h" #include "SVGSVGElementImpl.h" #include "SVGPathSegArcImpl.h" #include "SVGPathSegListImpl.h" #include "SVGPathElementImpl.h" #include "SVGPathSegLinetoImpl.h" #include "SVGPathSegMovetoImpl.h" #include "SVGAnimatedNumberImpl.h" #include "SVGPathSegClosePathImpl.h" #include "SVGPathSegCurvetoCubicImpl.h" #include "SVGPathSegLinetoVerticalImpl.h" #include "SVGPathSegLinetoHorizontalImpl.h" #include "SVGPathSegCurvetoQuadraticImpl.h" #include "SVGPathSegCurvetoCubicSmoothImpl.h" #include "SVGPathSegCurvetoQuadraticSmoothImpl.h" #include "SVGPaint.h" #include "CanvasItem.h" #include "KSVGCanvas.h" #include "BezierPath.h" #include "Point.h" using namespace KSVG; #include "SVGPathElementImpl.lut.h" #include "ksvg_scriptinterpreter.h" #include "ksvg_bridge.h" #include "ksvg_ecma.h" SVGPathElementImpl::SVGPathElementImpl(DOM::ElementImpl *impl) : SVGShapeImpl(impl), SVGTestsImpl(), SVGLangSpaceImpl(), SVGExternalResourcesRequiredImpl(), SVGStylableImpl(this), SVGTransformableImpl(), SVGAnimatedPathDataImpl(), SVGPathParser() { KSVG_EMPTY_FLAGS m_pathLength = new SVGAnimatedNumberImpl(); m_pathLength->ref(); m_pathLength->setBaseVal(0); } SVGPathElementImpl::~SVGPathElementImpl() { pathSegList()->clear(); if(m_pathLength) m_pathLength->deref(); } SVGAnimatedNumberImpl *SVGPathElementImpl::pathLength() const { return m_pathLength; } double SVGPathElementImpl::getTotalLength() { T2P::BezierPath *path = ownerDoc()->canvas()->toBezierPath(m_item); if(path) return path->length(); return 0; } SVGPointImpl *SVGPathElementImpl::getPointAtLength(double distance) { SVGPointImpl *ret = SVGSVGElementImpl::createSVGPoint(); double totalDistance = getTotalLength(); T2P::BezierPath *path = ownerDoc()->canvas()->toBezierPath(m_item); if(path) { T2P::Point p; path->pointTangentNormalAt(distance / totalDistance, &p); ret->setX(p.x()); ret->setY(p.y()); } return ret; } unsigned long SVGPathElementImpl::getPathSegAtLength(double) { return 0; } SVGPathSegClosePathImpl *SVGPathElementImpl::createSVGPathSegClosePath() { SVGPathSegClosePathImpl *temp = new SVGPathSegClosePathImpl(); temp->ref(); return temp; } SVGPathSegMovetoAbsImpl *SVGPathElementImpl::createSVGPathSegMovetoAbs(double x, double y) { SVGPathSegMovetoAbsImpl *temp = new SVGPathSegMovetoAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } SVGPathSegMovetoRelImpl *SVGPathElementImpl::createSVGPathSegMovetoRel(double x, double y) { SVGPathSegMovetoRelImpl *temp = new SVGPathSegMovetoRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } SVGPathSegLinetoAbsImpl *SVGPathElementImpl::createSVGPathSegLinetoAbs(double x, double y) { SVGPathSegLinetoAbsImpl *temp = new SVGPathSegLinetoAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } SVGPathSegLinetoRelImpl *SVGPathElementImpl::createSVGPathSegLinetoRel(double x, double y) { SVGPathSegLinetoRelImpl *temp = new SVGPathSegLinetoRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } SVGPathSegCurvetoCubicAbsImpl *SVGPathElementImpl::createSVGPathSegCurvetoCubicAbs(double x, double y, double x1, double y1, double x2, double y2) { SVGPathSegCurvetoCubicAbsImpl *temp = new SVGPathSegCurvetoCubicAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX1(x1); temp->setY1(y1); temp->setX2(x2); temp->setY2(y2); return temp; } SVGPathSegCurvetoCubicRelImpl *SVGPathElementImpl::createSVGPathSegCurvetoCubicRel(double x, double y, double x1, double y1, double x2, double y2) { SVGPathSegCurvetoCubicRelImpl *temp = new SVGPathSegCurvetoCubicRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX1(x1); temp->setY1(y1); temp->setX2(x2); temp->setY2(y2); return temp; } SVGPathSegCurvetoQuadraticAbsImpl *SVGPathElementImpl::createSVGPathSegCurvetoQuadraticAbs(double x, double y, double x1, double y1) { SVGPathSegCurvetoQuadraticAbsImpl *temp = new SVGPathSegCurvetoQuadraticAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX1(x1); temp->setY1(y1); return temp; } SVGPathSegCurvetoQuadraticRelImpl *SVGPathElementImpl::createSVGPathSegCurvetoQuadraticRel(double x, double y, double x1, double y1) { SVGPathSegCurvetoQuadraticRelImpl *temp = new SVGPathSegCurvetoQuadraticRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX1(x1); temp->setY1(y1); return temp; } SVGPathSegArcAbsImpl *SVGPathElementImpl::createSVGPathSegArcAbs(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag) { SVGPathSegArcAbsImpl *temp = new SVGPathSegArcAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setR1(r1); temp->setR2(r2); temp->setAngle(angle); temp->setLargeArcFlag(largeArcFlag); temp->setSweepFlag(sweepFlag); return temp; } SVGPathSegArcRelImpl *SVGPathElementImpl::createSVGPathSegArcRel(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag) { SVGPathSegArcRelImpl *temp = new SVGPathSegArcRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setR1(r1); temp->setR2(r2); temp->setAngle(angle); temp->setLargeArcFlag(largeArcFlag); temp->setSweepFlag(sweepFlag); return temp; } SVGPathSegLinetoHorizontalAbsImpl *SVGPathElementImpl::createSVGPathSegLinetoHorizontalAbs(double x) { SVGPathSegLinetoHorizontalAbsImpl *temp = new SVGPathSegLinetoHorizontalAbsImpl(); temp->ref(); temp->setX(x); return temp; } SVGPathSegLinetoHorizontalRelImpl *SVGPathElementImpl::createSVGPathSegLinetoHorizontalRel(double x) { SVGPathSegLinetoHorizontalRelImpl *temp = new SVGPathSegLinetoHorizontalRelImpl(); temp->ref(); temp->setX(x); return temp; } SVGPathSegLinetoVerticalAbsImpl *SVGPathElementImpl::createSVGPathSegLinetoVerticalAbs(double y) { SVGPathSegLinetoVerticalAbsImpl *temp = new SVGPathSegLinetoVerticalAbsImpl(); temp->ref(); temp->setY(y); return temp; } SVGPathSegLinetoVerticalRelImpl *SVGPathElementImpl::createSVGPathSegLinetoVerticalRel(double y) { SVGPathSegLinetoVerticalRelImpl *temp = new SVGPathSegLinetoVerticalRelImpl(); temp->ref(); temp->setY(y); return temp; } SVGPathSegCurvetoCubicSmoothAbsImpl *SVGPathElementImpl::createSVGPathSegCurvetoCubicSmoothAbs(double x, double y, double x2, double y2) { SVGPathSegCurvetoCubicSmoothAbsImpl *temp = new SVGPathSegCurvetoCubicSmoothAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX2(x2); temp->setY2(y2); return temp; } SVGPathSegCurvetoCubicSmoothRelImpl *SVGPathElementImpl::createSVGPathSegCurvetoCubicSmoothRel(double x, double y, double x2, double y2) { SVGPathSegCurvetoCubicSmoothRelImpl *temp = new SVGPathSegCurvetoCubicSmoothRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); temp->setX2(x2); temp->setY2(y2); return temp; } SVGPathSegCurvetoQuadraticSmoothAbsImpl *SVGPathElementImpl::createSVGPathSegCurvetoQuadraticSmoothAbs(double x, double y) { SVGPathSegCurvetoQuadraticSmoothAbsImpl *temp = new SVGPathSegCurvetoQuadraticSmoothAbsImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } SVGPathSegCurvetoQuadraticSmoothRelImpl *SVGPathElementImpl::createSVGPathSegCurvetoQuadraticSmoothRel(double x, double y) { SVGPathSegCurvetoQuadraticSmoothRelImpl *temp = new SVGPathSegCurvetoQuadraticSmoothRelImpl(); temp->ref(); temp->setX(x); temp->setY(y); return temp; } void SVGPathElementImpl::svgMoveTo(double x1, double y1, bool, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegMovetoAbs(x1, y1)); else pathSegList()->appendItem(createSVGPathSegMovetoRel(x1, y1)); } void SVGPathElementImpl::svgLineTo(double x1, double y1, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegLinetoAbs(x1, y1)); else pathSegList()->appendItem(createSVGPathSegLinetoRel(x1, y1)); } void SVGPathElementImpl::svgLineToHorizontal(double x, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegLinetoHorizontalAbs(x)); else pathSegList()->appendItem(createSVGPathSegLinetoHorizontalRel(x)); } void SVGPathElementImpl::svgLineToVertical(double y, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegLinetoVerticalAbs(y)); else pathSegList()->appendItem(createSVGPathSegLinetoVerticalRel(y)); } void SVGPathElementImpl::svgCurveToCubic(double x1, double y1, double x2, double y2, double x, double y, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegCurvetoCubicAbs(x, y, x1, y1, x2, y2)); else pathSegList()->appendItem(createSVGPathSegCurvetoCubicRel(x, y, x1, y1, x2, y2)); } void SVGPathElementImpl::svgCurveToCubicSmooth(double x, double y, double x2, double y2, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y)); else pathSegList()->appendItem(createSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y)); } void SVGPathElementImpl::svgCurveToQuadratic(double x, double y, double x1, double y1, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y)); else pathSegList()->appendItem(createSVGPathSegCurvetoQuadraticRel(x1, y1, x, y)); } void SVGPathElementImpl::svgCurveToQuadraticSmooth(double x, double y, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegCurvetoQuadraticSmoothAbs(x, y)); else pathSegList()->appendItem(createSVGPathSegCurvetoQuadraticSmoothRel(x, y)); } void SVGPathElementImpl::svgArcTo(double x, double y, double r1, double r2, double angle, bool largeArcFlag, bool sweepFlag, bool abs) { if(abs) pathSegList()->appendItem(createSVGPathSegArcAbs(x, y, r1, r2, angle, largeArcFlag, sweepFlag)); else pathSegList()->appendItem(createSVGPathSegArcRel(x, y, r1, r2, angle, largeArcFlag, sweepFlag)); } void SVGPathElementImpl::svgClosePath() { pathSegList()->appendItem(createSVGPathSegClosePath()); } // Ecma stuff /* @namespace KSVG @begin SVGPathElementImpl::s_hashTable 3 d SVGPathElementImpl::D DontDelete|ReadOnly pathLength SVGPathElementImpl::PathLength DontDelete|ReadOnly @end @namespace KSVG @begin SVGPathElementImplProto::s_hashTable 23 getTotalLength SVGPathElementImpl::GetTotalLength DontDelete|Function 0 getPointAtLength SVGPathElementImpl::GetPointAtLength DontDelete|Function 1 getPathSegAtLength SVGPathElementImpl::GetPathSegAtLength DontDelete|Function 1 createSVGPathSegClosePath SVGPathElementImpl::CreateSVGPathSegClosePath DontDelete|Function 0 createSVGPathSegMovetoAbs SVGPathElementImpl::CreateSVGPathSegMovetoAbs DontDelete|Function 2 createSVGPathSegMovetoRel SVGPathElementImpl::CreateSVGPathSegMovetoRel DontDelete|Function 2 createSVGPathSegLinetoAbs SVGPathElementImpl::CreateSVGPathSegLinetoAbs DontDelete|Function 2 createSVGPathSegLinetoRel SVGPathElementImpl::CreateSVGPathSegLinetoRel DontDelete|Function 2 createSVGPathSegArcAbs SVGPathElementImpl::CreateSVGPathSegArcAbs DontDelete|Function 7 createSVGPathSegArcRel SVGPathElementImpl::CreateSVGPathSegArcRel DontDelete|Function 7 createSVGPathSegCurvetoCubicAbs SVGPathElementImpl::CreateSVGPathSegCurvetoCubicAbs DontDelete|Function 6 createSVGPathSegCurvetoCubicRel SVGPathElementImpl::CreateSVGPathSegCurvetoCubicRel DontDelete|Function 6 createSVGPathSegCurvetoQuadraticAbs SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticAbs DontDelete|Function 4 createSVGPathSegCurvetoQuadraticRel SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticRel DontDelete|Function 4 createSVGPathSegLinetoHorizontalAbs SVGPathElementImpl::CreateSVGPathSegLinetoHorizontalAbs DontDelete|Function 1 createSVGPathSegLinetoHorizontalRel SVGPathElementImpl::CreateSVGPathSegLinetoHorizontalRel DontDelete|Function 1 createSVGPathSegLinetoVerticalAbs SVGPathElementImpl::CreateSVGPathSegLinetoVerticalAbs DontDelete|Function 1 createSVGPathSegLinetoVerticalRel SVGPathElementImpl::CreateSVGPathSegLinetoVerticalRel DontDelete|Function 1 createSVGPathSegCurvetoCubicAbs SVGPathElementImpl::CreateSVGPathSegCurvetoCubicAbs DontDelete|Function 4 createSVGPathSegCurvetoCubicRel SVGPathElementImpl::CreateSVGPathSegCurvetoCubicRel DontDelete|Function 4 createSVGPathSegCurvetoQuadraticAbs SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticAbs DontDelete|Function 2 createSVGPathSegCurvetoQuadraticRel SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticRel DontDelete|Function 2 @end */ KSVG_IMPLEMENT_PROTOTYPE("SVGPathElementImpl", SVGPathElementImplProto, SVGPathElementImplProtoFunc) Value SVGPathElementImpl::getValueProperty(ExecState *exec, int token) const { //KSVG_CHECK_ATTRIBUTE switch(token) { case PathLength: return m_pathLength->cache(exec); case D: // if(!attributeMode) { TQString d; unsigned int nrSegs = pathSegList()->numberOfItems(); SVGPathSegImpl *curseg = 0; for(unsigned int i = 0; i < nrSegs; i++) { curseg = pathSegList()->getItem(i); if(curseg) d += curseg->toString() + " "; } return String(d); } default: kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl; return Undefined(); } } void SVGPathElementImpl::putValueProperty(ExecState *exec, int token, const Value &value, int attr) { // This class has just ReadOnly properties, only with the Internal flag set // it's allowed to modify those. if(!(attr & KJS::Internal)) return; switch(token) { case D: { pathSegList()->clear(); TQString d = value.toString(exec).qstring(); parseSVG(d, false); if(hasMarkers()) m_markerData = MarkerData(pathSegList()); break; } default: kdWarning() << "Unhandled token in " << k_funcinfo << " : " << token << endl; } } Value SVGPathElementImplProtoFunc::call(ExecState *exec, Object &thisObj, const List &args) { KSVG_CHECK_THIS(SVGPathElementImpl) switch(id) { case SVGPathElementImpl::GetTotalLength: return Number(obj->getTotalLength()); case SVGPathElementImpl::GetPointAtLength: return obj->getPointAtLength(args[0].toNumber(exec))->cache(exec); case SVGPathElementImpl::GetPathSegAtLength: return Number(obj->getPathSegAtLength(args[0].toNumber(exec))); case SVGPathElementImpl::CreateSVGPathSegClosePath: return obj->createSVGPathSegClosePath()->cache(exec); case SVGPathElementImpl::CreateSVGPathSegMovetoAbs: return obj->createSVGPathSegMovetoAbs(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegMovetoRel: return obj->createSVGPathSegMovetoRel(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoAbs: return obj->createSVGPathSegLinetoAbs(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoRel: return obj->createSVGPathSegLinetoRel(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoCubicAbs: return obj->createSVGPathSegCurvetoCubicAbs(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec), args[4].toNumber(exec), args[5].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoCubicRel: return obj->createSVGPathSegCurvetoCubicRel(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec), args[4].toNumber(exec), args[5].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticAbs: return obj->createSVGPathSegCurvetoQuadraticAbs(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticRel: return obj->createSVGPathSegCurvetoQuadraticRel(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegArcAbs: return obj->createSVGPathSegArcAbs(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec), args[4].toNumber(exec), args[5].toBoolean(exec), args[6].toBoolean(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegArcRel: return obj->createSVGPathSegArcRel(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec), args[4].toNumber(exec), args[5].toBoolean(exec), args[6].toBoolean(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoHorizontalAbs: return obj->createSVGPathSegLinetoHorizontalAbs(args[0].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoHorizontalRel: return obj->createSVGPathSegLinetoHorizontalRel(args[0].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoVerticalAbs: return obj->createSVGPathSegLinetoVerticalAbs(args[0].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegLinetoVerticalRel: return obj->createSVGPathSegLinetoVerticalRel(args[0].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoCubicSmoothAbs: return obj->createSVGPathSegCurvetoCubicSmoothAbs(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoCubicSmoothRel: return obj->createSVGPathSegCurvetoCubicSmoothRel(args[0].toNumber(exec), args[1].toNumber(exec), args[2].toNumber(exec), args[3].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticSmoothAbs: return obj->createSVGPathSegCurvetoQuadraticSmoothAbs(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); case SVGPathElementImpl::CreateSVGPathSegCurvetoQuadraticSmoothRel: return obj->createSVGPathSegCurvetoQuadraticSmoothRel(args[0].toNumber(exec), args[1].toNumber(exec))->cache(exec); default: kdWarning() << "Unhandled function id in " << k_funcinfo << " : " << id << endl; break; } return Undefined(); } SVGRectImpl *SVGPathElementImpl::getBBox() { SVGRectImpl *ret = SVGSVGElementImpl::createSVGRect(); if(m_item) { T2P::BezierPath *path = ownerDoc()->canvas()->toBezierPath(m_item); if(path) { T2P::Point topLeft; T2P::Point bottomRight; path->boundingBox(&topLeft, &bottomRight); ret->setX(topLeft.x()); ret->setY(topLeft.y()); ret->setWidth(bottomRight.x() - topLeft.x()); ret->setHeight(bottomRight.y() - topLeft.y()); } } return ret; } void SVGPathElementImpl::createItem(KSVGCanvas *c) { if(!c) c = ownerDoc()->canvas(); if(!m_item) { // TODO : this is a quick fix for this problem: // d attribute encountered before marker attributes. // Try to process the attributes in the right order, ie. // d attr processing should be last. if(hasMarkers() && m_markerData.numMarkers() == 0) m_markerData = MarkerData(pathSegList()); m_item = c->createPath(this); c->insert(m_item); } } SVGPathElementImpl::MarkerData::MarkerData(SVGPathSegListImpl *path) { unsigned int numSegments = path->numberOfItems(); double curx = 0; double cury = 0; int currentSubpathStartIndex = -1; double previousQuadraticX1 = 0; double previousQuadraticY1 = 0; double previousCubicX2 = 0; double previousCubicY2 = 0; TQValueVector<SegmentData> pathSegmentData(numSegments); for(unsigned int i = 0; i < numSegments; i++) { SVGPathSegImpl *segment = path->getItem(i); struct SegmentData data; data.type = segment->pathSegType(); if(segment->pathSegType() == PATHSEG_MOVETO_ABS || segment->pathSegType() == PATHSEG_MOVETO_REL) { if(currentSubpathStartIndex >= 0) { // Finish the previous subpath. for(unsigned int j = currentSubpathStartIndex; j < i; j++) { pathSegmentData[j].subpathStartIndex = currentSubpathStartIndex; pathSegmentData[j].subpathEndIndex = i - 1; pathSegmentData[j].subpathIsClosed = false; } } currentSubpathStartIndex = i; } else if(segment->pathSegType() == PATHSEG_CLOSEPATH) { if(currentSubpathStartIndex >= 0) { SVGPathSegClosePathImpl *s = static_cast<SVGPathSegClosePathImpl *>(segment); s->setX(pathSegmentData[currentSubpathStartIndex].startx + pathSegmentData[currentSubpathStartIndex].dx); s->setY(pathSegmentData[currentSubpathStartIndex].starty + pathSegmentData[currentSubpathStartIndex].dy); for(unsigned int j = currentSubpathStartIndex; j < i; j++) { pathSegmentData[j].subpathStartIndex = currentSubpathStartIndex; pathSegmentData[j].subpathEndIndex = i; pathSegmentData[j].subpathIsClosed = true; } data.subpathStartIndex = currentSubpathStartIndex; data.subpathEndIndex = i; data.subpathIsClosed = true; } currentSubpathStartIndex = i + 1; } switch(segment->pathSegType()) { case PATHSEG_CURVETO_CUBIC_ABS: { SVGPathSegCurvetoCubicAbsImpl *s = static_cast<SVGPathSegCurvetoCubicAbsImpl *>(segment); previousCubicX2 = s->x2(); previousCubicY2 = s->y2(); break; } case PATHSEG_CURVETO_CUBIC_REL: { SVGPathSegCurvetoCubicRelImpl *s = static_cast<SVGPathSegCurvetoCubicRelImpl *>(segment); previousCubicX2 = curx + s->x2(); previousCubicY2 = cury + s->y2(); break; } case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: { SVGPathSegCurvetoCubicSmoothAbsImpl *s = static_cast<SVGPathSegCurvetoCubicSmoothAbsImpl *>(segment); s->setPreviousX2(previousCubicX2); s->setPreviousY2(previousCubicY2); previousCubicX2 = s->x2(); previousCubicY2 = s->y2(); break; } case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: { SVGPathSegCurvetoCubicSmoothRelImpl *s = static_cast<SVGPathSegCurvetoCubicSmoothRelImpl *>(segment); s->setPreviousAbsX2(previousCubicX2); s->setPreviousAbsY2(previousCubicY2); previousCubicX2 = curx + s->x2(); previousCubicY2 = cury + s->y2(); break; } case PATHSEG_CURVETO_TQUADRATIC_ABS: { SVGPathSegCurvetoQuadraticAbsImpl *s = static_cast<SVGPathSegCurvetoQuadraticAbsImpl *>(segment); previousQuadraticX1 = s->x1(); previousQuadraticY1 = s->y1(); break; } case PATHSEG_CURVETO_TQUADRATIC_REL: { SVGPathSegCurvetoQuadraticRelImpl *s = static_cast<SVGPathSegCurvetoQuadraticRelImpl *>(segment); previousQuadraticX1 = curx + s->x1(); previousQuadraticY1 = cury + s->y1(); break; } case PATHSEG_CURVETO_TQUADRATIC_SMOOTH_ABS: { SVGPathSegCurvetoQuadraticSmoothAbsImpl *s = static_cast<SVGPathSegCurvetoQuadraticSmoothAbsImpl *>(segment); s->setPreviousX1(previousQuadraticX1); s->setPreviousY1(previousQuadraticY1); previousQuadraticX1 = s->x1(curx); previousQuadraticY1 = s->y1(cury); break; } case PATHSEG_CURVETO_TQUADRATIC_SMOOTH_REL: { SVGPathSegCurvetoQuadraticSmoothRelImpl *s = static_cast<SVGPathSegCurvetoQuadraticSmoothRelImpl *>(segment); s->setPreviousAbsX1(previousQuadraticX1); s->setPreviousAbsY1(previousQuadraticY1); previousQuadraticX1 = s->absX1(curx); previousQuadraticY1 = s->absY1(cury); break; } default: previousCubicX2 = curx; previousCubicY2 = cury; previousQuadraticX1 = curx; previousQuadraticY1 = cury; break; } data.startx = curx; data.starty = cury; segment->getDeltasAndSlopes(curx, cury, &data.dx, &data.dy, &data.startSlope, &data.endSlope); pathSegmentData[i] = data; curx += data.dx; cury += data.dy; } if(currentSubpathStartIndex >= 0) { // Finish the previous subpath. for(unsigned int j = currentSubpathStartIndex; j < numSegments; j++) { pathSegmentData[j].subpathStartIndex = currentSubpathStartIndex; pathSegmentData[j].subpathEndIndex = numSegments - 1; pathSegmentData[j].subpathIsClosed = false; } } m_markers.resize(numSegments); for(unsigned int i = 0; i < numSegments; i++) { struct Marker marker; marker.x = pathSegmentData[i].startx + pathSegmentData[i].dx; marker.y = pathSegmentData[i].starty + pathSegmentData[i].dy; double inSlope; double outSlope; bool haveInSlope = false; bool haveOutSlope = false; if(pathSegmentData[i].subpathStartIndex == i && pathSegmentData[i].subpathIsClosed) { // Spec: For closed subpaths, the marker for the initial vertex uses the end direction // of the corresponding closepath for its incoming slope and the first segment's // start slope for its outgoing slope. haveInSlope = getEndSlope(pathSegmentData, pathSegmentData[i].subpathEndIndex, &inSlope); haveOutSlope = getStartSlope(pathSegmentData, i + 1, &outSlope); } else if(pathSegmentData[i].type == PATHSEG_CLOSEPATH) { haveInSlope = getEndSlope(pathSegmentData, i, &inSlope); // Spec: If the segment following a closepath is other than a moveto, the marker // for the closepath uses the following segment's start direction as its // outgoing direction. if(i + 1 < numSegments && (pathSegmentData[i + 1].type != PATHSEG_MOVETO_ABS && pathSegmentData[i + 1].type != PATHSEG_MOVETO_REL)) haveOutSlope = getStartSlope(pathSegmentData, i + 1, &outSlope); else haveOutSlope = getStartSlope(pathSegmentData, pathSegmentData[i].subpathStartIndex, &outSlope); } else { haveOutSlope = getStartSlope(pathSegmentData, i + 1, &outSlope); haveInSlope = getEndSlope(pathSegmentData, i, &inSlope); } if(!haveInSlope && !haveOutSlope) { outSlope = 0; inSlope = 0; } else if(haveInSlope && !haveOutSlope) outSlope = inSlope; else if(!haveInSlope && haveOutSlope) inSlope = outSlope; double bisector = SVGAngleImpl::shortestArcBisector(inSlope, outSlope); marker.angle = bisector; m_markers[i] = marker; } } bool SVGPathElementImpl::MarkerData::getStartSlope(TQValueVector<SegmentData> segments, unsigned int i, double *pStartSlope) { if(i > segments.count() - 1 || segments[i].type == PATHSEG_MOVETO_ABS || segments[i].type == PATHSEG_MOVETO_REL) return false; else { const double epsilon = DBL_EPSILON; if(fabs(segments[i].dx) > epsilon || fabs(segments[i].dy) > epsilon) { *pStartSlope = segments[i].startSlope; return true; } else { int subpathStartIndex = segments[i].subpathStartIndex; for(int j = i - 1; j >= subpathStartIndex; j--) { if(segments[j].type == PATHSEG_MOVETO_ABS || segments[j].type == PATHSEG_MOVETO_REL) return false; if(fabs(segments[j].dx) > epsilon || fabs(segments[j].dy) > epsilon) { *pStartSlope = segments[j].endSlope; return true; } } return false; } } } bool SVGPathElementImpl::MarkerData::getEndSlope(TQValueVector<SegmentData> segments, unsigned int i, double *pEndSlope) { if(i > segments.count() - 1 || segments[i].type == PATHSEG_MOVETO_ABS || segments[i].type == PATHSEG_MOVETO_REL) return false; else { const double epsilon = DBL_EPSILON; if(fabs(segments[i].dx) > epsilon || fabs(segments[i].dy) > epsilon) { *pEndSlope = segments[i].endSlope; return true; } else { int subpathEndIndex = segments[i].subpathEndIndex; for(int j = i + 1; j <= subpathEndIndex; j++) { if(segments[j].type == PATHSEG_MOVETO_ABS || segments[j].type == PATHSEG_MOVETO_REL) return false; if(fabs(segments[j].dx) > epsilon || fabs(segments[j].dy) > epsilon) { *pEndSlope = segments[j].startSlope; return true; } } return false; } } } // vim:ts=4:noet