Krita Source Code Documentation
Loading...
Searching...
No Matches
KoPathSegment Class Reference

A KoPathSegment consist of two neighboring KoPathPoints. More...

#include <KoPathSegment.h>

+ Inheritance diagram for KoPathSegment:

Public Member Functions

QRectF boundingRect () const
 Returns the axis aligned tight bounding rect.
 
qreal chordLength () const
 Returns the chord length, i.e. the distance between first and last control point.
 
QRectF controlPointRect () const
 Returns the control point bounding rect.
 
QList< QPointF > controlPoints () const
 Returns ordered list of control points.
 
QList< QPointF > convexHull () const
 Returns the convex hull polygon of the segment.
 
void deCasteljau (qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const
 
int degree () const
 Returns the degree of the segment: 1 = line, 2 = quadratic, 3 = cubic, -1 = invalid.
 
qreal distanceFromChord (const QPointF &point) const
 calculates signed distance of given point from segment chord
 
QList< qreal > extrema () const
 Returns parameters for curve extrema.
 
KoPathPointfirst () const
 Returns the first point of the segment.
 
QList< QPointF > intersections (const KoPathSegment &segment) const
 Returns list of intersections with the given path segment.
 
bool isFlat (qreal tolerance=0.01) const
 
bool isValid () const
 Returns if segment is valid, e.g. has two valid points.
 
 KoPathSegment (const KoPathSegment &segment)
 Constructs segment by copying another segment.
 
 KoPathSegment (const QPointF &p0, const QPointF &p1)
 Creates a new line segment.
 
 KoPathSegment (const QPointF &p0, const QPointF &p1, const QPointF &p2)
 Creates a new quadratic segment.
 
 KoPathSegment (const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3)
 Creates a new cubic segment.
 
 KoPathSegment (KoPathPoint *first=0, KoPathPoint *second=0)
 
qreal length (qreal error=0.005) const
 
qreal lengthAt (qreal t, qreal error=0.005) const
 
QList< QPointF > linesIntersection (const KoPathSegment &segment) const
 Returns intersection of lines if one exists.
 
KoPathSegment mapped (const QTransform &matrix) const
 Returns transformed segment.
 
qreal nearestPoint (const QPointF &point) const
 
KoPathSegmentoperator= (const KoPathSegment &other)
 Assigns segment.
 
bool operator== (const KoPathSegment &other) const
 Compare operator.
 
qreal paramAtLength (qreal length, qreal tolerance=0.001) const
 
QPointF pointAt (qreal t) const
 Returns point at given t.
 
 Private (KoPathSegment *qq, KoPathPoint *p1, KoPathPoint *p2)
 
QList< qreal > roots () const
 Returns parameters for curve roots.
 
KoPathPointsecond () const
 Returns the second point of the segment.
 
void setFirst (KoPathPoint *first)
 Sets the first segment point.
 
void setSecond (KoPathPoint *second)
 Sets the second segment point.
 
QPair< KoPathSegment, KoPathSegmentsplitAt (qreal t) const
 Splits segment at given position returning the two resulting segments.
 
KoPathSegment toCubic () const
 Returns cubic bezier curve segment of this segment.
 
 ~KoPathSegment ()
 Destroys the path segment.
 

Static Public Member Functions

static KoPathSegment interpolate (const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal t)
 

Public Attributes

KoPathPointfirst
 
KoPathSegmentq
 
KoPathPointsecond
 

Private Attributes

Private *const d
 
- Private Attributes inherited from Private
KisCanvas2canvas
 
int displayedFrame
 
int intendedFrame
 

Additional Inherited Members

- Private Member Functions inherited from Private
 Private (KisCanvas2 *c)
 

Detailed Description

A KoPathSegment consist of two neighboring KoPathPoints.

Definition at line 18 of file KoPathSegment.cpp.

Constructor & Destructor Documentation

◆ KoPathSegment() [1/5]

KoPathSegment::KoPathSegment ( KoPathPoint * first = 0,
KoPathPoint * second = 0 )
explicit

Creates a new segment from the given path points It takes ownership of the path points which do not have a parent path shape set.

Definition at line 313 of file KoPathSegment.cpp.

314 : d(new Private(this, first, second))
315{
316}
Private *const d
KoPathPoint * first
KoPathPoint * second

◆ KoPathSegment() [2/5]

KoPathSegment::KoPathSegment ( const KoPathSegment & segment)

Constructs segment by copying another segment.

Definition at line 318 of file KoPathSegment.cpp.

319 : d(new Private(this, 0, 0))
320{
321 if (! segment.first() || segment.first()->parent())
322 setFirst(segment.first());
323 else
324 setFirst(new KoPathPoint(*segment.first()));
325
326 if (! segment.second() || segment.second()->parent())
327 setSecond(segment.second());
328 else
329 setSecond(new KoPathPoint(*segment.second()));
330}
A KoPathPoint represents a point in a path.
KoPathShape * parent() const
Get the path shape the point belongs to.
void setSecond(KoPathPoint *second)
Sets the second segment point.
void setFirst(KoPathPoint *first)
Sets the first segment point.

References first, KoPathPoint::parent(), second, setFirst(), and setSecond().

◆ KoPathSegment() [3/5]

KoPathSegment::KoPathSegment ( const QPointF & p0,
const QPointF & p1 )

Creates a new line segment.

Definition at line 332 of file KoPathSegment.cpp.

333 : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
334{
335 d->first->setPoint(p0);
336 d->second->setPoint(p1);
337}
QPointF p0
QPointF p1

References d, p0, and p1.

◆ KoPathSegment() [4/5]

KoPathSegment::KoPathSegment ( const QPointF & p0,
const QPointF & p1,
const QPointF & p2 )

Creates a new quadratic segment.

Definition at line 339 of file KoPathSegment.cpp.

340 : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
341{
342 d->first->setPoint(p0);
343 d->first->setControlPoint2(p1);
344 d->second->setPoint(p2);
345}
QPointF p2

References d, p0, p1, and p2.

◆ KoPathSegment() [5/5]

KoPathSegment::KoPathSegment ( const QPointF & p0,
const QPointF & p1,
const QPointF & p2,
const QPointF & p3 )

Creates a new cubic segment.

Definition at line 347 of file KoPathSegment.cpp.

348 : d(new Private(this, new KoPathPoint(), new KoPathPoint()))
349{
350 d->first->setPoint(p0);
351 d->first->setControlPoint2(p1);
352 d->second->setControlPoint1(p2);
353 d->second->setPoint(p3);
354}
QPointF p3

References d, p0, p1, p2, and p3.

◆ ~KoPathSegment()

KoPathSegment::~KoPathSegment ( )

Destroys the path segment.

Definition at line 374 of file KoPathSegment.cpp.

375{
376 if (d->first && ! d->first->parent())
377 delete d->first;
378 if (d->second && ! d->second->parent())
379 delete d->second;
380 delete d;
381}

References d.

Member Function Documentation

◆ boundingRect()

QRectF KoPathSegment::boundingRect ( ) const

Returns the axis aligned tight bounding rect.

Definition at line 479 of file KoPathSegment.cpp.

480{
481 if (!isValid())
482 return QRectF();
483
484 QRectF rect = QRectF(d->first->point(), d->second->point()).normalized();
485
486 if (degree() == 1) {
487 // adjust bounding rect of horizontal and vertical lines
488 if (rect.height() == 0.0)
489 rect.setHeight(0.1);
490 if (rect.width() == 0.0)
491 rect.setWidth(0.1);
492 } else {
493 /*
494 * The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments
495 * was found in comp.graphics.algorithms:
496 * Use the points at the extrema of the curve to calculate the AABB.
497 */
498 foreach (qreal t, d->extrema()) {
499 if (t >= 0.0 && t <= 1.0) {
500 QPointF p = pointAt(t);
501 rect.setLeft(qMin(rect.left(), p.x()));
502 rect.setRight(qMax(rect.right(), p.x()));
503 rect.setTop(qMin(rect.top(), p.y()));
504 rect.setBottom(qMax(rect.bottom(), p.y()));
505 }
506 }
507 }
508
509 return rect;
510}
const Params2D p
int degree() const
Returns the degree of the segment: 1 = line, 2 = quadratic, 3 = cubic, -1 = invalid.
QPointF pointAt(qreal t) const
Returns point at given t.
bool isValid() const
Returns if segment is valid, e.g. has two valid points.

References d, degree(), isValid(), p, and pointAt().

◆ chordLength()

qreal KoPathSegment::chordLength ( ) const

Returns the chord length, i.e. the distance between first and last control point.

◆ controlPointRect()

QRectF KoPathSegment::controlPointRect ( ) const

Returns the control point bounding rect.

Definition at line 454 of file KoPathSegment.cpp.

455{
456 if (!isValid())
457 return QRectF();
458
459 QList<QPointF> points = controlPoints();
460 QRectF bbox(points.first(), points.first());
461 Q_FOREACH (const QPointF &p, points) {
462 bbox.setLeft(qMin(bbox.left(), p.x()));
463 bbox.setRight(qMax(bbox.right(), p.x()));
464 bbox.setTop(qMin(bbox.top(), p.y()));
465 bbox.setBottom(qMax(bbox.bottom(), p.y()));
466 }
467
468 if (degree() == 1) {
469 // adjust bounding rect of horizontal and vertical lines
470 if (bbox.height() == 0.0)
471 bbox.setHeight(0.1);
472 if (bbox.width() == 0.0)
473 bbox.setWidth(0.1);
474 }
475
476 return bbox;
477}
QList< QPointF > controlPoints() const
Returns ordered list of control points.

References controlPoints(), degree(), isValid(), and p.

◆ controlPoints()

QList< QPointF > KoPathSegment::controlPoints ( ) const

Returns ordered list of control points.

Definition at line 1021 of file KoPathSegment.cpp.

1022{
1024 controlPoints.append(d->first->point());
1025 if (d->first->activeControlPoint2())
1026 controlPoints.append(d->first->controlPoint2());
1027 if (d->second->activeControlPoint1())
1028 controlPoints.append(d->second->controlPoint1());
1029 controlPoints.append(d->second->point());
1030
1031 return controlPoints;
1032}

References controlPoints(), and d.

◆ convexHull()

QList< QPointF > KoPathSegment::convexHull ( ) const

Returns the convex hull polygon of the segment.

Definition at line 913 of file KoPathSegment.cpp.

914{
915 QList<QPointF> hull;
916 int deg = degree();
917 if (deg == 1) {
918 // easy just the two end points
919 hull.append(d->first->point());
920 hull.append(d->second->point());
921 } else if (deg == 2) {
922 // we want to have a counter-clockwise oriented triangle
923 // of the three control points
924 QPointF chord = d->second->point() - d->first->point();
925 QPointF cp = d->first->activeControlPoint2() ? d->first->controlPoint2() : d->second->controlPoint1();
926 QPointF relP = cp - d->first->point();
927 // check on which side of the chord the control point is
928 bool pIsRight = (chord.x() * relP.y() - chord.y() * relP.x() > 0);
929 hull.append(d->first->point());
930 if (pIsRight)
931 hull.append(cp);
932 hull.append(d->second->point());
933 if (! pIsRight)
934 hull.append(cp);
935 } else if (deg == 3) {
936 // we want a counter-clockwise oriented polygon
937 QPointF chord = d->second->point() - d->first->point();
938 QPointF relP1 = d->first->controlPoint2() - d->first->point();
939 // check on which side of the chord the control points are
940 bool p1IsRight = (chord.x() * relP1.y() - chord.y() * relP1.x() > 0);
941 hull.append(d->first->point());
942 if (p1IsRight)
943 hull.append(d->first->controlPoint2());
944 hull.append(d->second->point());
945 if (! p1IsRight)
946 hull.append(d->first->controlPoint2());
947
948 // now we have a counter-clockwise triangle with the points i,j,k
949 // we have to check where the last control points lies
950 bool rightOfEdge[3];
951 QPointF lastPoint = d->second->controlPoint1();
952 for (int i = 0; i < 3; ++i) {
953 QPointF relP = lastPoint - hull[i];
954 QPointF edge = hull[(i+1)%3] - hull[i];
955 rightOfEdge[i] = (edge.x() * relP.y() - edge.y() * relP.x() > 0);
956 }
957 for (int i = 0; i < 3; ++i) {
958 int prev = (3 + i - 1) % 3;
959 int next = (i + 1) % 3;
960 // check if point is only right of the n-th edge
961 if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) {
962 // insert by breaking the n-th edge
963 hull.insert(i + 1, lastPoint);
964 break;
965 }
966 // check if it is right of the n-th and right of the (n+1)-th edge
967 if (rightOfEdge[i] && rightOfEdge[next]) {
968 // remove both edge, insert two new edges
969 hull[i+1] = lastPoint;
970 break;
971 }
972 // check if it is right of n-th and right of (n-1)-th edge
973 if (rightOfEdge[i] && rightOfEdge[prev]) {
974 hull[i] = lastPoint;
975 break;
976 }
977 }
978 }
979
980 return hull;
981}
QAction * next(const QObject *recvr, const char *slot, QObject *parent)

