1import{r as u,j as R}from"./index-_Sg_1KT6.js";import{u as te}from"./useCanvas-DRXw8nFR.js";import{E as se}from"./ExperimentControls-DmwC3FZh.js";import{E as re}from"./ExperimentMetrics-D-l2z47K.js";import{E as ne}from"./ExperimentNav-CPfQyQAJ.js";const ce=[{id:"point",label:"point()"},{id:"line",label:"straightedge()"},{id:"circle",label:"compass()"},{id:"intersect",label:"intersect()"}],j=.001,J=15,oe=(l,h)=>Math.hypot(l.x-h.x,l.y-h.y),ae=l=>`${Math.round(l.x/4)}:${Math.round(l.y/4)}`,le=(l,h)=>{const x=l.b.x-l.a.x,t=l.b.y-l.a.y,n=l.a.x-h.c.x,f=l.a.y-h.c.y,g=x*x+t*t,m=2*(n*x+f*t),C=n*n+f*f-h.r*h.r,w=m*m-4*g*C;if(w<-j||g<j)return[];if(Math.abs(w)<j){const I=-m/(2*g);return[{x:l.a.x+x*I,y:l.a.y+t*I}]}const d=Math.sqrt(w),y=(-m-d)/(2*g),E=(-m+d)/(2*g);return[{x:l.a.x+x*y,y:l.a.y+t*y},{x:l.a.x+x*E,y:l.a.y+t*E}]},ie=(l,h)=>{const x=l.a.x,t=l.a.y,n=l.b.x,f=l.b.y,g=h.a.x,m=h.a.y,C=h.b.x,w=h.b.y,d=(x-n)*(m-w)-(t-f)*(g-C);if(Math.abs(d)<j)return null;const y=((x*f-t*n)*(g-C)-(x-n)*(g*w-m*C))/d,E=((x*f-t*n)*(m-w)-(t-f)*(g*w-m*C))/d;return{x:y,y:E}},ue=(l,h)=>{const x=h.c.x-l.c.x,t=h.c.y-l.c.y,n=Math.hypot(x,t);if(n<j||n>l.r+h.r+j||n<Math.abs(l.r-h.r)-j)return[];const f=(l.r*l.r-h.r*h.r+n*n)/(2*n),g=l.r*l.r-f*f;if(g<-j)return[];const m=l.c.x+f*x/n,C=l.c.y+f*t/n;if(Math.abs(g)<j)return[{x:m,y:C}];const w=Math.sqrt(g),d=-t*(w/n),y=x*(w/n);return[{x:m+d,y:C+y},{x:m-d,y:C-y}]},ye=({category:l,experiment:h})=>{const{canvasRef:x,ctx:t,dimensions:n}=te(),[f,g]=u.useState("point"),[m,C]=u.useState(0),[w,d]=u.useState("Euclid wakes in the dark glass"),y=u.useRef([]),E=u.useRef([]),I=u.useRef([]),M=u.useRef([]),B=u.useRef([]),F=u.useRef(0),Y=u.useRef(0),v=u.useCallback(()=>C(e=>e+1),[]),H=u.useCallback(e=>{const r="ABCDEFGHIJKLMNOPQRSTUVWXYZ";return e<r.length?r[e]:`P${e-r.length+1}`},[]),k=u.useCallback((e,r,c="free")=>{const a=y.current.find(o=>Math.hypot(o.x-e,o.y-r)<7);if(a)return a;const i={id:`p-${F.current}`,label:H(F.current),x:e,y:r,kind:c};return F.current++,y.current.push(i),i},[H]),N=u.useCallback(e=>y.current.find(r=>r.id===e),[]),L=u.useCallback(()=>E.current.map(e=>{const r=N(e.a),c=N(e.b);return r&&c?{...e,a:r,b:c}:null}).filter(Boolean),[N]),A=u.useCallback(()=>I.current.map(e=>{const r=N(e.c),c=N(e.edge);return r&&c?{...e,c:r,edge:c,r:oe(r,c)}:null}).filter(Boolean),[N]),S=u.useCallback(()=>{const e=L(),r=A(),c=[],a=new Set,i=(o,s)=>{if(!o||!Number.isFinite(o.x)||!Number.isFinite(o.y)||o.x<-80||o.y<-80||o.x>n.width+80||o.y>n.height+80)return;const b=ae(o);a.has(b)||y.current.some(p=>Math.hypot(p.x-o.x,p.y-o.y)<7)||(a.add(b),c.push({...o,source:s}))};for(let o=0;o<e.length;o++)for(let s=o+1;s<e.length;s++)i(ie(e[o],e[s]),"line-line");e.forEach(o=>{r.forEach(s=>{le(o,s).forEach(b=>i(b,"line-circle"))})});for(let o=0;o<r.length;o++)for(let s=o+1;s<r.length;s++)ue(r[o],r[s]).forEach(b=>i(b,"circle-circle"));return B.current=c,c},[L,A,n.width,n.height]),W=u.useCallback(()=>{if(y.current=[],E.current=[],I.current=[],M.current=[],B.current=[],F.current=0,n.width>0&&n.height>0){const e=n.centerY+n.height*.14,r=Math.min(180,n.width*.22),c=k(n.centerX-r,e,"axiom"),a=k(n.centerX+r,e,"axiom");M.current=[c.id,a.id],d("two axioms placed; choose a construction")}v()},[k,v,n.width,n.height,n.centerX,n.centerY]);u.useEffect(()=>{n.width===0||y.current.length>0||W()},[n.width,W]);const z=u.useCallback((e,r)=>{let c=null,a=J;return y.current.forEach(i=>{const o=Math.hypot(i.x-e,i.y-r);o<a&&(a=o,c=i)}),c},[]),K=u.useCallback((e,r)=>{let c=null,a=J;return B.current.forEach(i=>{const o=Math.hypot(i.x-e,i.y-r);o<a&&(a=o,c=i)}),c},[]),D=u.useCallback(e=>{const r=M.current.filter(c=>c!==e.id);r.push(e.id),M.current=r.slice(-2)},[]),$=u.useCallback((e,r)=>{if(e===r)return;E.current.some(a=>a.a===e&&a.b===r||a.a===r&&a.b===e)||E.current.push({id:`l-${E.current.length}`,a:e,b:r})},[]),q=u.useCallback((e,r)=>{if(e===r)return;I.current.some(a=>a.c===e&&a.edge===r)||I.current.push({id:`c-${I.current.length}`,c:e,edge:r})},[]),X=u.useCallback(e=>{const r=x.current;if(!r)return;const c=r.getBoundingClientRect(),a=e.clientX-c.left,i=e.clientY-c.top,o=z(a,i),s=K(a,i);if(f==="point"){const P=o||k(a,i,"free");D(P),d(`${P.label} marked on the proof-skin`),S(),v();return}if(f==="intersect"){if(!s){d("no unresolved crossing under the paw");return}const P=k(s.x,s.y,"derived");D(P),d(`${P.label} materialized from ${s.source}`),S(),v();return}const b=o||s&&k(s.x,s.y,"derived");if(!b){d("choose existing points, or resolve a crossing first");return}D(b);const[p,T]=M.current;p&&T?(f==="line"?($(p,T),d("straightedge laid across selected stars")):f==="circle"&&(q(p,T),d("compass radius remembered")),S()):d(`${b.label} selected; await the second mark`),v()},[q,$,k,v,x,S,K,z,f,D]);u.useEffect(()=>{const e=x.current;if(e)return e.addEventListener("pointerdown",X),()=>e.removeEventListener("pointerdown",X)},[x,X]);const Q=u.useCallback(()=>{const e=S();let r=0;e.forEach(c=>{k(c.x,c.y,"derived").kind==="derived"&&r++}),M.current=y.current.slice(-2).map(c=>c.id),d(`${r} crossings condensed into selectable points`),v()},[k,v,S]),U=u.useCallback(()=>{const[e,r]=M.current;if(!e||!r){d("select two points before invoking the equilateral rite");return}q(e,r),q(r,e);const a=S().find(i=>i.source==="circle-circle");if(a){const i=k(a.x,a.y,"derived");$(e,i.id),$(r,i.id),M.current=[e,i.id]}d("equilateral proof unfolded from twin compass bites"),v()},[q,$,k,v,S]),V=u.useCallback(()=>{const[e,r]=M.current,c=N(e),a=N(r);if(!c||!a){d("select two points to split the segment");return}const i=k((c.x+a.x)/2,(c.y+a.y)/2,"derived"),o=a.x-c.x,s=a.y-c.y,b=Math.hypot(o,s)||1,p=Math.max(n.width,n.height)*.18,T=k(i.x-s/b*p,i.y+o/b*p,"helper"),P=k(i.x+s/b*p,i.y-o/b*p,"helper");$(T.id,P.id),M.current=[e,i.id],d(`${i.label} bisects the chosen interval`),v()},[$,k,v,n.width,n.height,N]),Z=u.useCallback(()=>{M.current=[],d("selection washed clean"),v()},[v]),O=u.useCallback(e=>{const r=e.b.x-e.a.x,c=e.b.y-e.a.y,a=Math.hypot(r,c);if(a<j)return;const i=r/a,o=c/a,s=Math.max(n.width,n.height)*2;t.beginPath(),t.moveTo(e.a.x-i*s,e.a.y-o*s),t.lineTo(e.a.x+i*s,e.a.y+o*s),t.stroke()},[t,n.width,n.height]),G=u.useCallback(()=>
1{if(!t||n.width===0)return;Y.current++;const e=Y.current*.03,r=y.current,c=L(),a=A(),i=S(),o=new Set(M.current);t.fillStyle="rgba(0, 2, 7, 0.34)",t.fillRect(0,0,n.width,n.height),t.lineWidth=1;for(let s=0;s<n.width;s+=36)t.strokeStyle=s%108===0?"rgba(102, 255, 204, 0.07)":"rgba(102, 255, 204, 0.035)",t.beginPath(),t.moveTo(s,0),t.lineTo(s,n.height),t.stroke();for(let s=0;s<n.height;s+=36)t.strokeStyle=s%108===0?"rgba(102, 255, 204, 0.07)":"rgba(102, 255, 204, 0.035)",t.beginPath(),t.moveTo(0,s),t.lineTo(n.width,s),t.stroke();t.lineWidth=1.25,a.forEach(s=>{t.strokeStyle="rgba(255, 214, 122, 0.38)",t.shadowBlur=10,t.shadowColor="rgba(255, 214, 122, 0.16)",t.beginPath(),t.arc(s.c.x,s.c.y,s.r,0,Math.PI*2),t.stroke()}),t.shadowBlur=8,t.shadowColor="rgba(126, 227, 255, 0.15)",t.strokeStyle="rgba(126, 227, 255, 0.5)",c.forEach(O),t.shadowBlur=0,i.forEach((s,b)=>{const p=.5+Math.sin(e+b)*.25;t.strokeStyle=`rgba(255, 102, 204, ${.45+p*.35})`,t.lineWidth=1,t.beginPath(),t.moveTo(s.x-6,s.y),t.lineTo(s.x+6,s.y),t.moveTo(s.x,s.y-6),t.lineTo(s.x,s.y+6),t.stroke()}),r.forEach(s=>{const b=o.has(s.id),p=s.kind==="derived",T=s.kind==="helper",P=b?7:p?5:T?4:5.5;t.beginPath(),t.arc(s.x,s.y,P,0,Math.PI*2),t.fillStyle=T?"rgba(153, 255, 221, 0.58)":p?"rgba(255, 102, 204, 0.82)":"rgba(230, 255, 154, 0.9)",t.fill(),t.strokeStyle=b?"rgba(255, 255, 255, 0.95)":"rgba(255, 255, 255, 0.42)",t.lineWidth=b?2:1,t.stroke(),b&&(t.beginPath(),t.arc(s.x,s.y,P+6+Math.sin(e*2)*1.5,0,Math.PI*2),t.strokeStyle="rgba(230, 255, 154, 0.35)",t.stroke()),t.fillStyle="rgba(235, 255, 245, 0.74)",t.font='11px "Courier New", monospace',t.textAlign="center",t.fillText(s.label,s.x,s.y-13)})},[S,t,A,L,n.width,n.height,O]);u.useEffect(()=>{if(!t||n.width===0)return;let e;const r=()=>{G(),e=requestAnimationFrame(r)};return e=requestAnimationFrame(r),()=>cancelAnimationFrame(e)},[t,n.width,G]);const _=u.useMemo(()=>[{label:"points",value:y.current.length},{label:"lines",value:E.current.length},{label:"circles",value:I.current.length},{label:"crossings",value:B.current.length}],[m]),ee=[{id:"resolve",label:"resolve.crossings()",onClick:Q},{id:"equilateral",label:"equilateral()",onClick:U},{id:"bisect",label:"bisect()",onClick:V},{id:"clear-selection",label:"clear.selection()",onClick:Z},{id:"reset",label:"reset.slate()",onClick:W,variant:"reset"}];return R.jsxs("div",{className:"fixed inset-0 flex flex-col",children:[R.jsxs("header",{className:"relative z-50 flex items-center justify-between p-2 sm:p-4 border-b border-void-green/20 bg-void-dark/80 backdrop-blur-sm",children:[R.jsxs("div",{className:"flex items-center gap-2 sm:gap-4",children:[R.jsx(ne,{currentCategory:l.slug,currentExperiment:h.slug}),R.jsx("h1",{className:"text-xl text-glow hidden sm:block",style:{color:h.color},children:h.name})]}),R.jsx(re,{metrics:_})]}),R.jsxs("div",{className:"flex flex-col lg:flex-row items-start lg:items-center justify-between gap-2 sm:gap-3 p-2 sm:p-4 border-b border-void-green/10 bg-void-dark/60 backdrop-blur-sm",children:[R.jsx(se,{modes:ce,currentMode:f,onModeChange:g,controls:ee}),R.jsx("p",{className:"text-void-green/50 text-xs lg:text-right max-w-lg",children:w})]}),R.jsx("div",{className:"flex-1 min-h-0 relative bg-void-dark",children:R.jsx("canvas",{ref:x,className:"absolute inset-0 w-full h-full touch-none cursor-crosshair","data-testid":"compass-proof-canvas"})})]})};export{ye as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.