Resources Numerical accuracy Calculation policy

Element outlines in the IFC view

Finds the edge lines drawn on each element in the IFC view.

How the precision is kept

Edges are found per element from the 32-bit mesh: an edge is drawn where two triangles meet at more than 30° or where it belongs to one triangle only. The result is a Float32Array of line segments for the GPU. Measure takes its snap corners from the same edge finder.

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.

ifc-vis.js BimCraft 0.8.1 32-bit, display only 2 calculations Reviewed

Tests bin/test-bimcraft.mjs

Feature edges of one element

32-bit, display only

The edges of an element where its faces meet at more than the feature angle, plus its free edges.

edge drawn when n₁ · n₂ ≤ cos(angle), or when only one triangle uses it

Reads 32-bit positions and matches vertices after rounding to 0.0001 m. A triangle with zero area has no normal and adds no edges.

  • ifc-vis.js
  • lines 110 to 138
  • BimCraft 0.8.1
/** One element's feature edges (triangles t0 … t0 + tn of an indexed batch): appended to `out` as x y z x y z. */
export function runEdges(P, I, t0, tn, angle = 30, out = []) {
  const cos = Math.cos(angle * Math.PI / 180), q = 1e4;
  const n = [0, 0, 0];
  {
    const seen = new Map();   // "a|b" (quantised vertices, sorted) → [i0, i1, nx, ny, nz]
    for (let t = t0, tEnd = t0 + tn; t < tEnd; t++) {
      const a = I[t * 3] * 3, b = I[t * 3 + 1] * 3, c = I[t * 3 + 2] * 3;
      const ux = P[b] - P[a], uy = P[b + 1] - P[a + 1], uz = P[b + 2] - P[a + 2];
      const vx = P[c] - P[a], vy = P[c + 1] - P[a + 1], vz = P[c + 2] - P[a + 2];
      n[0] = uy * vz - uz * vy; n[1] = uz * vx - ux * vz; n[2] = ux * vy - uy * vx;
      const l = Math.hypot(n[0], n[1], n[2]);
      if (!(l > 0)) continue;   // a degenerate triangle has no normal and no edges of its own
      n[0] /= l; n[1] /= l; n[2] /= l;
      const v = [a, b, c], h = v.map((o) => Math.round(P[o] * q) + ',' + Math.round(P[o + 1] * q) + ',' + Math.round(P[o + 2] * q));
      for (let k = 0; k < 3; k++) {
        const i0 = v[k], i1 = v[(k + 1) % 3], h0 = h[k], h1 = h[(k + 1) % 3];
        if (h0 === h1) continue;
        const key = h0 < h1 ? h0 + '|' + h1 : h1 + '|' + h0;
        const other = seen.get(key);
        if (!other) { seen.set(key, [i0, i1, n[0], n[1], n[2]]); continue; }
        seen.delete(key);
        if (other[2] * n[0] + other[3] * n[1] + other[4] * n[2] <= cos) out.push(P[i0], P[i0 + 1], P[i0 + 2], P[i1], P[i1 + 1], P[i1 + 2]);
      }
    }
    for (const [i0, i1] of seen.values()) out.push(P[i0], P[i0 + 1], P[i0 + 2], P[i1], P[i1 + 1], P[i1 + 2]);
  }
  return out;
}

Edge segments of a batch

32-bit, display only

Collects every element's edges into one array of line segments with a run per element.

The segments are stored in a Float32Array and the per-element runs in Uint32Array, for drawing.

  • ifc-vis.js
  • lines 93 to 108
  • BimCraft 0.8.1
/**
 * Edge lines of each element on its own (an edge where the faces meet at more than `angle` degrees, or a free edge):
 * the drafting look, and an element's edges hide with it. Vertices are matched by position (0.1 mm in metres), as
 * three.js EdgesGeometry does, but never across two elements, so element outlines stay. → segments grouped by element.
 */
export function batchEdges(batch, angle = 30) {
  const P = batch.positions, I = batch.indices, E = batch.elems;
  const out = [], ids = [], s0 = [], sn = [];
  for (let r = 0; r < E.ids.length; r++) {
    const start = out.length / 6;
    runEdges(P, I, E.t0[r], E.tn[r], angle, out);
    const count = out.length / 6 - start;
    if (count) { ids.push(E.ids[r]); s0.push(start); sn.push(count); }
  }
  return { positions: Float32Array.from(out), ids: Uint32Array.from(ids), s0: Uint32Array.from(s0), sn: Uint32Array.from(sn) };
}

In detail

Method
Triangle normals by cross product; vertices matched by rounding positions to 0.0001 m; an edge kept when the dot product of the two unit normals is at most cos 30°.
Number types
Float32Array positions in and out; normals and the dot product in 64-bit.
Tolerances
Vertex matching at 0.0001 m (absolute). Feature angle 30° by default.

Approach: Display only

Computed to draw the screen, often in 32-bit on the graphics processor. No number a person reads, saves or exports comes from it.

Every policy that takes this approach