Feature edges of one element
32-bit, display onlyThe 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.
/** 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;
}