References d, and degree().

◆ deCasteljau()

void KoPathSegment::deCasteljau ( qreal t,
QPointF * p1,
QPointF * p2,
QPointF * p3,
QPointF * p4,
QPointF * p5 ) const

The DeCasteljau algorithm for parameter t.

Parameters
tthe parameter to evaluate at
p1the new control point of the segment start (for cubic curves only)
p2the first control point at t
p3the new point at t
p4the second control point at t
p3the new control point of the segment end (for cubic curves only)

◆ degree()

int KoPathSegment::degree ( ) const

Returns the degree of the segment: 1 = line, 2 = quadratic, 3 = cubic, -1 = invalid.

Definition at line 424 of file KoPathSegment.cpp.

425{
426 if (!d->first || !d->second)
427 return -1;
428
429 bool c1 = d->first->activeControlPoint2();
430 bool c2 = d->second->activeControlPoint1();
431 if (!c1 && !c2)
432 return 1;
433 if (c1 && c2)
434 return 3;
435 return 2;
436}

References d.

◆ distanceFromChord()

qreal KoPathSegment::distanceFromChord ( const QPointF & point) const

calculates signed distance of given point from segment chord

◆ extrema()

QList< qreal > KoPathSegment::extrema ( ) const

Returns parameters for curve extrema.

