Some code refactoring for #1740

This commit is contained in:
Jürg Lehni 2019-12-13 16:47:49 +01:00
parent a183dc0c0d
commit 46f1aaeca1
3 changed files with 104 additions and 114 deletions

View file

@ -2103,9 +2103,9 @@ new function() { // Scope for bezier intersection using fat-line clipping
} }
function getIntersections(curves1, curves2, include, matrix1, matrix2, function getIntersections(curves1, curves2, include, matrix1, matrix2,
_returnFirst) { _returnFirst) {
var epsilon = Numerical.GEOMETRIC_EPSILON; var epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON,
var self = !curves2; self = !curves2;
if (self) if (self)
curves2 = curves1; curves2 = curves1;
var length1 = curves1.length, var length1 = curves1.length,
@ -2115,17 +2115,15 @@ new function() { // Scope for bezier intersection using fat-line clipping
locations = []; locations = [];
for (var i = 0; i < length1; i++) { for (var i = 0; i < length1; i++) {
var v = curves1[i].getValues(matrix1); values1[i] = curves1[i].getValues(matrix1);
values1[i] = v;
} }
if (!self) { if (!self) {
for (var i = 0; i < length2; i++) { for (var i = 0; i < length2; i++) {
var v = curves2[i].getValues(matrix2); values2[i] = curves2[i].getValues(matrix2);
values2[i] = v;
} }
} }
var boundsCollisions = CollisionDetection.findCurveBoundsCollisions( var boundsCollisions = CollisionDetection.findCurveBoundsCollisions(
values1, self ? null : values2, epsilon); values1, self ? null : values2, epsilon);
for (var index1 = 0; index1 < length1; index1++) { for (var index1 = 0; index1 < length1; index1++) {
var curve1 = curves1[index1], var curve1 = curves1[index1],
v1 = values1[index1]; v1 = values1[index1];
@ -2146,8 +2144,7 @@ new function() { // Scope for bezier intersection using fat-line clipping
var curve2 = curves2[index2], var curve2 = curves2[index2],
v2 = values2[index2]; v2 = values2[index2];
getCurveIntersections( getCurveIntersections(
v1, v2, curve1, curve2, locations, include v1, v2, curve1, curve2, locations, include);
);
} }
} }
} }

View file

