← cad viewer

CAD viewer · #7

True B-rep edges — from topology, not heuristics

The edge outlines in /viewer have been quietly wrong since day one. EdgesGeometry draws a line wherever adjacent triangles bend past a threshold angle — a rendering heuristic, not a fact about the model. On a fillet it can draw "edges" that don't exist in the CAD data at all.

This milestone replaces the heuristic with the real thing: face boundaries. Where one B-rep face ends and another begins, there is an edge. Everything else is shading.

The observation that makes it cheap

We knew occt-import-js returns brep_faces — triangle ranges per face. What we found by counting: OCCT's tessellation keeps each face's vertices separate. An edge shared between two triangles of the same face shows up twice in the index buffer; an edge on a face's boundary shows up exactly once:

for (let t = 0; t < index.length / 3; t++)
  for (let e = 0; e < 3; e++) {
    const a = index[t * 3 + e], b = index[t * 3 + ((e + 1) % 3)];
    const k = a < b ? a * 2 ** 32 + b : b * 2 ** 32 + a;
    edgeCount.set(k, (edgeCount.get(k) || 0) + 1);
  }
// count === 1 → B-rep edge

No geometry math, no angle thresholds, no adjacency tables — one pass over the index buffer, done in the web worker next to the parse. The edges it finds are topologically the B-rep's own edges, sampled at tessellation resolution.

Honest caveats

  • Seams are invisible. A closed cylinder's seam edge lives inside one face — both sides belong to it — so face-boundary counting can't see it. Every edge between faces is found; the edge inside a face is the one case that isn't. Acceptable: the overwhelming majority of visible CAD edges are face boundaries.
  • Each edge is drawn twice — once per adjacent face, since they don't share vertices. A few hundred extra segments, visually identical.
  • The 64-bit packed key trick (a << 32 | b) overflows doubles past ~2M vertices per mesh — the worker detects that and falls back to BigInt keys. Rare, but silent corruption is worse than slow.

What changed, visually

Fillets and blends now show their true boundaries only. The AS1 assembly went from heuristic edges (with occasional false positives on curved surfaces) to 5,260 boundary segments that match the part drawings.

Next: WebGPU — the renderer swap that was deferred since post #1.