From d5c25dbc52ba26f9a5c98226c7a1d96e63c9e4a2 Mon Sep 17 00:00:00 2001 From: Peter Vanbroekhoven Date: Sun, 22 Feb 2015 20:05:06 +0100 Subject: [PATCH 1/2] Change PathFitter to constrain newly found control points. See https://github.com/paperjs/paper.js/issues/414 . --- src/path/PathFitter.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/path/PathFitter.js b/src/path/PathFitter.js index 0abe478f..6780c2f6 100644 --- a/src/path/PathFitter.js +++ b/src/path/PathFitter.js @@ -80,13 +80,14 @@ var PathFitter = Base.extend({ // Parameterize points, and attempt to fit curve var uPrime = this.chordLengthParameterize(first, last), maxError = Math.max(this.error, this.error * this.error), - split; + split, + parametersInOrder = true; // Try 4 iterations for (var i = 0; i <= 4; i++) { var curve = this.generateBezier(first, last, uPrime, tan1, tan2); // Find max deviation of points to fitted curve var max = this.findMaxError(first, last, curve, uPrime); - if (max.error < this.error) { + if (max.error < this.error && parametersInOrder) { this.addCurve(curve); return; } @@ -94,7 +95,7 @@ var PathFitter = Base.extend({ // If error not too large, try reparameterization and iteration if (max.error >= maxError) break; - this.reparameterize(first, last, uPrime, curve); + parametersInOrder = this.reparameterize(first, last, uPrime, curve); maxError = max.error; } // Fitting failed -- split at max error point and fit recursively @@ -178,12 +179,23 @@ var PathFitter = Base.extend({ alpha1 = alpha2 = segLength / 3; } + // Check if the found control points are in the right order when + // projected onto the line through pt1 and pt2. + var line = pt2.subtract(pt1), + // Control points 1 and 2 are positioned an alpha distance out + // on the tangent vectors, left and right, respectively + cp1Delta = tan1.normalize(alpha1), + cp2Delta = tan2.normalize(alpha2); + if (cp1Delta.dot(line) - cp2Delta.dot(line) > segLength * segLength) { + // Fall back to the Wu/Barsky heuristic above. + alpha1 = alpha2 = segLength / 3; + cp1Delta = tan1.normalize(alpha1); + cp2Delta = tan2.normalize(alpha2); + } + // First and last control points of the Bezier curve are // positioned exactly at the first and last data points - // Control points 1 and 2 are positioned an alpha distance out - // on the tangent vectors, left and right, respectively - return [pt1, pt1.add(tan1.normalize(alpha1)), - pt2.add(tan2.normalize(alpha2)), pt2]; + return [pt1, pt1.add(cp1Delta), pt2.add(cp2Delta), pt2]; }, // Given set of points and their parameterization, try to find @@ -192,6 +204,15 @@ var PathFitter = Base.extend({ for (var i = first; i <= last; i++) { u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); } + // Detect if the new parameterization has reordered the points. + // In that case, we would fit the points of the path in the + // wrong order. + for (var j = 1, jLen = u.length; j < jLen; j++) { + if (u[j] <= u[j - 1]) { + return false; + } + } + return true; }, // Use Newton-Raphson iteration to find better root. From 563a26f11296dba57708af9a81d5713da95564ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ju=CC=88rg=20Lehni?= Date: Sat, 4 Apr 2015 15:54:34 +0200 Subject: [PATCH 2/2] Clean up PathFitter fix a bit. --- src/path/PathFitter.js | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/path/PathFitter.js b/src/path/PathFitter.js index 6780c2f6..a7b4a57f 100644 --- a/src/path/PathFitter.js +++ b/src/path/PathFitter.js @@ -171,31 +171,33 @@ var PathFitter = Base.extend({ // If alpha negative, use the Wu/Barsky heuristic (see text) // (if alpha is 0, you get coincident control points that lead to // divide by zero in any subsequent NewtonRaphsonRootFind() call. - var segLength = pt2.getDistance(pt1); - epsilon *= segLength; - if (alpha1 < epsilon || alpha2 < epsilon) { + var segLength = pt2.getDistance(pt1), + eps = epsilon * segLength, + handle1, + handle2; + if (alpha1 < eps || alpha2 < eps) { // fall back on standard (probably inaccurate) formula, // and subdivide further if needed. alpha1 = alpha2 = segLength / 3; - } - - // Check if the found control points are in the right order when - // projected onto the line through pt1 and pt2. - var line = pt2.subtract(pt1), + } else { + // Check if the found control points are in the right order when + // projected onto the line through pt1 and pt2. + var line = pt2.subtract(pt1); // Control points 1 and 2 are positioned an alpha distance out // on the tangent vectors, left and right, respectively - cp1Delta = tan1.normalize(alpha1), - cp2Delta = tan2.normalize(alpha2); - if (cp1Delta.dot(line) - cp2Delta.dot(line) > segLength * segLength) { - // Fall back to the Wu/Barsky heuristic above. - alpha1 = alpha2 = segLength / 3; - cp1Delta = tan1.normalize(alpha1); - cp2Delta = tan2.normalize(alpha2); + handle1 = tan1.normalize(alpha1); + handle2 = tan2.normalize(alpha2); + if (handle1.dot(line) - handle2.dot(line) > segLength * segLength) { + // Fall back to the Wu/Barsky heuristic above. + alpha1 = alpha2 = segLength / 3; + handle1 = handle2 = null; // Force recalculation + } } // First and last control points of the Bezier curve are // positioned exactly at the first and last data points - return [pt1, pt1.add(cp1Delta), pt2.add(cp2Delta), pt2]; + return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)), + pt2.add(handle2 || tan2.normalize(alpha2)), pt2]; }, // Given set of points and their parameterization, try to find @@ -205,12 +207,10 @@ var PathFitter = Base.extend({ u[i - first] = this.findRoot(curve, this.points[i], u[i - first]); } // Detect if the new parameterization has reordered the points. - // In that case, we would fit the points of the path in the - // wrong order. - for (var j = 1, jLen = u.length; j < jLen; j++) { - if (u[j] <= u[j - 1]) { - return false; - } + // In that case, we would fit the points of the path in the wrong order. + for (var i = 1, l = u.length; i < l; i++) { + if (u[i] <= u[i - 1]) + return false; } return true; },