Krita Source Code Documentation
Loading...
Searching...
No Matches
KoCurveFit.cpp File Reference
#include "KoCurveFit.h"
#include <KoPathShape.h>
#include <QVector>
#include <math.h>

Go to the source code of this file.

Classes

class  FitVector
 

Functions

static qreal B0 (qreal u)
 
static qreal B1 (qreal u)
 
static qreal B2 (qreal u)
 
static qreal B3 (qreal u)
 
KoPathShapebezierFit (const QList< QPointF > &points, float error)
 
static QPointF BezierII (int degree, QPointF *V, qreal t)
 
static qreal * ChordLengthParameterize (const QList< QPointF > &points, int first, int last)
 
static FitVector ComputeCenterTangent (const QList< QPointF > &points, int center)
 
FitVector ComputeLeftTangent (const QList< QPointF > &points, int end)
 
static qreal ComputeMaxError (const QList< QPointF > &points, int first, int last, QPointF *curve, qreal *u, int *splitPoint)
 
FitVector ComputeRightTangent (const QList< QPointF > &points, int end)
 
qreal distance (const QPointF &p1, const QPointF &p2)
 
QPointF * FitCubic (const QList< QPointF > &points, int first, int last, FitVector tHat1, FitVector tHat2, float error, int &width)
 
QPointF * GenerateBezier (const QList< QPointF > &points, int first, int last, qreal *uPrime, FitVector tHat1, FitVector tHat2)
 
static qreal NewtonRaphsonRootFind (QPointF *Q, QPointF P, qreal u)
 
static qreal * Reparameterize (const QList< QPointF > &points, int first, int last, qreal *u, QPointF *curve)
 
static FitVector VectorAdd (FitVector a, FitVector b)
 
static FitVector VectorScale (FitVector v, qreal s)
 
static FitVector VectorSub (FitVector a, FitVector b)
 

Variables

const qreal Zero = 10e-12
 our equivalent to zero
 

Function Documentation

◆ B0()

static qreal B0 ( qreal u)
static

Definition at line 180 of file KoCurveFit.cpp.

181{
182 qreal tmp = 1.0 - u;
183 return (tmp * tmp * tmp);
184}
qreal u

References u.

◆ B1()

static qreal B1 ( qreal u)
static

Definition at line 187 of file KoCurveFit.cpp.

188{
189 qreal tmp = 1.0 - u;
190 return (3 * u *(tmp * tmp));
191}

References u.

◆ B2()

static qreal B2 ( qreal u)
static

Definition at line 193 of file KoCurveFit.cpp.

194{
195 qreal tmp = 1.0 - u;
196 return (3 * u * u * tmp);
197}

References u.

◆ B3()

static qreal B3 ( qreal u)
static

Definition at line 199 of file KoCurveFit.cpp.

200{
201 return (u * u * u);
202}

References u.

◆ bezierFit()

KoPathShape * bezierFit ( const QList< QPointF > & points,
float error )

Definition at line 543 of file KoCurveFit.cpp.

544{
545 FitVector tHat1, tHat2;
546
547 tHat1 = ComputeLeftTangent(points, 0);
548 tHat2 = ComputeRightTangent(points, points.count() - 1);
549
550 int width = 0;
551 QPointF *curve;
552 curve = FitCubic(points, 0, points.count() - 1, tHat1, tHat2, error, width);
553
554 KoPathShape * path = new KoPathShape();
555
556 if (width > 3) {
557 path->moveTo(curve[0]);
558 path->curveTo(curve[1], curve[2], curve[3]);
559 for (int i = 4; i < width; i += 4) {
560 path->curveTo(curve[i+1], curve[i+2], curve[i+3]);
561 }
562 }
563
564 delete[] curve;
565 return path;
566}
FitVector ComputeLeftTangent(const QList< QPointF > &points, int end)
FitVector ComputeRightTangent(const QList< QPointF > &points, int end)
QPointF * FitCubic(const QList< QPointF > &points, int first, int last, FitVector tHat1, FitVector tHat2, float error, int &width)
The position of a path point within a path shape.
Definition KoPathShape.h:63

References ComputeLeftTangent(), ComputeRightTangent(), and FitCubic().

◆ BezierII()

static QPointF BezierII ( int degree,
QPointF * V,
qreal t )
static

Definition at line 328 of file KoCurveFit.cpp.

