Implement new root finding algorithm, combining Newton-Raphson Method with Bisection, and update Curve#getParameter() to use it.

This commit is contained in:
Jürg Lehni 2011-04-26 12:23:09 +01:00
parent 7868bc1bdc
commit 6609dc2307
2 changed files with 21 additions and 41 deletions

View file

@ -382,6 +382,7 @@ var Curve = this.Curve = Base.extend({
len = 0; len = 0;
if (length >= rangeLength) if (length >= rangeLength)
return forward ? b : a; return forward ? b : a;
// Iteratively calculate curve range lengths, and add them up, // Iteratively calculate curve range lengths, and add them up,
// using integration precision depending on the size of the // using integration precision depending on the size of the
// range. This is much faster and also more precise than not // range. This is much faster and also more precise than not
@ -396,10 +397,9 @@ var Curve = this.Curve = Base.extend({
start = t; start = t;
return len - length; return len - length;
} }
return Numerical.findRootNewton(f, ds, return Numerical.findRoot(f, ds,
forward ? a : b - guess, // a forward ? a + guess : b - guess, // Initial guess for x
forward ? a + guess : b, // b a, b, 16, Numerical.TOLERANCE);
16, Numerical.TOLERANCE);
}, },
subdivide: function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { subdivide: function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {

View file

@ -77,50 +77,30 @@ var Numerical = new function() {
}, },
/** /**
* Newton-Raphson Method Using Derivative. This is a special version * Root finding using Newton-Raphson Method combined with Bisection.
* that clips results to 0 .. 1, as required by Paper.js iterative
* approach for curve time parametrization: Ending up far outside
* the bezier curve boundaries resulted in inprecision of added up
* curve lengths.
*/ */
findRootNewton: function(f, fd, a, b, n, tol) { findRoot: function(f, df, x, a, b, n, tol) {
var x = 0.5 * (a + b);
for (var i = 0; i < n; i++) { for (var i = 0; i < n; i++) {
var dx = f(x) / fd(x); var fx = f(x),
x -= dx; dx = fx / df(x);
// Clip to 0 .. t .. 1. See comment above
if (x < 0) x = 0;
else if (x > 1) x = 1;
if (Math.abs(dx) < tol) if (Math.abs(dx) < tol)
return x; return x;
} // Generate a candidate for Newton's method.
// If we did not succeed, fall back on False Position method for var nx = x - dx;
// accurate results. // Update the root-bounding interval and test for containment of
return Numerical.findRootFalsePosition(f, a, b, n, tol); // the candidate. If candidate is outside the root-bounding
}, // interval, use bisection instead.
// There is no need to compare to lower / upper because the
findRootFalsePosition: function(f, a, b, n, tol) { // tangent line has positive slope, guaranteeing that the x-axis
var fa = f(a), // intercept is larger than lower / smaller than upper.
fb = f(b), if (fx > 0) {
dx = b - a,
del, x;
for (var i = 0; i < n; i++) {
x = a + dx * fa / (fa - fb);
var fx = f(x);
if (fx < 0) {
del = a - x;
a = x;
fa = fx;
} else {
del = b - x;
b = x; b = x;
fb = fx; x = nx <= a ? 0.5 * (a + b) : nx;
} else {
a = x;
x = nx >= b ? 0.5 * (a + b) : nx;
} }
dx = b - a;
if (Math.abs(del) < tol || fx == 0)
return x;
} }
return x;
} }
}; };
}; };