◆ first()

KoPathPoint * KoPathSegment::first ( ) const

Returns the first point of the segment.

◆ interpolate()

KoPathSegment KoPathSegment::interpolate ( const QPointF & p0,
const QPointF & p1,
const QPointF & p2,
qreal t )
static

Interpolates quadric bezier curve.

Returns
the interpolated bezier segment

Definition at line 1042 of file KoPathSegment.cpp.

1043{
1044 if (t <= 0.0 || t >= 1.0)
1045 return KoPathSegment();
1046
1047 /*
1048 B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2
1049
1050 B(t) - (1-t)^2*P0 - t^2*P2
1051 P1 = --------------------------
1052 2t*(1-t)
1053 */
1054
1055 QPointF c1 = p1 - (1.0-t) * (1.0-t)*p0 - t * t * p2;
1056
1057 qreal denom = 2.0 * t * (1.0-t);
1058
1059 c1.rx() /= denom;
1060 c1.ry() /= denom;
1061
1062 return KoPathSegment(p0, c1, p2);
1063}
KoPathSegment(KoPathPoint *first=0, KoPathPoint *second=0)

References KoPathSegment(), p0, p1, and p2.

◆ intersections()

QList< QPointF > KoPathSegment::intersections ( const KoPathSegment & segment) const

