Gives each app an element's triangles and the matrix that places them, in meters with Y up, moved near the origin.
How the precision is kept
The placement matrices are 64-bit: the file's placement, a scale by the length unit, an axis turn and a shift to the first element's center. The vertex data itself is returned as the mesh's interleaved Float32Array, in the file's length unit. Code that measures from these vertices (ifc-util.js productTriangles reads them) starts from 32-bit positions.
Open item. Offer the positions in a Float64Array beside the 32-bit buffer, and have measuring code read that one.
Calculations under this policy
4 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.
ifc/api.jsBimCraft 0.8.1 32-bit in part4 calculationsReviewed
Tests tools/ifc-test/test.mjs
Vertex data of one geometry
32-bit in part
Returns a geometry's positions and normals for drawing and for measuring.
The data comes from mesh.interleaved() (geo-mesh.js), which copies the mesh's 64-bit positions into a Float32Array. Every consumer of GetVertexArray, including the triangle reader used for quantities, receives about 7 digits per coordinate.
The matrix that takes a geometry's vertices to model space in meters, Y up.
M = C × Y-up × scale(u) × placement
Three 4 × 4 products of 64-bit numbers. The axis turn holds only 0, 1 and −1, so it adds no rounding; the unit scale multiplies by the file's length unit factor.
Finds the translation that brings the model near the origin.
c = (box min + box max) ÷ 2 of the first element; C = translate(−(Y-up × scale(u) × placement × c))
The box center and the matrix products are 64-bit. The shift is whatever the first element with geometry gives, not a rounded value, so undoing it needs the stored matrix; a matrix passed in by the caller is used as given when all sixteen values are finite.
ifc/api.js
lines 261 to 279
BimCraft 0.8.1
ensureC(mid) {
const m = this.m(mid);
if (m.C) return m.C;
m.C = I4();
if (!m.toOrigin) return m.C;
const geo = this.geometry(mid);
for (const p of geo.products()) {
const pcs = geo.pieces(p);
if (!pcs.length) continue;
// move the first element's centre to the origin (metres, Y up), as models far from it lose float precision
const b = geo.mesh(pcs[0].geomId).bounds();
if (!b) continue;
const c = [(b.min[0] + b.max[0]) / 2, (b.min[1] + b.max[1]) / 2, (b.min[2] + b.max[2]) / 2];
const w = mul(this.toYupMetres(mid, pcs[0].matrix), [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, c[0], c[1], c[2], 1]);
m.C = translation(-w[12], -w[13], -w[14]);
break;
}
return m.C;
}
File units to meters, Z up to Y up
64-bit
Scales a placement by the file's length unit and turns its axes.
M′ = Y-up × scale(u) × M
u is the length unit factor read by geo-ctx.js (meters per file unit). The scale is one multiplication per matrix entry, so a factor that has no exact binary value brings its rounding in here.
flat transformation = C × Y-up × scale(length unit) × placement. C moves the center of the first element's box to the origin when the model was opened with COORDINATE_TO_ORIGIN, or is the matrix given by the caller.
Number types
Matrices as arrays of sixteen 64-bit numbers; vertices as a Float32Array of x y z nx ny nz; indices as a Uint32Array.
Limits
The 32-bit rounding applies to each geometry's coordinates before its placement matrix, so a geometry stored far from its own origin loses more than one stored near it.
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.