Section of a faceted mesh
32-bit in partThe section view's visible lines, cut outline, extents and the cutting plane's trace.
Float32Array.from(P) rounds every coordinate to 32-bit before the cut, and the cut solid's vertices come back from a 32-bit array. The section's lines and box in the PDF therefore carry about 7 significant digits. The trace of the plane in the neighboring view is computed in 64-bit from the bounding box center and does not pass through the 32-bit data.
/**
* A section view of a faceted mesh (0.4 K.3, the mesh kernel's own): the solid cut by the plane square to view `sec.of` at
* `sec.at` along its direction (default: through the middle of the part), the viewer's side taken away (manifold
* trimByPlane), the rest projected like the other views (visible lines only, as brep-draw.js does); `cut` = the outline of
* the facets lying in the cutting plane (drawing.js hatches inside it); `trace` = where the plane shows edge-on in the
* neighbouring view (SECTION_TRACE). The same shape as brep-draw.js's views.section; null when nothing is left behind the plane.
*/
export function meshSection(M, mesh, views, sec) {
const view = VIEWS[sec.of];
const P = mesh.positions;
const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
for (let i = 0; i < P.length; i += 3) for (let k = 0; k < 3; k++) { min[k] = Math.min(min[k], P[i + k]); max[k] = Math.max(max[k], P[i + k]); }
const centre = V.mul(V.add(min, max), 0.5);
const size = Math.max(1e-6, V.len(V.sub(max, min)));
const at = Number.isFinite(sec.at) ? sec.at : V.dot(centre, view.dir);
const o = V.add(V.sub(centre, V.mul(view.dir, V.dot(centre, view.dir))), V.mul(view.dir, at));
const live = [];
try {
const gl = new M.Mesh({ numProp: 3, vertProperties: Float32Array.from(P), triVerts: Uint32Array.from(mesh.indices) });
gl.merge();
const whole = new M.Manifold(gl); live.push(whole);
// trimByPlane keeps what lies on the normal's side: keep n·p ≥ −at with n = −dir, i.e. dir·p ≤ at (away from the viewer)
const rest = whole.trimByPlane(V.mul(view.dir, -1), -at); live.push(rest);
if (rest.isEmpty()) return null;
const g = rest.getMesh();
const np = g.numProp, nv = g.vertProperties.length / np;
const pos = new Float64Array(nv * 3);
for (let i = 0; i < nv; i++) for (let k = 0; k < 3; k++) pos[i * 3 + k] = g.vertProperties[i * np + k];
const cutMesh = { positions: pos, indices: Uint32Array.from(g.triVerts) };
const v = projectView(cutMesh, meshEdges(cutMesh), view, { hidden: false });
// facets in the plane facing the viewer: the edges only one of them uses are the cut's outline
const tol = 1e-6 * size, I = cutMesh.indices, nt = I.length / 3;
const use = new Map(), runs = new Map(); // undirected key → count, and the edge as its facet runs it
const pt = (i) => [pos[i * 3], pos[i * 3 + 1], pos[i * 3 + 2]];
for (let t = 0; t < nt; t++) {
const a = pt(I[t * 3]), b = pt(I[t * 3 + 1]), c = pt(I[t * 3 + 2]);
if ([a, b, c].some((q) => Math.abs(V.dot(q, view.dir) - at) > tol)) continue;
const n = V.cross(V.sub(b, a), V.sub(c, a));
if (V.dot(n, view.dir) <= 0) continue;
for (const [p, q] of [[I[t * 3], I[t * 3 + 1]], [I[t * 3 + 1], I[t * 3 + 2]], [I[t * 3 + 2], I[t * 3]]]) {
const key = p < q ? p + ',' + q : q + ',' + p;
use.set(key, (use.get(key) || 0) + 1);
runs.set(key, [p, q]);
}
}
// outline segments keep their facet's direction: counter-clockwise round the cut seen from the viewer (holes clockwise)
const cut = [];
for (const [key, n] of use) {
if (n !== 1) continue;
const [p, q] = runs.get(key);
const a = pt(p), b = pt(q);
cut.push([V.dot(a, view.right), V.dot(a, view.up), V.dot(b, view.right), V.dot(b, view.up)]);
}
let u0 = Infinity, v0 = Infinity, u1 = -Infinity, v1 = -Infinity;
for (const s of v.visible.concat(cut)) { u0 = Math.min(u0, s[0], s[2]); u1 = Math.max(u1, s[0], s[2]); v0 = Math.min(v0, s[1], s[3]); v1 = Math.max(v1, s[1], s[3]); }
// the cutting plane's trace in the view that sees it edge-on (the same arithmetic as brep-draw.js)
const traceView = SECTION_TRACE[sec.of], tv = VIEWS[traceView];
const along = V.norm(V.cross(view.dir, tv.dir));
const trace = { view: traceView, p: [V.dot(o, tv.right), V.dot(o, tv.up)], d: [V.dot(along, tv.right), V.dot(along, tv.up)],
sight: [V.dot(V.mul(view.dir, -1), tv.right), V.dot(V.mul(view.dir, -1), tv.up)], box: views[traceView].box };
return { visible: v.visible, hidden: [], cut, box: u0 === Infinity ? { u0: 0, v0: 0, u1: 1, v1: 1 } : { u0, v0, u1, v1 }, of: sec.of, at, trace };
} finally {
for (const m of live) { try { m.delete(); } catch { /* gone */ } }
}
}