Returns list of intersections with the given path segment.

Definition at line 512 of file KoPathSegment.cpp.

513{
514 // this function uses a technique known as bezier clipping to find the
515 // intersections of the two bezier curves
516
517 QList<QPointF> isects;
518
519 if (!isValid() || !segment.isValid())
520 return isects;
521
522 int degree1 = degree();
523 int degree2 = segment.degree();
524
525 QRectF myBound = boundingRect();
526 QRectF otherBound = segment.boundingRect();
527 //debugFlake << "my boundingRect =" << myBound;
528 //debugFlake << "other boundingRect =" << otherBound;
529 if (!myBound.intersects(otherBound)) {
530 //debugFlake << "segments do not intersect";
531 return isects;
532 }
533
534 // short circuit lines intersection
535 if (degree1 == 1 && degree2 == 1) {
536 //debugFlake << "intersecting two lines";
537 isects += d->linesIntersection(segment);
538 return isects;
539 }
540
541 // first calculate the fat line L by using the signed distances
542 // of the control points from the chord
543 qreal dmin, dmax;
544 if (degree1 == 1) {
545 dmin = 0.0;
546 dmax = 0.0;
547 } else if (degree1 == 2) {
548 qreal d1;
549 if (d->first->activeControlPoint2())
550 d1 = d->distanceFromChord(d->first->controlPoint2());
551 else
552 d1 = d->distanceFromChord(d->second->controlPoint1());
553 dmin = qMin(qreal(0.0), qreal(0.5 * d1));
554 dmax = qMax(qreal(0.0), qreal(0.5 * d1));
555 } else {
556 qreal d1 = d->distanceFromChord(d->first->controlPoint2());
557 qreal d2 = d->distanceFromChord(d->second->controlPoint1());
558 if (d1*d2 > 0.0) {
559 dmin = 0.75 * qMin(qreal(0.0), qMin(d1, d2));
560 dmax = 0.75 * qMax(qreal(0.0), qMax(d1, d2));
561 } else {
562 dmin = 4.0 / 9.0 * qMin(qreal(0.0), qMin(d1, d2));
563 dmax = 4.0 / 9.0 * qMax(qreal(0.0), qMax(d1, d2));
564 }
565 }
566
567 //debugFlake << "using fat line: dmax =" << dmax << " dmin =" << dmin;
568
569 /*
570 the other segment is given as a bezier curve of the form:
571 (1) P(t) = sum_i P_i * B_{n,i}(t)
572 our chord line is of the form:
573 (2) ax + by + c = 0
574 we can determine the distance d(t) from any point P(t) to our chord
575 by substituting formula (1) into formula (2):
576 d(t) = sum_i d_i B_{n,i}(t), where d_i = a*x_i + b*y_i + c
577 which forms another explicit bezier curve
578 D(t) = (t,d(t)) = sum_i D_i B_{n,i}(t)
579 now values of t for which P(t) lies outside of our fat line L
580 corresponds to values of t for which D(t) lies above d = dmax or
581 below d = dmin
582 we can determine parameter ranges of t for which P(t) is guaranteed
583 to lie outside of L by identifying ranges of t which the convex hull
584 of D(t) lies above dmax or below dmin
585 */
586 // now calculate the control points of D(t) by using the signed
587 // distances of P_i to our chord
588 KoPathSegment dt;
589 if (degree2 == 1) {
590 QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
591 QPointF p1(1.0, d->distanceFromChord(segment.second()->point()));
592 dt = KoPathSegment(p0, p1);
593 } else if (degree2 == 2) {
594 QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
595 QPointF p1 = segment.first()->activeControlPoint2()
596 ? QPointF(0.5, d->distanceFromChord(segment.first()->controlPoint2()))
597 : QPointF(0.5, d->distanceFromChord(segment.second()->controlPoint1()));
598 QPointF p2(1.0, d->distanceFromChord(segment.second()->point()));
599 dt = KoPathSegment(p0, p1, p2);
600 } else if (degree2 == 3) {
601 QPointF p0(0.0, d->distanceFromChord(segment.first()->point()));
602 QPointF p1(1. / 3., d->distanceFromChord(segment.first()->controlPoint2()));
603 QPointF p2(2. / 3., d->distanceFromChord(segment.second()->controlPoint1()));
604 QPointF p3(1.0, d->distanceFromChord(segment.second()->point()));
605 dt = KoPathSegment(p0, p1, p2, p3);
606 } else {
607 //debugFlake << "invalid degree of segment -> exiting";
608 return isects;
609 }
610
611 // get convex hull of the segment D(t)
612 QList<QPointF> hull = dt.convexHull();
613
614 // now calculate intersections with the line y1 = dmin, y2 = dmax
615 // with the convex hull edges
616 int hullCount = hull.count();
617 qreal tmin = 1.0, tmax = 0.0;
618 bool intersectionsFoundMax = false;
619 bool intersectionsFoundMin = false;
620
621 for (int i = 0; i < hullCount; ++i) {
622 QPointF p1 = hull[i];
623 QPointF p2 = hull[(i+1) % hullCount];
624 //debugFlake << "intersecting hull edge (" << p1 << p2 << ")";
625 // hull edge is completely above dmax
626 if (p1.y() > dmax && p2.y() > dmax)
627 continue;
628 // hull edge is completely below dmin
629 if (p1.y() < dmin && p2.y() < dmin)
630 continue;
631 if (p1.x() == p2.x()) {
632 // vertical edge
633 bool dmaxIntersection = (dmax < qMax(p1.y(), p2.y()) && dmax > qMin(p1.y(), p2.y()));
634 bool dminIntersection = (dmin < qMax(p1.y(), p2.y()) && dmin > qMin(p1.y(), p2.y()));
635 if (dmaxIntersection || dminIntersection) {
636 tmin = qMin(tmin, p1.x());
637 tmax = qMax(tmax, p1.x());
638 if (dmaxIntersection) {
639 intersectionsFoundMax = true;
640 //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax;
641 } else {
642 intersectionsFoundMin = true;
643 //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin;
644 }
645 }
646 } else if (p1.y() == p2.y()) {
647 // horizontal line
648 if (p1.y() == dmin || p1.y() == dmax) {
649 if (p1.y() == dmin) {
650 intersectionsFoundMin = true;
651 //debugFlake << "found intersection with dmin at " << p1.x() << "," << dmin;
652 //debugFlake << "found intersection with dmin at " << p2.x() << "," << dmin;
653 } else {
654 intersectionsFoundMax = true;
655 //debugFlake << "found intersection with dmax at " << p1.x() << "," << dmax;
656 //debugFlake << "found intersection with dmax at " << p2.x() << "," << dmax;
657 }
658 tmin = qMin(tmin, p1.x());
659 tmin = qMin(tmin, p2.x());
660 tmax = qMax(tmax, p1.x());
661 tmax = qMax(tmax, p2.x());
662 }
663 } else {
664 qreal dx = p2.x() - p1.x();
665 qreal dy = p2.y() - p1.y();
666 qreal m = dy / dx;
667 qreal n = p1.y() - m * p1.x();
668 qreal t1 = (dmax - n) / m;
669 if (t1 >= 0.0 && t1 <= 1.0) {
670 tmin = qMin(tmin, t1);
671 tmax = qMax(tmax, t1);
672 intersectionsFoundMax = true;
673 //debugFlake << "found intersection with dmax at " << t1 << "," << dmax;
674 }
675 qreal t2 = (dmin - n) / m;
676 if (t2 >= 0.0 && t2 < 1.0) {
677 tmin = qMin(tmin, t2);
678 tmax = qMax(tmax, t2);
679 intersectionsFoundMin = true;
680 //debugFlake << "found intersection with dmin at " << t2 << "," << dmin;
681 }
682 }
683 }
684
685 bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax;
686
687 //if (intersectionsFound)
688 // debugFlake << "clipping segment to interval [" << tmin << "," << tmax << "]";
689
690 if (!intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) {
691 //debugFlake << "could not clip enough -> split segment";
692 // we could not reduce the interval significantly
693 // so split the curve and calculate intersections
694 // with the remaining parts
695 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
696 if (d->chordLength() < 1e-5)
697 isects += parts.first.second()->point();
698 else {
699 isects += segment.intersections(parts.first);
700 isects += segment.intersections(parts.second);
701 }
702 } else if (qAbs(tmin - tmax) < 1e-5) {
703 //debugFlake << "Yay, we found an intersection";
704 // the interval is pretty small now, just calculate the intersection at this point
705 isects.append(segment.pointAt(tmin));
706 } else {
707 QPair<KoPathSegment, KoPathSegment> clip1 = segment.splitAt(tmin);
708 //debugFlake << "splitting segment at" << tmin;
709 qreal t = (tmax - tmin) / (1.0 - tmin);
710 QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t);
711 //debugFlake << "splitting second part at" << t << "("<<tmax<<")";
712 isects += clip2.first.intersections(*this);
713 }
714
715 return isects;
716}
QPointF point
QPointF controlPoint1
bool activeControlPoint2
QPointF controlPoint2
A KoPathSegment consist of two neighboring KoPathPoints.
QPair< KoPathSegment, KoPathSegment > splitAt(qreal t) const
Splits segment at given position returning the two resulting segments.
QList< QPointF > convexHull() const
Returns the convex hull polygon of the segment.
QList< QPointF > intersections(const KoPathSegment &segment) const
Returns list of intersections with the given path segment.
qreal distanceFromChord(const QPointF &point) const
calculates signed distance of given point from segment chord
QRectF boundingRect() const
Returns the axis aligned tight bounding rect.

