{"version":3,"file":"floorPlan.geometry.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.geometry.ts"],"sourcesContent":["/**\n * Pure vector and polygon primitives. Nothing here knows about the plan model.\n *\n * All lengths are millimetres (see floorPlan.types.ts).\n */\n\nimport type { PlanPoint } from './floorPlan.types';\n\n// ─── Model-integrity epsilons ──────────────────────────────────────────────────\n// These are FIXED millimetre values describing when the model itself is degenerate.\n// They are a separate family from the user-intent tolerances in floorPlan.snap.ts,\n// which are screen pixels converted through the current zoom. Conflating the two\n// gives you either a model that accumulates 0.2mm walls, or snapping that stops\n// working when zoomed in.\n\n/** Shortest wall the graph will accept. Keeps zero-length walls out entirely. */\nexport const MIN_WALL_LENGTH = 10;\n/** Two points closer than this are the same point. */\nexport const EPS_COINCIDENT = 1;\n/** A ring with |area| below this encloses nothing (0.0001 m²). */\nexport const EPS_AREA = 100;\n/** Angular tolerance for \"these two walls are collinear\", in radians. */\nexport const EPS_ANGLE = 1e-4;\n/** Narrowest scanline span that counts as room interior rather than a slit. */\nexport const EPS_SPAN = 2;\n/** Narrowest opening worth keeping when a wall shortens under it. */\nexport const MIN_OPENING_WIDTH = 100;\n\n// ─── Vectors ───────────────────────────────────────────────────────────────────\n\nexport function dist(a: PlanPoint, b: PlanPoint): number {\n  return Math.hypot(a.x - b.x, a.y - b.y);\n}\n\nexport function distSq(a: PlanPoint, b: PlanPoint): number {\n  const dx = a.x - b.x;\n  const dy = a.y - b.y;\n  return dx * dx + dy * dy;\n}\n\n/** Cross product of ab and ac. Positive when c is clockwise of b in SVG's y-down space. */\nexport function cross(a: PlanPoint, b: PlanPoint, c: PlanPoint): number {\n  return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);\n}\n\nexport function lerp(a: PlanPoint, b: PlanPoint, t: number): PlanPoint {\n  return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t };\n}\n\n/** Unit vector from a to b, or null when they are coincident. */\nexport function direction(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n  const dx = b.x - a.x;\n  const dy = b.y - a.y;\n  const len = Math.hypot(dx, dy);\n  if (len < 1e-9) return null;\n  return { x: dx / len, y: dy / len };\n}\n\n/** Left-hand normal of the direction from a to b, or null when coincident. */\nexport function normal(a: PlanPoint, b: PlanPoint): PlanPoint | null {\n  const d = direction(a, b);\n  return d ? { x: -d.y, y: d.x } : null;\n}\n\n// ─── Point / segment ───────────────────────────────────────────────────────────\n\n/**\n * Distance from p to segment ab, plus the clamped parameter t of the closest point.\n * `t` is clamped to [0, 1], so the closest point is always on the segment.\n */\nexport function pointToSegment(\n  p: PlanPoint,\n  a: PlanPoint,\n  b: PlanPoint,\n): { dist: number; t: number; point: PlanPoint } {\n  const dx = b.x - a.x;\n  const dy = b.y - a.y;\n  const len2 = dx * dx + dy * dy;\n  if (len2 === 0) return { dist: dist(p, a), t: 0, point: { x: a.x, y: a.y } };\n  const raw = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;\n  const t = raw < 0 ? 0 : raw > 1 ? 1 : raw;\n  const point = { x: a.x + t * dx, y: a.y + t * dy };\n  return { dist: dist(p, point), t, point };\n}\n\n/**\n * Exact projection of p onto segment ab, clamped to the segment.\n *\n * Used after a fuzzy hit tolerance has decided that p belongs to this segment: intent\n * is allowed to be approximate, the stored model never is.\n */\nexport function projectOnSegment(p: PlanPoint, a: PlanPoint, b: PlanPoint): PlanPoint {\n  return pointToSegment(p, a, b).point;\n}\n\nexport type SegmentIntersectionKind =\n  /** The segments cross strictly inside both. */\n  | 'proper'\n  /** They are parallel and do not touch. */\n  | 'none'\n  /** They lie on the same line and overlap over a non-zero length. */\n  | 'collinearOverlap'\n  /** They lie on the same line but do not overlap. */\n  | 'collinearDisjoint'\n  /** An endpoint of one lies on the other, or they share an endpoint. */\n  | 'touch';\n\nexport interface SegmentIntersection {\n  kind: SegmentIntersectionKind;\n  /** Parameter along ab, for 'proper' and 'touch'. */\n  t?: number;\n  /** Parameter along cd, for 'proper' and 'touch'. */\n  s?: number;\n  point?: PlanPoint;\n}\n\n/**\n * Classifies how segments ab and cd meet.\n *\n * `eps` is a millimetre distance tolerance, not a parameter tolerance, so the result\n * does not change with segment length.\n */\nexport function segmentIntersection(\n  a: PlanPoint,\n  b: PlanPoint,\n  c: PlanPoint,\n  d: PlanPoint,\n  eps = EPS_COINCIDENT,\n): SegmentIntersection {\n  const abLen = dist(a, b);\n  const cdLen = dist(c, d);\n  if (abLen < 1e-9 || cdLen < 1e-9) return { kind: 'none' };\n\n  const rx = b.x - a.x;\n  const ry = b.y - a.y;\n  const sx = d.x - c.x;\n  const sy = d.y - c.y;\n  const denom = rx * sy - ry * sx;\n\n  // Perpendicular distance of c and d from line ab, used to tell parallel from collinear.\n  const distC = Math.abs(cross(a, b, c)) / abLen;\n  const distD = Math.abs(cross(a, b, d)) / abLen;\n\n  if (Math.abs(denom) < 1e-12 || (distC < eps && distD < eps)) {\n    if (distC >= eps || distD >= eps) return { kind: 'none' };\n    // Collinear: compare parameters along ab.\n    const len2 = rx * rx + ry * ry;\n    const tc = ((c.x - a.x) * rx + (c.y - a.y) * ry) / len2;\n    const td = ((d.x - a.x) * rx + (d.y - a.y) * ry) / len2;\n    const lo = Math.min(tc, td);\n    const hi = Math.max(tc, td);\n    const epsT = eps / abLen;\n    if (hi < epsT || lo > 1 - epsT) return { kind: 'collinearDisjoint' };\n    if (Math.min(hi, 1) - Math.max(lo, 0) < epsT) return { kind: 'collinearDisjoint' };\n    return { kind: 'collinearOverlap' };\n  }\n\n  const t = ((c.x - a.x) * sy - (c.y - a.y) * sx) / denom;\n  const s = ((c.x - a.x) * ry - (c.y - a.y) * rx) / denom;\n  const epsT = eps / abLen;\n  const epsS = eps / cdLen;\n\n  if (t < -epsT || t > 1 + epsT || s < -epsS || s > 1 + epsS) return { kind: 'none' };\n\n  const point = { x: a.x + t * rx, y: a.y + t * ry };\n  const atAbEnd = t <= epsT || t >= 1 - epsT;\n  const atCdEnd = s <= epsS || s >= 1 - epsS;\n  if (atAbEnd || atCdEnd) return { kind: 'touch', t, s, point };\n  return { kind: 'proper', t, s, point };\n}\n\n// ─── Rings ─────────────────────────────────────────────────────────────────────\n\n/**\n * Shoelace area of a closed ring (first vertex not repeated).\n *\n * Positive means visually clockwise in SVG's y-down space. See `PlanFace.outer`.\n */\nexport function signedArea(ring: readonly PlanPoint[]): number {\n  const n = ring.length;\n  if (n < 3) return 0;\n  let sum = 0;\n  for (let i = 0, j = n - 1; i < n; j = i++) {\n    sum += ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n  }\n  return sum / 2;\n}\n\nexport function perimeter(ring: readonly PlanPoint[]): number {\n  const n = ring.length;\n  if (n < 2) return 0;\n  let sum = 0;\n  for (let i = 0, j = n - 1; i < n; j = i++) sum += dist(ring[j], ring[i]);\n  return sum;\n}\n\n/**\n * Area-weighted centroid of a ring.\n *\n * Deliberately not the vertex mean: a dangling wall adds an out-and-back vertex pair\n * that skews the mean but contributes nothing to the area moment. Falls back to the\n * vertex mean only for a degenerate (zero-area) ring.\n */\nexport function areaCentroid(ring: readonly PlanPoint[]): PlanPoint {\n  const n = ring.length;\n  if (n === 0) return { x: 0, y: 0 };\n  if (n < 3) {\n    return {\n      x: ring.reduce((s, p) => s + p.x, 0) / n,\n      y: ring.reduce((s, p) => s + p.y, 0) / n,\n    };\n  }\n  let a = 0;\n  let cx = 0;\n  let cy = 0;\n  for (let i = 0, j = n - 1; i < n; j = i++) {\n    const f = ring[j].x * ring[i].y - ring[i].x * ring[j].y;\n    a += f;\n    cx += (ring[j].x + ring[i].x) * f;\n    cy += (ring[j].y + ring[i].y) * f;\n  }\n  if (Math.abs(a) < 1e-9) {\n    return {\n      x: ring.reduce((s, p) => s + p.x, 0) / n,\n      y: ring.reduce((s, p) => s + p.y, 0) / n,\n    };\n  }\n  return { x: cx / (3 * a), y: cy / (3 * a) };\n}\n\n/** Ray-casting point-in-ring test. Points exactly on an edge are unspecified. */\nexport function pointInRing(p: PlanPoint, ring: readonly PlanPoint[]): boolean {\n  let inside = false;\n  for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n    const xi = ring[i].x;\n    const yi = ring[i].y;\n    const xj = ring[j].x;\n    const yj = ring[j].y;\n    if (yi > p.y !== yj > p.y && p.x < ((xj - xi) * (p.y - yi)) / (yj - yi) + xi) {\n      inside = !inside;\n    }\n  }\n  return inside;\n}\n\n/** Inside `outer` and outside every hole. */\nexport function pointInRings(\n  p: PlanPoint,\n  outer: readonly PlanPoint[],\n  holes: readonly (readonly PlanPoint[])[] = [],\n): boolean {\n  if (!pointInRing(p, outer)) return false;\n  for (const hole of holes) if (pointInRing(p, hole)) return false;\n  return true;\n}\n\n/**\n * A point guaranteed to lie strictly inside the ring set, placed somewhere visually\n * central where possible.\n *\n * The area centroid is preferred, but it is not guaranteed to be inside a concave\n * polygon (an L-shaped room's centroid can land in the notch), so a scanline fallback\n * picks the widest interior span across several horizontal lines. The EPS_SPAN test on\n * that span is what stops an anchor landing inside the zero-width slit that a dangling\n * wall leaves in a face.\n */\nexport function representativePoint(\n  outer: readonly PlanPoint[],\n  holes: readonly (readonly PlanPoint[])[] = [],\n): PlanPoint {\n  if (outer.length === 0) return { x: 0, y: 0 };\n\n  const centre = areaCentroid(outer);\n  if (pointInRings(centre, outer, holes)) return centre;\n\n  let ymin = Infinity;\n  let ymax = -Infinity;\n  for (const p of outer) {\n    if (p.y < ymin) ymin = p.y;\n    if (p.y > ymax) ymax = p.y;\n  }\n  if (!Number.isFinite(ymin) || ymax - ymin < 1e-9) return centre;\n\n  const rings: readonly (readonly PlanPoint[])[] = [outer, ...holes];\n  let best: { point: PlanPoint; width: number } | null = null;\n\n  const SAMPLES = 7;\n  for (let k = 1; k <= SAMPLES; k++) {\n    const y = ymin + ((ymax - ymin) * k) / (SAMPLES + 1);\n    const xs: number[] = [];\n    // One crossing list across ALL rings, so even-odd pairing yields interior spans\n    // directly. Running the scanline per ring would not compose with holes.\n    for (const ring of rings) {\n      for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {\n        const p = ring[j];\n        const q = ring[i];\n        if (p.y > y !== q.y > y) {\n          xs.push(p.x + ((y - p.y) * (q.x - p.x)) / (q.y - p.y));\n        }\n      }\n    }\n    xs.sort((m, n) => m - n);\n    for (let i = 0; i + 1 < xs.length; i += 2) {\n      const width = xs[i + 1] - xs[i];\n      if (width > EPS_SPAN && (!best || width > best.width)) {\n        best = { point: { x: (xs[i] + xs[i + 1]) / 2, y }, width };\n      }\n    }\n  }\n\n  return best ? best.point : centre;\n}\n\n/**\n * Removes out-and-back spurs (`... P, Q, P ...`) from a ring, repeatedly until none\n * remain.\n *\n * A face containing a dangling wall is a valid fill but is not a simple polygon, which\n * breaks polygon offsetting and makes some triangulators emit zero-area triangles. Run\n * this before triangulating a floor slab; keep the raw ring for the 2D fill, where the\n * slit is harmless and `wallIds` alignment matters.\n */\nexport function stripSpurs(ring: readonly PlanPoint[]): PlanPoint[] {\n  let out = ring.slice();\n  let changed = true;\n  while (changed && out.length > 3) {\n    changed = false;\n    const next: PlanPoint[] = [];\n    const n = out.length;\n    for (let i = 0; i < n; i++) {\n      const prev = out[(i - 1 + n) % n];\n      const nextPt = out[(i + 1) % n];\n      // The middle of an out-and-back is the vertex whose neighbours coincide.\n      if (dist(prev, nextPt) < EPS_COINCIDENT) {\n        changed = true;\n        continue;\n      }\n      next.push(out[i]);\n    }\n    if (changed) {\n      // Drop the now-duplicated turning point left behind by each removed spur tip.\n      out = next.filter((p, i) => dist(p, next[(i + 1) % next.length]) >= EPS_COINCIDENT);\n    }\n  }\n  return out;\n}\n\nexport function rectToPoints(x: number, y: number, w: number, h: number): PlanPoint[] {\n  return [\n    { x, y },\n    { x: x + w, y },\n    { x: x + w, y: y + h },\n    { x, y: y + h },\n  ];\n}\n\nexport function boundsOf(points: readonly PlanPoint[]): {\n  minX: number;\n  minY: number;\n  maxX: number;\n  maxY: number;\n} | null {\n  if (points.length === 0) return null;\n  let minX = Infinity;\n  let minY = Infinity;\n  let maxX = -Infinity;\n  let maxY = -Infinity;\n  for (const p of points) {\n    if (p.x < minX) minX = p.x;\n    if (p.y < minY) minY = p.y;\n    if (p.x > maxX) maxX = p.x;\n    if (p.y > maxY) maxY = p.y;\n  }\n  return { minX, minY, maxX, maxY };\n}\n"],"mappings":"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"}