Cutting a story's elements
32-bit, display onlyThe cut segments of every element the cut plane passes through, and the edges of what lies below or above.
Reads the 32-bit mesh positions and transforms them into three Float64Array of plan coordinates. An element is cut when its lowest and highest vertices straddle the cut height.
/**
* Cut every element of `storey` (a storey expressID; batch.storey) at plan height cutZ (mm above the storey's origin).
* `skip` = keys drawn some other way. Elements wholly below the cut (and not more than `depth` below `bottom`, 0 = the level)
* give their feature edges instead (30°), flattened; with `top` (0.6 DF.19, a view range), elements wholly above the cut and
* starting under `top` give theirs as `above` (drawn dashed: overhead). → { cut, below, above, bounds: { x0, y0, x1, y1 } | null }
*/
export function planCut(model, { M, storey, cutZ = 1200, skip = null, depth = 50, bottom = 0, top = null, edges = true } = {}) {
const cut = new Map(), below = new Map(), above = new Map();
const bb = { x0: Infinity, y0: Infinity, x1: -Infinity, y1: -Infinity };
const grow = (x, y) => { if (x < bb.x0) bb.x0 = x; if (x > bb.x1) bb.x1 = x; if (y < bb.y0) bb.y0 = y; if (y > bb.y1) bb.y1 = y; };
for (const b of model.batches || []) {
if (b.space || b.storey !== storey) continue;
const P = b.positions, I = b.indices, E = b.elems;
// the batch's vertices in plan axes, once
const n = P.length / 3, X = new Float64Array(n), Y = new Float64Array(n), Z = new Float64Array(n);
for (let i = 0; i < n; i++) {
const x = P[i * 3], y = P[i * 3 + 1], z = P[i * 3 + 2];
X[i] = M[0] * x + M[4] * y + M[8] * z + M[12];
Y[i] = M[1] * x + M[5] * y + M[9] * z + M[13];
Z[i] = M[2] * x + M[6] * y + M[10] * z + M[14];
}
for (let r = 0; r < E.ids.length; r++) {
const key = E.ids[r];
if (skip?.has(key)) continue;
const t0 = E.t0[r], tn = E.tn[r];
let zmin = Infinity, zmax = -Infinity;
for (let v = E.v0[r], ve = E.v0[r] + E.vn[r]; v < ve; v++) { if (Z[v] < zmin) zmin = Z[v]; if (Z[v] > zmax) zmax = Z[v]; }
if (zmin <= cutZ && zmax >= cutZ) {
const out = [];
for (let t = t0; t < t0 + tn; t++) {
const a = I[t * 3], c = I[t * 3 + 1], d = I[t * 3 + 2];
triCut(X, Y, Z, a, c, d, cutZ, out);
}
if (out.length) { const prev = cut.get(key); cut.set(key, prev ? concat(prev, out) : Float64Array.from(out)); for (let i = 0; i < out.length; i += 2) grow(out[i], out[i + 1]); }
} else if (edges && ((zmax < cutZ && zmax > bottom - depth) || (top !== null && zmin > cutZ && zmin < top))) {
const into = zmax < cutZ ? below : above;
const raw = runEdges(P, I, t0, tn, 30, []), out = [];
for (let i = 0; i < raw.length; i += 3) {
const x = raw[i], y = raw[i + 1], z = raw[i + 2];
out.push(M[0] * x + M[4] * y + M[8] * z + M[12], M[1] * x + M[5] * y + M[9] * z + M[13]);
}
// drop edges seen end on (vertical ones)
const keep = [];
for (let i = 0; i < out.length; i += 4) if (Math.hypot(out[i + 2] - out[i], out[i + 3] - out[i + 1]) > 0.5) { keep.push(out[i], out[i + 1], out[i + 2], out[i + 3]); grow(out[i], out[i + 1]); }
if (keep.length) { const prev = into.get(key); into.set(key, prev ? concat(prev, keep) : Float64Array.from(keep)); }
}
}
}
return { cut, below, above, bounds: Number.isFinite(bb.x0) ? bb : null };
}