@ -23,9 +23,8 @@
* - Boolean operations on self-intersecting Paths items * - Boolean operations on self-intersecting Paths items
* *
* @author Harikrishnan Gopalakrishnan <hari.exeption@gmail.com> * @author Harikrishnan Gopalakrishnan <hari.exeption@gmail.com>
* @author Jan Boesenberg <development@iconexperience.com> * @author Jan Boesenberg <jan.boesenberg@gmail.com>
* @author Juerg Lehni <juerg@scratchdisk.com> * @author Juerg Lehni <juerg@scratchdisk.com>
* https://hkrish.com/playground/paperjs/booleanStudy.html
*/ */
PathItem.inject(new function() { PathItem.inject(new function() {
var min = Math.min, var min = Math.min,
@ -179,22 +178,22 @@ PathItem.inject(new function() {
horCurvesMap[pathId][curve.getIndex()] = collidingCurves; horCurvesMap[pathId][curve.getIndex()] = collidingCurves;
} }
var vertCurveCollisions = var verCurveCollisions =
CollisionDetection.findCurveBoundsCollisions( CollisionDetection.findCurveBoundsCollisions(
curvesValues, curvesValues, 0, true, true); curvesValues, curvesValues, 0, true, true);
var vertCurvesMap = {}; var verCurvesMap = {};
for (var i = 0; i < curves.length; i++) { for (var i = 0; i < curves.length; i++) {
var curve = curves[i], var curve = curves[i],
collidingCurves = [], collidingCurves = [],
collisionIndices = vertCurveCollisions[i]; collisionIndices = verCurveCollisions[i];
if (collisionIndices) { if (collisionIndices) {
for (var j = 0; j < collisionIndices.length; j++) { for (var j = 0; j < collisionIndices.length; j++) {
collidingCurves.push(curves[collisionIndices[j]]); collidingCurves.push(curves[collisionIndices[j]]);
} }
} }
var pathId = curve.getPath().getId(); var pathId = curve.getPath().getId();
vertCurvesMap[pathId] = vertCurvesMap[pathId] || {}; verCurvesMap[pathId] = verCurvesMap[pathId] || {};
vertCurvesMap[pathId][curve.getIndex()] = collidingCurves; verCurvesMap[pathId][curve.getIndex()] = collidingCurves;
} }
// Propagate the winding contribution. Winding contribution of // Propagate the winding contribution. Winding contribution of
@ -203,14 +202,14 @@ PathItem.inject(new function() {
// in all crossings: // in all crossings:
for (var i = 0, l = crossings.length; i < l; i++) { for (var i = 0, l = crossings.length; i < l; i++) {
propagateWinding(crossings[i]._segment, _path1, _path2, propagateWinding(crossings[i]._segment, _path1, _path2,
horCurvesMap, vertCurvesMap, operator); horCurvesMap, verCurvesMap, operator);
} }
for (var i = 0, l = segments.length; i < l; i++) { for (var i = 0, l = segments.length; i < l; i++) {
var segment = segments[i], var segment = segments[i],
inter = segment._intersection; inter = segment._intersection;
if (!segment._winding) { if (!segment._winding) {
propagateWinding(segment, _path1, _path2, propagateWinding(segment, _path1, _path2,
horCurvesMap, vertCurvesMap, operator); horCurvesMap, verCurvesMap, operator);
} }
// See if all encountered segments in a path are overlaps. // See if all encountered segments in a path are overlaps.
if (!(inter && inter._overlap)) if (!(inter && inter._overlap))
@ -349,28 +348,28 @@ PathItem.inject(new function() {
// Now determine the winding for each path, from large to small. // Now determine the winding for each path, from large to small.
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
var path1 = sorted[i], var path1 = sorted[i],
indicesI = collisions[i],
entry1 = lookup[path1._id], entry1 = lookup[path1._id],
containerWinding = 0; containerWinding = 0,
if (indicesI) { indices = collisions[i];
var point = null; // interior point, only get it if required if (indices) {
for (var j = indicesI.length - 1; j >= 0; j--) { var point = null; // interior point, only get it if required.
if (indicesI[j] < i) { for (var j = indices.length - 1; j >= 0; j--) {
if (indices[j] < i) {
point = point || path1.getInteriorPoint(); point = point || path1.getInteriorPoint();
var path2 = sorted[indicesI[j]]; var path2 = sorted[indices[j]];
// As we run through the paths from largest to // As we run through the paths from largest to
// smallest, for any current path, all potentially // smallest, for any current path, all potentially
// containing paths have already been processed and // containing paths have already been processed and
// their orientation fixed. To achieve correct // their orientation fixed. To achieve correct
// orientation of contained paths based on winding, // orientation of contained paths based on winding,
// we have to find one containing path with // find one containing path with different
// different "insideness" and set opposite orientation. // "insideness" and set opposite orientation.
if (path2.contains(point)) { if (path2.contains(point)) {
var entry2 = lookup[path2._id]; var entry2 = lookup[path2._id];
containerWinding = entry2.winding; containerWinding = entry2.winding;
entry1.winding += containerWinding; entry1.winding += containerWinding;
entry1.container = entry2.exclude ? entry1.container = entry2.exclude
entry2.container : path2; ? entry2.container : path2;
break; break;
} }
} }
@ -388,8 +387,8 @@ PathItem.inject(new function() {
// If the containing path is not excluded, we're done // If the containing path is not excluded, we're done
// searching for the orientation defining path. // searching for the orientation defining path.
var container = entry1.container; var container = entry1.container;
path1.setClockwise(container ? !container.isClockwise() path1.setClockwise(
: clockwise); container ? !container.isClockwise() : clockwise);
} }
} }
} }
@ -793,8 +792,8 @@ PathItem.inject(new function() {
}; };
} }
function propagateWinding(segment, path1, path2, horCurveCollisionsMap, function propagateWinding(segment, path1, path2, horCurvesMap, verCurvesMap,
vertCurveCollisionsMap, operator) { operator) {
// Here we try to determine the most likely winding number contribution // Here we try to determine the most likely winding number contribution
// for the curve-chain starting with this segment. Once we have enough // for the curve-chain starting with this segment. Once we have enough
// confidence in the winding contribution, we can propagate it until the // confidence in the winding contribution, we can propagate it until the
@ -845,9 +844,8 @@ PathItem.inject(new function() {
var wind = null; var wind = null;
if (operator.subtract && path2) { if (operator.subtract && path2) {
// Calculate path winding at point depending on operand. // Calculate path winding at point depending on operand.
var pathWinding = operand === path1 var otherPath = operand === path1 ? path2 : path1,
? path2._getWinding(pt, dir, true) pathWinding = otherPath._getWinding(pt, dir, true);
: path1._getWinding(pt, dir, true);
// Check if curve should be omitted. // Check if curve should be omitted.
if (operand === path1 && pathWinding.winding || if (operand === path1 && pathWinding.winding ||
operand === path2 && !pathWinding.winding) { operand === path2 && !pathWinding.winding) {
@ -861,12 +859,13 @@ PathItem.inject(new function() {
} }
} }
} }
var pathId = path.getId(); if (!wind) {
var curveIndex = curve.getIndex(); var pathId = path.getId(),
var hCollisions = horCurveCollisionsMap[pathId][curveIndex]; curveIndex = curve.getIndex(),
var vCollisions = vertCurveCollisionsMap[pathId][curveIndex]; curvesH = horCurvesMap[pathId][curveIndex],
wind = wind || curvesV = verCurvesMap[pathId][curveIndex];
getWinding(pt, hCollisions, vCollisions, dir, true); wind = getWinding(pt, curvesH, curvesV, dir, true);
}
if (wind.quality > winding.quality) if (wind.quality > winding.quality)
winding = wind; winding = wind;
break; break;

View file

@ -14,54 +14,51 @@
* @name CollisionDetection * @name CollisionDetection
* @namespace * @namespace
* @private * @private
* @author Jan Boesenberg <jan.boesenberg@gmail.com>
*/ */
var CollisionDetection = /** @lends CollisionDetection */{ var CollisionDetection = /** @lends CollisionDetection */{
/** /**
* Finds collisions between axis aligned bounding boxes of items. * Finds collisions between axis aligned bounding boxes of items.
* *
* This function takes the bounds of all items in the items1 and items2 * This function takes the bounds of all items in the items1 and items2
* arrays and calls findBoundsCollisions(). * arrays and calls findBoundsCollisions().
* *
* @param {Array} itemsA Array of curve values for which collisions should * @param {Array} items1 Array of items for which collisions should be
* be found. * found.
* @param {Array} [itemsA] Array of curve values that the first array should * @param {Array} [items2] Array of items that the first array should be
* be compared with. If not provided, collisions between items within * compared with. If not provided, collisions between items within
* the first arrray will be returned. * the first array will be returned.
* @param {Number} [tolerance] If provided, the tolerance will be added to * @param {Number} [tolerance] If provided, the tolerance will be added to
* all sides of each bounds when checking for collisions. * all sides of each bounds when checking for collisions.
* @param {Boolean} [sweepVertical] If set to true, the sweep is done * @param {Boolean} [sweepVertical] If true, the sweep is performed along
* along the y axis. * the y-axis.
* @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks
* checks will be done on the secondary axis. * will be done on the secondary axis.
* @returns {Array} Array containing for the bounds at thes same index in * @returns {Array} Array containing for the bounds at the same index in
* itemsA an array of the indexes of colliding bounds in itemsB * items1 an array of the indexes of colliding bounds in items2
*
* @author Jan Boesenberg <jan.boesenberg@gmail.com>
*/ */
findItemBoundsCollisions: function(itemsA, itemsB, tolerance, findItemBoundsCollisions: function(items1, items2, tolerance,
sweepVertical, onlySweepAxisCollisions) { sweepVertical, onlySweepAxisCollisions) {
var boundsArr1 = new Array(itemsA.length), var bounds1 = new Array(items1.length),
boundsArr2; bounds2;
for (var i = 0; i < boundsArr1.length; i++) { for (var i = 0; i < items1.length; i++) {
var bounds = itemsA[i].bounds; var bounds = items1[i].bounds;
boundsArr1[i] = [bounds.left, bounds.top, bounds.right, bounds1[i] = [bounds.left, bounds.top, bounds.right, bounds.bottom];
bounds.bottom];
} }
if (itemsB) { if (items2) {
if (itemsB === itemsA) { if (items2 === items1) {
boundsArr2 = boundsArr1; bounds2 = bounds1;
} else { } else {
boundsArr2 = new Array(itemsB.length); bounds2 = new Array(items2.length);
for (var i = 0; i < boundsArr2.length; i++) { for (var i = 0; i < items2.length; i++) {
var bounds = itemsB[i].bounds; var bounds = items2[i].bounds;
boundsArr2[i] = [bounds.left, bounds.top, bounds.right, bounds2[i] = [bounds.left, bounds.top, bounds.right,
bounds.bottom]; bounds.bottom];
} }
} }
} }
return this.findBoundsCollisions(boundsArr1, boundsArr2, tolerance || 0, return this.findBoundsCollisions(bounds1, bounds2, tolerance || 0,
sweepVertical, onlySweepAxisCollisions); sweepVertical, onlySweepAxisCollisions);
}, },
/** /**
@ -80,25 +77,23 @@ var CollisionDetection = /** @lends CollisionDetection */{
* curve bounds within the first arrray will be returned. * curve bounds within the first arrray will be returned.
* @param {Number} [tolerance] If provided, the tolerance will be added to * @param {Number} [tolerance] If provided, the tolerance will be added to
* all sides of each bounds when checking for collisions. * all sides of each bounds when checking for collisions.
* @param {Boolean} [sweepVertical] If set to true, the sweep is done * @param {Boolean} [sweepVertical] If true, the sweep is performed along
* along the y axis. * the y-axis.
* @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks
* checks will be done on the secondary axis. * will be done on the secondary axis.
* @returns {Array} Array containing for the bounds at thes same index in * @returns {Array} Array containing for the bounds at the same index in
* curveValuesA an array of the indexes of colliding bounds in * curveValues1 an array of the indexes of colliding bounds in
* curveValuesB * curveValues2
*
* @author Jan Boesenberg <jan.boesenberg@gmail.com>
*/ */
findCurveBoundsCollisions: function(curvesValues1, curvesValues2, findCurveBoundsCollisions: function(curvesValues1, curvesValues2,
tolerance, sweepVertical, onlySweepAxisCollisions) { tolerance, sweepVertical, onlySweepAxisCollisions) {
var min = Math.min, var min = Math.min,
max = Math.max, max = Math.max,
boundsArr1 = new Array(curvesValues1.length), bounds1 = new Array(curvesValues1.length),
boundsArr2; bounds2;
for (var i = 0; i < boundsArr1.length; i++) { for (var i = 0; i < bounds1.length; i++) {
var v1 = curvesValues1[i]; var v1 = curvesValues1[i];
boundsArr1[i] = [ bounds1[i] = [
min(v1[0], v1[2], v1[4], v1[6]), min(v1[0], v1[2], v1[4], v1[6]),
min(v1[1], v1[3], v1[5], v1[7]), min(v1[1], v1[3], v1[5], v1[7]),
max(v1[0], v1[2], v1[4], v1[6]), max(v1[0], v1[2], v1[4], v1[6]),
@ -107,12 +102,12 @@ var CollisionDetection = /** @lends CollisionDetection */{
} }
if (curvesValues2) { if (curvesValues2) {
if (curvesValues2 === curvesValues1) { if (curvesValues2 === curvesValues1) {
boundsArr2 = boundsArr1; bounds2 = bounds1;
} else { } else {
boundsArr2 = new Array(curvesValues2.length); bounds2 = new Array(curvesValues2.length);
for (var i = 0; i < boundsArr2.length; i++) { for (var i = 0; i < bounds2.length; i++) {
var v2 = curvesValues2[i]; var v2 = curvesValues2[i];
boundsArr2[i] = [ bounds2[i] = [
min(v2[0], v2[2], v2[4], v2[6]), min(v2[0], v2[2], v2[4], v2[6]),
min(v2[1], v2[3], v2[5], v2[7]), min(v2[1], v2[3], v2[5], v2[7]),
max(v2[0], v2[2], v2[4], v2[6]), max(v2[0], v2[2], v2[4], v2[6]),
@ -121,8 +116,8 @@ var CollisionDetection = /** @lends CollisionDetection */{
} }
} }
} }
return this.findBoundsCollisions(boundsArr1, boundsArr2, return this.findBoundsCollisions(bounds1, bounds2,
tolerance || 0, sweepVertical, onlySweepAxisCollisions); 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 * Each entry in the bounds arrays must be an array of length 4 with
* x0, y0, x1, and y1 as the array elements. * 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 * 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. * in ascending order.
* *
* If the second bounds array parameter is null, collisions between bounds * 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. * 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. * 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 * be compared with. If not provided, collisions between bounds within
* the first arrray will be returned. * the first arrray will be returned.
* @param {Number} [tolerance] If provided, the tolerance will be added to * @param {Number} [tolerance] If provided, the tolerance will be added to
* all sides of each bounds when checking for collisions. * all sides of each bounds when checking for collisions.
* @param {Boolean} [sweepVertical] If set to true, the sweep is done * @param {Boolean} [sweepVertical] If true, the sweep is performed along
* along the y axis. * the y-axis.
* @param {Boolean} [onlySweepAxisCollisionss] If set to true, no collision * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks
* checks will be done on the secondary axis. * will be done on the secondary axis.
* @returns {Array} Array containing for the bounds at thes same index in * @returns {Array} Array containing for the bounds at the same index in
* boundsA an array of the indexes of colliding bounds in boundsB * boundsA an array of the indexes of colliding bounds in boundsB
*
* @author Jan Boesenberg <jan.boesenberg@gmail.com>
*/ */
findBoundsCollisions: function(boundsA, boundsB, tolerance, findBoundsCollisions: function(boundsA, boundsB, tolerance,
sweepVertical, onlySweepAxisCollisions) { sweepVertical, onlySweepAxisCollisions) {
@ -176,7 +169,7 @@ var CollisionDetection = /** @lends CollisionDetection */{
lo = 0; lo = 0;
hi = indices.length; hi = indices.length;
while (lo < hi) { 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) { if (allBounds[indices[mid]][coordinate] < coordinateValue) {
lo = mid + 1; lo = mid + 1;
} else { } else {
@ -209,8 +202,9 @@ var CollisionDetection = /** @lends CollisionDetection */{
for (var i = 0; i < countAll; i++) { for (var i = 0; i < countAll; i++) {
var currentIndex = allIndicesByP0[i], var currentIndex = allIndicesByP0[i],
currentBounds = allBounds[currentIndex], currentBounds = allBounds[currentIndex],
currentOriginalIndex = self ? currentIndex currentOriginalIndex = self // index in boundsA or boundsB
: currentIndex - countA, // index in boundsA or boundsB array ? currentIndex
: currentIndex - countA,
isCurrentA = currentIndex < countA, isCurrentA = currentIndex < countA,
isCurrentB = self || currentIndex >= countA, isCurrentB = self || currentIndex >= countA,
currentCollisions = isCurrentA ? [] : null; currentCollisions = isCurrentA ? [] : null;
@ -273,13 +267,13 @@ var CollisionDetection = /** @lends CollisionDetection */{
if (activeIndicesByP1.length) { if (activeIndicesByP1.length) {
var currentP1 = currentBounds[coordP1], var currentP1 = currentBounds[coordP1],
insertIndex = insertIndex =
binarySearch(activeIndicesByP1, currentP1, coordP1) + 1; binarySearch(activeIndicesByP1, currentP1, coordP1) + 1;
activeIndicesByP1.splice(insertIndex, 0, currentIndex); activeIndicesByP1.splice(insertIndex, 0, currentIndex);
} else { } else {
activeIndicesByP1.push(currentIndex); activeIndicesByP1.push(currentIndex);
} }
} }
// Sort collision indices in ascending order // Sort collision indices in ascending order.
for (var i = 0; i < allCollisions.length; i++) { for (var i = 0; i < allCollisions.length; i++) {
if (allCollisions[i]) { if (allCollisions[i]) {
allCollisions[i].sort(function(i1, i2) { allCollisions[i].sort(function(i1, i2) {