@norbert_bodziony/bloub 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ import{Dynamic as e,createComponent as t,escape as n,mergeProps as r,ssr as i,ssrAttribute as a,ssrHydrationKey as o,ssrStyle as s}from"solid-js/web";import{For as c,Show as l,createEffect as u,createMemo as d,createSignal as f,createUniqueId as p,mergeProps as m,on as h,onCleanup as g,onMount as _}from"solid-js";var v=Math.PI*2,y=(e,t=0,n=1)=>e<t?t:e>n?n:e,b=(e,t,n)=>e+(t-e)*n,x={easeOutCubic:e=>1-(1-e)**3,easeInOutCubic:e=>e<.5?4*e**3:1-(-2*e+2)**3/2,easeOutQuint:e=>1-(1-e)**5};function S(e,t,n=0){let r=e/t*v;return .55*Math.sin(r+n)+.3*Math.sin(2*r+n*1.7+1.1)+.15*Math.sin(3*r+n*2.3+2.4)}function C(e){let t=e>>>0;return()=>{t=t+1831565813>>>0;let e=Math.imul(t^t>>>15,1|t);return e=e+Math.imul(e^e>>>7,61|e)^e,((e^e>>>14)>>>0)/4294967296}}var w=e=>Math.round(e*100)/100;function T(e,t=.55,n=.62){let r=(e%360+360)%360,i=(1-Math.abs(2*n-1))*t,a=i*(1-Math.abs(r/60%2-1)),o=n-i/2,[s,c,l]=r<60?[i,a,0]:r<120?[a,i,0]:r<180?[0,i,a]:r<240?[0,a,i]:r<300?[a,0,i]:[i,0,a],u=e=>Math.round((e+o)*255).toString(16).padStart(2,`0`);return`#${u(s)}${u(c)}${u(l)}`}function ee(e,t,n,r,i=1){let a=e.phase+t*e.speed*v,o=Math.cos(e.tilt),s=Math.sin(e.tilt),c=Math.sqrt(Math.max(0,1-e.k*e.k)),l=e.sweep*v,u=``,d=``,f=null;for(let t=0;t<=64;t++){let r=a+t/64*l,i=Math.cos(r),p=Math.sin(r),m=e.a*(i*o+p*-s*e.k)+e.cx,h=e.a*(i*s+p*o*e.k)+e.cy,g=e.a*p*c<0,_=w(m*n),v=w(h*n),y=g===f?`L`:`M`;g?d+=`${y}${_} ${v}`:u+=`${y}${_} ${v}`,f=g}let p=Math.cos(e.tilt)*e.a*n,m=Math.sin(e.tilt)*e.a*n;return{id:r,front:u,back:d,width:e.width*n,opacity:i,grad:{x1:w(e.cx*n-p),y1:w(e.cy*n-m),x2:w(e.cx*n+p),y2:w(e.cy*n+m),stops:[T(e.hue),T(e.hue+e.hueSpan*.5),T(e.hue+e.hueSpan)]}}}var E=C(659918),D=Array.from({length:6},(e,t)=>({a:1.3+E()*.1,k:.05+E()*.4,tilt:t/6*Math.PI+E()*.5,speed:3+E()*.7,phase:E()*v,sweep:.6+E()*.25,hue:t*360/6+E()*30,hueSpan:60+E()*60,width:.05+E()*.012,cx:0,cy:.1})),O=Array.from({length:4},(e,t)=>({a:.78+t*.2,k:.05+t*.02,tilt:-.62+t*.05,speed:.3,phase:.06*t,sweep:.4,hue:95+t*62,hueSpan:100,width:.05,cx:0,cy:-.12})),te=[-.557,-.013,.532],k=.165,ne=1.25,re=C(48879),ie=Array.from({length:5},(e,t)=>({birth:t*.2,angle:re()*v,rho:.58+re()*.18}));function ae(e,t){let n=[];for(let r of ie){let i=e-r.birth;if(i<0||i>.62)continue;let a=r.rho*.75**(i*10),o=r.angle+i*100*Math.PI/180;n.push({x:Math.cos(o)*a*t,y:Math.sin(o)*a*t,r:(.04+.028*y(i/.55))*t,depth:y(1-a/.8),opacity:y(i/.06)*y((.62-i)/.08)})}return n}var oe=C(49383),A=Array.from({length:4},(e,t)=>{let n=t-1.5;return{a:.85*(1+n*.03),k:.15/.85*(1+n*.16),tilt:34*Math.PI/180+n*.035,speed:210/360,phase:-t*.045+oe()*.012,sweep:.34,hue:t*85+oe()*20,hueSpan:80,width:.095,cx:0,cy:0}}),j=.129,se=`#2496e8`,M=1.003,N=.15,ce=1.14,le=.054,P=15.46,F=.186,I=.412,L={yaw:28.49,pitch:28.62,roll:-13},R=e=>e*Math.PI/180;function z(e,t,n){let r=Math.cos(n),i=Math.sin(n);return[[e[0]*r+t[0]*i,e[1]*r+t[1]*i,e[2]*r+t[2]*i],[t[0]*r-e[0]*i,t[1]*r-e[1]*i,t[2]*r-e[2]*i]]}function B(e,t,n=P){let r=[0,0,1],i=[1,0,0],a=[0,1,0];[r,i]=z(r,i,R(e.yaw)),[a,r]=z(a,r,R(e.pitch)),[i,a]=z(i,a,R(e.roll));let o=e=>{let[o,s]=z(r,i,R(n*e));return{x:o[0]*t,y:o[1]*t,a:s[0],b:s[1],c:a[0],d:a[1],depth:o[2]}};return[o(-1),o(1)]}var ue=C(24301),de=(()=>{let e=[],t=1.4;for(;t<900;)e.push(t),t+=1.9+ue()*2.7,ue()<.18&&(e.push(t),t+=.24);return e})(),fe=.18;function pe(e){for(let t=0;t<de.length;t++){let n=de[t];if(e<n)break;let r=(e-n)/fe;if(r>=0&&r<=1)return r<.45?1-r/.45:(r-.45)/.55}return 1}function me(e,t={}){let{wander:n=1,blink:r=!0,float:i=!0}=t;return{dYaw:(S(e,11.3,.4)*5.5+S(e,3.7,2.1)*1.6)*n,dPitch:(S(e,9.1,1.3)*4.2+S(e,4.3,.7)*1.3)*n,dRoll:S(e,13.7,3.2)*2.2*n,lid:r?pe(e):1,driftX:i?S(e,7.9,1.9)*.006:0,driftY:i?S(e,5.3,.3)*.007:0,breath:i?1+Math.sin(e/3.4*Math.PI*2)*.005:1}}function he(e){return .06+.94*y(e)}var V=(e,t,n=0,r=1)=>({w:e,h:t,tilt:n,open:r}),H=(e,t,n=0,r=1)=>[V(e,t,n,r),V(e,t,-n,r)],U=[{id:`neutre`,gaze:{...L},split:P,eyes:[V(F,I),V(F,I)]},{id:`attentif`,gaze:{yaw:4,pitch:5,roll:-4},split:16,eyes:H(.21,.44)},{id:`surpris`,gaze:{yaw:3,pitch:-3,roll:0},split:19,eyes:H(.45,.47)},{id:`excite`,gaze:{yaw:6,pitch:-14,roll:0},split:19.5,eyes:H(.4,.56,-10)},{id:`heureux`,gaze:{yaw:5,pitch:9,roll:0},split:17,eyes:H(.27,.17,14)},{id:`hilare`,gaze:{yaw:4,pitch:14,roll:0},split:18,eyes:H(.34,.13,20)},{id:`colere`,gaze:{yaw:3,pitch:7,roll:0},split:17,eyes:H(.34,.15,30)},{id:`triste`,gaze:{yaw:3,pitch:-13,roll:0},split:16,eyes:H(.22,.4,-28)},{id:`effraye`,gaze:{yaw:2,pitch:-20,roll:0},split:20.5,eyes:H(.4,.6)},{id:`mefiant`,gaze:{yaw:12,pitch:6,roll:-6},split:16,eyes:[V(.21,.4),V(.22,.15)]},{id:`confus`,gaze:{yaw:-14,pitch:3,roll:8},split:16.5,eyes:[V(.2,.44,-18),V(.28,.17,14)]},{id:`curieux`,gaze:{yaw:16,pitch:-9,roll:-15},split:16.5,eyes:[V(.24,.46,-8),V(.2,.38,-8)]},{id:`fier`,gaze:{yaw:5,pitch:17,roll:0},split:17,eyes:H(.3,.15,18)},{id:`timide`,gaze:{yaw:-19,pitch:-14,roll:-7},split:14,eyes:H(.17,.3)},{id:`blase`,gaze:{yaw:-22,pitch:2,roll:0},split:16,eyes:H(.3,.12)},{id:`somnolent`,gaze:{yaw:6,pitch:-9,roll:-3},split:16,eyes:H(.2,.42,0,.42)}],ge=new Map(U.map(e=>[e.id,e])),_e=`neutre`,ve=(e,t,n)=>({w:b(e.w,t.w,n),h:b(e.h,t.h,n),tilt:b(e.tilt??0,t.tilt??0,n),open:b(e.open,t.open,n)});function ye(e,t,n){return{id:t.id,gaze:{yaw:b(e.gaze.yaw,t.gaze.yaw,n),pitch:b(e.gaze.pitch,t.gaze.pitch,n),roll:b(e.gaze.roll,t.gaze.roll,n)},split:b(e.split,t.split,n),eyes:[ve(e.eyes[0],t.eyes[0],n),ve(e.eyes[1],t.eyes[1],n)]}}var be={egg:[.8369,.8424,.8497,.8585,.8674,.8775,.8878,.8983,.9089,.9185,.9288,.9374,.9445,.9504,.9543,.9559,.9555,.9519,.9466,.9389,.9302,.9193,.9085,.8969,.8852,.8734,.8625,.8513,.8411,.8325,.8243,.8179,.8137,.8112,.8102,.8128,.8178,.8262,.8374,.8518,.8702,.8922,.9169,.9446,.9741,1.0023,1.0267,1.0433,1.0481,1.0393,1.0216,.997,.9697,.9418,.9169,.8949,.876,.8604,.849,.8394,.8337,.8314,.8305,.8326],hexagon:[.921,.9282,.9441,.9706,.9984,1.0059,.9896,.9562,.929,.9124,.9047,.9058,.9157,.9349,.9642,.9873,.9882,.9665,.9336,.9105,.8968,.8918,.8955,.908,.9293,.9611,.982,.9812,.959,.9282,.9089,.8978,.8964,.9026,.9189,.9439,.9778,.999,.9964,.9713,.9439,.9274,.9196,.9206,.9308,.9502,.9799,1.0121,1.0226,1.0071,.9752,.951,.9366,.9316,.9351,.9485,.9711,1.0026,1.0213,1.0155,.9863,.9547,.9347,.9232],triangle:[.7819,.8211,.8747,.944,1.0223,1.096,1.1401,1.134,1.0808,1.0047,.9265,.8603,.8104,.773,.745,.7273,.7151,.7118,.7148,.7245,.7427,.768,.8037,.8518,.9148,.9876,1.0583,1.1073,1.1109,1.0667,.994,.9164,.8482,.7948,.7555,.7261,.7056,.6925,.6859,.6869,.6938,.7084,.7305,.7615,.804,.8595,.9311,1.0092,1.0791,1.1171,1.1054,1.0501,.9779,.905,.845,.799,.7656,.7413,.7258,.716,.7146,.7204,.733,.7528]},W=Array.from({length:64},(e,t)=>t/64*v),G=W.map(Math.cos),K=W.map(Math.sin);function xe(e,t={}){return{radii:[...be[e]],rot:0,cx:0,cy:0,sx:1,sy:1,...t}}function q(e,t={}){return{radii:Array(64).fill(e),rot:0,cx:0,cy:0,sx:1,sy:1,...t}}function Se(e,t,n,r){let i=r??{radii:Array(64),rot:0,cx:0,cy:0,sx:1,sy:1};for(let r=0;r<64;r++)i.radii[r]=b(e.radii[r]??1,t.radii[r]??1,n);let a=t.rot-e.rot;for(;a>Math.PI;)a-=v;for(;a<-Math.PI;)a+=v;return i.rot=e.rot+a*n,i.cx=b(e.cx,t.cx,n),i.cy=b(e.cy,t.cy,n),i.sx=b(e.sx,t.sx,n),i.sy=b(e.sy,t.sy,n),i}function Ce(e,t,n=[]){let r=Math.cos(e.rot),i=Math.sin(e.rot);for(let a=0;a<64;a++){let o=e.radii[a]??1,s=o*(G[a]??0),c=o*(K[a]??0),l=s*r-c*i,u=s*i+c*r,d=n[a]??{x:0,y:0};d.x=(l*e.sx+e.cx)*t,d.y=(u*e.sy+e.cy)*t,n[a]=d}return n.length=64,n}function we(e,t=1/6){let n=e.length;if(n<3)return``;let r=e[0],i=`M${w(r.x)} ${w(r.y)}`;for(let r=0;r<n;r++){let a=e[(r-1+n)%n],o=e[r],s=e[(r+1)%n],c=e[(r+2)%n],l=o.x+(s.x-a.x)*t,u=o.y+(s.y-a.y)*t,d=s.x-(c.x-o.x)*t,f=s.y-(c.y-o.y)*t;i+=`C${w(l)} ${w(u)} ${w(d)} ${w(f)} ${w(s.x)} ${w(s.y)}`}return`${i}Z`}function J(e,t,n){let r=Array(64).fill(0),i=e.length;for(let a=0;a<64;a++){let o=G[a]??0,s=K[a]??0,c=0;for(let r=0;r<i;r++){let a=e[r],l=e[(r+1)%i],u=l.x-a.x,d=l.y-a.y,f=o*d-s*u;if(Math.abs(f)<1e-9)continue;let p=a.x-t,m=a.y-n,h=(p*d-m*u)/f,g=(p*s-m*o)/f;h>c&&g>=0&&g<=1&&(c=h)}r[a]=c}return r}function Y(e,t,n,r,i,a,o=96){let s=r-e,c=i-t,l=Math.hypot(s,c)||1e-6,u=Math.atan2(c,s),d=Math.acos(Math.max(-1,Math.min(1,(n-a)/l))),f=[];for(let r=0;r<=o/2;r++){let i=u+d+(v-2*d)*r/(o/2);f.push({x:e+Math.cos(i)*n,y:t+Math.sin(i)*n})}for(let e=0;e<=o/2;e++){let t=u-d+2*d*e/(o/2);f.push({x:r+Math.cos(t)*a,y:i+Math.sin(t)*a})}return f}function Te(e,t){let n=e.length,r=(t/v%1+1)%1*n,i=Math.floor(r);return b(e[i%n]??1,e[(i+1)%n]??1,r-i)}function Ee(e,t=1,n=1){return W.map((r,i)=>(Math.abs((G[i]??0)/t)**e+Math.abs((K[i]??0)/n)**e)**(-1/e))}function De(e){let t=Array(64).fill(0);for(let n=0;n<64;n++){let r=G[n]??0,i=K[n]??0,a=0;for(let t of e){let e=r*t.x+i*t.y,n=e*e-(t.x*t.x+t.y*t.y-t.r*t.r);if(n<0)continue;let o=e+Math.sqrt(n);o>a&&(a=o)}t[n]=a}return t}function Oe(e,t,n=10){let r=e.length,i=[],a=(e,t)=>{let n=t.x-e.x,r=t.y-e.y,i=Math.hypot(n,r)||1;return Math.atan2(-n/i,r/i)};for(let o=0;o<r;o++){let s=e[(o-1+r)%r],c=e[o],l=e[(o+1)%r],u=a(s,c),d=a(c,l)-u;for(;d>Math.PI;)d-=v;for(;d<-Math.PI;)d+=v;for(let e=0;e<=n;e++){let r=u+d*e/n;i.push({x:c.x+Math.cos(r)*t,y:c.y+Math.sin(r)*t})}}return i}function ke(e,t,n,r=0){let i=r*Math.PI/180;return J(Oe(Array.from({length:e},(r,a)=>{let o=i+a/e*v;return{x:Math.cos(o)*(t-n),y:Math.sin(o)*(t-n)}}),n),0,0)}function Ae(e,t=1){if(e.length<3)return``;let n=``;for(let r=0;r<e.length;r++){let i=e[r];n+=`${r===0?`M`:`L`}${w(i.x*t)} ${w(i.y*t)}`}return`${n}Z`}function je(e,t){let n=Math.max(e,.01)/2,r=Math.max(t,.01)/2,i=Math.min(n,r);return`M${w(-n)} ${w(-r+i)}A${w(i)} ${w(i)} 0 0 1 ${w(-n+i)} ${w(-r)}L${w(n-i)} ${w(-r)}A${w(i)} ${w(i)} 0 0 1 ${w(n)} ${w(-r+i)}L${w(n)} ${w(r-i)}A${w(i)} ${w(i)} 0 0 1 ${w(n-i)} ${w(r)}L${w(-n+i)} ${w(r)}A${w(i)} ${w(i)} 0 0 1 ${w(-n)} ${w(r-i)}Z`}function Me(e,t=1){let n=Math.max(...e);if(n<=0)return e;let r=t/n;return e.map(e=>e*r)}var Ne=Me(Array.from({length:64},(e,t)=>t/64*Math.PI*2).map(e=>1+.075*Math.cos(2*e+.5)+.035*Math.cos(3*e+2.1)),1.02),Pe=Me(De([{x:-.44,y:.2,r:.54},{x:.46,y:.2,r:.5},{x:.02,y:.3,r:.6},{x:-.24,y:-.3,r:.48},{x:.3,y:-.24,r:.44}]),1.02),Fe=Me(J(Y(0,.28,.66,0,-.96,.05),0,0),1.04),Ie=J(Y(-.42,0,.62,.42,0,.62),0,0),Le=[{id:`cercle`,radii:Array(64).fill(1)},{id:`galet`,radii:Ne},{id:`squircle`,radii:Me(Ee(4.2),1.15)},{id:`capsule`,radii:Ie},{id:`triangle`,radii:ke(3,1.12,.34,-90)},{id:`hexagone`,radii:ke(6,1.04,.26,0)},{id:`nuage`,radii:Pe},{id:`goutte`,radii:Fe}],Re=new Map(Le.map(e=>[e.id,e])),ze=`cercle`,Be=[{id:`encre`,hex:`#0a0a0c`},{id:`brun`,hex:`#8b5e3c`},{id:`rouge`,hex:`#e8483f`},{id:`orange`,hex:`#f08a24`},{id:`ambre`,hex:`#f0b429`},{id:`vert`,hex:`#3ecf8e`},{id:`turquoise`,hex:`#2fbfa0`},{id:`bleu`,hex:`#3b93f0`},{id:`violet`,hex:`#8b5cf6`},{id:`rose`,hex:`#e152b0`},{id:`gris`,hex:`#a3a3a3`},{id:`creme`,hex:`#f1efe9`}],Ve=new Map(Be.map(e=>[e.id,e])),He=`encre`;function Ue(e,t,n){let r=e=>{let t=parseInt(e.slice(1),16);return[t>>16&255,t>>8&255,t&255]},i=r(e),a=r(t);return`#${i.map((e,t)=>Math.round(e+(a[t]-e)*n)).map(e=>e.toString(16).padStart(2,`0`)).join(``)}`}var X=(e,t)=>[{w:e,h:t,open:1},{w:e,h:t,open:1}];function Z(e={}){return{sil:q(1),offX:0,offY:0,gaze:{...L},split:P,eyes:X(F,I),eyeAlpha:1,bodyAlpha:1,dots:[],arcs:[],notif:null,dotsBehind:!1,...e}}var We=-.1875,Ge=J(Y(0,-.505,.132,0,.13,.075),0,We),Ke=J(Y(0,-.2535,.1345,0,.2535,.1345),0,0),qe=(e={})=>({radii:[...Ge],rot:0,cx:0,cy:We,sx:1,sy:1,...e}),Je=(e={})=>({radii:[...Ke],rot:0,cx:0,cy:0,sx:1,sy:1,...e}),Ye=Ae(Y(0,0,.118,0,.172,.012)),Xe=.213;function Ze(e){return xe(`triangle`,{rot:e,cx:-.213*Math.sin(e),cy:Xe*Math.cos(e)})}function Qe(e,t){let n=((e-t*.5)/1.5%1+1)%1;return y((n<.5?.5-.5*Math.cos(n*v):0)*2)}var $e=[{id:`idle`,duration:2.4,morph:.45,blinkIn:!1,baseFace:!0,baseBody:!0,pose:()=>Z()},{id:`thinking`,duration:2.6,morph:.4,baseFace:!1,baseBody:!1,blinkIn:!0,pose:e=>{let t=Qe(e,1),n=.3+.7*x.easeOutCubic(y(e/.3));return Z({sil:q(k*(1+(ne-1)*t),{cx:te[1]}),eyeAlpha:0,dots:[0,2].map(t=>{let r=Qe(e,t);return{x:te[t]*n,y:0,r:k*(1+(ne-1)*r),opacity:.55+.45*r}})})}},{id:`wink`,duration:1.6,morph:.3,blinkIn:!0,baseFace:!1,baseBody:!0,pose:()=>Z({gaze:{yaw:-5.37,pitch:4.55,roll:6.7},split:16.25,eyes:[{w:.236,h:.464,open:1},{w:.447,h:.089,open:1}]})},{id:`wide`,duration:1.8,morph:.55,blinkIn:!0,baseFace:!1,baseBody:!0,pose:()=>Z({gaze:{yaw:6.92,pitch:-21.96,roll:11.6},split:18.43,eyes:X(.356,.875)})},{id:`alert`,duration:2.4,minDuration:2,morph:.45,baseFace:!1,baseBody:!1,blinkIn:!1,pose:e=>{let t=y(e/1.5),n=x.easeInOutCubic(t)*.82-.087,r=e>1.6?y((e-1.6)/.4):0,i=n*(1-r)+.1*r,a=Math.sin(e*2.5*v)*.005,o=17.7*Math.PI/180;return Z({sil:Je({rot:o,cx:i,cy:-.325-a}),eyeAlpha:0,dots:[{x:i-Math.sin(o)*.58,y:-.325+Math.cos(o)*.58+a*2.8,r:.118,d:Ye,rot:o*180/Math.PI,opacity:1}]})}},{id:`notify`,duration:2.2,morph:.5,blinkIn:!0,baseFace:!1,baseBody:!0,pose:e=>{let t=y(e/.45),n=1+(ce-1)*Math.sin(t*Math.PI)*(1-t*.35),r=N*(t<1?n:1),i=-42*Math.PI/180;return Z({gaze:{yaw:-21.94,pitch:-5.82,roll:-12.2},split:18.89,eyes:X(.505,.498),notif:{x:Math.cos(i)*M,y:Math.sin(i)*M,r,notch:r+le}})}},{id:`exclaim`,duration:2,morph:.45,baseFace:!1,baseBody:!1,blinkIn:!1,pose:()=>Z({sil:qe(),eyeAlpha:0,dots:[{x:-.012,y:.526,r:.113,opacity:1}]})},{id:`sleep`,duration:2.4,morph:.5,baseFace:!1,baseBody:!1,blinkIn:!1,pose:e=>Z({sil:q(.1585,{cy:.11+Math.sin(v/.6*e)*.19}),eyeAlpha:0})},{id:`egg`,duration:1.8,morph:.4,baseFace:!1,baseBody:!1,blinkIn:!0,pose:()=>Z({sil:xe(`egg`),gaze:{yaw:19.97,pitch:26.01,roll:-17.1},split:11.07,eyes:X(.164,.385)})},{id:`hexagon`,duration:1.6,morph:.4,baseFace:!1,baseBody:!1,blinkIn:!0,pose:()=>Z({sil:xe(`hexagon`),gaze:{yaw:23.11,pitch:24.42,roll:-13.3},split:13.37,eyes:X(.177,.411)})},{id:`play`,duration:2,morph:.5,baseFace:!1,baseBody:!1,blinkIn:!0,pose:e=>{let t=y(e/.35)*y((2.2-e)/.5);return Z({sil:Ze(0),gaze:{yaw:12,pitch:-8,roll:-6},split:15,eyes:X(.18,.34),arcs:O.map((n,r)=>({id:`sw${r}`,seed:{...n,cx:.45-e*.42},t:e,opacity:t}))})}},{id:`orbit`,duration:3.4,minDuration:2.5,morph:.6,baseFace:!1,baseBody:!1,blinkIn:!1,pose:e=>{let t=x.easeInOutCubic(y(e/.35)),n=-v*1.25*e*t,r=x.easeInOutCubic(y((e-1.6)/.9)),i=Ze(n),a=q(1,{rot:n}),o={radii:i.radii.map((e,t)=>e+(a.radii[t]-e)*r),rot:n,cx:i.cx*(1-r),cy:i.cy*(1-r),sx:1,sy:1},s=y(e/.8)*y((3.6-e)/.9);return Z({sil:o,gaze:{yaw:L.yaw+Math.sin(e*6.5)*65*(1-r),pitch:-4+r*32,roll:-13},eyes:X(.18,.34+r*.07),arcs:D.map((t,n)=>({id:`rg${n}`,seed:t,t:e,opacity:s*y((e-n*.13)/.3)}))})}},{id:`swirl`,duration:1.3,minDuration:1.3,morph:.3,baseFace:!0,baseBody:!0,blinkIn:!0,pose:e=>Z({arcs:D.slice(0,3).map((t,n)=>({id:`sw${n}`,seed:t,t:e,opacity:y((e-n*.06)/.14)*y((1.22-e)/.34)}))})},{id:`burst`,duration:2.6,minDuration:2.4,morph:.4,baseFace:!1,baseBody:!1,blinkIn:!1,pose:e=>{let t=1-.834*x.easeOutQuint(y(e/.7)),n=x.easeOutQuint(y((e-1.7)/.7));return Z({sil:q(t+(1-t)*n),eyeAlpha:y((e-1.85)/.4),dots:ae(e,1),dotsBehind:!0})}},{id:`comet`,duration:2.4,minDuration:2.4,morph:.45,baseFace:!1,baseBody:!1,blinkIn:!1,pose:e=>{let t=1-(1-j)*x.easeOutQuint(y(e/.55)),n=x.easeOutQuint(y((e-1.85)/.6)),r=y((e-.15)/.25)*y((1.95-e)/.3);return Z({sil:q(t+(1-t)*n,{cy:Math.sin(y(e/1.7)*Math.PI)*.035}),eyeAlpha:y((e-2)/.35),arcs:A.map((t,n)=>({id:`cm${n}`,seed:t,t:e,opacity:r}))})}}],Q=new Map($e.map(e=>[e.id,e])),et={idle:1,thinking:1.1,wink:.8,wide:.8,alert:.75,notify:.9,exclaim:.8,sleep:.45,egg:.8,hexagon:.8,play:.9,orbit:1.2,swirl:.5,burst:.45,comet:1.15},tt=[`idle`,`thinking`,`wink`,`wide`,`alert`,`notify`,`exclaim`,`sleep`,`egg`,`hexagon`,`play`,`orbit`,`burst`,`comet`],$=100,nt=7.1,rt=5.5,it=.006,at=.007;function ot(e,t,n){let r=[],i=B(e.gaze,$,e.split);for(let a=0;a<2;a++){let o=i[a];if(o.depth<=.02)continue;let s=e.eyes[a],c=(s.tilt??0)*Math.PI/180,l=Math.cos(c),u=Math.sin(c),d=o.a*l+o.c*u,f=o.b*l+o.d*u,p=-o.a*u+o.c*l,m=-o.b*u+o.d*l,h=Math.max(s.w*$,.01)/2,g=Math.max(s.h*$,.01)/2,_=Math.min(h,g),v=g>h,y=v?g-_:h-_,b=Te(n,Math.atan2(o.y,o.x)-t.rot);r.push({x:o.x*b,y:o.y*b,ax:(v?p:d)*y,ay:(v?m:f)*y,r:_,m:[d,f,p,m]})}return r}function st(e,t,n,r,i){let a=r-t,o=i-n,s=a*a+o*o,c=1/0,l=0,u=0;for(let r=0;r<e.length;r++){let i=e[r],d=s>0?((i.x-t)*a+(i.y-n)*o)/s:0;d=d<0?0:d>1?1:d;let f=t+d*a-i.x,p=n+d*o-i.y,m=f*f+p*p;m<c&&(c=m,l=f,u=p)}let d=Math.sqrt(c);return{d,ux:d>1e-9?l/d:0,uy:d>1e-9?u/d:0}}var ct=Math.hypot(it,at)*$;function lt(e,t,n,r){let i=1/0,a=0,o=0;for(let s of t){let t=s.x+n,c=s.y+r,l=st(e,t-s.ax,c-s.ay,t+s.ax,c+s.ay),[u,d,f,p]=s.m,m=s.r*Math.hypot(u*l.ux+d*l.uy,f*l.ux+p*l.uy)+ct;l.d-m<i&&(i=l.d-m,a=l.ux,o=l.uy)}return{marge:i,ux:a,uy:o}}var ut=12,dt=8;function ft(e){if(!e.length)return{x:0,y:0};let t=(t,n)=>{let r=1/0;for(let i of e)r=Math.min(r,lt(i.contour,i.empreintes,t,n).marge);return r},n=1/0;for(let t of e)n=Math.min(n,lt(t.calContour,t.reference,0,0).marge);let r=0,i=0,a=e[0].empreintes;for(let e of a)r-=e.x/a.length,i-=e.y/a.length;let o=Math.max(.35*$,Math.hypot(r,i)*1.25);n=Math.min(n,t(r,i));let s=t(0,0);if(s>=n&&s>=0)return{x:0,y:0};let c=Math.max(n,0),l=0,u=0,d=1/0,f=0,p=0,m=s;for(let e=0;e<ut;e++){let n=e/ut*Math.PI*2,r=Math.cos(n),i=Math.sin(n);if(t(r*o,i*o)<c){for(let e of[.3,.6,1]){let n=t(r*o*e,i*o*e);n>m&&(m=n,f=r*o*e,p=i*o*e)}continue}let a=0,s=o;for(let e=0;e<dt;e++){let e=(a+s)/2;t(r*e,i*e)>=c?s=e:a=e}s<d&&(d=s,l=r*s,u=i*s)}let h=d===1/0?f:l,g=d===1/0?p:u;return{x:+(h/$).toFixed(6),y:+(g/$).toFixed(6)}}function pt(e,t,n){return e.baseFace&&n?{gaze:n.gaze,split:n.split,eyes:n.eyes}:{gaze:t.gaze,split:t.split,eyes:t.eyes}}function mt(e){let t=e=>JSON.stringify([e.gaze,e.split,e.eyes,e.sil.rot,e.sil.cx,e.sil.cy,e.sil.sx,e.sil.sy]);return t(e.pose(0))===t(e.pose(e.duration))?[0]:Array.from({length:3},(t,n)=>n/2*e.duration)}function ht(e,t,n){let r=[];for(let i of mt(e)){let a=e.pose(i),o=Ce({...a.sil,radii:t},$),s=Ce(a.sil,$),c=pt(e,a,n),l=[];for(let e of[-7.1,nt])for(let t of[-5.5,rt])l.push({...c,gaze:{yaw:c.gaze.yaw+e,pitch:c.gaze.pitch+t,roll:c.gaze.roll}});for(let e of l)r.push({empreintes:ot(e,a.sil,t),reference:ot(e,a.sil,a.sil.radii),contour:o,calContour:s})}return ft(r)}var gt={x:0,y:0},_t=(e,t)=>`${e}|${t??``}`;function vt(){return new Map(Le.map(e=>{let t=new Map;for(let n of $e){if(!n.baseBody)continue;let r=n.baseFace?[null,...U]:[null];for(let i of r)t.set(_t(n.id,i?.id??null),ht(n,e.radii,i))}return[e.radii,t]}))}var yt=vt();function bt(e,t,n){if(!e)return gt;let r=yt.get(e);return r?r.get(_t(t,n))??r.get(_t(t,null))??gt:gt}var xt={yaw:0,pitch:0,mix:0,spin:0,wander:1},St=(e,t,n)=>({yaw:b(e.yaw,t.yaw,n),pitch:b(e.pitch,t.pitch,n),mix:b(e.mix,t.mix,n),spin:b(e.spin,t.spin,n),wander:b(e.wander,t.wander,n)}),Ct=(e,t,n)=>({w:b(e.w,t.w,n),h:b(e.h,t.h,n),open:b(e.open,t.open,n),tilt:b(e.tilt??0,t.tilt??0,n)});function wt(e,t,n){let r=1-n;return{sil:Se(e.sil,t.sil,n),offX:b(e.offX,t.offX,n),offY:b(e.offY,t.offY,n),gaze:{yaw:b(e.gaze.yaw,t.gaze.yaw,n),pitch:b(e.gaze.pitch,t.gaze.pitch,n),roll:b(e.gaze.roll,t.gaze.roll,n)},split:b(e.split,t.split,n),eyes:[Ct(e.eyes[0],t.eyes[0],n),Ct(e.eyes[1],t.eyes[1],n)],eyeAlpha:b(e.eyeAlpha,t.eyeAlpha,n),bodyAlpha:b(e.bodyAlpha,t.bodyAlpha,n),dots:[...e.dots.map(e=>({...e,opacity:e.opacity*r})),...t.dots.map(e=>({...e,opacity:e.opacity*n}))],arcs:[...e.arcs.map(e=>({...e,id:`a${e.id}`,opacity:e.opacity*r})),...t.arcs.map(e=>({...e,id:`b${e.id}`,opacity:e.opacity*n}))],notif:n<.5?e.notif:t.notif,dotsBehind:n<.5?e.dotsBehind:t.dotsBehind}}var Tt=class e{scale;cur;prev=null;departFige=null;tCur=0;tPrev=0;blinkAt=-10;pts=[];shape=null;shapePrev=null;shapeAt=-10;expr=null;exprPrev=null;exprAt=-10;look=xt;lookPrev=xt;lookAt=-10;lookMorph=.24;static SHAPE_MORPH=.45;static LOOK_MORPH=.24;constructor(e=100,t=`idle`,n=null,r=null){this.scale=e,this.cur=t,this.shape=n,this.expr=r}setExpression(e,t=0){e!==this.expr&&(this.exprPrev=this.expr,this.expr=e,this.exprAt=t)}exprAtTime(t){let n=this.expr,r=this.exprPrev;if(!n||!r)return n;let i=(t-this.exprAt)/e.SHAPE_MORPH;return i>=1?n:ye(r,n,x.easeOutQuint(y(i)))}setShape(e,t=0){e!==this.shape&&(this.shapePrev=this.shape,this.shape=e,this.shapeAt=t)}shapeAtTime(t){let n=this.shape,r=this.shapePrev;if(!n||!r)return n;let i=(t-this.shapeAt)/e.SHAPE_MORPH;if(i>=1)return n;let a=x.easeOutQuint(y(i));return n.map((e,t)=>b(r[t]??e,e,a))}setLook(t,n,r=e.LOOK_MORPH){t&&!Number.isFinite(t.yaw+t.pitch+t.mix+t.spin+t.wander)||(this.lookPrev=this.lookAtTime(n),this.look=t??xt,this.lookAt=n,this.lookMorph=r)}lookAtTime(e){let t=(e-this.lookAt)/this.lookMorph;return t>=1?this.look:St(this.lookPrev,this.look,x.easeOutQuint(y(t)))}posed(e,t,n,r){let i=e.pose(t);return e.baseBody&&n&&(i={...i,sil:{...i.sil,radii:n}}),e.baseFace&&r&&(i={...i,gaze:r.gaze,split:r.split,eyes:r.eyes}),i}decalageAtTime(t,n){let r=(e,n,r,i)=>{if(r===i)return i;let a=(t-e)/n;if(a>=1)return i;let o=x.easeOutQuint(y(a));return{x:b(r.x,i.x,o),y:b(r.y,i.y,o)}},i=t=>r(this.exprAt,e.SHAPE_MORPH,bt(t,n,this.exprPrev?.id??null),bt(t,n,this.expr?.id??null));return r(this.shapeAt,e.SHAPE_MORPH,i(this.shapePrev),i(this.shape))}get state(){return this.cur}reset(e,t){this.cur=e,this.prev=null,this.departFige=null,this.tCur=t,this.tPrev=t,this.blinkAt=-10}origine(e,t,n){if(this.departFige)return this.departFige;if(!this.prev)return null;let r=Q.get(this.prev);return this.posed(r,Math.max(0,e-this.tPrev),t,n)}poseComposee(e){let t=Q.get(this.cur),n=this.shapeAtTime(e),r=this.exprAtTime(e),i=this.posed(t,Math.max(0,e-this.tCur),n,r),a=e-this.tCur;if(a>=t.morph)return i;let o=this.origine(e,n,r);return o?wt(o,i,x.easeOutQuint(y(a/t.morph))):i}setState(e,t){if(e===this.cur)return;let n=Q.get(this.cur).morph,r=this.prev!==null&&t-this.tCur<n;this.departFige=r?this.poseComposee(t):null,this.prev=this.cur,this.tPrev=this.tCur,this.cur=e,this.tCur=t,Q.get(e)?.blinkIn&&(this.blinkAt=t)}sample(e){let t=this.scale,n=Q.get(this.cur),r=this.shapeAtTime(e),i=this.exprAtTime(e),a=this.posed(n,Math.max(0,e-this.tCur),r,i),o=this.decalageAtTime(e,this.cur),s=e-this.tCur,c=s<n.morph?this.origine(e,r,i):null;if(c){let t=x.easeOutQuint(y(s/n.morph));a=wt(c,a,t);let r=this.prev;if(r){let n=this.decalageAtTime(e,r);o={x:b(n.x,o.x,t),y:b(n.y,o.y,t)}}}let l=a.eyeAlpha>.01,u=this.lookAtTime(e),d=me(e,{wander:l?u.wander:0,blink:l}),f={yaw:b(a.gaze.yaw,u.yaw,u.mix)+d.dYaw-u.spin,pitch:b(a.gaze.pitch,u.pitch,u.mix)+d.dPitch,roll:a.gaze.roll+d.dRoll},p=y((e-this.blinkAt)/.2),m=p<1?Math.abs(p*2-1):1,h=Math.min(d.lid,m),g=a.offX+d.driftX,_=a.offY+d.driftY,v=we(Ce({...a.sil,cx:a.sil.cx+g,cy:a.sil.cy+_,sy:a.sil.sy*d.breath},t,this.pts)),S=(e,t)=>Te(a.sil.radii,Math.atan2(t,e)-a.sil.rot),C=[];if(a.eyeAlpha>.01){let e=B(f,t,a.split);for(let n=0;n<2;n++){let r=e[n];if(r.depth<=.02)continue;let i=a.eyes[n],s=S(r.x,r.y),c=(i.tilt??0)*Math.PI/180,l=Math.cos(c),u=Math.sin(c),d=r.a*l+r.c*u,f=r.b*l+r.d*u,p=-r.a*u+r.c*l,m=-r.b*u+r.d*l,v=he(Math.min(h,i.open));C.push({d:je(i.w*t,i.h*t),matrix:`matrix(${w(d)},${w(f*v)},${w(p)},${w(m*v)},${w(r.x*s+(g+o.x)*t)},${w(r.y*s+(_+o.y)*t)})`,alpha:a.eyeAlpha*y(r.depth/.12)})}}let T=a.dots.filter(e=>e.opacity>.01&&e.r>5e-4).map(e=>({...e,x:(e.x+g)*t,y:(e.y+_)*t,r:e.r*t})),E=a.notif?S(a.notif.x,a.notif.y):1,D=a.notif?(a.notif.x*E+g)*t:0,O=a.notif?(a.notif.y*E+_)*t:0,te=a.notif?{x:D,y:O,r:a.notif.r*t}:null,k=a.notif?{x:D,y:O,r:a.notif.notch*t}:null;return{bodyPath:v,bodyAlpha:a.bodyAlpha,eyes:C,dots:T,dotsBehind:a.dotsBehind,arcs:a.arcs.filter(e=>e.opacity>.01).map(e=>ee(e.seed,e.t,t,e.id,e.opacity)),notif:te,notch:k}}},Et=1.1,Dt=1.5,Ot=e=>({yaw:0,pitch:0,mix:0,spin:360*(1-x.easeInOutCubic(y(e/Dt))),wander:1});function kt({nx:e,ny:t,tour:n,pointer:r}){return{yaw:-26+e*16,pitch:10-t*13,mix:n,spin:360*(1-n),wander:+!r}}var At=Math.max(...$e.map(e=>e.morph)),jt=10,Mt=200,Nt=50,Pt=.1,Ft=`defaut`;function It(e){return Math.max(At,Q.get(e)?.minDuration??At)}function Lt(e,t){let n=Math.round(t/Pt)*Pt,r=Math.min(10,Math.max(It(e),n));return Math.round(r*100)/100}function Rt(e){return{state:e,duration:Lt(e,Q.get(e)?.duration??2)}}function zt(){return{name:``,id:Ft,blocks:tt.map(Rt)}}function Bt(e){return e.reduce((e,t)=>e+t.duration,0)}function Vt(e,t){let n=0;for(let r=0;r<t&&r<e.length;r++)n+=e[r].duration;return n}function Ht(e,t){let n=Bt(e);if(!e.length||n<=0)return{index:0,elapsed:0};let r=t>=0&&t<n?t:(t%n+n)%n,i=0;for(let t=0;t<e.length;t++){let n=i+e[t].duration;if(r<n)return{index:t,elapsed:r-i};i=n}return{index:e.length-1,elapsed:0}}function Ut(e,t){return e.length>=200?e:[...e,Rt(t)]}function Wt(e,t,n){let r=e.slice(),[i]=r.splice(t,1);return i?(r.splice(Math.min(Math.max(n,0),r.length),0,i),r):e}function Gt(e,t){let n=new Set(t.map(e=>e.name));if(!n.has(e))return e;let r=2;for(;n.has(`${e} ${r}`);)r++;return`${e} ${r}`}function Kt(e){let t=new Set(e.map(e=>e.id)),n=1;for(;t.has(`c${n}`);)n++;return`c${n}`}function qt(e){if(typeof e!=`object`||!e)return null;let{state:t,duration:n}=e;return typeof t!=`string`||!tt.includes(t)||typeof n!=`number`||!Number.isFinite(n)?null:{state:t,duration:Lt(t,n)}}function Jt(e,t){if(typeof e!=`object`||!e)return null;let{id:n,name:r,blocks:i}=e;if(typeof n!=`string`||!n||typeof r!=`string`||!Array.isArray(i))return null;let a=i.slice(0,200).map(qt).filter(e=>e!==null);return!a.length||t.some(e=>e.id===n)?null:{id:n,name:r,blocks:a}}function Yt(e){if(!e)return[];let t;try{t=JSON.parse(e)}catch{return[]}if(!Array.isArray(t))return[];let n=[];for(let e of t.slice(0,50)){let t=Jt(e,n);t&&n.push(t)}return n}var Xt=100,Zt=158,Qt=[`<g`,`>`,`</g>`],$t=[`<svg`,` viewBox="`,`" role="img"`,` style="`,`"><defs><mask`,` maskUnits="userSpaceOnUse"`,`><path`,` fill="#fff"></path><!--$-->`,`<!--/--><!--$-->`,`<!--/--></mask><!--$-->`,`<!--/--></defs><g fill="none" stroke-linecap="round">`,`</g><!--$-->`,`<!--/--><g`,`><path`,`></path><g mask="`,`"><rect`,`></rect></g></g><!--$-->`,`<!--/--><!--$-->`,`<!--/--><g fill="none" stroke-linecap="round">`,`</g></svg>`],en=[`<path`,` fill="#000"></path>`],tn=[`<circle`,` fill="#000"></circle>`],nn=[`<linearGradient`,` id="`,`" gradientUnits="userSpaceOnUse"`,`>`,`</linearGradient>`],rn=[`<stop`,`></stop>`],an=[`<path`,` stroke="`,`"`,`></path>`],on=[`<circle`,`></circle>`];function sn(v){let y=m({size:320,shape:ze,color:He,expression:_e,paper:`#f9f9f9`,cycle:zt().blocks,follow:!1,gaze:null,ariaLabel:`Animated Bloub avatar`},v),[b,x]=f(0),[S,C]=f(`idle`),[w]=f(!1),[T,ee]=f(0),E=()=>v.block??b(),D=()=>v.state??S(),O=()=>v.playing??w(),te=()=>v.elapsed??T();function k(e){v.block===void 0&&x(e),v.onBlockChange?.(e)}function ne(e){v.state===void 0&&C(e),v.onStateChange?.(e)}function re(e){v.elapsed===void 0&&ee(e),v.onElapsedChange?.(e)}let ie=d(()=>Re.get(y.shape)?.radii??null),ae=d(()=>Ve.get(y.color)?.hex??`#0a0a0c`),oe=d(()=>ge.get(y.expression)??null),A=new Tt(100,D(),ie(),oe()),[j,M]=f(A.sample(y.frozenAt??0),{equals:!1}),N=p(),ce=`bot-mask-${N}`,le=0,P=1/0,F=0,I=0,L=0,R=0,z=-1;function B(e,t=0){let n=y.cycle[e];if(!n){P=1/0;return}L=I-t,re(t),ne(n.state),A.setState(n.state,I),P=O()?L+n.duration:1/0}function ue(e){k(e),B(e)}function de(e,t=0){if(E()===e){B(e,t);return}R=t,k(e)}function fe(e){let t=y.cycle;if(!t.length)return;let{index:n}=Ht(t,e);if(n!==z){let e=t[n];ne(e.state),n<z?A.reset(e.state,Vt(t,n)):A.setState(e.state,Vt(t,n)),z=n}M(A.sample(e))}let pe=!1,me=0,he=!1,V=1/60;function H(e){e.pointerType!==`touch`&&(e.clientX,e.clientY)}function U(){}function ve(){pe&&=(A.setLook(null,I,Et),!1)}function ye(){if(!Q.get(D())?.baseFace){ve();return}}function be(e){A.setLook(e(I-me),I,V)}function W(){window.removeEventListener(`pointermove`,H),document.removeEventListener(`pointerleave`,U)}function G(e){le=requestAnimationFrame(G);let t=F?Math.min((e-F)/1e3,.064):0;F=e,I+=t,O()&&(I>=P&&y.cycle.length?ue((E()+1)%y.cycle.length):re(I-L)),y.follow?ye():y.gaze&&be(y.gaze),M(A.sample(I))}function K(){y.frozenAt!==void 0&&M(A.sample(y.frozenAt))}u(h(()=>y.gaze,e=>{e?(me=I,he=!0,A.setLook(e(0),I-V,V)):he&&=(A.setLook(null,I),!1)})),u(h(E,e=>{B(e,R),R=0},{defer:!0})),u(h(D,e=>{A.state!==e&&(A.setState(e,I),K())},{defer:!0})),u(h(O,e=>{e?B(E(),te()):P=1/0},{defer:!0})),u(h(()=>y.cycle,e=>{if(!e.length){P=1/0;return}let t=Math.min(E(),e.length-1);t===E()?P=O()?L+e[t].duration:1/0:ue(t)},{defer:!0})),u(h(ie,e=>{A.setShape(e,I),K()},{defer:!0})),u(h(oe,e=>{A.setExpression(e,I),K()},{defer:!0})),u(h(()=>y.frozenAt,K,{defer:!0})),u(()=>{y.follow&&y.frozenAt===void 0?(window.addEventListener(`pointermove`,H),document.addEventListener(`pointerleave`,U)):(W(),ve())}),_(()=>{v.ref?.({get svg(){},seek:de,renderAt:fe}),y.frozenAt===void 0&&(B(E(),te()),le=requestAnimationFrame(G))}),g(()=>{typeof cancelAnimationFrame<`u`&&cancelAnimationFrame(le),typeof window<`u`&&W()});function xe(e){let t={fill:e.color??(e.depth===void 0?ae():Ue(y.paper,ae(),e.depth)),opacity:e.opacity};return e.d?{...t,d:e.d,transform:`translate(${e.x} ${e.y}) rotate(${e.rot??0}) scale(100)`}:{...t,cx:e.x,cy:e.y,r:e.r}}let q=()=>t(c,{get each(){return j().dots},children:n=>t(e,r({get component(){return n.d?`path`:`circle`}},()=>xe(n)))});return i($t,o()+a(`width`,n(y.size,!0),!1)+a(`height`,n(y.size,!0),!1),`-158 -158 ${n(158,!0)*2} ${n(158,!0)*2}`,a(`aria-label`,n(y.ariaLabel,!0),!1)+a(`class`,n(y.class,!0),!1),s(y.style),a(`id`,n(ce,!0),!1),a(`x`,-158,!1)+a(`y`,-158,!1)+a(`width`,n(158,!0)*2,!1)+a(`height`,n(158,!0)*2,!1),a(`d`,n(j().bodyPath,!0),!1),n(t(c,{get each(){return j().eyes},children:e=>i(en,o()+a(`d`,n(e.d,!0),!1)+a(`transform`,n(e.matrix,!0),!1)+a(`opacity`,n(e.alpha,!0),!1))})),n(t(l,{get when(){return j().notch},children:e=>i(tn,o()+a(`cx`,n(e().x,!0),!1)+a(`cy`,n(e().y,!0),!1)+a(`r`,n(e().r,!0),!1))})),n(t(c,{get each(){return j().arcs},children:e=>i(nn,o(),`${n(N,!0)}-${n(e.id,!0)}`,a(`x1`,n(e.grad.x1,!0),!1)+a(`y1`,n(e.grad.y1,!0),!1)+a(`x2`,n(e.grad.x2,!0),!1)+a(`y2`,n(e.grad.y2,!0),!1),n(t(c,{get each(){return e.grad.stops},children:(t,r)=>i(rn,o()+a(`offset`,n(r(),!0)/(n(e.grad.stops.length,!0)-1),!1)+a(`stop-color`,n(t,!0),!1))})))})),n(t(c,{get each(){return j().arcs},children:e=>i(an,o()+a(`d`,n(e.back,!0),!1),`url(#${n(N,!0)}-${n(e.id,!0)})`,a(`stroke-width`,n(e.width,!0),!1)+a(`opacity`,n(e.opacity,!0),!1))})),n(t(l,{get when(){return j().dotsBehind},get children(){return i(Qt,o(),n(t(q,{})))}})),a(`opacity`,n(j().bodyAlpha,!0),!1),a(`d`,n(j().bodyPath,!0),!1)+a(`fill`,n(y.paper,!0),!1),`url(#${n(ce,!0)})`,a(`x`,-158,!1)+a(`y`,-158,!1)+a(`width`,n(158,!0)*2,!1)+a(`height`,n(158,!0)*2,!1)+a(`fill`,n(ae(),!0),!1),n(t(l,{get when(){return!j().dotsBehind},get children(){return i(Qt,o(),n(t(q,{})))}})),n(t(l,{get when(){return j().notif},children:e=>i(on,o()+a(`cx`,n(e().x,!0),!1)+a(`cy`,n(e().y,!0),!1)+a(`r`,n(e().r,!0),!1)+a(`fill`,n(se,!0),!1))})),n(t(c,{get each(){return j().arcs},children:e=>i(an,o()+a(`d`,n(e.front,!0),!1),`url(#${n(N,!0)}-${n(e.id,!0)})`,a(`stroke-width`,n(e.width,!0),!1)+a(`opacity`,n(e.opacity,!0),!1))})))}export{sn as BloubBot,Tt as BotEngine,Be as COLORS,Ve as COLOR_BY_ID,He as DEFAULT_COLOR,_e as DEFAULT_EXPRESSION,ze as DEFAULT_SHAPE,Zt as DEMI_VIEWBOX,U as EXPRESSIONS,ge as EXPRESSION_BY_ID,jt as MAX_BLOCK,Mt as MAX_BLOCS,Nt as MAX_CYCLES,At as MIN_BLOCK,et as POSES,Xt as RAYON,tt as SEQUENCE,Le as SHAPES,Re as SHAPE_BY_ID,$e as STATES,Q as STATE_BY_ID,Pt as STEP,Dt as TOUR_TIME,Et as TURN_TIME,ye as blendExpression,Ht as blockAt,Ut as blocksWith,Lt as clampDuration,zt as defaultCycle,kt as lookTarget,Rt as makeBlock,It as minDurationOf,Ue as mixHex,Wt as moveBlock,Kt as nextCycleId,Vt as offsetOf,Yt as parseCycles,Bt as totalDuration,Ot as tourLook,Gt as uniqueName};
@@ -0,0 +1,97 @@
1
+ import { type StateId } from './states.js';
2
+ /**
3
+ * Un cycle est un montage : une suite de blocs, chacun un etat tenu pendant une
4
+ * duree choisie. C'est la partie "editeur" du dossier, et elle en garde les
5
+ * regles — donnees pures, aucune horloge, aucun import Vue : le meme cycle doit
6
+ * pouvoir etre relu par les tests, par le lecteur et par la timeline.
7
+ *
8
+ * Un bloc n'a pas d'identifiant : c'est une position dans une liste, la cle de
9
+ * rendu est l'index. Ca garde le JSON du localStorage lisible et les tests
10
+ * deterministes.
11
+ */
12
+ export interface Block {
13
+ state: StateId;
14
+ duration: number;
15
+ }
16
+ export interface Cycle {
17
+ id: string;
18
+ name: string;
19
+ blocks: Block[];
20
+ }
21
+ /**
22
+ * Plancher commun a tous les blocs. Le moteur ne garde qu'une case d'historique
23
+ * (`BotEngine.setState` ecrase `prev`), donc un bloc plus court que le fondu d'entree du
24
+ * bloc suivant saute a l'image au lieu de se fondre.
25
+ *
26
+ * DERIVE du catalogue et non ecrit a la main. La valeur etait 0,6, ce qui marchait
27
+ * uniquement parce que 0,6 se trouvait etre le plus long `morph` du catalogue — celui
28
+ * d'`orbit`. Rien ne le garantissait : ajouter un etat qui morphe en 0,8 s aurait fait
29
+ * trembler l'editeur sans qu'aucun test ne bronche. Maintenant le plancher suit.
30
+ */
31
+ export declare const MIN_BLOCK: number;
32
+ /**
33
+ * Garde-fou d'editeur, pas une mesure : allonger un bloc est sans risque (les
34
+ * etats saturent leurs rampes et tiennent leur pose finale), mais une piste de
35
+ * blocs d'une minute n'est plus lisible.
36
+ */
37
+ export declare const MAX_BLOCK = 10;
38
+ /**
39
+ * Combien de blocs et de montages on accepte, a l'edition comme a la relecture.
40
+ *
41
+ * Ce ne sont pas des limites de produit mais des bornes contre un stockage hostile, qui
42
+ * est modifiable et tient quelques megaoctets alors que rien en aval n'est dimensionne
43
+ * pour ca : un seul cycle de 150 000 blocs, soit environ 4 Mo de JSON, donne 1 500 000 s
44
+ * de duree, autant de graduations a allouer et une piste de 29 700 000 px de large.
45
+ * L'onglet figeait en entrant dans la vue Animations.
46
+ *
47
+ * 200 blocs font une demi-heure de montage, largement au-dela de tout usage.
48
+ */
49
+ export declare const MAX_BLOCS = 200;
50
+ export declare const MAX_CYCLES = 50;
51
+ /** Pas de la molette et du redimensionnement, en secondes. */
52
+ export declare const STEP = 0.1;
53
+ /** Duree minimale d'un bloc : le plancher moteur, ou la mesure de l'etat. */
54
+ export declare function minDurationOf(state: StateId): number;
55
+ /** Ramene une duree dans ses bornes et sur le pas, sans trainee de flottants. */
56
+ export declare function clampDuration(state: StateId, seconds: number): number;
57
+ export declare function makeBlock(state: StateId): Block;
58
+ /**
59
+ * Le montage releve sur la video : l'ordre de `SEQUENCE`, chaque etat tenu sa
60
+ * duree mesuree. Il sert d'amorce au premier lancement, puis il appartient a
61
+ * l'utilisateur — il s'edite et se stocke comme les autres. La reference, elle,
62
+ * reste dans le code : vider le stockage la fait revenir.
63
+ */
64
+ export declare function defaultCycle(): Cycle;
65
+ export declare function totalDuration(blocks: Block[]): number;
66
+ /** Date de debut d'un bloc dans le montage. */
67
+ export declare function offsetOf(blocks: Block[], index: number): number;
68
+ /**
69
+ * Bloc joue a la date `t` et temps ecoule dedans. Au-dela du dernier bloc on
70
+ * retombe au debut : la lecture boucle. L'appelant verifie que le montage n'est
71
+ * pas vide.
72
+ */
73
+ export declare function blockAt(blocks: Block[], t: number): {
74
+ index: number;
75
+ elapsed: number;
76
+ };
77
+ /**
78
+ * Ajoute une animation a la fin du montage (palette de droite ou carte « + »).
79
+ *
80
+ * Plafonnee a `MAX_BLOCS`, comme la relecture. Sans ca l'editeur laissait construire un
81
+ * montage plus grand que ce que le stockage rend au rechargement, et le travail
82
+ * disparaissait en silence — une borne de relecture qui n'est pas aussi une borne d'edition
83
+ * est un piege, pas une protection.
84
+ */
85
+ export declare function blocksWith(blocks: Block[], state: StateId): Block[];
86
+ /** Deplace un bloc, en rendant une nouvelle liste (les etats Vue sont remplaces). */
87
+ export declare function moveBlock(blocks: Block[], from: number, to: number): Block[];
88
+ /** `Mon cycle`, `Mon cycle 2`, `Mon cycle 3`... — jamais deux fois le meme nom. */
89
+ export declare function uniqueName(base: string, cycles: Cycle[]): string;
90
+ /** Identifiant sans collision, y compris avec un localStorage bricole a la main. */
91
+ export declare function nextCycleId(cycles: Cycle[]): string;
92
+ /**
93
+ * Le localStorage est modifiable a la main : on ne lui fait pas confiance, meme
94
+ * regle que pour le hash de l'URL. Tout ce qui ne se relit pas est jete
95
+ * silencieusement plutot que de casser l'application au demarrage.
96
+ */
97
+ export declare function parseCycles(raw: string | null): Cycle[];
@@ -0,0 +1,113 @@
1
+ export interface DotRender {
2
+ x: number;
3
+ y: number;
4
+ r: number;
5
+ opacity: number;
6
+ /** couleur explicite ; par defaut le rendu prend celle du corps */
7
+ color?: string;
8
+ /**
9
+ * Brume de profondeur : 0 = fondu dans le fond, 1 = couleur du corps pleine.
10
+ * Le melange se fait au rendu, qui seul connait la couleur choisie.
11
+ */
12
+ depth?: number;
13
+ /**
14
+ * Forme non circulaire, en unites de rayon de boule et centree sur l'origine
15
+ * (le point du "!" penche est une goutte, pas un disque). Quand elle est
16
+ * fournie, `r` n'est plus utilise pour le trace.
17
+ */
18
+ d?: string;
19
+ /** rotation appliquee a `d`, en degres */
20
+ rot?: number;
21
+ }
22
+ /**
23
+ * Ce qu'un etat declare : la geometrie de l'arc reste en unites de rayon de
24
+ * boule, c'est le moteur (seul a connaitre l'echelle du viewBox) qui la
25
+ * rasterise. Sans ca les etats devraient connaitre le viewBox.
26
+ */
27
+ export interface ArcSpec {
28
+ id: string;
29
+ seed: ArcSeed;
30
+ t: number;
31
+ opacity: number;
32
+ }
33
+ export interface ArcRender {
34
+ id: string;
35
+ /** portion devant le corps */
36
+ front: string;
37
+ /** portion derriere le corps (dessinee avant, donc masquee par la silhouette) */
38
+ back: string;
39
+ width: number;
40
+ opacity: number;
41
+ /** degrade de teinte le long du trace */
42
+ grad: {
43
+ x1: number;
44
+ y1: number;
45
+ x2: number;
46
+ y2: number;
47
+ stops: string[];
48
+ };
49
+ }
50
+ export interface ArcSeed {
51
+ /** demi-grand axe, en unites de rayon de boule */
52
+ a: number;
53
+ /** aplatissement b/a : mesure <= 0.45, les plans d'orbite sont vus par la tranche */
54
+ k: number;
55
+ /** inclinaison du grand axe a l'ecran, radians */
56
+ tilt: number;
57
+ /** tours par seconde */
58
+ speed: number;
59
+ phase: number;
60
+ /** fraction du tour reellement tracee */
61
+ sweep: number;
62
+ hue: number;
63
+ hueSpan: number;
64
+ width: number;
65
+ cx: number;
66
+ cy: number;
67
+ }
68
+ /**
69
+ * Projette un cercle 3D incline en orthographique.
70
+ *
71
+ * Le cercle vit dans le plan engendre par u (dans l'ecran) et v (qui plonge
72
+ * dans la profondeur). La composante z sert a couper l'arc en deux : la moitie
73
+ * arriere est dessinee avant le corps, donc occultee par lui. C'est ce vrai tri
74
+ * en profondeur qui fait lire les anneaux comme des orbites et pas comme un
75
+ * dessin plat.
76
+ */
77
+ export declare function arcRender(seed: ArcSeed, t: number, scale: number, id: string, opacity?: number): ArcRender;
78
+ /**
79
+ * 6 anneaux, demi-grand axe 1.30-1.40 (donc nettement plus grands que la
80
+ * boule), aplatissement toujours <= 0.45, epaisseur 0.055, ~3.3 tours/s.
81
+ */
82
+ export declare const RINGS: ArcSeed[];
83
+ /**
84
+ * Bouquet d'arcs emboites qui balaie le triangle juste avant les orbites.
85
+ * Vus quasiment par la tranche (d'ou la forme en epingle a cheveux), rmax 1.37.
86
+ */
87
+ export declare const SWOOSH: ArcSeed[];
88
+ /** x mesures : -0.557 / -0.013 / +0.532, y = 0. */
89
+ export declare const DOT_X: readonly [-0.557, -0.013, 0.532];
90
+ export declare const DOT_R = 0.165;
91
+ export declare const DOT_PEAK = 1.25;
92
+ /**
93
+ * Les particules ne partent pas en ligne droite : elles spiralent vers le
94
+ * centre (rayon x0.75 par frame, angle +100 deg/s) en grossissant, et passent
95
+ * derriere le noyau ou elles sont avalees.
96
+ */
97
+ export declare function particles(t: number, scale: number): DotRender[];
98
+ export declare const COMET_RIBBONS: ArcSeed[];
99
+ /** Rayon du point de la comete, mesure a 0.129. */
100
+ export declare const COMET_DOT = 0.129;
101
+ /** Bleu releve au pixel. */
102
+ export declare const NOTIF_BLUE = "#2496e8";
103
+ /** La pastille est posee exactement sur la circonference, a -42deg. */
104
+ export declare const NOTIF_ANGLE = -42;
105
+ export declare const NOTIF_DIST = 1.003;
106
+ /** Rayon au repos ; le pop culmine 14 % au-dessus. */
107
+ export declare const NOTIF_R = 0.15;
108
+ export declare const NOTIF_POP = 1.14;
109
+ /**
110
+ * L'encoche est un disque concentrique a la pastille, soustrait du corps.
111
+ * La marge est constante (0.054 R) et suit l'echelle du corps.
112
+ */
113
+ export declare const NOTIF_MARGIN = 0.054;
@@ -0,0 +1,206 @@
1
+ import { type ArcRender, type DotRender } from './decor.js';
2
+ import { type BotExpression } from './expressions.js';
3
+ import { type StateId } from './states.js';
4
+ export interface RenderedEye {
5
+ d: string;
6
+ matrix: string;
7
+ alpha: number;
8
+ }
9
+ export interface BotFrame {
10
+ bodyPath: string;
11
+ bodyAlpha: number;
12
+ eyes: RenderedEye[];
13
+ dots: DotRender[];
14
+ /** true = les points passent derriere le corps (particules de l'eclatement) */
15
+ dotsBehind: boolean;
16
+ arcs: ArcRender[];
17
+ notif: {
18
+ x: number;
19
+ y: number;
20
+ r: number;
21
+ } | null;
22
+ notch: {
23
+ x: number;
24
+ y: number;
25
+ r: number;
26
+ } | null;
27
+ }
28
+ /**
29
+ * Ou le bot porte son regard quand quelque chose d'exterieur le pilote — le
30
+ * pointeur de la souris, aujourd'hui.
31
+ *
32
+ * `yaw` et `pitch` sont des directions ABSOLUES, qui remplacent celles de la pose
33
+ * a mesure que `mix` monte. Deux raisons, chacune un piege deja tombe :
34
+ *
35
+ * - c'est le MOTEUR qui doit faire ce melange, pas l'appelant, parce que lui seul
36
+ * connait la pose A CET INSTANT. Un appelant qui compenserait l'orientation de
37
+ * l'expression lirait sa valeur d'arrivee pendant que le morph est encore en
38
+ * cours, et les yeux sautaient a chaque changement d'humeur ;
39
+ * - et il faut que ce soit absolu sur les DEUX axes. En relatif, la hauteur des
40
+ * yeux suivait celle de chaque expression — « neutre » regarde a +28,6deg quand
41
+ * les autres sont entre -9 et +9 — donc les yeux tombaient d'un coup au premier
42
+ * changement d'humeur. Ce qui fait le caractere d'une expression pendant le
43
+ * suivi, c'est la FORME de ses yeux (plisses, ronds, dissymetriques), pas
44
+ * l'endroit ou elle regarde : celui-la, c'est le curseur qui le decide.
45
+ *
46
+ * `mix` dit a quel point l'exterieur commande la DIRECTION (0 = pas du tout).
47
+ *
48
+ * `wander` dit, separement, ce qui reste de derive automatique. Les deux ne se
49
+ * confondent pas : quand le pointeur bouge, la derive doit s'eteindre — cumulees,
50
+ * le bot aurait l'air de chercher le curseur sans jamais le tenir. Mais quand il
51
+ * n'y a PAS de pointeur (arrivee au clavier, au tactile, ou souris sortie de la
52
+ * fenetre), la tete doit rester tournee ET continuer de vivre. Les confondre
53
+ * figeait le regard des que la vue s'ouvrait.
54
+ *
55
+ * `spin` est un tour a parcourir EN CHEMIN, en degres, qu'on fait fondre vers 0
56
+ * avec l'arrivee. Comme les yeux vivent sur une sphere, un tour les fait passer
57
+ * derriere la boule et revenir de l'autre cote — et `-360deg` etant le meme
58
+ * angle que `0`, il ne change rien a l'endroit ou ils se posent.
59
+ */
60
+ export interface Look {
61
+ yaw: number;
62
+ pitch: number;
63
+ mix: number;
64
+ spin: number;
65
+ wander: number;
66
+ }
67
+ /**
68
+ * Moteur sans horloge : `sample(t)` est une fonction pure du temps.
69
+ *
70
+ * Consequence pratique : pause, reprise, ralenti et saut a une date arbitraire
71
+ * donnent exactement la meme image, et le rendu est testable sans DOM.
72
+ */
73
+ export declare class BotEngine {
74
+ /** rayon de la boule au repos, en unites de viewBox */
75
+ readonly scale: number;
76
+ private cur;
77
+ private prev;
78
+ /**
79
+ * Pose de depart FIGEE, posee seulement quand un changement d'etat arrive alors qu'un
80
+ * fondu est deja en cours. Cf. `setState`.
81
+ */
82
+ private departFige;
83
+ private tCur;
84
+ private tPrev;
85
+ private blinkAt;
86
+ private pts;
87
+ private shape;
88
+ private shapePrev;
89
+ private shapeAt;
90
+ private expr;
91
+ private exprPrev;
92
+ private exprAt;
93
+ private look;
94
+ private lookPrev;
95
+ private lookAt;
96
+ /** duree de rattrapage en cours ; voir `LOOK_MORPH`, sa valeur par defaut */
97
+ private lookMorph;
98
+ /** duree du morph quand on change la forme du corps */
99
+ static readonly SHAPE_MORPH = 0.45;
100
+ /**
101
+ * Duree de rattrapage du regard vers la cible. Plus court que `SHAPE_MORPH` :
102
+ * un regard qui suit doit paraitre attentif, pas visqueux. Comme la cible est
103
+ * reposee a chaque mouvement de souris, c'est cette duree qui donne au suivi
104
+ * son inertie — le regard n'atteint jamais tout a fait un curseur qui bouge.
105
+ */
106
+ static readonly LOOK_MORPH = 0.24;
107
+ constructor(scale?: number, initial?: StateId, shape?: number[] | null, expression?: BotExpression | null);
108
+ /**
109
+ * Expression de repos choisie dans le personnalisateur. Comme la forme, elle
110
+ * glisse vers la nouvelle valeur au lieu de sauter.
111
+ */
112
+ setExpression(expression: BotExpression | null, now?: number): void;
113
+ /** Expression effective a l'instant `now`, morph en cours compris. */
114
+ private exprAtTime;
115
+ /**
116
+ * Forme choisie dans le personnalisateur. Elle ne remplace le corps que sur
117
+ * les etats au repos (`baseBody`) : sur les autres, la silhouette EST
118
+ * l'animation et ne doit pas etre ecrasee.
119
+ *
120
+ * Le changement se fait en morph, pas d'un coup : comme toutes les formes sont
121
+ * echantillonnees aux memes angles, il suffit d'interpoler les rayons.
122
+ */
123
+ setShape(radii: number[] | null, now?: number): void;
124
+ /**
125
+ * Forme effective a l'instant `now`, morph en cours compris.
126
+ *
127
+ * Ne remet PAS `shapePrev` a null en fin de morph : `sample` doit rester une
128
+ * fonction pure du temps, donc relire une date passee doit redonner l'image
129
+ * intermediaire. On garde juste une reference de plus.
130
+ */
131
+ private shapeAtTime;
132
+ /**
133
+ * Nouvelle cible de regard, `null` pour revenir a celui de l'etat.
134
+ *
135
+ * Elle repart de la valeur COURANTE, et non de la cible precedente comme
136
+ * `setShape` : cette methode est appelee a chaque mouvement de pointeur, et
137
+ * repartir de l'ancienne cible ferait reculer le regard d'un cran avant
138
+ * chaque rattrapage — le suivi tremblerait au lieu de glisser.
139
+ *
140
+ * Meme contrat que `setShape` par ailleurs : l'etat externe entre par un
141
+ * setter horodate, jamais par une variable lue pendant `sample`, sinon le
142
+ * moteur cesse d'etre une fonction pure du temps.
143
+ */
144
+ setLook(look: Look | null, now: number, morph?: number): void;
145
+ /** Regard effectif a l'instant `now`, rattrapage en cours compris. */
146
+ private lookAtTime;
147
+ private posed;
148
+ /**
149
+ * Decalage des yeux a l'instant `now` pour un etat donne, en unites de rayon de boule.
150
+ *
151
+ * Il est LU dans une table et interpole, jamais recalcule : `eyefit.ts` explique
152
+ * pourquoi cette distinction est tout le correctif. Ici il ne reste qu'a l'interpoler
153
+ * sur l'axe de la forme, avec exactement la courbe et la duree du morph de silhouette
154
+ * — c'est la meme cause, donc ce doit etre le meme mouvement.
155
+ *
156
+ * On interroge la table sur les BORNES du morph (`shapePrev` et `shape`) et non sur le
157
+ * profil que rend `shapeAtTime` : celui-la est un tableau neuf alloue a chaque image,
158
+ * donc sans identite, et il n'existe dans aucune table.
159
+ */
160
+ private decalageAtTime;
161
+ get state(): StateId;
162
+ /**
163
+ * Repart sur `id` SANS etat precedent, comme un moteur neuf pose sur cet etat.
164
+ *
165
+ * C'est ce que veut dire « rembobiner » pour ce moteur. `setState` seul ne peut pas le
166
+ * faire : il garde l'etat quitte pour le fondre, ce qui est exactement son role en
167
+ * lecture, et exactement ce qu'il ne faut pas quand on revient au debut d'une sequence.
168
+ * Rejouer l'image 0 apres une passe complete melangeait le premier etat avec le DERNIER,
169
+ * et l'export GIF s'ouvrait sur une boule sans yeux — la comete a un `eyeAlpha` nul.
170
+ *
171
+ * `sample` reste une fonction pure du temps : comme `setState`, ceci est un setter DATE,
172
+ * appele par le pilote de la sequence, jamais pendant un echantillonnage.
173
+ */
174
+ reset(id: StateId, now: number): void;
175
+ /**
176
+ * Origine du fondu en cours : la pose figee s'il y en a une, sinon l'etat quitte evalue
177
+ * a son propre temps ecoule — donc encore en train de s'animer, ce qui est voulu.
178
+ */
179
+ private origine;
180
+ /**
181
+ * Pose composite a l'instant `now`, fondu en cours compris : exactement ce que `sample`
182
+ * melange, avant la couche de vie au repos et de regard. Extraite pour que `setState`
183
+ * puisse la figer.
184
+ */
185
+ private poseComposee;
186
+ /**
187
+ * Changement d'etat, date.
188
+ *
189
+ * Le moteur ne garde qu'UNE case d'historique, donc un changement qui arrive pendant un
190
+ * fondu remplacait l'origine du melange par la pose PLEINE de l'etat qu'on quittait, au
191
+ * lieu de l'image partiellement melangee qui etait a l'ecran. Mesure sur
192
+ * `idle -> wide -> idle` a 100 ms : 35,9 px de saut contre 8,0 px de mouvement normal.
193
+ *
194
+ * On fige donc la pose composite courante et on melange depuis elle. Continu par
195
+ * construction, quel que soit le nombre de changements enchaines.
196
+ *
197
+ * Et SEULEMENT dans ce cas. Figer a chaque changement arreterait net l'animation de
198
+ * l'etat qu'on quitte pendant tout le fondu — le « ! » d'`alert` se figerait en pleine
199
+ * course — alors qu'il n'y a rien a corriger hors morph : l'etat quitte y est deja
200
+ * exactement l'image affichee. La lecture d'un montage, dont les blocs durent au moins
201
+ * le plus long fondu (`MIN_BLOCK`), ne fige donc jamais rien et rend au bit ce qu'elle
202
+ * rendait.
203
+ */
204
+ setState(id: StateId, now: number): void;
205
+ sample(now: number): BotFrame;
206
+ }
@@ -0,0 +1,34 @@
1
+ import { type HeadGaze } from './face.js';
2
+ import type { EyeCfg } from './states.js';
3
+ /**
4
+ * Expression de repos du bot.
5
+ *
6
+ * Le visage ne tient qu'à deux gélules, donc tout se joue sur quatre leviers :
7
+ * l'orientation de la tête, l'écart des yeux, leurs proportions, et
8
+ * l'inclinaison propre de chaque œil. C'est ce dernier qui permet la colère et
9
+ * la tristesse : elles demandent des inclinaisons EN MIROIR (les hauts qui
10
+ * convergent ou divergent), impossible avec le seul roulis de tête qui incline
11
+ * les deux yeux du même côté.
12
+ *
13
+ * Seul l'état de repos porte cette expression. Les états expressifs de la vidéo
14
+ * (clin d'œil, yeux écarquillés, notification) gardent la leur : c'est elle
15
+ * qu'on est venu reproduire.
16
+ *
17
+ * Les amplitudes s'appuient sur bible-strong-avatar-lab, qui expose le même
18
+ * modèle (tête X/Y/Z, largeur et hauteur par œil, écart, angle par œil) : chez
19
+ * eux la largeur va de 0,8 à 2,7 fois le neutre, la hauteur de 0,3 à 1,5, et
20
+ * les angles jusqu'à ±80°. On reste dans cette enveloppe.
21
+ */
22
+ /** Enumeres pour que la couche i18n verifie leurs traductions a la compilation. */
23
+ export type ExpressionId = 'neutre' | 'attentif' | 'surpris' | 'excite' | 'heureux' | 'hilare' | 'colere' | 'triste' | 'effraye' | 'mefiant' | 'confus' | 'curieux' | 'fier' | 'timide' | 'blase' | 'somnolent';
24
+ export interface BotExpression {
25
+ id: ExpressionId;
26
+ gaze: HeadGaze;
27
+ split: number;
28
+ eyes: [EyeCfg, EyeCfg];
29
+ }
30
+ export declare const EXPRESSIONS: BotExpression[];
31
+ export declare const EXPRESSION_BY_ID: Map<string, BotExpression>;
32
+ export declare const DEFAULT_EXPRESSION = "neutre";
33
+ /** Interpolation de deux expressions : le changement se fait en glissant. */
34
+ export declare function blendExpression(a: BotExpression, b: BotExpression, t: number): BotExpression;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Ou poser le visage sur une forme du personnalisateur.
3
+ *
4
+ * Les yeux vivent sur une sphere, et `radiusAtAngle` les recolle au contour reel au
5
+ * prorata du rayon local. Ce prorata place bien leur CENTRE, mais l'oeil a une taille :
6
+ * la marge qui lui reste devant le bord est multipliee par le meme facteur, donc une
7
+ * silhouette etroite dans sa direction le pousse contre le bord jusqu'a ce que le
8
+ * masque l'ouvre vers l'exterieur. La gelule apparaissait comme une encoche dans le
9
+ * corps sur `capsule`, `triangle`, `nuage` et `goutte`.
10
+ *
11
+ * Ce module resout le probleme UNE FOIS, au chargement, et rend une table de decalages.
12
+ * Ce choix est l'essentiel du correctif, bien plus que la geometrie qui suit :
13
+ *
14
+ * Resolue dans la boucle de rendu, la correction reagit a tout ce qui bouge a soixante
15
+ * images par seconde — la derive du regard, le pointeur, l'expression en cours de
16
+ * morph, le bord le plus proche qui change, l'oeil le plus contraint qui change. Sept
17
+ * variantes ont ete ecrites ainsi et toutes produisaient un artefact de mouvement
18
+ * visible : tremblement permanent, saut de direction de 26 unites quand le bord de
19
+ * reference basculait, grossissement brusque quand la taille entrait dans le calcul.
20
+ * Le defaut n'etait dans aucune de leurs geometries, il etait dans le fait de resoudre
21
+ * par image.
22
+ *
23
+ * Le reste du moteur ne travaille pas comme ca : les poses sont DECLAREES et il ne fait
24
+ * que les interpoler avec des courbes connues. Un decalage tabule rentre dans ce moule.
25
+ * Il ne bouge pas quand le regard derive ni quand le pointeur bouge, et sur un changement
26
+ * de forme ou d'expression il ne fait qu'aller d'une entree de table a l'autre, sur la
27
+ * courbe de ce morph. Le tremblement devient impossible par construction, au lieu d'etre
28
+ * repousse : interpoler entre deux constantes est monotone, alors que re-resoudre le
29
+ * probleme sur un regard en cours d'interpolation ne l'est pas.
30
+ *
31
+ * Corollaire agreable : le solveur n'a plus aucune contrainte de continuite, puisqu'il ne
32
+ * tourne pas pendant l'animation. Il peut donc sonder tout un faisceau de directions et
33
+ * couvrir le pire cas de la derive du regard, ce qu'une version par image ne pouvait pas
34
+ * se permettre.
35
+ *
36
+ * La table est une constante de module, batie a l'import a partir de donnees pures :
37
+ * meme nature que le calendrier de clignements de `face.ts`, deterministe et sans etat,
38
+ * donc sans effet sur la purete de `engine.sample(t)`.
39
+ */
40
+ import { type StateId } from './states.js';
41
+ /**
42
+ * Table des decalages, batie a l'import : une entree par (forme, etat a corps de base,
43
+ * expression). Seuls `idle` et `swirl` portent le visage de repos, donc seuls eux se
44
+ * declinent par expression — les trois autres etats a corps de base ont un visage releve
45
+ * sur la video et une seule entree.
46
+ *
47
+ * Clef par REFERENCE du tableau de rayons, ce qui est deja la convention du moteur : ses
48
+ * gardes `radii === this.shape` et `expression === this.expr` reposent sur la meme
49
+ * stabilite. Un profil inconnu, ou `null`, ne corrige rien — l'API accepte n'importe quel
50
+ * tableau et le moteur n'a pas a dependre de la prudence de ses appelants.
51
+ */
52
+ declare function batir(): Map<number[], Map<string, {
53
+ x: number;
54
+ y: number;
55
+ }>>;
56
+ /**
57
+ * Decalage a appliquer aux deux yeux pour cette forme sur cet etat, en unites de rayon
58
+ * de boule — le moteur le remet a son echelle.
59
+ *
60
+ * Vaut zero des que la forme n'est pas au catalogue, ce qui couvre `null` et le cercle :
61
+ * sur le cercle les deux profils sont le meme, donc la marge est deja celle exigee et la
62
+ * descente sort au premier tour. La forme relevee sur la video ne bouge donc pas, sans
63
+ * cas particulier.
64
+ */
65
+ export declare function decalageDesYeux(radii: number[] | null, state: StateId, expr: string | null): {
66
+ x: number;
67
+ y: number;
68
+ };
69
+ /** Pour les tests : de quoi verifier la table sans refaire la geometrie. */
70
+ /** Pour les tests : de quoi chronometrer la construction de la table. */
71
+ export declare const POUR_TESTS: {
72
+ batir: typeof batir;
73
+ };
74
+ export {};
@@ -0,0 +1,62 @@
1
+ /** Demi-ecart des yeux sur la sphere, en degres (separation totale ~31deg). */
2
+ export declare const EYE_SPLIT = 15.46;
3
+ /** Taille de l'oeil au repos, en unites de rayon de boule. */
4
+ export declare const EYE_W = 0.186;
5
+ export declare const EYE_H = 0.412;
6
+ /** Orientation de tete au repos, ajustee sur les frames de reference. */
7
+ export declare const REST_GAZE: HeadGaze;
8
+ export interface EyePose {
9
+ x: number;
10
+ y: number;
11
+ /** matrice tangente 2x2 : [a b c d] au sens SVG matrix(a,b,c,d,e,f) */
12
+ a: number;
13
+ b: number;
14
+ c: number;
15
+ d: number;
16
+ /** composante z de la normale : > 0 = face visible */
17
+ depth: number;
18
+ }
19
+ export interface HeadGaze {
20
+ /** lacet, degres, positif = regarde a droite */
21
+ yaw: number;
22
+ /** tangage, degres, positif = regarde en haut */
23
+ pitch: number;
24
+ /** roulis, degres, inclinaison de la tete */
25
+ roll: number;
26
+ }
27
+ /**
28
+ * Repere de la tete puis des deux yeux.
29
+ * Repere ecran : x a droite, y vers le bas, z vers le spectateur.
30
+ * L'indice 0 est l'oeil interieur, l'indice 1 l'oeil exterieur.
31
+ */
32
+ export declare function eyePoses(gaze: HeadGaze, scale: number, split?: number): [EyePose, EyePose];
33
+ /**
34
+ * Vie au repos : derive lente du regard, saccades, clignements.
35
+ *
36
+ * Fonction pure du temps (aucun etat interne), donc pause, reprise et saut a
37
+ * une date arbitraire donnent toujours la meme image. Les valeurs sont des
38
+ * ECARTS a ajouter a la pose de l'etat courant.
39
+ */
40
+ export interface Liveliness {
41
+ dYaw: number;
42
+ dPitch: number;
43
+ dRoll: number;
44
+ /** 1 = oeil ouvert, 0 = ferme (ecrasement vertical en repere ecran) */
45
+ lid: number;
46
+ driftX: number;
47
+ driftY: number;
48
+ breath: number;
49
+ }
50
+ export interface LivelinessOptions {
51
+ wander?: number;
52
+ blink?: boolean;
53
+ float?: boolean;
54
+ }
55
+ export declare function liveliness(t: number, opt?: LivelinessOptions): Liveliness;
56
+ /**
57
+ * Le clignement est un ecrasement VERTICAL en repere ecran autour du centre de
58
+ * l'oeil (mesure : la largeur de bbox est conservee, la hauteur tombe a ~0.35),
59
+ * pas un retrecissement le long de l'axe incline de la gelule. On le compose
60
+ * donc apres la matrice tangente, en n'affectant que les sorties en y.
61
+ */
62
+ export declare function blinkScale(lid: number): number;