{"version":3,"file":"SetSquare-Ce05Q5wO.cjs","sources":["../app/components/setsquare/SetSquare.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport RotateHandle from \"../ui/RotateHandle\";\nimport { useWindowStore } from \"../windows/windowStore\";\n\ntype SetSquareProps = {\n  windowId?: string;\n  sizePx?: number; // outer square side\n  angleDeg?: 45 | 30; // 45-45-90 or 30-60-90 set square\n};\n\nexport default function SetSquare({\n  windowId,\n  sizePx = 320,\n  angleDeg = 45,\n}: SetSquareProps) {\n  const containerRef = useRef<HTMLDivElement | null>(null);\n  const S = sizePx;\n  const margin = Math.max(8, S * 0.03);\n  const borderW = Math.max(1, S * 0.004);\n  const faceOpacity = 0.06;\n  const labelFont = Math.max(10, S * 0.035);\n\n  // Physical baseline (match ruler): 1in = 96 CSS px\n  const pxPerInch = 96;\n  const pxPerCm = pxPerInch / 2.54;\n  const pxPerMm = pxPerCm / 10;\n\n  // Leg lengths by variant: 45° => equal legs; 30° => height ≈ base/√3\n  const legX = S;\n  const legY = angleDeg === 45 ? S : Math.round(S / Math.sqrt(3));\n\n  // Tick lengths and strokes to match ruler visuals\n  const tickLenCm = Math.max(10, S * 0.065);\n  const tickLenHalf = Math.max(8, tickLenCm * 0.66);\n  const tickLenMm = Math.max(6, tickLenCm * 0.5);\n  const swMajor = 1.2;\n  const swMedium = 1.0;\n  const swMinor = 0.8;\n\n  const [size, setSize] = useState({\n    width: S + margin * 2,\n    height: S + margin * 2,\n  });\n  const rotationDeg = useWindowStore((s) =>\n    windowId ? s.windows[windowId]?.rotationDeg ?? 0 : 0\n  );\n  const updateRotation = useWindowStore((s) => s.updateRotation);\n\n  useEffect(() => {\n    const el = containerRef.current;\n    if (!el) return;\n    const obs = new ResizeObserver((entries) => {\n      const cr = entries[0]?.contentRect;\n      if (!cr) return;\n      setSize((s) => ({\n        width: Math.max(cr.width, S + margin * 2),\n        height: s.height,\n      }));\n    });\n    obs.observe(el);\n    return () => obs.disconnect();\n  }, [S, margin]);\n\n  // Define triangle points (right triangle aligned with bottom-left baseline)\n  const x0 = margin; // right angle corner\n  const y0 = margin + legY;\n  const x1 = margin + legX; // base end\n  const y1 = margin + legY;\n  const x2 = margin; // vertical end\n  const y2 = margin;\n\n  // Inner cutout: inset a small right triangle near the right angle\n  const frame = Math.max(12, S * 0.1);\n  const innerB_x = Math.min(x1 - 1, x0 + frame);\n  const innerB_y = y0;\n  const innerL_x = x0;\n  const innerL_y = Math.max(y2 + 1, y0 - frame);\n  const innerC_x = innerB_x;\n  const innerC_y = innerL_y;\n\n  // Build cm/mm ticks along legs based on pxPerMm (match ruler)\n  const legTicks = useMemo(() => {\n    type Kind = \"cm\" | \"halfCm\" | \"mm\";\n    const bottom: { x: number; y: number; kind: Kind; label?: string }[] = [];\n    const left: { x: number; y: number; kind: Kind; label?: string }[] = [];\n    const stepsBottom = Math.floor(legX / pxPerMm);\n    for (let i = 0; i <= stepsBottom; i++) {\n      const pos = i * pxPerMm;\n      const x = x0 + pos;\n      const isCm = i % 10 === 0;\n      const isHalf = !isCm && i % 5 === 0;\n      const kind: Kind = isCm ? \"cm\" : isHalf ? \"halfCm\" : \"mm\";\n      bottom.push({\n        x,\n        y: y0,\n        kind,\n        label: isCm ? `${i / 10}`.replace(/\\.0$/, \"\") : undefined,\n      });\n    }\n    const stepsLeft = Math.floor(legY / pxPerMm);\n    for (let i = 0; i <= stepsLeft; i++) {\n      const pos = i * pxPerMm;\n      const y = y0 - pos;\n      const isCm = i % 10 === 0;\n      const isHalf = !isCm && i % 5 === 0;\n      const kind: Kind = isCm ? \"cm\" : isHalf ? \"halfCm\" : \"mm\";\n      left.push({\n        x: x0,\n        y,\n        kind,\n        label: isCm ? `${i / 10}`.replace(/\\.0$/, \"\") : undefined,\n      });\n    }\n    return { bottom, left };\n  }, [legX, legY, pxPerMm, x0, y0]);\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"relative h-full w-full select-none text-foreground\"\n      style={{ background: \"transparent\" }}\n      aria-label=\"Set Square\"\n    >\n      {windowId && (\n        <RotateHandle\n          className=\"no-drag absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 h-6 w-6 rounded-full bg-card/70 border border-border shadow\"\n          title=\"Rotate\"\n          initialDeg={rotationDeg}\n          getCenter={() => {\n            const root = containerRef.current;\n            if (!root) return null;\n            const rect = root.getBoundingClientRect();\n            return {\n              x: rect.left + rect.width / 2,\n              y: rect.top + rect.height / 2,\n            };\n          }}\n          onChange={(deg) => updateRotation(windowId, deg)}\n        />\n      )}\n      <svg\n        width={size.width}\n        height={size.height}\n        role=\"img\"\n        aria-label=\"Set square graphic\"\n        style={{ display: \"block\" }}\n      >\n        <g shapeRendering=\"geometricPrecision\">\n          {/* Outer triangle with inner cutout using even-odd rule */}\n          <path\n            d={`M ${x0} ${y0} L ${x1} ${y1} L ${x2} ${y2} Z M ${innerC_x} ${innerC_y} L ${innerB_x} ${innerB_y} L ${innerL_x} ${innerL_y} Z`}\n            fill={`rgba(255,255,255,${faceOpacity})`}\n            fillRule=\"evenodd\"\n            stroke=\"currentColor\"\n            strokeOpacity={0.6}\n            strokeWidth={borderW}\n          />\n\n          {/* Baseline guide */}\n          <line\n            x1={x0}\n            y1={y0}\n            x2={x1}\n            y2={y1}\n            stroke=\"currentColor\"\n            strokeOpacity={0.35}\n            strokeWidth={borderW}\n          />\n          {/* Vertical guide */}\n          <line\n            x1={x0}\n            y1={y0}\n            x2={x2}\n            y2={y2}\n            stroke=\"currentColor\"\n            strokeOpacity={0.35}\n            strokeWidth={borderW}\n          />\n\n          {/* Ticks on bottom leg (cm/5mm/mm) */}\n          <g\n            vectorEffect=\"non-scaling-stroke\"\n            shapeRendering=\"crispEdges\"\n            strokeLinecap=\"butt\"\n          >\n            {legTicks.bottom.map((t, i) => {\n              const len =\n                t.kind === \"cm\"\n                  ? tickLenCm\n                  : t.kind === \"halfCm\"\n                  ? tickLenHalf\n                  : tickLenMm;\n              return (\n                <line\n                  key={`b-${i}`}\n                  x1={t.x}\n                  y1={t.y}\n                  x2={t.x}\n                  y2={t.y - len}\n                  stroke=\"currentColor\"\n                  strokeOpacity={0.8}\n                  strokeWidth={\n                    t.kind === \"cm\"\n                      ? swMajor\n                      : t.kind === \"halfCm\"\n                      ? swMedium\n                      : swMinor\n                  }\n                />\n              );\n            })}\n          </g>\n\n          {/* Ticks on left leg (cm/5mm/mm) */}\n          <g\n            vectorEffect=\"non-scaling-stroke\"\n            shapeRendering=\"crispEdges\"\n            strokeLinecap=\"butt\"\n          >\n            {legTicks.left.map((t, i) => {\n              const len =\n                t.kind === \"cm\"\n                  ? tickLenCm\n                  : t.kind === \"halfCm\"\n                  ? tickLenHalf\n                  : tickLenMm;\n              return (\n                <line\n                  key={`l-${i}`}\n                  x1={t.x}\n                  y1={t.y}\n                  x2={t.x + len}\n                  y2={t.y}\n                  stroke=\"currentColor\"\n                  strokeOpacity={0.8}\n                  strokeWidth={\n                    t.kind === \"cm\"\n                      ? swMajor\n                      : t.kind === \"halfCm\"\n                      ? swMedium\n                      : swMinor\n                  }\n                />\n              );\n            })}\n          </g>\n\n          {/* Labels on legs at each cm (skip 0) */}\n          {legTicks.bottom.map((t, i) => {\n            if (t.kind !== \"cm\" || t.label === \"0\") return null;\n            return (\n              <text\n                key={`bl-${i}`}\n                x={t.x}\n                y={t.y - tickLenCm - 6}\n                fontSize={labelFont}\n                textAnchor=\"middle\"\n                dominantBaseline=\"text-after-edge\"\n                fill=\"currentColor\"\n                fillOpacity={0.85}\n              >\n                {t.label}\n              </text>\n            );\n          })}\n          {legTicks.left.map((t, i) => {\n            if (t.kind !== \"cm\" || t.label === \"0\") return null;\n            return (\n              <text\n                key={`ll-${i}`}\n                x={t.x + tickLenCm + 6}\n                y={t.y}\n                fontSize={labelFont}\n                textAnchor=\"start\"\n                dominantBaseline=\"middle\"\n                fill=\"currentColor\"\n                fillOpacity={0.85}\n              >\n                {t.label}\n              </text>\n            );\n          })}\n        </g>\n      </svg>\n    </div>\n  );\n}\n"],"names":["SetSquare","windowId","sizePx","angleDeg","containerRef","useRef","S","margin","borderW","faceOpacity","labelFont","pxPerMm","legX","legY","tickLenCm","tickLenHalf","tickLenMm","swMajor","swMedium","swMinor","size","setSize","useState","rotationDeg","useWindowStore","s","updateRotation","useEffect","el","obs","entries","cr","x0","y0","x1","y1","x2","y2","frame","innerB_x","innerB_y","innerL_x","innerL_y","innerC_x","innerC_y","legTicks","useMemo","bottom","left","stepsBottom","i","pos","x","isCm","isHalf","kind","stepsLeft","y","jsxs","jsx","RotateHandle","root","rect","deg","t","len"],"mappings":"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"}