References KoPathPoint::activeControlPoint2, boundingRect(), KoPathPoint::controlPoint1, KoPathPoint::controlPoint2, convexHull(), d, degree(), first, intersections(), isValid(), KoPathSegment(), p0, p1, p2, p3, KoPathPoint::point, pointAt(), second, and splitAt().

◆ isFlat()

bool KoPathSegment::isFlat ( qreal tolerance = 0.01) const

Checks if the segment is flat, i.e. the height smaller then the given tolerance

Parameters
tolerancethe flatness tolerance

Definition at line 875 of file KoPathSegment.cpp.

876{
877 /*
878 * Calculate the height of the bezier curve.
879 * This is done by rotating the curve so that then chord
880 * is parallel to the x-axis and the calculating the
881 * parameters t for the extrema of the curve.
882 * The curve points at the extrema are then used to
883 * calculate the height.
884 */
885 if (degree() <= 1)
886 return true;
887
888 QPointF chord = d->second->point() - d->first->point();
889 // calculate angle of chord to the x-axis
890 qreal chordAngle = atan2(chord.y(), chord.x());
891 QTransform m;
892 m.translate(d->first->point().x(), d->first->point().y());
893 m.rotate(chordAngle * M_PI / 180.0);
894 m.translate(-d->first->point().x(), -d->first->point().y());
895
896 KoPathSegment s = mapped(m);
897
898 qreal minDist = 0.0;
899 qreal maxDist = 0.0;
900
901 foreach (qreal t, s.d->extrema()) {
902 if (t >= 0.0 && t <= 1.0) {
903 QPointF p = pointAt(t);
904 qreal dist = s.d->distanceFromChord(p);
905 minDist = qMin(dist, minDist);
906 maxDist = qMax(dist, maxDist);
907 }
908 }
909
910 return (maxDist - minDist <= tolerance);
911}
KoPathSegment mapped(const QTransform &matrix) const
Returns transformed segment.
#define M_PI
Definition kis_global.h:111
KRITAIMAGE_EXPORT qreal atan2(qreal y, qreal x)
atan2 replacement

