{"version":3,"file":"planCanvas.mjs","names":[],"sources":["../../../../package/common/planCanvas/planCanvas.tsx"],"sourcesContent":["import { useMemo, type ReactElement } from 'react';\nimport cx from 'classnames';\n\nimport {\n  OpeningKind,\n  buildWallPaths,\n  openingGeometry,\n  type DerivedRoom,\n  type PlanOpening,\n  type PlanPoint,\n  type PlanSelection,\n} from '../floorPlan';\nimport type { PlanCanvasProps } from './planCanvas.types';\nimport styles from './planCanvas.module.pcss';\n\n/**\n * The read-only renderer shared by RoomDrawer and RoomViewer.\n *\n * It owns the projection, the layer stack, and the accessible room shapes. It owns no\n * editing state: the drawer supplies its handles and previews through `overlay`. Having\n * one renderer is the point, so that a plan cannot look different in the editor than it\n * does in the viewer.\n *\n * The viewport is a TRANSFORM on a group, not an SVG `viewBox`. That is what lets the\n * screen-space layer draw labels and handles at a fixed pixel size while the drawing\n * itself scales.\n */\n\nconst DEFAULT_OUTLINE_PX = 1;\n\n/** Stable identity, so a default-valued `selection` prop does not re-run memos. */\nconst EMPTY_SELECTION: readonly PlanSelection[] = [];\n\nfunction formatAreaDefault(areaMm2: number): string {\n  const m2 = areaMm2 / 1_000_000;\n  return `${m2.toFixed(m2 < 10 ? 1 : 0)} m²`;\n}\n\nfunction ringToPath(ring: PlanPoint[]): string {\n  if (ring.length === 0) return '';\n  return `${ring.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x},${p.y}`).join(' ')} Z`;\n}\n\n/** Outer ring plus holes as one path; `fill-rule: evenodd` punches the holes out. */\nfunction faceToPath(face: DerivedRoom): string {\n  return [ringToPath(face.outer), ...face.holes.map(ringToPath)].filter(Boolean).join(' ');\n}\n\n/**\n * The ref lands on the `<svg>` itself, which is what a consumer wants for measuring or\n * screenshotting. Both the drawer and the viewer expose it as `getSvgElement()`.\n */\nfunction PlanCanvas(props: PlanCanvasProps) {\n  const {\n    ref,\n    floor,\n    underlay,\n    rooms,\n    view,\n    selection = EMPTY_SELECTION,\n    hovered = null,\n    interactiveRooms = false,\n    interactiveOpenings = false,\n    showGrid = false,\n    gridSize = 100,\n    showRoomLabels = true,\n    showOpeningSymbols = true,\n    getRoomColor,\n    renderRoomLabel,\n    getRoomLabel,\n    formatArea = formatAreaDefault,\n    onRoomActivate,\n    onOpeningActivate,\n    onBackgroundActivate,\n    onHoverChange,\n    overlay,\n    worldOverlay,\n    size,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    'aria-label': ariaLabel,\n  } = props;\n\n  const pxToWorld = view.zoom > 0 ? 1 / view.zoom : 1;\n  const outlineWidth = DEFAULT_OUTLINE_PX * pxToWorld;\n\n  const wallPaths = useMemo(() => buildWallPaths(floor), [floor]);\n\n  /**\n   * Centrelines only, at one stroke width. The underlay is there to line walls up\n   * against, so it wants to read as a tracing rather than compete with the floor being\n   * drawn; the two-pass thick-wall treatment would give it equal weight.\n   */\n  const underlayLines = useMemo(() => {\n    if (!underlay) return [];\n    return Object.values(underlay.walls).flatMap((wall) => {\n      const a = underlay.nodes[wall.a];\n      const b = underlay.nodes[wall.b];\n      return a && b ? [{ id: wall.id, x1: a.x, y1: a.y, x2: b.x, y2: b.y }] : [];\n    });\n  }, [underlay]);\n\n  /** World point to screen point, for the untransformed layer. */\n  const toScreen = (p: PlanPoint) => ({\n    x: (p.x - view.x) * view.zoom,\n    y: (p.y - view.y) * view.zoom,\n  });\n\n  // Grid lines are generated for the visible rect only. Deliberately not an SVG\n  // <pattern>: a pattern needs an id, and two canvases on one page then collide.\n  const gridLines = useMemo(() => {\n    if (!showGrid || gridSize <= 0) return null;\n    const worldW = size.width * pxToWorld;\n    const worldH = size.height * pxToWorld;\n    // Keep the line count sane when zoomed far out.\n    const step = gridSize * Math.max(1, Math.pow(10, Math.ceil(Math.log10(worldW / gridSize / 60))));\n    const startX = Math.floor(view.x / step) * step;\n    const startY = Math.floor(view.y / step) * step;\n    const verticals: number[] = [];\n    const horizontals: number[] = [];\n    for (let x = startX; x <= view.x + worldW; x += step) verticals.push(x);\n    for (let y = startY; y <= view.y + worldH; y += step) horizontals.push(y);\n    return { verticals, horizontals, step, worldW, worldH };\n  }, [showGrid, gridSize, size.width, size.height, pxToWorld, view.x, view.y]);\n\n  const isSelected = (type: string, id: string) => selection.some((entry) => entry.type === type && entry.id === id);\n  const isHovered = (type: string, id: string) => hovered?.type === type && hovered.id === id;\n\n  /**\n   * The walls bounding the selected rooms. A SELECTED room is marked by colouring its\n   * boundary rather than by flooding its floor, which would bury its doors and its label;\n   * hover keeps the faint floor tint instead, since the two say different things.\n   */\n  const highlightedWalls = useMemo(() => {\n    const selectedRooms = new Set(selection.filter((entry) => entry.type === 'room').map((entry) => entry.id));\n    const ids = new Set<string>();\n    for (const room of rooms) {\n      if (room.roomId && selectedRooms.has(room.roomId)) {\n        // A wall shared by two selected rooms belongs to both; the set draws it once.\n        for (const id of room.wallIds) ids.add(id);\n      }\n    }\n    return ids;\n  }, [rooms, selection]);\n\n  const roomEdge = useMemo(\n    () => (highlightedWalls.size > 0 ? buildWallPaths(floor, { wallIds: highlightedWalls }) : { groups: [], caps: [] }),\n    [floor, highlightedWalls],\n  );\n\n  function renderOpening(opening: PlanOpening) {\n    const geo = openingGeometry(floor, opening);\n    if (!geo) return null;\n    const wall = floor.walls[opening.wallId];\n    const half = wall ? wall.thickness / 2 : 50;\n    const { p1, p2, normal, dir, centre } = geo;\n\n    const selected = isSelected('opening', opening.id);\n    const hoveredNow = isHovered('opening', opening.id);\n    const width = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n\n    const parts: ReactElement[] = [];\n\n    if (showOpeningSymbols && opening.kind === OpeningKind.Door) {\n      // Leaf plus swing arc, drawn on the wall's left side.\n      const hingeAtStart = opening.swing !== 'endLeft' && opening.swing !== 'endRight';\n      const hinge = hingeAtStart ? p1 : p2;\n      const sweepDir = hingeAtStart ? 1 : -1;\n      const leafEnd = { x: hinge.x + normal.x * width, y: hinge.y + normal.y * width };\n      const arcEnd = {\n        x: hinge.x + dir.x * width * sweepDir,\n        y: hinge.y + dir.y * width * sweepDir,\n      };\n      parts.push(\n        <path\n          key='leaf'\n          className={styles.planCanvasDoorLeaf}\n          d={`M ${hinge.x},${hinge.y} L ${leafEnd.x},${leafEnd.y}`}\n          strokeWidth={outlineWidth}\n        />,\n        <path\n          key='swing'\n          className={styles.planCanvasDoorSwing}\n          d={`M ${leafEnd.x},${leafEnd.y} A ${width},${width} 0 0,${hingeAtStart ? 0 : 1} ${arcEnd.x},${arcEnd.y}`}\n          strokeWidth={outlineWidth}\n        />,\n      );\n    }\n\n    if (showOpeningSymbols && opening.kind === OpeningKind.Window) {\n      // A pane line down the middle of the wall's thickness.\n      parts.push(\n        <path\n          key='pane'\n          className={styles.planCanvasWindowPane}\n          d={`M ${p1.x},${p1.y} L ${p2.x},${p2.y}`}\n          strokeWidth={outlineWidth * 2}\n        />,\n      );\n    }\n\n    const hitPath = `M ${p1.x + normal.x * half},${p1.y + normal.y * half} L ${\n      p2.x + normal.x * half\n    },${p2.y + normal.y * half} L ${p2.x - normal.x * half},${p2.y - normal.y * half} L ${\n      p1.x - normal.x * half\n    },${p1.y - normal.y * half} Z`;\n\n    return (\n      <g\n        key={opening.id}\n        className={cx(\n          selected && styles.planCanvasOpeningSelected,\n          hoveredNow && styles.planCanvasOpeningHovered,\n          classNames?.opening,\n        )}\n        style={slotStyles?.opening}\n        data-opening-kind={opening.kind}\n      >\n        {parts}\n        <path\n          className={cx(styles.planCanvasOpeningHit, interactiveOpenings && styles.planCanvasOpeningHitInteractive)}\n          d={hitPath}\n          onClick={\n            interactiveOpenings && onOpeningActivate\n              ? (e) => {\n                  e.stopPropagation();\n                  onOpeningActivate(opening.id);\n                }\n              : undefined\n          }\n          onPointerEnter={onHoverChange ? () => onHoverChange({ type: 'opening', id: opening.id }) : undefined}\n        >\n          {interactiveOpenings && <title>{opening.kind}</title>}\n        </path>\n        {(selected || hoveredNow) && (\n          <path className={styles.planCanvasOpeningOutline} d={hitPath} vectorEffect='non-scaling-stroke' />\n        )}\n        {/* The jamb faces, so an opening reads as a hole with sides rather than a blank. */}\n        <path\n          className={cx(\n            styles.planCanvasJamb,\n            // The jamb face belongs to the wall it is cut into, so it follows that wall\n            // into the highlight rather than staying white against it.\n            highlightedWalls.has(opening.wallId) && styles.planCanvasJambHighlighted,\n          )}\n          d={`M ${p1.x - normal.x * half},${p1.y - normal.y * half} L ${p1.x + normal.x * half},${\n            p1.y + normal.y * half\n          } M ${p2.x - normal.x * half},${p2.y - normal.y * half} L ${p2.x + normal.x * half},${\n            p2.y + normal.y * half\n          }`}\n          strokeWidth={outlineWidth}\n        />\n        {centre && null}\n      </g>\n    );\n  }\n\n  return (\n    <svg\n      ref={ref}\n      className={cx(styles.planCanvas, className, classNames?.canvas)}\n      style={{ ...style, ...slotStyles?.canvas }}\n      viewBox={`0 0 ${Math.max(1, size.width)} ${Math.max(1, size.height)}`}\n      width={size.width}\n      height={size.height}\n      role='img'\n      aria-label={ariaLabel}\n      onClick={onBackgroundActivate ? () => onBackgroundActivate() : undefined}\n      onPointerLeave={onHoverChange ? () => onHoverChange(null) : undefined}\n    >\n      <g transform={`translate(${-view.x * view.zoom} ${-view.y * view.zoom}) scale(${view.zoom})`}>\n        {/* 1. Grid */}\n        {gridLines && (\n          <g className={cx(styles.planCanvasGrid, classNames?.grid)} style={slotStyles?.grid} aria-hidden='true'>\n            {gridLines.verticals.map((x) => (\n              <line key={`v${x}`} x1={x} y1={view.y} x2={x} y2={view.y + gridLines.worldH} strokeWidth={pxToWorld} />\n            ))}\n            {gridLines.horizontals.map((y) => (\n              <line key={`h${y}`} x1={view.x} y1={y} x2={view.x + gridLines.worldW} y2={y} strokeWidth={pxToWorld} />\n            ))}\n          </g>\n        )}\n\n        {/* 1b. The storey below, traced */}\n        {underlayLines.length > 0 && (\n          <g\n            className={cx(styles.planCanvasUnderlay, classNames?.underlay)}\n            style={slotStyles?.underlay}\n            aria-hidden='true'\n          >\n            {/* Screen units: the rule sets vector-effect: non-scaling-stroke, so the width\n                is read in screen pixels rather than world millimetres. */}\n            {underlayLines.map((line) => (\n              <line key={line.id} x1={line.x1} y1={line.y1} x2={line.x2} y2={line.y2} strokeWidth={1.5} />\n            ))}\n          </g>\n        )}\n\n        {/* 2. Room fills — the only hit target for room selection */}\n        <g className={cx(styles.planCanvasRooms, classNames?.rooms)} style={slotStyles?.rooms}>\n          {rooms.map((room) => {\n            const selected = room.roomId ? isSelected('room', room.roomId) : false;\n            const hoveredNow = room.roomId ? isHovered('room', room.roomId) : false;\n            const colour = room.meta ? getRoomColor?.(room.meta, room.area) : undefined;\n            const label = getRoomLabel?.(room.meta, { area: room.area });\n\n            return (\n              <path\n                key={room.id}\n                className={cx(\n                  styles.planCanvasRoomFill,\n                  hoveredNow && styles.planCanvasRoomFillHovered,\n                  interactiveRooms && styles.planCanvasRoomFillInteractive,\n                  classNames?.roomFill,\n                )}\n                data-state={selected ? 'selected' : hoveredNow ? 'hovered' : undefined}\n                style={{ ...slotStyles?.roomFill, ...(colour ? { fill: colour } : null) }}\n                d={faceToPath(room)}\n                fillRule='evenodd'\n                tabIndex={interactiveRooms ? 0 : undefined}\n                role={interactiveRooms ? 'button' : undefined}\n                aria-pressed={interactiveRooms ? selected : undefined}\n                onClick={\n                  interactiveRooms && onRoomActivate && room.roomId\n                    ? (e) => {\n                        e.stopPropagation();\n                        onRoomActivate(room.roomId!);\n                      }\n                    : undefined\n                }\n                onKeyDown={\n                  interactiveRooms && onRoomActivate && room.roomId\n                    ? (e) => {\n                        if (e.key !== 'Enter' && e.key !== ' ') return;\n                        e.preventDefault();\n                        onRoomActivate(room.roomId!);\n                      }\n                    : undefined\n                }\n                onPointerEnter={\n                  onHoverChange && room.roomId ? () => onHoverChange({ type: 'room', id: room.roomId! }) : undefined\n                }\n              >\n                {/* Both a <title> and the aria-label: aria-label on SVG shapes is\n                    unreliable in older Safari/VoiceOver, and <title> doubles as the\n                    native tooltip. */}\n                {interactiveRooms && label && <title>{label}</title>}\n              </path>\n            );\n          })}\n        </g>\n\n        {/* 3. Wall bodies — outline pass, then fill pass. Never interleaved: the visible\n            outline is what is left of the first pass after the second covers its middle. */}\n        <g className={cx(styles.planCanvasWalls, classNames?.walls)} style={slotStyles?.walls} aria-hidden='true'>\n          <g className={styles.planCanvasWallOutline}>\n            {wallPaths.groups.map((group) => (\n              <path\n                key={`o${group.thickness}`}\n                className={classNames?.wallBody}\n                style={slotStyles?.wallBody}\n                d={group.d}\n                strokeWidth={group.thickness}\n              />\n            ))}\n          </g>\n          <g className={styles.planCanvasWallFill}>\n            {wallPaths.groups.map((group) => {\n              const fillWidth = group.thickness - 2 * outlineWidth;\n              // Zoomed far out the fill would invert; walls collapsing to solid lines is\n              // the right look at that scale.\n              if (fillWidth <= 0) return null;\n              return <path key={`f${group.thickness}`} d={group.d} strokeWidth={fillWidth} />;\n            })}\n          </g>\n          <g className={styles.planCanvasRoomEdgeSelected}>\n            {/* The same two passes the walls themselves use, on the same path data: an\n                outline at full thickness, then a fill inside it. Recolouring only the body\n                left the wall edged in its normal colour, so it did not read as one object. */}\n            {roomEdge.groups.map((group) => (\n              <path\n                key={`o${group.thickness}`}\n                className={styles.planCanvasRoomEdgeOutline}\n                d={group.d}\n                strokeWidth={group.thickness}\n              />\n            ))}\n            {roomEdge.groups.map((group) => {\n              const fillWidth = group.thickness - 2 * outlineWidth;\n              if (fillWidth <= 0) return null;\n              return (\n                <path\n                  key={`f${group.thickness}`}\n                  className={styles.planCanvasRoomEdgeFill}\n                  d={group.d}\n                  strokeWidth={fillWidth}\n                />\n              );\n            })}\n          </g>\n\n          <g className={styles.planCanvasWallCaps}>\n            {/* A highlighted wall's caps are drawn below in the highlight colour. Skipped\n                here rather than painted over: an antialiased hairline is never fully\n                covered, and the two would blend into a pale seam. */}\n            {wallPaths.caps\n              .filter((cap) => !highlightedWalls.has(cap.wallId))\n              .map((cap, i) => (\n                <line\n                  key={`${cap.wallId}-${cap.reason}-${i}`}\n                  x1={cap.a.x}\n                  y1={cap.a.y}\n                  x2={cap.b.x}\n                  y2={cap.b.y}\n                  strokeWidth={outlineWidth}\n                />\n              ))}\n          </g>\n          <g className={styles.planCanvasRoomEdgeCaps}>\n            {roomEdge.caps.map((cap, i) => (\n              <line\n                key={`${cap.wallId}-${cap.reason}-${i}`}\n                x1={cap.a.x}\n                y1={cap.a.y}\n                x2={cap.b.x}\n                y2={cap.b.y}\n                strokeWidth={outlineWidth}\n              />\n            ))}\n          </g>\n        </g>\n\n        {/* 4. Openings */}\n        <g className={cx(styles.planCanvasOpenings, classNames?.openings)} style={slotStyles?.openings}>\n          {Object.values(floor.openings).map(renderOpening)}\n        </g>\n\n        {/* 5. Selection / hover outlines for walls and nodes */}\n        <g className={styles.planCanvasSelection} aria-hidden='true'>\n          {selection\n            .filter((entry) => entry.type === 'wall')\n            .map((entry) => {\n              const wall = floor.walls[entry.id];\n              if (!wall) return null;\n              const a = floor.nodes[wall.a];\n              const b = floor.nodes[wall.b];\n              if (!a || !b) return null;\n              return (\n                <line\n                  key={entry.id}\n                  x1={a.x}\n                  y1={a.y}\n                  x2={b.x}\n                  y2={b.y}\n                  strokeWidth={wall.thickness}\n                  className={styles.planCanvasWallSelected}\n                />\n              );\n            })}\n          {hovered?.type === 'wall' &&\n            !isSelected('wall', hovered.id) &&\n            (() => {\n              const wall = floor.walls[hovered.id];\n              if (!wall) return null;\n              const a = floor.nodes[wall.a];\n              const b = floor.nodes[wall.b];\n              if (!a || !b) return null;\n              return (\n                <line\n                  x1={a.x}\n                  y1={a.y}\n                  x2={b.x}\n                  y2={b.y}\n                  strokeWidth={wall.thickness}\n                  className={styles.planCanvasWallHovered}\n                />\n              );\n            })()}\n        </g>\n\n        {worldOverlay}\n      </g>\n\n      {/* Screen space: sized in pixels, so labels and handles stay constant under zoom */}\n      <g className={cx(styles.planCanvasScreen, classNames?.overlay)} style={slotStyles?.overlay}>\n        {showRoomLabels &&\n          rooms.map((room) => {\n            const at = toScreen(room.anchor);\n            if (at.x < -200 || at.y < -200 || at.x > size.width + 200 || at.y > size.height + 200) {\n              return null;\n            }\n            const custom = renderRoomLabel?.(room.meta, { area: room.area, faceId: room.id });\n            if (custom === null) return null;\n\n            return (\n              <g\n                key={room.id}\n                className={cx(styles.planCanvasRoomLabel, classNames?.roomLabel)}\n                style={slotStyles?.roomLabel}\n                transform={`translate(${at.x} ${at.y})`}\n                aria-hidden='true'\n              >\n                {custom !== undefined ? (\n                  custom\n                ) : (\n                  <>\n                    <text className={styles.planCanvasRoomName} textAnchor='middle'>\n                      {room.meta?.name ?? ''}\n                    </text>\n                    <text className={styles.planCanvasRoomArea} textAnchor='middle' dy='1.2em'>\n                      {formatArea(room.area)}\n                    </text>\n                  </>\n                )}\n              </g>\n            );\n          })}\n        {overlay}\n      </g>\n    </svg>\n  );\n}\n\nexport default PlanCanvas;\n"],"mappings":"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"}