@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
137 lines (105 loc) • 3.42 kB
JavaScript
import { assert } from "../../../../../core/assert.js";
import { BitSet } from "../../../../../core/binary/BitSet.js";
/**
* Naive flood implementation of a distance field computation algorithm
* @param {Sampler2D} source
* @param {Sampler2D} destination
* @param {number} emptyValue
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export function computeSignedDistanceField_NaiveFlood(
source,
destination,
emptyValue
) {
assert.equal(source.itemSize, 1, `unsupported source.itemSize, expected 1, got '${source.itemSize}'`);
assert.isNumber(emptyValue, 'emptyValue');
let i, j;
const distanceData = destination.data;
const visited = new BitSet();
const openSet = new BitSet();
//use "flood" algorithm
//mark all visible tiles as visited
const samplerData = source.data;
const totalCellCount = samplerData.length;
function traverseNeighbours(index, visitor) {
let i = index - (width + 1);
const top = index > width;
const bottom = index < (totalCellCount - width);
const x = index % width;
const left = x > 0;
const right = x < width - 1;
if (top) {
if (left) {
visitor(i);
}
visitor(i + 1);
if (right) {
visitor(i + 2);
}
}
i += width;
if (left) {
visitor(i);
}
if (right) {
visitor(i + 2);
}
i += width;
if (bottom) {
if (left) {
visitor(i);
}
visitor(i + 1);
if (right) {
visitor(i + 2);
}
}
}
const width = destination.width;
for (i = 0; i < totalCellCount; i++) {
const sampleValue = samplerData[i];
if (sampleValue !== emptyValue) {
visited.set(i, true);
//write distance data
distanceData[i] = 0;
} else {
distanceData[i] = 255;
}
}
//populate initial open set
for (i = visited.nextSetBit(0); i !== -1; i = visited.nextSetBit(i + 1)) {
j = 0;
traverseNeighbours(i, function (neighbourIndex) {
if (!visited.get(neighbourIndex)) {
//increment number of not visited
j++;
}
});
if (j === 0) {
//all neighbours are visited, we can safely ignore this cell
} else {
openSet.set(i, true);
}
}
for (i = openSet.nextSetBit(0); i !== -1; i = openSet.nextSetBit(0)) {
//remove from open set
openSet.set(i, false);
const value = distanceData[i];
traverseNeighbours(i, function (neighbourIndex) {
const neighbourValue = value + 1;
if (visited.get(neighbourIndex)) {
if (distanceData[neighbourIndex] <= neighbourValue) {
return;
}
} else {
visited.set(neighbourIndex, true);
}
distanceData[neighbourIndex] = neighbourValue;
//add neighbour to open set
openSet.set(neighbourIndex, true);
});
}
}