Some more optimizations for #1740

This commit is contained in:
Jürg Lehni 2019-12-14 20:29:35 +01:00
parent c82e5d41f7
commit a9ebe475e0
3 changed files with 58 additions and 72 deletions

View file

@ -2123,7 +2123,7 @@ new function() { // Scope for bezier intersection using fat-line clipping
} }
} }
var boundsCollisions = CollisionDetection.findCurveBoundsCollisions( var boundsCollisions = CollisionDetection.findCurveBoundsCollisions(
values1, self ? null : values2, epsilon); values1, 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];

View file

@ -139,7 +139,7 @@ PathItem.inject(new function() {
curves = [], curves = [],
paths; paths;
function collect(paths) { function collectPaths(paths) {
for (var i = 0, l = paths.length; i < l; i++) { for (var i = 0, l = paths.length; i < l; i++) {
var path = paths[i]; var path = paths[i];
Base.push(segments, path._segments); 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) { if (crossings.length) {
// Collect all segments and curves of both involved operands. // Collect all segments and curves of both involved operands.
collect(paths1); collectPaths(paths1);
if (paths2) if (paths2)
collect(paths2); collectPaths(paths2);
var curvesValues = new Array(curves.length); var curvesValues = new Array(curves.length);
for (var i = 0, l = curves.length; i < l; i++) { for (var i = 0, l = curves.length; i < l; i++) {
curvesValues[i] = curves[i].getValues(); curvesValues[i] = curves[i].getValues();
} }
var horCurveCollisions = var curveCollisions = CollisionDetection.findCurveBoundsCollisions(
CollisionDetection.findCurveBoundsCollisions( curvesValues, curvesValues, 0, true);
curvesValues, curvesValues, 0, false, true); var curveCollisionsMap = {};
var horCurvesMap = {};
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 = [], id = curve._path._id,
collisionIndices = horCurveCollisions[i]; map = curveCollisionsMap[id] = curveCollisionsMap[id] || {};
if (collisionIndices) { map[curve.getIndex()] = {
for (var j = 0; j < collisionIndices.length; j++) { hor: getCurves(curveCollisions[i].hor),
collidingCurves.push(curves[collisionIndices[j]]); ver: getCurves(curveCollisions[i].ver)
} };
}
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;
} }
// Propagate the winding contribution. Winding contribution of // Propagate the winding contribution. Winding contribution of
@ -202,14 +187,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, verCurvesMap, operator); curveCollisionsMap, 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, verCurvesMap, operator); curveCollisionsMap, 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))
@ -533,16 +518,9 @@ PathItem.inject(new function() {
* *
* @param {Point} point the location for which to determine the winding * @param {Point} point the location for which to determine the winding
* contribution * 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 * to check, as returned by {@link Path#curves} or
* {@link CompoundPath#curves}. This only has to contain those curves * {@link CompoundPath#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.
* @param {Boolean} [dir=false] the direction in which to determine the * @param {Boolean} [dir=false] the direction in which to determine the
* winding contribution, `false`: in x-direction, `true`: in y-direction * winding contribution, `false`: in x-direction, `true`: in y-direction
* @param {Boolean} [closed=false] determines how areas should be closed * @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 * well as an indication whether the point was situated on the contour
* @private * @private
*/ */
function getWinding(point, curvesH, curvesV, dir, closed, dontFlip) { function getWinding(point, curves, dir, closed, dontFlip) {
var curves = !dir ? curvesV : curvesH; // `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 // Determine the index of the abscissa and ordinate values in the curve
// values arrays, based on the direction: // values arrays, based on the direction:
var ia = dir ? 1 : 0, // the abscissa index 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. // again with flipped direction and return that result instead.
return !dontFlip && a > paL && a < paR return !dontFlip && a > paL && a < paR
&& Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0 && Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0
&& getWinding(point, curvesH, curvesV, !dir, closed, true); && getWinding(point, curves, !dir, closed, true);
} }
function handleCurve(v) { function handleCurve(v) {
@ -702,12 +686,12 @@ PathItem.inject(new function() {
} }
} }
for (var i = 0, l = curves.length; i < l; i++) { for (var i = 0, l = curvesList.length; i < l; i++) {
var curve = curves[i], var curve = curvesList[i],
path = curve._path, path = curve._path,
v = curve.getValues(), v = curve.getValues(),
res; 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 // We're on a new (sub-)path, so we need to determine values of
// the last non-horizontal curve on this path. // the last non-horizontal curve on this path.
vPrev = null; vPrev = null;
@ -751,7 +735,7 @@ PathItem.inject(new function() {
if (res = handleCurve(v)) if (res = handleCurve(v))
return res; 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 // We're at the last curve of the current (sub-)path. If a
// closing curve was calculated at the beginning of it, handle // closing curve was calculated at the beginning of it, handle
// it now to treat the path as closed: // 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) { 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
@ -859,13 +843,9 @@ PathItem.inject(new function() {
} }
} }
} }
if (!wind) { wind = wind || getWinding(
var pathId = path.getId(), pt, curveCollisionsMap[path._id][curve.getIndex()],
curveIndex = curve.getIndex(), dir, true);
curvesH = horCurvesMap[pathId][curveIndex],
curvesV = verCurvesMap[pathId][curveIndex];
wind = getWinding(pt, curvesH, curvesV, dir, true);
}
if (wind.quality > winding.quality) if (wind.quality > winding.quality)
winding = wind; winding = wind;
break; break;
@ -1141,8 +1121,7 @@ PathItem.inject(new function() {
* @return {Number} the winding number * @return {Number} the winding number
*/ */
_getWinding: function(point, dir, closed) { _getWinding: function(point, dir, closed) {
var curves = this.getCurves(); return getWinding(point, this.getCurves(), dir, closed);
return getWinding(point, curves, curves, dir, closed);
}, },
/** /**

View file

@ -66,15 +66,12 @@ var CollisionDetection = /** @lends CollisionDetection */{
* bounds within the first arrray will be returned. * 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 true, the sweep is performed along * @param {Boolean} [bothAxis] If true, the sweep is performed along both
* the y-axis. * axis, and the results include collisions for both: `{ hor, ver }`.
* @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 * @returns {Array} Array containing for the bounds at the same index in
* curves1 an array of the indexes of colliding bounds in curves2 * curves1 an array of the indexes of colliding bounds in curves2
*/ */
findCurveBoundsCollisions: function(curves1, curves2, findCurveBoundsCollisions: function(curves1, curves2, tolerance, bothAxis) {
tolerance, sweepVertical, onlySweepAxisCollisions) {
function getBounds(curves) { function getBounds(curves) {
var min = Math.min, var min = Math.min,
max = Math.max, max = Math.max,
@ -95,8 +92,18 @@ var CollisionDetection = /** @lends CollisionDetection */{
bounds2 = !curves2 || curves2 === curves1 bounds2 = !curves2 || curves2 === curves1
? bounds1 ? bounds1
: getBounds(curves2); : getBounds(curves2);
return this.findBoundsCollisions(bounds1, bounds2, if (bothAxis) {
tolerance || 0, sweepVertical, onlySweepAxisCollisions); 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);
}, },
/** /**