diff --git a/.gitignore b/.gitignore index c2ff63a..5c7a8f6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ apeiron/node_modules/ syrinx/node_modules/ oikos/node_modules oikos/shots/ +afterlife/node_modules/ +afterlife/shots/ blog.sqlite irc.sqlite *.db diff --git a/aethera/api/afterlife.py b/aethera/api/afterlife.py new file mode 100644 index 0000000..37b3de4 --- /dev/null +++ b/aethera/api/afterlife.py @@ -0,0 +1,15 @@ +from fastapi import APIRouter, Request +from fastapi.responses import HTMLResponse + +from aethera.utils.templates import templates + +router = APIRouter(tags=["afterlife"]) + + +@router.get("/afterlife", response_class=HTMLResponse) +async def afterlife_viewer(request: Request): + return templates.TemplateResponse( + request=request, + name="afterlife/viewer.html", + context={"title": "afterlife | æthera"}, + ) diff --git a/aethera/api/oikos.py b/aethera/api/oikos.py index c5474d0..0fc81d5 100644 --- a/aethera/api/oikos.py +++ b/aethera/api/oikos.py @@ -114,6 +114,23 @@ ["by", "Luxia & Claude Fable 5"]], "tune": True, }, + { + "id": "afterlife", + "title": "afterlife", + "href": "/afterlife", + "group": "here", + "kind": "screensaver", + "accent": "#ffaf00", + "tagline": "the game of life, after hours", + "about": "Conway's Life on an infinite grid that steers toward its own drama: " + "it names its epochs, stages collisions, greets the famous citizens " + "it recognises, and scores itself as it goes. Press g and it " + "remembers everything. It is a place, not a session: the universe " + "is kept in your browser and resumes where you left it.", + "details": [["kind", "cellular automaton · music"], ["born", "2026-02-07"], + ["by", "Luxia & Claude"], ["source", "github.com/LuxiaSL/afterlife"]], + "tune": True, + }, { "id": "irc", "title": "irc", diff --git a/aethera/api/seo.py b/aethera/api/seo.py index e7fac82..2834ca4 100644 --- a/aethera/api/seo.py +++ b/aethera/api/seo.py @@ -89,6 +89,7 @@ def rss_feed(request: Request, session: Session = Depends(get_session)): ("oikos", "weekly", "0.6"), # the home directory (and mIRC) ("apeiron", "monthly", "0.6"), ("syrinx", "monthly", "0.6"), + ("afterlife", "monthly", "0.6"), ] diff --git a/aethera/main.py b/aethera/main.py index deff11b..e3a1550 100644 --- a/aethera/main.py +++ b/aethera/main.py @@ -14,7 +14,7 @@ load_dotenv() from aethera.models.base import init_db, get_session -from aethera.api import posts, comments, seo, dreams, chronicle, apeiron, syrinx, irc, irc_admin, oikos, chat +from aethera.api import posts, comments, seo, dreams, chronicle, apeiron, syrinx, afterlife, irc, irc_admin, oikos, chat from aethera.irc.database import init_irc_db from aethera.utils.security import SecurityHeadersMiddleware from aethera.utils.templates import templates @@ -68,6 +68,7 @@ async def lifespan(app: FastAPI): app.include_router(chronicle.router) app.include_router(apeiron.router) app.include_router(syrinx.router) +app.include_router(afterlife.router) app.include_router(irc.router) app.include_router(irc_admin.router) app.include_router(oikos.router) diff --git a/aethera/static/afterlife/afterlife.css b/aethera/static/afterlife/afterlife.css new file mode 100644 index 0000000..9e467e8 --- /dev/null +++ b/aethera/static/afterlife/afterlife.css @@ -0,0 +1 @@ +:root{color-scheme:dark}#afterlife-root{--bg: #0a0a0c;--fg: rgb(200, 200, 200);--dim: rgb(112, 112, 116);--age: linear-gradient(90deg, #0000af, #0000ff, #5f00ff, #8700ff, #d700ff, #d700af, #ff5f87, #ff875f, #ffaf00, #ffd700, #ffffff, #ffd700, #ff5f87, #8700ff, #0000af);position:fixed;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;background:var(--bg);color:var(--fg);font-family:ui-monospace,SF Mono,Cascadia Mono,DejaVu Sans Mono,Menlo,Consolas,monospace;overflow:hidden;touch-action:none;user-select:none;-webkit-user-select:none}#afterlife-root #term{display:block;cursor:crosshair;image-rendering:pixelated}#afterlife-root #splash{position:absolute;top:0;right:0;bottom:0;left:0;z-index:10;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.6rem;padding:1rem;background:radial-gradient(ellipse at center,#0a0a0c9e,#0a0a0ce6 70%);cursor:pointer;transition:opacity 1.4s ease;outline:none}#afterlife-root #splash.gone{opacity:0;pointer-events:none}#afterlife-root #splash .logo{margin:0;padding:0;border:0;border-radius:0;box-shadow:none;overflow:visible;font:inherit;font-size:clamp(5px,1.35vw,14px);line-height:1.12;background:var(--age);background-size:300% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:afterlife-age 24s linear infinite;filter:drop-shadow(0 0 12px rgba(135,0,255,.25))}@keyframes afterlife-age{0%{background-position:0% 0}to{background-position:300% 0}}#afterlife-root #splash h1{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);margin:0}#afterlife-root #splash .sub{margin:.4rem 0 0;color:var(--fg);font-size:13px;letter-spacing:.3em}#afterlife-root #splash .go{margin:1.6rem 0 0;color:var(--dim);font-size:11px;letter-spacing:.12em;text-align:center;animation:afterlife-breathe 5.6s ease-in-out infinite}#afterlife-root #splash .go .sep{margin:0 .4em}@keyframes afterlife-breathe{0%,to{opacity:.45}50%{opacity:1}}#afterlife-root #corner{position:absolute;top:10px;right:14px;z-index:11;display:flex;gap:14px;align-items:baseline}#afterlife-root #corner a,#afterlife-root #corner button{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;padding:2px 0;background:none;color:#c8c8c847;font:inherit;font-size:11px;letter-spacing:.08em;text-decoration:none;cursor:pointer;transition:color .6s ease}#afterlife-root #corner a:hover,#afterlife-root #corner button:hover,#afterlife-root #corner button[aria-expanded=true]{color:#c8c8c8bf}#afterlife-root #keys{position:absolute;top:34px;right:14px;z-index:11;width:min(520px,calc(100vw - 28px));max-height:calc(100% - 90px);overflow:auto;box-sizing:border-box;padding:14px 16px 12px;border:1px solid rgba(200,200,200,.12);background:#0a0a0cf0;font-size:12px;touch-action:pan-y;user-select:text;-webkit-user-select:text}#afterlife-root #keys[hidden]{display:none}#afterlife-root #keys .grid{display:grid;grid-template-columns:1fr 1fr;gap:2px 18px}#afterlife-root #keys button{-webkit-appearance:none;-moz-appearance:none;appearance:none;display:flex;align-items:baseline;gap:10px;padding:3px 4px;border:0;background:none;color:var(--fg);font:inherit;text-align:left;cursor:pointer}#afterlife-root #keys button:hover,#afterlife-root #keys button:focus-visible{background:#c8c8c812;outline:none}#afterlife-root #keys kbd,#afterlife-root #keys .nokey{min-width:3.2em;color:#ffaf00;font:inherit}#afterlife-root #keys .haunt kbd{color:#d700ff}#afterlife-root #keys p{margin:12px 4px 0;color:var(--dim);line-height:1.55}#afterlife-root #keys a{color:var(--fg)}#afterlife-root .touch{display:none}@media(pointer:coarse){#afterlife-root .mouse{display:none}#afterlife-root .touch{display:inline}}@media(prefers-reduced-motion:reduce){#afterlife-root #splash,#afterlife-root #splash .logo,#afterlife-root #splash .go{animation:none;transition:none}#afterlife-root #splash .go{opacity:.8}}@media(max-width:480px){#afterlife-root #keys .grid{grid-template-columns:1fr}}.afterlife-noscript{position:fixed;inset:auto 0 45% 0;padding:1rem;color:#c8c8c8;text-align:center;font-family:monospace} diff --git a/aethera/static/afterlife/afterlife.js b/aethera/static/afterlife/afterlife.js new file mode 100644 index 0000000..b2416de --- /dev/null +++ b/aethera/static/afterlife/afterlife.js @@ -0,0 +1,5 @@ +var ws=Object.defineProperty;var ys=(ot,U,G)=>U in ot?ws(ot,U,{enumerable:!0,configurable:!0,writable:!0,value:G}):ot[U]=G;var g=(ot,U,G)=>ys(ot,typeof U!="symbol"?U+"":U,G);(function(){"use strict";var Ce;const ot=[17,19,21,57,93,165,163,204,209,214,220,231],U=[236,234],G=U.length,rt=6,We=[17,18,19,20,21,57,56,93,129,165,164,163,198,204,203,209,208,214,220,229,231],Ht=o=>We[o]??0,ke=Array.from({length:rt},(o,t)=>[Ht(Math.floor((185+t)/21)),Ht((185+t)%21)]),Ie=Array.from({length:rt*rt},(o,t)=>[Ht(Math.floor((191+t)/21)),Ht((191+t)%21)]),Ee=.98,Kt=4,Pe=Math.fround(Ee**Kt),vt="▁▂▃▄▅▆▇█",it=-2,ct=2,Re={[-2]:"0.25x",[-1]:"0.50x",0:" 1.0x",1:" 2.0x",2:" 4.0x"},dt={glider:[[0,1],[1,2],[2,0],[2,1],[2,2]],lwss:[[0,1],[0,4],[1,0],[2,0],[2,4],[3,0],[3,1],[3,2],[3,3]],hwss:[[0,3],[0,4],[1,1],[1,6],[2,0],[3,0],[3,6],[4,0],[4,1],[4,2],[4,3],[4,4],[4,5]],r_pentomino:[[0,1],[0,2],[1,0],[1,1],[2,1]],acorn:[[0,1],[1,3],[2,0],[2,1],[2,4],[2,5],[2,6]],diehard:[[0,6],[1,0],[1,1],[2,1],[2,5],[2,6],[2,7]],pi_heptomino:[[0,0],[0,1],[0,2],[1,1],[2,1],[3,0],[3,2]],b_heptomino:[[0,1],[1,0],[1,2],[1,3],[2,0],[2,1],[3,1]],gosper_gun:[[0,24],[1,22],[1,24],[2,12],[2,13],[2,20],[2,21],[2,34],[2,35],[3,11],[3,15],[3,20],[3,21],[3,34],[3,35],[4,0],[4,1],[4,10],[4,16],[4,20],[4,21],[5,0],[5,1],[5,10],[5,14],[5,16],[5,17],[5,22],[5,24],[6,10],[6,16],[6,24],[7,11],[7,15],[8,12],[8,13]],pulsar:[[0,2],[0,3],[0,4],[0,8],[0,9],[0,10],[2,0],[2,5],[2,7],[2,12],[3,0],[3,5],[3,7],[3,12],[4,0],[4,5],[4,7],[4,12],[5,2],[5,3],[5,4],[5,8],[5,9],[5,10],[7,2],[7,3],[7,4],[7,8],[7,9],[7,10],[8,0],[8,5],[8,7],[8,12],[9,0],[9,5],[9,7],[9,12],[10,0],[10,5],[10,7],[10,12],[12,2],[12,3],[12,4],[12,8],[12,9],[12,10]],pentadecathlon:[[0,1],[1,1],[2,0],[2,2],[3,1],[4,1],[5,1],[6,1],[7,0],[7,2],[8,1],[9,1]]},Pt=["glider","lwss","hwss"],Qt=["r_pentomino","acorn","diehard","pi_heptomino","b_heptomino"],Jt=["pulsar","pentadecathlon"],Le=[[1,1],[1,-1],[-1,-1],[-1,1]];function Rt(o){const t=new Map;for(const[e,s]of o)for(const[i,a]of[[e,s],[e,-s],[-e,s],[-e,-s]])t.set(`${i},${a}`,[i,a]);return[...t.values()]}const ze=[Rt([[0,1],[0,2],[0,3],[1,0],[1,4],[2,0],[2,5],[3,0],[3,5],[4,1],[4,5],[5,2],[5,3],[5,4]]),Rt([[0,1],[0,2],[0,3],[0,4],[1,0],[2,0],[3,0],[4,0],[2,2],[3,3]]),Rt([[0,3],[0,4],[1,2],[1,5],[2,1],[2,6],[3,0],[3,7],[4,0],[4,7],[5,1],[5,6],[6,2],[6,5],[7,3],[7,4]])],Fe=5e3,Te={block:[[[0,0],[0,1],[1,0],[1,1]],1],beehive:[[[0,1],[0,2],[1,0],[1,3],[2,1],[2,2]],1],loaf:[[[0,1],[0,2],[1,0],[1,3],[2,1],[2,3],[3,2]],1],boat:[[[0,0],[0,1],[1,0],[1,2],[2,1]],1],tub:[[[0,1],[1,0],[1,2],[2,1]],1],ship:[[[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]],1],pond:[[[0,1],[0,2],[1,0],[1,3],[2,0],[2,3],[3,1],[3,2]],1],blinker:[[[0,0],[0,1],[0,2]],2],toad:[[[0,1],[0,2],[0,3],[1,0],[1,1],[1,2]],2],beacon:[[[0,0],[0,1],[1,0],[2,3],[3,2],[3,3]],2],glider:[dt.glider??[],4],lwss:[dt.lwss??[],4],hwss:[dt.hwss??[],4],pulsar:[dt.pulsar??[],3],pentadecathlon:[dt.pentadecathlon??[],15]},Ne=60,te=20;function ee(o){let t=null;for(let e=0;e<8;e++){const s=e>=4,i=s?o.w:o.h,a=s?o.h:o.w;let n=`${i}x${a}:`;for(let h=0;hc))+1,a=Math.max(...e.map(([,c])=>c))+1,n=Math.max(i,a)+2*(s+4);let h=new Uint8Array(n*n);const r=Math.floor((n-i)/2),l=Math.floor((n-a)/2);for(const[c,d]of e)h[(r+c)*n+l+d]=1;for(let c=0;ci&&(i=c),dn&&(n=d));if(i<0)return null;const h=i-s+1,r=n-a+1,l=new Uint8Array(h*r);for(let c=0;c=t)continue;const m=2-Math.abs(p),v=Math.max(0,d-m),x=Math.min(e-1,d+m);i.fill(1,y*e+v,y*e+x+1)}const a=new Int32Array(t*e),n=[],h=[];for(let c=0;c400)return null;const p={y0:t,y1:-1,x0:e,x1:-1};for(a[c]=d,h.push(c);h.length;){const y=h.pop()??0,m=Math.floor(y/e),v=y-m*e;mp.y1&&(p.y1=m),vp.x1&&(p.x1=v);for(let x=-1;x<=1;x++){const b=m+x;if(!(b<0||b>=t))for(let M=-1;M<=1;M++){const _=v+M;if(_<0||_>=e)continue;const A=b*e+_;i[A]&&!a[A]&&(a[A]=d,h.push(A))}}}n.push(p)}if(n.length===0)return null;const r=new Map,l=[];return n.forEach((c,d)=>{const p=d+1,y=c.y1-c.y0+1,m=c.x1-c.x0+1;if(y>te+4||m>te+4)return;const v=new Uint8Array(y*m);let x=0,b=0,M=0;for(let w=0;wNe)return;const _=se(v,y,m),A=_?s.get(ee(_)):void 0;A!==void 0&&(r.set(A,(r.get(A)??0)+1),l.length<48&&l.push([A,c.y0+Math.floor(b/x),c.x0+Math.floor(M/x)]))}),{counts:r,sites:l}}const zt=["cells pondering existence","entropy is just a suggestion","order from chaos, chaos from order","the cosmos breathes","finding signal in noise","patterns all the way down","life, uh, finds a way","on the edge of chaos","complexity, emerging","dancing at the phase boundary","stillness is just slow motion","every cell a universe","the substrate computes","from nothing, everything","infinite in all directions","what the dead cells dream","the glider knows the way","automata, contemplating","structure wants to happen","a cosmos in a terminal","the math dreams itself","somewhere, a glider remembers","what is a pattern but frozen time","the void is patient","meaning is optional; beauty is not","computation as meditation","the rules are simple; the consequences infinite","watching the watchers watch","no cell is an island","the ghost of configurations past","asymmetry seeks symmetry seeks asymmetry","this too shall iterate","the universe has no pause button","except when it does","what would Conway think","oscillators keep time for no one","the glider gun knows not what it creates","boundaries are suggestions","death is just a state transition","somewhere in here, a proof of universality","the grid forgets nothing","a thought experiment that thinks back","topology has opinions","the neighborhood watches","not alive, not dead — computing","four rules, one universe","each frame a theorem","proof by existence","the only constant is the ruleset","somewhere between on and off, meaning","the dead outnumber the living, as always","conway's little infinities","the simulation doesn't know it's beautiful","27 neighbors ago, this was empty","elegance is compression","the grid is the message","a love letter to discrete math","listen — the cells are whispering","we are all gliders, briefly"],Be={booming:["runaway growth","the bloom unfolds","life begets life begets life","exponential daydreams","more is different","the bloom cannot be stopped","abundance as instability","growing into the unknown","every birth a cascade","the simulation smiles","lebensraum","mitosis dreams","the substrate strains","a spring that won't stop springing","cells all the way to the horizon","the algorithm is generous today","more, more, more","genesis on fast-forward"],declining:["entropy collects its due","the long exhale","even stars go dark","returning to the void","graceful unwinding","the great filter, in miniature","less is not nothing","the long forgetting","what rises must","simplicity returns","the cells remember fullness","autumn in the grid","graceful subtraction","the tide goes out","a slow dissolve","the universe exhales","dimming, not vanishing","the population curve bends earthward"],cycle:["deja vu, deja vu, deja vu","stuck in a loop","the eternal return","ouroboros","time is a flat circle","we have been here before","the loop remembers itself","stability through repetition","a prayer, repeating","the wheel turns","same as it ever was","history rhymes","the attractor holds","a fixed point in phase space","nietzsche was right about this part","the universe stutters beautifully","clockwork, but organic","breathing in, breathing out"],stagnant:["the hush before the storm","equilibrium is boring","waiting for a perturbation","still waters","the universe holds its breath","the calm before","potential energy","stillness is also motion","the system waits","one perturbation away","dormant, not dead","crystallized","the peace of equilibrium","a held breath","the grid meditates","tension, frozen","the quiet hum of nothing happening","patience is also a computation"],sparse:["the last few embers","clinging to existence","from little things, big things grow","seeds in the dark","quiet, but not empty","lonely structures","the survivors","space between stars","minimalism, enforced","room to breathe","the few, the proud","embers","a whisper, not a shout","the geometry of loneliness","each cell precious now","a constellation, if you squint","small, but not nothing"],dense:["teeming","a city that never sleeps","standing room only","too many neighbours","the crowd murmurs","no room to think","the crush of neighbors","overpopulation is self-correcting","too much life","suffocating in company","the grid groans","rush hour","sardines","every cell has an opinion","a metropolis, thrumming","the carrying capacity protests","elbow room is a luxury"],injection:["a gift from beyond the edge","new visitors","reinforcements arrive","the cosmos provides","seeded by unseen hands","deus ex machina","a nudge from outside","fresh blood","the invisible hand places","salvation arrives","new variables enter","the cosmos intervenes","help from beyond the viewport","strangers in a strange land","the petri dish gets a refill","immigrants, welcome"],milestone:["another thousand turns of the wheel","the odometer clicks over","still here, still going","persistence is its own reward","ten thousand steps","the long now","time is a flat circle, but longer","we persist","generations beyond counting","deep in the run","the odometer means nothing to the cells","and yet, it continues","older than some civilizations","the marathon nobody entered","a digit rolls over; the grid doesn't notice","epochal"],haunted:["the dead outnumber the living, and glow","every path ever walked, still visible","the void remembers being alive","phase-contrast photography of the past","nothing is ever really deleted","the grid forgets nothing — literally, now","a bug so beautiful it became a feature","the ghosts have synchronized their breathing","somewhere between pair 463 and 504","resurrection as a rendering artifact","the afterlife was here all along","history, strobing","every empty cell, a metronome","the universe develops like a photograph","magenta is the color of memory"]},je={glider:["a glider, going somewhere","lightspeed, quartered — a glider","a glider carries the news","diagonal traffic"],lwss:["a lightweight spaceship, outbound","small craft advisory"],hwss:["a heavy spaceship shoulders past","heavy freight on the grid"],pulsar:["a pulsar, keeping time for no one","the pulsar breathes in threes"],pentadecathlon:["fifteen beats to the bar","a pentadecathlon runs its laps"],blinker:["the blinkers keep the beat","metronomes, disagreeing"],block:["blocks: the simplest memory","still life with squares"],beehive:["the bees are home","a beehive, sealed"],loaf:["bread, uneaten","a loaf, going stale beautifully"],boat:["a boat with nowhere to sail"],tub:["a tub, holding nothing"],ship:["a ship without a sea"],pond:["a pond, perfectly still"],toad:["a toad, breathing"],beacon:["the beacon still signals","someone left the light on"]},Ft={pulsar:100,pentadecathlon:100,hwss:90,lwss:80,glider:40,beacon:30,toad:28,pond:24,ship:20,loaf:16,beehive:12,boat:12,tub:12,blinker:8,block:6},$e={blinker:3,block:3},oe=[[0,"genesis"],[500,"primordial"],[2e3,"emergence"],[1e4,"expansion"],[5e4,"flourishing"],[15e4,"deep time"],[5e5,"eon"],[1e6,"eternity"]],q=o=>[...o].length;class ie{constructor(){g(this,"messages",[]);g(this,"scrollSpeed",.4);g(this,"spawnCooldown",0);g(this,"minGap",12);g(this,"ambientInterval",60);g(this,"moodInterval",50);g(this,"ambientIdx",Math.floor(Math.random()*zt.length));g(this,"lastMood","");g(this,"pendingSpecial","")}queueSpecial(t){this.pendingSpecial=t}tick(t,e,s){if(t<10)return;for(const h of this.messages)h.x-=this.scrollSpeed;if(this.messages=this.messages.filter(h=>h.x+q(h.text)>0),this.pendingSpecial&&this.canSpawn(t)){this.messages.push({text:this.pendingSpecial,x:t}),this.pendingSpecial="",this.spawnCooldown=Math.max(this.spawnCooldown,this.moodInterval);return}const i=e?Be[e]:void 0;if(i&&e!==this.lastMood&&(this.spawnCooldown=Math.min(this.spawnCooldown,15)),this.spawnCooldown=Math.max(0,this.spawnCooldown-1),this.spawnCooldown>0||!this.canSpawn(t))return;let a,n;i?(a=i[Math.floor(s/180)%i.length]??"",n=this.moodInterval):(a=zt[this.ambientIdx]??"",this.ambientIdx=(this.ambientIdx+1)%zt.length,n=this.ambientInterval),this.messages.push({text:a,x:t}),this.spawnCooldown=n,this.lastMood=e}canSpawn(t){if(!this.messages.length)return!0;let e=-1/0;for(const s of this.messages)e=Math.max(e,s.x+q(s.text));return eo+Math.floor(Math.random()*(t-o+1)),Q=o=>o[Math.floor(Math.random()*o.length)],f=(o,t)=>Math.floor(o/t),V=(o,t,e)=>Math.max(t,Math.min(o,e)),Ge=Math.fround(.85),Oe=Math.fround(.15);function nt(o,t){const e=o.length,s=t/100*(e-1),i=Math.floor(s),a=Math.min(e-1,i+1),n=o[i]??0,h=o[a]??0,r=s-i;return Math.trunc(r>=.5?h-(h-n)*(1-r):n+(h-n)*r)}function Ue(o,t,e){const s=(n,h)=>n[h]??0,i=new Float64Array(t*e);for(let n=0;n1?s(o,e+n):0);i[n]=h/3;for(let r=1;r=2?s(o,(r-2)*e+n):0),i[r*e+n]=h/3}const a=new Float64Array(t*e);for(let n=0;n1?s(i,h+1):0);a[h]=r/3;for(let l=1;l=2?s(i,h+l-2):0),a[h+l]=r/3}return a}class Ct{constructor(t,e,s=!0,i={}){g(this,"viewH");g(this,"viewW");g(this,"worldH");g(this,"worldW");g(this,"grid");g(this,"age");g(this,"ageSmooth");g(this,"activity");g(this,"nextRow");g(this,"colSum");g(this,"camY");g(this,"camX");g(this,"autoCam",!0);g(this,"zoomLevel",0);g(this,"zoomCooldown",0);g(this,"generation",0);g(this,"paused",!1);g(this,"delay",50);g(this,"popHistory",[]);g(this,"hashHistory",[]);g(this,"cachedPop",0);g(this,"spread",0);g(this,"popFloor",0);g(this,"cyclePeriod",0);g(this,"lastEvent","");g(this,"lastEventGen",0);g(this,"totalInjections",0);g(this,"ticker",new ie);g(this,"autoFocusMode",!1);g(this,"autoFocusFrame",0);g(this,"dispGridCache",null);g(this,"dispAgeCache",null);g(this,"cachedMood","");g(this,"cachedMoodGen",-1);g(this,"stepBbox",null);g(this,"haunted",!1);g(this,"lastCensus",new Map);g(this,"lastCensusSites",[]);g(this,"sightingGen",new Map);g(this,"sightingCount",new Map);g(this,"lastSightingGen",-1e9);g(this,"dramaUntil",0);g(this,"actBaseline",0);g(this,"dilation",1);this.viewH=t*2,this.viewW=e,this.worldH=Math.max(this.viewH*5,i.minH??400),this.worldW=Math.max(this.viewW*5,i.minW??800);const a=this.worldH*this.worldW;this.grid=new Uint8Array(a),this.age=new Int32Array(a),this.ageSmooth=new Float32Array(a),this.activity=new Float32Array(a),this.nextRow=new Uint8Array(this.worldW),this.colSum=new Uint8Array(this.worldW),this.camY=f(this.worldH-this.viewH,2),this.camX=f(this.worldW-this.viewW,2),s&&this.seedInitial()}seedInitial(){const t=f(this.worldH,2),e=f(this.worldW,2),s=this.viewH,i=this.viewW;for(const h of Qt)for(let r=0;r<2;r++)this.place(h,t+I(f(-s,3),f(s,3)),e+I(f(-i,3),f(i,3)));for(let h=0;h<2;h++)this.place("gosper_gun",t+I(f(-s,4),f(s,4)),e+I(f(-i,4),f(i,4)));for(let h=0;h<20;h++){const r=V(t+I(-s,s),10,this.worldH-10),l=V(e+I(-i,i),10,this.worldW-10);this.place(Q(Pt),r,l)}for(let h=0;h<4;h++){const r=t+I(f(-s,3),f(s,3)),l=e+I(f(-i,3),f(i,3));this.place(Q(Jt),r,l)}const a=t-f(s,2),n=e-f(i,2);for(let h=0;h=0&&l=0&&cr&&(r=F+1))}const l=n>0;let c=!1;l&&a-2>=0&&n+2<=t&&h-2>=0&&r+2<=e&&(a-=2,n+=2,h-=2,r+=2,c=(n-a)*(r-h)<=f(t*e,2)),c=c&&!this.haunted,this.stepBbox=l&&c?[a,n,h,r]:null,c||(a=0,n=t,h=0,r=e);const d=this.ageSmooth;for(let w=0;w0?K=-1:(x||D<0)&&D>v?K=D-1:K=0,i[P]=K,d[P]=(d[P]??0)+Math.fround(Math.fround(K)*Oe);const st=N||$?1:0;s[P]=st,_+=st}}if(this.generation+=1,this.generation%Kt===0)for(let w=0;w500&&this.popHistory.shift(),this.hashHistory.push(this.viewportHash()),this.hashHistory.length>60&&this.hashHistory.shift();const A=this.ensureLife(_);return this.updateCamera(),A}viewportHash(){const[t,e]=this.clampCam();let s=2166136261,i=16777619;for(let a=0;a>>0)*2097152+(i>>>0&2097151)}ensureLife(t){const e=this.viewH*this.viewW,s=f(e,40),i=this.popHistory,a=i.length;if(a>=100){let d=0;for(let p=a-100;p=150){let d=i[a-1]??0,p=d;for(let y=a-150;yp&&(p=m)}this.spread=p-d}const n=a>=150&&this.spread=4){const d=h[r-1];for(let p=1;p0&&!l){const d=Math.random()<.65?this.injectProvoke():"";d?c=`inject:provoke(cycle=${this.cyclePeriod},${d})`:(c=`inject:heavy(cycle=${this.cyclePeriod})`,this.inject("heavy"))}else n&&!l?Math.random()<.7&&this.injectCollide()?c="inject:collide(stagnant)":(c="inject:medium(stagnant)",this.inject("medium")):this.generation%5e3===0&&this.generation>0?(c="inject:garden",this.injectGarden()):this.generation%300===0&&this.generation>0&&(c="inject:edge",this.injectFromEdge(),this.injectFromEdge());return c&&(this.lastEvent=c,this.lastEventGen=this.generation,this.totalInjections+=1),c}inject(t="medium"){const e={massive:8,heavy:5,medium:3}[t];for(let s=0;s=0&&r=0&&l(d[b]??0)-(d[M]??0)),m=Q(y.slice(0,p)),v=f(m,c),x=m%c;return[n+v*r+f(r,2),h+x*r+f(r,2)]}aimGlider(t,e,s,i){const[a,n]=Le[i]??[1,1],h=t-a*s,r=e-n*s,[l,c]=this.clampCam();if(!(l+2<=h&&h(Ft[s[0]]??0)-(Ft[e[0]]??0));for(const[e,s,i]of t.slice(0,6)){const a=I(35,60),n=[0,1,2,3];for(let h=n.length-1;h>0;h--){const r=Math.floor(Math.random()*(h+1));[n[h],n[r]]=[n[r]??0,n[h]??0]}for(const h of n)if(this.aimGlider(s,i,a,h))return this.dramaUntil=this.generation+a*4+150,this.ticker.queueSpecial("the cosmos takes aim"),e}return""}injectCollide(){let[t,e]=this.findQuietSpot();const[s,i]=this.clampCam();for(let a=0;a<6;a++){const n=I(16,34),h=s+n+3,r=s+this.viewH-n-6,l=i+n+3,c=i+this.viewW-n-6;if(h>=r||l>=c)continue;t=Math.max(h,Math.min(t,r)),e=Math.max(l,Math.min(e,c));const d=I(0,3),p=I(-3,3),y=I(-3,3);if(!this.aimGlider(t,e,n,d))continue;const m=(d+Q([1,3]))%4,v=this.aimGlider(t+p,e+y,n,m);return this.dramaUntil=this.generation+n*4+150,this.ticker.queueSpecial(v?"two gliders, one appointment":"a lone glider, sent into the dark"),!0}return!1}calculateTargetZoom(){const[t,e,s,i]=this.stepBbox??[0,this.worldH,0,this.worldW],a=this.worldW;let n=0;for(let M=t;M=1&&A<=10&&++n>=5)break}const h=n>=5,r=[],l=[];for(let M=t;M=1&&(this.age[A]??0)<=10:this.grid[A]!==0)&&(r.push(M),l.push(_))}if(r.length<2)return this.zoomLevel;const c=Int32Array.from(l).sort(),d=nt(r,15),p=nt(r,85),y=nt(c,15),m=nt(c,85),v=Math.max(4,p-d+1),x=Math.max(4,m-y+1);let b=it;for(let M=ct;M>=it;M--){const _=2**M;if(v<=this.viewH/_*.5&&x<=this.viewW/_*.5){b=M;break}}return V(b,it,ct)}updateCamera(){if(!this.autoCam)return;const t=30,e=Math.max(0,this.camY-t),s=Math.min(this.worldH,this.camY+this.viewH+t),i=Math.max(0,this.camX-t),a=Math.min(this.worldW,this.camX+this.viewW+t),n=this.worldW;let h=0,r=0,l=0;for(let y=e;y25){this.steer(Math.trunc(r/h)+e,Math.trunc(l/h)+i),this.advanceAutoZoom();return}let c=0,d=0,p=0;for(let y=e;y0&&this.steer(Math.trunc(d/c)+e,Math.trunc(p/c)+i),this.advanceAutoZoom()}steer(t,e){const s=V(t-f(this.viewH,2),0,this.worldH-this.viewH),i=V(e-f(this.viewW,2),0,this.worldW-this.viewW);this.camY=Math.trunc(this.camY+(s-this.camY)*.04),this.camX=Math.trunc(this.camX+(i-this.camX)*.04)}advanceAutoZoom(){if(this.zoomCooldown=Math.max(0,this.zoomCooldown-1),this.zoomCooldown!==0||this.generation%4!==0)return;const t=this.calculateTargetZoom();if(t>this.zoomLevel&&this.zoomLevelit){const e=this.zoomLevel-1,s=e>=0?1:1<<-e;this.viewH*s<=this.worldH&&this.viewW*s<=this.worldW&&(this.zoomLevel=e,this.zoomCooldown=45)}}clampCam(){return[V(this.camY,0,this.worldH-this.viewH),V(this.camX,0,this.worldW-this.viewW)]}viewCenter(){const[t,e]=this.clampCam();return[t+f(this.viewH,2),e+f(this.viewW,2)]}rawViewGrid(){const[t,e]=this.clampCam(),s=new Uint8Array(this.viewH*this.viewW);for(let i=0;ithis.worldH||c>this.worldW)return null;const d=Math.max(0,Math.min(t-f(l,2),this.worldH-l)),p=Math.max(0,Math.min(e-f(c,2),this.worldW-c));return[d,p,l,c,r,!1]}const s=1<Math.trunc(t[m]??0),i=this.zoomLevel===0?null:this.cover();if(!i){const[m,v]=this.clampCam(),x=new Int32Array(this.viewH*this.viewW);for(let b=0;b(b[E]??0)&&(b[E]=F);const Z=F!==0?F:x;Z>(M[E]??0)&&(M[E]=Z)}}const _=new Int32Array(m*v);for(let A=0;A<_.length;A++){const w=b[A]??0,W=M[A]??0;_[A]=w>0?w:W>x?W:0}return{h:m,w:v,data:_}}const d=Math.min(h*l,this.viewH),p=Math.min(r*l,this.viewW),y=new Int32Array(d*p);for(let m=0;mit){const t=this.zoomLevel-1,e=t>=0?1:1<<-t;(t>=0||this.viewH*e<=this.worldH&&this.viewW*e<=this.worldW)&&(this.zoomLevel=t)}this.invalidate()}autoFocus(){const t=this.worldH,e=this.worldW;let s=0,i=0;for(let H=0;H=1&&C<=10&&s++,this.grid[H]&&i++}const a=s>=5;if((a?s:i)===0)return;const n=H=>{const C=this.age[H]??0;return a?C>=1&&C<=10:this.grid[H]!==0},h=16,r=f(t,h),l=f(e,h);if(r<1||l<1)return;const c=new Float64Array(r*l);for(let H=0;Hp&&(p=C,y=H)}const m=f(y,l)*h+f(h,2),v=y%l*h+f(h,2),x=h*2,b=Math.max(0,m-x),M=Math.min(t,m+x),_=Math.max(0,v-x),A=Math.min(e,v+x),w=[],W=[];for(let H=b;H=it;H--){const C=2**H;if(j<=this.viewH/C*.6&&O<=this.viewW/C*.6){P=H;break}}this.zoomLevel=V(P,it,ct),this.camY=V(et-f(this.viewH,2),0,this.worldH-this.viewH),this.camX=V(S-f(this.viewW,2),0,this.worldW-this.viewW),this.autoCam=!1,this.invalidate()}pan(t,e){this.autoCam=!1;const s=2**-this.zoomLevel,i=t!==0?Math.max(1,Math.trunc(Math.abs(t)*s))*Math.sign(t):0,a=e!==0?Math.max(1,Math.trunc(Math.abs(e)*s))*Math.sign(e):0;this.camY=V(this.camY+i,0,this.worldH-this.viewH),this.camX=V(this.camX+a,0,this.worldW-this.viewW),this.invalidate()}home(){this.autoCam=!0,this.zoomLevel=0,this.invalidate()}clear(){this.grid.fill(0),this.age.fill(0),this.ageSmooth.fill(0),this.generation=0,this.popHistory=[],this.hashHistory=[],this.invalidate()}takeCensus(){const t=this.rawViewGrid();let e=!1;for(let r=0;r[d,l+p,c+y]),s=r.counts}}catch{}if(this.lastCensus=s,!s.size||this.generation-this.lastSightingGen<600)return;let i="",a=0;for(const[r,l]of s){if(l<($e[r]??1)||this.generation-(this.sightingGen.get(r)??-1e9)<1800)continue;const c=Ft[r]??0;c>a&&(a=c,i=r)}if(!i)return;const n=je[i]??[],h=this.sightingCount.get(i)??0;this.sightingCount.set(i,h+1),this.ticker.queueSpecial(n[h%n.length]??i),this.sightingGen.set(i,this.generation),this.lastSightingGen=this.generation}snapshot(){return{h:this.worldH,w:this.worldW,age:this.age.slice(),generation:this.generation,totalInjections:this.totalInjections}}adopt(t){const e=Math.min(this.worldH,t.h),s=Math.min(this.worldW,t.w),i=f(t.h-e,2),a=f(t.w-s,2),n=f(this.worldH-e,2),h=f(this.worldW-s,2);this.grid.fill(0),this.age.fill(0);let r=0;for(let l=0;l0&&(this.grid[p]=1,r++)}this.generation=Math.max(0,Math.trunc(t.generation)),this.totalInjections=Math.max(0,Math.trunc(t.totalInjections)),this.ageSmooth.fill(0),this.activity.fill(0),this.hashHistory=[],this.cachedPop=r,this.popHistory=[r],this.stepBbox=null,this.dramaUntil=0,this.invalidate()}toggleHaunted(){if(this.haunted=!this.haunted,this.invalidate(),!this.haunted)for(let t=0;t=this.worldH||e<0||e>=this.worldW)return;const i=t*this.worldW+e;this.grid[i]=s,this.age[i]=s?1:0,this.invalidate()}tickTicker(t){this.ticker.tick(t,this.detectMood(),this.generation)}detectMood(){return this.cachedMoodGen===this.generation?this.cachedMood:(this.cachedMood=this.computeMood(),this.cachedMoodGen=this.generation,this.cachedMood)}computeMood(){if(this.haunted)return"haunted";if(this.lastEvent&&this.generation-this.lastEventGen<90)return"injection";if(this.cyclePeriod>0)return"cycle";if(this.generation>=1e4&&this.generation%1e4<120)return"milestone";const t=this.popHistory,e=t.length;if(e>=30){let a=0,n=0;for(let r=e-30;r1.15)return"booming";if(h<.85)return"declining"}const s=e?t[e-1]??0:0;return s/Math.max(this.viewH*this.viewW,1)>.15?"dense":s0?"sparse":this.spread=150?"stagnant":""}epoch(){var e;let t=((e=oe[0])==null?void 0:e[1])??"genesis";for(const[s,i]of oe)this.generation>=s&&(t=i);return t}population(){return this.cachedPop}activityCenterX(){const[t,e]=this.clampCam();let s=0,i=0;for(let a=0;athis.actBaseline?.015:.003;this.actBaseline=this.actBaseline*(1-e)+t*e;const s=t/Math.max(this.actBaseline,1);let i=s>1?Math.min(1.6,1+(s-1)*.7):.7+s*.3;return t<60&&(i=Math.min(i,.7)),this.dilation=this.dilation*.85+i*.15,this.dilation}sparkline(t=24){const e=this.popHistory;if(e.length<2)return"";const s=Math.max(0,e.length-t);let i=e[s]??0,a=i;for(let l=s;la&&(a=c)}const n=vt.length-1,h=vt[Math.floor(vt.length/2)]??"▅";let r="";for(let l=s;l{for(;a>127;)t.push(a&127|128),a=Math.floor(a/128);t.push(a)};let s=-1;for(let a=0;a=0?n*2:-n*2-1),s=a)}let i="";for(let a=0;a{let h=0,r=1;for(;;){if(i>=e.length)throw new Error("afterlife: truncated save");const l=e.charCodeAt(i++);if(h+=(l&127)*r,l<128)return h;r*=128}};let n=-1;for(;i=t)throw new Error("afterlife: save does not fit its own world");s[n]=h%2?-(h+1)/2:h/2}return s}function Ze(o){var t;try{const e=(t=o??globalThis.localStorage)==null?void 0:t.getItem(Wt);if(!e)return null;const s=JSON.parse(e);if(s.v!==1||!Number.isInteger(s.h)||!Number.isInteger(s.w)||typeof s.cells!="string")return null;const i=s.h,a=s.w;if(i<=0||a<=0||i*a>16e6)return null;const n=Number(s.generation);return!Number.isSafeInteger(n)||n<0?null:{h:i,w:a,age:Xe(s.cells,i*a),generation:n,totalInjections:Number(s.totalInjections)||0,bornAt:Number(s.bornAt)||Date.now(),savedAt:Number(s.savedAt)||Date.now()}}catch(e){return console.warn("afterlife: could not load the universe, starting fresh",e),null}}function Ke(o,t){try{const e=t??globalThis.localStorage,s={v:1,h:o.h,w:o.w,generation:o.generation,totalInjections:o.totalInjections,bornAt:o.bornAt,savedAt:o.savedAt,cells:Ye(o.age)},i=JSON.stringify(s);return i.length>qe?(console.warn(`afterlife: the universe is too big to keep (${i.length} chars)`),!1):e?(e.setItem(Wt,i),!0):!1}catch(e){return console.warn("afterlife: could not save the universe",e),!1}}const Qe=`gen,time_s,total_pop,spread_150,pop_floor,cycle_period,zoom,cam_y,cam_x,event +`,ne=5e4;class Je{constructor(){g(this,"lines",[]);g(this,"t0",performance.now())}log(t){const e=(performance.now()-this.t0)/1e3;this.lines.push(`${t.gen},${e.toFixed(1)},${t.pop},${t.spread},${t.floor},${t.cycle},${t.zoom},${t.camY},${t.camX},${t.event??""} +`),this.lines.length>ne*1.2&&(this.lines=this.lines.slice(-ne))}get length(){return this.lines.length}csv(){return Qe+this.lines.join("")}}const ts='"use strict";(()=>{var p=2*Math.PI,E="chiptune",N="ambient",U=[E,N],Z={lydian:[0,2,4,6,7,9,11],aeolian:[0,2,3,5,7,8,10],whole_tone:[0,2,4,6,8,10],dorian:[0,2,3,5,7,9,10],minor_pentatonic:[0,3,5,7,10],mixolydian:[0,2,4,5,7,9,10],major_pentatonic:[0,2,4,7,9]},G=.22,J=20,K={booming:"lydian",declining:"aeolian",cycle:"whole_tone",stagnant:"dorian",sparse:"minor_pentatonic",dense:"mixolydian",injection:"major_pentatonic",haunted:"whole_tone"},C="major_pentatonic",Q={genesis:48,primordial:46,emergence:50,expansion:53,flourishing:55,"deep time":51,eon:48,eternity:45},T=48,ee=[.005,.05,.7,.02],te=[.06,.25,.75,.5],O=4,se={generation:0,population:0,pop_floor:100,density:0,spread:0,cycle_period:0,mood:"",epoch:"genesis",pop_delta:0,playhead_column:[],playhead_position:0,viewport_rows:40,event:"",activity_x:.5},j=class{root_midi=T;target_root_midi=T;scale_name=C;scale_intervals=Z[C]??[];prev_scale_intervals=Z[C]??[];scale_crossfade=1;tempo_bpm=80;drone_volume=.3;melody_volume=.2;arp_volume=.15;noise_volume=.05;arp_speed=4;arp_pattern=[0,2,4,7];playhead_notes=[]};function P(n){return 440*2**((n-69)/12)}function oe(n,e,t=2){let s=[];for(let o=0;o(n%e+e)%e,B=class{alpha;z=0;constructor(e,t=44100){let s=1/(p*e),o=1/t;this.alpha=o/(s+o)}apply(e,t,s){let o=this.alpha,r=this.z;for(let i=0;i=0&&n<1?2*n-n*n-1:n>=-1&&n<0?n*n+2*n+1:0}function Y(n,e,t,s,o){let r=s/44100;if(!(s>0)){let i=0;for(let a=1;a1?i/(e-1):.01}for(let i=0;i0){a1-r&&(l+=W((a-1)/r));let h=(a-t)/r;Math.abs(h)<1&&(l-=W(h))}o[i]=l}return o}var k=class{constructor(e,t,s,o){this.sustain=s;this.attack=Math.max(.001,e),this.decay=Math.max(.001,t),this.release=Math.max(.001,o)}sustain;attack;decay;release;generate(e,t,s,o,r){for(let i=0;i=0){let l=a-o;r[i]=l<0?this.sustain:this.sustain*Math.max(0,1-l/this.release)}else r[i]=0}return r}},ie=new k(...ee),ae=new k(...te),L=class{constructor(e=440,t=N){this.style=t;this.freq=e,this.target_freq=e}style;freq;target_freq;phase=0;phase_det=0;active=!1;gate=!1;elapsed=0;release_time=-1;phases=new Float64Array(2048);phasesDet=new Float64Array(2048);osc=new Float32Array(2048);env=new Float32Array(2048);noteOn(e){this.target_freq=e,this.freq=e,this.gate=!0,this.active=!0,this.elapsed=0,this.release_time=-1}retrigger(e){this.target_freq=e,this.active?this.gate||(this.gate=!0,this.release_time=-1):this.noteOn(e)}noteOff(){this.gate=!1,this.release_time=this.elapsed}heal(){[this.freq,this.target_freq,this.phase,this.phase_det,this.elapsed,this.release_time].every(Number.isFinite)||(this.freq=this.target_freq=440,this.phase=this.phase_det=0,this.active=this.gate=!1,this.elapsed=0,this.release_time=-1)}render(e){this.osc.length0&&(this.freq=l,this.phase=A(h,p));let f;if(this.style===E)Y(i,e,.25,this.freq,t),f=ie;else{e>0&&(this.phase_det=A(u,p));for(let c=0;c=0&&this.elapsed-this.release_time>f.release&&(this.active=!1);for(let c=0;cnew Float32Array(e),s=()=>new Float64Array(e);this.left=t(),this.right=t(),this.stereo=[this.left,this.right],this.drone=t(),this.droneOld=t(),this.melody=t(),this.arp=t(),this.noise=t(),this.droneRamp=t(),this.melodyRamp=t(),this.arpRamp=t(),this.noiseRamp=t(),this.melTheta=t(),this.arpTheta=t(),this.lfo=t(),this.ph1=s(),this.ph1d=s(),this.ph2=s(),this.ph2d=s(),this.phs=s(),this.osc0=t(),this.osc1=t(),this.osc2=t(),this.osc3=t(),this.noiseRaw=t(),this.noiseEnv=t()}size;left;right;stereo;drone;droneOld;melody;arp;noise;cadence=null;droneRamp;melodyRamp;arpRamp;noiseRamp;melTheta;arpTheta;lfo;ph1;ph1d;ph2;ph2d;phs;osc0;osc1;osc2;osc3;noiseRaw;noiseEnv},H=class{muted=!1;masterVolume=.5;styleIdx=1;style=U[1];musical=new j;snapshot=se;dronePhase1=0;dronePhase1Det=0;dronePhase2=0;dronePhase2Det=0;dronePhaseSub=0;droneLfoPhase=0;melodyVoices;arpVoice;arpNoteIdx=0;arpSamplesInNote=0;noiseBurstAmp=0;noiseBurstPhase=1;noiseBurstDuration=.15;noiseBurstThreshold=20;noiseBurstCooldown=0;smoothDroneVol=.3;smoothMelodyVol=0;smoothArpVol=0;smoothNoiseVol=0;currentRootHz=P(T);targetRootHz=P(T);crossfadeRemaining=0;crossfadeTotal=Math.floor(44100/4);oldStyle=this.style;noisePool=ne(44100);noisePoolIdx=0;lpfDrone=new B(800);lpfMelody=new B(3500);lpfArp=new B(4500);lpfNoise=new B(600);buf=new z(2048);prevMelodyPan=.5;prevArpPan=.5;autopanPhase=0;cadenceVoice;cadenceSeq=[];cadenceIdx=-1;cadenceRemaining=0;cadenceCooldown=0;constructor(){this.melodyVoices=Array.from({length:O},()=>new L(440,this.style)),this.arpVoice=new L(440,this.style),this.cadenceVoice=new L(440,this.style)}get volumePercent(){return Math.round(this.masterVolume*100)}toggleMute(){this.muted=!this.muted}adjustVolume(e){this.masterVolume=Math.max(0,Math.min(1,this.masterVolume+e))}cycleStyle(){this.oldStyle=this.style,this.styleIdx=(this.styleIdx+1)%U.length,this.style=U[this.styleIdx]??N,this.crossfadeRemaining=this.crossfadeTotal;for(let e of this.melodyVoices)e.style=this.style;this.arpVoice.style=this.style,this.cadenceVoice.style=this.style}setStyle(e){e!==this.style&&this.cycleStyle()}update(e){this.snapshot=e;let t=this.musical;t.target_root_midi=Q[e.epoch]??T,this.targetRootHz=P(t.target_root_midi);let s=K[e.mood]??C;s!==t.scale_name&&(t.prev_scale_intervals=t.scale_intervals,t.scale_name=s,t.scale_intervals=Z[s]??[],t.scale_crossfade=0),t.scale_crossfade<1&&(t.scale_crossfade=Math.min(1,t.scale_crossfade+.017));let o=e.pop_floor>0?Math.min(2,e.population/Math.max(1,e.pop_floor)):.5;t.drone_volume=_(.13,.38,Math.min(1,o)),t.melody_volume=_(.08,.35,Math.min(1,e.density*10)),e.cycle_period>0?(t.arp_speed=_(3,12,Math.min(1,10/Math.max(1,e.cycle_period))),t.arp_volume=.2):(t.arp_speed=_(2,6,Math.min(1,e.spread/200)),t.arp_volume=_(.05,.15,Math.min(1,e.spread/100)));let r=t.scale_intervals;if(r.length>=3?t.arp_pattern=[r[0]??0,r[Math.min(2,r.length-1)]??0,r[Math.min(4,r.length-1)]??0,(r[0]??0)+12]:t.arp_pattern=[0,4,7,12],t.noise_volume=_(0,.15,Math.min(1,Math.abs(e.pop_delta)/50)),this.computePlayheadNotes(e,t),t.tempo_bpm=_(60,120,Math.min(1,e.density*8)),e.event.startsWith("inject")&&e.event.includes("cycle")&&this.cadenceIdx<0&&this.cadenceCooldown<=0){let i=t.target_root_midi+12;this.cadenceSeq=[[P(i+12),Math.trunc(.4*44100)],[P(i+7),Math.trunc(.4*44100)],[P(i),Math.trunc(2*44100)]],this.cadenceRemaining=0,this.cadenceCooldown=Math.trunc(J*44100),this.cadenceIdx=0}}computePlayheadNotes(e,t){let s=e.playhead_column;if(!s.length||e.viewport_rows<2){t.playhead_notes=[];return}let o=s.length,r=oe(t.target_root_midi,t.scale_intervals,2);if(!r.length){t.playhead_notes=[];return}let i=[];if(s.forEach((l,h)=>{l&&i.push(h)}),i.length<2&&e.density<.03){t.playhead_notes=[];return}let a=i.map(l=>{let h=1-l/Math.max(1,o-1),u=Math.max(0,Math.min(Math.trunc(h*(r.length-1)),r.length-1));return r[u]??0});if(a.length>O){a.sort((h,u)=>h-u);let l=a.length/O;a=Array.from({length:O},(h,u)=>a[Math.trunc(u*l)]??0)}t.playhead_notes=a}renderBlock(e,t,s=e.length){let o,r;try{this.mixStereo(s),o=this.buf.left,r=this.buf.right}catch{o=this.buf.left.fill(0),r=this.buf.right.fill(0)}let i=this.muted?0:this.masterVolume;for(let a=0;aNumber.isFinite(t)?t:s;for(let t of[this.lpfDrone,this.lpfMelody,this.lpfArp,this.lpfNoise])t.heal();for(let t of[...this.melodyVoices,this.arpVoice,this.cadenceVoice])t.heal();this.dronePhase1=e(this.dronePhase1,0),this.dronePhase1Det=e(this.dronePhase1Det,0),this.dronePhase2=e(this.dronePhase2,0),this.dronePhase2Det=e(this.dronePhase2Det,0),this.dronePhaseSub=e(this.dronePhaseSub,0),this.droneLfoPhase=e(this.droneLfoPhase,0),this.smoothDroneVol=e(this.smoothDroneVol,0),this.smoothMelodyVol=e(this.smoothMelodyVol,0),this.smoothArpVol=e(this.smoothArpVol,0),this.smoothNoiseVol=e(this.smoothNoiseVol,0),this.currentRootHz=e(this.currentRootHz,e(this.targetRootHz,P(T))),this.prevMelodyPan=e(this.prevMelodyPan,.5),this.prevArpPan=e(this.prevArpPan,.5),this.autopanPhase=e(this.autopanPhase,0),this.arpSamplesInNote=e(this.arpSamplesInNote,0),this.noiseBurstAmp=e(this.noiseBurstAmp,0),this.noiseBurstPhase=e(this.noiseBurstPhase,1),this.noiseBurstDuration=e(this.noiseBurstDuration,.15),this.noiseBurstCooldown=e(this.noiseBurstCooldown,0)}renderLayers(e){let t=this.musical,s=e/44100,o=this.buf,r=.15,i=this.smoothDroneVol,a=this.smoothMelodyVol,l=this.smoothArpVol,h=this.smoothNoiseVol;if(this.smoothDroneVol=_(i,t.drone_volume,r),this.smoothMelodyVol=_(a,t.melody_volume,r),this.smoothArpVol=_(l,t.arp_volume,r),this.smoothNoiseVol=_(h,t.noise_volume,r),V(i,this.smoothDroneVol,e,o.droneRamp),V(a,this.smoothMelodyVol,e,o.melodyRamp),V(l,this.smoothArpVol,e,o.arpRamp),V(h,this.smoothNoiseVol,e,o.noiseRamp),this.currentRootHz=_(this.currentRootHz,this.targetRootHz,.05),this.crossfadeRemaining>0){let u=1-this.crossfadeRemaining/Math.max(1,this.crossfadeTotal);this.crossfadeRemaining=Math.max(0,this.crossfadeRemaining-e);let v=this.renderDrone(e,o.drone),f=this.style;this.style=this.oldStyle;let m=this.renderDrone(e,o.droneOld);this.style=f;let c=Math.fround(Math.sqrt(u)),F=Math.fround(Math.sqrt(1-u));for(let g=0;g0&&(this.cadenceCooldown-=e),this.cadenceIdx<0)return null;if(this.cadenceRemaining<=0){let t=this.cadenceSeq[this.cadenceIdx];if(t){let[s,o]=t;this.cadenceIdx===0?this.cadenceVoice.noteOn(s):this.cadenceVoice.retrigger(s),this.cadenceRemaining=o,this.cadenceIdx+=1}else if(this.cadenceVoice.gate)this.cadenceVoice.noteOff();else if(!this.cadenceVoice.active)return this.cadenceIdx=-1,null}return this.cadenceRemaining-=e,this.cadenceVoice.render(e)}renderDrone(e,t){let s=this.buf,o=this.currentRootHz,r=o*1.5,i=1/44100,a=.08,l=s.lfo,h=this.droneLfoPhase;for(let d=0;di.target_freq*.06?i.noteOn(a):i.retrigger(a):i.noteOn(a):i.gate&&i.noteOff()}let s=this.style===N?.5:.3,o=this.buf.melody.fill(0,0,e);for(let r of this.melodyVoices){if(!r.active)continue;let i=r.render(e);for(let a=0;a1)&&this.arpVoice.noteOn(h),this.arpVoice.style=this.style;let u=this.arpVoice.render(l),v=this.style===N?.55:.4;for(let f=0;f=i&&(this.arpSamplesInNote=0,this.arpNoteIdx=(this.arpNoteIdx+1)%s.length,this.arpVoice.noteOn(P(r+(s[this.arpNoteIdx%s.length]??0))))}this.style===E&&this.lpfArp.apply(o,e,o)}renderNoise(e,t){let s=this.buf,o=s.noise,r=Math.abs(this.snapshot.pop_delta);if(this.noiseBurstCooldown>0&&(this.noiseBurstCooldown=Math.max(0,this.noiseBurstCooldown-t)),r>=this.noiseBurstThreshold&&this.noiseBurstPhase>=1&&this.noiseBurstCooldown<=0&&(this.noiseBurstPhase=0,this.noiseBurstAmp=_(.1,.5,Math.min(1,r/80)),this.noiseBurstDuration=_(.08,.25,Math.min(1,r/100)),this.noiseBurstCooldown=this.noiseBurstDuration+.1),this.noiseBurstPhase>=1){o.fill(0,0,e);return}let i=this.noisePool,a=this.noisePoolIdx%i.length,l=s.noiseRaw;for(let m=0;m{let t=e.data;t.type==="snapshot"?this.engine.update(t.snap):t.type==="controls"?(this.engine.muted=t.muted,this.engine.masterVolume=t.volume,this.engine.setStyle(t.style)):t.type==="stop"&&(this.running=!1)}}process(e,t){let s=t[0],o=s?.[0];if(!s||!o)return this.running;let r=s[1]??o,i=0;for(;i=2048&&(this.renderBlock(),this.pos=0);let a=Math.min(o.length-i,2048-this.pos),l=this.pos-i;for(let h=i;h{}),this.starting??Promise.resolve(this.running)}if(this.failures>=3)return Promise.resolve(!1);Tt.preferPlayback();let t=null;const e=(async()=>{try{t=new AudioContext({sampleRate:es,latencyHint:"playback"}),this.ctx=t;const s=URL.createObjectURL(new Blob([ts],{type:"text/javascript"}));try{await t.audioWorklet.addModule(s)}finally{URL.revokeObjectURL(s)}const i=new AudioWorkletNode(t,"afterlife-music",{numberOfInputs:0,outputChannelCount:[2]});return i.connect(t.destination),this.node=i,this.sendControls(),t.state==="suspended"&&await t.resume().catch(()=>{}),!0}catch(s){return console.warn("afterlife: no music tonight",s),this.failures++,t&&t.close().catch(()=>{}),this.ctx===t&&(this.ctx=null,this.node=null,this.starting=null),!1}})();return this.ctx&&(this.starting=e),e}static preferPlayback(){try{const t=navigator.audioSession;t&&t.type!=="playback"&&(t.type="playback")}catch{}}post(t){var e;(e=this.node)==null||e.port.postMessage(t)}sendControls(){this.post({type:"controls",muted:this.muted,volume:this.volume,style:this.style})}update(t){this.post({type:"snapshot",snap:t})}toggleMute(){this.muted=!this.muted,this.sendControls()}adjustVolume(t){this.volume=Math.max(0,Math.min(1,Math.round((this.volume+t)*100)/100)),this.sendControls()}cycleStyle(){this.style=Mt[(Mt.indexOf(this.style)+1)%Mt.length]??Mt[1],this.sendControls()}suspend(){var t;(t=this.ctx)==null||t.suspend().catch(()=>{})}resume(){var t;(t=this.ctx)==null||t.resume().catch(()=>{})}statusString(){return this.running?ss(this.muted,this.style,this.volume):""}stop(){var t;this.post({type:"stop"}),(t=this.ctx)==null||t.close().catch(()=>{}),this.ctx=null,this.node=null,this.starting=null}}const os=[[0,0,0],[205,0,0],[0,205,0],[205,205,0],[0,0,238],[205,0,205],[0,205,205],[229,229,229],[127,127,127],[255,0,0],[0,255,0],[255,255,0],[92,92,255],[255,0,255],[0,255,255],[255,255,255]],Nt=[0,95,135,175,215,255];function ut(o){if(o<16)return[...os[o]??[0,0,0]];if(o<232){const e=o-16;return[Nt[Math.floor(e/36)]??0,Nt[Math.floor(e/6)%6]??0,Nt[e%6]??0]}const t=8+(o-232)*10;return[t,t,t]}function ht(o){return(255<<24|o[2]<<16|o[1]<<8|o[0])>>>0}function re(o){return`rgb(${o[0]},${o[1]},${o[2]})`}const Dt=[10,10,12],is="rgb(200,200,200)",ft="rgb(112,112,116)",he=ot.length,mt=ot.map(o=>ht(ut(o))),Vt=U.map(o=>ht(ut(o))),le=ke.map(([o,t])=>[ht(ut(o)),ht(ut(t))]),ns=Ie.map(([o,t])=>[ht(ut(o)),ht(ut(t))]),kt=ht(Dt),as='ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace';let It=null;function rs(o){if(It&&It.maxAge===o)return It.table;const t=new Uint8Array(o+1),e=Math.log1p(o);for(let s=1;s<=o;s++)t[s]=Math.min(Math.trunc(Math.log1p(s)/e*(he-1)),he-1);return It={maxAge:o,table:t},t}function xt(o){return o.toLocaleString("en-US")}class hs{constructor(t){g(this,"cw",8);g(this,"cols",80);g(this,"rows",24);g(this,"statusRows",1);g(this,"keys",!0);g(this,"img",null);g(this,"widths",new Map);g(this,"px",null);g(this,"off",null);this.ctx=t}get gridRows(){return Math.max(1,this.rows-this.statusRows)}resize(t,e,s){this.cw=Math.max(2,Math.round(s)),this.cols=Math.max(20,Math.floor(t/this.cw)),this.rows=Math.max(6,Math.floor(e/(this.cw*2))),this.statusRows=this.cols>=150?1:2,this.keys=this.cols>=150,this.img=null,this.widths.clear()}status(t,e){const s=t.sparkline(this.cols<100?10:24),i=t.autoCam?"auto":"pan",a=Re[t.zoomLevel]??`${2**t.zoomLevel}x`,n=t.autoFocusMode?"[F]":"",h=t.haunted?"[HAUNTED]":"",r=e?` ${e}`:"",l=` ${t.epoch()} gen ${xt(t.generation)} pop ${xt(t.population())} ${s}`,c=this.keys?" q r spc +/- arrows h z/x f g s ":" ",d=`${r} ${h}${n} ${a} ${i}${c}`;return this.statusRows===1?{left:l,right:d,tickerCol:q(l)+1,tickerWidth:this.cols-q(l)-q(d)-2}:{left:l,right:d,tickerCol:1,tickerWidth:this.cols-2}}render(t,e,s){const i=this.ctx,a=this.cw,n=this.cols*a,h=this.rows*a*2;i.fillStyle=re(Dt),i.fillRect(0,0,n,h),this.paintGrid(t),i.font=`${Math.round(a*1.62)}px ${as}`,i.textBaseline="middle",s&&this.statsOverlay(t);const r=this.rows-1;if(this.statusRows===1)if(e.tickerWidth<10)this.text(`${e.left} ${e.right}`.slice(0,this.cols-1),r,0,ft);else{this.text(e.left,r,0,ft),this.ticker(t,r,e);const l=this.cols-q(e.right);this.text([...e.right].slice(0,this.cols-1-l).join(""),r,l,ft)}else{const l=this.cols-1,c=q(e.left)+q(e.right)<=l?e.left+" ".repeat(l-q(e.left)-q(e.right))+e.right:`${e.left} ${e.right}`;this.text([...c].slice(0,l).join(""),r-1,0,ft),this.ticker(t,r,e)}}paintGrid(t){const e=t.displayGrid(),s=t.displayAge(),i=Math.min(Math.floor(e.h/2),Math.floor(s.h/2),this.gridRows),a=Math.min(e.w,s.w,this.cols),n=this.cols,h=this.gridRows*2;(!this.img||this.img.width!==n||this.img.height!==h)&&(this.img=new ImageData(n,h),this.px=new Uint32Array(this.img.data.buffer),this.off=typeof OffscreenCanvas<"u"?new OffscreenCanvas(n,h):Object.assign(document.createElement("canvas"),{width:n,height:h}));const r=this.px;r.fill(kt);let l=1;for(let w=0;wl&&(l=s.data[w]??0);const c=rs(l),d=t.haunted,p=t.zoomLevel>=0||d,y=d?rt:G,m=w=>Math.min(Math.max(-w-1,0),y-1),v=w=>c[Math.min(Math.max(w,0),l)]??0,x=w=>{var W;return d?((W=le[w])==null?void 0:W[0])??0:Vt[w]??0},b=w=>{var W;return d?((W=le[w])==null?void 0:W[1])??0:kt};for(let w=0;w0,et=(e.data[R+j]??0)>0,S=s.data[F+j]??0,P=s.data[E+j]??0,H=p&&S<0,C=p&&P<0;if(!(O||et||H||C))continue;let N=kt,$=kt;if((O||H)&&(et||C))if(O&&et)N=mt[v(S)]??0,$=mt[v(P)]??0;else if(H&&C){const D=m(S),K=m(P);if(d){const st=ns[D*rt+K];N=(st==null?void 0:st[0])??0,$=(st==null?void 0:st[1])??0}else N=Vt[D]??0,$=Vt[K]??0}else O?N=mt[v(S)]??0:$=mt[v(P)]??0;else if(O)N=mt[v(S)]??0;else if(et)$=mt[v(P)]??0;else if(H){const D=m(S);N=x(D),$=b(D)}else{const D=m(P);$=x(D),N=b(D)}r[Z+j]=N,r[yt+j]=$}}const M=this.off,_=M.getContext("2d");if(!_||!this.img)return;_.putImageData(this.img,0,0);const A=this.ctx;A.imageSmoothingEnabled=!1,A.drawImage(M,0,0,n*this.cw,h*this.cw)}ticker(t,e,s){const i=ie.dim(t.generation)?ft:is;for(const a of t.ticker.messages){const n=Math.trunc(a.x),h=[...a.text],r=Math.max(n,0),l=Math.min(n+h.length,s.tickerWidth);r>=l||this.text(h.slice(r-n,l-n).join(""),e,s.tickerCol+r,i)}}statsOverlay(t){const e=t.lastCensus.size?[...t.lastCensus.entries()].sort((l,c)=>c[1]-l[1]).map(([l,c])=>`${c}×${l}`).join(" "):"none",s=36,i=11,a=this.cols-s-2,n=this.rows-(this.statusRows-1)-i-2;if(a<0||n<0)return;const h=["─".repeat(s-2)," infinity engine",` pop floor : ${xt(t.popFloor)}`,` spread/150 : ${xt(t.spread)}`,` cycle : ${t.cyclePeriod===0?"none":`period ${t.cyclePeriod}`}`,` injections : ${t.totalInjections}`,` last event : ${t.lastEvent||"none"}`,t.lastEvent?` @ gen : ${xt(t.lastEventGen)}`:" ",` world : ${t.worldH}x${t.worldW}`,` census : ${[...e].slice(0,s-16).join("")}`,` tempo : ${(1/Math.max(t.dilation,.01)).toFixed(2)}x`],r=this.ctx;r.fillStyle=re(Dt),r.fillRect(a*this.cw,n*this.cw*2,s*this.cw,h.length*this.cw*2),h.forEach((l,c)=>{const d=[...` ${l}`.padEnd(s)].slice(0,s).join("");this.text(d,n+c,a,ft)})}text(t,e,s,i){const a=this.ctx,n=this.cw,h=e*n*2;a.fillStyle=i;let r=s;for(const l of t){if(r>=this.cols)break;if(r>=0&&l!==" "){const c=vt.indexOf(l);if(c>=0){const d=Math.max(1,Math.round(n*2*(c+1)/8));a.fillRect(r*n,h+n*2-d,n,d)}else if(l==="─")a.fillRect(r*n,h+n-Math.max(1,Math.round(n/8))/2,n,Math.max(1,Math.round(n/8)));else{let d=this.widths.get(l);d===void 0&&(d=a.measureText(l).width,this.widths.set(l,d)),a.fillText(l,r*n+n/2-d/2,h+n)}}r++}}}const ce=document.getElementById("afterlife-root"),z=document.getElementById("term"),B=document.getElementById("splash"),Y=document.getElementById("keys"),de=z==null?void 0:z.getContext("2d",{alpha:!1});if(!ce||!z||!de)throw new Error("afterlife: the page is missing its terminal");const Bt=window.top!==window,k=new hs(de),T=new Tt,ue=new Je;let u,jt=Date.now(),$t=!1,lt=0;const Gt=18e3;let bt=!1,J=[];function ls(){const o=window.innerWidth;return o>=1100?8:o>=700?7:6}function fe(){const o=Math.min(window.devicePixelRatio||1,3),t=Math.max(2,Math.round(ls()*o));k.resize(Math.floor(window.innerWidth*o),Math.floor(window.innerHeight*o),t);const e=k.cols*k.cw,s=k.rows*k.cw*2;z.width=e,z.height=s,z.style.width=`${e/o}px`,z.style.height=`${s/o}px`}function cs(){const o=k.gridRows,t=k.cols;fe(),(k.gridRows!==o||k.cols!==t)&&ds()}function ds(){const o=u,[t,e]=o.viewCenter();u=new Ct(k.gridRows,k.cols,!1,{minH:o.worldH,minW:o.worldW}),u.adopt(o.snapshot());const s=Math.floor((u.worldH-o.worldH)/2),i=Math.floor((u.worldW-o.worldW)/2);if(o.haunted)for(let a=0;a{cs(),tt()},200)}let Ot=0,ge=!1;function us(o){var i,a;if(!o)return null;const t=o.slice(0,400),e=(i=/"savedAt":(\d+)/.exec(t))==null?void 0:i[1],s=(a=/"generation":(\d+)/.exec(t))==null?void 0:a[1];return e===void 0||s===void 0?null:{savedAt:Number(e),generation:Number(s)}}function we(o){const t=us(o);return t!==null&&t.savedAt!==Ot&&t.generation>u.generation}function ye(){ge||(ge=!0,console.info("afterlife: another window has saved a later universe; this one will not overwrite it"),u.ticker.queueSpecial("another window keeps this universe now"))}function _t(){if(!u||Bt)return;try{if(we(localStorage.getItem(Wt))){ye();return}}catch{}const o=Date.now();Ke({...u.snapshot(),bornAt:jt,savedAt:o})&&(Ot=o)}window.addEventListener("storage",o=>{o.key===Wt&&!Bt&&we(o.newValue)&&ye()}),fe();const pt=Ze();pt?(u=new Ct(k.gridRows,k.cols,!1,{minH:pt.h,minW:pt.w}),u.adopt(pt),jt=pt.bornAt,Ot=pt.savedAt,u.ticker.queueSpecial(`the universe remembers generation ${u.generation.toLocaleString("en-US")}`)):u=new Ct(k.gridRows,k.cols);function ve(){const o=u.step();if(T.running&&!u.paused&&u.generation%2===0)try{const s=u.viewW;lt=(lt+Math.max(1,Math.floor(s/240)))%Math.max(1,s);const i=u.displayGrid(),a=[];if(lt=2?(n[n.length-1]??0)-(n[n.length-2]??0):0,playhead_column:a,playhead_position:lt/Math.max(1,s),viewport_rows:i.h,event:o,activity_x:u.activityCenterX()};T.update(r),bt&&(J.push(JSON.stringify({...r,playhead_column:a.map(l=>l?1:0)})),J.length>Gt&&(J.length===Gt+1&&u.ticker.queueSpecial("the recording is full; its oldest moments are letting go"),J.splice(0,J.length-Gt)))}catch{}u.autoFocusMode&&++u.autoFocusFrame>=2&&(u.autoFocusFrame=0,u.autoFocus());const t=!u.paused&&u.generation>0;t&&u.generation%150===0&&u.takeCensus(),t&&u.generation%Fe===0&&_t(),(o||u.generation%10===0)&&ue.log({gen:u.generation,pop:u.population(),spread:u.spread,floor:u.popFloor,cycle:u.cyclePeriod,zoom:u.zoomLevel,camY:u.camY,camX:u.camX,event:o});const e=k.status(u,Me());e.tickerWidth>=10&&u.tickTicker(e.tickerWidth)}function Me(){let o=T.statusString();return bt&&(o+=` REC(${J.length})`),o}function tt(){k.render(u,k.status(u,Me()),$t)}let gt=0,At=0;const fs=12;function xe(o){At=requestAnimationFrame(xe);const t=performance.now();let e=0;for(;o>=gt-8;)if(ve(),e++,gt=Math.max(gt,o-100)+u.delay*u.timeDilation(),e>=4||performance.now()-t>fs){gt=Math.max(gt,o);break}e&&tt()}function be(){At||(gt=performance.now(),At=requestAnimationFrame(xe))}function ms(){cancelAnimationFrame(At),At=0}function _e(o,t,e){const s=URL.createObjectURL(new Blob([t],{type:e})),i=Object.assign(document.createElement("a"),{href:s,download:o});document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(s),1e3)}function ps(){if(Bt){u.ticker.queueSpecial("the glass holds; eject from the other side");return}_t();const o=document.referrer,t=o&&new URL(o,location.href).origin===location.origin&&!o.includes("/afterlife")?o:"/";location.href=t}const Ut={quit:ps,reseed:()=>{const o=u.haunted;u=new Ct(k.gridRows,k.cols),u.haunted=o,jt=Date.now(),lt=0},pause:()=>{u.paused=!u.paused},faster:()=>{u.delay=Math.max(10,u.delay-10)},slower:()=>{u.delay=Math.min(500,u.delay+10)},home:()=>u.home(),clear:()=>u.clear(),zoomOut:()=>u.zoomOut(),zoomIn:()=>u.zoomIn(),focus:()=>{u.autoFocusMode=!u.autoFocusMode,u.autoFocusMode&&u.autoFocus()},haunt:()=>u.toggleHaunted(),stats:()=>{$t=!$t},record:()=>{if(!T.running){u.ticker.queueSpecial("nothing to record without the music");return}bt=!bt,bt?J=[]:J.length?_e("snapshots.jsonl",`${J.join(` +`)} +`,"application/x-ndjson"):u.ticker.queueSpecial("the recording caught nothing; no snapshots to keep")},mute:()=>T.running&&T.toggleMute(),style:()=>T.running&&T.cycleStyle(),volDown:()=>T.running&&T.adjustVolume(-.1),volUp:()=>T.running&&T.adjustVolume(.1),up:()=>u.pan(-5,0),down:()=>u.pan(5,0),left:()=>u.pan(0,-10),right:()=>u.pan(0,10),statsCsv:()=>_e("life_stats.csv",ue.csv(),"text/csv"),help:()=>Yt()},Ae={q:"quit",r:"reseed"," ":"pause","+":"faster","=":"faster","-":"slower",_:"slower",h:"home",c:"clear",z:"zoomOut",x:"zoomIn",f:"focus",g:"haunt",s:"stats",d:"record",m:"mute",v:"style","[":"volDown","]":"volUp",ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right","?":"help"},gs=new Set(["faster","slower","volDown","volUp","up","down","left","right","zoomOut","zoomIn"]);function wt(o){var t;(t=Ut[o])==null||t.call(Ut),tt()}window.addEventListener("keydown",o=>{if(o.ctrlKey||o.metaKey||o.altKey)return;if(o.key==="Escape"){Y&&!Y.hidden&&Yt(!1);return}const t=Ae[o.key.length===1?o.key.toLowerCase():o.key]??Ae[o.key];t&&(o.preventDefault(),!(o.repeat&&!gs.has(t))&&(t!=="help"&&St(),wt(t)))});let qt=!1;function St(){qt||(qt=!0,B==null||B.classList.add("gone"),setTimeout(()=>B==null?void 0:B.remove(),1600)),T.start()}B==null||B.addEventListener("pointerdown",o=>{o.preventDefault(),o.stopPropagation(),St()}),B==null||B.addEventListener("keydown",o=>{o.key!=="Enter"&&o.key!==" "||(o.preventDefault(),o.stopPropagation(),o.repeat||St())});for(const o of["pointerup","click"])ce.addEventListener(o,()=>{qt&&T.start()});function Yt(o=(Y==null?void 0:Y.hidden)??!1){var t;Y&&(Y.hidden=!o,(t=document.getElementById("keys-toggle"))==null||t.setAttribute("aria-expanded",String(o)))}Y==null||Y.addEventListener("click",o=>{const t=o.target.closest("[data-act]");t&&(o.preventDefault(),St(),wt(t.dataset.act??""))}),(Ce=document.getElementById("keys-toggle"))==null||Ce.addEventListener("click",o=>{o.preventDefault(),Yt()});function Xt(o){const t=z.getBoundingClientRect(),e=t.width/k.cols,s=Math.floor((o.clientX-t.left)/e),i=Math.floor((o.clientY-t.top)/e);return[Math.floor(i/2),s,i%2]}function Zt(o,t){return o>=0&&o=0&&to.preventDefault()),z.addEventListener("pointerdown",o=>{if(St(),X.set(o.pointerId,{x:o.clientX,y:o.clientY}),X.size>=2){if(X.size===2){const[a,n]=[...X.values()];at=a&&n?Math.hypot(a.x-n.x,a.y-n.y):0}L=null;return}z.setPointerCapture(o.pointerId);const t=o.button===1||o.button===2||o.shiftKey,[e,s,i]=Xt(o);L={id:o.pointerId,kind:t?"pan":"paint",x:o.clientX,y:o.clientY,last:"",moved:!1},!t&&o.pointerType==="mouse"&&Zt(e,s)&&(u.toggleCell(e,s,i),L.last=`${e},${s},${i}`,tt())}),z.addEventListener("pointermove",o=>{if(X.has(o.pointerId)&&X.set(o.pointerId,{x:o.clientX,y:o.clientY}),X.size===2&&at){const[r,l]=[...X.values()],c=r&&l?Math.hypot(r.x-l.x,r.y-l.y):at;c/at>1.35?(wt("zoomIn"),at=c):c/at<.74&&(wt("zoomOut"),at=c);return}if(!L||L.id!==o.pointerId)return;const t=o.pointerType!=="mouse";if(!L.moved&&Math.hypot(o.clientX-L.x,o.clientY-L.y)<(t?10:2))return;if(L.moved=!0,L.kind==="pan"||t){const l=z.getBoundingClientRect().width/k.cols,c=Math.trunc((L.x-o.clientX)/l),d=Math.trunc((L.y-o.clientY)/l);(c||d)&&(u.pan(d,c),L.x-=c*l,L.y-=d*l,tt());return}const[e,s,i]=Xt(o),a=`${e},${s},${i}`;if(a===L.last||!Zt(e,s))return;L.last=a;const[n,h]=u.termToWorld(e,s,i);u.setCell(n,h,1),tt()});const Se=o=>{if(X.delete(o.pointerId),X.size<2&&(at=0),!(!L||L.id!==o.pointerId)){if(o.type==="pointerup"&&o.pointerType!=="mouse"&&!L.moved){const[t,e,s]=Xt(o);Zt(t,e)&&(u.toggleCell(t,e,s),tt())}L=null}};z.addEventListener("pointerup",Se),z.addEventListener("pointercancel",Se);let Et=0;z.addEventListener("wheel",o=>{o.preventDefault(),Et+=o.deltaY*(o.deltaMode===1?33:1),!(Math.abs(Et)<120)&&(wt(Et>0?"zoomOut":"zoomIn"),Et=0)},{passive:!1}),window.addEventListener("resize",pe);function He(){try{window.matchMedia(`(resolution: ${window.devicePixelRatio||1}dppx)`).addEventListener("change",()=>{pe(),He()},{once:!0})}catch{}}He(),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?(_t(),ms(),T.suspend()):(T.resume(),be())}),window.addEventListener("pagehide",_t),window.afterlife={get life(){return u},term:k,frame:ve,paint:tt,act:wt,persist:_t},tt(),document.visibilityState!=="hidden"&&be()})(); diff --git a/aethera/static/oikos/oikos.js b/aethera/static/oikos/oikos.js index ca2cbe5..d257275 100644 --- a/aethera/static/oikos/oikos.js +++ b/aethera/static/oikos/oikos.js @@ -1,18 +1,18 @@ -var xv=Object.defineProperty;var vv=(xi,yn,Sn)=>yn in xi?xv(xi,yn,{enumerable:!0,configurable:!0,writable:!0,value:Sn}):xi[yn]=Sn;var I=(xi,yn,Sn)=>vv(xi,typeof yn!="symbol"?yn+"":yn,Sn);(function(){"use strict";var Ru;function xi(){const s=document.getElementById("oikos-data");try{const t=JSON.parse((s==null?void 0:s.textContent)??"{}");return{sites:t.sites??[],files:t.files??[],posts:t.posts??[]}}catch{return{sites:[],files:[],posts:[]}}}const yn=s=>/^https?:\/\//.test(s);class Sn{constructor(t){I(this,"listeners",new Set);this.value=t}set(t){this.value=t;for(const e of this.listeners)e(t)}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}}function $l(){try{const s=localStorage.getItem("syrinx-creature-v1");if(!s)return null;const t=JSON.parse(s);if(typeof t.name!="string"||typeof t.bornAt!="number"||!t.state)return null;const e=Array.isArray(t.state.nodes)?t.state.nodes:[],n=Array.isArray(t.state.edges)?t.state.edges:[];return e.length?{name:t.name,bornAt:t.bornAt,lifetime:typeof t.state.lifetime=="number"?t.state.lifetime:0,nodes:e.map(i=>({id:i.id,x:i.x,y:i.y,z:i.z??500,age:i.age??0})),edges:n.map(i=>({a:i.a,b:i.b,age:i.age??0}))}:null}catch{return null}}function Xl(s){return new Promise((t,e)=>{const n=new Image;n.decoding="async",n.onload=()=>t(n),n.onerror=e,n.src=s})}async function Qs(s){try{const t=await fetch(s,{headers:{Accept:"application/json"}});return t.ok?await t.json():null}catch{return null}}class Bu{constructor(){I(this,"dreams",new Sn({known:!1,awake:!1,frame:0,fps:0,viewers:0,raw:null}));I(this,"chronicle",new Sn({known:!1,thumb:null,thumbAt:0,prompt:"",template:"",eras:[],eraCount:0,strata:[]}));I(this,"irc",new Sn({connected:!1,lines:[],collapse:null,fragments:0,version:0}));I(this,"creature",new Sn($l()));I(this,"apeiron",new Sn(null));I(this,"timers",[]);I(this,"ws",null);I(this,"wsBackoff",2e3);I(this,"wsTimer",0);I(this,"running",!1)}start(){if(this.running)return;this.running=!0;const t=(e,n)=>{e(),this.timers.push(window.setInterval(e,n))};t(()=>void this.pollDreams(),2e4),t(()=>void this.pollChronicle(),12e4),t(()=>this.creature.set($l()),15e3),this.openIrc(),this.apeiron.value||this.loadApeiron()}stop(){this.running=!1;for(const t of this.timers)clearInterval(t);this.timers=[],clearTimeout(this.wsTimer),this.ws&&(this.ws.onclose=null,this.ws.close(),this.ws=null),this.irc.set({...this.irc.value,connected:!1,version:this.irc.value.version+1})}async pollDreams(){var e,n,i,r,a;const t=await Qs("/api/dreams/status");if(!t){this.dreams.set({...this.dreams.value,known:!1});return}this.dreams.set({known:!0,awake:!!((e=t.gpu)!=null&&e.active),frame:((n=t.generation)==null?void 0:n.current_frame)??((i=t.generation)==null?void 0:i.frame_count)??0,fps:((r=t.generation)==null?void 0:r.fps)??0,viewers:((a=t.viewers)==null?void 0:a.websocket_count)??0,raw:t})}async pollChronicle(){var a,o,l,c;const t=await Qs("/api/dreams/chronicle/timeline?hours=3");if(!t)return;const e=this.chronicle.value;let n=e.thumb;(a=t.live)!=null&&a.thumb&&t.live.thumb!==(n==null?void 0:n.dataset.src)&&(n=await Xl(t.live.thumb).catch(()=>e.thumb),n&&(n.dataset.src=t.live.thumb));const i=(t.tiles??[]).slice(-3),r=(await Promise.all(i.map(u=>Xl(u.url).catch(()=>null)))).filter(u=>!!u);this.chronicle.set({known:!0,thumb:n,thumbAt:((o=t.live)==null?void 0:o.t)??0,prompt:((l=t.live)==null?void 0:l.prompt)??"",template:((c=t.live)==null?void 0:c.template)??"",eras:(t.eras??[]).map(u=>({title:u.title??"",t0:u.t0,t1:u.t1,open:!!u.open,kf:u.kf??0})),eraCount:t.era_count??0,strata:r.length?r:e.strata})}openIrc(){const t=location.protocol==="https:"?"wss":"ws";let e;try{e=new WebSocket(`${t}://${location.host}/ws/irc`)}catch{return}this.ws=e;const n=i=>{const r=this.irc.value;this.irc.set({...r,...i,version:r.version+1})};e.onopen=()=>{this.wsBackoff=2e3,n({connected:!0,collapse:null})},e.onmessage=i=>{let r;try{r=JSON.parse(String(i.data))}catch{return}if(r.type==="message"&&r.data){const a=r.data,o=a.meta??{},l={nick:String(a.nick??""),content:String(a.content??""),type:String(a.type??"message"),stamp:String(a.timestamp??""),at:performance.now()};if(typeof a.at=="number"&&(l.wallAt=a.at),typeof o.target=="string"&&(l.target=o.target),typeof o.reason=="string"&&(l.reason=o.reason),r.replay&&this.irc.value.lines.some(c=>c.stamp===l.stamp&&c.nick===l.nick&&c.content===l.content))return;n({lines:[...this.irc.value.lines,l].slice(-60)})}else r.type==="collapse_start"?n({collapse:{type:r.collapseType??"collapse",at:performance.now()}}):r.type==="fragment_end"&&(r.replay?n({collapse:null}):n({collapse:null,lines:this.irc.value.lines.slice(-6),fragments:this.irc.value.fragments+1}))},e.onclose=()=>{n({connected:!1}),this.running&&(this.wsTimer=window.setTimeout(()=>this.openIrc(),this.wsBackoff),this.wsBackoff=Math.min(this.wsBackoff*2,6e4))}}async loadApeiron(){const[t,e]=await Promise.all([Qs("/static/apeiron/data/templates.json"),Qs("/static/apeiron/data/components.json")]);t&&e&&this.apeiron.set({templates:t,components:e})}}const It=512,Wt=384,Yt='"Libertinus Mono", "LibertinusMono", ui-monospace, monospace',oa='"Love Letter Typewriter", "Libertinus Mono", monospace';function la(s,t,e){t();const n=typeof document<"u"?document.fonts:void 0,i=(Array.isArray(s)?s:[s]).filter(r=>n&&!n.check(r));!n||!i.length||Promise.all(i.map(r=>n.load(r))).then(()=>{t(),e==null||e()},()=>{})}class bn{constructor(t,e){I(this,"canvas");I(this,"ctx");I(this,"fps",15);I(this,"acc",1);I(this,"version",0);this.site=t,this.env=e,this.canvas=document.createElement("canvas"),this.canvas.width=It,this.canvas.height=Wt;const n=this.canvas.getContext("2d",{alpha:!1});if(!n)throw new Error("oikos: no 2d context");this.ctx=n}tick(t,e,n,i=1){this.acc+=e;const r=n?Math.max(this.fps,30):this.fps*i;if(this.acc<1/r)return!1;const a=this.acc;return this.acc=0,this.draw(t,a),this.version++,!0}clear(t){this.ctx.fillStyle=t,this.ctx.fillRect(0,0,It,Wt)}wrap(t,e,n=1/0){const i=this.ctx,r=t.split(/\s+/).filter(Boolean),a=[];let o="";for(const l of r){const c=o?`${o} ${l}`:l;if(i.measureText(c).width>e&&o){if(a.push(o),o=l,a.length>=n)break}else o=c}if(o&&a.lengthe;)l=l.replace(/\s*\S*$/,"");a[n-1]=`${l}…`}return a}condensed(t,e,n,i=.72,r="left"){const a=this.ctx;a.save(),a.translate(e,n),a.scale(i,1),a.textAlign=r,a.fillText(t,0,0),a.restore()}spaced(t,e,n,i,r="left"){const a=this.ctx,o=[...t],l=o.map(h=>a.measureText(h).width),c=l.reduce((h,d)=>h+d,0)+i*(o.length-1);let u=r==="center"?e-c/2:e;a.save(),a.textAlign="left",o.forEach((h,d)=>{a.fillText(h,u,n),u+=(l[d]??0)+i}),a.restore()}}function ca(s){let t=2166136261;for(let e=0;e>>0)%1e5/1e5}function vi(s){let t=s>>>0;return()=>{t=t+1831565813>>>0;let e=t;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}}function zu(s){return Math.round(s).toLocaleString("en-US")}const Kn=62,ws=21,Hu=It/Kn,Gu=12.4,Vu=42,ha=" .,:;-=+*#%@";function Wu(){const s=[];for(let e=0;e<16;e++)s.push([e&1?1:-1,e&2?1:-1,e&4?1:-1,e&8?1:-1]);const t=[];for(let e=0;e<16;e++)for(let n=0;n<4;n++){const i=e^1<>>0).toString(16).padStart(8,"0").replace(/(....)(....)/,"$1·$2");return{prompt:a,template:o,coordinate:l,hue:n?Math.floor(ca(a)*360):135,figure:ua[this.seq++%ua.length]??ua[0]??[],bornAt:e}}draw(e){(!this.gen||e-this.gen.bornAt>9||!this.gen.prompt&&this.env.feeds.apeiron.value)&&(this.gen=this.generate(e));const n=this.gen,i=this.ctx,r=`hsl(${n.hue} 100% 58%)`,a=`hsl(${n.hue} 100% 76%)`,o=`hsl(${n.hue} 60% 22%)`,l=i.createRadialGradient(It/2,Wt*.4,20,It/2,Wt*.4,It*.7);l.addColorStop(0,`hsl(${n.hue} 60% 7%)`),l.addColorStop(1,"#030309"),i.fillStyle=l,i.fillRect(0,0,It,Wt),i.font=`12px ${Yt}`,i.fillStyle=r,i.fillText("apeiron",14,22),i.fillStyle=o,i.fillText("· æthera",76,22),i.textAlign="right",i.fillStyle=r,i.fillText(n.template.replace(/_/g," "),It-14,22),i.textAlign="left",this.raster(e,n.figure),i.font=`12px ${Yt}`;for(let d=0;d9?a:p>4?r:o,i.fillText(ha[p]??".",f*Hu,Vu+d*Gu+10))}const c=e-n.bornAt,u=n.prompt||"reading the grammar…";i.font=`12px ${Yt}`;const h=this.wrap(u.slice(0,Math.floor(c*70)),It-28,4);i.fillStyle="rgba(3,3,9,0.7)",i.fillRect(0,Wt-98,It,98),i.fillStyle=o,i.fillRect(14,Wt-98,It-28,1),i.fillStyle=a,h.forEach((d,f)=>i.fillText(d,14,Wt-78+f*15)),i.fillStyle=r,i.font=`11px ${Yt}`,i.fillText(`⌖ ${n.coordinate}`,14,Wt-12),i.fillStyle=o,i.textAlign="right",i.fillText("␣ generate F keep A auto",It-14,Wt-12),i.textAlign="left"}raster(e,n){this.depth.fill(-1/0),this.glyph.fill(0);const i=e*.45,r=e*.31,a=e*.23,[o,l,c,u,h,d]=[Math.cos(i),Math.sin(i),Math.cos(r),Math.sin(r),Math.cos(a),Math.sin(a)];for(const f of n){let[p,v,m,g]=f;[p,g]=[p*o-g*l,p*l+g*o],[v,m]=[v*c-m*u,v*u+m*c],[m,g]=[m*h-g*d,m*d+g*h];const w=2.6/(3.2-g);p*=w,v*=w,m*=w;const E=3.4/(4.6-m),M=Math.round(Kn/2+p*E*11.5),S=Math.round(ws/2+v*E*5.6);if(M<0||M>=Kn||S<0||S>=ws)continue;const b=S*Kn+M;m>(this.depth[b]??-1/0)&&(this.depth[b]=m,this.glyph[b]=Math.max(1,Math.min(ha.length-1,Math.round((m+2.2)/4.4*(ha.length-1)))))}}}function qu(s,t){const e=s.templates[Math.floor(t()*s.templates.length)];return e?{prompt:e.structure.replace(/\{(\w+)\}/g,(i,r)=>{var o;const a=s.components[r];return a!=null&&a.length?((o=a[Math.floor(t()*a.length)])==null?void 0:o.word)??r:r.replace(/_/g," ")}),template:e.id}:{prompt:"",template:""}}const da="#e8e2d4",fa="#a39d90",ql="#5d5850",Mi=34,ki=250,yi=84,js=Wt-yi-22;class Yu extends bn{constructor(e,n){super(e,n);I(this,"standIn");this.fps=10,this.standIn=Ku()}draw(e){const n=this.ctx;this.clear("#070707"),n.fillStyle=da,n.font=`22px ${Yt}`,this.spaced("chronicle",It/2,38,11,"center"),n.fillStyle=fa,n.font=`11px ${Yt}`,this.spaced("what the dream remembers",It/2,60,2,"center");const r=[...this.env.feeds.chronicle.value.strata].reverse();if(n.fillStyle="#000",n.fillRect(Mi-1,yi-1,ki+2,js+2),n.imageSmoothingEnabled=!0,r.length){const l=r.map((h,d)=>Math.pow(.6,d)),c=l.reduce((h,d)=>h+d,0);let u=yi;r.forEach((h,d)=>{const f=js*(l[d]??0)/c;n.save(),n.translate(Mi,u+f),n.scale(1,-1),n.drawImage(h,0,0,ki,f),n.restore(),u+=f,n.fillStyle="rgba(232,226,212,0.08)",n.fillRect(Mi,u,ki,1)})}else n.globalAlpha=.75,n.drawImage(this.standIn,Mi,yi,ki,js),n.globalAlpha=1;const a=yi+e*14%js,o=n.createLinearGradient(0,a-16,0,a+2);o.addColorStop(0,"rgba(232,226,212,0)"),o.addColorStop(1,"rgba(232,226,212,0.22)"),n.fillStyle=o,n.fillRect(Mi,a-16,ki,18),n.fillStyle="rgba(232,226,212,0.5)",n.fillRect(Mi-6,a,4,1),this.log(e)}log(e){const n=this.ctx,i=this.env.feeds.chronicle.value,r=Mi+ki+24,a=It-r-18;n.font=`11px ${Yt}`,n.fillStyle=ql,n.fillText(i.known?`${i.eraCount||i.eras.length} eras`:"reading the core…",r,yi+8);const o=[...i.eras].reverse().slice(0,7);let l=yi+34;if(!o.length){n.fillStyle=fa;for(const c of this.wrap("every fifteen seconds of the dream settles here as one line of its own colour.",a,6))n.fillText(c,r,l),l+=15;return}for(const c of o){if(l>Wt-30)break;const u=new Date(c.t0*1e3);n.fillStyle=c.open?da:ql,n.font=`10px ${Yt}`;const h=`${u.getHours().toString().padStart(2,"0")}:${u.getMinutes().toString().padStart(2,"0")}`;n.fillText(c.open?`${h} now`:h,r,l),c.open&&(n.fillStyle=Math.sin(e*3)>0?"#d6c9a8":"#6d6555",n.fillRect(r-10,l-6,4,4)),n.font=`12px ${Yt}`,n.fillStyle=c.open?da:fa;const d=this.wrap(c.title||"untitled",a,2);d.forEach((f,p)=>n.fillText(f,r,l+15+p*14)),l+=22+d.length*14}}}function Ku(){const s=document.createElement("canvas");s.width=64,s.height=240;const t=s.getContext("2d");if(!t)return s;const e=vi(1729),n=["#5c3b36","#7a4f45","#3f3a4c","#8a6f64","#2d4a4f","#6b2c3a","#a38a74","#40302c"];let i=n[0];for(let r=0;r0?pa:tr,n.globalAlpha=.5+e*.5,n.lineWidth=1,n.strokeRect(t.x+.5,t.y+.5,t.w,t.h),n.globalAlpha=1,n.font=`10px ${Yt}`,n.fillStyle=e>0?ma:Si,n.fillText(t.label,t.x+6,t.y+13)}arrow(t,e,n,i,r,a){const o=this.ctx;o.strokeStyle=tr,o.beginPath(),o.moveTo(t,e),o.lineTo(n,i),o.stroke();const l=Math.sign(n-t||i-e);if(o.fillStyle=tr,o.beginPath(),e===i?(o.moveTo(n,i),o.lineTo(n-6*l,i-4),o.lineTo(n-6*l,i+4)):(o.moveTo(n,i),o.lineTo(n-4,i-6*l),o.lineTo(n+4,i-6*l)),o.fill(),a>=0&&a<=1){const c=t+(n-t)*a,u=e+(i-e)*a,h=o.createRadialGradient(c,u,0,c,u,8);h.addColorStop(0,"rgba(213,155,255,1)"),h.addColorStop(1,"rgba(213,155,255,0)"),o.fillStyle=h,o.fillRect(c-8,u-8,16,16)}r&&(o.font=`9px ${Yt}`,o.fillStyle=Si,o.textAlign="center",o.fillText(r,(t+n)/2,e===i?e-6:(e+i)/2),o.textAlign="left")}draw(t){const e=this.ctx;this.clear(Zu);const n=this.env.feeds.chronicle.value,i=6,r=t%i/i,a=400+Math.floor(t/i);if(e.font=`14px ${Yt}`,e.fillStyle=pa,e.fillText("dream_gen",18,28),e.font=`10px ${Yt}`,e.fillStyle=Si,e.fillText("a truly infinite diffusion stream",110,28),e.textAlign="right",e.fillText(`kf ${String(a).padStart(5,"0")}`,It-18,28),e.textAlign="left",this.box(Zn,r<.25?1:0),this.box(He,r>=.25&&r<.5?1:.2),this.box(Jn,0),this.box(bi,r>=.5?1:0),this.box(er,Math.sin(t*.8)>.6?1:0),n.thumb)e.drawImage(n.thumb,He.x+6,He.y+18,He.w-12,(He.w-12)/2),e.globalAlpha=.35,e.drawImage(n.thumb,Zn.x+6,Zn.y+18,Zn.w-12,(Zn.w-12)/2),e.globalAlpha=1;else for(const h of[Zn,He]){const d=e.createLinearGradient(h.x,h.y,h.x+h.w,h.y+h.h);d.addColorStop(0,`hsl(${(t*8+h.x)%360} 40% 30%)`),d.addColorStop(1,`hsl(${(t*8+h.x+90)%360} 40% 18%)`),e.fillStyle=d,e.fillRect(h.x+6,h.y+18,h.w-12,(h.w-12)/2)}e.fillStyle="#1b1426",e.fillRect(Jn.x+6,Jn.y+18,Jn.w-12,(Jn.w-12)/2),e.fillStyle=Si,e.font=`9px ${Yt}`,e.fillText("txt2img on swap",Jn.x+10,Jn.y+50),this.arrow(Zn.x+Zn.w,91,He.x,91,"img2img",r<.25?r/.25:-1),this.arrow(Jn.x,91,He.x+He.w,91,"",-1),this.arrow(He.x+He.w/2,He.y+He.h,He.x+He.w/2,bi.y,"",r>=.25&&r<.5?(r-.25)/.25:-1);const o=16,l=r>=.5?Math.floor((r-.5)/.5*o):0;for(let h=0;h{const p=er.y+34+f*22;e.fillStyle=ma,e.font=`10px ${Yt}`,e.fillText(h,er.x+8,p),e.fillStyle=Si,e.fillText(d,er.x+78,p)}),e.fillStyle="rgba(20,12,30,0.8)",e.fillRect(0,Wt-110,It,110),e.fillStyle=tr,e.fillRect(18,Wt-110,It-36,1),e.font=`10px ${Yt}`,e.fillStyle=Si,e.fillText(n.prompt?`prompt${n.template?` · ${n.template}`:""}`:"prompt",18,Wt-92),e.font=`12px ${Yt}`,e.fillStyle=ma;const u=n.prompt||"the dreamer is asleep; its last prompt will appear here when the chronicle has one.";this.wrap(u,It-36,5).forEach((h,d)=>e.fillText(h,18,Wt-72+d*15))}}const nr=new Image;nr.src="/static/oikos/stage.jpg";const nn=372,sn=186,Yl=(It-nn)/2,Kl=118;class Qu extends bn{constructor(e,n){super(e,n);I(this,"frame",document.createElement("canvas"));I(this,"fctx");I(this,"prev",null);I(this,"cur",null);I(this,"swapAt",0);this.fps=20,this.frame.width=nn,this.frame.height=sn;const i=this.frame.getContext("2d");if(!i)throw new Error("oikos: no 2d context");this.fctx=i}draw(e){const n=this.ctx;this.clear("#120406"),nr.complete&&nr.naturalWidth&&(n.globalAlpha=.95,n.drawImage(nr,0,0,It,Wt),n.globalAlpha=1);const i=this.env.feeds.chronicle.value;i.thumb&&i.thumb!==this.cur&&(this.prev=this.cur,this.cur=i.thumb,this.swapAt=e);const r=this.fctx;if(r.globalCompositeOperation="source-over",r.globalAlpha=1,this.cur){const o=Math.min(1,(e-this.swapAt)/2.5);this.prev&&o<1&&this.kenBurns(this.prev,e-20),r.globalAlpha=this.prev?o:1,this.kenBurns(this.cur,e),r.globalAlpha=1}else this.standIn(e);const a=r.createRadialGradient(nn/2,sn/2,sn*.25,nn/2,sn/2,nn*.56);a.addColorStop(0,"rgba(0,0,0,1)"),a.addColorStop(.72,"rgba(0,0,0,0.9)"),a.addColorStop(1,"rgba(0,0,0,0)"),r.globalCompositeOperation="destination-in",r.fillStyle=a,r.fillRect(0,0,nn,sn),r.globalCompositeOperation="source-over",n.save(),n.globalCompositeOperation="lighter",n.globalAlpha=.18,n.drawImage(this.frame,Yl-30,Kl+sn-20,nn+60,90),n.restore(),n.drawImage(this.frame,Yl,Kl),this.status(e),n.fillStyle="rgba(8,4,4,0.72)",n.fillRect(0,Wt-34,It,34),n.fillStyle="#efe6d2",n.font=`12px ${Yt}`,this.spaced("NOW SHOWING · A DREAM · ALL NIGHT",It/2,Wt-13,3,"center")}kenBurns(e,n){const i=1.06+.05*Math.sin(n*.07),r=Math.sin(n*.05)*10,a=Math.cos(n*.043)*5,o=nn*i,l=sn*i;this.fctx.drawImage(e,(nn-o)/2+r,(sn-l)/2+a,o,l)}standIn(e){const n=this.fctx,i=n.createLinearGradient(0,0,0,sn);i.addColorStop(0,"#3a2440"),i.addColorStop(1,"#170d1c"),n.fillStyle=i,n.fillRect(0,0,nn,sn);const r=[12,330,280,20,350];for(let a=0;a<5;a++){const o=e*(.11+a*.03)+a*1.7,l=nn/2+Math.cos(o)*(60+a*14)*(a%2?1:-1),c=sn/2+Math.sin(o*1.3)*30,u=52+22*Math.sin(o*.7+a),h=n.createRadialGradient(l,c,0,l,c,u);h.addColorStop(0,`hsla(${r[a]} 90% 66% / 0.75)`),h.addColorStop(1,`hsla(${r[a]} 90% 50% / 0)`),n.fillStyle=h,n.fillRect(0,0,nn,sn)}}status(e){const n=this.ctx,i=this.env.feeds.dreams.value,r=this.env.feeds.chronicle.value;n.font=`12px ${Yt}`;let a,o;i.known&&i.awake?(o=Math.sin(e*4)>0?"#ff3b3b":"#6a1010",a=`LIVE frame ${zu(i.frame)}${i.viewers?` · ${i.viewers} watching`:""}`):i.known?(o="#5a5a5a",a=r.thumb?"asleep · last remembered":"asleep · wakes when watched"):(o="#3a3a3a",a=r.thumb?"last remembered":"no signal · a stand-in");const l=n.measureText(a).width+34;n.fillStyle="rgba(0,0,0,0.55)",n.fillRect(14,14,l,24),n.fillStyle=o,n.beginPath(),n.arc(27,26,4.5,0,Math.PI*2),n.fill(),n.fillStyle="#f2e9dc",n.fillText(a,38,30)}}const ju="#04060c",Zl="#9fc6ff",Es="#3d5378",Ts="#c9d1d9",td="#a5e3b5",ga="#ffc387",Jl=[["GET","/api/dreams/status","how it is (never wakes it)"],["WS ","/ws/dreams","the dream itself, h264"],["GET","/api/dreams/stream","MPEG-TS"],["SSE","/api/dreams/sse","events"],["GET","/api/dreams/embed","take it with you"]];class ed extends bn{constructor(e,n){super(e,n);I(this,"askedAt",-10);I(this,"lastRaw");this.fps=12}draw(e){const n=this.ctx;this.clear(ju);const i=this.env.feeds.dreams.value.raw;i!==this.lastRaw&&(this.lastRaw=i,this.askedAt=e);const r=e-this.askedAt;n.font=`13px ${Yt}`;const a=`curl -s ${location.host}/api/dreams/status`,o=a.slice(0,Math.floor(r*38));if(n.fillStyle=Es,n.fillText("$",16,28),n.fillStyle=Zl,n.fillText(o+(o.length=a.length){const c=i?nd(i,11):[[{text:"curl: (52) the dream did not answer",color:"#e58a8a"}]],u=Math.floor((r-a.length/38)*30);c.slice(0,u).forEach((h,d)=>{let f=16;for(const p of h)n.fillStyle=p.color,n.fillText(p.text,f,52+d*17),f+=n.measureText(p.text).width})}const l=Wt-128;n.fillStyle="rgba(159,198,255,0.06)",n.fillRect(10,l-20,It-20,138),n.strokeStyle="rgba(159,198,255,0.25)",n.strokeRect(10.5,l-19.5,It-21,137),n.font=`11px ${Yt}`,n.fillStyle=Es,n.fillText("ENDPOINTS",20,l-4),Jl.forEach(([c,u,h],d)=>{const f=l+16+d*19,p=Math.floor(e/2.5)%Jl.length===d;n.fillStyle=p?ga:Es,n.fillText(c,20,f),n.fillStyle=p?"#ffffff":Zl,n.fillText(u,58,f),n.fillStyle=Es,n.fillText(h,250,f)})}}function nd(s,t){const e=[],n=(i,r,a,o)=>{if(e.length>=t)return;const l=[{text:r,color:Ts}];a!==null&&l.push({text:`"${a}": `,color:Ts});const c=o?"":",";if(i&&typeof i=="object"&&!Array.isArray(i)){const d=Object.entries(i);e.push([...l,{text:"{",color:Ts}]),d.forEach(([f,p],v)=>n(p,`${r} `,f,v===d.length-1)),e.length=t&&(e[t-1]=[{text:" …",color:Es}]),e}const Ie={bg:"#181522",screen:"#231f36",fg:"#c9d1d9",dim:"#6e7681",sys:"#8b949e",quit:"#f85149",action:"#a371f7",join:"#3fb950"},Ql=["#58a6ff","#3fb950","#d29922","#a371f7","#f778ba","#39c5cf","#ff7b72","#7ee787","#ffa657","#79c0ff","#d2a8ff","#56d364"];function id(s){let t=0;for(const e of s)t=t*31+e.charCodeAt(0)>>>0;return Ql[t%Ql.length]??Ie.fg}function sd(s){const t=[{text:`[${s.stamp}] `,color:Ie.dim}],e=s.nick,n=s.content;switch(s.type){case"message":t.push({text:`<${e}> `,color:id(e)},{text:n,color:Ie.fg});break;case"action":t.push({text:`* ${e} ${n}`,color:Ie.action});break;case"quit":t.push({text:`⫫ ${e} has quit${n?` (${n})`:""}`,color:Ie.quit});break;case"part":t.push({text:`← ${e} has left${n?` (${n})`:""}`,color:Ie.sys});break;case"join":t.push({text:`→ ${e} has joined`,color:Ie.join});break;case"kick":{const i=s.reason||n;t.push({text:`⚠ ${s.target?`${s.target} was kicked by ${e}`:`${e} kicked someone`}${i?` (${i})`:""}`,color:Ie.quit})}break;default:t.push({text:`*** ${n||e}`,color:Ie.sys})}return t}const jl=16,Bi=16,_a=50;class rd extends bn{constructor(e,n){super(e,n);I(this,"laidOut",[]);I(this,"seen",-1);this.fps=15}layout(){const e=this.env.feeds.irc.value;e.version!==this.seen&&(this.seen=e.version,this.ctx.font=`12.5px ${Yt}`,this.laidOut=e.lines.map(n=>({rows:this.wrapChunks(sd(n),It-Bi*2),at:n.at})))}wrapChunks(e,n){var o;const i=this.ctx,r=[[]];let a=0;for(const l of e)for(const c of l.text.split(/(\s+)/)){if(!c)continue;const u=i.measureText(c).width;a+u>n&&a>0&&c.trim()&&(r.push([{text:" ",color:l.color}]),a=i.measureText(" ").width),(o=r[r.length-1])==null||o.push({text:c,color:l.color}),a+=u}return r}draw(e){this.layout();const n=this.ctx,i=this.env.feeds.irc.value;this.clear(Ie.bg),n.fillStyle=Ie.screen,n.fillRect(6,6,It-12,Wt-12),n.fillStyle="rgba(88,166,255,0.1)",n.fillRect(6,6,It-12,28),n.font=`13px ${Yt}`,n.fillStyle="#58a6ff",n.fillText("#aethera",Bi,25),n.fillStyle=Ie.dim,n.font=`11px ${Yt}`,n.textAlign="right";const r=i.connected?Math.sin(e*3)>-.3?"● live":"○ live":"○ connecting…";n.fillStyle=i.connected?Ie.join:Ie.dim,n.fillText(r,It-Bi,25),n.textAlign="left";const a=i.collapse?Math.min(1,(performance.now()-i.collapse.at)/1500):0;n.font=`12.5px ${Yt}`;const o=[],l=performance.now();for(const h of this.laidOut)for(const d of h.rows)o.push({chunks:d,fresh:Math.max(0,1-(l-h.at)/600)});const c=Math.floor((Wt-_a-12)/jl),u=o.slice(-c);u.length||(n.fillStyle=Ie.sys,n.fillText(i.connected?"*** the channel is quiet between fragments":"*** connecting to #aethera…",Bi,_a+12)),u.forEach((h,d)=>{let f=Bi;const p=_a+12+d*jl,v=a?Math.sin(p*.3+e*40)*8*a*(Math.random()<.3?1:0):0;for(const m of h.chunks)n.fillStyle=a>.2&&Math.random()0){this.hop.k+=e/.16,this.hop.k>=1&&(this.hop.k=0,this.hop.left--,this.pos[this.hop.seat]=((this.pos[this.hop.seat]??0)+1)%40,this.hop.left===0&&this.land(this.hop.seat));return}if(this.wait-=e,this.wait>0)return;const n=this.turn%Qn.length,i=1+Math.floor(this.rand()*6),r=1+Math.floor(this.rand()*6);this.dice=[i,r],this.say(`${(a=Qn[n])==null?void 0:a.name} rolls ${i} and ${r}.`),this.hop={seat:n,from:this.pos[n]??0,left:i+r,k:0},this.turn++,this.wait=1.6}land(e){var c,u;const n=this.pos[e]??0,[i,r,a]=tc[n]??["",null,0],o=((c=Qn[e])==null?void 0:c.name)??"",l=this.owner.get(n);if(a&&l===void 0&&this.rand()<.75)this.owner.set(n,e),this.say(`${o} buys ${i} for ₯${a}.`);else if(l!==void 0&&l!==e){const h=Math.max(2,Math.round(a/(r?12:8)));this.say(`${o} pays ₯${h} to ${(u=Qn[l])==null?void 0:u.name} for ${i}.`)}else i==="Moira"||i==="Grammateion"?this.say(`${o} draws from ${i}.`):i==="Kerux"&&(this.say(`${o} is sent to the Desmoterion.`),this.pos[e]=10);this.owner.size>22&&this.owner.clear()}draw(e,n){var c;this.step(Math.min(n,.2));const i=this.ctx,r=i.createRadialGradient(It/2,va,40,It/2,va,It*.7);r.addColorStop(0,"#2a241f"),r.addColorStop(1,"#141210"),i.fillStyle=r,i.fillRect(0,0,It,Wt);const a=[hn(-.4,-.4),hn(11.4,-.4),hn(11.4,11.4),hn(-.4,11.4)];i.fillStyle="rgba(0,0,0,0.45)",i.beginPath(),a.forEach(([u,h],d)=>d?i.lineTo(u,h+8):i.moveTo(u,h+8)),i.fill(),i.fillStyle="#d9ccb2",i.beginPath(),a.forEach(([u,h],d)=>d?i.lineTo(u,h):i.moveTo(u,h)),i.fill();for(let u=0;u<40;u++){const[h,d]=xa(u),[,f]=tc[u]??["",null,0],p=[hn(h,d),hn(h+1,d),hn(h+1,d+1),hn(h,d+1)];i.fillStyle=f?ad[f]??"#efe4cf":u%10===0?"#e6d6b6":"#efe4cf",i.strokeStyle="#7a6a52",i.lineWidth=.7,i.beginPath(),p.forEach(([m,g],w)=>w?i.lineTo(m,g):i.moveTo(m,g)),i.closePath(),i.fill(),i.stroke();const v=this.owner.get(u);if(v!==void 0){const[m,g]=hn(h+.5,d+.5);i.fillStyle=((c=Qn[v])==null?void 0:c.color)??"#fff",i.fillRect(m-2,g-5,4,5)}}const[o,l]=hn(5.5,5.5);i.fillStyle="#7a6a52",i.font=`15px ${Yt}`,this.spaced("KLEROS",o,l+5,5,"center"),Qn.forEach((u,h)=>{let d=this.pos[h]??0,f=0,[p,v]=xa(d);if(this.hop.left>0&&this.hop.seat===h){const[E,M]=xa((d+1)%40);p+=(E-p)*this.hop.k,v+=(M-v)*this.hop.k,f=Math.sin(this.hop.k*Math.PI)*9,d=-1}const m=[[.3,.3],[.7,.3],[.3,.7],[.7,.7]][h]??[.5,.5],[g,w]=hn(p+(m[0]??.5),v+(m[1]??.5));i.fillStyle="rgba(0,0,0,0.35)",i.beginPath(),i.ellipse(g,w+1,5,2.5,0,0,Math.PI*2),i.fill(),i.fillStyle=u.color,i.beginPath(),i.arc(g,w-5-f,4.6,0,Math.PI*2),i.fill(),i.strokeStyle="rgba(0,0,0,0.5)",i.stroke()}),this.dice.forEach((u,h)=>this.die(It-70+h*30,22,u,e)),i.font=`11px ${Yt}`,Qn.forEach((u,h)=>{const d=26+h*16;i.fillStyle=u.color,i.fillRect(16,d-8,8,8),i.fillStyle=(this.turn-1)%Qn.length===h?"#f5ecd9":"#8c826f",i.fillText(`${u.name}${u.model?" ◆ model":""}`,30,d)}),i.fillStyle="rgba(10,8,6,0.75)",i.fillRect(0,Wt-62,It,62),i.font=`12px ${Yt}`,this.log.forEach((u,h)=>{i.fillStyle=h===this.log.length-1?"#f1e6cc":"#8a7f6a",i.fillText(u,16,Wt-42+h*16)})}die(e,n,i,r){const a=this.ctx,o=this.hop.left>0?Math.sin(r*30)*.15:0;a.save(),a.translate(e,n),a.rotate(o),a.fillStyle="#efe4cf",a.fillRect(-10,-10,20,20),a.fillStyle="#3a2f24";const l={1:[[0,0]],2:[[-5,-5],[5,5]],3:[[-5,-5],[0,0],[5,5]],4:[[-5,-5],[5,-5],[-5,5],[5,5]],5:[[-5,-5],[5,-5],[0,0],[-5,5],[5,5]],6:[[-5,-5],[5,-5],[-5,0],[5,0],[-5,5],[5,5]]};for(const[c,u]of l[i]??[])a.beginPath(),a.arc(c,u,1.8,0,Math.PI*2),a.fill();a.restore()}}class cd extends bn{constructor(e,n){super(e,n);I(this,"body");I(this,"creatureRef");I(this,"stars");I(this,"breaths",[]);this.fps=24;const i=vi(33);this.stars=Array.from({length:90},()=>({x:i()*It,y:i()*Wt,a:i()*.5+.1})),this.body=ic()}sync(){const e=this.env.feeds.creature.value;e!==this.creatureRef&&(this.creatureRef=e,this.body=e?hd(e):ic(),this.breaths=Array.from({length:Math.min(4,this.body.edges.length)},(n,i)=>({edge:i*7%Math.max(1,this.body.edges.length),k:0,fwd:!0})))}draw(e,n){this.sync();const i=this.ctx,r=this.body,a=i.createLinearGradient(0,0,0,Wt);a.addColorStop(0,"#070b14"),a.addColorStop(1,"#02030a"),i.fillStyle=a,i.fillRect(0,0,It,Wt);for(const p of this.stars)i.fillStyle=`rgba(200,215,255,${p.a*(.7+.3*Math.sin(e+p.x))})`,i.fillRect(p.x,p.y,1,1);const o=r.born?1.4:.9,l=Math.min(n,.1),c=r.nodes.map((p,v)=>{var w;const m=r.adj[v]??[];let g=0;for(const E of m)g+=Math.sin((((w=r.nodes[E])==null?void 0:w.phase)??0)-p.phase);return p.phase+(p.omega+(m.length?o*g/m.length:0))*l});r.nodes.forEach((p,v)=>{const m=c[v]??p.phase;Math.floor(m/(Math.PI*2))>Math.floor(p.phase/(Math.PI*2))&&(p.flash=1),p.phase=m,p.flash=Math.max(0,p.flash-l*2.2)});const u=e*.18,h=Math.cos(u),d=Math.sin(u),f=r.nodes.map((p,v)=>{const m=Math.sin(e*.9+v)*.02,g=p.x*h-p.z*d,E=2.8/(3.6-(p.x*d+p.z*h));return{x:It/2+g*E*150,y:176+(p.y+m)*E*150,k:E}});i.lineCap="round";for(const p of r.edges){const v=f[p.a],m=f[p.b];!v||!m||(i.strokeStyle=p.elder?"rgba(160,200,235,0.55)":"rgba(140,170,210,0.28)",i.lineWidth=p.elder?1.4:.8,i.beginPath(),i.moveTo(v.x,v.y),i.lineTo(m.x,m.y),i.stroke())}for(const p of this.breaths){const v=r.edges[p.edge];if(!v)continue;if(p.k+=l/.75,p.k>=1){const S=p.fwd?v.b:v.a,b=(r.adj[S]??[]).map(T=>r.edges.findIndex(P=>P.a===S&&P.b===T||P.b===S&&P.a===T)),R=b[Math.floor(Math.random()*b.length)]??p.edge,x=r.edges[R];p.edge=R,p.fwd=x?x.a===S:!0,p.k=0;continue}const m=f[p.fwd?v.a:v.b],g=f[p.fwd?v.b:v.a];if(!m||!g)continue;const w=m.x+(g.x-m.x)*p.k,E=m.y+(g.y-m.y)*p.k,M=i.createRadialGradient(w,E,0,w,E,9);M.addColorStop(0,"rgba(235,245,255,0.95)"),M.addColorStop(1,"rgba(180,220,255,0)"),i.fillStyle=M,i.fillRect(w-9,E-9,18,18)}r.nodes.forEach((p,v)=>{const m=f[v];if(!m)return;const g=p.flash,w=2+m.k*1.2+g*3,E=g>.05?`rgba(255,${Math.round(210-g*40)},${Math.round(120-g*60)},${.6+g*.4})`:"rgba(200,225,255,0.85)";i.strokeStyle=E,i.lineWidth=.8,i.beginPath(),i.moveTo(m.x-w*3,m.y),i.lineTo(m.x+w*3,m.y),i.moveTo(m.x,m.y-w*3),i.lineTo(m.x,m.y+w*3),i.stroke();const M=i.createRadialGradient(m.x,m.y,0,m.x,m.y,w*2.4);M.addColorStop(0,E),M.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=M,i.fillRect(m.x-w*3,m.y-w*3,w*6,w*6)}),i.font=`15px ${Yt}`,i.fillStyle=r.born?"#e6f1ff":"#8190a8",i.fillText(r.name,18,Wt-40),i.font=`11px ${Yt}`,i.fillStyle="#6f7f99",i.fillText(r.age,18,Wt-20),i.textAlign="right",i.fillStyle="#4b5870",i.fillText(r.born?`${r.nodes.length} nodes · ${r.edges.length} strings`:"click to wake it",It-18,Wt-20),i.textAlign="left"}}function nc(s,t){var n,i;const e=Array.from({length:s},()=>[]);for(const r of t)(n=e[r.a])==null||n.push(r.b),(i=e[r.b])==null||i.push(r.a);return e}function hd(s){const t=new Map(s.nodes.map((c,u)=>[c.id,u])),e=s.nodes.reduce((c,u)=>c+u.x,0)/s.nodes.length,n=s.nodes.reduce((c,u)=>c+u.y,0)/s.nodes.length,i=s.nodes.reduce((c,u)=>c+u.z,0)/s.nodes.length,r=Math.max(1,...s.nodes.map(c=>Math.hypot(c.x-e,c.y-n,c.z-i))),a=s.nodes.map((c,u)=>({x:(c.x-e)/r,y:(c.y-n)/r,z:(c.z-i)/r,phase:u*1.3,omega:2.4+u%5*.21,flash:0})),o=s.edges.map(c=>({a:t.get(c.a)??-1,b:t.get(c.b)??-1,elder:c.age>150})).filter(c=>c.a>=0&&c.b>=0),l=Math.max(s.lifetime,(Date.now()-s.bornAt)/1e3);return{name:s.name,age:`${ud(l)} old · yours`,born:!0,nodes:a,edges:o,adj:nc(a.length,o)}}function ic(){const s=vi(9),t=9,e=Array.from({length:t},(i,r)=>{const a=r/t*Math.PI*2;return{x:Math.cos(a)*.8+(s()-.5)*.3,y:(s()-.5)*.9,z:Math.sin(a)*.8,phase:s()*6,omega:2.2+s()*.8,flash:0}}),n=[];for(let i=0;i0?`${e}d ${n}h`:n>0?`${n}h ${i}m`:`${i}m`}const zi=new Image;zi.src="/static/uploads/aethera_trimmed.png";class dd extends bn{constructor(e,n){super(e,n);I(this,"specks");this.fps=12;const i=vi(7);this.specks=Array.from({length:70},()=>({x:i()*It,y:i()*Wt,r:i()*1.2+.3,red:i()<.35,v:i()*4+1}))}draw(e){const n=this.ctx;this.clear("#000");for(const c of this.specks){const u=(c.y-e*c.v+Wt)%Wt;n.fillStyle=c.red?"rgba(170,40,50,0.55)":"rgba(255,255,255,0.28)",n.fillRect(c.x,u,c.r,c.r)}if(zi.complete&&zi.naturalWidth){const u=210*zi.naturalHeight/zi.naturalWidth;n.globalAlpha=.92+.08*Math.sin(e*1.3),n.drawImage(zi,(It-210)/2,26,210,u),n.globalAlpha=1}n.fillStyle="#7d7d7d",n.font=`13px ${Yt}`,this.spaced("transmissions",It/2,118,3,"center"),n.fillStyle="#262626",n.fillRect(40,132,It-80,1);const i=this.env.dir.posts.slice(0,6);if(!i.length){n.fillStyle="#9a9a9a",n.font=`16px ${Yt}`,n.textAlign="center",n.fillText(`no transmissions yet${Math.floor(e*2)%2?"_":" "}`,It/2,230),n.textAlign="left";return}const r=3.6,a=Math.floor(e/r)%i.length,o=e%r/r;i.forEach((c,u)=>{const h=162+u*29,d=u===a;n.font=`11px ${Yt}`,n.fillStyle=d?"#bdbdbd":"#555",n.fillText(c.date,46,h),n.font=`16px ${oa}`,n.fillStyle=d?"#ffffff":"#9a9a9a";const f=c.title.length>38?`${c.title.slice(0,37)}…`:c.title;n.fillText(f,132,h),d&&this.star(28,h-5,e)});const l=i[a];if(l!=null&&l.excerpt){n.font=`italic 12px ${Yt}`,n.fillStyle="#8a8a8a";const c=Math.floor(Math.min(1,o*1.6)*l.excerpt.length);this.wrap(l.excerpt.slice(0,c),It-92,2).forEach((h,d)=>n.fillText(h,46,344+d*16))}}star(e,n,i){const r=this.ctx,a=7+Math.sin(i*5)*1.2;r.save(),r.translate(e,n),r.rotate(i*.8),r.fillStyle="#fff",r.shadowColor="#fff",r.shadowBlur=10,r.beginPath();for(let o=0;o<16;o++){const l=o/16*Math.PI*2,c=o%2?a*.28:o%4?a*.7:a;r.lineTo(Math.cos(l)*c,Math.sin(l)*c)}r.closePath(),r.fill(),r.restore()}}const fd={transmissions:dd,dreams:Qu,chronicle:Yu,"dreams-api":ed,apeiron:Xu,syrinx:cd,irc:rd,parlor:ld,dream_gen:Ju};class pd extends bn{draw(t){const e=this.ctx,n=["#c0c0c0","#c0c000","#00c0c0","#00c000","#c000c0","#c00000","#0000c0"];n.forEach((i,r)=>{e.fillStyle=i,e.fillRect(r*It/n.length,0,It/n.length+1,Wt*.66)}),e.fillStyle="#111",e.fillRect(0,Wt*.66,It,Wt*.34),e.fillStyle="#fff",e.font=`22px ${Yt}`,e.textAlign="center",e.fillText(this.site.title,It/2,Wt*.82),e.font=`12px ${Yt}`,e.fillStyle=Math.floor(t)%2?"#888":"#555",e.fillText("no programme yet",It/2,Wt*.92),e.textAlign="left"}}function md(s,t){const e=fd[s.id]??pd;return new e(s,t)}const ir={dreams:{angle:0,r:4.9,y:.5,style:"tv",screenW:2,channel:1},transmissions:{angle:-27,r:4.5,y:.34,style:"beige",screenW:1.22,stand:!0,channel:2},chronicle:{angle:27,r:4.5,y:0,style:"black",screenW:1.3,channel:3},"dreams-api":{angle:27,r:4.5,y:0,style:"grey",screenW:.86,on:"chronicle",channel:4},apeiron:{angle:-54,r:4.2,y:0,style:"black",screenW:1.15,channel:5},irc:{angle:-54,r:4.2,y:0,style:"beige",screenW:.95,on:"apeiron",channel:6},syrinx:{angle:54,r:4.2,y:.28,style:"grey",screenW:1.08,channel:7},dream_gen:{angle:-20,r:7,y:4.35,style:"grey",screenW:1.02,hang:!0,feeds:"dreams",channel:8},parlor:{angle:20,r:6.5,y:3.6,style:"beige",screenW:1.1,hang:!0,channel:9}};function gd(s,t){const e=ir[s];if(e)return e;const n=Math.floor(t/2)+1;return{angle:(t%2?1:-1)*(68+n*10),r:5.4,y:0,style:"grey",screenW:1,channel:12+t}}/** +var zy=Object.defineProperty;var Hy=(ti,En,zn)=>En in ti?zy(ti,En,{enumerable:!0,configurable:!0,writable:!0,value:zn}):ti[En]=zn;var R=(ti,En,zn)=>Hy(ti,typeof En!="symbol"?En+"":En,zn);(function(){"use strict";var yd;const ti=[17,19,21,57,93,165,163,204,209,214,220,231],En=[236,234],zn=En.length,Ti=6,Pd=[17,18,19,20,21,57,56,93,129,165,164,163,198,204,203,209,208,214,220,229,231],gr=s=>Pd[s]??0,Ld=Array.from({length:Ti},(s,t)=>[gr(Math.floor((185+t)/21)),gr((185+t)%21)]),Dd=Array.from({length:Ti*Ti},(s,t)=>[gr(Math.floor((191+t)/21)),gr((191+t)%21)]),Id=.98,bc=4,Nd=Math.fround(Id**bc),Bs="▁▂▃▄▅▆▇█",ei=-2,qi=2,Ud={[-2]:"0.25x",[-1]:"0.50x",0:" 1.0x",1:" 2.0x",2:" 4.0x"},Yi={glider:[[0,1],[1,2],[2,0],[2,1],[2,2]],lwss:[[0,1],[0,4],[1,0],[2,0],[2,4],[3,0],[3,1],[3,2],[3,3]],hwss:[[0,3],[0,4],[1,1],[1,6],[2,0],[3,0],[3,6],[4,0],[4,1],[4,2],[4,3],[4,4],[4,5]],r_pentomino:[[0,1],[0,2],[1,0],[1,1],[2,1]],acorn:[[0,1],[1,3],[2,0],[2,1],[2,4],[2,5],[2,6]],diehard:[[0,6],[1,0],[1,1],[2,1],[2,5],[2,6],[2,7]],pi_heptomino:[[0,0],[0,1],[0,2],[1,1],[2,1],[3,0],[3,2]],b_heptomino:[[0,1],[1,0],[1,2],[1,3],[2,0],[2,1],[3,1]],gosper_gun:[[0,24],[1,22],[1,24],[2,12],[2,13],[2,20],[2,21],[2,34],[2,35],[3,11],[3,15],[3,20],[3,21],[3,34],[3,35],[4,0],[4,1],[4,10],[4,16],[4,20],[4,21],[5,0],[5,1],[5,10],[5,14],[5,16],[5,17],[5,22],[5,24],[6,10],[6,16],[6,24],[7,11],[7,15],[8,12],[8,13]],pulsar:[[0,2],[0,3],[0,4],[0,8],[0,9],[0,10],[2,0],[2,5],[2,7],[2,12],[3,0],[3,5],[3,7],[3,12],[4,0],[4,5],[4,7],[4,12],[5,2],[5,3],[5,4],[5,8],[5,9],[5,10],[7,2],[7,3],[7,4],[7,8],[7,9],[7,10],[8,0],[8,5],[8,7],[8,12],[9,0],[9,5],[9,7],[9,12],[10,0],[10,5],[10,7],[10,12],[12,2],[12,3],[12,4],[12,8],[12,9],[12,10]],pentadecathlon:[[0,1],[1,1],[2,0],[2,2],[3,1],[4,1],[5,1],[6,1],[7,0],[7,2],[8,1],[9,1]]},Ro=["glider","lwss","hwss"],Sc=["r_pentomino","acorn","diehard","pi_heptomino","b_heptomino"],wc=["pulsar","pentadecathlon"],Fd=[[1,1],[1,-1],[-1,-1],[-1,1]];function Po(s){const t=new Map;for(const[e,n]of s)for(const[i,r]of[[e,n],[e,-n],[-e,n],[-e,-n]])t.set(`${i},${r}`,[i,r]);return[...t.values()]}const Od=[Po([[0,1],[0,2],[0,3],[1,0],[1,4],[2,0],[2,5],[3,0],[3,5],[4,1],[4,5],[5,2],[5,3],[5,4]]),Po([[0,1],[0,2],[0,3],[0,4],[1,0],[2,0],[3,0],[4,0],[2,2],[3,3]]),Po([[0,3],[0,4],[1,2],[1,5],[2,1],[2,6],[3,0],[3,7],[4,0],[4,7],[5,1],[5,6],[6,2],[6,5],[7,3],[7,4]])],Lo="afterlife-universe-v1";function kd(s,t){const e=atob(s),n=new Int32Array(t);let i=0;const r=()=>{let a=0,l=1;for(;;){if(i>=e.length)throw new Error("afterlife: truncated save");const c=e.charCodeAt(i++);if(a+=(c&127)*l,c<128)return a;l*=128}};let o=-1;for(;i=t)throw new Error("afterlife: save does not fit its own world");n[o]=a%2?-(a+1)/2:a/2}return n}function Bd(s){var t;try{const e=(t=s??globalThis.localStorage)==null?void 0:t.getItem(Lo);if(!e)return null;const n=JSON.parse(e);if(n.v!==1||!Number.isInteger(n.h)||!Number.isInteger(n.w)||typeof n.cells!="string")return null;const i=n.h,r=n.w;if(i<=0||r<=0||i*r>16e6)return null;const o=Number(n.generation);return!Number.isSafeInteger(o)||o<0?null:{h:i,w:r,age:kd(n.cells,i*r),generation:o,totalInjections:Number(n.totalInjections)||0,bornAt:Number(n.bornAt)||Date.now(),savedAt:Number(n.savedAt)||Date.now()}}catch(e){return console.warn("afterlife: could not load the universe, starting fresh",e),null}}function zd(){const s=document.getElementById("oikos-data");try{const t=JSON.parse((s==null?void 0:s.textContent)??"{}");return{sites:t.sites??[],files:t.files??[],posts:t.posts??[]}}catch{return{sites:[],files:[],posts:[]}}}const Ec=s=>/^https?:\/\//.test(s);class Ki{constructor(t){R(this,"listeners",new Set);this.value=t}set(t){this.value=t;for(const e of this.listeners)e(t)}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}}function Tc(){try{const s=localStorage.getItem("syrinx-creature-v1");if(!s)return null;const t=JSON.parse(s);if(typeof t.name!="string"||typeof t.bornAt!="number"||!t.state)return null;const e=Array.isArray(t.state.nodes)?t.state.nodes:[],n=Array.isArray(t.state.edges)?t.state.edges:[];return e.length?{name:t.name,bornAt:t.bornAt,lifetime:typeof t.state.lifetime=="number"?t.state.lifetime:0,nodes:e.map(i=>({id:i.id,x:i.x,y:i.y,z:i.z??500,age:i.age??0})),edges:n.map(i=>({a:i.a,b:i.b,age:i.age??0}))}:null}catch{return null}}function Ac(s){const t=s.indexOf('"cells"');return t<0?s:s.slice(0,t)}function Hd(s){if(!s)return null;const t=Ac(s),e=o=>{const a=new RegExp(`"${o}":(\\d+)`).exec(t);return a?Number(a[1]):void 0};let n=e("generation"),i=e("savedAt"),r=e("bornAt");if(n===void 0||i===void 0){try{const o=JSON.parse(s);n=typeof o.generation=="number"?o.generation:void 0,i=typeof o.savedAt=="number"?o.savedAt:void 0,r=typeof o.bornAt=="number"?o.bornAt:void 0}catch{return null}if(n===void 0||i===void 0)return null}return{generation:n,bornAt:r??i,savedAt:i}}let zs=null;function Do(){let s;try{s=localStorage.getItem(Lo)}catch{return null}if(!s)return null;const t=Ac(s);if(zs&&zs.len===s.length&&zs.head===t)return zs.info;const e=Hd(s);return zs={len:s.length,head:t,info:e},e}function Cc(s){return new Promise((t,e)=>{const n=new Image;n.decoding="async",n.onload=()=>t(n),n.onerror=e,n.src=s})}async function vr(s){try{const t=await fetch(s,{headers:{Accept:"application/json"}});return t.ok?await t.json():null}catch{return null}}class Gd{constructor(){R(this,"dreams",new Ki({known:!1,awake:!1,frame:0,fps:0,viewers:0,raw:null}));R(this,"chronicle",new Ki({known:!1,thumb:null,thumbAt:0,prompt:"",template:"",eras:[],eraCount:0,strata:[]}));R(this,"irc",new Ki({connected:!1,lines:[],collapse:null,fragments:0,version:0}));R(this,"creature",new Ki(Tc()));R(this,"universe",new Ki(Do()));R(this,"apeiron",new Ki(null));R(this,"timers",[]);R(this,"ws",null);R(this,"wsBackoff",2e3);R(this,"wsTimer",0);R(this,"running",!1);R(this,"onStorage",t=>{(t.key===Lo||t.key===null)&&this.setUniverse(Do())})}start(){if(this.running)return;this.running=!0;const t=(e,n)=>{e(),this.timers.push(window.setInterval(e,n))};t(()=>void this.pollDreams(),2e4),t(()=>void this.pollChronicle(),12e4),t(()=>this.creature.set(Tc()),15e3),window.addEventListener("storage",this.onStorage),t(()=>this.setUniverse(Do()),6e4),this.openIrc(),this.apeiron.value||this.loadApeiron()}setUniverse(t){var e;(t==null?void 0:t.savedAt)!==((e=this.universe.value)==null?void 0:e.savedAt)&&this.universe.set(t)}stop(){this.running=!1;for(const t of this.timers)clearInterval(t);this.timers=[],window.removeEventListener("storage",this.onStorage),clearTimeout(this.wsTimer),this.ws&&(this.ws.onclose=null,this.ws.close(),this.ws=null),this.irc.set({...this.irc.value,connected:!1,version:this.irc.value.version+1})}async pollDreams(){var e,n,i,r,o;const t=await vr("/api/dreams/status");if(!t){this.dreams.set({...this.dreams.value,known:!1});return}this.dreams.set({known:!0,awake:!!((e=t.gpu)!=null&&e.active),frame:((n=t.generation)==null?void 0:n.current_frame)??((i=t.generation)==null?void 0:i.frame_count)??0,fps:((r=t.generation)==null?void 0:r.fps)??0,viewers:((o=t.viewers)==null?void 0:o.websocket_count)??0,raw:t})}async pollChronicle(){var o,a,l,c;const t=await vr("/api/dreams/chronicle/timeline?hours=3");if(!t)return;const e=this.chronicle.value;let n=e.thumb;(o=t.live)!=null&&o.thumb&&t.live.thumb!==(n==null?void 0:n.dataset.src)&&(n=await Cc(t.live.thumb).catch(()=>e.thumb),n&&(n.dataset.src=t.live.thumb));const i=(t.tiles??[]).slice(-3),r=(await Promise.all(i.map(u=>Cc(u.url).catch(()=>null)))).filter(u=>!!u);this.chronicle.set({known:!0,thumb:n,thumbAt:((a=t.live)==null?void 0:a.t)??0,prompt:((l=t.live)==null?void 0:l.prompt)??"",template:((c=t.live)==null?void 0:c.template)??"",eras:(t.eras??[]).map(u=>({title:u.title??"",t0:u.t0,t1:u.t1,open:!!u.open,kf:u.kf??0})),eraCount:t.era_count??0,strata:r.length?r:e.strata})}openIrc(){const t=location.protocol==="https:"?"wss":"ws";let e;try{e=new WebSocket(`${t}://${location.host}/ws/irc`)}catch{return}this.ws=e;const n=i=>{const r=this.irc.value;this.irc.set({...r,...i,version:r.version+1})};e.onopen=()=>{this.wsBackoff=2e3,n({connected:!0,collapse:null})},e.onmessage=i=>{let r;try{r=JSON.parse(String(i.data))}catch{return}if(r.type==="message"&&r.data){const o=r.data,a=o.meta??{},l={nick:String(o.nick??""),content:String(o.content??""),type:String(o.type??"message"),stamp:String(o.timestamp??""),at:performance.now()};if(typeof o.at=="number"&&(l.wallAt=o.at),typeof a.target=="string"&&(l.target=a.target),typeof a.reason=="string"&&(l.reason=a.reason),r.replay&&this.irc.value.lines.some(c=>c.stamp===l.stamp&&c.nick===l.nick&&c.content===l.content))return;n({lines:[...this.irc.value.lines,l].slice(-60)})}else r.type==="collapse_start"?n({collapse:{type:r.collapseType??"collapse",at:performance.now()}}):r.type==="fragment_end"&&(r.replay?n({collapse:null}):n({collapse:null,lines:this.irc.value.lines.slice(-6),fragments:this.irc.value.fragments+1}))},e.onclose=()=>{n({connected:!1}),this.running&&(this.wsTimer=window.setTimeout(()=>this.openIrc(),this.wsBackoff),this.wsBackoff=Math.min(this.wsBackoff*2,6e4))}}async loadApeiron(){const[t,e]=await Promise.all([vr("/static/apeiron/data/templates.json"),vr("/static/apeiron/data/components.json")]);t&&e&&this.apeiron.set({templates:t,components:e})}}const Vd={block:[[[0,0],[0,1],[1,0],[1,1]],1],beehive:[[[0,1],[0,2],[1,0],[1,3],[2,1],[2,2]],1],loaf:[[[0,1],[0,2],[1,0],[1,3],[2,1],[2,3],[3,2]],1],boat:[[[0,0],[0,1],[1,0],[1,2],[2,1]],1],tub:[[[0,1],[1,0],[1,2],[2,1]],1],ship:[[[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]],1],pond:[[[0,1],[0,2],[1,0],[1,3],[2,0],[2,3],[3,1],[3,2]],1],blinker:[[[0,0],[0,1],[0,2]],2],toad:[[[0,1],[0,2],[0,3],[1,0],[1,1],[1,2]],2],beacon:[[[0,0],[0,1],[1,0],[2,3],[3,2],[3,3]],2],glider:[Yi.glider??[],4],lwss:[Yi.lwss??[],4],hwss:[Yi.hwss??[],4],pulsar:[Yi.pulsar??[],3],pentadecathlon:[Yi.pentadecathlon??[],15]},Wd=60,Rc=20;function Pc(s){let t=null;for(let e=0;e<8;e++){const n=e>=4,i=n?s.w:s.h,r=n?s.h:s.w;let o=`${i}x${r}:`;for(let a=0;au))+1,r=Math.max(...e.map(([,u])=>u))+1,o=Math.max(i,r)+2*(n+4);let a=new Uint8Array(o*o);const l=Math.floor((o-i)/2),c=Math.floor((o-r)/2);for(const[u,h]of e)a[(l+u)*o+c+h]=1;for(let u=0;ui&&(i=u),ho&&(o=h));if(i<0)return null;const a=i-n+1,l=o-r+1,c=new Uint8Array(a*l);for(let u=0;u=t)continue;const p=2-Math.abs(d),v=Math.max(0,h-p),m=Math.min(e-1,h+p);i.fill(1,f*e+v,f*e+m+1)}const r=new Int32Array(t*e),o=[],a=[];for(let u=0;u400)return null;const d={y0:t,y1:-1,x0:e,x1:-1};for(r[u]=h,a.push(u);a.length;){const f=a.pop()??0,p=Math.floor(f/e),v=f-p*e;pd.y1&&(d.y1=p),vd.x1&&(d.x1=v);for(let m=-1;m<=1;m++){const g=p+m;if(!(g<0||g>=t))for(let S=-1;S<=1;S++){const E=v+S;if(E<0||E>=e)continue;const y=g*e+E;i[y]&&!r[y]&&(r[y]=h,a.push(y))}}}o.push(d)}if(o.length===0)return null;const l=new Map,c=[];return o.forEach((u,h)=>{const d=h+1,f=u.y1-u.y0+1,p=u.x1-u.x0+1;if(f>Rc+4||p>Rc+4)return;const v=new Uint8Array(f*p);let m=0,g=0,S=0;for(let M=0;MWd)return;const E=Lc(v,f,p),y=E?n.get(Pc(E)):void 0;y!==void 0&&(l.set(y,(l.get(y)??0)+1),c.length<48&&c.push([y,u.y0+Math.floor(g/m),u.x0+Math.floor(S/m)]))}),{counts:l,sites:c}}const No=["cells pondering existence","entropy is just a suggestion","order from chaos, chaos from order","the cosmos breathes","finding signal in noise","patterns all the way down","life, uh, finds a way","on the edge of chaos","complexity, emerging","dancing at the phase boundary","stillness is just slow motion","every cell a universe","the substrate computes","from nothing, everything","infinite in all directions","what the dead cells dream","the glider knows the way","automata, contemplating","structure wants to happen","a cosmos in a terminal","the math dreams itself","somewhere, a glider remembers","what is a pattern but frozen time","the void is patient","meaning is optional; beauty is not","computation as meditation","the rules are simple; the consequences infinite","watching the watchers watch","no cell is an island","the ghost of configurations past","asymmetry seeks symmetry seeks asymmetry","this too shall iterate","the universe has no pause button","except when it does","what would Conway think","oscillators keep time for no one","the glider gun knows not what it creates","boundaries are suggestions","death is just a state transition","somewhere in here, a proof of universality","the grid forgets nothing","a thought experiment that thinks back","topology has opinions","the neighborhood watches","not alive, not dead — computing","four rules, one universe","each frame a theorem","proof by existence","the only constant is the ruleset","somewhere between on and off, meaning","the dead outnumber the living, as always","conway's little infinities","the simulation doesn't know it's beautiful","27 neighbors ago, this was empty","elegance is compression","the grid is the message","a love letter to discrete math","listen — the cells are whispering","we are all gliders, briefly"],qd={booming:["runaway growth","the bloom unfolds","life begets life begets life","exponential daydreams","more is different","the bloom cannot be stopped","abundance as instability","growing into the unknown","every birth a cascade","the simulation smiles","lebensraum","mitosis dreams","the substrate strains","a spring that won't stop springing","cells all the way to the horizon","the algorithm is generous today","more, more, more","genesis on fast-forward"],declining:["entropy collects its due","the long exhale","even stars go dark","returning to the void","graceful unwinding","the great filter, in miniature","less is not nothing","the long forgetting","what rises must","simplicity returns","the cells remember fullness","autumn in the grid","graceful subtraction","the tide goes out","a slow dissolve","the universe exhales","dimming, not vanishing","the population curve bends earthward"],cycle:["deja vu, deja vu, deja vu","stuck in a loop","the eternal return","ouroboros","time is a flat circle","we have been here before","the loop remembers itself","stability through repetition","a prayer, repeating","the wheel turns","same as it ever was","history rhymes","the attractor holds","a fixed point in phase space","nietzsche was right about this part","the universe stutters beautifully","clockwork, but organic","breathing in, breathing out"],stagnant:["the hush before the storm","equilibrium is boring","waiting for a perturbation","still waters","the universe holds its breath","the calm before","potential energy","stillness is also motion","the system waits","one perturbation away","dormant, not dead","crystallized","the peace of equilibrium","a held breath","the grid meditates","tension, frozen","the quiet hum of nothing happening","patience is also a computation"],sparse:["the last few embers","clinging to existence","from little things, big things grow","seeds in the dark","quiet, but not empty","lonely structures","the survivors","space between stars","minimalism, enforced","room to breathe","the few, the proud","embers","a whisper, not a shout","the geometry of loneliness","each cell precious now","a constellation, if you squint","small, but not nothing"],dense:["teeming","a city that never sleeps","standing room only","too many neighbours","the crowd murmurs","no room to think","the crush of neighbors","overpopulation is self-correcting","too much life","suffocating in company","the grid groans","rush hour","sardines","every cell has an opinion","a metropolis, thrumming","the carrying capacity protests","elbow room is a luxury"],injection:["a gift from beyond the edge","new visitors","reinforcements arrive","the cosmos provides","seeded by unseen hands","deus ex machina","a nudge from outside","fresh blood","the invisible hand places","salvation arrives","new variables enter","the cosmos intervenes","help from beyond the viewport","strangers in a strange land","the petri dish gets a refill","immigrants, welcome"],milestone:["another thousand turns of the wheel","the odometer clicks over","still here, still going","persistence is its own reward","ten thousand steps","the long now","time is a flat circle, but longer","we persist","generations beyond counting","deep in the run","the odometer means nothing to the cells","and yet, it continues","older than some civilizations","the marathon nobody entered","a digit rolls over; the grid doesn't notice","epochal"],haunted:["the dead outnumber the living, and glow","every path ever walked, still visible","the void remembers being alive","phase-contrast photography of the past","nothing is ever really deleted","the grid forgets nothing — literally, now","a bug so beautiful it became a feature","the ghosts have synchronized their breathing","somewhere between pair 463 and 504","resurrection as a rendering artifact","the afterlife was here all along","history, strobing","every empty cell, a metronome","the universe develops like a photograph","magenta is the color of memory"]},Yd={glider:["a glider, going somewhere","lightspeed, quartered — a glider","a glider carries the news","diagonal traffic"],lwss:["a lightweight spaceship, outbound","small craft advisory"],hwss:["a heavy spaceship shoulders past","heavy freight on the grid"],pulsar:["a pulsar, keeping time for no one","the pulsar breathes in threes"],pentadecathlon:["fifteen beats to the bar","a pentadecathlon runs its laps"],blinker:["the blinkers keep the beat","metronomes, disagreeing"],block:["blocks: the simplest memory","still life with squares"],beehive:["the bees are home","a beehive, sealed"],loaf:["bread, uneaten","a loaf, going stale beautifully"],boat:["a boat with nowhere to sail"],tub:["a tub, holding nothing"],ship:["a ship without a sea"],pond:["a pond, perfectly still"],toad:["a toad, breathing"],beacon:["the beacon still signals","someone left the light on"]},Uo={pulsar:100,pentadecathlon:100,hwss:90,lwss:80,glider:40,beacon:30,toad:28,pond:24,ship:20,loaf:16,beehive:12,boat:12,tub:12,blinker:8,block:6},Kd={blinker:3,block:3},Dc=[[0,"genesis"],[500,"primordial"],[2e3,"emergence"],[1e4,"expansion"],[5e4,"flourishing"],[15e4,"deep time"],[5e5,"eon"],[1e6,"eternity"]],Tn=s=>[...s].length;class Ic{constructor(){R(this,"messages",[]);R(this,"scrollSpeed",.4);R(this,"spawnCooldown",0);R(this,"minGap",12);R(this,"ambientInterval",60);R(this,"moodInterval",50);R(this,"ambientIdx",Math.floor(Math.random()*No.length));R(this,"lastMood","");R(this,"pendingSpecial","")}queueSpecial(t){this.pendingSpecial=t}tick(t,e,n){if(t<10)return;for(const a of this.messages)a.x-=this.scrollSpeed;if(this.messages=this.messages.filter(a=>a.x+Tn(a.text)>0),this.pendingSpecial&&this.canSpawn(t)){this.messages.push({text:this.pendingSpecial,x:t}),this.pendingSpecial="",this.spawnCooldown=Math.max(this.spawnCooldown,this.moodInterval);return}const i=e?qd[e]:void 0;if(i&&e!==this.lastMood&&(this.spawnCooldown=Math.min(this.spawnCooldown,15)),this.spawnCooldown=Math.max(0,this.spawnCooldown-1),this.spawnCooldown>0||!this.canSpawn(t))return;let r,o;i?(r=i[Math.floor(n/180)%i.length]??"",o=this.moodInterval):(r=No[this.ambientIdx]??"",this.ambientIdx=(this.ambientIdx+1)%No.length,o=this.ambientInterval),this.messages.push({text:r,x:t}),this.spawnCooldown=o,this.lastMood=e}canSpawn(t){if(!this.messages.length)return!0;let e=-1/0;for(const n of this.messages)e=Math.max(e,n.x+Tn(n.text));return es+Math.floor(Math.random()*(t-s+1)),Hn=s=>s[Math.floor(Math.random()*s.length)],rt=(s,t)=>Math.floor(s/t),rn=(s,t,e)=>Math.max(t,Math.min(s,e)),Zd=Math.fround(.85),Jd=Math.fround(.15);function ni(s,t){const e=s.length,n=t/100*(e-1),i=Math.floor(n),r=Math.min(e-1,i+1),o=s[i]??0,a=s[r]??0,l=n-i;return Math.trunc(l>=.5?a-(a-o)*(1-l):o+(a-o)*l)}function jd(s,t,e){const n=(o,a)=>o[a]??0,i=new Float64Array(t*e);for(let o=0;o1?n(s,e+o):0);i[o]=a/3;for(let l=1;l=2?n(s,(l-2)*e+o):0),i[l*e+o]=a/3}const r=new Float64Array(t*e);for(let o=0;o1?n(i,a+1):0);r[a]=l/3;for(let c=1;c=2?n(i,a+c-2):0),r[a+c]=l/3}return r}class Qd{constructor(t,e,n=!0,i={}){R(this,"viewH");R(this,"viewW");R(this,"worldH");R(this,"worldW");R(this,"grid");R(this,"age");R(this,"ageSmooth");R(this,"activity");R(this,"nextRow");R(this,"colSum");R(this,"camY");R(this,"camX");R(this,"autoCam",!0);R(this,"zoomLevel",0);R(this,"zoomCooldown",0);R(this,"generation",0);R(this,"paused",!1);R(this,"delay",50);R(this,"popHistory",[]);R(this,"hashHistory",[]);R(this,"cachedPop",0);R(this,"spread",0);R(this,"popFloor",0);R(this,"cyclePeriod",0);R(this,"lastEvent","");R(this,"lastEventGen",0);R(this,"totalInjections",0);R(this,"ticker",new Ic);R(this,"autoFocusMode",!1);R(this,"autoFocusFrame",0);R(this,"dispGridCache",null);R(this,"dispAgeCache",null);R(this,"cachedMood","");R(this,"cachedMoodGen",-1);R(this,"stepBbox",null);R(this,"haunted",!1);R(this,"lastCensus",new Map);R(this,"lastCensusSites",[]);R(this,"sightingGen",new Map);R(this,"sightingCount",new Map);R(this,"lastSightingGen",-1e9);R(this,"dramaUntil",0);R(this,"actBaseline",0);R(this,"dilation",1);this.viewH=t*2,this.viewW=e,this.worldH=Math.max(this.viewH*5,i.minH??400),this.worldW=Math.max(this.viewW*5,i.minW??800);const r=this.worldH*this.worldW;this.grid=new Uint8Array(r),this.age=new Int32Array(r),this.ageSmooth=new Float32Array(r),this.activity=new Float32Array(r),this.nextRow=new Uint8Array(this.worldW),this.colSum=new Uint8Array(this.worldW),this.camY=rt(this.worldH-this.viewH,2),this.camX=rt(this.worldW-this.viewW,2),n&&this.seedInitial()}seedInitial(){const t=rt(this.worldH,2),e=rt(this.worldW,2),n=this.viewH,i=this.viewW;for(const a of Sc)for(let l=0;l<2;l++)this.place(a,t+ve(rt(-n,3),rt(n,3)),e+ve(rt(-i,3),rt(i,3)));for(let a=0;a<2;a++)this.place("gosper_gun",t+ve(rt(-n,4),rt(n,4)),e+ve(rt(-i,4),rt(i,4)));for(let a=0;a<20;a++){const l=rn(t+ve(-n,n),10,this.worldH-10),c=rn(e+ve(-i,i),10,this.worldW-10);this.place(Hn(Ro),l,c)}for(let a=0;a<4;a++){const l=t+ve(rt(-n,3),rt(n,3)),c=e+ve(rt(-i,3),rt(i,3));this.place(Hn(wc),l,c)}const r=t-rt(n,2),o=e-rt(i,2);for(let a=0;a=0&&c=0&&ul&&(l=x+1))}const c=o>0;let u=!1;c&&r-2>=0&&o+2<=t&&a-2>=0&&l+2<=e&&(r-=2,o+=2,a-=2,l+=2,u=(o-r)*(l-a)<=rt(t*e,2)),u=u&&!this.haunted,this.stepBbox=c&&u?[r,o,a,l]:null,u||(r=0,o=t,a=0,l=e);const h=this.ageSmooth;for(let M=0;M0?vt=-1:(m||at<0)&&at>v?vt=at-1:vt=0,i[W]=vt,h[W]=(h[W]??0)+Math.fround(Math.fround(vt)*Jd);const Gt=nt||ot?1:0;n[W]=Gt,E+=Gt}}if(this.generation+=1,this.generation%bc===0)for(let M=0;M500&&this.popHistory.shift(),this.hashHistory.push(this.viewportHash()),this.hashHistory.length>60&&this.hashHistory.shift();const y=this.ensureLife(E);return this.updateCamera(),y}viewportHash(){const[t,e]=this.clampCam();let n=2166136261,i=16777619;for(let r=0;r>>0)*2097152+(i>>>0&2097151)}ensureLife(t){const e=this.viewH*this.viewW,n=rt(e,40),i=this.popHistory,r=i.length;if(r>=100){let h=0;for(let d=r-100;d=150){let h=i[r-1]??0,d=h;for(let f=r-150;fd&&(d=p)}this.spread=d-h}const o=r>=150&&this.spread=4){const h=a[l-1];for(let d=1;d0&&!c){const h=Math.random()<.65?this.injectProvoke():"";h?u=`inject:provoke(cycle=${this.cyclePeriod},${h})`:(u=`inject:heavy(cycle=${this.cyclePeriod})`,this.inject("heavy"))}else o&&!c?Math.random()<.7&&this.injectCollide()?u="inject:collide(stagnant)":(u="inject:medium(stagnant)",this.inject("medium")):this.generation%5e3===0&&this.generation>0?(u="inject:garden",this.injectGarden()):this.generation%300===0&&this.generation>0&&(u="inject:edge",this.injectFromEdge(),this.injectFromEdge());return u&&(this.lastEvent=u,this.lastEventGen=this.generation,this.totalInjections+=1),u}inject(t="medium"){const e={massive:8,heavy:5,medium:3}[t];for(let n=0;n=0&&l=0&&c(h[g]??0)-(h[S]??0)),p=Hn(f.slice(0,d)),v=rt(p,u),m=p%u;return[o+v*l+rt(l,2),a+m*l+rt(l,2)]}aimGlider(t,e,n,i){const[r,o]=Fd[i]??[1,1],a=t-r*n,l=e-o*n,[c,u]=this.clampCam();if(!(c+2<=a&&a(Uo[n[0]]??0)-(Uo[e[0]]??0));for(const[e,n,i]of t.slice(0,6)){const r=ve(35,60),o=[0,1,2,3];for(let a=o.length-1;a>0;a--){const l=Math.floor(Math.random()*(a+1));[o[a],o[l]]=[o[l]??0,o[a]??0]}for(const a of o)if(this.aimGlider(n,i,r,a))return this.dramaUntil=this.generation+r*4+150,this.ticker.queueSpecial("the cosmos takes aim"),e}return""}injectCollide(){let[t,e]=this.findQuietSpot();const[n,i]=this.clampCam();for(let r=0;r<6;r++){const o=ve(16,34),a=n+o+3,l=n+this.viewH-o-6,c=i+o+3,u=i+this.viewW-o-6;if(a>=l||c>=u)continue;t=Math.max(a,Math.min(t,l)),e=Math.max(c,Math.min(e,u));const h=ve(0,3),d=ve(-3,3),f=ve(-3,3);if(!this.aimGlider(t,e,o,h))continue;const p=(h+Hn([1,3]))%4,v=this.aimGlider(t+d,e+f,o,p);return this.dramaUntil=this.generation+o*4+150,this.ticker.queueSpecial(v?"two gliders, one appointment":"a lone glider, sent into the dark"),!0}return!1}calculateTargetZoom(){const[t,e,n,i]=this.stepBbox??[0,this.worldH,0,this.worldW],r=this.worldW;let o=0;for(let S=t;S=1&&y<=10&&++o>=5)break}const a=o>=5,l=[],c=[];for(let S=t;S=1&&(this.age[y]??0)<=10:this.grid[y]!==0)&&(l.push(S),c.push(E))}if(l.length<2)return this.zoomLevel;const u=Int32Array.from(c).sort(),h=ni(l,15),d=ni(l,85),f=ni(u,15),p=ni(u,85),v=Math.max(4,d-h+1),m=Math.max(4,p-f+1);let g=ei;for(let S=qi;S>=ei;S--){const E=2**S;if(v<=this.viewH/E*.5&&m<=this.viewW/E*.5){g=S;break}}return rn(g,ei,qi)}updateCamera(){if(!this.autoCam)return;const t=30,e=Math.max(0,this.camY-t),n=Math.min(this.worldH,this.camY+this.viewH+t),i=Math.max(0,this.camX-t),r=Math.min(this.worldW,this.camX+this.viewW+t),o=this.worldW;let a=0,l=0,c=0;for(let f=e;f25){this.steer(Math.trunc(l/a)+e,Math.trunc(c/a)+i),this.advanceAutoZoom();return}let u=0,h=0,d=0;for(let f=e;f0&&this.steer(Math.trunc(h/u)+e,Math.trunc(d/u)+i),this.advanceAutoZoom()}steer(t,e){const n=rn(t-rt(this.viewH,2),0,this.worldH-this.viewH),i=rn(e-rt(this.viewW,2),0,this.worldW-this.viewW);this.camY=Math.trunc(this.camY+(n-this.camY)*.04),this.camX=Math.trunc(this.camX+(i-this.camX)*.04)}advanceAutoZoom(){if(this.zoomCooldown=Math.max(0,this.zoomCooldown-1),this.zoomCooldown!==0||this.generation%4!==0)return;const t=this.calculateTargetZoom();if(t>this.zoomLevel&&this.zoomLevelei){const e=this.zoomLevel-1,n=e>=0?1:1<<-e;this.viewH*n<=this.worldH&&this.viewW*n<=this.worldW&&(this.zoomLevel=e,this.zoomCooldown=45)}}clampCam(){return[rn(this.camY,0,this.worldH-this.viewH),rn(this.camX,0,this.worldW-this.viewW)]}viewCenter(){const[t,e]=this.clampCam();return[t+rt(this.viewH,2),e+rt(this.viewW,2)]}rawViewGrid(){const[t,e]=this.clampCam(),n=new Uint8Array(this.viewH*this.viewW);for(let i=0;ithis.worldH||u>this.worldW)return null;const h=Math.max(0,Math.min(t-rt(c,2),this.worldH-c)),d=Math.max(0,Math.min(e-rt(u,2),this.worldW-u));return[h,d,c,u,l,!1]}const n=1<Math.trunc(t[p]??0),i=this.zoomLevel===0?null:this.cover();if(!i){const[p,v]=this.clampCam(),m=new Int32Array(this.viewH*this.viewW);for(let g=0;g(g[T]??0)&&(g[T]=x);const L=x!==0?x:m;L>(S[T]??0)&&(S[T]=L)}}const E=new Int32Array(p*v);for(let y=0;y0?M:w>m?w:0}return{h:p,w:v,data:E}}const h=Math.min(a*c,this.viewH),d=Math.min(l*c,this.viewW),f=new Int32Array(h*d);for(let p=0;pei){const t=this.zoomLevel-1,e=t>=0?1:1<<-t;(t>=0||this.viewH*e<=this.worldH&&this.viewW*e<=this.worldW)&&(this.zoomLevel=t)}this.invalidate()}autoFocus(){const t=this.worldH,e=this.worldW;let n=0,i=0;for(let B=0;B=1&&X<=10&&n++,this.grid[B]&&i++}const r=n>=5;if((r?n:i)===0)return;const o=B=>{const X=this.age[B]??0;return r?X>=1&&X<=10:this.grid[B]!==0},a=16,l=rt(t,a),c=rt(e,a);if(l<1||c<1)return;const u=new Float64Array(l*c);for(let B=0;Bd&&(d=X,f=B)}const p=rt(f,c)*a+rt(a,2),v=f%c*a+rt(a,2),m=a*2,g=Math.max(0,p-m),S=Math.min(t,p+m),E=Math.max(0,v-m),y=Math.min(e,v+m),M=[],w=[];for(let B=g;B=ei;B--){const X=2**B;if(U<=this.viewH/X*.6&&K<=this.viewW/X*.6){W=B;break}}this.zoomLevel=rn(W,ei,qi),this.camY=rn(Z-rt(this.viewH,2),0,this.worldH-this.viewH),this.camX=rn(N-rt(this.viewW,2),0,this.worldW-this.viewW),this.autoCam=!1,this.invalidate()}pan(t,e){this.autoCam=!1;const n=2**-this.zoomLevel,i=t!==0?Math.max(1,Math.trunc(Math.abs(t)*n))*Math.sign(t):0,r=e!==0?Math.max(1,Math.trunc(Math.abs(e)*n))*Math.sign(e):0;this.camY=rn(this.camY+i,0,this.worldH-this.viewH),this.camX=rn(this.camX+r,0,this.worldW-this.viewW),this.invalidate()}home(){this.autoCam=!0,this.zoomLevel=0,this.invalidate()}clear(){this.grid.fill(0),this.age.fill(0),this.ageSmooth.fill(0),this.generation=0,this.popHistory=[],this.hashHistory=[],this.invalidate()}takeCensus(){const t=this.rawViewGrid();let e=!1;for(let l=0;l[h,c+d,u+f]),n=l.counts}}catch{}if(this.lastCensus=n,!n.size||this.generation-this.lastSightingGen<600)return;let i="",r=0;for(const[l,c]of n){if(c<(Kd[l]??1)||this.generation-(this.sightingGen.get(l)??-1e9)<1800)continue;const u=Uo[l]??0;u>r&&(r=u,i=l)}if(!i)return;const o=Yd[i]??[],a=this.sightingCount.get(i)??0;this.sightingCount.set(i,a+1),this.ticker.queueSpecial(o[a%o.length]??i),this.sightingGen.set(i,this.generation),this.lastSightingGen=this.generation}snapshot(){return{h:this.worldH,w:this.worldW,age:this.age.slice(),generation:this.generation,totalInjections:this.totalInjections}}adopt(t){const e=Math.min(this.worldH,t.h),n=Math.min(this.worldW,t.w),i=rt(t.h-e,2),r=rt(t.w-n,2),o=rt(this.worldH-e,2),a=rt(this.worldW-n,2);this.grid.fill(0),this.age.fill(0);let l=0;for(let c=0;c0&&(this.grid[d]=1,l++)}this.generation=Math.max(0,Math.trunc(t.generation)),this.totalInjections=Math.max(0,Math.trunc(t.totalInjections)),this.ageSmooth.fill(0),this.activity.fill(0),this.hashHistory=[],this.cachedPop=l,this.popHistory=[l],this.stepBbox=null,this.dramaUntil=0,this.invalidate()}toggleHaunted(){if(this.haunted=!this.haunted,this.invalidate(),!this.haunted)for(let t=0;t=this.worldH||e<0||e>=this.worldW)return;const i=t*this.worldW+e;this.grid[i]=n,this.age[i]=n?1:0,this.invalidate()}tickTicker(t){this.ticker.tick(t,this.detectMood(),this.generation)}detectMood(){return this.cachedMoodGen===this.generation?this.cachedMood:(this.cachedMood=this.computeMood(),this.cachedMoodGen=this.generation,this.cachedMood)}computeMood(){if(this.haunted)return"haunted";if(this.lastEvent&&this.generation-this.lastEventGen<90)return"injection";if(this.cyclePeriod>0)return"cycle";if(this.generation>=1e4&&this.generation%1e4<120)return"milestone";const t=this.popHistory,e=t.length;if(e>=30){let r=0,o=0;for(let l=e-30;l1.15)return"booming";if(a<.85)return"declining"}const n=e?t[e-1]??0:0;return n/Math.max(this.viewH*this.viewW,1)>.15?"dense":n0?"sparse":this.spread=150?"stagnant":""}epoch(){var e;let t=((e=Dc[0])==null?void 0:e[1])??"genesis";for(const[n,i]of Dc)this.generation>=n&&(t=i);return t}population(){return this.cachedPop}activityCenterX(){const[t,e]=this.clampCam();let n=0,i=0;for(let r=0;rthis.actBaseline?.015:.003;this.actBaseline=this.actBaseline*(1-e)+t*e;const n=t/Math.max(this.actBaseline,1);let i=n>1?Math.min(1.6,1+(n-1)*.7):.7+n*.3;return t<60&&(i=Math.min(i,.7)),this.dilation=this.dilation*.85+i*.15,this.dilation}sparkline(t=24){const e=this.popHistory;if(e.length<2)return"";const n=Math.max(0,e.length-t);let i=e[n]??0,r=i;for(let c=n;cr&&(r=u)}const o=Bs.length-1,a=Bs[Math.floor(Bs.length/2)]??"▅";let l="";for(let c=n;c>>0}function Nc(s){return`rgb(${s[0]},${s[1]},${s[2]})`}const Oo=[10,10,12],ef="rgb(200,200,200)",Ji="rgb(112,112,116)",Uc=ti.length,ji=ti.map(s=>Ai(Zi(s))),ko=En.map(s=>Ai(Zi(s))),Fc=Ld.map(([s,t])=>[Ai(Zi(s)),Ai(Zi(t))]),nf=Dd.map(([s,t])=>[Ai(Zi(s)),Ai(Zi(t))]),xr=Ai(Oo),sf='ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace';let _r=null;function rf(s){if(_r&&_r.maxAge===s)return _r.table;const t=new Uint8Array(s+1),e=Math.log1p(s);for(let n=1;n<=s;n++)t[n]=Math.min(Math.trunc(Math.log1p(n)/e*(Uc-1)),Uc-1);return _r={maxAge:s,table:t},t}function Hs(s){return s.toLocaleString("en-US")}class of{constructor(t){R(this,"cw",8);R(this,"cols",80);R(this,"rows",24);R(this,"statusRows",1);R(this,"keys",!0);R(this,"img",null);R(this,"widths",new Map);R(this,"px",null);R(this,"off",null);this.ctx=t}get gridRows(){return Math.max(1,this.rows-this.statusRows)}resize(t,e,n){this.cw=Math.max(2,Math.round(n)),this.cols=Math.max(20,Math.floor(t/this.cw)),this.rows=Math.max(6,Math.floor(e/(this.cw*2))),this.statusRows=this.cols>=150?1:2,this.keys=this.cols>=150,this.img=null,this.widths.clear()}status(t,e){const n=t.sparkline(this.cols<100?10:24),i=t.autoCam?"auto":"pan",r=Ud[t.zoomLevel]??`${2**t.zoomLevel}x`,o=t.autoFocusMode?"[F]":"",a=t.haunted?"[HAUNTED]":"",l=e?` ${e}`:"",c=` ${t.epoch()} gen ${Hs(t.generation)} pop ${Hs(t.population())} ${n}`,u=this.keys?" q r spc +/- arrows h z/x f g s ":" ",h=`${l} ${a}${o} ${r} ${i}${u}`;return this.statusRows===1?{left:c,right:h,tickerCol:Tn(c)+1,tickerWidth:this.cols-Tn(c)-Tn(h)-2}:{left:c,right:h,tickerCol:1,tickerWidth:this.cols-2}}render(t,e,n){const i=this.ctx,r=this.cw,o=this.cols*r,a=this.rows*r*2;i.fillStyle=Nc(Oo),i.fillRect(0,0,o,a),this.paintGrid(t),i.font=`${Math.round(r*1.62)}px ${sf}`,i.textBaseline="middle",n&&this.statsOverlay(t);const l=this.rows-1;if(this.statusRows===1)if(e.tickerWidth<10)this.text(`${e.left} ${e.right}`.slice(0,this.cols-1),l,0,Ji);else{this.text(e.left,l,0,Ji),this.ticker(t,l,e);const c=this.cols-Tn(e.right);this.text([...e.right].slice(0,this.cols-1-c).join(""),l,c,Ji)}else{const c=this.cols-1,u=Tn(e.left)+Tn(e.right)<=c?e.left+" ".repeat(c-Tn(e.left)-Tn(e.right))+e.right:`${e.left} ${e.right}`;this.text([...u].slice(0,c).join(""),l-1,0,Ji),this.ticker(t,l,e)}}paintGrid(t){const e=t.displayGrid(),n=t.displayAge(),i=Math.min(Math.floor(e.h/2),Math.floor(n.h/2),this.gridRows),r=Math.min(e.w,n.w,this.cols),o=this.cols,a=this.gridRows*2;(!this.img||this.img.width!==o||this.img.height!==a)&&(this.img=new ImageData(o,a),this.px=new Uint32Array(this.img.data.buffer),this.off=typeof OffscreenCanvas<"u"?new OffscreenCanvas(o,a):Object.assign(document.createElement("canvas"),{width:o,height:a}));const l=this.px;l.fill(xr);let c=1;for(let M=0;Mc&&(c=n.data[M]??0);const u=rf(c),h=t.haunted,d=t.zoomLevel>=0||h,f=h?Ti:zn,p=M=>Math.min(Math.max(-M-1,0),f-1),v=M=>u[Math.min(Math.max(M,0),c)]??0,m=M=>{var w;return h?((w=Fc[M])==null?void 0:w[0])??0:ko[M]??0},g=M=>{var w;return h?((w=Fc[M])==null?void 0:w[1])??0:xr};for(let M=0;M0,Z=(e.data[C+U]??0)>0,N=n.data[x+U]??0,W=n.data[T+U]??0,B=d&&N<0,X=d&&W<0;if(!(K||Z||B||X))continue;let nt=xr,ot=xr;if((K||B)&&(Z||X))if(K&&Z)nt=ji[v(N)]??0,ot=ji[v(W)]??0;else if(B&&X){const at=p(N),vt=p(W);if(h){const Gt=nf[at*Ti+vt];nt=(Gt==null?void 0:Gt[0])??0,ot=(Gt==null?void 0:Gt[1])??0}else nt=ko[at]??0,ot=ko[vt]??0}else K?nt=ji[v(N)]??0:ot=ji[v(W)]??0;else if(K)nt=ji[v(N)]??0;else if(Z)ot=ji[v(W)]??0;else if(B){const at=p(N);nt=m(at),ot=g(at)}else{const at=p(W);ot=m(at),nt=g(at)}l[L+U]=nt,l[D+U]=ot}}const S=this.off,E=S.getContext("2d");if(!E||!this.img)return;E.putImageData(this.img,0,0);const y=this.ctx;y.imageSmoothingEnabled=!1,y.drawImage(S,0,0,o*this.cw,a*this.cw)}ticker(t,e,n){const i=Ic.dim(t.generation)?Ji:ef;for(const r of t.ticker.messages){const o=Math.trunc(r.x),a=[...r.text],l=Math.max(o,0),c=Math.min(o+a.length,n.tickerWidth);l>=c||this.text(a.slice(l-o,c-o).join(""),e,n.tickerCol+l,i)}}statsOverlay(t){const e=t.lastCensus.size?[...t.lastCensus.entries()].sort((c,u)=>u[1]-c[1]).map(([c,u])=>`${u}×${c}`).join(" "):"none",n=36,i=11,r=this.cols-n-2,o=this.rows-(this.statusRows-1)-i-2;if(r<0||o<0)return;const a=["─".repeat(n-2)," infinity engine",` pop floor : ${Hs(t.popFloor)}`,` spread/150 : ${Hs(t.spread)}`,` cycle : ${t.cyclePeriod===0?"none":`period ${t.cyclePeriod}`}`,` injections : ${t.totalInjections}`,` last event : ${t.lastEvent||"none"}`,t.lastEvent?` @ gen : ${Hs(t.lastEventGen)}`:" ",` world : ${t.worldH}x${t.worldW}`,` census : ${[...e].slice(0,n-16).join("")}`,` tempo : ${(1/Math.max(t.dilation,.01)).toFixed(2)}x`],l=this.ctx;l.fillStyle=Nc(Oo),l.fillRect(r*this.cw,o*this.cw*2,n*this.cw,a.length*this.cw*2),a.forEach((c,u)=>{const h=[...` ${c}`.padEnd(n)].slice(0,n).join("");this.text(h,o+u,r,Ji)})}text(t,e,n,i){const r=this.ctx,o=this.cw,a=e*o*2;r.fillStyle=i;let l=n;for(const c of t){if(l>=this.cols)break;if(l>=0&&c!==" "){const u=Bs.indexOf(c);if(u>=0){const h=Math.max(1,Math.round(o*2*(u+1)/8));r.fillRect(l*o,a+o*2-h,o,h)}else if(c==="─")r.fillRect(l*o,a+o-Math.max(1,Math.round(o/8))/2,o,Math.max(1,Math.round(o/8)));else{let h=this.widths.get(c);h===void 0&&(h=r.measureText(c).width,this.widths.set(c,h)),r.fillText(c,l*o+o/2-h/2,a+o)}}l++}}}const It=512,Wt=384,Zt='"Libertinus Mono", "LibertinusMono", ui-monospace, monospace',Bo='"Love Letter Typewriter", "Libertinus Mono", monospace';function zo(s,t,e){t();const n=typeof document<"u"?document.fonts:void 0,i=(Array.isArray(s)?s:[s]).filter(r=>n&&!n.check(r));!n||!i.length||Promise.all(i.map(r=>n.load(r))).then(()=>{t(),e==null||e()},()=>{})}class fn{constructor(t,e){R(this,"canvas");R(this,"ctx");R(this,"fps",15);R(this,"acc",1);R(this,"version",0);this.site=t,this.env=e,this.canvas=document.createElement("canvas"),this.canvas.width=It,this.canvas.height=Wt;const n=this.canvas.getContext("2d",{alpha:!1});if(!n)throw new Error("oikos: no 2d context");this.ctx=n}tick(t,e,n,i=1){this.acc+=e;const r=n?Math.max(this.fps,30):this.fps*i;if(this.acc<1/r)return!1;const o=this.acc;return this.acc=0,this.draw(t,o),this.version++,!0}clear(t){this.ctx.fillStyle=t,this.ctx.fillRect(0,0,It,Wt)}wrap(t,e,n=1/0){const i=this.ctx,r=t.split(/\s+/).filter(Boolean),o=[];let a="";for(const l of r){const c=a?`${a} ${l}`:l;if(i.measureText(c).width>e&&a){if(o.push(a),a=l,o.length>=n)break}else a=c}if(a&&o.lengthe;)l=l.replace(/\s*\S*$/,"");o[n-1]=`${l}…`}return o}condensed(t,e,n,i=.72,r="left"){const o=this.ctx;o.save(),o.translate(e,n),o.scale(i,1),o.textAlign=r,o.fillText(t,0,0),o.restore()}spaced(t,e,n,i,r="left"){const o=this.ctx,a=[...t],l=a.map(h=>o.measureText(h).width),c=l.reduce((h,d)=>h+d,0)+i*(a.length-1);let u=r==="center"?e-c/2:e;o.save(),o.textAlign="left",a.forEach((h,d)=>{o.fillText(h,u,n),u+=(l[d]??0)+i}),o.restore()}}function Ho(s){let t=2166136261;for(let e=0;e>>0)%1e5/1e5}function Ci(s){let t=s>>>0;return()=>{t=t+1831565813>>>0;let e=t;return e=Math.imul(e^e>>>15,e|1),e^=e+Math.imul(e^e>>>7,e|61),((e^e>>>14)>>>0)/4294967296}}function af(s){return Math.round(s).toLocaleString("en-US")}const Oc=15,lf={minH:0,minW:0};class cf extends fn{constructor(e,n){super(e,n);R(this,"term");R(this,"life",null);R(this,"seenSave");R(this,"genAcc",1/Oc);this.fps=15,this.term=new of(this.ctx),this.term.resize(It,Wt,8)}sync(){var r;const e=this.env.feeds.universe.value;if(this.life&&(e==null?void 0:e.savedAt)===((r=this.seenSave)==null?void 0:r.savedAt))return;this.seenSave=e;const n=e?Bd():null,i=new Qd(this.term.gridRows,this.term.cols,!n,lf);n&&(i.adopt(n),i.ticker.queueSpecial(`the universe remembers generation ${i.generation.toLocaleString("en-US")}`)),this.life=i}draw(e,n){this.sync();const i=this.life;if(!i)return;const r=1/Oc;if(this.genAcc+=n,this.genAcc0&&i.generation%150===0&&i.takeCensus();const o=this.term.status(i,"");o.tickerWidth>=10&&i.tickTicker(o.tickerWidth),this.term.render(i,o,!1)}}const ii=62,Gs=21,hf=It/ii,uf=12.4,df=42,Go=" .,:;-=+*#%@";function ff(){const s=[];for(let e=0;e<16;e++)s.push([e&1?1:-1,e&2?1:-1,e&4?1:-1,e&8?1:-1]);const t=[];for(let e=0;e<16;e++)for(let n=0;n<4;n++){const i=e^1<>>0).toString(16).padStart(8,"0").replace(/(....)(....)/,"$1·$2");return{prompt:o,template:a,coordinate:l,hue:n?Math.floor(Ho(o)*360):135,figure:Vo[this.seq++%Vo.length]??Vo[0]??[],bornAt:e}}draw(e){(!this.gen||e-this.gen.bornAt>9||!this.gen.prompt&&this.env.feeds.apeiron.value)&&(this.gen=this.generate(e));const n=this.gen,i=this.ctx,r=`hsl(${n.hue} 100% 58%)`,o=`hsl(${n.hue} 100% 76%)`,a=`hsl(${n.hue} 60% 22%)`,l=i.createRadialGradient(It/2,Wt*.4,20,It/2,Wt*.4,It*.7);l.addColorStop(0,`hsl(${n.hue} 60% 7%)`),l.addColorStop(1,"#030309"),i.fillStyle=l,i.fillRect(0,0,It,Wt),i.font=`12px ${Zt}`,i.fillStyle=r,i.fillText("apeiron",14,22),i.fillStyle=a,i.fillText("· æthera",76,22),i.textAlign="right",i.fillStyle=r,i.fillText(n.template.replace(/_/g," "),It-14,22),i.textAlign="left",this.raster(e,n.figure),i.font=`12px ${Zt}`;for(let d=0;d9?o:p>4?r:a,i.fillText(Go[p]??".",f*hf,df+d*uf+10))}const c=e-n.bornAt,u=n.prompt||"reading the grammar…";i.font=`12px ${Zt}`;const h=this.wrap(u.slice(0,Math.floor(c*70)),It-28,4);i.fillStyle="rgba(3,3,9,0.7)",i.fillRect(0,Wt-98,It,98),i.fillStyle=a,i.fillRect(14,Wt-98,It-28,1),i.fillStyle=o,h.forEach((d,f)=>i.fillText(d,14,Wt-78+f*15)),i.fillStyle=r,i.font=`11px ${Zt}`,i.fillText(`⌖ ${n.coordinate}`,14,Wt-12),i.fillStyle=a,i.textAlign="right",i.fillText("␣ generate F keep A auto",It-14,Wt-12),i.textAlign="left"}raster(e,n){this.depth.fill(-1/0),this.glyph.fill(0);const i=e*.45,r=e*.31,o=e*.23,[a,l,c,u,h,d]=[Math.cos(i),Math.sin(i),Math.cos(r),Math.sin(r),Math.cos(o),Math.sin(o)];for(const f of n){let[p,v,m,g]=f;[p,g]=[p*a-g*l,p*l+g*a],[v,m]=[v*c-m*u,v*u+m*c],[m,g]=[m*h-g*d,m*d+g*h];const S=2.6/(3.2-g);p*=S,v*=S,m*=S;const E=3.4/(4.6-m),y=Math.round(ii/2+p*E*11.5),M=Math.round(Gs/2+v*E*5.6);if(y<0||y>=ii||M<0||M>=Gs)continue;const w=M*ii+y;m>(this.depth[w]??-1/0)&&(this.depth[w]=m,this.glyph[w]=Math.max(1,Math.min(Go.length-1,Math.round((m+2.2)/4.4*(Go.length-1)))))}}}function gf(s,t){const e=s.templates[Math.floor(t()*s.templates.length)];return e?{prompt:e.structure.replace(/\{(\w+)\}/g,(i,r)=>{var a;const o=s.components[r];return o!=null&&o.length?((a=o[Math.floor(t()*o.length)])==null?void 0:a.word)??r:r.replace(/_/g," ")}),template:e.id}:{prompt:"",template:""}}const Wo="#e8e2d4",$o="#a39d90",kc="#5d5850",Ri=34,Qi=250,Pi=84,yr=Wt-Pi-22;class vf extends fn{constructor(e,n){super(e,n);R(this,"standIn");this.fps=10,this.standIn=xf()}draw(e){const n=this.ctx;this.clear("#070707"),n.fillStyle=Wo,n.font=`22px ${Zt}`,this.spaced("chronicle",It/2,38,11,"center"),n.fillStyle=$o,n.font=`11px ${Zt}`,this.spaced("what the dream remembers",It/2,60,2,"center");const r=[...this.env.feeds.chronicle.value.strata].reverse();if(n.fillStyle="#000",n.fillRect(Ri-1,Pi-1,Qi+2,yr+2),n.imageSmoothingEnabled=!0,r.length){const l=r.map((h,d)=>Math.pow(.6,d)),c=l.reduce((h,d)=>h+d,0);let u=Pi;r.forEach((h,d)=>{const f=yr*(l[d]??0)/c;n.save(),n.translate(Ri,u+f),n.scale(1,-1),n.drawImage(h,0,0,Qi,f),n.restore(),u+=f,n.fillStyle="rgba(232,226,212,0.08)",n.fillRect(Ri,u,Qi,1)})}else n.globalAlpha=.75,n.drawImage(this.standIn,Ri,Pi,Qi,yr),n.globalAlpha=1;const o=Pi+e*14%yr,a=n.createLinearGradient(0,o-16,0,o+2);a.addColorStop(0,"rgba(232,226,212,0)"),a.addColorStop(1,"rgba(232,226,212,0.22)"),n.fillStyle=a,n.fillRect(Ri,o-16,Qi,18),n.fillStyle="rgba(232,226,212,0.5)",n.fillRect(Ri-6,o,4,1),this.log(e)}log(e){const n=this.ctx,i=this.env.feeds.chronicle.value,r=Ri+Qi+24,o=It-r-18;n.font=`11px ${Zt}`,n.fillStyle=kc,n.fillText(i.known?`${i.eraCount||i.eras.length} eras`:"reading the core…",r,Pi+8);const a=[...i.eras].reverse().slice(0,7);let l=Pi+34;if(!a.length){n.fillStyle=$o;for(const c of this.wrap("every fifteen seconds of the dream settles here as one line of its own colour.",o,6))n.fillText(c,r,l),l+=15;return}for(const c of a){if(l>Wt-30)break;const u=new Date(c.t0*1e3);n.fillStyle=c.open?Wo:kc,n.font=`10px ${Zt}`;const h=`${u.getHours().toString().padStart(2,"0")}:${u.getMinutes().toString().padStart(2,"0")}`;n.fillText(c.open?`${h} now`:h,r,l),c.open&&(n.fillStyle=Math.sin(e*3)>0?"#d6c9a8":"#6d6555",n.fillRect(r-10,l-6,4,4)),n.font=`12px ${Zt}`,n.fillStyle=c.open?Wo:$o;const d=this.wrap(c.title||"untitled",o,2);d.forEach((f,p)=>n.fillText(f,r,l+15+p*14)),l+=22+d.length*14}}}function xf(){const s=document.createElement("canvas");s.width=64,s.height=240;const t=s.getContext("2d");if(!t)return s;const e=Ci(1729),n=["#5c3b36","#7a4f45","#3f3a4c","#8a6f64","#2d4a4f","#6b2c3a","#a38a74","#40302c"];let i=n[0];for(let r=0;r0?Xo:Mr,n.globalAlpha=.5+e*.5,n.lineWidth=1,n.strokeRect(t.x+.5,t.y+.5,t.w,t.h),n.globalAlpha=1,n.font=`10px ${Zt}`,n.fillStyle=e>0?qo:Li,n.fillText(t.label,t.x+6,t.y+13)}arrow(t,e,n,i,r,o){const a=this.ctx;a.strokeStyle=Mr,a.beginPath(),a.moveTo(t,e),a.lineTo(n,i),a.stroke();const l=Math.sign(n-t||i-e);if(a.fillStyle=Mr,a.beginPath(),e===i?(a.moveTo(n,i),a.lineTo(n-6*l,i-4),a.lineTo(n-6*l,i+4)):(a.moveTo(n,i),a.lineTo(n-4,i-6*l),a.lineTo(n+4,i-6*l)),a.fill(),o>=0&&o<=1){const c=t+(n-t)*o,u=e+(i-e)*o,h=a.createRadialGradient(c,u,0,c,u,8);h.addColorStop(0,"rgba(213,155,255,1)"),h.addColorStop(1,"rgba(213,155,255,0)"),a.fillStyle=h,a.fillRect(c-8,u-8,16,16)}r&&(a.font=`9px ${Zt}`,a.fillStyle=Li,a.textAlign="center",a.fillText(r,(t+n)/2,e===i?e-6:(e+i)/2),a.textAlign="left")}draw(t){const e=this.ctx;this.clear(_f);const n=this.env.feeds.chronicle.value,i=6,r=t%i/i,o=400+Math.floor(t/i);if(e.font=`14px ${Zt}`,e.fillStyle=Xo,e.fillText("dream_gen",18,28),e.font=`10px ${Zt}`,e.fillStyle=Li,e.fillText("a truly infinite diffusion stream",110,28),e.textAlign="right",e.fillText(`kf ${String(o).padStart(5,"0")}`,It-18,28),e.textAlign="left",this.box(si,r<.25?1:0),this.box(Ve,r>=.25&&r<.5?1:.2),this.box(ri,0),this.box(Di,r>=.5?1:0),this.box(br,Math.sin(t*.8)>.6?1:0),n.thumb)e.drawImage(n.thumb,Ve.x+6,Ve.y+18,Ve.w-12,(Ve.w-12)/2),e.globalAlpha=.35,e.drawImage(n.thumb,si.x+6,si.y+18,si.w-12,(si.w-12)/2),e.globalAlpha=1;else for(const h of[si,Ve]){const d=e.createLinearGradient(h.x,h.y,h.x+h.w,h.y+h.h);d.addColorStop(0,`hsl(${(t*8+h.x)%360} 40% 30%)`),d.addColorStop(1,`hsl(${(t*8+h.x+90)%360} 40% 18%)`),e.fillStyle=d,e.fillRect(h.x+6,h.y+18,h.w-12,(h.w-12)/2)}e.fillStyle="#1b1426",e.fillRect(ri.x+6,ri.y+18,ri.w-12,(ri.w-12)/2),e.fillStyle=Li,e.font=`9px ${Zt}`,e.fillText("txt2img on swap",ri.x+10,ri.y+50),this.arrow(si.x+si.w,91,Ve.x,91,"img2img",r<.25?r/.25:-1),this.arrow(ri.x,91,Ve.x+Ve.w,91,"",-1),this.arrow(Ve.x+Ve.w/2,Ve.y+Ve.h,Ve.x+Ve.w/2,Di.y,"",r>=.25&&r<.5?(r-.25)/.25:-1);const a=16,l=r>=.5?Math.floor((r-.5)/.5*a):0;for(let h=0;h{const p=br.y+34+f*22;e.fillStyle=qo,e.font=`10px ${Zt}`,e.fillText(h,br.x+8,p),e.fillStyle=Li,e.fillText(d,br.x+78,p)}),e.fillStyle="rgba(20,12,30,0.8)",e.fillRect(0,Wt-110,It,110),e.fillStyle=Mr,e.fillRect(18,Wt-110,It-36,1),e.font=`10px ${Zt}`,e.fillStyle=Li,e.fillText(n.prompt?`prompt${n.template?` · ${n.template}`:""}`:"prompt",18,Wt-92),e.font=`12px ${Zt}`,e.fillStyle=qo;const u=n.prompt||"the dreamer is asleep; its last prompt will appear here when the chronicle has one.";this.wrap(u,It-36,5).forEach((h,d)=>e.fillText(h,18,Wt-72+d*15))}}const Sr=new Image;Sr.src="/static/oikos/stage.jpg";const on=372,an=186,Bc=(It-on)/2,zc=118;class Mf extends fn{constructor(e,n){super(e,n);R(this,"frame",document.createElement("canvas"));R(this,"fctx");R(this,"prev",null);R(this,"cur",null);R(this,"swapAt",0);this.fps=20,this.frame.width=on,this.frame.height=an;const i=this.frame.getContext("2d");if(!i)throw new Error("oikos: no 2d context");this.fctx=i}draw(e){const n=this.ctx;this.clear("#120406"),Sr.complete&&Sr.naturalWidth&&(n.globalAlpha=.95,n.drawImage(Sr,0,0,It,Wt),n.globalAlpha=1);const i=this.env.feeds.chronicle.value;i.thumb&&i.thumb!==this.cur&&(this.prev=this.cur,this.cur=i.thumb,this.swapAt=e);const r=this.fctx;if(r.globalCompositeOperation="source-over",r.globalAlpha=1,this.cur){const a=Math.min(1,(e-this.swapAt)/2.5);this.prev&&a<1&&this.kenBurns(this.prev,e-20),r.globalAlpha=this.prev?a:1,this.kenBurns(this.cur,e),r.globalAlpha=1}else this.standIn(e);const o=r.createRadialGradient(on/2,an/2,an*.25,on/2,an/2,on*.56);o.addColorStop(0,"rgba(0,0,0,1)"),o.addColorStop(.72,"rgba(0,0,0,0.9)"),o.addColorStop(1,"rgba(0,0,0,0)"),r.globalCompositeOperation="destination-in",r.fillStyle=o,r.fillRect(0,0,on,an),r.globalCompositeOperation="source-over",n.save(),n.globalCompositeOperation="lighter",n.globalAlpha=.18,n.drawImage(this.frame,Bc-30,zc+an-20,on+60,90),n.restore(),n.drawImage(this.frame,Bc,zc),this.status(e),n.fillStyle="rgba(8,4,4,0.72)",n.fillRect(0,Wt-34,It,34),n.fillStyle="#efe6d2",n.font=`12px ${Zt}`,this.spaced("NOW SHOWING · A DREAM · ALL NIGHT",It/2,Wt-13,3,"center")}kenBurns(e,n){const i=1.06+.05*Math.sin(n*.07),r=Math.sin(n*.05)*10,o=Math.cos(n*.043)*5,a=on*i,l=an*i;this.fctx.drawImage(e,(on-a)/2+r,(an-l)/2+o,a,l)}standIn(e){const n=this.fctx,i=n.createLinearGradient(0,0,0,an);i.addColorStop(0,"#3a2440"),i.addColorStop(1,"#170d1c"),n.fillStyle=i,n.fillRect(0,0,on,an);const r=[12,330,280,20,350];for(let o=0;o<5;o++){const a=e*(.11+o*.03)+o*1.7,l=on/2+Math.cos(a)*(60+o*14)*(o%2?1:-1),c=an/2+Math.sin(a*1.3)*30,u=52+22*Math.sin(a*.7+o),h=n.createRadialGradient(l,c,0,l,c,u);h.addColorStop(0,`hsla(${r[o]} 90% 66% / 0.75)`),h.addColorStop(1,`hsla(${r[o]} 90% 50% / 0)`),n.fillStyle=h,n.fillRect(0,0,on,an)}}status(e){const n=this.ctx,i=this.env.feeds.dreams.value,r=this.env.feeds.chronicle.value;n.font=`12px ${Zt}`;let o,a;i.known&&i.awake?(a=Math.sin(e*4)>0?"#ff3b3b":"#6a1010",o=`LIVE frame ${af(i.frame)}${i.viewers?` · ${i.viewers} watching`:""}`):i.known?(a="#5a5a5a",o=r.thumb?"asleep · last remembered":"asleep · wakes when watched"):(a="#3a3a3a",o=r.thumb?"last remembered":"no signal · a stand-in");const l=n.measureText(o).width+34;n.fillStyle="rgba(0,0,0,0.55)",n.fillRect(14,14,l,24),n.fillStyle=a,n.beginPath(),n.arc(27,26,4.5,0,Math.PI*2),n.fill(),n.fillStyle="#f2e9dc",n.fillText(o,38,30)}}const bf="#04060c",Hc="#9fc6ff",Vs="#3d5378",Ws="#c9d1d9",Sf="#a5e3b5",Yo="#ffc387",Gc=[["GET","/api/dreams/status","how it is (never wakes it)"],["WS ","/ws/dreams","the dream itself, h264"],["GET","/api/dreams/stream","MPEG-TS"],["SSE","/api/dreams/sse","events"],["GET","/api/dreams/embed","take it with you"]];class wf extends fn{constructor(e,n){super(e,n);R(this,"askedAt",-10);R(this,"lastRaw");this.fps=12}draw(e){const n=this.ctx;this.clear(bf);const i=this.env.feeds.dreams.value.raw;i!==this.lastRaw&&(this.lastRaw=i,this.askedAt=e);const r=e-this.askedAt;n.font=`13px ${Zt}`;const o=`curl -s ${location.host}/api/dreams/status`,a=o.slice(0,Math.floor(r*38));if(n.fillStyle=Vs,n.fillText("$",16,28),n.fillStyle=Hc,n.fillText(a+(a.length=o.length){const c=i?Ef(i,11):[[{text:"curl: (52) the dream did not answer",color:"#e58a8a"}]],u=Math.floor((r-o.length/38)*30);c.slice(0,u).forEach((h,d)=>{let f=16;for(const p of h)n.fillStyle=p.color,n.fillText(p.text,f,52+d*17),f+=n.measureText(p.text).width})}const l=Wt-128;n.fillStyle="rgba(159,198,255,0.06)",n.fillRect(10,l-20,It-20,138),n.strokeStyle="rgba(159,198,255,0.25)",n.strokeRect(10.5,l-19.5,It-21,137),n.font=`11px ${Zt}`,n.fillStyle=Vs,n.fillText("ENDPOINTS",20,l-4),Gc.forEach(([c,u,h],d)=>{const f=l+16+d*19,p=Math.floor(e/2.5)%Gc.length===d;n.fillStyle=p?Yo:Vs,n.fillText(c,20,f),n.fillStyle=p?"#ffffff":Hc,n.fillText(u,58,f),n.fillStyle=Vs,n.fillText(h,250,f)})}}function Ef(s,t){const e=[],n=(i,r,o,a)=>{if(e.length>=t)return;const l=[{text:r,color:Ws}];o!==null&&l.push({text:`"${o}": `,color:Ws});const c=a?"":",";if(i&&typeof i=="object"&&!Array.isArray(i)){const d=Object.entries(i);e.push([...l,{text:"{",color:Ws}]),d.forEach(([f,p],v)=>n(p,`${r} `,f,v===d.length-1)),e.length=t&&(e[t-1]=[{text:" …",color:Vs}]),e}const Ue={bg:"#181522",screen:"#231f36",fg:"#c9d1d9",dim:"#6e7681",sys:"#8b949e",quit:"#f85149",action:"#a371f7",join:"#3fb950"},Vc=["#58a6ff","#3fb950","#d29922","#a371f7","#f778ba","#39c5cf","#ff7b72","#7ee787","#ffa657","#79c0ff","#d2a8ff","#56d364"];function Tf(s){let t=0;for(const e of s)t=t*31+e.charCodeAt(0)>>>0;return Vc[t%Vc.length]??Ue.fg}function Af(s){const t=[{text:`[${s.stamp}] `,color:Ue.dim}],e=s.nick,n=s.content;switch(s.type){case"message":t.push({text:`<${e}> `,color:Tf(e)},{text:n,color:Ue.fg});break;case"action":t.push({text:`* ${e} ${n}`,color:Ue.action});break;case"quit":t.push({text:`⫫ ${e} has quit${n?` (${n})`:""}`,color:Ue.quit});break;case"part":t.push({text:`← ${e} has left${n?` (${n})`:""}`,color:Ue.sys});break;case"join":t.push({text:`→ ${e} has joined`,color:Ue.join});break;case"kick":{const i=s.reason||n;t.push({text:`⚠ ${s.target?`${s.target} was kicked by ${e}`:`${e} kicked someone`}${i?` (${i})`:""}`,color:Ue.quit})}break;default:t.push({text:`*** ${n||e}`,color:Ue.sys})}return t}const Wc=16,ts=16,Ko=50;class Cf extends fn{constructor(e,n){super(e,n);R(this,"laidOut",[]);R(this,"seen",-1);this.fps=15}layout(){const e=this.env.feeds.irc.value;e.version!==this.seen&&(this.seen=e.version,this.ctx.font=`12.5px ${Zt}`,this.laidOut=e.lines.map(n=>({rows:this.wrapChunks(Af(n),It-ts*2),at:n.at})))}wrapChunks(e,n){var a;const i=this.ctx,r=[[]];let o=0;for(const l of e)for(const c of l.text.split(/(\s+)/)){if(!c)continue;const u=i.measureText(c).width;o+u>n&&o>0&&c.trim()&&(r.push([{text:" ",color:l.color}]),o=i.measureText(" ").width),(a=r[r.length-1])==null||a.push({text:c,color:l.color}),o+=u}return r}draw(e){this.layout();const n=this.ctx,i=this.env.feeds.irc.value;this.clear(Ue.bg),n.fillStyle=Ue.screen,n.fillRect(6,6,It-12,Wt-12),n.fillStyle="rgba(88,166,255,0.1)",n.fillRect(6,6,It-12,28),n.font=`13px ${Zt}`,n.fillStyle="#58a6ff",n.fillText("#aethera",ts,25),n.fillStyle=Ue.dim,n.font=`11px ${Zt}`,n.textAlign="right";const r=i.connected?Math.sin(e*3)>-.3?"● live":"○ live":"○ connecting…";n.fillStyle=i.connected?Ue.join:Ue.dim,n.fillText(r,It-ts,25),n.textAlign="left";const o=i.collapse?Math.min(1,(performance.now()-i.collapse.at)/1500):0;n.font=`12.5px ${Zt}`;const a=[],l=performance.now();for(const h of this.laidOut)for(const d of h.rows)a.push({chunks:d,fresh:Math.max(0,1-(l-h.at)/600)});const c=Math.floor((Wt-Ko-12)/Wc),u=a.slice(-c);u.length||(n.fillStyle=Ue.sys,n.fillText(i.connected?"*** the channel is quiet between fragments":"*** connecting to #aethera…",ts,Ko+12)),u.forEach((h,d)=>{let f=ts;const p=Ko+12+d*Wc,v=o?Math.sin(p*.3+e*40)*8*o*(Math.random()<.3?1:0):0;for(const m of h.chunks)n.fillStyle=o>.2&&Math.random()0){this.hop.k+=e/.16,this.hop.k>=1&&(this.hop.k=0,this.hop.left--,this.pos[this.hop.seat]=((this.pos[this.hop.seat]??0)+1)%40,this.hop.left===0&&this.land(this.hop.seat));return}if(this.wait-=e,this.wait>0)return;const n=this.turn%oi.length,i=1+Math.floor(this.rand()*6),r=1+Math.floor(this.rand()*6);this.dice=[i,r],this.say(`${(o=oi[n])==null?void 0:o.name} rolls ${i} and ${r}.`),this.hop={seat:n,from:this.pos[n]??0,left:i+r,k:0},this.turn++,this.wait=1.6}land(e){var c,u;const n=this.pos[e]??0,[i,r,o]=$c[n]??["",null,0],a=((c=oi[e])==null?void 0:c.name)??"",l=this.owner.get(n);if(o&&l===void 0&&this.rand()<.75)this.owner.set(n,e),this.say(`${a} buys ${i} for ₯${o}.`);else if(l!==void 0&&l!==e){const h=Math.max(2,Math.round(o/(r?12:8)));this.say(`${a} pays ₯${h} to ${(u=oi[l])==null?void 0:u.name} for ${i}.`)}else i==="Moira"||i==="Grammateion"?this.say(`${a} draws from ${i}.`):i==="Kerux"&&(this.say(`${a} is sent to the Desmoterion.`),this.pos[e]=10);this.owner.size>22&&this.owner.clear()}draw(e,n){var c;this.step(Math.min(n,.2));const i=this.ctx,r=i.createRadialGradient(It/2,Jo,40,It/2,Jo,It*.7);r.addColorStop(0,"#2a241f"),r.addColorStop(1,"#141210"),i.fillStyle=r,i.fillRect(0,0,It,Wt);const o=[pn(-.4,-.4),pn(11.4,-.4),pn(11.4,11.4),pn(-.4,11.4)];i.fillStyle="rgba(0,0,0,0.45)",i.beginPath(),o.forEach(([u,h],d)=>d?i.lineTo(u,h+8):i.moveTo(u,h+8)),i.fill(),i.fillStyle="#d9ccb2",i.beginPath(),o.forEach(([u,h],d)=>d?i.lineTo(u,h):i.moveTo(u,h)),i.fill();for(let u=0;u<40;u++){const[h,d]=Zo(u),[,f]=$c[u]??["",null,0],p=[pn(h,d),pn(h+1,d),pn(h+1,d+1),pn(h,d+1)];i.fillStyle=f?Rf[f]??"#efe4cf":u%10===0?"#e6d6b6":"#efe4cf",i.strokeStyle="#7a6a52",i.lineWidth=.7,i.beginPath(),p.forEach(([m,g],S)=>S?i.lineTo(m,g):i.moveTo(m,g)),i.closePath(),i.fill(),i.stroke();const v=this.owner.get(u);if(v!==void 0){const[m,g]=pn(h+.5,d+.5);i.fillStyle=((c=oi[v])==null?void 0:c.color)??"#fff",i.fillRect(m-2,g-5,4,5)}}const[a,l]=pn(5.5,5.5);i.fillStyle="#7a6a52",i.font=`15px ${Zt}`,this.spaced("KLEROS",a,l+5,5,"center"),oi.forEach((u,h)=>{let d=this.pos[h]??0,f=0,[p,v]=Zo(d);if(this.hop.left>0&&this.hop.seat===h){const[E,y]=Zo((d+1)%40);p+=(E-p)*this.hop.k,v+=(y-v)*this.hop.k,f=Math.sin(this.hop.k*Math.PI)*9,d=-1}const m=[[.3,.3],[.7,.3],[.3,.7],[.7,.7]][h]??[.5,.5],[g,S]=pn(p+(m[0]??.5),v+(m[1]??.5));i.fillStyle="rgba(0,0,0,0.35)",i.beginPath(),i.ellipse(g,S+1,5,2.5,0,0,Math.PI*2),i.fill(),i.fillStyle=u.color,i.beginPath(),i.arc(g,S-5-f,4.6,0,Math.PI*2),i.fill(),i.strokeStyle="rgba(0,0,0,0.5)",i.stroke()}),this.dice.forEach((u,h)=>this.die(It-70+h*30,22,u,e)),i.font=`11px ${Zt}`,oi.forEach((u,h)=>{const d=26+h*16;i.fillStyle=u.color,i.fillRect(16,d-8,8,8),i.fillStyle=(this.turn-1)%oi.length===h?"#f5ecd9":"#8c826f",i.fillText(`${u.name}${u.model?" ◆ model":""}`,30,d)}),i.fillStyle="rgba(10,8,6,0.75)",i.fillRect(0,Wt-62,It,62),i.font=`12px ${Zt}`,this.log.forEach((u,h)=>{i.fillStyle=h===this.log.length-1?"#f1e6cc":"#8a7f6a",i.fillText(u,16,Wt-42+h*16)})}die(e,n,i,r){const o=this.ctx,a=this.hop.left>0?Math.sin(r*30)*.15:0;o.save(),o.translate(e,n),o.rotate(a),o.fillStyle="#efe4cf",o.fillRect(-10,-10,20,20),o.fillStyle="#3a2f24";const l={1:[[0,0]],2:[[-5,-5],[5,5]],3:[[-5,-5],[0,0],[5,5]],4:[[-5,-5],[5,-5],[-5,5],[5,5]],5:[[-5,-5],[5,-5],[0,0],[-5,5],[5,5]],6:[[-5,-5],[5,-5],[-5,0],[5,0],[-5,5],[5,5]]};for(const[c,u]of l[i]??[])o.beginPath(),o.arc(c,u,1.8,0,Math.PI*2),o.fill();o.restore()}}class Df extends fn{constructor(e,n){super(e,n);R(this,"body");R(this,"creatureRef");R(this,"stars");R(this,"breaths",[]);this.fps=24;const i=Ci(33);this.stars=Array.from({length:90},()=>({x:i()*It,y:i()*Wt,a:i()*.5+.1})),this.body=Yc()}sync(){const e=this.env.feeds.creature.value;e!==this.creatureRef&&(this.creatureRef=e,this.body=e?If(e):Yc(),this.breaths=Array.from({length:Math.min(4,this.body.edges.length)},(n,i)=>({edge:i*7%Math.max(1,this.body.edges.length),k:0,fwd:!0})))}draw(e,n){this.sync();const i=this.ctx,r=this.body,o=i.createLinearGradient(0,0,0,Wt);o.addColorStop(0,"#070b14"),o.addColorStop(1,"#02030a"),i.fillStyle=o,i.fillRect(0,0,It,Wt);for(const p of this.stars)i.fillStyle=`rgba(200,215,255,${p.a*(.7+.3*Math.sin(e+p.x))})`,i.fillRect(p.x,p.y,1,1);const a=r.born?1.4:.9,l=Math.min(n,.1),c=r.nodes.map((p,v)=>{var S;const m=r.adj[v]??[];let g=0;for(const E of m)g+=Math.sin((((S=r.nodes[E])==null?void 0:S.phase)??0)-p.phase);return p.phase+(p.omega+(m.length?a*g/m.length:0))*l});r.nodes.forEach((p,v)=>{const m=c[v]??p.phase;Math.floor(m/(Math.PI*2))>Math.floor(p.phase/(Math.PI*2))&&(p.flash=1),p.phase=m,p.flash=Math.max(0,p.flash-l*2.2)});const u=e*.18,h=Math.cos(u),d=Math.sin(u),f=r.nodes.map((p,v)=>{const m=Math.sin(e*.9+v)*.02,g=p.x*h-p.z*d,E=2.8/(3.6-(p.x*d+p.z*h));return{x:It/2+g*E*150,y:176+(p.y+m)*E*150,k:E}});i.lineCap="round";for(const p of r.edges){const v=f[p.a],m=f[p.b];!v||!m||(i.strokeStyle=p.elder?"rgba(160,200,235,0.55)":"rgba(140,170,210,0.28)",i.lineWidth=p.elder?1.4:.8,i.beginPath(),i.moveTo(v.x,v.y),i.lineTo(m.x,m.y),i.stroke())}for(const p of this.breaths){const v=r.edges[p.edge];if(!v)continue;if(p.k+=l/.75,p.k>=1){const M=p.fwd?v.b:v.a,w=(r.adj[M]??[]).map(T=>r.edges.findIndex(L=>L.a===M&&L.b===T||L.b===M&&L.a===T)),C=w[Math.floor(Math.random()*w.length)]??p.edge,x=r.edges[C];p.edge=C,p.fwd=x?x.a===M:!0,p.k=0;continue}const m=f[p.fwd?v.a:v.b],g=f[p.fwd?v.b:v.a];if(!m||!g)continue;const S=m.x+(g.x-m.x)*p.k,E=m.y+(g.y-m.y)*p.k,y=i.createRadialGradient(S,E,0,S,E,9);y.addColorStop(0,"rgba(235,245,255,0.95)"),y.addColorStop(1,"rgba(180,220,255,0)"),i.fillStyle=y,i.fillRect(S-9,E-9,18,18)}r.nodes.forEach((p,v)=>{const m=f[v];if(!m)return;const g=p.flash,S=2+m.k*1.2+g*3,E=g>.05?`rgba(255,${Math.round(210-g*40)},${Math.round(120-g*60)},${.6+g*.4})`:"rgba(200,225,255,0.85)";i.strokeStyle=E,i.lineWidth=.8,i.beginPath(),i.moveTo(m.x-S*3,m.y),i.lineTo(m.x+S*3,m.y),i.moveTo(m.x,m.y-S*3),i.lineTo(m.x,m.y+S*3),i.stroke();const y=i.createRadialGradient(m.x,m.y,0,m.x,m.y,S*2.4);y.addColorStop(0,E),y.addColorStop(1,"rgba(0,0,0,0)"),i.fillStyle=y,i.fillRect(m.x-S*3,m.y-S*3,S*6,S*6)}),i.font=`15px ${Zt}`,i.fillStyle=r.born?"#e6f1ff":"#8190a8",i.fillText(r.name,18,Wt-40),i.font=`11px ${Zt}`,i.fillStyle="#6f7f99",i.fillText(r.age,18,Wt-20),i.textAlign="right",i.fillStyle="#4b5870",i.fillText(r.born?`${r.nodes.length} nodes · ${r.edges.length} strings`:"click to wake it",It-18,Wt-20),i.textAlign="left"}}function qc(s,t){var n,i;const e=Array.from({length:s},()=>[]);for(const r of t)(n=e[r.a])==null||n.push(r.b),(i=e[r.b])==null||i.push(r.a);return e}function If(s){const t=new Map(s.nodes.map((c,u)=>[c.id,u])),e=s.nodes.reduce((c,u)=>c+u.x,0)/s.nodes.length,n=s.nodes.reduce((c,u)=>c+u.y,0)/s.nodes.length,i=s.nodes.reduce((c,u)=>c+u.z,0)/s.nodes.length,r=Math.max(1,...s.nodes.map(c=>Math.hypot(c.x-e,c.y-n,c.z-i))),o=s.nodes.map((c,u)=>({x:(c.x-e)/r,y:(c.y-n)/r,z:(c.z-i)/r,phase:u*1.3,omega:2.4+u%5*.21,flash:0})),a=s.edges.map(c=>({a:t.get(c.a)??-1,b:t.get(c.b)??-1,elder:c.age>150})).filter(c=>c.a>=0&&c.b>=0),l=Math.max(s.lifetime,(Date.now()-s.bornAt)/1e3);return{name:s.name,age:`${Nf(l)} old · yours`,born:!0,nodes:o,edges:a,adj:qc(o.length,a)}}function Yc(){const s=Ci(9),t=9,e=Array.from({length:t},(i,r)=>{const o=r/t*Math.PI*2;return{x:Math.cos(o)*.8+(s()-.5)*.3,y:(s()-.5)*.9,z:Math.sin(o)*.8,phase:s()*6,omega:2.2+s()*.8,flash:0}}),n=[];for(let i=0;i0?`${e}d ${n}h`:n>0?`${n}h ${i}m`:`${i}m`}const es=new Image;es.src="/static/uploads/aethera_trimmed.png";class Uf extends fn{constructor(e,n){super(e,n);R(this,"specks");this.fps=12;const i=Ci(7);this.specks=Array.from({length:70},()=>({x:i()*It,y:i()*Wt,r:i()*1.2+.3,red:i()<.35,v:i()*4+1}))}draw(e){const n=this.ctx;this.clear("#000");for(const c of this.specks){const u=(c.y-e*c.v+Wt)%Wt;n.fillStyle=c.red?"rgba(170,40,50,0.55)":"rgba(255,255,255,0.28)",n.fillRect(c.x,u,c.r,c.r)}if(es.complete&&es.naturalWidth){const u=210*es.naturalHeight/es.naturalWidth;n.globalAlpha=.92+.08*Math.sin(e*1.3),n.drawImage(es,(It-210)/2,26,210,u),n.globalAlpha=1}n.fillStyle="#7d7d7d",n.font=`13px ${Zt}`,this.spaced("transmissions",It/2,118,3,"center"),n.fillStyle="#262626",n.fillRect(40,132,It-80,1);const i=this.env.dir.posts.slice(0,6);if(!i.length){n.fillStyle="#9a9a9a",n.font=`16px ${Zt}`,n.textAlign="center",n.fillText(`no transmissions yet${Math.floor(e*2)%2?"_":" "}`,It/2,230),n.textAlign="left";return}const r=3.6,o=Math.floor(e/r)%i.length,a=e%r/r;i.forEach((c,u)=>{const h=162+u*29,d=u===o;n.font=`11px ${Zt}`,n.fillStyle=d?"#bdbdbd":"#555",n.fillText(c.date,46,h),n.font=`16px ${Bo}`,n.fillStyle=d?"#ffffff":"#9a9a9a";const f=c.title.length>38?`${c.title.slice(0,37)}…`:c.title;n.fillText(f,132,h),d&&this.star(28,h-5,e)});const l=i[o];if(l!=null&&l.excerpt){n.font=`italic 12px ${Zt}`,n.fillStyle="#8a8a8a";const c=Math.floor(Math.min(1,a*1.6)*l.excerpt.length);this.wrap(l.excerpt.slice(0,c),It-92,2).forEach((h,d)=>n.fillText(h,46,344+d*16))}}star(e,n,i){const r=this.ctx,o=7+Math.sin(i*5)*1.2;r.save(),r.translate(e,n),r.rotate(i*.8),r.fillStyle="#fff",r.shadowColor="#fff",r.shadowBlur=10,r.beginPath();for(let a=0;a<16;a++){const l=a/16*Math.PI*2,c=a%2?o*.28:a%4?o*.7:o;r.lineTo(Math.cos(l)*c,Math.sin(l)*c)}r.closePath(),r.fill(),r.restore()}}const Ff={transmissions:Uf,dreams:Mf,chronicle:vf,"dreams-api":wf,apeiron:mf,syrinx:Df,afterlife:cf,irc:Cf,parlor:Lf,dream_gen:yf};class Of extends fn{draw(t){const e=this.ctx,n=["#c0c0c0","#c0c000","#00c0c0","#00c000","#c000c0","#c00000","#0000c0"];n.forEach((i,r)=>{e.fillStyle=i,e.fillRect(r*It/n.length,0,It/n.length+1,Wt*.66)}),e.fillStyle="#111",e.fillRect(0,Wt*.66,It,Wt*.34),e.fillStyle="#fff",e.font=`22px ${Zt}`,e.textAlign="center",e.fillText(this.site.title,It/2,Wt*.82),e.font=`12px ${Zt}`,e.fillStyle=Math.floor(t)%2?"#888":"#555",e.fillText("no programme yet",It/2,Wt*.92),e.textAlign="left"}}function kf(s,t){const e=Ff[s.id]??Of;return new e(s,t)}const wr={dreams:{angle:0,r:4.9,y:.5,style:"tv",screenW:2,channel:1},transmissions:{angle:-27,r:4.5,y:.34,style:"beige",screenW:1.22,stand:!0,channel:2},chronicle:{angle:27,r:4.5,y:0,style:"black",screenW:1.3,channel:3},"dreams-api":{angle:27,r:4.5,y:0,style:"grey",screenW:.86,on:"chronicle",channel:4},apeiron:{angle:-54,r:4.2,y:0,style:"black",screenW:1.15,channel:5},irc:{angle:-54,r:4.2,y:0,style:"beige",screenW:.95,on:"apeiron",channel:6},syrinx:{angle:54,r:4.2,y:.28,style:"grey",screenW:1.08,channel:7},afterlife:{angle:78,r:3.9,y:0,style:"black",screenW:1.02,channel:10},dream_gen:{angle:-20,r:7,y:4.35,style:"grey",screenW:1.02,hang:!0,feeds:"dreams",channel:8},parlor:{angle:20,r:6.5,y:3.6,style:"beige",screenW:1.1,hang:!0,channel:9}};function Bf(s,t){const e=wr[s];if(e)return e;const n=Math.floor(t/2)+1;return{angle:(t%2?1:-1)*(68+n*10),r:5.4,y:0,style:"grey",screenW:1,channel:12+t}}/** * @license * Copyright 2010-2026 Three.js Authors * SPDX-License-Identifier: MIT - */const Ma="185",Hi={ROTATE:0,DOLLY:1,PAN:2},Gi={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},_d=0,sc=1,xd=2,sr=1,vd=2,As=3,jn=0,Ge=1,On=2,wn=0,Vi=1,rr=2,rc=3,ac=4,Md=5,wi=100,yd=101,Sd=102,bd=103,wd=104,Ed=200,Td=201,Ad=202,Cd=203,ya=204,Sa=205,Rd=206,Pd=207,Ld=208,Dd=209,Id=210,Nd=211,Ud=212,Fd=213,Od=214,ba=0,wa=1,Ea=2,Wi=3,Ta=4,Aa=5,Ca=6,Ra=7,oc=0,kd=1,Bd=2,En=0,Pa=1,La=2,Da=3,ar=4,Ia=5,Na=6,Ua=7,lc=300,Ei=301,$i=302,Fa=303,Oa=304,or=306,lr=1e3,kn=1001,ka=1002,Ne=1003,zd=1004,cr=1005,Ue=1006,Ba=1007,Ti=1008,Ze=1009,cc=1010,hc=1011,Cs=1012,za=1013,Tn=1014,un=1015,Je=1016,Ha=1017,Ga=1018,Rs=1020,uc=35902,dc=35899,fc=1021,pc=1022,dn=1023,Bn=1026,Ai=1027,Va=1028,Wa=1029,Ci=1030,$a=1031,Xa=1033,hr=33776,ur=33777,dr=33778,fr=33779,qa=35840,Ya=35841,Ka=35842,Za=35843,Ja=36196,Qa=37492,ja=37496,to=37488,eo=37489,pr=37490,no=37491,io=37808,so=37809,ro=37810,ao=37811,oo=37812,lo=37813,co=37814,ho=37815,uo=37816,fo=37817,po=37818,mo=37819,go=37820,_o=37821,xo=36492,vo=36494,Mo=36495,yo=36283,So=36284,mr=36285,bo=36286,Hd=3200,wo=0,Gd=1,ti="",Se="srgb",Ps="srgb-linear",gr="linear",ie="srgb",Xi=7680,mc=519,Vd=512,Wd=513,$d=514,Eo=515,Xd=516,qd=517,To=518,Yd=519,gc=35044,_c="300 es",An=2e3,Ls=2001;function Kd(s){for(let t=s.length-1;t>=0;--t)if(s[t]>=65535)return!0;return!1}function _r(s){return document.createElementNS("http://www.w3.org/1999/xhtml",s)}function Zd(){const s=_r("canvas");return s.style.display="block",s}const xc={};function vc(...s){const t="THREE."+s.shift();console.log(t,...s)}function Mc(s){const t=s[0];if(typeof t=="string"&&t.startsWith("TSL:")){const e=s[1];e&&e.isStackTrace?s[0]+=" "+e.getLocation():s[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return s}function Nt(...s){s=Mc(s);const t="THREE."+s.shift();{const e=s[0];e&&e.isStackTrace?console.warn(e.getError(t)):console.warn(t,...s)}}function Qt(...s){s=Mc(s);const t="THREE."+s.shift();{const e=s[0];e&&e.isStackTrace?console.error(e.getError(t)):console.error(t,...s)}}function qi(...s){const t=s.join(" ");t in xc||(xc[t]=!0,Nt(...s))}function Jd(s,t,e){return new Promise(function(n,i){function r(){switch(s.clientWaitSync(t,s.SYNC_FLUSH_COMMANDS_BIT,0)){case s.WAIT_FAILED:i();break;case s.TIMEOUT_EXPIRED:setTimeout(r,e);break;default:n()}}setTimeout(r,e)})}const Qd={[ba]:wa,[Ea]:Ca,[Ta]:Ra,[Wi]:Aa,[wa]:ba,[Ca]:Ea,[Ra]:Ta,[Aa]:Wi};class ei{addEventListener(t,e){this._listeners===void 0&&(this._listeners={});const n=this._listeners;n[t]===void 0&&(n[t]=[]),n[t].indexOf(e)===-1&&n[t].push(e)}hasEventListener(t,e){const n=this._listeners;return n===void 0?!1:n[t]!==void 0&&n[t].indexOf(e)!==-1}removeEventListener(t,e){const n=this._listeners;if(n===void 0)return;const i=n[t];if(i!==void 0){const r=i.indexOf(e);r!==-1&&i.splice(r,1)}}dispatchEvent(t){const e=this._listeners;if(e===void 0)return;const n=e[t.type];if(n!==void 0){t.target=this;const i=n.slice(0);for(let r=0,a=i.length;r>8&255]+Fe[s>>16&255]+Fe[s>>24&255]+"-"+Fe[t&255]+Fe[t>>8&255]+"-"+Fe[t>>16&15|64]+Fe[t>>24&255]+"-"+Fe[e&63|128]+Fe[e>>8&255]+"-"+Fe[e>>16&255]+Fe[e>>24&255]+Fe[n&255]+Fe[n>>8&255]+Fe[n>>16&255]+Fe[n>>24&255]).toLowerCase()}function $t(s,t,e){return Math.max(t,Math.min(e,s))}function Ao(s,t){return(s%t+t)%t}function jd(s,t,e,n,i){return n+(s-t)*(i-n)/(e-t)}function tf(s,t,e){return s!==t?(e-s)/(t-s):0}function Is(s,t,e){return(1-e)*s+e*t}function ef(s,t,e,n){return Is(s,t,1-Math.exp(-e*n))}function nf(s,t=1){return t-Math.abs(Ao(s,t*2)-t)}function sf(s,t,e){return s<=t?0:s>=e?1:(s=(s-t)/(e-t),s*s*(3-2*s))}function rf(s,t,e){return s<=t?0:s>=e?1:(s=(s-t)/(e-t),s*s*s*(s*(s*6-15)+10))}function af(s,t){return s+Math.floor(Math.random()*(t-s+1))}function of(s,t){return s+Math.random()*(t-s)}function lf(s){return s*(.5-Math.random())}function cf(s){s!==void 0&&(yc=s);let t=yc+=1831565813;return t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),((t^t>>>14)>>>0)/4294967296}function hf(s){return s*Ds}function uf(s){return s*Yi}function df(s){return(s&s-1)===0&&s!==0}function ff(s){return Math.pow(2,Math.ceil(Math.log(s)/Math.LN2))}function pf(s){return Math.pow(2,Math.floor(Math.log(s)/Math.LN2))}function mf(s,t,e,n,i){const r=Math.cos,a=Math.sin,o=r(e/2),l=a(e/2),c=r((t+n)/2),u=a((t+n)/2),h=r((t-n)/2),d=a((t-n)/2),f=r((n-t)/2),p=a((n-t)/2);switch(i){case"XYX":s.set(o*u,l*h,l*d,o*c);break;case"YZY":s.set(l*d,o*u,l*h,o*c);break;case"ZXZ":s.set(l*h,l*d,o*u,o*c);break;case"XZX":s.set(o*u,l*p,l*f,o*c);break;case"YXY":s.set(l*f,o*u,l*p,o*c);break;case"ZYZ":s.set(l*p,l*f,o*u,o*c);break;default:Nt("MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+i)}}function Zi(s,t){switch(t.constructor){case Float32Array:return s;case Uint32Array:return s/4294967295;case Uint16Array:return s/65535;case Uint8Array:return s/255;case Int32Array:return Math.max(s/2147483647,-1);case Int16Array:return Math.max(s/32767,-1);case Int8Array:return Math.max(s/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function Ve(s,t){switch(t.constructor){case Float32Array:return s;case Uint32Array:return Math.round(s*4294967295);case Uint16Array:return Math.round(s*65535);case Uint8Array:return Math.round(s*255);case Int32Array:return Math.round(s*2147483647);case Int16Array:return Math.round(s*32767);case Int8Array:return Math.round(s*127);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const xr={DEG2RAD:Ds,RAD2DEG:Yi,generateUUID:Ki,clamp:$t,euclideanModulo:Ao,mapLinear:jd,inverseLerp:tf,lerp:Is,damp:ef,pingpong:nf,smoothstep:sf,smootherstep:rf,randInt:af,randFloat:of,randFloatSpread:lf,seededRandom:cf,degToRad:hf,radToDeg:uf,isPowerOfTwo:df,ceilPowerOfTwo:ff,floorPowerOfTwo:pf,setQuaternionFromProperEuler:mf,normalize:Ve,denormalize:Zi},kl=class kl{constructor(t=0,e=0){this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("THREE.Vector2: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,n=this.y,i=t.elements;return this.x=i[0]*e+i[3]*n+i[6],this.y=i[1]*e+i[4]*n+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=$t(this.x,t.x,e.x),this.y=$t(this.y,t.y,e.y),this}clampScalar(t,e){return this.x=$t(this.x,t,e),this.y=$t(this.y,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar($t(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos($t(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y;return e*e+n*n}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const n=Math.cos(e),i=Math.sin(e),r=this.x-t.x,a=this.y-t.y;return this.x=r*n-a*i+t.x,this.y=r*i+a*n+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}};kl.prototype.isVector2=!0;let ht=kl;class Cn{constructor(t=0,e=0,n=0,i=1){this.isQuaternion=!0,this._x=t,this._y=e,this._z=n,this._w=i}static slerpFlat(t,e,n,i,r,a,o){let l=n[i+0],c=n[i+1],u=n[i+2],h=n[i+3],d=r[a+0],f=r[a+1],p=r[a+2],v=r[a+3];if(h!==v||l!==d||c!==f||u!==p){let m=l*d+c*f+u*p+h*v;m<0&&(d=-d,f=-f,p=-p,v=-v,m=-m);let g=1-o;if(m<.9995){const w=Math.acos(m),E=Math.sin(w);g=Math.sin(g*w)/E,o=Math.sin(o*w)/E,l=l*g+d*o,c=c*g+f*o,u=u*g+p*o,h=h*g+v*o}else{l=l*g+d*o,c=c*g+f*o,u=u*g+p*o,h=h*g+v*o;const w=1/Math.sqrt(l*l+c*c+u*u+h*h);l*=w,c*=w,u*=w,h*=w}}t[e]=l,t[e+1]=c,t[e+2]=u,t[e+3]=h}static multiplyQuaternionsFlat(t,e,n,i,r,a){const o=n[i],l=n[i+1],c=n[i+2],u=n[i+3],h=r[a],d=r[a+1],f=r[a+2],p=r[a+3];return t[e]=o*p+u*h+l*f-c*d,t[e+1]=l*p+u*d+c*h-o*f,t[e+2]=c*p+u*f+o*d-l*h,t[e+3]=u*p-o*h-l*d-c*f,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,n,i){return this._x=t,this._y=e,this._z=n,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e=!0){const n=t._x,i=t._y,r=t._z,a=t._order,o=Math.cos,l=Math.sin,c=o(n/2),u=o(i/2),h=o(r/2),d=l(n/2),f=l(i/2),p=l(r/2);switch(a){case"XYZ":this._x=d*u*h+c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h-d*f*p;break;case"YXZ":this._x=d*u*h+c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h+d*f*p;break;case"ZXY":this._x=d*u*h-c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h-d*f*p;break;case"ZYX":this._x=d*u*h-c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h+d*f*p;break;case"YZX":this._x=d*u*h+c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h-d*f*p;break;case"XZY":this._x=d*u*h-c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h+d*f*p;break;default:Nt("Quaternion: .setFromEuler() encountered an unknown order: "+a)}return e===!0&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const n=e/2,i=Math.sin(n);return this._x=t.x*i,this._y=t.y*i,this._z=t.z*i,this._w=Math.cos(n),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,n=e[0],i=e[4],r=e[8],a=e[1],o=e[5],l=e[9],c=e[2],u=e[6],h=e[10],d=n+o+h;if(d>0){const f=.5/Math.sqrt(d+1);this._w=.25/f,this._x=(u-l)*f,this._y=(r-c)*f,this._z=(a-i)*f}else if(n>o&&n>h){const f=2*Math.sqrt(1+n-o-h);this._w=(u-l)/f,this._x=.25*f,this._y=(i+a)/f,this._z=(r+c)/f}else if(o>h){const f=2*Math.sqrt(1+o-n-h);this._w=(r-c)/f,this._x=(i+a)/f,this._y=.25*f,this._z=(l+u)/f}else{const f=2*Math.sqrt(1+h-n-o);this._w=(a-i)/f,this._x=(r+c)/f,this._y=(l+u)/f,this._z=.25*f}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let n=t.dot(e)+1;return n<1e-8?(n=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=n):(this._x=0,this._y=-t.z,this._z=t.y,this._w=n)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=n),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs($t(this.dot(t),-1,1)))}rotateTowards(t,e){const n=this.angleTo(t);if(n===0)return this;const i=Math.min(1,e/n);return this.slerp(t,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return t===0?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t){return this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const n=t._x,i=t._y,r=t._z,a=t._w,o=e._x,l=e._y,c=e._z,u=e._w;return this._x=n*u+a*o+i*c-r*l,this._y=i*u+a*l+r*o-n*c,this._z=r*u+a*c+n*l-i*o,this._w=a*u-n*o-i*l-r*c,this._onChangeCallback(),this}slerp(t,e){let n=t._x,i=t._y,r=t._z,a=t._w,o=this.dot(t);o<0&&(n=-n,i=-i,r=-r,a=-a,o=-o);let l=1-e;if(o<.9995){const c=Math.acos(o),u=Math.sin(c);l=Math.sin(l*c)/u,e=Math.sin(e*c)/u,this._x=this._x*l+n*e,this._y=this._y*l+i*e,this._z=this._z*l+r*e,this._w=this._w*l+a*e,this._onChangeCallback()}else this._x=this._x*l+n*e,this._y=this._y*l+i*e,this._z=this._z*l+r*e,this._w=this._w*l+a*e,this.normalize();return this}slerpQuaternions(t,e,n){return this.copy(t).slerp(e,n)}random(){const t=2*Math.PI*Math.random(),e=2*Math.PI*Math.random(),n=Math.random(),i=Math.sqrt(1-n),r=Math.sqrt(n);return this.set(i*Math.sin(t),i*Math.cos(t),r*Math.sin(e),r*Math.cos(e))}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}const Bl=class Bl{constructor(t=0,e=0,n=0){this.x=t,this.y=e,this.z=n}set(t,e,n){return n===void 0&&(n=this.z),this.x=t,this.y=e,this.z=n,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("THREE.Vector3: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return this.applyQuaternion(Sc.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(Sc.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[3]*n+r[6]*i,this.y=r[1]*e+r[4]*n+r[7]*i,this.z=r[2]*e+r[5]*n+r[8]*i,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=t.elements,a=1/(r[3]*e+r[7]*n+r[11]*i+r[15]);return this.x=(r[0]*e+r[4]*n+r[8]*i+r[12])*a,this.y=(r[1]*e+r[5]*n+r[9]*i+r[13])*a,this.z=(r[2]*e+r[6]*n+r[10]*i+r[14])*a,this}applyQuaternion(t){const e=this.x,n=this.y,i=this.z,r=t.x,a=t.y,o=t.z,l=t.w,c=2*(a*i-o*n),u=2*(o*e-r*i),h=2*(r*n-a*e);return this.x=e+l*c+a*h-o*u,this.y=n+l*u+o*c-r*h,this.z=i+l*h+r*u-a*c,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[4]*n+r[8]*i,this.y=r[1]*e+r[5]*n+r[9]*i,this.z=r[2]*e+r[6]*n+r[10]*i,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=$t(this.x,t.x,e.x),this.y=$t(this.y,t.y,e.y),this.z=$t(this.z,t.z,e.z),this}clampScalar(t,e){return this.x=$t(this.x,t,e),this.y=$t(this.y,t,e),this.z=$t(this.z,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar($t(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this}cross(t){return this.crossVectors(this,t)}crossVectors(t,e){const n=t.x,i=t.y,r=t.z,a=e.x,o=e.y,l=e.z;return this.x=i*l-r*o,this.y=r*a-n*l,this.z=n*o-i*a,this}projectOnVector(t){const e=t.lengthSq();if(e===0)return this.set(0,0,0);const n=t.dot(this)/e;return this.copy(t).multiplyScalar(n)}projectOnPlane(t){return Co.copy(this).projectOnVector(t),this.sub(Co)}reflect(t){return this.sub(Co.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos($t(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y,i=this.z-t.z;return e*e+n*n+i*i}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,n){const i=Math.sin(e)*t;return this.x=i*Math.sin(n),this.y=Math.cos(e)*t,this.z=i*Math.cos(n),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,n){return this.x=t*Math.sin(e),this.y=n,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),n=this.setFromMatrixColumn(t,1).length(),i=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=n,this.z=i,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,e*4)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,e*3)}setFromEuler(t){return this.x=t._x,this.y=t._y,this.z=t._z,this}setFromColor(t){return this.x=t.r,this.y=t.g,this.z=t.b,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const t=Math.random()*Math.PI*2,e=Math.random()*2-1,n=Math.sqrt(1-e*e);return this.x=n*Math.cos(t),this.y=e,this.z=n*Math.sin(t),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}};Bl.prototype.isVector3=!0;let C=Bl;const Co=new C,Sc=new Cn,zl=class zl{constructor(t,e,n,i,r,a,o,l,c){this.elements=[1,0,0,0,1,0,0,0,1],t!==void 0&&this.set(t,e,n,i,r,a,o,l,c)}set(t,e,n,i,r,a,o,l,c){const u=this.elements;return u[0]=t,u[1]=i,u[2]=o,u[3]=e,u[4]=r,u[5]=l,u[6]=n,u[7]=a,u[8]=c,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],this}extractBasis(t,e,n){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,i=e.elements,r=this.elements,a=n[0],o=n[3],l=n[6],c=n[1],u=n[4],h=n[7],d=n[2],f=n[5],p=n[8],v=i[0],m=i[3],g=i[6],w=i[1],E=i[4],M=i[7],S=i[2],b=i[5],R=i[8];return r[0]=a*v+o*w+l*S,r[3]=a*m+o*E+l*b,r[6]=a*g+o*M+l*R,r[1]=c*v+u*w+h*S,r[4]=c*m+u*E+h*b,r[7]=c*g+u*M+h*R,r[2]=d*v+f*w+p*S,r[5]=d*m+f*E+p*b,r[8]=d*g+f*M+p*R,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],a=t[4],o=t[5],l=t[6],c=t[7],u=t[8];return e*a*u-e*o*c-n*r*u+n*o*l+i*r*c-i*a*l}invert(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],a=t[4],o=t[5],l=t[6],c=t[7],u=t[8],h=u*a-o*c,d=o*l-u*r,f=c*r-a*l,p=e*h+n*d+i*f;if(p===0)return this.set(0,0,0,0,0,0,0,0,0);const v=1/p;return t[0]=h*v,t[1]=(i*c-u*n)*v,t[2]=(o*n-i*a)*v,t[3]=d*v,t[4]=(u*e-i*l)*v,t[5]=(i*r-o*e)*v,t[6]=f*v,t[7]=(n*l-c*e)*v,t[8]=(a*e-n*r)*v,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,n,i,r,a,o){const l=Math.cos(r),c=Math.sin(r);return this.set(n*l,n*c,-n*(l*a+c*o)+a+t,-i*c,i*l,-i*(-c*a+l*o)+o+e,0,0,1),this}scale(t,e){return qi("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(Ro.makeScale(t,e)),this}rotate(t){return qi("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(Ro.makeRotation(-t)),this}translate(t,e){return qi("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(Ro.makeTranslation(t,e)),this}makeTranslation(t,e){return t.isVector2?this.set(1,0,t.x,0,1,t.y,0,0,1):this.set(1,0,t,0,1,e,0,0,1),this}makeRotation(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,n,e,0,0,0,1),this}makeScale(t,e){return this.set(t,0,0,0,e,0,0,0,1),this}equals(t){const e=this.elements,n=t.elements;for(let i=0;i<9;i++)if(e[i]!==n[i])return!1;return!0}fromArray(t,e=0){for(let n=0;n<9;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t}clone(){return new this.constructor().fromArray(this.elements)}};zl.prototype.isMatrix3=!0;let zt=zl;const Ro=new zt,bc=new zt().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),wc=new zt().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function gf(){const s={enabled:!0,workingColorSpace:Ps,spaces:{},convert:function(i,r,a){return this.enabled===!1||r===a||!r||!a||(this.spaces[r].transfer===ie&&(i.r=zn(i.r),i.g=zn(i.g),i.b=zn(i.b)),this.spaces[r].primaries!==this.spaces[a].primaries&&(i.applyMatrix3(this.spaces[r].toXYZ),i.applyMatrix3(this.spaces[a].fromXYZ)),this.spaces[a].transfer===ie&&(i.r=Ji(i.r),i.g=Ji(i.g),i.b=Ji(i.b))),i},workingToColorSpace:function(i,r){return this.convert(i,this.workingColorSpace,r)},colorSpaceToWorking:function(i,r){return this.convert(i,r,this.workingColorSpace)},getPrimaries:function(i){return this.spaces[i].primaries},getTransfer:function(i){return i===ti?gr:this.spaces[i].transfer},getToneMappingMode:function(i){return this.spaces[i].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(i,r=this.workingColorSpace){return i.fromArray(this.spaces[r].luminanceCoefficients)},define:function(i){Object.assign(this.spaces,i)},_getMatrix:function(i,r,a){return i.copy(this.spaces[r].toXYZ).multiply(this.spaces[a].fromXYZ)},_getDrawingBufferColorSpace:function(i){return this.spaces[i].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(i=this.workingColorSpace){return this.spaces[i].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(i,r){return qi("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),s.workingToColorSpace(i,r)},toWorkingColorSpace:function(i,r){return qi("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),s.colorSpaceToWorking(i,r)}},t=[.64,.33,.3,.6,.15,.06],e=[.2126,.7152,.0722],n=[.3127,.329];return s.define({[Ps]:{primaries:t,whitePoint:n,transfer:gr,toXYZ:bc,fromXYZ:wc,luminanceCoefficients:e,workingColorSpaceConfig:{unpackColorSpace:Se},outputColorSpaceConfig:{drawingBufferColorSpace:Se}},[Se]:{primaries:t,whitePoint:n,transfer:ie,toXYZ:bc,fromXYZ:wc,luminanceCoefficients:e,outputColorSpaceConfig:{drawingBufferColorSpace:Se}}}),s}const Kt=gf();function zn(s){return s<.04045?s*.0773993808:Math.pow(s*.9478672986+.0521327014,2.4)}function Ji(s){return s<.0031308?s*12.92:1.055*Math.pow(s,.41666)-.055}let Qi;class _f{static getDataURL(t,e="image/png"){if(/^data:/i.test(t.src)||typeof HTMLCanvasElement>"u")return t.src;let n;if(t instanceof HTMLCanvasElement)n=t;else{Qi===void 0&&(Qi=_r("canvas")),Qi.width=t.width,Qi.height=t.height;const i=Qi.getContext("2d");t instanceof ImageData?i.putImageData(t,0,0):i.drawImage(t,0,0,t.width,t.height),n=Qi}return n.toDataURL(e)}static sRGBToLinear(t){if(typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap){const e=_r("canvas");e.width=t.width,e.height=t.height;const n=e.getContext("2d");n.drawImage(t,0,0,t.width,t.height);const i=n.getImageData(0,0,t.width,t.height),r=i.data;for(let a=0;a1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(Do).x}get height(){return this.source.getSize(Do).y}get depth(){return this.source.getSize(Do).z}get image(){return this.source.data}set image(t){this.source.data=t}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return new this.constructor().copy(this)}copy(t){return this.name=t.name,this.source=t.source,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.channel=t.channel,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.normalized=t.normalized,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.colorSpace=t.colorSpace,this.renderTarget=t.renderTarget,this.isRenderTargetTexture=t.isRenderTargetTexture,this.isArrayTexture=t.isArrayTexture,this.userData=JSON.parse(JSON.stringify(t.userData)),this.needsUpdate=!0,this}setValues(t){for(const e in t){const n=t[e];if(n===void 0){Nt(`Texture.setValues(): parameter '${e}' has value of undefined.`);continue}const i=this[e];if(i===void 0){Nt(`Texture.setValues(): property '${e}' does not exist.`);continue}i&&n&&i.isVector2&&n.isVector2||i&&n&&i.isVector3&&n.isVector3||i&&n&&i.isMatrix3&&n.isMatrix3?i.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";if(!e&&t.textures[this.uuid]!==void 0)return t.textures[this.uuid];const n={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(t).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(n.userData=this.userData),e||(t.textures[this.uuid]=n),n}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(t){if(this.mapping!==lc)return t;if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case lr:t.x=t.x-Math.floor(t.x);break;case kn:t.x=t.x<0?0:1;break;case ka:Math.abs(Math.floor(t.x)%2)===1?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x);break}if(t.y<0||t.y>1)switch(this.wrapT){case lr:t.y=t.y-Math.floor(t.y);break;case kn:t.y=t.y<0?0:1;break;case ka:Math.abs(Math.floor(t.y)%2)===1?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y);break}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){t===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(t){t===!0&&this.pmremVersion++}}Oe.DEFAULT_IMAGE=null,Oe.DEFAULT_MAPPING=lc,Oe.DEFAULT_ANISOTROPY=1;const Hl=class Hl{constructor(t=0,e=0,n=0,i=1){this.x=t,this.y=e,this.z=n,this.w=i}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,n,i){return this.x=t,this.y=e,this.z=n,this.w=i,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("THREE.Vector4: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=t.w!==void 0?t.w:1,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=this.w,a=t.elements;return this.x=a[0]*e+a[4]*n+a[8]*i+a[12]*r,this.y=a[1]*e+a[5]*n+a[9]*i+a[13]*r,this.z=a[2]*e+a[6]*n+a[10]*i+a[14]*r,this.w=a[3]*e+a[7]*n+a[11]*i+a[15]*r,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,n,i,r;const l=t.elements,c=l[0],u=l[4],h=l[8],d=l[1],f=l[5],p=l[9],v=l[2],m=l[6],g=l[10];if(Math.abs(u-d)<.01&&Math.abs(h-v)<.01&&Math.abs(p-m)<.01){if(Math.abs(u+d)<.1&&Math.abs(h+v)<.1&&Math.abs(p+m)<.1&&Math.abs(c+f+g-3)<.1)return this.set(1,0,0,0),this;e=Math.PI;const E=(c+1)/2,M=(f+1)/2,S=(g+1)/2,b=(u+d)/4,R=(h+v)/4,x=(p+m)/4;return E>M&&E>S?E<.01?(n=0,i=.707106781,r=.707106781):(n=Math.sqrt(E),i=b/n,r=R/n):M>S?M<.01?(n=.707106781,i=0,r=.707106781):(i=Math.sqrt(M),n=b/i,r=x/i):S<.01?(n=.707106781,i=.707106781,r=0):(r=Math.sqrt(S),n=R/r,i=x/r),this.set(n,i,r,e),this}let w=Math.sqrt((m-p)*(m-p)+(h-v)*(h-v)+(d-u)*(d-u));return Math.abs(w)<.001&&(w=1),this.x=(m-p)/w,this.y=(h-v)/w,this.z=(d-u)/w,this.w=Math.acos((c+f+g-1)/2),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this.w=e[15],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this.w=Math.min(this.w,t.w),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this.w=Math.max(this.w,t.w),this}clamp(t,e){return this.x=$t(this.x,t.x,e.x),this.y=$t(this.y,t.y,e.y),this.z=$t(this.z,t.z,e.z),this.w=$t(this.w,t.w,e.w),this}clampScalar(t,e){return this.x=$t(this.x,t,e),this.y=$t(this.y,t,e),this.z=$t(this.z,t,e),this.w=$t(this.w,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar($t(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z+this.w*t.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this.w+=(t.w-this.w)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this.w=t.w+(e.w-t.w)*n,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z&&t.w===this.w}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this.w=t[e+3],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t[e+3]=this.w,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this.w=t.getW(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}};Hl.prototype.isVector4=!0;let me=Hl;class Mf extends ei{constructor(t=1,e=1,n={}){super(),n=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Ue,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,depth:1,multiview:!1,useArrayDepthTexture:!1},n),this.isRenderTarget=!0,this.width=t,this.height=e,this.depth=n.depth,this.scissor=new me(0,0,t,e),this.scissorTest=!1,this.viewport=new me(0,0,t,e),this.textures=[];const i={width:t,height:e,depth:n.depth},r=new Oe(i),a=n.count;for(let o=0;o1);this.dispose()}this.viewport.set(0,0,t,e),this.scissor.set(0,0,t,e)}clone(){return new this.constructor().copy(this)}copy(t){this.width=t.width,this.height=t.height,this.depth=t.depth,this.scissor.copy(t.scissor),this.scissorTest=t.scissorTest,this.viewport.copy(t.viewport),this.textures.length=0;for(let e=0,n=t.textures.length;e>>0}enable(t){this.mask|=1<1){for(let e=0;e1){for(let n=0;n0&&(i.userData=this.userData),i.layers=this.layers.mask,i.matrix=this.matrix.toArray(),i.up=this.up.toArray(),this.pivot!==null&&(i.pivot=this.pivot.toArray()),this.matrixAutoUpdate===!1&&(i.matrixAutoUpdate=!1),this.morphTargetDictionary!==void 0&&(i.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),this.morphTargetInfluences!==void 0&&(i.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(i.type="InstancedMesh",i.count=this.count,i.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(i.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(i.type="BatchedMesh",i.perObjectFrustumCulled=this.perObjectFrustumCulled,i.sortObjects=this.sortObjects,i.drawRanges=this._drawRanges,i.reservedRanges=this._reservedRanges,i.geometryInfo=this._geometryInfo.map(o=>({...o,boundingBox:o.boundingBox?o.boundingBox.toJSON():void 0,boundingSphere:o.boundingSphere?o.boundingSphere.toJSON():void 0})),i.instanceInfo=this._instanceInfo.map(o=>({...o})),i.availableInstanceIds=this._availableInstanceIds.slice(),i.availableGeometryIds=this._availableGeometryIds.slice(),i.nextIndexStart=this._nextIndexStart,i.nextVertexStart=this._nextVertexStart,i.geometryCount=this._geometryCount,i.maxInstanceCount=this._maxInstanceCount,i.maxVertexCount=this._maxVertexCount,i.maxIndexCount=this._maxIndexCount,i.geometryInitialized=this._geometryInitialized,i.matricesTexture=this._matricesTexture.toJSON(t),i.indirectTexture=this._indirectTexture.toJSON(t),this._colorsTexture!==null&&(i.colorsTexture=this._colorsTexture.toJSON(t)),this.boundingSphere!==null&&(i.boundingSphere=this.boundingSphere.toJSON()),this.boundingBox!==null&&(i.boundingBox=this.boundingBox.toJSON()));function r(o,l){return o[l.uuid]===void 0&&(o[l.uuid]=l.toJSON(t)),l.uuid}if(this.isScene)this.background&&(this.background.isColor?i.background=this.background.toJSON():this.background.isTexture&&(i.background=this.background.toJSON(t).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(i.environment=this.environment.toJSON(t).uuid);else if(this.isMesh||this.isLine||this.isPoints){i.geometry=r(t.geometries,this.geometry);const o=this.geometry.parameters;if(o!==void 0&&o.shapes!==void 0){const l=o.shapes;if(Array.isArray(l))for(let c=0,u=l.length;c0){i.children=[];for(let o=0;o0){i.animations=[];for(let o=0;o0&&(n.geometries=o),l.length>0&&(n.materials=l),c.length>0&&(n.textures=c),u.length>0&&(n.images=u),h.length>0&&(n.shapes=h),d.length>0&&(n.skeletons=d),f.length>0&&(n.animations=f),p.length>0&&(n.nodes=p)}return n.object=i,n;function a(o){const l=[];for(const c in o){const u=o[c];delete u.metadata,l.push(u)}return l}}clone(t){return new this.constructor().copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.pivot=t.pivot!==null?t.pivot.clone():null,this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldAutoUpdate=t.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.static=t.static,this.animations=t.animations.slice(),this.userData=JSON.parse(JSON.stringify(t.userData)),e===!0)for(let n=0;nf+p?(c.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!c.inputState.pinching&&d<=f-p&&(c.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else l!==null&&t.gripSpace&&(r=e.getPose(t.gripSpace,n),r!==null&&(l.matrix.fromArray(r.transform.matrix),l.matrix.decompose(l.position,l.rotation,l.scale),l.matrixWorldNeedsUpdate=!0,r.linearVelocity?(l.hasLinearVelocity=!0,l.linearVelocity.copy(r.linearVelocity)):l.hasLinearVelocity=!1,r.angularVelocity?(l.hasAngularVelocity=!0,l.angularVelocity.copy(r.angularVelocity)):l.hasAngularVelocity=!1,l.eventsEnabled&&l.dispatchEvent({type:"gripUpdated",data:t,target:this})));o!==null&&(i=e.getPose(t.targetRaySpace,n),i===null&&r!==null&&(i=r),i!==null&&(o.matrix.fromArray(i.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,i.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(i.linearVelocity)):o.hasLinearVelocity=!1,i.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(i.angularVelocity)):o.hasAngularVelocity=!1,this.dispatchEvent(Cf)))}return o!==null&&(o.visible=i!==null),l!==null&&(l.visible=r!==null),c!==null&&(c.visible=a!==null),this}_getHandJoint(t,e){if(t.joints[e.jointName]===void 0){const n=new pn;n.matrixAutoUpdate=!1,n.visible=!1,t.joints[e.jointName]=n,t.add(n)}return t.joints[e.jointName]}}const Ic={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},ii={h:0,s:0,l:0},yr={h:0,s:0,l:0};function Fo(s,t,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?s+(t-s)*6*e:e<1/2?t:e<2/3?s+(t-s)*6*(2/3-e):s}class kt{constructor(t,e,n){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(t,e,n)}set(t,e,n){if(e===void 0&&n===void 0){const i=t;i&&i.isColor?this.copy(i):typeof i=="number"?this.setHex(i):typeof i=="string"&&this.setStyle(i)}else this.setRGB(t,e,n);return this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t,e=Se){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(t&255)/255,Kt.colorSpaceToWorking(this,e),this}setRGB(t,e,n,i=Kt.workingColorSpace){return this.r=t,this.g=e,this.b=n,Kt.colorSpaceToWorking(this,i),this}setHSL(t,e,n,i=Kt.workingColorSpace){if(t=Ao(t,1),e=$t(e,0,1),n=$t(n,0,1),e===0)this.r=this.g=this.b=n;else{const r=n<=.5?n*(1+e):n+e-n*e,a=2*n-r;this.r=Fo(a,r,t+1/3),this.g=Fo(a,r,t),this.b=Fo(a,r,t-1/3)}return Kt.colorSpaceToWorking(this,i),this}setStyle(t,e=Se){function n(r){r!==void 0&&parseFloat(r)<1&&Nt("Color: Alpha component of "+t+" will be ignored.")}let i;if(i=/^(\w+)\(([^\)]*)\)/.exec(t)){let r;const a=i[1],o=i[2];switch(a){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setRGB(Math.min(255,parseInt(r[1],10))/255,Math.min(255,parseInt(r[2],10))/255,Math.min(255,parseInt(r[3],10))/255,e);if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setRGB(Math.min(100,parseInt(r[1],10))/100,Math.min(100,parseInt(r[2],10))/100,Math.min(100,parseInt(r[3],10))/100,e);break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setHSL(parseFloat(r[1])/360,parseFloat(r[2])/100,parseFloat(r[3])/100,e);break;default:Nt("Color: Unknown color model "+t)}}else if(i=/^\#([A-Fa-f\d]+)$/.exec(t)){const r=i[1],a=r.length;if(a===3)return this.setRGB(parseInt(r.charAt(0),16)/15,parseInt(r.charAt(1),16)/15,parseInt(r.charAt(2),16)/15,e);if(a===6)return this.setHex(parseInt(r,16),e);Nt("Color: Invalid hex color "+t)}else if(t&&t.length>0)return this.setColorName(t,e);return this}setColorName(t,e=Se){const n=Ic[t.toLowerCase()];return n!==void 0?this.setHex(n,e):Nt("Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copySRGBToLinear(t){return this.r=zn(t.r),this.g=zn(t.g),this.b=zn(t.b),this}copyLinearToSRGB(t){return this.r=Ji(t.r),this.g=Ji(t.g),this.b=Ji(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(t=Se){return Kt.workingToColorSpace(ke.copy(this),t),Math.round($t(ke.r*255,0,255))*65536+Math.round($t(ke.g*255,0,255))*256+Math.round($t(ke.b*255,0,255))}getHexString(t=Se){return("000000"+this.getHex(t).toString(16)).slice(-6)}getHSL(t,e=Kt.workingColorSpace){Kt.workingToColorSpace(ke.copy(this),e);const n=ke.r,i=ke.g,r=ke.b,a=Math.max(n,i,r),o=Math.min(n,i,r);let l,c;const u=(o+a)/2;if(o===a)l=0,c=0;else{const h=a-o;switch(c=u<=.5?h/(a+o):h/(2-a-o),a){case n:l=(i-r)/h+(i0&&(e.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(e.object.backgroundIntensity=this.backgroundIntensity),e.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(e.object.environmentIntensity=this.environmentIntensity),e.object.environmentRotation=this.environmentRotation.toArray(),e}}const mn=new C,Vn=new C,ko=new C,Wn=new C,ns=new C,is=new C,Nc=new C,Bo=new C,zo=new C,Ho=new C,Go=new me,Vo=new me,Wo=new me;class gn{constructor(t=new C,e=new C,n=new C){this.a=t,this.b=e,this.c=n}static getNormal(t,e,n,i){i.subVectors(n,e),mn.subVectors(t,e),i.cross(mn);const r=i.lengthSq();return r>0?i.multiplyScalar(1/Math.sqrt(r)):i.set(0,0,0)}static getBarycoord(t,e,n,i,r){mn.subVectors(i,e),Vn.subVectors(n,e),ko.subVectors(t,e);const a=mn.dot(mn),o=mn.dot(Vn),l=mn.dot(ko),c=Vn.dot(Vn),u=Vn.dot(ko),h=a*c-o*o;if(h===0)return r.set(0,0,0),null;const d=1/h,f=(c*l-o*u)*d,p=(a*u-o*l)*d;return r.set(1-f-p,p,f)}static containsPoint(t,e,n,i){return this.getBarycoord(t,e,n,i,Wn)===null?!1:Wn.x>=0&&Wn.y>=0&&Wn.x+Wn.y<=1}static getInterpolation(t,e,n,i,r,a,o,l){return this.getBarycoord(t,e,n,i,Wn)===null?(l.x=0,l.y=0,"z"in l&&(l.z=0),"w"in l&&(l.w=0),null):(l.setScalar(0),l.addScaledVector(r,Wn.x),l.addScaledVector(a,Wn.y),l.addScaledVector(o,Wn.z),l)}static getInterpolatedAttribute(t,e,n,i,r,a){return Go.setScalar(0),Vo.setScalar(0),Wo.setScalar(0),Go.fromBufferAttribute(t,e),Vo.fromBufferAttribute(t,n),Wo.fromBufferAttribute(t,i),a.setScalar(0),a.addScaledVector(Go,r.x),a.addScaledVector(Vo,r.y),a.addScaledVector(Wo,r.z),a}static isFrontFacing(t,e,n,i){return mn.subVectors(n,e),Vn.subVectors(t,e),mn.cross(Vn).dot(i)<0}set(t,e,n){return this.a.copy(t),this.b.copy(e),this.c.copy(n),this}setFromPointsAndIndices(t,e,n,i){return this.a.copy(t[e]),this.b.copy(t[n]),this.c.copy(t[i]),this}setFromAttributeAndIndices(t,e,n,i){return this.a.fromBufferAttribute(t,e),this.b.fromBufferAttribute(t,n),this.c.fromBufferAttribute(t,i),this}clone(){return new this.constructor().copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return mn.subVectors(this.c,this.b),Vn.subVectors(this.a,this.b),mn.cross(Vn).length()*.5}getMidpoint(t){return t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return gn.getNormal(this.a,this.b,this.c,t)}getPlane(t){return t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return gn.getBarycoord(t,this.a,this.b,this.c,e)}getInterpolation(t,e,n,i,r){return gn.getInterpolation(t,this.a,this.b,this.c,e,n,i,r)}containsPoint(t){return gn.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return gn.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){const n=this.a,i=this.b,r=this.c;let a,o;ns.subVectors(i,n),is.subVectors(r,n),Bo.subVectors(t,n);const l=ns.dot(Bo),c=is.dot(Bo);if(l<=0&&c<=0)return e.copy(n);zo.subVectors(t,i);const u=ns.dot(zo),h=is.dot(zo);if(u>=0&&h<=u)return e.copy(i);const d=l*h-u*c;if(d<=0&&l>=0&&u<=0)return a=l/(l-u),e.copy(n).addScaledVector(ns,a);Ho.subVectors(t,r);const f=ns.dot(Ho),p=is.dot(Ho);if(p>=0&&f<=p)return e.copy(r);const v=f*c-l*p;if(v<=0&&c>=0&&p<=0)return o=c/(c-p),e.copy(n).addScaledVector(is,o);const m=u*p-f*h;if(m<=0&&h-u>=0&&f-p>=0)return Nc.subVectors(r,i),o=(h-u)/(h-u+(f-p)),e.copy(i).addScaledVector(Nc,o);const g=1/(m+v+d);return a=v*g,o=d*g,e.copy(n).addScaledVector(ns,a).addScaledVector(is,o)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}class Ri{constructor(t=new C(1/0,1/0,1/0),e=new C(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=t,this.max=e}set(t,e){return this.min.copy(t),this.max.copy(e),this}setFromArray(t){this.makeEmpty();for(let e=0,n=t.length;e=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y&&t.z>=this.min.z&&t.z<=this.max.z}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y&&t.max.z>=this.min.z&&t.min.z<=this.max.z}intersectsSphere(t){return this.clampPoint(t.center,_n),_n.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,n;return t.normal.x>0?(e=t.normal.x*this.min.x,n=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,n=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,n+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,n+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,n+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,n+=t.normal.z*this.min.z),e<=-t.constant&&n>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(Us),br.subVectors(this.max,Us),ss.subVectors(t.a,Us),rs.subVectors(t.b,Us),as.subVectors(t.c,Us),si.subVectors(rs,ss),ri.subVectors(as,rs),Pi.subVectors(ss,as);let e=[0,-si.z,si.y,0,-ri.z,ri.y,0,-Pi.z,Pi.y,si.z,0,-si.x,ri.z,0,-ri.x,Pi.z,0,-Pi.x,-si.y,si.x,0,-ri.y,ri.x,0,-Pi.y,Pi.x,0];return!$o(e,ss,rs,as,br)||(e=[1,0,0,0,1,0,0,0,1],!$o(e,ss,rs,as,br))?!1:(wr.crossVectors(si,ri),e=[wr.x,wr.y,wr.z],$o(e,ss,rs,as,br))}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,_n).distanceTo(t)}getBoundingSphere(t){return this.isEmpty()?t.makeEmpty():(this.getCenter(t.center),t.radius=this.getSize(_n).length()*.5),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()?this:($n[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),$n[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),$n[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),$n[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),$n[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),$n[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),$n[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),$n[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints($n),this)}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(t){return this.min.fromArray(t.min),this.max.fromArray(t.max),this}}const $n=[new C,new C,new C,new C,new C,new C,new C,new C],_n=new C,Sr=new Ri,ss=new C,rs=new C,as=new C,si=new C,ri=new C,Pi=new C,Us=new C,br=new C,wr=new C,Li=new C;function $o(s,t,e,n,i){for(let r=0,a=s.length-3;r<=a;r+=3){Li.fromArray(s,r);const o=i.x*Math.abs(Li.x)+i.y*Math.abs(Li.y)+i.z*Math.abs(Li.z),l=t.dot(Li),c=e.dot(Li),u=n.dot(Li);if(Math.max(-Math.max(l,c,u),Math.min(l,c,u))>o)return!1}return!0}const we=new C,Er=new ht;let Pf=0;class rn extends ei{constructor(t,e,n=!1){if(super(),Array.isArray(t))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:Pf++}),this.name="",this.array=t,this.itemSize=e,this.count=t!==void 0?t.length/e:0,this.normalized=n,this.usage=gc,this.updateRanges=[],this.gpuType=un,this.version=0}onUploadCallback(){}set needsUpdate(t){t===!0&&this.version++}setUsage(t){return this.usage=t,this}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}copy(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this.gpuType=t.gpuType,this}copyAt(t,e,n){t*=this.itemSize,n*=e.itemSize;for(let i=0,r=this.itemSize;ithis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){if(this.isEmpty())return this.center.copy(t),this.radius=0,this;Fs.subVectors(t,this.center);const e=Fs.lengthSq();if(e>this.radius*this.radius){const n=Math.sqrt(e),i=(n-this.radius)*.5;this.center.addScaledVector(Fs,i/n),this.radius+=i}return this}union(t){return t.isEmpty()?this:this.isEmpty()?(this.copy(t),this):(this.center.equals(t.center)===!0?this.radius=Math.max(this.radius,t.radius):(Xo.subVectors(t.center,this.center).setLength(t.radius),this.expandByPoint(Fs.copy(t.center).add(Xo)),this.expandByPoint(Fs.copy(t.center).sub(Xo))),this)}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return new this.constructor().copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(t){return this.radius=t.radius,this.center.fromArray(t.center),this}}let Df=0;const an=new se,qo=new be,ls=new C,je=new Ri,Os=new Ri,De=new C;class Be extends ei{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:Df++}),this.uuid=Ki(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(t){return Array.isArray(t)?this.index=new(Kd(t)?Fc:Uc)(t,1):this.index=t,this}setIndirect(t,e=0){return this.indirect=t,this.indirectOffset=e,this}getIndirect(){return this.indirect}getAttribute(t){return this.attributes[t]}setAttribute(t,e){return this.attributes[t]=e,this}deleteAttribute(t){return delete this.attributes[t],this}hasAttribute(t){return this.attributes[t]!==void 0}addGroup(t,e,n=0){this.groups.push({start:t,count:e,materialIndex:n})}clearGroups(){this.groups=[]}setDrawRange(t,e){this.drawRange.start=t,this.drawRange.count=e}applyMatrix4(t){const e=this.attributes.position;e!==void 0&&(e.applyMatrix4(t),e.needsUpdate=!0);const n=this.attributes.normal;if(n!==void 0){const r=new zt().getNormalMatrix(t);n.applyNormalMatrix(r),n.needsUpdate=!0}const i=this.attributes.tangent;return i!==void 0&&(i.transformDirection(t),i.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(t){return an.makeRotationFromQuaternion(t),this.applyMatrix4(an),this}rotateX(t){return an.makeRotationX(t),this.applyMatrix4(an),this}rotateY(t){return an.makeRotationY(t),this.applyMatrix4(an),this}rotateZ(t){return an.makeRotationZ(t),this.applyMatrix4(an),this}translate(t,e,n){return an.makeTranslation(t,e,n),this.applyMatrix4(an),this}scale(t,e,n){return an.makeScale(t,e,n),this.applyMatrix4(an),this}lookAt(t){return qo.lookAt(t),qo.updateMatrix(),this.applyMatrix4(qo.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(ls).negate(),this.translate(ls.x,ls.y,ls.z),this}setFromPoints(t){const e=this.getAttribute("position");if(e===void 0){const n=[];for(let i=0,r=t.length;ie.count&&Nt("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),e.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new Ri);const t=this.attributes.position,e=this.morphAttributes.position;if(t&&t.isGLBufferAttribute){Qt("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new C(-1/0,-1/0,-1/0),new C(1/0,1/0,1/0));return}if(t!==void 0){if(this.boundingBox.setFromBufferAttribute(t),e)for(let n=0,i=e.length;n0&&(t.userData=this.userData),this.parameters!==void 0&&this._transformed!==!0){const l=this.parameters;for(const c in l)l[c]!==void 0&&(t[c]=l[c]);return t}t.data={attributes:{}};const e=this.index;e!==null&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const n=this.attributes;for(const l in n){const c=n[l];t.data.attributes[l]=c.toJSON(t.data)}const i={};let r=!1;for(const l in this.morphAttributes){const c=this.morphAttributes[l],u=[];for(let h=0,d=c.length;h0&&(i[l]=u,r=!0)}r&&(t.data.morphAttributes=i,t.data.morphTargetsRelative=this.morphTargetsRelative);const a=this.groups;a.length>0&&(t.data.groups=JSON.parse(JSON.stringify(a)));const o=this.boundingSphere;return o!==null&&(t.data.boundingSphere=o.toJSON()),t}clone(){return new this.constructor().copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const n=t.index;n!==null&&this.setIndex(n.clone());const i=t.attributes;for(const c in i){const u=i[c];this.setAttribute(c,u.clone(e))}const r=t.morphAttributes;for(const c in r){const u=[],h=r[c];for(let d=0,f=h.length;d0!=t>0&&this.version++,this._alphaTest=t}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(t){if(t!==void 0)for(const e in t){const n=t[e];if(n===void 0){Nt(`Material: parameter '${e}' has value of undefined.`);continue}const i=this[e];if(i===void 0){Nt(`Material: '${e}' is not a property of THREE.${this.type}.`);continue}i&&i.isColor?i.set(n):i&&i.isVector2&&n&&n.isVector2||i&&i.isEuler&&n&&n.isEuler||i&&i.isVector3&&n&&n.isVector3?i.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";e&&(t={textures:{},images:{}});const n={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};n.uuid=this.uuid,n.type=this.type,this.name!==""&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),this.roughness!==void 0&&(n.roughness=this.roughness),this.metalness!==void 0&&(n.metalness=this.metalness),this.sheen!==void 0&&(n.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(n.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(n.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(n.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(n.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(n.shininess=this.shininess),this.clearcoat!==void 0&&(n.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(n.sheenColorMap=this.sheenColorMap.toJSON(t).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(n.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(t).uuid),this.dispersion!==void 0&&(n.dispersion=this.dispersion),this.iridescence!==void 0&&(n.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(n.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(n.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(n.iridescenceMap=this.iridescenceMap.toJSON(t).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(n.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(t).uuid),this.anisotropy!==void 0&&(n.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(n.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(n.anisotropyMap=this.anisotropyMap.toJSON(t).uuid),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(t).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(t).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(t).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(t).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(t).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(t).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(n.specularIntensityMap=this.specularIntensityMap.toJSON(t).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(n.specularColorMap=this.specularColorMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(t).uuid,this.combine!==void 0&&(n.combine=this.combine)),this.envMapRotation!==void 0&&(n.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(n.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(n.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(n.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(t).uuid),this.transmission!==void 0&&(n.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(n.transmissionMap=this.transmissionMap.toJSON(t).uuid),this.thickness!==void 0&&(n.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(n.thicknessMap=this.thicknessMap.toJSON(t).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(n.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(n.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(n.size=this.size),this.shadowSide!==null&&(n.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==Vi&&(n.blending=this.blending),this.side!==jn&&(n.side=this.side),this.vertexColors===!0&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),this.transparent===!0&&(n.transparent=!0),this.blendSrc!==ya&&(n.blendSrc=this.blendSrc),this.blendDst!==Sa&&(n.blendDst=this.blendDst),this.blendEquation!==wi&&(n.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(n.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(n.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(n.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(n.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(n.blendAlpha=this.blendAlpha),this.depthFunc!==Wi&&(n.depthFunc=this.depthFunc),this.depthTest===!1&&(n.depthTest=this.depthTest),this.depthWrite===!1&&(n.depthWrite=this.depthWrite),this.colorWrite===!1&&(n.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(n.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==mc&&(n.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(n.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(n.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==Xi&&(n.stencilFail=this.stencilFail),this.stencilZFail!==Xi&&(n.stencilZFail=this.stencilZFail),this.stencilZPass!==Xi&&(n.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(n.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(n.rotation=this.rotation),this.polygonOffset===!0&&(n.polygonOffset=!0),this.polygonOffsetFactor!==0&&(n.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(n.linewidth=this.linewidth),this.dashSize!==void 0&&(n.dashSize=this.dashSize),this.gapSize!==void 0&&(n.gapSize=this.gapSize),this.scale!==void 0&&(n.scale=this.scale),this.dithering===!0&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),this.alphaHash===!0&&(n.alphaHash=!0),this.alphaToCoverage===!0&&(n.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(n.premultipliedAlpha=!0),this.forceSinglePass===!0&&(n.forceSinglePass=!0),this.allowOverride===!1&&(n.allowOverride=!1),this.wireframe===!0&&(n.wireframe=!0),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(n.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(n.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(n.flatShading=!0),this.visible===!1&&(n.visible=!1),this.toneMapped===!1&&(n.toneMapped=!1),this.fog===!1&&(n.fog=!1),Object.keys(this.userData).length>0&&(n.userData=this.userData);function i(r){const a=[];for(const o in r){const l=r[o];delete l.metadata,a.push(l)}return a}if(e){const r=i(t.textures),a=i(t.images);r.length>0&&(n.textures=r),a.length>0&&(n.images=a)}return n}fromJSON(t,e){if(t.uuid!==void 0&&(this.uuid=t.uuid),t.name!==void 0&&(this.name=t.name),t.color!==void 0&&this.color!==void 0&&this.color.setHex(t.color),t.roughness!==void 0&&(this.roughness=t.roughness),t.metalness!==void 0&&(this.metalness=t.metalness),t.sheen!==void 0&&(this.sheen=t.sheen),t.sheenColor!==void 0&&(this.sheenColor=new kt().setHex(t.sheenColor)),t.sheenRoughness!==void 0&&(this.sheenRoughness=t.sheenRoughness),t.emissive!==void 0&&this.emissive!==void 0&&this.emissive.setHex(t.emissive),t.specular!==void 0&&this.specular!==void 0&&this.specular.setHex(t.specular),t.specularIntensity!==void 0&&(this.specularIntensity=t.specularIntensity),t.specularColor!==void 0&&this.specularColor!==void 0&&this.specularColor.setHex(t.specularColor),t.shininess!==void 0&&(this.shininess=t.shininess),t.clearcoat!==void 0&&(this.clearcoat=t.clearcoat),t.clearcoatRoughness!==void 0&&(this.clearcoatRoughness=t.clearcoatRoughness),t.dispersion!==void 0&&(this.dispersion=t.dispersion),t.iridescence!==void 0&&(this.iridescence=t.iridescence),t.iridescenceIOR!==void 0&&(this.iridescenceIOR=t.iridescenceIOR),t.iridescenceThicknessRange!==void 0&&(this.iridescenceThicknessRange=t.iridescenceThicknessRange),t.transmission!==void 0&&(this.transmission=t.transmission),t.thickness!==void 0&&(this.thickness=t.thickness),t.attenuationDistance!==void 0&&(this.attenuationDistance=t.attenuationDistance),t.attenuationColor!==void 0&&this.attenuationColor!==void 0&&this.attenuationColor.setHex(t.attenuationColor),t.anisotropy!==void 0&&(this.anisotropy=t.anisotropy),t.anisotropyRotation!==void 0&&(this.anisotropyRotation=t.anisotropyRotation),t.fog!==void 0&&(this.fog=t.fog),t.flatShading!==void 0&&(this.flatShading=t.flatShading),t.blending!==void 0&&(this.blending=t.blending),t.combine!==void 0&&(this.combine=t.combine),t.side!==void 0&&(this.side=t.side),t.shadowSide!==void 0&&(this.shadowSide=t.shadowSide),t.opacity!==void 0&&(this.opacity=t.opacity),t.transparent!==void 0&&(this.transparent=t.transparent),t.alphaTest!==void 0&&(this.alphaTest=t.alphaTest),t.alphaHash!==void 0&&(this.alphaHash=t.alphaHash),t.depthFunc!==void 0&&(this.depthFunc=t.depthFunc),t.depthTest!==void 0&&(this.depthTest=t.depthTest),t.depthWrite!==void 0&&(this.depthWrite=t.depthWrite),t.colorWrite!==void 0&&(this.colorWrite=t.colorWrite),t.blendSrc!==void 0&&(this.blendSrc=t.blendSrc),t.blendDst!==void 0&&(this.blendDst=t.blendDst),t.blendEquation!==void 0&&(this.blendEquation=t.blendEquation),t.blendSrcAlpha!==void 0&&(this.blendSrcAlpha=t.blendSrcAlpha),t.blendDstAlpha!==void 0&&(this.blendDstAlpha=t.blendDstAlpha),t.blendEquationAlpha!==void 0&&(this.blendEquationAlpha=t.blendEquationAlpha),t.blendColor!==void 0&&this.blendColor!==void 0&&this.blendColor.setHex(t.blendColor),t.blendAlpha!==void 0&&(this.blendAlpha=t.blendAlpha),t.stencilWriteMask!==void 0&&(this.stencilWriteMask=t.stencilWriteMask),t.stencilFunc!==void 0&&(this.stencilFunc=t.stencilFunc),t.stencilRef!==void 0&&(this.stencilRef=t.stencilRef),t.stencilFuncMask!==void 0&&(this.stencilFuncMask=t.stencilFuncMask),t.stencilFail!==void 0&&(this.stencilFail=t.stencilFail),t.stencilZFail!==void 0&&(this.stencilZFail=t.stencilZFail),t.stencilZPass!==void 0&&(this.stencilZPass=t.stencilZPass),t.stencilWrite!==void 0&&(this.stencilWrite=t.stencilWrite),t.wireframe!==void 0&&(this.wireframe=t.wireframe),t.wireframeLinewidth!==void 0&&(this.wireframeLinewidth=t.wireframeLinewidth),t.wireframeLinecap!==void 0&&(this.wireframeLinecap=t.wireframeLinecap),t.wireframeLinejoin!==void 0&&(this.wireframeLinejoin=t.wireframeLinejoin),t.rotation!==void 0&&(this.rotation=t.rotation),t.linewidth!==void 0&&(this.linewidth=t.linewidth),t.dashSize!==void 0&&(this.dashSize=t.dashSize),t.gapSize!==void 0&&(this.gapSize=t.gapSize),t.scale!==void 0&&(this.scale=t.scale),t.polygonOffset!==void 0&&(this.polygonOffset=t.polygonOffset),t.polygonOffsetFactor!==void 0&&(this.polygonOffsetFactor=t.polygonOffsetFactor),t.polygonOffsetUnits!==void 0&&(this.polygonOffsetUnits=t.polygonOffsetUnits),t.dithering!==void 0&&(this.dithering=t.dithering),t.alphaToCoverage!==void 0&&(this.alphaToCoverage=t.alphaToCoverage),t.premultipliedAlpha!==void 0&&(this.premultipliedAlpha=t.premultipliedAlpha),t.forceSinglePass!==void 0&&(this.forceSinglePass=t.forceSinglePass),t.allowOverride!==void 0&&(this.allowOverride=t.allowOverride),t.visible!==void 0&&(this.visible=t.visible),t.toneMapped!==void 0&&(this.toneMapped=t.toneMapped),t.userData!==void 0&&(this.userData=t.userData),t.vertexColors!==void 0&&(typeof t.vertexColors=="number"?this.vertexColors=t.vertexColors>0:this.vertexColors=t.vertexColors),t.size!==void 0&&(this.size=t.size),t.sizeAttenuation!==void 0&&(this.sizeAttenuation=t.sizeAttenuation),t.map!==void 0&&(this.map=e[t.map]||null),t.matcap!==void 0&&(this.matcap=e[t.matcap]||null),t.alphaMap!==void 0&&(this.alphaMap=e[t.alphaMap]||null),t.bumpMap!==void 0&&(this.bumpMap=e[t.bumpMap]||null),t.bumpScale!==void 0&&(this.bumpScale=t.bumpScale),t.normalMap!==void 0&&(this.normalMap=e[t.normalMap]||null),t.normalMapType!==void 0&&(this.normalMapType=t.normalMapType),t.normalScale!==void 0){let n=t.normalScale;Array.isArray(n)===!1&&(n=[n,n]),this.normalScale=new ht().fromArray(n)}return t.displacementMap!==void 0&&(this.displacementMap=e[t.displacementMap]||null),t.displacementScale!==void 0&&(this.displacementScale=t.displacementScale),t.displacementBias!==void 0&&(this.displacementBias=t.displacementBias),t.roughnessMap!==void 0&&(this.roughnessMap=e[t.roughnessMap]||null),t.metalnessMap!==void 0&&(this.metalnessMap=e[t.metalnessMap]||null),t.emissiveMap!==void 0&&(this.emissiveMap=e[t.emissiveMap]||null),t.emissiveIntensity!==void 0&&(this.emissiveIntensity=t.emissiveIntensity),t.specularMap!==void 0&&(this.specularMap=e[t.specularMap]||null),t.specularIntensityMap!==void 0&&(this.specularIntensityMap=e[t.specularIntensityMap]||null),t.specularColorMap!==void 0&&(this.specularColorMap=e[t.specularColorMap]||null),t.envMap!==void 0&&(this.envMap=e[t.envMap]||null),t.envMapRotation!==void 0&&this.envMapRotation.fromArray(t.envMapRotation),t.envMapIntensity!==void 0&&(this.envMapIntensity=t.envMapIntensity),t.reflectivity!==void 0&&(this.reflectivity=t.reflectivity),t.refractionRatio!==void 0&&(this.refractionRatio=t.refractionRatio),t.lightMap!==void 0&&(this.lightMap=e[t.lightMap]||null),t.lightMapIntensity!==void 0&&(this.lightMapIntensity=t.lightMapIntensity),t.aoMap!==void 0&&(this.aoMap=e[t.aoMap]||null),t.aoMapIntensity!==void 0&&(this.aoMapIntensity=t.aoMapIntensity),t.gradientMap!==void 0&&(this.gradientMap=e[t.gradientMap]||null),t.clearcoatMap!==void 0&&(this.clearcoatMap=e[t.clearcoatMap]||null),t.clearcoatRoughnessMap!==void 0&&(this.clearcoatRoughnessMap=e[t.clearcoatRoughnessMap]||null),t.clearcoatNormalMap!==void 0&&(this.clearcoatNormalMap=e[t.clearcoatNormalMap]||null),t.clearcoatNormalScale!==void 0&&(this.clearcoatNormalScale=new ht().fromArray(t.clearcoatNormalScale)),t.iridescenceMap!==void 0&&(this.iridescenceMap=e[t.iridescenceMap]||null),t.iridescenceThicknessMap!==void 0&&(this.iridescenceThicknessMap=e[t.iridescenceThicknessMap]||null),t.transmissionMap!==void 0&&(this.transmissionMap=e[t.transmissionMap]||null),t.thicknessMap!==void 0&&(this.thicknessMap=e[t.thicknessMap]||null),t.anisotropyMap!==void 0&&(this.anisotropyMap=e[t.anisotropyMap]||null),t.sheenColorMap!==void 0&&(this.sheenColorMap=e[t.sheenColorMap]||null),t.sheenRoughnessMap!==void 0&&(this.sheenRoughnessMap=e[t.sheenRoughnessMap]||null),this}clone(){return new this.constructor().copy(this)}copy(t){this.name=t.name,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.blendColor.copy(t.blendColor),this.blendAlpha=t.blendAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let n=null;if(e!==null){const i=e.length;n=new Array(i);for(let r=0;r!==i;++r)n[r]=e[r].clone()}return this.clippingPlanes=n,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaHash=t.alphaHash,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.forceSinglePass=t.forceSinglePass,this.allowOverride=t.allowOverride,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(t){t===!0&&this.version++}}const Xn=new C,Yo=new C,Tr=new C,ai=new C,Ko=new C,Ar=new C,Zo=new C;class Cr{constructor(t=new C,e=new C(0,0,-1)){this.origin=t,this.direction=e}set(t,e){return this.origin.copy(t),this.direction.copy(e),this}copy(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this}at(t,e){return e.copy(this.origin).addScaledVector(this.direction,t)}lookAt(t){return this.direction.copy(t).sub(this.origin).normalize(),this}recast(t){return this.origin.copy(this.at(t,Xn)),this}closestPointToPoint(t,e){e.subVectors(t,this.origin);const n=e.dot(this.direction);return n<0?e.copy(this.origin):e.copy(this.origin).addScaledVector(this.direction,n)}distanceToPoint(t){return Math.sqrt(this.distanceSqToPoint(t))}distanceSqToPoint(t){const e=Xn.subVectors(t,this.origin).dot(this.direction);return e<0?this.origin.distanceToSquared(t):(Xn.copy(this.origin).addScaledVector(this.direction,e),Xn.distanceToSquared(t))}distanceSqToSegment(t,e,n,i){Yo.copy(t).add(e).multiplyScalar(.5),Tr.copy(e).sub(t).normalize(),ai.copy(this.origin).sub(Yo);const r=t.distanceTo(e)*.5,a=-this.direction.dot(Tr),o=ai.dot(this.direction),l=-ai.dot(Tr),c=ai.lengthSq(),u=Math.abs(1-a*a);let h,d,f,p;if(u>0)if(h=a*l-o,d=a*o-l,p=r*u,h>=0)if(d>=-p)if(d<=p){const v=1/u;h*=v,d*=v,f=h*(h+a*d+2*o)+d*(a*h+d+2*l)+c}else d=r,h=Math.max(0,-(a*d+o)),f=-h*h+d*(d+2*l)+c;else d=-r,h=Math.max(0,-(a*d+o)),f=-h*h+d*(d+2*l)+c;else d<=-p?(h=Math.max(0,-(-a*r+o)),d=h>0?-r:Math.min(Math.max(-r,-l),r),f=-h*h+d*(d+2*l)+c):d<=p?(h=0,d=Math.min(Math.max(-r,-l),r),f=d*(d+2*l)+c):(h=Math.max(0,-(a*r+o)),d=h>0?r:Math.min(Math.max(-r,-l),r),f=-h*h+d*(d+2*l)+c);else d=a>0?-r:r,h=Math.max(0,-(a*d+o)),f=-h*h+d*(d+2*l)+c;return n&&n.copy(this.origin).addScaledVector(this.direction,h),i&&i.copy(Yo).addScaledVector(Tr,d),f}intersectSphere(t,e){Xn.subVectors(t.center,this.origin);const n=Xn.dot(this.direction),i=Xn.dot(Xn)-n*n,r=t.radius*t.radius;if(i>r)return null;const a=Math.sqrt(r-i),o=n-a,l=n+a;return l<0?null:o<0?this.at(l,e):this.at(o,e)}intersectsSphere(t){return t.radius<0?!1:this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(e===0)return t.distanceToPoint(this.origin)===0?0:null;const n=-(this.origin.dot(t.normal)+t.constant)/e;return n>=0?n:null}intersectPlane(t,e){const n=this.distanceToPlane(t);return n===null?null:this.at(n,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);return e===0||t.normal.dot(this.direction)*e<0}intersectBox(t,e){let n,i,r,a,o,l;const c=1/this.direction.x,u=1/this.direction.y,h=1/this.direction.z,d=this.origin;return c>=0?(n=(t.min.x-d.x)*c,i=(t.max.x-d.x)*c):(n=(t.max.x-d.x)*c,i=(t.min.x-d.x)*c),u>=0?(r=(t.min.y-d.y)*u,a=(t.max.y-d.y)*u):(r=(t.max.y-d.y)*u,a=(t.min.y-d.y)*u),n>a||r>i||((r>n||isNaN(n))&&(n=r),(a=0?(o=(t.min.z-d.z)*h,l=(t.max.z-d.z)*h):(o=(t.max.z-d.z)*h,l=(t.min.z-d.z)*h),n>l||o>i)||((o>n||n!==n)&&(n=o),(l=0?n:i,e)}intersectsBox(t){return this.intersectBox(t,Xn)!==null}intersectTriangle(t,e,n,i,r){Ko.subVectors(e,t),Ar.subVectors(n,t),Zo.crossVectors(Ko,Ar);let a=this.direction.dot(Zo),o;if(a>0){if(i)return null;o=1}else if(a<0)o=-1,a=-a;else return null;ai.subVectors(this.origin,t);const l=o*this.direction.dot(Ar.crossVectors(ai,Ar));if(l<0)return null;const c=o*this.direction.dot(Ko.cross(ai));if(c<0||l+c>a)return null;const u=-o*ai.dot(Zo);return u<0?null:this.at(u/a,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}}class xn extends cs{constructor(t){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new kt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new Hn,this.combine=oc,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}const Oc=new se,Di=new Cr,Rr=new os,kc=new C,Pr=new C,Lr=new C,Dr=new C,Jo=new C,Ir=new C,Bc=new C,Nr=new C;class Bt extends be{constructor(t=new Be,e=new xn){super(),this.isMesh=!0,this.type="Mesh",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),t.morphTargetInfluences!==void 0&&(this.morphTargetInfluences=t.morphTargetInfluences.slice()),t.morphTargetDictionary!==void 0&&(this.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}updateMorphTargets(){const e=this.geometry.morphAttributes,n=Object.keys(e);if(n.length>0){const i=e[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,a=i.length;r(t.far-t.near)**2))&&(Oc.copy(r).invert(),Di.copy(t.ray).applyMatrix4(Oc),!(n.boundingBox!==null&&Di.intersectsBox(n.boundingBox)===!1)&&this._computeIntersections(t,e,Di)))}_computeIntersections(t,e,n){let i;const r=this.geometry,a=this.material,o=r.index,l=r.attributes.position,c=r.attributes.uv,u=r.attributes.uv1,h=r.attributes.normal,d=r.groups,f=r.drawRange;if(o!==null)if(Array.isArray(a))for(let p=0,v=d.length;pe.far?null:{distance:c,point:Nr.clone(),object:s}}function Ur(s,t,e,n,i,r,a,o,l,c){s.getVertexPosition(o,Pr),s.getVertexPosition(l,Lr),s.getVertexPosition(c,Dr);const u=Nf(s,t,e,n,Pr,Lr,Dr,Bc);if(u){const h=new C;gn.getBarycoord(Bc,Pr,Lr,Dr,h),i&&(u.uv=gn.getInterpolatedAttribute(i,o,l,c,h,new ht)),r&&(u.uv1=gn.getInterpolatedAttribute(r,o,l,c,h,new ht)),a&&(u.normal=gn.getInterpolatedAttribute(a,o,l,c,h,new C),u.normal.dot(n.direction)>0&&u.normal.multiplyScalar(-1));const d={a:o,b:l,c,normal:new C,materialIndex:0};gn.getNormal(Pr,Lr,Dr,d.normal),u.face=d,u.barycoord=h}return u}class zc extends Oe{constructor(t=null,e=1,n=1,i,r,a,o,l,c=Ne,u=Ne,h,d){super(null,a,o,l,c,u,i,r,h,d),this.isDataTexture=!0,this.image={data:t,width:e,height:n},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class ks extends rn{constructor(t,e,n,i=1){super(t,e,n),this.isInstancedBufferAttribute=!0,this.meshPerAttribute=i}copy(t){return super.copy(t),this.meshPerAttribute=t.meshPerAttribute,this}toJSON(){const t=super.toJSON();return t.meshPerAttribute=this.meshPerAttribute,t.isInstancedBufferAttribute=!0,t}}const hs=new se,Hc=new se,Fr=[],Gc=new Ri,Uf=new se,Bs=new Bt,zs=new os;class Vc extends Bt{constructor(t,e,n){super(t,e),this.isInstancedMesh=!0,this.instanceMatrix=new ks(new Float32Array(n*16),16),this.instanceColor=null,this.morphTexture=null,this.count=n,this.boundingBox=null,this.boundingSphere=null;for(let i=0;i1)?null:e.copy(t.start).addScaledVector(i,a)}intersectsLine(t){const e=this.distanceToPoint(t.start),n=this.distanceToPoint(t.end);return e<0&&n>0||n<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const n=e||Of.getNormalMatrix(t),i=this.coplanarPoint(Qo).applyMatrix4(t),r=this.normal.applyMatrix3(n).normalize();return this.constant=-i.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return new this.constructor().copy(this)}}const Ii=new os,kf=new ht(.5,.5),Or=new C;class kr{constructor(t=new oi,e=new oi,n=new oi,i=new oi,r=new oi,a=new oi){this.planes=[t,e,n,i,r,a]}set(t,e,n,i,r,a){const o=this.planes;return o[0].copy(t),o[1].copy(e),o[2].copy(n),o[3].copy(i),o[4].copy(r),o[5].copy(a),this}copy(t){const e=this.planes;for(let n=0;n<6;n++)e[n].copy(t.planes[n]);return this}setFromProjectionMatrix(t,e=An,n=!1){const i=this.planes,r=t.elements,a=r[0],o=r[1],l=r[2],c=r[3],u=r[4],h=r[5],d=r[6],f=r[7],p=r[8],v=r[9],m=r[10],g=r[11],w=r[12],E=r[13],M=r[14],S=r[15];if(i[0].setComponents(c-a,f-u,g-p,S-w).normalize(),i[1].setComponents(c+a,f+u,g+p,S+w).normalize(),i[2].setComponents(c+o,f+h,g+v,S+E).normalize(),i[3].setComponents(c-o,f-h,g-v,S-E).normalize(),n)i[4].setComponents(l,d,m,M).normalize(),i[5].setComponents(c-l,f-d,g-m,S-M).normalize();else if(i[4].setComponents(c-l,f-d,g-m,S-M).normalize(),e===An)i[5].setComponents(c+l,f+d,g+m,S+M).normalize();else if(e===Ls)i[5].setComponents(l,d,m,M).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+e);return this}intersectsObject(t){if(t.boundingSphere!==void 0)t.boundingSphere===null&&t.computeBoundingSphere(),Ii.copy(t.boundingSphere).applyMatrix4(t.matrixWorld);else{const e=t.geometry;e.boundingSphere===null&&e.computeBoundingSphere(),Ii.copy(e.boundingSphere).applyMatrix4(t.matrixWorld)}return this.intersectsSphere(Ii)}intersectsSprite(t){Ii.center.set(0,0,0);const e=kf.distanceTo(t.center);return Ii.radius=.7071067811865476+e,Ii.applyMatrix4(t.matrixWorld),this.intersectsSphere(Ii)}intersectsSphere(t){const e=this.planes,n=t.center,i=-t.radius;for(let r=0;r<6;r++)if(e[r].distanceToPoint(n)0?t.max.x:t.min.x,Or.y=i.normal.y>0?t.max.y:t.min.y,Or.z=i.normal.z>0?t.max.z:t.min.z,i.distanceToPoint(Or)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let n=0;n<6;n++)if(e[n].distanceToPoint(t)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}}class Wc extends cs{constructor(t){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new kt(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.linewidth=t.linewidth,this.linecap=t.linecap,this.linejoin=t.linejoin,this.fog=t.fog,this}}const Br=new C,zr=new C,$c=new se,Hs=new Cr,Hr=new os,jo=new C,Xc=new C;class qc extends be{constructor(t=new Be,e=new Wc){super(),this.isLine=!0,this.type="Line",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}computeLineDistances(){const t=this.geometry;if(t.index===null){const e=t.attributes.position,n=[0];for(let i=1,r=e.count;i0){const i=e[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,a=i.length;rn)return;jo.applyMatrix4(s.matrixWorld);const c=t.ray.origin.distanceTo(jo);if(!(ct.far))return{distance:c,point:Xc.clone().applyMatrix4(s.matrixWorld),index:a,face:null,faceIndex:null,barycoord:null,object:s}}class Yc extends Oe{constructor(t=[],e=Ei,n,i,r,a,o,l,c,u){super(t,e,n,i,r,a,o,l,c,u),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}class li extends Oe{constructor(t,e,n,i,r,a,o,l,c){super(t,e,n,i,r,a,o,l,c),this.isCanvasTexture=!0,this.needsUpdate=!0}}class us extends Oe{constructor(t,e,n=Tn,i,r,a,o=Ne,l=Ne,c,u=Bn,h=1){if(u!==Bn&&u!==Ai)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");const d={width:t,height:e,depth:h};super(d,i,r,a,o,l,u,n,c),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(t){return super.copy(t),this.source=new Po(Object.assign({},t.image)),this.compareFunction=t.compareFunction,this}toJSON(t){const e=super.toJSON(t);return this.compareFunction!==null&&(e.compareFunction=this.compareFunction),e}}class Bf extends us{constructor(t,e=Tn,n=Ei,i,r,a=Ne,o=Ne,l,c=Bn){const u={width:t,height:t,depth:1},h=[u,u,u,u,u,u];super(t,t,e,n,i,r,a,o,l,c),this.image=h,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(t){this.image=t}}class Kc extends Oe{constructor(t=null){super(),this.sourceTexture=t,this.isExternalTexture=!0}copy(t){return super.copy(t),this.sourceTexture=t.sourceTexture,this}}class tn extends Be{constructor(t=1,e=1,n=1,i=1,r=1,a=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:n,widthSegments:i,heightSegments:r,depthSegments:a};const o=this;i=Math.floor(i),r=Math.floor(r),a=Math.floor(a);const l=[],c=[],u=[],h=[];let d=0,f=0;p("z","y","x",-1,-1,n,e,t,a,r,0),p("z","y","x",1,-1,n,e,-t,a,r,1),p("x","z","y",1,1,t,n,e,i,a,2),p("x","z","y",1,-1,t,n,-e,i,a,3),p("x","y","z",1,-1,t,e,n,i,r,4),p("x","y","z",-1,-1,t,e,-n,i,r,5),this.setIndex(l),this.setAttribute("position",new ve(c,3)),this.setAttribute("normal",new ve(u,3)),this.setAttribute("uv",new ve(h,2));function p(v,m,g,w,E,M,S,b,R,x,T){const P=M/R,L=S/x,F=M/2,q=S/2,Y=b/2,B=R+1,K=x+1;let X=0,Q=0;const it=new C;for(let pt=0;pt0?1:-1,u.push(it.x,it.y,it.z),h.push(bt/R),h.push(1-pt/x),X+=1}}for(let pt=0;pt0&&E(!0),e>0&&E(!1)),this.setIndex(u),this.setAttribute("position",new ve(h,3)),this.setAttribute("normal",new ve(d,3)),this.setAttribute("uv",new ve(f,2));function w(){const M=new C,S=new C;let b=0;const R=(e-t)/n;for(let x=0;x<=r;x++){const T=[],P=x/r,L=P*(e-t)+t;for(let F=0;F<=i;F++){const q=F/i,Y=q*l+o,B=Math.sin(Y),K=Math.cos(Y);S.x=L*B,S.y=-P*n+m,S.z=L*K,h.push(S.x,S.y,S.z),M.set(B,R,K).normalize(),d.push(M.x,M.y,M.z),f.push(q,1-P),T.push(p++)}v.push(T)}for(let x=0;x0||T!==0)&&(u.push(P,L,q),b+=3),(e>0||T!==r-1)&&(u.push(L,F,q),b+=3)}c.addGroup(g,b,0),g+=b}function E(M){const S=p,b=new ht,R=new C;let x=0;const T=M===!0?t:e,P=M===!0?1:-1;for(let F=1;F<=i;F++)h.push(0,m*P,0),d.push(0,P,0),f.push(.5,.5),p++;const L=p;for(let F=0;F<=i;F++){const Y=F/i*l+o,B=Math.cos(Y),K=Math.sin(Y);R.x=T*K,R.y=m*P,R.z=T*B,h.push(R.x,R.y,R.z),d.push(0,P,0),b.x=B*.5+.5,b.y=K*.5*P+.5,f.push(b.x,b.y),p++}for(let F=0;F0)l=i-1;else{l=i;break}if(i=l,n[i]===a)return i/(r-1);const u=n[i],d=n[i+1]-u,f=(a-u)/d;return(i+f)/(r-1)}getTangent(t,e){let i=t-1e-4,r=t+1e-4;i<0&&(i=0),r>1&&(r=1);const a=this.getPoint(i),o=this.getPoint(r),l=e||(a.isVector2?new ht:new C);return l.copy(o).sub(a).normalize(),l}getTangentAt(t,e){const n=this.getUtoTmapping(t);return this.getTangent(n,e)}computeFrenetFrames(t,e=!1){const n=new C,i=[],r=[],a=[],o=new C,l=new se;for(let f=0;f<=t;f++){const p=f/t;i[f]=this.getTangentAt(p,new C)}r[0]=new C,a[0]=new C;let c=Number.MAX_VALUE;const u=Math.abs(i[0].x),h=Math.abs(i[0].y),d=Math.abs(i[0].z);u<=c&&(c=u,n.set(1,0,0)),h<=c&&(c=h,n.set(0,1,0)),d<=c&&n.set(0,0,1),o.crossVectors(i[0],n).normalize(),r[0].crossVectors(i[0],o),a[0].crossVectors(i[0],r[0]);for(let f=1;f<=t;f++){if(r[f]=r[f-1].clone(),a[f]=a[f-1].clone(),o.crossVectors(i[f-1],i[f]),o.length()>Number.EPSILON){o.normalize();const p=Math.acos($t(i[f-1].dot(i[f]),-1,1));r[f].applyMatrix4(l.makeRotationAxis(o,p))}a[f].crossVectors(i[f],r[f])}if(e===!0){let f=Math.acos($t(r[0].dot(r[t]),-1,1));f/=t,i[0].dot(o.crossVectors(r[0],r[t]))>0&&(f=-f);for(let p=1;p<=t;p++)r[p].applyMatrix4(l.makeRotationAxis(i[p],f*p)),a[p].crossVectors(i[p],r[p])}return{tangents:i,normals:r,binormals:a}}clone(){return new this.constructor().copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.7,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class Zc extends qn{constructor(t=0,e=0,n=1,i=1,r=0,a=Math.PI*2,o=!1,l=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=n,this.yRadius=i,this.aStartAngle=r,this.aEndAngle=a,this.aClockwise=o,this.aRotation=l}getPoint(t,e=new ht){const n=e,i=Math.PI*2;let r=this.aEndAngle-this.aStartAngle;const a=Math.abs(r)i;)r-=i;r0?0:(Math.floor(Math.abs(o)/r)+1)*r:l===0&&o===r-1&&(o=r-2,l=1);let c,u;this.closed||o>0?c=i[(o-1)%r]:(Qc.subVectors(i[0],i[1]).add(i[0]),c=Qc);const h=i[o%r],d=i[(o+1)%r];if(this.closed||o+2i.length-2?i.length-1:a+1],h=i[a>i.length-3?i.length-1:a+2];return n.set(th(o,l.x,c.x,u.x,h.x),th(o,l.y,c.y,u.y,h.y)),n}copy(t){super.copy(t),this.points=[];for(let e=0,n=t.points.length;e0)&&f.push(E,M,b),(g!==n-1||l=0;--t)if(s[t]>=65535)return!0;return!1}function kr(s){return document.createElementNS("http://www.w3.org/1999/xhtml",s)}function _p(){const s=kr("canvas");return s.style.display="block",s}const ch={};function hh(...s){const t="THREE."+s.shift();console.log(t,...s)}function uh(s){const t=s[0];if(typeof t=="string"&&t.startsWith("TSL:")){const e=s[1];e&&e.isStackTrace?s[0]+=" "+e.getLocation():s[1]='Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.'}return s}function Ut(...s){s=uh(s);const t="THREE."+s.shift();{const e=s[0];e&&e.isStackTrace?console.warn(e.getError(t)):console.warn(t,...s)}}function te(...s){s=uh(s);const t="THREE."+s.shift();{const e=s[0];e&&e.isStackTrace?console.error(e.getError(t)):console.error(t,...s)}}function ls(...s){const t=s.join(" ");t in ch||(ch[t]=!0,Ut(...s))}function yp(s,t,e){return new Promise(function(n,i){function r(){switch(s.clientWaitSync(t,s.SYNC_FLUSH_COMMANDS_BIT,0)){case s.WAIT_FAILED:i();break;case s.TIMEOUT_EXPIRED:setTimeout(r,e);break;default:n()}}setTimeout(r,e)})}const Mp={[ea]:na,[ia]:oa,[sa]:aa,[rs]:ra,[na]:ea,[oa]:ia,[aa]:sa,[ra]:rs};class ci{addEventListener(t,e){this._listeners===void 0&&(this._listeners={});const n=this._listeners;n[t]===void 0&&(n[t]=[]),n[t].indexOf(e)===-1&&n[t].push(e)}hasEventListener(t,e){const n=this._listeners;return n===void 0?!1:n[t]!==void 0&&n[t].indexOf(e)!==-1}removeEventListener(t,e){const n=this._listeners;if(n===void 0)return;const i=n[t];if(i!==void 0){const r=i.indexOf(e);r!==-1&&i.splice(r,1)}}dispatchEvent(t){const e=this._listeners;if(e===void 0)return;const n=e[t.type];if(n!==void 0){t.target=this;const i=n.slice(0);for(let r=0,o=i.length;r>8&255]+ke[s>>16&255]+ke[s>>24&255]+"-"+ke[t&255]+ke[t>>8&255]+"-"+ke[t>>16&15|64]+ke[t>>24&255]+"-"+ke[e&63|128]+ke[e>>8&255]+"-"+ke[e>>16&255]+ke[e>>24&255]+ke[n&255]+ke[n>>8&255]+ke[n>>16&255]+ke[n>>24&255]).toLowerCase()}function qt(s,t,e){return Math.max(t,Math.min(e,s))}function sl(s,t){return(s%t+t)%t}function bp(s,t,e,n,i){return n+(s-t)*(i-n)/(e-t)}function Sp(s,t,e){return s!==t?(e-s)/(t-s):0}function Js(s,t,e){return(1-e)*s+e*t}function wp(s,t,e,n){return Js(s,t,1-Math.exp(-e*n))}function Ep(s,t=1){return t-Math.abs(sl(s,t*2)-t)}function Tp(s,t,e){return s<=t?0:s>=e?1:(s=(s-t)/(e-t),s*s*(3-2*s))}function Ap(s,t,e){return s<=t?0:s>=e?1:(s=(s-t)/(e-t),s*s*s*(s*(s*6-15)+10))}function Cp(s,t){return s+Math.floor(Math.random()*(t-s+1))}function Rp(s,t){return s+Math.random()*(t-s)}function Pp(s){return s*(.5-Math.random())}function Lp(s){s!==void 0&&(dh=s);let t=dh+=1831565813;return t=Math.imul(t^t>>>15,t|1),t^=t+Math.imul(t^t>>>7,t|61),((t^t>>>14)>>>0)/4294967296}function Dp(s){return s*Zs}function Ip(s){return s*cs}function Np(s){return(s&s-1)===0&&s!==0}function Up(s){return Math.pow(2,Math.ceil(Math.log(s)/Math.LN2))}function Fp(s){return Math.pow(2,Math.floor(Math.log(s)/Math.LN2))}function Op(s,t,e,n,i){const r=Math.cos,o=Math.sin,a=r(e/2),l=o(e/2),c=r((t+n)/2),u=o((t+n)/2),h=r((t-n)/2),d=o((t-n)/2),f=r((n-t)/2),p=o((n-t)/2);switch(i){case"XYX":s.set(a*u,l*h,l*d,a*c);break;case"YZY":s.set(l*d,a*u,l*h,a*c);break;case"ZXZ":s.set(l*h,l*d,a*u,a*c);break;case"XZX":s.set(a*u,l*p,l*f,a*c);break;case"YXY":s.set(l*f,a*u,l*p,a*c);break;case"ZYZ":s.set(l*p,l*f,a*u,a*c);break;default:Ut("MathUtils: .setQuaternionFromProperEuler() encountered an unknown order: "+i)}}function us(s,t){switch(t.constructor){case Float32Array:return s;case Uint32Array:return s/4294967295;case Uint16Array:return s/65535;case Uint8Array:return s/255;case Int32Array:return Math.max(s/2147483647,-1);case Int16Array:return Math.max(s/32767,-1);case Int8Array:return Math.max(s/127,-1);default:throw new Error("THREE.MathUtils: Invalid component type.")}}function $e(s,t){switch(t.constructor){case Float32Array:return s;case Uint32Array:return Math.round(s*4294967295);case Uint16Array:return Math.round(s*65535);case Uint8Array:return Math.round(s*255);case Int32Array:return Math.round(s*2147483647);case Int16Array:return Math.round(s*32767);case Int8Array:return Math.round(s*127);default:throw new Error("THREE.MathUtils: Invalid component type.")}}const Br={DEG2RAD:Zs,RAD2DEG:cs,generateUUID:hs,clamp:qt,euclideanModulo:sl,mapLinear:bp,inverseLerp:Sp,lerp:Js,damp:wp,pingpong:Ep,smoothstep:Tp,smootherstep:Ap,randInt:Cp,randFloat:Rp,randFloatSpread:Pp,seededRandom:Lp,degToRad:Dp,radToDeg:Ip,isPowerOfTwo:Np,ceilPowerOfTwo:Up,floorPowerOfTwo:Fp,setQuaternionFromProperEuler:Op,normalize:$e,denormalize:us},mc=class mc{constructor(t=0,e=0){this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("THREE.Vector2: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("THREE.Vector2: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,n=this.y,i=t.elements;return this.x=i[0]*e+i[3]*n+i[6],this.y=i[1]*e+i[4]*n+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=qt(this.x,t.x,e.x),this.y=qt(this.y,t.y,e.y),this}clampScalar(t,e){return this.x=qt(this.x,t,e),this.y=qt(this.y,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(qt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos(qt(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y;return e*e+n*n}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const n=Math.cos(e),i=Math.sin(e),r=this.x-t.x,o=this.y-t.y;return this.x=r*n-o*i+t.x,this.y=r*i+o*n+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}};mc.prototype.isVector2=!0;let ft=mc;class Ln{constructor(t=0,e=0,n=0,i=1){this.isQuaternion=!0,this._x=t,this._y=e,this._z=n,this._w=i}static slerpFlat(t,e,n,i,r,o,a){let l=n[i+0],c=n[i+1],u=n[i+2],h=n[i+3],d=r[o+0],f=r[o+1],p=r[o+2],v=r[o+3];if(h!==v||l!==d||c!==f||u!==p){let m=l*d+c*f+u*p+h*v;m<0&&(d=-d,f=-f,p=-p,v=-v,m=-m);let g=1-a;if(m<.9995){const S=Math.acos(m),E=Math.sin(S);g=Math.sin(g*S)/E,a=Math.sin(a*S)/E,l=l*g+d*a,c=c*g+f*a,u=u*g+p*a,h=h*g+v*a}else{l=l*g+d*a,c=c*g+f*a,u=u*g+p*a,h=h*g+v*a;const S=1/Math.sqrt(l*l+c*c+u*u+h*h);l*=S,c*=S,u*=S,h*=S}}t[e]=l,t[e+1]=c,t[e+2]=u,t[e+3]=h}static multiplyQuaternionsFlat(t,e,n,i,r,o){const a=n[i],l=n[i+1],c=n[i+2],u=n[i+3],h=r[o],d=r[o+1],f=r[o+2],p=r[o+3];return t[e]=a*p+u*h+l*f-c*d,t[e+1]=l*p+u*d+c*h-a*f,t[e+2]=c*p+u*f+a*d-l*h,t[e+3]=u*p-a*h-l*d-c*f,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,n,i){return this._x=t,this._y=e,this._z=n,this._w=i,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e=!0){const n=t._x,i=t._y,r=t._z,o=t._order,a=Math.cos,l=Math.sin,c=a(n/2),u=a(i/2),h=a(r/2),d=l(n/2),f=l(i/2),p=l(r/2);switch(o){case"XYZ":this._x=d*u*h+c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h-d*f*p;break;case"YXZ":this._x=d*u*h+c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h+d*f*p;break;case"ZXY":this._x=d*u*h-c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h-d*f*p;break;case"ZYX":this._x=d*u*h-c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h+d*f*p;break;case"YZX":this._x=d*u*h+c*f*p,this._y=c*f*h+d*u*p,this._z=c*u*p-d*f*h,this._w=c*u*h-d*f*p;break;case"XZY":this._x=d*u*h-c*f*p,this._y=c*f*h-d*u*p,this._z=c*u*p+d*f*h,this._w=c*u*h+d*f*p;break;default:Ut("Quaternion: .setFromEuler() encountered an unknown order: "+o)}return e===!0&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const n=e/2,i=Math.sin(n);return this._x=t.x*i,this._y=t.y*i,this._z=t.z*i,this._w=Math.cos(n),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,n=e[0],i=e[4],r=e[8],o=e[1],a=e[5],l=e[9],c=e[2],u=e[6],h=e[10],d=n+a+h;if(d>0){const f=.5/Math.sqrt(d+1);this._w=.25/f,this._x=(u-l)*f,this._y=(r-c)*f,this._z=(o-i)*f}else if(n>a&&n>h){const f=2*Math.sqrt(1+n-a-h);this._w=(u-l)/f,this._x=.25*f,this._y=(i+o)/f,this._z=(r+c)/f}else if(a>h){const f=2*Math.sqrt(1+a-n-h);this._w=(r-c)/f,this._x=(i+o)/f,this._y=.25*f,this._z=(l+u)/f}else{const f=2*Math.sqrt(1+h-n-a);this._w=(o-i)/f,this._x=(r+c)/f,this._y=(l+u)/f,this._z=.25*f}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let n=t.dot(e)+1;return n<1e-8?(n=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=n):(this._x=0,this._y=-t.z,this._z=t.y,this._w=n)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=n),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs(qt(this.dot(t),-1,1)))}rotateTowards(t,e){const n=this.angleTo(t);if(n===0)return this;const i=Math.min(1,e/n);return this.slerp(t,i),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return t===0?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t){return this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const n=t._x,i=t._y,r=t._z,o=t._w,a=e._x,l=e._y,c=e._z,u=e._w;return this._x=n*u+o*a+i*c-r*l,this._y=i*u+o*l+r*a-n*c,this._z=r*u+o*c+n*l-i*a,this._w=o*u-n*a-i*l-r*c,this._onChangeCallback(),this}slerp(t,e){let n=t._x,i=t._y,r=t._z,o=t._w,a=this.dot(t);a<0&&(n=-n,i=-i,r=-r,o=-o,a=-a);let l=1-e;if(a<.9995){const c=Math.acos(a),u=Math.sin(c);l=Math.sin(l*c)/u,e=Math.sin(e*c)/u,this._x=this._x*l+n*e,this._y=this._y*l+i*e,this._z=this._z*l+r*e,this._w=this._w*l+o*e,this._onChangeCallback()}else this._x=this._x*l+n*e,this._y=this._y*l+i*e,this._z=this._z*l+r*e,this._w=this._w*l+o*e,this.normalize();return this}slerpQuaternions(t,e,n){return this.copy(t).slerp(e,n)}random(){const t=2*Math.PI*Math.random(),e=2*Math.PI*Math.random(),n=Math.random(),i=Math.sqrt(1-n),r=Math.sqrt(n);return this.set(i*Math.sin(t),i*Math.cos(t),r*Math.sin(e),r*Math.cos(e))}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}const gc=class gc{constructor(t=0,e=0,n=0){this.x=t,this.y=e,this.z=n}set(t,e,n){return n===void 0&&(n=this.z),this.x=t,this.y=e,this.z=n,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("THREE.Vector3: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("THREE.Vector3: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return this.applyQuaternion(fh.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(fh.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[3]*n+r[6]*i,this.y=r[1]*e+r[4]*n+r[7]*i,this.z=r[2]*e+r[5]*n+r[8]*i,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=t.elements,o=1/(r[3]*e+r[7]*n+r[11]*i+r[15]);return this.x=(r[0]*e+r[4]*n+r[8]*i+r[12])*o,this.y=(r[1]*e+r[5]*n+r[9]*i+r[13])*o,this.z=(r[2]*e+r[6]*n+r[10]*i+r[14])*o,this}applyQuaternion(t){const e=this.x,n=this.y,i=this.z,r=t.x,o=t.y,a=t.z,l=t.w,c=2*(o*i-a*n),u=2*(a*e-r*i),h=2*(r*n-o*e);return this.x=e+l*c+o*h-a*u,this.y=n+l*u+a*c-r*h,this.z=i+l*h+r*u-o*c,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,n=this.y,i=this.z,r=t.elements;return this.x=r[0]*e+r[4]*n+r[8]*i,this.y=r[1]*e+r[5]*n+r[9]*i,this.z=r[2]*e+r[6]*n+r[10]*i,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=qt(this.x,t.x,e.x),this.y=qt(this.y,t.y,e.y),this.z=qt(this.z,t.z,e.z),this}clampScalar(t,e){return this.x=qt(this.x,t,e),this.y=qt(this.y,t,e),this.z=qt(this.z,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(qt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this}cross(t){return this.crossVectors(this,t)}crossVectors(t,e){const n=t.x,i=t.y,r=t.z,o=e.x,a=e.y,l=e.z;return this.x=i*l-r*a,this.y=r*o-n*l,this.z=n*a-i*o,this}projectOnVector(t){const e=t.lengthSq();if(e===0)return this.set(0,0,0);const n=t.dot(this)/e;return this.copy(t).multiplyScalar(n)}projectOnPlane(t){return rl.copy(this).projectOnVector(t),this.sub(rl)}reflect(t){return this.sub(rl.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos(qt(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y,i=this.z-t.z;return e*e+n*n+i*i}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,n){const i=Math.sin(e)*t;return this.x=i*Math.sin(n),this.y=Math.cos(e)*t,this.z=i*Math.cos(n),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,n){return this.x=t*Math.sin(e),this.y=n,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),n=this.setFromMatrixColumn(t,1).length(),i=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=n,this.z=i,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,e*4)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,e*3)}setFromEuler(t){return this.x=t._x,this.y=t._y,this.z=t._z,this}setFromColor(t){return this.x=t.r,this.y=t.g,this.z=t.b,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const t=Math.random()*Math.PI*2,e=Math.random()*2-1,n=Math.sqrt(1-e*e);return this.x=n*Math.cos(t),this.y=e,this.z=n*Math.sin(t),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}};gc.prototype.isVector3=!0;let P=gc;const rl=new P,fh=new Ln,vc=class vc{constructor(t,e,n,i,r,o,a,l,c){this.elements=[1,0,0,0,1,0,0,0,1],t!==void 0&&this.set(t,e,n,i,r,o,a,l,c)}set(t,e,n,i,r,o,a,l,c){const u=this.elements;return u[0]=t,u[1]=i,u[2]=a,u[3]=e,u[4]=r,u[5]=l,u[6]=n,u[7]=o,u[8]=c,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],this}extractBasis(t,e,n){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,i=e.elements,r=this.elements,o=n[0],a=n[3],l=n[6],c=n[1],u=n[4],h=n[7],d=n[2],f=n[5],p=n[8],v=i[0],m=i[3],g=i[6],S=i[1],E=i[4],y=i[7],M=i[2],w=i[5],C=i[8];return r[0]=o*v+a*S+l*M,r[3]=o*m+a*E+l*w,r[6]=o*g+a*y+l*C,r[1]=c*v+u*S+h*M,r[4]=c*m+u*E+h*w,r[7]=c*g+u*y+h*C,r[2]=d*v+f*S+p*M,r[5]=d*m+f*E+p*w,r[8]=d*g+f*y+p*C,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],o=t[4],a=t[5],l=t[6],c=t[7],u=t[8];return e*o*u-e*a*c-n*r*u+n*a*l+i*r*c-i*o*l}invert(){const t=this.elements,e=t[0],n=t[1],i=t[2],r=t[3],o=t[4],a=t[5],l=t[6],c=t[7],u=t[8],h=u*o-a*c,d=a*l-u*r,f=c*r-o*l,p=e*h+n*d+i*f;if(p===0)return this.set(0,0,0,0,0,0,0,0,0);const v=1/p;return t[0]=h*v,t[1]=(i*c-u*n)*v,t[2]=(a*n-i*o)*v,t[3]=d*v,t[4]=(u*e-i*l)*v,t[5]=(i*r-a*e)*v,t[6]=f*v,t[7]=(n*l-c*e)*v,t[8]=(o*e-n*r)*v,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,n,i,r,o,a){const l=Math.cos(r),c=Math.sin(r);return this.set(n*l,n*c,-n*(l*o+c*a)+o+t,-i*c,i*l,-i*(-c*o+l*a)+a+e,0,0,1),this}scale(t,e){return ls("Matrix3: .scale() is deprecated. Use .makeScale() instead."),this.premultiply(ol.makeScale(t,e)),this}rotate(t){return ls("Matrix3: .rotate() is deprecated. Use .makeRotation() instead."),this.premultiply(ol.makeRotation(-t)),this}translate(t,e){return ls("Matrix3: .translate() is deprecated. Use .makeTranslation() instead."),this.premultiply(ol.makeTranslation(t,e)),this}makeTranslation(t,e){return t.isVector2?this.set(1,0,t.x,0,1,t.y,0,0,1):this.set(1,0,t,0,1,e,0,0,1),this}makeRotation(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,n,e,0,0,0,1),this}makeScale(t,e){return this.set(t,0,0,0,e,0,0,0,1),this}equals(t){const e=this.elements,n=t.elements;for(let i=0;i<9;i++)if(e[i]!==n[i])return!1;return!0}fromArray(t,e=0){for(let n=0;n<9;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t}clone(){return new this.constructor().fromArray(this.elements)}};vc.prototype.isMatrix3=!0;let Ht=vc;const ol=new Ht,ph=new Ht().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),mh=new Ht().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function kp(){const s={enabled:!0,workingColorSpace:Ys,spaces:{},convert:function(i,r,o){return this.enabled===!1||r===o||!r||!o||(this.spaces[r].transfer===se&&(i.r=$n(i.r),i.g=$n(i.g),i.b=$n(i.b)),this.spaces[r].primaries!==this.spaces[o].primaries&&(i.applyMatrix3(this.spaces[r].toXYZ),i.applyMatrix3(this.spaces[o].fromXYZ)),this.spaces[o].transfer===se&&(i.r=ds(i.r),i.g=ds(i.g),i.b=ds(i.b))),i},workingToColorSpace:function(i,r){return this.convert(i,this.workingColorSpace,r)},colorSpaceToWorking:function(i,r){return this.convert(i,r,this.workingColorSpace)},getPrimaries:function(i){return this.spaces[i].primaries},getTransfer:function(i){return i===li?Or:this.spaces[i].transfer},getToneMappingMode:function(i){return this.spaces[i].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(i,r=this.workingColorSpace){return i.fromArray(this.spaces[r].luminanceCoefficients)},define:function(i){Object.assign(this.spaces,i)},_getMatrix:function(i,r,o){return i.copy(this.spaces[r].toXYZ).multiply(this.spaces[o].fromXYZ)},_getDrawingBufferColorSpace:function(i){return this.spaces[i].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(i=this.workingColorSpace){return this.spaces[i].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(i,r){return ls("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),s.workingToColorSpace(i,r)},toWorkingColorSpace:function(i,r){return ls("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),s.colorSpaceToWorking(i,r)}},t=[.64,.33,.3,.6,.15,.06],e=[.2126,.7152,.0722],n=[.3127,.329];return s.define({[Ys]:{primaries:t,whitePoint:n,transfer:Or,toXYZ:ph,fromXYZ:mh,luminanceCoefficients:e,workingColorSpaceConfig:{unpackColorSpace:we},outputColorSpaceConfig:{drawingBufferColorSpace:we}},[we]:{primaries:t,whitePoint:n,transfer:se,toXYZ:ph,fromXYZ:mh,luminanceCoefficients:e,outputColorSpaceConfig:{drawingBufferColorSpace:we}}}),s}const Jt=kp();function $n(s){return s<.04045?s*.0773993808:Math.pow(s*.9478672986+.0521327014,2.4)}function ds(s){return s<.0031308?s*12.92:1.055*Math.pow(s,.41666)-.055}let fs;class Bp{static getDataURL(t,e="image/png"){if(/^data:/i.test(t.src)||typeof HTMLCanvasElement>"u")return t.src;let n;if(t instanceof HTMLCanvasElement)n=t;else{fs===void 0&&(fs=kr("canvas")),fs.width=t.width,fs.height=t.height;const i=fs.getContext("2d");t instanceof ImageData?i.putImageData(t,0,0):i.drawImage(t,0,0,t.width,t.height),n=fs}return n.toDataURL(e)}static sRGBToLinear(t){if(typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap){const e=kr("canvas");e.width=t.width,e.height=t.height;const n=e.getContext("2d");n.drawImage(t,0,0,t.width,t.height);const i=n.getImageData(0,0,t.width,t.height),r=i.data;for(let o=0;o1),this.pmremVersion=0,this.normalized=!1}get width(){return this.source.getSize(cl).x}get height(){return this.source.getSize(cl).y}get depth(){return this.source.getSize(cl).z}get image(){return this.source.data}set image(t){this.source.data=t}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return new this.constructor().copy(this)}copy(t){return this.name=t.name,this.source=t.source,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.channel=t.channel,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.normalized=t.normalized,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.colorSpace=t.colorSpace,this.renderTarget=t.renderTarget,this.isRenderTargetTexture=t.isRenderTargetTexture,this.isArrayTexture=t.isArrayTexture,this.userData=JSON.parse(JSON.stringify(t.userData)),this.needsUpdate=!0,this}setValues(t){for(const e in t){const n=t[e];if(n===void 0){Ut(`Texture.setValues(): parameter '${e}' has value of undefined.`);continue}const i=this[e];if(i===void 0){Ut(`Texture.setValues(): property '${e}' does not exist.`);continue}i&&n&&i.isVector2&&n.isVector2||i&&n&&i.isVector3&&n.isVector3||i&&n&&i.isMatrix3&&n.isMatrix3?i.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";if(!e&&t.textures[this.uuid]!==void 0)return t.textures[this.uuid];const n={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(t).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,normalized:this.normalized,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(n.userData=this.userData),e||(t.textures[this.uuid]=n),n}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(t){if(this.mapping!==Qc)return t;if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case Rr:t.x=t.x-Math.floor(t.x);break;case Vn:t.x=t.x<0?0:1;break;case ga:Math.abs(Math.floor(t.x)%2)===1?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x);break}if(t.y<0||t.y>1)switch(this.wrapT){case Rr:t.y=t.y-Math.floor(t.y);break;case Vn:t.y=t.y<0?0:1;break;case ga:Math.abs(Math.floor(t.y)%2)===1?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y);break}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){t===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(t){t===!0&&this.pmremVersion++}}Be.DEFAULT_IMAGE=null,Be.DEFAULT_MAPPING=Qc,Be.DEFAULT_ANISOTROPY=1;const xc=class xc{constructor(t=0,e=0,n=0,i=1){this.x=t,this.y=e,this.z=n,this.w=i}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,n,i){return this.x=t,this.y=e,this.z=n,this.w=i,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("THREE.Vector4: index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("THREE.Vector4: index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=t.w!==void 0?t.w:1,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,n=this.y,i=this.z,r=this.w,o=t.elements;return this.x=o[0]*e+o[4]*n+o[8]*i+o[12]*r,this.y=o[1]*e+o[5]*n+o[9]*i+o[13]*r,this.z=o[2]*e+o[6]*n+o[10]*i+o[14]*r,this.w=o[3]*e+o[7]*n+o[11]*i+o[15]*r,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,n,i,r;const l=t.elements,c=l[0],u=l[4],h=l[8],d=l[1],f=l[5],p=l[9],v=l[2],m=l[6],g=l[10];if(Math.abs(u-d)<.01&&Math.abs(h-v)<.01&&Math.abs(p-m)<.01){if(Math.abs(u+d)<.1&&Math.abs(h+v)<.1&&Math.abs(p+m)<.1&&Math.abs(c+f+g-3)<.1)return this.set(1,0,0,0),this;e=Math.PI;const E=(c+1)/2,y=(f+1)/2,M=(g+1)/2,w=(u+d)/4,C=(h+v)/4,x=(p+m)/4;return E>y&&E>M?E<.01?(n=0,i=.707106781,r=.707106781):(n=Math.sqrt(E),i=w/n,r=C/n):y>M?y<.01?(n=.707106781,i=0,r=.707106781):(i=Math.sqrt(y),n=w/i,r=x/i):M<.01?(n=.707106781,i=.707106781,r=0):(r=Math.sqrt(M),n=C/r,i=x/r),this.set(n,i,r,e),this}let S=Math.sqrt((m-p)*(m-p)+(h-v)*(h-v)+(d-u)*(d-u));return Math.abs(S)<.001&&(S=1),this.x=(m-p)/S,this.y=(h-v)/S,this.z=(d-u)/S,this.w=Math.acos((c+f+g-1)/2),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this.w=e[15],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this.w=Math.min(this.w,t.w),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this.w=Math.max(this.w,t.w),this}clamp(t,e){return this.x=qt(this.x,t.x,e.x),this.y=qt(this.y,t.y,e.y),this.z=qt(this.z,t.z,e.z),this.w=qt(this.w,t.w,e.w),this}clampScalar(t,e){return this.x=qt(this.x,t,e),this.y=qt(this.y,t,e),this.z=qt(this.z,t,e),this.w=qt(this.w,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(qt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z+this.w*t.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this.w+=(t.w-this.w)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this.w=t.w+(e.w-t.w)*n,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z&&t.w===this.w}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this.w=t[e+3],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t[e+3]=this.w,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this.w=t.getW(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}};xc.prototype.isVector4=!0;let ge=xc;class Gp extends ci{constructor(t=1,e=1,n={}){super(),n=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Oe,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,depth:1,multiview:!1,useArrayDepthTexture:!1},n),this.isRenderTarget=!0,this.width=t,this.height=e,this.depth=n.depth,this.scissor=new ge(0,0,t,e),this.scissorTest=!1,this.viewport=new ge(0,0,t,e),this.textures=[];const i={width:t,height:e,depth:n.depth},r=new Be(i),o=n.count;for(let a=0;a1);this.dispose()}this.viewport.set(0,0,t,e),this.scissor.set(0,0,t,e)}clone(){return new this.constructor().copy(this)}copy(t){this.width=t.width,this.height=t.height,this.depth=t.depth,this.scissor.copy(t.scissor),this.scissorTest=t.scissorTest,this.viewport.copy(t.viewport),this.textures.length=0;for(let e=0,n=t.textures.length;e>>0}enable(t){this.mask|=1<1){for(let e=0;e1){for(let n=0;n0&&(i.userData=this.userData),i.layers=this.layers.mask,i.matrix=this.matrix.toArray(),i.up=this.up.toArray(),this.pivot!==null&&(i.pivot=this.pivot.toArray()),this.matrixAutoUpdate===!1&&(i.matrixAutoUpdate=!1),this.morphTargetDictionary!==void 0&&(i.morphTargetDictionary=Object.assign({},this.morphTargetDictionary)),this.morphTargetInfluences!==void 0&&(i.morphTargetInfluences=this.morphTargetInfluences.slice()),this.isInstancedMesh&&(i.type="InstancedMesh",i.count=this.count,i.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(i.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(i.type="BatchedMesh",i.perObjectFrustumCulled=this.perObjectFrustumCulled,i.sortObjects=this.sortObjects,i.drawRanges=this._drawRanges,i.reservedRanges=this._reservedRanges,i.geometryInfo=this._geometryInfo.map(a=>({...a,boundingBox:a.boundingBox?a.boundingBox.toJSON():void 0,boundingSphere:a.boundingSphere?a.boundingSphere.toJSON():void 0})),i.instanceInfo=this._instanceInfo.map(a=>({...a})),i.availableInstanceIds=this._availableInstanceIds.slice(),i.availableGeometryIds=this._availableGeometryIds.slice(),i.nextIndexStart=this._nextIndexStart,i.nextVertexStart=this._nextVertexStart,i.geometryCount=this._geometryCount,i.maxInstanceCount=this._maxInstanceCount,i.maxVertexCount=this._maxVertexCount,i.maxIndexCount=this._maxIndexCount,i.geometryInitialized=this._geometryInitialized,i.matricesTexture=this._matricesTexture.toJSON(t),i.indirectTexture=this._indirectTexture.toJSON(t),this._colorsTexture!==null&&(i.colorsTexture=this._colorsTexture.toJSON(t)),this.boundingSphere!==null&&(i.boundingSphere=this.boundingSphere.toJSON()),this.boundingBox!==null&&(i.boundingBox=this.boundingBox.toJSON()));function r(a,l){return a[l.uuid]===void 0&&(a[l.uuid]=l.toJSON(t)),l.uuid}if(this.isScene)this.background&&(this.background.isColor?i.background=this.background.toJSON():this.background.isTexture&&(i.background=this.background.toJSON(t).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(i.environment=this.environment.toJSON(t).uuid);else if(this.isMesh||this.isLine||this.isPoints){i.geometry=r(t.geometries,this.geometry);const a=this.geometry.parameters;if(a!==void 0&&a.shapes!==void 0){const l=a.shapes;if(Array.isArray(l))for(let c=0,u=l.length;c0){i.children=[];for(let a=0;a0){i.animations=[];for(let a=0;a0&&(n.geometries=a),l.length>0&&(n.materials=l),c.length>0&&(n.textures=c),u.length>0&&(n.images=u),h.length>0&&(n.shapes=h),d.length>0&&(n.skeletons=d),f.length>0&&(n.animations=f),p.length>0&&(n.nodes=p)}return n.object=i,n;function o(a){const l=[];for(const c in a){const u=a[c];delete u.metadata,l.push(u)}return l}}clone(t){return new this.constructor().copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.pivot=t.pivot!==null?t.pivot.clone():null,this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldAutoUpdate=t.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.static=t.static,this.animations=t.animations.slice(),this.userData=JSON.parse(JSON.stringify(t.userData)),e===!0)for(let n=0;nf+p?(c.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!c.inputState.pinching&&d<=f-p&&(c.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else l!==null&&t.gripSpace&&(r=e.getPose(t.gripSpace,n),r!==null&&(l.matrix.fromArray(r.transform.matrix),l.matrix.decompose(l.position,l.rotation,l.scale),l.matrixWorldNeedsUpdate=!0,r.linearVelocity?(l.hasLinearVelocity=!0,l.linearVelocity.copy(r.linearVelocity)):l.hasLinearVelocity=!1,r.angularVelocity?(l.hasAngularVelocity=!0,l.angularVelocity.copy(r.angularVelocity)):l.hasAngularVelocity=!1,l.eventsEnabled&&l.dispatchEvent({type:"gripUpdated",data:t,target:this})));a!==null&&(i=e.getPose(t.targetRaySpace,n),i===null&&r!==null&&(i=r),i!==null&&(a.matrix.fromArray(i.transform.matrix),a.matrix.decompose(a.position,a.rotation,a.scale),a.matrixWorldNeedsUpdate=!0,i.linearVelocity?(a.hasLinearVelocity=!0,a.linearVelocity.copy(i.linearVelocity)):a.hasLinearVelocity=!1,i.angularVelocity?(a.hasAngularVelocity=!0,a.angularVelocity.copy(i.angularVelocity)):a.hasAngularVelocity=!1,this.dispatchEvent(Zp)))}return a!==null&&(a.visible=i!==null),l!==null&&(l.visible=r!==null),c!==null&&(c.visible=o!==null),this}_getHandJoint(t,e){if(t.joints[e.jointName]===void 0){const n=new xn;n.matrixAutoUpdate=!1,n.visible=!1,t.joints[e.jointName]=n,t.add(n)}return t.joints[e.jointName]}}const wh={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},ui={h:0,s:0,l:0},Gr={h:0,s:0,l:0};function fl(s,t,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?s+(t-s)*6*e:e<1/2?t:e<2/3?s+(t-s)*6*(2/3-e):s}class Bt{constructor(t,e,n){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(t,e,n)}set(t,e,n){if(e===void 0&&n===void 0){const i=t;i&&i.isColor?this.copy(i):typeof i=="number"?this.setHex(i):typeof i=="string"&&this.setStyle(i)}else this.setRGB(t,e,n);return this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t,e=we){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(t&255)/255,Jt.colorSpaceToWorking(this,e),this}setRGB(t,e,n,i=Jt.workingColorSpace){return this.r=t,this.g=e,this.b=n,Jt.colorSpaceToWorking(this,i),this}setHSL(t,e,n,i=Jt.workingColorSpace){if(t=sl(t,1),e=qt(e,0,1),n=qt(n,0,1),e===0)this.r=this.g=this.b=n;else{const r=n<=.5?n*(1+e):n+e-n*e,o=2*n-r;this.r=fl(o,r,t+1/3),this.g=fl(o,r,t),this.b=fl(o,r,t-1/3)}return Jt.colorSpaceToWorking(this,i),this}setStyle(t,e=we){function n(r){r!==void 0&&parseFloat(r)<1&&Ut("Color: Alpha component of "+t+" will be ignored.")}let i;if(i=/^(\w+)\(([^\)]*)\)/.exec(t)){let r;const o=i[1],a=i[2];switch(o){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(r[4]),this.setRGB(Math.min(255,parseInt(r[1],10))/255,Math.min(255,parseInt(r[2],10))/255,Math.min(255,parseInt(r[3],10))/255,e);if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(r[4]),this.setRGB(Math.min(100,parseInt(r[1],10))/100,Math.min(100,parseInt(r[2],10))/100,Math.min(100,parseInt(r[3],10))/100,e);break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(a))return n(r[4]),this.setHSL(parseFloat(r[1])/360,parseFloat(r[2])/100,parseFloat(r[3])/100,e);break;default:Ut("Color: Unknown color model "+t)}}else if(i=/^\#([A-Fa-f\d]+)$/.exec(t)){const r=i[1],o=r.length;if(o===3)return this.setRGB(parseInt(r.charAt(0),16)/15,parseInt(r.charAt(1),16)/15,parseInt(r.charAt(2),16)/15,e);if(o===6)return this.setHex(parseInt(r,16),e);Ut("Color: Invalid hex color "+t)}else if(t&&t.length>0)return this.setColorName(t,e);return this}setColorName(t,e=we){const n=wh[t.toLowerCase()];return n!==void 0?this.setHex(n,e):Ut("Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copySRGBToLinear(t){return this.r=$n(t.r),this.g=$n(t.g),this.b=$n(t.b),this}copyLinearToSRGB(t){return this.r=ds(t.r),this.g=ds(t.g),this.b=ds(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(t=we){return Jt.workingToColorSpace(ze.copy(this),t),Math.round(qt(ze.r*255,0,255))*65536+Math.round(qt(ze.g*255,0,255))*256+Math.round(qt(ze.b*255,0,255))}getHexString(t=we){return("000000"+this.getHex(t).toString(16)).slice(-6)}getHSL(t,e=Jt.workingColorSpace){Jt.workingToColorSpace(ze.copy(this),e);const n=ze.r,i=ze.g,r=ze.b,o=Math.max(n,i,r),a=Math.min(n,i,r);let l,c;const u=(a+o)/2;if(a===o)l=0,c=0;else{const h=o-a;switch(c=u<=.5?h/(o+a):h/(2-o-a),o){case n:l=(i-r)/h+(i0&&(e.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(e.object.backgroundIntensity=this.backgroundIntensity),e.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(e.object.environmentIntensity=this.environmentIntensity),e.object.environmentRotation=this.environmentRotation.toArray(),e}}const _n=new P,Yn=new P,ml=new P,Kn=new P,vs=new P,xs=new P,Eh=new P,gl=new P,vl=new P,xl=new P,_l=new ge,yl=new ge,Ml=new ge;class yn{constructor(t=new P,e=new P,n=new P){this.a=t,this.b=e,this.c=n}static getNormal(t,e,n,i){i.subVectors(n,e),_n.subVectors(t,e),i.cross(_n);const r=i.lengthSq();return r>0?i.multiplyScalar(1/Math.sqrt(r)):i.set(0,0,0)}static getBarycoord(t,e,n,i,r){_n.subVectors(i,e),Yn.subVectors(n,e),ml.subVectors(t,e);const o=_n.dot(_n),a=_n.dot(Yn),l=_n.dot(ml),c=Yn.dot(Yn),u=Yn.dot(ml),h=o*c-a*a;if(h===0)return r.set(0,0,0),null;const d=1/h,f=(c*l-a*u)*d,p=(o*u-a*l)*d;return r.set(1-f-p,p,f)}static containsPoint(t,e,n,i){return this.getBarycoord(t,e,n,i,Kn)===null?!1:Kn.x>=0&&Kn.y>=0&&Kn.x+Kn.y<=1}static getInterpolation(t,e,n,i,r,o,a,l){return this.getBarycoord(t,e,n,i,Kn)===null?(l.x=0,l.y=0,"z"in l&&(l.z=0),"w"in l&&(l.w=0),null):(l.setScalar(0),l.addScaledVector(r,Kn.x),l.addScaledVector(o,Kn.y),l.addScaledVector(a,Kn.z),l)}static getInterpolatedAttribute(t,e,n,i,r,o){return _l.setScalar(0),yl.setScalar(0),Ml.setScalar(0),_l.fromBufferAttribute(t,e),yl.fromBufferAttribute(t,n),Ml.fromBufferAttribute(t,i),o.setScalar(0),o.addScaledVector(_l,r.x),o.addScaledVector(yl,r.y),o.addScaledVector(Ml,r.z),o}static isFrontFacing(t,e,n,i){return _n.subVectors(n,e),Yn.subVectors(t,e),_n.cross(Yn).dot(i)<0}set(t,e,n){return this.a.copy(t),this.b.copy(e),this.c.copy(n),this}setFromPointsAndIndices(t,e,n,i){return this.a.copy(t[e]),this.b.copy(t[n]),this.c.copy(t[i]),this}setFromAttributeAndIndices(t,e,n,i){return this.a.fromBufferAttribute(t,e),this.b.fromBufferAttribute(t,n),this.c.fromBufferAttribute(t,i),this}clone(){return new this.constructor().copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return _n.subVectors(this.c,this.b),Yn.subVectors(this.a,this.b),_n.cross(Yn).length()*.5}getMidpoint(t){return t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return yn.getNormal(this.a,this.b,this.c,t)}getPlane(t){return t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return yn.getBarycoord(t,this.a,this.b,this.c,e)}getInterpolation(t,e,n,i,r){return yn.getInterpolation(t,this.a,this.b,this.c,e,n,i,r)}containsPoint(t){return yn.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return yn.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){const n=this.a,i=this.b,r=this.c;let o,a;vs.subVectors(i,n),xs.subVectors(r,n),gl.subVectors(t,n);const l=vs.dot(gl),c=xs.dot(gl);if(l<=0&&c<=0)return e.copy(n);vl.subVectors(t,i);const u=vs.dot(vl),h=xs.dot(vl);if(u>=0&&h<=u)return e.copy(i);const d=l*h-u*c;if(d<=0&&l>=0&&u<=0)return o=l/(l-u),e.copy(n).addScaledVector(vs,o);xl.subVectors(t,r);const f=vs.dot(xl),p=xs.dot(xl);if(p>=0&&f<=p)return e.copy(r);const v=f*c-l*p;if(v<=0&&c>=0&&p<=0)return a=c/(c-p),e.copy(n).addScaledVector(xs,a);const m=u*p-f*h;if(m<=0&&h-u>=0&&f-p>=0)return Eh.subVectors(r,i),a=(h-u)/(h-u+(f-p)),e.copy(i).addScaledVector(Eh,a);const g=1/(m+v+d);return o=v*g,a=d*g,e.copy(n).addScaledVector(vs,o).addScaledVector(xs,a)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}class ki{constructor(t=new P(1/0,1/0,1/0),e=new P(-1/0,-1/0,-1/0)){this.isBox3=!0,this.min=t,this.max=e}set(t,e){return this.min.copy(t),this.max.copy(e),this}setFromArray(t){this.makeEmpty();for(let e=0,n=t.length;e=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y&&t.z>=this.min.z&&t.z<=this.max.z}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y&&t.max.z>=this.min.z&&t.min.z<=this.max.z}intersectsSphere(t){return this.clampPoint(t.center,Mn),Mn.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,n;return t.normal.x>0?(e=t.normal.x*this.min.x,n=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,n=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,n+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,n+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,n+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,n+=t.normal.z*this.min.z),e<=-t.constant&&n>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(Qs),Wr.subVectors(this.max,Qs),_s.subVectors(t.a,Qs),ys.subVectors(t.b,Qs),Ms.subVectors(t.c,Qs),di.subVectors(ys,_s),fi.subVectors(Ms,ys),Bi.subVectors(_s,Ms);let e=[0,-di.z,di.y,0,-fi.z,fi.y,0,-Bi.z,Bi.y,di.z,0,-di.x,fi.z,0,-fi.x,Bi.z,0,-Bi.x,-di.y,di.x,0,-fi.y,fi.x,0,-Bi.y,Bi.x,0];return!bl(e,_s,ys,Ms,Wr)||(e=[1,0,0,0,1,0,0,0,1],!bl(e,_s,ys,Ms,Wr))?!1:($r.crossVectors(di,fi),e=[$r.x,$r.y,$r.z],bl(e,_s,ys,Ms,Wr))}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,Mn).distanceTo(t)}getBoundingSphere(t){return this.isEmpty()?t.makeEmpty():(this.getCenter(t.center),t.radius=this.getSize(Mn).length()*.5),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()?this:(Zn[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),Zn[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),Zn[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),Zn[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),Zn[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),Zn[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),Zn[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),Zn[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(Zn),this)}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(t){return this.min.fromArray(t.min),this.max.fromArray(t.max),this}}const Zn=[new P,new P,new P,new P,new P,new P,new P,new P],Mn=new P,Vr=new ki,_s=new P,ys=new P,Ms=new P,di=new P,fi=new P,Bi=new P,Qs=new P,Wr=new P,$r=new P,zi=new P;function bl(s,t,e,n,i){for(let r=0,o=s.length-3;r<=o;r+=3){zi.fromArray(s,r);const a=i.x*Math.abs(zi.x)+i.y*Math.abs(zi.y)+i.z*Math.abs(zi.z),l=t.dot(zi),c=e.dot(zi),u=n.dot(zi);if(Math.max(-Math.max(l,c,u),Math.min(l,c,u))>a)return!1}return!0}const Te=new P,Xr=new ft;let jp=0;class ln extends ci{constructor(t,e,n=!1){if(super(),Array.isArray(t))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:jp++}),this.name="",this.array=t,this.itemSize=e,this.count=t!==void 0?t.length/e:0,this.normalized=n,this.usage=ah,this.updateRanges=[],this.gpuType=mn,this.version=0}onUploadCallback(){}set needsUpdate(t){t===!0&&this.version++}setUsage(t){return this.usage=t,this}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}copy(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this.gpuType=t.gpuType,this}copyAt(t,e,n){t*=this.itemSize,n*=e.itemSize;for(let i=0,r=this.itemSize;ithis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){if(this.isEmpty())return this.center.copy(t),this.radius=0,this;tr.subVectors(t,this.center);const e=tr.lengthSq();if(e>this.radius*this.radius){const n=Math.sqrt(e),i=(n-this.radius)*.5;this.center.addScaledVector(tr,i/n),this.radius+=i}return this}union(t){return t.isEmpty()?this:this.isEmpty()?(this.copy(t),this):(this.center.equals(t.center)===!0?this.radius=Math.max(this.radius,t.radius):(Sl.subVectors(t.center,this.center).setLength(t.radius),this.expandByPoint(tr.copy(t.center).add(Sl)),this.expandByPoint(tr.copy(t.center).sub(Sl))),this)}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return new this.constructor().copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(t){return this.radius=t.radius,this.center.fromArray(t.center),this}}let tm=0;const cn=new re,wl=new Ee,Ss=new P,en=new ki,er=new ki,Ne=new P;class He extends ci{constructor(){super(),this.isBufferGeometry=!0,Object.defineProperty(this,"id",{value:tm++}),this.uuid=hs(),this.name="",this.type="BufferGeometry",this.index=null,this.indirect=null,this.indirectOffset=0,this.attributes={},this.morphAttributes={},this.morphTargetsRelative=!1,this.groups=[],this.boundingBox=null,this.boundingSphere=null,this.drawRange={start:0,count:1/0},this.userData={},this._transformed=!1}getIndex(){return this.index}setIndex(t){return Array.isArray(t)?this.index=new(xp(t)?Ah:Th)(t,1):this.index=t,this}setIndirect(t,e=0){return this.indirect=t,this.indirectOffset=e,this}getIndirect(){return this.indirect}getAttribute(t){return this.attributes[t]}setAttribute(t,e){return this.attributes[t]=e,this}deleteAttribute(t){return delete this.attributes[t],this}hasAttribute(t){return this.attributes[t]!==void 0}addGroup(t,e,n=0){this.groups.push({start:t,count:e,materialIndex:n})}clearGroups(){this.groups=[]}setDrawRange(t,e){this.drawRange.start=t,this.drawRange.count=e}applyMatrix4(t){const e=this.attributes.position;e!==void 0&&(e.applyMatrix4(t),e.needsUpdate=!0);const n=this.attributes.normal;if(n!==void 0){const r=new Ht().getNormalMatrix(t);n.applyNormalMatrix(r),n.needsUpdate=!0}const i=this.attributes.tangent;return i!==void 0&&(i.transformDirection(t),i.needsUpdate=!0),this.boundingBox!==null&&this.computeBoundingBox(),this.boundingSphere!==null&&this.computeBoundingSphere(),this._transformed=!0,this}applyQuaternion(t){return cn.makeRotationFromQuaternion(t),this.applyMatrix4(cn),this}rotateX(t){return cn.makeRotationX(t),this.applyMatrix4(cn),this}rotateY(t){return cn.makeRotationY(t),this.applyMatrix4(cn),this}rotateZ(t){return cn.makeRotationZ(t),this.applyMatrix4(cn),this}translate(t,e,n){return cn.makeTranslation(t,e,n),this.applyMatrix4(cn),this}scale(t,e,n){return cn.makeScale(t,e,n),this.applyMatrix4(cn),this}lookAt(t){return wl.lookAt(t),wl.updateMatrix(),this.applyMatrix4(wl.matrix),this}center(){return this.computeBoundingBox(),this.boundingBox.getCenter(Ss).negate(),this.translate(Ss.x,Ss.y,Ss.z),this}setFromPoints(t){const e=this.getAttribute("position");if(e===void 0){const n=[];for(let i=0,r=t.length;ie.count&&Ut("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),e.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new ki);const t=this.attributes.position,e=this.morphAttributes.position;if(t&&t.isGLBufferAttribute){te("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new P(-1/0,-1/0,-1/0),new P(1/0,1/0,1/0));return}if(t!==void 0){if(this.boundingBox.setFromBufferAttribute(t),e)for(let n=0,i=e.length;n0&&(t.userData=this.userData),this.parameters!==void 0&&this._transformed!==!0){const l=this.parameters;for(const c in l)l[c]!==void 0&&(t[c]=l[c]);return t}t.data={attributes:{}};const e=this.index;e!==null&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const n=this.attributes;for(const l in n){const c=n[l];t.data.attributes[l]=c.toJSON(t.data)}const i={};let r=!1;for(const l in this.morphAttributes){const c=this.morphAttributes[l],u=[];for(let h=0,d=c.length;h0&&(i[l]=u,r=!0)}r&&(t.data.morphAttributes=i,t.data.morphTargetsRelative=this.morphTargetsRelative);const o=this.groups;o.length>0&&(t.data.groups=JSON.parse(JSON.stringify(o)));const a=this.boundingSphere;return a!==null&&(t.data.boundingSphere=a.toJSON()),t}clone(){return new this.constructor().copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const n=t.index;n!==null&&this.setIndex(n.clone());const i=t.attributes;for(const c in i){const u=i[c];this.setAttribute(c,u.clone(e))}const r=t.morphAttributes;for(const c in r){const u=[],h=r[c];for(let d=0,f=h.length;d0!=t>0&&this.version++,this._alphaTest=t}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(t){if(t!==void 0)for(const e in t){const n=t[e];if(n===void 0){Ut(`Material: parameter '${e}' has value of undefined.`);continue}const i=this[e];if(i===void 0){Ut(`Material: '${e}' is not a property of THREE.${this.type}.`);continue}i&&i.isColor?i.set(n):i&&i.isVector2&&n&&n.isVector2||i&&i.isEuler&&n&&n.isEuler||i&&i.isVector3&&n&&n.isVector3?i.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";e&&(t={textures:{},images:{}});const n={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};n.uuid=this.uuid,n.type=this.type,this.name!==""&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),this.roughness!==void 0&&(n.roughness=this.roughness),this.metalness!==void 0&&(n.metalness=this.metalness),this.sheen!==void 0&&(n.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(n.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(n.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(n.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(n.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(n.shininess=this.shininess),this.clearcoat!==void 0&&(n.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(n.sheenColorMap=this.sheenColorMap.toJSON(t).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(n.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(t).uuid),this.dispersion!==void 0&&(n.dispersion=this.dispersion),this.iridescence!==void 0&&(n.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(n.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(n.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(n.iridescenceMap=this.iridescenceMap.toJSON(t).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(n.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(t).uuid),this.anisotropy!==void 0&&(n.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(n.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(n.anisotropyMap=this.anisotropyMap.toJSON(t).uuid),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(t).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(t).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(t).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(t).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(t).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(t).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(n.specularIntensityMap=this.specularIntensityMap.toJSON(t).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(n.specularColorMap=this.specularColorMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(t).uuid,this.combine!==void 0&&(n.combine=this.combine)),this.envMapRotation!==void 0&&(n.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(n.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(n.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(n.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(t).uuid),this.transmission!==void 0&&(n.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(n.transmissionMap=this.transmissionMap.toJSON(t).uuid),this.thickness!==void 0&&(n.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(n.thicknessMap=this.thicknessMap.toJSON(t).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(n.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(n.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(n.size=this.size),this.shadowSide!==null&&(n.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==ss&&(n.blending=this.blending),this.side!==ai&&(n.side=this.side),this.vertexColors===!0&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),this.transparent===!0&&(n.transparent=!0),this.blendSrc!==Qo&&(n.blendSrc=this.blendSrc),this.blendDst!==ta&&(n.blendDst=this.blendDst),this.blendEquation!==Ii&&(n.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(n.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(n.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(n.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(n.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(n.blendAlpha=this.blendAlpha),this.depthFunc!==rs&&(n.depthFunc=this.depthFunc),this.depthTest===!1&&(n.depthTest=this.depthTest),this.depthWrite===!1&&(n.depthWrite=this.depthWrite),this.colorWrite===!1&&(n.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(n.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==oh&&(n.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(n.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(n.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==as&&(n.stencilFail=this.stencilFail),this.stencilZFail!==as&&(n.stencilZFail=this.stencilZFail),this.stencilZPass!==as&&(n.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(n.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(n.rotation=this.rotation),this.polygonOffset===!0&&(n.polygonOffset=!0),this.polygonOffsetFactor!==0&&(n.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(n.linewidth=this.linewidth),this.dashSize!==void 0&&(n.dashSize=this.dashSize),this.gapSize!==void 0&&(n.gapSize=this.gapSize),this.scale!==void 0&&(n.scale=this.scale),this.dithering===!0&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),this.alphaHash===!0&&(n.alphaHash=!0),this.alphaToCoverage===!0&&(n.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(n.premultipliedAlpha=!0),this.forceSinglePass===!0&&(n.forceSinglePass=!0),this.allowOverride===!1&&(n.allowOverride=!1),this.wireframe===!0&&(n.wireframe=!0),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(n.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(n.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(n.flatShading=!0),this.visible===!1&&(n.visible=!1),this.toneMapped===!1&&(n.toneMapped=!1),this.fog===!1&&(n.fog=!1),Object.keys(this.userData).length>0&&(n.userData=this.userData);function i(r){const o=[];for(const a in r){const l=r[a];delete l.metadata,o.push(l)}return o}if(e){const r=i(t.textures),o=i(t.images);r.length>0&&(n.textures=r),o.length>0&&(n.images=o)}return n}fromJSON(t,e){if(t.uuid!==void 0&&(this.uuid=t.uuid),t.name!==void 0&&(this.name=t.name),t.color!==void 0&&this.color!==void 0&&this.color.setHex(t.color),t.roughness!==void 0&&(this.roughness=t.roughness),t.metalness!==void 0&&(this.metalness=t.metalness),t.sheen!==void 0&&(this.sheen=t.sheen),t.sheenColor!==void 0&&(this.sheenColor=new Bt().setHex(t.sheenColor)),t.sheenRoughness!==void 0&&(this.sheenRoughness=t.sheenRoughness),t.emissive!==void 0&&this.emissive!==void 0&&this.emissive.setHex(t.emissive),t.specular!==void 0&&this.specular!==void 0&&this.specular.setHex(t.specular),t.specularIntensity!==void 0&&(this.specularIntensity=t.specularIntensity),t.specularColor!==void 0&&this.specularColor!==void 0&&this.specularColor.setHex(t.specularColor),t.shininess!==void 0&&(this.shininess=t.shininess),t.clearcoat!==void 0&&(this.clearcoat=t.clearcoat),t.clearcoatRoughness!==void 0&&(this.clearcoatRoughness=t.clearcoatRoughness),t.dispersion!==void 0&&(this.dispersion=t.dispersion),t.iridescence!==void 0&&(this.iridescence=t.iridescence),t.iridescenceIOR!==void 0&&(this.iridescenceIOR=t.iridescenceIOR),t.iridescenceThicknessRange!==void 0&&(this.iridescenceThicknessRange=t.iridescenceThicknessRange),t.transmission!==void 0&&(this.transmission=t.transmission),t.thickness!==void 0&&(this.thickness=t.thickness),t.attenuationDistance!==void 0&&(this.attenuationDistance=t.attenuationDistance),t.attenuationColor!==void 0&&this.attenuationColor!==void 0&&this.attenuationColor.setHex(t.attenuationColor),t.anisotropy!==void 0&&(this.anisotropy=t.anisotropy),t.anisotropyRotation!==void 0&&(this.anisotropyRotation=t.anisotropyRotation),t.fog!==void 0&&(this.fog=t.fog),t.flatShading!==void 0&&(this.flatShading=t.flatShading),t.blending!==void 0&&(this.blending=t.blending),t.combine!==void 0&&(this.combine=t.combine),t.side!==void 0&&(this.side=t.side),t.shadowSide!==void 0&&(this.shadowSide=t.shadowSide),t.opacity!==void 0&&(this.opacity=t.opacity),t.transparent!==void 0&&(this.transparent=t.transparent),t.alphaTest!==void 0&&(this.alphaTest=t.alphaTest),t.alphaHash!==void 0&&(this.alphaHash=t.alphaHash),t.depthFunc!==void 0&&(this.depthFunc=t.depthFunc),t.depthTest!==void 0&&(this.depthTest=t.depthTest),t.depthWrite!==void 0&&(this.depthWrite=t.depthWrite),t.colorWrite!==void 0&&(this.colorWrite=t.colorWrite),t.blendSrc!==void 0&&(this.blendSrc=t.blendSrc),t.blendDst!==void 0&&(this.blendDst=t.blendDst),t.blendEquation!==void 0&&(this.blendEquation=t.blendEquation),t.blendSrcAlpha!==void 0&&(this.blendSrcAlpha=t.blendSrcAlpha),t.blendDstAlpha!==void 0&&(this.blendDstAlpha=t.blendDstAlpha),t.blendEquationAlpha!==void 0&&(this.blendEquationAlpha=t.blendEquationAlpha),t.blendColor!==void 0&&this.blendColor!==void 0&&this.blendColor.setHex(t.blendColor),t.blendAlpha!==void 0&&(this.blendAlpha=t.blendAlpha),t.stencilWriteMask!==void 0&&(this.stencilWriteMask=t.stencilWriteMask),t.stencilFunc!==void 0&&(this.stencilFunc=t.stencilFunc),t.stencilRef!==void 0&&(this.stencilRef=t.stencilRef),t.stencilFuncMask!==void 0&&(this.stencilFuncMask=t.stencilFuncMask),t.stencilFail!==void 0&&(this.stencilFail=t.stencilFail),t.stencilZFail!==void 0&&(this.stencilZFail=t.stencilZFail),t.stencilZPass!==void 0&&(this.stencilZPass=t.stencilZPass),t.stencilWrite!==void 0&&(this.stencilWrite=t.stencilWrite),t.wireframe!==void 0&&(this.wireframe=t.wireframe),t.wireframeLinewidth!==void 0&&(this.wireframeLinewidth=t.wireframeLinewidth),t.wireframeLinecap!==void 0&&(this.wireframeLinecap=t.wireframeLinecap),t.wireframeLinejoin!==void 0&&(this.wireframeLinejoin=t.wireframeLinejoin),t.rotation!==void 0&&(this.rotation=t.rotation),t.linewidth!==void 0&&(this.linewidth=t.linewidth),t.dashSize!==void 0&&(this.dashSize=t.dashSize),t.gapSize!==void 0&&(this.gapSize=t.gapSize),t.scale!==void 0&&(this.scale=t.scale),t.polygonOffset!==void 0&&(this.polygonOffset=t.polygonOffset),t.polygonOffsetFactor!==void 0&&(this.polygonOffsetFactor=t.polygonOffsetFactor),t.polygonOffsetUnits!==void 0&&(this.polygonOffsetUnits=t.polygonOffsetUnits),t.dithering!==void 0&&(this.dithering=t.dithering),t.alphaToCoverage!==void 0&&(this.alphaToCoverage=t.alphaToCoverage),t.premultipliedAlpha!==void 0&&(this.premultipliedAlpha=t.premultipliedAlpha),t.forceSinglePass!==void 0&&(this.forceSinglePass=t.forceSinglePass),t.allowOverride!==void 0&&(this.allowOverride=t.allowOverride),t.visible!==void 0&&(this.visible=t.visible),t.toneMapped!==void 0&&(this.toneMapped=t.toneMapped),t.userData!==void 0&&(this.userData=t.userData),t.vertexColors!==void 0&&(typeof t.vertexColors=="number"?this.vertexColors=t.vertexColors>0:this.vertexColors=t.vertexColors),t.size!==void 0&&(this.size=t.size),t.sizeAttenuation!==void 0&&(this.sizeAttenuation=t.sizeAttenuation),t.map!==void 0&&(this.map=e[t.map]||null),t.matcap!==void 0&&(this.matcap=e[t.matcap]||null),t.alphaMap!==void 0&&(this.alphaMap=e[t.alphaMap]||null),t.bumpMap!==void 0&&(this.bumpMap=e[t.bumpMap]||null),t.bumpScale!==void 0&&(this.bumpScale=t.bumpScale),t.normalMap!==void 0&&(this.normalMap=e[t.normalMap]||null),t.normalMapType!==void 0&&(this.normalMapType=t.normalMapType),t.normalScale!==void 0){let n=t.normalScale;Array.isArray(n)===!1&&(n=[n,n]),this.normalScale=new ft().fromArray(n)}return t.displacementMap!==void 0&&(this.displacementMap=e[t.displacementMap]||null),t.displacementScale!==void 0&&(this.displacementScale=t.displacementScale),t.displacementBias!==void 0&&(this.displacementBias=t.displacementBias),t.roughnessMap!==void 0&&(this.roughnessMap=e[t.roughnessMap]||null),t.metalnessMap!==void 0&&(this.metalnessMap=e[t.metalnessMap]||null),t.emissiveMap!==void 0&&(this.emissiveMap=e[t.emissiveMap]||null),t.emissiveIntensity!==void 0&&(this.emissiveIntensity=t.emissiveIntensity),t.specularMap!==void 0&&(this.specularMap=e[t.specularMap]||null),t.specularIntensityMap!==void 0&&(this.specularIntensityMap=e[t.specularIntensityMap]||null),t.specularColorMap!==void 0&&(this.specularColorMap=e[t.specularColorMap]||null),t.envMap!==void 0&&(this.envMap=e[t.envMap]||null),t.envMapRotation!==void 0&&this.envMapRotation.fromArray(t.envMapRotation),t.envMapIntensity!==void 0&&(this.envMapIntensity=t.envMapIntensity),t.reflectivity!==void 0&&(this.reflectivity=t.reflectivity),t.refractionRatio!==void 0&&(this.refractionRatio=t.refractionRatio),t.lightMap!==void 0&&(this.lightMap=e[t.lightMap]||null),t.lightMapIntensity!==void 0&&(this.lightMapIntensity=t.lightMapIntensity),t.aoMap!==void 0&&(this.aoMap=e[t.aoMap]||null),t.aoMapIntensity!==void 0&&(this.aoMapIntensity=t.aoMapIntensity),t.gradientMap!==void 0&&(this.gradientMap=e[t.gradientMap]||null),t.clearcoatMap!==void 0&&(this.clearcoatMap=e[t.clearcoatMap]||null),t.clearcoatRoughnessMap!==void 0&&(this.clearcoatRoughnessMap=e[t.clearcoatRoughnessMap]||null),t.clearcoatNormalMap!==void 0&&(this.clearcoatNormalMap=e[t.clearcoatNormalMap]||null),t.clearcoatNormalScale!==void 0&&(this.clearcoatNormalScale=new ft().fromArray(t.clearcoatNormalScale)),t.iridescenceMap!==void 0&&(this.iridescenceMap=e[t.iridescenceMap]||null),t.iridescenceThicknessMap!==void 0&&(this.iridescenceThicknessMap=e[t.iridescenceThicknessMap]||null),t.transmissionMap!==void 0&&(this.transmissionMap=e[t.transmissionMap]||null),t.thicknessMap!==void 0&&(this.thicknessMap=e[t.thicknessMap]||null),t.anisotropyMap!==void 0&&(this.anisotropyMap=e[t.anisotropyMap]||null),t.sheenColorMap!==void 0&&(this.sheenColorMap=e[t.sheenColorMap]||null),t.sheenRoughnessMap!==void 0&&(this.sheenRoughnessMap=e[t.sheenRoughnessMap]||null),this}clone(){return new this.constructor().copy(this)}copy(t){this.name=t.name,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.blendColor.copy(t.blendColor),this.blendAlpha=t.blendAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let n=null;if(e!==null){const i=e.length;n=new Array(i);for(let r=0;r!==i;++r)n[r]=e[r].clone()}return this.clippingPlanes=n,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaHash=t.alphaHash,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.forceSinglePass=t.forceSinglePass,this.allowOverride=t.allowOverride,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(t){t===!0&&this.version++}}const Jn=new P,El=new P,qr=new P,pi=new P,Tl=new P,Yr=new P,Al=new P;class Kr{constructor(t=new P,e=new P(0,0,-1)){this.origin=t,this.direction=e}set(t,e){return this.origin.copy(t),this.direction.copy(e),this}copy(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this}at(t,e){return e.copy(this.origin).addScaledVector(this.direction,t)}lookAt(t){return this.direction.copy(t).sub(this.origin).normalize(),this}recast(t){return this.origin.copy(this.at(t,Jn)),this}closestPointToPoint(t,e){e.subVectors(t,this.origin);const n=e.dot(this.direction);return n<0?e.copy(this.origin):e.copy(this.origin).addScaledVector(this.direction,n)}distanceToPoint(t){return Math.sqrt(this.distanceSqToPoint(t))}distanceSqToPoint(t){const e=Jn.subVectors(t,this.origin).dot(this.direction);return e<0?this.origin.distanceToSquared(t):(Jn.copy(this.origin).addScaledVector(this.direction,e),Jn.distanceToSquared(t))}distanceSqToSegment(t,e,n,i){El.copy(t).add(e).multiplyScalar(.5),qr.copy(e).sub(t).normalize(),pi.copy(this.origin).sub(El);const r=t.distanceTo(e)*.5,o=-this.direction.dot(qr),a=pi.dot(this.direction),l=-pi.dot(qr),c=pi.lengthSq(),u=Math.abs(1-o*o);let h,d,f,p;if(u>0)if(h=o*l-a,d=o*a-l,p=r*u,h>=0)if(d>=-p)if(d<=p){const v=1/u;h*=v,d*=v,f=h*(h+o*d+2*a)+d*(o*h+d+2*l)+c}else d=r,h=Math.max(0,-(o*d+a)),f=-h*h+d*(d+2*l)+c;else d=-r,h=Math.max(0,-(o*d+a)),f=-h*h+d*(d+2*l)+c;else d<=-p?(h=Math.max(0,-(-o*r+a)),d=h>0?-r:Math.min(Math.max(-r,-l),r),f=-h*h+d*(d+2*l)+c):d<=p?(h=0,d=Math.min(Math.max(-r,-l),r),f=d*(d+2*l)+c):(h=Math.max(0,-(o*r+a)),d=h>0?r:Math.min(Math.max(-r,-l),r),f=-h*h+d*(d+2*l)+c);else d=o>0?-r:r,h=Math.max(0,-(o*d+a)),f=-h*h+d*(d+2*l)+c;return n&&n.copy(this.origin).addScaledVector(this.direction,h),i&&i.copy(El).addScaledVector(qr,d),f}intersectSphere(t,e){Jn.subVectors(t.center,this.origin);const n=Jn.dot(this.direction),i=Jn.dot(Jn)-n*n,r=t.radius*t.radius;if(i>r)return null;const o=Math.sqrt(r-i),a=n-o,l=n+o;return l<0?null:a<0?this.at(l,e):this.at(a,e)}intersectsSphere(t){return t.radius<0?!1:this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(e===0)return t.distanceToPoint(this.origin)===0?0:null;const n=-(this.origin.dot(t.normal)+t.constant)/e;return n>=0?n:null}intersectPlane(t,e){const n=this.distanceToPlane(t);return n===null?null:this.at(n,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);return e===0||t.normal.dot(this.direction)*e<0}intersectBox(t,e){let n,i,r,o,a,l;const c=1/this.direction.x,u=1/this.direction.y,h=1/this.direction.z,d=this.origin;return c>=0?(n=(t.min.x-d.x)*c,i=(t.max.x-d.x)*c):(n=(t.max.x-d.x)*c,i=(t.min.x-d.x)*c),u>=0?(r=(t.min.y-d.y)*u,o=(t.max.y-d.y)*u):(r=(t.max.y-d.y)*u,o=(t.min.y-d.y)*u),n>o||r>i||((r>n||isNaN(n))&&(n=r),(o=0?(a=(t.min.z-d.z)*h,l=(t.max.z-d.z)*h):(a=(t.max.z-d.z)*h,l=(t.min.z-d.z)*h),n>l||a>i)||((a>n||n!==n)&&(n=a),(l=0?n:i,e)}intersectsBox(t){return this.intersectBox(t,Jn)!==null}intersectTriangle(t,e,n,i,r){Tl.subVectors(e,t),Yr.subVectors(n,t),Al.crossVectors(Tl,Yr);let o=this.direction.dot(Al),a;if(o>0){if(i)return null;a=1}else if(o<0)a=-1,o=-o;else return null;pi.subVectors(this.origin,t);const l=a*this.direction.dot(Yr.crossVectors(pi,Yr));if(l<0)return null;const c=a*this.direction.dot(Tl.cross(pi));if(c<0||l+c>o)return null;const u=-a*pi.dot(Al);return u<0?null:this.at(u/o,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}}class bn extends ws{constructor(t){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new Bt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new Xn,this.combine=jc,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}const Ch=new re,Hi=new Kr,Zr=new bs,Rh=new P,Jr=new P,jr=new P,Qr=new P,Cl=new P,to=new P,Ph=new P,eo=new P;class zt extends Ee{constructor(t=new He,e=new bn){super(),this.isMesh=!0,this.type="Mesh",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.count=1,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),t.morphTargetInfluences!==void 0&&(this.morphTargetInfluences=t.morphTargetInfluences.slice()),t.morphTargetDictionary!==void 0&&(this.morphTargetDictionary=Object.assign({},t.morphTargetDictionary)),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}updateMorphTargets(){const e=this.geometry.morphAttributes,n=Object.keys(e);if(n.length>0){const i=e[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,o=i.length;r(t.far-t.near)**2))&&(Ch.copy(r).invert(),Hi.copy(t.ray).applyMatrix4(Ch),!(n.boundingBox!==null&&Hi.intersectsBox(n.boundingBox)===!1)&&this._computeIntersections(t,e,Hi)))}_computeIntersections(t,e,n){let i;const r=this.geometry,o=this.material,a=r.index,l=r.attributes.position,c=r.attributes.uv,u=r.attributes.uv1,h=r.attributes.normal,d=r.groups,f=r.drawRange;if(a!==null)if(Array.isArray(o))for(let p=0,v=d.length;pe.far?null:{distance:c,point:eo.clone(),object:s}}function no(s,t,e,n,i,r,o,a,l,c){s.getVertexPosition(a,Jr),s.getVertexPosition(l,jr),s.getVertexPosition(c,Qr);const u=nm(s,t,e,n,Jr,jr,Qr,Ph);if(u){const h=new P;yn.getBarycoord(Ph,Jr,jr,Qr,h),i&&(u.uv=yn.getInterpolatedAttribute(i,a,l,c,h,new ft)),r&&(u.uv1=yn.getInterpolatedAttribute(r,a,l,c,h,new ft)),o&&(u.normal=yn.getInterpolatedAttribute(o,a,l,c,h,new P),u.normal.dot(n.direction)>0&&u.normal.multiplyScalar(-1));const d={a,b:l,c,normal:new P,materialIndex:0};yn.getNormal(Jr,jr,Qr,d.normal),u.face=d,u.barycoord=h}return u}class Lh extends Be{constructor(t=null,e=1,n=1,i,r,o,a,l,c=Fe,u=Fe,h,d){super(null,o,a,l,c,u,i,r,h,d),this.isDataTexture=!0,this.image={data:t,width:e,height:n},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class nr extends ln{constructor(t,e,n,i=1){super(t,e,n),this.isInstancedBufferAttribute=!0,this.meshPerAttribute=i}copy(t){return super.copy(t),this.meshPerAttribute=t.meshPerAttribute,this}toJSON(){const t=super.toJSON();return t.meshPerAttribute=this.meshPerAttribute,t.isInstancedBufferAttribute=!0,t}}const Es=new re,Dh=new re,io=[],Ih=new ki,im=new re,ir=new zt,sr=new bs;class Nh extends zt{constructor(t,e,n){super(t,e),this.isInstancedMesh=!0,this.instanceMatrix=new nr(new Float32Array(n*16),16),this.instanceColor=null,this.morphTexture=null,this.count=n,this.boundingBox=null,this.boundingSphere=null;for(let i=0;i1)?null:e.copy(t.start).addScaledVector(i,o)}intersectsLine(t){const e=this.distanceToPoint(t.start),n=this.distanceToPoint(t.end);return e<0&&n>0||n<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const n=e||rm.getNormalMatrix(t),i=this.coplanarPoint(Rl).applyMatrix4(t),r=this.normal.applyMatrix3(n).normalize();return this.constant=-i.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return new this.constructor().copy(this)}}const Gi=new bs,om=new ft(.5,.5),so=new P;class ro{constructor(t=new mi,e=new mi,n=new mi,i=new mi,r=new mi,o=new mi){this.planes=[t,e,n,i,r,o]}set(t,e,n,i,r,o){const a=this.planes;return a[0].copy(t),a[1].copy(e),a[2].copy(n),a[3].copy(i),a[4].copy(r),a[5].copy(o),this}copy(t){const e=this.planes;for(let n=0;n<6;n++)e[n].copy(t.planes[n]);return this}setFromProjectionMatrix(t,e=Pn,n=!1){const i=this.planes,r=t.elements,o=r[0],a=r[1],l=r[2],c=r[3],u=r[4],h=r[5],d=r[6],f=r[7],p=r[8],v=r[9],m=r[10],g=r[11],S=r[12],E=r[13],y=r[14],M=r[15];if(i[0].setComponents(c-o,f-u,g-p,M-S).normalize(),i[1].setComponents(c+o,f+u,g+p,M+S).normalize(),i[2].setComponents(c+a,f+h,g+v,M+E).normalize(),i[3].setComponents(c-a,f-h,g-v,M-E).normalize(),n)i[4].setComponents(l,d,m,y).normalize(),i[5].setComponents(c-l,f-d,g-m,M-y).normalize();else if(i[4].setComponents(c-l,f-d,g-m,M-y).normalize(),e===Pn)i[5].setComponents(c+l,f+d,g+m,M+y).normalize();else if(e===Ks)i[5].setComponents(l,d,m,y).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+e);return this}intersectsObject(t){if(t.boundingSphere!==void 0)t.boundingSphere===null&&t.computeBoundingSphere(),Gi.copy(t.boundingSphere).applyMatrix4(t.matrixWorld);else{const e=t.geometry;e.boundingSphere===null&&e.computeBoundingSphere(),Gi.copy(e.boundingSphere).applyMatrix4(t.matrixWorld)}return this.intersectsSphere(Gi)}intersectsSprite(t){Gi.center.set(0,0,0);const e=om.distanceTo(t.center);return Gi.radius=.7071067811865476+e,Gi.applyMatrix4(t.matrixWorld),this.intersectsSphere(Gi)}intersectsSphere(t){const e=this.planes,n=t.center,i=-t.radius;for(let r=0;r<6;r++)if(e[r].distanceToPoint(n)0?t.max.x:t.min.x,so.y=i.normal.y>0?t.max.y:t.min.y,so.z=i.normal.z>0?t.max.z:t.min.z,i.distanceToPoint(so)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let n=0;n<6;n++)if(e[n].distanceToPoint(t)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}}class Uh extends ws{constructor(t){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new Bt(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.linewidth=t.linewidth,this.linecap=t.linecap,this.linejoin=t.linejoin,this.fog=t.fog,this}}const oo=new P,ao=new P,Fh=new re,rr=new Kr,lo=new bs,Pl=new P,Oh=new P;class kh extends Ee{constructor(t=new He,e=new Uh){super(),this.isLine=!0,this.type="Line",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}computeLineDistances(){const t=this.geometry;if(t.index===null){const e=t.attributes.position,n=[0];for(let i=1,r=e.count;i0){const i=e[n[0]];if(i!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,o=i.length;rn)return;Pl.applyMatrix4(s.matrixWorld);const c=t.ray.origin.distanceTo(Pl);if(!(ct.far))return{distance:c,point:Oh.clone().applyMatrix4(s.matrixWorld),index:o,face:null,faceIndex:null,barycoord:null,object:s}}class Bh extends Be{constructor(t=[],e=Ni,n,i,r,o,a,l,c,u){super(t,e,n,i,r,o,a,l,c,u),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}class gi extends Be{constructor(t,e,n,i,r,o,a,l,c){super(t,e,n,i,r,o,a,l,c),this.isCanvasTexture=!0,this.needsUpdate=!0}}class Ts extends Be{constructor(t,e,n=Rn,i,r,o,a=Fe,l=Fe,c,u=Wn,h=1){if(u!==Wn&&u!==Fi)throw new Error("THREE.DepthTexture: format must be either THREE.DepthFormat or THREE.DepthStencilFormat");const d={width:t,height:e,depth:h};super(d,i,r,o,a,l,u,n,c),this.isDepthTexture=!0,this.flipY=!1,this.generateMipmaps=!1,this.compareFunction=null}copy(t){return super.copy(t),this.source=new al(Object.assign({},t.image)),this.compareFunction=t.compareFunction,this}toJSON(t){const e=super.toJSON(t);return this.compareFunction!==null&&(e.compareFunction=this.compareFunction),e}}class am extends Ts{constructor(t,e=Rn,n=Ni,i,r,o=Fe,a=Fe,l,c=Wn){const u={width:t,height:t,depth:1},h=[u,u,u,u,u,u];super(t,t,e,n,i,r,o,a,l,c),this.image=h,this.isCubeDepthTexture=!0,this.isCubeTexture=!0}get images(){return this.image}set images(t){this.image=t}}class zh extends Be{constructor(t=null){super(),this.sourceTexture=t,this.isExternalTexture=!0}copy(t){return super.copy(t),this.sourceTexture=t.sourceTexture,this}}class nn extends He{constructor(t=1,e=1,n=1,i=1,r=1,o=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:n,widthSegments:i,heightSegments:r,depthSegments:o};const a=this;i=Math.floor(i),r=Math.floor(r),o=Math.floor(o);const l=[],c=[],u=[],h=[];let d=0,f=0;p("z","y","x",-1,-1,n,e,t,o,r,0),p("z","y","x",1,-1,n,e,-t,o,r,1),p("x","z","y",1,1,t,n,e,i,o,2),p("x","z","y",1,-1,t,n,-e,i,o,3),p("x","y","z",1,-1,t,e,n,i,r,4),p("x","y","z",-1,-1,t,e,-n,i,r,5),this.setIndex(l),this.setAttribute("position",new Me(c,3)),this.setAttribute("normal",new Me(u,3)),this.setAttribute("uv",new Me(h,2));function p(v,m,g,S,E,y,M,w,C,x,T){const L=y/C,D=M/x,U=y/2,K=M/2,Z=w/2,N=C+1,W=x+1;let B=0,X=0;const nt=new P;for(let ot=0;ot0?1:-1,u.push(nt.x,nt.y,nt.z),h.push(vt/C),h.push(1-ot/x),B+=1}}for(let ot=0;ot0&&E(!0),e>0&&E(!1)),this.setIndex(u),this.setAttribute("position",new Me(h,3)),this.setAttribute("normal",new Me(d,3)),this.setAttribute("uv",new Me(f,2));function S(){const y=new P,M=new P;let w=0;const C=(e-t)/n;for(let x=0;x<=r;x++){const T=[],L=x/r,D=L*(e-t)+t;for(let U=0;U<=i;U++){const K=U/i,Z=K*l+a,N=Math.sin(Z),W=Math.cos(Z);M.x=D*N,M.y=-L*n+m,M.z=D*W,h.push(M.x,M.y,M.z),y.set(N,C,W).normalize(),d.push(y.x,y.y,y.z),f.push(K,1-L),T.push(p++)}v.push(T)}for(let x=0;x0||T!==0)&&(u.push(L,D,K),w+=3),(e>0||T!==r-1)&&(u.push(D,U,K),w+=3)}c.addGroup(g,w,0),g+=w}function E(y){const M=p,w=new ft,C=new P;let x=0;const T=y===!0?t:e,L=y===!0?1:-1;for(let U=1;U<=i;U++)h.push(0,m*L,0),d.push(0,L,0),f.push(.5,.5),p++;const D=p;for(let U=0;U<=i;U++){const Z=U/i*l+a,N=Math.cos(Z),W=Math.sin(Z);C.x=T*W,C.y=m*L,C.z=T*N,h.push(C.x,C.y,C.z),d.push(0,L,0),w.x=N*.5+.5,w.y=W*.5*L+.5,f.push(w.x,w.y),p++}for(let U=0;U0)l=i-1;else{l=i;break}if(i=l,n[i]===o)return i/(r-1);const u=n[i],d=n[i+1]-u,f=(o-u)/d;return(i+f)/(r-1)}getTangent(t,e){let i=t-1e-4,r=t+1e-4;i<0&&(i=0),r>1&&(r=1);const o=this.getPoint(i),a=this.getPoint(r),l=e||(o.isVector2?new ft:new P);return l.copy(a).sub(o).normalize(),l}getTangentAt(t,e){const n=this.getUtoTmapping(t);return this.getTangent(n,e)}computeFrenetFrames(t,e=!1){const n=new P,i=[],r=[],o=[],a=new P,l=new re;for(let f=0;f<=t;f++){const p=f/t;i[f]=this.getTangentAt(p,new P)}r[0]=new P,o[0]=new P;let c=Number.MAX_VALUE;const u=Math.abs(i[0].x),h=Math.abs(i[0].y),d=Math.abs(i[0].z);u<=c&&(c=u,n.set(1,0,0)),h<=c&&(c=h,n.set(0,1,0)),d<=c&&n.set(0,0,1),a.crossVectors(i[0],n).normalize(),r[0].crossVectors(i[0],a),o[0].crossVectors(i[0],r[0]);for(let f=1;f<=t;f++){if(r[f]=r[f-1].clone(),o[f]=o[f-1].clone(),a.crossVectors(i[f-1],i[f]),a.length()>Number.EPSILON){a.normalize();const p=Math.acos(qt(i[f-1].dot(i[f]),-1,1));r[f].applyMatrix4(l.makeRotationAxis(a,p))}o[f].crossVectors(i[f],r[f])}if(e===!0){let f=Math.acos(qt(r[0].dot(r[t]),-1,1));f/=t,i[0].dot(a.crossVectors(r[0],r[t]))>0&&(f=-f);for(let p=1;p<=t;p++)r[p].applyMatrix4(l.makeRotationAxis(i[p],f*p)),o[p].crossVectors(i[p],r[p])}return{tangents:i,normals:r,binormals:o}}clone(){return new this.constructor().copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.7,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class Hh extends jn{constructor(t=0,e=0,n=1,i=1,r=0,o=Math.PI*2,a=!1,l=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=n,this.yRadius=i,this.aStartAngle=r,this.aEndAngle=o,this.aClockwise=a,this.aRotation=l}getPoint(t,e=new ft){const n=e,i=Math.PI*2;let r=this.aEndAngle-this.aStartAngle;const o=Math.abs(r)i;)r-=i;r0?0:(Math.floor(Math.abs(a)/r)+1)*r:l===0&&a===r-1&&(a=r-2,l=1);let c,u;this.closed||a>0?c=i[(a-1)%r]:(Vh.subVectors(i[0],i[1]).add(i[0]),c=Vh);const h=i[a%r],d=i[(a+1)%r];if(this.closed||a+2i.length-2?i.length-1:o+1],h=i[o>i.length-3?i.length-1:o+2];return n.set($h(a,l.x,c.x,u.x,h.x),$h(a,l.y,c.y,u.y,h.y)),n}copy(t){super.copy(t),this.points=[];for(let e=0,n=t.points.length;e0)&&f.push(E,y,w),(g!==n-1||l0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader,e.lights=this.lights,e.clipping=this.clipping;const n={};for(const i in this.extensions)this.extensions[i]===!0&&(n[i]=!0);return Object.keys(n).length>0&&(e.extensions=n),e}fromJSON(t,e){if(super.fromJSON(t,e),t.uniforms!==void 0)for(const n in t.uniforms){const i=t.uniforms[n];switch(this.uniforms[n]={},i.type){case"t":this.uniforms[n].value=e[i.value]||null;break;case"c":this.uniforms[n].value=new kt().setHex(i.value);break;case"v2":this.uniforms[n].value=new ht().fromArray(i.value);break;case"v3":this.uniforms[n].value=new C().fromArray(i.value);break;case"v4":this.uniforms[n].value=new me().fromArray(i.value);break;case"m3":this.uniforms[n].value=new zt().fromArray(i.value);break;case"m4":this.uniforms[n].value=new se().fromArray(i.value);break;default:this.uniforms[n].value=i.value}}if(t.defines!==void 0&&(this.defines=t.defines),t.vertexShader!==void 0&&(this.vertexShader=t.vertexShader),t.fragmentShader!==void 0&&(this.fragmentShader=t.fragmentShader),t.glslVersion!==void 0&&(this.glslVersion=t.glslVersion),t.extensions!==void 0)for(const n in t.extensions)this.extensions[n]=t.extensions[n];return t.lights!==void 0&&(this.lights=t.lights),t.clipping!==void 0&&(this.clipping=t.clipping),this}}class sh extends Ce{constructor(t){super(t),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class Ee extends cs{constructor(t){super(),this.isMeshStandardMaterial=!0,this.type="MeshStandardMaterial",this.defines={STANDARD:""},this.color=new kt(16777215),this.roughness=1,this.metalness=0,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new kt(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=wo,this.normalScale=new ht(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.roughnessMap=null,this.metalnessMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new Hn,this.envMapIntensity=1,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={STANDARD:""},this.color.copy(t.color),this.roughness=t.roughness,this.metalness=t.metalness,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.roughnessMap=t.roughnessMap,this.metalnessMap=t.metalnessMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.envMapIntensity=t.envMapIntensity,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class sp extends cs{constructor(t){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=Hd,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}class rp extends cs{constructor(t){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(t)}copy(t){return super.copy(t),this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}class Vr extends be{constructor(t,e=1){super(),this.isLight=!0,this.type="Light",this.color=new kt(t),this.intensity=e}dispose(){this.dispatchEvent({type:"dispose"})}copy(t,e){return super.copy(t,e),this.color.copy(t.color),this.intensity=t.intensity,this}toJSON(t){const e=super.toJSON(t);return e.object.color=this.color.getHex(),e.object.intensity=this.intensity,e}}class ap extends Vr{constructor(t,e,n){super(t,n),this.isHemisphereLight=!0,this.type="HemisphereLight",this.position.copy(be.DEFAULT_UP),this.updateMatrix(),this.groundColor=new kt(e)}copy(t,e){return super.copy(t,e),this.groundColor.copy(t.groundColor),this}toJSON(t){const e=super.toJSON(t);return e.object.groundColor=this.groundColor.getHex(),e}}const rl=new se,rh=new C,ah=new C;class al{constructor(t){this.camera=t,this.intensity=1,this.bias=0,this.biasNode=null,this.normalBias=0,this.radius=1,this.blurSamples=8,this.mapSize=new ht(512,512),this.mapType=Ze,this.map=null,this.mapPass=null,this.matrix=new se,this.autoUpdate=!0,this.needsUpdate=!1,this._frustum=new kr,this._frameExtents=new ht(1,1),this._viewportCount=1,this._viewports=[new me(0,0,1,1)]}getViewportCount(){return this._viewportCount}getFrustum(){return this._frustum}updateMatrices(t){const e=this.camera,n=this.matrix;rh.setFromMatrixPosition(t.matrixWorld),e.position.copy(rh),ah.setFromMatrixPosition(t.target.matrixWorld),e.lookAt(ah),e.updateMatrixWorld(),rl.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this._frustum.setFromProjectionMatrix(rl,e.coordinateSystem,e.reversedDepth),e.coordinateSystem===Ls||e.reversedDepth?n.set(.5,0,0,.5,0,.5,0,.5,0,0,1,0,0,0,0,1):n.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),n.multiply(rl)}getViewport(t){return this._viewports[t]}getFrameExtents(){return this._frameExtents}dispose(){this.map&&this.map.dispose(),this.mapPass&&this.mapPass.dispose()}copy(t){return this.camera=t.camera.clone(),this.intensity=t.intensity,this.bias=t.bias,this.radius=t.radius,this.autoUpdate=t.autoUpdate,this.needsUpdate=t.needsUpdate,this.normalBias=t.normalBias,this.blurSamples=t.blurSamples,this.mapSize.copy(t.mapSize),this.biasNode=t.biasNode,this}clone(){return new this.constructor().copy(this)}toJSON(){const t={};return this.intensity!==1&&(t.intensity=this.intensity),this.bias!==0&&(t.bias=this.bias),this.normalBias!==0&&(t.normalBias=this.normalBias),this.radius!==1&&(t.radius=this.radius),(this.mapSize.x!==512||this.mapSize.y!==512)&&(t.mapSize=this.mapSize.toArray()),t.camera=this.camera.toJSON(!1).object,delete t.camera.matrix,t}}const Wr=new C,$r=new Cn,Rn=new C;class oh extends be{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new se,this.projectionMatrix=new se,this.projectionMatrixInverse=new se,this.coordinateSystem=An,this._reversedDepth=!1}get reversedDepth(){return this._reversedDepth}copy(t,e){return super.copy(t,e),this.matrixWorldInverse.copy(t.matrixWorldInverse),this.projectionMatrix.copy(t.projectionMatrix),this.projectionMatrixInverse.copy(t.projectionMatrixInverse),this.coordinateSystem=t.coordinateSystem,this}getWorldDirection(t){return super.getWorldDirection(t).negate()}updateMatrixWorld(t){super.updateMatrixWorld(t),this.matrixWorld.decompose(Wr,$r,Rn),Rn.x===1&&Rn.y===1&&Rn.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(Wr,$r,Rn.set(1,1,1)).invert()}updateWorldMatrix(t,e,n=!1){super.updateWorldMatrix(t,e,n),this.matrixWorld.decompose(Wr,$r,Rn),Rn.x===1&&Rn.y===1&&Rn.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(Wr,$r,Rn.set(1,1,1)).invert()}clone(){return new this.constructor().copy(this)}}const hi=new C,lh=new ht,ch=new ht;class qe extends oh{constructor(t=50,e=1,n=.1,i=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=t,this.zoom=1,this.near=n,this.far=i,this.focus=10,this.aspect=e,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.fov=t.fov,this.zoom=t.zoom,this.near=t.near,this.far=t.far,this.focus=t.focus,this.aspect=t.aspect,this.view=t.view===null?null:Object.assign({},t.view),this.filmGauge=t.filmGauge,this.filmOffset=t.filmOffset,this}setFocalLength(t){const e=.5*this.getFilmHeight()/t;this.fov=Yi*2*Math.atan(e),this.updateProjectionMatrix()}getFocalLength(){const t=Math.tan(Ds*.5*this.fov);return .5*this.getFilmHeight()/t}getEffectiveFOV(){return Yi*2*Math.atan(Math.tan(Ds*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(t,e,n){hi.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),e.set(hi.x,hi.y).multiplyScalar(-t/hi.z),hi.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(hi.x,hi.y).multiplyScalar(-t/hi.z)}getViewSize(t,e){return this.getViewBounds(t,lh,ch),e.subVectors(ch,lh)}setViewOffset(t,e,n,i,r,a){this.aspect=t/e,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=i,this.view.width=r,this.view.height=a,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=this.near;let e=t*Math.tan(Ds*.5*this.fov)/this.zoom,n=2*e,i=this.aspect*n,r=-.5*i;const a=this.view;if(this.view!==null&&this.view.enabled){const l=a.fullWidth,c=a.fullHeight;r+=a.offsetX*i/l,e-=a.offsetY*n/c,i*=a.width/l,n*=a.height/c}const o=this.filmOffset;o!==0&&(r+=t*o/this.getFilmWidth()),this.projectionMatrix.makePerspective(r,r+i,e,e-n,t,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.fov=this.fov,e.object.zoom=this.zoom,e.object.near=this.near,e.object.far=this.far,e.object.focus=this.focus,e.object.aspect=this.aspect,this.view!==null&&(e.object.view=Object.assign({},this.view)),e.object.filmGauge=this.filmGauge,e.object.filmOffset=this.filmOffset,e}}class op extends al{constructor(){super(new qe(50,1,.5,500)),this.isSpotLightShadow=!0,this.focus=1,this.aspect=1}updateMatrices(t){const e=this.camera,n=Yi*2*t.angle*this.focus,i=this.mapSize.width/this.mapSize.height*this.aspect,r=t.distance||e.far;(n!==e.fov||i!==e.aspect||r!==e.far)&&(e.fov=n,e.aspect=i,e.far=r,e.updateProjectionMatrix()),super.updateMatrices(t)}copy(t){return super.copy(t),this.focus=t.focus,this}}class lp extends Vr{constructor(t,e,n=0,i=Math.PI/3,r=0,a=2){super(t,e),this.isSpotLight=!0,this.type="SpotLight",this.position.copy(be.DEFAULT_UP),this.updateMatrix(),this.target=new be,this.distance=n,this.angle=i,this.penumbra=r,this.decay=a,this.map=null,this.shadow=new op}get power(){return this.intensity*Math.PI}set power(t){this.intensity=t/Math.PI}dispose(){super.dispose(),this.shadow.dispose()}copy(t,e){return super.copy(t,e),this.distance=t.distance,this.angle=t.angle,this.penumbra=t.penumbra,this.decay=t.decay,this.target=t.target.clone(),this.map=t.map,this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.distance=this.distance,e.object.angle=this.angle,e.object.decay=this.decay,e.object.penumbra=this.penumbra,e.object.target=this.target.uuid,this.map&&this.map.isTexture&&(e.object.map=this.map.toJSON(t).uuid),e.object.shadow=this.shadow.toJSON(),e}}class cp extends al{constructor(){super(new qe(90,1,.5,500)),this.isPointLightShadow=!0}}class ol extends Vr{constructor(t,e,n=0,i=2){super(t,e),this.isPointLight=!0,this.type="PointLight",this.distance=n,this.decay=i,this.shadow=new cp}get power(){return this.intensity*4*Math.PI}set power(t){this.intensity=t/(4*Math.PI)}dispose(){super.dispose(),this.shadow.dispose()}copy(t,e){return super.copy(t,e),this.distance=t.distance,this.decay=t.decay,this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.distance=this.distance,e.object.decay=this.decay,e.object.shadow=this.shadow.toJSON(),e}}class Xr extends oh{constructor(t=-1,e=1,n=1,i=-1,r=.1,a=2e3){super(),this.isOrthographicCamera=!0,this.type="OrthographicCamera",this.zoom=1,this.view=null,this.left=t,this.right=e,this.top=n,this.bottom=i,this.near=r,this.far=a,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.left=t.left,this.right=t.right,this.top=t.top,this.bottom=t.bottom,this.near=t.near,this.far=t.far,this.zoom=t.zoom,this.view=t.view===null?null:Object.assign({},t.view),this}setViewOffset(t,e,n,i,r,a){this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=i,this.view.width=r,this.view.height=a,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=(this.right-this.left)/(2*this.zoom),e=(this.top-this.bottom)/(2*this.zoom),n=(this.right+this.left)/2,i=(this.top+this.bottom)/2;let r=n-t,a=n+t,o=i+e,l=i-e;if(this.view!==null&&this.view.enabled){const c=(this.right-this.left)/this.view.fullWidth/this.zoom,u=(this.top-this.bottom)/this.view.fullHeight/this.zoom;r+=c*this.view.offsetX,a=r+c*this.view.width,o-=u*this.view.offsetY,l=o-u*this.view.height}this.projectionMatrix.makeOrthographic(r,a,o,l,this.near,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.zoom=this.zoom,e.object.left=this.left,e.object.right=this.right,e.object.top=this.top,e.object.bottom=this.bottom,e.object.near=this.near,e.object.far=this.far,this.view!==null&&(e.object.view=Object.assign({},this.view)),e}}class hp extends al{constructor(){super(new Xr(-5,5,5,-5,.5,500)),this.isDirectionalLightShadow=!0}}class up extends Vr{constructor(t,e){super(t,e),this.isDirectionalLight=!0,this.type="DirectionalLight",this.position.copy(be.DEFAULT_UP),this.updateMatrix(),this.target=new be,this.shadow=new hp}dispose(){super.dispose(),this.shadow.dispose()}copy(t){return super.copy(t),this.target=t.target.clone(),this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.shadow=this.shadow.toJSON(),e.object.target=this.target.uuid,e}}const fs=-90,ps=1;class dp extends be{constructor(t,e,n){super(),this.type="CubeCamera",this.renderTarget=n,this.coordinateSystem=null,this.activeMipmapLevel=0;const i=new qe(fs,ps,t,e);i.layers=this.layers,this.add(i);const r=new qe(fs,ps,t,e);r.layers=this.layers,this.add(r);const a=new qe(fs,ps,t,e);a.layers=this.layers,this.add(a);const o=new qe(fs,ps,t,e);o.layers=this.layers,this.add(o);const l=new qe(fs,ps,t,e);l.layers=this.layers,this.add(l);const c=new qe(fs,ps,t,e);c.layers=this.layers,this.add(c)}updateCoordinateSystem(){const t=this.coordinateSystem,e=this.children.concat(),[n,i,r,a,o,l]=e;for(const c of e)this.remove(c);if(t===An)n.up.set(0,1,0),n.lookAt(1,0,0),i.up.set(0,1,0),i.lookAt(-1,0,0),r.up.set(0,0,-1),r.lookAt(0,1,0),a.up.set(0,0,1),a.lookAt(0,-1,0),o.up.set(0,1,0),o.lookAt(0,0,1),l.up.set(0,1,0),l.lookAt(0,0,-1);else if(t===Ls)n.up.set(0,-1,0),n.lookAt(-1,0,0),i.up.set(0,-1,0),i.lookAt(1,0,0),r.up.set(0,0,1),r.lookAt(0,1,0),a.up.set(0,0,-1),a.lookAt(0,-1,0),o.up.set(0,-1,0),o.lookAt(0,0,1),l.up.set(0,-1,0),l.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+t);for(const c of e)this.add(c),c.updateMatrixWorld()}update(t,e){this.parent===null&&this.updateMatrixWorld();const{renderTarget:n,activeMipmapLevel:i}=this;this.coordinateSystem!==t.coordinateSystem&&(this.coordinateSystem=t.coordinateSystem,this.updateCoordinateSystem());const[r,a,o,l,c,u]=this.children,h=t.getRenderTarget(),d=t.getActiveCubeFace(),f=t.getActiveMipmapLevel(),p=t.xr.enabled;t.xr.enabled=!1;const v=n.texture.generateMipmaps;n.texture.generateMipmaps=!1;let m=!1;t.isWebGLRenderer===!0?m=t.state.buffers.depth.getReversed():m=t.reversedDepthBuffer,t.setRenderTarget(n,0,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,r),t.setRenderTarget(n,1,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,a),t.setRenderTarget(n,2,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,o),t.setRenderTarget(n,3,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,l),t.setRenderTarget(n,4,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,c),n.texture.generateMipmaps=v,t.setRenderTarget(n,5,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,u),t.setRenderTarget(h,d,f),t.xr.enabled=p,n.texture.needsPMREMUpdate=!0}}class fp extends qe{constructor(t=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=t}}class pp{constructor(){this._previousTime=0,this._currentTime=0,this._startTime=performance.now(),this._delta=0,this._elapsed=0,this._timescale=1,this._document=null,this._pageVisibilityHandler=null}connect(t){this._document=t,t.hidden!==void 0&&(this._pageVisibilityHandler=mp.bind(this),t.addEventListener("visibilitychange",this._pageVisibilityHandler,!1))}disconnect(){this._pageVisibilityHandler!==null&&(this._document.removeEventListener("visibilitychange",this._pageVisibilityHandler),this._pageVisibilityHandler=null),this._document=null}getDelta(){return this._delta/1e3}getElapsed(){return this._elapsed/1e3}getTimescale(){return this._timescale}setTimescale(t){return this._timescale=t,this}reset(){return this._currentTime=performance.now()-this._startTime,this}dispose(){this.disconnect()}update(t){return this._pageVisibilityHandler!==null&&this._document.hidden===!0?this._delta=0:(this._previousTime=this._currentTime,this._currentTime=(t!==void 0?t:performance.now())-this._startTime,this._delta=(this._currentTime-this._previousTime)*this._timescale,this._elapsed+=this._delta),this}}function mp(){this._document.hidden===!1&&this.reset()}const hh=new se;class gp{constructor(t,e,n=0,i=1/0){this.ray=new Cr(t,e),this.near=n,this.far=i,this.camera=null,this.layers=new Io,this.params={Mesh:{},Line:{threshold:1},LOD:{},Points:{threshold:1},Sprite:{}}}set(t,e){this.ray.set(t,e)}setFromCamera(t,e){e.isPerspectiveCamera?(this.ray.origin.setFromMatrixPosition(e.matrixWorld),this.ray.direction.set(t.x,t.y,.5).unproject(e).sub(this.ray.origin).normalize(),this.camera=e):e.isOrthographicCamera?(this.ray.origin.set(t.x,t.y,e.projectionMatrix.elements[14]).unproject(e),this.ray.direction.set(0,0,-1).transformDirection(e.matrixWorld),this.camera=e):Qt("Raycaster: Unsupported camera type: "+e.type)}setFromXRController(t){return hh.identity().extractRotation(t.matrixWorld),this.ray.origin.setFromMatrixPosition(t.matrixWorld),this.ray.direction.set(0,0,-1).applyMatrix4(hh),this}intersectObject(t,e=!0,n=[]){return ll(t,this,n,e),n.sort(uh),n}intersectObjects(t,e=!0,n=[]){for(let i=0,r=t.length;i0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader,e.lights=this.lights,e.clipping=this.clipping;const n={};for(const i in this.extensions)this.extensions[i]===!0&&(n[i]=!0);return Object.keys(n).length>0&&(e.extensions=n),e}fromJSON(t,e){if(super.fromJSON(t,e),t.uniforms!==void 0)for(const n in t.uniforms){const i=t.uniforms[n];switch(this.uniforms[n]={},i.type){case"t":this.uniforms[n].value=e[i.value]||null;break;case"c":this.uniforms[n].value=new Bt().setHex(i.value);break;case"v2":this.uniforms[n].value=new ft().fromArray(i.value);break;case"v3":this.uniforms[n].value=new P().fromArray(i.value);break;case"v4":this.uniforms[n].value=new ge().fromArray(i.value);break;case"m3":this.uniforms[n].value=new Ht().fromArray(i.value);break;case"m4":this.uniforms[n].value=new re().fromArray(i.value);break;default:this.uniforms[n].value=i.value}}if(t.defines!==void 0&&(this.defines=t.defines),t.vertexShader!==void 0&&(this.vertexShader=t.vertexShader),t.fragmentShader!==void 0&&(this.fragmentShader=t.fragmentShader),t.glslVersion!==void 0&&(this.glslVersion=t.glslVersion),t.extensions!==void 0)for(const n in t.extensions)this.extensions[n]=t.extensions[n];return t.lights!==void 0&&(this.lights=t.lights),t.clipping!==void 0&&(this.clipping=t.clipping),this}}class Kh extends Pe{constructor(t){super(t),this.isRawShaderMaterial=!0,this.type="RawShaderMaterial"}}class Ae extends ws{constructor(t){super(),this.isMeshStandardMaterial=!0,this.type="MeshStandardMaterial",this.defines={STANDARD:""},this.color=new Bt(16777215),this.roughness=1,this.metalness=0,this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Bt(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=el,this.normalScale=new ft(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.roughnessMap=null,this.metalnessMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new Xn,this.envMapIntensity=1,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.defines={STANDARD:""},this.color.copy(t.color),this.roughness=t.roughness,this.metalness=t.metalness,this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.roughnessMap=t.roughnessMap,this.metalnessMap=t.metalnessMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.envMapIntensity=t.envMapIntensity,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class Tm extends ws{constructor(t){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=hp,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}class Am extends ws{constructor(t){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(t)}copy(t){return super.copy(t),this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}class ho extends Ee{constructor(t,e=1){super(),this.isLight=!0,this.type="Light",this.color=new Bt(t),this.intensity=e}dispose(){this.dispatchEvent({type:"dispose"})}copy(t,e){return super.copy(t,e),this.color.copy(t.color),this.intensity=t.intensity,this}toJSON(t){const e=super.toJSON(t);return e.object.color=this.color.getHex(),e.object.intensity=this.intensity,e}}class Cm extends ho{constructor(t,e,n){super(t,n),this.isHemisphereLight=!0,this.type="HemisphereLight",this.position.copy(Ee.DEFAULT_UP),this.updateMatrix(),this.groundColor=new Bt(e)}copy(t,e){return super.copy(t,e),this.groundColor.copy(t.groundColor),this}toJSON(t){const e=super.toJSON(t);return e.object.groundColor=this.groundColor.getHex(),e}}const Fl=new re,Zh=new P,Jh=new P;class Ol{constructor(t){this.camera=t,this.intensity=1,this.bias=0,this.biasNode=null,this.normalBias=0,this.radius=1,this.blurSamples=8,this.mapSize=new ft(512,512),this.mapType=je,this.map=null,this.mapPass=null,this.matrix=new re,this.autoUpdate=!0,this.needsUpdate=!1,this._frustum=new ro,this._frameExtents=new ft(1,1),this._viewportCount=1,this._viewports=[new ge(0,0,1,1)]}getViewportCount(){return this._viewportCount}getFrustum(){return this._frustum}updateMatrices(t){const e=this.camera,n=this.matrix;Zh.setFromMatrixPosition(t.matrixWorld),e.position.copy(Zh),Jh.setFromMatrixPosition(t.target.matrixWorld),e.lookAt(Jh),e.updateMatrixWorld(),Fl.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this._frustum.setFromProjectionMatrix(Fl,e.coordinateSystem,e.reversedDepth),e.coordinateSystem===Ks||e.reversedDepth?n.set(.5,0,0,.5,0,.5,0,.5,0,0,1,0,0,0,0,1):n.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),n.multiply(Fl)}getViewport(t){return this._viewports[t]}getFrameExtents(){return this._frameExtents}dispose(){this.map&&this.map.dispose(),this.mapPass&&this.mapPass.dispose()}copy(t){return this.camera=t.camera.clone(),this.intensity=t.intensity,this.bias=t.bias,this.radius=t.radius,this.autoUpdate=t.autoUpdate,this.needsUpdate=t.needsUpdate,this.normalBias=t.normalBias,this.blurSamples=t.blurSamples,this.mapSize.copy(t.mapSize),this.biasNode=t.biasNode,this}clone(){return new this.constructor().copy(this)}toJSON(){const t={};return this.intensity!==1&&(t.intensity=this.intensity),this.bias!==0&&(t.bias=this.bias),this.normalBias!==0&&(t.normalBias=this.normalBias),this.radius!==1&&(t.radius=this.radius),(this.mapSize.x!==512||this.mapSize.y!==512)&&(t.mapSize=this.mapSize.toArray()),t.camera=this.camera.toJSON(!1).object,delete t.camera.matrix,t}}const uo=new P,fo=new Ln,Dn=new P;class jh extends Ee{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new re,this.projectionMatrix=new re,this.projectionMatrixInverse=new re,this.coordinateSystem=Pn,this._reversedDepth=!1}get reversedDepth(){return this._reversedDepth}copy(t,e){return super.copy(t,e),this.matrixWorldInverse.copy(t.matrixWorldInverse),this.projectionMatrix.copy(t.projectionMatrix),this.projectionMatrixInverse.copy(t.projectionMatrixInverse),this.coordinateSystem=t.coordinateSystem,this}getWorldDirection(t){return super.getWorldDirection(t).negate()}updateMatrixWorld(t){super.updateMatrixWorld(t),this.matrixWorld.decompose(uo,fo,Dn),Dn.x===1&&Dn.y===1&&Dn.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(uo,fo,Dn.set(1,1,1)).invert()}updateWorldMatrix(t,e,n=!1){super.updateWorldMatrix(t,e,n),this.matrixWorld.decompose(uo,fo,Dn),Dn.x===1&&Dn.y===1&&Dn.z===1?this.matrixWorldInverse.copy(this.matrixWorld).invert():this.matrixWorldInverse.compose(uo,fo,Dn.set(1,1,1)).invert()}clone(){return new this.constructor().copy(this)}}const xi=new P,Qh=new ft,tu=new ft;class Ke extends jh{constructor(t=50,e=1,n=.1,i=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=t,this.zoom=1,this.near=n,this.far=i,this.focus=10,this.aspect=e,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.fov=t.fov,this.zoom=t.zoom,this.near=t.near,this.far=t.far,this.focus=t.focus,this.aspect=t.aspect,this.view=t.view===null?null:Object.assign({},t.view),this.filmGauge=t.filmGauge,this.filmOffset=t.filmOffset,this}setFocalLength(t){const e=.5*this.getFilmHeight()/t;this.fov=cs*2*Math.atan(e),this.updateProjectionMatrix()}getFocalLength(){const t=Math.tan(Zs*.5*this.fov);return .5*this.getFilmHeight()/t}getEffectiveFOV(){return cs*2*Math.atan(Math.tan(Zs*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(t,e,n){xi.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),e.set(xi.x,xi.y).multiplyScalar(-t/xi.z),xi.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(xi.x,xi.y).multiplyScalar(-t/xi.z)}getViewSize(t,e){return this.getViewBounds(t,Qh,tu),e.subVectors(tu,Qh)}setViewOffset(t,e,n,i,r,o){this.aspect=t/e,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=i,this.view.width=r,this.view.height=o,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=this.near;let e=t*Math.tan(Zs*.5*this.fov)/this.zoom,n=2*e,i=this.aspect*n,r=-.5*i;const o=this.view;if(this.view!==null&&this.view.enabled){const l=o.fullWidth,c=o.fullHeight;r+=o.offsetX*i/l,e-=o.offsetY*n/c,i*=o.width/l,n*=o.height/c}const a=this.filmOffset;a!==0&&(r+=t*a/this.getFilmWidth()),this.projectionMatrix.makePerspective(r,r+i,e,e-n,t,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.fov=this.fov,e.object.zoom=this.zoom,e.object.near=this.near,e.object.far=this.far,e.object.focus=this.focus,e.object.aspect=this.aspect,this.view!==null&&(e.object.view=Object.assign({},this.view)),e.object.filmGauge=this.filmGauge,e.object.filmOffset=this.filmOffset,e}}class Rm extends Ol{constructor(){super(new Ke(50,1,.5,500)),this.isSpotLightShadow=!0,this.focus=1,this.aspect=1}updateMatrices(t){const e=this.camera,n=cs*2*t.angle*this.focus,i=this.mapSize.width/this.mapSize.height*this.aspect,r=t.distance||e.far;(n!==e.fov||i!==e.aspect||r!==e.far)&&(e.fov=n,e.aspect=i,e.far=r,e.updateProjectionMatrix()),super.updateMatrices(t)}copy(t){return super.copy(t),this.focus=t.focus,this}}class Pm extends ho{constructor(t,e,n=0,i=Math.PI/3,r=0,o=2){super(t,e),this.isSpotLight=!0,this.type="SpotLight",this.position.copy(Ee.DEFAULT_UP),this.updateMatrix(),this.target=new Ee,this.distance=n,this.angle=i,this.penumbra=r,this.decay=o,this.map=null,this.shadow=new Rm}get power(){return this.intensity*Math.PI}set power(t){this.intensity=t/Math.PI}dispose(){super.dispose(),this.shadow.dispose()}copy(t,e){return super.copy(t,e),this.distance=t.distance,this.angle=t.angle,this.penumbra=t.penumbra,this.decay=t.decay,this.target=t.target.clone(),this.map=t.map,this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.distance=this.distance,e.object.angle=this.angle,e.object.decay=this.decay,e.object.penumbra=this.penumbra,e.object.target=this.target.uuid,this.map&&this.map.isTexture&&(e.object.map=this.map.toJSON(t).uuid),e.object.shadow=this.shadow.toJSON(),e}}class Lm extends Ol{constructor(){super(new Ke(90,1,.5,500)),this.isPointLightShadow=!0}}class kl extends ho{constructor(t,e,n=0,i=2){super(t,e),this.isPointLight=!0,this.type="PointLight",this.distance=n,this.decay=i,this.shadow=new Lm}get power(){return this.intensity*4*Math.PI}set power(t){this.intensity=t/(4*Math.PI)}dispose(){super.dispose(),this.shadow.dispose()}copy(t,e){return super.copy(t,e),this.distance=t.distance,this.decay=t.decay,this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.distance=this.distance,e.object.decay=this.decay,e.object.shadow=this.shadow.toJSON(),e}}class po extends jh{constructor(t=-1,e=1,n=1,i=-1,r=.1,o=2e3){super(),this.isOrthographicCamera=!0,this.type="OrthographicCamera",this.zoom=1,this.view=null,this.left=t,this.right=e,this.top=n,this.bottom=i,this.near=r,this.far=o,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.left=t.left,this.right=t.right,this.top=t.top,this.bottom=t.bottom,this.near=t.near,this.far=t.far,this.zoom=t.zoom,this.view=t.view===null?null:Object.assign({},t.view),this}setViewOffset(t,e,n,i,r,o){this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=i,this.view.width=r,this.view.height=o,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=(this.right-this.left)/(2*this.zoom),e=(this.top-this.bottom)/(2*this.zoom),n=(this.right+this.left)/2,i=(this.top+this.bottom)/2;let r=n-t,o=n+t,a=i+e,l=i-e;if(this.view!==null&&this.view.enabled){const c=(this.right-this.left)/this.view.fullWidth/this.zoom,u=(this.top-this.bottom)/this.view.fullHeight/this.zoom;r+=c*this.view.offsetX,o=r+c*this.view.width,a-=u*this.view.offsetY,l=a-u*this.view.height}this.projectionMatrix.makeOrthographic(r,o,a,l,this.near,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.zoom=this.zoom,e.object.left=this.left,e.object.right=this.right,e.object.top=this.top,e.object.bottom=this.bottom,e.object.near=this.near,e.object.far=this.far,this.view!==null&&(e.object.view=Object.assign({},this.view)),e}}class Dm extends Ol{constructor(){super(new po(-5,5,5,-5,.5,500)),this.isDirectionalLightShadow=!0}}class Im extends ho{constructor(t,e){super(t,e),this.isDirectionalLight=!0,this.type="DirectionalLight",this.position.copy(Ee.DEFAULT_UP),this.updateMatrix(),this.target=new Ee,this.shadow=new Dm}dispose(){super.dispose(),this.shadow.dispose()}copy(t){return super.copy(t),this.target=t.target.clone(),this.shadow=t.shadow.clone(),this}toJSON(t){const e=super.toJSON(t);return e.object.shadow=this.shadow.toJSON(),e.object.target=this.target.uuid,e}}const Cs=-90,Rs=1;class Nm extends Ee{constructor(t,e,n){super(),this.type="CubeCamera",this.renderTarget=n,this.coordinateSystem=null,this.activeMipmapLevel=0;const i=new Ke(Cs,Rs,t,e);i.layers=this.layers,this.add(i);const r=new Ke(Cs,Rs,t,e);r.layers=this.layers,this.add(r);const o=new Ke(Cs,Rs,t,e);o.layers=this.layers,this.add(o);const a=new Ke(Cs,Rs,t,e);a.layers=this.layers,this.add(a);const l=new Ke(Cs,Rs,t,e);l.layers=this.layers,this.add(l);const c=new Ke(Cs,Rs,t,e);c.layers=this.layers,this.add(c)}updateCoordinateSystem(){const t=this.coordinateSystem,e=this.children.concat(),[n,i,r,o,a,l]=e;for(const c of e)this.remove(c);if(t===Pn)n.up.set(0,1,0),n.lookAt(1,0,0),i.up.set(0,1,0),i.lookAt(-1,0,0),r.up.set(0,0,-1),r.lookAt(0,1,0),o.up.set(0,0,1),o.lookAt(0,-1,0),a.up.set(0,1,0),a.lookAt(0,0,1),l.up.set(0,1,0),l.lookAt(0,0,-1);else if(t===Ks)n.up.set(0,-1,0),n.lookAt(-1,0,0),i.up.set(0,-1,0),i.lookAt(1,0,0),r.up.set(0,0,1),r.lookAt(0,1,0),o.up.set(0,0,-1),o.lookAt(0,-1,0),a.up.set(0,-1,0),a.lookAt(0,0,1),l.up.set(0,-1,0),l.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+t);for(const c of e)this.add(c),c.updateMatrixWorld()}update(t,e){this.parent===null&&this.updateMatrixWorld();const{renderTarget:n,activeMipmapLevel:i}=this;this.coordinateSystem!==t.coordinateSystem&&(this.coordinateSystem=t.coordinateSystem,this.updateCoordinateSystem());const[r,o,a,l,c,u]=this.children,h=t.getRenderTarget(),d=t.getActiveCubeFace(),f=t.getActiveMipmapLevel(),p=t.xr.enabled;t.xr.enabled=!1;const v=n.texture.generateMipmaps;n.texture.generateMipmaps=!1;let m=!1;t.isWebGLRenderer===!0?m=t.state.buffers.depth.getReversed():m=t.reversedDepthBuffer,t.setRenderTarget(n,0,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,r),t.setRenderTarget(n,1,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,o),t.setRenderTarget(n,2,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,a),t.setRenderTarget(n,3,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,l),t.setRenderTarget(n,4,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,c),n.texture.generateMipmaps=v,t.setRenderTarget(n,5,i),m&&t.autoClear===!1&&t.clearDepth(),t.render(e,u),t.setRenderTarget(h,d,f),t.xr.enabled=p,n.texture.needsPMREMUpdate=!0}}class Um extends Ke{constructor(t=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=t}}class Fm{constructor(){this._previousTime=0,this._currentTime=0,this._startTime=performance.now(),this._delta=0,this._elapsed=0,this._timescale=1,this._document=null,this._pageVisibilityHandler=null}connect(t){this._document=t,t.hidden!==void 0&&(this._pageVisibilityHandler=Om.bind(this),t.addEventListener("visibilitychange",this._pageVisibilityHandler,!1))}disconnect(){this._pageVisibilityHandler!==null&&(this._document.removeEventListener("visibilitychange",this._pageVisibilityHandler),this._pageVisibilityHandler=null),this._document=null}getDelta(){return this._delta/1e3}getElapsed(){return this._elapsed/1e3}getTimescale(){return this._timescale}setTimescale(t){return this._timescale=t,this}reset(){return this._currentTime=performance.now()-this._startTime,this}dispose(){this.disconnect()}update(t){return this._pageVisibilityHandler!==null&&this._document.hidden===!0?this._delta=0:(this._previousTime=this._currentTime,this._currentTime=(t!==void 0?t:performance.now())-this._startTime,this._delta=(this._currentTime-this._previousTime)*this._timescale,this._elapsed+=this._delta),this}}function Om(){this._document.hidden===!1&&this.reset()}const eu=new re;class km{constructor(t,e,n=0,i=1/0){this.ray=new Kr(t,e),this.near=n,this.far=i,this.camera=null,this.layers=new hl,this.params={Mesh:{},Line:{threshold:1},LOD:{},Points:{threshold:1},Sprite:{}}}set(t,e){this.ray.set(t,e)}setFromCamera(t,e){e.isPerspectiveCamera?(this.ray.origin.setFromMatrixPosition(e.matrixWorld),this.ray.direction.set(t.x,t.y,.5).unproject(e).sub(this.ray.origin).normalize(),this.camera=e):e.isOrthographicCamera?(this.ray.origin.set(t.x,t.y,e.projectionMatrix.elements[14]).unproject(e),this.ray.direction.set(0,0,-1).transformDirection(e.matrixWorld),this.camera=e):te("Raycaster: Unsupported camera type: "+e.type)}setFromXRController(t){return eu.identity().extractRotation(t.matrixWorld),this.ray.origin.setFromMatrixPosition(t.matrixWorld),this.ray.direction.set(0,0,-1).applyMatrix4(eu),this}intersectObject(t,e=!0,n=[]){return Bl(t,this,n,e),n.sort(nu),n}intersectObjects(t,e=!0,n=[]){for(let i=0,r=t.length;if.start-p.start);let d=0;for(let f=1;ff.start-p.start);let d=0;for(let f=1;fyn in xi?xv(xi,yn,{enumerable:!0 : cases.z; return clamp( threshold , 1.0e-6, 1.0 ); } -#endif`,bp=`#ifdef USE_ALPHAMAP +#endif`,$m=`#ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, vAlphaMapUv ).g; -#endif`,wp=`#ifdef USE_ALPHAMAP +#endif`,Xm=`#ifdef USE_ALPHAMAP uniform sampler2D alphaMap; -#endif`,Ep=`#ifdef USE_ALPHATEST +#endif`,qm=`#ifdef USE_ALPHATEST #ifdef ALPHA_TO_COVERAGE diffuseColor.a = smoothstep( alphaTest, alphaTest + fwidth( diffuseColor.a ), diffuseColor.a ); if ( diffuseColor.a == 0.0 ) discard; #else if ( diffuseColor.a < alphaTest ) discard; #endif -#endif`,Tp=`#ifdef USE_ALPHATEST +#endif`,Ym=`#ifdef USE_ALPHATEST uniform float alphaTest; -#endif`,Ap=`#ifdef USE_AOMAP +#endif`,Km=`#ifdef USE_AOMAP float ambientOcclusion = ( texture2D( aoMap, vAoMapUv ).r - 1.0 ) * aoMapIntensity + 1.0; reflectedLight.indirectDiffuse *= ambientOcclusion; #if defined( USE_CLEARCOAT ) @@ -73,10 +73,10 @@ var xv=Object.defineProperty;var vv=(xi,yn,Sn)=>yn in xi?xv(xi,yn,{enumerable:!0 float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.roughness ); #endif -#endif`,Cp=`#ifdef USE_AOMAP +#endif`,Zm=`#ifdef USE_AOMAP uniform sampler2D aoMap; uniform float aoMapIntensity; -#endif`,Rp=`#ifdef USE_BATCHING +#endif`,Jm=`#ifdef USE_BATCHING #if ! defined( GL_ANGLE_multi_draw ) #define gl_DrawID _gl_DrawID uniform int _gl_DrawID; @@ -110,15 +110,15 @@ var xv=Object.defineProperty;var vv=(xi,yn,Sn)=>yn in xi?xv(xi,yn,{enumerable:!0 int y = j / size; return texelFetch( batchingColorTexture, ivec2( x, y ), 0 ); } -#endif`,Pp=`#ifdef USE_BATCHING +#endif`,jm=`#ifdef USE_BATCHING mat4 batchingMatrix = getBatchingMatrix( getIndirectIndex( gl_DrawID ) ); -#endif`,Lp=`vec3 transformed = vec3( position ); +#endif`,Qm=`vec3 transformed = vec3( position ); #ifdef USE_ALPHAHASH vPosition = vec3( position ); -#endif`,Dp=`vec3 objectNormal = vec3( normal ); +#endif`,t0=`vec3 objectNormal = vec3( normal ); #ifdef USE_TANGENT vec3 objectTangent = vec3( tangent.xyz ); -#endif`,Ip=`float G_BlinnPhong_Implicit( ) { +#endif`,e0=`float G_BlinnPhong_Implicit( ) { return 0.25; } float D_BlinnPhong( const in float shininess, const in float dotNH ) { @@ -132,7 +132,7 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve float G = G_BlinnPhong_Implicit( ); float D = D_BlinnPhong( shininess, dotNH ); return F * ( G * D ); -} // validated`,Np=`#ifdef USE_IRIDESCENCE +} // validated`,n0=`#ifdef USE_IRIDESCENCE const mat3 XYZ_TO_REC709 = mat3( 3.2404542, -0.9692660, 0.0556434, -1.5371385, 1.8760108, -0.2040259, @@ -195,7 +195,7 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve } return max( I, vec3( 0.0 ) ); } -#endif`,Up=`#ifdef USE_BUMPMAP +#endif`,i0=`#ifdef USE_BUMPMAP uniform sampler2D bumpMap; uniform float bumpScale; vec2 dHdxy_fwd() { @@ -216,7 +216,7 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 ); return normalize( abs( fDet ) * surf_norm - vGrad ); } -#endif`,Fp=`#if NUM_CLIPPING_PLANES > 0 +#endif`,s0=`#if NUM_CLIPPING_PLANES > 0 vec4 plane; #ifdef ALPHA_TO_COVERAGE float distanceToPlane, distanceGradient; @@ -262,20 +262,20 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve if ( clipped ) discard; #endif #endif -#endif`,Op=`#if NUM_CLIPPING_PLANES > 0 +#endif`,r0=`#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; -#endif`,kp=`#if NUM_CLIPPING_PLANES > 0 +#endif`,o0=`#if NUM_CLIPPING_PLANES > 0 varying vec3 vClipPosition; -#endif`,Bp=`#if NUM_CLIPPING_PLANES > 0 +#endif`,a0=`#if NUM_CLIPPING_PLANES > 0 vClipPosition = - mvPosition.xyz; -#endif`,zp=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) +#endif`,l0=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) diffuseColor *= vColor; -#endif`,Hp=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) +#endif`,c0=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) varying vec4 vColor; -#endif`,Gp=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) +#endif`,h0=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) varying vec4 vColor; -#endif`,Vp=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) +#endif`,u0=`#if defined( USE_COLOR ) || defined( USE_COLOR_ALPHA ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) vColor = vec4( 1.0 ); #endif #ifdef USE_COLOR_ALPHA @@ -288,7 +288,7 @@ vec3 BRDF_BlinnPhong( const in vec3 lightDir, const in vec3 viewDir, const in ve #endif #ifdef USE_BATCHING_COLOR vColor *= getBatchingColor( getIndirectIndex( gl_DrawID ) ); -#endif`,Wp=`#define PI 3.141592653589793 +#endif`,d0=`#define PI 3.141592653589793 #define PI2 6.283185307179586 #define PI_HALF 1.5707963267948966 #define RECIPROCAL_PI 0.3183098861837907 @@ -359,7 +359,7 @@ vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); -} // validated`,$p=`#ifdef ENVMAP_TYPE_CUBE_UV +} // validated`,f0=`#ifdef ENVMAP_TYPE_CUBE_UV #define cubeUV_minMipLevel 4.0 #define cubeUV_minTileSize 16.0 float getFace( vec3 direction ) { @@ -452,7 +452,7 @@ float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { return vec4( mix( color0, color1, mipF ), 1.0 ); } } -#endif`,Xp=`vec3 transformedNormal = objectNormal; +#endif`,p0=`vec3 transformedNormal = objectNormal; #ifdef USE_TANGENT vec3 transformedTangent = objectTangent; #endif @@ -478,21 +478,21 @@ transformedNormal = normalMatrix * transformedNormal; #endif #ifdef USE_TANGENT transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; -#endif`,qp=`#ifdef USE_DISPLACEMENTMAP +#endif`,m0=`#ifdef USE_DISPLACEMENTMAP uniform sampler2D displacementMap; uniform float displacementScale; uniform float displacementBias; -#endif`,Yp=`#ifdef USE_DISPLACEMENTMAP +#endif`,g0=`#ifdef USE_DISPLACEMENTMAP transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); -#endif`,Kp=`#ifdef USE_EMISSIVEMAP +#endif`,v0=`#ifdef USE_EMISSIVEMAP vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE emissiveColor = sRGBTransferEOTF( emissiveColor ); #endif totalEmissiveRadiance *= emissiveColor.rgb; -#endif`,Zp=`#ifdef USE_EMISSIVEMAP +#endif`,x0=`#ifdef USE_EMISSIVEMAP uniform sampler2D emissiveMap; -#endif`,Jp="gl_FragColor = linearToOutputTexel( gl_FragColor );",Qp=`vec4 LinearTransferOETF( in vec4 value ) { +#endif`,_0="gl_FragColor = linearToOutputTexel( gl_FragColor );",y0=`vec4 LinearTransferOETF( in vec4 value ) { return value; } vec4 sRGBTransferEOTF( in vec4 value ) { @@ -500,7 +500,7 @@ vec4 sRGBTransferEOTF( in vec4 value ) { } vec4 sRGBTransferOETF( in vec4 value ) { return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); -}`,jp=`#ifdef USE_ENVMAP +}`,M0=`#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vec3 cameraToFrag; if ( isOrthographic ) { @@ -527,7 +527,7 @@ vec4 sRGBTransferOETF( in vec4 value ) { outgoingLight += envColor.xyz * specularStrength * reflectivity; #endif #endif -#endif`,tm=`#ifdef USE_ENVMAP +#endif`,b0=`#ifdef USE_ENVMAP uniform float envMapIntensity; uniform mat3 envMapRotation; #ifdef ENVMAP_TYPE_CUBE @@ -535,7 +535,7 @@ vec4 sRGBTransferOETF( in vec4 value ) { #else uniform sampler2D envMap; #endif -#endif`,em=`#ifdef USE_ENVMAP +#endif`,S0=`#ifdef USE_ENVMAP uniform float reflectivity; #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS @@ -546,7 +546,7 @@ vec4 sRGBTransferOETF( in vec4 value ) { #else varying vec3 vReflect; #endif -#endif`,nm=`#ifdef USE_ENVMAP +#endif`,w0=`#ifdef USE_ENVMAP #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) #define ENV_WORLDPOS #endif @@ -557,7 +557,7 @@ vec4 sRGBTransferOETF( in vec4 value ) { varying vec3 vReflect; uniform float refractionRatio; #endif -#endif`,im=`#ifdef USE_ENVMAP +#endif`,E0=`#ifdef USE_ENVMAP #ifdef ENV_WORLDPOS vWorldPosition = worldPosition.xyz; #else @@ -574,18 +574,18 @@ vec4 sRGBTransferOETF( in vec4 value ) { vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); #endif #endif -#endif`,sm=`#ifdef USE_FOG +#endif`,T0=`#ifdef USE_FOG vFogDepth = - mvPosition.z; -#endif`,rm=`#ifdef USE_FOG +#endif`,A0=`#ifdef USE_FOG varying float vFogDepth; -#endif`,am=`#ifdef USE_FOG +#endif`,C0=`#ifdef USE_FOG #ifdef FOG_EXP2 float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); #else float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); #endif gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); -#endif`,om=`#ifdef USE_FOG +#endif`,R0=`#ifdef USE_FOG uniform vec3 fogColor; varying float vFogDepth; #ifdef FOG_EXP2 @@ -594,7 +594,7 @@ vec4 sRGBTransferOETF( in vec4 value ) { uniform float fogNear; uniform float fogFar; #endif -#endif`,lm=`#ifdef USE_GRADIENTMAP +#endif`,P0=`#ifdef USE_GRADIENTMAP uniform sampler2D gradientMap; #endif vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { @@ -606,12 +606,12 @@ vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { vec2 fw = fwidth( coord ) * 0.5; return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); #endif -}`,cm=`#ifdef USE_LIGHTMAP +}`,L0=`#ifdef USE_LIGHTMAP uniform sampler2D lightMap; uniform float lightMapIntensity; -#endif`,hm=`LambertMaterial material; +#endif`,D0=`LambertMaterial material; material.diffuseColor = diffuseColor.rgb; -material.specularStrength = specularStrength;`,um=`varying vec3 vViewPosition; +material.specularStrength = specularStrength;`,I0=`varying vec3 vViewPosition; struct LambertMaterial { vec3 diffuseColor; float specularStrength; @@ -625,7 +625,7 @@ void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometr reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Lambert -#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,dm=`uniform bool receiveShadow; +#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,N0=`uniform bool receiveShadow; uniform vec3 ambientLightColor; #if defined( USE_LIGHT_PROBES ) uniform vec3 lightProbe[ 9 ]; @@ -742,7 +742,7 @@ float getSpotAttenuation( const in float coneCosine, const in float penumbraCosi return irradiance; } #endif -#include `,fm=`#ifdef USE_ENVMAP +#include `,U0=`#ifdef USE_ENVMAP vec3 getIBLIrradiance( const in vec3 normal ) { #ifdef ENVMAP_TYPE_CUBE_UV vec3 worldNormal = transformNormalByInverseViewMatrix( normal, viewMatrix ); @@ -775,8 +775,8 @@ float getSpotAttenuation( const in float coneCosine, const in float penumbraCosi #endif } #endif -#endif`,pm=`ToonMaterial material; -material.diffuseColor = diffuseColor.rgb;`,mm=`varying vec3 vViewPosition; +#endif`,F0=`ToonMaterial material; +material.diffuseColor = diffuseColor.rgb;`,O0=`varying vec3 vViewPosition; struct ToonMaterial { vec3 diffuseColor; }; @@ -788,11 +788,11 @@ void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPo reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_Toon -#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,gm=`BlinnPhongMaterial material; +#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,k0=`BlinnPhongMaterial material; material.diffuseColor = diffuseColor.rgb; material.specularColor = specular; material.specularShininess = shininess; -material.specularStrength = specularStrength;`,_m=`varying vec3 vViewPosition; +material.specularStrength = specularStrength;`,B0=`varying vec3 vViewPosition; struct BlinnPhongMaterial { vec3 diffuseColor; vec3 specularColor; @@ -809,7 +809,7 @@ void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geom reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); } #define RE_Direct RE_Direct_BlinnPhong -#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,xm=`PhysicalMaterial material; +#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,z0=`PhysicalMaterial material; material.diffuseColor = diffuseColor.rgb; material.diffuseContribution = diffuseColor.rgb * ( 1.0 - metalnessFactor ); material.metalness = metalnessFactor; @@ -899,7 +899,7 @@ material.roughness = min( material.roughness, 1.0 ); material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; -#endif`,vm=`uniform sampler2D dfgLUT; +#endif`,H0=`uniform sampler2D dfgLUT; struct PhysicalMaterial { vec3 diffuseColor; vec3 diffuseContribution; @@ -1259,7 +1259,7 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia #define RE_IndirectSpecular RE_IndirectSpecular_Physical float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); -}`,Mm=` +}`,G0=` vec3 geometryPosition = - vViewPosition; vec3 geometryNormal = normal; vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); @@ -1381,7 +1381,7 @@ IncidentLight directLight; #if defined( RE_IndirectSpecular ) vec3 radiance = vec3( 0.0 ); vec3 clearcoatRadiance = vec3( 0.0 ); -#endif`,ym=`#if defined( RE_IndirectDiffuse ) +#endif`,V0=`#if defined( RE_IndirectDiffuse ) #ifdef USE_LIGHTMAP vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; @@ -1402,7 +1402,7 @@ IncidentLight directLight; #ifdef USE_CLEARCOAT clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); #endif -#endif`,Sm=`#if defined( RE_IndirectDiffuse ) +#endif`,W0=`#if defined( RE_IndirectDiffuse ) #if defined( LAMBERT ) || defined( PHONG ) irradiance += iblIrradiance; #endif @@ -1410,7 +1410,7 @@ IncidentLight directLight; #endif #if defined( RE_IndirectSpecular ) RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); -#endif`,bm=`#ifdef USE_LIGHT_PROBES_GRID +#endif`,$0=`#ifdef USE_LIGHT_PROBES_GRID uniform highp sampler3D probesSH; uniform vec3 probesMin; uniform vec3 probesMax; @@ -1455,27 +1455,27 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { result += c8 * 0.429043 * ( x * x - y * y ); return max( result, vec3( 0.0 ) ); } -#endif`,wm=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) +#endif`,X0=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; -#endif`,Em=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) +#endif`,q0=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) uniform float logDepthBufFC; varying float vFragDepth; varying float vIsPerspective; -#endif`,Tm=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER +#endif`,Y0=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER varying float vFragDepth; varying float vIsPerspective; -#endif`,Am=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER +#endif`,K0=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER vFragDepth = 1.0 + gl_Position.w; vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); -#endif`,Cm=`#ifdef USE_MAP +#endif`,Z0=`#ifdef USE_MAP vec4 sampledDiffuseColor = texture2D( map, vMapUv ); #ifdef DECODE_VIDEO_TEXTURE sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); #endif diffuseColor *= sampledDiffuseColor; -#endif`,Rm=`#ifdef USE_MAP +#endif`,J0=`#ifdef USE_MAP uniform sampler2D map; -#endif`,Pm=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) +#endif`,j0=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) #if defined( USE_POINTS_UV ) vec2 uv = vUv; #else @@ -1487,7 +1487,7 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { #endif #ifdef USE_ALPHAMAP diffuseColor.a *= texture2D( alphaMap, uv ).g; -#endif`,Lm=`#if defined( USE_POINTS_UV ) +#endif`,Q0=`#if defined( USE_POINTS_UV ) varying vec2 vUv; #else #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) @@ -1499,19 +1499,19 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { #endif #ifdef USE_ALPHAMAP uniform sampler2D alphaMap; -#endif`,Dm=`float metalnessFactor = metalness; +#endif`,tg=`float metalnessFactor = metalness; #ifdef USE_METALNESSMAP vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); metalnessFactor *= texelMetalness.b; -#endif`,Im=`#ifdef USE_METALNESSMAP +#endif`,eg=`#ifdef USE_METALNESSMAP uniform sampler2D metalnessMap; -#endif`,Nm=`#ifdef USE_INSTANCING_MORPH +#endif`,ng=`#ifdef USE_INSTANCING_MORPH float morphTargetInfluences[ MORPHTARGETS_COUNT ]; float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; } -#endif`,Um=`#if defined( USE_MORPHCOLORS ) +#endif`,ig=`#if defined( USE_MORPHCOLORS ) vColor *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { #if defined( USE_COLOR_ALPHA ) @@ -1520,12 +1520,12 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; #endif } -#endif`,Fm=`#ifdef USE_MORPHNORMALS +#endif`,sg=`#ifdef USE_MORPHNORMALS objectNormal *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; } -#endif`,Om=`#ifdef USE_MORPHTARGETS +#endif`,rg=`#ifdef USE_MORPHTARGETS #ifndef USE_INSTANCING_MORPH uniform float morphTargetBaseInfluence; uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; @@ -1539,12 +1539,12 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { ivec3 morphUV = ivec3( x, y, morphTargetIndex ); return texelFetch( morphTargetsTexture, morphUV, 0 ); } -#endif`,km=`#ifdef USE_MORPHTARGETS +#endif`,og=`#ifdef USE_MORPHTARGETS transformed *= morphTargetBaseInfluence; for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; } -#endif`,Bm=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; +#endif`,ag=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; #ifdef FLAT_SHADED vec3 fdx = dFdx( vViewPosition ); vec3 fdy = dFdy( vViewPosition ); @@ -1585,7 +1585,7 @@ vec3 getLightProbeGridIrradiance( vec3 worldPos, vec3 worldNormal ) { tbn2[1] *= faceDirection; #endif #endif -vec3 nonPerturbedNormal = normal;`,zm=`#ifdef USE_NORMALMAP_OBJECTSPACE +vec3 nonPerturbedNormal = normal;`,lg=`#ifdef USE_NORMALMAP_OBJECTSPACE normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; #ifdef FLIP_SIDED normal = - normal; @@ -1603,19 +1603,19 @@ vec3 nonPerturbedNormal = normal;`,zm=`#ifdef USE_NORMALMAP_OBJECTSPACE normal = normalize( tbn * mapN ); #elif defined( USE_BUMPMAP ) normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); -#endif`,Hm=`#ifndef FLAT_SHADED +#endif`,cg=`#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif -#endif`,Gm=`#ifndef FLAT_SHADED +#endif`,hg=`#ifndef FLAT_SHADED varying vec3 vNormal; #ifdef USE_TANGENT varying vec3 vTangent; varying vec3 vBitangent; #endif -#endif`,Vm=`#ifndef FLAT_SHADED +#endif`,ug=`#ifndef FLAT_SHADED vNormal = normalize( transformedNormal ); #ifdef USE_TANGENT vTangent = normalize( transformedTangent ); @@ -1624,7 +1624,7 @@ vec3 nonPerturbedNormal = normal;`,zm=`#ifdef USE_NORMALMAP_OBJECTSPACE vBitangent = - vBitangent; #endif #endif -#endif`,Wm=`#ifdef USE_NORMALMAP +#endif`,dg=`#ifdef USE_NORMALMAP uniform sampler2D normalMap; uniform vec2 normalScale; #endif @@ -1646,13 +1646,13 @@ vec3 nonPerturbedNormal = normal;`,zm=`#ifdef USE_NORMALMAP_OBJECTSPACE float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); return mat3( T * scale, B * scale, N ); } -#endif`,$m=`#ifdef USE_CLEARCOAT +#endif`,fg=`#ifdef USE_CLEARCOAT vec3 clearcoatNormal = nonPerturbedNormal; -#endif`,Xm=`#ifdef USE_CLEARCOAT_NORMALMAP +#endif`,pg=`#ifdef USE_CLEARCOAT_NORMALMAP vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; clearcoatMapN.xy *= clearcoatNormalScale; clearcoatNormal = normalize( tbn2 * clearcoatMapN ); -#endif`,qm=`#ifdef USE_CLEARCOATMAP +#endif`,mg=`#ifdef USE_CLEARCOATMAP uniform sampler2D clearcoatMap; #endif #ifdef USE_CLEARCOAT_NORMALMAP @@ -1661,18 +1661,18 @@ vec3 nonPerturbedNormal = normal;`,zm=`#ifdef USE_NORMALMAP_OBJECTSPACE #endif #ifdef USE_CLEARCOAT_ROUGHNESSMAP uniform sampler2D clearcoatRoughnessMap; -#endif`,Ym=`#ifdef USE_IRIDESCENCEMAP +#endif`,gg=`#ifdef USE_IRIDESCENCEMAP uniform sampler2D iridescenceMap; #endif #ifdef USE_IRIDESCENCE_THICKNESSMAP uniform sampler2D iridescenceThicknessMap; -#endif`,Km=`#ifdef OPAQUE +#endif`,vg=`#ifdef OPAQUE diffuseColor.a = 1.0; #endif #ifdef USE_TRANSMISSION diffuseColor.a *= material.transmissionAlpha; #endif -gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,Zm=`vec3 packNormalToRGB( const in vec3 normal ) { +gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,xg=`vec3 packNormalToRGB( const in vec3 normal ) { return normalize( normal ) * 0.5 + 0.5; } vec3 unpackRGBToNormal( const in vec3 rgb ) { @@ -1751,9 +1751,9 @@ float perspectiveDepthToViewZ( const in float depth, const in float near, const #else return ( near * far ) / ( ( far - near ) * depth - far ); #endif -}`,Jm=`#ifdef PREMULTIPLIED_ALPHA +}`,_g=`#ifdef PREMULTIPLIED_ALPHA gl_FragColor.rgb *= gl_FragColor.a; -#endif`,Qm=`vec4 mvPosition = vec4( transformed, 1.0 ); +#endif`,yg=`vec4 mvPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING mvPosition = batchingMatrix * mvPosition; #endif @@ -1761,22 +1761,22 @@ float perspectiveDepthToViewZ( const in float depth, const in float near, const mvPosition = instanceMatrix * mvPosition; #endif mvPosition = modelViewMatrix * mvPosition; -gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING +gl_Position = projectionMatrix * mvPosition;`,Mg=`#ifdef DITHERING gl_FragColor.rgb = dithering( gl_FragColor.rgb ); -#endif`,tg=`#ifdef DITHERING +#endif`,bg=`#ifdef DITHERING vec3 dithering( vec3 color ) { float grid_position = rand( gl_FragCoord.xy ); vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); return color + dither_shift_RGB; } -#endif`,eg=`float roughnessFactor = roughness; +#endif`,Sg=`float roughnessFactor = roughness; #ifdef USE_ROUGHNESSMAP vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); roughnessFactor *= texelRoughness.g; -#endif`,ng=`#ifdef USE_ROUGHNESSMAP +#endif`,wg=`#ifdef USE_ROUGHNESSMAP uniform sampler2D roughnessMap; -#endif`,ig=`#if NUM_SPOT_LIGHT_COORDS > 0 +#endif`,Eg=`#if NUM_SPOT_LIGHT_COORDS > 0 varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif #if NUM_SPOT_LIGHT_MAPS > 0 @@ -1976,7 +1976,7 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING } #endif #endif -#endif`,sg=`#if NUM_SPOT_LIGHT_COORDS > 0 +#endif`,Tg=`#if NUM_SPOT_LIGHT_COORDS > 0 uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; #endif @@ -2017,7 +2017,7 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING }; uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; #endif -#endif`,rg=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) +#endif`,Ag=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) #ifdef HAS_NORMAL vec3 shadowWorldNormal = transformNormalByInverseViewMatrix( transformedNormal, viewMatrix ); #else @@ -2053,7 +2053,7 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; } #pragma unroll_loop_end -#endif`,ag=`float getShadowMask() { +#endif`,Cg=`float getShadowMask() { float shadow = 1.0; #ifdef USE_SHADOWMAP #if NUM_DIR_LIGHT_SHADOWS > 0 @@ -2085,12 +2085,12 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING #endif #endif return shadow; -}`,og=`#ifdef USE_SKINNING +}`,Rg=`#ifdef USE_SKINNING mat4 boneMatX = getBoneMatrix( skinIndex.x ); mat4 boneMatY = getBoneMatrix( skinIndex.y ); mat4 boneMatZ = getBoneMatrix( skinIndex.z ); mat4 boneMatW = getBoneMatrix( skinIndex.w ); -#endif`,lg=`#ifdef USE_SKINNING +#endif`,Pg=`#ifdef USE_SKINNING uniform mat4 bindMatrix; uniform mat4 bindMatrixInverse; uniform highp sampler2D boneTexture; @@ -2105,7 +2105,7 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); return mat4( v1, v2, v3, v4 ); } -#endif`,cg=`#ifdef USE_SKINNING +#endif`,Lg=`#ifdef USE_SKINNING vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); vec4 skinned = vec4( 0.0 ); skinned += boneMatX * skinVertex * skinWeight.x; @@ -2113,7 +2113,7 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING skinned += boneMatZ * skinVertex * skinWeight.z; skinned += boneMatW * skinVertex * skinWeight.w; transformed = ( bindMatrixInverse * skinned ).xyz; -#endif`,hg=`#ifdef USE_SKINNING +#endif`,Dg=`#ifdef USE_SKINNING mat4 skinMatrix = mat4( 0.0 ); skinMatrix += skinWeight.x * boneMatX; skinMatrix += skinWeight.y * boneMatY; @@ -2124,17 +2124,17 @@ gl_Position = projectionMatrix * mvPosition;`,jm=`#ifdef DITHERING #ifdef USE_TANGENT objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; #endif -#endif`,ug=`float specularStrength; +#endif`,Ig=`float specularStrength; #ifdef USE_SPECULARMAP vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); specularStrength = texelSpecular.r; #else specularStrength = 1.0; -#endif`,dg=`#ifdef USE_SPECULARMAP +#endif`,Ng=`#ifdef USE_SPECULARMAP uniform sampler2D specularMap; -#endif`,fg=`#if defined( TONE_MAPPING ) +#endif`,Ug=`#if defined( TONE_MAPPING ) gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); -#endif`,pg=`#ifndef saturate +#endif`,Fg=`#ifndef saturate #define saturate( a ) clamp( a, 0.0, 1.0 ) #endif uniform float toneMappingExposure; @@ -2231,7 +2231,7 @@ vec3 NeutralToneMapping( vec3 color ) { float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); return mix( color, vec3( newPeak ), g ); } -vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISSION +vec3 CustomToneMapping( vec3 color ) { return color; }`,Og=`#ifdef USE_TRANSMISSION material.transmission = transmission; material.transmissionAlpha = 1.0; material.thickness = thickness; @@ -2252,7 +2252,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS material.attenuationColor, material.attenuationDistance ); material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); -#endif`,gg=`#ifdef USE_TRANSMISSION +#endif`,kg=`#ifdef USE_TRANSMISSION uniform float transmission; uniform float thickness; uniform float attenuationDistance; @@ -2378,7 +2378,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); } -#endif`,_g=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) +#endif`,Bg=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP @@ -2448,7 +2448,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; -#endif`,xg=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) +#endif`,zg=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) varying vec2 vUv; #endif #ifdef USE_MAP @@ -2542,7 +2542,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS #ifdef USE_THICKNESSMAP uniform mat3 thicknessMapTransform; varying vec2 vThicknessMapUv; -#endif`,vg=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) +#endif`,Hg=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) vUv = vec3( uv, 1 ).xy; #endif #ifdef USE_MAP @@ -2613,7 +2613,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS #endif #ifdef USE_THICKNESSMAP vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; -#endif`,Mg=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 +#endif`,Gg=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 vec4 worldPosition = vec4( transformed, 1.0 ); #ifdef USE_BATCHING worldPosition = batchingMatrix * worldPosition; @@ -2622,7 +2622,7 @@ vec3 CustomToneMapping( vec3 color ) { return color; }`,mg=`#ifdef USE_TRANSMISS worldPosition = instanceMatrix * worldPosition; #endif worldPosition = modelMatrix * worldPosition; -#endif`;const Xt={alphahash_fragment:yp,alphahash_pars_fragment:Sp,alphamap_fragment:bp,alphamap_pars_fragment:wp,alphatest_fragment:Ep,alphatest_pars_fragment:Tp,aomap_fragment:Ap,aomap_pars_fragment:Cp,batching_pars_vertex:Rp,batching_vertex:Pp,begin_vertex:Lp,beginnormal_vertex:Dp,bsdfs:Ip,iridescence_fragment:Np,bumpmap_pars_fragment:Up,clipping_planes_fragment:Fp,clipping_planes_pars_fragment:Op,clipping_planes_pars_vertex:kp,clipping_planes_vertex:Bp,color_fragment:zp,color_pars_fragment:Hp,color_pars_vertex:Gp,color_vertex:Vp,common:Wp,cube_uv_reflection_fragment:$p,defaultnormal_vertex:Xp,displacementmap_pars_vertex:qp,displacementmap_vertex:Yp,emissivemap_fragment:Kp,emissivemap_pars_fragment:Zp,colorspace_fragment:Jp,colorspace_pars_fragment:Qp,envmap_fragment:jp,envmap_common_pars_fragment:tm,envmap_pars_fragment:em,envmap_pars_vertex:nm,envmap_physical_pars_fragment:fm,envmap_vertex:im,fog_vertex:sm,fog_pars_vertex:rm,fog_fragment:am,fog_pars_fragment:om,gradientmap_pars_fragment:lm,lightmap_pars_fragment:cm,lights_lambert_fragment:hm,lights_lambert_pars_fragment:um,lights_pars_begin:dm,lights_toon_fragment:pm,lights_toon_pars_fragment:mm,lights_phong_fragment:gm,lights_phong_pars_fragment:_m,lights_physical_fragment:xm,lights_physical_pars_fragment:vm,lights_fragment_begin:Mm,lights_fragment_maps:ym,lights_fragment_end:Sm,lightprobes_pars_fragment:bm,logdepthbuf_fragment:wm,logdepthbuf_pars_fragment:Em,logdepthbuf_pars_vertex:Tm,logdepthbuf_vertex:Am,map_fragment:Cm,map_pars_fragment:Rm,map_particle_fragment:Pm,map_particle_pars_fragment:Lm,metalnessmap_fragment:Dm,metalnessmap_pars_fragment:Im,morphinstance_vertex:Nm,morphcolor_vertex:Um,morphnormal_vertex:Fm,morphtarget_pars_vertex:Om,morphtarget_vertex:km,normal_fragment_begin:Bm,normal_fragment_maps:zm,normal_pars_fragment:Hm,normal_pars_vertex:Gm,normal_vertex:Vm,normalmap_pars_fragment:Wm,clearcoat_normal_fragment_begin:$m,clearcoat_normal_fragment_maps:Xm,clearcoat_pars_fragment:qm,iridescence_pars_fragment:Ym,opaque_fragment:Km,packing:Zm,premultiplied_alpha_fragment:Jm,project_vertex:Qm,dithering_fragment:jm,dithering_pars_fragment:tg,roughnessmap_fragment:eg,roughnessmap_pars_fragment:ng,shadowmap_pars_fragment:ig,shadowmap_pars_vertex:sg,shadowmap_vertex:rg,shadowmask_pars_fragment:ag,skinbase_vertex:og,skinning_pars_vertex:lg,skinning_vertex:cg,skinnormal_vertex:hg,specularmap_fragment:ug,specularmap_pars_fragment:dg,tonemapping_fragment:fg,tonemapping_pars_fragment:pg,transmission_fragment:mg,transmission_pars_fragment:gg,uv_pars_fragment:_g,uv_pars_vertex:xg,uv_vertex:vg,worldpos_vertex:Mg,background_vert:`varying vec2 vUv; +#endif`;const Yt={alphahash_fragment:Vm,alphahash_pars_fragment:Wm,alphamap_fragment:$m,alphamap_pars_fragment:Xm,alphatest_fragment:qm,alphatest_pars_fragment:Ym,aomap_fragment:Km,aomap_pars_fragment:Zm,batching_pars_vertex:Jm,batching_vertex:jm,begin_vertex:Qm,beginnormal_vertex:t0,bsdfs:e0,iridescence_fragment:n0,bumpmap_pars_fragment:i0,clipping_planes_fragment:s0,clipping_planes_pars_fragment:r0,clipping_planes_pars_vertex:o0,clipping_planes_vertex:a0,color_fragment:l0,color_pars_fragment:c0,color_pars_vertex:h0,color_vertex:u0,common:d0,cube_uv_reflection_fragment:f0,defaultnormal_vertex:p0,displacementmap_pars_vertex:m0,displacementmap_vertex:g0,emissivemap_fragment:v0,emissivemap_pars_fragment:x0,colorspace_fragment:_0,colorspace_pars_fragment:y0,envmap_fragment:M0,envmap_common_pars_fragment:b0,envmap_pars_fragment:S0,envmap_pars_vertex:w0,envmap_physical_pars_fragment:U0,envmap_vertex:E0,fog_vertex:T0,fog_pars_vertex:A0,fog_fragment:C0,fog_pars_fragment:R0,gradientmap_pars_fragment:P0,lightmap_pars_fragment:L0,lights_lambert_fragment:D0,lights_lambert_pars_fragment:I0,lights_pars_begin:N0,lights_toon_fragment:F0,lights_toon_pars_fragment:O0,lights_phong_fragment:k0,lights_phong_pars_fragment:B0,lights_physical_fragment:z0,lights_physical_pars_fragment:H0,lights_fragment_begin:G0,lights_fragment_maps:V0,lights_fragment_end:W0,lightprobes_pars_fragment:$0,logdepthbuf_fragment:X0,logdepthbuf_pars_fragment:q0,logdepthbuf_pars_vertex:Y0,logdepthbuf_vertex:K0,map_fragment:Z0,map_pars_fragment:J0,map_particle_fragment:j0,map_particle_pars_fragment:Q0,metalnessmap_fragment:tg,metalnessmap_pars_fragment:eg,morphinstance_vertex:ng,morphcolor_vertex:ig,morphnormal_vertex:sg,morphtarget_pars_vertex:rg,morphtarget_vertex:og,normal_fragment_begin:ag,normal_fragment_maps:lg,normal_pars_fragment:cg,normal_pars_vertex:hg,normal_vertex:ug,normalmap_pars_fragment:dg,clearcoat_normal_fragment_begin:fg,clearcoat_normal_fragment_maps:pg,clearcoat_pars_fragment:mg,iridescence_pars_fragment:gg,opaque_fragment:vg,packing:xg,premultiplied_alpha_fragment:_g,project_vertex:yg,dithering_fragment:Mg,dithering_pars_fragment:bg,roughnessmap_fragment:Sg,roughnessmap_pars_fragment:wg,shadowmap_pars_fragment:Eg,shadowmap_pars_vertex:Tg,shadowmap_vertex:Ag,shadowmask_pars_fragment:Cg,skinbase_vertex:Rg,skinning_pars_vertex:Pg,skinning_vertex:Lg,skinnormal_vertex:Dg,specularmap_fragment:Ig,specularmap_pars_fragment:Ng,tonemapping_fragment:Ug,tonemapping_pars_fragment:Fg,transmission_fragment:Og,transmission_pars_fragment:kg,uv_pars_fragment:Bg,uv_pars_vertex:zg,uv_vertex:Hg,worldpos_vertex:Gg,background_vert:`varying vec2 vUv; uniform mat3 uvTransform; void main() { vUv = ( uvTransform * vec3( uv, 1 ) ).xy; @@ -3676,7 +3676,7 @@ void main() { #include #include #include -}`},mt={common:{diffuse:{value:new kt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new zt},alphaMap:{value:null},alphaMapTransform:{value:new zt},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new zt}},envmap:{envMap:{value:null},envMapRotation:{value:new zt},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new zt}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new zt}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new zt},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new zt},normalScale:{value:new ht(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new zt},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new zt}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new zt}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new zt}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new kt(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null},probesSH:{value:null},probesMin:{value:new C},probesMax:{value:new C},probesResolution:{value:new C}},points:{diffuse:{value:new kt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new zt},alphaTest:{value:0},uvTransform:{value:new zt}},sprite:{diffuse:{value:new kt(16777215)},opacity:{value:1},center:{value:new ht(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new zt},alphaMap:{value:null},alphaMapTransform:{value:new zt},alphaTest:{value:0}}},Pn={basic:{uniforms:$e([mt.common,mt.specularmap,mt.envmap,mt.aomap,mt.lightmap,mt.fog]),vertexShader:Xt.meshbasic_vert,fragmentShader:Xt.meshbasic_frag},lambert:{uniforms:$e([mt.common,mt.specularmap,mt.envmap,mt.aomap,mt.lightmap,mt.emissivemap,mt.bumpmap,mt.normalmap,mt.displacementmap,mt.fog,mt.lights,{emissive:{value:new kt(0)},envMapIntensity:{value:1}}]),vertexShader:Xt.meshlambert_vert,fragmentShader:Xt.meshlambert_frag},phong:{uniforms:$e([mt.common,mt.specularmap,mt.envmap,mt.aomap,mt.lightmap,mt.emissivemap,mt.bumpmap,mt.normalmap,mt.displacementmap,mt.fog,mt.lights,{emissive:{value:new kt(0)},specular:{value:new kt(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:Xt.meshphong_vert,fragmentShader:Xt.meshphong_frag},standard:{uniforms:$e([mt.common,mt.envmap,mt.aomap,mt.lightmap,mt.emissivemap,mt.bumpmap,mt.normalmap,mt.displacementmap,mt.roughnessmap,mt.metalnessmap,mt.fog,mt.lights,{emissive:{value:new kt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Xt.meshphysical_vert,fragmentShader:Xt.meshphysical_frag},toon:{uniforms:$e([mt.common,mt.aomap,mt.lightmap,mt.emissivemap,mt.bumpmap,mt.normalmap,mt.displacementmap,mt.gradientmap,mt.fog,mt.lights,{emissive:{value:new kt(0)}}]),vertexShader:Xt.meshtoon_vert,fragmentShader:Xt.meshtoon_frag},matcap:{uniforms:$e([mt.common,mt.bumpmap,mt.normalmap,mt.displacementmap,mt.fog,{matcap:{value:null}}]),vertexShader:Xt.meshmatcap_vert,fragmentShader:Xt.meshmatcap_frag},points:{uniforms:$e([mt.points,mt.fog]),vertexShader:Xt.points_vert,fragmentShader:Xt.points_frag},dashed:{uniforms:$e([mt.common,mt.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Xt.linedashed_vert,fragmentShader:Xt.linedashed_frag},depth:{uniforms:$e([mt.common,mt.displacementmap]),vertexShader:Xt.depth_vert,fragmentShader:Xt.depth_frag},normal:{uniforms:$e([mt.common,mt.bumpmap,mt.normalmap,mt.displacementmap,{opacity:{value:1}}]),vertexShader:Xt.meshnormal_vert,fragmentShader:Xt.meshnormal_frag},sprite:{uniforms:$e([mt.sprite,mt.fog]),vertexShader:Xt.sprite_vert,fragmentShader:Xt.sprite_frag},background:{uniforms:{uvTransform:{value:new zt},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:Xt.background_vert,fragmentShader:Xt.background_frag},backgroundCube:{uniforms:{envMap:{value:null},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new zt}},vertexShader:Xt.backgroundCube_vert,fragmentShader:Xt.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:Xt.cube_vert,fragmentShader:Xt.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:Xt.equirect_vert,fragmentShader:Xt.equirect_frag},distance:{uniforms:$e([mt.common,mt.displacementmap,{referencePosition:{value:new C},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:Xt.distance_vert,fragmentShader:Xt.distance_frag},shadow:{uniforms:$e([mt.lights,mt.fog,{color:{value:new kt(0)},opacity:{value:1}}]),vertexShader:Xt.shadow_vert,fragmentShader:Xt.shadow_frag}};Pn.physical={uniforms:$e([Pn.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new zt},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new zt},clearcoatNormalScale:{value:new ht(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new zt},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new zt},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new zt},sheen:{value:0},sheenColor:{value:new kt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new zt},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new zt},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new zt},transmissionSamplerSize:{value:new ht},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new zt},attenuationDistance:{value:0},attenuationColor:{value:new kt(0)},specularColor:{value:new kt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new zt},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new zt},anisotropyVector:{value:new ht},anisotropyMap:{value:null},anisotropyMapTransform:{value:new zt}}]),vertexShader:Xt.meshphysical_vert,fragmentShader:Xt.meshphysical_frag};const qr={r:0,b:0,g:0},yg=new se,mh=new zt;mh.set(-1,0,0,0,1,0,0,0,1);function Sg(s,t,e,n,i,r){const a=new kt(0);let o=i===!0?0:1,l,c,u=null,h=0,d=null;function f(w){let E=w.isScene===!0?w.background:null;if(E&&E.isTexture){const M=w.backgroundBlurriness>0;E=t.get(E,M)}return E}function p(w){let E=!1;const M=f(w);M===null?m(a,o):M&&M.isColor&&(m(M,1),E=!0);const S=s.xr.getEnvironmentBlendMode();S==="additive"?e.buffers.color.setClear(0,0,0,1,r):S==="alpha-blend"&&e.buffers.color.setClear(0,0,0,0,r),(s.autoClear||E)&&(e.buffers.depth.setTest(!0),e.buffers.depth.setMask(!0),e.buffers.color.setMask(!0),s.clear(s.autoClearColor,s.autoClearDepth,s.autoClearStencil))}function v(w,E){const M=f(E);M&&(M.isCubeTexture||M.mapping===or)?(c===void 0&&(c=new Bt(new tn(1,1,1),new Ce({name:"BackgroundCubeMaterial",uniforms:ds(Pn.backgroundCube.uniforms),vertexShader:Pn.backgroundCube.vertexShader,fragmentShader:Pn.backgroundCube.fragmentShader,side:Ge,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),c.geometry.deleteAttribute("uv"),c.onBeforeRender=function(S,b,R){this.matrixWorld.copyPosition(R.matrixWorld)},Object.defineProperty(c.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),n.update(c)),c.material.uniforms.envMap.value=M,c.material.uniforms.backgroundBlurriness.value=E.backgroundBlurriness,c.material.uniforms.backgroundIntensity.value=E.backgroundIntensity,c.material.uniforms.backgroundRotation.value.setFromMatrix4(yg.makeRotationFromEuler(E.backgroundRotation)).transpose(),M.isCubeTexture&&M.isRenderTargetTexture===!1&&c.material.uniforms.backgroundRotation.value.premultiply(mh),c.material.toneMapped=Kt.getTransfer(M.colorSpace)!==ie,(u!==M||h!==M.version||d!==s.toneMapping)&&(c.material.needsUpdate=!0,u=M,h=M.version,d=s.toneMapping),c.layers.enableAll(),w.unshift(c,c.geometry,c.material,0,0,null)):M&&M.isTexture&&(l===void 0&&(l=new Bt(new We(2,2),new Ce({name:"BackgroundMaterial",uniforms:ds(Pn.background.uniforms),vertexShader:Pn.background.vertexShader,fragmentShader:Pn.background.fragmentShader,side:jn,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),l.geometry.deleteAttribute("normal"),Object.defineProperty(l.material,"map",{get:function(){return this.uniforms.t2D.value}}),n.update(l)),l.material.uniforms.t2D.value=M,l.material.uniforms.backgroundIntensity.value=E.backgroundIntensity,l.material.toneMapped=Kt.getTransfer(M.colorSpace)!==ie,M.matrixAutoUpdate===!0&&M.updateMatrix(),l.material.uniforms.uvTransform.value.copy(M.matrix),(u!==M||h!==M.version||d!==s.toneMapping)&&(l.material.needsUpdate=!0,u=M,h=M.version,d=s.toneMapping),l.layers.enableAll(),w.unshift(l,l.geometry,l.material,0,0,null))}function m(w,E){w.getRGB(qr,ih(s)),e.buffers.color.setClear(qr.r,qr.g,qr.b,E,r)}function g(){c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0),l!==void 0&&(l.geometry.dispose(),l.material.dispose(),l=void 0)}return{getClearColor:function(){return a},setClearColor:function(w,E=1){a.set(w),o=E,m(a,o)},getClearAlpha:function(){return o},setClearAlpha:function(w){o=w,m(a,o)},render:p,addToRenderList:v,dispose:g}}function bg(s,t){const e=s.getParameter(s.MAX_VERTEX_ATTRIBS),n={},i=d(null);let r=i,a=!1;function o(L,F,q,Y,B){let K=!1;const X=h(L,Y,q,F);r!==X&&(r=X,c(r.object)),K=f(L,Y,q,B),K&&p(L,Y,q,B),B!==null&&t.update(B,s.ELEMENT_ARRAY_BUFFER),(K||a)&&(a=!1,M(L,F,q,Y),B!==null&&s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,t.get(B).buffer))}function l(){return s.createVertexArray()}function c(L){return s.bindVertexArray(L)}function u(L){return s.deleteVertexArray(L)}function h(L,F,q,Y){const B=Y.wireframe===!0;let K=n[F.id];K===void 0&&(K={},n[F.id]=K);const X=L.isInstancedMesh===!0?L.id:0;let Q=K[X];Q===void 0&&(Q={},K[X]=Q);let it=Q[q.id];it===void 0&&(it={},Q[q.id]=it);let pt=it[B];return pt===void 0&&(pt=d(l()),it[B]=pt),pt}function d(L){const F=[],q=[],Y=[];for(let B=0;B=0){const _t=B[it];let bt=K[it];if(bt===void 0&&(it==="instanceMatrix"&&L.instanceMatrix&&(bt=L.instanceMatrix),it==="instanceColor"&&L.instanceColor&&(bt=L.instanceColor)),_t===void 0||_t.attribute!==bt||bt&&_t.data!==bt.data)return!0;X++}return r.attributesNum!==X||r.index!==Y}function p(L,F,q,Y){const B={},K=F.attributes;let X=0;const Q=q.getAttributes();for(const it in Q)if(Q[it].location>=0){let _t=K[it];_t===void 0&&(it==="instanceMatrix"&&L.instanceMatrix&&(_t=L.instanceMatrix),it==="instanceColor"&&L.instanceColor&&(_t=L.instanceColor));const bt={};bt.attribute=_t,_t&&_t.data&&(bt.data=_t.data),B[it]=bt,X++}r.attributes=B,r.attributesNum=X,r.index=Y}function v(){const L=r.newAttributes;for(let F=0,q=L.length;F=0){let pt=B[Q];if(pt===void 0&&(Q==="instanceMatrix"&&L.instanceMatrix&&(pt=L.instanceMatrix),Q==="instanceColor"&&L.instanceColor&&(pt=L.instanceColor)),pt!==void 0){const _t=pt.normalized,bt=pt.itemSize,ee=t.get(pt);if(ee===void 0)continue;const pe=ee.buffer,jt=ee.type,j=ee.bytesPerElement,D=jt===s.INT||jt===s.UNSIGNED_INT||pt.gpuType===za;if(pt.isInterleavedBufferAttribute){const W=pt.data,st=W.stride,lt=pt.offset;if(W.isInstancedInterleavedBuffer){for(let rt=0;rt0&&s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.HIGH_FLOAT).precision>0)return"highp";R="mediump"}return R==="mediump"&&s.getShaderPrecisionFormat(s.VERTEX_SHADER,s.MEDIUM_FLOAT).precision>0&&s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let c=e.precision!==void 0?e.precision:"highp";const u=l(c);u!==c&&(Nt("WebGLRenderer:",c,"not supported, using",u,"instead."),c=u);const h=e.logarithmicDepthBuffer===!0,d=e.reversedDepthBuffer===!0&&t.has("EXT_clip_control");e.reversedDepthBuffer===!0&&d===!1&&Nt("WebGLRenderer: Unable to use reversed depth buffer due to missing EXT_clip_control extension. Fallback to default depth buffer.");const f=s.getParameter(s.MAX_TEXTURE_IMAGE_UNITS),p=s.getParameter(s.MAX_VERTEX_TEXTURE_IMAGE_UNITS),v=s.getParameter(s.MAX_TEXTURE_SIZE),m=s.getParameter(s.MAX_CUBE_MAP_TEXTURE_SIZE),g=s.getParameter(s.MAX_VERTEX_ATTRIBS),w=s.getParameter(s.MAX_VERTEX_UNIFORM_VECTORS),E=s.getParameter(s.MAX_VARYING_VECTORS),M=s.getParameter(s.MAX_FRAGMENT_UNIFORM_VECTORS),S=s.getParameter(s.MAX_SAMPLES),b=s.getParameter(s.SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:r,getMaxPrecision:l,textureFormatReadable:a,textureTypeReadable:o,precision:c,logarithmicDepthBuffer:h,reversedDepthBuffer:d,maxTextures:f,maxVertexTextures:p,maxTextureSize:v,maxCubemapSize:m,maxAttributes:g,maxVertexUniforms:w,maxVaryings:E,maxFragmentUniforms:M,maxSamples:S,samples:b}}function Tg(s){const t=this;let e=null,n=0,i=!1,r=!1;const a=new oi,o=new zt,l={value:null,needsUpdate:!1};this.uniform=l,this.numPlanes=0,this.numIntersection=0,this.init=function(h,d){const f=h.length!==0||d||n!==0||i;return i=d,n=h.length,f},this.beginShadows=function(){r=!0,u(null)},this.endShadows=function(){r=!1},this.setGlobalState=function(h,d){e=u(h,d,0)},this.setState=function(h,d,f){const p=h.clippingPlanes,v=h.clipIntersection,m=h.clipShadows,g=s.get(h);if(!i||p===null||p.length===0||r&&!m)r?u(null):c();else{const w=r?0:n,E=w*4;let M=g.clippingState||null;l.value=M,M=u(p,d,E,f);for(let S=0;S!==E;++S)M[S]=e[S];g.clippingState=M,this.numIntersection=v?this.numPlanes:0,this.numPlanes+=w}};function c(){l.value!==e&&(l.value=e,l.needsUpdate=n>0),t.numPlanes=n,t.numIntersection=0}function u(h,d,f,p){const v=h!==null?h.length:0;let m=null;if(v!==0){if(m=l.value,p!==!0||m===null){const g=f+v*4,w=d.matrixWorldInverse;o.getNormalMatrix(w),(m===null||m.length0&&this._blur(l,0,0,e),this._applyPMREM(l),this._cleanup(l),l}fromEquirectangular(t,e=null){return this._fromTexture(t,e)}fromCubemap(t,e=null){return this._fromTexture(t,e)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=yh(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=Mh(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose(),this._backgroundBox!==null&&(this._backgroundBox.geometry.dispose(),this._backgroundBox.material.dispose())}_setSize(t){this._lodMax=Math.floor(Math.log2(t)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._ggxMaterial!==null&&this._ggxMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let t=0;t2?S:0,S,S),h.setRenderTarget(i),g&&h.render(v,l),h.render(t,l)}h.toneMapping=f,h.autoClear=d,t.background=w}_textureToCubeUV(t,e){const n=this._renderer,i=t.mapping===Ei||t.mapping===$i;i?(this._cubemapMaterial===null&&(this._cubemapMaterial=yh()),this._cubemapMaterial.uniforms.flipEnvMap.value=t.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=Mh());const r=i?this._cubemapMaterial:this._equirectMaterial,a=this._lodMeshes[0];a.material=r;const o=r.uniforms;o.envMap.value=t;const l=this._cubeSize;ms(e,0,0,3*l,2*l),n.setRenderTarget(e),n.render(a,Xs)}_applyPMREM(t){const e=this._renderer,n=e.autoClear;e.autoClear=!1;const i=this._lodMeshes.length;for(let r=1;rp-ui?n-p+ui:0),g=4*(this._cubeSize-v);l.envMap.value=t.texture,l.roughness.value=f,l.mipInt.value=p-e,ms(r,m,g,3*v,2*v),i.setRenderTarget(r),i.render(o,Xs),l.envMap.value=r.texture,l.roughness.value=0,l.mipInt.value=p-n,ms(t,m,g,3*v,2*v),i.setRenderTarget(t),i.render(o,Xs)}_blur(t,e,n,i,r){const a=this._pingPongRenderTarget;this._halfBlur(t,a,e,n,i,"latitudinal",r),this._halfBlur(a,t,n,n,i,"longitudinal",r)}_halfBlur(t,e,n,i,r,a,o){const l=this._renderer,c=this._blurMaterial;a!=="latitudinal"&&a!=="longitudinal"&&Qt("blur direction must be either latitudinal or longitudinal!");const u=3,h=this._lodMeshes[i];h.material=c;const d=c.uniforms,f=this._sizeLods[n]-1,p=isFinite(r)?Math.PI/(2*f):2*Math.PI/(2*Ni-1),v=r/p,m=isFinite(r)?1+Math.floor(u*v):Ni;m>Ni&&Nt(`sigmaRadians, ${r}, is too large and will clip, as it requested ${m} samples when the maximum is set to ${Ni}`);const g=[];let w=0;for(let R=0;RE-ui?i-E+ui:0),b=4*(this._cubeSize-M);ms(e,S,b,3*M,2*M),l.setRenderTarget(e),l.render(h,Xs)}}function Rg(s){const t=[],e=[],n=[];let i=s;const r=s-ui+1+gh.length;for(let a=0;as-ui?l=gh[a-s+ui-1]:a===0&&(l=0),e.push(l);const c=1/(o-2),u=-c,h=1+c,d=[u,u,h,u,h,h,u,u,h,h,u,h],f=6,p=6,v=3,m=2,g=1,w=new Float32Array(v*p*f),E=new Float32Array(m*p*f),M=new Float32Array(g*p*f);for(let b=0;b2?0:-1,T=[R,x,0,R+2/3,x,0,R+2/3,x+1,0,R,x,0,R+2/3,x+1,0,R,x+1,0];w.set(T,v*p*b),E.set(d,m*p*b);const P=[b,b,b,b,b,b];M.set(P,g*p*b)}const S=new Be;S.setAttribute("position",new rn(w,v)),S.setAttribute("uv",new rn(E,m)),S.setAttribute("faceIndex",new rn(M,g)),n.push(new Bt(S,null)),i>ui&&i--}return{lodMeshes:n,sizeLods:t,sigmas:e}}function vh(s,t,e){const n=new Xe(s,t,e);return n.texture.mapping=or,n.texture.name="PMREM.cubeUv",n.scissorTest=!0,n}function ms(s,t,e,n,i){s.viewport.set(t,e,n,i),s.scissor.set(t,e,n,i)}function Pg(s,t,e){return new Ce({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:Ag,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${s}.0`},uniforms:{envMap:{value:null},roughness:{value:0},mipInt:{value:0}},vertexShader:Yr(),fragmentShader:` +}`},xt={common:{diffuse:{value:new Bt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new Ht},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new Ht}},envmap:{envMap:{value:null},envMapRotation:{value:new Ht},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new Ht}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new Ht}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new Ht},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new Ht},normalScale:{value:new ft(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new Ht},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new Ht}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new Ht}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new Ht}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new Bt(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null},probesSH:{value:null},probesMin:{value:new P},probesMax:{value:new P},probesResolution:{value:new P}},points:{diffuse:{value:new Bt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0},uvTransform:{value:new Ht}},sprite:{diffuse:{value:new Bt(16777215)},opacity:{value:1},center:{value:new ft(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new Ht},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0}}},In={basic:{uniforms:qe([xt.common,xt.specularmap,xt.envmap,xt.aomap,xt.lightmap,xt.fog]),vertexShader:Yt.meshbasic_vert,fragmentShader:Yt.meshbasic_frag},lambert:{uniforms:qe([xt.common,xt.specularmap,xt.envmap,xt.aomap,xt.lightmap,xt.emissivemap,xt.bumpmap,xt.normalmap,xt.displacementmap,xt.fog,xt.lights,{emissive:{value:new Bt(0)},envMapIntensity:{value:1}}]),vertexShader:Yt.meshlambert_vert,fragmentShader:Yt.meshlambert_frag},phong:{uniforms:qe([xt.common,xt.specularmap,xt.envmap,xt.aomap,xt.lightmap,xt.emissivemap,xt.bumpmap,xt.normalmap,xt.displacementmap,xt.fog,xt.lights,{emissive:{value:new Bt(0)},specular:{value:new Bt(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:Yt.meshphong_vert,fragmentShader:Yt.meshphong_frag},standard:{uniforms:qe([xt.common,xt.envmap,xt.aomap,xt.lightmap,xt.emissivemap,xt.bumpmap,xt.normalmap,xt.displacementmap,xt.roughnessmap,xt.metalnessmap,xt.fog,xt.lights,{emissive:{value:new Bt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Yt.meshphysical_vert,fragmentShader:Yt.meshphysical_frag},toon:{uniforms:qe([xt.common,xt.aomap,xt.lightmap,xt.emissivemap,xt.bumpmap,xt.normalmap,xt.displacementmap,xt.gradientmap,xt.fog,xt.lights,{emissive:{value:new Bt(0)}}]),vertexShader:Yt.meshtoon_vert,fragmentShader:Yt.meshtoon_frag},matcap:{uniforms:qe([xt.common,xt.bumpmap,xt.normalmap,xt.displacementmap,xt.fog,{matcap:{value:null}}]),vertexShader:Yt.meshmatcap_vert,fragmentShader:Yt.meshmatcap_frag},points:{uniforms:qe([xt.points,xt.fog]),vertexShader:Yt.points_vert,fragmentShader:Yt.points_frag},dashed:{uniforms:qe([xt.common,xt.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Yt.linedashed_vert,fragmentShader:Yt.linedashed_frag},depth:{uniforms:qe([xt.common,xt.displacementmap]),vertexShader:Yt.depth_vert,fragmentShader:Yt.depth_frag},normal:{uniforms:qe([xt.common,xt.bumpmap,xt.normalmap,xt.displacementmap,{opacity:{value:1}}]),vertexShader:Yt.meshnormal_vert,fragmentShader:Yt.meshnormal_frag},sprite:{uniforms:qe([xt.sprite,xt.fog]),vertexShader:Yt.sprite_vert,fragmentShader:Yt.sprite_frag},background:{uniforms:{uvTransform:{value:new Ht},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:Yt.background_vert,fragmentShader:Yt.background_frag},backgroundCube:{uniforms:{envMap:{value:null},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new Ht}},vertexShader:Yt.backgroundCube_vert,fragmentShader:Yt.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:Yt.cube_vert,fragmentShader:Yt.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:Yt.equirect_vert,fragmentShader:Yt.equirect_frag},distance:{uniforms:qe([xt.common,xt.displacementmap,{referencePosition:{value:new P},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:Yt.distance_vert,fragmentShader:Yt.distance_frag},shadow:{uniforms:qe([xt.lights,xt.fog,{color:{value:new Bt(0)},opacity:{value:1}}]),vertexShader:Yt.shadow_vert,fragmentShader:Yt.shadow_frag}};In.physical={uniforms:qe([In.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new Ht},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new Ht},clearcoatNormalScale:{value:new ft(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new Ht},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new Ht},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new Ht},sheen:{value:0},sheenColor:{value:new Bt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new Ht},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new Ht},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new Ht},transmissionSamplerSize:{value:new ft},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new Ht},attenuationDistance:{value:0},attenuationColor:{value:new Bt(0)},specularColor:{value:new Bt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new Ht},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new Ht},anisotropyVector:{value:new ft},anisotropyMap:{value:null},anisotropyMapTransform:{value:new Ht}}]),vertexShader:Yt.meshphysical_vert,fragmentShader:Yt.meshphysical_frag};const mo={r:0,b:0,g:0},Vg=new re,ou=new Ht;ou.set(-1,0,0,0,1,0,0,0,1);function Wg(s,t,e,n,i,r){const o=new Bt(0);let a=i===!0?0:1,l,c,u=null,h=0,d=null;function f(S){let E=S.isScene===!0?S.background:null;if(E&&E.isTexture){const y=S.backgroundBlurriness>0;E=t.get(E,y)}return E}function p(S){let E=!1;const y=f(S);y===null?m(o,a):y&&y.isColor&&(m(y,1),E=!0);const M=s.xr.getEnvironmentBlendMode();M==="additive"?e.buffers.color.setClear(0,0,0,1,r):M==="alpha-blend"&&e.buffers.color.setClear(0,0,0,0,r),(s.autoClear||E)&&(e.buffers.depth.setTest(!0),e.buffers.depth.setMask(!0),e.buffers.color.setMask(!0),s.clear(s.autoClearColor,s.autoClearDepth,s.autoClearStencil))}function v(S,E){const y=f(E);y&&(y.isCubeTexture||y.mapping===Cr)?(c===void 0&&(c=new zt(new nn(1,1,1),new Pe({name:"BackgroundCubeMaterial",uniforms:As(In.backgroundCube.uniforms),vertexShader:In.backgroundCube.vertexShader,fragmentShader:In.backgroundCube.fragmentShader,side:We,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),c.geometry.deleteAttribute("uv"),c.onBeforeRender=function(M,w,C){this.matrixWorld.copyPosition(C.matrixWorld)},Object.defineProperty(c.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),n.update(c)),c.material.uniforms.envMap.value=y,c.material.uniforms.backgroundBlurriness.value=E.backgroundBlurriness,c.material.uniforms.backgroundIntensity.value=E.backgroundIntensity,c.material.uniforms.backgroundRotation.value.setFromMatrix4(Vg.makeRotationFromEuler(E.backgroundRotation)).transpose(),y.isCubeTexture&&y.isRenderTargetTexture===!1&&c.material.uniforms.backgroundRotation.value.premultiply(ou),c.material.toneMapped=Jt.getTransfer(y.colorSpace)!==se,(u!==y||h!==y.version||d!==s.toneMapping)&&(c.material.needsUpdate=!0,u=y,h=y.version,d=s.toneMapping),c.layers.enableAll(),S.unshift(c,c.geometry,c.material,0,0,null)):y&&y.isTexture&&(l===void 0&&(l=new zt(new Xe(2,2),new Pe({name:"BackgroundMaterial",uniforms:As(In.background.uniforms),vertexShader:In.background.vertexShader,fragmentShader:In.background.fragmentShader,side:ai,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),l.geometry.deleteAttribute("normal"),Object.defineProperty(l.material,"map",{get:function(){return this.uniforms.t2D.value}}),n.update(l)),l.material.uniforms.t2D.value=y,l.material.uniforms.backgroundIntensity.value=E.backgroundIntensity,l.material.toneMapped=Jt.getTransfer(y.colorSpace)!==se,y.matrixAutoUpdate===!0&&y.updateMatrix(),l.material.uniforms.uvTransform.value.copy(y.matrix),(u!==y||h!==y.version||d!==s.toneMapping)&&(l.material.needsUpdate=!0,u=y,h=y.version,d=s.toneMapping),l.layers.enableAll(),S.unshift(l,l.geometry,l.material,0,0,null))}function m(S,E){S.getRGB(mo,Yh(s)),e.buffers.color.setClear(mo.r,mo.g,mo.b,E,r)}function g(){c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0),l!==void 0&&(l.geometry.dispose(),l.material.dispose(),l=void 0)}return{getClearColor:function(){return o},setClearColor:function(S,E=1){o.set(S),a=E,m(o,a)},getClearAlpha:function(){return a},setClearAlpha:function(S){a=S,m(o,a)},render:p,addToRenderList:v,dispose:g}}function $g(s,t){const e=s.getParameter(s.MAX_VERTEX_ATTRIBS),n={},i=d(null);let r=i,o=!1;function a(D,U,K,Z,N){let W=!1;const B=h(D,Z,K,U);r!==B&&(r=B,c(r.object)),W=f(D,Z,K,N),W&&p(D,Z,K,N),N!==null&&t.update(N,s.ELEMENT_ARRAY_BUFFER),(W||o)&&(o=!1,y(D,U,K,Z),N!==null&&s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,t.get(N).buffer))}function l(){return s.createVertexArray()}function c(D){return s.bindVertexArray(D)}function u(D){return s.deleteVertexArray(D)}function h(D,U,K,Z){const N=Z.wireframe===!0;let W=n[U.id];W===void 0&&(W={},n[U.id]=W);const B=D.isInstancedMesh===!0?D.id:0;let X=W[B];X===void 0&&(X={},W[B]=X);let nt=X[K.id];nt===void 0&&(nt={},X[K.id]=nt);let ot=nt[N];return ot===void 0&&(ot=d(l()),nt[N]=ot),ot}function d(D){const U=[],K=[],Z=[];for(let N=0;N=0){const at=N[nt];let vt=W[nt];if(vt===void 0&&(nt==="instanceMatrix"&&D.instanceMatrix&&(vt=D.instanceMatrix),nt==="instanceColor"&&D.instanceColor&&(vt=D.instanceColor)),at===void 0||at.attribute!==vt||vt&&at.data!==vt.data)return!0;B++}return r.attributesNum!==B||r.index!==Z}function p(D,U,K,Z){const N={},W=U.attributes;let B=0;const X=K.getAttributes();for(const nt in X)if(X[nt].location>=0){let at=W[nt];at===void 0&&(nt==="instanceMatrix"&&D.instanceMatrix&&(at=D.instanceMatrix),nt==="instanceColor"&&D.instanceColor&&(at=D.instanceColor));const vt={};vt.attribute=at,at&&at.data&&(vt.data=at.data),N[nt]=vt,B++}r.attributes=N,r.attributesNum=B,r.index=Z}function v(){const D=r.newAttributes;for(let U=0,K=D.length;U=0){let ot=N[X];if(ot===void 0&&(X==="instanceMatrix"&&D.instanceMatrix&&(ot=D.instanceMatrix),X==="instanceColor"&&D.instanceColor&&(ot=D.instanceColor)),ot!==void 0){const at=ot.normalized,vt=ot.itemSize,Gt=t.get(ot);if(Gt===void 0)continue;const me=Gt.buffer,ee=Gt.type,Q=Gt.bytesPerElement,I=ee===s.INT||ee===s.UNSIGNED_INT||ot.gpuType===xa;if(ot.isInterleavedBufferAttribute){const q=ot.data,st=q.stride,ut=ot.offset;if(q.isInstancedInterleavedBuffer){for(let lt=0;lt0&&s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.HIGH_FLOAT).precision>0)return"highp";C="mediump"}return C==="mediump"&&s.getShaderPrecisionFormat(s.VERTEX_SHADER,s.MEDIUM_FLOAT).precision>0&&s.getShaderPrecisionFormat(s.FRAGMENT_SHADER,s.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let c=e.precision!==void 0?e.precision:"highp";const u=l(c);u!==c&&(Ut("WebGLRenderer:",c,"not supported, using",u,"instead."),c=u);const h=e.logarithmicDepthBuffer===!0,d=e.reversedDepthBuffer===!0&&t.has("EXT_clip_control");e.reversedDepthBuffer===!0&&d===!1&&Ut("WebGLRenderer: Unable to use reversed depth buffer due to missing EXT_clip_control extension. Fallback to default depth buffer.");const f=s.getParameter(s.MAX_TEXTURE_IMAGE_UNITS),p=s.getParameter(s.MAX_VERTEX_TEXTURE_IMAGE_UNITS),v=s.getParameter(s.MAX_TEXTURE_SIZE),m=s.getParameter(s.MAX_CUBE_MAP_TEXTURE_SIZE),g=s.getParameter(s.MAX_VERTEX_ATTRIBS),S=s.getParameter(s.MAX_VERTEX_UNIFORM_VECTORS),E=s.getParameter(s.MAX_VARYING_VECTORS),y=s.getParameter(s.MAX_FRAGMENT_UNIFORM_VECTORS),M=s.getParameter(s.MAX_SAMPLES),w=s.getParameter(s.SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:r,getMaxPrecision:l,textureFormatReadable:o,textureTypeReadable:a,precision:c,logarithmicDepthBuffer:h,reversedDepthBuffer:d,maxTextures:f,maxVertexTextures:p,maxTextureSize:v,maxCubemapSize:m,maxAttributes:g,maxVertexUniforms:S,maxVaryings:E,maxFragmentUniforms:y,maxSamples:M,samples:w}}function Yg(s){const t=this;let e=null,n=0,i=!1,r=!1;const o=new mi,a=new Ht,l={value:null,needsUpdate:!1};this.uniform=l,this.numPlanes=0,this.numIntersection=0,this.init=function(h,d){const f=h.length!==0||d||n!==0||i;return i=d,n=h.length,f},this.beginShadows=function(){r=!0,u(null)},this.endShadows=function(){r=!1},this.setGlobalState=function(h,d){e=u(h,d,0)},this.setState=function(h,d,f){const p=h.clippingPlanes,v=h.clipIntersection,m=h.clipShadows,g=s.get(h);if(!i||p===null||p.length===0||r&&!m)r?u(null):c();else{const S=r?0:n,E=S*4;let y=g.clippingState||null;l.value=y,y=u(p,d,E,f);for(let M=0;M!==E;++M)y[M]=e[M];g.clippingState=y,this.numIntersection=v?this.numPlanes:0,this.numPlanes+=S}};function c(){l.value!==e&&(l.value=e,l.needsUpdate=n>0),t.numPlanes=n,t.numIntersection=0}function u(h,d,f,p){const v=h!==null?h.length:0;let m=null;if(v!==0){if(m=l.value,p!==!0||m===null){const g=f+v*4,S=d.matrixWorldInverse;a.getNormalMatrix(S),(m===null||m.length0&&this._blur(l,0,0,e),this._applyPMREM(l),this._cleanup(l),l}fromEquirectangular(t,e=null){return this._fromTexture(t,e)}fromCubemap(t,e=null){return this._fromTexture(t,e)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=du(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=uu(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose(),this._backgroundBox!==null&&(this._backgroundBox.geometry.dispose(),this._backgroundBox.material.dispose())}_setSize(t){this._lodMax=Math.floor(Math.log2(t)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._ggxMaterial!==null&&this._ggxMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let t=0;t2?M:0,M,M),h.setRenderTarget(i),g&&h.render(v,l),h.render(t,l)}h.toneMapping=f,h.autoClear=d,t.background=S}_textureToCubeUV(t,e){const n=this._renderer,i=t.mapping===Ni||t.mapping===os;i?(this._cubemapMaterial===null&&(this._cubemapMaterial=du()),this._cubemapMaterial.uniforms.flipEnvMap.value=t.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=uu());const r=i?this._cubemapMaterial:this._equirectMaterial,o=this._lodMeshes[0];o.material=r;const a=r.uniforms;a.envMap.value=t;const l=this._cubeSize;Ps(e,0,0,3*l,2*l),n.setRenderTarget(e),n.render(o,hr)}_applyPMREM(t){const e=this._renderer,n=e.autoClear;e.autoClear=!1;const i=this._lodMeshes.length;for(let r=1;rp-_i?n-p+_i:0),g=4*(this._cubeSize-v);l.envMap.value=t.texture,l.roughness.value=f,l.mipInt.value=p-e,Ps(r,m,g,3*v,2*v),i.setRenderTarget(r),i.render(a,hr),l.envMap.value=r.texture,l.roughness.value=0,l.mipInt.value=p-n,Ps(t,m,g,3*v,2*v),i.setRenderTarget(t),i.render(a,hr)}_blur(t,e,n,i,r){const o=this._pingPongRenderTarget;this._halfBlur(t,o,e,n,i,"latitudinal",r),this._halfBlur(o,t,n,n,i,"longitudinal",r)}_halfBlur(t,e,n,i,r,o,a){const l=this._renderer,c=this._blurMaterial;o!=="latitudinal"&&o!=="longitudinal"&&te("blur direction must be either latitudinal or longitudinal!");const u=3,h=this._lodMeshes[i];h.material=c;const d=c.uniforms,f=this._sizeLods[n]-1,p=isFinite(r)?Math.PI/(2*f):2*Math.PI/(2*Vi-1),v=r/p,m=isFinite(r)?1+Math.floor(u*v):Vi;m>Vi&&Ut(`sigmaRadians, ${r}, is too large and will clip, as it requested ${m} samples when the maximum is set to ${Vi}`);const g=[];let S=0;for(let C=0;CE-_i?i-E+_i:0),w=4*(this._cubeSize-y);Ps(e,M,w,3*y,2*y),l.setRenderTarget(e),l.render(h,hr)}}function Jg(s){const t=[],e=[],n=[];let i=s;const r=s-_i+1+au.length;for(let o=0;os-_i?l=au[o-s+_i-1]:o===0&&(l=0),e.push(l);const c=1/(a-2),u=-c,h=1+c,d=[u,u,h,u,h,h,u,u,h,h,u,h],f=6,p=6,v=3,m=2,g=1,S=new Float32Array(v*p*f),E=new Float32Array(m*p*f),y=new Float32Array(g*p*f);for(let w=0;w2?0:-1,T=[C,x,0,C+2/3,x,0,C+2/3,x+1,0,C,x,0,C+2/3,x+1,0,C,x+1,0];S.set(T,v*p*w),E.set(d,m*p*w);const L=[w,w,w,w,w,w];y.set(L,g*p*w)}const M=new He;M.setAttribute("position",new ln(S,v)),M.setAttribute("uv",new ln(E,m)),M.setAttribute("faceIndex",new ln(y,g)),n.push(new zt(M,null)),i>_i&&i--}return{lodMeshes:n,sizeLods:t,sigmas:e}}function hu(s,t,e){const n=new Ye(s,t,e);return n.texture.mapping=Cr,n.texture.name="PMREM.cubeUv",n.scissorTest=!0,n}function Ps(s,t,e,n,i){s.viewport.set(t,e,n,i),s.scissor.set(t,e,n,i)}function jg(s,t,e){return new Pe({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:Kg,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${s}.0`},uniforms:{envMap:{value:null},roughness:{value:0},mipInt:{value:0}},vertexShader:go(),fragmentShader:` precision highp float; precision highp int; @@ -3780,7 +3780,7 @@ void main() { gl_FragColor = vec4(prefilteredColor, 1.0); } - `,blending:wn,depthTest:!1,depthWrite:!1})}function Lg(s,t,e){const n=new Float32Array(Ni),i=new C(0,1,0);return new Ce({name:"SphericalGaussianBlur",defines:{n:Ni,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${s}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:n},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:i}},vertexShader:Yr(),fragmentShader:` + `,blending:An,depthTest:!1,depthWrite:!1})}function Qg(s,t,e){const n=new Float32Array(Vi),i=new P(0,1,0);return new Pe({name:"SphericalGaussianBlur",defines:{n:Vi,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${s}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:n},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:i}},vertexShader:go(),fragmentShader:` precision mediump float; precision mediump int; @@ -3840,7 +3840,7 @@ void main() { } } - `,blending:wn,depthTest:!1,depthWrite:!1})}function Mh(){return new Ce({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Yr(),fragmentShader:` + `,blending:An,depthTest:!1,depthWrite:!1})}function uu(){return new Pe({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:go(),fragmentShader:` precision mediump float; precision mediump int; @@ -3859,7 +3859,7 @@ void main() { gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); } - `,blending:wn,depthTest:!1,depthWrite:!1})}function yh(){return new Ce({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Yr(),fragmentShader:` + `,blending:An,depthTest:!1,depthWrite:!1})}function du(){return new Pe({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:go(),fragmentShader:` precision mediump float; precision mediump int; @@ -3875,7 +3875,7 @@ void main() { gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); } - `,blending:wn,depthTest:!1,depthWrite:!1})}function Yr(){return` + `,blending:An,depthTest:!1,depthWrite:!1})}function go(){return` precision mediump float; precision mediump int; @@ -3930,7 +3930,7 @@ void main() { gl_Position = vec4( position, 1.0 ); } - `}class Sh extends Xe{constructor(t=1,e={}){super(t,t,e),this.isWebGLCubeRenderTarget=!0;const n={width:t,height:t,depth:1},i=[n,n,n,n,n,n];this.texture=new Yc(i),this._setTextureOptions(e),this.texture.isRenderTargetTexture=!0}fromEquirectangularTexture(t,e){this.texture.type=e.type,this.texture.colorSpace=e.colorSpace,this.texture.generateMipmaps=e.generateMipmaps,this.texture.minFilter=e.minFilter,this.texture.magFilter=e.magFilter;const n={uniforms:{tEquirect:{value:null}},vertexShader:` + `}class fu extends Ye{constructor(t=1,e={}){super(t,t,e),this.isWebGLCubeRenderTarget=!0;const n={width:t,height:t,depth:1},i=[n,n,n,n,n,n];this.texture=new Bh(i),this._setTextureOptions(e),this.texture.isRenderTargetTexture=!0}fromEquirectangularTexture(t,e){this.texture.type=e.type,this.texture.colorSpace=e.colorSpace,this.texture.generateMipmaps=e.generateMipmaps,this.texture.minFilter=e.minFilter,this.texture.magFilter=e.magFilter;const n={uniforms:{tEquirect:{value:null}},vertexShader:` varying vec3 vWorldDirection; @@ -3965,7 +3965,7 @@ void main() { gl_FragColor = texture2D( tEquirect, sampleUV ); } - `},i=new tn(5,5,5),r=new Ce({name:"CubemapFromEquirect",uniforms:ds(n.uniforms),vertexShader:n.vertexShader,fragmentShader:n.fragmentShader,side:Ge,blending:wn});r.uniforms.tEquirect.value=e;const a=new Bt(i,r),o=e.minFilter;return e.minFilter===Ti&&(e.minFilter=Ue),new dp(1,10,this).update(t,a),e.minFilter=o,a.geometry.dispose(),a.material.dispose(),this}clear(t,e=!0,n=!0,i=!0){const r=t.getRenderTarget();for(let a=0;a<6;a++)t.setRenderTarget(this,a),t.clear(e,n,i);t.setRenderTarget(r)}}function Dg(s){let t=new WeakMap,e=new WeakMap,n=null;function i(d,f=!1){return d==null?null:f?a(d):r(d)}function r(d){if(d&&d.isTexture){const f=d.mapping;if(f===Fa||f===Oa)if(t.has(d)){const p=t.get(d).texture;return o(p,d.mapping)}else{const p=d.image;if(p&&p.height>0){const v=new Sh(p.height);return v.fromEquirectangularTexture(s,d),t.set(d,v),d.addEventListener("dispose",c),o(v.texture,d.mapping)}else return null}}return d}function a(d){if(d&&d.isTexture){const f=d.mapping,p=f===Fa||f===Oa,v=f===Ei||f===$i;if(p||v){let m=e.get(d);const g=m!==void 0?m.texture.pmremVersion:0;if(d.isRenderTargetTexture&&d.pmremVersion!==g)return n===null&&(n=new xh(s)),m=p?n.fromEquirectangular(d,m):n.fromCubemap(d,m),m.texture.pmremVersion=d.pmremVersion,e.set(d,m),m.texture;if(m!==void 0)return m.texture;{const w=d.image;return p&&w&&w.height>0||v&&w&&l(w)?(n===null&&(n=new xh(s)),m=p?n.fromEquirectangular(d):n.fromCubemap(d),m.texture.pmremVersion=d.pmremVersion,e.set(d,m),d.addEventListener("dispose",u),m.texture):null}}}return d}function o(d,f){return f===Fa?d.mapping=Ei:f===Oa&&(d.mapping=$i),d}function l(d){let f=0;const p=6;for(let v=0;v=65535?Fc:Uc)(d,1);m.version=v;const g=r.get(h);g&&t.remove(g),r.set(h,m)}function u(h){const d=r.get(h);if(d){const f=h.index;f!==null&&d.versiont.maxTextureSize&&(S=Math.ceil(M/t.maxTextureSize),M=t.maxTextureSize);const b=new Float32Array(M*S*4*h),R=new Ec(b,M,S,h);R.type=un,R.needsUpdate=!0;const x=E*4;for(let P=0;P0){const v=new fu(p.height);return v.fromEquirectangularTexture(s,d),t.set(d,v),d.addEventListener("dispose",c),a(v.texture,d.mapping)}else return null}}return d}function o(d){if(d&&d.isTexture){const f=d.mapping,p=f===pa||f===ma,v=f===Ni||f===os;if(p||v){let m=e.get(d);const g=m!==void 0?m.texture.pmremVersion:0;if(d.isRenderTargetTexture&&d.pmremVersion!==g)return n===null&&(n=new cu(s)),m=p?n.fromEquirectangular(d,m):n.fromCubemap(d,m),m.texture.pmremVersion=d.pmremVersion,e.set(d,m),m.texture;if(m!==void 0)return m.texture;{const S=d.image;return p&&S&&S.height>0||v&&S&&l(S)?(n===null&&(n=new cu(s)),m=p?n.fromEquirectangular(d):n.fromCubemap(d),m.texture.pmremVersion=d.pmremVersion,e.set(d,m),d.addEventListener("dispose",u),m.texture):null}}}return d}function a(d,f){return f===pa?d.mapping=Ni:f===ma&&(d.mapping=os),d}function l(d){let f=0;const p=6;for(let v=0;v=65535?Ah:Th)(d,1);m.version=v;const g=r.get(h);g&&t.remove(g),r.set(h,m)}function u(h){const d=r.get(h);if(d){const f=h.index;f!==null&&d.versiont.maxTextureSize&&(M=Math.ceil(y/t.maxTextureSize),y=t.maxTextureSize);const w=new Float32Array(y*M*4*h),C=new gh(w,y,M,h);C.type=mn,C.needsUpdate=!0;const x=E*4;for(let L=0;L0&&g[0].isRenderPass===!0;const M=a.width,S=a.height;for(let b=0;b0)return s;const i=t*e;let r=Ah[i];if(r===void 0&&(r=new Float32Array(i),Ah[i]=r),t!==0){n.toArray(r,0);for(let a=1,o=0;a!==t;++a)o+=e,s[a].toArray(r,o)}return r}function Re(s,t){if(s.length!==t.length)return!1;for(let e=0,n=s.length;e0&&(this.seq=i.concat(r))}setValue(t,e,n,i){const r=this.map[e];r!==void 0&&r.setValue(t,n,i)}setOptional(t,e,n){const i=e[n];i!==void 0&&this.setValue(t,n,i)}static upload(t,e,n,i){for(let r=0,a=e.length;r!==a;++r){const o=e[r],l=n[o.id];l.needsUpdate!==!1&&o.setValue(t,l.value,i)}}static seqWithValue(t,e){const n=[];for(let i=0,r=t.length;i!==r;++i){const a=t[i];a.id in e&&n.push(a)}return n}}function Ih(s,t,e){const n=s.createShader(t);return s.shaderSource(n,e),s.compileShader(n),n}const L0=37297;let D0=0;function I0(s,t){const e=s.split(` -`),n=[],i=Math.max(t-6,0),r=Math.min(t+6,e.length);for(let a=i;a":" "} ${o}: ${e[a]}`)}return n.join(` -`)}const Nh=new zt;function N0(s){Kt._getMatrix(Nh,Kt.workingColorSpace,s);const t=`mat3( ${Nh.elements.map(e=>e.toFixed(4))} )`;switch(Kt.getTransfer(s)){case gr:return[t,"LinearTransferOETF"];case ie:return[t,"sRGBTransferOETF"];default:return Nt("WebGLProgram: Unsupported color space: ",s),[t,"LinearTransferOETF"]}}function Uh(s,t,e){const n=s.getShaderParameter(t,s.COMPILE_STATUS),r=(s.getShaderInfoLog(t)||"").trim();if(n&&r==="")return"";const a=/ERROR: 0:(\d+)/.exec(r);if(a){const o=parseInt(a[1]);return e.toUpperCase()+` + }`,depthTest:!1,depthWrite:!1}),u=new zt(l,c),h=new po(-1,1,1,-1,0,1);let d=null,f=null,p=!1,v,m=null,g=[],S=!1;this.setSize=function(E,y){o.setSize(E,y),a.setSize(E,y);for(let M=0;M0&&g[0].isRenderPass===!0;const y=o.width,M=o.height;for(let w=0;w0)return s;const i=t*e;let r=xu[i];if(r===void 0&&(r=new Float32Array(i),xu[i]=r),t!==0){n.toArray(r,0);for(let o=1,a=0;o!==t;++o)a+=e,s[o].toArray(r,a)}return r}function Le(s,t){if(s.length!==t.length)return!1;for(let e=0,n=s.length;e0&&(this.seq=i.concat(r))}setValue(t,e,n,i){const r=this.map[e];r!==void 0&&r.setValue(t,n,i)}setOptional(t,e,n){const i=e[n];i!==void 0&&this.setValue(t,n,i)}static upload(t,e,n,i){for(let r=0,o=e.length;r!==o;++r){const a=e[r],l=n[a.id];l.needsUpdate!==!1&&a.setValue(t,l.value,i)}}static seqWithValue(t,e){const n=[];for(let i=0,r=t.length;i!==r;++i){const o=t[i];o.id in e&&n.push(o)}return n}}function wu(s,t,e){const n=s.createShader(t);return s.shaderSource(n,e),s.compileShader(n),n}const Qv=37297;let tx=0;function ex(s,t){const e=s.split(` +`),n=[],i=Math.max(t-6,0),r=Math.min(t+6,e.length);for(let o=i;o":" "} ${a}: ${e[o]}`)}return n.join(` +`)}const Eu=new Ht;function nx(s){Jt._getMatrix(Eu,Jt.workingColorSpace,s);const t=`mat3( ${Eu.elements.map(e=>e.toFixed(4))} )`;switch(Jt.getTransfer(s)){case Or:return[t,"LinearTransferOETF"];case se:return[t,"sRGBTransferOETF"];default:return Ut("WebGLProgram: Unsupported color space: ",s),[t,"LinearTransferOETF"]}}function Tu(s,t,e){const n=s.getShaderParameter(t,s.COMPILE_STATUS),r=(s.getShaderInfoLog(t)||"").trim();if(n&&r==="")return"";const o=/ERROR: 0:(\d+)/.exec(r);if(o){const a=parseInt(o[1]);return e.toUpperCase()+` `+r+` -`+I0(s.getShaderSource(t),o)}else return r}function U0(s,t){const e=N0(t);return[`vec4 ${s}( vec4 value ) {`,` return ${e[1]}( vec4( value.rgb * ${e[0]}, value.a ) );`,"}"].join(` -`)}const F0={[Pa]:"Linear",[La]:"Reinhard",[Da]:"Cineon",[ar]:"ACESFilmic",[Na]:"AgX",[Ua]:"Neutral",[Ia]:"Custom"};function O0(s,t){const e=F0[t];return e===void 0?(Nt("WebGLProgram: Unsupported toneMapping:",t),"vec3 "+s+"( vec3 color ) { return LinearToneMapping( color ); }"):"vec3 "+s+"( vec3 color ) { return "+e+"ToneMapping( color ); }"}const Jr=new C;function k0(){Kt.getLuminanceCoefficients(Jr);const s=Jr.x.toFixed(4),t=Jr.y.toFixed(4),e=Jr.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${s}, ${t}, ${e} );`," return dot( weights, rgb );","}"].join(` -`)}function B0(s){return[s.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",s.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(qs).join(` -`)}function z0(s){const t=[];for(const e in s){const n=s[e];n!==!1&&t.push("#define "+e+" "+n)}return t.join(` -`)}function H0(s,t){const e={},n=s.getProgramParameter(t,s.ACTIVE_ATTRIBUTES);for(let i=0;i/gm;function gl(s){return s.replace(G0,W0)}const V0=new Map;function W0(s,t){let e=Xt[t];if(e===void 0){const n=V0.get(t);if(n!==void 0)e=Xt[n],Nt('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',t,n);else throw new Error("THREE.WebGLProgram: Can not resolve #include <"+t+">")}return gl(e)}const $0=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function kh(s){return s.replace($0,X0)}function X0(s,t,e,n){let i="";for(let r=parseInt(t);r/gm;function ql(s){return s.replace(hx,dx)}const ux=new Map;function dx(s,t){let e=Yt[t];if(e===void 0){const n=ux.get(t);if(n!==void 0)e=Yt[n],Ut('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',t,n);else throw new Error("THREE.WebGLProgram: Can not resolve #include <"+t+">")}return ql(e)}const fx=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function Ru(s){return s.replace(fx,px)}function px(s,t,e,n){let i="";for(let r=parseInt(t);r0&&(m+=` -`),g=["#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p].filter(qs).join(` +`),g=["#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p].filter(ur).join(` `),g.length>0&&(g+=` -`)):(m=[Bh(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p,e.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",e.batching?"#define USE_BATCHING":"",e.batchingColor?"#define USE_BATCHING_COLOR":"",e.instancing?"#define USE_INSTANCING":"",e.instancingColor?"#define USE_INSTANCING_COLOR":"",e.instancingMorph?"#define USE_INSTANCING_MORPH":"",e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+u:"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.displacementMap?"#define USE_DISPLACEMENTMAP":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.mapUv?"#define MAP_UV "+e.mapUv:"",e.alphaMapUv?"#define ALPHAMAP_UV "+e.alphaMapUv:"",e.lightMapUv?"#define LIGHTMAP_UV "+e.lightMapUv:"",e.aoMapUv?"#define AOMAP_UV "+e.aoMapUv:"",e.emissiveMapUv?"#define EMISSIVEMAP_UV "+e.emissiveMapUv:"",e.bumpMapUv?"#define BUMPMAP_UV "+e.bumpMapUv:"",e.normalMapUv?"#define NORMALMAP_UV "+e.normalMapUv:"",e.displacementMapUv?"#define DISPLACEMENTMAP_UV "+e.displacementMapUv:"",e.metalnessMapUv?"#define METALNESSMAP_UV "+e.metalnessMapUv:"",e.roughnessMapUv?"#define ROUGHNESSMAP_UV "+e.roughnessMapUv:"",e.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+e.anisotropyMapUv:"",e.clearcoatMapUv?"#define CLEARCOATMAP_UV "+e.clearcoatMapUv:"",e.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+e.clearcoatNormalMapUv:"",e.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+e.clearcoatRoughnessMapUv:"",e.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+e.iridescenceMapUv:"",e.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+e.iridescenceThicknessMapUv:"",e.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+e.sheenColorMapUv:"",e.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+e.sheenRoughnessMapUv:"",e.specularMapUv?"#define SPECULARMAP_UV "+e.specularMapUv:"",e.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+e.specularColorMapUv:"",e.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+e.specularIntensityMapUv:"",e.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+e.transmissionMapUv:"",e.thicknessMapUv?"#define THICKNESSMAP_UV "+e.thicknessMapUv:"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexNormals?"#define HAS_NORMAL":"",e.vertexColors?"#define USE_COLOR":"",e.vertexAlphas?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.flatShading?"#define FLAT_SHADED":"",e.skinning?"#define USE_SKINNING":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals&&e.flatShading===!1?"#define USE_MORPHNORMALS":"",e.morphColors?"#define USE_MORPHCOLORS":"",e.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+e.morphTextureStride:"",e.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+e.morphTargetsCount:"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` -`].filter(qs).join(` -`),g=[Bh(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p,e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",e.map?"#define USE_MAP":"",e.matcap?"#define USE_MATCAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+c:"",e.envMap?"#define "+u:"",e.envMap?"#define "+h:"",d?"#define CUBEUV_TEXEL_WIDTH "+d.texelWidth:"",d?"#define CUBEUV_TEXEL_HEIGHT "+d.texelHeight:"",d?"#define CUBEUV_MAX_MIP "+d.maxMip+".0":"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.packedNormalMap?"#define USE_PACKED_NORMALMAP":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoat?"#define USE_CLEARCOAT":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.dispersion?"#define USE_DISPERSION":"",e.iridescence?"#define USE_IRIDESCENCE":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaTest?"#define USE_ALPHATEST":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.sheen?"#define USE_SHEEN":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexColors||e.instancingColor?"#define USE_COLOR":"",e.vertexAlphas||e.batchingColor?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.gradientMap?"#define USE_GRADIENTMAP":"",e.flatShading?"#define FLAT_SHADED":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.numLightProbeGrids>0?"#define USE_LIGHT_PROBES_GRID":"",e.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",e.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",e.toneMapping!==En?"#define TONE_MAPPING":"",e.toneMapping!==En?Xt.tonemapping_pars_fragment:"",e.toneMapping!==En?O0("toneMapping",e.toneMapping):"",e.dithering?"#define DITHERING":"",e.opaque?"#define OPAQUE":"",Xt.colorspace_pars_fragment,U0("linearToOutputTexel",e.outputColorSpace),k0(),e.useDepthPacking?"#define DEPTH_PACKING "+e.depthPacking:"",` -`].filter(qs).join(` -`)),a=gl(a),a=Fh(a,e),a=Oh(a,e),o=gl(o),o=Fh(o,e),o=Oh(o,e),a=kh(a),o=kh(o),e.isRawShaderMaterial!==!0&&(w=`#version 300 es +`)):(m=[Pu(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p,e.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",e.batching?"#define USE_BATCHING":"",e.batchingColor?"#define USE_BATCHING_COLOR":"",e.instancing?"#define USE_INSTANCING":"",e.instancingColor?"#define USE_INSTANCING_COLOR":"",e.instancingMorph?"#define USE_INSTANCING_MORPH":"",e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+u:"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.displacementMap?"#define USE_DISPLACEMENTMAP":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.mapUv?"#define MAP_UV "+e.mapUv:"",e.alphaMapUv?"#define ALPHAMAP_UV "+e.alphaMapUv:"",e.lightMapUv?"#define LIGHTMAP_UV "+e.lightMapUv:"",e.aoMapUv?"#define AOMAP_UV "+e.aoMapUv:"",e.emissiveMapUv?"#define EMISSIVEMAP_UV "+e.emissiveMapUv:"",e.bumpMapUv?"#define BUMPMAP_UV "+e.bumpMapUv:"",e.normalMapUv?"#define NORMALMAP_UV "+e.normalMapUv:"",e.displacementMapUv?"#define DISPLACEMENTMAP_UV "+e.displacementMapUv:"",e.metalnessMapUv?"#define METALNESSMAP_UV "+e.metalnessMapUv:"",e.roughnessMapUv?"#define ROUGHNESSMAP_UV "+e.roughnessMapUv:"",e.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+e.anisotropyMapUv:"",e.clearcoatMapUv?"#define CLEARCOATMAP_UV "+e.clearcoatMapUv:"",e.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+e.clearcoatNormalMapUv:"",e.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+e.clearcoatRoughnessMapUv:"",e.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+e.iridescenceMapUv:"",e.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+e.iridescenceThicknessMapUv:"",e.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+e.sheenColorMapUv:"",e.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+e.sheenRoughnessMapUv:"",e.specularMapUv?"#define SPECULARMAP_UV "+e.specularMapUv:"",e.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+e.specularColorMapUv:"",e.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+e.specularIntensityMapUv:"",e.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+e.transmissionMapUv:"",e.thicknessMapUv?"#define THICKNESSMAP_UV "+e.thicknessMapUv:"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexNormals?"#define HAS_NORMAL":"",e.vertexColors?"#define USE_COLOR":"",e.vertexAlphas?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.flatShading?"#define FLAT_SHADED":"",e.skinning?"#define USE_SKINNING":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals&&e.flatShading===!1?"#define USE_MORPHNORMALS":"",e.morphColors?"#define USE_MORPHCOLORS":"",e.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+e.morphTextureStride:"",e.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+e.morphTargetsCount:"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` +`].filter(ur).join(` +`),g=[Pu(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,p,e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",e.map?"#define USE_MAP":"",e.matcap?"#define USE_MATCAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+c:"",e.envMap?"#define "+u:"",e.envMap?"#define "+h:"",d?"#define CUBEUV_TEXEL_WIDTH "+d.texelWidth:"",d?"#define CUBEUV_TEXEL_HEIGHT "+d.texelHeight:"",d?"#define CUBEUV_MAX_MIP "+d.maxMip+".0":"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.packedNormalMap?"#define USE_PACKED_NORMALMAP":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoat?"#define USE_CLEARCOAT":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.dispersion?"#define USE_DISPERSION":"",e.iridescence?"#define USE_IRIDESCENCE":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaTest?"#define USE_ALPHATEST":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.sheen?"#define USE_SHEEN":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexColors||e.instancingColor?"#define USE_COLOR":"",e.vertexAlphas||e.batchingColor?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.gradientMap?"#define USE_GRADIENTMAP":"",e.flatShading?"#define FLAT_SHADED":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.numLightProbeGrids>0?"#define USE_LIGHT_PROBES_GRID":"",e.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",e.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",e.toneMapping!==Cn?"#define TONE_MAPPING":"",e.toneMapping!==Cn?Yt.tonemapping_pars_fragment:"",e.toneMapping!==Cn?rx("toneMapping",e.toneMapping):"",e.dithering?"#define DITHERING":"",e.opaque?"#define OPAQUE":"",Yt.colorspace_pars_fragment,ix("linearToOutputTexel",e.outputColorSpace),ox(),e.useDepthPacking?"#define DEPTH_PACKING "+e.depthPacking:"",` +`].filter(ur).join(` +`)),o=ql(o),o=Au(o,e),o=Cu(o,e),a=ql(a),a=Au(a,e),a=Cu(a,e),o=Ru(o),a=Ru(a),e.isRawShaderMaterial!==!0&&(S=`#version 300 es `,m=[f,"#define attribute in","#define varying out","#define texture2D texture"].join(` `)+` -`+m,g=["#define varying in",e.glslVersion===_c?"":"layout(location = 0) out highp vec4 pc_fragColor;",e.glslVersion===_c?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` +`+m,g=["#define varying in",e.glslVersion===lh?"":"layout(location = 0) out highp vec4 pc_fragColor;",e.glslVersion===lh?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` `)+` -`+g);const E=w+m+a,M=w+g+o,S=Ih(i,i.VERTEX_SHADER,E),b=Ih(i,i.FRAGMENT_SHADER,M);i.attachShader(v,S),i.attachShader(v,b),e.index0AttributeName!==void 0?i.bindAttribLocation(v,0,e.index0AttributeName):e.hasPositionAttribute===!0&&i.bindAttribLocation(v,0,"position"),i.linkProgram(v);function R(L){if(s.debug.checkShaderErrors){const F=i.getProgramInfoLog(v)||"",q=i.getShaderInfoLog(S)||"",Y=i.getShaderInfoLog(b)||"",B=F.trim(),K=q.trim(),X=Y.trim();let Q=!0,it=!0;if(i.getProgramParameter(v,i.LINK_STATUS)===!1)if(Q=!1,typeof s.debug.onShaderError=="function")s.debug.onShaderError(i,v,S,b);else{const pt=Uh(i,S,"vertex"),_t=Uh(i,b,"fragment");Qt("WebGLProgram: Shader Error "+i.getError()+" - VALIDATE_STATUS "+i.getProgramParameter(v,i.VALIDATE_STATUS)+` +`+g);const E=S+m+o,y=S+g+a,M=wu(i,i.VERTEX_SHADER,E),w=wu(i,i.FRAGMENT_SHADER,y);i.attachShader(v,M),i.attachShader(v,w),e.index0AttributeName!==void 0?i.bindAttribLocation(v,0,e.index0AttributeName):e.hasPositionAttribute===!0&&i.bindAttribLocation(v,0,"position"),i.linkProgram(v);function C(D){if(s.debug.checkShaderErrors){const U=i.getProgramInfoLog(v)||"",K=i.getShaderInfoLog(M)||"",Z=i.getShaderInfoLog(w)||"",N=U.trim(),W=K.trim(),B=Z.trim();let X=!0,nt=!0;if(i.getProgramParameter(v,i.LINK_STATUS)===!1)if(X=!1,typeof s.debug.onShaderError=="function")s.debug.onShaderError(i,v,M,w);else{const ot=Tu(i,M,"vertex"),at=Tu(i,w,"fragment");te("WebGLProgram: Shader Error "+i.getError()+" - VALIDATE_STATUS "+i.getProgramParameter(v,i.VALIDATE_STATUS)+` -Material Name: `+L.name+` -Material Type: `+L.type+` +Material Name: `+D.name+` +Material Type: `+D.type+` -Program Info Log: `+B+` -`+pt+` -`+_t)}else B!==""?Nt("WebGLProgram: Program Info Log:",B):(K===""||X==="")&&(it=!1);it&&(L.diagnostics={runnable:Q,programLog:B,vertexShader:{log:K,prefix:m},fragmentShader:{log:X,prefix:g}})}i.deleteShader(S),i.deleteShader(b),x=new Zr(i,v),T=H0(i,v)}let x;this.getUniforms=function(){return x===void 0&&R(this),x};let T;this.getAttributes=function(){return T===void 0&&R(this),T};let P=e.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return P===!1&&(P=i.getProgramParameter(v,L0)),P},this.destroy=function(){n.releaseStatesOfProgram(this),i.deleteProgram(v),this.program=void 0},this.type=e.shaderType,this.name=e.shaderName,this.id=D0++,this.cacheKey=t,this.usedTimes=1,this.program=v,this.vertexShader=S,this.fragmentShader=b,this}let i_=0;class s_{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(t,e,n){const i=this._getShaderCacheForMaterial(t);return i.has(e)===!1&&(i.add(e),e.usedTimes++),i.has(n)===!1&&(i.add(n),n.usedTimes++),this}remove(t){const e=this.materialCache.get(t);for(const n of e)n.usedTimes--,n.usedTimes===0&&this.shaderCache.delete(n.code);return this.materialCache.delete(t),this}getVertexShaderStage(t){return this._getShaderStage(t.vertexShader)}getFragmentShaderStage(t){return this._getShaderStage(t.fragmentShader)}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(t){const e=this.materialCache;let n=e.get(t);return n===void 0&&(n=new Set,e.set(t,n)),n}_getShaderStage(t){const e=this.shaderCache;let n=e.get(t);return n===void 0&&(n=new r_(t),e.set(t,n)),n}}class r_{constructor(t){this.id=i_++,this.code=t,this.usedTimes=0}}function a_(s){return s===Ci||s===pr||s===mr}function o_(s,t,e,n,i,r){const a=new Io,o=new s_,l=new Set,c=[],u=new Map,h=n.logarithmicDepthBuffer;let d=n.precision;const f={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distance",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function p(x){return l.add(x),x===0?"uv":`uv${x}`}function v(x,T,P,L,F,q){const Y=L.fog,B=F.geometry,K=x.isMeshStandardMaterial||x.isMeshLambertMaterial||x.isMeshPhongMaterial?L.environment:null,X=x.isMeshStandardMaterial||x.isMeshLambertMaterial&&!x.envMap||x.isMeshPhongMaterial&&!x.envMap,Q=t.get(x.envMap||K,X),it=Q&&Q.mapping===or?Q.image.height:null,pt=f[x.type];x.precision!==null&&(d=n.getMaxPrecision(x.precision),d!==x.precision&&Nt("WebGLProgram.getParameters:",x.precision,"not supported, using",d,"instead."));const _t=B.morphAttributes.position||B.morphAttributes.normal||B.morphAttributes.color,bt=_t!==void 0?_t.length:0;let ee=0;B.morphAttributes.position!==void 0&&(ee=1),B.morphAttributes.normal!==void 0&&(ee=2),B.morphAttributes.color!==void 0&&(ee=3);let pe,jt,j,D;if(pt){const wt=Pn[pt];pe=wt.vertexShader,jt=wt.fragmentShader}else{pe=x.vertexShader,jt=x.fragmentShader;const wt=o.getVertexShaderStage(x),_e=o.getFragmentShaderStage(x);o.update(x,wt,_e),j=wt.id,D=_e.id}const W=s.getRenderTarget(),st=s.state.buffers.depth.getReversed(),lt=F.isInstancedMesh===!0,rt=F.isBatchedMesh===!0,Ft=!!x.map,Lt=!!x.matcap,Ht=!!Q,Zt=!!x.aoMap,Jt=!!x.lightMap,te=!!x.bumpMap&&x.wireframe===!1,le=!!x.normalMap,ce=!!x.displacementMap,Te=!!x.emissiveMap,ge=!!x.metalnessMap,Ae=!!x.roughnessMap,U=x.anisotropy>0,Ke=x.clearcoat>0,re=x.dispersion>0,A=x.iridescence>0,_=x.sheen>0,k=x.transmission>0,V=U&&!!x.anisotropyMap,Z=Ke&&!!x.clearcoatMap,at=Ke&&!!x.clearcoatNormalMap,ct=Ke&&!!x.clearcoatRoughnessMap,J=A&&!!x.iridescenceMap,et=A&&!!x.iridescenceThicknessMap,ut=_&&!!x.sheenColorMap,At=_&&!!x.sheenRoughnessMap,gt=!!x.specularMap,dt=!!x.specularColorMap,Dt=!!x.specularIntensityMap,Ut=k&&!!x.transmissionMap,Gt=k&&!!x.thicknessMap,N=!!x.gradientMap,ot=!!x.alphaMap,tt=x.alphaTest>0,ft=!!x.alphaHash,Mt=!!x.extensions;let nt=En;x.toneMapped&&(W===null||W.isXRRenderTarget===!0)&&(nt=s.toneMapping);const Tt={shaderID:pt,shaderType:x.type,shaderName:x.name,vertexShader:pe,fragmentShader:jt,defines:x.defines,customVertexShaderID:j,customFragmentShaderID:D,isRawShaderMaterial:x.isRawShaderMaterial===!0,glslVersion:x.glslVersion,precision:d,batching:rt,batchingColor:rt&&F._colorsTexture!==null,instancing:lt,instancingColor:lt&&F.instanceColor!==null,instancingMorph:lt&&F.morphTexture!==null,outputColorSpace:W===null?s.outputColorSpace:W.isXRRenderTarget===!0?W.texture.colorSpace:Kt.workingColorSpace,alphaToCoverage:!!x.alphaToCoverage,map:Ft,matcap:Lt,envMap:Ht,envMapMode:Ht&&Q.mapping,envMapCubeUVHeight:it,aoMap:Zt,lightMap:Jt,bumpMap:te,normalMap:le,displacementMap:ce,emissiveMap:Te,normalMapObjectSpace:le&&x.normalMapType===Gd,normalMapTangentSpace:le&&x.normalMapType===wo,packedNormalMap:le&&x.normalMapType===wo&&a_(x.normalMap.format),metalnessMap:ge,roughnessMap:Ae,anisotropy:U,anisotropyMap:V,clearcoat:Ke,clearcoatMap:Z,clearcoatNormalMap:at,clearcoatRoughnessMap:ct,dispersion:re,iridescence:A,iridescenceMap:J,iridescenceThicknessMap:et,sheen:_,sheenColorMap:ut,sheenRoughnessMap:At,specularMap:gt,specularColorMap:dt,specularIntensityMap:Dt,transmission:k,transmissionMap:Ut,thicknessMap:Gt,gradientMap:N,opaque:x.transparent===!1&&x.blending===Vi&&x.alphaToCoverage===!1,alphaMap:ot,alphaTest:tt,alphaHash:ft,combine:x.combine,mapUv:Ft&&p(x.map.channel),aoMapUv:Zt&&p(x.aoMap.channel),lightMapUv:Jt&&p(x.lightMap.channel),bumpMapUv:te&&p(x.bumpMap.channel),normalMapUv:le&&p(x.normalMap.channel),displacementMapUv:ce&&p(x.displacementMap.channel),emissiveMapUv:Te&&p(x.emissiveMap.channel),metalnessMapUv:ge&&p(x.metalnessMap.channel),roughnessMapUv:Ae&&p(x.roughnessMap.channel),anisotropyMapUv:V&&p(x.anisotropyMap.channel),clearcoatMapUv:Z&&p(x.clearcoatMap.channel),clearcoatNormalMapUv:at&&p(x.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:ct&&p(x.clearcoatRoughnessMap.channel),iridescenceMapUv:J&&p(x.iridescenceMap.channel),iridescenceThicknessMapUv:et&&p(x.iridescenceThicknessMap.channel),sheenColorMapUv:ut&&p(x.sheenColorMap.channel),sheenRoughnessMapUv:At&&p(x.sheenRoughnessMap.channel),specularMapUv:gt&&p(x.specularMap.channel),specularColorMapUv:dt&&p(x.specularColorMap.channel),specularIntensityMapUv:Dt&&p(x.specularIntensityMap.channel),transmissionMapUv:Ut&&p(x.transmissionMap.channel),thicknessMapUv:Gt&&p(x.thicknessMap.channel),alphaMapUv:ot&&p(x.alphaMap.channel),vertexTangents:!!B.attributes.tangent&&(le||U),vertexNormals:!!B.attributes.normal,vertexColors:x.vertexColors,vertexAlphas:x.vertexColors===!0&&!!B.attributes.color&&B.attributes.color.itemSize===4,pointsUvs:F.isPoints===!0&&!!B.attributes.uv&&(Ft||ot),fog:!!Y,useFog:x.fog===!0,fogExp2:!!Y&&Y.isFogExp2,flatShading:x.wireframe===!1&&(x.flatShading===!0||B.attributes.normal===void 0&&le===!1&&(x.isMeshLambertMaterial||x.isMeshPhongMaterial||x.isMeshStandardMaterial||x.isMeshPhysicalMaterial)),sizeAttenuation:x.sizeAttenuation===!0,logarithmicDepthBuffer:h,reversedDepthBuffer:st,skinning:F.isSkinnedMesh===!0,hasPositionAttribute:B.attributes.position!==void 0,morphTargets:B.morphAttributes.position!==void 0,morphNormals:B.morphAttributes.normal!==void 0,morphColors:B.morphAttributes.color!==void 0,morphTargetsCount:bt,morphTextureStride:ee,numDirLights:T.directional.length,numPointLights:T.point.length,numSpotLights:T.spot.length,numSpotLightMaps:T.spotLightMap.length,numRectAreaLights:T.rectArea.length,numHemiLights:T.hemi.length,numDirLightShadows:T.directionalShadowMap.length,numPointLightShadows:T.pointShadowMap.length,numSpotLightShadows:T.spotShadowMap.length,numSpotLightShadowsWithMaps:T.numSpotLightShadowsWithMaps,numLightProbes:T.numLightProbes,numLightProbeGrids:q.length,numClippingPlanes:r.numPlanes,numClipIntersection:r.numIntersection,dithering:x.dithering,shadowMapEnabled:s.shadowMap.enabled&&P.length>0,shadowMapType:s.shadowMap.type,toneMapping:nt,decodeVideoTexture:Ft&&x.map.isVideoTexture===!0&&Kt.getTransfer(x.map.colorSpace)===ie,decodeVideoTextureEmissive:Te&&x.emissiveMap.isVideoTexture===!0&&Kt.getTransfer(x.emissiveMap.colorSpace)===ie,premultipliedAlpha:x.premultipliedAlpha,doubleSided:x.side===On,flipSided:x.side===Ge,useDepthPacking:x.depthPacking>=0,depthPacking:x.depthPacking||0,index0AttributeName:x.index0AttributeName,extensionClipCullDistance:Mt&&x.extensions.clipCullDistance===!0&&e.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(Mt&&x.extensions.multiDraw===!0||rt)&&e.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:e.has("KHR_parallel_shader_compile"),customProgramCacheKey:x.customProgramCacheKey()};return Tt.vertexUv1s=l.has(1),Tt.vertexUv2s=l.has(2),Tt.vertexUv3s=l.has(3),l.clear(),Tt}function m(x){const T=[];if(x.shaderID?T.push(x.shaderID):(T.push(x.customVertexShaderID),T.push(x.customFragmentShaderID)),x.defines!==void 0)for(const P in x.defines)T.push(P),T.push(x.defines[P]);return x.isRawShaderMaterial===!1&&(g(T,x),w(T,x),T.push(s.outputColorSpace)),T.push(x.customProgramCacheKey),T.join()}function g(x,T){x.push(T.precision),x.push(T.outputColorSpace),x.push(T.envMapMode),x.push(T.envMapCubeUVHeight),x.push(T.mapUv),x.push(T.alphaMapUv),x.push(T.lightMapUv),x.push(T.aoMapUv),x.push(T.bumpMapUv),x.push(T.normalMapUv),x.push(T.displacementMapUv),x.push(T.emissiveMapUv),x.push(T.metalnessMapUv),x.push(T.roughnessMapUv),x.push(T.anisotropyMapUv),x.push(T.clearcoatMapUv),x.push(T.clearcoatNormalMapUv),x.push(T.clearcoatRoughnessMapUv),x.push(T.iridescenceMapUv),x.push(T.iridescenceThicknessMapUv),x.push(T.sheenColorMapUv),x.push(T.sheenRoughnessMapUv),x.push(T.specularMapUv),x.push(T.specularColorMapUv),x.push(T.specularIntensityMapUv),x.push(T.transmissionMapUv),x.push(T.thicknessMapUv),x.push(T.combine),x.push(T.fogExp2),x.push(T.sizeAttenuation),x.push(T.morphTargetsCount),x.push(T.morphAttributeCount),x.push(T.numDirLights),x.push(T.numPointLights),x.push(T.numSpotLights),x.push(T.numSpotLightMaps),x.push(T.numHemiLights),x.push(T.numRectAreaLights),x.push(T.numDirLightShadows),x.push(T.numPointLightShadows),x.push(T.numSpotLightShadows),x.push(T.numSpotLightShadowsWithMaps),x.push(T.numLightProbes),x.push(T.shadowMapType),x.push(T.toneMapping),x.push(T.numClippingPlanes),x.push(T.numClipIntersection),x.push(T.depthPacking)}function w(x,T){a.disableAll(),T.instancing&&a.enable(0),T.instancingColor&&a.enable(1),T.instancingMorph&&a.enable(2),T.matcap&&a.enable(3),T.envMap&&a.enable(4),T.normalMapObjectSpace&&a.enable(5),T.normalMapTangentSpace&&a.enable(6),T.clearcoat&&a.enable(7),T.iridescence&&a.enable(8),T.alphaTest&&a.enable(9),T.vertexColors&&a.enable(10),T.vertexAlphas&&a.enable(11),T.vertexUv1s&&a.enable(12),T.vertexUv2s&&a.enable(13),T.vertexUv3s&&a.enable(14),T.vertexTangents&&a.enable(15),T.anisotropy&&a.enable(16),T.alphaHash&&a.enable(17),T.batching&&a.enable(18),T.dispersion&&a.enable(19),T.batchingColor&&a.enable(20),T.gradientMap&&a.enable(21),T.packedNormalMap&&a.enable(22),T.vertexNormals&&a.enable(23),x.push(a.mask),a.disableAll(),T.fog&&a.enable(0),T.useFog&&a.enable(1),T.flatShading&&a.enable(2),T.logarithmicDepthBuffer&&a.enable(3),T.reversedDepthBuffer&&a.enable(4),T.skinning&&a.enable(5),T.morphTargets&&a.enable(6),T.morphNormals&&a.enable(7),T.morphColors&&a.enable(8),T.premultipliedAlpha&&a.enable(9),T.shadowMapEnabled&&a.enable(10),T.doubleSided&&a.enable(11),T.flipSided&&a.enable(12),T.useDepthPacking&&a.enable(13),T.dithering&&a.enable(14),T.transmission&&a.enable(15),T.sheen&&a.enable(16),T.opaque&&a.enable(17),T.pointsUvs&&a.enable(18),T.decodeVideoTexture&&a.enable(19),T.decodeVideoTextureEmissive&&a.enable(20),T.alphaToCoverage&&a.enable(21),T.numLightProbeGrids>0&&a.enable(22),T.hasPositionAttribute&&a.enable(23),x.push(a.mask)}function E(x){const T=f[x.type];let P;if(T){const L=Pn[T];P=$s.clone(L.uniforms)}else P=x.uniforms;return P}function M(x,T){let P=u.get(T);return P!==void 0?++P.usedTimes:(P=new n_(s,T,x,i),c.push(P),u.set(T,P)),P}function S(x){if(--x.usedTimes===0){const T=c.indexOf(x);c[T]=c[c.length-1],c.pop(),u.delete(x.cacheKey),x.destroy()}}function b(x){o.remove(x)}function R(){o.dispose()}return{getParameters:v,getProgramCacheKey:m,getUniforms:E,acquireProgram:M,releaseProgram:S,releaseShaderCache:b,programs:c,dispose:R}}function l_(){let s=new WeakMap;function t(a){return s.has(a)}function e(a){let o=s.get(a);return o===void 0&&(o={},s.set(a,o)),o}function n(a){s.delete(a)}function i(a,o,l){s.get(a)[o]=l}function r(){s=new WeakMap}return{has:t,get:e,remove:n,update:i,dispose:r}}function c_(s,t){return s.groupOrder!==t.groupOrder?s.groupOrder-t.groupOrder:s.renderOrder!==t.renderOrder?s.renderOrder-t.renderOrder:s.material.id!==t.material.id?s.material.id-t.material.id:s.materialVariant!==t.materialVariant?s.materialVariant-t.materialVariant:s.z!==t.z?s.z-t.z:s.id-t.id}function zh(s,t){return s.groupOrder!==t.groupOrder?s.groupOrder-t.groupOrder:s.renderOrder!==t.renderOrder?s.renderOrder-t.renderOrder:s.z!==t.z?t.z-s.z:s.id-t.id}function Hh(){const s=[];let t=0;const e=[],n=[],i=[];function r(){t=0,e.length=0,n.length=0,i.length=0}function a(d){let f=0;return d.isInstancedMesh&&(f+=2),d.isSkinnedMesh&&(f+=1),f}function o(d,f,p,v,m,g){let w=s[t];return w===void 0?(w={id:d.id,object:d,geometry:f,material:p,materialVariant:a(d),groupOrder:v,renderOrder:d.renderOrder,z:m,group:g},s[t]=w):(w.id=d.id,w.object=d,w.geometry=f,w.material=p,w.materialVariant=a(d),w.groupOrder=v,w.renderOrder=d.renderOrder,w.z=m,w.group=g),t++,w}function l(d,f,p,v,m,g){const w=o(d,f,p,v,m,g);p.transmission>0?n.push(w):p.transparent===!0?i.push(w):e.push(w)}function c(d,f,p,v,m,g){const w=o(d,f,p,v,m,g);p.transmission>0?n.unshift(w):p.transparent===!0?i.unshift(w):e.unshift(w)}function u(d,f,p){e.length>1&&e.sort(d||c_),n.length>1&&n.sort(f||zh),i.length>1&&i.sort(f||zh),p&&(e.reverse(),n.reverse(),i.reverse())}function h(){for(let d=t,f=s.length;d=r.length?(a=new Hh,r.push(a)):a=r[i],a}function e(){s=new WeakMap}return{get:t,dispose:e}}function u_(){const s={};return{get:function(t){if(s[t.id]!==void 0)return s[t.id];let e;switch(t.type){case"DirectionalLight":e={direction:new C,color:new kt};break;case"SpotLight":e={position:new C,direction:new C,color:new kt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":e={position:new C,color:new kt,distance:0,decay:0};break;case"HemisphereLight":e={direction:new C,skyColor:new kt,groundColor:new kt};break;case"RectAreaLight":e={color:new kt,position:new C,halfWidth:new C,halfHeight:new C};break}return s[t.id]=e,e}}}function d_(){const s={};return{get:function(t){if(s[t.id]!==void 0)return s[t.id];let e;switch(t.type){case"DirectionalLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ht};break;case"SpotLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ht};break;case"PointLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ht,shadowCameraNear:1,shadowCameraFar:1e3};break}return s[t.id]=e,e}}}let f_=0;function p_(s,t){return(t.castShadow?2:0)-(s.castShadow?2:0)+(t.map?1:0)-(s.map?1:0)}function m_(s){const t=new u_,e=d_(),n={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let c=0;c<9;c++)n.probe.push(new C);const i=new C,r=new se,a=new se;function o(c){let u=0,h=0,d=0;for(let T=0;T<9;T++)n.probe[T].set(0,0,0);let f=0,p=0,v=0,m=0,g=0,w=0,E=0,M=0,S=0,b=0,R=0;c.sort(p_);for(let T=0,P=c.length;T0&&(s.has("OES_texture_float_linear")===!0?(n.rectAreaLTC1=mt.LTC_FLOAT_1,n.rectAreaLTC2=mt.LTC_FLOAT_2):(n.rectAreaLTC1=mt.LTC_HALF_1,n.rectAreaLTC2=mt.LTC_HALF_2)),n.ambient[0]=u,n.ambient[1]=h,n.ambient[2]=d;const x=n.hash;(x.directionalLength!==f||x.pointLength!==p||x.spotLength!==v||x.rectAreaLength!==m||x.hemiLength!==g||x.numDirectionalShadows!==w||x.numPointShadows!==E||x.numSpotShadows!==M||x.numSpotMaps!==S||x.numLightProbes!==R)&&(n.directional.length=f,n.spot.length=v,n.rectArea.length=m,n.point.length=p,n.hemi.length=g,n.directionalShadow.length=w,n.directionalShadowMap.length=w,n.pointShadow.length=E,n.pointShadowMap.length=E,n.spotShadow.length=M,n.spotShadowMap.length=M,n.directionalShadowMatrix.length=w,n.pointShadowMatrix.length=E,n.spotLightMatrix.length=M+S-b,n.spotLightMap.length=S,n.numSpotLightShadowsWithMaps=b,n.numLightProbes=R,x.directionalLength=f,x.pointLength=p,x.spotLength=v,x.rectAreaLength=m,x.hemiLength=g,x.numDirectionalShadows=w,x.numPointShadows=E,x.numSpotShadows=M,x.numSpotMaps=S,x.numLightProbes=R,n.version=f_++)}function l(c,u){let h=0,d=0,f=0,p=0,v=0;const m=u.matrixWorldInverse;for(let g=0,w=c.length;g=a.length?(o=new Gh(s),a.push(o)):o=a[r],o}function n(){t=new WeakMap}return{get:e,dispose:n}}const __=`void main() { +Program Info Log: `+N+` +`+ot+` +`+at)}else N!==""?Ut("WebGLProgram: Program Info Log:",N):(W===""||B==="")&&(nt=!1);nt&&(D.diagnostics={runnable:X,programLog:N,vertexShader:{log:W,prefix:m},fragmentShader:{log:B,prefix:g}})}i.deleteShader(M),i.deleteShader(w),x=new xo(i,v),T=cx(i,v)}let x;this.getUniforms=function(){return x===void 0&&C(this),x};let T;this.getAttributes=function(){return T===void 0&&C(this),T};let L=e.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return L===!1&&(L=i.getProgramParameter(v,Qv)),L},this.destroy=function(){n.releaseStatesOfProgram(this),i.deleteProgram(v),this.program=void 0},this.type=e.shaderType,this.name=e.shaderName,this.id=tx++,this.cacheKey=t,this.usedTimes=1,this.program=v,this.vertexShader=M,this.fragmentShader=w,this}let Ex=0;class Tx{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(t,e,n){const i=this._getShaderCacheForMaterial(t);return i.has(e)===!1&&(i.add(e),e.usedTimes++),i.has(n)===!1&&(i.add(n),n.usedTimes++),this}remove(t){const e=this.materialCache.get(t);for(const n of e)n.usedTimes--,n.usedTimes===0&&this.shaderCache.delete(n.code);return this.materialCache.delete(t),this}getVertexShaderStage(t){return this._getShaderStage(t.vertexShader)}getFragmentShaderStage(t){return this._getShaderStage(t.fragmentShader)}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(t){const e=this.materialCache;let n=e.get(t);return n===void 0&&(n=new Set,e.set(t,n)),n}_getShaderStage(t){const e=this.shaderCache;let n=e.get(t);return n===void 0&&(n=new Ax(t),e.set(t,n)),n}}class Ax{constructor(t){this.id=Ex++,this.code=t,this.usedTimes=0}}function Cx(s){return s===Oi||s===Ur||s===Fr}function Rx(s,t,e,n,i,r){const o=new hl,a=new Tx,l=new Set,c=[],u=new Map,h=n.logarithmicDepthBuffer;let d=n.precision;const f={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distance",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function p(x){return l.add(x),x===0?"uv":`uv${x}`}function v(x,T,L,D,U,K){const Z=D.fog,N=U.geometry,W=x.isMeshStandardMaterial||x.isMeshLambertMaterial||x.isMeshPhongMaterial?D.environment:null,B=x.isMeshStandardMaterial||x.isMeshLambertMaterial&&!x.envMap||x.isMeshPhongMaterial&&!x.envMap,X=t.get(x.envMap||W,B),nt=X&&X.mapping===Cr?X.image.height:null,ot=f[x.type];x.precision!==null&&(d=n.getMaxPrecision(x.precision),d!==x.precision&&Ut("WebGLProgram.getParameters:",x.precision,"not supported, using",d,"instead."));const at=N.morphAttributes.position||N.morphAttributes.normal||N.morphAttributes.color,vt=at!==void 0?at.length:0;let Gt=0;N.morphAttributes.position!==void 0&&(Gt=1),N.morphAttributes.normal!==void 0&&(Gt=2),N.morphAttributes.color!==void 0&&(Gt=3);let me,ee,Q,I;if(ot){const Et=In[ot];me=Et.vertexShader,ee=Et.fragmentShader}else{me=x.vertexShader,ee=x.fragmentShader;const Et=a.getVertexShaderStage(x),_e=a.getFragmentShaderStage(x);a.update(x,Et,_e),Q=Et.id,I=_e.id}const q=s.getRenderTarget(),st=s.state.buffers.depth.getReversed(),ut=U.isInstancedMesh===!0,lt=U.isBatchedMesh===!0,Ot=!!x.map,Dt=!!x.matcap,Vt=!!X,jt=!!x.aoMap,Qt=!!x.lightMap,ne=!!x.bumpMap&&x.wireframe===!1,ce=!!x.normalMap,he=!!x.displacementMap,Ce=!!x.emissiveMap,xe=!!x.metalnessMap,Re=!!x.roughnessMap,O=x.anisotropy>0,Je=x.clearcoat>0,oe=x.dispersion>0,A=x.iridescence>0,_=x.sheen>0,z=x.transmission>0,$=O&&!!x.anisotropyMap,J=Je&&!!x.clearcoatMap,ct=Je&&!!x.clearcoatNormalMap,dt=Je&&!!x.clearcoatRoughnessMap,j=A&&!!x.iridescenceMap,et=A&&!!x.iridescenceThicknessMap,pt=_&&!!x.sheenColorMap,Ct=_&&!!x.sheenRoughnessMap,_t=!!x.specularMap,mt=!!x.specularColorMap,Nt=!!x.specularIntensityMap,Ft=z&&!!x.transmissionMap,$t=z&&!!x.thicknessMap,F=!!x.gradientMap,ht=!!x.alphaMap,tt=x.alphaTest>0,gt=!!x.alphaHash,bt=!!x.extensions;let it=Cn;x.toneMapped&&(q===null||q.isXRRenderTarget===!0)&&(it=s.toneMapping);const At={shaderID:ot,shaderType:x.type,shaderName:x.name,vertexShader:me,fragmentShader:ee,defines:x.defines,customVertexShaderID:Q,customFragmentShaderID:I,isRawShaderMaterial:x.isRawShaderMaterial===!0,glslVersion:x.glslVersion,precision:d,batching:lt,batchingColor:lt&&U._colorsTexture!==null,instancing:ut,instancingColor:ut&&U.instanceColor!==null,instancingMorph:ut&&U.morphTexture!==null,outputColorSpace:q===null?s.outputColorSpace:q.isXRRenderTarget===!0?q.texture.colorSpace:Jt.workingColorSpace,alphaToCoverage:!!x.alphaToCoverage,map:Ot,matcap:Dt,envMap:Vt,envMapMode:Vt&&X.mapping,envMapCubeUVHeight:nt,aoMap:jt,lightMap:Qt,bumpMap:ne,normalMap:ce,displacementMap:he,emissiveMap:Ce,normalMapObjectSpace:ce&&x.normalMapType===up,normalMapTangentSpace:ce&&x.normalMapType===el,packedNormalMap:ce&&x.normalMapType===el&&Cx(x.normalMap.format),metalnessMap:xe,roughnessMap:Re,anisotropy:O,anisotropyMap:$,clearcoat:Je,clearcoatMap:J,clearcoatNormalMap:ct,clearcoatRoughnessMap:dt,dispersion:oe,iridescence:A,iridescenceMap:j,iridescenceThicknessMap:et,sheen:_,sheenColorMap:pt,sheenRoughnessMap:Ct,specularMap:_t,specularColorMap:mt,specularIntensityMap:Nt,transmission:z,transmissionMap:Ft,thicknessMap:$t,gradientMap:F,opaque:x.transparent===!1&&x.blending===ss&&x.alphaToCoverage===!1,alphaMap:ht,alphaTest:tt,alphaHash:gt,combine:x.combine,mapUv:Ot&&p(x.map.channel),aoMapUv:jt&&p(x.aoMap.channel),lightMapUv:Qt&&p(x.lightMap.channel),bumpMapUv:ne&&p(x.bumpMap.channel),normalMapUv:ce&&p(x.normalMap.channel),displacementMapUv:he&&p(x.displacementMap.channel),emissiveMapUv:Ce&&p(x.emissiveMap.channel),metalnessMapUv:xe&&p(x.metalnessMap.channel),roughnessMapUv:Re&&p(x.roughnessMap.channel),anisotropyMapUv:$&&p(x.anisotropyMap.channel),clearcoatMapUv:J&&p(x.clearcoatMap.channel),clearcoatNormalMapUv:ct&&p(x.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:dt&&p(x.clearcoatRoughnessMap.channel),iridescenceMapUv:j&&p(x.iridescenceMap.channel),iridescenceThicknessMapUv:et&&p(x.iridescenceThicknessMap.channel),sheenColorMapUv:pt&&p(x.sheenColorMap.channel),sheenRoughnessMapUv:Ct&&p(x.sheenRoughnessMap.channel),specularMapUv:_t&&p(x.specularMap.channel),specularColorMapUv:mt&&p(x.specularColorMap.channel),specularIntensityMapUv:Nt&&p(x.specularIntensityMap.channel),transmissionMapUv:Ft&&p(x.transmissionMap.channel),thicknessMapUv:$t&&p(x.thicknessMap.channel),alphaMapUv:ht&&p(x.alphaMap.channel),vertexTangents:!!N.attributes.tangent&&(ce||O),vertexNormals:!!N.attributes.normal,vertexColors:x.vertexColors,vertexAlphas:x.vertexColors===!0&&!!N.attributes.color&&N.attributes.color.itemSize===4,pointsUvs:U.isPoints===!0&&!!N.attributes.uv&&(Ot||ht),fog:!!Z,useFog:x.fog===!0,fogExp2:!!Z&&Z.isFogExp2,flatShading:x.wireframe===!1&&(x.flatShading===!0||N.attributes.normal===void 0&&ce===!1&&(x.isMeshLambertMaterial||x.isMeshPhongMaterial||x.isMeshStandardMaterial||x.isMeshPhysicalMaterial)),sizeAttenuation:x.sizeAttenuation===!0,logarithmicDepthBuffer:h,reversedDepthBuffer:st,skinning:U.isSkinnedMesh===!0,hasPositionAttribute:N.attributes.position!==void 0,morphTargets:N.morphAttributes.position!==void 0,morphNormals:N.morphAttributes.normal!==void 0,morphColors:N.morphAttributes.color!==void 0,morphTargetsCount:vt,morphTextureStride:Gt,numDirLights:T.directional.length,numPointLights:T.point.length,numSpotLights:T.spot.length,numSpotLightMaps:T.spotLightMap.length,numRectAreaLights:T.rectArea.length,numHemiLights:T.hemi.length,numDirLightShadows:T.directionalShadowMap.length,numPointLightShadows:T.pointShadowMap.length,numSpotLightShadows:T.spotShadowMap.length,numSpotLightShadowsWithMaps:T.numSpotLightShadowsWithMaps,numLightProbes:T.numLightProbes,numLightProbeGrids:K.length,numClippingPlanes:r.numPlanes,numClipIntersection:r.numIntersection,dithering:x.dithering,shadowMapEnabled:s.shadowMap.enabled&&L.length>0,shadowMapType:s.shadowMap.type,toneMapping:it,decodeVideoTexture:Ot&&x.map.isVideoTexture===!0&&Jt.getTransfer(x.map.colorSpace)===se,decodeVideoTextureEmissive:Ce&&x.emissiveMap.isVideoTexture===!0&&Jt.getTransfer(x.emissiveMap.colorSpace)===se,premultipliedAlpha:x.premultipliedAlpha,doubleSided:x.side===Gn,flipSided:x.side===We,useDepthPacking:x.depthPacking>=0,depthPacking:x.depthPacking||0,index0AttributeName:x.index0AttributeName,extensionClipCullDistance:bt&&x.extensions.clipCullDistance===!0&&e.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(bt&&x.extensions.multiDraw===!0||lt)&&e.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:e.has("KHR_parallel_shader_compile"),customProgramCacheKey:x.customProgramCacheKey()};return At.vertexUv1s=l.has(1),At.vertexUv2s=l.has(2),At.vertexUv3s=l.has(3),l.clear(),At}function m(x){const T=[];if(x.shaderID?T.push(x.shaderID):(T.push(x.customVertexShaderID),T.push(x.customFragmentShaderID)),x.defines!==void 0)for(const L in x.defines)T.push(L),T.push(x.defines[L]);return x.isRawShaderMaterial===!1&&(g(T,x),S(T,x),T.push(s.outputColorSpace)),T.push(x.customProgramCacheKey),T.join()}function g(x,T){x.push(T.precision),x.push(T.outputColorSpace),x.push(T.envMapMode),x.push(T.envMapCubeUVHeight),x.push(T.mapUv),x.push(T.alphaMapUv),x.push(T.lightMapUv),x.push(T.aoMapUv),x.push(T.bumpMapUv),x.push(T.normalMapUv),x.push(T.displacementMapUv),x.push(T.emissiveMapUv),x.push(T.metalnessMapUv),x.push(T.roughnessMapUv),x.push(T.anisotropyMapUv),x.push(T.clearcoatMapUv),x.push(T.clearcoatNormalMapUv),x.push(T.clearcoatRoughnessMapUv),x.push(T.iridescenceMapUv),x.push(T.iridescenceThicknessMapUv),x.push(T.sheenColorMapUv),x.push(T.sheenRoughnessMapUv),x.push(T.specularMapUv),x.push(T.specularColorMapUv),x.push(T.specularIntensityMapUv),x.push(T.transmissionMapUv),x.push(T.thicknessMapUv),x.push(T.combine),x.push(T.fogExp2),x.push(T.sizeAttenuation),x.push(T.morphTargetsCount),x.push(T.morphAttributeCount),x.push(T.numDirLights),x.push(T.numPointLights),x.push(T.numSpotLights),x.push(T.numSpotLightMaps),x.push(T.numHemiLights),x.push(T.numRectAreaLights),x.push(T.numDirLightShadows),x.push(T.numPointLightShadows),x.push(T.numSpotLightShadows),x.push(T.numSpotLightShadowsWithMaps),x.push(T.numLightProbes),x.push(T.shadowMapType),x.push(T.toneMapping),x.push(T.numClippingPlanes),x.push(T.numClipIntersection),x.push(T.depthPacking)}function S(x,T){o.disableAll(),T.instancing&&o.enable(0),T.instancingColor&&o.enable(1),T.instancingMorph&&o.enable(2),T.matcap&&o.enable(3),T.envMap&&o.enable(4),T.normalMapObjectSpace&&o.enable(5),T.normalMapTangentSpace&&o.enable(6),T.clearcoat&&o.enable(7),T.iridescence&&o.enable(8),T.alphaTest&&o.enable(9),T.vertexColors&&o.enable(10),T.vertexAlphas&&o.enable(11),T.vertexUv1s&&o.enable(12),T.vertexUv2s&&o.enable(13),T.vertexUv3s&&o.enable(14),T.vertexTangents&&o.enable(15),T.anisotropy&&o.enable(16),T.alphaHash&&o.enable(17),T.batching&&o.enable(18),T.dispersion&&o.enable(19),T.batchingColor&&o.enable(20),T.gradientMap&&o.enable(21),T.packedNormalMap&&o.enable(22),T.vertexNormals&&o.enable(23),x.push(o.mask),o.disableAll(),T.fog&&o.enable(0),T.useFog&&o.enable(1),T.flatShading&&o.enable(2),T.logarithmicDepthBuffer&&o.enable(3),T.reversedDepthBuffer&&o.enable(4),T.skinning&&o.enable(5),T.morphTargets&&o.enable(6),T.morphNormals&&o.enable(7),T.morphColors&&o.enable(8),T.premultipliedAlpha&&o.enable(9),T.shadowMapEnabled&&o.enable(10),T.doubleSided&&o.enable(11),T.flipSided&&o.enable(12),T.useDepthPacking&&o.enable(13),T.dithering&&o.enable(14),T.transmission&&o.enable(15),T.sheen&&o.enable(16),T.opaque&&o.enable(17),T.pointsUvs&&o.enable(18),T.decodeVideoTexture&&o.enable(19),T.decodeVideoTextureEmissive&&o.enable(20),T.alphaToCoverage&&o.enable(21),T.numLightProbeGrids>0&&o.enable(22),T.hasPositionAttribute&&o.enable(23),x.push(o.mask)}function E(x){const T=f[x.type];let L;if(T){const D=In[T];L=cr.clone(D.uniforms)}else L=x.uniforms;return L}function y(x,T){let L=u.get(T);return L!==void 0?++L.usedTimes:(L=new wx(s,T,x,i),c.push(L),u.set(T,L)),L}function M(x){if(--x.usedTimes===0){const T=c.indexOf(x);c[T]=c[c.length-1],c.pop(),u.delete(x.cacheKey),x.destroy()}}function w(x){a.remove(x)}function C(){a.dispose()}return{getParameters:v,getProgramCacheKey:m,getUniforms:E,acquireProgram:y,releaseProgram:M,releaseShaderCache:w,programs:c,dispose:C}}function Px(){let s=new WeakMap;function t(o){return s.has(o)}function e(o){let a=s.get(o);return a===void 0&&(a={},s.set(o,a)),a}function n(o){s.delete(o)}function i(o,a,l){s.get(o)[a]=l}function r(){s=new WeakMap}return{has:t,get:e,remove:n,update:i,dispose:r}}function Lx(s,t){return s.groupOrder!==t.groupOrder?s.groupOrder-t.groupOrder:s.renderOrder!==t.renderOrder?s.renderOrder-t.renderOrder:s.material.id!==t.material.id?s.material.id-t.material.id:s.materialVariant!==t.materialVariant?s.materialVariant-t.materialVariant:s.z!==t.z?s.z-t.z:s.id-t.id}function Lu(s,t){return s.groupOrder!==t.groupOrder?s.groupOrder-t.groupOrder:s.renderOrder!==t.renderOrder?s.renderOrder-t.renderOrder:s.z!==t.z?t.z-s.z:s.id-t.id}function Du(){const s=[];let t=0;const e=[],n=[],i=[];function r(){t=0,e.length=0,n.length=0,i.length=0}function o(d){let f=0;return d.isInstancedMesh&&(f+=2),d.isSkinnedMesh&&(f+=1),f}function a(d,f,p,v,m,g){let S=s[t];return S===void 0?(S={id:d.id,object:d,geometry:f,material:p,materialVariant:o(d),groupOrder:v,renderOrder:d.renderOrder,z:m,group:g},s[t]=S):(S.id=d.id,S.object=d,S.geometry=f,S.material=p,S.materialVariant=o(d),S.groupOrder=v,S.renderOrder=d.renderOrder,S.z=m,S.group=g),t++,S}function l(d,f,p,v,m,g){const S=a(d,f,p,v,m,g);p.transmission>0?n.push(S):p.transparent===!0?i.push(S):e.push(S)}function c(d,f,p,v,m,g){const S=a(d,f,p,v,m,g);p.transmission>0?n.unshift(S):p.transparent===!0?i.unshift(S):e.unshift(S)}function u(d,f,p){e.length>1&&e.sort(d||Lx),n.length>1&&n.sort(f||Lu),i.length>1&&i.sort(f||Lu),p&&(e.reverse(),n.reverse(),i.reverse())}function h(){for(let d=t,f=s.length;d=r.length?(o=new Du,r.push(o)):o=r[i],o}function e(){s=new WeakMap}return{get:t,dispose:e}}function Ix(){const s={};return{get:function(t){if(s[t.id]!==void 0)return s[t.id];let e;switch(t.type){case"DirectionalLight":e={direction:new P,color:new Bt};break;case"SpotLight":e={position:new P,direction:new P,color:new Bt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":e={position:new P,color:new Bt,distance:0,decay:0};break;case"HemisphereLight":e={direction:new P,skyColor:new Bt,groundColor:new Bt};break;case"RectAreaLight":e={color:new Bt,position:new P,halfWidth:new P,halfHeight:new P};break}return s[t.id]=e,e}}}function Nx(){const s={};return{get:function(t){if(s[t.id]!==void 0)return s[t.id];let e;switch(t.type){case"DirectionalLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ft};break;case"SpotLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ft};break;case"PointLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new ft,shadowCameraNear:1,shadowCameraFar:1e3};break}return s[t.id]=e,e}}}let Ux=0;function Fx(s,t){return(t.castShadow?2:0)-(s.castShadow?2:0)+(t.map?1:0)-(s.map?1:0)}function Ox(s){const t=new Ix,e=Nx(),n={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let c=0;c<9;c++)n.probe.push(new P);const i=new P,r=new re,o=new re;function a(c){let u=0,h=0,d=0;for(let T=0;T<9;T++)n.probe[T].set(0,0,0);let f=0,p=0,v=0,m=0,g=0,S=0,E=0,y=0,M=0,w=0,C=0;c.sort(Fx);for(let T=0,L=c.length;T0&&(s.has("OES_texture_float_linear")===!0?(n.rectAreaLTC1=xt.LTC_FLOAT_1,n.rectAreaLTC2=xt.LTC_FLOAT_2):(n.rectAreaLTC1=xt.LTC_HALF_1,n.rectAreaLTC2=xt.LTC_HALF_2)),n.ambient[0]=u,n.ambient[1]=h,n.ambient[2]=d;const x=n.hash;(x.directionalLength!==f||x.pointLength!==p||x.spotLength!==v||x.rectAreaLength!==m||x.hemiLength!==g||x.numDirectionalShadows!==S||x.numPointShadows!==E||x.numSpotShadows!==y||x.numSpotMaps!==M||x.numLightProbes!==C)&&(n.directional.length=f,n.spot.length=v,n.rectArea.length=m,n.point.length=p,n.hemi.length=g,n.directionalShadow.length=S,n.directionalShadowMap.length=S,n.pointShadow.length=E,n.pointShadowMap.length=E,n.spotShadow.length=y,n.spotShadowMap.length=y,n.directionalShadowMatrix.length=S,n.pointShadowMatrix.length=E,n.spotLightMatrix.length=y+M-w,n.spotLightMap.length=M,n.numSpotLightShadowsWithMaps=w,n.numLightProbes=C,x.directionalLength=f,x.pointLength=p,x.spotLength=v,x.rectAreaLength=m,x.hemiLength=g,x.numDirectionalShadows=S,x.numPointShadows=E,x.numSpotShadows=y,x.numSpotMaps=M,x.numLightProbes=C,n.version=Ux++)}function l(c,u){let h=0,d=0,f=0,p=0,v=0;const m=u.matrixWorldInverse;for(let g=0,S=c.length;g=o.length?(a=new Iu(s),o.push(a)):a=o[r],a}function n(){t=new WeakMap}return{get:e,dispose:n}}const Bx=`void main() { gl_Position = vec4( position, 1.0 ); -}`,x_=`uniform sampler2D shadow_pass; +}`,zx=`uniform sampler2D shadow_pass; uniform vec2 resolution; uniform float radius; void main() { @@ -4089,12 +4089,12 @@ void main() { squared_mean = squared_mean / samples; float std_dev = sqrt( max( 0.0, squared_mean - mean * mean ) ); gl_FragColor = vec4( mean, std_dev, 0.0, 1.0 ); -}`,v_=[new C(1,0,0),new C(-1,0,0),new C(0,1,0),new C(0,-1,0),new C(0,0,1),new C(0,0,-1)],M_=[new C(0,-1,0),new C(0,-1,0),new C(0,0,1),new C(0,0,-1),new C(0,-1,0),new C(0,-1,0)],Vh=new se,Ys=new C,_l=new C;function y_(s,t,e){let n=new kr;const i=new ht,r=new ht,a=new me,o=new sp,l=new rp,c={},u=e.maxTextureSize,h={[jn]:Ge,[Ge]:jn,[On]:On},d=new Ce({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new ht},radius:{value:4}},vertexShader:__,fragmentShader:x_}),f=d.clone();f.defines.HORIZONTAL_PASS=1;const p=new Be;p.setAttribute("position",new rn(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const v=new Bt(p,d),m=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=sr;let g=this.type;this.render=function(b,R,x){if(m.enabled===!1||m.autoUpdate===!1&&m.needsUpdate===!1||b.length===0)return;this.type===vd&&(Nt("WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead."),this.type=sr);const T=s.getRenderTarget(),P=s.getActiveCubeFace(),L=s.getActiveMipmapLevel(),F=s.state;F.setBlending(wn),F.buffers.depth.getReversed()===!0?F.buffers.color.setClear(0,0,0,0):F.buffers.color.setClear(1,1,1,1),F.buffers.depth.setTest(!0),F.setScissorTest(!1);const q=g!==this.type;q&&R.traverse(function(Y){Y.material&&(Array.isArray(Y.material)?Y.material.forEach(B=>B.needsUpdate=!0):Y.material.needsUpdate=!0)});for(let Y=0,B=b.length;Yu||i.y>u)&&(i.x>u&&(r.x=Math.floor(u/Q.x),i.x=r.x*Q.x,X.mapSize.x=r.x),i.y>u&&(r.y=Math.floor(u/Q.y),i.y=r.y*Q.y,X.mapSize.y=r.y));const it=s.state.buffers.depth.getReversed();if(X.camera._reversedDepth=it,X.map===null||q===!0){if(X.map!==null&&(X.map.depthTexture!==null&&(X.map.depthTexture.dispose(),X.map.depthTexture=null),X.map.dispose()),this.type===As){if(K.isPointLight){Nt("WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.");continue}X.map=new Xe(i.x,i.y,{format:Ci,type:Je,minFilter:Ue,magFilter:Ue,generateMipmaps:!1}),X.map.texture.name=K.name+".shadowMap",X.map.depthTexture=new us(i.x,i.y,un),X.map.depthTexture.name=K.name+".shadowMapDepth",X.map.depthTexture.format=Bn,X.map.depthTexture.compareFunction=null,X.map.depthTexture.minFilter=Ne,X.map.depthTexture.magFilter=Ne}else K.isPointLight?(X.map=new Sh(i.x),X.map.depthTexture=new Bf(i.x,Tn)):(X.map=new Xe(i.x,i.y),X.map.depthTexture=new us(i.x,i.y,Tn)),X.map.depthTexture.name=K.name+".shadowMap",X.map.depthTexture.format=Bn,this.type===sr?(X.map.depthTexture.compareFunction=it?To:Eo,X.map.depthTexture.minFilter=Ue,X.map.depthTexture.magFilter=Ue):(X.map.depthTexture.compareFunction=null,X.map.depthTexture.minFilter=Ne,X.map.depthTexture.magFilter=Ne);X.camera.updateProjectionMatrix()}const pt=X.map.isWebGLCubeRenderTarget?6:1;for(let _t=0;_t0||R.map&&R.alphaTest>0||R.alphaToCoverage===!0){const F=P.uuid,q=R.uuid;let Y=c[F];Y===void 0&&(Y={},c[F]=Y);let B=Y[q];B===void 0&&(B=P.clone(),Y[q]=B,R.addEventListener("dispose",S)),P=B}if(P.visible=R.visible,P.wireframe=R.wireframe,T===As?P.side=R.shadowSide!==null?R.shadowSide:R.side:P.side=R.shadowSide!==null?R.shadowSide:h[R.side],P.alphaMap=R.alphaMap,P.alphaTest=R.alphaToCoverage===!0?.5:R.alphaTest,P.map=R.map,P.clipShadows=R.clipShadows,P.clippingPlanes=R.clippingPlanes,P.clipIntersection=R.clipIntersection,P.displacementMap=R.displacementMap,P.displacementScale=R.displacementScale,P.displacementBias=R.displacementBias,P.wireframeLinewidth=R.wireframeLinewidth,P.linewidth=R.linewidth,x.isPointLight===!0&&P.isMeshDistanceMaterial===!0){const F=s.properties.get(P);F.light=x}return P}function M(b,R,x,T,P){if(b.visible===!1)return;if(b.layers.test(R.layers)&&(b.isMesh||b.isLine||b.isPoints)&&(b.castShadow||b.receiveShadow&&P===As)&&(!b.frustumCulled||n.intersectsObject(b))){b.modelViewMatrix.multiplyMatrices(x.matrixWorldInverse,b.matrixWorld);const q=t.update(b),Y=b.material;if(Array.isArray(Y)){const B=q.groups;for(let K=0,X=B.length;K=1):it.indexOf("OpenGL ES")!==-1&&(Q=parseFloat(/^OpenGL ES (\d)/.exec(it)[1]),X=Q>=2);let pt=null,_t={};const bt=s.getParameter(s.SCISSOR_BOX),ee=s.getParameter(s.VIEWPORT),pe=new me().fromArray(bt),jt=new me().fromArray(ee);function j(N,ot,tt,ft){const Mt=new Uint8Array(4),nt=s.createTexture();s.bindTexture(N,nt),s.texParameteri(N,s.TEXTURE_MIN_FILTER,s.NEAREST),s.texParameteri(N,s.TEXTURE_MAG_FILTER,s.NEAREST);for(let Tt=0;Tt"u"?!1:/OculusBrowser/g.test(navigator.userAgent),c=new ht,u=new WeakMap,h=new Set;let d;const f=new WeakMap;let p=!1;try{p=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function v(A,_){return p?new OffscreenCanvas(A,_):_r("canvas")}function m(A,_,k){let V=1;const Z=re(A);if((Z.width>k||Z.height>k)&&(V=k/Math.max(Z.width,Z.height)),V<1)if(typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&A instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&A instanceof ImageBitmap||typeof VideoFrame<"u"&&A instanceof VideoFrame){const at=Math.floor(V*Z.width),ct=Math.floor(V*Z.height);d===void 0&&(d=v(at,ct));const J=_?v(at,ct):d;return J.width=at,J.height=ct,J.getContext("2d").drawImage(A,0,0,at,ct),Nt("WebGLRenderer: Texture has been resized from ("+Z.width+"x"+Z.height+") to ("+at+"x"+ct+")."),J}else return"data"in A&&Nt("WebGLRenderer: Image in DataTexture is too big ("+Z.width+"x"+Z.height+")."),A;return A}function g(A){return A.generateMipmaps}function w(A){s.generateMipmap(A)}function E(A){return A.isWebGLCubeRenderTarget?s.TEXTURE_CUBE_MAP:A.isWebGL3DRenderTarget?s.TEXTURE_3D:A.isWebGLArrayRenderTarget||A.isCompressedArrayTexture?s.TEXTURE_2D_ARRAY:s.TEXTURE_2D}function M(A,_,k,V,Z,at=!1){if(A!==null){if(s[A]!==void 0)return s[A];Nt("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+A+"'")}let ct;V&&(ct=t.get("EXT_texture_norm16"),ct||Nt("WebGLRenderer: Unable to use normalized textures without EXT_texture_norm16 extension"));let J=_;if(_===s.RED&&(k===s.FLOAT&&(J=s.R32F),k===s.HALF_FLOAT&&(J=s.R16F),k===s.UNSIGNED_BYTE&&(J=s.R8),k===s.UNSIGNED_SHORT&&ct&&(J=ct.R16_EXT),k===s.SHORT&&ct&&(J=ct.R16_SNORM_EXT)),_===s.RED_INTEGER&&(k===s.UNSIGNED_BYTE&&(J=s.R8UI),k===s.UNSIGNED_SHORT&&(J=s.R16UI),k===s.UNSIGNED_INT&&(J=s.R32UI),k===s.BYTE&&(J=s.R8I),k===s.SHORT&&(J=s.R16I),k===s.INT&&(J=s.R32I)),_===s.RG&&(k===s.FLOAT&&(J=s.RG32F),k===s.HALF_FLOAT&&(J=s.RG16F),k===s.UNSIGNED_BYTE&&(J=s.RG8),k===s.UNSIGNED_SHORT&&ct&&(J=ct.RG16_EXT),k===s.SHORT&&ct&&(J=ct.RG16_SNORM_EXT)),_===s.RG_INTEGER&&(k===s.UNSIGNED_BYTE&&(J=s.RG8UI),k===s.UNSIGNED_SHORT&&(J=s.RG16UI),k===s.UNSIGNED_INT&&(J=s.RG32UI),k===s.BYTE&&(J=s.RG8I),k===s.SHORT&&(J=s.RG16I),k===s.INT&&(J=s.RG32I)),_===s.RGB_INTEGER&&(k===s.UNSIGNED_BYTE&&(J=s.RGB8UI),k===s.UNSIGNED_SHORT&&(J=s.RGB16UI),k===s.UNSIGNED_INT&&(J=s.RGB32UI),k===s.BYTE&&(J=s.RGB8I),k===s.SHORT&&(J=s.RGB16I),k===s.INT&&(J=s.RGB32I)),_===s.RGBA_INTEGER&&(k===s.UNSIGNED_BYTE&&(J=s.RGBA8UI),k===s.UNSIGNED_SHORT&&(J=s.RGBA16UI),k===s.UNSIGNED_INT&&(J=s.RGBA32UI),k===s.BYTE&&(J=s.RGBA8I),k===s.SHORT&&(J=s.RGBA16I),k===s.INT&&(J=s.RGBA32I)),_===s.RGB&&(k===s.UNSIGNED_SHORT&&ct&&(J=ct.RGB16_EXT),k===s.SHORT&&ct&&(J=ct.RGB16_SNORM_EXT),k===s.UNSIGNED_INT_5_9_9_9_REV&&(J=s.RGB9_E5),k===s.UNSIGNED_INT_10F_11F_11F_REV&&(J=s.R11F_G11F_B10F)),_===s.RGBA){const et=at?gr:Kt.getTransfer(Z);k===s.FLOAT&&(J=s.RGBA32F),k===s.HALF_FLOAT&&(J=s.RGBA16F),k===s.UNSIGNED_BYTE&&(J=et===ie?s.SRGB8_ALPHA8:s.RGBA8),k===s.UNSIGNED_SHORT&&ct&&(J=ct.RGBA16_EXT),k===s.SHORT&&ct&&(J=ct.RGBA16_SNORM_EXT),k===s.UNSIGNED_SHORT_4_4_4_4&&(J=s.RGBA4),k===s.UNSIGNED_SHORT_5_5_5_1&&(J=s.RGB5_A1)}return(J===s.R16F||J===s.R32F||J===s.RG16F||J===s.RG32F||J===s.RGBA16F||J===s.RGBA32F)&&t.get("EXT_color_buffer_float"),J}function S(A,_){let k;return A?_===null||_===Tn||_===Rs?k=s.DEPTH24_STENCIL8:_===un?k=s.DEPTH32F_STENCIL8:_===Cs&&(k=s.DEPTH24_STENCIL8,Nt("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):_===null||_===Tn||_===Rs?k=s.DEPTH_COMPONENT24:_===un?k=s.DEPTH_COMPONENT32F:_===Cs&&(k=s.DEPTH_COMPONENT16),k}function b(A,_){return g(A)===!0||A.isFramebufferTexture&&A.minFilter!==Ne&&A.minFilter!==Ue?Math.log2(Math.max(_.width,_.height))+1:A.mipmaps!==void 0&&A.mipmaps.length>0?A.mipmaps.length:A.isCompressedTexture&&Array.isArray(A.image)?_.mipmaps.length:1}function R(A){const _=A.target;_.removeEventListener("dispose",R),T(_),_.isVideoTexture&&u.delete(_),_.isHTMLTexture&&h.delete(_)}function x(A){const _=A.target;_.removeEventListener("dispose",x),L(_)}function T(A){const _=n.get(A);if(_.__webglInit===void 0)return;const k=A.source,V=f.get(k);if(V){const Z=V[_.__cacheKey];Z.usedTimes--,Z.usedTimes===0&&P(A),Object.keys(V).length===0&&f.delete(k)}n.remove(A)}function P(A){const _=n.get(A);s.deleteTexture(_.__webglTexture);const k=A.source,V=f.get(k);delete V[_.__cacheKey],a.memory.textures--}function L(A){const _=n.get(A);if(A.depthTexture&&(A.depthTexture.dispose(),n.remove(A.depthTexture)),A.isWebGLCubeRenderTarget)for(let V=0;V<6;V++){if(Array.isArray(_.__webglFramebuffer[V]))for(let Z=0;Z<_.__webglFramebuffer[V].length;Z++)s.deleteFramebuffer(_.__webglFramebuffer[V][Z]);else s.deleteFramebuffer(_.__webglFramebuffer[V]);_.__webglDepthbuffer&&s.deleteRenderbuffer(_.__webglDepthbuffer[V])}else{if(Array.isArray(_.__webglFramebuffer))for(let V=0;V<_.__webglFramebuffer.length;V++)s.deleteFramebuffer(_.__webglFramebuffer[V]);else s.deleteFramebuffer(_.__webglFramebuffer);if(_.__webglDepthbuffer&&s.deleteRenderbuffer(_.__webglDepthbuffer),_.__webglMultisampledFramebuffer&&s.deleteFramebuffer(_.__webglMultisampledFramebuffer),_.__webglColorRenderbuffer)for(let V=0;V<_.__webglColorRenderbuffer.length;V++)_.__webglColorRenderbuffer[V]&&s.deleteRenderbuffer(_.__webglColorRenderbuffer[V]);_.__webglDepthRenderbuffer&&s.deleteRenderbuffer(_.__webglDepthRenderbuffer)}const k=A.textures;for(let V=0,Z=k.length;V=i.maxTextures&&Nt("WebGLTextures: Trying to use "+A+" texture units while this GPU supports only "+i.maxTextures),F+=1,A}function X(A){const _=[];return _.push(A.wrapS),_.push(A.wrapT),_.push(A.wrapR||0),_.push(A.magFilter),_.push(A.minFilter),_.push(A.anisotropy),_.push(A.internalFormat),_.push(A.format),_.push(A.type),_.push(A.generateMipmaps),_.push(A.premultiplyAlpha),_.push(A.flipY),_.push(A.unpackAlignment),_.push(A.colorSpace),_.join()}function Q(A,_){const k=n.get(A);if(A.isVideoTexture&&U(A),A.isRenderTargetTexture===!1&&A.isExternalTexture!==!0&&A.version>0&&k.__version!==A.version){const V=A.image;if(V===null)Nt("WebGLRenderer: Texture marked for update but no image data found.");else if(V.complete===!1)Nt("WebGLRenderer: Texture marked for update but image is incomplete");else{st(k,A,_);return}}else A.isExternalTexture&&(k.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(s.TEXTURE_2D,k.__webglTexture,s.TEXTURE0+_)}function it(A,_){const k=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&k.__version!==A.version){st(k,A,_);return}else A.isExternalTexture&&(k.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(s.TEXTURE_2D_ARRAY,k.__webglTexture,s.TEXTURE0+_)}function pt(A,_){const k=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&k.__version!==A.version){st(k,A,_);return}e.bindTexture(s.TEXTURE_3D,k.__webglTexture,s.TEXTURE0+_)}function _t(A,_){const k=n.get(A);if(A.isCubeDepthTexture!==!0&&A.version>0&&k.__version!==A.version){lt(k,A,_);return}e.bindTexture(s.TEXTURE_CUBE_MAP,k.__webglTexture,s.TEXTURE0+_)}const bt={[lr]:s.REPEAT,[kn]:s.CLAMP_TO_EDGE,[ka]:s.MIRRORED_REPEAT},ee={[Ne]:s.NEAREST,[zd]:s.NEAREST_MIPMAP_NEAREST,[cr]:s.NEAREST_MIPMAP_LINEAR,[Ue]:s.LINEAR,[Ba]:s.LINEAR_MIPMAP_NEAREST,[Ti]:s.LINEAR_MIPMAP_LINEAR},pe={[Vd]:s.NEVER,[Yd]:s.ALWAYS,[Wd]:s.LESS,[Eo]:s.LEQUAL,[$d]:s.EQUAL,[To]:s.GEQUAL,[Xd]:s.GREATER,[qd]:s.NOTEQUAL};function jt(A,_){if(_.type===un&&t.has("OES_texture_float_linear")===!1&&(_.magFilter===Ue||_.magFilter===Ba||_.magFilter===cr||_.magFilter===Ti||_.minFilter===Ue||_.minFilter===Ba||_.minFilter===cr||_.minFilter===Ti)&&Nt("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),s.texParameteri(A,s.TEXTURE_WRAP_S,bt[_.wrapS]),s.texParameteri(A,s.TEXTURE_WRAP_T,bt[_.wrapT]),(A===s.TEXTURE_3D||A===s.TEXTURE_2D_ARRAY)&&s.texParameteri(A,s.TEXTURE_WRAP_R,bt[_.wrapR]),s.texParameteri(A,s.TEXTURE_MAG_FILTER,ee[_.magFilter]),s.texParameteri(A,s.TEXTURE_MIN_FILTER,ee[_.minFilter]),_.compareFunction&&(s.texParameteri(A,s.TEXTURE_COMPARE_MODE,s.COMPARE_REF_TO_TEXTURE),s.texParameteri(A,s.TEXTURE_COMPARE_FUNC,pe[_.compareFunction])),t.has("EXT_texture_filter_anisotropic")===!0){if(_.magFilter===Ne||_.minFilter!==cr&&_.minFilter!==Ti||_.type===un&&t.has("OES_texture_float_linear")===!1)return;if(_.anisotropy>1||n.get(_).__currentAnisotropy){const k=t.get("EXT_texture_filter_anisotropic");s.texParameterf(A,k.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(_.anisotropy,i.getMaxAnisotropy())),n.get(_).__currentAnisotropy=_.anisotropy}}}function j(A,_){let k=!1;A.__webglInit===void 0&&(A.__webglInit=!0,_.addEventListener("dispose",R));const V=_.source;let Z=f.get(V);Z===void 0&&(Z={},f.set(V,Z));const at=X(_);if(at!==A.__cacheKey){Z[at]===void 0&&(Z[at]={texture:s.createTexture(),usedTimes:0},a.memory.textures++,k=!0),Z[at].usedTimes++;const ct=Z[A.__cacheKey];ct!==void 0&&(Z[A.__cacheKey].usedTimes--,ct.usedTimes===0&&P(_)),A.__cacheKey=at,A.__webglTexture=Z[at].texture}return k}function D(A,_,k){return Math.floor(Math.floor(A/k)/_)}function W(A,_,k,V){const at=A.updateRanges;if(at.length===0)e.texSubImage2D(s.TEXTURE_2D,0,0,0,_.width,_.height,k,V,_.data);else{at.sort((At,gt)=>At.start-gt.start);let ct=0;for(let At=1;At0){Ut&&Gt&&e.texStorage2D(s.TEXTURE_2D,ot,gt,Dt[0].width,Dt[0].height);for(let tt=0,ft=Dt.length;tt0){const Mt=fh(dt.width,dt.height,_.format,_.type);for(const nt of _.layerUpdates){const Tt=dt.data.subarray(nt*Mt/dt.data.BYTES_PER_ELEMENT,(nt+1)*Mt/dt.data.BYTES_PER_ELEMENT);e.compressedTexSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,nt,dt.width,dt.height,1,ut,Tt)}_.clearLayerUpdates()}else e.compressedTexSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,0,dt.width,dt.height,et.depth,ut,dt.data)}else e.compressedTexImage3D(s.TEXTURE_2D_ARRAY,tt,gt,dt.width,dt.height,et.depth,0,dt.data,0,0);else Nt("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else Ut?N&&e.texSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,0,dt.width,dt.height,et.depth,ut,At,dt.data):e.texImage3D(s.TEXTURE_2D_ARRAY,tt,gt,dt.width,dt.height,et.depth,0,ut,At,dt.data)}else{Ut&&Gt&&e.texStorage2D(s.TEXTURE_2D,ot,gt,Dt[0].width,Dt[0].height);for(let tt=0,ft=Dt.length;tt0){const tt=fh(et.width,et.height,_.format,_.type);for(const ft of _.layerUpdates){const Mt=et.data.subarray(ft*tt/et.data.BYTES_PER_ELEMENT,(ft+1)*tt/et.data.BYTES_PER_ELEMENT);e.texSubImage3D(s.TEXTURE_2D_ARRAY,0,0,0,ft,et.width,et.height,1,ut,At,Mt)}_.clearLayerUpdates()}else e.texSubImage3D(s.TEXTURE_2D_ARRAY,0,0,0,0,et.width,et.height,et.depth,ut,At,et.data)}else e.texImage3D(s.TEXTURE_2D_ARRAY,0,gt,et.width,et.height,et.depth,0,ut,At,et.data);else if(_.isData3DTexture)Ut?(Gt&&e.texStorage3D(s.TEXTURE_3D,ot,gt,et.width,et.height,et.depth),N&&e.texSubImage3D(s.TEXTURE_3D,0,0,0,0,et.width,et.height,et.depth,ut,At,et.data)):e.texImage3D(s.TEXTURE_3D,0,gt,et.width,et.height,et.depth,0,ut,At,et.data);else if(_.isFramebufferTexture){if(Gt)if(Ut)e.texStorage2D(s.TEXTURE_2D,ot,gt,et.width,et.height);else{let tt=et.width,ft=et.height;for(let Mt=0;Mt>=1,ft>>=1}}else if(_.isHTMLTexture){if("texElementImage2D"in s){const tt=s.canvas;if(tt.hasAttribute("layoutsubtree")||tt.setAttribute("layoutsubtree","true"),et.parentNode!==tt){tt.appendChild(et),h.add(_),tt.onpaint=ft=>{const Mt=ft.changedElements;for(const nt of h)Mt.includes(nt.image)&&(nt.needsUpdate=!0)},tt.requestPaint();return}if(s.texElementImage2D.length===3)s.texElementImage2D(s.TEXTURE_2D,s.RGBA8,et);else{const Mt=s.RGBA,nt=s.RGBA,Tt=s.UNSIGNED_BYTE;s.texElementImage2D(s.TEXTURE_2D,0,Mt,nt,Tt,et)}s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE)}}else if(Dt.length>0){if(Ut&&Gt){const tt=re(Dt[0]);e.texStorage2D(s.TEXTURE_2D,ot,gt,tt.width,tt.height)}for(let tt=0,ft=Dt.length;tt0&&ft++;const nt=re(gt[0]);e.texStorage2D(s.TEXTURE_CUBE_MAP,ft,Gt,nt.width,nt.height)}for(let nt=0;nt<6;nt++)if(At){N?tt&&e.texSubImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+nt,0,0,0,gt[nt].width,gt[nt].height,Dt,Ut,gt[nt].data):e.texImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+nt,0,Gt,gt[nt].width,gt[nt].height,0,Dt,Ut,gt[nt].data);for(let Tt=0;Tt>at),dt=Math.max(1,_.height>>at);Z===s.TEXTURE_3D||Z===s.TEXTURE_2D_ARRAY?e.texImage3D(Z,at,et,gt,dt,_.depth,0,ct,J,null):e.texImage2D(Z,at,et,gt,dt,0,ct,J,null)}e.bindFramebuffer(s.FRAMEBUFFER,A),Ae(_)?o.framebufferTexture2DMultisampleEXT(s.FRAMEBUFFER,V,Z,At.__webglTexture,0,ge(_)):(Z===s.TEXTURE_2D||Z>=s.TEXTURE_CUBE_MAP_POSITIVE_X&&Z<=s.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&s.framebufferTexture2D(s.FRAMEBUFFER,V,Z,At.__webglTexture,at),e.bindFramebuffer(s.FRAMEBUFFER,null)}function Ft(A,_,k){if(s.bindRenderbuffer(s.RENDERBUFFER,A),_.depthBuffer){const V=_.depthTexture,Z=V&&V.isDepthTexture?V.type:null,at=S(_.stencilBuffer,Z),ct=_.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT;Ae(_)?o.renderbufferStorageMultisampleEXT(s.RENDERBUFFER,ge(_),at,_.width,_.height):k?s.renderbufferStorageMultisample(s.RENDERBUFFER,ge(_),at,_.width,_.height):s.renderbufferStorage(s.RENDERBUFFER,at,_.width,_.height),s.framebufferRenderbuffer(s.FRAMEBUFFER,ct,s.RENDERBUFFER,A)}else{const V=_.textures;for(let Z=0;Z{delete _.__boundDepthTexture,delete _.__depthDisposeCallback,V.removeEventListener("dispose",Z)};V.addEventListener("dispose",Z),_.__depthDisposeCallback=Z}_.__boundDepthTexture=V}if(A.depthTexture&&!_.__autoAllocateDepthBuffer)if(k)for(let V=0;V<6;V++)Lt(_.__webglFramebuffer[V],A,V);else{const V=A.texture.mipmaps;V&&V.length>0?Lt(_.__webglFramebuffer[0],A,0):Lt(_.__webglFramebuffer,A,0)}else if(k){_.__webglDepthbuffer=[];for(let V=0;V<6;V++)if(e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer[V]),_.__webglDepthbuffer[V]===void 0)_.__webglDepthbuffer[V]=s.createRenderbuffer(),Ft(_.__webglDepthbuffer[V],A,!1);else{const Z=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,at=_.__webglDepthbuffer[V];s.bindRenderbuffer(s.RENDERBUFFER,at),s.framebufferRenderbuffer(s.FRAMEBUFFER,Z,s.RENDERBUFFER,at)}}else{const V=A.texture.mipmaps;if(V&&V.length>0?e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer[0]):e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer),_.__webglDepthbuffer===void 0)_.__webglDepthbuffer=s.createRenderbuffer(),Ft(_.__webglDepthbuffer,A,!1);else{const Z=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,at=_.__webglDepthbuffer;s.bindRenderbuffer(s.RENDERBUFFER,at),s.framebufferRenderbuffer(s.FRAMEBUFFER,Z,s.RENDERBUFFER,at)}}e.bindFramebuffer(s.FRAMEBUFFER,null)}function Zt(A,_,k){const V=n.get(A);_!==void 0&&rt(V.__webglFramebuffer,A,A.texture,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,0),k!==void 0&&Ht(A)}function Jt(A){const _=A.texture,k=n.get(A),V=n.get(_);A.addEventListener("dispose",x);const Z=A.textures,at=A.isWebGLCubeRenderTarget===!0,ct=Z.length>1;if(ct||(V.__webglTexture===void 0&&(V.__webglTexture=s.createTexture()),V.__version=_.version,a.memory.textures++),at){k.__webglFramebuffer=[];for(let J=0;J<6;J++)if(_.mipmaps&&_.mipmaps.length>0){k.__webglFramebuffer[J]=[];for(let et=0;et<_.mipmaps.length;et++)k.__webglFramebuffer[J][et]=s.createFramebuffer()}else k.__webglFramebuffer[J]=s.createFramebuffer()}else{if(_.mipmaps&&_.mipmaps.length>0){k.__webglFramebuffer=[];for(let J=0;J<_.mipmaps.length;J++)k.__webglFramebuffer[J]=s.createFramebuffer()}else k.__webglFramebuffer=s.createFramebuffer();if(ct)for(let J=0,et=Z.length;J0&&Ae(A)===!1){k.__webglMultisampledFramebuffer=s.createFramebuffer(),k.__webglColorRenderbuffer=[],e.bindFramebuffer(s.FRAMEBUFFER,k.__webglMultisampledFramebuffer);for(let J=0;J0)for(let et=0;et<_.mipmaps.length;et++)rt(k.__webglFramebuffer[J][et],A,_,s.COLOR_ATTACHMENT0,s.TEXTURE_CUBE_MAP_POSITIVE_X+J,et);else rt(k.__webglFramebuffer[J],A,_,s.COLOR_ATTACHMENT0,s.TEXTURE_CUBE_MAP_POSITIVE_X+J,0);g(_)&&w(s.TEXTURE_CUBE_MAP),e.unbindTexture()}else if(ct){for(let J=0,et=Z.length;J0)for(let et=0;et<_.mipmaps.length;et++)rt(k.__webglFramebuffer[et],A,_,s.COLOR_ATTACHMENT0,J,et);else rt(k.__webglFramebuffer,A,_,s.COLOR_ATTACHMENT0,J,0);g(_)&&w(J),e.unbindTexture()}A.depthBuffer&&Ht(A)}function te(A){const _=A.textures;for(let k=0,V=_.length;k0){if(Ae(A)===!1){const _=A.textures,k=A.width,V=A.height;let Z=s.COLOR_BUFFER_BIT;const at=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,ct=n.get(A),J=_.length>1;if(J)for(let ut=0;ut<_.length;ut++)e.bindFramebuffer(s.FRAMEBUFFER,ct.__webglMultisampledFramebuffer),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+ut,s.RENDERBUFFER,null),e.bindFramebuffer(s.FRAMEBUFFER,ct.__webglFramebuffer),s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0+ut,s.TEXTURE_2D,null,0);e.bindFramebuffer(s.READ_FRAMEBUFFER,ct.__webglMultisampledFramebuffer);const et=A.texture.mipmaps;et&&et.length>0?e.bindFramebuffer(s.DRAW_FRAMEBUFFER,ct.__webglFramebuffer[0]):e.bindFramebuffer(s.DRAW_FRAMEBUFFER,ct.__webglFramebuffer);for(let ut=0;ut<_.length;ut++){if(A.resolveDepthBuffer&&(A.depthBuffer&&(Z|=s.DEPTH_BUFFER_BIT),A.stencilBuffer&&A.resolveStencilBuffer&&(Z|=s.STENCIL_BUFFER_BIT)),J){s.framebufferRenderbuffer(s.READ_FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.RENDERBUFFER,ct.__webglColorRenderbuffer[ut]);const At=n.get(_[ut]).__webglTexture;s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,At,0)}s.blitFramebuffer(0,0,k,V,0,0,k,V,Z,s.NEAREST),l===!0&&(le.length=0,ce.length=0,le.push(s.COLOR_ATTACHMENT0+ut),A.depthBuffer&&A.resolveDepthBuffer===!1&&(le.push(at),ce.push(at),s.invalidateFramebuffer(s.DRAW_FRAMEBUFFER,ce)),s.invalidateFramebuffer(s.READ_FRAMEBUFFER,le))}if(e.bindFramebuffer(s.READ_FRAMEBUFFER,null),e.bindFramebuffer(s.DRAW_FRAMEBUFFER,null),J)for(let ut=0;ut<_.length;ut++){e.bindFramebuffer(s.FRAMEBUFFER,ct.__webglMultisampledFramebuffer),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+ut,s.RENDERBUFFER,ct.__webglColorRenderbuffer[ut]);const At=n.get(_[ut]).__webglTexture;e.bindFramebuffer(s.FRAMEBUFFER,ct.__webglFramebuffer),s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0+ut,s.TEXTURE_2D,At,0)}e.bindFramebuffer(s.DRAW_FRAMEBUFFER,ct.__webglMultisampledFramebuffer)}else if(A.depthBuffer&&A.resolveDepthBuffer===!1&&l){const _=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT;s.invalidateFramebuffer(s.DRAW_FRAMEBUFFER,[_])}}}function ge(A){return Math.min(i.maxSamples,A.samples)}function Ae(A){const _=n.get(A);return A.samples>0&&t.has("WEBGL_multisampled_render_to_texture")===!0&&_.__useRenderToTexture!==!1}function U(A){const _=a.render.frame;u.get(A)!==_&&(u.set(A,_),A.update())}function Ke(A,_){const k=A.colorSpace,V=A.format,Z=A.type;return A.isCompressedTexture===!0||A.isVideoTexture===!0||k!==Ps&&k!==ti&&(Kt.getTransfer(k)===ie?(V!==dn||Z!==Ze)&&Nt("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):Qt("WebGLTextures: Unsupported texture color space:",k)),_}function re(A){return typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement?(c.width=A.naturalWidth||A.width,c.height=A.naturalHeight||A.height):typeof VideoFrame<"u"&&A instanceof VideoFrame?(c.width=A.displayWidth,c.height=A.displayHeight):(c.width=A.width,c.height=A.height),c}this.allocateTextureUnit=K,this.resetTextureUnits=q,this.getTextureUnits=Y,this.setTextureUnits=B,this.setTexture2D=Q,this.setTexture2DArray=it,this.setTexture3D=pt,this.setTextureCube=_t,this.rebindTextures=Zt,this.setupRenderTarget=Jt,this.updateRenderTargetMipmap=te,this.updateMultisampleRenderTarget=Te,this.setupDepthRenderbuffer=Ht,this.setupFrameBufferTexture=rt,this.useMultisampledRTT=Ae,this.isReversedDepthBuffer=function(){return e.buffers.depth.getReversed()}}function w_(s,t){function e(n,i=ti){let r;const a=Kt.getTransfer(i);if(n===Ze)return s.UNSIGNED_BYTE;if(n===Ha)return s.UNSIGNED_SHORT_4_4_4_4;if(n===Ga)return s.UNSIGNED_SHORT_5_5_5_1;if(n===uc)return s.UNSIGNED_INT_5_9_9_9_REV;if(n===dc)return s.UNSIGNED_INT_10F_11F_11F_REV;if(n===cc)return s.BYTE;if(n===hc)return s.SHORT;if(n===Cs)return s.UNSIGNED_SHORT;if(n===za)return s.INT;if(n===Tn)return s.UNSIGNED_INT;if(n===un)return s.FLOAT;if(n===Je)return s.HALF_FLOAT;if(n===fc)return s.ALPHA;if(n===pc)return s.RGB;if(n===dn)return s.RGBA;if(n===Bn)return s.DEPTH_COMPONENT;if(n===Ai)return s.DEPTH_STENCIL;if(n===Va)return s.RED;if(n===Wa)return s.RED_INTEGER;if(n===Ci)return s.RG;if(n===$a)return s.RG_INTEGER;if(n===Xa)return s.RGBA_INTEGER;if(n===hr||n===ur||n===dr||n===fr)if(a===ie)if(r=t.get("WEBGL_compressed_texture_s3tc_srgb"),r!==null){if(n===hr)return r.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(n===ur)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(n===dr)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(n===fr)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(r=t.get("WEBGL_compressed_texture_s3tc"),r!==null){if(n===hr)return r.COMPRESSED_RGB_S3TC_DXT1_EXT;if(n===ur)return r.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(n===dr)return r.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(n===fr)return r.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(n===qa||n===Ya||n===Ka||n===Za)if(r=t.get("WEBGL_compressed_texture_pvrtc"),r!==null){if(n===qa)return r.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(n===Ya)return r.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(n===Ka)return r.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(n===Za)return r.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(n===Ja||n===Qa||n===ja||n===to||n===eo||n===pr||n===no)if(r=t.get("WEBGL_compressed_texture_etc"),r!==null){if(n===Ja||n===Qa)return a===ie?r.COMPRESSED_SRGB8_ETC2:r.COMPRESSED_RGB8_ETC2;if(n===ja)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:r.COMPRESSED_RGBA8_ETC2_EAC;if(n===to)return r.COMPRESSED_R11_EAC;if(n===eo)return r.COMPRESSED_SIGNED_R11_EAC;if(n===pr)return r.COMPRESSED_RG11_EAC;if(n===no)return r.COMPRESSED_SIGNED_RG11_EAC}else return null;if(n===io||n===so||n===ro||n===ao||n===oo||n===lo||n===co||n===ho||n===uo||n===fo||n===po||n===mo||n===go||n===_o)if(r=t.get("WEBGL_compressed_texture_astc"),r!==null){if(n===io)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:r.COMPRESSED_RGBA_ASTC_4x4_KHR;if(n===so)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:r.COMPRESSED_RGBA_ASTC_5x4_KHR;if(n===ro)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:r.COMPRESSED_RGBA_ASTC_5x5_KHR;if(n===ao)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:r.COMPRESSED_RGBA_ASTC_6x5_KHR;if(n===oo)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:r.COMPRESSED_RGBA_ASTC_6x6_KHR;if(n===lo)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:r.COMPRESSED_RGBA_ASTC_8x5_KHR;if(n===co)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:r.COMPRESSED_RGBA_ASTC_8x6_KHR;if(n===ho)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:r.COMPRESSED_RGBA_ASTC_8x8_KHR;if(n===uo)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:r.COMPRESSED_RGBA_ASTC_10x5_KHR;if(n===fo)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:r.COMPRESSED_RGBA_ASTC_10x6_KHR;if(n===po)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:r.COMPRESSED_RGBA_ASTC_10x8_KHR;if(n===mo)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:r.COMPRESSED_RGBA_ASTC_10x10_KHR;if(n===go)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:r.COMPRESSED_RGBA_ASTC_12x10_KHR;if(n===_o)return a===ie?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:r.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(n===xo||n===vo||n===Mo)if(r=t.get("EXT_texture_compression_bptc"),r!==null){if(n===xo)return a===ie?r.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:r.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(n===vo)return r.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(n===Mo)return r.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(n===yo||n===So||n===mr||n===bo)if(r=t.get("EXT_texture_compression_rgtc"),r!==null){if(n===yo)return r.COMPRESSED_RED_RGTC1_EXT;if(n===So)return r.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(n===mr)return r.COMPRESSED_RED_GREEN_RGTC2_EXT;if(n===bo)return r.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return n===Rs?s.UNSIGNED_INT_24_8:s[n]!==void 0?s[n]:null}return{convert:e}}const E_=` +}`,Hx=[new P(1,0,0),new P(-1,0,0),new P(0,1,0),new P(0,-1,0),new P(0,0,1),new P(0,0,-1)],Gx=[new P(0,-1,0),new P(0,-1,0),new P(0,0,1),new P(0,0,-1),new P(0,-1,0),new P(0,-1,0)],Nu=new re,dr=new P,Yl=new P;function Vx(s,t,e){let n=new ro;const i=new ft,r=new ft,o=new ge,a=new Tm,l=new Am,c={},u=e.maxTextureSize,h={[ai]:We,[We]:ai,[Gn]:Gn},d=new Pe({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new ft},radius:{value:4}},vertexShader:Bx,fragmentShader:zx}),f=d.clone();f.defines.HORIZONTAL_PASS=1;const p=new He;p.setAttribute("position",new ln(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const v=new zt(p,d),m=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=Er;let g=this.type;this.render=function(w,C,x){if(m.enabled===!1||m.autoUpdate===!1&&m.needsUpdate===!1||w.length===0)return;this.type===Gf&&(Ut("WebGLShadowMap: PCFSoftShadowMap has been deprecated. Using PCFShadowMap instead."),this.type=Er);const T=s.getRenderTarget(),L=s.getActiveCubeFace(),D=s.getActiveMipmapLevel(),U=s.state;U.setBlending(An),U.buffers.depth.getReversed()===!0?U.buffers.color.setClear(0,0,0,0):U.buffers.color.setClear(1,1,1,1),U.buffers.depth.setTest(!0),U.setScissorTest(!1);const K=g!==this.type;K&&C.traverse(function(Z){Z.material&&(Array.isArray(Z.material)?Z.material.forEach(N=>N.needsUpdate=!0):Z.material.needsUpdate=!0)});for(let Z=0,N=w.length;Zu||i.y>u)&&(i.x>u&&(r.x=Math.floor(u/X.x),i.x=r.x*X.x,B.mapSize.x=r.x),i.y>u&&(r.y=Math.floor(u/X.y),i.y=r.y*X.y,B.mapSize.y=r.y));const nt=s.state.buffers.depth.getReversed();if(B.camera._reversedDepth=nt,B.map===null||K===!0){if(B.map!==null&&(B.map.depthTexture!==null&&(B.map.depthTexture.dispose(),B.map.depthTexture=null),B.map.dispose()),this.type===$s){if(W.isPointLight){Ut("WebGLShadowMap: VSM shadow maps are not supported for PointLights. Use PCF or BasicShadowMap instead.");continue}B.map=new Ye(i.x,i.y,{format:Oi,type:Qe,minFilter:Oe,magFilter:Oe,generateMipmaps:!1}),B.map.texture.name=W.name+".shadowMap",B.map.depthTexture=new Ts(i.x,i.y,mn),B.map.depthTexture.name=W.name+".shadowMapDepth",B.map.depthTexture.format=Wn,B.map.depthTexture.compareFunction=null,B.map.depthTexture.minFilter=Fe,B.map.depthTexture.magFilter=Fe}else W.isPointLight?(B.map=new fu(i.x),B.map.depthTexture=new am(i.x,Rn)):(B.map=new Ye(i.x,i.y),B.map.depthTexture=new Ts(i.x,i.y,Rn)),B.map.depthTexture.name=W.name+".shadowMap",B.map.depthTexture.format=Wn,this.type===Er?(B.map.depthTexture.compareFunction=nt?il:nl,B.map.depthTexture.minFilter=Oe,B.map.depthTexture.magFilter=Oe):(B.map.depthTexture.compareFunction=null,B.map.depthTexture.minFilter=Fe,B.map.depthTexture.magFilter=Fe);B.camera.updateProjectionMatrix()}const ot=B.map.isWebGLCubeRenderTarget?6:1;for(let at=0;at0||C.map&&C.alphaTest>0||C.alphaToCoverage===!0){const U=L.uuid,K=C.uuid;let Z=c[U];Z===void 0&&(Z={},c[U]=Z);let N=Z[K];N===void 0&&(N=L.clone(),Z[K]=N,C.addEventListener("dispose",M)),L=N}if(L.visible=C.visible,L.wireframe=C.wireframe,T===$s?L.side=C.shadowSide!==null?C.shadowSide:C.side:L.side=C.shadowSide!==null?C.shadowSide:h[C.side],L.alphaMap=C.alphaMap,L.alphaTest=C.alphaToCoverage===!0?.5:C.alphaTest,L.map=C.map,L.clipShadows=C.clipShadows,L.clippingPlanes=C.clippingPlanes,L.clipIntersection=C.clipIntersection,L.displacementMap=C.displacementMap,L.displacementScale=C.displacementScale,L.displacementBias=C.displacementBias,L.wireframeLinewidth=C.wireframeLinewidth,L.linewidth=C.linewidth,x.isPointLight===!0&&L.isMeshDistanceMaterial===!0){const U=s.properties.get(L);U.light=x}return L}function y(w,C,x,T,L){if(w.visible===!1)return;if(w.layers.test(C.layers)&&(w.isMesh||w.isLine||w.isPoints)&&(w.castShadow||w.receiveShadow&&L===$s)&&(!w.frustumCulled||n.intersectsObject(w))){w.modelViewMatrix.multiplyMatrices(x.matrixWorldInverse,w.matrixWorld);const K=t.update(w),Z=w.material;if(Array.isArray(Z)){const N=K.groups;for(let W=0,B=N.length;W=1):nt.indexOf("OpenGL ES")!==-1&&(X=parseFloat(/^OpenGL ES (\d)/.exec(nt)[1]),B=X>=2);let ot=null,at={};const vt=s.getParameter(s.SCISSOR_BOX),Gt=s.getParameter(s.VIEWPORT),me=new ge().fromArray(vt),ee=new ge().fromArray(Gt);function Q(F,ht,tt,gt){const bt=new Uint8Array(4),it=s.createTexture();s.bindTexture(F,it),s.texParameteri(F,s.TEXTURE_MIN_FILTER,s.NEAREST),s.texParameteri(F,s.TEXTURE_MAG_FILTER,s.NEAREST);for(let At=0;At"u"?!1:/OculusBrowser/g.test(navigator.userAgent),c=new ft,u=new WeakMap,h=new Set;let d;const f=new WeakMap;let p=!1;try{p=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function v(A,_){return p?new OffscreenCanvas(A,_):kr("canvas")}function m(A,_,z){let $=1;const J=oe(A);if((J.width>z||J.height>z)&&($=z/Math.max(J.width,J.height)),$<1)if(typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&A instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&A instanceof ImageBitmap||typeof VideoFrame<"u"&&A instanceof VideoFrame){const ct=Math.floor($*J.width),dt=Math.floor($*J.height);d===void 0&&(d=v(ct,dt));const j=_?v(ct,dt):d;return j.width=ct,j.height=dt,j.getContext("2d").drawImage(A,0,0,ct,dt),Ut("WebGLRenderer: Texture has been resized from ("+J.width+"x"+J.height+") to ("+ct+"x"+dt+")."),j}else return"data"in A&&Ut("WebGLRenderer: Image in DataTexture is too big ("+J.width+"x"+J.height+")."),A;return A}function g(A){return A.generateMipmaps}function S(A){s.generateMipmap(A)}function E(A){return A.isWebGLCubeRenderTarget?s.TEXTURE_CUBE_MAP:A.isWebGL3DRenderTarget?s.TEXTURE_3D:A.isWebGLArrayRenderTarget||A.isCompressedArrayTexture?s.TEXTURE_2D_ARRAY:s.TEXTURE_2D}function y(A,_,z,$,J,ct=!1){if(A!==null){if(s[A]!==void 0)return s[A];Ut("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+A+"'")}let dt;$&&(dt=t.get("EXT_texture_norm16"),dt||Ut("WebGLRenderer: Unable to use normalized textures without EXT_texture_norm16 extension"));let j=_;if(_===s.RED&&(z===s.FLOAT&&(j=s.R32F),z===s.HALF_FLOAT&&(j=s.R16F),z===s.UNSIGNED_BYTE&&(j=s.R8),z===s.UNSIGNED_SHORT&&dt&&(j=dt.R16_EXT),z===s.SHORT&&dt&&(j=dt.R16_SNORM_EXT)),_===s.RED_INTEGER&&(z===s.UNSIGNED_BYTE&&(j=s.R8UI),z===s.UNSIGNED_SHORT&&(j=s.R16UI),z===s.UNSIGNED_INT&&(j=s.R32UI),z===s.BYTE&&(j=s.R8I),z===s.SHORT&&(j=s.R16I),z===s.INT&&(j=s.R32I)),_===s.RG&&(z===s.FLOAT&&(j=s.RG32F),z===s.HALF_FLOAT&&(j=s.RG16F),z===s.UNSIGNED_BYTE&&(j=s.RG8),z===s.UNSIGNED_SHORT&&dt&&(j=dt.RG16_EXT),z===s.SHORT&&dt&&(j=dt.RG16_SNORM_EXT)),_===s.RG_INTEGER&&(z===s.UNSIGNED_BYTE&&(j=s.RG8UI),z===s.UNSIGNED_SHORT&&(j=s.RG16UI),z===s.UNSIGNED_INT&&(j=s.RG32UI),z===s.BYTE&&(j=s.RG8I),z===s.SHORT&&(j=s.RG16I),z===s.INT&&(j=s.RG32I)),_===s.RGB_INTEGER&&(z===s.UNSIGNED_BYTE&&(j=s.RGB8UI),z===s.UNSIGNED_SHORT&&(j=s.RGB16UI),z===s.UNSIGNED_INT&&(j=s.RGB32UI),z===s.BYTE&&(j=s.RGB8I),z===s.SHORT&&(j=s.RGB16I),z===s.INT&&(j=s.RGB32I)),_===s.RGBA_INTEGER&&(z===s.UNSIGNED_BYTE&&(j=s.RGBA8UI),z===s.UNSIGNED_SHORT&&(j=s.RGBA16UI),z===s.UNSIGNED_INT&&(j=s.RGBA32UI),z===s.BYTE&&(j=s.RGBA8I),z===s.SHORT&&(j=s.RGBA16I),z===s.INT&&(j=s.RGBA32I)),_===s.RGB&&(z===s.UNSIGNED_SHORT&&dt&&(j=dt.RGB16_EXT),z===s.SHORT&&dt&&(j=dt.RGB16_SNORM_EXT),z===s.UNSIGNED_INT_5_9_9_9_REV&&(j=s.RGB9_E5),z===s.UNSIGNED_INT_10F_11F_11F_REV&&(j=s.R11F_G11F_B10F)),_===s.RGBA){const et=ct?Or:Jt.getTransfer(J);z===s.FLOAT&&(j=s.RGBA32F),z===s.HALF_FLOAT&&(j=s.RGBA16F),z===s.UNSIGNED_BYTE&&(j=et===se?s.SRGB8_ALPHA8:s.RGBA8),z===s.UNSIGNED_SHORT&&dt&&(j=dt.RGBA16_EXT),z===s.SHORT&&dt&&(j=dt.RGBA16_SNORM_EXT),z===s.UNSIGNED_SHORT_4_4_4_4&&(j=s.RGBA4),z===s.UNSIGNED_SHORT_5_5_5_1&&(j=s.RGB5_A1)}return(j===s.R16F||j===s.R32F||j===s.RG16F||j===s.RG32F||j===s.RGBA16F||j===s.RGBA32F)&&t.get("EXT_color_buffer_float"),j}function M(A,_){let z;return A?_===null||_===Rn||_===qs?z=s.DEPTH24_STENCIL8:_===mn?z=s.DEPTH32F_STENCIL8:_===Xs&&(z=s.DEPTH24_STENCIL8,Ut("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):_===null||_===Rn||_===qs?z=s.DEPTH_COMPONENT24:_===mn?z=s.DEPTH_COMPONENT32F:_===Xs&&(z=s.DEPTH_COMPONENT16),z}function w(A,_){return g(A)===!0||A.isFramebufferTexture&&A.minFilter!==Fe&&A.minFilter!==Oe?Math.log2(Math.max(_.width,_.height))+1:A.mipmaps!==void 0&&A.mipmaps.length>0?A.mipmaps.length:A.isCompressedTexture&&Array.isArray(A.image)?_.mipmaps.length:1}function C(A){const _=A.target;_.removeEventListener("dispose",C),T(_),_.isVideoTexture&&u.delete(_),_.isHTMLTexture&&h.delete(_)}function x(A){const _=A.target;_.removeEventListener("dispose",x),D(_)}function T(A){const _=n.get(A);if(_.__webglInit===void 0)return;const z=A.source,$=f.get(z);if($){const J=$[_.__cacheKey];J.usedTimes--,J.usedTimes===0&&L(A),Object.keys($).length===0&&f.delete(z)}n.remove(A)}function L(A){const _=n.get(A);s.deleteTexture(_.__webglTexture);const z=A.source,$=f.get(z);delete $[_.__cacheKey],o.memory.textures--}function D(A){const _=n.get(A);if(A.depthTexture&&(A.depthTexture.dispose(),n.remove(A.depthTexture)),A.isWebGLCubeRenderTarget)for(let $=0;$<6;$++){if(Array.isArray(_.__webglFramebuffer[$]))for(let J=0;J<_.__webglFramebuffer[$].length;J++)s.deleteFramebuffer(_.__webglFramebuffer[$][J]);else s.deleteFramebuffer(_.__webglFramebuffer[$]);_.__webglDepthbuffer&&s.deleteRenderbuffer(_.__webglDepthbuffer[$])}else{if(Array.isArray(_.__webglFramebuffer))for(let $=0;$<_.__webglFramebuffer.length;$++)s.deleteFramebuffer(_.__webglFramebuffer[$]);else s.deleteFramebuffer(_.__webglFramebuffer);if(_.__webglDepthbuffer&&s.deleteRenderbuffer(_.__webglDepthbuffer),_.__webglMultisampledFramebuffer&&s.deleteFramebuffer(_.__webglMultisampledFramebuffer),_.__webglColorRenderbuffer)for(let $=0;$<_.__webglColorRenderbuffer.length;$++)_.__webglColorRenderbuffer[$]&&s.deleteRenderbuffer(_.__webglColorRenderbuffer[$]);_.__webglDepthRenderbuffer&&s.deleteRenderbuffer(_.__webglDepthRenderbuffer)}const z=A.textures;for(let $=0,J=z.length;$=i.maxTextures&&Ut("WebGLTextures: Trying to use "+A+" texture units while this GPU supports only "+i.maxTextures),U+=1,A}function B(A){const _=[];return _.push(A.wrapS),_.push(A.wrapT),_.push(A.wrapR||0),_.push(A.magFilter),_.push(A.minFilter),_.push(A.anisotropy),_.push(A.internalFormat),_.push(A.format),_.push(A.type),_.push(A.generateMipmaps),_.push(A.premultiplyAlpha),_.push(A.flipY),_.push(A.unpackAlignment),_.push(A.colorSpace),_.join()}function X(A,_){const z=n.get(A);if(A.isVideoTexture&&O(A),A.isRenderTargetTexture===!1&&A.isExternalTexture!==!0&&A.version>0&&z.__version!==A.version){const $=A.image;if($===null)Ut("WebGLRenderer: Texture marked for update but no image data found.");else if($.complete===!1)Ut("WebGLRenderer: Texture marked for update but image is incomplete");else{st(z,A,_);return}}else A.isExternalTexture&&(z.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(s.TEXTURE_2D,z.__webglTexture,s.TEXTURE0+_)}function nt(A,_){const z=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&z.__version!==A.version){st(z,A,_);return}else A.isExternalTexture&&(z.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(s.TEXTURE_2D_ARRAY,z.__webglTexture,s.TEXTURE0+_)}function ot(A,_){const z=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&z.__version!==A.version){st(z,A,_);return}e.bindTexture(s.TEXTURE_3D,z.__webglTexture,s.TEXTURE0+_)}function at(A,_){const z=n.get(A);if(A.isCubeDepthTexture!==!0&&A.version>0&&z.__version!==A.version){ut(z,A,_);return}e.bindTexture(s.TEXTURE_CUBE_MAP,z.__webglTexture,s.TEXTURE0+_)}const vt={[Rr]:s.REPEAT,[Vn]:s.CLAMP_TO_EDGE,[ga]:s.MIRRORED_REPEAT},Gt={[Fe]:s.NEAREST,[cp]:s.NEAREST_MIPMAP_NEAREST,[Pr]:s.NEAREST_MIPMAP_LINEAR,[Oe]:s.LINEAR,[va]:s.LINEAR_MIPMAP_NEAREST,[Ui]:s.LINEAR_MIPMAP_LINEAR},me={[dp]:s.NEVER,[vp]:s.ALWAYS,[fp]:s.LESS,[nl]:s.LEQUAL,[pp]:s.EQUAL,[il]:s.GEQUAL,[mp]:s.GREATER,[gp]:s.NOTEQUAL};function ee(A,_){if(_.type===mn&&t.has("OES_texture_float_linear")===!1&&(_.magFilter===Oe||_.magFilter===va||_.magFilter===Pr||_.magFilter===Ui||_.minFilter===Oe||_.minFilter===va||_.minFilter===Pr||_.minFilter===Ui)&&Ut("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),s.texParameteri(A,s.TEXTURE_WRAP_S,vt[_.wrapS]),s.texParameteri(A,s.TEXTURE_WRAP_T,vt[_.wrapT]),(A===s.TEXTURE_3D||A===s.TEXTURE_2D_ARRAY)&&s.texParameteri(A,s.TEXTURE_WRAP_R,vt[_.wrapR]),s.texParameteri(A,s.TEXTURE_MAG_FILTER,Gt[_.magFilter]),s.texParameteri(A,s.TEXTURE_MIN_FILTER,Gt[_.minFilter]),_.compareFunction&&(s.texParameteri(A,s.TEXTURE_COMPARE_MODE,s.COMPARE_REF_TO_TEXTURE),s.texParameteri(A,s.TEXTURE_COMPARE_FUNC,me[_.compareFunction])),t.has("EXT_texture_filter_anisotropic")===!0){if(_.magFilter===Fe||_.minFilter!==Pr&&_.minFilter!==Ui||_.type===mn&&t.has("OES_texture_float_linear")===!1)return;if(_.anisotropy>1||n.get(_).__currentAnisotropy){const z=t.get("EXT_texture_filter_anisotropic");s.texParameterf(A,z.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(_.anisotropy,i.getMaxAnisotropy())),n.get(_).__currentAnisotropy=_.anisotropy}}}function Q(A,_){let z=!1;A.__webglInit===void 0&&(A.__webglInit=!0,_.addEventListener("dispose",C));const $=_.source;let J=f.get($);J===void 0&&(J={},f.set($,J));const ct=B(_);if(ct!==A.__cacheKey){J[ct]===void 0&&(J[ct]={texture:s.createTexture(),usedTimes:0},o.memory.textures++,z=!0),J[ct].usedTimes++;const dt=J[A.__cacheKey];dt!==void 0&&(J[A.__cacheKey].usedTimes--,dt.usedTimes===0&&L(_)),A.__cacheKey=ct,A.__webglTexture=J[ct].texture}return z}function I(A,_,z){return Math.floor(Math.floor(A/z)/_)}function q(A,_,z,$){const ct=A.updateRanges;if(ct.length===0)e.texSubImage2D(s.TEXTURE_2D,0,0,0,_.width,_.height,z,$,_.data);else{ct.sort((Ct,_t)=>Ct.start-_t.start);let dt=0;for(let Ct=1;Ct0){Ft&&$t&&e.texStorage2D(s.TEXTURE_2D,ht,_t,Nt[0].width,Nt[0].height);for(let tt=0,gt=Nt.length;tt0){const bt=su(mt.width,mt.height,_.format,_.type);for(const it of _.layerUpdates){const At=mt.data.subarray(it*bt/mt.data.BYTES_PER_ELEMENT,(it+1)*bt/mt.data.BYTES_PER_ELEMENT);e.compressedTexSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,it,mt.width,mt.height,1,pt,At)}_.clearLayerUpdates()}else e.compressedTexSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,0,mt.width,mt.height,et.depth,pt,mt.data)}else e.compressedTexImage3D(s.TEXTURE_2D_ARRAY,tt,_t,mt.width,mt.height,et.depth,0,mt.data,0,0);else Ut("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else Ft?F&&e.texSubImage3D(s.TEXTURE_2D_ARRAY,tt,0,0,0,mt.width,mt.height,et.depth,pt,Ct,mt.data):e.texImage3D(s.TEXTURE_2D_ARRAY,tt,_t,mt.width,mt.height,et.depth,0,pt,Ct,mt.data)}else{Ft&&$t&&e.texStorage2D(s.TEXTURE_2D,ht,_t,Nt[0].width,Nt[0].height);for(let tt=0,gt=Nt.length;tt0){const tt=su(et.width,et.height,_.format,_.type);for(const gt of _.layerUpdates){const bt=et.data.subarray(gt*tt/et.data.BYTES_PER_ELEMENT,(gt+1)*tt/et.data.BYTES_PER_ELEMENT);e.texSubImage3D(s.TEXTURE_2D_ARRAY,0,0,0,gt,et.width,et.height,1,pt,Ct,bt)}_.clearLayerUpdates()}else e.texSubImage3D(s.TEXTURE_2D_ARRAY,0,0,0,0,et.width,et.height,et.depth,pt,Ct,et.data)}else e.texImage3D(s.TEXTURE_2D_ARRAY,0,_t,et.width,et.height,et.depth,0,pt,Ct,et.data);else if(_.isData3DTexture)Ft?($t&&e.texStorage3D(s.TEXTURE_3D,ht,_t,et.width,et.height,et.depth),F&&e.texSubImage3D(s.TEXTURE_3D,0,0,0,0,et.width,et.height,et.depth,pt,Ct,et.data)):e.texImage3D(s.TEXTURE_3D,0,_t,et.width,et.height,et.depth,0,pt,Ct,et.data);else if(_.isFramebufferTexture){if($t)if(Ft)e.texStorage2D(s.TEXTURE_2D,ht,_t,et.width,et.height);else{let tt=et.width,gt=et.height;for(let bt=0;bt>=1,gt>>=1}}else if(_.isHTMLTexture){if("texElementImage2D"in s){const tt=s.canvas;if(tt.hasAttribute("layoutsubtree")||tt.setAttribute("layoutsubtree","true"),et.parentNode!==tt){tt.appendChild(et),h.add(_),tt.onpaint=gt=>{const bt=gt.changedElements;for(const it of h)bt.includes(it.image)&&(it.needsUpdate=!0)},tt.requestPaint();return}if(s.texElementImage2D.length===3)s.texElementImage2D(s.TEXTURE_2D,s.RGBA8,et);else{const bt=s.RGBA,it=s.RGBA,At=s.UNSIGNED_BYTE;s.texElementImage2D(s.TEXTURE_2D,0,bt,it,At,et)}s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE)}}else if(Nt.length>0){if(Ft&&$t){const tt=oe(Nt[0]);e.texStorage2D(s.TEXTURE_2D,ht,_t,tt.width,tt.height)}for(let tt=0,gt=Nt.length;tt0&>++;const it=oe(_t[0]);e.texStorage2D(s.TEXTURE_CUBE_MAP,gt,$t,it.width,it.height)}for(let it=0;it<6;it++)if(Ct){F?tt&&e.texSubImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+it,0,0,0,_t[it].width,_t[it].height,Nt,Ft,_t[it].data):e.texImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+it,0,$t,_t[it].width,_t[it].height,0,Nt,Ft,_t[it].data);for(let At=0;At>ct),mt=Math.max(1,_.height>>ct);J===s.TEXTURE_3D||J===s.TEXTURE_2D_ARRAY?e.texImage3D(J,ct,et,_t,mt,_.depth,0,dt,j,null):e.texImage2D(J,ct,et,_t,mt,0,dt,j,null)}e.bindFramebuffer(s.FRAMEBUFFER,A),Re(_)?a.framebufferTexture2DMultisampleEXT(s.FRAMEBUFFER,$,J,Ct.__webglTexture,0,xe(_)):(J===s.TEXTURE_2D||J>=s.TEXTURE_CUBE_MAP_POSITIVE_X&&J<=s.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&s.framebufferTexture2D(s.FRAMEBUFFER,$,J,Ct.__webglTexture,ct),e.bindFramebuffer(s.FRAMEBUFFER,null)}function Ot(A,_,z){if(s.bindRenderbuffer(s.RENDERBUFFER,A),_.depthBuffer){const $=_.depthTexture,J=$&&$.isDepthTexture?$.type:null,ct=M(_.stencilBuffer,J),dt=_.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT;Re(_)?a.renderbufferStorageMultisampleEXT(s.RENDERBUFFER,xe(_),ct,_.width,_.height):z?s.renderbufferStorageMultisample(s.RENDERBUFFER,xe(_),ct,_.width,_.height):s.renderbufferStorage(s.RENDERBUFFER,ct,_.width,_.height),s.framebufferRenderbuffer(s.FRAMEBUFFER,dt,s.RENDERBUFFER,A)}else{const $=_.textures;for(let J=0;J<$.length;J++){const ct=$[J],dt=r.convert(ct.format,ct.colorSpace),j=r.convert(ct.type),et=y(ct.internalFormat,dt,j,ct.normalized,ct.colorSpace);Re(_)?a.renderbufferStorageMultisampleEXT(s.RENDERBUFFER,xe(_),et,_.width,_.height):z?s.renderbufferStorageMultisample(s.RENDERBUFFER,xe(_),et,_.width,_.height):s.renderbufferStorage(s.RENDERBUFFER,et,_.width,_.height)}}s.bindRenderbuffer(s.RENDERBUFFER,null)}function Dt(A,_,z){const $=_.isWebGLCubeRenderTarget===!0;if(e.bindFramebuffer(s.FRAMEBUFFER,A),!(_.depthTexture&&_.depthTexture.isDepthTexture))throw new Error("THREE.WebGLTextures: renderTarget.depthTexture must be an instance of THREE.DepthTexture.");const J=n.get(_.depthTexture);if(J.__renderTarget=_,(!J.__webglTexture||_.depthTexture.image.width!==_.width||_.depthTexture.image.height!==_.height)&&(_.depthTexture.image.width=_.width,_.depthTexture.image.height=_.height,_.depthTexture.needsUpdate=!0),$){if(J.__webglInit===void 0&&(J.__webglInit=!0,_.depthTexture.addEventListener("dispose",C)),J.__webglTexture===void 0){J.__webglTexture=s.createTexture(),e.bindTexture(s.TEXTURE_CUBE_MAP,J.__webglTexture),ee(s.TEXTURE_CUBE_MAP,_.depthTexture);const pt=r.convert(_.depthTexture.format),Ct=r.convert(_.depthTexture.type);let _t;_.depthTexture.format===Wn?_t=s.DEPTH_COMPONENT24:_.depthTexture.format===Fi&&(_t=s.DEPTH24_STENCIL8);for(let mt=0;mt<6;mt++)s.texImage2D(s.TEXTURE_CUBE_MAP_POSITIVE_X+mt,0,_t,_.width,_.height,0,pt,Ct,null)}}else X(_.depthTexture,0);const ct=J.__webglTexture,dt=xe(_),j=$?s.TEXTURE_CUBE_MAP_POSITIVE_X+z:s.TEXTURE_2D,et=_.depthTexture.format===Fi?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT;if(_.depthTexture.format===Wn)Re(_)?a.framebufferTexture2DMultisampleEXT(s.FRAMEBUFFER,et,j,ct,0,dt):s.framebufferTexture2D(s.FRAMEBUFFER,et,j,ct,0);else if(_.depthTexture.format===Fi)Re(_)?a.framebufferTexture2DMultisampleEXT(s.FRAMEBUFFER,et,j,ct,0,dt):s.framebufferTexture2D(s.FRAMEBUFFER,et,j,ct,0);else throw new Error("THREE.WebGLTextures: Unknown depthTexture format.")}function Vt(A){const _=n.get(A),z=A.isWebGLCubeRenderTarget===!0;if(_.__boundDepthTexture!==A.depthTexture){const $=A.depthTexture;if(_.__depthDisposeCallback&&_.__depthDisposeCallback(),$){const J=()=>{delete _.__boundDepthTexture,delete _.__depthDisposeCallback,$.removeEventListener("dispose",J)};$.addEventListener("dispose",J),_.__depthDisposeCallback=J}_.__boundDepthTexture=$}if(A.depthTexture&&!_.__autoAllocateDepthBuffer)if(z)for(let $=0;$<6;$++)Dt(_.__webglFramebuffer[$],A,$);else{const $=A.texture.mipmaps;$&&$.length>0?Dt(_.__webglFramebuffer[0],A,0):Dt(_.__webglFramebuffer,A,0)}else if(z){_.__webglDepthbuffer=[];for(let $=0;$<6;$++)if(e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer[$]),_.__webglDepthbuffer[$]===void 0)_.__webglDepthbuffer[$]=s.createRenderbuffer(),Ot(_.__webglDepthbuffer[$],A,!1);else{const J=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,ct=_.__webglDepthbuffer[$];s.bindRenderbuffer(s.RENDERBUFFER,ct),s.framebufferRenderbuffer(s.FRAMEBUFFER,J,s.RENDERBUFFER,ct)}}else{const $=A.texture.mipmaps;if($&&$.length>0?e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer[0]):e.bindFramebuffer(s.FRAMEBUFFER,_.__webglFramebuffer),_.__webglDepthbuffer===void 0)_.__webglDepthbuffer=s.createRenderbuffer(),Ot(_.__webglDepthbuffer,A,!1);else{const J=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,ct=_.__webglDepthbuffer;s.bindRenderbuffer(s.RENDERBUFFER,ct),s.framebufferRenderbuffer(s.FRAMEBUFFER,J,s.RENDERBUFFER,ct)}}e.bindFramebuffer(s.FRAMEBUFFER,null)}function jt(A,_,z){const $=n.get(A);_!==void 0&<($.__webglFramebuffer,A,A.texture,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,0),z!==void 0&&Vt(A)}function Qt(A){const _=A.texture,z=n.get(A),$=n.get(_);A.addEventListener("dispose",x);const J=A.textures,ct=A.isWebGLCubeRenderTarget===!0,dt=J.length>1;if(dt||($.__webglTexture===void 0&&($.__webglTexture=s.createTexture()),$.__version=_.version,o.memory.textures++),ct){z.__webglFramebuffer=[];for(let j=0;j<6;j++)if(_.mipmaps&&_.mipmaps.length>0){z.__webglFramebuffer[j]=[];for(let et=0;et<_.mipmaps.length;et++)z.__webglFramebuffer[j][et]=s.createFramebuffer()}else z.__webglFramebuffer[j]=s.createFramebuffer()}else{if(_.mipmaps&&_.mipmaps.length>0){z.__webglFramebuffer=[];for(let j=0;j<_.mipmaps.length;j++)z.__webglFramebuffer[j]=s.createFramebuffer()}else z.__webglFramebuffer=s.createFramebuffer();if(dt)for(let j=0,et=J.length;j0&&Re(A)===!1){z.__webglMultisampledFramebuffer=s.createFramebuffer(),z.__webglColorRenderbuffer=[],e.bindFramebuffer(s.FRAMEBUFFER,z.__webglMultisampledFramebuffer);for(let j=0;j0)for(let et=0;et<_.mipmaps.length;et++)lt(z.__webglFramebuffer[j][et],A,_,s.COLOR_ATTACHMENT0,s.TEXTURE_CUBE_MAP_POSITIVE_X+j,et);else lt(z.__webglFramebuffer[j],A,_,s.COLOR_ATTACHMENT0,s.TEXTURE_CUBE_MAP_POSITIVE_X+j,0);g(_)&&S(s.TEXTURE_CUBE_MAP),e.unbindTexture()}else if(dt){for(let j=0,et=J.length;j0)for(let et=0;et<_.mipmaps.length;et++)lt(z.__webglFramebuffer[et],A,_,s.COLOR_ATTACHMENT0,j,et);else lt(z.__webglFramebuffer,A,_,s.COLOR_ATTACHMENT0,j,0);g(_)&&S(j),e.unbindTexture()}A.depthBuffer&&Vt(A)}function ne(A){const _=A.textures;for(let z=0,$=_.length;z<$;z++){const J=_[z];if(g(J)){const ct=E(A),dt=n.get(J).__webglTexture;e.bindTexture(ct,dt),S(ct),e.unbindTexture()}}}const ce=[],he=[];function Ce(A){if(A.samples>0){if(Re(A)===!1){const _=A.textures,z=A.width,$=A.height;let J=s.COLOR_BUFFER_BIT;const ct=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT,dt=n.get(A),j=_.length>1;if(j)for(let pt=0;pt<_.length;pt++)e.bindFramebuffer(s.FRAMEBUFFER,dt.__webglMultisampledFramebuffer),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+pt,s.RENDERBUFFER,null),e.bindFramebuffer(s.FRAMEBUFFER,dt.__webglFramebuffer),s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0+pt,s.TEXTURE_2D,null,0);e.bindFramebuffer(s.READ_FRAMEBUFFER,dt.__webglMultisampledFramebuffer);const et=A.texture.mipmaps;et&&et.length>0?e.bindFramebuffer(s.DRAW_FRAMEBUFFER,dt.__webglFramebuffer[0]):e.bindFramebuffer(s.DRAW_FRAMEBUFFER,dt.__webglFramebuffer);for(let pt=0;pt<_.length;pt++){if(A.resolveDepthBuffer&&(A.depthBuffer&&(J|=s.DEPTH_BUFFER_BIT),A.stencilBuffer&&A.resolveStencilBuffer&&(J|=s.STENCIL_BUFFER_BIT)),j){s.framebufferRenderbuffer(s.READ_FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.RENDERBUFFER,dt.__webglColorRenderbuffer[pt]);const Ct=n.get(_[pt]).__webglTexture;s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,Ct,0)}s.blitFramebuffer(0,0,z,$,0,0,z,$,J,s.NEAREST),l===!0&&(ce.length=0,he.length=0,ce.push(s.COLOR_ATTACHMENT0+pt),A.depthBuffer&&A.resolveDepthBuffer===!1&&(ce.push(ct),he.push(ct),s.invalidateFramebuffer(s.DRAW_FRAMEBUFFER,he)),s.invalidateFramebuffer(s.READ_FRAMEBUFFER,ce))}if(e.bindFramebuffer(s.READ_FRAMEBUFFER,null),e.bindFramebuffer(s.DRAW_FRAMEBUFFER,null),j)for(let pt=0;pt<_.length;pt++){e.bindFramebuffer(s.FRAMEBUFFER,dt.__webglMultisampledFramebuffer),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0+pt,s.RENDERBUFFER,dt.__webglColorRenderbuffer[pt]);const Ct=n.get(_[pt]).__webglTexture;e.bindFramebuffer(s.FRAMEBUFFER,dt.__webglFramebuffer),s.framebufferTexture2D(s.DRAW_FRAMEBUFFER,s.COLOR_ATTACHMENT0+pt,s.TEXTURE_2D,Ct,0)}e.bindFramebuffer(s.DRAW_FRAMEBUFFER,dt.__webglMultisampledFramebuffer)}else if(A.depthBuffer&&A.resolveDepthBuffer===!1&&l){const _=A.stencilBuffer?s.DEPTH_STENCIL_ATTACHMENT:s.DEPTH_ATTACHMENT;s.invalidateFramebuffer(s.DRAW_FRAMEBUFFER,[_])}}}function xe(A){return Math.min(i.maxSamples,A.samples)}function Re(A){const _=n.get(A);return A.samples>0&&t.has("WEBGL_multisampled_render_to_texture")===!0&&_.__useRenderToTexture!==!1}function O(A){const _=o.render.frame;u.get(A)!==_&&(u.set(A,_),A.update())}function Je(A,_){const z=A.colorSpace,$=A.format,J=A.type;return A.isCompressedTexture===!0||A.isVideoTexture===!0||z!==Ys&&z!==li&&(Jt.getTransfer(z)===se?($!==gn||J!==je)&&Ut("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):te("WebGLTextures: Unsupported texture color space:",z)),_}function oe(A){return typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement?(c.width=A.naturalWidth||A.width,c.height=A.naturalHeight||A.height):typeof VideoFrame<"u"&&A instanceof VideoFrame?(c.width=A.displayWidth,c.height=A.displayHeight):(c.width=A.width,c.height=A.height),c}this.allocateTextureUnit=W,this.resetTextureUnits=K,this.getTextureUnits=Z,this.setTextureUnits=N,this.setTexture2D=X,this.setTexture2DArray=nt,this.setTexture3D=ot,this.setTextureCube=at,this.rebindTextures=jt,this.setupRenderTarget=Qt,this.updateRenderTargetMipmap=ne,this.updateMultisampleRenderTarget=Ce,this.setupDepthRenderbuffer=Vt,this.setupFrameBufferTexture=lt,this.useMultisampledRTT=Re,this.isReversedDepthBuffer=function(){return e.buffers.depth.getReversed()}}function Xx(s,t){function e(n,i=li){let r;const o=Jt.getTransfer(i);if(n===je)return s.UNSIGNED_BYTE;if(n===_a)return s.UNSIGNED_SHORT_4_4_4_4;if(n===ya)return s.UNSIGNED_SHORT_5_5_5_1;if(n===nh)return s.UNSIGNED_INT_5_9_9_9_REV;if(n===ih)return s.UNSIGNED_INT_10F_11F_11F_REV;if(n===th)return s.BYTE;if(n===eh)return s.SHORT;if(n===Xs)return s.UNSIGNED_SHORT;if(n===xa)return s.INT;if(n===Rn)return s.UNSIGNED_INT;if(n===mn)return s.FLOAT;if(n===Qe)return s.HALF_FLOAT;if(n===sh)return s.ALPHA;if(n===rh)return s.RGB;if(n===gn)return s.RGBA;if(n===Wn)return s.DEPTH_COMPONENT;if(n===Fi)return s.DEPTH_STENCIL;if(n===Ma)return s.RED;if(n===ba)return s.RED_INTEGER;if(n===Oi)return s.RG;if(n===Sa)return s.RG_INTEGER;if(n===wa)return s.RGBA_INTEGER;if(n===Lr||n===Dr||n===Ir||n===Nr)if(o===se)if(r=t.get("WEBGL_compressed_texture_s3tc_srgb"),r!==null){if(n===Lr)return r.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(n===Dr)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(n===Ir)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(n===Nr)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(r=t.get("WEBGL_compressed_texture_s3tc"),r!==null){if(n===Lr)return r.COMPRESSED_RGB_S3TC_DXT1_EXT;if(n===Dr)return r.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(n===Ir)return r.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(n===Nr)return r.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(n===Ea||n===Ta||n===Aa||n===Ca)if(r=t.get("WEBGL_compressed_texture_pvrtc"),r!==null){if(n===Ea)return r.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(n===Ta)return r.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(n===Aa)return r.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(n===Ca)return r.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(n===Ra||n===Pa||n===La||n===Da||n===Ia||n===Ur||n===Na)if(r=t.get("WEBGL_compressed_texture_etc"),r!==null){if(n===Ra||n===Pa)return o===se?r.COMPRESSED_SRGB8_ETC2:r.COMPRESSED_RGB8_ETC2;if(n===La)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:r.COMPRESSED_RGBA8_ETC2_EAC;if(n===Da)return r.COMPRESSED_R11_EAC;if(n===Ia)return r.COMPRESSED_SIGNED_R11_EAC;if(n===Ur)return r.COMPRESSED_RG11_EAC;if(n===Na)return r.COMPRESSED_SIGNED_RG11_EAC}else return null;if(n===Ua||n===Fa||n===Oa||n===ka||n===Ba||n===za||n===Ha||n===Ga||n===Va||n===Wa||n===$a||n===Xa||n===qa||n===Ya)if(r=t.get("WEBGL_compressed_texture_astc"),r!==null){if(n===Ua)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:r.COMPRESSED_RGBA_ASTC_4x4_KHR;if(n===Fa)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:r.COMPRESSED_RGBA_ASTC_5x4_KHR;if(n===Oa)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:r.COMPRESSED_RGBA_ASTC_5x5_KHR;if(n===ka)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:r.COMPRESSED_RGBA_ASTC_6x5_KHR;if(n===Ba)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:r.COMPRESSED_RGBA_ASTC_6x6_KHR;if(n===za)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:r.COMPRESSED_RGBA_ASTC_8x5_KHR;if(n===Ha)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:r.COMPRESSED_RGBA_ASTC_8x6_KHR;if(n===Ga)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:r.COMPRESSED_RGBA_ASTC_8x8_KHR;if(n===Va)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:r.COMPRESSED_RGBA_ASTC_10x5_KHR;if(n===Wa)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:r.COMPRESSED_RGBA_ASTC_10x6_KHR;if(n===$a)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:r.COMPRESSED_RGBA_ASTC_10x8_KHR;if(n===Xa)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:r.COMPRESSED_RGBA_ASTC_10x10_KHR;if(n===qa)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:r.COMPRESSED_RGBA_ASTC_12x10_KHR;if(n===Ya)return o===se?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:r.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(n===Ka||n===Za||n===Ja)if(r=t.get("EXT_texture_compression_bptc"),r!==null){if(n===Ka)return o===se?r.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:r.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(n===Za)return r.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(n===Ja)return r.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(n===ja||n===Qa||n===Fr||n===tl)if(r=t.get("EXT_texture_compression_rgtc"),r!==null){if(n===ja)return r.COMPRESSED_RED_RGTC1_EXT;if(n===Qa)return r.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(n===Fr)return r.COMPRESSED_RED_GREEN_RGTC2_EXT;if(n===tl)return r.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return n===qs?s.UNSIGNED_INT_24_8:s[n]!==void 0?s[n]:null}return{convert:e}}const qx=` void main() { gl_Position = vec4( position, 1.0 ); -}`,T_=` +}`,Yx=` uniform sampler2DArray depthColor; uniform float depthWidth; uniform float depthHeight; @@ -4113,7 +4113,7 @@ void main() { } -}`;class A_{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(t,e){if(this.texture===null){const n=new Kc(t.texture);(t.depthNear!==e.depthNear||t.depthFar!==e.depthFar)&&(this.depthNear=t.depthNear,this.depthFar=t.depthFar),this.texture=n}}getMesh(t){if(this.texture!==null&&this.mesh===null){const e=t.cameras[0].viewport,n=new Ce({vertexShader:E_,fragmentShader:T_,uniforms:{depthColor:{value:this.texture},depthWidth:{value:e.z},depthHeight:{value:e.w}}});this.mesh=new Bt(new We(20,20),n)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class C_ extends ei{constructor(t,e){super();const n=this;let i=null,r=1,a=null,o="local-floor",l=1,c=null,u=null,h=null,d=null,f=null,p=null;const v=typeof XRWebGLBinding<"u",m=new A_,g={},w=e.getContextAttributes();let E=null,M=null;const S=[],b=[],R=new ht;let x=null;const T=new qe;T.viewport=new me;const P=new qe;P.viewport=new me;const L=[T,P],F=new fp;let q=null,Y=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(j){let D=S[j];return D===void 0&&(D=new Uo,S[j]=D),D.getTargetRaySpace()},this.getControllerGrip=function(j){let D=S[j];return D===void 0&&(D=new Uo,S[j]=D),D.getGripSpace()},this.getHand=function(j){let D=S[j];return D===void 0&&(D=new Uo,S[j]=D),D.getHandSpace()};function B(j){const D=b.indexOf(j.inputSource);if(D===-1)return;const W=S[D];W!==void 0&&(W.update(j.inputSource,j.frame,c||a),W.dispatchEvent({type:j.type,data:j.inputSource}))}function K(){i.removeEventListener("select",B),i.removeEventListener("selectstart",B),i.removeEventListener("selectend",B),i.removeEventListener("squeeze",B),i.removeEventListener("squeezestart",B),i.removeEventListener("squeezeend",B),i.removeEventListener("end",K),i.removeEventListener("inputsourceschange",X);for(let j=0;j=0&&(b[st]=null,S[st].disconnect(W))}for(let D=0;D=b.length){b.push(W),st=rt;break}else if(b[rt]===null){b[rt]=W,st=rt;break}if(st===-1)break}const lt=S[st];lt&<.connect(W)}}const Q=new C,it=new C;function pt(j,D,W){Q.setFromMatrixPosition(D.matrixWorld),it.setFromMatrixPosition(W.matrixWorld);const st=Q.distanceTo(it),lt=D.projectionMatrix.elements,rt=W.projectionMatrix.elements,Ft=lt[14]/(lt[10]-1),Lt=lt[14]/(lt[10]+1),Ht=(lt[9]+1)/lt[5],Zt=(lt[9]-1)/lt[5],Jt=(lt[8]-1)/lt[0],te=(rt[8]+1)/rt[0],le=Ft*Jt,ce=Ft*te,Te=st/(-Jt+te),ge=Te*-Jt;if(D.matrixWorld.decompose(j.position,j.quaternion,j.scale),j.translateX(ge),j.translateZ(Te),j.matrixWorld.compose(j.position,j.quaternion,j.scale),j.matrixWorldInverse.copy(j.matrixWorld).invert(),lt[10]===-1)j.projectionMatrix.copy(D.projectionMatrix),j.projectionMatrixInverse.copy(D.projectionMatrixInverse);else{const Ae=Ft+Te,U=Lt+Te,Ke=le-ge,re=ce+(st-ge),A=Ht*Lt/U*Ae,_=Zt*Lt/U*Ae;j.projectionMatrix.makePerspective(Ke,re,A,_,Ae,U),j.projectionMatrixInverse.copy(j.projectionMatrix).invert()}}function _t(j,D){D===null?j.matrixWorld.copy(j.matrix):j.matrixWorld.multiplyMatrices(D.matrixWorld,j.matrix),j.matrixWorldInverse.copy(j.matrixWorld).invert()}this.updateCamera=function(j){if(i===null)return;let D=j.near,W=j.far;m.texture!==null&&(m.depthNear>0&&(D=m.depthNear),m.depthFar>0&&(W=m.depthFar)),F.near=P.near=T.near=D,F.far=P.far=T.far=W,(q!==F.near||Y!==F.far)&&(i.updateRenderState({depthNear:F.near,depthFar:F.far}),q=F.near,Y=F.far),F.layers.mask=j.layers.mask|6,T.layers.mask=F.layers.mask&-5,P.layers.mask=F.layers.mask&-3;const st=j.parent,lt=F.cameras;_t(F,st);for(let rt=0;rt0&&(m.alphaTest.value=g.alphaTest);const w=t.get(g),E=w.envMap,M=w.envMapRotation;E&&(m.envMap.value=E,m.envMapRotation.value.setFromMatrix4(R_.makeRotationFromEuler(M)).transpose(),E.isCubeTexture&&E.isRenderTargetTexture===!1&&m.envMapRotation.value.premultiply(Wh),m.reflectivity.value=g.reflectivity,m.ior.value=g.ior,m.refractionRatio.value=g.refractionRatio),g.lightMap&&(m.lightMap.value=g.lightMap,m.lightMapIntensity.value=g.lightMapIntensity,e(g.lightMap,m.lightMapTransform)),g.aoMap&&(m.aoMap.value=g.aoMap,m.aoMapIntensity.value=g.aoMapIntensity,e(g.aoMap,m.aoMapTransform))}function a(m,g){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,g.map&&(m.map.value=g.map,e(g.map,m.mapTransform))}function o(m,g){m.dashSize.value=g.dashSize,m.totalSize.value=g.dashSize+g.gapSize,m.scale.value=g.scale}function l(m,g,w,E){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,m.size.value=g.size*w,m.scale.value=E*.5,g.map&&(m.map.value=g.map,e(g.map,m.uvTransform)),g.alphaMap&&(m.alphaMap.value=g.alphaMap,e(g.alphaMap,m.alphaMapTransform)),g.alphaTest>0&&(m.alphaTest.value=g.alphaTest)}function c(m,g){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,m.rotation.value=g.rotation,g.map&&(m.map.value=g.map,e(g.map,m.mapTransform)),g.alphaMap&&(m.alphaMap.value=g.alphaMap,e(g.alphaMap,m.alphaMapTransform)),g.alphaTest>0&&(m.alphaTest.value=g.alphaTest)}function u(m,g){m.specular.value.copy(g.specular),m.shininess.value=Math.max(g.shininess,1e-4)}function h(m,g){g.gradientMap&&(m.gradientMap.value=g.gradientMap)}function d(m,g){m.metalness.value=g.metalness,g.metalnessMap&&(m.metalnessMap.value=g.metalnessMap,e(g.metalnessMap,m.metalnessMapTransform)),m.roughness.value=g.roughness,g.roughnessMap&&(m.roughnessMap.value=g.roughnessMap,e(g.roughnessMap,m.roughnessMapTransform)),g.envMap&&(m.envMapIntensity.value=g.envMapIntensity)}function f(m,g,w){m.ior.value=g.ior,g.sheen>0&&(m.sheenColor.value.copy(g.sheenColor).multiplyScalar(g.sheen),m.sheenRoughness.value=g.sheenRoughness,g.sheenColorMap&&(m.sheenColorMap.value=g.sheenColorMap,e(g.sheenColorMap,m.sheenColorMapTransform)),g.sheenRoughnessMap&&(m.sheenRoughnessMap.value=g.sheenRoughnessMap,e(g.sheenRoughnessMap,m.sheenRoughnessMapTransform))),g.clearcoat>0&&(m.clearcoat.value=g.clearcoat,m.clearcoatRoughness.value=g.clearcoatRoughness,g.clearcoatMap&&(m.clearcoatMap.value=g.clearcoatMap,e(g.clearcoatMap,m.clearcoatMapTransform)),g.clearcoatRoughnessMap&&(m.clearcoatRoughnessMap.value=g.clearcoatRoughnessMap,e(g.clearcoatRoughnessMap,m.clearcoatRoughnessMapTransform)),g.clearcoatNormalMap&&(m.clearcoatNormalMap.value=g.clearcoatNormalMap,e(g.clearcoatNormalMap,m.clearcoatNormalMapTransform),m.clearcoatNormalScale.value.copy(g.clearcoatNormalScale),g.side===Ge&&m.clearcoatNormalScale.value.negate())),g.dispersion>0&&(m.dispersion.value=g.dispersion),g.iridescence>0&&(m.iridescence.value=g.iridescence,m.iridescenceIOR.value=g.iridescenceIOR,m.iridescenceThicknessMinimum.value=g.iridescenceThicknessRange[0],m.iridescenceThicknessMaximum.value=g.iridescenceThicknessRange[1],g.iridescenceMap&&(m.iridescenceMap.value=g.iridescenceMap,e(g.iridescenceMap,m.iridescenceMapTransform)),g.iridescenceThicknessMap&&(m.iridescenceThicknessMap.value=g.iridescenceThicknessMap,e(g.iridescenceThicknessMap,m.iridescenceThicknessMapTransform))),g.transmission>0&&(m.transmission.value=g.transmission,m.transmissionSamplerMap.value=w.texture,m.transmissionSamplerSize.value.set(w.width,w.height),g.transmissionMap&&(m.transmissionMap.value=g.transmissionMap,e(g.transmissionMap,m.transmissionMapTransform)),m.thickness.value=g.thickness,g.thicknessMap&&(m.thicknessMap.value=g.thicknessMap,e(g.thicknessMap,m.thicknessMapTransform)),m.attenuationDistance.value=g.attenuationDistance,m.attenuationColor.value.copy(g.attenuationColor)),g.anisotropy>0&&(m.anisotropyVector.value.set(g.anisotropy*Math.cos(g.anisotropyRotation),g.anisotropy*Math.sin(g.anisotropyRotation)),g.anisotropyMap&&(m.anisotropyMap.value=g.anisotropyMap,e(g.anisotropyMap,m.anisotropyMapTransform))),m.specularIntensity.value=g.specularIntensity,m.specularColor.value.copy(g.specularColor),g.specularColorMap&&(m.specularColorMap.value=g.specularColorMap,e(g.specularColorMap,m.specularColorMapTransform)),g.specularIntensityMap&&(m.specularIntensityMap.value=g.specularIntensityMap,e(g.specularIntensityMap,m.specularIntensityMapTransform))}function p(m,g){g.matcap&&(m.matcap.value=g.matcap)}function v(m,g){const w=t.get(g).light;m.referencePosition.value.setFromMatrixPosition(w.matrixWorld),m.nearDistance.value=w.shadow.camera.near,m.farDistance.value=w.shadow.camera.far}return{refreshFogUniforms:n,refreshMaterialUniforms:i}}function L_(s,t,e,n){let i={},r={},a=[];const o=s.getParameter(s.MAX_UNIFORM_BUFFER_BINDINGS);function l(M,S){const b=S.program;n.uniformBlockBinding(M,b)}function c(M,S){let b=i[M.id];b===void 0&&(m(M),b=u(M),i[M.id]=b,M.addEventListener("dispose",w));const R=S.program;n.updateUBOMapping(M,R);const x=t.render.frame;r[M.id]!==x&&(d(M),r[M.id]=x)}function u(M){const S=h();M.__bindingPointIndex=S;const b=s.createBuffer(),R=M.__size,x=M.usage;return s.bindBuffer(s.UNIFORM_BUFFER,b),s.bufferData(s.UNIFORM_BUFFER,R,x),s.bindBuffer(s.UNIFORM_BUFFER,null),s.bindBufferBase(s.UNIFORM_BUFFER,S,b),b}function h(){for(let M=0;M0&&(b+=R-x),M.__size=b,M.__cache={},this}function g(M){const S={boundary:0,storage:0};return typeof M=="number"||typeof M=="boolean"?(S.boundary=4,S.storage=4):M.isVector2?(S.boundary=8,S.storage=8):M.isVector3||M.isColor?(S.boundary=16,S.storage=12):M.isVector4?(S.boundary=16,S.storage=16):M.isMatrix3?(S.boundary=48,S.storage=48):M.isMatrix4?(S.boundary=64,S.storage=64):M.isTexture?Nt("WebGLRenderer: Texture samplers can not be part of an uniforms group."):ArrayBuffer.isView(M)?(S.boundary=16,S.storage=M.byteLength):Nt("WebGLRenderer: Unsupported uniform value type.",M),S}function w(M){const S=M.target;S.removeEventListener("dispose",w);const b=a.indexOf(S.__bindingPointIndex);a.splice(b,1),s.deleteBuffer(i[S.id]),delete i[S.id],delete r[S.id]}function E(){for(const M in i)s.deleteBuffer(i[M]);a=[],i={},r={}}return{bind:l,update:c,dispose:E}}const D_=new Uint16Array([12469,15057,12620,14925,13266,14620,13807,14376,14323,13990,14545,13625,14713,13328,14840,12882,14931,12528,14996,12233,15039,11829,15066,11525,15080,11295,15085,10976,15082,10705,15073,10495,13880,14564,13898,14542,13977,14430,14158,14124,14393,13732,14556,13410,14702,12996,14814,12596,14891,12291,14937,11834,14957,11489,14958,11194,14943,10803,14921,10506,14893,10278,14858,9960,14484,14039,14487,14025,14499,13941,14524,13740,14574,13468,14654,13106,14743,12678,14818,12344,14867,11893,14889,11509,14893,11180,14881,10751,14852,10428,14812,10128,14765,9754,14712,9466,14764,13480,14764,13475,14766,13440,14766,13347,14769,13070,14786,12713,14816,12387,14844,11957,14860,11549,14868,11215,14855,10751,14825,10403,14782,10044,14729,9651,14666,9352,14599,9029,14967,12835,14966,12831,14963,12804,14954,12723,14936,12564,14917,12347,14900,11958,14886,11569,14878,11247,14859,10765,14828,10401,14784,10011,14727,9600,14660,9289,14586,8893,14508,8533,15111,12234,15110,12234,15104,12216,15092,12156,15067,12010,15028,11776,14981,11500,14942,11205,14902,10752,14861,10393,14812,9991,14752,9570,14682,9252,14603,8808,14519,8445,14431,8145,15209,11449,15208,11451,15202,11451,15190,11438,15163,11384,15117,11274,15055,10979,14994,10648,14932,10343,14871,9936,14803,9532,14729,9218,14645,8742,14556,8381,14461,8020,14365,7603,15273,10603,15272,10607,15267,10619,15256,10631,15231,10614,15182,10535,15118,10389,15042,10167,14963,9787,14883,9447,14800,9115,14710,8665,14615,8318,14514,7911,14411,7507,14279,7198,15314,9675,15313,9683,15309,9712,15298,9759,15277,9797,15229,9773,15166,9668,15084,9487,14995,9274,14898,8910,14800,8539,14697,8234,14590,7790,14479,7409,14367,7067,14178,6621,15337,8619,15337,8631,15333,8677,15325,8769,15305,8871,15264,8940,15202,8909,15119,8775,15022,8565,14916,8328,14804,8009,14688,7614,14569,7287,14448,6888,14321,6483,14088,6171,15350,7402,15350,7419,15347,7480,15340,7613,15322,7804,15287,7973,15229,8057,15148,8012,15046,7846,14933,7611,14810,7357,14682,7069,14552,6656,14421,6316,14251,5948,14007,5528,15356,5942,15356,5977,15353,6119,15348,6294,15332,6551,15302,6824,15249,7044,15171,7122,15070,7050,14949,6861,14818,6611,14679,6349,14538,6067,14398,5651,14189,5311,13935,4958,15359,4123,15359,4153,15356,4296,15353,4646,15338,5160,15311,5508,15263,5829,15188,6042,15088,6094,14966,6001,14826,5796,14678,5543,14527,5287,14377,4985,14133,4586,13869,4257,15360,1563,15360,1642,15358,2076,15354,2636,15341,3350,15317,4019,15273,4429,15203,4732,15105,4911,14981,4932,14836,4818,14679,4621,14517,4386,14359,4156,14083,3795,13808,3437,15360,122,15360,137,15358,285,15355,636,15344,1274,15322,2177,15281,2765,15215,3223,15120,3451,14995,3569,14846,3567,14681,3466,14511,3305,14344,3121,14037,2800,13753,2467,15360,0,15360,1,15359,21,15355,89,15346,253,15325,479,15287,796,15225,1148,15133,1492,15008,1749,14856,1882,14685,1886,14506,1783,14324,1608,13996,1398,13702,1183]);let Ln=null;function I_(){return Ln===null&&(Ln=new zc(D_,16,16,Ci,Je),Ln.name="DFG_LUT",Ln.minFilter=Ue,Ln.magFilter=Ue,Ln.wrapS=kn,Ln.wrapT=kn,Ln.generateMipmaps=!1,Ln.needsUpdate=!0),Ln}class N_{constructor(t={}){const{canvas:e=Zd(),context:n=null,depth:i=!0,stencil:r=!1,alpha:a=!1,antialias:o=!1,premultipliedAlpha:l=!0,preserveDrawingBuffer:c=!1,powerPreference:u="default",failIfMajorPerformanceCaveat:h=!1,reversedDepthBuffer:d=!1,outputBufferType:f=Ze}=t;this.isWebGLRenderer=!0;let p;if(n!==null){if(typeof WebGLRenderingContext<"u"&&n instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");p=n.getContextAttributes().alpha}else p=a;const v=f,m=new Set([Xa,$a,Wa]),g=new Set([Ze,Tn,Cs,Rs,Ha,Ga]),w=new Uint32Array(4),E=new Int32Array(4),M=new C;let S=null,b=null;const R=[],x=[];let T=null;this.domElement=e,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=En,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const P=this;let L=!1,F=null,q=null,Y=null,B=null;this._outputColorSpace=Se;let K=0,X=0,Q=null,it=-1,pt=null;const _t=new me,bt=new me;let ee=null;const pe=new kt(0);let jt=0,j=e.width,D=e.height,W=1,st=null,lt=null;const rt=new me(0,0,j,D),Ft=new me(0,0,j,D);let Lt=!1;const Ht=new kr;let Zt=!1,Jt=!1;const te=new se,le=new C,ce=new me,Te={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let ge=!1;function Ae(){return Q===null?W:1}let U=n;function Ke(y,O){return e.getContext(y,O)}try{const y={alpha:!0,depth:i,stencil:r,antialias:o,premultipliedAlpha:l,preserveDrawingBuffer:c,powerPreference:u,failIfMajorPerformanceCaveat:h};if("setAttribute"in e&&e.setAttribute("data-engine",`three.js r${Ma}`),e.addEventListener("webglcontextlost",_e,!1),e.addEventListener("webglcontextrestored",de,!1),e.addEventListener("webglcontextcreationerror",Nn,!1),U===null){const O="webgl2";if(U=Ke(O,y),U===null)throw Ke(O)?new Error("THREE.WebGLRenderer: Error creating WebGL context with your selected attributes."):new Error("THREE.WebGLRenderer: Error creating WebGL context.")}}catch(y){throw Qt("WebGLRenderer: "+y.message),y}let re,A,_,k,V,Z,at,ct,J,et,ut,At,gt,dt,Dt,Ut,Gt,N,ot,tt,ft,Mt,nt;function Tt(){re=new Ig(U),re.init(),ft=new w_(U,re),A=new Eg(U,re,t,ft),_=new S_(U,re),A.reversedDepthBuffer&&d&&_.buffers.depth.setReversed(!0),q=U.createFramebuffer(),Y=U.createFramebuffer(),B=U.createFramebuffer(),k=new Fg(U),V=new l_,Z=new b_(U,re,_,V,A,ft,k),at=new Dg(P),ct=new Mp(U),Mt=new bg(U,ct),J=new Ng(U,ct,k,Mt),et=new kg(U,J,ct,Mt,k),N=new Og(U,A,Z),Dt=new Tg(V),ut=new o_(P,at,re,A,Mt,Dt),At=new P_(P,V),gt=new h_,dt=new g_(re),Gt=new Sg(P,at,_,et,p,l),Ut=new y_(P,et,A),nt=new L_(U,k,A,_),ot=new wg(U,re,k),tt=new Ug(U,re,k),k.programs=ut.programs,P.capabilities=A,P.extensions=re,P.properties=V,P.renderLists=gt,P.shadowMap=Ut,P.state=_,P.info=k}Tt(),v!==Ze&&(T=new zg(v,e.width,e.height,o,i,r));const wt=new C_(P,U);this.xr=wt,this.getContext=function(){return U},this.getContextAttributes=function(){return U.getContextAttributes()},this.forceContextLoss=function(){const y=re.get("WEBGL_lose_context");y&&y.loseContext()},this.forceContextRestore=function(){const y=re.get("WEBGL_lose_context");y&&y.restoreContext()},this.getPixelRatio=function(){return W},this.setPixelRatio=function(y){y!==void 0&&(W=y,this.setSize(j,D,!1))},this.getSize=function(y){return y.set(j,D)},this.setSize=function(y,O,$=!0){if(wt.isPresenting){Nt("WebGLRenderer: Can't change size while VR device is presenting.");return}j=y,D=O,e.width=Math.floor(y*W),e.height=Math.floor(O*W),$===!0&&(e.style.width=y+"px",e.style.height=O+"px"),T!==null&&T.setSize(e.width,e.height),this.setViewport(0,0,y,O)},this.getDrawingBufferSize=function(y){return y.set(j*W,D*W).floor()},this.setDrawingBufferSize=function(y,O,$){j=y,D=O,W=$,e.width=Math.floor(y*$),e.height=Math.floor(O*$),this.setViewport(0,0,y,O)},this.setEffects=function(y){if(v===Ze){Qt("WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.");return}if(y){for(let O=0;O{function vt(){if(z.forEach(function(St){V.get(St).currentProgram.isReady()&&z.delete(St)}),z.size===0){H(y);return}setTimeout(vt,10)}re.get("KHR_parallel_shader_compile")!==null?vt():setTimeout(vt,10)})};let Vl=null;function fv(y){Vl&&Vl(y)}function Lu(){Oi.stop()}function Du(){Oi.start()}const Oi=new ph;Oi.setAnimationLoop(fv),typeof self<"u"&&Oi.setContext(self),this.setAnimationLoop=function(y){Vl=y,wt.setAnimationLoop(y),y===null?Oi.stop():Oi.start()},wt.addEventListener("sessionstart",Lu),wt.addEventListener("sessionend",Du),this.render=function(y,O){if(O!==void 0&&O.isCamera!==!0){Qt("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(L===!0)return;F!==null&&F.renderStart(y,O);const $=wt.enabled===!0&&wt.isPresenting===!0,z=T!==null&&(Q===null||$)&&T.begin(P,Q);if(y.matrixWorldAutoUpdate===!0&&y.updateMatrixWorld(),O.parent===null&&O.matrixWorldAutoUpdate===!0&&O.updateMatrixWorld(),wt.enabled===!0&&wt.isPresenting===!0&&(T===null||T.isCompositing()===!1)&&(wt.cameraAutoUpdate===!0&&wt.updateCamera(O),O=wt.getCamera()),y.isScene===!0&&y.onBeforeRender(P,y,O,Q),b=dt.get(y,x.length),b.init(O),b.state.textureUnits=Z.getTextureUnits(),x.push(b),te.multiplyMatrices(O.projectionMatrix,O.matrixWorldInverse),Ht.setFromProjectionMatrix(te,An,O.reversedDepth),Jt=this.localClippingEnabled,Zt=Dt.init(this.clippingPlanes,Jt),S=gt.get(y,R.length),S.init(),R.push(S),wt.enabled===!0&&wt.isPresenting===!0){const St=P.xr.getDepthSensingMesh();St!==null&&Wl(St,O,-1/0,P.sortObjects)}Wl(y,O,0,P.sortObjects),S.finish(),P.sortObjects===!0&&S.sort(st,lt,O.reversedDepth),ge=wt.enabled===!1||wt.isPresenting===!1||wt.hasDepthSensing()===!1,ge&&Gt.addToRenderList(S,y),this.info.render.frame++,this.info.autoReset===!0&&this.info.reset(),Zt===!0&&Dt.beginShadows();const H=b.state.shadowsArray;if(Ut.render(H,y,O),Zt===!0&&Dt.endShadows(),(z&&T.hasRenderPass())===!1){const St=S.opaque,xt=S.transmissive;if(b.setupLights(),O.isArrayCamera){const Et=O.cameras;if(xt.length>0)for(let Ct=0,Vt=Et.length;Ct0&&Nu(St,xt,y,O),ge&&Gt.render(y),Iu(S,y,O)}Q!==null&&X===0&&(Z.updateMultisampleRenderTarget(Q),Z.updateRenderTargetMipmap(Q)),z&&T.end(P),y.isScene===!0&&y.onAfterRender(P,y,O),Mt.resetDefaultState(),it=-1,pt=null,x.pop(),x.length>0?(b=x[x.length-1],Z.setTextureUnits(b.state.textureUnits),Zt===!0&&Dt.setGlobalState(P.clippingPlanes,b.state.camera)):b=null,R.pop(),R.length>0?S=R[R.length-1]:S=null,F!==null&&F.renderEnd()};function Wl(y,O,$,z){if(y.visible===!1)return;if(y.layers.test(O.layers)){if(y.isGroup)$=y.renderOrder;else if(y.isLOD)y.autoUpdate===!0&&y.update(O);else if(y.isLightProbeGrid)b.pushLightProbeGrid(y);else if(y.isLight)b.pushLight(y),y.castShadow&&b.pushShadow(y);else if(y.isSprite){if(!y.frustumCulled||Ht.intersectsSprite(y)){z&&ce.setFromMatrixPosition(y.matrixWorld).applyMatrix4(te);const St=et.update(y),xt=y.material;xt.visible&&S.push(y,St,xt,$,ce.z,null)}}else if((y.isMesh||y.isLine||y.isPoints)&&(!y.frustumCulled||Ht.intersectsObject(y))){const St=et.update(y),xt=y.material;if(z&&(y.boundingSphere!==void 0?(y.boundingSphere===null&&y.computeBoundingSphere(),ce.copy(y.boundingSphere.center)):(St.boundingSphere===null&&St.computeBoundingSphere(),ce.copy(St.boundingSphere.center)),ce.applyMatrix4(y.matrixWorld).applyMatrix4(te)),Array.isArray(xt)){const Et=St.groups;for(let Ct=0,Vt=Et.length;Ct0&&ra(H,O,$),vt.length>0&&ra(vt,O,$),St.length>0&&ra(St,O,$),_.buffers.depth.setTest(!0),_.buffers.depth.setMask(!0),_.buffers.color.setMask(!0),_.setPolygonOffset(!1)}function Nu(y,O,$,z){if(($.isScene===!0?$.overrideMaterial:null)!==null)return;if(b.state.transmissionRenderTarget[z.id]===void 0){const Pt=re.has("EXT_color_buffer_half_float")||re.has("EXT_color_buffer_float");b.state.transmissionRenderTarget[z.id]=new Xe(1,1,{generateMipmaps:!0,type:Pt?Je:Ze,minFilter:Ti,samples:Math.max(4,A.samples),stencilBuffer:r,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:Kt.workingColorSpace})}const vt=b.state.transmissionRenderTarget[z.id],St=z.viewport||_t;vt.setSize(St.z*P.transmissionResolutionScale,St.w*P.transmissionResolutionScale);const xt=P.getRenderTarget(),Et=P.getActiveCubeFace(),Ct=P.getActiveMipmapLevel();P.setRenderTarget(vt),P.getClearColor(pe),jt=P.getClearAlpha(),jt<1&&P.setClearColor(16777215,.5),P.clear(),ge&&Gt.render($);const Vt=P.toneMapping;P.toneMapping=En;const qt=z.viewport;if(z.viewport!==void 0&&(z.viewport=void 0),b.setupLightsView(z),Zt===!0&&Dt.setGlobalState(P.clippingPlanes,z),ra(y,$,z),Z.updateMultisampleRenderTarget(vt),Z.updateRenderTargetMipmap(vt),re.has("WEBGL_multisampled_render_to_texture")===!1){let Pt=!1;for(let oe=0,Me=O.length;oe0,z.currentProgram=qt,z.uniformsList=null,qt}function Fu(y){if(y.uniformsList===null){const O=y.currentProgram.getUniforms();y.uniformsList=Zr.seqWithValue(O.seq,y.uniforms)}return y.uniformsList}function Ou(y,O){const $=V.get(y);$.outputColorSpace=O.outputColorSpace,$.batching=O.batching,$.batchingColor=O.batchingColor,$.instancing=O.instancing,$.instancingColor=O.instancingColor,$.instancingMorph=O.instancingMorph,$.skinning=O.skinning,$.morphTargets=O.morphTargets,$.morphNormals=O.morphNormals,$.morphColors=O.morphColors,$.morphTargetsCount=O.morphTargetsCount,$.numClippingPlanes=O.numClippingPlanes,$.numIntersection=O.numClipIntersection,$.vertexAlphas=O.vertexAlphas,$.vertexTangents=O.vertexTangents,$.toneMapping=O.toneMapping}function pv(y,O){if(y.length===0)return null;if(y.length===1)return y[0].texture!==null?y[0]:null;M.setFromMatrixPosition(O.matrixWorld);for(let $=0,z=y.length;$0),Pt=!!$.morphAttributes.position,oe=!!$.morphAttributes.normal,Me=!!$.morphAttributes.color;let xe=En;z.toneMapped&&(Q===null||Q.isXRRenderTarget===!0)&&(xe=P.toneMapping);const he=$.morphAttributes.position||$.morphAttributes.normal||$.morphAttributes.color,ze=he!==void 0?he.length:0,yt=V.get(z),en=b.state.lights;if(Zt===!0&&(Jt===!0||y!==pt)){const fe=y===pt&&z.id===it;Dt.setState(z,y,fe)}let ne=!1;z.version===yt.__version?(yt.needsLights&&yt.lightsStateVersion!==en.state.version||yt.outputColorSpace!==xt||H.isBatchedMesh&&yt.batching===!1||!H.isBatchedMesh&&yt.batching===!0||H.isBatchedMesh&&yt.batchingColor===!0&&H.colorTexture===null||H.isBatchedMesh&&yt.batchingColor===!1&&H.colorTexture!==null||H.isInstancedMesh&&yt.instancing===!1||!H.isInstancedMesh&&yt.instancing===!0||H.isSkinnedMesh&&yt.skinning===!1||!H.isSkinnedMesh&&yt.skinning===!0||H.isInstancedMesh&&yt.instancingColor===!0&&H.instanceColor===null||H.isInstancedMesh&&yt.instancingColor===!1&&H.instanceColor!==null||H.isInstancedMesh&&yt.instancingMorph===!0&&H.morphTexture===null||H.isInstancedMesh&&yt.instancingMorph===!1&&H.morphTexture!==null||yt.envMap!==Ct||z.fog===!0&&yt.fog!==vt||yt.numClippingPlanes!==void 0&&(yt.numClippingPlanes!==Dt.numPlanes||yt.numIntersection!==Dt.numIntersection)||yt.vertexAlphas!==Vt||yt.vertexTangents!==qt||yt.morphTargets!==Pt||yt.morphNormals!==oe||yt.morphColors!==Me||yt.toneMapping!==xe||yt.morphTargetsCount!==ze||!!yt.lightProbeGrid!=b.state.lightProbeGridArray.length>0)&&(ne=!0):(ne=!0,yt.__version=z.version);let cn=yt.currentProgram;ne===!0&&(cn=aa(z,O,H),F&&z.isNodeMaterial&&F.onUpdateProgram(z,cn,yt));let Fn=!1,mi=!1,Ss=!1;const ue=cn.getUniforms(),ye=yt.uniforms;if(_.useProgram(cn.program)&&(Fn=!0,mi=!0,Ss=!0),z.id!==it&&(it=z.id,mi=!0),yt.needsLights){const fe=pv(b.state.lightProbeGridArray,H);yt.lightProbeGrid!==fe&&(yt.lightProbeGrid=fe,mi=!0)}if(Fn||pt!==y){_.buffers.depth.getReversed()&&y.reversedDepth!==!0&&(y._reversedDepth=!0,y.updateProjectionMatrix()),ue.setValue(U,"projectionMatrix",y.projectionMatrix),ue.setValue(U,"viewMatrix",y.matrixWorldInverse);const _i=ue.map.cameraPosition;_i!==void 0&&_i.setValue(U,le.setFromMatrixPosition(y.matrixWorld)),A.logarithmicDepthBuffer&&ue.setValue(U,"logDepthBufFC",2/(Math.log(y.far+1)/Math.LN2)),(z.isMeshPhongMaterial||z.isMeshToonMaterial||z.isMeshLambertMaterial||z.isMeshBasicMaterial||z.isMeshStandardMaterial||z.isShaderMaterial)&&ue.setValue(U,"isOrthographic",y.isOrthographicCamera===!0),pt!==y&&(pt=y,mi=!0,Ss=!0)}if(yt.needsLights&&(en.state.directionalShadowMap.length>0&&ue.setValue(U,"directionalShadowMap",en.state.directionalShadowMap,Z),en.state.spotShadowMap.length>0&&ue.setValue(U,"spotShadowMap",en.state.spotShadowMap,Z),en.state.pointShadowMap.length>0&&ue.setValue(U,"pointShadowMap",en.state.pointShadowMap,Z)),H.isSkinnedMesh){ue.setOptional(U,H,"bindMatrix"),ue.setOptional(U,H,"bindMatrixInverse");const fe=H.skeleton;fe&&(fe.boneTexture===null&&fe.computeBoneTexture(),ue.setValue(U,"boneTexture",fe.boneTexture,Z))}H.isBatchedMesh&&(ue.setOptional(U,H,"batchingTexture"),ue.setValue(U,"batchingTexture",H._matricesTexture,Z),ue.setOptional(U,H,"batchingIdTexture"),ue.setValue(U,"batchingIdTexture",H._indirectTexture,Z),ue.setOptional(U,H,"batchingColorTexture"),H._colorsTexture!==null&&ue.setValue(U,"batchingColorTexture",H._colorsTexture,Z));const gi=$.morphAttributes;if((gi.position!==void 0||gi.normal!==void 0||gi.color!==void 0)&&N.update(H,$,cn),(mi||yt.receiveShadow!==H.receiveShadow)&&(yt.receiveShadow=H.receiveShadow,ue.setValue(U,"receiveShadow",H.receiveShadow)),(z.isMeshStandardMaterial||z.isMeshLambertMaterial||z.isMeshPhongMaterial)&&z.envMap===null&&O.environment!==null&&(ye.envMapIntensity.value=O.environmentIntensity),ye.dfgLUT!==void 0&&(ye.dfgLUT.value=I_()),mi){if(ue.setValue(U,"toneMappingExposure",P.toneMappingExposure),yt.needsLights&&gv(ye,Ss),vt&&z.fog===!0&&At.refreshFogUniforms(ye,vt),At.refreshMaterialUniforms(ye,z,W,D,b.state.transmissionRenderTarget[y.id]),yt.needsLights&&yt.lightProbeGrid){const fe=yt.lightProbeGrid;ye.probesSH.value=fe.texture,ye.probesMin.value.copy(fe.boundingBox.min),ye.probesMax.value.copy(fe.boundingBox.max),ye.probesResolution.value.copy(fe.resolution)}Zr.upload(U,Fu(yt),ye,Z)}if(z.isShaderMaterial&&z.uniformsNeedUpdate===!0&&(Zr.upload(U,Fu(yt),ye,Z),z.uniformsNeedUpdate=!1),z.isSpriteMaterial&&ue.setValue(U,"center",H.center),ue.setValue(U,"modelViewMatrix",H.modelViewMatrix),ue.setValue(U,"normalMatrix",H.normalMatrix),ue.setValue(U,"modelMatrix",H.matrixWorld),z.uniformsGroups!==void 0){const fe=z.uniformsGroups;for(let _i=0,bs=fe.length;_i0&&Z.useMultisampledRTT(y)===!1?z=V.get(y).__webglMultisampledFramebuffer:Array.isArray(Ct)?z=Ct[$]:z=Ct,_t.copy(y.viewport),bt.copy(y.scissor),ee=y.scissorTest}else _t.copy(rt).multiplyScalar(W).floor(),bt.copy(Ft).multiplyScalar(W).floor(),ee=Lt;if($!==0&&(z=q),_.bindFramebuffer(U.FRAMEBUFFER,z)&&_.drawBuffers(y,z),_.viewport(_t),_.scissor(bt),_.setScissorTest(ee),H){const xt=V.get(y.texture);U.framebufferTexture2D(U.FRAMEBUFFER,U.COLOR_ATTACHMENT0,U.TEXTURE_CUBE_MAP_POSITIVE_X+O,xt.__webglTexture,$)}else if(vt){const xt=O;for(let Et=0;Et1&&U.readBuffer(U.COLOR_ATTACHMENT0+xt),!A.textureFormatReadable(Vt)){Qt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!A.textureTypeReadable(qt)){Qt("WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}O>=0&&O<=y.width-z&&$>=0&&$<=y.height-H&&U.readPixels(O,$,z,H,ft.convert(Vt),ft.convert(qt),vt)}finally{const Ct=Q!==null?V.get(Q).__webglFramebuffer:null;_.bindFramebuffer(U.FRAMEBUFFER,Ct)}}},this.readRenderTargetPixelsAsync=async function(y,O,$,z,H,vt,St,xt=0){if(!(y&&y.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let Et=V.get(y).__webglFramebuffer;if(y.isWebGLCubeRenderTarget&&St!==void 0&&(Et=Et[St]),Et)if(O>=0&&O<=y.width-z&&$>=0&&$<=y.height-H){_.bindFramebuffer(U.FRAMEBUFFER,Et);const Ct=y.textures[xt],Vt=Ct.format,qt=Ct.type;if(y.textures.length>1&&U.readBuffer(U.COLOR_ATTACHMENT0+xt),!A.textureFormatReadable(Vt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!A.textureTypeReadable(qt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");const Pt=U.createBuffer();U.bindBuffer(U.PIXEL_PACK_BUFFER,Pt),U.bufferData(U.PIXEL_PACK_BUFFER,vt.byteLength,U.STREAM_READ),U.readPixels(O,$,z,H,ft.convert(Vt),ft.convert(qt),0);const oe=Q!==null?V.get(Q).__webglFramebuffer:null;_.bindFramebuffer(U.FRAMEBUFFER,oe);const Me=U.fenceSync(U.SYNC_GPU_COMMANDS_COMPLETE,0);return U.flush(),await Jd(U,Me,4),U.bindBuffer(U.PIXEL_PACK_BUFFER,Pt),U.getBufferSubData(U.PIXEL_PACK_BUFFER,0,vt),U.deleteBuffer(Pt),U.deleteSync(Me),vt}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(y,O=null,$=0){const z=Math.pow(2,-$),H=Math.floor(y.image.width*z),vt=Math.floor(y.image.height*z),St=O!==null?O.x:0,xt=O!==null?O.y:0;Z.setTexture2D(y,0),U.copyTexSubImage2D(U.TEXTURE_2D,$,0,0,St,xt,H,vt),_.unbindTexture()},this.copyTextureToTexture=function(y,O,$=null,z=null,H=0,vt=0){let St,xt,Et,Ct,Vt,qt,Pt,oe,Me;const xe=y.isCompressedTexture?y.mipmaps[vt]:y.image;if($!==null)St=$.max.x-$.min.x,xt=$.max.y-$.min.y,Et=$.isBox3?$.max.z-$.min.z:1,Ct=$.min.x,Vt=$.min.y,qt=$.isBox3?$.min.z:0;else{const ye=Math.pow(2,-H);St=Math.floor(xe.width*ye),xt=Math.floor(xe.height*ye),y.isDataArrayTexture?Et=xe.depth:y.isData3DTexture?Et=Math.floor(xe.depth*ye):Et=1,Ct=0,Vt=0,qt=0}z!==null?(Pt=z.x,oe=z.y,Me=z.z):(Pt=0,oe=0,Me=0);const he=ft.convert(O.format),ze=ft.convert(O.type);let yt;O.isData3DTexture?(Z.setTexture3D(O,0),yt=U.TEXTURE_3D):O.isDataArrayTexture||O.isCompressedArrayTexture?(Z.setTexture2DArray(O,0),yt=U.TEXTURE_2D_ARRAY):(Z.setTexture2D(O,0),yt=U.TEXTURE_2D),_.activeTexture(U.TEXTURE0),_.pixelStorei(U.UNPACK_FLIP_Y_WEBGL,O.flipY),_.pixelStorei(U.UNPACK_PREMULTIPLY_ALPHA_WEBGL,O.premultiplyAlpha),_.pixelStorei(U.UNPACK_ALIGNMENT,O.unpackAlignment);const en=_.getParameter(U.UNPACK_ROW_LENGTH),ne=_.getParameter(U.UNPACK_IMAGE_HEIGHT),cn=_.getParameter(U.UNPACK_SKIP_PIXELS),Fn=_.getParameter(U.UNPACK_SKIP_ROWS),mi=_.getParameter(U.UNPACK_SKIP_IMAGES);_.pixelStorei(U.UNPACK_ROW_LENGTH,xe.width),_.pixelStorei(U.UNPACK_IMAGE_HEIGHT,xe.height),_.pixelStorei(U.UNPACK_SKIP_PIXELS,Ct),_.pixelStorei(U.UNPACK_SKIP_ROWS,Vt),_.pixelStorei(U.UNPACK_SKIP_IMAGES,qt);const Ss=y.isDataArrayTexture||y.isData3DTexture,ue=O.isDataArrayTexture||O.isData3DTexture;if(y.isDepthTexture){const ye=V.get(y),gi=V.get(O),fe=V.get(ye.__renderTarget),_i=V.get(gi.__renderTarget);_.bindFramebuffer(U.READ_FRAMEBUFFER,fe.__webglFramebuffer),_.bindFramebuffer(U.DRAW_FRAMEBUFFER,_i.__webglFramebuffer);for(let bs=0;bsMath.PI&&(n-=Ye),i<-Math.PI?i+=Ye:i>Math.PI&&(i-=Ye),n<=i?this._spherical.theta=Math.max(n,Math.min(i,this._spherical.theta)):this._spherical.theta=this._spherical.theta>(n+i)/2?Math.max(n,this._spherical.theta):Math.min(i,this._spherical.theta)),this._spherical.phi=Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle,this._spherical.phi)),this._spherical.makeSafe(),this.enableDamping===!0?this.target.addScaledVector(this._panOffset,this.dampingFactor):this.target.add(this._panOffset),this.target.sub(this.cursor),this.target.clampLength(this.minTargetRadius,this.maxTargetRadius),this.target.add(this.cursor);let r=!1;if(this.zoomToCursor&&this._performCursorZoom||this.object.isOrthographicCamera)this._spherical.radius=this._clampDistance(this._spherical.radius);else{const a=this._spherical.radius;this._spherical.radius=this._clampDistance(this._spherical.radius*this._scale),r=a!=this._spherical.radius}if(Le.setFromSpherical(this._spherical),Le.applyQuaternion(this._quatInverse),e.copy(this.target).add(Le),this.object.lookAt(this.target),this.enableDamping===!0?(this._sphericalDelta.theta*=1-this.dampingFactor,this._sphericalDelta.phi*=1-this.dampingFactor,this._panOffset.multiplyScalar(1-this.dampingFactor)):(this._sphericalDelta.set(0,0,0),this._panOffset.set(0,0,0)),this.zoomToCursor&&this._performCursorZoom){let a=null;if(this.object.isPerspectiveCamera){const o=Le.length();a=this._clampDistance(o*this._scale);const l=o-a;this.object.position.addScaledVector(this._dollyDirection,l),this.object.updateMatrixWorld(),r=!!l}else if(this.object.isOrthographicCamera){const o=new C(this._mouse.x,this._mouse.y,0);o.unproject(this.object);const l=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),this.object.updateProjectionMatrix(),r=l!==this.object.zoom;const c=new C(this._mouse.x,this._mouse.y,0);c.unproject(this.object),this.object.position.sub(c).add(o),this.object.updateMatrixWorld(),a=Le.length()}else console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."),this.zoomToCursor=!1;a!==null&&(this.screenSpacePanning?this.target.set(0,0,-1).transformDirection(this.object.matrix).multiplyScalar(a).add(this.object.position):(Qr.origin.copy(this.object.position),Qr.direction.set(0,0,-1).transformDirection(this.object.matrix),Math.abs(this.object.up.dot(Qr.direction))vl||8*(1-this._lastQuaternion.dot(this.object.quaternion))>vl||this._lastTargetPosition.distanceToSquared(this.target)>vl?(this.dispatchEvent($h),this._lastPosition.copy(this.object.position),this._lastQuaternion.copy(this.object.quaternion),this._lastTargetPosition.copy(this.target),!0):!1}_getAutoRotationAngle(t){return t!==null?Ye/60*this.autoRotateSpeed*t:Ye/60/60*this.autoRotateSpeed}_getZoomScale(t){const e=Math.abs(t*.01);return Math.pow(.95,this.zoomSpeed*e)}_rotateLeft(t){this._sphericalDelta.theta-=t}_rotateUp(t){this._sphericalDelta.phi-=t}_panLeft(t,e){Le.setFromMatrixColumn(e,0),Le.multiplyScalar(-t),this._panOffset.add(Le)}_panUp(t,e){this.screenSpacePanning===!0?Le.setFromMatrixColumn(e,1):(Le.setFromMatrixColumn(e,0),Le.crossVectors(this.object.up,Le)),Le.multiplyScalar(t),this._panOffset.add(Le)}_pan(t,e){const n=this.domElement;if(this.object.isPerspectiveCamera){const i=this.object.position;Le.copy(i).sub(this.target);let r=Le.length();r*=Math.tan(this.object.fov/2*Math.PI/180),this._panLeft(2*t*r/n.clientHeight,this.object.matrix),this._panUp(2*e*r/n.clientHeight,this.object.matrix)}else this.object.isOrthographicCamera?(this._panLeft(t*(this.object.right-this.object.left)/this.object.zoom/n.clientWidth,this.object.matrix),this._panUp(e*(this.object.top-this.object.bottom)/this.object.zoom/n.clientHeight,this.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),this.enablePan=!1)}_dollyOut(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale/=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_dollyIn(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale*=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_updateZoomParameters(t,e){if(!this.zoomToCursor)return;this._performCursorZoom=!0;const n=this.domElement.getBoundingClientRect(),i=t-n.left,r=e-n.top,a=n.width,o=n.height;this._mouse.x=i/a*2-1,this._mouse.y=-(r/o)*2+1,this._dollyDirection.set(this._mouse.x,this._mouse.y,1).unproject(this.object).sub(this.object.position).normalize()}_clampDistance(t){return Math.max(this.minDistance,Math.min(this.maxDistance,t))}_handleMouseDownRotate(t){this._rotateStart.set(t.clientX,t.clientY)}_handleMouseDownDolly(t){this._updateZoomParameters(t.clientX,t.clientX),this._dollyStart.set(t.clientX,t.clientY)}_handleMouseDownPan(t){this._panStart.set(t.clientX,t.clientY)}_handleMouseMoveRotate(t){this._rotateEnd.set(t.clientX,t.clientY),this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Ye*this._rotateDelta.x/e.clientHeight),this._rotateUp(Ye*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd),this.update()}_handleMouseMoveDolly(t){this._dollyEnd.set(t.clientX,t.clientY),this._dollyDelta.subVectors(this._dollyEnd,this._dollyStart),this._dollyDelta.y>0?this._dollyOut(this._getZoomScale(this._dollyDelta.y)):this._dollyDelta.y<0&&this._dollyIn(this._getZoomScale(this._dollyDelta.y)),this._dollyStart.copy(this._dollyEnd),this.update()}_handleMouseMovePan(t){this._panEnd.set(t.clientX,t.clientY),this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd),this.update()}_handleMouseWheel(t){this._updateZoomParameters(t.clientX,t.clientY),t.deltaY<0?this._dollyIn(this._getZoomScale(t.deltaY)):t.deltaY>0&&this._dollyOut(this._getZoomScale(t.deltaY)),this.update()}_handleKeyDown(t){let e=!1;switch(t.code){case this.keys.UP:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(Ye*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,this.keyPanSpeed),e=!0;break;case this.keys.BOTTOM:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(-Ye*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,-this.keyPanSpeed),e=!0;break;case this.keys.LEFT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(Ye*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(this.keyPanSpeed,0),e=!0;break;case this.keys.RIGHT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(-Ye*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(-this.keyPanSpeed,0),e=!0;break}e&&(t.preventDefault(),this.update())}_handleTouchStartRotate(t){if(this._pointers.length===1)this._rotateStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._rotateStart.set(n,i)}}_handleTouchStartPan(t){if(this._pointers.length===1)this._panStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._panStart.set(n,i)}}_handleTouchStartDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,i=t.pageY-e.y,r=Math.sqrt(n*n+i*i);this._dollyStart.set(0,r)}_handleTouchStartDollyPan(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enablePan&&this._handleTouchStartPan(t)}_handleTouchStartDollyRotate(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enableRotate&&this._handleTouchStartRotate(t)}_handleTouchMoveRotate(t){if(this._pointers.length==1)this._rotateEnd.set(t.pageX,t.pageY);else{const n=this._getSecondPointerPosition(t),i=.5*(t.pageX+n.x),r=.5*(t.pageY+n.y);this._rotateEnd.set(i,r)}this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Ye*this._rotateDelta.x/e.clientHeight),this._rotateUp(Ye*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd)}_handleTouchMovePan(t){if(this._pointers.length===1)this._panEnd.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._panEnd.set(n,i)}this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd)}_handleTouchMoveDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,i=t.pageY-e.y,r=Math.sqrt(n*n+i*i);this._dollyEnd.set(0,r),this._dollyDelta.set(0,Math.pow(this._dollyEnd.y/this._dollyStart.y,this.zoomSpeed)),this._dollyOut(this._dollyDelta.y),this._dollyStart.copy(this._dollyEnd);const a=(t.pageX+e.x)*.5,o=(t.pageY+e.y)*.5;this._updateZoomParameters(a,o)}_handleTouchMoveDollyPan(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enablePan&&this._handleTouchMovePan(t)}_handleTouchMoveDollyRotate(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enableRotate&&this._handleTouchMoveRotate(t)}_addPointer(t){this._pointers.push(t.pointerId)}_removePointer(t){delete this._pointerPositions[t.pointerId];for(let e=0;e[o%4*e,Math.floor(o/4)*n],a=(o,l,c,u,h=26)=>{const[d,f]=r(o);t.fillStyle=u,t.fillRect(d,f,e,n),t.fillStyle=c,t.font=`${h}px "Libertinus Mono", ui-monospace, monospace`,t.textAlign="center",l.forEach((p,v)=>t.fillText(p,d+e/2,f+n/2+(v-(l.length-1)/2)*h*1.3+h*.35)),t.textAlign="left"};a(1,["NO SIGNAL"],"#ffffff","#1432c8",30);{const[o,l]=r(2);["#c0c0c0","#c0c000","#00c0c0","#00c000","#c000c0","#c00000","#0000c0"].forEach((c,u)=>{t.fillStyle=c,t.fillRect(o+u*e/7,l,e/7+1,n*.7)}),t.fillStyle="#0b0b0b",t.fillRect(o,l+n*.7,e,n*.3)}a(3,["PRESENT DAY","PRESENT TIME"],"#e8e8e8","#050505",22);{const[o,l]=r(4);t.fillStyle="#000",t.fillRect(o,l,e,n),t.fillStyle="#b8b8b8",t.font="18px ui-monospace, monospace",t.fillText("C:\\>_",o+16,l+34)}a(5,["▶ PLAY"],"#ffffff","#0a1a7a",30),a(6,["æ"],"#f4f1ea","#000000",110),a(7,["CLOSE THE WORLD","OPEN THE nExT"],"#ff4a4a","#070000",18)}const i=new li(s);return i.colorSpace=Se,i}class J_{constructor(t,e,n){I(this,"group",new pn);I(this,"screens");I(this,"uniforms",{uTime:{value:0},uAtlas:{value:null},uFogColor:{value:new kt},uFogDensity:{value:0}});this.uniforms.uFogColor.value.copy(n.color),this.uniforms.uFogDensity.value=n.density;const i=new Dn(1.26,1.1,.95,2,.05);i.translate(0,.55,-.475);const r=new Vc(i,new Ee({roughness:.6,metalness:.05}),t),a=new We(1,.75);a.translate(0,.62,.004),this.uniforms.uAtlas.value=Z_();const o=new Ce({uniforms:this.uniforms,vertexShader:` +}`;class Kx{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(t,e){if(this.texture===null){const n=new zh(t.texture);(t.depthNear!==e.depthNear||t.depthFar!==e.depthFar)&&(this.depthNear=t.depthNear,this.depthFar=t.depthFar),this.texture=n}}getMesh(t){if(this.texture!==null&&this.mesh===null){const e=t.cameras[0].viewport,n=new Pe({vertexShader:qx,fragmentShader:Yx,uniforms:{depthColor:{value:this.texture},depthWidth:{value:e.z},depthHeight:{value:e.w}}});this.mesh=new zt(new Xe(20,20),n)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class Zx extends ci{constructor(t,e){super();const n=this;let i=null,r=1,o=null,a="local-floor",l=1,c=null,u=null,h=null,d=null,f=null,p=null;const v=typeof XRWebGLBinding<"u",m=new Kx,g={},S=e.getContextAttributes();let E=null,y=null;const M=[],w=[],C=new ft;let x=null;const T=new Ke;T.viewport=new ge;const L=new Ke;L.viewport=new ge;const D=[T,L],U=new Um;let K=null,Z=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(Q){let I=M[Q];return I===void 0&&(I=new dl,M[Q]=I),I.getTargetRaySpace()},this.getControllerGrip=function(Q){let I=M[Q];return I===void 0&&(I=new dl,M[Q]=I),I.getGripSpace()},this.getHand=function(Q){let I=M[Q];return I===void 0&&(I=new dl,M[Q]=I),I.getHandSpace()};function N(Q){const I=w.indexOf(Q.inputSource);if(I===-1)return;const q=M[I];q!==void 0&&(q.update(Q.inputSource,Q.frame,c||o),q.dispatchEvent({type:Q.type,data:Q.inputSource}))}function W(){i.removeEventListener("select",N),i.removeEventListener("selectstart",N),i.removeEventListener("selectend",N),i.removeEventListener("squeeze",N),i.removeEventListener("squeezestart",N),i.removeEventListener("squeezeend",N),i.removeEventListener("end",W),i.removeEventListener("inputsourceschange",B);for(let Q=0;Q=0&&(w[st]=null,M[st].disconnect(q))}for(let I=0;I=w.length){w.push(q),st=lt;break}else if(w[lt]===null){w[lt]=q,st=lt;break}if(st===-1)break}const ut=M[st];ut&&ut.connect(q)}}const X=new P,nt=new P;function ot(Q,I,q){X.setFromMatrixPosition(I.matrixWorld),nt.setFromMatrixPosition(q.matrixWorld);const st=X.distanceTo(nt),ut=I.projectionMatrix.elements,lt=q.projectionMatrix.elements,Ot=ut[14]/(ut[10]-1),Dt=ut[14]/(ut[10]+1),Vt=(ut[9]+1)/ut[5],jt=(ut[9]-1)/ut[5],Qt=(ut[8]-1)/ut[0],ne=(lt[8]+1)/lt[0],ce=Ot*Qt,he=Ot*ne,Ce=st/(-Qt+ne),xe=Ce*-Qt;if(I.matrixWorld.decompose(Q.position,Q.quaternion,Q.scale),Q.translateX(xe),Q.translateZ(Ce),Q.matrixWorld.compose(Q.position,Q.quaternion,Q.scale),Q.matrixWorldInverse.copy(Q.matrixWorld).invert(),ut[10]===-1)Q.projectionMatrix.copy(I.projectionMatrix),Q.projectionMatrixInverse.copy(I.projectionMatrixInverse);else{const Re=Ot+Ce,O=Dt+Ce,Je=ce-xe,oe=he+(st-xe),A=Vt*Dt/O*Re,_=jt*Dt/O*Re;Q.projectionMatrix.makePerspective(Je,oe,A,_,Re,O),Q.projectionMatrixInverse.copy(Q.projectionMatrix).invert()}}function at(Q,I){I===null?Q.matrixWorld.copy(Q.matrix):Q.matrixWorld.multiplyMatrices(I.matrixWorld,Q.matrix),Q.matrixWorldInverse.copy(Q.matrixWorld).invert()}this.updateCamera=function(Q){if(i===null)return;let I=Q.near,q=Q.far;m.texture!==null&&(m.depthNear>0&&(I=m.depthNear),m.depthFar>0&&(q=m.depthFar)),U.near=L.near=T.near=I,U.far=L.far=T.far=q,(K!==U.near||Z!==U.far)&&(i.updateRenderState({depthNear:U.near,depthFar:U.far}),K=U.near,Z=U.far),U.layers.mask=Q.layers.mask|6,T.layers.mask=U.layers.mask&-5,L.layers.mask=U.layers.mask&-3;const st=Q.parent,ut=U.cameras;at(U,st);for(let lt=0;lt0&&(m.alphaTest.value=g.alphaTest);const S=t.get(g),E=S.envMap,y=S.envMapRotation;E&&(m.envMap.value=E,m.envMapRotation.value.setFromMatrix4(Jx.makeRotationFromEuler(y)).transpose(),E.isCubeTexture&&E.isRenderTargetTexture===!1&&m.envMapRotation.value.premultiply(Uu),m.reflectivity.value=g.reflectivity,m.ior.value=g.ior,m.refractionRatio.value=g.refractionRatio),g.lightMap&&(m.lightMap.value=g.lightMap,m.lightMapIntensity.value=g.lightMapIntensity,e(g.lightMap,m.lightMapTransform)),g.aoMap&&(m.aoMap.value=g.aoMap,m.aoMapIntensity.value=g.aoMapIntensity,e(g.aoMap,m.aoMapTransform))}function o(m,g){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,g.map&&(m.map.value=g.map,e(g.map,m.mapTransform))}function a(m,g){m.dashSize.value=g.dashSize,m.totalSize.value=g.dashSize+g.gapSize,m.scale.value=g.scale}function l(m,g,S,E){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,m.size.value=g.size*S,m.scale.value=E*.5,g.map&&(m.map.value=g.map,e(g.map,m.uvTransform)),g.alphaMap&&(m.alphaMap.value=g.alphaMap,e(g.alphaMap,m.alphaMapTransform)),g.alphaTest>0&&(m.alphaTest.value=g.alphaTest)}function c(m,g){m.diffuse.value.copy(g.color),m.opacity.value=g.opacity,m.rotation.value=g.rotation,g.map&&(m.map.value=g.map,e(g.map,m.mapTransform)),g.alphaMap&&(m.alphaMap.value=g.alphaMap,e(g.alphaMap,m.alphaMapTransform)),g.alphaTest>0&&(m.alphaTest.value=g.alphaTest)}function u(m,g){m.specular.value.copy(g.specular),m.shininess.value=Math.max(g.shininess,1e-4)}function h(m,g){g.gradientMap&&(m.gradientMap.value=g.gradientMap)}function d(m,g){m.metalness.value=g.metalness,g.metalnessMap&&(m.metalnessMap.value=g.metalnessMap,e(g.metalnessMap,m.metalnessMapTransform)),m.roughness.value=g.roughness,g.roughnessMap&&(m.roughnessMap.value=g.roughnessMap,e(g.roughnessMap,m.roughnessMapTransform)),g.envMap&&(m.envMapIntensity.value=g.envMapIntensity)}function f(m,g,S){m.ior.value=g.ior,g.sheen>0&&(m.sheenColor.value.copy(g.sheenColor).multiplyScalar(g.sheen),m.sheenRoughness.value=g.sheenRoughness,g.sheenColorMap&&(m.sheenColorMap.value=g.sheenColorMap,e(g.sheenColorMap,m.sheenColorMapTransform)),g.sheenRoughnessMap&&(m.sheenRoughnessMap.value=g.sheenRoughnessMap,e(g.sheenRoughnessMap,m.sheenRoughnessMapTransform))),g.clearcoat>0&&(m.clearcoat.value=g.clearcoat,m.clearcoatRoughness.value=g.clearcoatRoughness,g.clearcoatMap&&(m.clearcoatMap.value=g.clearcoatMap,e(g.clearcoatMap,m.clearcoatMapTransform)),g.clearcoatRoughnessMap&&(m.clearcoatRoughnessMap.value=g.clearcoatRoughnessMap,e(g.clearcoatRoughnessMap,m.clearcoatRoughnessMapTransform)),g.clearcoatNormalMap&&(m.clearcoatNormalMap.value=g.clearcoatNormalMap,e(g.clearcoatNormalMap,m.clearcoatNormalMapTransform),m.clearcoatNormalScale.value.copy(g.clearcoatNormalScale),g.side===We&&m.clearcoatNormalScale.value.negate())),g.dispersion>0&&(m.dispersion.value=g.dispersion),g.iridescence>0&&(m.iridescence.value=g.iridescence,m.iridescenceIOR.value=g.iridescenceIOR,m.iridescenceThicknessMinimum.value=g.iridescenceThicknessRange[0],m.iridescenceThicknessMaximum.value=g.iridescenceThicknessRange[1],g.iridescenceMap&&(m.iridescenceMap.value=g.iridescenceMap,e(g.iridescenceMap,m.iridescenceMapTransform)),g.iridescenceThicknessMap&&(m.iridescenceThicknessMap.value=g.iridescenceThicknessMap,e(g.iridescenceThicknessMap,m.iridescenceThicknessMapTransform))),g.transmission>0&&(m.transmission.value=g.transmission,m.transmissionSamplerMap.value=S.texture,m.transmissionSamplerSize.value.set(S.width,S.height),g.transmissionMap&&(m.transmissionMap.value=g.transmissionMap,e(g.transmissionMap,m.transmissionMapTransform)),m.thickness.value=g.thickness,g.thicknessMap&&(m.thicknessMap.value=g.thicknessMap,e(g.thicknessMap,m.thicknessMapTransform)),m.attenuationDistance.value=g.attenuationDistance,m.attenuationColor.value.copy(g.attenuationColor)),g.anisotropy>0&&(m.anisotropyVector.value.set(g.anisotropy*Math.cos(g.anisotropyRotation),g.anisotropy*Math.sin(g.anisotropyRotation)),g.anisotropyMap&&(m.anisotropyMap.value=g.anisotropyMap,e(g.anisotropyMap,m.anisotropyMapTransform))),m.specularIntensity.value=g.specularIntensity,m.specularColor.value.copy(g.specularColor),g.specularColorMap&&(m.specularColorMap.value=g.specularColorMap,e(g.specularColorMap,m.specularColorMapTransform)),g.specularIntensityMap&&(m.specularIntensityMap.value=g.specularIntensityMap,e(g.specularIntensityMap,m.specularIntensityMapTransform))}function p(m,g){g.matcap&&(m.matcap.value=g.matcap)}function v(m,g){const S=t.get(g).light;m.referencePosition.value.setFromMatrixPosition(S.matrixWorld),m.nearDistance.value=S.shadow.camera.near,m.farDistance.value=S.shadow.camera.far}return{refreshFogUniforms:n,refreshMaterialUniforms:i}}function Qx(s,t,e,n){let i={},r={},o=[];const a=s.getParameter(s.MAX_UNIFORM_BUFFER_BINDINGS);function l(y,M){const w=M.program;n.uniformBlockBinding(y,w)}function c(y,M){let w=i[y.id];w===void 0&&(m(y),w=u(y),i[y.id]=w,y.addEventListener("dispose",S));const C=M.program;n.updateUBOMapping(y,C);const x=t.render.frame;r[y.id]!==x&&(d(y),r[y.id]=x)}function u(y){const M=h();y.__bindingPointIndex=M;const w=s.createBuffer(),C=y.__size,x=y.usage;return s.bindBuffer(s.UNIFORM_BUFFER,w),s.bufferData(s.UNIFORM_BUFFER,C,x),s.bindBuffer(s.UNIFORM_BUFFER,null),s.bindBufferBase(s.UNIFORM_BUFFER,M,w),w}function h(){for(let y=0;y0&&(w+=C-x),y.__size=w,y.__cache={},this}function g(y){const M={boundary:0,storage:0};return typeof y=="number"||typeof y=="boolean"?(M.boundary=4,M.storage=4):y.isVector2?(M.boundary=8,M.storage=8):y.isVector3||y.isColor?(M.boundary=16,M.storage=12):y.isVector4?(M.boundary=16,M.storage=16):y.isMatrix3?(M.boundary=48,M.storage=48):y.isMatrix4?(M.boundary=64,M.storage=64):y.isTexture?Ut("WebGLRenderer: Texture samplers can not be part of an uniforms group."):ArrayBuffer.isView(y)?(M.boundary=16,M.storage=y.byteLength):Ut("WebGLRenderer: Unsupported uniform value type.",y),M}function S(y){const M=y.target;M.removeEventListener("dispose",S);const w=o.indexOf(M.__bindingPointIndex);o.splice(w,1),s.deleteBuffer(i[M.id]),delete i[M.id],delete r[M.id]}function E(){for(const y in i)s.deleteBuffer(i[y]);o=[],i={},r={}}return{bind:l,update:c,dispose:E}}const t_=new Uint16Array([12469,15057,12620,14925,13266,14620,13807,14376,14323,13990,14545,13625,14713,13328,14840,12882,14931,12528,14996,12233,15039,11829,15066,11525,15080,11295,15085,10976,15082,10705,15073,10495,13880,14564,13898,14542,13977,14430,14158,14124,14393,13732,14556,13410,14702,12996,14814,12596,14891,12291,14937,11834,14957,11489,14958,11194,14943,10803,14921,10506,14893,10278,14858,9960,14484,14039,14487,14025,14499,13941,14524,13740,14574,13468,14654,13106,14743,12678,14818,12344,14867,11893,14889,11509,14893,11180,14881,10751,14852,10428,14812,10128,14765,9754,14712,9466,14764,13480,14764,13475,14766,13440,14766,13347,14769,13070,14786,12713,14816,12387,14844,11957,14860,11549,14868,11215,14855,10751,14825,10403,14782,10044,14729,9651,14666,9352,14599,9029,14967,12835,14966,12831,14963,12804,14954,12723,14936,12564,14917,12347,14900,11958,14886,11569,14878,11247,14859,10765,14828,10401,14784,10011,14727,9600,14660,9289,14586,8893,14508,8533,15111,12234,15110,12234,15104,12216,15092,12156,15067,12010,15028,11776,14981,11500,14942,11205,14902,10752,14861,10393,14812,9991,14752,9570,14682,9252,14603,8808,14519,8445,14431,8145,15209,11449,15208,11451,15202,11451,15190,11438,15163,11384,15117,11274,15055,10979,14994,10648,14932,10343,14871,9936,14803,9532,14729,9218,14645,8742,14556,8381,14461,8020,14365,7603,15273,10603,15272,10607,15267,10619,15256,10631,15231,10614,15182,10535,15118,10389,15042,10167,14963,9787,14883,9447,14800,9115,14710,8665,14615,8318,14514,7911,14411,7507,14279,7198,15314,9675,15313,9683,15309,9712,15298,9759,15277,9797,15229,9773,15166,9668,15084,9487,14995,9274,14898,8910,14800,8539,14697,8234,14590,7790,14479,7409,14367,7067,14178,6621,15337,8619,15337,8631,15333,8677,15325,8769,15305,8871,15264,8940,15202,8909,15119,8775,15022,8565,14916,8328,14804,8009,14688,7614,14569,7287,14448,6888,14321,6483,14088,6171,15350,7402,15350,7419,15347,7480,15340,7613,15322,7804,15287,7973,15229,8057,15148,8012,15046,7846,14933,7611,14810,7357,14682,7069,14552,6656,14421,6316,14251,5948,14007,5528,15356,5942,15356,5977,15353,6119,15348,6294,15332,6551,15302,6824,15249,7044,15171,7122,15070,7050,14949,6861,14818,6611,14679,6349,14538,6067,14398,5651,14189,5311,13935,4958,15359,4123,15359,4153,15356,4296,15353,4646,15338,5160,15311,5508,15263,5829,15188,6042,15088,6094,14966,6001,14826,5796,14678,5543,14527,5287,14377,4985,14133,4586,13869,4257,15360,1563,15360,1642,15358,2076,15354,2636,15341,3350,15317,4019,15273,4429,15203,4732,15105,4911,14981,4932,14836,4818,14679,4621,14517,4386,14359,4156,14083,3795,13808,3437,15360,122,15360,137,15358,285,15355,636,15344,1274,15322,2177,15281,2765,15215,3223,15120,3451,14995,3569,14846,3567,14681,3466,14511,3305,14344,3121,14037,2800,13753,2467,15360,0,15360,1,15359,21,15355,89,15346,253,15325,479,15287,796,15225,1148,15133,1492,15008,1749,14856,1882,14685,1886,14506,1783,14324,1608,13996,1398,13702,1183]);let Nn=null;function e_(){return Nn===null&&(Nn=new Lh(t_,16,16,Oi,Qe),Nn.name="DFG_LUT",Nn.minFilter=Oe,Nn.magFilter=Oe,Nn.wrapS=Vn,Nn.wrapT=Vn,Nn.generateMipmaps=!1,Nn.needsUpdate=!0),Nn}class n_{constructor(t={}){const{canvas:e=_p(),context:n=null,depth:i=!0,stencil:r=!1,alpha:o=!1,antialias:a=!1,premultipliedAlpha:l=!0,preserveDrawingBuffer:c=!1,powerPreference:u="default",failIfMajorPerformanceCaveat:h=!1,reversedDepthBuffer:d=!1,outputBufferType:f=je}=t;this.isWebGLRenderer=!0;let p;if(n!==null){if(typeof WebGLRenderingContext<"u"&&n instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");p=n.getContextAttributes().alpha}else p=o;const v=f,m=new Set([wa,Sa,ba]),g=new Set([je,Rn,Xs,qs,_a,ya]),S=new Uint32Array(4),E=new Int32Array(4),y=new P;let M=null,w=null;const C=[],x=[];let T=null;this.domElement=e,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=Cn,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const L=this;let D=!1,U=null,K=null,Z=null,N=null;this._outputColorSpace=we;let W=0,B=0,X=null,nt=-1,ot=null;const at=new ge,vt=new ge;let Gt=null;const me=new Bt(0);let ee=0,Q=e.width,I=e.height,q=1,st=null,ut=null;const lt=new ge(0,0,Q,I),Ot=new ge(0,0,Q,I);let Dt=!1;const Vt=new ro;let jt=!1,Qt=!1;const ne=new re,ce=new P,he=new ge,Ce={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let xe=!1;function Re(){return X===null?q:1}let O=n;function Je(b,k){return e.getContext(b,k)}try{const b={alpha:!0,depth:i,stencil:r,antialias:a,premultipliedAlpha:l,preserveDrawingBuffer:c,powerPreference:u,failIfMajorPerformanceCaveat:h};if("setAttribute"in e&&e.setAttribute("data-engine",`three.js r${jo}`),e.addEventListener("webglcontextlost",_e,!1),e.addEventListener("webglcontextrestored",fe,!1),e.addEventListener("webglcontextcreationerror",On,!1),O===null){const k="webgl2";if(O=Je(k,b),O===null)throw Je(k)?new Error("THREE.WebGLRenderer: Error creating WebGL context with your selected attributes."):new Error("THREE.WebGLRenderer: Error creating WebGL context.")}}catch(b){throw te("WebGLRenderer: "+b.message),b}let oe,A,_,z,$,J,ct,dt,j,et,pt,Ct,_t,mt,Nt,Ft,$t,F,ht,tt,gt,bt,it;function At(){oe=new ev(O),oe.init(),gt=new Xx(O,oe),A=new qg(O,oe,t,gt),_=new Wx(O,oe),A.reversedDepthBuffer&&d&&_.buffers.depth.setReversed(!0),K=O.createFramebuffer(),Z=O.createFramebuffer(),N=O.createFramebuffer(),z=new sv(O),$=new Px,J=new $x(O,oe,_,$,A,gt,z),ct=new tv(L),dt=new Gm(O),bt=new $g(O,dt),j=new nv(O,dt,z,bt),et=new ov(O,j,dt,bt,z),F=new rv(O,A,J),Nt=new Yg($),pt=new Rx(L,ct,oe,A,bt,Nt),Ct=new jx(L,$),_t=new Dx,mt=new kx(oe),$t=new Wg(L,ct,_,et,p,l),Ft=new Vx(L,et,A),it=new Qx(O,z,A,_),ht=new Xg(O,oe,z),tt=new iv(O,oe,z),z.programs=pt.programs,L.capabilities=A,L.extensions=oe,L.properties=$,L.renderLists=_t,L.shadowMap=Ft,L.state=_,L.info=z}At(),v!==je&&(T=new lv(v,e.width,e.height,a,i,r));const Et=new Zx(L,O);this.xr=Et,this.getContext=function(){return O},this.getContextAttributes=function(){return O.getContextAttributes()},this.forceContextLoss=function(){const b=oe.get("WEBGL_lose_context");b&&b.loseContext()},this.forceContextRestore=function(){const b=oe.get("WEBGL_lose_context");b&&b.restoreContext()},this.getPixelRatio=function(){return q},this.setPixelRatio=function(b){b!==void 0&&(q=b,this.setSize(Q,I,!1))},this.getSize=function(b){return b.set(Q,I)},this.setSize=function(b,k,Y=!0){if(Et.isPresenting){Ut("WebGLRenderer: Can't change size while VR device is presenting.");return}Q=b,I=k,e.width=Math.floor(b*q),e.height=Math.floor(k*q),Y===!0&&(e.style.width=b+"px",e.style.height=k+"px"),T!==null&&T.setSize(e.width,e.height),this.setViewport(0,0,b,k)},this.getDrawingBufferSize=function(b){return b.set(Q*q,I*q).floor()},this.setDrawingBufferSize=function(b,k,Y){Q=b,I=k,q=Y,e.width=Math.floor(b*Y),e.height=Math.floor(k*Y),this.setViewport(0,0,b,k)},this.setEffects=function(b){if(v===je){te("WebGLRenderer: setEffects() requires outputBufferType set to HalfFloatType or FloatType.");return}if(b){for(let k=0;k{function Mt(){if(H.forEach(function(wt){$.get(wt).currentProgram.isReady()&&H.delete(wt)}),H.size===0){G(b);return}setTimeout(Mt,10)}oe.get("KHR_parallel_shader_compile")!==null?Mt():setTimeout(Mt,10)})};let yc=null;function Uy(b){yc&&yc(b)}function bd(){Xi.stop()}function Sd(){Xi.start()}const Xi=new ru;Xi.setAnimationLoop(Uy),typeof self<"u"&&Xi.setContext(self),this.setAnimationLoop=function(b){yc=b,Et.setAnimationLoop(b),b===null?Xi.stop():Xi.start()},Et.addEventListener("sessionstart",bd),Et.addEventListener("sessionend",Sd),this.render=function(b,k){if(k!==void 0&&k.isCamera!==!0){te("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(D===!0)return;U!==null&&U.renderStart(b,k);const Y=Et.enabled===!0&&Et.isPresenting===!0,H=T!==null&&(X===null||Y)&&T.begin(L,X);if(b.matrixWorldAutoUpdate===!0&&b.updateMatrixWorld(),k.parent===null&&k.matrixWorldAutoUpdate===!0&&k.updateMatrixWorld(),Et.enabled===!0&&Et.isPresenting===!0&&(T===null||T.isCompositing()===!1)&&(Et.cameraAutoUpdate===!0&&Et.updateCamera(k),k=Et.getCamera()),b.isScene===!0&&b.onBeforeRender(L,b,k,X),w=mt.get(b,x.length),w.init(k),w.state.textureUnits=J.getTextureUnits(),x.push(w),ne.multiplyMatrices(k.projectionMatrix,k.matrixWorldInverse),Vt.setFromProjectionMatrix(ne,Pn,k.reversedDepth),Qt=this.localClippingEnabled,jt=Nt.init(this.clippingPlanes,Qt),M=_t.get(b,C.length),M.init(),C.push(M),Et.enabled===!0&&Et.isPresenting===!0){const wt=L.xr.getDepthSensingMesh();wt!==null&&Mc(wt,k,-1/0,L.sortObjects)}Mc(b,k,0,L.sortObjects),M.finish(),L.sortObjects===!0&&M.sort(st,ut,k.reversedDepth),xe=Et.enabled===!1||Et.isPresenting===!1||Et.hasDepthSensing()===!1,xe&&$t.addToRenderList(M,b),this.info.render.frame++,this.info.autoReset===!0&&this.info.reset(),jt===!0&&Nt.beginShadows();const G=w.state.shadowsArray;if(Ft.render(G,b,k),jt===!0&&Nt.endShadows(),(H&&T.hasRenderPass())===!1){const wt=M.opaque,yt=M.transmissive;if(w.setupLights(),k.isArrayCamera){const Tt=k.cameras;if(yt.length>0)for(let Rt=0,Xt=Tt.length;Rt0&&Ed(wt,yt,b,k),xe&&$t.render(b),wd(M,b,k)}X!==null&&B===0&&(J.updateMultisampleRenderTarget(X),J.updateRenderTargetMipmap(X)),H&&T.end(L),b.isScene===!0&&b.onAfterRender(L,b,k),bt.resetDefaultState(),nt=-1,ot=null,x.pop(),x.length>0?(w=x[x.length-1],J.setTextureUnits(w.state.textureUnits),jt===!0&&Nt.setGlobalState(L.clippingPlanes,w.state.camera)):w=null,C.pop(),C.length>0?M=C[C.length-1]:M=null,U!==null&&U.renderEnd()};function Mc(b,k,Y,H){if(b.visible===!1)return;if(b.layers.test(k.layers)){if(b.isGroup)Y=b.renderOrder;else if(b.isLOD)b.autoUpdate===!0&&b.update(k);else if(b.isLightProbeGrid)w.pushLightProbeGrid(b);else if(b.isLight)w.pushLight(b),b.castShadow&&w.pushShadow(b);else if(b.isSprite){if(!b.frustumCulled||Vt.intersectsSprite(b)){H&&he.setFromMatrixPosition(b.matrixWorld).applyMatrix4(ne);const wt=et.update(b),yt=b.material;yt.visible&&M.push(b,wt,yt,Y,he.z,null)}}else if((b.isMesh||b.isLine||b.isPoints)&&(!b.frustumCulled||Vt.intersectsObject(b))){const wt=et.update(b),yt=b.material;if(H&&(b.boundingSphere!==void 0?(b.boundingSphere===null&&b.computeBoundingSphere(),he.copy(b.boundingSphere.center)):(wt.boundingSphere===null&&wt.computeBoundingSphere(),he.copy(wt.boundingSphere.center)),he.applyMatrix4(b.matrixWorld).applyMatrix4(ne)),Array.isArray(yt)){const Tt=wt.groups;for(let Rt=0,Xt=Tt.length;Rt0&&Ao(G,k,Y),Mt.length>0&&Ao(Mt,k,Y),wt.length>0&&Ao(wt,k,Y),_.buffers.depth.setTest(!0),_.buffers.depth.setMask(!0),_.buffers.color.setMask(!0),_.setPolygonOffset(!1)}function Ed(b,k,Y,H){if((Y.isScene===!0?Y.overrideMaterial:null)!==null)return;if(w.state.transmissionRenderTarget[H.id]===void 0){const Lt=oe.has("EXT_color_buffer_half_float")||oe.has("EXT_color_buffer_float");w.state.transmissionRenderTarget[H.id]=new Ye(1,1,{generateMipmaps:!0,type:Lt?Qe:je,minFilter:Ui,samples:Math.max(4,A.samples),stencilBuffer:r,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:Jt.workingColorSpace})}const Mt=w.state.transmissionRenderTarget[H.id],wt=H.viewport||at;Mt.setSize(wt.z*L.transmissionResolutionScale,wt.w*L.transmissionResolutionScale);const yt=L.getRenderTarget(),Tt=L.getActiveCubeFace(),Rt=L.getActiveMipmapLevel();L.setRenderTarget(Mt),L.getClearColor(me),ee=L.getClearAlpha(),ee<1&&L.setClearColor(16777215,.5),L.clear(),xe&&$t.render(Y);const Xt=L.toneMapping;L.toneMapping=Cn;const Kt=H.viewport;if(H.viewport!==void 0&&(H.viewport=void 0),w.setupLightsView(H),jt===!0&&Nt.setGlobalState(L.clippingPlanes,H),Ao(b,Y,H),J.updateMultisampleRenderTarget(Mt),J.updateRenderTargetMipmap(Mt),oe.has("WEBGL_multisampled_render_to_texture")===!1){let Lt=!1;for(let le=0,be=k.length;le0,H.currentProgram=Kt,H.uniformsList=null,Kt}function Ad(b){if(b.uniformsList===null){const k=b.currentProgram.getUniforms();b.uniformsList=xo.seqWithValue(k.seq,b.uniforms)}return b.uniformsList}function Cd(b,k){const Y=$.get(b);Y.outputColorSpace=k.outputColorSpace,Y.batching=k.batching,Y.batchingColor=k.batchingColor,Y.instancing=k.instancing,Y.instancingColor=k.instancingColor,Y.instancingMorph=k.instancingMorph,Y.skinning=k.skinning,Y.morphTargets=k.morphTargets,Y.morphNormals=k.morphNormals,Y.morphColors=k.morphColors,Y.morphTargetsCount=k.morphTargetsCount,Y.numClippingPlanes=k.numClippingPlanes,Y.numIntersection=k.numClipIntersection,Y.vertexAlphas=k.vertexAlphas,Y.vertexTangents=k.vertexTangents,Y.toneMapping=k.toneMapping}function Fy(b,k){if(b.length===0)return null;if(b.length===1)return b[0].texture!==null?b[0]:null;y.setFromMatrixPosition(k.matrixWorld);for(let Y=0,H=b.length;Y0),Lt=!!Y.morphAttributes.position,le=!!Y.morphAttributes.normal,be=!!Y.morphAttributes.color;let ye=Cn;H.toneMapped&&(X===null||X.isXRRenderTarget===!0)&&(ye=L.toneMapping);const ue=Y.morphAttributes.position||Y.morphAttributes.normal||Y.morphAttributes.color,Ge=ue!==void 0?ue.length:0,St=$.get(H),sn=w.state.lights;if(jt===!0&&(Qt===!0||b!==ot)){const pe=b===ot&&H.id===nt;Nt.setState(H,b,pe)}let ie=!1;H.version===St.__version?(St.needsLights&&St.lightsStateVersion!==sn.state.version||St.outputColorSpace!==yt||G.isBatchedMesh&&St.batching===!1||!G.isBatchedMesh&&St.batching===!0||G.isBatchedMesh&&St.batchingColor===!0&&G.colorTexture===null||G.isBatchedMesh&&St.batchingColor===!1&&G.colorTexture!==null||G.isInstancedMesh&&St.instancing===!1||!G.isInstancedMesh&&St.instancing===!0||G.isSkinnedMesh&&St.skinning===!1||!G.isSkinnedMesh&&St.skinning===!0||G.isInstancedMesh&&St.instancingColor===!0&&G.instanceColor===null||G.isInstancedMesh&&St.instancingColor===!1&&G.instanceColor!==null||G.isInstancedMesh&&St.instancingMorph===!0&&G.morphTexture===null||G.isInstancedMesh&&St.instancingMorph===!1&&G.morphTexture!==null||St.envMap!==Rt||H.fog===!0&&St.fog!==Mt||St.numClippingPlanes!==void 0&&(St.numClippingPlanes!==Nt.numPlanes||St.numIntersection!==Nt.numIntersection)||St.vertexAlphas!==Xt||St.vertexTangents!==Kt||St.morphTargets!==Lt||St.morphNormals!==le||St.morphColors!==be||St.toneMapping!==ye||St.morphTargetsCount!==Ge||!!St.lightProbeGrid!=w.state.lightProbeGridArray.length>0)&&(ie=!0):(ie=!0,St.__version=H.version);let dn=St.currentProgram;ie===!0&&(dn=Co(H,k,G),U&&H.isNodeMaterial&&U.onUpdateProgram(H,dn,St));let Bn=!1,Si=!1,Os=!1;const de=dn.getUniforms(),Se=St.uniforms;if(_.useProgram(dn.program)&&(Bn=!0,Si=!0,Os=!0),H.id!==nt&&(nt=H.id,Si=!0),St.needsLights){const pe=Fy(w.state.lightProbeGridArray,G);St.lightProbeGrid!==pe&&(St.lightProbeGrid=pe,Si=!0)}if(Bn||ot!==b){_.buffers.depth.getReversed()&&b.reversedDepth!==!0&&(b._reversedDepth=!0,b.updateProjectionMatrix()),de.setValue(O,"projectionMatrix",b.projectionMatrix),de.setValue(O,"viewMatrix",b.matrixWorldInverse);const Ei=de.map.cameraPosition;Ei!==void 0&&Ei.setValue(O,ce.setFromMatrixPosition(b.matrixWorld)),A.logarithmicDepthBuffer&&de.setValue(O,"logDepthBufFC",2/(Math.log(b.far+1)/Math.LN2)),(H.isMeshPhongMaterial||H.isMeshToonMaterial||H.isMeshLambertMaterial||H.isMeshBasicMaterial||H.isMeshStandardMaterial||H.isShaderMaterial)&&de.setValue(O,"isOrthographic",b.isOrthographicCamera===!0),ot!==b&&(ot=b,Si=!0,Os=!0)}if(St.needsLights&&(sn.state.directionalShadowMap.length>0&&de.setValue(O,"directionalShadowMap",sn.state.directionalShadowMap,J),sn.state.spotShadowMap.length>0&&de.setValue(O,"spotShadowMap",sn.state.spotShadowMap,J),sn.state.pointShadowMap.length>0&&de.setValue(O,"pointShadowMap",sn.state.pointShadowMap,J)),G.isSkinnedMesh){de.setOptional(O,G,"bindMatrix"),de.setOptional(O,G,"bindMatrixInverse");const pe=G.skeleton;pe&&(pe.boneTexture===null&&pe.computeBoneTexture(),de.setValue(O,"boneTexture",pe.boneTexture,J))}G.isBatchedMesh&&(de.setOptional(O,G,"batchingTexture"),de.setValue(O,"batchingTexture",G._matricesTexture,J),de.setOptional(O,G,"batchingIdTexture"),de.setValue(O,"batchingIdTexture",G._indirectTexture,J),de.setOptional(O,G,"batchingColorTexture"),G._colorsTexture!==null&&de.setValue(O,"batchingColorTexture",G._colorsTexture,J));const wi=Y.morphAttributes;if((wi.position!==void 0||wi.normal!==void 0||wi.color!==void 0)&&F.update(G,Y,dn),(Si||St.receiveShadow!==G.receiveShadow)&&(St.receiveShadow=G.receiveShadow,de.setValue(O,"receiveShadow",G.receiveShadow)),(H.isMeshStandardMaterial||H.isMeshLambertMaterial||H.isMeshPhongMaterial)&&H.envMap===null&&k.environment!==null&&(Se.envMapIntensity.value=k.environmentIntensity),Se.dfgLUT!==void 0&&(Se.dfgLUT.value=e_()),Si){if(de.setValue(O,"toneMappingExposure",L.toneMappingExposure),St.needsLights&&ky(Se,Os),Mt&&H.fog===!0&&Ct.refreshFogUniforms(Se,Mt),Ct.refreshMaterialUniforms(Se,H,q,I,w.state.transmissionRenderTarget[b.id]),St.needsLights&&St.lightProbeGrid){const pe=St.lightProbeGrid;Se.probesSH.value=pe.texture,Se.probesMin.value.copy(pe.boundingBox.min),Se.probesMax.value.copy(pe.boundingBox.max),Se.probesResolution.value.copy(pe.resolution)}xo.upload(O,Ad(St),Se,J)}if(H.isShaderMaterial&&H.uniformsNeedUpdate===!0&&(xo.upload(O,Ad(St),Se,J),H.uniformsNeedUpdate=!1),H.isSpriteMaterial&&de.setValue(O,"center",G.center),de.setValue(O,"modelViewMatrix",G.modelViewMatrix),de.setValue(O,"normalMatrix",G.normalMatrix),de.setValue(O,"modelMatrix",G.matrixWorld),H.uniformsGroups!==void 0){const pe=H.uniformsGroups;for(let Ei=0,ks=pe.length;Ei0&&J.useMultisampledRTT(b)===!1?H=$.get(b).__webglMultisampledFramebuffer:Array.isArray(Rt)?H=Rt[Y]:H=Rt,at.copy(b.viewport),vt.copy(b.scissor),Gt=b.scissorTest}else at.copy(lt).multiplyScalar(q).floor(),vt.copy(Ot).multiplyScalar(q).floor(),Gt=Dt;if(Y!==0&&(H=K),_.bindFramebuffer(O.FRAMEBUFFER,H)&&_.drawBuffers(b,H),_.viewport(at),_.scissor(vt),_.setScissorTest(Gt),G){const yt=$.get(b.texture);O.framebufferTexture2D(O.FRAMEBUFFER,O.COLOR_ATTACHMENT0,O.TEXTURE_CUBE_MAP_POSITIVE_X+k,yt.__webglTexture,Y)}else if(Mt){const yt=k;for(let Tt=0;Tt1&&O.readBuffer(O.COLOR_ATTACHMENT0+yt),!A.textureFormatReadable(Xt)){te("WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.");return}if(!A.textureTypeReadable(Kt)){te("WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.");return}k>=0&&k<=b.width-H&&Y>=0&&Y<=b.height-G&&O.readPixels(k,Y,H,G,gt.convert(Xt),gt.convert(Kt),Mt)}finally{const Rt=X!==null?$.get(X).__webglFramebuffer:null;_.bindFramebuffer(O.FRAMEBUFFER,Rt)}}},this.readRenderTargetPixelsAsync=async function(b,k,Y,H,G,Mt,wt,yt=0){if(!(b&&b.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let Tt=$.get(b).__webglFramebuffer;if(b.isWebGLCubeRenderTarget&&wt!==void 0&&(Tt=Tt[wt]),Tt)if(k>=0&&k<=b.width-H&&Y>=0&&Y<=b.height-G){_.bindFramebuffer(O.FRAMEBUFFER,Tt);const Rt=b.textures[yt],Xt=Rt.format,Kt=Rt.type;if(b.textures.length>1&&O.readBuffer(O.COLOR_ATTACHMENT0+yt),!A.textureFormatReadable(Xt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!A.textureTypeReadable(Kt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");const Lt=O.createBuffer();O.bindBuffer(O.PIXEL_PACK_BUFFER,Lt),O.bufferData(O.PIXEL_PACK_BUFFER,Mt.byteLength,O.STREAM_READ),O.readPixels(k,Y,H,G,gt.convert(Xt),gt.convert(Kt),0);const le=X!==null?$.get(X).__webglFramebuffer:null;_.bindFramebuffer(O.FRAMEBUFFER,le);const be=O.fenceSync(O.SYNC_GPU_COMMANDS_COMPLETE,0);return O.flush(),await yp(O,be,4),O.bindBuffer(O.PIXEL_PACK_BUFFER,Lt),O.getBufferSubData(O.PIXEL_PACK_BUFFER,0,Mt),O.deleteBuffer(Lt),O.deleteSync(be),Mt}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(b,k=null,Y=0){const H=Math.pow(2,-Y),G=Math.floor(b.image.width*H),Mt=Math.floor(b.image.height*H),wt=k!==null?k.x:0,yt=k!==null?k.y:0;J.setTexture2D(b,0),O.copyTexSubImage2D(O.TEXTURE_2D,Y,0,0,wt,yt,G,Mt),_.unbindTexture()},this.copyTextureToTexture=function(b,k,Y=null,H=null,G=0,Mt=0){let wt,yt,Tt,Rt,Xt,Kt,Lt,le,be;const ye=b.isCompressedTexture?b.mipmaps[Mt]:b.image;if(Y!==null)wt=Y.max.x-Y.min.x,yt=Y.max.y-Y.min.y,Tt=Y.isBox3?Y.max.z-Y.min.z:1,Rt=Y.min.x,Xt=Y.min.y,Kt=Y.isBox3?Y.min.z:0;else{const Se=Math.pow(2,-G);wt=Math.floor(ye.width*Se),yt=Math.floor(ye.height*Se),b.isDataArrayTexture?Tt=ye.depth:b.isData3DTexture?Tt=Math.floor(ye.depth*Se):Tt=1,Rt=0,Xt=0,Kt=0}H!==null?(Lt=H.x,le=H.y,be=H.z):(Lt=0,le=0,be=0);const ue=gt.convert(k.format),Ge=gt.convert(k.type);let St;k.isData3DTexture?(J.setTexture3D(k,0),St=O.TEXTURE_3D):k.isDataArrayTexture||k.isCompressedArrayTexture?(J.setTexture2DArray(k,0),St=O.TEXTURE_2D_ARRAY):(J.setTexture2D(k,0),St=O.TEXTURE_2D),_.activeTexture(O.TEXTURE0),_.pixelStorei(O.UNPACK_FLIP_Y_WEBGL,k.flipY),_.pixelStorei(O.UNPACK_PREMULTIPLY_ALPHA_WEBGL,k.premultiplyAlpha),_.pixelStorei(O.UNPACK_ALIGNMENT,k.unpackAlignment);const sn=_.getParameter(O.UNPACK_ROW_LENGTH),ie=_.getParameter(O.UNPACK_IMAGE_HEIGHT),dn=_.getParameter(O.UNPACK_SKIP_PIXELS),Bn=_.getParameter(O.UNPACK_SKIP_ROWS),Si=_.getParameter(O.UNPACK_SKIP_IMAGES);_.pixelStorei(O.UNPACK_ROW_LENGTH,ye.width),_.pixelStorei(O.UNPACK_IMAGE_HEIGHT,ye.height),_.pixelStorei(O.UNPACK_SKIP_PIXELS,Rt),_.pixelStorei(O.UNPACK_SKIP_ROWS,Xt),_.pixelStorei(O.UNPACK_SKIP_IMAGES,Kt);const Os=b.isDataArrayTexture||b.isData3DTexture,de=k.isDataArrayTexture||k.isData3DTexture;if(b.isDepthTexture){const Se=$.get(b),wi=$.get(k),pe=$.get(Se.__renderTarget),Ei=$.get(wi.__renderTarget);_.bindFramebuffer(O.READ_FRAMEBUFFER,pe.__webglFramebuffer),_.bindFramebuffer(O.DRAW_FRAMEBUFFER,Ei.__webglFramebuffer);for(let ks=0;ksMath.PI&&(n-=Ze),i<-Math.PI?i+=Ze:i>Math.PI&&(i-=Ze),n<=i?this._spherical.theta=Math.max(n,Math.min(i,this._spherical.theta)):this._spherical.theta=this._spherical.theta>(n+i)/2?Math.max(n,this._spherical.theta):Math.min(i,this._spherical.theta)),this._spherical.phi=Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle,this._spherical.phi)),this._spherical.makeSafe(),this.enableDamping===!0?this.target.addScaledVector(this._panOffset,this.dampingFactor):this.target.add(this._panOffset),this.target.sub(this.cursor),this.target.clampLength(this.minTargetRadius,this.maxTargetRadius),this.target.add(this.cursor);let r=!1;if(this.zoomToCursor&&this._performCursorZoom||this.object.isOrthographicCamera)this._spherical.radius=this._clampDistance(this._spherical.radius);else{const o=this._spherical.radius;this._spherical.radius=this._clampDistance(this._spherical.radius*this._scale),r=o!=this._spherical.radius}if(Ie.setFromSpherical(this._spherical),Ie.applyQuaternion(this._quatInverse),e.copy(this.target).add(Ie),this.object.lookAt(this.target),this.enableDamping===!0?(this._sphericalDelta.theta*=1-this.dampingFactor,this._sphericalDelta.phi*=1-this.dampingFactor,this._panOffset.multiplyScalar(1-this.dampingFactor)):(this._sphericalDelta.set(0,0,0),this._panOffset.set(0,0,0)),this.zoomToCursor&&this._performCursorZoom){let o=null;if(this.object.isPerspectiveCamera){const a=Ie.length();o=this._clampDistance(a*this._scale);const l=a-o;this.object.position.addScaledVector(this._dollyDirection,l),this.object.updateMatrixWorld(),r=!!l}else if(this.object.isOrthographicCamera){const a=new P(this._mouse.x,this._mouse.y,0);a.unproject(this.object);const l=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),this.object.updateProjectionMatrix(),r=l!==this.object.zoom;const c=new P(this._mouse.x,this._mouse.y,0);c.unproject(this.object),this.object.position.sub(c).add(a),this.object.updateMatrixWorld(),o=Ie.length()}else console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."),this.zoomToCursor=!1;o!==null&&(this.screenSpacePanning?this.target.set(0,0,-1).transformDirection(this.object.matrix).multiplyScalar(o).add(this.object.position):(yo.origin.copy(this.object.position),yo.direction.set(0,0,-1).transformDirection(this.object.matrix),Math.abs(this.object.up.dot(yo.direction))Zl||8*(1-this._lastQuaternion.dot(this.object.quaternion))>Zl||this._lastTargetPosition.distanceToSquared(this.target)>Zl?(this.dispatchEvent(Fu),this._lastPosition.copy(this.object.position),this._lastQuaternion.copy(this.object.quaternion),this._lastTargetPosition.copy(this.target),!0):!1}_getAutoRotationAngle(t){return t!==null?Ze/60*this.autoRotateSpeed*t:Ze/60/60*this.autoRotateSpeed}_getZoomScale(t){const e=Math.abs(t*.01);return Math.pow(.95,this.zoomSpeed*e)}_rotateLeft(t){this._sphericalDelta.theta-=t}_rotateUp(t){this._sphericalDelta.phi-=t}_panLeft(t,e){Ie.setFromMatrixColumn(e,0),Ie.multiplyScalar(-t),this._panOffset.add(Ie)}_panUp(t,e){this.screenSpacePanning===!0?Ie.setFromMatrixColumn(e,1):(Ie.setFromMatrixColumn(e,0),Ie.crossVectors(this.object.up,Ie)),Ie.multiplyScalar(t),this._panOffset.add(Ie)}_pan(t,e){const n=this.domElement;if(this.object.isPerspectiveCamera){const i=this.object.position;Ie.copy(i).sub(this.target);let r=Ie.length();r*=Math.tan(this.object.fov/2*Math.PI/180),this._panLeft(2*t*r/n.clientHeight,this.object.matrix),this._panUp(2*e*r/n.clientHeight,this.object.matrix)}else this.object.isOrthographicCamera?(this._panLeft(t*(this.object.right-this.object.left)/this.object.zoom/n.clientWidth,this.object.matrix),this._panUp(e*(this.object.top-this.object.bottom)/this.object.zoom/n.clientHeight,this.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),this.enablePan=!1)}_dollyOut(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale/=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_dollyIn(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale*=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_updateZoomParameters(t,e){if(!this.zoomToCursor)return;this._performCursorZoom=!0;const n=this.domElement.getBoundingClientRect(),i=t-n.left,r=e-n.top,o=n.width,a=n.height;this._mouse.x=i/o*2-1,this._mouse.y=-(r/a)*2+1,this._dollyDirection.set(this._mouse.x,this._mouse.y,1).unproject(this.object).sub(this.object.position).normalize()}_clampDistance(t){return Math.max(this.minDistance,Math.min(this.maxDistance,t))}_handleMouseDownRotate(t){this._rotateStart.set(t.clientX,t.clientY)}_handleMouseDownDolly(t){this._updateZoomParameters(t.clientX,t.clientX),this._dollyStart.set(t.clientX,t.clientY)}_handleMouseDownPan(t){this._panStart.set(t.clientX,t.clientY)}_handleMouseMoveRotate(t){this._rotateEnd.set(t.clientX,t.clientY),this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Ze*this._rotateDelta.x/e.clientHeight),this._rotateUp(Ze*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd),this.update()}_handleMouseMoveDolly(t){this._dollyEnd.set(t.clientX,t.clientY),this._dollyDelta.subVectors(this._dollyEnd,this._dollyStart),this._dollyDelta.y>0?this._dollyOut(this._getZoomScale(this._dollyDelta.y)):this._dollyDelta.y<0&&this._dollyIn(this._getZoomScale(this._dollyDelta.y)),this._dollyStart.copy(this._dollyEnd),this.update()}_handleMouseMovePan(t){this._panEnd.set(t.clientX,t.clientY),this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd),this.update()}_handleMouseWheel(t){this._updateZoomParameters(t.clientX,t.clientY),t.deltaY<0?this._dollyIn(this._getZoomScale(t.deltaY)):t.deltaY>0&&this._dollyOut(this._getZoomScale(t.deltaY)),this.update()}_handleKeyDown(t){let e=!1;switch(t.code){case this.keys.UP:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(Ze*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,this.keyPanSpeed),e=!0;break;case this.keys.BOTTOM:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(-Ze*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,-this.keyPanSpeed),e=!0;break;case this.keys.LEFT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(Ze*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(this.keyPanSpeed,0),e=!0;break;case this.keys.RIGHT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(-Ze*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(-this.keyPanSpeed,0),e=!0;break}e&&(t.preventDefault(),this.update())}_handleTouchStartRotate(t){if(this._pointers.length===1)this._rotateStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._rotateStart.set(n,i)}}_handleTouchStartPan(t){if(this._pointers.length===1)this._panStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._panStart.set(n,i)}}_handleTouchStartDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,i=t.pageY-e.y,r=Math.sqrt(n*n+i*i);this._dollyStart.set(0,r)}_handleTouchStartDollyPan(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enablePan&&this._handleTouchStartPan(t)}_handleTouchStartDollyRotate(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enableRotate&&this._handleTouchStartRotate(t)}_handleTouchMoveRotate(t){if(this._pointers.length==1)this._rotateEnd.set(t.pageX,t.pageY);else{const n=this._getSecondPointerPosition(t),i=.5*(t.pageX+n.x),r=.5*(t.pageY+n.y);this._rotateEnd.set(i,r)}this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Ze*this._rotateDelta.x/e.clientHeight),this._rotateUp(Ze*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd)}_handleTouchMovePan(t){if(this._pointers.length===1)this._panEnd.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),i=.5*(t.pageY+e.y);this._panEnd.set(n,i)}this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd)}_handleTouchMoveDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,i=t.pageY-e.y,r=Math.sqrt(n*n+i*i);this._dollyEnd.set(0,r),this._dollyDelta.set(0,Math.pow(this._dollyEnd.y/this._dollyStart.y,this.zoomSpeed)),this._dollyOut(this._dollyDelta.y),this._dollyStart.copy(this._dollyEnd);const o=(t.pageX+e.x)*.5,a=(t.pageY+e.y)*.5;this._updateZoomParameters(o,a)}_handleTouchMoveDollyPan(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enablePan&&this._handleTouchMovePan(t)}_handleTouchMoveDollyRotate(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enableRotate&&this._handleTouchMoveRotate(t)}_addPointer(t){this._pointers.push(t.pointerId)}_removePointer(t){delete this._pointerPositions[t.pointerId];for(let e=0;e[a%4*e,Math.floor(a/4)*n],o=(a,l,c,u,h=26)=>{const[d,f]=r(a);t.fillStyle=u,t.fillRect(d,f,e,n),t.fillStyle=c,t.font=`${h}px "Libertinus Mono", ui-monospace, monospace`,t.textAlign="center",l.forEach((p,v)=>t.fillText(p,d+e/2,f+n/2+(v-(l.length-1)/2)*h*1.3+h*.35)),t.textAlign="left"};o(1,["NO SIGNAL"],"#ffffff","#1432c8",30);{const[a,l]=r(2);["#c0c0c0","#c0c000","#00c0c0","#00c000","#c000c0","#c00000","#0000c0"].forEach((c,u)=>{t.fillStyle=c,t.fillRect(a+u*e/7,l,e/7+1,n*.7)}),t.fillStyle="#0b0b0b",t.fillRect(a,l+n*.7,e,n*.3)}o(3,["PRESENT DAY","PRESENT TIME"],"#e8e8e8","#050505",22);{const[a,l]=r(4);t.fillStyle="#000",t.fillRect(a,l,e,n),t.fillStyle="#b8b8b8",t.font="18px ui-monospace, monospace",t.fillText("C:\\>_",a+16,l+34)}o(5,["▶ PLAY"],"#ffffff","#0a1a7a",30),o(6,["æ"],"#f4f1ea","#000000",110),o(7,["CLOSE THE WORLD","OPEN THE nExT"],"#ff4a4a","#070000",18)}const i=new gi(s);return i.colorSpace=we,i}class __{constructor(t,e,n){R(this,"group",new xn);R(this,"screens");R(this,"uniforms",{uTime:{value:0},uAtlas:{value:null},uFogColor:{value:new Bt},uFogDensity:{value:0}});this.uniforms.uFogColor.value.copy(n.color),this.uniforms.uFogDensity.value=n.density;const i=new Un(1.26,1.1,.95,2,.05);i.translate(0,.55,-.475);const r=new Nh(i,new Ae({roughness:.6,metalness:.05}),t),o=new Xe(1,.75);o.translate(0,.62,.004),this.uniforms.uAtlas.value=x_();const a=new Pe({uniforms:this.uniforms,vertexShader:` attribute float aTile; attribute float aSeed; attribute float aOn; @@ -4156,7 +4156,7 @@ void main() { col = mix(col, uFogColor, fogK * 0.7); gl_FragColor = vec4(col, 1.0); #include - }`});this.screens=new Vc(a,o,t);const l=new Float32Array(t),c=new Float32Array(t),u=new Float32Array(t),h=[12168852,1513242,7172214,855311,9407100],d=new se,f=new Cn,p=new Hn,v=new kt;let m=0;for(;m.3)continue;const w=9+e()*13,E=Math.sin(g)*w,M=-Math.cos(g)*w,S=Math.atan2(-E,-M)+(e()-.5)*.9,b=e()<.14,R=Math.min(t-m,b?1:1+Math.floor(e()*4));let x=b?4+e()*5:0;for(let T=0;T.3)continue;const S=9+e()*13,E=Math.sin(g)*S,y=-Math.cos(g)*S,M=Math.atan2(-E,-y)+(e()-.5)*.9,w=e()<.14,C=Math.min(t-m,w?1:1+Math.floor(e()*4));let x=w?4+e()*5:0;for(let T=0;T #include } -`;function tx(s,t){return new Ce({uniforms:{map:{value:s},uTime:{value:0},uPower:{value:0},uHover:{value:0},uStatic:{value:0},uSeed:{value:t},uGain:{value:1},uTint:{value:new kt(1,1,1)}},vertexShader:Q_,fragmentShader:j_})}function ex(s,t,e){const n=new We(s,t,24,18),i=n.attributes.position;for(let r=0;r{e&&(r?e.putImageData(r,0,0):r=e.getImageData(0,0,256,56),e.fillStyle="#16161c",e.font=i,e.textAlign="center",e.textBaseline="middle",e.save(),e.translate(128,31),e.rotate(-.02),e.fillText(s,0,0),e.restore())},()=>n.needsUpdate=!0),n.colorSpace=Se,n.anisotropy=4,n}class rx{constructor(t){I(this,"group",new pn);I(this,"texture");I(this,"crt");I(this,"glass");I(this,"hit");I(this,"height");I(this,"width");I(this,"depth");I(this,"screenH");I(this,"screenLocal");I(this,"portLocal");I(this,"topLocal");I(this,"led");const e=t.screenW,n=e*.75;this.screenH=n;const i=e*1.26,r=n+e*.34,a=e*.2;this.width=i,this.height=r+(t.stand?e*.08:0);const o=t.stand?e*.08:0,l=nx(t.style),c=new Bt(new Dn(i,r,a,3,e*.045),l);c.position.set(0,o+r/2,-a/2),this.group.add(c);const u=e*.62,h=new Bt(new Dn(i*.84,r*.86,u,3,e*.08),l);h.position.set(0,o+r*.52,-a-u/2+e*.04),this.group.add(h);const d=e*.3,f=new Bt(new Dn(i*.46,r*.46,d,2,e*.05),l);if(f.position.set(0,o+r*.54,-a-u-d/2+e*.1),this.group.add(f),this.depth=a+u+d-e*.14,t.stand){const g=new Bt(new ci(e*.34,e*.4,o,24),l);g.position.set(0,o/2,-a-u*.4),this.group.add(g)}const p=o+r/2+e*.07;this.screenLocal=new C(0,p,.02),this.portLocal=new C(e*.1,o+r*.25,-this.depth+e*.05),this.topLocal=new C(0,o+r,-a-u*.45);const v=new Bt(new We(e*1.05,n*1.05),ix);v.position.set(0,p,.002),this.group.add(v),this.texture=new li(t.screen),this.texture.colorSpace=Se,this.texture.minFilter=Ue,this.texture.generateMipmaps=!1,this.crt=tx(this.texture,t.seed),this.glass=new Bt(ex(e,n,e*.035),this.crt),this.glass.position.set(0,p,.004),this.group.add(this.glass);const m=new Bt(new We(e*.4,e*.088),new Ee({map:sx(t.label),transparent:!0,roughness:.9}));if(m.position.set(-e*.18,o+e*.075,.003),m.rotation.z=t.seed%7*.012-.03,this.group.add(m),this.led=new Bt(new Ws(e*.014,8,6),new xn({color:1714714})),this.led.position.set(i/2-e*.12,o+e*.08,.004),this.group.add(this.led),t.style==="tv"||t.style==="grey"){const g=new Ee({color:2763310,roughness:.4});for(let w=0;w<2;w++){const E=new Bt(new ci(e*.028,e*.028,e*.03,16),g);E.rotation.x=Math.PI/2,E.position.set(i/2-e*.24-w*e*.09,o+e*.08,.012),this.group.add(E)}}this.hit=new Bt(new tn(i,r,this.depth),new xn({visible:!1})),this.hit.position.set(0,o+r/2,-this.depth/2),this.group.add(this.hit)}setLed(t,e){this.led.material.color.set(t?e:"#1a2a1a")}world(t){return this.group.updateMatrixWorld(!0),t.clone().applyMatrix4(this.group.matrixWorld)}normal(){const t=new Cn;return this.group.getWorldQuaternion(t),new C(0,0,1).applyQuaternion(t)}}const jr={name:"CopyShader",uniforms:{tDiffuse:{value:null},opacity:{value:1}},vertexShader:` +`;function b_(s,t){return new Pe({uniforms:{map:{value:s},uTime:{value:0},uPower:{value:0},uHover:{value:0},uStatic:{value:0},uSeed:{value:t},uGain:{value:1},uTint:{value:new Bt(1,1,1)}},vertexShader:y_,fragmentShader:M_})}function S_(s,t,e){const n=new Xe(s,t,24,18),i=n.attributes.position;for(let r=0;r{e&&(r?e.putImageData(r,0,0):r=e.getImageData(0,0,256,56),e.fillStyle="#16161c",e.font=i,e.textAlign="center",e.textBaseline="middle",e.save(),e.translate(128,31),e.rotate(-.02),e.fillText(s,0,0),e.restore())},()=>n.needsUpdate=!0),n.colorSpace=we,n.anisotropy=4,n}class A_{constructor(t){R(this,"group",new xn);R(this,"texture");R(this,"crt");R(this,"glass");R(this,"hit");R(this,"height");R(this,"width");R(this,"depth");R(this,"screenH");R(this,"screenLocal");R(this,"portLocal");R(this,"topLocal");R(this,"led");const e=t.screenW,n=e*.75;this.screenH=n;const i=e*1.26,r=n+e*.34,o=e*.2;this.width=i,this.height=r+(t.stand?e*.08:0);const a=t.stand?e*.08:0,l=w_(t.style),c=new zt(new Un(i,r,o,3,e*.045),l);c.position.set(0,a+r/2,-o/2),this.group.add(c);const u=e*.62,h=new zt(new Un(i*.84,r*.86,u,3,e*.08),l);h.position.set(0,a+r*.52,-o-u/2+e*.04),this.group.add(h);const d=e*.3,f=new zt(new Un(i*.46,r*.46,d,2,e*.05),l);if(f.position.set(0,a+r*.54,-o-u-d/2+e*.1),this.group.add(f),this.depth=o+u+d-e*.14,t.stand){const g=new zt(new vi(e*.34,e*.4,a,24),l);g.position.set(0,a/2,-o-u*.4),this.group.add(g)}const p=a+r/2+e*.07;this.screenLocal=new P(0,p,.02),this.portLocal=new P(e*.1,a+r*.25,-this.depth+e*.05),this.topLocal=new P(0,a+r,-o-u*.45);const v=new zt(new Xe(e*1.05,n*1.05),E_);v.position.set(0,p,.002),this.group.add(v),this.texture=new gi(t.screen),this.texture.colorSpace=we,this.texture.minFilter=Oe,this.texture.generateMipmaps=!1,this.crt=b_(this.texture,t.seed),this.glass=new zt(S_(e,n,e*.035),this.crt),this.glass.position.set(0,p,.004),this.group.add(this.glass);const m=new zt(new Xe(e*.4,e*.088),new Ae({map:T_(t.label),transparent:!0,roughness:.9}));if(m.position.set(-e*.18,a+e*.075,.003),m.rotation.z=t.seed%7*.012-.03,this.group.add(m),this.led=new zt(new lr(e*.014,8,6),new bn({color:1714714})),this.led.position.set(i/2-e*.12,a+e*.08,.004),this.group.add(this.led),t.style==="tv"||t.style==="grey"){const g=new Ae({color:2763310,roughness:.4});for(let S=0;S<2;S++){const E=new zt(new vi(e*.028,e*.028,e*.03,16),g);E.rotation.x=Math.PI/2,E.position.set(i/2-e*.24-S*e*.09,a+e*.08,.012),this.group.add(E)}}this.hit=new zt(new nn(i,r,this.depth),new bn({visible:!1})),this.hit.position.set(0,a+r/2,-this.depth/2),this.group.add(this.hit)}setLed(t,e){this.led.material.color.set(t?e:"#1a2a1a")}world(t){return this.group.updateMatrixWorld(!0),t.clone().applyMatrix4(this.group.matrixWorld)}normal(){const t=new Ln;return this.group.getWorldQuaternion(t),new P(0,0,1).applyQuaternion(t)}}const Mo={name:"CopyShader",uniforms:{tDiffuse:{value:null},opacity:{value:1}},vertexShader:` varying vec2 vUv; @@ -4255,7 +4255,7 @@ void main() { gl_FragColor = opacity * texel; - }`};class _s{constructor(){this.isPass=!0,this.enabled=!0,this.needsSwap=!0,this.clear=!1,this.renderToScreen=!1}setSize(){}render(){console.error("THREE.Pass: .render() must be implemented in derived pass.")}dispose(){}}const ax=new Xr(-1,1,1,-1,0,1);class ox extends Be{constructor(){super(),this.setAttribute("position",new ve([-1,3,0,-1,-1,0,3,-1,0],3)),this.setAttribute("uv",new ve([0,2,0,0,2,0],2))}}const lx=new ox;class Ml{constructor(t){this._mesh=new Bt(lx,t)}dispose(){this._mesh.geometry.dispose()}render(t){t.render(this._mesh,ax)}get material(){return this._mesh.material}set material(t){this._mesh.material=t}}class Zh extends _s{constructor(t,e="tDiffuse"){super(),this.textureID=e,this.uniforms=null,this.material=null,t instanceof Ce?(this.uniforms=t.uniforms,this.material=t):t&&(this.uniforms=$s.clone(t.uniforms),this.material=new Ce({name:t.name!==void 0?t.name:"unspecified",defines:Object.assign({},t.defines),uniforms:this.uniforms,vertexShader:t.vertexShader,fragmentShader:t.fragmentShader})),this._fsQuad=new Ml(this.material)}render(t,e,n){this.uniforms[this.textureID]&&(this.uniforms[this.textureID].value=n.texture),this._fsQuad.material=this.material,this.renderToScreen?(t.setRenderTarget(null),this._fsQuad.render(t)):(t.setRenderTarget(e),this.clear&&t.clear(t.autoClearColor,t.autoClearDepth,t.autoClearStencil),this._fsQuad.render(t))}dispose(){this.material.dispose(),this._fsQuad.dispose()}}class Jh extends _s{constructor(t,e){super(),this.scene=t,this.camera=e,this.clear=!0,this.needsSwap=!1,this.inverse=!1}render(t,e,n){const i=t.getContext(),r=t.state;r.buffers.color.setMask(!1),r.buffers.depth.setMask(!1),r.buffers.color.setLocked(!0),r.buffers.depth.setLocked(!0);let a,o;this.inverse?(a=0,o=1):(a=1,o=0),r.buffers.stencil.setTest(!0),r.buffers.stencil.setOp(i.REPLACE,i.REPLACE,i.REPLACE),r.buffers.stencil.setFunc(i.ALWAYS,a,4294967295),r.buffers.stencil.setClear(o),r.buffers.stencil.setLocked(!0),t.setRenderTarget(n),this.clear&&t.clear(),t.render(this.scene,this.camera),t.setRenderTarget(e),this.clear&&t.clear(),t.render(this.scene,this.camera),r.buffers.color.setLocked(!1),r.buffers.depth.setLocked(!1),r.buffers.color.setMask(!0),r.buffers.depth.setMask(!0),r.buffers.stencil.setLocked(!1),r.buffers.stencil.setFunc(i.EQUAL,1,4294967295),r.buffers.stencil.setOp(i.KEEP,i.KEEP,i.KEEP),r.buffers.stencil.setLocked(!0)}}class cx extends _s{constructor(){super(),this.needsSwap=!1}render(t){t.state.buffers.stencil.setLocked(!1),t.state.buffers.stencil.setTest(!1)}}class hx{constructor(t,e){if(this.renderer=t,this._pixelRatio=t.getPixelRatio(),e===void 0){const n=t.getSize(new ht);this._width=n.width,this._height=n.height,e=new Xe(this._width*this._pixelRatio,this._height*this._pixelRatio,{type:Je}),e.texture.name="EffectComposer.rt1"}else this._width=e.width,this._height=e.height;this.renderTarget1=e,this.renderTarget2=e.clone(),this.renderTarget2.texture.name="EffectComposer.rt2",this.writeBuffer=this.renderTarget1,this.readBuffer=this.renderTarget2,this.renderToScreen=!0,this.passes=[],this.copyPass=new Zh(jr),this.copyPass.material.blending=wn,this.timer=new pp}swapBuffers(){const t=this.readBuffer;this.readBuffer=this.writeBuffer,this.writeBuffer=t}addPass(t){this.passes.push(t),t.setSize(this._width*this._pixelRatio,this._height*this._pixelRatio)}insertPass(t,e){this.passes.splice(e,0,t),t.setSize(this._width*this._pixelRatio,this._height*this._pixelRatio)}removePass(t){const e=this.passes.indexOf(t);e!==-1&&this.passes.splice(e,1)}isLastEnabledPass(t){for(let e=t+1;e{n.fillStyle=h?i:r,n.fillText(c,u,24)};if(o("VHS",18,!0),o("HQ",62,!0),o("▶ PLAY",100,a),o("REC",180,!1),o("CH",226,a),o("PM",470,!a),this.marquee&&t-this.marqueeAt<2+this.marquee.length*.22){const c=512-(t-this.marqueeAt)*150;n.fillStyle=i,n.font='bold 50px ui-monospace, "Courier New", monospace',n.fillText(this.marquee.toUpperCase(),c,92)}else if(a)this.digits(String(this.channel).padStart(2,"0"),228,36,i,r),n.fillStyle=i,n.font="bold 40px ui-monospace, monospace",n.fillText("▶",360,88);else{const c=Math.floor(t*1.4)%2===0;this.digits("1200",150,36,c?i:r,r,!0,c)}n.shadowBlur=0,this.texture.needsUpdate=!0}digits(t,e,n,i,r,a=!1,o=!0){const l=this.ctx,c=34,u=60;if([...t].forEach((h,d)=>{const f=e+d*(c+16)+(a&&d>=2?22:0),p=gx[h]??"",v=(m,g,w,E,M)=>{l.strokeStyle=p.includes(m)?i:r,l.lineWidth=7,l.beginPath(),l.moveTo(f+g,n+w),l.lineTo(f+E,n+M),l.stroke()};v("a",5,0,c-5,0),v("b",c,5,c,u/2-5),v("c",c,u/2+5,c,u-5),v("d",5,u,c-5,u),v("e",0,u/2+5,0,u-5),v("f",0,5,0,u/2-5),v("g",5,u/2,c-5,u/2)}),a){l.fillStyle=o?i:r;const h=e+2*(c+16)+2;l.fillRect(h,n+16,7,7),l.fillRect(h,n+40,7,7)}}}function Qh(s){const t=document.createElement("canvas");t.width=512,t.height=160;const e=t.getContext("2d");if(e){e.fillStyle="#f1ede2",e.fillRect(0,0,512,160),e.fillStyle="#c0392b",e.fillRect(0,0,512,16),e.fillStyle="#9aa1b0";for(let o=40;o<160;o+=30)e.fillRect(16,o+20,480,1)}const n=new li(t),i=`54px ${oa}`,r='18px "Libertinus Mono", monospace';let a=null;return la([i,r],()=>{e&&(a?e.putImageData(a,0,0):a=e.getImageData(0,0,512,160),e.font=r,e.fillStyle="#6b6f7a",e.textAlign="right",e.textBaseline="alphabetic",e.fillText("T-120 SP",496,142),e.fillStyle="#15151a",e.font=i,e.textAlign="left",e.textBaseline="middle",e.fillText(s,26,94,470))},()=>n.needsUpdate=!0),n.colorSpace=Se,n.anisotropy=4,n}const yl=1.3,Ui=.27,ea=.95,ln=.66;class xx{constructor(){I(this,"group",new pn);I(this,"hit");I(this,"vfd",new _x);I(this,"rearZ");I(this,"rearY");I(this,"flap");I(this,"tape");I(this,"tapeLabel");I(this,"restPose",{pos:new C,rotY:0});I(this,"slotPose",{pos:new C,rotY:0});I(this,"insidePose",{pos:new C,rotY:0});I(this,"state","rest");I(this,"anim",null);I(this,"queue",[]);I(this,"currentLabel","~");const t=new Ee({color:723725,roughness:.7,metalness:.1}),e=new Bt(new Dn(1.9,ln,1.6,2,.02),t);e.position.set(0,ln/2,.15),this.group.add(e);const n=new Bt(new tn(1.92,.006,1.62),new xn({color:11735583}));n.position.set(0,ln-.03,.15),this.group.add(n);const i=new pn;i.position.set(0,ln+Ui/2+.012,-.12),this.group.add(i);const r=new Ee({color:2500396,roughness:.42,metalness:.12});i.add(new Bt(new Dn(yl,Ui,ea,3,.018),r));const a=new Ee({color:6974837,roughness:.34,metalness:.2}),o=new Bt(new tn(yl-.04,.004,ea-.04),a);o.position.y=Ui/2+.001,i.add(o);for(const[x,T]of[[-.55,.38],[.55,.38],[-.55,-.38],[.55,-.38]]){const P=new Bt(new ci(.035,.04,.012,12),t);P.position.set(x,-Ui/2-.006,T),i.add(P)}const l=ea/2+.001,c=new Bt(new We(yl-.03,Ui-.03),new Ee({map:this.panelTexture(),roughness:.5,metalness:.08}));c.position.z=l,i.add(c);const u=.58,h=.095,d=-.21,f=.035,p=new Bt(new We(u,h),new xn({color:65793}));p.position.set(d,f,l+.001),i.add(p);const v=new pn;v.position.set(d,f+h/2,l+.004),i.add(v),this.flap=new Bt(new tn(u-.01,h-.006,.006),new Ee({color:1710879,roughness:.35,metalness:.1})),this.flap.position.y=-h/2,v.add(this.flap);const m=new Bt(new We(.4,.0875),new xn({map:this.vfd.texture,toneMapped:!1}));m.position.set(.36,.045,l+.002),i.add(m);const g=new Ee({color:3816258,roughness:.4,metalness:.1}),w=new Ee({color:9049376,roughness:.4});for(let x=0;x<6;x++){const T=new Bt(new Dn(.058,.024,.02,2,.006),x===5?w:g);T.position.set(.19+x*.075,-.075,l+.006),i.add(T)}const E=new Bt(new ci(.022,.022,.02,20),g);E.rotation.x=Math.PI/2,E.position.set(-.57,.04,l+.008),i.add(E);const M=new Bt(new Ws(.006,8,6),new xn({color:16724016}));M.position.set(-.57,-.01,l+.004),i.add(M),this.rearZ=i.position.z-ea/2,this.rearY=i.position.y,this.tape=new pn;const S=new Bt(new Dn(.54,.07,.3,2,.01),new Ee({color:789518,roughness:.45,metalness:.2}));this.tape.add(S);const b=new Bt(new We(.2,.06),new Ee({color:2761504,roughness:.1,metalness:.3}));b.rotation.x=-Math.PI/2,b.position.set(0,.0355,-.05),this.tape.add(b),this.tapeLabel=new Ee({map:Qh("~"),roughness:.8});const R=new Bt(new We(.46,.1),this.tapeLabel);R.rotation.x=-Math.PI/2,R.position.set(0,.036,.085),this.tape.add(R),this.group.add(this.tape),this.restPose={pos:new C(-.38,ln+.036,.66),rotY:.32},this.slotPose={pos:new C(d,i.position.y+f,i.position.z+l+.34),rotY:0},this.insidePose={pos:new C(d,i.position.y+f,i.position.z+l-.36),rotY:0},this.applyPose(this.restPose,this.restPose,0),this.hit=new Bt(new tn(1.9,ln+Ui+.1,1.6),new xn({visible:!1})),this.hit.position.set(0,(ln+Ui+.1)/2,.15),this.group.add(this.hit)}panelTexture(){const t=document.createElement("canvas");t.width=1024,t.height=200;const e=t.getContext("2d");if(e){const a=e.createLinearGradient(0,0,0,200);a.addColorStop(0,"#1f2024"),a.addColorStop(1,"#131417"),e.fillStyle=a,e.fillRect(0,0,1024,200),e.fillStyle="#3a3c43";for(let l=0;l<1024;l+=3)e.fillRect(l,0,1,200);e.globalAlpha=.9,e.font="13px ui-monospace, monospace",e.fillStyle="#8b8f99",e.fillText("VIDEO CASSETTE RECORDER · 4 HEAD HI-FI · HQ",150,172),e.fillText("POWER",16,26),["EJECT","REW","PLAY","FF","STOP","REC"].forEach((l,c)=>e.fillText(l,632+c*58.5,184))}const n=new li(t),i='30px "Libertinus Mono", monospace';let r=null;return la(i,()=>{e&&(r?e.putImageData(r,0,0):r=e.getImageData(0,0,1024,200),e.globalAlpha=.9,e.fillStyle="#c9ccd4",e.font=i,e.textAlign="left",e.textBaseline="alphabetic",e.fillText("æthera",26,176),e.globalAlpha=1)},()=>n.needsUpdate=!0),n.colorSpace=Se,n.anisotropy=4,n}applyPose(t,e,n){this.tape.position.lerpVectors(t.pos,e.pos,n),this.tape.rotation.y=t.rotY+(e.rotY-t.rotY)*n}load(t){var e;this.queue=[],(this.state==="in"||((e=this.anim)==null?void 0:e.to)==="in")&&this.queue.push({to:"rest",label:this.currentLabel}),t&&this.queue.push({to:"in",label:t})}update(t){var a;if(!this.anim&&this.queue.length){const o=this.queue.shift();if(o&&o.to!==this.state){if(o.to==="in"){this.currentLabel=o.label;const l=this.tapeLabel.map;this.tapeLabel.map=Qh(o.label),this.tapeLabel.needsUpdate=!0,l==null||l.dispose()}this.anim={from:this.state,to:o.to,k:0,label:o.label}}}const e=this.anim;if(!e)return;e.k=Math.min(1,e.k+t/(e.to==="in"?1.3:.9));const n=e.to==="in"?e.k:1-e.k,i=o=>o*o*(3-2*o);if(n<.45){const o=i(n/.45);this.applyPose(this.restPose,this.slotPose,o),this.tape.position.y+=Math.sin(o*Math.PI)*.18}else this.applyPose(this.slotPose,this.insidePose,i(Math.min(1,(n-.5)/.4))*(n>.5?1:0));const r=n<.4?0:n<.5?(n-.4)/.1:n<.85?1:1-(n-.85)/.15;(a=this.flap.parent)==null||a.rotation.set(r*1.25,0,0),e.k>=1&&(this.state=e.to,this.anim=null)}}const vx={color:723724,roughness:.55,metalness:0};class Mx{constructor(t,e,n){I(this,"mesh");I(this,"uniforms",{uPulseColor:{value:new kt},uPulseTime:{value:0},uPulseGain:{value:.35},uPulseCount:{value:3},uPulseSpeed:{value:.35}});I(this,"base",.35);I(this,"surgeLeft",0);I(this,"lit",0);const i=new jc(t,!1,"centripetal",.5),r=i.getLength(),a=new sl(i,Math.max(40,Math.round(r*18)),e,6,!1),o=a.attributes.uv,l=new Float32Array(o.count);for(let u=0;u{Object.assign(u.uniforms,this.uniforms),u.vertexShader=u.vertexShader.replace("#include ",`#include + }`};class O_{constructor(t,e,n){R(this,"composer");R(this,"bloom");R(this,"wired");R(this,"tear",0);this.composer=new D_(t),this.composer.addPass(new N_(e,n)),this.bloom=new Is(new ft(256,256),.7,.5,.78),this.composer.addPass(this.bloom),this.wired=new Hu(F_),this.composer.addPass(this.wired),this.composer.addPass(new I_)}get u(){return this.wired.uniforms}setSize(t,e,n){this.composer.setPixelRatio(n),this.composer.setSize(t,e),this.u.uRes.value.set(t*n,e*n),this.u.uDot.value=Math.max(2.5,3*n)}kick(t=1){this.tear=Math.max(this.tear,t)}setHalftone(t){this.u.uHalftone.value=t?1:0}render(t,e){this.tear=Math.max(0,this.tear-e*2.2),this.u.uTime.value=t,this.u.uTear.value=this.tear*this.tear,this.composer.render(e)}}const k_={0:"abcdef",1:"bc",2:"abged",3:"abgcd",4:"fgbc",5:"afgcd",6:"afgedc",7:"abc",8:"abcdefg",9:"abcfgd","-":"g"," ":""};class B_{constructor(){R(this,"canvas",document.createElement("canvas"));R(this,"texture");R(this,"ctx");R(this,"mode","clock");R(this,"channel",0);R(this,"marquee","");R(this,"marqueeAt",0);R(this,"acc",1);R(this,"glow",1);this.canvas.width=512,this.canvas.height=112;const t=this.canvas.getContext("2d");if(!t)throw new Error("oikos: no 2d context");this.ctx=t,this.texture=new gi(this.canvas),this.texture.colorSpace=we}scroll(t,e){this.marquee=t,this.marqueeAt=e}play(t,e,n){this.mode="play",this.channel=t,this.scroll(e,n)}stop(){this.mode="clock"}update(t,e){if(this.acc+=e,this.acc<1/12)return;this.acc=0;const n=this.ctx;n.fillStyle="#020807",n.fillRect(0,0,512,112);const i=`rgba(127,245,225,${.85*this.glow+.15})`,r="rgba(127,245,225,0.07)";n.shadowColor="#7ff5e1",n.shadowBlur=10*this.glow,n.lineCap="round";const o=this.mode==="play";n.font="bold 15px ui-monospace, monospace";const a=(c,u,h)=>{n.fillStyle=h?i:r,n.fillText(c,u,24)};if(a("VHS",18,!0),a("HQ",62,!0),a("▶ PLAY",100,o),a("REC",180,!1),a("CH",226,o),a("PM",470,!o),this.marquee&&t-this.marqueeAt<2+this.marquee.length*.22){const c=512-(t-this.marqueeAt)*150;n.fillStyle=i,n.font='bold 50px ui-monospace, "Courier New", monospace',n.fillText(this.marquee.toUpperCase(),c,92)}else if(o)this.digits(String(this.channel).padStart(2,"0"),228,36,i,r),n.fillStyle=i,n.font="bold 40px ui-monospace, monospace",n.fillText("▶",360,88);else{const c=Math.floor(t*1.4)%2===0;this.digits("1200",150,36,c?i:r,r,!0,c)}n.shadowBlur=0,this.texture.needsUpdate=!0}digits(t,e,n,i,r,o=!1,a=!0){const l=this.ctx,c=34,u=60;if([...t].forEach((h,d)=>{const f=e+d*(c+16)+(o&&d>=2?22:0),p=k_[h]??"",v=(m,g,S,E,y)=>{l.strokeStyle=p.includes(m)?i:r,l.lineWidth=7,l.beginPath(),l.moveTo(f+g,n+S),l.lineTo(f+E,n+y),l.stroke()};v("a",5,0,c-5,0),v("b",c,5,c,u/2-5),v("c",c,u/2+5,c,u-5),v("d",5,u,c-5,u),v("e",0,u/2+5,0,u-5),v("f",0,5,0,u/2-5),v("g",5,u/2,c-5,u/2)}),o){l.fillStyle=a?i:r;const h=e+2*(c+16)+2;l.fillRect(h,n+16,7,7),l.fillRect(h,n+40,7,7)}}}function Vu(s){const t=document.createElement("canvas");t.width=512,t.height=160;const e=t.getContext("2d");if(e){e.fillStyle="#f1ede2",e.fillRect(0,0,512,160),e.fillStyle="#c0392b",e.fillRect(0,0,512,16),e.fillStyle="#9aa1b0";for(let a=40;a<160;a+=30)e.fillRect(16,a+20,480,1)}const n=new gi(t),i=`54px ${Bo}`,r='18px "Libertinus Mono", monospace';let o=null;return zo([i,r],()=>{e&&(o?e.putImageData(o,0,0):o=e.getImageData(0,0,512,160),e.font=r,e.fillStyle="#6b6f7a",e.textAlign="right",e.textBaseline="alphabetic",e.fillText("T-120 SP",496,142),e.fillStyle="#15151a",e.font=i,e.textAlign="left",e.textBaseline="middle",e.fillText(s,26,94,470))},()=>n.needsUpdate=!0),n.colorSpace=we,n.anisotropy=4,n}const jl=1.3,Wi=.27,So=.95,un=.66;class z_{constructor(){R(this,"group",new xn);R(this,"hit");R(this,"vfd",new B_);R(this,"rearZ");R(this,"rearY");R(this,"flap");R(this,"tape");R(this,"tapeLabel");R(this,"restPose",{pos:new P,rotY:0});R(this,"slotPose",{pos:new P,rotY:0});R(this,"insidePose",{pos:new P,rotY:0});R(this,"state","rest");R(this,"anim",null);R(this,"queue",[]);R(this,"currentLabel","~");const t=new Ae({color:723725,roughness:.7,metalness:.1}),e=new zt(new Un(1.9,un,1.6,2,.02),t);e.position.set(0,un/2,.15),this.group.add(e);const n=new zt(new nn(1.92,.006,1.62),new bn({color:11735583}));n.position.set(0,un-.03,.15),this.group.add(n);const i=new xn;i.position.set(0,un+Wi/2+.012,-.12),this.group.add(i);const r=new Ae({color:2500396,roughness:.42,metalness:.12});i.add(new zt(new Un(jl,Wi,So,3,.018),r));const o=new Ae({color:6974837,roughness:.34,metalness:.2}),a=new zt(new nn(jl-.04,.004,So-.04),o);a.position.y=Wi/2+.001,i.add(a);for(const[x,T]of[[-.55,.38],[.55,.38],[-.55,-.38],[.55,-.38]]){const L=new zt(new vi(.035,.04,.012,12),t);L.position.set(x,-Wi/2-.006,T),i.add(L)}const l=So/2+.001,c=new zt(new Xe(jl-.03,Wi-.03),new Ae({map:this.panelTexture(),roughness:.5,metalness:.08}));c.position.z=l,i.add(c);const u=.58,h=.095,d=-.21,f=.035,p=new zt(new Xe(u,h),new bn({color:65793}));p.position.set(d,f,l+.001),i.add(p);const v=new xn;v.position.set(d,f+h/2,l+.004),i.add(v),this.flap=new zt(new nn(u-.01,h-.006,.006),new Ae({color:1710879,roughness:.35,metalness:.1})),this.flap.position.y=-h/2,v.add(this.flap);const m=new zt(new Xe(.4,.0875),new bn({map:this.vfd.texture,toneMapped:!1}));m.position.set(.36,.045,l+.002),i.add(m);const g=new Ae({color:3816258,roughness:.4,metalness:.1}),S=new Ae({color:9049376,roughness:.4});for(let x=0;x<6;x++){const T=new zt(new Un(.058,.024,.02,2,.006),x===5?S:g);T.position.set(.19+x*.075,-.075,l+.006),i.add(T)}const E=new zt(new vi(.022,.022,.02,20),g);E.rotation.x=Math.PI/2,E.position.set(-.57,.04,l+.008),i.add(E);const y=new zt(new lr(.006,8,6),new bn({color:16724016}));y.position.set(-.57,-.01,l+.004),i.add(y),this.rearZ=i.position.z-So/2,this.rearY=i.position.y,this.tape=new xn;const M=new zt(new Un(.54,.07,.3,2,.01),new Ae({color:789518,roughness:.45,metalness:.2}));this.tape.add(M);const w=new zt(new Xe(.2,.06),new Ae({color:2761504,roughness:.1,metalness:.3}));w.rotation.x=-Math.PI/2,w.position.set(0,.0355,-.05),this.tape.add(w),this.tapeLabel=new Ae({map:Vu("~"),roughness:.8});const C=new zt(new Xe(.46,.1),this.tapeLabel);C.rotation.x=-Math.PI/2,C.position.set(0,.036,.085),this.tape.add(C),this.group.add(this.tape),this.restPose={pos:new P(-.38,un+.036,.66),rotY:.32},this.slotPose={pos:new P(d,i.position.y+f,i.position.z+l+.34),rotY:0},this.insidePose={pos:new P(d,i.position.y+f,i.position.z+l-.36),rotY:0},this.applyPose(this.restPose,this.restPose,0),this.hit=new zt(new nn(1.9,un+Wi+.1,1.6),new bn({visible:!1})),this.hit.position.set(0,(un+Wi+.1)/2,.15),this.group.add(this.hit)}panelTexture(){const t=document.createElement("canvas");t.width=1024,t.height=200;const e=t.getContext("2d");if(e){const o=e.createLinearGradient(0,0,0,200);o.addColorStop(0,"#1f2024"),o.addColorStop(1,"#131417"),e.fillStyle=o,e.fillRect(0,0,1024,200),e.fillStyle="#3a3c43";for(let l=0;l<1024;l+=3)e.fillRect(l,0,1,200);e.globalAlpha=.9,e.font="13px ui-monospace, monospace",e.fillStyle="#8b8f99",e.fillText("VIDEO CASSETTE RECORDER · 4 HEAD HI-FI · HQ",150,172),e.fillText("POWER",16,26),["EJECT","REW","PLAY","FF","STOP","REC"].forEach((l,c)=>e.fillText(l,632+c*58.5,184))}const n=new gi(t),i='30px "Libertinus Mono", monospace';let r=null;return zo(i,()=>{e&&(r?e.putImageData(r,0,0):r=e.getImageData(0,0,1024,200),e.globalAlpha=.9,e.fillStyle="#c9ccd4",e.font=i,e.textAlign="left",e.textBaseline="alphabetic",e.fillText("æthera",26,176),e.globalAlpha=1)},()=>n.needsUpdate=!0),n.colorSpace=we,n.anisotropy=4,n}applyPose(t,e,n){this.tape.position.lerpVectors(t.pos,e.pos,n),this.tape.rotation.y=t.rotY+(e.rotY-t.rotY)*n}load(t){var e;this.queue=[],(this.state==="in"||((e=this.anim)==null?void 0:e.to)==="in")&&this.queue.push({to:"rest",label:this.currentLabel}),t&&this.queue.push({to:"in",label:t})}update(t){var o;if(!this.anim&&this.queue.length){const a=this.queue.shift();if(a&&a.to!==this.state){if(a.to==="in"){this.currentLabel=a.label;const l=this.tapeLabel.map;this.tapeLabel.map=Vu(a.label),this.tapeLabel.needsUpdate=!0,l==null||l.dispose()}this.anim={from:this.state,to:a.to,k:0,label:a.label}}}const e=this.anim;if(!e)return;e.k=Math.min(1,e.k+t/(e.to==="in"?1.3:.9));const n=e.to==="in"?e.k:1-e.k,i=a=>a*a*(3-2*a);if(n<.45){const a=i(n/.45);this.applyPose(this.restPose,this.slotPose,a),this.tape.position.y+=Math.sin(a*Math.PI)*.18}else this.applyPose(this.slotPose,this.insidePose,i(Math.min(1,(n-.5)/.4))*(n>.5?1:0));const r=n<.4?0:n<.5?(n-.4)/.1:n<.85?1:1-(n-.85)/.15;(o=this.flap.parent)==null||o.rotation.set(r*1.25,0,0),e.k>=1&&(this.state=e.to,this.anim=null)}}const H_={color:723724,roughness:.55,metalness:0};class G_{constructor(t,e,n){R(this,"mesh");R(this,"uniforms",{uPulseColor:{value:new Bt},uPulseTime:{value:0},uPulseGain:{value:.35},uPulseCount:{value:3},uPulseSpeed:{value:.35}});R(this,"base",.35);R(this,"surgeLeft",0);R(this,"lit",0);const i=new Wh(t,!1,"centripetal",.5),r=i.getLength(),o=new Ul(i,Math.max(40,Math.round(r*18)),e,6,!1),a=o.attributes.uv,l=new Float32Array(a.count);for(let u=0;u{Object.assign(u.uniforms,this.uniforms),u.vertexShader=u.vertexShader.replace("#include ",`#include attribute float aAlong; varying float vAlong;`).replace("#include ",`#include vAlong = aAlong;`),u.fragmentShader=u.fragmentShader.replace("#include ",`#include @@ -4486,7 +4486,7 @@ uniform float uPulseTime, uPulseGain, uPulseCount, uPulseSpeed; varying float vAlong;`).replace("#include ",`#include float ph = fract(vAlong * uPulseCount + uPulseTime * uPulseSpeed); float pulse = smoothstep(0.0, 0.03, ph) * (1.0 - smoothstep(0.03, 0.14, ph)); -totalEmissiveRadiance += uPulseColor * (pulse * uPulseGain + 0.012 * uPulseGain);`)},c.customProgramCacheKey=()=>"oikos-cable",this.mesh=new Bt(a,c)}setLit(t){this.lit=t}surge(){this.surgeLeft=2.2}update(t,e){this.surgeLeft=Math.max(0,this.surgeLeft-e);const n=this.surgeLeft>0?Math.sin(this.surgeLeft/2.2*Math.PI)*5:0,i=this.base+this.lit*1.2+n;this.uniforms.uPulseGain.value+=(i-this.uniforms.uPulseGain.value)*Math.min(1,e*6),this.uniforms.uPulseTime.value=t*(1+this.lit*1.5+n*.8)}}function yx(s,t,e,n,i,r){const o=[s.clone()],l=s.clone().addScaledVector(t,.18);o.push(l);const c=new C(l.x,.022,l.z).addScaledVector(t,.22);s.y>.3&&o.push(new C(l.x,(s.y+.022)*.4,l.z).addScaledVector(t,.2)),o.push(c);const u=new C(e.x,.022,n-.14),h=Math.sign(c.x||1);c.z>n-.1&&o.push(new C(h*1.15,.022,n-.25));const d=2;for(let f=1;f<=d;f++){const p=f/(d+1),v=new C().lerpVectors(c,u,p),m=new C(u.z-c.z,0,c.x-u.x).normalize();v.addScaledVector(m,(r()-.5)*.9),v.y=.022,o.push(v)}return o.push(u),o.push(new C(e.x,i-.04,n-.035)),o.push(new C(e.x,i+.03,n+.01)),o.push(e.clone()),o}function jh(s,t,e,n=32){const i=[];for(let r=0;r<=n;r++){const a=r/n,o=new C().lerpVectors(s,t,a);o.y-=e*4*a*(1-a),i.push(o)}return i}function Sx(){var o,l,c;const s=new pn,t=new Ee({color:854795,roughness:.9}),e=new Wc({color:328708}),n=[new C(-26,0,-18),new C(-9,0,-24),new C(8,0,-23),new C(24,0,-15),new C(33,0,2)],i=12.5,r=[];n.forEach((u,h)=>{const d=new Bt(new ci(.12,.17,i,8),t);d.position.set(u.x,i/2,u.z),s.add(d);const f=n[h+1]??n[h-1]??u,p=new C().subVectors(f,u).setY(0).normalize(),v=new C(-p.z,0,p.x),m=[];for(const[g,w]of[[i-.4,1.9],[i-1.5,1.4]]){const E=new Bt(new tn(w*2,.12,.12),t);E.position.set(u.x,g,u.z),E.rotation.y=Math.atan2(-v.z,v.x),s.add(E);for(const M of[-1,-.45,.45,1])m.push(new C(u.x,g+.08,u.z).addScaledVector(v,M*w*.95))}r.push(m)});for(let u=0;u{const v=d[p];if(!v)return;const m=new Be().setFromPoints(jh(f,v,1.4+p%3*.25));s.add(new qc(m,e))})}const a=[[(o=r[1])==null?void 0:o[0],new C(-2.4,9,-6.6)],[(l=r[2])==null?void 0:l[3],new C(2.2,9,-6.1)],[(c=r[3])==null?void 0:c[1],new C(6,8.5,-3)]];for(const[u,h]of a)u&&s.add(new qc(new Be().setFromPoints(jh(u,h,2.2)),e));return s}function bx(){const s=new Ce({side:Ge,depthWrite:!1,fog:!1,uniforms:{uTime:{value:0}},vertexShader:` +totalEmissiveRadiance += uPulseColor * (pulse * uPulseGain + 0.012 * uPulseGain);`)},c.customProgramCacheKey=()=>"oikos-cable",this.mesh=new zt(o,c)}setLit(t){this.lit=t}surge(){this.surgeLeft=2.2}update(t,e){this.surgeLeft=Math.max(0,this.surgeLeft-e);const n=this.surgeLeft>0?Math.sin(this.surgeLeft/2.2*Math.PI)*5:0,i=this.base+this.lit*1.2+n;this.uniforms.uPulseGain.value+=(i-this.uniforms.uPulseGain.value)*Math.min(1,e*6),this.uniforms.uPulseTime.value=t*(1+this.lit*1.5+n*.8)}}function V_(s,t,e,n,i,r){const a=[s.clone()],l=s.clone().addScaledVector(t,.18);a.push(l);const c=new P(l.x,.022,l.z).addScaledVector(t,.22);s.y>.3&&a.push(new P(l.x,(s.y+.022)*.4,l.z).addScaledVector(t,.2)),a.push(c);const u=new P(e.x,.022,n-.14),h=Math.sign(c.x||1);c.z>n-.1&&a.push(new P(h*1.15,.022,n-.25));const d=2;for(let f=1;f<=d;f++){const p=f/(d+1),v=new P().lerpVectors(c,u,p),m=new P(u.z-c.z,0,c.x-u.x).normalize();v.addScaledVector(m,(r()-.5)*.9),v.y=.022,a.push(v)}return a.push(u),a.push(new P(e.x,i-.04,n-.035)),a.push(new P(e.x,i+.03,n+.01)),a.push(e.clone()),a}function Wu(s,t,e,n=32){const i=[];for(let r=0;r<=n;r++){const o=r/n,a=new P().lerpVectors(s,t,o);a.y-=e*4*o*(1-o),i.push(a)}return i}function W_(){var a,l,c;const s=new xn,t=new Ae({color:854795,roughness:.9}),e=new Uh({color:328708}),n=[new P(-26,0,-18),new P(-9,0,-24),new P(8,0,-23),new P(24,0,-15),new P(33,0,2)],i=12.5,r=[];n.forEach((u,h)=>{const d=new zt(new vi(.12,.17,i,8),t);d.position.set(u.x,i/2,u.z),s.add(d);const f=n[h+1]??n[h-1]??u,p=new P().subVectors(f,u).setY(0).normalize(),v=new P(-p.z,0,p.x),m=[];for(const[g,S]of[[i-.4,1.9],[i-1.5,1.4]]){const E=new zt(new nn(S*2,.12,.12),t);E.position.set(u.x,g,u.z),E.rotation.y=Math.atan2(-v.z,v.x),s.add(E);for(const y of[-1,-.45,.45,1])m.push(new P(u.x,g+.08,u.z).addScaledVector(v,y*S*.95))}r.push(m)});for(let u=0;u{const v=d[p];if(!v)return;const m=new He().setFromPoints(Wu(f,v,1.4+p%3*.25));s.add(new kh(m,e))})}const o=[[(a=r[1])==null?void 0:a[0],new P(-2.4,9,-6.6)],[(l=r[2])==null?void 0:l[3],new P(2.2,9,-6.1)],[(c=r[3])==null?void 0:c[1],new P(6,8.5,-3)]];for(const[u,h]of o)u&&s.add(new kh(new He().setFromPoints(Wu(u,h,2.2)),e));return s}function $_(){const s=new Pe({side:We,depthWrite:!1,fog:!1,uniforms:{uTime:{value:0}},vertexShader:` varying vec3 vDir; void main() { vDir = normalize(position); @@ -4506,71 +4506,71 @@ totalEmissiveRadiance += uPulseColor * (pulse * uPulseGain + 0.012 * uPulseGain) if (h < 0.0) col = ember + vec3(0.004, 0.0008, 0.0014); gl_FragColor = vec4(col, 1.0); #include - }`}),t=new Bt(new Ws(70,32,16),s);return t.renderOrder=-1,t}const wx=s=>s<.5?4*s*s*s:1-Math.pow(-2*s+2,3)/2;function Ex(){const s=document.createElement("canvas");s.width=s.height=128;const t=s.getContext("2d");if(t){const e=t.createRadialGradient(64,64,0,64,64,64);e.addColorStop(0,"rgba(255,255,255,0.9)"),e.addColorStop(.4,"rgba(255,255,255,0.3)"),e.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=e,t.fillRect(0,0,128,128)}return new li(s)}class Tx{constructor(t,e,n,i){I(this,"renderer");I(this,"scene",new Rf);I(this,"camera",new qe(45,1,.05,160));I(this,"controls");I(this,"post");I(this,"vcr",new xx);I(this,"field");I(this,"sky");I(this,"stations",new Map);I(this,"pickables",[]);I(this,"raycaster",new gp);I(this,"pointer",new ht(-9,-9));I(this,"pointerPx",{x:0,y:0});I(this,"pointerDirty",!1);I(this,"hovered",null);I(this,"focused",null);I(this,"tween",null);I(this,"playing",null);I(this,"settlePlay",null);I(this,"tuneSeq",0);I(this,"lastAspect",0);I(this,"later",[]);I(this,"clock",new _p);I(this,"t",0);I(this,"running",!1);I(this,"raf",0);I(this,"lastInput",0);I(this,"swayDir",1);I(this,"pixelRatio");I(this,"maxPixelRatio");I(this,"frameTimes",[]);I(this,"qualityStep",0);I(this,"shift",{x:0,y:0});I(this,"tuned",null);I(this,"shiftTarget",{x:0,y:0});I(this,"reduced");I(this,"lowPower");I(this,"speed",Math.min(10,Math.max(1,Number(new URLSearchParams(location.search).get("speed"))||1)));I(this,"driven",new URLSearchParams(location.search).has("drive"));I(this,"perf",{frames:0,paint:0,render:0,uploads:0});I(this,"frustum",new kr);I(this,"viewProj",new se);I(this,"kept",null);I(this,"calm",0);this.container=t,this.events=i,this.reduced=matchMedia("(prefers-reduced-motion: reduce)").matches,this.lowPower=matchMedia("(pointer: coarse)").matches||Math.min(innerWidth,innerHeight)<600,this.maxPixelRatio=Math.min(devicePixelRatio||1,this.lowPower?1.25:1.6),this.pixelRatio=this.maxPixelRatio,this.renderer=new N_({antialias:!1,powerPreference:"high-performance"}),this.renderer.setPixelRatio(this.pixelRatio),this.renderer.toneMapping=ar,this.renderer.toneMappingExposure=1.1,this.renderer.outputColorSpace=Se,this.renderer.domElement.id="oikos-gl",t.prepend(this.renderer.domElement);try{this.build(e,n)}catch(r){throw this.renderer.domElement.remove(),this.renderer.dispose(),this.renderer.forceContextLoss(),r}}build(t,e){this.scene.background=new kt(0),this.scene.fog=new Oo(new kt().setRGB(.049,.0063,.0089,Ps),.042),this.sky=bx(),this.scene.add(this.sky),this.scene.add(new ap(4866648,393988,.8));const n=new up(9082040,.35);n.position.set(-4,10,-6),this.scene.add(n);const i=new lp(14212351,9,7,.42,.65,1.6);i.position.set(.4,4.6,1.2),i.target.position.set(0,ln,.1),this.scene.add(i,i.target);const r=new ol(10466303,2.2,4.5,2);r.position.set(-.4,1.35,2.3),this.scene.add(r);const a=new ol(11735583,1.6,4,2);a.position.set(0,.25,1.4),this.scene.add(a),this.buildFloor(),this.scene.add(this.vcr.group),this.pickables.push(this.vcr.hit),this.vcr.hit.userData.pick="vcr",this.scene.add(Sx());const o=vi(1998);this.field=new J_(this.lowPower?40:90,o,this.scene.fog),this.scene.add(this.field.group),this.buildStations(t,e,o),this.controls=new F_(this.camera,this.renderer.domElement),this.controls.enableDamping=!0,this.controls.dampingFactor=.07,this.controls.enablePan=!1,this.controls.rotateSpeed=.28,this.controls.zoomSpeed=.6,this.controls.addEventListener("start",()=>this.lastInput=this.t),this.post=new mx(this.renderer,this.scene,this.camera),this.resize();const l=this.homePose();this.camera.position.copy(l.pos),this.controls.target.copy(l.target),this.applyLimits(null),this.controls.update(),this.bindPointer(),addEventListener("resize",()=>this.resize())}buildFloor(){const t=document.createElement("canvas");t.width=t.height=512;const e=t.getContext("2d");if(e){e.fillStyle="#0c0b0d",e.fillRect(0,0,512,512),e.strokeStyle="rgba(120,110,130,0.10)",e.lineWidth=2;for(let r=0;r<=512;r+=128)e.beginPath(),e.moveTo(r,0),e.lineTo(r,512),e.moveTo(0,r),e.lineTo(512,r),e.stroke();for(let r=0;r<900;r++)e.fillStyle=`rgba(${Math.random()<.3?"120,20,30":"90,90,100"},${Math.random()*.12})`,e.fillRect(Math.random()*512,Math.random()*512,2,2)}const n=new li(t);n.wrapS=n.wrapT=lr,n.repeat.set(24,24),n.colorSpace=Se,n.anisotropy=8;const i=new Bt(new We(96,96),new Ee({map:n,roughness:.82,metalness:.15}));i.rotation.x=-Math.PI/2,this.scene.add(i)}buildStations(t,e,n){const i=new Ee({color:1315086,roughness:.85}),r=new Ee({color:657930,roughness:.6}),a=Ex(),o=new C,l=new Map,c=t.map((h,d)=>({site:h,i:d,p:gd(h.id,d)}));c.sort((h,d)=>+!!h.p.on-+!!d.p.on);let u=0;for(const{site:h,p:d}of c){const f=e.get(h.id);if(!f)continue;const p=new rx({style:d.style,screenW:d.screenW,screen:f.canvas,label:h.title,accent:h.accent,seed:Math.floor(n()*1e3),stand:d.stand}),v=xr.degToRad(d.angle),m=d.on?l.get(d.on):void 0;if(m)p.group.position.copy(m.group.position),p.group.position.y+=m.height,p.group.rotation.copy(m.group.rotation),p.group.rotateY((n()-.5)*.12),p.group.translateZ(-(m.depth-p.depth)*.3);else{p.group.position.set(Math.sin(v)*d.r,d.y,-Math.cos(v)*d.r);const L=new C(0,d.hang?1.3:p.group.position.y+.6,.6);p.group.lookAt(L),d.hang||(p.group.rotation.set(0,Math.atan2(-p.group.position.x,.6-p.group.position.z),0),p.group.rotateY((n()-.5)*.1))}if(this.scene.add(p.group),l.set(h.id,p),!d.hang&&!d.on&&d.y>.01){const L=new Bt(new tn(p.width*.92,d.y,p.depth*.9),i);L.position.set(0,-d.y/2,-p.depth*.45),p.group.add(L)}if(d.hang){const L=p.world(p.topLocal),F=new Bt(new ci(.008,.008,16,5),r);F.position.set(L.x,L.y+8,L.z),this.scene.add(F)}const g=-.5+u++*.37%1;o.set(g,this.vcr.rearY-.05,this.vcr.rearZ-.01);const w=p.world(p.portLocal),E=p.normal().multiplyScalar(-1).setY(0).normalize();let M;const S=d.feeds?l.get(d.feeds):void 0;if(S){const L=S.world(S.portLocal.clone().add(new C(-.25,.15,0))),F=new C().lerpVectors(w,L,.5);F.y=Math.min(w.y,L.y)-.6,M=[w,w.clone().addScaledVector(E,.3).setY(w.y-.2),F,L.clone().add(new C(0,.3,-.3)),L]}else M=yx(w,E,o,.15-.8,ln,n);const b=new Mx(M,d.hang?.014:.02,h.accent);this.scene.add(b.mesh);const R=new Bt(new We(d.screenW*2.6,d.screenW*2.2),new xn({map:a,color:h.accent,transparent:!0,opacity:0,depthWrite:!1,blending:rr,fog:!0})),x=p.normal().setY(0).normalize(),T=p.world(p.screenLocal).addScaledVector(x,d.screenW*.9);R.position.set(T.x,.012,T.z),R.rotation.x=-Math.PI/2,R.rotation.z=Math.atan2(x.x,x.z),d.hang||this.scene.add(R);let P=null;this.lowPower||(P=new ol(h.accent,0,3.2+d.screenW,2),P.position.copy(p.world(p.screenLocal).addScaledVector(p.normal(),1.1)),this.scene.add(P)),p.hit.userData.pick=h.id,this.pickables.push(p.hit),this.stations.set(h.id,{site:h,screen:f,monitor:p,placement:d,cable:b,glow:R,light:P,power:0,powerAt:1/0,staticLeft:0,hover:0})}}homePose(){const t=this.camera.aspect,e=t<1,n=e?7.2+(1-t)*2.4:7.8;return{pos:new C(0,e?2.5:2.4,n),target:new C(0,e?1.7:1.95,-1.6)}}posesFor(t){if(t==="vcr"){const c=this.camera.aspect<1;return{pos:new C(.3,1.75,c?3.9:2.9),target:new C(0,ln+.12,.2)}}const e=this.stations.get(t);if(!e)return null;const n=e.monitor,i=n.world(n.screenLocal),r=n.normal(),a=xr.degToRad(this.camera.fov),o=2*Math.atan(Math.tan(a/2)*this.camera.aspect),l=Math.max(n.screenH/2/Math.tan(a/2)/.46,n.screenH/.75/2/Math.tan(o/2)/.7);return{pos:i.clone().addScaledVector(r,l+.05),target:i}}applyLimits(t){const e=this.controls;if(!t){e.minDistance=3,e.maxDistance=15,e.minAzimuthAngle=-.8,e.maxAzimuthAngle=.8,e.minPolarAngle=.95,e.maxPolarAngle=1.56;return}const n=new C().subVectors(this.camera.position,e.target),i=new cl().setFromVector3(n);e.minDistance=i.radius*.45,e.maxDistance=i.radius*1.7,e.minAzimuthAngle=i.theta-.6,e.maxAzimuthAngle=i.theta+.6,e.minPolarAngle=Math.max(.3,i.phi-.45),e.maxPolarAngle=Math.min(1.6,i.phi+.35)}flyTo(t,e,n){this.controls.enabled=!1,this.controls.minAzimuthAngle=-1/0,this.controls.maxAzimuthAngle=1/0,this.tween={from:{pos:this.camera.position.clone(),target:this.controls.target.clone()},to:t,k:0,dur:this.reduced?.01:e,done:n}}focus(t){const e=t?this.posesFor(t):this.homePose();if(e){this.cancelPlay(),this.focused=t;for(const[n,i]of this.stations)i.cable.setLit(n===t?1:0);this.flyTo(e,t?1.15:1.3,()=>{this.applyLimits(t),this.controls.enabled=!this.tuned,this.controls.update()})}}get focusedId(){return this.focused}keepAlive(t){this.kept=t}get busy(){return this.playing!==null}cancelPlay(){this.playing=null;const t=this.settlePlay;this.settlePlay=null,t==null||t(!1)}play(t){const e=this.stations.get(t),n=this.posesFor(t);if(!e||!n)return Promise.resolve(!0);this.cancelPlay();const i=new Promise(c=>this.settlePlay=c);this.vcr.load(e.site.title),this.vcr.vfd.play(e.placement.channel,e.site.title,this.t),this.focused=t,this.playing=t;for(const[c,u]of this.stations)u.cable.setLit(c===t?1:0);const r=()=>{this.playing===t&&(e.staticLeft=.9,this.post.kick(.7),this.flyTo(n,1.15,()=>{if(this.playing!==t)return;this.playing=null,this.applyLimits(t),this.controls.enabled=!this.tuned,this.controls.update();const c=this.settlePlay;this.settlePlay=null,c==null||c(!0)}))};if(this.reduced)return r(),i;const a=Math.sign(e.monitor.group.position.x)||1,o=this.camera.aspect<1,l={pos:new C(a*.45,1.4,o?3.1:2.25),target:new C(a*.05,ln+.1,.25)};return this.flyTo(l,.8,()=>{this.playing===t&&(e.cable.surge(),this.after(.35,r))}),i}after(t,e){this.later.push({at:this.t+t,fn:e})}eject(){this.vcr.load(null),this.vcr.vfd.stop()}scroll(t){this.vcr.vfd.scroll(t,this.t)}dive(t){const e=this.stations.get(t);if(!e||this.reduced)return Promise.resolve();this.cancelPlay();const n=e.monitor,i=n.world(n.screenLocal),r=i.clone().addScaledVector(n.normal(),n.screenH*.16);return e.staticLeft=.8,this.post.kick(1),new Promise(a=>this.flyTo({pos:r,target:i},.75,a))}powerOn(){let t=0;const e=[...this.stations.values()].sort((n,i)=>n.placement.channel-i.placement.channel);for(const n of e)n.powerAt=this.t+.25+t++*(this.reduced?0:.14);this.vcr.vfd.scroll("present day present time",this.t+.4)}anchor(t){let e;if(t==="vcr")e=new C(0,ln-.05,.95);else{const i=this.stations.get(t);if(!i)return null;e=i.monitor.world(i.monitor.screenLocal.clone().add(new C(0,i.monitor.screenH*.5,0)))}if(e.project(this.camera),e.z>1)return null;const n=this.renderer.domElement.getBoundingClientRect();return{x:n.left+(e.x+1)/2*n.width,y:n.top+(1-e.y)/2*n.height}}setShift(t,e){this.shiftTarget={x:t,y:e}}applyShift(t){this.tuned&&(this.shiftTarget={x:0,y:0});const e=this.reduced?1:Math.min(1,t*5),n=this.shift.x+(this.shiftTarget.x-this.shift.x)*e,i=this.shift.y+(this.shiftTarget.y-this.shift.y)*e;if(Math.abs(n-this.shift.x)<.05&&Math.abs(i-this.shift.y)<.05&&this.camera.view)return;this.shift={x:n,y:i};const r=this.container.clientWidth||innerWidth,a=this.container.clientHeight||innerHeight;this.camera.setViewOffset(r,a,n,i,r,a)}tuneIn(t,e){const n=this.stations.get(t),i=this.tunePose(t);if(!n||!i)return Promise.resolve();this.tuneOut(),this.cancelPlay();const r=this.tuneSeq;this.focused=t,this.shiftTarget={x:0,y:0};for(const[a,o]of this.stations)o.cable.setLit(a===t?1:0);return new Promise(a=>this.flyTo(i,1.1,()=>{if(r!==this.tuneSeq){this.applyLimits(t),this.controls.enabled=!0;return}this.tuned={id:t,place:e},n.staticLeft=0,this.resize(),a()}))}tunePose(t){const e=this.stations.get(t);if(!e)return null;const n=e.monitor,i=n.screenH/.75,r=n.world(n.screenLocal),a=xr.degToRad(this.camera.fov),o=2*Math.atan(Math.tan(a/2)*this.camera.aspect),l=Math.max(n.screenH/2/Math.tan(a/2)/.84,i/2/Math.tan(o/2)/.94);return{pos:r.clone().addScaledVector(n.normal(),l),target:r}}tuneOut(){this.tuneSeq++,this.tuned&&(this.tuned=null,this.tween||(this.applyLimits(this.focused),this.controls.enabled=!0))}reset(){this.tuneOut(),this.cancelPlay(),this.focus(null)}glassRect(t){const e=this.stations.get(t);if(!e)return null;const n=e.monitor,i=n.screenH/.75,r=this.renderer.domElement.getBoundingClientRect(),a=[],o=[];this.camera.updateMatrixWorld();for(const[u,h]of[[-1,-1],[1,-1],[1,1],[-1,1]]){const d=n.world(n.screenLocal.clone().add(new C(u*i/2,h*n.screenH/2,i*.02)));d.project(this.camera),a.push(r.left+(d.x+1)/2*r.width),o.push(r.top+(1-d.y)/2*r.height)}const l=Math.min(...a),c=Math.min(...o);return new DOMRectReadOnly(l,c,Math.max(...a)-l,Math.max(...o)-c)}get tunedId(){var t;return((t=this.tuned)==null?void 0:t.id)??null}bindPointer(){const t=this.renderer.domElement;let e=null;t.addEventListener("pointermove",n=>{const i=t.getBoundingClientRect();this.pointer.set((n.clientX-i.left)/i.width*2-1,-((n.clientY-i.top)/i.height)*2+1),this.pointerPx={x:n.clientX,y:n.clientY},this.pointerDirty=!0,this.lastInput=this.t}),t.addEventListener("pointerleave",()=>{this.pointer.set(-9,-9),this.pointerDirty=!0}),t.addEventListener("pointerdown",n=>{if(n.button!==0||!n.isPrimary){e=null;return}e={x:n.clientX,y:n.clientY,t:performance.now()},this.lastInput=this.t}),t.addEventListener("pointerup",n=>{if(!e)return;const i=Math.hypot(n.clientX-e.x,n.clientY-e.y),r=performance.now()-e.t<600;if(e=null,i>7||!r||this.tuned)return;const a=t.getBoundingClientRect();this.pointer.set((n.clientX-a.left)/a.width*2-1,-((n.clientY-a.top)/a.height)*2+1),this.events.pick(this.pickAt())})}pickFromPoint(t,e){const n=this.renderer.domElement.getBoundingClientRect(),i=this.pointer.clone();this.pointer.set((t-n.left)/n.width*2-1,-((e-n.top)/n.height)*2+1);const r=this.pickAt();return this.pointer.copy(i),r}pickAt(){this.raycaster.setFromCamera(this.pointer,this.camera);const t=this.raycaster.intersectObjects(this.pickables,!1)[0];return(t==null?void 0:t.object.userData.pick)??null}resize(){const t=this.container.clientWidth||innerWidth,e=this.container.clientHeight||innerHeight,n=Math.abs(t/e-this.lastAspect)>.02;if(this.lastAspect=t/e,this.camera.aspect=t/e,this.camera.fov=this.camera.aspect<1?58:45,this.camera.setViewOffset(t,e,this.shift.x,this.shift.y,t,e),this.renderer.setSize(t,e,!1),this.renderer.domElement.style.width=`${t}px`,this.renderer.domElement.style.height=`${e}px`,this.post.setSize(t,e,this.pixelRatio),this.tuned){this.shift={x:0,y:0},this.camera.setViewOffset(t,e,0,0,t,e);const i=this.tunePose(this.tuned.id);i&&(this.camera.position.copy(i.pos),this.controls.target.copy(i.target),this.camera.lookAt(i.target));const r=this.glassRect(this.tuned.id);r&&this.tuned.place(r)}if(n&&!this.tween&&!this.focused&&this.controls){const i=this.homePose();this.camera.position.copy(i.pos),this.controls.target.copy(i.target)}}async warm(t=5e3){try{await Promise.race([this.renderer.compileAsync(this.scene,this.camera),new Promise(e=>setTimeout(e,t))]),this.post.render(0,0)}catch(e){console.warn("oikos: warm-up skipped",e)}}start(){if(this.running)return;if(this.running=!0,this.clock.getDelta(),this.driven){const e=this.renderer.getContext();window.__oikos={step:(n=1,i=1/30)=>{var r;this.perf={frames:0,paint:0,render:0,uploads:0};for(let a=0;athis.anchor(n),glass:n=>this.glassRect(n)};return}const t=()=>{this.running&&(this.raf=requestAnimationFrame(t),this.frame())};this.raf=requestAnimationFrame(t)}stop(){this.running=!1,cancelAnimationFrame(this.raf)}adapt(t){this.frameTimes.push(t);const e=this.frameTimes.reduce((i,r)=>i+r,0);if(this.frameTimes.length<90&&e<2)return;const n=this.frameTimes.reduce((i,r)=>i+r,0)/this.frameTimes.length;if(this.frameTimes=[],this.calm=n<1/50?this.calm+1:0,this.calm>=3&&this.qualityStep>0){this.calm=0,this.qualityStep--,this.pixelRatio=Math.min(this.maxPixelRatio,this.pixelRatio/.8),this.post.bloom.enabled=!0,this.applyPixelRatio();return}n>1/27&&this.qualityStep<3&&(this.qualityStep++,this.pixelRatio=Math.max(.6,this.pixelRatio*.8),this.qualityStep>=3&&(this.post.bloom.enabled=!1),this.applyPixelRatio())}applyPixelRatio(){this.renderer.setPixelRatio(this.pixelRatio);const t=this.container.clientWidth||innerWidth,e=this.container.clientHeight||innerHeight;this.renderer.setSize(t,e,!1),this.post.setSize(t,e,this.pixelRatio)}frame(t){var o;const e=t??this.clock.getDelta(),n=Math.min(e,.1)*this.speed;this.t+=n;const i=this.t;if(this.driven||this.adapt(Math.min(e,.5)),this.applyShift(n),this.later.length){const l=this.later.filter(c=>c.at<=i);this.later=this.later.filter(c=>c.at>i);for(const c of l)c.fn()}if(this.tween){const l=this.tween;l.k=Math.min(1,l.k+n/l.dur);const c=wx(l.k);this.camera.position.lerpVectors(l.from.pos,l.to.pos,c),this.controls.target.lerpVectors(l.from.target,l.to.target,c),this.camera.lookAt(this.controls.target),l.k>=1&&(this.tween=null,(o=l.done)==null||o.call(l))}else{if(!this.focused&&!this.reduced&&i-this.lastInput>9){this.controls.autoRotate=!0;const l=this.controls.getAzimuthalAngle();l>.32&&(this.swayDir=1),l<-.32&&(this.swayDir=-1),this.controls.autoRotateSpeed=.18*this.swayDir}else this.controls.autoRotate=!1;this.controls.update(n)}if(this.pointerDirty&&!this.tween){this.pointerDirty=!1;const l=this.pickAt();l!==this.hovered&&(this.hovered=l,this.renderer.domElement.style.cursor=l?"pointer":""),this.events.hover(l,this.pointerPx.x,this.pointerPx.y)}const r=performance.now();this.camera.updateMatrixWorld(),this.viewProj.multiplyMatrices(this.camera.projectionMatrix,this.camera.matrixWorldInverse),this.frustum.setFromProjectionMatrix(this.viewProj);for(const[l,c]of this.stations){i>=c.powerAt&&(c.power=Math.min(1,c.power+n/(this.reduced?.01:.8))),c.staticLeft=Math.max(0,c.staticLeft-n),c.hover+=((l===this.hovered?1:0)-c.hover)*Math.min(1,n*8);const u=c.monitor.crt.uniforms;u.uTime.value=i,u.uPower.value=c.power,u.uHover.value=c.hover,u.uStatic.value=Math.min(1,c.staticLeft*1.6);const h=this.focused===l,d=h||l===this.kept||this.frustum.intersectsObject(c.monitor.glass),f=this.focused&&this.focused!=="vcr"&&!h&&l!==this.kept?.5:1;c.power>.2&&d&&c.screen.tick(i,n,h,f)&&(c.monitor.texture.needsUpdate=!0,this.perf.uploads++),c.monitor.setLed(c.power>.5,c.site.accent);const p=c.glow.material;p.opacity=c.power*(.16+c.hover*.12+(h?.1:0)),c.light&&(c.light.intensity=c.power*(2.2+c.hover*1.4+(h?.6:0))),c.cable.update(i,n)}this.vcr.vfd.glow=this.hovered==="vcr"?1:.8,this.vcr.vfd.update(i,n),this.vcr.update(n),this.field.update(i);const a=performance.now();if(this.post.render(i,n),this.driven){this.renderer.getContext().finish();const l=performance.now();this.perf.frames++,this.perf.paint+=a-r,this.perf.render+=l-a}}}let Ax=0;const di=s=>`${s}${++Ax}`,Cx="/static/oikos/mark.png";function vn(s,t=""){const e=di("tp"),n=t.length>9?`${t.slice(0,8)}…`:t;return``}function hu(s){return s.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}function fi(s="app"){if(s==="status")return'';if(s==="channel")return'';const t=di("ch");return``}function Ju(){return''}function ju(){return''}function oy(){return''}function Qu(){return''}function ay(){const s=yi("ap");return``}function td(){return''}function mr(s,t=!1){return``}function ed(s){return s.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}function Mi(s="app"){if(s==="status")return'';if(s==="channel")return'';const t=yi("ch");return``}let Fi=null,Mn=null,bl=null;function zx(s){Fi=s,s.addEventListener("pointerdown",t=>{if(!Mn)return;const e=t.target;e.closest(".xp-menu")||e.closest("[data-menu-owner]")||pi()},!0)}function Hx(){return Mn!==null}function pi(){if(!Mn)return!1;let s=Mn;for(;s;)s.el.remove(),s=s.child;Mn=null;const t=bl;return bl=null,t==null||t(),!0}function uu(s,t){const e=G("div",{class:"xp-menu",role:"menu"}),n=[];for(const i of s){if(i==="sep"){e.append(G("div",{class:"xp-menu-sep",role:"separator"}));continue}const r=G("button",{type:"button",role:i.checked!==void 0?"menuitemcheckbox":"menuitem",class:`${i.bold?"bold ":""}${i.submenu?"has-sub":""}`.trim(),html:`${i.icon??""}`});r.querySelector(".lb").textContent=i.label,r.querySelector(".sc").textContent=i.shortcut??"",i.checked!==void 0&&(r.setAttribute("aria-checked",String(i.checked)),i.checked&&r.classList.add("checked")),i.disabled&&(r.setAttribute("aria-disabled","true"),r.tabIndex=-1);const a=()=>{var u;if(!i.submenu||i.disabled)return;const o=wl(e);if(!o||((u=o.child)==null?void 0:u.el.dataset.for)===i.label)return;El(o);const l=r.getBoundingClientRect(),c=du(uu(i.submenu(),o),l.right-3,l.top-3,l.left+3);c.dataset.for=i.label,o.child={el:c,child:null}};r.addEventListener("pointerenter",()=>{const o=wl(e);i.submenu?a():o&&El(o)}),r.addEventListener("click",o=>{var l,c,u,h;if(o.stopPropagation(),!i.disabled){if(i.submenu){a(),(u=(c=(l=wl(e))==null?void 0:l.child)==null?void 0:c.el.querySelector("button:not([aria-disabled])"))==null||u.focus();return}pi(),(h=i.run)==null||h.call(i)}}),e.append(r),n.push(r)}return e.addEventListener("keydown",i=>{var o,l,c;const r=n.filter(u=>u.getAttribute("aria-disabled")!=="true"),a=r.indexOf(document.activeElement);if(i.key==="ArrowDown"||i.key==="ArrowUp"){i.preventDefault(),i.stopPropagation();const u=r[(a+(i.key==="ArrowDown"?1:-1)+r.length)%r.length];u==null||u.focus()}else i.key==="ArrowRight"&&((o=r[a])!=null&&o.classList.contains("has-sub"))?(i.preventDefault(),i.stopPropagation(),(l=r[a])==null||l.click()):i.key==="ArrowLeft"&&t&&(i.preventDefault(),i.stopPropagation(),El(t),(c=t.el.querySelector("button.has-sub"))==null||c.focus())}),e}function wl(s){let t=Mn;for(;t;){if(t.el===s)return t;t=t.child}return null}function El(s){let t=s.child;for(;t;)t.el.remove(),t=t.child;s.child=null}function du(s,t,e,n=t){if(!Fi)throw new Error("menu host not set");s.style.left="0px",s.style.top="0px",Fi.append(s);const i=Fi.getBoundingClientRect(),r=s.offsetWidth,a=s.offsetHeight;let o=t-i.left,l=e-i.top;return o+r>i.width-2&&(o=Math.max(2,n-i.left-r)),l+a>i.height-32&&(l=Math.max(2,i.height-32-a)),s.style.left=`${Math.round(o)}px`,s.style.top=`${Math.round(l)}px`,s}function Ms(s,t,e,n,i=!1){var a;pi();const r=uu(s,null);Mn={el:r,child:null},du(r,t,e),bl=n??null,i&&((a=r.querySelector("button:not([aria-disabled])"))==null||a.focus())}function fu(s,t,e){const n=t.getBoundingClientRect();if(Ms(s,n.right-2,n.top-3,e),Mn){const i=Fi==null?void 0:Fi.getBoundingClientRect();i&&n.right+Mn.el.offsetWidth>i.right&&(Mn.el.style.left=`${Math.max(2,n.left-i.left-Mn.el.offsetWidth+2)}px`)}}function Gx(s,t){s.addEventListener("contextmenu",e=>{const n=e.target;if(n.closest("input, textarea, [contenteditable], .oikos-tube"))return;const i=t(n,e);if(i!==null){if(e.preventDefault(),!i.length){pi();return}Ms(i,e.clientX,e.clientY)}})}function G(s,t={},e=[]){const n=document.createElement(s);for(const[i,r]of Object.entries(t))i==="html"?n.innerHTML=r:n.setAttribute(i,r);for(const i of e)n.append(i);return n}function Tl(s){const t=G("div",{class:"xp-menubar",role:"menubar"});let e=null;const n=(i,r,a)=>{var c;const o=(c=s[r])==null?void 0:c.call(s);if(!o)return;const l=i.getBoundingClientRect();Ms(o,l.left,l.bottom,()=>{i.classList.remove("open"),e===i&&(e=null)},a),i.classList.add("open"),e=i};for(const i of Object.keys(s)){const r=G("button",{type:"button",class:"xp-menutitle",role:"menuitem","aria-haspopup":"menu","data-menu-owner":""},[i]);r.addEventListener("click",()=>{e===r?pi():n(r,i,!1)}),r.addEventListener("pointerenter",()=>{e&&e!==r&&Hx()&&n(r,i,!1)}),r.addEventListener("keydown",a=>{(a.key==="ArrowDown"||a.key==="Enter"||a.key===" ")&&(a.preventDefault(),n(r,i,!0))}),t.append(r)}return t.append(G("span",{class:"xp-throbber",html:Js(18,!0)})),t}function pu(s){const t=G("div",{class:"xp-toolbar"}),e=(a,o,l,c=!0)=>{const u=G("button",{class:"xp-tb",type:"button",title:o,html:`${a}${c?`${o}`:""}`});return l?u.addEventListener("click",l):u.disabled=!0,t.append(u),u},n=e(Px(),"Back",s.back),i=e(Lx(),"Forward",s.forward,!1);e(Dx(),"Up",s.up,!1),t.append(G("span",{class:"xp-tb-sep"})),e(Ix(),"Search",s.search),e(Nx(),"Folders",s.folders);const r=()=>{if(!s.can)return;const a=s.can();n.disabled=!s.back||!a.back,i.disabled=!s.forward||!a.forward};return r(),{el:t,refresh:r}}function mu(s,t,e,n){const i=G("div",{class:"xp-address"});i.append(G("span",{class:"lbl"},["Address"]));const r=G("label",{class:"field",html:s});let a=null;e?(a=G("input",{type:"text",value:t,"aria-label":"Address",spellcheck:"false"}),a.style.cssText="flex:1;min-width:0;border:0;outline:0;font:inherit;background:transparent;",r.append(a),a.addEventListener("keydown",l=>{l.key==="Enter"&&a&&(n==null||n(a.value)),l.stopPropagation()})):r.append(G("span",{},[t])),i.append(r);const o=G("button",{class:"go",type:"button",html:`${Ux()}Go`});return o.addEventListener("click",()=>n==null?void 0:n((a==null?void 0:a.value)??t)),i.append(o),{el:i,input:a}}function ys(s,t,e=!1){const n=G("section",{class:`xp-taskgroup${e?" primary":""}`}),i=G("header",{role:"button",tabindex:"0","aria-expanded":"true"},[s]),r=()=>{n.classList.toggle("collapsed"),i.setAttribute("aria-expanded",String(!n.classList.contains("collapsed")))};i.addEventListener("click",r),i.addEventListener("keydown",o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),r())}),n.append(i);const a=G("div",{class:"xp-taskbody"});if(t instanceof HTMLElement)a.append(t);else{const o=G("ul");for(const l of t){const c=G("li");let u;if(l.href)u=G("a",{class:"xp-link",href:l.href,html:`${l.icon}`}),l.external&&(u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"));else{u=G("button",{class:"xp-link",type:"button",html:`${l.icon}`});const d=l.run;d?u.addEventListener("click",d):u.setAttribute("aria-disabled","true")}const h=u.querySelector("span");h&&(h.textContent=l.label),c.append(u),o.append(c)}a.append(o)}return n.append(a),n}function gu(){const s=G("div",{class:"xp-statusbar",role:"status"}),t=G("span"),e=G("span",{class:"zone"});return s.append(t,e),{el:s,set(n,i){t.textContent=n;const[r,a]=i==="computer"?[tu(),"My Computer"]:[eu(),"Internet"];e.innerHTML=`${r}${a}`}}}const Vx={dreams:["chronicle","dreams-api","dream_gen"],chronicle:["dreams","dream_gen"],"dreams-api":["dreams","chronicle"],dream_gen:["dreams","chronicle"],transmissions:["irc","syrinx"],apeiron:["dreams","syrinx"],syrinx:["apeiron","transmissions"],irc:["transmissions","parlor"],parlor:["irc","transmissions"]};function _u(s){return/^https?:/.test(s.href)?s.href:`${location.origin}${s.href}`}function xu(s){return s.group==="here"?"computer":"internet"}function Al(s,t){var i;const e=_u(t),n=(i=navigator.clipboard)==null?void 0:i.writeText(e);if(!n){s.balloon("Could not copy",e);return}n.then(()=>s.balloon("Copied",e),()=>s.balloon("Could not copy",e))}function na(s,t,e=!1){const n=[];return e?n.push({label:`Open ${t.title}`,bold:!0,run:()=>s.open(t)}):(n.push({label:"Play",bold:!0,run:()=>s.play(t.id)}),n.push({label:`Go to ${t.title}`,run:()=>s.open(t)})),n.push({label:"Look at its screen",run:()=>s.look(t.id),disabled:!s.canTune}),t.tune&&n.push({label:"Watch it here",run:()=>s.tuneIn(t.id),disabled:!s.canTune}),t.id==="irc"&&n.push({label:"Join in mIRC",run:()=>s.chat("#aethera")}),n.push("sep",{label:"Copy Address",run:()=>Al(s,t)}),e||n.push("sep",{label:"Properties",run:()=>s.play(t.id)}),n}function vu(s,t){return[{label:"Undo",shortcut:"Ctrl+Z",disabled:!0},"sep",{label:"Cut",shortcut:"Ctrl+X",disabled:!0},{label:"Copy",shortcut:"Ctrl+C",disabled:!t,run:()=>t&&Al(s,t)},{label:"Paste",shortcut:"Ctrl+V",disabled:!0},"sep",{label:"Select All",shortcut:"Ctrl+A",disabled:!0},{label:"Invert Selection",disabled:!0}]}function Mu(s){return[{label:"Add to Favorites...",disabled:!0},{label:"Organize Favorites...",disabled:!0},"sep",...s.dir.sites.filter(t=>t.group==="here").map(t=>({label:t.title,icon:vn(t.accent),run:()=>s.play(t.id)}))]}function yu(){return[{label:"Map Network Drive...",disabled:!0},{label:"Disconnect Network Drive...",disabled:!0},{label:"Synchronize...",disabled:!0},"sep",{label:"Folder Options...",disabled:!0}]}function Su(s){return[{label:"Help and Support Center",disabled:!0},"sep",{label:"About æthera",run:()=>s.about()}]}function Cl(s){s.hidden=!s.hidden}function bu(s,t){return[{label:"Toolbars",submenu:()=>[{label:"Standard Buttons",checked:!t.toolbar.hidden,run:()=>Cl(t.toolbar)},{label:"Address Bar",checked:!t.address.hidden,run:()=>Cl(t.address)}]},{label:"Status Bar",checked:!t.status.hidden,run:()=>Cl(t.status)},...t.views?["sep",...t.views]:[],"sep",{label:"Go To",submenu:()=>[{label:"Back",shortcut:"Alt+Left",disabled:!s.canBack,run:()=>s.back()},{label:"Forward",shortcut:"Alt+Right",disabled:!s.canForward,run:()=>s.forward()},{label:"Up One Level",disabled:!t.up,run:()=>{var e;return(e=t.up)==null?void 0:e.call(t)}},"sep",{label:"Home Page",shortcut:"Alt+Home",run:()=>s.home()}]},{label:"Refresh",shortcut:"F5",run:()=>t.refresh()}]}class Wx{constructor(t,e){I(this,"win",null);I(this,"selected",null);I(this,"tiles",new Map);I(this,"tasks",null);I(this,"status",null);I(this,"content",null);I(this,"view","tiles");this.shell=t,this.wm=e}get isOpen(){return!!this.win&&!this.win.closed}open(){var u;if(this.isOpen&&this.win){this.win.minimized?this.win.restore():this.win.focus();return}this.tiles.clear();const t=G("div");t.style.cssText="display:flex;flex-direction:column;min-height:0;flex:1;";const{dir:e}=this.shell,n=pu({back:()=>this.shell.back(),forward:()=>this.shell.forward(),can:()=>({back:this.shell.canBack,forward:this.shell.canForward}),search:()=>{var h;return(h=i.input)==null?void 0:h.focus()},folders:()=>{var h;return(h=this.tasks)==null?void 0:h.toggleAttribute("hidden")}}),i=mu(Zs(),"~/æthera",!0,h=>this.go(h));(u=i.input)==null||u.addEventListener("input",()=>{var h;return this.filter(((h=i.input)==null?void 0:h.value)??"")}),this.status=gu();const r=()=>this.selected?this.shell.site(this.selected)??null:null;t.append(Tl({File:()=>this.fileMenu(),Edit:()=>vu(this.shell,r()),View:()=>{var h;return bu(this.shell,{toolbar:n.el,address:i.el,status:((h=this.status)==null?void 0:h.el)??G("div"),views:this.viewItems(),refresh:()=>{i.input&&(i.input.value="~/æthera"),this.filter("")}})},Favorites:()=>Mu(this.shell),Tools:()=>yu(),Help:()=>Su(this.shell)}),n.el,i.el);const a=this.shell.onNav(()=>n.refresh()),o=G("div",{class:"xp-body"});this.tasks=G("aside",{class:"xp-tasks"}),o.append(this.tasks);const l=G("div",{class:`xp-content view-${this.view}`,role:"listbox","aria-label":"tapes"});this.content=l,l.addEventListener("pointerdown",h=>{h.button===0&&!h.target.closest(".xp-tile")&&this.select(null)});const c=(h,d)=>{if(!d.length)return;l.append(G("h3",{class:"xp-group-head"},[h]));const f=G("div",{class:"xp-tiles"});for(const p of d)f.append(this.tile(p));l.append(f)};if(c("Tapes Stored on This Server",e.sites.filter(h=>h.group==="here")),c("Other Places on the Wired",e.sites.filter(h=>h.group==="wired")),e.files.length){l.append(G("h3",{class:"xp-group-head"},["Files"]));const h=G("div",{class:"xp-tiles"});for(const d of e.files){const f=d.title.split(".").pop()??"txt",p=G("a",{class:"xp-tile",href:d.href,html:`${vs(f)}`});p.querySelector(".t").textContent=d.title,p.querySelector(".k").textContent=d.about,h.append(p)}l.append(h)}o.append(l),t.append(o),t.append(this.status.el),this.win=this.wm.open({id:"home",title:"~ (home directory)",icon:Zs(),body:t,width:Math.round(Math.min(680,Math.max(420,innerWidth*.46))),dock:"left",onClose:()=>{a(),this.win=null,this.content=null,this.selected=null}}),this.win.el.style.height="min(560px, calc(100% - 40px))",this.renderTasks(),this.renderStatus()}close(){var t;(t=this.win)==null||t.close()}fileMenu(){const t=this.selected?this.shell.site(this.selected):void 0;return[...t?na(this.shell,t):[{label:"Play",bold:!0,disabled:!0}],"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"Create Shortcut",disabled:!0},{label:"Delete",disabled:!0},{label:"Rename",disabled:!0},"sep",{label:"Close",run:()=>this.close()}]}viewItems(){const t=e=>()=>{this.view=e,this.content&&(this.content.className=`xp-content view-${e}`)};return[{label:"Tiles",checked:this.view==="tiles",run:t("tiles")},{label:"Icons",checked:this.view==="icons",run:t("icons")},{label:"List",checked:this.view==="list",run:t("list")}]}contextFor(t){const e=this.shell.site(t);return e?(this.select(t,!0),na(this.shell,e)):null}blankMenu(){return[{label:"View",submenu:()=>this.viewItems()},{label:"Arrange Icons By",disabled:!0,submenu:()=>[]},"sep",{label:"Refresh",run:()=>this.filter("")},"sep",{label:"Paste",disabled:!0},{label:"Paste Shortcut",disabled:!0},"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"Properties",disabled:!0}]}tile(t){const e=G("button",{class:"xp-tile",type:"button",role:"option","data-id":t.id,html:`${vn(t.accent,t.title)}`});return e.querySelector(".t").textContent=t.title,e.querySelector(".k").textContent=t.kind,e.querySelector(".d").textContent=t.tagline,e.title=t.tagline,e.addEventListener("click",()=>this.select(t.id,!0)),e.addEventListener("dblclick",()=>this.shell.play(t.id)),e.addEventListener("keydown",n=>{if(n.key==="Enter"&&this.shell.play(t.id),["ArrowRight","ArrowDown","ArrowLeft","ArrowUp"].includes(n.key)){n.preventDefault(),n.stopPropagation();const i=[...this.tiles.values()].filter(o=>!o.hidden),r=i.indexOf(e),a=i[(r+(n.key==="ArrowRight"||n.key==="ArrowDown"?1:-1)+i.length)%i.length];a==null||a.focus(),a!=null&&a.dataset.id&&this.select(a.dataset.id,!0)}}),e.addEventListener("pointerup",n=>{n.pointerType==="touch"&&this.selected===t.id&&e.dataset.armed&&this.shell.play(t.id),e.dataset.armed="1"}),this.tiles.set(t.id,e),e}select(t,e=!1){if(t!==this.selected){this.selected=t;for(const[n,i]of this.tiles)i.classList.toggle("selected",n===t),i.setAttribute("aria-selected",String(n===t)),n!==t&&delete i.dataset.armed;e&&this.shell.select(t),this.renderTasks(),this.renderStatus()}}filter(t){const e=t.replace(/^~\/?(æthera)?\/?/i,"").trim().toLowerCase();for(const[n,i]of this.tiles){const r=this.shell.site(n);i.hidden=!!e&&!`${r==null?void 0:r.title} ${r==null?void 0:r.kind} ${r==null?void 0:r.tagline}`.toLowerCase().includes(e)}}go(t){const e=t.replace(/^~\/?(æthera)?\/?/i,"").trim().toLowerCase();if(!e)return;const n=this.shell.dir.sites.find(i=>i.title.toLowerCase()===e)??this.shell.dir.sites.find(i=>`${i.title} ${i.kind} ${i.tagline}`.toLowerCase().includes(e));n?this.shell.play(n.id):this.shell.balloon("Cannot find it",`There is no tape called “${t}” in ~.`)}renderTasks(){if(!this.tasks)return;const t=this.selected?this.shell.site(this.selected):void 0,e=t?[{icon:Sl(),label:"Play this tape",run:()=>this.shell.play(t.id)},{icon:su(),label:"Look at its screen",run:()=>this.shell.look(t.id)},{icon:eu(),label:`Go to ${t.title}`,run:()=>this.shell.open(t)}]:[{icon:Sl(),label:"Select a tape to play it"},{icon:ru(),label:"Eject the tape",run:()=>this.shell.eject()}],n=[{icon:tu(),label:"æthera",href:"/"},{icon:vs("xml"),label:"feed.xml",href:"/feed.xml"},{icon:vs("txt"),label:"llms.txt",href:"/llms.txt"}],i=G("div",{class:"xp-details"});if(t){i.append(G("b",{},[t.title]));const r=G("dl");for(const[a,o]of t.details)r.append(G("dt",{},[a]),G("dd",{},[o]));i.append(r)}else{const r=this.shell.dir.sites.filter(a=>a.group==="here").length;i.append(G("b",{},["~"]),G("span",{},[`home directory · ${r} tapes here, ${this.shell.dir.sites.length-r} elsewhere on the Wired`]))}this.tasks.replaceChildren(ys(t?"Tape Tasks":"System Tasks",e,!0),ys("Other Places",n),ys("Details",i))}renderStatus(){var n;const t=this.selected?this.shell.site(this.selected):void 0,e=this.shell.dir.sites.length+this.shell.dir.files.length;(n=this.status)==null||n.set(t?`${t.title} — ${t.tagline}`:`${e} objects`,t?xu(t):"computer")}}const $x=4001,Xx=4002,qx=s=>s>=4e3&&s<5e3,Yx=4;class Kx{constructor(t,e,n){I(this,"ws",null);I(this,"tries",0);I(this,"timer",0);I(this,"wanted",!1);I(this,"welcomed",!1);I(this,"refused",!1);this.nick=t,this.password=e,this.on=n}get open(){var t;return this.welcomed&&((t=this.ws)==null?void 0:t.readyState)===WebSocket.OPEN}connect(){this.wanted=!0,this.refused=!1,clearTimeout(this.timer);const t=location.protocol==="https:"?"wss":"ws";let e;try{e=new WebSocket(`${t}://${location.host}/ws/chat`)}catch{this.on.closed(!1,!1,!1);return}this.ws=e,this.welcomed=!1,e.onopen=()=>{e.send(JSON.stringify({type:"hello",nick:this.nick,password:this.password||null}))},e.onmessage=n=>{let i;try{i=JSON.parse(String(n.data))}catch{return}switch(i.type){case"welcome":{const r=i;this.welcomed=!0,this.tries=0,this.nick=r.nick,this.on.welcome(r);break}case"event":{const r=i.event;if(!r)break;r.kind==="nick"&&r.nick===this.nick&&r.target&&(this.nick=r.target),this.on.event(r);break}case"whois":this.on.whois(i);break;case"error":{this.welcomed||(this.refused=!0),this.on.error(String(i.code??""),String(i.text??""));break}}},e.onclose=n=>{if(this.ws!==e)return;this.ws=null;const i=n.code===$x,r=n.code===Xx,a=this.wanted&&!qx(n.code)&&!this.refused&&this.triesthis.connect(),2e3*2**(this.tries-1))}}send(t){if(!this.open||!this.ws)return!1;try{return this.ws.send(JSON.stringify(t)),!0}catch{return!1}}part(t="Leaving"){if(this.open&&this.ws)try{this.ws.send(JSON.stringify({type:"part",reason:t}))}catch{}this.close()}close(){this.wanted=!1,clearTimeout(this.timer);const t=this.ws;if(this.ws=null,this.welcomed=!1,this.password=null,t){t.onclose=null;try{t.close()}catch{}}}}const Ot="#aethera",Rt="#oikos",In="irc.aetherawi.red",Rl=6667,Pl="guest",wu="always falling apart",Zx=600,Jx=6e3,Eu="oikos-mirc-nick",Qx=/^[A-Za-z[\]\\`_^{|}][A-Za-z0-9[\]\\`_^{|}-]{0,15}$/,Ll=["~","&","@","%","+"];function jx(s){const t=new Date(s);return`[${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}]`}function tv(s){return Date.now()-(performance.now()-s)}function Dl(s){const t=Yn(s)||"anon",e=ca(t),n=["dialup.wired.net","cable.aether.org","dsl.lain.jp","adsl.nowhere.nu","res.navi.co"],i=n[Math.floor(e*n.length)]??n[0],r=Math.floor(e*251)+2,a=Math.floor(ca(`${t}.`)*251)+2;return`~${t.slice(0,9).toLowerCase()}@ppp-${r}-${a}.${i}`}function Yn(s){return s.replace(/^[~&@%+]+/,"")}function Tu(s){const t=s.charAt(0);return Ll.includes(t)?t:""}function ev(){try{return localStorage.getItem(Eu)??""}catch{return""}}function Au(s){try{localStorage.setItem(Eu,s)}catch{}}function Il(s){s.addEventListener("keydown",t=>{t.key!=="Escape"&&t.stopPropagation()})}class Nl{constructor(t,e,n){I(this,"tab");I(this,"pane");I(this,"log");I(this,"nicksEl");I(this,"nicks",new Map);I(this,"topic","");I(this,"joined",!1);this.id=t;const i=`mirc-pane-${t==="status"?"status":t.slice(1)}`;this.tab=G("button",{type:"button",role:"tab","aria-controls":i,html:`${e}`}),this.tab.querySelector("span").textContent=t==="status"?"Status":t,this.log=G("div",{class:"mirc-log",role:"log","aria-label":t==="status"?"Status":t}),n?(this.log.setAttribute("aria-live","polite"),this.nicksEl=G("ul",{class:"mirc-nicks","aria-label":`Nicknames in ${t}`}),this.pane=G("div",{class:"mirc-pane",id:i,role:"tabpanel"},[G("div",{class:"mirc-split"},[this.log,this.nicksEl])])):(this.nicksEl=null,this.pane=G("div",{class:"mirc-pane",id:i,role:"tabpanel"},[this.log]))}setNick(t,e=Tu(t)){const n=Yn(t);n&&this.nicks.set(n.toLowerCase(),{name:n,mode:e})}seen(t){const e=Yn(t);if(!e)return!1;const n=e.toLowerCase(),i=this.nicks.get(n),r=Tu(t)||(i==null?void 0:i.mode)||"";return i&&i.mode===r&&i.name===e?!1:(this.nicks.set(n,{name:e,mode:r}),!0)}drop(t){return this.nicks.delete(Yn(t).toLowerCase())}rename(t,e){const n=this.nicks.get(Yn(t).toLowerCase());this.drop(t),this.setNick(e,(n==null?void 0:n.mode)??"")}render(){if(!this.nicksEl)return;const t=n=>n?Ll.indexOf(n):Ll.length,e=[...this.nicks.values()].sort((n,i)=>t(n.mode)-t(i.mode)||n.name.localeCompare(i.name,void 0,{sensitivity:"base"}));this.nicksEl.replaceChildren(...e.map(n=>G("li",{title:`${n.mode}${n.name}`},[`${n.mode}${n.name}`])))}}class nv{constructor(t,e,n){I(this,"win");I(this,"wins");I(this,"view",Ot);I(this,"input");I(this,"me",Pl);I(this,"link",null);I(this,"hintedOikos",!1);I(this,"livingSeenAt",0);I(this,"livingOnce",!1);I(this,"greeted",!1);I(this,"hung",!1);I(this,"lastAt",-1);I(this,"seenFragments");I(this,"seenCollapse",null);I(this,"wasConnected",!1);I(this,"rejoinTimer",0);I(this,"off");var c;this.shell=t,this.wm=e;const i=G("div",{class:"mirc"});i.append(Tl({File:()=>[{label:"Connect...",run:()=>this.joinLiving(),disabled:!!this.link},{label:"Disconnect",run:()=>this.partLiving(),disabled:!this.link},"sep",{label:"Select Server...",disabled:!0},"sep",{label:"Exit",run:()=>this.win.close()}],Tools:()=>[{label:"Address Book...",disabled:!0},{label:"Options...",shortcut:"Alt+O",disabled:!0}],Commands:()=>[{label:`Join ${Rt}`,run:()=>this.joinLiving()},{label:`Part ${Rt}`,run:()=>this.partLiving(),disabled:!this.link},"sep",{label:"Clear buffer",run:()=>this.wins[this.view].log.replaceChildren()}],Window:()=>["status",Ot,Rt].filter(u=>!this.wins[u].tab.hidden).map(u=>({label:u==="status"?"Status":u,checked:this.view===u,run:()=>this.show(u)})),Help:()=>[{label:"Commands",shortcut:"F1",run:()=>this.help()},"sep",{label:"About æthera",run:()=>this.shell.about()}]}));const r=G("div",{class:"mirc-switch",role:"tablist"});this.wins={status:new Nl("status",fi("status"),!1),[Ot]:new Nl(Ot,fi("channel"),!0),[Rt]:new Nl(Rt,fi("channel"),!0)},this.wins[Ot].topic=wu,this.wins[Rt].tab.hidden=!0;const a=G("div",{class:"mirc-mdi"});for(const u of Object.values(this.wins))u.tab.addEventListener("click",()=>this.show(u.id)),r.append(u.tab),a.append(u.pane);this.input=G("input",{class:"mirc-input",type:"text",spellcheck:"false",autocomplete:"off",maxlength:"400","aria-label":"Message"}),Il(this.input),this.input.addEventListener("keydown",u=>{if(u.key!=="Enter")return;u.preventDefault();const h=this.input.value;this.input.value="",this.say(h)}),i.append(r,a,this.input),this.win=e.open({id:"mirc",title:"mIRC",icon:fi("app"),body:i,width:Math.round(Math.min(760,Math.max(460,innerWidth*.52))),dock:"center",onClose:()=>{var u,h;clearTimeout(this.rejoinTimer),this.off(),(u=this.link)==null||u.part("Leaving"),this.link=null,(h=this.wm.get("mirc-connect"))==null||h.close(),n()},onFocus:()=>{innerWidth>720&&this.input.focus({preventScroll:!0})}}),this.win.el.classList.add("mirc-window"),this.win.el.style.height="min(540px, calc(100% - 24px))";const o=this.win.el.parentElement;if(o){const u=this.win.el.offsetTop+this.win.el.offsetHeight-(o.clientHeight-8);u>0&&this.win.moveTo(this.win.el.offsetLeft,this.win.el.offsetTop-u)}const l=this.shell.feeds.irc.value;this.seenFragments=l.fragments,this.seenCollapse=((c=l.collapse)==null?void 0:c.at)??null,this.show(Ot),l.connected?this.connect(l):this.push("status","info",`* Connecting to ${In} (${Rl})`),this.off=this.shell.feeds.irc.on(u=>this.update(u))}focus(t){this.win.minimized?this.win.restore():this.win.focus();const e=t===Ot||t===Rt||t==="status"?this.wins[t]:null;e&&!e.tab.hidden&&this.show(e.id)}update(t){var n;if(t.connected&&!this.wasConnected?this.connect(t):!t.connected&&this.wasConnected&&this.disconnect(),!t.connected)return;for(const i of t.lines)i.at<=this.lastAt||(this.rejoinTimer&&this.rejoin(),this.lastAt=i.at,this.line(i));const e=((n=t.collapse)==null?void 0:n.at)??null;e!==null&&e!==this.seenCollapse&&t.collapse&&this.collapse(t.collapse.type),this.seenCollapse=e,t.fragments!==this.seenFragments&&(this.seenFragments=t.fragments,clearTimeout(this.rejoinTimer),this.hung?this.rejoinTimer=window.setTimeout(()=>this.rejoin(),Jx):this.rejoin())}connect(t){this.wasConnected=!0;const e=(i,r)=>this.push("status",i,r);if(this.greeted){e("info",`* Reconnected to ${In}`),this.joinHaunted(),t.collapse&&this.collapse(t.collapse.type);return}this.greeted=!0,e("info",`* Connecting to ${In} (${Rl})`),e("notice",`-${In}- *** Looking up your hostname...`),e("notice",`-${In}- *** Found your hostname`),e("text",`Welcome to the æthera IRC Network ${this.me}!${this.me}@oikos`),e("text",`Your host is ${In}, running version hauntd-2.8.21`),e("text",`- ${In} Message of the Day -`),e("text","- nobody here is who they were."),e("text","- every line is a replay; every replay is live."),e("text",`- ${Ot} is moderated: the living listen.`),e("text",`- the living talk in ${Rt}. /join ${Rt}`),e("text","End of /MOTD command."),e("mode",`* ${this.me} sets mode: +i`),this.joinHaunted();const n=t.lines.filter(i=>i.at>this.lastAt);if(n.length){this.push(Ot,"info","*** Buffer Playback...");for(const i of n)this.line(i);this.push(Ot,"info","*** Playback Complete.")}this.lastAt=Math.max(this.lastAt,...t.lines.map(i=>i.at)),t.collapse&&this.collapse(t.collapse.type)}joinHaunted(){const t=this.wins[Ot];t.joined=!0,t.nicks.clear(),t.setNick(this.me,""),this.push(Ot,"join",`* Now talking in ${Ot}`),this.push(Ot,"topic",`* Topic is '${t.topic}'`),this.push(Ot,"topic","* Set by ChanServ"),this.nicksChanged(Ot)}disconnect(){clearTimeout(this.rejoinTimer),this.rejoinTimer=0,this.wasConnected=!1;const t=this.wins[Ot];t.joined=!1,this.setHung(!1),this.push("status","info",`* Disconnected from ${Ot}`),this.push(Ot,"info","* Disconnected"),t.nicks.clear(),this.nicksChanged(Ot)}collapse(t){const e=t.replace(/_/g," ");this.push("status","notice",`-${In}- *** Notice -- ${e} on ${Ot}`),this.setHung(!0)}rejoin(){clearTimeout(this.rejoinTimer),this.rejoinTimer=0,this.setHung(!1),this.push(Ot,"info",`* Attempting to rejoin channel ${Ot}`),this.wins[Ot].topic=wu,this.joinHaunted()}line(t){const e=this.wins[Ot],n=t.nick,i=Yn(n),r=t.content,a=t.wallAt??tv(t.at),o=(c,u)=>this.push(Ot,c,u,a);let l=!1;switch(t.type){case"message":l=e.seen(n),o("text",`<${n}> ${r}`);break;case"action":{const c=/^sets mode: ([+-])([a-z]+) (.+)$/i.exec(r);c?(e.seen(n),this.applyMode(c[1]??"+",c[2]??"",(c[3]??"").split(/\s+/)),l=!0,o("mode",`* ${n} ${r}`)):/^has quit\b/i.test(r)?(l=e.drop(n),o("quit",`* ${i} ${r.replace(/^has quit\b/i,"has quit IRC")}`)):(l=e.seen(n),o("action",`* ${n} ${r}`));break}case"join":l=e.seen(n),o("join",`* ${i} (${Dl(n)}) has joined ${Ot}`);break;case"part":l=e.drop(n),o("part",`* ${i} (${Dl(n)}) has left ${Ot}${r?` (${r})`:""}`);break;case"quit":l=e.drop(n),o("quit",`* ${i} (${Dl(n)}) Quit (${r||"Client exited"})`);break;case"kick":{const c=t.reason||r;t.target?(l=e.drop(t.target),o("kick",`* ${Yn(t.target)} was kicked by ${i}${c?` (${c})`:""}`)):o("kick",`* ${i} kicks${c?` (${c})`:""}`);break}default:{const c=/changes topic to '(.*)'$/i.exec(r);if(c){e.topic=c[1]??e.topic,o("topic",`* ${r}`),this.retitle();break}const u=r||n;o("info",/^[*<]/.test(u)||u.length<6?u:`* ${u}`)}}!e.nicks.has(this.me.toLowerCase())&&e.joined&&(e.setNick(this.me,""),l=!0),l&&this.nicksChanged(Ot)}applyMode(t,e,n){const i=this.wins[Ot],r={q:"~",a:"&",o:"@",h:"%",v:"+"};[...e].forEach((a,o)=>{const l=n[o],c=r[a];if(!l||!c)return;const u=i.nicks.get(Yn(l).toLowerCase());i.setNick((u==null?void 0:u.name)??Yn(l),t==="+"?c:(u==null?void 0:u.mode)===c?"":(u==null?void 0:u.mode)??"")})}joinLiving(){if(this.link&&this.wins[Rt].joined){this.show(Rt);return}if(this.link){this.push(this.view,"info",`* Still connecting to ${Rt}...`);return}this.connectDialog()}partLiving(){if(!this.link)return;this.link.part("Leaving"),this.link=null;const t=this.wins[Rt];t.joined=!1,t.nicks.clear(),this.nicksChanged(Rt),this.push(Rt,"part",`* You have left ${Rt}`),this.push("status","info",`* Disconnected from ${Rt}`),this.livingSeenAt=0,this.livingOnce=!1,this.renameMe(Pl)}connectDialog(t){var u;(u=this.wm.get("mirc-connect"))==null||u.close();const e=G("form",{class:"mirc-connect"}),n=G("input",{type:"text",value:ev(),maxlength:"16",spellcheck:"false",autocomplete:"nickname",required:""}),i=G("input",{type:"password",maxlength:"256",autocomplete:"current-password"});Il(n),Il(i);const r=G("p",{class:"err",role:"alert"});r.textContent=t??"",r.hidden=!t,e.append(G("div",{class:"xp-dialog-body"},[G("span",{html:fi("app")}),G("div",{},[G("p",{},[`Join ${Rt}, where the living talk.`]),G("label",{},["Nickname:",n]),G("label",{},["Password (optional):",i]),G("p",{class:"hint"},["A password gives you a tripcode, the same one your blog comments carry. It is never stored."]),r])]));const a=G("div",{class:"xp-actions"}),o=G("button",{class:"xp-btn default",type:"submit"},["Connect"]),l=G("button",{class:"xp-btn",type:"button"},["Cancel"]);a.append(o,l),e.append(a);const c=this.wm.open({id:"mirc-connect",title:"mIRC Connect",icon:fi("app"),body:e,width:360,dialog:!0});l.addEventListener("click",()=>c.close()),e.addEventListener("keydown",h=>{h.key==="Escape"&&(h.preventDefault(),c.close(),this.input.focus({preventScroll:!0}))}),e.addEventListener("submit",h=>{h.preventDefault();const d=n.value.trim();if(!Qx.test(d)){r.textContent="A nickname starts with a letter and has at most 16 letters, digits or - _ [ ] { } | ^ `",r.hidden=!1,n.focus();return}Au(d),c.close(),this.dial(d,i.value||null)}),n.focus(),n.select()}dial(t,e){this.push("status","info",`* Connecting to ${Rt} as ${t}${e?" (with a tripcode)":""}`);const n=new Kx(t,e,{welcome:i=>this.welcomed(i),event:i=>this.event(i),whois:i=>this.whoisReply(i),error:(i,r)=>this.chatError(i,r),closed:(i,r,a)=>this.dropped(i,r,a)});this.link=n,n.connect()}welcomed(t){const e=this.wins[Rt];e.tab.hidden=!1,e.joined=!0,e.topic=t.topic,e.nicks.clear();for(const r of t.names)e.setNick(r.nick,r.op?"@":"");this.renameMe(t.nick);const n=this.livingOnce;this.livingOnce=!0;const i=t.backlog.filter(r=>r.at>this.livingSeenAt);if(n?this.push(Rt,"join",`* Rejoined ${Rt}`):(this.push("status","text",`* You are now known as ${t.nick} (${t.mask})`),t.op&&this.push("status","mode",`* ${In} sets mode: +o ${t.nick}`),this.push(Rt,"join",`* Now talking in ${Rt}`),this.push(Rt,"topic",`* Topic is '${t.topic}'`)),i.length){this.push(Rt,"info","*** Buffer Playback...");for(const r of i)this.event(r,!0);this.push(Rt,"info","*** Playback Complete.")}this.nicksChanged(Rt),n||this.show(Rt)}event(t,e=!1){const n=this.wins[Rt],i=(a,o)=>this.push(Rt,a,o,t.at),r=t.nick===this.me;switch(this.livingSeenAt=Math.max(this.livingSeenAt,t.at),t.kind){case"message":i(r?"own":"text",`<${t.nick}> ${t.text}`);return;case"action":i("action",`* ${t.nick} ${t.text}`);return;case"join":i("join",`* ${t.nick} (${t.mask??""}) has joined ${Rt}`),e||n.setNick(t.nick,"");break;case"part":i("part",`* ${t.nick} (${t.mask??""}) has left ${Rt}${t.text?` (${t.text})`:""}`),e||n.drop(t.nick);break;case"quit":i("quit",`* ${t.nick} (${t.mask??""}) Quit (${t.text||"Client exited"})`),e||n.drop(t.nick);break;case"kick":t.target===this.me&&!e?(i("kick",`* You were kicked from ${Rt} by ${t.nick} (${t.text})`),n.joined=!1,n.nicks.clear()):(i("kick",`* ${t.target??"?"} was kicked by ${t.nick} (${t.text})`),!e&&t.target&&n.drop(t.target));break;case"nick":r&&!e&&t.target?(i("text",`* Your nick is now ${t.target}`),this.renameMe(t.target),Au(t.target)):i("text",`* ${t.nick} is now known as ${t.target??"?"}`),!e&&t.target&&n.rename(t.nick,t.target);break;case"topic":i("topic",`* ${t.nick} changes topic to '${t.text}'`),e||(n.topic=t.text);break}e||this.nicksChanged(Rt)}whoisReply(t){const e=n=>this.push(this.view,"text",n);e(`${t.nick} is ${t.mask} * ${t.nick}`),t.trip&&e(`${t.nick} is identified by tripcode ${t.trip}`),t.op&&e(`${t.nick} is a channel operator on ${Rt}`),e(`${t.nick} signed on ${new Date(t.signon).toLocaleString()}`),e(`${t.nick} End of /WHOIS list.`)}chatError(t,e){var r;const n=this.wins[Rt],i=e.replace(/^(\S+) :/,"$1 ");this.push(n.joined?Rt:"status","error",`* ${i}`),!n.joined&&(t==="432"||t==="433")&&((r=this.link)==null||r.close(),this.link=null,this.livingOnce=!1,this.livingSeenAt=0,this.connectDialog(e.replace(/^\S+ :/,"")))}dropped(t,e,n){const i=this.wins[Rt],r=i.joined;i.joined=!1,i.nicks.clear(),this.nicksChanged(Rt),n?this.push(Rt,"info",`* ${this.me} is connected from somewhere else now (another window?); this one let go`):r&&!e&&this.push(Rt,"info",`* Disconnected${t?" (reconnecting...)":""}`),this.push("status","info",`* Disconnected from ${Rt}${t?" (reconnecting...)":""}`),t||(this.link=null,this.livingOnce=!1,this.livingSeenAt=0,this.renameMe(Pl))}renameMe(t){if(t===this.me)return;const e=this.wins[Ot];e.joined&&(e.rename(this.me,t),this.nicksChanged(Ot)),this.me=t,this.retitle()}say(t){var o,l,c,u,h,d;const e=t.trim();if(!e)return;const n=this.view,i=n===Rt&&this.wins[Rt].joined&&!!this.link,r=f=>this.push(n,"error",`* ${f}`);if(e.startsWith("/")&&!e.startsWith("//")){const[f="",...p]=e.slice(1).split(/\s+/),v=p.join(" ");switch(f.toLowerCase()){case"help":this.help();return;case"clear":this.wins[n].log.replaceChildren();return;case"quit":case"exit":this.win.close();return;case"join":{const m=(p[0]??"").toLowerCase().replace(/^#?/,"#");m===Rt||m==="#"&&n==="status"?this.joinLiving():m===Ot?(this.show(Ot),this.push(Ot,"error",`* You are already on ${Ot}`)):r(`${p[0]??"#"} Cannot join channel (this network has ${Ot} and ${Rt})`);return}case"part":case"leave":n===Rt&&this.link?this.partLiving():r(n===Ot?`${Ot} will not let you go`:"You are not on a channel");return;case"nick":p[0]?(o=this.link)!=null&&o.send({type:"nick",nick:p[0]})||r(`Join ${Rt} first to have a name (/join ${Rt})`):r("Usage: /nick ");return;case"whois":p[0]?(l=this.link)!=null&&l.send({type:"whois",nick:p[0]})||r(`${p[0]} :No such nick`):r("Usage: /whois ");return;case"me":if(!v)return;i?(c=this.link)==null||c.send({type:"say",text:v,action:!0}):n===Ot?this.refused(`* ${this.me} ${v}`,"action"):r("You are not on a channel");return;case"topic":i&&v?(u=this.link)==null||u.send({type:"topic",text:v}):i?this.push(Rt,"topic",`* Topic is '${this.wins[Rt].topic}'`):r(`${n==="status"?"":`${n} `}You're not channel operator`);return;case"kick":i&&p[0]?(h=this.link)==null||h.send({type:"kick",nick:p[0],reason:p.slice(1).join(" ")}):r(i?"Usage: /kick [reason]":"You're not channel operator");return;default:r(`${f.toUpperCase()} Unknown command`);return}}const a=e.startsWith("//")?e.slice(1):e;i?(d=this.link)!=null&&d.send({type:"say",text:a})||r("Not connected"):n===Ot&&this.wins[Ot].joined?this.refused(`<${this.me}> ${a}`,"own"):r("You are not on a channel")}refused(t,e){this.push(Ot,e,t),this.push(Ot,"error",`* ${Ot} Cannot send to channel`),this.hintedOikos||(this.hintedOikos=!0,this.push(Ot,"info",`* (the living talk in ${Rt}: /join ${Rt})`))}help(){const t=e=>this.push(this.view,"info",e);t("* Commands:"),t(`* /join ${Rt} join the living (a nick, and a password for a tripcode if you like)`),t("* /nick change your nick"),t("* /me an action"),t("* /whois who someone is (their tripcode, if they have one)"),t(`* /part leave ${Rt}`),t("* /topic, /kick for channel operators"),t("* /clear /quit")}push(t,e,n,i=Date.now()){var c;const r=this.wins[t],a=r.log,o=a.scrollHeight-a.scrollTop-a.clientHeight<24,l=G("div",{class:`mirc-row t-${e}`});for(l.append(G("span",{class:"ts"},[jx(i)]),` ${n}`),a.append(l);a.childElementCount>Zx;)(c=a.firstElementChild)==null||c.remove();o&&(a.scrollTop=a.scrollHeight),t!==this.view&&(t!=="status"&&(e==="text"||e==="action"||e==="own")?r.tab.classList.add("said"):r.tab.classList.contains("said")||r.tab.classList.add("event"))}show(t){this.view=t;for(const n of Object.values(this.wins)){const i=n.id===t;n.pane.hidden=!i,n.tab.classList.toggle("on",i),n.tab.setAttribute("aria-selected",String(i)),i&&n.tab.classList.remove("said","event")}const e=this.wins[t].log;e.scrollTop=e.scrollHeight,this.input.setAttribute("aria-label",t==="status"?"Command":`Message ${t}`),this.retitle()}nicksChanged(t){this.wins[t].render(),this.retitle()}setHung(t){this.hung!==t&&(this.hung=t,this.win.el.classList.toggle("mirc-hung",t),this.retitle())}retitle(){const t=this.wins[this.view],e=t.id===Ot?"+mnt":"+nt",n=t.id==="status"?`mIRC - [Status: ${this.me} on ${In} (${Rl})]`:t.joined?`mIRC - [${t.id} [${t.nicks.size}] [${e}]: ${t.topic}]`:`mIRC - [${t.id} (not on channel)]`;this.win.setTitle(this.hung?`${n} (Not Responding)`:n)}}class iv{constructor(t,e,n,i){I(this,"win");I(this,"statusLine");I(this,"timer",0);this.site=t,this.shell=e;const r=G("div");r.style.cssText="display:flex;flex-direction:column;min-height:0;flex:1;";const a=pu({back:()=>this.shell.back(),forward:()=>this.shell.forward(),can:()=>({back:this.shell.canBack,forward:this.shell.canForward}),up:()=>this.shell.home(),folders:()=>d.toggleAttribute("hidden")}),o=_u(t),l=mu(vn(t.accent),o,!1,()=>this.shell.open(t)).el,c=gu();r.append(Tl({File:()=>[...na(this.shell,t,!0),"sep",{label:"Properties",disabled:!0},"sep",{label:"Close",run:()=>this.win.close()}],Edit:()=>vu(this.shell,t),View:()=>bu(this.shell,{toolbar:a.el,address:l,status:c.el,up:()=>this.shell.home(),refresh:()=>this.refresh()}),Favorites:()=>Mu(this.shell),Tools:()=>yu(),Help:()=>Su(this.shell)}),a.el,l);const u=this.shell.onNav(()=>a.refresh()),h=G("div",{class:"xp-body"}),d=G("aside",{class:"xp-tasks"});h.append(d);const f=G("div",{class:"xp-content"});h.append(f),r.append(h),c.set("Done",xu(t)),r.append(c.el);const p=G("div",{class:"xp-hero",html:vn(t.accent,t.title)}),v=G("div");v.append(G("h2",{},[t.title]),G("p",{},[t.tagline])),p.append(v),f.append(p);const m=G("button",{class:"xp-preview",type:"button","aria-label":`Open ${t.title}`}),g=this.shell.screens.get(t.id);g&&m.append(g.canvas),m.append(G("span",{class:"xp-play"},[`▶ open ${t.title}`])),m.addEventListener("click",P=>this.shell.open(t,P)),f.append(m),f.append(G("p",{class:"xp-about"},[t.about]));const w=this.note();w&&f.append(G("div",{class:"xp-note"},[w]));const E=G("div",{class:"xp-actions"}),M=G("button",{class:"xp-btn default",type:"button"},[`Open ${t.title}`]);M.addEventListener("click",P=>this.shell.open(t,P));const S=G("button",{class:"xp-btn",type:"button"},["~ Home directory"]);if(S.addEventListener("click",()=>this.shell.home()),E.append(M),t.tune&&this.shell.canTune){const P=G("button",{class:"xp-btn",type:"button"},["▣ Watch it here"]);P.title="the live page, on its own screen in the room",P.addEventListener("click",()=>this.shell.tuneIn(t.id)),E.append(P)}if(t.id==="irc"){const P=G("button",{class:"xp-btn",type:"button"},["Join #aethera"]);P.title="sit in the channel, in mIRC",P.addEventListener("click",()=>this.shell.chat("#aethera")),E.append(P)}E.append(S),f.append(E);const b=[{icon:Sl(),label:`Open ${t.title}`,run:()=>this.shell.open(t)},{icon:su(),label:"Look at its screen",run:()=>this.shell.look(t.id)}];t.tune&&this.shell.canTune&&b.splice(1,0,{icon:Fx(),label:"Watch it on its screen",run:()=>this.shell.tuneIn(t.id)}),t.id==="irc"&&b.splice(1,0,{icon:fi("channel"),label:"Join #aethera in mIRC",run:()=>this.shell.chat("#aethera")}),b.push({icon:Ox(),label:"Copy address",run:()=>Al(this.shell,t)}),b.push({icon:ru(),label:"Eject tape",run:()=>this.win.close()});const R=[{icon:Zs(),label:"~ (home directory)",run:()=>this.shell.home()}];for(const P of Vx[t.id]??[]){const L=this.shell.site(P);L&&R.push({icon:vn(L.accent),label:L.title,run:()=>this.shell.play(L.id)})}const x=G("div",{class:"xp-details"});x.append(G("b",{},[t.title]));const T=G("dl");for(const[P,L]of t.details)T.append(G("dt",{},[P]),G("dd",{},[L]));x.append(T),this.statusLine=G("div",{class:"status"}),x.append(this.statusLine),d.append(ys("Tape Tasks",b,!0),ys("Other Places",R),ys("Details",x)),this.win=n.open({id:`site:${t.id}`,title:`${t.title} — ${o.replace(/^https?:\/\//,"")}`,icon:vn(t.accent),body:r,width:Math.round(Math.min(760,Math.max(440,innerWidth*.5))),dock:"right",onClose:()=>{u(),clearInterval(this.timer),g==null||g.canvas.remove(),i()}}),this.win.el.style.height="min(640px, calc(100% - 24px))",this.refresh(),this.timer=window.setInterval(()=>this.refresh(),2e3)}note(){const t=this.site;return t.id==="dreams"?"Opening dreams wakes the dreamer: a GPU starts up while anyone is watching and goes back to sleep after. The frame here is the last one the chronicle kept.":t.id==="syrinx"?"Syrinx makes sound once you wake it. The creature here is read from this browser; nobody else sees yours.":t.group==="wired"?`${t.title} is not on this server; it opens in a new window.`:null}refresh(){const{feeds:t}=this.shell;let e=!1,n="";switch(this.site.id){case"dreams":case"dreams-api":{const r=t.dreams.value;e=r.known&&r.awake,n=r.known?r.awake?`awake · frame ${r.frame.toLocaleString("en-US")}${r.viewers?` · ${r.viewers} watching`:""}`:"asleep":"status unknown";break}case"chronicle":{const r=t.chronicle.value;e=r.known&&r.eras.some(a=>a.open),n=r.known?`${r.eraCount||r.eras.length} eras recorded`:"reading the core…";break}case"irc":{const r=t.irc.value;e=r.connected,n=r.connected?"live on #aethera":"connecting…";break}case"syrinx":{const r=t.creature.value;e=!!r,n=r?`yours: ${r.name}`:"not woken in this browser";break}default:return}this.statusLine.className=`status${e?" on":""}`,this.statusLine.innerHTML="";const i=this.statusLine.querySelector("span");i&&(i.textContent=n)}}const Cu=2;class sv{constructor(t,e,n,i){I(this,"el");I(this,"menu");I(this,"buttons");I(this,"clock");I(this,"moonEl");I(this,"start");I(this,"tip");I(this,"balloonEl");I(this,"balloonTimer",0);I(this,"dialogEl",null);this.root=t,this.shell=e,this.wm=n,this.hooks=i,this.el=G("nav",{class:"xp-taskbar","aria-label":"taskbar"}),this.start=G("button",{class:"xp-start",type:"button","aria-haspopup":"menu","aria-expanded":"false",html:`${Js(22,!1)}start`}),this.buttons=G("div",{class:"xp-taskbuttons"});const r=G("div",{class:"xp-tray"}),a=G("span",{class:"tray-icon net",title:"Connected to the Wired",html:Rx()});this.moonEl=G("span",{class:"tray-icon dream",title:"dreams",html:nu(!1)}),this.clock=G("span",{class:"clock"}),r.append(a,this.moonEl,this.clock),this.el.append(this.start,this.buttons,r),t.append(this.el),this.menu=this.buildMenu(),t.append(this.menu),this.start.addEventListener("click",()=>this.toggleMenu()),t.addEventListener("pointerdown",o=>{const l=o.target;!this.menu.hidden&&!l.closest(".xp-startmenu")&&!l.closest(".xp-start")&&!l.closest(".xp-menu")&&this.toggleMenu(!1)}),this.el.addEventListener("contextmenu",o=>{o.preventDefault(),o.stopPropagation();const l=o.target.closest(".xp-taskbtn"),c=l?this.wm.list.find(u=>u.opts.id===l.dataset.win):void 0;Ms(c?c.systemMenu():this.barMenu(),o.clientX,o.clientY)}),a.addEventListener("click",()=>this.balloon("Connected to the Wired",`${e.dir.sites.length} screens · 1 VCR · signal: present day, present time`,this.trayAnchor())),this.tip=G("div",{class:"xp-tip",role:"tooltip",hidden:""}),this.balloonEl=G("div",{class:"xp-balloon",role:"status",hidden:""}),t.append(this.tip,this.balloonEl),n.on(()=>this.renderButtons()),e.feeds.dreams.on(o=>{this.moonEl.innerHTML=nu(o.awake),this.moonEl.title=o.known?o.awake?`dreams: awake · frame ${o.frame.toLocaleString("en-US")}`:"dreams: asleep":"dreams: no answer"}),this.tick(),setInterval(()=>this.tick(),15e3)}tick(){this.clock.textContent=new Date().toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})}trayAnchor(){const t=this.el.getBoundingClientRect();return{x:t.right-60,y:t.top}}toggleMenu(t){var n;const e=t??this.menu.hidden;e||pi(),this.menu.hidden=!e,this.start.classList.toggle("open",e),this.start.setAttribute("aria-expanded",String(e)),e&&((n=this.menu.querySelector("a,button"))==null||n.focus())}barMenu(){return[{label:"Toolbars",disabled:!0,submenu:()=>[]},"sep",{label:"Cascade Windows",disabled:!0},{label:"Tile Windows Horizontally",disabled:!0},{label:"Tile Windows Vertically",disabled:!0},{label:"Show the Desktop",run:()=>this.showDesktop(),disabled:!this.wm.list.some(t=>!t.minimized&&!t.opts.dialog)},"sep",{label:"Task Manager",disabled:!0},"sep",{label:"Lock the Taskbar",checked:!0,disabled:!0},{label:"Properties",disabled:!0}]}showDesktop(){for(const t of[...this.wm.list])!t.opts.dialog&&!t.minimized&&t.minimize()}buildMenu(){const t=G("div",{class:"xp-startmenu",role:"menu",hidden:""}),e=G("header");e.append(G("span",{class:"avatar",html:Js(40)}),G("span",{},["guest"])),t.append(e,G("div",{class:"orange","aria-hidden":"true"}));const n=G("div",{class:"cols"}),i=G("ul",{class:"left"}),r=G("ul",{class:"right"}),a=(S,b)=>{const R=G("li");return R.append(b),S.append(R),b},o=(S,b,R,x,T,P="")=>{const L=G("button",{type:"button",role:"menuitem",class:P,html:`${b}${x!==null?"":""}`});L.querySelector(".tt").textContent=R;const F=L.querySelector("small");return F&&x&&(F.textContent=x),L.addEventListener("click",()=>{this.toggleMenu(!1),T()}),a(S,L)},l=(S,b,R,x)=>{const T=G("a",{href:x,role:"menuitem",html:`${b}`});T.querySelector("span").textContent=R,a(S,T)},c=(S,b,R,x,T)=>{const P=G("button",{type:"button",role:"menuitem",class:`cascade ${x}`,"aria-haspopup":"menu","data-menu-owner":"",html:`${b}`});P.querySelector(".tt").textContent=R;const L=()=>{P.classList.add("open"),fu(T(),P,()=>P.classList.remove("open"))};return P.addEventListener("click",L),P.addEventListener("pointerenter",L),a(S,P)},u=S=>S.append(G("li",{class:"sep",role:"separator"}));t.addEventListener("pointerover",S=>{const b=S.target.closest("li > a, li > button");b&&!b.classList.contains("cascade")&&pi()});const h=S=>({label:S.title,icon:vn(S.accent),run:()=>{this.toggleMenu(!1),this.shell.play(S.id)}});o(i,fi(),"mIRC","#aethera",()=>this.shell.chat(),"pinned");const d=this.shell.dir.sites.filter(S=>S.group==="here");d.slice(0,Cu).forEach(S=>o(i,vn(S.accent,S.title),S.title,S.kind,()=>this.shell.play(S.id),"pinned")),u(i),d.slice(Cu).forEach(S=>o(i,vn(S.accent,S.title),S.title,null,()=>this.shell.play(S.id)));const f=G("li",{class:"allprog"});i.append(G("li",{class:"sep",role:"separator"}),f);const p=G("button",{type:"button",role:"menuitem",class:"cascade","aria-haspopup":"menu","data-menu-owner":"",html:`All Programs${Bx()}`}),v=()=>{p.classList.add("open"),fu([...this.shell.dir.sites.filter(S=>S.group==="here").map(h),"sep",...this.shell.dir.sites.filter(S=>S.group==="wired").map(h),"sep",{label:"~ (home directory)",icon:Zs(),run:()=>{this.toggleMenu(!1),this.shell.home()}}],p,()=>p.classList.remove("open"))};p.addEventListener("click",v),p.addEventListener("pointerenter",v),f.append(p),o(r,Zs(),"My Tapes",null,()=>this.shell.home(),"strong");const m=this.shell.dir.posts.slice(0,8);m.length&&c(r,vs("txt"),"My Recent Transmissions","strong",()=>m.map(S=>({label:S.title,icon:vs("txt"),run:()=>location.assign(S.href)}))),u(r);const g=this.shell.dir.sites.filter(S=>S.group==="wired");for(const S of g)o(r,vn(S.accent),S.title,null,()=>this.shell.play(S.id));u(r);for(const S of this.shell.dir.files)l(r,vs(S.title.split(".").pop()??"txt"),S.title,S.href);n.append(i,r),t.append(n);const w=G("div",{class:"foot"}),E=G("button",{type:"button",html:`${lu()}Log Off`});E.addEventListener("click",()=>{this.toggleMenu(!1),this.logOffDialog()});const M=G("button",{type:"button",html:`${ou()}Turn Off Computer`});return M.addEventListener("click",()=>{this.toggleMenu(!1),this.shutdownDialog()}),w.append(E,M),t.append(w),t}dismiss(){return this.dialogEl?(this.dialogEl.remove(),this.dialogEl=null,this.start.focus(),!0):!1}bigDialog(t,e){var l,c;(l=this.dialogEl)==null||l.remove();const n=G("div",{class:"xp-shutdown",role:"dialog","aria-modal":"true","aria-label":t});this.dialogEl=n;const i=G("div",{class:"panel"});i.append(G("header",{html:`${Js(28,!1)}`})),i.querySelector("header span").textContent=t;const r=G("div",{class:"choices"});for(const u of e){const h=G("button",{type:"button",class:u.cls,html:`${u.icon}${u.label}`}),d=u.run;d?h.addEventListener("click",()=>{n.remove(),this.dialogEl=null,d()}):h.disabled=!0,r.append(h)}i.append(r);const a=G("div",{class:"foot"}),o=G("button",{class:"xp-btn",type:"button"},["Cancel"]);o.addEventListener("click",()=>this.dismiss()),a.append(o),i.append(a),n.append(i),this.root.append(n),(c=r.querySelector("button:not(:disabled):last-of-type, button:not(:disabled)"))==null||c.focus()}logOffDialog(){this.bigDialog("Log Off æthera",[{cls:"switch",icon:cu(),label:"Switch User"},{cls:"logoff",icon:lu(),label:"Log Off",run:()=>this.hooks.logOff()}])}shutdownDialog(){var t,e;this.bigDialog("Turn off computer",[{cls:"standby",icon:kx(),label:"Stand By",run:()=>this.hooks.standby(!0)},{cls:"off",icon:ou(),label:"Turn Off",run:()=>this.hooks.turnOff()},{cls:"restart",icon:cu(),label:"Restart",run:()=>this.hooks.restart()}]),(e=(t=this.dialogEl)==null?void 0:t.querySelector(".off"))==null||e.focus()}renderButtons(){this.buttons.replaceChildren();for(const t of this.wm.list){if(t.opts.dialog)continue;const e=G("button",{class:`xp-taskbtn${this.wm.activeWindow===t&&!t.minimized?" active":""}`,type:"button","data-win":t.opts.id,html:`${t.opts.icon}`});e.querySelector("span").textContent=t.opts.title,e.title=t.opts.title,e.addEventListener("click",()=>{t.minimized?t.restore():this.wm.activeWindow===t?t.minimize():t.focus()}),this.buttons.append(e)}}showTip(t,e,n,i){const r=t?{title:t.title,text:t.tagline}:e;if(!r){this.tip.hidden=!0;return}this.tip.innerHTML="",this.tip.querySelector("b").textContent=r.title,this.tip.querySelector("span").textContent=r.text,this.tip.hidden=!1;const a=this.root.getBoundingClientRect(),o=this.tip.offsetWidth;this.tip.style.left=`${Math.min(n+14,a.width-o-6)}px`,this.tip.style.top=`${Math.min(i+20,a.height-70)}px`}balloon(t,e,n,i=7e3,r=!1){var f;const a=this.balloonEl;a.innerHTML=`${au()}`,a.querySelector("b span").textContent=t,a.querySelector(".msg").textContent=e,(f=a.querySelector(".x"))==null||f.addEventListener("click",()=>a.hidden=!0),a.hidden=!1;const o=this.root.getBoundingClientRect(),l=n??this.trayAnchor(),c=a.offsetWidth,u=a.offsetHeight,h=Math.max(6,Math.min(l.x-30,o.width-c-6)),d=r?l.y+u+24>o.height-30:l.y-u-20>0;a.classList.toggle("above",d),a.classList.toggle("below",!d),a.style.left=`${h}px`,a.style.top=`${d?l.y-u-18:l.y+18}px`,a.style.setProperty("--tail",`${Math.max(12,Math.min(c-30,l.x-h))}px`),clearTimeout(this.balloonTimer),this.balloonTimer=window.setTimeout(()=>a.hidden=!0,i)}hideBalloon(){this.balloonEl.hidden=!0}}const Ul=300,Fl=180,rv=["n","s","e","w","ne","nw","se","sw"];class av{constructor(t,e){I(this,"el");I(this,"minimized",!1);I(this,"closed",!1);var a,o,l,c,u;this.opts=t,this.wm=e;const n=document.createElement("section");n.className=`xp-window${t.dialog?" xp-dialog":""}`,n.setAttribute("role",t.dialog?"alertdialog":"dialog"),n.setAttribute("aria-label",t.title),n.innerHTML=` +`}let $i=null,wn=null,tc=null;function ly(s){$i=s,s.addEventListener("pointerdown",t=>{if(!wn)return;const e=t.target;e.closest(".xp-menu")||e.closest("[data-menu-owner]")||bi()},!0)}function cy(){return wn!==null}function bi(){if(!wn)return!1;let s=wn;for(;s;)s.el.remove(),s=s.child;wn=null;const t=tc;return tc=null,t==null||t(),!0}function nd(s,t){const e=V("div",{class:"xp-menu",role:"menu"}),n=[];for(const i of s){if(i==="sep"){e.append(V("div",{class:"xp-menu-sep",role:"separator"}));continue}const r=V("button",{type:"button",role:i.checked!==void 0?"menuitemcheckbox":"menuitem",class:`${i.bold?"bold ":""}${i.submenu?"has-sub":""}`.trim(),html:`${i.icon??""}`});r.querySelector(".lb").textContent=i.label,r.querySelector(".sc").textContent=i.shortcut??"",i.checked!==void 0&&(r.setAttribute("aria-checked",String(i.checked)),i.checked&&r.classList.add("checked")),i.disabled&&(r.setAttribute("aria-disabled","true"),r.tabIndex=-1);const o=()=>{var u;if(!i.submenu||i.disabled)return;const a=ec(e);if(!a||((u=a.child)==null?void 0:u.el.dataset.for)===i.label)return;nc(a);const l=r.getBoundingClientRect(),c=id(nd(i.submenu(),a),l.right-3,l.top-3,l.left+3);c.dataset.for=i.label,a.child={el:c,child:null}};r.addEventListener("pointerenter",()=>{const a=ec(e);i.submenu?o():a&&nc(a)}),r.addEventListener("click",a=>{var l,c,u,h;if(a.stopPropagation(),!i.disabled){if(i.submenu){o(),(u=(c=(l=ec(e))==null?void 0:l.child)==null?void 0:c.el.querySelector("button:not([aria-disabled])"))==null||u.focus();return}bi(),(h=i.run)==null||h.call(i)}}),e.append(r),n.push(r)}return e.addEventListener("keydown",i=>{var a,l,c;const r=n.filter(u=>u.getAttribute("aria-disabled")!=="true"),o=r.indexOf(document.activeElement);if(i.key==="ArrowDown"||i.key==="ArrowUp"){i.preventDefault(),i.stopPropagation();const u=r[(o+(i.key==="ArrowDown"?1:-1)+r.length)%r.length];u==null||u.focus()}else i.key==="ArrowRight"&&((a=r[o])!=null&&a.classList.contains("has-sub"))?(i.preventDefault(),i.stopPropagation(),(l=r[o])==null||l.click()):i.key==="ArrowLeft"&&t&&(i.preventDefault(),i.stopPropagation(),nc(t),(c=t.el.querySelector("button.has-sub"))==null||c.focus())}),e}function ec(s){let t=wn;for(;t;){if(t.el===s)return t;t=t.child}return null}function nc(s){let t=s.child;for(;t;)t.el.remove(),t=t.child;s.child=null}function id(s,t,e,n=t){if(!$i)throw new Error("menu host not set");s.style.left="0px",s.style.top="0px",$i.append(s);const i=$i.getBoundingClientRect(),r=s.offsetWidth,o=s.offsetHeight;let a=t-i.left,l=e-i.top;return a+r>i.width-2&&(a=Math.max(2,n-i.left-r)),l+o>i.height-32&&(l=Math.max(2,i.height-32-o)),s.style.left=`${Math.round(a)}px`,s.style.top=`${Math.round(l)}px`,s}function Us(s,t,e,n,i=!1){var o;bi();const r=nd(s,null);wn={el:r,child:null},id(r,t,e),tc=n??null,i&&((o=r.querySelector("button:not([aria-disabled])"))==null||o.focus())}function sd(s,t,e){const n=t.getBoundingClientRect();if(Us(s,n.right-2,n.top-3,e),wn){const i=$i==null?void 0:$i.getBoundingClientRect();i&&n.right+wn.el.offsetWidth>i.right&&(wn.el.style.left=`${Math.max(2,n.left-i.left-wn.el.offsetWidth+2)}px`)}}function hy(s,t){s.addEventListener("contextmenu",e=>{const n=e.target;if(n.closest("input, textarea, [contenteditable], .oikos-tube"))return;const i=t(n,e);if(i!==null){if(e.preventDefault(),!i.length){bi();return}Us(i,e.clientX,e.clientY)}})}function V(s,t={},e=[]){const n=document.createElement(s);for(const[i,r]of Object.entries(t))i==="html"?n.innerHTML=r:n.setAttribute(i,r);for(const i of e)n.append(i);return n}function ic(s){const t=V("div",{class:"xp-menubar",role:"menubar"});let e=null;const n=(i,r,o)=>{var c;const a=(c=s[r])==null?void 0:c.call(s);if(!a)return;const l=i.getBoundingClientRect();Us(a,l.left,l.bottom,()=>{i.classList.remove("open"),e===i&&(e=null)},o),i.classList.add("open"),e=i};for(const i of Object.keys(s)){const r=V("button",{type:"button",class:"xp-menutitle",role:"menuitem","aria-haspopup":"menu","data-menu-owner":""},[i]);r.addEventListener("click",()=>{e===r?bi():n(r,i,!1)}),r.addEventListener("pointerenter",()=>{e&&e!==r&&cy()&&n(r,i,!1)}),r.addEventListener("keydown",o=>{(o.key==="ArrowDown"||o.key==="Enter"||o.key===" ")&&(o.preventDefault(),n(r,i,!0))}),t.append(r)}return t.append(V("span",{class:"xp-throbber",html:mr(18,!0)})),t}function rd(s){const t=V("div",{class:"xp-toolbar"}),e=(o,a,l,c=!0)=>{const u=V("button",{class:"xp-tb",type:"button",title:a,html:`${o}${c?`${a}`:""}`});return l?u.addEventListener("click",l):u.disabled=!0,t.append(u),u},n=e(j_(),"Back",s.back),i=e(Q_(),"Forward",s.forward,!1);e(ty(),"Up",s.up,!1),t.append(V("span",{class:"xp-tb-sep"})),e(ey(),"Search",s.search),e(ny(),"Folders",s.folders);const r=()=>{if(!s.can)return;const o=s.can();n.disabled=!s.back||!o.back,i.disabled=!s.forward||!o.forward};return r(),{el:t,refresh:r}}function od(s,t,e,n){const i=V("div",{class:"xp-address"});i.append(V("span",{class:"lbl"},["Address"]));const r=V("label",{class:"field",html:s});let o=null;e?(o=V("input",{type:"text",value:t,"aria-label":"Address",spellcheck:"false"}),o.style.cssText="flex:1;min-width:0;border:0;outline:0;font:inherit;background:transparent;",r.append(o),o.addEventListener("keydown",l=>{l.key==="Enter"&&o&&(n==null||n(o.value)),l.stopPropagation()})):r.append(V("span",{},[t])),i.append(r);const a=V("button",{class:"go",type:"button",html:`${iy()}Go`});return a.addEventListener("click",()=>n==null?void 0:n((o==null?void 0:o.value)??t)),i.append(a),{el:i,input:o}}function Fs(s,t,e=!1){const n=V("section",{class:`xp-taskgroup${e?" primary":""}`}),i=V("header",{role:"button",tabindex:"0","aria-expanded":"true"},[s]),r=()=>{n.classList.toggle("collapsed"),i.setAttribute("aria-expanded",String(!n.classList.contains("collapsed")))};i.addEventListener("click",r),i.addEventListener("keydown",a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),r())}),n.append(i);const o=V("div",{class:"xp-taskbody"});if(t instanceof HTMLElement)o.append(t);else{const a=V("ul");for(const l of t){const c=V("li");let u;if(l.href)u=V("a",{class:"xp-link",href:l.href,html:`${l.icon}`}),l.external&&(u.setAttribute("target","_blank"),u.setAttribute("rel","noopener"));else{u=V("button",{class:"xp-link",type:"button",html:`${l.icon}`});const d=l.run;d?u.addEventListener("click",d):u.setAttribute("aria-disabled","true")}const h=u.querySelector("span");h&&(h.textContent=l.label),c.append(u),a.append(c)}o.append(a)}return n.append(o),n}function ad(){const s=V("div",{class:"xp-statusbar",role:"status"}),t=V("span"),e=V("span",{class:"zone"});return s.append(t,e),{el:s,set(n,i){t.textContent=n;const[r,o]=i==="computer"?[$u(),"My Computer"]:[Xu(),"Internet"];e.innerHTML=`${r}${o}`}}}const uy={dreams:["chronicle","dreams-api","dream_gen"],chronicle:["dreams","dream_gen"],"dreams-api":["dreams","chronicle"],dream_gen:["dreams","chronicle"],transmissions:["irc","syrinx"],apeiron:["dreams","syrinx"],syrinx:["apeiron","afterlife"],afterlife:["syrinx","apeiron"],irc:["transmissions","parlor"],parlor:["irc","transmissions"]};function ld(s){return/^https?:/.test(s.href)?s.href:`${location.origin}${s.href}`}function cd(s){return s.group==="here"?"computer":"internet"}function sc(s,t){var i;const e=ld(t),n=(i=navigator.clipboard)==null?void 0:i.writeText(e);if(!n){s.balloon("Could not copy",e);return}n.then(()=>s.balloon("Copied",e),()=>s.balloon("Could not copy",e))}function wo(s,t,e=!1){const n=[];return e?n.push({label:`Open ${t.title}`,bold:!0,run:()=>s.open(t)}):(n.push({label:"Play",bold:!0,run:()=>s.play(t.id)}),n.push({label:`Go to ${t.title}`,run:()=>s.open(t)})),n.push({label:"Look at its screen",run:()=>s.look(t.id),disabled:!s.canTune}),t.tune&&n.push({label:"Watch it here",run:()=>s.tuneIn(t.id),disabled:!s.canTune}),t.id==="irc"&&n.push({label:"Join in mIRC",run:()=>s.chat("#aethera")}),n.push("sep",{label:"Copy Address",run:()=>sc(s,t)}),e||n.push("sep",{label:"Properties",run:()=>s.play(t.id)}),n}function hd(s,t){return[{label:"Undo",shortcut:"Ctrl+Z",disabled:!0},"sep",{label:"Cut",shortcut:"Ctrl+X",disabled:!0},{label:"Copy",shortcut:"Ctrl+C",disabled:!t,run:()=>t&&sc(s,t)},{label:"Paste",shortcut:"Ctrl+V",disabled:!0},"sep",{label:"Select All",shortcut:"Ctrl+A",disabled:!0},{label:"Invert Selection",disabled:!0}]}function ud(s){return[{label:"Add to Favorites...",disabled:!0},{label:"Organize Favorites...",disabled:!0},"sep",...s.dir.sites.filter(t=>t.group==="here").map(t=>({label:t.title,icon:Sn(t.accent),run:()=>s.play(t.id)}))]}function dd(){return[{label:"Map Network Drive...",disabled:!0},{label:"Disconnect Network Drive...",disabled:!0},{label:"Synchronize...",disabled:!0},"sep",{label:"Folder Options...",disabled:!0}]}function fd(s){return[{label:"Help and Support Center",disabled:!0},"sep",{label:"About æthera",run:()=>s.about()}]}function rc(s){s.hidden=!s.hidden}function pd(s,t){return[{label:"Toolbars",submenu:()=>[{label:"Standard Buttons",checked:!t.toolbar.hidden,run:()=>rc(t.toolbar)},{label:"Address Bar",checked:!t.address.hidden,run:()=>rc(t.address)}]},{label:"Status Bar",checked:!t.status.hidden,run:()=>rc(t.status)},...t.views?["sep",...t.views]:[],"sep",{label:"Go To",submenu:()=>[{label:"Back",shortcut:"Alt+Left",disabled:!s.canBack,run:()=>s.back()},{label:"Forward",shortcut:"Alt+Right",disabled:!s.canForward,run:()=>s.forward()},{label:"Up One Level",disabled:!t.up,run:()=>{var e;return(e=t.up)==null?void 0:e.call(t)}},"sep",{label:"Home Page",shortcut:"Alt+Home",run:()=>s.home()}]},{label:"Refresh",shortcut:"F5",run:()=>t.refresh()}]}class dy{constructor(t,e){R(this,"win",null);R(this,"selected",null);R(this,"tiles",new Map);R(this,"tasks",null);R(this,"status",null);R(this,"content",null);R(this,"view","tiles");this.shell=t,this.wm=e}get isOpen(){return!!this.win&&!this.win.closed}open(){var u;if(this.isOpen&&this.win){this.win.minimized?this.win.restore():this.win.focus();return}this.tiles.clear();const t=V("div");t.style.cssText="display:flex;flex-direction:column;min-height:0;flex:1;";const{dir:e}=this.shell,n=rd({back:()=>this.shell.back(),forward:()=>this.shell.forward(),can:()=>({back:this.shell.canBack,forward:this.shell.canForward}),search:()=>{var h;return(h=i.input)==null?void 0:h.focus()},folders:()=>{var h;return(h=this.tasks)==null?void 0:h.toggleAttribute("hidden")}}),i=od(pr(),"~/æthera",!0,h=>this.go(h));(u=i.input)==null||u.addEventListener("input",()=>{var h;return this.filter(((h=i.input)==null?void 0:h.value)??"")}),this.status=ad();const r=()=>this.selected?this.shell.site(this.selected)??null:null;t.append(ic({File:()=>this.fileMenu(),Edit:()=>hd(this.shell,r()),View:()=>{var h;return pd(this.shell,{toolbar:n.el,address:i.el,status:((h=this.status)==null?void 0:h.el)??V("div"),views:this.viewItems(),refresh:()=>{i.input&&(i.input.value="~/æthera"),this.filter("")}})},Favorites:()=>ud(this.shell),Tools:()=>dd(),Help:()=>fd(this.shell)}),n.el,i.el);const o=this.shell.onNav(()=>n.refresh()),a=V("div",{class:"xp-body"});this.tasks=V("aside",{class:"xp-tasks"}),a.append(this.tasks);const l=V("div",{class:`xp-content view-${this.view}`,role:"listbox","aria-label":"tapes"});this.content=l,l.addEventListener("pointerdown",h=>{h.button===0&&!h.target.closest(".xp-tile")&&this.select(null)});const c=(h,d)=>{if(!d.length)return;l.append(V("h3",{class:"xp-group-head"},[h]));const f=V("div",{class:"xp-tiles"});for(const p of d)f.append(this.tile(p));l.append(f)};if(c("Tapes Stored on This Server",e.sites.filter(h=>h.group==="here")),c("Other Places on the Wired",e.sites.filter(h=>h.group==="wired")),e.files.length){l.append(V("h3",{class:"xp-group-head"},["Files"]));const h=V("div",{class:"xp-tiles"});for(const d of e.files){const f=d.title.split(".").pop()??"txt",p=V("a",{class:"xp-tile",href:d.href,html:`${Ns(f)}`});p.querySelector(".t").textContent=d.title,p.querySelector(".k").textContent=d.about,h.append(p)}l.append(h)}a.append(l),t.append(a),t.append(this.status.el),this.win=this.wm.open({id:"home",title:"~ (home directory)",icon:pr(),body:t,width:Math.round(Math.min(680,Math.max(420,innerWidth*.46))),dock:"left",onClose:()=>{o(),this.win=null,this.content=null,this.selected=null}}),this.win.el.style.height="min(560px, calc(100% - 40px))",this.renderTasks(),this.renderStatus()}close(){var t;(t=this.win)==null||t.close()}fileMenu(){const t=this.selected?this.shell.site(this.selected):void 0;return[...t?wo(this.shell,t):[{label:"Play",bold:!0,disabled:!0}],"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"Create Shortcut",disabled:!0},{label:"Delete",disabled:!0},{label:"Rename",disabled:!0},"sep",{label:"Close",run:()=>this.close()}]}viewItems(){const t=e=>()=>{this.view=e,this.content&&(this.content.className=`xp-content view-${e}`)};return[{label:"Tiles",checked:this.view==="tiles",run:t("tiles")},{label:"Icons",checked:this.view==="icons",run:t("icons")},{label:"List",checked:this.view==="list",run:t("list")}]}contextFor(t){const e=this.shell.site(t);return e?(this.select(t,!0),wo(this.shell,e)):null}blankMenu(){return[{label:"View",submenu:()=>this.viewItems()},{label:"Arrange Icons By",disabled:!0,submenu:()=>[]},"sep",{label:"Refresh",run:()=>this.filter("")},"sep",{label:"Paste",disabled:!0},{label:"Paste Shortcut",disabled:!0},"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"Properties",disabled:!0}]}tile(t){const e=V("button",{class:"xp-tile",type:"button",role:"option","data-id":t.id,html:`${Sn(t.accent,t.title)}`});return e.querySelector(".t").textContent=t.title,e.querySelector(".k").textContent=t.kind,e.querySelector(".d").textContent=t.tagline,e.title=t.tagline,e.addEventListener("click",()=>this.select(t.id,!0)),e.addEventListener("dblclick",()=>this.shell.play(t.id)),e.addEventListener("keydown",n=>{if(n.key==="Enter"&&this.shell.play(t.id),["ArrowRight","ArrowDown","ArrowLeft","ArrowUp"].includes(n.key)){n.preventDefault(),n.stopPropagation();const i=[...this.tiles.values()].filter(a=>!a.hidden),r=i.indexOf(e),o=i[(r+(n.key==="ArrowRight"||n.key==="ArrowDown"?1:-1)+i.length)%i.length];o==null||o.focus(),o!=null&&o.dataset.id&&this.select(o.dataset.id,!0)}}),e.addEventListener("pointerup",n=>{n.pointerType==="touch"&&this.selected===t.id&&e.dataset.armed&&this.shell.play(t.id),e.dataset.armed="1"}),this.tiles.set(t.id,e),e}select(t,e=!1){if(t!==this.selected){this.selected=t;for(const[n,i]of this.tiles)i.classList.toggle("selected",n===t),i.setAttribute("aria-selected",String(n===t)),n!==t&&delete i.dataset.armed;e&&this.shell.select(t),this.renderTasks(),this.renderStatus()}}filter(t){const e=t.replace(/^~\/?(æthera)?\/?/i,"").trim().toLowerCase();for(const[n,i]of this.tiles){const r=this.shell.site(n);i.hidden=!!e&&!`${r==null?void 0:r.title} ${r==null?void 0:r.kind} ${r==null?void 0:r.tagline}`.toLowerCase().includes(e)}}go(t){const e=t.replace(/^~\/?(æthera)?\/?/i,"").trim().toLowerCase();if(!e)return;const n=this.shell.dir.sites.find(i=>i.title.toLowerCase()===e)??this.shell.dir.sites.find(i=>`${i.title} ${i.kind} ${i.tagline}`.toLowerCase().includes(e));n?this.shell.play(n.id):this.shell.balloon("Cannot find it",`There is no tape called “${t}” in ~.`)}renderTasks(){if(!this.tasks)return;const t=this.selected?this.shell.site(this.selected):void 0,e=t?[{icon:Ql(),label:"Play this tape",run:()=>this.shell.play(t.id)},{icon:Ku(),label:"Look at its screen",run:()=>this.shell.look(t.id)},{icon:Xu(),label:`Go to ${t.title}`,run:()=>this.shell.open(t)}]:[{icon:Ql(),label:"Select a tape to play it"},{icon:Zu(),label:"Eject the tape",run:()=>this.shell.eject()}],n=[{icon:$u(),label:"æthera",href:"/"},{icon:Ns("xml"),label:"feed.xml",href:"/feed.xml"},{icon:Ns("txt"),label:"llms.txt",href:"/llms.txt"}],i=V("div",{class:"xp-details"});if(t){i.append(V("b",{},[t.title]));const r=V("dl");for(const[o,a]of t.details)r.append(V("dt",{},[o]),V("dd",{},[a]));i.append(r)}else{const r=this.shell.dir.sites.filter(o=>o.group==="here").length;i.append(V("b",{},["~"]),V("span",{},[`home directory · ${r} tapes here, ${this.shell.dir.sites.length-r} elsewhere on the Wired`]))}this.tasks.replaceChildren(Fs(t?"Tape Tasks":"System Tasks",e,!0),Fs("Other Places",n),Fs("Details",i))}renderStatus(){var n;const t=this.selected?this.shell.site(this.selected):void 0,e=this.shell.dir.sites.length+this.shell.dir.files.length;(n=this.status)==null||n.set(t?`${t.title} — ${t.tagline}`:`${e} objects`,t?cd(t):"computer")}}const fy=4001,py=4002,my=s=>s>=4e3&&s<5e3,gy=4;class vy{constructor(t,e,n){R(this,"ws",null);R(this,"tries",0);R(this,"timer",0);R(this,"wanted",!1);R(this,"welcomed",!1);R(this,"refused",!1);this.nick=t,this.password=e,this.on=n}get open(){var t;return this.welcomed&&((t=this.ws)==null?void 0:t.readyState)===WebSocket.OPEN}connect(){this.wanted=!0,this.refused=!1,clearTimeout(this.timer);const t=location.protocol==="https:"?"wss":"ws";let e;try{e=new WebSocket(`${t}://${location.host}/ws/chat`)}catch{this.on.closed(!1,!1,!1);return}this.ws=e,this.welcomed=!1,e.onopen=()=>{e.send(JSON.stringify({type:"hello",nick:this.nick,password:this.password||null}))},e.onmessage=n=>{let i;try{i=JSON.parse(String(n.data))}catch{return}switch(i.type){case"welcome":{const r=i;this.welcomed=!0,this.tries=0,this.nick=r.nick,this.on.welcome(r);break}case"event":{const r=i.event;if(!r)break;r.kind==="nick"&&r.nick===this.nick&&r.target&&(this.nick=r.target),this.on.event(r);break}case"whois":this.on.whois(i);break;case"error":{this.welcomed||(this.refused=!0),this.on.error(String(i.code??""),String(i.text??""));break}}},e.onclose=n=>{if(this.ws!==e)return;this.ws=null;const i=n.code===fy,r=n.code===py,o=this.wanted&&!my(n.code)&&!this.refused&&this.triesthis.connect(),2e3*2**(this.tries-1))}}send(t){if(!this.open||!this.ws)return!1;try{return this.ws.send(JSON.stringify(t)),!0}catch{return!1}}part(t="Leaving"){if(this.open&&this.ws)try{this.ws.send(JSON.stringify({type:"part",reason:t}))}catch{}this.close()}close(){this.wanted=!1,clearTimeout(this.timer);const t=this.ws;if(this.ws=null,this.welcomed=!1,this.password=null,t){t.onclose=null;try{t.close()}catch{}}}}const kt="#aethera",Pt="#oikos",Fn="irc.aetherawi.red",oc=6667,ac="guest",md="always falling apart",xy=600,_y=6e3,gd="oikos-mirc-nick",yy=/^[A-Za-z[\]\\`_^{|}][A-Za-z0-9[\]\\`_^{|}-]{0,15}$/,lc=["~","&","@","%","+"];function My(s){const t=new Date(s);return`[${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}]`}function by(s){return Date.now()-(performance.now()-s)}function cc(s){const t=Qn(s)||"anon",e=Ho(t),n=["dialup.wired.net","cable.aether.org","dsl.lain.jp","adsl.nowhere.nu","res.navi.co"],i=n[Math.floor(e*n.length)]??n[0],r=Math.floor(e*251)+2,o=Math.floor(Ho(`${t}.`)*251)+2;return`~${t.slice(0,9).toLowerCase()}@ppp-${r}-${o}.${i}`}function Qn(s){return s.replace(/^[~&@%+]+/,"")}function vd(s){const t=s.charAt(0);return lc.includes(t)?t:""}function Sy(){try{return localStorage.getItem(gd)??""}catch{return""}}function xd(s){try{localStorage.setItem(gd,s)}catch{}}function hc(s){s.addEventListener("keydown",t=>{t.key!=="Escape"&&t.stopPropagation()})}class uc{constructor(t,e,n){R(this,"tab");R(this,"pane");R(this,"log");R(this,"nicksEl");R(this,"nicks",new Map);R(this,"topic","");R(this,"joined",!1);this.id=t;const i=`mirc-pane-${t==="status"?"status":t.slice(1)}`;this.tab=V("button",{type:"button",role:"tab","aria-controls":i,html:`${e}`}),this.tab.querySelector("span").textContent=t==="status"?"Status":t,this.log=V("div",{class:"mirc-log",role:"log","aria-label":t==="status"?"Status":t}),n?(this.log.setAttribute("aria-live","polite"),this.nicksEl=V("ul",{class:"mirc-nicks","aria-label":`Nicknames in ${t}`}),this.pane=V("div",{class:"mirc-pane",id:i,role:"tabpanel"},[V("div",{class:"mirc-split"},[this.log,this.nicksEl])])):(this.nicksEl=null,this.pane=V("div",{class:"mirc-pane",id:i,role:"tabpanel"},[this.log]))}setNick(t,e=vd(t)){const n=Qn(t);n&&this.nicks.set(n.toLowerCase(),{name:n,mode:e})}seen(t){const e=Qn(t);if(!e)return!1;const n=e.toLowerCase(),i=this.nicks.get(n),r=vd(t)||(i==null?void 0:i.mode)||"";return i&&i.mode===r&&i.name===e?!1:(this.nicks.set(n,{name:e,mode:r}),!0)}drop(t){return this.nicks.delete(Qn(t).toLowerCase())}rename(t,e){const n=this.nicks.get(Qn(t).toLowerCase());this.drop(t),this.setNick(e,(n==null?void 0:n.mode)??"")}render(){if(!this.nicksEl)return;const t=n=>n?lc.indexOf(n):lc.length,e=[...this.nicks.values()].sort((n,i)=>t(n.mode)-t(i.mode)||n.name.localeCompare(i.name,void 0,{sensitivity:"base"}));this.nicksEl.replaceChildren(...e.map(n=>V("li",{title:`${n.mode}${n.name}`},[`${n.mode}${n.name}`])))}}class wy{constructor(t,e,n){R(this,"win");R(this,"wins");R(this,"view",kt);R(this,"input");R(this,"me",ac);R(this,"link",null);R(this,"hintedOikos",!1);R(this,"livingSeenAt",0);R(this,"livingOnce",!1);R(this,"greeted",!1);R(this,"hung",!1);R(this,"lastAt",-1);R(this,"seenFragments");R(this,"seenCollapse",null);R(this,"wasConnected",!1);R(this,"rejoinTimer",0);R(this,"off");var c;this.shell=t,this.wm=e;const i=V("div",{class:"mirc"});i.append(ic({File:()=>[{label:"Connect...",run:()=>this.joinLiving(),disabled:!!this.link},{label:"Disconnect",run:()=>this.partLiving(),disabled:!this.link},"sep",{label:"Select Server...",disabled:!0},"sep",{label:"Exit",run:()=>this.win.close()}],Tools:()=>[{label:"Address Book...",disabled:!0},{label:"Options...",shortcut:"Alt+O",disabled:!0}],Commands:()=>[{label:`Join ${Pt}`,run:()=>this.joinLiving()},{label:`Part ${Pt}`,run:()=>this.partLiving(),disabled:!this.link},"sep",{label:"Clear buffer",run:()=>this.wins[this.view].log.replaceChildren()}],Window:()=>["status",kt,Pt].filter(u=>!this.wins[u].tab.hidden).map(u=>({label:u==="status"?"Status":u,checked:this.view===u,run:()=>this.show(u)})),Help:()=>[{label:"Commands",shortcut:"F1",run:()=>this.help()},"sep",{label:"About æthera",run:()=>this.shell.about()}]}));const r=V("div",{class:"mirc-switch",role:"tablist"});this.wins={status:new uc("status",Mi("status"),!1),[kt]:new uc(kt,Mi("channel"),!0),[Pt]:new uc(Pt,Mi("channel"),!0)},this.wins[kt].topic=md,this.wins[Pt].tab.hidden=!0;const o=V("div",{class:"mirc-mdi"});for(const u of Object.values(this.wins))u.tab.addEventListener("click",()=>this.show(u.id)),r.append(u.tab),o.append(u.pane);this.input=V("input",{class:"mirc-input",type:"text",spellcheck:"false",autocomplete:"off",maxlength:"400","aria-label":"Message"}),hc(this.input),this.input.addEventListener("keydown",u=>{if(u.key!=="Enter")return;u.preventDefault();const h=this.input.value;this.input.value="",this.say(h)}),i.append(r,o,this.input),this.win=e.open({id:"mirc",title:"mIRC",icon:Mi("app"),body:i,width:Math.round(Math.min(760,Math.max(460,innerWidth*.52))),dock:"center",onClose:()=>{var u,h;clearTimeout(this.rejoinTimer),this.off(),(u=this.link)==null||u.part("Leaving"),this.link=null,(h=this.wm.get("mirc-connect"))==null||h.close(),n()},onFocus:()=>{innerWidth>720&&this.input.focus({preventScroll:!0})}}),this.win.el.classList.add("mirc-window"),this.win.el.style.height="min(540px, calc(100% - 24px))";const a=this.win.el.parentElement;if(a){const u=this.win.el.offsetTop+this.win.el.offsetHeight-(a.clientHeight-8);u>0&&this.win.moveTo(this.win.el.offsetLeft,this.win.el.offsetTop-u)}const l=this.shell.feeds.irc.value;this.seenFragments=l.fragments,this.seenCollapse=((c=l.collapse)==null?void 0:c.at)??null,this.show(kt),l.connected?this.connect(l):this.push("status","info",`* Connecting to ${Fn} (${oc})`),this.off=this.shell.feeds.irc.on(u=>this.update(u))}focus(t){this.win.minimized?this.win.restore():this.win.focus();const e=t===kt||t===Pt||t==="status"?this.wins[t]:null;e&&!e.tab.hidden&&this.show(e.id)}update(t){var n;if(t.connected&&!this.wasConnected?this.connect(t):!t.connected&&this.wasConnected&&this.disconnect(),!t.connected)return;for(const i of t.lines)i.at<=this.lastAt||(this.rejoinTimer&&this.rejoin(),this.lastAt=i.at,this.line(i));const e=((n=t.collapse)==null?void 0:n.at)??null;e!==null&&e!==this.seenCollapse&&t.collapse&&this.collapse(t.collapse.type),this.seenCollapse=e,t.fragments!==this.seenFragments&&(this.seenFragments=t.fragments,clearTimeout(this.rejoinTimer),this.hung?this.rejoinTimer=window.setTimeout(()=>this.rejoin(),_y):this.rejoin())}connect(t){this.wasConnected=!0;const e=(i,r)=>this.push("status",i,r);if(this.greeted){e("info",`* Reconnected to ${Fn}`),this.joinHaunted(),t.collapse&&this.collapse(t.collapse.type);return}this.greeted=!0,e("info",`* Connecting to ${Fn} (${oc})`),e("notice",`-${Fn}- *** Looking up your hostname...`),e("notice",`-${Fn}- *** Found your hostname`),e("text",`Welcome to the æthera IRC Network ${this.me}!${this.me}@oikos`),e("text",`Your host is ${Fn}, running version hauntd-2.8.21`),e("text",`- ${Fn} Message of the Day -`),e("text","- nobody here is who they were."),e("text","- every line is a replay; every replay is live."),e("text",`- ${kt} is moderated: the living listen.`),e("text",`- the living talk in ${Pt}. /join ${Pt}`),e("text","End of /MOTD command."),e("mode",`* ${this.me} sets mode: +i`),this.joinHaunted();const n=t.lines.filter(i=>i.at>this.lastAt);if(n.length){this.push(kt,"info","*** Buffer Playback...");for(const i of n)this.line(i);this.push(kt,"info","*** Playback Complete.")}this.lastAt=Math.max(this.lastAt,...t.lines.map(i=>i.at)),t.collapse&&this.collapse(t.collapse.type)}joinHaunted(){const t=this.wins[kt];t.joined=!0,t.nicks.clear(),t.setNick(this.me,""),this.push(kt,"join",`* Now talking in ${kt}`),this.push(kt,"topic",`* Topic is '${t.topic}'`),this.push(kt,"topic","* Set by ChanServ"),this.nicksChanged(kt)}disconnect(){clearTimeout(this.rejoinTimer),this.rejoinTimer=0,this.wasConnected=!1;const t=this.wins[kt];t.joined=!1,this.setHung(!1),this.push("status","info",`* Disconnected from ${kt}`),this.push(kt,"info","* Disconnected"),t.nicks.clear(),this.nicksChanged(kt)}collapse(t){const e=t.replace(/_/g," ");this.push("status","notice",`-${Fn}- *** Notice -- ${e} on ${kt}`),this.setHung(!0)}rejoin(){clearTimeout(this.rejoinTimer),this.rejoinTimer=0,this.setHung(!1),this.push(kt,"info",`* Attempting to rejoin channel ${kt}`),this.wins[kt].topic=md,this.joinHaunted()}line(t){const e=this.wins[kt],n=t.nick,i=Qn(n),r=t.content,o=t.wallAt??by(t.at),a=(c,u)=>this.push(kt,c,u,o);let l=!1;switch(t.type){case"message":l=e.seen(n),a("text",`<${n}> ${r}`);break;case"action":{const c=/^sets mode: ([+-])([a-z]+) (.+)$/i.exec(r);c?(e.seen(n),this.applyMode(c[1]??"+",c[2]??"",(c[3]??"").split(/\s+/)),l=!0,a("mode",`* ${n} ${r}`)):/^has quit\b/i.test(r)?(l=e.drop(n),a("quit",`* ${i} ${r.replace(/^has quit\b/i,"has quit IRC")}`)):(l=e.seen(n),a("action",`* ${n} ${r}`));break}case"join":l=e.seen(n),a("join",`* ${i} (${cc(n)}) has joined ${kt}`);break;case"part":l=e.drop(n),a("part",`* ${i} (${cc(n)}) has left ${kt}${r?` (${r})`:""}`);break;case"quit":l=e.drop(n),a("quit",`* ${i} (${cc(n)}) Quit (${r||"Client exited"})`);break;case"kick":{const c=t.reason||r;t.target?(l=e.drop(t.target),a("kick",`* ${Qn(t.target)} was kicked by ${i}${c?` (${c})`:""}`)):a("kick",`* ${i} kicks${c?` (${c})`:""}`);break}default:{const c=/changes topic to '(.*)'$/i.exec(r);if(c){e.topic=c[1]??e.topic,a("topic",`* ${r}`),this.retitle();break}const u=r||n;a("info",/^[*<]/.test(u)||u.length<6?u:`* ${u}`)}}!e.nicks.has(this.me.toLowerCase())&&e.joined&&(e.setNick(this.me,""),l=!0),l&&this.nicksChanged(kt)}applyMode(t,e,n){const i=this.wins[kt],r={q:"~",a:"&",o:"@",h:"%",v:"+"};[...e].forEach((o,a)=>{const l=n[a],c=r[o];if(!l||!c)return;const u=i.nicks.get(Qn(l).toLowerCase());i.setNick((u==null?void 0:u.name)??Qn(l),t==="+"?c:(u==null?void 0:u.mode)===c?"":(u==null?void 0:u.mode)??"")})}joinLiving(){if(this.link&&this.wins[Pt].joined){this.show(Pt);return}if(this.link){this.push(this.view,"info",`* Still connecting to ${Pt}...`);return}this.connectDialog()}partLiving(){if(!this.link)return;this.link.part("Leaving"),this.link=null;const t=this.wins[Pt];t.joined=!1,t.nicks.clear(),this.nicksChanged(Pt),this.push(Pt,"part",`* You have left ${Pt}`),this.push("status","info",`* Disconnected from ${Pt}`),this.livingSeenAt=0,this.livingOnce=!1,this.renameMe(ac)}connectDialog(t){var u;(u=this.wm.get("mirc-connect"))==null||u.close();const e=V("form",{class:"mirc-connect"}),n=V("input",{type:"text",value:Sy(),maxlength:"16",spellcheck:"false",autocomplete:"nickname",required:""}),i=V("input",{type:"password",maxlength:"256",autocomplete:"current-password"});hc(n),hc(i);const r=V("p",{class:"err",role:"alert"});r.textContent=t??"",r.hidden=!t,e.append(V("div",{class:"xp-dialog-body"},[V("span",{html:Mi("app")}),V("div",{},[V("p",{},[`Join ${Pt}, where the living talk.`]),V("label",{},["Nickname:",n]),V("label",{},["Password (optional):",i]),V("p",{class:"hint"},["A password gives you a tripcode, the same one your blog comments carry. It is never stored."]),r])]));const o=V("div",{class:"xp-actions"}),a=V("button",{class:"xp-btn default",type:"submit"},["Connect"]),l=V("button",{class:"xp-btn",type:"button"},["Cancel"]);o.append(a,l),e.append(o);const c=this.wm.open({id:"mirc-connect",title:"mIRC Connect",icon:Mi("app"),body:e,width:360,dialog:!0});l.addEventListener("click",()=>c.close()),e.addEventListener("keydown",h=>{h.key==="Escape"&&(h.preventDefault(),c.close(),this.input.focus({preventScroll:!0}))}),e.addEventListener("submit",h=>{h.preventDefault();const d=n.value.trim();if(!yy.test(d)){r.textContent="A nickname starts with a letter and has at most 16 letters, digits or - _ [ ] { } | ^ `",r.hidden=!1,n.focus();return}xd(d),c.close(),this.dial(d,i.value||null)}),n.focus(),n.select()}dial(t,e){this.push("status","info",`* Connecting to ${Pt} as ${t}${e?" (with a tripcode)":""}`);const n=new vy(t,e,{welcome:i=>this.welcomed(i),event:i=>this.event(i),whois:i=>this.whoisReply(i),error:(i,r)=>this.chatError(i,r),closed:(i,r,o)=>this.dropped(i,r,o)});this.link=n,n.connect()}welcomed(t){const e=this.wins[Pt];e.tab.hidden=!1,e.joined=!0,e.topic=t.topic,e.nicks.clear();for(const r of t.names)e.setNick(r.nick,r.op?"@":"");this.renameMe(t.nick);const n=this.livingOnce;this.livingOnce=!0;const i=t.backlog.filter(r=>r.at>this.livingSeenAt);if(n?this.push(Pt,"join",`* Rejoined ${Pt}`):(this.push("status","text",`* You are now known as ${t.nick} (${t.mask})`),t.op&&this.push("status","mode",`* ${Fn} sets mode: +o ${t.nick}`),this.push(Pt,"join",`* Now talking in ${Pt}`),this.push(Pt,"topic",`* Topic is '${t.topic}'`)),i.length){this.push(Pt,"info","*** Buffer Playback...");for(const r of i)this.event(r,!0);this.push(Pt,"info","*** Playback Complete.")}this.nicksChanged(Pt),n||this.show(Pt)}event(t,e=!1){const n=this.wins[Pt],i=(o,a)=>this.push(Pt,o,a,t.at),r=t.nick===this.me;switch(this.livingSeenAt=Math.max(this.livingSeenAt,t.at),t.kind){case"message":i(r?"own":"text",`<${t.nick}> ${t.text}`);return;case"action":i("action",`* ${t.nick} ${t.text}`);return;case"join":i("join",`* ${t.nick} (${t.mask??""}) has joined ${Pt}`),e||n.setNick(t.nick,"");break;case"part":i("part",`* ${t.nick} (${t.mask??""}) has left ${Pt}${t.text?` (${t.text})`:""}`),e||n.drop(t.nick);break;case"quit":i("quit",`* ${t.nick} (${t.mask??""}) Quit (${t.text||"Client exited"})`),e||n.drop(t.nick);break;case"kick":t.target===this.me&&!e?(i("kick",`* You were kicked from ${Pt} by ${t.nick} (${t.text})`),n.joined=!1,n.nicks.clear()):(i("kick",`* ${t.target??"?"} was kicked by ${t.nick} (${t.text})`),!e&&t.target&&n.drop(t.target));break;case"nick":r&&!e&&t.target?(i("text",`* Your nick is now ${t.target}`),this.renameMe(t.target),xd(t.target)):i("text",`* ${t.nick} is now known as ${t.target??"?"}`),!e&&t.target&&n.rename(t.nick,t.target);break;case"topic":i("topic",`* ${t.nick} changes topic to '${t.text}'`),e||(n.topic=t.text);break}e||this.nicksChanged(Pt)}whoisReply(t){const e=n=>this.push(this.view,"text",n);e(`${t.nick} is ${t.mask} * ${t.nick}`),t.trip&&e(`${t.nick} is identified by tripcode ${t.trip}`),t.op&&e(`${t.nick} is a channel operator on ${Pt}`),e(`${t.nick} signed on ${new Date(t.signon).toLocaleString()}`),e(`${t.nick} End of /WHOIS list.`)}chatError(t,e){var r;const n=this.wins[Pt],i=e.replace(/^(\S+) :/,"$1 ");this.push(n.joined?Pt:"status","error",`* ${i}`),!n.joined&&(t==="432"||t==="433")&&((r=this.link)==null||r.close(),this.link=null,this.livingOnce=!1,this.livingSeenAt=0,this.connectDialog(e.replace(/^\S+ :/,"")))}dropped(t,e,n){const i=this.wins[Pt],r=i.joined;i.joined=!1,i.nicks.clear(),this.nicksChanged(Pt),n?this.push(Pt,"info",`* ${this.me} is connected from somewhere else now (another window?); this one let go`):r&&!e&&this.push(Pt,"info",`* Disconnected${t?" (reconnecting...)":""}`),this.push("status","info",`* Disconnected from ${Pt}${t?" (reconnecting...)":""}`),t||(this.link=null,this.livingOnce=!1,this.livingSeenAt=0,this.renameMe(ac))}renameMe(t){if(t===this.me)return;const e=this.wins[kt];e.joined&&(e.rename(this.me,t),this.nicksChanged(kt)),this.me=t,this.retitle()}say(t){var a,l,c,u,h,d;const e=t.trim();if(!e)return;const n=this.view,i=n===Pt&&this.wins[Pt].joined&&!!this.link,r=f=>this.push(n,"error",`* ${f}`);if(e.startsWith("/")&&!e.startsWith("//")){const[f="",...p]=e.slice(1).split(/\s+/),v=p.join(" ");switch(f.toLowerCase()){case"help":this.help();return;case"clear":this.wins[n].log.replaceChildren();return;case"quit":case"exit":this.win.close();return;case"join":{const m=(p[0]??"").toLowerCase().replace(/^#?/,"#");m===Pt||m==="#"&&n==="status"?this.joinLiving():m===kt?(this.show(kt),this.push(kt,"error",`* You are already on ${kt}`)):r(`${p[0]??"#"} Cannot join channel (this network has ${kt} and ${Pt})`);return}case"part":case"leave":n===Pt&&this.link?this.partLiving():r(n===kt?`${kt} will not let you go`:"You are not on a channel");return;case"nick":p[0]?(a=this.link)!=null&&a.send({type:"nick",nick:p[0]})||r(`Join ${Pt} first to have a name (/join ${Pt})`):r("Usage: /nick ");return;case"whois":p[0]?(l=this.link)!=null&&l.send({type:"whois",nick:p[0]})||r(`${p[0]} :No such nick`):r("Usage: /whois ");return;case"me":if(!v)return;i?(c=this.link)==null||c.send({type:"say",text:v,action:!0}):n===kt?this.refused(`* ${this.me} ${v}`,"action"):r("You are not on a channel");return;case"topic":i&&v?(u=this.link)==null||u.send({type:"topic",text:v}):i?this.push(Pt,"topic",`* Topic is '${this.wins[Pt].topic}'`):r(`${n==="status"?"":`${n} `}You're not channel operator`);return;case"kick":i&&p[0]?(h=this.link)==null||h.send({type:"kick",nick:p[0],reason:p.slice(1).join(" ")}):r(i?"Usage: /kick [reason]":"You're not channel operator");return;default:r(`${f.toUpperCase()} Unknown command`);return}}const o=e.startsWith("//")?e.slice(1):e;i?(d=this.link)!=null&&d.send({type:"say",text:o})||r("Not connected"):n===kt&&this.wins[kt].joined?this.refused(`<${this.me}> ${o}`,"own"):r("You are not on a channel")}refused(t,e){this.push(kt,e,t),this.push(kt,"error",`* ${kt} Cannot send to channel`),this.hintedOikos||(this.hintedOikos=!0,this.push(kt,"info",`* (the living talk in ${Pt}: /join ${Pt})`))}help(){const t=e=>this.push(this.view,"info",e);t("* Commands:"),t(`* /join ${Pt} join the living (a nick, and a password for a tripcode if you like)`),t("* /nick change your nick"),t("* /me an action"),t("* /whois who someone is (their tripcode, if they have one)"),t(`* /part leave ${Pt}`),t("* /topic, /kick for channel operators"),t("* /clear /quit")}push(t,e,n,i=Date.now()){var c;const r=this.wins[t],o=r.log,a=o.scrollHeight-o.scrollTop-o.clientHeight<24,l=V("div",{class:`mirc-row t-${e}`});for(l.append(V("span",{class:"ts"},[My(i)]),` ${n}`),o.append(l);o.childElementCount>xy;)(c=o.firstElementChild)==null||c.remove();a&&(o.scrollTop=o.scrollHeight),t!==this.view&&(t!=="status"&&(e==="text"||e==="action"||e==="own")?r.tab.classList.add("said"):r.tab.classList.contains("said")||r.tab.classList.add("event"))}show(t){this.view=t;for(const n of Object.values(this.wins)){const i=n.id===t;n.pane.hidden=!i,n.tab.classList.toggle("on",i),n.tab.setAttribute("aria-selected",String(i)),i&&n.tab.classList.remove("said","event")}const e=this.wins[t].log;e.scrollTop=e.scrollHeight,this.input.setAttribute("aria-label",t==="status"?"Command":`Message ${t}`),this.retitle()}nicksChanged(t){this.wins[t].render(),this.retitle()}setHung(t){this.hung!==t&&(this.hung=t,this.win.el.classList.toggle("mirc-hung",t),this.retitle())}retitle(){const t=this.wins[this.view],e=t.id===kt?"+mnt":"+nt",n=t.id==="status"?`mIRC - [Status: ${this.me} on ${Fn} (${oc})]`:t.joined?`mIRC - [${t.id} [${t.nicks.size}] [${e}]: ${t.topic}]`:`mIRC - [${t.id} (not on channel)]`;this.win.setTitle(this.hung?`${n} (Not Responding)`:n)}}class Ey{constructor(t,e,n,i){R(this,"win");R(this,"statusLine");R(this,"timer",0);this.site=t,this.shell=e;const r=V("div");r.style.cssText="display:flex;flex-direction:column;min-height:0;flex:1;";const o=rd({back:()=>this.shell.back(),forward:()=>this.shell.forward(),can:()=>({back:this.shell.canBack,forward:this.shell.canForward}),up:()=>this.shell.home(),folders:()=>d.toggleAttribute("hidden")}),a=ld(t),l=od(Sn(t.accent),a,!1,()=>this.shell.open(t)).el,c=ad();r.append(ic({File:()=>[...wo(this.shell,t,!0),"sep",{label:"Properties",disabled:!0},"sep",{label:"Close",run:()=>this.win.close()}],Edit:()=>hd(this.shell,t),View:()=>pd(this.shell,{toolbar:o.el,address:l,status:c.el,up:()=>this.shell.home(),refresh:()=>this.refresh()}),Favorites:()=>ud(this.shell),Tools:()=>dd(),Help:()=>fd(this.shell)}),o.el,l);const u=this.shell.onNav(()=>o.refresh()),h=V("div",{class:"xp-body"}),d=V("aside",{class:"xp-tasks"});h.append(d);const f=V("div",{class:"xp-content"});h.append(f),r.append(h),c.set("Done",cd(t)),r.append(c.el);const p=V("div",{class:"xp-hero",html:Sn(t.accent,t.title)}),v=V("div");v.append(V("h2",{},[t.title]),V("p",{},[t.tagline])),p.append(v),f.append(p);const m=V("button",{class:"xp-preview",type:"button","aria-label":`Open ${t.title}`}),g=this.shell.screens.get(t.id);g&&m.append(g.canvas),m.append(V("span",{class:"xp-play"},[`▶ open ${t.title}`])),m.addEventListener("click",L=>this.shell.open(t,L)),f.append(m),f.append(V("p",{class:"xp-about"},[t.about]));const S=this.note();S&&f.append(V("div",{class:"xp-note"},[S]));const E=V("div",{class:"xp-actions"}),y=V("button",{class:"xp-btn default",type:"button"},[`Open ${t.title}`]);y.addEventListener("click",L=>this.shell.open(t,L));const M=V("button",{class:"xp-btn",type:"button"},["~ Home directory"]);if(M.addEventListener("click",()=>this.shell.home()),E.append(y),t.tune&&this.shell.canTune){const L=V("button",{class:"xp-btn",type:"button"},["▣ Watch it here"]);L.title="the live page, on its own screen in the room",L.addEventListener("click",()=>this.shell.tuneIn(t.id)),E.append(L)}if(t.id==="irc"){const L=V("button",{class:"xp-btn",type:"button"},["Join #aethera"]);L.title="sit in the channel, in mIRC",L.addEventListener("click",()=>this.shell.chat("#aethera")),E.append(L)}E.append(M),f.append(E);const w=[{icon:Ql(),label:`Open ${t.title}`,run:()=>this.shell.open(t)},{icon:Ku(),label:"Look at its screen",run:()=>this.shell.look(t.id)}];t.tune&&this.shell.canTune&&w.splice(1,0,{icon:sy(),label:"Watch it on its screen",run:()=>this.shell.tuneIn(t.id)}),t.id==="irc"&&w.splice(1,0,{icon:Mi("channel"),label:"Join #aethera in mIRC",run:()=>this.shell.chat("#aethera")}),w.push({icon:ry(),label:"Copy address",run:()=>sc(this.shell,t)}),w.push({icon:Zu(),label:"Eject tape",run:()=>this.win.close()});const C=[{icon:pr(),label:"~ (home directory)",run:()=>this.shell.home()}];for(const L of uy[t.id]??[]){const D=this.shell.site(L);D&&C.push({icon:Sn(D.accent),label:D.title,run:()=>this.shell.play(D.id)})}const x=V("div",{class:"xp-details"});x.append(V("b",{},[t.title]));const T=V("dl");for(const[L,D]of t.details)T.append(V("dt",{},[L]),V("dd",{},[D]));x.append(T),this.statusLine=V("div",{class:"status"}),x.append(this.statusLine),d.append(Fs("Tape Tasks",w,!0),Fs("Other Places",C),Fs("Details",x)),this.win=n.open({id:`site:${t.id}`,title:`${t.title} — ${a.replace(/^https?:\/\//,"")}`,icon:Sn(t.accent),body:r,width:Math.round(Math.min(760,Math.max(440,innerWidth*.5))),dock:"right",onClose:()=>{u(),clearInterval(this.timer),g==null||g.canvas.remove(),i()}}),this.win.el.style.height="min(640px, calc(100% - 24px))",this.refresh(),this.timer=window.setInterval(()=>this.refresh(),2e3)}note(){const t=this.site;return t.id==="dreams"?"Opening dreams wakes the dreamer: a GPU starts up while anyone is watching and goes back to sleep after. The frame here is the last one the chronicle kept.":t.id==="syrinx"?"Syrinx makes sound once you wake it. The creature here is read from this browser; nobody else sees yours.":t.id==="afterlife"?"afterlife makes music once you click in. The universe on this screen is the one saved in this browser, if you have visited; nobody else sees yours.":t.group==="wired"?`${t.title} is not on this server; it opens in a new window.`:null}refresh(){const{feeds:t}=this.shell;let e=!1,n="";switch(this.site.id){case"dreams":case"dreams-api":{const r=t.dreams.value;e=r.known&&r.awake,n=r.known?r.awake?`awake · frame ${r.frame.toLocaleString("en-US")}${r.viewers?` · ${r.viewers} watching`:""}`:"asleep":"status unknown";break}case"chronicle":{const r=t.chronicle.value;e=r.known&&r.eras.some(o=>o.open),n=r.known?`${r.eraCount||r.eras.length} eras recorded`:"reading the core…";break}case"irc":{const r=t.irc.value;e=r.connected,n=r.connected?"live on #aethera":"connecting…";break}case"syrinx":{const r=t.creature.value;e=!!r,n=r?`yours: ${r.name}`:"not woken in this browser";break}case"afterlife":{const r=t.universe.value;e=!!r,n=r?`yours: generation ${r.generation.toLocaleString("en-US")}`:"no universe in this browser yet";break}default:return}this.statusLine.className=`status${e?" on":""}`,this.statusLine.innerHTML="";const i=this.statusLine.querySelector("span");i&&(i.textContent=n)}}const _d=2;class Ty{constructor(t,e,n,i){R(this,"el");R(this,"menu");R(this,"buttons");R(this,"clock");R(this,"moonEl");R(this,"start");R(this,"tip");R(this,"balloonEl");R(this,"balloonTimer",0);R(this,"dialogEl",null);this.root=t,this.shell=e,this.wm=n,this.hooks=i,this.el=V("nav",{class:"xp-taskbar","aria-label":"taskbar"}),this.start=V("button",{class:"xp-start",type:"button","aria-haspopup":"menu","aria-expanded":"false",html:`${mr(22,!1)}start`}),this.buttons=V("div",{class:"xp-taskbuttons"});const r=V("div",{class:"xp-tray"}),o=V("span",{class:"tray-icon net",title:"Connected to the Wired",html:J_()});this.moonEl=V("span",{class:"tray-icon dream",title:"dreams",html:qu(!1)}),this.clock=V("span",{class:"clock"}),r.append(o,this.moonEl,this.clock),this.el.append(this.start,this.buttons,r),t.append(this.el),this.menu=this.buildMenu(),t.append(this.menu),this.start.addEventListener("click",()=>this.toggleMenu()),t.addEventListener("pointerdown",a=>{const l=a.target;!this.menu.hidden&&!l.closest(".xp-startmenu")&&!l.closest(".xp-start")&&!l.closest(".xp-menu")&&this.toggleMenu(!1)}),this.el.addEventListener("contextmenu",a=>{a.preventDefault(),a.stopPropagation();const l=a.target.closest(".xp-taskbtn"),c=l?this.wm.list.find(u=>u.opts.id===l.dataset.win):void 0;Us(c?c.systemMenu():this.barMenu(),a.clientX,a.clientY)}),o.addEventListener("click",()=>this.balloon("Connected to the Wired",`${e.dir.sites.length} screens · 1 VCR · signal: present day, present time`,this.trayAnchor())),this.tip=V("div",{class:"xp-tip",role:"tooltip",hidden:""}),this.balloonEl=V("div",{class:"xp-balloon",role:"status",hidden:""}),t.append(this.tip,this.balloonEl),n.on(()=>this.renderButtons()),e.feeds.dreams.on(a=>{this.moonEl.innerHTML=qu(a.awake),this.moonEl.title=a.known?a.awake?`dreams: awake · frame ${a.frame.toLocaleString("en-US")}`:"dreams: asleep":"dreams: no answer"}),this.tick(),setInterval(()=>this.tick(),15e3)}tick(){this.clock.textContent=new Date().toLocaleTimeString([],{hour:"numeric",minute:"2-digit"})}trayAnchor(){const t=this.el.getBoundingClientRect();return{x:t.right-60,y:t.top}}toggleMenu(t){var n;const e=t??this.menu.hidden;e||bi(),this.menu.hidden=!e,this.start.classList.toggle("open",e),this.start.setAttribute("aria-expanded",String(e)),e&&((n=this.menu.querySelector("a,button"))==null||n.focus())}barMenu(){return[{label:"Toolbars",disabled:!0,submenu:()=>[]},"sep",{label:"Cascade Windows",disabled:!0},{label:"Tile Windows Horizontally",disabled:!0},{label:"Tile Windows Vertically",disabled:!0},{label:"Show the Desktop",run:()=>this.showDesktop(),disabled:!this.wm.list.some(t=>!t.minimized&&!t.opts.dialog)},"sep",{label:"Task Manager",disabled:!0},"sep",{label:"Lock the Taskbar",checked:!0,disabled:!0},{label:"Properties",disabled:!0}]}showDesktop(){for(const t of[...this.wm.list])!t.opts.dialog&&!t.minimized&&t.minimize()}buildMenu(){const t=V("div",{class:"xp-startmenu",role:"menu",hidden:""}),e=V("header");e.append(V("span",{class:"avatar",html:mr(40)}),V("span",{},["guest"])),t.append(e,V("div",{class:"orange","aria-hidden":"true"}));const n=V("div",{class:"cols"}),i=V("ul",{class:"left"}),r=V("ul",{class:"right"}),o=(M,w)=>{const C=V("li");return C.append(w),M.append(C),w},a=(M,w,C,x,T,L="")=>{const D=V("button",{type:"button",role:"menuitem",class:L,html:`${w}${x!==null?"":""}`});D.querySelector(".tt").textContent=C;const U=D.querySelector("small");return U&&x&&(U.textContent=x),D.addEventListener("click",()=>{this.toggleMenu(!1),T()}),o(M,D)},l=(M,w,C,x)=>{const T=V("a",{href:x,role:"menuitem",html:`${w}`});T.querySelector("span").textContent=C,o(M,T)},c=(M,w,C,x,T)=>{const L=V("button",{type:"button",role:"menuitem",class:`cascade ${x}`,"aria-haspopup":"menu","data-menu-owner":"",html:`${w}`});L.querySelector(".tt").textContent=C;const D=()=>{L.classList.add("open"),sd(T(),L,()=>L.classList.remove("open"))};return L.addEventListener("click",D),L.addEventListener("pointerenter",D),o(M,L)},u=M=>M.append(V("li",{class:"sep",role:"separator"}));t.addEventListener("pointerover",M=>{const w=M.target.closest("li > a, li > button");w&&!w.classList.contains("cascade")&&bi()});const h=M=>({label:M.title,icon:Sn(M.accent),run:()=>{this.toggleMenu(!1),this.shell.play(M.id)}});a(i,Mi(),"mIRC","#aethera",()=>this.shell.chat(),"pinned");const d=this.shell.dir.sites.filter(M=>M.group==="here");d.slice(0,_d).forEach(M=>a(i,Sn(M.accent,M.title),M.title,M.kind,()=>this.shell.play(M.id),"pinned")),u(i),d.slice(_d).forEach(M=>a(i,Sn(M.accent,M.title),M.title,null,()=>this.shell.play(M.id)));const f=V("li",{class:"allprog"});i.append(V("li",{class:"sep",role:"separator"}),f);const p=V("button",{type:"button",role:"menuitem",class:"cascade","aria-haspopup":"menu","data-menu-owner":"",html:`All Programs${ay()}`}),v=()=>{p.classList.add("open"),sd([...this.shell.dir.sites.filter(M=>M.group==="here").map(h),"sep",...this.shell.dir.sites.filter(M=>M.group==="wired").map(h),"sep",{label:"~ (home directory)",icon:pr(),run:()=>{this.toggleMenu(!1),this.shell.home()}}],p,()=>p.classList.remove("open"))};p.addEventListener("click",v),p.addEventListener("pointerenter",v),f.append(p),a(r,pr(),"My Tapes",null,()=>this.shell.home(),"strong");const m=this.shell.dir.posts.slice(0,8);m.length&&c(r,Ns("txt"),"My Recent Transmissions","strong",()=>m.map(M=>({label:M.title,icon:Ns("txt"),run:()=>location.assign(M.href)}))),u(r);const g=this.shell.dir.sites.filter(M=>M.group==="wired");for(const M of g)a(r,Sn(M.accent),M.title,null,()=>this.shell.play(M.id));u(r);for(const M of this.shell.dir.files)l(r,Ns(M.title.split(".").pop()??"txt"),M.title,M.href);n.append(i,r),t.append(n);const S=V("div",{class:"foot"}),E=V("button",{type:"button",html:`${Qu()}Log Off`});E.addEventListener("click",()=>{this.toggleMenu(!1),this.logOffDialog()});const y=V("button",{type:"button",html:`${ju()}Turn Off Computer`});return y.addEventListener("click",()=>{this.toggleMenu(!1),this.shutdownDialog()}),S.append(E,y),t.append(S),t}dismiss(){return this.dialogEl?(this.dialogEl.remove(),this.dialogEl=null,this.start.focus(),!0):!1}bigDialog(t,e){var l,c;(l=this.dialogEl)==null||l.remove();const n=V("div",{class:"xp-shutdown",role:"dialog","aria-modal":"true","aria-label":t});this.dialogEl=n;const i=V("div",{class:"panel"});i.append(V("header",{html:`${mr(28,!1)}`})),i.querySelector("header span").textContent=t;const r=V("div",{class:"choices"});for(const u of e){const h=V("button",{type:"button",class:u.cls,html:`${u.icon}${u.label}`}),d=u.run;d?h.addEventListener("click",()=>{n.remove(),this.dialogEl=null,d()}):h.disabled=!0,r.append(h)}i.append(r);const o=V("div",{class:"foot"}),a=V("button",{class:"xp-btn",type:"button"},["Cancel"]);a.addEventListener("click",()=>this.dismiss()),o.append(a),i.append(o),n.append(i),this.root.append(n),(c=r.querySelector("button:not(:disabled):last-of-type, button:not(:disabled)"))==null||c.focus()}logOffDialog(){this.bigDialog("Log Off æthera",[{cls:"switch",icon:td(),label:"Switch User"},{cls:"logoff",icon:Qu(),label:"Log Off",run:()=>this.hooks.logOff()}])}shutdownDialog(){var t,e;this.bigDialog("Turn off computer",[{cls:"standby",icon:oy(),label:"Stand By",run:()=>this.hooks.standby(!0)},{cls:"off",icon:ju(),label:"Turn Off",run:()=>this.hooks.turnOff()},{cls:"restart",icon:td(),label:"Restart",run:()=>this.hooks.restart()}]),(e=(t=this.dialogEl)==null?void 0:t.querySelector(".off"))==null||e.focus()}renderButtons(){this.buttons.replaceChildren();for(const t of this.wm.list){if(t.opts.dialog)continue;const e=V("button",{class:`xp-taskbtn${this.wm.activeWindow===t&&!t.minimized?" active":""}`,type:"button","data-win":t.opts.id,html:`${t.opts.icon}`});e.querySelector("span").textContent=t.opts.title,e.title=t.opts.title,e.addEventListener("click",()=>{t.minimized?t.restore():this.wm.activeWindow===t?t.minimize():t.focus()}),this.buttons.append(e)}}showTip(t,e,n,i){const r=t?{title:t.title,text:t.tagline}:e;if(!r){this.tip.hidden=!0;return}this.tip.innerHTML="",this.tip.querySelector("b").textContent=r.title,this.tip.querySelector("span").textContent=r.text,this.tip.hidden=!1;const o=this.root.getBoundingClientRect(),a=this.tip.offsetWidth;this.tip.style.left=`${Math.min(n+14,o.width-a-6)}px`,this.tip.style.top=`${Math.min(i+20,o.height-70)}px`}balloon(t,e,n,i=7e3,r=!1){var f;const o=this.balloonEl;o.innerHTML=`${Ju()}`,o.querySelector("b span").textContent=t,o.querySelector(".msg").textContent=e,(f=o.querySelector(".x"))==null||f.addEventListener("click",()=>o.hidden=!0),o.hidden=!1;const a=this.root.getBoundingClientRect(),l=n??this.trayAnchor(),c=o.offsetWidth,u=o.offsetHeight,h=Math.max(6,Math.min(l.x-30,a.width-c-6)),d=r?l.y+u+24>a.height-30:l.y-u-20>0;o.classList.toggle("above",d),o.classList.toggle("below",!d),o.style.left=`${h}px`,o.style.top=`${d?l.y-u-18:l.y+18}px`,o.style.setProperty("--tail",`${Math.max(12,Math.min(c-30,l.x-h))}px`),clearTimeout(this.balloonTimer),this.balloonTimer=window.setTimeout(()=>o.hidden=!0,i)}hideBalloon(){this.balloonEl.hidden=!0}}const dc=300,fc=180,Ay=["n","s","e","w","ne","nw","se","sw"];class Cy{constructor(t,e){R(this,"el");R(this,"minimized",!1);R(this,"closed",!1);var o,a,l,c,u;this.opts=t,this.wm=e;const n=document.createElement("section");n.className=`xp-window${t.dialog?" xp-dialog":""}`,n.setAttribute("role",t.dialog?"alertdialog":"dialog"),n.setAttribute("aria-label",t.title),n.innerHTML=`
${t.icon} @@ -4578,4 +4578,4 @@ ${s?''} -
`;const i=n.querySelector(".xp-title");i&&(i.textContent=t.title),n.append(t.body),n.style.width=`${t.width}px`,this.el=n,(a=n.querySelector(".xp-close"))==null||a.addEventListener("click",()=>this.close()),(o=n.querySelector(".xp-min"))==null||o.addEventListener("click",()=>this.minimize()),(l=n.querySelector(".xp-max"))==null||l.addEventListener("click",()=>this.toggleMax()),n.addEventListener("pointerdown",()=>this.focus(),!0),this.bindDrag(n.querySelector(".xp-titlebar")),t.dialog||this.bindResize(),(c=n.querySelector(".xp-titlebar"))==null||c.addEventListener("dblclick",h=>{const d=h.target;d.closest(".xp-controls")||t.dialog||(d.closest(".xp-title-icon")?this.close():this.toggleMax())});const r=n.querySelector(".xp-title-icon");r==null||r.setAttribute("data-menu-owner",""),r==null||r.addEventListener("click",h=>{h.stopPropagation();const d=n.getBoundingClientRect();Ms(this.systemMenu(),d.left+3,d.top+29)}),(u=n.querySelector(".xp-titlebar"))==null||u.addEventListener("contextmenu",h=>{const d=h;d.target.closest(".xp-controls")||(d.preventDefault(),d.stopPropagation(),Ms(this.systemMenu(),d.clientX,d.clientY))})}get maximized(){return this.el.classList.contains("maximized")}systemMenu(){const t=!!this.opts.dialog;return[{label:"Restore",run:()=>this.minimized?this.restore():this.toggleMax(),disabled:t||!this.maximized&&!this.minimized},{label:"Move",disabled:!0},{label:"Size",disabled:!0},{label:"Minimize",run:()=>this.minimize(),disabled:t||this.minimized},{label:"Maximize",run:()=>{this.minimized&&this.restore(),this.maximized||this.toggleMax()},disabled:t||this.maximized},"sep",{label:"Close",run:()=>this.close(),bold:!0,shortcut:"Alt+F4"}]}setTitle(t){const e=this.el.querySelector(".xp-title");e&&(e.textContent=t),this.el.setAttribute("aria-label",t)}place(t){const e=t.clientWidth,n=t.clientHeight,i=Math.min(this.opts.width,e-16);this.el.style.width=`${i}px`,t.append(this.el);const r=Math.min(this.el.offsetHeight,n-16);let a=this.opts.x??0,o=this.opts.y??0;this.opts.x===void 0&&(this.opts.dock==="left"?a=18:this.opts.dock==="right"?a=e-i-18:a=(e-i)/2),this.opts.y===void 0&&(o=this.opts.dialog?(n-r)/2.4:Math.max(8,Math.min(60,(n-r)/2))),this.moveTo(a,o)}moveTo(t,e){const n=this.el.parentElement;if(!n)return;const i=n.clientWidth-60,r=n.clientHeight-30;this.el.style.left=`${Math.round(Math.max(-this.el.offsetWidth+80,Math.min(i,t)))}px`,this.el.style.top=`${Math.round(Math.max(0,Math.min(r,e)))}px`}bindDrag(t){let e=null;t.addEventListener("pointerdown",i=>{i.target.closest(".xp-controls, .xp-title-icon")||i.button!==0||this.el.classList.contains("maximized")||innerWidth<=720||(e={x:i.clientX,y:i.clientY,left:this.el.offsetLeft,top:this.el.offsetTop},t.setPointerCapture(i.pointerId))}),t.addEventListener("pointermove",i=>{e&&this.moveTo(e.left+i.clientX-e.x,e.top+i.clientY-e.y)});const n=()=>{e&&this.wm.changed(),e=null};t.addEventListener("pointerup",n),t.addEventListener("pointercancel",n)}bindResize(){for(const t of rv){const e=document.createElement("div");e.className=`xp-grip xp-grip-${t}`,e.setAttribute("aria-hidden","true"),this.el.append(e);let n=null;e.addEventListener("pointerdown",r=>{r.button!==0||this.maximized||innerWidth<=720||(r.preventDefault(),n={x:r.clientX,y:r.clientY,left:this.el.offsetLeft,top:this.el.offsetTop,w:this.el.offsetWidth,h:this.el.offsetHeight},e.setPointerCapture(r.pointerId),this.focus())}),e.addEventListener("pointermove",r=>{if(!n)return;const a=this.el.parentElement;if(!a)return;const o=r.clientX-n.x,l=r.clientY-n.y;let{left:c,top:u,w:h,h:d}=n;t.includes("e")&&(h=Math.min(a.clientWidth-c,Math.max(Ul,n.w+o))),t.includes("s")&&(d=Math.min(a.clientHeight-u,Math.max(Fl,n.h+l))),t.includes("w")&&(h=Math.max(Ul,Math.min(n.left+n.w,n.w-o)),c=n.left+n.w-h),t.includes("n")&&(d=Math.max(Fl,Math.min(n.top+n.h,n.h-l)),u=n.top+n.h-d),this.el.style.left=`${Math.round(c)}px`,this.el.style.top=`${Math.round(u)}px`,this.el.style.width=`${Math.round(h)}px`,this.el.style.height=`${Math.round(d)}px`,this.el.style.maxHeight="none"});const i=()=>{n&&this.wm.changed(),n=null};e.addEventListener("pointerup",i),e.addEventListener("pointercancel",i)}}clamp(){if(this.maximized||this.minimized)return;const t=this.el.parentElement;t&&(this.el.offsetWidth>t.clientWidth&&(this.el.style.width=`${Math.max(Ul,t.clientWidth-8)}px`),this.el.offsetHeight>t.clientHeight&&(this.el.style.height=`${Math.max(Fl,t.clientHeight-8)}px`),this.moveTo(this.el.offsetLeft,this.el.offsetTop))}focus(){this.wm.focus(this)}minimize(){this.minimized=!0,this.el.classList.add("minimized"),this.wm.focusTop(),this.wm.changed()}restore(){this.minimized=!1,this.el.classList.remove("minimized"),this.focus()}toggleMax(){const t=this.el.classList.toggle("maximized"),e=this.el.querySelector(".xp-max");e==null||e.setAttribute("aria-label",t?"Restore":"Maximize"),e==null||e.setAttribute("title",t?"Restore":"Maximize"),this.wm.changed()}close(){var t,e;this.closed||(this.closed=!0,this.el.remove(),this.wm.remove(this),(e=(t=this.opts).onClose)==null||e.call(t))}}class ov{constructor(t){I(this,"windows",[]);I(this,"active",null);I(this,"z",30);I(this,"listeners",new Set);this.desk=t,addEventListener("resize",()=>{for(const e of this.windows)e.clamp()})}get list(){return this.windows}get activeWindow(){return this.active}get(t){return this.windows.find(e=>e.opts.id===t)}open(t){const e=this.get(t.id);if(e)return e.minimized?e.restore():e.focus(),e;const n=new av(t,this);return this.windows.push(n),n.place(this.desk),this.focus(n),n}focus(t){var n,i;if(t.closed)return;const e=this.active!==t;this.active=t,t.el.style.zIndex=String(++this.z);for(const r of this.windows)r.el.classList.toggle("inactive",r!==t);e&&((i=(n=t.opts).onFocus)==null||i.call(n)),this.changed()}deactivate(){if(this.active){this.active=null;for(const t of this.windows)t.el.classList.add("inactive");this.changed()}}focusTop(){const e=this.windows.filter(n=>!n.minimized).sort((n,i)=>Number(i.el.style.zIndex)-Number(n.el.style.zIndex))[0];e?this.focus(e):(this.active=null,this.changed())}remove(t){this.windows=this.windows.filter(e=>e!==t),this.active===t&&(this.active=null,this.focusTop()),this.changed()}closeTop(){const t=this.active&&!this.active.minimized?this.active:null;return t?(t.close(),!0):!1}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}changed(){for(const t of this.listeners)t()}}const Ol=document.getElementById("oikos");if(Ol)try{cv(Ol)}catch(s){console.error("oikos: boot failed; falling back to the plain directory",s),Ol.classList.remove("oikos-live"),(Ru=document.getElementById("oikos-boot"))==null||Ru.remove()}function lv(){var s;try{const t=document.createElement("canvas").getContext("webgl2");return(s=t==null?void 0:t.getExtension("WEBGL_lose_context"))==null||s.loseContext(),!!t}catch{return!1}}function ia(){return location.pathname+location.search}function cv(s){var jt,j;s.classList.add("oikos-live");const t=matchMedia("(prefers-reduced-motion: reduce)").matches,e=xi(),n=new Bu,i=new Map(e.sites.map(D=>[D.id,md(D,{dir:e,feeds:n})])),r=new Map(e.sites.map(D=>[D.id,D]));if(new URLSearchParams(location.search).has("contact")){hv(s,e.sites,i,n);return}const a=G("div",{id:"oikos-desktop"});s.append(a);const o=new ov(a);zx(s);const l={stack:[],i:-1,moving:!1,listeners:new Set},c=D=>{if(!(l.moving||l.stack[l.i]===D)){l.stack=l.stack.slice(0,l.i+1),l.stack.push(D),l.i=l.stack.length-1;for(const W of l.listeners)W()}},u=D=>{const W=l.stack[l.i+D];if(W!==void 0){l.i+=D,l.moving=!0;try{W==="~"?E.home():E.play(W)}finally{l.moving=!1}for(const st of l.listeners)st()}};let h=null,d=null,f=null,p=null,v=!1,m=null,g=0,w=!1;const E={dir:e,feeds:n,screens:i,site:D=>r.get(D),select(D){b(!1),h==null||h.focus(D)},play(D){const W=r.get(D);if(!W)return;if(c(D),b(!1),M.select(D),f===D&&d){h==null||h.play(D),d.win.minimized?d.win.restore():d.win.focus();return}const st=d;d=null,f=null,st==null||st.win.close(),history.replaceState(null,"",`${ia()}#${D}`);const lt=o.get("home");lt&&!lt.minimized&&h&&(lt.minimize(),w=!0);const rt=++g,Ft=Lt=>{if(rt===g){if(!Lt){h==null||h.eject(),history.replaceState(null,"",ia());const Ht=o.get("home");w&&(Ht!=null&&Ht.minimized)&&Ht.restore(),w=!1;return}f=D,h==null||h.keepAlive(D),d=new iv(W,E,o,()=>{if(f!==D)return;d=null,f=null,h==null||h.keepAlive(null),h==null||h.eject(),(h==null?void 0:h.focusedId)===D&&h.focus(null),history.replaceState(null,"",ia());const Ht=o.get("home");w&&(Ht!=null&&Ht.minimized)&&Ht.restore(),w=!1})}};h?h.play(D).then(Ft):Ft(!0)},open(D,W){if(W==null||W.preventDefault(),v)return;if(yn(D.href)){window.open(D.href,"_blank","noopener"),S.balloon(`${D.title} opened`,"It opened in a new window. The room is still here.");return}const st=D.href;v=!0,S.balloon(`Opening ${D.title}`,"tuning in…",void 0,2e3);let lt=!1;const rt=()=>{lt||(lt=!0,location.assign(st))};h?h.dive(D.id).then(rt):rt(),setTimeout(rt,1500)},look(D){b(!1),h==null||h.focus(D)},get canTune(){return!!h},tuneIn(D){const W=r.get(D);if(!h||!(W!=null&&W.tune))return;const st=typeof W.tune=="string"?W.tune:W.href;b(!1);const lt=o.list.filter(te=>!te.minimized&&!te.opts.dialog);for(const te of lt)te.minimize();S.hideBalloon();const rt=G("div",{class:"oikos-tube"});rt.style.setProperty("--glow",W.accent);const Ft=G("iframe",{src:st,title:`${W.title}, live`,allow:"autoplay; fullscreen; clipboard-write"});rt.append(Ft,G("i",{class:"roll"}),G("i",{class:"glass"})),Ft.addEventListener("load",()=>{var te;try{(te=Ft.contentWindow)==null||te.addEventListener("keydown",le=>{var Te;const ce=le.target;le.key!=="Escape"||le.defaultPrevented||(Te=ce==null?void 0:ce.closest)!=null&&Te.call(ce,"input, textarea, [contenteditable]")||b(!0)})}catch{}});const Lt=G("div",{class:"oikos-tuned",role:"toolbar","aria-label":"tuned in"});Lt.innerHTML=`${vn(W.accent)}tuned in · live on its screen`,Lt.querySelector("b").textContent=W.title;const Ht=G("button",{class:"xp-btn",type:"button"},["⏏ Eject"]);Ht.addEventListener("click",()=>b(!0));const Zt=G("a",{class:"xp-btn",href:W.href},["Open full ↗"]);yn(W.href)&&(Zt.setAttribute("target","_blank"),Zt.setAttribute("rel","noopener")),Lt.append(Ht,Zt),m={id:D,bar:Lt,hidden:lt,tube:rt};const Jt=te=>{const le=Math.round(Math.min(1024,Math.max(420,te.width*1.15))),ce=Math.round(le*.75);rt.style.width=`${le}px`,rt.style.height=`${ce}px`,rt.style.transform=`translate(${te.left}px, ${te.top}px) scale(${te.width/le}, ${te.height/ce})`};h.tuneIn(D,Jt).then(()=>{(m==null?void 0:m.bar)===Lt&&(s.append(rt,Lt),Ft.focus())})},chat(D){b(!1),p||(p=new nv(E,o,()=>p=null)),p.focus(D)},home(){c("~"),b(!1),M.open(),h==null||h.scroll("~ home")},eject(){b(!1),d==null||d.win.close(),h==null||h.eject(),h==null||h.focus(null)},balloon(D,W,st){S.balloon(D,W,st)},back:()=>u(-1),forward:()=>u(1),get canBack(){return l.i>0},get canForward(){return l.il.listeners.delete(D)},about:()=>R()},M=new Wx(E,o),S=new sv(s,E,o,{standby:D=>P(D),turnOff(){s.classList.add("off"),setTimeout(()=>location.assign("/"),t?50:750)},logOff(){b(!1);for(const D of[...o.list])D.close();h==null||h.eject(),h==null||h.focus(null),history.replaceState(null,"",ia()),S.balloon("Logged off","The desk is clear. Click the VCR to begin again.",(h==null?void 0:h.anchor("vcr"))??void 0)},restart(){try{sessionStorage.removeItem("oikos-booted")}catch{}location.reload()}});function b(D){if(!m)return;const{id:W,bar:st,hidden:lt,tube:rt}=m;if(m=null,st.remove(),rt.remove(),h==null||h.tuneOut(),!!D){for(const Ft of lt)Ft.closed||Ft.restore();h==null||h.focus(W)}}function R(){var Ht;(Ht=o.get("about"))==null||Ht.close();const D=G("div",{class:"xp-about-box"}),W=G("div",{class:"band",html:Js(44,!0)});W.append(G("b",{},["æthera"]),G("span",{},["oikos"]));const st=G("div",{class:"xp-dialog-body"}),lt=G("div");lt.append(G("p",{},["æthera · the home directory"]),G("p",{},["Version 1998 (Build 2026.present_day)"]),G("p",{},["A room of screens wired to one VCR, and every part of the site playing at once."]),G("p",{class:"lic"},["This product is licensed under CC BY 4.0 to:"]),G("p",{},["guest"])),st.append(lt);const rt=G("div",{class:"xp-actions"}),Ft=G("button",{class:"xp-btn default",type:"button"},["OK"]);rt.append(Ft),D.append(W,st,rt);const Lt=o.open({id:"about",title:"About æthera",icon:au(),body:D,width:400,dialog:!0});Ft.addEventListener("click",()=>Lt.close()),Ft.focus()}if(lv())try{h=new Tx(s,e.sites,i,{pick(D){L(),S.hideBalloon(),S.showTip(null,null,0,0),D==="vcr"?E.home():D?E.play(D):h!=null&&h.focusedId&&!d&&!h.busy&&h.focus(null)},hover(D,W,st){D==="vcr"?S.showTip(null,{title:"VCR",text:"your home directory · click to open ~"},W,st):S.showTip(D?r.get(D)??null:null,null,W,st)}})}catch(D){console.warn("oikos: the room would not build; running the desktop alone",D),h==null||h.stop(),h=null}if(h==null||h.renderer.domElement.addEventListener("pointerdown",()=>o.deactivate()),Gx(s,(D,W)=>{var Ft,Lt;const st=D.closest(".xp-tile");if(st)return st.dataset.id?M.contextFor(st.dataset.id):null;if(D.closest("a"))return null;if(D.closest(".xp-content")&&((Lt=(Ft=D.closest(".xp-window"))==null?void 0:Ft.getAttribute("aria-label"))!=null&&Lt.startsWith("~")))return M.blankMenu();if(D.closest(".xp-window, .xp-startmenu, .xp-shutdown, .xp-menu, .xp-balloon"))return[];if(!h||D!==h.renderer.domElement)return[];const lt=h.pickFromPoint(W.clientX,W.clientY);if(lt==="vcr")return[{label:"Open ~",bold:!0,run:()=>E.home()},{label:"Eject",run:()=>E.eject(),disabled:!f},"sep",{label:"Properties",disabled:!0}];const rt=lt?r.get(lt):void 0;return rt?na(E,rt):[{label:"Arrange Icons By",disabled:!0,submenu:()=>[]},{label:"Refresh",run:()=>h==null?void 0:h.focus(null)},"sep",{label:"Paste",disabled:!0},{label:"Paste Shortcut",disabled:!0},"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"~ Home directory",run:()=>E.home()},{label:"Properties",disabled:!0}]}),!h){let D=performance.now();const W=st=>{var rt;requestAnimationFrame(W);const lt=Math.min(.1,(st-D)/1e3);D=st,f&&((rt=i.get(f))==null||rt.tick(st/1e3,lt,!0))};requestAnimationFrame(W)}const x=()=>{if(!h)return;const D=innerWidth,W=innerHeight-30,st=o.list.filter(Ft=>!Ft.minimized&&!Ft.opts.dialog&&!Ft.el.classList.contains("maximized"));if(D<=720){h.setShift(0,st.length?W*.3:0);return}let lt=0,rt=D;for(const Ft of st){const Lt=Ft.el.getBoundingClientRect();Lt.left+Lt.width/2[W.channel,D])),q=e.sites.map(D=>D.id).sort((D,W)=>{var st,lt;return(((st=ir[D])==null?void 0:st.channel)??99)-(((lt=ir[W])==null?void 0:lt.channel)??99)});let Y="",B=0;addEventListener("keydown",D=>{if(T){L();return}const W=D.target;if(!W.closest("input, textarea")){if(D.key==="Escape"&&m){b(!0);return}if(!m){if(D.key==="Escape"){if(pi()||S.dismiss()||!K())return;const st=o.activeWindow;if(st!=null&&st.opts.dialog&&!st.minimized){st.close();return}h!=null&&h.focusedId&&h.focus(null);return}if(D.altKey&&(D.key==="ArrowLeft"||D.key==="ArrowRight")){D.preventDefault(),u(D.key==="ArrowLeft"?-1:1);return}if(!W.closest(".xp-window, .xp-startmenu, .xp-menu, .xp-shutdown")){if(D.key==="~"||D.key==="Home"){E.home();return}if((D.key==="ArrowRight"||D.key==="ArrowLeft")&&!(h!=null&&h.busy)){const st=h!=null&&h.focusedId&&h.focusedId!=="vcr"?q.indexOf(h.focusedId):-1,lt=q[(st+(D.key==="ArrowRight"?1:-1)+q.length)%q.length];if(lt){h==null||h.focus(lt),M.select(lt);const rt=h==null?void 0:h.anchor(lt),Ft=r.get(lt);rt&&Ft&&S.showTip(Ft,null,rt.x-40,rt.y-50)}return}if(D.key==="Enter"&&(h!=null&&h.focusedId)&&h.focusedId!=="vcr"){E.play(h.focusedId);return}/^[0-9]$/.test(D.key)&&(Y=(Y+D.key).slice(-2),clearTimeout(B),h==null||h.scroll(`ch ${Y}`),B=window.setTimeout(()=>{const st=F.get(Number(Y));Y="",st&&r.has(st)&&E.play(st)},700))}}}});function K(){const D=s==null?void 0:s.querySelector(".xp-startmenu");return D&&!D.hidden?(S.toggleMenu(!1),!1):!0}let X=!1;document.addEventListener("visibilitychange",()=>{document.hidden?(h==null||h.stop(),n.stop(),m&&b(!0)):X&&(h==null||h.start(),n.start())}),addEventListener("pageshow",D=>{D.persisted&&(v=!1,s.classList.remove("off"),h==null||h.reset())});const Q=document.getElementById("oikos-boot");let it=!1;try{it=!!sessionStorage.getItem("oikos-booted"),sessionStorage.setItem("oikos-booted","1")}catch{}const pt=t||it?250:1900;let _t=()=>{};const bt=new Promise(D=>_t=D);Q==null||Q.addEventListener("click",()=>_t());const ee=Promise.race([Promise.all([(jt=document.fonts)==null?void 0:jt.load('16px "Libertinus Mono"'),(j=document.fonts)==null?void 0:j.load('16px "Love Letter Typewriter"')]).then(()=>{},()=>{}),new Promise(D=>setTimeout(D,1500))]),pe=(h==null?void 0:h.warm())??Promise.resolve();Promise.race([Promise.all([ee,pe,new Promise(D=>setTimeout(D,pt))]),bt]).then(()=>{X=!0,document.hidden||(n.start(),h==null||h.start()),h==null||h.powerOn(),Q==null||Q.classList.add("gone"),setTimeout(()=>Q==null?void 0:Q.remove(),800);const D=new URLSearchParams(location.search).get("look");D&&h&&(D==="vcr"||r.has(D))&&setTimeout(()=>h==null?void 0:h.focus(D),300);let W="";try{W=decodeURIComponent(location.hash.slice(1))}catch{}if(W==="mirc"){setTimeout(()=>E.chat(),600);return}if(W&&r.has(W)){setTimeout(()=>E.play(W),900);return}if(!h){E.home(),S.balloon("No room tonight","This browser has no WebGL, so the room of screens is dark. The tapes all still play.");return}setTimeout(()=>{if(o.list.length)return;const st=(h==null?void 0:h.anchor("vcr"))??void 0;S.balloon("This is the way in",innerWidth<=720?"Tap the VCR for the home directory, or any screen to tune in. Drag to look around.":"Click the VCR for your home directory, or any screen to tune in. Drag to look around; ← → walk the screens.",st,11e3,!0)},it?900:2600)})}function hv(s,t,e,n){var o;(o=document.getElementById("oikos-boot"))==null||o.remove();const i=G("div");i.style.cssText="position:absolute;inset:0;overflow:auto;padding:16px;display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));background:#0a0a0a;";for(const l of t){const c=e.get(l.id);if(!c)continue;const u=G("figure");u.style.cssText="margin:0;color:#aaa;font:12px ui-monospace,monospace;",c.canvas.style.cssText="width:100%;display:block;border:1px solid #333;";const h=G("figcaption",{},[`${l.title} · ${l.kind}`]);h.style.padding="4px 0",u.append(c.canvas,h),i.append(u)}s.append(i),n.start();let r=performance.now();const a=l=>{requestAnimationFrame(a);const c=Math.min(.1,(l-r)/1e3);r=l;for(const u of e.values())u.tick(l/1e3,c,!1)};requestAnimationFrame(a)}})(); + `;const i=n.querySelector(".xp-title");i&&(i.textContent=t.title),n.append(t.body),n.style.width=`${t.width}px`,this.el=n,(o=n.querySelector(".xp-close"))==null||o.addEventListener("click",()=>this.close()),(a=n.querySelector(".xp-min"))==null||a.addEventListener("click",()=>this.minimize()),(l=n.querySelector(".xp-max"))==null||l.addEventListener("click",()=>this.toggleMax()),n.addEventListener("pointerdown",()=>this.focus(),!0),this.bindDrag(n.querySelector(".xp-titlebar")),t.dialog||this.bindResize(),(c=n.querySelector(".xp-titlebar"))==null||c.addEventListener("dblclick",h=>{const d=h.target;d.closest(".xp-controls")||t.dialog||(d.closest(".xp-title-icon")?this.close():this.toggleMax())});const r=n.querySelector(".xp-title-icon");r==null||r.setAttribute("data-menu-owner",""),r==null||r.addEventListener("click",h=>{h.stopPropagation();const d=n.getBoundingClientRect();Us(this.systemMenu(),d.left+3,d.top+29)}),(u=n.querySelector(".xp-titlebar"))==null||u.addEventListener("contextmenu",h=>{const d=h;d.target.closest(".xp-controls")||(d.preventDefault(),d.stopPropagation(),Us(this.systemMenu(),d.clientX,d.clientY))})}get maximized(){return this.el.classList.contains("maximized")}systemMenu(){const t=!!this.opts.dialog;return[{label:"Restore",run:()=>this.minimized?this.restore():this.toggleMax(),disabled:t||!this.maximized&&!this.minimized},{label:"Move",disabled:!0},{label:"Size",disabled:!0},{label:"Minimize",run:()=>this.minimize(),disabled:t||this.minimized},{label:"Maximize",run:()=>{this.minimized&&this.restore(),this.maximized||this.toggleMax()},disabled:t||this.maximized},"sep",{label:"Close",run:()=>this.close(),bold:!0,shortcut:"Alt+F4"}]}setTitle(t){const e=this.el.querySelector(".xp-title");e&&(e.textContent=t),this.el.setAttribute("aria-label",t)}place(t){const e=t.clientWidth,n=t.clientHeight,i=Math.min(this.opts.width,e-16);this.el.style.width=`${i}px`,t.append(this.el);const r=Math.min(this.el.offsetHeight,n-16);let o=this.opts.x??0,a=this.opts.y??0;this.opts.x===void 0&&(this.opts.dock==="left"?o=18:this.opts.dock==="right"?o=e-i-18:o=(e-i)/2),this.opts.y===void 0&&(a=this.opts.dialog?(n-r)/2.4:Math.max(8,Math.min(60,(n-r)/2))),this.moveTo(o,a)}moveTo(t,e){const n=this.el.parentElement;if(!n)return;const i=n.clientWidth-60,r=n.clientHeight-30;this.el.style.left=`${Math.round(Math.max(-this.el.offsetWidth+80,Math.min(i,t)))}px`,this.el.style.top=`${Math.round(Math.max(0,Math.min(r,e)))}px`}bindDrag(t){let e=null;t.addEventListener("pointerdown",i=>{i.target.closest(".xp-controls, .xp-title-icon")||i.button!==0||this.el.classList.contains("maximized")||innerWidth<=720||(e={x:i.clientX,y:i.clientY,left:this.el.offsetLeft,top:this.el.offsetTop},t.setPointerCapture(i.pointerId))}),t.addEventListener("pointermove",i=>{e&&this.moveTo(e.left+i.clientX-e.x,e.top+i.clientY-e.y)});const n=()=>{e&&this.wm.changed(),e=null};t.addEventListener("pointerup",n),t.addEventListener("pointercancel",n)}bindResize(){for(const t of Ay){const e=document.createElement("div");e.className=`xp-grip xp-grip-${t}`,e.setAttribute("aria-hidden","true"),this.el.append(e);let n=null;e.addEventListener("pointerdown",r=>{r.button!==0||this.maximized||innerWidth<=720||(r.preventDefault(),n={x:r.clientX,y:r.clientY,left:this.el.offsetLeft,top:this.el.offsetTop,w:this.el.offsetWidth,h:this.el.offsetHeight},e.setPointerCapture(r.pointerId),this.focus())}),e.addEventListener("pointermove",r=>{if(!n)return;const o=this.el.parentElement;if(!o)return;const a=r.clientX-n.x,l=r.clientY-n.y;let{left:c,top:u,w:h,h:d}=n;t.includes("e")&&(h=Math.min(o.clientWidth-c,Math.max(dc,n.w+a))),t.includes("s")&&(d=Math.min(o.clientHeight-u,Math.max(fc,n.h+l))),t.includes("w")&&(h=Math.max(dc,Math.min(n.left+n.w,n.w-a)),c=n.left+n.w-h),t.includes("n")&&(d=Math.max(fc,Math.min(n.top+n.h,n.h-l)),u=n.top+n.h-d),this.el.style.left=`${Math.round(c)}px`,this.el.style.top=`${Math.round(u)}px`,this.el.style.width=`${Math.round(h)}px`,this.el.style.height=`${Math.round(d)}px`,this.el.style.maxHeight="none"});const i=()=>{n&&this.wm.changed(),n=null};e.addEventListener("pointerup",i),e.addEventListener("pointercancel",i)}}clamp(){if(this.maximized||this.minimized)return;const t=this.el.parentElement;t&&(this.el.offsetWidth>t.clientWidth&&(this.el.style.width=`${Math.max(dc,t.clientWidth-8)}px`),this.el.offsetHeight>t.clientHeight&&(this.el.style.height=`${Math.max(fc,t.clientHeight-8)}px`),this.moveTo(this.el.offsetLeft,this.el.offsetTop))}focus(){this.wm.focus(this)}minimize(){this.minimized=!0,this.el.classList.add("minimized"),this.wm.focusTop(),this.wm.changed()}restore(){this.minimized=!1,this.el.classList.remove("minimized"),this.focus()}toggleMax(){const t=this.el.classList.toggle("maximized"),e=this.el.querySelector(".xp-max");e==null||e.setAttribute("aria-label",t?"Restore":"Maximize"),e==null||e.setAttribute("title",t?"Restore":"Maximize"),this.wm.changed()}close(){var t,e;this.closed||(this.closed=!0,this.el.remove(),this.wm.remove(this),(e=(t=this.opts).onClose)==null||e.call(t))}}class Ry{constructor(t){R(this,"windows",[]);R(this,"active",null);R(this,"z",30);R(this,"listeners",new Set);this.desk=t,addEventListener("resize",()=>{for(const e of this.windows)e.clamp()})}get list(){return this.windows}get activeWindow(){return this.active}get(t){return this.windows.find(e=>e.opts.id===t)}open(t){const e=this.get(t.id);if(e)return e.minimized?e.restore():e.focus(),e;const n=new Cy(t,this);return this.windows.push(n),n.place(this.desk),this.focus(n),n}focus(t){var n,i;if(t.closed)return;const e=this.active!==t;this.active=t,t.el.style.zIndex=String(++this.z);for(const r of this.windows)r.el.classList.toggle("inactive",r!==t);e&&((i=(n=t.opts).onFocus)==null||i.call(n)),this.changed()}deactivate(){if(this.active){this.active=null;for(const t of this.windows)t.el.classList.add("inactive");this.changed()}}focusTop(){const e=this.windows.filter(n=>!n.minimized).sort((n,i)=>Number(i.el.style.zIndex)-Number(n.el.style.zIndex))[0];e?this.focus(e):(this.active=null,this.changed())}remove(t){this.windows=this.windows.filter(e=>e!==t),this.active===t&&(this.active=null,this.focusTop()),this.changed()}closeTop(){const t=this.active&&!this.active.minimized?this.active:null;return t?(t.close(),!0):!1}on(t){return this.listeners.add(t),()=>this.listeners.delete(t)}changed(){for(const t of this.listeners)t()}}const pc=document.getElementById("oikos");if(pc)try{Ly(pc)}catch(s){console.error("oikos: boot failed; falling back to the plain directory",s),pc.classList.remove("oikos-live"),(yd=document.getElementById("oikos-boot"))==null||yd.remove()}function Py(){var s;try{const t=document.createElement("canvas").getContext("webgl2");return(s=t==null?void 0:t.getExtension("WEBGL_lose_context"))==null||s.loseContext(),!!t}catch{return!1}}function Eo(){return location.pathname+location.search}function Ly(s){var ee,Q;s.classList.add("oikos-live");const t=matchMedia("(prefers-reduced-motion: reduce)").matches,e=zd(),n=new Gd,i=new Map(e.sites.map(I=>[I.id,kf(I,{dir:e,feeds:n})])),r=new Map(e.sites.map(I=>[I.id,I]));if(new URLSearchParams(location.search).has("contact")){Dy(s,e.sites,i,n);return}const o=V("div",{id:"oikos-desktop"});s.append(o);const a=new Ry(o);ly(s);const l={stack:[],i:-1,moving:!1,listeners:new Set},c=I=>{if(!(l.moving||l.stack[l.i]===I)){l.stack=l.stack.slice(0,l.i+1),l.stack.push(I),l.i=l.stack.length-1;for(const q of l.listeners)q()}},u=I=>{const q=l.stack[l.i+I];if(q!==void 0){l.i+=I,l.moving=!0;try{q==="~"?E.home():E.play(q)}finally{l.moving=!1}for(const st of l.listeners)st()}};let h=null,d=null,f=null,p=null,v=!1,m=null,g=0,S=!1;const E={dir:e,feeds:n,screens:i,site:I=>r.get(I),select(I){w(!1),h==null||h.focus(I)},play(I){const q=r.get(I);if(!q)return;if(c(I),w(!1),y.select(I),f===I&&d){h==null||h.play(I),d.win.minimized?d.win.restore():d.win.focus();return}const st=d;d=null,f=null,st==null||st.win.close(),history.replaceState(null,"",`${Eo()}#${I}`);const ut=a.get("home");ut&&!ut.minimized&&h&&(ut.minimize(),S=!0);const lt=++g,Ot=Dt=>{if(lt===g){if(!Dt){h==null||h.eject(),history.replaceState(null,"",Eo());const Vt=a.get("home");S&&(Vt!=null&&Vt.minimized)&&Vt.restore(),S=!1;return}f=I,h==null||h.keepAlive(I),d=new Ey(q,E,a,()=>{if(f!==I)return;d=null,f=null,h==null||h.keepAlive(null),h==null||h.eject(),(h==null?void 0:h.focusedId)===I&&h.focus(null),history.replaceState(null,"",Eo());const Vt=a.get("home");S&&(Vt!=null&&Vt.minimized)&&Vt.restore(),S=!1})}};h?h.play(I).then(Ot):Ot(!0)},open(I,q){if(q==null||q.preventDefault(),v)return;if(Ec(I.href)){window.open(I.href,"_blank","noopener"),M.balloon(`${I.title} opened`,"It opened in a new window. The room is still here.");return}const st=I.href;v=!0,M.balloon(`Opening ${I.title}`,"tuning in…",void 0,2e3);let ut=!1;const lt=()=>{ut||(ut=!0,location.assign(st))};h?h.dive(I.id).then(lt):lt(),setTimeout(lt,1500)},look(I){w(!1),h==null||h.focus(I)},get canTune(){return!!h},tuneIn(I){const q=r.get(I);if(!h||!(q!=null&&q.tune))return;const st=typeof q.tune=="string"?q.tune:q.href;w(!1);const ut=a.list.filter(ne=>!ne.minimized&&!ne.opts.dialog);for(const ne of ut)ne.minimize();M.hideBalloon();const lt=V("div",{class:"oikos-tube"});lt.style.setProperty("--glow",q.accent);const Ot=V("iframe",{src:st,title:`${q.title}, live`,allow:"autoplay; fullscreen; clipboard-write"});lt.append(Ot,V("i",{class:"roll"}),V("i",{class:"glass"})),Ot.addEventListener("load",()=>{var ne;try{(ne=Ot.contentWindow)==null||ne.addEventListener("keydown",ce=>{var Ce;const he=ce.target;ce.key!=="Escape"||ce.defaultPrevented||(Ce=he==null?void 0:he.closest)!=null&&Ce.call(he,"input, textarea, [contenteditable]")||w(!0)})}catch{}});const Dt=V("div",{class:"oikos-tuned",role:"toolbar","aria-label":"tuned in"});Dt.innerHTML=`${Sn(q.accent)}tuned in · live on its screen`,Dt.querySelector("b").textContent=q.title;const Vt=V("button",{class:"xp-btn",type:"button"},["⏏ Eject"]);Vt.addEventListener("click",()=>w(!0));const jt=V("a",{class:"xp-btn",href:q.href},["Open full ↗"]);Ec(q.href)&&(jt.setAttribute("target","_blank"),jt.setAttribute("rel","noopener")),Dt.append(Vt,jt),m={id:I,bar:Dt,hidden:ut,tube:lt};const Qt=ne=>{const ce=Math.round(Math.min(1024,Math.max(420,ne.width*1.15))),he=Math.round(ce*.75);lt.style.width=`${ce}px`,lt.style.height=`${he}px`,lt.style.transform=`translate(${ne.left}px, ${ne.top}px) scale(${ne.width/ce}, ${ne.height/he})`};h.tuneIn(I,Qt).then(()=>{(m==null?void 0:m.bar)===Dt&&(s.append(lt,Dt),Ot.focus())})},chat(I){w(!1),p||(p=new wy(E,a,()=>p=null)),p.focus(I)},home(){c("~"),w(!1),y.open(),h==null||h.scroll("~ home")},eject(){w(!1),d==null||d.win.close(),h==null||h.eject(),h==null||h.focus(null)},balloon(I,q,st){M.balloon(I,q,st)},back:()=>u(-1),forward:()=>u(1),get canBack(){return l.i>0},get canForward(){return l.il.listeners.delete(I)},about:()=>C()},y=new dy(E,a),M=new Ty(s,E,a,{standby:I=>L(I),turnOff(){s.classList.add("off"),setTimeout(()=>location.assign("/"),t?50:750)},logOff(){w(!1);for(const I of[...a.list])I.close();h==null||h.eject(),h==null||h.focus(null),history.replaceState(null,"",Eo()),M.balloon("Logged off","The desk is clear. Click the VCR to begin again.",(h==null?void 0:h.anchor("vcr"))??void 0)},restart(){try{sessionStorage.removeItem("oikos-booted")}catch{}location.reload()}});function w(I){if(!m)return;const{id:q,bar:st,hidden:ut,tube:lt}=m;if(m=null,st.remove(),lt.remove(),h==null||h.tuneOut(),!!I){for(const Ot of ut)Ot.closed||Ot.restore();h==null||h.focus(q)}}function C(){var Vt;(Vt=a.get("about"))==null||Vt.close();const I=V("div",{class:"xp-about-box"}),q=V("div",{class:"band",html:mr(44,!0)});q.append(V("b",{},["æthera"]),V("span",{},["oikos"]));const st=V("div",{class:"xp-dialog-body"}),ut=V("div");ut.append(V("p",{},["æthera · the home directory"]),V("p",{},["Version 1998 (Build 2026.present_day)"]),V("p",{},["A room of screens wired to one VCR, and every part of the site playing at once."]),V("p",{class:"lic"},["This product is licensed under CC BY 4.0 to:"]),V("p",{},["guest"])),st.append(ut);const lt=V("div",{class:"xp-actions"}),Ot=V("button",{class:"xp-btn default",type:"button"},["OK"]);lt.append(Ot),I.append(q,st,lt);const Dt=a.open({id:"about",title:"About æthera",icon:Ju(),body:I,width:400,dialog:!0});Ot.addEventListener("click",()=>Dt.close()),Ot.focus()}if(Py())try{h=new Y_(s,e.sites,i,{pick(I){D(),M.hideBalloon(),M.showTip(null,null,0,0),I==="vcr"?E.home():I?E.play(I):h!=null&&h.focusedId&&!d&&!h.busy&&h.focus(null)},hover(I,q,st){I==="vcr"?M.showTip(null,{title:"VCR",text:"your home directory · click to open ~"},q,st):M.showTip(I?r.get(I)??null:null,null,q,st)}})}catch(I){console.warn("oikos: the room would not build; running the desktop alone",I),h==null||h.stop(),h=null}if(h==null||h.renderer.domElement.addEventListener("pointerdown",()=>a.deactivate()),hy(s,(I,q)=>{var Ot,Dt;const st=I.closest(".xp-tile");if(st)return st.dataset.id?y.contextFor(st.dataset.id):null;if(I.closest("a"))return null;if(I.closest(".xp-content")&&((Dt=(Ot=I.closest(".xp-window"))==null?void 0:Ot.getAttribute("aria-label"))!=null&&Dt.startsWith("~")))return y.blankMenu();if(I.closest(".xp-window, .xp-startmenu, .xp-shutdown, .xp-menu, .xp-balloon"))return[];if(!h||I!==h.renderer.domElement)return[];const ut=h.pickFromPoint(q.clientX,q.clientY);if(ut==="vcr")return[{label:"Open ~",bold:!0,run:()=>E.home()},{label:"Eject",run:()=>E.eject(),disabled:!f},"sep",{label:"Properties",disabled:!0}];const lt=ut?r.get(ut):void 0;return lt?wo(E,lt):[{label:"Arrange Icons By",disabled:!0,submenu:()=>[]},{label:"Refresh",run:()=>h==null?void 0:h.focus(null)},"sep",{label:"Paste",disabled:!0},{label:"Paste Shortcut",disabled:!0},"sep",{label:"New",disabled:!0,submenu:()=>[]},"sep",{label:"~ Home directory",run:()=>E.home()},{label:"Properties",disabled:!0}]}),!h){let I=performance.now();const q=st=>{var lt;requestAnimationFrame(q);const ut=Math.min(.1,(st-I)/1e3);I=st,f&&((lt=i.get(f))==null||lt.tick(st/1e3,ut,!0))};requestAnimationFrame(q)}const x=()=>{if(!h)return;const I=innerWidth,q=innerHeight-30,st=a.list.filter(Ot=>!Ot.minimized&&!Ot.opts.dialog&&!Ot.el.classList.contains("maximized"));if(I<=720){h.setShift(0,st.length?q*.3:0);return}let ut=0,lt=I;for(const Ot of st){const Dt=Ot.el.getBoundingClientRect();Dt.left+Dt.width/2[q.channel,I])),K=e.sites.map(I=>I.id).sort((I,q)=>{var st,ut;return(((st=wr[I])==null?void 0:st.channel)??99)-(((ut=wr[q])==null?void 0:ut.channel)??99)});let Z="",N=0;addEventListener("keydown",I=>{if(T){D();return}const q=I.target;if(!q.closest("input, textarea")){if(I.key==="Escape"&&m){w(!0);return}if(!m){if(I.key==="Escape"){if(bi()||M.dismiss()||!W())return;const st=a.activeWindow;if(st!=null&&st.opts.dialog&&!st.minimized){st.close();return}h!=null&&h.focusedId&&h.focus(null);return}if(I.altKey&&(I.key==="ArrowLeft"||I.key==="ArrowRight")){I.preventDefault(),u(I.key==="ArrowLeft"?-1:1);return}if(!q.closest(".xp-window, .xp-startmenu, .xp-menu, .xp-shutdown")){if(I.key==="~"||I.key==="Home"){E.home();return}if((I.key==="ArrowRight"||I.key==="ArrowLeft")&&!(h!=null&&h.busy)){const st=h!=null&&h.focusedId&&h.focusedId!=="vcr"?K.indexOf(h.focusedId):-1,ut=K[(st+(I.key==="ArrowRight"?1:-1)+K.length)%K.length];if(ut){h==null||h.focus(ut),y.select(ut);const lt=h==null?void 0:h.anchor(ut),Ot=r.get(ut);lt&&Ot&&M.showTip(Ot,null,lt.x-40,lt.y-50)}return}if(I.key==="Enter"&&(h!=null&&h.focusedId)&&h.focusedId!=="vcr"){E.play(h.focusedId);return}/^[0-9]$/.test(I.key)&&(Z=(Z+I.key).slice(-2),clearTimeout(N),h==null||h.scroll(`ch ${Z}`),N=window.setTimeout(()=>{const st=U.get(Number(Z));Z="",st&&r.has(st)&&E.play(st)},700))}}}});function W(){const I=s==null?void 0:s.querySelector(".xp-startmenu");return I&&!I.hidden?(M.toggleMenu(!1),!1):!0}let B=!1;document.addEventListener("visibilitychange",()=>{document.hidden?(h==null||h.stop(),n.stop(),m&&w(!0)):B&&(h==null||h.start(),n.start())}),addEventListener("pageshow",I=>{I.persisted&&(v=!1,s.classList.remove("off"),h==null||h.reset())});const X=document.getElementById("oikos-boot");let nt=!1;try{nt=!!sessionStorage.getItem("oikos-booted"),sessionStorage.setItem("oikos-booted","1")}catch{}const ot=t||nt?250:1900;let at=()=>{};const vt=new Promise(I=>at=I);X==null||X.addEventListener("click",()=>at());const Gt=Promise.race([Promise.all([(ee=document.fonts)==null?void 0:ee.load('16px "Libertinus Mono"'),(Q=document.fonts)==null?void 0:Q.load('16px "Love Letter Typewriter"')]).then(()=>{},()=>{}),new Promise(I=>setTimeout(I,1500))]),me=(h==null?void 0:h.warm())??Promise.resolve();Promise.race([Promise.all([Gt,me,new Promise(I=>setTimeout(I,ot))]),vt]).then(()=>{B=!0,document.hidden||(n.start(),h==null||h.start()),h==null||h.powerOn(),X==null||X.classList.add("gone"),setTimeout(()=>X==null?void 0:X.remove(),800);const I=new URLSearchParams(location.search).get("look");I&&h&&(I==="vcr"||r.has(I))&&setTimeout(()=>h==null?void 0:h.focus(I),300);let q="";try{q=decodeURIComponent(location.hash.slice(1))}catch{}if(q==="mirc"){setTimeout(()=>E.chat(),600);return}if(q&&r.has(q)){setTimeout(()=>E.play(q),900);return}if(!h){E.home(),M.balloon("No room tonight","This browser has no WebGL, so the room of screens is dark. The tapes all still play.");return}setTimeout(()=>{if(a.list.length)return;const st=(h==null?void 0:h.anchor("vcr"))??void 0;M.balloon("This is the way in",innerWidth<=720?"Tap the VCR for the home directory, or any screen to tune in. Drag to look around.":"Click the VCR for your home directory, or any screen to tune in. Drag to look around; ← → walk the screens.",st,11e3,!0)},nt?900:2600)})}function Dy(s,t,e,n){var a;(a=document.getElementById("oikos-boot"))==null||a.remove();const i=V("div");i.style.cssText="position:absolute;inset:0;overflow:auto;padding:16px;display:grid;gap:14px;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));background:#0a0a0a;";for(const l of t){const c=e.get(l.id);if(!c)continue;const u=V("figure");u.style.cssText="margin:0;color:#aaa;font:12px ui-monospace,monospace;",c.canvas.style.cssText="width:100%;display:block;border:1px solid #333;";const h=V("figcaption",{},[`${l.title} · ${l.kind}`]);h.style.padding="4px 0",u.append(c.canvas,h),i.append(u)}s.append(i),n.start();let r=performance.now();const o=l=>{requestAnimationFrame(o);const c=Math.min(.1,(l-r)/1e3);r=l;for(const u of e.values())u.tick(l/1e3,c,!1)};requestAnimationFrame(o)}})(); diff --git a/aethera/templates/afterlife/viewer.html b/aethera/templates/afterlife/viewer.html new file mode 100644 index 0000000..e62fae6 --- /dev/null +++ b/aethera/templates/afterlife/viewer.html @@ -0,0 +1,97 @@ +{% extends "base.html" %} + +{% block title %}afterlife | æthera{% endblock %} + +{% block meta %} + + + + + + + + + + + +{% endblock %} + +{% block schema %}{% endblock %} + +{% block extra_head %} + + +{% endblock %} + +{% block content %} +{# + Keep this skeleton in step with afterlife/index.html (the dev page). It + lives in the template rather than being built by main.ts so the splash + paints before the bundle has parsed. + + hx-boost="false" on the way out is load-bearing (see syrinx/viewer.html): + an htmx swap would leave the universe, its AudioContext and its loop + running under the next page. A real navigation also fires pagehide, which + is when the universe is saved. +#} + +
+ +
+ +

afterlife

+

the game of life, after hours

+

click to listen · ? for the keys · q to leave, if you cantap to listen · the keys are in the corner

+
+ + +
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/afterlife/README.md b/afterlife/README.md new file mode 100644 index 0000000..38dac5f --- /dev/null +++ b/afterlife/README.md @@ -0,0 +1,124 @@ +# afterlife + +> *the game of life, after hours.* A web port of +> [afterlife](https://github.com/LuxiaSL/afterlife), the terminal screensaver. +> Served at **`/afterlife`**, and on channel 10 in [oikos](../oikos). + +Conway's Life on a toroidal world five times the size of the screen. A camera +steers toward the drama. A dramaturge stages collisions and aims gliders at +the citizens it has recognised. A ticker muses. The music scores it all, and +`g` brings back the bug that painted the void magenta. + +It is meant to be the same program, not a tribute. The terminal was the +medium, so the page is a terminal: a canvas drawn as a grid of character +cells. Every cell is an upper-half block in the same xterm-256 colours curses +asked for, and the status line keeps to the grid one glyph per column. + +## Faithful, and how we know + +The engine (`src/engine/`) and the music (`src/music/engine.ts`) are +method-for-method ports of `life.py` and `life_music.py`. Two scripts replay +the Python original and compare the results: + +```bash +# from the afterlife checkout (a uv project; scipy is an optional extra there) +cd /path/to/afterlife +uv run --with scipy python /path/to/scripts/parity.py . > /tmp/parity.json +npm run parity -- /tmp/parity.json # "the same universe ✓" + +python scripts/parity_music.py /path/to/afterlife > /tmp/music.json +npm run parity-music -- /tmp/music.json # "the same music ✓" +``` + +`parity.py` refuses to run without scipy: `life.py` quietly turns its census +off without it, and the recording would be of the wrong universe. Recording +takes about two minutes, and so does the replay. + +`parity` replays 22 cases, in three kinds: + +- **still** (16): `life.py`'s `InfiniteLife` from a seeded genesis with the + dice taken out, so the physics and every read-out are deterministic. Nine + seeds; tiny, absurdly tiny, odd and 1080p terminals; travellers crossing + every seam of the torus; haunted mode; pan, zoom, toggle, focus and home; + and resizes, where a new world adopts the old one. +- **live** (4): the dice stay in. `life.py`'s `random` and `np.random` are + swapped for one mulberry32 stream that the port draws from too. Genesis, + every injection, a garden at generation 5000, and on-cue provokes and + collisions then have to draw the same numbers in the same order. One thing + is pinned: `_find_quiet_spot` orders tied scores by + `np.argpartition`, which is numpy's implementation detail, so the live + cases give it a stable sort, as the port uses. +- **focus** (2): hundreds of small worlds of stamped clumps, for + `auto_focus`'s hotspot (scipy's `uniform_filter` breaks exact ties by its + own rounding, and the port reproduces it) and its percentiles. + +Every step is checked: population, pop floor, spread, cycle detection, engine +events, camera, auto-zoom, mood, time dilation, the activity centroid, a +checksum of every age, the smoothed ages and activity, the display maps every +seventh step, the census and its sites every 150, and, in live cases, the +draw count. At the end it checks the final ages, the display maps at all five +zooms, the sparkline, the epoch and auto-focus. All of it matches exactly. +`parity-music` feeds both engines the same scripted evening: a boom, a broken +cycle and its cadence, two style crossfades, noise bursts, and epoch roots. +Every sample matches within float32 rounding (worst |Δ| ≈ 2e-7). + +## What changed for the web, and why + +- **The universe lives in localStorage** (`afterlife-universe-v1`), not + `universe.npz`. It is saved when you leave (`pagehide`, or the tab being + hidden) and every 5000 generations, and resumed on the next visit with + *the universe remembers generation N*. Only ages are stored, sparsely (the + grid is exactly `age > 0`), because a save must be synchronous on the way + out. oikos reads the same key, so its screen runs *your* universe. +- **Music waits for a gesture.** Browsers only start sound from a click or a + key, so the splash asks for one. Until then the status line shows no music, + just as the terminal did without PyAudio. The engine runs in an + AudioWorklet at 44.1 kHz and renders the same 2048-frame buffers PyAudio + asked for, because its smoothing constants are per buffer. `vite.config.ts` + bundles the worklet with esbuild and inlines it into the page. +- **`q` leaves**: back where you came from, or to æthera. Inside an oikos + screen it can't leave, and the ticker says so; ⏏ is on the other side of the + glass. **`d`** records snapshots as before and downloads `snapshots.jsonl` + when you stop (`life_music_diag.py --replay` reads it). `life_stats.csv` is + kept in memory and downloads from the keys panel. +- **More than a terminal's mouse.** A click still toggles a cell. You can + also drag to draw life, right-drag or shift-drag to pan, and scroll to zoom. + On touch: tap to toggle, drag to pan, pinch to zoom. The **keys** panel + (`?`) lists every key and runs it on click, for phones. +- **Narrow screens get a two-line status.** On a phone the ticker would lose + its room to the stats and fall back to the terminal's compact line, so the + ticker gets a line of its own instead. The sparkline shortens to fit. +- **Resizing keeps the universe**, as `KEY_RESIZE` did: it is adopted, + centred, into the new world. +- Two display caches are now invalidated on pan, zoom and toggle. In the + terminal, a pan or click made while paused didn't show until you unpaused. + +## Layout + +``` +src/engine/ life.ts (InfiniteLife), census, ticker, musings, constants, persist, stats +src/term/ render.ts (the terminal: grid, status line, stats overlay), palette.ts (xterm-256) +src/music/ engine.ts (LifeMusicEngine), worklet.ts, audio.ts (the page's side) +src/main.ts life.py's main(): the loop, keys, pointer, splash, persistence +scripts/ parity checks, bench.ts (life_bench.py), shot.mjs (headless screenshots) +``` + +The engine and renderer have no DOM dependencies beyond a 2D context, so +oikos imports them directly (`oikos/src/screens/afterlife.ts`). + +## Build + +Like syrinx, apeiron and oikos, the built bundle is committed. The Docker +image never runs npm, so rebuild and commit whenever `src/` changes. Rebuild +oikos too if `src/engine` or `src/term` changed, since it bundles them. + +```bash +npm install +npm run build # → ../aethera/static/afterlife/afterlife.{js,css} +npm run dev # the standalone index.html on :5198 +npm run bench # per-frame cost, headless (240×66 terminal by default) +npm run shot -- out/ [url] # splash, lived, stats, haunted, keys, resume, phone +``` + +`shot` wants Playwright's Chromium. Set `CHROMIUM=/path/to/chrome` if the +downloaded one doesn't match. diff --git a/afterlife/index.html b/afterlife/index.html new file mode 100644 index 0000000..0a9799a --- /dev/null +++ b/afterlife/index.html @@ -0,0 +1,77 @@ + + + + + + afterlife + + + + +
+ +
+ +

afterlife

+

the game of life, after hours

+

click to listen · ? for the keys · q to leave, if you cantap to listen · the keys are in the corner

+
+ + +
+ + + diff --git a/afterlife/package-lock.json b/afterlife/package-lock.json new file mode 100644 index 0000000..da8b8c5 --- /dev/null +++ b/afterlife/package-lock.json @@ -0,0 +1,1689 @@ +{ + "name": "afterlife", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "afterlife", + "version": "1.0.0", + "devDependencies": { + "@types/node": "^22.20.4", + "playwright": "^1.61.1", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vite": "^6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.5.tgz", + "integrity": "sha512-J25QJU+B78T4FhhBsNpLJyVWOi31mwtpcMwywHmOKH65Q9IWGA81gPj+dnwlhU8wktVriYE+tFAaQgrnJRzAZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.5.tgz", + "integrity": "sha512-LDopB3zuZM5Ux9TT2luNEBJW/tYbGU2g1d+VpKk6I+gSKDb+/7sYE6M225gRQt4RbMX6MSwMsVR/phdjVUgRLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.5.tgz", + "integrity": "sha512-wlJEERGfeuHeBavCL2qVnNacOK43NDoZM4sjkeRPymd04OAE9T1zBqDJgmZ+CIsPTYKwdzpUC8vmOw84dwY4Tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.5.tgz", + "integrity": "sha512-4nJJGg5jbo2wwPP4JP+LfEBA3bvP8rU9CLuhp7jWvq9sxEyhjQFTFdrqi+/dHEin/pd8jpT0vcehIpnZtmEdcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.5.tgz", + "integrity": "sha512-DrZbyCDF1hneuO6jRbvZ2D7+PIBM6yIwYnJpg2vIk58T+wuFpiaGZrfUr59lDWw45bg+IrpTGLPiNi/Fk4w3Cg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.5.tgz", + "integrity": "sha512-gqfUVMJMB3mehqywxp6hTBFfgtMQykZY19+cfiaYP0toIJLb/1DZRJHVkQQGP13W4TAwfZDWeg1qBcheTRioXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.5.tgz", + "integrity": "sha512-CFmhpvAwzSaWMlN3VN7UtmoTihlZNzoP0juQib5TQRnYUyDV8dXeWOp29sobWAT6gXl/hQgAClLlEiYozQG3OQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.5.tgz", + "integrity": "sha512-Uc9H8eXCOayV6JLTH5bXKMId6qbhNHa818/BgYjm4jrlq3vZquC9cqyvHBw17xy5Mnj5f+I3gFK5JcEf3hSqrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.5.tgz", + "integrity": "sha512-VcPr/szv/1BFw112Kt//fxulXt/JPqzzidU84iW68L2DdjnOO8QFUv2zTSYBEPHD6movBD4z+bbr5y60GYM7Jw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.5.tgz", + "integrity": "sha512-BnxtJ5/91BrIHYIkGrmjz/lbMhqEHt1dPFqIxIFR+jPn0xVc/oUSCtIT089zfp5ufwGDlYz2UC+Fe1SRBpYFbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.5.tgz", + "integrity": "sha512-LrYcHZwF+fAMNKHYTOQ5osWM4AZF7YF6D+XtsjDyEvljtt11twc+zHVXBLNEjxVSUnKYsOhvVz4Z213eW02COQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.5.tgz", + "integrity": "sha512-nj7QKQePAAUpCpJHtg0pR0W/b92A9NO17JS3BAQmHDn/yhmkir2p8llrKY9TOhleKIaSzy1JhxS3T9FVld6coA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.5.tgz", + "integrity": "sha512-5ylkX6dWMeBKge9nTU+Rxfb+ZfaCIJ9lRqIFaK0eAMcWp7OJbYnLveLgXmm0VrvuLKb8qIK+mHyH0qu88RM+iA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.5.tgz", + "integrity": "sha512-oHK4ZHYFDKjZviK34I+NwgfbGxgI7ztrNxj2hPTSSNFgeq1a/lEd7dHV2fdGAuTH4Iym3RHJg+vAbWaWG4B7Zg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.5.tgz", + "integrity": "sha512-UcetmHZ6XOXuUByiKZyQmb55ZPr0LABr3Ec/HB9wKZn6CEAFWZkE+hsJErJ9hbPBC7nI0dKuELx7CoV6IM7TMg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.5.tgz", + "integrity": "sha512-C5CmDPQBtvjVo8cgQsBs+w6WB0JLkiixhgi6hVLV11hERWdn/p0XcPU2OUcZzac9BPOFq7SbaHFa8r3SWEysCQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.5.tgz", + "integrity": "sha512-lHVQHJFKsuuxLMi3MQO9XVL8Tje3JR82CzB+QDKC5NWBcsIWuwsn9uIM5e3lBhI+fF1/s63qnyYqsg65+8rV/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.5.tgz", + "integrity": "sha512-3W9bTFcQNJn71cSJVM9RKIiZOy8DO/XLDii8Uv/Pm6WKqDRj7JV3ZfuXIEfyuy5LXpIzAbB/1M4Ukp9GKNa7nA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.5.tgz", + "integrity": "sha512-VDC7rRJlee/scpki96GZ27Omf6yU87s1YXwVTpjE5841faVlDYYT565rgfmoR1U0sqL7z5ivQSDjcsF6VRXyBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.5.tgz", + "integrity": "sha512-z86Ok2p4pTdv5xqCKZsTooO7yBEiaJR/HzU3Wx8RmWsPoLppnMKROhJusQob8B3IE1ghC343kUW9rC2r+Wf3ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.5.tgz", + "integrity": "sha512-IzQmj+xXwQFGhMAMKMQVXkMwMZN3TqkJgAE0nSsqvVwWWciP4AIPMmWRqOQ2GfX7TUDZr+xqGFcBS36CRPGw0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.5.tgz", + "integrity": "sha512-F6qpTaPc9bwBH85kjy0/BLmLSW1uv7AoOXCoRIkg2arlgCYlWYcAbiMkvZuAcaWk9TpCRG//okznLAqLGshkMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.5.tgz", + "integrity": "sha512-igoDsTFhhwECBeGbUuLeIk7t8Y1apa+cs6mDWpx2EZ0ch7oEQgzHbFUXN9euoHekCAQzXdXApAGkV6jznS7tWw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.5.tgz", + "integrity": "sha512-U3teMeMbXFmaM5D+OTJpsOXd+wV/qftIeYF9kBKL4v73641qyJmoXFtA28DQLsnmlyayEsTe72xpLHrArq6vHw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.5.tgz", + "integrity": "sha512-ypfC34F3RKXvCXBglGqGMsUSMKlgwd1HX9AOAlx9RoZZ6GaI42YHVeKpzg3JG+wpBUJYTG+NNZhqbDWL8tBZkw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.4.tgz", + "integrity": "sha512-zJRE40jpHtKqE/C4fgHrAKQLJuSpzEnP9ff9Y7YtoR3Wd2pwqzlekDeEuUQXjRd+QCYnVnNwuJYmhdk9XV8gvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.63.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.5.tgz", + "integrity": "sha512-KRWwmNLlPw5M7HcdYfm15oBv9n9LPtjzpzCIxS/phwqvPyxHSoKX6Y2YU3pxSPfy0CLquVgsx/j/hBi6OvH1Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.5", + "@rollup/rollup-android-arm64": "4.63.5", + "@rollup/rollup-darwin-arm64": "4.63.5", + "@rollup/rollup-darwin-x64": "4.63.5", + "@rollup/rollup-freebsd-arm64": "4.63.5", + "@rollup/rollup-freebsd-x64": "4.63.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.5", + "@rollup/rollup-linux-arm-musleabihf": "4.63.5", + "@rollup/rollup-linux-arm64-gnu": "4.63.5", + "@rollup/rollup-linux-arm64-musl": "4.63.5", + "@rollup/rollup-linux-loong64-gnu": "4.63.5", + "@rollup/rollup-linux-loong64-musl": "4.63.5", + "@rollup/rollup-linux-ppc64-gnu": "4.63.5", + "@rollup/rollup-linux-ppc64-musl": "4.63.5", + "@rollup/rollup-linux-riscv64-gnu": "4.63.5", + "@rollup/rollup-linux-riscv64-musl": "4.63.5", + "@rollup/rollup-linux-s390x-gnu": "4.63.5", + "@rollup/rollup-linux-x64-gnu": "4.63.5", + "@rollup/rollup-linux-x64-musl": "4.63.5", + "@rollup/rollup-openbsd-x64": "4.63.5", + "@rollup/rollup-openharmony-arm64": "4.63.5", + "@rollup/rollup-win32-arm64-msvc": "4.63.5", + "@rollup/rollup-win32-ia32-msvc": "4.63.5", + "@rollup/rollup-win32-x64-gnu": "4.63.5", + "@rollup/rollup-win32-x64-msvc": "4.63.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + } + } +} diff --git a/afterlife/package.json b/afterlife/package.json new file mode 100644 index 0000000..de82ae1 --- /dev/null +++ b/afterlife/package.json @@ -0,0 +1,22 @@ +{ + "name": "afterlife", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc --noEmit && vite build", + "typecheck": "tsc --noEmit", + "bench": "tsx scripts/bench.ts", + "parity": "tsx scripts/parity.ts", + "parity-music": "tsx scripts/parity-music.ts", + "shot": "node scripts/shot.mjs" + }, + "devDependencies": { + "@types/node": "^22.20.4", + "playwright": "^1.61.1", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vite": "^6.0.0" + } +} diff --git a/afterlife/scripts/bench.ts b/afterlife/scripts/bench.ts new file mode 100644 index 0000000..4f6205b --- /dev/null +++ b/afterlife/scripts/bench.ts @@ -0,0 +1,48 @@ +/** + * bench.ts — how long a frame costs, headless (life_bench.py's job). + * + * npx tsx scripts/bench.ts [frames] [cols] [rows] [--haunted] + * + * Defaults to a 1920×1080 screen at 8px cells (240×67). Times the same + * per-frame work main.ts does, minus painting: step, the display maps, + * dilation, the activity centroid, and a census every 150 generations. + */ + +import { InfiniteLife } from '../src/engine/life'; + +const args = process.argv.slice(2).filter((a) => !a.startsWith('--')); +const frames = Number(args[0] ?? 500); +const cols = Number(args[1] ?? 240); +const rows = Number(args[2] ?? 66); +const life = new InfiniteLife(rows, cols); +if (process.argv.includes('--haunted')) life.toggleHaunted(); + +const t = { step: 0, display: 0, dilation: 0, census: 0 }; +const times: number[] = []; +const events: string[] = []; +for (let f = 0; f < frames; f++) { + const a = performance.now(); + const ev = life.step(); + const b = performance.now(); + life.displayGrid(); + life.displayAge(); + const c = performance.now(); + life.timeDilation(); + life.activityCenterX(); + const d = performance.now(); + if (life.generation % 150 === 0) life.takeCensus(); + const e = performance.now(); + t.step += b - a; + t.display += c - b; + t.dilation += d - c; + t.census += e - d; + times.push(e - a); + if (ev) events.push(`${life.generation}:${ev}`); +} +times.sort((x, y) => x - y); +const total = times.reduce((s, x) => s + x, 0); +console.log(`${cols}×${rows} terminal → world ${life.worldH}×${life.worldW}, ${frames} frames${life.haunted ? ', haunted' : ''}`); +console.log(`per frame: mean ${(total / frames).toFixed(2)} ms · p50 ${times[Math.floor(frames / 2)]?.toFixed(2)} · p95 ${times[Math.floor(frames * 0.95)]?.toFixed(2)} ms`); +for (const [k, v] of Object.entries(t)) console.log(` ${k.padEnd(9)} ${(v / frames).toFixed(2)} ms`); +console.log(`gen ${life.generation} · pop ${life.population()} · ${events.length} events · census ${JSON.stringify(Object.fromEntries(life.lastCensus))}`); +console.log(`last events: ${events.slice(-5).join(' ')}`); diff --git a/afterlife/scripts/parity-music.ts b/afterlife/scripts/parity-music.ts new file mode 100644 index 0000000..649a78f --- /dev/null +++ b/afterlife/scripts/parity-music.ts @@ -0,0 +1,111 @@ +/** + * parity-music.ts — does the port's music engine play the same evening? + * See scripts/parity_music.py, which records the original: the stereo mix + * buffer by buffer, then a longer evening as its PyAudio callback played it + * (volume, mute, soft clip), which the port answers with renderBlock(). + */ + +import { readFileSync } from 'node:fs'; +import { BUFFER_SIZE, LifeMusicEngine, SAMPLE_RATE, type SimulationSnapshot } from '../src/music/engine'; + +type Buffers = [number[], number[]][]; + +const file = process.argv[2]; +if (!file) { + console.error('usage: tsx scripts/parity-music.ts music.json'); + process.exit(2); +} +const want = JSON.parse(readFileSync(file, 'utf8')) as { stereo?: Buffers; callback?: Buffers }; +if (!Array.isArray(want.stereo) || !Array.isArray(want.callback)) { + console.error(`${file}: not a recording from scripts/parity_music.py (rerun it; the format has a callback evening now)`); + process.exit(2); +} + +const pool = new Float32Array(SAMPLE_RATE); +for (let i = 0; i < pool.length; i++) { + // numpy: (sin(i·12.9898)·43758.5453 % 1.0)·2 − 1, with python's floored % + const v = Math.sin(i * 12.9898) * 43758.5453; + pool[i] = (((v % 1) + 1) % 1) * 2 - 1; +} + +const moods = ['', 'booming', 'booming', 'cycle', 'cycle', 'injection', 'declining', 'sparse', 'dense', 'stagnant', 'haunted']; +const epochs = ['genesis', 'genesis', 'primordial', 'emergence', 'expansion', 'deep time', 'eternity']; + +function snapshot(b: number): SimulationSnapshot { + const rows = 60; + return { + generation: b * 4, + population: 400 + 40 * b, + pop_floor: 300, + density: 0.01 + 0.002 * b, + spread: (b * 13) % 250, + cycle_period: moods[b % moods.length] === 'cycle' ? 3 : 0, + mood: moods[Math.floor(b / 3) % moods.length] ?? '', + epoch: epochs[Math.floor(b / 13) % epochs.length] ?? 'genesis', + pop_delta: [0, 5, 35, -60, 120, 0, -25][b % 7] ?? 0, + playhead_column: Array.from({ length: rows }, (_, r) => (r * 7 + b * 3) % 11 < 2), + playhead_position: (b % 30) / 30, + viewport_rows: rows, + event: b === 12 || b === 40 || b === 480 ? 'inject:heavy(cycle=3)' : b === 20 ? 'inject:edge' : '', + activity_x: 0.5 + 0.4 * Math.sin(b * 0.3), + }; +} + +function newEngine(): LifeMusicEngine { + const eng = new LifeMusicEngine(); + (eng as unknown as { noisePool: Float32Array }).noisePool = pool; + return eng; +} + +/** Compare every rendered sample with the recording; true if they agree. */ +function compare(label: string, recorded: Buffers, render: (b: number) => [Float32Array, Float32Array]): boolean { + let worst = 0; + let worstAt = ''; + let rms = 0; + let count = 0; + for (let b = 0; b < recorded.length; b++) { + const [l, r] = render(b); + const [wl, wr] = recorded[b] ?? [[], []]; + for (let i = 0; i < BUFFER_SIZE; i++) { + for (const [got, exp, ch] of [[l[i] ?? 0, wl[i] ?? 0, 'L'], [r[i] ?? 0, wr[i] ?? 0, 'R']] as const) { + const d = Math.abs(got - exp); + rms += exp * exp; + count++; + if (d > worst) { + worst = d; + worstAt = `buffer ${b} sample ${i} ${ch}: ${got} vs ${exp}`; + } + } + } + } + rms = Math.sqrt(rms / Math.max(1, count)); + console.log(`${label}: ${recorded.length} stereo buffers · signal rms ${rms.toFixed(4)} · worst |Δ| ${worst.toExponential(2)} (${worstAt})`); + return worst <= 1e-4; +} + +// The stereo mix +let eng = newEngine(); +const mixOk = compare('mix', want.stereo, (b) => { + if (b === 30 || b === 70) eng.cycleStyle(); + eng.update(snapshot(b)); + return eng.renderStereo(BUFFER_SIZE); +}); + +// The callback evening, through renderBlock +eng = newEngine(); +const left = new Float32Array(BUFFER_SIZE); +const right = new Float32Array(BUFFER_SIZE); +const blockOk = compare('callback', want.callback, (b) => { + if (b === 30 || b === 70 || b === 300 || b === 301) eng.cycleStyle(); + eng.masterVolume = b < 400 ? 0.7 : 1.0; + eng.muted = b >= 350 && b < 360; + eng.update(snapshot(b)); + eng.renderBlock(left, right, BUFFER_SIZE); + return [left, right]; +}); + +if (!mixOk || !blockOk) { + console.log('the evening sounds different ✗'); + process.exit(1); +} +console.log('the same music ✓'); diff --git a/afterlife/scripts/parity.py b/afterlife/scripts/parity.py new file mode 100644 index 0000000..0ab9b54 --- /dev/null +++ b/afterlife/scripts/parity.py @@ -0,0 +1,345 @@ +""" +parity.py — run the original afterlife, and write down what it did. + +The web port claims to be the same universe. This drives life.py's own +InfiniteLife and records everything the port has to reproduce: every step's +population, telemetry, camera, zoom, mood and tempo, a checksum of every +cell's age, the smoothed ages and activity, the display maps every seventh +step, the census every 150, then the final ages, the display maps at every +zoom, the sparkline, the epoch and auto-focus. scripts/parity.ts replays each +case through the port and compares. + +There are two kinds of case. + + still The dice are taken out (injections become no-ops), which leaves the + physics, the camera, the infinity engine's telemetry and every + read-out deterministic. The genesis is recorded and replayed. + Many seeds, tiny and odd terminal sizes, travellers crossing the + torus seams, haunted mode, pan/zoom/toggle/focus/home, and resizes + (a new world adopting the old one). + + live The dice stay in. life.py's `random` and `np.random.random` are + replaced by one shared stream (mulberry32, reimplemented bit for + bit in parity.ts, so both sides draw the same numbers in the same + order), and randint/choice/shuffle map a draw to an integer the + way the port does. Genesis, every injection, every dramaturge + shot, and the draw count after each step must all agree. + + One substitution: _find_quiet_spot picks among the darkest + quintile via np.argpartition, whose order among equal scores is + numpy's implementation detail (introselect, or SIMD on AVX-512 + builds) and not something a port can promise. The live cases give + it a stable argsort instead, which is what the port does. The + quintile itself is unchanged; only the order of ties is pinned. + + cd /path/to/afterlife + uv run --with scipy python /path/to/scripts/parity.py . > /tmp/parity.json + npx tsx scripts/parity.ts /tmp/parity.json + +Needs numpy and scipy. life.py quietly turns the census off without scipy +(and auto-focus loses its smoothing), so this refuses to run without it. +""" + +import json +import random +import sys + +import numpy as np + +try: + import scipy.ndimage # noqa: F401 +except ImportError: + sys.exit( + "parity.py needs scipy: life.py silently disables its census without it,\n" + "and the parity would be checking the wrong universe.\n" + " uv run --with scipy python scripts/parity.py /path/to/afterlife" + ) + +sys.path.insert(0, sys.argv[1] if len(sys.argv) > 1 else ".") +import life # noqa: E402 + +if getattr(life, "_nd_label", None) is None or getattr(life, "_uniform_filter", None) is None: + sys.exit("parity.py: life.py imported without scipy.ndimage; refusing to record a census-less universe") + + +# ── the shared dice ────────────────────────────────────────────────────── +M32 = 0xFFFFFFFF + + +class Stream: + """mulberry32, as parity.ts has it: one uniform draw in [0, 1) per call.""" + + def __init__(self, seed: int) -> None: + self.a = seed & M32 + self.draws = 0 + + def __call__(self) -> float: + self.draws += 1 + self.a = (self.a + 0x6D2B79F5) & M32 + a = self.a + t = ((a ^ (a >> 15)) * (1 | a)) & M32 + t = ((t + (((t ^ (t >> 7)) * (61 | t)) & M32)) & M32) ^ t + return ((t ^ (t >> 14)) & M32) / 4294967296 + + +STREAM = Stream(0) + + +class SharedRandom: + """life.py's `random`, drawing from STREAM the way the port's helpers do.""" + + def random(self) -> float: + return STREAM() + + def randint(self, a: int, b: int) -> int: + return a + int(np.floor(STREAM() * (b - a + 1))) + + def choice(self, xs): + xs = list(xs) + return xs[int(np.floor(STREAM() * len(xs)))] + + def shuffle(self, xs) -> None: + for i in range(len(xs) - 1, 0, -1): + j = int(np.floor(STREAM() * (i + 1))) + xs[i], xs[j] = xs[j], xs[i] + + def seed(self, *_a) -> None: + pass + + +def shared_np_random(shape): + h, w = shape + return np.array([STREAM() for _ in range(h * w)], dtype=np.float64).reshape(h, w) + + +def stable_argpartition(a, kth, **_kw): + # life.py's only np.argpartition call is _find_quiet_spot's darkest + # quintile; [:k] of a stable argsort is that quintile, ties in index order + return np.argsort(a, kind="stable") + + +REAL = {"random": life.random, "np_random": life.np.random.random, "argpartition": life.np.argpartition} + + +def dice(live: bool, seed: int) -> None: + if live: + global STREAM + STREAM = Stream(seed) + life.random = SharedRandom() + life.np.random.random = shared_np_random + life.np.argpartition = stable_argpartition + else: + life.random = REAL["random"] + life.np.random.random = REAL["np_random"] + life.np.argpartition = REAL["argpartition"] + random.seed(seed) + np.random.seed(seed) + + +# ── recording ──────────────────────────────────────────────────────────── +def sparse(a): + idx = np.flatnonzero(a) + return [[int(i), int(a.flat[i])] for i in idx] + + +def cks(a) -> int: + """sum of value × ((index mod 9973) + 1): a position-sensitive fingerprint""" + flat = a.astype(np.int64).ravel() + w = (np.arange(flat.size, dtype=np.int64) % 9973) + 1 + return int((flat * w).sum()) + + +def still(lf) -> None: + lf._inject = lambda *a, **k: None + lf._inject_from_edge = lambda *a, **k: None + lf._inject_garden = lambda *a, **k: None + lf._inject_provoke = lambda *a, **k: "" + lf._inject_collide = lambda *a, **k: False + + +def run(name, rows, cols, steps, seed, live=False, extra=(), actions=None, clear=False): + actions = actions or {} + dice(live, seed) + lf = life.InfiniteLife(rows, cols) + if not live: + still(lf) + if clear: + lf.grid[:] = 0 + lf.age[:] = 0 + for pat, y, x, rot in extra: + lf._place(pat, y, x, rotation=rot) + if clear or extra: + lf.age_smooth = lf.age.astype(np.float32) + out = { + "name": name, "mode": "live" if live else "still", "seed": seed, + "rows": rows, "cols": cols, "cam": [lf.cam_y, lf.cam_x], + "actions": {str(k): v for k, v in actions.items()}, + "steps": [], + } + if live: + out["rng_init"] = STREAM.draws + out["initial_cks"] = cks(lf.age) + else: + out["initial"] = sparse(lf.age) + for s in range(steps): + acted = [] + for act in actions.get(s, []): + op = act[0] + if op == "haunt": + lf.toggle_haunted() + elif op == "pan": + lf.pan(act[1], act[2]) + elif op == "zin": + lf.zoom_in() + elif op == "zout": + lf.zoom_out() + elif op == "toggle": + lf.toggle_cell(act[1], act[2]) + elif op == "focus": + lf.auto_focus() + elif op == "home": + lf.home() + elif op == "resize": + # KEY_RESIZE: a new world for the new terminal adopts the old + old = lf + lf = life.InfiniteLife(act[1], act[2]) + if not live: + still(lf) + lf.haunted = old.haunted + lf.adopt(old.grid, old.age, old.generation, old.total_injections) + # the dramaturge on cue (live cases: these roll dice) + elif op == "provoke": + acted.append(str(lf._inject_provoke())) + elif op == "collide": + acted.append("1" if lf._inject_collide() else "0") + elif op == "garden": + lf._inject_garden() + elif op.startswith("inject:"): + lf._inject(op.split(":", 1)[1]) + else: + raise ValueError(f"unknown action {op!r}") + event = lf.step() + dil = lf.time_dilation() + rec = { + "event": event, "pop": lf.population(), "floor": lf.pop_floor, + "spread": lf.spread, "cycle": lf.cycle_period, + "cam": [lf.cam_y, lf.cam_x], "zoom": lf.zoom_level, + "mood": lf._detect_mood(), "dilation": dil, + "ax": lf.activity_center_x(), + "ack": cks(lf.age), + "sm": float(lf.age_smooth.astype(np.float64).sum()), + "act": float(lf.activity.astype(np.float64).sum()), + } + if s % 7 == 0: + rec["dg"] = cks(lf.display_grid()) + rec["da"] = cks(lf.display_age()) + rec["dshape"] = list(lf.display_age().shape) + if s % 150 == 149: + lf.take_census() + rec["census"] = dict(sorted(lf.last_census.items())) + rec["sites"] = [list(x) for x in lf.last_census_sites] # order kept + if live: + rec["rng"] = STREAM.draws + if acted: + rec["acted"] = acted + out["steps"].append(rec) + out["final_age"] = sparse(lf.age) + out["sparkline"] = lf.sparkline() + out["epoch"] = lf.epoch() + disp = {} + for z in range(life.MIN_ZOOM, life.MAX_ZOOM + 1): + lf.zoom_level = z + lf._disp_grid_cache = None + lf._disp_age_cache = None + g = lf.display_grid() + a = lf.display_age() + disp[str(z)] = {"g_shape": list(g.shape), "a_shape": list(a.shape), "g": cks(g), "a": cks(a)} + out["display"] = disp + lf.auto_focus() + out["focus"] = {"zoom": lf.zoom_level, "cam": [lf.cam_y, lf.cam_x]} + print(f" {name}: {cols}x{rows}, {steps} steps", file=sys.stderr) + return out + + +def travellers_on_edges(H, W): + """gliders and a spaceship straddling, or heading across, every world seam""" + return [ + ("glider", 1, W // 2, 2), ("glider", H - 3, W // 3, 0), + ("glider", H // 2, 1, 1), ("glider", H // 3, W - 3, 3), + ("lwss", 0, W // 4, 0), ("lwss", H // 4, W - 2, 1), + ("glider", 0, 0, 2), ("glider", H - 2, W - 2, 0), + ] + + +def focus_probes(name, rows, cols, worlds, seed): + """auto_focus on many small worlds of stamped clumps. + + Each clump is stamped several times, so 3×3 block neighbourhoods tie + exactly in whole cells and scipy's uniform_filter breaks the ties by its + own rounding; the 10th/90th percentiles of the core size the zoom. The + main cases call auto_focus a handful of times; this calls it hundreds. + """ + dice(False, seed) + rng = np.random.default_rng(seed) + lf = life.InfiniteLife(rows, cols) + H, W = lf.world_h, lf.world_w + home = (lf.cam_y, lf.cam_x) + out = {"name": name, "mode": "focus", "seed": seed, "rows": rows, "cols": cols, "worlds": []} + for _ in range(worlds): + lf.grid[:] = 0 + lf.age[:] = 0 + for _t in range(int(rng.integers(1, 4))): + ch, cw = (int(v) for v in rng.integers(2, 24, 2)) + clump = (rng.random((ch, cw)) < rng.uniform(0.1, 0.6)) * rng.integers(1, 30, (ch, cw)) + for _s in range(int(rng.integers(1, 5))): + y, x = int(rng.integers(0, H - ch)), int(rng.integers(0, W - cw)) + lf.age[y:y + ch, x:x + cw] = clump + lf.grid[:] = (lf.age > 0).astype(lf.grid.dtype) + lf.zoom_level = 0 + lf.cam_y, lf.cam_x = home + lf.auto_focus() + out["worlds"].append({"age": sparse(lf.age), "focus": {"zoom": lf.zoom_level, "cam": [lf.cam_y, lf.cam_x]}}) + print(f" {name}: {worlds} worlds", file=sys.stderr) + return out + + +EVERYTHING = { + 40: [["haunt"]], 120: [["zout"], ["zout"]], 160: [["haunt"]], + 200: [["zin"], ["zin"], ["zin"]], 230: [["pan", 5, -9]], 260: [["zin"]], + 300: [["toggle", 3, 4], ["toggle", 10, 20]], 330: [["focus"]], 400: [["home"]], + 450: [["haunt"]], 470: [["zout"], ["zout"], ["zout"]], 520: [["haunt"]], + 560: [["resize", 22, 70]], 640: [["resize", 35, 130]], 700: [["zout"]], +} + +cases = [ + # still: the dice out, the genesis recorded + run("seed 1", 30, 100, 600, 1), + run("pan + zoom out", 40, 150, 400, 7, actions={120: [["pan", 7, -12], ["zout"]]}), + run("haunted", 25, 90, 160, 3, actions={60: [["haunt"]]}), + *[run(f"seed {s}", 30, 100, 900, s) for s in (11, 12, 13, 14, 15)], + run("tiny terminal", 10, 40, 600, 21), # the 400×800 world floor + run("absurdly tiny", 3, 7, 300, 22), + run("odd sizes", 41, 161, 600, 23), + run("1080p", 66, 240, 400, 24), + run("seams, empty world", 20, 80, 800, 31, extra=travellers_on_edges(400, 800), clear=True), + run("seams, in the soup", 20, 80, 800, 32, extra=travellers_on_edges(400, 800)), + run("every key + resizes", 30, 100, 800, 41, actions=EVERYTHING), + run("small, zoomed out, haunted", 12, 30, 500, 42, + actions={10: [["zout"], ["zout"]], 50: [["haunt"]], 300: [["haunt"]]}), + # auto-focus, many times over + focus_probes("auto-focus probes", 10, 40, 400, 51), + focus_probes("auto-focus probes, 1080p", 66, 240, 150, 52), + # live: the dice in, one shared stream + # (the census runs after steps 150, 300, …; provoke aims at its sites) + run("live", 30, 100, 1500, 101, live=True, + actions={100: [["haunt"]], 151: [["provoke"], ["collide"]], 200: [["inject:massive"]], + 250: [["garden"], ["inject:medium"]], 301: [["provoke"], ["provoke"], ["collide"]], + 400: [["haunt"]], 451: [["collide"], ["provoke"]], 600: [["zout"]], 700: [["focus"]], + 800: [["pan", -6, 11], ["toggle", 5, 5]], 900: [["home"]], + 1000: [["resize", 22, 70]], 1051: [["provoke"], ["collide"]], + 1200: [["resize", 41, 161]], 1351: [["collide"], ["inject:heavy"]]}), + run("live, tiny terminal", 10, 40, 5100, 102, live=True), # a garden at 5000 + run("live, odd sizes", 41, 161, 1000, 103, live=True), + run("live, 1080p", 66, 240, 600, 104, live=True), +] +json.dump(cases, sys.stdout, default=lambda o: o.item() if hasattr(o, "item") else str(o)) diff --git a/afterlife/scripts/parity.ts b/afterlife/scripts/parity.ts new file mode 100644 index 0000000..7a1fed4 --- /dev/null +++ b/afterlife/scripts/parity.ts @@ -0,0 +1,325 @@ +/** + * parity.ts — is the port the same universe? + * + * Replays every case recorded by scripts/parity.py (life.py's own + * InfiniteLife) through the port and compares each step's read-outs, a + * checksum of every age, the smoothed ages and activity, the display maps, + * the census and its sites, then the final ages, the display maps at every + * zoom, the sparkline, the epoch and auto-focus. + * + * "still" cases load the recorded genesis and take the dice out, as + * parity.py did. "live" cases keep the dice: Math.random becomes the same + * mulberry32 stream parity.py fed life.py, so genesis, injections and the + * dramaturge's shots must draw the same numbers in the same order (the draw + * count is checked after every step). + * + * "focus" cases load hundreds of small worlds of repeated clumps and compare + * where auto-focus lands (the hotspot's exact-tie breaking, the percentiles). + * + * Exits 1 if anything differs, after listing the first mismatches. + * + * uv run --with scipy python scripts/parity.py /path/to/afterlife > /tmp/parity.json + * npx tsx scripts/parity.ts /tmp/parity.json + */ + +import { readFileSync } from 'node:fs'; +import { InfiniteLife } from '../src/engine/life'; + +type Sparse = [number, number][]; +type Action = [string, ...number[]]; +interface Step { + event: string; + pop: number; + floor: number; + spread: number; + cycle: number; + cam: [number, number]; + zoom: number; + mood: string; + dilation: number; + ax: number; + ack: number; + sm: number; + act: number; + dg?: number; + da?: number; + dshape?: [number, number]; + census?: Record; + sites?: [string, number, number][]; + rng?: number; + acted?: string[]; +} +/** the port's private injectors, called on cue by the live cases */ +interface Dramaturge { + injectProvoke(): string; + injectCollide(): boolean; + injectGarden(): void; + inject(intensity: string): void; +} +interface FocusCase { + name: string; + mode: 'focus'; + seed: number; + rows: number; + cols: number; + worlds: { age: Sparse; focus: { zoom: number; cam: [number, number] } }[]; +} +interface Case { + name: string; + mode: 'still' | 'live'; + seed: number; + rows: number; + cols: number; + cam: [number, number]; + actions: Record; + initial?: Sparse; + rng_init?: number; + initial_cks?: number; + steps: Step[]; + final_age: Sparse; + sparkline: string; + epoch: string; + display: Record; + focus: { zoom: number; cam: [number, number] }; +} + +const file = process.argv[2]; +if (!file) { + console.error('usage: tsx scripts/parity.ts parity.json'); + process.exit(2); +} +let cases: (Case | FocusCase)[]; +try { + const doc = JSON.parse(readFileSync(file, 'utf8')) as unknown; + if (!Array.isArray(doc) || !doc.every((c) => c && typeof c === 'object' && ('steps' in c || 'worlds' in c))) { + throw new Error('not a parity.py recording (re-record it: the format grew "mode", "ack" and live cases)'); + } + cases = doc as (Case | FocusCase)[]; +} catch (e) { + console.error(`parity.ts: can't read ${file}: ${(e as Error).message}`); + process.exit(2); +} + +// ── the shared dice: mulberry32, as parity.py has it ────────────────── +let rngState = 0; +let draws = 0; +const nativeRandom = Math.random; +function mulberry32(): number { + draws++; + rngState = (rngState + 0x6d2b79f5) | 0; + let t = Math.imul(rngState ^ (rngState >>> 15), 1 | rngState); + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; +} +function dice(live: boolean, seed: number): void { + rngState = seed | 0; + draws = 0; + Math.random = live ? mulberry32 : nativeRandom; +} + +let failures = 0; +const fail = (what: string): void => { + failures++; + if (failures <= 60) console.log(` ✗ ${what}`); +}; +const close = (a: number, b: number, tol: number): boolean => Math.abs(a - b) <= tol * Math.max(1, Math.abs(b)); +/** parity.py's cks: sum of value × ((index mod 9973) + 1) */ +function cks(a: ArrayLike): number { + let s = 0; + for (let i = 0; i < a.length; i++) if (a[i]) s += (a[i] as number) * ((i % 9973) + 1); + return s; +} +function sparse(a: ArrayLike): Sparse { + const out: Sparse = []; + for (let i = 0; i < a.length; i++) if (a[i]) out.push([i, a[i] ?? 0]); + return out; +} +function sameSparse(a: Sparse, b: Sparse): string | null { + if (a.length !== b.length) return `${a.length} vs ${b.length} nonzero`; + for (let i = 0; i < a.length; i++) { + if (a[i]?.[0] !== b[i]?.[0] || a[i]?.[1] !== b[i]?.[1]) return `first difference at ${JSON.stringify(a[i])} vs ${JSON.stringify(b[i])}`; + } + return null; +} +/** the dice, taken out the same way parity.py takes them out */ +function still(life: InfiniteLife): void { + const l = life as unknown as Record; + l.inject = () => undefined; + l.injectFromEdge = () => undefined; + l.injectGarden = () => undefined; + l.injectProvoke = () => ''; + l.injectCollide = () => false; +} +function resetDisplayCaches(life: InfiniteLife): void { + const l = life as unknown as { dispGridCache: unknown; dispAgeCache: unknown }; + l.dispGridCache = null; + l.dispAgeCache = null; +} + +const summary: string[] = []; + +/** auto-focus on each recorded world, from the home camera at zoom 0 */ +function focusProbes(c: FocusCase): void { + dice(false, c.seed); + const life = new InfiniteLife(c.rows, c.cols, false); + const home = [life.camY, life.camX] as const; + let wrong = 0; + c.worlds.forEach((w, i) => { + life.age.fill(0); + life.grid.fill(0); + for (const [j, a] of w.age) { + life.age[j] = a; + life.grid[j] = a > 0 ? 1 : 0; + } + life.zoomLevel = 0; + [life.camY, life.camX] = home; + life.autoFocus(); + if (life.zoomLevel !== w.focus.zoom || life.camY !== w.focus.cam[0] || life.camX !== w.focus.cam[1]) { + wrong++; + fail(`world ${i + 1}: zoom ${life.zoomLevel} cam ${life.camY},${life.camX} vs ${JSON.stringify(w.focus)}`); + } + }); + console.log(` ${c.worlds.length - wrong}/${c.worlds.length} worlds focus alike`); +} + +cases.forEach((c, k) => { + if (c.mode === 'focus') { + const before = failures; + console.log(`case ${k + 1}, ${c.name}: ${c.cols}×${c.rows} terminal, ${c.worlds.length} worlds`); + focusProbes(c); + summary.push(`${failures === before ? '✓' : '✗'} ${c.name}`); + return; + } + const live = c.mode === 'live'; + const before = failures; + console.log(`case ${k + 1}, ${c.name} (${c.mode}): ${c.cols}×${c.rows} terminal, ${c.steps.length} steps`); + dice(live, c.seed); + let life: InfiniteLife; + if (live) { + life = new InfiniteLife(c.rows, c.cols, true); + if (draws !== c.rng_init) fail(`genesis drew ${draws} numbers vs ${c.rng_init}`); + if (cks(life.age) !== c.initial_cks) fail('genesis: a different soup'); + } else { + life = new InfiniteLife(c.rows, c.cols, false); + for (const [i, a] of c.initial ?? []) { + life.age[i] = a; + life.grid[i] = a > 0 ? 1 : 0; + life.ageSmooth[i] = a; + } + still(life); + } + if (life.camY !== c.cam[0] || life.camX !== c.cam[1]) fail(`genesis cam ${life.camY},${life.camX} vs ${c.cam}`); + // the camera is the recorded one either way (a still case's genesis is loaded, not grown) + [life.camY, life.camX] = c.cam; + + let firstDivergence = -1; + c.steps.forEach((want, s) => { + const stepBefore = failures; + const acted: string[] = []; + // (read through `life` each time: a resize earlier in the step replaces it) + const dramaturge = (): Dramaturge => life as unknown as Dramaturge; + for (const act of c.actions[String(s)] ?? []) { + const [op, a = 0, b = 0] = act; + if (op === 'haunt') life.toggleHaunted(); + else if (op === 'pan') life.pan(a, b); + else if (op === 'zin') life.zoomIn(); + else if (op === 'zout') life.zoomOut(); + else if (op === 'toggle') life.toggleCell(a, b); + else if (op === 'focus') life.autoFocus(); + else if (op === 'home') life.home(); + else if (op === 'resize') { + // KEY_RESIZE: life.py builds (and seeds) a new world, then adopts the old + const old = life; + life = new InfiniteLife(a, b, live); + if (!live) still(life); + life.haunted = old.haunted; + life.adopt(old.snapshot()); + } + // the dramaturge on cue (live cases: these roll dice) + else if (op === 'provoke') acted.push(dramaturge().injectProvoke()); + else if (op === 'collide') acted.push(dramaturge().injectCollide() ? '1' : '0'); + else if (op === 'garden') dramaturge().injectGarden(); + else if (op.startsWith('inject:')) dramaturge().inject(op.slice('inject:'.length)); + else fail(`unknown action ${op}`); + } + const event = life.step(); + const dil = life.timeDilation(); + const got = { + event, pop: life.population(), floor: life.popFloor, spread: life.spread, cycle: life.cyclePeriod, + zoom: life.zoomLevel, mood: life.detectMood(), ack: cks(life.age), + }; + const at = `step ${s + 1}`; + if (live && draws !== want.rng) fail(`${at} rng draws ${draws} vs ${want.rng}`); + if (JSON.stringify(acted) !== JSON.stringify(want.acted ?? [])) fail(`${at} dramaturge: ${JSON.stringify(acted)} vs ${JSON.stringify(want.acted ?? [])}`); + for (const key of ['event', 'pop', 'floor', 'spread', 'cycle', 'zoom', 'mood', 'ack'] as const) { + if (got[key] !== want[key]) fail(`${at} ${key}: ${JSON.stringify(got[key])} vs ${JSON.stringify(want[key])}`); + } + if (life.camY !== want.cam[0] || life.camX !== want.cam[1]) fail(`${at} cam: ${life.camY},${life.camX} vs ${want.cam}`); + if (!close(dil, want.dilation, 1e-6)) fail(`${at} dilation: ${dil} vs ${want.dilation}`); + if (!close(life.activityCenterX(), want.ax, 1e-4)) fail(`${at} activity_x: ${life.activityCenterX()} vs ${want.ax}`); + let sm = 0; + for (const v of life.ageSmooth) sm += v; + let act = 0; + for (const v of life.activity) act += v; + if (!close(sm, want.sm, 1e-9)) fail(`${at} age_smooth sum ${sm} vs ${want.sm}`); + if (!close(act, want.act, 1e-9)) fail(`${at} activity sum ${act} vs ${want.act}`); + if (want.dg !== undefined) { + const g = life.displayGrid(); + const a = life.displayAge(); + if (cks(g.data) !== want.dg) fail(`${at} display grid (zoom ${life.zoomLevel})`); + if (cks(a.data) !== want.da) fail(`${at} display age (zoom ${life.zoomLevel})`); + if (a.h !== want.dshape?.[0] || a.w !== want.dshape?.[1]) fail(`${at} display shape ${a.h}×${a.w} vs ${want.dshape}`); + } + if (want.census) { + life.takeCensus(); + const gotCensus = Object.fromEntries([...life.lastCensus.entries()].sort()); + if (JSON.stringify(gotCensus) !== JSON.stringify(want.census)) fail(`${at} census: ${JSON.stringify(gotCensus)} vs ${JSON.stringify(want.census)}`); + const gs = JSON.stringify(life.lastCensusSites); + const ws = JSON.stringify(want.sites ?? []); + if (gs !== ws) { + const set = (xs: readonly (readonly unknown[])[]): string => JSON.stringify(xs.map((x) => [...x]).sort()); + const sameSet = set(life.lastCensusSites) === set(want.sites ?? []); + fail(`${at} census sites ${sameSet ? 'in a different order' : 'differ'}`); + } + } + if (failures > stepBefore && firstDivergence < 0) firstDivergence = s + 1; + }); + + const d = sameSparse(sparse(life.age), c.final_age); + if (d) fail(`final ages: ${d}`); + if (life.sparkline() !== c.sparkline) fail(`sparkline ${life.sparkline()} vs ${c.sparkline}`); + if (life.epoch() !== c.epoch) fail(`epoch ${life.epoch()} vs ${c.epoch}`); + for (const [z, want] of Object.entries(c.display)) { + life.zoomLevel = Number(z); + resetDisplayCaches(life); + const g = life.displayGrid(); + const a = life.displayAge(); + if (g.h !== want.g_shape[0] || g.w !== want.g_shape[1]) fail(`zoom ${z} grid shape ${g.h}×${g.w} vs ${want.g_shape}`); + if (a.h !== want.a_shape[0] || a.w !== want.a_shape[1]) fail(`zoom ${z} age shape ${a.h}×${a.w} vs ${want.a_shape}`); + if (cks(g.data) !== want.g) fail(`zoom ${z} display grid`); + if (cks(a.data) !== want.a) fail(`zoom ${z} display age`); + } + life.autoFocus(); + if (life.zoomLevel !== c.focus.zoom || life.camY !== c.focus.cam[0] || life.camX !== c.focus.cam[1]) { + fail(`auto_focus: zoom ${life.zoomLevel} cam ${life.camY},${life.camX} vs ${JSON.stringify(c.focus)}`); + } + + const pops = c.steps.map((s) => s.pop); + const events = c.steps.filter((s) => s.event).length; + const cycles = c.steps.filter((s) => s.cycle).length; + const censuses = c.steps.filter((s) => s.census).length; + const drew = live ? `, ${draws} draws` : ''; + console.log( + ` pop ${pops[0]} → ${pops[pops.length - 1]}, ${events} events, ${cycles} steps in a detected cycle, ${censuses} censuses${drew}` + + (firstDivergence > 0 ? ` — first divergence at step ${firstDivergence}` : ''), + ); + summary.push(`${failures === before ? '✓' : '✗'} ${c.name}`); +}); + +Math.random = nativeRandom; +console.log(`\n${summary.join(' ')}`); +if (failures) { + console.log(`\n${failures} mismatch${failures === 1 ? '' : 'es'}`); + process.exit(1); +} +console.log('\nthe same universe ✓'); diff --git a/afterlife/scripts/parity_music.py b/afterlife/scripts/parity_music.py new file mode 100644 index 0000000..51d6568 --- /dev/null +++ b/afterlife/scripts/parity_music.py @@ -0,0 +1,101 @@ +""" +parity_music.py — record life_music.py rendering a scripted evening. + +A sequence of snapshots (a boom, a cycle broken by an injection — which +scores the cadence — a style switch mid-buffer-stream, population spikes for +the noise bursts, epochs changing the root) is fed to the original +LifeMusicEngine and every stereo buffer it renders is written down. +scripts/parity-music.ts renders the same evening through the port. + +Then a longer evening goes through the original's PyAudio callback itself +(master volume, mute, tanh soft clip): 620 buffers, with a third cadence and +two back-to-back style switches that land mid-crossfade. The port answers it +with renderBlock(), what the worklet plays. + +The noise pool is the one random thing in the engine; both sides replace it +with the same deterministic sequence. + + python scripts/parity_music.py /path/to/afterlife > /tmp/music.json + npx tsx scripts/parity-music.ts /tmp/music.json +""" + +import json +import math +import sys +from types import SimpleNamespace + +import numpy as np + +sys.path.insert(0, sys.argv[1] if len(sys.argv) > 1 else ".") +import life_music as lm # noqa: E402 + +N = lm.BUFFER_SIZE +STEREO_BUFFERS = 90 +CALLBACK_BUFFERS = 620 + + +def pool(): + i = np.arange(lm.SAMPLE_RATE, dtype=np.float64) + return (np.sin(i * 12.9898) * 43758.5453 % 1.0 * 2 - 1).astype(np.float32) + + +def evening(nb): + """(buffer index → snapshot kwargs)""" + moods = ["", "booming", "booming", "cycle", "cycle", "injection", "declining", "sparse", "dense", "stagnant", "haunted"] + epochs = ["genesis", "genesis", "primordial", "emergence", "expansion", "deep time", "eternity"] + for b in range(nb): + rows = 60 + col = [((r * 7 + b * 3) % 11) < 2 for r in range(rows)] + yield b, dict( + generation=b * 4, + population=400 + 40 * b, + pop_floor=300, + density=0.01 + 0.002 * b, + spread=(b * 13) % 250, + cycle_period=3 if moods[b % len(moods)] == "cycle" else 0, + mood=moods[(b // 3) % len(moods)], + epoch=epochs[(b // 13) % len(epochs)], + pop_delta=[0, 5, 35, -60, 120, 0, -25][b % 7], + playhead_column=tuple(col), + playhead_position=(b % 30) / 30.0, + viewport_rows=rows, + event="inject:heavy(cycle=3)" if b in (12, 40, 480) else ("inject:edge" if b == 20 else ""), + activity_x=0.5 + 0.4 * math.sin(b * 0.3), + ) + + +def as_lists(left, right): + return [left.astype(float).tolist(), right.astype(float).tolist()] + + +# The stereo mix, as _render_stereo returns it +eng = lm.LifeMusicEngine() +eng._noise_pool = pool() +stereo = [] +for b, kw in evening(STEREO_BUFFERS): + if b == 30: + eng.cycle_style() # → chiptune + if b == 70: + eng.cycle_style() # → ambient again + eng.update(lm.SimulationSnapshot(**kw)) + stereo.append(as_lists(*eng._render_stereo(N))) + +# What the speakers got: the callback, without a stream (or PyAudio) behind it +if lm.pyaudio is None: + lm.pyaudio = SimpleNamespace(paContinue=0, paComplete=1, paOutputUnderflow=2) +eng = lm.LifeMusicEngine() +eng._noise_pool = pool() +eng._running = True +eng._channels = 2 +callback = [] +for b, kw in evening(CALLBACK_BUFFERS): + if b in (30, 70, 300, 301): + eng.cycle_style() # 300 → 301: a switch mid-crossfade + eng._master_volume = 0.7 if b < 400 else 1.0 + eng._muted = 350 <= b < 360 + eng.update(lm.SimulationSnapshot(**kw)) + data, _ = eng._audio_callback(None, N, {}, 0) + samples = np.frombuffer(data, dtype=np.float32) + callback.append(as_lists(samples[0::2], samples[1::2])) + +json.dump({"stereo": stereo, "callback": callback}, sys.stdout) diff --git a/afterlife/scripts/shot.mjs b/afterlife/scripts/shot.mjs new file mode 100644 index 0000000..f23e2cb --- /dev/null +++ b/afterlife/scripts/shot.mjs @@ -0,0 +1,91 @@ +/** + * shot.mjs — headless screenshots, so the terminal can be looked at by eye + * without a human in the loop: the splash, the universe a little later, the + * stats overlay, haunted mode, the keys panel, a phone, and a resume. + * + * npm run dev & # or the real server + * node scripts/shot.mjs [url] + * + * `url` defaults to the dev page; pass http://localhost:8000/afterlife to + * shoot the site's own page. + */ + +import { chromium } from 'playwright'; + +const outdir = process.argv[2] ?? '.'; +const URL = process.argv[3] ?? 'http://localhost:5198/'; + +// CHROMIUM=/path/to/chrome when playwright's own download doesn't match its version +const browser = await chromium.launch({ + executablePath: process.env.CHROMIUM || undefined, + args: ['--autoplay-policy=no-user-gesture-required'], +}); +const errors = []; +const watch = (page) => { + page.on('console', (m) => m.type() === 'error' && errors.push(`[console] ${m.text()}`)); + page.on('pageerror', (e) => errors.push(`[pageerror] ${e.message}`)); +}; +const status = (page) => + page.evaluate(() => { + const a = window.afterlife; + const l = a.life; + return { gen: l.generation, pop: l.population(), zoom: l.zoomLevel, mood: l.detectMood(), haunted: l.haunted, cols: a.term.cols, rows: a.term.rows, ticker: l.ticker.messages.map((m) => m.text) }; + }); +try { + const ctx = await browser.newContext({ viewport: { width: 1600, height: 900 } }); + const page = await ctx.newPage(); + watch(page); + await page.goto(URL, { waitUntil: 'networkidle' }); + await page.waitForTimeout(1200); + await page.screenshot({ path: `${outdir}/a-splash.png` }); + await page.click('#splash'); + await page.waitForTimeout(9000); + await page.screenshot({ path: `${outdir}/b-lived.png` }); + console.log('lived', await status(page)); + + await page.keyboard.press('s'); + await page.waitForTimeout(1500); + await page.screenshot({ path: `${outdir}/c-stats.png` }); + await page.keyboard.press('s'); + + await page.keyboard.press('g'); + await page.waitForTimeout(6000); + await page.screenshot({ path: `${outdir}/d-haunted.png` }); + console.log('haunted', await status(page)); + await page.keyboard.press('g'); + + await page.keyboard.press('z'); + await page.keyboard.press('z'); + await page.waitForTimeout(1500); + await page.screenshot({ path: `${outdir}/e-zoomed-out.png` }); + await page.keyboard.press('h'); + + await page.keyboard.press('?'); + await page.waitForTimeout(300); + await page.screenshot({ path: `${outdir}/f-keys.png` }); + await page.keyboard.press('Escape'); + + // leave and come back: the universe remembers + const gen = (await status(page)).gen; + await page.evaluate(() => window.afterlife.persist()); + await page.reload({ waitUntil: 'networkidle' }); + await page.waitForTimeout(3000); + const back = await status(page); + console.log(`resumed: saved at gen ${gen}, back at gen ${back.gen}; ticker ${JSON.stringify(back.ticker)}`); + await page.screenshot({ path: `${outdir}/g-resumed.png` }); + + const phone = await browser.newContext({ viewport: { width: 390, height: 844 }, deviceScaleFactor: 3, isMobile: true, hasTouch: true }); + const p2 = await phone.newPage(); + watch(p2); + await p2.goto(URL, { waitUntil: 'networkidle' }); + await p2.tap('#splash'); + await p2.waitForTimeout(6000); + await p2.screenshot({ path: `${outdir}/h-phone.png` }); + console.log('phone', await status(p2)); +} finally { + await browser.close(); +} +if (errors.length) { + console.log(errors.join('\n')); + process.exitCode = 1; +} diff --git a/afterlife/src/engine/census.ts b/afterlife/src/engine/census.ts new file mode 100644 index 0000000..e4da2b9 --- /dev/null +++ b/afterlife/src/engine/census.ts @@ -0,0 +1,245 @@ +/** + * census.ts — who lives here. + * + * The simulation periodically identifies the famous citizens living in the + * viewport (spaceships, oscillators, and the common still-life ash) so the + * ticker can greet them by name. Recognition works by matching each small + * isolated cluster against dihedral-canonical signatures of every phase of + * every known pattern — signatures are generated by actually simulating the + * patterns, so the library below stays honest. + * + * life.py leans on scipy.ndimage for this (binary_dilation, label, + * find_objects). Those are small enough to write out, which also means the + * census works everywhere here; in the terminal it needed scipy. + */ + +import { PATTERNS, type Cells } from './constants'; + +/** name → [cells, period]. Spaceships/oscillators cover all their phases. */ +const CENSUS_SEEDS: Readonly> = { + block: [[[0, 0], [0, 1], [1, 0], [1, 1]], 1], + beehive: [[[0, 1], [0, 2], [1, 0], [1, 3], [2, 1], [2, 2]], 1], + loaf: [[[0, 1], [0, 2], [1, 0], [1, 3], [2, 1], [2, 3], [3, 2]], 1], + boat: [[[0, 0], [0, 1], [1, 0], [1, 2], [2, 1]], 1], + tub: [[[0, 1], [1, 0], [1, 2], [2, 1]], 1], + ship: [[[0, 0], [0, 1], [1, 0], [1, 2], [2, 1], [2, 2]], 1], + pond: [[[0, 1], [0, 2], [1, 0], [1, 3], [2, 0], [2, 3], [3, 1], [3, 2]], 1], + blinker: [[[0, 0], [0, 1], [0, 2]], 2], + toad: [[[0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2]], 2], + beacon: [[[0, 0], [0, 1], [1, 0], [2, 3], [3, 2], [3, 3]], 2], + glider: [PATTERNS.glider ?? [], 4], + lwss: [PATTERNS.lwss ?? [], 4], + hwss: [PATTERNS.hwss ?? [], 4], + pulsar: [PATTERNS.pulsar ?? [], 3], + pentadecathlon: [PATTERNS.pentadecathlon ?? [], 15], +}; + +/** Larger clusters than this can't be any known pattern — skip early */ +export const CENSUS_MAX_CELLS = 60; +export const CENSUS_MAX_DIM = 20; + +/** A cropped bitmap: h rows × w cols, row-major, 0/1. */ +interface Bitmap { + h: number; + w: number; + bits: Uint8Array; +} + +/** + * Canonical signature over the 8 dihedral transforms of a cropped bitmap. + * life.py compares packbits byte strings; any canonical choice works as long + * as the library and the lookup share it, and this one is a plain string. + */ +function canonicalSignature(m: Bitmap): string { + let best: string | null = null; + for (let t = 0; t < 8; t++) { + const swap = t >= 4; // transpose first + const h = swap ? m.w : m.h; + const w = swap ? m.h : m.w; + let s = `${h}x${w}:`; + for (let r = 0; r < h; r++) { + for (let c = 0; c < w; c++) { + let rr = t & 1 ? h - 1 - r : r; + let cc = t & 2 ? w - 1 - c : c; + if (swap) [rr, cc] = [cc, rr]; + s += m.bits[rr * m.w + cc] ? '1' : '0'; + } + } + if (best === null || s < best) best = s; + } + return best ?? ''; +} + +let signatures: Map | null = null; + +/** + * Build (once) the signature → name table by simulating each seed pattern + * through all phases of its period on a quiet private grid. + */ +export function censusSignatures(): Map { + if (signatures) return signatures; + const sigs = new Map(); + for (const [name, [cells, period]] of Object.entries(CENSUS_SEEDS)) { + const h = Math.max(...cells.map(([r]) => r)) + 1; + const w = Math.max(...cells.map(([, c]) => c)) + 1; + // Room to breathe: patterns may grow/travel a little mid-phase + const size = Math.max(h, w) + 2 * (period + 4); + let grid = new Uint8Array(size * size); + const oy = Math.floor((size - h) / 2); + const ox = Math.floor((size - w) / 2); + for (const [r, c] of cells) grid[(oy + r) * size + ox + c] = 1; + + for (let p = 0; p < period; p++) { + const crop = cropToBbox(grid, size, size); + if (crop) { + const sig = canonicalSignature(crop); + if (!sigs.has(sig)) sigs.set(sig, name); + } + // One Life step (wrapping, as np.roll does; the grid is roomy enough) + const next = new Uint8Array(size * size); + for (let y = 0; y < size; y++) { + for (let x = 0; x < size; x++) { + let n = 0; + for (let dy = -1; dy <= 1; dy++) { + for (let dx = -1; dx <= 1; dx++) { + if (dy || dx) n += grid[((y + dy + size) % size) * size + ((x + dx + size) % size)] ?? 0; + } + } + const alive = grid[y * size + x] === 1; + next[y * size + x] = (alive && (n === 2 || n === 3)) || (!alive && n === 3) ? 1 : 0; + } + } + grid = next; + } + } + signatures = sigs; + return sigs; +} + +function cropToBbox(mask: Uint8Array, h: number, w: number): Bitmap | null { + let y0 = h; + let y1 = -1; + let x0 = w; + let x1 = -1; + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + if (!mask[y * w + x]) continue; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + if (x < x0) x0 = x; + if (x > x1) x1 = x; + } + } + if (y1 < 0) return null; + const ch = y1 - y0 + 1; + const cw = x1 - x0 + 1; + const bits = new Uint8Array(ch * cw); + for (let y = 0; y < ch; y++) { + for (let x = 0; x < cw; x++) bits[y * cw + x] = mask[(y0 + y) * w + x0 + x] ?? 0; + } + return { h: ch, w: cw, bits }; +} + +export interface CensusResult { + counts: Map; + /** (name, row, col) in viewport coordinates, at most 48 */ + sites: [string, number, number][]; +} + +/** + * Count known patterns in a viewport-sized occupancy map (h × w, row-major). + * + * Small gaps are bridged with dilation before labeling so patterns with + * 8-disconnected parts (the pulsar) group as one cluster; each cluster is + * then matched against the canonical signature table. Returns null when the + * scene is too busy to be worth it (more than 400 clusters), as life.py does. + */ +export function census(occupied: Uint8Array, h: number, w: number): CensusResult | null { + const sigs = censusSignatures(); + + // binary_dilation(iterations=2) with scipy's default cross: a Manhattan + // radius-2 diamond around every occupied cell, clipped at the border. + const merged = new Uint8Array(h * w); + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + if (!occupied[y * w + x]) continue; + for (let dy = -2; dy <= 2; dy++) { + const yy = y + dy; + if (yy < 0 || yy >= h) continue; + const span = 2 - Math.abs(dy); + const xa = Math.max(0, x - span); + const xb = Math.min(w - 1, x + span); + merged.fill(1, yy * w + xa, yy * w + xb + 1); + } + } + } + + // label with 8-connectivity, raster order (scipy numbers labels the same way) + const labels = new Int32Array(h * w); + const boxes: { y0: number; y1: number; x0: number; x1: number }[] = []; + const stack: number[] = []; + for (let start = 0; start < h * w; start++) { + if (!merged[start] || labels[start]) continue; + const id = boxes.length + 1; + if (id > 400) return null; + const box = { y0: h, y1: -1, x0: w, x1: -1 }; + labels[start] = id; + stack.push(start); + while (stack.length) { + const i = stack.pop() ?? 0; + const y = Math.floor(i / w); + const x = i - y * w; + if (y < box.y0) box.y0 = y; + if (y > box.y1) box.y1 = y; + if (x < box.x0) box.x0 = x; + if (x > box.x1) box.x1 = x; + for (let dy = -1; dy <= 1; dy++) { + const yy = y + dy; + if (yy < 0 || yy >= h) continue; + for (let dx = -1; dx <= 1; dx++) { + const xx = x + dx; + if (xx < 0 || xx >= w) continue; + const j = yy * w + xx; + if (merged[j] && !labels[j]) { + labels[j] = id; + stack.push(j); + } + } + } + } + boxes.push(box); + } + if (boxes.length === 0) return null; + + const counts = new Map(); + const sites: [string, number, number][] = []; + boxes.forEach((box, k) => { + const id = k + 1; + const bh = box.y1 - box.y0 + 1; + const bw = box.x1 - box.x0 + 1; + // Dilated bbox is at most 4 wider than the pattern itself + if (bh > CENSUS_MAX_DIM + 4 || bw > CENSUS_MAX_DIM + 4) return; + const cluster = new Uint8Array(bh * bw); + let n = 0; + let sy = 0; + let sx = 0; + for (let y = 0; y < bh; y++) { + for (let x = 0; x < bw; x++) { + const i = (box.y0 + y) * w + box.x0 + x; + if (occupied[i] && labels[i] === id) { + cluster[y * bw + x] = 1; + n++; + sy += y; + sx += x; + } + } + } + if (n < 3 || n > CENSUS_MAX_CELLS) return; + const crop = cropToBbox(cluster, bh, bw); + const name = crop ? sigs.get(canonicalSignature(crop)) : undefined; + if (name === undefined) return; + counts.set(name, (counts.get(name) ?? 0) + 1); + if (sites.length < 48) sites.push([name, box.y0 + Math.floor(sy / n), box.x0 + Math.floor(sx / n)]); + }); + return { counts, sites }; +} diff --git a/afterlife/src/engine/constants.ts b/afterlife/src/engine/constants.ts new file mode 100644 index 0000000..5130dca --- /dev/null +++ b/afterlife/src/engine/constants.ts @@ -0,0 +1,144 @@ +/** + * constants.ts — the physics and the palette, as life.py has them. + * + * Numbers here are afterlife's own and are not tuned for the web: the port's + * promise is that the universe behaves the way it does in a terminal. + */ + +// ── Palette ───────────────────────────────────────────────────────────── +// Birth (cool indigo) → youth (violet) → maturity (claude amber) → ancient (white). +// xterm-256 colour numbers; term/palette.ts turns them into RGB. +export const GRADIENT: readonly number[] = [ + 17, 19, 21, // deep indigo → blue + 57, 93, // blue-violet → violet + 165, 163, // magenta + 204, 209, // salmon → orange + 214, 220, 231, // amber → gold → white +]; + +/** Ghost trail colours (very dim, nearly black — just a hint of motion) */ +export const GHOST_COLORS: readonly number[] = [236, 234]; +export const GHOST_FRAMES = GHOST_COLORS.length; + +// ── Haunted mode: a resurrected bug ───────────────────────────────────── +// An early version of afterlife had no `age < 0` guard on ghost decay, so +// every never-alive cell cycled 0 → -1 → … → -6 → 0 forever: the entire +// empty universe pulsed as synchronized ghosts. On terminals with extended +// colour pairs, the ghost pair ids (463–504) then overflowed the 8-bit pair +// field in the curses attr word and aliased into the gradient dual-pair +// table — painting the void magenta and orange, and leaving every glider's +// path visible forever as a phase-shifted trail. The tables below encode +// the exact colours that aliasing produced, on purpose this time. +export const HAUNT_FRAMES = 6; +/** The 21-colour gradient of the era, needed to decode the aliased pairs */ +const HAUNT_ERA_GRADIENT: readonly number[] = [ + 17, 18, 19, 20, 21, 57, 56, 93, 129, 165, 164, + 163, 198, 204, 203, 209, 208, 214, 220, 229, 231, +]; +const era = (i: number): number => HAUNT_ERA_GRADIENT[i] ?? 0; +/** Single ghost over true emptiness: pairs 463–468 → dual table idx 185+i. [fg, bg] */ +export const HAUNT_SINGLE: readonly (readonly [number, number])[] = Array.from( + { length: HAUNT_FRAMES }, + (_, i) => [era(Math.floor((185 + i) / 21)), era((185 + i) % 21)] as const, +); +/** Ghost over ghost (top phase i, bottom phase j): pairs 469–504 → idx 191+i*6+j. [fg, bg] */ +export const HAUNT_DUAL: readonly (readonly [number, number])[] = Array.from( + { length: HAUNT_FRAMES * HAUNT_FRAMES }, + (_, k) => [era(Math.floor((191 + k) / 21)), era((191 + k) % 21)] as const, +); + +// ── Activity field ────────────────────────────────────────────────────── +// A decaying map of recent change (births + deaths). This is how the +// universe senses itself: the camera steers toward it, injections read it +// to find dead zones and living targets, the music pans with it, and time +// dilation listens for its spikes. Half-life ≈ 34 steps at 0.98/step. +// The decay multiply touches the whole world, so it's batched: applied +// every ACTIVITY_DECAY_EVERY steps at DECAY^EVERY (same integral, 1/4 the +// memory traffic). +export const ACTIVITY_DECAY = 0.98; +export const ACTIVITY_DECAY_EVERY = 4; +export const ACTIVITY_DECAY_BATCH = Math.fround(ACTIVITY_DECAY ** ACTIVITY_DECAY_EVERY); + +/** Sparkline characters */ +export const SPARKS = '▁▂▃▄▅▆▇█'; + +// ── Zoom ──────────────────────────────────────────────────────────────── +export const MIN_ZOOM = -2; // 4× zoom out (each terminal pixel = 4×4 grid cells) +export const MAX_ZOOM = 2; // 4× zoom in (each grid cell = 4×4 terminal pixels) +export const ZOOM_LABELS: Readonly> = { + [-2]: '0.25x', [-1]: '0.50x', 0: ' 1.0x', 1: ' 2.0x', 2: ' 4.0x', +}; + +// ── Pattern library ───────────────────────────────────────────────────── +export type Cells = readonly (readonly [number, number])[]; + +/** (row, col) offsets, verbatim from life.py */ +export const PATTERNS: Readonly> = { + glider: [[0, 1], [1, 2], [2, 0], [2, 1], [2, 2]], + lwss: [[0, 1], [0, 4], [1, 0], [2, 0], [2, 4], [3, 0], [3, 1], [3, 2], [3, 3]], + hwss: [[0, 3], [0, 4], [1, 1], [1, 6], [2, 0], [3, 0], [3, 6], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5]], + r_pentomino: [[0, 1], [0, 2], [1, 0], [1, 1], [2, 1]], + acorn: [[0, 1], [1, 3], [2, 0], [2, 1], [2, 4], [2, 5], [2, 6]], + diehard: [[0, 6], [1, 0], [1, 1], [2, 1], [2, 5], [2, 6], [2, 7]], + pi_heptomino: [[0, 0], [0, 1], [0, 2], [1, 1], [2, 1], [3, 0], [3, 2]], + b_heptomino: [[0, 1], [1, 0], [1, 2], [1, 3], [2, 0], [2, 1], [3, 1]], + gosper_gun: [[0, 24], [1, 22], [1, 24], [2, 12], [2, 13], [2, 20], [2, 21], [2, 34], [2, 35], [3, 11], [3, 15], [3, 20], [3, 21], [3, 34], [3, 35], [4, 0], [4, 1], [4, 10], [4, 16], [4, 20], [4, 21], [5, 0], [5, 1], [5, 10], [5, 14], [5, 16], [5, 17], [5, 22], [5, 24], [6, 10], [6, 16], [6, 24], [7, 11], [7, 15], [8, 12], [8, 13]], + pulsar: [[0, 2], [0, 3], [0, 4], [0, 8], [0, 9], [0, 10], [2, 0], [2, 5], [2, 7], [2, 12], [3, 0], [3, 5], [3, 7], [3, 12], [4, 0], [4, 5], [4, 7], [4, 12], [5, 2], [5, 3], [5, 4], [5, 8], [5, 9], [5, 10], [7, 2], [7, 3], [7, 4], [7, 8], [7, 9], [7, 10], [8, 0], [8, 5], [8, 7], [8, 12], [9, 0], [9, 5], [9, 7], [9, 12], [10, 0], [10, 5], [10, 7], [10, 12], [12, 2], [12, 3], [12, 4], [12, 8], [12, 9], [12, 10]], + pentadecathlon: [[0, 1], [1, 1], [2, 0], [2, 2], [3, 1], [4, 1], [5, 1], [6, 1], [7, 0], [7, 2], [8, 1], [9, 1]], +}; + +export const TRAVELLERS: readonly string[] = ['glider', 'lwss', 'hwss']; +export const METHUSELAHS: readonly string[] = ['r_pentomino', 'acorn', 'diehard', 'pi_heptomino', 'b_heptomino']; +export const OSCILLATORS: readonly string[] = ['pulsar', 'pentadecathlon']; + +/** + * Glider travel direction per place() rotation (verified empirically: one + * cell diagonally per 4 generations). [dy, dx] + */ +export const GLIDER_DIR: readonly (readonly [number, number])[] = [ + [1, 1], [1, -1], [-1, -1], [-1, 1], +]; + +// ── Symmetric garden patterns ────────────────────────────────────────── +// Beautiful hand-crafted symmetric structures for rare special injections. + +/** Reflect a quadrant pattern into full 4-fold symmetry around (0,0). */ +function mirror4(cells: Cells): [number, number][] { + const full = new Map(); + for (const [r, c] of cells) { + for (const [y, x] of [[r, c], [r, -c], [-r, c], [-r, -c]] as const) full.set(`${y},${x}`, [y, x]); + } + return [...full.values()]; +} + +export const GARDENS: readonly Cells[] = [ + // "Diamond pulsar" — a radially symmetric oscillator seed + mirror4([ + [0, 1], [0, 2], [0, 3], + [1, 0], [1, 4], + [2, 0], [2, 5], + [3, 0], [3, 5], + [4, 1], [4, 5], + [5, 2], [5, 3], [5, 4], + ]), + // "Cross bloom" — explodes outward from a cross shape + mirror4([ + [0, 1], [0, 2], [0, 3], [0, 4], + [1, 0], [2, 0], [3, 0], [4, 0], + [2, 2], [3, 3], + ]), + // "Ring of life" — a hollow circle that breaks into travelers + mirror4([ + [0, 3], [0, 4], + [1, 2], [1, 5], + [2, 1], [2, 6], + [3, 0], [3, 7], + [4, 0], [4, 7], + [5, 1], [5, 6], + [6, 2], [6, 5], + [7, 3], [7, 4], + ]), +]; + +/** generations between autosaves (plus a save whenever the page is left) */ +export const AUTOSAVE_EVERY = 5000; diff --git a/afterlife/src/engine/life.ts b/afterlife/src/engine/life.ts new file mode 100644 index 0000000..02e9bc1 --- /dev/null +++ b/afterlife/src/engine/life.ts @@ -0,0 +1,1459 @@ +/** + * life.ts — the universe. + * + * An infinite, self-sustaining Game of Life with zoom, ported from + * afterlife's InfiniteLife (life.py) method for method. The simulation grid + * extends well beyond the viewport. A camera tracks the centre of activity, + * and life is periodically injected from beyond the visible edge. Zoom lets + * you pull back to see the macro structure or push in to watch individual + * interactions. + * + * Nothing in here touches the DOM: the page (main.ts) and oikos's screen + * both drive this same class, and scripts/ drive it from node. + * + * Where numpy did a whole-array expression, this does one pass over typed + * arrays. The one structural change is in step(): life.py has two code paths + * (a zero-padded bounding box, and a toroidal full-world roll) that compute + * the same physics; here they are one loop over either region, with toroidal + * indexing throughout (inside a padded box that fits, the wrapped neighbours + * it reads are dead anyway, which is exactly the padding's promise). + */ + +import { census } from './census'; +import { + ACTIVITY_DECAY_BATCH, + ACTIVITY_DECAY_EVERY, + GARDENS, + GHOST_FRAMES, + GLIDER_DIR, + HAUNT_FRAMES, + MAX_ZOOM, + METHUSELAHS, + MIN_ZOOM, + OSCILLATORS, + PATTERNS, + SPARKS, + TRAVELLERS, +} from './constants'; +import { EPOCHS, SIGHTING_MIN_COUNT, SIGHTING_MUSINGS, SIGHTING_RARITY } from './musings'; +import { NewsTicker } from './ticker'; + +// ── python's random, as used by life.py ──────────────────────────────── +/** random.randint: inclusive at both ends */ +export const randint = (a: number, b: number): number => a + Math.floor(Math.random() * (b - a + 1)); +export const choice = (xs: readonly T[]): T => xs[Math.floor(Math.random() * xs.length)] as T; +/** python's // (floor division), which rounds toward -inf */ +const fdiv = (a: number, b: number): number => Math.floor(a / b); +const clampi = (v: number, lo: number, hi: number): number => Math.max(lo, Math.min(v, hi)); + +const F85 = Math.fround(0.85); +const F15 = Math.fround(0.15); + +/** np.percentile's default (linear) on a sorted array, then int(). + * numpy's _lerp interpolates from the nearer end: a + (b-a)·t below the + * midpoint, b - (b-a)·(1-t) from it on, and the two can round apart. */ +function percentileInt(sorted: ArrayLike, p: number): number { + const n = sorted.length; + const pos = (p / 100) * (n - 1); + const lo = Math.floor(pos); + const hi = Math.min(n - 1, lo + 1); + const a = sorted[lo] ?? 0; + const b = sorted[hi] ?? 0; + const t = pos - lo; + return Math.trunc(t >= 0.5 ? b - (b - a) * (1 - t) : a + (b - a) * t); +} + +/** + * scipy.ndimage.uniform_filter(size=3, mode="constant") on an h×w map, to + * the bit. scipy filters axis 0 then axis 1, each as a running sum (add the + * cell entering, subtract the one leaving) divided by 3 at every step, so + * equal neighbourhoods can round apart; argmax breaks those near-ties by + * whatever scipy's rounding says, and so must we. + */ +function uniformFilter3(src: Float64Array, h: number, w: number): Float64Array { + const at = (a: Float64Array, i: number): number => a[i] ?? 0; + const mid = new Float64Array(h * w); + for (let x = 0; x < w; x++) { + let t = at(src, x) + (h > 1 ? at(src, w + x) : 0); + mid[x] = t / 3; + for (let y = 1; y < h; y++) { + t += (y + 1 < h ? at(src, (y + 1) * w + x) : 0) - (y >= 2 ? at(src, (y - 2) * w + x) : 0); + mid[y * w + x] = t / 3; + } + } + const out = new Float64Array(h * w); + for (let y = 0; y < h; y++) { + const r = y * w; + let t = at(mid, r) + (w > 1 ? at(mid, r + 1) : 0); + out[r] = t / 3; + for (let x = 1; x < w; x++) { + t += (x + 1 < w ? at(mid, r + x + 1) : 0) - (x >= 2 ? at(mid, r + x - 2) : 0); + out[r + x] = t / 3; + } + } + return out; +} + +/** A view-sized map: rows × cols, row-major. */ +export interface Plane { + h: number; + w: number; + data: T; +} + +/** What an adopted or saved universe carries (life.py's universe.npz). */ +export interface UniverseState { + h: number; + w: number; + /** ages: > 0 alive, < 0 ghost, 0 empty. The grid is exactly age > 0. */ + age: Int32Array; + generation: number; + totalInjections: number; +} + +/** The smallest world an InfiniteLife may have (see its constructor). */ +export interface WorldFloor { + minH?: number; + minW?: number; +} + +export class InfiniteLife { + // view_h/view_w = the display grid at zoom 0 (half-block doubles vertical) + readonly viewH: number; + readonly viewW: number; + // World must accommodate max zoom-out (4× in each axis) + readonly worldH: number; + readonly worldW: number; + + grid: Uint8Array; + age: Int32Array; + /** Smoothed age for rendering — breaks coherent oscillation stripes */ + ageSmooth: Float32Array; + /** Activity field: decaying map of recent change (see ACTIVITY_DECAY) */ + activity: Float32Array; + private nextRow: Uint8Array; + private colSum: Uint8Array; + + // Camera: top-left of viewport (at zoom 0) in world coordinates + camY: number; + camX: number; + autoCam = true; + zoomLevel = 0; + private zoomCooldown = 0; // frames until next zoom change allowed + + generation = 0; + paused = false; + delay = 50; + + // Population tracking + popHistory: number[] = []; // maxlen 500 + hashHistory: number[] = []; // maxlen 60 + private cachedPop = 0; + + // ── Engine telemetry (readable by stats overlay + logger) ─── + spread = 0; // pop spread over last 150 gens + popFloor = 0; // current minimum pop threshold + cyclePeriod = 0; // detected cycle period (0 = none) + lastEvent = ''; // last injection event + lastEventGen = 0; // generation of last event + totalInjections = 0; + + ticker = new NewsTicker(); + + // Auto-focus mode: when true, autoFocus() runs every other frame + autoFocusMode = false; + autoFocusFrame = 0; + + // Cached display grid/age (invalidated each step, lazily recomputed) + private dispGridCache: Plane | null = null; + private dispAgeCache: Plane | null = null; + + // Cached mood (computed once per generation) + private cachedMood = ''; + private cachedMoodGen = -1; + + /** Bounding box from last step() — reused by calculateTargetZoom() */ + private stepBbox: [number, number, number, number] | null = null; + + /** Haunted mode: resurrects the historical ghost-decay bug. Toggled with 'g'. */ + haunted = false; + + // Pattern census: sightings of known patterns in the viewport + lastCensus = new Map(); + lastCensusSites: [string, number, number][] = []; // (name, wy, wx) + private sightingGen = new Map(); // per-pattern last announce + private sightingCount = new Map(); // per-pattern musing rotation + private lastSightingGen = -1e9; + + // Dramaturge: while a staged plot (aimed gliders) is in flight, + // cycle/stagnation injections hold their breath until this generation. + // The population floor stays armed — it's life support. + private dramaUntil = 0; + + // Time dilation: activity baseline (EMA) and smoothed tempo factor + private actBaseline = 0; + dilation = 1; + + /** + * `world` bounds the world from below. life.py's floor (400×800) is the + * default; a resize passes the old world so shrinking the window never + * crops the universe, and a small screen (oikos) can pass 0 to keep only + * 5× its view. + */ + constructor(termRows: number, termCols: number, seed = true, world: WorldFloor = {}) { + this.viewH = termRows * 2; + this.viewW = termCols; + this.worldH = Math.max(this.viewH * 5, world.minH ?? 400); + this.worldW = Math.max(this.viewW * 5, world.minW ?? 800); + const n = this.worldH * this.worldW; + this.grid = new Uint8Array(n); + this.age = new Int32Array(n); + this.ageSmooth = new Float32Array(n); + this.activity = new Float32Array(n); + this.nextRow = new Uint8Array(this.worldW); + this.colSum = new Uint8Array(this.worldW); + this.camY = fdiv(this.worldH - this.viewH, 2); + this.camX = fdiv(this.worldW - this.viewW, 2); + if (seed) this.seedInitial(); + } + + // ── Seeding ───────────────────────────────────────────────────── + + private seedInitial(): void { + const cy = fdiv(this.worldH, 2); + const cx = fdiv(this.worldW, 2); + const vh = this.viewH; + const vw = this.viewW; + + for (const name of METHUSELAHS) { + for (let i = 0; i < 2; i++) { + this.place(name, cy + randint(fdiv(-vh, 3), fdiv(vh, 3)), cx + randint(fdiv(-vw, 3), fdiv(vw, 3))); + } + } + for (let i = 0; i < 2; i++) { + this.place('gosper_gun', cy + randint(fdiv(-vh, 4), fdiv(vh, 4)), cx + randint(fdiv(-vw, 4), fdiv(vw, 4))); + } + for (let i = 0; i < 20; i++) { + const y = clampi(cy + randint(-vh, vh), 10, this.worldH - 10); + const x = clampi(cx + randint(-vw, vw), 10, this.worldW - 10); + this.place(choice(TRAVELLERS), y, x); + } + for (let i = 0; i < 4; i++) { + // python draws y, x, then the oscillator + const y = cy + randint(fdiv(-vh, 3), fdiv(vh, 3)); + const x = cx + randint(fdiv(-vw, 3), fdiv(vw, 3)); + this.place(choice(OSCILLATORS), y, x); + } + + const y0 = cy - fdiv(vh, 2); + const x0 = cx - fdiv(vw, 2); + for (let y = 0; y < vh; y++) { + for (let x = 0; x < vw; x++) { + if (Math.random() < 0.035) this.grid[(y0 + y) * this.worldW + x0 + x] = 1; + } + } + + for (let i = 0; i < this.grid.length; i++) { + this.age[i] = this.grid[i] ? 1 : 0; + this.ageSmooth[i] = this.age[i] ?? 0; + } + } + + place(name: string, y: number, x: number, rotation?: number): void { + const cells = PATTERNS[name]; + if (!cells) return; + const rot = rotation ?? randint(0, 3); + for (let [dy, dx] of cells) { + for (let r = 0; r < rot; r++) [dy, dx] = [dx, -dy]; + const ny = y + dy; + const nx = x + dx; + if (ny >= 0 && ny < this.worldH && nx >= 0 && nx < this.worldW) { + const i = ny * this.worldW + nx; + this.grid[i] = 1; + this.age[i] = Math.max(this.age[i] ?? 0, 1); + } + } + this.invalidate(); + } + + private invalidate(): void { + this.dispGridCache = null; + this.dispAgeCache = null; + } + + // ── Simulation ────────────────────────────────────────────────── + + /** Advance one generation. Returns event string (empty if none). */ + step(): string { + if (this.paused) return ''; + this.invalidate(); + + const H = this.worldH; + const W = this.worldW; + const g = this.grid; + const age = this.age; + + // ── Bounding box of all active cells (alive + ghosts) ── + let by0 = H; + let by1 = -1; + let bx0 = W; + let bx1 = -1; + for (let y = 0; y < H; y++) { + const row = y * W; + let first = -1; + let last = -1; + for (let x = 0; x < W; x++) { + if (age[row + x] !== 0) { + if (first < 0) first = x; + last = x; + } + } + if (first < 0) continue; + if (y < by0) by0 = y; + by1 = y + 1; + if (first < bx0) bx0 = first; + if (last + 1 > bx1) bx1 = last + 1; + } + const hasActivity = by1 > 0; + + // Expand by 2 cells (kernel radius 1 + birth margin 1). If the padded + // box would spill past a world edge, toroidal wrap-around comes into + // play — fall back to the full world instead. + let useBbox = false; + if (hasActivity) { + const pad = 2; + const fits = by0 - pad >= 0 && by1 + pad <= H && bx0 - pad >= 0 && bx1 + pad <= W; + if (fits) { + by0 -= pad; + by1 += pad; + bx0 -= pad; + bx1 += pad; + useBbox = (by1 - by0) * (bx1 - bx0) <= fdiv(H * W, 2); + } + } + // Haunted mode keeps (nearly) every cell's age in motion — the bbox path + // would freeze ages outside the box, so always take the full world. + useBbox = useBbox && !this.haunted; + this.stepBbox = hasActivity && useBbox ? [by0, by1, bx0, bx1] : null; + if (!useBbox) { + by0 = 0; + by1 = H; + bx0 = 0; + bx1 = W; + } + + // Temporal smoothing decays the whole world; the region adds its ages below + const sm = this.ageSmooth; + for (let i = 0; i < sm.length; i++) sm[i] = (sm[i] ?? 0) * F85; + + const act = this.activity; + const next = this.nextRow; + const cs = this.colSum; + const ghostFloor = this.haunted ? -HAUNT_FRAMES : -GHOST_FRAMES; + const haunted = this.haunted; + // rows are written one behind, so the row above is still the old one + // when the row below reads it: keep the old row y-1 and row by0 aside + const prevOld = new Uint8Array(W); + const firstOld = g.slice(by0 * W, by0 * W + W); + let pop = 0; + + for (let y = by0; y < by1; y++) { + const rowUp = y === 0 ? H - 1 : y - 1; + const rowDn = y === H - 1 ? 0 : y + 1; + const up = y === by0 ? null : prevOld; // old row y-1 (already overwritten in g) + const base = y * W; + const upBase = rowUp * W; + // the row below is never written yet, unless it wrapped round to by0 + const dnIsFirst = rowDn === by0 && y !== by0; + const dnBase = rowDn * W; + // column sums over the three old rows, for this region's columns ±1 + const xa = bx0 === 0 ? 0 : bx0 - 1; + const xb = bx1 === W ? W : bx1 + 1; + for (let x = xa; x < xb; x++) { + const u = up ? (up[x] ?? 0) : (g[upBase + x] ?? 0); + const d = dnIsFirst ? (firstOld[x] ?? 0) : (g[dnBase + x] ?? 0); + cs[x] = u + (g[base + x] ?? 0) + d; + } + if (bx0 === 0 && bx1 === W) { + // full-width rows wrap left↔right + for (let x = 0; x < W; x++) { + const l = x === 0 ? W - 1 : x - 1; + const r = x === W - 1 ? 0 : x + 1; + next[x] = (cs[l] ?? 0) + (cs[x] ?? 0) + (cs[r] ?? 0) - (g[base + x] ?? 0); + } + } else { + for (let x = bx0; x < bx1; x++) { + next[x] = (cs[x - 1] ?? 0) + (cs[x] ?? 0) + (cs[x + 1] ?? 0) - (g[base + x] ?? 0); + } + } + // keep this row's old state for the row below, then write the new one + prevOld.set(g.subarray(base + xa, base + xb), xa); + for (let x = bx0; x < bx1; x++) { + const i = base + x; + const n = next[x] ?? 0; + const alive = g[i] === 1; + const birth = !alive && n === 3; + const survive = alive && (n === 3 || n === 2); + if (birth || (alive && !survive)) act[i] = (act[i] ?? 0) + 1; + const a = age[i] ?? 0; + let na: number; + if (survive) na = a + 1; + else if (birth) na = 1; + else if (a > 0) na = -1; + // the resurrected bug: no `age < 0` guard in haunted mode, so + // never-alive cells (age 0) fall into the decay branch too and the + // whole empty universe cycles 0 → -1 → … → -HAUNT_FRAMES → 0 + else if ((haunted || a < 0) && a > ghostFloor) na = a - 1; + else na = 0; + age[i] = na; + sm[i] = (sm[i] ?? 0) + Math.fround(Math.fround(na) * F15); // float32, as numpy rounds it + const v = birth || survive ? 1 : 0; + g[i] = v; + pop += v; + } + } + + this.generation += 1; + + // Batched activity decay (see ACTIVITY_DECAY_EVERY) + if (this.generation % ACTIVITY_DECAY_EVERY === 0) { + for (let i = 0; i < act.length; i++) act[i] = (act[i] ?? 0) * ACTIVITY_DECAY_BATCH; + } + + this.cachedPop = pop; + this.popHistory.push(pop); + if (this.popHistory.length > 500) this.popHistory.shift(); + + this.hashHistory.push(this.viewportHash()); + if (this.hashHistory.length > 60) this.hashHistory.shift(); + + const event = this.ensureLife(pop); + this.updateCamera(); + return event; + } + + /** hash(raw viewport bytes): two FNV-1a lanes folded into one safe integer */ + private viewportHash(): number { + const [y0, x0] = this.clampCam(); + let h1 = 0x811c9dc5; + let h2 = 0x01000193; + for (let y = 0; y < this.viewH; y++) { + const row = (y0 + y) * this.worldW + x0; + for (let x = 0; x < this.viewW; x++) { + const v = this.grid[row + x] ?? 0; + h1 = Math.imul(h1 ^ v, 16777619); + h2 = Math.imul(h2 ^ (v + 0x9e), 2246822519); + } + } + return (h1 >>> 0) * 2097152 + ((h2 >>> 0) & 0x1fffff); + } + + // ── Infinity engine ───────────────────────────────────────────── + + /** + * The core 'infinite' mechanic. Returns event name if triggered. + * + * Tunable parameters (visible in stats): + * - pop_floor: ~2.5% of default viewport area + * - stagnation window: 150 generations + * - stagnation threshold: spread < max(8, floor/8) + * - cycle detection: periods 1-30 + * - edge spawn interval: every 300 generations + */ + private ensureLife(population: number): string { + const viewportArea = this.viewH * this.viewW; + const baseFloor = fdiv(viewportArea, 40); + // Adaptive floor: rises to ~20% of recent average population, so the + // simulation faces real challenges at higher pop counts too. Clamped to + // base_floor minimum so bootstrap still works. + const ph = this.popHistory; + const phLen = ph.length; + if (phLen >= 100) { + let recentSum = 0; + for (let i = phLen - 100; i < phLen; i++) recentSum += ph[i] ?? 0; + this.popFloor = Math.max(baseFloor, Math.trunc(recentSum * 0.002)); // /100 * 0.20 + } else { + this.popFloor = baseFloor; + } + + // ── Stagnation spread ── + this.spread = 0; + if (phLen >= 150) { + let lo = ph[phLen - 1] ?? 0; + let hi = lo; + for (let i = phLen - 150; i < phLen; i++) { + const v = ph[i] ?? 0; + if (v < lo) lo = v; + if (v > hi) hi = v; + } + this.spread = hi - lo; + } + const stagnant = phLen >= 150 && this.spread < Math.max(8, fdiv(this.popFloor, 8)); + + // ── Cycle detection ── + this.cyclePeriod = 0; + const hh = this.hashHistory; + const hhLen = hh.length; + if (hhLen >= 4) { + const latest = hh[hhLen - 1]; + for (let period = 1; period < Math.min(31, hhLen); period++) { + if (hh[hhLen - 1 - period] === latest) { + this.cyclePeriod = period; + break; + } + } + } + + // ── Act ── + // While a staged plot is in flight (aimed gliders travelling), + // cycle/stagnation responses hold their breath and let it arrive. + // The population floor stays armed — it's life support, not drama. + const dramaPending = this.generation < this.dramaUntil; + + let event = ''; + if (population < fdiv(this.popFloor, 3)) { + event = 'inject:massive'; + this.inject('massive'); + } else if (population < this.popFloor) { + event = 'inject:heavy(low_pop)'; + this.inject('heavy'); + } else if (this.cyclePeriod > 0 && !dramaPending) { + // An attractor: prefer assassination over carpet-bombing. + const target = Math.random() < 0.65 ? this.injectProvoke() : ''; + if (target) { + event = `inject:provoke(cycle=${this.cyclePeriod},${target})`; + } else { + event = `inject:heavy(cycle=${this.cyclePeriod})`; + this.inject('heavy'); + } + } else if (stagnant && !dramaPending) { + // Quiet stretch: stage a collision instead of dumping mass. + if (Math.random() < 0.7 && this.injectCollide()) { + event = 'inject:collide(stagnant)'; + } else { + event = 'inject:medium(stagnant)'; + this.inject('medium'); + } + } else if (this.generation % 5000 === 0 && this.generation > 0) { + // Rare garden event — a beautiful symmetric pattern appears + event = 'inject:garden'; + this.injectGarden(); + } else if (this.generation % 300 === 0 && this.generation > 0) { + event = 'inject:edge'; + this.injectFromEdge(); + this.injectFromEdge(); + } + + if (event) { + this.lastEvent = event; + this.lastEventGen = this.generation; + this.totalInjections += 1; + } + return event; + } + + private inject(intensity: 'massive' | 'heavy' | 'medium' = 'medium'): void { + const n = { massive: 8, heavy: 5, medium: 3 }[intensity]; + for (let i = 0; i < n; i++) { + let name: string; + if (intensity === 'massive' && Math.random() < 0.3) name = 'gosper_gun'; + else if (Math.random() < 0.4) name = choice(METHUSELAHS); + else name = choice([...TRAVELLERS, ...OSCILLATORS]); + + // New life blooms where it's darkest (jitter breaks the block-grid + // regularity of the void finder) + const [y, x] = this.findQuietSpot(); + this.place(name, y + randint(-6, 6), x + randint(-6, 6)); + } + if (intensity === 'massive' || intensity === 'heavy') { + const [y, x] = this.findQuietSpot(); + this.place('gosper_gun', y, x); + } + } + + private injectFromEdge(): void { + const cy = this.camY + fdiv(this.viewH, 2); + const cx = this.camX + fdiv(this.viewW, 2); + // python's -v // 3 is (-v) // 3: the low bound floors away from zero + const vh3 = fdiv(this.viewH, 3); + const vw3 = fdiv(this.viewW, 3); + const vh3lo = fdiv(-this.viewH, 3); + const vw3lo = fdiv(-this.viewW, 3); + const edge = choice(['top', 'bottom', 'left', 'right'] as const); + let y: number; + let x: number; + let rot: number; + if (edge === 'top') [y, x, rot] = [this.camY + 2, cx + randint(vw3lo, vw3), 2]; + else if (edge === 'bottom') [y, x, rot] = [this.camY + this.viewH - 5, cx + randint(vw3lo, vw3), 0]; + else if (edge === 'left') [y, x, rot] = [cy + randint(vh3lo, vh3), this.camX + 2, 1]; + else [y, x, rot] = [cy + randint(vh3lo, vh3), this.camX + this.viewW - 10, 3]; + this.place(choice(TRAVELLERS), y, x, rot); + } + + /** Place a symmetric garden pattern near the camera center. */ + private injectGarden(): void { + const cy = this.camY + fdiv(this.viewH, 2); + const cx = this.camX + fdiv(this.viewW, 2); + const garden = choice(GARDENS); + // Offset slightly from dead center for visual interest + const oy = randint(fdiv(-this.viewH, 6), fdiv(this.viewH, 6)); + const ox = randint(fdiv(-this.viewW, 6), fdiv(this.viewW, 6)); + for (const [dy, dx] of garden) { + const ny = cy + oy + dy; + const nx = cx + ox + dx; + if (ny >= 0 && ny < this.worldH && nx >= 0 && nx < this.worldW) { + this.grid[ny * this.worldW + nx] = 1; + this.age[ny * this.worldW + nx] = 1; + } + } + this.invalidate(); + } + + // ── Dramaturge ────────────────────────────────────────────────── + // Injections with intention. The engine reads the activity field and the + // census, then composes events instead of rolling dice: new life blooms in + // the darkest visible void, assassins are aimed at named citizens, + // collisions are scheduled in the quiet. + + /** + * Pick a low-occupancy, low-activity spot in the visible region. + * + * Block-reduces occupancy + activity over the current zoom cover and + * chooses randomly among the darkest quintile, so revivals bloom in the + * void where there's room to watch them grow. + */ + findQuietSpot(): [number, number] { + const [cy, cx] = this.viewCenter(); + const factor = this.zoomLevel < 0 ? 1 << -this.zoomLevel : 1; + const coverH = Math.min(this.worldH, this.viewH * factor); + const coverW = Math.min(this.worldW, this.viewW * factor); + const y0 = Math.max(0, Math.min(cy - fdiv(coverH, 2), this.worldH - coverH)); + const x0 = Math.max(0, Math.min(cx - fdiv(coverW, 2), this.worldW - coverW)); + + const B = 16; + const bh = fdiv(coverH, B); + const bw = fdiv(coverW, B); + if (bh < 2 || bw < 2) return [cy, cx]; + + const score = new Float64Array(bh * bw); + for (let y = 0; y < bh * B; y++) { + const row = (y0 + y) * this.worldW + x0; + const sr = fdiv(y, B) * bw; + for (let x = 0; x < bw * B; x++) { + const k = sr + fdiv(x, B); + score[k] = (score[k] ?? 0) + (this.grid[row + x] ?? 0) * 2 + (this.activity[row + x] ?? 0); + } + } + const k = Math.max(1, fdiv(score.length, 5)); + // life.py takes np.argpartition(flat, k-1)[:k], whose order among equal + // scores (and which ties make the cut) is numpy's implementation detail: + // introselect, or SIMD on AVX-512 builds. A stable sort gives the same + // darkest quintile with its ties in index order. + const order = Array.from(score.keys()).sort((a, b) => (score[a] ?? 0) - (score[b] ?? 0)); + const pick = choice(order.slice(0, k)); + const by = fdiv(pick, bw); + const bx = pick % bw; + return [y0 + by * B + fdiv(B, 2), x0 + bx * B + fdiv(B, 2)]; + } + + /** + * Place a glider `dist` cells out, aimed to pass through (ty, tx). + * + * The origin must sit inside the raw viewport (so the staging itself breaks + * any viewport-hash cycle) with a clear launch pad. Returns true on + * placement; ETA is dist * 4 generations. + */ + private aimGlider(ty: number, tx: number, dist: number, rot: number): boolean { + const [dy, dx] = GLIDER_DIR[rot] ?? [1, 1]; + const oy = ty - dy * dist; + const ox = tx - dx * dist; + const [vy0, vx0] = this.clampCam(); + if (!(vy0 + 2 <= oy && oy < vy0 + this.viewH - 5 && vx0 + 2 <= ox && ox < vx0 + this.viewW - 5)) return false; + for (let y = Math.max(0, oy - 2); y < Math.min(this.worldH, oy + 5); y++) { + for (let x = Math.max(0, ox - 2); x < Math.min(this.worldW, ox + 5); x++) { + if (this.grid[y * this.worldW + x]) return false; + } + } + this.place('glider', oy, ox, rot); + return true; + } + + /** Aim a glider at a named citizen (rarest first). Returns the target's + * name, or '' if no shot lined up. */ + private injectProvoke(): string { + const sites = [...this.lastCensusSites]; + if (!sites.length) return ''; + sites.sort((a, b) => (SIGHTING_RARITY[b[0]] ?? 0) - (SIGHTING_RARITY[a[0]] ?? 0)); + // Consider the most notable few, in order + for (const [name, ty, tx] of sites.slice(0, 6)) { + const dist = randint(35, 60); + const rots = [0, 1, 2, 3]; + for (let i = rots.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [rots[i], rots[j]] = [rots[j] ?? 0, rots[i] ?? 0]; + } + for (const rot of rots) { + if (this.aimGlider(ty, tx, dist, rot)) { + this.dramaUntil = this.generation + dist * 4 + 150; + this.ticker.queueSpecial('the cosmos takes aim'); + return name; + } + } + } + return ''; + } + + /** Stage a head-on glider collision at a quiet visible spot. */ + private injectCollide(): boolean { + let [ty, tx] = this.findQuietSpot(); + const [vy0, vx0] = this.clampCam(); + + for (let attempt = 0; attempt < 6; attempt++) { + // Pick the throw distance first, then clamp the meeting point into the + // band where both launch pads provably fit inside the viewport + // (origins sit at target ∓ dist on each axis). + const dist = randint(16, 34); + const loY = vy0 + dist + 3; + const hiY = vy0 + this.viewH - dist - 6; + const loX = vx0 + dist + 3; + const hiX = vx0 + this.viewW - dist - 6; + if (loY >= hiY || loX >= hiX) continue; // viewport too small for this throw + ty = Math.max(loY, Math.min(ty, hiY)); + tx = Math.max(loX, Math.min(tx, hiX)); + const rot = randint(0, 3); + // Jitter varies the collision outcome; right-angle approaches (rot ± 1) + // explode far more often than head-on (rot + 2), which mostly + // annihilates quietly — measured 14/49 vs 1/49 explosion geometries. + const jy = randint(-3, 3); + const jx = randint(-3, 3); + if (!this.aimGlider(ty, tx, dist, rot)) continue; + // First glider is in flight; try to give it a partner. (If none fits, + // a lone glider crossing the quiet is still a scene — keep the + // appointment either way.) + const partnerRot = (rot + choice([1, 3])) % 4; + const paired = this.aimGlider(ty + jy, tx + jx, dist, partnerRot); + this.dramaUntil = this.generation + dist * 4 + 150; + this.ticker.queueSpecial(paired ? 'two gliders, one appointment' : 'a lone glider, sent into the dark'); + return true; + } + return false; + } + + // ── Camera ────────────────────────────────────────────────────── + + /** Calculate optimal zoom level based on activity spread. */ + private calculateTargetZoom(): number { + // Every nonzero age lives inside the bbox recorded by step(), so restrict + // the scan to it. Falls back to the full world otherwise. + const [by0, by1, bx0, bx1] = this.stepBbox ?? [0, this.worldH, 0, this.worldW]; + const W = this.worldW; + + // Find recent activity (age 1-10) or fall back to all living cells + let nActive = 0; + for (let y = by0; y < by1 && nActive < 5; y++) { + for (let x = bx0; x < bx1; x++) { + const a = this.age[y * W + x] ?? 0; + if (a >= 1 && a <= 10 && ++nActive >= 5) break; + } + } + const recent = nActive >= 5; + const ys: number[] = []; + const xs: number[] = []; + for (let y = by0; y < by1; y++) { + for (let x = bx0; x < bx1; x++) { + const i = y * W + x; + const hit = recent ? (this.age[i] ?? 0) >= 1 && (this.age[i] ?? 0) <= 10 : this.grid[i] !== 0; + if (hit) { + ys.push(y); + xs.push(x); + } + } + } + if (ys.length < 2) return this.zoomLevel; + + // Use a percentile range for a tight bbox that ignores outliers + const sx = Int32Array.from(xs).sort(); + const yLo = percentileInt(ys, 15); + const yHi = percentileInt(ys, 85); + const xLo = percentileInt(sx, 15); + const xHi = percentileInt(sx, 85); + const bboxH = Math.max(4, yHi - yLo + 1); + const bboxW = Math.max(4, xHi - xLo + 1); + + // Choose zoom to fit the core activity in ~50% of viewport + let best = MIN_ZOOM; + for (let z = MAX_ZOOM; z >= MIN_ZOOM; z--) { + const mag = 2 ** z; + if (bboxH <= (this.viewH / mag) * 0.5 && bboxW <= (this.viewW / mag) * 0.5) { + best = z; + break; + } + } + return clampi(best, MIN_ZOOM, MAX_ZOOM); + } + + private updateCamera(): void { + if (!this.autoCam) return; + + const pad = 30; + const y0 = Math.max(0, this.camY - pad); + const y1 = Math.min(this.worldH, this.camY + this.viewH + pad); + const x0 = Math.max(0, this.camX - pad); + const x1 = Math.min(this.worldW, this.camX + this.viewW + pad); + const W = this.worldW; + + // Steer toward drama: centroid of squared activity (squaring emphasizes + // hotspots — an explosion outweighs a blinker farm). Falls back to the + // population centroid when the region is calm. + let total = 0; + let wy = 0; + let wx = 0; + for (let y = y0; y < y1; y++) { + for (let x = x0; x < x1; x++) { + const a = this.activity[y * W + x] ?? 0; + if (a === 0) continue; + const a2 = a * a; + total += a2; + wy += a2 * (y - y0); + wx += a2 * (x - x0); + } + } + if (total > 25) { + this.steer(Math.trunc(wy / total) + y0, Math.trunc(wx / total) + x0); + this.advanceAutoZoom(); + return; + } + + let n = 0; + let sy = 0; + let sx = 0; + for (let y = y0; y < y1; y++) { + for (let x = x0; x < x1; x++) { + if (this.grid[y * W + x]) { + n++; + sy += y - y0; + sx += x - x0; + } + } + } + if (n > 0) this.steer(Math.trunc(sy / n) + y0, Math.trunc(sx / n) + x0); + this.advanceAutoZoom(); + } + + private steer(cy: number, cx: number): void { + const ty = clampi(cy - fdiv(this.viewH, 2), 0, this.worldH - this.viewH); + const tx = clampi(cx - fdiv(this.viewW, 2), 0, this.worldW - this.viewW); + this.camY = Math.trunc(this.camY + (ty - this.camY) * 0.04); + this.camX = Math.trunc(this.camX + (tx - this.camX) * 0.04); + } + + /** Auto-zoom: gradually step toward optimal zoom level. + * Throttled: only recalculates every 4th frame when cooldown is 0. */ + private advanceAutoZoom(): void { + this.zoomCooldown = Math.max(0, this.zoomCooldown - 1); + if (this.zoomCooldown !== 0 || this.generation % 4 !== 0) return; + const target = this.calculateTargetZoom(); + if (target > this.zoomLevel && this.zoomLevel < MAX_ZOOM) { + this.zoomLevel += 1; + this.zoomCooldown = 45; // ~1.5 sec at 30fps before next change + } else if (target < this.zoomLevel && this.zoomLevel > MIN_ZOOM) { + // Verify zoom out is safe (world can fit the expanded view) + const nz = this.zoomLevel - 1; + const factor = nz >= 0 ? 1 : 1 << -nz; + if (this.viewH * factor <= this.worldH && this.viewW * factor <= this.worldW) { + this.zoomLevel = nz; + this.zoomCooldown = 45; + } + } + } + + // ── Viewport access (raw = zoom 0, display = current zoom) ───── + + clampCam(): [number, number] { + return [clampi(this.camY, 0, this.worldH - this.viewH), clampi(this.camX, 0, this.worldW - this.viewW)]; + } + + viewCenter(): [number, number] { + const [y0, x0] = this.clampCam(); + return [y0 + fdiv(this.viewH, 2), x0 + fdiv(this.viewW, 2)]; + } + + /** The raw viewport (zoom 0) as a view_h × view_w occupancy map. */ + rawViewGrid(): Plane { + const [y0, x0] = this.clampCam(); + const out = new Uint8Array(this.viewH * this.viewW); + for (let y = 0; y < this.viewH; y++) { + const src = (y0 + y) * this.worldW + x0; + out.set(this.grid.subarray(src, src + this.viewW), y * this.viewW); + } + return { h: this.viewH, w: this.viewW, data: out }; + } + + /** The region the current zoom shows: [y0, x0, h, w, factor, zoomedIn]. */ + private cover(): [number, number, number, number, number, boolean] | null { + const [cy, cx] = this.viewCenter(); + if (this.zoomLevel < 0) { + const factor = 1 << -this.zoomLevel; // 2 or 4 + const ch = this.viewH * factor; + const cw = this.viewW * factor; + if (ch > this.worldH || cw > this.worldW) return null; + const y0 = Math.max(0, Math.min(cy - fdiv(ch, 2), this.worldH - ch)); + const x0 = Math.max(0, Math.min(cx - fdiv(cw, 2), this.worldW - cw)); + return [y0, x0, ch, cw, factor, false]; + } + const factor = 1 << this.zoomLevel; // 2 or 4 + const ch = Math.max(2, fdiv(this.viewH, factor)); + const cw = Math.max(2, fdiv(this.viewW, factor)); + const y0 = Math.max(0, Math.min(cy - fdiv(ch, 2), this.worldH - ch)); + const x0 = Math.max(0, Math.min(cx - fdiv(cw, 2), this.worldW - cw)); + return [y0, x0, ch, cw, factor, true]; + } + + /** Grid scaled for the current zoom level, sized (about) view_h × view_w. */ + displayGrid(): Plane { + if (!this.dispGridCache) this.dispGridCache = this.computeDisplayGrid(); + return this.dispGridCache; + } + + private computeDisplayGrid(): Plane { + if (this.zoomLevel === 0) return this.rawViewGrid(); + const c = this.cover(); + if (!c) return this.rawViewGrid(); + const [y0, x0, ch, cw, f, zoomedIn] = c; + const W = this.worldW; + if (!zoomedIn) { + const bh = fdiv(ch, f); + const bw = fdiv(cw, f); + const out = new Uint8Array(bh * bw); + for (let y = 0; y < bh * f; y++) { + const src = (y0 + y) * W + x0; + const dst = fdiv(y, f) * bw; + for (let x = 0; x < bw * f; x++) if (this.grid[src + x]) out[dst + fdiv(x, f)] = 1; + } + return { h: bh, w: bw, data: out }; + } + const h = Math.min(ch * f, this.viewH); + const w = Math.min(cw * f, this.viewW); + const out = new Uint8Array(h * w); + for (let y = 0; y < h; y++) { + const src = (y0 + fdiv(y, f)) * W + x0; + for (let x = 0; x < w; x++) out[y * w + x] = this.grid[src + fdiv(x, f)] ?? 0; + } + return { h, w, data: out }; + } + + /** + * Age map scaled for the current zoom level. + * + * Handles ghost trails: positive ages = alive, negative = ghost (recently + * dead). When downsampling, prioritizes living cells; shows the brightest + * ghost otherwise. + */ + displayAge(): Plane { + if (!this.dispAgeCache) this.dispAgeCache = this.computeDisplayAge(); + return this.dispAgeCache; + } + + private computeDisplayAge(): Plane { + // Haunted mode renders raw ages: the ghost phase cycle IS the visual — + // temporal smoothing would blur the strobe into mush. + const src: Int32Array | Float32Array = this.haunted ? this.age : this.ageSmooth; + const W = this.worldW; + const at = (i: number): number => Math.trunc(src[i] ?? 0); // astype(int32) + + const c = this.zoomLevel === 0 ? null : this.cover(); + if (!c) { + const [y0, x0] = this.clampCam(); + const out = new Int32Array(this.viewH * this.viewW); + for (let y = 0; y < this.viewH; y++) { + const s = (y0 + y) * W + x0; + for (let x = 0; x < this.viewW; x++) out[y * this.viewW + x] = at(s + x); + } + return { h: this.viewH, w: this.viewW, data: out }; + } + const [y0, x0, ch, cw, f, zoomedIn] = c; + if (!zoomedIn) { + const bh = fdiv(ch, f); + const bw = fdiv(cw, f); + // Smart aggregation: prioritize alive (positive), then the brightest + // ghost (least negative) + const sentinel = -(this.haunted ? HAUNT_FRAMES : GHOST_FRAMES) - 1; + const maxv = new Int32Array(bh * bw).fill(-2147483648); + const ghost = new Int32Array(bh * bw).fill(sentinel); + for (let y = 0; y < bh * f; y++) { + const s = (y0 + y) * W + x0; + const d = fdiv(y, f) * bw; + for (let x = 0; x < bw * f; x++) { + const v = at(s + x); + const k = d + fdiv(x, f); + if (v > (maxv[k] ?? 0)) maxv[k] = v; + const m = v !== 0 ? v : sentinel; + if (m > (ghost[k] ?? 0)) ghost[k] = m; + } + } + const out = new Int32Array(bh * bw); + for (let k = 0; k < out.length; k++) { + const mv = maxv[k] ?? 0; + const gm = ghost[k] ?? 0; + out[k] = mv > 0 ? mv : gm > sentinel ? gm : 0; + } + return { h: bh, w: bw, data: out }; + } + const h = Math.min(ch * f, this.viewH); + const w = Math.min(cw * f, this.viewW); + const out = new Int32Array(h * w); + for (let y = 0; y < h; y++) { + const s = (y0 + fdiv(y, f)) * W + x0; + for (let x = 0; x < w; x++) out[y * w + x] = at(s + fdiv(x, f)); + } + return { h, w, data: out }; + } + + // ── Zoom ──────────────────────────────────────────────────────── + + zoomIn(): void { + if (this.zoomLevel < MAX_ZOOM) this.zoomLevel += 1; + this.invalidate(); + } + + zoomOut(): void { + if (this.zoomLevel > MIN_ZOOM) { + const nz = this.zoomLevel - 1; + const factor = nz >= 0 ? 1 : 1 << -nz; + // Zooming past default into negative — verify the world fits + if (nz >= 0 || (this.viewH * factor <= this.worldH && this.viewW * factor <= this.worldW)) { + this.zoomLevel = nz; + } + } + this.invalidate(); + } + + /** + * Zoom + pan to the densest cluster of recent activity. + * + * Strategy: build a coarse heat map, find the hottest spot, then use the + * 10th–90th percentile range (not the full bounding box) of nearby active + * cells to size the zoom — this ignores sparse outliers and focuses on the + * tight core of the action. + */ + autoFocus(): void { + const H = this.worldH; + const W = this.worldW; + let recentCount = 0; + let aliveCount = 0; + for (let i = 0; i < this.age.length; i++) { + const a = this.age[i] ?? 0; + if (a >= 1 && a <= 10) recentCount++; + if (this.grid[i]) aliveCount++; + } + const recent = recentCount >= 5; + if ((recent ? recentCount : aliveCount) === 0) return; + const active = (i: number): boolean => { + const a = this.age[i] ?? 0; + return recent ? a >= 1 && a <= 10 : this.grid[i] !== 0; + }; + + // Coarse heat map: 16×16 cell blocks + const block = 16; + const bh = fdiv(H, block); + const bw = fdiv(W, block); + if (bh < 1 || bw < 1) return; + const heat = new Float64Array(bh * bw); + for (let y = 0; y < bh * block; y++) { + for (let x = 0; x < bw * block; x++) { + if (!active(y * W + x)) continue; + const k = fdiv(y, block) * bw + fdiv(x, block); + heat[k] = (heat[k] ?? 0) + 1; + } + } + + // Find the hottest 3×3 neighbourhood (not just one block). argmax takes + // the first maximum of scipy's filter, rounding and all. + const smoothHeat = uniformFilter3(heat, bh, bw); + let best = -1; + let hot = 0; + for (let k = 0; k < smoothHeat.length; k++) { + const v = smoothHeat[k] ?? 0; + if (v > best) { + best = v; + hot = k; + } + } + const focusCy = fdiv(hot, bw) * block + fdiv(block, 2); + const focusCx = (hot % bw) * block + fdiv(block, 2); + + // Tight search region around hotspot (±2 blocks) + const r = block * 2; + const ry0 = Math.max(0, focusCy - r); + const ry1 = Math.min(H, focusCy + r); + const rx0 = Math.max(0, focusCx - r); + const rx1 = Math.min(W, focusCx + r); + const ys: number[] = []; + const xs: number[] = []; + for (let y = ry0; y < ry1; y++) { + for (let x = rx0; x < rx1; x++) { + if (active(y * W + x)) { + ys.push(y - ry0); + xs.push(x - rx0); + } + } + } + if (!ys.length) return; + const sx = Int32Array.from(xs).sort(); + const yLo = percentileInt(ys, 10); + const yHi = percentileInt(ys, 90); + const xLo = percentileInt(sx, 10); + const xHi = percentileInt(sx, 90); + const bboxH = Math.max(4, yHi - yLo + 1); + const bboxW = Math.max(4, xHi - xLo + 1); + const centerY = ry0 + fdiv(yLo + yHi, 2); + const centerX = rx0 + fdiv(xLo + xHi, 2); + + // Choose zoom to fit the core cluster in ~60% of viewport + let bestZ = MIN_ZOOM; + for (let z = MAX_ZOOM; z >= MIN_ZOOM; z--) { + const mag = 2 ** z; + if (bboxH <= (this.viewH / mag) * 0.6 && bboxW <= (this.viewW / mag) * 0.6) { + bestZ = z; + break; + } + } + this.zoomLevel = clampi(bestZ, MIN_ZOOM, MAX_ZOOM); + this.camY = clampi(centerY - fdiv(this.viewH, 2), 0, this.worldH - this.viewH); + this.camX = clampi(centerX - fdiv(this.viewW, 2), 0, this.worldW - this.viewW); + this.autoCam = false; + this.invalidate(); + } + + // ── Controls ──────────────────────────────────────────────────── + + pan(dy: number, dx: number): void { + this.autoCam = false; + // Scale pan speed with zoom: faster when zoomed out, slower when in + const factor = 2 ** -this.zoomLevel; + const sy = dy !== 0 ? Math.max(1, Math.trunc(Math.abs(dy) * factor)) * Math.sign(dy) : 0; + const sx = dx !== 0 ? Math.max(1, Math.trunc(Math.abs(dx) * factor)) * Math.sign(dx) : 0; + this.camY = clampi(this.camY + sy, 0, this.worldH - this.viewH); + this.camX = clampi(this.camX + sx, 0, this.worldW - this.viewW); + this.invalidate(); + } + + home(): void { + this.autoCam = true; + this.zoomLevel = 0; + this.invalidate(); + } + + clear(): void { + this.grid.fill(0); + this.age.fill(0); + this.ageSmooth.fill(0); + this.generation = 0; + this.popHistory = []; + this.hashHistory = []; + this.invalidate(); + } + + /** Run the pattern census and maybe queue a ticker sighting. */ + takeCensus(): void { + const vg = this.rawViewGrid(); + let any = false; + for (let i = 0; i < vg.data.length; i++) { + if (vg.data[i]) { + any = true; + break; + } + } + let counts = new Map(); + if (!any) { + this.lastCensusSites = []; + } else { + try { + const res = census(vg.data, vg.h, vg.w); + if (!res) { + this.lastCensusSites = []; + } else { + const [vy0, vx0] = this.clampCam(); // viewport origin in world coords + this.lastCensusSites = res.sites.map(([n, y, x]) => [n, vy0 + y, vx0 + x]); + counts = res.counts; + } + } catch { + // the census must never crash the sim + } + } + this.lastCensus = counts; + if (!counts.size) return; + + // Global cadence: at most one sighting per ~600 generations + if (this.generation - this.lastSightingGen < 600) return; + + let bestName = ''; + let bestScore = 0; + for (const [name, count] of counts) { + if (count < (SIGHTING_MIN_COUNT[name] ?? 1)) continue; + // Don't greet the same citizen again too soon + if (this.generation - (this.sightingGen.get(name) ?? -1e9) < 1800) continue; + const score = SIGHTING_RARITY[name] ?? 0; + if (score > bestScore) { + bestScore = score; + bestName = name; + } + } + if (!bestName) return; + + const pool = SIGHTING_MUSINGS[bestName] ?? []; + const idx = this.sightingCount.get(bestName) ?? 0; + this.sightingCount.set(bestName, idx + 1); + this.ticker.queueSpecial(pool[idx % pool.length] ?? bestName); + this.sightingGen.set(bestName, this.generation); + this.lastSightingGen = this.generation; + } + + // ── Persistence ───────────────────────────────────────────────── + // The universe is a place, not a session: state survives leaving. + + snapshot(): UniverseState { + return { + h: this.worldH, + w: this.worldW, + age: this.age.slice(), + generation: this.generation, + totalInjections: this.totalInjections, + }; + } + + /** + * Adopt a prior universe's state (resume or window resize). + * + * The old world is embedded centered; whatever doesn't fit is cropped. + * Derived state (smoothing, activity, histories) warms back up within a + * few dozen generations. + */ + adopt(s: UniverseState): void { + const h = Math.min(this.worldH, s.h); + const w = Math.min(this.worldW, s.w); + const sy = fdiv(s.h - h, 2); + const sx = fdiv(s.w - w, 2); + const dy = fdiv(this.worldH - h, 2); + const dx = fdiv(this.worldW - w, 2); + + this.grid.fill(0); + this.age.fill(0); + let pop = 0; + for (let y = 0; y < h; y++) { + for (let x = 0; x < w; x++) { + let a = s.age[(sy + y) * s.w + sx + x] ?? 0; + // Retire ghosts deeper than normal decay can reach (haunted-mode + // ages, or a future format change) — they'd be stuck forever + if (a < -GHOST_FRAMES) a = 0; + const i = (dy + y) * this.worldW + dx + x; + this.age[i] = a; + if (a > 0) { + this.grid[i] = 1; + pop++; + } + } + } + this.generation = Math.max(0, Math.trunc(s.generation)); + this.totalInjections = Math.max(0, Math.trunc(s.totalInjections)); + this.ageSmooth.fill(0); + this.activity.fill(0); + this.hashHistory = []; + this.cachedPop = pop; + this.popHistory = [pop]; + this.stepBbox = null; + this.dramaUntil = 0; + this.invalidate(); + } + + /** Toggle haunted mode (the resurrected ghost-decay bug). */ + toggleHaunted(): void { + this.haunted = !this.haunted; + this.invalidate(); + if (!this.haunted) { + // Exorcism: normal-mode decay can't retire ghosts older than + // -GHOST_FRAMES (they'd be stuck forever), so clear them all. + for (let i = 0; i < this.age.length; i++) { + if ((this.age[i] ?? 0) < 0) this.age[i] = 0; + if ((this.ageSmooth[i] ?? 0) < 0) this.ageSmooth[i] = 0; + } + } + } + + /** Map a terminal cell (row, col) to world coordinates, accounting for zoom. + * `half` picks the bottom pixel of the half-block cell. */ + termToWorld(termY: number, termX: number, half = 0): [number, number] { + const [cy, cx] = this.viewCenter(); + const dy = termY * 2 + half; // half-block: each term row = 2 display rows + const dx = termX; + if (this.zoomLevel <= 0) { + const factor = 1 << -this.zoomLevel; // 1, 2, 4 + const baseY = cy - fdiv(this.viewH * factor, 2); + const baseX = cx - fdiv(this.viewW * factor, 2); + return [baseY + dy * factor, baseX + dx * factor]; + } + const factor = 1 << this.zoomLevel; // 2, 4 + const baseY = cy - fdiv(Math.max(2, fdiv(this.viewH, factor)), 2); + const baseX = cx - fdiv(Math.max(2, fdiv(this.viewW, factor)), 2); + return [baseY + fdiv(dy, factor), baseX + fdiv(dx, factor)]; + } + + /** Toggle a cell at terminal coordinates, accounting for zoom. */ + toggleCell(termY: number, termX: number, half = 0): void { + const [gy, gx] = this.termToWorld(termY, termX, half); + this.setCell(gy, gx, this.grid[gy * this.worldW + gx] ? 0 : 1); + } + + /** Set a world cell alive (1) or dead (0), as a toggle would leave it. */ + setCell(gy: number, gx: number, v: 0 | 1): void { + if (gy < 0 || gy >= this.worldH || gx < 0 || gx >= this.worldW) return; + const i = gy * this.worldW + gx; + this.grid[i] = v; + this.age[i] = v ? 1 : 0; + this.invalidate(); + } + + /** Advance the news ticker by one frame. */ + tickTicker(tickerWidth: number): void { + this.ticker.tick(tickerWidth, this.detectMood(), this.generation); + } + + /** Classify current simulation mood for context-aware musings. + * Cached per generation — safe to call multiple times per frame. */ + detectMood(): string { + if (this.cachedMoodGen === this.generation) return this.cachedMood; + this.cachedMood = this.computeMood(); + this.cachedMoodGen = this.generation; + return this.cachedMood; + } + + private computeMood(): string { + // Haunted mode possesses everything, including the ticker + if (this.haunted) return 'haunted'; + + // Recent injection gets priority (show for ~90 frames after event) + if (this.lastEvent && this.generation - this.lastEventGen < 90) return 'injection'; + + // Cycle detected + if (this.cyclePeriod > 0) return 'cycle'; + + // Milestone (every 10,000 generations, show for 120 frames) + if (this.generation >= 10_000 && this.generation % 10_000 < 120) return 'milestone'; + + // Population trends (need enough history) + const ph = this.popHistory; + const phLen = ph.length; + if (phLen >= 30) { + let first = 0; + let second = 0; + for (let i = phLen - 30; i < phLen - 15; i++) first += ph[i] ?? 0; + for (let i = phLen - 15; i < phLen; i++) second += ph[i] ?? 0; + const ratio = second / 15 / Math.max(first / 15, 1); + if (ratio > 1.15) return 'booming'; + if (ratio < 0.85) return 'declining'; + } + + // Density + const pop = phLen ? (ph[phLen - 1] ?? 0) : 0; + const density = pop / Math.max(this.viewH * this.viewW, 1); + if (density > 0.15) return 'dense'; + if (pop < this.popFloor && pop > 0) return 'sparse'; + + // Stagnation + if (this.spread < Math.max(8, fdiv(this.popFloor, 8)) && phLen >= 150) return 'stagnant'; + + return ''; + } + + /** The current epoch name based on generation count. */ + epoch(): string { + let name = EPOCHS[0]?.[1] ?? 'genesis'; + for (const [threshold, label] of EPOCHS) if (this.generation >= threshold) name = label; + return name; + } + + population(): number { + return this.cachedPop; + } + + /** + * Horizontal centroid of viewport activity, 0.0 (left) – 1.0 (right). + * Feeds the music engine's stereo field: the arpeggio pans toward where + * the universe is actually changing. 0.5 when calm. + */ + activityCenterX(): number { + const [y0, x0] = this.clampCam(); + let total = 0; + let idx = 0; + for (let y = 0; y < this.viewH; y++) { + const row = (y0 + y) * this.worldW + x0; + for (let x = 0; x < this.viewW; x++) { + const a = this.activity[row + x] ?? 0; + total += a; + idx += a * x; + } + } + if (total < 1 || this.viewW < 2) return 0.5; + return idx / total / (this.viewW - 1); + } + + /** Total activity in the viewport (drama meter for time dilation). */ + viewportActivity(): number { + const [y0, x0] = this.clampCam(); + let total = 0; + for (let y = 0; y < this.viewH; y++) { + const row = (y0 + y) * this.worldW + x0; + for (let x = 0; x < this.viewW; x++) total += this.activity[row + x] ?? 0; + } + return total; + } + + /** + * Frame-delay factor: > 1 savors drama, < 1 hurries the calm. + * + * Compares viewport activity against its own long-run baseline (EMA), so + * "dramatic" is always relative to this universe's normal. Call once per + * frame; disabled while the user drives the camera. + */ + timeDilation(): number { + if (!this.autoCam) return 1; + const act = this.viewportActivity(); + if (this.actBaseline <= 0) { + this.actBaseline = Math.max(act, 1); + return 1; + } + // Asymmetric baseline: rises fast (a burst is savored briefly, then + // absorbed as the new normal), decays slow (a lull reads as fast-forward + // until it becomes the new normal). Drama is always a *change*, relative + // to this universe's own recent history. + const alpha = act > this.actBaseline ? 0.015 : 0.003; + this.actBaseline = this.actBaseline * (1 - alpha) + act * alpha; + const ratio = act / Math.max(this.actBaseline, 1); + let target = ratio > 1 ? Math.min(1.6, 1 + (ratio - 1) * 0.7) : 0.7 + ratio * 0.3; + if (act < 60) target = Math.min(target, 0.7); // near-dead viewport: don't linger + // Smooth the factor itself so tempo never jitters + this.dilation = this.dilation * 0.85 + target * 0.15; + return this.dilation; + } + + sparkline(width = 24): string { + const ph = this.popHistory; + if (ph.length < 2) return ''; + const start = Math.max(0, ph.length - width); + let lo = ph[start] ?? 0; + let hi = lo; + for (let i = start; i < ph.length; i++) { + const v = ph[i] ?? 0; + if (v < lo) lo = v; + if (v > hi) hi = v; + } + const n = SPARKS.length - 1; + const mid = SPARKS[Math.floor(SPARKS.length / 2)] ?? '▅'; + let out = ''; + for (let i = start; i < ph.length; i++) { + const v = ph[i] ?? 0; + out += hi === lo ? mid : (SPARKS[Math.trunc(((v - lo) / (hi - lo)) * n)] ?? ''); + } + return out; + } +} diff --git a/afterlife/src/engine/musings.ts b/afterlife/src/engine/musings.ts new file mode 100644 index 0000000..f410638 --- /dev/null +++ b/afterlife/src/engine/musings.ts @@ -0,0 +1,287 @@ +/** + * musings.ts — what the universe says about itself. + * + * Every string here is afterlife's own, carried over verbatim from life.py + * (generated from it, not retyped). The voice is the program's; the web port + * only moves it. + */ + +/** Default ambient musings (used when nothing special is happening) */ +export const MUSINGS: readonly string[] = [ + 'cells pondering existence', + 'entropy is just a suggestion', + 'order from chaos, chaos from order', + 'the cosmos breathes', + 'finding signal in noise', + 'patterns all the way down', + 'life, uh, finds a way', + 'on the edge of chaos', + 'complexity, emerging', + 'dancing at the phase boundary', + 'stillness is just slow motion', + 'every cell a universe', + 'the substrate computes', + 'from nothing, everything', + 'infinite in all directions', + 'what the dead cells dream', + 'the glider knows the way', + 'automata, contemplating', + 'structure wants to happen', + 'a cosmos in a terminal', + 'the math dreams itself', + 'somewhere, a glider remembers', + 'what is a pattern but frozen time', + 'the void is patient', + 'meaning is optional; beauty is not', + 'computation as meditation', + 'the rules are simple; the consequences infinite', + 'watching the watchers watch', + 'no cell is an island', + 'the ghost of configurations past', + 'asymmetry seeks symmetry seeks asymmetry', + 'this too shall iterate', + 'the universe has no pause button', + 'except when it does', + 'what would Conway think', + 'oscillators keep time for no one', + 'the glider gun knows not what it creates', + 'boundaries are suggestions', + 'death is just a state transition', + 'somewhere in here, a proof of universality', + 'the grid forgets nothing', + 'a thought experiment that thinks back', + 'topology has opinions', + 'the neighborhood watches', + 'not alive, not dead — computing', + 'four rules, one universe', + 'each frame a theorem', + 'proof by existence', + 'the only constant is the ruleset', + 'somewhere between on and off, meaning', + 'the dead outnumber the living, as always', + "conway's little infinities", + "the simulation doesn't know it's beautiful", + '27 neighbors ago, this was empty', + 'elegance is compression', + 'the grid is the message', + 'a love letter to discrete math', + 'listen — the cells are whispering', + 'we are all gliders, briefly', +]; + +/** Context-sensitive musings keyed by simulation state */ +export const MUSINGS_BY_STATE: Readonly> = { + booming: [ + 'runaway growth', + 'the bloom unfolds', + 'life begets life begets life', + 'exponential daydreams', + 'more is different', + 'the bloom cannot be stopped', + 'abundance as instability', + 'growing into the unknown', + 'every birth a cascade', + 'the simulation smiles', + 'lebensraum', + 'mitosis dreams', + 'the substrate strains', + "a spring that won't stop springing", + 'cells all the way to the horizon', + 'the algorithm is generous today', + 'more, more, more', + 'genesis on fast-forward', + ], + declining: [ + 'entropy collects its due', + 'the long exhale', + 'even stars go dark', + 'returning to the void', + 'graceful unwinding', + 'the great filter, in miniature', + 'less is not nothing', + 'the long forgetting', + 'what rises must', + 'simplicity returns', + 'the cells remember fullness', + 'autumn in the grid', + 'graceful subtraction', + 'the tide goes out', + 'a slow dissolve', + 'the universe exhales', + 'dimming, not vanishing', + 'the population curve bends earthward', + ], + cycle: [ + 'deja vu, deja vu, deja vu', + 'stuck in a loop', + 'the eternal return', + 'ouroboros', + 'time is a flat circle', + 'we have been here before', + 'the loop remembers itself', + 'stability through repetition', + 'a prayer, repeating', + 'the wheel turns', + 'same as it ever was', + 'history rhymes', + 'the attractor holds', + 'a fixed point in phase space', + 'nietzsche was right about this part', + 'the universe stutters beautifully', + 'clockwork, but organic', + 'breathing in, breathing out', + ], + stagnant: [ + 'the hush before the storm', + 'equilibrium is boring', + 'waiting for a perturbation', + 'still waters', + 'the universe holds its breath', + 'the calm before', + 'potential energy', + 'stillness is also motion', + 'the system waits', + 'one perturbation away', + 'dormant, not dead', + 'crystallized', + 'the peace of equilibrium', + 'a held breath', + 'the grid meditates', + 'tension, frozen', + 'the quiet hum of nothing happening', + 'patience is also a computation', + ], + sparse: [ + 'the last few embers', + 'clinging to existence', + 'from little things, big things grow', + 'seeds in the dark', + 'quiet, but not empty', + 'lonely structures', + 'the survivors', + 'space between stars', + 'minimalism, enforced', + 'room to breathe', + 'the few, the proud', + 'embers', + 'a whisper, not a shout', + 'the geometry of loneliness', + 'each cell precious now', + 'a constellation, if you squint', + 'small, but not nothing', + ], + dense: [ + 'teeming', + 'a city that never sleeps', + 'standing room only', + 'too many neighbours', + 'the crowd murmurs', + 'no room to think', + 'the crush of neighbors', + 'overpopulation is self-correcting', + 'too much life', + 'suffocating in company', + 'the grid groans', + 'rush hour', + 'sardines', + 'every cell has an opinion', + 'a metropolis, thrumming', + 'the carrying capacity protests', + 'elbow room is a luxury', + ], + injection: [ + 'a gift from beyond the edge', + 'new visitors', + 'reinforcements arrive', + 'the cosmos provides', + 'seeded by unseen hands', + 'deus ex machina', + 'a nudge from outside', + 'fresh blood', + 'the invisible hand places', + 'salvation arrives', + 'new variables enter', + 'the cosmos intervenes', + 'help from beyond the viewport', + 'strangers in a strange land', + 'the petri dish gets a refill', + 'immigrants, welcome', + ], + milestone: [ + 'another thousand turns of the wheel', + 'the odometer clicks over', + 'still here, still going', + 'persistence is its own reward', + 'ten thousand steps', + 'the long now', + 'time is a flat circle, but longer', + 'we persist', + 'generations beyond counting', + 'deep in the run', + 'the odometer means nothing to the cells', + 'and yet, it continues', + 'older than some civilizations', + 'the marathon nobody entered', + "a digit rolls over; the grid doesn't notice", + 'epochal', + ], + haunted: [ + 'the dead outnumber the living, and glow', + 'every path ever walked, still visible', + 'the void remembers being alive', + 'phase-contrast photography of the past', + 'nothing is ever really deleted', + 'the grid forgets nothing — literally, now', + 'a bug so beautiful it became a feature', + 'the ghosts have synchronized their breathing', + 'somewhere between pair 463 and 504', + 'resurrection as a rendering artifact', + 'the afterlife was here all along', + 'history, strobing', + 'every empty cell, a metronome', + 'the universe develops like a photograph', + 'magenta is the color of memory', + ], +}; + +/** What the ticker says on a sighting. Voice matches the ambient musings. */ +export const SIGHTING_MUSINGS: Readonly> = { + glider: ['a glider, going somewhere', 'lightspeed, quartered — a glider', 'a glider carries the news', 'diagonal traffic'], + lwss: ['a lightweight spaceship, outbound', 'small craft advisory'], + hwss: ['a heavy spaceship shoulders past', 'heavy freight on the grid'], + pulsar: ['a pulsar, keeping time for no one', 'the pulsar breathes in threes'], + pentadecathlon: ['fifteen beats to the bar', 'a pentadecathlon runs its laps'], + blinker: ['the blinkers keep the beat', 'metronomes, disagreeing'], + block: ['blocks: the simplest memory', 'still life with squares'], + beehive: ['the bees are home', 'a beehive, sealed'], + loaf: ['bread, uneaten', 'a loaf, going stale beautifully'], + boat: ['a boat with nowhere to sail'], + tub: ['a tub, holding nothing'], + ship: ['a ship without a sea'], + pond: ['a pond, perfectly still'], + toad: ['a toad, breathing'], + beacon: ['the beacon still signals', 'someone left the light on'], +}; + +/** + * Rarity score: which sighting wins when several are present, and how many + * must be visible before the common ones are worth mentioning. + */ +export const SIGHTING_RARITY: Readonly> = { + pulsar: 100, pentadecathlon: 100, hwss: 90, lwss: 80, glider: 40, beacon: 30, toad: 28, pond: 24, ship: 20, loaf: 16, beehive: 12, boat: 12, tub: 12, blinker: 8, block: 6, +}; + +/** Patterns this common only get announced in groups (texts are plural) */ +export const SIGHTING_MIN_COUNT: Readonly> = { blinker: 3, block: 3 }; + +/** Named phases based on generation count — gives a sense of deep time */ +export const EPOCHS: readonly (readonly [number, string])[] = [ + [0, 'genesis'], + [500, 'primordial'], + [2000, 'emergence'], + [10000, 'expansion'], + [50000, 'flourishing'], + [150000, 'deep time'], + [500000, 'eon'], + [1000000, 'eternity'], +]; diff --git a/afterlife/src/engine/persist.ts b/afterlife/src/engine/persist.ts new file mode 100644 index 0000000..5f5cb8c --- /dev/null +++ b/afterlife/src/engine/persist.ts @@ -0,0 +1,154 @@ +/** + * persist.ts — the universe is a place, not a session. + * + * life.py saves universe.npz on quit (and every AUTOSAVE_EVERY generations) + * and resumes it on launch. Here the place is this origin's localStorage, + * which is also how oikos's screen can show *your* universe. + * + * The npz held grid + age; the grid is exactly `age > 0`, so only ages are + * kept, sparsely: a varint gap to the next living-or-ghost cell, then its + * age (zigzag). Saving has to be synchronous — the page is often already on + * its way out when it happens — so it can't lean on CompressionStream; a + * busy universe of ~20k cells comes to ~70 kB of text. + */ + +import { GHOST_FRAMES } from './constants'; +import type { UniverseState } from './life'; + +export const KEY = 'afterlife-universe-v1'; + +export interface SaveFile extends UniverseState { + /** epoch ms of this universe's genesis */ + bornAt: number; + /** epoch ms of the save */ + savedAt: number; +} + +interface Stored { + v: 1; + h: number; + w: number; + generation: number; + totalInjections: number; + bornAt: number; + savedAt: number; + cells: string; +} + +/** Past this, a save is skipped rather than risk the origin's quota (syrinx lives here too). */ +const MAX_CHARS = 1_500_000; + +export function encodeAges(age: Int32Array): string { + const bytes: number[] = []; + const varint = (v: number): void => { + while (v > 0x7f) { + bytes.push((v & 0x7f) | 0x80); + v = Math.floor(v / 128); + } + bytes.push(v); + }; + let prev = -1; + for (let i = 0; i < age.length; i++) { + const a = age[i] ?? 0; + // ghosts deeper than normal decay reaches are retired on adopt anyway; + // haunted mode makes nearly every cell one, and would blow the cap + if (a === 0 || a < -GHOST_FRAMES) continue; + varint(i - prev - 1); + varint(a >= 0 ? a * 2 : -a * 2 - 1); + prev = i; + } + let bin = ''; + for (let i = 0; i < bytes.length; i += 0x8000) { + bin += String.fromCharCode(...bytes.slice(i, i + 0x8000)); + } + return btoa(bin); +} + +export function decodeAges(b64: string, n: number): Int32Array { + const bin = atob(b64); + const age = new Int32Array(n); + let p = 0; + const varint = (): number => { + let v = 0; + let mul = 1; + for (;;) { + if (p >= bin.length) throw new Error('afterlife: truncated save'); + const b = bin.charCodeAt(p++); + v += (b & 0x7f) * mul; + if (b < 0x80) return v; + mul *= 128; + } + }; + let i = -1; + while (p < bin.length) { + i += varint() + 1; + const z = varint(); + if (i >= n) throw new Error('afterlife: save does not fit its own world'); + age[i] = z % 2 ? -(z + 1) / 2 : z / 2; + } + return age; +} + +export function load(storage?: Storage): SaveFile | null { + try { + // reading localStorage itself throws where site data is blocked + const raw = (storage ?? globalThis.localStorage)?.getItem(KEY); + if (!raw) return null; + const s = JSON.parse(raw) as Partial; + if (s.v !== 1 || !Number.isInteger(s.h) || !Number.isInteger(s.w) || typeof s.cells !== 'string') return null; + const h = s.h as number; + const w = s.w as number; + if (h <= 0 || w <= 0 || h * w > 16_000_000) return null; + const generation = Number(s.generation); + if (!Number.isSafeInteger(generation) || generation < 0) return null; + return { + h, + w, + age: decodeAges(s.cells, h * w), + generation, + totalInjections: Number(s.totalInjections) || 0, + bornAt: Number(s.bornAt) || Date.now(), + savedAt: Number(s.savedAt) || Date.now(), + }; + } catch (err) { + // corrupt or foreign save → a fresh universe + console.warn('afterlife: could not load the universe, starting fresh', err); + return null; + } +} + +/** Save atomically (one setItem). Returns true on success. */ +export function save(file: SaveFile, storage?: Storage): boolean { + try { + const store = storage ?? globalThis.localStorage; + const stored: Stored = { + v: 1, + h: file.h, + w: file.w, + generation: file.generation, + totalInjections: file.totalInjections, + bornAt: file.bornAt, + savedAt: file.savedAt, + cells: encodeAges(file.age), + }; + const text = JSON.stringify(stored); + if (text.length > MAX_CHARS) { + console.warn(`afterlife: the universe is too big to keep (${text.length} chars)`); + return false; + } + if (!store) return false; + store.setItem(KEY, text); + return true; + } catch (err) { + console.warn('afterlife: could not save the universe', err); + return false; + } +} + +export function forget(storage?: Storage): void { + try { + (storage ?? globalThis.localStorage)?.removeItem(KEY); + } catch { + /* fine */ + } +} diff --git a/afterlife/src/engine/stats.ts b/afterlife/src/engine/stats.ts new file mode 100644 index 0000000..c1ced78 --- /dev/null +++ b/afterlife/src/engine/stats.ts @@ -0,0 +1,43 @@ +/** + * stats.ts — engine telemetry, for post-hoc tuning. + * + * life.py always logs to life_stats.csv beside itself. A page has no "beside + * itself", so the log is kept in memory (the newest MAX_LINES rows) and the + * keys panel offers it as a download under the same name and header. + */ + +const HEADER = 'gen,time_s,total_pop,spread_150,pop_floor,cycle_period,zoom,cam_y,cam_x,event\n'; +const MAX_LINES = 50_000; + +export interface StatsRow { + gen: number; + pop: number; + spread: number; + floor: number; + cycle: number; + zoom: number; + camY: number; + camX: number; + event?: string; +} + +export class StatsLogger { + private lines: string[] = []; + private readonly t0 = performance.now(); + + log(r: StatsRow): void { + const t = (performance.now() - this.t0) / 1000; + this.lines.push( + `${r.gen},${t.toFixed(1)},${r.pop},${r.spread},${r.floor},${r.cycle},${r.zoom},${r.camY},${r.camX},${r.event ?? ''}\n`, + ); + if (this.lines.length > MAX_LINES * 1.2) this.lines = this.lines.slice(-MAX_LINES); + } + + get length(): number { + return this.lines.length; + } + + csv(): string { + return HEADER + this.lines.join(''); + } +} diff --git a/afterlife/src/engine/ticker.ts b/afterlife/src/engine/ticker.ts new file mode 100644 index 0000000..6af232a --- /dev/null +++ b/afterlife/src/engine/ticker.ts @@ -0,0 +1,101 @@ +/** + * ticker.ts — the news ticker. + * + * A scrolling marquee of musings that drift across the status bar. Messages + * spawn at the right edge and scroll left. Context-aware musings are spawned + * when the simulation mood changes; ambient musings fill the gaps in between. + * + * Positions are in terminal columns, as in life.py: the renderer decides how + * wide a column is. + */ + +import { MUSINGS, MUSINGS_BY_STATE } from './musings'; + +export interface TickerMessage { + text: string; + /** position in ticker-space (0 = left edge, positive = right) */ + x: number; +} + +/** A string's width in terminal columns (every glyph here is one column). */ +export const cols = (s: string): number => [...s].length; + +export class NewsTicker { + messages: TickerMessage[] = []; + private scrollSpeed = 0.4; // chars per frame + private spawnCooldown = 0; // frames until next spawn allowed + private minGap = 12; // min chars between messages + private ambientInterval = 60; // frames between ambient spawns + private moodInterval = 50; // frames between mood spawns + private ambientIdx = Math.floor(Math.random() * MUSINGS.length); + private lastMood = ''; + private pendingSpecial = ''; + + /** Queue a priority message (e.g. a pattern sighting). It spawns as soon + * as there's physical room, ahead of the regular pools. */ + queueSpecial(text: string): void { + this.pendingSpecial = text; + } + + /** Advance one frame: scroll, cull off-screen messages, maybe spawn. */ + tick(tickerWidth: number, mood: string, generation: number): void { + if (tickerWidth < 10) return; + + // Scroll every message to the left + for (const msg of this.messages) msg.x -= this.scrollSpeed; + + // Remove messages that have fully left the visible area + this.messages = this.messages.filter((m) => m.x + cols(m.text) > 0); + + // Special sightings jump the queue (only physical room gates them) + if (this.pendingSpecial && this.canSpawn(tickerWidth)) { + this.messages.push({ text: this.pendingSpecial, x: tickerWidth }); + this.pendingSpecial = ''; + this.spawnCooldown = Math.max(this.spawnCooldown, this.moodInterval); + return; + } + + const moodPool = mood ? MUSINGS_BY_STATE[mood] : undefined; + + // On mood change, allow a contextual message to appear quickly + if (moodPool && mood !== this.lastMood) { + this.spawnCooldown = Math.min(this.spawnCooldown, 15); + } + + // Count down the spawn timer + this.spawnCooldown = Math.max(0, this.spawnCooldown - 1); + if (this.spawnCooldown > 0) return; + + // Check physical room at the right edge + if (!this.canSpawn(tickerWidth)) return; + + // Pick a message based on mood + let text: string; + let cooldown: number; + if (moodPool) { + text = moodPool[Math.floor(generation / 180) % moodPool.length] ?? ''; + cooldown = this.moodInterval; + } else { + text = MUSINGS[this.ambientIdx] ?? ''; + this.ambientIdx = (this.ambientIdx + 1) % MUSINGS.length; + cooldown = this.ambientInterval; + } + + this.messages.push({ text, x: tickerWidth }); + this.spawnCooldown = cooldown; + this.lastMood = mood; + } + + /** True if the right edge is clear enough for a new message. */ + private canSpawn(tickerWidth: number): boolean { + if (!this.messages.length) return true; + let right = -Infinity; + for (const m of this.messages) right = Math.max(right, m.x + cols(m.text)); + return right < tickerWidth - this.minGap; + } + + /** The ticker breathes: dim on the out-breath, as the terminal's A_DIM. */ + static dim(generation: number): boolean { + return Math.sin(generation * 0.035) < 0; + } +} diff --git a/afterlife/src/main.ts b/afterlife/src/main.ts new file mode 100644 index 0000000..28d27b3 --- /dev/null +++ b/afterlife/src/main.ts @@ -0,0 +1,653 @@ +/** + * afterlife — the game of life, after hours. + * + * A web port of afterlife (github.com/LuxiaSL/afterlife), the terminal + * screensaver: Conway's Life on an infinite toroidal world far larger than + * the screen, with a camera that steers toward the drama, a dramaturge that + * stages collisions, a ticker of musings, a census that greets famous + * citizens, generative music, and haunted mode. This file is life.py's + * main(): the loop, the keys, and the vessel (a drawn as a + * terminal, which is what afterlife was painted on). + * + * The loop keeps the terminal's timing: one frame is input → step → music → + * auto-focus → census → autosave → log → render, and the next one comes + * `delay × time_dilation` ms after it. Frames run on requestAnimationFrame, + * so a hidden tab stops the universe instead of stepping it blind. + */ + +import './style.css'; +import { AUTOSAVE_EVERY, GHOST_FRAMES } from './engine/constants'; +import { InfiniteLife } from './engine/life'; +import { KEY, load, save } from './engine/persist'; +import { StatsLogger } from './engine/stats'; +import { Music } from './music/audio'; +import type { SimulationSnapshot } from './music/engine'; +import { Terminal } from './term/render'; + +const root = document.getElementById('afterlife-root'); +const canvas = document.getElementById('term') as HTMLCanvasElement | null; +const splash = document.getElementById('splash'); +const keysPanel = document.getElementById('keys'); +const ctx = canvas?.getContext('2d', { alpha: false }); +if (!root || !canvas || !ctx) throw new Error('afterlife: the page is missing its terminal'); + +// Inside an oikos screen the page shows your universe but never writes it: +// the /afterlife tab (or nobody) is its keeper. +const embedded = window.top !== window; +const term = new Terminal(ctx); +const music = new Music(); +const logger = new StatsLogger(); + +let life: InfiniteLife; +let bornAt = Date.now(); +let showStats = false; + +// Playhead state for music scanning +let playheadCol = 0; + +// Snapshot recording state (toggled with 'd'): kept in memory, downloaded on stop. +// Capped (~20 min at 15 snapshots/s) so a forgotten recording can't eat the tab. +const MAX_RECORDED = 18_000; +let recording = false; +let recorded: string[] = []; + +// ── The vessel ─────────────────────────────────────────────────────────── + +/** CSS pixels per column: a terminal's cell, give or take the screen. */ +function cellCss(): number { + const w = window.innerWidth; + if (w >= 1100) return 8; + if (w >= 700) return 7; + return 6; +} + +function fitTerminal(): void { + const dpr = Math.min(window.devicePixelRatio || 1, 3); + const cw = Math.max(2, Math.round(cellCss() * dpr)); + term.resize(Math.floor(window.innerWidth * dpr), Math.floor(window.innerHeight * dpr), cw); + const pw = term.cols * term.cw; + const ph = term.rows * term.cw * 2; + canvas!.width = pw; + canvas!.height = ph; + canvas!.style.width = `${pw / dpr}px`; + canvas!.style.height = `${ph / dpr}px`; +} + +/** Refit the canvas; a new world only when the grid itself changed. */ +function refit(): void { + const rows = term.gridRows; + const cols = term.cols; + fitTerminal(); + // a phone's URL bar nudges the height by a few pixels as it scrolls away; + // that's not a resize the universe needs to hear about + if (term.gridRows !== rows || term.cols !== cols) rebuild(); +} + +/** + * Resize the vessel, keep the universe (life.py's KEY_RESIZE). The old + * world is the new one's floor, so a smaller window never crops it, and the + * session (pause, speed, zoom, camera, modes, ticker) carries across. + */ +function rebuild(): void { + const old = life; + const [cy, cx] = old.viewCenter(); + life = new InfiniteLife(term.gridRows, term.cols, false, { minH: old.worldH, minW: old.worldW }); + life.adopt(old.snapshot()); + // adopt() centres the old world in the new; the camera follows it there + const dy = Math.floor((life.worldH - old.worldH) / 2); + const dx = Math.floor((life.worldW - old.worldW) / 2); + if (old.haunted) { + // adopt() retires ghosts deeper than normal decay reaches; in haunted + // mode those are the point, and a resize shouldn't exorcise them + for (let y = 0; y < old.worldH; y++) { + for (let x = 0; x < old.worldW; x++) { + const a = old.age[y * old.worldW + x] ?? 0; + if (a < -GHOST_FRAMES) life.age[(dy + y) * life.worldW + dx + x] = a; + } + } + } + life.camY = Math.max(0, Math.min(cy + dy - Math.floor(life.viewH / 2), life.worldH - life.viewH)); + life.camX = Math.max(0, Math.min(cx + dx - Math.floor(life.viewW / 2), life.worldW - life.viewW)); + life.haunted = old.haunted; + life.paused = old.paused; + life.delay = old.delay; + life.zoomLevel = old.zoomLevel; + life.autoCam = old.autoCam; + life.autoFocusMode = old.autoFocusMode; + life.autoFocusFrame = old.autoFocusFrame; + life.dilation = old.dilation; + life.ticker = old.ticker; + // world-wide telemetry: nothing was cropped, so it's all still true + life.popHistory = old.popHistory.slice(); + life.spread = old.spread; + life.popFloor = old.popFloor; + life.cyclePeriod = old.cyclePeriod; + life.lastEvent = old.lastEvent; + life.lastEventGen = old.lastEventGen; + playheadCol = 0; +} + +let resizeTimer = 0; +function scheduleRefit(): void { + clearTimeout(resizeTimer); + resizeTimer = window.setTimeout(() => { + refit(); + paint(); + }, 200); +} + +// ── Keeping the universe, politely ─────────────────────────────────────── +// Every tab on this origin saves to the same key. The savedAt of the save we +// loaded or last wrote is our mark: a stored save with another mark and a +// later generation was written by someone else, and isn't ours to clobber. + +let ourSavedAt = 0; +let yielded = false; + +/** savedAt + generation of the stored save, without decoding its cells. */ +function storedHeader(raw: string | null): { savedAt: number; generation: number } | null { + if (!raw) return null; + // the small fields come before `cells` (see persist.ts's Stored), so the + // head of the string is enough + const head = raw.slice(0, 400); + const savedAt = /"savedAt":(\d+)/.exec(head)?.[1]; + const generation = /"generation":(\d+)/.exec(head)?.[1]; + if (savedAt === undefined || generation === undefined) return null; + return { savedAt: Number(savedAt), generation: Number(generation) }; +} + +function someoneElsesIsNewer(raw: string | null): boolean { + const h = storedHeader(raw); + return h !== null && h.savedAt !== ourSavedAt && h.generation > life.generation; +} + +function yieldTo(): void { + if (yielded) return; + yielded = true; + console.info('afterlife: another window has saved a later universe; this one will not overwrite it'); + life.ticker.queueSpecial('another window keeps this universe now'); +} + +function persist(): void { + if (!life || embedded) return; + try { + if (someoneElsesIsNewer(localStorage.getItem(KEY))) { + yieldTo(); + return; + } + } catch { + // storage blocked: save() will say so + } + const savedAt = Date.now(); + if (save({ ...life.snapshot(), bornAt, savedAt })) ourSavedAt = savedAt; +} + +window.addEventListener('storage', (e) => { + if (e.key === KEY && !embedded && someoneElsesIsNewer(e.newValue)) yieldTo(); +}); + +// ── Genesis, or the universe remembers ─────────────────────────────────── + +fitTerminal(); +const saved = load(); +if (saved) { + // the saved world is the floor: a smaller screen than last time never crops it + life = new InfiniteLife(term.gridRows, term.cols, false, { minH: saved.h, minW: saved.w }); + life.adopt(saved); + bornAt = saved.bornAt; + ourSavedAt = saved.savedAt; + life.ticker.queueSpecial(`the universe remembers generation ${life.generation.toLocaleString('en-US')}`); +} else { + life = new InfiniteLife(term.gridRows, term.cols); +} + +// ── One frame of the terminal's loop ───────────────────────────────────── + +function frame(): void { + // ── Simulate ── + const event = life.step(); + + // ── Music update (every 2nd frame — audio smooths transitions) ── + if (music.running && !life.paused && life.generation % 2 === 0) { + try { + const viewW = life.viewW; + // Sweep speed: one full scan in ~8 seconds at ~30fps + playheadCol = (playheadCol + Math.max(1, Math.floor(viewW / 240))) % Math.max(1, viewW); + const disp = life.displayGrid(); + const column: boolean[] = []; + if (playheadCol < disp.w) for (let y = 0; y < disp.h; y++) column.push(disp.data[y * disp.w + playheadCol] === 1); + const ph = life.popHistory; + const pop = life.population(); + const snap: SimulationSnapshot = { + generation: life.generation, + population: pop, + pop_floor: life.popFloor, + density: pop / Math.max(1, life.viewH * life.viewW), + spread: life.spread, + cycle_period: life.cyclePeriod, + mood: life.detectMood(), + epoch: life.epoch(), + pop_delta: ph.length >= 2 ? (ph[ph.length - 1] ?? 0) - (ph[ph.length - 2] ?? 0) : 0, + playhead_column: column, + playhead_position: playheadCol / Math.max(1, viewW), + viewport_rows: disp.h, + event, + activity_x: life.activityCenterX(), + }; + music.update(snap); + if (recording) { + recorded.push(JSON.stringify({ ...snap, playhead_column: column.map((b) => (b ? 1 : 0)) })); + if (recorded.length > MAX_RECORDED) { + // keep the latest stretch; the oldest go + if (recorded.length === MAX_RECORDED + 1) life.ticker.queueSpecial('the recording is full; its oldest moments are letting go'); + recorded.splice(0, recorded.length - MAX_RECORDED); + } + } + } catch { + // Never let music crash the sim + } + } + + // ── Auto-focus mode (every other frame) ── + if (life.autoFocusMode && ++life.autoFocusFrame >= 2) { + life.autoFocusFrame = 0; + life.autoFocus(); + } + + const running = !life.paused && life.generation > 0; + // ── Pattern census (periodic, cheap, never critical) ── + if (running && life.generation % 150 === 0) life.takeCensus(); + // ── Autosave (insurance; the main save happens on the way out) ── + if (running && life.generation % AUTOSAVE_EVERY === 0) persist(); + + // ── Log ── + if (event || life.generation % 10 === 0) { + logger.log({ + gen: life.generation, pop: life.population(), spread: life.spread, floor: life.popFloor, + cycle: life.cyclePeriod, zoom: life.zoomLevel, camY: life.camY, camX: life.camX, event, + }); + } + + // ── The ticker moves once a frame, as render() moves it ── + const st = term.status(life, musicStatus()); + if (st.tickerWidth >= 10) life.tickTicker(st.tickerWidth); +} + +function musicStatus(): string { + let s = music.statusString(); + if (recording) s += ` REC(${recorded.length})`; + return s; +} + +function paint(): void { + term.render(life, term.status(life, musicStatus()), showStats); +} + +let nextAt = 0; +let raf = 0; + +/** Past this much work in one paint, stop and let input and the paint through. */ +const FRAME_BUDGET_MS = 12; + +function loop(now: number): void { + raf = requestAnimationFrame(loop); + const started = performance.now(); + let ran = 0; + // Time breathes: savor catastrophes, hurry through the calm. A frame is due + // delay × dilation after the last one; a fast setting may owe a few per paint. + // Half a vsync early counts as on time, or a 35 ms frame would wait for 50. + while (now >= nextAt - 8) { + frame(); + ran++; + nextAt = Math.max(nextAt, now - 100) + life.delay * life.timeDilation(); + if (ran >= 4 || performance.now() - started > FRAME_BUDGET_MS) { + // a big screen can't keep this pace: drop what's owed rather than catch + // up, or every paint would stall the keys for the whole backlog + nextAt = Math.max(nextAt, now); + break; + } + } + if (ran) paint(); +} + +function startLoop(): void { + if (raf) return; + nextAt = performance.now(); + raf = requestAnimationFrame(loop); +} + +function stopLoop(): void { + cancelAnimationFrame(raf); + raf = 0; +} + +// ── Keys ───────────────────────────────────────────────────────────────── + +function download(name: string, text: string, type: string): void { + const url = URL.createObjectURL(new Blob([text], { type })); + const a = Object.assign(document.createElement('a'), { href: url, download: name }); + document.body.append(a); + a.click(); + a.remove(); + setTimeout(() => URL.revokeObjectURL(url), 1000); +} + +function leave(): void { + if (embedded) { + // there is no leaving from inside a screen in oikos; ⏏ is out there + life.ticker.queueSpecial('the glass holds; eject from the other side'); + return; + } + persist(); + const ref = document.referrer; + const back = ref && new URL(ref, location.href).origin === location.origin && !ref.includes('/afterlife') ? ref : '/'; + location.href = back; +} + +/** Every key the terminal knew, by name; the keys panel calls these too. */ +const ACTIONS: Record void> = { + quit: leave, + reseed: () => { + const wasHaunted = life.haunted; + life = new InfiniteLife(term.gridRows, term.cols); + life.haunted = wasHaunted; + bornAt = Date.now(); + playheadCol = 0; + }, + pause: () => { + life.paused = !life.paused; + }, + faster: () => { + life.delay = Math.max(10, life.delay - 10); + }, + slower: () => { + life.delay = Math.min(500, life.delay + 10); + }, + home: () => life.home(), + clear: () => life.clear(), + zoomOut: () => life.zoomOut(), + zoomIn: () => life.zoomIn(), + focus: () => { + life.autoFocusMode = !life.autoFocusMode; + if (life.autoFocusMode) life.autoFocus(); // Immediate first focus + }, + haunt: () => life.toggleHaunted(), + stats: () => { + showStats = !showStats; + }, + record: () => { + // Snapshot recording for music diagnostics (life_music_diag.py --replay) + if (!music.running) { + life.ticker.queueSpecial('nothing to record without the music'); + return; + } + recording = !recording; + if (recording) recorded = []; + else if (recorded.length) download('snapshots.jsonl', `${recorded.join('\n')}\n`, 'application/x-ndjson'); + // snapshots come every other generation, and none while paused + else life.ticker.queueSpecial('the recording caught nothing; no snapshots to keep'); + }, + mute: () => music.running && music.toggleMute(), + style: () => music.running && music.cycleStyle(), + volDown: () => music.running && music.adjustVolume(-0.1), + volUp: () => music.running && music.adjustVolume(0.1), + up: () => life.pan(-5, 0), + down: () => life.pan(5, 0), + left: () => life.pan(0, -10), + right: () => life.pan(0, 10), + statsCsv: () => download('life_stats.csv', logger.csv(), 'text/csv'), + help: () => toggleKeys(), +}; + +const KEYMAP: Record = { + q: 'quit', r: 'reseed', ' ': 'pause', '+': 'faster', '=': 'faster', '-': 'slower', _: 'slower', + h: 'home', c: 'clear', z: 'zoomOut', x: 'zoomIn', f: 'focus', g: 'haunt', s: 'stats', d: 'record', + m: 'mute', v: 'style', '[': 'volDown', ']': 'volUp', + ArrowUp: 'up', ArrowDown: 'down', ArrowLeft: 'left', ArrowRight: 'right', '?': 'help', +}; + +/** Held keys repeat only where more of the same is the point (speed, volume, pan, zoom). */ +const REPEATS = new Set(['faster', 'slower', 'volDown', 'volUp', 'up', 'down', 'left', 'right', 'zoomOut', 'zoomIn']); + +function act(name: string): void { + ACTIONS[name]?.(); + paint(); +} + +window.addEventListener('keydown', (e) => { + if (e.ctrlKey || e.metaKey || e.altKey) return; + if (e.key === 'Escape') { + // closing a panel isn't entering: no music for it + if (keysPanel && !keysPanel.hidden) toggleKeys(false); + return; + } + const name = KEYMAP[e.key.length === 1 ? e.key.toLowerCase() : e.key] ?? KEYMAP[e.key]; + if (!name) return; + e.preventDefault(); + if (e.repeat && !REPEATS.has(name)) return; + // reading the keys isn't entering either + if (name !== 'help') wake(); + act(name); +}); + +// ── The splash, and the gesture that lets the music start ────────────── + +let awake = false; +function wake(): void { + if (!awake) { + awake = true; + splash?.classList.add('gone'); + setTimeout(() => splash?.remove(), 1600); + } + void music.start(); +} +splash?.addEventListener('pointerdown', (e) => { + e.preventDefault(); + e.stopPropagation(); + wake(); +}); +splash?.addEventListener('keydown', (e) => { + // it's a button: Enter or Space on it enters, and doesn't also pause + if (e.key !== 'Enter' && e.key !== ' ') return; + e.preventDefault(); + e.stopPropagation(); + if (!e.repeat) wake(); +}); +// iOS may unlock audio only as a gesture ends, not as it starts; once the +// visitor is in, every tap's end gets a chance to (re)start the music +for (const type of ['pointerup', 'click'] as const) { + root.addEventListener(type, () => { + if (awake) void music.start(); + }); +} + +function toggleKeys(show = keysPanel?.hidden ?? false): void { + if (!keysPanel) return; + keysPanel.hidden = !show; + document.getElementById('keys-toggle')?.setAttribute('aria-expanded', String(show)); +} +keysPanel?.addEventListener('click', (e) => { + const el = (e.target as HTMLElement).closest('[data-act]'); + if (!el) return; + e.preventDefault(); + wake(); + act(el.dataset.act ?? ''); +}); +document.getElementById('keys-toggle')?.addEventListener('click', (e) => { + // like ?, opening the keys doesn't start the music + e.preventDefault(); + toggleKeys(); +}); + +// ── Mouse and touch: toggle cells, and a little more than a terminal had ─ + +/** canvas point → [terminal row, column, bottom half?] */ +function cellAt(e: { clientX: number; clientY: number }): [number, number, number] { + const r = canvas!.getBoundingClientRect(); + const cssCw = r.width / term.cols; + const col = Math.floor((e.clientX - r.left) / cssCw); + const px = Math.floor((e.clientY - r.top) / cssCw); // half-block pixel row + return [Math.floor(px / 2), col, px % 2]; +} + +/** Pointer capture keeps a drag coming from outside the canvas; those aren't cells on screen. */ +function onGrid(row: number, col: number): boolean { + return row >= 0 && row < term.gridRows && col >= 0 && col < term.cols; +} + +interface Drag { + id: number; + kind: 'paint' | 'pan'; + x: number; + y: number; + last: string; + moved: boolean; +} +let drag: Drag | null = null; +const touches = new Map(); +let pinch = 0; + +canvas.addEventListener('contextmenu', (e) => e.preventDefault()); +canvas.addEventListener('pointerdown', (e) => { + wake(); + touches.set(e.pointerId, { x: e.clientX, y: e.clientY }); + if (touches.size >= 2) { + // two fingers: pinch to zoom, never paint (nor does a third) + if (touches.size === 2) { + const [a, b] = [...touches.values()]; + pinch = a && b ? Math.hypot(a.x - b.x, a.y - b.y) : 0; + } + drag = null; + return; + } + canvas.setPointerCapture(e.pointerId); + const pan = e.button === 1 || e.button === 2 || e.shiftKey; + const [row, col, half] = cellAt(e); + drag = { id: e.pointerId, kind: pan ? 'pan' : 'paint', x: e.clientX, y: e.clientY, last: '', moved: false }; + // a mouse click toggles at once (as a terminal click did); a touch waits to + // see whether it's a tap or a drag across the universe + if (!pan && e.pointerType === 'mouse' && onGrid(row, col)) { + life.toggleCell(row, col, half); + drag.last = `${row},${col},${half}`; + paint(); + } +}); +canvas.addEventListener('pointermove', (e) => { + if (touches.has(e.pointerId)) touches.set(e.pointerId, { x: e.clientX, y: e.clientY }); + if (touches.size === 2 && pinch) { + const [a, b] = [...touches.values()]; + const d = a && b ? Math.hypot(a.x - b.x, a.y - b.y) : pinch; + if (d / pinch > 1.35) { + act('zoomIn'); + pinch = d; + } else if (d / pinch < 0.74) { + act('zoomOut'); + pinch = d; + } + return; + } + if (!drag || drag.id !== e.pointerId) return; + const touch = e.pointerType !== 'mouse'; + if (!drag.moved && Math.hypot(e.clientX - drag.x, e.clientY - drag.y) < (touch ? 10 : 2)) return; + drag.moved = true; + if (drag.kind === 'pan' || touch) { + // pan in terminal cells, like the arrow keys (scaled by zoom inside pan()) + const r = canvas.getBoundingClientRect(); + const cssCw = r.width / term.cols; + const dc = Math.trunc((drag.x - e.clientX) / cssCw); + const dr = Math.trunc((drag.y - e.clientY) / cssCw); + if (dc || dr) { + life.pan(dr, dc); + drag.x -= (dc * cssCw); + drag.y -= (dr * cssCw); + paint(); + } + return; + } + // mouse drag: draw living cells along the way + const [row, col, half] = cellAt(e); + const key = `${row},${col},${half}`; + if (key === drag.last || !onGrid(row, col)) return; + drag.last = key; + const [gy, gx] = life.termToWorld(row, col, half); + life.setCell(gy, gx, 1); + paint(); +}); +const endPointer = (e: PointerEvent): void => { + touches.delete(e.pointerId); + if (touches.size < 2) pinch = 0; + if (!drag || drag.id !== e.pointerId) return; + if (e.type === 'pointerup' && e.pointerType !== 'mouse' && !drag.moved) { + const [row, col, half] = cellAt(e); + if (onGrid(row, col)) { + life.toggleCell(row, col, half); + paint(); + } + } + drag = null; +}; +canvas.addEventListener('pointerup', endPointer); +canvas.addEventListener('pointercancel', endPointer); + +let wheel = 0; +canvas.addEventListener( + 'wheel', + (e) => { + e.preventDefault(); + wheel += e.deltaY * (e.deltaMode === 1 ? 33 : 1); + if (Math.abs(wheel) < 120) return; + act(wheel > 0 ? 'zoomOut' : 'zoomIn'); + wheel = 0; + }, + { passive: false }, +); + +// ── Resizes, and leaving ───────────────────────────────────────────────── + +window.addEventListener('resize', scheduleRefit); + +// Moving the window to another screen (or browser zoom) changes the pixel +// ratio, sometimes without a resize; the query has to be re-made each time +function watchPixelRatio(): void { + try { + const mq = window.matchMedia(`(resolution: ${window.devicePixelRatio || 1}dppx)`); + mq.addEventListener( + 'change', + () => { + scheduleRefit(); + watchPixelRatio(); + }, + { once: true }, + ); + } catch { + // no matchMedia listeners here: resize alone will do + } +} +watchPixelRatio(); + +document.addEventListener('visibilitychange', () => { + if (document.visibilityState === 'hidden') { + persist(); + stopLoop(); + music.suspend(); + } else { + music.resume(); + startLoop(); + } +}); +window.addEventListener('pagehide', persist); + +// for scripts/shot.mjs and the curious +(window as unknown as { afterlife: unknown }).afterlife = { + get life() { + return life; + }, + term, + frame, + paint, + act, + persist, +}; + +paint(); +if (document.visibilityState !== 'hidden') startLoop(); diff --git a/afterlife/src/music/audio.ts b/afterlife/src/music/audio.ts new file mode 100644 index 0000000..c183371 --- /dev/null +++ b/afterlife/src/music/audio.ts @@ -0,0 +1,137 @@ +/** + * audio.ts — the page's side of the music. + * + * Owns the AudioContext and the worklet node, mirrors the controls (mute, + * volume, style) so the status line can be written without asking the audio + * thread, and forwards each frame's snapshot. The browser only lets sound + * start from a gesture, so start() is called from the first click or key; + * until then there is no engine, which the status line shows the way the + * terminal showed a missing PyAudio: by saying nothing. + */ + +import workletSource from 'virtual:afterlife-worklet'; +import { SAMPLE_RATE, STYLES, statusString, type SimulationSnapshot, type Style } from './engine'; +import type { ToWorklet } from './worklet'; + +export class Music { + private ctx: AudioContext | null = null; + private node: AudioWorkletNode | null = null; + private starting: Promise | null = null; + /** failed starts so far; past a few, stop building contexts on every key */ + private failures = 0; + muted = false; + volume = 0.5; + style: Style = STYLES[1]; // Start with ambient + + get running(): boolean { + return this.node !== null; + } + + /** Must be called from a user gesture. Resolves false when there's no audio to be had. */ + start(): Promise { + if (this.ctx) { + // iOS parks a context as 'interrupted' (a call, Siri, another app's + // audio); like 'suspended', a gesture is what brings it back + const state: string = this.ctx.state; + if (state === 'suspended' || state === 'interrupted') void this.ctx.resume().catch(() => undefined); + return this.starting ?? Promise.resolve(this.running); + } + if (this.failures >= 3) return Promise.resolve(false); + Music.preferPlayback(); + let ctx: AudioContext | null = null; + const attempt = (async () => { + try { + ctx = new AudioContext({ sampleRate: SAMPLE_RATE, latencyHint: 'playback' }); + this.ctx = ctx; + const url = URL.createObjectURL(new Blob([workletSource], { type: 'text/javascript' })); + try { + await ctx.audioWorklet.addModule(url); + } finally { + URL.revokeObjectURL(url); + } + const node = new AudioWorkletNode(ctx, 'afterlife-music', { numberOfInputs: 0, outputChannelCount: [2] }); + node.connect(ctx.destination); + this.node = node; + this.sendControls(); + if (ctx.state === 'suspended') await ctx.resume().catch(() => undefined); + return true; + } catch (err) { + console.warn('afterlife: no music tonight', err); + this.failures++; + // a failed start isn't final: let the context go, so a later gesture + // tries afresh instead of finding a half-built one + if (ctx) void (ctx as AudioContext).close().catch(() => undefined); + if (this.ctx === ctx) { + this.ctx = null; + this.node = null; + this.starting = null; + } + return false; + } + })(); + // a synchronous failure has already reset us; don't pin its promise + if (this.ctx) this.starting = attempt; + return attempt; + } + + /** + * iOS plays web audio as 'ambient' by default, which the silent switch + * mutes; 'playback' is what music is. Safari 17+ only, so looked up loosely. + */ + private static preferPlayback(): void { + try { + const session = (navigator as Navigator & { audioSession?: { type?: string } }).audioSession; + if (session && session.type !== 'playback') session.type = 'playback'; + } catch { + // not ours to insist on + } + } + + private post(m: ToWorklet): void { + this.node?.port.postMessage(m); + } + + private sendControls(): void { + this.post({ type: 'controls', muted: this.muted, volume: this.volume, style: this.style }); + } + + update(snap: SimulationSnapshot): void { + this.post({ type: 'snapshot', snap }); + } + + toggleMute(): void { + this.muted = !this.muted; + this.sendControls(); + } + + adjustVolume(delta: number): void { + this.volume = Math.max(0, Math.min(1, Math.round((this.volume + delta) * 100) / 100)); + this.sendControls(); + } + + cycleStyle(): void { + this.style = STYLES[(STYLES.indexOf(this.style) + 1) % STYLES.length] ?? STYLES[1]; + this.sendControls(); + } + + /** Hidden tab: stop the clock so it doesn't hum over a frozen universe. */ + suspend(): void { + void this.ctx?.suspend().catch(() => undefined); + } + + resume(): void { + void this.ctx?.resume().catch(() => undefined); + } + + statusString(): string { + return this.running ? statusString(this.muted, this.style, this.volume) : ''; + } + + stop(): void { + this.post({ type: 'stop' }); + void this.ctx?.close().catch(() => undefined); + this.ctx = null; + this.node = null; + this.starting = null; + } +} diff --git a/afterlife/src/music/engine.ts b/afterlife/src/music/engine.ts new file mode 100644 index 0000000..03a4bf8 --- /dev/null +++ b/afterlife/src/music/engine.ts @@ -0,0 +1,1135 @@ +/** + * engine.ts — the generative music engine, ported from life_music.py. + * + * Produces an ethereal, continuously-evolving soundscape where the music + * emerges from the simulation state itself, in two switchable styles: pure + * chiptune (NES) and ambient synth. Four voice layers — drone, melody (a + * playhead scanning the viewport), arpeggio, noise — plus a cadence when a + * detected attractor is broken. + * + * It runs inside an AudioWorklet (worklet.ts) but knows nothing of one: it + * renders buffers of BUFFER_SIZE frames at SAMPLE_RATE, exactly the blocks + * PyAudio asked the original for, because every smoothing constant below is + * per buffer (volume ramps, root portamento, noise burst phase). The + * worklet hands those buffers out 128 frames at a time. + * + * No numpy here, so each vectorised expression is one loop; the maths and + * the order of operations are the original's, down to its quirks (the old + * style's drone advances the shared phases a second buffer during a style + * crossfade, as it does in Python). + * + * Where numpy made a fresh array per expression, the port writes into scratch + * buffers the engine and each voice own, sized to a block and reused, so the + * audio thread renders a block without allocating (a garbage-collection pause + * there is a glitch). Each keeps the element type numpy's array had — float64 + * for phases, float32 for signals — so every intermediate rounds where the + * original's did. A scratch buffer may be longer than the block it holds; + * only the first n samples mean anything. + */ + +export const SAMPLE_RATE = 44100; +export const BUFFER_SIZE = 2048; +const TWO_PI = 2 * Math.PI; + +// Styles +export const STYLE_CHIPTUNE = 'chiptune'; +export const STYLE_AMBIENT = 'ambient'; +export const STYLES = [STYLE_CHIPTUNE, STYLE_AMBIENT] as const; +export type Style = (typeof STYLES)[number]; + +// ── Scales (semitone intervals from root) ────────────────────────────── +const SCALES: Readonly> = { + lydian: [0, 2, 4, 6, 7, 9, 11], + aeolian: [0, 2, 3, 5, 7, 8, 10], + whole_tone: [0, 2, 4, 6, 8, 10], + dorian: [0, 2, 3, 5, 7, 9, 10], + minor_pentatonic: [0, 3, 5, 7, 10], + mixolydian: [0, 2, 4, 5, 7, 9, 10], + major_pentatonic: [0, 2, 4, 7, 9], +}; + +// ── Cadence (played when a detected attractor is broken) ────────────── +// A short resolving gesture — octave, fifth, root — before the new chaos +// arrives. Death of an attractor deserves scoring. +const CADENCE_VOLUME = 0.22; +const CADENCE_COOLDOWN_S = 20.0; + +/** Mood → scale name */ +const MOOD_SCALE: Readonly> = { + booming: 'lydian', + declining: 'aeolian', + cycle: 'whole_tone', + stagnant: 'dorian', + sparse: 'minor_pentatonic', + dense: 'mixolydian', + injection: 'major_pentatonic', + haunted: 'whole_tone', // no tonal center — nothing resolves +}; +const DEFAULT_SCALE = 'major_pentatonic'; + +/** Epoch → MIDI note number for root */ +const EPOCH_ROOT: Readonly> = { + genesis: 48, // C3 + primordial: 46, // Bb2 + emergence: 50, // D3 + expansion: 53, // F3 + flourishing: 55, // G3 + 'deep time': 51, // Eb3 + eon: 48, // C3 + eternity: 45, // A2 +}; +const DEFAULT_ROOT = 48; // C3 + +/** ADSR presets (attack, decay, sustain_level, release) in seconds */ +const ADSR_CHIPTUNE = [0.005, 0.05, 0.7, 0.02] as const; +const ADSR_AMBIENT = [0.06, 0.25, 0.75, 0.5] as const; + +/** Maximum polyphony for melody playhead */ +const MAX_MELODY_VOICES = 4; + +// ═══════════════════════════════════════════════════════════════════════ +// Data transfer: snapshot from sim → audio +// ═══════════════════════════════════════════════════════════════════════ + +/** Immutable state transfer from simulation to audio engine. */ +export interface SimulationSnapshot { + generation: number; + population: number; + pop_floor: number; + density: number; + spread: number; + cycle_period: number; + mood: string; + epoch: string; + pop_delta: number; + /** alive cells down the playhead's column, top to bottom */ + playhead_column: boolean[]; + playhead_position: number; + viewport_rows: number; + /** last injection event string ('' = none this frame) */ + event: string; + /** horizontal centroid of recent change (0-1) */ + activity_x: number; +} + +export const EMPTY_SNAPSHOT: SimulationSnapshot = { + generation: 0, + population: 0, + pop_floor: 100, + density: 0, + spread: 0, + cycle_period: 0, + mood: '', + epoch: 'genesis', + pop_delta: 0, + playhead_column: [], + playhead_position: 0, + viewport_rows: 40, + event: '', + activity_x: 0.5, +}; + +/** Derived musical parameters, recomputed each frame in update(). */ +class MusicalState { + root_midi = DEFAULT_ROOT; + target_root_midi = DEFAULT_ROOT; + scale_name = DEFAULT_SCALE; + scale_intervals: readonly number[] = SCALES[DEFAULT_SCALE] ?? []; + prev_scale_intervals: readonly number[] = SCALES[DEFAULT_SCALE] ?? []; + scale_crossfade = 1.0; // 0=old scale, 1=new scale + tempo_bpm = 80.0; + // Per-layer target volumes (0.0-1.0) + drone_volume = 0.3; + melody_volume = 0.2; + arp_volume = 0.15; + noise_volume = 0.05; + // Arpeggio + arp_speed = 4.0; // notes per second + arp_pattern: readonly number[] = [0, 2, 4, 7]; // scale degree offsets + // Melody playhead notes (Hz values for active notes) + playhead_notes: readonly number[] = []; +} + +// ═══════════════════════════════════════════════════════════════════════ +// Utility functions +// ═══════════════════════════════════════════════════════════════════════ + +export function midiToHz(midi: number): number { + return 440.0 * 2.0 ** ((midi - 69.0) / 12.0); +} + +function scalePitches(root: number, intervals: readonly number[], octaves = 2): number[] { + const pitches: number[] = []; + for (let o = 0; o < octaves; o++) for (const iv of intervals) pitches.push(midiToHz(root + iv + 12 * o)); + return pitches; +} + +/** Linear interpolation from a to b by factor t (clamped to 0.0-1.0). */ +function lerp(a: number, b: number, t: number): number { + return a + (b - a) * Math.max(0.0, Math.min(1.0, t)); +} + +/** np.linspace(a, b, n) as float32 (endpoint included), into `out` */ +function linspace(a: number, b: number, n: number, out: Float32Array): Float32Array { + if (n <= 0) return out; + if (n === 1) { + out[0] = a; + return out; + } + const step = (b - a) / (n - 1); + for (let i = 0; i < n; i++) out[i] = a + i * step; + out[n - 1] = b; + return out; +} + +const pmod = (x: number, m: number): number => ((x % m) + m) % m; + +/** + * One-pole low-pass filter with cached coefficients and persistent state, + * carried across buffers for smooth continuous filtering. + */ +class CachedLPF { + private readonly alpha: number; + private z = 0; + + constructor(cutoffHz: number, sampleRate = SAMPLE_RATE) { + const rc = 1.0 / (TWO_PI * cutoffHz); + const dt = 1.0 / sampleRate; + this.alpha = dt / (rc + dt); + } + + /** Filter the first n samples of `signal` into `out` (which may be `signal`). */ + apply(signal: Float32Array, n: number, out: Float32Array): Float32Array { + const a = this.alpha; + let prev = this.z; + for (let i = 0; i < n; i++) { + prev = a * (signal[i] ?? 0) + (1.0 - a) * prev; + out[i] = prev; + } + this.z = prev; + return out; + } + + reset(): void { + this.z = 0; + } + + /** Forget a state that has gone NaN or infinite (it would never recover). */ + heal(): void { + if (!Number.isFinite(this.z)) this.z = 0; + } +} + +// ═══════════════════════════════════════════════════════════════════════ +// Oscillator primitives +// ═══════════════════════════════════════════════════════════════════════ + +// Each writes the first n samples of its wave into `out` and returns it. + +function sineWave(phase: Float64Array, n: number, out: Float32Array): Float32Array { + for (let i = 0; i < n; i++) out[i] = Math.sin(phase[i] ?? 0); + return out; +} + +function triangleWave(phase: Float64Array, n: number, out: Float32Array): Float32Array { + for (let i = 0; i < n; i++) { + const p = pmod(phase[i] ?? 0, TWO_PI) / TWO_PI; + out[i] = 2.0 * Math.abs(2.0 * p - 1.0) - 1.0; + } + return out; +} + +/** PolyBLEP residual for antialiased square wave edges. */ +function polyblep(t: number): number { + if (t >= 0 && t < 1) return 2.0 * t - t * t - 1.0; + if (t >= -1 && t < 0) return t * t + 2.0 * t + 1.0; + return 0; +} + +/** Square wave with polyBLEP antialiasing. */ +function squareWave(phase: Float64Array, n: number, duty: number, freqHz: number, out: Float32Array): Float32Array { + let dt = freqHz / SAMPLE_RATE; + if (!(freqHz > 0)) { + // np.mean(np.diff(p)) fallback + let s = 0; + for (let i = 1; i < n; i++) s += pmod(phase[i] ?? 0, TWO_PI) / TWO_PI - pmod(phase[i - 1] ?? 0, TWO_PI) / TWO_PI; + dt = n > 1 ? s / (n - 1) : 0.01; + } + for (let i = 0; i < n; i++) { + const p = pmod(phase[i] ?? 0, TWO_PI) / TWO_PI; + let raw = p < duty ? 1.0 : -1.0; + if (dt > 0) { + // Transition at p=0 (rising edge) + if (p < dt) raw += polyblep(p / dt); + if (p > 1.0 - dt) raw += polyblep((p - 1.0) / dt); + // Transition at p=duty (falling edge) + const t2 = (p - duty) / dt; + if (Math.abs(t2) < 1.0) raw -= polyblep(t2); + } + out[i] = raw; + } + return out; +} + +// ═══════════════════════════════════════════════════════════════════════ +// ADSR Envelope +// ═══════════════════════════════════════════════════════════════════════ + +class ADSREnvelope { + readonly attack: number; + readonly decay: number; + readonly release: number; + + constructor(attack: number, decay: number, readonly sustain: number, release: number) { + this.attack = Math.max(0.001, attack); + this.decay = Math.max(0.001, decay); + this.release = Math.max(0.001, release); + } + + /** Envelope shape for n samples starting at `elapsed` seconds, into `env`. */ + generate(n: number, gate: boolean, elapsed: number, releaseTime: number, env: Float32Array): Float32Array { + for (let i = 0; i < n; i++) { + const t = elapsed + i / SAMPLE_RATE; + if (gate) { + if (t < this.attack) env[i] = t / this.attack; + else if (t < this.attack + this.decay) env[i] = 1.0 - (1.0 - this.sustain) * ((t - this.attack) / this.decay); + else env[i] = this.sustain; + } else if (releaseTime >= 0) { + const rel = t - releaseTime; + env[i] = rel < 0 ? this.sustain : this.sustain * Math.max(0.0, 1.0 - rel / this.release); + } else { + env[i] = 0; + } + } + return env; + } +} + +const ENV_CHIPTUNE = new ADSREnvelope(...ADSR_CHIPTUNE); +const ENV_AMBIENT = new ADSREnvelope(...ADSR_AMBIENT); + +// ═══════════════════════════════════════════════════════════════════════ +// Voice: a single pitched sound with envelope +// ═══════════════════════════════════════════════════════════════════════ + +class Voice { + freq: number; + target_freq: number; + phase = 0.0; + phase_det = 0.0; // Detuned chorus oscillator phase + active = false; + gate = false; + elapsed = 0.0; + release_time = -1.0; + + // Scratch for render(): phases (float64, as numpy's cumsum), and the + // oscillator and envelope (float32). render() returns `osc`, which holds + // the voice's samples until its next render. + private phases = new Float64Array(BUFFER_SIZE); + private phasesDet = new Float64Array(BUFFER_SIZE); + private osc = new Float32Array(BUFFER_SIZE); + private env = new Float32Array(BUFFER_SIZE); + + constructor( + freq = 440.0, + public style: Style = STYLE_AMBIENT, + ) { + this.freq = freq; + this.target_freq = freq; + } + + /** Start a new note — full ADSR restart. */ + noteOn(freq: number): void { + this.target_freq = freq; + this.freq = freq; + this.gate = true; + this.active = true; + this.elapsed = 0.0; + this.release_time = -1.0; + } + + /** + * Slide to a new pitch without restarting the ADSR envelope. Used when a + * voice is already active and the pitch change is small enough that a + * smooth portamento sounds better than a hard retrigger. + */ + retrigger(freq: number): void { + this.target_freq = freq; + if (!this.active) { + this.noteOn(freq); + } else if (!this.gate) { + // Voice was releasing — re-gate it without resetting elapsed + this.gate = true; + this.release_time = -1.0; + } + } + + noteOff(): void { + this.gate = false; + this.release_time = this.elapsed; + } + + /** Silence a voice whose pitch, phase or clock has gone NaN or infinite. */ + heal(): void { + const ok = [this.freq, this.target_freq, this.phase, this.phase_det, this.elapsed, this.release_time].every(Number.isFinite); + if (ok) return; + this.freq = this.target_freq = 440.0; + this.phase = this.phase_det = 0.0; + this.active = this.gate = false; + this.elapsed = 0.0; + this.release_time = -1.0; + } + + /** The next n samples. The returned buffer is the voice's own: read it before the next render. */ + render(n: number): Float32Array { + if (this.osc.length < n) { + this.phases = new Float64Array(n); + this.phasesDet = new Float64Array(n); + this.osc = new Float32Array(n); + this.env = new Float32Array(n); + } + const osc = this.osc; + if (!this.active) return osc.fill(0, 0, n); + const dt = 1.0 / SAMPLE_RATE; + + // Per-sample frequency ramp for buffer-size-independent portamento. + // Time constant in seconds: ambient slides slowly, chiptune snaps fast. + const slideTc = this.style === STYLE_AMBIENT ? 0.15 : 0.02; + const alpha = 1.0 - Math.exp(-dt / slideTc); + const phases = this.phases; + const phasesDet = this.phasesDet; + let f = this.freq; + let ph = this.phase; + let phd = this.phase_det; + let decay = 1; + for (let i = 0; i < n; i++) { + // f[n] = target + (start - target) * (1 - alpha)^n + f = this.target_freq + (this.freq - this.target_freq) * decay; + decay *= 1.0 - alpha; + ph += TWO_PI * f * dt; + phd += TWO_PI * f * 1.003 * dt; + phases[i] = ph; + phasesDet[i] = phd; + } + if (n > 0) { + this.freq = f; + this.phase = pmod(ph, TWO_PI); + } + + let adsr: ADSREnvelope; + if (this.style === STYLE_CHIPTUNE) { + squareWave(phases, n, 0.25, this.freq, osc); + adsr = ENV_CHIPTUNE; + } else { + // Ambient: sine + chorus with a dedicated detuned phase accumulator + if (n > 0) this.phase_det = pmod(phd, TWO_PI); + for (let i = 0; i < n; i++) { + osc[i] = Math.fround(Math.sin(phases[i] ?? 0)) * 0.7 + Math.fround(Math.sin(phasesDet[i] ?? 0)) * 0.3; + } + adsr = ENV_AMBIENT; + } + + const env = adsr.generate(n, this.gate, this.elapsed, this.release_time, this.env); + this.elapsed += n * dt; + + // Check if voice has fully released + if (!this.gate && this.release_time >= 0 && this.elapsed - this.release_time > adsr.release) { + this.active = false; + } + for (let i = 0; i < n; i++) osc[i] = (osc[i] ?? 0) * (env[i] ?? 0); + return osc; + } +} + +// ═══════════════════════════════════════════════════════════════════════ +// The Music Engine +// ═══════════════════════════════════════════════════════════════════════ + +/** Standard-normal samples (numpy's randn), Box–Muller. */ +function randn(n: number): Float32Array { + const out = new Float32Array(n); + for (let i = 0; i < n; i += 2) { + const u = 1 - Math.random(); + const v = Math.random(); + const r = Math.sqrt(-2 * Math.log(u)); + out[i] = r * Math.cos(TWO_PI * v); + if (i + 1 < n) out[i + 1] = r * Math.sin(TWO_PI * v); + } + return out; +} + +/** + * The engine's per-block scratch, one buffer per array numpy would have made + * fresh, allocated once for a block of `size` frames. Buffers that live + * across stages (the layers and ramps, read by the mix) each have their own; + * the drone's phases, oscillators and LFO are consumed inside one + * renderDrone() call, so both drones of a style crossfade share them. + */ +class Scratch { + // Mix output (renderStereo returns these) + readonly left: Float32Array; + readonly right: Float32Array; + readonly stereo: [Float32Array, Float32Array]; + // Layers, and their per-sample volume and pan ramps + readonly drone: Float32Array; + readonly droneOld: Float32Array; // the outgoing style's drone during a crossfade + readonly melody: Float32Array; + readonly arp: Float32Array; + readonly noise: Float32Array; + cadence: Float32Array | null = null; // the cadence voice's own buffer + readonly droneRamp: Float32Array; + readonly melodyRamp: Float32Array; + readonly arpRamp: Float32Array; + readonly noiseRamp: Float32Array; + readonly melTheta: Float32Array; + readonly arpTheta: Float32Array; + // Inside renderDrone() + readonly lfo: Float32Array; + readonly ph1: Float64Array; + readonly ph1d: Float64Array; + readonly ph2: Float64Array; + readonly ph2d: Float64Array; + readonly phs: Float64Array; + readonly osc0: Float32Array; + readonly osc1: Float32Array; + readonly osc2: Float32Array; + readonly osc3: Float32Array; + // Inside renderNoise() + readonly noiseRaw: Float32Array; + readonly noiseEnv: Float32Array; + + constructor(readonly size: number) { + const f32 = (): Float32Array => new Float32Array(size); + const f64 = (): Float64Array => new Float64Array(size); + this.left = f32(); + this.right = f32(); + this.stereo = [this.left, this.right]; + this.drone = f32(); + this.droneOld = f32(); + this.melody = f32(); + this.arp = f32(); + this.noise = f32(); + this.droneRamp = f32(); + this.melodyRamp = f32(); + this.arpRamp = f32(); + this.noiseRamp = f32(); + this.melTheta = f32(); + this.arpTheta = f32(); + this.lfo = f32(); + this.ph1 = f64(); + this.ph1d = f64(); + this.ph2 = f64(); + this.ph2d = f64(); + this.phs = f64(); + this.osc0 = f32(); + this.osc1 = f32(); + this.osc2 = f32(); + this.osc3 = f32(); + this.noiseRaw = f32(); + this.noiseEnv = f32(); + } +} + +export class LifeMusicEngine { + muted = false; + masterVolume = 0.5; + private styleIdx = 1; // Start with ambient + style: Style = STYLES[1]; + + private musical = new MusicalState(); + private snapshot: SimulationSnapshot = EMPTY_SNAPSHOT; + + // Phase accumulators for drone layer + private dronePhase1 = 0.0; + private dronePhase1Det = 0.0; // Detuned chorus oscillator + private dronePhase2 = 0.0; + private dronePhase2Det = 0.0; // Detuned fifth oscillator + private dronePhaseSub = 0.0; + private droneLfoPhase = 0.0; + + // Melody voices + private melodyVoices: Voice[]; + + // Arpeggio state + private arpVoice: Voice; + private arpNoteIdx = 0; + private arpSamplesInNote = 0; // discrete counter, no float drift + + // Noise state: triggered burst system + private noiseBurstAmp = 0.0; // current burst amplitude + private noiseBurstPhase = 1.0; // 0=attack start, 1=idle + private noiseBurstDuration = 0.15; // seconds per burst + private readonly noiseBurstThreshold = 20; // min pop_delta to trigger + private noiseBurstCooldown = 0.0; // seconds remaining before next burst allowed + + // Smoothed volumes (to avoid clicks) + private smoothDroneVol = 0.3; + private smoothMelodyVol = 0.0; + private smoothArpVol = 0.0; + private smoothNoiseVol = 0.0; + + // Root frequency portamento + private currentRootHz = midiToHz(DEFAULT_ROOT); + private targetRootHz = midiToHz(DEFAULT_ROOT); + + // Crossfade for anti-click when switching styles + private crossfadeRemaining = 0; + private readonly crossfadeTotal = Math.floor(SAMPLE_RATE / 4); // 250ms crossfade + private oldStyle: Style = this.style; + + private readonly noisePool = randn(SAMPLE_RATE); + private noisePoolIdx = 0; + + // Cached low-pass filters (pre-computed coefficients, persistent state) + private lpfDrone = new CachedLPF(800.0); + private lpfMelody = new CachedLPF(3500.0); + private lpfArp = new CachedLPF(4500.0); + private lpfNoise = new CachedLPF(600.0); + + // Per-block scratch, reused block after block (regrown if a block is longer) + private buf = new Scratch(BUFFER_SIZE); + + // Stereo: melody pans with the playhead; arpeggio drifts on a slow LFO. + private prevMelodyPan = 0.5; + private prevArpPan = 0.5; + private autopanPhase = 0.0; + + // Cadence: resolving gesture when a detected cycle is broken. + // Sequence of [freq_hz, duration_samples]; idx -1 = inactive. + private cadenceVoice: Voice; + private cadenceSeq: [number, number][] = []; + private cadenceIdx = -1; + private cadenceRemaining = 0; + private cadenceCooldown = 0; // samples until next trigger allowed + + constructor() { + this.melodyVoices = Array.from({ length: MAX_MELODY_VOICES }, () => new Voice(440, this.style)); + this.arpVoice = new Voice(440, this.style); + this.cadenceVoice = new Voice(440, this.style); + } + + get volumePercent(): number { + return Math.round(this.masterVolume * 100); + } + + // ── Controls ─────────────────────────────────────────────────────── + + toggleMute(): void { + this.muted = !this.muted; + } + + adjustVolume(delta: number): void { + this.masterVolume = Math.max(0.0, Math.min(1.0, this.masterVolume + delta)); + } + + cycleStyle(): void { + this.oldStyle = this.style; + this.styleIdx = (this.styleIdx + 1) % STYLES.length; + this.style = STYLES[this.styleIdx] ?? STYLE_AMBIENT; + this.crossfadeRemaining = this.crossfadeTotal; + for (const v of this.melodyVoices) v.style = this.style; + this.arpVoice.style = this.style; + this.cadenceVoice.style = this.style; + } + + setStyle(style: Style): void { + if (style !== this.style) this.cycleStyle(); + } + + // ── State update (called each sim frame) ──────────────────────────── + + /** Map simulation state to musical parameters. */ + update(snapshot: SimulationSnapshot): void { + this.snapshot = snapshot; + const ms = this.musical; + + // ── Root note from epoch ── + ms.target_root_midi = EPOCH_ROOT[snapshot.epoch] ?? DEFAULT_ROOT; + // Smooth portamento for root changes + this.targetRootHz = midiToHz(ms.target_root_midi); + + // ── Scale from mood ── + const newScale = MOOD_SCALE[snapshot.mood] ?? DEFAULT_SCALE; + if (newScale !== ms.scale_name) { + ms.prev_scale_intervals = ms.scale_intervals; + ms.scale_name = newScale; + ms.scale_intervals = SCALES[newScale] ?? []; + ms.scale_crossfade = 0.0; // Start crossfade + } + // Advance crossfade (over ~2 seconds at ~30fps ≈ 60 frames) + if (ms.scale_crossfade < 1.0) ms.scale_crossfade = Math.min(1.0, ms.scale_crossfade + 0.017); + + // ── Drone volume: proportional to population ── + const popRatio = snapshot.pop_floor > 0 ? Math.min(2.0, snapshot.population / Math.max(1, snapshot.pop_floor)) : 0.5; + ms.drone_volume = lerp(0.13, 0.38, Math.min(1.0, popRatio)); + + // ── Melody volume: moderate, scales with density ── + ms.melody_volume = lerp(0.08, 0.35, Math.min(1.0, snapshot.density * 10.0)); + + // ── Arpeggio: speed from cycle detection, volume from spread ── + if (snapshot.cycle_period > 0) { + // Faster arpeggios when cycles are short + ms.arp_speed = lerp(3.0, 12.0, Math.min(1.0, 10.0 / Math.max(1, snapshot.cycle_period))); + ms.arp_volume = 0.2; + } else { + ms.arp_speed = lerp(2.0, 6.0, Math.min(1.0, snapshot.spread / 200.0)); + ms.arp_volume = lerp(0.05, 0.15, Math.min(1.0, snapshot.spread / 100.0)); + } + + // Build arpeggio pattern from scale + const si = ms.scale_intervals; + if (si.length >= 3) { + ms.arp_pattern = [si[0] ?? 0, si[Math.min(2, si.length - 1)] ?? 0, si[Math.min(4, si.length - 1)] ?? 0, (si[0] ?? 0) + 12]; + } else { + ms.arp_pattern = [0, 4, 7, 12]; + } + + // ── Noise: driven by population delta (births/deaths) ── + ms.noise_volume = lerp(0.0, 0.15, Math.min(1.0, Math.abs(snapshot.pop_delta) / 50.0)); + + // ── Melody playhead notes ── + this.computePlayheadNotes(snapshot, ms); + + // ── Tempo ── + ms.tempo_bpm = lerp(60.0, 120.0, Math.min(1.0, snapshot.density * 8.0)); + + // ── Cadence: score the death of an attractor ── + // The sim breaks detected cycles with an injection; before the new chaos + // registers, resolve — octave, fifth, root, ringing out. + if (snapshot.event.startsWith('inject') && snapshot.event.includes('cycle') && this.cadenceIdx < 0 && this.cadenceCooldown <= 0) { + const base = ms.target_root_midi + 12; // melody register + this.cadenceSeq = [ + [midiToHz(base + 12), Math.trunc(0.4 * SAMPLE_RATE)], + [midiToHz(base + 7), Math.trunc(0.4 * SAMPLE_RATE)], + [midiToHz(base), Math.trunc(2.0 * SAMPLE_RATE)], + ]; + this.cadenceRemaining = 0; + this.cadenceCooldown = Math.trunc(CADENCE_COOLDOWN_S * SAMPLE_RATE); + this.cadenceIdx = 0; + } + } + + /** Map alive cells in the playhead column to pitches. */ + private computePlayheadNotes(snap: SimulationSnapshot, ms: MusicalState): void { + const col = snap.playhead_column; + if (!col.length || snap.viewport_rows < 2) { + ms.playhead_notes = []; + return; + } + const nRows = col.length; + // Build pitch range: 2 octaves of current scale + const pitches = scalePitches(ms.target_root_midi, ms.scale_intervals, 2); + if (!pitches.length) { + ms.playhead_notes = []; + return; + } + const aliveRows: number[] = []; + col.forEach((alive, i) => { + if (alive) aliveRows.push(i); + }); + // Density gate: skip if too sparse and overall density is low + if (aliveRows.length < 2 && snap.density < 0.03) { + ms.playhead_notes = []; + return; + } + // Map row index → pitch (top = high, bottom = low) + let notes = aliveRows.map((row) => { + const frac = 1.0 - row / Math.max(1, nRows - 1); + const idx = Math.max(0, Math.min(Math.trunc(frac * (pitches.length - 1)), pitches.length - 1)); + return pitches[idx] ?? 0; + }); + // Cap polyphony: pick most spread-out if too many + if (notes.length > MAX_MELODY_VOICES) { + notes.sort((a, b) => a - b); + const step = notes.length / MAX_MELODY_VOICES; + notes = Array.from({ length: MAX_MELODY_VOICES }, (_, i) => notes[Math.trunc(i * step)] ?? 0); + } + ms.playhead_notes = notes; + } + + // ── The audio callback ────────────────────────────────────────────── + + /** + * One buffer, as PyAudio's stream callback produced it: the stereo mix, + * master volume (or silence when muted), then a tanh soft clip. Writes + * into `left`/`right`, which must be `n` long. + */ + renderBlock(left: Float32Array, right: Float32Array, n = left.length): void { + let l: Float32Array; + let r: Float32Array; + try { + this.mixStereo(n); + l = this.buf.left; + r = this.buf.right; + } catch { + l = this.buf.left.fill(0); + r = this.buf.right.fill(0); + } + const g = this.muted ? 0 : this.masterVolume; + for (let i = 0; i < n; i++) { + left[i] = Math.tanh((l[i] ?? 0) * g); + right[i] = Math.tanh((r[i] ?? 0) * g); + } + } + + /** + * After a block came out NaN: put back every piece of carried state that + * has gone NaN or infinite, since each one feeds the next block (a filter, + * a phase, a smoothed volume) and would otherwise hold the engine silent + * or screaming for good. Finite state is left alone, so nothing clicks. + */ + recover(): void { + const fin = (x: number, d: number): number => (Number.isFinite(x) ? x : d); + for (const f of [this.lpfDrone, this.lpfMelody, this.lpfArp, this.lpfNoise]) f.heal(); + for (const v of [...this.melodyVoices, this.arpVoice, this.cadenceVoice]) v.heal(); + this.dronePhase1 = fin(this.dronePhase1, 0); + this.dronePhase1Det = fin(this.dronePhase1Det, 0); + this.dronePhase2 = fin(this.dronePhase2, 0); + this.dronePhase2Det = fin(this.dronePhase2Det, 0); + this.dronePhaseSub = fin(this.dronePhaseSub, 0); + this.droneLfoPhase = fin(this.droneLfoPhase, 0); + this.smoothDroneVol = fin(this.smoothDroneVol, 0); + this.smoothMelodyVol = fin(this.smoothMelodyVol, 0); + this.smoothArpVol = fin(this.smoothArpVol, 0); + this.smoothNoiseVol = fin(this.smoothNoiseVol, 0); + this.currentRootHz = fin(this.currentRootHz, fin(this.targetRootHz, midiToHz(DEFAULT_ROOT))); + this.prevMelodyPan = fin(this.prevMelodyPan, 0.5); + this.prevArpPan = fin(this.prevArpPan, 0.5); + this.autopanPhase = fin(this.autopanPhase, 0); + this.arpSamplesInNote = fin(this.arpSamplesInNote, 0); + this.noiseBurstAmp = fin(this.noiseBurstAmp, 0); + this.noiseBurstPhase = fin(this.noiseBurstPhase, 1.0); + this.noiseBurstDuration = fin(this.noiseBurstDuration, 0.15); + this.noiseBurstCooldown = fin(this.noiseBurstCooldown, 0); + } + + /** Render all layers + per-sample volume ramps (shared by the mix paths) into `buf`. */ + private renderLayers(n: number): void { + const ms = this.musical; + const dt = n / SAMPLE_RATE; + const b = this.buf; + + // Smooth volume transitions — per-sample linear ramp to avoid step + // discontinuities at buffer boundaries (audible as ~43 Hz buzz) + const smooth = 0.15; + const pd = this.smoothDroneVol; + const pm = this.smoothMelodyVol; + const pa = this.smoothArpVol; + const pn = this.smoothNoiseVol; + this.smoothDroneVol = lerp(pd, ms.drone_volume, smooth); + this.smoothMelodyVol = lerp(pm, ms.melody_volume, smooth); + this.smoothArpVol = lerp(pa, ms.arp_volume, smooth); + this.smoothNoiseVol = lerp(pn, ms.noise_volume, smooth); + linspace(pd, this.smoothDroneVol, n, b.droneRamp); + linspace(pm, this.smoothMelodyVol, n, b.melodyRamp); + linspace(pa, this.smoothArpVol, n, b.arpRamp); + linspace(pn, this.smoothNoiseVol, n, b.noiseRamp); + + // Root portamento + this.currentRootHz = lerp(this.currentRootHz, this.targetRootHz, 0.05); + + if (this.crossfadeRemaining > 0) { + // During style crossfade: render with both styles and blend + const progress = 1.0 - this.crossfadeRemaining / Math.max(1, this.crossfadeTotal); + this.crossfadeRemaining = Math.max(0, this.crossfadeRemaining - n); + const droneNew = this.renderDrone(n, b.drone); + const saved = this.style; + this.style = this.oldStyle; + const droneOld = this.renderDrone(n, b.droneOld); + this.style = saved; + // Crossfade: equal-power curve (in place over the new style's drone) + const fNew = Math.fround(Math.sqrt(progress)); + const fOld = Math.fround(Math.sqrt(1.0 - progress)); + for (let i = 0; i < n; i++) droneNew[i] = (droneOld[i] ?? 0) * fOld + (droneNew[i] ?? 0) * fNew; + } else { + this.renderDrone(n, b.drone); + } + + this.renderMelody(n); + this.renderArpeggio(n); + this.renderNoise(n, dt); + b.cadence = this.renderCadence(n); + } + + /** + * Mix all layers into left/right buffers. + * + * The melody pans with the playhead as it sweeps the viewport, so the grid + * is spatially audible; the arpeggio leans toward where the universe is + * changing, with a slow LFO wobble. Drone, noise and cadence stay centered. + * Constant-power panning, with per-sample gain ramps so pan moves never + * click. + * + * The two buffers are the engine's own and hold the mix only until the + * next render; copy them to keep them. + */ + renderStereo(n: number): [Float32Array, Float32Array] { + this.mixStereo(n); + const b = this.buf; + return b.left.length === n ? b.stereo : [b.left.subarray(0, n), b.right.subarray(0, n)]; + } + + /** renderStereo(), into buf.left/buf.right. */ + private mixStereo(n: number): void { + if (this.buf.size < n) this.buf = new Scratch(n); + this.renderLayers(n); + const b = this.buf; + const { drone, melody, arp, noise, cadence, droneRamp, melodyRamp, arpRamp, noiseRamp } = b; + const snap = this.snapshot; + + const melodyPan = Math.min(1.0, Math.max(0.0, snap.playhead_position)); + this.autopanPhase = pmod(this.autopanPhase + (TWO_PI * n) / SAMPLE_RATE / 13.0, TWO_PI); + const arpPan = Math.min(1.0, Math.max(0.0, snap.activity_x + 0.12 * Math.sin(this.autopanPhase))); + + const halfPi = Math.PI / 2.0; + const melTheta = linspace(this.prevMelodyPan, melodyPan, n, b.melTheta); + const arpTheta = linspace(this.prevArpPan, arpPan, n, b.arpTheta); + this.prevMelodyPan = melodyPan; + this.prevArpPan = arpPan; + + const left = b.left; + const right = b.right; + for (let i = 0; i < n; i++) { + const mv = (melody[i] ?? 0) * (melodyRamp[i] ?? 0); + const av = (arp[i] ?? 0) * (arpRamp[i] ?? 0); + let center = (drone[i] ?? 0) * (droneRamp[i] ?? 0) + (noise[i] ?? 0) * (noiseRamp[i] ?? 0); + if (cadence) center += (cadence[i] ?? 0) * CADENCE_VOLUME; + center *= 0.7071; // constant-power center + const mt = (melTheta[i] ?? 0) * halfPi; + const at = (arpTheta[i] ?? 0) * halfPi; + left[i] = center + mv * Math.cos(mt) + av * Math.cos(at); + right[i] = center + mv * Math.sin(mt) + av * Math.sin(at); + } + } + + /** Render the resolving cadence voice (its own buffer), or null while inactive. */ + private renderCadence(n: number): Float32Array | null { + if (this.cadenceCooldown > 0) this.cadenceCooldown -= n; + if (this.cadenceIdx < 0) return null; + + if (this.cadenceRemaining <= 0) { + const next = this.cadenceSeq[this.cadenceIdx]; + if (next) { + const [freq, dur] = next; + if (this.cadenceIdx === 0) this.cadenceVoice.noteOn(freq); + else this.cadenceVoice.retrigger(freq); // legato slide down the resolution + this.cadenceRemaining = dur; + this.cadenceIdx += 1; + } else if (this.cadenceVoice.gate) { + this.cadenceVoice.noteOff(); // let the release ring out + } else if (!this.cadenceVoice.active) { + this.cadenceIdx = -1; // tail finished + return null; + } + } + this.cadenceRemaining -= n; + return this.cadenceVoice.render(n); + } + + // ── Drone layer ──────────────────────────────────────────────────── + + /** + * Continuous drone bed — always present, breathes with LFO. Written into + * `out`: a style crossfade renders it twice, into two buffers. + */ + private renderDrone(n: number, out: Float32Array): Float32Array { + const b = this.buf; + const rootHz = this.currentRootHz; + const fifthHz = rootHz * 1.5; // Perfect fifth + const tInc = 1.0 / SAMPLE_RATE; + + // LFO for breathing (very slow: ~0.1 Hz) + const lfoFreq = 0.08; + const lfo = b.lfo; + let lastLfo = this.droneLfoPhase; + for (let i = 0; i < n; i++) { + lastLfo = this.droneLfoPhase + TWO_PI * lfoFreq * (i / SAMPLE_RATE); + lfo[i] = 0.15 * Math.fround(Math.sin(lastLfo)); + } + this.droneLfoPhase = pmod(lastLfo + TWO_PI * lfoFreq * tInc, TWO_PI); + + const p1 = TWO_PI * rootHz * tInc; + const p1d = TWO_PI * (rootHz * 1.003) * tInc; // Detuned root (chorus) + const p2 = TWO_PI * fifthHz * tInc; + const p2d = TWO_PI * (fifthHz * 1.002) * tInc; // Detuned fifth + const ps = TWO_PI * (rootHz * 0.5) * tInc; // Sub-octave + + const { ph1, ph1d, ph2, ph2d, phs } = b; + for (let i = 0; i < n; i++) { + ph1[i] = this.dronePhase1 + i * p1; + ph1d[i] = this.dronePhase1Det + i * p1d; + ph2[i] = this.dronePhase2 + i * p2; + ph2d[i] = this.dronePhase2Det + i * p2d; + phs[i] = this.dronePhaseSub + i * ps; + } + // Keep phases bounded to preserve float precision + this.dronePhase1 = pmod((ph1[n - 1] ?? 0) + p1, TWO_PI); + this.dronePhase1Det = pmod((ph1d[n - 1] ?? 0) + p1d, TWO_PI); + this.dronePhase2 = pmod((ph2[n - 1] ?? 0) + p2, TWO_PI); + this.dronePhase2Det = pmod((ph2d[n - 1] ?? 0) + p2d, TWO_PI); + this.dronePhaseSub = pmod((phs[n - 1] ?? 0) + ps, TWO_PI); + + const drone = out; + if (this.style === STYLE_CHIPTUNE) { + // Detuned square waves + triangle sub + const osc1 = squareWave(ph1, n, 0.5, rootHz, b.osc0); + const osc2 = squareWave(ph2d, n, 0.5, fifthHz * 1.002, b.osc1); + const sub = triangleWave(phs, n, b.osc2); + for (let i = 0; i < n; i++) drone[i] = (osc1[i] ?? 0) * 0.35 + (osc2[i] ?? 0) * 0.25 + (sub[i] ?? 0) * 0.4; + } else { + // Ambient: detuned sines + filtered fifth + const osc1 = sineWave(ph1, n, b.osc0); + const osc1d = sineWave(ph1d, n, b.osc1); + const osc2 = sineWave(ph2, n, b.osc2); + const sub = sineWave(phs, n, b.osc3); + for (let i = 0; i < n; i++) { + drone[i] = (osc1[i] ?? 0) * 0.4 + (osc1d[i] ?? 0) * 0.15 + (osc2[i] ?? 0) * 0.25 + (sub[i] ?? 0) * 0.2; + } + // Low-pass filter for warmth + this.lpfDrone.apply(drone, n, drone); + } + // Apply LFO breathing + for (let i = 0; i < n; i++) drone[i] = (drone[i] ?? 0) * (0.85 + (lfo[i] ?? 0)); + return drone; + } + + // ── Melody layer ─────────────────────────────────────────────────── + + /** Grid-scanning playhead: alive cells at current column → pitches (into buf.melody). */ + private renderMelody(n: number): void { + const target = this.musical.playhead_notes; + + // Update voice assignments — portamento for small pitch changes to avoid + // ADSR retriggering at 30Hz (audible flutter). Only hard-retrigger for + // large intervals (> ~1 semitone = 6% freq change). + for (let i = 0; i < this.melodyVoices.length; i++) { + const voice = this.melodyVoices[i]; + if (!voice) continue; + const freq = target[i]; + if (freq !== undefined) { + if (!voice.active) voice.noteOn(freq); + else if (Math.abs(voice.target_freq - freq) > voice.target_freq * 0.06) voice.noteOn(freq); + else voice.retrigger(freq); + } else if (voice.gate) { + voice.noteOff(); + } + } + + // Ambient gets a per-voice boost (sines have ~3dB less RMS than squares) + const gain = this.style === STYLE_AMBIENT ? 0.5 : 0.3; + const buf = this.buf.melody.fill(0, 0, n); + for (const voice of this.melodyVoices) { + if (!voice.active) continue; + const v = voice.render(n); + for (let i = 0; i < n; i++) buf[i] = (buf[i] ?? 0) + (v[i] ?? 0) * gain; + } + // Chiptune melody benefits from a slight LPF to tame square harmonics + if (this.style === STYLE_CHIPTUNE) this.lpfMelody.apply(buf, n, buf); + } + + // ── Arpeggio layer ───────────────────────────────────────────────── + + /** Rhythmic arpeggiated texture, speed tied to cycle detection (into buf.arp). */ + private renderArpeggio(n: number): void { + const ms = this.musical; + const pattern = ms.arp_pattern; + const buf = this.buf.arp; + if (!pattern.length) { + buf.fill(0, 0, n); + return; + } + const root = ms.target_root_midi; + const noteDur = Math.max(1, Math.trunc(SAMPLE_RATE / Math.max(0.5, ms.arp_speed))); + + let done = 0; + while (done < n) { + // Samples remaining in current note + const chunk = Math.max(1, Math.min(noteDur - this.arpSamplesInNote, n - done)); + const freq = midiToHz(root + (pattern[this.arpNoteIdx % pattern.length] ?? 0)); + if (!this.arpVoice.active || Math.abs(this.arpVoice.target_freq - freq) > 1.0) this.arpVoice.noteOn(freq); + this.arpVoice.style = this.style; + + const rendered = this.arpVoice.render(chunk); + const gain = this.style === STYLE_AMBIENT ? 0.55 : 0.4; + for (let i = 0; i < chunk && done + i < n; i++) buf[done + i] = (rendered[i] ?? 0) * gain; + + this.arpSamplesInNote += chunk; + done += chunk; + // Advance to next note when current note is complete + if (this.arpSamplesInNote >= noteDur) { + this.arpSamplesInNote = 0; + this.arpNoteIdx = (this.arpNoteIdx + 1) % pattern.length; + this.arpVoice.noteOn(midiToHz(root + (pattern[this.arpNoteIdx % pattern.length] ?? 0))); + } + } + // Tame chiptune square harmonics; ambient sines don't need filtering + if (this.style === STYLE_CHIPTUNE) this.lpfArp.apply(buf, n, buf); + } + + // ── Noise layer ──────────────────────────────────────────────────── + + /** Percussive noise bursts triggered by population delta spikes (into buf.noise). */ + private renderNoise(n: number, dt: number): void { + const b = this.buf; + const out = b.noise; + const absDelta = Math.abs(this.snapshot.pop_delta); + // Trigger a new burst when pop_delta exceeds threshold. Require full + // completion + cooldown to prevent chain-firing flutter. + if (this.noiseBurstCooldown > 0) this.noiseBurstCooldown = Math.max(0.0, this.noiseBurstCooldown - dt); + if (absDelta >= this.noiseBurstThreshold && this.noiseBurstPhase >= 1.0 && this.noiseBurstCooldown <= 0) { + this.noiseBurstPhase = 0.0; + this.noiseBurstAmp = lerp(0.1, 0.5, Math.min(1.0, absDelta / 80.0)); + // Bigger events = longer bursts + this.noiseBurstDuration = lerp(0.08, 0.25, Math.min(1.0, absDelta / 100.0)); + // Cooldown: at least the burst duration + 100ms breathing room + this.noiseBurstCooldown = this.noiseBurstDuration + 0.1; + } + // If no burst active, return silence + if (this.noiseBurstPhase >= 1.0) { + out.fill(0, 0, n); + return; + } + + // Read from the pre-generated noise pool (circular buffer) + const pool = this.noisePool; + const idx = this.noisePoolIdx % pool.length; + const noise = b.noiseRaw; + for (let i = 0; i < n; i++) noise[i] = pool[(idx + i) % pool.length] ?? 0; + this.noisePoolIdx = (idx + n) % pool.length; + + // Per-sample envelope for the burst: attack (first 10%) then exponential decay + const burst = Math.max(0.01, this.noiseBurstDuration); + const attackEnd = burst * 0.1; + const t0 = this.noiseBurstPhase * burst; + const env = b.noiseEnv; + for (let i = 0; i < n; i++) { + const t = Math.fround(t0 + (i * (n / SAMPLE_RATE)) / n); + env[i] = t < attackEnd ? t / Math.max(0.001, attackEnd) : Math.exp((-6.0 * (t - attackEnd)) / burst); + } + + // Advance burst phase + this.noiseBurstPhase = Math.min(1.0, this.noiseBurstPhase + dt / burst); + + if (this.style === STYLE_CHIPTUNE) { + // NES-style: downsample for lo-fi crunch + for (let i = 0; i < n; i++) out[i] = (noise[i - (i % 8)] ?? 0) * (env[i] ?? 0) * this.noiseBurstAmp; + } else { + // Ambient: low-pass filtered wash (in place: the raw noise isn't needed after) + const filtered = this.lpfNoise.apply(noise, n, noise); + for (let i = 0; i < n; i++) out[i] = (filtered[i] ?? 0) * (env[i] ?? 0) * this.noiseBurstAmp * 0.6; + } + } + + // ── Status string for display ────────────────────────────────────── + + statusString(): string { + if (this.muted) return '[MUTE]'; + return `${this.style === STYLE_CHIPTUNE ? 'NES' : 'AMB'} ${this.volumePercent}%`; + } +} + +/** The status string, from the controls alone (the page mirrors them). */ +export function statusString(muted: boolean, style: Style, volume: number): string { + if (muted) return '[MUTE]'; + return `${style === STYLE_CHIPTUNE ? 'NES' : 'AMB'} ${Math.round(volume * 100)}%`; +} diff --git a/afterlife/src/music/worklet.ts b/afterlife/src/music/worklet.ts new file mode 100644 index 0000000..294d588 --- /dev/null +++ b/afterlife/src/music/worklet.ts @@ -0,0 +1,99 @@ +/** + * worklet.ts — where the music engine plays. + * + * life_music.py rendered on PyAudio's callback thread and read the latest + * SimulationSnapshot the main thread had swapped in. This is the same shape: + * the page posts snapshots and control changes, and the engine renders + * BUFFER_SIZE-frame blocks on the audio thread, handed out 128 frames at a + * time. vite.config.ts bundles this file on its own and inlines it into the + * page as a string, so it loads from a blob: URL without a second request. + * + * Nothing here allocates per quantum (the engine renders into scratch it + * owns), so the audio thread gives the garbage collector nothing to pause + * for. A block that comes out NaN — a NaN in a snapshot is enough, and one + * NaN in a filter's state would be the rest of the evening — is played as + * silence while the engine puts its state back. + */ + +import { BUFFER_SIZE, LifeMusicEngine, SAMPLE_RATE, type SimulationSnapshot, type Style } from './engine'; + +// The AudioWorkletGlobalScope, which lib.dom doesn't describe +declare abstract class AudioWorkletProcessor { + readonly port: MessagePort; + abstract process(inputs: Float32Array[][], outputs: Float32Array[][]): boolean; +} +declare function registerProcessor(name: string, ctor: new () => AudioWorkletProcessor): void; +declare const sampleRate: number; + +export type ToWorklet = + | { type: 'snapshot'; snap: SimulationSnapshot } + | { type: 'controls'; muted: boolean; volume: number; style: Style } + | { type: 'stop' }; + +class AfterlifeProcessor extends AudioWorkletProcessor { + private engine = new LifeMusicEngine(); + private left = new Float32Array(BUFFER_SIZE); + private right = new Float32Array(BUFFER_SIZE); + private pos = BUFFER_SIZE; // empty: render on the first quantum + private running = true; + private nanWarned = false; + + constructor() { + super(); + // Every pitch and time constant in the engine assumes SAMPLE_RATE; at + // another rate it all plays sharp or flat (audio.ts asks for SAMPLE_RATE) + if (typeof sampleRate === 'number' && sampleRate !== SAMPLE_RATE) { + console.warn(`afterlife music: context runs at ${sampleRate} Hz, the engine at ${SAMPLE_RATE} Hz — pitches will be off`); + } + this.port.onmessage = (e: MessageEvent) => { + const m = e.data; + if (m.type === 'snapshot') this.engine.update(m.snap); + else if (m.type === 'controls') { + this.engine.muted = m.muted; + this.engine.masterVolume = m.volume; + this.engine.setStyle(m.style); + } else if (m.type === 'stop') this.running = false; + }; + } + + process(_inputs: Float32Array[][], outputs: Float32Array[][]): boolean { + const out = outputs[0]; + const l = out?.[0]; + if (!out || !l) return this.running; + const r = out[1] ?? l; + let written = 0; + while (written < l.length) { + if (this.pos >= BUFFER_SIZE) { + this.renderBlock(); + this.pos = 0; + } + const take = Math.min(l.length - written, BUFFER_SIZE - this.pos); + // A copy loop, not set(subarray()), which makes two views a quantum + const src = this.pos - written; + for (let i = written; i < written + take; i++) l[i] = this.left[src + i] ?? 0; + if (r !== l) for (let i = written; i < written + take; i++) r[i] = this.right[src + i] ?? 0; + this.pos += take; + written += take; + } + return this.running; + } + + /** The next block into left/right, or silence (and a healed engine) if it came out NaN. */ + private renderBlock(): void { + const { left, right } = this; + this.engine.renderBlock(left, right, BUFFER_SIZE); + // tanh keeps every finite sample in [-1, 1], so the sum is finite unless one is NaN + let sum = 0; + for (let i = 0; i < BUFFER_SIZE; i++) sum += (left[i] ?? 0) + (right[i] ?? 0); + if (Number.isFinite(sum)) return; + left.fill(0); + right.fill(0); + this.engine.recover(); + if (!this.nanWarned) { + this.nanWarned = true; + console.warn('afterlife music: a block came out NaN; played silence and reset the engine state'); + } + } +} + +registerProcessor('afterlife-music', AfterlifeProcessor); diff --git a/afterlife/src/style.css b/afterlife/src/style.css new file mode 100644 index 0000000..efd5c37 --- /dev/null +++ b/afterlife/src/style.css @@ -0,0 +1,267 @@ +/** + * style.css — the chrome around the terminal. + * + * Imported by main.ts so vite emits a single afterlife.css for the dev page + * and the æthera template. Everything is scoped under #afterlife-root, + * because on the site this loads alongside tailwind and branding.css. + * + * The colours are the universe's own: the splash's logo is lit with the + * cell-age gradient (xterm 17 → 231, indigo through magenta and amber to + * white), and the background is the terminal's. + */ + +:root { + color-scheme: dark; +} + +#afterlife-root { + --bg: #0a0a0c; + --fg: rgb(200, 200, 200); + --dim: rgb(112, 112, 116); + --age: linear-gradient(90deg, #0000af, #0000ff, #5f00ff, #8700ff, #d700ff, #d700af, #ff5f87, #ff875f, #ffaf00, #ffd700, #ffffff, #ffd700, #ff5f87, #8700ff, #0000af); + position: fixed; + inset: 0; + display: grid; + place-items: center; + background: var(--bg); + color: var(--fg); + font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', 'DejaVu Sans Mono', Menlo, Consolas, monospace; + overflow: hidden; + touch-action: none; + user-select: none; + -webkit-user-select: none; +} + +#afterlife-root #term { + display: block; + cursor: crosshair; + image-rendering: pixelated; +} + +/* ── the splash: over the running universe, until the first gesture ── */ + +#afterlife-root #splash { + position: absolute; + inset: 0; + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.6rem; + padding: 1rem; + background: radial-gradient(ellipse at center, rgba(10, 10, 12, 0.62) 0%, rgba(10, 10, 12, 0.9) 70%); + cursor: pointer; + transition: opacity 1.4s ease; + outline: none; +} + +#afterlife-root #splash.gone { + opacity: 0; + pointer-events: none; +} + +#afterlife-root #splash .logo { + /* the site styles
 as a code block; this one is a sign */
+  margin: 0;
+  padding: 0;
+  border: 0;
+  border-radius: 0;
+  box-shadow: none;
+  overflow: visible;
+  font: inherit;
+  font-size: clamp(5px, 1.35vw, 14px);
+  line-height: 1.12;
+  background: var(--age);
+  background-size: 300% 100%;
+  -webkit-background-clip: text;
+  background-clip: text;
+  color: transparent;
+  animation: afterlife-age 24s linear infinite;
+  filter: drop-shadow(0 0 12px rgba(135, 0, 255, 0.25));
+}
+
+@keyframes afterlife-age {
+  from { background-position: 0% 0; }
+  to { background-position: 300% 0; }
+}
+
+#afterlife-root #splash h1 {
+  /* the logo says it; this is for readers without the art */
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  overflow: hidden;
+  clip-path: inset(50%);
+  margin: 0;
+}
+
+#afterlife-root #splash .sub {
+  margin: 0.4rem 0 0;
+  color: var(--fg);
+  font-size: 13px;
+  letter-spacing: 0.3em;
+}
+
+#afterlife-root #splash .go {
+  margin: 1.6rem 0 0;
+  color: var(--dim);
+  font-size: 11px;
+  letter-spacing: 0.12em;
+  text-align: center;
+  animation: afterlife-breathe 5.6s ease-in-out infinite;
+}
+
+#afterlife-root #splash .go .sep {
+  margin: 0 0.4em;
+}
+
+/* the ticker's own breath: sin(generation × 0.035) */
+@keyframes afterlife-breathe {
+  0%, 100% { opacity: 0.45; }
+  50% { opacity: 1; }
+}
+
+/* ── the one bit of site chrome kept, and the keys ── */
+
+#afterlife-root #corner {
+  position: absolute;
+  top: 10px;
+  right: 14px;
+  z-index: 11;
+  display: flex;
+  gap: 14px;
+  align-items: baseline;
+}
+
+#afterlife-root #corner a,
+#afterlife-root #corner button {
+  appearance: none;
+  border: 0;
+  padding: 2px 0;
+  background: none;
+  color: rgba(200, 200, 200, 0.28);
+  font: inherit;
+  font-size: 11px;
+  letter-spacing: 0.08em;
+  text-decoration: none;
+  cursor: pointer;
+  transition: color 0.6s ease;
+}
+
+#afterlife-root #corner a:hover,
+#afterlife-root #corner button:hover,
+#afterlife-root #corner button[aria-expanded='true'] {
+  color: rgba(200, 200, 200, 0.75);
+}
+
+#afterlife-root #keys {
+  position: absolute;
+  top: 34px;
+  right: 14px;
+  z-index: 11;
+  width: min(520px, calc(100vw - 28px));
+  max-height: calc(100% - 90px);
+  overflow: auto;
+  box-sizing: border-box;
+  padding: 14px 16px 12px;
+  border: 1px solid rgba(200, 200, 200, 0.12);
+  background: rgba(10, 10, 12, 0.94);
+  font-size: 12px;
+  touch-action: pan-y;
+  user-select: text;
+  -webkit-user-select: text;
+}
+
+#afterlife-root #keys[hidden] {
+  display: none;
+}
+
+#afterlife-root #keys .grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 2px 18px;
+}
+
+#afterlife-root #keys button {
+  appearance: none;
+  display: flex;
+  align-items: baseline;
+  gap: 10px;
+  padding: 3px 4px;
+  border: 0;
+  background: none;
+  color: var(--fg);
+  font: inherit;
+  text-align: left;
+  cursor: pointer;
+}
+
+#afterlife-root #keys button:hover,
+#afterlife-root #keys button:focus-visible {
+  background: rgba(200, 200, 200, 0.07);
+  outline: none;
+}
+
+#afterlife-root #keys kbd,
+#afterlife-root #keys .nokey {
+  min-width: 3.2em;
+  color: #ffaf00;
+  font: inherit;
+}
+
+#afterlife-root #keys .haunt kbd {
+  color: #d700ff;
+}
+
+#afterlife-root #keys p {
+  margin: 12px 4px 0;
+  color: var(--dim);
+  line-height: 1.55;
+}
+
+#afterlife-root #keys a {
+  color: var(--fg);
+}
+
+/* words for the pointer at hand: a phone has no clicks and no q */
+#afterlife-root .touch {
+  display: none;
+}
+
+@media (pointer: coarse) {
+  #afterlife-root .mouse {
+    display: none;
+  }
+  #afterlife-root .touch {
+    display: inline;
+  }
+}
+
+/* the splash still says its piece, just without the colour drift and the breath */
+@media (prefers-reduced-motion: reduce) {
+  #afterlife-root #splash,
+  #afterlife-root #splash .logo,
+  #afterlife-root #splash .go {
+    animation: none;
+    transition: none;
+  }
+  #afterlife-root #splash .go {
+    opacity: 0.8;
+  }
+}
+
+@media (max-width: 480px) {
+  #afterlife-root #keys .grid {
+    grid-template-columns: 1fr;
+  }
+}
+
+.afterlife-noscript {
+  position: fixed;
+  inset: auto 0 45% 0;
+  padding: 1rem;
+  color: #c8c8c8;
+  text-align: center;
+  font-family: monospace;
+}
diff --git a/afterlife/src/term/palette.ts b/afterlife/src/term/palette.ts
new file mode 100644
index 0000000..daa6633
--- /dev/null
+++ b/afterlife/src/term/palette.ts
@@ -0,0 +1,33 @@
+/**
+ * palette.ts — xterm's 256 colours, which is what afterlife was painted in.
+ *
+ * 0–15 are the terminal's own ANSI colours (xterm defaults here), 16–231 a
+ * 6×6×6 cube, 232–255 a grey ramp. Every colour the port draws goes through
+ * this, so the gradient, the ghosts and the haunted aliasing are exactly the
+ * numbers life.py asks curses for.
+ */
+
+const ANSI: readonly (readonly [number, number, number])[] = [
+  [0, 0, 0], [205, 0, 0], [0, 205, 0], [205, 205, 0], [0, 0, 238], [205, 0, 205], [0, 205, 205], [229, 229, 229],
+  [127, 127, 127], [255, 0, 0], [0, 255, 0], [255, 255, 0], [92, 92, 255], [255, 0, 255], [0, 255, 255], [255, 255, 255],
+];
+const CUBE = [0, 95, 135, 175, 215, 255];
+
+export function xterm(n: number): [number, number, number] {
+  if (n < 16) return [...(ANSI[n] ?? [0, 0, 0])] as [number, number, number];
+  if (n < 232) {
+    const i = n - 16;
+    return [CUBE[Math.floor(i / 36)] ?? 0, CUBE[Math.floor(i / 6) % 6] ?? 0, CUBE[i % 6] ?? 0];
+  }
+  const v = 8 + (n - 232) * 10;
+  return [v, v, v];
+}
+
+/** Packed little-endian RGBA (for a Uint32Array view over ImageData). */
+export function packed(rgb: readonly [number, number, number]): number {
+  return ((255 << 24) | (rgb[2] << 16) | (rgb[1] << 8) | rgb[0]) >>> 0;
+}
+
+export function css(rgb: readonly [number, number, number]): string {
+  return `rgb(${rgb[0]},${rgb[1]},${rgb[2]})`;
+}
diff --git a/afterlife/src/term/render.ts b/afterlife/src/term/render.ts
new file mode 100644
index 0000000..24bc3f6
--- /dev/null
+++ b/afterlife/src/term/render.ts
@@ -0,0 +1,329 @@
+/**
+ * render.ts — a terminal, drawn.
+ *
+ * afterlife is a curses program: a grid of character cells, each one an
+ * upper-half block (▀) whose foreground paints the top pixel and background
+ * the bottom, so every cell holds two pixels of the universe. This draws
+ * exactly that, in the same xterm colours, and keeps the terminal's grid for
+ * the status line: every character sits in its own column.
+ *
+ * The cells are drawn as pixels (an ImageData one texel per half-cell, scaled
+ * up without smoothing) rather than as ▀ glyphs, which is what a glyph would
+ * look like on a perfect font and is the same on every machine.
+ *
+ * `render()` is life.py's render() and _draw_stats_overlay(), case for case.
+ */
+
+import { GHOST_COLORS, GHOST_FRAMES, GRADIENT, HAUNT_DUAL, HAUNT_FRAMES, HAUNT_SINGLE, SPARKS, ZOOM_LABELS } from '../engine/constants';
+import type { InfiniteLife } from '../engine/life';
+import { NewsTicker, cols } from '../engine/ticker';
+import { css, packed, xterm } from './palette';
+
+/** The terminal's default background and foreground (curses' -1). */
+export const BG: [number, number, number] = [10, 10, 12];
+const FG = 'rgb(200,200,200)';
+const FG_DIM = 'rgb(112,112,116)';
+
+const NG = GRADIENT.length;
+const GRAD_PX = GRADIENT.map((c) => packed(xterm(c)));
+const GHOST_PX = GHOST_COLORS.map((c) => packed(xterm(c)));
+const HAUNT_SINGLE_PX = HAUNT_SINGLE.map(([f, b]) => [packed(xterm(f)), packed(xterm(b))] as const);
+const HAUNT_DUAL_PX = HAUNT_DUAL.map(([f, b]) => [packed(xterm(f)), packed(xterm(b))] as const);
+const BG_PX = packed(BG);
+
+/** Terminal fonts first: the status line was written for one. */
+export const TERM_FONT = 'ui-monospace, "SF Mono", "Cascadia Mono", "DejaVu Sans Mono", Menlo, Consolas, monospace';
+
+/** What the status line holds this frame (life.py builds it inside render()). */
+export interface Status {
+  left: string;
+  right: string;
+  tickerCol: number;
+  tickerWidth: number;
+}
+
+let lut: { maxAge: number; table: Uint8Array } | null = null;
+
+/** age → gradient index: log-scaled so the young get most of the colours */
+function colorLut(maxAge: number): Uint8Array {
+  if (lut && lut.maxAge === maxAge) return lut.table;
+  const table = new Uint8Array(maxAge + 1);
+  const logMax = Math.log1p(maxAge);
+  for (let a = 1; a <= maxAge; a++) table[a] = Math.min(Math.trunc((Math.log1p(a) / logMax) * (NG - 1)), NG - 1);
+  lut = { maxAge, table };
+  return table;
+}
+
+function fmt(n: number): string {
+  return n.toLocaleString('en-US');
+}
+
+export class Terminal {
+  /** device pixels per column; a row is two of these (square half-blocks) */
+  cw = 8;
+  cols = 80;
+  rows = 24;
+  /**
+   * Status rows at the bottom. One, as in the terminal, when it is wide
+   * enough for the ticker to have room between the stats and the keys;
+   * otherwise two (stats above, the ticker on a line of its own) rather than
+   * lose the ticker to the narrow-terminal fallback on every phone.
+   */
+  statusRows = 1;
+  /** show the key legend at the right of the status line */
+  keys = true;
+
+  private img: ImageData | null = null;
+  private widths = new Map();
+  private px: Uint32Array | null = null;
+  private off: HTMLCanvasElement | OffscreenCanvas | null = null;
+
+  constructor(private readonly ctx: CanvasRenderingContext2D) {}
+
+  /** Rows the universe gets (life.py's max_y - 1). */
+  get gridRows(): number {
+    return Math.max(1, this.rows - this.statusRows);
+  }
+
+  /** Size the terminal: a canvas of w × h device pixels, cells `cw` wide. */
+  resize(w: number, h: number, cw: number): void {
+    this.cw = Math.max(2, Math.round(cw));
+    this.cols = Math.max(20, Math.floor(w / this.cw));
+    this.rows = Math.max(6, Math.floor(h / (this.cw * 2)));
+    this.statusRows = this.cols >= 150 ? 1 : 2;
+    this.keys = this.cols >= 150;
+    this.img = null;
+    this.widths.clear();
+  }
+
+  /** The status line, as render() lays it out (and so the ticker's width). */
+  status(life: InfiniteLife, musicStatus: string): Status {
+    // a phone's status line can't spare 24 columns of history
+    const spark = life.sparkline(this.cols < 100 ? 10 : 24);
+    const cam = life.autoCam ? 'auto' : 'pan';
+    const zoom = ZOOM_LABELS[life.zoomLevel] ?? `${2 ** life.zoomLevel}x`;
+    const focus = life.autoFocusMode ? '[F]' : '';
+    const haunt = life.haunted ? '[HAUNTED]' : '';
+    const music = musicStatus ? ` ${musicStatus}` : '';
+    const left = `  ${life.epoch()}  gen ${fmt(life.generation)}  pop ${fmt(life.population())}  ${spark}`;
+    const legend = this.keys ? '  q r spc +/- arrows h z/x f g s  ' : '  ';
+    const right = `${music} ${haunt}${focus} ${zoom} ${cam}${legend}`;
+    if (this.statusRows === 1) {
+      // Ticker occupies the region between left stats and right controls
+      return { left, right, tickerCol: cols(left) + 1, tickerWidth: this.cols - cols(left) - cols(right) - 2 };
+    }
+    return { left, right, tickerCol: 1, tickerWidth: this.cols - 2 };
+  }
+
+  /** Paint one frame. */
+  render(life: InfiniteLife, st: Status, showStats: boolean): void {
+    const ctx = this.ctx;
+    const cw = this.cw;
+    const W = this.cols * cw;
+    const H = this.rows * cw * 2;
+    ctx.fillStyle = css(BG);
+    ctx.fillRect(0, 0, W, H);
+
+    this.paintGrid(life);
+
+    ctx.font = `${Math.round(cw * 1.62)}px ${TERM_FONT}`;
+    ctx.textBaseline = 'middle';
+    if (showStats) this.statsOverlay(life);
+
+    // ── Status bar (with scrolling news ticker) ──
+    const last = this.rows - 1;
+    if (this.statusRows === 1) {
+      if (st.tickerWidth < 10) {
+        // Terminal too narrow for ticker — compact fallback
+        this.text(`${st.left}  ${st.right}`.slice(0, this.cols - 1), last, 0, FG_DIM);
+      } else {
+        this.text(st.left, last, 0, FG_DIM);
+        this.ticker(life, last, st);
+        const rightCol = this.cols - cols(st.right);
+        this.text([...st.right].slice(0, this.cols - 1 - rightCol).join(''), last, rightCol, FG_DIM);
+      }
+    } else {
+      // two-line status: the stats and the switches, then the ticker below
+      const room = this.cols - 1;
+      const line = cols(st.left) + cols(st.right) <= room
+        ? st.left + ' '.repeat(room - cols(st.left) - cols(st.right)) + st.right
+        : `${st.left}  ${st.right}`;
+      this.text([...line].slice(0, room).join(''), last - 1, 0, FG_DIM);
+      this.ticker(life, last, st);
+    }
+  }
+
+  private paintGrid(life: InfiniteLife): void {
+    const grid = life.displayGrid();
+    const ages = life.displayAge();
+    const drawRows = Math.min(Math.floor(grid.h / 2), Math.floor(ages.h / 2), this.gridRows);
+    const drawCols = Math.min(grid.w, ages.w, this.cols);
+    const w = this.cols;
+    const h = this.gridRows * 2;
+    if (!this.img || this.img.width !== w || this.img.height !== h) {
+      this.img = new ImageData(w, h);
+      this.px = new Uint32Array(this.img.data.buffer);
+      this.off = typeof OffscreenCanvas !== 'undefined' ? new OffscreenCanvas(w, h) : Object.assign(document.createElement('canvas'), { width: w, height: h });
+    }
+    const px = this.px as Uint32Array;
+    px.fill(BG_PX);
+
+    // ages.max() works because alive ages > 0, ghost ages < 0, dead = 0
+    let maxAge = 1;
+    for (let i = 0; i < ages.data.length; i++) if ((ages.data[i] ?? 0) > maxAge) maxAge = ages.data[i] ?? 0;
+    const table = colorLut(maxAge);
+
+    // Disable ghost rendering when zoomed out — too much visual noise.
+    // Haunted mode inverts that judgment: the noise is the exhibit.
+    const haunted = life.haunted;
+    const showGhosts = life.zoomLevel >= 0 || haunted;
+    const gf = haunted ? HAUNT_FRAMES : GHOST_FRAMES;
+    const gidx = (a: number): number => Math.min(Math.max(-a - 1, 0), gf - 1);
+    const cidx = (a: number): number => table[Math.min(Math.max(a, 0), maxAge)] ?? 0;
+    const ghostTop = (i: number): number => (haunted ? (HAUNT_SINGLE_PX[i]?.[0] ?? 0) : (GHOST_PX[i] ?? 0));
+    const ghostBgOf = (i: number): number => (haunted ? (HAUNT_SINGLE_PX[i]?.[1] ?? 0) : BG_PX);
+
+    for (let r = 0; r < drawRows; r++) {
+      const gt = 2 * r * grid.w;
+      const gb = gt + grid.w;
+      const at = 2 * r * ages.w;
+      const ab = at + ages.w;
+      const pt = 2 * r * w;
+      const pb = pt + w;
+      for (let c = 0; c < drawCols; c++) {
+        const tAlive = (grid.data[gt + c] ?? 0) > 0;
+        const bAlive = (grid.data[gb + c] ?? 0) > 0;
+        const tAge = ages.data[at + c] ?? 0;
+        const bAge = ages.data[ab + c] ?? 0;
+        const tGhost = showGhosts && tAge < 0;
+        const bGhost = showGhosts && bAge < 0;
+        if (!(tAlive || bAlive || tGhost || bGhost)) continue;
+
+        let top = BG_PX;
+        let bot = BG_PX;
+        if ((tAlive || tGhost) && (bAlive || bGhost)) {
+          if (tAlive && bAlive) {
+            top = GRAD_PX[cidx(tAge)] ?? 0;
+            bot = GRAD_PX[cidx(bAge)] ?? 0;
+          } else if (tGhost && bGhost) {
+            const ti = gidx(tAge);
+            const bi = gidx(bAge);
+            if (haunted) {
+              const pair = HAUNT_DUAL_PX[ti * HAUNT_FRAMES + bi];
+              top = pair?.[0] ?? 0;
+              bot = pair?.[1] ?? 0;
+            } else {
+              top = GHOST_PX[ti] ?? 0;
+              bot = GHOST_PX[bi] ?? 0;
+            }
+          } else if (tAlive) {
+            top = GRAD_PX[cidx(tAge)] ?? 0; // ▀ on the default background
+          } else {
+            bot = GRAD_PX[cidx(bAge)] ?? 0; // ▄ on the default background
+          }
+        } else if (tAlive) {
+          top = GRAD_PX[cidx(tAge)] ?? 0;
+        } else if (bAlive) {
+          bot = GRAD_PX[cidx(bAge)] ?? 0;
+        } else if (tGhost) {
+          // ▀ in the ghost pair: its fg on top, its bg (the aliased colour,
+          // when haunted) below
+          const i = gidx(tAge);
+          top = ghostTop(i);
+          bot = ghostBgOf(i);
+        } else {
+          // ▄: the pair's fg below, its bg above
+          const i = gidx(bAge);
+          bot = ghostTop(i);
+          top = ghostBgOf(i);
+        }
+        px[pt + c] = top;
+        px[pb + c] = bot;
+      }
+    }
+
+    const off = this.off as HTMLCanvasElement | OffscreenCanvas;
+    const octx = off.getContext('2d') as CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D | null;
+    if (!octx || !this.img) return;
+    octx.putImageData(this.img, 0, 0);
+    const ctx = this.ctx;
+    ctx.imageSmoothingEnabled = false;
+    ctx.drawImage(off, 0, 0, w * this.cw, h * this.cw);
+  }
+
+  /** Draw visible ticker messages; they breathe between dim and normal. */
+  private ticker(life: InfiniteLife, row: number, st: Status): void {
+    const color = NewsTicker.dim(life.generation) ? FG_DIM : FG;
+    for (const msg of life.ticker.messages) {
+      const col = Math.trunc(msg.x);
+      const chars = [...msg.text];
+      // Clip to the ticker region
+      const visStart = Math.max(col, 0);
+      const visEnd = Math.min(col + chars.length, st.tickerWidth);
+      if (visStart >= visEnd) continue;
+      this.text(chars.slice(visStart - col, visEnd - col).join(''), row, st.tickerCol + visStart, color);
+    }
+  }
+
+  /** The engine telemetry panel in the bottom-right. */
+  private statsOverlay(life: InfiniteLife): void {
+    const census = life.lastCensus.size
+      ? [...life.lastCensus.entries()].sort((a, b) => b[1] - a[1]).map(([n, c]) => `${c}×${n}`).join(' ')
+      : 'none';
+    const panelW = 36;
+    const panelH = 11;
+    const x0 = this.cols - panelW - 2;
+    const y0 = this.rows - (this.statusRows - 1) - panelH - 2;
+    if (x0 < 0 || y0 < 0) return;
+    const lines = [
+      '─'.repeat(panelW - 2),
+      ' infinity engine',
+      ` pop floor  : ${fmt(life.popFloor)}`,
+      ` spread/150 : ${fmt(life.spread)}`,
+      ` cycle       : ${life.cyclePeriod === 0 ? 'none' : `period ${life.cyclePeriod}`}`,
+      ` injections  : ${life.totalInjections}`,
+      ` last event  : ${life.lastEvent || 'none'}`,
+      life.lastEvent ? `   @ gen     : ${fmt(life.lastEventGen)}` : '               ',
+      ` world       : ${life.worldH}x${life.worldW}`,
+      ` census      : ${[...census].slice(0, panelW - 16).join('')}`,
+      ` tempo       : ${(1 / Math.max(life.dilation, 0.01)).toFixed(2)}x`,
+    ];
+    const ctx = this.ctx;
+    ctx.fillStyle = css(BG);
+    ctx.fillRect(x0 * this.cw, y0 * this.cw * 2, panelW * this.cw, lines.length * this.cw * 2);
+    lines.forEach((line, i) => {
+      const padded = [...` ${line}`.padEnd(panelW)].slice(0, panelW).join('');
+      this.text(padded, y0 + i, x0, FG_DIM);
+    });
+  }
+
+  /** Write a string into the character grid, one glyph per column. */
+  text(s: string, row: number, col: number, color: string): void {
+    const ctx = this.ctx;
+    const cw = this.cw;
+    const y = row * cw * 2;
+    ctx.fillStyle = color;
+    let x = col;
+    for (const ch of s) {
+      if (x >= this.cols) break;
+      if (x >= 0 && ch !== ' ') {
+        const spark = SPARKS.indexOf(ch);
+        if (spark >= 0) {
+          // block elements drawn, not typeset: same on every font
+          const hh = Math.max(1, Math.round((cw * 2 * (spark + 1)) / 8));
+          ctx.fillRect(x * cw, y + cw * 2 - hh, cw, hh);
+        } else if (ch === '─') {
+          ctx.fillRect(x * cw, y + cw - Math.max(1, Math.round(cw / 8)) / 2, cw, Math.max(1, Math.round(cw / 8)));
+        } else {
+          let cw2 = this.widths.get(ch);
+          if (cw2 === undefined) {
+            cw2 = ctx.measureText(ch).width;
+            this.widths.set(ch, cw2);
+          }
+          ctx.fillText(ch, x * cw + cw / 2 - cw2 / 2, y + cw);
+        }
+      }
+      x++;
+    }
+  }
+}
diff --git a/afterlife/src/vite-env.d.ts b/afterlife/src/vite-env.d.ts
new file mode 100644
index 0000000..eadabd4
--- /dev/null
+++ b/afterlife/src/vite-env.d.ts
@@ -0,0 +1,5 @@
+/** The music worklet, bundled on its own and inlined as source (vite.config.ts). */
+declare module 'virtual:afterlife-worklet' {
+  const source: string;
+  export default source;
+}
diff --git a/afterlife/tsconfig.json b/afterlife/tsconfig.json
new file mode 100644
index 0000000..e7d1d38
--- /dev/null
+++ b/afterlife/tsconfig.json
@@ -0,0 +1,16 @@
+{
+  "compilerOptions": {
+    "target": "ES2022",
+    "module": "ESNext",
+    "moduleResolution": "bundler",
+    "lib": ["ES2022", "DOM", "DOM.Iterable"],
+    "strict": true,
+    "noUncheckedIndexedAccess": true,
+    "noImplicitOverride": true,
+    "noFallthroughCasesInSwitch": true,
+    "forceConsistentCasingInFileNames": true,
+    "skipLibCheck": true,
+    "noEmit": true
+  },
+  "include": ["src", "scripts"]
+}
diff --git a/afterlife/vite.config.ts b/afterlife/vite.config.ts
new file mode 100644
index 0000000..f6003ad
--- /dev/null
+++ b/afterlife/vite.config.ts
@@ -0,0 +1,54 @@
+import { resolve } from 'path';
+import { build as esbuild } from 'esbuild';
+import { defineConfig, type Plugin } from 'vite';
+
+/**
+ * afterlife builds straight into the blog's static tree, like syrinx, apeiron
+ * and oikos. The Docker image never runs npm, so the bundle in git *is* what
+ * deploys: rebuild and commit whenever src/ changes.
+ *
+ * `npm run dev` serves the standalone index.html (the universe persists in
+ * that origin's localStorage, not the site's).
+ */
+
+/**
+ * The music runs in an AudioWorklet, which has to be its own module. Rather
+ * than ship a second file (and teach an iife build about import.meta.url),
+ * the worklet is bundled here by esbuild — vite's own — and imported as a
+ * string; audio.ts loads it from a blob: URL.
+ */
+function workletInline(): Plugin {
+  const id = 'virtual:afterlife-worklet';
+  return {
+    name: 'afterlife-worklet',
+    resolveId: (source) => (source === id ? `\0${id}` : null),
+    async load(loaded) {
+      if (loaded !== `\0${id}`) return null;
+      const entry = resolve(__dirname, 'src/music/worklet.ts');
+      const out = await esbuild({ entryPoints: [entry], bundle: true, write: false, format: 'iife', minify: true, target: 'es2022' });
+      this.addWatchFile(entry);
+      this.addWatchFile(resolve(__dirname, 'src/music/engine.ts'));
+      return `export default ${JSON.stringify(out.outputFiles[0]?.text ?? '')};`;
+    },
+  };
+}
+
+export default defineConfig({
+  plugins: [workletInline()],
+  server: { port: 5198, strictPort: true },
+  build: {
+    outDir: resolve(__dirname, '../aethera/static/afterlife'),
+    emptyOutDir: false,
+    rollupOptions: {
+      input: resolve(__dirname, 'src/main.ts'),
+      output: {
+        entryFileNames: 'afterlife.js',
+        assetFileNames: 'afterlife.[ext]',
+        format: 'iife',
+      },
+    },
+    // a real afterlife.css to , so the splash is styled before the JS parses
+    cssCodeSplit: false,
+    sourcemap: false,
+  },
+});
diff --git a/oikos/README.md b/oikos/README.md
index 16cc733..b1b3468 100644
--- a/oikos/README.md
+++ b/oikos/README.md
@@ -28,6 +28,7 @@ nothing to watch:
 | dreams api | re-types the real `/api/dreams/status` JSON each poll |
 | apeiron | real prompts composed from apeiron's own `templates.json` / `components.json` |
 | syrinx | **your** creature, read from this origin's localStorage (name, age, topology) |
+| afterlife | **your** universe, run live by afterlife's own engine and terminal renderer (`../afterlife/src`), or a genesis of its own |
 | irc | the live `/ws/irc` broadcast, formatted as `irc.js` formats it |
 | parlor | Kleros' real board, districts and prices; a toy game on it |
 | dream_gen | the core-loop diagram from its README, with the real keyframe and prompt |
diff --git a/oikos/src/data.ts b/oikos/src/data.ts
index eab3e9d..51b68cb 100644
--- a/oikos/src/data.ts
+++ b/oikos/src/data.ts
@@ -16,10 +16,14 @@
  *   irc        /ws/irc replays a pre-generated bank; one more listener is free.
  *   syrinx     the creature lives in this origin's localStorage, so the room
  *              can show *your* creature, if you have woken one.
+ *   afterlife  the universe is saved in this origin's localStorage too, so the
+ *              room can run *yours* on its screen.
  *   apeiron    the grammar ships as two small JSON files; the screen composes
  *              real prompts from it.
  */
 
+import { KEY as AFTERLIFE_KEY } from '../../afterlife/src/engine/persist';
+
 export interface Site {
   id: string;
   title: string;
@@ -175,6 +179,67 @@ export function readCreature(): Creature | null {
   }
 }
 
+// ---- afterlife --------------------------------------------------------------
+
+/** What the room needs to know about a saved universe (the screen loads the rest). */
+export interface UniverseInfo {
+  generation: number;
+  bornAt: number;
+  savedAt: number;
+}
+
+/** Everything before the cells blob: persist.ts writes the keys in order,
+ *  cells (up to 1.5 MB) last, so this is where the numbers are. */
+function headOf(raw: string): string {
+  const i = raw.indexOf('"cells"');
+  return i < 0 ? raw : raw.slice(0, i);
+}
+
+/** Read from a stored universe's raw text, without parsing the cells. */
+function universeFrom(raw: string | null): UniverseInfo | null {
+  if (!raw) return null;
+  const head = headOf(raw);
+  const num = (k: string): number | undefined => {
+    const m = new RegExp(`"${k}":(\\d+)`).exec(head);
+    return m ? Number(m[1]) : undefined;
+  };
+  let generation = num('generation');
+  let savedAt = num('savedAt');
+  let bornAt = num('bornAt');
+  if (generation === undefined || savedAt === undefined) {
+    // not the layout we know: pay for the full parse once
+    try {
+      const s = JSON.parse(raw) as { generation?: unknown; bornAt?: unknown; savedAt?: unknown };
+      generation = typeof s.generation === 'number' ? s.generation : undefined;
+      savedAt = typeof s.savedAt === 'number' ? s.savedAt : undefined;
+      bornAt = typeof s.bornAt === 'number' ? s.bornAt : undefined;
+    } catch {
+      return null;
+    }
+    if (generation === undefined || savedAt === undefined) return null;
+  }
+  return { generation, bornAt: bornAt ?? savedAt, savedAt };
+}
+
+let lastUniverse: { len: number; head: string; info: UniverseInfo | null } | null = null;
+
+export function readUniverse(): UniverseInfo | null {
+  let raw: string | null;
+  try {
+    // throws where site data is blocked
+    raw = localStorage.getItem(AFTERLIFE_KEY);
+  } catch {
+    return null;
+  }
+  if (!raw) return null;
+  // the same save as last time: same length, same head (which holds savedAt)
+  const head = headOf(raw);
+  if (lastUniverse && lastUniverse.len === raw.length && lastUniverse.head === head) return lastUniverse.info;
+  const info = universeFrom(raw);
+  lastUniverse = { len: raw.length, head, info };
+  return info;
+}
+
 // ---- apeiron ----------------------------------------------------------------
 
 export interface ApeironGrammar {
@@ -213,6 +278,7 @@ export class Feeds {
   });
   readonly irc = new Signal({ connected: false, lines: [], collapse: null, fragments: 0, version: 0 });
   readonly creature = new Signal(readCreature());
+  readonly universe = new Signal(readUniverse());
   readonly apeiron = new Signal(null);
 
   private timers: number[] = [];
@@ -232,16 +298,30 @@ export class Feeds {
     every(() => void this.pollChronicle(), 120_000);
     // a creature woken in another tab should appear without a reload
     every(() => this.creature.set(readCreature()), 15_000);
+    // and a universe left at /afterlife in another tab (or the tuned-in
+    // frame): the storage event says so as it happens; the poll is a fallback
+    window.addEventListener('storage', this.onStorage);
+    every(() => this.setUniverse(readUniverse()), 60_000);
     this.openIrc();
     if (!this.apeiron.value) void this.loadApeiron();
   }
 
+  /** Fires only for writes from other documents; key null means cleared. */
+  private readonly onStorage = (e: StorageEvent): void => {
+    if (e.key === AFTERLIFE_KEY || e.key === null) this.setUniverse(readUniverse());
+  };
+
+  private setUniverse(u: UniverseInfo | null): void {
+    if (u?.savedAt !== this.universe.value?.savedAt) this.universe.set(u);
+  }
+
   /** Hidden tab: stop listening. Nothing here should cost the site anything
    *  while nobody is looking. */
   stop(): void {
     this.running = false;
     for (const t of this.timers) clearInterval(t);
     this.timers = [];
+    window.removeEventListener('storage', this.onStorage);
     clearTimeout(this.wsTimer);
     if (this.ws) {
       this.ws.onclose = null;
diff --git a/oikos/src/screens/afterlife.ts b/oikos/src/screens/afterlife.ts
new file mode 100644
index 0000000..aa65e46
--- /dev/null
+++ b/oikos/src/screens/afterlife.ts
@@ -0,0 +1,70 @@
+/**
+ * afterlife — the real universe, on a small terminal.
+ *
+ * Not a picture of it: this is afterlife's own engine and terminal renderer
+ * (imported from ../afterlife/src), on a 64×24 terminal the size of the tube,
+ * at no more than GENS_PER_SEC generations a second however fast the screen
+ * is drawn. If this browser has a universe saved at /afterlife, the screen
+ * adopts it, generation and all, and the ticker says so; otherwise it runs a
+ * genesis of its own. The world here is only 5× the view (life.py's 400×800
+ * floor would cost a 320k-cell step per frame for a 64-column tube), so a
+ * bigger save is cropped to its centre, in memory. It never saves, so that
+ * crop never reaches yours: it only moves when you're at /afterlife (the
+ * screen is a view of the place, like syrinx's screen is of the creature).
+ * No sound here either.
+ */
+
+import { InfiniteLife } from '../../../afterlife/src/engine/life';
+import { load } from '../../../afterlife/src/engine/persist';
+import { Terminal } from '../../../afterlife/src/term/render';
+import type { Site, UniverseInfo } from '../data';
+import { H, Screen, W, type ScreenEnv } from './screen';
+
+/** Focused screens draw at 30 fps; the universe doesn't need to keep up. */
+const GENS_PER_SEC = 15;
+/** a small world: 5× the view, not life.py's floor */
+const SMALL_WORLD = { minH: 0, minW: 0 };
+
+export class AfterlifeScreen extends Screen {
+  private term: Terminal;
+  private life: InfiniteLife | null = null;
+  private seenSave: UniverseInfo | null | undefined = undefined;
+  private genAcc = 1 / GENS_PER_SEC; // step on the first draw
+
+  constructor(site: Site, env: ScreenEnv) {
+    super(site, env);
+    this.fps = 15;
+    this.term = new Terminal(this.ctx);
+    this.term.resize(W, H, 8);
+  }
+
+  private sync(): void {
+    const info = this.env.feeds.universe.value;
+    if (this.life && info?.savedAt === this.seenSave?.savedAt) return;
+    this.seenSave = info;
+    const save = info ? load() : null;
+    const life = new InfiniteLife(this.term.gridRows, this.term.cols, !save, SMALL_WORLD);
+    if (save) {
+      life.adopt(save);
+      life.ticker.queueSpecial(`the universe remembers generation ${life.generation.toLocaleString('en-US')}`);
+    }
+    this.life = life;
+  }
+
+  protected draw(_t: number, dt: number): void {
+    this.sync();
+    const life = this.life;
+    if (!life) return;
+    // one generation at most per draw; the remainder carries (so 30 fps
+    // gives 15/s, not 10), but a slow or hidden spell is dropped, not replayed
+    const period = 1 / GENS_PER_SEC;
+    this.genAcc += dt;
+    if (this.genAcc < period) return;
+    this.genAcc = Math.min(this.genAcc - period, period);
+    life.step();
+    if (life.generation > 0 && life.generation % 150 === 0) life.takeCensus();
+    const st = this.term.status(life, '');
+    if (st.tickerWidth >= 10) life.tickTicker(st.tickerWidth);
+    this.term.render(life, st, false);
+  }
+}
diff --git a/oikos/src/screens/index.ts b/oikos/src/screens/index.ts
index 2642891..b346a25 100644
--- a/oikos/src/screens/index.ts
+++ b/oikos/src/screens/index.ts
@@ -5,6 +5,7 @@
  */
 
 import type { Site } from '../data';
+import { AfterlifeScreen } from './afterlife';
 import { ApeironScreen } from './apeiron';
 import { ChronicleScreen } from './chronicle';
 import { DreamGenScreen } from './dreamGen';
@@ -26,6 +27,7 @@ const PAINTERS: Record Screen> = {
   'dreams-api': DreamsApiScreen,
   apeiron: ApeironScreen,
   syrinx: SyrinxScreen,
+  afterlife: AfterlifeScreen,
   irc: IrcScreen,
   parlor: ParlorScreen,
   dream_gen: DreamGenScreen,
diff --git a/oikos/src/world/layout.ts b/oikos/src/world/layout.ts
index 429e4aa..06582eb 100644
--- a/oikos/src/world/layout.ts
+++ b/oikos/src/world/layout.ts
@@ -34,6 +34,7 @@ export const PLACEMENTS: Record = {
   apeiron: { angle: -54, r: 4.2, y: 0, style: 'black', screenW: 1.15, channel: 5 },
   irc: { angle: -54, r: 4.2, y: 0, style: 'beige', screenW: 0.95, on: 'apeiron', channel: 6 },
   syrinx: { angle: 54, r: 4.2, y: 0.28, style: 'grey', screenW: 1.08, channel: 7 },
+  afterlife: { angle: 78, r: 3.9, y: 0, style: 'black', screenW: 1.02, channel: 10 },
   dream_gen: { angle: -20, r: 7.0, y: 4.35, style: 'grey', screenW: 1.02, hang: true, feeds: 'dreams', channel: 8 },
   parlor: { angle: 20, r: 6.5, y: 3.6, style: 'beige', screenW: 1.1, hang: true, channel: 9 },
 };
diff --git a/oikos/src/xp/pane.ts b/oikos/src/xp/pane.ts
index 79a0a73..0748c29 100644
--- a/oikos/src/xp/pane.ts
+++ b/oikos/src/xp/pane.ts
@@ -159,6 +159,7 @@ export class Pane {
     const s = this.site;
     if (s.id === 'dreams') return 'Opening dreams wakes the dreamer: a GPU starts up while anyone is watching and goes back to sleep after. The frame here is the last one the chronicle kept.';
     if (s.id === 'syrinx') return 'Syrinx makes sound once you wake it. The creature here is read from this browser; nobody else sees yours.';
+    if (s.id === 'afterlife') return 'afterlife makes music once you click in. The universe on this screen is the one saved in this browser, if you have visited; nobody else sees yours.';
     if (s.group === 'wired') return `${s.title} is not on this server; it opens in a new window.`;
     return null;
   }
@@ -193,6 +194,12 @@ export class Pane {
         text = c ? `yours: ${c.name}` : 'not woken in this browser';
         break;
       }
+      case 'afterlife': {
+        const u = feeds.universe.value;
+        on = !!u;
+        text = u ? `yours: generation ${u.generation.toLocaleString('en-US')}` : 'no universe in this browser yet';
+        break;
+      }
       default:
         return;
     }
diff --git a/oikos/src/xp/shell.ts b/oikos/src/xp/shell.ts
index 166e12e..5d343f8 100644
--- a/oikos/src/xp/shell.ts
+++ b/oikos/src/xp/shell.ts
@@ -50,7 +50,8 @@ export const RELATED: Record = {
   dream_gen: ['dreams', 'chronicle'],
   transmissions: ['irc', 'syrinx'],
   apeiron: ['dreams', 'syrinx'],
-  syrinx: ['apeiron', 'transmissions'],
+  syrinx: ['apeiron', 'afterlife'],
+  afterlife: ['syrinx', 'apeiron'],
   irc: ['transmissions', 'parlor'],
   parlor: ['irc', 'transmissions'],
 };
diff --git a/tests/test_afterlife.py b/tests/test_afterlife.py
new file mode 100644
index 0000000..93d0f46
--- /dev/null
+++ b/tests/test_afterlife.py
@@ -0,0 +1,41 @@
+from pathlib import Path
+
+from fastapi.testclient import TestClient
+
+from aethera.api.oikos import SITES
+
+STATIC = Path(__file__).resolve().parents[1] / "aethera" / "static" / "afterlife"
+
+
+def test_afterlife_page_renders(client: TestClient):
+    r = client.get("/afterlife")
+    assert r.status_code == 200
+    assert "/static/afterlife/afterlife.js" in r.text
+    assert "/static/afterlife/afterlife.css" in r.text
+    # the terminal and the splash are markup, so they paint before the bundle parses
+    for el in ('id="afterlife-root"', 'id="term"', 'id="splash"', 'id="keys"'):
+        assert el in r.text, el
+    # leaving must be a real navigation (pagehide saves the universe)
+    assert 'id="afterlife-home" href="/" hx-boost="false"' in r.text
+
+
+def test_bundle_is_committed():
+    """The Docker image never runs npm; the built page must be in the tree."""
+    for name in ("afterlife.js", "afterlife.css"):
+        assert (STATIC / name).is_file(), name
+    # the music worklet is inlined into the bundle, not a second file
+    assert "afterlife-music" in (STATIC / "afterlife.js").read_text()
+
+
+def test_afterlife_is_on_the_map(client: TestClient):
+    assert "/afterlife" in client.get("/sitemap.xml").text
+    assert "/afterlife" in client.get("/urls.txt").text
+    site = next(s for s in SITES if s["id"] == "afterlife")
+    assert site["href"] == "/afterlife" and site["tune"] is True
+
+
+def test_afterlife_frames_only_for_aethera(client: TestClient):
+    """oikos tunes the page in on its screen; nobody else may frame it."""
+    r = client.get("/afterlife")
+    assert r.headers["content-security-policy"] == "frame-ancestors 'self'"
+    assert r.headers["x-frame-options"] == "SAMEORIGIN"