329{
330 int i, j;
331 QPointF Q; /* Point on curve at parameter t */
332 QPointF *Vtemp; /* Local copy of control points */
333
334 Vtemp = new QPointF[degree+1];
335
336 for (i = 0; i <= degree; ++i) {
337 Vtemp[i] = V[i];
338 }
339
340 /* Triangle computation */
341 for (i = 1; i <= degree; ++i) {
342 for (j = 0; j <= degree - i; ++j) {
343 Vtemp[j].setX((1.0 - t) * Vtemp[j].x() + t * Vtemp[j+1].x());
344 Vtemp[j].setY((1.0 - t) * Vtemp[j].y() + t * Vtemp[j+1].y());
345 }
346 }
347
348 Q = Vtemp[0];
349 delete[] Vtemp;
350 return Q;
351}
Eigen::Matrix< double, 4, 2 > Q

References Q.

◆ ChordLengthParameterize()

static qreal * ChordLengthParameterize ( const QList< QPointF > & points,
int first,
int last )
static

Definition at line 115 of file KoCurveFit.cpp.

116{
117 int i;
118 qreal *u; /* Parameterization */
119
120 u = new qreal[(last-first+1)];
121
122 u[0] = 0.0;
123 for (i = first + 1; i <= last; ++i) {
124 u[i-first] = u[i-first-1] +
125 distance(points.at(i), points.at(i - 1));
126 }
127
128 qreal denominator = u[last-first];
129 if (qFuzzyCompare(denominator, qreal(0.0))) {
130 denominator = Zero;
131 }
132
133 for (i = first + 1; i <= last; ++i) {
134 u[i-first] = u[i-first] / denominator;
135 }
136
137 return(u);
138}
const qreal Zero
our equivalent to zero
qreal distance(const QPointF &p1, const QPointF &p2)
static bool qFuzzyCompare(half p1, half p2)

References distance(), qFuzzyCompare(), u, and Zero.

◆ ComputeCenterTangent()

static FitVector ComputeCenterTangent ( const QList< QPointF > & points,
int center )
static

Definition at line 160 of file KoCurveFit.cpp.

161{
162 FitVector V1, V2, tHatCenter;
163
164 FitVector cpointb(points.at(center - 1));
165 FitVector cpoint(points.at(center));
166 FitVector cpointa(points.at(center + 1));
167
168 V1 = VectorSub(cpointb, cpoint);
169 V2 = VectorSub(cpoint, cpointa);
170 tHatCenter.m_X = ((V1.m_X + V2.m_X) / 2.0);
171 tHatCenter.m_Y = ((V1.m_Y + V2.m_Y) / 2.0);
172 tHatCenter.normalize();
173 return tHatCenter;
174}
static FitVector VectorSub(FitVector a, FitVector b)
void normalize()

References FitVector::m_X, FitVector::m_Y, FitVector::normalize(), and VectorSub().

◆ ComputeLeftTangent()

FitVector ComputeLeftTangent ( const QList< QPointF > & points,
int end )

Definition at line 92 of file KoCurveFit.cpp.

93{
94 FitVector tHat1(points.at(end + 1), points.at(end));
95
96 tHat1.normalize();
97
98 return tHat1;
99}

References FitVector::normalize().

◆ ComputeMaxError()

static qreal ComputeMaxError ( const QList< QPointF > & points,
int first,
int last,
QPointF * curve,
qreal * u,
int * splitPoint )
static

Definition at line 358 of file KoCurveFit.cpp.

359{
360 int i;
361 qreal maxDist; /* Maximum error */
362 qreal dist; /* Current error */
363 QPointF P; /* Point on curve */
364 FitVector v; /* Vector from point to curve */
365
366 *splitPoint = (last - first + 1) / 2;
367 maxDist = 0.0;
368 for (i = first + 1; i < last; ++i) {
369 P = BezierII(3, curve, u[i-first]);
370 v = VectorSub(P, points.at(i));
371 dist = v.length();
372 if (dist >= maxDist) {
373 maxDist = dist;
374 *splitPoint = i;
375 }
376 }
377 return (maxDist);
378}
qreal v
static QPointF BezierII(int degree, QPointF *V, qreal t)
#define P(i, j, k)

References BezierII(), P, u, v, and VectorSub().

◆ ComputeRightTangent()

FitVector ComputeRightTangent ( const QList< QPointF > & points,
int end )

Definition at line 101 of file KoCurveFit.cpp.

