@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
141 lines (106 loc) • 3.48 kB
JavaScript
import { clamp } from "../../../../../core/math/clamp.js";
import { min2 } from "../../../../../core/math/min2.js";
/**
* algorithm proposed by Borgefors, Chamfer distance [J. ACM 15 (1968) 600, Comput. Vis. Graph. Image Process. 34 (1986) 344], h
* @param {Sampler2D} source
* @param {Sampler2D} distanceField
* @param {number} emptyValue
* @param {number} d1 distance between two adjacent pixels in either x or y direction
* @param {number} d2 distance between two diagonally adjacent pixels
* @param maxD
*/
export function computeSignedDistanceField_Chamfer(source, distanceField, emptyValue, d1, d2, maxD) {
const sourceData = source.data;
const distanceFieldData = distanceField.data;
const width = source.width;
const height = source.height;
const maxX = width - 1;
const maxY = height - 1;
function getS(x, y) {
x = clamp(x, 0, maxX);
y = clamp(y, 0, maxY);
const index = x + y * width;
return sourceData[index];
}
function getD(x, y) {
x = clamp(x, 0, maxX);
y = clamp(y, 0, maxY);
const index = x + y * width;
return distanceFieldData[index];
}
function setD(x, y, v) {
x = clamp(x, 0, maxX);
y = clamp(y, 0, maxY);
const index = x + y * width;
distanceFieldData[index] = min2(v, maxD);
}
let x, y;
//initialize distance field
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (
getS(x - 1, y) !== getS(x, y)
|| getS(x + 1, y) !== getS(x, y)
|| getS(x, y - 1) !== getS(x, y)
|| getS(x, y + 1) !== getS(x, y)
) {
setD(x, y, 0);
} else {
setD(x, y, 255);
}
}
}
//first pass (forward)
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
const v = getD(x, y);
const v0 = getD(x - 1, y - 1) + d2;
if (v0 < v) {
setD(x, y, v0);
}
const v1 = getD(x, y - 1) + d1;
if (v1 < v) {
setD(x, y, v1);
}
const v2 = getD(x + 1, y - 1) + d2;
if (v2 < v) {
setD(x, y, v2);
}
const v3 = getD(x - 1, y) + d1;
if (v3 < v) {
setD(x, y, v3);
}
}
}
//second pass (backward)
for (y = maxY; y >= 0; y--) {
for (x = maxX; x >= 0; x--) {
const v = getD(x, y);
const v0 = getD(x + 1, y) + d1;
if (v0 < v) {
setD(x, y, v0);
}
const v1 = getD(x - 1, y + 1) + d2;
if (v1 < v) {
setD(x, y, v1);
}
const v2 = getD(x, y + 1) + d1;
if (v2 < v) {
setD(x, y, v2);
}
const v3 = getD(x + 1, y + 1) + d2;
if (v3 < v) {
setD(x, y, v3);
}
}
}
//indicate inside & outside
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
if (getS(x, y) !== emptyValue) {
//inside
setD(x, y, -getD(x, y));
}
}
}
}