Resources Numerical accuracy Calculation policy

Assembly drawing sheet and mesh exports

Lays out the assembly drawing (view scale, positions on the sheet, bill of materials masses) and writes the placed assembly as one mesh for STL or as a 3MF file.

How the precision is kept

Sheet coordinates are 64-bit. The mesh exports start from each part's Float32Array positions; the STL mesh is transformed in 64-bit and stored back in a Float32Array. The 3MF writer prints every number, including each instance's placement matrix, with 9 significant digits.

Open item. Write the 3MF item transform with 17 significant digits; only the vertex coordinates are limited to 32-bit by their source.

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.

asm-drawing.js PartCraft 0.8.1 32-bit in part 5 calculations Reviewed

Tests tools/partcraft-test/test-assembly-tools.mjs

View scale and position on the sheet

64-bit

Picks the drawing scale and maps view coordinates to sheet millimeters.

scale = first s with w × s ≤ 0.78 × frame width and h × s ≤ 0.78 × frame height; sheet = origin + (u − u₀) × scale

The scale is one of the fixed values in drawing.js (10 down to 1/1000); when none fits, the smallest is used even though the view then overflows the frame. The mapping subtracts the view's minimum before scaling, in 64-bit.

  • asm-drawing.js
  • lines 81 to 84
  • PartCraft 0.8.1
const bw = v.box.u1 - v.box.u0, bh = v.box.v1 - v.box.v0;
const scale = SCALES.find((s) => bw * s <= (vx1 - vx0) * 0.78 && bh * s <= (vy1 - vy0) * 0.78) || SCALES[SCALES.length - 1];
const ox = vx0 + ((vx1 - vx0) - bw * scale) / 2, oy = vy0 + ((vy1 - vy0) - bh * scale) / 2;
const toSheet = (u, w) => [ox + (u - v.box.u0) * scale, oy + (w - v.box.v0) * scale];

Mass text in the bill of materials

64-bit

Formats a part's mass for the drawing's parts table.

g ≥ 1000: kg with 3 decimals; else g with 2 decimals

Rounds for the printed table only: kilograms to 0.001 and grams to 0.01, with trailing zeros removed. The mass value itself is not changed.

  • asm-drawing.js
  • line 34
  • PartCraft 0.8.1
const fmtMass = (g) => (g === null ? '' : g >= 1000 ? String(Number((g / 1000).toFixed(3))) + ' kg' : String(Number(g.toFixed(2))) + ' g');

Assembly as one mesh

32-bit in part

Moves every part's mesh to its place in the assembly and joins them, for STL export and the 3D-printing check.

p′ = M × p for each vertex

Reads each vertex from the part mesh's Float32Array, applies the 64-bit placement matrix, and stores the result in a new Float32Array. A coordinate far from the origin keeps about 7 significant digits.

  • asm-drawing.js
  • lines 132 to 144
  • PartCraft 0.8.1
/** items: [{ mesh, matrix }] → { positions, indices } in world coordinates. */
export function assemblyMesh(items) {
  let nv = 0, ni = 0;
  for (const it of items) { nv += it.mesh.positions.length; ni += it.mesh.indices.length; }
  const positions = new Float32Array(nv), indices = new Uint32Array(ni);
  let pv = 0, pi = 0;
  for (const it of items) {
    const P = it.mesh.positions, base = pv / 3;
    for (let i = 0; i < P.length; i += 3) { const q = V.applyMat(it.matrix, [P[i], P[i + 1], P[i + 2]]); positions[pv++] = q[0]; positions[pv++] = q[1]; positions[pv++] = q[2]; }
    for (const k of it.mesh.indices) indices[pi++] = base + k;
  }
  return { positions, indices };
}

Numbers written to 3MF

64-bit

Turns a coordinate or a matrix entry into the text stored in the 3MF file.

text = toPrecision(9) of v; |v| < 1e-12 → 0

Every number is rounded to 9 significant digits. That is enough to round-trip a 32-bit vertex coordinate, but it also rounds the 64-bit placement matrix. Values smaller than 1e-12 in absolute size are written as 0.

  • asm-drawing.js
  • line 147
  • PartCraft 0.8.1
const num = (v) => { const r = Math.abs(v) < 1e-12 ? 0 : v; return Number(r.toPrecision(9)).toString(); };

Instance placement in 3MF

64-bit

Converts each instance's placement from Y-up to Z-up and writes it as the item transform.

T = Y_TO_Z × M × Z_TO_Y; 12 entries written row by row

The axis-swap matrices hold only 0, 1 and −1, so the two products add no rounding beyond sign changes. The 12 entries then pass through the 9-digit text conversion.

  • asm-drawing.js
  • lines 171 to 174
  • PartCraft 0.8.1
// Z-up placement: Y_TO_Z · M · Z_TO_Y; 3MF writes the 3×4 as m00 m01 m02 m10 … m30 m31 m32 (row vectors)
const m = V.matMul(Y_TO_Z, V.matMul(it.matrix, Z_TO_Y));
const t = [m[0], m[1], m[2], m[4], m[5], m[6], m[8], m[9], m[10], m[12], m[13], m[14]].map(num).join(' ');
out.push(`  <item objectid="${ids.get(it.pid)}" transform="${t}"/>\n`);

In detail

Method
The view scale is the first of the fixed scales in drawing.js at which the view fits in 78 % of the frame. 3MF placement is Y_TO_Z × M × Z_TO_Y written as a 3 × 4 transform.
Number types
64-bit numbers for the sheet; Float32Array for mesh positions in and out; decimal text with 9 significant digits in the 3MF file.
Tolerances
In the 3MF text a value with absolute size below 1e-12 is written as 0.
Limits
STL coordinates are 32-bit. The 3MF placement transform is rounded to 9 significant digits although the placement itself is 64-bit.

Approach: 32-bit step, flagged

One step stores or exchanges 32-bit values that reach a number a person reads. It is listed here with its open item, and the step is either moved to 64-bit or the app will warn where the number is read.

Every policy that takes this approach