diff --git a/src/path/Curve.js b/src/path/Curve.js index 5cabea60..258ca82f 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -2103,9 +2103,9 @@ new function() { // Scope for bezier intersection using fat-line clipping } function getIntersections(curves1, curves2, include, matrix1, matrix2, - _returnFirst) { - var epsilon = Numerical.GEOMETRIC_EPSILON; - var self = !curves2; + _returnFirst) { + var epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON, + self = !curves2; if (self) curves2 = curves1; var length1 = curves1.length, @@ -2115,23 +2115,21 @@ new function() { // Scope for bezier intersection using fat-line clipping locations = []; for (var i = 0; i < length1; i++) { - var v = curves1[i].getValues(matrix1); - values1[i] = v; + values1[i] = curves1[i].getValues(matrix1); } if (!self) { for (var i = 0; i < length2; i++) { - var v = curves2[i].getValues(matrix2); - values2[i] = v; + values2[i] = curves2[i].getValues(matrix2); } } var boundsCollisions = CollisionDetection.findCurveBoundsCollisions( - values1, self ? null : values2, epsilon); + values1, self ? null : values2, epsilon); for (var index1 = 0; index1 < length1; index1++) { var curve1 = curves1[index1], v1 = values1[index1]; if (self) { // First check for self-intersections within the same curve. - getSelfIntersection(v1, curve1, locations, include); + getSelfIntersection(v1, curve1, locations, include); } // Check for intersections with potentially intersecting curves. var collisions1 = boundsCollisions[index1]; @@ -2146,12 +2144,11 @@ new function() { // Scope for bezier intersection using fat-line clipping var curve2 = curves2[index2], v2 = values2[index2]; getCurveIntersections( - v1, v2, curve1, curve2, locations, include - ); + v1, v2, curve1, curve2, locations, include); } } } - } + } return locations; } diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index a45c44e1..cfef7004 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -23,9 +23,8 @@ * - Boolean operations on self-intersecting Paths items * * @author Harikrishnan Gopalakrishnan - * @author Jan Boesenberg + * @author Jan Boesenberg * @author Juerg Lehni - * https://hkrish.com/playground/paperjs/booleanStudy.html */ PathItem.inject(new function() { var min = Math.min, @@ -179,22 +178,22 @@ PathItem.inject(new function() { horCurvesMap[pathId][curve.getIndex()] = collidingCurves; } - var vertCurveCollisions = + var verCurveCollisions = CollisionDetection.findCurveBoundsCollisions( curvesValues, curvesValues, 0, true, true); - var vertCurvesMap = {}; + var verCurvesMap = {}; for (var i = 0; i < curves.length; i++) { var curve = curves[i], collidingCurves = [], - collisionIndices = vertCurveCollisions[i]; + collisionIndices = verCurveCollisions[i]; if (collisionIndices) { for (var j = 0; j < collisionIndices.length; j++) { collidingCurves.push(curves[collisionIndices[j]]); } } var pathId = curve.getPath().getId(); - vertCurvesMap[pathId] = vertCurvesMap[pathId] || {}; - vertCurvesMap[pathId][curve.getIndex()] = collidingCurves; + verCurvesMap[pathId] = verCurvesMap[pathId] || {}; + verCurvesMap[pathId][curve.getIndex()] = collidingCurves; } // Propagate the winding contribution. Winding contribution of @@ -203,14 +202,14 @@ PathItem.inject(new function() { // in all crossings: for (var i = 0, l = crossings.length; i < l; i++) { propagateWinding(crossings[i]._segment, _path1, _path2, - horCurvesMap, vertCurvesMap, operator); + horCurvesMap, verCurvesMap, operator); } for (var i = 0, l = segments.length; i < l; i++) { var segment = segments[i], inter = segment._intersection; if (!segment._winding) { propagateWinding(segment, _path1, _path2, - horCurvesMap, vertCurvesMap, operator); + horCurvesMap, verCurvesMap, operator); } // See if all encountered segments in a path are overlaps. if (!(inter && inter._overlap)) @@ -349,28 +348,28 @@ PathItem.inject(new function() { // Now determine the winding for each path, from large to small. for (var i = 0; i < length; i++) { var path1 = sorted[i], - indicesI = collisions[i], entry1 = lookup[path1._id], - containerWinding = 0; - if (indicesI) { - var point = null; // interior point, only get it if required - for (var j = indicesI.length - 1; j >= 0; j--) { - if (indicesI[j] < i) { + containerWinding = 0, + indices = collisions[i]; + if (indices) { + var point = null; // interior point, only get it if required. + for (var j = indices.length - 1; j >= 0; j--) { + if (indices[j] < i) { point = point || path1.getInteriorPoint(); - var path2 = sorted[indicesI[j]]; + var path2 = sorted[indices[j]]; // As we run through the paths from largest to // smallest, for any current path, all potentially // containing paths have already been processed and // their orientation fixed. To achieve correct // orientation of contained paths based on winding, - // we have to find one containing path with - // different "insideness" and set opposite orientation. + // find one containing path with different + // "insideness" and set opposite orientation. if (path2.contains(point)) { var entry2 = lookup[path2._id]; containerWinding = entry2.winding; entry1.winding += containerWinding; - entry1.container = entry2.exclude ? - entry2.container : path2; + entry1.container = entry2.exclude + ? entry2.container : path2; break; } } @@ -388,8 +387,8 @@ PathItem.inject(new function() { // If the containing path is not excluded, we're done // searching for the orientation defining path. var container = entry1.container; - path1.setClockwise(container ? !container.isClockwise() - : clockwise); + path1.setClockwise( + container ? !container.isClockwise() : clockwise); } } } @@ -793,8 +792,8 @@ PathItem.inject(new function() { }; } - function propagateWinding(segment, path1, path2, horCurveCollisionsMap, - vertCurveCollisionsMap, operator) { + function propagateWinding(segment, path1, path2, horCurvesMap, verCurvesMap, + operator) { // Here we try to determine the most likely winding number contribution // for the curve-chain starting with this segment. Once we have enough // confidence in the winding contribution, we can propagate it until the @@ -845,9 +844,8 @@ PathItem.inject(new function() { var wind = null; if (operator.subtract && path2) { // Calculate path winding at point depending on operand. - var pathWinding = operand === path1 - ? path2._getWinding(pt, dir, true) - : path1._getWinding(pt, dir, true); + var otherPath = operand === path1 ? path2 : path1, + pathWinding = otherPath._getWinding(pt, dir, true); // Check if curve should be omitted. if (operand === path1 && pathWinding.winding || operand === path2 && !pathWinding.winding) { @@ -861,12 +859,13 @@ PathItem.inject(new function() { } } } - var pathId = path.getId(); - var curveIndex = curve.getIndex(); - var hCollisions = horCurveCollisionsMap[pathId][curveIndex]; - var vCollisions = vertCurveCollisionsMap[pathId][curveIndex]; - wind = wind || - getWinding(pt, hCollisions, vCollisions, dir, true); + if (!wind) { + var pathId = path.getId(), + curveIndex = curve.getIndex(), + curvesH = horCurvesMap[pathId][curveIndex], + curvesV = verCurvesMap[pathId][curveIndex]; + wind = getWinding(pt, curvesH, curvesV, dir, true); + } if (wind.quality > winding.quality) winding = wind; break; diff --git a/src/util/CollisionDetection.js b/src/util/CollisionDetection.js index 24160a30..28cdb38a 100644 --- a/src/util/CollisionDetection.js +++ b/src/util/CollisionDetection.js @@ -14,54 +14,51 @@ * @name CollisionDetection * @namespace * @private + * @author Jan Boesenberg */ -var CollisionDetection = /** @lends CollisionDetection */{ - +var CollisionDetection = /** @lends CollisionDetection */{ /** * Finds collisions between axis aligned bounding boxes of items. * * This function takes the bounds of all items in the items1 and items2 * arrays and calls findBoundsCollisions(). * - * @param {Array} itemsA Array of curve values for which collisions should - * be found. - * @param {Array} [itemsA] Array of curve values that the first array should - * be compared with. If not provided, collisions between items within - * the first arrray will be returned. + * @param {Array} items1 Array of items for which collisions should be + * found. + * @param {Array} [items2] Array of items that the first array should be + * compared with. If not provided, collisions between items within + * the first array will be returned. * @param {Number} [tolerance] If provided, the tolerance will be added to * all sides of each bounds when checking for collisions. - * @param {Boolean} [sweepVertical] If set to true, the sweep is done - * along the y axis. - * @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision - * checks will be done on the secondary axis. - * @returns {Array} Array containing for the bounds at thes same index in - * itemsA an array of the indexes of colliding bounds in itemsB - * - * @author Jan Boesenberg + * @param {Boolean} [sweepVertical] If true, the sweep is performed along + * the y-axis. + * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks + * will be done on the secondary axis. + * @returns {Array} Array containing for the bounds at the same index in + * items1 an array of the indexes of colliding bounds in items2 */ - findItemBoundsCollisions: function(itemsA, itemsB, tolerance, - sweepVertical, onlySweepAxisCollisions) { - var boundsArr1 = new Array(itemsA.length), - boundsArr2; - for (var i = 0; i < boundsArr1.length; i++) { - var bounds = itemsA[i].bounds; - boundsArr1[i] = [bounds.left, bounds.top, bounds.right, - bounds.bottom]; + findItemBoundsCollisions: function(items1, items2, tolerance, + sweepVertical, onlySweepAxisCollisions) { + var bounds1 = new Array(items1.length), + bounds2; + for (var i = 0; i < items1.length; i++) { + var bounds = items1[i].bounds; + bounds1[i] = [bounds.left, bounds.top, bounds.right, bounds.bottom]; } - if (itemsB) { - if (itemsB === itemsA) { - boundsArr2 = boundsArr1; + if (items2) { + if (items2 === items1) { + bounds2 = bounds1; } else { - boundsArr2 = new Array(itemsB.length); - for (var i = 0; i < boundsArr2.length; i++) { - var bounds = itemsB[i].bounds; - boundsArr2[i] = [bounds.left, bounds.top, bounds.right, + bounds2 = new Array(items2.length); + for (var i = 0; i < items2.length; i++) { + var bounds = items2[i].bounds; + bounds2[i] = [bounds.left, bounds.top, bounds.right, bounds.bottom]; } } } - return this.findBoundsCollisions(boundsArr1, boundsArr2, tolerance || 0, - sweepVertical, onlySweepAxisCollisions); + return this.findBoundsCollisions(bounds1, bounds2, tolerance || 0, + sweepVertical, onlySweepAxisCollisions); }, /** @@ -80,25 +77,23 @@ var CollisionDetection = /** @lends CollisionDetection */{ * curve bounds within the first arrray will be returned. * @param {Number} [tolerance] If provided, the tolerance will be added to * all sides of each bounds when checking for collisions. - * @param {Boolean} [sweepVertical] If set to true, the sweep is done - * along the y axis. - * @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision - * checks will be done on the secondary axis. - * @returns {Array} Array containing for the bounds at thes same index in - * curveValuesA an array of the indexes of colliding bounds in - * curveValuesB - * - * @author Jan Boesenberg + * @param {Boolean} [sweepVertical] If true, the sweep is performed along + * the y-axis. + * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks + * will be done on the secondary axis. + * @returns {Array} Array containing for the bounds at the same index in + * curveValues1 an array of the indexes of colliding bounds in + * curveValues2 */ findCurveBoundsCollisions: function(curvesValues1, curvesValues2, - tolerance, sweepVertical, onlySweepAxisCollisions) { + tolerance, sweepVertical, onlySweepAxisCollisions) { var min = Math.min, max = Math.max, - boundsArr1 = new Array(curvesValues1.length), - boundsArr2; - for (var i = 0; i < boundsArr1.length; i++) { + bounds1 = new Array(curvesValues1.length), + bounds2; + for (var i = 0; i < bounds1.length; i++) { var v1 = curvesValues1[i]; - boundsArr1[i] = [ + bounds1[i] = [ min(v1[0], v1[2], v1[4], v1[6]), min(v1[1], v1[3], v1[5], v1[7]), max(v1[0], v1[2], v1[4], v1[6]), @@ -107,12 +102,12 @@ var CollisionDetection = /** @lends CollisionDetection */{ } if (curvesValues2) { if (curvesValues2 === curvesValues1) { - boundsArr2 = boundsArr1; + bounds2 = bounds1; } else { - boundsArr2 = new Array(curvesValues2.length); - for (var i = 0; i < boundsArr2.length; i++) { + bounds2 = new Array(curvesValues2.length); + for (var i = 0; i < bounds2.length; i++) { var v2 = curvesValues2[i]; - boundsArr2[i] = [ + bounds2[i] = [ min(v2[0], v2[2], v2[4], v2[6]), min(v2[1], v2[3], v2[5], v2[7]), max(v2[0], v2[2], v2[4], v2[6]), @@ -121,8 +116,8 @@ var CollisionDetection = /** @lends CollisionDetection */{ } } } - return this.findBoundsCollisions(boundsArr1, boundsArr2, - tolerance || 0, sweepVertical, onlySweepAxisCollisions); + return this.findBoundsCollisions(bounds1, bounds2, + tolerance || 0, sweepVertical, onlySweepAxisCollisions); }, /** @@ -135,9 +130,9 @@ var CollisionDetection = /** @lends CollisionDetection */{ * Each entry in the bounds arrays must be an array of length 4 with * x0, y0, x1, and y1 as the array elements. * - * The returned array has the same length as boundsArr1. Each entry + * The returned array has the same length as bounds1. Each entry * contains an array with all indices of overlapping bounds of - * boundsArr2 (or boundsArr1 if boundsArr2 is not provided) sorted + * bounds2 (or bounds1 if bounds2 is not provided) sorted * in ascending order. * * If the second bounds array parameter is null, collisions between bounds @@ -145,21 +140,19 @@ var CollisionDetection = /** @lends CollisionDetection */{ * returned for each bounds will not contain the bounds' own index. * * - * @param {Array} boundsArr1 Array of bounds objects for which collisions + * @param {Array} boundsA Array of bounds objects for which collisions * should be found. - * @param {Array} [boundsArr2] Array of bounds that the first array should + * @param {Array} [boundsB] Array of bounds that the first array should * be compared with. If not provided, collisions between bounds within * the first arrray will be returned. * @param {Number} [tolerance] If provided, the tolerance will be added to * all sides of each bounds when checking for collisions. - * @param {Boolean} [sweepVertical] If set to true, the sweep is done - * along the y axis. - * @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision - * checks will be done on the secondary axis. - * @returns {Array} Array containing for the bounds at thes same index in - * boundsA an array of the indexes of colliding bounds in boundsB - * - * @author Jan Boesenberg + * @param {Boolean} [sweepVertical] If true, the sweep is performed along + * the y-axis. + * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks + * will be done on the secondary axis. + * @returns {Array} Array containing for the bounds at the same index in + * boundsA an array of the indexes of colliding bounds in boundsB */ findBoundsCollisions: function(boundsA, boundsB, tolerance, sweepVertical, onlySweepAxisCollisions) { @@ -176,7 +169,7 @@ var CollisionDetection = /** @lends CollisionDetection */{ lo = 0; hi = indices.length; while (lo < hi) { - var mid = (hi + lo) >>> 1; // same as Math.floor((hi+lo)/2) + var mid = (hi + lo) >>> 1; // Same as Math.floor((hi + lo) / 2) if (allBounds[indices[mid]][coordinate] < coordinateValue) { lo = mid + 1; } else { @@ -209,8 +202,9 @@ var CollisionDetection = /** @lends CollisionDetection */{ for (var i = 0; i < countAll; i++) { var currentIndex = allIndicesByP0[i], currentBounds = allBounds[currentIndex], - currentOriginalIndex = self ? currentIndex - : currentIndex - countA, // index in boundsA or boundsB array + currentOriginalIndex = self // index in boundsA or boundsB + ? currentIndex + : currentIndex - countA, isCurrentA = currentIndex < countA, isCurrentB = self || currentIndex >= countA, currentCollisions = isCurrentA ? [] : null; @@ -273,13 +267,13 @@ var CollisionDetection = /** @lends CollisionDetection */{ if (activeIndicesByP1.length) { var currentP1 = currentBounds[coordP1], insertIndex = - binarySearch(activeIndicesByP1, currentP1, coordP1) + 1; + binarySearch(activeIndicesByP1, currentP1, coordP1) + 1; activeIndicesByP1.splice(insertIndex, 0, currentIndex); } else { activeIndicesByP1.push(currentIndex); } } - // Sort collision indices in ascending order + // Sort collision indices in ascending order. for (var i = 0; i < allCollisions.length; i++) { if (allCollisions[i]) { allCollisions[i].sort(function(i1, i2) {