Resources Numerical accuracy Calculation policy

Where a remnant's number is written

Finds the roomiest spot inside a remnant for its number, and the text height that fits there.

How the precision is kept

The distance from each free cell to the edge of its region is kept in a 32-bit array and built up with steps of 1 and 1.4142 (a rounded √2). The label's position comes from whole cell indexes, but its text height is a product of that 32-bit distance. The value is a text size on the nest drawing, not a measure of the remnant.

Open item. Keep the distances in a Float64Array and use Math.SQRT2 for the diagonal step, so the label height is a 64-bit value.

Calculations under this policy

1 calculation in 1 source file. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

addons/apparel/lib/nest/nest-stock.js CadCraft 0.8.2 32-bit in part 1 calculation Reviewed

Tests tools/nest/test-stock.mjs

Label position and text height

32-bit in part

The 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.

  • addons/apparel/lib/nest/nest-stock.js
  • lines 294 to 306
  • CadCraft 0.8.2
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)) } });

In detail

Method
Two-pass chamfer distance transform on the grid; the cell with the largest distance takes the label.
Number types
Float32Array for the distances; 64-bit numbers for the result.
Tolerances
Diagonal step 1.4142 in place of √2. Text height = 0.6 × distance × cell, at least one cell and at most a quarter of the remnant's height.
Limits
A chamfer distance with steps of 1 and 1.4142 is an approximation of the straight-line distance; here it only picks a place for text.

Approach: 32-bit step, flagged

One step stores or exchanges 32-bit values that reach a number a person reads. It is listed here with its open item, and the step is either moved to 64-bit or the app will warn where the number is read.

Every policy that takes this approach