Resources Numerical accuracy Calculation policy

3DM mesh import: placement and units

Brings the meshes of a .3dm file, including block placements, into one triangle list in the part's units.

How the precision is kept

Each vertex is multiplied by the 4×4 placement matrix of its block chain, divided by the homogeneous weight and scaled to millimeters when the file's unit is not one PartCraft uses. All of it is 64-bit and the result is a Float64Array.

Calculations under this policy

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

mesh3dm.js PartCraft 0.8.1 64-bit 2 calculations Reviewed

Tests tools/3dm/test.mjs

Vertex placement and unit scale

64-bit

Moves every mesh vertex to where the file places it and converts it to the part's unit.

p′ = (M · p) ÷ w × s; s = meters per file unit × 1000

One 64-bit expression per coordinate. The scale s is 1 when the file's unit is µm, mm, cm, m, in or ft, and the part then takes that unit; otherwise it is the file's meters per unit times 1000. A weight w that evaluates to 0 falls back to 1 through the || operator.

  • mesh3dm.js
  • lines 33 to 56
  • PartCraft 0.8.1
const s = unitOk ? 1 : scene.units.metres * 1000;   // other units: into millimetres
const P = [], T = [];
const facts = { objects: 0, cached: scene.report.surfaces.cached, ours: Object.values(scene.report.surfaces.ours).reduce((a, n) => a + n, 0), curves: scene.report.curves, hidden: 0 };
const hiddenLayer = (it) => it.layer >= 0 && scene.layers[it.layer] && !scene.layers[it.layer].visible;
const put = (items, M, depth) => {
  for (const it of items) {
    if (it.hidden || hiddenLayer(it)) { if (it.kind !== 'curve') facts.hidden++; continue; }
    if (it.kind === 'mesh') {
      const base = P.length / 3, p = it.positions;
      for (let i = 0; i < p.length; i += 3) {
        const x = p[i], y = p[i + 1], z = p[i + 2];
        const w = M[12] * x + M[13] * y + M[14] * z + M[15] || 1;
        P.push((M[0] * x + M[1] * y + M[2] * z + M[3]) / w * s, (M[4] * x + M[5] * y + M[6] * z + M[7]) / w * s, (M[8] * x + M[9] * y + M[10] * z + M[11]) / w * s);
      }
      for (let i = 0; i < it.indices.length; i++) T.push(base + it.indices[i]);
      if (T.length / 3 > MAX_TRIS) throw new Mesh3dmError('mesh.err.too_many', { max: MAX_TRIS });
      facts.objects++;
    } else if (it.kind === 'instance' && depth < MAX_DEPTH) {
      const b = scene.blocks.get(it.block);
      if (b) put(b.items, mul(M, it.xform), depth + 1);
    }
  }
};
put(scene.items, I, 0);

Combined block placement

64-bit

Multiplies the placement of a block by the placement of the block inside it.

(A · B)ᵢⱼ = Σₖ Aᵢₖ × Bₖⱼ

A plain 4×4 product in 64-bit, each entry summed in index order. One product is added per block level, at most 16 levels.

  • mesh3dm.js
  • lines 21 to 25
  • PartCraft 0.8.1
const mul = (a, b) => {
  const r = new Array(16);
  for (let i = 0; i < 4; i++) for (let j = 0; j < 4; j++) { let s = 0; for (let k = 0; k < 4; k++) s += a[i * 4 + k] * b[k * 4 + j]; r[i * 4 + j] = s; }
  return r;
};

In detail

Method
A homogeneous 4×4 transform per vertex, with the matrices of nested blocks multiplied together, then one unit scale factor.
Number types
64-bit numbers collected in plain arrays, then a Float64Array of positions and a Uint32Array of indices.
Limits
A homogeneous weight of exactly 0 is replaced by 1. Nested blocks are followed to a depth of 16. At most 3,000,000 triangles.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach