From a9ebe475e04435bb357cb26ce32f3a2f4b88986a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Sat, 14 Dec 2019 20:29:35 +0100 Subject: [PATCH] Some more optimizations for #1740 --- src/path/Curve.js | 2 +- src/path/PathItem.Boolean.js | 105 +++++++++++++-------------------- src/util/CollisionDetection.js | 23 +++++--- 3 files changed, 58 insertions(+), 72 deletions(-) diff --git a/src/path/Curve.js b/src/path/Curve.js index 258ca82f..c2f6c073 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -2123,7 +2123,7 @@ new function() { // Scope for bezier intersection using fat-line clipping } } var boundsCollisions = CollisionDetection.findCurveBoundsCollisions( - values1, self ? null : values2, epsilon); + values1, values2, epsilon); for (var index1 = 0; index1 < length1; index1++) { var curve1 = curves1[index1], v1 = values1[index1]; diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index cfef7004..ec64d05e 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -139,7 +139,7 @@ PathItem.inject(new function() { curves = [], paths; - function collect(paths) { + function collectPaths(paths) { for (var i = 0, l = paths.length; i < l; i++) { var path = paths[i]; Base.push(segments, path._segments); @@ -150,50 +150,35 @@ PathItem.inject(new function() { } } + function getCurves(indices) { + var list = []; + for (var i = 0, l = indices && indices.length; i < l; i++) { + list.push(curves[indices[i]]); + } + return list; + } + if (crossings.length) { // Collect all segments and curves of both involved operands. - collect(paths1); + collectPaths(paths1); if (paths2) - collect(paths2); + collectPaths(paths2); var curvesValues = new Array(curves.length); for (var i = 0, l = curves.length; i < l; i++) { curvesValues[i] = curves[i].getValues(); } - var horCurveCollisions = - CollisionDetection.findCurveBoundsCollisions( - curvesValues, curvesValues, 0, false, true); - var horCurvesMap = {}; + var curveCollisions = CollisionDetection.findCurveBoundsCollisions( + curvesValues, curvesValues, 0, true); + var curveCollisionsMap = {}; for (var i = 0; i < curves.length; i++) { var curve = curves[i], - collidingCurves = [], - collisionIndices = horCurveCollisions[i]; - if (collisionIndices) { - for (var j = 0; j < collisionIndices.length; j++) { - collidingCurves.push(curves[collisionIndices[j]]); - } - } - var pathId = curve.getPath().getId(); - horCurvesMap[pathId] = horCurvesMap[pathId] || {}; - horCurvesMap[pathId][curve.getIndex()] = collidingCurves; - } - - var verCurveCollisions = - CollisionDetection.findCurveBoundsCollisions( - curvesValues, curvesValues, 0, true, true); - var verCurvesMap = {}; - for (var i = 0; i < curves.length; i++) { - var curve = curves[i], - collidingCurves = [], - collisionIndices = verCurveCollisions[i]; - if (collisionIndices) { - for (var j = 0; j < collisionIndices.length; j++) { - collidingCurves.push(curves[collisionIndices[j]]); - } - } - var pathId = curve.getPath().getId(); - verCurvesMap[pathId] = verCurvesMap[pathId] || {}; - verCurvesMap[pathId][curve.getIndex()] = collidingCurves; + id = curve._path._id, + map = curveCollisionsMap[id] = curveCollisionsMap[id] || {}; + map[curve.getIndex()] = { + hor: getCurves(curveCollisions[i].hor), + ver: getCurves(curveCollisions[i].ver) + }; } // Propagate the winding contribution. Winding contribution of @@ -202,14 +187,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, verCurvesMap, operator); + curveCollisionsMap, 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, verCurvesMap, operator); + curveCollisionsMap, operator); } // See if all encountered segments in a path are overlaps. if (!(inter && inter._overlap)) @@ -533,16 +518,9 @@ PathItem.inject(new function() { * * @param {Point} point the location for which to determine the winding * contribution - * @param {Curve[]} curvesH The curves that describe the shape against which + * @param {Curve[]} curves The curves that describe the shape against which * to check, as returned by {@link Path#curves} or - * {@link CompoundPath#curves}. This only has to contain those curves - * that can be crossed by a horizontal line through the point to be - * checked. - * @param {Curve[]} curvesV The curves that describe the shape against which - * to check, as returned by {@link Path#curves} or - * {@link CompoundPath#curves}. This only has to contain those curves - * that can be crossed by a vertical line through the point to be - * checked. + * {@link CompoundPath#curves}. * @param {Boolean} [dir=false] the direction in which to determine the * winding contribution, `false`: in x-direction, `true`: in y-direction * @param {Boolean} [closed=false] determines how areas should be closed @@ -555,8 +533,14 @@ PathItem.inject(new function() { * well as an indication whether the point was situated on the contour * @private */ - function getWinding(point, curvesH, curvesV, dir, closed, dontFlip) { - var curves = !dir ? curvesV : curvesH; + function getWinding(point, curves, dir, closed, dontFlip) { + // `curves` can either be an array of curves, or an object containing of + // the form `{ hor: [], ver: [] }` (see `curveCollisionsMap`), with each + // key / value pair holding only those curves that can be crossed by a + // horizontal / vertical line through the point to be checked. + var curvesList = Array.isArray(curves) + ? curves + : curves[dir ? 'hor' : 'ver']; // Determine the index of the abscissa and ordinate values in the curve // values arrays, based on the direction: var ia = dir ? 1 : 0, // the abscissa index @@ -671,7 +655,7 @@ PathItem.inject(new function() { // again with flipped direction and return that result instead. return !dontFlip && a > paL && a < paR && Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0 - && getWinding(point, curvesH, curvesV, !dir, closed, true); + && getWinding(point, curves, !dir, closed, true); } function handleCurve(v) { @@ -702,12 +686,12 @@ PathItem.inject(new function() { } } - for (var i = 0, l = curves.length; i < l; i++) { - var curve = curves[i], + for (var i = 0, l = curvesList.length; i < l; i++) { + var curve = curvesList[i], path = curve._path, v = curve.getValues(), res; - if (!i || curves[i - 1]._path !== path) { + if (!i || curvesList[i - 1]._path !== path) { // We're on a new (sub-)path, so we need to determine values of // the last non-horizontal curve on this path. vPrev = null; @@ -751,7 +735,7 @@ PathItem.inject(new function() { if (res = handleCurve(v)) return res; - if (i + 1 === l || curves[i + 1]._path !== path) { + if (i + 1 === l || curvesList[i + 1]._path !== path) { // We're at the last curve of the current (sub-)path. If a // closing curve was calculated at the beginning of it, handle // it now to treat the path as closed: @@ -792,7 +776,7 @@ PathItem.inject(new function() { }; } - function propagateWinding(segment, path1, path2, horCurvesMap, verCurvesMap, + function propagateWinding(segment, path1, path2, curveCollisionsMap, operator) { // Here we try to determine the most likely winding number contribution // for the curve-chain starting with this segment. Once we have enough @@ -859,13 +843,9 @@ PathItem.inject(new function() { } } } - if (!wind) { - var pathId = path.getId(), - curveIndex = curve.getIndex(), - curvesH = horCurvesMap[pathId][curveIndex], - curvesV = verCurvesMap[pathId][curveIndex]; - wind = getWinding(pt, curvesH, curvesV, dir, true); - } + wind = wind || getWinding( + pt, curveCollisionsMap[path._id][curve.getIndex()], + dir, true); if (wind.quality > winding.quality) winding = wind; break; @@ -1141,8 +1121,7 @@ PathItem.inject(new function() { * @return {Number} the winding number */ _getWinding: function(point, dir, closed) { - var curves = this.getCurves(); - return getWinding(point, curves, curves, dir, closed); + return getWinding(point, this.getCurves(), dir, closed); }, /** diff --git a/src/util/CollisionDetection.js b/src/util/CollisionDetection.js index 118af14b..442ab9d9 100644 --- a/src/util/CollisionDetection.js +++ b/src/util/CollisionDetection.js @@ -66,15 +66,12 @@ var CollisionDetection = /** @lends CollisionDetection */{ * 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 true, the sweep is performed along - * the y-axis. - * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks - * will be done on the secondary axis. + * @param {Boolean} [bothAxis] If true, the sweep is performed along both + * axis, and the results include collisions for both: `{ hor, ver }`. * @returns {Array} Array containing for the bounds at the same index in * curves1 an array of the indexes of colliding bounds in curves2 */ - findCurveBoundsCollisions: function(curves1, curves2, - tolerance, sweepVertical, onlySweepAxisCollisions) { + findCurveBoundsCollisions: function(curves1, curves2, tolerance, bothAxis) { function getBounds(curves) { var min = Math.min, max = Math.max, @@ -95,8 +92,18 @@ var CollisionDetection = /** @lends CollisionDetection */{ bounds2 = !curves2 || curves2 === curves1 ? bounds1 : getBounds(curves2); - return this.findBoundsCollisions(bounds1, bounds2, - tolerance || 0, sweepVertical, onlySweepAxisCollisions); + if (bothAxis) { + var hor = this.findBoundsCollisions( + bounds1, bounds2, tolerance || 0, false, true), + ver = this.findBoundsCollisions( + bounds1, bounds2, tolerance || 0, true, true), + list = []; + for (var i = 0, l = hor.length; i < l; i++) { + list[i] = { hor: hor[i], ver: ver[i] }; + } + return list; + } + return this.findBoundsCollisions(bounds1, bounds2, tolerance || 0); }, /**