Krita Source Code Documentation
Loading...
Searching...
No Matches
KoPathShape.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 SPDX-FileCopyrightText: 2006-2008, 2010-2011 Thorsten Zachmann <zachmann@kde.org>
3 SPDX-FileCopyrightText: 2006-2011 Jan Hambrecht <jaham@gmx.net>
4 SPDX-FileCopyrightText: 2007-2009 Thomas Zander <zander@kde.org>
5 SPDX-FileCopyrightText: 2011 Jean-Nicolas Artaud <jeannicolasartaud@gmail.com>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "KoPathShape.h"
11#include "KoPathShape_p.h"
12
13#include "KoPathSegment.h"
14#include "KoPathPoint.h"
15#include "KoShapeStrokeModel.h"
16#include "KoPathShapeLoader.h"
19#include "KoShapeBackground.h"
20#include "KoShapeContainer.h"
21#include "KoMarker.h"
22#include "KoShapeStroke.h"
23#include "KoInsets.h"
24
25#include <KoUnit.h>
27
28#include <FlakeDebug.h>
29#include <QPainter>
30#include <QPainterPath>
31
32#include "kis_global.h"
33#include <kis_algebra_2d.h>
34
35#include <qnumeric.h> // for qIsNaN
36static bool qIsNaNPoint(const QPointF &p) {
37 return qIsNaN(p.x()) || qIsNaN(p.y());
38}
39
41 : fillRule(Qt::OddEvenFill)
42 , autoFillMarkers(false)
43{
44}
45
47 : fillRule(rhs.fillRule)
48 , markersNew(rhs.markersNew)
49 , autoFillMarkers(rhs.autoFillMarkers)
50{
51}
52
53QRectF KoPathShape::Private::handleRect(const QPointF &p, qreal radius) const
54{
55 return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
56}
57
58
60 : KoShape()
61 , d(new Private)
62{
63}
64
66 : KoShape(rhs)
67 , d(new Private(*rhs.d))
68{
69 // local data cannot be shared via QSharedData because
70 // every path point holds a pointer to the parent shape
72 Q_FOREACH (KoSubpath *subPath, rhs.d->subpaths) {
73 KoSubpath *clonedSubPath = new KoSubpath();
74
75 Q_FOREACH (KoPathPoint *point, *subPath) {
76 *clonedSubPath << new KoPathPoint(*point, this);
77 }
78
79 subpaths << clonedSubPath;
80 }
81 d->subpaths = subpaths;
82}
83
88
90{
91 return new KoPathShape(*this);
92}
93
95{
96 Q_FOREACH (KoSubpath *subpath, d->subpaths) {
97 Q_FOREACH (KoPathPoint *point, *subpath)
98 delete point;
99 delete subpath;
100 }
101 d->subpaths.clear();
102
104}
105
106void KoPathShape::paint(QPainter &painter) const
107{
108 KisQPainterStateSaver saver(&painter);
109 Q_UNUSED(saver);
110
111 QPainterPath path(outline());
112 path.setFillRule(d->fillRule);
113
114 if (background()) {
115 background()->paint(painter, path);
116 }
117 //d->paintDebug(painter);
118}
119
120
121#ifndef NDEBUG
122void KoPathShape::Private::paintDebug(QPainter &painter)
123{
124 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
125 int i = 0;
126
127 QPen pen(Qt::black, 0);
128 painter.save();
129 painter.setPen(pen);
130 for (; pathIt != subpaths.constEnd(); ++pathIt) {
131 KoSubpath::const_iterator it((*pathIt)->constBegin());
132 for (; it != (*pathIt)->constEnd(); ++it) {
133 ++i;
134 KoPathPoint *point = (*it);
135 QRectF r(point->point(), QSizeF(5, 5));
136 r.translate(-2.5, -2.5);
137 QPen pen(Qt::black, 0);
138 painter.setPen(pen);
139 if (point->activeControlPoint1() && point->activeControlPoint2()) {
140 QBrush b(Qt::red);
141 painter.setBrush(b);
142 } else if (point->activeControlPoint1()) {
143 QBrush b(Qt::yellow);
144 painter.setBrush(b);
145 } else if (point->activeControlPoint2()) {
146 QBrush b(Qt::darkYellow);
147 painter.setBrush(b);
148 }
149 painter.drawEllipse(r);
150 }
151 }
152 painter.restore();
153 debugFlake << "nop =" << i;
154}
155
157{
158 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
159 for (; pathIt != subpaths.constEnd(); ++pathIt) {
160 KoSubpath::const_iterator it((*pathIt)->constBegin());
161 for (; it != (*pathIt)->constEnd(); ++it) {
162 debugFlake << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties();
163 }
164 }
165}
166#endif
167
169{
170 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
171
172 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
173 KoSubpath::const_iterator it((*pathIt)->constBegin());
174 for (; it != (*pathIt)->constEnd(); ++it)
175 (*it)->paint(handlesHelper, KoPathPoint::Node);
176 }
177}
178
180{
181 return outline().boundingRect();
182}
183
184QPainterPath KoPathShape::outline() const
185{
186 QPainterPath path;
187 for (auto subpathIt = d->subpaths.constBegin(); subpathIt != d->subpaths.constEnd(); ++subpathIt) {
188 const KoSubpath * subpath = *subpathIt;
189 const KoPathPoint * lastPoint = subpath->constFirst();
190 bool activeCP = false;
191 for (auto pointIt = subpath->constBegin(); pointIt != subpath->constEnd(); ++pointIt) {
192 const KoPathPoint * currPoint = *pointIt;
193 KoPathPoint::PointProperties currProperties = currPoint->properties();
194 if (currPoint == subpath->constFirst()) {
195 if (currProperties & KoPathPoint::StartSubpath) {
196 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
197 path.moveTo(currPoint->point());
198 }
199 } else if (activeCP && currPoint->activeControlPoint1()) {
200 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
201 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
202 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
203 path.cubicTo(
204 lastPoint->controlPoint2(),
205 currPoint->controlPoint1(),
206 currPoint->point());
207 } else if (activeCP || currPoint->activeControlPoint1()) {
208 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
209 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
210 path.quadTo(
211 activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
212 currPoint->point());
213 } else {
214 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
215 path.lineTo(currPoint->point());
216 }
217 if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
218 // add curve when there is a curve on the way to the first point
219 KoPathPoint * firstPoint = subpath->first();
220 Q_ASSERT(!qIsNaNPoint(firstPoint->point()));
221 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
222 path.cubicTo(
223 currPoint->controlPoint2(),
224 firstPoint->controlPoint1(),
225 firstPoint->point());
226 }
227 else if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
228 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
229 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
230 path.quadTo(
231 currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
232 firstPoint->point());
233 }
234 path.closeSubpath();
235 }
236
237 if (currPoint->activeControlPoint2()) {
238 activeCP = true;
239 } else {
240 activeCP = false;
241 }
242 lastPoint = currPoint;
243 }
244 }
245
246 return path;
247}
248
250{
251 const QTransform transform = absoluteTransformation();
252
258 qreal outlineSweepWidth = 0;
259
260 const QSharedPointer<KoShapeStroke> lineBorder = qSharedPointerDynamicCast<KoShapeStroke>(stroke());
261 if (lineBorder) {
262 outlineSweepWidth = lineBorder->lineWidth();
263 }
264
265 if (stroke()) {
266 KoInsets inset;
267 stroke()->strokeInsets(this, inset);
268 const qreal maxInset = std::max({inset.left, inset.top, inset.right, inset.bottom});
269
270 // insets extend outside the shape, but width extends both inside and outside,
271 // so we should multiply insets by 2.0
272 outlineSweepWidth = std::max({outlineSweepWidth,
273 2.0 * maxInset,
274 2.0 * stroke()->strokeMaxMarkersInset(this)});
275 }
276
277
278
281
282#if 0
283 QPen pen(Qt::black, outlineSweepWidth);
284
285 // select round joins and caps to ensure it sweeps exactly
286 // 'outlineSweepWidth' pixels in every possible
287 pen.setJoinStyle(Qt::RoundJoin);
288 pen.setCapStyle(Qt::RoundCap);
289 QRectF bb = transform.map(pathStroke(pen)).boundingRect();
290#endif
291
292 // add 10% extra update area around the doubled insets
293 return transform.mapRect(kisGrowRect(outline().boundingRect(), 1.1 * 0.5 * outlineSweepWidth));
294}
295
296QSizeF KoPathShape::size() const
297{
298 // don't call boundingRect here as it uses absoluteTransformation
299 // which itself uses size() -> leads to infinite recursion
300 return outlineRect().size();
301}
302
303void KoPathShape::setSize(const QSizeF &newSize)
304{
305 QTransform matrix(resizeMatrix(newSize));
306
307 KoShape::setSize(newSize);
308 d->map(matrix);
309}
310
311QTransform KoPathShape::resizeMatrix(const QSizeF & newSize) const
312{
313 QSizeF oldSize = size();
314 if (oldSize.width() == 0.0) {
315 oldSize.setWidth(0.000001);
316 }
317 if (oldSize.height() == 0.0) {
318 oldSize.setHeight(0.000001);
319 }
320
321 QSizeF sizeNew(newSize);
322 if (sizeNew.width() == 0.0) {
323 sizeNew.setWidth(0.000001);
324 }
325 if (sizeNew.height() == 0.0) {
326 sizeNew.setHeight(0.000001);
327 }
328
329 return QTransform(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
330}
331
333{
335 KoSubpath * path = new KoSubpath;
336 path->push_back(point);
337 d->subpaths.push_back(path);
339 return point;
340}
341
343{
344 if (d->subpaths.empty()) {
345 moveTo(QPointF(0, 0));
346 }
348 KoPathPoint * lastPoint = d->subpaths.last()->last();
349 updateLastPriv(&lastPoint);
350 d->subpaths.last()->push_back(point);
352 return point;
353}
354
355KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
356{
357 if (d->subpaths.empty()) {
358 moveTo(QPointF(0, 0));
359 }
360 KoPathPoint * lastPoint = d->subpaths.last()->last();
361 updateLastPriv(&lastPoint);
362 lastPoint->setControlPoint2(c1);
364 point->setControlPoint1(c2);
365 d->subpaths.last()->push_back(point);
367 return point;
368}
369
370KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
371{
372 if (d->subpaths.empty())
373 moveTo(QPointF(0, 0));
374
375 KoPathPoint * lastPoint = d->subpaths.last()->last();
376 updateLastPriv(&lastPoint);
377 lastPoint->setControlPoint2(c);
379 d->subpaths.last()->push_back(point);
381 return point;
382}
383
384KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
385{
386 if (d->subpaths.empty()) {
387 moveTo(QPointF(0, 0));
388 }
389
390 KoPathPoint * lastPoint = d->subpaths.last()->last();
391 if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
392 lastPoint = d->subpaths.last()->first();
393 }
394 QPointF startpoint(lastPoint->point());
395
396 KoPathPoint * newEndPoint = lastPoint;
397
398 QPointF curvePoints[12];
399 int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
400 for (int i = 0; i < pointCnt; i += 3) {
401 newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
402 }
403 return newEndPoint;
404}
405
406int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
407{
408 int pointCnt = 0;
409
410 // check Parameters
411 if (sweepAngle == 0.0)
412 return pointCnt;
413
414 sweepAngle = qBound(-360.0, sweepAngle, 360.0);
415
416 if (rx == 0 || ry == 0) {
417 //TODO
418 }
419
420 // split angles bigger than 90° so that it gives a good approximation to the circle
421 qreal parts = ceil(qAbs(sweepAngle / 90.0));
422
423 qreal sa_rad = startAngle * M_PI / 180.0;
424 qreal partangle = sweepAngle / parts;
425 qreal endangle = startAngle + partangle;
426 qreal se_rad = endangle * M_PI / 180.0;
427 qreal sinsa = sin(sa_rad);
428 qreal cossa = cos(sa_rad);
429 qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
430
431 // startpoint is at the last point is the path but when it is closed
432 // it is at the first point
433 QPointF startpoint(offset);
434
435 //center berechnen
436 QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
437
438 //debugFlake <<"kappa" << kappa <<"parts" << parts;
439
440 for (int part = 0; part < parts; ++part) {
441 // start tangent
442 curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
443
444 qreal sinse = sin(se_rad);
445 qreal cosse = cos(se_rad);
446
447 // end point
448 QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
449 // end tangent
450 curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
451 curvePoints[pointCnt++] = endpoint;
452
453 // set the endpoint as next start point
454 startpoint = endpoint;
455 sinsa = sinse;
456 cossa = cosse;
457 endangle += partangle;
458 se_rad = endangle * M_PI / 180.0;
459 }
460
461 return pointCnt;
462}
463
465{
466 if (d->subpaths.empty()) {
467 return;
468 }
469 closeSubpathPriv(d->subpaths.last());
470}
471
473{
474 if (d->subpaths.empty()) {
475 return;
476 }
477 closeMergeSubpathPriv(d->subpaths.last());
478}
479
481{
482 QPointF tl(outline().boundingRect().topLeft());
483 QTransform matrix;
484 matrix.translate(-tl.x(), -tl.y());
485 d->map(matrix);
486
487 // keep the top left point of the object
488 applyTransformation(matrix.inverted());
490 return tl;
491}
492
493void KoPathShape::Private::map(const QTransform &matrix)
494{
495 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
496 for (; pathIt != subpaths.constEnd(); ++pathIt) {
497 KoSubpath::const_iterator it((*pathIt)->constBegin());
498 for (; it != (*pathIt)->constEnd(); ++it) {
499 // It's possible there are null points in the map...
500 if (*it) {
501 (*it)->map(matrix);
502 }
503 }
504 }
505}
506
508{
509 // check if we are about to add a new point to a closed subpath
510 if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
511 && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
512 // get the first point of the subpath
513 KoPathPoint *subpathStart = d->subpaths.last()->first();
514 // clone the first point of the subpath...
515 KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart, this);
516 // ... and make it a normal point
517 newLastPoint->setProperties(KoPathPoint::Normal);
518 // now start a new subpath with the cloned start point
519 KoSubpath *path = new KoSubpath;
520 path->push_back(newLastPoint);
521 d->subpaths.push_back(path);
522 *lastPoint = newLastPoint;
523 } else {
524 // the subpath was not closed so the formerly last point
525 // of the subpath is no end point anymore
527 }
528 (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
529}
530
531QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r, const bool useControlPoints) const
532{
533 QList<KoPathPoint*> result;
534
535 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
536 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
537 KoSubpath::const_iterator it((*pathIt)->constBegin());
538 for (; it != (*pathIt)->constEnd(); ++it) {
539 if (r.contains((*it)->point()))
540 result.append(*it);
541 else if (useControlPoints) {
542 if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
543 result.append(*it);
544 else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
545 result.append(*it);
546 }
547 }
548 }
549 return result;
550}
551
553{
554 QList<KoPathSegment> segments;
555 int subpathCount = d->subpaths.count();
556 for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
557 KoSubpath * subpath = d->subpaths[subpathIndex];
558 int pointCount = subpath->count();
559 bool subpathClosed = isClosedSubpath(subpathIndex);
560 for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
561 if (pointIndex == (pointCount - 1) && ! subpathClosed)
562 break;
563 KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
564 QRectF controlRect = s.controlPointRect();
565 if (! r.intersects(controlRect) && ! controlRect.contains(r))
566 continue;
567 QRectF bound = s.boundingRect();
568 if (! r.intersects(bound) && ! bound.contains(r))
569 continue;
570
571 segments.append(s);
572 }
573 }
574 return segments;
575}
576
578{
579 for (int subpathIndex = 0; subpathIndex < d->subpaths.size(); ++subpathIndex) {
580 KoSubpath * subpath = d->subpaths.at(subpathIndex);
581 for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
582 if (subpath->at(pointPos) == point) {
583 return KoPathPointIndex(subpathIndex, pointPos);
584 }
585 }
586 }
587 return KoPathPointIndex(-1, -1);
588}
589
591{
592 KoSubpath *subpath = d->subPath(pointIndex.first);
593
594 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
595 return 0;
596
597 return subpath->at(pointIndex.second);
598}
599
601{
602 KoPathSegment segment(0, 0);
603
604 KoSubpath *subpath = d->subPath(pointIndex.first);
605
606 if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
607 KoPathPoint * point = subpath->at(pointIndex.second);
608 int index = pointIndex.second;
609 // check if we have a (closing) segment starting from the last point
610 if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
611 index = 0;
612 else
613 ++index;
614
615 if (index < subpath->size()) {
616 segment = KoPathSegment(point, subpath->at(index));
617 }
618 }
619 return segment;
620}
621
623{
624 int i = 0;
625 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
626 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
627 i += (*pathIt)->size();
628 }
629
630 return i;
631}
632
634{
635 return d->subpaths.count();
636}
637
638int KoPathShape::subpathPointCount(int subpathIndex) const
639{
640 KoSubpath *subpath = d->subPath(subpathIndex);
641
642 if (subpath == 0)
643 return -1;
644
645 return subpath->size();
646}
647
648bool KoPathShape::isClosedSubpath(int subpathIndex) const
649{
650 KoSubpath *subpath = d->subPath(subpathIndex);
651
652 if (subpath == 0)
653 return false;
654
655 const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
656 const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
657
658 return firstClosed && lastClosed;
659}
660
662{
663 KoSubpath *subpath = d->subPath(pointIndex.first);
664
665 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
666 return false;
667
668 KoPathPoint::PointProperties properties = point->properties();
669 properties &= ~KoPathPoint::StartSubpath;
670 properties &= ~KoPathPoint::StopSubpath;
671 properties &= ~KoPathPoint::CloseSubpath;
672 // check if new point starts subpath
673 if (pointIndex.second == 0) {
674 properties |= KoPathPoint::StartSubpath;
675 // subpath was closed
676 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
677 // keep the path closed
678 properties |= KoPathPoint::CloseSubpath;
679 }
680 // old first point does not start the subpath anymore
681 subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
682 }
683 // check if new point stops subpath
684 else if (pointIndex.second == subpath->size()) {
685 properties |= KoPathPoint::StopSubpath;
686 // subpath was closed
687 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
688 // keep the path closed
689 properties = properties | KoPathPoint::CloseSubpath;
690 }
691 // old last point does not end subpath anymore
692 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
693 }
694
695 point->setProperties(properties);
696 point->setParent(this);
697 subpath->insert(pointIndex.second , point);
699
700 return true;
701}
702
704{
705 KoSubpath *subpath = d->subPath(pointIndex.first);
706
707 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
708 return 0;
709
710 KoPathPoint * point = subpath->takeAt(pointIndex.second);
711 point->setParent(0);
712
713 //don't do anything (not even crash), if there was only one point
714 if (pointCount()==0) {
715 return point;
716 }
717 // check if we removed the first point
718 else if (pointIndex.second == 0) {
719 // first point removed, set new StartSubpath
720 subpath->first()->setProperty(KoPathPoint::StartSubpath);
721 // check if path was closed
722 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
723 // keep path closed
724 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
725 }
726 }
727 // check if we removed the last point
728 else if (pointIndex.second == subpath->size()) { // use size as point is already removed
729 // last point removed, set new StopSubpath
730 subpath->last()->setProperty(KoPathPoint::StopSubpath);
731 // check if path was closed
732 if (point->properties() & KoPathPoint::CloseSubpath) {
733 // keep path closed
734 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
735 }
736 }
737
739
740 return point;
741}
742
744{
745 KoSubpath *subpath = d->subPath(pointIndex.first);
746
747 if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
748 || isClosedSubpath(pointIndex.first))
749 return false;
750
751 KoSubpath * newSubpath = new KoSubpath;
752
753 int size = subpath->size();
754 for (int i = pointIndex.second + 1; i < size; ++i) {
755 newSubpath->append(subpath->takeAt(pointIndex.second + 1));
756 }
757 // now make the first point of the new subpath a starting node
758 newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
759 // the last point of the old subpath is now an ending node
760 subpath->last()->setProperty(KoPathPoint::StopSubpath);
761
762 // insert the new subpath after the broken one
763 d->subpaths.insert(pointIndex.first + 1, newSubpath);
765
766 return true;
767}
768
769bool KoPathShape::join(int subpathIndex)
770{
771 KoSubpath *subpath = d->subPath(subpathIndex);
772 KoSubpath *nextSubpath = d->subPath(subpathIndex + 1);
773
774 if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
775 || isClosedSubpath(subpathIndex+1))
776 return false;
777
778 // the last point of the subpath does not end the subpath anymore
779 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
780 // the first point of the next subpath does not start a subpath anymore
781 nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
782
783 // append the second subpath to the first
784 Q_FOREACH (KoPathPoint * p, *nextSubpath)
785 subpath->append(p);
786
787 // remove the nextSubpath from path
788 d->subpaths.removeAt(subpathIndex + 1);
789
790 // delete it as it is no longer possible to use it
791 delete nextSubpath;
792
794
795 return true;
796}
797
798bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
799{
800 KoSubpath *subpath = d->subPath(oldSubpathIndex);
801
802 if (subpath == 0 || newSubpathIndex >= d->subpaths.size())
803 return false;
804
805 if (oldSubpathIndex == newSubpathIndex)
806 return true;
807
808 d->subpaths.removeAt(oldSubpathIndex);
809 d->subpaths.insert(newSubpathIndex, subpath);
810
812
813 return true;
814}
815
817{
818 KoSubpath *subpath = d->subPath(pointIndex.first);
819
820 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
821 || !isClosedSubpath(pointIndex.first))
822 return KoPathPointIndex(-1, -1);
823
824 KoPathPoint * oldStartPoint = subpath->first();
825 // the old starting node no longer starts the subpath
827 // the old end node no longer closes the subpath
828 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
829
830 // reorder the subpath
831 for (int i = 0; i < pointIndex.second; ++i) {
832 subpath->append(subpath->takeFirst());
833 }
834 // make the first point a start node
835 subpath->first()->setProperty(KoPathPoint::StartSubpath);
836 // make the last point an end node
837 subpath->last()->setProperty(KoPathPoint::StopSubpath);
838
840
841 return pathPointIndex(oldStartPoint);
842}
843
845{
846 KoSubpath *subpath = d->subPath(pointIndex.first);
847
848 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
849 || isClosedSubpath(pointIndex.first))
850 return KoPathPointIndex(-1, -1);
851
852 KoPathPoint * oldStartPoint = subpath->first();
853 // the old starting node no longer starts the subpath
855 // the old end node no longer ends the subpath
856 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
857
858 // reorder the subpath
859 for (int i = 0; i < pointIndex.second; ++i) {
860 subpath->append(subpath->takeFirst());
861 }
862 subpath->first()->setProperty(KoPathPoint::StartSubpath);
863 subpath->last()->setProperty(KoPathPoint::StopSubpath);
864
865 closeSubpathPriv(subpath);
866
868
869 return pathPointIndex(oldStartPoint);
870}
871
872bool KoPathShape::reverseSubpath(int subpathIndex)
873{
874 KoSubpath *subpath = d->subPath(subpathIndex);
875
876 if (subpath == 0)
877 return false;
878
879 int size = subpath->size();
880 for (int i = 0; i < size; ++i) {
881 KoPathPoint *p = subpath->takeAt(i);
882 p->reverse();
883 subpath->prepend(p);
884 }
885
886 // adjust the position dependent properties
887 KoPathPoint *first = subpath->first();
888 KoPathPoint *last = subpath->last();
889
890 KoPathPoint::PointProperties firstProps = first->properties();
891 KoPathPoint::PointProperties lastProps = last->properties();
892
893 firstProps |= KoPathPoint::StartSubpath;
894 firstProps &= ~KoPathPoint::StopSubpath;
895 lastProps |= KoPathPoint::StopSubpath;
896 lastProps &= ~KoPathPoint::StartSubpath;
897 if (firstProps & KoPathPoint::CloseSubpath) {
898 firstProps |= KoPathPoint::CloseSubpath;
899 lastProps |= KoPathPoint::CloseSubpath;
900 }
901 first->setProperties(firstProps);
902 last->setProperties(lastProps);
903
905
906 return true;
907}
908
910{
911 KoSubpath *subpath = d->subPath(subpathIndex);
912
913 if (subpath != 0) {
914 Q_FOREACH (KoPathPoint* point, *subpath) {
915 point->setParent(this);
916 }
917 d->subpaths.removeAt(subpathIndex);
918 }
919
921
922 return subpath;
923}
924
925bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
926{
927 if (subpathIndex < 0 || subpathIndex > d->subpaths.size())
928 return false;
929
930 Q_FOREACH (KoPathPoint* point, *subpath) {
931 point->setParent(this);
932 }
933
934 d->subpaths.insert(subpathIndex, subpath);
936
937
938 return true;
939}
941{
942 int insertSegmentPosition = -1;
943 if (!path) return insertSegmentPosition;
944
945 QTransform pathMatrix = path->absoluteTransformation();
946 QTransform myMatrix = absoluteTransformation().inverted();
947
948 Q_FOREACH (KoSubpath* subpath, path->d->subpaths) {
949 KoSubpath *newSubpath = new KoSubpath();
950
951 Q_FOREACH (KoPathPoint* point, *subpath) {
952 KoPathPoint *newPoint = new KoPathPoint(*point, this);
953 newPoint->map(pathMatrix);
954 newPoint->map(myMatrix);
955 newSubpath->append(newPoint);
956 }
957 d->subpaths.append(newSubpath);
958
959 if (insertSegmentPosition < 0) {
960 insertSegmentPosition = d->subpaths.size() - 1;
961 }
962 }
963 normalize();
964
966 return insertSegmentPosition;
967}
968
970{
971 if (! d->subpaths.size())
972 return false;
973
974 QTransform myMatrix = absoluteTransformation();
975
976 Q_FOREACH (KoSubpath* subpath, d->subpaths) {
977 KoPathShape *shape = new KoPathShape();
978
979 shape->setStroke(stroke());
980 shape->setBackground(background());
981 shape->setShapeId(shapeId());
982 shape->setZIndex(zIndex());
983
984 KoSubpath *newSubpath = new KoSubpath();
985
986 Q_FOREACH (KoPathPoint* point, *subpath) {
987 KoPathPoint *newPoint = new KoPathPoint(*point, shape);
988 newPoint->map(myMatrix);
989 newSubpath->append(newPoint);
990 }
991 shape->d->subpaths.append(newSubpath);
992 shape->normalize();
993
994 // NOTE: shape cannot have any listeners yet, so no notification about
995 // points modification is needed
996
997 separatedPaths.append(shape);
998 }
999 return true;
1000}
1001
1003{
1004 if (! subpath)
1005 return;
1006
1007 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
1008 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
1009
1011}
1012
1014{
1015 if (! subpath || subpath->size() < 2)
1016 return;
1017
1018 KoPathPoint * lastPoint = subpath->last();
1019 KoPathPoint * firstPoint = subpath->first();
1020
1021 // check if first and last points are coincident
1022 if (lastPoint->point() == firstPoint->point()) {
1023 // we are removing the current last point and
1024 // reuse its first control point if active
1027 if (lastPoint->activeControlPoint1())
1028 firstPoint->setControlPoint1(lastPoint->controlPoint1());
1029 // remove last point
1030 delete subpath->takeLast();
1031 // the new last point closes the subpath now
1032 lastPoint = subpath->last();
1035
1037 } else {
1038 closeSubpathPriv(subpath);
1039 }
1040}
1041
1043{
1044 return d->subpaths;
1045}
1046
1048{
1049 return d->subpaths;
1050}
1051
1052void KoPathShape::map(const QTransform &matrix)
1053{
1054 return d->map(matrix);
1055}
1056
1058{
1059 if (subpathIndex < 0 || subpathIndex >= subpaths.size())
1060 return 0;
1061
1062 return subpaths.at(subpathIndex);
1063}
1064
1066{
1067 return KoPathShapeId;
1068}
1069
1070QString KoPathShape::toString(const QTransform &matrix) const
1071{
1072 QString pathString;
1073
1074 // iterate over all subpaths
1075 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
1076 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
1077 KoSubpath::const_iterator pointIt((*pathIt)->constBegin());
1078 // keep a pointer to the first point of the subpath
1079 KoPathPoint *firstPoint(*pointIt);
1080 // keep a pointer to the previous point of the subpath
1081 KoPathPoint *lastPoint = firstPoint;
1082 // keep track if the previous point has an active control point 2
1083 bool activeControlPoint2 = false;
1084
1085 // iterate over all points of the current subpath
1086 for (; pointIt != (*pathIt)->constEnd(); ++pointIt) {
1087 KoPathPoint *currPoint(*pointIt);
1088 if (!currPoint) {
1089 qWarning() << "Found a zero point in the shape's path!";
1090 continue;
1091 }
1092 // first point of subpath ?
1093 if (currPoint == firstPoint) {
1094 // are we starting a subpath ?
1095 if (currPoint->properties() & KoPathPoint::StartSubpath) {
1096 const QPointF p = matrix.map(currPoint->point());
1097 pathString += QString("M%1 %2").arg(p.x()).arg(p.y());
1098 }
1099 }
1100 // end point of curve segment ?
1101 else if (activeControlPoint2 || currPoint->activeControlPoint1()) {
1102 // check if we have a cubic or quadratic curve
1103 const bool isCubic = activeControlPoint2 && currPoint->activeControlPoint1();
1104 KoPathSegment cubicSeg = isCubic ? KoPathSegment(lastPoint, currPoint)
1105 : KoPathSegment(lastPoint, currPoint).toCubic();
1106 if (cubicSeg.first() && cubicSeg.second()) {
1107 const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1108 const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1109 const QPointF p = matrix.map(cubicSeg.second()->point());
1110 pathString += QString("C%1 %2 %3 %4 %5 %6")
1111 .arg(cp1.x()).arg(cp1.y())
1112 .arg(cp2.x()).arg(cp2.y())
1113 .arg(p.x()).arg(p.y());
1114 }
1115 }
1116 // end point of line segment!
1117 else {
1118 const QPointF p = matrix.map(currPoint->point());
1119 pathString += QString("L%1 %2").arg(p.x()).arg(p.y());
1120 }
1121 // last point closes subpath ?
1122 if (currPoint->properties() & KoPathPoint::StopSubpath
1123 && currPoint->properties() & KoPathPoint::CloseSubpath) {
1124 // add curve when there is a curve on the way to the first point
1125 if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
1126 // check if we have a cubic or quadratic curve
1127 const bool isCubic = currPoint->activeControlPoint2() && firstPoint->activeControlPoint1();
1128 KoPathSegment cubicSeg = isCubic ? KoPathSegment(currPoint, firstPoint)
1129 : KoPathSegment(currPoint, firstPoint).toCubic();
1130 if (cubicSeg.first() && cubicSeg.second()) {
1131 const QPointF cp1 = matrix.map(cubicSeg.first()->controlPoint2());
1132 const QPointF cp2 = matrix.map(cubicSeg.second()->controlPoint1());
1133
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 pathString += QString("Z");
1142 }
1143
1144 activeControlPoint2 = currPoint->activeControlPoint2();
1145 lastPoint = currPoint;
1146 }
1147 }
1148
1149 return pathString;
1150}
1151
1152char nodeType(const KoPathPoint * point)
1153{
1154 if (point->properties() & KoPathPoint::IsSmooth) {
1155 return 's';
1156 }
1157 else if (point->properties() & KoPathPoint::IsSymmetric) {
1158 return 'z';
1159 }
1160 else {
1161 return 'c';
1162 }
1163}
1164
1166{
1167 QString types;
1168 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
1169 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
1170 KoSubpath::const_iterator it((*pathIt)->constBegin());
1171 for (; it != (*pathIt)->constEnd(); ++it) {
1172 if (it == (*pathIt)->constBegin()) {
1173 types.append('c');
1174 }
1175 else {
1176 types.append(nodeType(*it));
1177 }
1178
1179 if ((*it)->properties() & KoPathPoint::StopSubpath
1180 && (*it)->properties() & KoPathPoint::CloseSubpath) {
1181 KoPathPoint * firstPoint = (*pathIt)->first();
1182 types.append(nodeType(firstPoint));
1183 }
1184 }
1185 }
1186 return types;
1187}
1188
1189void updateNodeType(KoPathPoint * point, const QChar & nodeType)
1190{
1191 if (nodeType == 's') {
1193 }
1194 else if (nodeType == 'z') {
1196 }
1197}
1198
1200{
1201 QString::const_iterator nIt(nodeTypes.constBegin());
1202 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
1203 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
1204 KoSubpath::const_iterator it((*pathIt)->constBegin());
1205 for (; it != (*pathIt)->constEnd(); ++it, nIt++) {
1206 // be sure not to crash if there are not enough nodes in nodeTypes
1207 if (nIt == nodeTypes.constEnd()) {
1208 warnFlake << "not enough nodes in sodipodi:nodetypes";
1209 return;
1210 }
1211 // the first node is always of type 'c'
1212 if (it != (*pathIt)->constBegin()) {
1213 updateNodeType(*it, *nIt);
1214 }
1215
1216 if ((*it)->properties() & KoPathPoint::StopSubpath
1217 && (*it)->properties() & KoPathPoint::CloseSubpath) {
1218 ++nIt;
1219 updateNodeType((*pathIt)->first(), *nIt);
1220 }
1221 }
1222 }
1223}
1224
1225Qt::FillRule KoPathShape::fillRule() const
1226{
1227 return d->fillRule;
1228}
1229
1231{
1232 d->fillRule = fillRule;
1233}
1234
1236{
1237 KoPathShape * shape = new KoPathShape();
1238
1239 int elementCount = path.elementCount();
1240 for (int i = 0; i < elementCount; i++) {
1241 QPainterPath::Element element = path.elementAt(i);
1242 switch (element.type) {
1243 case QPainterPath::MoveToElement:
1244 shape->moveTo(QPointF(element.x, element.y));
1245 break;
1246 case QPainterPath::LineToElement:
1247 shape->lineTo(QPointF(element.x, element.y));
1248 break;
1249 case QPainterPath::CurveToElement:
1250 shape->curveTo(QPointF(element.x, element.y),
1251 QPointF(path.elementAt(i + 1).x, path.elementAt(i + 1).y),
1252 QPointF(path.elementAt(i + 2).x, path.elementAt(i + 2).y));
1253 break;
1254 default:
1255 continue;
1256 }
1257 }
1258
1259 shape->setShapeId(KoPathShapeId);
1260
1261 //shape->normalize();
1262 return shape;
1263}
1264
1265bool KoPathShape::hitTest(const QPointF &position) const
1266{
1267 if (parent() && parent()->isClipped(this) && ! parent()->hitTest(position))
1268 return false;
1269
1270 QPointF point = absoluteTransformation().inverted().map(position);
1271 const QPainterPath outlinePath = outline();
1272 if (stroke()) {
1273 KoInsets insets;
1274 stroke()->strokeInsets(this, insets);
1275 QRectF roi(QPointF(-insets.left, -insets.top), QPointF(insets.right, insets.bottom));
1276
1277 roi.moveCenter(point);
1278 if (outlinePath.intersects(roi) || outlinePath.contains(roi))
1279 return true;
1280 } else {
1281 if (outlinePath.contains(point))
1282 return true;
1283 }
1284
1285 return false;
1286}
1287
1289{
1290 if (!marker && d->markersNew.contains(pos)) {
1291 d->markersNew.remove(pos);
1292 } else {
1293 d->markersNew[pos] = marker;
1294 }
1295
1296 notifyChanged();
1298}
1299
1301{
1302 return d->markersNew[pos].data();
1303}
1304
1306{
1307 return !d->markersNew.isEmpty();
1308}
1309
1311{
1312 return d->autoFillMarkers;
1313}
1314
1316{
1317 d->autoFillMarkers = value;
1318}
1319
1320KoPathSegment KoPathShape::segmentAtPoint(const QPointF &point, const QRectF &grabRoi) const
1321{
1322 const qreal distanceThreshold = 0.5 * KisAlgebra2D::maxDimension(grabRoi);
1323 KoPathSegment segment;
1324
1325 // convert document point to shape coordinates
1326 const QPointF p = documentToShape(point);
1327 // our region of interest, i.e. a region around our mouse position
1328 const QRectF roi = documentToShape(grabRoi);
1329
1330 qreal minDistance = std::numeric_limits<qreal>::max();
1331
1332 // check all segments of this shape which intersect the region of interest
1333 const QList<KoPathSegment> segments = segmentsAt(roi);
1334
1335 foreach (KoPathSegment s, segments) {
1336 const qreal nearestPointParam = s.nearestPoint(p);
1337 const QPointF nearestPoint = s.pointAt(nearestPointParam);
1338 const qreal distance = kisDistance(p, nearestPoint);
1339
1340 // are we within the allowed distance ?
1341 if (distance > distanceThreshold)
1342 continue;
1343 // are we closer to the last closest point ?
1344 if (distance < minDistance) {
1345 segment = s;
1346 }
1347 }
1348
1349 return segment;
1350}
1351
1353{
1354 Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) {
1355 PointSelectionChangeListener *pointListener = dynamic_cast<PointSelectionChangeListener*>(listener);
1356 if (pointListener) {
1357 pointListener->recommendPointSelectionChange(this, newSelection);
1358 }
1359 }
1360}
1361
1363{
1364 Q_FOREACH (KoShape::ShapeChangeListener *listener, listeners()) {
1365 PointSelectionChangeListener *pointListener = dynamic_cast<PointSelectionChangeListener*>(listener);
1366 if (pointListener) {
1367 pointListener->notifyPathPointsChanged(this);
1368 }
1369 }
1370}
1371
1372QPainterPath KoPathShape::pathStroke(const QPen &pen) const
1373{
1374 if (d->subpaths.isEmpty()) {
1375 return QPainterPath();
1376 }
1377 QPainterPath pathOutline;
1378
1379 QPainterPathStroker stroker;
1380 stroker.setWidth(0);
1381 stroker.setJoinStyle(Qt::MiterJoin);
1382 stroker.setWidth(pen.widthF());
1383 stroker.setJoinStyle(pen.joinStyle());
1384 stroker.setMiterLimit(pen.miterLimit());
1385 stroker.setCapStyle(pen.capStyle());
1386 stroker.setDashOffset(pen.dashOffset());
1387 stroker.setDashPattern(pen.dashPattern());
1388
1389 QPainterPath path = stroker.createStroke(outline());
1390
1391 pathOutline.addPath(path);
1392 pathOutline.setFillRule(Qt::WindingFill);
1393
1394 return pathOutline;
1395}
1396
1398{
1399 Q_UNUSED(type);
1400 Q_UNUSED(shape);
1401}
#define warnFlake
Definition FlakeDebug.h:16
#define debugFlake
Definition FlakeDebug.h:15
float value(const T *src, size_t ch)
const Params2D p
quint64 part(quint64 n1, quint64 n2, int p)
qreal distance(const QPointF &p1, const QPointF &p2)
static bool qIsNaNPoint(const QPointF &p)
static bool qIsNaNPoint(const QPointF &p)
char nodeType(const KoPathPoint *point)
void updateNodeType(KoPathPoint *point, const QChar &nodeType)
QList< KoPathPoint * > KoSubpath
a KoSubpath contains a path from a moveTo until a close or a new moveTo
Definition KoPathShape.h:31
#define KoPathShapeId
Definition KoPathShape.h:20
QPair< int, int > KoPathPointIndex
Definition KoPathShape.h:28
The KisHandlePainterHelper class is a special helper for painting handles around objects....
A KoPathPoint represents a point in a path.
PointProperties properties
void setProperties(PointProperties properties)
Set the properties of a point.
void setControlPoint1(const QPointF &point)
Set the control point 1.
QPointF point
void setProperty(PointProperty property)
Sets a single property of a point.
void setControlPoint2(const QPointF &point)
Set the control point 2.
QPointF controlPoint1
void map(const QTransform &matrix)
apply matrix on the point
@ IsSmooth
it is smooth, both control points on a line through the point
Definition KoPathPoint.h:41
@ StartSubpath
it starts a new subpath by a moveTo command
Definition KoPathPoint.h:38
@ Normal
it has no control points
Definition KoPathPoint.h:37
@ IsSymmetric
it is symmetric, like smooth but control points have same distance to point
Definition KoPathPoint.h:42
@ CloseSubpath
it closes a subpath (only applicable on StartSubpath and StopSubpath)
Definition KoPathPoint.h:40
@ StopSubpath
it stops a subpath (last point of subpath)
Definition KoPathPoint.h:39
@ Node
the node point
Definition KoPathPoint.h:49
void unsetProperty(PointProperty property)
Removes a property from the point.
bool activeControlPoint1
bool activeControlPoint2
void setParent(KoPathShape *parent)
Sets the parent path shape.
QPointF controlPoint2
A KoPathSegment consist of two neighboring KoPathPoints.
KoPathPoint * first
KoPathPoint * second
KoPathSegment toCubic() const
Returns cubic bezier curve segment of this segment.
KoSubpathList subpaths
void debugPath() const
print debug information about a the points of the path
void paintDebug(QPainter &painter)
void map(const QTransform &matrix)
QRectF handleRect(const QPointF &p, qreal radius) const
KoSubpath * subPath(int subpathIndex) const
Returns subpath at given index.
The position of a path point within a path shape.
Definition KoPathShape.h:63
const KoSubpathList & subpaths() const
bool autoFillMarkers() const
QTransform resizeMatrix(const QSizeF &newSize) const
bool breakAfter(const KoPathPointIndex &pointIndex)
Breaks the path after the point index.
int subpathPointCount(int subpathIndex) const
Returns the number of points in a subpath.
bool isClosedSubpath(int subpathIndex) const
Checks if a subpath is closed.
void map(const QTransform &matrix)
virtual QString pathShapeId() const
KoMarker * marker(KoFlake::MarkerPosition pos) const
bool reverseSubpath(int subpathIndex)
Reverse subpath.
KoPathSegment segmentAtPoint(const QPointF &point, const QRectF &grabRoi) const
KoPathPoint * lineTo(const QPointF &p)
Adds a new line segment.
bool hitTest(const QPointF &position) const override
reimplemented
void setAutoFillMarkers(bool value)
void recommendPointSelectionChange(const QList< KoPathPointIndex > &newSelection)
KoSubpath * removeSubpath(int subpathIndex)
Removes subpath from the path.
virtual QPointF normalize()
Normalizes the path data.
QSizeF size() const override
reimplemented
void close()
Closes the current subpath.
void notifyPointsChanged()
bool addSubpath(KoSubpath *subpath, int subpathIndex)
Adds a subpath at the given index to the path.
void setSize(const QSizeF &size) override
QRectF outlineRect() const override
reimplemented
QPainterPath pathStroke(const QPen &pen) const
Qt::FillRule fillRule() const
Returns the fill rule for the path object.
void closeMerge()
Closes the current subpath.
void setFillRule(Qt::FillRule fillRule)
Sets the fill rule to be used for painting the background.
KoPathPoint * moveTo(const QPointF &p)
Starts a new Subpath.
int arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF &offset, QPointF *curvePoints) const
Add an arc.
int pointCount() const
Returns the number of points in the path.
bool moveSubpath(int oldSubpathIndex, int newSubpathIndex)
Moves the position of a subpath within a path.
void closeSubpathPriv(KoSubpath *subpath)
closes specified subpath
bool join(int subpathIndex)
Joins the given subpath with the following one.
QList< KoPathPoint * > pointsAt(const QRectF &rect, const bool useControlPoints=false) const
Returns the path points within the given rectangle.
KoPathShape()
constructor
~KoPathShape() override
void setMarker(KoMarker *marker, KoFlake::MarkerPosition pos)
bool separate(QList< KoPathShape * > &separatedPaths)
Creates separate path shapes, one for each existing subpath.
void updateLastPriv(KoPathPoint **lastPoint)
QList< KoPathSegment > segmentsAt(const QRectF &rect) const
Returns the list of path segments within the given rectangle.
void paint(QPainter &painter) const override
reimplemented
KoShape * cloneShape() const override
creates a deep copy of the shape or shape's subtree
KoPathPointIndex closeSubpath(const KoPathPointIndex &pointIndex)
Close a open subpath.
KoPathPoint * arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
Add an arc.
KoPathPointIndex openSubpath(const KoPathPointIndex &pointIndex)
Opens a closed subpath.
QString toString(const QTransform &matrix=QTransform()) const
Returns a odf/svg string representation of the path data with the given matrix applied.
QPainterPath outline() const override
reimplemented
KoPathPoint * curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
Adds a new cubic Bezier curve segment.
QString nodeTypes() const
Saves the node types.
QRectF boundingRect() const override
reimplemented
KoPathPoint * removePoint(const KoPathPointIndex &pointIndex)
Removes a point from the path.
int subpathCount() const
Returns the number of subpaths in the path.
static KoPathShape * createShapeFromPainterPath(const QPainterPath &path)
Creates path shape from given QPainterPath.
KoPathSegment segmentByIndex(const KoPathPointIndex &pointIndex) const
Returns the segment specified by a path point index.
virtual void paintPoints(KisHandlePainterHelper &handlesHelper)
KoPathPointIndex pathPointIndex(const KoPathPoint *point) const
Returns the path point index of a given path point.
bool hasMarkers() const
void loadNodeTypes(const QString &nodeTypes)
Loads node types.
KoPathPoint * pointByIndex(const KoPathPointIndex &pointIndex) const
Returns the path point specified by a path point index.
QScopedPointer< Private > d
void closeMergeSubpathPriv(KoSubpath *subpath)
close-merges specified subpath
void clear()
Removes all subpaths and their points from the path.
bool insertPoint(KoPathPoint *point, const KoPathPointIndex &pointIndex)
Inserts a new point into the given subpath at the specified position.
int combine(KoPathShape *path)
Combines two path shapes by appending the data of the specified path.
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:787
QString shapeId() const
Definition KoShape.cpp:880
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:890
QPointF documentToShape(const QPointF &point) const
Transforms point from document coordinates to shape coordinates.
Definition KoShape.cpp:1016
QList< ShapeChangeListener * > listeners() const
Definition KoShape.cpp:1173
void applyTransformation(const QTransform &matrix)
Definition KoShape.cpp:363
KoShapeContainer * parent() const
Definition KoShape.cpp:862
void shapeChangedPriv(KoShape::ChangeType type)
Definition KoShape.cpp:104
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:904
QTransform absoluteTransformation() const
Definition KoShape.cpp:335
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:751
ChangeType
Used by shapeChanged() to select which change was made.
Definition KoShape.h:92
@ StrokeChanged
the shapes stroke has changed
Definition KoShape.h:102
@ ContentChanged
the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
Definition KoShape.h:106
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:759
QSharedDataPointer< SharedData > s
Definition KoShape.h:977
void notifyChanged()
Definition KoShape.cpp:618
void setShapeId(const QString &id)
Definition KoShape.cpp:885
qint16 zIndex() const
Definition KoShape.cpp:529
QTransform transform() const
return the current matrix that contains the rotation/scale/position of this shape
Definition KoShape.cpp:950
virtual void setSize(const QSizeF &size)
Resize the shape.
Definition KoShape.cpp:248
QPointF position() const
Get the position of the shape in pt.
Definition KoShape.cpp:745
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
#define M_PI
Definition kis_global.h:111
auto maxDimension(Size size) -> decltype(size.width())
MarkerPosition
Definition KoFlake.h:41
qreal bottom
Bottom inset.
Definition KoInsets.h:50
qreal right
Right inset.
Definition KoInsets.h:52
qreal top
Top inset.
Definition KoInsets.h:49
qreal left
Left inset.
Definition KoInsets.h:51
virtual void recommendPointSelectionChange(KoPathShape *shape, const QList< KoPathPointIndex > &newSelection)=0
virtual void notifyPathPointsChanged(KoPathShape *shape)=0
void notifyShapeChanged(ChangeType type, KoShape *shape) override