Places things can stand on
64-bitTop-level placements and large flat faces with their box, top height, height and area.
placement area = Δx × Δy of its box; face area from the model kernel; every number rounded to 0.001
For a placement the area listed is the plan area of its axis-aligned box, which is larger than the true footprint for a shape that is not a rectangle along the axes. A face's height is the z of its first point. The listed numbers are rounded to three decimals; sorting uses the unrounded areas.
/** Placements and big flat faces of the model's top level that something can stand on or in: the assistant picks one by its id. */
export function placesOf(model, { limit = 50 } = {}) {
const out = [];
for (const [id, inst] of model.root.insts) {
if (sceneKind(model, inst)) continue;
const bb = boxOf(model, inst); if (!bb) continue;
const d = model.defs.get(inst.def), height = bb.max[2] - bb.min[2], dx = bb.max[0] - bb.min[0], dy = bb.max[1] - bb.min[1];
out.push({ instance: id, name: inst.name || (d && d.name) || '', kind: height <= 0.6 ? 'flat' : 'standing', bbox: { min: bb.min.map(r3), max: bb.max.map(r3) }, topZ: r3(bb.max[2]), height: r3(height), areaM2: r3(dx * dy), _a: dx * dy });
}
for (const [id, f] of model.root.faces) {
if (f.hidden) continue;
const n = faceNormal(model.root, f);
if (Math.abs(n[2]) < 0.98) continue;
const area = faceArea(model.root, f); if (area < 4) continue;
const pts = fPts(model.root, f)[0], xs = pts.map((p) => p[0]), ys = pts.map((p) => p[1]);
const z = pts[0][2];
out.push({ face: id, name: '', kind: 'flat', bbox: { min: [Math.min(...xs), Math.min(...ys), z].map(r3), max: [Math.max(...xs), Math.max(...ys), z].map(r3) }, topZ: r3(z), height: 0, areaM2: r3(area), _a: area });
}
out.sort((a, b) => b._a - a._a);
return { total: out.length, places: out.slice(0, limit).map(({ _a, ...p }) => p) };
}