Resources Numerical accuracy Calculation policy

Cut list sizes, areas and banding lengths

Works out each part's cut size from its finished size and edge banding, and totals pieces, areas, banding lengths and a rough sheet count.

How the precision is kept

Sizes stay in the caller's unit; nothing converts. Cut sizes, group areas and banding lengths are 64-bit sums rounded to 6 decimals. The CSV export rounds every number to 2 decimals by default. The sheet count is an estimate from area with a fixed waste factor, as the file's header says.

Open item. Write CSV sizes with at least 4 decimals, so sizes in sixteenths of an inch are exported exactly.

Calculations under this policy

7 calculations 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/cabinetcraft/lib/cutlist/cutlist.js CadCraft 0.8.2 64-bit 7 calculations Reviewed

Tests tools/cutlist/test.mjs

Rounding to a number of decimals

64-bit

Rounds a value to dp decimals, 6 by default.

round(v, dp) = Math.round(v × 10^dp) ÷ 10^dp

The scaling happens in binary, so a value on a decimal half can round either way. Negative zero is returned as 0.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • line 35
  • CadCraft 0.8.2
const round = (v, dp = 6) => { const k = 10 ** dp; const r = Math.round(v * k) / k; return Object.is(r, -0) ? 0 : r; };

Cut size of a part

64-bit

The size to cut a part so it measures its finished size once the banding is on.

cut width = width − (t_L1 + t_L2) + premill × banded L edges; cut length = length − (t_W1 + t_W2) + premill × banded W edges

The two banding thicknesses are added first and subtracted once. The result is rounded to 6 decimals.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 64 to 76
  • CadCraft 0.8.2
export function cutSize(part, { deductBand = true, premill = 0 } = {}) {
  let length = part.length, width = part.width;
  if (deductBand) {
    // the L edges run along the length: their banding sits at the ends of the width, and the other way round
    width -= bt(part, 'l1') + bt(part, 'l2');
    length -= bt(part, 'w1') + bt(part, 'w2');
  }
  if (premill) {
    width += premill * ((part.band?.l1 ? 1 : 0) + (part.band?.l2 ? 1 : 0));
    length += premill * ((part.band?.w1 ? 1 : 0) + (part.band?.w2 ? 1 : 0));
  }
  return { length: round(length), width: round(width) };
}

Pieces and areas per material

64-bit

The piece count, finished area and cut area of each material and thickness.

area = Σ qty × length × width; cut area = Σ qty × cut length × cut width

Sums in part order with no compensation; the totals are rounded to 6 decimals after the loop. The cut area uses cut sizes that were already rounded to 6 decimals.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 96 to 102
  • CadCraft 0.8.2
const cut = cutSize(p, { deductBand, premill });
const prev = merge ? g.rows.find((r) => sameKind(r, p)) : null;
if (prev) { prev.qty += p.qty; prev.ids.push(p.id); }
else g.rows.push({ ...p, cut, ids: [p.id] });
g.pieces += p.qty;
g.area += p.qty * p.length * p.width;
g.cutArea += p.qty * cut.length * cut.width;

Edge banding length

64-bit

The length of each banding, the number of edges, and the length with trim waste.

length = Σ qty × edge; with waste = Σ qty × (edge + waste per edge)

The edge is the finished length for L edges and the finished width for W edges. Sums in part order, rounded to 6 decimals afterwards.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 111 to 112
  • CadCraft 0.8.2
const e = bands.get(k), len = s[0] === 'l' ? p.length : p.width;
e.length += p.qty * len; e.edges += p.qty; e.withWaste += p.qty * (len + bandWaste);

Rough sheet count

64-bit

An estimate of the sheets a material needs, from area only.

estimate = ⌈round(cut area × (1 + 0.15) ÷ sheet area, 9 decimals)⌉

The quotient is rounded to 9 decimals before Math.ceil, so a value a rounding error above a whole number does not add a sheet. The 15 % waste is a default the caller can change.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 118 to 124
  • CadCraft 0.8.2
