Handle out-of-bounds offsets/parameters correctly.

Closes #660
This commit is contained in:
Jürg Lehni 2015-04-04 17:05:39 +02:00
parent faecea3955
commit 7749641e66
2 changed files with 33 additions and 4 deletions

View file

@ -483,6 +483,9 @@ statics: {
// TODO: Instead of constants for type, use a "enum" and code substitution. // TODO: Instead of constants for type, use a "enum" and code substitution.
evaluate: function(v, t, type) { evaluate: function(v, t, type) {
// Do not produce results if parameter is out of range or invalid.
if (t == null || t < 0 || t > 1)
return null;
var p1x = v[0], p1y = v[1], var p1x = v[0], p1y = v[1],
c1x = v[2], c1y = v[3], c1x = v[2], c1y = v[3],
c2x = v[4], c2y = v[5], c2x = v[4], c2y = v[5],
@ -850,7 +853,9 @@ statics: {
getLocationAt: function(offset, isParameter) { getLocationAt: function(offset, isParameter) {
if (!isParameter) if (!isParameter)
offset = this.getParameterAt(offset); offset = this.getParameterAt(offset);
return offset >= 0 && offset <= 1 && new CurveLocation(this, offset); return offset != null && offset >= 0 && offset <= 1
? new CurveLocation(this, offset)
: null;
}, },
/** /**
@ -1025,7 +1030,9 @@ new function() { // Scope for methods that require numerical integration
return start; return start;
// See if we're going forward or backward, and handle cases // See if we're going forward or backward, and handle cases
// differently // differently
var forward = offset > 0, var tolerance = /*#=*/Numerical.TOLERANCE,
abs = Math.abs,
forward = offset > 0,
a = forward ? start : 0, a = forward ? start : 0,
b = forward ? 1 : start, b = forward ? 1 : start,
// Use integrand to calculate both range length and part // Use integrand to calculate both range length and part
@ -1034,8 +1041,13 @@ new function() { // Scope for methods that require numerical integration
// Get length of total range // Get length of total range
rangeLength = Numerical.integrate(ds, a, b, rangeLength = Numerical.integrate(ds, a, b,
getIterations(a, b)); getIterations(a, b));
if (Math.abs(offset) >= rangeLength) if (abs(offset - rangeLength) <= tolerance) {
// Matched the end:
return forward ? b : a; return forward ? b : a;
} else if (abs(offset) > rangeLength) {
// We're out of bounds.
return null;
}
// Use offset / rangeLength for an initial guess for t, to // Use offset / rangeLength for an initial guess for t, to
// bring us closer: // bring us closer:
var guess = offset / rangeLength, var guess = offset / rangeLength,
@ -1054,7 +1066,7 @@ new function() { // Scope for methods that require numerical integration
// Start with out initial guess for x. // Start with out initial guess for x.
// NOTE: guess is a negative value when not looking forward. // NOTE: guess is a negative value when not looking forward.
return Numerical.findRoot(f, ds, start + guess, a, b, 16, return Numerical.findRoot(f, ds, start + guess, a, b, 16,
/*#=*/Numerical.TOLERANCE); tolerance);
} }
}; };
}, new function() { // Scope for intersection using bezier fat-line clipping }, new function() { // Scope for intersection using bezier fat-line clipping

View file

@ -31,6 +31,9 @@ test('Curve#getPointAt()', function() {
equals(curve.getPointAt(entry[0], true), entry[1], equals(curve.getPointAt(entry[0], true), entry[1],
'curve.getPointAt(' + entry[0] + ', true);'); 'curve.getPointAt(' + entry[0] + ', true);');
} }
equals(curve.getPointAt(curve.length + 1), null,
'Should return null when offset is out of range.');
}); });
test('Curve#getTangentAt()', function() { test('Curve#getTangentAt()', function() {
@ -132,4 +135,18 @@ test('Curve#getParameterAt()', function() {
+ ' should be the same value as at offset' + o2, + ' should be the same value as at offset' + o2,
Numerical.TOLERANCE); Numerical.TOLERANCE);
} }
equals(curve.getParameterAt(curve.length + 1), null,
'Should return null when offset is out of range.');
});
test('Curve#getLocationAt()', function() {
var curve = new Path([
[[0, 0], [0, 0], [100, 0]],
[[200, 200]],
]).firstCurve;
equals(curve.getLocationAt(curve.length + 1), null,
'Should return null when offset is out of range.');
// 'Should return null when point is not on the curve.');
}); });