Resources Numerical accuracy Calculation policy

Issue viewpoints in BCF files

Converts cameras and section planes between the viewer and the model's own coordinates, and writes and reads them in BCF issue files.

How the precision is kept

Points and directions go through a 4 × 4 matrix and its inverse in 64-bit; the change between Y up and Z up is a swap and a sign. On writing, every number is rounded to six decimals. On reading, numbers are taken at full 64-bit digits and directions are normalized again.

Open item. Write direction components with all their digits; six decimals suit positions in meters but are coarse for unit vectors.

Calculations under this policy

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

bcf.js BimCraft 0.8.1 64-bit 8 calculations Reviewed

Tests bin/test-bimcraft.mjs

Inverse of a 4 × 4 matrix

64-bit

The inverse of the model's coordination matrix.

M⁻¹ = adj(M) ÷ det(M)

Cofactor expansion in 64-bit and one division by the determinant. A zero determinant gives an all-zero matrix, not an error. The determinant is a sum of products with mixed signs, so digits are lost as the matrix nears singular.

  • bcf.js
  • lines 25 to 37
  • BimCraft 0.8.1
function inv4(m) {   // column-major 4 × 4
  const a = m, o = new Array(16);
  const b00 = a[0] * a[5] - a[1] * a[4], b01 = a[0] * a[6] - a[2] * a[4], b02 = a[0] * a[7] - a[3] * a[4], b03 = a[1] * a[6] - a[2] * a[5];
  const b04 = a[1] * a[7] - a[3] * a[5], b05 = a[2] * a[7] - a[3] * a[6], b06 = a[8] * a[13] - a[9] * a[12], b07 = a[8] * a[14] - a[10] * a[12];
  const b08 = a[8] * a[15] - a[11] * a[12], b09 = a[9] * a[14] - a[10] * a[13], b10 = a[9] * a[15] - a[11] * a[13], b11 = a[10] * a[15] - a[11] * a[14];
  const det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
  const d = det ? 1 / det : 0;
  o[0] = (a[5] * b11 - a[6] * b10 + a[7] * b09) * d; o[1] = (a[2] * b10 - a[1] * b11 - a[3] * b09) * d; o[2] = (a[13] * b05 - a[14] * b04 + a[15] * b03) * d; o[3] = (a[10] * b04 - a[9] * b05 - a[11] * b03) * d;
  o[4] = (a[6] * b08 - a[4] * b11 - a[7] * b07) * d; o[5] = (a[0] * b11 - a[2] * b08 + a[3] * b07) * d; o[6] = (a[14] * b02 - a[12] * b05 - a[15] * b01) * d; o[7] = (a[8] * b05 - a[10] * b02 + a[11] * b01) * d;
  o[8] = (a[4] * b10 - a[5] * b08 + a[7] * b06) * d; o[9] = (a[1] * b08 - a[0] * b10 - a[3] * b06) * d; o[10] = (a[12] * b04 - a[13] * b02 + a[15] * b00) * d; o[11] = (a[9] * b02 - a[8] * b04 - a[11] * b00) * d;
  o[12] = (a[5] * b07 - a[4] * b09 - a[6] * b06) * d; o[13] = (a[0] * b09 - a[1] * b07 + a[2] * b06) * d; o[14] = (a[13] * b01 - a[12] * b03 - a[14] * b00) * d; o[15] = (a[8] * b03 - a[9] * b01 + a[10] * b00) * d;
  return o;
}

Viewer point to model point

64-bit

A camera position or a point on a section plane in the model's own coordinates, in meters with Z up.

(x, y, z) = C⁻¹ v; IFC = (x, −z, y)

The coordination matrix is inverted on every call and applied in 64-bit. The axis change is a swap and a sign, which is exact.

  • bcf.js
  • lines 41 to 42
  • BimCraft 0.8.1
/** viewer (Y up, coordinated) → IFC (Z up, metres) */
export const toIfcPoint = (C, v) => { const [x, y, z] = pt(inv4(C), v); return [x, -z, y]; };

Viewer direction to model direction

64-bit

A camera direction, up vector or section plane normal in the model's own axes.

Only the rotation part of the inverse is applied, then the vector is normalized with Math.hypot. A zero vector is left as it is.

  • bcf.js
  • line 43
  • BimCraft 0.8.1
export const toIfcDir = (C, v) => { const [x, y, z] = dr(inv4(C), v); return norm([x, -z, y]); };

Model point to viewer point

64-bit

A point read from an issue file, placed in the viewer.

v = C (x, z, −y)

The exact swap and sign first, then one matrix product in 64-bit.

  • bcf.js
  • line 44
  • BimCraft 0.8.1
export const fromIfcPoint = (C, p) => pt(C, [p[0], p[2], -p[1]]);

Model direction to viewer direction

64-bit

A direction read from an issue file, in the viewer's axes.

The rotation part of the matrix, then normalized with Math.hypot.

  • bcf.js
  • line 45
  • BimCraft 0.8.1
export const fromIfcDir = (C, d) => norm(dr(C, [d[0], d[2], -d[1]]));

Numbers written to the file

64-bit

How each coordinate, direction, field of view and scale is written in a viewpoint.

round(v × 10⁶) ÷ 10⁶

Every number written is rounded to six decimals. For a position in meters that is 0.001 mm. For a unit direction it limits the direction to about one part in a million, up to about 0.1 mm across 100 m for a section plane.

  • bcf.js
  • line 161
  • BimCraft 0.8.1
const f6 = (v) => String(Math.round(v * 1e6) / 1e6);

Numbers read from the file

64-bit

Reads the X, Y and Z of a point or direction.

Each part is read with Number() at full 64-bit digits. A missing or non-numeric part drops the whole point.

  • bcf.js
  • lines 155 to 156
  • BimCraft 0.8.1
const num = (v) => { const x = Number(v); return Number.isFinite(x) ? x : null; };
const xyz = (n) => { if (!n) return null; const v = ['X', 'Y', 'Z'].map((k) => num(txt(n, k))); return v.every((x) => x !== null) ? v : null; };

Camera read from a viewpoint

64-bit

The camera of an issue as BimCraft keeps it.

The direction and the up vector are normalized again after reading. A missing or zero field of view becomes 60 and a missing or zero scale becomes 10.

  • bcf.js
  • lines 231 to 232
  • BimCraft 0.8.1
const vp = xyz(kid(cam, 'CameraViewPoint')), d = xyz(kid(cam, 'CameraDirection')), up = xyz(kid(cam, 'CameraUpVector'));
if (vp && d && up) camera = pc ? { kind: 'perspective', viewPoint: vp, direction: norm(d), up: norm(up), fov: num(txt(pc, 'FieldOfView')) || 60 } : { kind: 'orthogonal', viewPoint: vp, direction: norm(d), up: norm(up), scale: num(txt(oc, 'ViewToWorldScale')) || 10 };

In detail

Method
General 4 × 4 inverse by cofactors; IFC (x, y, z) = (x, −z, y) of the viewer point taken back through the coordination matrix.
Number types
64-bit numbers in plain arrays; decimal text in the file.
Tolerances
None in the conversions. Written values are rounded to 1e-6.
Limits
A coordination matrix with a zero determinant gives an all-zero inverse without an error. Unit directions written to six decimals are accurate to about one part in a million.

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