Improve and clean up fat-line bug fix.

This commit is contained in:
Jürg Lehni 2013-06-13 15:53:28 -07:00
parent 38f31be6b2
commit 01e48b3322
5 changed files with 30 additions and 36 deletions

View file

@ -485,7 +485,7 @@ function testIntersections(path1, path2, caption, testname, testdata, nomark) {
fatTime: t2, fatTime: t2,
success: success success: success
}); });
console.log(found); console.log(ixsPaper.length, found);
if (!success) { if (!success) {
var ser = new XMLSerializer(); var ser = new XMLSerializer();
console.log('failcase:', ser.serializeToString(path1.exportSVG()), console.log('failcase:', ser.serializeToString(path1.exportSVG()),

View file

@ -19,6 +19,6 @@ var options = {
browser: true, browser: true,
stats: true, stats: true,
svg: true, svg: true,
fatline: false, fatline: true,
debug: false debug: false
}; };

View file

@ -869,7 +869,7 @@ statics: {
step /= 2; step /= 2;
} }
var pt = Curve.evaluate(values, minT, true, 0); var pt = Curve.evaluate(values, minT, true, 0);
return new CurveLocation(this, minT, pt, null, null, return new CurveLocation(this, minT, pt, null, null, null,
point.getDistance(pt)); point.getDistance(pt));
}, },
@ -966,7 +966,7 @@ new function() { // Scope for methods that require numerical integration
a = 0; a = 0;
if (b === undefined) if (b === undefined)
b = 1; b = 1;
// if (p1 == c1 && p2 == c2): // See if the curve is linear by checking p1 == c1 and p2 == c2
if (v[0] == v[2] && v[1] == v[3] && v[6] == v[4] && v[7] == v[5]) { if (v[0] == v[2] && v[1] == v[3] && v[6] == v[4] && v[7] == v[5]) {
// Straight line // Straight line
var dx = v[6] - v[0], // p2x - p1x var dx = v[6] - v[0], // p2x - p1x
@ -1030,13 +1030,14 @@ new function() { // Scope for methods that require numerical integration
} }
}; };
}, new function() { // Scope for intersection using bezier fat-line clipping }, new function() { // Scope for intersection using bezier fat-line clipping
function addLocation(locations, curve1, t1, point, curve2, t2) { function addLocation(locations, curve1, t1, point1, curve2, t2, point2) {
// Avoid duplicates when hitting segments (closed paths too) // Avoid duplicates when hitting segments (closed paths too)
var first = locations[0], var first = locations[0],
last = locations[locations.length - 1]; last = locations[locations.length - 1];
if ((!first || !point.equals(first._point)) if ((!first || !point1.equals(first._point))
&& (!last || !point.equals(last._point))) && (!last || !point1.equals(last._point)))
locations.push(new CurveLocation(curve1, t1, point, curve2, t2)); locations.push(
new CurveLocation(curve1, t1, point1, curve2, t2, point2));
} }
function addCurveIntersections(v1, v2, curve1, curve2, locations, function addCurveIntersections(v1, v2, curve1, curve2, locations,
@ -1065,9 +1066,7 @@ new function() { // Scope for methods that require numerical integration
// degenerate case seperately, where fat-line clipping can become // degenerate case seperately, where fat-line clipping can become
// numerically unstable when one of the curves has converged to a point // numerically unstable when one of the curves has converged to a point
// and the other hasn't. // and the other hasn't.
while (iteration++ < 20 while (iteration++ < 20) {
&& (Math.abs(range1[1] - range1[0]) > /*#=*/ Numerical.TOLERANCE
|| Math.abs(range2[1] - range2[0]) > /*#=*/ Numerical.TOLERANCE)) {
// First we clip v2 with v1's fat-line // First we clip v2 with v1's fat-line
var range, var range,
intersects1 = clipFatLine(part1, part2, range = range2.slice()), intersects1 = clipFatLine(part1, part2, range = range2.slice()),
@ -1119,14 +1118,15 @@ new function() { // Scope for methods that require numerical integration
} }
} }
// We need to bailout of clipping and try a numerically stable // We need to bailout of clipping and try a numerically stable
// method if both of the parameter ranges have converged reasonably well // method if both of the parameter ranges have converged reasonably
// (according to Numerical.TOLERANCE). // well (according to Numerical.TOLERANCE).
if (Math.abs(range1[1] - range1[0]) < /*#=*/ Numerical.TOLERANCE && if (Math.abs(range1[1] - range1[0]) <= /*#=*/ Numerical.TOLERANCE &&
Math.abs(range2[1] - range2[0]) < /*#=*/ Numerical.TOLERANCE) { Math.abs(range2[1] - range2[0]) <= /*#=*/ Numerical.TOLERANCE) {
var t1 = (range1[0] + range1[1]) / 2, var t1 = (range1[0] + range1[1]) / 2,
t2 = (range2[0] + range2[1]) / 2; t2 = (range2[0] + range2[1]) / 2;
addLocation(locations, curve1, t1, addLocation(locations,
Curve.evaluate(v1, t1, true, 0), curve2, t2); curve1, t1, Curve.evaluate(v1, t1, true, 0),
curve2, t2, Curve.evaluate(v2, t2, true, 0));
break; break;
} }
} }
@ -1330,10 +1330,9 @@ new function() { // Scope for methods that require numerical integration
* line is on the X axis, and solve the implicit equations for the X axis * line is on the X axis, and solve the implicit equations for the X axis
* and the curve. * and the curve.
*/ */
function addCurveLineIntersections(v1, v2, curve1, curve2, locations, flip) { function addCurveLineIntersections(v1, v2, curve1, curve2, locations) {
if (flip === undefined) var flip = Curve.isLinear(v1),
flip = Curve.isLinear(v1); vc = flip ? v2 : v1,
var vc = flip ? v2 : v1,
vl = flip ? v1 : v2, vl = flip ? v1 : v2,
l1x = vl[0], l1y = vl[1], l1x = vl[0], l1y = vl[1],
l2x = vl[6], l2y = vl[7], l2x = vl[6], l2y = vl[7],

View file

@ -36,8 +36,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* @param {Number} parameter * @param {Number} parameter
* @param {Point} point * @param {Point} point
*/ */
initialize: function CurveLocation(curve, parameter, point, _otherCurve, initialize: function CurveLocation(curve, parameter, point, _curve2,
_otherParameter, _distance) { _parameter2, _point2, _distance) {
// Define this CurveLocation's unique id. // Define this CurveLocation's unique id.
this._id = CurveLocation._id = (CurveLocation._id || 0) + 1; this._id = CurveLocation._id = (CurveLocation._id || 0) + 1;
this._curve = curve; this._curve = curve;
@ -48,8 +48,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
this._segment2 = curve._segment2; this._segment2 = curve._segment2;
this._parameter = parameter; this._parameter = parameter;
this._point = point; this._point = point;
this._otherCurve = _otherCurve; this._curve2 = _curve2;
this._otherParameter = _otherParameter; this._parameter2 = _parameter2;
this._point2 = _point2;
this._distance = _distance; this._distance = _distance;
}, },
@ -110,17 +111,12 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
*/ */
getIntersection: function() { getIntersection: function() {
var intersection = this._intersection; var intersection = this._intersection;
if (!intersection && this._otherCurve) { if (!intersection && this._curve2) {
var param = this._otherParameter; var param = this._parameter2;
// If we have the parameter on the other curve use that for // If we have the parameter on the other curve use that for
// intersection rather than the point. // intersection rather than the point.
this._intersection = intersection = new CurveLocation( this._intersection = intersection = new CurveLocation(
this._otherCurve, param, param ? null : this._point, this); this._curve2, param, this._point2 || this._point, this);
// Force calculate the other point from the parameter.
// DEBUG: @jlehni - Not sure why we have to do this? Shouldn't
// it auto-calculate upon first access?!
if (param)
intersection.getPoint();
intersection._intersection = this; intersection._intersection = this;
} }
return intersection; return intersection;
@ -197,8 +193,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* @type Point * @type Point
* @bean * @bean
*/ */
getPoint: function() { getPoint: function(/* uncached */) {
if (!this._point && this._parameter != null) { if ((!this._point || arguments[0]) && this._parameter != null) {
var curve = this.getCurve(); var curve = this.getCurve();
this._point = curve && curve.getPointAt(this._parameter, true); this._point = curve && curve.getPointAt(this._parameter, true);
} }

View file

@ -61,7 +61,6 @@ var Numerical = new function() {
return { return {
TOLERANCE: 10e-6, TOLERANCE: 10e-6,
// Precision when comparing against 0 // Precision when comparing against 0
// TODO: Find a good value
EPSILON: 10e-12, EPSILON: 10e-12,
// Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/ // Kappa, see: http://www.whizkidtech.redprince.net/bezier/circle/kappa/
KAPPA: 4 * (sqrt(2) - 1) / 3, KAPPA: 4 * (sqrt(2) - 1) / 3,