References d, degree(), M_PI, mapped(), p, and pointAt().

◆ isValid()

bool KoPathSegment::isValid ( ) const

Returns if segment is valid, e.g. has two valid points.

Definition at line 407 of file KoPathSegment.cpp.

408{
409 return (d->first && d->second);
410}

References d.

◆ length()

qreal KoPathSegment::length ( qreal error = 0.005) const

Returns the length of the path segment

Parameters
errorthe error tolerance

Definition at line 760 of file KoPathSegment.cpp.

761{
762 /*
763 * This algorithm is implemented based on an idea by Jens Gravesen:
764 * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute,
765 * The Technical University of Denmark.
766 *
767 * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve.
768 * If you denote the length of the control polygon by L1 i.e.:
769 * L1 = |P0 P1| +|P1 P2| +|P2 P3|
770 *
771 * and the length of the cord by L0 i.e.:
772 * L0 = |P0 P3|
773 *
774 * then
775 * L = 1/2*L0 + 1/2*L1
776 *
777 * is a good approximation to the length of the curve, and the difference
778 * ERR = L1-L0
779 *
780 * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value
781 * 1/2, and find the length of each half.
782 * If m is the number of subdivisions then the error goes to zero as 2^-4m.
783 * If you don't have a cubic curve but a curve of degree n then you put
784 * L = (2*L0 + (n-1)*L1)/(n+1)
785 */
786
787 int deg = degree();
788
789 if (deg == -1)
790 return 0.0;
791
792 QList<QPointF> ctrlPoints = controlPoints();
793
794 // calculate chord length
795 qreal chordLen = d->chordLength();
796
797 if (deg == 1) {
798 return chordLen;
799 }
800
801 // calculate length of control polygon
802 qreal polyLength = 0.0;
803
804 for (int i = 0; i < deg; ++i) {
805 QPointF ctrlSegment = ctrlPoints[i+1] - ctrlPoints[i];
806 polyLength += sqrt(ctrlSegment.x() * ctrlSegment.x() + ctrlSegment.y() * ctrlSegment.y());
807 }
808
809 if ((polyLength - chordLen) > error) {
810 // the error is still bigger than our tolerance -> split segment
811 QPair<KoPathSegment, KoPathSegment> parts = splitAt(0.5);
812 return parts.first.length(error) + parts.second.length(error);
813 } else {
814 // the error is smaller than our tolerance
815 if (deg == 3)
816 return 0.5 * chordLen + 0.5 * polyLength;
817 else
818 return (2.0 * chordLen + polyLength) / 3.0;
819 }
820}

References controlPoints(), d, degree(), and splitAt().

◆ lengthAt()

qreal KoPathSegment::lengthAt ( qreal t,
qreal error = 0.005 ) const

Returns segment length at given parameter

Splits the segment at the given parameter t and calculates the length of the first segment of the split.

