Handle intersection between linear curves without recursive subdivision.

Closes #207.
This commit is contained in:
Jürg Lehni 2013-04-26 14:31:42 -07:00
parent 06ffd8089d
commit c513a24f46
3 changed files with 29 additions and 20 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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),