const sheetArea = sheet && sheet.w > 0 && sheet.h > 0 ? sheet.w * sheet.h : 0;
return {
  groups: list,
  pieces: list.reduce((n, g) => n + g.pieces, 0),
  area: round(list.reduce((n, g) => n + g.area, 0)),
  banding,
  sheets: list.map((g) => ({ key: g.key, material: g.material, thickness: g.thickness, area: g.cutArea, estimate: sheetArea ? Math.ceil(round(g.cutArea * (1 + kerfWaste) / sheetArea, 9)) : null })),

Parts handed to nesting

64-bit

Each part as a rectangle at its cut size for the nesting engine.

The rectangle's corners are 0 and the cut length and width, which are rounded to 6 decimals. A part with its own outline is passed on unchanged.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 128 to 143
  • CadCraft 0.8.2
/**
 * Parts → lib/nest's part list (no import: the shape is lib/nest/nest.js `nest(parts, …)`'s). Each part is nested at its CUT
 * size (the bander adds the rest); x runs along the length, so a part with grain has grain angle 0 and may turn round
 * (turn: true = 0° or 180°), a part without grain any way. One nest per material and thickness: `byMaterial` groups them.
 */
export function toNestParts(partsIn, { deductBand = true, premill = 0 } = {}) {
  const out = [];
  for (const p0 of partsIn) {
    const p = cleanPart(p0);
    if (!p) continue;
    const c = cutSize(p, { deductBand, premill });
    const rect = [{ x: 0, y: 0 }, { x: c.length, y: 0 }, { x: c.length, y: c.width }, { x: 0, y: c.width }];
    out.push({ id: p.id, name: p.name, loops: p.outline || [rect], qty: p.qty, grain: p.grain === 'length' ? 0 : null, turn: true, material: p.material, thickness: p.thickness });
  }
  return out;
}

Cut list CSV numbers

64-bit

Writes the cut list as CSV text.

number cell = round(v, dp), dp = 2 by default

Every numeric cell is rounded to dp decimals, 2 unless the caller passes another value. With 2 decimals a length of 23.0625 is written 23.06.

  • addons/cabinetcraft/lib/cutlist/cutlist.js
  • lines 176 to 188
  • CadCraft 0.8.2
/** The cut list as CSV: one row per list row, grouped by material. `head` relabels columns (a translated header). */
export function toCsv(list, { columns = CSV_COLUMNS, head = CSV_HEAD, dp = 2 } = {}) {
  const f = (v) => (typeof v === 'number' ? String(round(v, dp)) : v);
  const rows = [columns.map((c) => csvCell(head[c] ?? c))];
  for (const g of list.groups) for (const r of g.rows) {
    const cell = {
      ...r, id: r.ids.join(' '), grain: r.grain, cutLength: r.cut.length, cutWidth: r.cut.width,
      l1: r.band.l1?.code || '', l2: r.band.l2?.code || '', w1: r.band.w1?.code || '', w2: r.band.w2?.code || '',
    };
    rows.push(columns.map((c) => csvCell(f(cell[c]))));
  }
  return rows.map((r) => r.join(',')).join('\r\n') + '\r\n';
}

In detail

Method
Cut size = finished size − banding thickness on each banded edge + pre-mill per banded edge; area = Σ qty × length × width; banding = Σ qty × edge length; sheets ≈ ⌈cut area × (1 + 0.15) ÷ sheet area⌉.
Number types
64-bit numbers.
Checked by
tools/cutlist/test.mjs runs lib/cutlist/cutlist.js, which was byte-identical to this file at the review. It compares cutSize of a 717 × 597 door banded 2 mm all round with 713 × 593, group areas and banding lengths with hand sums, and the sheet estimate with the same formula.
Limits
With 2 decimals in the CSV, a size in sixteenths of an inch is not written exactly: 23.0625 becomes 23.06. The sheet count ignores shapes and grain; nesting gives the real count.

Approach: 64-bit, stated limit

The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.

Every policy that takes this approach