Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBezierUtils.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2008-2009 Jan Hambrecht <jaham@gmx.net>
3 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "KisBezierUtils.h"
9
10#include <tuple>
11#include <QStack>
12#include <QDebug>
13#include "kis_debug.h"
14
15#include "KisBezierPatch.h"
16#include <iostream>
17
18#include <config-gsl.h>
19
20#ifdef HAVE_GSL
21#include <gsl/gsl_multimin.h>
22#endif
23
24#include <Eigen/Dense>
25
26namespace KisBezierUtils
27{
28
29QVector<qreal> linearizeCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal eps)
30{
31 const qreal minStepSize = 2.0 / kisDistance(p0, p3);
32
33 QVector<qreal> steps;
34 steps << 0.0;
35
36
38 stackedPoints.push(std::make_tuple(p3, 3 * (p3 - p2), 1.0));
39
40 QPointF lastP = p0;
41 QPointF lastD = 3 * (p1 - p0);
42 qreal lastT = 0.0;
43
44 while (!stackedPoints.isEmpty()) {
45 QPointF p = std::get<0>(stackedPoints.top());
46 QPointF d = std::get<1>(stackedPoints.top());
47 qreal t = std::get<2>(stackedPoints.top());
48
49 if (t - lastT < minStepSize ||
50 isLinearSegmentByDerivatives(lastP, lastD, p, d, eps)) {
51
52 lastP = p;
53 lastD = d;
54 lastT = t;
55 steps << t;
56 stackedPoints.pop();
57 } else {
58 t = 0.5 * (lastT + t);
59 p = bezierCurve(p0, p1, p2, p3, t);
60 d = bezierCurveDeriv(p0, p1, p2, p3, t);
61
62 stackedPoints.push(std::make_tuple(p, d, t));
63 }
64 }
65
66 return steps;
67}
68
70{
71 QVector<qreal> result;
72
73 std::merge(a.constBegin(), a.constEnd(),
74 b.constBegin(), b.constEnd(),
75 std::back_inserter(result));
76 result.erase(
77 std::unique(result.begin(), result.end(),
78 [] (qreal x, qreal y) { return qFuzzyCompare(x, y); }),
79 result.end());
80
81 return result;
82}
83
85{
86private:
88 const int MaxRecursionDepth = 64;
90 const qreal FlatnessTolerance = ldexp(1.0,-MaxRecursionDepth-1);
91
92public:
93 BezierSegment(int degree = 0, QPointF *p = 0)
94 {
95 if (degree) {
96 for (int i = 0; i <= degree; ++i)
97 points.append(p[i]);
98 }
99 }
100
102 {
103 points.clear();
104 if (degree) {
105 for (int i = 0; i <= degree; ++i)
106 points.append(QPointF());
107 }
108 }
109
110 int degree() const
111 {
112 return points.count() - 1;
113 }
114
115 QPointF point(int index) const
116 {
117 if (static_cast<int>(index) > degree())
118 return QPointF();
119
120 return points[index];
121 }
122
123 void setPoint(int index, const QPointF &p)
124 {
125 if (static_cast<int>(index) > degree())
126 return;
127
128 points[index] = p;
129 }
130
131 QPointF evaluate(qreal t, BezierSegment *left, BezierSegment *right) const
132 {
133 int deg = degree();
134 if (! deg)
135 return QPointF();
136
137 QVector<QVector<QPointF> > Vtemp(deg + 1);
138 for (int i = 0; i <= deg; ++i)
139 Vtemp[i].resize(deg + 1);
140
141 /* Copy control points */
142 for (int j = 0; j <= deg; j++) {
143 Vtemp[0][j] = points[j];
144 }
145
146 /* Triangle computation */
147 for (int i = 1; i <= deg; i++) {
148 for (int j =0 ; j <= deg - i; j++) {
149 Vtemp[i][j].rx() = (1.0 - t) * Vtemp[i-1][j].x() + t * Vtemp[i-1][j+1].x();
150 Vtemp[i][j].ry() = (1.0 - t) * Vtemp[i-1][j].y() + t * Vtemp[i-1][j+1].y();
151 }
152 }
153
154 if (left) {
155 left->setDegree(deg);
156 for (int j = 0; j <= deg; j++) {
157 left->setPoint(j, Vtemp[j][0]);
158 }
159 }
160 if (right) {
161 right->setDegree(deg);
162 for (int j = 0; j <= deg; j++) {
163 right->setPoint(j, Vtemp[deg-j][j]);
164 }
165 }
166
167 return (Vtemp[deg][0]);
168 }
169
170 QList<qreal> roots(int depth = 0) const
171 {
172 QList<qreal> rootParams;
173
174 if (! degree())
175 return rootParams;
176
177 // Calculate how often the control polygon crosses the x-axis
178 // This is the upper limit for the number of roots.
179 int xAxisCrossings = controlPolygonZeros(points);
180
181 if (! xAxisCrossings) {
182 // No solutions.
183 return rootParams;
184 }
185 else if (xAxisCrossings == 1) {
186 // Exactly one solution.
187
188 // Stop recursion when the tree is deep enough
189 if (depth >= MaxRecursionDepth) {
190 // if deep enough, return 1 solution at midpoint
191 rootParams.append((points.first().x() + points.last().x()) / 2.0);
192 return rootParams;
193 }
194 else if (isFlat(FlatnessTolerance)) {
195 // Calculate intersection of chord with x-axis.
196 QPointF chord = points.last() - points.first();
197 QPointF segStart = points.first();
198 rootParams.append((chord.x() * segStart.y() - chord.y() * segStart.x()) / - chord.y());
199 return rootParams;
200 }
201 }
202
203 // Many solutions. Do recursive midpoint subdivision.
204 BezierSegment left, right;
205 evaluate(0.5, &left, &right);
206 rootParams += left.roots(depth+1);
207 rootParams += right.roots(depth+1);
208
209 return rootParams;
210 }
211
212 static uint controlPolygonZeros(const QList<QPointF> &controlPoints)
213 {
214 int controlPointCount = controlPoints.count();
215 if (controlPointCount < 2)
216 return 0;
217
218 int signChanges = 0;
219
220 int currSign = controlPoints[0].y() < 0.0 ? -1 : 1;
221 int oldSign;
222
223 for (short i = 1; i < controlPointCount; ++i) {
224 oldSign = currSign;
225 currSign = controlPoints[i].y() < 0.0 ? -1 : 1;
226
227 if (currSign != oldSign) {
228 ++signChanges;
229 }
230 }
231
232
233 return signChanges;
234 }
235
236 int isFlat (qreal tolerance) const
237 {
238 int deg = degree();
239
240 // Find the perpendicular distance from each interior control point to
241 // the line connecting points[0] and points[degree]
242 qreal *distance = new qreal[deg + 1];
243
244 // Derive the implicit equation for line connecting first and last control points
245 qreal a = points[0].y() - points[deg].y();
246 qreal b = points[deg].x() - points[0].x();
247 qreal c = points[0].x() * points[deg].y() - points[deg].x() * points[0].y();
248
249 qreal abSquared = (a * a) + (b * b);
250
251 for (int i = 1; i < deg; i++) {
252 // Compute distance from each of the points to that line
253 distance[i] = a * points[i].x() + b * points[i].y() + c;
254 if (distance[i] > 0.0) {
255 distance[i] = (distance[i] * distance[i]) / abSquared;
256 }
257 if (distance[i] < 0.0) {
258 distance[i] = -((distance[i] * distance[i]) / abSquared);
259 }
260 }
261
262
263 // Find the largest distance
264 qreal max_distance_above = 0.0;
265 qreal max_distance_below = 0.0;
266 for (int i = 1; i < deg; i++) {
267 if (distance[i] < 0.0) {
268 max_distance_below = qMin(max_distance_below, distance[i]);
269 }
270 if (distance[i] > 0.0) {
271 max_distance_above = qMax(max_distance_above, distance[i]);
272 }
273 }
274 delete [] distance;
275
276 // Implicit equation for zero line
277 qreal a1 = 0.0;
278 qreal b1 = 1.0;
279 qreal c1 = 0.0;
280
281 // Implicit equation for "above" line
282 qreal a2 = a;
283 qreal b2 = b;
284 qreal c2 = c + max_distance_above;
285
286 qreal det = a1 * b2 - a2 * b1;
287 qreal dInv = 1.0/det;
288
289 qreal intercept_1 = (b1 * c2 - b2 * c1) * dInv;
290
291 // Implicit equation for "below" line
292 a2 = a;
293 b2 = b;
294 c2 = c + max_distance_below;
295
296 det = a1 * b2 - a2 * b1;
297 dInv = 1.0/det;
298
299 qreal intercept_2 = (b1 * c2 - b2 * c1) * dInv;
300
301 // Compute intercepts of bounding box
302 qreal left_intercept = qMin(intercept_1, intercept_2);
303 qreal right_intercept = qMax(intercept_1, intercept_2);
304
305 qreal error = 0.5 * (right_intercept-left_intercept);
306
307 return (error < tolerance);
308 }
309
310#ifndef NDEBUG
311 void printDebug() const
312 {
313 int index = 0;
314 Q_FOREACH (const QPointF &p, points) {
315 qDebug() << QString("P%1 ").arg(index++) << p;
316 }
317 }
318#endif
319
320private:
322};
323
324qreal nearestPoint(const QList<QPointF> controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)
325{
326 const int deg = controlPoints.size() - 1;
327
328 // use shortcut for line segments
329 if (deg == 1) {
330 // the segments chord
331 QPointF chord = controlPoints.last() - controlPoints.first();
332 // the point relative to the segment
333 QPointF relPoint = point - controlPoints.first();
334 // project point to chord (dot product)
335 qreal scale = chord.x() * relPoint.x() + chord.y() * relPoint.y();
336 // normalize using the chord length
337 scale /= chord.x() * chord.x() + chord.y() * chord.y();
338
339 if (scale < 0.0) {
340 return 0.0;
341 } else if (scale > 1.0) {
342 return 1.0;
343 } else {
344 return scale;
345 }
346 }
347
348 /* This function solves the "nearest point on curve" problem. That means, it
349 * calculates the point q (to be precise: it's parameter t) on this segment, which
350 * is located nearest to the input point P.
351 * The basic idea is best described (because it is freely available) in "Phoenix:
352 * An Interactive Curve Design System Based on the Automatic Fitting of
353 * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of
354 * Washington).
355 *
356 * For the nearest point q = C(t) on this segment, the first derivative is
357 * orthogonal to the distance vector "C(t) - P". In other words we are looking for
358 * solutions of f(t) = (C(t) - P) * C'(t) = 0.
359 * (C(t) - P) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a
360 * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions.
361 * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method".
362 * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j):
363 *
364 * n n-1
365 * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t)
366 * i=0 j=0
367 *
368 * n n-1
369 * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t)
370 * i=0 j=0
371 *
372 * with w_{ij} = c_i * d_j * z_{ij} and
373 *
374 * BinomialCoeff(n, i) * BinomialCoeff(n - i ,j)
375 * z_{ij} = -----------------------------------------------
376 * BinomialCoeff(2n - 1, i + j)
377 *
378 * This Bernstein-Bezier polynom representation can now be solved for its roots.
379 */
380
381 QList<QPointF> ctlPoints = controlPoints;
382
383 // Calculate the c_i = point(i) - P.
384 QPointF * c_i = new QPointF[ deg + 1 ];
385
386 for (int i = 0; i <= deg; ++i) {
387 c_i[ i ] = ctlPoints[ i ] - point;
388 }
389
390 // Calculate the d_j = point(j + 1) - point(j).
391 QPointF *d_j = new QPointF[deg];
392
393 for (int j = 0; j <= deg - 1; ++j) {
394 d_j[j] = 3.0 * (ctlPoints[j+1] - ctlPoints[j]);
395 }
396
397 // Calculate the dot products of c_i and d_i.
398 qreal *products = new qreal[deg * (deg + 1)];
399
400 for (int j = 0; j <= deg - 1; ++j) {
401 for (int i = 0; i <= deg; ++i) {
402 products[j * (deg + 1) + i] = d_j[j].x() * c_i[i].x() + d_j[j].y() * c_i[i].y();
403 }
404 }
405
406 // We don't need the c_i and d_i anymore.
407 delete[] d_j ;
408 delete[] c_i ;
409
410 // Calculate the control points of the new 2n-1th degree curve.
411 BezierSegment newCurve;
412 newCurve.setDegree(2 * deg - 1);
413 // Set up control points in the (u, f(u))-plane.
414 for (unsigned short u = 0; u <= 2 * deg - 1; ++u) {
415 newCurve.setPoint(u, QPointF(static_cast<qreal>(u) / static_cast<qreal>(2 * deg - 1), 0.0));
416 }
417
418 // Precomputed "z" for cubics
419 static const qreal z3[3*4] = {1.0, 0.6, 0.3, 0.1, 0.4, 0.6, 0.6, 0.4, 0.1, 0.3, 0.6, 1.0};
420 // Precomputed "z" for quadrics
421 static const qreal z2[2*3] = {1.0, 2./3., 1./3., 1./3., 2./3., 1.0};
422
423 const qreal *z = deg == 3 ? z3 : z2;
424
425 // Set f(u)-values.
426 for (int k = 0; k <= 2 * deg - 1; ++k) {
427 int min = qMin(k, deg);
428
429 for (unsigned short i = qMax(0, k - (deg - 1)); i <= min; ++i) {
430 unsigned short j = k - i;
431
432 // p_k += products[j][i] * z[j][i].
433 QPointF currentPoint = newCurve.point(k);
434 currentPoint.ry() += products[j * (deg + 1) + i] * z[j * (deg + 1) + i];
435 newCurve.setPoint(k, currentPoint);
436 }
437 }
438
439 // We don't need the c_i/d_i dot products and the z_{ij} anymore.
440 delete[] products;
441
442 // Find roots.
443 QList<qreal> rootParams = newCurve.roots();
444
445 // Now compare the distances of the candidate points.
446
447 // First candidate is the previous knot.
448 qreal distanceSquared = kisSquareDistance(point, controlPoints.first());
449 qreal minDistanceSquared = distanceSquared;
450 qreal resultParam = 0.0;
451 if (resultDistance) {
452 *resultDistance = std::sqrt(distanceSquared);
453 }
454 if (resultPoint) {
455 *resultPoint = controlPoints.first();
456 }
457
458 // Iterate over the found candidate params.
459 Q_FOREACH (qreal root, rootParams) {
460 const QPointF rootPoint = bezierCurve(controlPoints, root);
461 distanceSquared = kisSquareDistance(point, rootPoint);
462
463 if (distanceSquared < minDistanceSquared) {
464 minDistanceSquared = distanceSquared;
465 resultParam = root;
466 if (resultDistance) {
467 *resultDistance = std::sqrt(distanceSquared);
468 }
469 if (resultPoint) {
470 *resultPoint = rootPoint;
471 }
472 }
473 }
474
475 // Last candidate is the knot.
476 distanceSquared = kisSquareDistance(point, controlPoints.last());
477 if (distanceSquared < minDistanceSquared) {
478 minDistanceSquared = distanceSquared;
479 resultParam = 1.0;
480 if (resultDistance) {
481 *resultDistance = std::sqrt(distanceSquared);
482 }
483 if (resultPoint) {
484 *resultPoint = controlPoints.last();
485 }
486 }
487
488 return resultParam;
489}
490
491int controlPolygonZeros(const QList<QPointF> &controlPoints)
492{
493 return static_cast<int>(BezierSegment::controlPolygonZeros(controlPoints));
494}
495
496namespace {
497
498struct Params2D {
499 QPointF p0, p1, p2, p3; // top curve
500 QPointF q0, q1, q2, q3; // bottom curve
501 QPointF r0, r1, r2, r3; // left curve
502 QPointF s0, s1, s2, s3; // right curve
503
504 QPointF dstPoint;
505};
506
507struct LevelBasedPatchMethod
508{
509 LevelBasedPatchMethod(qreal u, qreal v, const Params2D &p)
510 {
511 M_3 << 1, 0, 0, 0,
512 -3, 3, 0, 0,
513 3, -6, 3, 0,
514 -1, 3, -3, 1;
515
516 M_3rel2abs << 1, 0, 0, 0,
517 1, 1, 0, 0,
518 0, 0, 1, 1,
519 0, 0, 0, 1;
520
521 M_1 << -1, 1, 0, 0,
522 1, -1, -1, 1;
523
524 PQ_left << p.p0.x(), p.p0.y(),
525 p.p1.x(), p.p1.y(),
526 p.q0.x(), p.q0.y(),
527 p.q1.x(), p.q1.y();
528
529 PQ_right << p.p3.x(), p.p3.y(),
530 p.p2.x(), p.p2.y(),
531 p.q3.x(), p.q3.y(),
532 p.q2.x(), p.q2.y();
533
534 RS_top << p.r0.x(), p.r0.y(),
535 p.r1.x(), p.r1.y(),
536 p.s0.x(), p.s0.y(),
537 p.s1.x(), p.s1.y();
538
539 RS_bottom << p.r3.x(), p.r3.y(),
540 p.r2.x(), p.r2.y(),
541 p.s3.x(), p.s3.y(),
542 p.s2.x(), p.s2.y();
543
544 R << p.r0.x(), p.r0.y(),
545 p.r1.x(), p.r1.y(),
546 p.r2.x(), p.r2.y(),
547 p.r3.x(), p.r3.y();
548
549 S << p.s0.x(), p.s0.y(),
550 p.s1.x(), p.s1.y(),
551 p.s2.x(), p.s2.y(),
552 p.s3.x(), p.s3.y();
553
554 P << p.p0.x(), p.p0.y(),
555 p.p1.x(), p.p1.y(),
556 p.p2.x(), p.p2.y(),
557 p.p3.x(), p.p3.y();
558
559 Q << p.q0.x(), p.q0.y(),
560 p.q1.x(), p.q1.y(),
561 p.q2.x(), p.q2.y(),
562 p.q3.x(), p.q3.y();
563
564 T_u3 << 1, u, pow2(u), pow3(u);
565 T_v3 << 1, v, pow2(v), pow3(v);
566 T_dot_u3 << 0, 1, 2 * u, 3 * pow2(u);
567 T_dot_v3 << 0, 1, 2 * v, 3 * pow2(v);
568
569 T_u1 << 1, u;
570 T_v1 << 1, v;
571 T_dot_u1 << 0, 1;
572 T_dot_v1 << 0, 1;
573 }
574
575 Eigen::Matrix4d M_3;
576 Eigen::Matrix4d M_3rel2abs;
577 Eigen::Matrix<double, 2, 4> M_1;
578
579
580 Eigen::Matrix<double, 4, 2> PQ_left;
581 Eigen::Matrix<double, 4, 2> PQ_right;
582 Eigen::Matrix<double, 4, 2> RS_top;
583 Eigen::Matrix<double, 4, 2> RS_bottom;
584
585 Eigen::Matrix<double, 4, 2> R;
586 Eigen::Matrix<double, 4, 2> S;
587 Eigen::Matrix<double, 4, 2> P;
588 Eigen::Matrix<double, 4, 2> Q;
589
590 Eigen::Matrix<double, 1, 4> T_u3;
591 Eigen::Matrix<double, 1, 4> T_v3;
592 Eigen::Matrix<double, 1, 4> T_dot_u3;
593 Eigen::Matrix<double, 1, 4> T_dot_v3;
594
595 Eigen::Matrix<double, 1, 2> T_u1;
596 Eigen::Matrix<double, 1, 2> T_v1;
597 Eigen::Matrix<double, 1, 2> T_dot_u1;
598 Eigen::Matrix<double, 1, 2> T_dot_v1;
599
600 QPointF value() const {
601 Eigen::Matrix<double, 1, 2> L_1 = T_v3 * M_3 * R;
602 Eigen::Matrix<double, 1, 2> L_2 = T_v1 * M_1 * PQ_left;
603 Eigen::Matrix<double, 1, 2> L_3 = T_v1 * M_1 * PQ_right;
604 Eigen::Matrix<double, 1, 2> L_4 = T_v3 * M_3 * S;
605
606 Eigen::Matrix<double, 4, 2> L_controls;
607 L_controls << L_1, L_2, L_3, L_4;
608
609 Eigen::Matrix<double, 1, 2> L = T_u3 * M_3 * M_3rel2abs * L_controls;
610
611
612 Eigen::Matrix<double, 1, 2> H_1 = T_u3 * M_3 * P;
613 Eigen::Matrix<double, 1, 2> H_2 = T_u1 * M_1 * RS_top;
614 Eigen::Matrix<double, 1, 2> H_3 = T_u1 * M_1 * RS_bottom;
615 Eigen::Matrix<double, 1, 2> H_4 = T_u3 * M_3 * Q;
616
617 Eigen::Matrix<double, 4, 2> H_controls;
618 H_controls << H_1, H_2, H_3, H_4;
619
620 Eigen::Matrix<double, 1, 2> H = T_v3 * M_3 * M_3rel2abs * H_controls;
621
622 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H);
623
624 return QPointF(result(0,0), result(0,1));
625 }
626
627 QPointF diffU() const {
628 Eigen::Matrix<double, 1, 2> L_1 = T_v3 * M_3 * R;
629 Eigen::Matrix<double, 1, 2> L_2 = T_v1 * M_1 * PQ_left;
630 Eigen::Matrix<double, 1, 2> L_3 = T_v1 * M_1 * PQ_right;
631 Eigen::Matrix<double, 1, 2> L_4 = T_v3 * M_3 * S;
632
633 Eigen::Matrix<double, 4, 2> L_controls;
634 L_controls << L_1, L_2, L_3, L_4;
635
636 Eigen::Matrix<double, 1, 2> L = T_dot_u3 * M_3 * M_3rel2abs * L_controls;
637
638
639 Eigen::Matrix<double, 1, 2> H_1 = T_dot_u3 * M_3 * P;
640 Eigen::Matrix<double, 1, 2> H_2 = T_dot_u1 * M_1 * RS_top;
641 Eigen::Matrix<double, 1, 2> H_3 = T_dot_u1 * M_1 * RS_bottom;
642 Eigen::Matrix<double, 1, 2> H_4 = T_dot_u3 * M_3 * Q;
643
644 Eigen::Matrix<double, 4, 2> H_controls;
645 H_controls << H_1, H_2, H_3, H_4;
646
647 Eigen::Matrix<double, 1, 2> H = T_v3 * M_3 * M_3rel2abs * H_controls;
648
649 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H);
650 return QPointF(result(0,0), result(0,1));
651 }
652
653 QPointF diffV() const {
654 Eigen::Matrix<double, 1, 2> L_1 = T_dot_v3 * M_3 * R;
655 Eigen::Matrix<double, 1, 2> L_2 = T_dot_v1 * M_1 * PQ_left;
656 Eigen::Matrix<double, 1, 2> L_3 = T_dot_v1 * M_1 * PQ_right;
657 Eigen::Matrix<double, 1, 2> L_4 = T_dot_v3 * M_3 * S;
658
659 Eigen::Matrix<double, 4, 2> L_controls;
660 L_controls << L_1, L_2, L_3, L_4;
661
662 Eigen::Matrix<double, 1, 2> L = T_u3 * M_3 * M_3rel2abs * L_controls;
663
664 Eigen::Matrix<double, 1, 2> H_1 = T_u3 * M_3 * P;
665 Eigen::Matrix<double, 1, 2> H_2 = T_u1 * M_1 * RS_top;
666 Eigen::Matrix<double, 1, 2> H_3 = T_u1 * M_1 * RS_bottom;
667 Eigen::Matrix<double, 1, 2> H_4 = T_u3 * M_3 * Q;
668
669 Eigen::Matrix<double, 4, 2> H_controls;
670 H_controls << H_1, H_2, H_3, H_4;
671
672 Eigen::Matrix<double, 1, 2> H = T_dot_v3 * M_3 * M_3rel2abs * H_controls;
673
674 Eigen::Matrix<double, 1, 2> result = 0.5 * (L + H);
675
676 return QPointF(result(0,0), result(0,1));
677 }
678
679};
680
681struct SvgPatchMethod
682{
683private:
684
689 static QPointF meshForwardMapping(qreal u, qreal v, const Params2D &p) {
690 return p.r0 + pow3(u)*v*(p.p0 - 3*p.p1 + 3*p.p2 - p.p3 - p.q0 + 3*p.q1 - 3*p.q2 + p.q3) + pow3(u)*(-p.p0 + 3*p.p1 - 3*p.p2 + p.p3) + pow2(u)*v*(-3*p.p0 + 6*p.p1 - 3*p.p2 + 3*p.q0 - 6*p.q1 + 3*p.q2) + pow2(u)*(3*p.p0 - 6*p.p1 + 3*p.p2) + u*pow3(v)*(p.r0 - 3*p.r1 + 3*p.r2 - p.r3 - p.s0 + 3*p.s1 - 3*p.s2 + p.s3) + u*pow2(v)*(-3*p.r0 + 6*p.r1 - 3*p.r2 + 3*p.s0 - 6*p.s1+ 3*p.s2) + u*v*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1) + u*(-2*p.p0 + 3*p.p1 - p.p3 - p.r0 + p.s0) + pow3(v)*(-p.r0 + 3*p.r1 - 3*p.r2 + p.r3) + pow2(v)*(3*p.r0 - 6*p.r1 + 3*p.r2) + v*(-3*p.r0 + 3*p.r1);
691 }
692
693 static QPointF meshForwardMappingDiffU(qreal u, qreal v, const Params2D &p) {
694 return -2*p.p0 + 3*p.p1 - p.p3 - p.r0 + p.s0 + pow2(u)*v*(3*p.p0 - 9*p.p1 + 9*p.p2 - 3*p.p3 - 3*p.q0 + 9*p.q1 - 9*p.q2 + 3*p.q3) + pow2(u)*(-3*p.p0 + 9*p.p1 - 9*p.p2 + 3*p.p3) + u*v*(-6*p.p0 + 12*p.p1 - 6*p.p2 + 6*p.q0 - 12*p.q1 + 6*p.q2) + u*(6*p.p0 - 12*p.p1 + 6*p.p2) + pow3(v)*(p.r0 - 3*p.r1 + 3*p.r2 - p.r3 - p.s0 + 3*p.s1 - 3*p.s2 + p.s3) + pow2(v)*(-3*p.r0 + 6*p.r1 - 3*p.r2 + 3*p.s0 - 6*p.s1 + 3*p.s2) + v*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1);
695 }
696
697 static QPointF meshForwardMappingDiffV(qreal u, qreal v, const Params2D &p) {
698 return -3*p.r0 + 3*p.r1 + pow3(u)*(p.p0 - 3*p.p1 + 3*p.p2 - p.p3 - p.q0 + 3*p.q1 - 3*p.q2 + p.q3) + pow2(u)*(-3*p.p0 + 6*p.p1 - 3*p.p2 + 3*p.q0 - 6*p.q1 + 3*p.q2) + u*pow2(v)*(3*p.r0 - 9*p.r1 + 9*p.r2 - 3*p.r3 - 3*p.s0 + 9*p.s1 - 9*p.s2 + 3*p.s3) + u*v*(-6*p.r0 + 12*p.r1 - 6*p.r2 + 6*p.s0 - 12*p.s1 + 6*p.s2) + u*(2*p.p0 - 3*p.p1 + p.p3 - 2*p.q0 + 3*p.q1 - p.q3 + 3*p.r0 - 3*p.r1 - 3*p.s0 + 3*p.s1) + pow2(v)*(-3*p.r0 + 9*p.r1 - 9*p.r2 + 3*p.r3) + v*(6*p.r0 - 12*p.r1 + 6*p.r2);
699 }
700
701 qreal u = 0.0;
702 qreal v = 0.0;
703 const Params2D p;
704
705public:
706 SvgPatchMethod(qreal _u, qreal _v, const Params2D &_p)
707 : u(_u), v(_v), p(_p)
708 {
709 }
710
711 QPointF value() const {
712 return meshForwardMapping(u, v, p);
713 }
714
715 QPointF diffU() const {
716 return meshForwardMappingDiffU(u, v, p);
717 }
718
719 QPointF diffV() const {
720 return meshForwardMappingDiffV(u, v, p);
721 }
722
723};
724
725template <class PatchMethod>
726double my_f(const gsl_vector * x, void *paramsPtr)
727{
728 const Params2D *params = static_cast<const Params2D*>(paramsPtr);
729 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1));
730
731 PatchMethod mat(pos.x(), pos.y(), *params);
732 const QPointF S = mat.value();
733
734 return kisSquareDistance(S, params->dstPoint);
735}
736
737template <class PatchMethod>
738void my_fdf (const gsl_vector *x, void *paramsPtr, double *f, gsl_vector *df)
739{
740 const Params2D *params = static_cast<const Params2D*>(paramsPtr);
741 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1));
742
743 PatchMethod mat(pos.x(), pos.y(), *params);
744 const QPointF S = mat.value();
745 const QPointF dU = mat.diffU();
746 const QPointF dV = mat.diffV();
747
748 *f = kisSquareDistance(S, params->dstPoint);
749
750 gsl_vector_set(df, 0,
751 2 * (S.x() - params->dstPoint.x()) * dU.x() +
752 2 * (S.y() - params->dstPoint.y()) * dU.y());
753 gsl_vector_set(df, 1,
754 2 * (S.x() - params->dstPoint.x()) * dV.x() +
755 2 * (S.y() - params->dstPoint.y()) * dV.y());
756}
757
758template <class PatchMethod>
759void my_df (const gsl_vector *x, void *paramsPtr,
760 gsl_vector *df)
761{
762 const Params2D *params = static_cast<const Params2D*>(paramsPtr);
763 const QPointF pos(gsl_vector_get(x, 0), gsl_vector_get(x, 1));
764
765 PatchMethod mat(pos.x(), pos.y(), *params);
766 const QPointF S = mat.value();
767 const QPointF dU = mat.diffU();
768 const QPointF dV = mat.diffV();
769
770 gsl_vector_set(df, 0,
771 2 * (S.x() - params->dstPoint.x()) * dU.x() +
772 2 * (S.y() - params->dstPoint.y()) * dU.y());
773 gsl_vector_set(df, 1,
774 2 * (S.x() - params->dstPoint.x()) * dV.x() +
775 2 * (S.y() - params->dstPoint.y()) * dV.y());
776}
777}
778
779template <class PatchMethod>
780QPointF calculateLocalPosImpl(const std::array<QPointF, 12> &points, const QPointF &globalPoint)
781{
782 QRectF patchBounds;
783
784 for (auto it = points.begin(); it != points.end(); ++it) {
785 KisAlgebra2D::accumulateBounds(*it, &patchBounds);
786 }
787
788 const QPointF approxStart = KisAlgebra2D::absoluteToRelative(globalPoint, patchBounds);
789 KIS_SAFE_ASSERT_RECOVER_NOOP(QRectF(0,0,1.0,1.0).contains(approxStart));
790
791#ifdef HAVE_GSL
792 const gsl_multimin_fdfminimizer_type *T =
793 gsl_multimin_fdfminimizer_vector_bfgs2;
794 gsl_multimin_fdfminimizer *s = 0;
795 gsl_vector *x;
796 gsl_multimin_function_fdf minex_func;
797
798 size_t iter = 0;
799 int status;
800
801 /* Starting point */
802 x = gsl_vector_alloc (2);
803 gsl_vector_set (x, 0, approxStart.x());
804 gsl_vector_set (x, 1, approxStart.y());
805
806 Params2D p;
807
808 p.p0 = points[KisBezierPatch::TL];
809 p.p1 = points[KisBezierPatch::TL_HC];
810 p.p2 = points[KisBezierPatch::TR_HC];
811 p.p3 = points[KisBezierPatch::TR];
812
813 p.q0 = points[KisBezierPatch::BL];
814 p.q1 = points[KisBezierPatch::BL_HC];
815 p.q2 = points[KisBezierPatch::BR_HC];
816 p.q3 = points[KisBezierPatch::BR];
817
818 p.r0 = points[KisBezierPatch::TL];
819 p.r1 = points[KisBezierPatch::TL_VC];
820 p.r2 = points[KisBezierPatch::BL_VC];
821 p.r3 = points[KisBezierPatch::BL];
822
823 p.s0 = points[KisBezierPatch::TR];
824 p.s1 = points[KisBezierPatch::TR_VC];
825 p.s2 = points[KisBezierPatch::BR_VC];
826 p.s3 = points[KisBezierPatch::BR];
827
828 p.dstPoint = globalPoint;
829
830 /* Initialize method and iterate */
831 minex_func.n = 2;
832 minex_func.f = my_f<PatchMethod>;
833 minex_func.params = (void*)&p;
834 minex_func.df = my_df<PatchMethod>;
835 minex_func.fdf = my_fdf<PatchMethod>;
836
837 s = gsl_multimin_fdfminimizer_alloc (T, 2);
838 gsl_multimin_fdfminimizer_set (s, &minex_func, x, 0.01, 0.1);
839
840 QPointF result;
841
842
843 result.rx() = gsl_vector_get (s->x, 0);
844 result.ry() = gsl_vector_get (s->x, 1);
845
846 do
847 {
848 iter++;
849 status = gsl_multimin_fdfminimizer_iterate(s);
850
851 if (status)
852 break;
853
854 status = gsl_multimin_test_gradient (s->gradient, 1e-4);
855
856 result.rx() = gsl_vector_get (s->x, 0);
857 result.ry() = gsl_vector_get (s->x, 1);
858
859 if (status == GSL_SUCCESS)
860 {
861 result.rx() = gsl_vector_get (s->x, 0);
862 result.ry() = gsl_vector_get (s->x, 1);
863 //qDebug() << "******* Converged to minimum" << ppVar(result);
864
865 }
866 }
867 while (status == GSL_CONTINUE && iter < 10000);
868
869// ENTER_FUNCTION()<< ppVar(iter) << ppVar(globalPoint) << ppVar(result);
870// ENTER_FUNCTION() << ppVar(meshForwardMapping(result.x(), result.y(), p));
871
872 gsl_vector_free(x);
873 gsl_multimin_fdfminimizer_free (s);
874
875 return result;
876#else
877 return approxStart;
878#endif
879}
880
881template <class PatchMethod>
882QPointF calculateGlobalPosImpl(const std::array<QPointF, 12> &points, const QPointF &localPoint)
883{
884 Params2D p;
885
886 p.p0 = points[KisBezierPatch::TL];
887 p.p1 = points[KisBezierPatch::TL_HC];
888 p.p2 = points[KisBezierPatch::TR_HC];
889 p.p3 = points[KisBezierPatch::TR];
890
891 p.q0 = points[KisBezierPatch::BL];
892 p.q1 = points[KisBezierPatch::BL_HC];
893 p.q2 = points[KisBezierPatch::BR_HC];
894 p.q3 = points[KisBezierPatch::BR];
895
896 p.r0 = points[KisBezierPatch::TL];
897 p.r1 = points[KisBezierPatch::TL_VC];
898 p.r2 = points[KisBezierPatch::BL_VC];
899 p.r3 = points[KisBezierPatch::BL];
900
901 p.s0 = points[KisBezierPatch::TR];
902 p.s1 = points[KisBezierPatch::TR_VC];
903 p.s2 = points[KisBezierPatch::BR_VC];
904 p.s3 = points[KisBezierPatch::BR];
905
906 PatchMethod f(localPoint.x(), localPoint.y(), p);
907 return f.value();
908}
909
910QPointF calculateLocalPos(const std::array<QPointF, 12> &points, const QPointF &globalPoint)
911{
912 return calculateLocalPosImpl<LevelBasedPatchMethod>(points, globalPoint);
913}
914
915QPointF calculateGlobalPos(const std::array<QPointF, 12> &points, const QPointF &localPoint)
916{
917 return calculateGlobalPosImpl<LevelBasedPatchMethod>(points, localPoint);
918}
919
920QPointF calculateLocalPosSVG2(const std::array<QPointF, 12> &points, const QPointF &globalPoint)
921{
922 return calculateLocalPosImpl<SvgPatchMethod>(points, globalPoint);
923}
924
925QPointF calculateGlobalPosSVG2(const std::array<QPointF, 12> &points, const QPointF &localPoint)
926{
927 return calculateGlobalPosImpl<SvgPatchMethod>(points, localPoint);
928}
929
930QPointF interpolateQuadric(const QPointF &p0, const QPointF &p2, const QPointF &pt, qreal t)
931{
932 if (t <= 0.0 || t >= 1.0)
933 return lerp(p0, p2, 0.5);
934
935 /*
936 B(t) = [x2 y2] = (1-t)^2*P0 + 2t*(1-t)*P1 + t^2*P2
937
938 B(t) - (1-t)^2*P0 - t^2*P2
939 P1 = --------------------------
940 2t*(1-t)
941 */
942
943 QPointF c1 = pt - (1.0-t) * (1.0-t)*p0 - t * t * p2;
944
945 qreal denom = 2.0 * t * (1.0-t);
946
947 c1.rx() /= denom;
948 c1.ry() /= denom;
949
950 return c1;
951}
952
953std::pair<QPointF, QPointF> offsetSegment(qreal t, const QPointF &offset)
954{
955 /*
956 * method from inkscape, original method and idea borrowed from Simon Budig
957 * <simon@gimp.org> and the GIMP
958 * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
959 *
960 * feel good is an arbitrary parameter that distributes the delta between handles
961 * if t of the drag point is less than 1/6 distance form the endpoint only
962 * the corresponding handle is adjusted. This matches the behavior in GIMP
963 */
964 qreal feel_good;
965 if (t <= 1.0 / 6.0)
966 feel_good = 0;
967 else if (t <= 0.5)
968 feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
969 else if (t <= 5.0 / 6.0)
970 feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
971 else
972 feel_good = 1;
973
974 const QPointF moveP1 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * offset;
975 const QPointF moveP2 = (feel_good/(3*t*t*(1-t))) * offset;
976
977 return std::make_pair(moveP1, moveP2);
978}
979
980qreal curveLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal error)
981{
982 /*
983 * This algorithm is implemented based on an idea by Jens Gravesen:
984 * "Adaptive subdivision and the length of Bezier curves" mat-report no. 1992-10, Mathematical Institute,
985 * The Technical University of Denmark.
986 *
987 * By subdividing the curve at parameter value t you only have to find the length of a full Bezier curve.
988 * If you denote the length of the control polygon by L1 i.e.:
989 * L1 = |P0 P1| +|P1 P2| +|P2 P3|
990 *
991 * and the length of the cord by L0 i.e.:
992 * L0 = |P0 P3|
993 *
994 * then
995 * L = 1/2*L0 + 1/2*L1
996 *
997 * is a good approximation to the length of the curve, and the difference
998 * ERR = L1-L0
999 *
1000 * is a measure of the error. If the error is to large, then you just subdivide curve at parameter value
1001 * 1/2, and find the length of each half.
1002 * If m is the number of subdivisions then the error goes to zero as 2^-4m.
1003 * If you don't have a cubic curve but a curve of degree n then you put
1004 * L = (2*L0 + (n-1)*L1)/(n+1)
1005 */
1006
1007 const int deg = bezierDegree(p0, p1, p2, p3);
1008
1009 if (deg == -1)
1010 return 0.0;
1011
1012 // calculate chord length
1013 const qreal chordLen = kisDistance(p0, p3);
1014
1015 if (deg == 1) {
1016 return chordLen;
1017 }
1018
1019 // calculate length of control polygon
1020 qreal polyLength = 0.0;
1021
1022 polyLength += kisDistance(p0, p1);
1023 polyLength += kisDistance(p1, p2);
1024 polyLength += kisDistance(p2, p3);
1025
1026 if ((polyLength - chordLen) > error) {
1027 QPointF q0, q1, q2, q3, q4;
1028 deCasteljau(p0, p1, p2, p3, 0.5, &q0, &q1, &q2, &q3, &q4);
1029
1030 return curveLength(p0, q0, q1, q2, error) +
1031 curveLength(q2, q3, q4, p3, error);
1032 } else {
1033 // the error is smaller than our tolerance
1034 if (deg == 3)
1035 return 0.5 * chordLen + 0.5 * polyLength;
1036 else
1037 return (2.0 * chordLen + polyLength) / 3.0;
1038 }
1039}
1040
1041qreal curveLengthAtPoint(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
1042{
1043 QPointF q0, q1, q2, q3, q4;
1044 deCasteljau(p0, p1, p2, p3, t, &q0, &q1, &q2, &q3, &q4);
1045
1046 return curveLength(p0, q0, q1, q2, error);
1047}
1048
1049qreal curveParamBySegmentLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal expectedLength, qreal totalLength, const qreal error)
1050{
1051 const qreal splitAtParam = expectedLength / totalLength;
1052
1053 QPointF q0, q1, q2, q3, q4;
1054 deCasteljau(p0, p1, p2, p3, splitAtParam, &q0, &q1, &q2, &q3, &q4);
1055
1056 const qreal portionLength = curveLength(p0, q0, q1, q2, error);
1057
1058 if (std::abs(portionLength - expectedLength) < error) {
1059 return splitAtParam;
1060 } else if (portionLength < expectedLength) {
1061 return splitAtParam + (1.0 - splitAtParam) * curveParamBySegmentLength(q2, q3, q4, p3, expectedLength - portionLength, totalLength - portionLength, error);
1062 } else {
1063 return splitAtParam * curveParamBySegmentLength(p0, q0, q1, q2, expectedLength, portionLength, error);
1064 }
1065}
1066
1067
1068qreal curveParamByProportion(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal proportion, const qreal error)
1069{
1070 const qreal totalLength = curveLength(p0, p1, p2, p3, error);
1071 const qreal expectedLength = proportion * totalLength;
1072
1073 return curveParamBySegmentLength(p0, p1, p2, p3, expectedLength, totalLength, error);
1074}
1075
1076qreal curveProportionByParam(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
1077{
1078 return curveLengthAtPoint(p0, p1, p2, p3, t, error) / curveLength(p0, p1, p2, p3, error);
1079}
1080
1081std::pair<QPointF, QPointF> removeBezierNode(const QPointF &p0,
1082 const QPointF &p1,
1083 const QPointF &p2,
1084 const QPointF &p3,
1085 const QPointF &q1,
1086 const QPointF &q2,
1087 const QPointF &q3)
1088{
1144 const qreal lenP = KisBezierUtils::curveLength(p0, p1, p2, p3, 0.001);
1145 const qreal lenQ = KisBezierUtils::curveLength(p3, q1, q2, q3, 0.001);
1146
1147 const qreal z = lenP / (lenP + lenQ);
1148
1149 Eigen::Matrix4f M;
1150 M << 1, 0, 0, 0,
1151 -3, 3, 0, 0,
1152 3, -6, 3, 0,
1153 -1, 3, -3, 1;
1154
1155 Eigen::DiagonalMatrix<float, 4> Z_1;
1156 Z_1.diagonal() << 1, z, pow2(z), pow3(z);
1157
1158 Eigen::Matrix4f Z_2;
1159 Z_2 << 1, z, pow2(z), pow3(z),
1160 0, 1 - z, 2 * z * (1 - z), 3 * pow2(z) * (1 - z),
1161 0, 0, pow2(1 - z), 3 * z * pow2(1 - z),
1162 0, 0, 0, pow3(1 - z);
1163
1164 Eigen::Matrix<float, 8, 2> R;
1165 R << p0.x(), p0.y(),
1166 p1.x(), p1.y(),
1167 p2.x(), p2.y(),
1168 p3.x(), p3.y(),
1169 p3.x(), p3.y(),
1170 q1.x(), q1.y(),
1171 q2.x(), q2.y(),
1172 q3.x(), q3.y();
1173
1174 Eigen::Matrix<float, 2, 2> B_const;
1175 B_const << p0.x(), p0.y(),
1176 q3.x(), q3.y();
1177
1178
1179 Eigen::Matrix4f M1 = M.inverse() * Z_1 * M;
1180 Eigen::Matrix4f M2 = M.inverse() * Z_2 * M;
1181
1182 Eigen::Matrix<float, 8, 4> C;
1183 C << M1, M2;
1184
1185 Eigen::Matrix<float, 8, 2> C_const;
1186 C_const << C.col(0), C.col(3);
1187
1188 Eigen::Matrix<float, 8, 2> C_var;
1189 C_var << C.col(1), C.col(2);
1190
1191 Eigen::Matrix<float, 8, 2> R_var;
1192 R_var = R - C_const * B_const;
1193
1194 Eigen::Matrix<float, 6, 2> R_reduced;
1195 R_reduced = R_var.block(1, 0, 6, 2);
1196
1197 Eigen::Matrix<float, 6, 2> C_reduced;
1198 C_reduced = C_var.block(1, 0, 6, 2);
1199
1200 Eigen::Matrix<float, 2, 2> result;
1201 result = (C_reduced.transpose() * C_reduced).inverse() * C_reduced.transpose() * R_reduced;
1202
1203 QPointF resultP0(result(0, 0), result(0, 1));
1204 QPointF resultP1(result(1, 0), result(1, 1));
1205
1206 return std::make_pair(resultP0, resultP1);
1207}
1208QVector<qreal> intersectWithLineImpl(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps, qreal alpha, qreal beta)
1209{
1211
1212 QVector<qreal> result;
1213
1214 const qreal length =
1215 kisDistance(p0, p1) +
1216 kisDistance(p1, p2) +
1217 kisDistance(p2, p3);
1218
1219 if (length < eps) {
1220 if (intersectLines(p0, p3, line.p1(), line.p2())) {
1221 result << alpha * 0.5 + beta;
1222 }
1223 } else {
1224
1225 const bool hasIntersections =
1226 intersectLines(p0, p1, line.p1(), line.p2()) ||
1227 intersectLines(p1, p2, line.p1(), line.p2()) ||
1228 intersectLines(p2, p3, line.p1(), line.p2());
1229
1230 if (hasIntersections) {
1231 QPointF q0, q1, q2, q3, q4;
1232
1233 deCasteljau(p0, p1, p2, p3, 0.5, &q0, &q1, &q2, &q3, &q4);
1234
1235 result << intersectWithLineImpl(p0, q0, q1, q2, line, eps, 0.5 * alpha, beta);
1236 result << intersectWithLineImpl(q2, q3, q4, p3, line, eps, 0.5 * alpha, beta + 0.5 * alpha);
1237 }
1238 }
1239 return result;
1240}
1241
1242QVector<qreal> intersectWithLine(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps)
1243{
1244 return intersectWithLineImpl(p0, p1, p2, p3, line, eps, 1.0, 0.0);
1245}
1246
1247boost::optional<qreal> intersectWithLineNearest(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, const QPointF &nearestAnchor, qreal eps)
1248{
1249 QVector<qreal> result = intersectWithLine(p0, p1, p2, p3, line, eps);
1250
1251 qreal minDistance = std::numeric_limits<qreal>::max();
1252 boost::optional<qreal> nearestRoot;
1253
1254 Q_FOREACH (qreal root, result) {
1255 const QPointF pt = bezierCurve(p0, p1, p2, p3, root);
1256 const qreal distance = kisDistance(pt, nearestAnchor);
1257
1258 if (distance < minDistance) {
1259 minDistance = distance;
1260 nearestRoot = root;
1261 }
1262 }
1263
1264 return nearestRoot;
1265}
1266
1267}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
float value(const T *src, size_t ch)
QPointF r2
Eigen::Matrix< double, 4, 2 > Q
QPointF q1
Eigen::Matrix< double, 4, 2 > S
QPointF s3
Eigen::Matrix4d M_3
QPointF r0
QPointF s1
Eigen::Matrix< double, 4, 2 > RS_bottom
Eigen::Matrix< double, 1, 4 > T_dot_v3
const Params2D p
Eigen::Matrix< double, 4, 2 > PQ_left
QPointF q0
Eigen::Matrix< double, 1, 2 > T_dot_u1
QPointF s0
QPointF p0
Eigen::Matrix< double, 4, 2 > RS_top
Eigen::Matrix4d M_3rel2abs
Eigen::Matrix< double, 1, 2 > T_u1
Eigen::Matrix< double, 4, 2 > R
QPointF q3
qreal v
QPointF s2
QPointF r1
qreal u
QPointF dstPoint
QPointF p2
Eigen::Matrix< double, 1, 4 > T_dot_u3
Eigen::Matrix< double, 1, 2 > T_dot_v1
QPointF q2
Eigen::Matrix< double, 1, 2 > T_v1
Eigen::Matrix< double, 1, 4 > T_u3
Eigen::Matrix< double, 1, 4 > T_v3
Eigen::Matrix< double, 2, 4 > M_1
QPointF p3
QPointF r3
QPointF p1
Eigen::Matrix< double, 4, 2 > PQ_right
qreal distance(const QPointF &p1, const QPointF &p2)
unsigned int uint
QPointF lerp(const QPointF &p1, const QPointF &p2, qreal t)
#define C(i, j)
#define P(i, j, k)
int isFlat(qreal tolerance) const
const int MaxRecursionDepth
Maximal recursion depth for finding root params.
QPointF evaluate(qreal t, BezierSegment *left, BezierSegment *right) const
static uint controlPolygonZeros(const QList< QPointF > &controlPoints)
void setPoint(int index, const QPointF &p)
BezierSegment(int degree=0, QPointF *p=0)
QPointF point(int index) const
const qreal FlatnessTolerance
Flatness tolerance for finding root params.
QList< qreal > roots(int depth=0) const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
const qreal eps
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T pow3(const T &x)
Definition kis_global.h:171
T pow2(const T &x)
Definition kis_global.h:166
qreal kisSquareDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:194
QPointF absoluteToRelative(const QPointF &pt, const QRectF &rc)
void accumulateBounds(const Point &pt, Rect *bounds)
boost::optional< QPointF > intersectLines(const QLineF &boundedLine, const QLineF &unboundedLine)
qreal curveParamByProportion(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal proportion, const qreal error)
QPointF calculateLocalPos(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
calculates local (u,v) coordinates of the patch corresponding to globalPoint
void deCasteljau(const QPointF &q0, const QPointF &q1, const QPointF &q2, const QPointF &q3, qreal t, QPointF *p0, QPointF *p1, QPointF *p2, QPointF *p3, QPointF *p4)
QPointF calculateLocalPosSVG2(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
calculates local (u,v) coordinates of the patch corresponding to globalPoint
QPointF calculateLocalPosImpl(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
QVector< qreal > intersectWithLine(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps)
qreal curveProportionByParam(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
QVector< qreal > linearizeCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal eps)
qreal curveLengthAtPoint(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t, const qreal error)
QVector< qreal > intersectWithLineImpl(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, qreal eps, qreal alpha, qreal beta)
QPointF calculateGlobalPos(const std::array< QPointF, 12 > &points, const QPointF &localPoint)
calculates global coordinate corresponding to the patch coordinate (u, v)
bool isLinearSegmentByDerivatives(const QPointF &p0, const QPointF &d0, const QPointF &p1, const QPointF &d1, const qreal eps=1e-4)
int controlPolygonZeros(const QList< QPointF > &controlPoints)
QPointF calculateGlobalPosSVG2(const std::array< QPointF, 12 > &points, const QPointF &localPoint)
calculates global coordinate corresponding to the patch coordinate (u, v)
QPointF bezierCurveDeriv(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
qreal curveParamBySegmentLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal expectedLength, qreal totalLength, const qreal error)
std::pair< QPointF, QPointF > offsetSegment(qreal t, const QPointF &offset)
moves point t of the curve by offset offset
QPointF bezierCurve(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, qreal t)
qreal nearestPoint(const QList< QPointF > controlPoints, const QPointF &point, qreal *resultDistance, QPointF *resultPoint)
std::pair< QPointF, QPointF > removeBezierNode(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &q1, const QPointF &q2, const QPointF &q3)
Adjusts position for the bezier control points after removing a node.
qreal curveLength(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3, const qreal error)
QPointF calculateGlobalPosImpl(const std::array< QPointF, 12 > &points, const QPointF &localPoint)
QVector< qreal > mergeLinearizationSteps(const QVector< qreal > &a, const QVector< qreal > &b)
boost::optional< qreal > intersectWithLineNearest(const QPointF &p0, const QPointF &p1, const QPointF &p2, const QPointF &p3, const QLineF &line, const QPointF &nearestAnchor, qreal eps)
QPointF interpolateQuadric(const QPointF &p0, const QPointF &p2, const QPointF &pt, qreal t)
Interpolates quadric curve passing through given points.
int bezierDegree(const QPointF p0, const QPointF p1, const QPointF p2, const QPointF p3)