Resources Numerical accuracy Calculation policy

Export to IFC

The points, directions, placements and level elevations written when a model is exported as an IFC4 file.

How the precision is kept

Geometry is computed in 64-bit and every real number is written with 9 decimals. For coordinates in meters that is 1 nanometer, but for the unit direction vectors of a placement it is 9 significant digits. Points whose 9-decimal text is equal are written once.

Open item. Write placement directions with 15 or more significant digits instead of 9 decimals.

Calculations under this policy

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

m3d/io.js CadCraft 0.8.2 64-bit 6 calculations Reviewed

Tests None found by the review

IFC number format

64-bit

How every coordinate, direction component and elevation is written.

text = x rounded to 9 decimals

toFixed(9) rounds to 1e-9 in absolute terms: 1 nanometer for a coordinate in meters, 9 digits for a component of a unit direction. A rounded value that prints in exponent form is written instead from the unrounded number with 10 significant digits.

  • m3d/io.js
  • line 1813
  • CadCraft 0.8.2
const real = (x) => { let v = +(+x).toFixed(9); if (Object.is(v, -0) || Math.abs(v) < 1e-12) v = 0; let s = String(v); if (s.includes('e')) s = (+x).toExponential(9).replace('e', 'E'); if (!/[.E]/.test(s)) s += '.'; return s; };

Shared points

64-bit

The corner points of the faces, each written once.

Two corners become one IfcCartesianPoint when their coordinates agree as text at 9 decimals.

  • m3d/io.js
  • line 1865
  • CadCraft 0.8.2
const P = (p) => { const k2 = p.map((x) => (+x).toFixed(9)).join(','); if (!pts.has(k2)) pts.set(k2, pt(p)); return pts.get(k2); };

Is a placement rigid

64-bit

Decides whether a group is exported in its own coordinates with a placement, or in world coordinates.

|column| = 1 ± 1e-9; columns square within 1e-9; determinant > 0

A placement with any scale, mirror or skew beyond these tolerances is not written as an IFC placement; its geometry is multiplied out to world coordinates instead.

  • m3d/io.js
  • lines 1815 to 1823
  • CadCraft 0.8.2
function rigid(m) {
  const cols = [[m[0], m[1], m[2]], [m[4], m[5], m[6]], [m[8], m[9], m[10]]];
  if (Math.abs(m[3]) + Math.abs(m[7]) + Math.abs(m[11]) > 1e-12 || Math.abs(m[15] - 1) > 1e-12) return false;
  for (let a = 0; a < 3; a++) {
    if (Math.abs(len(cols[a]) - 1) > 1e-9) return false;
    for (let b = a + 1; b < 3; b++) if (Math.abs(dot(cols[a], cols[b])) > 1e-9) return false;
  }
  return det3(m) > 0;
}

Placement of an element

64-bit

Where each exported element sits relative to its level.

location = (t_x, t_y, t_z − level elevation); axis = third column; ref direction = first column

The location is the matrix translation with the level's elevation subtracted from z. The axis and reference direction are the normalized third and first columns, each written to 9 decimals, so the rotation in the file carries 9 digits.

  • m3d/io.js
  • lines 1875 to 1877
  • CadCraft 0.8.2
// placed relative to its storey, so it stays where it is in the world
const axes = m ? L(`IFCAXIS2PLACEMENT3D(${pt([m[12], m[13], m[14] - lv.elevation])},${dir(norm([m[8], m[9], m[10]]))},${dir(norm([m[0], m[1], m[2]]))})`)
  : lv.elevation ? L(`IFCAXIS2PLACEMENT3D(${pt([0, 0, -lv.elevation])},$,$)`) : null;

Level of an element

64-bit

Which level an element is listed under.

the highest level with elevation ≤ z_min + 1e-6

z_min is the lowest corner of the element in world coordinates; the comparison has an absolute slack of 1e-6 m.

  • m3d/io.js
  • lines 1854 to 1855
  • CadCraft 0.8.2
/** The storey an element whose lowest point is z belongs to: the highest one at or below it (the lowest if none). */
const levelOf = (z) => { let s = levels[0]; for (const l of levels) if (l.elevation <= z + 1e-6) s = l; return s; };

Precision stated in the file

64-bit

The precision value the IFC file declares for its geometry.

The geometric representation context is written with a fixed precision of 1.E-05, while coordinates are written to 9 decimals.

  • m3d/io.js
  • line 1835
  • CadCraft 0.8.2
const ctx = L(`IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.E-05,${worldAxes},$)`);

In detail

Method
One faceted shape per top-level group or component, in the group's own coordinates when its placement is rigid (placed by an IfcAxis2Placement3D), otherwise in world coordinates.
Number types
64-bit numbers, written with toFixed(9).
Tolerances
Rigid placement: column lengths within 1e-9 of 1, columns square within 1e-9, bottom row within 1e-12. Level of an element: elevation ≤ lowest point + 1e-6 m. The file's geometric context states a precision of 1e-5.
Limits
A placement's axis directions are rounded to 9 decimals, so a point D meters from the placement's origin can shift by an amount on the order of D × 1e-9 m when the file is read. The precision stated in the file (1e-5) is coarser than the 1e-9 the coordinates are written to.

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