CabinetCraft in a drawing: units, room outline and solids
Reads a room from a closed polyline of the drawing in millimeters, converts cabinet shop drawings and 3D solids to the drawing's unit, and decides where the sheets are drawn (CabinetCraft add-on in CadCraft).
How the precision is kept
Unit factors come from a table of meters per unit that holds the exact definitions (0.0254, 0.3048, 0.9144) and are applied in 64-bit numbers. A room outline with arcs is read as chords of at most 15 degrees, so a curved wall comes in as a polygon. Two places read a bounding box by field names that geom.js does not use, so they always take their fallback value.
Open item. Read the bounding box by the names geom.js returns (minX, maxX, minY, maxY); with maxx and minx the opening width is always the 900 mm default and the sheets always start at (0, 0).
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.
The factor from the drawing's unit to millimeters.
mm per unit = meters per unit ÷ 0.001; inch 0.0254, foot 0.3048, yard 0.9144, cm 0.01, m 1
The table holds the exact definitions of the inch, foot and yard in meters. The division by 0.001 gives 25.4, 304.8, 914.4, 10 and 1000 as the nearest 64-bit numbers to those values. An unknown or empty unit counts as millimeters.
The corners of a closed polyline in millimeters, with arcs turned into chords.
sweep = 4·atan(bulge); steps = max(1, ⌈|sweep| ÷ 15°⌉); r = chord ÷ (2·sin(|sweep| ÷ 2)); point = (c + r·(cos, sin)) × f
Corner coordinates are multiplied by the unit factor once. An arc segment becomes points on the arc at equal angle steps of at most 15 degrees, so a curved wall of the room is read as chords between those points. A segment of zero length is skipped before the division.
addons/cabinetcraft/host.js
lines 23 to 45
CadCraft 0.8.2
/** A polyline's corners in mm; a bulge becomes chords (15 degrees each at most). */
export function polyMm(e, f) {
const out = [];
const pts = e.pts || [];
const n = pts.length;
for (let i = 0; i < n; i++) {
const a = pts[i], b = pts[(i + 1) % n];
out.push([a.x * f, a.y * f]);
const bulge = Number(a.b) || 0;
if (bulge && (i < n - 1 || e.closed)) {
const sweep = 4 * Math.atan(bulge), steps = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 12)));
const dx = b.x - a.x, dy = b.y - a.y, chord = Math.hypot(dx, dy);
if (chord > 0) {
// the centre is on the left of a → b for a counter-clockwise arc (bulge > 0), on the right for a clockwise one
const r = chord / (2 * Math.sin(Math.abs(sweep) / 2)), off = r * Math.cos(Math.abs(sweep) / 2) * (bulge > 0 ? 1 : -1);
const cx = (a.x + b.x) / 2 - (dy / chord) * off, cy = (a.y + b.y) / 2 + (dx / chord) * off;
const a0 = Math.atan2(a.y - cy, a.x - cx);
for (let k = 1; k < steps; k++) { const ang = a0 + sweep * (k / steps); out.push([(cx + r * Math.cos(ang)) * f, (cy + r * Math.sin(ang)) * f]); }
}
}
}
return out;
}
Drawing units per job unit
64-bit
The factor that scales the cabinet job (millimeters or inches) to the drawing's unit.
k = (25.4 or 1) ÷ mm per drawing unit
One division of two 64-bit factors. For a millimeter job in a millimeter drawing k is exactly 1 and the sheets are not rescaled.
addons/cabinetcraft/host.js
line 55
CadCraft 0.8.2
const units = (c) => { const d = doc(); const f = mmPerUnit(d); return { f, k: (c.project.unit === 'in' ? 25.4 : 1) / f }; }; // k: drawing units per job unit
Room area in the list
64-bit
The floor area shown beside each closed polyline offered as a room.
area = |Σ (xᵢ·yᵢ₊₁ − xᵢ₊₁·yᵢ)| ÷ 2 ÷ 1e6 m²; shown as round(area × 100) ÷ 100
The shoelace sum of the millimeter points about the drawing origin, so a room far from the origin subtracts large products. Arcs count as their chords. The label is rounded to 0.01 m².
The position and width of each door or window block near the room.
w = max(box width, box height) × f, limited to 300…3000 mm; position = insertion point × f
The width is meant to come from the block reference's bounding box, but the code tests b.maxx and geom.js names the field maxX, so the test never passes and every opening gets the 900 mm default. The position is the insertion point times the unit factor.
addons/cabinetcraft/host.js
lines 125 to 127
CadCraft 0.8.2
let w = 900;
try { const b = entityBounds(e, d.blocks); if (b && Number.isFinite(b.maxx)) w = Math.max(b.maxx - b.minx, b.maxy - b.miny) * f; } catch { /* default width */ }
openings.push({ kind, x: e.x * f, y: e.y * f, w: Math.min(3000, Math.max(300, w)) });
Where the sheets are drawn
64-bit
The point in the drawing where the shop drawing sheets start.
intended: x = right edge of what is drawn + 0.3 × tallest sheet; y = its top edge
The extents are read as b.maxx and b.maxy, which geom.js does not set, so nothing is found and the function returns (0, 0) whenever no position was given.
addons/cabinetcraft/host.js
lines 62 to 70
CadCraft 0.8.2
function origin(c, res) {
if (Number.isFinite(state.x) && Number.isFinite(state.y)) return { x: state.x, y: state.y };
// right of what is in the drawing already (not our own earlier sheets: they are replaced), 5 sheets' gap away
let x1 = -Infinity, y1 = -Infinity;
const key = keyOf(c), mine = new Set(ours(doc(), key).map((e) => e.id));
for (const e of doc().model.values()) { if (mine.has(e.id)) continue; try { const b = entityBounds(e, doc().blocks); if (b && Number.isFinite(b.maxx)) { x1 = Math.max(x1, b.maxx); y1 = Math.max(y1, b.maxy); } } catch { /* unbounded */ } }
const maxH = Math.max(0, ...res.sheets.map((s) => s.h));
return Number.isFinite(x1) ? { x: x1 + maxH * 0.3, y: y1 } : { x: 0, y: 0 };
}
Cabinet solids for the 3D tab
64-bit
The faces of each cabinet as polygons in drawing units.
point = (x ÷ f, y ÷ f, z ÷ f)
Each coordinate in millimeters is divided once by the millimeters per drawing unit. The faces are the bottom, the top and one rectangle per side of every prism; no rounding.
addons/cabinetcraft/host.js
lines 91 to 101
CadCraft 0.8.2
function solidSpec(el, f) {
const polys = [];
const P = (x, y, z) => [x / f, y / f, z / f];
for (const p of el.pieces) {
const pts = p.pts, n = pts.length;
polys.push(pts.slice().reverse().map(([x, y]) => P(x, y, p.z0)));
polys.push(pts.map(([x, y]) => P(x, y, p.z1)));
for (let i = 0; i < n; i++) { const j = (i + 1) % n; polys.push([P(pts[i][0], pts[i][1], p.z0), P(pts[j][0], pts[j][1], p.z0), P(pts[j][0], pts[j][1], p.z1), P(pts[i][0], pts[i][1], p.z1)]); }
}
return { name: el.name, polys, color: el.pieces[0] ? el.pieces[0].color : '#c9c2b4', tag: 'CabinetCraft' };
}
In detail
Method
Millimeters per drawing unit = meters per unit ÷ 0.001; bulge to center and radius; shoelace area.
Number types
64-bit numbers; room points are arrays [x, y] in millimeters.
Tolerances
Arc chords of at most 15 degrees. The room area in the list is rounded to 0.01 m². Opening widths are limited to 300…3000 mm.
Limits
A curved wall of the room is read as chords. The opening width and the sheet origin do not use the drawing's extents as written (see the open item).
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.