More refactoring for #1740

This commit is contained in:
Jürg Lehni 2019-12-13 18:36:07 +01:00
parent 46f1aaeca1
commit d63647eb06

View file

@ -30,35 +30,24 @@ var CollisionDetection = /** @lends CollisionDetection */{
* the first array 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 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 * @returns {Array} Array containing for the bounds at the same index in
* items1 an array of the indexes of colliding bounds in items2 * items1 an array of the indexes of colliding bounds in items2
*/ */
findItemBoundsCollisions: function(items1, items2, tolerance, findItemBoundsCollisions: function(items1, items2, tolerance) {
sweepVertical, onlySweepAxisCollisions) { function getBounds(items) {
var bounds1 = new Array(items1.length), var bounds = new Array(items.length);
bounds2; for (var i = 0; i < items.length; i++) {
for (var i = 0; i < items1.length; i++) { var rect = items[i].getBounds();
var bounds = items1[i].bounds; bounds[i] = [rect.left, rect.top, rect.right, rect.bottom];
bounds1[i] = [bounds.left, bounds.top, bounds.right, bounds.bottom];
} }
if (items2) { return bounds;
if (items2 === items1) {
bounds2 = bounds1;
} else {
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];
} }
}
} var bounds1 = getBounds(items1),
return this.findBoundsCollisions(bounds1, bounds2, tolerance || 0, bounds2 = !items2 || items2 === items1
sweepVertical, onlySweepAxisCollisions); ? bounds1
: getBounds(items2);
return this.findBoundsCollisions(bounds1, bounds2, tolerance || 0);
}, },
/** /**
@ -67,14 +56,14 @@ var CollisionDetection = /** @lends CollisionDetection */{
* the actual bounds. Broad bounds guarantee to contain the full curve, * the actual bounds. Broad bounds guarantee to contain the full curve,
* but they are usually larger than the actual bounds of a curve. * but they are usually larger than the actual bounds of a curve.
* *
* This function takes the broad bounds of all curve values in the * This function takes the broad bounds of all curve values in the curves1
* curveValues1 and curveValues2 arrays and calls findBoundsCollisions(). * and curves2 arrays and calls findBoundsCollisions().
* *
* @param {Array} curvesValues1 Array of curve values for which collisions * @param {Array} curves1 Array of curve values for which collisions should
* should be found. * be found.
* @param {Array} [curvesValues2] Array of curve values that the first * @param {Array} [curves2] Array of curve values that the first array
* array should be compared with. If not provided, collisions between * should be compared with. If not provided, collisions between curve
* curve 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} [sweepVertical] If true, the sweep is performed along
@ -82,40 +71,30 @@ var CollisionDetection = /** @lends CollisionDetection */{
* @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks * @param {Boolean} [onlySweepAxisCollisions] If true, no collision checks
* will be done on the secondary axis. * 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
* curveValues1 an array of the indexes of colliding bounds in * curves1 an array of the indexes of colliding bounds in curves2
* curveValues2
*/ */
findCurveBoundsCollisions: function(curvesValues1, curvesValues2, findCurveBoundsCollisions: function(curves1, curves2,
tolerance, sweepVertical, onlySweepAxisCollisions) { tolerance, sweepVertical, onlySweepAxisCollisions) {
function getBounds(curves) {
var min = Math.min, var min = Math.min,
max = Math.max, max = Math.max,
bounds1 = new Array(curvesValues1.length), bounds = new Array(curves.length);
bounds2; for (var i = 0; i < curves.length; i++) {
for (var i = 0; i < bounds1.length; i++) { var v = curves[i];
var v1 = curvesValues1[i]; bounds[i] = [
bounds1[i] = [ min(v[0], v[2], v[4], v[6]),
min(v1[0], v1[2], v1[4], v1[6]), min(v[1], v[3], v[5], v[7]),
min(v1[1], v1[3], v1[5], v1[7]), max(v[0], v[2], v[4], v[6]),
max(v1[0], v1[2], v1[4], v1[6]), max(v[1], v[3], v[5], v[7])
max(v1[1], v1[3], v1[5], v1[7])
]; ];
} }
if (curvesValues2) { return bounds;
if (curvesValues2 === curvesValues1) {
bounds2 = bounds1;
} else {
bounds2 = new Array(curvesValues2.length);
for (var i = 0; i < bounds2.length; i++) {
var v2 = curvesValues2[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]),
max(v2[1], v2[3], v2[5], v2[7])
];
}
}
} }
var bounds1 = getBounds(curves1),
bounds2 = !curves2 || curves2 === curves1
? bounds1
: getBounds(curves2);
return this.findBoundsCollisions(bounds1, bounds2, return this.findBoundsCollisions(bounds1, bounds2,
tolerance || 0, sweepVertical, onlySweepAxisCollisions); tolerance || 0, sweepVertical, onlySweepAxisCollisions);
}, },
@ -156,21 +135,20 @@ var CollisionDetection = /** @lends CollisionDetection */{
*/ */
findBoundsCollisions: function(boundsA, boundsB, tolerance, findBoundsCollisions: function(boundsA, boundsB, tolerance,
sweepVertical, onlySweepAxisCollisions) { sweepVertical, onlySweepAxisCollisions) {
var self = !boundsB || boundsA === boundsB,
allBounds = self ? boundsA : boundsA.concat(boundsB),
lengthA = boundsA.length,
lengthAll = allBounds.length;
// Binary search utility function. // Binary search utility function.
// For multiple same entries, this returns the rightmost entry. // For multiple same entries, this returns the rightmost entry.
// https://en.wikipedia.org/wiki/Binary_search_algorithm#Procedure_for_finding_the_rightmost_element // https://en.wikipedia.org/wiki/Binary_search_algorithm#Procedure_for_finding_the_rightmost_element
var self = !boundsB || boundsA === boundsB, function binarySearch(indices, coord, value) {
allBounds = self ? boundsA : boundsA.concat(boundsB), var lo = 0,
countA = boundsA.length,
countAll = allBounds.length,
lo, hi;
function binarySearch(indices, coordinateValue, coordinate) {
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]][coord] < value) {
lo = mid + 1; lo = mid + 1;
} else { } else {
hi = mid; hi = mid;
@ -182,73 +160,73 @@ var CollisionDetection = /** @lends CollisionDetection */{
// Set coordinates for primary and secondary axis depending on sweep // Set coordinates for primary and secondary axis depending on sweep
// direction. By default we sweep in horizontal direction, which // direction. By default we sweep in horizontal direction, which
// means x is the primary axis. // means x is the primary axis.
var coordP0 = sweepVertical ? 1 : 0, var pri0 = sweepVertical ? 1 : 0,
coordP1 = coordP0 + 2, pri1 = pri0 + 2,
coordS0 = sweepVertical ? 0 : 1, sec0 = sweepVertical ? 0 : 1,
coordS1 = coordS0 + 2; sec1 = sec0 + 2;
// Create array with all indices sorted by lower boundary on primary // Create array with all indices sorted by lower boundary on primary
// axis. // axis.
var allIndicesByP0 = new Array(countAll); var allIndicesByPri0 = new Array(lengthAll);
for (var i = 0; i < countAll; i++) { for (var i = 0; i < lengthAll; i++) {
allIndicesByP0[i] = i; allIndicesByPri0[i] = i;
} }
allIndicesByP0.sort(function(i1, i2) { allIndicesByPri0.sort(function(i1, i2) {
return allBounds[i1][coordP0] - allBounds[i2][coordP0]; return allBounds[i1][pri0] - allBounds[i2][pri0];
}); });
// Sweep along primary axis. Indices of active bounds are kept in an // Sweep along primary axis. Indices of active bounds are kept in an
// array sorted by higher boundary on primary axis. // array sorted by higher boundary on primary axis.
var activeIndicesByP1 = [], var activeIndicesByPri1 = [],
allCollisions = new Array(countA); allCollisions = new Array(lengthA);
for (var i = 0; i < countAll; i++) { for (var i = 0; i < lengthAll; i++) {
var currentIndex = allIndicesByP0[i], var curIndex = allIndicesByPri0[i],
currentBounds = allBounds[currentIndex], curBounds = allBounds[curIndex],
currentOriginalIndex = self // index in boundsA or boundsB // The original index in boundsA or boundsB:
? currentIndex origIndex = self ? curIndex : curIndex - lengthA,
: currentIndex - countA, isCurrentA = curIndex < lengthA,
isCurrentA = currentIndex < countA, isCurrentB = self || !isCurrentA,
isCurrentB = self || currentIndex >= countA, curCollisions = isCurrentA ? [] : null;
currentCollisions = isCurrentA ? [] : null; if (activeIndicesByPri1.length) {
if (activeIndicesByP1.length) { // remove (prune) indices that are no longer active.
// remove (prune) indices that are no longer active var pruneCount = binarySearch(activeIndicesByPri1, pri1,
var pruneCount = binarySearch(activeIndicesByP1, curBounds[pri0] - tolerance) + 1;
currentBounds[coordP0] - tolerance, coordP1) + 1; activeIndicesByPri1.splice(0, pruneCount);
activeIndicesByP1.splice(0, pruneCount); // Add collisions for current index.
// add collisions for current index
if (self && onlySweepAxisCollisions) { if (self && onlySweepAxisCollisions) {
// All active indexes can be added, no further checks needed // All active indexes can be added, no further checks needed
currentCollisions = currentCollisions.concat( curCollisions = curCollisions.concat(activeIndicesByPri1);
activeIndicesByP1.slice());
// Add current index to collisions of all active indexes // Add current index to collisions of all active indexes
for (var j = 0; j < activeIndicesByP1.length; j++) { for (var j = 0; j < activeIndicesByPri1.length; j++) {
var activeIndex = activeIndicesByP1[j]; var activeIndex = activeIndicesByPri1[j];
allCollisions[activeIndex].push(currentOriginalIndex); allCollisions[activeIndex].push(origIndex);
} }
} else { } else {
var currentS1 = currentBounds[coordS1], var curSec1 = curBounds[sec1],
currentS0 = currentBounds[coordS0]; curSec0 = curBounds[sec0];
for (var j = 0; j < activeIndicesByP1.length; j++) { for (var j = 0; j < activeIndicesByPri1.length; j++) {
var activeIndex = activeIndicesByP1[j], var activeIndex = activeIndicesByPri1[j],
isActiveA = activeIndex < countA, activeBounds = allBounds[activeIndex],
isActiveB = self || activeIndex >= countA; isActiveA = activeIndex < lengthA,
// Check secondary axis bounds if necessary isActiveB = self || activeIndex >= lengthA;
if (onlySweepAxisCollisions ||
(((isCurrentA && isActiveB) || // Check secondary axis bounds if necessary.
(isCurrentB && isActiveA)) && if (
currentS1 >= onlySweepAxisCollisions ||
allBounds[activeIndex][coordS0] - (
tolerance && isCurrentA && isActiveB ||
currentS0 <= isCurrentB && isActiveA
allBounds[activeIndex][coordS1] + ) && (
tolerance)) { curSec1 >= activeBounds[sec0] - tolerance &&
curSec0 <= activeBounds[sec1] + tolerance
)
) {
// Add current index to collisions of active // Add current index to collisions of active
// indices and vice versa. // indices and vice versa.
if (isCurrentA && isActiveB) { if (isCurrentA && isActiveB) {
currentCollisions.push( curCollisions.push(
self ? activeIndex : activeIndex - countA); self ? activeIndex : activeIndex - lengthA);
} }
if (isCurrentB && isActiveA) { if (isCurrentB && isActiveA) {
allCollisions[activeIndex].push( allCollisions[activeIndex].push(origIndex);
currentOriginalIndex);
} }
} }
} }
@ -256,29 +234,27 @@ var CollisionDetection = /** @lends CollisionDetection */{
} }
if (isCurrentA) { if (isCurrentA) {
if (boundsA === boundsB) { if (boundsA === boundsB) {
// if both arrays are the same, add self collision // If both arrays are the same, add self collision.
currentCollisions.push(currentIndex); curCollisions.push(curIndex);
} }
// add collisions for current index // Add collisions for current index.
allCollisions[currentIndex] = currentCollisions; allCollisions[curIndex] = curCollisions;
} }
// add current index to active indices. Keep array sorted by // Add current index to active indices. Keep array sorted by
// their higher boundary on the primary axis // their higher boundary on the primary axis.s
if (activeIndicesByP1.length) { if (activeIndicesByPri1.length) {
var currentP1 = currentBounds[coordP1], var curPri1 = curBounds[pri1],
insertIndex = index = binarySearch(activeIndicesByPri1, pri1, curPri1);
binarySearch(activeIndicesByP1, currentP1, coordP1) + 1; activeIndicesByPri1.splice(index + 1, 0, curIndex);
activeIndicesByP1.splice(insertIndex, 0, currentIndex);
} else { } else {
activeIndicesByP1.push(currentIndex); activeIndicesByPri1.push(curIndex);
} }
} }
// 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]) { var collisions = allCollisions[i];
allCollisions[i].sort(function(i1, i2) { if (collisions) {
return i1 - i2; collisions.sort(function(i1, i2) { return i1 - i2; });
});
} }
} }
return allCollisions; return allCollisions;