From c513a24f4653276becdb2cc0a3ec5565e425b685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Fri, 26 Apr 2013 14:31:42 -0700 Subject: [PATCH] Handle intersection between linear curves without recursive subdivision. Closes #207. --- src/basic/Line.js | 16 ++++++++-------- src/basic/Point.js | 2 +- src/path/Curve.js | 31 ++++++++++++++++++++----------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/basic/Line.js b/src/basic/Line.js index 6f113d9d..5faebe30 100644 --- a/src/basic/Line.js +++ b/src/basic/Line.js @@ -67,23 +67,23 @@ var Line = this.Line = Base.extend(/** @lends Line# */{ /** * @param {Line} line - * @return {Point} the intersection point of the lines + * @return {Point} the intersection point of the lines, {@code undefined} + * if the two lines are colinear, or {@code null} if they don't intersect. */ - intersect: function(line, excludeStart, excludeEnd) { + intersect: function(line) { var cross = this.vector.cross(line.vector); // Avoid divisions by 0, and errors when getting too close to 0 if (Numerical.isZero(cross)) - return null; + return undefined; var v = line.point.subtract(this.point), t1 = v.cross(line.vector) / cross, t2 = v.cross(this.vector) / cross; // Check the ranges of t parameters if the line is not allowed to // extend beyond the definition points. - return (this.infinite || (excludeStart ? 0 < t1 : 0 <= t1) - && (excludeEnd ? t1 < 1 : t1 <= 1)) - && (line.infinite || (excludeStart ? 0 < t2 : 0 <= t2) - && (excludeEnd ? t2 < 1 : t2 <= 1)) - ? this.point.add(this.vector.multiply(t1)) : null; + return (this.infinite || 0 <= t1 && t1 <= 1) + && (line.infinite || 0 <= t2 && t2 <= 1) + ? this.point.add(this.vector.multiply(t1)) + : null; }, // DOCS: document Line#getSide(point) diff --git a/src/basic/Point.js b/src/basic/Point.js index 6a039b8c..ed8c0ab5 100644 --- a/src/basic/Point.js +++ b/src/basic/Point.js @@ -685,7 +685,7 @@ var Point = this.Point = Base.extend(/** @lends Point# */{ * another vector. * * @param {Point} point the vector to check against - * @returns {Boolean} {@true it is parallel} + * @returns {Boolean} {@true it is colinear} */ isColinear: function(point) { return this.cross(point) < /*#=*/ Numerical.TOLERANCE; diff --git a/src/path/Curve.js b/src/path/Curve.js index f4225734..67de8279 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -638,6 +638,10 @@ statics: { return v; }, + isLinear: function(v) { + return v[0] === v[2] && v[1] === v[3] && v[4] === v[6] && v[5] === v[7]; + }, + isFlatEnough: function(v, tolerance) { // Thanks to Kaspar Fischer and Roger Willcocks for the following: // http://hcklbrrfnn.files.wordpress.com/2012/08/bez.pdf @@ -728,8 +732,10 @@ statics: { }); /*#*/ } if (bounds1.touches(bounds2)) { - // See if both curves are flat enough to be treated as lines. - if (this.isFlatEnough(v1, /*#=*/ Numerical.TOLERANCE) + // See if both curves are flat enough to be treated as lines, either + // because they have no control points at all, or are "flat enough" + if (this.isLinear(v1) && this.isLinear(v2) + || this.isFlatEnough(v1, /*#=*/ Numerical.TOLERANCE) && this.isFlatEnough(v2, /*#=*/ Numerical.TOLERANCE)) { /*#*/ if (options.debug) { new Path.Line({ @@ -747,15 +753,18 @@ statics: { /*#*/ } // See if the parametric equations of the lines interesct. var point = new Line(v1[0], v1[1], v1[6], v1[7], false) - .intersect(new Line(v2[0], v2[1], v2[6], v2[7], false), - // Filter out beginnings of the curves, to avoid - // duplicate solutions where curves join. - true, false); - if (point) - // Passing null for parameter leads to lazy determination of - // parameter values in CurveLocation#getParameter() only - // once they are requested. - locations.push(new CurveLocation(curve, null, point)); + .intersect(new Line(v2[0], v2[1], v2[6], v2[7], false)); + if (point) { + // Avoid duplicates when hitting segments (closed paths too) + var first = locations[0], + last = locations[locations.length - 1]; + if ((!first || !point.equals(first._point)) + && (!last || !point.equals(last._point))) + // Passing null for parameter leads to lazy determination + // of parameter values in CurveLocation#getParameter() + // only once they are requested. + locations.push(new CurveLocation(curve, null, point)); + } } else { // Subdivide both curves, and see if they intersect. var v1s = this.subdivide(v1),