Resources Numerical accuracy Calculation policy

3D solid commands: sizes, paths and reported volume

Turns the heights, angles, outlines and paths typed or picked for EXTRUDE, SWEEP and REVOLVE into the numbers the solid is built from, and works out the volume the command line prints.

How the precision is kept

Typed sizes are 64-bit numbers in drawing units and are multiplied once by the model's meters-per-unit factor k before a solid is made. A sweep path that contains arcs is replaced by chords of at most 7.5 degrees, so the swept solid follows a polygon and not the arc. The printed volume is the 64-bit sum of the solids' volumes divided by k³; only its text is rounded, to 4 decimals.

Calculations under this policy

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

commands3d.js CadCraft 0.8.2 64-bit 5 calculations Reviewed

Tests bin/test-cadcraft-3d.mjs

Volume printed after a 3D command

64-bit

The total volume of the solids a command just made, in drawing units cubed.

V = Σ volumeᵢ ÷ (k × k × k)

The solids' volumes are added in list order as 64-bit numbers and divided once by k × k × k. The message receives both the full value and a text rounded to 4 decimals by fmt; nothing rounded is stored.

  • commands3d.js
  • lines 39 to 43
  • CadCraft 0.8.2
function volumeText(m3, iids) {
  const k = m3.k, s = solidsOf(m3).filter((x) => iids.includes(x.iid));
  const v = s.reduce((a, x) => a + (x.volume || 0), 0) / (k * k * k);
  return t('cmd3d.volume', { n: v, v: fmt(v) });
}

Number text in 3D command messages

64-bit

How heights, radii, angles and volumes are written on the command line.

|n| < 1e-10 → 0; otherwise n rounded to 4 decimals

toFixed(4) rounds to 4 decimals and trailing zeros are dropped; a magnitude below 1e-10 prints as 0. The cut is absolute, so a volume smaller than 0.00005 units³ prints as 0.

  • commands3d.js
  • line 29
  • CadCraft 0.8.2
const fmt = (n) => (Math.abs(n) < 1e-10 ? '0' : Number(n.toFixed(4)).toString());

Sweep path from a 2D object

64-bit

The points a profile is swept along, taken from a line, polyline, arc, circle or ellipse.

n = max(2, ⌈sweep ÷ (π ÷ 24)⌉); pᵢ = c + r × (cos aᵢ, sin aᵢ), aᵢ = a₀ ± sweep × i ÷ n

Each arc becomes n chords of at most 7.5 degrees, so the path is an inscribed polygon. A point within 1e-9 drawing units (absolute) of the previous point is dropped. The sweep is reduced modulo 2π and a zero sweep is taken as a full turn.

  • commands3d.js
  • lines 145 to 158
  • CadCraft 0.8.2
/** A path from a 2D object: its points (arcs in 7.5° steps) and whether it closes. */
function pathOf(e) {
  if (!e || !['line', 'poly', 'arc', 'circle', 'ellipse'].includes(e.type)) return null;
  if (e.type === 'circle' || e.type === 'ellipse') { const o = outline2d(e); return o.error ? null : { pts: o.pts, closed: !o.error && !(e.type === 'ellipse' && Math.abs(e.t1 - e.t0) < 2 * Math.PI - 1e-6) }; }
  const pts = [];
  const push = (x, y) => { const l = pts[pts.length - 1]; if (!l || Math.hypot(l[0] - x, l[1] - y) > 1e-9) pts.push([x, y]); };
  for (const q of G.prims(e, null)) {
    if (q.k === 'L') { push(q.x1, q.y1); push(q.x2, q.y2); continue; }
    const sw = ((q.a1 - q.a0) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) || 2 * Math.PI, n = Math.max(2, Math.ceil(sw / (Math.PI / 24)));
    const from = q.rev ? q.a1 : q.a0, dir = q.rev ? -1 : 1;
    for (let i = 0; i <= n; i++) { const a = from + dir * sw * i / n; push(q.cx + q.r * Math.cos(a), q.cy + q.r * Math.sin(a)); }
  }
  return pts.length >= 2 ? { pts, closed: !!e.closed } : null;
}

Extrude: outline, height and taper to model units

64-bit

Converts the picked outline and the typed height and taper angle into the numbers the solid is made from.

ring = (x × k, y × k); height = h × k; taper = degrees × π ÷ 180

One 64-bit multiplication per coordinate and per height. The taper is converted with Math.PI ÷ 180 in the order taper × π ÷ 180.

  • commands3d.js
  • lines 120 to 121
  • CadCraft 0.8.2
const ring = s.ring.map(([x, y]) => [x * k, y * k]);
try { made.push({ id: s.id, iid: taper ? makeTapered(m, ring, 0, h * k, taper * Math.PI / 180) : makePrism(m, ring, 0, h * k, 'Extrusion') }); } catch (err) { failed.push(err.message); }

Revolve angle

64-bit

The angle a profile is turned through; no angle means a full turn.

θ = 2π if |angle| < 1e-9, else angle

The angle is kept in radians as typed or picked. The 1e-9 radian cut is absolute.

  • commands3d.js
  • line 205
  • CadCraft 0.8.2
const th = Math.abs(ang) < 1e-9 ? 2 * Math.PI : ang;

In detail

Method
Scale by k; arcs of a path sampled at equal angle steps of at most π ÷ 24; volume = Σ volume ÷ k³.
Number types
64-bit numbers throughout. Rings and paths are plain arrays of [x, y] pairs.
Tolerances
A typed height below 1e-9 in magnitude is refused as zero and a revolve angle below 1e-9 radians means a full turn; path points closer than 1e-9 drawing units to the previous one are dropped; an ellipse counts as closed when its parameter range is within 1e-6 of 2π; a taper is accepted only below 89.5 degrees. All are absolute, not scaled to the drawing.
Limits
Arcs in a sweep path are faceted in 7.5 degree steps. Messages show 4 decimals.

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