UNPKG

vprsdf

Version:

Tools for creating signed distance fields from image data

162 lines (161 loc) 5.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sdf = void 0; function sdf(inFilled, width) { var height = Math.floor(inFilled.length / width); var halfVector = new Array(inFilled.length).fill([ 2 * width + 1, 2 * height + 1, ]); sdfPartial(inFilled, width, halfVector, euclidean, false); sdfPartial(inFilled, width, halfVector, euclidean, true); var out = new Array(inFilled.length); for (var i = 0; i < halfVector.length; i++) { var _a = halfVector[i], dx = _a[0], dy = _a[1]; out[i] = euclidean(dx, dy) / 2; if (inFilled[i]) { out[i] = -out[i]; } } return out; } exports.sdf = sdf; function euclidean(dx, dy) { return Math.sqrt(dx * dx + dy * dy); } function sdfPartial(inFilled, width, inHalfVector, metric, negate) { if (width === 0) throw new Error("Width cannot be zero."); var height = Math.floor(inFilled.length / width); if (height === 0) throw new Error("Height cannot be zero."); var validPixel = function (x, y) { return x >= 0 && x < width && y >= 0 && y < height; }; var coord = function (x, y) { return x + width * y; }; var filled = function (x, y) { return validPixel(x, y) ? inFilled[coord(x, y)] !== negate : negate; }; var doNeighbors = function (x, y, f) { for (var dy = -1; dy <= 1; dy++) { for (var dx = -1; dx <= 1; dx++) { if (validPixel(x + dx, y + dy)) { f(x + dx, y + dy); } } } }; var halfVector = inHalfVector; var closed = new Array(inFilled.length).fill(false); var QueueCompare = /** @class */ (function () { function QueueCompare() { } QueueCompare.prototype.compare = function (a, b) { return a.dist > b.dist; }; return QueueCompare; }()); var pq = new PriorityQueue(new QueueCompare().compare); var addToQueue = function (x, y, dx, dy) { pq.push({ x: x, y: y, dx: dx, dy: dy, dist: metric(dx, dy) }); }; var _loop_1 = function (y) { var _loop_3 = function (x) { if (filled(x, y)) { doNeighbors(x, y, function (x2, y2) { if (!filled(x2, y2)) { addToQueue(x2, y2, x2 - x, y2 - y); } }); } }; for (var x = 0; x < width; x++) { _loop_3(x); } }; for (var y = 0; y < height; y++) { _loop_1(y); } var _loop_2 = function () { var current = pq.pop(); if (closed[coord(current.x, current.y)]) return "continue"; closed[coord(current.x, current.y)] = true; halfVector[coord(current.x, current.y)] = [current.dx, current.dy]; doNeighbors(current.x, current.y, function (x2, y2) { if (!filled(x2, y2) && !closed[coord(x2, y2)]) { var dx = 2 * (x2 - current.x); var dy = 2 * (y2 - current.y); var _a = halfVector[coord(current.x, current.y)], ddx = _a[0], ddy = _a[1]; dx += ddx; dy += ddy; addToQueue(x2, y2, dx, dy); } }); }; while (!pq.isEmpty()) { _loop_2(); } } // PriorityQueue implementation var PriorityQueue = /** @class */ (function () { function PriorityQueue(compare) { this.heap = []; this.compare = compare; } PriorityQueue.prototype.push = function (item) { this.heap.push(item); this.bubbleUp(this.heap.length - 1); }; PriorityQueue.prototype.pop = function () { var top = this.heap[0]; var bottom = this.heap.pop(); if (this.heap.length > 0 && bottom !== undefined) { this.heap[0] = bottom; this.sinkDown(0); } return top; }; PriorityQueue.prototype.isEmpty = function () { return this.heap.length === 0; }; PriorityQueue.prototype.bubbleUp = function (index) { var element = this.heap[index]; while (index > 0) { var parentIndex = Math.floor((index + 1) / 2) - 1; var parent_1 = this.heap[parentIndex]; if (this.compare(element, parent_1)) break; this.heap[index] = parent_1; index = parentIndex; } this.heap[index] = element; }; PriorityQueue.prototype.sinkDown = function (index) { var length = this.heap.length; var element = this.heap[index]; while (true) { var swap = null; var leftIndex = 2 * (index + 1) - 1; var rightIndex = 2 * (index + 1); if (leftIndex < length) { var left = this.heap[leftIndex]; if (!this.compare(left, element)) { swap = leftIndex; } } if (rightIndex < length) { var right = this.heap[rightIndex]; if (!this.compare(right, swap === null ? element : this.heap[leftIndex])) { swap = rightIndex; } } if (swap === null) break; this.heap[index] = this.heap[swap]; index = swap; } this.heap[index] = element; }; return PriorityQueue; }());