1import{u as Z,r as c,T as J,V as B,S as Q,O as ee,W as ne,n as oe,o as M,c as te,M as ie,j as n,d as u}from"./index-y4oTXVR4.js";import{g as re,a as se,b as ae}from"./experimentConfig-B3ULZx1R.js";const le=`/** 2 * GALOIS Shader - Complex Polynomial Domain Coloring 3 * 4 * Visualizes polynomial solutions through domain coloring: 5 * - White/bright regions indicate zeros (solutions) 6 * - Hue encodes the phase/angle of the polynomial output 7 * - Brightness encodes the magnitude 8 * 9 * This is Step 1 of the 7-step GALOIS implementation guide. 10 * See: src/components/experiments/v20/GALOIS_IMPLEMENTATION_GUIDE.md 11 */ 12 13precision highp float; 14 15uniform vec2 u_resolution; 16uniform float u_time; 17uniform vec2 u_mouse; 18uniform vec3 u_backgroundColor; 19 20// Polynomial configuration 21uniform vec2 u_coefficients[6]; // Complex coefficients (real, imag) - up to degree 5 22uniform int u_degree; 23uniform float u_zoom; 24uniform vec2 u_pan; 25 26// Visual settings 27uniform float u_brightness; 28uniform float u_saturation; 29 30/** 31 * Complex number multiplication 32 * (a + bi)(c + di) = (ac - bd) + (ad + bc)i 33 */ 34vec2 cmul(vec2 a, vec2 b) { 35 return vec2(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); 36} 37 38/** 39 * Complex number division 40 * (a + bi)/(c + di) = ((ac + bd) + (bc - ad)i) / (c² + d²) 41 */ 42vec2 cdiv(vec2 a, vec2 b) { 43 float denom = b.x * b.x + b.y * b.y; 44 return vec2( 45 (a.x * b.x + a.y * b.y) / denom, 46 (a.y * b.x - a.x * b.y) / denom 47 ); 48} 49 50/** 51 * Evaluate polynomial at complex point z 52 * P(z) = aâ + aâz + aâz² + ... + aâzâ¿ 53 */ 54vec2 evalPoly(vec2 z) { 55 vec2 result = u_coefficients[0]; 56 vec2 zPower = z; 57 58 // Horner's method would be more efficient, but this is clearer 59 for (int i = 1; i <= 5; i++) { 60 if (i > u_degree) break; 61 result += cmul(u_coefficients[i], zPower); 62 zPower = cmul(zPower, z); 63 } 64 65 return result; 66} 67 68/** 69 * HSV to RGB color conversion 70 * Standard algorithm for mapping hue/saturation/value to RGB 71 */ 72vec3 hsv2rgb(vec3 c) { 73 vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 74 vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 75 return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 76} 77 78/** 79 * Domain coloring function 80 * Maps complex output to color: 81 * - Hue from phase angle (argument) 82 * - Brightness from magnitude (modulus) 83 */ 84vec3 domainColor(vec2 w) { 85 float magnitude = length(w); 86 float phase = atan(w.y, w.x); 87 88 // Brightness: white near zeros (where magnitude â 0) 89 // Using inverse relationship so small magnitudes = bright 90 float brightness = 1.0 - 1.0 / (1.0 + magnitude * u_brightness); 91 92 // Hue from phase angle, normalized to [0, 1] 93 float hue = phase / (2.0 * 3.14159265) + 0.5; 94 95 // Create color with configurable saturation 96 return hsv2rgb(vec3(hue, u_saturation, brightness)); 97} 98 99/** 100 * Grid overlay for visual reference 101 * Shows unit circle and coordinate axes 102 */ 103float grid(vec2 z, float lineWidth) { 104 // Unit circle 105 float circle = abs(length(z) - 1.0); 106 float circleAlpha = 1.0 - smoothstep(0.0, lineWidth, circle); 107 108 // Axes 109 float xAxis = abs(z.y); 110 float yAxis = abs(z.x); 111 float axisAlpha = 1.0 - smoothstep(0.0, lineWidth * 0.5, min(xAxis, yAxis)); 112 113 // Grid lines at integer values
114 float gridX = abs(fract(z.x + 0.5) - 0.5); 115 float gridY = abs(fract(z.y + 0.5) - 0.5); 116 float gridAlpha = 1.0 - smoothstep(0.0, lineWidth * 0.3, min(gridX, gridY)); 117 118 return max(max(circleAlpha * 0.3, axisAlpha * 0.2), gridAlpha * 0.1); 119} 120 121/** 122 * Animated highlight for solution regions 123 * Creates a subtle pulsing effect around zeros 124 */ 125float solutionGlow(float magnitude, float time) { 126 float pulse = sin(time * 2.0) * 0.5 + 0.5; 127 float glow = exp(-magnitude * 3.0) * (0.5 + 0.5 * pulse); 128 return glow; 129} 130 131void main() { 132 // Map pixel coordinates to complex plane 133 vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / min(u_resolution.x, u_resolution.y); 134 vec2 z = uv * u_zoom + u_pan; 135 136 // Evaluate polynomial at this point 137 vec2 w = evalPoly(z); 138 139 // Get base domain coloring 140 vec3 color = domainColor(w); 141 142 // Add solution glow effect 143 float glow = solutionGlow(length(w), u_time); 144 color = mix(color, vec3(1.0), glow * 0.5); 145 146 // Optional: Add grid overlay 147 float lineWidth = 0.02 * u_zoom; 148 float gridAlpha = grid(z, lineWidth); 149 vec3 gridColor = vec3(1.0, 1.0, 1.0); 150 color = mix(color, gridColor, gridAlpha * 0.3); 151 152 // Mouse interaction: highlight region near cursor 153 vec2 mouseComplex = (u_mouse - 0.5) * u_zoom * 2.0 + u_pan; 154 float mouseDist = length(z - mouseComplex); 155 float mouseHighlight = exp(-mouseDist * mouseDist * 2.0) * 0.2; 156 color = mix(color, vec3(1.0, 0.9, 0.8), mouseHighlight); 157 158 // Blend with background color for dark theme support 159 color = mix(u_backgroundColor, color, 0.95); 160 161 gl_FragColor = vec4(color, 1.0); 162} 163`,_="v20",ce=u.div` 164 position: fixed; 165 top: 0; 166 left: 0; 167 width: 100%; 168 height: 100%; 169 background: ${e=>e.$isDarkMode?"#000":"#fff"}; 170`,ue=u.div` 171 position: fixed; 172 top: 0; 173 left: 0; 174 right: 0; 175 padding: 40px; 176 display: flex; 177 justify-content: space-between; 178 align-items: center; 179 z-index: 100; 180 pointer-events: none; 181 182 & > * { 183 pointer-events: auto; 184 } 185`,F=u.button` 186 background: ${e=>e.theme.colors.background.overlay}; 187 border: 1px solid ${e=>e.theme.colors.border.subtle}; 188 color: ${e=>e.theme.colors.text.secondary}; 189 padding: 8px 16px; 190 font-family: ${e=>e.theme.fonts.primary}; 191 font-size: 12px; 192 letter-spacing: 2px; 193 cursor: pointer; 194 backdrop-filter: blur(5px); 195 -webkit-backdrop-filter: blur(5px); 196 transition: ${e=>e.theme.transitions.standard}; 197 198 &:hover { 199 background: ${e=>e.theme.colors.background.secondary}; 200 color: ${e=>e.theme.colors.text.hover}; 201 border-color: ${e=>e.theme.colors.accent.blue}; 202 } 203`,de=u.div` 204 display: flex; 205 gap: 10px; 206`,me=u.div` 207 position: fixed; 208 bottom: 40px; 209 left: 40px; 210 background: ${e=>e.$isDarkMode?"rgba(0, 0, 0, 0.8)":"rgba(255, 255, 255, 0.8)"}; 211 border: 1px solid ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)"}; 212 border-radius: 8px; 213 padding: 20px; 214 backdrop-filter: blur(10px); 215 -webkit-backdrop-filter: blur(10px); 216 z-index: 100; 217 min-width: 280px; 218`,pe=u.div` 219 color: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.8)":"rgba(0, 0, 0, 0.8)"}; 220 font-family: 'Roboto Mono', monospace; 221 font-size: 11px; 222 letter-spacing: 2px; 223 text-transform: uppercase; 224 margin-bottom: 16px; 225`,y=u.div` 226 display: flex; 227 align-items: center; 228 justify-content: space-between; 229 margin-bottom: 12px; 230`,k=u.label` 231 color: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.6)":"rgba(0, 0, 0, 0.6)"}; 232 font-family: 'Roboto Mono', monospace; 233 font-size: 11px; 234`,$=u.input` 235 width: 120px; 236 height: 4px; 237 -webkit-appearance: none; 238 background: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.2)":"rgba(0, 0, 0, 0.2)"}; 239 border-radius: 2px; 240 cursor: pointer; 241 242 &::-webkit-slider-thumb { 243 -webkit-appearance: none; 244 width: 14px; 245 height: 14px; 246 border-radius: 50%; 247 background: #4A9EFF; 248 cursor: pointer; 249 } 250 251 &::-moz-range-thumb { 252 width: 14px; 253 height: 14px; 254 border-radius: 50%; 255 background: #4A9EFF; 256 cursor: pointer; 257 border: none; 258 } 259`,z=u.span` 260 color: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.5)":"rgba(0, 0, 0, 0.5)"}; 261 font-family: 'Roboto Mono', monospace; 262 font-size: 10px; 263 min-width: 40px; 264 text-align: right; 265`,xe=u.div` 266 color: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.9)":"rgba(0, 0, 0, 0.9)"}; 267 font-family: 'Roboto Mono', monospace; 268 font-size: 14px; 269 margin-top: 16px; 270 padding-top: 16px; 271 border-top: 1px solid ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)"}; 272 text-align: center; 273`,ge=u.div` 274 color: ${e=>e.$isDarkMode?"rgba(74, 158, 255, 0.8)":"rgba(74, 158, 255, 1)"}; 275 font-family: 'Roboto Mono', monospace; 276 font-size: 10px; 277 margin-top: 8px; 278 text-align: center; 279`,fe=u.div` 280 position: absolute; 281 top: 20px; 282 left: 20px; 283 color: ${e=>e.$isDarkMode?"rgba(255, 255, 255, 0.6)":"rgba(0, 0, 0, 0.6)"}; 284 font-family: 'Roboto Mono', monospace; 285 font-size: 12px; 286 letter-spacing: 0.1em; 287 text-transform: uppercase; 288 pointer-events: none; 289 z-index: 1; 290`,C=(e,d)=>{if(Math.abs(d)<.001)return e.toFixed(2);if(Math.abs(e)<.001)return`${d.toFixed(2)}i`;const o=d>=0?"+":"";return`${e.toFixed(2)}${o}${d.toFixed(2)}i`},he=(e,d)=>{const o=[];for(let i=d;i>=0;i--){const r=e[i];if(Math.abs(r.x)<.001&&Math.abs(r.y)<.001)continue;let m="";if(i===d?m=i===0?C(r.x,r.y):"":m=o.length>0?" + ":"",i===0)m+=C(r.x,r.y);else if(i===1){const s=C(r.x,r.y);m+=s==="1.00"?"z":`(${s})z`}else{const s=C(r.x,r.y);m+=s==="1.00"?`z${I(i)}`:`(${s})z${I(i)}`}m.trim()&&o.push(m)}return o.join("")+" = 0"},I=e=>["â°","¹","²","³","â´","âµ"][e]||e.toString(),ve=` 291void main() {
292 gl_Position = vec4(position, 1.0); 293} 294`,$e=()=>{const e=Z(),{theme:d,isDarkMode:o}=c.useContext(J),i=c.useRef(null),r=c.useRef(null),m=c.useRef(null),[s,N]=c.useState(3),[b,O]=c.useState(4),[D,be]=c.useState(.5),[j,we]=c.useState(.7),[x,V]=c.useState([{x:1,y:0},{x:0,y:0},{x:0,y:0},{x:1,y:0},{x:0,y:0},{x:0,y:0}]),E=re(_),R=se(_),A=ae(_),S=c.useCallback((t,p,v)=>{V(a=>{const g=[...a];return g[t]={...g[t],[p]:parseFloat(v)},g})},[]);return c.useEffect(()=>{if(!m.current)return;const t=m.current;t.uniforms.u_degree.value=s,x.forEach((p,v)=>{t.uniforms.u_coefficients.value[v].set(p.x,p.y)}),t.uniforms.u_zoom.value=b,t.uniforms.u_brightness.value=D,t.uniforms.u_saturation.value=j},[s,x,b,D,j]),c.useEffect(()=>{if(!i.current)return;const t=o?new B(.02,.02,.05):new B(.95,.95,.98),p=new Q,v=new ee(-1,1,1,-1,.1,10);v.position.z=5;const a=new ne({alpha:!0,antialias:!0,powerPreference:"high-performance"}),g=Math.min(window.devicePixelRatio,2);a.setSize(window.innerWidth,window.innerHeight),a.setPixelRatio(g),a.domElement.style.position="absolute",a.domElement.style.top="0",a.domElement.style.left="0",i.current.appendChild(a.domElement);const W=new oe(2,2),K=window.innerWidth*g,U=window.innerHeight*g,X=x.map(l=>new M(l.x,l.y)),f=new te({uniforms:{u_time:{value:0},u_resolution:{value:new M(K,U)},u_mouse:{value:new M(.5,.5)},u_backgroundColor:{value:t},u_coefficients:{value:X},u_degree:{value:s},u_zoom:{value:b},u_pan:{value:new M(0,0)},u_brightness:{value:D},u_saturation:{value:j}},vertexShader:ve,fragmentShader:le,transparent:!0});m.current=f;const Y=new ie(W,f);p.add(Y);const q=performance.now(),G=()=>{const l=(performance.now()-q)/1e3;f.uniforms.u_time.value=l,a.render(p,v),r.current=requestAnimationFrame(G)};G();const L=l=>{const h=l.clientX/window.innerWidth,w=1-l.clientY/window.innerHeight;f.uniforms.u_mouse.value.set(h,w)};window.addEventListener("mousemove",L);const H=l=>{if(l.touches.length>0){const h=l.touches[0],w=h.clientX/window.innerWidth,P=1-h.clientY/window.innerHeight;f.uniforms.u_mouse.value.set(w,P)}};window.addEventListener("touchmove",H,{passive:!0});const T=()=>{const{innerWidth:l,innerHeight:h}=window;a.setSize(l,h);const w=l*g,P=h*g;f.uniforms.u_resolution.value.set(w,P)};return window.addEventListener("resize",T),()=>{r.current&&cancelAnimationFrame(r.current),window.removeEventListener("mousemove",L),window.removeEventListener("touchmove",H),window.removeEventListener("resize",T),i.current&&a.domElement.parentNode===i.current&&i.current.removeChild(a.domElement),a.dispose(),W.dispose(),f.dispose(),m.current=null}},[o]),c.useEffect(()=>{const t=p=>{p.key==="Escape"&&e("/experiments"),p.key==="ArrowLeft"&&e(`/experiments/${E.id}`),p.key==="ArrowRight"&&e(`/experiments/${R.id}`)};return window.addEventListener("keydown",t),()=>window.removeEventListener("keydown",t)},[e,E,R]),n.jsxs(ce,{$isDarkMode:o,children:[n.jsx("div",{ref:i}),n.jsxs(fe,{$isDarkMode:o,children:[_.toUpperCase(),": ",(A==null?void 0:A.name)||"Galois"]}),n.jsxs(ue,{children:[n.jsx(F,{theme:d,onClick:()=>e("/experiments"),children:"â BACK"}),n.jsxs(de,{children:[n.jsx(F,{theme:d,onClick:()=>e(`/experiments/${E.id}`),children:"â PREV"}),n.jsx(F,{theme:d,onClick:()=>e(`/experiments/${R.id}`),children:"NEXT â"})]})]}),n.jsxs(me,{$isDarkMode:o,children:[n.jsx(pe,{$isDarkMode:o,children:"Polynomial Controls"}),n.jsxs(y,{children:[n.jsx(k,{$isDarkMode:o,children:"Degree"}),n.jsx($,{$isDarkMode:o,type:"range",min:"2",max:"5",step:"1",value:s,onChange:t=>N(parseInt(t.target.value))}),n.jsx(z,{$isDarkMode:o,children:s})]}),n.jsxs(y,{children:[n.jsx(k,{$isDarkMode:o,children:"Zoom"}),n.jsx($,{$isDarkMode:o,type:"range",min:"1",max:"10",step:"0.1",value:b,onChange:t=>O(parseFloat(t.target.value))}),n.jsx(z,{$isDarkMode:o,children:b.toFixed(1)})]}),n.jsxs(y,{children:[n.jsx(k,{$isDarkMode:o,children:"aâ (Re)"}),n.jsx($,{$isDarkMode:o,type:"range",min:"-2",max:"2",step:"0.1",value:x[0].x,onChange:t=>S(0,"x",t.target.value)}),n.jsx(z,{$isDarkMode:o,children:x[0].x.toFixed(1)})]}),n.jsxs(y,{children:[n.jsx(k,{$isDarkMode:o,children:"aâ (Re)"}),n.jsx($,{$isDarkMode:o,type:"range",min:"-2",max:"2",step:"0.1",value:x[1].x,onChange:t=>S(1,"x",t.target.value)}),n.jsx(z,{$isDarkMode:o,children:x[1].x.toFixed(1)})]}),n.jsxs(y,{children:[n.jsx(k,{$isDarkMode:o,children:"aâ (Re)"}),n.jsx($,{$isDarkMode:o,type:"range",min:"-2",max:"2",step:"0.1",value:x[2].x,onChange:t=>S(2,"x",t.target.value)}),n.jsx(z,{$isDarkMode:o,children:x[2].x.toFixed(1)})]}),n.jsx(xe,{$isDarkMode:o,children:he(x,s)}),n.jsxs(ge,{$isDarkMode:o,children:[s," solutions ⢠White regions = zeros"]})]})]})};export{$e 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.