102{
103 FitVector tHat1(points.at(end - 1), points.at(end));
104
105 tHat1.normalize();
106
107 return tHat1;
108}

References FitVector::normalize().

◆ distance()

qreal distance ( const QPointF & p1,
const QPointF & p2 )

Definition at line 84 of file KoCurveFit.cpp.

85{
86 qreal dx = (p1.x() - p2.x());
87 qreal dy = (p1.y() - p2.y());
88 return sqrt(dx*dx + dy*dy);
89}
QPointF p2
QPointF p1

References p1, and p2.

◆ FitCubic()

QPointF * FitCubic ( const QList< QPointF > & points,
int first,
int last,
FitVector tHat1,
FitVector tHat2,
float error,
int & width )

Definition at line 445 of file KoCurveFit.cpp.

446{
447 qreal *u;
448 qreal *uPrime;
449 qreal maxError;
450 int splitPoint;
451 int nPts;
452 qreal iterationError;
453 int maxIterations = 4;
454 FitVector tHatCenter;
455 QPointF *curve;
456 int i;
457
458 width = 0;
459
460
461 iterationError = error * error;
462 nPts = last - first + 1;
463
464 if (nPts == 2) {
465 qreal dist = distance(points.at(last), points.at(first)) / 3.0;
466
467 curve = new QPointF[4];
468
469 curve[0] = points.at(first);
470 curve[3] = points.at(last);
471
472 tHat1.scale(dist);
473 tHat2.scale(dist);
474
475 curve[1] = tHat1 + curve[0];
476 curve[2] = tHat2 + curve[3];
477
478 width = 4;
479 return curve;
480 }
481
482 /* Parameterize points, and attempt to fit curve */
483 u = ChordLengthParameterize(points, first, last);
484 curve = GenerateBezier(points, first, last, u, tHat1, tHat2);
485
486
487 /* Find max deviation of points to fitted curve */
488 maxError = ComputeMaxError(points, first, last, curve, u, &splitPoint);
489 if (maxError < error) {
490 delete[] u;
491 width = 4;
492 return curve;
493 }
494
495
496 /* If error not too large, try some reparameterization */
497 /* and iteration */
498 if (maxError < iterationError) {
499 for (i = 0; i < maxIterations; ++i) {
500 uPrime = Reparameterize(points, first, last, u, curve);
501 delete[] curve;
502 curve = GenerateBezier(points, first, last, uPrime, tHat1, tHat2);
503 maxError = ComputeMaxError(points, first, last,
504 curve, uPrime, &splitPoint);
505 if (maxError < error) {
506 delete[] u;
507 delete[] uPrime;
508 width = 4;
509 return curve;
510 }
511 delete[] u;
512 u = uPrime;
513 }
514 }
515
516 /* Fitting failed -- split at max error point and fit recursively */
517 delete[] u;
518 delete[] curve;
519 tHatCenter = ComputeCenterTangent(points, splitPoint);
520
521 int w1, w2;
522 QPointF *cu1 = 0, *cu2 = 0;
523 cu1 = FitCubic(points, first, splitPoint, tHat1, tHatCenter, error, w1);
524
525 tHatCenter.negate();
526 cu2 = FitCubic(points, splitPoint, last, tHatCenter, tHat2, error, w2);
527
528 QPointF *newcurve = new QPointF[w1+w2];
529 for (int i = 0; i < w1; ++i) {
530 newcurve[i] = cu1[i];
531 }
532 for (int i = 0; i < w2; ++i) {
533 newcurve[i+w1] = cu2[i];
534 }
535
536 delete[] cu1;
537 delete[] cu2;
538 width = w1 + w2;
539 return newcurve;
540}
static qreal * Reparameterize(const QList< QPointF > &points, int first, int last, qreal *u, QPointF *curve)
QPointF * GenerateBezier(const QList< QPointF > &points, int first, int last, qreal *uPrime, FitVector tHat1, FitVector tHat2)
static FitVector ComputeCenterTangent(const QList< QPointF > &points, int center)
static qreal ComputeMaxError(const QList< QPointF > &points, int first, int last, QPointF *curve, qreal *u, int *splitPoint)
static qreal * ChordLengthParameterize(const QList< QPointF > &points, int first, int last)
void scale(qreal s)
void negate()

References ChordLengthParameterize(), ComputeCenterTangent(), ComputeMaxError(), distance(), FitCubic(), GenerateBezier(), FitVector::negate(), Reparameterize(), FitVector::scale(), and u.

