File indexing completed on 2024-05-12 15:56:44

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