{"version":3,"file":"floorPlan.snap.mjs","names":[],"sources":["../../../../package/common/floorPlan/floorPlan.snap.ts"],"sourcesContent":["/**\n * Snapping, and the screen-to-world conversion it depends on.\n *\n * Two things here are deliberately different from the previous implementation:\n *\n * 1. Conversion and snapping are separate. `clientToWorld` only converts; `resolveSnap`\n *    is the only thing that decides. The old `clientToSVG` called `snapPt` internally,\n *    which made grid snap unconditional and left no way to hold Alt to disable it.\n *\n * 2. Every tolerance is in SCREEN PIXELS, converted through the current zoom at the\n *    point of use. A fixed world-unit tolerance (the old `WALL_TOLERANCE = 8`) means\n *    snapping covers a smaller and smaller part of the screen as you zoom in, until it\n *    stops working.\n */\n\nimport { dist, pointToSegment, projectOnSegment, segmentIntersection } from './floorPlan.geometry';\nimport type { PlanFloor, PlanPoint } from './floorPlan.types';\n\nexport enum SnapKind {\n  None = 'none',\n  Node = 'node',\n  /** A point projected onto a wall's centreline. */\n  Wall = 'wall',\n  /** The crossing of two walls, or of a wall with the active angle ray. */\n  Intersection = 'intersection',\n  /** On the infinite line continuing an existing wall. */\n  Extension = 'extension',\n  /** Shares an x or y coordinate with an existing node. */\n  Alignment = 'alignment',\n  /** On a ray at a multiple of `angleStepDeg` from `origin`. */\n  Angle = 'angle',\n  Grid = 'grid',\n}\n\nexport interface SnapGuide {\n  kind: SnapKind;\n  a: PlanPoint;\n  b: PlanPoint;\n  /** Render as an unbounded construction line rather than a segment. */\n  infinite?: boolean;\n}\n\nexport interface SnapOptions {\n  /** Which snaps are active. Pass `[]` (Alt held) to disable snapping entirely. */\n  enabled: SnapKind[];\n  /** Millimetres. 0 disables grid snap. */\n  gridSize: number;\n  /** Degrees. 45 gives 0/45/90. 0 disables angle lock. */\n  angleStepDeg: number;\n  toleranceScreenPx: number;\n  /** Nodes are the most valuable snap, so they get a little more room. */\n  nodeToleranceScreenPx?: number;\n}\n\nexport const DEFAULT_SNAP_OPTIONS: SnapOptions = {\n  enabled: [\n    SnapKind.Node,\n    SnapKind.Intersection,\n    SnapKind.Wall,\n    SnapKind.Angle,\n    SnapKind.Extension,\n    SnapKind.Alignment,\n    SnapKind.Grid,\n  ],\n  gridSize: 100,\n  angleStepDeg: 0,\n  toleranceScreenPx: 10,\n  nodeToleranceScreenPx: 12,\n};\n\nexport interface SnapContext {\n  floor: PlanFloor;\n  /** World units per screen pixel at the current zoom: `1 / view.zoom`. */\n  pxToWorld: number;\n  /** The anchored point. Required for angle lock; also enables the length readout. */\n  origin?: PlanPoint;\n  /**\n   * A direction in radians that angle lock measures from, in ADDITION to the world axes.\n   *\n   * Pass the direction of the wall being continued and the lock offers perpendicular and\n   * parallel to that wall, not just to the world. Without it, angle lock is only useful\n   * on a building that happens to be aligned to the page.\n   */\n  referenceAngle?: number;\n  excludeWallIds?: ReadonlySet<string>;\n  excludeNodeIds?: ReadonlySet<string>;\n  options?: Partial<SnapOptions>;\n}\n\nexport interface SnapResult {\n  point: PlanPoint;\n  kind: SnapKind;\n  nodeId?: string;\n  wallId?: string;\n  /** Parameter along `wallId`, for `SnapKind.Wall`. */\n  t?: number;\n  /** Everything an overlay needs to draw to explain the snap. */\n  guides: SnapGuide[];\n  /** Radians from `origin` to `point`, when an origin was given. */\n  angle?: number;\n  /** Distance from `origin` to `point`, when an origin was given. */\n  distance?: number;\n  /**\n   * Set when angle lock produced the result, saying what it locked against: the world\n   * axes, or the reference direction that was passed in.\n   */\n  angleRelativeTo?: 'world' | 'reference';\n}\n\n/** An infinite line as a point and a unit direction. */\ninterface Line {\n  kind: SnapKind;\n  point: PlanPoint;\n  dir: PlanPoint;\n  residual: number;\n  /** Angle lock only: which basis the ray was measured from. */\n  relativeTo?: 'world' | 'reference';\n}\n\n/**\n * Client (screen) coordinates to world coordinates. Pure conversion, no snapping.\n *\n * Uses `getBoundingClientRect` arithmetic against an explicit viewport rather than\n * `getScreenCTM`, which returns `null` in jsdom and so makes anything built on it\n * untestable.\n */\nexport function clientToWorld(\n  clientX: number,\n  clientY: number,\n  rect: { left: number; top: number },\n  view: { x: number; y: number; zoom: number },\n): PlanPoint {\n  return {\n    x: view.x + (clientX - rect.left) / view.zoom,\n    y: view.y + (clientY - rect.top) / view.zoom,\n  };\n}\n\nfunction perpendicularDistanceToLine(p: PlanPoint, line: { point: PlanPoint; dir: PlanPoint }): number {\n  const vx = p.x - line.point.x;\n  const vy = p.y - line.point.y;\n  return Math.abs(vx * line.dir.y - vy * line.dir.x);\n}\n\nfunction projectOnLine(p: PlanPoint, line: { point: PlanPoint; dir: PlanPoint }): PlanPoint {\n  const vx = p.x - line.point.x;\n  const vy = p.y - line.point.y;\n  const along = vx * line.dir.x + vy * line.dir.y;\n  return { x: line.point.x + line.dir.x * along, y: line.point.y + line.dir.y * along };\n}\n\nfunction intersectLines(l1: Line, l2: Line): PlanPoint | null {\n  const denom = l1.dir.x * l2.dir.y - l1.dir.y * l2.dir.x;\n  if (Math.abs(denom) < 1e-9) return null;\n  const dx = l2.point.x - l1.point.x;\n  const dy = l2.point.y - l1.point.y;\n  const t = (dx * l2.dir.y - dy * l2.dir.x) / denom;\n  return { x: l1.point.x + l1.dir.x * t, y: l1.point.y + l1.dir.y * t };\n}\n\nfunction angleBetween(l1: Line, l2: Line): number {\n  const dot = Math.abs(l1.dir.x * l2.dir.x + l1.dir.y * l2.dir.y);\n  return Math.acos(Math.min(1, dot));\n}\n\nfunction roundTo(value: number, step: number): number {\n  return step > 0 ? Math.round(value / step) * step : value;\n}\n\n/**\n * Resolves a raw pointer position to the point the user meant.\n *\n * Two stages, because half of these snaps are constraints rather than points. A hard\n * snap (node, intersection, wall) fixes both degrees of freedom, so the first match\n * wins. A constraint (angle lock, extension, alignment) removes only one, so a flat\n * \"first match wins\" list would kick the point off the angle ray the moment an\n * alignment also matched. Two constraints intersect; one constraint is projected onto\n * and then slid ALONG to the nearest grid position, which is what lets angle lock and\n * grid coexist instead of fighting.\n */\nexport function resolveSnap(raw: PlanPoint, ctx: SnapContext): SnapResult {\n  const options: SnapOptions = { ...DEFAULT_SNAP_OPTIONS, ...ctx.options };\n  const enabled = new Set(options.enabled);\n\n  /**\n   * Measures the resolved point back against the origin, so every branch reports the\n   * same angle and distance without each one having to compute them. Taking them from\n   * the final point rather than from whichever branch produced it means they always\n   * describe what the user actually gets.\n   */\n  const finish = (result: SnapResult): SnapResult => {\n    if (!ctx.origin) return result;\n    const dx = result.point.x - ctx.origin.x;\n    const dy = result.point.y - ctx.origin.y;\n    return { ...result, angle: Math.atan2(dy, dx), distance: Math.hypot(dx, dy) };\n  };\n\n  if (enabled.size === 0) return finish({ point: raw, kind: SnapKind.None, guides: [] });\n\n  const tol = options.toleranceScreenPx * ctx.pxToWorld;\n  const nodeTol = (options.nodeToleranceScreenPx ?? options.toleranceScreenPx) * ctx.pxToWorld;\n  const { floor } = ctx;\n\n  /**\n   * Angle lock, when the caller has asked for it, is a HARD constraint rather than\n   * another proximity snap: `angleStepDeg > 0` only happens because the user latched the\n   * lock or held the modifier. Gating it by tolerance like the ambient snaps would mean\n   * it silently stopped applying as soon as you dragged more than a few pixels off the\n   * ray, which is the opposite of what a lock is for.\n   */\n  const lockedRay: Line | null = (() => {\n    if (!enabled.has(SnapKind.Angle) || !ctx.origin || options.angleStepDeg <= 0) return null;\n    const step = (options.angleStepDeg * Math.PI) / 180;\n    const rawAngle = Math.atan2(raw.y - ctx.origin.y, raw.x - ctx.origin.x);\n\n    // Measure from the world axes, and also from the reference direction when one was\n    // given, so \"square to the wall I am continuing\" is available as well as \"square to\n    // the page\". Whichever ray the pointer is closest to wins.\n    const bases: Array<{ angle: number; relativeTo: 'world' | 'reference' }> = [{ angle: 0, relativeTo: 'world' }];\n    if (ctx.referenceAngle !== undefined) {\n      bases.push({ angle: ctx.referenceAngle, relativeTo: 'reference' });\n    }\n\n    let best: Line | null = null;\n    for (const base of bases) {\n      const snapped = base.angle + Math.round((rawAngle - base.angle) / step) * step;\n      const dir = { x: Math.cos(snapped), y: Math.sin(snapped) };\n      const line: Line = {\n        kind: SnapKind.Angle,\n        point: ctx.origin,\n        dir,\n        residual: 0,\n        relativeTo: base.relativeTo,\n      };\n      line.residual = perpendicularDistanceToLine(raw, line);\n      if (!best || line.residual < best.residual) best = line;\n    }\n    return best;\n  })();\n\n  /**\n   * While the lock is on, a hard snap is only allowed to win if it sits ON the ray.\n   * Otherwise \"hold the angle\" would be quietly overridden by any nearby corner, which\n   * is exactly the case the lock exists to prevent.\n   */\n  const allowedByLock = (p: PlanPoint): boolean => !lockedRay || perpendicularDistanceToLine(p, lockedRay) <= tol;\n\n  // ── Stage A: hard snaps ──────────────────────────────────────────────────────\n\n  if (enabled.has(SnapKind.Node)) {\n    let best: { id: string; point: PlanPoint; d: number } | null = null;\n    for (const node of Object.values(floor.nodes)) {\n      if (ctx.excludeNodeIds?.has(node.id)) continue;\n      const d = dist(raw, node);\n      if (d <= nodeTol && allowedByLock(node) && (!best || d < best.d)) {\n        best = { id: node.id, point: { x: node.x, y: node.y }, d };\n      }\n    }\n    if (best) {\n      return finish({ point: best.point, kind: SnapKind.Node, nodeId: best.id, guides: [] });\n    }\n  }\n\n  const walls = Object.values(floor.walls).filter((w) => !ctx.excludeWallIds?.has(w.id));\n\n  if (enabled.has(SnapKind.Intersection)) {\n    let best: { point: PlanPoint; d: number } | null = null;\n    for (let i = 0; i < walls.length; i++) {\n      for (let j = i + 1; j < walls.length; j++) {\n        const a = floor.nodes[walls[i].a];\n        const b = floor.nodes[walls[i].b];\n        const c = floor.nodes[walls[j].a];\n        const d2 = floor.nodes[walls[j].b];\n        if (!a || !b || !c || !d2) continue;\n        const hit = segmentIntersection(a, b, c, d2);\n        if (!hit.point) continue;\n        const d = dist(raw, hit.point);\n        if (d <= tol && allowedByLock(hit.point) && (!best || d < best.d)) {\n          best = { point: hit.point, d };\n        }\n      }\n    }\n    if (best) return finish({ point: best.point, kind: SnapKind.Intersection, guides: [] });\n  }\n\n  if (enabled.has(SnapKind.Wall)) {\n    let best: { wallId: string; point: PlanPoint; t: number; d: number } | null = null;\n    for (const wall of walls) {\n      const a = floor.nodes[wall.a];\n      const b = floor.nodes[wall.b];\n      if (!a || !b) continue;\n      const r = pointToSegment(raw, a, b);\n      if (r.dist <= tol && allowedByLock(r.point) && (!best || r.dist < best.d)) {\n        // Project exactly: intent is allowed to be approximate, the model is not.\n        best = { wallId: wall.id, point: projectOnSegment(raw, a, b), t: r.t, d: r.dist };\n      }\n    }\n    if (best) {\n      return finish({\n        point: best.point,\n        kind: SnapKind.Wall,\n        wallId: best.wallId,\n        t: best.t,\n        guides: [],\n      });\n    }\n  }\n\n  // ── Stage B: constraints ─────────────────────────────────────────────────────\n\n  const constraints: Line[] = [];\n\n  if (enabled.has(SnapKind.Extension)) {\n    let best: Line | null = null;\n    for (const wall of walls) {\n      const a = floor.nodes[wall.a];\n      const b = floor.nodes[wall.b];\n      if (!a || !b) continue;\n      const len = dist(a, b);\n      if (len < 1e-6) continue;\n      const dir = { x: (b.x - a.x) / len, y: (b.y - a.y) / len };\n      const line: Line = { kind: SnapKind.Extension, point: { x: a.x, y: a.y }, dir, residual: 0 };\n      line.residual = perpendicularDistanceToLine(raw, line);\n      // Only beyond the segment itself; on the segment, the wall snap already applies.\n      const onSegment = pointToSegment(raw, a, b).dist <= tol;\n      if (line.residual <= tol && !onSegment && (!best || line.residual < best.residual)) {\n        best = line;\n      }\n    }\n    if (best) constraints.push(best);\n  }\n\n  if (enabled.has(SnapKind.Alignment)) {\n    let bestV: Line | null = null;\n    let bestH: Line | null = null;\n    for (const node of Object.values(floor.nodes)) {\n      if (ctx.excludeNodeIds?.has(node.id)) continue;\n      const dx = Math.abs(raw.x - node.x);\n      if (dx <= tol && (!bestV || dx < bestV.residual)) {\n        bestV = {\n          kind: SnapKind.Alignment,\n          point: { x: node.x, y: node.y },\n          dir: { x: 0, y: 1 },\n          residual: dx,\n        };\n      }\n      const dy = Math.abs(raw.y - node.y);\n      if (dy <= tol && (!bestH || dy < bestH.residual)) {\n        bestH = {\n          kind: SnapKind.Alignment,\n          point: { x: node.x, y: node.y },\n          dir: { x: 1, y: 0 },\n          residual: dy,\n        };\n      }\n    }\n    if (bestV) constraints.push(bestV);\n    if (bestH) constraints.push(bestH);\n  }\n\n  const guideFor = (line: Line, through: PlanPoint): SnapGuide => ({\n    kind: line.kind,\n    a: { x: through.x - line.dir.x * 1e5, y: through.y - line.dir.y * 1e5 },\n    b: { x: through.x + line.dir.x * 1e5, y: through.y + line.dir.y * 1e5 },\n    infinite: true,\n  });\n\n  if (lockedRay) {\n    // The ray is fixed; the only freedom left is where along it the point sits. Another\n    // constraint that genuinely crosses it pins that down, which is the useful\n    // \"45 degrees AND level with that corner\" case.\n    const crossing = constraints\n      .filter((c) => c.residual <= tol && angleBetween(lockedRay, c) > (15 * Math.PI) / 180)\n      .sort((a, b) => a.residual - b.residual)[0];\n\n    if (crossing) {\n      const point = intersectLines(lockedRay, crossing);\n      if (point && dist(point, raw) <= tol * 4) {\n        return finish({\n          point,\n          kind: SnapKind.Angle,\n          guides: [guideFor(lockedRay, point), guideFor(crossing, point)],\n          angleRelativeTo: lockedRay.relativeTo,\n        });\n      }\n    }\n\n    let point = projectOnLine(raw, lockedRay);\n    if (enabled.has(SnapKind.Grid) && options.gridSize > 0) {\n      // Slide along the ray to a round length, rather than rounding both coordinates,\n      // which would take the point straight back off it.\n      const along = (point.x - lockedRay.point.x) * lockedRay.dir.x + (point.y - lockedRay.point.y) * lockedRay.dir.y;\n      const snappedAlong = roundTo(along, options.gridSize);\n      point = {\n        x: lockedRay.point.x + lockedRay.dir.x * snappedAlong,\n        y: lockedRay.point.y + lockedRay.dir.y * snappedAlong,\n      };\n    }\n    return finish({\n      point,\n      kind: SnapKind.Angle,\n      guides: [guideFor(lockedRay, point)],\n      angleRelativeTo: lockedRay.relativeTo,\n    });\n  }\n\n  if (constraints.length >= 2) {\n    const sorted = [...constraints].sort((a, b) => a.residual - b.residual);\n    const [l1, l2] = sorted;\n    const point = intersectLines(l1, l2);\n    // Two nearly parallel constraints intersect somewhere useless and far away.\n    if (point && angleBetween(l1, l2) > (15 * Math.PI) / 180 && dist(point, raw) <= tol * 2) {\n      return finish({\n        point,\n        kind: SnapKind.Intersection,\n        guides: [guideFor(l1, point), guideFor(l2, point)],\n      });\n    }\n  }\n\n  if (constraints.length >= 1) {\n    const line = [...constraints].sort((a, b) => a.residual - b.residual)[0];\n    let point = projectOnLine(raw, line);\n    if (enabled.has(SnapKind.Grid) && options.gridSize > 0) {\n      // Slide along the constraint to a round distance, rather than rounding both\n      // coordinates, which would take the point back off the line.\n      const along = (point.x - line.point.x) * line.dir.x + (point.y - line.point.y) * line.dir.y;\n      const snappedAlong = roundTo(along, options.gridSize);\n      point = {\n        x: line.point.x + line.dir.x * snappedAlong,\n        y: line.point.y + line.dir.y * snappedAlong,\n      };\n    }\n    return finish({\n      point,\n      kind: line.kind,\n      guides: [guideFor(line, point)],\n      angleRelativeTo: line.relativeTo,\n    });\n  }\n\n  if (enabled.has(SnapKind.Grid) && options.gridSize > 0) {\n    return finish({\n      point: { x: roundTo(raw.x, options.gridSize), y: roundTo(raw.y, options.gridSize) },\n      kind: SnapKind.Grid,\n      guides: [],\n    });\n  }\n\n  return finish({ point: raw, kind: SnapKind.None, guides: [] });\n}\n"],"mappings":"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"}