Parameters
tcurve parameter to get length at
errorthe error tolerance

Definition at line 822 of file KoPathSegment.cpp.

823{
824 if (t == 0.0)
825 return 0.0;
826 if (t == 1.0)
827 return length(error);
828
829 QPair<KoPathSegment, KoPathSegment> parts = splitAt(t);
830 return parts.first.length(error);
831}
qreal length(qreal error=0.005) const

References length(), and splitAt().

◆ linesIntersection()

QList< QPointF > KoPathSegment::linesIntersection ( const KoPathSegment & segment) const

Returns intersection of lines if one exists.

◆ mapped()

KoPathSegment KoPathSegment::mapped ( const QTransform & matrix) const

Returns transformed segment.

Definition at line 718 of file KoPathSegment.cpp.

719{
720 if (!isValid())
721 return *this;
722
723 KoPathPoint * p1 = new KoPathPoint(*d->first);
724 KoPathPoint * p2 = new KoPathPoint(*d->second);
725 p1->map(matrix);
726 p2->map(matrix);
727
728 return KoPathSegment(p1, p2);
729}

References d, isValid(), KoPathSegment(), p1, and p2.

◆ nearestPoint()

qreal KoPathSegment::nearestPoint ( const QPointF & point) const

Returns the parameter for the curve point nearest to the given point

Parameters
pointthe point to find nearest point to
Returns
the parameter of the curve point nearest to the given point

Definition at line 1034 of file KoPathSegment.cpp.

1035{
1036 if (!isValid())
1037 return -1.0;
1038
1040}
qreal nearestPoint(const QList< QPointF > controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)

References controlPoints(), isValid(), and KisBezierUtils::nearestPoint().

◆ operator=()

KoPathSegment & KoPathSegment::operator= ( const KoPathSegment & other)

Assigns segment.

Definition at line 356 of file KoPathSegment.cpp.

357{
358 if (this == &rhs)
359 return (*this);
360
361 if (! rhs.first() || rhs.first()->parent())
362 setFirst(rhs.first());
363 else
364 setFirst(new KoPathPoint(*rhs.first()));
365
366 if (! rhs.second() || rhs.second()->parent())
367 setSecond(rhs.second());
368 else
369 setSecond(new KoPathPoint(*rhs.second()));
370
371 return (*this);
372}

References first, KoPathPoint::parent(), second, setFirst(), and setSecond().

◆ operator==()

bool KoPathSegment::operator== ( const KoPathSegment & other) const

Compare operator.

Definition at line 412 of file KoPathSegment.cpp.

413{
414 if (!isValid() && !rhs.isValid())
415 return true;
416 if (isValid() && !rhs.isValid())
417 return false;
418 if (!isValid() && rhs.isValid())
419 return false;
420
421 return (*first() == *rhs.first() && *second() == *rhs.second());
422}

References first, isValid(), and second.

◆ paramAtLength()

qreal KoPathSegment::paramAtLength ( qreal length,
qreal tolerance = 0.001 ) const

Returns the curve parameter at the given length of the segment

If the specified length is negative it returns 0.0. If the specified length is bigger that the actual length of the segment it return 1.0.

Parameters
lengththe length to get the curve parameter for
tolerancethe length error tolerance

Definition at line 833 of file KoPathSegment.cpp.

834{
835 const int deg = degree();
836 // invalid degree or invalid specified length
837 if (deg < 1 || length <= 0.0) {
838 return 0.0;
839 }
840
841 if (deg == 1) {
842 // make sure we return a maximum value of 1.0
843 return qMin(qreal(1.0), length / d->chordLength());
844 }
845
846 // for curves we need to make sure, that the specified length
847 // value does not exceed the actual length of the segment
848 // if that happens, we return 1.0 to avoid infinite looping
849 if (length >= d->chordLength() && length >= this->length(tolerance)) {
850 return 1.0;
851 }
852
853 qreal startT = 0.0; // interval start
854 qreal midT = 0.5; // interval center
855 qreal endT = 1.0; // interval end
856
857 // divide and conquer, split a midpoint and check
858 // on which side of the midpoint to continue
859 qreal midLength = lengthAt(0.5);
860 while (qAbs(midLength - length) / length > tolerance) {
861 if (midLength < length)
862 startT = midT;
863 else
864 endT = midT;
865
866 // new interval center
867 midT = 0.5 * (startT + endT);
868 // length at new interval center
869 midLength = lengthAt(midT);
870 }
871
872 return midT;
873}
qreal lengthAt(qreal t, qreal error=0.005) const

References d, degree(), length(), and lengthAt().

◆ pointAt()

QPointF KoPathSegment::pointAt ( qreal t) const

Returns point at given t.

Definition at line 438 of file KoPathSegment.cpp.

439{
440 if (!isValid())
441 return QPointF();
442
443 if (degree() == 1) {
444 return d->first->point() + t * (d->second->point() - d->first->point());
445 } else {
446 QPointF splitP;
447
448 d->deCasteljau(t, 0, 0, &splitP, 0, 0);
449
450 return splitP;
451 }
452}

References d, degree(), and isValid().

