diff --git a/src/load.js b/src/load.js index cb864fae..15128719 100644 --- a/src/load.js +++ b/src/load.js @@ -23,7 +23,8 @@ var options = { browser: true, stats: true, version: 'dev', - parser: 'acorn' + parser: 'acorn', + debug: false }; // This folder is specified relatively to the lib folder from which prepro.js is diff --git a/src/path/Curve.js b/src/path/Curve.js index ecff8d3c..3bbd278f 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -313,6 +313,11 @@ var Curve = this.Curve = Base.extend(/** @lends Curve# */{ return Curve.getParameter(this.getValues(), point.x, point.y); }, + getIntersections: function(curve) { + return Curve._addIntersections(this.getValues(), curve.getValues(), + this, []); + }, + getCrossings: function(point, roots) { // Implement the crossing number algorithm: // http://en.wikipedia.org/wiki/Point_in_polygon @@ -340,7 +345,6 @@ var Curve = this.Curve = Base.extend(/** @lends Curve# */{ }, // TODO: getLocation - // TODO: getIntersections // TODO: adjustThroughPoint /** @@ -610,6 +614,63 @@ statics: { + t * t * t * v3, padding); } + }, + + // We need to provide the original left curve reference to the + // #_addIntersections() calls as it is required for the returned + // CurveLocation instances. + _addIntersections: function(v1, v2, curve, locations) { + var bounds1 = Curve.getBounds(v1), + bounds2 = Curve.getBounds(v2); +/*#*/ if (options.debug) { + new Path.Rectangle(bounds1).set({ + strokeColor: 'green', + strokeWidth: 0.1 + }); + new Path.Rectangle(bounds2).set({ + strokeColor: 'red', + strokeWidth: 0.1 + }); +/*#*/ } + // We are not using Rectangle#intersects() here, since in order to + // detect intersections that lie on curve bounds, we need to consider + // touching on one side of the tested rectangles as intersection as well + // If touch is condired at both sides, solutions lying on the border of + // bounds would turn up twice. + if (bounds1.x + bounds1.width >= bounds2.x + && bounds1.y + bounds1.height >= bounds2.y + && bounds1.x < bounds2.x + bounds2.width + && bounds1.y < bounds2.y + bounds2.height) { + if (Curve.isFlatEnough(v1) && Curve.isFlatEnough(v2)) { + // Treat both curves as lines and see if their parametric + // equations interesct. +/*#*/ if (options.debug) { + new Path.Line(v1[0], v1[1], v1[6], v1[7]).set({ + strokeColor: 'green', + strokeWidth: 0.1 + }); + new Path.Line(v2[0], v2[1], v2[6], v2[7]).set({ + strokeColor: 'red', + strokeWidth: 0.1 + }); +/*#*/ } + 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)); + // Passing null for parameter leads to lazy determination of + // parameter values in CurveLocation#getParameter() only once + // they are requested. + if (point) + locations.push(new CurveLocation(curve, null, point)); + } else { + var v1s = Curve.subdivide(v1), + v2s = Curve.subdivide(v2); + this._addIntersections(v1s[0], v2s[0], curve, locations); + this._addIntersections(v1s[0], v2s[1], curve, locations); + this._addIntersections(v1s[1], v2s[0], curve, locations); + this._addIntersections(v1s[1], v2s[1], curve, locations); + } + } + return locations; } }}, Base.each(['getBounds', 'getStrokeBounds', 'getHandleBounds', 'getRoughBounds'], // Note: Although Curve.getBounds() exists, we are using Path.getBounds() to diff --git a/src/path/CurveLocation.js b/src/path/CurveLocation.js index a2d174d0..4c327376 100644 --- a/src/path/CurveLocation.js +++ b/src/path/CurveLocation.js @@ -24,12 +24,12 @@ * {@link Path#curves} array is also provided. * * The class is in use in many places, such as - * {@link Path#getLocationAt(offset)}, Path#getNearestLocation(point), etc. + * {@link Path#getLocationAt(offset)}, Path#getNearestLocation(point), + * {@link PathItem#getIntersections(path)}, etc. */ CurveLocation = Base.extend(/** @lends CurveLocation# */{ // DOCS: CurveLocation class description: add these back when the mentioned - // functioned have been added: {@link Path#split(location)}, - // {@link PathItem#getIntersections(path)}, etc. + // functioned have been added: {@link Path#split(location)} /** * Creates a new CurveLocation object. * diff --git a/src/path/Path.js b/src/path/Path.js index b124deb6..c2d63eea 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -1348,7 +1348,6 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{ // TODO: intersect(item) // TODO: unite(item) // TODO: exclude(item) - // TODO: getIntersections(path) }, new function() { // Scope for drawing // Note that in the code below we're often accessing _x and _y on point diff --git a/src/path/PathItem.js b/src/path/PathItem.js index 6707c6d1..3b22c0f9 100644 --- a/src/path/PathItem.js +++ b/src/path/PathItem.js @@ -24,14 +24,26 @@ * @extends Item */ var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{ + getIntersections: function(path) { // First check the bounds of the two paths. If they don't intersect, // we don't need to iterate through the whole path. - if (this.getBounds().intersects(path.getBounds())) + if (!this.getBounds().intersects(path.getBounds())) return []; var locations = [], curves1 = this.getCurves(), - curves2 = path.getCurves(); + curves2 = path.getCurves(), + length2 = curves2.length, + values2 = []; + for (var i = 0; i < length2; i++) + values2[i] = curves2[i].getValues(); + for (var i = 0, l = curves1.length; i < l; i++) { + var curve = curves1[i], + values1 = curve.getValues(); + for (var j = 0; j < length2; j++) + Curve._addIntersections(values1, values2[j], curve, locations); + } + return locations; } /** * Smooth bezier curves without changing the amount of segments or their