Label position and text height
32-bit in partThe point where a remnant's number is written and the height of that text.
d = chamfer distance in cells (steps 1 and 1.4142); x = (i + 0.5) × cell; y = (j + 0.5) × cell; h = max(cell, min(0.6 × d × cell, height ÷ 4))
The distance array is 32-bit and the largest distance is read back from it, so the text height carries about 7 digits. The position is computed from whole cell indexes in 64-bit numbers and is not affected. 1.4142 is √2 rounded to 5 digits.
const d = new Float32Array(cols * rows);
const BIG = 1e9, at = (i, j) => (i < 0 || j < 0 || i >= cols || j >= rows ? 0 : d[i * rows + j]);
for (let i = 0; i < cols; i++) for (let j = 0; j < rows; j++) d[i * rows + j] = label[i * rows + j] === comp.id ? BIG : 0;
for (let i = 0; i < cols; i++) for (let j = 0; j < rows; j++) { if (d[i * rows + j] === 0) continue; d[i * rows + j] = Math.min(d[i * rows + j], at(i - 1, j) + 1, at(i, j - 1) + 1, at(i - 1, j - 1) + 1.4142, at(i - 1, j + 1) + 1.4142); }
let bi = 0, bj = 0, bd = -1;
for (let i = cols - 1; i >= 0; i--) for (let j = rows - 1; j >= 0; j--) {
if (d[i * rows + j] === 0) continue;
d[i * rows + j] = Math.min(d[i * rows + j], at(i + 1, j) + 1, at(i, j + 1) + 1, at(i + 1, j + 1) + 1.4142, at(i + 1, j - 1) + 1.4142);
if (d[i * rows + j] > bd) { bd = d[i * rows + j]; bi = i; bj = j; }
}
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
for (const p of flatten(outline, 1e-3)) { x0 = Math.min(x0, p.x); y0 = Math.min(y0, p.y); x1 = Math.max(x1, p.x); y1 = Math.max(y1, p.y); }
out.push({ loops: [outline, ...holes], area, w: x1 - x0, h: y1 - y0, x0, y0, label: { x: (bi + 0.5) * res, y: (bj + 0.5) * res, h: Math.max(res, Math.min(bd * res * 0.6, (y1 - y0) / 4)) } });