◆ Private()

KoPathSegment::Private ( KoPathSegment * qq,
KoPathPoint * p1,
KoPathPoint * p2 )
inline

Definition at line 21 of file KoPathSegment.cpp.

22 : first(p1),
23 second(p2),
24 q(qq)
25 {
26 }
KoPathSegment * q

◆ roots()

QList< qreal > KoPathSegment::roots ( ) const

Returns parameters for curve roots.

◆ second()

KoPathPoint * KoPathSegment::second ( ) const

Returns the second point of the segment.

◆ setFirst()

void KoPathSegment::setFirst ( KoPathPoint * first)

Sets the first segment point.

Definition at line 388 of file KoPathSegment.cpp.

389{
390 if (d->first && !d->first->parent())
391 delete d->first;
392 d->first = first;
393}

References d, and first.

◆ setSecond()

void KoPathSegment::setSecond ( KoPathPoint * second)

Sets the second segment point.

Definition at line 400 of file KoPathSegment.cpp.

401{
402 if (d->second && !d->second->parent())
403 delete d->second;
404 d->second = second;
405}

References d, and second.

◆ splitAt()

QPair< KoPathSegment, KoPathSegment > KoPathSegment::splitAt ( qreal t) const

Splits segment at given position returning the two resulting segments.

Definition at line 983 of file KoPathSegment.cpp.

984{
985 QPair<KoPathSegment, KoPathSegment> results;
986 if (!isValid())
987 return results;
988
989 if (degree() == 1) {
990 QPointF p = d->first->point() + t * (d->second->point() - d->first->point());
991 results.first = KoPathSegment(d->first->point(), p);
992 results.second = KoPathSegment(p, d->second->point());
993 } else {
994 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2;
995
996 d->deCasteljau(t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1);
997
998 if (degree() == 2) {
999 if (second()->activeControlPoint1()) {
1000 KoPathPoint *s1p1 = new KoPathPoint(0, d->first->point());
1001 KoPathPoint *s1p2 = new KoPathPoint(0, splitP);
1002 s1p2->setControlPoint1(splitCP1);
1003 KoPathPoint *s2p1 = new KoPathPoint(0, splitP);
1004 KoPathPoint *s2p2 = new KoPathPoint(0, d->second->point());
1005 s2p2->setControlPoint1(splitCP2);
1006 results.first = KoPathSegment(s1p1, s1p2);
1007 results.second = KoPathSegment(s2p1, s2p2);
1008 } else {
1009 results.first = KoPathSegment(d->first->point(), splitCP1, splitP);
1010 results.second = KoPathSegment(splitP, splitCP2, d->second->point());
1011 }
1012 } else {
1013 results.first = KoPathSegment(d->first->point(), newCP2, splitCP1, splitP);
1014 results.second = KoPathSegment(splitP, splitCP2, newCP1, d->second->point());
1015 }
1016 }
1017
1018 return results;
1019}
void setControlPoint1(const QPointF &point)
Set the control point 1.

References d, degree(), isValid(), KoPathSegment(), p, second, and KoPathPoint::setControlPoint1().

◆ toCubic()

KoPathSegment KoPathSegment::toCubic ( ) const

Returns cubic bezier curve segment of this segment.

Definition at line 731 of file KoPathSegment.cpp.

732{
733 if (! isValid())
734 return KoPathSegment();
735
736 KoPathPoint * p1 = new KoPathPoint(*d->first);
737 KoPathPoint * p2 = new KoPathPoint(*d->second);
738
739 if (degree() == 1) {
740 p1->setControlPoint2(p1->point() + 0.3 * (p2->point() - p1->point()));
741 p2->setControlPoint1(p2->point() + 0.3 * (p1->point() - p2->point()));
742 } else if (degree() == 2) {
743 /* quadric bezier (a0,a1,a2) to cubic bezier (b0,b1,b2,b3):
744 *
745 * b0 = a0
746 * b1 = a0 + 2/3 * (a1-a0)
747 * b2 = a1 + 1/3 * (a2-a1)
748 * b3 = a2
749 */
750 QPointF a1 = p1->activeControlPoint2() ? p1->controlPoint2() : p2->controlPoint1();
751 QPointF b1 = p1->point() + 2.0 / 3.0 * (a1 - p1->point());
752 QPointF b2 = a1 + 1.0 / 3.0 * (p2->point() - a1);
753 p1->setControlPoint2(b1);
754 p2->setControlPoint1(b2);
755 }
756
757 return KoPathSegment(p1, p2);
758}

References d, degree(), isValid(), KoPathSegment(), p1, and p2.

Member Data Documentation

◆ d

Private* const KoPathSegment::d
private

Definition at line 144 of file KoPathSegment.h.

◆ first

KoPathPoint * KoPathSegment::first

Definition at line 54 of file KoPathSegment.cpp.

◆ q

KoPathSegment* KoPathSegment::q

Definition at line 56 of file KoPathSegment.cpp.

◆ second

KoPathPoint * KoPathSegment::second

Definition at line 55 of file KoPathSegment.cpp.


The documentation for this class was generated from the following files: