Works out the cut-list parts of a cabinet job: face-frame stiles and rails, overlay doors and drawer fronts, drawer boxes, blind panels, fillers and end panels.
How the precision is kept
Sizes are 64-bit numbers in the job's unit, millimeters or inches. Every part length and width and every front position is rounded to 6 decimals when it is stored. Default sizes are given in millimeters; for an inch job they are divided by 25.4 and rounded to the nearest 1/16 inch.
Open item. Keep part sizes unrounded in the job and round only where a list or file is written.
Calculations under this policy
11 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.
The stile and rail width, overlay and door gap a job uses when it sets none.
stile = rail = 38 mm; overlay = 12 mm; gap = 3 mm; inch job: round(mm ÷ 25.4 × 16) ÷ 16
conv (core.js) leaves millimeter values as they are and, for an inch job, divides by 25.4 and rounds to the nearest 1/16 inch. Sizes set in the job replace the defaults unchanged.
The rails, the openings between them and the doors or drawer fronts that cover each opening.
opening h = (box h − (2 + mids) × rail) ÷ (mids + 1); front h = opening h + lap below + lap above; pair door w = (opening w + 2 × overlay − gap) ÷ 2
The running height adds unrounded opening heights and rails; the openings' y, width and height and every front's position and size are rounded to 6 decimals. Front sizes are computed from the opening's already rounded y, width and height and rounded again. Two stacked doors are used when box height less the reveal exceeds the maximum door height.
addons/cabinetcraft/lib/build.js
lines 44 to 80
CadCraft 0.8.2
/** The frame, its openings and the overlay fronts of a face-frame cabinet (front view: x from the left side, y from the bottom incl. the plinth). */
export function frameLayout(cab, R, F, blind = 0, hinge = 'left') {
const { kick, boxH, iw } = boxOf(cab, R);
const blindLeft = blind > 0 && hinge === 'right';
const x0 = F.stile + (blindLeft ? blind : 0), openW = cab.w - 2 * F.stile - blind;
const rv = R.reveal;
const n = cab.type === 'drawers' ? (cab.fronts ? cab.fronts.length : cab.drawers) : (cab.doors === 0 ? 0 : (boxH - rv > R.maxDoorH ? 2 : 1));
const rails = []; // y of each rail from the plinth top, bottom up
const stack = cab.type === 'drawers' ? n : n; // openings stacked
const mids = Math.max(0, (cab.type === 'drawers' ? n : n) - 1);
const openH = (boxH - (2 + mids) * F.rail) / Math.max(1, mids + 1);
rails.push({ y: kick, h: F.rail, kind: 'rail' });
const openings = [];
let y = kick + F.rail;
for (let i = 0; i < Math.max(1, stack); i++) {
openings.push({ x: x0, y: r6(y), w: r6(openW), h: r6(openH) });
y += openH;
if (i < Math.max(1, stack) - 1) { rails.push({ y: r6(y), h: F.rail, kind: 'mid' }); y += F.rail; }
}
rails.push({ y: r6(kick + boxH - F.rail), h: F.rail, kind: 'rail' });
const fronts = [];
if (n > 0) {
const edge = F.overlay, inner = (F.rail - F.gap) / 2;
const pair = cab.type !== 'drawers' && (cab.doors === 2 || (cab.doors === 'auto' && openW + 2 * edge > R.maxDoorW));
openings.forEach((o, i) => {
const bottom = i === 0 ? edge : inner, top = i === openings.length - 1 ? edge : inner;
const fy = o.y - bottom, fh = o.h + bottom + top;
if (cab.type === 'drawers') fronts.push({ kind: 'drawer', x: r6(o.x - edge), y: r6(fy), w: r6(o.w + 2 * edge), h: r6(fh), opening: o });
else if (pair) {
const dw = (o.w + 2 * edge - F.gap) / 2;
fronts.push({ kind: 'door', x: r6(o.x - edge), y: r6(fy), w: r6(dw), h: r6(fh), hinge: 'left', opening: o });
fronts.push({ kind: 'door', x: r6(o.x - edge + dw + F.gap), y: r6(fy), w: r6(dw), h: r6(fh), hinge: 'right', opening: o });
} else fronts.push({ kind: 'door', x: r6(o.x - edge), y: r6(fy), w: r6(o.w + 2 * edge), h: r6(fh), hinge, opening: o });
});
}
return { stile: F.stile, rails, openings, fronts, boxH, kick, iw };
}
Door and blind panel of a blind corner
64-bit
The door over the open part of a blind corner cabinet and the panel over the blind part.
door w = (w − blind) − reveal; panel w = blind − reveal; h = box h − reveal
Subtractions of the reveal and half the reveal in 64-bit; each position and size is rounded to 6 decimals.
addons/cabinetcraft/lib/build.js
lines 82 to 91
CadCraft 0.8.2
/** A blind corner's fronts (frameless): one door over the open part, a blind panel over the blind part. */
function blindFronts(cab, R, blind, hinge) {
const { kick, boxH } = boxOf(cab, R);
const rv = R.reveal, openW = cab.w - blind;
const blindLeft = hinge === 'right';
const dx = (blindLeft ? blind : 0) + rv / 2;
const out = [{ kind: 'door', x: r6(dx), y: r6(kick + rv / 2), w: r6(openW - rv), h: r6(boxH - rv), hinge }];
out.push({ kind: 'blind', x: r6(blindLeft ? rv / 2 : openW + rv / 2), y: r6(kick + rv / 2), w: r6(blind - rv), h: r6(boxH - rv) });
return out;
}
Part size stored in the cut list
64-bit
Adds one part with its length, width, thickness and material.
Length and width are rounded to 6 decimals of the job's unit when the part is stored; thickness is kept as given. A part whose length or width is not above 0 is left out.
Groups equal fronts into one cut-list line with a quantity.
Fronts are grouped by the text of their rounded height and width, so two fronts count as one size only when both rounded values are identical. The quantity is a count.
addons/cabinetcraft/lib/build.js
lines 150 to 153
CadCraft 0.8.2
const group = (list) => { const m = new Map(); for (const f of list) { const k = f.h + 'x' + f.w; m.set(k, { f, n: (m.get(k)?.n || 0) + 1 }); } return [...m.values()]; };
for (const { f, n } of group(doors)) addPart(L, cab.no, 'door', 'part.door', f.h, f.w, R.frontT, R.frontMat, { qty: n, band: { l1: R.bandFront, l2: R.bandFront, w1: R.bandFront, w2: R.bandFront } });
const dr = fronts.filter((f) => f.kind === 'drawer');
for (const { f, n } of group(dr)) addPart(L, cab.no, 'drawer-front', 'part.drawer-front', f.w, f.h, R.frontT, R.frontMat, { qty: n, band: { l1: R.bandFront, l2: R.bandFront, w1: R.bandFront, w2: R.bandFront } });
Drawer boxes of a face-frame cabinet
64-bit
The sides, ends and bottom of each drawer box behind a face frame.
box h = min(front h − clearance, opening h − 20 mm); end = bw − 2t; bottom = (bw − 2t + 2g) × (slide − 2t + 2g); bw = opening w − 2 × slide gap
The box height is rounded to 6 decimals and used as the key that counts equal boxes. The 20 mm allowance goes through conv, so an inch job uses 13/16 in (20.6375 mm). Boxes lower than the minimum height are left out.
The outline points of a made-to-order curved cabinet, measured from the cell's origin along its two axes.
(x′, y′) = ((q − o) · u, (q − o) · n)
Two dot products of 64-bit differences with the axes u and n of the cell's frame (cell.pf), each rounded to 6 decimals. Without a frame the points are kept as they are.
Face frame: opening height = (box height − (2 + mid rails) × rail) ÷ openings. Fronts lap the opening by the overlay on outer edges and by (rail − gap) ÷ 2 where two fronts meet on a rail. A pair of doors splits the width less the gap.
Stored sizes carry 6 decimals of the job's unit. Fronts are computed from openings already rounded to 6 decimals. Inch defaults are multiples of 1/16 inch, so they are not the millimeter defaults converted exactly.
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.