10#include <QPainterPath>
52 void deCasteljau(qreal t, QPointF *
p1, QPointF *
p2, QPointF *
p3, QPointF *p4, QPointF *p5)
const;
59void KoPathSegment::Private::deCasteljau(qreal t, QPointF *
p1, QPointF *
p2, QPointF *
p3, QPointF *p4, QPointF *p5)
const
67 q[0] = first->point();
69 q[1] = first->activeControlPoint2() ? first->controlPoint2() : second->controlPoint1();
70 }
else if (deg == 3) {
71 q[1] = first->controlPoint2();
72 q[2] = second->controlPoint1();
75 q[deg] = second->point();
82 for (
unsigned short j = 1; j <= deg; ++j) {
83 for (
unsigned short i = 0; i <= deg - j; ++i) {
84 q[i] = (1.0 - t) * q[i] + t * q[i + 1];
96 }
else if (deg == 3) {
121 if (!xAxisCrossings) {
124 else if (xAxisCrossings == 1 && q->isFlat(0.01 / chordLength())) {
127 QPointF chord = second->point() - first->point();
128 QPointF segStart = first->point();
129 rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y());
133 QPair<KoPathSegment, KoPathSegment> splitSegments = q->splitAt(0.5);
134 rootParams += splitSegments.first.d->roots();
135 rootParams += splitSegments.second.d->roots();
143 int deg = q->degree();
176 QPointF cp = first->activeControlPoint2() ?
177 first->controlPoint2() : second->controlPoint1();
178 QPointF x0 = cp - first->point();
179 QPointF x1 = second->point() - cp;
186 params.append(-c.x() / a.x());
188 params.append(-c.y() / a.y());
189 }
else if (deg == 3) {
203 QPointF x0 = first->controlPoint2() - first->point();
204 QPointF x1 = second->controlPoint1() - first->controlPoint2();
205 QPointF x2 = second->point() - second->controlPoint1();
208 QPointF a = x2 - 2.0 * x1 + x0;
209 QPointF
b = 2.0 * x1 - 2.0 * x0;
214 params.append(- c.x() /
b.x());
216 qreal rx =
b.x() *
b.x() - 4.0 * a.x() * c.x();
219 params.append((-
b.x() + sqrt(rx)) / (2.0*a.x()));
220 params.append((-
b.x() - sqrt(rx)) / (2.0*a.x()));
225 params.append(- c.y() /
b.y());
227 qreal ry =
b.y() *
b.y() - 4.0 * a.y() * c.y();
230 params.append((-
b.y() + sqrt(ry)) / (2.0*a.y()));
231 params.append((-
b.y() - sqrt(ry)) / (2.0*a.y()));
238qreal KoPathSegment::Private::distanceFromChord(
const QPointF &point)
const
241 QPointF chord = second->point() - first->point();
243 QPointF relPoint = point - first->point();
245 qreal
scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
246 scale /= chord.x() * chord.x() + chord.y() * chord.y();
249 QPointF diffVec =
scale * chord - relPoint;
252 qreal
distance = sqrt(diffVec.x() * diffVec.x() + diffVec.y() * diffVec.y());
255 if (chord.x()*relPoint.y() - chord.y()*relPoint.x() > 0) {
262qreal KoPathSegment::Private::chordLength()
const
264 QPointF chord = second->point() - first->point();
265 return sqrt(chord.x() * chord.x() + chord.y() * chord.y());
284 QPointF
A = first->point();
285 QPointF
B = second->point();
289 qreal denom = (
B.x() -
A.x()) * (
D.y() -
C.y()) - (
B.y() -
A.y()) * (
D.x() -
C.x());
290 qreal num_r = (
A.y() -
C.y()) * (
D.x() -
C.x()) - (
A.x() -
C.x()) * (
D.y() -
C.y());
292 if (denom == 0.0 && num_r == 0.0)
295 qreal num_s = (
A.y() -
C.y()) * (
B.x() -
A.x()) - (
A.x() -
C.x()) * (
B.y() -
A.y());
296 qreal
r = num_r / denom;
297 qreal s = num_s / denom;
300 if (r < 0.0 || r > 1.0)
302 if (s < 0.0 || s > 1.0)
306 isects.append(
A + r * (
B -
A));
314 : d(new
Private(this, first, second))
335 d->first->setPoint(
p0);
336 d->second->setPoint(
p1);
342 d->first->setPoint(
p0);
343 d->first->setControlPoint2(
p1);
344 d->second->setPoint(
p2);
350 d->first->setPoint(
p0);
351 d->first->setControlPoint2(
p1);
352 d->second->setControlPoint1(
p2);
353 d->second->setPoint(
p3);
376 if (
d->first && !
d->first->parent())
378 if (
d->second && !
d->second->parent())
390 if (
d->first && !
d->first->parent())
402 if (
d->second && !
d->second->parent())
409 return (
d->first &&
d->second);
426 if (!
d->first || !
d->second)
429 bool c1 =
d->first->activeControlPoint2();
430 bool c2 =
d->second->activeControlPoint1();
444 return d->first->point() + t * (
d->second->point() -
d->first->point());
448 d->deCasteljau(t, 0, 0, &splitP, 0, 0);
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()));
470 if (bbox.height() == 0.0)
472 if (bbox.width() == 0.0)
484 QRectF
rect = QRectF(
d->first->point(),
d->second->point()).normalized();
488 if (
rect.height() == 0.0)
490 if (
rect.width() == 0.0)
498 foreach (qreal t,
d->extrema()) {
499 if (t >= 0.0 && t <= 1.0) {
502 rect.setRight(qMax(
rect.right(),
p.x()));
504 rect.setBottom(qMax(
rect.bottom(),
p.y()));
523 int degree2 = segment.
degree();
529 if (!myBound.intersects(otherBound)) {
535 if (degree1 == 1 && degree2 == 1) {
537 isects +=
d->linesIntersection(segment);
547 }
else if (degree1 == 2) {
549 if (
d->first->activeControlPoint2())
550 d1 =
d->distanceFromChord(
d->first->controlPoint2());
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));
556 qreal d1 =
d->distanceFromChord(
d->first->controlPoint2());
557 qreal d2 =
d->distanceFromChord(
d->second->controlPoint1());
559 dmin = 0.75 * qMin(qreal(0.0), qMin(d1, d2));
560 dmax = 0.75 * qMax(qreal(0.0), qMax(d1, d2));
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));
590 QPointF
p0(0.0,
d->distanceFromChord(segment.
first()->
point()));
593 }
else if (degree2 == 2) {
594 QPointF
p0(0.0,
d->distanceFromChord(segment.
first()->
point()));
600 }
else if (degree2 == 3) {
601 QPointF
p0(0.0,
d->distanceFromChord(segment.
first()->
point()));
616 int hullCount = hull.count();
617 qreal tmin = 1.0, tmax = 0.0;
618 bool intersectionsFoundMax =
false;
619 bool intersectionsFoundMin =
false;
621 for (
int i = 0; i < hullCount; ++i) {
622 QPointF
p1 = hull[i];
623 QPointF
p2 = hull[(i+1) % hullCount];
626 if (
p1.y() > dmax &&
p2.y() > dmax)
629 if (
p1.y() < dmin &&
p2.y() < dmin)
631 if (
p1.x() ==
p2.x()) {
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;
642 intersectionsFoundMin =
true;
646 }
else if (
p1.y() ==
p2.y()) {
648 if (
p1.y() == dmin ||
p1.y() == dmax) {
649 if (
p1.y() == dmin) {
650 intersectionsFoundMin =
true;
654 intersectionsFoundMax =
true;
658 tmin = qMin(tmin,
p1.x());
659 tmin = qMin(tmin,
p2.x());
660 tmax = qMax(tmax,
p1.x());
661 tmax = qMax(tmax,
p2.x());
664 qreal dx =
p2.x() -
p1.x();
665 qreal dy =
p2.y() -
p1.y();
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;
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;
685 bool intersectionsFound = intersectionsFoundMin && intersectionsFoundMax;
690 if (!intersectionsFound || (1.0 - (tmax - tmin)) <= 0.2) {
695 QPair<KoPathSegment, KoPathSegment> parts =
splitAt(0.5);
696 if (
d->chordLength() < 1e-5)
697 isects += parts.first.second()->point();
702 }
else if (qAbs(tmin - tmax) < 1e-5) {
705 isects.append(segment.
pointAt(tmin));
707 QPair<KoPathSegment, KoPathSegment> clip1 = segment.
splitAt(tmin);
709 qreal t = (tmax - tmin) / (1.0 - tmin);
710 QPair<KoPathSegment, KoPathSegment> clip2 = clip1.second.splitAt(t);
712 isects += clip2.first.intersections(*
this);
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) {
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);
795 qreal chordLen =
d->chordLength();
802 qreal polyLength = 0.0;
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());
809 if ((polyLength - chordLen) > error) {
811 QPair<KoPathSegment, KoPathSegment> parts =
splitAt(0.5);
812 return parts.first.length(error) + parts.second.length(error);
816 return 0.5 * chordLen + 0.5 * polyLength;
818 return (2.0 * chordLen + polyLength) / 3.0;
829 QPair<KoPathSegment, KoPathSegment> parts =
splitAt(t);
830 return parts.first.length(error);
837 if (deg < 1 ||
length <= 0.0) {
843 return qMin(qreal(1.0),
length /
d->chordLength());
849 if (
length >=
d->chordLength() &&
length >= this->length(tolerance)) {
867 midT = 0.5 * (startT + endT);
888 QPointF chord =
d->second->point() -
d->first->point();
890 qreal chordAngle = atan2(chord.y(), chord.x());
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());
901 foreach (qreal t, s.
d->extrema()) {
902 if (t >= 0.0 && t <= 1.0) {
904 qreal dist = s.
d->distanceFromChord(
p);
905 minDist = qMin(dist, minDist);
906 maxDist = qMax(dist, maxDist);
910 return (maxDist - minDist <= tolerance);
919 hull.append(
d->first->point());
920 hull.append(
d->second->point());
921 }
else if (deg == 2) {
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();
928 bool pIsRight = (chord.x() * relP.y() - chord.y() * relP.x() > 0);
929 hull.append(
d->first->point());
932 hull.append(
d->second->point());
935 }
else if (deg == 3) {
937 QPointF chord =
d->second->point() -
d->first->point();
938 QPointF relP1 =
d->first->controlPoint2() -
d->first->point();
940 bool p1IsRight = (chord.x() * relP1.y() - chord.y() * relP1.x() > 0);
941 hull.append(
d->first->point());
943 hull.append(
d->first->controlPoint2());
944 hull.append(
d->second->point());
946 hull.append(
d->first->controlPoint2());
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);
957 for (
int i = 0; i < 3; ++i) {
958 int prev = (3 + i - 1) % 3;
959 int next = (i + 1) % 3;
961 if (! rightOfEdge[prev] && rightOfEdge[i] && ! rightOfEdge[next]) {
963 hull.insert(i + 1, lastPoint);
967 if (rightOfEdge[i] && rightOfEdge[next]) {
969 hull[i+1] = lastPoint;
973 if (rightOfEdge[i] && rightOfEdge[prev]) {
985 QPair<KoPathSegment, KoPathSegment> results;
990 QPointF
p =
d->first->point() + t * (
d->second->point() -
d->first->point());
994 QPointF newCP2, newCP1, splitP, splitCP1, splitCP2;
996 d->deCasteljau(t, &newCP2, &splitCP1, &splitP, &splitCP2, &newCP1);
999 if (
second()->activeControlPoint1()) {
1009 results.first =
KoPathSegment(
d->first->point(), splitCP1, splitP);
1010 results.second =
KoPathSegment(splitP, splitCP2,
d->second->point());
1013 results.first =
KoPathSegment(
d->first->point(), newCP2, splitCP1, splitP);
1014 results.second =
KoPathSegment(splitP, splitCP2, newCP1,
d->second->point());
1025 if (
d->first->activeControlPoint2())
1027 if (
d->second->activeControlPoint1())
1044 if (t <= 0.0 || t >= 1.0)
1055 QPointF c1 =
p1 - (1.0-t) * (1.0-t)*
p0 - t * t *
p2;
1057 qreal denom = 2.0 * t * (1.0-t);
1066void KoPathSegment::printDebug()
const
1076 }
else if (deg == 2) {
1077 if (
d->first->activeControlPoint2())
1080 debugFlake <<
"P1:" <<
d->second->controlPoint1();
1082 }
else if (deg == 3) {
1084 debugFlake <<
"P2:" <<
d->second->controlPoint1();
qreal length(const QPointF &vec)
qreal distance(const QPointF &p1, const QPointF &p2)
qreal D(qreal t, const QPointF &P0, const QPointF &P1, const QPointF &P2, const QPointF &P3, const QPointF &p)
A KoPathPoint represents a point in a path.
void setControlPoint1(const QPointF &point)
Set the control point 1.
KoPathShape * parent() const
Get the path shape the point belongs to.
A KoPathSegment consist of two neighboring KoPathPoints.
QList< qreal > roots() const
Returns parameters for curve roots.
QPair< KoPathSegment, KoPathSegment > splitAt(qreal t) const
Splits segment at given position returning the two resulting segments.
void setSecond(KoPathPoint *second)
Sets the second segment point.
KoPathSegment mapped(const QTransform &matrix) const
Returns transformed segment.
qreal length(qreal error=0.005) const
QRectF controlPointRect() const
Returns the control point bounding rect.
qreal paramAtLength(qreal length, qreal tolerance=0.001) const
qreal chordLength() const
Returns the chord length, i.e. the distance between first and last control point.
qreal lengthAt(qreal t, qreal error=0.005) const
KoPathSegment & operator=(const KoPathSegment &other)
Assigns segment.
Private(KoPathSegment *qq, KoPathPoint *p1, KoPathPoint *p2)
KoPathSegment(KoPathPoint *first=0, KoPathPoint *second=0)
bool isFlat(qreal tolerance=0.01) const
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.
static KoPathSegment interpolate(const QPointF &p0, const QPointF &p1, const QPointF &p2, qreal t)
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 nearestPoint(const QPointF &point) const
QList< QPointF > linesIntersection(const KoPathSegment &segment) const
Returns intersection of lines if one exists.
void deCasteljau(qreal t, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4, QPointF *p5) const
~KoPathSegment()
Destroys the path segment.
bool operator==(const KoPathSegment &other) const
Compare operator.
void setFirst(KoPathPoint *first)
Sets the first segment point.
qreal distanceFromChord(const QPointF &point) const
calculates signed distance of given point from segment chord
bool isValid() const
Returns if segment is valid, e.g. has two valid points.
QRectF boundingRect() const
Returns the axis aligned tight bounding rect.
QList< QPointF > controlPoints() const
Returns ordered list of control points.
QList< qreal > extrema() const
Returns parameters for curve extrema.
KoPathSegment toCubic() const
Returns cubic bezier curve segment of this segment.
int controlPolygonZeros(const QList< QPointF > &controlPoints)
qreal nearestPoint(const QList< QPointF > controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)