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 "KoShapeShadow.h"
20#include "KoShapeBackground.h"
21#include "KoShapeContainer.h"
22#include "KoFilterEffectStack.h"
23#include "KoMarker.h"
24#include "KoShapeStroke.h"
25#include "KoInsets.h"
26
27#include <KoUnit.h>
29
30#include <FlakeDebug.h>
31#include <QPainter>
32#include <QPainterPath>
33
34#include "kis_global.h"
35
36#include <qnumeric.h> // for qIsNaN
37static bool qIsNaNPoint(const QPointF &p) {
38 return qIsNaN(p.x()) || qIsNaN(p.y());
39}
40
42 : fillRule(Qt::OddEvenFill)
43 , autoFillMarkers(false)
44{
45}
46
48 : fillRule(rhs.fillRule)
49 , markersNew(rhs.markersNew)
50 , autoFillMarkers(rhs.autoFillMarkers)
51{
52}
53
54QRectF KoPathShape::Private::handleRect(const QPointF &p, qreal radius) const
55{
56 return QRectF(p.x() - radius, p.y() - radius, 2*radius, 2*radius);
57}
58
59
65
67 : KoTosContainer(rhs)
68 , d(new Private(*rhs.d))
69{
70 // local data cannot be shared via QSharedData because
71 // every path point holds a pointer to the parent shape
73 Q_FOREACH (KoSubpath *subPath, rhs.d->subpaths) {
74 KoSubpath *clonedSubPath = new KoSubpath();
75
76 Q_FOREACH (KoPathPoint *point, *subPath) {
77 *clonedSubPath << new KoPathPoint(*point, this);
78 }
79
80 subpaths << clonedSubPath;
81 }
82 d->subpaths = subpaths;
83}
84
89
91{
92 return new KoPathShape(*this);
93}
94
96{
97 Q_FOREACH (KoSubpath *subpath, d->subpaths) {
98 Q_FOREACH (KoPathPoint *point, *subpath)
99 delete point;
100 delete subpath;
101 }
102 d->subpaths.clear();
103
105}
106
107void KoPathShape::paint(QPainter &painter) const
108{
109 KisQPainterStateSaver saver(&painter);
110 Q_UNUSED(saver);
111
112 QPainterPath path(outline());
113 path.setFillRule(d->fillRule);
114
115 if (background()) {
116 background()->paint(painter, path);
117 }
118 //d->paintDebug(painter);
119}
120
121
122#ifndef NDEBUG
123void KoPathShape::Private::paintDebug(QPainter &painter)
124{
125 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
126 int i = 0;
127
128 QPen pen(Qt::black, 0);
129 painter.save();
130 painter.setPen(pen);
131 for (; pathIt != subpaths.constEnd(); ++pathIt) {
132 KoSubpath::const_iterator it((*pathIt)->constBegin());
133 for (; it != (*pathIt)->constEnd(); ++it) {
134 ++i;
135 KoPathPoint *point = (*it);
136 QRectF r(point->point(), QSizeF(5, 5));
137 r.translate(-2.5, -2.5);
138 QPen pen(Qt::black, 0);
139 painter.setPen(pen);
140 if (point->activeControlPoint1() && point->activeControlPoint2()) {
141 QBrush b(Qt::red);
142 painter.setBrush(b);
143 } else if (point->activeControlPoint1()) {
144 QBrush b(Qt::yellow);
145 painter.setBrush(b);
146 } else if (point->activeControlPoint2()) {
147 QBrush b(Qt::darkYellow);
148 painter.setBrush(b);
149 }
150 painter.drawEllipse(r);
151 }
152 }
153 painter.restore();
154 debugFlake << "nop =" << i;
155}
156
158{
159 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
160 for (; pathIt != subpaths.constEnd(); ++pathIt) {
161 KoSubpath::const_iterator it((*pathIt)->constBegin());
162 for (; it != (*pathIt)->constEnd(); ++it) {
163 debugFlake << "p:" << (*pathIt) << "," << *it << "," << (*it)->point() << "," << (*it)->properties();
164 }
165 }
166}
167#endif
168
170{
171 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
172
173 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
174 KoSubpath::const_iterator it((*pathIt)->constBegin());
175 for (; it != (*pathIt)->constEnd(); ++it)
176 (*it)->paint(handlesHelper, KoPathPoint::Node);
177 }
178}
179
181{
182 return outline().boundingRect();
183}
184
185QPainterPath KoPathShape::outline() const
186{
187 QPainterPath path;
188 for (auto subpathIt = d->subpaths.constBegin(); subpathIt != d->subpaths.constEnd(); ++subpathIt) {
189 const KoSubpath * subpath = *subpathIt;
190 const KoPathPoint * lastPoint = subpath->constFirst();
191 bool activeCP = false;
192 for (auto pointIt = subpath->constBegin(); pointIt != subpath->constEnd(); ++pointIt) {
193 const KoPathPoint * currPoint = *pointIt;
194 KoPathPoint::PointProperties currProperties = currPoint->properties();
195 if (currPoint == subpath->constFirst()) {
196 if (currProperties & KoPathPoint::StartSubpath) {
197 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
198 path.moveTo(currPoint->point());
199 }
200 } else if (activeCP && currPoint->activeControlPoint1()) {
201 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
202 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
203 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
204 path.cubicTo(
205 lastPoint->controlPoint2(),
206 currPoint->controlPoint1(),
207 currPoint->point());
208 } else if (activeCP || currPoint->activeControlPoint1()) {
209 Q_ASSERT(!qIsNaNPoint(lastPoint->controlPoint2()));
210 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
211 path.quadTo(
212 activeCP ? lastPoint->controlPoint2() : currPoint->controlPoint1(),
213 currPoint->point());
214 } else {
215 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
216 path.lineTo(currPoint->point());
217 }
218 if (currProperties & KoPathPoint::CloseSubpath && currProperties & KoPathPoint::StopSubpath) {
219 // add curve when there is a curve on the way to the first point
220 KoPathPoint * firstPoint = subpath->first();
221 Q_ASSERT(!qIsNaNPoint(firstPoint->point()));
222 if (currPoint->activeControlPoint2() && firstPoint->activeControlPoint1()) {
223 path.cubicTo(
224 currPoint->controlPoint2(),
225 firstPoint->controlPoint1(),
226 firstPoint->point());
227 }
228 else if (currPoint->activeControlPoint2() || firstPoint->activeControlPoint1()) {
229 Q_ASSERT(!qIsNaNPoint(currPoint->point()));
230 Q_ASSERT(!qIsNaNPoint(currPoint->controlPoint1()));
231 path.quadTo(
232 currPoint->activeControlPoint2() ? currPoint->controlPoint2() : firstPoint->controlPoint1(),
233 firstPoint->point());
234 }
235 path.closeSubpath();
236 }
237
238 if (currPoint->activeControlPoint2()) {
239 activeCP = true;
240 } else {
241 activeCP = false;
242 }
243 lastPoint = currPoint;
244 }
245 }
246
247 return path;
248}
249
251{
252 const QTransform transform = absoluteTransformation();
253
259 qreal outlineSweepWidth = 0;
260
261 const QSharedPointer<KoShapeStroke> lineBorder = qSharedPointerDynamicCast<KoShapeStroke>(stroke());
262 if (lineBorder) {
263 outlineSweepWidth = lineBorder->lineWidth();
264 }
265
266 if (stroke()) {
267 KoInsets inset;
268 stroke()->strokeInsets(this, inset);
269 const qreal maxInset = std::max({inset.left, inset.top, inset.right, inset.bottom});
270
271 // insets extend outside the shape, but width extends both inside and outside,
272 // so we should multiply insets by 2.0
273 outlineSweepWidth = std::max({outlineSweepWidth,
274 2.0 * maxInset,
275 2.0 * stroke()->strokeMaxMarkersInset(this)});
276 }
277
278
279
282
283#if 0
284 QPen pen(Qt::black, outlineSweepWidth);
285
286 // select round joins and caps to ensure it sweeps exactly
287 // 'outlineSweepWidth' pixels in every possible
288 pen.setJoinStyle(Qt::RoundJoin);
289 pen.setCapStyle(Qt::RoundCap);
290 QRectF bb = transform.map(pathStroke(pen)).boundingRect();
291#endif
292
293 // add 10% extra update area around the doubled insets
294 QRectF bb = transform.mapRect(kisGrowRect(outline().boundingRect(), 1.1 * 0.5 * outlineSweepWidth));
295
296 if (shadow()) {
297 KoInsets insets;
298 shadow()->insets(insets);
299 bb.adjust(-insets.left, -insets.top, insets.right, insets.bottom);
300 }
301 if (filterEffectStack()) {
302 QRectF clipRect = filterEffectStack()->clipRectForBoundingRect(QRectF(QPointF(), size()));
303 bb |= transform.mapRect(clipRect);
304 }
305 return bb;
306}
307
308QSizeF KoPathShape::size() const
309{
310 // don't call boundingRect here as it uses absoluteTransformation
311 // which itself uses size() -> leads to infinite recursion
312 return outlineRect().size();
313}
314
315void KoPathShape::setSize(const QSizeF &newSize)
316{
317 QTransform matrix(resizeMatrix(newSize));
318
319 KoShape::setSize(newSize);
320 d->map(matrix);
321}
322
323QTransform KoPathShape::resizeMatrix(const QSizeF & newSize) const
324{
325 QSizeF oldSize = size();
326 if (oldSize.width() == 0.0) {
327 oldSize.setWidth(0.000001);
328 }
329 if (oldSize.height() == 0.0) {
330 oldSize.setHeight(0.000001);
331 }
332
333 QSizeF sizeNew(newSize);
334 if (sizeNew.width() == 0.0) {
335 sizeNew.setWidth(0.000001);
336 }
337 if (sizeNew.height() == 0.0) {
338 sizeNew.setHeight(0.000001);
339 }
340
341 return QTransform(sizeNew.width() / oldSize.width(), 0, 0, sizeNew.height() / oldSize.height(), 0, 0);
342}
343
345{
347 KoSubpath * path = new KoSubpath;
348 path->push_back(point);
349 d->subpaths.push_back(path);
351 return point;
352}
353
355{
356 if (d->subpaths.empty()) {
357 moveTo(QPointF(0, 0));
358 }
360 KoPathPoint * lastPoint = d->subpaths.last()->last();
361 updateLastPriv(&lastPoint);
362 d->subpaths.last()->push_back(point);
364 return point;
365}
366
367KoPathPoint * KoPathShape::curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
368{
369 if (d->subpaths.empty()) {
370 moveTo(QPointF(0, 0));
371 }
372 KoPathPoint * lastPoint = d->subpaths.last()->last();
373 updateLastPriv(&lastPoint);
374 lastPoint->setControlPoint2(c1);
376 point->setControlPoint1(c2);
377 d->subpaths.last()->push_back(point);
379 return point;
380}
381
382KoPathPoint * KoPathShape::curveTo(const QPointF &c, const QPointF &p)
383{
384 if (d->subpaths.empty())
385 moveTo(QPointF(0, 0));
386
387 KoPathPoint * lastPoint = d->subpaths.last()->last();
388 updateLastPriv(&lastPoint);
389 lastPoint->setControlPoint2(c);
391 d->subpaths.last()->push_back(point);
393 return point;
394}
395
396KoPathPoint * KoPathShape::arcTo(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle)
397{
398 if (d->subpaths.empty()) {
399 moveTo(QPointF(0, 0));
400 }
401
402 KoPathPoint * lastPoint = d->subpaths.last()->last();
403 if (lastPoint->properties() & KoPathPoint::CloseSubpath) {
404 lastPoint = d->subpaths.last()->first();
405 }
406 QPointF startpoint(lastPoint->point());
407
408 KoPathPoint * newEndPoint = lastPoint;
409
410 QPointF curvePoints[12];
411 int pointCnt = arcToCurve(rx, ry, startAngle, sweepAngle, startpoint, curvePoints);
412 for (int i = 0; i < pointCnt; i += 3) {
413 newEndPoint = curveTo(curvePoints[i], curvePoints[i+1], curvePoints[i+2]);
414 }
415 return newEndPoint;
416}
417
418int KoPathShape::arcToCurve(qreal rx, qreal ry, qreal startAngle, qreal sweepAngle, const QPointF & offset, QPointF * curvePoints) const
419{
420 int pointCnt = 0;
421
422 // check Parameters
423 if (sweepAngle == 0.0)
424 return pointCnt;
425
426 sweepAngle = qBound(-360.0, sweepAngle, 360.0);
427
428 if (rx == 0 || ry == 0) {
429 //TODO
430 }
431
432 // split angles bigger than 90° so that it gives a good approximation to the circle
433 qreal parts = ceil(qAbs(sweepAngle / 90.0));
434
435 qreal sa_rad = startAngle * M_PI / 180.0;
436 qreal partangle = sweepAngle / parts;
437 qreal endangle = startAngle + partangle;
438 qreal se_rad = endangle * M_PI / 180.0;
439 qreal sinsa = sin(sa_rad);
440 qreal cossa = cos(sa_rad);
441 qreal kappa = 4.0 / 3.0 * tan((se_rad - sa_rad) / 4);
442
443 // startpoint is at the last point is the path but when it is closed
444 // it is at the first point
445 QPointF startpoint(offset);
446
447 //center berechnen
448 QPointF center(startpoint - QPointF(cossa * rx, -sinsa * ry));
449
450 //debugFlake <<"kappa" << kappa <<"parts" << parts;
451
452 for (int part = 0; part < parts; ++part) {
453 // start tangent
454 curvePoints[pointCnt++] = QPointF(startpoint - QPointF(sinsa * rx * kappa, cossa * ry * kappa));
455
456 qreal sinse = sin(se_rad);
457 qreal cosse = cos(se_rad);
458
459 // end point
460 QPointF endpoint(center + QPointF(cosse * rx, -sinse * ry));
461 // end tangent
462 curvePoints[pointCnt++] = QPointF(endpoint - QPointF(-sinse * rx * kappa, -cosse * ry * kappa));
463 curvePoints[pointCnt++] = endpoint;
464
465 // set the endpoint as next start point
466 startpoint = endpoint;
467 sinsa = sinse;
468 cossa = cosse;
469 endangle += partangle;
470 se_rad = endangle * M_PI / 180.0;
471 }
472
473 return pointCnt;
474}
475
477{
478 if (d->subpaths.empty()) {
479 return;
480 }
481 closeSubpathPriv(d->subpaths.last());
482}
483
485{
486 if (d->subpaths.empty()) {
487 return;
488 }
489 closeMergeSubpathPriv(d->subpaths.last());
490}
491
493{
494 QPointF tl(outline().boundingRect().topLeft());
495 QTransform matrix;
496 matrix.translate(-tl.x(), -tl.y());
497 d->map(matrix);
498
499 // keep the top left point of the object
500 applyTransformation(matrix.inverted());
502 return tl;
503}
504
505void KoPathShape::Private::map(const QTransform &matrix)
506{
507 KoSubpathList::const_iterator pathIt(subpaths.constBegin());
508 for (; pathIt != subpaths.constEnd(); ++pathIt) {
509 KoSubpath::const_iterator it((*pathIt)->constBegin());
510 for (; it != (*pathIt)->constEnd(); ++it) {
511 // It's possible there are null points in the map...
512 if (*it) {
513 (*it)->map(matrix);
514 }
515 }
516 }
517}
518
520{
521 // check if we are about to add a new point to a closed subpath
522 if ((*lastPoint)->properties() & KoPathPoint::StopSubpath
523 && (*lastPoint)->properties() & KoPathPoint::CloseSubpath) {
524 // get the first point of the subpath
525 KoPathPoint *subpathStart = d->subpaths.last()->first();
526 // clone the first point of the subpath...
527 KoPathPoint * newLastPoint = new KoPathPoint(*subpathStart, this);
528 // ... and make it a normal point
529 newLastPoint->setProperties(KoPathPoint::Normal);
530 // now start a new subpath with the cloned start point
531 KoSubpath *path = new KoSubpath;
532 path->push_back(newLastPoint);
533 d->subpaths.push_back(path);
534 *lastPoint = newLastPoint;
535 } else {
536 // the subpath was not closed so the formerly last point
537 // of the subpath is no end point anymore
539 }
540 (*lastPoint)->unsetProperty(KoPathPoint::CloseSubpath);
541}
542
543QList<KoPathPoint*> KoPathShape::pointsAt(const QRectF &r, const bool useControlPoints) const
544{
545 QList<KoPathPoint*> result;
546
547 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
548 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
549 KoSubpath::const_iterator it((*pathIt)->constBegin());
550 for (; it != (*pathIt)->constEnd(); ++it) {
551 if (r.contains((*it)->point()))
552 result.append(*it);
553 else if (useControlPoints) {
554 if ((*it)->activeControlPoint1() && r.contains((*it)->controlPoint1()))
555 result.append(*it);
556 else if ((*it)->activeControlPoint2() && r.contains((*it)->controlPoint2()))
557 result.append(*it);
558 }
559 }
560 }
561 return result;
562}
563
565{
566 QList<KoPathSegment> segments;
567 int subpathCount = d->subpaths.count();
568 for (int subpathIndex = 0; subpathIndex < subpathCount; ++subpathIndex) {
569 KoSubpath * subpath = d->subpaths[subpathIndex];
570 int pointCount = subpath->count();
571 bool subpathClosed = isClosedSubpath(subpathIndex);
572 for (int pointIndex = 0; pointIndex < pointCount; ++pointIndex) {
573 if (pointIndex == (pointCount - 1) && ! subpathClosed)
574 break;
575 KoPathSegment s(subpath->at(pointIndex), subpath->at((pointIndex + 1) % pointCount));
576 QRectF controlRect = s.controlPointRect();
577 if (! r.intersects(controlRect) && ! controlRect.contains(r))
578 continue;
579 QRectF bound = s.boundingRect();
580 if (! r.intersects(bound) && ! bound.contains(r))
581 continue;
582
583 segments.append(s);
584 }
585 }
586 return segments;
587}
588
590{
591 for (int subpathIndex = 0; subpathIndex < d->subpaths.size(); ++subpathIndex) {
592 KoSubpath * subpath = d->subpaths.at(subpathIndex);
593 for (int pointPos = 0; pointPos < subpath->size(); ++pointPos) {
594 if (subpath->at(pointPos) == point) {
595 return KoPathPointIndex(subpathIndex, pointPos);
596 }
597 }
598 }
599 return KoPathPointIndex(-1, -1);
600}
601
603{
604 KoSubpath *subpath = d->subPath(pointIndex.first);
605
606 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
607 return 0;
608
609 return subpath->at(pointIndex.second);
610}
611
613{
614 KoPathSegment segment(0, 0);
615
616 KoSubpath *subpath = d->subPath(pointIndex.first);
617
618 if (subpath != 0 && pointIndex.second >= 0 && pointIndex.second < subpath->size()) {
619 KoPathPoint * point = subpath->at(pointIndex.second);
620 int index = pointIndex.second;
621 // check if we have a (closing) segment starting from the last point
622 if ((index == subpath->size() - 1) && point->properties() & KoPathPoint::CloseSubpath)
623 index = 0;
624 else
625 ++index;
626
627 if (index < subpath->size()) {
628 segment = KoPathSegment(point, subpath->at(index));
629 }
630 }
631 return segment;
632}
633
635{
636 int i = 0;
637 KoSubpathList::const_iterator pathIt(d->subpaths.constBegin());
638 for (; pathIt != d->subpaths.constEnd(); ++pathIt) {
639 i += (*pathIt)->size();
640 }
641
642 return i;
643}
644
646{
647 return d->subpaths.count();
648}
649
650int KoPathShape::subpathPointCount(int subpathIndex) const
651{
652 KoSubpath *subpath = d->subPath(subpathIndex);
653
654 if (subpath == 0)
655 return -1;
656
657 return subpath->size();
658}
659
660bool KoPathShape::isClosedSubpath(int subpathIndex) const
661{
662 KoSubpath *subpath = d->subPath(subpathIndex);
663
664 if (subpath == 0)
665 return false;
666
667 const bool firstClosed = subpath->first()->properties() & KoPathPoint::CloseSubpath;
668 const bool lastClosed = subpath->last()->properties() & KoPathPoint::CloseSubpath;
669
670 return firstClosed && lastClosed;
671}
672
674{
675 KoSubpath *subpath = d->subPath(pointIndex.first);
676
677 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second > subpath->size())
678 return false;
679
680 KoPathPoint::PointProperties properties = point->properties();
681 properties &= ~KoPathPoint::StartSubpath;
682 properties &= ~KoPathPoint::StopSubpath;
683 properties &= ~KoPathPoint::CloseSubpath;
684 // check if new point starts subpath
685 if (pointIndex.second == 0) {
686 properties |= KoPathPoint::StartSubpath;
687 // subpath was closed
688 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
689 // keep the path closed
690 properties |= KoPathPoint::CloseSubpath;
691 }
692 // old first point does not start the subpath anymore
693 subpath->first()->unsetProperty(KoPathPoint::StartSubpath);
694 }
695 // check if new point stops subpath
696 else if (pointIndex.second == subpath->size()) {
697 properties |= KoPathPoint::StopSubpath;
698 // subpath was closed
699 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
700 // keep the path closed
701 properties = properties | KoPathPoint::CloseSubpath;
702 }
703 // old last point does not end subpath anymore
704 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
705 }
706
707 point->setProperties(properties);
708 point->setParent(this);
709 subpath->insert(pointIndex.second , point);
711
712 return true;
713}
714
716{
717 KoSubpath *subpath = d->subPath(pointIndex.first);
718
719 if (subpath == 0 || pointIndex.second < 0 || pointIndex.second >= subpath->size())
720 return 0;
721
722 KoPathPoint * point = subpath->takeAt(pointIndex.second);
723 point->setParent(0);
724
725 //don't do anything (not even crash), if there was only one point
726 if (pointCount()==0) {
727 return point;
728 }
729 // check if we removed the first point
730 else if (pointIndex.second == 0) {
731 // first point removed, set new StartSubpath
732 subpath->first()->setProperty(KoPathPoint::StartSubpath);
733 // check if path was closed
734 if (subpath->last()->properties() & KoPathPoint::CloseSubpath) {
735 // keep path closed
736 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
737 }
738 }
739 // check if we removed the last point
740 else if (pointIndex.second == subpath->size()) { // use size as point is already removed
741 // last point removed, set new StopSubpath
742 subpath->last()->setProperty(KoPathPoint::StopSubpath);
743 // check if path was closed
744 if (point->properties() & KoPathPoint::CloseSubpath) {
745 // keep path closed
746 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
747 }
748 }
749
751
752 return point;
753}
754
756{
757 KoSubpath *subpath = d->subPath(pointIndex.first);
758
759 if (!subpath || pointIndex.second < 0 || pointIndex.second > subpath->size() - 2
760 || isClosedSubpath(pointIndex.first))
761 return false;
762
763 KoSubpath * newSubpath = new KoSubpath;
764
765 int size = subpath->size();
766 for (int i = pointIndex.second + 1; i < size; ++i) {
767 newSubpath->append(subpath->takeAt(pointIndex.second + 1));
768 }
769 // now make the first point of the new subpath a starting node
770 newSubpath->first()->setProperty(KoPathPoint::StartSubpath);
771 // the last point of the old subpath is now an ending node
772 subpath->last()->setProperty(KoPathPoint::StopSubpath);
773
774 // insert the new subpath after the broken one
775 d->subpaths.insert(pointIndex.first + 1, newSubpath);
777
778 return true;
779}
780
781bool KoPathShape::join(int subpathIndex)
782{
783 KoSubpath *subpath = d->subPath(subpathIndex);
784 KoSubpath *nextSubpath = d->subPath(subpathIndex + 1);
785
786 if (!subpath || !nextSubpath || isClosedSubpath(subpathIndex)
787 || isClosedSubpath(subpathIndex+1))
788 return false;
789
790 // the last point of the subpath does not end the subpath anymore
791 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
792 // the first point of the next subpath does not start a subpath anymore
793 nextSubpath->first()->unsetProperty(KoPathPoint::StartSubpath);
794
795 // append the second subpath to the first
796 Q_FOREACH (KoPathPoint * p, *nextSubpath)
797 subpath->append(p);
798
799 // remove the nextSubpath from path
800 d->subpaths.removeAt(subpathIndex + 1);
801
802 // delete it as it is no longer possible to use it
803 delete nextSubpath;
804
806
807 return true;
808}
809
810bool KoPathShape::moveSubpath(int oldSubpathIndex, int newSubpathIndex)
811{
812 KoSubpath *subpath = d->subPath(oldSubpathIndex);
813
814 if (subpath == 0 || newSubpathIndex >= d->subpaths.size())
815 return false;
816
817 if (oldSubpathIndex == newSubpathIndex)
818 return true;
819
820 d->subpaths.removeAt(oldSubpathIndex);
821 d->subpaths.insert(newSubpathIndex, subpath);
822
824
825 return true;
826}
827
829{
830 KoSubpath *subpath = d->subPath(pointIndex.first);
831
832 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
833 || !isClosedSubpath(pointIndex.first))
834 return KoPathPointIndex(-1, -1);
835
836 KoPathPoint * oldStartPoint = subpath->first();
837 // the old starting node no longer starts the subpath
839 // the old end node no longer closes the subpath
840 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
841
842 // reorder the subpath
843 for (int i = 0; i < pointIndex.second; ++i) {
844 subpath->append(subpath->takeFirst());
845 }
846 // make the first point a start node
847 subpath->first()->setProperty(KoPathPoint::StartSubpath);
848 // make the last point an end node
849 subpath->last()->setProperty(KoPathPoint::StopSubpath);
850
852
853 return pathPointIndex(oldStartPoint);
854}
855
857{
858 KoSubpath *subpath = d->subPath(pointIndex.first);
859
860 if (!subpath || pointIndex.second < 0 || pointIndex.second >= subpath->size()
861 || isClosedSubpath(pointIndex.first))
862 return KoPathPointIndex(-1, -1);
863
864 KoPathPoint * oldStartPoint = subpath->first();
865 // the old starting node no longer starts the subpath
867 // the old end node no longer ends the subpath
868 subpath->last()->unsetProperty(KoPathPoint::StopSubpath);
869
870 // reorder the subpath
871 for (int i = 0; i < pointIndex.second; ++i) {
872 subpath->append(subpath->takeFirst());
873 }
874 subpath->first()->setProperty(KoPathPoint::StartSubpath);
875 subpath->last()->setProperty(KoPathPoint::StopSubpath);
876
877 closeSubpathPriv(subpath);
878
880
881 return pathPointIndex(oldStartPoint);
882}
883
884bool KoPathShape::reverseSubpath(int subpathIndex)
885{
886 KoSubpath *subpath = d->subPath(subpathIndex);
887
888 if (subpath == 0)
889 return false;
890
891 int size = subpath->size();
892 for (int i = 0; i < size; ++i) {
893 KoPathPoint *p = subpath->takeAt(i);
894 p->reverse();
895 subpath->prepend(p);
896 }
897
898 // adjust the position dependent properties
899 KoPathPoint *first = subpath->first();
900 KoPathPoint *last = subpath->last();
901
902 KoPathPoint::PointProperties firstProps = first->properties();
903 KoPathPoint::PointProperties lastProps = last->properties();
904
905 firstProps |= KoPathPoint::StartSubpath;
906 firstProps &= ~KoPathPoint::StopSubpath;
907 lastProps |= KoPathPoint::StopSubpath;
908 lastProps &= ~KoPathPoint::StartSubpath;
909 if (firstProps & KoPathPoint::CloseSubpath) {
910 firstProps |= KoPathPoint::CloseSubpath;
911 lastProps |= KoPathPoint::CloseSubpath;
912 }
913 first->setProperties(firstProps);
914 last->setProperties(lastProps);
915
917
918 return true;
919}
920
922{
923 KoSubpath *subpath = d->subPath(subpathIndex);
924
925 if (subpath != 0) {
926 Q_FOREACH (KoPathPoint* point, *subpath) {
927 point->setParent(this);
928 }
929 d->subpaths.removeAt(subpathIndex);
930 }
931
933
934 return subpath;
935}
936
937bool KoPathShape::addSubpath(KoSubpath * subpath, int subpathIndex)
938{
939 if (subpathIndex < 0 || subpathIndex > d->subpaths.size())
940 return false;
941
942 Q_FOREACH (KoPathPoint* point, *subpath) {
943 point->setParent(this);
944 }
945
946 d->subpaths.insert(subpathIndex, subpath);
948
949
950 return true;
951}
953{
954 int insertSegmentPosition = -1;
955 if (!path) return insertSegmentPosition;
956
957 QTransform pathMatrix = path->absoluteTransformation();
958 QTransform myMatrix = absoluteTransformation().inverted();
959
960 Q_FOREACH (KoSubpath* subpath, path->d->subpaths) {
961 KoSubpath *newSubpath = new KoSubpath();
962
963 Q_FOREACH (KoPathPoint* point, *subpath) {
964 KoPathPoint *newPoint = new KoPathPoint(*point, this);
965 newPoint->map(pathMatrix);
966 newPoint->map(myMatrix);
967 newSubpath->append(newPoint);
968 }
969 d->subpaths.append(newSubpath);
970
971 if (insertSegmentPosition < 0) {
972 insertSegmentPosition = d->subpaths.size() - 1;
973 }
974 }
975 normalize();
976
978 return insertSegmentPosition;
979}
980
982{
983 if (! d->subpaths.size())
984 return false;
985
986 QTransform myMatrix = absoluteTransformation();
987
988 Q_FOREACH (KoSubpath* subpath, d->subpaths) {
989 KoPathShape *shape = new KoPathShape();
990
991 shape->setStroke(stroke());
992 shape->setBackground(background());
993 shape->setShapeId(shapeId());
994 shape->setZIndex(zIndex());
995
996 KoSubpath *newSubpath = new KoSubpath();
997
998 Q_FOREACH (KoPathPoint* point, *subpath) {
999 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
1015{
1016 if (! subpath)
1017 return;
1018
1019 subpath->last()->setProperty(KoPathPoint::CloseSubpath);
1020 subpath->first()->setProperty(KoPathPoint::CloseSubpath);
1021
1023}
1024
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
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();
1047
1049 } else {
1050 closeSubpathPriv(subpath);
1051 }
1052}
1053
1055{
1056 return d->subpaths;
1057}
1058
1060{
1061 return d->subpaths;
1062}
1063
1064void KoPathShape::map(const QTransform &matrix)
1065{
1066 return d->map(matrix);
1067}
1068
1070{
1071 if (subpathIndex < 0 || subpathIndex >= subpaths.size())
1072 return 0;
1073
1074 return subpaths.at(subpathIndex);
1075}
1076
1078{
1079 return KoPathShapeId;
1080}
1081
1082QString 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
1164char 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
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
1201void updateNodeType(KoPathPoint * point, const QChar & nodeType)
1202{
1203 if (nodeType == 's') {
1205 }
1206 else if (nodeType == 'z') {
1208 }
1209}
1210
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
1237Qt::FillRule KoPathShape::fillRule() const
1238{
1239 return d->fillRule;
1240}
1241
1243{
1244 d->fillRule = fillRule;
1245}
1246
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
1277bool 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
1309{
1310 if (!marker && d->markersNew.contains(pos)) {
1311 d->markersNew.remove(pos);
1312 } else {
1313 d->markersNew[pos] = marker;
1314 }
1315
1316 notifyChanged();
1318}
1319
1321{
1322 return d->markersNew[pos].data();
1323}
1324
1326{
1327 return !d->markersNew.isEmpty();
1328}
1329
1331{
1332 return d->autoFillMarkers;
1333}
1334
1336{
1337 d->autoFillMarkers = value;
1338}
1339
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
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
1360QPainterPath 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
1386{
1387 Q_UNUSED(type);
1388 Q_UNUSED(shape);
1389}
#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)
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....
QRectF clipRectForBoundingRect(const QRectF &boundingRect) const
Returns the clipping rectangle for the given bounding rect.
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.
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.
bool isClipped(const KoShape *child) const
void insets(KoInsets &insets) const
Fills the insets object with the space the shadow takes around a shape.
QList< KoShape::ShapeChangeListener * > listeners
Definition KoShape_p.h:84
KoShapeContainer * parent
Definition KoShape_p.h:80
void setZIndex(qint16 zIndex)
Definition KoShape.cpp:954
QString shapeId() const
Definition KoShape.cpp:1057
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
void applyTransformation(const QTransform &matrix)
Definition KoShape.cpp:410
void shapeChangedPriv(KoShape::ChangeType type)
Definition KoShape.cpp:132
virtual void setStroke(KoShapeStrokeModelSP stroke)
Definition KoShape.cpp:1081
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
KoShapeShadow * shadow() const
Returns the currently set shadow or 0 if there is no shadow set.
Definition KoShape.cpp:1116
virtual void setBackground(QSharedPointer< KoShapeBackground > background)
Definition KoShape.cpp:918
ChangeType
Used by shapeChanged() to select which change was made.
Definition KoShape.h:95
@ StrokeChanged
the shapes stroke has changed
Definition KoShape.h:105
@ ContentChanged
the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
Definition KoShape.h:110
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
KoFilterEffectStack * filterEffectStack() const
Definition KoShape.cpp:1294
QSharedDataPointer< SharedData > s
Definition KoShape.h:1138
void notifyChanged()
Definition KoShape.cpp:698
void setShapeId(const QString &id)
Definition KoShape.cpp:1062
qint16 zIndex() const
Definition KoShape.cpp:600
QTransform transform() const
return the current matrix that contains the rotation/scale/position of this shape
Definition KoShape.cpp:1145
virtual void setSize(const QSizeF &size)
Resize the shape.
Definition KoShape.cpp:276
QPointF position() const
Get the position of the shape in pt.
Definition KoShape.cpp:825
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
#define M_PI
Definition kis_global.h:111
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