◆ GenerateBezier()

QPointF * GenerateBezier ( const QList< QPointF > & points,
int first,
int last,
qreal * uPrime,
FitVector tHat1,
FitVector tHat2 )

Definition at line 209 of file KoCurveFit.cpp.

210{
211 int i;
212 int nPts; /* Number of pts in sub-curve */
213 qreal C[2][2]; /* Matrix C */
214 qreal X[2]; /* Matrix X */
215 qreal det_C0_C1, /* Determinants of matrices */
216 det_C0_X,
217 det_X_C1;
218 qreal alpha_l, /* Alpha values, left and right */
219 alpha_r;
220 FitVector tmp; /* Utility variable */
221 QPointF *curve;
222
223 curve = new QPointF[4];
224 nPts = last - first + 1;
225
226 /* Precomputed rhs for eqn */
227 // FitVector A[nPts][2]
229
230 /* Compute the A's */
231 for (i = 0; i < nPts; ++i) {
232 FitVector v1, v2;
233 v1 = tHat1;
234 v2 = tHat2;
235 v1.scale(B1(uPrime[i]));
236 v2.scale(B2(uPrime[i]));
237 A[i][0] = v1;
238 A[i][1] = v2;
239 }
240
241 /* Create the C and X matrices */
242 C[0][0] = 0.0;
243 C[0][1] = 0.0;
244 C[1][0] = 0.0;
245 C[1][1] = 0.0;
246 X[0] = 0.0;
247 X[1] = 0.0;
248
249 for (i = 0; i < nPts; ++i) {
250 C[0][0] += (A[i][0]).dot(A[i][0]);
251 C[0][1] += A[i][0].dot(A[i][1]);
252 /* C[1][0] += V2Dot(&A[i][0], &A[i][1]);*/
253 C[1][0] = C[0][1];
254 C[1][1] += A[i][1].dot(A[i][1]);
255
256 FitVector vfirstp1(points.at(first + i));
257 FitVector vfirst(points.at(first));
258 FitVector vlast(points.at(last));
259
260 tmp = VectorSub(vfirstp1,
261 VectorAdd(
262 VectorScale(vfirst, B0(uPrime[i])),
263 VectorAdd(
264 VectorScale(vfirst, B1(uPrime[i])),
265 VectorAdd(
266 VectorScale(vlast, B2(uPrime[i])),
267 VectorScale(vlast, B3(uPrime[i]))))));
268
269
270 X[0] += A[i][0].dot(tmp);
271 X[1] += A[i][1].dot(tmp);
272 }
273
274 /* Compute the determinants of C and X */
275 det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
276 det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
277 det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
278
279 /* Finally, derive alpha values */
280 if (qFuzzyCompare(det_C0_C1, qreal(0.0))) {
281 det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;
282 if (qFuzzyCompare(det_C0_C1, qreal(0.0))) {
283 det_C0_C1 = Zero;
284 }
285 }
286 alpha_l = det_X_C1 / det_C0_C1;
287 alpha_r = det_C0_X / det_C0_C1;
288
289
290 /* If alpha negative, use the Wu/Barsky heuristic (see text) */
291 /* (if alpha is 0, you get coincident control points that lead to
292 * divide by zero in any subsequent NewtonRaphsonRootFind() call. */
293 if (alpha_l < 1.0e-6 || alpha_r < 1.0e-6) {
294 qreal dist = distance(points.at(last), points.at(first)) / 3.0;
295
296 curve[0] = points.at(first);
297 curve[3] = points.at(last);
298
299 tHat1.scale(dist);
300 tHat2.scale(dist);
301
302 curve[1] = tHat1 + curve[0];
303 curve[2] = tHat2 + curve[3];
304 return curve;
305 }
306
307 /* First and last control points of the Bezier curve are */
308 /* positioned exactly at the first and last data points */
309 /* Control points 1 and 2 are positioned an alpha distance out */
310 /* on the tangent vectors, left and right, respectively */
311 curve[0] = points.at(first);
312 curve[3] = points.at(last);
313
314 tHat1.scale(alpha_l);
315 tHat2.scale(alpha_r);
316
317 curve[1] = tHat1 + curve[0];
318 curve[2] = tHat2 + curve[3];
319
320 return (curve);
321}
static FitVector VectorScale(FitVector v, qreal s)
static qreal B2(qreal u)
static qreal B0(qreal u)
static qreal B1(qreal u)
static qreal B3(qreal u)
static FitVector VectorAdd(FitVector a, FitVector b)
#define C(i, j)
const QString X

