Normal per corner
32-bit, display onlyA unit normal for every corner of every face.
n = normalize(Σ N_f) over the faces of a smooth group
The unnormalized Newell vectors (length twice the face area) are summed, so larger faces weigh more; the sum is normalized and stored in a Float32Array. A custom 'normal' layer is copied instead when the mesh has one.
/**
* A unit normal per corner (Float32Array, 3 per half-edge slot): the 'normal' layer when there is one, else the faces
* round each vertex averaged by area, split where an edge is sharp (FL_SHARP) — or every corner its face's own normal
* when `flat`.
*/
cornerNormals({ flat = false } = {}) {
const out = new Float32Array(this.nh * 3);
const custom = this.attrs.get('normal');
const fn = new Float64Array(this.nf * 3), t = [0, 0, 0];
for (let f = 0; f < this.nf; f++) { if (this.faceDead(f)) continue; this.faceNormalRaw(f, t); fn[f * 3] = t[0]; fn[f * 3 + 1] = t[1]; fn[f * 3 + 2] = t[2]; }
const hF = this.hF, hFl = this.hFl;
if (custom && custom.domain === 'corner' && custom.size === 3) {
for (let h = 0; h < this.nh; h++) { if (hFl[h] & FL_DEAD) continue; for (let k = 0; k < 3; k++) out[h * 3 + k] = custom.col.a[h * 3 + k]; }
return out;
}
const unit = (o, x, y, z) => { const l = Math.hypot(x, y, z) || 1; out[o] = x / l; out[o + 1] = y / l; out[o + 2] = z / l; };
if (flat) {
for (let h = 0; h < this.nh; h++) { if (hFl[h] & FL_DEAD) continue; const f = hF[h] * 3; unit(h * 3, fn[f], fn[f + 1], fn[f + 2]); }
return out;
}
for (let v = 0; v < this.nv; v++) {
if (this.vertDead(v)) continue;
const n = this._walk(v), r = this._ring;
if (!n) continue;
// start a group just after a sharp edge when the fan is closed, so no group wraps round the start
let s = 0;
const closed = this.hTw[r[n - 1]] >= 0;
if (closed) for (let i = 0; i < n; i++) if (hFl[r[i]] & FL_SHARP) { s = (i + 1) % n; break; }
let i0 = 0;
while (i0 < n) {
let x = 0, y = 0, z = 0, i1 = i0;
for (;;) {
const h = r[(s + i1) % n], f = hF[h] * 3;
x += fn[f]; y += fn[f + 1]; z += fn[f + 2];
i1++;
if (i1 >= n || hFl[h] & FL_SHARP) break;
}
for (let i = i0; i < i1; i++) unit(r[(s + i) % n] * 3, x, y, z);
i0 = i1;
}
}
return out;
}