Works out the sizes an edit puts into a cabinet job: the job in the other unit, a filler for a leftover, a gap at a door or window, and the notch of a new L or U room.
How the precision is kept
Each edit copies the job, changes numbers with ordinary 64-bit arithmetic and hands the copy to the job cleaner. The values kept are rounded on purpose: a unit change rounds every size to 1/16 inch or to a whole millimeter, a gap is rounded up to 0.1 job unit and a new notch to a whole job unit. The rounding, not the arithmetic, sets the accuracy.
Open item. Convert the room notch once in setUnit: its loop over three keys converts the notch width and depth three times.
Calculations under this policy
4 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.
Every size in the job converted from millimeters to inches or back when the job's unit is changed.
to inches: round(v × (1 ÷ 25.4) × 16) ÷ 16; to millimeters: round(v × 25.4)
Every converted size is rounded: to 1/16 inch going to inches, to a whole millimeter going back, so the stored value is not the exact conversion. The line that converts the room notch sits in a loop over three keys and does not use the key, so the notch width and depth are converted three times: a 1333 mm notch becomes 1 in, where 52.5 in is the single conversion. The typed hardware sizes (hw) are not among the converted values, and the shop's rules are reset to the new unit's defaults.
addons/cabinetcraft/lib/edit.js
lines 243 to 264
CadCraft 0.8.2
export function setUnit(p, unit) {
const to = unit === 'in' ? 'in' : 'mm';
if (p.unit === to) return p;
const k = to === 'in' ? 1 / 25.4 : 25.4;
const c = (v) => (to === 'in' ? Math.round(v * k * 16) / 16 : Math.round(v * k));
const q = clone(p);
q.unit = to;
for (const key of ['w', 'd', 'h']) q.room[key] = c(q.room[key]);
for (const key of ['w', 'h', 'margin', 'spacing']) q.sheet[key] = c(q.sheet[key]);
for (const key of Object.keys(q.counter)) q.counter[key] = c(q.counter[key]);
for (const key of Object.keys(q.ff)) q.ff[key] = c(q.ff[key]);
q.rules = {}; // the shop's numbers were in the old unit: back to the unit's own rule set
for (const key of ['w', 'd', 'h']) if (q.room.notch) { q.room.notch.w = c(q.room.notch.w); q.room.notch.d = c(q.room.notch.d); }
if (q.room.pts) q.room.pts = q.room.pts.map(([x, y]) => [c(x), c(y)]);
for (const o of q.openings) for (const key of ['at', 'w', 'h', 'sill']) o[key] = c(o[key]);
for (const i of q.islands) { for (const key of ['x', 'y', 'len', 'seat']) i[key] = c(i[key]); for (const row of ['base', 'back']) for (const it of i[row]) for (const key of ['w', 'h', 'd', 'elev', 'blind']) if (typeof it[key] === 'number') it[key] = c(it[key]); }
for (const r of q.runs) {
r.from = c(r.from); r.to = c(r.to);
for (const row of ['base', 'upper']) for (const it of r[row]) for (const key of ['w', 'h', 'd', 'elev', 'blind']) if (typeof it[key] === 'number') it[key] = c(it[key]);
}
return cleanProject(q);
}
Gap at a door or window
64-bit
The width of the gap put in a run's floor row so that the cabinets after it clear an opening.
width = ⌈(at + w + clear − start) × 10⌉ ÷ 10
The width is rounded up to 0.1 job unit, in millimeter and inch jobs alike, so the stored gap is up to 0.1 unit wider than the opening needs. The start is the end of the last cabinet that ends no more than 1e-6 units past the opening start minus the clearance.
addons/cabinetcraft/lib/edit.js
lines 188 to 203
CadCraft 0.8.2
/** Put a gap in the run's floor row where the opening is, so the cabinets after it move on: the gap starts at the end of the last cabinet that ends before the opening
* (minus the clearance) and runs past the opening plus the clearance. The run must be on the opening's wall. */
export function openGap(p, runId, openingId, { clear = conv(50, p.unit) } = {}) {
const op = p.openings.find((o) => o.id === openingId);
const run = p.runs.find((r) => r.id === runId);
if (!op || !run || run.wall !== op.wall) return p;
const lay = layoutProject(p).runs.find((r) => r.run.id === runId);
if (!lay) return p;
const cells = lay.rows.base.cells.filter((c) => c.kind !== 'endpanel' && !c.auto);
const a = op.at - clear, b = op.at + op.w + clear;
let at = 0, start = lay.rows.base.start;
for (let i = 0; i < cells.length; i++) { if (cells[i].x + cells[i].w <= a + 1e-6) { at = i + 1; start = cells[i].x + cells[i].w; } }
const width = Math.ceil((b - start) * 10) / 10;
if (!(width > 0)) return p;
return addItem(p, runId, 'base', { kind: 'gap', w: width }, at);
}
Filler for a leftover
64-bit
Adds a filler as wide as the space a row leaves on its wall.
filler width = rest, when 0.5 < rest ≤ 75 mm
The filler takes the layout's leftover unchanged. The lower bound 0.5 is in job units in both unit systems (0.5 mm or 0.5 in); only the upper bound of 75 mm is converted for an inch job.
Unit change: size × 25.4 or size × (1 ÷ 25.4), then rounded. Gap: from the end of the last cabinet before the opening to the far side of the opening plus a clearance. Filler: the row's leftover as the layout reports it.
Number types
64-bit numbers. The job is copied through JSON text, which keeps a 64-bit number whole.
Tolerances
A cabinet counts as ending before the opening when its end is at most 1e-6 job units past the gap start (absolute). A leftover becomes a filler only above 0.5 job units and up to 75 mm (in an inch job 75 mm converted and rounded to 1/16 inch).
Limits
A unit change is not reversible: 4000 mm becomes 157.5 in, which comes back as 4001 mm.
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.