mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-03 19:45:44 -05:00
Improve curve time parametrization precision by iteratively adding up sub-range lengths, and optimise speed by determining integration precision based on range size.
This commit is contained in:
parent
a794816097
commit
417d015eab
2 changed files with 41 additions and 29 deletions
|
@ -302,7 +302,12 @@ var Curve = this.Curve = Base.extend({
|
|||
}
|
||||
|
||||
// Amount of integral evaluations
|
||||
var numEval = 16;
|
||||
function getIterations(a, b) {
|
||||
// Guess required precision based and size of range...
|
||||
// TODO: There should be much better educated guesses for
|
||||
// this. Also, what does this depend on? Required precision?
|
||||
return Math.max(2, Math.min(16, Math.ceil(Math.abs(b - a) * 32)));
|
||||
}
|
||||
|
||||
return {
|
||||
getPoint: function(parameter) {
|
||||
|
@ -331,7 +336,7 @@ var Curve = this.Curve = Base.extend({
|
|||
}
|
||||
var ds = getLengthIntegrand(
|
||||
p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
|
||||
return Numerical.integrate(ds, a, b, numEval);
|
||||
return Numerical.integrate(ds, a, b, getIterations(a, b));
|
||||
},
|
||||
|
||||
getParameter: function(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y,
|
||||
|
@ -347,39 +352,39 @@ var Curve = this.Curve = Base.extend({
|
|||
return Math.max(Math.min(start
|
||||
+ length / Math.sqrt(dx * dx + dy * dy), 0, 1));
|
||||
}
|
||||
// Let's use the Van Wijngaarden–Dekker–Brent Method to find
|
||||
// solutions more reliably than with False Position Method.
|
||||
// The precision of 5 iterations seems enough for this
|
||||
// See if we're going forward or backward, and handle cases
|
||||
// differently
|
||||
var forward = length > 0,
|
||||
a = forward ? start : 0,
|
||||
b = forward ? 1 : start,
|
||||
length = Math.abs(length),
|
||||
// Use integrand to calculate both range length and part
|
||||
// lengths in f(t) below.
|
||||
ds = getLengthIntegrand(
|
||||
p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y),
|
||||
a, b, f;
|
||||
// See if we're going forward or backward, and handle cases
|
||||
// differently
|
||||
if (forward) { // Normal way
|
||||
a = start;
|
||||
b = 1;
|
||||
// We're moving b to the right to find root for length
|
||||
f = function(t) {
|
||||
return Numerical.integrate(ds, a, t, numEval) - length;
|
||||
}
|
||||
} else { // Going backwards
|
||||
a = 0;
|
||||
b = start;
|
||||
length = -length;
|
||||
// We're moving a to the left to find root for length
|
||||
f = function(t) {
|
||||
return Numerical.integrate(ds, t, b, numEval) - length;
|
||||
}
|
||||
}
|
||||
var rangeLength = Numerical.integrate(ds, a, b, numEval);
|
||||
// Get length of total range
|
||||
rangeLength = Numerical.integrate(ds, a, b,
|
||||
getIterations(a, b)),
|
||||
// Use length / rangeLength for an initial guess for t, to
|
||||
// bring us closer:
|
||||
guess = length / rangeLength,
|
||||
len = 0;
|
||||
if (length >= rangeLength)
|
||||
return forward ? b : a;
|
||||
// Use length / rangeLength for an initial guess for t, to
|
||||
// bring us closer:
|
||||
var guess = length / rangeLength;
|
||||
// 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
|
||||
// modifing start and calculating total length each time.
|
||||
function f(t) {
|
||||
var count = getIterations(start, t);
|
||||
if (start < t) {
|
||||
len += Numerical.integrate(ds, start, t, count);
|
||||
} else {
|
||||
len -= Numerical.integrate(ds, t, start, count);
|
||||
}
|
||||
start = t;
|
||||
return len - length;
|
||||
}
|
||||
return Numerical.findRootNewton(f, ds,
|
||||
forward ? a : b - guess, // a
|
||||
forward ? a + guess : b, // b
|
||||
|
|
|
@ -74,13 +74,20 @@ var Numerical = new function() {
|
|||
},
|
||||
|
||||
/**
|
||||
* Newton-Raphson Method Using Derivative
|
||||
* 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.
|
||||
*/
|
||||
findRootNewton: function(f, fd, a, b, n, tol) {
|
||||
var x = 0.5 * (a + b);
|
||||
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;
|
||||
if (Math.abs(dx) < tol)
|
||||
return x;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue