Resources Numerical accuracy Calculation policy

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.

addons/cabinetcraft/host.js CadCraft 0.8.2 64-bit 7 calculations Reviewed

Tests tools/addons-test/browser-cabinetcraft-cadcraft.mjs

Millimeters in one drawing unit

64-bit

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.

  • addons/cabinetcraft/host.js
  • lines 18 to 20
  • CadCraft 0.8.2
const UNIT_M = { '': 0.001, inches: 0.0254, feet: 0.3048, millimeters: 0.001, centimeters: 0.01, meters: 1, yards: 0.9144 };
/** Millimetres in one drawing unit. */
export const mmPerUnit = (doc) => (UNIT_M[String(doc && doc.meta && doc.meta.units || '')] ?? 0.001) / 0.001;

Room outline from a polyline

64-bit

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

  • addons/cabinetcraft/host.js
  • lines 113 to 114
  • CadCraft 0.8.2
const area = (p) => Math.abs(p.reduce((s, a, i) => { const b = p[(i + 1) % p.length]; return s + a[0] * b[1] - b[0] * a[1]; }, 0) / 2) / 1e6;
return found.slice(0, 40).map((x, i) => ({ id: String(i), label: t('cad.room.row', { layer: x.e.layer, n: x.pts.length, area: Math.round(area(x.pts) * 100) / 100 }) + (x.sel ? ' — ' + t('cad.room.selected') : '') }));

Door and window openings read from blocks

64-bit

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.

Every policy that takes this approach