avbd-babylon 0.1.0 → 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.
package/dist/avbd.js CHANGED
@@ -1,4 +1,4 @@
1
- var L=(o=0,e=0,t=0)=>Float64Array.of(o,e,t),Bi=(o=0,e=0,t=0,i=1)=>Float64Array.of(o,e,t,i),Y=()=>new Float64Array(9);var Yo=o=>o<0?-1:o>0?1:0,et=(o,e)=>o<e?o:e,Yn=(o,e)=>o>e?o:e,Re=(o,e,t)=>Yn(e,et(t,o)),Z=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],tt=o=>Z(o,o),wi=o=>Math.sqrt(tt(o));function qe(o,e,t,i){return o[0]=e,o[1]=t,o[2]=i,o}var Sr=(o,e,t)=>qe(o,e[0]+t[0],e[1]+t[1],e[2]+t[2]),X=(o,e,t)=>qe(o,e[0]-t[0],e[1]-t[1],e[2]-t[2]),we=(o,e,t)=>qe(o,e[0]*t,e[1]*t,e[2]*t),Jt=(o,e,t)=>qe(o,e[0]/t,e[1]/t,e[2]/t),de=(o,e)=>qe(o,-e[0],-e[1],-e[2]),Fs=(o,e)=>qe(o,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),U=(o,e,t,i)=>qe(o,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),Qn=(o,e)=>Jt(o,e,wi(e)),_t=(o,e,t)=>qe(o,e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]);function Ai(o,e,t,i,r){return o[0]=e,o[1]=t,o[2]=i,o[3]=r,o}function Qo(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=t[0],l=t[1],c=t[2],f=t[3];return Ai(o,a*n+i*f+r*c-s*l,a*l-i*c+r*f+s*n,a*c+i*l-r*n+s*f,a*f-i*n-r*l-s*c)}var Kn=(o,e)=>Ai(o,-e[0],-e[1],-e[2],e[3]),Ko=o=>tt(o)+o[3]*o[3];function Zn(o,e){let t=Ko(e);return Ai(o,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function el(o,e){let t=Math.sqrt(Ko(e));return Ai(o,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var xi=new Float64Array(4),Wt=new Float64Array(4);function Et(o,e,t){return Qo(xi,e,Zn(xi,t)),qe(o,xi[0]*2,xi[1]*2,xi[2]*2)}function Cr(o,e,t){return Ai(Wt,t[0],t[1],t[2],0),Qo(Wt,Wt,e),el(o,Ai(o,e[0]+Wt[0]*.5,e[1]+Wt[1]*.5,e[2]+Wt[2]*.5,e[3]+Wt[3]*.5))}function ce(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=(r*t[2]-s*t[1])*2,l=(s*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return qe(o,t[0]+n*a+(r*c-s*l),t[1]+l*a+(s*n-i*c),t[2]+c*a+(i*l-r*n))}var Xo=new Float64Array(3);function ht(o,e,t,i){return ce(Xo,t,i),Sr(o,Xo,e)}function Si(o,e,t){return ce(o,Kn(xi,e),t)}function Oe(o,e,t,i){return o.fill(0),o[0]=e,o[4]=t,o[8]=i,o}function Ns(o,e){return o[0]=0,o[1]=-e[2],o[2]=e[1],o[3]=e[2],o[4]=0,o[5]=-e[0],o[6]=-e[1],o[7]=e[0],o[8]=0,o}function $t(o,e){let t=e[1],i=e[2],r=e[5];return o[0]=e[0],o[1]=e[3],o[2]=e[6],o[3]=t,o[4]=e[4],o[5]=e[7],o[6]=i,o[7]=r,o[8]=e[8],o}function je(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=e[4],l=e[5],c=e[6],f=e[7],u=e[8],d=t[0],h=t[1],m=t[2],p=t[3],b=t[4],y=t[5],A=t[6],w=t[7],g=t[8];return o[0]=i*d+r*p+s*A,o[1]=i*h+r*b+s*w,o[2]=i*m+r*y+s*g,o[3]=a*d+n*p+l*A,o[4]=a*h+n*b+l*w,o[5]=a*m+n*y+l*g,o[6]=c*d+f*p+u*A,o[7]=c*h+f*b+u*w,o[8]=c*m+f*y+u*g,o}function ie(o,e,t){let i=t[0],r=t[1],s=t[2];return qe(o,e[0]*i+e[1]*r+e[2]*s,e[3]*i+e[4]*r+e[5]*s,e[6]*i+e[7]*r+e[8]*s)}function Se(o,e){for(let t=0;t<9;t++)o[t]+=e[t];return o}function Xt(o,e,t){for(let i=0;i<9;i++)o[i]=e[i]*t;return o}function Zo(o,e){for(let t=0;t<9;t++)o[t]=-e[t];return o}function kr(o,e,t){for(let i=0;i<3;i++)for(let r=0;r<3;r++)o[i*3+r]=t[r]*e[i];return o}function ea(o,e){let t=Math.sqrt(e[0]*e[0]+e[3]*e[3]+e[6]*e[6]),i=Math.sqrt(e[1]*e[1]+e[4]*e[4]+e[7]*e[7]),r=Math.sqrt(e[2]*e[2]+e[5]*e[5]+e[8]*e[8]);return Oe(o,t,i,r)}function ta(o,e){let t=Math.abs(e[0])>Math.abs(e[2])?L(-e[1],e[0],0):L(0,-e[2],e[1]);Qn(t,t);let i=_t(L(),e,t);return o[0]=e[0],o[1]=e[1],o[2]=e[2],o.set(t,3),o.set(i,6),o}function ia(o,e,t,i,r,s,a){let n=o[0],l=o[3],c=o[4],f=o[6],u=o[7],d=o[8],h=t[0],m=t[1],p=t[2],b=e[0],y=t[3],A=t[4],w=t[5],g=e[3],x=e[4],v=t[6],B=t[7],E=t[8],k=e[6],z=e[7],M=e[8],P=l/n,_=f/n,S=h/n,C=y/n,R=v/n,I=n,O=c-P*P*I,q=(u-P*_*I)/O,G=(m-P*S*I)/O,j=(A-P*C*I)/O,F=(B-P*R*I)/O,W=d-(_*_*I+q*q*O),ee=(p-_*S*I-q*G*O)/W,ne=(w-_*C*I-q*j*O)/W,ve=(E-_*R*I-q*F*O)/W,N=b-(S*S*I+G*G*O+ee*ee*W),Ee=(g-S*C*I-G*j*O-ee*ne*W)/N,ze=(k-S*R*I-G*F*O-ee*ve*W)/N,le=x-(C*C*I+j*j*O+ne*ne*W+Ee*Ee*N),Fe=(z-C*R*I-j*F*O-ne*ve*W-Ee*ze*N)/le,Lt=M-(R*R*I+F*F*O+ve*ve*W+ze*ze*N+Fe*Fe*le),Me=i[0],Ne=i[1]-P*Me,Ie=i[2]-_*Me-q*Ne,Ue=r[0]-S*Me-G*Ne-ee*Ie,Ze=r[1]-C*Me-j*Ne-ne*Ie-Ee*Ue,bi=r[2]-R*Me-F*Ne-ve*Ie-ze*Ue-Fe*Ze,gi=Me/I,yi=Ne/O,vi=Ie/W,At=Ue/N,$n=Ze/le,Xn=bi/Lt;a[2]=Xn,a[1]=$n-Fe*a[2],a[0]=At-Ee*a[1]-ze*a[2],s[2]=vi-ee*a[0]-ne*a[1]-ve*a[2],s[1]=yi-q*s[2]-G*a[0]-j*a[1]-F*a[2],s[0]=gi-P*s[1]-_*s[2]-S*a[0]-C*a[1]-R*a[2]}var Ki=1,pt=1e10,ra=.01,sa=1e-5,at=class{solver;forces=[];positionLin;positionAng=Bi();initialLin=L();initialAng=Bi();inertialLin=L();inertialAng=Bi();velocityLin;velocityAng=L();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,s,a=[0,0,0]){this.solver=e,this.size=L(t[0],t[1],t[2]),this.friction=r,this.positionLin=L(s[0],s[1],s[2]),this.velocityLin=L(a[0],a[1],a[2]),this.prevVelocityLin=L(a[0],a[1],a[2]);let[n,l,c]=this.size;this.mass=n*l*c*i,this.moment=L((l*l+c*c)/12*this.mass,(n*n+c*c)/12*this.mass,(n*n+l*l)/12*this.mass),this.radius=wi([n*.5,l*.5,c*.5]),e.bodies.push(this)}constrainedTo(e){for(let t of this.forces)if(t.bodyA===this&&t.bodyB===e||t.bodyA===e&&t.bodyB===this)return!0;return!1}},oa=()=>({lhsLin:Y(),lhsAng:Y(),lhsCross:Y(),rhsLin:L(),rhsAng:L()}),zt=class{solver;bodyA;bodyB;constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}};var ua=8,aa=16,Qt=1e-6,qs=1e-5,tl=1e-6,Vs=34028234663852886e22,Pr=0,fa=1,Lr=2;function na(o){let e=o.positionAng;return{center:o.positionLin,half:we(L(),o.size,.5),axis:[ce(L(),e,[1,0,0]),ce(L(),e,[0,1,0]),ce(L(),e,[0,0,1])]}}var Yt=(o,e)=>Math.abs(Z(o,e));function _r(o,e){let t=Z(e,o.axis[0])>=0?1:-1,i=Z(e,o.axis[1])>=0?1:-1,r=Z(e,o.axis[2])>=0?1:-1,s=U(L(),o.center,o.axis[0],o.half[0]*t);return U(s,s,o.axis[1],o.half[1]*i),U(s,s,o.axis[2],o.half[2]*r)}function da(o,e){return e===0?{u:o.axis[1],v:o.axis[2],extentU:o.half[1],extentV:o.half[2]}:e===1?{u:o.axis[0],v:o.axis[2],extentU:o.half[0],extentV:o.half[2]}:{u:o.axis[0],v:o.axis[1],extentU:o.half[0],extentV:o.half[1]}}function il(o,e,t){let i=Z(t,o.axis[e])>=0?1:-1,r=we(L(),o.axis[e],i),s=U(L(),o.center,r,o.half[e]);return{normal:r,center:s,...da(o,e)}}function rl(o,e){let t=0,i=-Vs;for(let r=0;r<3;r++){let s=Yt(o.axis[r],e);s>i&&(i=s,t=r)}return t}function sl(o,e,t){let i=Z(o.axis[e],t)>0?-1:1,r=we(L(),o.axis[e],i),s=U(L(),o.center,r,o.half[e]),{u:a,v:n,extentU:l,extentV:c}=da(o,e),f=(u,d)=>{let h=U(L(),s,a,u*l);return U(h,h,n,d*c)};return[f(1,1),f(-1,1),f(-1,-1),f(1,-1)]}function ol(o,e,t){let i=[];if(o.length===0)return i;let r=o[o.length-1],s=Z(e,r)-t;for(let a of o){let n=Z(e,a)-t,l=s<=qs,c=n<=qs;if(l!==c){let f=0,u=s-n;Math.abs(u)>Qt&&(f=Re(s/u,0,1)),i.length<aa&&i.push(U(L(),r,X(L(),a,r),f))}c&&i.length<aa&&i.push(a),r=a,s=n}return i}var Er=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=we(L(),Sr(L(),e,t),.5);for(let n of this.midpoints)if(tt(X(L(),r,n))<tl)return;if(this.contacts.length>=ua)return;let{bodyA:s,bodyB:a}=this;this.contacts.push({feature:i,rA:Si(L(),s.positionAng,X(L(),e,s.positionLin)),rB:Si(L(),a.positionAng,X(L(),t,a.positionLin))}),this.midpoints.push(r)}};function Us(o,e,t,i,r,s,a,n){let l=tt(i);if(l<Qt)return!0;let c=1/Math.sqrt(l),f=we(L(),i,c);Z(f,t)<0&&de(f,f);let u=Math.abs(Z(t,f)),d=o.half[0]*Yt(f,o.axis[0])+o.half[1]*Yt(f,o.axis[1])+o.half[2]*Yt(f,o.axis[2]),h=e.half[0]*Yt(f,e.axis[0])+e.half[1]*Yt(f,e.axis[1])+e.half[2]*Yt(f,e.axis[2]),m=u-(d+h);return m>0?!1:((!n.valid||m>n.separation)&&(n.valid=!0,n.type=r,n.indexA=s,n.indexB=a,n.separation=m,n.normalAB=f),!0)}function la(o,e,t){let i=(e+1)%3,r=(e+2)%3,s=Z(t,o.axis[i])>=0?1:-1,a=Z(t,o.axis[r])>=0?1:-1,n=U(L(),o.center,o.axis[i],o.half[i]*s);U(n,n,o.axis[r],o.half[r]*a);let l=we(L(),o.axis[e],o.half[e]);return[X(L(),n,l),Sr(L(),n,l)]}function al(o,e,t,i){let r=X(L(),e,o),s=X(L(),i,t),a=X(L(),o,t),n=Z(r,r),l=Z(s,s),c=Z(s,a),f=0,u=0;if(n<=Qt&&l<=Qt)return[o,t];if(n<=Qt)u=Re(c/l,0,1);else{let d=Z(r,a);if(l<=Qt)f=Re(-d/n,0,1);else{let h=Z(r,s),m=n*l-h*h;Math.abs(m)>Qt&&(f=Re((h*c-d*l)/m,0,1)),u=(h*f+c)/l,u<0?(u=0,f=Re(-d/n,0,1)):u>1&&(u=1,f=Re((h-d)/n,0,1))}}return[U(L(),o,r,f),U(L(),t,s,u)]}function ca(o,e,t,i,r,s,a){let n=r?t:i,l=r?i:t,c=r?a:de(L(),a),f=il(n,s,c),u=rl(l,f.normal),d=sl(l,u,f.normal),h=[[f.u,f.extentU],[de(L(),f.u),f.extentU],[f.v,f.extentV],[de(L(),f.v),f.extentV]];for(let[b,y]of h)if(d=ol(d,b,Z(b,f.center)+y),!d.length)return[];let m=new Er(o,e),p=(r?Pr:fa)<<24|(s&255)<<16|(u&255)<<8;for(let b=0;b<d.length&&m.contacts.length<ua;b++){let y=d[b],A=Z(X(L(),y,f.center),f.normal);if(A>qs)continue;let w=U(L(),y,f.normal,-A),g=r?w:y,x=r?y:w;m.add(g,x,p|b&255)}return m.contacts.length||m.add(_r(t,a),_r(i,de(L(),a)),p),m.contacts}function nl(o,e,t,i,r,s,a){let[n,l]=la(t,r,a),[c,f]=la(i,s,de(L(),a)),[u,d]=al(n,l,c,f),h=new Er(o,e),m=Lr<<24|(r&255)<<8|s&255;return h.add(u,d,m),h.contacts.length||h.add(_r(t,a),_r(i,de(L(),a)),m),h.contacts}function ha(o,e,t){let i=na(o),r=na(e),s=X(L(),r.center,i.center),a={type:Pr,indexA:0,indexB:0,separation:-Vs,normalAB:L(),valid:!1},n={type:Lr,indexA:0,indexB:0,separation:-Vs,normalAB:L(),valid:!1};for(let f=0;f<3;f++)if(!Us(i,r,s,i.axis[f],Pr,f,-1,a))return[];for(let f=0;f<3;f++)if(!Us(i,r,s,r.axis[f],fa,-1,f,a))return[];let l=L();for(let f=0;f<3;f++)for(let u=0;u<3;u++)if(_t(l,i.axis[f],r.axis[u]),!Us(i,r,s,l,Lr,f,u,n))return[];if(!a.valid)return[];let c=a;return n.valid&&.95*n.separation>a.separation+.01&&(c=n),ta(t,de(L(),c.normalAB)),c.type===Lr?nl(o,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===Pr?ca(o,e,i,r,!0,c.indexA,c.normalAB):ca(o,e,i,r,!1,c.indexB,c.normalAB)}var pa=L(),ma=L(),ba=L(),ga=L(),ya=L(),va=L(),zr=Y(),Hs=Y(),Ws=Y(),Mr=Y(),Js=Y(),$s=Y(),Xs=Y(),Ir=Y(),he=L(),xe=L(),Kt=L(),Mt=L(),xa=L(),Aa=L(),Ci=0,Zi=0;function Ba(o,e,t){for(let i=0;i<3;i++)Mt[0]=t[i*3],Mt[1]=t[i*3+1],Mt[2]=t[i*3+2],_t(Mt,e,Mt),o[i*3]=Mt[0],o[i*3+1]=Mt[1],o[i*3+2]=Mt[2];return o}var ki=class extends zt{contacts=[];basis=Y();friction=0;constructor(e,t,i){super(e,t,i)}get numContacts(){return this.contacts.length}initialize(){let e=this.bodyA,t=this.bodyB,{solver:i}=this;this.friction=Math.sqrt(e.friction*t.friction);let r=this.contacts;this.contacts=ha(e,t,this.basis).map(s=>{let a=r.find(n=>n.feature===s.feature);return a?{feature:s.feature,rA:a.stick?a.rA:s.rA,rB:a.stick?a.rB:s.rB,C0:L(),penalty:a.penalty.slice(),lambda:a.lambda.slice(),stick:a.stick}:{feature:s.feature,rA:s.rA,rB:s.rB,C0:L(),penalty:L(),lambda:L(),stick:!1}});for(let s of this.contacts){ht(xa,e.positionLin,e.positionAng,s.rA),ht(Aa,t.positionLin,t.positionAng,s.rB),ie(s.C0,this.basis,X(Kt,xa,Aa)),s.C0[0]+=ra;for(let a=0;a<3;a++)s.lambda[a]=s.lambda[a]*i.alpha*i.gamma,s.penalty[a]=Re(s.penalty[a]*i.gamma,Ki,pt)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,s=this.basis;ce(ya,i.positionAng,e.rA),ce(va,r.positionAng,e.rB),Zo(zr,s),Ba(Hs,ya,s),Ba(Ws,va,zr),Oe(Mr,e.penalty[0],e.penalty[1],e.penalty[2]),we(he,e.C0,1-t),U(he,he,ie(Kt,s,pa),1),U(he,he,ie(Kt,zr,ba),1),U(he,he,ie(Kt,Hs,ma),1),U(he,he,ie(Kt,Ws,ga),1),ie(xe,Mr,he),U(xe,xe,e.lambda,1),xe[0]=et(xe[0],0),Zi=Math.abs(xe[0])*this.friction,Ci=Math.sqrt(xe[1]*xe[1]+xe[2]*xe[2]),Ci>Zi&&Ci>0&&(xe[1]*=Zi/Ci,xe[2]*=Zi/Ci)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;X(pa,e.positionLin,e.initialLin),Et(ma,e.positionAng,e.initialAng),X(ba,t.positionLin,t.initialLin),Et(ga,t.positionAng,t.initialAng)}updatePrimal(e,t,i){this.computeDisplacements();let r=e===this.bodyA;for(let s of this.contacts){this.evaluate(s,t);let a=r?this.basis:zr,n=r?Hs:Ws;$t(Js,a),$t($s,n),je(Xs,$s,Mr),Se(i.lhsLin,je(Ir,je(Ir,Js,Mr),a)),Se(i.lhsAng,je(Ir,Xs,n)),Se(i.lhsCross,je(Ir,Xs,a)),U(i.rhsLin,i.rhsLin,ie(Kt,Js,xe),1),U(i.rhsAng,i.rhsAng,ie(Kt,$s,xe),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(xe),xe[0]<0&&(i.penalty[0]=et(i.penalty[0]+t.betaLin*Math.abs(he[0]),pt)),Ci<=Zi&&(i.penalty[1]=et(i.penalty[1]+t.betaLin*Math.abs(he[1]),pt),i.penalty[2]=et(i.penalty[2]+t.betaLin*Math.abs(he[2]),pt),i.stick=Math.sqrt(he[1]*he[1]+he[2]*he[2])<sa)}};var Ys=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),Pi=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,Ys())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,s=L(),a=L();for(let n=this.bodies.length-1;n>=0;n--){let l=this.bodies[n];if(l.mass<=0)continue;Si(s,l.positionAng,X(s,e,l.positionLin)),Si(a,l.positionAng,t);let c=0,f=1/0,u=!0;for(let h=0;h<3&&u;h++){let m=l.size[h]*.5;if(Math.abs(a[h])<1e-6){(s[h]<-m||s[h]>m)&&(u=!1);continue}let p=1/a[h],b=(-m-s[h])*p,y=(m-s[h])*p;b>y&&([b,y]=[y,b]),c=Math.max(c,b),f=Math.min(f,y),c>f&&(u=!1)}if(!u)continue;let d=c>=0?c:f;d<0||(!r||d<r.t)&&(r={body:l,local:U(L(),s,a,d),t:d})}return r}sys=oa();dxLin=L();dxAng=L();dp=L();accel=L();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:s}=this;for(let n=i.length-1;n>=0;n--){let l=i[n];for(let c=n-1;c>=0;c--){let f=i[c];X(s,l.positionLin,f.positionLin);let u=l.radius+f.radius;Z(s,s)<=u*u&&!l.constrainedTo(f)&&new ki(this,l,f)}}let a=[];for(let n of this.forces)n.initialize()?a.push(n):n.unlinkFromBodies();this.forces=a;for(let n=i.length-1;n>=0;n--){let l=i[n];U(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),Cr(l.inertialAng,l.positionAng,wa(l.velocityAng,e)),Jt(this.accel,X(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*Yo(t),f=Re(c/Math.abs(t),0,1);Number.isFinite(f)||(f=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(U(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(f*e*e),Cr(l.positionAng,l.positionAng,wa(l.velocityAng,e)))}for(let n=0;n<this.iterations;n++){for(let l=i.length-1;l>=0;l--){let c=i[l];if(c.mass<=0)continue;let f=c.mass,u=c.moment;Oe(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),Oe(r.lhsAng,u[0]/(e*e),u[1]/(e*e),u[2]/(e*e)),r.lhsCross.fill(0),ie(r.rhsLin,r.lhsLin,X(s,c.positionLin,c.inertialLin)),ie(r.rhsAng,r.lhsAng,Et(s,c.positionAng,c.inertialAng));let d=c.forces;for(let h=d.length-1;h>=0;h--)d[h].updatePrimal(c,this.alpha,r);ia(r.lhsLin,r.lhsAng,r.lhsCross,de(r.rhsLin,r.rhsLin),de(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),U(c.positionLin,c.positionLin,this.dxLin,1),Cr(c.positionAng,c.positionAng,this.dxAng)}for(let l of this.forces)l.updateDual(this.alpha)}for(let n of i)n.prevVelocityLin.set(n.velocityLin),n.mass>0&&(Jt(n.velocityLin,X(n.velocityLin,n.positionLin,n.initialLin),e),Jt(n.velocityAng,Et(n.velocityAng,n.positionAng,n.initialAng),e))}},ll=L(),wa=(o,e)=>we(ll,o,e);var oe=(o,e)=>[o[0]-e[0],o[1]-e[1],o[2]-e[2]],nt=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],Zt=(o,e)=>[o[1]*e[2]-o[2]*e[1],o[2]*e[0]-o[0]*e[2],o[0]*e[1]-o[1]*e[0]],Te=o=>Math.sqrt(nt(o,o));function er(o){let e=Sa(o);return!e||e.vertices.length/3<=32?e:Sa(cl(e,32))}function cl(o,e){let[t,i,r,s]=o.rotation,a=u=>{let d=[2*(i*u[2]-r*u[1]),2*(r*u[0]-t*u[2]),2*(t*u[1]-i*u[0])];return[u[0]+s*d[0]+(i*d[2]-r*d[1])+o.center[0],u[1]+s*d[1]+(r*d[0]-t*d[2])+o.center[1],u[2]+s*d[2]+(t*d[1]-i*d[0])+o.center[2]]},n=[];for(let u=0;u<o.vertices.length;u+=3)n.push(a([o.vertices[u],o.vertices[u+1],o.vertices[u+2]]));let l=0;n.forEach((u,d)=>{Te(oe(u,o.center))>Te(oe(n[l],o.center))&&(l=d)});let c=[l],f=n.map(u=>Te(oe(u,n[l])));for(;c.length<e;){let u=0;f.forEach((d,h)=>{d>f[u]&&(u=h)}),c.push(u),n.forEach((d,h)=>{f[h]=Math.min(f[h],Te(oe(d,n[u])))})}return c.flatMap(u=>n[u])}function Sa(o){let e=0;for(let g=0;g<o.length;g++)e=Math.max(e,Math.abs(o[g]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let g=0;g+2<o.length;g+=3){let x=[o[g],o[g+1],o[g+2]],v=x.map(B=>Math.round(B/t)).join(",");i.has(v)||(i.add(v),r.push(x))}if(r.length<4)return null;let s=Math.max(e,1e-9)*1e-10,a=0;for(let g=1;g<r.length;g++)r[g][0]<r[a][0]&&(a=g);let n=-1,l=0;for(let g=0;g<r.length;g++){let x=Te(oe(r[g],r[a]));x>l&&(l=x,n=g)}if(n<0||l<s)return null;let c=-1;l=0;let f=oe(r[n],r[a]);for(let g=0;g<r.length;g++){let x=Te(Zt(f,oe(r[g],r[a])));x>l&&(l=x,c=g)}if(c<0||l<s*Te(f))return null;let u=Zt(f,oe(r[c],r[a])),d=-1;l=0;for(let g=0;g<r.length;g++){let x=Math.abs(nt(u,oe(r[g],r[a])));x>l&&(l=x,d=g)}if(d<0||l<s*Te(u))return null;let h=[],m=(g,x,v)=>{let B=Zt(oe(r[x],r[g]),oe(r[v],r[g])),E=Te(B)||1,k=[B[0]/E,B[1]/E,B[2]/E];return{v:[g,x,v],n:k,d:nt(k,r[g])}},b=nt(u,oe(r[d],r[a]))>0?[a,c,n]:[a,n,c];h.push(m(b[0],b[1],b[2])),h.push(m(b[0],d,b[1])),h.push(m(b[1],d,b[2])),h.push(m(b[2],d,b[0]));let y=new Set([a,n,c,d]),A=r.map((g,x)=>x).filter(g=>!y.has(g)),w=[a,n,c,d].reduce((g,x)=>[g[0]+r[x][0]/4,g[1]+r[x][1]/4,g[2]+r[x][2]/4],[0,0,0]);A.sort((g,x)=>Te(oe(r[x],w))-Te(oe(r[g],w)));for(let g of A){let x=h.filter(P=>nt(P.n,r[g])-P.d>s);if(!x.length)continue;let v=new Map;for(let P of h)for(let _=0;_<3;_++)v.set(`${P.v[_]},${P.v[(_+1)%3]}`,P);let B=new Set(x),E=[...x];for(;E.length;){let P=E.pop();for(let _=0;_<3;_++){let S=v.get(`${P.v[(_+1)%3]},${P.v[_]}`);S&&!B.has(S)&&nt(S.n,r[g])-S.d>-s&&(B.add(S),E.push(S))}}let k=[...B],z=new Set;for(let P of k)for(let _=0;_<3;_++)z.add(`${P.v[_]},${P.v[(_+1)%3]}`);let M=[];for(let P of k)for(let _=0;_<3;_++){let S=P.v[_],C=P.v[(_+1)%3];z.has(`${C},${S}`)||M.push([S,C])}for(let P of k)h.splice(h.indexOf(P),1);for(let[P,_]of M)h.push(m(P,_,g))}return ul(r.flat(),h.flatMap(g=>g.v))}function ul(o,e){let t=[];for(let S=0;S+2<o.length;S+=3)t.push([o[S],o[S+1],o[S+2]]);let i=0;for(let S of t)i=Math.max(i,Math.abs(S[0]),Math.abs(S[1]),Math.abs(S[2]));let r=Math.max(i,1e-9)*1e-5,s=[];for(let S=0;S+2<e.length;S+=3){let C=[e[S],e[S+1],e[S+2]],R=Zt(oe(t[C[1]],t[C[0]]),oe(t[C[2]],t[C[0]])),I=Te(R);if(!(I>0))continue;let O=[R[0]/I,R[1]/I,R[2]/I];s.push({v:C,n:O,d:nt(O,t[C[0]])})}let a=new Map;s.forEach((S,C)=>{for(let R=0;R<3;R++)a.set(`${S.v[R]},${S.v[(R+1)%3]}`,C)});let n=new Array(s.length).fill(-1),l=[];s.forEach((S,C)=>{if(n[C]>=0)return;let R=[C];n[C]=l.length;for(let I=0;I<R.length;I++){let O=s[R[I]];for(let q=0;q<3;q++){let G=a.get(`${O.v[(q+1)%3]},${O.v[q]}`);if(G===void 0||n[G]>=0)continue;let j=s[G];nt(S.n,j.n)>1-1e-5&&j.v.every(F=>Math.abs(nt(S.n,t[F])-S.d)<=r)&&(n[G]=l.length,R.push(G))}}l.push(R)});let c=[];for(let S of l){let C=new Set;for(let j of S)for(let F=0;F<3;F++)C.add(`${s[j].v[F]},${s[j].v[(F+1)%3]}`);let R=new Map;for(let j of S)for(let F=0;F<3;F++){let W=s[j].v[F],ee=s[j].v[(F+1)%3];if(!C.has(`${ee},${W}`)){if(R.has(W))return null;R.set(W,ee)}}let I=[0,0,0];for(let j of S){let F=s[j],W=Zt(oe(t[F.v[1]],t[F.v[0]]),oe(t[F.v[2]],t[F.v[0]]));I[0]+=W[0],I[1]+=W[1],I[2]+=W[2]}let O=Te(I)||1,q=R.keys().next().value,G=[q];for(let j=R.get(q);j!==q&&G.length<=R.size;j=R.get(j))G.push(j);if(G.length!==R.size)return null;c.push({n:[I[0]/O,I[1]/O,I[2]/O],verts:G})}let f=new Map,u=[];for(let S of c)for(let C of S.verts)f.has(C)||(f.set(C,u.length),u.push(t[C]));for(let S of c)S.verts=S.verts.map(C=>f.get(C));let d=new Set,h=0;for(let S of c)S.verts.forEach((C,R)=>{d.add(`${C},${S.verts[(R+1)%S.verts.length]}`),h++});if(d.size!==h||![...d].every(S=>{let[C,R]=S.split(",");return d.has(`${R},${C}`)})||u.length-d.size/2+c.length!==2)return null;let m=0,p=[0,0,0],b=[0,0,0,0,0,0];for(let S of c){let C=u[S.verts[0]];for(let R=1;R+1<S.verts.length;R++){let I=u[S.verts[R]],O=u[S.verts[R+1]],q=nt(C,Zt(I,O));m+=q/6;for(let j=0;j<3;j++)p[j]+=q/24*(C[j]+I[j]+O[j]);let G=(j,F)=>q/120*(C[j]*C[F]+I[j]*I[F]+O[j]*O[F]+(C[j]+I[j]+O[j])*(C[F]+I[F]+O[F]));b[0]+=G(0,0),b[1]+=G(1,1),b[2]+=G(2,2),b[3]+=G(0,1),b[4]+=G(0,2),b[5]+=G(1,2)}}if(!(m>0))return null;for(let S=0;S<3;S++)p[S]/=m;b[0]-=m*p[0]*p[0],b[1]-=m*p[1]*p[1],b[2]-=m*p[2]*p[2],b[3]-=m*p[0]*p[1],b[4]-=m*p[0]*p[2],b[5]-=m*p[1]*p[2];let y=b[0]+b[1]+b[2],A=[[y-b[0],-b[3],-b[4]],[-b[3],y-b[1],-b[5]],[-b[4],-b[5],y-b[2]]],{values:w,vectors:g}=fl(A),x=g,v=S=>{let C=oe(S,p);return[x[0][0]*C[0]+x[1][0]*C[1]+x[2][0]*C[2],x[0][1]*C[0]+x[1][1]*C[1]+x[2][1]*C[2],x[0][2]*C[0]+x[1][2]*C[1]+x[2][2]*C[2]]},B=u.map(v),E=new Float32Array(B.flat()),k=c.map(S=>{let C=S.n,R=[x[0][0]*C[0]+x[1][0]*C[1]+x[2][0]*C[2],x[0][1]*C[0]+x[1][1]*C[1]+x[2][1]*C[2],x[0][2]*C[0]+x[1][2]*C[1]+x[2][2]*C[2]],I=-1/0;for(let O of S.verts)I=Math.max(I,nt(R,B[O]));return{normal:R,d:I,verts:S.verts}}),z=new Map;k.forEach((S,C)=>S.verts.forEach((R,I)=>z.set(`${R},${S.verts[(I+1)%S.verts.length]}`,C)));let M=[];k.forEach((S,C)=>S.verts.forEach((R,I)=>{let O=S.verts[(I+1)%S.verts.length],q=z.get(`${O},${R}`);R<O&&q!==void 0&&M.push([R,O,C,q])}));let P=[0,0,0],_=0;for(let S of B){for(let C=0;C<3;C++)P[C]=Math.max(P[C],2*Math.abs(S[C]));_=Math.max(_,Te(S))}return{vertices:E,faces:k,edges:M,volume:m,moments:[w[0],w[1],w[2]],size:P,radius:_,center:p,rotation:dl(x)}}function fl(o){let e=o.map(n=>[...n]),t=[[1,0,0],[0,1,0],[0,0,1]];for(let n=0;n<50&&!(e[0][1]**2+e[0][2]**2+e[1][2]**2<1e-30*(e[0][0]**2+e[1][1]**2+e[2][2]**2+1e-300));n++)for(let c=0;c<3;c++)for(let f=c+1;f<3;f++){if(Math.abs(e[c][f])<1e-300)continue;let u=(e[f][f]-e[c][c])/(2*e[c][f]),d=Math.sign(u||1)/(Math.abs(u)+Math.sqrt(u*u+1)),h=1/Math.sqrt(d*d+1),m=d*h;for(let p=0;p<3;p++){let b=e[p][c],y=e[p][f];e[p][c]=h*b-m*y,e[p][f]=m*b+h*y}for(let p=0;p<3;p++){let b=e[c][p],y=e[f][p];e[c][p]=h*b-m*y,e[f][p]=m*b+h*y}for(let p=0;p<3;p++){let b=t[p][c],y=t[p][f];t[p][c]=h*b-m*y,t[p][f]=m*b+h*y}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],s=Zt(i,r),a=[[i[0],r[0],s[0]],[i[1],r[1],s[1]],[i[2],r[2],s[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:a}}function dl(o){let e=o[0][0]+o[1][1]+o[2][2],t,i,r,s;if(e>0){let n=.5/Math.sqrt(e+1);s=.25/n,t=(o[2][1]-o[1][2])*n,i=(o[0][2]-o[2][0])*n,r=(o[1][0]-o[0][1])*n}else if(o[0][0]>o[1][1]&&o[0][0]>o[2][2]){let n=2*Math.sqrt(1+o[0][0]-o[1][1]-o[2][2]);s=(o[2][1]-o[1][2])/n,t=.25*n,i=(o[0][1]+o[1][0])/n,r=(o[0][2]+o[2][0])/n}else if(o[1][1]>o[2][2]){let n=2*Math.sqrt(1+o[1][1]-o[0][0]-o[2][2]);s=(o[0][2]-o[2][0])/n,t=(o[0][1]+o[1][0])/n,i=.25*n,r=(o[1][2]+o[2][1])/n}else{let n=2*Math.sqrt(1+o[2][2]-o[0][0]-o[1][1]);s=(o[1][0]-o[0][1])/n,t=(o[0][2]+o[2][0])/n,i=(o[1][2]+o[2][1])/n,r=.25*n}let a=Math.hypot(t,i,r,s);return[t/a,i/a,r/a,s/a]}var ka=new WeakSet,tr=o=>ka.has(o),hl=new WeakSet;var Pa=o=>hl.has(o);function Rr(o,e,t,i,r,s=[0,0,0]){let a=new at(o,[2*e,2*e,2*e],t,i,r,s);a.mass=t>0?4/3*Math.PI*e*e*e*t:0;let n=.4*a.mass*e*e;return a.moment.set([n,n,n]),a.radius=e,ka.add(a),a}var La=new WeakMap,_a=o=>La.get(o);function Or(o,e,t,i,r,s=[0,0,0,1],a=[0,0,0]){if(e.vertices.length/3>32||e.faces.length>255||e.edges.length>255)throw new Error(`hull: ${e.vertices.length/3} vertices, ${e.faces.length} faces, ${e.edges.length} edges (at most ${32} vertices, 255 faces and edges)`);let n=new at(o,e.size,t,i,r,a);return n.positionAng.set([s[0],s[1],s[2],s[3]]),n.mass=t>0?e.volume*t:0,n.moment.set(e.moments.map(l=>l*Math.max(t,0))),n.radius=e.radius,La.set(n,e),n}var Qs=`
1
+ var L=(o=0,e=0,t=0)=>Float64Array.of(o,e,t),Pi=(o=0,e=0,t=0,i=1)=>Float64Array.of(o,e,t,i),Y=()=>new Float64Array(9);var ca=o=>o<0?-1:o>0?1:0,tt=(o,e)=>o<e?o:e,hl=(o,e)=>o>e?o:e,Oe=(o,e,t)=>hl(e,tt(t,o)),Z=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],it=o=>Z(o,o),Li=o=>Math.sqrt(it(o));function We(o,e,t,i){return o[0]=e,o[1]=t,o[2]=i,o}var Lr=(o,e,t)=>We(o,e[0]+t[0],e[1]+t[1],e[2]+t[2]),X=(o,e,t)=>We(o,e[0]-t[0],e[1]-t[1],e[2]-t[2]),Ce=(o,e,t)=>We(o,e[0]*t,e[1]*t,e[2]*t),ei=(o,e,t)=>We(o,e[0]/t,e[1]/t,e[2]/t),he=(o,e)=>We(o,-e[0],-e[1],-e[2]),Js=(o,e)=>We(o,Math.abs(e[0]),Math.abs(e[1]),Math.abs(e[2])),N=(o,e,t,i)=>We(o,e[0]+t[0]*i,e[1]+t[1]*i,e[2]+t[2]*i),pl=(o,e)=>ei(o,e,Li(e)),Mt=(o,e,t)=>We(o,e[1]*t[2]-e[2]*t[1],e[2]*t[0]-e[0]*t[2],e[0]*t[1]-e[1]*t[0]);function ki(o,e,t,i,r){return o[0]=e,o[1]=t,o[2]=i,o[3]=r,o}function ua(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=t[0],l=t[1],c=t[2],f=t[3];return ki(o,a*n+i*f+r*c-s*l,a*l-i*c+r*f+s*n,a*c+i*l-r*n+s*f,a*f-i*n-r*l-s*c)}var ml=(o,e)=>ki(o,-e[0],-e[1],-e[2],e[3]),fa=o=>it(o)+o[3]*o[3];function bl(o,e){let t=fa(e);return ki(o,-e[0]/t,-e[1]/t,-e[2]/t,e[3]/t)}function gl(o,e){let t=Math.sqrt(fa(e));return ki(o,e[0]/t,e[1]/t,e[2]/t,e[3]/t)}var Si=new Float64Array(4),Zt=new Float64Array(4);function It(o,e,t){return ua(Si,e,bl(Si,t)),We(o,Si[0]*2,Si[1]*2,Si[2]*2)}function _r(o,e,t){return ki(Zt,t[0],t[1],t[2],0),ua(Zt,Zt,e),gl(o,ki(o,e[0]+Zt[0]*.5,e[1]+Zt[1]*.5,e[2]+Zt[2]*.5,e[3]+Zt[3]*.5))}function ue(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=(r*t[2]-s*t[1])*2,l=(s*t[0]-i*t[2])*2,c=(i*t[1]-r*t[0])*2;return We(o,t[0]+n*a+(r*c-s*l),t[1]+l*a+(s*n-i*c),t[2]+c*a+(i*l-r*n))}var la=new Float64Array(3);function ht(o,e,t,i){return ue(la,t,i),Lr(o,la,e)}function _i(o,e,t){return ue(o,ml(Si,e),t)}function je(o,e,t,i){return o.fill(0),o[0]=e,o[4]=t,o[8]=i,o}function $s(o,e){return o[0]=0,o[1]=-e[2],o[2]=e[1],o[3]=e[2],o[4]=0,o[5]=-e[0],o[6]=-e[1],o[7]=e[0],o[8]=0,o}function ti(o,e){let t=e[1],i=e[2],r=e[5];return o[0]=e[0],o[1]=e[3],o[2]=e[6],o[3]=t,o[4]=e[4],o[5]=e[7],o[6]=i,o[7]=r,o[8]=e[8],o}function Te(o,e,t){let i=e[0],r=e[1],s=e[2],a=e[3],n=e[4],l=e[5],c=e[6],f=e[7],u=e[8],d=t[0],p=t[1],m=t[2],h=t[3],b=t[4],y=t[5],v=t[6],B=t[7],g=t[8];return o[0]=i*d+r*h+s*v,o[1]=i*p+r*b+s*B,o[2]=i*m+r*y+s*g,o[3]=a*d+n*h+l*v,o[4]=a*p+n*b+l*B,o[5]=a*m+n*y+l*g,o[6]=c*d+f*h+u*v,o[7]=c*p+f*b+u*B,o[8]=c*m+f*y+u*g,o}function ie(o,e,t){let i=t[0],r=t[1],s=t[2];return We(o,e[0]*i+e[1]*r+e[2]*s,e[3]*i+e[4]*r+e[5]*s,e[6]*i+e[7]*r+e[8]*s)}function Se(o,e){for(let t=0;t<9;t++)o[t]+=e[t];return o}function ii(o,e,t){for(let i=0;i<9;i++)o[i]=e[i]*t;return o}function da(o,e){for(let t=0;t<9;t++)o[t]=-e[t];return o}function Er(o,e,t){for(let i=0;i<3;i++)for(let r=0;r<3;r++)o[i*3+r]=t[r]*e[i];return o}function ha(o,e){let t=Math.sqrt(e[0]*e[0]+e[3]*e[3]+e[6]*e[6]),i=Math.sqrt(e[1]*e[1]+e[4]*e[4]+e[7]*e[7]),r=Math.sqrt(e[2]*e[2]+e[5]*e[5]+e[8]*e[8]);return je(o,t,i,r)}function pa(o,e){let t=Math.abs(e[0])>Math.abs(e[2])?L(-e[1],e[0],0):L(0,-e[2],e[1]);pl(t,t);let i=Mt(L(),e,t);return o[0]=e[0],o[1]=e[1],o[2]=e[2],o.set(t,3),o.set(i,6),o}function ma(o,e,t,i,r,s,a){let n=o[0],l=o[3],c=o[4],f=o[6],u=o[7],d=o[8],p=t[0],m=t[1],h=t[2],b=e[0],y=t[3],v=t[4],B=t[5],g=e[3],A=e[4],x=t[6],w=t[7],E=t[8],k=e[6],z=e[7],M=e[8],P=l/n,_=f/n,C=p/n,S=y/n,R=x/n,I=n,O=c-P*P*I,q=(u-P*_*I)/O,G=(m-P*C*I)/O,j=(v-P*S*I)/O,F=(w-P*R*I)/O,H=d-(_*_*I+q*q*O),ee=(h-_*C*I-q*G*O)/H,le=(B-_*S*I-q*j*O)/H,xe=(E-_*R*I-q*F*O)/H,U=b-(C*C*I+G*G*O+ee*ee*H),ze=(g-C*S*I-G*j*O-ee*le*H)/U,Me=(k-C*R*I-G*F*O-ee*xe*H)/U,ce=A-(S*S*I+j*j*O+le*le*H+ze*ze*U),Ne=(z-S*R*I-j*F*O-le*xe*H-ze*Me*U)/ce,zt=M-(R*R*I+F*F*O+xe*xe*H+Me*Me*U+Ne*Ne*ce),Ie=i[0],qe=i[1]-P*Ie,Re=i[2]-_*Ie-q*qe,Ve=r[0]-C*Ie-G*qe-ee*Re,et=r[1]-S*Ie-j*qe-le*Re-ze*Ve,Ai=r[2]-R*Ie-F*qe-xe*Re-Me*Ve-Ne*et,wi=Ie/I,Bi=qe/O,Ci=Re/H,At=Ve/U,fl=et/ce,dl=Ai/zt;a[2]=dl,a[1]=fl-Ne*a[2],a[0]=At-ze*a[1]-Me*a[2],s[2]=Ci-ee*a[0]-le*a[1]-xe*a[2],s[1]=Bi-q*s[2]-G*a[0]-j*a[1]-F*a[2],s[0]=wi-P*s[1]-_*s[2]-C*a[0]-S*a[1]-R*a[2]}var rr=1,pt=1e10,ba=.01,ga=1e-5,De=class{solver;forces=[];positionLin;positionAng=Pi();initialLin=L();initialAng=Pi();inertialLin=L();inertialAng=Pi();velocityLin;velocityAng=L();prevVelocityLin;size;mass;moment;friction;radius;constructor(e,t,i,r,s,a=[0,0,0]){this.solver=e,this.size=L(t[0],t[1],t[2]),this.friction=r,this.positionLin=L(s[0],s[1],s[2]),this.velocityLin=L(a[0],a[1],a[2]),this.prevVelocityLin=L(a[0],a[1],a[2]);let[n,l,c]=this.size;this.mass=n*l*c*i,this.moment=L((l*l+c*c)/12*this.mass,(n*n+c*c)/12*this.mass,(n*n+l*l)/12*this.mass),this.radius=Li([n*.5,l*.5,c*.5]),e.bodies.push(this)}constrainedTo(e){for(let t of this.forces)if(t.bodyA===this&&t.bodyB===e||t.bodyA===e&&t.bodyB===this)return!0;return!1}},ya=()=>({lhsLin:Y(),lhsAng:Y(),lhsCross:Y(),rhsLin:L(),rhsAng:L()}),Rt=class{solver;bodyA;bodyB;constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}};var Ba=8,va=16,si=1e-6,Ys=1e-5,yl=1e-6,Qs=34028234663852886e22,zr=0,Ca=1,Mr=2;function xa(o){let e=o.positionAng;return{center:o.positionLin,half:Ce(L(),o.size,.5),axis:[ue(L(),e,[1,0,0]),ue(L(),e,[0,1,0]),ue(L(),e,[0,0,1])]}}var ri=(o,e)=>Math.abs(Z(o,e));function Ir(o,e){let t=Z(e,o.axis[0])>=0?1:-1,i=Z(e,o.axis[1])>=0?1:-1,r=Z(e,o.axis[2])>=0?1:-1,s=N(L(),o.center,o.axis[0],o.half[0]*t);return N(s,s,o.axis[1],o.half[1]*i),N(s,s,o.axis[2],o.half[2]*r)}function Sa(o,e){return e===0?{u:o.axis[1],v:o.axis[2],extentU:o.half[1],extentV:o.half[2]}:e===1?{u:o.axis[0],v:o.axis[2],extentU:o.half[0],extentV:o.half[2]}:{u:o.axis[0],v:o.axis[1],extentU:o.half[0],extentV:o.half[1]}}function vl(o,e,t){let i=Z(t,o.axis[e])>=0?1:-1,r=Ce(L(),o.axis[e],i),s=N(L(),o.center,r,o.half[e]);return{normal:r,center:s,...Sa(o,e)}}function xl(o,e){let t=0,i=-Qs;for(let r=0;r<3;r++){let s=ri(o.axis[r],e);s>i&&(i=s,t=r)}return t}function Al(o,e,t){let i=Z(o.axis[e],t)>0?-1:1,r=Ce(L(),o.axis[e],i),s=N(L(),o.center,r,o.half[e]),{u:a,v:n,extentU:l,extentV:c}=Sa(o,e),f=(u,d)=>{let p=N(L(),s,a,u*l);return N(p,p,n,d*c)};return[f(1,1),f(-1,1),f(-1,-1),f(1,-1)]}function wl(o,e,t){let i=[];if(o.length===0)return i;let r=o[o.length-1],s=Z(e,r)-t;for(let a of o){let n=Z(e,a)-t,l=s<=Ys,c=n<=Ys;if(l!==c){let f=0,u=s-n;Math.abs(u)>si&&(f=Oe(s/u,0,1)),i.length<va&&i.push(N(L(),r,X(L(),a,r),f))}c&&i.length<va&&i.push(a),r=a,s=n}return i}var Rr=class{contacts=[];midpoints=[];bodyA;bodyB;constructor(e,t){this.bodyA=e,this.bodyB=t}add(e,t,i){let r=Ce(L(),Lr(L(),e,t),.5);for(let n of this.midpoints)if(it(X(L(),r,n))<yl)return;if(this.contacts.length>=Ba)return;let{bodyA:s,bodyB:a}=this;this.contacts.push({feature:i,rA:_i(L(),s.positionAng,X(L(),e,s.positionLin)),rB:_i(L(),a.positionAng,X(L(),t,a.positionLin))}),this.midpoints.push(r)}};function Xs(o,e,t,i,r,s,a,n){let l=it(i);if(l<si)return!0;let c=1/Math.sqrt(l),f=Ce(L(),i,c);Z(f,t)<0&&he(f,f);let u=Math.abs(Z(t,f)),d=o.half[0]*ri(f,o.axis[0])+o.half[1]*ri(f,o.axis[1])+o.half[2]*ri(f,o.axis[2]),p=e.half[0]*ri(f,e.axis[0])+e.half[1]*ri(f,e.axis[1])+e.half[2]*ri(f,e.axis[2]),m=u-(d+p);return m>0?!1:((!n.valid||m>n.separation)&&(n.valid=!0,n.type=r,n.indexA=s,n.indexB=a,n.separation=m,n.normalAB=f),!0)}function Aa(o,e,t){let i=(e+1)%3,r=(e+2)%3,s=Z(t,o.axis[i])>=0?1:-1,a=Z(t,o.axis[r])>=0?1:-1,n=N(L(),o.center,o.axis[i],o.half[i]*s);N(n,n,o.axis[r],o.half[r]*a);let l=Ce(L(),o.axis[e],o.half[e]);return[X(L(),n,l),Lr(L(),n,l)]}function Bl(o,e,t,i){let r=X(L(),e,o),s=X(L(),i,t),a=X(L(),o,t),n=Z(r,r),l=Z(s,s),c=Z(s,a),f=0,u=0;if(n<=si&&l<=si)return[o,t];if(n<=si)u=Oe(c/l,0,1);else{let d=Z(r,a);if(l<=si)f=Oe(-d/n,0,1);else{let p=Z(r,s),m=n*l-p*p;Math.abs(m)>si&&(f=Oe((p*c-d*l)/m,0,1)),u=(p*f+c)/l,u<0?(u=0,f=Oe(-d/n,0,1)):u>1&&(u=1,f=Oe((p-d)/n,0,1))}}return[N(L(),o,r,f),N(L(),t,s,u)]}function wa(o,e,t,i,r,s,a){let n=r?t:i,l=r?i:t,c=r?a:he(L(),a),f=vl(n,s,c),u=xl(l,f.normal),d=Al(l,u,f.normal),p=[[f.u,f.extentU],[he(L(),f.u),f.extentU],[f.v,f.extentV],[he(L(),f.v),f.extentV]];for(let[b,y]of p)if(d=wl(d,b,Z(b,f.center)+y),!d.length)return[];let m=new Rr(o,e),h=(r?zr:Ca)<<24|(s&255)<<16|(u&255)<<8;for(let b=0;b<d.length&&m.contacts.length<Ba;b++){let y=d[b],v=Z(X(L(),y,f.center),f.normal);if(v>Ys)continue;let B=N(L(),y,f.normal,-v),g=r?B:y,A=r?y:B;m.add(g,A,h|b&255)}return m.contacts.length||m.add(Ir(t,a),Ir(i,he(L(),a)),h),m.contacts}function Cl(o,e,t,i,r,s,a){let[n,l]=Aa(t,r,a),[c,f]=Aa(i,s,he(L(),a)),[u,d]=Bl(n,l,c,f),p=new Rr(o,e),m=Mr<<24|(r&255)<<8|s&255;return p.add(u,d,m),p.contacts.length||p.add(Ir(t,a),Ir(i,he(L(),a)),m),p.contacts}function ka(o,e,t){let i=xa(o),r=xa(e),s=X(L(),r.center,i.center),a={type:zr,indexA:0,indexB:0,separation:-Qs,normalAB:L(),valid:!1},n={type:Mr,indexA:0,indexB:0,separation:-Qs,normalAB:L(),valid:!1};for(let f=0;f<3;f++)if(!Xs(i,r,s,i.axis[f],zr,f,-1,a))return[];for(let f=0;f<3;f++)if(!Xs(i,r,s,r.axis[f],Ca,-1,f,a))return[];let l=L();for(let f=0;f<3;f++)for(let u=0;u<3;u++)if(Mt(l,i.axis[f],r.axis[u]),!Xs(i,r,s,l,Mr,f,u,n))return[];if(!a.valid)return[];let c=a;return n.valid&&.95*n.separation>a.separation+.01&&(c=n),pa(t,he(L(),c.normalAB)),c.type===Mr?Cl(o,e,i,r,c.indexA,c.indexB,c.normalAB):c.type===zr?wa(o,e,i,r,!0,c.indexA,c.normalAB):wa(o,e,i,r,!1,c.indexB,c.normalAB)}var Pa=L(),La=L(),_a=L(),Ea=L(),za=L(),Ma=L(),Or=Y(),Ks=Y(),Zs=Y(),jr=Y(),eo=Y(),to=Y(),io=Y(),Tr=Y(),pe=L(),Ae=L(),oi=L(),Ot=L(),Ia=L(),Ra=L(),Ei=0,sr=0;function Oa(o,e,t){for(let i=0;i<3;i++)Ot[0]=t[i*3],Ot[1]=t[i*3+1],Ot[2]=t[i*3+2],Mt(Ot,e,Ot),o[i*3]=Ot[0],o[i*3+1]=Ot[1],o[i*3+2]=Ot[2];return o}var zi=class extends Rt{contacts=[];basis=Y();friction=0;constructor(e,t,i){super(e,t,i)}get numContacts(){return this.contacts.length}initialize(){let e=this.bodyA,t=this.bodyB,{solver:i}=this;this.friction=Math.sqrt(e.friction*t.friction);let r=this.contacts;this.contacts=ka(e,t,this.basis).map(s=>{let a=r.find(n=>n.feature===s.feature);return a?{feature:s.feature,rA:a.stick?a.rA:s.rA,rB:a.stick?a.rB:s.rB,C0:L(),penalty:a.penalty.slice(),lambda:a.lambda.slice(),stick:a.stick}:{feature:s.feature,rA:s.rA,rB:s.rB,C0:L(),penalty:L(),lambda:L(),stick:!1}});for(let s of this.contacts){ht(Ia,e.positionLin,e.positionAng,s.rA),ht(Ra,t.positionLin,t.positionAng,s.rB),ie(s.C0,this.basis,X(oi,Ia,Ra)),s.C0[0]+=ba;for(let a=0;a<3;a++)s.lambda[a]=s.lambda[a]*i.alpha*i.gamma,s.penalty[a]=Oe(s.penalty[a]*i.gamma,rr,pt)}return this.contacts.length>0}evaluate(e,t){let i=this.bodyA,r=this.bodyB,s=this.basis;ue(za,i.positionAng,e.rA),ue(Ma,r.positionAng,e.rB),da(Or,s),Oa(Ks,za,s),Oa(Zs,Ma,Or),je(jr,e.penalty[0],e.penalty[1],e.penalty[2]),Ce(pe,e.C0,1-t),N(pe,pe,ie(oi,s,Pa),1),N(pe,pe,ie(oi,Or,_a),1),N(pe,pe,ie(oi,Ks,La),1),N(pe,pe,ie(oi,Zs,Ea),1),ie(Ae,jr,pe),N(Ae,Ae,e.lambda,1),Ae[0]=tt(Ae[0],0),sr=Math.abs(Ae[0])*this.friction,Ei=Math.sqrt(Ae[1]*Ae[1]+Ae[2]*Ae[2]),Ei>sr&&Ei>0&&(Ae[1]*=sr/Ei,Ae[2]*=sr/Ei)}computeDisplacements(){let e=this.bodyA,t=this.bodyB;X(Pa,e.positionLin,e.initialLin),It(La,e.positionAng,e.initialAng),X(_a,t.positionLin,t.initialLin),It(Ea,t.positionAng,t.initialAng)}updatePrimal(e,t,i){this.computeDisplacements();let r=e===this.bodyA;for(let s of this.contacts){this.evaluate(s,t);let a=r?this.basis:Or,n=r?Ks:Zs;ti(eo,a),ti(to,n),Te(io,to,jr),Se(i.lhsLin,Te(Tr,Te(Tr,eo,jr),a)),Se(i.lhsAng,Te(Tr,io,n)),Se(i.lhsCross,Te(Tr,io,a)),N(i.rhsLin,i.rhsLin,ie(oi,eo,Ae),1),N(i.rhsAng,i.rhsAng,ie(oi,to,Ae),1)}}updateDual(e){let{solver:t}=this;this.computeDisplacements();for(let i of this.contacts)this.evaluate(i,e),i.lambda.set(Ae),Ae[0]<0&&(i.penalty[0]=tt(i.penalty[0]+t.betaLin*Math.abs(pe[0]),pt)),Ei<=sr&&(i.penalty[1]=tt(i.penalty[1]+t.betaLin*Math.abs(pe[1]),pt),i.penalty[2]=tt(i.penalty[2]+t.betaLin*Math.abs(pe[2]),pt),i.stick=Math.sqrt(pe[1]*pe[1]+pe[2]*pe[2])<ga)}};var ro=()=>({dt:1/60,gravity:-10,iterations:10,betaLin:1e4,betaAng:100,alpha:.99,gamma:.999}),jt=class{dt=1/60;gravity=-10;iterations=10;alpha=.99;betaLin=1e4;betaAng=100;gamma=.999;bodies=[];forces=[];constructor(){this.defaultParams()}defaultParams(){Object.assign(this,ro())}clear(){this.forces=[],this.bodies=[]}pick(e,t){let r=null,s=L(),a=L();for(let n=this.bodies.length-1;n>=0;n--){let l=this.bodies[n];if(l.mass<=0)continue;_i(s,l.positionAng,X(s,e,l.positionLin)),_i(a,l.positionAng,t);let c=0,f=1/0,u=!0;for(let p=0;p<3&&u;p++){let m=l.size[p]*.5;if(Math.abs(a[p])<1e-6){(s[p]<-m||s[p]>m)&&(u=!1);continue}let h=1/a[p],b=(-m-s[p])*h,y=(m-s[p])*h;b>y&&([b,y]=[y,b]),c=Math.max(c,b),f=Math.min(f,y),c>f&&(u=!1)}if(!u)continue;let d=c>=0?c:f;d<0||(!r||d<r.t)&&(r={body:l,local:N(L(),s,a,d),t:d})}return r}sys=ya();dxLin=L();dxAng=L();dp=L();accel=L();step(){let{dt:e,gravity:t,bodies:i,sys:r,dp:s}=this;for(let n=i.length-1;n>=0;n--){let l=i[n];for(let c=n-1;c>=0;c--){let f=i[c];X(s,l.positionLin,f.positionLin);let u=l.radius+f.radius;Z(s,s)<=u*u&&!l.constrainedTo(f)&&new zi(this,l,f)}}let a=[];for(let n of this.forces)n.initialize()?a.push(n):n.unlinkFromBodies();this.forces=a;for(let n=i.length-1;n>=0;n--){let l=i[n];N(l.inertialLin,l.positionLin,l.velocityLin,e),l.mass>0&&(l.inertialLin[2]+=t*(e*e)),_r(l.inertialAng,l.positionAng,ja(l.velocityAng,e)),ei(this.accel,X(this.accel,l.velocityLin,l.prevVelocityLin),e);let c=this.accel[2]*ca(t),f=Oe(c/Math.abs(t),0,1);Number.isFinite(f)||(f=0),l.initialLin.set(l.positionLin),l.initialAng.set(l.positionAng),l.mass>0&&(N(l.positionLin,l.positionLin,l.velocityLin,e),l.positionLin[2]+=t*(f*e*e),_r(l.positionAng,l.positionAng,ja(l.velocityAng,e)))}for(let n=0;n<this.iterations;n++){for(let l=i.length-1;l>=0;l--){let c=i[l];if(c.mass<=0)continue;let f=c.mass,u=c.moment;je(r.lhsLin,f/(e*e),f/(e*e),f/(e*e)),je(r.lhsAng,u[0]/(e*e),u[1]/(e*e),u[2]/(e*e)),r.lhsCross.fill(0),ie(r.rhsLin,r.lhsLin,X(s,c.positionLin,c.inertialLin)),ie(r.rhsAng,r.lhsAng,It(s,c.positionAng,c.inertialAng));let d=c.forces;for(let p=d.length-1;p>=0;p--)d[p].updatePrimal(c,this.alpha,r);ma(r.lhsLin,r.lhsAng,r.lhsCross,he(r.rhsLin,r.rhsLin),he(r.rhsAng,r.rhsAng),this.dxLin,this.dxAng),N(c.positionLin,c.positionLin,this.dxLin,1),_r(c.positionAng,c.positionAng,this.dxAng)}for(let l of this.forces)l.updateDual(this.alpha)}for(let n of i)n.prevVelocityLin.set(n.velocityLin),n.mass>0&&(ei(n.velocityLin,X(n.velocityLin,n.positionLin,n.initialLin),e),ei(n.velocityAng,It(n.velocityAng,n.positionAng,n.initialAng),e))}},Sl=L(),ja=(o,e)=>Ce(Sl,o,e);var oe=(o,e)=>[o[0]-e[0],o[1]-e[1],o[2]-e[2]],nt=(o,e)=>o[0]*e[0]+o[1]*e[1]+o[2]*e[2],ai=(o,e)=>[o[1]*e[2]-o[2]*e[1],o[2]*e[0]-o[0]*e[2],o[0]*e[1]-o[1]*e[0]],Ge=o=>Math.sqrt(nt(o,o));function or(o){let e=Ta(o);return!e||e.vertices.length/3<=32?e:Ta(kl(e,32))}function kl(o,e){let[t,i,r,s]=o.rotation,a=u=>{let d=[2*(i*u[2]-r*u[1]),2*(r*u[0]-t*u[2]),2*(t*u[1]-i*u[0])];return[u[0]+s*d[0]+(i*d[2]-r*d[1])+o.center[0],u[1]+s*d[1]+(r*d[0]-t*d[2])+o.center[1],u[2]+s*d[2]+(t*d[1]-i*d[0])+o.center[2]]},n=[];for(let u=0;u<o.vertices.length;u+=3)n.push(a([o.vertices[u],o.vertices[u+1],o.vertices[u+2]]));let l=0;n.forEach((u,d)=>{Ge(oe(u,o.center))>Ge(oe(n[l],o.center))&&(l=d)});let c=[l],f=n.map(u=>Ge(oe(u,n[l])));for(;c.length<e;){let u=0;f.forEach((d,p)=>{d>f[u]&&(u=p)}),c.push(u),n.forEach((d,p)=>{f[p]=Math.min(f[p],Ge(oe(d,n[u])))})}return c.flatMap(u=>n[u])}function Ta(o){let e=0;for(let g=0;g<o.length;g++)e=Math.max(e,Math.abs(o[g]));let t=Math.max(e,1e-9)*1e-6,i=new Set,r=[];for(let g=0;g+2<o.length;g+=3){let A=[o[g],o[g+1],o[g+2]],x=A.map(w=>Math.round(w/t)).join(",");i.has(x)||(i.add(x),r.push(A))}if(r.length<4)return null;let s=Math.max(e,1e-9)*1e-10,a=0;for(let g=1;g<r.length;g++)r[g][0]<r[a][0]&&(a=g);let n=-1,l=0;for(let g=0;g<r.length;g++){let A=Ge(oe(r[g],r[a]));A>l&&(l=A,n=g)}if(n<0||l<s)return null;let c=-1;l=0;let f=oe(r[n],r[a]);for(let g=0;g<r.length;g++){let A=Ge(ai(f,oe(r[g],r[a])));A>l&&(l=A,c=g)}if(c<0||l<s*Ge(f))return null;let u=ai(f,oe(r[c],r[a])),d=-1;l=0;for(let g=0;g<r.length;g++){let A=Math.abs(nt(u,oe(r[g],r[a])));A>l&&(l=A,d=g)}if(d<0||l<s*Ge(u))return null;let p=[],m=(g,A,x)=>{let w=ai(oe(r[A],r[g]),oe(r[x],r[g])),E=Ge(w)||1,k=[w[0]/E,w[1]/E,w[2]/E];return{v:[g,A,x],n:k,d:nt(k,r[g])}},b=nt(u,oe(r[d],r[a]))>0?[a,c,n]:[a,n,c];p.push(m(b[0],b[1],b[2])),p.push(m(b[0],d,b[1])),p.push(m(b[1],d,b[2])),p.push(m(b[2],d,b[0]));let y=new Set([a,n,c,d]),v=r.map((g,A)=>A).filter(g=>!y.has(g)),B=[a,n,c,d].reduce((g,A)=>[g[0]+r[A][0]/4,g[1]+r[A][1]/4,g[2]+r[A][2]/4],[0,0,0]);v.sort((g,A)=>Ge(oe(r[A],B))-Ge(oe(r[g],B)));for(let g of v){let A=p.filter(P=>nt(P.n,r[g])-P.d>s);if(!A.length)continue;let x=new Map;for(let P of p)for(let _=0;_<3;_++)x.set(`${P.v[_]},${P.v[(_+1)%3]}`,P);let w=new Set(A),E=[...A];for(;E.length;){let P=E.pop();for(let _=0;_<3;_++){let C=x.get(`${P.v[(_+1)%3]},${P.v[_]}`);C&&!w.has(C)&&nt(C.n,r[g])-C.d>-s&&(w.add(C),E.push(C))}}let k=[...w],z=new Set;for(let P of k)for(let _=0;_<3;_++)z.add(`${P.v[_]},${P.v[(_+1)%3]}`);let M=[];for(let P of k)for(let _=0;_<3;_++){let C=P.v[_],S=P.v[(_+1)%3];z.has(`${S},${C}`)||M.push([C,S])}for(let P of k)p.splice(p.indexOf(P),1);for(let[P,_]of M)p.push(m(P,_,g))}return Pl(r.flat(),p.flatMap(g=>g.v))}function Pl(o,e){let t=[];for(let C=0;C+2<o.length;C+=3)t.push([o[C],o[C+1],o[C+2]]);let i=0;for(let C of t)i=Math.max(i,Math.abs(C[0]),Math.abs(C[1]),Math.abs(C[2]));let r=Math.max(i,1e-9)*1e-5,s=[];for(let C=0;C+2<e.length;C+=3){let S=[e[C],e[C+1],e[C+2]],R=ai(oe(t[S[1]],t[S[0]]),oe(t[S[2]],t[S[0]])),I=Ge(R);if(!(I>0))continue;let O=[R[0]/I,R[1]/I,R[2]/I];s.push({v:S,n:O,d:nt(O,t[S[0]])})}let a=new Map;s.forEach((C,S)=>{for(let R=0;R<3;R++)a.set(`${C.v[R]},${C.v[(R+1)%3]}`,S)});let n=new Array(s.length).fill(-1),l=[];s.forEach((C,S)=>{if(n[S]>=0)return;let R=[S];n[S]=l.length;for(let I=0;I<R.length;I++){let O=s[R[I]];for(let q=0;q<3;q++){let G=a.get(`${O.v[(q+1)%3]},${O.v[q]}`);if(G===void 0||n[G]>=0)continue;let j=s[G];nt(C.n,j.n)>1-1e-5&&j.v.every(F=>Math.abs(nt(C.n,t[F])-C.d)<=r)&&(n[G]=l.length,R.push(G))}}l.push(R)});let c=[];for(let C of l){let S=new Set;for(let j of C)for(let F=0;F<3;F++)S.add(`${s[j].v[F]},${s[j].v[(F+1)%3]}`);let R=new Map;for(let j of C)for(let F=0;F<3;F++){let H=s[j].v[F],ee=s[j].v[(F+1)%3];if(!S.has(`${ee},${H}`)){if(R.has(H))return null;R.set(H,ee)}}let I=[0,0,0];for(let j of C){let F=s[j],H=ai(oe(t[F.v[1]],t[F.v[0]]),oe(t[F.v[2]],t[F.v[0]]));I[0]+=H[0],I[1]+=H[1],I[2]+=H[2]}let O=Ge(I)||1,q=R.keys().next().value,G=[q];for(let j=R.get(q);j!==q&&G.length<=R.size;j=R.get(j))G.push(j);if(G.length!==R.size)return null;c.push({n:[I[0]/O,I[1]/O,I[2]/O],verts:G})}let f=new Map,u=[];for(let C of c)for(let S of C.verts)f.has(S)||(f.set(S,u.length),u.push(t[S]));for(let C of c)C.verts=C.verts.map(S=>f.get(S));let d=new Set,p=0;for(let C of c)C.verts.forEach((S,R)=>{d.add(`${S},${C.verts[(R+1)%C.verts.length]}`),p++});if(d.size!==p||![...d].every(C=>{let[S,R]=C.split(",");return d.has(`${R},${S}`)})||u.length-d.size/2+c.length!==2)return null;let m=0,h=[0,0,0],b=[0,0,0,0,0,0];for(let C of c){let S=u[C.verts[0]];for(let R=1;R+1<C.verts.length;R++){let I=u[C.verts[R]],O=u[C.verts[R+1]],q=nt(S,ai(I,O));m+=q/6;for(let j=0;j<3;j++)h[j]+=q/24*(S[j]+I[j]+O[j]);let G=(j,F)=>q/120*(S[j]*S[F]+I[j]*I[F]+O[j]*O[F]+(S[j]+I[j]+O[j])*(S[F]+I[F]+O[F]));b[0]+=G(0,0),b[1]+=G(1,1),b[2]+=G(2,2),b[3]+=G(0,1),b[4]+=G(0,2),b[5]+=G(1,2)}}if(!(m>0))return null;for(let C=0;C<3;C++)h[C]/=m;b[0]-=m*h[0]*h[0],b[1]-=m*h[1]*h[1],b[2]-=m*h[2]*h[2],b[3]-=m*h[0]*h[1],b[4]-=m*h[0]*h[2],b[5]-=m*h[1]*h[2];let y=b[0]+b[1]+b[2],v=[[y-b[0],-b[3],-b[4]],[-b[3],y-b[1],-b[5]],[-b[4],-b[5],y-b[2]]],{values:B,vectors:g}=Ll(v),A=g,x=C=>{let S=oe(C,h);return[A[0][0]*S[0]+A[1][0]*S[1]+A[2][0]*S[2],A[0][1]*S[0]+A[1][1]*S[1]+A[2][1]*S[2],A[0][2]*S[0]+A[1][2]*S[1]+A[2][2]*S[2]]},w=u.map(x),E=new Float32Array(w.flat()),k=c.map(C=>{let S=C.n,R=[A[0][0]*S[0]+A[1][0]*S[1]+A[2][0]*S[2],A[0][1]*S[0]+A[1][1]*S[1]+A[2][1]*S[2],A[0][2]*S[0]+A[1][2]*S[1]+A[2][2]*S[2]],I=-1/0;for(let O of C.verts)I=Math.max(I,nt(R,w[O]));return{normal:R,d:I,verts:C.verts}}),z=new Map;k.forEach((C,S)=>C.verts.forEach((R,I)=>z.set(`${R},${C.verts[(I+1)%C.verts.length]}`,S)));let M=[];k.forEach((C,S)=>C.verts.forEach((R,I)=>{let O=C.verts[(I+1)%C.verts.length],q=z.get(`${O},${R}`);R<O&&q!==void 0&&M.push([R,O,S,q])}));let P=[0,0,0],_=0;for(let C of w){for(let S=0;S<3;S++)P[S]=Math.max(P[S],2*Math.abs(C[S]));_=Math.max(_,Ge(C))}return{vertices:E,faces:k,edges:M,volume:m,moments:[B[0],B[1],B[2]],size:P,radius:_,center:h,rotation:_l(A)}}function Ll(o){let e=o.map(n=>[...n]),t=[[1,0,0],[0,1,0],[0,0,1]];for(let n=0;n<50&&!(e[0][1]**2+e[0][2]**2+e[1][2]**2<1e-30*(e[0][0]**2+e[1][1]**2+e[2][2]**2+1e-300));n++)for(let c=0;c<3;c++)for(let f=c+1;f<3;f++){if(Math.abs(e[c][f])<1e-300)continue;let u=(e[f][f]-e[c][c])/(2*e[c][f]),d=Math.sign(u||1)/(Math.abs(u)+Math.sqrt(u*u+1)),p=1/Math.sqrt(d*d+1),m=d*p;for(let h=0;h<3;h++){let b=e[h][c],y=e[h][f];e[h][c]=p*b-m*y,e[h][f]=m*b+p*y}for(let h=0;h<3;h++){let b=e[c][h],y=e[f][h];e[c][h]=p*b-m*y,e[f][h]=m*b+p*y}for(let h=0;h<3;h++){let b=t[h][c],y=t[h][f];t[h][c]=p*b-m*y,t[h][f]=m*b+p*y}}let i=[t[0][0],t[1][0],t[2][0]],r=[t[0][1],t[1][1],t[2][1]],s=ai(i,r),a=[[i[0],r[0],s[0]],[i[1],r[1],s[1]],[i[2],r[2],s[2]]];return{values:[e[0][0],e[1][1],e[2][2]],vectors:a}}function _l(o){let e=o[0][0]+o[1][1]+o[2][2],t,i,r,s;if(e>0){let n=.5/Math.sqrt(e+1);s=.25/n,t=(o[2][1]-o[1][2])*n,i=(o[0][2]-o[2][0])*n,r=(o[1][0]-o[0][1])*n}else if(o[0][0]>o[1][1]&&o[0][0]>o[2][2]){let n=2*Math.sqrt(1+o[0][0]-o[1][1]-o[2][2]);s=(o[2][1]-o[1][2])/n,t=.25*n,i=(o[0][1]+o[1][0])/n,r=(o[0][2]+o[2][0])/n}else if(o[1][1]>o[2][2]){let n=2*Math.sqrt(1+o[1][1]-o[0][0]-o[2][2]);s=(o[0][2]-o[2][0])/n,t=(o[0][1]+o[1][0])/n,i=.25*n,r=(o[1][2]+o[2][1])/n}else{let n=2*Math.sqrt(1+o[2][2]-o[0][0]-o[1][1]);s=(o[1][0]-o[0][1])/n,t=(o[0][2]+o[2][0])/n,i=(o[1][2]+o[2][1])/n,r=.25*n}let a=Math.hypot(t,i,r,s);return[t/a,i/a,r/a,s/a]}var Ga=new WeakSet,ar=o=>Ga.has(o),El=new WeakSet;var Fa=o=>El.has(o);function Mi(o,e,t,i,r,s=[0,0,0]){let a=new De(o,[2*e,2*e,2*e],t,i,r,s);a.mass=t>0?4/3*Math.PI*e*e*e*t:0;let n=.4*a.mass*e*e;return a.moment.set([n,n,n]),a.radius=e,Ga.add(a),a}var Ua=new WeakMap,Dr=o=>Ua.get(o);function Gr(o,e,t,i,r,s=[0,0,0,1],a=[0,0,0]){if(e.vertices.length/3>32||e.faces.length>255||e.edges.length>255)throw new Error(`hull: ${e.vertices.length/3} vertices, ${e.faces.length} faces, ${e.edges.length} edges (at most ${32} vertices, 255 faces and edges)`);let n=new De(o,e.size,t,i,r,a);return n.positionAng.set([s[0],s[1],s[2],s[3]]),n.mass=t>0?e.volume*t:0,n.moment.set(e.moments.map(l=>l*Math.max(t,0))),n.radius=e.radius,Ua.set(n,e),n}var so=`
2
2
  const T_NONE = 0;
3
3
 
4
4
  const MAX_COLORS = 64u;
@@ -40,7 +40,7 @@ fn hash32(v: u32) -> u32 {
40
40
  }
41
41
  `,mt=`
42
42
  // Shared by every 2D module (layout.ts)
43
- ${Qs}
43
+ ${so}
44
44
  const T_JOINT = 1;
45
45
  const T_SPRING = 2;
46
46
  const T_MOTOR = 3;
@@ -148,7 +148,7 @@ fn rot(angle: f32, v: vec2f) -> vec2f {
148
148
  return vec2f(cs.x * v.x - cs.y * v.y, cs.y * v.x + cs.x * v.y);
149
149
  }
150
150
 
151
- `,Ks=(o,e={counter:"C_CONTACTS",prevCounter:"C_PREV_CONTACTS",capacity:"params.contactCapacity"})=>`
151
+ `,oo=(o,e={counter:"C_CONTACTS",prevCounter:"C_PREV_CONTACTS",capacity:"params.contactCapacity"})=>`
152
152
  ${o}
153
153
  ${e.colorThreads??"fn colorThreads(bodies: u32) -> u32 { return bodies; }"}
154
154
 
@@ -185,7 +185,7 @@ fn argsColors(@builtin(local_invocation_id) lid: vec3u) {
185
185
  let c = lid.x;
186
186
  setArgs(IA_COLOR + 3u * c, colorThreads(color[params.colorStartOffset + c + 1u] - color[params.colorStartOffset + c]));
187
187
  }
188
- `,jr=Ks(mt),ue=["collision","adjacency","coloring","solve"];var pl=`
188
+ `,Fr=oo(mt),fe=["collision","adjacency","coloring","solve"];var zl=`
189
189
  struct ScanParams {
190
190
  offset: u32, // first element of the range in data
191
191
  n: u32, // elements in the range
@@ -233,7 +233,7 @@ fn addBlocks(@builtin(workgroup_id) wid: vec3u, @builtin(local_invocation_id) li
233
233
  if (base < sp.n) { data[sp.offset + base] += add; }
234
234
  if (base + 1u < sp.n) { data[sp.offset + base + 1u] += add; }
235
235
  }
236
- `,De=class{levels=[];buffers=[];scanPipeline;addPipeline;constructor(e,t,i,r){let s=e.createShaderModule({label:"prefix scan",code:pl}),a=e.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}}]}),n=e.createPipelineLayout({bindGroupLayouts:[a]});this.scanPipeline=e.createComputePipeline({label:"scanBlocks",layout:n,compute:{module:s,entryPoint:"scanBlocks"}}),this.addPipeline=e.createComputePipeline({label:"addBlocks",layout:n,compute:{module:s,entryPoint:"addBlocks"}});let l=t,c=i,f=r;for(;;){let u=Math.max(1,Math.ceil(f/512)),d=e.createBuffer({label:`scan sums ${this.levels.length}`,size:Math.max(u,4)*4,usage:GPUBufferUsage.STORAGE}),h=e.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});new Uint32Array(h.getMappedRange()).set([c,f,0,0]),h.unmap(),this.buffers.push(d,h);let m=e.createBindGroup({layout:a,entries:[{binding:0,resource:{buffer:h}},{binding:1,resource:{buffer:l}},{binding:2,resource:{buffer:d}}]});if(this.levels.push({n:f,blocks:u,group:m}),u===1)break;l=d,c=0,f=u}}encode(e){e.setPipeline(this.scanPipeline);for(let t of this.levels)e.setBindGroup(0,t.group),e.dispatchWorkgroups(t.blocks);e.setPipeline(this.addPipeline);for(let t=this.levels.length-2;t>=0;t--)e.setBindGroup(0,this.levels[t].group),e.dispatchWorkgroups(this.levels[t].blocks)}destroy(){for(let e of this.buffers)e.destroy()}};var ml=`
236
+ `,Fe=class{levels=[];buffers=[];scanPipeline;addPipeline;constructor(e,t,i,r){let s=e.createShaderModule({label:"prefix scan",code:zl}),a=e.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform"}},{binding:1,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}},{binding:2,visibility:GPUShaderStage.COMPUTE,buffer:{type:"storage"}}]}),n=e.createPipelineLayout({bindGroupLayouts:[a]});this.scanPipeline=e.createComputePipeline({label:"scanBlocks",layout:n,compute:{module:s,entryPoint:"scanBlocks"}}),this.addPipeline=e.createComputePipeline({label:"addBlocks",layout:n,compute:{module:s,entryPoint:"addBlocks"}});let l=t,c=i,f=r;for(;;){let u=Math.max(1,Math.ceil(f/512)),d=e.createBuffer({label:`scan sums ${this.levels.length}`,size:Math.max(u,4)*4,usage:GPUBufferUsage.STORAGE}),p=e.createBuffer({size:16,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});new Uint32Array(p.getMappedRange()).set([c,f,0,0]),p.unmap(),this.buffers.push(d,p);let m=e.createBindGroup({layout:a,entries:[{binding:0,resource:{buffer:p}},{binding:1,resource:{buffer:l}},{binding:2,resource:{buffer:d}}]});if(this.levels.push({n:f,blocks:u,group:m}),u===1)break;l=d,c=0,f=u}}encode(e){e.setPipeline(this.scanPipeline);for(let t of this.levels)e.setBindGroup(0,t.group),e.dispatchWorkgroups(t.blocks);e.setPipeline(this.addPipeline);for(let t=this.levels.length-2;t>=0;t--)e.setBindGroup(0,this.levels[t].group),e.dispatchWorkgroups(this.levels[t].blocks)}destroy(){for(let e of this.buffers)e.destroy()}};var Ml=`
237
237
  alias TopoItem = Contact;
238
238
 
239
239
  fn topoCount() -> u32 {
@@ -254,7 +254,7 @@ fn jointActive(j: u32) -> bool {
254
254
  let s = joints[j].stiff;
255
255
  return info[j].x != T_NONE && (s.x != 0.0 || s.y != 0.0 || s.z != 0.0);
256
256
  }
257
- `,ti=(o,e)=>`
257
+ `,li=(o,e)=>`
258
258
  ${o}
259
259
  ${e}
260
260
 
@@ -532,11 +532,11 @@ fn colorScatter(@builtin(global_invocation_id) gid: vec3u, @builtin(workgroup_id
532
532
  let slot = atomicLoad(&color[params.colorHistOffset + col * params.colorGroups + wid.x]) + state(params.stateBOffset, b);
533
533
  atomicStore(&color[params.colorBodiesOffset + slot], b);
534
534
  }
535
- `,ir=ti(mt,ml);var bl=Bi(),lt=Y(),Tr=Y(),ii=Y(),Zs=Y(),Li=Y(),eo=Y(),Ve=Y(),Dr=Y(),rr=Y(),re=L(),pe=L(),se=L(),It=L(),_i=L();function to(o,e,t){return Oe(o,-t[e],-t[e],-t[e]),o[e]+=t[0],o[3+e]+=t[1],o[6+e]+=t[2],o}var Gr=(o,e,t)=>qe(o,et(e[0],t),et(e[1],t),et(e[2],t)),Ae=class extends zt{rA;rB;C0Lin=L();C0Ang=L();penaltyLin=L();penaltyAng=L();lambdaLin=L();lambdaAng=L();stiffnessLin;stiffnessAng;fracture;torqueArm;broken=!1;constructor(e,t,i,r,s,a=1/0,n=0,l=1/0){super(e,t,i),this.rA=L(r[0],r[1],r[2]),this.rB=L(s[0],s[1],s[2]),this.stiffnessLin=a,this.stiffnessAng=n,this.fracture=l;let c=t?t.size:[0,0,0];this.torqueArm=tt([c[0]+i.size[0],c[1]+i.size[1],c[2]+i.size[2]])}evaluateLin(e){let{bodyA:t,bodyB:i}=this;return t?ht(It,t.positionLin,t.positionAng,this.rA):It.set(this.rA),X(e,It,ht(_i,i.positionLin,i.positionAng,this.rB))}evaluateAng(e){return Et(e,this.bodyA?this.bodyA.positionAng:bl,this.bodyB.positionAng),we(e,e,this.torqueArm)}initialize(){let{solver:e}=this;this.evaluateLin(this.C0Lin),this.evaluateAng(this.C0Ang);for(let t=0;t<3;t++)this.lambdaLin[t]=this.lambdaLin[t]*e.alpha*e.gamma,this.lambdaAng[t]=this.lambdaAng[t]*e.alpha*e.gamma,this.penaltyLin[t]=Re(this.penaltyLin[t]*e.gamma,Ki,pt),this.penaltyAng[t]=Re(this.penaltyAng[t]*e.gamma,Ki,pt);return Gr(this.penaltyLin,this.penaltyLin,this.stiffnessLin),Gr(this.penaltyAng,this.penaltyAng,this.stiffnessAng),!this.broken}updatePrimal(e,t,i){let r=e===this.bodyA;if(tt(this.penaltyLin)>0){Oe(lt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(re),this.stiffnessLin===1/0&&U(re,re,this.C0Lin,-t),ie(pe,lt,re),U(pe,pe,this.lambdaLin,1);let s=r?1:-1;Oe(Tr,s,s,s),r?Ns(ii,de(se,ce(se,this.bodyA.positionAng,this.rA))):Ns(ii,ce(se,this.bodyB.positionAng,this.rB)),$t(Zs,Tr),$t(Li,ii),je(eo,Li,lt),Se(i.lhsLin,je(Ve,je(Ve,Zs,lt),Tr)),Se(i.lhsAng,je(Ve,eo,ii)),Se(i.lhsCross,je(Ve,eo,Tr)),r?ce(se,this.bodyA.positionAng,this.rA):de(se,ce(se,this.bodyB.positionAng,this.rB)),Xt(Dr,to(rr,0,se),pe[0]),Se(Dr,Xt(rr,to(rr,1,se),pe[1])),Se(Dr,Xt(rr,to(rr,2,se),pe[2])),Se(i.lhsAng,ea(Ve,Dr)),U(i.rhsLin,i.rhsLin,ie(se,Zs,pe),1),U(i.rhsAng,i.rhsAng,ie(se,Li,pe),1)}if(tt(this.penaltyAng)>0){Oe(lt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(re),this.stiffnessAng===1/0&&U(re,re,this.C0Ang,-t),ie(pe,lt,re),U(pe,pe,this.lambdaAng,1);let s=(r?1:-1)*this.torqueArm;Oe(ii,s,s,s),$t(Li,ii),Se(i.lhsAng,je(Ve,je(Ve,Li,lt),ii)),U(i.rhsAng,i.rhsAng,ie(se,Li,pe),1)}}updateDual(e){let{solver:t}=this;tt(this.penaltyLin)>0&&(Oe(lt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(re),this.stiffnessLin===1/0&&(U(re,re,this.C0Lin,-e),ie(pe,lt,re),U(this.lambdaLin,pe,this.lambdaLin,1)),U(this.penaltyLin,this.penaltyLin,Fs(se,re),t.betaLin),Gr(this.penaltyLin,this.penaltyLin,et(this.stiffnessLin,pt))),tt(this.penaltyAng)>0&&(Oe(lt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(re),this.stiffnessAng===1/0&&(U(re,re,this.C0Ang,-e),ie(pe,lt,re),U(this.lambdaAng,pe,this.lambdaAng,1)),U(this.penaltyAng,this.penaltyAng,Fs(se,re),t.betaAng),Gr(this.penaltyAng,this.penaltyAng,et(this.stiffnessAng,pt))),tt(this.lambdaAng)>this.fracture*this.fracture&&(this.penaltyLin.fill(0),this.penaltyAng.fill(0),this.lambdaLin.fill(0),this.lambdaAng.fill(0),this.broken=!0)}},ct=class extends zt{rA;rB;rest;stiffness;constructor(e,t,i,r,s,a,n=-1){super(e,t,i),this.rA=L(r[0],r[1],r[2]),this.rB=L(s[0],s[1],s[2]),this.stiffness=a,this.rest=n,this.rest<0&&(ht(It,t.positionLin,t.positionAng,this.rA),ht(_i,i.positionLin,i.positionAng,this.rB),this.rest=wi(X(re,It,_i)))}initialize(){return!0}updatePrimal(e,t,i){let r=this.bodyA,s=this.bodyB;ht(It,r.positionLin,r.positionAng,this.rA),ht(_i,s.positionLin,s.positionAng,this.rB);let a=X(re,It,_i),n=wi(a);if(n<=1e-6)return;let l=Jt(pe,a,n),c=n-this.rest,f=this.stiffness*c,u=It,d=_i;e===r?(ce(se,r.positionAng,this.rA),u.set(l),_t(d,se,l)):(ce(se,s.positionAng,this.rB),de(u,l),de(d,_t(d,se,l))),Se(i.lhsLin,Xt(Ve,kr(Ve,u,u),this.stiffness)),Se(i.lhsAng,Xt(Ve,kr(Ve,d,d),this.stiffness)),Se(i.lhsCross,Xt(Ve,kr(Ve,d,u),this.stiffness)),U(i.rhsLin,i.rhsLin,we(se,u,f),1),U(i.rhsAng,i.rhsAng,we(se,d,f),1)}updateDual(){}},Fr=class extends zt{initialize(){return!0}updatePrimal(){}updateDual(){}};var Ce=40,Ei=0,sr=4,Ea=8,za=12,io=16,ro=20,Ma=24,Ia=28,so=32,oo=36,te=32,Rt=0,or=4,zi=8,Mi=12,Ra=16,Oa=20,Ot=24,Ii=28,bt=8;var Nr=4,Bt=16,Ri=0,Ur=4,qr=8,Vr=12,ar=2147483648,Oi=6,gl=7,ja=0,ao=1,no=2,Hr=3,nr=1,ji=2,lo=1,co=2,uo=4,fo=8,ho=16,yl=.1,Ta=.002,Da=.002,vl=.05,po=44,me=`
535
+ `,nr=li(mt,Ml);var Il=Pi(),lt=Y(),Ur=Y(),ci=Y(),ao=Y(),Ii=Y(),no=Y(),He=Y(),Nr=Y(),lr=Y(),re=L(),me=L(),se=L(),Tt=L(),Ri=L();function lo(o,e,t){return je(o,-t[e],-t[e],-t[e]),o[e]+=t[0],o[3+e]+=t[1],o[6+e]+=t[2],o}var qr=(o,e,t)=>We(o,tt(e[0],t),tt(e[1],t),tt(e[2],t)),we=class extends Rt{rA;rB;C0Lin=L();C0Ang=L();penaltyLin=L();penaltyAng=L();lambdaLin=L();lambdaAng=L();stiffnessLin;stiffnessAng;fracture;torqueArm;broken=!1;constructor(e,t,i,r,s,a=1/0,n=0,l=1/0){super(e,t,i),this.rA=L(r[0],r[1],r[2]),this.rB=L(s[0],s[1],s[2]),this.stiffnessLin=a,this.stiffnessAng=n,this.fracture=l;let c=t?t.size:[0,0,0];this.torqueArm=it([c[0]+i.size[0],c[1]+i.size[1],c[2]+i.size[2]])}evaluateLin(e){let{bodyA:t,bodyB:i}=this;return t?ht(Tt,t.positionLin,t.positionAng,this.rA):Tt.set(this.rA),X(e,Tt,ht(Ri,i.positionLin,i.positionAng,this.rB))}evaluateAng(e){return It(e,this.bodyA?this.bodyA.positionAng:Il,this.bodyB.positionAng),Ce(e,e,this.torqueArm)}initialize(){let{solver:e}=this;this.evaluateLin(this.C0Lin),this.evaluateAng(this.C0Ang);for(let t=0;t<3;t++)this.lambdaLin[t]=this.lambdaLin[t]*e.alpha*e.gamma,this.lambdaAng[t]=this.lambdaAng[t]*e.alpha*e.gamma,this.penaltyLin[t]=Oe(this.penaltyLin[t]*e.gamma,rr,pt),this.penaltyAng[t]=Oe(this.penaltyAng[t]*e.gamma,rr,pt);return qr(this.penaltyLin,this.penaltyLin,this.stiffnessLin),qr(this.penaltyAng,this.penaltyAng,this.stiffnessAng),!this.broken}updatePrimal(e,t,i){let r=e===this.bodyA;if(it(this.penaltyLin)>0){je(lt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(re),this.stiffnessLin===1/0&&N(re,re,this.C0Lin,-t),ie(me,lt,re),N(me,me,this.lambdaLin,1);let s=r?1:-1;je(Ur,s,s,s),r?$s(ci,he(se,ue(se,this.bodyA.positionAng,this.rA))):$s(ci,ue(se,this.bodyB.positionAng,this.rB)),ti(ao,Ur),ti(Ii,ci),Te(no,Ii,lt),Se(i.lhsLin,Te(He,Te(He,ao,lt),Ur)),Se(i.lhsAng,Te(He,no,ci)),Se(i.lhsCross,Te(He,no,Ur)),r?ue(se,this.bodyA.positionAng,this.rA):he(se,ue(se,this.bodyB.positionAng,this.rB)),ii(Nr,lo(lr,0,se),me[0]),Se(Nr,ii(lr,lo(lr,1,se),me[1])),Se(Nr,ii(lr,lo(lr,2,se),me[2])),Se(i.lhsAng,ha(He,Nr)),N(i.rhsLin,i.rhsLin,ie(se,ao,me),1),N(i.rhsAng,i.rhsAng,ie(se,Ii,me),1)}if(it(this.penaltyAng)>0){je(lt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(re),this.stiffnessAng===1/0&&N(re,re,this.C0Ang,-t),ie(me,lt,re),N(me,me,this.lambdaAng,1);let s=(r?1:-1)*this.torqueArm;je(ci,s,s,s),ti(Ii,ci),Se(i.lhsAng,Te(He,Te(He,Ii,lt),ci)),N(i.rhsAng,i.rhsAng,ie(se,Ii,me),1)}}updateDual(e){let{solver:t}=this;it(this.penaltyLin)>0&&(je(lt,this.penaltyLin[0],this.penaltyLin[1],this.penaltyLin[2]),this.evaluateLin(re),this.stiffnessLin===1/0&&(N(re,re,this.C0Lin,-e),ie(me,lt,re),N(this.lambdaLin,me,this.lambdaLin,1)),N(this.penaltyLin,this.penaltyLin,Js(se,re),t.betaLin),qr(this.penaltyLin,this.penaltyLin,tt(this.stiffnessLin,pt))),it(this.penaltyAng)>0&&(je(lt,this.penaltyAng[0],this.penaltyAng[1],this.penaltyAng[2]),this.evaluateAng(re),this.stiffnessAng===1/0&&(N(re,re,this.C0Ang,-e),ie(me,lt,re),N(this.lambdaAng,me,this.lambdaAng,1)),N(this.penaltyAng,this.penaltyAng,Js(se,re),t.betaAng),qr(this.penaltyAng,this.penaltyAng,tt(this.stiffnessAng,pt))),it(this.lambdaAng)>this.fracture*this.fracture&&(this.penaltyLin.fill(0),this.penaltyAng.fill(0),this.lambdaLin.fill(0),this.lambdaAng.fill(0),this.broken=!0)}},ct=class extends Rt{rA;rB;rest;stiffness;constructor(e,t,i,r,s,a,n=-1){super(e,t,i),this.rA=L(r[0],r[1],r[2]),this.rB=L(s[0],s[1],s[2]),this.stiffness=a,this.rest=n,this.rest<0&&(ht(Tt,t.positionLin,t.positionAng,this.rA),ht(Ri,i.positionLin,i.positionAng,this.rB),this.rest=Li(X(re,Tt,Ri)))}initialize(){return!0}updatePrimal(e,t,i){let r=this.bodyA,s=this.bodyB;ht(Tt,r.positionLin,r.positionAng,this.rA),ht(Ri,s.positionLin,s.positionAng,this.rB);let a=X(re,Tt,Ri),n=Li(a);if(n<=1e-6)return;let l=ei(me,a,n),c=n-this.rest,f=this.stiffness*c,u=Tt,d=Ri;e===r?(ue(se,r.positionAng,this.rA),u.set(l),Mt(d,se,l)):(ue(se,s.positionAng,this.rB),he(u,l),he(d,Mt(d,se,l))),Se(i.lhsLin,ii(He,Er(He,u,u),this.stiffness)),Se(i.lhsAng,ii(He,Er(He,d,d),this.stiffness)),Se(i.lhsCross,ii(He,Er(He,d,u),this.stiffness)),N(i.rhsLin,i.rhsLin,Ce(se,u,f),1),N(i.rhsAng,i.rhsAng,Ce(se,d,f),1)}updateDual(){}},Vr=class extends Rt{initialize(){return!0}updatePrimal(){}updateDual(){}};var ke=40,Oi=0,cr=4,Na=8,qa=12,co=16,uo=20,Va=24,Wa=28,fo=32,ho=36,te=32,Dt=0,ur=4,ji=8,Ti=12,Ha=16,Ja=20,Gt=24,Di=28,bt=8;var Wr=4,wt=16,Gi=0,Hr=4,Jr=8,$r=12,fr=2147483648,Fi=6,Rl=7,$a=0,po=1,mo=2,Xr=3,dr=1,Ui=2,bo=1,go=2,yo=4,vo=8,xo=16,Ol=.1,Xa=.002,Ya=.002,jl=.05,Ao=44,ae=`
536
536
  // Shared by every 3D module (avbd3d/gpu/layout.ts)
537
- ${Qs}
538
- const T_JOINT = ${nr};
539
- const T_SPRING = ${ji};
537
+ ${so}
538
+ const T_JOINT = ${dr};
539
+ const T_SPRING = ${Ui};
540
540
 
541
541
  const PENALTY_MIN = 1.0;
542
542
  const PENALTY_MAX = 1e10;
@@ -545,22 +545,22 @@ const PIVOT_FLOOR = 1e-5;
545
545
  const COLLISION_MARGIN = 0.01;
546
546
  const STICK_THRESH = 0.00001;
547
547
 
548
- const SHAPE_SPHERE = ${ao}.0;
549
- const SHAPE_SAIL = ${no}.0;
550
- const SHAPE_HULL = ${Hr}.0;
551
- const STICK_BIT = ${ar}u;
552
- const C_MANIFOLDS = ${Oi}u;
553
- const C_PREV_MANIFOLDS = ${gl}u;
548
+ const SHAPE_SPHERE = ${po}.0;
549
+ const SHAPE_SAIL = ${mo}.0;
550
+ const SHAPE_HULL = ${Xr}.0;
551
+ const STICK_BIT = ${fr}u;
552
+ const C_MANIFOLDS = ${Fi}u;
553
+ const C_PREV_MANIFOLDS = ${Rl}u;
554
554
  /** Feature key of every contact involving a sphere (one contact per pair). */
555
555
  const SPHERE_FEATURE = 3u << 24u;
556
556
 
557
- const FLAG_MATCH_NEAREST = ${lo}u;
558
- const FLAG_FACE_BIAS = ${co}u;
559
- const FLAG_REUSE_CONTACTS = ${uo}u;
560
- const FLAG_START_AT_REST = ${fo}u;
561
- const FLAG_MASS_PENALTY = ${ho}u;
562
- const REST_SPEED = ${yl};
563
- const NEAREST_FRACTION = ${vl};
557
+ const FLAG_MATCH_NEAREST = ${bo}u;
558
+ const FLAG_FACE_BIAS = ${go}u;
559
+ const FLAG_REUSE_CONTACTS = ${yo}u;
560
+ const FLAG_START_AT_REST = ${vo}u;
561
+ const FLAG_MASS_PENALTY = ${xo}u;
562
+ const REST_SPEED = ${Ol};
563
+ const NEAREST_FRACTION = ${jl};
564
564
 
565
565
  struct Body {
566
566
  pos: vec4f, // xyz, w: friction
@@ -678,7 +678,7 @@ fn orthonormal(n: vec3f) -> mat3x3f {
678
678
  // Columns of the WGSL matrix are the basis rows
679
679
  return mat3x3f(n, t1, cross(n, t1));
680
680
  }
681
- `,Ti=`
681
+ `,Ni=`
682
682
  alias TopoItem = Manifold;
683
683
 
684
684
  /** A body pair has one manifold, so the other body alone orders a body's contacts. */
@@ -699,7 +699,7 @@ fn jointActive(j: u32) -> bool {
699
699
  let k = joints[j];
700
700
  return info[j].x != T_NONE && (k.penLin.w != 0.0 || k.penAng.w != 0.0);
701
701
  }
702
- `,mo=`
702
+ `,wo=`
703
703
  fn lanesFor(bodies: u32) -> u32 {
704
704
  let rule = params.primalLanes;
705
705
  if (bodies >= (1u << (rule & 255u))) { return 1u; }
@@ -711,7 +711,7 @@ fn lanesFor(bodies: u32) -> u32 {
711
711
  fn colorThreads(bodies: u32) -> u32 {
712
712
  return bodies * lanesFor(bodies);
713
713
  }
714
- `,Ga={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:mo};var Fa=`
714
+ `,Qa={counter:"C_MANIFOLDS",prevCounter:"C_PREV_MANIFOLDS",capacity:"params.manifoldCapacity",colorThreads:wo};var Ka=`
715
715
  const MAX_CLIP = 64u;
716
716
  const NO_HULL = 0xffffffffu;
717
717
 
@@ -1095,8 +1095,8 @@ fn sphereHull(c: vec3f, r: f32, X: Poly) -> SphereBoxHit {
1095
1095
  out.onSphere = c - n * r;
1096
1096
  return out;
1097
1097
  }
1098
- `;var Na=`
1099
- ${me}
1098
+ `;var Yr=`
1099
+ ${ae}
1100
1100
 
1101
1101
  struct RefPose {
1102
1102
  pos: vec4f,
@@ -1116,15 +1116,15 @@ fn updateRefs(@builtin(global_invocation_id) gid: vec3u) {
1116
1116
  let last = refs[i];
1117
1117
  // A zero quaternion marks a body without a reference yet
1118
1118
  let moved = dot(last.rot, last.rot) == 0.0
1119
- || length(pos - last.pos.xyz) > ${Ta}
1120
- || length(qsub(rot, last.rot)) > ${Da};
1119
+ || length(pos - last.pos.xyz) > ${Xa}
1120
+ || length(qsub(rot, last.rot)) > ${Ya};
1121
1121
  if (moved) {
1122
1122
  refs[i] = RefPose(vec4f(pos, 0.0), rot);
1123
1123
  bodies[i].inertialPos.w = bitcast<f32>(params.step);
1124
1124
  }
1125
1125
  }
1126
- `,jt=`
1127
- ${me}
1126
+ `,Ft=`
1127
+ ${ae}
1128
1128
 
1129
1129
  @group(0) @binding(0) var<uniform> params: Params;
1130
1130
  @group(0) @binding(1) var<storage, read> bodies: array<Body>;
@@ -1331,8 +1331,8 @@ fn findPairs(@builtin(global_invocation_id) gid: vec3u) {
1331
1331
  testPair(P, l);
1332
1332
  }
1333
1333
  }
1334
- `,lr=o=>`
1335
- ${me}
1334
+ `,ui=o=>`
1335
+ ${ae}
1336
1336
 
1337
1337
  @group(0) @binding(0) var<uniform> params: Params;
1338
1338
  @group(0) @binding(1) var<storage, read> bodies: array<Body>;
@@ -1747,7 +1747,7 @@ fn reuseContacts(a: u32, b: u32) -> bool {
1747
1747
  return true;
1748
1748
  }
1749
1749
 
1750
- ${o?Fa:"fn isHull(i: u32) -> bool { return false; }"}
1750
+ ${o?Ka:"fn isHull(i: u32) -> bool { return false; }"}
1751
1751
 
1752
1752
  @compute @workgroup_size(64)
1753
1753
  fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
@@ -1876,9 +1876,9 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
1876
1876
  contacts[base + i] = Contact(rA, found.feature[i] | stick, rB, c0.x, pen, c0.y, lam, c0.z);
1877
1877
  }
1878
1878
  }
1879
- `,Ua=lr(!1),qa=lr(!0);var ke=`
1880
- ${me}
1881
- ${mo}
1879
+ `,Za=ui(!1),en=ui(!0);var be=`
1880
+ ${ae}
1881
+ ${wo}
1882
1882
 
1883
1883
  // Per-dispatch constants, selected with a dynamic uniform offset
1884
1884
  struct PassConstants {
@@ -2418,36 +2418,36 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
2418
2418
  bodies[i].vel.w = prevUp;
2419
2419
  }
2420
2420
  }
2421
- `;function Wr(o,e){let t=0,i=o.length-1;for(;t<i;){let r=o[e],s=t,a=i;do{for(;o[s]<r;)s++;for(;r<o[a];)a--;if(s<=a){let n=o[s];o[s]=o[a],o[a]=n,s++,a--}}while(s<=a);a<e&&(t=s),e<s&&(i=a)}return o[e]}function bo(o){let e=o.length,t=e>0?Wr(o,e>>1):1,i=0;for(let r=0;r<e;r++)o[r]<=2*t&&(i=Math.max(i,o[r]));return e>64&&(i=Math.max(i,Wr(o,e-64-1))),i}var wt=o=>o===1/0?3e38:o===-1/0?-3e38:o,He=o=>Math.ceil(o/64),Ha=o=>2**Math.ceil(Math.log2(Math.max(o,2))),xl=o=>Math.max(o,1)*Ce*4,Al=8,Jr=3,Bl=8,wl=1.6,Sl=.8,Cl=2,kl=[0,0,1],Pl=()=>({...Ys(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function Ll(o,e){let t=o.vertices.length/3,i=o.faces.length,r=o.faces.reduce((m,p)=>m+p.verts.length,0),s=Math.ceil(r/4),a=o.edges.length,n=new Float32Array((2+t+2*i+s+a)*4),l=new Uint32Array(n.buffer),c=e+2,f=c+t,u=f+2*i,d=u+s;l.set([c,t,f,i,d,a,u,0],0);for(let m=0;m<t;m++)n.set(o.vertices.subarray(m*3,m*3+3),(2+m)*4);let h=0;return o.faces.forEach((m,p)=>{let b=(2+t+2*p)*4;n.set(m.normal,b),n[b+3]=m.d,l[b+4]=h,l[b+5]=m.verts.length;for(let y of m.verts)l[(2+t+2*i)*4+h++]=y}),o.edges.forEach((m,p)=>l.set(m,(2+t+2*i+s+p)*4)),n}var go=.001;function _l(o,e){let t=o.length,i=o.map(b=>[0,1,2].map(y=>{let A=[0,0,0];return A[y]=1,ce(L(),b.positionAng,A)})),r=o.map((b,y)=>{if(tr(b))return[b.radius,b.radius,b.radius];let A=[0,0,0];for(let w=0;w<3;w++)for(let g=0;g<3;g++)A[g]+=b.size[w]*.5*Math.abs(i[y][w][g]);return A}),s=bo(Float64Array.from(o,b=>b.radius)),a=Math.max(2*s,.001),n=(b,y)=>{let A=o[b],w=o[y];if(A.mass<=0&&w.mass<=0)return!1;let g=[0,1,2].map(v=>A.positionLin[v]-w.positionLin[v]),x=A.radius+w.radius;return g[0]*g[0]+g[1]*g[1]+g[2]*g[2]>x*x?!1:g.every((v,B)=>Math.abs(v)<=r[b][B]+r[y][B])},l=(b,y)=>b[0]*y[0]+b[1]*y[1]+b[2]*y[2],c=(b,y)=>{let A=o[b],w=o[y],g=[0,1,2].map(k=>w.positionLin[k]-A.positionLin[k]),[x,v]=[tr(A),tr(w)];if(x&&v)return Math.hypot(g[0],g[1],g[2])<=A.radius+w.radius+go;if(x||v){let[k,z,M]=x?[y,A.radius,-1]:[b,w.radius,1],P=0;for(let _=0;_<3;_++){let S=Math.abs(M*l(g,i[k][_]))-o[k].size[_]*.5;S>0&&(P+=S*S)}return P<=(z+go)**2}let B=(k,z)=>o[k].size[0]*.5*Math.abs(l(i[k][0],z))+o[k].size[1]*.5*Math.abs(l(i[k][1],z))+o[k].size[2]*.5*Math.abs(l(i[k][2],z)),E=k=>{let z=Math.hypot(k[0],k[1],k[2]);return z<1e-6?!1:Math.abs(l(g,k))-B(b,k)-B(y,k)>go*z};for(let k=0;k<3;k++)if(E(i[b][k])||E(i[y][k]))return!1;for(let k of i[b])for(let z of i[y])if(E([k[1]*z[2]-k[2]*z[1],k[2]*z[0]-k[0]*z[2],k[0]*z[1]-k[1]*z[0]]))return!1;return!0},f=new Set,u=new Map,d=(b,y,A)=>(b*73856093^y*19349663^A*83492791)>>>0,h=b=>[0,1,2].map(y=>Math.floor(o[b].positionLin[y]/a));for(let b=0;b<t;b++){if(o[b].radius>s){f.add(b);continue}let[y,A,w]=h(b),g=d(y,A,w),x=u.get(g);x?x.push(b):u.set(g,[b])}let m=0,p=0;for(let b=0;b<t;b++){if(f.has(b))continue;let[y,A,w]=h(b);for(let g=-1;g<=1;g++)for(let x=-1;x<=1;x++)for(let v=-1;v<=1;v++)for(let B of u.get(d(y+v,A+x,w+g))??[])B<b&&n(b,B)&&(m++,c(b,B)&&(p++,e[b]++,e[B]++))}for(let b of f)for(let y=0;y<t;y++)y!==b&&(!f.has(y)||y<b)&&n(b,y)&&(m++,c(b,y)&&(p++,e[b]++,e[y]++));return{pairs:m,touching:p}}function El(o){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let s of o)for(let a=0;a<3;a++)e[a]=Math.min(e[a],s.positionLin[a]),t[a]=Math.max(t[a],s.positionLin[a]);let i=s=>{let a=s&1023;return a=(a|a<<16)&50331903,a=(a|a<<8)&50393103,a=(a|a<<4)&51130563,a=(a|a<<2)&153391689,a},r=o.map(s=>{let a=[0,1,2].map(n=>Math.min(1023,Math.floor((s.positionLin[n]-e[n])/Math.max(t[n]-e[n],1e-9)*1024)));return(i(a[0])|i(a[1])<<1|i(a[2])<<2)>>>0});return o.map((s,a)=>a).sort((s,a)=>r[s]-r[a]||s-a)}var fe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,$r=class{device;params=Pl();bodyCount=0;bodyCapacity;bodies=[];hulls;hullBuffer;get hullStorage(){return this.hullBuffer}hullCapacity=4096;hullTop=0;hullSlots=new Map;contactShaders;hullShaders=!1;hullFree=[];refToGpu;jointCount=0;jointCapacity=0;info=new Int32Array(0);pairCapacity=0;manifoldCapacity=0;contactCapacity=0;colorCap=12;fixedColors=null;primalLanes=[2**31,2**15,2**12];splitPasses=!1;shrinkVotes=0;colorRounds;get colorHistOffset(){return 3*this.bodyCapacity+65}bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;manifoldBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;filterBuffer;get filterStorage(){return this.filterBuffer}counterBuffer;get contactStorage(){let e=1-this.parity;return{manifolds:this.manifoldBuffers[e],contacts:this.contactBuffers[e],counters:this.counterBuffer}}argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;refBuffer;stepCount=0;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;radiiDirty=!0;noCollideDirty=!0;large=[];entries=[];pendingEntries=[];releasedSlots=new Set;freeSlots=[];layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){this.device=e;let{dt:r,gravity:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f}=t;if(Object.assign(this.params,{dt:r,gravity:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f,up:kl}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(Ce*4)):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies 3d",size:xl(this.bodyCapacity),usage:fe()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:fe()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let u=this.bodyCapacity;this.tableSize=Ha(2*u),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*u)*4,usage:fe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:fe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:fe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(u/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*u+65+64*this.colorGroups+1)*4,usage:fe()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:po*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:u*32,usage:fe()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:fe()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(u).fill(255));let d=2*ue.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:d}),resolve:e.createBuffer({size:d*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:d*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(i.shaders??{}),this.gridScan=new De(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new De(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let h=i.spatialSort===!1?t.bodies.map((v,B)=>B):El(t.bodies);this.refToGpu=new Int32Array(h.length),h.forEach((v,B)=>this.refToGpu[v]=B);let m=new Map(t.bodies.map((v,B)=>[v,this.refToGpu[B]])),p=t.forces.filter(v=>v instanceof Ae||v instanceof ct);for(let v of t.forces){if(!(v instanceof Fr))continue;let B=m.get(v.bodyA),E=m.get(v.bodyB);this.pendingEntries.push([Math.max(B,E),Math.min(B,E),4294967295])}this.allocateJoints(Math.max(p.length+256,i.capacity?.joints??0));for(let v of p)this.writeJoint(this.jointCount++,v,m);let b=new Int32Array(t.bodies.length),{pairs:y,touching:A}=_l(t.bodies,b),w=new Map(t.bodies.map((v,B)=>[v,B]));for(let v of p)v.bodyA&&b[w.get(v.bodyA)]++,b[w.get(v.bodyB)]++;let g=0;t.bodies.forEach((v,B)=>{v.mass>0&&(g=Math.max(g,b[B]))}),this.colorCap=Math.min(64,Math.max(Bl,g+1+Jr,i.capacity?.colors??0));let x=i.capacity??{};this.allocateContacts(Math.max(8192,4*u,Al*A,x.contacts??0),Math.max(4096,4*u,2*y,x.pairs??0),Math.max(4096,u,wl*A,x.manifolds??0)),this.writeBodies(0,h.map(v=>t.bodies[v]))}gpuIndex(e){return e<this.refToGpu.length?this.refToGpu[e]:e}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",s="uniform";return{broad:t("broadphase 3d",[s,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[s,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[s,i,i,i,i,r,r,r]),solve:t("solve 3d",[s,r,r,i,r,i,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args 3d",[s,i,i,r]),refs:t("refs 3d",[s,r,r])}}createPipelines(e){let t=this.device,i=(s,a,n)=>{let l=t.createShaderModule({code:s}),c=t.createPipelineLayout({bindGroupLayouts:a});for(let f of n)this.pipes[f]=t.createComputePipeline({label:f,layout:c,compute:{module:l,entryPoint:f}})},r=this.layouts;i(jt,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(Na,[r.refs],["updateRefs"]),i(e.contacts??Ua,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:s=>i(s,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(ti(me,Ti),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??ke,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(Ks(me,Ga),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*te*4,usage:fe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:fe()});if(this.jointBuffer){let a=t.createCommandEncoder();a.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*te*4),a.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([a.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let s=new Int32Array(e*4);s.set(this.info.subarray(0,Math.min(this.info.length,s.length))),this.info=s,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,s=r.limits.maxStorageBufferBindingSize,a=Math.min(Math.ceil(e),Math.floor(s/(Bt*4))),n=Math.min(Math.ceil(t),Math.floor(s/8)),l=Math.min(Math.ceil(i),n,Math.floor(s/(bt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let f of[this.pairBuffer,this.tableBuffer])f?.destroy();if(this.contactCapacity=a,this.pairCapacity=n,this.manifoldCapacity=l,this.hashSize=Ha(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:n*8,usage:fe()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:fe()}),this.contactBuffers=[0,1].map(f=>r.createBuffer({label:`contacts 3d ${f}`,size:a*Bt*4,usage:fe()})),this.manifoldBuffers=[0,1].map(f=>r.createBuffer({label:`manifolds 3d ${f}`,size:l*bt*4,usage:fe()})),c.contacts){let f=r.createCommandEncoder(),u=Math.min(c.contactCapacity,a)*Bt*4,d=Math.min(c.manifoldCapacity,l)*bt*4;c.contacts.forEach((h,m)=>f.copyBufferToBuffer(h,0,this.contactBuffers[m],0,u)),c.manifolds.forEach((h,m)=>f.copyBufferToBuffer(h,0,this.manifoldBuffers[m],0,d)),r.queue.submit([f.finish()]),[...c.contacts,...c.manifolds].forEach(h=>h.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.manifoldCapacity))*4,usage:fe()}),this.adjScan?.destroy(),this.adjScan=new De(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:fe()});let i=(d,h)=>e.createBindGroup({layout:d,entries:h.map((m,p)=>({binding:p,resource:{buffer:m}}))}),r=this.paramsBuffer,[s,a]=this.contactBuffers,[n,l]=this.manifoldBuffers,c=this.adjBuffer,f=this.tableBuffer,u=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,u,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,s,a,n,l,f,u,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,l,n,f,u,...this.hulls?[this.hullBuffer]:[]])],topo:[n,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,u,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[s,a][d],c,this.colorBuffer,u,[n,l][d]])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer]),refs:i(this.layouts.refs,[r,this.bodyBuffer,this.refBuffer])}}writeBodies(e,t){let i=new Float32Array(Math.max(t.length,1)*Ce);t.forEach((r,s)=>{let a=this.pack(r,i,s*Ce,this.bodies[e+s]?.hull);this.bodies[e+s]?.radius!==a.radius&&(this.radiiDirty=!0),this.bodies[e+s]=a}),this.device.queue.writeBuffer(this.bodyBuffer,e*Ce*4,i,0,t.length*Ce)}rewriteFixed(e,t,i,r,s){if(!e.length)return;let a=new Float32Array(Ce),n={positionLin:[0,0,0],positionAng:[0,0,0,1],initialLin:[0,0,0],initialAng:[0,0,0,1],inertialLin:[0,0,0],inertialAng:[0,0,0,1],velocityLin:[0,0,0],prevVelocityLin:[0,0,0],velocityAng:[0,0,0],size:[i[0],i[1],i[2]],mass:0,moment:[0,0,0],friction:r,radius:Math.hypot(i[0]/2,i[1]/2,i[2]/2)},l=this.pack(n,a,0);for(let c=0;c<e.length;){let f=c+1;for(;f<e.length&&e[f]===e[f-1]+1;)f++;let u=new Float32Array((f-c)*Ce);for(let d=c;d<f;d++){let h=(d-c)*Ce;u.set(a,h);for(let p=0;p<3;p++)u[h+Ei+p]=t[3*d+p];if(s)for(let p=0;p<4;p++)u[h+sr+p]=s[4*d+p];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*Ce*4,u),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(f-c).fill(255)),c=f}}pack(e,t,i,r){{t.set(e.positionLin,i+Ei),t[i+Ei+3]=e.friction,t.set(e.positionAng,i+sr),t.set(e.size,i+io),t[i+io+3]=e.mass,t.set(e.moment,i+ro),t[i+ro+3]=e.radius,t.set(e.initialLin,i+Ea),t.set(e.initialAng,i+za),t.set(e.inertialLin,i+Ma),t.set(e.inertialAng,i+Ia),t.set(e.velocityLin,i+so);let s=this.params.up;t[i+so+3]=e.prevVelocityLin[0]*s[0]+e.prevVelocityLin[1]*s[1]+e.prevVelocityLin[2]*s[2],t.set(e.velocityAng,i+oo);let a=tr(e),n=this.hulls?_a(e):void 0;r&&r!==n&&this.releaseHull(r);let l=n?r===n?this.hullSlots.get(n).offset:this.acquireHull(n):-1;return t[i+oo+3]=a?ao:l>=0?Hr+l:Pa(e)?no:ja,{radius:e.radius,dynamic:e.mass>0,sphere:a,size:[e.size[0],e.size[1],e.size[2]],hull:n}}}writeJoint(e,t,i){let r=new Float32Array(te),s=t.bodyA?i.get(t.bodyA):-1,a=i.get(t.bodyB);t instanceof Ae?(r.set(t.penaltyLin,Rt),r[Rt+3]=wt(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,or),r[or+3]=wt(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,zi),r[zi+3]=wt(t.fracture),r.set(t.lambdaAng,Mi),r[Mi+3]=t.torqueArm,r.set(t.C0Lin,Ra),r.set(t.C0Ang,Oa),r.set(t.rA,Ot),r.set(t.rB,Ii)):(r[Rt+3]=t.stiffness,r.set(t.rA,Ot),r[Ot+3]=t.rest,r.set(t.rB,Ii)),this.info.set([t instanceof Ae?nr:ji,s,a,0],e*4),this.device.queue.writeBuffer(this.jointBuffer,e*te*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info,e*4,4),this.noCollide(s,a,e)}noCollide(e,t,i){e<0||(this.pendingEntries.push([Math.max(e,t),Math.min(e,t),i]),this.noCollideDirty=!0)}uploadStatics(){if(this.radiiDirty){let r=this.bodyCount,s=new Float64Array(r);for(let c=0;c<r;c++)s[c]=this.bodies[c].radius;let a=bo(s),n=1/0;for(let c=0;c<r;c++)s[c]>a&&(n=Math.min(n,s[c]));let l=n===1/0?a*1.5:(a+n)/2;this.maxSmallRadius=l,this.cellSize=Math.max(2*a*(1+1e-4),.001),this.large=[];for(let c=0;c<r;c++)this.bodies[c].radius>l&&this.large.push(c);this.radiiDirty=!1}if(this.noCollideDirty){let r=(l,c)=>l[0]-c[0]||l[1]-c[1],s=this.releasedSlots.size?this.entries.filter(l=>!this.releasedSlots.has(l[2])):this.entries;this.releasedSlots.clear();let a=this.pendingEntries.sort(r);this.pendingEntries=[];let n=[];for(let l=0,c=0;l<s.length||c<a.length;)c>=a.length||l<s.length&&r(s[l],a[c])<=0?n.push(s[l++]):n.push(a[c++]);this.entries=n,this.noCollideDirty=!1}let{large:e,entries:t}=this;this.largeCount=e.length,this.noCollideCount=t.length;let i=new Uint32Array(Math.max(e.length+3*t.length,4));i.set(e,0),t.forEach((r,s)=>i.set(r,e.length+3*s)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:fe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,p)=>m!==p))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,p)=>[m,p]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof Ae||m instanceof ct)&&this.writeJoint(i++,m,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(m=>m instanceof ki),s=r.reduce((m,p)=>m+p.contacts.length,0);if(s>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let a=new ArrayBuffer(Math.max(s,1)*Bt*4),n=new Uint32Array(a),l=new Float32Array(a),c=new ArrayBuffer(Math.max(r.length,1)*bt*4),f=new Uint32Array(c),u=new Float32Array(c),d=0;r.forEach((m,p)=>{f.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],p*bt),u.set([m.basis[0],m.basis[1],m.basis[2],m.friction],p*bt+Nr);for(let b of m.contacts){let y=d++*Bt;l.set(b.rA,y+Ri),n[y+Ri+3]=b.feature>>>0|(b.stick?ar:0),l.set([...b.rB,b.C0[0]],y+Ur),l.set([...b.penalty,b.C0[1]],y+qr),l.set([...b.lambda,b.C0[2]],y+Vr)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,a),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let h=new Uint32Array(16);h[1]=s,h[Oi]=r.length,this.device.queue.writeBuffer(this.counterBuffer,0,h)}sequentialColors(){let e=new Uint32Array(this.bodyCount).fill(255),t=0;for(let i=this.bodyCount-1;i>=0;i--)this.bodies[i].dynamic&&(e[i]=t++);if(t>64)throw new Error(`sequentialColors: ${t} dynamic bodies, at most ${64}`);return e}acquireHull(e){!this.hullShaders&&!this.contactShaders.custom&&(this.contactShaders.make(qa),this.hullShaders=!0);let t=this.hullSlots.get(e);if(t)return t.refs++,t.offset;let i=2+e.vertices.length/3+2*e.faces.length+Math.ceil(e.faces.reduce((a,n)=>a+n.verts.length,0)/4)+e.edges.length,r=-1,s=this.hullFree.findIndex(a=>a.size>=i);if(s>=0){let a=this.hullFree[s];r=a.offset,a.size===i?this.hullFree.splice(s,1):Object.assign(a,{offset:a.offset+i,size:a.size-i})}else r=this.hullTop,this.hullTop+=i,this.hullTop>this.hullCapacity&&this.growHulls(this.hullTop);if(r+Hr>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,Ll(e,r)),this.hullSlots.set(e,{offset:r,size:i,refs:1}),r}releaseHull(e){let t=this.hullSlots.get(e);if(!t||--t.refs>0)return;this.hullSlots.delete(e),this.hullFree.push({offset:t.offset,size:t.size}),this.hullFree.sort((r,s)=>r.offset-s.offset);let i=[];for(let r of this.hullFree){let s=i[i.length-1];s&&s.offset+s.size===r.offset?s.size+=r.size:i.push({...r})}this.hullFree=i}growHulls(e){let t=this.hullCapacity;for(;t<e;)t*=2;let i=this.device.createBuffer({label:"hulls",size:t*16,usage:fe()}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(this.hullBuffer,0,i,0,this.hullCapacity*16),this.device.queue.submit([r.finish()]),this.hullBuffer.destroy(),this.hullBuffer=i,this.hullCapacity=t,this.contactBuffers&&this.rebuildBindings()}addBody(e){return this.addBodies([e])}addBodies(e){let t=Math.min(e.length,this.bodyCapacity-this.bodyCount);if(t<=0)return-1;let i=this.bodyCount;return this.bodyCount+=t,this.writeBodies(i,e.slice(0,t)),this.device.queue.writeBuffer(this.colorBuffer,i*4,new Uint32Array(t).fill(255)),this.adjScan.destroy(),this.adjScan=new De(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,s,a){let n=this.jointCount;n>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(te);l[Rt+3]=wt(s),l[or+3]=wt(a),l[zi+3]=3e38,l.set([i[0],i[1],i[2]],Ot),l.set([r[0],r[1],r[2]],Ii);let c=e>=0?this.bodies[e].size:[0,0,0],f=this.bodies[t].size;return l[Mi+3]=(c[0]+f[0])**2+(c[1]+f[1])**2+(c[2]+f[2])**2,this.info.set([nr,e,t,0],n*4),this.device.queue.writeBuffer(this.jointBuffer,n*te*4,l),this.device.queue.writeBuffer(this.infoBuffer,n*16,this.info,n*4,4),this.noCollide(e,t,n),n}appendJoints(e,t,i=!1){return this.appendConstraints(e.length,(r,s)=>{let{a,b:n,rA:l,rB:c,angular:f}=e[r];s[Rt+3]=3e38,s[or+3]=f===void 0?3e38:wt(f),s[zi+3]=i?-wt(t):wt(t),s.set([l[0],l[1],l[2]],Ot),s.set([c[0],c[1],c[2]],Ii);let[u,d]=[this.bodies[a].size,this.bodies[n].size];return s[Mi+3]=(u[0]+d[0])**2+(u[1]+d[1])**2+(u[2]+d[2])**2,[nr,a,n]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:s,rA:a,rB:n,stiffness:l,rest:c}=e[t];return i[Rt+3]=wt(l),i.set([a[0],a[1],a[2],c],Ot),i.set([n[0],n[1],n[2]],Ii),[ji,r,s]})}appendConstraints(e,t){let i=[];if(this.freeSlots.length)for(this.freeSlots.sort((n,l)=>l-n);i.length<e&&this.freeSlots.length;)i.push(this.freeSlots.pop());let r=e-i.length,s=this.jointCapacity;for(;this.jointCount+r>s;)s*=2;s>this.jointCapacity&&this.allocateJoints(s);for(let n=0;n<r;n++)i.push(this.jointCount++);let a=new Float32Array(e*te);for(let n=0;n<e;n++){let[l,c,f]=t(n,a.subarray(n*te,(n+1)*te));this.info.set([l,c,f,0],i[n]*4),this.releasedSlots.delete(i[n]),this.noCollide(c,f,i[n])}for(let n=0;n<e;){let l=n+1;for(;l<e&&i[l]===i[l-1]+1;)l++;this.device.queue.writeBuffer(this.jointBuffer,i[n]*te*4,a,n*te,(l-n)*te),this.device.queue.writeBuffer(this.infoBuffer,i[n]*16,this.info,i[n]*4,(l-n)*4),n=l}return i}releaseJoints(e){let t=[...new Set(Array.from(e))].filter(r=>r<this.jointCount&&this.info[r*4]!==0).sort((r,s)=>r-s);if(!t.length)return;for(let r of t)this.info.set([0,-1,-1,0],r*4),this.releasedSlots.add(r),this.freeSlots.push(r);let i=new Float32Array(0);for(let r=0;r<t.length;){let s=r+1;for(;s<t.length&&t[s]===t[s-1]+1;)s++;i.length<(s-r)*te&&(i=new Float32Array((s-r)*te)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*te*4,i,0,(s-r)*te),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(s-r)*4),r=s}this.noCollideDirty=!0}rewriteBodies(e,t){for(let i=0;i<e.length;){let r=i+1;for(;r<e.length&&e[r]===e[r-1]+1;)r++;this.writeBodies(e[i],t.slice(i,r)),this.device.queue.writeBuffer(this.colorBuffer,e[i]*4,new Uint32Array(r-i).fill(255)),i=r}}setFilters(e,t,i){for(let r=0;r<e.length;){let s=r+1;for(;s<e.length&&e[s]===e[s-1]+1;)s++;let a=new Uint32Array(2*(s-r));for(let n=r;n<s;n++)a.set([t[n]>>>0,i[n]>>>0],2*(n-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,a),r=s}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Ot)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Rt)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+zi)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+Mi)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(po*4),r=new Float32Array(i),s=new Uint32Array(i);r[0]=e.dt,r[1]=e.gravity,r[2]=e.betaLin,r[3]=e.betaAng,r[4]=e.gamma,r[5]=e.alpha,s[6]=(e.matchNearest?lo:0)|(e.faceBias?co:0)|(e.reuseContacts?uo:0)|(e.startAtRest?fo:0)|(e.massPenalty?ho:0),s[7]=this.bodyCount,s[8]=this.jointCount,s[9]=this.colorCap,r[10]=this.cellSize,s[11]=this.tableSize-1,r[12]=this.maxSmallRadius,s[13]=this.largeCount,s[14]=this.noCollideCount,s[15]=this.pairCapacity,s[16]=this.contactCapacity,s[17]=this.hashSize-1,s[18]=t+1,s[19]=2*t+1,s[20]=this.tableSize+1,s[21]=2*this.tableSize+1,s[22]=2*this.tableSize+1+t,s[23]=t,s[24]=this.colorHistOffset,s[25]=2*t,s[26]=this.colorGroups,s[27]=2*t+65,s[28]=this.colorRounds,s[29]=this.manifoldCapacity,s[30]=this.stepCount;let a=n=>Math.min(31,Math.max(0,Math.round(Math.log2(n))));s[31]=a(this.primalLanes[0])|a(this.primalLanes[1])<<8|a(this.primalLanes[2])<<16,r.set([e.windSpeed*Math.cos(e.windAngle),e.windSpeed*Math.sin(e.windAngle),0,e.windPressure,e.windGust],32),r.set([e.up[0],e.up[1],e.up[2],0],40),this.device.queue.writeBuffer(this.paramsBuffer,0,i)}writePassConstants(e,t){let i=this.colorCap+1,r=Math.max(e*i,1);(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,s)}step(){(this.radiiDirty||this.noCollideDirty)&&this.uploadStatics();let e=this.params;this.fixedColors&&(this.device.queue.writeBuffer(this.colorBuffer,0,this.fixedColors),this.colorCap=Math.max(1,...this.fixedColors.map(A=>A===255?0:A+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,s=this.jointCount,a=this.device.createCommandEncoder({label:"avbd3d step"});a.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),a.clearBuffer(this.tableBuffer),a.clearBuffer(this.adjBuffer,0,(2*i+1)*4);let n=this.timingCallback!==null&&this.timing!==null,l,c=0,f=n||this.splitPasses,u=()=>{l&&!f||(l?.end(),l=a.beginComputePass({label:f?ue[c]:"avbd3d step",timestampWrites:n?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,h=(A,w,g)=>{g<=0||(l.setPipeline(this.pipes[A]),l.setBindGroup(0,w),l.dispatchWorkgroups(g))},m=(A,w,g)=>{l.setPipeline(this.pipes[A]),l.setBindGroup(0,w),l.dispatchWorkgroupsIndirect(this.argsBuffer,g*4)};if(u(),h("beginFrame",d.broad,1),e.reuseContacts&&h("updateRefs",d.refs,He(r)),h("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),h("gridCount",d.broad,He(r)),this.gridScan.encode(l),h("gridScatter",d.broad,He(r)),h("findPairs",d.broad,He(r)),h("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),h("argsContacts",d.args,1),u(),h("degreeJoints",d.topo[t],He(s)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),h("fillJoints",d.topo[t],He(s)),m("fillContacts",d.topo[t],3),h("sortAdjacency",d.topo[t],He(r)),u(),!this.fixedColors){h("colorCompact",d.topo[t],He(r)),h("colorMark",d.topo[t],He(r));for(let A=0;A<this.colorRounds;A++)h(A%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],He(r))}h("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),h("colorStarts",d.topo[t],1),h("argsColors",d.args,1),h("colorScatter",d.topo[t],this.colorGroups),u();let p=this.colorCap+1,b=A=>l.setBindGroup(1,this.passGroup,[A*256]);l.setBindGroup(0,d.solve[t]),b(0),s>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(He(s))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(He(r));for(let A=0;A<e.iterations;A++){l.setPipeline(this.pipes.primal);for(let w=0;w<this.colorCap;w++)b(A*p+w),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*w)*4);b(A*p+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(He(r)),l.end();let y=2*ue.length;if(n){let{querySet:A,resolve:w,read:g}=this.timing;a.resolveQuerySet(A,0,y,w,0),a.copyBufferToBuffer(w,0,g,0,y*8)}if(this.device.queue.submit([a.finish()]),this.parity=1-t,n){let A=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let w=this.timing.read;w.mapAsync(GPUMapMode.READ).then(()=>{let g=new BigUint64Array(w.getMappedRange()).slice();if(w.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let x=(B,E)=>Number(g[E]-g[B])/1e6,v={total:x(0,y-1)};ue.forEach((B,E)=>v[B]=x(2*E,2*E+1)),A(v)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*Ce*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*te*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[Oi],overflow:e[3],clashes:e[4],colors:e[5]}}async readContactList(){let e=await this.readCounters(),t=Math.min(e.manifolds,this.manifoldCapacity),i=Math.min(e.contacts,this.contactCapacity),r=1-this.parity,[s,a]=await Promise.all([this.read(this.manifoldBuffers[r],t*bt*4),this.read(this.contactBuffers[r],i*Bt*4)]),n=new Uint32Array(s),l=new Float32Array(s),c=new Uint32Array(a),f=new Float32Array(a),u=[];for(let d=0;d<t;d++){let h=d*bt,m=[...l.subarray(h+Nr,h+Nr+3)];for(let p=n[h+2];p<n[h+2]+(n[h+3]&15);p++){let b=p*Bt,y=c[b+Ri+3];u.push({a:n[h],b:n[h+1],feature:(y&~ar)>>>0,stick:(y&ar)!==0,rA:[...f.subarray(b+Ri,b+Ri+3)],rB:[...f.subarray(b+Ur,b+Ur+3)],pen:[...f.subarray(b+qr,b+qr+3)],lam:[...f.subarray(b+Vr,b+Vr+3)],normal:m})}}return u}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}jointInfo(){return this.info.subarray(0,this.jointCount*4)}adapt(e){if(this.fixedColors)return;let t=e.colors;e.clashes>0&&(this.colorRounds=Math.min(32,this.colorRounds*2)),e.clashes>0||this.colorCap-t<2?(this.colorCap=Math.min(64,Math.max(t+Jr+2,this.colorCap+4)),this.shrinkVotes=0):t+Jr+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Jr),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(n,l,c)=>(e.overflow&c)!==0||n>Sl*l?Math.max(Cl*n,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),s=i(e.pairs,this.pairCapacity,1),a=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,s,a)&&this.allocateContacts(r,s,a)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(Bt*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(bt*4))>this.manifoldCapacity}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,...this.manifoldBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.refBuffer,this.hullBuffer,this.passBuffer,this.filterBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Wa=` e.C = vec3f(
2421
+ `;function Qr(o,e){let t=0,i=o.length-1;for(;t<i;){let r=o[e],s=t,a=i;do{for(;o[s]<r;)s++;for(;r<o[a];)a--;if(s<=a){let n=o[s];o[s]=o[a],o[a]=n,s++,a--}}while(s<=a);a<e&&(t=s),e<s&&(i=a)}return o[e]}function Bo(o){let e=o.length,t=e>0?Qr(o,e>>1):1,i=0;for(let r=0;r<e;r++)o[r]<=2*t&&(i=Math.max(i,o[r]));return e>64&&(i=Math.max(i,Qr(o,e-64-1))),i}var Bt=o=>o===1/0?3e38:o===-1/0?-3e38:o,Je=o=>Math.ceil(o/64),rn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),Tl=o=>Math.max(o,1)*ke*4,Dl=8,Kr=3,Gl=8,Fl=1.6,Ul=.8,Nl=2,ql=[0,0,1],Vl=()=>({...ro(),matchNearest:!0,faceBias:!0,reuseContacts:!0,startAtRest:!0,massPenalty:!0,windSpeed:0,windAngle:0,windGust:.4,windPressure:.72,up:[0,1,0]});function Wl(o,e){let t=o.vertices.length/3,i=o.faces.length,r=o.faces.reduce((m,h)=>m+h.verts.length,0),s=Math.ceil(r/4),a=o.edges.length,n=new Float32Array((2+t+2*i+s+a)*4),l=new Uint32Array(n.buffer),c=e+2,f=c+t,u=f+2*i,d=u+s;l.set([c,t,f,i,d,a,u,0],0);for(let m=0;m<t;m++)n.set(o.vertices.subarray(m*3,m*3+3),(2+m)*4);let p=0;return o.faces.forEach((m,h)=>{let b=(2+t+2*h)*4;n.set(m.normal,b),n[b+3]=m.d,l[b+4]=p,l[b+5]=m.verts.length;for(let y of m.verts)l[(2+t+2*i)*4+p++]=y}),o.edges.forEach((m,h)=>l.set(m,(2+t+2*i+s+h)*4)),n}var Co=.001;function Hl(o,e){let t=o.length,i=o.map(b=>[0,1,2].map(y=>{let v=[0,0,0];return v[y]=1,ue(L(),b.positionAng,v)})),r=o.map((b,y)=>{if(ar(b))return[b.radius,b.radius,b.radius];let v=[0,0,0];for(let B=0;B<3;B++)for(let g=0;g<3;g++)v[g]+=b.size[B]*.5*Math.abs(i[y][B][g]);return v}),s=Bo(Float64Array.from(o,b=>b.radius)),a=Math.max(2*s,.001),n=(b,y)=>{let v=o[b],B=o[y];if(v.mass<=0&&B.mass<=0)return!1;let g=[0,1,2].map(x=>v.positionLin[x]-B.positionLin[x]),A=v.radius+B.radius;return g[0]*g[0]+g[1]*g[1]+g[2]*g[2]>A*A?!1:g.every((x,w)=>Math.abs(x)<=r[b][w]+r[y][w])},l=(b,y)=>b[0]*y[0]+b[1]*y[1]+b[2]*y[2],c=(b,y)=>{let v=o[b],B=o[y],g=[0,1,2].map(k=>B.positionLin[k]-v.positionLin[k]),[A,x]=[ar(v),ar(B)];if(A&&x)return Math.hypot(g[0],g[1],g[2])<=v.radius+B.radius+Co;if(A||x){let[k,z,M]=A?[y,v.radius,-1]:[b,B.radius,1],P=0;for(let _=0;_<3;_++){let C=Math.abs(M*l(g,i[k][_]))-o[k].size[_]*.5;C>0&&(P+=C*C)}return P<=(z+Co)**2}let w=(k,z)=>o[k].size[0]*.5*Math.abs(l(i[k][0],z))+o[k].size[1]*.5*Math.abs(l(i[k][1],z))+o[k].size[2]*.5*Math.abs(l(i[k][2],z)),E=k=>{let z=Math.hypot(k[0],k[1],k[2]);return z<1e-6?!1:Math.abs(l(g,k))-w(b,k)-w(y,k)>Co*z};for(let k=0;k<3;k++)if(E(i[b][k])||E(i[y][k]))return!1;for(let k of i[b])for(let z of i[y])if(E([k[1]*z[2]-k[2]*z[1],k[2]*z[0]-k[0]*z[2],k[0]*z[1]-k[1]*z[0]]))return!1;return!0},f=new Set,u=new Map,d=(b,y,v)=>(b*73856093^y*19349663^v*83492791)>>>0,p=b=>[0,1,2].map(y=>Math.floor(o[b].positionLin[y]/a));for(let b=0;b<t;b++){if(o[b].radius>s){f.add(b);continue}let[y,v,B]=p(b),g=d(y,v,B),A=u.get(g);A?A.push(b):u.set(g,[b])}let m=0,h=0;for(let b=0;b<t;b++){if(f.has(b))continue;let[y,v,B]=p(b);for(let g=-1;g<=1;g++)for(let A=-1;A<=1;A++)for(let x=-1;x<=1;x++)for(let w of u.get(d(y+x,v+A,B+g))??[])w<b&&n(b,w)&&(m++,c(b,w)&&(h++,e[b]++,e[w]++))}for(let b of f)for(let y=0;y<t;y++)y!==b&&(!f.has(y)||y<b)&&n(b,y)&&(m++,c(b,y)&&(h++,e[b]++,e[y]++));return{pairs:m,touching:h}}function Jl(o){let e=[1/0,1/0,1/0],t=[-1/0,-1/0,-1/0];for(let s of o)for(let a=0;a<3;a++)e[a]=Math.min(e[a],s.positionLin[a]),t[a]=Math.max(t[a],s.positionLin[a]);let i=s=>{let a=s&1023;return a=(a|a<<16)&50331903,a=(a|a<<8)&50393103,a=(a|a<<4)&51130563,a=(a|a<<2)&153391689,a},r=o.map(s=>{let a=[0,1,2].map(n=>Math.min(1023,Math.floor((s.positionLin[n]-e[n])/Math.max(t[n]-e[n],1e-9)*1024)));return(i(a[0])|i(a[1])<<1|i(a[2])<<2)>>>0});return o.map((s,a)=>a).sort((s,a)=>r[s]-r[a]||s-a)}var de=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,Zr=class{device;params=Vl();bodyCount=0;bodyCapacity;bodies=[];hulls;hullBuffer;get hullStorage(){return this.hullBuffer}hullCapacity=4096;hullTop=0;hullSlots=new Map;contactShaders;hullShaders=!1;hullFree=[];refToGpu;jointCount=0;jointCapacity=0;info=new Int32Array(0);pairCapacity=0;manifoldCapacity=0;contactCapacity=0;colorCap=12;fixedColors=null;primalLanes=[2**31,2**15,2**12];splitPasses=!1;shrinkVotes=0;colorRounds;get colorHistOffset(){return 3*this.bodyCapacity+65}bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;manifoldBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;filterBuffer;get filterStorage(){return this.filterBuffer}counterBuffer;get contactStorage(){let e=1-this.parity;return{manifolds:this.manifoldBuffers[e],contacts:this.contactBuffers[e],counters:this.counterBuffer}}argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;refBuffer;stepCount=0;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;radiiDirty=!0;noCollideDirty=!0;large=[];entries=[];pendingEntries=[];releasedSlots=new Set;freeSlots=[];layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){this.device=e;let{dt:r,gravity:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f}=t;if(Object.assign(this.params,{dt:r,gravity:s,iterations:a,alpha:n,betaLin:l,betaAng:c,gamma:f,up:ql}),this.bodyCount=t.bodies.length,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/(ke*4)):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies 3d",size:Tl(this.bodyCapacity),usage:de()}),this.filterBuffer=e.createBuffer({label:"collision filters",size:Math.max(this.bodyCapacity,1)*8,usage:de()}),e.queue.writeBuffer(this.filterBuffer,0,new Uint32Array(Math.max(this.bodyCapacity,1)*2).fill(4294967295));let u=this.bodyCapacity;this.tableSize=rn(2*u),this.gridBuffer=e.createBuffer({label:"grid 3d",size:(2*this.tableSize+1+4*u)*4,usage:de()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:de()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:de()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(u/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*u+65+64*this.colorGroups+1)*4,usage:de()}),this.paramsBuffer=e.createBuffer({label:"params 3d",size:Ao*4,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.refBuffer=e.createBuffer({label:"reference poses",size:u*32,usage:de()}),this.hulls=(i.hulls??!0)&&e.limits.maxStorageBuffersPerShaderStage>=9,this.hullBuffer=e.createBuffer({label:"hulls",size:this.hullCapacity*16,usage:de()}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(u).fill(255));let d=2*fe.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:d}),resolve:e.createBuffer({size:d*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:d*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(i.shaders??{}),this.gridScan=new Fe(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new Fe(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1);let p=i.spatialSort===!1?t.bodies.map((x,w)=>w):Jl(t.bodies);this.refToGpu=new Int32Array(p.length),p.forEach((x,w)=>this.refToGpu[x]=w);let m=new Map(t.bodies.map((x,w)=>[x,this.refToGpu[w]])),h=t.forces.filter(x=>x instanceof we||x instanceof ct);for(let x of t.forces){if(!(x instanceof Vr))continue;let w=m.get(x.bodyA),E=m.get(x.bodyB);this.pendingEntries.push([Math.max(w,E),Math.min(w,E),4294967295])}this.allocateJoints(Math.max(h.length+256,i.capacity?.joints??0));for(let x of h)this.writeJoint(this.jointCount++,x,m);let b=new Int32Array(t.bodies.length),{pairs:y,touching:v}=Hl(t.bodies,b),B=new Map(t.bodies.map((x,w)=>[x,w]));for(let x of h)x.bodyA&&b[B.get(x.bodyA)]++,b[B.get(x.bodyB)]++;let g=0;t.bodies.forEach((x,w)=>{x.mass>0&&(g=Math.max(g,b[w]))}),this.colorCap=Math.min(64,Math.max(Gl,g+1+Kr,i.capacity?.colors??0));let A=i.capacity??{};this.allocateContacts(Math.max(8192,4*u,Dl*v,A.contacts??0),Math.max(4096,4*u,2*y,A.pairs??0),Math.max(4096,u,Fl*v,A.manifolds??0)),this.writeBodies(0,p.map(x=>t.bodies[x]))}gpuIndex(e){return e<this.refToGpu.length?this.refToGpu[e]:e}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",s="uniform";return{broad:t("broadphase 3d",[s,i,r,r,r,i,i,i]),contacts:t("contacts 3d",[s,i,i,r,i,r,i,r,r,...this.hulls?[i]:[]]),topo:t("topology 3d",[s,i,i,i,i,r,r,r]),solve:t("solve 3d",[s,r,r,i,r,i,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args 3d",[s,i,i,r]),refs:t("refs 3d",[s,r,r])}}createPipelines(e){let t=this.device,i=(s,a,n)=>{let l=t.createShaderModule({code:s}),c=t.createPipelineLayout({bindGroupLayouts:a});for(let f of n)this.pipes[f]=t.createComputePipeline({label:f,layout:c,compute:{module:l,entryPoint:f}})},r=this.layouts;i(Ft,[r.broad],["beginFrame","gridCount","gridScatter","findPairs"]),i(Yr,[r.refs],["updateRefs"]),i(e.contacts??Za,[r.contacts],["hashInsert","narrowphase"]),this.contactShaders={make:s=>i(s,[r.contacts],["hashInsert","narrowphase"]),custom:!!e.contacts},i(li(ae,Ni),[r.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),i(e.solve??be,[r.solve,r.pass],["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"]),i(oo(ae,Qa),[r.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints 3d",size:Math.max(e,1)*te*4,usage:de()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:de()});if(this.jointBuffer){let a=t.createCommandEncoder();a.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*te*4),a.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([a.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}let s=new Int32Array(e*4);s.set(this.info.subarray(0,Math.min(this.info.length,s.length))),this.info=s,this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t,i){let r=this.device,s=r.limits.maxStorageBufferBindingSize,a=Math.min(Math.ceil(e),Math.floor(s/(wt*4))),n=Math.min(Math.ceil(t),Math.floor(s/8)),l=Math.min(Math.ceil(i),n,Math.floor(s/(bt*4))),c={contacts:this.contactBuffers,manifolds:this.manifoldBuffers,contactCapacity:this.contactCapacity,manifoldCapacity:this.manifoldCapacity};for(let f of[this.pairBuffer,this.tableBuffer])f?.destroy();if(this.contactCapacity=a,this.pairCapacity=n,this.manifoldCapacity=l,this.hashSize=rn(2*l),this.pairBuffer=r.createBuffer({label:"pairs",size:n*8,usage:de()}),this.tableBuffer=r.createBuffer({label:"manifold hash",size:this.hashSize*4,usage:de()}),this.contactBuffers=[0,1].map(f=>r.createBuffer({label:`contacts 3d ${f}`,size:a*wt*4,usage:de()})),this.manifoldBuffers=[0,1].map(f=>r.createBuffer({label:`manifolds 3d ${f}`,size:l*bt*4,usage:de()})),c.contacts){let f=r.createCommandEncoder(),u=Math.min(c.contactCapacity,a)*wt*4,d=Math.min(c.manifoldCapacity,l)*bt*4;c.contacts.forEach((p,m)=>f.copyBufferToBuffer(p,0,this.contactBuffers[m],0,u)),c.manifolds.forEach((p,m)=>f.copyBufferToBuffer(p,0,this.manifoldBuffers[m],0,d)),r.queue.submit([f.finish()]),[...c.contacts,...c.manifolds].forEach(p=>p.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.manifoldCapacity))*4,usage:de()}),this.adjScan?.destroy(),this.adjScan=new Fe(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:de()});let i=(d,p)=>e.createBindGroup({layout:d,entries:p.map((m,h)=>({binding:h,resource:{buffer:m}}))}),r=this.paramsBuffer,[s,a]=this.contactBuffers,[n,l]=this.manifoldBuffers,c=this.adjBuffer,f=this.tableBuffer,u=this.counterBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,u,this.staticBuffer,this.jointBuffer,this.filterBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,s,a,n,l,f,u,...this.hulls?[this.hullBuffer]:[]]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,l,n,f,u,...this.hulls?[this.hullBuffer]:[]])],topo:[n,l].map(d=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,d,u,c,this.colorBuffer])),solve:[0,1].map(d=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,[s,a][d],c,this.colorBuffer,u,[n,l][d]])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer]),refs:i(this.layouts.refs,[r,this.bodyBuffer,this.refBuffer])}}writeBodies(e,t){let i=new Float32Array(Math.max(t.length,1)*ke);t.forEach((r,s)=>{let a=this.pack(r,i,s*ke,this.bodies[e+s]?.hull);this.bodies[e+s]?.radius!==a.radius&&(this.radiiDirty=!0),this.bodies[e+s]=a}),this.device.queue.writeBuffer(this.bodyBuffer,e*ke*4,i,0,t.length*ke)}rewriteFixed(e,t,i,r,s){if(!e.length)return;let a=new Float32Array(ke),n={positionLin:[0,0,0],positionAng:[0,0,0,1],initialLin:[0,0,0],initialAng:[0,0,0,1],inertialLin:[0,0,0],inertialAng:[0,0,0,1],velocityLin:[0,0,0],prevVelocityLin:[0,0,0],velocityAng:[0,0,0],size:[i[0],i[1],i[2]],mass:0,moment:[0,0,0],friction:r,radius:Math.hypot(i[0]/2,i[1]/2,i[2]/2)},l=this.pack(n,a,0);for(let c=0;c<e.length;){let f=c+1;for(;f<e.length&&e[f]===e[f-1]+1;)f++;let u=new Float32Array((f-c)*ke);for(let d=c;d<f;d++){let p=(d-c)*ke;u.set(a,p);for(let h=0;h<3;h++)u[p+Oi+h]=t[3*d+h];if(s)for(let h=0;h<4;h++)u[p+cr+h]=s[4*d+h];let m=this.bodies[e[d]];m?.radius!==l.radius&&(this.radiiDirty=!0),m?.hull&&this.releaseHull(m.hull),this.bodies[e[d]]={...l,size:[...l.size]}}this.device.queue.writeBuffer(this.bodyBuffer,e[c]*ke*4,u),this.device.queue.writeBuffer(this.colorBuffer,e[c]*4,new Uint32Array(f-c).fill(255)),c=f}}pack(e,t,i,r){{t.set(e.positionLin,i+Oi),t[i+Oi+3]=e.friction,t.set(e.positionAng,i+cr),t.set(e.size,i+co),t[i+co+3]=e.mass,t.set(e.moment,i+uo),t[i+uo+3]=e.radius,t.set(e.initialLin,i+Na),t.set(e.initialAng,i+qa),t.set(e.inertialLin,i+Va),t.set(e.inertialAng,i+Wa),t.set(e.velocityLin,i+fo);let s=this.params.up;t[i+fo+3]=e.prevVelocityLin[0]*s[0]+e.prevVelocityLin[1]*s[1]+e.prevVelocityLin[2]*s[2],t.set(e.velocityAng,i+ho);let a=ar(e),n=this.hulls?Dr(e):void 0;r&&r!==n&&this.releaseHull(r);let l=n?r===n?this.hullSlots.get(n).offset:this.acquireHull(n):-1;return t[i+ho+3]=a?po:l>=0?Xr+l:Fa(e)?mo:$a,{radius:e.radius,dynamic:e.mass>0,sphere:a,size:[e.size[0],e.size[1],e.size[2]],hull:n}}}writeJoint(e,t,i){let r=new Float32Array(te),s=t.bodyA?i.get(t.bodyA):-1,a=i.get(t.bodyB);t instanceof we?(r.set(t.penaltyLin,Dt),r[Dt+3]=Bt(t.broken?0:t.stiffnessLin),r.set(t.penaltyAng,ur),r[ur+3]=Bt(t.broken?0:t.stiffnessAng),r.set(t.lambdaLin,ji),r[ji+3]=Bt(t.fracture),r.set(t.lambdaAng,Ti),r[Ti+3]=t.torqueArm,r.set(t.C0Lin,Ha),r.set(t.C0Ang,Ja),r.set(t.rA,Gt),r.set(t.rB,Di)):(r[Dt+3]=t.stiffness,r.set(t.rA,Gt),r[Gt+3]=t.rest,r.set(t.rB,Di)),this.info.set([t instanceof we?dr:Ui,s,a,0],e*4),this.device.queue.writeBuffer(this.jointBuffer,e*te*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info,e*4,4),this.noCollide(s,a,e)}noCollide(e,t,i){e<0||(this.pendingEntries.push([Math.max(e,t),Math.min(e,t),i]),this.noCollideDirty=!0)}uploadStatics(){if(this.radiiDirty){let r=this.bodyCount,s=new Float64Array(r);for(let c=0;c<r;c++)s[c]=this.bodies[c].radius;let a=Bo(s),n=1/0;for(let c=0;c<r;c++)s[c]>a&&(n=Math.min(n,s[c]));let l=n===1/0?a*1.5:(a+n)/2;this.maxSmallRadius=l,this.cellSize=Math.max(2*a*(1+1e-4),.001),this.large=[];for(let c=0;c<r;c++)this.bodies[c].radius>l&&this.large.push(c);this.radiiDirty=!1}if(this.noCollideDirty){let r=(l,c)=>l[0]-c[0]||l[1]-c[1],s=this.releasedSlots.size?this.entries.filter(l=>!this.releasedSlots.has(l[2])):this.entries;this.releasedSlots.clear();let a=this.pendingEntries.sort(r);this.pendingEntries=[];let n=[];for(let l=0,c=0;l<s.length||c<a.length;)c>=a.length||l<s.length&&r(s[l],a[c])<=0?n.push(s[l++]):n.push(a[c++]);this.entries=n,this.noCollideDirty=!1}let{large:e,entries:t}=this;this.largeCount=e.length,this.noCollideCount=t.length;let i=new Uint32Array(Math.max(e.length+3*t.length,4));i.set(e,0),t.forEach((r,s)=>i.set(r,e.length+3*s)),this.staticBuffer.size<i.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:i.byteLength,usage:de()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,i)}seedFrom(e){if(this.refToGpu.some((m,h)=>m!==h))throw new Error("seedFrom needs spatialSort: false");let t=new Map(e.bodies.map((m,h)=>[m,h]));this.writeBodies(0,e.bodies);let i=0;for(let m of e.forces)(m instanceof we||m instanceof ct)&&this.writeJoint(i++,m,t);if(i!==this.jointCount)throw new Error("seedFrom: joints differ from the reference");let r=e.forces.filter(m=>m instanceof zi),s=r.reduce((m,h)=>m+h.contacts.length,0);if(s>this.contactCapacity||r.length>this.manifoldCapacity)throw new Error("seedFrom: too many contacts");let a=new ArrayBuffer(Math.max(s,1)*wt*4),n=new Uint32Array(a),l=new Float32Array(a),c=new ArrayBuffer(Math.max(r.length,1)*bt*4),f=new Uint32Array(c),u=new Float32Array(c),d=0;r.forEach((m,h)=>{f.set([t.get(m.bodyA),t.get(m.bodyB),d,m.contacts.length],h*bt),u.set([m.basis[0],m.basis[1],m.basis[2],m.friction],h*bt+Wr);for(let b of m.contacts){let y=d++*wt;l.set(b.rA,y+Gi),n[y+Gi+3]=b.feature>>>0|(b.stick?fr:0),l.set([...b.rB,b.C0[0]],y+Hr),l.set([...b.penalty,b.C0[1]],y+Jr),l.set([...b.lambda,b.C0[2]],y+$r)}}),this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,a),this.device.queue.writeBuffer(this.manifoldBuffers[1-this.parity],0,c);let p=new Uint32Array(16);p[1]=s,p[Fi]=r.length,this.device.queue.writeBuffer(this.counterBuffer,0,p)}sequentialColors(){let e=new Uint32Array(this.bodyCount).fill(255),t=0;for(let i=this.bodyCount-1;i>=0;i--)this.bodies[i].dynamic&&(e[i]=t++);if(t>64)throw new Error(`sequentialColors: ${t} dynamic bodies, at most ${64}`);return e}acquireHull(e){!this.hullShaders&&!this.contactShaders.custom&&(this.contactShaders.make(en),this.hullShaders=!0);let t=this.hullSlots.get(e);if(t)return t.refs++,t.offset;let i=2+e.vertices.length/3+2*e.faces.length+Math.ceil(e.faces.reduce((a,n)=>a+n.verts.length,0)/4)+e.edges.length,r=-1,s=this.hullFree.findIndex(a=>a.size>=i);if(s>=0){let a=this.hullFree[s];r=a.offset,a.size===i?this.hullFree.splice(s,1):Object.assign(a,{offset:a.offset+i,size:a.size-i})}else r=this.hullTop,this.hullTop+=i,this.hullTop>this.hullCapacity&&this.growHulls(this.hullTop);if(r+Xr>=2**24)throw new Error("hull buffer beyond 2^24 vec4s");return this.device.queue.writeBuffer(this.hullBuffer,r*16,Wl(e,r)),this.hullSlots.set(e,{offset:r,size:i,refs:1}),r}releaseHull(e){let t=this.hullSlots.get(e);if(!t||--t.refs>0)return;this.hullSlots.delete(e),this.hullFree.push({offset:t.offset,size:t.size}),this.hullFree.sort((r,s)=>r.offset-s.offset);let i=[];for(let r of this.hullFree){let s=i[i.length-1];s&&s.offset+s.size===r.offset?s.size+=r.size:i.push({...r})}this.hullFree=i}growHulls(e){let t=this.hullCapacity;for(;t<e;)t*=2;let i=this.device.createBuffer({label:"hulls",size:t*16,usage:de()}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(this.hullBuffer,0,i,0,this.hullCapacity*16),this.device.queue.submit([r.finish()]),this.hullBuffer.destroy(),this.hullBuffer=i,this.hullCapacity=t,this.contactBuffers&&this.rebuildBindings()}addBody(e){return this.addBodies([e])}addBodies(e){let t=Math.min(e.length,this.bodyCapacity-this.bodyCount);if(t<=0)return-1;let i=this.bodyCount;return this.bodyCount+=t,this.writeBodies(i,e.slice(0,t)),this.device.queue.writeBuffer(this.colorBuffer,i*4,new Uint32Array(t).fill(255)),this.adjScan.destroy(),this.adjScan=new Fe(this.device,this.adjBuffer,0,this.bodyCount+1),i}appendJoint(e,t,i,r,s,a){let n=this.jointCount;n>=this.jointCapacity&&this.allocateJoints(this.jointCapacity*2),this.jointCount++;let l=new Float32Array(te);l[Dt+3]=Bt(s),l[ur+3]=Bt(a),l[ji+3]=3e38,l.set([i[0],i[1],i[2]],Gt),l.set([r[0],r[1],r[2]],Di);let c=e>=0?this.bodies[e].size:[0,0,0],f=this.bodies[t].size;return l[Ti+3]=(c[0]+f[0])**2+(c[1]+f[1])**2+(c[2]+f[2])**2,this.info.set([dr,e,t,0],n*4),this.device.queue.writeBuffer(this.jointBuffer,n*te*4,l),this.device.queue.writeBuffer(this.infoBuffer,n*16,this.info,n*4,4),this.noCollide(e,t,n),n}appendJoints(e,t,i=!1){return this.appendConstraints(e.length,(r,s)=>{let{a,b:n,rA:l,rB:c,angular:f}=e[r];s[Dt+3]=3e38,s[ur+3]=f===void 0?3e38:Bt(f),s[ji+3]=i?-Bt(t):Bt(t),s.set([l[0],l[1],l[2]],Gt),s.set([c[0],c[1],c[2]],Di);let[u,d]=[this.bodies[a].size,this.bodies[n].size];return s[Ti+3]=(u[0]+d[0])**2+(u[1]+d[1])**2+(u[2]+d[2])**2,[dr,a,n]})}appendSprings(e){return this.appendConstraints(e.length,(t,i)=>{let{a:r,b:s,rA:a,rB:n,stiffness:l,rest:c}=e[t];return i[Dt+3]=Bt(l),i.set([a[0],a[1],a[2],c],Gt),i.set([n[0],n[1],n[2]],Di),[Ui,r,s]})}appendConstraints(e,t){let i=[];if(this.freeSlots.length)for(this.freeSlots.sort((n,l)=>l-n);i.length<e&&this.freeSlots.length;)i.push(this.freeSlots.pop());let r=e-i.length,s=this.jointCapacity;for(;this.jointCount+r>s;)s*=2;s>this.jointCapacity&&this.allocateJoints(s);for(let n=0;n<r;n++)i.push(this.jointCount++);let a=new Float32Array(e*te);for(let n=0;n<e;n++){let[l,c,f]=t(n,a.subarray(n*te,(n+1)*te));this.info.set([l,c,f,0],i[n]*4),this.releasedSlots.delete(i[n]),this.noCollide(c,f,i[n])}for(let n=0;n<e;){let l=n+1;for(;l<e&&i[l]===i[l-1]+1;)l++;this.device.queue.writeBuffer(this.jointBuffer,i[n]*te*4,a,n*te,(l-n)*te),this.device.queue.writeBuffer(this.infoBuffer,i[n]*16,this.info,i[n]*4,(l-n)*4),n=l}return i}releaseJoints(e){let t=[...new Set(Array.from(e))].filter(r=>r<this.jointCount&&this.info[r*4]!==0).sort((r,s)=>r-s);if(!t.length)return;for(let r of t)this.info.set([0,-1,-1,0],r*4),this.releasedSlots.add(r),this.freeSlots.push(r);let i=new Float32Array(0);for(let r=0;r<t.length;){let s=r+1;for(;s<t.length&&t[s]===t[s-1]+1;)s++;i.length<(s-r)*te&&(i=new Float32Array((s-r)*te)),this.device.queue.writeBuffer(this.jointBuffer,t[r]*te*4,i,0,(s-r)*te),this.device.queue.writeBuffer(this.infoBuffer,t[r]*16,this.info,t[r]*4,(s-r)*4),r=s}this.noCollideDirty=!0}rewriteBodies(e,t){for(let i=0;i<e.length;){let r=i+1;for(;r<e.length&&e[r]===e[r-1]+1;)r++;this.writeBodies(e[i],t.slice(i,r)),this.device.queue.writeBuffer(this.colorBuffer,e[i]*4,new Uint32Array(r-i).fill(255)),i=r}}setFilters(e,t,i){for(let r=0;r<e.length;){let s=r+1;for(;s<e.length&&e[s]===e[s-1]+1;)s++;let a=new Uint32Array(2*(s-r));for(let n=r;n<s;n++)a.set([t[n]>>>0,i[n]>>>0],2*(n-r));this.device.queue.writeBuffer(this.filterBuffer,e[r]*8,a),r=s}}setWorldAnchor(e,t){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Gt)*4,new Float32Array([t[0],t[1],t[2]]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*te+Dt)*4,new Float32Array(8)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+ji)*4,new Float32Array(3)),this.device.queue.writeBuffer(this.jointBuffer,(e*te+Ti)*4,new Float32Array(3))}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(){let e=this.params,t=this.bodyCapacity,i=new ArrayBuffer(Ao*4),r=new Float32Array(i),s=new Uint32Array(i);r[0]=e.dt,r[1]=e.gravity,r[2]=e.betaLin,r[3]=e.betaAng,r[4]=e.gamma,r[5]=e.alpha,s[6]=(e.matchNearest?bo:0)|(e.faceBias?go:0)|(e.reuseContacts?yo:0)|(e.startAtRest?vo:0)|(e.massPenalty?xo:0),s[7]=this.bodyCount,s[8]=this.jointCount,s[9]=this.colorCap,r[10]=this.cellSize,s[11]=this.tableSize-1,r[12]=this.maxSmallRadius,s[13]=this.largeCount,s[14]=this.noCollideCount,s[15]=this.pairCapacity,s[16]=this.contactCapacity,s[17]=this.hashSize-1,s[18]=t+1,s[19]=2*t+1,s[20]=this.tableSize+1,s[21]=2*this.tableSize+1,s[22]=2*this.tableSize+1+t,s[23]=t,s[24]=this.colorHistOffset,s[25]=2*t,s[26]=this.colorGroups,s[27]=2*t+65,s[28]=this.colorRounds,s[29]=this.manifoldCapacity,s[30]=this.stepCount;let a=n=>Math.min(31,Math.max(0,Math.round(Math.log2(n))));s[31]=a(this.primalLanes[0])|a(this.primalLanes[1])<<8|a(this.primalLanes[2])<<16,r.set([e.windSpeed*Math.cos(e.windAngle),e.windSpeed*Math.sin(e.windAngle),0,e.windPressure,e.windGust],32),r.set([e.up[0],e.up[1],e.up[2],0],40),this.device.queue.writeBuffer(this.paramsBuffer,0,i)}writePassConstants(e,t){let i=this.colorCap+1,r=Math.max(e*i,1);(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t}this.device.queue.writeBuffer(this.passBuffer,0,s)}step(){(this.radiiDirty||this.noCollideDirty)&&this.uploadStatics();let e=this.params;this.fixedColors&&(this.device.queue.writeBuffer(this.colorBuffer,0,this.fixedColors),this.colorCap=Math.max(1,...this.fixedColors.map(v=>v===255?0:v+1))),this.stepCount++,this.writeParams(),this.writePassConstants(e.iterations,e.alpha);let t=this.parity,i=this.bodyCapacity,r=this.bodyCount,s=this.jointCount,a=this.device.createCommandEncoder({label:"avbd3d step"});a.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),a.clearBuffer(this.tableBuffer),a.clearBuffer(this.adjBuffer,0,(2*i+1)*4);let n=this.timingCallback!==null&&this.timing!==null,l,c=0,f=n||this.splitPasses,u=()=>{l&&!f||(l?.end(),l=a.beginComputePass({label:f?fe[c]:"avbd3d step",timestampWrites:n?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*c,endOfPassWriteIndex:2*c+1}:void 0}),c++)},d=this.groups,p=(v,B,g)=>{g<=0||(l.setPipeline(this.pipes[v]),l.setBindGroup(0,B),l.dispatchWorkgroups(g))},m=(v,B,g)=>{l.setPipeline(this.pipes[v]),l.setBindGroup(0,B),l.dispatchWorkgroupsIndirect(this.argsBuffer,g*4)};if(u(),p("beginFrame",d.broad,1),e.reuseContacts&&p("updateRefs",d.refs,Je(r)),p("argsPrev",d.args,1),m("hashInsert",d.contacts[t],6),p("gridCount",d.broad,Je(r)),this.gridScan.encode(l),p("gridScatter",d.broad,Je(r)),p("findPairs",d.broad,Je(r)),p("argsPairs",d.args,1),m("narrowphase",d.contacts[t],0),p("argsContacts",d.args,1),u(),p("degreeJoints",d.topo[t],Je(s)),m("degreeContacts",d.topo[t],3),this.adjScan.encode(l),p("fillJoints",d.topo[t],Je(s)),m("fillContacts",d.topo[t],3),p("sortAdjacency",d.topo[t],Je(r)),u(),!this.fixedColors){p("colorCompact",d.topo[t],Je(r)),p("colorMark",d.topo[t],Je(r));for(let v=0;v<this.colorRounds;v++)p(v%2===0?"colorRoundAB":"colorRoundBA",d.topo[t],Je(r))}p("colorCount",d.topo[t],this.colorGroups),this.colorHistScan.encode(l),p("colorStarts",d.topo[t],1),p("argsColors",d.args,1),p("colorScatter",d.topo[t],this.colorGroups),u();let h=this.colorCap+1,b=v=>l.setBindGroup(1,this.passGroup,[v*256]);l.setBindGroup(0,d.solve[t]),b(0),s>0&&(l.setPipeline(this.pipes.warmStartJoints),l.dispatchWorkgroups(Je(s))),l.setPipeline(this.pipes.warmStartBodies),l.dispatchWorkgroups(Je(r));for(let v=0;v<e.iterations;v++){l.setPipeline(this.pipes.primal);for(let B=0;B<this.colorCap;B++)b(v*h+B),l.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);b(v*h+this.colorCap),l.setPipeline(this.pipes.dual),l.dispatchWorkgroupsIndirect(this.argsBuffer,36)}l.setPipeline(this.pipes.updateVelocities),l.dispatchWorkgroups(Je(r)),l.end();let y=2*fe.length;if(n){let{querySet:v,resolve:B,read:g}=this.timing;a.resolveQuerySet(v,0,y,B,0),a.copyBufferToBuffer(B,0,g,0,y*8)}if(this.device.queue.submit([a.finish()]),this.parity=1-t,n){let v=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let B=this.timing.read;B.mapAsync(GPUMapMode.READ).then(()=>{let g=new BigUint64Array(B.getMappedRange()).slice();if(B.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let A=(w,E)=>Number(g[E]-g[w])/1e6,x={total:A(0,y-1)};fe.forEach((w,E)=>x[w]=A(2*E,2*E+1)),v(x)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*ke*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*te*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],manifolds:e[Fi],overflow:e[3],clashes:e[4],colors:e[5]}}async readContactList(){let e=await this.readCounters(),t=Math.min(e.manifolds,this.manifoldCapacity),i=Math.min(e.contacts,this.contactCapacity),r=1-this.parity,[s,a]=await Promise.all([this.read(this.manifoldBuffers[r],t*bt*4),this.read(this.contactBuffers[r],i*wt*4)]),n=new Uint32Array(s),l=new Float32Array(s),c=new Uint32Array(a),f=new Float32Array(a),u=[];for(let d=0;d<t;d++){let p=d*bt,m=[...l.subarray(p+Wr,p+Wr+3)];for(let h=n[p+2];h<n[p+2]+(n[p+3]&15);h++){let b=h*wt,y=c[b+Gi+3];u.push({a:n[p],b:n[p+1],feature:(y&~fr)>>>0,stick:(y&fr)!==0,rA:[...f.subarray(b+Gi,b+Gi+3)],rB:[...f.subarray(b+Hr,b+Hr+3)],pen:[...f.subarray(b+Jr,b+Jr+3)],lam:[...f.subarray(b+$r,b+$r+3)],normal:m})}}return u}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}jointInfo(){return this.info.subarray(0,this.jointCount*4)}adapt(e){if(this.fixedColors)return;let t=e.colors;e.clashes>0&&(this.colorRounds=Math.min(32,this.colorRounds*2)),e.clashes>0||this.colorCap-t<2?(this.colorCap=Math.min(64,Math.max(t+Kr+2,this.colorCap+4)),this.shrinkVotes=0):t+Kr+2<this.colorCap||this.colorRounds>4?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,t+Kr),this.colorRounds=Math.max(4,this.colorRounds-4),this.shrinkVotes=0):this.shrinkVotes=0;let i=(n,l,c)=>(e.overflow&c)!==0||n>Ul*l?Math.max(Nl*n,1.25*l):l,r=i(e.contacts,this.contactCapacity,2),s=i(e.pairs,this.pairCapacity,1),a=i(e.manifolds,this.manifoldCapacity,4);this.capacityCanGrow(r,s,a)&&this.allocateContacts(r,s,a)}capacityCanGrow(e,t,i){let r=this.device.limits.maxStorageBufferBindingSize;return Math.min(e,r/(wt*4))>this.contactCapacity||Math.min(t,r/8)>this.pairCapacity||Math.min(i,t,r/(bt*4))>this.manifoldCapacity}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,...this.manifoldBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.refBuffer,this.hullBuffer,this.passBuffer,this.filterBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var sn=` e.C = vec3f(
2422
2422
  k.c0x * keep + dot(n, A.dLin) - dot(n, B.dLin) + dot(cross(e.rAW, n), A.dAng) + dot(cross(e.rBW, -n), B.dAng),
2423
2423
  k.c0y * keep + dot(t1, A.dLin) - dot(t1, B.dLin) + dot(cross(e.rAW, t1), A.dAng) + dot(cross(e.rBW, -t1), B.dAng),
2424
- k.c0z * keep + dot(t2, A.dLin) - dot(t2, B.dLin) + dot(cross(e.rAW, t2), A.dAng) + dot(cross(e.rBW, -t2), B.dAng));`;if(ke.split(Wa).length!==2)throw Error("Pinned contact evaluation changed; review the app kernel variant");var ur=ke.replace(Wa,` let delta = A.dLin - B.dLin + cross(A.dAng, e.rAW) - cross(B.dAng, e.rBW);
2425
- e.C = vec3f(k.c0x, k.c0y, k.c0z) * keep + vec3f(dot(n, delta), dot(t1, delta), dot(t2, delta));`);function st(o,e,t){if(o.split(e).length!==2)throw Error("Pinned solve layout changed; review the app kernel variant");return o.replace(e,t)}var Xr=(()=>{let o=st(ur," var e = adj[i] + lane;",` let begin = adj[i];
2426
- var e = begin;`);return o=st(o,` if (c == cEnd) {
2424
+ k.c0z * keep + dot(t2, A.dLin) - dot(t2, B.dLin) + dot(cross(e.rAW, t2), A.dAng) + dot(cross(e.rBW, -t2), B.dAng));`;if(be.split(sn).length!==2)throw Error("Pinned contact evaluation changed; review the app kernel variant");var pr=be.replace(sn,` let delta = A.dLin - B.dLin + cross(A.dAng, e.rAW) - cross(B.dAng, e.rBW);
2425
+ e.C = vec3f(k.c0x, k.c0y, k.c0z) * keep + vec3f(dot(n, delta), dot(t1, delta), dot(t2, delta));`);function ot(o,e,t){if(o.split(e).length!==2)throw Error("Pinned solve layout changed; review the app kernel variant");return o.replace(e,t)}var es=(()=>{let o=ot(pr," var e = adj[i] + lane;",` let begin = adj[i];
2426
+ var e = begin;`);return o=ot(o,` if (c == cEnd) {
2427
2427
  `,` if (c >= cEnd) {
2428
- `),o=st(o,` e += lanes;
2428
+ `),o=ot(o,` e += lanes;
2429
2429
  if (id < params.jointCount) {
2430
2430
  addJoint(&acc, id, pc.alpha, i);`,` e++;
2431
2431
  if (id < params.jointCount) {
2432
2432
  if ((e - begin - 1u) % lanes != lane) { continue; }
2433
- addJoint(&acc, id, pc.alpha, i);`),o=st(o," isA = i == mf.ids.x;",` c = mf.ids.z + lane;
2433
+ addJoint(&acc, id, pc.alpha, i);`),o=ot(o," isA = i == mf.ids.x;",` c = mf.ids.z + lane;
2434
2434
  cEnd = mf.ids.z + pairCount(mf);
2435
2435
  if (c >= cEnd) { continue; }
2436
- isA = i == mf.ids.x;`),o=st(o,` c = mf.ids.z;
2436
+ isA = i == mf.ids.x;`),o=ot(o,` c = mf.ids.z;
2437
2437
  cEnd = mf.ids.z + pairCount(mf);
2438
- if (c == cEnd) { continue; }`,""),o=st(o," c++;"," c += lanes;"),o})();function So(o=ke){o=st(o,"fn manifoldCount() -> u32 {",`fn solverStepValid() -> bool {
2438
+ if (c == cEnd) { continue; }`,""),o=ot(o," c++;"," c += lanes;"),o})();function zo(o=be){o=ot(o,"fn manifoldCount() -> u32 {",`fn solverStepValid() -> bool {
2439
2439
  return counters[C_OVERFLOW] == 0u && counters[C_CLASHES] == 0u;
2440
2440
  }
2441
2441
 
2442
2442
 
2443
- fn manifoldCount() -> u32 {`);for(let[e,t]of[["warmStartJoints","global_invocation_id"],["warmStartBodies","global_invocation_id"],["dual","global_invocation_id"],["updateVelocities","global_invocation_id"]]){let i=`fn ${e}(@builtin(${t}) gid: vec3u) {`;o=st(o,i,`${i}
2444
- if (!solverStepValid()) { return; }`)}return st(o," let live = k < count;"," let live = k < count && solverStepValid();")}function Co(o){return o=st(o," return qsub(rotA(a), bodies[b].rot) * k.lamAng.w;",` let rest = vec4f(k.c0Lin.w, k.c0Ang.w, k.rA.w, k.rB.w);
2443
+ fn manifoldCount() -> u32 {`);for(let[e,t]of[["warmStartJoints","global_invocation_id"],["warmStartBodies","global_invocation_id"],["dual","global_invocation_id"],["updateVelocities","global_invocation_id"]]){let i=`fn ${e}(@builtin(${t}) gid: vec3u) {`;o=ot(o,i,`${i}
2444
+ if (!solverStepValid()) { return; }`)}return ot(o," let live = k < count;"," let live = k < count && solverStepValid();")}function Mo(o){return o=ot(o," return qsub(rotA(a), bodies[b].rot) * k.lamAng.w;",` let rest = vec4f(k.c0Lin.w, k.c0Ang.w, k.rA.w, k.rB.w);
2445
2445
  var restTarget = rotA(a);
2446
2446
  if (dot(rest, rest) > 0.5) {
2447
2447
  restTarget = vec4f(restTarget.w * rest.xyz + rest.w * restTarget.xyz + cross(restTarget.xyz, rest.xyz),
2448
2448
  restTarget.w * rest.w - dot(restTarget.xyz, rest.xyz));
2449
2449
  }
2450
- return qsub(restTarget, bodies[b].rot) * k.lamAng.w;`),o=st(o,"k.c0Lin = vec4f(jointLinC(k, a, b), 0.0);","k.c0Lin = vec4f(jointLinC(k, a, b), k.c0Lin.w);"),st(o,"k.c0Ang = vec4f(jointAngC(k, a, b), 0.0);","k.c0Ang = vec4f(jointAngC(k, a, b), k.c0Ang.w);")}var Ja=`// Capture new constraints from live GPU poses; no stale mesh poses or readback.
2450
+ return qsub(restTarget, bodies[b].rot) * k.lamAng.w;`),o=ot(o,"k.c0Lin = vec4f(jointLinC(k, a, b), 0.0);","k.c0Lin = vec4f(jointLinC(k, a, b), k.c0Lin.w);"),ot(o,"k.c0Ang = vec4f(jointAngC(k, a, b), 0.0);","k.c0Ang = vec4f(jointAngC(k, a, b), k.c0Ang.w);")}var on=`// Capture new constraints from live GPU poses; no stale mesh poses or readback.
2451
2451
  struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
2452
2452
  struct Joint { penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f }
2453
2453
  @group(0) @binding(0) var<storage,read> bodies:array<Body>;
@@ -2472,7 +2472,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
2472
2472
  joints[slot].rA.w=length(pa-pb);
2473
2473
  }
2474
2474
  }
2475
- `;var $a=`// Conservative GPU sleeping. Resting contacts form supports from the ground
2475
+ `;var an=`// Conservative GPU sleeping. Resting contacts form supports from the ground
2476
2476
  // upward. Any impact, moving joint endpoint or explicit edit wakes the world.
2477
2477
  // This deliberately favors reliable waking over finely partitioned islands.
2478
2478
  struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
@@ -2522,13 +2522,13 @@ fn impact(a:u32,b:u32) {
2522
2522
  }
2523
2523
  if(states[i].asleep!=0u){atomicAdd(&globals[1],1u);}
2524
2524
  }
2525
- `;var ko=class{constructor(e){this.device=e,this.targets=new Map,this.bits=new Float32Array(1),this.words=new Uint32Array(this.bits.buffer)}target(e){let t=this.targets.get(e);return t||this.targets.set(e,t={pending:new Map}),t}uint(e,t,i){this.target(e).pending.set(t,i>>>0)}float(e,t,i){this.bits[0]=i,this.uint(e,t,this.words[0])}get pending(){for(let e of this.targets.values())if(e.pending.size)return!0;return!1}flush(e){let t=this.device,i=e,r=0,s=0,a=0,n=0;for(let[l,c]of this.targets){let f=c.pending.size;if(!f)continue;(!c.data||c.data.length<f*2)&&(c.data=new Uint32Array(Math.max(f*2,(c.data?.length??0)*2,128)));let u=Array.from(c.pending.keys()),d=!0;for(let m=1;m<f;m++)if(u[m]<u[m-1]){d=!1;break}d||u.sort((m,p)=>m-p);let h=1;for(let m=1;m<f;m++)u[m]!==u[m-1]+1&&h++;if(h<=4||f<=8){for(let m=0;m<f;m++)c.data[m]=c.pending.get(u[m]);for(let m=0;m<f;){let p=m+1;for(;p<f&&u[p]===u[p-1]+1;)p++;t.queue.writeBuffer(l,u[m]*4,c.data.buffer,m*4,(p-m)*4),r++,m=p}n+=f*4}else{let m=f*8,p=Math.min(t.limits.maxBufferSize,t.limits.maxStorageBufferBindingSize);if(m>p)throw Error("Property batch exceeds GPU buffer limits");for(let w=0;w<f;w++)c.data[w*2]=u[w],c.data[w*2+1]=c.pending.get(u[w]);(!c.upload||c.upload.size<m)&&(c.upload?.destroy(),c.upload=t.createBuffer({size:Math.min(p,m*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),c.group=null),t.queue.writeBuffer(c.upload,0,c.data.buffer,0,m),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"applyEdits",module:t.createShaderModule({code:`
2525
+ `;var Io=class{constructor(e){this.device=e,this.targets=new Map,this.bits=new Float32Array(1),this.words=new Uint32Array(this.bits.buffer)}target(e){let t=this.targets.get(e);return t||this.targets.set(e,t={pending:new Map}),t}uint(e,t,i){this.target(e).pending.set(t,i>>>0)}float(e,t,i){this.bits[0]=i,this.uint(e,t,this.words[0])}get pending(){for(let e of this.targets.values())if(e.pending.size)return!0;return!1}flush(e){let t=this.device,i=e,r=0,s=0,a=0,n=0;for(let[l,c]of this.targets){let f=c.pending.size;if(!f)continue;(!c.data||c.data.length<f*2)&&(c.data=new Uint32Array(Math.max(f*2,(c.data?.length??0)*2,128)));let u=Array.from(c.pending.keys()),d=!0;for(let m=1;m<f;m++)if(u[m]<u[m-1]){d=!1;break}d||u.sort((m,h)=>m-h);let p=1;for(let m=1;m<f;m++)u[m]!==u[m-1]+1&&p++;if(p<=4||f<=8){for(let m=0;m<f;m++)c.data[m]=c.pending.get(u[m]);for(let m=0;m<f;){let h=m+1;for(;h<f&&u[h]===u[h-1]+1;)h++;t.queue.writeBuffer(l,u[m]*4,c.data.buffer,m*4,(h-m)*4),r++,m=h}n+=f*4}else{let m=f*8,h=Math.min(t.limits.maxBufferSize,t.limits.maxStorageBufferBindingSize);if(m>h)throw Error("Property batch exceeds GPU buffer limits");for(let B=0;B<f;B++)c.data[B*2]=u[B],c.data[B*2+1]=c.pending.get(u[B]);(!c.upload||c.upload.size<m)&&(c.upload?.destroy(),c.upload=t.createBuffer({size:Math.min(h,m*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),c.group=null),t.queue.writeBuffer(c.upload,0,c.data.buffer,0,m),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"applyEdits",module:t.createShaderModule({code:`
2526
2526
  @group(0) @binding(0) var<storage,read_write> outputWords:array<u32>;
2527
2527
  @group(0) @binding(1) var<storage,read> edits:array<vec2u>;
2528
2528
  @compute @workgroup_size(64) fn applyEdits(@builtin(global_invocation_id) id:vec3u){
2529
2529
  let i=id.x+id.y*${t.limits.maxComputeWorkgroupsPerDimension*64}u;
2530
2530
  if(i>=arrayLength(&edits)){return;}let edit=edits[i];outputWords[edit.x]=edit.y;
2531
- }`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let b=i.beginComputePass();b.setPipeline(this.pipeline),b.setBindGroup(0,c.group);let y=Math.ceil(f/64),A=t.limits.maxComputeWorkgroupsPerDimension;b.dispatchWorkgroups(Math.min(y,A),Math.ceil(y/A)),b.end(),r++,s++,n+=m}a+=f,c.pending.clear()}!e&&i&&t.queue.submit([i.finish()]),a&&(this.lastBatch={words:a,writes:r,dispatches:s,uploadedBytes:n,submissions:!e&&i?1:0})}destroy(){for(let e of this.targets.values())e.upload?.destroy();this.targets.clear()}forget(e){this.targets.get(e)?.upload?.destroy(),this.targets.delete(e)}};function Q(o){return o.propertyEdits??=new ko(o.device)}var li=class{constructor(e,t={}){if(this.world=e,this.device=e.device,this.speed=t.speedThreshold??.03,this.delay=t.timeThreshold??.5,!Number.isFinite(this.speed)||this.speed<=0||!Number.isFinite(this.delay)||this.delay<=0)throw Error("Sleep speedThreshold and timeThreshold must be positive and finite");let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.capacity*32,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:48,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=i.createShaderModule({code:$a});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((c,f)=>({binding:f,visibility:GPUShaderStage.COMPUTE,buffer:{type:c}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipelines=Object.fromEntries(["before","clear","contacts","joints","rest"].map(c=>[c,i.createComputePipeline({layout:a,compute:{module:s,entryPoint:c}})]));let n=new Float32Array(e.capacity*8),l=new Uint32Array(n.buffer);for(let c of e.aggregates)if(!c.disposed){let f=(c.gpuSlot??e.gpu.gpuIndex(c.index))*8;n[f]=c.rigid.mass,l[f+4]=c.allowSleep===!1||c.rigid.allowSleep===!1?0:1}i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t,i=!0){let r=new Float32Array([t,0,0,0,0,0,0,0]);new Uint32Array(r.buffer)[4]=i?1:0;let s=new Uint32Array(r.buffer),a=Q(this.world.gpu);for(let n=0;n<8;n++)a.uint(this.state,e*8+n,s[n]);this.wakeRequested=!0}bindContacts(){let e=this.world.gpu,t=e.contactStorage,i=[e.bodyBuffer,this.state,e.infoBuffer,t.manifolds,t.counters,this.globals,this.params],r=this.groups?.find(s=>s.buffers.every((a,n)=>a===i[n]));r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((s,a)=>({binding:a,resource:{buffer:s}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),this.group=r.group}before(e){this.world.gpu.flushPropertyEdits(e);let{gpu:t}=this.world,i=t.contactStorage,r=new ArrayBuffer(48),s=new Uint32Array(r),a=new Float32Array(r);s.set([t.bodyCount,t.jointCount,t.manifoldCapacity,this.wakeRequested?1:0]),a.set([this.speed,this.delay,t.params.dt,t.params.gravity],4),a.set(t.params.up,8),this.device.queue.writeBuffer(this.params,0,r),this.wakeRequested=!1,this.bindContacts(),this.run([["before",t.bodyCount],["clear",1]],e)}after(e){let t=this.world.gpu,i=t.contactStorage;this.bindContacts(),this.run([["contacts",t.manifoldCapacity],["joints",t.jointCount],["rest",t.bodyCount]],e)}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let[s,a]of e)a&&(r.setPipeline(this.pipelines[s]),r.dispatchWorkgroups(s==="clear"?1:Math.ceil(a/64)));r.end(),t||this.device.queue.submit([i.finish()])}async readStats(){let e=this.device,t=e.createBuffer({size:8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let i=e.createCommandEncoder();i.copyBufferToBuffer(this.globals,0,t,0,8),e.queue.submit([i.finish()]),await t.mapAsync(GPUMapMode.READ);let r=new Uint32Array(t.getMappedRange());return{sleeping:r[1],wakeRequested:!!r[0]}}finally{t.destroy()}}dispose(){this.world.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var Po=new WeakMap;function St(o={}){let e={};for(let t of["breakStrain","breakForce"]){let i=o[t]??1/0;if(i!==1/0&&(!Number.isFinite(i)||i<=0))throw Error(`${t} must be positive or Infinity`);e[t]=i}return e}function Yr(o,e){let t=St(e);Number.isFinite(t.breakStrain)||Number.isFinite(t.breakForce)?Po.set(o,t):Po.delete(o)}var Lo=o=>Po.get(o);function Qr(o){let e="fn dualJoint(j: u32) {";if(o.split(e).length!==2)throw Error("Review spring fracture kernel integration");return o.replace(e,`${e}
2531
+ }`})}}),(!c.group||c.bytes!==m)&&(c.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:l}},{binding:1,resource:{buffer:c.upload,size:m}}]}),c.bytes=m),i??=t.createCommandEncoder({label:"AVBD property edits"});let b=i.beginComputePass();b.setPipeline(this.pipeline),b.setBindGroup(0,c.group);let y=Math.ceil(f/64),v=t.limits.maxComputeWorkgroupsPerDimension;b.dispatchWorkgroups(Math.min(y,v),Math.ceil(y/v)),b.end(),r++,s++,n+=m}a+=f,c.pending.clear()}!e&&i&&t.queue.submit([i.finish()]),a&&(this.lastBatch={words:a,writes:r,dispatches:s,uploadedBytes:n,submissions:!e&&i?1:0})}destroy(){for(let e of this.targets.values())e.upload?.destroy();this.targets.clear()}forget(e){this.targets.get(e)?.upload?.destroy(),this.targets.delete(e)}};function Q(o){return o.propertyEdits??=new Io(o.device)}var hi=class{constructor(e,t={}){if(this.world=e,this.device=e.device,this.speed=t.speedThreshold??.03,this.delay=t.timeThreshold??.5,!Number.isFinite(this.speed)||this.speed<=0||!Number.isFinite(this.delay)||this.delay<=0)throw Error("Sleep speedThreshold and timeThreshold must be positive and finite");let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.capacity*32,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:48,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=i.createShaderModule({code:an});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((c,f)=>({binding:f,visibility:GPUShaderStage.COMPUTE,buffer:{type:c}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipelines=Object.fromEntries(["before","clear","contacts","joints","rest"].map(c=>[c,i.createComputePipeline({layout:a,compute:{module:s,entryPoint:c}})]));let n=new Float32Array(e.capacity*8),l=new Uint32Array(n.buffer);for(let c of e.aggregates)if(!c.disposed){let f=(c.gpuSlot??e.gpu.gpuIndex(c.index))*8;n[f]=c.rigid.mass,l[f+4]=c.allowSleep===!1||c.rigid.allowSleep===!1?0:1}i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t,i=!0){let r=new Float32Array([t,0,0,0,0,0,0,0]);new Uint32Array(r.buffer)[4]=i?1:0;let s=new Uint32Array(r.buffer),a=Q(this.world.gpu);for(let n=0;n<8;n++)a.uint(this.state,e*8+n,s[n]);this.wakeRequested=!0}bindContacts(){let e=this.world.gpu,t=e.contactStorage,i=[e.bodyBuffer,this.state,e.infoBuffer,t.manifolds,t.counters,this.globals,this.params],r=this.groups?.find(s=>s.buffers.every((a,n)=>a===i[n]));r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((s,a)=>({binding:a,resource:{buffer:s}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),this.group=r.group}before(e){this.world.gpu.flushPropertyEdits(e);let{gpu:t}=this.world,i=t.contactStorage,r=new ArrayBuffer(48),s=new Uint32Array(r),a=new Float32Array(r);s.set([t.bodyCount,t.jointCount,t.manifoldCapacity,this.wakeRequested?1:0]),a.set([this.speed,this.delay,t.params.dt,t.params.gravity],4),a.set(t.params.up,8),this.device.queue.writeBuffer(this.params,0,r),this.wakeRequested=!1,this.bindContacts(),this.run([["before",t.bodyCount],["clear",1]],e)}after(e){let t=this.world.gpu,i=t.contactStorage;this.bindContacts(),this.run([["contacts",t.manifoldCapacity],["joints",t.jointCount],["rest",t.bodyCount]],e)}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let[s,a]of e)a&&(r.setPipeline(this.pipelines[s]),r.dispatchWorkgroups(s==="clear"?1:Math.ceil(a/64)));r.end(),t||this.device.queue.submit([i.finish()])}async readStats(){let e=this.device,t=e.createBuffer({size:8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let i=e.createCommandEncoder();i.copyBufferToBuffer(this.globals,0,t,0,8),e.queue.submit([i.finish()]),await t.mapAsync(GPUMapMode.READ);let r=new Uint32Array(t.getMappedRange());return{sleeping:r[1],wakeRequested:!!r[0]}}finally{t.destroy()}}dispose(){this.world.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var Ro=new WeakMap;function Pt(o={}){let e={};for(let t of["breakStrain","breakForce"]){let i=o[t]??1/0;if(i!==1/0&&(!Number.isFinite(i)||i<=0))throw Error(`${t} must be positive or Infinity`);e[t]=i}return e}function ts(o,e){let t=Pt(e);Number.isFinite(t.breakStrain)||Number.isFinite(t.breakForce)?Ro.set(o,t):Ro.delete(o)}var Oo=o=>Ro.get(o);function is(o){let e="fn dualJoint(j: u32) {";if(o.split(e).length!==2)throw Error("Review spring fracture kernel integration");return o.replace(e,`${e}
2532
2532
  if (info[j].x == T_SPRING) {
2533
2533
  var material = joints[j];
2534
2534
  let strainLimit = material.lamLin.w;
@@ -2550,7 +2550,7 @@ fn impact(a:u32,b:u32) {
2550
2550
  }
2551
2551
  }
2552
2552
  }
2553
- `)}var Il=["auto","standard","optimized","points"];function Di({requested:o="auto",bodyCount:e,constraintCount:t=0,contactScheduling:i="manifolds",custom:r=!1}){if(!Il.includes(o))throw Error("solverMode must be 'auto', 'standard', 'optimized' or 'points'");if(!["manifolds","points"].includes(i))throw Error("contactScheduling must be 'manifolds' or 'points'");if(r&&o!=="auto")throw Error("A scene-specific solve shader cannot be replaced with a rigid-body solver mode");let s=r?"custom":o!=="auto"?o:t>0||e<5e4?"standard":i==="points"?"points":"optimized",a=r?"This scene supplies its own solver. Automatic keeps that implementation.":o!=="auto"?"Explicit implementation override (developer setting).":t>0?"Work layout selected for joints and springs.":e<5e4?"Work layout selected for smaller rigid scenes.":s==="points"?"Large rigid scene with a measured shared-contact-points layout.":"Work layout selected for large rigid scenes.";return{requested:o,selected:s,reason:a,bodyCount:e,constraintCount:t}}var Kr=`// Portable hierarchical PLOC: Morton ordering and independent SAH treelets.
2553
+ `)}var Yl=["auto","standard","optimized","points"];function qi({requested:o="auto",bodyCount:e,constraintCount:t=0,contactScheduling:i="manifolds",custom:r=!1}){if(!Yl.includes(o))throw Error("solverMode must be 'auto', 'standard', 'optimized' or 'points'");if(!["manifolds","points"].includes(i))throw Error("contactScheduling must be 'manifolds' or 'points'");if(r&&o!=="auto")throw Error("A scene-specific solve shader cannot be replaced with a rigid-body solver mode");let s=r?"custom":o!=="auto"?o:t>0||e<5e4?"standard":i==="points"?"points":"optimized",a=r?"This scene supplies its own solver. Automatic keeps that implementation.":o!=="auto"?"Explicit implementation override (developer setting).":t>0?"Work layout selected for joints and springs.":e<5e4?"Work layout selected for smaller rigid scenes.":s==="points"?"Large rigid scene with a measured shared-contact-points layout.":"Work layout selected for large rigid scenes.";return{requested:o,selected:s,reason:a,bodyCount:e,constraintCount:t}}var rs=`// Portable hierarchical PLOC: Morton ordering and independent SAH treelets.
2554
2554
  // Dispatch boundaries publish each hierarchy level; no cross-workgroup spin waits.
2555
2555
  struct Node {
2556
2556
  lo: vec3f,
@@ -2807,7 +2807,7 @@ fn ropes(@builtin(global_invocation_id) gid: vec3u) {
2807
2807
  }
2808
2808
  links[build.escapeOffset + id] = escape;
2809
2809
  }\r
2810
- `;var Xa=`// Stable 4-bit GPU radix sort; PrefixScan scans digit-major block counts.
2810
+ `;var nn=`// Stable 4-bit GPU radix sort; PrefixScan scans digit-major block counts.
2811
2811
  struct SortSettings { count: u32, blocks: u32, shift: u32, _pad: u32 }
2812
2812
  @group(0) @binding(0) var<uniform> sort: SortSettings;
2813
2813
  @group(0) @binding(1) var<storage, read> input: array<vec2u>;
@@ -2855,10 +2855,10 @@ fn scatterDigits(
2855
2855
  }
2856
2856
  output[hist[digit * sort.blocks + wid.x] + rank] = value;
2857
2857
  }\r
2858
- `;var jl=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,ut=(o,e=128)=>Math.max(1,Math.ceil(o/e)),Tl=4294967295,_o=jt.indexOf(`@compute @workgroup_size(64)
2859
- fn gridCount`),Eo=jt.indexOf("/** Binary search"),Ya=jt.indexOf(`@compute @workgroup_size(64)
2860
- fn findPairs`);if(_o<0||Eo<_o||Ya<Eo)throw Error("Pinned broadphase changed; review H-PLOC integration");var Dl=Kr.slice(0,Kr.indexOf("@group(0)")),Gl=(jt.slice(0,_o)+jt.slice(Eo,Ya)).replace("var<storage, read_write> grid: array<atomic<u32>>;","var<storage, read_write> tree: array<Node>;")+`
2861
- ${Dl}
2858
+ `;var Zl=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC,ut=(o,e=128)=>Math.max(1,Math.ceil(o/e)),ec=4294967295,jo=Ft.indexOf(`@compute @workgroup_size(64)
2859
+ fn gridCount`),To=Ft.indexOf("/** Binary search"),ln=Ft.indexOf(`@compute @workgroup_size(64)
2860
+ fn findPairs`);if(jo<0||To<jo||ln<To)throw Error("Pinned broadphase changed; review H-PLOC integration");var tc=rs.slice(0,rs.indexOf("@group(0)")),ic=(Ft.slice(0,jo)+Ft.slice(To,ln)).replace("var<storage, read_write> grid: array<atomic<u32>>;","var<storage, read_write> tree: array<Node>;")+`
2861
+ ${tc}
2862
2862
  @group(1) @binding(0) var<uniform> build: BuildSettings;
2863
2863
  @group(1) @binding(1) var<storage,read> links: array<u32>;
2864
2864
  @compute @workgroup_size(64)
@@ -2880,11 +2880,11 @@ fn findTreePairs(@builtin(global_invocation_id) gid:vec3u) {
2880
2880
  at=links[build.escapeOffset+at];
2881
2881
  } else {at=node.left;}
2882
2882
  }
2883
- }`,Zr=class{constructor(e,t,{rebuildInterval:i=64}={}){if(!Number.isInteger(i)||i<1)throw Error("BVH rebuildInterval must be a positive integer");if(this.device=e,this.capacity=t.bodyCapacity,this.capacity>16*e.limits.maxComputeWorkgroupsPerDimension)throw Error("H-PLOC capacity exceeds the adapter dispatch limit; select the grid");this.bodyBuffer=t.bodyBuffer,this.rebuildInterval=i,this.nodeCapacity=2*this.capacity+16*Math.ceil(Math.log2(Math.max(2,this.capacity)))+32,this.buffers=[],this.uniforms=[],this.stats={builds:0,refits:0,nodes:0,extraBytes:0};let r=(f,u,d=jl())=>{if(d&GPUBufferUsage.STORAGE&&u>e.limits.maxStorageBufferBindingSize)throw Error(`${f} exceeds the adapter storage binding limit`);let h=e.createBuffer({label:f,size:Math.max(16,u),usage:d});return this.buffers.push(h),this.stats.extraBytes+=h.size,h};this.nodes=r("H-PLOC nodes",this.nodeCapacity*32),this.links=r("H-PLOC parents and escape links",this.nodeCapacity*8),this.keys=[r("Morton keys A",this.capacity*8),r("Morton keys B",this.capacity*8)],this.clusters=r("H-PLOC level roots",this.capacity*8+128),this.bounds=r("H-PLOC scene bounds",(ut(this.capacity)*2+256)*16),this.sortBlocks=ut(this.capacity),this.hist=r("Morton radix counts",this.sortBlocks*16*4),this.scan=new De(e,this.hist,0,this.sortBlocks*16);let s=(f,u)=>({binding:f,visibility:GPUShaderStage.COMPUTE,buffer:{type:u}});this.layout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage"),...Array.from({length:5},(f,u)=>s(u+2,"storage"))]}),this.extraLayout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage")]});let a=e.createShaderModule({label:"portable H-PLOC",code:me+Kr+`
2883
+ }`,ss=class{constructor(e,t,{rebuildInterval:i=64}={}){if(!Number.isInteger(i)||i<1)throw Error("BVH rebuildInterval must be a positive integer");if(this.device=e,this.capacity=t.bodyCapacity,this.capacity>16*e.limits.maxComputeWorkgroupsPerDimension)throw Error("H-PLOC capacity exceeds the adapter dispatch limit; select the grid");this.bodyBuffer=t.bodyBuffer,this.rebuildInterval=i,this.nodeCapacity=2*this.capacity+16*Math.ceil(Math.log2(Math.max(2,this.capacity)))+32,this.buffers=[],this.uniforms=[],this.stats={builds:0,refits:0,nodes:0,extraBytes:0};let r=(f,u,d=Zl())=>{if(d&GPUBufferUsage.STORAGE&&u>e.limits.maxStorageBufferBindingSize)throw Error(`${f} exceeds the adapter storage binding limit`);let p=e.createBuffer({label:f,size:Math.max(16,u),usage:d});return this.buffers.push(p),this.stats.extraBytes+=p.size,p};this.nodes=r("H-PLOC nodes",this.nodeCapacity*32),this.links=r("H-PLOC parents and escape links",this.nodeCapacity*8),this.keys=[r("Morton keys A",this.capacity*8),r("Morton keys B",this.capacity*8)],this.clusters=r("H-PLOC level roots",this.capacity*8+128),this.bounds=r("H-PLOC scene bounds",(ut(this.capacity)*2+256)*16),this.sortBlocks=ut(this.capacity),this.hist=r("Morton radix counts",this.sortBlocks*16*4),this.scan=new Fe(e,this.hist,0,this.sortBlocks*16);let s=(f,u)=>({binding:f,visibility:GPUShaderStage.COMPUTE,buffer:{type:u}});this.layout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage"),...Array.from({length:5},(f,u)=>s(u+2,"storage"))]}),this.extraLayout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage")]});let a=e.createShaderModule({label:"portable H-PLOC",code:ae+rs+`
2884
2884
  @compute @workgroup_size(128)
2885
2885
  fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.escapeOffset){links[gid.x]=0xffffffffu;}}
2886
- `}),n=e.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["leaves","reduceBounds","mortonCodes","seedClusters","miniHploc","refitTreelets","ropes","initializeLinks"].map(f=>[f,e.createComputePipeline({layout:n,compute:{module:a,entryPoint:f}})]));let l=e.createShaderModule({label:"stable Morton radix",code:Xa});this.sortLayout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage"),s(2,"storage"),s(3,"storage")]});let c=e.createPipelineLayout({bindGroupLayouts:[this.sortLayout]});this.sortPipes=Object.fromEntries(["countDigits","scatterDigits"].map(f=>[f,e.createComputePipeline({layout:c,compute:{module:l,entryPoint:f}})])),this.traverse=e.createComputePipeline({label:"H-PLOC collision traversal",layout:e.createPipelineLayout({bindGroupLayouts:[t.layouts.broad,this.extraLayout]}),compute:{module:e.createShaderModule({code:Gl}),entryPoint:"findTreePairs"}}),this.configure(t.bodyCount)}uniform(e){let t=this.device.createBuffer({size:e.length*4,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});return new Uint32Array(t.getMappedRange()).set(e),t.unmap(),this.uniforms.push(t),t}configure(e){if(e===this.count)return;if(e>this.capacity)throw Error("H-PLOC body capacity exceeded");for(let d of this.uniforms)d.destroy();this.uniforms=[],this.count=e,this.built=!1,this.stepsSinceBuild=0;let t=[],i=e,r=e,s=0,a=e,n=e===0?Tl:0;for(;i>1;){let d=Math.ceil(i/16);t.push({inputCount:i,nodeBase:r,inputOffset:s,outputOffset:a,blocks:d}),d===1&&(n=r+i-2),r+=d*15,s=a,a+=d,i=d}this.stats.nodes=e?2*e-1:0;let l=(d={})=>{let h=this.uniform([e,d.nodeBase??0,d.inputOffset??0,d.outputOffset??0,n,this.sortBlocks,0,d.boundsOffset??0,d.inputCount??e,this.nodeCapacity,0,0]);return{group:this.device.createBindGroup({layout:this.layout,entries:[h,this.bodyBuffer,this.nodes,this.keys[0],this.bounds,this.links,this.clusters].map((p,b)=>({binding:b,resource:{buffer:p}}))}),buffer:h}};this.base=l(),this.levels=t.map(d=>({...d,...l(d)})),this.reductions=[];let c=ut(e),f=0,u=c*2;for(;c>1;){let d=ut(c);this.reductions.push({blocks:d,...l({inputCount:c,inputOffset:f,outputOffset:u})}),f=u,u+=d*2,c=d}this.morton=l({boundsOffset:f}),this.sortGroups=Array.from({length:8},(d,h)=>{let m=this.uniform([e,this.sortBlocks,h*4,0]);return this.device.createBindGroup({layout:this.sortLayout,entries:[m,this.keys[h%2],this.keys[1-h%2],this.hist].map((p,b)=>({binding:b,resource:{buffer:p}}))})}),this.extra=this.device.createBindGroup({layout:this.extraLayout,entries:[{binding:0,resource:{buffer:this.base.buffer}},{binding:1,resource:{buffer:this.links}}]}),this.broadBuffers=null}encodeBuild(e){let t=(r,s,a)=>{e.setPipeline(this.pipes[r]),e.setBindGroup(0,s),e.dispatchWorkgroups(a)};t("leaves",this.base.group,ut(this.count));let i=!this.built||this.stepsSinceBuild>=this.rebuildInterval;if(this.lastRebuilt=i,i){t("initializeLinks",this.base.group,ut(this.nodeCapacity));for(let r of this.reductions)t("reduceBounds",r.group,r.blocks);t("mortonCodes",this.morton.group,ut(this.count));for(let r of this.sortGroups)e.setPipeline(this.sortPipes.countDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks),this.scan.encode(e),e.setPipeline(this.sortPipes.scatterDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks);t("seedClusters",this.base.group,ut(this.count));for(let r of this.levels)t("miniHploc",r.group,r.blocks);t("ropes",this.base.group,ut(this.nodeCapacity)),this.built=!0,this.stepsSinceBuild=0,this.stats.builds++}else{for(let r of this.levels)t("refitTreelets",r.group,ut(r.blocks,64));this.stats.refits++}this.stepsSinceBuild++}encodePairs(e,t){this.configure(t.bodyCount);let i=[t.paramsBuffer,t.bodyBuffer,this.nodes,t.pairBuffer,t.counterBuffer,t.staticBuffer,t.jointBuffer,t.filterBuffer];(!this.broadBuffers||i.some((r,s)=>r!==this.broadBuffers[s]))&&(this.broadBuffers=i,this.broad=this.device.createBindGroup({layout:t.layouts.broad,entries:i.map((r,s)=>({binding:s,resource:{buffer:r}}))})),e.setPipeline(this.traverse),e.setBindGroup(0,this.broad),e.setBindGroup(1,this.extra),e.dispatchWorkgroups(ut(this.count,64))}destroy(){this.scan.destroy();for(let e of[...this.buffers,...this.uniforms])e.destroy()}};var Je=o=>Math.ceil(o/64);function Qa(o){(o.radiiDirty||o.noCollideDirty)&&o.uploadStatics();let e=o.params;o.fixedColors&&(o.device.queue.writeBuffer(o.colorBuffer,0,o.fixedColors),o.colorCap=Math.max(1,...o.fixedColors.map(v=>v===255?0:v+1))),o.stepCount++,o.writeParams(),o.writePassConstants(e.iterations,e.alpha);let t=o.parity,i=o.bodyCapacity,r=o.bodyCount,s=o.jointCount,a=o.groups,n=o.device.createCommandEncoder({label:"AVBD app step"});o.encodeStepPrelude?.(n),o.bvh||n.clearBuffer(o.gridBuffer,0,(2*o.tableSize+1)*4),n.clearBuffer(o.tableBuffer),n.clearBuffer(o.adjBuffer,0,(2*i+1)*4);let l=!!o.detailCallback,c=l||o.timingCallback!==null&&o.timing!==null,f=c||o.splitPasses,u=l?o.detailTiming:o.timing,d=[],h,m,p=(v,B)=>{if(h&&!l&&(m===B||!f))return;h?.end(),m=B;let E=l?d.length:ue.indexOf(B);d.push({name:v,aggregate:B}),h=n.beginComputePass({label:v,timestampWrites:c?{querySet:u.querySet,beginningOfPassWriteIndex:E*2,endOfPassWriteIndex:E*2+1}:void 0})},b=(v,B,E)=>{E<=0||(h.setPipeline(o.pipes[v]),h.setBindGroup(0,B),h.dispatchWorkgroups(E))},y=(v,B,E)=>{h.setPipeline(o.pipes[v]),h.setBindGroup(0,B),h.dispatchWorkgroupsIndirect(o.argsBuffer,E*4)};if(p("contactCache","collision"),b("beginFrame",a.broad,1),e.reuseContacts&&b("updateRefs",a.refs,Je(r)),b("argsPrev",a.args,1),y("hashInsert",a.contacts[t],6),p("broadphaseBuild","collision"),o.bvh?(o.bvh.configure(r),o.bvh.encodeBuild(h)):(b("gridCount",a.broad,Je(r)),o.gridScan.encode(h),b("gridScatter",a.broad,Je(r))),p("broadphasePairs","collision"),o.bvh?o.bvh.encodePairs(h,o):b("findPairs",a.broad,Je(r)),p("narrowphase","collision"),b("argsPairs",a.args,1),y("narrowphase",a.contacts[t],0),b("argsContacts",a.args,1),p("adjacency","adjacency"),b("degreeJoints",a.topo[t],Je(s)),y("degreeContacts",a.topo[t],3),o.adjScan.encode(h),b("fillJoints",a.topo[t],Je(s)),y("fillContacts",a.topo[t],3),b("sortAdjacency",a.topo[t],Je(r)),p("coloring","coloring"),!o.fixedColors){b("colorCompact",a.topo[t],Je(r)),b("colorMark",a.topo[t],Je(r));for(let v=0;v<o.colorRounds;v++)b(v%2===0?"colorRoundAB":"colorRoundBA",a.topo[t],Je(r))}b("colorCount",a.topo[t],o.colorGroups),o.colorHistScan.encode(h),b("colorStarts",a.topo[t],1),b("argsColors",a.args,1),b("colorScatter",a.topo[t],o.colorGroups);let A=v=>h.setBindGroup(1,o.passGroup,[v*256]),w=()=>{h.setBindGroup(0,a.solve[t]),A(0)};p("warmStart","solve"),w(),s>0&&(h.setPipeline(o.pipes.warmStartJoints),h.dispatchWorkgroups(Je(s))),h.setPipeline(o.pipes.warmStartBodies),r&&h.dispatchWorkgroups(Je(r));for(let v=0;v<e.iterations;v++){p("bodySolve","solve"),w(),h.setPipeline(o.pipes.primal);for(let B=0;B<o.colorCap;B++)A(v*(o.colorCap+1)+B),h.dispatchWorkgroupsIndirect(o.argsBuffer,(12+3*B)*4);p("contactUpdate","solve"),w(),A(v*(o.colorCap+1)+o.colorCap),h.setPipeline(o.pipes.dual),h.dispatchWorkgroupsIndirect(o.argsBuffer,36)}p("velocities","solve"),w(),h.setPipeline(o.pipes.updateVelocities),r&&h.dispatchWorkgroups(Je(r)),h.end();let g=2*(l?d.length:ue.length);if(c&&(n.resolveQuerySet(u.querySet,0,g,u.resolve,0),n.copyBufferToBuffer(u.resolve,0,u.read,0,g*8)),o.parity=1-t,o.encodeStepPostlude?.(n),o.device.queue.submit([n.finish()]),!c)return;let x=l?o.detailCallback:o.timingCallback;l?o.detailCallback=null:o.timingCallback=null,o.timingBusy=!0,u.read.mapAsync(GPUMapMode.READ).then(()=>{let v=new BigUint64Array(u.read.getMappedRange()).slice();if(u.read.unmap(),o.timingBusy=!1,o.destroyed){o.releaseAppTiming(),o.releaseTiming();return}let B=(k,z)=>Number(v[z]-v[k])/1e6,E={total:B(0,g-1),broadphase:o.broadphase,bvhRebuilt:o.bvh?.lastRebuilt??!1};if(l){E.details={};for(let[k,z]of d.entries()){let M=B(k*2,k*2+1);E[z.aggregate]=(E[z.aggregate]??0)+M,E.details[z.name]=(E.details[z.name]??0)+M}}else ue.forEach((k,z)=>E[k]=B(z*2,z*2+1));x(E)},v=>{o.timingBusy=!1,o.profileErrors.push(v.message),o.destroyed&&(o.releaseAppTiming(),o.releaseTiming())})}var Ka=` let list = params.adjListOffset;
2887
- for (var i = lo + 1u; i < hi; i++) {`,Za=ti(me,Ti);if(Za.split(Ka).length!==2)throw Error("Pinned adjacency sort changed; review the cached-key variant");var en=Za.replace(Ka,`
2886
+ `}),n=e.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["leaves","reduceBounds","mortonCodes","seedClusters","miniHploc","refitTreelets","ropes","initializeLinks"].map(f=>[f,e.createComputePipeline({layout:n,compute:{module:a,entryPoint:f}})]));let l=e.createShaderModule({label:"stable Morton radix",code:nn});this.sortLayout=e.createBindGroupLayout({entries:[s(0,"uniform"),s(1,"read-only-storage"),s(2,"storage"),s(3,"storage")]});let c=e.createPipelineLayout({bindGroupLayouts:[this.sortLayout]});this.sortPipes=Object.fromEntries(["countDigits","scatterDigits"].map(f=>[f,e.createComputePipeline({layout:c,compute:{module:l,entryPoint:f}})])),this.traverse=e.createComputePipeline({label:"H-PLOC collision traversal",layout:e.createPipelineLayout({bindGroupLayouts:[t.layouts.broad,this.extraLayout]}),compute:{module:e.createShaderModule({code:ic}),entryPoint:"findTreePairs"}}),this.configure(t.bodyCount)}uniform(e){let t=this.device.createBuffer({size:e.length*4,usage:GPUBufferUsage.UNIFORM,mappedAtCreation:!0});return new Uint32Array(t.getMappedRange()).set(e),t.unmap(),this.uniforms.push(t),t}configure(e){if(e===this.count)return;if(e>this.capacity)throw Error("H-PLOC body capacity exceeded");for(let d of this.uniforms)d.destroy();this.uniforms=[],this.count=e,this.built=!1,this.stepsSinceBuild=0;let t=[],i=e,r=e,s=0,a=e,n=e===0?ec:0;for(;i>1;){let d=Math.ceil(i/16);t.push({inputCount:i,nodeBase:r,inputOffset:s,outputOffset:a,blocks:d}),d===1&&(n=r+i-2),r+=d*15,s=a,a+=d,i=d}this.stats.nodes=e?2*e-1:0;let l=(d={})=>{let p=this.uniform([e,d.nodeBase??0,d.inputOffset??0,d.outputOffset??0,n,this.sortBlocks,0,d.boundsOffset??0,d.inputCount??e,this.nodeCapacity,0,0]);return{group:this.device.createBindGroup({layout:this.layout,entries:[p,this.bodyBuffer,this.nodes,this.keys[0],this.bounds,this.links,this.clusters].map((h,b)=>({binding:b,resource:{buffer:h}}))}),buffer:p}};this.base=l(),this.levels=t.map(d=>({...d,...l(d)})),this.reductions=[];let c=ut(e),f=0,u=c*2;for(;c>1;){let d=ut(c);this.reductions.push({blocks:d,...l({inputCount:c,inputOffset:f,outputOffset:u})}),f=u,u+=d*2,c=d}this.morton=l({boundsOffset:f}),this.sortGroups=Array.from({length:8},(d,p)=>{let m=this.uniform([e,this.sortBlocks,p*4,0]);return this.device.createBindGroup({layout:this.sortLayout,entries:[m,this.keys[p%2],this.keys[1-p%2],this.hist].map((h,b)=>({binding:b,resource:{buffer:h}}))})}),this.extra=this.device.createBindGroup({layout:this.extraLayout,entries:[{binding:0,resource:{buffer:this.base.buffer}},{binding:1,resource:{buffer:this.links}}]}),this.broadBuffers=null}encodeBuild(e){let t=(r,s,a)=>{e.setPipeline(this.pipes[r]),e.setBindGroup(0,s),e.dispatchWorkgroups(a)};t("leaves",this.base.group,ut(this.count));let i=!this.built||this.stepsSinceBuild>=this.rebuildInterval;if(this.lastRebuilt=i,i){t("initializeLinks",this.base.group,ut(this.nodeCapacity));for(let r of this.reductions)t("reduceBounds",r.group,r.blocks);t("mortonCodes",this.morton.group,ut(this.count));for(let r of this.sortGroups)e.setPipeline(this.sortPipes.countDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks),this.scan.encode(e),e.setPipeline(this.sortPipes.scatterDigits),e.setBindGroup(0,r),e.dispatchWorkgroups(this.sortBlocks);t("seedClusters",this.base.group,ut(this.count));for(let r of this.levels)t("miniHploc",r.group,r.blocks);t("ropes",this.base.group,ut(this.nodeCapacity)),this.built=!0,this.stepsSinceBuild=0,this.stats.builds++}else{for(let r of this.levels)t("refitTreelets",r.group,ut(r.blocks,64));this.stats.refits++}this.stepsSinceBuild++}encodePairs(e,t){this.configure(t.bodyCount);let i=[t.paramsBuffer,t.bodyBuffer,this.nodes,t.pairBuffer,t.counterBuffer,t.staticBuffer,t.jointBuffer,t.filterBuffer];(!this.broadBuffers||i.some((r,s)=>r!==this.broadBuffers[s]))&&(this.broadBuffers=i,this.broad=this.device.createBindGroup({layout:t.layouts.broad,entries:i.map((r,s)=>({binding:s,resource:{buffer:r}}))})),e.setPipeline(this.traverse),e.setBindGroup(0,this.broad),e.setBindGroup(1,this.extra),e.dispatchWorkgroups(ut(this.count,64))}destroy(){this.scan.destroy();for(let e of[...this.buffers,...this.uniforms])e.destroy()}};function cn(o,e={}){let t,i,r=!0,s=new Map,a=l=>{if(!i&&!l)return;let c=e.timestampWrites,f=o.beginComputePass({...e,timestampWrites:c&&(r||l)?{querySet:c.querySet,...r&&c.beginningOfPassWriteIndex!==void 0?{beginningOfPassWriteIndex:c.beginningOfPassWriteIndex}:{},...l&&c.endOfPassWriteIndex!==void 0?{endOfPassWriteIndex:c.endOfPassWriteIndex}:{}}:void 0});if(i){f.setPipeline(i.pipeline);for(let[u,d]of i.bindings)f.setBindGroup(u,...d);f[i.method](...i.args)}f.end(),i=null,r=!1},n=(l,c)=>{a(!1),i={pipeline:t,bindings:new Map(s),method:l,args:c}};return{setPipeline(l){t=l},setBindGroup(l,c,f){s.set(l,f===void 0?[c]:[c,[...f]])},dispatchWorkgroups(...l){n("dispatchWorkgroups",l)},dispatchWorkgroupsIndirect(...l){n("dispatchWorkgroupsIndirect",l)},end(){a(!0)}}}var $e=o=>Math.ceil(o/64);function un(o){(o.radiiDirty||o.noCollideDirty)&&o.uploadStatics();let e=o.params;o.fixedColors&&(o.device.queue.writeBuffer(o.colorBuffer,0,o.fixedColors),o.colorCap=Math.max(1,...o.fixedColors.map(x=>x===255?0:x+1))),o.stepCount++,o.writeParams(),o.writePassConstants(e.iterations,e.alpha);let t=o.parity,i=o.bodyCapacity,r=o.bodyCount,s=o.jointCount,a=o.groups,n=o.device.createCommandEncoder({label:"AVBD app step"});o.encodeStepPrelude?.(n),o.bvh||n.clearBuffer(o.gridBuffer,0,(2*o.tableSize+1)*4),n.clearBuffer(o.tableBuffer),n.clearBuffer(o.adjBuffer,0,(2*i+1)*4);let l=!!o.detailCallback,c=l||o.timingCallback!==null&&o.timing!==null,f=c||o.splitPasses,u=l?o.detailTiming:o.timing,d=[],p,m,h=(x,w)=>{if(p&&!l&&(m===w||!f))return;p?.end(),m=w;let E=l?d.length:fe.indexOf(w);d.push({name:x,aggregate:w});let k={label:x,timestampWrites:c?{querySet:u.querySet,beginningOfPassWriteIndex:E*2,endOfPassWriteIndex:E*2+1}:void 0};p=o.dispatchIsolation?cn(n,k):n.beginComputePass(k)},b=(x,w,E)=>{E<=0||(p.setPipeline(o.pipes[x]),p.setBindGroup(0,w),p.dispatchWorkgroups(E))},y=(x,w,E)=>{p.setPipeline(o.pipes[x]),p.setBindGroup(0,w),p.dispatchWorkgroupsIndirect(o.argsBuffer,E*4)};if(h("contactCache","collision"),b("beginFrame",a.broad,1),e.reuseContacts&&b("updateRefs",a.refs,$e(r)),b("argsPrev",a.args,1),y("hashInsert",a.contacts[t],6),h("broadphaseBuild","collision"),o.bvh?(o.bvh.configure(r),o.bvh.encodeBuild(p)):(b("gridCount",a.broad,$e(r)),o.gridScan.encode(p),b("gridScatter",a.broad,$e(r))),h("broadphasePairs","collision"),o.bvh?o.bvh.encodePairs(p,o):b("findPairs",a.broad,$e(r)),h("narrowphase","collision"),b("argsPairs",a.args,1),y("narrowphase",a.contacts[t],0),b("argsContacts",a.args,1),h("adjacency","adjacency"),b("degreeJoints",a.topo[t],$e(s)),y("degreeContacts",a.topo[t],3),o.adjScan.encode(p),b("fillJoints",a.topo[t],$e(s)),y("fillContacts",a.topo[t],3),b("sortAdjacency",a.topo[t],$e(r)),h("coloring","coloring"),!o.fixedColors){b("colorCompact",a.topo[t],$e(r)),b("colorMark",a.topo[t],$e(r));for(let x=0;x<o.colorRounds;x++)b(x%2===0?"colorRoundAB":"colorRoundBA",a.topo[t],$e(r))}b("colorCount",a.topo[t],o.colorGroups),o.colorHistScan.encode(p),b("colorStarts",a.topo[t],1),b("argsColors",a.args,1),b("colorScatter",a.topo[t],o.colorGroups);let v=x=>p.setBindGroup(1,o.passGroup,[x*256]),B=()=>{p.setBindGroup(0,a.solve[t]),v(0)};h("warmStart","solve"),B(),s>0&&(p.setPipeline(o.pipes.warmStartJoints),p.dispatchWorkgroups($e(s))),p.setPipeline(o.pipes.warmStartBodies),r&&p.dispatchWorkgroups($e(r));for(let x=0;x<e.iterations;x++){h("bodySolve","solve"),B(),p.setPipeline(o.pipes.primal);for(let w=0;w<o.colorCap;w++)v(x*(o.colorCap+1)+w),p.dispatchWorkgroupsIndirect(o.argsBuffer,(12+3*w)*4);h("contactUpdate","solve"),B(),v(x*(o.colorCap+1)+o.colorCap),p.setPipeline(o.pipes.dual),p.dispatchWorkgroupsIndirect(o.argsBuffer,36)}h("velocities","solve"),B(),p.setPipeline(o.pipes.updateVelocities),r&&p.dispatchWorkgroups($e(r)),p.end();let g=2*(l?d.length:fe.length);if(c&&(n.resolveQuerySet(u.querySet,0,g,u.resolve,0),n.copyBufferToBuffer(u.resolve,0,u.read,0,g*8)),o.parity=1-t,o.encodeStepPostlude?.(n),o.device.queue.submit([n.finish()]),!c)return;let A=l?o.detailCallback:o.timingCallback;l?o.detailCallback=null:o.timingCallback=null,o.timingBusy=!0,u.read.mapAsync(GPUMapMode.READ).then(()=>{let x=new BigUint64Array(u.read.getMappedRange()).slice();if(u.read.unmap(),o.timingBusy=!1,o.destroyed){o.releaseAppTiming(),o.releaseTiming();return}let w=(k,z)=>Number(x[z]-x[k])/1e6,E={total:w(0,g-1),broadphase:o.broadphase,bvhRebuilt:o.bvh?.lastRebuilt??!1};if(l){E.details={};for(let[k,z]of d.entries()){let M=w(k*2,k*2+1);E[z.aggregate]=(E[z.aggregate]??0)+M,E.details[z.name]=(E.details[z.name]??0)+M}}else fe.forEach((k,z)=>E[k]=w(z*2,z*2+1));A(E)},x=>{o.timingBusy=!1,o.profileErrors.push(x.message),o.destroyed&&(o.releaseAppTiming(),o.releaseTiming())})}var fn=` let list = params.adjListOffset;
2887
+ for (var i = lo + 1u; i < hi; i++) {`,dn=li(ae,Ni);if(dn.split(fn).length!==2)throw Error("Pinned adjacency sort changed; review the cached-key variant");var hn=dn.replace(fn,`
2888
2888
  let list = params.adjListOffset;
2889
2889
  let count = hi - lo;
2890
2890
  if (count <= 16u) {
@@ -2913,13 +2913,23 @@ fn initializeLinks(@builtin(global_invocation_id) gid:vec3u){if(gid.x<build.esca
2913
2913
  }
2914
2914
  return;
2915
2915
  }
2916
- for (var i = lo + 1u; i < hi; i++) {`);function tn(o){return o.replace("return m.ids.w & 15u;","return select(m.ids.w & 15u, 0u, (m.ids.w & 0x80000000u) != 0u);").replace("bodies[i].initialPos = pos;","bodies[i].initialPos = vec4f(pos.xyz, bodies[i].initialPos.w);")}function zo(o){return o.replace("let generated = prev.ids.w >> 4u;","let generated = (prev.ids.w >> 4u) & 0x07ffffffu;").replace("reuseContacts(a, b))","bodies[a].initialPos.w == 0.0 && bodies[b].initialPos.w == 0.0 && reuseContacts(a, b))").replace("count | (params.step << 4u)","count | ((params.step & 0x07ffffffu) << 4u) | select(0u, 0x80000000u, bodies[a].initialPos.w != 0.0 || bodies[b].initialPos.w != 0.0)")}function rn(o=ti(me,Ti)){return o.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
2917
- if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function sn(o){let e=o.device,t=e.createShaderModule({code:`
2916
+ for (var i = lo + 1u; i < hi; i++) {`);function pn(o){return o.replace("return m.ids.w & 15u;","return select(m.ids.w & 15u, 0u, (m.ids.w & 0x80000000u) != 0u);").replace("bodies[i].initialPos = pos;","bodies[i].initialPos = vec4f(pos.xyz, bodies[i].initialPos.w);")}function Do(o){return o.replace("let generated = prev.ids.w >> 4u;","let generated = (prev.ids.w >> 4u) & 0x07ffffffu;").replace("reuseContacts(a, b))","bodies[a].initialPos.w == 0.0 && bodies[b].initialPos.w == 0.0 && reuseContacts(a, b))").replace("count | (params.step << 4u)","count | ((params.step & 0x07ffffffu) << 4u) | select(0u, 0x80000000u, bodies[a].initialPos.w != 0.0 || bodies[b].initialPos.w != 0.0)")}function mn(o=li(ae,Ni)){return o.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
2917
+ if ((contacts[gid.x].ids.w & 0x80000000u) != 0u) { return; }`)}function bn(o){let e=o.device,t=e.createShaderModule({code:`
2918
2918
  @group(0) @binding(0) var<storage,read_write> records:array<vec4f>;
2919
2919
  @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){
2920
2920
  if(id.x*10u+2u>=arrayLength(&records)){return;}
2921
2921
  records[id.x*10u+2u].w=0.;
2922
- }`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:o.bodyBuffer}}]}),s=e.createCommandEncoder(),a=s.beginComputePass();a.setPipeline(i),a.setBindGroup(0,r),a.dispatchWorkgroups(Math.ceil(o.bodyCount/64)),a.end(),e.queue.submit([s.finish()])}var Fl=`${me}
2922
+ }`}),i=e.createComputePipeline({layout:"auto",compute:{module:t,entryPoint:"clear"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:o.bodyBuffer}}]}),s=e.createCommandEncoder(),a=s.beginComputePass();a.setPipeline(i),a.setBindGroup(0,r),a.dispatchWorkgroups(Math.ceil(o.bodyCount/64)),a.end(),e.queue.submit([s.finish()])}var rc=`
2923
+ fn contactGeneration(value: f32) -> u32 {
2924
+ return select(0u, bitcast<u32>(value) ^ 0x3f800000u, value != 0.0);
2925
+ }
2926
+ fn contactMovedAfter(value: f32, generated: u32) -> bool {
2927
+ let age = (contactGeneration(value) - generated) & 0x07ffffffu;
2928
+ return age != 0u && age < 0x04000000u;
2929
+ }
2930
+ `;function gn(o){let e="bodies[i].inertialPos.w = bitcast<f32>(params.step);";if(!o.includes(e))throw Error("Contact reference layout changed");return o.replace(e,"bodies[i].inertialPos.w = bitcast<f32>((params.step & 0x07ffffffu) ^ 0x3f800000u);")}function Go(o){let e=` let moved = max(bitcast<u32>(bodies[a].inertialPos.w), bitcast<u32>(bodies[b].inertialPos.w));
2931
+ if (count == 0u || generated < moved) { return false; }`;if(!o.includes(e))throw Error("Contact cache layout changed");return rc+o.replace(e,` let generation = generated & 0x07ffffffu;
2932
+ if (count == 0u || contactMovedAfter(bodies[a].inertialPos.w, generation) || contactMovedAfter(bodies[b].inertialPos.w, generation)) { return false; }`)}var sc=`${ae}
2923
2933
  struct Config { count:u32, joints:u32, capacity:u32, list:u32, threshold:f32, dt:f32, pad0:u32, pad1:u32 }
2924
2934
  struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
2925
2935
  struct Bounce { impulse:f32, delta:f32, reboundSpeed:f32, inverseMass:f32, rA:vec4f, rB:vec4f }
@@ -2995,7 +3005,7 @@ fn pointVelocity(i:u32,r:vec3f,incoming:bool)->vec3f {
2995
3005
  let i=id.x;if(i>=cfg.count || bodies[i].size.w<=0.){return;}
2996
3006
  bodies[i].vel=velocities[i].linear;bodies[i].angVel=velocities[i].angular;
2997
3007
  }
2998
- `,Gi=class{constructor(e,{code:t=Fl,dimension:i=3}={}){this.solver=e,this.dimension=i,this.device=e.device,this.active=new Set;let r=this.device,s=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST;this.materials=r.createBuffer({size:e.bodyCapacity*4,usage:s}),this.velocities=r.createBuffer({size:e.bodyCapacity*64,usage:s}),this.params=r.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.layout=r.createBindGroupLayout({entries:["uniform","storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=r.createShaderModule({code:t}),n=r.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["captureIncoming","captureSolved","initialize","pairs","apply","writeBack"].map(l=>[l,r.createComputePipeline({layout:n,compute:{module:a,entryPoint:l}})]))}set(e,t){if(!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");t>0?this.active.add(e):this.active.delete(e),Q(this.solver).float(this.materials,e,t)}bind(){let e=this.solver,t=this.device,i=this.dimension===3?e.contactStorage:{manifolds:e.contactBuffers[1-e.parity],contacts:e.contactBuffers[1-e.parity],counters:e.counterBuffer},r=this.dimension===3?e.manifoldCapacity:e.contactCapacity;this.capacity!==r&&(this.bounce?.destroy(),this.capacity=r,this.bounce=t.createBuffer({size:Math.max(1,this.capacity)*48,usage:GPUBufferUsage.STORAGE}));let s=[this.params,e.bodyBuffer,this.velocities,this.materials,i.manifolds,i.contacts,e.adjBuffer,i.counters,this.bounce];(!this.buffers||s.some((n,l)=>n!==this.buffers[l]))&&(this.buffers=s,this.group=t.createBindGroup({layout:this.layout,entries:s.map((n,l)=>({binding:l,resource:{buffer:n}}))}));let a=new Uint32Array([e.bodyCount,e.jointCount,r,2*e.bodyCapacity+1,0,0,0,0]);new Float32Array(a.buffer).set([1,e.params.dt],4),t.queue.writeBuffer(this.params,0,a)}run(e,t){this.bind();let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let s of e)r.setPipeline(this.pipes[s]),r.dispatchWorkgroups(Math.max(1,Math.ceil((s==="initialize"||s==="pairs"?this.capacity:this.solver.bodyCount)/64)));r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.active.size&&this.run(["captureIncoming"],e)}after(e){this.active.size&&this.run(["captureSolved","initialize",...Array.from({length:12},()=>["pairs","apply"]).flat(),"writeBack"],e)}destroy(){for(let e of["materials","velocities","params","bounce"])this[e]?.destroy()}};var on=new WeakMap,Io=o=>on.get(o);function Fi(o,e){on.set(o,e)}var Mo=(o,e,t)=>{if(o.split(e).length!==2)throw Error("Pinned angular solver layout changed");return o.replace(e,t)};function Ro(o){return o=Mo(o,"fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {",`
3008
+ `,Vi=class{constructor(e,{code:t=sc,dimension:i=3}={}){this.solver=e,this.dimension=i,this.device=e.device,this.active=new Set;let r=this.device,s=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST;this.materials=r.createBuffer({size:e.bodyCapacity*4,usage:s}),this.velocities=r.createBuffer({size:e.bodyCapacity*64,usage:s}),this.params=r.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.layout=r.createBindGroupLayout({entries:["uniform","storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=r.createShaderModule({code:t}),n=r.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["captureIncoming","captureSolved","initialize","pairs","apply","writeBack"].map(l=>[l,r.createComputePipeline({layout:n,compute:{module:a,entryPoint:l}})]))}set(e,t){if(!Number.isFinite(t)||t<0||t>1)throw Error("restitution must be between 0 and 1");t>0?this.active.add(e):this.active.delete(e),Q(this.solver).float(this.materials,e,t)}bind(){let e=this.solver,t=this.device,i=this.dimension===3?e.contactStorage:{manifolds:e.contactBuffers[1-e.parity],contacts:e.contactBuffers[1-e.parity],counters:e.counterBuffer},r=this.dimension===3?e.manifoldCapacity:e.contactCapacity;this.capacity!==r&&(this.bounce?.destroy(),this.capacity=r,this.bounce=t.createBuffer({size:Math.max(1,this.capacity)*48,usage:GPUBufferUsage.STORAGE}));let s=[this.params,e.bodyBuffer,this.velocities,this.materials,i.manifolds,i.contacts,e.adjBuffer,i.counters,this.bounce];(!this.buffers||s.some((n,l)=>n!==this.buffers[l]))&&(this.buffers=s,this.group=t.createBindGroup({layout:this.layout,entries:s.map((n,l)=>({binding:l,resource:{buffer:n}}))}));let a=new Uint32Array([e.bodyCount,e.jointCount,r,2*e.bodyCapacity+1,0,0,0,0]);new Float32Array(a.buffer).set([1,e.params.dt],4),t.queue.writeBuffer(this.params,0,a)}run(e,t){this.bind();let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();r.setBindGroup(0,this.group);for(let s of e)r.setPipeline(this.pipes[s]),r.dispatchWorkgroups(Math.max(1,Math.ceil((s==="initialize"||s==="pairs"?this.capacity:this.solver.bodyCount)/64)));r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.active.size&&this.run(["captureIncoming"],e)}after(e){this.active.size&&this.run(["captureSolved","initialize",...Array.from({length:12},()=>["pairs","apply"]).flat(),"writeBack"],e)}destroy(){for(let e of["materials","velocities","params","bounce"])this[e]?.destroy()}};var yn=new WeakMap,Uo=o=>yn.get(o);function Wi(o,e){yn.set(o,e)}var Fo=(o,e,t)=>{if(o.split(e).length!==2)throw Error("Pinned angular solver layout changed");return o.replace(e,t)};function No(o){return o=Fo(o,"fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {",`
2999
3009
  fn angularFrame(k:Joint,a:i32,b:i32)->mat3x3f {
3000
3010
  let qa=rotA(a);let axis=qrotate(qa,k.rA.xyz);
3001
3011
  let ta=qrotate(qa,k.c0Lin.xyz);let tb=qrotate(bodies[b].rot,k.c0Ang.xyz);
@@ -3046,10 +3056,10 @@ fn dualAngular(j:u32){
3046
3056
  }
3047
3057
  `+`
3048
3058
  fn addJoint(acc: ptr<function, Acc>, j: u32, alpha: f32, i: u32) {
3049
- if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),o=Mo(o," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if(j>=params.jointCount){return;}
3059
+ if(info[j].x==3 || info[j].x==4){if(joints[j].penLin.w!=0.){addAngular(acc,j,alpha,i);}return;}`),o=Fo(o," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if(j>=params.jointCount){return;}
3050
3060
  if(info[j].x==3 || info[j].x==4){warmAngular(j);return;}
3051
- if(info[j].x!=T_JOINT){return;}`),Mo(o,"fn dualJoint(j: u32) {",`fn dualJoint(j: u32) {
3052
- if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function gt({type:o,axisA:e=[0,1,0],axisB:t=[0,1,0],speed:i=1,maxTorque:r=100,minAngle:s=-Math.PI,maxAngle:a=Math.PI}={}){let n=l=>{let c=l&&typeof l.x=="number"?[l.x,l.y,l.z]:Array.from(l),f=Math.hypot(...c);if(c.length!==3||!c.every(Number.isFinite)||f<1e-8)throw Error("Angular axis requires three finite, nonzero components");return c.map(u=>u/f)};if(o!=="motor"&&o!=="limit")throw Error("Invalid angular constraint type");if(!Number.isFinite(i)||!Number.isFinite(r)||r<0)throw Error("Motor speed must be finite and maxTorque nonnegative");if(!Number.isFinite(s)||!Number.isFinite(a)||s<-Math.PI||a>Math.PI||s>a)throw Error("Hinge angles must be ordered within -PI..PI radians");return{type:o,axisA:n(e),axisB:n(t),speed:i,maxTorque:r,minAngle:s,maxAngle:a}}var an=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
3061
+ if(info[j].x!=T_JOINT){return;}`),Fo(o,"fn dualJoint(j: u32) {",`fn dualJoint(j: u32) {
3062
+ if(info[j].x==3 || info[j].x==4){dualAngular(j);return;}`)}function gt({type:o,axisA:e=[0,1,0],axisB:t=[0,1,0],speed:i=1,maxTorque:r=100,minAngle:s=-Math.PI,maxAngle:a=Math.PI}={}){let n=l=>{let c=l&&typeof l.x=="number"?[l.x,l.y,l.z]:Array.from(l),f=Math.hypot(...c);if(c.length!==3||!c.every(Number.isFinite)||f<1e-8)throw Error("Angular axis requires three finite, nonzero components");return c.map(u=>u/f)};if(o!=="motor"&&o!=="limit")throw Error("Invalid angular constraint type");if(!Number.isFinite(i)||!Number.isFinite(r)||r<0)throw Error("Motor speed must be finite and maxTorque nonnegative");if(!Number.isFinite(s)||!Number.isFinite(a)||s<-Math.PI||a>Math.PI||s>a)throw Error("Hinge angles must be ordered within -PI..PI radians");return{type:o,axisA:n(e),axisB:n(t),speed:i,maxTorque:r,minAngle:s,maxAngle:a}}var vn=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
3053
3063
  struct Joint {penLin:vec4f,penAng:vec4f,lamLin:vec4f,lamAng:vec4f,c0Lin:vec4f,c0Ang:vec4f,rA:vec4f,rB:vec4f}
3054
3064
  @group(0) @binding(0) var<storage,read> bodies:array<Body>;
3055
3065
  @group(0) @binding(1) var<storage,read_write> joints:array<Joint>;
@@ -3067,7 +3077,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f{let t=2.*cross(q.xyz,v);return v+q.w*t+cross(q
3067
3077
  if(length(tangentB)<1.e-6){tangentB=select(vec3f(0.,-axisB.z,axisB.y),vec3f(-axisB.y,axisB.x,0.),abs(axisB.x)>abs(axisB.z));}
3068
3078
  joints[j].c0Lin=vec4f(tangent,0.);joints[j].c0Ang=vec4f(normalize(tangentB),0.);
3069
3079
  }
3070
- `;var nn=new WeakSet,es=o=>nn.has(o);function ts(o,e,t,i=1,r=.6,s=[0,0,0]){if(!Number.isFinite(e)||e<=0||!Number.isFinite(t)||t<2*e)throw Error("Capsule height must be at least twice its positive radius");if(!Number.isFinite(i)||i<0)throw Error("Capsule density must be nonnegative");let a=new at(o,[2*e,t,2*e],i,r,s),n=t-2*e,l=Math.PI*e*e*n*i,c=4/3*Math.PI*e**3*i;a.mass=l+c;let f=l*e**2/2+c*2*e**2/5,u=l*(n**2/12+e**2/4)+c*(2*e**2/5+n**2/4+3*n*e/8);return a.moment.set([u,f,u]),a.radius=t/2,nn.add(a),a}var ln=`// Exact rounded capsule geometry. Shape -1: radius=size.x/2, segment along Y,
3080
+ `;var xn=new WeakSet,os=o=>xn.has(o);function Hi(o,e,t,i=1,r=.6,s=[0,0,0]){if(!Number.isFinite(e)||e<=0||!Number.isFinite(t)||t<2*e)throw Error("Capsule height must be at least twice its positive radius");if(!Number.isFinite(i)||i<0)throw Error("Capsule density must be nonnegative");let a=new De(o,[2*e,t,2*e],i,r,s),n=t-2*e,l=Math.PI*e*e*n*i,c=4/3*Math.PI*e**3*i;a.mass=l+c;let f=l*e**2/2+c*2*e**2/5,u=l*(n**2/12+e**2/4)+c*(2*e**2/5+n**2/4+3*n*e/8);return a.moment.set([u,f,u]),a.radius=t/2,xn.add(a),a}var An=`// Exact rounded capsule geometry. Shape -1: radius=size.x/2, segment along Y,
3071
3081
  // half-length=size.y/2-radius. No tessellation, endpoint-only collision test,
3072
3082
  // shape storage buffer or iterative support search for capsule/sphere/box.
3073
3083
  struct Capsule {a:vec3f,b:vec3f,r:f32}
@@ -3146,16 +3156,16 @@ fn capsuleBox(A:Capsule,B:Box,q:vec4f,sat:ptr<function,Sat>)->Found{
3146
3156
  if(ends.count==2u){return ends;}
3147
3157
  addFound(&found,B.c+qrotate(q,center)+n*A.r,B.c+qrotate(q,onBox),4u<<24u);return found;
3148
3158
  }
3149
- `;function cn(o){if(o.includes("fn capsuleOf"))throw Error("Capsule collision kernel installed twice");let e=`@compute @workgroup_size(64)
3150
- fn narrowphase`;if(!o.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=o.includes("struct Poly");t&&(o=o.replace(e,ql+`
3151
- `+e)),o=o.replace(e,ln+`
3159
+ `;function wn(o){if(o.includes("fn capsuleOf"))throw Error("Capsule collision kernel installed twice");let e=`@compute @workgroup_size(64)
3160
+ fn narrowphase`;if(!o.includes(e))throw Error("Pinned capsule narrowphase layout changed");let t=o.includes("struct Poly");t&&(o=o.replace(e,nc+`
3161
+ `+e)),o=o.replace(e,An+`
3152
3162
  `+e);let i=t?" else if(isHull(b)){found=capsulePoly(capsuleOf(a),polyOf(b),&sat);}":"",r=t?" else if(isHull(a)){found=capsulePoly(capsuleOf(b),polyOf(a),&sat);}":"";return o.replace("let anySphere = sphereA || sphereB;","let anySphere = sphereA || sphereB || capsuleA || capsuleB;").replace(" if (sphereA && sphereB) {",` let capsuleA=bodies[a].angVel.w == -1.;let capsuleB=bodies[b].angVel.w == -1.;
3153
3163
  if(capsuleA || capsuleB){
3154
3164
  if(capsuleA && capsuleB){found=capsuleCapsule(capsuleOf(a),capsuleOf(b),&sat);}
3155
3165
  else if(capsuleA){if(sphereB){found=capsuleSphere(capsuleOf(a),B.c,B.h.x,&sat);}${i} else{found=capsuleBox(capsuleOf(a),B,bodies[b].rot,&sat);}}
3156
3166
  else{if(sphereA){found=capsuleSphere(capsuleOf(b),A.c,A.h.x,&sat);}${r} else{found=capsuleBox(capsuleOf(b),A,bodies[a].rot,&sat);}
3157
3167
  sat.n=-sat.n;for(var c=0u;c<found.count;c++){let p=found.xA[c];found.xA[c]=found.xB[c];found.xB[c]=p;}}
3158
- } else if (sphereA && sphereB) {`)}var ql=`
3168
+ } else if (sphereA && sphereB) {`)}var nc=`
3159
3169
  fn capPolyAxis(A:Capsule,P:Poly,axis:vec3f,best:ptr<function,f32>,normal:ptr<function,vec3f>)->bool{
3160
3170
  let l=length(axis);if(l<1.e-8){return true;}let n=axis/l;
3161
3171
  let lo=min(dot(A.a,n),dot(A.b,n))-A.r;let hi=max(dot(A.a,n),dot(A.b,n))+A.r;
@@ -3187,7 +3197,7 @@ fn capsulePoly(A:Capsule,P:Poly,sat:ptr<function,Sat>)->Found{
3187
3197
  }
3188
3198
  (*sat).n=n;addFound(&found,points.a+n*A.r,points.b,4u<<24u);return found;
3189
3199
  }
3190
- `;var Oo=`// GPU queries against current poses. Rounded box/hull sweeps test offset faces
3200
+ `;var qo=`// GPU queries against current poses. Rounded box/hull sweeps test offset faces
3191
3201
  // and rounded edges, so expanding a bounding box cannot create corner hits.
3192
3202
  struct Query { origin:vec4f, direction:vec4f }
3193
3203
  struct Params { head:vec4u, flags:vec4u, ignored:array<vec4u,4> }
@@ -3341,9 +3351,88 @@ fn nearest(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||igno
3341
3351
  fn owner(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.x||ignored(id.x)){return;}let h=trace(id.x,queries[id.y]);if(h.t>=0.&&bitcast<u32>(max(0.,h.t))==atomicLoad(&best[id.y*2u])){atomicMin(&best[id.y*2u+1u],id.x);}}
3342
3352
  @compute @workgroup_size(64)
3343
3353
  fn finish(@builtin(global_invocation_id) id:vec3u){if(id.x>=params.head.y){return;}let index=atomicLoad(&best[id.x*2u+1u]);if(index==0xffffffffu){hits[id.x*2u]=vec4f(0.,0.,0.,-1.);return;}let h=trace(index,queries[id.x]);hits[id.x*2u]=vec4f(h.n,max(0.,h.t));hits[id.x*2u+1u]=vec4f(bitcast<f32>(index),0.,0.,0.);}
3344
- `;var Dt=class{constructor(e,t=3){this.solver=e,this.dimension=t,this.jobs=new Set,this.idle=[],this.destroyed=!1;let i=e.device,r=i.createShaderModule({label:`AVBD ${t}D queries`,code:t===2?Oo.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):Oo});this.layout=i.createBindGroupLayout({entries:["read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform","read-only-storage"].map((a,n)=>({binding:n,visibility:GPUShaderStage.COMPUTE,buffer:{type:a}}))});let s=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["nearest","owner","finish"].map(a=>[a,i.createComputePipeline({layout:s,compute:{module:r,entryPoint:a}})])),this.dummy=i.createBuffer({size:16,usage:GPUBufferUsage.STORAGE})}acquire(e){if(this.destroyed)throw Error("GPU queries have been destroyed");let t=this.idle.findIndex(r=>r.capacity>=e),i=t>=0?this.idle.splice(t,1)[0]:this.idle.pop()??{};if(!i.capacity||i.capacity<e){let r=Math.min(65535,Math.max(e,(i.capacity??0)*2,16));for(let[s,a,n]of[["queryBuffer",r*32,GPUBufferUsage.STORAGE],["best",r*8,GPUBufferUsage.STORAGE],["hits",r*32,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["uniform",96,GPUBufferUsage.UNIFORM],["read",r*32,GPUBufferUsage.MAP_READ]])i[s]?.destroy(),i[s]=this.solver.device.createBuffer({size:a,usage:n|GPUBufferUsage.COPY_DST});i.capacity=r,i.buffers=null,i.data=new Float32Array(r*8),i.initial=new Uint32Array(r*2),i.params=new Uint32Array(24)}return this.jobs.add(i),i}release(e){if(e.read.unmap(),!this.destroyed&&e.capacity*104<=2*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.delete(e)}}async cast(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){if(this.destroyed)throw Error("GPU queries have been destroyed");let s=this.solver,a=s.device,n=this.dimension;if(!Number.isInteger(i)||i<0||i>4294967295)throw Error("Query mask must be an unsigned 32-bit integer");if(typeof r!="boolean")throw Error("includeTriggers must be boolean");if(t.length>16||t.some(u=>!Number.isInteger(u)||u<0||u>=s.bodyCount))throw Error("Queries can ignore up to 16 live body indices");if(e.length>Math.min(65535,a.limits.maxComputeWorkgroupsPerDimension))throw Error("Too many queries in one batch");let l=e.map(u=>{let d=Array.from(u.origin),h=Array.from(u.direction),m=u.radius??0,p=u.maxDistance??3e38;if(d.length!==n||h.length!==n||![...d,...h].every(Number.isFinite)||Math.hypot(...h)===0)throw Error(`A query requires ${n} finite components and a nonzero direction`);if(!Number.isFinite(m)||m<0||!Number.isFinite(p)||p<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...h);return{origin:d,direction:h.map(y=>y/b),radius:m,maxDistance:p}});if(!l.length)return[];let c=l.length,f=this.acquire(c);try{let u=f.data;l.forEach((k,z)=>{u.set([...k.origin,...Array(3-n).fill(0),k.maxDistance,...k.direction,...Array(3-n).fill(0),k.radius],z*8)});let d=f.params;d.fill(0),d.set([s.bodyCount,c,t.length,i,r?0:1,n===3||s.filters?1:0,s.sensorsEnabled?1:0,n===2&&s.shapeBuffer?1:0]),d.set(t,8);let h=f.initial;for(let k=0;k<c;k++)h.set([2139095040,4294967295],k*2);let{queryBuffer:m,best:p,hits:b,uniform:y,read:A}=f;a.queue.writeBuffer(m,0,u.buffer,0,c*32),a.queue.writeBuffer(p,0,h.buffer,0,c*8),a.queue.writeBuffer(y,0,d);let w=[s.bodyBuffer,n===3?s.hullStorage:s.shapeBuffer??this.dummy,m,p,b,y,n===3?s.filterStorage:s.filters??this.dummy];(!f.buffers||w.some((k,z)=>k!==f.buffers[z]))&&(f.group=a.createBindGroup({layout:this.layout,entries:w.map((k,z)=>({binding:z,resource:{buffer:k}}))}),f.buffers=w);let g=a.createCommandEncoder(),x=g.beginComputePass();x.setBindGroup(0,f.group);for(let k of["nearest","owner"])x.setPipeline(this.pipes[k]),x.dispatchWorkgroups(Math.ceil(s.bodyCount/64),c);x.setPipeline(this.pipes.finish),x.dispatchWorkgroups(Math.ceil(c/64)),x.end(),g.copyBufferToBuffer(b,0,A,0,c*32),a.queue.submit([g.finish()]),await A.mapAsync(GPUMapMode.READ,0,c*32);let v=A.getMappedRange(0,c*32),B=new Float32Array(v),E=new Uint32Array(v);return l.map((k,z)=>{let M=B[z*8+3];if(M<0)return null;let P=Array.from(B.slice(z*8,z*8+n)),_=k.origin.map((S,C)=>S+M*k.direction[C]);return{index:E[z*8+4],distance:M,normal:P,center:_,point:_.map((S,C)=>S-k.radius*P[C])}})}finally{this.release(f)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.clear(),this.idle.length=0,this.dummy.destroy()}};function is(o,e){let t=this.passConstantCache??={alphas:[]},i=t.iterations!==o||t.colors!==this.colorCap||t.buffer!==this.passBuffer||!this.passGroup;for(let n=0;n<o;n++){let l=typeof e=="function"?e(n):e;t.alphas[n]!==l&&(i=!0),t.alphas[n]=l}if(!i)return;let r=this.colorCap+1,s=Math.max(o*r,1),a=s*256;(s>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"AVBD pass constants",size:a,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=s,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]})),(!t.data||t.data.byteLength<a)&&(t.data=new ArrayBuffer(Math.max(a,(t.data?.byteLength??0)*2)),t.u32=new Uint32Array(t.data),t.f32=new Float32Array(t.data));for(let n=0;n<o;n++)for(let l=0;l<=this.colorCap;l++){let c=(n*r+l)*256/4;t.u32[c]=l,t.f32[c+1]=t.alphas[n]}this.device.queue.writeBuffer(this.passBuffer,0,t.data,0,a),t.iterations=o,t.colors=this.colorCap,t.buffer=this.passBuffer,t.uploads=(t.uploads??0)+1}var Ni=class extends $r{writePassConstants(e,t){is.call(this,e,t)}constructor(e,t,i={}){let r=t.forces.some(u=>Io(u)),s=t.forces.some(u=>{let d=Lo(u);return d&&(Number.isFinite(d.breakStrain)||Number.isFinite(d.breakForce))}),a={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},n=Di({...a,bodyCount:t.bodies.length,constraintCount:t.forces.filter(u=>u instanceof Ae||u instanceof ct).length}),l=n.selected==="custom"?i.shaders.solve:n.selected==="optimized"?ur:n.selected==="points"?Xr:void 0,c=i.broadphase??"auto";if(!["grid","hploc","auto"].includes(c))throw Error("broadphase must be 'grid', 'hploc' or 'auto'");let f=i.bvh?.rebuildInterval??64;if(!Number.isInteger(f)||f<1)throw Error("BVH rebuildInterval must be a positive integer");for(let[u,d]of[["minimumColors",64],["minimumColorRounds",32]]){let h=i[u]??0;if(!Number.isInteger(h)||h<0||h>d)throw Error(`${u} must be an integer between 0 and ${d}`);if(u==="minimumColorRounds"&&h%2)throw Error("minimumColorRounds must be even")}super(e,t,{...i,shaders:{...i.shaders,solve:So(Co(r?Ro(s?Qr(l??ke):l??ke):s?Qr(l??ke):l??ke))}}),this.solverPolicy=a,this.springFracture=s,this.angularConstraints=r,this.customSolveSource=i.shaders?.solve,t.forces.forEach((u,d)=>{let h=Lo(u);h&&this.setSpringMaterial(d,h)}),this.adaptiveScheduling=i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,this.adjacencyPipelines={original:this.pipes.sortAdjacency},this.selectAdjacencyPipeline(),this.solverDecision=n,this.solvePipelineCache=new Map([[n.selected,Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(u=>[u,this.pipes[u]]))]]),this.minimumColors=i.minimumColors??0,this.minimumColorRounds=i.minimumColorRounds??4,this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.broadphaseRequested=c,this.broadphase=c==="hploc"?"hploc":"grid",this.bvhOptions=i.bvh??{},this.bvh=null,this.detailCallback=null,this.detailTiming=null,this.profileErrors=[],this.selectBroadphase(),t.forces.forEach((u,d)=>{let h=Io(u);h&&this.setAngularConstraint(d,h)}),t.bodies.forEach((u,d)=>{es(u)&&this.installCapsule(this.gpuIndex(d))})}installCapsule(e){if(!this.capsulesEnabled){if(this.contactShaders.custom)throw Error("Analytic capsules require the built-in contact kernel");this.capsulesEnabled=!0;let t=this.contactShaders.make;this.contactShaders.make=i=>t(cn(i)),this.contactShaders.make(lr(this.hullShaders??!1))}this.device.queue.writeBuffer(this.bodyBuffer,e*160+156,new Float32Array([-1]))}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new Dt(this),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async sphereCast(e,t,i,r={}){let[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}sphereCastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new Dt(this),this.shapeQueries.cast(e,t)}selectBroadphase(){if(this.broadphaseRequested!=="auto"){this.broadphaseDecision={requested:this.broadphaseRequested,selected:this.broadphase,reason:"Explicit selection"};return}let e=new Float64Array(this.bodyCount),t=0,i=0,r=0;for(let d=0;d<this.bodyCount;d++){let h=this.bodies[d];if(h.radius>0&&(e[t++]=h.radius),h.dynamic){i++;let[m,p,b]=h.size,y=Math.min(m,p,b),A=Math.max(m,p,b),w=m+p+b-y-A;!h.sphere&&!h.hull&&y<=.4*w&&w>=.5*A&&r++}}let s=t?Wr(e.subarray(0,t),t>>1):1,a=this.cellSize/(2*s),n=this.jointCount-this.freeSlots.length,l=i?r/i:0,c=this.bodyCapacity<=16*this.device.limits.maxComputeWorkgroupsPerDimension&&(2*this.bodyCapacity+16*Math.ceil(Math.log2(Math.max(2,this.bodyCapacity)))+32)*32<=this.device.limits.maxStorageBufferBindingSize,f=n>=this.bodyCount&&l>=.25,u=c&&this.bodyCount>=1e4&&(a>=4||f);this.broadphase=u?"hploc":"grid",this.broadphaseDecision={requested:"auto",selected:this.broadphase,bodyCount:this.bodyCount,cellRatio:a,constraintCount:n,thinPlateFraction:l,reason:c?u?f?"Many linked thin plates; the tree avoids empty grid-cell space":"Large grid cells contain many smaller objects":"Grid retained for this body count and size distribution":"The GPU tree exceeds this adapter's limits; the grid is used"}}step(){this.selectAdjacencyPipeline(),this.selectSolver(),this.radiiDirty&&(this.uploadStatics(),this.selectBroadphase()),this.broadphaseRequested==="auto"&&this.broadphaseDecision.constraintCount!==this.jointCount-this.freeSlots.length&&this.selectBroadphase(),this.broadphase==="grid"&&this.bvh&&(this.bvh.destroy(),this.bvh=null),this.broadphase==="hploc"&&(!this.bvh||this.bvh.bodyBuffer!==this.bodyBuffer)&&(this.bvh?.destroy(),this.bvh=new Zr(this.device,this,this.bvhOptions)),this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?Qa(this):super.step()}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.externalBeforeStep?.(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}setRestitution(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a value between 0 and 1");t>0&&!this.restitution&&(this.restitution=new Gi(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return Di({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e){return this.springFracture&&(e=Qr(e)),this.angularConstraints&&(e=Ro(e)),this.sensorsEnabled&&(e=tn(e)),So(Co(e))}setAngularConstraint(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("Angular constraint needs a live slot");let i=gt(t);this.flushPropertyEdits(),this.angularConstraints||(this.angularConstraints=!0,this.rebuildSolvePipelines());let r=new Float32Array(32);r[0]=r[1]=1e3,r[3]=3e38,r.set(i.axisA,24),r.set(i.axisB,28),r[27]=i.type==="motor"?i.speed:i.minAngle,r[31]=i.type==="motor"?i.maxTorque:i.maxAngle,this.device.queue.writeBuffer(this.jointBuffer,e*128,r),this.info[e*4]=i.type==="motor"?3:4,this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info.subarray(e*4,e*4+4)),this.captureAngularFrames([e]),this.wakeAll()}captureAngularFrames(e){if(!e.length)return;this.flushPropertyEdits();let t=this.device;this.angularFramePipeline??=t.createComputePipeline({layout:"auto",compute:{module:t.createShaderModule({code:`const DISPATCH_STRIDE = ${t.limits.maxComputeWorkgroupsPerDimension*64}u;
3345
- ${an.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}),this.runCapture("angular",this.angularFramePipeline,Uint32Array.from(e),e.length)}setMotor(e,{speed:t,maxTorque:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(this.info[e*4]!==3)throw Error("Motor needs an active motor slot");if(t!==void 0&&!Number.isFinite(t))throw Error("Motor speed must be finite");if(i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("maxTorque must be nonnegative");t!==void 0&&Q(this).float(this.jointBuffer,e*32+27,t),i!==void 0&&Q(this).float(this.jointBuffer,e*32+31,i),this.wakeAll()}rebuildSolvePipelines(){this.solvePipelineCache.clear();let e=this.solverDecision.selected,t=e==="custom"?this.customSolveSource:e==="optimized"?ur:e==="points"?Xr:ke,i=this.device.createShaderModule({code:this.solveSource(t)}),r=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]}),s=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(a=>[a,this.device.createComputePipeline({layout:r,compute:{module:i,entryPoint:a}})]));Object.assign(this.pipes,s),this.solvePipelineCache.set(e,s)}setSensor(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||typeof t!="boolean")throw Error("Sensor requires a live body index and boolean flag");if(t&&!this.sensorsEnabled){this.sensorsEnabled=!0,sn(this),this.rebuildSolvePipelines(),this.contactShaders.make(zo(lr(this.hullShaders??!1)));let i=this.contactShaders.make;this.contactShaders.make=a=>i(zo(a));let r=this.device.createShaderModule({code:rn()}),s=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]});for(let a of["degreeContacts","fillContacts"])this.pipes[a]=this.device.createComputePipeline({layout:s,compute:{module:r,entryPoint:a}})}this.sensorsEnabled&&Q(this).float(this.bodyBuffer,e*40+11,t?1:0),this.wakeAll()}selectSolver(){let e=this.jointCount-this.freeSlots.length;this.adaptiveScheduling&&this.schedulingDecision?.dense&&(this.bodyCount<25e4||e>0)&&(this.primalLanes=[...this.defaultPrimalLanes],this.schedulingDecision={...this.schedulingDecision,dense:!1,reason:"Threads per body follow solve-group size"});let t=this.solverDecision;if(t.requested===this.solverPolicy.requested&&t.bodyCount===this.bodyCount&&t.constraintCount===e)return;let i=Di({...this.solverPolicy,bodyCount:this.bodyCount,constraintCount:e});if(i.selected!==t.selected){let r=this.solvePipelineCache.get(i.selected);if(!r){let s=i.selected==="optimized"?ur:i.selected==="points"?Xr:void 0,a=this.device.createShaderModule({code:this.solveSource(s??ke)}),n=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]});r=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(l=>[l,this.device.createComputePipeline({label:`${i.selected} ${l}`,layout:n,compute:{module:a,entryPoint:l}})])),this.solvePipelineCache.set(i.selected,r)}Object.assign(this.pipes,r)}this.solverDecision=i}profileDetailedNextStep(e){if(!this.device.features.has("timestamp-query")||this.timingBusy||this.timingCallback||this.detailCallback)return!1;let t=2*(9+2*this.params.iterations);return(!this.detailTiming||this.detailTiming.count<t)&&(this.releaseAppTiming(),this.detailTiming={count:t,querySet:this.device.createQuerySet({type:"timestamp",count:t}),resolve:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ})}),this.detailCallback=e,!0}captureConstraintFrames(e){if(e.length){this.flushPropertyEdits(),this.framePipeline||(this.framePipeline=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
3346
- ${Ja.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}})),(!this.frameData||this.frameData.length<e.length*4)&&(this.frameData=new Uint32Array(Math.max(e.length*4,(this.frameData?.length??0)*2,64)));for(let t=0;t<e.length;t++)this.frameData[t*4]=e[t].slot,this.frameData[t*4+1]=e[t].spring?1:0;this.runCapture("frame",this.framePipeline,this.frameData.subarray(0,e.length*4),e.length)}}runCapture(e,t,i,r){this.flushEdits?.();let s=this.device,a=(this.captureUploads??={})[e]??={};(!a.buffer||a.buffer.size<i.byteLength)&&(a.buffer?.destroy(),a.buffer=s.createBuffer({size:i.byteLength*2,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.buffers=null),s.queue.writeBuffer(a.buffer,0,i);let n=[this.bodyBuffer,this.jointBuffer,this.infoBuffer,a.buffer];(!a.buffers||n.some((d,h)=>d!==a.buffers[h])||a.bytes!==i.byteLength)&&(a.group=s.createBindGroup({layout:t.getBindGroupLayout(0),entries:n.map((d,h)=>({binding:h,resource:{buffer:d,...h===3?{size:i.byteLength}:{}}}))}),a.buffers=n,a.bytes=i.byteLength);let l=s.createCommandEncoder(),c=l.beginComputePass();c.setPipeline(t),c.setBindGroup(0,a.group);let f=Math.ceil(r/64),u=s.limits.maxComputeWorkgroupsPerDimension;c.dispatchWorkgroups(Math.min(f,u),Math.ceil(f/u)),c.end(),s.queue.submit([l.finish()])}setSpringMaterial(e,t={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4]!==ji)throw Error("Spring material requires an active spring slot");let i=St(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,s]of[["breakStrain",11],["breakForce",15]])Q(this).float(this.jointBuffer,e*32+s,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,s,a){let n=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,f)=>{f[3]=n(s),f[7]=n(a),f[11]=3e38,f.set(i,24),f.set(r,28);let u=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return f[15]=u.reduce((h,m,p)=>h+(m+d[p])**2,0),[1,e,t]});return this.wakeAll(),l}appendJoints(...e){let t=super.appendJoints(...e);return this.wakeAll(),t}appendSprings(...e){let t=super.appendSprings(...e);return this.wakeAll(),t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new li({device:this.device,gpu:this,capacity:this.bodyCapacity,aggregates:e.bodies.map((i,r)=>({rigid:i,index:r}))},t))}wakeAll(){this.sleeping&&(this.sleeping.wakeRequested=!0)}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}addBodies(e){this.flushPropertyEdits();let t=super.addBodies(e);if(t>=0)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)es(e[i])&&this.installCapsule(t+i);if(t>=0&&this.sleeping)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)this.sleeping.register(t+i,e[i].mass,e[i].allowSleep!==!1);return t}rewriteBodies(e,t){this.flushPropertyEdits(),super.rewriteBodies(e,t);for(let i=0;i<e.length;i++)es(t[i])&&this.installCapsule(e[i]);if(this.sleeping)for(let i=0;i<e.length;i++)this.sleeping.register(e[i],t[i].mass,t[i].allowSleep!==!1)}releaseJoints(e){this.flushPropertyEdits(),super.releaseJoints(e),this.wakeAll()}setWorldAnchor(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4+1]!==-1||t?.length!==3||!Array.from(t).every(Number.isFinite))throw Error("A world joint and finite xyz anchor are required");for(let i=0;i<3;i++)Q(this).float(this.jointBuffer,e*32+24+i,t[i]);this.wakeAll()}disableConstraint(e){this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll()}setFilters(e,t,i){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");for(let s=0;s<e.length;s++){if(!Number.isInteger(e[s])||e[s]<0||e[s]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let a of[t[s],i[s]])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=Q(this);for(let s=0;s<e.length;s++)r.uint(this.filterBuffer,e[s]*2,t[s]),r.uint(this.filterBuffer,e[s]*2+1,i[s]);this.wakeAll()}profileNextStep(e){return this.detailCallback?!1:super.profileNextStep(t=>e({...t,broadphase:this.broadphase,bvhRebuilt:this.bvh?.lastRebuilt??!1}))}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}releaseAppTiming(){if(this.detailTiming){for(let e of["querySet","resolve","read"])this.detailTiming[e].destroy();this.detailTiming=null}}destroy(){this.passConstantCache=null;for(let e of Object.values(this.captureUploads??{}))e.buffer.destroy();this.captureUploads=null,this.frameData=null,this.propertyEdits?.destroy(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.sleeping?.dispose(),this.bvh?.destroy(),this.bvh=null,this.timingBusy||this.releaseAppTiming(),super.destroy(),this.solvePipelineCache.clear(),this.adjacencyPipelines={}}adapt(e){if(super.adapt(e),this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.adaptiveScheduling){let t=this.bodyCount>=25e4&&this.jointCount-this.freeSlots.length===0&&e.manifolds>=1.75*this.bodyCount&&!e.overflow&&!e.clashes;this.primalLanes=t?[2**31,2**31,2**31]:[...this.defaultPrimalLanes],this.schedulingDecision={bodyCount:this.bodyCount,manifolds:e.manifolds,dense:t,cachedAdjacency:this.adjacencyCached,reason:t?"Eight threads share each dense body solve":"Threads per body follow solve-group size"}}}selectAdjacencyPipeline(){let e=this.cacheAdjacencyKeys??this.bodyCount>=25e4;this.adjacencyCached!==e&&(e&&!this.adjacencyPipelines.cached&&(this.adjacencyPipelines.cached=this.device.createComputePipeline({label:"Cached adjacency keys",layout:this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]}),compute:{module:this.device.createShaderModule({code:en}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};async function jo(o={}){if(!globalThis.navigator?.gpu)throw Error("WebGPU needs a supported browser on localhost or HTTPS");let e=await navigator.gpu.requestAdapter({powerPreference:o.powerPreference??"high-performance"});if(!e)throw Error("No WebGPU adapter is available");let t={};for(let[r,s]of Object.entries(o.preferredLimits??{})){if(!r.startsWith("max")||e.limits[r]===void 0||!Number.isSafeInteger(s)||s<0)throw Error(`Invalid preferred GPU limit: ${r}`);t[r]=Math.min(s,e.limits[r])}for(let[r,s]of Object.entries(o.requiredLimits??{})){if(e.limits[r]===void 0||!Number.isSafeInteger(s)||s<0)throw Error(`Invalid required GPU limit: ${r}`);let a=e.limits[r];if(r.startsWith("min")&&s<a||!r.startsWith("min")&&s>a)throw Error(`GPU limit ${r} requires ${s}; this adapter supports ${a}`);t[r]=s}let i=await e.requestDevice({requiredLimits:t,requiredFeatures:e.features.has("timestamp-query")?["timestamp-query"]:[]});return{adapter:e,device:i}}var un=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
3354
+ `;var Nt=class{constructor(e,t=3){this.solver=e,this.dimension=t,this.jobs=new Set,this.idle=[],this.destroyed=!1;let i=e.device,r=i.createShaderModule({label:`AVBD ${t}D queries`,code:t===2?qo.replace("const DIM=3u;","const DIM=2u;").replace("const STRIDE=10u;","const STRIDE=6u;"):qo});this.layout=i.createBindGroupLayout({entries:["read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform","read-only-storage"].map((a,n)=>({binding:n,visibility:GPUShaderStage.COMPUTE,buffer:{type:a}}))});let s=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["nearest","owner","finish"].map(a=>[a,i.createComputePipeline({layout:s,compute:{module:r,entryPoint:a}})])),this.dummy=i.createBuffer({size:16,usage:GPUBufferUsage.STORAGE})}acquire(e){if(this.destroyed)throw Error("GPU queries have been destroyed");let t=this.idle.findIndex(r=>r.capacity>=e),i=t>=0?this.idle.splice(t,1)[0]:this.idle.pop()??{};if(!i.capacity||i.capacity<e){let r=Math.min(65535,Math.max(e,(i.capacity??0)*2,16));for(let[s,a,n]of[["queryBuffer",r*32,GPUBufferUsage.STORAGE],["best",r*8,GPUBufferUsage.STORAGE],["hits",r*32,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["uniform",96,GPUBufferUsage.UNIFORM],["read",r*32,GPUBufferUsage.MAP_READ]])i[s]?.destroy(),i[s]=this.solver.device.createBuffer({size:a,usage:n|GPUBufferUsage.COPY_DST});i.capacity=r,i.buffers=null,i.data=new Float32Array(r*8),i.initial=new Uint32Array(r*2),i.params=new Uint32Array(24)}return this.jobs.add(i),i}release(e){if(e.read.unmap(),!this.destroyed&&e.capacity*104<=2*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.delete(e)}}async cast(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){if(this.destroyed)throw Error("GPU queries have been destroyed");let s=this.solver,a=s.device,n=this.dimension;if(!Number.isInteger(i)||i<0||i>4294967295)throw Error("Query mask must be an unsigned 32-bit integer");if(typeof r!="boolean")throw Error("includeTriggers must be boolean");if(t.length>16||t.some(u=>!Number.isInteger(u)||u<0||u>=s.bodyCount))throw Error("Queries can ignore up to 16 live body indices");if(e.length>Math.min(65535,a.limits.maxComputeWorkgroupsPerDimension))throw Error("Too many queries in one batch");let l=e.map(u=>{let d=Array.from(u.origin),p=Array.from(u.direction),m=u.radius??0,h=u.maxDistance??3e38;if(d.length!==n||p.length!==n||![...d,...p].every(Number.isFinite)||Math.hypot(...p)===0)throw Error(`A query requires ${n} finite components and a nonzero direction`);if(!Number.isFinite(m)||m<0||!Number.isFinite(h)||h<0)throw Error("Query radius and maximum distance must be finite and nonnegative");let b=Math.hypot(...p);return{origin:d,direction:p.map(y=>y/b),radius:m,maxDistance:h}});if(!l.length)return[];let c=l.length,f=this.acquire(c);try{let u=f.data;l.forEach((k,z)=>{u.set([...k.origin,...Array(3-n).fill(0),k.maxDistance,...k.direction,...Array(3-n).fill(0),k.radius],z*8)});let d=f.params;d.fill(0),d.set([s.bodyCount,c,t.length,i,r?0:1,n===3||s.filters?1:0,s.sensorsEnabled?1:0,n===2&&s.shapeBuffer?1:0]),d.set(t,8);let p=f.initial;for(let k=0;k<c;k++)p.set([2139095040,4294967295],k*2);let{queryBuffer:m,best:h,hits:b,uniform:y,read:v}=f;a.queue.writeBuffer(m,0,u.buffer,0,c*32),a.queue.writeBuffer(h,0,p.buffer,0,c*8),a.queue.writeBuffer(y,0,d);let B=[s.bodyBuffer,n===3?s.hullStorage:s.shapeBuffer??this.dummy,m,h,b,y,n===3?s.filterStorage:s.filters??this.dummy];(!f.buffers||B.some((k,z)=>k!==f.buffers[z]))&&(f.group=a.createBindGroup({layout:this.layout,entries:B.map((k,z)=>({binding:z,resource:{buffer:k}}))}),f.buffers=B);let g=a.createCommandEncoder(),A=g.beginComputePass();A.setBindGroup(0,f.group);for(let k of["nearest","owner"])A.setPipeline(this.pipes[k]),A.dispatchWorkgroups(Math.ceil(s.bodyCount/64),c);A.setPipeline(this.pipes.finish),A.dispatchWorkgroups(Math.ceil(c/64)),A.end(),g.copyBufferToBuffer(b,0,v,0,c*32),a.queue.submit([g.finish()]),await v.mapAsync(GPUMapMode.READ,0,c*32);let x=v.getMappedRange(0,c*32),w=new Float32Array(x),E=new Uint32Array(x);return l.map((k,z)=>{let M=w[z*8+3];if(M<0)return null;let P=Array.from(w.slice(z*8,z*8+n)),_=k.origin.map((C,S)=>C+M*k.direction[S]);return{index:E[z*8+4],distance:M,normal:P,center:_,point:_.map((C,S)=>C-k.radius*P[S])}})}finally{this.release(f)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["queryBuffer","best","hits","uniform","read"])e[t].destroy();this.jobs.clear(),this.idle.length=0,this.dummy.destroy()}};function as(o,e){let t=this.passConstantCache??={alphas:[]},i=t.iterations!==o||t.colors!==this.colorCap||t.buffer!==this.passBuffer||!this.passGroup;for(let n=0;n<o;n++){let l=typeof e=="function"?e(n):e;t.alphas[n]!==l&&(i=!0),t.alphas[n]=l}if(!i)return;let r=this.colorCap+1,s=Math.max(o*r,1),a=s*256;(s>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"AVBD pass constants",size:a,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=s,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]})),(!t.data||t.data.byteLength<a)&&(t.data=new ArrayBuffer(Math.max(a,(t.data?.byteLength??0)*2)),t.u32=new Uint32Array(t.data),t.f32=new Float32Array(t.data));for(let n=0;n<o;n++)for(let l=0;l<=this.colorCap;l++){let c=(n*r+l)*256/4;t.u32[c]=l,t.f32[c+1]=t.alphas[n]}this.device.queue.writeBuffer(this.passBuffer,0,t.data,0,a),t.iterations=o,t.colors=this.colorCap,t.buffer=this.passBuffer,t.uploads=(t.uploads??0)+1}var Vo=new WeakMap;function ns(o){return Vo.get(o)}function Bn(o,e){e?Vo.set(o,Object.freeze({...e})):Vo.delete(o)}function Wo(o){let e=o.indexOf("/** A partial system per body slot handed between lanes (primal). */"),t=o.indexOf("fn accumulate(",e),i=o.lastIndexOf("/**",t);if(e<0||t<0||i<=e)throw Error("3D primal layout changed; review the compatibility kernel");return o.slice(0,e)+`
3355
+ @compute @workgroup_size(64)
3356
+ fn primal(@builtin(global_invocation_id) gid: vec3u) {
3357
+ let start = color[params.colorStartOffset + pc.color];
3358
+ let count = color[params.colorStartOffset + pc.color + 1u] - start;
3359
+ if (gid.x >= count || !solverStepValid()) { return; }
3360
+ let i = color[params.colorBodiesOffset + start + gid.x];
3361
+ finishBody(i, accumulate(i, 0u, 1u));
3362
+ }
3363
+
3364
+ `+o.slice(i)}function qt(o,e,t){if(o.split(e).length!==2)throw Error("3D contact layout changed; review the compatibility kernel");return o.replace(e,t)}function ls(o,e,t){let i=new RegExp(`struct ${e} \\{[^}]*\\}`,"g");if([...o.matchAll(i)].length!==1)throw Error(`Expected one 3D ${e} layout`);return o.replace(i,t)}function Cn(o,e,t){let i=`fn ${e}(`;if(o.split(i).length!==2)throw Error(`Expected one 3D ${e} function`);let r=o.indexOf(i),a=o.indexOf("{",r)+1,n=1;for(;a<o.length&&n;a++)o[a]==="{"&&n++,o[a]==="}"&&n--;if(n)throw Error(`Incomplete 3D ${e} function`);return o.slice(0,r)+t(o.slice(r,a))+o.slice(a)}function Ho(o){o=ls(o,"PairBody",`struct PairBody {
3365
+ ids: vec4u,
3366
+ rot: vec4f,
3367
+ dLin: vec4f,
3368
+ dAng: vec4f,
3369
+ }`),o=qt(o,"return PairBody(i, rot, bodies[i].pos.xyz - bodies[i].initialPos.xyz, qsub(rot, bodies[i].initialRot));","return PairBody(vec4u(i, 0u, 0u, 0u), rot, vec4f(bodies[i].pos.xyz - bodies[i].initialPos.xyz, 0.0), vec4f(qsub(rot, bodies[i].initialRot), 0.0));"),o=o.replace(/\b(A|B)\.index\b/g,"$1.ids.x").replace(/\b(A|B)\.(dLin|dAng)\b/g,"$1.$2.xyz"),o=ls(o,"Contact",`struct Contact {
3370
+ anchorA: vec4u, // xyz: float bits, w: integer feature key
3371
+ anchorB: vec4f, // xyz: anchor, w: normal error
3372
+ penalty: vec4f, // xyz: penalties, w: first tangent error
3373
+ force: vec4f, // xyz: forces, w: second tangent error
3374
+ }`);let e={rA:"bitcast<vec3f>(k.anchorA.xyz)",rB:"k.anchorB.xyz",key:"k.anchorA.w",c0x:"k.anchorB.w",c0y:"k.penalty.w",c0z:"k.force.w",pen:"k.penalty.xyz",lam:"k.force.xyz"};for(let t of["evalContact","accumulate","dualManifold"])o=Cn(o,t,i=>i.replace(/\bk\.(rA|rB|key|c0x|c0y|c0z|pen|lam)\b/g,(r,s)=>e[s]));return o=qt(o,"contacts[c].lam = e.F;","contacts[c].force = vec4f(e.F, k.force.w);"),o=qt(o,"contacts[c].key =","contacts[c].anchorA.w ="),o=qt(o,"contacts[c].pen = pen;","contacts[c].penalty = vec4f(pen, k.penalty.w);"),o=Cn(o,"evalContact",t=>{t=qt(t,` var e: ContactEval;
3375
+ `,""),t=qt(t," e.rAW ="," let rAW ="),t=qt(t," e.rBW ="," let rBW ="),t=t.replaceAll("e.rAW","rAW").replaceAll("e.rBW","rBW"),t=qt(t," e.C ="," let C =");let i=t.indexOf(" var F ="),r=t.indexOf(" return e;",i);if(i<0||r<0)throw Error("Expected 3D contact force projection");return t.slice(0,i)+` let raw = k.penalty.xyz * C + k.force.xyz;
3376
+ let normalForce = select(0.0, raw.x, raw.x < 0.0);
3377
+ let bounds = abs(normalForce) * friction;
3378
+ let frictionScale = length(raw.yz);
3379
+ var scale = 1.0;
3380
+ if (frictionScale > bounds && frictionScale > 0.0) {
3381
+ scale = bounds / frictionScale;
3382
+ }
3383
+ let force = vec3f(normalForce, raw.yz * scale);
3384
+ return ContactEval(vec4f(rAW, 0.0), vec4f(rBW, 0.0),
3385
+ vec4f(C, 0.0), vec4f(force, 0.0), vec4f(frictionScale, bounds, 0.0, 0.0));
3386
+ }`}),o=ls(o,"ContactEval",`struct ContactEval {
3387
+ rAW: vec4f,
3388
+ rBW: vec4f,
3389
+ C: vec4f,
3390
+ F: vec4f,
3391
+ limits: vec4f, // x: tangent force length, y: Coulomb bound
3392
+ }`),o=o.replace(/\b(ev|e)\.(rAW|rBW|C|F)\b/g,"$1.$2.xyz").replace(/\be\.frictionScale\b/g,"e.limits.x").replace(/\be\.bounds\b/g,"e.limits.y"),o=ls(o,"PassConstants","struct PassConstants { data: vec4u, }"),o.replaceAll("pc.color","pc.data.x").replaceAll("pc.alpha","bitcast<f32>(pc.data.y)")}function Jo(o){if(!o.includes("struct Poly"))return o;let e=`@compute @workgroup_size(64)
3393
+ fn narrowphase(@builtin(global_invocation_id) gid: vec3u)`;if(o.split(e).length!==2)throw Error("3D hull narrowphase changed; review the compatibility kernel");return o.replace(e,"fn narrowphasePair(gid: vec3u)")+`
3394
+ @compute @workgroup_size(32)
3395
+ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
3396
+ for (var offset = 0u; offset < 2u; offset++) {
3397
+ narrowphasePair(vec3u(gid.x * 2u + offset, 0u, 0u));
3398
+ }
3399
+ }
3400
+ `}var Vt=class extends Zr{writePassConstants(e,t){as.call(this,e,t)}constructor(e,t,i={}){let r=ns(e),s=i.scalarPrimal??r?.scalarPrimal??!1,a=i.portableContactMath??r?.portableContactMath??!1,n=t.forces.some(b=>Uo(b)),l=t.forces.some(b=>{let y=Oo(b);return y&&(Number.isFinite(y.breakStrain)||Number.isFinite(y.breakForce))}),c={requested:i.solverMode??"auto",contactScheduling:i.contactScheduling??"manifolds",custom:!!i.shaders?.solve},f=qi({...c,bodyCount:t.bodies.length,constraintCount:t.forces.filter(b=>b instanceof we||b instanceof ct).length}),u=f.selected==="custom"?i.shaders.solve:f.selected==="optimized"?pr:f.selected==="points"?es:void 0,d=r?.broadphase??i.broadphase??"auto";if(!["grid","hploc","auto"].includes(d))throw Error("broadphase must be 'grid', 'hploc' or 'auto'");let p=i.bvh?.rebuildInterval??64;if(!Number.isInteger(p)||p<1)throw Error("BVH rebuildInterval must be a positive integer");for(let[b,y]of[["minimumColors",64],["minimumColorRounds",32]]){let v=i[b]??0;if(!Number.isInteger(v)||v<0||v>y)throw Error(`${b} must be an integer between 0 and ${y}`);if(b==="minimumColorRounds"&&v%2)throw Error("minimumColorRounds must be even")}let m=zo(Mo(n?No(l?is(u??be):u??be):l?is(u??be):u??be));s&&(m=Wo(m)),a&&(m=Ho(m));let h=a&&!i.shaders?.contacts&&i.hulls!==!1&&e.limits.maxStorageBuffersPerShaderStage>=9&&t.bodies.some(b=>Dr(b));if(super(e,t,{...i,shaders:{...i.shaders,contacts:h?Jo(ui(!0)):i.shaders?.contacts,solve:m}}),this.solverPolicy=c,this.scalarPrimal=s,this.portableContactMath=a,s&&(this.primalLanes=[1,1,1]),this.dispatchIsolation=r?.dispatchIsolation??!1,!i.shaders?.contacts){h&&(this.contactShaders.custom=!1,this.hullShaders=!0);let b=this.contactShaders.make;this.contactShaders.make=y=>b(a?Jo(Go(y)):Go(y)),this.contactShaders.make(ui(this.hullShaders??!1)),this.pipes.updateRefs=e.createComputePipeline({label:"Portable contact references",layout:e.createPipelineLayout({bindGroupLayouts:[this.layouts.refs]}),compute:{module:e.createShaderModule({code:gn(Yr)}),entryPoint:"updateRefs"}})}this.springFracture=l,this.angularConstraints=n,this.customSolveSource=i.shaders?.solve,t.forces.forEach((b,y)=>{let v=Oo(b);v&&this.setSpringMaterial(y,v)}),this.adaptiveScheduling=s?!1:i.adaptiveScheduling??!0,this.defaultPrimalLanes=[...this.primalLanes],this.cacheAdjacencyKeys=i.cacheAdjacencyKeys,this.adjacencyPipelines={original:this.pipes.sortAdjacency},this.selectAdjacencyPipeline(),this.solverDecision=f,this.solvePipelineCache=new Map([[f.selected,Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(b=>[b,this.pipes[b]]))]]),this.minimumColors=i.minimumColors??0,this.minimumColorRounds=i.minimumColorRounds??4,this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.broadphaseRequested=d,this.broadphase=d==="hploc"?"hploc":"grid",this.bvhOptions=i.bvh??{},this.bvh=null,this.detailCallback=null,this.detailTiming=null,this.profileErrors=[],this.selectBroadphase(),t.forces.forEach((b,y)=>{let v=Uo(b);v&&this.setAngularConstraint(y,v)}),t.bodies.forEach((b,y)=>{os(b)&&this.installCapsule(this.gpuIndex(y))})}installCapsule(e){if(!this.capsulesEnabled){if(this.contactShaders.custom)throw Error("Analytic capsules require the built-in contact kernel");this.capsulesEnabled=!0;let t=this.contactShaders.make;this.contactShaders.make=i=>t(wn(i)),this.contactShaders.make(ui(this.hullShaders??!1))}this.device.queue.writeBuffer(this.bodyBuffer,e*160+156,new Float32Array([-1]))}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new Nt(this),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async sphereCast(e,t,i,r={}){let[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}sphereCastAll(e,t={}){return this.flushPropertyEdits(),this.shapeQueries??=new Nt(this),this.shapeQueries.cast(e,t)}selectBroadphase(){if(this.broadphaseRequested!=="auto"){this.broadphaseDecision={requested:this.broadphaseRequested,selected:this.broadphase,reason:"Explicit selection"};return}let e=new Float64Array(this.bodyCount),t=0,i=0,r=0;for(let d=0;d<this.bodyCount;d++){let p=this.bodies[d];if(p.radius>0&&(e[t++]=p.radius),p.dynamic){i++;let[m,h,b]=p.size,y=Math.min(m,h,b),v=Math.max(m,h,b),B=m+h+b-y-v;!p.sphere&&!p.hull&&y<=.4*B&&B>=.5*v&&r++}}let s=t?Qr(e.subarray(0,t),t>>1):1,a=this.cellSize/(2*s),n=this.jointCount-this.freeSlots.length,l=i?r/i:0,c=this.bodyCapacity<=16*this.device.limits.maxComputeWorkgroupsPerDimension&&(2*this.bodyCapacity+16*Math.ceil(Math.log2(Math.max(2,this.bodyCapacity)))+32)*32<=this.device.limits.maxStorageBufferBindingSize,f=n>=this.bodyCount&&l>=.25,u=c&&this.bodyCount>=1e4&&(a>=4||f);this.broadphase=u?"hploc":"grid",this.broadphaseDecision={requested:"auto",selected:this.broadphase,bodyCount:this.bodyCount,cellRatio:a,constraintCount:n,thinPlateFraction:l,reason:c?u?f?"Many linked thin plates; the tree avoids empty grid-cell space":"Large grid cells contain many smaller objects":"Grid retained for this body count and size distribution":"The GPU tree exceeds this adapter's limits; the grid is used"}}step(){this.selectAdjacencyPipeline(),this.selectSolver(),this.radiiDirty&&(this.uploadStatics(),this.selectBroadphase()),this.broadphaseRequested==="auto"&&this.broadphaseDecision.constraintCount!==this.jointCount-this.freeSlots.length&&this.selectBroadphase(),this.broadphase==="grid"&&this.bvh&&(this.bvh.destroy(),this.bvh=null),this.broadphase==="hploc"&&(!this.bvh||this.bvh.bodyBuffer!==this.bodyBuffer)&&(this.bvh?.destroy(),this.bvh=new ss(this.device,this,this.bvhOptions)),this.dispatchIsolation||this.bvh||this.detailCallback||this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending||this.externalBeforeStep?un(this):super.step()}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.externalBeforeStep?.(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e),this.externalAfterStep?.(e)}setRestitution(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a value between 0 and 1");t>0&&!this.restitution&&(this.restitution=new Vi(this)),this.restitution?.set(e,t),this.wakeAll()}setSolverMode(e){return qi({...this.solverPolicy,requested:e,bodyCount:this.bodyCount}),this.solverPolicy.requested=e,this.selectSolver(),this.solverDecision}solveSource(e){return this.springFracture&&(e=is(e)),this.angularConstraints&&(e=No(e)),this.sensorsEnabled&&(e=pn(e)),e=zo(Mo(e)),this.scalarPrimal&&(e=Wo(e)),this.portableContactMath&&(e=Ho(e)),e}setAngularConstraint(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount)throw Error("Angular constraint needs a live slot");let i=gt(t);this.flushPropertyEdits(),this.angularConstraints||(this.angularConstraints=!0,this.rebuildSolvePipelines());let r=new Float32Array(32);r[0]=r[1]=1e3,r[3]=3e38,r.set(i.axisA,24),r.set(i.axisB,28),r[27]=i.type==="motor"?i.speed:i.minAngle,r[31]=i.type==="motor"?i.maxTorque:i.maxAngle,this.device.queue.writeBuffer(this.jointBuffer,e*128,r),this.info[e*4]=i.type==="motor"?3:4,this.device.queue.writeBuffer(this.infoBuffer,e*16,this.info.subarray(e*4,e*4+4)),this.captureAngularFrames([e]),this.wakeAll()}captureAngularFrames(e){if(!e.length)return;this.flushPropertyEdits();let t=this.device;this.angularFramePipeline??=t.createComputePipeline({layout:"auto",compute:{module:t.createShaderModule({code:`const DISPATCH_STRIDE = ${t.limits.maxComputeWorkgroupsPerDimension*64}u;
3401
+ ${vn.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}}),this.runCapture("angular",this.angularFramePipeline,Uint32Array.from(e),e.length)}setMotor(e,{speed:t,maxTorque:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(this.info[e*4]!==3)throw Error("Motor needs an active motor slot");if(t!==void 0&&!Number.isFinite(t))throw Error("Motor speed must be finite");if(i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("maxTorque must be nonnegative");t!==void 0&&Q(this).float(this.jointBuffer,e*32+27,t),i!==void 0&&Q(this).float(this.jointBuffer,e*32+31,i),this.wakeAll()}rebuildSolvePipelines(){this.solvePipelineCache.clear();let e=this.solverDecision.selected,t=e==="custom"?this.customSolveSource:e==="optimized"?pr:e==="points"?es:be,i=this.device.createShaderModule({code:this.solveSource(t)}),r=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]}),s=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(a=>[a,this.device.createComputePipeline({layout:r,compute:{module:i,entryPoint:a}})]));Object.assign(this.pipes,s),this.solvePipelineCache.set(e,s)}setSensor(e,t){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount||typeof t!="boolean")throw Error("Sensor requires a live body index and boolean flag");if(t&&!this.sensorsEnabled){this.sensorsEnabled=!0,bn(this),this.rebuildSolvePipelines(),this.contactShaders.make(Do(ui(this.hullShaders??!1)));let i=this.contactShaders.make;this.contactShaders.make=a=>i(Do(a));let r=this.device.createShaderModule({code:mn()}),s=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]});for(let a of["degreeContacts","fillContacts"])this.pipes[a]=this.device.createComputePipeline({layout:s,compute:{module:r,entryPoint:a}})}this.sensorsEnabled&&Q(this).float(this.bodyBuffer,e*40+11,t?1:0),this.wakeAll()}selectSolver(){let e=this.jointCount-this.freeSlots.length;this.adaptiveScheduling&&this.schedulingDecision?.dense&&(this.bodyCount<25e4||e>0)&&(this.primalLanes=[...this.defaultPrimalLanes],this.schedulingDecision={...this.schedulingDecision,dense:!1,reason:"Threads per body follow solve-group size"});let t=this.solverDecision;if(t.requested===this.solverPolicy.requested&&t.bodyCount===this.bodyCount&&t.constraintCount===e)return;let i=qi({...this.solverPolicy,bodyCount:this.bodyCount,constraintCount:e});if(i.selected!==t.selected){let r=this.solvePipelineCache.get(i.selected);if(!r){let s=i.selected==="optimized"?pr:i.selected==="points"?es:void 0,a=this.device.createShaderModule({code:this.solveSource(s??be)}),n=this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.solve,this.layouts.pass]});r=Object.fromEntries(["warmStartJoints","warmStartBodies","primal","dual","updateVelocities"].map(l=>[l,this.device.createComputePipeline({label:`${i.selected} ${l}`,layout:n,compute:{module:a,entryPoint:l}})])),this.solvePipelineCache.set(i.selected,r)}Object.assign(this.pipes,r)}this.solverDecision=i}profileDetailedNextStep(e){if(!this.device.features.has("timestamp-query")||this.timingBusy||this.timingCallback||this.detailCallback)return!1;let t=2*(9+2*this.params.iterations);return(!this.detailTiming||this.detailTiming.count<t)&&(this.releaseAppTiming(),this.detailTiming={count:t,querySet:this.device.createQuerySet({type:"timestamp",count:t}),resolve:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:this.device.createBuffer({size:t*8,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ})}),this.detailCallback=e,!0}captureConstraintFrames(e){if(e.length){this.flushPropertyEdits(),this.framePipeline||(this.framePipeline=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
3402
+ ${on.replaceAll("id.x","(id.x+id.y*DISPATCH_STRIDE)")}`}),entryPoint:"capture"}})),(!this.frameData||this.frameData.length<e.length*4)&&(this.frameData=new Uint32Array(Math.max(e.length*4,(this.frameData?.length??0)*2,64)));for(let t=0;t<e.length;t++)this.frameData[t*4]=e[t].slot,this.frameData[t*4+1]=e[t].spring?1:0;this.runCapture("frame",this.framePipeline,this.frameData.subarray(0,e.length*4),e.length)}}runCapture(e,t,i,r){this.flushEdits?.();let s=this.device,a=(this.captureUploads??={})[e]??={};(!a.buffer||a.buffer.size<i.byteLength)&&(a.buffer?.destroy(),a.buffer=s.createBuffer({size:i.byteLength*2,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),a.buffers=null),s.queue.writeBuffer(a.buffer,0,i);let n=[this.bodyBuffer,this.jointBuffer,this.infoBuffer,a.buffer];(!a.buffers||n.some((d,p)=>d!==a.buffers[p])||a.bytes!==i.byteLength)&&(a.group=s.createBindGroup({layout:t.getBindGroupLayout(0),entries:n.map((d,p)=>({binding:p,resource:{buffer:d,...p===3?{size:i.byteLength}:{}}}))}),a.buffers=n,a.bytes=i.byteLength);let l=s.createCommandEncoder(),c=l.beginComputePass();c.setPipeline(t),c.setBindGroup(0,a.group);let f=Math.ceil(r/64),u=s.limits.maxComputeWorkgroupsPerDimension;c.dispatchWorkgroups(Math.min(f,u),Math.ceil(f/u)),c.end(),s.queue.submit([l.finish()])}setSpringMaterial(e,t={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4]!==Ui)throw Error("Spring material requires an active spring slot");let i=Pt(t);!this.springFracture&&(Number.isFinite(i.breakStrain)||Number.isFinite(i.breakForce))&&(this.springFracture=!0,this.rebuildSolvePipelines());for(let[r,s]of[["breakStrain",11],["breakForce",15]])Q(this).float(this.jointBuffer,e*32+s,i[r]===1/0?0:i[r]);this.wakeAll()}appendConstraints(...e){return this.flushPropertyEdits(),super.appendConstraints(...e)}appendJoint(e,t,i,r,s,a){let n=c=>c===1/0?3e38:c,[l]=this.appendConstraints(1,(c,f)=>{f[3]=n(s),f[7]=n(a),f[11]=3e38,f.set(i,24),f.set(r,28);let u=e>=0?this.bodies[e].size:[0,0,0],d=this.bodies[t].size;return f[15]=u.reduce((p,m,h)=>p+(m+d[h])**2,0),[1,e,t]});return this.wakeAll(),l}appendJoints(...e){let t=super.appendJoints(...e);return this.wakeAll(),t}appendSprings(...e){let t=super.appendSprings(...e);return this.wakeAll(),t}enableSleeping(e,t={}){this.sleeping||(this.sleeping=new hi({device:this.device,gpu:this,capacity:this.bodyCapacity,aggregates:e.bodies.map((i,r)=>({rigid:i,index:r}))},t))}wakeAll(){this.sleeping&&(this.sleeping.wakeRequested=!0)}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}addBodies(e){this.flushPropertyEdits();let t=super.addBodies(e);if(t>=0)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)os(e[i])&&this.installCapsule(t+i);if(t>=0&&this.sleeping)for(let i=0;i<Math.min(e.length,this.bodyCount-t);i++)this.sleeping.register(t+i,e[i].mass,e[i].allowSleep!==!1);return t}rewriteBodies(e,t){this.flushPropertyEdits(),super.rewriteBodies(e,t);for(let i=0;i<e.length;i++)os(t[i])&&this.installCapsule(e[i]);if(this.sleeping)for(let i=0;i<e.length;i++)this.sleeping.register(e[i],t[i].mass,t[i].allowSleep!==!1)}releaseJoints(e){this.flushPropertyEdits(),super.releaseJoints(e),this.wakeAll()}setWorldAnchor(e,t){if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.info[e*4+1]!==-1||t?.length!==3||!Array.from(t).every(Number.isFinite))throw Error("A world joint and finite xyz anchor are required");for(let i=0;i<3;i++)Q(this).float(this.jointBuffer,e*32+24+i,t[i]);this.wakeAll()}disableConstraint(e){this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll()}setFilters(e,t,i){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");for(let s=0;s<e.length;s++){if(!Number.isInteger(e[s])||e[s]<0||e[s]>=this.bodyCount)throw Error("Collision filters require live body indices");for(let a of[t[s],i[s]])if(!Number.isInteger(a)||a<0||a>4294967295)throw Error("Collision masks must be unsigned 32-bit integers")}let r=Q(this);for(let s=0;s<e.length;s++)r.uint(this.filterBuffer,e[s]*2,t[s]),r.uint(this.filterBuffer,e[s]*2+1,i[s]);this.wakeAll()}profileNextStep(e){return this.detailCallback?!1:super.profileNextStep(t=>e({...t,broadphase:this.broadphase,bvhRebuilt:this.bvh?.lastRebuilt??!1}))}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}releaseAppTiming(){if(this.detailTiming){for(let e of["querySet","resolve","read"])this.detailTiming[e].destroy();this.detailTiming=null}}destroy(){this.passConstantCache=null;for(let e of Object.values(this.captureUploads??{}))e.buffer.destroy();this.captureUploads=null,this.frameData=null,this.propertyEdits?.destroy(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.sleeping?.dispose(),this.bvh?.destroy(),this.bvh=null,this.timingBusy||this.releaseAppTiming(),super.destroy(),this.solvePipelineCache.clear(),this.adjacencyPipelines={}}adapt(e){if(super.adapt(e),this.colorCap=Math.max(this.minimumColors,this.colorCap),this.colorRounds=Math.max(this.minimumColorRounds,this.colorRounds),this.adaptiveScheduling){let t=this.bodyCount>=25e4&&this.jointCount-this.freeSlots.length===0&&e.manifolds>=1.75*this.bodyCount&&!e.overflow&&!e.clashes;this.primalLanes=t?[2**31,2**31,2**31]:[...this.defaultPrimalLanes],this.schedulingDecision={bodyCount:this.bodyCount,manifolds:e.manifolds,dense:t,cachedAdjacency:this.adjacencyCached,reason:t?"Eight threads share each dense body solve":"Threads per body follow solve-group size"}}}selectAdjacencyPipeline(){let e=this.cacheAdjacencyKeys??this.bodyCount>=25e4;this.adjacencyCached!==e&&(e&&!this.adjacencyPipelines.cached&&(this.adjacencyPipelines.cached=this.device.createComputePipeline({label:"Cached adjacency keys",layout:this.device.createPipelineLayout({bindGroupLayouts:[this.layouts.topo]}),compute:{module:this.device.createShaderModule({code:hn}),entryPoint:"sortAdjacency"}})),this.pipes.sortAdjacency=e?this.adjacencyPipelines.cached:this.adjacencyPipelines.original,this.adjacencyCached=e)}};var $o=new WeakMap,Sn=[{name:"batched-grid",broadphase:"grid",dispatchIsolation:!1},{name:"isolated-grid",broadphase:"grid",dispatchIsolation:!0},{name:"batched-hploc",broadphase:"hploc",dispatchIsolation:!1},{name:"isolated-hploc",broadphase:"hploc",dispatchIsolation:!0},{name:"scalar-grid",broadphase:"grid",dispatchIsolation:!1,scalarPrimal:!0},{name:"scalar-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0},{name:"portable-contact-grid",broadphase:"grid",dispatchIsolation:!1,portableContactMath:!0},{name:"portable-contact-isolated-grid",broadphase:"grid",dispatchIsolation:!0,scalarPrimal:!0,portableContactMath:!0}];function kn(o){return $o.has(o)||$o.set(o,cc(o)),$o.get(o)}async function cc(o,e){let t=[];for(let i of Sn){let r=await pc(o,i,e);if(t.push(r),!!r.passed)return Bn(o,{dispatchIsolation:i.dispatchIsolation,scalarPrimal:i.scalarPrimal??!1,portableContactMath:i.portableContactMath??!1,broadphase:i.broadphase==="hploc"?"hploc":void 0}),{...r,selected:i.name,compatibility:i.name!==Sn[0].name,attempts:t}}return{...t[0],selected:null,compatibility:!1,attempts:t}}function uc(o){let e=new jt;return e.iterations=5,o||(e.gravity=0),new De(e,[16,.25,16],0,.6,[0,-.125,0]),new De(e,[1,1,1],1,.6,[-2,o?2:.45,0]),Mi(e,.5,1,.6,[0,o?2.3:.45,0]),Hi(e,.25,1.6,1,.6,[2,o?2.5:.75,0]),e}async function Wt(o,e,t=e.size){let i=o.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST});try{let r=o.createCommandEncoder();return r.copyBufferToBuffer(e,0,i,0,t),o.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ),i.getMappedRange().slice(0)}finally{i.destroy()}}async function fc(o){o.step();let e=await o.readCounters(),t=new Uint32Array(await Wt(o.device,o.argsBuffer)),i=await hc(o),r=new Uint32Array(i),s=new Float32Array(i),a=new Uint32Array(await Wt(o.device,o.adjBuffer)),n=new Uint32Array(await Wt(o.device,o.colorBuffer)),{manifolds:l,contacts:c}=o.contactStorage,f=await Wt(o.device,l,Math.max(32,e.manifolds*32)),u=new Uint32Array(f),d=new Float32Array(f),p=new Float32Array(await Wt(o.device,c,Math.max(64,e.contacts*64))),m=await o.readBodies(),h=e.manifolds>=3?await dc(o):[],b=h.some(({contactEvaluation:y})=>y.returnedForce===0&&y.independentForce<-1e-5);return{expectedFloorPairs:3,counters:e,pairs:e.pairs?Array.from(await o.readPairs()):[],narrowphaseWorkgroups:Array.from(t.subarray(0,3)),solver:{scalarPrimal:o.scalarPrimal,portableContactMath:o.portableContactMath,lanes:[...o.primalLanes],contactWorkgroups:Array.from(t.subarray(3,6)),dualWorkgroups:Array.from(t.subarray(9,12)),colorWorkgroups:Array.from(t.subarray(12,12+3*o.colorCap)),adjacencyStarts:Array.from(a.subarray(0,5)),adjacencyEntries:Array.from(a.subarray(2*o.bodyCapacity+1,2*o.bodyCapacity+1+a[4])),colorStarts:Array.from(n.subarray(2*o.bodyCapacity,2*o.bodyCapacity+o.colorCap+1)),colorBodies:Array.from(n.subarray(2*o.bodyCapacity+65,2*o.bodyCapacity+68)),positionsAfterOverlap:[1,2,3].map(y=>Array.from(m.subarray(y*40,y*40+3))),manifolds:Array.from({length:e.manifolds},(y,v)=>({ids:Array.from(u.subarray(v*8,v*8+4)),normalAndFriction:Array.from(d.subarray(v*8+4,v*8+8))})),contacts:Array.from({length:e.contacts},(y,v)=>({anchorA:Array.from(p.subarray(v*16,v*16+3)),anchorB:Array.from(p.subarray(v*16+4,v*16+7)),gap:p[v*16+7],penalty:Array.from(p.subarray(v*16+8,v*16+11)),force:Array.from(p.subarray(v*16+12,v*16+15))})),serialSystems:h,failure:b?"contact-evaluation":null},filters:Array.from(new Uint32Array(await Wt(o.device,o.filterBuffer))),params:{bodyCount:r[0],largeCount:r[1],noCollideCount:r[2],pairCapacity:r[3],contactCapacity:r[4],dt:s[5],cellSize:s[6],maxSmallRadius:s[7],up:Array.from(s.subarray(8,11)),alpha:s[11]},stage:e.pairs<3?"collision-pairs":e.manifolds<3?"contact-generation":"contacts-created"}}async function dc(o){let e=o.portableContactMath?"bitcast<f32>(pc.data.y)":"pc.alpha",t=o.portableContactMath?"k.anchorB.w":"k.c0x",i=o.portableContactMath?"k.penalty.x":"k.pen.x",r=o.portableContactMath?"k.force.x":"k.lam.x",s=o.device.createShaderModule({code:o.solveSource(be)+`
3403
+ @compute @workgroup_size(1) fn inspectSystems(@builtin(global_invocation_id) gid: vec3u) {
3404
+ let acc = accumulate(gid.x + 1u, 0u, 1u);
3405
+ joints[gid.x].penLin = vec4f(acc.lin[0][0], acc.lin[1][1], acc.lin[2][2], 0.0);
3406
+ joints[gid.x].penAng = vec4f(acc.ang[0][0], acc.ang[1][1], acc.ang[2][2], 0.0);
3407
+ joints[gid.x].c0Lin = vec4f(acc.rLin, 0.0);
3408
+ joints[gid.x].c0Ang = vec4f(acc.rAng, 0.0);
3409
+ let mf = manifolds[gid.x];
3410
+ let k = contacts[mf.ids.z];
3411
+ let A = pairBody(mf.ids.x); let B = pairBody(mf.ids.y);
3412
+ let basis = orthonormal(mf.geo.xyz);
3413
+ let result = evalContact(k, basis, mf.geo.w, A, B, ${e});
3414
+ // Independently recompute the normal row from scalar inputs. Compare input
3415
+ // reads, pass alpha, the returned error and the returned clamped force.
3416
+ let dA = bodies[mf.ids.x].pos.xyz - bodies[mf.ids.x].initialPos.xyz;
3417
+ let dB = bodies[mf.ids.y].pos.xyz - bodies[mf.ids.y].initialPos.xyz;
3418
+ let wA = qsub(bodies[mf.ids.x].rot, bodies[mf.ids.x].initialRot);
3419
+ let wB = qsub(bodies[mf.ids.y].rot, bodies[mf.ids.y].initialRot);
3420
+ let expectedC = ${t} * (1.0 - ${e}) + dot(basis[0], dA - dB)
3421
+ + dot(cross(result.rAW.xyz, basis[0]), wA)
3422
+ - dot(cross(result.rBW.xyz, basis[0]), wB);
3423
+ let raw = ${i} * expectedC + ${r};
3424
+ joints[gid.x].rA = vec4f(${t}, ${e}, result.C.x, result.F.x);
3425
+ joints[gid.x].rB = vec4f(${i}, ${r}, expectedC, min(raw, 0.0));
3426
+ }`}),a=o.device.createComputePipeline({layout:o.device.createPipelineLayout({bindGroupLayouts:[o.layouts.solve,o.layouts.pass]}),compute:{module:s,entryPoint:"inspectSystems"}}),n=o.device.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(a),l.setBindGroup(0,o.groups.solve[1-o.parity]),l.setBindGroup(1,o.passGroup,[0]),l.dispatchWorkgroups(3),l.end(),o.device.queue.submit([n.finish()]);let c=new Float32Array(await Wt(o.device,o.jointBuffer,384));return[0,1,2].map(f=>({linearDiagonal:Array.from(c.subarray(f*32,f*32+3)),angularDiagonal:Array.from(c.subarray(f*32+4,f*32+7)),linearForce:Array.from(c.subarray(f*32+16,f*32+19)),angularForce:Array.from(c.subarray(f*32+20,f*32+23)),contactEvaluation:{inputGap:c[f*32+24],passAlpha:c[f*32+25],returnedError:c[f*32+26],returnedForce:c[f*32+27],inputPenalty:c[f*32+28],inputForce:c[f*32+29],independentError:c[f*32+30],independentForce:c[f*32+31]}}))}async function hc(o){let{device:e}=o,t=e.createBuffer({size:48,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC});try{let i=e.createComputePipeline({layout:"auto",compute:{module:e.createShaderModule({code:ae+`
3427
+ @group(0) @binding(0) var<uniform> p: Params;
3428
+ @group(0) @binding(1) var<storage,read_write> out: array<u32>;
3429
+ @compute @workgroup_size(1) fn inspectParams() {
3430
+ out[0]=p.bodyCount; out[1]=p.largeCount; out[2]=p.noCollideCount;
3431
+ out[3]=p.pairCapacity; out[4]=p.contactCapacity;
3432
+ out[5]=bitcast<u32>(p.dt); out[6]=bitcast<u32>(p.cellSize); out[7]=bitcast<u32>(p.maxSmallRadius);
3433
+ out[8]=bitcast<u32>(p.up.x); out[9]=bitcast<u32>(p.up.y); out[10]=bitcast<u32>(p.up.z);
3434
+ out[11]=bitcast<u32>(p.alpha);
3435
+ }`}),entryPoint:"inspectParams"}}),r=e.createBindGroup({layout:i.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:o.paramsBuffer}},{binding:1,resource:{buffer:t}}]}),s=e.createCommandEncoder(),a=s.beginComputePass();return a.setPipeline(i),a.setBindGroup(0,r),a.dispatchWorkgroups(1),a.end(),e.queue.submit([s.finish()]),await Wt(e,t)}finally{t.destroy()}}async function pc(o,e,t){let i={mode:e.name,passed:!1,steps:180,cases:[],errors:[],contactCache:"normal-float-generations-v1",diagnosticVersion:4,limits:{storageBindings:o.limits.maxStorageBuffersPerShaderStage,workgroupStorageBytes:o.limits.maxComputeWorkgroupStorageSize,workgroupInvocations:o.limits.maxComputeInvocationsPerWorkgroup,storageBindingBytes:o.limits.maxStorageBufferBindingSize}},r,s=a=>{let n=new Vt(o,uc(a),{spatialSort:!1,bodyCapacity:4,broadphase:e.broadphase,scalarPrimal:e.scalarPrimal??!1,portableContactMath:e.portableContactMath??!1,capacity:{pairs:32,manifolds:32,contacts:256,colors:8}});return n.params.up=[0,1,0],n.dispatchIsolation=e.dispatchIsolation,t?.(n,e,a),n};o.pushErrorScope("validation");try{if(r=s(!1),i.pipeline=await fc(r),i.pipeline.stage!=="contacts-created")i.reason=i.pipeline.stage==="collision-pairs"?"The GPU did not find the three known overlapping floor pairs.":"The GPU found overlapping pairs but did not create all floor contacts.";else{r.destroy(),r=s(!0);let a=0;i.samples=[];for(let l=0;l<i.steps;l++){if(r.step(),(l+1)%30&&l!==39)continue;let c=await r.readCounters();if(i.samples.push({step:l+1,...c}),c.manifolds&&a++,c.overflow||c.clashes)throw Error("Incomplete GPU contact graph")}let n=await r.readBodies();for(let l=1;l<4;l++){let c=l*40,[f,u,d,p]=n.subarray(c+4,c+8),m=l===1?.5*(Math.abs(2*(f*u+p*d))+Math.abs(1-2*(f*f+d*d))+Math.abs(2*(u*d-p*f))):l===2?.5:.25+.55*Math.abs(1-2*(f*f+d*d)),h=n[c+1]-m,b=n.subarray(c,c+40).every(Number.isFinite);i.cases.push({shape:["floor","box","sphere","capsule"][l],passed:b&&h>=-.06&&h<.12,floorGap:h,position:Array.from(n.subarray(c,c+3))})}i.contactSteps=a,i.passed=a>0&&i.cases.every(l=>l.passed),i.passed||(i.reason=i.pipeline.solver.failure==="contact-evaluation"?"The GPU created floor contacts but returned zero force where a push was required.":"The GPU did not keep all three falling shapes on the floor.")}}catch(a){i.errors.push(a.message)}finally{try{let a=await o.popErrorScope();a&&i.errors.push(a.message)}catch(a){i.errors.push(a.message)}r?.destroy()}return i.passed=i.passed&&!i.errors.length,i}async function Xo(o={}){if(!globalThis.navigator?.gpu)throw Error("WebGPU needs a supported browser on localhost or HTTPS");let e=await navigator.gpu.requestAdapter({powerPreference:o.powerPreference??"high-performance"});if(!e)throw Error("No WebGPU adapter is available");let t={};for(let[r,s]of Object.entries(o.preferredLimits??{})){if(!r.startsWith("max")||e.limits[r]===void 0||!Number.isSafeInteger(s)||s<0)throw Error(`Invalid preferred GPU limit: ${r}`);t[r]=Math.min(s,e.limits[r])}for(let[r,s]of Object.entries(o.requiredLimits??{})){if(e.limits[r]===void 0||!Number.isSafeInteger(s)||s<0)throw Error(`Invalid required GPU limit: ${r}`);let a=e.limits[r];if(r.startsWith("min")&&s<a||!r.startsWith("min")&&s>a)throw Error(`GPU limit ${r} requires ${s}; this adapter supports ${a}`);t[r]=s}let i=await e.requestDevice({requiredLimits:t,requiredFeatures:e.features.has("timestamp-query")?["timestamp-query"]:[]});if(o.validate3D)try{await cs(i)}catch(r){throw i.destroy(),r}return{adapter:e,device:i}}async function cs(o){if(ns(o))return o;let e=await kn(o);if(!e.passed)throw Error(`GPU 3D contact check failed: ${e.reason??e.errors.join("; ")}`);return o}var Pn=`struct Body { pos:vec4f,rot:vec4f,initialPos:vec4f,initialRot:vec4f,size:vec4f,moment:vec4f,inertialPos:vec4f,inertialRot:vec4f,velocity:vec4f,angular:vec4f }
3347
3436
  struct Command { index:u32,mask:u32,pad0:u32,pad1:u32,velocity:vec4f,angular:vec4f,impulse:vec4f,torque:vec4f,position:vec4f,rotation:vec4f,pointImpulse:vec4f }
3348
3437
  @group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
3349
3438
  @group(0) @binding(1) var<storage,read> commands:array<Command>;
@@ -3365,7 +3454,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
3365
3454
  if((c.mask&16u)!=0u){b.size.w=0.;b.moment=vec4f(vec3f(0),b.moment.w);b.velocity=vec4f(0);b.angular=vec4f(vec3f(0),b.angular.w);}
3366
3455
  bodies[c.index]=b;
3367
3456
  }
3368
- `;var Ct=class{constructor(e,t,i="linear"){this.solver=e,this.dimension=t,this.components=i==="angular"&&t===2?1:t;let r=e.device,s=t===3?10:6,a=t===3?i==="angular"?9:8:3;this.pipeline=r.createComputePipeline({layout:"auto",compute:{entryPoint:"setVelocities",module:r.createShaderModule({code:`
3457
+ `;var Lt=class{constructor(e,t,i="linear"){this.solver=e,this.dimension=t,this.components=i==="angular"&&t===2?1:t;let r=e.device,s=t===3?10:6,a=t===3?i==="angular"?9:8:3;this.pipeline=r.createComputePipeline({layout:"auto",compute:{entryPoint:"setVelocities",module:r.createShaderModule({code:`
3369
3458
  @group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
3370
3459
  @group(0) @binding(1) var<storage,read> indices:array<u32>;
3371
3460
  @group(0) @binding(2) var<storage,read> velocities:array<f32>;
@@ -3376,7 +3465,7 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
3376
3465
  var v=bodies[base];
3377
3466
  ${this.components===1?"v.z=velocities[i];":`v.x=velocities[i*${t}u];v.y=velocities[i*${t}u+1u];${t===3?"v.z=velocities[i*3u+2u];":""}`}
3378
3467
  bodies[base]=v;
3379
- }`})}})}set(e,t){let i=this.solver,r=i.device,s=e instanceof Uint32Array?e:Uint32Array.from(e),a=t instanceof Float32Array?t:Float32Array.from(t);if(a.length!==s.length*this.components)throw Error(`Provide ${this.components} velocity components per body`);let n=-1;for(let c=0;c<e.length;c++){if(i.liveIndex(e[c]),e[c]<=n)throw Error("Velocity batch indices must be unique and ascending GPU slots");n=e[c]}for(let c of a)if(!Number.isFinite(c))throw Error("Velocities must be finite Float32 values");let l=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(s.byteLength>l||a.byteLength>l)throw Error("Velocity batch exceeds GPU buffer limits");s.length&&(i.wakeAll(),i.flushEdits(),this.submitValidated(s,a))}submitValidated(e,t,i){if(!e.length)return;let r=this.solver,s=r.device,a=Math.min(s.limits.maxStorageBufferBindingSize,s.limits.maxBufferSize);if(e.byteLength>a||t.byteLength>a)throw Error("Velocity batch exceeds GPU buffer limits");for(let[u,d]of[["indices",e],["values",t]])(!this[u]||this[u].size<d.byteLength)&&(this[u]?.destroy(),this[u]=s.createBuffer({size:Math.min(a,d.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),s.queue.writeBuffer(this[u],0,d);let n=i??s.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),(!this.binding||this.boundBody!==r.bodyBuffer||this.boundIndices!==this.indices||this.boundValues!==this.values||this.boundBytes!==t.byteLength)&&(this.binding=s.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.bodyBuffer}},{binding:1,resource:{buffer:this.indices,size:e.byteLength}},{binding:2,resource:{buffer:this.values,size:t.byteLength}}]}),this.boundBody=r.bodyBuffer,this.boundIndices=this.indices,this.boundValues=this.values,this.boundBytes=t.byteLength),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/64),f=s.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,f),Math.ceil(c/f)),l.end(),i||s.queue.submit([n.finish()]),this.lastBatch={bodies:e.length,uploadedBytes:e.byteLength+t.byteLength}}destroy(){this.indices?.destroy(),this.values?.destroy()}},Gt=class{constructor(e,t){this.solver=e,this.dimension=t,this.wordsPerBody=t===3?8:5}set(e,{linear:t,angular:i}={}){let r=this.solver,s=this.dimension,a=s===3?3:1,n=e instanceof Uint32Array?e:Uint32Array.from(e),l=t===void 0?null:t instanceof Float32Array?t:Float32Array.from(t),c=i===void 0?null:i instanceof Float32Array?i:Float32Array.from(i);if(!l&&!c)throw Error("Provide linear and/or angular velocities");if(l&&l.length!==n.length*s)throw Error(`Provide ${s} linear components per body`);if(c&&c.length!==n.length*a)throw Error(`Provide ${a} angular components per body`);let f=-1;for(let h=0;h<e.length;h++){if(r.liveIndex(e[h]),e[h]<=f)throw Error("Velocity batch indices must be unique and ascending GPU slots");f=e[h]}for(let h of[l,c])if(h){for(let m of h)if(!Number.isFinite(m))throw Error("Velocities must be finite Float32 values")}let u=n.length*this.wordsPerBody,d=Math.min(r.device.limits.maxStorageBufferBindingSize,r.device.limits.maxBufferSize);if(u*4>d)throw Error("Motion batch exceeds GPU buffer limits");if(u){(!this.data||this.data.length<u)&&(this.data=new Uint32Array(Math.max(u,(this.data?.length??0)*2)),this.floats=new Float32Array(this.data.buffer));for(let h=0;h<n.length;h++){let m=h*this.wordsPerBody;if(this.data[m]=n[h],this.data[m+1]=(l?1:0)|(c?2:0),l)for(let p=0;p<s;p++)this.floats[m+2+p]=l[h*s+p];if(c)for(let p=0;p<a;p++)this.floats[m+2+s+p]=c[h*a+p]}r.wakeAll(),r.flushEdits(),this.submitValidated(this.data.subarray(0,u))}}submitValidated(e,t){if(!e.length)return;let i=this.solver,r=i.device,s=this.wordsPerBody,a=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(e.byteLength>a)throw Error("Motion batch exceeds GPU buffer limits");(!this.buffer||this.buffer.size<e.byteLength)&&(this.buffer?.destroy(),this.buffer=r.createBuffer({size:Math.min(a,e.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),r.queue.writeBuffer(this.buffer,0,e),this.pipeline??=r.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:r.createShaderModule({code:`
3468
+ }`})}})}set(e,t){let i=this.solver,r=i.device,s=e instanceof Uint32Array?e:Uint32Array.from(e),a=t instanceof Float32Array?t:Float32Array.from(t);if(a.length!==s.length*this.components)throw Error(`Provide ${this.components} velocity components per body`);let n=-1;for(let c=0;c<e.length;c++){if(i.liveIndex(e[c]),e[c]<=n)throw Error("Velocity batch indices must be unique and ascending GPU slots");n=e[c]}for(let c of a)if(!Number.isFinite(c))throw Error("Velocities must be finite Float32 values");let l=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(s.byteLength>l||a.byteLength>l)throw Error("Velocity batch exceeds GPU buffer limits");s.length&&(i.wakeAll(),i.flushEdits(),this.submitValidated(s,a))}submitValidated(e,t,i){if(!e.length)return;let r=this.solver,s=r.device,a=Math.min(s.limits.maxStorageBufferBindingSize,s.limits.maxBufferSize);if(e.byteLength>a||t.byteLength>a)throw Error("Velocity batch exceeds GPU buffer limits");for(let[u,d]of[["indices",e],["values",t]])(!this[u]||this[u].size<d.byteLength)&&(this[u]?.destroy(),this[u]=s.createBuffer({size:Math.min(a,d.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),s.queue.writeBuffer(this[u],0,d);let n=i??s.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),(!this.binding||this.boundBody!==r.bodyBuffer||this.boundIndices!==this.indices||this.boundValues!==this.values||this.boundBytes!==t.byteLength)&&(this.binding=s.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:r.bodyBuffer}},{binding:1,resource:{buffer:this.indices,size:e.byteLength}},{binding:2,resource:{buffer:this.values,size:t.byteLength}}]}),this.boundBody=r.bodyBuffer,this.boundIndices=this.indices,this.boundValues=this.values,this.boundBytes=t.byteLength),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/64),f=s.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,f),Math.ceil(c/f)),l.end(),i||s.queue.submit([n.finish()]),this.lastBatch={bodies:e.length,uploadedBytes:e.byteLength+t.byteLength}}destroy(){this.indices?.destroy(),this.values?.destroy()}},Ht=class{constructor(e,t){this.solver=e,this.dimension=t,this.wordsPerBody=t===3?8:5}set(e,{linear:t,angular:i}={}){let r=this.solver,s=this.dimension,a=s===3?3:1,n=e instanceof Uint32Array?e:Uint32Array.from(e),l=t===void 0?null:t instanceof Float32Array?t:Float32Array.from(t),c=i===void 0?null:i instanceof Float32Array?i:Float32Array.from(i);if(!l&&!c)throw Error("Provide linear and/or angular velocities");if(l&&l.length!==n.length*s)throw Error(`Provide ${s} linear components per body`);if(c&&c.length!==n.length*a)throw Error(`Provide ${a} angular components per body`);let f=-1;for(let p=0;p<e.length;p++){if(r.liveIndex(e[p]),e[p]<=f)throw Error("Velocity batch indices must be unique and ascending GPU slots");f=e[p]}for(let p of[l,c])if(p){for(let m of p)if(!Number.isFinite(m))throw Error("Velocities must be finite Float32 values")}let u=n.length*this.wordsPerBody,d=Math.min(r.device.limits.maxStorageBufferBindingSize,r.device.limits.maxBufferSize);if(u*4>d)throw Error("Motion batch exceeds GPU buffer limits");if(u){(!this.data||this.data.length<u)&&(this.data=new Uint32Array(Math.max(u,(this.data?.length??0)*2)),this.floats=new Float32Array(this.data.buffer));for(let p=0;p<n.length;p++){let m=p*this.wordsPerBody;if(this.data[m]=n[p],this.data[m+1]=(l?1:0)|(c?2:0),l)for(let h=0;h<s;h++)this.floats[m+2+h]=l[p*s+h];if(c)for(let h=0;h<a;h++)this.floats[m+2+s+h]=c[p*a+h]}r.wakeAll(),r.flushEdits(),this.submitValidated(this.data.subarray(0,u))}}submitValidated(e,t){if(!e.length)return;let i=this.solver,r=i.device,s=this.wordsPerBody,a=Math.min(r.limits.maxStorageBufferBindingSize,r.limits.maxBufferSize);if(e.byteLength>a)throw Error("Motion batch exceeds GPU buffer limits");(!this.buffer||this.buffer.size<e.byteLength)&&(this.buffer?.destroy(),this.buffer=r.createBuffer({size:Math.min(a,e.byteLength*2),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),r.queue.writeBuffer(this.buffer,0,e),this.pipeline??=r.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:r.createShaderModule({code:`
3380
3469
  @group(0) @binding(0) var<storage,read_write> bodies:array<vec4f>;
3381
3470
  @group(0) @binding(1) var<storage,read> commands:array<u32>;
3382
3471
  @compute @workgroup_size(64) fn edit(@builtin(global_invocation_id) id:vec3u) {
@@ -3388,14 +3477,14 @@ fn rotate(q:vec4f,v:vec3f)->vec3f {let t=2.*cross(q.xyz,v);return v+q.w*t+cross(
3388
3477
  if((mask&1u)!=0u){v.x=bitcast<f32>(commands[o+2u]);v.y=bitcast<f32>(commands[o+3u]);${this.dimension===3?"v.z=bitcast<f32>(commands[o+4u]);":""}}
3389
3478
  ${this.dimension===2?"if((mask&2u)!=0u){v.z=bitcast<f32>(commands[o+4u]);}":"if((mask&2u)!=0u){var a=bodies[b+9u];a.xyz=vec3f(bitcast<f32>(commands[o+5u]),bitcast<f32>(commands[o+6u]),bitcast<f32>(commands[o+7u]));bodies[b+9u]=a;}"}
3390
3479
  bodies[b+${this.dimension===3?8:3}u]=v;
3391
- }`})}}),(!this.binding||this.boundBody!==i.bodyBuffer||this.boundBuffer!==this.buffer||this.boundBytes!==e.byteLength)&&(this.binding=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.buffer,size:e.byteLength}}]}),this.boundBody=i.bodyBuffer,this.boundBuffer=this.buffer,this.boundBytes=e.byteLength);let n=t??r.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/s/64),f=r.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,f),Math.ceil(c/f)),l.end(),t||r.queue.submit([n.finish()]),this.lastBatch={bodies:e.length/s,uploadedBytes:e.byteLength}}destroy(){this.buffer?.destroy(),this.binding=null,this.data=this.floats=null}};function rs(o,e={}){return Di({...e,requested:e.solverMode??"auto",bodyCount:o.bodies.length,custom:!!e.shaders?.solve}),{...e,solverMode:e.solverMode??"auto"}}function Ui(o,e){let t=o&&typeof o.x=="number"?[o.x,o.y,o.z]:Array.from(o);if(t.length!==3||!t.every(Number.isFinite))throw Error(`${e} requires three finite components`);return t}function ss(o,e){if(o!==1/0&&(!Number.isFinite(o)||o<0))throw Error(`${e} must be nonnegative or Infinity`);return o}function fn(o,e){let t=Ui(e,"anchor");if(!o)return t;let i=o.aggregate,r=i.mesh.position.constructor;return new r(...t).subtract(i.localCenter).applyRotationQuaternion(i.localRotation.conjugate()).asArray()}var fr=class{constructor(e,t,i,r={}){e.assertAlive();for(let a of[t,i])if(a&&(a.assertAlive(),a.aggregate.world!==e))throw Error("Constraint bodies must belong to this world");if(!i||t===i)throw Error("A constraint needs a body B and distinct endpoints");let s=r.type??"ball";if(!["ball","fixed","spring","motor","limit"].includes(s))throw Error("Constraint type must be ball, fixed, spring, motor or limit");if(s==="spring"&&!t)throw Error("A spring requires two bodies; use a static aggregate for a world anchor");if(this.world=e,this.bodyA=t,this.bodyB=i,this.type=s,s==="motor"||s==="limit"){let a=(n,l)=>n?new n.aggregate.mesh.position.constructor(...Ui(l,"axis")).applyRotationQuaternion(n.aggregate.localRotation.conjugate()).asArray():Ui(l,"axis");this.angularMaterial=gt({...r,type:s,axisA:a(t,r.axisA??[0,1,0]),axisB:a(i,r.axisB??[0,1,0])})}if(this.anchorA=fn(t,r.anchorA??[0,0,0]),this.anchorB=fn(i,r.anchorB??[0,0,0]),this.linear=ss(s==="spring"?r.stiffness??1e3:r.stiffness??1/0,"stiffness"),this.angular=s==="fixed"?ss(r.angularStiffness??1/0,"angularStiffness"):0,this.breakForce=ss(r.breakForce??1/0,"breakForce"),this.breakForce===0)throw Error("breakForce must be positive");if(this.material=St({breakForce:this.breakForce,breakStrain:r.breakStrain}),s!=="spring"&&r.breakStrain!==void 0)throw Error("breakStrain applies to springs only");if(this.breakOnPull=r.breakOnPull??!1,this.rest=r.rest,this.rest!==void 0&&(!Number.isFinite(this.rest)||this.rest<0))throw Error("Spring rest length must be nonnegative and finite");if(s==="spring"&&!Number.isFinite(this.linear))throw Error("Spring stiffness must be finite");e.gpu?(e.flushCommands(),this.attach()):this.force=s==="spring"?new ct(e.ref,t.aggregate.rigid,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.rest??-1):new Ae(e.ref,t?.aggregate.rigid??null,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.angular,this.breakForce),this.force&&s==="spring"&&Yr(this.force,this.material),this.force&&this.angularMaterial&&Fi(this.force,this.angularMaterial),e.constraints.add(this),e.wakeAll?.()}attach(e,t=!0){let i=this.world.gpu;if(e!==void 0?this.slot=e:this.type==="spring"?[this.slot]=i.appendSprings([{a:this.bodyA.gpuIndex,b:this.bodyB.gpuIndex,rA:this.anchorA,rB:this.anchorB,stiffness:this.linear,rest:this.rest??0}]):this.slot=i.appendJoint(this.bodyA?.gpuIndex??-1,this.bodyB.gpuIndex,this.anchorA,this.anchorB,this.linear,this.angular),this.angularMaterial&&i.setAngularConstraint(this.slot,this.angularMaterial),this.type==="spring"&&(Number.isFinite(this.material.breakStrain)||Number.isFinite(this.material.breakForce))&&i.setSpringMaterial(this.slot,this.material),this.type!=="spring"&&Number.isFinite(this.breakForce)){let r=Number.isFinite(this.breakForce)?this.breakForce:1e30;i.device.queue.writeBuffer(i.jointBuffer,(this.slot*32+11)*4,new Float32Array([this.breakOnPull?-r:r]))}t&&(this.type==="fixed"||this.type==="spring"&&this.rest===void 0)&&i.captureConstraintFrames([{slot:this.slot,spring:this.type==="spring"}])}assertAlive(){if(this.world.assertAlive(),this.disposed)throw Error("Constraint is disposed")}setWorldAnchor(e){if(this.assertAlive(),this.bodyA||this.type==="spring")throw Error("Only world-anchored joints have a movable world anchor");return this.anchorA=Ui(e,"world anchor"),this.world.gpu?this.world.gpu.setWorldAnchor(this.slot,this.anchorA):this.force.rA.set(this.anchorA),this.world.wakeAll?.(),this}async readState(){this.assertAlive(),this.world.initialize();let e=this.world.gpu,t=this.world.device,i=t.createBuffer({size:128,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(e.jointBuffer,this.slot*128,i,0,128),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=new Float32Array(i.getMappedRange().slice(0));return{broken:s[3]===0&&s[7]===0,linearForce:Array.from(s.subarray(8,11)),angularForce:Array.from(s.subarray(12,15))}}finally{i.destroy()}}setMotor(e){if(this.assertAlive(),this.type!=="motor")throw Error("setMotor requires a motor constraint");return this.angularMaterial=gt({...this.angularMaterial,...e}),this.world.gpu?this.world.gpu.setMotor(this.slot,e):Fi(this.force,this.angularMaterial),this.world.wakeAll(),this}dispose(){this.disposed||(this.world.disposed||(this.world.gpu?this.world.gpu.releaseJoints([this.slot]):this.force.destroy(),this.world.wakeAll?.()),this.world.constraints.delete(this),this.disposed=!0)}},dr=class{constructor(e,t,i,r={}){let s=gt({...r,type:"limit"}),a=r.span??1;if(!Number.isFinite(a)||a<=0)throw Error("Hinge span must be positive");let n=Ui(r.anchorA??[0,0,0],"anchorA"),l=Ui(r.anchorB??[0,0,0],"anchorB");r.motor&&gt({...r.motor,type:"motor",axisA:s.axisA,axisB:s.axisB}),this.world=e,this.bodyA=t,this.bodyB=i,this.disposed=!1,this.joints=[-1,1].map(c=>e.addJoint(t,i,{anchorA:n.map((f,u)=>f+c*a*.5*s.axisA[u]),anchorB:l.map((f,u)=>f+c*a*.5*s.axisB[u]),stiffness:r.stiffness??1/0})),(r.minAngle!==void 0||r.maxAngle!==void 0)&&(this.limit=e.addConstraint(t,i,{...s,type:"limit"})),r.motor&&(this.motor=e.addMotor(t,i,{axisA:s.axisA,axisB:s.axisB,...r.motor}))}dispose(){if(!this.disposed){for(let e of[...this.joints,this.limit,this.motor])e?.dispose();this.disposed=!0}}};var Ft=class{constructor(e){this.device=e,this.pipelines=new Map,this.idle=[],this.jobs=new Set,this.destroyed=!1}acquire(e,t){if(this.destroyed)throw Error("Pose readback has been destroyed");let i=this.device;if(e>i.limits.maxStorageBufferBindingSize||t>Math.min(i.limits.maxStorageBufferBindingSize,i.limits.maxBufferSize))throw Error("Pose selection exceeds GPU buffer limits");let r=this.idle.findIndex(a=>a.selection.size>=e&&a.selected.size>=t),s=r>=0?this.idle.splice(r,1)[0]:this.idle.pop()??{};for(let[a,n,l]of[["selection",e,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST],["selected",t,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["staging",t,GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST]])(!s[a]||s[a].size<n)&&(s[a]?.destroy(),s[a]=i.createBuffer({size:n,usage:l}));return this.jobs.add(s),s}release(e){if(e.staging.unmap(),!this.destroyed&&e.selected.size<=8*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.delete(e)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.clear(),this.idle.length=0}async read(e,t,i=!1,r=10){if(!t.length)return new Float32Array;let s=i?r===6?1:2:r,a=this.device,n=a.limits.maxComputeWorkgroupsPerDimension,l=`${r}:${s}`,c=this.pipelines.get(l);c||(c=a.createComputePipeline({layout:"auto",compute:{entryPoint:"gather",module:a.createShaderModule({code:`
3480
+ }`})}}),(!this.binding||this.boundBody!==i.bodyBuffer||this.boundBuffer!==this.buffer||this.boundBytes!==e.byteLength)&&(this.binding=r.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.buffer,size:e.byteLength}}]}),this.boundBody=i.bodyBuffer,this.boundBuffer=this.buffer,this.boundBytes=e.byteLength);let n=t??r.createCommandEncoder(),l=n.beginComputePass();l.setPipeline(this.pipeline),l.setBindGroup(0,this.binding);let c=Math.ceil(e.length/s/64),f=r.limits.maxComputeWorkgroupsPerDimension;l.dispatchWorkgroups(Math.min(c,f),Math.ceil(c/f)),l.end(),t||r.queue.submit([n.finish()]),this.lastBatch={bodies:e.length/s,uploadedBytes:e.byteLength}}destroy(){this.buffer?.destroy(),this.binding=null,this.data=this.floats=null}};function us(o,e={}){return qi({...e,requested:e.solverMode??"auto",bodyCount:o.bodies.length,custom:!!e.shaders?.solve}),{...e,solverMode:e.solverMode??"auto"}}function Ji(o,e){let t=o&&typeof o.x=="number"?[o.x,o.y,o.z]:Array.from(o);if(t.length!==3||!t.every(Number.isFinite))throw Error(`${e} requires three finite components`);return t}function fs(o,e){if(o!==1/0&&(!Number.isFinite(o)||o<0))throw Error(`${e} must be nonnegative or Infinity`);return o}function Ln(o,e){let t=Ji(e,"anchor");if(!o)return t;let i=o.aggregate,r=i.mesh.position.constructor;return new r(...t).subtract(i.localCenter).applyRotationQuaternion(i.localRotation.conjugate()).asArray()}var mr=class{constructor(e,t,i,r={}){e.assertAlive();for(let a of[t,i])if(a&&(a.assertAlive(),a.aggregate.world!==e))throw Error("Constraint bodies must belong to this world");if(!i||t===i)throw Error("A constraint needs a body B and distinct endpoints");let s=r.type??"ball";if(!["ball","fixed","spring","motor","limit"].includes(s))throw Error("Constraint type must be ball, fixed, spring, motor or limit");if(s==="spring"&&!t)throw Error("A spring requires two bodies; use a static aggregate for a world anchor");if(this.world=e,this.bodyA=t,this.bodyB=i,this.type=s,s==="motor"||s==="limit"){let a=(n,l)=>n?new n.aggregate.mesh.position.constructor(...Ji(l,"axis")).applyRotationQuaternion(n.aggregate.localRotation.conjugate()).asArray():Ji(l,"axis");this.angularMaterial=gt({...r,type:s,axisA:a(t,r.axisA??[0,1,0]),axisB:a(i,r.axisB??[0,1,0])})}if(this.anchorA=Ln(t,r.anchorA??[0,0,0]),this.anchorB=Ln(i,r.anchorB??[0,0,0]),this.linear=fs(s==="spring"?r.stiffness??1e3:r.stiffness??1/0,"stiffness"),this.angular=s==="fixed"?fs(r.angularStiffness??1/0,"angularStiffness"):0,this.breakForce=fs(r.breakForce??1/0,"breakForce"),this.breakForce===0)throw Error("breakForce must be positive");if(this.material=Pt({breakForce:this.breakForce,breakStrain:r.breakStrain}),s!=="spring"&&r.breakStrain!==void 0)throw Error("breakStrain applies to springs only");if(this.breakOnPull=r.breakOnPull??!1,this.rest=r.rest,this.rest!==void 0&&(!Number.isFinite(this.rest)||this.rest<0))throw Error("Spring rest length must be nonnegative and finite");if(s==="spring"&&!Number.isFinite(this.linear))throw Error("Spring stiffness must be finite");e.gpu?(e.flushCommands(),this.attach()):this.force=s==="spring"?new ct(e.ref,t.aggregate.rigid,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.rest??-1):new we(e.ref,t?.aggregate.rigid??null,i.aggregate.rigid,this.anchorA,this.anchorB,this.linear,this.angular,this.breakForce),this.force&&s==="spring"&&ts(this.force,this.material),this.force&&this.angularMaterial&&Wi(this.force,this.angularMaterial),e.constraints.add(this),e.wakeAll?.()}attach(e,t=!0){let i=this.world.gpu;if(e!==void 0?this.slot=e:this.type==="spring"?[this.slot]=i.appendSprings([{a:this.bodyA.gpuIndex,b:this.bodyB.gpuIndex,rA:this.anchorA,rB:this.anchorB,stiffness:this.linear,rest:this.rest??0}]):this.slot=i.appendJoint(this.bodyA?.gpuIndex??-1,this.bodyB.gpuIndex,this.anchorA,this.anchorB,this.linear,this.angular),this.angularMaterial&&i.setAngularConstraint(this.slot,this.angularMaterial),this.type==="spring"&&(Number.isFinite(this.material.breakStrain)||Number.isFinite(this.material.breakForce))&&i.setSpringMaterial(this.slot,this.material),this.type!=="spring"&&Number.isFinite(this.breakForce)){let r=Number.isFinite(this.breakForce)?this.breakForce:1e30;i.device.queue.writeBuffer(i.jointBuffer,(this.slot*32+11)*4,new Float32Array([this.breakOnPull?-r:r]))}t&&(this.type==="fixed"||this.type==="spring"&&this.rest===void 0)&&i.captureConstraintFrames([{slot:this.slot,spring:this.type==="spring"}])}assertAlive(){if(this.world.assertAlive(),this.disposed)throw Error("Constraint is disposed")}setWorldAnchor(e){if(this.assertAlive(),this.bodyA||this.type==="spring")throw Error("Only world-anchored joints have a movable world anchor");return this.anchorA=Ji(e,"world anchor"),this.world.gpu?this.world.gpu.setWorldAnchor(this.slot,this.anchorA):this.force.rA.set(this.anchorA),this.world.wakeAll?.(),this}async readState(){this.assertAlive(),this.world.initialize();let e=this.world.gpu,t=this.world.device,i=t.createBuffer({size:128,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(e.jointBuffer,this.slot*128,i,0,128),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=new Float32Array(i.getMappedRange().slice(0));return{broken:s[3]===0&&s[7]===0,linearForce:Array.from(s.subarray(8,11)),angularForce:Array.from(s.subarray(12,15))}}finally{i.destroy()}}setMotor(e){if(this.assertAlive(),this.type!=="motor")throw Error("setMotor requires a motor constraint");return this.angularMaterial=gt({...this.angularMaterial,...e}),this.world.gpu?this.world.gpu.setMotor(this.slot,e):Wi(this.force,this.angularMaterial),this.world.wakeAll(),this}dispose(){this.disposed||(this.world.disposed||(this.world.gpu?this.world.gpu.releaseJoints([this.slot]):this.force.destroy(),this.world.wakeAll?.()),this.world.constraints.delete(this),this.disposed=!0)}},br=class{constructor(e,t,i,r={}){let s=gt({...r,type:"limit"}),a=r.span??1;if(!Number.isFinite(a)||a<=0)throw Error("Hinge span must be positive");let n=Ji(r.anchorA??[0,0,0],"anchorA"),l=Ji(r.anchorB??[0,0,0],"anchorB");r.motor&&gt({...r.motor,type:"motor",axisA:s.axisA,axisB:s.axisB}),this.world=e,this.bodyA=t,this.bodyB=i,this.disposed=!1,this.joints=[-1,1].map(c=>e.addJoint(t,i,{anchorA:n.map((f,u)=>f+c*a*.5*s.axisA[u]),anchorB:l.map((f,u)=>f+c*a*.5*s.axisB[u]),stiffness:r.stiffness??1/0})),(r.minAngle!==void 0||r.maxAngle!==void 0)&&(this.limit=e.addConstraint(t,i,{...s,type:"limit"})),r.motor&&(this.motor=e.addMotor(t,i,{axisA:s.axisA,axisB:s.axisB,...r.motor}))}dispose(){if(!this.disposed){for(let e of[...this.joints,this.limit,this.motor])e?.dispose();this.disposed=!0}}};var Jt=class{constructor(e){this.device=e,this.pipelines=new Map,this.idle=[],this.jobs=new Set,this.destroyed=!1}acquire(e,t){if(this.destroyed)throw Error("Pose readback has been destroyed");let i=this.device;if(e>i.limits.maxStorageBufferBindingSize||t>Math.min(i.limits.maxStorageBufferBindingSize,i.limits.maxBufferSize))throw Error("Pose selection exceeds GPU buffer limits");let r=this.idle.findIndex(a=>a.selection.size>=e&&a.selected.size>=t),s=r>=0?this.idle.splice(r,1)[0]:this.idle.pop()??{};for(let[a,n,l]of[["selection",e,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST],["selected",t,GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC],["staging",t,GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST]])(!s[a]||s[a].size<n)&&(s[a]?.destroy(),s[a]=i.createBuffer({size:n,usage:l}));return this.jobs.add(s),s}release(e){if(e.staging.unmap(),!this.destroyed&&e.selected.size<=8*1024*1024&&this.idle.length<2)this.idle.push(e);else{for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.delete(e)}}destroy(){this.destroyed=!0;for(let e of this.jobs)for(let t of["selection","selected","staging"])e[t].destroy();this.jobs.clear(),this.idle.length=0}async read(e,t,i=!1,r=10){if(!t.length)return new Float32Array;let s=i?r===6?1:2:r,a=this.device,n=a.limits.maxComputeWorkgroupsPerDimension,l=`${r}:${s}`,c=this.pipelines.get(l);c||(c=a.createComputePipeline({layout:"auto",compute:{entryPoint:"gather",module:a.createShaderModule({code:`
3392
3481
  @group(0) @binding(0) var<storage,read> bodies:array<vec4f>;
3393
3482
  @group(0) @binding(1) var<storage,read> indices:array<u32>;
3394
3483
  @group(0) @binding(2) var<storage,read_write> selected:array<vec4f>;
3395
3484
  @compute @workgroup_size(64) fn gather(@builtin(global_invocation_id) id:vec3u) {
3396
3485
  let i=id.x+id.y*${n*64}u; if(i>=arrayLength(&selected)){return;}
3397
3486
  selected[i]=bodies[indices[i/${s}u]*${r}u+i%${s}u];
3398
- }`})}}),this.pipelines.set(l,c));let f=t.length*s*16,u=this.acquire(t.length*4,f),{selection:d,selected:h,staging:m}=u;try{a.queue.writeBuffer(d,0,t instanceof Uint32Array?t:Uint32Array.from(t)),(!u.group||u.body!==e||u.pipeline!==c||u.boundSelection!==d||u.boundSelected!==h||u.indexBytes!==t.length*4||u.bytes!==f)&&(u.group=a.createBindGroup({layout:c.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e}},{binding:1,resource:{buffer:d,size:t.length*4}},{binding:2,resource:{buffer:h,size:f}}]}),u.body=e,u.pipeline=c,u.boundSelection=d,u.boundSelected=h,u.indexBytes=t.length*4,u.bytes=f);let p=a.createCommandEncoder(),b=p.beginComputePass();b.setPipeline(c),b.setBindGroup(0,u.group);let y=Math.ceil(t.length*s/64);return b.dispatchWorkgroups(Math.min(y,n),Math.ceil(y/n)),b.end(),p.copyBufferToBuffer(h,0,m,0,f),a.queue.submit([p.finish()]),await m.mapAsync(GPUMapMode.READ,0,f),new Float32Array(m.getMappedRange(0,f).slice(0))}finally{this.release(u)}}};var To=4,qi=3,Ut=1,Wl=2,hr=`
3487
+ }`})}}),this.pipelines.set(l,c));let f=t.length*s*16,u=this.acquire(t.length*4,f),{selection:d,selected:p,staging:m}=u;try{a.queue.writeBuffer(d,0,t instanceof Uint32Array?t:Uint32Array.from(t)),(!u.group||u.body!==e||u.pipeline!==c||u.boundSelection!==d||u.boundSelected!==p||u.indexBytes!==t.length*4||u.bytes!==f)&&(u.group=a.createBindGroup({layout:c.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:e}},{binding:1,resource:{buffer:d,size:t.length*4}},{binding:2,resource:{buffer:p,size:f}}]}),u.body=e,u.pipeline=c,u.boundSelection=d,u.boundSelected=p,u.indexBytes=t.length*4,u.bytes=f);let h=a.createCommandEncoder(),b=h.beginComputePass();b.setPipeline(c),b.setBindGroup(0,u.group);let y=Math.ceil(t.length*s/64);return b.dispatchWorkgroups(Math.min(y,n),Math.ceil(y/n)),b.end(),h.copyBufferToBuffer(p,0,m,0,f),a.queue.submit([h.finish()]),await m.mapAsync(GPUMapMode.READ,0,f),new Float32Array(m.getMappedRange(0,f).slice(0))}finally{this.release(u)}}};var Yo=4,$i=3,Xt=1,bc=2,gr=`
3399
3488
  struct Manifold { ids: vec4u, geo: vec4f } // a, b (a > b), first contact, count (low 4 bits); normal (b to a)
3400
3489
  struct Contact { rA: vec3f, key: u32, rB: vec3f, c0x: f32, pen: vec3f, c0y: f32, lam: vec3f, c0z: f32 }
3401
3490
  struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step: u32, dt: f32, pad: u32 }
@@ -3409,7 +3498,7 @@ struct Params { prev: u32, now: u32, mask: u32, capacity: u32, pairs: u32, step:
3409
3498
  @group(0) @binding(6) var<storage, read_write> events: array<vec4f>;
3410
3499
  @group(0) @binding(7) var<uniform> params: Params;
3411
3500
 
3412
- const STRIDE = ${Ce/4}u;
3501
+ const STRIDE = ${ke/4}u;
3413
3502
 
3414
3503
  fn hash32(x0: u32) -> u32 {
3415
3504
  var x = x0;
@@ -3424,7 +3513,7 @@ fn turn(q: vec4f, v: vec3f) -> vec3f {
3424
3513
  }
3425
3514
 
3426
3515
  fn slotOf(base: u32, h: u32) -> u32 {
3427
- return ${To}u + 2u * (base + h);
3516
+ return ${Yo}u + 2u * (base + h);
3428
3517
  }
3429
3518
 
3430
3519
  fn insert(base: u32, a: u32, b: u32) {
@@ -3457,16 +3546,16 @@ fn has(base: u32, a: u32, b: u32) -> bool {
3457
3546
  fn emit(kind: u32, a: u32, b: u32, point: vec3f, impulse: f32, normal: vec3f) {
3458
3547
  let e = atomicAdd(&table[0], 1u);
3459
3548
  if (e >= params.capacity) { return; }
3460
- events[${qi}u * e] = bitcast<vec4f>(vec4u(kind, a, b, params.step));
3461
- events[${qi}u * e + 1u] = vec4f(point, impulse);
3462
- events[${qi}u * e + 2u] = vec4f(normal, 0.0);
3549
+ events[${$i}u * e] = bitcast<vec4f>(vec4u(kind, a, b, params.step));
3550
+ events[${$i}u * e + 1u] = vec4f(point, impulse);
3551
+ events[${$i}u * e + 2u] = vec4f(normal, 0.0);
3463
3552
  }
3464
3553
 
3465
3554
  /** Per pair this step: into this step's set, and a begin if it wasn't in last step's. */
3466
3555
  @compute @workgroup_size(64)
3467
3556
  fn touching(@builtin(global_invocation_id) id: vec3u) {
3468
3557
  let m = id.x;
3469
- if (m >= min(counters[${Oi}u], params.pairs)) { return; }
3558
+ if (m >= min(counters[${Fi}u], params.pairs)) { return; }
3470
3559
  let mf = manifolds[m];
3471
3560
  let count = mf.ids.w & 15u;
3472
3561
  let a = mf.ids.x;
@@ -3475,8 +3564,8 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
3475
3564
  insert(params.now, a, b);
3476
3565
  if (has(params.prev, a, b)) { return; }
3477
3566
  // Where (the points' average, on a) and how hard (the normal force over the step)
3478
- let pA = bodies[a * STRIDE + ${Ei/4}u].xyz;
3479
- let qA = bodies[a * STRIDE + ${sr/4}u];
3567
+ let pA = bodies[a * STRIDE + ${Oi/4}u].xyz;
3568
+ let qA = bodies[a * STRIDE + ${cr/4}u];
3480
3569
  var point = vec3f(0.0);
3481
3570
  var force = 0.0;
3482
3571
  for (var i = 0u; i < count; i++) {
@@ -3484,7 +3573,7 @@ fn touching(@builtin(global_invocation_id) id: vec3u) {
3484
3573
  point += turn(qA, k.rA) + pA;
3485
3574
  force += k.lam.x;
3486
3575
  }
3487
- emit(${Ut}u, a, b, point / f32(count), abs(force) * params.dt, mf.geo.xyz);
3576
+ emit(${Xt}u, a, b, point / f32(count), abs(force) * params.dt, mf.geo.xyz);
3488
3577
  }
3489
3578
 
3490
3579
  /** Per slot of last step's set: an end if the pair isn't in this step's. */
@@ -3495,10 +3584,10 @@ fn ended(@builtin(global_invocation_id) id: vec3u) {
3495
3584
  let k = atomicLoad(&table[s]);
3496
3585
  if (k == 0u) { return; }
3497
3586
  let b = atomicLoad(&table[s + 1u]);
3498
- if (!has(params.now, k - 1u, b)) { emit(${Wl}u, k - 1u, b, vec3f(0.0), 0.0, vec3f(0.0)); }
3587
+ if (!has(params.now, k - 1u, b)) { emit(${bc}u, k - 1u, b, vec3f(0.0), 0.0, vec3f(0.0)); }
3499
3588
  }
3500
- `,Jl=hr.replace("struct Manifold { ids: vec4u, geo: vec4f }","struct Manifold { ids:vec4u, pl:vec4f, anchors:vec4f, geo:vec4f, misc:vec4f }").replace(`const STRIDE = ${Ce/4}u;`,"const STRIDE = 6u;").replace(hr.slice(hr.indexOf(`@compute @workgroup_size(64)
3501
- fn touching`),hr.indexOf("/** Per slot of last step")),`
3589
+ `,gc=gr.replace("struct Manifold { ids: vec4u, geo: vec4f }","struct Manifold { ids:vec4u, pl:vec4f, anchors:vec4f, geo:vec4f, misc:vec4f }").replace(`const STRIDE = ${ke/4}u;`,"const STRIDE = 6u;").replace(gr.slice(gr.indexOf(`@compute @workgroup_size(64)
3590
+ fn touching`),gr.indexOf("/** Per slot of last step")),`
3502
3591
  @compute @workgroup_size(64) fn touching(@builtin(global_invocation_id) id:vec3u){
3503
3592
  let m=id.x;let count=min(counters[1u],params.pairs);if(m>=count){return;}
3504
3593
  let c=manifolds[m];let a=c.ids.x;let b=c.ids.y;
@@ -3507,11 +3596,11 @@ fn touching`),hr.indexOf("/** Per slot of last step")),`
3507
3596
  insert(params.now,a,b);if(has(params.prev,a,b)){return;}
3508
3597
  let pose=bodies[a*STRIDE];let cs=cos(pose.z);let sn=sin(pose.z);
3509
3598
  let r=c.anchors.xy;let point=pose.xy+vec2f(cs*r.x-sn*r.y,sn*r.x+cs*r.y);
3510
- emit(${Ut}u,a,b,vec3f(point,0.),abs(c.pl.z)*params.dt,vec3f(c.geo.zw,0.));
3599
+ emit(${Xt}u,a,b,vec3f(point,0.),abs(c.pl.z)*params.dt,vec3f(c.geo.zw,0.));
3511
3600
  }
3512
3601
 
3513
- `),Nt=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,s,a=3){this.device=e,this.bodies=t,this.capacity=s,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let n=e.createShaderModule({label:"contact events",code:a===2?Jl:hr}),l=["read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform"];this.layout=e.createBindGroupLayout({label:"contact events",entries:l.map((d,h)=>({binding:h,visibility:GPUShaderStage.COMPUTE,buffer:{type:d}}))});let c=e.createPipelineLayout({bindGroupLayouts:[this.layout]}),f=d=>e.createComputePipeline({label:`contact events ${d}`,layout:c,compute:{module:n,entryPoint:d}});this.pipelines={touching:f("touching"),ended:f("ended")};let u=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.watch=e.createBuffer({label:"contact watch",size:Math.max(16,i*4),usage:u}),this.table=e.createBuffer({label:"contact sets",size:(To+4*this.setSlots)*4,usage:u}),this.events=e.createBuffer({label:"contact events",size:s*qi*16,usage:u}),this.params=e.createBuffer({label:"contact params",size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})}setWatched(e,t){let i=e.map((r,s)=>[r,t[s]?1:0]).sort((r,s)=>r[0]-s[0]);for(let r=0;r<i.length;){let s=r+1;for(;s<i.length&&i[s][0]===i[s-1][0]+1;)s++;this.device.queue.writeBuffer(this.watch,i[r][0]*4,Uint32Array.from(i.slice(r,s),a=>a[1])),r=s}}run(e,t,i,r){let s=this.device,a=[this.bodies,e.manifolds,e.contacts,e.counters,this.watch,this.table,this.events,this.params];(!this.group||a.some((d,h)=>d!==this.bound[h]))&&(this.bound=a,this.group=s.createBindGroup({layout:this.layout,entries:a.map((d,h)=>({binding:h,resource:{buffer:d}}))})),this.flip=1-this.flip;let[n,l]=this.flip?[0,this.setSlots]:[this.setSlots,0],c=new Uint32Array([n,l,this.setSlots-1,this.capacity,t,i,0,0]);new Float32Array(c.buffer)[6]=r,s.queue.writeBuffer(this.params,0,c);let f=s.createCommandEncoder({label:"contact events"});f.clearBuffer(this.table,(To+2*l)*4,2*this.setSlots*4);let u=f.beginComputePass({label:"contact events"});u.setBindGroup(0,this.group),u.setPipeline(this.pipelines.touching),u.dispatchWorkgroups(Math.max(1,Math.ceil(t/64))),u.setPipeline(this.pipelines.ended),u.dispatchWorkgroups(Math.ceil(this.setSlots/64)),u.end(),s.queue.submit([f.finish()])}async read(){let e=this.device,t=16+this.capacity*qi*16,i=e.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=e.createCommandEncoder({label:"read contact events"});r.copyBufferToBuffer(this.table,0,i,0,16),r.copyBufferToBuffer(this.events,0,i,16,t-16),r.clearBuffer(this.table,0,16),e.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);i.unmap(),i.destroy();let a=new Uint32Array(s),n=new Float32Array(s),l=Math.min(a[0],this.capacity),c=[];for(let f=0;f<l;f++){let u=4+f*qi*4;c.push({kind:a[u],a:a[u+1],b:a[u+2],step:a[u+3],point:[n[u+4],n[u+5],n[u+6]],impulse:n[u+7],normal:[n[u+8],n[u+9],n[u+10]]})}return c.sort((f,u)=>f.step-u.step),{events:c,dropped:a[0]-l,full:a[1]!==0}}destroy(){for(let e of[this.watch,this.table,this.events,this.params])e.destroy()}};var $e=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),Le=o=>o&&typeof o.x=="number"?[o.x,o.y,o.z,...typeof o.w=="number"?[o.w]:[]]:Array.from(o),pr=new WeakMap,Vi=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o},os=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},as=class o{static async create({device:e,...t}={}){if(t.scene&&pr.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await jo({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return r=new o(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(s){throw r?r.dispose():i&&e.destroy(),s}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:s=10,capacity:a=4096,syncMeshes:n=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:f="auto",bvh:u={},autoAttach:d=!0,maxContactPairs:h=8192,maxContactEvents:m=4096,sleeping:p=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=d,this.syncEnabled=n,this.maxSyncedBodies=l,!Number.isInteger(a)||a<1||!Number.isInteger(l)||l<0)throw Error("capacity and maxSyncedBodies must be valid integer counts");if(this.capacity=a,this.collisionOptions={broadphase:c,bvh:u,solverMode:f},this.ref=new Pi,this.ref.dt=os(r,"timeStep"),!Number.isInteger(s)||s<1)throw Error("iterations must be a positive integer");this.ref.iterations=s,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,A]of Object.entries({maxContactPairs:h,maxContactEvents:m}))if(!Number.isInteger(A)||A<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:h,maxContactEvents:m},this.bodyReadback=new Ft(e),this.sleepOptions=p,p!==!1&&p!==!0&&(typeof p!="object"||p===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let b=new WeakRef(this);e.lost.then(y=>{let A=b.deref();!A||A.disposed||(A.deviceLost={reason:y.reason,message:y.message},A.errors.push(`WebGPU device lost: ${y.message||y.reason}`),A.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&pr.set(t,this)}setGravity(e){let t=Le(e);if(t.length!==3||!t.every(Number.isFinite))throw Error("gravity requires three finite components");let i=Math.hypot(...t);this.gravity=-i,this.up=i?t.map(r=>-r/i):[0,1,0],this.gpu&&(this.gpu.params.gravity=this.gravity,this.gpu.params.up=this.up),this.wakeAll()}get solverDecision(){return this.initialize(),{...this.gpu.solverDecision}}setSolverMode(e){this.initialize();let t=this.gpu.setSolverMode(e);return this.collisionOptions.solverMode=e,{...t}}initialize(){if(this.assertAlive(),this.gpu)return;if(this.syncEnabled&&this.aggregates.filter(r=>r.sync).length>this.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(this.aggregates.length>this.capacity)throw Error(`Body capacity ${this.capacity} exceeded; size the world before initialization`);this.gpu=new Ni(this.device,this.ref,rs(this.ref,{bodyCapacity:Math.max(1,this.capacity),spatialSort:!0,...this.collisionOptions})),Object.assign(this.gpu.params,{gravity:this.gravity,up:this.up}),this.gpu.externalBeforeStep=r=>{this.sleepManager?.before(r),this.flushCommands(r)},this.gpu.externalAfterStep=r=>this.sleepManager?.after(r);let e=new Map([...this.constraints].map(r=>[r.force,r])),t=[],i=0;for(let r of this.ref.forces){let s=e.get(r);s&&(s.attach(i++,!1),(s.type==="fixed"||s.type==="spring"&&s.rest===void 0)&&t.push({slot:s.slot,spring:s.type==="spring"}))}this.gpu.captureConstraintFrames(t),this.byGpuIndex=new Map(this.aggregates.map(r=>[this.gpu.gpuIndex(r.index),r.body]));for(let r of this.aggregates)(r.disposed||r.group!==4294967295||r.collidesWith!==4294967295)&&this.gpu.setFilters([this.gpu.gpuIndex(r.index)],[r.disposed?0:r.group],[r.disposed?0:r.collidesWith]);for(let r of this.aggregates)r.isTrigger&&this.gpu.setSensor(r.body.gpuIndex,!0);for(let r of this.aggregates)r.restitution>0&&this.gpu.setRestitution(r.body.gpuIndex,r.restitution);this.contactListeners.size&&this.initializeContacts(),this.sleepOptions&&(this.sleepManager=new li(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new ns(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addConstraint(e,t,i={}){return new fr(this,e,t,i)}addJoint(e,t,i={}){return this.addConstraint(e,t,{...i,type:i.type??"ball"})}addWeld(e,t,i={}){return this.addConstraint(e,t,{...i,type:"fixed"})}addSpring(e,t,i={}){return this.addConstraint(e,t,{...i,type:"spring"})}enableSleeping(e={}){return this.assertAlive(),this.sleepOptions=e,this.gpu&&!this.sleepManager&&(this.sleepManager=new li(this,e)),this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new dr(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new Nt(this.device,this.gpu.bodyBuffer,this.capacity,e,t);let i=this.aggregates.filter(r=>!r.disposed);this.contactWatch.setWatched(i.map(r=>r.body.gpuIndex),i.map(()=>!0))}onContact(e){if(this.assertAlive(),typeof e!="function")throw Error("A contact callback is required");return this.contactListeners.add(e),this.gpu&&this.initializeContacts(),()=>this.contactListeners.delete(e)}async readContactEvents(){return this.initialize(),this.initializeContacts(),this.contactPending?this.contactPending:(this.contactPending=this.contactWatch.read().then(e=>({...e,events:e.events.map(t=>({...t,type:t.kind===Ut?"begin":"end",isTrigger:!!(this.byGpuIndex.get(t.a)?.aggregate.isTrigger||this.byGpuIndex.get(t.b)?.aggregate.isTrigger),a:this.byGpuIndex.get(t.a),b:this.byGpuIndex.get(t.b)}))})).finally(()=>this.contactPending=null),this.contactPending)}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}async sphereCast(e,t,i,r={}){let[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Vi(i),this.initialize(),this.flushCommands();for(let a of t)if(a.aggregate.world!==this||a.disposed)throw Error("Ignored cast bodies must belong to this world and be alive");return(await this.gpu.sphereCastAll(e.map(a=>({...a,origin:Le(a.origin),direction:Le(a.direction)})),{ignore:t.map(a=>a.gpuIndex),collidesWith:i,includeTriggers:r})).map(a=>a?{...a,body:this.byGpuIndex.get(a.index)??null}:null)}async raycastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){Vi(i),this.initialize(),this.flushCommands();let s=e.map(n=>{let l=Le(n.origin),c=Le(n.direction);if(l.length!==3||c.length!==3||![...l,...c].every(Number.isFinite)||!Math.hypot(...c))throw Error("A ray requires a finite origin and nonzero direction");return n.maxDistance!==void 0&&os(n.maxDistance,"maxDistance",!0),{...n,origin:l,direction:c}});for(let n of t)if(n.aggregate.world!==this)throw Error("Ignored ray bodies must belong to this world");return(await this.gpu.raycastAll(s,{ignore:t.map(n=>n.gpuIndex),collidesWith:i,includeTriggers:r})).map(n=>n?{...n,body:this.byGpuIndex.get(n.index)??null}:null)}getRenderBinding(e=this.device){if(this.initialize(),e!==this.device)throw Error("Direct GPU rendering must share the physics GPUDevice");return{device:e,buffer:this.bodyBuffer,stride:160,positionOffset:0,rotationOffset:16,sizeOffset:64,count:this.gpu.bodyCount}}wakeAll(){this.sleepManager&&(this.sleepManager.wakeRequested=!0)}async readSleepStats(){return this.initialize(),this.sleepManager?this.sleepManager.readStats():{sleeping:0,wakeRequested:!1}}assertAlive(){if(this.disposed)throw Error("AVBD world is disposed")}queue(e,t,i,r){this.assertAlive(),e.assertAlive();let s=Le(r);if(s.length!==(i==="rotation"?4:3)||!s.every(Number.isFinite))throw Error(`${i} requires ${i==="rotation"?4:3} finite components`);let a=this.commands.get(e);return a||this.commands.set(e,a={mask:0}),a.mask|=t,a[i]=s,this.wakeAll(),a}flushCommands(e){if(this.initialize(),this.gpu.flushPropertyEdits(e),!this.commands.size)return;this.sleepManager?.wakeRequested&&this.sleepManager.before(e);let t=this.commands.size,i=0;for(let p of this.commands.values())i|=p.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let A=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(A),this.velocityValues=new Float32Array(A*3)}let p=0;for(let[A,w]of this.commands)this.velocityIndices[p]=A.aggregate.gpuSlot??this.gpu.gpuIndex(A.aggregate.index),this.velocityValues.set(i===2?w.angular:w.velocity,p++*3);let b=i===2?"angularVelocityBatch":"velocityBatch",y=this[b]??=new Ct(this.gpu,3,i===2?"angular":"linear");y.solver=this.gpu,y.submitValidated(this.velocityIndices.subarray(0,t),this.velocityValues.subarray(0,t*3),e),this.lastCommandBatch={bodies:t,path:i===2?"packed-angular-velocities":"packed-velocities",uploadedBytes:t*16},this.commands.clear();return}let r=!0;for(let p of this.commands.values())if((p.mask&-4)!==0){r=!1;break}if(r){(!this.motionData||this.motionData.length<t*8)&&(this.motionData=new Uint32Array(Math.max(t*8,(this.motionData?.length??0)*2,512)),this.motionFloats=new Float32Array(this.motionData.buffer));let p=0;for(let[b,y]of this.commands){let A=p++*8;this.motionData[A]=b.aggregate.gpuSlot??this.gpu.gpuIndex(b.aggregate.index),this.motionData[A+1]=y.mask,y.velocity&&this.motionFloats.set(y.velocity,A+2),y.angular&&this.motionFloats.set(y.angular,A+5)}this.motionBatch??=new Gt(this.gpu,3),this.motionBatch.submitValidated(this.motionData.subarray(0,t*8),e),this.lastCommandBatch={bodies:t,path:"packed-motion",uploadedBytes:t*32},this.commands.clear();return}let s=t*128,a=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(s>a)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<s)&&(this.commandData=new ArrayBuffer(Math.min(a,Math.max(s,(this.commandData?.byteLength??0)*2,128))));let n=this.commandData,l=new Float32Array(n),c=new Uint32Array(n);l.fill(0,0,t*32);let f=0;for(let[p,b]of this.commands){let y=f++*32;c[y]=p.aggregate.gpuSlot??this.gpu.gpuIndex(p.aggregate.index),c[y+1]=b.mask;for(let[A,w]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])b[A]&&l.set(b[A],y+w)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<s)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(a,Math.max(s*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,n,0,s),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
3514
- ${un}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==s)&&(this.commandBinding=this.device.createBindGroup({layout:this.commandPipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.gpu.bodyBuffer}},{binding:1,resource:{buffer:this.commandBuffer,size:s}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=s);let u=e??this.device.createCommandEncoder(),d=u.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let h=Math.ceil(f/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(h,m),Math.ceil(h/m)),d.end(),e||this.device.queue.submit([u.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:s}}step(){this.initialize(),this.gpu.step(),this.steps++,this.contactWatch&&(this.contactWatch.run(this.gpu.contactStorage,this.gpu.manifoldCapacity,this.steps,this.ref.dt),this.contactListeners.size&&!this.contactPending&&this.readContactEvents().then(({events:e,dropped:t,full:i})=>{if(!this.disposed){(t||i)&&this.errors.push(`Contact event storage full: ${t} events dropped; increase maxContactEvents/maxContactPairs`);for(let r of e)for(let s of this.contactListeners)s(r)}}).catch(e=>{this.disposed||this.errors.push(e.message)})),this.syncEnabled&&!this.syncPending&&this.syncMeshes().catch(e=>this.errors.push(e.message)),this.steps%20===0&&!this.counterPending&&(this.counterPending=!0,this.gpu.readCounters().then(e=>{this.disposed||(this.counters=e,this.gpu.adapt(e),(e.overflow||e.clashes)&&this.errors.push(`Collision capacity/color conflict: ${JSON.stringify(e)}`))}).catch(e=>{this.disposed||this.errors.push(e.message)}).finally(()=>this.counterPending=!1))}async syncMeshes(){if(this.initialize(),this.flushCommands(),this.syncPending)return this.syncPending;let e=this.aggregates.filter(t=>t.sync&&!t.disposed);return this.syncPending=this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.body.gpuIndex),!0).then(t=>{if(!this.disposed){for(let i=0;i<e.length;i++)e[i].disposed||e[i].syncPose(t,i*8);this.lastSyncBytes=t.byteLength}}).finally(()=>this.syncPending=null),this.syncPending}get bodyBuffer(){return this.initialize(),this.gpu.bodyBuffer}async readBodies(e){if(this.initialize(),this.flushCommands(),e){for(let t of e)if(t.aggregate.world!==this)throw Error("Selected bodies must belong to this world");return this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.gpuIndex))}return this.gpu.readBodies()}dispose(){if(!this.disposed){this.disposed=!0,this.observer&&this.scene?.onBeforeRenderObservable.remove(this.observer),this.disposeObserver&&this.scene?.onDisposeObservable.remove(this.disposeObserver),this.scene&&pr.get(this.scene)===this&&pr.delete(this.scene),this.device.removeEventListener("uncapturederror",this.errorListener),this.gpu?.destroy(),this.contactWatch?.destroy(),this.sleepManager?.dispose(),this.commandBuffer?.destroy(),this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback.destroy(),this.commandData=this.velocityIndices=this.velocityValues=null,this.commandBinding=null;for(let e of this.aggregates)e.disposed=!0,e.body.disposed=!0;this.commands.clear(),this.contactListeners.clear();for(let e of this.constraints)e.disposed=!0;this.constraints.clear(),this.ownsDevice&&this.device.destroy()}}attachToScene(e=this.scene){if(this.assertAlive(),!e)throw Error("A Babylon scene is required");if(this.observer&&e===this.scene)return this;if(this.observer)throw Error("AVBD world is already attached to another scene");this.scene=e,this.disposeObserver=e.onDisposeObservable.addOnce(()=>this.dispose());let t=0;return this.observer=e.onBeforeRenderObservable.add(()=>{t+=Math.min(.05,e.getEngine().getDeltaTime()/1e3);for(let i=0;t>=this.ref.dt&&i<3;i++)this.step(),t-=this.ref.dt}),this}},ns=class{constructor(e,t,{mass:i=1,friction:r=.6,restitution:s=0,allowSleep:a,isTrigger:n=!1,sync:l=!0,group:c=4294967295,collidesWith:f=4294967295}={},u){if(u=u instanceof as?u:pr.get(u??e.getScene()),!u)throw Error("Create an AVBD world for this scene before adding aggregates");if(u.assertAlive(),typeof t=="number"&&(t=[$e.SPHERE,$e.CAPSULE,$e.CYLINDER,$e.BOX,$e.CONVEX_HULL][t]),os(i,"mass",!0),os(r,"friction",!0),!Number.isFinite(s)||s<0||s>1)throw Error("restitution must be between 0 and 1");if(e.parent)throw Error("AVBD aggregate meshes must have no parent; bake the parent transform first");if(!Object.values($e).includes(t))throw Error(`Unsupported AVBD shape: ${t}`);if(u.aggregates.length>=u.capacity)throw Error("AVBD body capacity exceeded");if(this.world=u,this.mesh=e,this.sync=l,this.type=t,a!==void 0&&typeof a!="boolean")throw Error("allowSleep must be a boolean");if(this.allowSleep=a??!0,typeof n!="boolean")throw Error("isTrigger must be a boolean");if(this.isTrigger=n,this.restitution=s,a===!0&&!u.sleepOptions&&u.enableSleeping(),this.group=Vi(c),this.collidesWith=Vi(f),u.gpu&&u.syncEnabled&&l&&u.aggregates.filter(g=>g.sync&&!g.disposed).length>=u.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![$e.BOX,$e.SPHERE,$e.CAPSULE].includes(t)&&u.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,h=e.scaling;if([h.x,h.y,h.z].some(g=>g<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(h),p=d.center.multiply(h),b=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=p,this.localRotation=b.constructor.Identity();let y;if(t===$e.BOX)y=new at(u.ref,Le(m),1,r,[0,0,0]);else if(t===$e.SPHERE){if(Math.max(m.x,m.y,m.z)-Math.min(m.x,m.y,m.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");y=Rr(u.ref,m.x/2,1,r,[0,0,0])}else if(t===$e.CAPSULE){if(Math.abs(m.x-m.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");y=ts(u.ref,m.x/2,m.y,1,r,[0,0,0])}else{let g=e.getVerticesData("position");if(!g)throw Error("Hull aggregate requires mesh positions");let x=Float32Array.from(g,(B,E)=>B*[h.x,h.y,h.z][E%3]),v=er(x);if(!v)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...v.center),this.localRotation=b.constructor.FromArray(v.rotation),y=Or(u.ref,v,1,r,[0,0,0])}let A=i/y.mass;y.mass=i,y.moment.forEach((g,x)=>y.moment[x]=g*A);let w=e.position.add(this.localCenter.applyRotationQuaternion(b));if(y.positionLin.set(Le(w)),y.positionAng.set(b.multiply(this.localRotation).asArray()),this.rigid=y,this.index=u.aggregates.length,this.body=new ls(this),u.aggregates.push(this),e.rotationQuaternion=b,u.gpu){if(this.gpuSlot=u.gpu.addBody(y),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");u.byGpuIndex.set(this.gpuSlot,this.body),u.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||u.gpu.sensorsEnabled)&&u.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&u.gpu.setRestitution(this.gpuSlot,this.restitution),u.contactWatch?.setWatched([this.gpuSlot],[!0]),u.sleepManager?.register(this.gpuSlot,y.mass,this.allowSleep),u.wakeAll?.()}}syncPose(e,t){let i=t??this.body.gpuIndex*40,r=this.localRotation.constructor.FromArray(e,i+4).multiply(this.localRotation.conjugate());this.mesh.rotationQuaternion.copyFrom(r),this.mesh.position.copyFrom(new this.mesh.position.constructor(...e.subarray(i,i+3)).subtract(this.localCenter.applyRotationQuaternion(r)))}dispose(){if(this.disposed)return;let e=this.world;for(let t of[...e.constraints])(t.bodyA===this.body||t.bodyB===this.body)&&t.dispose();if(e.gpu){let t=this.body.gpuIndex;e.queue(this.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}this.rigid.mass=0,this.rigid.moment.fill(0),this.sync=!1,this.disposed=this.body.disposed=!0,e.sleepManager?.register(this.gpuSlot??e.gpu?.gpuIndex(this.index)??this.index,0),e.wakeAll?.()}},ls=class{constructor(e){this.aggregate=e}assertAlive(){if(this.disposed||this.aggregate.disposed)throw Error("AVBD body is disposed")}get gpuIndex(){this.assertAlive();let e=this.aggregate.world;return e.initialize(),this.aggregate.gpuSlot??e.gpu.gpuIndex(this.aggregate.index)}setLinearVelocity(e){return this.aggregate.world.queue(this,1,"velocity",e),this}setAngularVelocity(e){return this.aggregate.world.queue(this,2,"angular",e),this}applyImpulse(e,t){let i=t?Le(t):null;if(i&&(i.length!==3||!i.every(Number.isFinite)))throw Error("Impulse point requires three finite components");let r=this.aggregate.world,s=Le(e),a=r.commands.get(this),n=s.map((c,f)=>c+(a?.impulse?.[f]||0)),l=r.queue(this,4,"impulse",n);if(t){let c=[i[1]*s[2]-i[2]*s[1],i[2]*s[0]-i[0]*s[2],i[0]*s[1]-i[1]*s[0]],f=l.torque||[0,0,0];l.torque=Le(c).map((u,d)=>u+f[d]),l.pointImpulse=s.map((u,d)=>u+(l.pointImpulse?.[d]||0))}return this}applyAngularImpulse(e){let t=this.aggregate.world,i=t.commands.get(this)?.torque||[0,0,0];return t.queue(this,4,"torque",Le(e).map((r,s)=>r+i[s])),this}applyForce(e,t){return this.applyImpulse(Le(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(Le(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Vi(e),s=Vi(t);return i.group=r,i.collidesWith=s,i.world.gpu&&i.world.gpu.setFilters([this.gpuIndex],[i.group],[i.collidesWith]),i.world.wakeAll?.(),this}setTrigger(e){if(this.assertAlive(),typeof e!="boolean")throw Error("isTrigger must be a boolean");let t=this.aggregate;return t.isTrigger=e,t.world.gpu&&t.world.gpu.setSensor(this.gpuIndex,e),t.world.wakeAll(),this}setRestitution(e){if(this.assertAlive(),!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");let t=this.aggregate;return t.restitution=e,t.world.gpu&&t.world.gpu.setRestitution(this.gpuIndex,e),t.world.wakeAll(),this}async readState(){let e=await this.aggregate.world.readBodies([this]),t=this.aggregate.rigid.mass;return{position:Array.from(e.subarray(0,3)),rotation:Array.from(e.subarray(4,8)),linearVelocity:Array.from(e.subarray(32,35)),angularVelocity:Array.from(e.subarray(36,39)),mass:t,effectiveMass:e[19],sleeping:t>0&&e[19]===0}}wakeUp(){return this.assertAlive(),this.aggregate.world.wakeAll(),this}setSleepEnabled(e){if(this.assertAlive(),typeof e!="boolean")throw Error("Sleep eligibility must be a boolean");let t=this.aggregate,i=t.world;return t.allowSleep=e,e&&!i.sleepOptions&&i.enableSleeping(),i.gpu&&i.sleepManager?.register(this.gpuIndex,t.rigid.mass,e),i.wakeAll(),this}teleport(e,t=this.aggregate.mesh.rotationQuaternion){let i=t.clone().normalize(),r=this.aggregate;return r.world.queue(this,8,"position",Le(e.add(r.localCenter.applyRotationQuaternion(i)))),r.world.queue(this,8,"rotation",i.multiply(r.localRotation).asArray()),this}};var ci=class{solver;bodyA;bodyB;J=new Float64Array(12);H=new Float64Array(36);C=new Float64Array(4);fmin=new Float64Array(4).fill(-1/0);fmax=new Float64Array(4).fill(1/0);stiffness=new Float64Array(4).fill(1/0);fracture=new Float64Array(4).fill(1/0);penalty=new Float64Array(4);lambda=new Float64Array(4);constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}disable(){this.stiffness.fill(0),this.penalty.fill(0),this.lambda.fill(0)}};var dn=o=>o<0?-1:o>0?1:0,Hi=(o,e)=>o<e?o:e,$l=(o,e)=>o>e?o:e,Wi=(o,e,t)=>$l(e,Hi(t,o));function mr(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function ui(o,e,t){let i=Math.cos(o[2]),r=Math.sin(o[2]);return[i*e-r*t+o[0],r*e+i*t+o[1]]}function kt(o,e,t,i,r){o[e*3]=t,o[e*3+1]=i,o[e*3+2]=r}var cs=class extends ci{rA;rB;C0=new Float64Array(3);torqueArm;restAngle;constructor(e,t,i,r,s,a=[1/0,1/0,1/0],n=1/0){super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[1]],this.stiffness[0]=a[0],this.stiffness[1]=a[1],this.stiffness[2]=a[2],this.fmax[2]=n,this.fmin[2]=-n,this.fracture[2]=n,this.restAngle=(t?t.position[2]:0)-i.position[2];let l=(t?t.size[0]:0)+i.size[0],c=(t?t.size[1]:0)+i.size[1];this.torqueArm=l*l+c*c}rows(){return 3}evaluate(e){let t=this.bodyA?ui(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=ui(this.bodyB.position,this.rB[0],this.rB[1]);e[0]=t[0]-i[0],e[1]=t[1]-i[1],e[2]=((this.bodyA?this.bodyA.position[2]:0)-this.bodyB.position[2]-this.restAngle)*this.torqueArm}initialize(){return this.evaluate(this.C0),this.stiffness[0]!==0||this.stiffness[1]!==0||this.stiffness[2]!==0}Cn=new Float64Array(3);computeConstraint(e){this.evaluate(this.Cn);for(let t=0;t<3;t++)this.C[t]=this.stiffness[t]===1/0?this.Cn[t]-this.C0[t]*e:this.Cn[t]}computeDerivatives(e){let{J:t,H:i}=this;if(i.fill(0,0,27),e===this.bodyA){let r=mr(e.position[2],this.rA[0],this.rA[1]);kt(t,0,1,0,-r[1]),kt(t,1,0,1,r[0]),kt(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=mr(e.position[2],this.rB[0],this.rB[1]);kt(t,0,-1,0,r[1]),kt(t,1,0,-1,-r[0]),kt(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},us=class extends ci{rA;rB;rest;constructor(e,t,i,r,s,a,n=-1){if(super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[1]],this.stiffness[0]=a,this.rest=n,this.rest<0){let l=ui(t.position,r[0],r[1]),c=ui(i.position,s[0],s[1]);this.rest=Math.hypot(l[0]-c[0],l[1]-c[1])}}rows(){return 1}initialize(){return!0}delta(){let e=ui(this.bodyA.position,this.rA[0],this.rA[1]),t=ui(this.bodyB.position,this.rB[0],this.rB[1]);return[e[0]-t[0],e[1]-t[1]]}computeConstraint(){let e=this.delta();this.C[0]=Math.hypot(e[0],e[1])-this.rest}computeDerivatives(e){let t=this.delta(),i=t[0]*t[0]+t[1]*t[1];if(i===0)return;let r=Math.sqrt(i),s=t[0]/r,a=t[1]/r,n=(1-s*s)/r,l=(0-s*a)/r,c=(0-a*s)/r,f=(1-a*a)/r,u=e===this.bodyA,d=u?this.rA:this.rB,h=mr(e.position[2],-d[1],d[0]),m=mr(e.position[2],d[0],d[1]),p=n*h[0]+l*h[1],b=c*h[0]+f*h[1],y=s*m[0]+a*m[1],A=s*h[0]+a*h[1];u?kt(this.J,0,s,a,A):kt(this.J,0,-s,-a,-A);let w=u?-y-y:y+y,g=this.H;g[0]=n,g[1]=l,g[2]=p,g[3]=c,g[4]=f,g[5]=b,g[6]=p,g[7]=b,g[8]=w}},fs=class extends ci{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},ds=class extends ci{speed;constructor(e,t,i,r,s){super(e,t,i),this.speed=r,this.fmax[0]=s,this.fmin[0]=-s}rows(){return 1}initialize(){return!0}computeConstraint(){let e=this.bodyA?this.bodyA.position[2]-this.bodyA.initial[2]:0,t=this.bodyB.position[2]-this.bodyB.initial[2];this.C[0]=e-t-this.speed*this.solver.dt}computeDerivatives(e){kt(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var hs=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),ps=()=>({...hs(),postStabilize:!1,alpha:.95,matchNearest:!0});var Ji=(o,e)=>o>e?o*2097152+e:e*2097152+o;function Xl(o,e,t){let i=0,r=e;for(;i<r;){let s=i+r>>>1;o[s]<t?i=s+1:r=s}return i}function Do(o,e,t){let i=Xl(o,e,t);return i<e&&o[i]===t}var ms=class{cellSize=1;pairs=new Float64Array(1024);pairCount=0;configuredFor=-1;large=new Int32Array(0);isLarge=new Uint8Array(0);cellX=new Int32Array(0);cellY=new Int32Array(0);tableSize=0;bucketStart=new Int32Array(0);sorted=new Int32Array(0);configure(e,t){this.configuredFor=e;let i=Float64Array.from({length:e},(n,l)=>t[l*4+1]).sort(),r=e>0?i[e>>1]:1,s=0;for(let n=0;n<e;n++)i[n]<=4*r&&(s=Math.max(s,i[n]));this.cellSize=Math.max(2*s,.001),this.isLarge=new Uint8Array(e);let a=[];for(let n=0;n<e;n++)t[n*4+1]>s&&(this.isLarge[n]=1,a.push(n));for(this.large=Int32Array.from(a),this.cellX=new Int32Array(e),this.cellY=new Int32Array(e),this.tableSize=1;this.tableSize<2*e;)this.tableSize<<=1;this.bucketStart=new Int32Array(this.tableSize+1),this.sorted=new Int32Array(e)}hash(e,t){return(Math.imul(e,73856093)^Math.imul(t,19349663))&this.tableSize-1}findPairs(e,t,i,r,s,a){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:n,cellY:l,isLarge:c,bucketStart:f,sorted:u}=this,d=1/this.cellSize;f.fill(0);for(let p=0;p<e;p++)c[p]||(n[p]=Math.floor(t[p*4]*d),l[p]=Math.floor(t[p*4+1]*d),f[this.hash(n[p],l[p])+1]++);for(let p=0;p<this.tableSize;p++)f[p+1]+=f[p];let h=f.slice(0,this.tableSize);for(let p=0;p<e;p++)c[p]||(u[h[this.hash(n[p],l[p])]++]=p);let m=(p,b)=>{if(!r[p]&&!r[b])return;let y=t[p*4]-t[b*4],A=t[p*4+1]-t[b*4+1],w=i[p*4+1]+i[b*4+1];if(y*y+A*A>w*w)return;let g=Ji(p,b);if(!Do(s,a,g)){if(this.pairCount===this.pairs.length){let x=new Float64Array(this.pairs.length*2);x.set(this.pairs),this.pairs=x}this.pairs[this.pairCount++]=g}};for(let p=0;p<e;p++){if(c[p])continue;let b=n[p],y=l[p];for(let A=-1;A<=1;A++)for(let w=-1;w<=1;w++){let g=this.hash(b+w,y+A);for(let x=f[g];x<f[g+1];x++){let v=u[x];v>=p||n[v]!==b+w||l[v]!==y+A||m(p,v)}}}for(let p of this.large)for(let b=0;b<e;b++)b===p||c[b]&&b>p||m(p,b);this.pairs.subarray(0,this.pairCount).sort()}};var Xe=new Float64Array(12),hn=new Float64Array(12),fi=new Float64Array(12);function yt(o,e,t,i,r,s,a,n){let l=e*6;o[l]=t,o[l+1]=i,o[l+2]=r,o[l+3]=s,o[l+4]=a,o[l+5]=n}function pn(o,e,t,i,r,s){let a=0,n=t*e[0]+i*e[1]-r,l=t*e[6]+i*e[7]-r;if(n<=0){for(let c=0;c<6;c++)o[a*6+c]=e[c];a++}if(l<=0){for(let c=0;c<6;c++)o[a*6+c]=e[6+c];a++}if(n*l<0){let c=n/(n-l),f=e[0]+(e[6]-e[0])*c,u=e[1]+(e[7]-e[1])*c;n>0?yt(o,a,f,u,s,e[3],0,e[5]):yt(o,a,f,u,e[8],s,e[10],0),a++}return a}function bs(o,e,t,i,r,s,a,n){let l=-(r*a+s*n),c=-(-s*a+r*n);Math.abs(l)>Math.abs(c)?l>0?(yt(Xe,0,o,-e,0,0,3,4),yt(Xe,1,o,e,0,0,4,1)):(yt(Xe,0,-o,e,0,0,1,2),yt(Xe,1,-o,-e,0,0,2,3)):c>0?(yt(Xe,0,o,e,0,0,4,1),yt(Xe,1,-o,e,0,0,1,2)):(yt(Xe,0,-o,-e,0,0,2,3),yt(Xe,1,o,-e,0,0,3,4));for(let f=0;f<2;f++){let u=f*6,d=Xe[u],h=Xe[u+1];Xe[u]=t+r*d-s*h,Xe[u+1]=i+s*d+r*h}}function mn(o,e,t,i,r,s,a,n,l,c,f){let u=Math.cos(t),d=Math.sin(t),h=Math.cos(n),m=Math.sin(n),p=s-o,b=a-e,y=u*p+d*b,A=-d*p+u*b,w=h*p+m*b,g=-m*p+h*b,x=Math.abs(u*h+d*m),v=Math.abs(u*-m+d*h),B=Math.abs(-d*h+u*m),E=Math.abs(-d*-m+u*h),k=Math.abs(y)-i-(x*l+v*c),z=Math.abs(A)-r-(B*l+E*c);if(k>0||z>0)return 0;let M=Math.abs(w)-(x*i+B*r)-l,P=Math.abs(g)-(v*i+E*r)-c;if(M>0||P>0)return 0;let _=0,S=k,C=y>0?u:-u,R=y>0?d:-d;z>.95*S+.01*r&&(_=1,S=z,C=A>0?-d:d,R=A>0?u:-u),M>.95*S+.01*l&&(_=2,S=M,C=w>0?h:-h,R=w>0?m:-m),P>.95*S+.01*c&&(_=3,S=P,C=g>0?-m:m,R=g>0?h:-h);let I,O,q,G,j,F,W,ee,ne;if(_===0){I=C,O=R,q=o*I+e*O+i,G=-d,j=u;let N=o*G+e*j;F=-N+r,W=N+r,ee=3,ne=1,bs(l,c,s,a,h,m,I,O)}else if(_===1){I=C,O=R,q=o*I+e*O+r,G=u,j=d;let N=o*G+e*j;F=-N+i,W=N+i,ee=2,ne=4,bs(l,c,s,a,h,m,I,O)}else if(_===2){I=-C,O=-R,q=s*I+a*O+l,G=-m,j=h;let N=s*G+a*j;F=-N+c,W=N+c,ee=3,ne=1,bs(i,r,o,e,u,d,I,O)}else{I=-C,O=-R,q=s*I+a*O+c,G=h,j=m;let N=s*G+a*j;F=-N+l,W=N+l,ee=2,ne=4,bs(i,r,o,e,u,d,I,O)}if(pn(hn,Xe,-G,-j,F,ee)<2||pn(fi,hn,G,j,W,ne)<2)return 0;let ve=0;for(let N=0;N<2;N++){let Ee=N*6,ze=fi[Ee],le=fi[Ee+1],Fe=I*ze+O*le-q;if(Fe>0)continue;let Lt=ze-I*Fe,Me=le-O*Fe,Ne=fi[Ee+2],Ie=fi[Ee+3],Ue=fi[Ee+4],Ze=fi[Ee+5],bi,gi,yi,vi;_>=2?([Ne,Ie,Ue,Ze]=[Ue,Ze,Ne,Ie],bi=ze-o,gi=le-e,yi=Lt-s,vi=Me-a):(bi=Lt-o,gi=Me-e,yi=ze-s,vi=le-a);let At=ve*7;f[At+0]=Ne&255|(Ie&255)<<8|(Ue&255)<<16|(Ze&255)<<24,f[At+1]=u*bi+d*gi,f[At+1+1]=-d*bi+u*gi,f[At+3]=h*yi+m*vi,f[At+3+1]=-m*yi+h*vi,f[At+5]=-C,f[At+5+1]=-R,ve++}return ve}var bn=o=>31-Math.clz32(o&-o);function Yl(o){let e=o+2654435769;return e^=e>>>16,e=Math.imul(e,2146121005),e^=e>>>15,e=Math.imul(e,2221713035),e^=e>>>16,e>>>0}var Go=(o,e,t,i)=>o>t||o===t&&e>i,gs=class{colors=new Int32Array(0);colorStart=new Int32Array(65);colorBodies=new Int32Array(0);next=new Int32Array(0);pending=new Uint8Array(0);prio=new Uint32Array(0);resize(e){if(this.colors.length===e)return;let t=new Int32Array(e).fill(-1);t.set(this.colors.subarray(0,Math.min(e,this.colors.length))),this.colors=t,this.next=new Int32Array(e),this.pending=new Uint8Array(e),this.colorBodies=new Int32Array(e),this.prio=Uint32Array.from({length:e},(i,r)=>Yl(r))}run(e,t,i,r,s,a){this.resize(e);let{colors:n,next:l,pending:c,prio:f}=this,u=(g,x)=>{let v=s[g*4+1],B=v===x?s[g*4+2]:v;return B>=0&&t[B]?B:-1},d=(g,x)=>{let v=0,B=0;for(let E=i[g];E<i[g+1];E++){let k=u(r[E],g);if(k<0||x&&c[k])continue;let z=n[k];z<0||(z<32?v|=1<<z:B|=1<<z-32)}return~v!==0?bn(~v):~B!==0?32+bn(~B):63};for(let g=0;g<e;g++){if(l[g]=n[g],!t[g]||n[g]<0)continue;let x=!0;for(let v=i[g];v<i[g+1]&&x;v++){let B=u(r[v],g);B>=0&&!Go(f[g],g,f[B],B)&&(x=!1)}x&&(l[g]=Math.min(n[g],d(g,!1)))}n.set(l);let h=()=>{let g=0;for(let x=0;x<e;x++)if(c[x]=0,!!t[x]){if(n[x]<0)c[x]=1;else for(let v=i[x];v<i[x+1];v++){let B=u(r[v],x);if(B>=0&&n[B]===n[x]&&Go(f[B],B,f[x],x)){c[x]=1;break}}g+=c[x]}return g},m=h(),p=0;for(;m>0&&p<a;){p++;for(let g=0;g<e;g++){if(l[g]=-2,!c[g])continue;let x=!0;for(let v=i[g];v<i[g+1]&&x;v++){let B=u(r[v],g);B>=0&&c[B]&&!Go(f[g],g,f[B],B)&&(x=!1)}x&&(l[g]=d(g,!0))}for(let g=0;g<e;g++)l[g]!==-2&&(n[g]=l[g],c[g]=0,m--)}let b=0;if(m>0){for(let g=0;g<e;g++)c[g]&&n[g]<0&&(n[g]=0);b=h()}let y=this.colorStart;y.fill(0);let A=0;for(let g=0;g<e;g++)t[g]&&(y[n[g]+1]++,A=Math.max(A,n[g]+1));for(let g=0;g<64;g++)y[g+1]+=y[g];let w=y.slice(0,64);for(let g=0;g<e;g++)t[g]&&(this.colorBodies[w[n[g]]++]=g);return{numColors:A,conflicts:b,rounds:p}}};var An=0,Vt=1,Bn=2,ys=3,Fo=4,wn=[0,3,1,1,2],ec=.05,D=4,T=32,be=0,ae=3,$=6,ft=9,dt=12,di=15,ot=18,H=21,K=23,ge=25,_e=26,vt=27,br=28,vs=class{params=hs();options;bodyCount=0;pose;initial;inertial;velocity;prevVelocity;shape;props;dynamic=new Uint8Array(0);jointCount=0;contactCount=0;info=new Int32Array(0);data;handles=[];prevCount=0;prevKeys=new Float64Array(0);prevInfo=new Int32Array(0);prevData=new Float64Array(0);ignorePairs=[];noCollide=new Float64Array(0);noCollideCount=0;noCollideDirty=!0;adjStart=new Int32Array(1);adjList=new Int32Array(0);broadphase=new ms;coloring=new gs;numColors=0;colorConflicts=0;colorRounds=0;profiling=!1;profile={};phaseStart=0;contactOut=new Float64Array(14);dxBuf=new Float64Array(0);constructor(e={}){this.options={precision:"f32",order:"colored",colorRounds:32,...e},this.pose=this.real(0),this.initial=this.real(0),this.inertial=this.real(0),this.velocity=this.real(0),this.prevVelocity=this.real(0),this.shape=this.real(0),this.props=this.real(0),this.data=this.real(0)}real(e){return this.options.precision==="f32"?new Float32Array(e):new Float64Array(e)}grow(e,t){if(e.length>=t)return e;let i=new e.constructor(Math.max(t,e.length*2,16));return i.set(e),i}clear(){this.bodyCount=0,this.jointCount=0,this.contactCount=0,this.prevCount=0,this.handles=[],this.ignorePairs=[],this.noCollideDirty=!0,this.coloring.resize(0)}addBody(e,t,i,r,s=[0,0,0]){let a=this.bodyCount++,n=this.bodyCount*4;this.pose=this.grow(this.pose,n),this.initial=this.grow(this.initial,n),this.inertial=this.grow(this.inertial,n),this.velocity=this.grow(this.velocity,n),this.prevVelocity=this.grow(this.prevVelocity,n),this.shape=this.grow(this.shape,n),this.props=this.grow(this.props,n),this.dynamic=this.grow(this.dynamic,this.bodyCount);let l=e[0]*e[1]*t;return this.pose.set([r[0],r[1],r[2]??0,0],a*4),this.velocity.set([s[0],s[1],s[2]??0,0],a*4),this.prevVelocity.set(this.velocity.subarray(a*4,a*4+4),a*4),this.shape.set([e[0],e[1],l,l*(e[0]*e[0]+e[1]*e[1])/12],a*4),this.props.set([i,Math.hypot(e[0]*.5,e[1]*.5),0,0],a*4),this.dynamic[a]=l>0?1:0,a}allocConstraint(){let e=this.jointCount++,t=this.jointCount+this.contactCount;this.info=this.grow(this.info,t*D),this.data=this.grow(this.data,t*T),this.contactCount>0&&(this.info.copyWithin((e+1)*D,e*D,(t-1)*D),this.data.copyWithin((e+1)*T,e*T,(t-1)*T)),this.data.fill(0,e*T,e*T+T);for(let i=0;i<3;i++)this.data[e*T+$+i]=1/0,this.data[e*T+ft+i]=-1/0,this.data[e*T+dt+i]=1/0,this.data[e*T+di+i]=1/0;return this.handles[e]=null,this.noCollideDirty=!0,e}setInfo(e,t,i,r){this.info.set([t,i,r,0],e*D)}addJoint(e,t,i,r,s=[1/0,1/0,1/0],a=1/0){let n=this.allocConstraint();this.setInfo(n,Vt,e,t);let l=this.data,c=n*T;l.set(s,c+$),l[c+dt+2]=a,l[c+ft+2]=-a,l[c+di+2]=a,l.set(i,c+H),l.set(r,c+K),l[c+ge]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let f=(e>=0?this.shape[e*4]:0)+this.shape[t*4],u=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+_e]=f*f+u*u;let d={slot:n,alive:!0};return this.handles[n]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*T+H]=t,this.data[e.slot*T+H+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,s,a){let n=this.allocConstraint();this.setInfo(n,Bn,e,t);let l=n*T;this.data[l+$]=s,this.data.set(i,l+H),this.data.set(r,l+K),this.data[l+ge]=a}addMotor(e,t,i,r){let s=this.allocConstraint();this.setInfo(s,ys,e,t);let a=s*T;this.data[a+dt]=r,this.data[a+ft]=-r,this.data[a+ge]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Ji(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(hs());Object.assign(this.params,Object.fromEntries(t.map(r=>[r,e[r]])));let i=new Map(e.bodies.map((r,s)=>[r,s]));for(let r of e.bodies){let s=this.addBody(r.size,0,r.friction,r.position,r.velocity);this.shape[s*4+2]=r.mass,this.shape[s*4+3]=r.moment,this.dynamic[s]=r.mass>0?1:0}for(let r of e.forces){let s=r.bodyA?i.get(r.bodyA):-1,a=i.get(r.bodyB);if(r instanceof cs){let n=this.addJoint(s,a,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[n.slot*T+ge]=r.restAngle,this.data[n.slot*T+_e]=r.torqueArm}else r instanceof us?this.addSpring(s,a,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof ds?this.addMotor(s,a,r.speed,r.fmax[0]):r instanceof fs&&this.addIgnoreCollision(s,a)}}step(){let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha;this.phaseStart=this.profiling?performance.now():0,this.saveContacts(),this.findPairs(),this.mark("broadphase"),this.initJoints(),this.narrowphase(),this.mark("narrowphase"),this.warmStartConstraints(t,i),this.buildAdjacency(),this.mark("adjacency"),this.colorBodies(),this.mark("coloring"),this.warmStartBodies();let r=e.iterations+(t?1:0);for(let s=0;s<r;s++){let a=t?s<e.iterations?1:0:i;this.primal(a),this.mark("primal"),s<e.iterations&&this.dual(a),s===e.iterations-1&&this.updateVelocities(),this.mark("dual")}t&&this.refreshStick()}mark(e){if(!this.profiling)return;let t=performance.now();this.profile[e]=(this.profile[e]??0)+t-this.phaseStart,this.phaseStart=t}resetProfile(){for(let e of Object.keys(this.profile))delete this.profile[e]}saveContacts(){let e=this.contactCount,t=this.jointCount;this.prevKeys.length<e&&(this.prevKeys=new Float64Array(e*2),this.prevInfo=new Int32Array(e*2*D),this.prevData=new Float64Array(e*2*T));for(let i=0;i<e;i++){let r=t+i;this.prevKeys[i]=Ji(this.info[r*D+1],this.info[r*D+2])}this.prevInfo.set(this.info.subarray(t*D,(t+e)*D)),this.prevData.set(this.data.subarray(t*T,(t+e)*T)),this.prevCount=e,this.contactCount=0}findPairs(){if(this.noCollideDirty){let e=[...this.ignorePairs];for(let t=0;t<this.jointCount;t++){let i=this.info[t*D+1],r=this.info[t*D+2];this.info[t*D]!==An&&i>=0&&e.push(Ji(i,r))}this.noCollide=Float64Array.from(e).sort(),this.noCollideCount=e.length,this.noCollideDirty=!1}this.broadphase.findPairs(this.bodyCount,this.pose,this.props,this.dynamic,this.noCollide,this.noCollideCount)}initJoints(){let{info:e,data:t,pose:i}=this,r=0;for(let s=0;s<this.jointCount;s++){let a=s*T;if(e[s*D]===Vt){if(t[a+$]===0&&t[a+$+1]===0&&t[a+$+2]===0){let f=this.handles[s];f&&(f.alive=!1);continue}let l=e[s*D+1],c=e[s*D+2];this.jointC(l,c,a,i,t,a+ot)}r!==s&&(e.copyWithin(r*D,s*D,(s+1)*D),t.copyWithin(r*T,a,a+T),this.handles[r]=this.handles[s],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let s=r;s<this.jointCount;s++)this.handles[s]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,s,a,n=s){let l,c,f;if(e>=0){let p=Math.cos(r[e*4+2]),b=Math.sin(r[e*4+2]),y=s[i+H],A=s[i+H+1];l=p*y-b*A+r[e*4],c=b*y+p*A+r[e*4+1],f=r[e*4+2]}else l=s[i+H],c=s[i+H+1],f=0;let u=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),h=s[i+K],m=s[i+K+1];n[a]=l-(u*h-d*m+r[t*4]),n[a+1]=c-(d*h+u*m+r[t*4+1]),n[a+2]=(f-r[t*4+2]-s[i+ge])*s[i+_e]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,s=this.contactOut;for(let a=0;a<this.broadphase.pairCount;a++){let n=r[a],l=Math.floor(n/2097152),c=n-l*2097152,f=mn(e[l*4],e[l*4+1],e[l*4+2],t[l*4]*.5,t[l*4+1]*.5,e[c*4],e[c*4+1],e[c*4+2],t[c*4]*.5,t[c*4+1]*.5,s);if(f===0)continue;let u=0,d=this.prevCount;for(;u<d;){let p=u+d>>>1;this.prevKeys[p]<n?u=p+1:d=p}let h=u,m=Math.sqrt(i[l*4]*i[c*4]);for(let p=0;p<f;p++){let b=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(b+1)*D),this.data=this.grow(this.data,(b+1)*T);let y=this.data,A=b*T,w=p*7,g=s[w+0];this.info.set([Fo,l,c,g],b*D),y.fill(0,A,A+T),y[A+$]=y[A+$+1]=1/0,y[A+ft]=-1/0,y[A+dt]=0,y[A+di]=y[A+di+1]=1/0,y[A+H]=s[w+1],y[A+H+1]=s[w+1+1],y[A+K]=s[w+3],y[A+K+1]=s[w+3+1],y[A+ge]=m,y[A+_e]=s[w+5],y[A+vt]=s[w+5+1];let x=-1;for(let S=h;S<this.prevCount&&this.prevKeys[S]===n;S++)this.prevInfo[S*D+3]===g&&(x=S);if(x<0&&this.params.matchNearest){let S=ec*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let C=h;C<this.prevCount&&this.prevKeys[C]===n;C++){let R=Math.hypot(this.prevData[C*T+H]-y[A+H],this.prevData[C*T+H+1]-y[A+H+1]);R<=S&&(S=R,x=C)}}if(x>=0){let S=x*T;y[A+be]=this.prevData[S+be],y[A+be+1]=this.prevData[S+be+1],y[A+ae]=this.prevData[S+ae],y[A+ae+1]=this.prevData[S+ae+1],this.prevData[S+br]&&(y[A+H]=this.prevData[S+H],y[A+H+1]=this.prevData[S+H+1],y[A+K]=this.prevData[S+K],y[A+K+1]=this.prevData[S+K+1])}let[v,B]=qt(e[l*4+2],y[A+H],y[A+H+1]),[E,k]=qt(e[c*4+2],y[A+K],y[A+K+1]),z=y[A+_e],M=y[A+vt],P=e[l*4]+v-e[c*4]-E,_=e[l*4+1]+B-e[c*4+1]-k;y[A+ot]=z*P+M*_+5e-4,y[A+ot+1]=M*P-z*_}}}warmStartConstraints(e,t){let i=this.params,r=this.data,s=this.jointCount+this.contactCount;for(let a=0;a<s;a++){let n=wn[this.info[a*D]],l=a*T;for(let c=0;c<n;c++){if(i.vbd){r[l+be+c]=Hi(r[l+$+c],i.vbdStiffness);continue}e||(r[l+ae+c]=r[l+ae+c]*t*i.gamma),r[l+be+c]=Hi(Wi(r[l+be+c]*i.gamma,1,1e9),r[l+$+c])}}}prepareTopology(){this.buildAdjacency(),this.colorBodies()}buildAdjacency(){let e=this.bodyCount,t=this.jointCount+this.contactCount;this.adjStart=this.grow(this.adjStart,e+1),this.adjList=this.grow(this.adjList,t*2);let i=this.adjStart;i.fill(0,0,e+1);for(let s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&i[n+1]++}for(let s=0;s<e;s++)i[s+1]+=i[s];let r=i.slice(0,e);for(let s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&(this.adjList[r[n]++]=s)}}colorBodies(){if(this.options.order!=="colored")return;let e=this.coloring.run(this.bodyCount,this.dynamic,this.adjStart,this.adjList,this.info,this.options.colorRounds);this.numColors=e.numColors,this.colorConflicts=e.conflicts,this.colorRounds=e.rounds}warmStartBodies(){let{dt:e,gravity:t}=this.params,{pose:i,velocity:r,initial:s,inertial:a,prevVelocity:n}=this;for(let l=0;l<this.bodyCount;l++){let c=l*4;r[c+2]=Wi(r[c+2],-50,50),a[c]=i[c]+r[c]*e,a[c+1]=i[c+1]+r[c+1]*e,a[c+2]=i[c+2]+r[c+2]*e,this.dynamic[l]&&(a[c+1]+=t*e*e);let f=(r[c+1]-n[c+1])/e*dn(t),u=Wi(f/Math.abs(t),0,1);Number.isFinite(u)||(u=0),s[c]=i[c],s[c+1]=i[c+1],s[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*u*e*e,i[c+2]=i[c+2]+r[c+2]*e}}eC=new Float64Array(3);eMin=new Float64Array(3);eMax=new Float64Array(3);eJ=new Float64Array(9);eG=new Float64Array(9);evalConstraint(e,t,i){let{info:r,data:s,pose:a,initial:n}=this,l=r[e*D],c=r[e*D+1],f=r[e*D+2],u=e*T,{eC:d,eMin:h,eMax:m,eJ:p,eG:b}=this,y=wn[l];for(let w=0;w<y;w++)h[w]=s[u+ft+w],m[w]=s[u+dt+w];let A=i===c;if(b.fill(0),l===Vt){this.jointC(c,f,u,a,s,0,d);for(let w=0;w<3;w++)s[u+$+w]===1/0&&(d[w]-=s[u+ot+w]*t);if(i>=0){let w=a[i*4+2],[g,x]=qt(w,s[u+(A?H:K)],s[u+(A?H:K)+1]),v=A?1:-1;p[0]=v,p[1]=0,p[2]=-v*x,p[3]=0,p[4]=v,p[5]=v*g,p[6]=0,p[7]=0,p[8]=v*s[u+_e],b[2]=Math.abs(g),b[5]=Math.abs(x)}}else if(l===Bn){let[w,g]=Sn(a,c,s[u+H],s[u+H+1]),[x,v]=Sn(a,f,s[u+K],s[u+K+1]),B=w-x,E=g-v,k=B*B+E*E,z=Math.sqrt(k);if(d[0]=z-s[u+ge],i>=0&&k!==0){let M=B/z,P=E/z,_=(1-M*M)/z,S=-M*P/z,C=(1-P*P)/z,R=a[i*4+2],I=A?H:K,[O,q]=qt(R,-s[u+I+1],s[u+I]),[G,j]=qt(R,s[u+I],s[u+I+1]),F=_*O+S*q,W=S*O+C*q,ee=M*G+P*j,ne=M*O+P*q,ve=A?1:-1;p[0]=ve*M,p[1]=ve*P,p[2]=ve*ne;let N=A?-ee-ee:ee+ee;b[0]=Math.hypot(_,S,F),b[1]=Math.hypot(S,C,W),b[2]=Math.hypot(F,W,N)}else i>=0&&p.fill(0,0,3)}else if(l===ys){let w=c>=0?a[c*4+2]-n[c*4+2]:0,g=a[f*4+2]-n[f*4+2];d[0]=w-g-s[u+ge]*this.params.dt,i>=0&&(p[0]=0,p[1]=0,p[2]=A?1:-1)}else if(l===Fo){let w=s[u+_e],g=s[u+vt],x=g,v=-w,[B,E]=qt(n[c*4+2],s[u+H],s[u+H+1]),[k,z]=qt(n[f*4+2],s[u+K],s[u+K+1]),M=B*g-E*w,P=-(k*g-z*w),_=B*v-E*x,S=-(k*v-z*x),C=a[c*4]-n[c*4],R=a[c*4+1]-n[c*4+1],I=a[c*4+2]-n[c*4+2],O=a[f*4]-n[f*4],q=a[f*4+1]-n[f*4+1],G=a[f*4+2]-n[f*4+2];d[0]=s[u+ot]*(1-t)+w*C+g*R+M*I+-w*O+-g*q+P*G,d[1]=s[u+ot+1]*(1-t)+x*C+v*R+_*I+-x*O+-v*q+S*G;let j=Math.abs(s[u+ae])*s[u+ge];m[1]=j,h[1]=-j,i>=0&&(A?(p[0]=w,p[1]=g,p[2]=M,p[3]=x,p[4]=v,p[5]=_):(p[0]=-w,p[1]=-g,p[2]=P,p[3]=-x,p[4]=-v,p[5]=S))}return y}solveBody(e,t){let i=this.params,{shape:r,pose:s,inertial:a,data:n}=this,l=i.dt*i.dt,c=r[e*4+2]/l,f=r[e*4+3]/l,u=c,d=0,h=c,m=0,p=0,b=f,y=c*(s[e*4]-a[e*4]),A=c*(s[e*4+1]-a[e*4+1]),w=f*(s[e*4+2]-a[e*4+2]),{eC:g,eMin:x,eMax:v,eJ:B,eG:E}=this,k=this.adjStart,z=this.adjList;for(let F=k[e+1]-1;F>=k[e];F--){let W=z[F],ee=this.evalConstraint(W,t,e),ne=W*T,ve=!i.vbd;for(let N=0;N<ee;N++){let Ee=n[ne+$+N],ze=ve&&Ee===1/0?n[ne+ae+N]:0,le=n[ne+be+N],Fe=g[N],Lt=le*Fe+ze,Me=Wi(Lt,x[N],v[N]),Ne=Math.abs(Me);i.stiffnessRescale&&Fe!==0&&(Lt<x[N]?le=Math.abs((x[N]-ze)/Fe):Lt>v[N]&&(le=Math.abs((v[N]-ze)/Fe)));let Ie=B[N*3],Ue=B[N*3+1],Ze=B[N*3+2];y+=Ie*Me,A+=Ue*Me,w+=Ze*Me,u+=Ie*Ie*le+E[N*3]*Ne,d+=Ue*Ie*le,h+=Ue*Ue*le+E[N*3+1]*Ne,m+=Ze*Ie*le,p+=Ze*Ue*le,b+=Ze*Ze*le+E[N*3+2]*Ne}}let M=u,P=d/u,_=m/u,S=h-P*P*M,C=(p-P*_*M)/S,R=b-(_*_*M+C*C*S),I=A-P*y,q=(w-_*y-C*I)/R,G=I/S-C*q,j=y/M-P*G-_*q;this.dxBuf[e*3]=j,this.dxBuf[e*3+1]=G,this.dxBuf[e*3+2]=q}applyDx(e){this.pose[e*4]-=this.dxBuf[e*3],this.pose[e*4+1]-=this.dxBuf[e*3+1],this.pose[e*4+2]-=this.dxBuf[e*3+2]}primal(e){if(this.dxBuf.length<this.bodyCount*3&&(this.dxBuf=new Float64Array(this.bodyCount*3)),this.options.order==="sequential"){for(let r=this.bodyCount-1;r>=0;r--)this.dynamic[r]&&(this.solveBody(r,e),this.applyDx(r));return}let{colorStart:t,colorBodies:i}=this.coloring;for(let r=0;r<this.numColors;r++){for(let s=t[r];s<t[r+1];s++)this.solveBody(i[s],e);for(let s=t[r];s<t[r+1];s++)this.applyDx(i[s])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:s,eMax:a}=this,n=this.jointCount+this.contactCount;for(let l=0;l<n;l++){let c=this.info[l*D];if(c===An)continue;let f=this.evalConstraint(l,e,-1),u=l*T;c===Fo&&(i[u+br]=Math.abs(i[u+ae+1])<a[1]&&Math.abs(i[u+ot+1])<.01?1:0);for(let d=0;d<f;d++){let h=i[u+$+d],m=!t.vbd&&h===1/0?i[u+ae+d]:0,p=Wi(i[u+be+d]*r[d]+m,s[d],a[d]);i[u+ae+d]=p,Math.abs(p)>=i[u+di+d]&&this.disable(l),!t.vbd&&p>s[d]&&p<a[d]&&(i[u+be+d]=Hi(i[u+be+d]+t.beta*Math.abs(r[d]),Hi(1e9,i[u+$+d])))}}}refreshStick(){let e=this.data;for(let t=0;t<this.contactCount;t++){let i=(this.jointCount+t)*T,r=Math.abs(e[i+ae])*e[i+ge];e[i+br]=Math.abs(e[i+ae+1])<r&&Math.abs(e[i+ot+1])<.01?1:0}}disable(e){let t=e*T;this.data.fill(0,t+be,t+be+3),this.data.fill(0,t+ae,t+ae+3),this.data.fill(0,t+$,t+$+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:s}=this;for(let a=0;a<this.bodyCount;a++){let n=a*4;s[n]=r[n],s[n+1]=r[n+1],s[n+2]=r[n+2],this.dynamic[a]&&(r[n]=(t[n]-i[n])/e,r[n+1]=(t[n+1]-i[n+1])/e,r[n+2]=(t[n+2]-i[n+2])/e)}}pick(e,t){for(let i=this.bodyCount-1;i>=0;i--){let[r,s]=qt(-this.pose[i*4+2],e-this.pose[i*4],t-this.pose[i*4+1]);if(Math.abs(r)<=this.shape[i*4]*.5&&Math.abs(s)<=this.shape[i*4+1]*.5)return{body:i,local:[r,s]}}return null}kineticEnergy(){let e=0;for(let t=0;t<this.bodyCount;t++){if(!this.dynamic[t])continue;let i=t*4,r=this.velocity;e+=.5*this.shape[i+2]*(r[i]*r[i]+r[i+1]*r[i+1])+.5*this.shape[i+3]*r[i+2]*r[i+2]}return e}maxJointError(){let e=0,t=new Float64Array(3);for(let i=0;i<this.jointCount;i++){if(this.info[i*D]!==Vt)continue;let r=i*T;this.jointC(this.info[i*D+1],this.info[i*D+2],r,this.pose,this.data,0,t);for(let s=0;s<2;s++)this.data[r+$+s]===1/0&&(e=Math.max(e,Math.abs(t[s])))}return e}isContactPairIgnored(e,t){return Do(this.noCollide,this.noCollideCount,Ji(e,t))}};function qt(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function Sn(o,e,t,i){let r=Math.cos(o[e*4+2]),s=Math.sin(o[e*4+2]);return[r*t-s*i+o[e*4],s*t+r*i+o[e*4+1]]}var gr=`
3602
+ `),$t=class{device;pipelines;layout;watch;table;events;params;setSlots;capacity;group=null;bound=[];flip=0;bodies;constructor(e,t,i,r,s,a=3){this.device=e,this.bodies=t,this.capacity=s,this.setSlots=2**Math.ceil(Math.log2(Math.max(64,2*r)));let n=e.createShaderModule({label:"contact events",code:a===2?gc:gr}),l=["read-only-storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","storage","uniform"];this.layout=e.createBindGroupLayout({label:"contact events",entries:l.map((d,p)=>({binding:p,visibility:GPUShaderStage.COMPUTE,buffer:{type:d}}))});let c=e.createPipelineLayout({bindGroupLayouts:[this.layout]}),f=d=>e.createComputePipeline({label:`contact events ${d}`,layout:c,compute:{module:n,entryPoint:d}});this.pipelines={touching:f("touching"),ended:f("ended")};let u=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.watch=e.createBuffer({label:"contact watch",size:Math.max(16,i*4),usage:u}),this.table=e.createBuffer({label:"contact sets",size:(Yo+4*this.setSlots)*4,usage:u}),this.events=e.createBuffer({label:"contact events",size:s*$i*16,usage:u}),this.params=e.createBuffer({label:"contact params",size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST})}setWatched(e,t){let i=e.map((r,s)=>[r,t[s]?1:0]).sort((r,s)=>r[0]-s[0]);for(let r=0;r<i.length;){let s=r+1;for(;s<i.length&&i[s][0]===i[s-1][0]+1;)s++;this.device.queue.writeBuffer(this.watch,i[r][0]*4,Uint32Array.from(i.slice(r,s),a=>a[1])),r=s}}run(e,t,i,r){let s=this.device,a=[this.bodies,e.manifolds,e.contacts,e.counters,this.watch,this.table,this.events,this.params];(!this.group||a.some((d,p)=>d!==this.bound[p]))&&(this.bound=a,this.group=s.createBindGroup({layout:this.layout,entries:a.map((d,p)=>({binding:p,resource:{buffer:d}}))})),this.flip=1-this.flip;let[n,l]=this.flip?[0,this.setSlots]:[this.setSlots,0],c=new Uint32Array([n,l,this.setSlots-1,this.capacity,t,i,0,0]);new Float32Array(c.buffer)[6]=r,s.queue.writeBuffer(this.params,0,c);let f=s.createCommandEncoder({label:"contact events"});f.clearBuffer(this.table,(Yo+2*l)*4,2*this.setSlots*4);let u=f.beginComputePass({label:"contact events"});u.setBindGroup(0,this.group),u.setPipeline(this.pipelines.touching),u.dispatchWorkgroups(Math.max(1,Math.ceil(t/64))),u.setPipeline(this.pipelines.ended),u.dispatchWorkgroups(Math.ceil(this.setSlots/64)),u.end(),s.queue.submit([f.finish()])}async read(){let e=this.device,t=16+this.capacity*$i*16,i=e.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=e.createCommandEncoder({label:"read contact events"});r.copyBufferToBuffer(this.table,0,i,0,16),r.copyBufferToBuffer(this.events,0,i,16,t-16),r.clearBuffer(this.table,0,16),e.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);i.unmap(),i.destroy();let a=new Uint32Array(s),n=new Float32Array(s),l=Math.min(a[0],this.capacity),c=[];for(let f=0;f<l;f++){let u=4+f*$i*4;c.push({kind:a[u],a:a[u+1],b:a[u+2],step:a[u+3],point:[n[u+4],n[u+5],n[u+6]],impulse:n[u+7],normal:[n[u+8],n[u+9],n[u+10]]})}return c.sort((f,u)=>f.step-u.step),{events:c,dropped:a[0]-l,full:a[1]!==0}}destroy(){for(let e of[this.watch,this.table,this.events,this.params])e.destroy()}};var Xe=Object.freeze({BOX:"box",SPHERE:"sphere",CAPSULE:"capsule",CYLINDER:"cylinder",CONVEX_HULL:"convex-hull"}),_e=o=>o&&typeof o.x=="number"?[o.x,o.y,o.z,...typeof o.w=="number"?[o.w]:[]]:Array.from(o),yr=new WeakMap,Xi=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o},ds=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},hs=class o{static async create({device:e,...t}={}){if(t.scene&&yr.has(t.scene))throw Error("This scene already has an AVBD world");let i=!e;e||({device:e}=await Xo({preferredLimits:{maxStorageBuffersPerShaderStage:9,maxStorageBufferBindingSize:512*1024*1024,maxBufferSize:1024*1024*1024}}));let r;try{return await cs(e),r=new o(e,t),r.ownsDevice=i,r.scene&&r.autoAttach&&r.attachToScene(),r}catch(s){throw r?r.dispose():i&&e.destroy(),s}}constructor(e,{scene:t,gravity:i=[0,-10,0],timeStep:r=1/60,iterations:s=10,capacity:a=4096,syncMeshes:n=!0,maxSyncedBodies:l=2048,broadphase:c="auto",solverMode:f="auto",bvh:u={},autoAttach:d=!0,maxContactPairs:p=8192,maxContactEvents:m=4096,sleeping:h=!1}={}){if(this.device=e,this.scene=t,this.autoAttach=d,this.syncEnabled=n,this.maxSyncedBodies=l,!Number.isInteger(a)||a<1||!Number.isInteger(l)||l<0)throw Error("capacity and maxSyncedBodies must be valid integer counts");if(this.capacity=a,this.collisionOptions={broadphase:c,bvh:u,solverMode:f},this.ref=new jt,this.ref.dt=ds(r,"timeStep"),!Number.isInteger(s)||s<1)throw Error("iterations must be a positive integer");this.ref.iterations=s,this.aggregates=[],this.constraints=new Set,this.contactListeners=new Set;for(let[y,v]of Object.entries({maxContactPairs:p,maxContactEvents:m}))if(!Number.isInteger(v)||v<1)throw Error(`${y} must be a positive integer`);if(this.contactOptions={maxContactPairs:p,maxContactEvents:m},this.bodyReadback=new Jt(e),this.sleepOptions=h,h!==!1&&h!==!0&&(typeof h!="object"||h===null))throw Error("sleeping must be a boolean or options object");this.commands=new Map,this.steps=0,this.errors=[],this.deviceLost=null;let b=new WeakRef(this);e.lost.then(y=>{let v=b.deref();!v||v.disposed||(v.deviceLost={reason:y.reason,message:y.message},v.errors.push(`WebGPU device lost: ${y.message||y.reason}`),v.dispose())}),this.setGravity(i),this.errorListener=y=>this.errors.push(y.error.message),e.addEventListener("uncapturederror",this.errorListener),t&&yr.set(t,this)}setGravity(e){let t=_e(e);if(t.length!==3||!t.every(Number.isFinite))throw Error("gravity requires three finite components");let i=Math.hypot(...t);this.gravity=-i,this.up=i?t.map(r=>-r/i):[0,1,0],this.gpu&&(this.gpu.params.gravity=this.gravity,this.gpu.params.up=this.up),this.wakeAll()}get solverDecision(){return this.initialize(),{...this.gpu.solverDecision}}setSolverMode(e){this.initialize();let t=this.gpu.setSolverMode(e);return this.collisionOptions.solverMode=e,{...t}}initialize(){if(this.assertAlive(),this.gpu)return;if(this.syncEnabled&&this.aggregates.filter(r=>r.sync).length>this.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(this.aggregates.length>this.capacity)throw Error(`Body capacity ${this.capacity} exceeded; size the world before initialization`);this.gpu=new Vt(this.device,this.ref,us(this.ref,{bodyCapacity:Math.max(1,this.capacity),spatialSort:!0,...this.collisionOptions})),Object.assign(this.gpu.params,{gravity:this.gravity,up:this.up}),this.gpu.externalBeforeStep=r=>{this.sleepManager?.before(r),this.flushCommands(r)},this.gpu.externalAfterStep=r=>this.sleepManager?.after(r);let e=new Map([...this.constraints].map(r=>[r.force,r])),t=[],i=0;for(let r of this.ref.forces){let s=e.get(r);s&&(s.attach(i++,!1),(s.type==="fixed"||s.type==="spring"&&s.rest===void 0)&&t.push({slot:s.slot,spring:s.type==="spring"}))}this.gpu.captureConstraintFrames(t),this.byGpuIndex=new Map(this.aggregates.map(r=>[this.gpu.gpuIndex(r.index),r.body]));for(let r of this.aggregates)(r.disposed||r.group!==4294967295||r.collidesWith!==4294967295)&&this.gpu.setFilters([this.gpu.gpuIndex(r.index)],[r.disposed?0:r.group],[r.disposed?0:r.collidesWith]);for(let r of this.aggregates)r.isTrigger&&this.gpu.setSensor(r.body.gpuIndex,!0);for(let r of this.aggregates)r.restitution>0&&this.gpu.setRestitution(r.body.gpuIndex,r.restitution);this.contactListeners.size&&this.initializeContacts(),this.sleepOptions&&(this.sleepManager=new hi(this,this.sleepOptions===!0?{}:this.sleepOptions))}addAggregate(e,t,i={}){return new ps(e,t,i,this)}addAggregates(e){return e.map(({mesh:t,type:i,options:r})=>this.addAggregate(t,i,r))}addConstraint(e,t,i={}){return new mr(this,e,t,i)}addJoint(e,t,i={}){return this.addConstraint(e,t,{...i,type:i.type??"ball"})}addWeld(e,t,i={}){return this.addConstraint(e,t,{...i,type:"fixed"})}addSpring(e,t,i={}){return this.addConstraint(e,t,{...i,type:"spring"})}enableSleeping(e={}){return this.assertAlive(),this.sleepOptions=e,this.gpu&&!this.sleepManager&&(this.sleepManager=new hi(this,e)),this}addMotor(e,t,i={}){return this.addConstraint(e,t,{...i,type:"motor"})}addHinge(e,t,i={}){return new br(this,e,t,i)}initializeContacts(){if(this.contactWatch)return;let{maxContactPairs:e,maxContactEvents:t}=this.contactOptions;this.contactWatch=new $t(this.device,this.gpu.bodyBuffer,this.capacity,e,t);let i=this.aggregates.filter(r=>!r.disposed);this.contactWatch.setWatched(i.map(r=>r.body.gpuIndex),i.map(()=>!0))}onContact(e){if(this.assertAlive(),typeof e!="function")throw Error("A contact callback is required");return this.contactListeners.add(e),this.gpu&&this.initializeContacts(),()=>this.contactListeners.delete(e)}async readContactEvents(){return this.initialize(),this.initializeContacts(),this.contactPending?this.contactPending:(this.contactPending=this.contactWatch.read().then(e=>({...e,events:e.events.map(t=>({...t,type:t.kind===Xt?"begin":"end",isTrigger:!!(this.byGpuIndex.get(t.a)?.aggregate.isTrigger||this.byGpuIndex.get(t.b)?.aggregate.isTrigger),a:this.byGpuIndex.get(t.a),b:this.byGpuIndex.get(t.b)}))})).finally(()=>this.contactPending=null),this.contactPending)}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}async sphereCast(e,t,i,r={}){let[s]=await this.sphereCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}async sphereCastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){this.assertAlive(),Xi(i),this.initialize(),this.flushCommands();for(let a of t)if(a.aggregate.world!==this||a.disposed)throw Error("Ignored cast bodies must belong to this world and be alive");return(await this.gpu.sphereCastAll(e.map(a=>({...a,origin:_e(a.origin),direction:_e(a.direction)})),{ignore:t.map(a=>a.gpuIndex),collidesWith:i,includeTriggers:r})).map(a=>a?{...a,body:this.byGpuIndex.get(a.index)??null}:null)}async raycastAll(e,{ignore:t=[],collidesWith:i=4294967295,includeTriggers:r=!0}={}){Xi(i),this.initialize(),this.flushCommands();let s=e.map(n=>{let l=_e(n.origin),c=_e(n.direction);if(l.length!==3||c.length!==3||![...l,...c].every(Number.isFinite)||!Math.hypot(...c))throw Error("A ray requires a finite origin and nonzero direction");return n.maxDistance!==void 0&&ds(n.maxDistance,"maxDistance",!0),{...n,origin:l,direction:c}});for(let n of t)if(n.aggregate.world!==this)throw Error("Ignored ray bodies must belong to this world");return(await this.gpu.raycastAll(s,{ignore:t.map(n=>n.gpuIndex),collidesWith:i,includeTriggers:r})).map(n=>n?{...n,body:this.byGpuIndex.get(n.index)??null}:null)}getRenderBinding(e=this.device){if(this.initialize(),e!==this.device)throw Error("Direct GPU rendering must share the physics GPUDevice");return{device:e,buffer:this.bodyBuffer,stride:160,positionOffset:0,rotationOffset:16,sizeOffset:64,count:this.gpu.bodyCount}}wakeAll(){this.sleepManager&&(this.sleepManager.wakeRequested=!0)}async readSleepStats(){return this.initialize(),this.sleepManager?this.sleepManager.readStats():{sleeping:0,wakeRequested:!1}}assertAlive(){if(this.disposed)throw Error("AVBD world is disposed")}queue(e,t,i,r){this.assertAlive(),e.assertAlive();let s=_e(r);if(s.length!==(i==="rotation"?4:3)||!s.every(Number.isFinite))throw Error(`${i} requires ${i==="rotation"?4:3} finite components`);let a=this.commands.get(e);return a||this.commands.set(e,a={mask:0}),a.mask|=t,a[i]=s,this.wakeAll(),a}flushCommands(e){if(this.initialize(),this.gpu.flushPropertyEdits(e),!this.commands.size)return;this.sleepManager?.wakeRequested&&this.sleepManager.before(e);let t=this.commands.size,i=0;for(let h of this.commands.values())i|=h.mask;if(i===1||i===2){if(!this.velocityIndices||this.velocityIndices.length<t){let v=Math.max(t,(this.velocityIndices?.length??0)*2,64);this.velocityIndices=new Uint32Array(v),this.velocityValues=new Float32Array(v*3)}let h=0;for(let[v,B]of this.commands)this.velocityIndices[h]=v.aggregate.gpuSlot??this.gpu.gpuIndex(v.aggregate.index),this.velocityValues.set(i===2?B.angular:B.velocity,h++*3);let b=i===2?"angularVelocityBatch":"velocityBatch",y=this[b]??=new Lt(this.gpu,3,i===2?"angular":"linear");y.solver=this.gpu,y.submitValidated(this.velocityIndices.subarray(0,t),this.velocityValues.subarray(0,t*3),e),this.lastCommandBatch={bodies:t,path:i===2?"packed-angular-velocities":"packed-velocities",uploadedBytes:t*16},this.commands.clear();return}let r=!0;for(let h of this.commands.values())if((h.mask&-4)!==0){r=!1;break}if(r){(!this.motionData||this.motionData.length<t*8)&&(this.motionData=new Uint32Array(Math.max(t*8,(this.motionData?.length??0)*2,512)),this.motionFloats=new Float32Array(this.motionData.buffer));let h=0;for(let[b,y]of this.commands){let v=h++*8;this.motionData[v]=b.aggregate.gpuSlot??this.gpu.gpuIndex(b.aggregate.index),this.motionData[v+1]=y.mask,y.velocity&&this.motionFloats.set(y.velocity,v+2),y.angular&&this.motionFloats.set(y.angular,v+5)}this.motionBatch??=new Ht(this.gpu,3),this.motionBatch.submitValidated(this.motionData.subarray(0,t*8),e),this.lastCommandBatch={bodies:t,path:"packed-motion",uploadedBytes:t*32},this.commands.clear();return}let s=t*128,a=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize);if(s>a)throw Error("Body command batch exceeds GPU buffer limits");(!this.commandData||this.commandData.byteLength<s)&&(this.commandData=new ArrayBuffer(Math.min(a,Math.max(s,(this.commandData?.byteLength??0)*2,128))));let n=this.commandData,l=new Float32Array(n),c=new Uint32Array(n);l.fill(0,0,t*32);let f=0;for(let[h,b]of this.commands){let y=f++*32;c[y]=h.aggregate.gpuSlot??this.gpu.gpuIndex(h.aggregate.index),c[y+1]=b.mask;for(let[v,B]of[["velocity",4],["angular",8],["impulse",12],["torque",16],["position",20],["rotation",24],["pointImpulse",28]])b[v]&&l.set(b[v],y+B)}this.commands.clear(),(!this.commandBuffer||this.commandBuffer.size<s)&&(this.commandBuffer?.destroy(),this.commandBuffer=this.device.createBuffer({size:Math.min(a,Math.max(s*2,128)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST})),this.device.queue.writeBuffer(this.commandBuffer,0,n,0,s),this.commandPipeline??=this.device.createComputePipeline({layout:"auto",compute:{module:this.device.createShaderModule({code:`const DISPATCH_STRIDE = ${this.device.limits.maxComputeWorkgroupsPerDimension*64}u;
3603
+ ${Pn}`,label:"AVBD body commands"}),entryPoint:"applyCommands"}}),(!this.commandBinding||this.boundCommandBody!==this.gpu.bodyBuffer||this.boundCommandBuffer!==this.commandBuffer||this.boundCommandBytes!==s)&&(this.commandBinding=this.device.createBindGroup({layout:this.commandPipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.gpu.bodyBuffer}},{binding:1,resource:{buffer:this.commandBuffer,size:s}}]}),this.boundCommandBody=this.gpu.bodyBuffer,this.boundCommandBuffer=this.commandBuffer,this.boundCommandBytes=s);let u=e??this.device.createCommandEncoder(),d=u.beginComputePass();d.setPipeline(this.commandPipeline),d.setBindGroup(0,this.commandBinding);let p=Math.ceil(f/64),m=this.device.limits.maxComputeWorkgroupsPerDimension;d.dispatchWorkgroups(Math.min(p,m),Math.ceil(p/m)),d.end(),e||this.device.queue.submit([u.finish()]),this.lastCommandBatch={bodies:t,path:"aggregate-commands",uploadedBytes:s}}step(){this.initialize(),this.gpu.step(),this.steps++,this.contactWatch&&(this.contactWatch.run(this.gpu.contactStorage,this.gpu.manifoldCapacity,this.steps,this.ref.dt),this.contactListeners.size&&!this.contactPending&&this.readContactEvents().then(({events:e,dropped:t,full:i})=>{if(!this.disposed){(t||i)&&this.errors.push(`Contact event storage full: ${t} events dropped; increase maxContactEvents/maxContactPairs`);for(let r of e)for(let s of this.contactListeners)s(r)}}).catch(e=>{this.disposed||this.errors.push(e.message)})),this.syncEnabled&&!this.syncPending&&this.syncMeshes().catch(e=>this.errors.push(e.message)),this.steps%20===0&&!this.counterPending&&(this.counterPending=!0,this.gpu.readCounters().then(e=>{this.disposed||(this.counters=e,this.gpu.adapt(e),(e.overflow||e.clashes)&&this.errors.push(`Collision capacity/color conflict: ${JSON.stringify(e)}`))}).catch(e=>{this.disposed||this.errors.push(e.message)}).finally(()=>this.counterPending=!1))}async syncMeshes(){if(this.initialize(),this.flushCommands(),this.syncPending)return this.syncPending;let e=this.aggregates.filter(t=>t.sync&&!t.disposed);return this.syncPending=this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.body.gpuIndex),!0).then(t=>{if(!this.disposed){for(let i=0;i<e.length;i++)e[i].disposed||e[i].syncPose(t,i*8);this.lastSyncBytes=t.byteLength}}).finally(()=>this.syncPending=null),this.syncPending}get bodyBuffer(){return this.initialize(),this.gpu.bodyBuffer}async readBodies(e){if(this.initialize(),this.flushCommands(),e){for(let t of e)if(t.aggregate.world!==this)throw Error("Selected bodies must belong to this world");return this.bodyReadback.read(this.bodyBuffer,e.map(t=>t.gpuIndex))}return this.gpu.readBodies()}dispose(){if(!this.disposed){this.disposed=!0,this.observer&&this.scene?.onBeforeRenderObservable.remove(this.observer),this.disposeObserver&&this.scene?.onDisposeObservable.remove(this.disposeObserver),this.scene&&yr.get(this.scene)===this&&yr.delete(this.scene),this.device.removeEventListener("uncapturederror",this.errorListener),this.gpu?.destroy(),this.contactWatch?.destroy(),this.sleepManager?.dispose(),this.commandBuffer?.destroy(),this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback.destroy(),this.commandData=this.velocityIndices=this.velocityValues=null,this.commandBinding=null;for(let e of this.aggregates)e.disposed=!0,e.body.disposed=!0;this.commands.clear(),this.contactListeners.clear();for(let e of this.constraints)e.disposed=!0;this.constraints.clear(),this.ownsDevice&&this.device.destroy()}}attachToScene(e=this.scene){if(this.assertAlive(),!e)throw Error("A Babylon scene is required");if(this.observer&&e===this.scene)return this;if(this.observer)throw Error("AVBD world is already attached to another scene");this.scene=e,this.disposeObserver=e.onDisposeObservable.addOnce(()=>this.dispose());let t=0;return this.observer=e.onBeforeRenderObservable.add(()=>{t+=Math.min(.05,e.getEngine().getDeltaTime()/1e3);for(let i=0;t>=this.ref.dt&&i<3;i++)this.step(),t-=this.ref.dt}),this}},ps=class{constructor(e,t,{mass:i=1,friction:r=.6,restitution:s=0,allowSleep:a,isTrigger:n=!1,sync:l=!0,group:c=4294967295,collidesWith:f=4294967295}={},u){if(u=u instanceof hs?u:yr.get(u??e.getScene()),!u)throw Error("Create an AVBD world for this scene before adding aggregates");if(u.assertAlive(),typeof t=="number"&&(t=[Xe.SPHERE,Xe.CAPSULE,Xe.CYLINDER,Xe.BOX,Xe.CONVEX_HULL][t]),ds(i,"mass",!0),ds(r,"friction",!0),!Number.isFinite(s)||s<0||s>1)throw Error("restitution must be between 0 and 1");if(e.parent)throw Error("AVBD aggregate meshes must have no parent; bake the parent transform first");if(!Object.values(Xe).includes(t))throw Error(`Unsupported AVBD shape: ${t}`);if(u.aggregates.length>=u.capacity)throw Error("AVBD body capacity exceeded");if(this.world=u,this.mesh=e,this.sync=l,this.type=t,a!==void 0&&typeof a!="boolean")throw Error("allowSleep must be a boolean");if(this.allowSleep=a??!0,typeof n!="boolean")throw Error("isTrigger must be a boolean");if(this.isTrigger=n,this.restitution=s,a===!0&&!u.sleepOptions&&u.enableSleeping(),this.group=Xi(c),this.collidesWith=Xi(f),u.gpu&&u.syncEnabled&&l&&u.aggregates.filter(g=>g.sync&&!g.disposed).length>=u.maxSyncedBodies)throw Error("For large scenes use syncMeshes:false and draw the GPU body buffer directly");if(![Xe.BOX,Xe.SPHERE,Xe.CAPSULE].includes(t)&&u.device.limits.maxStorageBuffersPerShaderStage<9)throw Error("Convex hull collisions require nine storage buffer bindings");e.computeWorldMatrix(!0),e.refreshBoundingInfo();let d=e.getBoundingInfo().boundingBox,p=e.scaling;if([p.x,p.y,p.z].some(g=>g<=0))throw Error("Bake negative or zero mesh scaling before creating an aggregate");let m=d.maximum.subtract(d.minimum).multiply(p),h=d.center.multiply(p),b=e.rotationQuaternion?.clone()||e.rotation.toQuaternion();this.localCenter=h,this.localRotation=b.constructor.Identity();let y;if(t===Xe.BOX)y=new De(u.ref,_e(m),1,r,[0,0,0]);else if(t===Xe.SPHERE){if(Math.max(m.x,m.y,m.z)-Math.min(m.x,m.y,m.z)>1e-4)throw Error("Sphere aggregate requires uniform dimensions");y=Mi(u.ref,m.x/2,1,r,[0,0,0])}else if(t===Xe.CAPSULE){if(Math.abs(m.x-m.z)>1e-4)throw Error("Capsule aggregate requires equal X and Z diameters; bake an upright Y-axis capsule mesh");y=Hi(u.ref,m.x/2,m.y,1,r,[0,0,0])}else{let g=e.getVerticesData("position");if(!g)throw Error("Hull aggregate requires mesh positions");let A=Float32Array.from(g,(w,E)=>w*[p.x,p.y,p.z][E%3]),x=or(A);if(!x)throw Error("Hull aggregate requires a closed shape with nonzero volume");this.localCenter=new e.position.constructor(...x.center),this.localRotation=b.constructor.FromArray(x.rotation),y=Gr(u.ref,x,1,r,[0,0,0])}let v=i/y.mass;y.mass=i,y.moment.forEach((g,A)=>y.moment[A]=g*v);let B=e.position.add(this.localCenter.applyRotationQuaternion(b));if(y.positionLin.set(_e(B)),y.positionAng.set(b.multiply(this.localRotation).asArray()),this.rigid=y,this.index=u.aggregates.length,this.body=new ms(this),u.aggregates.push(this),e.rotationQuaternion=b,u.gpu){if(this.gpuSlot=u.gpu.addBody(y),this.gpuSlot<0)throw Error("AVBD body capacity exceeded");u.byGpuIndex.set(this.gpuSlot,this.body),u.gpu.setFilters([this.gpuSlot],[this.group],[this.collidesWith]),(this.isTrigger||u.gpu.sensorsEnabled)&&u.gpu.setSensor(this.gpuSlot,this.isTrigger),this.restitution>0&&u.gpu.setRestitution(this.gpuSlot,this.restitution),u.contactWatch?.setWatched([this.gpuSlot],[!0]),u.sleepManager?.register(this.gpuSlot,y.mass,this.allowSleep),u.wakeAll?.()}}syncPose(e,t){let i=t??this.body.gpuIndex*40,r=this.localRotation.constructor.FromArray(e,i+4).multiply(this.localRotation.conjugate());this.mesh.rotationQuaternion.copyFrom(r),this.mesh.position.copyFrom(new this.mesh.position.constructor(...e.subarray(i,i+3)).subtract(this.localCenter.applyRotationQuaternion(r)))}dispose(){if(this.disposed)return;let e=this.world;for(let t of[...e.constraints])(t.bodyA===this.body||t.bodyB===this.body)&&t.dispose();if(e.gpu){let t=this.body.gpuIndex;e.queue(this.body,16,"velocity",[0,0,0]),e.gpu.setFilters([t],[0],[0]),e.gpu.setRestitution(t,0)}this.rigid.mass=0,this.rigid.moment.fill(0),this.sync=!1,this.disposed=this.body.disposed=!0,e.sleepManager?.register(this.gpuSlot??e.gpu?.gpuIndex(this.index)??this.index,0),e.wakeAll?.()}},ms=class{constructor(e){this.aggregate=e}assertAlive(){if(this.disposed||this.aggregate.disposed)throw Error("AVBD body is disposed")}get gpuIndex(){this.assertAlive();let e=this.aggregate.world;return e.initialize(),this.aggregate.gpuSlot??e.gpu.gpuIndex(this.aggregate.index)}setLinearVelocity(e){return this.aggregate.world.queue(this,1,"velocity",e),this}setAngularVelocity(e){return this.aggregate.world.queue(this,2,"angular",e),this}applyImpulse(e,t){let i=t?_e(t):null;if(i&&(i.length!==3||!i.every(Number.isFinite)))throw Error("Impulse point requires three finite components");let r=this.aggregate.world,s=_e(e),a=r.commands.get(this),n=s.map((c,f)=>c+(a?.impulse?.[f]||0)),l=r.queue(this,4,"impulse",n);if(t){let c=[i[1]*s[2]-i[2]*s[1],i[2]*s[0]-i[0]*s[2],i[0]*s[1]-i[1]*s[0]],f=l.torque||[0,0,0];l.torque=_e(c).map((u,d)=>u+f[d]),l.pointImpulse=s.map((u,d)=>u+(l.pointImpulse?.[d]||0))}return this}applyAngularImpulse(e){let t=this.aggregate.world,i=t.commands.get(this)?.torque||[0,0,0];return t.queue(this,4,"torque",_e(e).map((r,s)=>r+i[s])),this}applyForce(e,t){return this.applyImpulse(_e(e).map(i=>i*this.aggregate.world.ref.dt),t)}applyTorque(e){return this.applyAngularImpulse(_e(e).map(t=>t*this.aggregate.world.ref.dt))}setCollisionGroups(e,t=4294967295){this.assertAlive();let i=this.aggregate,r=Xi(e),s=Xi(t);return i.group=r,i.collidesWith=s,i.world.gpu&&i.world.gpu.setFilters([this.gpuIndex],[i.group],[i.collidesWith]),i.world.wakeAll?.(),this}setTrigger(e){if(this.assertAlive(),typeof e!="boolean")throw Error("isTrigger must be a boolean");let t=this.aggregate;return t.isTrigger=e,t.world.gpu&&t.world.gpu.setSensor(this.gpuIndex,e),t.world.wakeAll(),this}setRestitution(e){if(this.assertAlive(),!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");let t=this.aggregate;return t.restitution=e,t.world.gpu&&t.world.gpu.setRestitution(this.gpuIndex,e),t.world.wakeAll(),this}async readState(){let e=await this.aggregate.world.readBodies([this]),t=this.aggregate.rigid.mass;return{position:Array.from(e.subarray(0,3)),rotation:Array.from(e.subarray(4,8)),linearVelocity:Array.from(e.subarray(32,35)),angularVelocity:Array.from(e.subarray(36,39)),mass:t,effectiveMass:e[19],sleeping:t>0&&e[19]===0}}wakeUp(){return this.assertAlive(),this.aggregate.world.wakeAll(),this}setSleepEnabled(e){if(this.assertAlive(),typeof e!="boolean")throw Error("Sleep eligibility must be a boolean");let t=this.aggregate,i=t.world;return t.allowSleep=e,e&&!i.sleepOptions&&i.enableSleeping(),i.gpu&&i.sleepManager?.register(this.gpuIndex,t.rigid.mass,e),i.wakeAll(),this}teleport(e,t=this.aggregate.mesh.rotationQuaternion){let i=t.clone().normalize(),r=this.aggregate;return r.world.queue(this,8,"position",_e(e.add(r.localCenter.applyRotationQuaternion(i)))),r.world.queue(this,8,"rotation",i.multiply(r.localRotation).asArray()),this}};var pi=class{solver;bodyA;bodyB;J=new Float64Array(12);H=new Float64Array(36);C=new Float64Array(4);fmin=new Float64Array(4).fill(-1/0);fmax=new Float64Array(4).fill(1/0);stiffness=new Float64Array(4).fill(1/0);fracture=new Float64Array(4).fill(1/0);penalty=new Float64Array(4);lambda=new Float64Array(4);constructor(e,t,i){this.solver=e,this.bodyA=t,this.bodyB=i,e.forces.push(this),t?.forces.push(this),i.forces.push(this)}destroy(){let e=this.solver.forces,t=e.indexOf(this);t>=0&&e.splice(t,1),this.unlinkFromBodies()}unlinkFromBodies(){for(let e of[this.bodyA,this.bodyB]){if(!e)continue;let t=e.forces.indexOf(this);t>=0&&e.forces.splice(t,1)}}disable(){this.stiffness.fill(0),this.penalty.fill(0),this.lambda.fill(0)}};var _n=o=>o<0?-1:o>0?1:0,Yi=(o,e)=>o<e?o:e,yc=(o,e)=>o>e?o:e,Qi=(o,e,t)=>yc(e,Yi(t,o));function vr(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function mi(o,e,t){let i=Math.cos(o[2]),r=Math.sin(o[2]);return[i*e-r*t+o[0],r*e+i*t+o[1]]}function _t(o,e,t,i,r){o[e*3]=t,o[e*3+1]=i,o[e*3+2]=r}var bs=class extends pi{rA;rB;C0=new Float64Array(3);torqueArm;restAngle;constructor(e,t,i,r,s,a=[1/0,1/0,1/0],n=1/0){super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[1]],this.stiffness[0]=a[0],this.stiffness[1]=a[1],this.stiffness[2]=a[2],this.fmax[2]=n,this.fmin[2]=-n,this.fracture[2]=n,this.restAngle=(t?t.position[2]:0)-i.position[2];let l=(t?t.size[0]:0)+i.size[0],c=(t?t.size[1]:0)+i.size[1];this.torqueArm=l*l+c*c}rows(){return 3}evaluate(e){let t=this.bodyA?mi(this.bodyA.position,this.rA[0],this.rA[1]):this.rA,i=mi(this.bodyB.position,this.rB[0],this.rB[1]);e[0]=t[0]-i[0],e[1]=t[1]-i[1],e[2]=((this.bodyA?this.bodyA.position[2]:0)-this.bodyB.position[2]-this.restAngle)*this.torqueArm}initialize(){return this.evaluate(this.C0),this.stiffness[0]!==0||this.stiffness[1]!==0||this.stiffness[2]!==0}Cn=new Float64Array(3);computeConstraint(e){this.evaluate(this.Cn);for(let t=0;t<3;t++)this.C[t]=this.stiffness[t]===1/0?this.Cn[t]-this.C0[t]*e:this.Cn[t]}computeDerivatives(e){let{J:t,H:i}=this;if(i.fill(0,0,27),e===this.bodyA){let r=vr(e.position[2],this.rA[0],this.rA[1]);_t(t,0,1,0,-r[1]),_t(t,1,0,1,r[0]),_t(t,2,0,0,this.torqueArm),i[8]=-r[0],i[17]=-r[1]}else{let r=vr(e.position[2],this.rB[0],this.rB[1]);_t(t,0,-1,0,r[1]),_t(t,1,0,-1,-r[0]),_t(t,2,0,0,-this.torqueArm),i[8]=r[0],i[17]=r[1]}}},gs=class extends pi{rA;rB;rest;constructor(e,t,i,r,s,a,n=-1){if(super(e,t,i),this.rA=[r[0],r[1]],this.rB=[s[0],s[1]],this.stiffness[0]=a,this.rest=n,this.rest<0){let l=mi(t.position,r[0],r[1]),c=mi(i.position,s[0],s[1]);this.rest=Math.hypot(l[0]-c[0],l[1]-c[1])}}rows(){return 1}initialize(){return!0}delta(){let e=mi(this.bodyA.position,this.rA[0],this.rA[1]),t=mi(this.bodyB.position,this.rB[0],this.rB[1]);return[e[0]-t[0],e[1]-t[1]]}computeConstraint(){let e=this.delta();this.C[0]=Math.hypot(e[0],e[1])-this.rest}computeDerivatives(e){let t=this.delta(),i=t[0]*t[0]+t[1]*t[1];if(i===0)return;let r=Math.sqrt(i),s=t[0]/r,a=t[1]/r,n=(1-s*s)/r,l=(0-s*a)/r,c=(0-a*s)/r,f=(1-a*a)/r,u=e===this.bodyA,d=u?this.rA:this.rB,p=vr(e.position[2],-d[1],d[0]),m=vr(e.position[2],d[0],d[1]),h=n*p[0]+l*p[1],b=c*p[0]+f*p[1],y=s*m[0]+a*m[1],v=s*p[0]+a*p[1];u?_t(this.J,0,s,a,v):_t(this.J,0,-s,-a,-v);let B=u?-y-y:y+y,g=this.H;g[0]=n,g[1]=l,g[2]=h,g[3]=c,g[4]=f,g[5]=b,g[6]=h,g[7]=b,g[8]=B}},ys=class extends pi{rows(){return 0}initialize(){return!0}computeConstraint(){}computeDerivatives(){}},vs=class extends pi{speed;constructor(e,t,i,r,s){super(e,t,i),this.speed=r,this.fmax[0]=s,this.fmin[0]=-s}rows(){return 1}initialize(){return!0}computeConstraint(){let e=this.bodyA?this.bodyA.position[2]-this.bodyA.initial[2]:0,t=this.bodyB.position[2]-this.bodyB.initial[2];this.C[0]=e-t-this.speed*this.solver.dt}computeDerivatives(e){_t(this.J,0,0,0,e===this.bodyA?1:-1),this.H.fill(0,0,9)}};var xs=()=>({dt:1/60,gravity:-10,iterations:10,beta:1e5,alpha:.99,gamma:.99,postStabilize:!0,stiffnessRescale:!1,vbd:!1,vbdStiffness:1e6,matchNearest:!1}),As=()=>({...xs(),postStabilize:!1,alpha:.95,matchNearest:!0});var Ki=(o,e)=>o>e?o*2097152+e:e*2097152+o;function vc(o,e,t){let i=0,r=e;for(;i<r;){let s=i+r>>>1;o[s]<t?i=s+1:r=s}return i}function Qo(o,e,t){let i=vc(o,e,t);return i<e&&o[i]===t}var ws=class{cellSize=1;pairs=new Float64Array(1024);pairCount=0;configuredFor=-1;large=new Int32Array(0);isLarge=new Uint8Array(0);cellX=new Int32Array(0);cellY=new Int32Array(0);tableSize=0;bucketStart=new Int32Array(0);sorted=new Int32Array(0);configure(e,t){this.configuredFor=e;let i=Float64Array.from({length:e},(n,l)=>t[l*4+1]).sort(),r=e>0?i[e>>1]:1,s=0;for(let n=0;n<e;n++)i[n]<=4*r&&(s=Math.max(s,i[n]));this.cellSize=Math.max(2*s,.001),this.isLarge=new Uint8Array(e);let a=[];for(let n=0;n<e;n++)t[n*4+1]>s&&(this.isLarge[n]=1,a.push(n));for(this.large=Int32Array.from(a),this.cellX=new Int32Array(e),this.cellY=new Int32Array(e),this.tableSize=1;this.tableSize<2*e;)this.tableSize<<=1;this.bucketStart=new Int32Array(this.tableSize+1),this.sorted=new Int32Array(e)}hash(e,t){return(Math.imul(e,73856093)^Math.imul(t,19349663))&this.tableSize-1}findPairs(e,t,i,r,s,a){e!==this.configuredFor&&this.configure(e,i),this.pairCount=0;let{cellX:n,cellY:l,isLarge:c,bucketStart:f,sorted:u}=this,d=1/this.cellSize;f.fill(0);for(let h=0;h<e;h++)c[h]||(n[h]=Math.floor(t[h*4]*d),l[h]=Math.floor(t[h*4+1]*d),f[this.hash(n[h],l[h])+1]++);for(let h=0;h<this.tableSize;h++)f[h+1]+=f[h];let p=f.slice(0,this.tableSize);for(let h=0;h<e;h++)c[h]||(u[p[this.hash(n[h],l[h])]++]=h);let m=(h,b)=>{if(!r[h]&&!r[b])return;let y=t[h*4]-t[b*4],v=t[h*4+1]-t[b*4+1],B=i[h*4+1]+i[b*4+1];if(y*y+v*v>B*B)return;let g=Ki(h,b);if(!Qo(s,a,g)){if(this.pairCount===this.pairs.length){let A=new Float64Array(this.pairs.length*2);A.set(this.pairs),this.pairs=A}this.pairs[this.pairCount++]=g}};for(let h=0;h<e;h++){if(c[h])continue;let b=n[h],y=l[h];for(let v=-1;v<=1;v++)for(let B=-1;B<=1;B++){let g=this.hash(b+B,y+v);for(let A=f[g];A<f[g+1];A++){let x=u[A];x>=h||n[x]!==b+B||l[x]!==y+v||m(h,x)}}}for(let h of this.large)for(let b=0;b<e;b++)b===h||c[b]&&b>h||m(h,b);this.pairs.subarray(0,this.pairCount).sort()}};var Ye=new Float64Array(12),En=new Float64Array(12),bi=new Float64Array(12);function yt(o,e,t,i,r,s,a,n){let l=e*6;o[l]=t,o[l+1]=i,o[l+2]=r,o[l+3]=s,o[l+4]=a,o[l+5]=n}function zn(o,e,t,i,r,s){let a=0,n=t*e[0]+i*e[1]-r,l=t*e[6]+i*e[7]-r;if(n<=0){for(let c=0;c<6;c++)o[a*6+c]=e[c];a++}if(l<=0){for(let c=0;c<6;c++)o[a*6+c]=e[6+c];a++}if(n*l<0){let c=n/(n-l),f=e[0]+(e[6]-e[0])*c,u=e[1]+(e[7]-e[1])*c;n>0?yt(o,a,f,u,s,e[3],0,e[5]):yt(o,a,f,u,e[8],s,e[10],0),a++}return a}function Bs(o,e,t,i,r,s,a,n){let l=-(r*a+s*n),c=-(-s*a+r*n);Math.abs(l)>Math.abs(c)?l>0?(yt(Ye,0,o,-e,0,0,3,4),yt(Ye,1,o,e,0,0,4,1)):(yt(Ye,0,-o,e,0,0,1,2),yt(Ye,1,-o,-e,0,0,2,3)):c>0?(yt(Ye,0,o,e,0,0,4,1),yt(Ye,1,-o,e,0,0,1,2)):(yt(Ye,0,-o,-e,0,0,2,3),yt(Ye,1,o,-e,0,0,3,4));for(let f=0;f<2;f++){let u=f*6,d=Ye[u],p=Ye[u+1];Ye[u]=t+r*d-s*p,Ye[u+1]=i+s*d+r*p}}function Mn(o,e,t,i,r,s,a,n,l,c,f){let u=Math.cos(t),d=Math.sin(t),p=Math.cos(n),m=Math.sin(n),h=s-o,b=a-e,y=u*h+d*b,v=-d*h+u*b,B=p*h+m*b,g=-m*h+p*b,A=Math.abs(u*p+d*m),x=Math.abs(u*-m+d*p),w=Math.abs(-d*p+u*m),E=Math.abs(-d*-m+u*p),k=Math.abs(y)-i-(A*l+x*c),z=Math.abs(v)-r-(w*l+E*c);if(k>0||z>0)return 0;let M=Math.abs(B)-(A*i+w*r)-l,P=Math.abs(g)-(x*i+E*r)-c;if(M>0||P>0)return 0;let _=0,C=k,S=y>0?u:-u,R=y>0?d:-d;z>.95*C+.01*r&&(_=1,C=z,S=v>0?-d:d,R=v>0?u:-u),M>.95*C+.01*l&&(_=2,C=M,S=B>0?p:-p,R=B>0?m:-m),P>.95*C+.01*c&&(_=3,C=P,S=g>0?-m:m,R=g>0?p:-p);let I,O,q,G,j,F,H,ee,le;if(_===0){I=S,O=R,q=o*I+e*O+i,G=-d,j=u;let U=o*G+e*j;F=-U+r,H=U+r,ee=3,le=1,Bs(l,c,s,a,p,m,I,O)}else if(_===1){I=S,O=R,q=o*I+e*O+r,G=u,j=d;let U=o*G+e*j;F=-U+i,H=U+i,ee=2,le=4,Bs(l,c,s,a,p,m,I,O)}else if(_===2){I=-S,O=-R,q=s*I+a*O+l,G=-m,j=p;let U=s*G+a*j;F=-U+c,H=U+c,ee=3,le=1,Bs(i,r,o,e,u,d,I,O)}else{I=-S,O=-R,q=s*I+a*O+c,G=p,j=m;let U=s*G+a*j;F=-U+l,H=U+l,ee=2,le=4,Bs(i,r,o,e,u,d,I,O)}if(zn(En,Ye,-G,-j,F,ee)<2||zn(bi,En,G,j,H,le)<2)return 0;let xe=0;for(let U=0;U<2;U++){let ze=U*6,Me=bi[ze],ce=bi[ze+1],Ne=I*Me+O*ce-q;if(Ne>0)continue;let zt=Me-I*Ne,Ie=ce-O*Ne,qe=bi[ze+2],Re=bi[ze+3],Ve=bi[ze+4],et=bi[ze+5],Ai,wi,Bi,Ci;_>=2?([qe,Re,Ve,et]=[Ve,et,qe,Re],Ai=Me-o,wi=ce-e,Bi=zt-s,Ci=Ie-a):(Ai=zt-o,wi=Ie-e,Bi=Me-s,Ci=ce-a);let At=xe*7;f[At+0]=qe&255|(Re&255)<<8|(Ve&255)<<16|(et&255)<<24,f[At+1]=u*Ai+d*wi,f[At+1+1]=-d*Ai+u*wi,f[At+3]=p*Bi+m*Ci,f[At+3+1]=-m*Bi+p*Ci,f[At+5]=-S,f[At+5+1]=-R,xe++}return xe}var In=o=>31-Math.clz32(o&-o);function xc(o){let e=o+2654435769;return e^=e>>>16,e=Math.imul(e,2146121005),e^=e>>>15,e=Math.imul(e,2221713035),e^=e>>>16,e>>>0}var Ko=(o,e,t,i)=>o>t||o===t&&e>i,Cs=class{colors=new Int32Array(0);colorStart=new Int32Array(65);colorBodies=new Int32Array(0);next=new Int32Array(0);pending=new Uint8Array(0);prio=new Uint32Array(0);resize(e){if(this.colors.length===e)return;let t=new Int32Array(e).fill(-1);t.set(this.colors.subarray(0,Math.min(e,this.colors.length))),this.colors=t,this.next=new Int32Array(e),this.pending=new Uint8Array(e),this.colorBodies=new Int32Array(e),this.prio=Uint32Array.from({length:e},(i,r)=>xc(r))}run(e,t,i,r,s,a){this.resize(e);let{colors:n,next:l,pending:c,prio:f}=this,u=(g,A)=>{let x=s[g*4+1],w=x===A?s[g*4+2]:x;return w>=0&&t[w]?w:-1},d=(g,A)=>{let x=0,w=0;for(let E=i[g];E<i[g+1];E++){let k=u(r[E],g);if(k<0||A&&c[k])continue;let z=n[k];z<0||(z<32?x|=1<<z:w|=1<<z-32)}return~x!==0?In(~x):~w!==0?32+In(~w):63};for(let g=0;g<e;g++){if(l[g]=n[g],!t[g]||n[g]<0)continue;let A=!0;for(let x=i[g];x<i[g+1]&&A;x++){let w=u(r[x],g);w>=0&&!Ko(f[g],g,f[w],w)&&(A=!1)}A&&(l[g]=Math.min(n[g],d(g,!1)))}n.set(l);let p=()=>{let g=0;for(let A=0;A<e;A++)if(c[A]=0,!!t[A]){if(n[A]<0)c[A]=1;else for(let x=i[A];x<i[A+1];x++){let w=u(r[x],A);if(w>=0&&n[w]===n[A]&&Ko(f[w],w,f[A],A)){c[A]=1;break}}g+=c[A]}return g},m=p(),h=0;for(;m>0&&h<a;){h++;for(let g=0;g<e;g++){if(l[g]=-2,!c[g])continue;let A=!0;for(let x=i[g];x<i[g+1]&&A;x++){let w=u(r[x],g);w>=0&&c[w]&&!Ko(f[g],g,f[w],w)&&(A=!1)}A&&(l[g]=d(g,!0))}for(let g=0;g<e;g++)l[g]!==-2&&(n[g]=l[g],c[g]=0,m--)}let b=0;if(m>0){for(let g=0;g<e;g++)c[g]&&n[g]<0&&(n[g]=0);b=p()}let y=this.colorStart;y.fill(0);let v=0;for(let g=0;g<e;g++)t[g]&&(y[n[g]+1]++,v=Math.max(v,n[g]+1));for(let g=0;g<64;g++)y[g+1]+=y[g];let B=y.slice(0,64);for(let g=0;g<e;g++)t[g]&&(this.colorBodies[B[n[g]]++]=g);return{numColors:v,conflicts:b,rounds:h}}};var Dn=0,Qt=1,Gn=2,Ss=3,Zo=4,Fn=[0,3,1,1,2],Cc=.05,D=4,T=32,ge=0,ne=3,$=6,ft=9,dt=12,gi=15,at=18,W=21,K=23,ye=25,Ee=26,vt=27,xr=28,ks=class{params=xs();options;bodyCount=0;pose;initial;inertial;velocity;prevVelocity;shape;props;dynamic=new Uint8Array(0);jointCount=0;contactCount=0;info=new Int32Array(0);data;handles=[];prevCount=0;prevKeys=new Float64Array(0);prevInfo=new Int32Array(0);prevData=new Float64Array(0);ignorePairs=[];noCollide=new Float64Array(0);noCollideCount=0;noCollideDirty=!0;adjStart=new Int32Array(1);adjList=new Int32Array(0);broadphase=new ws;coloring=new Cs;numColors=0;colorConflicts=0;colorRounds=0;profiling=!1;profile={};phaseStart=0;contactOut=new Float64Array(14);dxBuf=new Float64Array(0);constructor(e={}){this.options={precision:"f32",order:"colored",colorRounds:32,...e},this.pose=this.real(0),this.initial=this.real(0),this.inertial=this.real(0),this.velocity=this.real(0),this.prevVelocity=this.real(0),this.shape=this.real(0),this.props=this.real(0),this.data=this.real(0)}real(e){return this.options.precision==="f32"?new Float32Array(e):new Float64Array(e)}grow(e,t){if(e.length>=t)return e;let i=new e.constructor(Math.max(t,e.length*2,16));return i.set(e),i}clear(){this.bodyCount=0,this.jointCount=0,this.contactCount=0,this.prevCount=0,this.handles=[],this.ignorePairs=[],this.noCollideDirty=!0,this.coloring.resize(0)}addBody(e,t,i,r,s=[0,0,0]){let a=this.bodyCount++,n=this.bodyCount*4;this.pose=this.grow(this.pose,n),this.initial=this.grow(this.initial,n),this.inertial=this.grow(this.inertial,n),this.velocity=this.grow(this.velocity,n),this.prevVelocity=this.grow(this.prevVelocity,n),this.shape=this.grow(this.shape,n),this.props=this.grow(this.props,n),this.dynamic=this.grow(this.dynamic,this.bodyCount);let l=e[0]*e[1]*t;return this.pose.set([r[0],r[1],r[2]??0,0],a*4),this.velocity.set([s[0],s[1],s[2]??0,0],a*4),this.prevVelocity.set(this.velocity.subarray(a*4,a*4+4),a*4),this.shape.set([e[0],e[1],l,l*(e[0]*e[0]+e[1]*e[1])/12],a*4),this.props.set([i,Math.hypot(e[0]*.5,e[1]*.5),0,0],a*4),this.dynamic[a]=l>0?1:0,a}allocConstraint(){let e=this.jointCount++,t=this.jointCount+this.contactCount;this.info=this.grow(this.info,t*D),this.data=this.grow(this.data,t*T),this.contactCount>0&&(this.info.copyWithin((e+1)*D,e*D,(t-1)*D),this.data.copyWithin((e+1)*T,e*T,(t-1)*T)),this.data.fill(0,e*T,e*T+T);for(let i=0;i<3;i++)this.data[e*T+$+i]=1/0,this.data[e*T+ft+i]=-1/0,this.data[e*T+dt+i]=1/0,this.data[e*T+gi+i]=1/0;return this.handles[e]=null,this.noCollideDirty=!0,e}setInfo(e,t,i,r){this.info.set([t,i,r,0],e*D)}addJoint(e,t,i,r,s=[1/0,1/0,1/0],a=1/0){let n=this.allocConstraint();this.setInfo(n,Qt,e,t);let l=this.data,c=n*T;l.set(s,c+$),l[c+dt+2]=a,l[c+ft+2]=-a,l[c+gi+2]=a,l.set(i,c+W),l.set(r,c+K),l[c+ye]=(e>=0?this.pose[e*4+2]:0)-this.pose[t*4+2];let f=(e>=0?this.shape[e*4]:0)+this.shape[t*4],u=(e>=0?this.shape[e*4+1]:0)+this.shape[t*4+1];l[c+Ee]=f*f+u*u;let d={slot:n,alive:!0};return this.handles[n]=d,d}setJointWorldAnchor(e,t,i){e.alive&&(this.data[e.slot*T+W]=t,this.data[e.slot*T+W+1]=i)}removeJoint(e){e.alive&&(this.disable(e.slot),e.alive=!1)}addSpring(e,t,i,r,s,a){let n=this.allocConstraint();this.setInfo(n,Gn,e,t);let l=n*T;this.data[l+$]=s,this.data.set(i,l+W),this.data.set(r,l+K),this.data[l+ye]=a}addMotor(e,t,i,r){let s=this.allocConstraint();this.setInfo(s,Ss,e,t);let a=s*T;this.data[a+dt]=r,this.data[a+ft]=-r,this.data[a+ye]=i}ignoredPairs(){return this.ignorePairs}addIgnoreCollision(e,t){this.ignorePairs.push(Ki(e,t)),this.noCollideDirty=!0}loadFromReference(e){this.clear();let t=Object.keys(xs());Object.assign(this.params,Object.fromEntries(t.map(r=>[r,e[r]])));let i=new Map(e.bodies.map((r,s)=>[r,s]));for(let r of e.bodies){let s=this.addBody(r.size,0,r.friction,r.position,r.velocity);this.shape[s*4+2]=r.mass,this.shape[s*4+3]=r.moment,this.dynamic[s]=r.mass>0?1:0}for(let r of e.forces){let s=r.bodyA?i.get(r.bodyA):-1,a=i.get(r.bodyB);if(r instanceof bs){let n=this.addJoint(s,a,r.rA,r.rB,[r.stiffness[0],r.stiffness[1],r.stiffness[2]],r.fracture[2]);this.data[n.slot*T+ye]=r.restAngle,this.data[n.slot*T+Ee]=r.torqueArm}else r instanceof gs?this.addSpring(s,a,r.rA,r.rB,r.stiffness[0],r.rest):r instanceof vs?this.addMotor(s,a,r.speed,r.fmax[0]):r instanceof ys&&this.addIgnoreCollision(s,a)}}step(){let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha;this.phaseStart=this.profiling?performance.now():0,this.saveContacts(),this.findPairs(),this.mark("broadphase"),this.initJoints(),this.narrowphase(),this.mark("narrowphase"),this.warmStartConstraints(t,i),this.buildAdjacency(),this.mark("adjacency"),this.colorBodies(),this.mark("coloring"),this.warmStartBodies();let r=e.iterations+(t?1:0);for(let s=0;s<r;s++){let a=t?s<e.iterations?1:0:i;this.primal(a),this.mark("primal"),s<e.iterations&&this.dual(a),s===e.iterations-1&&this.updateVelocities(),this.mark("dual")}t&&this.refreshStick()}mark(e){if(!this.profiling)return;let t=performance.now();this.profile[e]=(this.profile[e]??0)+t-this.phaseStart,this.phaseStart=t}resetProfile(){for(let e of Object.keys(this.profile))delete this.profile[e]}saveContacts(){let e=this.contactCount,t=this.jointCount;this.prevKeys.length<e&&(this.prevKeys=new Float64Array(e*2),this.prevInfo=new Int32Array(e*2*D),this.prevData=new Float64Array(e*2*T));for(let i=0;i<e;i++){let r=t+i;this.prevKeys[i]=Ki(this.info[r*D+1],this.info[r*D+2])}this.prevInfo.set(this.info.subarray(t*D,(t+e)*D)),this.prevData.set(this.data.subarray(t*T,(t+e)*T)),this.prevCount=e,this.contactCount=0}findPairs(){if(this.noCollideDirty){let e=[...this.ignorePairs];for(let t=0;t<this.jointCount;t++){let i=this.info[t*D+1],r=this.info[t*D+2];this.info[t*D]!==Dn&&i>=0&&e.push(Ki(i,r))}this.noCollide=Float64Array.from(e).sort(),this.noCollideCount=e.length,this.noCollideDirty=!1}this.broadphase.findPairs(this.bodyCount,this.pose,this.props,this.dynamic,this.noCollide,this.noCollideCount)}initJoints(){let{info:e,data:t,pose:i}=this,r=0;for(let s=0;s<this.jointCount;s++){let a=s*T;if(e[s*D]===Qt){if(t[a+$]===0&&t[a+$+1]===0&&t[a+$+2]===0){let f=this.handles[s];f&&(f.alive=!1);continue}let l=e[s*D+1],c=e[s*D+2];this.jointC(l,c,a,i,t,a+at)}r!==s&&(e.copyWithin(r*D,s*D,(s+1)*D),t.copyWithin(r*T,a,a+T),this.handles[r]=this.handles[s],this.handles[r]&&(this.handles[r].slot=r)),r++}if(r!==this.jointCount){for(let s=r;s<this.jointCount;s++)this.handles[s]=null;this.jointCount=r,this.noCollideDirty=!0}}jointC(e,t,i,r,s,a,n=s){let l,c,f;if(e>=0){let h=Math.cos(r[e*4+2]),b=Math.sin(r[e*4+2]),y=s[i+W],v=s[i+W+1];l=h*y-b*v+r[e*4],c=b*y+h*v+r[e*4+1],f=r[e*4+2]}else l=s[i+W],c=s[i+W+1],f=0;let u=Math.cos(r[t*4+2]),d=Math.sin(r[t*4+2]),p=s[i+K],m=s[i+K+1];n[a]=l-(u*p-d*m+r[t*4]),n[a+1]=c-(d*p+u*m+r[t*4+1]),n[a+2]=(f-r[t*4+2]-s[i+ye])*s[i+Ee]}narrowphase(){let{pose:e,shape:t,props:i}=this,r=this.broadphase.pairs,s=this.contactOut;for(let a=0;a<this.broadphase.pairCount;a++){let n=r[a],l=Math.floor(n/2097152),c=n-l*2097152,f=Mn(e[l*4],e[l*4+1],e[l*4+2],t[l*4]*.5,t[l*4+1]*.5,e[c*4],e[c*4+1],e[c*4+2],t[c*4]*.5,t[c*4+1]*.5,s);if(f===0)continue;let u=0,d=this.prevCount;for(;u<d;){let h=u+d>>>1;this.prevKeys[h]<n?u=h+1:d=h}let p=u,m=Math.sqrt(i[l*4]*i[c*4]);for(let h=0;h<f;h++){let b=this.jointCount+this.contactCount++;this.info=this.grow(this.info,(b+1)*D),this.data=this.grow(this.data,(b+1)*T);let y=this.data,v=b*T,B=h*7,g=s[B+0];this.info.set([Zo,l,c,g],b*D),y.fill(0,v,v+T),y[v+$]=y[v+$+1]=1/0,y[v+ft]=-1/0,y[v+dt]=0,y[v+gi]=y[v+gi+1]=1/0,y[v+W]=s[B+1],y[v+W+1]=s[B+1+1],y[v+K]=s[B+3],y[v+K+1]=s[B+3+1],y[v+ye]=m,y[v+Ee]=s[B+5],y[v+vt]=s[B+5+1];let A=-1;for(let C=p;C<this.prevCount&&this.prevKeys[C]===n;C++)this.prevInfo[C*D+3]===g&&(A=C);if(A<0&&this.params.matchNearest){let C=Cc*Math.min(t[l*4],t[l*4+1],t[c*4],t[c*4+1]);for(let S=p;S<this.prevCount&&this.prevKeys[S]===n;S++){let R=Math.hypot(this.prevData[S*T+W]-y[v+W],this.prevData[S*T+W+1]-y[v+W+1]);R<=C&&(C=R,A=S)}}if(A>=0){let C=A*T;y[v+ge]=this.prevData[C+ge],y[v+ge+1]=this.prevData[C+ge+1],y[v+ne]=this.prevData[C+ne],y[v+ne+1]=this.prevData[C+ne+1],this.prevData[C+xr]&&(y[v+W]=this.prevData[C+W],y[v+W+1]=this.prevData[C+W+1],y[v+K]=this.prevData[C+K],y[v+K+1]=this.prevData[C+K+1])}let[x,w]=Yt(e[l*4+2],y[v+W],y[v+W+1]),[E,k]=Yt(e[c*4+2],y[v+K],y[v+K+1]),z=y[v+Ee],M=y[v+vt],P=e[l*4]+x-e[c*4]-E,_=e[l*4+1]+w-e[c*4+1]-k;y[v+at]=z*P+M*_+5e-4,y[v+at+1]=M*P-z*_}}}warmStartConstraints(e,t){let i=this.params,r=this.data,s=this.jointCount+this.contactCount;for(let a=0;a<s;a++){let n=Fn[this.info[a*D]],l=a*T;for(let c=0;c<n;c++){if(i.vbd){r[l+ge+c]=Yi(r[l+$+c],i.vbdStiffness);continue}e||(r[l+ne+c]=r[l+ne+c]*t*i.gamma),r[l+ge+c]=Yi(Qi(r[l+ge+c]*i.gamma,1,1e9),r[l+$+c])}}}prepareTopology(){this.buildAdjacency(),this.colorBodies()}buildAdjacency(){let e=this.bodyCount,t=this.jointCount+this.contactCount;this.adjStart=this.grow(this.adjStart,e+1),this.adjList=this.grow(this.adjList,t*2);let i=this.adjStart;i.fill(0,0,e+1);for(let s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&i[n+1]++}for(let s=0;s<e;s++)i[s+1]+=i[s];let r=i.slice(0,e);for(let s=0;s<t;s++)for(let a=1;a<=2;a++){let n=this.info[s*D+a];n>=0&&this.dynamic[n]&&(this.adjList[r[n]++]=s)}}colorBodies(){if(this.options.order!=="colored")return;let e=this.coloring.run(this.bodyCount,this.dynamic,this.adjStart,this.adjList,this.info,this.options.colorRounds);this.numColors=e.numColors,this.colorConflicts=e.conflicts,this.colorRounds=e.rounds}warmStartBodies(){let{dt:e,gravity:t}=this.params,{pose:i,velocity:r,initial:s,inertial:a,prevVelocity:n}=this;for(let l=0;l<this.bodyCount;l++){let c=l*4;r[c+2]=Qi(r[c+2],-50,50),a[c]=i[c]+r[c]*e,a[c+1]=i[c+1]+r[c+1]*e,a[c+2]=i[c+2]+r[c+2]*e,this.dynamic[l]&&(a[c+1]+=t*e*e);let f=(r[c+1]-n[c+1])/e*_n(t),u=Qi(f/Math.abs(t),0,1);Number.isFinite(u)||(u=0),s[c]=i[c],s[c+1]=i[c+1],s[c+2]=i[c+2],i[c]=i[c]+r[c]*e,i[c+1]=i[c+1]+r[c+1]*e+t*u*e*e,i[c+2]=i[c+2]+r[c+2]*e}}eC=new Float64Array(3);eMin=new Float64Array(3);eMax=new Float64Array(3);eJ=new Float64Array(9);eG=new Float64Array(9);evalConstraint(e,t,i){let{info:r,data:s,pose:a,initial:n}=this,l=r[e*D],c=r[e*D+1],f=r[e*D+2],u=e*T,{eC:d,eMin:p,eMax:m,eJ:h,eG:b}=this,y=Fn[l];for(let B=0;B<y;B++)p[B]=s[u+ft+B],m[B]=s[u+dt+B];let v=i===c;if(b.fill(0),l===Qt){this.jointC(c,f,u,a,s,0,d);for(let B=0;B<3;B++)s[u+$+B]===1/0&&(d[B]-=s[u+at+B]*t);if(i>=0){let B=a[i*4+2],[g,A]=Yt(B,s[u+(v?W:K)],s[u+(v?W:K)+1]),x=v?1:-1;h[0]=x,h[1]=0,h[2]=-x*A,h[3]=0,h[4]=x,h[5]=x*g,h[6]=0,h[7]=0,h[8]=x*s[u+Ee],b[2]=Math.abs(g),b[5]=Math.abs(A)}}else if(l===Gn){let[B,g]=Un(a,c,s[u+W],s[u+W+1]),[A,x]=Un(a,f,s[u+K],s[u+K+1]),w=B-A,E=g-x,k=w*w+E*E,z=Math.sqrt(k);if(d[0]=z-s[u+ye],i>=0&&k!==0){let M=w/z,P=E/z,_=(1-M*M)/z,C=-M*P/z,S=(1-P*P)/z,R=a[i*4+2],I=v?W:K,[O,q]=Yt(R,-s[u+I+1],s[u+I]),[G,j]=Yt(R,s[u+I],s[u+I+1]),F=_*O+C*q,H=C*O+S*q,ee=M*G+P*j,le=M*O+P*q,xe=v?1:-1;h[0]=xe*M,h[1]=xe*P,h[2]=xe*le;let U=v?-ee-ee:ee+ee;b[0]=Math.hypot(_,C,F),b[1]=Math.hypot(C,S,H),b[2]=Math.hypot(F,H,U)}else i>=0&&h.fill(0,0,3)}else if(l===Ss){let B=c>=0?a[c*4+2]-n[c*4+2]:0,g=a[f*4+2]-n[f*4+2];d[0]=B-g-s[u+ye]*this.params.dt,i>=0&&(h[0]=0,h[1]=0,h[2]=v?1:-1)}else if(l===Zo){let B=s[u+Ee],g=s[u+vt],A=g,x=-B,[w,E]=Yt(n[c*4+2],s[u+W],s[u+W+1]),[k,z]=Yt(n[f*4+2],s[u+K],s[u+K+1]),M=w*g-E*B,P=-(k*g-z*B),_=w*x-E*A,C=-(k*x-z*A),S=a[c*4]-n[c*4],R=a[c*4+1]-n[c*4+1],I=a[c*4+2]-n[c*4+2],O=a[f*4]-n[f*4],q=a[f*4+1]-n[f*4+1],G=a[f*4+2]-n[f*4+2];d[0]=s[u+at]*(1-t)+B*S+g*R+M*I+-B*O+-g*q+P*G,d[1]=s[u+at+1]*(1-t)+A*S+x*R+_*I+-A*O+-x*q+C*G;let j=Math.abs(s[u+ne])*s[u+ye];m[1]=j,p[1]=-j,i>=0&&(v?(h[0]=B,h[1]=g,h[2]=M,h[3]=A,h[4]=x,h[5]=_):(h[0]=-B,h[1]=-g,h[2]=P,h[3]=-A,h[4]=-x,h[5]=C))}return y}solveBody(e,t){let i=this.params,{shape:r,pose:s,inertial:a,data:n}=this,l=i.dt*i.dt,c=r[e*4+2]/l,f=r[e*4+3]/l,u=c,d=0,p=c,m=0,h=0,b=f,y=c*(s[e*4]-a[e*4]),v=c*(s[e*4+1]-a[e*4+1]),B=f*(s[e*4+2]-a[e*4+2]),{eC:g,eMin:A,eMax:x,eJ:w,eG:E}=this,k=this.adjStart,z=this.adjList;for(let F=k[e+1]-1;F>=k[e];F--){let H=z[F],ee=this.evalConstraint(H,t,e),le=H*T,xe=!i.vbd;for(let U=0;U<ee;U++){let ze=n[le+$+U],Me=xe&&ze===1/0?n[le+ne+U]:0,ce=n[le+ge+U],Ne=g[U],zt=ce*Ne+Me,Ie=Qi(zt,A[U],x[U]),qe=Math.abs(Ie);i.stiffnessRescale&&Ne!==0&&(zt<A[U]?ce=Math.abs((A[U]-Me)/Ne):zt>x[U]&&(ce=Math.abs((x[U]-Me)/Ne)));let Re=w[U*3],Ve=w[U*3+1],et=w[U*3+2];y+=Re*Ie,v+=Ve*Ie,B+=et*Ie,u+=Re*Re*ce+E[U*3]*qe,d+=Ve*Re*ce,p+=Ve*Ve*ce+E[U*3+1]*qe,m+=et*Re*ce,h+=et*Ve*ce,b+=et*et*ce+E[U*3+2]*qe}}let M=u,P=d/u,_=m/u,C=p-P*P*M,S=(h-P*_*M)/C,R=b-(_*_*M+S*S*C),I=v-P*y,q=(B-_*y-S*I)/R,G=I/C-S*q,j=y/M-P*G-_*q;this.dxBuf[e*3]=j,this.dxBuf[e*3+1]=G,this.dxBuf[e*3+2]=q}applyDx(e){this.pose[e*4]-=this.dxBuf[e*3],this.pose[e*4+1]-=this.dxBuf[e*3+1],this.pose[e*4+2]-=this.dxBuf[e*3+2]}primal(e){if(this.dxBuf.length<this.bodyCount*3&&(this.dxBuf=new Float64Array(this.bodyCount*3)),this.options.order==="sequential"){for(let r=this.bodyCount-1;r>=0;r--)this.dynamic[r]&&(this.solveBody(r,e),this.applyDx(r));return}let{colorStart:t,colorBodies:i}=this.coloring;for(let r=0;r<this.numColors;r++){for(let s=t[r];s<t[r+1];s++)this.solveBody(i[s],e);for(let s=t[r];s<t[r+1];s++)this.applyDx(i[s])}}dual(e){let t=this.params,i=this.data,{eC:r,eMin:s,eMax:a}=this,n=this.jointCount+this.contactCount;for(let l=0;l<n;l++){let c=this.info[l*D];if(c===Dn)continue;let f=this.evalConstraint(l,e,-1),u=l*T;c===Zo&&(i[u+xr]=Math.abs(i[u+ne+1])<a[1]&&Math.abs(i[u+at+1])<.01?1:0);for(let d=0;d<f;d++){let p=i[u+$+d],m=!t.vbd&&p===1/0?i[u+ne+d]:0,h=Qi(i[u+ge+d]*r[d]+m,s[d],a[d]);i[u+ne+d]=h,Math.abs(h)>=i[u+gi+d]&&this.disable(l),!t.vbd&&h>s[d]&&h<a[d]&&(i[u+ge+d]=Yi(i[u+ge+d]+t.beta*Math.abs(r[d]),Yi(1e9,i[u+$+d])))}}}refreshStick(){let e=this.data;for(let t=0;t<this.contactCount;t++){let i=(this.jointCount+t)*T,r=Math.abs(e[i+ne])*e[i+ye];e[i+xr]=Math.abs(e[i+ne+1])<r&&Math.abs(e[i+at+1])<.01?1:0}}disable(e){let t=e*T;this.data.fill(0,t+ge,t+ge+3),this.data.fill(0,t+ne,t+ne+3),this.data.fill(0,t+$,t+$+3)}updateVelocities(){let{dt:e}=this.params,{pose:t,initial:i,velocity:r,prevVelocity:s}=this;for(let a=0;a<this.bodyCount;a++){let n=a*4;s[n]=r[n],s[n+1]=r[n+1],s[n+2]=r[n+2],this.dynamic[a]&&(r[n]=(t[n]-i[n])/e,r[n+1]=(t[n+1]-i[n+1])/e,r[n+2]=(t[n+2]-i[n+2])/e)}}pick(e,t){for(let i=this.bodyCount-1;i>=0;i--){let[r,s]=Yt(-this.pose[i*4+2],e-this.pose[i*4],t-this.pose[i*4+1]);if(Math.abs(r)<=this.shape[i*4]*.5&&Math.abs(s)<=this.shape[i*4+1]*.5)return{body:i,local:[r,s]}}return null}kineticEnergy(){let e=0;for(let t=0;t<this.bodyCount;t++){if(!this.dynamic[t])continue;let i=t*4,r=this.velocity;e+=.5*this.shape[i+2]*(r[i]*r[i]+r[i+1]*r[i+1])+.5*this.shape[i+3]*r[i+2]*r[i+2]}return e}maxJointError(){let e=0,t=new Float64Array(3);for(let i=0;i<this.jointCount;i++){if(this.info[i*D]!==Qt)continue;let r=i*T;this.jointC(this.info[i*D+1],this.info[i*D+2],r,this.pose,this.data,0,t);for(let s=0;s<2;s++)this.data[r+$+s]===1/0&&(e=Math.max(e,Math.abs(t[s])))}return e}isContactPairIgnored(e,t){return Qo(this.noCollide,this.noCollideCount,Ki(e,t))}};function Yt(o,e,t){let i=Math.cos(o),r=Math.sin(o);return[i*e-r*t,r*e+i*t]}function Un(o,e,t,i){let r=Math.cos(o[e*4+2]),s=Math.sin(o[e*4+2]);return[r*t-s*i+o[e*4],s*t+r*i+o[e*4+1]]}var Ar=`
3515
3604
  ${mt}
3516
3605
 
3517
3606
  @group(0) @binding(0) var<uniform> params: Params;
@@ -3651,7 +3740,7 @@ fn findPairs(@builtin(global_invocation_id) gid: vec3u) {
3651
3740
  testPair(l, i);
3652
3741
  }
3653
3742
  }
3654
- `,yr=`
3743
+ `,wr=`
3655
3744
  ${mt}
3656
3745
 
3657
3746
  @group(0) @binding(0) var<uniform> params: Params;
@@ -3978,7 +4067,7 @@ fn narrowphase(@builtin(global_invocation_id) gid: vec3u) {
3978
4067
  contacts[k] = rec;
3979
4068
  }
3980
4069
  }
3981
- `;var vr=`
4070
+ `;var Br=`
3982
4071
  ${mt}
3983
4072
 
3984
4073
  // Per-dispatch constants, selected with a dynamic uniform offset
@@ -4351,7 +4440,7 @@ fn updateVelocities(@builtin(global_invocation_id) gid: vec3u) {
4351
4440
  if (b.shape.z > 0.0) { b.vel = vec4f((b.pose.xyz - b.initial.xyz) / params.dt, 0.0); }
4352
4441
  bodies[i] = b;
4353
4442
  }
4354
- `;var xs=o=>o===1/0?3e38:o===-1/0?-3e38:o,Ye=o=>Math.ceil(o/64),Pn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),lc=o=>Math.max(o,1)*24*4,cc=4,Qe=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,As=class{device;params=ps();topology;bodyCount=0;bodyCapacity;jointCount=0;jointCapacity=0;pairCapacity=0;contactCapacity=0;colorCap=12;splitPasses=!1;primalMode="bucket";shrinkVotes=0;get colorHistOffset(){return 3*this.bodyCapacity+65}colorRounds;bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;counterBuffer;argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;staticsDirty=!0;layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){if(this.device=e,this.topology=t,Object.assign(this.params,t.params),this.bodyCount=t.bodyCount,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/96):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies",size:lc(this.bodyCapacity),usage:Qe()});let r=this.bodyCapacity;this.tableSize=Pn(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:Qe()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Qe()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Qe()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:Qe()}),this.paramsBuffer=e.createBuffer({label:"params",size:128,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(r).fill(255));let s=2*ue.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:s}),resolve:e.createBuffer({size:s*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:s*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new De(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new De(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1),this.jointCount=t.jointCount,this.allocateJoints(t.jointCount+256),this.uploadJoints(0,t.jointCount),this.allocateContacts(Math.max(4096,4*r)),this.uploadBodies()}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",s="uniform";return{broad:t("broadphase",[s,i,r,r,r,i,i]),contacts:t("contacts",[s,i,i,r,i,r,r]),topo:t("topology",[s,i,i,i,i,r,r,r]),solve:t("solve",[s,r,r,i,r,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args",[s,i,i,r])}}createPipelines(){let e=this.device,t=(r,s,a)=>{let n=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:s});for(let c of a)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:n,entryPoint:c}})},i=this.layouts;t(gr,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(yr,[i.contacts],["hashInsert","narrowphase"]),t(ir,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(vr,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(jr,[i.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints",size:Math.max(e,1)*36*4,usage:Qe()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Qe()});if(this.jointBuffer){let s=t.createCommandEncoder();s.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),s.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([s.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t=2*e){let i=this.device,r=i.limits.maxStorageBufferBindingSize,s=Math.min(e,Math.floor(r/80),Math.floor(r/16)),a=Math.min(t,Math.floor(r/8)),n=this.contactBuffers,l=this.contactCapacity;for(let c of[this.pairBuffer,this.tableBuffer])c?.destroy();if(this.contactCapacity=s,this.pairCapacity=a,this.hashSize=Pn(4*s),this.pairBuffer=i.createBuffer({label:"pairs",size:a*8,usage:Qe()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:Qe()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:s*20*4,usage:Qe()})),n){let c=i.createCommandEncoder(),f=Math.min(l,s)*20*4;n.forEach((u,d)=>c.copyBufferToBuffer(u,0,this.contactBuffers[d],0,f)),i.queue.submit([c.finish()]),n.forEach(u=>u.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.contactCapacity))*4,usage:Qe()}),this.adjScan?.destroy(),this.adjScan=new De(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Qe()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((f,u)=>({binding:u,resource:{buffer:f}}))}),r=this.paramsBuffer,[s,a]=this.contactBuffers,n=this.adjBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,s,a,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,this.tableBuffer,this.counterBuffer])],topo:[s,a].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,n,this.colorBuffer])),solve:[s,a].map(l=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,n,this.colorBuffer,this.counterBuffer])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer])}}uploadBodies(e=0,t=this.topology.bodyCount-e){let i=this.topology,r=new Float32Array(Math.max(t,1)*24);for(let s=0;s<t;s++){let a=e+s,n=s*24;r.set(i.pose.subarray(a*4,a*4+3),n),r[n+3]=i.props[a*4],r.set(i.initial.subarray(a*4,a*4+4),n+4),r.set(i.inertial.subarray(a*4,a*4+4),n+8),r.set(i.velocity.subarray(a*4,a*4+4),n+12),r.set(i.prevVelocity.subarray(a*4,a*4+4),n+16),r.set(i.shape.subarray(a*4,a*4+4),n+20)}this.device.queue.writeBuffer(this.bodyBuffer,e*24*4,r),this.staticsDirty=!0}uploadJoints(e,t){if(t===0)return;let i=this.topology,r=new Float32Array(t*36);for(let s=0;s<t;s++){let a=(e+s)*T,n=s*36;for(let l=0;l<3;l++)r[n+0+l]=i.data[a+be+l],r[n+4+l]=i.data[a+ae+l],r[n+8+l]=xs(i.data[a+$+l]),r[n+12+l]=xs(i.data[a+ft+l]),r[n+16+l]=xs(i.data[a+dt+l]),r[n+20+l]=xs(i.data[a+di+l]),r[n+24+l]=i.data[a+ot+l];r[n+28]=i.data[a+H],r[n+28+1]=i.data[a+H+1],r[n+28+2]=i.data[a+K],r[n+28+3]=i.data[a+K+1],r[n+32]=i.data[a+ge],r[n+32+1]=i.data[a+_e]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*D,t*D)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(u,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,s=0;for(let u=0;u<t;u++)i[u]<=cc*r&&(s=Math.max(s,i[u]));let a=1/0;for(let u=0;u<t;u++)i[u]>s&&(a=Math.min(a,i[u]));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[];for(let u=0;u<t;u++)e.props[u*4+1]>n&&l.push(u);let c=[];for(let u=0;u<e.jointCount;u++){let d=e.info[u*D+1],h=e.info[u*D+2];d>=0&&c.push([Math.max(d,h),Math.min(d,h),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l,0),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:f.byteLength,usage:Qe()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),this.staticsDirty=!1}seedContactsFrom(e){let t=e.contactCount;if(t>this.contactCapacity)throw new Error("seedContactsFrom: too many contacts");let i=new ArrayBuffer(Math.max(t,1)*20*4),r=new Uint32Array(i),s=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,f=c*T,u=l*20;r[u]=e.info[c*D+1],r[u+1]=e.info[c*D+2],r[u+2]=e.info[c*D+3],r[u+3]=e.data[f+br]?1:0,s.set([e.data[f+be],e.data[f+be+1],e.data[f+ae],e.data[f+ae+1]],u+4),s.set([e.data[f+H],e.data[f+H+1],e.data[f+K],e.data[f+K+1]],u+8),s.set([e.data[f+ot],e.data[f+ot+1],e.data[f+_e],e.data[f+vt]],u+12),s[u+16]=e.data[f+ge]}this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,i);let a=new Uint32Array(16);a[1]=t,this.device.queue.writeBuffer(this.counterBuffer,0,a);let n=Uint32Array.from({length:this.bodyCount},(l,c)=>(e.coloring.colors[c]??-1)<0?255:e.coloring.colors[c]);this.device.queue.writeBuffer(this.colorBuffer,0,n)}addBody(e,t,i,r,s){if(this.bodyCount>=this.bodyCapacity)return-1;let a=this.topology.addBody(e,t,i,r,s);return this.bodyCount=this.topology.bodyCount,this.uploadBodies(a,1),this.device.queue.writeBuffer(this.colorBuffer,a*4,new Uint32Array([255])),this.adjScan.destroy(),this.adjScan=new De(this.device,this.adjBuffer,0,this.bodyCount+1),a}appendJoint(e,t,i,r,s,a=1/0){let n=this.topology;if(n.contactCount>0)throw new Error("appendJoint: the CPU mirror must not hold contacts");let l=n.addJoint(e,t,i,r,s,a).slot;return l>=this.jointCapacity&&this.allocateJoints(Math.max(l+1,this.jointCapacity*2)),this.jointCount=l+1,this.uploadJoints(l,1),e>=0&&(this.staticsDirty=!0),l}setWorldAnchor(e,t,i){this.device.queue.writeBuffer(this.jointBuffer,(e*36+28)*4,new Float32Array([t,i]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*36+0)*4,new Float32Array(12)),this.topology.data.fill(0,e*T+$,e*T+$+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,s=new ArrayBuffer(128),a=new Float32Array(s),n=new Uint32Array(s);a[0]=i.dt,a[1]=i.gravity,a[2]=i.beta,a[3]=i.gamma,a[4]=e,a[5]=i.vbdStiffness,n[6]=(i.vbd?1:0)|(i.stiffnessRescale?2:0)|(t?4:0)|(i.matchNearest?8:0),n[7]=this.bodyCount,n[8]=this.jointCount,n[9]=this.colorCap,a[10]=this.cellSize,n[11]=this.tableSize-1,a[12]=this.maxSmallRadius,n[13]=this.largeCount,n[14]=this.noCollideCount,n[15]=this.pairCapacity,n[16]=this.contactCapacity,n[17]=this.hashSize-1,n[18]=r+1,n[19]=2*r+1,n[20]=this.tableSize+1,n[21]=2*this.tableSize+1,n[22]=2*this.tableSize+1+r,n[23]=r,n[24]=this.colorHistOffset,n[25]=2*r,n[26]=this.colorGroups,n[27]=2*r+65,n[28]=this.colorRounds,this.device.queue.writeBuffer(this.paramsBuffer,0,s)}writePassConstants(e,t){let i=this.colorCap+1,r=e*i;(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,s)}step(){this.staticsDirty&&this.uploadStatics();let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha,r=e.iterations+(t?1:0);this.writeParams(i,t),this.writePassConstants(r,x=>t?x<e.iterations?1:0:i);let s=this.parity,a=this.bodyCapacity,n=this.bodyCount,l=this.jointCount,c=this.device.createCommandEncoder({label:"avbd2d step"});c.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),c.clearBuffer(this.tableBuffer),c.clearBuffer(this.adjBuffer,0,(2*a+1)*4);let f=this.timingCallback!==null&&this.timing!==null,u,d=0,h=f||this.splitPasses,m=()=>{u&&!h||(u?.end(),u=c.beginComputePass({label:h?ue[d]:"avbd2d step",timestampWrites:f?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},p=this.groups,b=(x,v,B)=>{B<=0||(u.setPipeline(this.pipes[x]),u.setBindGroup(0,v),u.dispatchWorkgroups(B))},y=(x,v,B)=>{u.setPipeline(this.pipes[x]),u.setBindGroup(0,v),u.dispatchWorkgroupsIndirect(this.argsBuffer,B*4)};m(),b("beginFrame",p.broad,1),b("argsPrev",p.args,1),y("hashInsert",p.contacts[s],6),b("gridCount",p.broad,Ye(n)),this.gridScan.encode(u),b("gridScatter",p.broad,Ye(n)),b("findPairs",p.broad,Ye(n)),b("argsPairs",p.args,1),y("narrowphase",p.contacts[s],0),b("argsContacts",p.args,1),m(),b("degreeJoints",p.topo[s],Ye(l)),y("degreeContacts",p.topo[s],3),this.adjScan.encode(u),b("fillJoints",p.topo[s],Ye(l)),y("fillContacts",p.topo[s],3),b("sortAdjacency",p.topo[s],Ye(n)),m(),b("colorCompact",p.topo[s],Ye(n)),b("colorMark",p.topo[s],Ye(n));for(let x=0;x<this.colorRounds;x++)b(x%2===0?"colorRoundAB":"colorRoundBA",p.topo[s],Ye(n));b("colorCount",p.topo[s],this.colorGroups),this.colorHistScan.encode(u),b("colorStarts",p.topo[s],1),b("argsColors",p.args,1),b("colorScatter",p.topo[s],this.colorGroups),m();let A=this.colorCap+1,w=x=>u.setBindGroup(1,this.passGroup,[x*256]);u.setBindGroup(0,p.solve[s]),w(0),l>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(Ye(l))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(Ye(n));for(let x=0;x<r;x++){let v=this.primalMode==="scan";u.setPipeline(v?this.pipes.primalScan:this.pipes.primal);for(let B=0;B<this.colorCap;B++)w(x*A+B),v?u.dispatchWorkgroups(Ye(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*B)*4);x<e.iterations&&(w(x*A+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),x===e.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(Ye(n)))}t&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let g=2*ue.length;if(f){let{querySet:x,resolve:v,read:B}=this.timing;c.resolveQuerySet(x,0,g,v,0),c.copyBufferToBuffer(v,0,B,0,g*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-s,f){let x=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let v=this.timing.read;v.mapAsync(GPUMapMode.READ).then(()=>{let B=new BigUint64Array(v.getMappedRange()).slice();if(v.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(z,M)=>Number(B[M]-B[z])/1e6,k={total:E(0,g-1)};ue.forEach((z,M)=>k[z]=E(2*M,2*M+1)),x(k)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*24*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*36*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],overflow:e[3],clashes:e[4],colors:e[5]}}async readContacts(){let{contacts:e}=await this.readCounters(),t=Math.min(e,this.contactCapacity);return this.read(this.contactBuffers[1-this.parity],t*20*4)}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}async readColors(){return new Uint32Array(await this.read(this.colorBuffer,this.bodyCount*4))}async readAdjacency(){let e=new Uint32Array(await this.read(this.adjBuffer,this.adjBuffer.size)),t=e.slice(0,this.bodyCount+1),i=2*this.bodyCapacity+1;return{start:t,list:e.slice(i,i+t[this.bodyCount])}}adapt(e){let t=e.colors;e.clashes>0||t>=this.colorCap-1?(this.colorCap=Math.min(64,Math.max(t+4,this.colorCap+4)),this.shrinkVotes=0):t+2<this.colorCap?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,Math.max(4,t+2)),this.shrinkVotes=0):this.shrinkVotes=0;let i=(e.overflow&2)!==0||e.contacts>.8*this.contactCapacity,r=(e.overflow&1)!==0||e.pairs>.8*this.pairCapacity,s=this.contactCapacity*(i?2:1),a=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(s,a)&&this.allocateContacts(s,a)}capacityCanGrow(e,t){let i=this.device.limits.maxStorageBufferBindingSize,r=Math.min(Math.floor(i/80),Math.floor(i/16));return Math.min(e,r)>this.contactCapacity||Math.min(t,Math.floor(i/8))>this.pairCapacity}async readStats(e){let[t,i]=await Promise.all([e??this.readBodies(),this.readJoints()]),r=0;for(let c=0;c<this.bodyCount;c++){let f=c*24,u=t[f+22],d=t[f+12],h=t[f+13],m=t[f+14];u>0&&(r+=.5*u*(d*d+h*h)+.5*t[f+23]*m*m)}let s=0,a=0,n=this.topology.info,l=(c,f,u)=>{let d=c*24,h=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[h*f-m*u+t[d],m*f+h*u+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(n[c*D]!==Vt)continue;let f=c*36;if(i[f+8]===0&&i[f+8+1]===0&&i[f+8+2]===0)continue;a++;let u=n[c*D+1],d=n[c*D+2],h=u>=0?l(u,i[f+28],i[f+28+1]):[i[f+28],i[f+28+1]],m=l(d,i[f+28+2],i[f+28+3]);for(let p=0;p<2;p++)i[f+8+p]>=1e30&&(s=Math.max(s,Math.abs(h[p]-m[p])))}return{kineticEnergy:r,maxJointError:s,joints:a}}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.passBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var uc=`${mt}
4443
+ `;var Ps=o=>o===1/0?3e38:o===-1/0?-3e38:o,Qe=o=>Math.ceil(o/64),Vn=o=>2**Math.ceil(Math.log2(Math.max(o,2))),Mc=o=>Math.max(o,1)*24*4,Ic=4,Ke=()=>GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC|GPUBufferUsage.COPY_DST,Ls=class{device;params=As();topology;bodyCount=0;bodyCapacity;jointCount=0;jointCapacity=0;pairCapacity=0;contactCapacity=0;colorCap=12;splitPasses=!1;primalMode="bucket";shrinkVotes=0;get colorHistOffset(){return 3*this.bodyCapacity+65}colorRounds;bodyBuffer;ownsBodyBuffer;jointBuffer;infoBuffer;contactBuffers;pairBuffer;tableBuffer;gridBuffer;staticBuffer=null;counterBuffer;argsBuffer;adjBuffer=null;colorBuffer;paramsBuffer;passBuffer=null;passEntries=0;parity=0;tableSize;hashSize=0;cellSize=1;maxSmallRadius=0;largeCount=0;noCollideCount=0;staticsDirty=!0;layouts;pipes={};groups;passGroup=null;gridScan;colorHistScan;colorGroups;adjScan=null;timing;timingCallback=null;timingBusy=!1;destroyed=!1;constructor(e,t,i={}){if(this.device=e,this.topology=t,Object.assign(this.params,t.params),this.bodyCount=t.bodyCount,this.colorRounds=i.colorRounds??16,this.ownsBodyBuffer=!i.bodyBuffer,this.bodyCapacity=i.bodyBuffer?Math.floor(i.bodyBuffer.size/96):Math.max(i.bodyCapacity??this.bodyCount+1024,this.bodyCount,1),this.bodyCapacity<this.bodyCount)throw new Error("body buffer too small");this.bodyBuffer=i.bodyBuffer??e.createBuffer({label:"bodies",size:Mc(this.bodyCapacity),usage:Ke()});let r=this.bodyCapacity;this.tableSize=Vn(2*r),this.gridBuffer=e.createBuffer({label:"grid",size:(2*this.tableSize+1+3*r)*4,usage:Ke()}),this.counterBuffer=e.createBuffer({label:"counters",size:64,usage:Ke()}),this.argsBuffer=e.createBuffer({label:"indirect args",size:816,usage:Ke()|GPUBufferUsage.INDIRECT}),this.colorGroups=Math.ceil(r/256),this.colorBuffer=e.createBuffer({label:"colours",size:(3*r+65+64*this.colorGroups+1)*4,usage:Ke()}),this.paramsBuffer=e.createBuffer({label:"params",size:128,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),e.queue.writeBuffer(this.colorBuffer,0,new Uint32Array(r).fill(255));let s=2*fe.length;this.timing=e.features.has("timestamp-query")?{querySet:e.createQuerySet({type:"timestamp",count:s}),resolve:e.createBuffer({size:s*8,usage:GPUBufferUsage.QUERY_RESOLVE|GPUBufferUsage.COPY_SRC}),read:e.createBuffer({size:s*8,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST})}:null,this.layouts=this.createLayouts(),this.createPipelines(),this.gridScan=new Fe(e,this.gridBuffer,0,this.tableSize+1),this.colorHistScan=new Fe(e,this.colorBuffer,this.colorHistOffset,64*this.colorGroups+1),this.jointCount=t.jointCount,this.allocateJoints(t.jointCount+256),this.uploadJoints(0,t.jointCount),this.allocateContacts(Math.max(4096,4*r)),this.uploadBodies()}createLayouts(){let e=this.device,t=(a,n)=>e.createBindGroupLayout({label:a,entries:n.map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))}),i="read-only-storage",r="storage",s="uniform";return{broad:t("broadphase",[s,i,r,r,r,i,i]),contacts:t("contacts",[s,i,i,r,i,r,r]),topo:t("topology",[s,i,i,i,i,r,r,r]),solve:t("solve",[s,r,r,i,r,i,i,i]),pass:e.createBindGroupLayout({label:"pass",entries:[{binding:0,visibility:GPUShaderStage.COMPUTE,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:16}}]}),args:t("args",[s,i,i,r])}}createPipelines(){let e=this.device,t=(r,s,a)=>{let n=e.createShaderModule({code:r}),l=e.createPipelineLayout({bindGroupLayouts:s});for(let c of a)this.pipes[c]=e.createComputePipeline({label:c,layout:l,compute:{module:n,entryPoint:c}})},i=this.layouts;t(Ar,[i.broad],["beginFrame","gridCount","gridScatter","findPairs"]),t(wr,[i.contacts],["hashInsert","narrowphase"]),t(nr,[i.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),t(Br,[i.solve,i.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),t(Fr,[i.args],["argsPrev","argsPairs","argsContacts","argsColors"])}allocateJoints(e){let t=this.device,i=t.createBuffer({label:"joints",size:Math.max(e,1)*36*4,usage:Ke()}),r=t.createBuffer({label:"joint info",size:Math.max(e,1)*16,usage:Ke()});if(this.jointBuffer){let s=t.createCommandEncoder();s.copyBufferToBuffer(this.jointBuffer,0,i,0,this.jointCapacity*36*4),s.copyBufferToBuffer(this.infoBuffer,0,r,0,this.jointCapacity*16),t.queue.submit([s.finish()]),this.jointBuffer.destroy(),this.infoBuffer.destroy()}this.jointBuffer=i,this.infoBuffer=r,this.jointCapacity=e,this.contactBuffers&&this.rebuildBindings()}allocateContacts(e,t=2*e){let i=this.device,r=i.limits.maxStorageBufferBindingSize,s=Math.min(e,Math.floor(r/80),Math.floor(r/16)),a=Math.min(t,Math.floor(r/8)),n=this.contactBuffers,l=this.contactCapacity;for(let c of[this.pairBuffer,this.tableBuffer])c?.destroy();if(this.contactCapacity=s,this.pairCapacity=a,this.hashSize=Vn(4*s),this.pairBuffer=i.createBuffer({label:"pairs",size:a*8,usage:Ke()}),this.tableBuffer=i.createBuffer({label:"contact hash",size:this.hashSize*4,usage:Ke()}),this.contactBuffers=[0,1].map(c=>i.createBuffer({label:`contacts ${c}`,size:s*20*4,usage:Ke()})),n){let c=i.createCommandEncoder(),f=Math.min(l,s)*20*4;n.forEach((u,d)=>c.copyBufferToBuffer(u,0,this.contactBuffers[d],0,f)),i.queue.submit([c.finish()]),n.forEach(u=>u.destroy())}this.rebuildBindings()}rebuildBindings(){let e=this.device,t=this.bodyCapacity;this.adjBuffer?.destroy(),this.adjBuffer=e.createBuffer({label:"adjacency",size:(2*t+1+2*(this.jointCapacity+this.contactCapacity))*4,usage:Ke()}),this.adjScan?.destroy(),this.adjScan=new Fe(e,this.adjBuffer,0,this.bodyCount+1),this.staticBuffer??=e.createBuffer({label:"statics",size:16,usage:Ke()});let i=(l,c)=>e.createBindGroup({layout:l,entries:c.map((f,u)=>({binding:u,resource:{buffer:f}}))}),r=this.paramsBuffer,[s,a]=this.contactBuffers,n=this.adjBuffer;this.groups={broad:i(this.layouts.broad,[r,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer]),contacts:[i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,s,a,this.tableBuffer,this.counterBuffer]),i(this.layouts.contacts,[r,this.bodyBuffer,this.pairBuffer,a,s,this.tableBuffer,this.counterBuffer])],topo:[s,a].map(l=>i(this.layouts.topo,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,this.counterBuffer,n,this.colorBuffer])),solve:[s,a].map(l=>i(this.layouts.solve,[r,this.bodyBuffer,this.jointBuffer,this.infoBuffer,l,n,this.colorBuffer,this.counterBuffer])),args:i(this.layouts.args,[r,this.counterBuffer,this.colorBuffer,this.argsBuffer])}}uploadBodies(e=0,t=this.topology.bodyCount-e){let i=this.topology,r=new Float32Array(Math.max(t,1)*24);for(let s=0;s<t;s++){let a=e+s,n=s*24;r.set(i.pose.subarray(a*4,a*4+3),n),r[n+3]=i.props[a*4],r.set(i.initial.subarray(a*4,a*4+4),n+4),r.set(i.inertial.subarray(a*4,a*4+4),n+8),r.set(i.velocity.subarray(a*4,a*4+4),n+12),r.set(i.prevVelocity.subarray(a*4,a*4+4),n+16),r.set(i.shape.subarray(a*4,a*4+4),n+20)}this.device.queue.writeBuffer(this.bodyBuffer,e*24*4,r),this.staticsDirty=!0}uploadJoints(e,t){if(t===0)return;let i=this.topology,r=new Float32Array(t*36);for(let s=0;s<t;s++){let a=(e+s)*T,n=s*36;for(let l=0;l<3;l++)r[n+0+l]=i.data[a+ge+l],r[n+4+l]=i.data[a+ne+l],r[n+8+l]=Ps(i.data[a+$+l]),r[n+12+l]=Ps(i.data[a+ft+l]),r[n+16+l]=Ps(i.data[a+dt+l]),r[n+20+l]=Ps(i.data[a+gi+l]),r[n+24+l]=i.data[a+at+l];r[n+28]=i.data[a+W],r[n+28+1]=i.data[a+W+1],r[n+28+2]=i.data[a+K],r[n+28+3]=i.data[a+K+1],r[n+32]=i.data[a+ye],r[n+32+1]=i.data[a+Ee]}this.device.queue.writeBuffer(this.jointBuffer,e*36*4,r),this.device.queue.writeBuffer(this.infoBuffer,e*16,i.info,e*D,t*D)}uploadStatics(){let e=this.topology,t=this.bodyCount,i=Float64Array.from({length:t},(u,d)=>e.props[d*4+1]).sort(),r=t>0?i[t>>1]:1,s=0;for(let u=0;u<t;u++)i[u]<=Ic*r&&(s=Math.max(s,i[u]));let a=1/0;for(let u=0;u<t;u++)i[u]>s&&(a=Math.min(a,i[u]));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[];for(let u=0;u<t;u++)e.props[u*4+1]>n&&l.push(u);let c=[];for(let u=0;u<e.jointCount;u++){let d=e.info[u*D+1],p=e.info[u*D+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l,0),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"statics",size:f.byteLength,usage:Ke()}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),this.staticsDirty=!1}seedContactsFrom(e){let t=e.contactCount;if(t>this.contactCapacity)throw new Error("seedContactsFrom: too many contacts");let i=new ArrayBuffer(Math.max(t,1)*20*4),r=new Uint32Array(i),s=new Float32Array(i);for(let l=0;l<t;l++){let c=e.jointCount+l,f=c*T,u=l*20;r[u]=e.info[c*D+1],r[u+1]=e.info[c*D+2],r[u+2]=e.info[c*D+3],r[u+3]=e.data[f+xr]?1:0,s.set([e.data[f+ge],e.data[f+ge+1],e.data[f+ne],e.data[f+ne+1]],u+4),s.set([e.data[f+W],e.data[f+W+1],e.data[f+K],e.data[f+K+1]],u+8),s.set([e.data[f+at],e.data[f+at+1],e.data[f+Ee],e.data[f+vt]],u+12),s[u+16]=e.data[f+ye]}this.device.queue.writeBuffer(this.contactBuffers[1-this.parity],0,i);let a=new Uint32Array(16);a[1]=t,this.device.queue.writeBuffer(this.counterBuffer,0,a);let n=Uint32Array.from({length:this.bodyCount},(l,c)=>(e.coloring.colors[c]??-1)<0?255:e.coloring.colors[c]);this.device.queue.writeBuffer(this.colorBuffer,0,n)}addBody(e,t,i,r,s){if(this.bodyCount>=this.bodyCapacity)return-1;let a=this.topology.addBody(e,t,i,r,s);return this.bodyCount=this.topology.bodyCount,this.uploadBodies(a,1),this.device.queue.writeBuffer(this.colorBuffer,a*4,new Uint32Array([255])),this.adjScan.destroy(),this.adjScan=new Fe(this.device,this.adjBuffer,0,this.bodyCount+1),a}appendJoint(e,t,i,r,s,a=1/0){let n=this.topology;if(n.contactCount>0)throw new Error("appendJoint: the CPU mirror must not hold contacts");let l=n.addJoint(e,t,i,r,s,a).slot;return l>=this.jointCapacity&&this.allocateJoints(Math.max(l+1,this.jointCapacity*2)),this.jointCount=l+1,this.uploadJoints(l,1),e>=0&&(this.staticsDirty=!0),l}setWorldAnchor(e,t,i){this.device.queue.writeBuffer(this.jointBuffer,(e*36+28)*4,new Float32Array([t,i]))}disableConstraint(e){this.device.queue.writeBuffer(this.jointBuffer,(e*36+0)*4,new Float32Array(12)),this.topology.data.fill(0,e*T+$,e*T+$+3)}profileNextStep(e){this.timing&&!this.timingBusy&&(this.timingCallback=e)}writeParams(e,t){let i=this.params,r=this.bodyCapacity,s=new ArrayBuffer(128),a=new Float32Array(s),n=new Uint32Array(s);a[0]=i.dt,a[1]=i.gravity,a[2]=i.beta,a[3]=i.gamma,a[4]=e,a[5]=i.vbdStiffness,n[6]=(i.vbd?1:0)|(i.stiffnessRescale?2:0)|(t?4:0)|(i.matchNearest?8:0),n[7]=this.bodyCount,n[8]=this.jointCount,n[9]=this.colorCap,a[10]=this.cellSize,n[11]=this.tableSize-1,a[12]=this.maxSmallRadius,n[13]=this.largeCount,n[14]=this.noCollideCount,n[15]=this.pairCapacity,n[16]=this.contactCapacity,n[17]=this.hashSize-1,n[18]=r+1,n[19]=2*r+1,n[20]=this.tableSize+1,n[21]=2*this.tableSize+1,n[22]=2*this.tableSize+1+r,n[23]=r,n[24]=this.colorHistOffset,n[25]=2*r,n[26]=this.colorGroups,n[27]=2*r+65,n[28]=this.colorRounds,this.device.queue.writeBuffer(this.paramsBuffer,0,s)}writePassConstants(e,t){let i=this.colorCap+1,r=e*i;(r>this.passEntries||!this.passGroup)&&(this.passBuffer?.destroy(),this.passBuffer=this.device.createBuffer({label:"pass constants",size:r*256,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.passEntries=r,this.passGroup=this.device.createBindGroup({layout:this.layouts.pass,entries:[{binding:0,resource:{buffer:this.passBuffer,size:16}}]}));let s=new ArrayBuffer(r*256),a=new Uint32Array(s),n=new Float32Array(s);for(let l=0;l<e;l++)for(let c=0;c<=this.colorCap;c++){let f=(l*i+c)*256/4;a[f]=c,n[f+1]=t(l)}this.device.queue.writeBuffer(this.passBuffer,0,s)}step(){this.staticsDirty&&this.uploadStatics();let e=this.params,t=e.postStabilize&&!e.vbd,i=e.vbd?0:e.alpha,r=e.iterations+(t?1:0);this.writeParams(i,t),this.writePassConstants(r,A=>t?A<e.iterations?1:0:i);let s=this.parity,a=this.bodyCapacity,n=this.bodyCount,l=this.jointCount,c=this.device.createCommandEncoder({label:"avbd2d step"});c.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),c.clearBuffer(this.tableBuffer),c.clearBuffer(this.adjBuffer,0,(2*a+1)*4);let f=this.timingCallback!==null&&this.timing!==null,u,d=0,p=f||this.splitPasses,m=()=>{u&&!p||(u?.end(),u=c.beginComputePass({label:p?fe[d]:"avbd2d step",timestampWrites:f?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*d,endOfPassWriteIndex:2*d+1}:void 0}),d++)},h=this.groups,b=(A,x,w)=>{w<=0||(u.setPipeline(this.pipes[A]),u.setBindGroup(0,x),u.dispatchWorkgroups(w))},y=(A,x,w)=>{u.setPipeline(this.pipes[A]),u.setBindGroup(0,x),u.dispatchWorkgroupsIndirect(this.argsBuffer,w*4)};m(),b("beginFrame",h.broad,1),b("argsPrev",h.args,1),y("hashInsert",h.contacts[s],6),b("gridCount",h.broad,Qe(n)),this.gridScan.encode(u),b("gridScatter",h.broad,Qe(n)),b("findPairs",h.broad,Qe(n)),b("argsPairs",h.args,1),y("narrowphase",h.contacts[s],0),b("argsContacts",h.args,1),m(),b("degreeJoints",h.topo[s],Qe(l)),y("degreeContacts",h.topo[s],3),this.adjScan.encode(u),b("fillJoints",h.topo[s],Qe(l)),y("fillContacts",h.topo[s],3),b("sortAdjacency",h.topo[s],Qe(n)),m(),b("colorCompact",h.topo[s],Qe(n)),b("colorMark",h.topo[s],Qe(n));for(let A=0;A<this.colorRounds;A++)b(A%2===0?"colorRoundAB":"colorRoundBA",h.topo[s],Qe(n));b("colorCount",h.topo[s],this.colorGroups),this.colorHistScan.encode(u),b("colorStarts",h.topo[s],1),b("argsColors",h.args,1),b("colorScatter",h.topo[s],this.colorGroups),m();let v=this.colorCap+1,B=A=>u.setBindGroup(1,this.passGroup,[A*256]);u.setBindGroup(0,h.solve[s]),B(0),l>0&&(u.setPipeline(this.pipes.warmStartJoints),u.dispatchWorkgroups(Qe(l))),u.setPipeline(this.pipes.warmStartBodies),u.dispatchWorkgroups(Qe(n));for(let A=0;A<r;A++){let x=this.primalMode==="scan";u.setPipeline(x?this.pipes.primalScan:this.pipes.primal);for(let w=0;w<this.colorCap;w++)B(A*v+w),x?u.dispatchWorkgroups(Qe(n)):u.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*w)*4);A<e.iterations&&(B(A*v+this.colorCap),u.setPipeline(this.pipes.dual),u.dispatchWorkgroupsIndirect(this.argsBuffer,36)),A===e.iterations-1&&(u.setPipeline(this.pipes.updateVelocities),u.dispatchWorkgroups(Qe(n)))}t&&(u.setPipeline(this.pipes.refreshStick),u.dispatchWorkgroupsIndirect(this.argsBuffer,12)),u.end();let g=2*fe.length;if(f){let{querySet:A,resolve:x,read:w}=this.timing;c.resolveQuerySet(A,0,g,x,0),c.copyBufferToBuffer(x,0,w,0,g*8)}if(this.device.queue.submit([c.finish()]),this.parity=1-s,f){let A=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let x=this.timing.read;x.mapAsync(GPUMapMode.READ).then(()=>{let w=new BigUint64Array(x.getMappedRange()).slice();if(x.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let E=(z,M)=>Number(w[M]-w[z])/1e6,k={total:E(0,g-1)};fe.forEach((z,M)=>k[z]=E(2*M,2*M+1)),A(k)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}async readBodies(){return new Float32Array(await this.read(this.bodyBuffer,this.bodyCount*24*4))}async readJoints(){return new Float32Array(await this.read(this.jointBuffer,this.jointCount*36*4))}async readCounters(){let e=new Uint32Array(await this.read(this.counterBuffer,64));return{pairs:e[0],contacts:e[1],overflow:e[3],clashes:e[4],colors:e[5]}}async readContacts(){let{contacts:e}=await this.readCounters(),t=Math.min(e,this.contactCapacity);return this.read(this.contactBuffers[1-this.parity],t*20*4)}async readPairs(){let{pairs:e}=await this.readCounters();return new Uint32Array(await this.read(this.pairBuffer,Math.min(e,this.pairCapacity)*8))}async readColors(){return new Uint32Array(await this.read(this.colorBuffer,this.bodyCount*4))}async readAdjacency(){let e=new Uint32Array(await this.read(this.adjBuffer,this.adjBuffer.size)),t=e.slice(0,this.bodyCount+1),i=2*this.bodyCapacity+1;return{start:t,list:e.slice(i,i+t[this.bodyCount])}}adapt(e){let t=e.colors;e.clashes>0||t>=this.colorCap-1?(this.colorCap=Math.min(64,Math.max(t+4,this.colorCap+4)),this.shrinkVotes=0):t+2<this.colorCap?++this.shrinkVotes>=3&&(this.colorCap=Math.min(64,Math.max(4,t+2)),this.shrinkVotes=0):this.shrinkVotes=0;let i=(e.overflow&2)!==0||e.contacts>.8*this.contactCapacity,r=(e.overflow&1)!==0||e.pairs>.8*this.pairCapacity,s=this.contactCapacity*(i?2:1),a=this.pairCapacity*(r?2:1);(i||r)&&this.capacityCanGrow(s,a)&&this.allocateContacts(s,a)}capacityCanGrow(e,t){let i=this.device.limits.maxStorageBufferBindingSize,r=Math.min(Math.floor(i/80),Math.floor(i/16));return Math.min(e,r)>this.contactCapacity||Math.min(t,Math.floor(i/8))>this.pairCapacity}async readStats(e){let[t,i]=await Promise.all([e??this.readBodies(),this.readJoints()]),r=0;for(let c=0;c<this.bodyCount;c++){let f=c*24,u=t[f+22],d=t[f+12],p=t[f+13],m=t[f+14];u>0&&(r+=.5*u*(d*d+p*p)+.5*t[f+23]*m*m)}let s=0,a=0,n=this.topology.info,l=(c,f,u)=>{let d=c*24,p=Math.cos(t[d+2]),m=Math.sin(t[d+2]);return[p*f-m*u+t[d],m*f+p*u+t[d+1]]};for(let c=0;c<this.jointCount;c++){if(n[c*D]!==Qt)continue;let f=c*36;if(i[f+8]===0&&i[f+8+1]===0&&i[f+8+2]===0)continue;a++;let u=n[c*D+1],d=n[c*D+2],p=u>=0?l(u,i[f+28],i[f+28+1]):[i[f+28],i[f+28+1]],m=l(d,i[f+28+2],i[f+28+3]);for(let h=0;h<2;h++)i[f+8+h]>=1e30&&(s=Math.max(s,Math.abs(p[h]-m[h])))}return{kineticEnergy:r,maxJointError:s,joints:a}}async read(e,t){if(t===0)return new ArrayBuffer(0);let i=this.device.createBuffer({size:t,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),r=this.device.createCommandEncoder();r.copyBufferToBuffer(e,0,i,0,t),this.device.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=i.getMappedRange().slice(0);return i.unmap(),i.destroy(),s}releaseTiming(){this.timing?.resolve.destroy(),this.timing?.read.destroy(),this.timing?.querySet.destroy()}destroy(){this.ownsBodyBuffer&&this.bodyBuffer.destroy();let e=[this.jointBuffer,this.infoBuffer,...this.contactBuffers,this.pairBuffer,this.tableBuffer,this.gridBuffer,this.staticBuffer,this.counterBuffer,this.argsBuffer,this.adjBuffer,this.colorBuffer,this.paramsBuffer,this.passBuffer];for(let t of e)t?.destroy();this.destroyed=!0,this.timingBusy||this.releaseTiming(),this.gridScan.destroy(),this.colorHistScan.destroy(),this.adjScan?.destroy()}};var Rc=`${mt}
4355
4444
  struct Config { count:u32,joints:u32,capacity:u32,list:u32,threshold:f32,dt:f32,pad0:u32,pad1:u32 }
4356
4445
  struct Velocity { incoming:vec4f, incomingAngular:vec4f, linear:vec4f, angular:vec4f }
4357
4446
  struct Bounce { impulse:f32,delta:f32,reboundSpeed:f32,inverseMass:f32,rA:vec4f,rB:vec4f }
@@ -4397,9 +4486,9 @@ fn pointVelocity(i:u32,r:vec2f,incoming:bool)->vec2f{let v=select(velocities[i].
4397
4486
  velocities[i].linear+=vec4f(dv,0.);
4398
4487
  }
4399
4488
  @compute @workgroup_size(64) fn writeBack(@builtin(global_invocation_id) id:vec3u){let i=id.x;if(i>=cfg.count||bodies[i].shape.z<=0.){return;}bodies[i].vel=velocities[i].linear;}
4400
- `,Bs=class extends Gi{constructor(e){super(e,{code:uc,dimension:2})}};function Ln(o){for(let e of["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]){let t=`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`;if(o.split(t).length!==2)throw Error(`Pinned 2D ${e} signature changed; review the step guard`);o=o.replace(t,`${t}
4401
- if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return o}function _n(o){let e="if (col == NO_COLOR) { col = params.colorCap - 1u; }";if(o.split(e).length!==2)throw Error("Pinned 2D color-count layout changed; review the capacity guard");return o.replace(e,`${e}
4402
- if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var En=`// Sleeping is scheduling metadata in velocity.w; physical mass and inertia stay intact.
4489
+ `,_s=class extends Vi{constructor(e){super(e,{code:Rc,dimension:2})}};function Wn(o){for(let e of["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]){let t=`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`;if(o.split(t).length!==2)throw Error(`Pinned 2D ${e} signature changed; review the step guard`);o=o.replace(t,`${t}
4490
+ if (counters[C_OVERFLOW] != 0u || counters[C_CLASHES] != 0u) { return; }`)}return o}function Hn(o){let e="if (col == NO_COLOR) { col = params.colorCap - 1u; }";if(o.split(e).length!==2)throw Error("Pinned 2D color-count layout changed; review the capacity guard");return o.replace(e,`${e}
4491
+ if (col >= params.colorCap) { atomicAdd(&counters[C_CLASHES], 1u); }`)}var Jn=`// Sleeping is scheduling metadata in velocity.w; physical mass and inertia stay intact.
4403
4492
  struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,vel:vec4f,prev:vec4f,shape:vec4f}
4404
4493
  struct Joint {pen:vec4f,lam:vec4f,stiff:vec4f,fmin:vec4f,fmax:vec4f,frac:vec4f,c0:vec4f,anchors:vec4f,param:vec4f}
4405
4494
  struct Contact {ids:vec4u,pl:vec4f,anchors:vec4f,geo:vec4f,misc:vec4f}
@@ -4470,10 +4559,10 @@ fn rotate(angle:f32,p:vec2f)->vec2f{let c=cos(angle);let s=sin(angle);return vec
4470
4559
  }
4471
4560
  if(asleep(i)){atomicAdd(&globals[1],1u);}
4472
4561
  }
4473
- `;function Ss(o={}){if(o!==!0&&(typeof o!="object"||o===null))throw Error("sleeping must be a boolean or options object");let e=o.speedThreshold??.03,t=o.timeThreshold??.5;if(![e,t].every(i=>Number.isFinite(i)&&i>0))throw Error("Sleep thresholds must be positive and finite");return{speedThreshold:e,timeThreshold:t}}function zn(o){for(let e of["warmStartBodies","updateVelocities"])o=o.replace(`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`,`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {
4562
+ `;function zs(o={}){if(o!==!0&&(typeof o!="object"||o===null))throw Error("sleeping must be a boolean or options object");let e=o.speedThreshold??.03,t=o.timeThreshold??.5;if(![e,t].every(i=>Number.isFinite(i)&&i>0))throw Error("Sleep thresholds must be positive and finite");return{speedThreshold:e,timeThreshold:t}}function $n(o){for(let e of["warmStartBodies","updateVelocities"])o=o.replace(`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {`,`fn ${e}(@builtin(global_invocation_id) gid: vec3u) {
4474
4563
  if(gid.x<params.bodyCount && bodies[gid.x].vel.w!=0.){return;}`);return o.replace("fn solveBody(i: u32) {",`fn solveBody(i: u32) {
4475
- if(bodies[i].vel.w!=0.){return;}`)}function Mn(o){return o.replace("return i >= 0 && bodies[i].shape.z > 0.0;","return i >= 0 && bodies[i].shape.z > 0.0 && bodies[i].vel.w==0.;")}var ws=class{constructor(e,t){this.gpu=e,this.device=e.device,this.options=Ss(t);let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.bodyCapacity*16,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=i.createShaderModule({code:En});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["clear","before","contactsWake","jointsWake","decide","applyWake","rest"].map(l=>[l,i.createComputePipeline({layout:a,compute:{module:s,entryPoint:l}})]));let n=new Uint32Array(e.bodyCapacity*4);for(let l=0;l<e.bodyCount;l++)n[l*4+1]=e.sleepEligible?.get(l)===!1?0:1;i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t=!0){let i=Q(this.gpu);for(let r=0;r<4;r++)i.uint(this.state,e*4+r,r===1?Number(t):0);this.wakeRequested=!0}group(e){let t=this.gpu,i=[t.bodyBuffer,this.state,t.infoBuffer,t.jointBuffer,t.contactBuffers[e],t.counterBuffer,this.globals,this.params],r=this.groups?.find(s=>s.buffers.every((a,n)=>a===i[n]));return r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((s,a)=>({binding:a,resource:{buffer:s}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),r.group}dispatch(e,t,i){e.setBindGroup(0,this.group(t));for(let[r,s]of i)s&&(e.setPipeline(this.pipes[r]),e.dispatchWorkgroups(Math.ceil(s/64)))}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();this.dispatch(r,1-this.gpu.parity,e),r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.gpu.flushPropertyEdits(e);let t=this.gpu,i=new ArrayBuffer(32),r=new Uint32Array(i),s=new Float32Array(i);r.set([t.bodyCount,t.jointCount,t.contactCapacity,Number(this.wakeRequested)]),s.set([this.options.speedThreshold,this.options.timeThreshold,t.params.dt,t.params.gravity],4),this.device.queue.writeBuffer(this.params,0,i),this.wakeRequested=!1,this.run([["clear",1],["before",t.bodyCount]],e)}encodeWake(e,t){let i=this.gpu;this.dispatch(e,t,[["contactsWake",i.contactCapacity],["jointsWake",i.jointCount],["decide",i.bodyCount],["applyWake",i.bodyCount]])}after(e){this.run([["rest",this.gpu.bodyCount]],e)}async readStats(){let e=new Uint32Array(await this.gpu.read(this.globals,8));return{sleeping:e[1],wakeRequested:this.wakeRequested||!!e[0]}}destroy(){this.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var Ke=o=>Math.ceil(o/64);function In(){this.staticsDirty&&this.uploadStatics();let o=this.params,e=o.postStabilize&&!o.vbd,t=o.vbd?0:o.alpha,i=o.iterations+(e?1:0);this.writeParams(t,e),this.writePassConstants(i,g=>e?g<o.iterations?1:0:t);let r=this.parity,s=this.bodyCapacity,a=this.bodyCount,n=this.jointCount,l=this.device.createCommandEncoder({label:"avbd2d step"});this.encodeStepPrelude?.(l),l.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),l.clearBuffer(this.tableBuffer),l.clearBuffer(this.adjBuffer,0,(2*s+1)*4);let c=this.timingCallback!==null&&this.timing!==null,f,u=0,d=c||this.splitPasses,h=()=>{f&&!d||(f?.end(),f=l.beginComputePass({label:d?ue[u]:"avbd2d step",timestampWrites:c?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*u,endOfPassWriteIndex:2*u+1}:void 0}),u++)},m=this.groups,p=(g,x,v)=>{v<=0||(f.setPipeline(this.pipes[g]),f.setBindGroup(0,x),f.dispatchWorkgroups(v))},b=(g,x,v)=>{f.setPipeline(this.pipes[g]),f.setBindGroup(0,x),f.dispatchWorkgroupsIndirect(this.argsBuffer,v*4)};h(),p("beginFrame",m.broad,1),p("argsPrev",m.args,1),b("hashInsert",m.contacts[r],6),p("gridCount",m.broad,Ke(a)),this.gridScan.encode(f),p("gridScatter",m.broad,Ke(a)),p("findPairs",m.broad,Ke(a)),p("argsPairs",m.args,1),b("narrowphase",m.contacts[r],0),p("argsContacts",m.args,1),this.sleeping?.encodeWake(f,r),h(),p("degreeJoints",m.topo[r],Ke(n)),b("degreeContacts",m.topo[r],3),this.adjScan.encode(f),p("fillJoints",m.topo[r],Ke(n)),b("fillContacts",m.topo[r],3),p("sortAdjacency",m.topo[r],Ke(a)),h(),p("colorCompact",m.topo[r],Ke(a)),p("colorMark",m.topo[r],Ke(a));for(let g=0;g<this.colorRounds;g++)p(g%2===0?"colorRoundAB":"colorRoundBA",m.topo[r],Ke(a));p("colorCount",m.topo[r],this.colorGroups),this.colorHistScan.encode(f),p("colorStarts",m.topo[r],1),p("argsColors",m.args,1),p("colorScatter",m.topo[r],this.colorGroups),h();let y=this.colorCap+1,A=g=>f.setBindGroup(1,this.passGroup,[g*256]);f.setBindGroup(0,m.solve[r]),A(0),n>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(Ke(n))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(Ke(a));for(let g=0;g<i;g++){let x=this.primalMode==="scan";f.setPipeline(x?this.pipes.primalScan:this.pipes.primal);for(let v=0;v<this.colorCap;v++)A(g*y+v),x?f.dispatchWorkgroups(Ke(a)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*v)*4);g<o.iterations&&(A(g*y+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),g===o.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(Ke(a)))}e&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let w=2*ue.length;if(c){let{querySet:g,resolve:x,read:v}=this.timing;l.resolveQuerySet(g,0,w,x,0),l.copyBufferToBuffer(x,0,v,0,w*8)}if(this.parity=1-r,this.encodeStepPostlude?.(l),this.device.queue.submit([l.finish()]),c){let g=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let x=this.timing.read;x.mapAsync(GPUMapMode.READ).then(()=>{let v=new BigUint64Array(x.getMappedRange()).slice();if(x.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let B=(k,z)=>Number(v[z]-v[k])/1e6,E={total:B(0,w-1)};ue.forEach((k,z)=>E[k]=B(2*z,2*z+1)),g(E)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}var Cs=5;function Xi({minAngle:o=-Math.PI,maxAngle:e=Math.PI}={}){if(!Number.isFinite(o)||!Number.isFinite(e)||o>e)throw Error("Angle limits require finite minAngle <= maxAngle, in radians");return{minAngle:o,maxAngle:e}}function qo(o,e,t){let i=e*T;o.info[e*D]=Cs,o.data.set([1/0,1/0,0],i+$),o.data.set([-1/0,0,0],i+ft),o.data.set([0,1/0,0],i+dt),o.data[i+_e]=t.minAngle,o.data[i+vt]=t.maxAngle}function Rn(o){let e=o.topology;for(let t=0;t<e.jointCount;t++)e.info[t*D]===Cs&&o.device.queue.writeBuffer(o.jointBuffer,(t*36+32)*4,new Float32Array([e.data[t*T+ge],e.data[t*T+_e],e.data[t*T+vt],0]))}var Ar=(o,e,t)=>{if(o.split(e).length!==2)throw Error("Pinned 2D angular layout changed");return o.replace(e,t)};function On(o){return o=Ar(o,"case T_JOINT: { return 3u; }",`case T_JOINT: { return 3u; }
4476
- case 5: { return 2u; }`),o=Ar(o,"fn jointRowsC(j: u32, alpha: f32) -> vec3f {",`
4564
+ if(bodies[i].vel.w!=0.){return;}`)}function Xn(o){return o.replace("return i >= 0 && bodies[i].shape.z > 0.0;","return i >= 0 && bodies[i].shape.z > 0.0 && bodies[i].vel.w==0.;")}var Es=class{constructor(e,t){this.gpu=e,this.device=e.device,this.options=zs(t);let i=this.device,r=GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC;this.state=i.createBuffer({size:e.bodyCapacity*16,usage:r}),this.globals=i.createBuffer({size:8,usage:r}),this.params=i.createBuffer({size:32,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});let s=i.createShaderModule({code:Jn});this.layout=i.createBindGroupLayout({entries:["storage","storage","read-only-storage","read-only-storage","read-only-storage","read-only-storage","storage","uniform"].map((l,c)=>({binding:c,visibility:GPUShaderStage.COMPUTE,buffer:{type:l}}))});let a=i.createPipelineLayout({bindGroupLayouts:[this.layout]});this.pipes=Object.fromEntries(["clear","before","contactsWake","jointsWake","decide","applyWake","rest"].map(l=>[l,i.createComputePipeline({layout:a,compute:{module:s,entryPoint:l}})]));let n=new Uint32Array(e.bodyCapacity*4);for(let l=0;l<e.bodyCount;l++)n[l*4+1]=e.sleepEligible?.get(l)===!1?0:1;i.queue.writeBuffer(this.state,0,n),this.wakeRequested=!1}register(e,t=!0){let i=Q(this.gpu);for(let r=0;r<4;r++)i.uint(this.state,e*4+r,r===1?Number(t):0);this.wakeRequested=!0}group(e){let t=this.gpu,i=[t.bodyBuffer,this.state,t.infoBuffer,t.jointBuffer,t.contactBuffers[e],t.counterBuffer,this.globals,this.params],r=this.groups?.find(s=>s.buffers.every((a,n)=>a===i[n]));return r||(r={buffers:i,group:this.device.createBindGroup({layout:this.layout,entries:i.map((s,a)=>({binding:a,resource:{buffer:s}}))})},this.groups=[...(this.groups??[]).slice(-1),r]),r.group}dispatch(e,t,i){e.setBindGroup(0,this.group(t));for(let[r,s]of i)s&&(e.setPipeline(this.pipes[r]),e.dispatchWorkgroups(Math.ceil(s/64)))}run(e,t){let i=t??this.device.createCommandEncoder(),r=i.beginComputePass();this.dispatch(r,1-this.gpu.parity,e),r.end(),t||this.device.queue.submit([i.finish()])}before(e){this.gpu.flushPropertyEdits(e);let t=this.gpu,i=new ArrayBuffer(32),r=new Uint32Array(i),s=new Float32Array(i);r.set([t.bodyCount,t.jointCount,t.contactCapacity,Number(this.wakeRequested)]),s.set([this.options.speedThreshold,this.options.timeThreshold,t.params.dt,t.params.gravity],4),this.device.queue.writeBuffer(this.params,0,i),this.wakeRequested=!1,this.run([["clear",1],["before",t.bodyCount]],e)}encodeWake(e,t){let i=this.gpu;this.dispatch(e,t,[["contactsWake",i.contactCapacity],["jointsWake",i.jointCount],["decide",i.bodyCount],["applyWake",i.bodyCount]])}after(e){this.run([["rest",this.gpu.bodyCount]],e)}async readStats(){let e=new Uint32Array(await this.gpu.read(this.globals,8));return{sleeping:e[1],wakeRequested:this.wakeRequested||!!e[0]}}destroy(){this.gpu.propertyEdits?.forget(this.state),this.state.destroy(),this.globals.destroy(),this.params.destroy()}};var Ze=o=>Math.ceil(o/64);function Yn(){this.staticsDirty&&this.uploadStatics();let o=this.params,e=o.postStabilize&&!o.vbd,t=o.vbd?0:o.alpha,i=o.iterations+(e?1:0);this.writeParams(t,e),this.writePassConstants(i,g=>e?g<o.iterations?1:0:t);let r=this.parity,s=this.bodyCapacity,a=this.bodyCount,n=this.jointCount,l=this.device.createCommandEncoder({label:"avbd2d step"});this.encodeStepPrelude?.(l),l.clearBuffer(this.gridBuffer,0,(2*this.tableSize+1)*4),l.clearBuffer(this.tableBuffer),l.clearBuffer(this.adjBuffer,0,(2*s+1)*4);let c=this.timingCallback!==null&&this.timing!==null,f,u=0,d=c||this.splitPasses,p=()=>{f&&!d||(f?.end(),f=l.beginComputePass({label:d?fe[u]:"avbd2d step",timestampWrites:c?{querySet:this.timing.querySet,beginningOfPassWriteIndex:2*u,endOfPassWriteIndex:2*u+1}:void 0}),u++)},m=this.groups,h=(g,A,x)=>{x<=0||(f.setPipeline(this.pipes[g]),f.setBindGroup(0,A),f.dispatchWorkgroups(x))},b=(g,A,x)=>{f.setPipeline(this.pipes[g]),f.setBindGroup(0,A),f.dispatchWorkgroupsIndirect(this.argsBuffer,x*4)};p(),h("beginFrame",m.broad,1),h("argsPrev",m.args,1),b("hashInsert",m.contacts[r],6),h("gridCount",m.broad,Ze(a)),this.gridScan.encode(f),h("gridScatter",m.broad,Ze(a)),h("findPairs",m.broad,Ze(a)),h("argsPairs",m.args,1),b("narrowphase",m.contacts[r],0),h("argsContacts",m.args,1),this.sleeping?.encodeWake(f,r),p(),h("degreeJoints",m.topo[r],Ze(n)),b("degreeContacts",m.topo[r],3),this.adjScan.encode(f),h("fillJoints",m.topo[r],Ze(n)),b("fillContacts",m.topo[r],3),h("sortAdjacency",m.topo[r],Ze(a)),p(),h("colorCompact",m.topo[r],Ze(a)),h("colorMark",m.topo[r],Ze(a));for(let g=0;g<this.colorRounds;g++)h(g%2===0?"colorRoundAB":"colorRoundBA",m.topo[r],Ze(a));h("colorCount",m.topo[r],this.colorGroups),this.colorHistScan.encode(f),h("colorStarts",m.topo[r],1),h("argsColors",m.args,1),h("colorScatter",m.topo[r],this.colorGroups),p();let y=this.colorCap+1,v=g=>f.setBindGroup(1,this.passGroup,[g*256]);f.setBindGroup(0,m.solve[r]),v(0),n>0&&(f.setPipeline(this.pipes.warmStartJoints),f.dispatchWorkgroups(Ze(n))),f.setPipeline(this.pipes.warmStartBodies),f.dispatchWorkgroups(Ze(a));for(let g=0;g<i;g++){let A=this.primalMode==="scan";f.setPipeline(A?this.pipes.primalScan:this.pipes.primal);for(let x=0;x<this.colorCap;x++)v(g*y+x),A?f.dispatchWorkgroups(Ze(a)):f.dispatchWorkgroupsIndirect(this.argsBuffer,(12+3*x)*4);g<o.iterations&&(v(g*y+this.colorCap),f.setPipeline(this.pipes.dual),f.dispatchWorkgroupsIndirect(this.argsBuffer,36)),g===o.iterations-1&&(f.setPipeline(this.pipes.updateVelocities),f.dispatchWorkgroups(Ze(a)))}e&&(f.setPipeline(this.pipes.refreshStick),f.dispatchWorkgroupsIndirect(this.argsBuffer,12)),f.end();let B=2*fe.length;if(c){let{querySet:g,resolve:A,read:x}=this.timing;l.resolveQuerySet(g,0,B,A,0),l.copyBufferToBuffer(A,0,x,0,B*8)}if(this.parity=1-r,this.encodeStepPostlude?.(l),this.device.queue.submit([l.finish()]),c){let g=this.timingCallback;this.timingCallback=null,this.timingBusy=!0;let A=this.timing.read;A.mapAsync(GPUMapMode.READ).then(()=>{let x=new BigUint64Array(A.getMappedRange()).slice();if(A.unmap(),this.timingBusy=!1,this.destroyed)return this.releaseTiming();let w=(k,z)=>Number(x[z]-x[k])/1e6,E={total:w(0,B-1)};fe.forEach((k,z)=>E[k]=w(2*z,2*z+1)),g(E)},()=>{this.timingBusy=!1,this.destroyed&&this.releaseTiming()})}}var Ms=5;function er({minAngle:o=-Math.PI,maxAngle:e=Math.PI}={}){if(!Number.isFinite(o)||!Number.isFinite(e)||o>e)throw Error("Angle limits require finite minAngle <= maxAngle, in radians");return{minAngle:o,maxAngle:e}}function ia(o,e,t){let i=e*T;o.info[e*D]=Ms,o.data.set([1/0,1/0,0],i+$),o.data.set([-1/0,0,0],i+ft),o.data.set([0,1/0,0],i+dt),o.data[i+Ee]=t.minAngle,o.data[i+vt]=t.maxAngle}function Qn(o){let e=o.topology;for(let t=0;t<e.jointCount;t++)e.info[t*D]===Ms&&o.device.queue.writeBuffer(o.jointBuffer,(t*36+32)*4,new Float32Array([e.data[t*T+ye],e.data[t*T+Ee],e.data[t*T+vt],0]))}var Sr=(o,e,t)=>{if(o.split(e).length!==2)throw Error("Pinned 2D angular layout changed");return o.replace(e,t)};function Kn(o){return o=Sr(o,"case T_JOINT: { return 3u; }",`case T_JOINT: { return 3u; }
4565
+ case 5: { return 2u; }`),o=Sr(o,"fn jointRowsC(j: u32, alpha: f32) -> vec3f {",`
4477
4566
  fn limitAngle2D(k:Joint,a:i32,b:i32)->f32 {
4478
4567
  var angleA=0.;if(a>=0){angleA=bodies[a].pose.z;}
4479
4568
  return angleA-bodies[b].pose.z-k.param.x;
@@ -4483,10 +4572,10 @@ fn limitRows2D(k:Joint,a:i32,b:i32,alpha:f32)->vec3f {
4483
4572
  return vec3f(angle-k.param.y,angle-k.param.z,0.)-alpha*k.c0.xyz;
4484
4573
  }
4485
4574
  `+`
4486
- fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),o=Ar(o,` if (t == T_JOINT) {
4575
+ fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),o=Sr(o,` if (t == T_JOINT) {
4487
4576
  // Hard rows`,` if(t==5){return limitRows2D(k,a,b,alpha);}
4488
4577
  if (t == T_JOINT) {
4489
- // Hard rows`),o=Ar(o," let C = jointRowsC(j, alpha);",` let C = jointRowsC(j, alpha);
4578
+ // Hard rows`),o=Sr(o," let C = jointRowsC(j, alpha);",` let C = jointRowsC(j, alpha);
4490
4579
  if(t==5){
4491
4580
  // Unilateral rows only contribute while their force is active. Inside the
4492
4581
  // interval a hinge has no angular stiffness and remains free to rotate.
@@ -4494,8 +4583,8 @@ fn jointRowsC(j: u32, alpha: f32) -> vec3f {`),o=Ar(o,` if (t == T_JOINT) {
4494
4583
  if(F.x!=0.){addRow(acc,vec3f(0.,0.,sg),vec3f(0.),C.x,k.pen.x,k.lam.x,k.stiff.x,k.fmin.x,k.fmax.x);}
4495
4584
  if(F.y!=0.){addRow(acc,vec3f(0.,0.,sg),vec3f(0.),C.y,k.pen.y,k.lam.y,k.stiff.y,k.fmin.y,k.fmax.y);}
4496
4585
  return;
4497
- }`),o=Ar(o," if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }",` if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }
4498
- if(t==5){let angle=limitAngle2D(k,info[j].y,info[j].z);k.c0=vec4f(min(0.,angle-k.param.y),max(0.,angle-k.param.z),0.,0.);}`),o}var jn=`// Analytic circles and capsules, finite two-sided segments and solid half-space planes.
4586
+ }`),o=Sr(o," if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }",` if (t == T_JOINT) { k.c0 = vec4f(jointC(k, info[j].y, info[j].z), 0.0); }
4587
+ if(t==5){let angle=limitAngle2D(k,info[j].y,info[j].z);k.c0=vec4f(min(0.,angle-k.param.y),max(0.,angle-k.param.z),0.,0.);}`),o}var Zn=`// Analytic circles and capsules, finite two-sided segments and solid half-space planes.
4499
4588
  // Kind 4 stores bounding radius, vertex-record offset and vertex count.
4500
4589
  @group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
4501
4590
  struct Face2D { a:vec2f, b:vec2f, feature:u32 }
@@ -4648,7 +4737,7 @@ fn collideShapes2D(a:u32,b:u32)->Collision {
4648
4737
  }
4649
4738
  out.count=count;return out;
4650
4739
  }
4651
- `;var Vo=new WeakMap,Yi=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},ks=(o,e)=>{if(!o||o.length!==2||!Array.from(o).every(Number.isFinite))throw Error(`${e} requires two finite components`);return Array.from(o)},Br=o=>Vo.get(o);function Ps(o){let e=Array.from(o??[]),t=typeof e[0]=="number"?e:e.flatMap(v=>ks(v,"Hull point"));if(t.length<6||t.length%2||!t.every(Number.isFinite))throw Error("A 2D hull requires finite xy pairs spanning an area");let i=[];for(let v=0;v<t.length;v+=2)i.push([t[v],t[v+1]]);i.sort((v,B)=>v[0]-B[0]||v[1]-B[1]);let r=i.filter((v,B)=>!B||v[0]!==i[B-1][0]||v[1]!==i[B-1][1]),s=(v,B,E)=>(B[0]-v[0])*(E[1]-v[1])-(B[1]-v[1])*(E[0]-v[0]),a=v=>{let B=[];for(let E of v){for(;B.length>1&&s(B.at(-2),B.at(-1),E)<=0;)B.pop();B.push(E)}return B.slice(0,-1)},n=[...a(r),...a([...r].reverse())];if(n.length<3||n.length>32)throw Error("A 2D hull needs 3\u201332 boundary vertices spanning an area");let l=n[0],c=n.map(v=>[v[0]-l[0],v[1]-l[1]]),f=0,u=0,d=0;for(let v=0;v<c.length;v++){let B=c[v],E=c[(v+1)%c.length],k=B[0]*E[1]-E[0]*B[1];f+=k,u+=(B[0]+E[0])*k,d+=(B[1]+E[1])*k}if(!(f>0)||!Number.isFinite(f))throw Error("Hull area must be positive and finite");u/=3*f,d/=3*f;let h=c.map(v=>[v[0]-u,v[1]-d]),m=0,p=0,b=0,y=0,A=[];for(let v=0;v<h.length;v++){let B=h[v],E=h[(v+1)%h.length],k=B[0]*E[1]-E[0]*B[1];m+=k*(B[0]*B[0]+B[0]*E[0]+E[0]*E[0]+B[1]*B[1]+B[1]*E[1]+E[1]*E[1])/12;let z=E[0]-B[0],M=E[1]-B[1],P=Math.hypot(z,M);A.push(B[0],B[1],M/P,-z/P),p=Math.max(p,Math.hypot(...B)),b=Math.max(b,Math.abs(B[0])),y=Math.max(y,Math.abs(B[1]))}let w=f/2,g=m/w;if(![w,g,p,b,y,...A].every(v=>Number.isFinite(Math.fround(v)))||Math.fround(w)<=0||Math.fround(g)<=0)throw Error("Hull geometry exceeds GPU floating-point range");let x=[4,p,0,h.length];return x.vertices=A,{geometry:x,size:[2*b,2*y],area:w,momentPerMass:g,radius:p}}function Ls(o){let e=Yi(o,"radius");return{geometry:[1,e,0,0],size:[2*e,2*e],area:Math.PI*e*e,momentPerMass:e*e/2,radius:e}}function _s(o,e){let t=Yi(o,"radius");if(Yi(e,"length"),e<2*t)throw Error("Capsule length must be at least twice its radius");return Tn((e-2*t)/2,t)}function Tn(o,e){let t=2*o,i=2*e*t,r=Math.PI*e*e,s=i+r,a=i*(t*t+4*e*e)/12+r*(t*t/4+e*e/2+4*t*e/(3*Math.PI));return{geometry:[2,e,o,0],size:[t+2*e,Math.max(2*e,1e-6)],area:s,momentPerMass:s?a/s:0,radius:o+e}}function Es(o,e,t={}){let i=ks(o,"start"),r=ks(e,"end"),s=r[0]-i[0],a=r[1]-i[1],n=Math.hypot(s,a),l=Yi(t.radius??0,"radius",!0);if(Yi(n,"segment length"),t.position!==void 0||t.angle!==void 0)throw Error("Segment position and angle come from its endpoints");if(l===0&&(t.density??0)!==0)throw Error("A zero-thickness segment must be static; give it a radius for a dynamic capsule");return{...Tn(n/2,l),options:{...t,density:t.density??0,position:[(i[0]+r[0])/2,(i[1]+r[1])/2],angle:Math.atan2(a,s)}}}function zs(o,e=0,t={}){let i=ks(o,"normal"),r=Yi(Math.hypot(...i),"normal length");if(!Number.isFinite(e))throw Error("Plane offset must be finite");if((t.density??0)!==0)throw Error("An infinite plane must be static");if(t.position!==void 0||t.angle!==void 0)throw Error("Plane position and angle come from its normal and offset");let s=i[0]/r,a=i[1]/r;return{geometry:[3,0,0,0],size:[1,1],area:0,momentPerMass:0,radius:0,options:{...t,density:0,position:[s*e,a*e],angle:Math.atan2(-s,a)}}}function Ms(o,e,t,i){let r=Vo.get(o);r||Vo.set(o,r=new Map),r.set(e,t.geometry);let s=t.area*i,a=e*4;o.shape[a+2]=s,o.shape[a+3]=s*t.momentPerMass,o.props[a+1]=t.radius,o.dynamic[e]=+(s>0)}var Dn=`struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,velocity:vec4f,previous:vec4f,shape:vec4f}
4740
+ `;var ra=new WeakMap,tr=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},Is=(o,e)=>{if(!o||o.length!==2||!Array.from(o).every(Number.isFinite))throw Error(`${e} requires two finite components`);return Array.from(o)},kr=o=>ra.get(o);function Rs(o){let e=Array.from(o??[]),t=typeof e[0]=="number"?e:e.flatMap(x=>Is(x,"Hull point"));if(t.length<6||t.length%2||!t.every(Number.isFinite))throw Error("A 2D hull requires finite xy pairs spanning an area");let i=[];for(let x=0;x<t.length;x+=2)i.push([t[x],t[x+1]]);i.sort((x,w)=>x[0]-w[0]||x[1]-w[1]);let r=i.filter((x,w)=>!w||x[0]!==i[w-1][0]||x[1]!==i[w-1][1]),s=(x,w,E)=>(w[0]-x[0])*(E[1]-x[1])-(w[1]-x[1])*(E[0]-x[0]),a=x=>{let w=[];for(let E of x){for(;w.length>1&&s(w.at(-2),w.at(-1),E)<=0;)w.pop();w.push(E)}return w.slice(0,-1)},n=[...a(r),...a([...r].reverse())];if(n.length<3||n.length>32)throw Error("A 2D hull needs 3\u201332 boundary vertices spanning an area");let l=n[0],c=n.map(x=>[x[0]-l[0],x[1]-l[1]]),f=0,u=0,d=0;for(let x=0;x<c.length;x++){let w=c[x],E=c[(x+1)%c.length],k=w[0]*E[1]-E[0]*w[1];f+=k,u+=(w[0]+E[0])*k,d+=(w[1]+E[1])*k}if(!(f>0)||!Number.isFinite(f))throw Error("Hull area must be positive and finite");u/=3*f,d/=3*f;let p=c.map(x=>[x[0]-u,x[1]-d]),m=0,h=0,b=0,y=0,v=[];for(let x=0;x<p.length;x++){let w=p[x],E=p[(x+1)%p.length],k=w[0]*E[1]-E[0]*w[1];m+=k*(w[0]*w[0]+w[0]*E[0]+E[0]*E[0]+w[1]*w[1]+w[1]*E[1]+E[1]*E[1])/12;let z=E[0]-w[0],M=E[1]-w[1],P=Math.hypot(z,M);v.push(w[0],w[1],M/P,-z/P),h=Math.max(h,Math.hypot(...w)),b=Math.max(b,Math.abs(w[0])),y=Math.max(y,Math.abs(w[1]))}let B=f/2,g=m/B;if(![B,g,h,b,y,...v].every(x=>Number.isFinite(Math.fround(x)))||Math.fround(B)<=0||Math.fround(g)<=0)throw Error("Hull geometry exceeds GPU floating-point range");let A=[4,h,0,p.length];return A.vertices=v,{geometry:A,size:[2*b,2*y],area:B,momentPerMass:g,radius:h}}function Os(o){let e=tr(o,"radius");return{geometry:[1,e,0,0],size:[2*e,2*e],area:Math.PI*e*e,momentPerMass:e*e/2,radius:e}}function js(o,e){let t=tr(o,"radius");if(tr(e,"length"),e<2*t)throw Error("Capsule length must be at least twice its radius");return el((e-2*t)/2,t)}function el(o,e){let t=2*o,i=2*e*t,r=Math.PI*e*e,s=i+r,a=i*(t*t+4*e*e)/12+r*(t*t/4+e*e/2+4*t*e/(3*Math.PI));return{geometry:[2,e,o,0],size:[t+2*e,Math.max(2*e,1e-6)],area:s,momentPerMass:s?a/s:0,radius:o+e}}function Ts(o,e,t={}){let i=Is(o,"start"),r=Is(e,"end"),s=r[0]-i[0],a=r[1]-i[1],n=Math.hypot(s,a),l=tr(t.radius??0,"radius",!0);if(tr(n,"segment length"),t.position!==void 0||t.angle!==void 0)throw Error("Segment position and angle come from its endpoints");if(l===0&&(t.density??0)!==0)throw Error("A zero-thickness segment must be static; give it a radius for a dynamic capsule");return{...el(n/2,l),options:{...t,density:t.density??0,position:[(i[0]+r[0])/2,(i[1]+r[1])/2],angle:Math.atan2(a,s)}}}function Ds(o,e=0,t={}){let i=Is(o,"normal"),r=tr(Math.hypot(...i),"normal length");if(!Number.isFinite(e))throw Error("Plane offset must be finite");if((t.density??0)!==0)throw Error("An infinite plane must be static");if(t.position!==void 0||t.angle!==void 0)throw Error("Plane position and angle come from its normal and offset");let s=i[0]/r,a=i[1]/r;return{geometry:[3,0,0,0],size:[1,1],area:0,momentPerMass:0,radius:0,options:{...t,density:0,position:[s*e,a*e],angle:Math.atan2(-s,a)}}}function Gs(o,e,t,i){let r=ra.get(o);r||ra.set(o,r=new Map),r.set(e,t.geometry);let s=t.area*i,a=e*4;o.shape[a+2]=s,o.shape[a+3]=s*t.momentPerMass,o.props[a+1]=t.radius,o.dynamic[e]=+(s>0)}var tl=`struct Body {pose:vec4f,initial:vec4f,inertial:vec4f,velocity:vec4f,previous:vec4f,shape:vec4f}
4652
4741
  struct Command { header:vec4u, value:vec4f, point:vec4f }
4653
4742
  @group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
4654
4743
  @group(0) @binding(1) var<storage,read> commands:array<Command>;
@@ -4684,11 +4773,11 @@ fn edit(@builtin(global_invocation_id) id:vec3u) {
4684
4773
  bodies[index]=b;
4685
4774
  }
4686
4775
  \r
4687
- `;function xt(o,e){let t=o&&typeof o.x=="number"?[o.x,o.y]:Array.isArray(o)||ArrayBuffer.isView(o)&&!(o instanceof DataView)?o:Array.from(o??[]);if(t.length!==2||!t.every(Number.isFinite))throw Error(`${e} requires two finite components`);return t}var mi=class{constructor(){this.count=0,this.capacity=0}reserve(e){if(e<=this.capacity)return;let t=Math.max(e,this.capacity*2,64),i=new ArrayBuffer(t*48),r=new Float32Array(i),s=new Uint32Array(i);this.u32&&s.set(this.u32.subarray(0,this.count*12)),this.words=r,this.u32=s,this.capacity=t}append(e,t,i,r,s=!1){this.reserve(this.count+1),Gn(this.words,this.u32,this.count++*12,e,t,i,r,s)}};function Gn(o,e,t,i,r,s,a,n){e[t]=i,e[t+1]=r,e[t+2]=Number(n),e[t+3]=0;for(let l=0;l<4;l++)o[t+4+l]=s[l]??0,o[t+8+l]=a?.[l]??0}var Qi=class{constructor(e,t=Dn,i=2){this.solver=e,this.shader=t,this.dimension=i,this.pending=new Map,this.queue=new mi,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.hasTeleports=!1,this.motionOnly=!0,this.freeRanges=[]}slot(e,t){this.requested++,this.linearOnly&&=t===0,this.angularOnly&&=t===1,this.motionOnly&&=t===0||t===1,this.hasTeleports||=t===4;let i=this.pending.get(e);if(i&&(t===0||t===1)&&this.queue.u32[i.tail*12+1]===t)return i.tail;if(this.queue.reserve(this.queue.count+1),!this.next||this.next.length<this.queue.capacity){let s=new Int32Array(this.queue.capacity);this.next&&s.set(this.next),this.next=s}let r=this.queue.count++;return this.next[r]=-1,i?(this.next[i.tail]=r,i.tail=r,i.length++):(i=this.freeRanges.pop()??{},i.head=i.tail=r,i.length=1,this.pending.set(e,i)),r}enqueue(e,t,i,r,s=!1){let a=this.slot(e,t);Gn(this.queue.words,this.queue.u32,a*12,e,t,i,r,s)}enqueueValidated(e){for(let t=0;t<e.count;t++){let i=t*12,r=this.slot(e.u32[i],e.u32[i+1])*12;for(let s=0;s<12;s++)this.queue.u32[r+s]=e.u32[i+s]}}flush(e){if(!this.pending.size)return;let t=this.solver.device,i=this.pending.size;if(this.linearOnly||this.angularOnly){let r=this.angularOnly&&this.dimension===2?1:this.dimension;if(!this.linearIndices||this.linearIndices.length<i){let l=Math.max(i,(this.linearIndices?.length??0)*2,64);this.linearIndices=new Uint32Array(l),this.linearValues=new Float32Array(l*this.dimension)}let s=0;for(let[l,c]of this.pending){this.linearIndices[s]=l;for(let f=0;f<r;f++)this.linearValues[s*r+f]=this.queue.words[c.tail*12+4+f];s++}let a=this.angularOnly?"angularVelocityBatch":"velocityBatch",n=this.solver[a]??=new Ct(this.solver,this.dimension,this.angularOnly?"angular":"linear");n.submitValidated(this.linearIndices.subarray(0,i),this.linearValues.subarray(0,i*r),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:n.lastBatch.uploadedBytes,path:this.angularOnly?"packed-angular-velocities":"packed-velocities",submissions:e?0:1}}else if(this.motionOnly){let r=this.motionBatch??=new Gt(this.solver,this.dimension),s=r.wordsPerBody;(!this.motionData||this.motionData.length<i*s)&&(this.motionData=new Uint32Array(Math.max(i*s,(this.motionData?.length??0)*2,64*s)),this.motionFloats=new Float32Array(this.motionData.buffer));let a=0;for(let[n,l]of this.pending){let c=a++*s;this.motionData[c]=n;let f=0;for(let u=l.head;u!==-1;u=this.next[u]){let d=this.queue.u32[u*12+1];f|=1<<d;let h=c+(d===0?2:2+this.dimension),m=d===0||this.dimension===3?this.dimension:1;for(let p=0;p<m;p++)this.motionFloats[h+p]=this.queue.words[u*12+4+p]}this.motionData[c+1]=f}r.submitValidated(this.motionData.subarray(0,i*s),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:r.lastBatch.uploadedBytes,path:"packed-motion",submissions:e?0:1}}else{this.upload??=new mi,this.upload.count=0,this.upload.reserve(this.queue.count),(!this.rangeData||this.rangeData.length<i*2)&&(this.rangeData=new Uint32Array(Math.max(i*2,(this.rangeData?.length??0)*2,128)));let r=0,s=0;for(let h of this.pending.values()){this.rangeData[s++]=r,this.rangeData[s++]=h.length;for(let m=h.head;m!==-1;m=this.next[m]){for(let p=0;p<12;p++)this.upload.u32[r*12+p]=this.queue.u32[m*12+p];r++}}let a=r*48,n=i*8,l=Math.min(t.limits.maxStorageBufferBindingSize,t.limits.maxBufferSize);if(a>l||n>l)throw Error("Body command batch exceeds GPU buffer limits");for(let[h,m]of[["commands",a],["ranges",n]])(!this[h]||this[h].size<m)&&(this[h]?.destroy(),this[h]=t.createBuffer({size:Math.min(l,Math.max(m*2,16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}));t.queue.writeBuffer(this.commands,0,this.upload.words.buffer,0,a),t.queue.writeBuffer(this.ranges,0,this.rangeData.buffer,0,n),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:t.createShaderModule({code:"const DISPATCH_STRIDE = "+t.limits.maxComputeWorkgroupsPerDimension*64+`u;
4688
- `+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==n)&&(this.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.solver.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:n}}]}),this.boundBody=this.solver.bodyBuffer,this.boundCommands=this.commands,this.boundRanges=this.ranges,this.boundRangeBytes=n);let c=e??t.createCommandEncoder(),f=c.beginComputePass();f.setPipeline(this.pipeline),f.setBindGroup(0,this.group);let u=Math.ceil(i/64),d=t.limits.maxComputeWorkgroupsPerDimension;f.dispatchWorkgroups(Math.min(u,d),Math.ceil(u/d)),f.end(),this.encodeAfterEdits(c,i),e||t.queue.submit([c.finish()]),this.lastBatch={bodies:i,commands:r,requestedCommands:this.requested,uploadedBytes:a+n,path:"ordered-commands",submissions:e?0:1}}for(let r of this.pending.values())this.freeRanges.push(r);this.pending.clear(),this.queue.count=0,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.motionOnly=!0,this.hasTeleports=!1}encodeAfterEdits(){}destroy(){this.commands?.destroy(),this.ranges?.destroy(),this.solver.velocityBatch?.destroy(),this.solver.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.freeRanges.length=0,this.pending.clear(),this.queue=this.upload=this.next=this.linearIndices=this.linearValues=this.rangeData=this.motionData=this.motionFloats=null,this.group=null}};var Is=class extends As{writePassConstants(e,t){is.call(this,e,t)}createPipelines(){let e=this.layouts;this.compile(gr,[e.broad],["beginFrame","gridCount","gridScatter","findPairs"]),this.compile(yr,[e.contacts],["hashInsert","narrowphase"]),this.compile(ir,[e.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),this.compile(vr,[e.solve,e.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),this.compile(jr,[e.args],["argsPrev","argsPairs","argsContacts","argsColors"])}constructor(e,t,i={}){i.sleeping!==void 0&&i.sleeping!==!1&&Ss(i.sleeping),super(e,t,i),this.sleepEligible=new Map(t.avbdSleepEligible??[]),Rn(this),Br(t)?.size&&this.enableShapes(),(i.sleeping||[...this.sleepEligible.values()].some(r=>r===!0))&&this.enableSleeping(i.sleeping||{})}enableShapes(){this.shapeBuffer||(this.syncShapeGeometry(),this.shapeContactLayout=this.makeLayout(["uniform","read-only-storage","read-only-storage","storage","read-only-storage","storage","storage","read-only-storage"]),this.compileBroadphase(),this.compileContacts(),this.bindContacts(),this.staticsDirty=!0)}geometryBytes(e=0){let t=(this.geometryRecords??this.bodyCapacity)*4+e;if(this.geometryRecords===void 0)for(let r of Br(this.topology)?.values()??[])t+=r.vertices?.length??0;let i=t*4;if(i>Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,16777216*16))throw Error("2D hull geometry exceeds this GPU's storage capacity");return i}syncShapeGeometry(){let e=this.geometryBytes(),t=new Float32Array(e/4);this.packedShapes=new Map;let i=this.bodyCapacity;for(let[r,s]of Br(this.topology)??[]){let a=Array.from(s);s.vertices&&(a[2]=i,t.set(s.vertices,i*4),i+=s.vertices.length/4),t.set(a,r*4),this.packedShapes.set(r,a)}this.geometryRecords=i,this.shapeBuffer=this.createShapeBuffer(e),this.device.queue.writeBuffer(this.shapeBuffer,0,t)}createShapeBuffer(e){let t=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,268435456);return this.device.createBuffer({label:"2D collider geometry",size:Math.min(t,Math.max(e,Math.ceil(e*1.5/16)*16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC})}appendShapeGeometry(e,t){let i=this.geometryBytes(t.vertices?.length??0);if(this.shapeBuffer.size<i){let s=this.shapeBuffer;this.shapeBuffer=this.createShapeBuffer(i);let a=this.device.createCommandEncoder();a.copyBufferToBuffer(s,0,this.shapeBuffer,0,this.geometryRecords*16),this.device.queue.submit([a.finish()]),s.destroy(),this.bindFilters(),this.bindContacts()}let r=Array.from(t);t.vertices&&(r[2]=this.geometryRecords,this.device.queue.writeBuffer(this.shapeBuffer,this.geometryRecords*16,new Float32Array(t.vertices)),this.geometryRecords+=t.vertices.length/4),this.packedShapes.set(e,r),this.device.queue.writeBuffer(this.shapeBuffer,e*16,new Float32Array(r))}makeLayout(e){return this.device.createBindGroupLayout({entries:e.map((t,i)=>({binding:i,visibility:GPUShaderStage.COMPUTE,buffer:{type:t}}))})}contactSource(){let e=yr;return this.shapeBuffer&&(e=e.replace(`@compute @workgroup_size(64)
4689
- fn narrowphase`,jn+`
4776
+ `;function xt(o,e){let t=o&&typeof o.x=="number"?[o.x,o.y]:Array.isArray(o)||ArrayBuffer.isView(o)&&!(o instanceof DataView)?o:Array.from(o??[]);if(t.length!==2||!t.every(Number.isFinite))throw Error(`${e} requires two finite components`);return t}var xi=class{constructor(){this.count=0,this.capacity=0}reserve(e){if(e<=this.capacity)return;let t=Math.max(e,this.capacity*2,64),i=new ArrayBuffer(t*48),r=new Float32Array(i),s=new Uint32Array(i);this.u32&&s.set(this.u32.subarray(0,this.count*12)),this.words=r,this.u32=s,this.capacity=t}append(e,t,i,r,s=!1){this.reserve(this.count+1),il(this.words,this.u32,this.count++*12,e,t,i,r,s)}};function il(o,e,t,i,r,s,a,n){e[t]=i,e[t+1]=r,e[t+2]=Number(n),e[t+3]=0;for(let l=0;l<4;l++)o[t+4+l]=s[l]??0,o[t+8+l]=a?.[l]??0}var ir=class{constructor(e,t=tl,i=2){this.solver=e,this.shader=t,this.dimension=i,this.pending=new Map,this.queue=new xi,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.hasTeleports=!1,this.motionOnly=!0,this.freeRanges=[]}slot(e,t){this.requested++,this.linearOnly&&=t===0,this.angularOnly&&=t===1,this.motionOnly&&=t===0||t===1,this.hasTeleports||=t===4;let i=this.pending.get(e);if(i&&(t===0||t===1)&&this.queue.u32[i.tail*12+1]===t)return i.tail;if(this.queue.reserve(this.queue.count+1),!this.next||this.next.length<this.queue.capacity){let s=new Int32Array(this.queue.capacity);this.next&&s.set(this.next),this.next=s}let r=this.queue.count++;return this.next[r]=-1,i?(this.next[i.tail]=r,i.tail=r,i.length++):(i=this.freeRanges.pop()??{},i.head=i.tail=r,i.length=1,this.pending.set(e,i)),r}enqueue(e,t,i,r,s=!1){let a=this.slot(e,t);il(this.queue.words,this.queue.u32,a*12,e,t,i,r,s)}enqueueValidated(e){for(let t=0;t<e.count;t++){let i=t*12,r=this.slot(e.u32[i],e.u32[i+1])*12;for(let s=0;s<12;s++)this.queue.u32[r+s]=e.u32[i+s]}}flush(e){if(!this.pending.size)return;let t=this.solver.device,i=this.pending.size;if(this.linearOnly||this.angularOnly){let r=this.angularOnly&&this.dimension===2?1:this.dimension;if(!this.linearIndices||this.linearIndices.length<i){let l=Math.max(i,(this.linearIndices?.length??0)*2,64);this.linearIndices=new Uint32Array(l),this.linearValues=new Float32Array(l*this.dimension)}let s=0;for(let[l,c]of this.pending){this.linearIndices[s]=l;for(let f=0;f<r;f++)this.linearValues[s*r+f]=this.queue.words[c.tail*12+4+f];s++}let a=this.angularOnly?"angularVelocityBatch":"velocityBatch",n=this.solver[a]??=new Lt(this.solver,this.dimension,this.angularOnly?"angular":"linear");n.submitValidated(this.linearIndices.subarray(0,i),this.linearValues.subarray(0,i*r),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:n.lastBatch.uploadedBytes,path:this.angularOnly?"packed-angular-velocities":"packed-velocities",submissions:e?0:1}}else if(this.motionOnly){let r=this.motionBatch??=new Ht(this.solver,this.dimension),s=r.wordsPerBody;(!this.motionData||this.motionData.length<i*s)&&(this.motionData=new Uint32Array(Math.max(i*s,(this.motionData?.length??0)*2,64*s)),this.motionFloats=new Float32Array(this.motionData.buffer));let a=0;for(let[n,l]of this.pending){let c=a++*s;this.motionData[c]=n;let f=0;for(let u=l.head;u!==-1;u=this.next[u]){let d=this.queue.u32[u*12+1];f|=1<<d;let p=c+(d===0?2:2+this.dimension),m=d===0||this.dimension===3?this.dimension:1;for(let h=0;h<m;h++)this.motionFloats[p+h]=this.queue.words[u*12+4+h]}this.motionData[c+1]=f}r.submitValidated(this.motionData.subarray(0,i*s),e),this.lastBatch={bodies:i,commands:this.queue.count,requestedCommands:this.requested,uploadedBytes:r.lastBatch.uploadedBytes,path:"packed-motion",submissions:e?0:1}}else{this.upload??=new xi,this.upload.count=0,this.upload.reserve(this.queue.count),(!this.rangeData||this.rangeData.length<i*2)&&(this.rangeData=new Uint32Array(Math.max(i*2,(this.rangeData?.length??0)*2,128)));let r=0,s=0;for(let p of this.pending.values()){this.rangeData[s++]=r,this.rangeData[s++]=p.length;for(let m=p.head;m!==-1;m=this.next[m]){for(let h=0;h<12;h++)this.upload.u32[r*12+h]=this.queue.u32[m*12+h];r++}}let a=r*48,n=i*8,l=Math.min(t.limits.maxStorageBufferBindingSize,t.limits.maxBufferSize);if(a>l||n>l)throw Error("Body command batch exceeds GPU buffer limits");for(let[p,m]of[["commands",a],["ranges",n]])(!this[p]||this[p].size<m)&&(this[p]?.destroy(),this[p]=t.createBuffer({size:Math.min(l,Math.max(m*2,16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}));t.queue.writeBuffer(this.commands,0,this.upload.words.buffer,0,a),t.queue.writeBuffer(this.ranges,0,this.rangeData.buffer,0,n),this.pipeline??=t.createComputePipeline({layout:"auto",compute:{entryPoint:"edit",module:t.createShaderModule({code:"const DISPATCH_STRIDE = "+t.limits.maxComputeWorkgroupsPerDimension*64+`u;
4777
+ `+this.shader})}}),(!this.group||this.boundBody!==this.solver.bodyBuffer||this.boundCommands!==this.commands||this.boundRanges!==this.ranges||this.boundRangeBytes!==n)&&(this.group=t.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.solver.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:n}}]}),this.boundBody=this.solver.bodyBuffer,this.boundCommands=this.commands,this.boundRanges=this.ranges,this.boundRangeBytes=n);let c=e??t.createCommandEncoder(),f=c.beginComputePass();f.setPipeline(this.pipeline),f.setBindGroup(0,this.group);let u=Math.ceil(i/64),d=t.limits.maxComputeWorkgroupsPerDimension;f.dispatchWorkgroups(Math.min(u,d),Math.ceil(u/d)),f.end(),this.encodeAfterEdits(c,i),e||t.queue.submit([c.finish()]),this.lastBatch={bodies:i,commands:r,requestedCommands:this.requested,uploadedBytes:a+n,path:"ordered-commands",submissions:e?0:1}}for(let r of this.pending.values())this.freeRanges.push(r);this.pending.clear(),this.queue.count=0,this.requested=0,this.linearOnly=!0,this.angularOnly=!0,this.motionOnly=!0,this.hasTeleports=!1}encodeAfterEdits(){}destroy(){this.commands?.destroy(),this.ranges?.destroy(),this.solver.velocityBatch?.destroy(),this.solver.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.freeRanges.length=0,this.pending.clear(),this.queue=this.upload=this.next=this.linearIndices=this.linearValues=this.rangeData=this.motionData=this.motionFloats=null,this.group=null}};var Fs=class extends Ls{writePassConstants(e,t){as.call(this,e,t)}createPipelines(){let e=this.layouts;this.compile(Ar,[e.broad],["beginFrame","gridCount","gridScatter","findPairs"]),this.compile(wr,[e.contacts],["hashInsert","narrowphase"]),this.compile(nr,[e.topo],["degreeJoints","degreeContacts","fillJoints","fillContacts","sortAdjacency","colorCompact","colorMark","colorRoundAB","colorRoundBA","colorCount","colorStarts","colorScatter"]),this.compile(Br,[e.solve,e.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]),this.compile(Fr,[e.args],["argsPrev","argsPairs","argsContacts","argsColors"])}constructor(e,t,i={}){i.sleeping!==void 0&&i.sleeping!==!1&&zs(i.sleeping),super(e,t,i),this.sleepEligible=new Map(t.avbdSleepEligible??[]),Qn(this),kr(t)?.size&&this.enableShapes(),(i.sleeping||[...this.sleepEligible.values()].some(r=>r===!0))&&this.enableSleeping(i.sleeping||{})}enableShapes(){this.shapeBuffer||(this.syncShapeGeometry(),this.shapeContactLayout=this.makeLayout(["uniform","read-only-storage","read-only-storage","storage","read-only-storage","storage","storage","read-only-storage"]),this.compileBroadphase(),this.compileContacts(),this.bindContacts(),this.staticsDirty=!0)}geometryBytes(e=0){let t=(this.geometryRecords??this.bodyCapacity)*4+e;if(this.geometryRecords===void 0)for(let r of kr(this.topology)?.values()??[])t+=r.vertices?.length??0;let i=t*4;if(i>Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,16777216*16))throw Error("2D hull geometry exceeds this GPU's storage capacity");return i}syncShapeGeometry(){let e=this.geometryBytes(),t=new Float32Array(e/4);this.packedShapes=new Map;let i=this.bodyCapacity;for(let[r,s]of kr(this.topology)??[]){let a=Array.from(s);s.vertices&&(a[2]=i,t.set(s.vertices,i*4),i+=s.vertices.length/4),t.set(a,r*4),this.packedShapes.set(r,a)}this.geometryRecords=i,this.shapeBuffer=this.createShapeBuffer(e),this.device.queue.writeBuffer(this.shapeBuffer,0,t)}createShapeBuffer(e){let t=Math.min(this.device.limits.maxStorageBufferBindingSize,this.device.limits.maxBufferSize,268435456);return this.device.createBuffer({label:"2D collider geometry",size:Math.min(t,Math.max(e,Math.ceil(e*1.5/16)*16)),usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST|GPUBufferUsage.COPY_SRC})}appendShapeGeometry(e,t){let i=this.geometryBytes(t.vertices?.length??0);if(this.shapeBuffer.size<i){let s=this.shapeBuffer;this.shapeBuffer=this.createShapeBuffer(i);let a=this.device.createCommandEncoder();a.copyBufferToBuffer(s,0,this.shapeBuffer,0,this.geometryRecords*16),this.device.queue.submit([a.finish()]),s.destroy(),this.bindFilters(),this.bindContacts()}let r=Array.from(t);t.vertices&&(r[2]=this.geometryRecords,this.device.queue.writeBuffer(this.shapeBuffer,this.geometryRecords*16,new Float32Array(t.vertices)),this.geometryRecords+=t.vertices.length/4),this.packedShapes.set(e,r),this.device.queue.writeBuffer(this.shapeBuffer,e*16,new Float32Array(r))}makeLayout(e){return this.device.createBindGroupLayout({entries:e.map((t,i)=>({binding:i,visibility:GPUShaderStage.COMPUTE,buffer:{type:t}}))})}contactSource(){let e=wr;return this.shapeBuffer&&(e=e.replace(`@compute @workgroup_size(64)
4778
+ fn narrowphase`,Zn+`
4690
4779
  @compute @workgroup_size(64)
4691
- fn narrowphase`).replace("let col = collideBoxes(A.pose.xyz, A.shape.xy * 0.5, B.pose.xyz, B.shape.xy * 0.5);","let col = collideShapes2D(a,b);")),this.sensorsEnabled&&(e=e.replace("rec.ids = vec4u(a, b, o.feature, 0u);","rec.ids = vec4u(a,b,o.feature,select(0u,0x80000000u,A.initial.w!=0.||B.initial.w!=0.));").replace("if (j >= 0) {","if (j >= 0 && rec.ids.w==0u && (prevContacts[j].ids.w&0x80000000u)==0u) {")),this.bodyCommands&&(e=e.replace("if (j >= 0","if (A.prevVel.w==0. && B.prevVel.w==0. && j >= 0")),e}compileContacts(){this.compile(this.contactSource(),[this.shapeContactLayout??this.layouts.contacts],["hashInsert","narrowphase"])}bindContacts(){this.shapeBuffer&&(this.groups.contacts=this.contactBuffers.map((e,t)=>this.device.createBindGroup({layout:this.shapeContactLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.pairBuffer,e,this.contactBuffers[1-t],this.tableBuffer,this.counterBuffer,this.shapeBuffer].map((i,r)=>({binding:r,resource:{buffer:i}}))})))}compileBroadphase(){let e=["uniform","read-only-storage","storage","storage","storage","read-only-storage","read-only-storage"],t=gr;if(this.shapeBuffer&&(e.push("read-only-storage"),t=t.replace(`fn radius(i: u32) -> f32 {
4780
+ fn narrowphase`).replace("let col = collideBoxes(A.pose.xyz, A.shape.xy * 0.5, B.pose.xyz, B.shape.xy * 0.5);","let col = collideShapes2D(a,b);")),this.sensorsEnabled&&(e=e.replace("rec.ids = vec4u(a, b, o.feature, 0u);","rec.ids = vec4u(a,b,o.feature,select(0u,0x80000000u,A.initial.w!=0.||B.initial.w!=0.));").replace("if (j >= 0) {","if (j >= 0 && rec.ids.w==0u && (prevContacts[j].ids.w&0x80000000u)==0u) {")),this.bodyCommands&&(e=e.replace("if (j >= 0","if (A.prevVel.w==0. && B.prevVel.w==0. && j >= 0")),e}compileContacts(){this.compile(this.contactSource(),[this.shapeContactLayout??this.layouts.contacts],["hashInsert","narrowphase"])}bindContacts(){this.shapeBuffer&&(this.groups.contacts=this.contactBuffers.map((e,t)=>this.device.createBindGroup({layout:this.shapeContactLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.pairBuffer,e,this.contactBuffers[1-t],this.tableBuffer,this.counterBuffer,this.shapeBuffer].map((i,r)=>({binding:r,resource:{buffer:i}}))})))}compileBroadphase(){let e=["uniform","read-only-storage","storage","storage","storage","read-only-storage","read-only-storage"],t=Ar;if(this.shapeBuffer&&(e.push("read-only-storage"),t=t.replace(`fn radius(i: u32) -> f32 {
4692
4781
  return 0.5 * length(bodies[i].shape.xy);
4693
4782
  }`,`@group(0) @binding(7) var<storage,read> geometry:array<vec4f>;
4694
4783
  fn radius(i:u32)->f32{let g=geometry[i];if(g.x==4.){return g.y;}if(g.x==1.||g.x==2.){return g.y+g.z;}return .5*length(bodies[i].shape.xy);}`).replace("return radius(i) > params.maxSmallRadius;","return geometry[i].x==3. || radius(i) > params.maxSmallRadius;").replace("if (dot(d, d) > r * r) { return; }",`if(geometry[a].x==3.||geometry[b].x==3.) {
@@ -4696,13 +4785,13 @@ let plane=select(b,a,geometry[a].x==3.);let other=select(a,b,geometry[a].x==3.);
4696
4785
  if(dot(bodies[other].pose.xy-bodies[plane].pose.xy,normal)>radius(other)){return;}
4697
4786
  } else if(dot(d,d)>r*r){return;}`)),this.filters){let i=e.length;e.push("read-only-storage"),t=t.replace("fn ignored(hi: u32, lo: u32) -> bool {",`@group(0) @binding(${i}) var<storage,read> filters:array<vec2u>;
4698
4787
  fn ignored(hi:u32,lo:u32)->bool{
4699
- if((filters[hi].x&filters[lo].y)==0u || (filters[lo].x&filters[hi].y)==0u){return true;}`)}this.filterLayout=this.makeLayout(e),this.compile(t,[this.filterLayout],["beginFrame","gridCount","gridScatter","findPairs"]),this.bindFilters()}uploadBodies(e=0,t=this.topology.bodyCount-e){this.flushPropertyEdits(),super.uploadBodies(e,t);for(let s=e;s<e+t;s++)this.sleeping?.register(s,this.sleepEligible?.get(s)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let s=e;s<e+t;s++){let a=r?.get(s);i&&a&&i.set(a,(s-e)*4),this.triggers?.has(s)&&this.device.queue.writeBuffer(this.bodyBuffer,s*96+28,new Float32Array([1]))}i&&this.device.queue.writeBuffer(this.shapeBuffer,e*16,i)}uploadStatics(){if(!this.shapeBuffer)return super.uploadStatics();let e=this.topology,t=Br(e),i=[];for(let u=0;u<this.bodyCount;u++)t?.get(u)?.[0]!==3&&i.push(e.props[u*4+1]);i.sort((u,d)=>u-d);let r=i[i.length>>1]??1,s=0,a=1/0;for(let u of i)u<=4*r&&(s=Math.max(s,u));for(let u of i)u>s&&(a=Math.min(a,u));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[],c=[];for(let u=0;u<this.bodyCount;u++)(t?.get(u)?.[0]===3||e.props[u*4+1]>n)&&l.push(u);for(let u=0;u<e.jointCount;u++){let d=e.info[u*4+1],h=e.info[u*4+2];d>=0&&c.push([Math.max(d,h),Math.min(d,h),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:f.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),this.staticsDirty=!1}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new Dt(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[s]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new Dt(this,2),this.shapeQueries.cast(e,t)}liveIndex(e){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 2D body index is required")}queueBodyCommand(e,t,i,r,s=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,s),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new Qi(this),this.compileContacts()),this.bodyCommands}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,xt(t,"velocity"))}setAngularVelocity(e,t){if(!Number.isFinite(t))throw Error("Angular velocity must be finite");return this.queueBodyCommand(e,1,[t,0,0,0])}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,xt(t,"impulse"),i===void 0?void 0:xt(i,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...xt(t,"force"),0,this.params.dt],i===void 0?void 0:xt(i,"point"),i!==void 0)}applyAngularImpulse(e,t){if(!Number.isFinite(t))throw Error("Angular impulse must be finite");return this.queueBodyCommand(e,5,[t,0,0,0])}applyTorque(e,t){if(!Number.isFinite(t))throw Error("Torque must be finite");return this.queueBodyCommand(e,6,[t,0,0,this.params.dt])}teleport(e,t,i){if(i!==void 0&&!Number.isFinite(i))throw Error("Angle must be finite");return this.queueBodyCommand(e,4,[...xt(t,"position"),i??0,0],void 0,i!==void 0)}readBodies(){return this.flushEdits(),super.readBodies()}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.device,i=t.createBuffer({size:96,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(this.bodyBuffer,e*96,i,0,96),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=new Float32Array(i.getMappedRange());return{position:[s[0],s[1]],angle:s[2],linearVelocity:[s[12],s[13]],angularVelocity:s[14],mass:s[22],moment:s[23],sleeping:s[15]!==0}}finally{i.destroy()}}addBox(e,t={}){return this.addShape({size:Array.from(e)},t)}addCircle(e,t={}){return this.addShape(Ls(e),t)}addHull(e,t={}){return this.addShape(Ps(e),t)}addCapsule(e,t,i={}){return this.addShape(_s(e,t),i)}addSegment(e,t,i={}){return this.addShape(Es(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(zs(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:s=[0,0],angle:a=0,velocity:n=[0,0,0],restitution:l=0,isTrigger:c=!1,group:f=4294967295,collidesWith:u=4294967295,allowSleep:d}=t;if(this.destroyed)throw Error("The 2D solver has been destroyed");if(d!==void 0&&typeof d!="boolean")throw Error("allowSleep must be boolean");if(this.bodyCount>=this.bodyCapacity)throw Error("2D body capacity reached; reserve bodyCapacity when creating the solver");if(e.size.length!==2||!e.size.every(p=>Number.isFinite(p)&&p>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(a))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(n.length!==3||!Array.from(n).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([f,u].some(p=>!Number.isInteger(p)||p<0||p>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let h=xt(s,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...h,a],Array.from(n));return e.geometry&&(Ms(this.topology,m,e,i),this.shapeBuffer?this.appendShapeGeometry(m,e.geometry):this.enableShapes(),this.uploadBodies(m,1)),l&&this.setRestitution(m,l),c&&this.setSensor(m,!0),(f!==4294967295||u!==4294967295)&&this.setFilters([m],[f],[u]),this.contactWatch?.setWatched([m],[!this.watchedIndices||this.watchedIndices.has(m)]),this.sleepEligible.set(m,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(m,d!==!1),this.wakeAll(),m}setRestitution(e,t){if(this.liveIndex(e),!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a coefficient in 0..1");t>0&&!this.restitution&&(this.restitution=new Bs(this)),this.restitution?.set(e,t),this.wakeAll()}constraintSlot(e,t){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.topology.handles[e]?.disposed)throw Error("A live 2D constraint slot is required");if(t!==void 0&&this.topology.info[e*D]!==t)throw Error("The constraint has a different type");return e}setMotor(e,{speed:t,maxTorque:i}={}){if(this.constraintSlot(e,ys),t!==void 0&&!Number.isFinite(t)||i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("Motor speed must be finite and maximum torque nonnegative");let r=e*T,s=e*36;t!==void 0&&(this.topology.data[r+ge]=t,Q(this).float(this.jointBuffer,s+32,t)),i!==void 0&&(this.topology.data[r+ft]=-i,this.topology.data[r+dt]=i,Q(this).float(this.jointBuffer,s+12,-i),Q(this).float(this.jointBuffer,s+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Cs);let i=Xi(t);this.topology.data[e*T+_e]=i.minAngle,this.topology.data[e*T+vt]=i.maxAngle,Q(this).float(this.jointBuffer,e*36+32+1,i.minAngle),Q(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)Q(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Vt),this.topology.info[e*D+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");Q(this).float(this.jointBuffer,e*36+28,t),Q(this).float(this.jointBuffer,e*36+28+1,i),this.wakeAll()}disableConstraint(e){if(this.topology.handles[e]?.disposed)return;this.constraintSlot(e),this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll();let t=this.topology.handles[e];t&&(t.alive=!1,t.disposed=!0)}appendJoint(e,t,i,r,s,a=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let n=xt(i,"anchorA"),l=xt(r,"anchorB");if(!s||s.length!==3||Array.from(s).some(f=>f!==1/0&&(!Number.isFinite(f)||f<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");this.flushPropertyEdits();let c=super.appendJoint(e,t,n,l,Array.from(s),a);return this.wakeAll(),c}setFilters(e,t,i){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");let r=n=>{if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return n},s=Array.from(e,(n,l)=>(this.liveIndex(n),[n,r(t[l]),r(i[l])]));if(this.wakeAll(),!this.filters){let n=this.device;this.filters=n.createBuffer({size:this.bodyCapacity*8,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),n.queue.writeBuffer(this.filters,0,new Uint32Array(this.bodyCapacity*2).fill(4294967295)),this.compileBroadphase()}let a=Q(this);for(let[n,l,c]of s)a.uint(this.filters,n*2,l),a.uint(this.filters,n*2+1,c)}bindFilters(){this.filterLayout&&(this.groups.broad=this.device.createBindGroup({layout:this.filterLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer,...this.shapeBuffer?[this.shapeBuffer]:[],...this.filters?[this.filters]:[]].map((e,t)=>({binding:t,resource:{buffer:e}}))}))}rebuildBindings(){super.rebuildBindings(),this.bindFilters(),this.bindContacts()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sensor flag must be boolean");if(this.wakeAll(),t&&!this.sensorsEnabled){this.sensorsEnabled=!0;let i=this.device,r=i.createComputePipeline({layout:"auto",compute:{module:i.createShaderModule({code:"@group(0) @binding(0) var<storage,read_write> data:array<vec4f>; @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){if(id.x*6u+1u<arrayLength(&data)){data[id.x*6u+1u].w=0.;}}"}),entryPoint:"clear"}}),s=i.createBindGroup({layout:r.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.bodyBuffer}}]}),a=i.createCommandEncoder(),n=a.beginComputePass();n.setPipeline(r),n.setBindGroup(0,s),n.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),n.end(),i.queue.submit([a.finish()]),this.compileContacts();let l=vr.replace("b.initial = b.pose;","b.initial = vec4f(b.pose.xyz,b.initial.w);").replace("fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {",`fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {
4788
+ if((filters[hi].x&filters[lo].y)==0u || (filters[lo].x&filters[hi].y)==0u){return true;}`)}this.filterLayout=this.makeLayout(e),this.compile(t,[this.filterLayout],["beginFrame","gridCount","gridScatter","findPairs"]),this.bindFilters()}uploadBodies(e=0,t=this.topology.bodyCount-e){this.flushPropertyEdits(),super.uploadBodies(e,t);for(let s=e;s<e+t;s++)this.sleeping?.register(s,this.sleepEligible?.get(s)!==!1);if(!this.shapeBuffer&&!this.triggers?.size)return;let i=this.shapeBuffer&&t>0?new Float32Array(t*4):null,r=this.packedShapes;for(let s=e;s<e+t;s++){let a=r?.get(s);i&&a&&i.set(a,(s-e)*4),this.triggers?.has(s)&&this.device.queue.writeBuffer(this.bodyBuffer,s*96+28,new Float32Array([1]))}i&&this.device.queue.writeBuffer(this.shapeBuffer,e*16,i)}uploadStatics(){if(!this.shapeBuffer)return super.uploadStatics();let e=this.topology,t=kr(e),i=[];for(let u=0;u<this.bodyCount;u++)t?.get(u)?.[0]!==3&&i.push(e.props[u*4+1]);i.sort((u,d)=>u-d);let r=i[i.length>>1]??1,s=0,a=1/0;for(let u of i)u<=4*r&&(s=Math.max(s,u));for(let u of i)u>s&&(a=Math.min(a,u));let n=a===1/0?s*1.5:(s+a)/2;this.maxSmallRadius=n,this.cellSize=Math.max(2*s*(1+1e-4),.001);let l=[],c=[];for(let u=0;u<this.bodyCount;u++)(t?.get(u)?.[0]===3||e.props[u*4+1]>n)&&l.push(u);for(let u=0;u<e.jointCount;u++){let d=e.info[u*4+1],p=e.info[u*4+2];d>=0&&c.push([Math.max(d,p),Math.min(d,p),u])}for(let u of e.ignoredPairs()){let d=Math.floor(u/2097152);c.push([d,u-d*2097152,4294967295])}c.sort((u,d)=>u[0]-d[0]||u[1]-d[1]),this.largeCount=l.length,this.noCollideCount=c.length;let f=new Uint32Array(Math.max(l.length+3*c.length,4));f.set(l),c.forEach((u,d)=>f.set(u,l.length+3*d)),this.staticBuffer.size<f.byteLength&&(this.staticBuffer.destroy(),this.staticBuffer=this.device.createBuffer({label:"2D boundaries",size:f.byteLength,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),this.rebuildBindings()),this.device.queue.writeBuffer(this.staticBuffer,0,f),this.staticsDirty=!1}async raycast(e,t,i={}){let[r]=await this.raycastAll([{origin:e,direction:t,maxDistance:i.maxDistance}],i);return r}raycastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new Nt(this,2),this.shapeQueries.cast(e.map(i=>({...i,radius:0})),t)}async circleCast(e,t,i,r={}){let[s]=await this.circleCastAll([{origin:e,radius:t,direction:i,maxDistance:r.maxDistance}],r);return s}circleCastAll(e,t={}){return this.flushEdits(),this.shapeQueries??=new Nt(this,2),this.shapeQueries.cast(e,t)}liveIndex(e){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 2D body index is required")}queueBodyCommand(e,t,i,r,s=!1){return this.liveIndex(e),this.prepareBodyCommands().enqueue(e,t,i,r,s),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands||(this.bodyCommands=new ir(this),this.compileContacts()),this.bodyCommands}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,xt(t,"velocity"))}setAngularVelocity(e,t){if(!Number.isFinite(t))throw Error("Angular velocity must be finite");return this.queueBodyCommand(e,1,[t,0,0,0])}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,xt(t,"impulse"),i===void 0?void 0:xt(i,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...xt(t,"force"),0,this.params.dt],i===void 0?void 0:xt(i,"point"),i!==void 0)}applyAngularImpulse(e,t){if(!Number.isFinite(t))throw Error("Angular impulse must be finite");return this.queueBodyCommand(e,5,[t,0,0,0])}applyTorque(e,t){if(!Number.isFinite(t))throw Error("Torque must be finite");return this.queueBodyCommand(e,6,[t,0,0,this.params.dt])}teleport(e,t,i){if(i!==void 0&&!Number.isFinite(i))throw Error("Angle must be finite");return this.queueBodyCommand(e,4,[...xt(t,"position"),i??0,0],void 0,i!==void 0)}readBodies(){return this.flushEdits(),super.readBodies()}async readBodyState(e){this.liveIndex(e),this.flushEdits();let t=this.device,i=t.createBuffer({size:96,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.MAP_READ});try{let r=t.createCommandEncoder();r.copyBufferToBuffer(this.bodyBuffer,e*96,i,0,96),t.queue.submit([r.finish()]),await i.mapAsync(GPUMapMode.READ);let s=new Float32Array(i.getMappedRange());return{position:[s[0],s[1]],angle:s[2],linearVelocity:[s[12],s[13]],angularVelocity:s[14],mass:s[22],moment:s[23],sleeping:s[15]!==0}}finally{i.destroy()}}addBox(e,t={}){return this.addShape({size:Array.from(e)},t)}addCircle(e,t={}){return this.addShape(Os(e),t)}addHull(e,t={}){return this.addShape(Rs(e),t)}addCapsule(e,t,i={}){return this.addShape(js(e,t),i)}addSegment(e,t,i={}){return this.addShape(Ts(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Ds(e,t,i))}addShape(e,t={}){t=e.options??t;let{density:i=1,friction:r=.6,position:s=[0,0],angle:a=0,velocity:n=[0,0,0],restitution:l=0,isTrigger:c=!1,group:f=4294967295,collidesWith:u=4294967295,allowSleep:d}=t;if(this.destroyed)throw Error("The 2D solver has been destroyed");if(d!==void 0&&typeof d!="boolean")throw Error("allowSleep must be boolean");if(this.bodyCount>=this.bodyCapacity)throw Error("2D body capacity reached; reserve bodyCapacity when creating the solver");if(e.size.length!==2||!e.size.every(h=>Number.isFinite(h)&&h>0)||!Number.isFinite(i)||i<0||!Number.isFinite(r)||r<0||!Number.isFinite(a))throw Error("Body dimensions must be positive, density and friction nonnegative, and angle finite");if(n.length!==3||!Array.from(n).every(Number.isFinite))throw Error("Velocity requires [x, y, angular] finite components");if(typeof c!="boolean"||!Number.isFinite(l)||l<0||l>1)throw Error("isTrigger must be boolean and restitution between zero and one");if([f,u].some(h=>!Number.isInteger(h)||h<0||h>4294967295))throw Error("Collision masks must be unsigned 32-bit integers");let p=xt(s,"position");e.geometry&&this.geometryBytes(e.geometry.vertices?.length??0);let m=super.addBody(e.size,i,r,[...p,a],Array.from(n));return e.geometry&&(Gs(this.topology,m,e,i),this.shapeBuffer?this.appendShapeGeometry(m,e.geometry):this.enableShapes(),this.uploadBodies(m,1)),l&&this.setRestitution(m,l),c&&this.setSensor(m,!0),(f!==4294967295||u!==4294967295)&&this.setFilters([m],[f],[u]),this.contactWatch?.setWatched([m],[!this.watchedIndices||this.watchedIndices.has(m)]),this.sleepEligible.set(m,d??!0),d===!0&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(m,d!==!1),this.wakeAll(),m}setRestitution(e,t){if(this.liveIndex(e),!Number.isInteger(e)||e<0||e>=this.bodyCount||!Number.isFinite(t)||t<0||t>1)throw Error("Restitution requires a live body index and a coefficient in 0..1");t>0&&!this.restitution&&(this.restitution=new _s(this)),this.restitution?.set(e,t),this.wakeAll()}constraintSlot(e,t){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.jointCount||this.topology.handles[e]?.disposed)throw Error("A live 2D constraint slot is required");if(t!==void 0&&this.topology.info[e*D]!==t)throw Error("The constraint has a different type");return e}setMotor(e,{speed:t,maxTorque:i}={}){if(this.constraintSlot(e,Ss),t!==void 0&&!Number.isFinite(t)||i!==void 0&&(!Number.isFinite(i)||i<0))throw Error("Motor speed must be finite and maximum torque nonnegative");let r=e*T,s=e*36;t!==void 0&&(this.topology.data[r+ye]=t,Q(this).float(this.jointBuffer,s+32,t)),i!==void 0&&(this.topology.data[r+ft]=-i,this.topology.data[r+dt]=i,Q(this).float(this.jointBuffer,s+12,-i),Q(this).float(this.jointBuffer,s+16,i)),this.wakeAll()}setAngleLimits(e,t){this.constraintSlot(e,Ms);let i=er(t);this.topology.data[e*T+Ee]=i.minAngle,this.topology.data[e*T+vt]=i.maxAngle,Q(this).float(this.jointBuffer,e*36+32+1,i.minAngle),Q(this).float(this.jointBuffer,e*36+32+2,i.maxAngle);for(let r=4;r<8;r++)Q(this).float(this.jointBuffer,e*36+r,0);this.wakeAll()}setWorldAnchor(e,t,i){if(this.constraintSlot(e,Qt),this.topology.info[e*D+1]!==-1||!Number.isFinite(t)||!Number.isFinite(i))throw Error("A world joint and finite xy anchor are required");Q(this).float(this.jointBuffer,e*36+28,t),Q(this).float(this.jointBuffer,e*36+28+1,i),this.wakeAll()}disableConstraint(e){if(this.topology.handles[e]?.disposed)return;this.constraintSlot(e),this.flushPropertyEdits(),super.disableConstraint(e),this.wakeAll();let t=this.topology.handles[e];t&&(t.alive=!1,t.disposed=!0)}appendJoint(e,t,i,r,s,a=1/0){if(e!==-1&&this.liveIndex(e),this.liveIndex(t),e===t)throw Error("Constraint endpoints must differ");let n=xt(i,"anchorA"),l=xt(r,"anchorB");if(!s||s.length!==3||Array.from(s).some(f=>f!==1/0&&(!Number.isFinite(f)||f<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");this.flushPropertyEdits();let c=super.appendJoint(e,t,n,l,Array.from(s),a);return this.wakeAll(),c}setFilters(e,t,i){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(e.length!==t.length||e.length!==i.length)throw Error("Each body index needs a group and collision mask");let r=n=>{if(!Number.isInteger(n)||n<0||n>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return n},s=Array.from(e,(n,l)=>(this.liveIndex(n),[n,r(t[l]),r(i[l])]));if(this.wakeAll(),!this.filters){let n=this.device;this.filters=n.createBuffer({size:this.bodyCapacity*8,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),n.queue.writeBuffer(this.filters,0,new Uint32Array(this.bodyCapacity*2).fill(4294967295)),this.compileBroadphase()}let a=Q(this);for(let[n,l,c]of s)a.uint(this.filters,n*2,l),a.uint(this.filters,n*2+1,c)}bindFilters(){this.filterLayout&&(this.groups.broad=this.device.createBindGroup({layout:this.filterLayout,entries:[this.paramsBuffer,this.bodyBuffer,this.gridBuffer,this.pairBuffer,this.counterBuffer,this.staticBuffer,this.jointBuffer,...this.shapeBuffer?[this.shapeBuffer]:[],...this.filters?[this.filters]:[]].map((e,t)=>({binding:t,resource:{buffer:e}}))}))}rebuildBindings(){super.rebuildBindings(),this.bindFilters(),this.bindContacts()}setSensor(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sensor flag must be boolean");if(this.wakeAll(),t&&!this.sensorsEnabled){this.sensorsEnabled=!0;let i=this.device,r=i.createComputePipeline({layout:"auto",compute:{module:i.createShaderModule({code:"@group(0) @binding(0) var<storage,read_write> data:array<vec4f>; @compute @workgroup_size(64) fn clear(@builtin(global_invocation_id) id:vec3u){if(id.x*6u+1u<arrayLength(&data)){data[id.x*6u+1u].w=0.;}}"}),entryPoint:"clear"}}),s=i.createBindGroup({layout:r.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.bodyBuffer}}]}),a=i.createCommandEncoder(),n=a.beginComputePass();n.setPipeline(r),n.setBindGroup(0,s),n.dispatchWorkgroups(Math.ceil(this.bodyCount/64)),n.end(),i.queue.submit([a.finish()]),this.compileContacts();let l=Br.replace("b.initial = b.pose;","b.initial = vec4f(b.pose.xyz,b.initial.w);").replace("fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {",`fn addContact(acc: ptr<function, Acc>, c: u32, alpha: f32, i: u32) {
4700
4789
  if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace("fn dualContact(c: u32) {",`fn dualContact(c: u32) {
4701
4790
  if((contacts[c].ids.w&0x80000000u)!=0u){return;}`).replace(` let k = contacts[c];
4702
4791
  let bound`,` let k = contacts[c];
4703
4792
  if((k.ids.w&0x80000000u)!=0u){return;}
4704
- let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=ir.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
4705
- if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&Q(this).float(this.bodyBuffer,e*24+7,t?1:0),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}compile(e,t,i){e.includes("fn warmStartBodies(")&&(e=Ln(zn(On(e)))),e.includes("fn dynamicBody(")&&(e=Mn(e)),e.includes("fn colorCount(")&&(e=_n(e));let r=this.device,s=r.createShaderModule({code:e}),a=r.createPipelineLayout({bindGroupLayouts:t});for(let n of i)this.pipes[n]=r.createComputePipeline({layout:a,compute:{module:s,entryPoint:n}})}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array of live body indices");for(let r of i)this.liveIndex(r)}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");return this.contactWatch||(this.contactWatch=new Nt(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array.from({length:this.bodyCount},(r,s)=>this.watchedIndices.has(s)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ut?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}step(){if(this.destroyed)throw Error("The 2D solver has been destroyed");this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending?In.call(this):super.step(),this.contactWatch&&this.contactWatch.run({manifolds:this.contactBuffers[1-this.parity],contacts:this.contactBuffers[1-this.parity],counters:this.counterBuffer},this.contactCapacity,this.eventStep=(this.eventStep??0)+1,this.params.dt)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e)}destroy(){this.passConstantCache=null,this.propertyEdits?.destroy(),!this.destroyed&&(this.detachFromScene(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.filters?.destroy(),this.shapeBuffer?.destroy(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),this.sleeping?.destroy(),super.destroy())}enableSleeping(e={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping?this.sleeping.options=Ss(e):this.sleeping=new ws(this,e),this}disableSleeping(){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.destroy(),this.sleeping=null),this}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.sleepEligible.set(e,t),t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,t),this.wakeAll(),this}wakeAll(){return this.sleeping&&(this.sleeping.wakeRequested=!0),this}wakeUp(e){return this.liveIndex(e),this.wakeAll()}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}flushEdits(){this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}attachToScene(e,{maxSubSteps:t=6,maxFrameTime:i=.05,afterStep:r}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!e?.onBeforeRenderObservable?.add||!e?.onDisposeObservable?.addOnce||!e?.getEngine)throw Error("A Babylon scene is required");if(!Number.isInteger(t)||t<1||!Number.isFinite(i)||i<=0||r!==void 0&&typeof r!="function")throw Error("Scene stepping requires positive frame limits and an optional afterStep function");if(this.sceneBinding){if(this.sceneBinding.scene===e)return this;throw Error("The 2D solver is already attached to another scene")}let s=0,a=e.onBeforeRenderObservable.add(()=>{let l=this.params.dt;if(!(l>0)||!Number.isFinite(l))throw Error("Simulation timestep must be positive and finite");let c=e.getEngine().getDeltaTime()/1e3;s=Math.min(t*l,s+Math.min(i,Math.max(0,c)));for(let f=0;s+1e-12>=l&&f<t&&(this.step(),s=Math.max(0,s-l),r?.(this),!(!this.sceneBinding||this.destroyed));f++);}),n=e.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:e,before:a,dispose:n},this}detachFromScene(){let e=this.sceneBinding;return e&&(e.scene.onBeforeRenderObservable.remove(e.before),e.scene.onDisposeObservable.remove(e.dispose),this.sceneBinding=null),this}};var Rs=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Be(o,e,t){let i=o&&typeof o.x=="number"?[o.x,o.y,...e>=3?[o.z]:[],...e===4?[o.w]:[]]:Array.isArray(o)||ArrayBuffer.isView(o)&&!(o instanceof DataView)?o:Array.from(o??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i}var Fn=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function pc(o,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new mi:this.editStaging??=new mi;this.editInProgress=!0;try{return mc.call(this,o,e,i)}finally{this.editInProgress=t}}function mc(o,e,t){t.count=0;let i,r=(s,a)=>{if(e===3)return Be(s,3,a);if(!Number.isFinite(s))throw Error(`${a} must be finite`);return[s,0,0]};for(let s of o){let a=this.bodyIndex(s.body),{isTrigger:n,restitution:l,group:c,collidesWith:f,allowSleep:u}=s;if(n!==void 0||l!==void 0||c!==void 0||f!==void 0||u!==void 0){if(n!==void 0&&typeof n!="boolean")throw Error("isTrigger must be a boolean");if(u!==void 0&&typeof u!="boolean")throw Error("allowSleep must be a boolean");if(l!==void 0&&(!Number.isFinite(l)||l<0||l>1))throw Error("restitution must be between 0 and 1");if(f!==void 0&&c===void 0)throw Error("A collision-mask edit requires group as well");c!==void 0&&Rs(c),f!==void 0&&Rs(f),(i??=[]).push({index:a,isTrigger:n,restitution:l,group:c,collidesWith:f??4294967295,allowSleep:u})}let d=s.worldPoint===void 0?void 0:Be(s.worldPoint,e,"worldPoint");if(s.position!==void 0){let h=Be(s.position,e,"position"),m;if(s.rotation!==void 0){if(m=e===3?Be(s.rotation,4,"rotation"):[s.rotation],e===3){let p=Math.hypot(...m);if(!p)throw Error("rotation must be nonzero");m=m.map(b=>b/p)}else if(!Number.isFinite(s.rotation))throw Error("angle must be finite")}t.append(a,4,h,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(s.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let h=0;h<Fn.length;h++){if(h===4)continue;let m=Fn[h];if(s[m]===void 0)continue;let b=h===1||h>=5?r(s[m],m):Be(s[m],e,m);t.append(a,h,b,h===2||h===3?d:void 0,(h===2||h===3)&&d!==void 0),(h===3||h===6)&&(t.words[(t.count-1)*12+7]=this.params.dt)}}if(this.destroyed)throw Error("The solver was destroyed while reading body edits");t.count&&this.prepareBodyCommands().enqueueValidated(t);for(let s of i??[])s.isTrigger!==void 0&&this.setSensor(s.index,s.isTrigger),s.restitution!==void 0&&this.setRestitution(s.index,s.restitution),s.group!==void 0&&this.setFilters([s.index],[s.group],[s.collidesWith]),s.allowSleep!==void 0&&this.setSleepEnabled(s.index,s.allowSleep);return this}var Os=class{constructor(e,t){this.solver=e,this.index=t}get gpuIndex(){return this.solver.liveIndex(this.index),this.index}setLinearVelocity(e){return this.solver.setLinearVelocity(this.gpuIndex,e),this}setAngularVelocity(e){return this.solver.setAngularVelocity(this.gpuIndex,e),this}applyImpulse(e,t){return this.solver.applyImpulse(this.gpuIndex,e,t),this}applyForce(e,t){return this.solver.applyForce(this.gpuIndex,e,t),this}applyAngularImpulse(e){return this.solver.applyAngularImpulse(this.gpuIndex,e),this}applyTorque(e){return this.solver.applyTorque(this.gpuIndex,e),this}teleport(e,t){return this.solver.teleport(this.gpuIndex,e,t),this}setTrigger(e){return this.solver.setSensor(this.gpuIndex,e),this.solver.wakeAll(),this}setRestitution(e){return this.solver.setRestitution(this.gpuIndex,e),this.solver.wakeAll(),this}setCollisionGroups(e,t=4294967295){return this.solver.setFilters([this.gpuIndex],[Rs(e)],[Rs(t)]),this.solver.wakeAll(),this}setSleepEnabled(e){return this.solver.setSleepEnabled(this.gpuIndex,e),this}wakeUp(){return this.solver.liveIndex(this.index),this.solver.wakeAll(),this}readState(){return this.solver.readBodyState(this.gpuIndex)}};function Nn(o,{maxSubSteps:e=6,maxFrameTime:t=.05}={}){if(!Number.isInteger(e)||e<1||!Number.isFinite(t)||t<=0)throw Error("Stepping limits must be positive");let i=o.params.dt;if(!Number.isFinite(i)||i<=0)throw Error("Simulation timestep must be positive and finite");return{dt:i,maxSubSteps:e,maxFrameTime:t}}function Un(o,e,t,i){if(o.destroyed)throw Error("The solver has been destroyed");if(!Number.isFinite(e)||e<0)throw Error("elapsed must be finite nonnegative seconds");let{dt:r,maxSubSteps:s,maxFrameTime:a}=Nn(o,t);o.timeAccumulator=Math.min(s*r,(o.timeAccumulator??0)+Math.min(a,e));let n=0;for(;o.timeAccumulator+1e-12>=r&&n<s;)o.step(),o.timeAccumulator=Math.max(0,o.timeAccumulator-r),n++,i?.(o);return n}function bc(o,e){return Un(this,o,e)}function qn(o,{afterStep:e,...t}={}){if(this.destroyed)throw Error("The solver has been destroyed");if(!o?.onBeforeRenderObservable?.add||!o?.onDisposeObservable?.addOnce||!o?.getEngine)throw Error("A Babylon scene is required");if(e!==void 0&&typeof e!="function")throw Error("afterStep must be a function");if(Nn(this,t),this.sceneBinding){if(this.sceneBinding.scene===o)return this;throw Error("The solver is already attached to another scene")}let i=o.onBeforeRenderObservable.add(()=>{Un(this,Math.max(0,o.getEngine().getDeltaTime()/1e3),t,e)}),r=o.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:o,before:i,dispose:r},this}function Vn(){let o=this.sceneBinding;return o&&(o.scene.onBeforeRenderObservable.remove(o.before),o.scene.onDisposeObservable.remove(o.dispose),this.sceneBinding=null,this.timeAccumulator=0),this}function js(o,e,t){o.advance=bc;let i,r;o.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let n=a;if(a instanceof Os){if(a.solver!==this)throw Error("Body belongs to another solver");return a.gpuIndex}return typeof n!="number"&&t===3&&(r||(r=new WeakMap,e.bodies.forEach((l,c)=>r.set(l,this.gpuIndex(c)))),n=r.get(a)),this.liveIndex(n),n},o.body=function(a){let n=this.bodyIndex(a);return i??=new Map,i.has(n)||i.set(n,new Os(this,n)),i.get(n)},o.editBodies=function(a){return pc.call(this,a,t)},o.setLinearVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.velocityBatch??=new Ct(this,t),this.velocityBatch.set(a,n),this},o.setVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return n?.angular===void 0&&n?.linear!==void 0?this.setLinearVelocities(a,n.linear):n?.linear===void 0&&n?.angular!==void 0?this.setAngularVelocities(a,n.angular):(this.motionBatch??=new Gt(this,t),this.motionBatch.set(a,n),this)},o.setAngularVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.angularVelocityBatch??=new Ct(this,t,"angular"),this.angularVelocityBatch.set(a,n),this};let s=o.destroy;return o.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,s.call(this),i?.clear(),r=null,e=null)},t===2&&(o.readSelectedBodies=function(a,{posesOnly:n=!1}={}){let l=a instanceof Uint32Array?a:Array.from(a);return l.forEach(c=>this.liveIndex(c)),this.flushEdits(),this.bodyReadback??=new Ft(this.device),this.bodyReadback.read(this.bodyBuffer,l,n,6)}),o}var Ts=class extends Is{constructor(e,t,i){super(e,t,i),js(this,null,2)}};var Ho=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
4793
+ let bound`);this.compile(l,[this.layouts.solve,this.layouts.pass],["warmStartJoints","warmStartBodies","primal","primalScan","dual","refreshStick","updateVelocities"]);let c=nr.replaceAll("if (gid.x >= topoCount()) { return; }",`if (gid.x >= topoCount()) { return; }
4794
+ if((contacts[gid.x].ids.w&0x80000000u)!=0u){return;}`);this.compile(c,[this.layouts.topo],["degreeContacts","fillContacts"])}this.sensorsEnabled&&Q(this).float(this.bodyBuffer,e*24+7,t?1:0),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}compile(e,t,i){e.includes("fn warmStartBodies(")&&(e=Wn($n(Kn(e)))),e.includes("fn dynamicBody(")&&(e=Xn(e)),e.includes("fn colorCount(")&&(e=Hn(e));let r=this.device,s=r.createShaderModule({code:e}),a=r.createPipelineLayout({bindGroupLayouts:t});for(let n of i)this.pipes[n]=r.createComputePipeline({layout:a,compute:{module:s,entryPoint:n}})}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array of live body indices");for(let r of i)this.liveIndex(r)}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");return this.contactWatch||(this.contactWatch=new $t(this.device,this.bodyBuffer,this.bodyCapacity,e,t,2),i===void 0&&this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array(this.bodyCount).fill(!0))),i!==void 0&&(this.watchedIndices=new Set(i),this.contactWatch.setWatched(Array.from({length:this.bodyCount},(r,s)=>s),Array.from({length:this.bodyCount},(r,s)=>this.watchedIndices.has(s)))),this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Xt?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}step(){if(this.destroyed)throw Error("The 2D solver has been destroyed");this.sleeping||this.restitution?.active.size||this.bodyCommands?.pending.size||this.propertyEdits?.pending?Yn.call(this):super.step(),this.contactWatch&&this.contactWatch.run({manifolds:this.contactBuffers[1-this.parity],contacts:this.contactBuffers[1-this.parity],counters:this.counterBuffer},this.contactCapacity,this.eventStep=(this.eventStep??0)+1,this.params.dt)}encodeStepPrelude(e){this.flushPropertyEdits(e),this.sleeping?.before(e),this.bodyCommands?.flush(e),this.restitution?.before(e)}encodeStepPostlude(e){this.restitution?.after(e),this.sleeping?.after(e)}destroy(){this.passConstantCache=null,this.propertyEdits?.destroy(),!this.destroyed&&(this.detachFromScene(),this.shapeQueries?.destroy(),this.restitution?.destroy(),this.filters?.destroy(),this.shapeBuffer?.destroy(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),this.sleeping?.destroy(),super.destroy())}enableSleeping(e={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping?this.sleeping.options=zs(e):this.sleeping=new Es(this,e),this}disableSleeping(){if(this.destroyed)throw Error("The 2D solver has been destroyed");return this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.destroy(),this.sleeping=null),this}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");return this.sleepEligible.set(e,t),t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,t),this.wakeAll(),this}wakeAll(){return this.sleeping&&(this.sleeping.wakeRequested=!0),this}wakeUp(e){return this.liveIndex(e),this.wakeAll()}async readSleepStats(){return this.sleeping?this.sleeping.readStats():{sleeping:0,wakeRequested:!1}}flushEdits(){this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}flushPropertyEdits(e){this.propertyEdits?.flush(e)}readJoints(){return this.flushPropertyEdits(),super.readJoints()}attachToScene(e,{maxSubSteps:t=6,maxFrameTime:i=.05,afterStep:r}={}){if(this.destroyed)throw Error("The 2D solver has been destroyed");if(!e?.onBeforeRenderObservable?.add||!e?.onDisposeObservable?.addOnce||!e?.getEngine)throw Error("A Babylon scene is required");if(!Number.isInteger(t)||t<1||!Number.isFinite(i)||i<=0||r!==void 0&&typeof r!="function")throw Error("Scene stepping requires positive frame limits and an optional afterStep function");if(this.sceneBinding){if(this.sceneBinding.scene===e)return this;throw Error("The 2D solver is already attached to another scene")}let s=0,a=e.onBeforeRenderObservable.add(()=>{let l=this.params.dt;if(!(l>0)||!Number.isFinite(l))throw Error("Simulation timestep must be positive and finite");let c=e.getEngine().getDeltaTime()/1e3;s=Math.min(t*l,s+Math.min(i,Math.max(0,c)));for(let f=0;s+1e-12>=l&&f<t&&(this.step(),s=Math.max(0,s-l),r?.(this),!(!this.sceneBinding||this.destroyed));f++);}),n=e.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:e,before:a,dispose:n},this}detachFromScene(){let e=this.sceneBinding;return e&&(e.scene.onBeforeRenderObservable.remove(e.before),e.scene.onDisposeObservable.remove(e.dispose),this.sceneBinding=null),this}};var Us=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Be(o,e,t){let i=o&&typeof o.x=="number"?[o.x,o.y,...e>=3?[o.z]:[],...e===4?[o.w]:[]]:Array.isArray(o)||ArrayBuffer.isView(o)&&!(o instanceof DataView)?o:Array.from(o??[]);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i}var rl=["linearVelocity","angularVelocity","impulse","force","","angularImpulse","torque"];function Dc(o,e){if(this.destroyed)throw Error("The solver has been destroyed");let t=this.editInProgress,i=t?new xi:this.editStaging??=new xi;this.editInProgress=!0;try{return Gc.call(this,o,e,i)}finally{this.editInProgress=t}}function Gc(o,e,t){t.count=0;let i,r=(s,a)=>{if(e===3)return Be(s,3,a);if(!Number.isFinite(s))throw Error(`${a} must be finite`);return[s,0,0]};for(let s of o){let a=this.bodyIndex(s.body),{isTrigger:n,restitution:l,group:c,collidesWith:f,allowSleep:u}=s;if(n!==void 0||l!==void 0||c!==void 0||f!==void 0||u!==void 0){if(n!==void 0&&typeof n!="boolean")throw Error("isTrigger must be a boolean");if(u!==void 0&&typeof u!="boolean")throw Error("allowSleep must be a boolean");if(l!==void 0&&(!Number.isFinite(l)||l<0||l>1))throw Error("restitution must be between 0 and 1");if(f!==void 0&&c===void 0)throw Error("A collision-mask edit requires group as well");c!==void 0&&Us(c),f!==void 0&&Us(f),(i??=[]).push({index:a,isTrigger:n,restitution:l,group:c,collidesWith:f??4294967295,allowSleep:u})}let d=s.worldPoint===void 0?void 0:Be(s.worldPoint,e,"worldPoint");if(s.position!==void 0){let p=Be(s.position,e,"position"),m;if(s.rotation!==void 0){if(m=e===3?Be(s.rotation,4,"rotation"):[s.rotation],e===3){let h=Math.hypot(...m);if(!h)throw Error("rotation must be nonzero");m=m.map(b=>b/h)}else if(!Number.isFinite(s.rotation))throw Error("angle must be finite")}t.append(a,4,p,e===3?m:void 0,m!==void 0),e===2&&(t.words[(t.count-1)*12+6]=m?.[0]??0)}else if(s.rotation!==void 0)throw Error("A rotation edit requires position; use teleport for a full pose");for(let p=0;p<rl.length;p++){if(p===4)continue;let m=rl[p];if(s[m]===void 0)continue;let b=p===1||p>=5?r(s[m],m):Be(s[m],e,m);t.append(a,p,b,p===2||p===3?d:void 0,(p===2||p===3)&&d!==void 0),(p===3||p===6)&&(t.words[(t.count-1)*12+7]=this.params.dt)}}if(this.destroyed)throw Error("The solver was destroyed while reading body edits");t.count&&this.prepareBodyCommands().enqueueValidated(t);for(let s of i??[])s.isTrigger!==void 0&&this.setSensor(s.index,s.isTrigger),s.restitution!==void 0&&this.setRestitution(s.index,s.restitution),s.group!==void 0&&this.setFilters([s.index],[s.group],[s.collidesWith]),s.allowSleep!==void 0&&this.setSleepEnabled(s.index,s.allowSleep);return this}var Ns=class{constructor(e,t){this.solver=e,this.index=t}get gpuIndex(){return this.solver.liveIndex(this.index),this.index}setLinearVelocity(e){return this.solver.setLinearVelocity(this.gpuIndex,e),this}setAngularVelocity(e){return this.solver.setAngularVelocity(this.gpuIndex,e),this}applyImpulse(e,t){return this.solver.applyImpulse(this.gpuIndex,e,t),this}applyForce(e,t){return this.solver.applyForce(this.gpuIndex,e,t),this}applyAngularImpulse(e){return this.solver.applyAngularImpulse(this.gpuIndex,e),this}applyTorque(e){return this.solver.applyTorque(this.gpuIndex,e),this}teleport(e,t){return this.solver.teleport(this.gpuIndex,e,t),this}setTrigger(e){return this.solver.setSensor(this.gpuIndex,e),this.solver.wakeAll(),this}setRestitution(e){return this.solver.setRestitution(this.gpuIndex,e),this.solver.wakeAll(),this}setCollisionGroups(e,t=4294967295){return this.solver.setFilters([this.gpuIndex],[Us(e)],[Us(t)]),this.solver.wakeAll(),this}setSleepEnabled(e){return this.solver.setSleepEnabled(this.gpuIndex,e),this}wakeUp(){return this.solver.liveIndex(this.index),this.solver.wakeAll(),this}readState(){return this.solver.readBodyState(this.gpuIndex)}};function sl(o,{maxSubSteps:e=6,maxFrameTime:t=.05}={}){if(!Number.isInteger(e)||e<1||!Number.isFinite(t)||t<=0)throw Error("Stepping limits must be positive");let i=o.params.dt;if(!Number.isFinite(i)||i<=0)throw Error("Simulation timestep must be positive and finite");return{dt:i,maxSubSteps:e,maxFrameTime:t}}function ol(o,e,t,i){if(o.destroyed)throw Error("The solver has been destroyed");if(!Number.isFinite(e)||e<0)throw Error("elapsed must be finite nonnegative seconds");let{dt:r,maxSubSteps:s,maxFrameTime:a}=sl(o,t);o.timeAccumulator=Math.min(s*r,(o.timeAccumulator??0)+Math.min(a,e));let n=0;for(;o.timeAccumulator+1e-12>=r&&n<s;)o.step(),o.timeAccumulator=Math.max(0,o.timeAccumulator-r),n++,i?.(o);return n}function Fc(o,e){return ol(this,o,e)}function al(o,{afterStep:e,...t}={}){if(this.destroyed)throw Error("The solver has been destroyed");if(!o?.onBeforeRenderObservable?.add||!o?.onDisposeObservable?.addOnce||!o?.getEngine)throw Error("A Babylon scene is required");if(e!==void 0&&typeof e!="function")throw Error("afterStep must be a function");if(sl(this,t),this.sceneBinding){if(this.sceneBinding.scene===o)return this;throw Error("The solver is already attached to another scene")}let i=o.onBeforeRenderObservable.add(()=>{ol(this,Math.max(0,o.getEngine().getDeltaTime()/1e3),t,e)}),r=o.onDisposeObservable.addOnce(()=>this.destroy());return this.sceneBinding={scene:o,before:i,dispose:r},this}function nl(){let o=this.sceneBinding;return o&&(o.scene.onBeforeRenderObservable.remove(o.before),o.scene.onDisposeObservable.remove(o.dispose),this.sceneBinding=null,this.timeAccumulator=0),this}function qs(o,e,t){o.advance=Fc;let i,r;o.bodyIndex=function(a){if(this.destroyed)throw Error("The solver has been destroyed");let n=a;if(a instanceof Ns){if(a.solver!==this)throw Error("Body belongs to another solver");return a.gpuIndex}return typeof n!="number"&&t===3&&(r||(r=new WeakMap,e.bodies.forEach((l,c)=>r.set(l,this.gpuIndex(c)))),n=r.get(a)),this.liveIndex(n),n},o.body=function(a){let n=this.bodyIndex(a);return i??=new Map,i.has(n)||i.set(n,new Ns(this,n)),i.get(n)},o.editBodies=function(a){return Dc.call(this,a,t)},o.setLinearVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.velocityBatch??=new Lt(this,t),this.velocityBatch.set(a,n),this},o.setVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return n?.angular===void 0&&n?.linear!==void 0?this.setLinearVelocities(a,n.linear):n?.linear===void 0&&n?.angular!==void 0?this.setAngularVelocities(a,n.angular):(this.motionBatch??=new Ht(this,t),this.motionBatch.set(a,n),this)},o.setAngularVelocities=function(a,n){if(this.destroyed)throw Error("The solver has been destroyed");return this.angularVelocityBatch??=new Lt(this,t,"angular"),this.angularVelocityBatch.set(a,n),this};let s=o.destroy;return o.destroy=function(){this.destroyed||(this.velocityBatch?.destroy(),this.angularVelocityBatch?.destroy(),this.motionBatch?.destroy(),this.bodyReadback?.destroy(),this.editStaging=null,s.call(this),i?.clear(),r=null,e=null)},t===2&&(o.readSelectedBodies=function(a,{posesOnly:n=!1}={}){let l=a instanceof Uint32Array?a:Array.from(a);return l.forEach(c=>this.liveIndex(c)),this.flushEdits(),this.bodyReadback??=new Jt(this.device),this.bodyReadback.read(this.bodyBuffer,l,n,6)}),o}var Vs=class extends Fs{constructor(e,t,i){super(e,t,i),qs(this,null,2)}};var sa=`struct Body { pos:vec4f, rot:vec4f, initialPos:vec4f, initialRot:vec4f, size:vec4f, moment:vec4f, inertialPos:vec4f, inertialRot:vec4f, velocity:vec4f, angular:vec4f }
4706
4795
  struct Command { header:vec4u, value:vec4f, point:vec4f }
4707
4796
  struct Manifold { ids:vec4u, geo:vec4f }
4708
4797
  @group(0) @binding(0) var<storage,read_write> bodies:array<Body>;
@@ -4755,26 +4844,26 @@ fn angularImpulse(b:Body,impulse:vec3f)->vec3f {
4755
4844
  let index=commands[ranges[i].x].header.x;
4756
4845
  if(bodies[index].initialPos.w<0.){bodies[index].initialPos.w=-bodies[index].initialPos.w-1.;}
4757
4846
  }
4758
- `;var Wo=class extends Qi{constructor(e){super(e,Ho,3)}encodeAfterEdits(e,t){if(!this.hasTeleports)return;let i=this.solver,r=i.device;if(!this.invalidate){let c=r.createShaderModule({code:`const DISPATCH_STRIDE = ${r.limits.maxComputeWorkgroupsPerDimension*64}u;
4759
- ${Ho}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"invalidate"}}),this.clearMarkers=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"clearMarkers"}})}(this.invalidateBody!==i.bodyBuffer||this.invalidateManifolds!==i.contactStorage.manifolds)&&(this.invalidateGroup=r.createBindGroup({layout:this.invalidate.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:3,resource:{buffer:i.contactStorage.manifolds}}]}),this.invalidateBody=i.bodyBuffer,this.invalidateManifolds=i.contactStorage.manifolds);let s=e.beginComputePass();s.setPipeline(this.invalidate),s.setBindGroup(0,this.invalidateGroup);let a=Math.ceil(i.manifoldCapacity/64),n=r.limits.maxComputeWorkgroupsPerDimension;s.dispatchWorkgroups(Math.min(a,n),Math.ceil(a/n)),s.end(),(this.markerBody!==i.bodyBuffer||this.markerCommands!==this.commands||this.markerRanges!==this.ranges||this.markerBytes!==t*8)&&(this.markerGroup=r.createBindGroup({layout:this.clearMarkers.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:t*8}}]}),this.markerBody=i.bodyBuffer,this.markerCommands=this.commands,this.markerRanges=this.ranges,this.markerBytes=t*8),s=e.beginComputePass(),s.setPipeline(this.clearMarkers),s.setBindGroup(0,this.markerGroup);let l=Math.ceil(t/64);s.dispatchWorkgroups(Math.min(l,n),Math.ceil(l/n)),s.end()}},Ds=class extends Ni{constructor(e,t,i){super(e,t,i),this.nativeRef=t,js(this,{bodies:t.bodies},3)}rigidAt(e){return this.nativeBodies||(this.nativeBodies=[],this.nativeRef.bodies.forEach((t,i)=>this.nativeBodies[this.gpuIndex(i)]=t)),this.nativeBodies[e]}rewriteBodies(e,t){if(super.rewriteBodies(e,t),this.nativeBodies)for(let i=0;i<e.length;i++)this.nativeBodies[e[i]]=t[i]}liveIndex(e){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 3D GPU body index is required")}queueBodyCommand(e,t,i,r,s=!1){return this.liveIndex(e),this.prepareBodyCommands(),this.bodyCommands.enqueue(e,t,i,r,s),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands??=new Wo(this)}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,Be(t,3,"velocity"))}setAngularVelocity(e,t){return this.queueBodyCommand(e,1,Be(t,3,"angular velocity"))}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,Be(t,3,"impulse"),i===void 0?void 0:Be(i,3,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...Be(t,3,"force"),this.params.dt],i===void 0?void 0:Be(i,3,"point"),i!==void 0)}applyAngularImpulse(e,t){return this.queueBodyCommand(e,5,Be(t,3,"angular impulse"))}applyTorque(e,t){return this.queueBodyCommand(e,6,[...Be(t,3,"torque"),this.params.dt])}teleport(e,t,i){let r=Be(t,3,"position"),s;if(i!==void 0){s=Be(i,4,"rotation");let a=Math.hypot(...s);if(!a)throw Error("rotation must be nonzero");s=s.map(n=>n/a)}return this.queueBodyCommand(e,4,r,s,s!==void 0)}flushEdits(){if(this.destroyed)throw Error("The 3D solver has been destroyed");this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}readBodies(){return this.flushEdits(),super.readBodies()}readSelectedBodies(e,{posesOnly:t=!1}={}){let i=e instanceof Uint32Array?e:Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.bodyReadback??=new Ft(this.device),this.bodyReadback.read(this.bodyBuffer,i,t)}async readBodyState(e){let t=await this.readSelectedBodies([e]),i=this.rigidAt(e)?.mass??t[19];return{position:Array.from(t.subarray(0,3)),rotation:Array.from(t.subarray(4,8)),linearVelocity:Array.from(t.subarray(32,35)),angularVelocity:Array.from(t.subarray(36,39)),mass:i,effectiveMass:t[19],sleeping:i>0&&t[19]===0}}raycastAll(...e){return this.flushEdits(),super.raycastAll(...e)}sphereCastAll(...e){return this.flushEdits(),super.sphereCastAll(...e)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array");i.forEach(r=>this.liveIndex(r))}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");if(this.contactWatch||(this.contactWatch=new Nt(this.device,this.bodyBuffer,this.bodyCapacity,e,t),i===void 0&&(i=Array.from({length:this.bodyCount},(r,s)=>s))),i!==void 0){let r=new Set(i);this.contactWatch.setWatched(Array.from({length:this.bodyCount},(s,a)=>a),Array.from({length:this.bodyCount},(s,a)=>r.has(a)))}return this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Ut?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}setSensor(e,t){super.setSensor(e,t),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");let i=this.rigidAt(e);if(!i)throw Error("Sleep eligibility requires a scene body");return i.allowSleep=t,t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,i.mass,t),this.wakeAll(),this}enableSleeping(e={},t={}){let i=e.bodies?e:this.nativeRef;return super.enableSleeping(i,e.bodies?t:e),this}disableSleeping(){return this.flushEdits(),this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.dispose(),this.sleeping=null),this}attachToScene(e,t){return qn.call(this,e,t)}detachFromScene(){return Vn.call(this)}step(){if(this.destroyed)throw Error("The 3D solver has been destroyed");super.step(),this.contactWatch?.run(this.contactStorage,this.manifoldCapacity,this.stepCount,this.params.dt)}destroy(){this.destroyed||(this.detachFromScene(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),super.destroy(),this.destroyed=!0)}};function wr(o,e,t){if(o.split(e).length!==2)throw Error("Pinned solver changed; review the cloth kernel integration");return o.replace(e,t)}var Pt=wr(ke," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if (j >= params.jointCount) { return; }
4847
+ `;var oa=class extends ir{constructor(e){super(e,sa,3)}encodeAfterEdits(e,t){if(!this.hasTeleports)return;let i=this.solver,r=i.device;if(!this.invalidate){let c=r.createShaderModule({code:`const DISPATCH_STRIDE = ${r.limits.maxComputeWorkgroupsPerDimension*64}u;
4848
+ ${sa}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"invalidate"}}),this.clearMarkers=r.createComputePipeline({layout:"auto",compute:{module:c,entryPoint:"clearMarkers"}})}(this.invalidateBody!==i.bodyBuffer||this.invalidateManifolds!==i.contactStorage.manifolds)&&(this.invalidateGroup=r.createBindGroup({layout:this.invalidate.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:3,resource:{buffer:i.contactStorage.manifolds}}]}),this.invalidateBody=i.bodyBuffer,this.invalidateManifolds=i.contactStorage.manifolds);let s=e.beginComputePass();s.setPipeline(this.invalidate),s.setBindGroup(0,this.invalidateGroup);let a=Math.ceil(i.manifoldCapacity/64),n=r.limits.maxComputeWorkgroupsPerDimension;s.dispatchWorkgroups(Math.min(a,n),Math.ceil(a/n)),s.end(),(this.markerBody!==i.bodyBuffer||this.markerCommands!==this.commands||this.markerRanges!==this.ranges||this.markerBytes!==t*8)&&(this.markerGroup=r.createBindGroup({layout:this.clearMarkers.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:i.bodyBuffer}},{binding:1,resource:{buffer:this.commands}},{binding:2,resource:{buffer:this.ranges,size:t*8}}]}),this.markerBody=i.bodyBuffer,this.markerCommands=this.commands,this.markerRanges=this.ranges,this.markerBytes=t*8),s=e.beginComputePass(),s.setPipeline(this.clearMarkers),s.setBindGroup(0,this.markerGroup);let l=Math.ceil(t/64);s.dispatchWorkgroups(Math.min(l,n),Math.ceil(l/n)),s.end()}},Ws=class extends Vt{constructor(e,t,i){super(e,t,i),this.nativeRef=t,qs(this,{bodies:t.bodies},3)}rigidAt(e){return this.nativeBodies||(this.nativeBodies=[],this.nativeRef.bodies.forEach((t,i)=>this.nativeBodies[this.gpuIndex(i)]=t)),this.nativeBodies[e]}rewriteBodies(e,t){if(super.rewriteBodies(e,t),this.nativeBodies)for(let i=0;i<e.length;i++)this.nativeBodies[e[i]]=t[i]}liveIndex(e){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(!Number.isInteger(e)||e<0||e>=this.bodyCount)throw Error("A live 3D GPU body index is required")}queueBodyCommand(e,t,i,r,s=!1){return this.liveIndex(e),this.prepareBodyCommands(),this.bodyCommands.enqueue(e,t,i,r,s),this}prepareBodyCommands(){return this.wakeAll(),this.bodyCommands??=new oa(this)}setLinearVelocity(e,t){return this.queueBodyCommand(e,0,Be(t,3,"velocity"))}setAngularVelocity(e,t){return this.queueBodyCommand(e,1,Be(t,3,"angular velocity"))}applyImpulse(e,t,i){return this.queueBodyCommand(e,2,Be(t,3,"impulse"),i===void 0?void 0:Be(i,3,"point"),i!==void 0)}applyForce(e,t,i){return this.queueBodyCommand(e,3,[...Be(t,3,"force"),this.params.dt],i===void 0?void 0:Be(i,3,"point"),i!==void 0)}applyAngularImpulse(e,t){return this.queueBodyCommand(e,5,Be(t,3,"angular impulse"))}applyTorque(e,t){return this.queueBodyCommand(e,6,[...Be(t,3,"torque"),this.params.dt])}teleport(e,t,i){let r=Be(t,3,"position"),s;if(i!==void 0){s=Be(i,4,"rotation");let a=Math.hypot(...s);if(!a)throw Error("rotation must be nonzero");s=s.map(n=>n/a)}return this.queueBodyCommand(e,4,r,s,s!==void 0)}flushEdits(){if(this.destroyed)throw Error("The 3D solver has been destroyed");this.flushPropertyEdits(),this.sleeping?.wakeRequested&&this.sleeping.before(),this.bodyCommands?.flush()}readBodies(){return this.flushEdits(),super.readBodies()}readSelectedBodies(e,{posesOnly:t=!1}={}){let i=e instanceof Uint32Array?e:Array.from(e);return i.forEach(r=>this.liveIndex(r)),this.flushEdits(),this.bodyReadback??=new Jt(this.device),this.bodyReadback.read(this.bodyBuffer,i,t)}async readBodyState(e){let t=await this.readSelectedBodies([e]),i=this.rigidAt(e)?.mass??t[19];return{position:Array.from(t.subarray(0,3)),rotation:Array.from(t.subarray(4,8)),linearVelocity:Array.from(t.subarray(32,35)),angularVelocity:Array.from(t.subarray(36,39)),mass:i,effectiveMass:t[19],sleeping:i>0&&t[19]===0}}raycastAll(...e){return this.flushEdits(),super.raycastAll(...e)}sphereCastAll(...e){return this.flushEdits(),super.sphereCastAll(...e)}watchContacts({maxPairs:e=8192,maxEvents:t=4096,indices:i}={}){if(this.destroyed)throw Error("The 3D solver has been destroyed");if(i!==void 0){if(!Array.isArray(i))throw Error("Watched indices must be an array");i.forEach(r=>this.liveIndex(r))}for(let r of[e,t])if(!Number.isInteger(r)||r<1)throw Error("Contact capacities must be positive integers");if(this.contactWatch||(this.contactWatch=new $t(this.device,this.bodyBuffer,this.bodyCapacity,e,t),i===void 0&&(i=Array.from({length:this.bodyCount},(r,s)=>s))),i!==void 0){let r=new Set(i);this.contactWatch.setWatched(Array.from({length:this.bodyCount},(s,a)=>a),Array.from({length:this.bodyCount},(s,a)=>r.has(a)))}return this}async readContactEvents(){this.watchContacts();let e=await this.contactWatch.read();return{...e,events:e.events.map(t=>({...t,type:t.kind===Xt?"begin":"end",isTrigger:!!(this.triggers?.has(t.a)||this.triggers?.has(t.b))}))}}setSensor(e,t){super.setSensor(e,t),this.triggers??=new Set,t?this.triggers.add(e):this.triggers.delete(e)}setSleepEnabled(e,t){if(this.liveIndex(e),typeof t!="boolean")throw Error("Sleep flag must be boolean");let i=this.rigidAt(e);if(!i)throw Error("Sleep eligibility requires a scene body");return i.allowSleep=t,t&&!this.sleeping&&this.enableSleeping(),this.sleeping?.register(e,i.mass,t),this.wakeAll(),this}enableSleeping(e={},t={}){let i=e.bodies?e:this.nativeRef;return super.enableSleeping(i,e.bodies?t:e),this}disableSleeping(){return this.flushEdits(),this.sleeping&&(this.sleeping.wakeRequested=!0,this.sleeping.before(),this.sleeping.dispose(),this.sleeping=null),this}attachToScene(e,t){return al.call(this,e,t)}detachFromScene(){return nl.call(this)}step(){if(this.destroyed)throw Error("The 3D solver has been destroyed");super.step(),this.contactWatch?.run(this.contactStorage,this.manifoldCapacity,this.stepCount,this.params.dt)}destroy(){this.destroyed||(this.detachFromScene(),this.bodyCommands?.destroy(),this.contactWatch?.destroy(),super.destroy(),this.destroyed=!0)}};function Pr(o,e,t){if(o.split(e).length!==2)throw Error("Pinned solver changed; review the cloth kernel integration");return o.replace(e,t)}var Et=Pr(be," if (j >= params.jointCount || info[j].x != T_JOINT) { return; }",` if (j >= params.jointCount) { return; }
4760
4849
  if (info[j].x == T_SPRING) {
4761
4850
  // Finite material stiffness: Eq. 16, with no hard-constraint multiplier.
4762
4851
  joints[j].penLin.x = min(joints[j].penLin.w, max(PENALTY_MIN, joints[j].penLin.x * params.gamma));
4763
4852
  return;
4764
4853
  }
4765
- if (info[j].x != T_JOINT) { return; }`);Pt=wr(Pt," let stiffness = k.penLin.w;"," let stiffness = k.penLin.x;");Pt=wr(Pt," addRow(acc, n * sg, cross(r, n) * sg, stiffness, stiffness * (len - k.rA.w));",` let force = stiffness * (len - k.rA.w);
4854
+ if (info[j].x != T_JOINT) { return; }`);Et=Pr(Et," let stiffness = k.penLin.w;"," let stiffness = k.penLin.x;");Et=Pr(Et," addRow(acc, n * sg, cross(r, n) * sg, stiffness, stiffness * (len - k.rA.w));",` let force = stiffness * (len - k.rA.w);
4766
4855
  addRow(acc, n * sg, cross(r, n) * sg, stiffness, force);
4767
4856
  // Eq. 17: positive diagonal lumping of the geometric Hessian's column
4768
4857
  // norms. For C=|xA-xB|-L, G=(force/len)(I-n*n^T).
4769
4858
  let g = abs(force / len) * sqrt(max(vec3f(0.0), vec3f(1.0) - n*n));
4770
- (*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);Pt=wr(Pt," if (t != T_JOINT) { return; }",` if (t == T_SPRING) {
4859
+ (*acc).lin += mat3x3f(vec3f(g.x,0.0,0.0), vec3f(0.0,g.y,0.0), vec3f(0.0,0.0,g.z));`);Et=Pr(Et," if (t != T_JOINT) { return; }",` if (t == T_SPRING) {
4771
4860
  let a = info[j].y;
4772
4861
  let b = info[j].z;
4773
4862
  let C = length(anchorA(joints[j], a) - (qrotate(bodies[b].rot, joints[j].rB.xyz) + bodies[b].pos.xyz)) - joints[j].rA.w;
4774
4863
  joints[j].penLin.x = min(joints[j].penLin.w, joints[j].penLin.x + params.betaLin * abs(C));
4775
4864
  return;
4776
4865
  }
4777
- if (t != T_JOINT) { return; }`);Pt=wr(Pt,"fn finishBody(i: u32, acc: Acc) {",`fn finishBody(i: u32, acc: Acc) {
4866
+ if (t != T_JOINT) { return; }`);Et=Pr(Et,"fn finishBody(i: u32, acc: Acc) {",`fn finishBody(i: u32, acc: Acc) {
4778
4867
  if (all(bodies[i].moment.xyz == vec3f(0.0))) {
4779
4868
  // LDL^T of the point's 3x3 SPD system, using the same pivot floor as
4780
4869
  // rigid bodies. Contact friction moves the point; it cannot spin it.
@@ -4792,4 +4881,4 @@ ${Ho}`});this.invalidate=r.createComputePipeline({layout:"auto",compute:{module:
4792
4881
  let v1 = y1/d1 - l21*v2 - l31*v3;
4793
4882
  bodies[i].pos = vec4f(bodies[i].pos.xyz - vec3f(v1,v2,v3), bodies[i].pos.w);
4794
4883
  return;
4795
- }`);var Hn=Pt;var J=(o,e,t)=>{let i=Array.from(o);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i},V=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},Wn=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Gs(o){for(let t of["allowSleep","isTrigger"])if(o[t]!==void 0&&typeof o[t]!="boolean")throw Error(`${t} must be a boolean`);let e=o.restitution??0;if(!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");if(o.mass!==void 0&&(V(o.mass,"mass",!0),o.mass>0&&o.density===0))throw Error("A positive mass needs positive density");for(let t of["velocity","angularVelocity"])o[t]!==void 0&&J(o[t],3,t);if(o.rotation!==void 0&&!Math.hypot(...J(o.rotation,4,"rotation")))throw Error("rotation must be nonzero");Wn(o.group??4294967295),Wn(o.collidesWith??4294967295)}function Jn(o,e){if(e.timeStep!==void 0&&(o.dt=V(e.timeStep,"timeStep")),e.iterations!==void 0){if(V(e.iterations,"iterations"),!Number.isInteger(e.iterations))throw Error("iterations must be an integer");o.iterations=e.iterations}if(e.gravity!==void 0){if(!Number.isFinite(e.gravity))throw Error("gravity must be finite");o.gravity=e.gravity}}var Jo=class{constructor(e={}){if(this.ref=new Pi,Jn(this.ref,e),this.up=J(e.up??[0,1,0],3,"up"),Math.abs(Math.hypot(...this.up)-1)>1e-5)throw Error("up must be a unit vector");this.points=!1,this.handles=new Map,this.bodySet=new Set}get bodies(){return this.ref.bodies}get constraints(){return[...this.handles.values()]}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use the solver's GPU edit methods afterwards")}addBox(e,t={}){this.assertEditable(),Gs(t);let i=J(e,3,"size");i.forEach(s=>V(s,"size"));let r=new at(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position"));return this.configure(r,t)}addSphere(e,t={}){return this.assertEditable(),Gs(t),this.configure(Rr(this.ref,V(e,"radius"),V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),Gs(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=er(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(Or(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addCapsule(e,t,i={}){return this.assertEditable(),Gs(i),this.configure(ts(this.ref,e,t,V(i.density??1,"density",!0),V(i.friction??.6,"friction",!0),J(i.position??[0,0,0],3,"position")),i)}configure(e,t){if(t.allowSleep!==void 0&&typeof t.allowSleep!="boolean")throw Error("allowSleep must be a boolean");e.allowSleep=t.allowSleep??!0,this.sleepOptIn||=t.allowSleep===!0;let i=t.restitution??0;if(!Number.isFinite(i)||i<0||i>1)throw Error("restitution must be between 0 and 1");if(e.restitution=i,t.isTrigger!==void 0&&typeof t.isTrigger!="boolean")throw Error("isTrigger must be a boolean");if(e.isTrigger=t.isTrigger??!1,e.group=t.group??4294967295,e.collidesWith=t.collidesWith??4294967295,t.mass!==void 0){let r=V(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let s=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((a,n)=>e.moment[n]=a*s)}if(t.rotation){let r=J(t.rotation,4,"rotation"),s=Math.hypot(...r);if(!s)throw Error("rotation must be nonzero");e.positionAng.set(r.map(a=>a/s))}return t.velocity&&e.velocityLin.set(J(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(J(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let s=this.addSphere(i,{position:e,mass:t,friction:r});return s.moment.fill(0),this.points=!0,s}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:s=.02,stiffness:a=1e3,anchorStiffness:n=a,materialAt:l,breakStrain:c=1/0,breakForce:f=1/0,bendingStiffness:u=Number.isFinite(c)||Number.isFinite(f)?0:a/50,pinCorners:d=!1,pinEdges:h=!d}={}){for(let[M,P]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(P)||P<2)throw Error(`${M} must be an integer >= 2`);if(V(i,"spacing"),V(s,"mass"),V(a,"stiffness"),V(n,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(V(u,"bendingStiffness",!0),typeof d!="boolean"||typeof h!="boolean")throw Error("Fabric pin options must be boolean");let m=St({breakStrain:c,breakForce:f}),p=J(r,3,"origin"),b=(M,P)=>`${Math.min(M,P)},${Math.max(M,P)}`;function*y(){for(let M=0;M<t;M++)for(let P=0;P<e;P++){let _=M*e+P;P&&(yield[_,_-1,a]),M&&(yield[_,_-e,a]),P&&M&&(yield[_,_-e-1,a/2],yield[_-1,_-e,a/2]),P>1&&u&&(yield[_,_-2,u]),M>1&&u&&(yield[_,_-2*e,u])}}let A=new Map;if(l)for(let[M,P]of y()){let _=l((M%e+P%e)/(2*(e-1)),(Math.floor(M/e)+Math.floor(P/e))/(2*(t-1)))??{};if(typeof _!="object"||Array.isArray(_))throw Error("materialAt must return a material object");A.set(b(M,P),{scale:V(_.stiffnessScale??1,"materialAt.stiffnessScale"),material:St({...m,..._})})}let w=Array.from({length:t},(M,P)=>Array.from({length:e},(_,S)=>this.addPoint([p[0]+(S-(e-1)/2)*i,p[1],p[2]+(P-(t-1)/2)*i],{mass:h&&(S===0||P===0||S===e-1||P===t-1)||d&&(S===0||S===e-1)&&(P===0||P===t-1)?0:s,radius:i/Math.SQRT2}))),g=w.flat(),x=new Map(g.map((M,P)=>[M,P])),v=[],B=new Map,E=(M,P,_)=>{(M.mass===0||P.mass===0)&&(_*=n/a);let S=x.get(M),C=x.get(P),R=A.get(b(S,C)),I=this.addSpring(M,P,[0,0,0],[0,0,0],{stiffness:_*(R?.scale??1),...R?.material??m});return B.set(b(x.get(M),x.get(P)),v.length),v.push(I),I};for(let[M,P,_]of y())E(g[M],g[P],_);let k=[];for(let M=0;M<t;M++)for(let P=0;P<e;P++)if(P&&M){let _=(M-1)*e+P-1,S=_+1,C=M*e+P-1,R=C+1;k.push(_,C,S,S,C,R)}let z=[];for(let M=0;M<k.length;M+=3){let[P,_,S]=k.slice(M,M+3);z.push(B.get(b(P,_)),B.get(b(_,S)),B.get(b(S,P)))}return{points:g,grid:w,indices:Uint32Array.from(k),connections:v,triangleConnections:Uint32Array.from(z)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:s=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");V(t,"length"),V(r,"width"),V(s,"endMass",!0);let a=J(i,3,"origin"),n=t/e,l=this.addBox([r,r,r],{mass:0,position:a}),c=[];for(let f=0;f<e;f++){let u=this.addBox([r,n,r],{mass:1,position:[a[0],a[1]-(f+.5)*n,a[2]]});this.addJoint(f?c[f-1]:l,u,[0,f?-n/2:0,0],[0,n/2,0]),c.push(u)}if(s){let f=this.addBox([.6,.6,.6],{mass:s,position:[a[0],a[1]-t-.3,a[2]]});this.addJoint(c.at(-1),f,[0,-n/2,0],[0,.3,0]),c.push(f)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=J(e,3,"origin");V(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((f,u)=>f+i[u])}),s=r([.7,1.2,.35],[0,2,0]),a=this.addSphere(.25,{mass:t/10,position:[i[0],i[1]+2.9,i[2]]}),n=[s,a];this.addJoint(s,a,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),f=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(s,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,f,[0,-.4,0],[0,.35,0]);let u=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(s,u,[l*.2,-.6,0],[0,.4,0]),this.addJoint(u,d,[0,-.4,0],[0,.375,0]),n.push(c,f,u,d)}return{torso:s,head:a,bodies:n}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:s=1/0,angularStiffness:a=0,breakForce:n=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[s,a,n])c!==1/0&&V(c,"joint stiffness/breakForce",!0);return this.handle(new Ae(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a,l&&n!==1/0?-n:n))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:s=1e3,rest:a=-1,breakStrain:n=1/0,breakForce:l=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!e)throw Error("A spring requires two bodies");V(s,"stiffness",!0),a!==-1&&V(a,"rest",!0);let c=St({breakStrain:n,breakForce:l}),f=new ct(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a);return Yr(f,c),this.handle(f)}checkBodies(e,t){if(!t||e===t||!this.bodySet.has(t)||e&&!this.bodySet.has(e))throw Error("Constraint endpoints must be distinct bodies in this scene")}addMotor(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=gt({...i,type:"motor"}),s=new Ae(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return Fi(s,r),this.handle(s)}addHinge(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=gt({...i,type:"limit"}),s=V(i.span??1,"span"),a=J(i.anchorA??[0,0,0],3,"anchorA"),n=J(i.anchorB??[0,0,0],3,"anchorB"),l=[-1,1].map(u=>this.addJoint(e,t,a.map((d,h)=>d+u*s*.5*r.axisA[h]),n.map((d,h)=>d+u*s*.5*r.axisB[h]))),c,f;if(i.minAngle!==void 0||i.maxAngle!==void 0){let u=new Ae(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);Fi(u,r),c=this.handle(u)}return i.motor&&(f=this.addMotor(e,t,{axisA:r.axisA,axisB:r.axisB,...i.motor})),{joints:l,limit:c,motor:f,dispose(){for(let u of[...l,c,f])u?.dispose()}}}handle(e){let t={slot:-1,disposed:!1,dispose:()=>{t.disposed||(this.gpu?this.gpu.destroyed||this.gpu.releaseJoints([t.slot]):e.destroy(),this.handles.delete(e),t.disposed=!0)}};return t.destroy=t.dispose,this.handles.set(e,t),t}createSolver(e,t={}){if(this.assertEditable(),this.hulls&&e.limits.maxStorageBuffersPerShaderStage<9)throw Error("Hull collisions require nine storage buffer bindings");let i=new Ds(e,this.ref,rs(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:Hn}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof Ae&&l.stiffnessAng>0&&r.push({slot:c})}),i.captureConstraintFrames(r),i.params.up=[...this.up];let s=[],a=[],n=[];for(let[l,c]of this.ref.bodies.entries())(c.group!==4294967295||c.collidesWith!==4294967295)&&(s.push(i.gpuIndex(l)),a.push(c.group),n.push(c.collidesWith)),c.isTrigger&&i.setSensor(i.gpuIndex(l),!0),c.restitution>0&&i.setRestitution(i.gpuIndex(l),c.restitution);return s.length&&i.setFilters(s,a,n),(t.sleeping||this.sleepOptIn)&&i.enableSleeping(this.ref,t.sleeping===!0?{}:t.sleeping),this.gpu=i,this.created=!0,i}},$o=class{constructor(e={}){if(this.topology=new vs,Object.assign(this.topology.params,ps()),Jn(this.topology.params,e),e.alpha!==void 0){if(!Number.isFinite(e.alpha)||e.alpha<0||e.alpha>1)throw Error("alpha must be between zero and one");this.topology.params.alpha=e.alpha}for(let t of["postStabilize","matchNearest"])if(e[t]!==void 0){if(typeof e[t]!="boolean")throw Error(`${t} must be boolean`);this.topology.params[t]=e[t]}}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use GPU edit methods afterwards")}get bodyCount(){return this.topology.bodyCount}addShape(e,t={}){let i=e.options??t,r=this.addBox(e.size,i);return Ms(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(Ls(e),t)}addHull(e,t={}){return this.addShape(Ps(e),t)}addCapsule(e,t,i={}){return this.addShape(_s(e,t),i)}addSegment(e,t,i={}){return this.addShape(Es(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(zs(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:s=0,velocity:a=[0,0,0],restitution:n=0,isTrigger:l=!1,group:c=4294967295,collidesWith:f=4294967295,allowSleep:u}={}){if(this.assertEditable(),u!==void 0&&typeof u!="boolean")throw Error("allowSleep must be boolean");let d=J(e,2,"size");d.forEach(p=>V(p,"size"));let h=J(r,2,"position");if(!Number.isFinite(s))throw Error("angle must be finite");if(!Number.isFinite(n)||n<0||n>1)throw Error("restitution must be between 0 and 1");if(typeof l!="boolean")throw Error("isTrigger must be boolean");for(let p of[c,f])if(!Number.isInteger(p)||p<0||p>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,V(t,"density",!0),V(i,"friction",!0),[...h,s],J(a,3,"velocity"));return this.restitution??=new Map,n>0&&this.restitution.set(m,n),this.sensors??=new Set,l&&this.sensors.add(m),this.filters??=new Map,(c!==4294967295||f!==4294967295)&&this.filters.set(m,[c,f]),this.topology.avbdSleepEligible??=new Map,u!==void 0&&this.topology.avbdSleepEligible.set(m,u),m}checkBodies(e,t){if(!Number.isInteger(e)||!Number.isInteger(t)||e<-1||e>=this.bodyCount||t<0||t>=this.bodyCount||e===t)throw Error("Invalid 2D constraint endpoints")}addJoint(e,t,i=[0,0],r=[0,0],{stiffness:s=[1/0,1/0,0],breakForce:a=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!s||s.length!==3||Array.from(s).some(l=>l!==1/0&&(!Number.isFinite(l)||l<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");let n=this.topology.addJoint(e,t,J(i,2,"anchorA"),J(r,2,"anchorB"),s,a);return this.constraintHandle(n.slot,n)}constraintHandle(e,t={}){return Object.assign(t,{slot:e,alive:!0,disposed:!1}),t.dispose=()=>{t.disposed||(this.gpu&&!this.gpu.destroyed?this.gpu.disableConstraint(e):this.gpu||this.topology.data.fill(0,e*T+$,e*T+$+3),t.alive=!1,t.disposed=!0)},t.destroy=t.dispose,this.topology.handles[e]=t,t}addSpring(e,t,i=[0,0],r=[0,0],{stiffness:s=1e3,rest:a=1}={}){if(this.assertEditable(),this.checkBodies(e,t),e<0)throw Error("A spring requires two bodies");let n=this.topology.jointCount;return this.topology.addSpring(e,t,J(i,2,"anchorA"),J(r,2,"anchorB"),V(s,"stiffness",!0),V(a,"rest",!0)),this.constraintHandle(n)}addMotor(e,t,{speed:i=1,maxTorque:r=100}={}){if(this.assertEditable(),this.checkBodies(e,t),!Number.isFinite(i))throw Error("speed must be finite");let s=this.topology.jointCount;return this.topology.addMotor(e,t,i,V(r,"maxTorque",!0)),this.constraintHandle(s)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=Xi(i),s=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);qo(this.topology,s.slot,r);let a=this.constraintHandle(s.slot,s);return a.setLimits=n=>{if(a.disposed)throw Error("The angular limit has been disposed");return this.gpu?this.gpu.setAngleLimits(a.slot,n):qo(this.topology,a.slot,Xi(n)),a},a}addHinge(e,t,i=[0,0],r=[0,0],s={}){this.assertEditable(),this.checkBodies(e,t);let a=J(i,2,"anchorA"),n=J(r,2,"anchorB"),l=s.minAngle!==void 0||s.maxAngle!==void 0?Xi(s):null,c=s.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");V(c.maxTorque??100,"maxTorque",!0)}let f=this.addJoint(e,t,a,n),u=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:f,limits:u,motor:d,disposed:!1,dispose(){this.disposed||(this.joint.dispose(),this.limits?.dispose(),this.motor?.dispose(),this.disposed=!0)},destroy(){this.dispose()}}}createSolver(e,t={}){this.assertEditable();let i=new Ts(e,this.topology,t);for(let[r,s]of this.restitution??[])i.setRestitution(r,s);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,s]of this.filters??[])i.setFilters([r],[s[0]],[s[1]]);return this.gpu=i,this.created=!0,i}};export{as as AvbdPhysics,ns as AvbdPhysicsAggregate,ls as AvbdPhysicsBody,fr as AvbdPhysicsConstraint,dr as AvbdPhysicsHinge,$o as AvbdScene2D,Jo as AvbdScene3D,$e as AvbdShapeType,jo as createWebGPUDevice};
4884
+ }`);var ll=Et;var J=(o,e,t)=>{let i=Array.from(o);if(i.length!==e||!i.every(Number.isFinite))throw Error(`${t} requires ${e} finite components`);return i},V=(o,e,t=!1)=>{if(!Number.isFinite(o)||(t?o<0:o<=0))throw Error(`${e} must be ${t?"nonnegative":"positive"} and finite`);return o},cl=o=>{if(!Number.isInteger(o)||o<0||o>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");return o};function Hs(o){for(let t of["allowSleep","isTrigger"])if(o[t]!==void 0&&typeof o[t]!="boolean")throw Error(`${t} must be a boolean`);let e=o.restitution??0;if(!Number.isFinite(e)||e<0||e>1)throw Error("restitution must be between 0 and 1");if(o.mass!==void 0&&(V(o.mass,"mass",!0),o.mass>0&&o.density===0))throw Error("A positive mass needs positive density");for(let t of["velocity","angularVelocity"])o[t]!==void 0&&J(o[t],3,t);if(o.rotation!==void 0&&!Math.hypot(...J(o.rotation,4,"rotation")))throw Error("rotation must be nonzero");cl(o.group??4294967295),cl(o.collidesWith??4294967295)}function ul(o,e){if(e.timeStep!==void 0&&(o.dt=V(e.timeStep,"timeStep")),e.iterations!==void 0){if(V(e.iterations,"iterations"),!Number.isInteger(e.iterations))throw Error("iterations must be an integer");o.iterations=e.iterations}if(e.gravity!==void 0){if(!Number.isFinite(e.gravity))throw Error("gravity must be finite");o.gravity=e.gravity}}var aa=class{constructor(e={}){if(this.ref=new jt,ul(this.ref,e),this.up=J(e.up??[0,1,0],3,"up"),Math.abs(Math.hypot(...this.up)-1)>1e-5)throw Error("up must be a unit vector");this.points=!1,this.handles=new Map,this.bodySet=new Set}get bodies(){return this.ref.bodies}get constraints(){return[...this.handles.values()]}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use the solver's GPU edit methods afterwards")}addBox(e,t={}){this.assertEditable(),Hs(t);let i=J(e,3,"size");i.forEach(s=>V(s,"size"));let r=new De(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position"));return this.configure(r,t)}addSphere(e,t={}){return this.assertEditable(),Hs(t),this.configure(Mi(this.ref,V(e,"radius"),V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addHull(e,t={}){if(this.assertEditable(),Hs(t),e.length%3||!Array.from(e).every(Number.isFinite))throw Error("Hull points must contain finite xyz triples");let i=or(e);if(!i)throw Error("A convex hull requires finite points spanning a volume");return this.hulls=!0,this.configure(Gr(this.ref,i,V(t.density??1,"density",!0),V(t.friction??.6,"friction",!0),J(t.position??[0,0,0],3,"position")),t)}addCapsule(e,t,i={}){return this.assertEditable(),Hs(i),this.configure(Hi(this.ref,e,t,V(i.density??1,"density",!0),V(i.friction??.6,"friction",!0),J(i.position??[0,0,0],3,"position")),i)}configure(e,t){if(t.allowSleep!==void 0&&typeof t.allowSleep!="boolean")throw Error("allowSleep must be a boolean");e.allowSleep=t.allowSleep??!0,this.sleepOptIn||=t.allowSleep===!0;let i=t.restitution??0;if(!Number.isFinite(i)||i<0||i>1)throw Error("restitution must be between 0 and 1");if(e.restitution=i,t.isTrigger!==void 0&&typeof t.isTrigger!="boolean")throw Error("isTrigger must be a boolean");if(e.isTrigger=t.isTrigger??!1,e.group=t.group??4294967295,e.collidesWith=t.collidesWith??4294967295,t.mass!==void 0){let r=V(t.mass,"mass",!0);if(e.mass===0&&r>0)throw Error("A positive mass needs positive density");let s=e.mass>0?r/e.mass:0;e.mass=r,e.moment.forEach((a,n)=>e.moment[n]=a*s)}if(t.rotation){let r=J(t.rotation,4,"rotation"),s=Math.hypot(...r);if(!s)throw Error("rotation must be nonzero");e.positionAng.set(r.map(a=>a/s))}return t.velocity&&e.velocityLin.set(J(t.velocity,3,"velocity")),t.angularVelocity&&e.velocityAng.set(J(t.angularVelocity,3,"angularVelocity")),this.bodySet.add(e),e}addPoint(e,{mass:t=.02,radius:i=.05,friction:r=.5}={}){let s=this.addSphere(i,{position:e,mass:t,friction:r});return s.moment.fill(0),this.points=!0,s}addFabric({columns:e=16,rows:t=16,spacing:i=.2,origin:r=[0,3,0],mass:s=.02,stiffness:a=1e3,anchorStiffness:n=a,materialAt:l,breakStrain:c=1/0,breakForce:f=1/0,bendingStiffness:u=Number.isFinite(c)||Number.isFinite(f)?0:a/50,pinCorners:d=!1,pinEdges:p=!d}={}){for(let[M,P]of Object.entries({columns:e,rows:t}))if(!Number.isInteger(P)||P<2)throw Error(`${M} must be an integer >= 2`);if(V(i,"spacing"),V(s,"mass"),V(a,"stiffness"),V(n,"anchorStiffness"),l!==void 0&&typeof l!="function")throw Error("materialAt must be a function");if(V(u,"bendingStiffness",!0),typeof d!="boolean"||typeof p!="boolean")throw Error("Fabric pin options must be boolean");let m=Pt({breakStrain:c,breakForce:f}),h=J(r,3,"origin"),b=(M,P)=>`${Math.min(M,P)},${Math.max(M,P)}`;function*y(){for(let M=0;M<t;M++)for(let P=0;P<e;P++){let _=M*e+P;P&&(yield[_,_-1,a]),M&&(yield[_,_-e,a]),P&&M&&(yield[_,_-e-1,a/2],yield[_-1,_-e,a/2]),P>1&&u&&(yield[_,_-2,u]),M>1&&u&&(yield[_,_-2*e,u])}}let v=new Map;if(l)for(let[M,P]of y()){let _=l((M%e+P%e)/(2*(e-1)),(Math.floor(M/e)+Math.floor(P/e))/(2*(t-1)))??{};if(typeof _!="object"||Array.isArray(_))throw Error("materialAt must return a material object");v.set(b(M,P),{scale:V(_.stiffnessScale??1,"materialAt.stiffnessScale"),material:Pt({...m,..._})})}let B=Array.from({length:t},(M,P)=>Array.from({length:e},(_,C)=>this.addPoint([h[0]+(C-(e-1)/2)*i,h[1],h[2]+(P-(t-1)/2)*i],{mass:p&&(C===0||P===0||C===e-1||P===t-1)||d&&(C===0||C===e-1)&&(P===0||P===t-1)?0:s,radius:i/Math.SQRT2}))),g=B.flat(),A=new Map(g.map((M,P)=>[M,P])),x=[],w=new Map,E=(M,P,_)=>{(M.mass===0||P.mass===0)&&(_*=n/a);let C=A.get(M),S=A.get(P),R=v.get(b(C,S)),I=this.addSpring(M,P,[0,0,0],[0,0,0],{stiffness:_*(R?.scale??1),...R?.material??m});return w.set(b(A.get(M),A.get(P)),x.length),x.push(I),I};for(let[M,P,_]of y())E(g[M],g[P],_);let k=[];for(let M=0;M<t;M++)for(let P=0;P<e;P++)if(P&&M){let _=(M-1)*e+P-1,C=_+1,S=M*e+P-1,R=S+1;k.push(_,S,C,C,S,R)}let z=[];for(let M=0;M<k.length;M+=3){let[P,_,C]=k.slice(M,M+3);z.push(w.get(b(P,_)),w.get(b(_,C)),w.get(b(C,P)))}return{points:g,grid:B,indices:Uint32Array.from(k),connections:x,triangleConnections:Uint32Array.from(z)}}addRope({segments:e=12,length:t=12,origin:i=[0,12,0],width:r=.15,endMass:s=20}={}){if(!Number.isInteger(e)||e<1)throw Error("segments must be a positive integer");V(t,"length"),V(r,"width"),V(s,"endMass",!0);let a=J(i,3,"origin"),n=t/e,l=this.addBox([r,r,r],{mass:0,position:a}),c=[];for(let f=0;f<e;f++){let u=this.addBox([r,n,r],{mass:1,position:[a[0],a[1]-(f+.5)*n,a[2]]});this.addJoint(f?c[f-1]:l,u,[0,f?-n/2:0,0],[0,n/2,0]),c.push(u)}if(s){let f=this.addBox([.6,.6,.6],{mass:s,position:[a[0],a[1]-t-.3,a[2]]});this.addJoint(c.at(-1),f,[0,-n/2,0],[0,.3,0]),c.push(f)}return{anchor:l,links:c}}addRagdoll({origin:e=[0,2,0],mass:t=10}={}){let i=J(e,3,"origin");V(t,"mass");let r=(l,c)=>this.addBox(l,{mass:t/10,position:c.map((f,u)=>f+i[u])}),s=r([.7,1.2,.35],[0,2,0]),a=this.addSphere(.25,{mass:t/10,position:[i[0],i[1]+2.9,i[2]]}),n=[s,a];this.addJoint(s,a,[0,.6,0],[0,-.3,0]);for(let l of[-1,1]){let c=r([.3,.8,.3],[l*.5,1.95,0]),f=r([.26,.7,.26],[l*.5,1.2,0]);this.addJoint(s,c,[l*.35,.35,0],[-l*.15,.4,0]),this.addJoint(c,f,[0,-.4,0],[0,.35,0]);let u=r([.28,.8,.3],[l*.2,1,0]),d=r([.26,.75,.28],[l*.2,.225,0]);this.addJoint(s,u,[l*.2,-.6,0],[0,.4,0]),this.addJoint(u,d,[0,-.4,0],[0,.375,0]),n.push(c,f,u,d)}return{torso:s,head:a,bodies:n}}addJoint(e,t,i=[0,0,0],r=[0,0,0],{linearStiffness:s=1/0,angularStiffness:a=0,breakForce:n=1/0,breakOnPull:l=!1}={}){this.assertEditable(),this.checkBodies(e,t);for(let c of[s,a,n])c!==1/0&&V(c,"joint stiffness/breakForce",!0);return this.handle(new we(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a,l&&n!==1/0?-n:n))}addSpring(e,t,i=[0,0,0],r=[0,0,0],{stiffness:s=1e3,rest:a=-1,breakStrain:n=1/0,breakForce:l=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!e)throw Error("A spring requires two bodies");V(s,"stiffness",!0),a!==-1&&V(a,"rest",!0);let c=Pt({breakStrain:n,breakForce:l}),f=new ct(this.ref,e,t,J(i,3,"anchorA"),J(r,3,"anchorB"),s,a);return ts(f,c),this.handle(f)}checkBodies(e,t){if(!t||e===t||!this.bodySet.has(t)||e&&!this.bodySet.has(e))throw Error("Constraint endpoints must be distinct bodies in this scene")}addMotor(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=gt({...i,type:"motor"}),s=new we(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);return Wi(s,r),this.handle(s)}addHinge(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=gt({...i,type:"limit"}),s=V(i.span??1,"span"),a=J(i.anchorA??[0,0,0],3,"anchorA"),n=J(i.anchorB??[0,0,0],3,"anchorB"),l=[-1,1].map(u=>this.addJoint(e,t,a.map((d,p)=>d+u*s*.5*r.axisA[p]),n.map((d,p)=>d+u*s*.5*r.axisB[p]))),c,f;if(i.minAngle!==void 0||i.maxAngle!==void 0){let u=new we(this.ref,e,t,[0,0,0],[0,0,0],1/0,0);Wi(u,r),c=this.handle(u)}return i.motor&&(f=this.addMotor(e,t,{axisA:r.axisA,axisB:r.axisB,...i.motor})),{joints:l,limit:c,motor:f,dispose(){for(let u of[...l,c,f])u?.dispose()}}}handle(e){let t={slot:-1,disposed:!1,dispose:()=>{t.disposed||(this.gpu?this.gpu.destroyed||this.gpu.releaseJoints([t.slot]):e.destroy(),this.handles.delete(e),t.disposed=!0)}};return t.destroy=t.dispose,this.handles.set(e,t),t}createSolver(e,t={}){if(this.assertEditable(),this.hulls&&e.limits.maxStorageBuffersPerShaderStage<9)throw Error("Hull collisions require nine storage buffer bindings");let i=new Ws(e,this.ref,us(this.ref,{...t,...this.points?{shaders:{...t.shaders,solve:ll}}:{}})),r=[];this.ref.forces.forEach((l,c)=>{this.handles.get(l).slot=c,l instanceof we&&l.stiffnessAng>0&&r.push({slot:c})}),i.captureConstraintFrames(r),i.params.up=[...this.up];let s=[],a=[],n=[];for(let[l,c]of this.ref.bodies.entries())(c.group!==4294967295||c.collidesWith!==4294967295)&&(s.push(i.gpuIndex(l)),a.push(c.group),n.push(c.collidesWith)),c.isTrigger&&i.setSensor(i.gpuIndex(l),!0),c.restitution>0&&i.setRestitution(i.gpuIndex(l),c.restitution);return s.length&&i.setFilters(s,a,n),(t.sleeping||this.sleepOptIn)&&i.enableSleeping(this.ref,t.sleeping===!0?{}:t.sleeping),this.gpu=i,this.created=!0,i}},na=class{constructor(e={}){if(this.topology=new ks,Object.assign(this.topology.params,As()),ul(this.topology.params,e),e.alpha!==void 0){if(!Number.isFinite(e.alpha)||e.alpha<0||e.alpha>1)throw Error("alpha must be between zero and one");this.topology.params.alpha=e.alpha}for(let t of["postStabilize","matchNearest"])if(e[t]!==void 0){if(typeof e[t]!="boolean")throw Error(`${t} must be boolean`);this.topology.params[t]=e[t]}}assertEditable(){if(this.created)throw Error("Build this scene before createSolver; use GPU edit methods afterwards")}get bodyCount(){return this.topology.bodyCount}addShape(e,t={}){let i=e.options??t,r=this.addBox(e.size,i);return Gs(this.topology,r,e,i.density??1),r}addCircle(e,t={}){return this.addShape(Os(e),t)}addHull(e,t={}){return this.addShape(Rs(e),t)}addCapsule(e,t,i={}){return this.addShape(js(e,t),i)}addSegment(e,t,i={}){return this.addShape(Ts(e,t,i))}addPlane(e=[0,1],t=0,i={}){return this.addShape(Ds(e,t,i))}addBox(e,{density:t=1,friction:i=.6,position:r=[0,0],angle:s=0,velocity:a=[0,0,0],restitution:n=0,isTrigger:l=!1,group:c=4294967295,collidesWith:f=4294967295,allowSleep:u}={}){if(this.assertEditable(),u!==void 0&&typeof u!="boolean")throw Error("allowSleep must be boolean");let d=J(e,2,"size");d.forEach(h=>V(h,"size"));let p=J(r,2,"position");if(!Number.isFinite(s))throw Error("angle must be finite");if(!Number.isFinite(n)||n<0||n>1)throw Error("restitution must be between 0 and 1");if(typeof l!="boolean")throw Error("isTrigger must be boolean");for(let h of[c,f])if(!Number.isInteger(h)||h<0||h>4294967295)throw Error("Collision masks must be unsigned 32-bit integers");let m=this.topology.addBody(d,V(t,"density",!0),V(i,"friction",!0),[...p,s],J(a,3,"velocity"));return this.restitution??=new Map,n>0&&this.restitution.set(m,n),this.sensors??=new Set,l&&this.sensors.add(m),this.filters??=new Map,(c!==4294967295||f!==4294967295)&&this.filters.set(m,[c,f]),this.topology.avbdSleepEligible??=new Map,u!==void 0&&this.topology.avbdSleepEligible.set(m,u),m}checkBodies(e,t){if(!Number.isInteger(e)||!Number.isInteger(t)||e<-1||e>=this.bodyCount||t<0||t>=this.bodyCount||e===t)throw Error("Invalid 2D constraint endpoints")}addJoint(e,t,i=[0,0],r=[0,0],{stiffness:s=[1/0,1/0,0],breakForce:a=1/0}={}){if(this.assertEditable(),this.checkBodies(e,t),!s||s.length!==3||Array.from(s).some(l=>l!==1/0&&(!Number.isFinite(l)||l<0))||a!==1/0&&(!Number.isFinite(a)||a<0))throw Error("Joint stiffness and break force must be nonnegative or Infinity");let n=this.topology.addJoint(e,t,J(i,2,"anchorA"),J(r,2,"anchorB"),s,a);return this.constraintHandle(n.slot,n)}constraintHandle(e,t={}){return Object.assign(t,{slot:e,alive:!0,disposed:!1}),t.dispose=()=>{t.disposed||(this.gpu&&!this.gpu.destroyed?this.gpu.disableConstraint(e):this.gpu||this.topology.data.fill(0,e*T+$,e*T+$+3),t.alive=!1,t.disposed=!0)},t.destroy=t.dispose,this.topology.handles[e]=t,t}addSpring(e,t,i=[0,0],r=[0,0],{stiffness:s=1e3,rest:a=1}={}){if(this.assertEditable(),this.checkBodies(e,t),e<0)throw Error("A spring requires two bodies");let n=this.topology.jointCount;return this.topology.addSpring(e,t,J(i,2,"anchorA"),J(r,2,"anchorB"),V(s,"stiffness",!0),V(a,"rest",!0)),this.constraintHandle(n)}addMotor(e,t,{speed:i=1,maxTorque:r=100}={}){if(this.assertEditable(),this.checkBodies(e,t),!Number.isFinite(i))throw Error("speed must be finite");let s=this.topology.jointCount;return this.topology.addMotor(e,t,i,V(r,"maxTorque",!0)),this.constraintHandle(s)}addAngularLimit(e,t,i={}){this.assertEditable(),this.checkBodies(e,t);let r=er(i),s=this.topology.addJoint(e,t,[0,0],[0,0],[0,0,0]);ia(this.topology,s.slot,r);let a=this.constraintHandle(s.slot,s);return a.setLimits=n=>{if(a.disposed)throw Error("The angular limit has been disposed");return this.gpu?this.gpu.setAngleLimits(a.slot,n):ia(this.topology,a.slot,er(n)),a},a}addHinge(e,t,i=[0,0],r=[0,0],s={}){this.assertEditable(),this.checkBodies(e,t);let a=J(i,2,"anchorA"),n=J(r,2,"anchorB"),l=s.minAngle!==void 0||s.maxAngle!==void 0?er(s):null,c=s.motor;if(c){if(!Number.isFinite(c.speed??1))throw Error("speed must be finite");V(c.maxTorque??100,"maxTorque",!0)}let f=this.addJoint(e,t,a,n),u=l?this.addAngularLimit(e,t,l):null,d=c?this.addMotor(e,t,c):null;return{joint:f,limits:u,motor:d,disposed:!1,dispose(){this.disposed||(this.joint.dispose(),this.limits?.dispose(),this.motor?.dispose(),this.disposed=!0)},destroy(){this.dispose()}}}createSolver(e,t={}){this.assertEditable();let i=new Vs(e,this.topology,t);for(let[r,s]of this.restitution??[])i.setRestitution(r,s);for(let r of this.sensors??[])i.setSensor(r,!0);for(let[r,s]of this.filters??[])i.setFilters([r],[s[0]],[s[1]]);return this.gpu=i,this.created=!0,i}};export{hs as AvbdPhysics,ps as AvbdPhysicsAggregate,ms as AvbdPhysicsBody,mr as AvbdPhysicsConstraint,br as AvbdPhysicsHinge,na as AvbdScene2D,aa as AvbdScene3D,Xe as AvbdShapeType,Xo as createWebGPUDevice,cs as prepareWebGPUDevice3D};