{"version":3,"file":"floorPlan.mesh.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.mesh.ts"],"sourcesContent":["/**\n * Turns a floor into triangles: wall solids with their openings cut out, and a floor\n * slab under them.\n *\n * Plain arrays, no renderer and no dependency. `positions`/`normals`/`indices` is the\n * shape every engine takes, so the result feeds three.js, Babylon, a glTF writer or a\n * software rasteriser without this package having an opinion about which.\n *\n * AXES: plan x becomes world x, plan y becomes world z, and world y is up. A plan is\n * drawn in SVG's y-down space, so that mapping is a handedness flip, which is why\n * winding is fixed per face here rather than inherited from the 2D rings.\n *\n * Openings are cut by SPLITTING the wall, not by subtracting solids. Every opening is an\n * axis-aligned rectangle in its wall's own plane, so the wall falls into at most three\n * prisms — under the sill, over the head, and the full-height stretches between openings.\n * That is exact, and it needs no CSG.\n */\n\nimport { dist } from './floorPlan.geometry';\nimport { derivePlan } from './floorPlan.rooms';\nimport { wallCentrelineSpans } from './floorPlan.path';\nimport { triangulate } from './floorPlan.triangulate';\nimport type { PlanFloor, PlanOpening, PlanPoint, PlanWall } from './floorPlan.types';\n\nconst EPS = 1e-6;\n\n/**\n * Millimetres the walls drop past the slab, so the two solids never share a face.\n *\n * A wall overlaps the slab it stands over, because the slab reaches the wall centrelines\n * and the wall straddles them. Land their undersides on the same plane and the two\n * downward faces fight for the same pixels, which speckles the whole underside of the\n * building. One millimetre on a slab of a couple of hundred puts the wall's face clear\n * below and the slab's inside the wall, so each height has exactly one surface at it.\n */\nconst Z_BIAS = 1;\n\nexport type PlanMeshPart = 'wall' | 'floor';\n\nexport interface PlanMeshGroup {\n  part: PlanMeshPart;\n  /** Offset into `indices`, and how many of them. Draw ranges, or assign materials. */\n  start: number;\n  count: number;\n}\n\nexport interface PlanMesh {\n  /** World-space xyz triples, in millimetres. */\n  positions: Float32Array;\n  /** Unit face normals, one per vertex. */\n  normals: Float32Array;\n  indices: Uint32Array;\n  groups: PlanMeshGroup[];\n}\n\nexport interface BuildPlanMeshOptions {\n  /** Storey height for walls that do not carry their own. Defaults to the floor's. */\n  wallHeight?: number;\n  /** Lay a slab under each room. Default true. */\n  includeFloorSlab?: boolean;\n  /** Millimetres. The slab hangs below the floor level. Default 200. */\n  slabThickness?: number;\n  /**\n   * Lift everything by the floor's own elevation, so several storeys stack into one\n   * scene. Default true.\n   */\n  applyElevation?: boolean;\n}\n\n/** Accumulates triangles as flat arrays, with a face normal per triangle. */\nclass MeshBuilder {\n  readonly positions: number[] = [];\n  readonly normals: number[] = [];\n  readonly indices: number[] = [];\n\n  /**\n   * One triangle, with its vertices duplicated rather than shared.\n   *\n   * Walls are flat-shaded: sharing a vertex between two faces would average their\n   * normals and round off a corner that is physically square.\n   */\n  triangle(a: number[], b: number[], c: number[]): void {\n    const ux = b[0] - a[0];\n    const uy = b[1] - a[1];\n    const uz = b[2] - a[2];\n    const vx = c[0] - a[0];\n    const vy = c[1] - a[1];\n    const vz = c[2] - a[2];\n    let nx = uy * vz - uz * vy;\n    let ny = uz * vx - ux * vz;\n    let nz = ux * vy - uy * vx;\n    const len = Math.hypot(nx, ny, nz);\n    if (len < EPS) return; // degenerate: contributes nothing and has no normal\n    nx /= len;\n    ny /= len;\n    nz /= len;\n\n    const base = this.positions.length / 3;\n    for (const p of [a, b, c]) this.positions.push(p[0], p[1], p[2]);\n    for (let i = 0; i < 3; i++) this.normals.push(nx, ny, nz);\n    this.indices.push(base, base + 1, base + 2);\n  }\n\n  /** A planar quad as two triangles, wound so its normal follows a -> b -> c. */\n  quad(a: number[], b: number[], c: number[], d: number[]): void {\n    this.triangle(a, b, c);\n    this.triangle(a, c, d);\n  }\n}\n\n/**\n * A wall as a box: its footprint corners in plan, and the heights it spans.\n *\n * The footprint is the centreline offset by half the thickness each way. Walls that meet\n * at a corner overlap slightly inside the joint rather than mitring into each other; for\n * an opaque solid that is invisible from any angle, and it keeps each wall a closed box\n * instead of a shape that depends on its neighbours.\n */\n/** The walls meeting at each node, for mitring a corner. */\nfunction wallsByNode(floor: PlanFloor): Map<string, PlanWall[]> {\n  const at = new Map<string, PlanWall[]>();\n  for (const wall of Object.values(floor.walls)) {\n    for (const id of [wall.a, wall.b]) {\n      const list = at.get(id);\n      if (list) list.push(wall);\n      else at.set(id, [wall]);\n    }\n  }\n  return at;\n}\n\n/** How far a mitre may run before the corner is left square, as multiples of half a wall. */\nconst MITRE_LIMIT = 4;\n\n/**\n * How far the footprint's two edges reach past a node, as a vector to be scaled by half\n * the thickness and added on the wall's left, subtracted on its right.\n *\n * Two walls meeting at a corner both STOP at the shared node, so between them they leave\n * a square of half a thickness uncovered on the outside of the turn — the same notch the\n * 2D renderer fills with a mitre join, cut here through the full height of the wall.\n * Extending both walls to where their edges actually cross closes it.\n *\n * `null` leaves the end square: at a junction there is no single corner to mitre to, at a\n * change of thickness the two edges are offset by different amounts, and past the mitre\n * limit an acute corner would run away into a spike.\n */\nfunction mitreVector(floor: PlanFloor, wall: PlanWall, nodeId: string, at: Map<string, PlanWall[]>): PlanPoint | null {\n  const here = at.get(nodeId);\n  if (!here || here.length !== 2) return null;\n  const other = here[0].id === wall.id ? here[1] : here[0];\n  if (other.thickness !== wall.thickness) return null;\n\n  const node = floor.nodes[nodeId];\n  const far = floor.nodes[wall.a === nodeId ? wall.b : wall.a];\n  const onward = floor.nodes[other.a === nodeId ? other.b : other.a];\n  if (!node || !far || !onward) return null;\n\n  // Into the corner along this wall, then out along its neighbour.\n  const u = unit(far, node);\n  const v = unit(node, onward);\n  if (!u || !v) return null;\n\n  const along = u.x * v.x + u.y * v.y;\n  if (along < -1 + EPS) return null; // doubles back on itself: no corner to speak of\n  const nu = { x: -u.y, y: u.x };\n  const nv = { x: -v.y, y: v.x };\n  const scale = 1 + along;\n  const mitre = { x: (nu.x + nv.x) / scale, y: (nu.y + nv.y) / scale };\n  if (Math.hypot(mitre.x, mitre.y) > MITRE_LIMIT) return null;\n\n  // Returned on the wall's own left, which is `u`'s left at this end.\n  return wall.b === nodeId ? mitre : { x: -mitre.x, y: -mitre.y };\n}\n\nfunction unit(from: PlanPoint, to: PlanPoint): PlanPoint | null {\n  const len = dist(from, to);\n  if (len < EPS) return null;\n  return { x: (to.x - from.x) / len, y: (to.y - from.y) / len };\n}\n\n/**\n * The stretch of a wall between two offsets along it, as a footprint quad.\n *\n * Ends that are the wall's true ends are mitred into their neighbour; ends created by an\n * opening are left square, because a jamb is a square face.\n */\nfunction footprintBetween(\n  floor: PlanFloor,\n  wall: PlanWall,\n  from: number,\n  to: number,\n  at: Map<string, PlanWall[]>,\n): [PlanPoint, PlanPoint, PlanPoint, PlanPoint] | null {\n  const a = floor.nodes[wall.a];\n  const b = floor.nodes[wall.b];\n  if (!a || !b) return null;\n  const len = dist(a, b);\n  if (len < EPS || to - from < EPS) return null;\n\n  const d = unit(a, b)!;\n  const half = wall.thickness / 2;\n  const n = { x: -d.y * half, y: d.x * half };\n\n  const start = { x: a.x + d.x * from, y: a.y + d.y * from };\n  const end = { x: a.x + d.x * to, y: a.y + d.y * to };\n\n  const mitreA = from < EPS ? mitreVector(floor, wall, wall.a, at) : null;\n  const mitreB = to > len - EPS ? mitreVector(floor, wall, wall.b, at) : null;\n\n  const offset = (p: PlanPoint, m: PlanPoint | null, sign: 1 | -1) =>\n    m ? { x: p.x + m.x * half * sign, y: p.y + m.y * half * sign } : { x: p.x + n.x * sign, y: p.y + n.y * sign };\n\n  return [offset(start, mitreA, 1), offset(end, mitreB, 1), offset(end, mitreB, -1), offset(start, mitreA, -1)];\n}\n\n/** A closed box over a footprint, between two heights. */\nfunction box(mesh: MeshBuilder, foot: readonly PlanPoint[], bottom: number, top: number): void {\n  if (top - bottom < EPS) return;\n  const lo = foot.map((p) => [p.x, bottom, p.y]);\n  const hi = foot.map((p) => [p.x, top, p.y]);\n\n  /*\n   * Which way round the footprint runs decides which way the faces look. Plan y becomes\n   * world z with y up, which is a handedness flip, so a footprint that reads\n   * counter-clockwise in plan comes out clockwise seen from above and its faces would\n   * point inwards. Hence the inversion rather than taking the plan winding at its word.\n   */\n  let twice = 0;\n  for (let i = 0; i < foot.length; i++) {\n    const p = foot[i];\n    const q = foot[(i + 1) % foot.length];\n    twice += p.x * q.y - q.x * p.y;\n  }\n  const ccw = twice < 0;\n\n  for (let i = 0; i < foot.length; i++) {\n    const j = (i + 1) % foot.length;\n    if (ccw) mesh.quad(lo[i], lo[j], hi[j], hi[i]);\n    else mesh.quad(lo[j], lo[i], hi[i], hi[j]);\n  }\n\n  // Caps. A footprint is always convex here — it is a rectangle — so a fan is enough.\n  for (let i = 1; i + 1 < foot.length; i++) {\n    if (ccw) {\n      mesh.triangle(hi[0], hi[i], hi[i + 1]);\n      mesh.triangle(lo[0], lo[i + 1], lo[i]);\n    } else {\n      mesh.triangle(hi[0], hi[i + 1], hi[i]);\n      mesh.triangle(lo[0], lo[i], lo[i + 1]);\n    }\n  }\n}\n\n/** The openings on one wall, clamped to it and sorted along it. */\nfunction openingsOn(floor: PlanFloor, wall: PlanWall, length: number): PlanOpening[] {\n  return Object.values(floor.openings)\n    .filter((o) => o.wallId === wall.id)\n    .filter((o) => o.offset + o.width / 2 > EPS && o.offset - o.width / 2 < length - EPS)\n    .sort((a, b) => a.offset - b.offset);\n}\n\n/**\n * Triangles for one floor: every wall, with its openings cut out, and a slab per room.\n *\n * Call it per storey and concatenate to build a whole building; each floor already knows\n * its own elevation.\n */\nexport function buildPlanMesh(floor: PlanFloor, options: BuildPlanMeshOptions = {}): PlanMesh {\n  const { wallHeight, includeFloorSlab = true, slabThickness = 200, applyElevation = true } = options;\n\n  const mesh = new MeshBuilder();\n  const groups: PlanMeshGroup[] = [];\n  const base = applyElevation ? floor.elevation : 0;\n\n  /*\n   * `elevation` is the FINISHED FLOOR LEVEL, so a storey's slab hangs below it. Walls\n   * start at the slab's underside rather than at the level itself, which is to say they\n   * clothe the floor plate the way a facade does instead of stopping short of it.\n   *\n   * That keeps a wall exactly `floor.height` tall, and makes each storey's walls meet the\n   * ones below face to face: this storey starts at `elevation - slab`, and the storey\n   * below ends there too. No band of exposed slab between them, and nothing overlapping\n   * along the facade.\n   */\n  const slabDrop = includeFloorSlab ? slabThickness + Z_BIAS : 0;\n\n  // ── Walls ──────────────────────────────────────────────────────────────────────\n  const atNode = wallsByNode(floor);\n  const wallStart = mesh.indices.length;\n  for (const wall of Object.values(floor.walls)) {\n    const a = floor.nodes[wall.a];\n    const b = floor.nodes[wall.b];\n    if (!a || !b) continue;\n    const length = dist(a, b);\n    if (length < EPS) continue;\n\n    const bottom = base + (wall.baseHeight ?? 0) - slabDrop;\n    const top = bottom + (wall.height ?? wallHeight ?? floor.height);\n    if (top - bottom < EPS) continue;\n\n    const openings = openingsOn(floor, wall, length);\n    if (openings.length === 0) {\n      const foot = footprintBetween(floor, wall, 0, length, atNode);\n      if (foot) box(mesh, foot, bottom, top);\n      continue;\n    }\n\n    // Full-height stretches between the openings, from `wallCentrelineSpans` so the\n    // solid is cut exactly where the drawing is.\n    for (const [from, to] of wallCentrelineSpans(floor, wall)) {\n      const foot = footprintBetween(floor, wall, from, to, atNode);\n      if (foot) box(mesh, foot, bottom, top);\n    }\n\n    // Then the lintel over each opening and the upstand under it.\n    for (const opening of openings) {\n      const from = Math.max(0, opening.offset - opening.width / 2);\n      const to = Math.min(length, opening.offset + opening.width / 2);\n      const foot = footprintBetween(floor, wall, from, to, atNode);\n      if (!foot) continue;\n      const sill = bottom + Math.max(0, opening.sill);\n      const head = bottom + Math.max(0, opening.head);\n      if (sill > bottom + EPS) box(mesh, foot, bottom, Math.min(sill, top));\n      if (head < top - EPS) box(mesh, foot, Math.max(head, bottom), top);\n    }\n  }\n  if (mesh.indices.length > wallStart) {\n    groups.push({ part: 'wall', start: wallStart, count: mesh.indices.length - wallStart });\n  }\n\n  // ── Floor slabs ────────────────────────────────────────────────────────────────\n  if (includeFloorSlab) {\n    const slabStart = mesh.indices.length;\n    for (const room of derivePlan(floor).rooms) {\n      const { vertices, indices } = triangulate(room.outer, room.holes);\n      if (indices.length === 0) continue;\n      const top = base;\n      const bottom = base - slabThickness;\n\n      for (let i = 0; i < indices.length; i += 3) {\n        const t = [vertices[indices[i]], vertices[indices[i + 1]], vertices[indices[i + 2]]];\n        const up = t.map((q) => [q.x, top, q.y]);\n        const down = t.map((q) => [q.x, bottom, q.y]);\n        // `triangulate` returns counter-clockwise triangles in plan space; the y-down to\n        // y-up flip reverses that, so the top face is wound the other way here.\n        mesh.triangle(up[0], up[2], up[1]);\n        mesh.triangle(down[0], down[1], down[2]);\n      }\n\n      // The slab's edge, so it is a solid rather than two sheets.\n      for (const ring of [room.outer, ...room.holes]) {\n        for (let i = 0; i < ring.length; i++) {\n          const q = ring[i];\n          const r = ring[(i + 1) % ring.length];\n          // Wound so the face looks away from the solid. Holes come back wound opposite\n          // to the outer ring, so walking each ring as it is gives both the right way out.\n          mesh.quad([q.x, bottom, q.y], [q.x, top, q.y], [r.x, top, r.y], [r.x, bottom, r.y]);\n        }\n      }\n    }\n    if (mesh.indices.length > slabStart) {\n      groups.push({ part: 'floor', start: slabStart, count: mesh.indices.length - slabStart });\n    }\n  }\n\n  return {\n    positions: new Float32Array(mesh.positions),\n    normals: new Float32Array(mesh.normals),\n    indices: new Uint32Array(mesh.indices),\n    groups,\n  };\n}\n"],"mappings":"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"}