File indexing completed on 2024-05-12 05:46:35

0001 /* This file is part of the KDE project
0002    Copyright (C) 2006-2008, 2010-2011 Thorsten Zachmann <zachmann@kde.org>
0003    Copyright (C) 2006-2011 Jan Hambrecht <jaham@gmx.net>
0004    Copyright (C) 2007-2009 Thomas Zander <zander@kde.org>
0005    Copyright (C) 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KoPathShape.h"
0024 #include "KoPathShape_p.h"
0025 
0026 #include "KoPathSegment.h"
0027 #include "KoPathPoint.h"
0028 #include "KoShapeStrokeModel.h"
0029 #include "KoPathShapeLoader.h"
0030 #include "KoShapeSavingContext.h"
0031 #include "KoShapeLoadingContext.h"
0032 #include "KoShapeShadow.h"
0033 #include "KoShapeBackground.h"
0034 #include "KoShapeContainer.h"
0035 #include "KoFilterEffectStack.h"
0036 #include "KoMarker.h"
0037 #include "KoShapeStroke.h"
0038 #include "KoInsets.h"
0039 
0040 #include <KoXmlReader.h>
0041 #include <KoXmlWriter.h>
0042 #include <KoXmlNS.h>
0043 #include <KoUnit.h>
0044 #include "KisQPainterStateSaver.h"
0045 
0046 #include <FlakeDebug.h>
0047 #include <QPainter>
0048 #include <QPainterPath>
0049 
0050 #include "kis_global.h"
0051 
0052 #include <qnumeric.h> // for qIsNaN
0053 static bool qIsNaNPoint(const QPointF &p) {
0054     return qIsNaN(p.x()) || qIsNaN(p.y());
0055 }
0056 
0057 KoPathShape::Private::Private()
0058     : fillRule(Qt::OddEvenFill)
0059     , autoFillMarkers(false)
0060 {
0061 }
0062 
0063 KoPathShape::Private::Private(const Private &rhs)
0064     : fillRule(rhs.fillRule)
0065     , markersNew(rhs.markersNew)
0066     , autoFillMarkers(rhs.autoFillMarkers)
0067 {
0068 }
0069 
0070 QRectF KoPathShape::Private::handleRect(const QPointF &p, qreal radius) const
0071 {
0072     return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
0073 }
0074 
0075 
0076 KoPathShape::KoPathShape()
0077     : KoTosContainer()
0078     , d(new Private)
0079 {
0080 }
0081 
0082 KoPathShape::KoPathShape(const KoPathShape &rhs)
0083     : KoTosContainer(rhs)
0084     , d(new Private(*rhs.d))
0085 {
0086     // local data cannot be shared via QSharedData because
0087     // every path point holds a pointer to the parent shape
0088     KoSubpathList subpaths;
0089     Q_FOREACH (KoSubpath *subPath, rhs.d->subpaths) {
0090         KoSubpath *clonedSubPath = new KoSubpath();
0091 
0092         Q_FOREACH (KoPathPoint *point, *subPath) {
0093             *clonedSubPath << new KoPathPoint(*point, this);
0094         }
0095 
0096         subpaths << clonedSubPath;
0097     }
0098     d->subpaths = subpaths;
0099 }
0100 
0101 KoPathShape::~KoPathShape()
0102 {
0103     clear();
0104 }
0105 
0106 KoShape *KoPathShape::cloneShape() const
0107 {
0108     return new KoPathShape(*this);
0109 }
0110 
0111 void KoPathShape::clear()
0112 {
0113     Q_FOREACH (KoSubpath *subpath, d->subpaths) {
0114         Q_FOREACH (KoPathPoint *point, *subpath)
0115             delete point;
0116         delete subpath;
0117     }
0118     d->subpaths.clear();
0119 
0120     notifyPointsChanged();
0121 }
0122 
0123 void KoPathShape::paint(QPainter &painter, KoShapePaintingContext &paintContext) const
0124 {
0125     KisQPainterStateSaver saver(&painter);
0126 
0127     QPainterPath path(outline());
0128     path.setFillRule(d->fillRule);
0129 
0130     if (background()) {
0131         background()->paint(painter, paintContext, path);
0132     }
0133     //d->paintDebug(painter);
0134 }
0135 
0136 
0137 #ifndef NDEBUG
0138 void KoPathShape::Private::paintDebug(QPainter &painter)
0139 {
0140     KoSubpathList::const_iterator pathIt(subpaths.constBegin());
0141     int i = 0;
0142 
0143     QPen pen(Qt::black, 0);
0144     painter.save();
0145     painter.setPen(pen);
0146     for (; pathIt != subpaths.constEnd(); ++pathIt) {
0147         KoSubpath::const_iterator it((*pathIt)->constBegin());
0148         for (; it != (*pathIt)->constEnd(); ++it) {
0149             ++i;
0150             KoPathPoint *point = (*it);
0151             QRectF r(point->point(), QSizeF(5, 5));
0152             r.translate(-2.5, -2.5);
0153             QPen pen(Qt::black, 0);
0154             painter.setPen(pen);
0155             if (point->activeControlPoint1() && point->activeControlPoint2()) {
0156                 QBrush b(Qt::red);
0157                 painter.setBrush(b);
0158             } else if (point->activeControlPoint1()) {
0159                 QBrush b(Qt::yellow);
0160                 painter.setBrush(b);
0161             } else if (point->activeControlPoint2()) {
0162                 QBrush b(Qt::darkYellow);
0163                 painter.setBrush(b);
0164             }
0165             painter.drawEllipse(r);
0166         }
0167     }
0168     painter.restore();
0169     debugFlake << "nop =" << i;
0170 }
0171 
0172 void KoPathShape::Private::debugPath() const
0173 {
0174     KoSubpathList::const_iterator pathIt(subpaths.constBegin());
0175     for (; pathIt != subpaths.constEnd(); ++pathIt) {
0176         KoSubpath::const_iterator it((*pathIt)->constBegin());
0177         for (; it != (*pathIt)->constEnd(); ++it) {
0178             debugFlake << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties();
0179         }
0180     }
0181 }
0182 #endif
0183 
0184 void KoPathShape::paintPoints(KisHandlePainterHelper &handlesHelper)
0185 {
0186     KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
0187 
0188     for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
0189         KoSubpath::const_iterator it((*pathIt)->constBegin());
0190         for (; it != (*pathIt)->constEnd(); ++it)
0191             (*it)->paint(handlesHelper, KoPathPoint::Node);
0192     }
0193 }
0194 
0195 QRectF KoPathShape::outlineRect() const
0196 {
0197     return outline().boundingRect();
0198 }
0199 
0200 QPainterPath KoPathShape::outline() const
0201 {
0202     QPainterPath path;
0203     for (auto subpathIt = d->subpaths.constBegin(); subpathIt != d->subpaths.constEnd(); ++subpathIt) {
0204         const KoSubpath * subpath = *subpathIt;
0205         const KoPathPoint * lastPoint = subpath->constFirst();
0206         bool activeCP = false;
0207         for (auto pointIt = subpath->constBegin(); pointIt != subpath->constEnd(); ++pointIt) {
0208             const KoPathPoint * currPoint = *pointIt;
0209             KoPathPoint::PointProperties currProperties = currPoint->properties();
0210             if (currPoint == subpath->constFirst()) {
0211                 if (currProperties & KoPathPoint::StartSubpath) {
0212                     Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0213                     path.moveTo(currPoint->point());
0214                 }
0215             } else if (activeCP && currPoint->activeControlPoint1()) {
0216                 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
0217                 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0218                 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0219                 path.cubicTo(
0220                             lastPoint->controlPoint2(),
0221                             currPoint->controlPoint1(),
0222                             currPoint->point());
0223             } else if (activeCP || currPoint->activeControlPoint1()) {
0224                 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
0225                 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0226                 path.quadTo(
0227                             activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
0228                             currPoint->point());
0229             } else {
0230                 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0231                 path.lineTo(currPoint->point());
0232             }
0233             if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
0234                 // add curve when there is a curve on the way to the first point
0235                 KoPathPoint * firstPoint = subpath->first();
0236                 Q_ASSERT(!qIsNaNPoint(firstPoint->point()));
0237                 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
0238                     path.cubicTo(
0239                                 currPoint->controlPoint2(),
0240                                 firstPoint->controlPoint1(),
0241                                 firstPoint->point());
0242                 }
0243                 else if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
0244                     Q_ASSERT(!qIsNaNPoint(currPoint->point()));
0245                     Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
0246                     path.quadTo(
0247                                 currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
0248                                 firstPoint->point());
0249                 }
0250                 path.closeSubpath();
0251             }
0252 
0253             if (currPoint->activeControlPoint2()) {
0254                 activeCP = true;
0255             } else {
0256                 activeCP = false;
0257             }
0258             lastPoint = currPoint;
0259         }
0260     }
0261 
0262     return path;
0263 }
0264 
0265 QRectF KoPathShape::boundingRect() const
0266 {
0267     const QTransform transform = absoluteTransformation();
0268 
0269     /**
0270      * First we approximate the insets of the stroke by rendering a fat bezier curve
0271      * with width set to the maximum inset of miters and markers. The are swept by this
0272      * curve will be a good approximation of the real curve bounding rect.
0273      */
0274     qreal outlineSweepWidth = 0;
0275 
0276     const QSharedPointer<KoShapeStroke> lineBorder = qSharedPointerDynamicCast<KoShapeStroke>(stroke());
0277     if (lineBorder) {
0278         outlineSweepWidth = lineBorder->lineWidth();
0279     }
0280 
0281     if (stroke()) {
0282         KoInsets inset;
0283         stroke()->strokeInsets(this, inset);
0284         const qreal maxInset = std::max({inset.left, inset.top, inset.right, inset.bottom});
0285 
0286         // insets extend outside the shape, but width extends both inside and outside,
0287         // so we should multiply insets by 2.0
0288         outlineSweepWidth = std::max({outlineSweepWidth,
0289                                       2.0 * maxInset,
0290                                       2.0 * stroke()->strokeMaxMarkersInset(this)});
0291     }
0292 
0293 
0294 
0295     /// NOTE: stroking the entire shape might be too expensive, so try to
0296     ///       estimate the bounds using insets only...
0297 
0298 #if 0
0299     QPen pen(Qt::black, outlineSweepWidth);
0300 
0301     // select round joins and caps to ensure it sweeps exactly
0302     // 'outlineSweepWidth' pixels in every possible
0303     pen.setJoinStyle(Qt::RoundJoin);
0304     pen.setCapStyle(Qt::RoundCap);
0305     QRectF bb = transform.map(pathStroke(pen)).boundingRect();
0306 #endif
0307 
0308     // add 10% extra update area around the doubled insets
0309     QRectF bb = transform.mapRect(kisGrowRect(outline().boundingRect(), 1.1 * 0.5 * outlineSweepWidth));
0310 
0311     if (shadow()) {
0312         KoInsets insets;
0313         shadow()->insets(insets);
0314         bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
0315     }
0316     if (filterEffectStack()) {
0317         QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size()));
0318         bb |= transform.mapRect(clipRect);
0319     }
0320     return bb;
0321 }
0322 
0323 QSizeF KoPathShape::size() const
0324 {
0325     // don't call boundingRect here as it uses absoluteTransformation
0326     // which itself uses size() -> leads to infinite recursion
0327     return outlineRect().size();
0328 }
0329 
0330 void KoPathShape::setSize(const QSizeF &newSize)
0331 {
0332     QTransform matrix(resizeMatrix(newSize));
0333 
0334     KoShape::setSize(newSize);
0335     d->map(matrix);
0336 }
0337 
0338 QTransform KoPathShape::resizeMatrix(const QSizeF & newSize) const
0339 {
0340     QSizeF oldSize = size();
0341     if (oldSize.width() == 0.0) {
0342         oldSize.setWidth(0.000001);
0343     }
0344     if (oldSize.height() == 0.0) {
0345         oldSize.setHeight(0.000001);
0346     }
0347 
0348     QSizeF sizeNew(newSize);
0349     if (sizeNew.width() == 0.0) {
0350         sizeNew.setWidth(0.000001);
0351     }
0352     if (sizeNew.height() == 0.0) {
0353         sizeNew.setHeight(0.000001);
0354     }
0355 
0356     return QTransform(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
0357 }
0358 
0359 KoPathPoint * KoPathShape::moveTo(const QPointF &p)
0360 {
0361     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StartSubpath | KoPathPoint::StopSubpath);
0362     KoSubpath * path = new KoSubpath;
0363     path->push_back(point);
0364     d->subpaths.push_back(path);
0365     notifyPointsChanged();
0366     return point;
0367 }
0368 
0369 KoPathPoint * KoPathShape::lineTo(const QPointF &p)
0370 {
0371     if (d->subpaths.empty()) {
0372         moveTo(QPointF(0, 0));
0373     }
0374     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0375     KoPathPoint * lastPoint = d->subpaths.last()->last();
0376     updateLastPriv(&lastPoint);
0377     d->subpaths.last()->push_back(point);
0378     notifyPointsChanged();
0379     return point;
0380 }
0381 
0382 KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
0383 {
0384     if (d->subpaths.empty()) {
0385         moveTo(QPointF(0, 0));
0386     }
0387     KoPathPoint * lastPoint = d->subpaths.last()->last();
0388     updateLastPriv(&lastPoint);
0389     lastPoint->setControlPoint2(c1);
0390     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0391     point->setControlPoint1(c2);
0392     d->subpaths.last()->push_back(point);
0393     notifyPointsChanged();
0394     return point;
0395 }
0396 
0397 KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
0398 {
0399     if (d->subpaths.empty())
0400         moveTo(QPointF(0, 0));
0401 
0402     KoPathPoint * lastPoint = d->subpaths.last()->last();
0403     updateLastPriv(&lastPoint);
0404     lastPoint->setControlPoint2(c);
0405     KoPathPoint * point = new KoPathPoint(this, p, KoPathPoint::StopSubpath);
0406     d->subpaths.last()->push_back(point);
0407     notifyPointsChanged();
0408     return point;
0409 }
0410 
0411 KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
0412 {
0413     if (d->subpaths.empty()) {
0414         moveTo(QPointF(0, 0));
0415     }
0416 
0417     KoPathPoint * lastPoint = d->subpaths.last()->last();
0418     if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
0419         lastPoint = d->subpaths.last()->first();
0420     }
0421     QPointF startpoint(lastPoint->point());
0422 
0423     KoPathPoint * newEndPoint = lastPoint;
0424 
0425     QPointF curvePoints[12];
0426     int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
0427     for (int i = 0; i < pointCnt; i += 3) {
0428         newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
0429     }
0430     return newEndPoint;
0431 }
0432 
0433 int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
0434 {
0435     int pointCnt = 0;
0436 
0437     // check Parameters
0438     if (sweepAngle == 0.0)
0439         return pointCnt;
0440 
0441     sweepAngle = qBound(-360.0, sweepAngle, 360.0);
0442 
0443     if (rx == 0 || ry == 0) {
0444         //TODO
0445     }
0446 
0447     // split angles bigger than 90° so that it gives a good approximation to the circle
0448     qreal parts = ceil(qAbs(sweepAngle / 90.0));
0449 
0450     qreal sa_rad = startAngle * M_PI / 180.0;
0451     qreal partangle = sweepAngle / parts;
0452     qreal endangle = startAngle + partangle;
0453     qreal se_rad = endangle * M_PI / 180.0;
0454     qreal sinsa = sin(sa_rad);
0455     qreal cossa = cos(sa_rad);
0456     qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
0457 
0458     // startpoint is at the last point is the path but when it is closed
0459     // it is at the first point
0460     QPointF startpoint(offset);
0461 
0462     //center berechnen
0463     QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
0464 
0465     //debugFlake <<"kappa" << kappa <<"parts" << parts;
0466 
0467     for (int part = 0; part < parts; ++part) {
0468         // start tangent
0469         curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
0470 
0471         qreal sinse = sin(se_rad);
0472         qreal cosse = cos(se_rad);
0473 
0474         // end point
0475         QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
0476         // end tangent
0477         curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
0478         curvePoints[pointCnt++] = endpoint;
0479 
0480         // set the endpoint as next start point
0481         startpoint = endpoint;
0482         sinsa = sinse;
0483         cossa = cosse;
0484         endangle += partangle;
0485         se_rad = endangle * M_PI / 180.0;
0486     }
0487 
0488     return pointCnt;
0489 }
0490 
0491 void KoPathShape::close()
0492 {
0493     if (d->subpaths.empty()) {
0494         return;
0495     }
0496     closeSubpathPriv(d->subpaths.last());
0497 }
0498 
0499 void KoPathShape::closeMerge()
0500 {
0501     if (d->subpaths.empty()) {
0502         return;
0503     }
0504     closeMergeSubpathPriv(d->subpaths.last());
0505 }
0506 
0507 QPointF KoPathShape::normalize()
0508 {
0509     QPointF tl(outline().boundingRect().topLeft());
0510     QTransform matrix;
0511     matrix.translate(-tl.x(), -tl.y());
0512     d->map(matrix);
0513 
0514     // keep the top left point of the object
0515     applyTransformation(matrix.inverted());
0516     shapeChangedPriv(ContentChanged);
0517     return tl;
0518 }
0519 
0520 void KoPathShape::Private::map(const QTransform &matrix)
0521 {
0522     KoSubpathList::const_iterator pathIt(subpaths.constBegin());
0523     for (; pathIt != subpaths.constEnd(); ++pathIt) {
0524         KoSubpath::const_iterator it((*pathIt)->constBegin());
0525         for (; it != (*pathIt)->constEnd(); ++it) {
0526             // It's possible there are null points in the map...
0527             if (*it) {
0528                 (*it)->map(matrix);
0529             }
0530         }
0531     }
0532 }
0533 
0534 void KoPathShape::updateLastPriv(KoPathPoint **lastPoint)
0535 {
0536     // check if we are about to add a new point to a closed subpath
0537     if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
0538             && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
0539         // get the first point of the subpath
0540         KoPathPoint *subpathStart = d->subpaths.last()->first();
0541         // clone the first point of the subpath...
0542         KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart, this);
0543         // ... and make it a normal point
0544         newLastPoint->setProperties(KoPathPoint::Normal);
0545         // now start a new subpath with the cloned start point
0546         KoSubpath *path = new KoSubpath;
0547         path->push_back(newLastPoint);
0548         d->subpaths.push_back(path);
0549         *lastPoint = newLastPoint;
0550     } else {
0551         // the subpath was not closed so the formerly last point
0552         // of the subpath is no end point anymore
0553         (*lastPoint)->unsetProperty(KoPathPoint::StopSubpath);
0554     }
0555     (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
0556 }
0557 
0558 QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r) const
0559 {
0560     QList<KoPathPoint*> result;
0561 
0562     KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
0563     for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
0564         KoSubpath::const_iterator it((*pathIt)->constBegin());
0565         for (; it != (*pathIt)->constEnd(); ++it) {
0566             if (r.contains((*it)->point()))
0567                 result.append(*it);
0568             else if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
0569                 result.append(*it);
0570             else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
0571                 result.append(*it);
0572         }
0573     }
0574     return result;
0575 }
0576 
0577 QList<KoPathSegment> KoPathShape::segmentsAt(const QRectF &r) const
0578 {
0579     QList<KoPathSegment> segments;
0580     int subpathCount = d->subpaths.count();
0581     for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
0582         KoSubpath * subpath = d->subpaths[subpathIndex];
0583         int pointCount = subpath->count();
0584         bool subpathClosed = isClosedSubpath(subpathIndex);
0585         for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
0586             if (pointIndex == (pointCount - 1) && ! subpathClosed)
0587                 break;
0588             KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
0589             QRectF controlRect = s.controlPointRect();
0590             if (! r.intersects(controlRect) && ! controlRect.contains(r))
0591                 continue;
0592             QRectF bound = s.boundingRect();
0593             if (! r.intersects(bound) && ! bound.contains(r))
0594                 continue;
0595 
0596             segments.append(s);
0597         }
0598     }
0599     return segments;
0600 }
0601 
0602 KoPathPointIndex KoPathShape::pathPointIndex(const KoPathPoint *point) const
0603 {
0604     for (int subpathIndex = 0; subpathIndex < d->subpaths.size(); ++subpathIndex) {
0605         KoSubpath * subpath = d->subpaths.at(subpathIndex);
0606         for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
0607             if (subpath->at(pointPos) == point) {
0608                 return KoPathPointIndex(subpathIndex, pointPos);
0609             }
0610         }
0611     }
0612     return KoPathPointIndex(-1, -1);
0613 }
0614 
0615 KoPathPoint * KoPathShape::pointByIndex(const KoPathPointIndex &pointIndex) const
0616 {
0617     KoSubpath *subpath = d->subPath(pointIndex.first);
0618 
0619     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
0620         return 0;
0621 
0622     return subpath->at(pointIndex.second);
0623 }
0624 
0625 KoPathSegment KoPathShape::segmentByIndex(const KoPathPointIndex &pointIndex) const
0626 {
0627     KoPathSegment segment(0, 0);
0628 
0629     KoSubpath *subpath = d->subPath(pointIndex.first);
0630 
0631     if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
0632         KoPathPoint * point = subpath->at(pointIndex.second);
0633         int index = pointIndex.second;
0634         // check if we have a (closing) segment starting from the last point
0635         if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
0636             index = 0;
0637         else
0638             ++index;
0639 
0640         if (index < subpath->size()) {
0641             segment = KoPathSegment(point, subpath->at(index));
0642         }
0643     }
0644     return segment;
0645 }
0646 
0647 int KoPathShape::pointCount() const
0648 {
0649     int i = 0;
0650     KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
0651     for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
0652         i += (*pathIt)->size();
0653     }
0654 
0655     return i;
0656 }
0657 
0658 int KoPathShape::subpathCount() const
0659 {
0660     return d->subpaths.count();
0661 }
0662 
0663 int KoPathShape::subpathPointCount(int subpathIndex) const
0664 {
0665     KoSubpath *subpath = d->subPath(subpathIndex);
0666 
0667     if (subpath == 0)
0668         return -1;
0669 
0670     return subpath->size();
0671 }
0672 
0673 bool KoPathShape::isClosedSubpath(int subpathIndex) const
0674 {
0675     KoSubpath *subpath = d->subPath(subpathIndex);
0676 
0677     if (subpath == 0)
0678         return false;
0679 
0680     const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
0681     const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
0682 
0683     return firstClosed && lastClosed;
0684 }
0685 
0686 bool KoPathShape::insertPoint(KoPathPoint* point, const KoPathPointIndex &pointIndex)
0687 {
0688     KoSubpath *subpath = d->subPath(pointIndex.first);
0689 
0690     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
0691         return false;
0692 
0693     KoPathPoint::PointProperties properties = point->properties();
0694     properties &= ~KoPathPoint::StartSubpath;
0695     properties &= ~KoPathPoint::StopSubpath;
0696     properties &= ~KoPathPoint::CloseSubpath;
0697     // check if new point starts subpath
0698     if (pointIndex.second == 0) {
0699         properties |= KoPathPoint::StartSubpath;
0700         // subpath was closed
0701         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0702             // keep the path closed
0703             properties |= KoPathPoint::CloseSubpath;
0704         }
0705         // old first point does not start the subpath anymore
0706         subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
0707     }
0708     // check if new point stops subpath
0709     else if (pointIndex.second == subpath->size()) {
0710         properties |= KoPathPoint::StopSubpath;
0711         // subpath was closed
0712         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0713             // keep the path closed
0714             properties = properties | KoPathPoint::CloseSubpath;
0715         }
0716         // old last point does not end subpath anymore
0717         subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
0718     }
0719 
0720     point->setProperties(properties);
0721     point->setParent(this);
0722     subpath->insert(pointIndex.second , point);
0723     notifyPointsChanged();
0724 
0725     return true;
0726 }
0727 
0728 KoPathPoint * KoPathShape::removePoint(const KoPathPointIndex &pointIndex)
0729 {
0730     KoSubpath *subpath = d->subPath(pointIndex.first);
0731 
0732     if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
0733         return 0;
0734 
0735     KoPathPoint * point = subpath->takeAt(pointIndex.second);
0736     point->setParent(0);
0737 
0738     //don't do anything (not even crash), if there was only one point
0739     if (pointCount()==0) {
0740         return point;
0741     }
0742     // check if we removed the first point
0743     else if (pointIndex.second == 0) {
0744         // first point removed, set new StartSubpath
0745         subpath->first()->setProperty(KoPathPoint::StartSubpath);
0746         // check if path was closed
0747         if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
0748             // keep path closed
0749             subpath->first()->setProperty(KoPathPoint::CloseSubpath);
0750         }
0751     }
0752     // check if we removed the last point
0753     else if (pointIndex.second == subpath->size()) { // use size as point is already removed
0754         // last point removed, set new StopSubpath
0755         subpath->last()->setProperty(KoPathPoint::StopSubpath);
0756         // check if path was closed
0757         if (point->properties() & KoPathPoint::CloseSubpath) {
0758             // keep path closed
0759             subpath->last()->setProperty(KoPathPoint::CloseSubpath);
0760         }
0761     }
0762 
0763     notifyPointsChanged();
0764 
0765     return point;
0766 }
0767 
0768 bool KoPathShape::breakAfter(const KoPathPointIndex &pointIndex)
0769 {
0770     KoSubpath *subpath = d->subPath(pointIndex.first);
0771 
0772     if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
0773             || isClosedSubpath(pointIndex.first))
0774         return false;
0775 
0776     KoSubpath * newSubpath = new KoSubpath;
0777 
0778     int size = subpath->size();
0779     for (int i = pointIndex.second + 1; i < size; ++i) {
0780         newSubpath->append(subpath->takeAt(pointIndex.second + 1));
0781     }
0782     // now make the first point of the new subpath a starting node
0783     newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
0784     // the last point of the old subpath is now an ending node
0785     subpath->last()->setProperty(KoPathPoint::StopSubpath);
0786 
0787     // insert the new subpath after the broken one
0788     d->subpaths.insert(pointIndex.first + 1, newSubpath);
0789     notifyPointsChanged();
0790 
0791     return true;
0792 }
0793 
0794 bool KoPathShape::join(int subpathIndex)
0795 {
0796     KoSubpath *subpath = d->subPath(subpathIndex);
0797     KoSubpath *nextSubpath = d->subPath(subpathIndex + 1);
0798 
0799     if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
0800             || isClosedSubpath(subpathIndex+1))
0801         return false;
0802 
0803     // the last point of the subpath does not end the subpath anymore
0804     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
0805     // the first point of the next subpath does not start a subpath anymore
0806     nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
0807 
0808     // append the second subpath to the first
0809     Q_FOREACH (KoPathPoint * p, *nextSubpath)
0810         subpath->append(p);
0811 
0812     // remove the nextSubpath from path
0813     d->subpaths.removeAt(subpathIndex + 1);
0814 
0815     // delete it as it is no longer possible to use it
0816     delete nextSubpath;
0817 
0818     notifyPointsChanged();
0819 
0820     return true;
0821 }
0822 
0823 bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
0824 {
0825     KoSubpath *subpath = d->subPath(oldSubpathIndex);
0826 
0827     if (subpath == 0 || newSubpathIndex >= d->subpaths.size())
0828         return false;
0829 
0830     if (oldSubpathIndex == newSubpathIndex)
0831         return true;
0832 
0833     d->subpaths.removeAt(oldSubpathIndex);
0834     d->subpaths.insert(newSubpathIndex, subpath);
0835 
0836     notifyPointsChanged();
0837 
0838     return true;
0839 }
0840 
0841 KoPathPointIndex KoPathShape::openSubpath(const KoPathPointIndex &pointIndex)
0842 {
0843     KoSubpath *subpath = d->subPath(pointIndex.first);
0844 
0845     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
0846             || !isClosedSubpath(pointIndex.first))
0847         return KoPathPointIndex(-1, -1);
0848 
0849     KoPathPoint * oldStartPoint = subpath->first();
0850     // the old starting node no longer starts the subpath
0851     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
0852     // the old end node no longer closes the subpath
0853     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
0854 
0855     // reorder the subpath
0856     for (int i = 0; i < pointIndex.second; ++i) {
0857         subpath->append(subpath->takeFirst());
0858     }
0859     // make the first point a start node
0860     subpath->first()->setProperty(KoPathPoint::StartSubpath);
0861     // make the last point an end node
0862     subpath->last()->setProperty(KoPathPoint::StopSubpath);
0863 
0864     notifyPointsChanged();
0865 
0866     return pathPointIndex(oldStartPoint);
0867 }
0868 
0869 KoPathPointIndex KoPathShape::closeSubpath(const KoPathPointIndex &pointIndex)
0870 {
0871     KoSubpath *subpath = d->subPath(pointIndex.first);
0872 
0873     if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
0874             || isClosedSubpath(pointIndex.first))
0875         return KoPathPointIndex(-1, -1);
0876 
0877     KoPathPoint * oldStartPoint = subpath->first();
0878     // the old starting node no longer starts the subpath
0879     oldStartPoint->unsetProperty(KoPathPoint::StartSubpath);
0880     // the old end node no longer ends the subpath
0881     subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
0882 
0883     // reorder the subpath
0884     for (int i = 0; i < pointIndex.second; ++i) {
0885         subpath->append(subpath->takeFirst());
0886     }
0887     subpath->first()->setProperty(KoPathPoint::StartSubpath);
0888     subpath->last()->setProperty(KoPathPoint::StopSubpath);
0889 
0890     closeSubpathPriv(subpath);
0891 
0892     notifyPointsChanged();
0893 
0894     return pathPointIndex(oldStartPoint);
0895 }
0896 
0897 bool KoPathShape::reverseSubpath(int subpathIndex)
0898 {
0899     KoSubpath *subpath = d->subPath(subpathIndex);
0900 
0901     if (subpath == 0)
0902         return false;
0903 
0904     int size = subpath->size();
0905     for (int i = 0; i < size; ++i) {
0906         KoPathPoint *p = subpath->takeAt(i);
0907         p->reverse();
0908         subpath->prepend(p);
0909     }
0910 
0911     // adjust the position dependent properties
0912     KoPathPoint *first = subpath->first();
0913     KoPathPoint *last = subpath->last();
0914 
0915     KoPathPoint::PointProperties firstProps = first->properties();
0916     KoPathPoint::PointProperties lastProps = last->properties();
0917 
0918     firstProps |= KoPathPoint::StartSubpath;
0919     firstProps &= ~KoPathPoint::StopSubpath;
0920     lastProps |= KoPathPoint::StopSubpath;
0921     lastProps &= ~KoPathPoint::StartSubpath;
0922     if (firstProps & KoPathPoint::CloseSubpath) {
0923         firstProps |= KoPathPoint::CloseSubpath;
0924         lastProps |= KoPathPoint::CloseSubpath;
0925     }
0926     first->setProperties(firstProps);
0927     last->setProperties(lastProps);
0928 
0929     notifyPointsChanged();
0930 
0931     return true;
0932 }
0933 
0934 KoSubpath * KoPathShape::removeSubpath(int subpathIndex)
0935 {
0936     KoSubpath *subpath = d->subPath(subpathIndex);
0937 
0938     if (subpath != 0) {
0939         Q_FOREACH (KoPathPoint* point, *subpath) {
0940             point->setParent(this);
0941         }
0942         d->subpaths.removeAt(subpathIndex);
0943     }
0944 
0945     notifyPointsChanged();
0946 
0947     return subpath;
0948 }
0949 
0950 bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
0951 {
0952     if (subpathIndex < 0 || subpathIndex > d->subpaths.size())
0953         return false;
0954 
0955     Q_FOREACH (KoPathPoint* point, *subpath) {
0956         point->setParent(this);
0957     }
0958 
0959     d->subpaths.insert(subpathIndex, subpath);
0960     notifyPointsChanged();
0961 
0962 
0963     return true;
0964 }
0965 int KoPathShape::combine(KoPathShape *path)
0966 {
0967     int insertSegmentPosition = -1;
0968     if (!path) return insertSegmentPosition;
0969 
0970     QTransform pathMatrix = path->absoluteTransformation();
0971     QTransform myMatrix = absoluteTransformation().inverted();
0972 
0973     Q_FOREACH (KoSubpath* subpath, path->d->subpaths) {
0974         KoSubpath *newSubpath = new KoSubpath();
0975 
0976         Q_FOREACH (KoPathPoint* point, *subpath) {
0977             KoPathPoint *newPoint = new KoPathPoint(*point, this);
0978             newPoint->map(pathMatrix);
0979             newPoint->map(myMatrix);
0980             newSubpath->append(newPoint);
0981         }
0982         d->subpaths.append(newSubpath);
0983 
0984         if (insertSegmentPosition < 0) {
0985             insertSegmentPosition = d->subpaths.size() - 1;
0986         }
0987     }
0988     normalize();
0989 
0990     notifyPointsChanged();
0991     return insertSegmentPosition;
0992 }
0993 
0994 bool KoPathShape::separate(QList<KoPathShape*> & separatedPaths)
0995 {
0996     if (! d->subpaths.size())
0997         return false;
0998 
0999     QTransform myMatrix = absoluteTransformation();
1000 
1001     Q_FOREACH (KoSubpath* subpath, d->subpaths) {
1002         KoPathShape *shape = new KoPathShape();
1003 
1004         shape->setStroke(stroke());
1005         shape->setBackground(background());
1006         shape->setShapeId(shapeId());
1007         shape->setZIndex(zIndex());
1008 
1009         KoSubpath *newSubpath = new KoSubpath();
1010 
1011         Q_FOREACH (KoPathPoint* point, *subpath) {
1012             KoPathPoint *newPoint = new KoPathPoint(*point, shape);
1013             newPoint->map(myMatrix);
1014             newSubpath->append(newPoint);
1015         }
1016         shape->d->subpaths.append(newSubpath);
1017         shape->normalize();
1018 
1019         // NOTE: shape cannot have any listeners yet, so no notification about
1020         //       points modification is needed
1021 
1022         separatedPaths.append(shape);
1023     }
1024     return true;
1025 }
1026 
1027 void KoPathShape::closeSubpathPriv(KoSubpath *subpath)
1028 {
1029     if (! subpath)
1030         return;
1031 
1032     subpath->last()->setProperty(KoPathPoint::CloseSubpath);
1033     subpath->first()->setProperty(KoPathPoint::CloseSubpath);
1034 
1035     notifyPointsChanged();
1036 }
1037 
1038 void KoPathShape::closeMergeSubpathPriv(KoSubpath *subpath)
1039 {
1040     if (! subpath || subpath->size() < 2)
1041         return;
1042 
1043     KoPathPoint * lastPoint = subpath->last();
1044     KoPathPoint * firstPoint = subpath->first();
1045 
1046     // check if first and last points are coincident
1047     if (lastPoint->point() == firstPoint->point()) {
1048         // we are removing the current last point and
1049         // reuse its first control point if active
1050         firstPoint->setProperty(KoPathPoint::StartSubpath);
1051         firstPoint->setProperty(KoPathPoint::CloseSubpath);
1052         if (lastPoint->activeControlPoint1())
1053             firstPoint->setControlPoint1(lastPoint->controlPoint1());
1054         // remove last point
1055         delete subpath->takeLast();
1056         // the new last point closes the subpath now
1057         lastPoint = subpath->last();
1058         lastPoint->setProperty(KoPathPoint::StopSubpath);
1059         lastPoint->setProperty(KoPathPoint::CloseSubpath);
1060 
1061         notifyPointsChanged();
1062     } else {
1063         closeSubpathPriv(subpath);
1064     }
1065 }
1066 
1067 const KoSubpathList &KoPathShape::subpaths() const
1068 {
1069     return d->subpaths;
1070 }
1071 
1072 KoSubpathList &KoPathShape::subpaths()
1073 {
1074     return d->subpaths;
1075 }
1076 
1077 void KoPathShape::map(const QTransform &matrix)
1078 {
1079     return d->map(matrix);
1080 }
1081 
1082 KoSubpath *KoPathShape::Private::subPath(int subpathIndex) const
1083 {
1084     if (subpathIndex < 0 || subpathIndex >= subpaths.size())
1085         return 0;
1086 
1087     return subpaths.at(subpathIndex);
1088 }
1089 
1090 QString KoPathShape::pathShapeId() const
1091 {
1092     return KoPathShapeId;
1093 }
1094 
1095 QString KoPathShape::toString(const QTransform &matrix) const
1096 {
1097     QString pathString;
1098 
1099     // iterate over all subpaths
1100     KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
1101     for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
1102         KoSubpath::const_iterator pointIt((*pathIt)->constBegin());
1103         // keep a pointer to the first point of the subpath
1104         KoPathPoint *firstPoint(*pointIt);
1105         // keep a pointer to the previous point of the subpath
1106         KoPathPoint *lastPoint = firstPoint;
1107         // keep track if the previous point has an active control point 2
1108         bool activeControlPoint2 = false;
1109 
1110         // iterate over all points of the current subpath
1111         for (; pointIt != (*pathIt)->constEnd(); ++pointIt) {
1112             KoPathPoint *currPoint(*pointIt);
1113             if (!currPoint) {
1114                 qWarning() << "Found a zero point in the shape's path!";
1115                 continue;
1116             }
1117             // first point of subpath ?
1118             if (currPoint == firstPoint) {
1119                 // are we starting a subpath ?
1120                 if (currPoint->properties() & KoPathPoint::StartSubpath) {
1121                     const QPointF p = matrix.map(currPoint->point());
1122                     pathString += QString("M%1 %2").arg(p.x()).arg(p.y());
1123                 }
1124             }
1125             // end point of curve segment ?
1126             else if (activeControlPoint2 || currPoint->activeControlPoint1()) {
1127                 // check if we have a cubic or quadratic curve
1128                 const bool isCubic = activeControlPoint2 && currPoint->activeControlPoint1();
1129                 KoPathSegment cubicSeg = isCubic ? KoPathSegment(lastPoint, currPoint)
1130                                                  : KoPathSegment(lastPoint, currPoint).toCubic();
1131                 if (cubicSeg.first()  && cubicSeg.second()) {
1132                     const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1133                     const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1134                     const QPointF p = matrix.map(cubicSeg.second()->point());
1135                     pathString += QString("C%1 %2 %3 %4 %5 %6")
1136                             .arg(cp1.x()).arg(cp1.y())
1137                             .arg(cp2.x()).arg(cp2.y())
1138                             .arg(p.x()).arg(p.y());
1139                 }
1140             }
1141             // end point of line segment!
1142             else {
1143                 const QPointF p = matrix.map(currPoint->point());
1144                 pathString += QString("L%1 %2").arg(p.x()).arg(p.y());
1145             }
1146             // last point closes subpath ?
1147             if (currPoint->properties() & KoPathPoint::StopSubpath
1148                     && currPoint->properties() & KoPathPoint::CloseSubpath) {
1149                 // add curve when there is a curve on the way to the first point
1150                 if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
1151                     // check if we have a cubic or quadratic curve
1152                     const bool isCubic = currPoint->activeControlPoint2() && firstPoint->activeControlPoint1();
1153                     KoPathSegment cubicSeg = isCubic ? KoPathSegment(currPoint, firstPoint)
1154                                                      : KoPathSegment(currPoint, firstPoint).toCubic();
1155                     if (cubicSeg.first()  && cubicSeg.second()) {
1156                         const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1157                         const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1158 
1159                         const QPointF p = matrix.map(cubicSeg.second()->point());
1160                         pathString += QString("C%1 %2 %3 %4 %5 %6")
1161                                 .arg(cp1.x()).arg(cp1.y())
1162                                 .arg(cp2.x()).arg(cp2.y())
1163                                 .arg(p.x()).arg(p.y());
1164                     }
1165                 }
1166                 pathString += QString("Z");
1167             }
1168 
1169             activeControlPoint2 = currPoint->activeControlPoint2();
1170             lastPoint = currPoint;
1171         }
1172     }
1173 
1174     return pathString;
1175 }
1176 
1177 char nodeType(const KoPathPoint * point)
1178 {
1179     if (point->properties() & KoPathPoint::IsSmooth) {
1180         return 's';
1181     }
1182     else if (point->properties() & KoPathPoint::IsSymmetric) {
1183         return 'z';
1184     }
1185     else {
1186         return 'c';
1187     }
1188 }
1189 
1190 QString KoPathShape::Private::nodeTypes() const
1191 {
1192     QString types;
1193     KoSubpathList::const_iterator pathIt(subpaths.constBegin());
1194     for (; pathIt != subpaths.constEnd(); ++pathIt) {
1195         KoSubpath::const_iterator it((*pathIt)->constBegin());
1196         for (; it != (*pathIt)->constEnd(); ++it) {
1197             if (it == (*pathIt)->constBegin()) {
1198                 types.append('c');
1199             }
1200             else {
1201                 types.append(nodeType(*it));
1202             }
1203 
1204             if ((*it)->properties() & KoPathPoint::StopSubpath
1205                     && (*it)->properties() & KoPathPoint::CloseSubpath) {
1206                 KoPathPoint * firstPoint = (*pathIt)->first();
1207                 types.append(nodeType(firstPoint));
1208             }
1209         }
1210     }
1211     return types;
1212 }
1213 
1214 void updateNodeType(KoPathPoint * point, const QChar & nodeType)
1215 {
1216     if (nodeType == 's') {
1217         point->setProperty(KoPathPoint::IsSmooth);
1218     }
1219     else if (nodeType == 'z') {
1220         point->setProperty(KoPathPoint::IsSymmetric);
1221     }
1222 }
1223 
1224 void KoPathShape::Private::loadNodeTypes(const KoXmlElement &element)
1225 {
1226     if (element.hasAttributeNS(KoXmlNS::calligra, "nodeTypes")) {
1227         QString nodeTypes = element.attributeNS(KoXmlNS::calligra, "nodeTypes");
1228         QString::const_iterator nIt(nodeTypes.constBegin());
1229         KoSubpathList::const_iterator pathIt(subpaths.constBegin());
1230         for (; pathIt != subpaths.constEnd(); ++pathIt) {
1231             KoSubpath::const_iterator it((*pathIt)->constBegin());
1232             for (; it != (*pathIt)->constEnd(); ++it, nIt++) {
1233                 // be sure not to crash if there are not enough nodes in nodeTypes
1234                 if (nIt == nodeTypes.constEnd()) {
1235                     warnFlake << "not enough nodes in calligra:nodeTypes";
1236                     return;
1237                 }
1238                 // the first node is always of type 'c'
1239                 if (it != (*pathIt)->constBegin()) {
1240                     updateNodeType(*it, *nIt);
1241                 }
1242 
1243                 if ((*it)->properties() & KoPathPoint::StopSubpath
1244                         && (*it)->properties() & KoPathPoint::CloseSubpath) {
1245                     ++nIt;
1246                     updateNodeType((*pathIt)->first(), *nIt);
1247                 }
1248             }
1249         }
1250     }
1251 }
1252 
1253 Qt::FillRule KoPathShape::fillRule() const
1254 {
1255     return d->fillRule;
1256 }
1257 
1258 void KoPathShape::setFillRule(Qt::FillRule fillRule)
1259 {
1260     d->fillRule = fillRule;
1261 }
1262 
1263 KoPathShape * KoPathShape::createShapeFromPainterPath(const QPainterPath &path)
1264 {
1265     KoPathShape * shape = new KoPathShape();
1266 
1267     int elementCount = path.elementCount();
1268     for (int i = 0; i < elementCount; i++) {
1269         QPainterPath::Element element = path.elementAt(i);
1270         switch (element.type) {
1271         case QPainterPath::MoveToElement:
1272             shape->moveTo(QPointF(element.x, element.y));
1273             break;
1274         case QPainterPath::LineToElement:
1275             shape->lineTo(QPointF(element.x, element.y));
1276             break;
1277         case QPainterPath::CurveToElement:
1278             shape->curveTo(QPointF(element.x, element.y),
1279                            QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
1280                            QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y));
1281             break;
1282         default:
1283             continue;
1284         }
1285     }
1286 
1287     shape->setShapeId(KoPathShapeId);
1288 
1289     //shape->normalize();
1290     return shape;
1291 }
1292 
1293 bool KoPathShape::hitTest(const QPointF &position) const
1294 {
1295     if (parent() && parent()->isClipped(this) && ! parent()->hitTest(position))
1296         return false;
1297 
1298     QPointF point = absoluteTransformation().inverted().map(position);
1299     const QPainterPath outlinePath = outline();
1300     if (stroke()) {
1301         KoInsets insets;
1302         stroke()->strokeInsets(this, insets);
1303         QRectF roi(QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom));
1304 
1305         roi.moveCenter(point);
1306         if (outlinePath.intersects(roi) || outlinePath.contains(roi))
1307             return true;
1308     } else {
1309         if (outlinePath.contains(point))
1310             return true;
1311     }
1312 
1313     // if there is no shadow we can as well just leave
1314     if (! shadow())
1315         return false;
1316 
1317     // the shadow has an offset to the shape, so we simply
1318     // check if the position minus the shadow offset hits the shape
1319     point = absoluteTransformation().inverted().map(position - shadow()->offset());
1320 
1321     return outlinePath.contains(point);
1322 }
1323 
1324 void KoPathShape::setMarker(KoMarker *marker, KoFlake::MarkerPosition pos)
1325 {
1326     if (!marker && d->markersNew.contains(pos)) {
1327         d->markersNew.remove(pos);
1328     } else {
1329         d->markersNew[pos] = marker;
1330     }
1331 
1332     notifyChanged();
1333     shapeChangedPriv(StrokeChanged);
1334 }
1335 
1336 KoMarker *KoPathShape::marker(KoFlake::MarkerPosition pos) const
1337 {
1338     return d->markersNew[pos].data();
1339 }
1340 
1341 bool KoPathShape::hasMarkers() const
1342 {
1343     return !d->markersNew.isEmpty();
1344 }
1345 
1346 bool KoPathShape::autoFillMarkers() const
1347 {
1348     return d->autoFillMarkers;
1349 }
1350 
1351 void KoPathShape::setAutoFillMarkers(bool value)
1352 {
1353     d->autoFillMarkers = value;
1354 }
1355 
1356 void KoPathShape::recommendPointSelectionChange(const QList<KoPathPointIndex> &newSelection)
1357 {
1358     Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) {
1359         PointSelectionChangeListener *pointListener = dynamic_cast<PointSelectionChangeListener*>(listener);
1360         if (pointListener) {
1361             pointListener->recommendPointSelectionChange(this, newSelection);
1362         }
1363     }
1364 }
1365 
1366 void KoPathShape::notifyPointsChanged()
1367 {
1368     Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) {
1369         PointSelectionChangeListener *pointListener = dynamic_cast<PointSelectionChangeListener*>(listener);
1370         if (pointListener) {
1371             pointListener->notifyPathPointsChanged(this);
1372         }
1373     }
1374 }
1375 
1376 QPainterPath KoPathShape::pathStroke(const QPen &pen) const
1377 {
1378     if (d->subpaths.isEmpty()) {
1379         return QPainterPath();
1380     }
1381     QPainterPath pathOutline;
1382 
1383     QPainterPathStroker stroker;
1384     stroker.setWidth(0);
1385     stroker.setJoinStyle(Qt::MiterJoin);
1386     stroker.setWidth(pen.widthF());
1387     stroker.setJoinStyle(pen.joinStyle());
1388     stroker.setMiterLimit(pen.miterLimit());
1389     stroker.setCapStyle(pen.capStyle());
1390     stroker.setDashOffset(pen.dashOffset());
1391     stroker.setDashPattern(pen.dashPattern());
1392 
1393     QPainterPath path = stroker.createStroke(outline());
1394 
1395     pathOutline.addPath(path);
1396     pathOutline.setFillRule(Qt::WindingFill);
1397 
1398     return pathOutline;
1399 }
1400 
1401 void KoPathShape::PointSelectionChangeListener::notifyShapeChanged(KoShape::ChangeType type, KoShape *shape)
1402 {
1403     Q_UNUSED(type);
1404     Q_UNUSED(shape);
1405 }