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;
if (length >= rangeLength)
return forward ? b : a;
// Iteratively calculate curve range lengths, and add them up,
// using integration precision depending on the size of the
// range. This is much faster and also more precise than not
@ -396,10 +397,9 @@ var Curve = this.Curve = Base.extend({
start = t;
return len - length;
}
return Numerical.findRootNewton(f, ds,
forward ? a : b - guess, // a
forward ? a + guess : b, // b
16, Numerical.TOLERANCE);
return Numerical.findRoot(f, ds,
forward ? a + guess : b - guess, // Initial guess for x
a, b, 16, Numerical.TOLERANCE);
},
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
* 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.
* Root finding using Newton-Raphson Method combined with Bisection.
*/
findRootNewton: function(f, fd, a, b, n, tol) {
var x = 0.5 * (a + b);
findRoot: function(f, df, x, a, b, n, tol) {
for (var i = 0; i < n; i++) {
var dx = f(x) / fd(x);
x -= dx;
// Clip to 0 .. t .. 1. See comment above
if (x < 0) x = 0;
else if (x > 1) x = 1;
var fx = f(x),
dx = fx / df(x);
if (Math.abs(dx) < tol)
return x;
}
// If we did not succeed, fall back on False Position method for
// accurate results.
return Numerical.findRootFalsePosition(f, a, b, n, tol);
},
findRootFalsePosition: function(f, a, b, n, tol) {
var fa = f(a),
fb = f(b),
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;
// Generate a candidate for Newton's method.
var nx = x - dx;
// Update the root-bounding interval and test for containment of
// the candidate. If candidate is outside the root-bounding
// interval, use bisection instead.
// There is no need to compare to lower / upper because the
// tangent line has positive slope, guaranteeing that the x-axis
// intercept is larger than lower / smaller than upper.
if (fx > 0) {
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;
}
};
};