References A, B0(), B1(), B2(), B3(), C, distance(), qFuzzyCompare(), FitVector::scale(), VectorAdd(), VectorScale(), VectorSub(), and Zero.

◆ NewtonRaphsonRootFind()

static qreal NewtonRaphsonRootFind ( QPointF * Q,
QPointF P,
qreal u )
static

Definition at line 385 of file KoCurveFit.cpp.

386{
387 qreal numerator, denominator;
388 QPointF Q1[3], Q2[2]; /* Q' and Q'' */
389 QPointF Q_u, Q1_u, Q2_u; /*u evaluated at Q, Q', & Q'' */
390 qreal uPrime; /* Improved u */
391 int i;
392
393 /* Compute Q(u) */
394 Q_u = BezierII(3, Q, u);
395
396 /* Generate control vertices for Q' */
397 for (i = 0; i <= 2; ++i) {
398 Q1[i].setX((Q[i+1].x() - Q[i].x()) * 3.0);
399 Q1[i].setY((Q[i+1].y() - Q[i].y()) * 3.0);
400 }
401
402 /* Generate control vertices for Q'' */
403 for (i = 0; i <= 1; ++i) {
404 Q2[i].setX((Q1[i+1].x() - Q1[i].x()) * 2.0);
405 Q2[i].setY((Q1[i+1].y() - Q1[i].y()) * 2.0);
406 }
407
408 /* Compute Q'(u) and Q''(u) */
409 Q1_u = BezierII(2, Q1, u);
410 Q2_u = BezierII(1, Q2, u);
411
412 /* Compute f(u)/f'(u) */
413 numerator = (Q_u.x() - P.x()) * (Q1_u.x()) + (Q_u.y() - P.y()) * (Q1_u.y());
414 denominator = (Q1_u.x()) * (Q1_u.x()) + (Q1_u.y()) * (Q1_u.y()) +
415 (Q_u.x() - P.x()) * (Q2_u.x()) + (Q_u.y() - P.y()) * (Q2_u.y());
416
417 if (qFuzzyCompare(denominator, qreal(0.0))) {
418 denominator = Zero;
419 }
420
421 /* u = u - f(u)/f'(u) */
422 uPrime = u - (numerator / denominator);
423 return (uPrime);
424}

References BezierII(), P, Q, qFuzzyCompare(), u, and Zero.

◆ Reparameterize()

static qreal * Reparameterize ( const QList< QPointF > & points,
int first,
int last,
qreal * u,
QPointF * curve )
static

Definition at line 432 of file KoCurveFit.cpp.

433{
434 int nPts = last - first + 1;
435 int i;
436 qreal *uPrime; /* New parameter values */
437
438 uPrime = new qreal[nPts];
439 for (i = first; i <= last; ++i) {
440 uPrime[i-first] = NewtonRaphsonRootFind(curve, points.at(i), u[i-first]);
441 }
442 return (uPrime);
443}
static qreal NewtonRaphsonRootFind(QPointF *Q, QPointF P, qreal u)

References NewtonRaphsonRootFind(), and u.

◆ VectorAdd()

static FitVector VectorAdd ( FitVector a,
FitVector b )
static

Definition at line 140 of file KoCurveFit.cpp.

141{
142 FitVector c;
143 c.m_X = a.m_X + b.m_X; c.m_Y = a.m_Y + b.m_Y;
144 return (c);
145}

References FitVector::m_X, and FitVector::m_Y.

◆ VectorScale()

static FitVector VectorScale ( FitVector v,
qreal s )
static

Definition at line 146 of file KoCurveFit.cpp.

147{
148 FitVector result;
149 result.m_X = v.m_X * s; result.m_Y = v.m_Y * s;
150 return (result);
151}

References FitVector::m_X, FitVector::m_Y, and v.

◆ VectorSub()

static FitVector VectorSub ( FitVector a,
FitVector b )
static

Definition at line 153 of file KoCurveFit.cpp.

154{
155 FitVector c;
156 c.m_X = a.m_X - b.m_X; c.m_Y = a.m_Y - b.m_Y;
157 return (c);
158}

References FitVector::m_X, and FitVector::m_Y.

Variable Documentation

◆ Zero

const qreal Zero = 10e-12

our equivalent to zero

Definition at line 14 of file KoCurveFit.cpp.