1// The base slab that comes with a closed run of outer walls: whoever draws 2// the outline of a house on its lowest storey has drawn its ground slab 3// too, and it was one more tool and one more click to remember (Steffen's 4// prototype makes it the same way). 5// 6// Only on the lowest storey of a building part: higher up the floor is the 7// slab of the storey below. Not twice: a base slab already under the run 8// is left alone. 9 10import { pointInPolygon, signedShoelaceArea } from 'webcad/geometry' 11import { earClip } from 'webcad/triangulation' 12import { detectSlabOutlineAt } from 'webcad/space_shapes' 13 14// The outline of the base slab to lay under a closed run, or null when none 15// is wanted. `ring` is the run's axis points; `walls` the storey's walls 16// after the run was saved; `slabs` its slabs. 17export function baseSlabOutline ({ ring, walls, slabs, storey, storeys }) { 18 if (!storey || !isLowestOfPart(storey, storeys)) return null 19 const inside = interiorPoint(ring) 20 if (!inside) return null 21 const covered = (slabs || []).some(slab => slab.predefined_type === 'BASESLAB' && pointInPolygon(inside, slab.boundary_polygon || [])) 22 if (covered) return null 23 return detectSlabOutlineAt(walls || [], inside, 'outer') 24} 25 26// Whether no storey of the same building part lies lower. 27export function isLowestOfPart (storey, storeys) { 28 const level = (s) => Number(s.effective_niveau_height ?? s.elevation ?? 0) 29 const part = storey.section || null 30 return !(storeys || []).some(s => s.id !== storey.id && (s.section || null) === part && level(s) < level(storey) - 1e-6) 31} 32 33// A point inside a ring: its centroid when that lies inside (any convex 34// outline), else the centre of one of its triangles â an L's centroid can 35// fall into the notch. 36export function interiorPoint (ring) { 37 const pts = (ring || []).map(p => ({ x: Number(p.x), y: Number(p.y) })) 38 if (pts.length < 3 || Math.abs(signedShoelaceArea(pts)) < 1e-6) return null 39 const c = { x: pts.reduce((s, p) => s + p.x, 0) / pts.length, y: pts.reduce((s, p) => s + p.y, 0) / pts.length } 40 if (pointInPolygon(c, pts)) return c 41 const [tri] = earClip(pts) 42 if (!tri) return null 43 const [a, b, d] = tri.map(i => pts[i]) 44 return { x: (a.x + b.x + d.x) / 3, y: (a.y + b.y + d.y) / 3 } 45}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.