diff --git a/src/path/Curve.js b/src/path/Curve.js index da2a3766..581d2427 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -402,8 +402,8 @@ var Curve = Base.extend(/** @lends Curve# */{ * curves */ getIntersections: function(curve) { - return Curve._filterIntersections(Curve._getIntersections( - this.getValues(), curve.getValues(), this, curve, [], {})); + return Curve._getIntersections(this.getValues(), curve.getValues(), + this, curve, [], {}); }, // TODO: adjustThroughPoint @@ -1359,11 +1359,11 @@ new function() { // Scope for intersection using bezier fat-line clipping if (!Numerical.isZero(d1) || !Numerical.isZero(d2)) debugger; */ - locations.push( + CurveLocation.add(locations, new CurveLocation(c1, t1, p1 || Curve.getPoint(v1, t1), null, overlap, new CurveLocation(c2, t2, p2 || Curve.getPoint(v2, t2), - null, overlap))); + null, overlap)), true); } } diff --git a/src/path/CurveLocation.js b/src/path/CurveLocation.js index 3b12fd60..aa30785f 100644 --- a/src/path/CurveLocation.js +++ b/src/path/CurveLocation.js @@ -358,8 +358,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{ || loc instanceof CurveLocation // Call getCurve() and getParameter() to keep in sync && this.getCurve() === loc.getCurve() - && this.getPoint().isClose(loc.getPoint(), - /*#=*/Numerical.GEOMETRIC_EPSILON) + && Math.abs(this.getParameter() - loc.getParameter()) + < /*#=*/Numerical.CURVETIME_EPSILON && (_ignoreOther || (!this._intersection && !loc._intersection || this._intersection && this._intersection.equals( @@ -388,36 +388,55 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{ }, statics: { - sort: function(locations) { - function compare(l1, l2, _ignoreOther) { - if (!l1 || !l2) - return l1 ? -1 : 0; - var curve1 = l1._curve, - curve2 = l2._curve, + add: function(locations, loc, merge) { + // Insert-sort by path-id, curve, parameter so we can easily merge + // duplicates with calls to equals() after. + // NOTE: We don't call getCurve() / getParameter() here, since this + // code is used internally in boolean operations where all this + // information remains valid during processing. + var l = 0, + r = locations.length - 1; + while (l <= r) { + var m = (l + r) >>> 1, + loc2 = locations[m], + curve1 = loc._curve, + curve2 = loc2._curve, path1 = curve1._path, - path2 = curve2._path, - diff; - // Sort by path-id, curve, parameter, curve2, parameter2 so we - // can easily remove duplicates with calls to equals() after. - // NOTE: We don't call getCurve() / getParameter() here, since - // this code is used internally in boolean operations where all - // this information remains valid during processing. - return path1 === path2 - ? curve1 === curve2 - // TODO: Compare points instead of parameter like in - // equals? Or time there too? Why was it changed? - ? Math.abs((diff = l1._parameter - l2._parameter)) - < /*#=*/Numerical.CURVETIME_EPSILON - ? _ignoreOther - ? 0 - : compare(l1._intersection, - l2._intersection, true) - : diff - : curve1.getIndex() - curve2.getIndex() - // Sort by path id to group all locs on the same path. - : path1._id - path2._id; + path2 = curve2._path; + diff = path1 === path2 + ? curve1.getIndex() + loc._parameter + - curve2.getIndex() - loc2._parameter + // Sort by path id to group all locs on same path. + : path1._id - path2._id; + // Only compare location with equals() if diff is small enough + // NOTE: equals() takes the intersection location into account, + // while the above calculation of diff doesn't! + if (merge && Math.abs(diff) < /*#=*/Numerical.CURVETIME_EPSILON + && loc.equals(loc2)) { + // Carry over overlap setting! + if (loc._overlap) { + loc2._overlap = loc2._intersection._overlap = true; + } + // We're done, don't insert + return; + } + if (diff < 0) { + r = m - 1; + } else { + l = m + 1; + } } - locations.sort(compare); + locations.splice(l, 0, loc); + }, + + expand: function(locations) { + // Create a copy since add() keeps modifying the array and inserting + // at sorted indices. + var copy = locations.slice(); + for (var i = 0, l = locations.length; i < l; i++) { + this.add(copy, locations[i]._intersection, false); + } + return copy; } } }, Base.each(Curve.evaluateMethods, function(name) { diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index a07592ea..44e390e3 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -110,7 +110,7 @@ PathItem.inject(new function() { // console.timeEnd('self-intersection'); } // console.timeEnd('intersection'); - splitPath(Curve._filterIntersections(locations, true)); + splitPath(CurveLocation.expand(locations)); var segments = [], // Aggregate of all curves in both operands, monotonic in y diff --git a/src/path/PathItem.js b/src/path/PathItem.js index 1f931dd0..00ad1b92 100644 --- a/src/path/PathItem.js +++ b/src/path/PathItem.js @@ -63,8 +63,7 @@ var PathItem = Item.extend(/** @lends PathItem# */{ // intersections. // NOTE: The hidden argument _matrix is used internally to override the // passed path's transformation matrix. - return Curve._filterIntersections(this._getIntersections( - this !== path ? path : null, _matrix, [])); + return this._getIntersections(this !== path ? path : null, _matrix, []); }, _getIntersections: function(path, matrix, locations, returnFirst) {