@newkrok/three-particles 2.15.2 → 2.16.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/README.md +80 -0
- package/dist/index.d.ts +362 -7
- package/dist/index.js +1041 -395
- package/dist/index.js.map +1 -1
- package/dist/three-particles.min.js +1 -1
- package/dist/three-particles.min.js.map +1 -1
- package/dist/webgpu.js +1664 -0
- package/dist/webgpu.js.map +1 -0
- package/llms-full.txt +189 -0
- package/llms.txt +89 -0
- package/package.json +15 -10
- package/webgpu.d.ts +88 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/js/effects/three-particles/webgpu/compute-collision-planes.ts","../src/js/effects/three-particles/three-particles-bezier.ts","../src/js/effects/three-particles/three-particles-utils.ts","../src/js/effects/three-particles/webgpu/compute-force-fields.ts","../src/js/effects/three-particles/webgpu/curve-bake.ts","../src/js/effects/three-particles/webgpu/tsl-noise.ts","../src/js/effects/three-particles/webgpu/compute-modifiers.ts","../src/js/effects/three-particles/three-particles-constants.ts","../src/js/effects/three-particles/webgpu/tsl-shared.ts","../src/js/effects/three-particles/webgpu/tsl-instanced-billboard-material.ts","../src/js/effects/three-particles/webgpu/tsl-mesh-particle-material.ts","../src/js/effects/three-particles/webgpu/tsl-point-sprite-material.ts","../src/js/effects/three-particles/webgpu/tsl-trail-ribbon-material.ts","../src/js/effects/three-particles/webgpu/tsl-materials.ts","../src/webgpu.ts"],"names":["_encodeBuf","uniform","float","Fn","Loop","If","Continue","vec3","dot","vec4","tslMin","floor","Vector3","min","max","mod","vec2","abs","length","cos","sin","Discard","texture","attribute","varyingProperty","positionLocal","modelViewMatrix","cameraProjectionMatrix","MeshBasicNodeMaterial","NoColorSpace","normalize","cross","mix","smoothstep","screenUV"],"mappings":";;;;;;;AAgDA,IAAM,YAAA,GAAe,EAAA;AAGd,IAAM,oBAAA,GAAuB,EAAA;AAG7B,IAAM,4BAA4B,oBAAA,GAAuB,YAAA;AAGhE,IAAI,UAAA,GAAkC,IAAA;AAU/B,SAAS,4BACd,MAAA,EACc;AACd,EAAA,IAAI,CAAC,UAAA,IAAc,UAAA,CAAW,MAAA,KAAW,yBAAA,EAA2B;AAClE,IAAA,UAAA,GAAa,IAAI,aAAa,yBAAyB,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,IAAA,GAAO,UAAA;AACb,EAAA,IAAA,CAAK,KAAK,CAAC,CAAA;AAEX,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,QAAQ,oBAAoB,CAAA;AAC1D,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,IAAA,MAAM,EAAA,GAAK,OAAO,CAAC,CAAA;AACnB,IAAA,MAAM,OAAO,CAAA,GAAI,YAAA;AAEjB,IAAA,IAAA,CAAK,IAAI,CAAA,GAAI,EAAA,CAAG,QAAA,GAAW,CAAA,GAAI,CAAA;AAE/B,IAAA,IAAI,QAAA,GAAW,CAAA;AACf,IAAA,IAAI,EAAA,CAAG,8BAAmC,QAAA,GAAW,CAAA;AAAA,SAAA,IAC5C,EAAA,CAAG,gCAAoC,QAAA,GAAW,CAAA;AAC3D,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,QAAA;AAEjB,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,MAAA,CAAO,CAAA;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,MAAA,CAAO,CAAA;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,MAAA,CAAO,CAAA;AAC3B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,MAAA;AACpB,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,YAAA;AACpB,IAAA,IAAA,CAAK,IAAA,GAAO,EAAE,CAAA,GAAI,CAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,EAAE,CAAA,GAAI,CAAA;AAAA,EACpB;AAEA,EAAA,OAAO,IAAA;AACT;AAgBO,SAAS,uBAAA,CACd,UAAA,EACA,oBAAA,EACA,mBAAA,EACA;AACA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,mBAAA,EAAqB,oBAAoB,CAAA;AAChE,EAAA,MAAM,oBAAA,GAAuB,OAAA,CAAQ,KAAA,CAAM,KAAK,CAAC,CAAA;AACjD,EAAA,MAAM,MAAA,GAAS,oBAAA;AAcf,EAAA,MAAM,uBAAA,GAA0B,EAAA;AAAA,IAC9B,CAAC;AAAA,MACC,GAAA;AAAA,MACA,GAAA;AAAA,MACA,MAAA;AAAA,MACA,UAAA;AAAA,MACA,EAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACF,KASM;AACJ,MAAA,IAAA,CAAK,oBAAA,EAAsB,CAAC,EAAE,CAAA,EAAE,KAAqC;AACnE,QAAA,MAAM,OAAO,CAAA,CAAE,GAAA,CAAI,YAAY,CAAA,CAAE,IAAI,MAAM,CAAA;AAE3C,QAAA,MAAM,QAAA,GAAW,UAAA,CAAW,OAAA,CAAQ,IAAI,CAAA;AACxC,QAAA,EAAA,CAAG,QAAA,CAAS,QAAA,CAAS,GAAG,CAAA,EAAG,MAAM;AAC/B,UAAA,QAAA,EAAS;AAAA,QACX,CAAC,CAAA;AAED,QAAA,MAAM,OAAO,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAC3C,QAAA,MAAM,QAAA,GAAW,IAAA;AAAA,UACf,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC;AAAA,SAChC;AACA,QAAA,MAAM,WAAA,GAAc,IAAA;AAAA,UAClB,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC;AAAA,SAChC;AACA,QAAA,MAAM,SAAS,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAC7C,QAAA,MAAM,eAAe,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAGnD,QAAA,MAAM,UAAA,GAAa,GAAA,CAAI,GAAA,CAAI,QAAQ,CAAA;AACnC,QAAA,MAAM,UAAA,GAAa,GAAA,CAAI,UAAA,EAAY,WAAW,CAAA;AAG9C,QAAA,EAAA,CAAG,UAAA,CAAW,QAAA,CAAS,CAAG,CAAA,EAAG,MAAM;AAKjC,UAAA,EAAA,CAAG,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG,MAAM;AAC3B,YAAA,EAAA,CAAG,EAAE,MAAA,CAAO,SAAA,CAAU,IAAI,KAAA,CAAM,CAAG,CAAC,CAAC,CAAA;AAAA,UACvC,CAAC,CAAA;AAGD,UAAA,EAAA,CAAG,IAAA,CAAK,gBAAA,CAAiB,GAAG,CAAA,CAAE,GAAA,CAAI,KAAK,QAAA,CAAS,GAAG,CAAC,CAAA,EAAG,MAAM;AAE3D,YAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,YAAY,GAAA,CAAI,UAAU,CAAC,CAAC,CAAA;AAG/C,YAAA,MAAM,OAAA,GAAU,GAAA,CAAI,GAAA,EAAK,WAAW,CAAA;AACpC,YAAA,EAAA,CAAG,OAAA,CAAQ,QAAA,CAAS,CAAG,CAAA,EAAG,MAAM;AAC9B,cAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,YAAY,GAAA,CAAI,OAAO,CAAC,CAAC,CAAA;AAAA,YAC9C,CAAC,CAAA;AAAA,UACH,CAAC,CAAA;AAGD,UAAA,EAAA,CAAG,IAAA,CAAK,gBAAA,CAAiB,GAAG,CAAA,EAAG,MAAM;AAEnC,YAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,YAAY,GAAA,CAAI,UAAU,CAAC,CAAC,CAAA;AAG/C,YAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,EAAK,WAAW,CAAA;AAClC,YAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,WAAA,CAAY,IAAI,KAAA,CAAM,GAAA,CAAI,CAAG,CAAC,CAAC,CAAA;AACzD,YAAA,GAAA,CAAI,MAAA,CAAO,SAAA,CAAU,GAAA,CAAI,MAAM,CAAC,CAAA;AAGhC,YAAA,EAAA,CAAG,YAAA,CAAa,WAAA,CAAY,CAAG,CAAA,EAAG,MAAM;AACtC,cAAA,EAAA,CAAG,CAAA,CAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,SAAS,CAAA,CAAE,GAAA,CAAI,GAAM,CAAC,CAAC,CAAA;AAAA,YAC/D,CAAC,CAAA;AAAA,UACH,CAAC,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO;AAAA;AAAA,IAEL,YAAA,EAAc,oBAAA;AAAA;AAAA,IAEd,KAAA,EAAO;AAAA,GACT;AACF;;;ACvOA,IAAM,QAID,EAAC;AAEN,IAAM,GAAA,GAAM,CAAC,CAAA,EAAW,CAAA,KAAc;AACpC,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,IAAK,CAAA,EAAG,KAAK,CAAA,IAAA,CAAM,CAAA,GAAI,IAAI,CAAA,IAAK,CAAA;AAChD,EAAA,OAAO,CAAA;AACT,CAAA;AAEO,IAAM,yBAAA,GAA4B,CACvC,gBAAA,EACA,YAAA,KACG;AACH,EAAA,MAAM,aAAa,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS,IAAA,CAAK,iBAAiB,YAAY,CAAA;AAE1E,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,IAAI,CAAC,UAAA,CAAW,YAAA,CAAa,QAAA,CAAS,gBAAgB,CAAA;AACpD,MAAA,UAAA,CAAW,YAAA,CAAa,KAAK,gBAAgB,CAAA;AAC/C,IAAA,OAAO,UAAA,CAAW,aAAA;AAAA,EACpB;AAEA,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,YAAA,EAAc,CAAC,gBAAgB,CAAA;AAAA,IAC/B,YAAA;AAAA,IACA,aAAA,EAAe,CAAC,UAAA,KAA+B;AAC7C,MAAA,IAAI,UAAA,GAAa,CAAA,EAAG,OAAO,YAAA,CAAa,CAAC,CAAA,CAAE,CAAA;AAC3C,MAAA,IAAI,aAAa,CAAA,EAAG,OAAO,aAAa,YAAA,CAAa,MAAA,GAAS,CAAC,CAAA,CAAE,CAAA;AAEjE,MAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,MAAA,IAAI,IAAA,GAAO,aAAa,MAAA,GAAS,CAAA;AAEjC,MAAA,YAAA,CAAa,IAAA,CAAK,CAAC,KAAA,EAAO,KAAA,KAAU;AAClC,QAAA,MAAM,MAAA,GAAS,UAAA,IAAc,KAAA,CAAM,UAAA,IAAc,CAAA,CAAA;AACjD,QAAA,IAAI,QAAQ,IAAA,GAAO,KAAA;AAAA,aAAA,IACV,KAAA,CAAM,UAAA,KAAe,MAAA,EAAW,KAAA,GAAQ,KAAA;AACjD,QAAA,OAAO,MAAA;AAAA,MACT,CAAC,CAAA;AAED,MAAA,MAAM,IAAI,IAAA,GAAO,KAAA;AACjB,MAAA,MAAM,oBAAA,GAAA,CACH,UAAA,IAAc,YAAA,CAAa,KAAK,EAAE,UAAA,IAAc,CAAA,CAAA,KAAA,CAC/C,YAAA,CAAa,IAAI,EAAE,UAAA,IAAc,CAAA,KAChC,YAAA,CAAa,KAAK,EAAE,UAAA,IAAc,CAAA,CAAA,CAAA;AAEvC,MAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,EAAG,CAAA,EAAA,EAAK;AAC3B,QAAA,MAAM,CAAA,GAAI,YAAA,CAAa,KAAA,GAAQ,CAAC,CAAA;AAChC,QAAA,MAAM,CAAA,GACJ,GAAA,CAAI,CAAA,EAAG,CAAC,IACR,IAAA,CAAK,GAAA,CAAI,CAAA,GAAI,oBAAA,EAAsB,IAAI,CAAC,CAAA,GACxC,IAAA,CAAK,GAAA,CAAI,sBAAsB,CAAC,CAAA;AAClC,QAAA,KAAA,IAAS,IAAI,CAAA,CAAE,CAAA;AAAA,MACjB;AACA,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,OAAO,KAAA,CAAM,aAAA;AACf,CAAA;;;ACiVO,IAAM,eAAA,GAAkB,CAC7B,KAAA,KAC2B;AAC3B,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,MAAA,IAAU,KAAA;AAChD,CAAA;AAEO,IAAM,0BAAA,GAA6B,CACxC,gBAAA,EACA,aAAA,KACG;AACH,EAAA,IAAI,cAAc,IAAA,KAAA,QAAA,eAA+B;AAC/C,IAAA,OAAO,yBAAA;AAAA,MACL,gBAAA;AAAA,MACA,aAAA,CAAc;AAAA,KAChB;AAAA,EACF;AAEA,EAAA,IAAI,cAAc,IAAA,KAAA,QAAA,eAA+B;AAC/C,IAAA,OAAO,aAAA,CAAc,aAAA;AAAA,EACvB;AAEA,EAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,aAAa,CAAA,CAAE,CAAA;AAC5D,CAAA;AAEO,IAAM,cAAA,GAAiB,CAC5B,gBAAA,EACA,KAAA,EACA,OAAe,CAAA,KACJ;AACX,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,KAAA,IAAS,KAAA,IAAS,KAAA,IAAS,KAAA,EAAO;AACpC,IAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,KAAA,CAAM,GAAA,EAAK;AAC3B,MAAA,OAAO,MAAM,GAAA,IAAO,CAAA;AAAA,IACtB;AACA,IAAA,OAAa,gBAAU,SAAA,CAAU,KAAA,CAAM,OAAO,CAAA,EAAG,KAAA,CAAM,OAAO,CAAC,CAAA;AAAA,EACjE;AAEA,EAAA,MAAM,aAAA,GAAgB,KAAA;AACtB,EAAA,OACE,2BAA2B,gBAAA,EAAkB,aAAa,EAAE,IAAI,CAAA,IAC/D,cAAc,KAAA,IAAS,CAAA,CAAA;AAE5B,CAAA;;;ACxYA,IAAM,YAAA,GAAe,EAAA;AAGd,IAAM,gBAAA,GAAmB,EAAA;AAGzB,IAAM,wBAAwB,gBAAA,GAAmB,YAAA;AAGxD,IAAM,YAAA,GAAe,IAAA;AAGrB,IAAIA,WAAAA,GAAkC,IAAA;AAY/B,SAAS,uBAAA,CACd,WAAA,EACA,gBAAA,EACA,wBAAA,EACc;AACd,EAAA,IAAI,CAACA,WAAAA,IAAcA,WAAAA,CAAW,MAAA,KAAW,qBAAA,EAAuB;AAC9D,IAAAA,WAAAA,GAAa,IAAI,YAAA,CAAa,qBAAqB,CAAA;AAAA,EACrD;AACA,EAAA,MAAM,IAAA,GAAOA,WAAAA;AACb,EAAA,IAAA,CAAK,KAAK,CAAC,CAAA;AAEX,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,WAAA,CAAY,QAAQ,gBAAgB,CAAA;AAC3D,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,EAAO,CAAA,EAAA,EAAK;AAC9B,IAAA,MAAM,EAAA,GAAK,YAAY,CAAC,CAAA;AACxB,IAAA,MAAM,OAAO,CAAA,GAAI,YAAA;AAEjB,IAAA,IAAA,CAAK,IAAI,CAAA,GAAI,EAAA,CAAG,QAAA,GAAW,CAAA,GAAI,CAAA;AAC/B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,+BAAgC,CAAA,GAAI,CAAA;AACxD,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,QAAA,CAAS,CAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,SAAA,CAAU,CAAA;AAC9B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,SAAA,CAAU,CAAA;AAC9B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,EAAA,CAAG,SAAA,CAAU,CAAA;AAC9B,IAAA,IAAA,CAAK,IAAA,GAAO,CAAC,CAAA,GAAI,cAAA;AAAA,MACf,gBAAA;AAAA,MACA,EAAA,CAAG,QAAA;AAAA,MACH;AAAA,KACF;AACA,IAAA,IAAA,CAAK,OAAO,CAAC,CAAA,GAAI,GAAG,KAAA,KAAU,QAAA,GAAW,eAAe,EAAA,CAAG,KAAA;AAE3D,IAAA,IAAI,WAAA,GAAc,CAAA;AAClB,IAAA,IAAI,EAAA,CAAG,mCAAsC,WAAA,GAAc,CAAA;AAAA,SAAA,IAClD,EAAA,CAAG,yCAAyC,WAAA,GAAc,CAAA;AACnE,IAAA,IAAA,CAAK,IAAA,GAAO,EAAE,CAAA,GAAI,WAAA;AAClB,IAAA,IAAA,CAAK,IAAA,GAAO,EAAE,CAAA,GAAI,CAAA;AAAA,EACpB;AAEA,EAAA,OAAO,IAAA;AACT;AAgBO,SAAS,mBAAA,CACd,UAAA,EACA,gBAAA,EACA,eAAA,EACA;AACA,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,gBAAgB,CAAA;AACxD,EAAA,MAAM,gBAAA,GAAmBC,OAAAA,CAAQC,KAAAA,CAAM,KAAK,CAAC,CAAA;AAC7C,EAAA,MAAM,MAAA,GAAS,gBAAA;AASf,EAAA,MAAM,mBAAA,GAAsBC,EAAAA;AAAA,IAC1B,CAAC;AAAA,MACC,GAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACF,KAIM;AACJ,MAAAC,IAAAA,CAAK,gBAAA,EAAkB,CAAC,EAAE,GAAE,KAAqC;AAC/D,QAAA,MAAM,OAAO,CAAA,CAAE,GAAA,CAAI,YAAY,CAAA,CAAE,IAAI,MAAM,CAAA;AAE3C,QAAA,MAAM,QAAA,GAAW,UAAA,CAAW,OAAA,CAAQ,IAAI,CAAA;AACxC,QAAAC,EAAAA,CAAG,QAAA,CAAS,QAAA,CAAS,GAAG,GAAG,MAAM;AAC/B,UAAAC,QAAAA,EAAS;AAAA,QACX,CAAC,CAAA;AAED,QAAA,MAAM,YAAY,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAChD,QAAA,MAAM,QAAA,GAAWC,IAAAA;AAAA,UACf,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC;AAAA,SAChC;AACA,QAAA,MAAM,QAAA,GAAWA,IAAAA;AAAA,UACf,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAC9B,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC;AAAA,SAChC;AACA,QAAA,MAAM,WAAW,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAC/C,QAAA,MAAM,QAAQ,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,CAAC,CAAC,CAAA;AAC5C,QAAA,MAAM,cAAc,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,EAAE,CAAC,CAAA;AAEnD,QAAAF,EAAAA,CAAG,QAAA,CAAS,KAAA,CAAM,CAAG,GAAG,MAAM;AAC5B,UAAAC,QAAAA,EAAS;AAAA,QACX,CAAC,CAAA;AAGD,QAAAD,EAAAA,CAAG,SAAA,CAAU,WAAA,CAAY,GAAG,GAAG,MAAM;AACnC,UAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAChC,UAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,SAAS,GAAA,CAAI,KAAK,CAAC,CAAC,CAAA;AAAA,QACzC,CAAC,CAAA;AAGD,QAAAA,EAAAA,CAAG,SAAA,CAAU,QAAA,CAAS,GAAG,GAAG,MAAM;AAChC,UAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AAChC,UAAA,MAAM,IAAA,GAAO,OAAO,OAAO,CAAA;AAG3B,UAAAA,EAAAA,CAAG,IAAA,CAAK,WAAA,CAAY,IAAM,GAAG,MAAM;AAEjC,YAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA;AACnC,YAAAA,EAAAA,CAAG,SAAS,MAAM;AAChB,cAAA,MAAM,GAAA,GAAM,UAAU,OAAO,CAAA;AAG7B,cAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA;AAK/B,cAAA,MAAM,WAAA,GAAcH,MAAM,CAAG,CAAA;AAC7B,cAAA,MAAM,aAAA,GAAgBA,KAAAA,CAAM,CAAG,CAAA,CAAE,IAAI,QAAQ,CAAA;AAC7C,cAAA,MAAM,gBAAA,GAAmBA,MAAM,CAAG,CAAA,CAAE,IAAI,QAAA,CAAS,GAAA,CAAI,QAAQ,CAAC,CAAA;AAG9D,cAAA,MAAM,SAAA,GAAY,WAAA,CAAY,WAAA,CAAY,GAAG,CAAA;AAC7C,cAAA,MAAM,YAAA,GAAe,WAAA,CAAY,WAAA,CAAY,GAAG,CAAA;AAChD,cAAA,MAAM,UAAU,YAAA,CAAa,MAAA;AAAA,gBAC3B,gBAAA;AAAA,gBACA,SAAA,CAAU,MAAA,CAAO,aAAA,EAAe,WAAW;AAAA,eAC7C;AAGA,cAAA,MAAM,QAAQ,QAAA,CAAS,GAAA,CAAI,OAAO,CAAA,CAAE,IAAI,KAAK,CAAA;AAC7C,cAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,IAAI,GAAA,CAAI,KAAK,CAAC,CAAC,CAAA;AAAA,YACpC,CAAC,CAAA;AAAA,UACH,CAAC,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,OAAO;AAAA;AAAA,IAEL,YAAA,EAAc,gBAAA;AAAA;AAAA,IAEd,KAAA,EAAO;AAAA,GACT;AACF;;;AC9MO,IAAM,gBAAA,GAAmB,GAAA;AA8FhC,SAAS,mBAAA,CACP,MAAA,EACA,WAAA,EACA,gBAAA,EACA,KAAA,EACQ;AACR,EAAA,MAAM,OAAA,GAAU,0BAAA,CAA2B,gBAAA,EAAkB,KAAK,CAAA;AAClE,EAAA,MAAM,YAAY,gBAAA,GAAmB,CAAA;AAErC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,gBAAA,EAAkB,CAAA,EAAA,EAAK;AACzC,IAAA,MAAM,CAAA,GAA0B,CAAA,GAAI,SAAA;AACpC,IAAA,MAAA,CAAO,WAAA,GAAc,CAAC,CAAA,GAAI,OAAA,CAAQ,CAAC,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,WAAA,GAAc,gBAAA;AACvB;AAMA,SAAS,0BAAA,CACP,MAAA,EACA,WAAA,EACA,gBAAA,EACA,KAAA,EAIQ;AACR,EAAA,IAAI,eAAA,CAAgB,KAAK,CAAA,EAAG;AAC1B,IAAA,OAAO,mBAAA,CAAoB,MAAA,EAAQ,WAAA,EAAa,gBAAA,EAAkB,KAAK,CAAA;AAAA,EACzE;AAEA,EAAA,MAAM,aAAA,GAAgB,cAAA,CAAe,gBAAA,EAAkB,KAAA,EAAO,GAAG,CAAA;AACjE,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,gBAAA,EAAkB,CAAA,EAAA,EAAK;AACzC,IAAA,MAAA,CAAO,WAAA,GAAc,CAAC,CAAA,GAAI,aAAA;AAAA,EAC5B;AACA,EAAA,OAAO,WAAA,GAAc,gBAAA;AACvB;AAmCO,SAAS,wBAAA,CACd,kBACA,gBAAA,EACe;AAEf,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,MAAM;AAAA,IACJ,gBAAA;AAAA,IACA,mBAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF,GAAI,gBAAA;AAEJ,EAAA,MAAM,sBAAsB,gBAAA,CAAiB,QAAA;AAC7C,EAAA,MAAM,yBAAyB,mBAAA,CAAoB,QAAA;AACnD,EAAA,MAAM,uBAAuB,iBAAA,CAAkB,QAAA;AAI/C,EAAA,MAAM,cAAc,oBAAA,CAAqB,QAAA;AACzC,EAAA,MAAM,aAAA,GACJ,eACA,oBAAA,CAAqB,MAAA,CAAO,MAAM,MAAA,IAClC,oBAAA,CAAqB,OAAO,CAAA,KAAM,CAAA;AACpC,EAAA,MAAM,aAAA,GACJ,eACA,oBAAA,CAAqB,MAAA,CAAO,MAAM,MAAA,IAClC,oBAAA,CAAqB,OAAO,CAAA,KAAM,CAAA;AACpC,EAAA,MAAM,aAAA,GACJ,eACA,oBAAA,CAAqB,MAAA,CAAO,MAAM,MAAA,IAClC,oBAAA,CAAqB,OAAO,CAAA,KAAM,CAAA;AAEpC,EAAA,MAAM,cAAA,GACJ,eACA,oBAAA,CAAqB,OAAA,CAAQ,MAAM,MAAA,IACnC,oBAAA,CAAqB,QAAQ,CAAA,KAAM,CAAA;AACrC,EAAA,MAAM,cAAA,GACJ,eACA,oBAAA,CAAqB,OAAA,CAAQ,MAAM,MAAA,IACnC,oBAAA,CAAqB,QAAQ,CAAA,KAAM,CAAA;AACrC,EAAA,MAAM,cAAA,GACJ,eACA,oBAAA,CAAqB,OAAA,CAAQ,MAAM,MAAA,IACnC,oBAAA,CAAqB,QAAQ,CAAA,KAAM,CAAA;AAErC,EAAA,IAAI,mBAAA,EAAqB,UAAA,EAAA;AACzB,EAAA,IAAI,sBAAA,EAAwB,UAAA,EAAA;AAC5B,EAAA,IAAI,sBAAsB,UAAA,IAAc,CAAA;AACxC,EAAA,IAAI,aAAA,EAAe,UAAA,EAAA;AACnB,EAAA,IAAI,aAAA,EAAe,UAAA,EAAA;AACnB,EAAA,IAAI,aAAA,EAAe,UAAA,EAAA;AACnB,EAAA,IAAI,cAAA,EAAgB,UAAA,EAAA;AACpB,EAAA,IAAI,cAAA,EAAgB,UAAA,EAAA;AACpB,EAAA,IAAI,cAAA,EAAgB,UAAA,EAAA;AAGpB,EAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,UAAA,GAAa,gBAAgB,CAAA;AAG3D,EAAA,IAAI,WAAA,GAAc,CAAA;AAClB,EAAA,IAAI,SAAA,GAAY,CAAA;AAGhB,EAAA,IAAI,mBAAA,GAAsB,EAAA;AAC1B,EAAA,IAAI,sBAAA,GAAyB,EAAA;AAC7B,EAAA,IAAI,SAAA,GAAY,EAAA;AAChB,EAAA,IAAI,SAAA,GAAY,EAAA;AAChB,EAAA,IAAI,SAAA,GAAY,EAAA;AAChB,EAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,EAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,EAAA,IAAI,aAAA,GAAgB,EAAA;AACpB,EAAA,IAAI,cAAA,GAAiB,EAAA;AACrB,EAAA,IAAI,cAAA,GAAiB,EAAA;AACrB,EAAA,IAAI,cAAA,GAAiB,EAAA;AAErB,EAAA,IAAI,mBAAA,EAAqB;AACvB,IAAA,mBAAA,GAAsB,SAAA,EAAA;AACtB,IAAA,WAAA,GAAc,mBAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,gBAAA,CAAiB;AAAA,KACnB;AAAA,EACF;AAEA,EAAA,IAAI,sBAAA,EAAwB;AAC1B,IAAA,sBAAA,GAAyB,SAAA,EAAA;AACzB,IAAA,WAAA,GAAc,mBAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,mBAAA,CAAoB;AAAA,KACtB;AAAA,EACF;AAEA,EAAA,IAAI,oBAAA,EAAsB;AACxB,IAAA,SAAA,GAAY,SAAA,EAAA;AACZ,IAAA,WAAA,GAAc,mBAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,iBAAA,CAAkB;AAAA,KACpB;AAEA,IAAA,SAAA,GAAY,SAAA,EAAA;AACZ,IAAA,WAAA,GAAc,mBAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,iBAAA,CAAkB;AAAA,KACpB;AAEA,IAAA,SAAA,GAAY,SAAA,EAAA;AACZ,IAAA,WAAA,GAAc,mBAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,iBAAA,CAAkB;AAAA,KACpB;AAAA,EACF;AAEA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,aAAA,GAAgB,SAAA,EAAA;AAChB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,MAAA,CAAO;AAAA,KAC9B;AAAA,EACF;AAEA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,aAAA,GAAgB,SAAA,EAAA;AAChB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,MAAA,CAAO;AAAA,KAC9B;AAAA,EACF;AAEA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,aAAA,GAAgB,SAAA,EAAA;AAChB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,MAAA,CAAO;AAAA,KAC9B;AAAA,EACF;AAEA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,cAAA,GAAiB,SAAA,EAAA;AACjB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,OAAA,CAAQ;AAAA,KAC/B;AAAA,EACF;AAEA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,cAAA,GAAiB,SAAA,EAAA;AACjB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,OAAA,CAAQ;AAAA,KAC/B;AAAA,EACF;AAEA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,cAAA,GAAiB,SAAA,EAAA;AACjB,IAAA,WAAA,GAAc,0BAAA;AAAA,MACZ,IAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,qBAAqB,OAAA,CAAQ;AAAA,KAC/B;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,UAAA;AAAA,IACA,gBAAA,EAAkB,mBAAA;AAAA,IAClB,mBAAA,EAAqB,sBAAA;AAAA,IACrB,MAAA,EAAQ,SAAA;AAAA,IACR,MAAA,EAAQ,SAAA;AAAA,IACR,MAAA,EAAQ,SAAA;AAAA,IACR,UAAA,EAAY,aAAA;AAAA,IACZ,UAAA,EAAY,aAAA;AAAA,IACZ,UAAA,EAAY,aAAA;AAAA,IACZ,WAAA,EAAa,cAAA;AAAA,IACb,WAAA,EAAa,cAAA;AAAA,IACb,WAAA,EAAa;AAAA,GACf;AACF;ACvWA,IAAM,OAAA,GAAUC,EAAAA,CAAG,CAAC,EAAE,GAAE,KAA8C;AAEpE,EAAA,OAAO,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,EAAI,CAAA,CAAE,GAAA,CAAI,EAAI,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,EAAGD,KAAAA,CAAM,GAAK,CAAC,CAAA;AACvD,CAAC,CAAA;AASD,IAAM,aAAA,GAAgBC,EAAAA,CAAG,CAAC,EAAE,GAAE,KAA8C;AAC1E,EAAA,OAAOD,KAAAA,CAAM,gBAAgB,CAAA,CAAE,GAAA,CAAIA,MAAM,gBAAgB,CAAA,CAAE,GAAA,CAAI,CAAC,CAAC,CAAA;AACnE,CAAC,CAAA;AAqBM,IAAM,QAAA,GAAkCC,EAAAA;AAAA,EAC7C,CAAC,EAAE,CAAA,EAAE,KAA8C;AAGjD,IAAA,MAAM,SAAA,GAAYD,KAAAA,CAAM,CAAA,GAAM,CAAG,CAAA;AACjC,IAAA,MAAM,SAAA,GAAYA,KAAAA,CAAM,CAAA,GAAM,CAAG,CAAA;AAGjC,IAAA,MAAM,CAAA,GAAI,KAAA;AAAA,MACR,CAAA,CAAE,IAAIM,GAAAA,CAAI,CAAA,EAAGD,KAAK,SAAA,EAAW,SAAA,EAAW,SAAS,CAAC,CAAC;AAAA,MACnD,KAAA,EAAM;AAIR,IAAA,MAAM,EAAA,GAAK,CAAA,CACR,GAAA,CAAI,CAAC,EACL,GAAA,CAAIC,GAAAA,CAAI,CAAA,EAAGD,IAAAA,CAAK,WAAW,SAAA,EAAW,SAAS,CAAC,CAAC,EACjD,KAAA,EAAM;AAST,IAAA,MAAM,IAAI,IAAA,CAAK,EAAA,CAAG,KAAK,EAAA,CAAG,GAAG,EAAE,KAAA,EAAM;AACrC,IAAA,MAAM,IAAIL,KAAAA,CAAM,CAAG,EAAE,GAAA,CAAI,CAAC,EAAE,KAAA,EAAM;AAIlC,IAAA,MAAM,KAAK,GAAA,CAAI,CAAA,CAAE,KAAK,CAAA,CAAE,GAAG,EAAE,KAAA,EAAM;AACnC,IAAA,MAAM,KAAK,GAAA,CAAI,CAAA,CAAE,KAAK,CAAA,CAAE,GAAG,EAAE,KAAA,EAAM;AAMnC,IAAA,MAAM,EAAA,GAAK,GAAG,GAAA,CAAI,EAAE,EAAE,GAAA,CAAI,SAAS,EAAE,KAAA,EAAM;AAC3C,IAAA,MAAM,EAAA,GAAK,EAAA,CAAG,GAAA,CAAI,EAAE,CAAA,CAAE,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,CAAG,CAAC,CAAA,CAAE,KAAA,EAAM;AACpD,IAAA,MAAM,EAAA,GAAK,EAAA,CAAG,GAAA,CAAIA,KAAAA,CAAM,CAAG,CAAC,CAAA,CAAE,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,CAAG,CAAC,EAAE,KAAA,EAAM;AAI5D,IAAA,MAAM,KAAK,GAAA,CAAI,CAAA,EAAGA,MAAM,GAAK,CAAC,EAAE,KAAA,EAAM;AAUtC,IAAA,MAAM,QAAQ,OAAA,CAAQ;AAAA,MACpB,GAAG,OAAA,CAAQ;AAAA,QACT,CAAA,EAAGO,IAAAA;AAAA,UACD,IAAA,CAAK,GAAG,CAAA,EAAG,EAAA,CAAG,EAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAC,CAAA;AAAA,UACzB,IAAA,CAAK,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAA,EAAG,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,CAAG,CAAC;AAAA;AACpC,OACD,CAAA,CAAE,GAAA;AAAA,QACDA,IAAAA,CAAK,KAAK,EAAA,CAAG,CAAA,EAAG,GAAG,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAC,CAAA,EAAG,KAAK,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAA,EAAG,GAAG,CAAA,CAAE,GAAA,CAAI,CAAG,CAAC,CAAC;AAAA;AACtE,KACD,CAAA;AAGD,IAAA,MAAM,IAAI,OAAA,CAAQ;AAAA,MAChB,GAAG,KAAA,CAAM,GAAA;AAAA,QACPA,IAAAA,CAAK,KAAK,EAAA,CAAG,CAAA,EAAG,GAAG,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAC,CAAA,EAAG,KAAK,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,CAAC,CAAA,EAAG,GAAG,CAAA,CAAE,GAAA,CAAI,CAAG,CAAC,CAAC;AAAA;AACtE,KACD,CAAA;AAmBD,IAAA,MAAM,EAAA,GAAKP,MAAM,iBAAiB,CAAA;AAIlC,IAAA,MAAM,IAAI,CAAA,CAAE,GAAA,CAAIA,MAAM,EAAI,CAAA,CAAE,IAAI,KAAA,CAAM,CAAA,CAAE,GAAA,CAAI,EAAE,EAAE,GAAA,CAAI,EAAE,CAAC,CAAC,CAAC,EAAE,KAAA,EAAM;AAGjE,IAAA,MAAM,KAAK,KAAA,CAAM,CAAA,CAAE,IAAI,EAAE,CAAC,EAAE,KAAA,EAAM;AAElC,IAAA,MAAM,EAAA,GAAK,KAAA,CAAM,CAAA,CAAE,GAAA,CAAIA,KAAAA,CAAM,CAAG,CAAA,CAAE,GAAA,CAAI,EAAE,CAAC,CAAC,CAAA,CAAE,KAAA,EAAM;AAKlD,IAAA,MAAM,IAAA,GAAOA,MAAM,iBAAiB,CAAA;AACpC,IAAA,MAAM,IAAA,GAAOA,MAAM,kBAAkB,CAAA;AAErC,IAAA,MAAM,KAAK,EAAA,CAAG,GAAA,CAAI,IAAI,CAAA,CAAE,IAAI,IAAI,CAAA;AAChC,IAAA,MAAM,KAAK,EAAA,CAAG,GAAA,CAAI,IAAI,CAAA,CAAE,IAAI,IAAI,CAAA;AAGhC,IAAA,MAAM,EAAA,GAAKA,KAAAA,CAAM,CAAG,CAAA,CAAE,IAAI,GAAA,CAAI,EAAE,CAAC,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAC,EAAE,KAAA,EAAM;AAMtD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,EAAA,EAAIO,IAAAA,CAAK,CAAG,CAAC,CAAA;AACjC,IAAA,MAAM,EAAA,GAAK,OAAO,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,CAAE,GAAA,CAAI,GAAG,CAAC,CAAA;AACxC,IAAA,MAAM,EAAA,GAAK,OAAO,GAAA,CAAI,KAAA,CAAM,EAAE,CAAA,CAAE,GAAA,CAAI,GAAG,CAAC,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,EAAA,CAAG,GAAA,CAAI,EAAE,CAAA;AAC1B,IAAA,MAAM,QAAA,GAAW,EAAA,CAAG,GAAA,CAAI,EAAE,CAAA;AAG1B,IAAA,MAAM,EAAA,GAAKF,KAAK,QAAA,CAAS,CAAA,EAAG,SAAS,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,CAAE,KAAA,EAAM;AACpD,IAAA,MAAM,EAAA,GAAKA,KAAK,QAAA,CAAS,CAAA,EAAG,SAAS,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,CAAE,KAAA,EAAM;AACpD,IAAA,MAAM,EAAA,GAAKA,KAAK,QAAA,CAAS,CAAA,EAAG,SAAS,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,CAAE,KAAA,EAAM;AACpD,IAAA,MAAM,EAAA,GAAKA,KAAK,QAAA,CAAS,CAAA,EAAG,SAAS,CAAA,EAAG,EAAA,CAAG,CAAC,CAAA,CAAE,KAAA,EAAM;AAGpD,IAAA,MAAM,OAAO,aAAA,CAAc;AAAA,MACzB,CAAA,EAAGE,KAAK,IAAA,CAAKD,GAAAA,CAAI,IAAI,EAAE,CAAA,EAAGA,IAAI,EAAA,EAAI,EAAE,CAAC,CAAA,EAAG,IAAA,CAAKA,IAAI,EAAA,EAAI,EAAE,GAAGA,GAAAA,CAAI,EAAA,EAAI,EAAE,CAAC,CAAC;AAAA,KACvE,CAAA;AACD,IAAA,EAAA,CAAG,MAAA,CAAO,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,CAAC,CAAC,CAAA;AACxB,IAAA,EAAA,CAAG,MAAA,CAAO,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,CAAC,CAAC,CAAA;AACxB,IAAA,EAAA,CAAG,MAAA,CAAO,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,CAAC,CAAC,CAAA;AACxB,IAAA,EAAA,CAAG,MAAA,CAAO,EAAA,CAAG,GAAA,CAAI,IAAA,CAAK,CAAC,CAAC,CAAA;AAIxB,IAAA,MAAM,CAAA,GAAI,GAAA;AAAA,MACRC,IAAAA;AAAA,QACE,KAAKP,KAAAA,CAAM,GAAG,EAAE,GAAA,CAAIM,GAAAA,CAAI,IAAI,EAAE,CAAC,CAAA,EAAGN,KAAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAIM,IAAI,EAAA,EAAI,EAAE,CAAC,CAAC,CAAA;AAAA,QAC7D,KAAKN,KAAAA,CAAM,GAAG,EAAE,GAAA,CAAIM,GAAAA,CAAI,IAAI,EAAE,CAAC,CAAA,EAAGN,KAAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAIM,IAAI,EAAA,EAAI,EAAE,CAAC,CAAC;AAAA,OAC/D;AAAA,MACAN,MAAM,CAAG;AAAA,MACT,KAAA,EAAM;AAER,IAAA,MAAM,EAAA,GAAK,CAAA,CAAE,GAAA,CAAI,CAAC,EAAE,KAAA,EAAM;AAC1B,IAAA,MAAM,EAAA,GAAK,EAAA,CAAG,GAAA,CAAI,EAAE,EAAE,KAAA,EAAM;AAG5B,IAAA,MAAM,IAAA,GAAOO,IAAAA;AAAA,MACX,IAAA,CAAKD,IAAI,EAAA,EAAI,EAAE,GAAGA,GAAAA,CAAI,EAAA,EAAI,EAAE,CAAC,CAAA;AAAA,MAC7B,IAAA,CAAKA,IAAI,EAAA,EAAI,EAAE,GAAGA,GAAAA,CAAI,EAAA,EAAI,EAAE,CAAC;AAAA,KAC/B;AAKA,IAAA,OAAON,MAAM,EAAI,CAAA,CAAE,IAAIM,GAAAA,CAAI,EAAA,EAAI,IAAI,CAAC,CAAA;AAAA,EACtC;AACF,CAAA;AAwBqDL,EAAAA;AAAA,EACnD,CAAC,EAAE,CAAA,EAAE,KAA8C;AACjD,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,EAAE,CAAA,EAAGI,IAAAA,CAAK,CAAA,EAAGL,KAAAA,CAAM,CAAG,CAAA,EAAGA,KAAAA,CAAM,CAAG,CAAC,GAAG,CAAA;AAC9D,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,EAAE,CAAA,EAAGK,IAAAA,CAAK,CAAA,EAAG,CAAA,EAAGL,KAAAA,CAAM,CAAG,CAAC,CAAA,EAAG,CAAA;AACrD,IAAA,MAAM,MAAA,GAAS,SAAS,EAAE,CAAA,EAAGK,KAAK,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,EAAG,CAAA;AAC5C,IAAA,OAAOA,IAAAA,CAAK,MAAA,EAAQ,MAAA,EAAQ,MAAM,CAAA;AAAA,EACpC;AACF;;;ACvKO,IAAM,WAAA,GAAc,EAAA;AA2GpB,SAAS,6BACd,YAAA,EACA,SAAA,EACA,WACA,cAAA,GAAiB,KAAA,EACjB,qBAAqB,KAAA,EACG;AACxB,EAAA,MAAM,GAAA,GAAM,YACR,+BAAA,GACA,sBAAA;AAWJ,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,SAAA,CAAU,QAAQ,CAAC,CAAA;AAC7C,EAAA,MAAM,MAAA,GAAS,iBAAiB,qBAAA,GAAwB,CAAA;AACxD,EAAA,MAAM,MAAA,GAAS,qBAAqB,yBAAA,GAA4B,CAAA;AAChE,EAAA,MAAM,QAAA,GAAW,QAAA,GAAW,YAAA,GAAe,WAAA,GAAc,MAAA,GAAS,MAAA;AAClE,EAAA,MAAM,QAAA,GAAW,IAAI,YAAA,CAAa,QAAQ,CAAA;AAC1C,EAAA,QAAA,CAAS,GAAA,CAAI,SAAA,CAAU,MAAA,GAAS,CAAA,GAAI,SAAA,GAAY,IAAI,YAAA,CAAa,CAAC,CAAC,CAAC,CAAC,CAAA;AAGrE,EAAA,OAAO;AAAA;AAAA;AAAA,IAGL,QAAA,EAAU,IAAI,GAAA,CAAI,IAAI,aAAa,YAAA,GAAe,CAAC,GAAG,CAAC,CAAA;AAAA,IACvD,QAAA,EAAU,IAAI,sBAAA,CAAuB,IAAI,aAAa,YAAA,GAAe,CAAC,GAAG,CAAC,CAAA;AAAA,IAC1E,KAAA,EAAO,IAAI,GAAA,CAAI,IAAI,aAAa,YAAA,GAAe,CAAC,GAAG,CAAC,CAAA;AAAA;AAAA,IAEpD,aAAA,EAAe,IAAI,GAAA,CAAI,IAAI,aAAa,YAAA,GAAe,CAAC,GAAG,CAAC,CAAA;AAAA;AAAA,IAE5D,WAAA,EAAa,IAAI,GAAA,CAAI,IAAI,aAAa,YAAA,GAAe,CAAC,GAAG,CAAC,CAAA;AAAA;AAAA,IAE1D,gBAAgB,IAAI,sBAAA;AAAA,MAClB,IAAI,YAAA,CAAa,YAAA,GAAe,CAAC,CAAA;AAAA,MACjC;AAAA,KACF;AAAA;AAAA,IAEA,iBAAiB,IAAI,sBAAA;AAAA,MACnB,IAAI,YAAA,CAAa,YAAA,GAAe,CAAC,CAAA;AAAA,MACjC;AAAA,KACF;AAAA;AAAA,IAEA,SAAA,EAAW,IAAI,sBAAA,CAAuB,QAAA,EAAU,CAAC;AAAA,GACnD;AACF;AAKA,IAAM,WAAA,uBAAkB,OAAA,EAAwC;AAEhE,IAAM,iBAAA,uBAAwB,OAAA,EAAwC;AAEtE,IAAM,mBAAA,uBAA0B,OAAA,EAA0C;AAE1E,IAAM,oBAAA,uBAA2B,OAAA,EAA0C;AAYpE,SAAS,8BAAA,CACd,OAAA,EACA,KAAA,EACA,IAAA,EAoBM;AAEN,EAAA,MAAM,QAAA,GAAW,iBAAA,CAAkB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA,IAAK,CAAA;AAC7D,EAAA,MAAM,GAAA,GAAM,QAAQ,SAAA,CAAU,KAAA;AAC9B,EAAA,MAAM,IAAA,GAAO,WAAW,KAAA,GAAQ,WAAA;AAGhC,EAAA,GAAA,CAAI,IAAI,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC1B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC9B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC9B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,CAAA;AAEhB,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC9B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC9B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,QAAA,CAAS,CAAA;AAC9B,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,CAAA;AAEhB,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,MAAA;AACrB,EAAA,GAAA,CAAI,IAAA,GAAO,CAAC,CAAA,GAAI,IAAA,CAAK,MAAA;AACrB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,MAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,MAAA;AAEtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,CAAA;AACjB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,IAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,QAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,UAAA;AAEtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,aAAA,CAAc,CAAA;AACpC,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,aAAA,CAAc,CAAA;AACpC,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,aAAA,CAAc,CAAA;AACpC,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,CAAA;AAEjB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,aAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,SAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,YAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,WAAA;AAEtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,WAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,WAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,aAAA;AACtB,EAAA,GAAA,CAAI,IAAA,GAAO,EAAE,CAAA,GAAI,IAAA,CAAK,WAAA;AAEtB,EAAA,WAAA,CAAY,GAAA;AAAA,IACV,OAAA,CAAQ,SAAA;AAAA,IAAA,CACP,WAAA,CAAY,GAAA,CAAI,OAAA,CAAQ,SAAS,KAAK,CAAA,IAAK;AAAA,GAC9C;AAIA,EAAA,IAAI,OAAA,GAAU,mBAAA,CAAoB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA;AACvD,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAA,GAAU,EAAC;AACX,IAAA,mBAAA,CAAoB,GAAA,CAAI,OAAA,CAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,EACpD;AACA,EAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAKlB,EAAA,MAAM,KAAK,KAAA,GAAQ,CAAA;AAEnB,EAAA,MAAM,KAAA,GAAQ,QAAQ,WAAA,CAAY,KAAA;AAClC,EAAA,KAAA,CAAM,EAAE,IAAI,IAAA,CAAK,aAAA;AACjB,EAAA,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,SAAA;AACrB,EAAA,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,YAAA;AACrB,EAAA,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,WAAA;AAErB,EAAA,MAAM,MAAA,GAAS,QAAQ,cAAA,CAAe,KAAA;AACtC,EAAA,MAAA,CAAO,EAAE,IAAI,IAAA,CAAK,WAAA;AAClB,EAAA,MAAA,CAAO,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,WAAA;AACtB,EAAA,MAAA,CAAO,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,aAAA;AACtB,EAAA,MAAA,CAAO,EAAA,GAAK,CAAC,CAAA,GAAI,IAAA,CAAK,WAAA;AACxB;AAMO,SAAS,uBAAA,CACd,SACA,eAAA,EACM;AACN,EAAA,iBAAA,CAAkB,GAAA,CAAI,OAAA,CAAQ,SAAA,EAAW,eAAe,CAAA;AAC1D;AASO,SAAS,eAAe,OAAA,EAAyC;AACtE,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA,IAAK,CAAA;AACpD,EAAA,MAAM,QAAA,GAAW,iBAAA,CAAkB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA,IAAK,CAAA;AAC7D,EAAA,MAAM,GAAA,GAAM,QAAQ,SAAA,CAAU,KAAA;AAa9B,EAAA,MAAM,OAAA,GAAU,mBAAA,CAAoB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA;AACzD,EAAA,MAAM,QAAA,GAAW,oBAAA,CAAqB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA;AAE3D,EAAA,IAAI,UAAA,GAAa,KAAA;AAEjB,EAAA,IAAI,QAAA,IAAY,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AACnC,IAAA,MAAM,UAAA,GAAa,WAAW,OAAA,CAAQ,MAAA,GAAS,IAAI,IAAI,GAAA,CAAI,OAAO,CAAA,GAAI,IAAA;AACtE,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,QAAA,CAAS,QAAQ,CAAA,EAAA,EAAK;AACxC,MAAA,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AACpB,MAAA,IAAI,CAAC,UAAA,IAAc,CAAC,UAAA,CAAW,GAAA,CAAI,CAAC,CAAA,EAAG;AACrC,QAAA,MAAM,UAAA,GAAa,QAAA,GAAW,CAAA,GAAI,WAAA,GAAc,CAAA;AAChD,QAAA,IAAI,GAAA,CAAI,UAAU,CAAA,GAAI,GAAA,EAAK;AACzB,UAAA,GAAA,CAAI,UAAU,CAAA,GAAI,CAAA;AAClB,UAAA,UAAA,GAAa,IAAA;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,KAAA,GAAQ,KAAK,UAAA,EAAY;AAC3B,IAAA,OAAA,CAAQ,UAAU,WAAA,GAAc,IAAA;AAAA,EAClC;AAWA,EAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAEjC,IAAA,IAAI,OAAA,GAAU,oBAAA,CAAqB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA;AACxD,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,OAAA,GAAU,EAAC;AACX,MAAA,oBAAA,CAAqB,GAAA,CAAI,OAAA,CAAQ,SAAA,EAAW,OAAO,CAAA;AAAA,IACrD;AACA,IAAA,OAAA,CAAQ,SAAS,OAAA,CAAQ,MAAA;AACzB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACvC,MAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,OAAA,CAAQ,CAAC,CAAA;AAAA,IACxB;AACA,IAAA,OAAA,CAAQ,MAAA,GAAS,CAAA;AAAA,EACnB,CAAA,MAAO;AAEL,IAAA,MAAM,OAAA,GAAU,oBAAA,CAAqB,GAAA,CAAI,OAAA,CAAQ,SAAS,CAAA;AAC1D,IAAA,IAAI,OAAA,UAAiB,MAAA,GAAS,CAAA;AAC9B,IAAA,IAAI,OAAA,UAAiB,MAAA,GAAS,CAAA;AAAA,EAChC;AAEA,EAAA,WAAA,CAAY,GAAA,CAAI,OAAA,CAAQ,SAAA,EAAW,CAAC,CAAA;AACpC,EAAA,OAAO,KAAA;AACT;AASO,SAAS,mCAAA,CACd,UACA,MAAA,EACM;AAKR;AAWA,SAAS,kBAAkB,UAAA,EAAoC;AAC7D,EAAA,OAAOJ,EAAAA;AAAA,IACL,CAAC;AAAA,MACC,UAAA;AAAA,MACA;AAAA,KACF,KAGM;AACJ,MAAA,MAAM,OAAA,GAAUO,GAAA,CAAO,CAAA,EAAGR,KAAAA,CAAM,CAAG,CAAC,CAAA;AACpC,MAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,GAAA,CAAI,gBAAA,GAAmB,CAAC,CAAA;AAC5C,MAAA,MAAM,IAAA,GAAOS,MAAM,GAAG,CAAA;AACtB,MAAA,MAAM,CAAA,GAAI,MAAM,GAAG,CAAA;AAEnB,MAAA,MAAM,IAAA,GAAO,UAAA,CAAW,GAAA,CAAI,gBAAgB,CAAA;AAC5C,MAAA,MAAM,KAAK,UAAA,CAAW,OAAA,CAAQ,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAA;AAC5C,MAAA,MAAM,KAAK,UAAA,CAAW,OAAA;AAAA,QACpB,IAAA,CAAK,GAAA,CAAID,GAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAG,CAAA,EAAGR,KAAAA,CAAM,gBAAA,GAAmB,CAAC,CAAC,CAAC;AAAA,OAC7D;AAEA,MAAA,OAAO,GAAA,CAAI,EAAA,EAAI,EAAA,EAAI,CAAC,CAAA;AAAA,IACtB;AAAA,GACF;AACF;AAQO,SAAS,2BAAA,CACd,SACA,YAAA,EACA,QAAA,EACA,OACA,eAAA,GAAkB,CAAA,EAClB,sBAAsB,CAAA,EACG;AAGzB,EAAA,MAAM,MAAA,GAASD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AAC/B,EAAA,MAAM,QAAA,GAAWD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACjC,EAAA,MAAM,mBAAmBD,OAAAA,CAAQ,IAAI,QAAQ,CAAA,EAAG,CAAA,EAAG,CAAC,CAAC,CAAA;AACrD,EAAA,MAAM,uBAAuBA,OAAAA,CAAQ,IAAI,QAAQ,CAAA,EAAG,CAAA,EAAG,CAAC,CAAC,CAAA;AACzD,EAAA,MAAM,cAAA,GAAiBA,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACvC,EAAA,MAAM,cAAA,GAAiBD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACvC,EAAA,MAAM,WAAA,GAAcD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACpC,EAAA,MAAM,eAAA,GAAkBD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACxC,EAAA,MAAM,eAAA,GAAkBD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACxC,EAAA,MAAM,eAAA,GAAkBD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AACxC,EAAA,MAAM,gBAAA,GAAmBD,OAAAA,CAAQC,KAAAA,CAAM,CAAC,CAAC,CAAA;AAIzC,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,QAAA,EAAU,QAAQ,YAAY,CAAA;AAChE,EAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,OAAA,CAAQ,QAAA,EAAU,QAAQ,YAAY,CAAA;AAChE,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,QAAQ,YAAY,CAAA;AAE1D,EAAA,MAAM,cAAA,GAAiB,OAAA,CAAQ,OAAA,CAAQ,aAAA,EAAe,QAAQ,YAAY,CAAA;AAE1E,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,QAAQ,YAAY,CAAA;AAEtE,EAAA,MAAM,eAAA,GAAkB,OAAA,CAAQ,OAAA,CAAQ,cAAA,EAAgB,QAAQ,YAAY,CAAA;AAE5E,EAAA,MAAM,gBAAA,GAAmB,OAAA;AAAA,IACvB,OAAA,CAAQ,eAAA;AAAA,IACR,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,UAAA,GAAa,OAAA;AAAA,IACjB,OAAA,CAAQ,SAAA;AAAA,IACR,OAAA;AAAA,IACA,OAAA,CAAQ,UAAU,KAAA,CAAM;AAAA,GAC1B;AAGA,EAAA,MAAM,WAAW,IAAA,CAAK,GAAA,CAAI,QAAA,CAAS,IAAA,CAAK,QAAQ,CAAC,CAAA;AAEjD,EAAA,MAAM,WAAA,GAAc,kBAAkB,UAAU,CAAA;AAGhD,EAAA,MAAM,gBAAA,GAAmB,WAAW,YAAA,GAAe,WAAA;AACnD,EAAA,MAAM,kBAAkB,KAAA,CAAM,WAAA,GAC1B,oBAAoB,UAAA,EAAY,gBAAA,EAAkB,eAAe,CAAA,GACjE,IAAA;AAGJ,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,WAAA,GAAc,qBAAA,GAAwB,CAAA;AAC3D,EAAA,MAAM,uBAAuB,gBAAA,GAAmB,MAAA;AAChD,EAAA,MAAM,mBAAA,GAAsB,MAAM,eAAA,GAC9B,uBAAA;AAAA,IACE,UAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF,GACA,IAAA;AAUJ,EAAA,MAAM,aAAA,GAAgBC,GAAG,MAAM;AAC7B,IAAA,MAAM,CAAA,GAAI,aAAA;AAMV,IAAA,MAAM,WAAW,CAAA,CAAE,GAAA,CAAI,WAAW,CAAA,CAAE,IAAI,QAAQ,CAAA;AAEhD,IAAA,MAAM,WAAW,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AACnD,IAAAE,EAAAA,CAAG,QAAA,CAAS,WAAA,CAAY,GAAG,GAAG,MAAM;AAElC,MAAA,SAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCI,IAAAA;AAAA,UACE,UAAA,CAAW,QAAQ,QAAQ,CAAA;AAAA,UAC3B,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC;AAAA;AACF,OACF;AAEF,MAAA,SAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC;AAAA;AACF,OACF;AAEF,MAAA,MAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,CAAC,CAAC,CAAA;AAAA,UAClC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC;AAAA;AACrC,OACF;AAEF,MAAA,cAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC;AAAA;AACrC,OACF;AAEF,MAAA,gBAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC;AAAA;AACrC,OACF;AAEF,MAAA,YAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC;AAAA;AACrC,OACF;AAEF,MAAA,eAAA,CACG,OAAA,CAAQ,CAAC,CAAA,CACT,MAAA;AAAA,QACCA,IAAAA;AAAA,UACE,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,UACnC,UAAA,CAAW,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,EAAE,CAAC;AAAA;AACrC,OACF;AAMF,MAAA,UAAA,CAAW,OAAA,CAAQ,SAAS,GAAA,CAAI,CAAC,CAAC,CAAA,CAAE,MAAA,CAAOP,KAAAA,CAAM,CAAC,CAAC,CAAA;AAAA,IACrD,CAAC,CAAA;AAMD,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM;AACjD,IAAAG,EAAAA,CAAG,OAAO,CAAA,CAAE,gBAAA,CAAiBH,MAAM,GAAG,CAAC,GAAG,MAAM;AAI9C,MAAA,MAAM,MAAM,SAAA,CAAU,OAAA,CAAQ,CAAC,CAAA,CAAE,IAAI,KAAA,EAAM;AAC3C,MAAA,MAAM,MAAM,SAAA,CAAU,OAAA,CAAQ,CAAC,CAAA,CAAE,IAAI,KAAA,EAAM;AAC3C,MAAA,MAAM,EAAA,GAAK,cAAA,CAAe,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM;AAC3C,MAAA,MAAM,EAAA,GAAK,YAAA,CAAa,OAAA,CAAQ,CAAC,CAAA;AAGjC,MAAa,EAAA,CAAG;AAChB,MAAA,MAAM,YAAY,EAAA,CAAG,CAAA;AAKrB,MAAA,GAAA,CAAI,MAAA,CAAO,IAAI,GAAA,CAAIK,IAAAA,CAAK,gBAAgB,CAAA,CAAE,GAAA,CAAI,MAAM,CAAC,CAAC,CAAA;AAGtD,MAAA,IAAI,eAAA,EAAiB;AACnB,QAAA,eAAA,CAAgB,MAAM,EAAE,GAAA,EAAK,GAAA,EAAK,KAAA,EAAO,QAAQ,CAAA;AAAA,MACnD;AAGA,MAAAF,EAAAA,CAAG,cAAA,CAAe,WAAA,CAAY,GAAG,GAAG,MAAM;AACxC,QAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAIE,IAAAA,CAAK,oBAAoB,CAAC,CAAC,CAAA;AAAA,MAChD,CAAC,CAAA;AAGD,MAAA,GAAA,CAAI,OAAO,GAAA,CAAI,GAAA,CAAI,IAAI,GAAA,CAAI,MAAM,CAAC,CAAC,CAAA;AAGnC,MAAA,IAAI,mBAAA,EAAqB;AACvB,QAAA,mBAAA,CAAoB,KAAA,CAAM;AAAA,UACxB,GAAA;AAAA,UACA,GAAA;AAAA,UACA,MAAA;AAAA,UACA,UAAA,EAAY,MAAA;AAAA,UACZ,EAAA;AAAA,UACA,SAAA;AAAA,UACA,WAAA,EAAa,CAAA;AAAA,UACb,oBAAA,EAAsB;AAAA,SACvB,CAAA;AAAA,MACH;AAIA,MAAA,MAAM,OAAA,GAAUG,IAAO,EAAA,CAAG,CAAA,CAAE,IAAI,SAAS,CAAA,EAAGR,KAAAA,CAAM,CAAG,CAAC,CAAA;AAGtD,MAAA,EAAA,CAAG,EAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAA;AAK9B,MAAA,IAAI,MAAM,cAAA,EAAgB;AACxB,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,UAAA,IAAc,CAAA,GACnB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,UAAU,CAAA;AAAA,UACrC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AACf,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,UAAA,IAAc,CAAA,GACnB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,UAAU,CAAA;AAAA,UACrC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AACf,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,UAAA,IAAc,CAAA,GACnB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,UAAU,CAAA;AAAA,UACrC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AACf,QAAA,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,GAAA,CAAIK,IAAAA,CAAK,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAC,CAAC,CAAA;AAAA,MACrD;AAGA,MAAA,IAAI,MAAM,eAAA,EAAiB;AACzB,QAAA,MAAM,MAAA,GAASA,KAAK,MAAA,CAAO,CAAA,EAAG,OAAO,CAAA,EAAG,MAAA,CAAO,CAAC,CAAA,CAAE,KAAA,EAAM;AACxD,QAAA,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAE1B,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,WAAA,IAAe,CAAA,GACpB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYL,KAAAA,CAAM,QAAA,CAAS,WAAW,CAAA;AAAA,UACtC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AACf,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,WAAA,IAAe,CAAA,GACpB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,WAAW,CAAA;AAAA,UACtC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AACf,QAAA,MAAM,GAAA,GACJ,QAAA,CAAS,WAAA,IAAe,CAAA,GACpB,WAAA,CAAY;AAAA,UACV,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,WAAW,CAAA;AAAA,UACtC,CAAA,EAAG;AAAA,SACJ,CAAA,GACDA,KAAAA,CAAM,CAAG,CAAA;AASf,QAAA,MAAM,EAAA,GAAK,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AACzB,QAAA,MAAM,EAAA,GAAK,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AACzB,QAAA,MAAM,EAAA,GAAK,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AAGzB,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,EAAA,GAAK,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,KAAK,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,KAAK,CAAC,CAAA;AACtD,QAAA,MAAM,EAAA,GAAK,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,KAAK,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,KAAK,CAAC,CAAA;AACtD,QAAA,MAAM,KAAK,MAAA,CAAO,CAAA;AAGlB,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,EAAA,GAAK,GAAG,GAAA,CAAI,KAAK,EAAE,GAAA,CAAI,EAAA,CAAG,GAAA,CAAI,KAAK,CAAC,CAAA;AAC1C,QAAA,MAAM,EAAA,GAAK,EAAA;AACX,QAAA,MAAM,EAAA,GAAK,EAAA,CAAG,MAAA,EAAO,CAAE,GAAA,CAAI,KAAK,CAAA,CAAE,GAAA,CAAI,EAAA,CAAG,GAAA,CAAI,KAAK,CAAC,CAAA;AAGnD,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,KAAA,GAAQ,IAAI,EAAE,CAAA;AACpB,QAAA,MAAM,EAAA,GAAK,EAAA;AACX,QAAA,MAAM,EAAA,GAAK,GAAG,GAAA,CAAI,KAAK,EAAE,GAAA,CAAI,EAAA,CAAG,GAAA,CAAI,KAAK,CAAC,CAAA;AAC1C,QAAA,MAAM,EAAA,GAAK,GAAG,GAAA,CAAI,KAAK,EAAE,GAAA,CAAI,EAAA,CAAG,GAAA,CAAI,KAAK,CAAC,CAAA;AAE1C,QAAA,MAAA,CAAO,MAAA,CAAOK,IAAAA,CAAK,EAAA,EAAI,EAAA,EAAI,EAAE,CAAC,CAAA;AAG9B,QAAA,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACxB,QAAA,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACxB,QAAA,MAAA,CAAO,CAAA,CAAE,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA;AACxB,QAAA,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAAA,MAC5B;AAGA,MAAA,IAAI,KAAA,CAAM,gBAAA,IAAoB,QAAA,CAAS,gBAAA,IAAoB,CAAA,EAAG;AAC5D,QAAA,MAAM,aAAa,WAAA,CAAY;AAAA,UAC7B,UAAA,EAAYL,KAAAA,CAAM,QAAA,CAAS,gBAAgB,CAAA;AAAA,UAC3C,CAAA,EAAG;AAAA,SACJ,CAAA;AACD,QAAA,EAAA,CAAG,EAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,UAAU,CAAC,CAAA;AAAA,MAClC;AAGA,MAAA,IAAI,KAAA,CAAM,mBAAA,IAAuB,QAAA,CAAS,mBAAA,IAAuB,CAAA,EAAG;AAClE,QAAA,MAAM,aAAa,WAAA,CAAY;AAAA,UAC7B,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,mBAAmB,CAAA;AAAA,UAC9C,CAAA,EAAG;AAAA,SACJ,CAAA;AACD,QAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM;AACpC,QAAA,GAAA,CAAI,EAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,UAAU,CAAC,CAAA;AACjC,QAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,GAAG,CAAA;AAAA,MAC9B;AAIA,MAAA,IAAI,MAAM,iBAAA,EAAmB;AAC3B,QAAA,MAAM,GAAA,GAAM,MAAA,CAAO,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM;AACpC,QAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAA;AACrC,QAAA,IAAI,QAAA,CAAS,UAAU,CAAA,EAAG;AACxB,UAAA,MAAM,OAAO,WAAA,CAAY;AAAA,YACvB,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,YACjC,CAAA,EAAG;AAAA,WACJ,CAAA;AACD,UAAA,GAAA,CAAI,EAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,QAC7B;AACA,QAAA,IAAI,QAAA,CAAS,UAAU,CAAA,EAAG;AACxB,UAAA,MAAM,OAAO,WAAA,CAAY;AAAA,YACvB,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,YACjC,CAAA,EAAG;AAAA,WACJ,CAAA;AACD,UAAA,GAAA,CAAI,EAAE,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,QAC9B;AACA,QAAA,IAAI,QAAA,CAAS,UAAU,CAAA,EAAG;AACxB,UAAA,MAAM,OAAO,WAAA,CAAY;AAAA,YACvB,UAAA,EAAYA,KAAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,YACjC,CAAA,EAAG;AAAA,WACJ,CAAA;AACD,UAAA,GAAA,CAAI,EAAE,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,QAC9B;AACA,QAAA,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,GAAG,CAAA;AAAA,MAC9B;AAGA,MAAA,IAAI,MAAM,oBAAA,EAAsB;AAC9B,QAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAA;AACrC,QAAA,EAAA,CAAG,CAAA,CAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,IAAI,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,MAAM,EAAE,GAAA,CAAIA,KAAAA,CAAM,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,MAC1D;AAGA,MAAA,IAAI,MAAM,KAAA,EAAO;AACf,QAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,OAAA,CAAQ,CAAC,CAAA;AAErC,QAAA,MAAM,QAAA,GAAW,OAAA,CACd,GAAA,CAAI,GAAA,CAAI,CAAC,CAAA,CACT,GAAA,CAAI,EAAI,CAAA,CACR,GAAA,CAAI,cAAc,CAAA,CAClB,IAAI,eAAe,CAAA;AAEtB,QAAA,MAAM,MAAA,GAAS,QAAA,CAAS,EAAE,CAAA,EAAGK,IAAAA,CAAK,QAAA,EAAUL,KAAAA,CAAM,CAAC,CAAA,EAAGA,KAAAA,CAAM,CAAC,CAAC,GAAG,CAAA;AACjE,QAAA,MAAM,SAAS,QAAA,CAAS;AAAA,UACtB,GAAGK,IAAAA,CAAK,QAAA,EAAU,QAAA,EAAUL,KAAAA,CAAM,CAAC,CAAC;AAAA,SACrC,CAAA;AACD,QAAA,MAAM,SAAS,QAAA,CAAS;AAAA,UACtB,CAAA,EAAGK,IAAAA,CAAK,QAAA,EAAU,QAAA,EAAU,QAAQ;AAAA,SACrC,CAAA;AAGD,QAAA,GAAA,CAAI,MAAA;AAAA,UACF,GAAA,CAAI,GAAA;AAAA,YACFA,IAAAA,CAAK,QAAQ,MAAA,EAAQ,MAAM,EAAE,GAAA,CAAI,WAAW,CAAA,CAAE,GAAA,CAAI,eAAe;AAAA;AACnE,SACF;AAGA,QAAAF,EAAAA,CAAG,eAAA,CAAgB,WAAA,CAAY,IAAK,GAAG,MAAM;AAC3C,UAAA,EAAA,CAAG,CAAA,CAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,WAAW,CAAA,CAAE,GAAA,CAAI,eAAe,CAAC,CAAC,CAAA;AAAA,QACpE,CAAC,CAAA;AAGD,QAAAA,EAAAA,CAAG,gBAAA,CAAiB,WAAA,CAAY,IAAK,GAAG,MAAM;AAC5C,UAAA,EAAA,CAAG,CAAA,CAAE,MAAA,CAAO,EAAA,CAAG,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,WAAW,CAAA,CAAE,GAAA,CAAI,gBAAgB,CAAC,CAAC,CAAA;AAAA,QACrE,CAAC,CAAA;AAAA,MACH;AAIA,MAAA,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,OAAOI,IAAAA,CAAK,GAAA,EAAK,CAAC,CAAC,CAAA;AACxC,MAAA,SAAA,CAAU,QAAQ,CAAC,CAAA,CAAE,OAAOA,IAAAA,CAAK,GAAA,EAAK,CAAC,CAAC,CAAA;AACxC,MAAA,cAAA,CAAe,OAAA,CAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,EAAE,CAAA;AACnC,MAAA,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,MAAM,CAAA;AAGzC,MAAAJ,GAAG,EAAA,CAAG,CAAA,CAAE,WAAA,CAAY,SAAS,GAAG,MAAM;AAEpC,QAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,OAAA,CAAQ,CAAC,EAAE,KAAA,EAAM;AAClD,QAAA,OAAA,CAAQ,CAAA,CAAE,MAAA,CAAOH,KAAAA,CAAM,CAAC,CAAC,CAAA;AACzB,QAAA,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AAC1C,QAAA,MAAA,CAAO,QAAQ,CAAC,CAAA,CAAE,MAAA,CAAOO,IAAAA,CAAK,CAAC,CAAC,CAAA;AAAA,MAClC,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AAED,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,aAAA,EAAc,EAAG,YAAY,CAAA;AAEzD,EAAA,OAAO;AAAA,IACL,WAAA;AAAA,IACA,QAAA,EAAU;AAAA,MACR,KAAA,EAAO,MAAA;AAAA,MACP,OAAA,EAAS,QAAA;AAAA,MACT,eAAA,EAAiB,gBAAA;AAAA,MACjB,mBAAA,EAAqB,oBAAA;AAAA,MACrB,oBAAA,EAAsB,cAAA;AAAA,MACtB,aAAA,EAAe,cAAA;AAAA,MACf,UAAA,EAAY,WAAA;AAAA,MACZ,cAAA,EAAgB,eAAA;AAAA,MAChB,mBAAA,EAAqB,eAAA;AAAA,MACrB,mBAAA,EAAqB,eAAA;AAAA,MACrB,eAAA,EAAiB;AAAA,KACnB;AAAA,IACA,OAAA;AAAA,IACA,eAAA,EAAiB,QAAA;AAAA;AAAA,IAEjB,gBAAgB,eAAA,GACZ;AAAA,MACE,MAAA,EAAQ,gBAAA;AAAA,MACR,cAAc,eAAA,CAAgB;AAAA,KAChC,GACA,IAAA;AAAA;AAAA,IAEJ,oBAAoB,mBAAA,GAChB;AAAA,MACE,MAAA,EAAQ,oBAAA;AAAA,MACR,cAAc,mBAAA,CAAoB;AAAA,KACpC,GACA;AAAA,GACN;AACF;;;AC/6BO,IAAM,gBAAA,GAAmB,GAAA;AAGzB,IAAM,uBAAA,GAA0B,IAAA;AChBvC,IAAI,aAAA,GAAoC,IAAA;AACjC,SAAS,eAAA,GAA+B;AAC7C,EAAA,IAAI,CAAC,aAAA,EAAe;AAClB,IAAA,aAAA,GAAgB,IAAI,WAAA,CAAY,IAAI,UAAA,CAAW,CAAC,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAG,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAC1E,IAAA,aAAA,CAAc,WAAA,GAAc,IAAA;AAAA,EAC9B;AACA,EAAA,OAAO,aAAA;AACT;AAsBO,SAAS,uBAAuB,cAAA,EAAgC;AACrE,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAQ9B,EAAA,MAAM,GAAA,GAAO,cAAA,CAAe,GAAA,CAAI,KAAA,IAAS,KAAA;AACzC,EAAA,IAAI,GAAA,MAAS,UAAA,GAAa,YAAA;AAE1B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,GAAA;AAAA,IACN,UAAUR,OAAAA,CAAQC,KAAAA,CAAM,cAAA,CAAe,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,IACrD,MAAMD,OAAAA,CAAQC,KAAAA,CAAM,cAAA,CAAe,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,IAC7C,oBAAA,EAAsBD,OAAAA;AAAA,MACpBC,KAAAA,CAAM,cAAA,CAAe,mBAAA,CAAoB,KAAA,GAAQ,IAAI,CAAC;AAAA,KACxD;AAAA,IACA,MAAA,EAAQD,OAAAA,CAAQ,cAAA,CAAe,KAAA,CAAM,KAAK,CAAA;AAAA,IAC1C,UAAA,EAAYA,OAAAA;AAAA,MACVC,KAAAA,CAAM,cAAA,CAAe,sBAAA,CAAuB,KAAA,GAAQ,IAAI,CAAC;AAAA,KAC3D;AAAA,IACA,QAAA,EAAUD,OAAAA;AAAA,MACR,IAAIW,OAAAA;AAAA,QACF,cAAA,CAAe,gBAAgB,KAAA,CAAM,CAAA;AAAA,QACrC,cAAA,CAAe,gBAAgB,KAAA,CAAM,CAAA;AAAA,QACrC,cAAA,CAAe,gBAAgB,KAAA,CAAM;AAAA;AACvC,KACF;AAAA,IACA,cAAcX,OAAAA,CAAQC,KAAAA,CAAM,cAAA,CAAe,wBAAA,CAAyB,KAAK,CAAC,CAAA;AAAA,IAC1E,YAAA,EAAcD,OAAAA;AAAA,MACZC,KAAAA,CAAM,cAAA,CAAe,oBAAA,CAAqB,KAAA,GAAQ,IAAI,CAAC;AAAA,KACzD;AAAA,IACA,gBAAgBD,OAAAA,CAAQC,KAAAA,CAAM,cAAA,CAAe,sBAAA,CAAuB,KAAK,CAAC,CAAA;AAAA,IAC1E,cAAA,EAAgB,cAAA,CAAe,iBAAA,CAAkB,KAAA,IAAS,KAAA;AAAA,IAC1D,cAAA,EAAgBD,OAAAA,CAAQ,cAAA,CAAe,aAAA,CAAc,KAAK;AAAA,GAC5D;AACF;AAUO,IAAM,iBAAA,GAAoBE,EAAAA;AAAA,EAC/B,CAAC;AAAA,IACC,SAAA;AAAA,IACA,cAAA;AAAA,IACA,WAAA;AAAA,IACA,IAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA,GACF,KAA8C;AAC5C,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,OAAO,CAAC,CAAA;AACzC,IAAA,MAAM,WAAA,GAAcU,IAAI,SAAA,CAAU,GAAA,CAAI,cAAc,CAAA,EAAGX,KAAAA,CAAM,CAAG,CAAC,CAAA;AAGjE,IAAA,MAAM,QAAA,GAAWY,GAAAA,CAAI,SAAA,CAAU,GAAA,CAAI,GAAM,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA,EAAGZ,KAAAA,CAAM,CAAG,CAAC,CAAA;AAGhE,IAAA,MAAM,aAAA,GAAgBY,GAAAA;AAAA,MACpBD,GAAAA,CAAIF,KAAAA,CAAM,WAAA,CAAY,GAAA,CAAI,WAAW,CAAC,CAAA,EAAG,WAAA,CAAY,GAAA,CAAI,CAAG,CAAC,CAAA;AAAA,MAC7DT,MAAM,CAAG;AAAA,KACX;AAIA,IAAA,MAAM,SAAA,GAAY,KAAK,KAAA,CAAM,CAAG,EAAE,MAAA,CAAOA,KAAAA,CAAM,CAAG,CAAA,EAAG,QAAQ,CAAA;AAC7D,IAAA,MAAM,cAAc,oBAAA,CACjB,WAAA,CAAY,GAAG,CAAA,CACf,MAAA,CAAO,WAAW,aAAa,CAAA;AAElC,IAAA,OAAO,KAAA,CAAM,WAAW,CAAA,CAAE,GAAA,CAAI,WAAW,CAAA;AAAA,EAC3C;AACF,CAAA;AAUO,IAAM,oBAAA,GAAuBC,EAAAA;AAAA,EAClC,CAAC,EAAE,MAAA,EAAQ,UAAA,EAAY,QAAO,KAA8C;AAC1E,IAAA,MAAM,UAAUQ,KAAAA,CAAMI,GAAAA,CAAI,UAAA,EAAY,MAAA,CAAO,CAAC,CAAC,CAAA;AAC/C,IAAA,MAAM,OAAA,GAAUJ,KAAAA,CAAMI,GAAAA,CAAI,UAAA,CAAW,GAAA,CAAI,OAAO,CAAC,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAE7D,IAAA,OAAOC,IAAAA;AAAA,MACL,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA,CAAE,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MAChD,MAAA,CAAO,CAAA,CAAE,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA,CAAE,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,CAAC,CAAC;AAAA,KAClD;AAAA,EACF;AACF,CAAA;AAOO,IAAM,cAAA,GAAiBb,EAAAA;AAAA,EAC5B,CAAC,EAAE,WAAA,EAAa,IAAA,EAAM,KAAI,KAA8C;AACtE,IAAA,MAAM,OAAO,WAAA,CAAY,GAAA,CAAI,CAAG,CAAA,CAAE,IAAI,CAAG,CAAA;AACzC,IAAA,OAAO,IAAA,CACJ,IAAI,CAAG,CAAA,CACP,IAAI,GAAG,CAAA,CACP,IAAI,GAAA,CAAI,GAAA,CAAI,IAAI,CAAA,CAAE,GAAA,CAAI,KAAK,GAAA,CAAI,GAAA,CAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,EACnD;AACF,CAAA;AAMO,IAAM,uBAAA,GAA0BA,EAAAA;AAAA,EACrC,CAAC;AAAA,IACC,KAAA;AAAA,IACA,YAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACF,KAA8C;AAC5C,IAAA,MAAM,QAAA,GAAWD,KAAAA,CAAM,CAAG,CAAA,CAAE,KAAA,EAAM;AAElC,IAAAG,EAAAA,CAAG,YAAA,CAAa,WAAA,CAAY,GAAG,GAAG,MAAM;AACtC,MAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,cAAA,EAAgB,QAAQ,CAAA,CAAE,CAAA;AACtD,MAAA,MAAM,mBAAmB,cAAA,CAAe;AAAA,QACtC,WAAA;AAAA,QACA,MAAM,cAAA,CAAe,CAAA;AAAA,QACrB,KAAK,cAAA,CAAe;AAAA,OACrB,CAAA;AACD,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,GAAA,CAAI,KAAK,CAAA;AAC5C,MAAA,QAAA,CAAS,OAAO,UAAA,CAAWH,KAAAA,CAAM,CAAG,CAAA,EAAG,cAAA,EAAgB,SAAS,CAAC,CAAA;AAAA,IACnE,CAAC,CAAA;AAED,IAAA,OAAO,QAAA;AAAA,EACT;AACF,CAAA;AAOO,IAAM,sBAAA,GAAyBC,EAAAA;AAAA,EACpC,CAAC;AAAA,IACC,QAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF,KAA8C;AAC5C,IAAAE,EAAAA,CAAG,UAAA,CAAW,WAAA,CAAY,GAAG,GAAG,MAAM;AACpC,MAAA,MAAM,IAAA,GAAOE,IAAAA;AAAA,QACX,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,QAAA,CAAS,CAAC,CAAA;AAAA,QACzB,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,QAAA,CAAS,CAAC,CAAA;AAAA,QACzB,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,QAAA,CAAS,CAAC;AAAA,OAC3B;AACA,MAAAF,EAAAA,CAAGY,IAAIC,MAAAA,CAAO,IAAI,CAAC,CAAA,CAAE,QAAA,CAAS,YAAY,CAAA,EAAG,MAAM;AACjD,QAAA,OAAA,EAAQ;AAAA,MACV,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AACF,CAAA;AAgBO,IAAM,oBAAA,GAAuBf,EAAAA;AAAA,EAClC,CAAC,EAAE,KAAA,EAAM,KAA8C;AACrD,IAAA,OAAOM,KAAK,gBAAA,CAAiB,KAAA,CAAM,GAAG,CAAA,EAAG,MAAM,CAAC,CAAA;AAAA,EAClD;AACF,CAAA;;;ACnKO,SAAS,mCAAA,CACd,cAAA,EACA,cAAA,EAMA,UAAA,GAAa,KAAA,EACU;AACvB,EAAA,MAAM,CAAA,GAAI,uBAAuB,cAAc,CAAA;AAU/C,EAAA,MAAM,eAAA,GAAkBR,OAAAA;AAAA,IACtB,OAAQ,cAAA,CAAe,cAAA,EAAuC,UAC5D,QAAA,GACG,cAAA,CAAe,eAAqC,KAAA,GACrD;AAAA,GACN;AAIA,EAAA,cAAA,CAAe,cAAA,GAAiB,eAAA;AAOhC,EAAA,MAAM,eAAA,GAAkB,UAAU,gBAAgB,CAAA;AAElD,EAAA,MAAM,MAAA,GAAS,UAAU,eAAe,CAAA;AAGxC,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAa,SAAA,CAAU,uBAAuB,CAAA,GAAI,IAAA;AACzE,EAAA,MAAM,YAAA,GAAe,UAAA,GAAa,SAAA,CAAU,qBAAqB,CAAA,GAAI,IAAA;AAErE,EAAA,MAAM,KAAA,GAAQ,UAAA,GAAa,IAAA,GAAO,SAAA,CAAU,cAAc,CAAA;AAC1D,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAO,SAAA,CAAU,kBAAkB,CAAA;AAClE,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAa,IAAA,GAAO,SAAA,CAAU,uBAAuB,CAAA;AAC5E,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAO,SAAA,CAAU,kBAAkB,CAAA;AAClE,EAAA,MAAM,WAAA,GAAc,UAAA,GAAa,IAAA,GAAO,SAAA,CAAU,oBAAoB,CAAA;AAItE,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,MAAA,EAAQ,QAAQ,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAY,eAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AACtD,EAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,OAAA,EAAS,gBAAgB,CAAA;AAChE,EAAA,MAAM,SAAA,GAAY,eAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AACtD,EAAA,MAAM,WAAA,GAAc,eAAA,CAAgB,OAAA,EAAS,aAAa,CAAA;AAE1D,EAAA,MAAM,GAAA,GAAM,eAAA,CAAgB,MAAA,EAAQ,KAAK,CAAA;AAEzC,EAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,OAAA,EAAS,QAAQ,CAAA;AA2BhD,EAAA,MAAM,UAAA,GAAaE,GAAG,MAA8B;AAGlD,IAAA,MAAM,UAAUM,IAAAA,CAAK,CAAA,EAAK,GAAK,CAAA,EAAK,CAAG,EAAE,KAAA,EAAM;AAE/C,IAAAJ,GAAG,MAAA,CAAO,CAAA,CAAE,WAAA,CAAY,CAAG,GAAG,MAAM;AAElC,MAAA,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,CAAA;AAC5B,MAAA,IAAI,UAAA,EAAY;AAEd,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAClC,QAAA,cAAA,CAAe,MAAA,CAAO,aAAc,CAAC,CAAA;AACrC,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAClC,QAAA,WAAA,CAAY,MAAA,CAAO,eAAgB,CAAC,CAAA;AAAA,MACtC,CAAA,MAAO;AACL,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAC3B,QAAA,cAAA,CAAe,OAAO,cAAe,CAAA;AACrC,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAC3B,QAAA,WAAA,CAAY,OAAO,WAAY,CAAA;AAAA,MACjC;AAOA,MAAA,GAAA,CAAI,MAAA;AAAA,QACFW,IAAAA,CAAK,aAAA,CAAc,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA,EAAGd,KAAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,aAAA,CAAc,CAAC,CAAC;AAAA,OAChE;AAIA,MAAA,MAAM,UAAA,GAAa,gBAChB,GAAA,CAAIO,IAAAA,CAAK,gBAAgB,GAAA,EAAK,CAAG,CAAC,CAAA,CAClC,KAAA,EAAM;AAQT,MAAA,MAAM,IAAA,GAAOS,MAAAA,CAAO,UAAA,CAAW,GAAG,CAAA;AAClC,MAAA,MAAM,OAAA,GAAU,UAAA,GAAa,cAAA,CAAgB,CAAA,GAAI,KAAA;AACjD,MAAA,MAAM,cAAc,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,CAAE,IAAI,IAAI,CAAA;AAC1D,MAAA,MAAM,QAAQ,sBAAA,CAAuB,OAAA,CAAQ,CAAC,CAAA,CAAE,QAAQ,CAAC,CAAA;AACzD,MAAA,MAAM,eAAA,GAAkB,WAAA,CACrB,GAAA,CAAI,UAAA,CAAW,EAAE,MAAA,EAAQ,CAAA,CACzB,GAAA,CAAI,MAAM,GAAA,CAAI,eAAe,CAAA,CAAE,GAAA,CAAI,GAAG,CAAC,CAAA;AAI1C,MAAA,UAAA,CAAW,EAAE,SAAA,CAAU,aAAA,CAAc,CAAA,CAAE,GAAA,CAAI,eAAe,CAAC,CAAA;AAC3D,MAAA,UAAA,CAAW,EAAE,SAAA,CAAU,aAAA,CAAc,CAAA,CAAE,GAAA,CAAI,eAAe,CAAC,CAAA;AAE3D,MAAA,MAAA,CAAO,MAAA,CAAO,UAAA,CAAW,CAAA,CAAE,MAAA,EAAQ,CAAA;AAGnC,MAAA,OAAA,CAAQ,MAAA,CAAO,sBAAA,CAAuB,GAAA,CAAI,UAAU,CAAC,CAAA;AAAA,IACvD,CAAC,CAAA;AAED,IAAA,OAAO,OAAA;AAAA,EACT,CAAC,CAAA,EAAE;AAcH,EAAA,MAAM,aAAA,GAAgBf,GAAG,MAA8B;AACrD,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,EAAM;AAG9B,IAAA,MAAM,MAAA,GAASa,IAAAA,CAAK,GAAA,EAAK,GAAG,CAAA;AAC5B,IAAA,MAAM,QAAA,GAAW,GAAA,CAAI,GAAA,CAAI,MAAM,CAAA;AAC/B,IAAA,MAAM,IAAA,GAAOG,IAAI,SAAS,CAAA;AAC1B,IAAA,MAAM,IAAA,GAAOC,IAAI,SAAS,CAAA;AAC1B,IAAA,MAAM,OAAA,GAAUJ,IAAAA;AAAA,MACd,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA,CAAE,IAAI,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,MAC7C,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC;AAAA,KACxD;AACA,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AAGpC,IAAA,MAAM,IAAA,GAAOE,MAAAA,CAAO,SAAA,CAAU,GAAA,CAAI,MAAM,CAAC,CAAA;AACzC,IAAAb,EAAAA,CAAG,IAAA,CAAK,WAAA,CAAY,GAAG,GAAG,MAAM;AAC9B,MAAAgB,OAAAA,EAAQ;AAAA,IACV,CAAC,CAAA;AAGD,IAAA,MAAM,aAAa,iBAAA,CAAkB;AAAA,MACnC,SAAA;AAAA,MACA,cAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,sBAAsB,CAAA,CAAE,oBAAA;AAAA,MACxB,QAAQ,CAAA,CAAE;AAAA,KACX,CAAA;AAGD,IAAA,MAAM,UAAU,oBAAA,CAAqB;AAAA,MACnC,MAAA,EAAQ,SAAA;AAAA,MACR,UAAA;AAAA,MACA,QAAQ,CAAA,CAAE;AAAA,KACX,CAAA;AAGD,IAAA,MAAM,QAAA,GAAWC,OAAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,OAAO,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGtC,IAAA,sBAAA,CAAuB;AAAA,MACrB,QAAA;AAAA,MACA,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,cAAc,CAAA,CAAE;AAAA,KACjB,CAAA;AAGD,IAAA,MAAM,WAAW,uBAAA,CAAwB;AAAA,MACvC,KAAA,EAAO,MAAA;AAAA,MACP,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE;AAAA,KACnB,CAAA;AACD,IAAA,QAAA,CAAS,MAAA,CAAOb,KAAK,QAAA,CAAS,GAAA,EAAK,SAAS,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAC,CAAA;AAC5D,IAAAY,OAAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAC,CAAA;AAEpD,IAAA,OAAO,oBAAA,CAAqB,EAAE,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,EACjD,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,QAAA,GAAW,IAAI,qBAAA,EAAsB;AAC3C,EAAA,QAAA,CAAS,cAAc,cAAA,CAAe,WAAA;AACtC,EAAA,QAAA,CAAS,WAAW,cAAA,CAAe,QAAA;AACnC,EAAA,QAAA,CAAS,YAAY,cAAA,CAAe,SAAA;AACpC,EAAA,QAAA,CAAS,aAAa,cAAA,CAAe,UAAA;AACrC,EAAA,QAAA,CAAS,UAAA,GAAa,KAAA;AACtB,EAAA,QAAA,CAAS,GAAA,GAAM,KAAA;AAKf,EAAA,QAAA,CAAS,UAAA,GAAa,UAAA;AACtB,EAAA,QAAA,CAAS,SAAA,GAAY,aAAA;AAErB,EAAA,OAAO,QAAA;AACT;AC1QA,IAAM,eAAA,GAAkBlB,EAAAA;AAAA,EACtB,CAAC,EAAE,CAAA,EAAG,CAAA,EAAE,KAA8C;AACpD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,CAAE,KAAK,CAAC,CAAA,CAAE,IAAI,CAAG,CAAA;AACjC,IAAA,OAAO,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,CAAC,CAAC,CAAA,CAAE,GAAA,CAAI,KAAA,CAAM,CAAA,CAAE,GAAA,EAAK,CAAC,CAAC,CAAA;AAAA,EAC9C;AACF,CAAA;AAaO,SAAS,6BAAA,CACd,cAAA,EACA,cAAA,EAMA,UAAA,GAAa,KAAA,EACU;AACvB,EAAA,MAAM,CAAA,GAAI,uBAAuB,cAAc,CAAA;AAK/C,EAAA,MAAM,eAAA,GAAkBoB,UAAU,gBAAgB,CAAA;AAElD,EAAA,MAAM,MAAA,GAASA,UAAU,eAAe,CAAA;AAGxC,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAaA,SAAAA,CAAU,uBAAuB,CAAA,GAAI,IAAA;AACzE,EAAA,MAAM,YAAA,GAAe,UAAA,GAAaA,SAAAA,CAAU,qBAAqB,CAAA,GAAI,IAAA;AAErE,EAAA,MAAM,aAAA,GAAgB,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,cAAc,CAAA;AAClE,EAAA,MAAM,KAAA,GAAQ,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,cAAc,CAAA;AAC1D,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,kBAAkB,CAAA;AAClE,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,uBAAuB,CAAA;AAC5E,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,kBAAkB,CAAA;AAClE,EAAA,MAAM,WAAA,GAAc,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,oBAAoB,CAAA;AAItE,EAAA,MAAM,MAAA,GAASC,eAAAA,CAAgB,MAAA,EAAQ,QAAQ,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAYA,eAAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AACtD,EAAA,MAAM,cAAA,GAAiBA,eAAAA,CAAgB,OAAA,EAAS,gBAAgB,CAAA;AAChE,EAAA,MAAM,WAAA,GAAcA,eAAAA,CAAgB,OAAA,EAAS,aAAa,CAAA;AAC1D,EAAA,MAAM,SAAA,GAAYA,eAAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AAEtD,EAAA,MAAM,OAAA,GAAUA,eAAAA,CAAgB,MAAA,EAAQ,SAAS,CAAA;AAEjD,EAAA,MAAM,MAAA,GAASA,eAAAA,CAAgB,OAAA,EAAS,QAAQ,CAAA;AAWhD,EAAA,MAAM,WAAA,GAAcrB,GAAG,MAAM;AAK3B,IAAA,MAAM,UAAUM,IAAAA,CAAK,CAAA,EAAK,GAAK,CAAA,EAAK,CAAG,EAAE,KAAA,EAAM;AAE/C,IAAAJ,GAAG,MAAA,CAAO,CAAA,CAAE,WAAA,CAAY,CAAG,GAAG,MAAM;AAElC,MAAA,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,CAAA;AAC5B,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAClC,QAAA,cAAA,CAAe,MAAA,CAAO,aAAc,CAAC,CAAA;AACrC,QAAA,WAAA,CAAY,MAAA,CAAO,eAAgB,CAAC,CAAA;AACpC,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAAA,MACpC,CAAA,MAAO;AACL,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAC3B,QAAA,cAAA,CAAe,OAAO,cAAe,CAAA;AACrC,QAAA,WAAA,CAAY,OAAO,WAAY,CAAA;AAC/B,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAAA,MAC7B;AAIA,MAAA,IAAI,IAAA;AACJ,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,MAAM,KAAA,GAAQ,cAAA,CAAgB,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA;AACvC,QAAA,IAAA,GAAOI,IAAAA,CAAK,GAAK,CAAA,EAAKW,GAAAA,CAAI,KAAK,CAAA,EAAGD,GAAAA,CAAI,KAAK,CAAC,CAAA;AAAA,MAC9C,CAAA,MAAO;AACL,QAAA,IAAA,GAAO,aAAA;AAAA,MACT;AAGA,MAAA,MAAM,aAAa,eAAA,CAAgB;AAAA,QACjC,CAAA,EAAGM,aAAAA;AAAA,QACH,CAAA,EAAG;AAAA,OACJ,CAAA;AAGD,MAAA,MAAM,YAAY,UAAA,CAAW,GAAA,CAAI,UAAA,GAAa,cAAA,CAAgB,IAAI,KAAM,CAAA;AAIxE,MAAA,MAAM,QAAA,GAAW,SAAA,CAAU,GAAA,CAAI,eAAA,CAAgB,GAAG,CAAA;AAGlD,MAAA,MAAM,QAAQC,eAAAA,CAAgB,GAAA,CAAIjB,IAAAA,CAAK,QAAA,EAAU,CAAG,CAAC,CAAA;AACrD,MAAA,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,CAAA;AAG9B,MAAA,MAAM,gBAAgB,eAAA,CAAgB;AAAA,QACpC,CAAA,EAAG,WAAA;AAAA,QACH,CAAA,EAAG;AAAA,OACJ,CAAA;AACD,MAAA,MAAM,WAAWiB,eAAAA,CAAgB,GAAA,CAAIjB,KAAK,aAAA,EAAe,CAAG,CAAC,CAAA,CAAE,GAAA;AAC/D,MAAA,OAAA,CAAQ,MAAA,CAAO,QAAA,CAAS,SAAA,EAAW,CAAA;AAEnC,MAAA,OAAA,CAAQ,MAAA,CAAOkB,sBAAAA,CAAuB,GAAA,CAAI,KAAK,CAAC,CAAA;AAAA,IAClD,CAAC,CAAA;AAGD,IAAA,OAAO,OAAA;AAAA,EACT,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,aAAA,GAAgBxB,GAAG,MAAM;AAC7B,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,EAAM;AAG9B,IAAA,MAAM,OAAA,GAAUa,IAAAA,CAAK,EAAA,EAAI,EAAE,KAAA,EAAM;AAGjC,IAAAX,EAAAA,CAAG,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,YAAY,CAAG,CAAA,CAAE,EAAA,CAAG,CAAA,CAAE,OAAO,CAAA,CAAE,WAAA,CAAY,CAAG,CAAC,GAAG,MAAM;AACpE,MAAA,MAAM,aAAa,iBAAA,CAAkB;AAAA,QACnC,SAAA;AAAA,QACA,cAAA;AAAA,QACA,WAAA;AAAA,QACA,MAAM,CAAA,CAAE,IAAA;AAAA,QACR,sBAAsB,CAAA,CAAE,oBAAA;AAAA,QACxB,QAAQ,CAAA,CAAE;AAAA,OACX,CAAA;AAGD,MAAA,OAAA,CAAQ,MAAA;AAAA,QACN,oBAAA,CAAqB;AAAA,UACnB,QAAQ,EAAA,EAAG;AAAA,UACX,UAAA;AAAA,UACA,QAAQ,CAAA,CAAE;AAAA,SACX;AAAA,OACH;AAAA,IACF,CAAC,CAAA;AAGD,IAAA,MAAM,QAAA,GAAWiB,OAAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,OAAO,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGtC,IAAA,sBAAA,CAAuB;AAAA,MACrB,QAAA;AAAA,MACA,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,cAAc,CAAA,CAAE;AAAA,KACjB,CAAA;AAID,IAAA,MAAM,cAAA,GAAiBpB,KAAAA,CAAM,GAAG,CAAA,CAAE,GAAA;AAAA,MAChCA,MAAM,GAAG,CAAA,CAAE,GAAA,CAAIY,GAAAA,CAAIN,IAAI,OAAA,EAASD,IAAAA,CAAK,CAAA,EAAK,CAAA,EAAK,CAAG,CAAC,CAAA,EAAGL,KAAAA,CAAM,CAAG,CAAC,CAAC;AAAA,KACnE;AACA,IAAA,QAAA,CAAS,MAAA,CAAOO,KAAK,QAAA,CAAS,GAAA,CAAI,IAAI,cAAc,CAAA,EAAG,QAAA,CAAS,CAAC,CAAC,CAAA;AAGlE,IAAA,MAAM,WAAW,uBAAA,CAAwB;AAAA,MACvC,KAAA,EAAO,MAAA;AAAA,MACP,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE;AAAA,KACnB,CAAA;AACD,IAAA,QAAA,CAAS,MAAA,CAAOA,KAAK,QAAA,CAAS,GAAA,EAAK,SAAS,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAC,CAAA;AAC5D,IAAAY,OAAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAC,CAAA;AAEpD,IAAA,OAAO,oBAAA,CAAqB,EAAE,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,EACjD,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,QAAA,GAAW,IAAIO,qBAAAA,EAAsB;AAC3C,EAAA,QAAA,CAAS,cAAc,cAAA,CAAe,WAAA;AACtC,EAAA,QAAA,CAAS,WAAW,cAAA,CAAe,QAAA;AACnC,EAAA,QAAA,CAAS,YAAY,cAAA,CAAe,SAAA;AACpC,EAAA,QAAA,CAAS,aAAa,cAAA,CAAe,UAAA;AACrC,EAAA,QAAA,CAAS,UAAA,GAAa,KAAA;AACtB,EAAA,QAAA,CAAS,GAAA,GAAM,KAAA;AAGf,EAAA,QAAA,CAAS,UAAA,GAAa,WAAA;AACtB,EAAA,QAAA,CAAS,SAAA,GAAY,aAAA;AAErB,EAAA,OAAO,QAAA;AACT;ACrOO,SAAS,4BAAA,CACd,cAAA,EACA,cAAA,EAMA,UAAA,GAAa,KAAA,EACO;AACpB,EAAA,MAAM,CAAA,GAAI,uBAAuB,cAAc,CAAA;AAG/C,EAAA,MAAM,MAAA,GAASL,UAAU,OAAO,CAAA;AAGhC,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAaA,SAAAA,CAAU,eAAe,CAAA,GAAI,IAAA;AACjE,EAAA,MAAM,YAAA,GAAe,UAAA,GAAaA,SAAAA,CAAU,aAAa,CAAA,GAAI,IAAA;AAE7D,EAAA,MAAM,KAAA,GAAQ,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,MAAM,CAAA;AAClD,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,UAAU,CAAA;AAC1D,EAAA,MAAM,cAAA,GAAiB,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,eAAe,CAAA;AACpE,EAAA,MAAM,SAAA,GAAY,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,UAAU,CAAA;AAC1D,EAAA,MAAM,WAAA,GAAc,UAAA,GAAa,IAAA,GAAOA,SAAAA,CAAU,YAAY,CAAA;AAK9D,EAAA,MAAM,QAAQG,eAAAA,CAAgB,GAAA,CAAIjB,IAAAA,CAAKgB,aAAAA,EAAe,CAAG,CAAC,CAAA;AAC1D,EAAA,MAAM,OAAA,GAAU,UAAA,GAAa,cAAA,CAAgB,CAAA,GAAI,KAAA;AACjD,EAAA,MAAM,WAAW,MAAA,CAAO,CAAA,CACrB,YAAY,CAAG,CAAA,CACf,OAAO,OAAA,CAAQ,GAAA,CAAI,gBAAgB,CAAA,CAAE,GAAA,CAAIP,OAAO,KAAA,CAAM,GAAG,CAAC,CAAA,EAAGhB,KAAAA,CAAM,CAAG,CAAC,CAAA;AAG1E,EAAA,MAAM,MAAA,GAASsB,eAAAA,CAAgB,MAAA,EAAQ,QAAQ,CAAA;AAC/C,EAAA,MAAM,SAAA,GAAYA,eAAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AACtD,EAAA,MAAM,cAAA,GAAiBA,eAAAA,CAAgB,OAAA,EAAS,gBAAgB,CAAA;AAChE,EAAA,MAAM,SAAA,GAAYA,eAAAA,CAAgB,OAAA,EAAS,WAAW,CAAA;AACtD,EAAA,MAAM,WAAA,GAAcA,eAAAA,CAAgB,OAAA,EAAS,aAAa,CAAA;AAC1D,EAAA,MAAM,MAAA,GAASA,eAAAA,CAAgB,OAAA,EAAS,QAAQ,CAAA;AAGhD,EAAA,MAAM,WAAA,GAAcrB,GAAG,MAAM;AAG3B,IAAAE,GAAG,MAAA,CAAO,CAAA,CAAE,WAAA,CAAY,CAAG,GAAG,MAAM;AAClC,MAAA,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,CAAA;AAC5B,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAClC,QAAA,cAAA,CAAe,MAAA,CAAO,aAAc,CAAC,CAAA;AACrC,QAAA,SAAA,CAAU,MAAA,CAAO,eAAgB,CAAC,CAAA;AAClC,QAAA,WAAA,CAAY,MAAA,CAAO,eAAgB,CAAC,CAAA;AAAA,MACtC,CAAA,MAAO;AACL,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAC3B,QAAA,cAAA,CAAe,OAAO,cAAe,CAAA;AACrC,QAAA,SAAA,CAAU,OAAO,SAAU,CAAA;AAC3B,QAAA,WAAA,CAAY,OAAO,WAAY,CAAA;AAAA,MACjC;AACA,MAAA,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,CAAA;AAAA,IAChC,CAAC,CAAA;AACD,IAAA,OAAOoB,aAAAA;AAAA,EACT,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,aAAA,GAAgBtB,GAAG,MAAM;AAE7B,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,EAAM;AAG9B,IAAA,MAAM,aAAa,iBAAA,CAAkB;AAAA,MACnC,SAAA;AAAA,MACA,cAAA;AAAA,MACA,WAAA;AAAA,MACA,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,sBAAsB,CAAA,CAAE,oBAAA;AAAA,MACxB,QAAQ,CAAA,CAAE;AAAA,KACX,CAAA;AAGD,IAAA,MAAM,MAAA,GAASa,IAAAA,CAAK,GAAA,EAAK,GAAG,CAAA;AAC5B,IAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AACnC,IAAA,MAAM,IAAA,GAAOG,IAAI,SAAS,CAAA;AAC1B,IAAA,MAAM,IAAA,GAAOC,IAAI,SAAS,CAAA;AAC1B,IAAA,MAAM,OAAA,GAAUJ,IAAAA;AAAA,MACd,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA,CAAE,IAAI,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC,CAAA;AAAA,MAC7C,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,IAAI,CAAC;AAAA,KACxD;AACA,IAAA,MAAM,SAAA,GAAY,OAAA,CAAQ,GAAA,CAAI,MAAM,CAAA;AAGpC,IAAA,MAAM,IAAA,GAAOE,MAAAA,CAAO,SAAA,CAAU,GAAA,CAAI,MAAM,CAAC,CAAA;AACzC,IAAAG,OAAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,GAAG,CAAC,CAAA;AAG7B,IAAA,MAAM,UAAU,oBAAA,CAAqB;AAAA,MACnC,MAAA,EAAQ,SAAA;AAAA,MACR,UAAA;AAAA,MACA,QAAQ,CAAA,CAAE;AAAA,KACX,CAAA;AAGD,IAAA,MAAM,QAAA,GAAWC,OAAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,OAAO,CAAA;AACxC,IAAA,QAAA,CAAS,MAAA,CAAO,QAAA,CAAS,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGtC,IAAA,sBAAA,CAAuB;AAAA,MACrB,QAAA;AAAA,MACA,YAAY,CAAA,CAAE,UAAA;AAAA,MACd,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,cAAc,CAAA,CAAE;AAAA,KACjB,CAAA;AAGD,IAAA,MAAM,WAAW,uBAAA,CAAwB;AAAA,MACvC,KAAA,EAAO,MAAA;AAAA,MACP,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE,cAAA;AAAA,MAClB,gBAAgB,CAAA,CAAE;AAAA,KACnB,CAAA;AACD,IAAA,QAAA,CAAS,MAAA,CAAOb,KAAK,QAAA,CAAS,GAAA,EAAK,SAAS,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAC,CAAA;AAC5D,IAAAY,OAAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAC,CAAA;AAEpD,IAAA,OAAO,oBAAA,CAAqB,EAAE,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,EACjD,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,QAAA,GAAW,IAAI,kBAAA,EAAmB;AACxC,EAAA,QAAA,CAAS,cAAc,cAAA,CAAe,WAAA;AACtC,EAAA,QAAA,CAAS,WAAW,cAAA,CAAe,QAAA;AACnC,EAAA,QAAA,CAAS,YAAY,cAAA,CAAe,SAAA;AACpC,EAAA,QAAA,CAAS,aAAa,cAAA,CAAe,UAAA;AACrC,EAAA,QAAA,CAAS,UAAA,GAAa,KAAA;AACtB,EAAA,QAAA,CAAS,GAAA,GAAM,KAAA;AACf,EAAA,QAAA,CAAS,QAAA,GAAW,QAAA;AACpB,EAAA,QAAA,CAAS,YAAA,GAAe,WAAA;AACxB,EAAA,QAAA,CAAS,SAAA,GAAY,aAAA;AAErB,EAAA,OAAO,QAAA;AACT;AC5GA,SAAS,oBAAoB,aAAA,EAA8B;AACzD,EAAA,MAAM,QAAQ,eAAA,EAAgB;AAE9B,EAAA,MAAM,GAAA,GAAO,aAAA,CAAc,GAAA,CAAI,KAAA,IAAS,KAAA;AACxC,EAAA,IAAI,GAAA,MAAS,UAAA,GAAaQ,YAAAA;AAE1B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,GAAA;AAAA,IACN,OAAA,EAAS5B,QAAQC,KAAAA,CAAM,aAAA,CAAc,OAAO,KAAA,GAAQ,CAAA,GAAI,CAAC,CAAC,CAAA;AAAA,IAC1D,UAAA,EAAYD,OAAAA;AAAA,MACVC,KAAAA,CAAM,aAAA,CAAc,sBAAA,CAAuB,KAAA,GAAQ,IAAI,CAAC;AAAA,KAC1D;AAAA,IACA,QAAA,EAAUD,OAAAA;AAAA,MACR,IAAK,aAAA,CAAc,aAAA,CAAc,KAAA,CAAM,WAAA;AAAA,QAKrC,aAAA,CAAc,gBAAgB,KAAA,CAAM,CAAA;AAAA,QACpC,aAAA,CAAc,gBAAgB,KAAA,CAAM,CAAA;AAAA,QACpC,aAAA,CAAc,gBAAgB,KAAA,CAAM;AAAA;AACtC,KACF;AAAA,IACA,cAAcA,OAAAA,CAAQC,KAAAA,CAAM,aAAA,CAAc,wBAAA,CAAyB,KAAK,CAAC,CAAA;AAAA,IACzE,YAAA,EAAcD,OAAAA;AAAA,MACZC,KAAAA,CAAM,aAAA,CAAc,oBAAA,CAAqB,KAAA,GAAQ,IAAI,CAAC;AAAA,KACxD;AAAA,IACA,gBAAgBD,OAAAA,CAAQC,KAAAA,CAAM,aAAA,CAAc,sBAAA,CAAuB,KAAK,CAAC,CAAA;AAAA,IACzE,cAAA,EAAgB,aAAA,CAAc,iBAAA,CAAkB,KAAA,IAAS,KAAA;AAAA,IACzD,cAAA,EAAgBD,OAAAA,CAAQ,aAAA,CAAc,aAAA,CAAc,KAAK;AAAA,GAC3D;AACF;AAqBO,SAAS,4BAAA,CACd,eACA,cAAA,EAMuB;AACvB,EAAA,MAAM,CAAA,GAAI,oBAAoB,aAAa,CAAA;AAI3C,EAAA,MAAM,WAAA,GAAcsB,UAAU,YAAY,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAcA,SAAAA,CAAU,YAAA,EAAc,MAAM,CAAA;AAClD,EAAA,MAAM,YAAA,GAAeA,UAAU,aAAa,CAAA;AAC5C,EAAA,MAAM,eAAA,GAAkBA,UAAU,gBAAgB,CAAA;AAClD,EAAA,MAAM,UAAA,GAAaA,SAAAA,CAAU,WAAA,EAAa,MAAM,CAAA;AAChD,EAAA,MAAM,QAAA,GAAWA,SAAAA,CAAU,SAAA,EAAW,MAAM,CAAA;AAI5C,EAAA,MAAM,MAAA,GAASC,eAAAA,CAAgB,OAAA,EAAS,QAAQ,CAAA;AAChD,EAAA,MAAM,MAAA,GAASA,eAAAA,CAAgB,MAAA,EAAQ,QAAQ,CAAA;AAC/C,EAAA,MAAM,GAAA,GAAMA,eAAAA,CAAgB,MAAA,EAAQ,KAAK,CAAA;AACzC,EAAA,MAAM,MAAA,GAASA,eAAAA,CAAgB,OAAA,EAAS,QAAQ,CAAA;AAahD,EAAA,MAAM,YAAA,GAAerB,GAAG,MAA8B;AAEpD,IAAA,MAAA,CAAO,OAAO,WAAW,CAAA;AACzB,IAAA,MAAA,CAAO,OAAO,WAAW,CAAA;AACzB,IAAA,GAAA,CAAI,OAAO,QAAQ,CAAA;AAEnB,IAAA,MAAM,OAAA,GAAUI,KAAKkB,aAAa,CAAA;AAClC,IAAA,MAAM,IAAA,GAAOlB,KAAK,UAAU,CAAA;AAG5B,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA;AACnC,IAAA,MAAM,UAAA,GAAaW,OAAO,UAAU,CAAA;AAEpC,IAAA,MAAM,OAAA,GAAUY,SAAAA;AAAA,MACd,UAAA,CAAW,QAAA,CAAS,IAAM,CAAA,CAAE,MAAA,CAAOvB,KAAK,CAAA,EAAK,CAAA,EAAK,CAAG,CAAA,EAAG,UAAU;AAAA,KACpE;AAGA,IAAkBmB,eAAAA,CAAgB,GAAA,CAAIjB,IAAAA,CAAK,OAAA,EAAS,CAAG,CAAC;AAGxD,IAAA,MAAM,OAAA,GAAUqB,SAAAA,CAAU,cAAA,CAAe,GAAA,CAAI,OAAO,CAAC,CAAA;AAGrD,IAAA,MAAM,OAAA,GAAUC,KAAAA,CAAM,OAAA,EAAS,OAAO,CAAA;AACtC,IAAA,MAAM,OAAA,GAAUb,OAAO,OAAO,CAAA;AAI9B,IAAA,MAAM,QAAA,GAAWX,IAAAA;AAAA,MACf,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,MACrC,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,MACrC,gBAAA,CAAiB,OAAA,CAAQ,CAAC,CAAA,CAAE,QAAQ,CAAC;AAAA,KACvC;AAGA,IAAA,MAAM,kBAAA,GAAqBC,GAAAA,CAAI,QAAA,EAAU,OAAO,CAAA;AAChD,IAAA,MAAM,YAAA,GAAesB,SAAAA;AAAA,MACnB,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,kBAAkB,CAAC;AAAA,KAC9C;AAKA,IAAA,MAAM,IAAA,GAAOA,SAAAA;AAAA,MACX,OAAA,CACG,QAAA,CAAS,IAAM,CAAA,CACf,MAAA;AAAA,QACC,YAAA;AAAA,QACAA,SAAAA;AAAA,UACEE,GAAAA;AAAA,YACE,YAAA;AAAA,YACAF,UAAU,OAAO,CAAA;AAAA,YACjBG,WAAW/B,KAAAA,CAAM,CAAG,GAAGA,KAAAA,CAAM,GAAG,GAAG,OAAO;AAAA;AAC5C;AACF;AACF,KACJ;AAGA,IAAA,MAAM,SAAA,GAAY,QAAQ,GAAA,CAAI,IAAA,CAAK,IAAI,YAAY,CAAA,CAAE,GAAA,CAAI,eAAe,CAAC,CAAA;AAGzE,IAAA,MAAM,WAAWwB,eAAAA,CAAgB,GAAA,CAAIjB,IAAAA,CAAK,SAAA,EAAW,CAAG,CAAC,CAAA;AACzD,IAAA,MAAA,CAAO,MAAA,CAAO,QAAA,CAAS,CAAA,CAAE,MAAA,EAAQ,CAAA;AAKjC,IAAA,OAAO,SAAA;AAAA,EACT,CAAC,CAAA,EAAE;AAUH,EAAA,MAAM,SAAA,GAAYN,GAAG,MAA8B;AACjD,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,EAAM;AAI9B,IAAA,MAAM,QAAA,GAAWD,KAAAA,CAAM,CAAG,CAAA,CAAE,IAAIe,GAAAA,CAAI,GAAA,CAAI,CAAA,CAAE,GAAA,CAAI,CAAG,CAAA,CAAE,GAAA,CAAI,CAAG,CAAC,CAAC,CAAA;AAC5D,IAAA,MAAM,QAAA,GAAWgB,WAAW/B,KAAAA,CAAM,CAAG,GAAGA,KAAAA,CAAM,GAAG,GAAG,QAAQ,CAAA;AAI5D,IAAAG,GAAG,CAAA,CAAE,OAAA,CAAQ,WAAA,CAAY,GAAG,GAAG,MAAM;AACnC,MAAA,MAAM,QAAA,GAAWiB,OAAAA,CAAQ,CAAA,CAAE,IAAA,EAAM,GAAG,CAAA;AACpC,MAAA,MAAM,aAAA,GAAgBd,IAAI,QAAA,CAAS,GAAA,EAAKD,KAAK,KAAA,EAAO,KAAA,EAAO,KAAK,CAAC,CAAA;AACjE,MAAA,QAAA,CAAS,GAAA,CAAI,MAAA;AAAA,QACX,QAAA,CAAS,GAAA,CAAI,GAAA,CAAIL,KAAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,aAAA,CAAc,GAAA,CAAI,GAAG,CAAC,CAAC;AAAA,OACzD;AACA,MAAA,QAAA,CAAS,EAAE,MAAA,CAAO,QAAA,CAAS,EAAE,GAAA,CAAI,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,IAC9C,CAAC,CAAA;AAGD,IAAA,QAAA,CAAS,CAAA,CAAE,OAAO,QAAA,CAAS,CAAA,CAAE,IAAI,MAAM,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAA;AAGtD,IAAAmB,OAAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAC,CAAA;AAGpD,IAAAhB,GAAG,CAAA,CAAE,YAAA,CAAa,WAAA,CAAY,GAAG,GAAG,MAAM;AACxC,MAAA,MAAM,WAAA,GAAciB,OAAAA,CAAQ,CAAA,CAAE,cAAA,EAAgBY,QAAQ,CAAA,CAAE,CAAA;AACxD,MAAA,MAAM,mBAAmB,cAAA,CAAe;AAAA,QACtC,WAAA;AAAA,QACA,IAAA,EAAM,EAAE,cAAA,CAAe,CAAA;AAAA,QACvB,GAAA,EAAK,EAAE,cAAA,CAAe;AAAA,OACvB,CAAA;AACD,MAAA,MAAM,SAAA,GAAY,gBAAA,CAAiB,GAAA,CAAI,MAAM,CAAA;AAC7C,MAAA,MAAM,WAAWD,UAAAA,CAAW/B,KAAAA,CAAM,CAAG,CAAA,EAAG,CAAA,CAAE,gBAAgB,SAAS,CAAA;AACnE,MAAA,QAAA,CAAS,EAAE,MAAA,CAAO,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,QAAQ,CAAC,CAAA;AAAA,IAC5C,CAAC,CAAA;AACD,IAAAmB,OAAAA,CAAQ,QAAA,CAAS,CAAA,CAAE,QAAA,CAAS,uBAAuB,CAAC,CAAA;AAGpD,IAAA,MAAM,IAAA,GAAOd,IAAAA;AAAA,MACX,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,MAC3B,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,SAAS,CAAC,CAAA;AAAA,MAC3B,QAAA,CAAS,CAAA,CAAE,GAAA,CAAI,CAAA,CAAE,SAAS,CAAC;AAAA,KAC7B;AACA,IAAAc,OAAAA;AAAA,MACE,CAAA,CAAE,UAAA,CACC,WAAA,CAAY,GAAG,EACf,GAAA,CAAIJ,GAAAA,CAAIC,MAAAA,CAAO,IAAI,CAAC,CAAA,CAAE,QAAA,CAAS,CAAA,CAAE,YAAY,CAAC;AAAA,KACnD;AAEA,IAAA,OAAO,oBAAA,CAAqB,EAAE,KAAA,EAAO,QAAA,EAAU,CAAA;AAAA,EACjD,CAAC,CAAA,EAAE;AAIH,EAAA,MAAM,QAAA,GAAW,IAAIU,qBAAAA,EAAsB;AAC3C,EAAA,QAAA,CAAS,cAAc,cAAA,CAAe,WAAA;AACtC,EAAA,QAAA,CAAS,WAAW,cAAA,CAAe,QAAA;AACnC,EAAA,QAAA,CAAS,YAAY,cAAA,CAAe,SAAA;AACpC,EAAA,QAAA,CAAS,aAAa,cAAA,CAAe,UAAA;AACrC,EAAA,QAAA,CAAS,UAAA,GAAa,KAAA;AACtB,EAAA,QAAA,CAAS,GAAA,GAAM,KAAA;AACf,EAAA,QAAA,CAAS,IAAA,GAAO,UAAA;AAGhB,EAAA,QAAA,CAAS,YAAA,GAAe,YAAA;AACxB,EAAA,QAAA,CAAS,SAAA,GAAY,SAAA;AAErB,EAAA,OAAO,QAAA;AACT;;;AC5QO,SAAS,yBAAA,CACd,YAAA,EACA,cAAA,EACA,cAAA,EACA,aAAa,KAAA,EACG;AAChB,EAAA,QAAQ,YAAA;AAAc,IACpB,KAAA,WAAA;AACE,MAAA,OAAO,mCAAA;AAAA,QACL,cAAA;AAAA,QACA,cAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,KAAA,MAAA;AACE,MAAA,OAAO,6BAAA;AAAA,QACL,cAAA;AAAA,QACA,cAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF,KAAA,QAAA;AAAA,IACA;AACE,MAAA,OAAO,4BAAA;AAAA,QACL,cAAA;AAAA,QACA,cAAA;AAAA,QACA;AAAA,OACF;AAAA;AAEN;AASO,SAAS,sBAAA,CACd,eACA,cAAA,EACgB;AAChB,EAAA,OAAO,4BAAA,CAA6B,eAAe,cAAc,CAAA;AACnE;AAiBO,SAAS,sBACd,YAAA,EACA,SAAA,EACA,kBACA,gBAAA,EACA,eAAA,EACA,sBAAsB,CAAA,EACG;AACzB,EAAA,MAAM,WAAA,GAAc,wBAAA;AAAA,IAClB,gBAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,EAAE,sBAAqB,GAAI,gBAAA;AAEjC,EAAA,MAAM,KAAA,GAAuB;AAAA,IAC3B,gBAAA,EAAkB,iBAAiB,gBAAA,CAAiB,QAAA;AAAA,IACpD,mBAAA,EAAqB,iBAAiB,mBAAA,CAAoB,QAAA;AAAA,IAC1D,iBAAA,EAAmB,iBAAiB,iBAAA,CAAkB,QAAA;AAAA,IACtD,oBAAA,EAAsB,iBAAiB,oBAAA,CAAqB,QAAA;AAAA,IAC5D,cAAA,EACE,oBAAA,CAAqB,QAAA,KACpB,eAAA,CAAgB,oBAAA,CAAqB,MAAA,CAAO,CAAA,IAAK,CAAC,CAAA,IACjD,eAAA,CAAgB,oBAAA,CAAqB,MAAA,CAAO,KAAK,CAAC,CAAA,IAClD,eAAA,CAAgB,oBAAA,CAAqB,MAAA,CAAO,CAAA,IAAK,CAAC,CAAA,IAClD,qBAAqB,MAAA,CAAO,CAAA,KAAM,CAAA,IAClC,oBAAA,CAAqB,MAAA,CAAO,CAAA,KAAM,CAAA,IAClC,oBAAA,CAAqB,OAAO,CAAA,KAAM,CAAA,CAAA;AAAA,IACtC,eAAA,EACE,oBAAA,CAAqB,QAAA,KACpB,eAAA,CAAgB,oBAAA,CAAqB,OAAA,CAAQ,CAAA,IAAK,CAAC,CAAA,IAClD,eAAA,CAAgB,oBAAA,CAAqB,OAAA,CAAQ,KAAK,CAAC,CAAA,IACnD,eAAA,CAAgB,oBAAA,CAAqB,OAAA,CAAQ,CAAA,IAAK,CAAC,CAAA,IACnD,qBAAqB,OAAA,CAAQ,CAAA,KAAM,CAAA,IACnC,oBAAA,CAAqB,OAAA,CAAQ,CAAA,KAAM,CAAA,IACnC,oBAAA,CAAqB,QAAQ,CAAA,KAAM,CAAA,CAAA;AAAA,IACvC,KAAA,EAAO,iBAAiB,KAAA,CAAM,QAAA;AAAA,IAC9B,aAAa,eAAA,GAAkB,CAAA;AAAA,IAC/B,iBAAiB,mBAAA,GAAsB;AAAA,GACzC;AAEA,EAAA,MAAM,OAAA,GAAU,4BAAA;AAAA,IACd,YAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA,CAAY,IAAA;AAAA,IACZ,KAAA,CAAM,WAAA;AAAA,IACN,KAAA,CAAM;AAAA,GACR;AAEA,EAAA,OAAO,2BAAA;AAAA,IACL,OAAA;AAAA,IACA,YAAA;AAAA,IACA,WAAA;AAAA,IACA,KAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF;AACF;;;AChIO,SAAS,YAAA,GAAqB;AAMnC,EAAA,MAAM,OAAA,GAA4D;AAAA,IAChE,yBAAA;AAAA,IACA,sBAAA;AAAA,IACA,qBAAA;AAAA,IACA,8BAAA;AAAA,IACA,mCAAA;AAAA,IACA,cAAA;AAAA,IACA,uBAAA;AAAA,IACA,uBAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,0BAAA,CAA2B,OAAO,CAAA;AACpC","file":"webgpu.js","sourcesContent":["/**\n * GPU collision plane computation for particle systems.\n *\n * Encodes collision plane configurations into a flat Float32Array that can be\n * written into the shared curveData storage buffer (avoiding an extra\n * storage buffer binding that would exceed the WebGPU per-stage limit of 8).\n *\n * Provides a TSL helper function that iterates over the encoded planes\n * and applies collision responses (kill, clamp, bounce) to particles.\n *\n * Collision plane modes:\n * - KILL (0): deactivate the particle immediately\n * - CLAMP (1): project position onto plane, zero velocity along normal\n * - BOUNCE (2): reflect velocity with damping, project position\n *\n * @module\n */\nimport {\n Fn,\n float,\n vec3,\n vec4,\n uniform,\n If,\n Loop,\n Continue,\n dot,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\n\nimport { CollisionPlaneMode } from '../three-particles-enums.js';\nimport type { NormalizedCollisionPlaneConfig } from '../types.js';\n\n// ─── Encoding Layout ─────────────────────────────────────────────────────────\n\n/**\n * Per-plane stride in the packed Float32Array.\n *\n * Layout per collision plane (12 floats):\n * [0] isActive (0 or 1)\n * [1] mode (0 = KILL, 1 = CLAMP, 2 = BOUNCE)\n * [2-4] position (x, y, z)\n * [5-7] normal (x, y, z) — normalized\n * [8] dampen (0–1)\n * [9] lifetimeLoss (0–1)\n * [10-11] padding (reserved)\n */\nconst PLANE_STRIDE = 12;\n\n/** Maximum collision planes supported per particle system. */\nexport const MAX_COLLISION_PLANES = 16;\n\n/** Total floats reserved for collision plane data in the curveData buffer. */\nexport const COLLISION_PLANE_DATA_SIZE = MAX_COLLISION_PLANES * PLANE_STRIDE;\n\n/** Pre-allocated encoding buffer, reused every frame to avoid GC pressure. */\nlet _encodeBuf: Float32Array | null = null;\n\n// ─── CPU-side Encoding ───────────────────────────────────────────────────────\n\n/**\n * Packs an array of collision plane configs into a flat Float32Array for GPU upload.\n *\n * @param planes - Normalized collision plane configs from the particle system.\n * @returns A Float32Array of `MAX_COLLISION_PLANES * PLANE_STRIDE` floats.\n */\nexport function encodeCollisionPlanesForGPU(\n planes: ReadonlyArray<NormalizedCollisionPlaneConfig>\n): Float32Array {\n if (!_encodeBuf || _encodeBuf.length !== COLLISION_PLANE_DATA_SIZE) {\n _encodeBuf = new Float32Array(COLLISION_PLANE_DATA_SIZE);\n }\n const data = _encodeBuf;\n data.fill(0);\n\n const count = Math.min(planes.length, MAX_COLLISION_PLANES);\n for (let i = 0; i < count; i++) {\n const cp = planes[i];\n const base = i * PLANE_STRIDE;\n\n data[base] = cp.isActive ? 1 : 0;\n\n let modeCode = 0;\n if (cp.mode === CollisionPlaneMode.CLAMP) modeCode = 1;\n else if (cp.mode === CollisionPlaneMode.BOUNCE) modeCode = 2;\n data[base + 1] = modeCode;\n\n data[base + 2] = cp.position.x;\n data[base + 3] = cp.position.y;\n data[base + 4] = cp.position.z;\n data[base + 5] = cp.normal.x;\n data[base + 6] = cp.normal.y;\n data[base + 7] = cp.normal.z;\n data[base + 8] = cp.dampen;\n data[base + 9] = cp.lifetimeLoss;\n data[base + 10] = 0; // padding\n data[base + 11] = 0; // padding\n }\n\n return data;\n}\n\n// ─── TSL Collision Plane Application ─────────────────────────────────────────\n\n/**\n * Creates the TSL uniform and helper function for applying collision planes\n * in a GPU compute shader.\n *\n * Collision plane data is read from the shared curveData storage buffer at a\n * fixed offset, avoiding an additional storage buffer binding.\n *\n * @param sCurveData - The shared curveData storage node.\n * @param collisionPlaneOffset - Float offset into curveData where collision plane data starts.\n * @param collisionPlaneCount - Number of active collision planes (0 to MAX_COLLISION_PLANES).\n * @returns Object with the count uniform and the TSL apply function.\n */\nexport function createCollisionPlaneTSL(\n sCurveData: ShaderNodeObject<Node>,\n collisionPlaneOffset: number,\n collisionPlaneCount: number\n) {\n const count = Math.min(collisionPlaneCount, MAX_COLLISION_PLANES);\n const uCollisionPlaneCount = uniform(float(count));\n const cpBase = collisionPlaneOffset;\n\n /**\n * TSL function that applies all collision planes to a particle.\n *\n * @param pos - Current particle position (vec3, modified in place)\n * @param vel - Current particle velocity (vec3, modified in place)\n * @param oiaVec - orbitalIsActive vec4 (w = isActive, modified for KILL)\n * @param sColor - Color storage node (modified for KILL)\n * @param ps - particleState vec4 (x = lifetime, modified for lifetime loss)\n * @param startLife - Start lifetime scalar (for lifetime loss)\n * @param i - Particle index (for storage buffer element access)\n * @param sOrbitalIsActive - orbitalIsActive storage node (for KILL)\n */\n const applyCollisionPlanesTSL = Fn(\n ({\n pos,\n vel,\n oiaVec,\n sColorNode,\n ps,\n startLife,\n particleIdx,\n sOrbitalIsActiveNode,\n }: {\n pos: ShaderNodeObject<Node>;\n vel: ShaderNodeObject<Node>;\n oiaVec: ShaderNodeObject<Node>;\n sColorNode: ShaderNodeObject<Node>;\n ps: ShaderNodeObject<Node>;\n startLife: ShaderNodeObject<Node>;\n particleIdx: ShaderNodeObject<Node>;\n sOrbitalIsActiveNode: ShaderNodeObject<Node>;\n }) => {\n Loop(uCollisionPlaneCount, ({ i }: { i: ShaderNodeObject<Node> }) => {\n const base = i.mul(PLANE_STRIDE).add(cpBase);\n\n const isActive = sCurveData.element(base);\n If(isActive.lessThan(0.5), () => {\n Continue();\n });\n\n const mode = sCurveData.element(base.add(1));\n const planePos = vec3(\n sCurveData.element(base.add(2)),\n sCurveData.element(base.add(3)),\n sCurveData.element(base.add(4))\n );\n const planeNormal = vec3(\n sCurveData.element(base.add(5)),\n sCurveData.element(base.add(6)),\n sCurveData.element(base.add(7))\n );\n const dampen = sCurveData.element(base.add(8));\n const lifetimeLoss = sCurveData.element(base.add(9));\n\n // Signed distance from particle to plane\n const toParticle = pos.sub(planePos);\n const signedDist = dot(toParticle, planeNormal);\n\n // Only respond when particle is on the wrong side (signedDist < 0)\n If(signedDist.lessThan(0.0), () => {\n // KILL mode (mode < 0.5)\n // Set lifetime far past startLifetime so the death check at the end\n // of the kernel deactivates the particle and zeroes color AFTER all\n // modifiers have run (modifiers would otherwise overwrite our color).\n If(mode.lessThan(0.5), () => {\n ps.x.assign(startLife.add(float(1.0)));\n });\n\n // CLAMP mode (mode >= 0.5 && mode < 1.5)\n If(mode.greaterThanEqual(0.5).and(mode.lessThan(1.5)), () => {\n // Project position onto plane surface\n pos.assign(pos.sub(planeNormal.mul(signedDist)));\n\n // Remove velocity component along normal (only if moving into the plane)\n const velDotN = dot(vel, planeNormal);\n If(velDotN.lessThan(0.0), () => {\n vel.assign(vel.sub(planeNormal.mul(velDotN)));\n });\n });\n\n // BOUNCE mode (mode >= 1.5)\n If(mode.greaterThanEqual(1.5), () => {\n // Project position onto plane surface\n pos.assign(pos.sub(planeNormal.mul(signedDist)));\n\n // Reflect velocity: v' = (v - 2 * dot(v, n) * n) * dampen\n const vDotN = dot(vel, planeNormal);\n const reflected = vel.sub(planeNormal.mul(vDotN.mul(2.0)));\n vel.assign(reflected.mul(dampen));\n\n // Apply lifetime loss\n If(lifetimeLoss.greaterThan(0.0), () => {\n ps.x.assign(ps.x.add(lifetimeLoss.mul(startLife).mul(1000.0)));\n });\n });\n });\n });\n },\n 'void'\n );\n\n return {\n /** Uniform for the active collision plane count. */\n countUniform: uCollisionPlaneCount,\n /** TSL function to call in the compute kernel: apply({ pos, vel, ... }) */\n apply: applyCollisionPlanesTSL,\n };\n}\n","import { BezierPoint, CurveFunction } from './types.js';\n\nconst cache: Array<{\n bezierPoints: Array<BezierPoint>;\n curveFunction: CurveFunction;\n referencedBy: Array<number>;\n}> = [];\n\nconst nCr = (n: number, k: number) => {\n let z = 1;\n for (let i = 1; i <= k; i++) z *= (n + 1 - i) / i;\n return z;\n};\n\nexport const createBezierCurveFunction = (\n particleSystemId: number,\n bezierPoints: Array<BezierPoint>\n) => {\n const cacheEntry = cache.find((item) => item.bezierPoints === bezierPoints);\n\n if (cacheEntry) {\n if (!cacheEntry.referencedBy.includes(particleSystemId))\n cacheEntry.referencedBy.push(particleSystemId);\n return cacheEntry.curveFunction;\n }\n\n const entry = {\n referencedBy: [particleSystemId],\n bezierPoints,\n curveFunction: (percentage: number): number => {\n if (percentage < 0) return bezierPoints[0].y;\n if (percentage > 1) return bezierPoints[bezierPoints.length - 1].y;\n\n let start = 0;\n let stop = bezierPoints.length - 1;\n\n bezierPoints.find((point, index) => {\n const result = percentage < (point.percentage ?? 0);\n if (result) stop = index;\n else if (point.percentage !== undefined) start = index;\n return result;\n });\n\n const n = stop - start;\n const calculatedPercentage =\n (percentage - (bezierPoints[start].percentage ?? 0)) /\n ((bezierPoints[stop].percentage ?? 1) -\n (bezierPoints[start].percentage ?? 0));\n\n let value = 0;\n for (let i = 0; i <= n; i++) {\n const p = bezierPoints[start + i];\n const c =\n nCr(n, i) *\n Math.pow(1 - calculatedPercentage, n - i) *\n Math.pow(calculatedPercentage, i);\n value += c * p.y;\n }\n return value;\n },\n };\n\n cache.push(entry);\n return entry.curveFunction;\n};\n\nexport const removeBezierCurveFunction = (particleSystemId: number) => {\n while (true) {\n const index = cache.findIndex((item) =>\n item.referencedBy.includes(particleSystemId)\n );\n if (index === -1) break;\n const entry = cache[index];\n entry.referencedBy = entry.referencedBy.filter(\n (id) => id !== particleSystemId\n );\n if (entry.referencedBy.length === 0) cache.splice(index, 1);\n }\n};\n\nexport const getBezierCacheSize = () => cache.length;\n","import * as THREE from 'three';\n\nimport { createBezierCurveFunction } from './three-particles-bezier.js';\nimport { EmitFrom, LifeTimeCurve } from './three-particles-enums.js';\nimport {\n Constant,\n LifetimeCurve,\n Point3D,\n RandomBetweenTwoConstants,\n} from './types.js';\n\n/**\n * Calculates random position and velocity for particles emitted from a sphere.\n *\n * Supports emission from the entire volume or just the shell of the sphere.\n * Uses spherical coordinates for uniform distribution across the surface.\n *\n * @param position - Output vector for the particle's starting position\n * @param quaternion - Rotation to apply to the emission shape\n * @param velocity - Output vector for the particle's initial velocity\n * @param speed - Speed multiplier for the velocity\n * @param params - Sphere configuration\n * @param params.radius - Radius of the sphere\n * @param params.radiusThickness - Controls emission from volume (1.0) vs shell (0.0)\n * @param params.arc - Arc angle in degrees (360 = full sphere, 180 = hemisphere)\n *\n * @remarks\n * - `radiusThickness = 1.0`: Emit from entire volume\n * - `radiusThickness = 0.0`: Emit only from surface shell\n * - Particles are emitted radially outward from the center\n *\n * @see {@link Sphere} - Configuration type for sphere shape\n */\nexport const calculateRandomPositionAndVelocityOnSphere = (\n position: THREE.Vector3,\n quaternion: THREE.Quaternion,\n velocity: THREE.Vector3,\n speed: number,\n {\n radius,\n radiusThickness,\n arc,\n }: { radius: number; radiusThickness: number; arc: number }\n) => {\n const u = Math.random() * (arc / 360);\n const v = Math.random();\n const randomizedDistanceRatio = Math.random();\n const theta = 2 * Math.PI * u;\n const phi = Math.acos(2 * v - 1);\n const sinPhi = Math.sin(phi);\n\n const xDirection = sinPhi * Math.cos(theta);\n const yDirection = sinPhi * Math.sin(theta);\n const zDirection = Math.cos(phi);\n const normalizedThickness = 1 - radiusThickness;\n\n position.x =\n radius * normalizedThickness * xDirection +\n radius * radiusThickness * randomizedDistanceRatio * xDirection;\n position.y =\n radius * normalizedThickness * yDirection +\n radius * radiusThickness * randomizedDistanceRatio * yDirection;\n position.z =\n radius * normalizedThickness * zDirection +\n radius * radiusThickness * randomizedDistanceRatio * zDirection;\n\n position.applyQuaternion(quaternion);\n\n const speedMultiplierByPosition = 1 / position.length();\n velocity.set(\n position.x * speedMultiplierByPosition * speed,\n position.y * speedMultiplierByPosition * speed,\n position.z * speedMultiplierByPosition * speed\n );\n velocity.applyQuaternion(quaternion);\n};\n\n/**\n * Calculates random position and velocity for particles emitted from a cone.\n *\n * Useful for directional particle effects like fire, smoke plumes, fountains,\n * or spray effects. The cone emits particles in a spreading pattern.\n *\n * @param position - Output vector for the particle's starting position\n * @param quaternion - Rotation to apply to the emission shape\n * @param velocity - Output vector for the particle's initial velocity\n * @param speed - Speed multiplier for the velocity\n * @param params - Cone configuration\n * @param params.radius - Base radius of the cone\n * @param params.radiusThickness - Controls emission from volume (1.0) vs shell (0.0)\n * @param params.arc - Arc angle in degrees (360 = full cone, 180 = half cone)\n * @param params.angle - Cone opening angle in degrees (default: 90)\n * Smaller values create tighter cones\n *\n * @remarks\n * - The cone emits from its base (circular area) outward\n * - Particles travel in a conical spread pattern\n * - `angle = 0`: Straight line (no spread)\n * - `angle = 90`: Wide cone\n * - Common for fire (10-30°), smoke (30-60°), explosions (60-90°)\n *\n * @see {@link Cone} - Configuration type for cone shape\n */\nexport const calculateRandomPositionAndVelocityOnCone = (\n position: THREE.Vector3,\n quaternion: THREE.Quaternion,\n velocity: THREE.Vector3,\n speed: number,\n {\n radius,\n radiusThickness,\n arc,\n angle = 90,\n }: {\n radius: number;\n radiusThickness: number;\n arc: number;\n angle?: number;\n }\n) => {\n const theta = 2 * Math.PI * Math.random() * (arc / 360);\n const randomizedDistanceRatio = Math.random();\n\n const xDirection = Math.cos(theta);\n const yDirection = Math.sin(theta);\n const normalizedThickness = 1 - radiusThickness;\n\n position.x =\n radius * normalizedThickness * xDirection +\n radius * radiusThickness * randomizedDistanceRatio * xDirection;\n position.y =\n radius * normalizedThickness * yDirection +\n radius * radiusThickness * randomizedDistanceRatio * yDirection;\n position.z = 0;\n\n position.applyQuaternion(quaternion);\n\n const positionLength = position.length();\n const normalizedAngle = Math.abs(\n (positionLength / radius) * THREE.MathUtils.degToRad(angle)\n );\n const sinNormalizedAngle = Math.sin(normalizedAngle);\n\n const speedMultiplierByPosition = 1 / positionLength;\n velocity.set(\n position.x * sinNormalizedAngle * speedMultiplierByPosition * speed,\n position.y * sinNormalizedAngle * speedMultiplierByPosition * speed,\n Math.cos(normalizedAngle) * speed\n );\n velocity.applyQuaternion(quaternion);\n};\n\n/**\n * Calculates random position and velocity for particles emitted from a box.\n *\n * Supports three emission modes: volume, shell (surface), and edges.\n * Useful for area-based effects like dust clouds, rain, or geometric patterns.\n *\n * @param position - Output vector for the particle's starting position\n * @param quaternion - Rotation to apply to the emission shape\n * @param velocity - Output vector for the particle's initial velocity\n * @param speed - Speed multiplier for the velocity\n * @param params - Box configuration\n * @param params.scale - Size of the box on each axis (width, height, depth)\n * @param params.emitFrom - Emission mode:\n * - `VOLUME`: Random positions throughout the entire box volume\n * - `SHELL`: Random positions on the 6 faces (surface)\n * - `EDGE`: Random positions along the 12 edges\n *\n * @remarks\n * - All particles emit with velocity along the +Z axis (forward)\n * - Box is centered at the origin before rotation\n * - VOLUME mode: Best for rain, snow, or volumetric clouds\n * - SHELL mode: Best for hollow effects or surface particles\n * - EDGE mode: Best for wireframe effects or particle outlines\n *\n * @see {@link Box} - Configuration type for box shape\n * @see {@link EmitFrom} - Emission mode enum\n */\nexport const calculateRandomPositionAndVelocityOnBox = (\n position: THREE.Vector3,\n quaternion: THREE.Quaternion,\n velocity: THREE.Vector3,\n speed: number,\n { scale, emitFrom }: { scale: Point3D; emitFrom: EmitFrom }\n) => {\n const _scale = scale as Required<Point3D>;\n switch (emitFrom) {\n case EmitFrom.VOLUME:\n position.x = Math.random() * _scale.x - _scale.x / 2;\n position.y = Math.random() * _scale.y - _scale.y / 2;\n position.z = Math.random() * _scale.z - _scale.z / 2;\n break;\n\n case EmitFrom.SHELL:\n const side = Math.floor(Math.random() * 6);\n const perpendicularAxis = side % 3;\n const shellResult = [];\n shellResult[perpendicularAxis] = side > 2 ? 1 : 0;\n shellResult[(perpendicularAxis + 1) % 3] = Math.random();\n shellResult[(perpendicularAxis + 2) % 3] = Math.random();\n position.x = shellResult[0] * _scale.x - _scale.x / 2;\n position.y = shellResult[1] * _scale.y - _scale.y / 2;\n position.z = shellResult[2] * _scale.z - _scale.z / 2;\n break;\n\n case EmitFrom.EDGE:\n const side2 = Math.floor(Math.random() * 6);\n const perpendicularAxis2 = side2 % 3;\n const edge = Math.floor(Math.random() * 4);\n const edgeResult = [];\n edgeResult[perpendicularAxis2] = side2 > 2 ? 1 : 0;\n edgeResult[(perpendicularAxis2 + 1) % 3] =\n edge < 2 ? Math.random() : edge - 2;\n edgeResult[(perpendicularAxis2 + 2) % 3] =\n edge < 2 ? edge : Math.random();\n position.x = edgeResult[0] * _scale.x - _scale.x / 2;\n position.y = edgeResult[1] * _scale.y - _scale.y / 2;\n position.z = edgeResult[2] * _scale.z - _scale.z / 2;\n break;\n }\n\n position.applyQuaternion(quaternion);\n\n velocity.set(0, 0, speed);\n velocity.applyQuaternion(quaternion);\n};\n\n/**\n * Calculates random position and velocity for particles emitted from a circle.\n *\n * Emits particles from a circular area or ring. Useful for ground impacts,\n * radial effects, magic circles, or any circular planar emission.\n *\n * @param position - Output vector for the particle's starting position\n * @param quaternion - Rotation to apply to the emission shape\n * @param velocity - Output vector for the particle's initial velocity\n * @param speed - Speed multiplier for the velocity\n * @param params - Circle configuration\n * @param params.radius - Radius of the circle\n * @param params.radiusThickness - Controls emission from area (1.0) vs edge (0.0)\n * @param params.arc - Arc angle in degrees (360 = full circle, 180 = semicircle)\n *\n * @remarks\n * - Circle lies in the XY plane by default (Z = 0)\n * - Particles emit along the +Z axis (perpendicular to circle)\n * - `radiusThickness = 1.0`: Filled circle (disc)\n * - `radiusThickness = 0.0`: Ring (circle edge only)\n * - Good for ground impact effects, teleport circles, or radial bursts\n *\n * @see {@link Circle} - Configuration type for circle shape\n */\nexport const calculateRandomPositionAndVelocityOnCircle = (\n position: THREE.Vector3,\n quaternion: THREE.Quaternion,\n velocity: THREE.Vector3,\n speed: number,\n {\n radius,\n radiusThickness,\n arc,\n }: { radius: number; radiusThickness: number; arc: number }\n) => {\n const theta = 2 * Math.PI * Math.random() * (arc / 360);\n const randomizedDistanceRatio = Math.random();\n\n const xDirection = Math.cos(theta);\n const yDirection = Math.sin(theta);\n const normalizedThickness = 1 - radiusThickness;\n\n position.x =\n radius * normalizedThickness * xDirection +\n radius * radiusThickness * randomizedDistanceRatio * xDirection;\n position.y =\n radius * normalizedThickness * yDirection +\n radius * radiusThickness * randomizedDistanceRatio * yDirection;\n position.z = 0;\n\n position.applyQuaternion(quaternion);\n\n const positionLength = position.length();\n const speedMultiplierByPosition = 1 / positionLength;\n velocity.set(\n position.x * speedMultiplierByPosition * speed,\n position.y * speedMultiplierByPosition * speed,\n 0\n );\n velocity.applyQuaternion(quaternion);\n};\n\n/**\n * Calculates random position and velocity for particles emitted from a rectangle.\n *\n * Emits particles from a rectangular planar area. Useful for rain on a surface,\n * screen-space effects, or any planar emission pattern.\n *\n * @param position - Output vector for the particle's starting position\n * @param quaternion - Rotation to apply to the emission shape\n * @param velocity - Output vector for the particle's initial velocity\n * @param speed - Speed multiplier for the velocity\n * @param params - Rectangle configuration\n * @param params.rotation - Local rotation of the rectangle (degrees) before\n * applying quaternion\n * @param params.scale - Size of the rectangle (width and height)\n *\n * @remarks\n * - Rectangle lies in the XY plane by default\n * - Particles emit along the +Z axis (perpendicular to rectangle)\n * - The rotation parameter allows tilting the rectangle before the main\n * quaternion rotation is applied\n * - Good for rain effects, screen particles, or planar area emissions\n *\n * @see {@link Rectangle} - Configuration type for rectangle shape\n */\nexport const calculateRandomPositionAndVelocityOnRectangle = (\n position: THREE.Vector3,\n quaternion: THREE.Quaternion,\n velocity: THREE.Vector3,\n speed: number,\n { rotation, scale }: { rotation: Point3D; scale: Point3D }\n) => {\n const _scale = scale as Required<Point3D>;\n const _rotation = rotation as Required<Point3D>;\n\n const xOffset = Math.random() * _scale.x - _scale.x / 2;\n const yOffset = Math.random() * _scale.y - _scale.y / 2;\n const rotationX = THREE.MathUtils.degToRad(_rotation.x);\n const rotationY = THREE.MathUtils.degToRad(_rotation.y);\n position.x = xOffset * Math.cos(rotationY);\n position.y = yOffset * Math.cos(rotationX);\n position.z = xOffset * Math.sin(rotationY) - yOffset * Math.sin(rotationX);\n\n position.applyQuaternion(quaternion);\n\n velocity.set(0, 0, speed);\n velocity.applyQuaternion(quaternion);\n};\n\n/**\n * Creates a solid white 1x1 texture for mesh particles.\n * Unlike the circle texture used by point/billboard renderers, mesh particles\n * need a neutral texture so the geometry shape is visible.\n * @returns {THREE.CanvasTexture | null} The generated texture or null if context fails.\n */\nexport const createDefaultMeshTexture = (): THREE.CanvasTexture | null => {\n try {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n const context = canvas.getContext('2d');\n if (context) {\n context.fillStyle = 'white';\n context.fillRect(0, 0, 1, 1);\n const texture = new THREE.CanvasTexture(canvas);\n texture.needsUpdate = true;\n return texture;\n }\n return null;\n } catch {\n return null;\n }\n};\n\n/**\n * Creates a default white circle texture using CanvasTexture.\n * @returns {THREE.CanvasTexture | null} The generated texture or null if context fails.\n */\nexport const createDefaultParticleTexture = (): THREE.CanvasTexture | null => {\n try {\n const canvas = document.createElement('canvas');\n const size = 64;\n canvas.width = size;\n canvas.height = size;\n const context = canvas.getContext('2d');\n if (context) {\n const centerX = size / 2;\n const centerY = size / 2;\n const radius = size / 2 - 2; // Small padding\n\n context.beginPath();\n context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);\n context.fillStyle = 'white';\n context.fill();\n const texture = new THREE.CanvasTexture(canvas);\n texture.needsUpdate = true;\n return texture;\n } else {\n // eslint-disable-next-line no-console\n console.warn(\n 'Could not get 2D context to generate default particle texture.'\n );\n return null;\n }\n } catch (error) {\n // Handle potential errors (e.g., document not available in non-browser env)\n // eslint-disable-next-line no-console\n console.warn('Error creating default particle texture:', error);\n return null;\n }\n};\n\nexport const isLifeTimeCurve = (\n value: Constant | RandomBetweenTwoConstants | LifetimeCurve\n): value is LifetimeCurve => {\n return typeof value !== 'number' && 'type' in value;\n};\n\nexport const getCurveFunctionFromConfig = (\n particleSystemId: number,\n lifetimeCurve: LifetimeCurve\n) => {\n if (lifetimeCurve.type === LifeTimeCurve.BEZIER) {\n return createBezierCurveFunction(\n particleSystemId,\n lifetimeCurve.bezierPoints\n ); // Bézier curve\n }\n\n if (lifetimeCurve.type === LifeTimeCurve.EASING) {\n return lifetimeCurve.curveFunction; // Easing curve\n }\n\n throw new Error(`Unsupported value type: ${lifetimeCurve}`);\n};\n\nexport const calculateValue = (\n particleSystemId: number,\n value: Constant | RandomBetweenTwoConstants | LifetimeCurve,\n time: number = 0\n): number => {\n if (typeof value === 'number') {\n return value; // Constant value\n }\n\n if ('min' in value && 'max' in value) {\n if (value.min === value.max) {\n return value.min ?? 0; // Constant value\n }\n return THREE.MathUtils.randFloat(value.min ?? 0, value.max ?? 1); // Random range\n }\n\n const lifetimeCurve = value as LifetimeCurve;\n return (\n getCurveFunctionFromConfig(particleSystemId, lifetimeCurve)(time) *\n (lifetimeCurve.scale ?? 1)\n );\n};\n","/**\n * GPU force field computation for particle systems.\n *\n * Encodes force field configurations into a flat Float32Array that can be\n * written into the shared curveData storage buffer (avoiding an extra\n * storage buffer binding that would exceed the WebGPU per-stage limit of 8).\n *\n * Provides a TSL helper function that iterates over the encoded fields\n * and applies forces to particle velocity.\n *\n * Force field types:\n * - POINT: radial attract/repel toward a position with distance falloff\n * - DIRECTIONAL: constant force along a direction vector\n *\n * Falloff modes (POINT only):\n * - NONE: full strength regardless of distance\n * - LINEAR: 1 - (d / range)\n * - QUADRATIC: 1 - (d / range)²\n *\n * @module\n */\nimport {\n Fn,\n float,\n vec3,\n uniform,\n If,\n Loop,\n Continue,\n normalize,\n length,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\n\nimport { ForceFieldFalloff, ForceFieldType } from '../three-particles-enums.js';\nimport { calculateValue } from '../three-particles-utils.js';\nimport type { NormalizedForceFieldConfig } from '../types.js';\n\n// ─── Encoding Layout ─────────────────────────────────────────────────────────\n\n/**\n * Per-field stride in the packed Float32Array.\n *\n * Layout per force field (12 floats):\n * [0] isActive (0 or 1)\n * [1] type (0 = POINT, 1 = DIRECTIONAL)\n * [2-4] position (x, y, z)\n * [5-7] direction (x, y, z) — normalized\n * [8] strength (constant, evaluated at bake time)\n * [9] range (Infinity encoded as a very large number: 1e10)\n * [10] falloff (0 = NONE, 1 = LINEAR, 2 = QUADRATIC)\n * [11] padding (reserved)\n */\nconst FIELD_STRIDE = 12;\n\n/** Maximum force fields supported per particle system. */\nexport const MAX_FORCE_FIELDS = 16;\n\n/** Total floats reserved for force field data in the curveData buffer. */\nexport const FORCE_FIELD_DATA_SIZE = MAX_FORCE_FIELDS * FIELD_STRIDE;\n\n/** Sentinel value for \"infinite\" range on GPU (avoids Infinity in Float32). */\nconst GPU_INFINITY = 1e10;\n\n/** Pre-allocated encoding buffer, reused every frame to avoid GC pressure. */\nlet _encodeBuf: Float32Array | null = null;\n\n// ─── CPU-side Encoding ───────────────────────────────────────────────────────\n\n/**\n * Packs an array of force field configs into a flat Float32Array for GPU upload.\n *\n * @param forceFields - Normalized force field configs from the particle system.\n * @param particleSystemId - System ID for curve evaluation.\n * @param systemLifetimePercentage - Current system lifetime progress for strength curves.\n * @returns A Float32Array of `MAX_FORCE_FIELDS * FIELD_STRIDE` floats.\n */\nexport function encodeForceFieldsForGPU(\n forceFields: ReadonlyArray<NormalizedForceFieldConfig>,\n particleSystemId: number,\n systemLifetimePercentage: number\n): Float32Array {\n if (!_encodeBuf || _encodeBuf.length !== FORCE_FIELD_DATA_SIZE) {\n _encodeBuf = new Float32Array(FORCE_FIELD_DATA_SIZE);\n }\n const data = _encodeBuf;\n data.fill(0);\n\n const count = Math.min(forceFields.length, MAX_FORCE_FIELDS);\n for (let i = 0; i < count; i++) {\n const ff = forceFields[i];\n const base = i * FIELD_STRIDE;\n\n data[base] = ff.isActive ? 1 : 0;\n data[base + 1] = ff.type === ForceFieldType.POINT ? 0 : 1;\n data[base + 2] = ff.position.x;\n data[base + 3] = ff.position.y;\n data[base + 4] = ff.position.z;\n data[base + 5] = ff.direction.x;\n data[base + 6] = ff.direction.y;\n data[base + 7] = ff.direction.z;\n data[base + 8] = calculateValue(\n particleSystemId,\n ff.strength,\n systemLifetimePercentage\n );\n data[base + 9] = ff.range === Infinity ? GPU_INFINITY : ff.range;\n\n let falloffCode = 0;\n if (ff.falloff === ForceFieldFalloff.LINEAR) falloffCode = 1;\n else if (ff.falloff === ForceFieldFalloff.QUADRATIC) falloffCode = 2;\n data[base + 10] = falloffCode;\n data[base + 11] = 0; // padding\n }\n\n return data;\n}\n\n// ─── TSL Force Field Application ─────────────────────────────────────────────\n\n/**\n * Creates the TSL uniform and helper function for applying force fields\n * in a GPU compute shader.\n *\n * Force field data is read from the shared curveData storage buffer at a\n * fixed offset, avoiding an additional storage buffer binding.\n *\n * @param sCurveData - The shared curveData storage node.\n * @param forceFieldOffset - Float offset into curveData where force field data starts.\n * @param forceFieldCount - Number of active force fields (0 to MAX_FORCE_FIELDS).\n * @returns Object with the count uniform and the TSL apply function.\n */\nexport function createForceFieldTSL(\n sCurveData: ShaderNodeObject<Node>,\n forceFieldOffset: number,\n forceFieldCount: number\n) {\n const count = Math.min(forceFieldCount, MAX_FORCE_FIELDS);\n const uForceFieldCount = uniform(float(count));\n const ffBase = forceFieldOffset;\n\n /**\n * TSL function that applies all force fields to a particle's velocity.\n *\n * @param pos - Current particle position (vec3, read-only)\n * @param vel - Current particle velocity (vec3, modified in place)\n * @param delta - Frame time in seconds (float)\n */\n const applyForceFieldsTSL = Fn(\n ({\n pos,\n vel,\n delta,\n }: {\n pos: ShaderNodeObject<Node>;\n vel: ShaderNodeObject<Node>;\n delta: ShaderNodeObject<Node>;\n }) => {\n Loop(uForceFieldCount, ({ i }: { i: ShaderNodeObject<Node> }) => {\n const base = i.mul(FIELD_STRIDE).add(ffBase);\n\n const isActive = sCurveData.element(base);\n If(isActive.lessThan(0.5), () => {\n Continue();\n });\n\n const fieldType = sCurveData.element(base.add(1));\n const fieldPos = vec3(\n sCurveData.element(base.add(2)),\n sCurveData.element(base.add(3)),\n sCurveData.element(base.add(4))\n );\n const fieldDir = vec3(\n sCurveData.element(base.add(5)),\n sCurveData.element(base.add(6)),\n sCurveData.element(base.add(7))\n );\n const strength = sCurveData.element(base.add(8));\n const range = sCurveData.element(base.add(9));\n const falloffType = sCurveData.element(base.add(10));\n\n If(strength.equal(0.0), () => {\n Continue();\n });\n\n // DIRECTIONAL force (type == 1)\n If(fieldType.greaterThan(0.5), () => {\n const force = strength.mul(delta);\n vel.assign(vel.add(fieldDir.mul(force)));\n });\n\n // POINT force (type == 0)\n If(fieldType.lessThan(0.5), () => {\n const toField = fieldPos.sub(pos);\n const dist = length(toField);\n\n // Skip if too close (avoid division by zero)\n If(dist.greaterThan(0.0001), () => {\n // Skip if outside range (range > GPU_INFINITY means infinite)\n const inRange = dist.lessThan(range);\n If(inRange, () => {\n const dir = normalize(toField);\n\n // Compute falloff multiplier\n const normDist = dist.div(range);\n\n // NONE falloff (type == 0): multiplier = 1.0\n // LINEAR falloff (type == 1): multiplier = 1 - normDist\n // QUADRATIC falloff (type == 2): multiplier = 1 - normDist²\n const falloffNone = float(1.0);\n const falloffLinear = float(1.0).sub(normDist);\n const falloffQuadratic = float(1.0).sub(normDist.mul(normDist));\n\n // Select based on falloff type\n const useLinear = falloffType.greaterThan(0.5);\n const useQuadratic = falloffType.greaterThan(1.5);\n const falloff = useQuadratic.select(\n falloffQuadratic,\n useLinear.select(falloffLinear, falloffNone)\n );\n\n // Apply force\n const force = strength.mul(falloff).mul(delta);\n vel.assign(vel.add(dir.mul(force)));\n });\n });\n });\n });\n },\n 'void'\n );\n\n return {\n /** Uniform for the active force field count. */\n countUniform: uForceFieldCount,\n /** TSL function to call in the compute kernel: apply({ pos, vel, delta }) */\n apply: applyForceFieldsTSL,\n };\n}\n","/**\n * Curve baking utilities for WebGPU compute shader lookup.\n *\n * Converts JavaScript lifetime curve functions (Bezier and Easing) into packed\n * Float32Arrays that can be uploaded as GPU storage buffers or data textures.\n * The GPU shader looks up a baked value with:\n *\n * value = data[curveIndex * CURVE_RESOLUTION + floor(t * (CURVE_RESOLUTION - 1))]\n *\n * with linear interpolation between adjacent samples for smooth results.\n *\n * @module\n */\n\nimport {\n calculateValue,\n getCurveFunctionFromConfig,\n isLifeTimeCurve,\n} from '../three-particles-utils.js';\nimport type {\n NormalizedParticleSystemConfig,\n LifetimeCurve,\n} from '../types.js';\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\n/**\n * Number of samples per baked curve.\n *\n * 256 gives sub-0.4% maximum interpolation error for smooth curves while\n * keeping each curve to exactly one kilobyte of Float32 data (256 × 4 bytes).\n * The GPU shader indexes: `data[curveIndex * CURVE_RESOLUTION + floor(t * 255)]`.\n */\nexport const CURVE_RESOLUTION = 256;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/**\n * Result of baking all active lifetime curves from a particle system config.\n *\n * All curves are packed sequentially into a single `data` Float32Array.\n * Each slot stores a curve index (≥ 0) if the corresponding modifier is active,\n * or `-1` when the modifier is inactive / uses a constant value.\n *\n * The GPU shader accesses a sample via:\n * ```glsl\n * float t01 = clamp(particleLifetime / startLifetime, 0.0, 1.0);\n * int idx = curveIndex * CURVE_RESOLUTION + int(t01 * 255.0);\n * float val = data[idx]; // + optional linear interpolation\n * ```\n */\nexport type BakedCurveMap = {\n /** All baked curves packed sequentially (curveCount × CURVE_RESOLUTION floats). */\n data: Float32Array;\n /** Total number of baked curves stored in `data`. */\n curveCount: number;\n /** Curve index for `sizeOverLifetime.lifetimeCurve` (-1 if inactive). */\n sizeOverLifetime: number;\n /** Curve index for `opacityOverLifetime.lifetimeCurve` (-1 if inactive). */\n opacityOverLifetime: number;\n /** Curve index for `colorOverLifetime.r` (-1 if inactive). */\n colorR: number;\n /** Curve index for `colorOverLifetime.g` (-1 if inactive). */\n colorG: number;\n /** Curve index for `colorOverLifetime.b` (-1 if inactive). */\n colorB: number;\n /** Curve index for `velocityOverLifetime.linear.x` (-1 if not a curve). */\n linearVelX: number;\n /** Curve index for `velocityOverLifetime.linear.y` (-1 if not a curve). */\n linearVelY: number;\n /** Curve index for `velocityOverLifetime.linear.z` (-1 if not a curve). */\n linearVelZ: number;\n /** Curve index for `velocityOverLifetime.orbital.x` (-1 if not a curve). */\n orbitalVelX: number;\n /** Curve index for `velocityOverLifetime.orbital.y` (-1 if not a curve). */\n orbitalVelY: number;\n /** Curve index for `velocityOverLifetime.orbital.z` (-1 if not a curve). */\n orbitalVelZ: number;\n};\n\n// ─── Core Sampling ────────────────────────────────────────────────────────────\n\n/**\n * Samples a curve function at `resolution` evenly-spaced points over [0, 1]\n * and returns the results as a `Float32Array`.\n *\n * The i-th element is `curveFn(i / (resolution - 1))`, so both endpoints\n * (t = 0 and t = 1) are always included.\n *\n * @param curveFn - Any `(t: number) => number` function. t is in [0, 1].\n * @param resolution - Number of samples to take. Defaults to {@link CURVE_RESOLUTION}.\n * @returns A `Float32Array` of length `resolution` with the sampled values.\n *\n * @example\n * ```typescript\n * const linear = bakeCurve(t => t); // [0, 1/255, 2/255, …, 1]\n * const stepped = bakeCurve(t => t, 4); // [0, 0.333, 0.667, 1]\n * ```\n */\nexport function bakeCurve(\n curveFn: (t: number) => number,\n resolution: number = CURVE_RESOLUTION\n): Float32Array {\n const samples = new Float32Array(resolution);\n const lastIndex = resolution - 1;\n\n for (let i = 0; i < resolution; i++) {\n // Evenly space samples from 0 to 1 inclusive.\n const t = lastIndex === 0 ? 0 : i / lastIndex;\n samples[i] = curveFn(t);\n }\n\n return samples;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Bakes a single {@link LifetimeCurve} config into the packed buffer at the\n * given write offset and returns the next free write offset.\n *\n * @param buffer - The destination Float32Array.\n * @param writeOffset - The first float index to write into.\n * @param particleSystemId - Used by the Bezier caching layer.\n * @param curve - The curve configuration to bake.\n * @returns The next available write offset (writeOffset + CURVE_RESOLUTION).\n */\nfunction bakeCurveIntoBuffer(\n buffer: Float32Array,\n writeOffset: number,\n particleSystemId: number,\n curve: LifetimeCurve\n): number {\n const curveFn = getCurveFunctionFromConfig(particleSystemId, curve);\n const lastIndex = CURVE_RESOLUTION - 1;\n\n for (let i = 0; i < CURVE_RESOLUTION; i++) {\n const t = lastIndex === 0 ? 0 : i / lastIndex;\n buffer[writeOffset + i] = curveFn(t);\n }\n\n return writeOffset + CURVE_RESOLUTION;\n}\n\n/**\n * Bakes a velocity axis value (constant, random, or curve) into the buffer.\n * Constants and random ranges are baked as flat curves using their mid-point.\n */\nfunction bakeVelocityAxisIntoBuffer(\n buffer: Float32Array,\n writeOffset: number,\n particleSystemId: number,\n value:\n | import('../types.js').Constant\n | import('../types.js').RandomBetweenTwoConstants\n | LifetimeCurve\n): number {\n if (isLifeTimeCurve(value)) {\n return bakeCurveIntoBuffer(buffer, writeOffset, particleSystemId, value);\n }\n // Constant or RandomBetweenTwoConstants — bake as flat curve using mid-point\n const constantValue = calculateValue(particleSystemId, value, 0.5);\n for (let i = 0; i < CURVE_RESOLUTION; i++) {\n buffer[writeOffset + i] = constantValue;\n }\n return writeOffset + CURVE_RESOLUTION;\n}\n\n// ─── Particle System Curve Baking ─────────────────────────────────────────────\n\n/**\n * Bakes all active lifetime curves from a normalized particle system config\n * into a single, GPU-ready {@link BakedCurveMap}.\n *\n * The function inspects each modifier in order:\n * 1. `sizeOverLifetime` — baked if `isActive` is true\n * 2. `opacityOverLifetime` — baked if `isActive` is true\n * 3. `colorOverLifetime` (r, g, b) — all three baked if `isActive` is true\n * 4. `velocityOverLifetime.linear` (x, y, z) — each axis baked if the value\n * is a {@link LifetimeCurve} (constants and random ranges are skipped)\n * 5. `velocityOverLifetime.orbital` (x, y, z) — same rule as linear\n *\n * Curves are packed sequentially into a single `Float32Array`:\n * ```\n * [curve0_sample0, …, curve0_sample255, curve1_sample0, …, curveN_sample255]\n * ```\n *\n * @param normalizedConfig - A fully normalized particle system configuration\n * (all properties required, produced by the particle system initialization).\n * @param particleSystemId - Numeric ID passed to the Bezier caching layer so\n * it can reuse pre-evaluated Bezier functions for the same particle system.\n * @returns A {@link BakedCurveMap} with the packed data and per-modifier\n * curve indices. Inactive modifiers / constant axes have index `-1`.\n *\n * @example\n * ```typescript\n * const map = bakeParticleSystemCurves(normalizedConfig, systemId);\n * // Upload map.data to a GPU buffer.\n * // Pass map.sizeOverLifetime, map.opacityOverLifetime, … as uniforms.\n * ```\n */\nexport function bakeParticleSystemCurves(\n normalizedConfig: NormalizedParticleSystemConfig,\n particleSystemId: number\n): BakedCurveMap {\n // ── Phase 1: count how many curves will be baked ──────────────────────────\n let curveCount = 0;\n\n const {\n sizeOverLifetime,\n opacityOverLifetime,\n colorOverLifetime,\n velocityOverLifetime,\n } = normalizedConfig;\n\n const hasSizeOverLifetime = sizeOverLifetime.isActive;\n const hasOpacityOverLifetime = opacityOverLifetime.isActive;\n const hasColorOverLifetime = colorOverLifetime.isActive;\n\n // For velocity axes, bake BOTH curves AND constant/random values.\n // Constants/randoms are baked as flat curves using calculateValue (mid-point).\n const isVelActive = velocityOverLifetime.isActive;\n const hasLinearVelX =\n isVelActive &&\n velocityOverLifetime.linear.x !== undefined &&\n velocityOverLifetime.linear.x !== 0;\n const hasLinearVelY =\n isVelActive &&\n velocityOverLifetime.linear.y !== undefined &&\n velocityOverLifetime.linear.y !== 0;\n const hasLinearVelZ =\n isVelActive &&\n velocityOverLifetime.linear.z !== undefined &&\n velocityOverLifetime.linear.z !== 0;\n\n const hasOrbitalVelX =\n isVelActive &&\n velocityOverLifetime.orbital.x !== undefined &&\n velocityOverLifetime.orbital.x !== 0;\n const hasOrbitalVelY =\n isVelActive &&\n velocityOverLifetime.orbital.y !== undefined &&\n velocityOverLifetime.orbital.y !== 0;\n const hasOrbitalVelZ =\n isVelActive &&\n velocityOverLifetime.orbital.z !== undefined &&\n velocityOverLifetime.orbital.z !== 0;\n\n if (hasSizeOverLifetime) curveCount++;\n if (hasOpacityOverLifetime) curveCount++;\n if (hasColorOverLifetime) curveCount += 3; // r, g, b always together\n if (hasLinearVelX) curveCount++;\n if (hasLinearVelY) curveCount++;\n if (hasLinearVelZ) curveCount++;\n if (hasOrbitalVelX) curveCount++;\n if (hasOrbitalVelY) curveCount++;\n if (hasOrbitalVelZ) curveCount++;\n\n // ── Phase 2: allocate the packed buffer ───────────────────────────────────\n const data = new Float32Array(curveCount * CURVE_RESOLUTION);\n\n // ── Phase 3: bake curves in a consistent, sequential order ────────────────\n let writeOffset = 0;\n let nextIndex = 0;\n\n // Index slots — -1 signals \"not active / not a curve\".\n let sizeOverLifetimeIdx = -1;\n let opacityOverLifetimeIdx = -1;\n let colorRIdx = -1;\n let colorGIdx = -1;\n let colorBIdx = -1;\n let linearVelXIdx = -1;\n let linearVelYIdx = -1;\n let linearVelZIdx = -1;\n let orbitalVelXIdx = -1;\n let orbitalVelYIdx = -1;\n let orbitalVelZIdx = -1;\n\n if (hasSizeOverLifetime) {\n sizeOverLifetimeIdx = nextIndex++;\n writeOffset = bakeCurveIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n sizeOverLifetime.lifetimeCurve\n );\n }\n\n if (hasOpacityOverLifetime) {\n opacityOverLifetimeIdx = nextIndex++;\n writeOffset = bakeCurveIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n opacityOverLifetime.lifetimeCurve\n );\n }\n\n if (hasColorOverLifetime) {\n colorRIdx = nextIndex++;\n writeOffset = bakeCurveIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n colorOverLifetime.r\n );\n\n colorGIdx = nextIndex++;\n writeOffset = bakeCurveIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n colorOverLifetime.g\n );\n\n colorBIdx = nextIndex++;\n writeOffset = bakeCurveIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n colorOverLifetime.b\n );\n }\n\n if (hasLinearVelX) {\n linearVelXIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.linear.x!\n );\n }\n\n if (hasLinearVelY) {\n linearVelYIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.linear.y!\n );\n }\n\n if (hasLinearVelZ) {\n linearVelZIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.linear.z!\n );\n }\n\n if (hasOrbitalVelX) {\n orbitalVelXIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.orbital.x!\n );\n }\n\n if (hasOrbitalVelY) {\n orbitalVelYIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.orbital.y!\n );\n }\n\n if (hasOrbitalVelZ) {\n orbitalVelZIdx = nextIndex++;\n writeOffset = bakeVelocityAxisIntoBuffer(\n data,\n writeOffset,\n particleSystemId,\n velocityOverLifetime.orbital.z!\n );\n }\n\n return {\n data,\n curveCount,\n sizeOverLifetime: sizeOverLifetimeIdx,\n opacityOverLifetime: opacityOverLifetimeIdx,\n colorR: colorRIdx,\n colorG: colorGIdx,\n colorB: colorBIdx,\n linearVelX: linearVelXIdx,\n linearVelY: linearVelYIdx,\n linearVelZ: linearVelZIdx,\n orbitalVelX: orbitalVelXIdx,\n orbitalVelY: orbitalVelYIdx,\n orbitalVelZ: orbitalVelZIdx,\n };\n}\n\n// ─── GPU Upload Helpers ───────────────────────────────────────────────────────\n\n/**\n * Wraps a {@link BakedCurveMap}'s packed data in a `THREE.DataTexture` that\n * can be bound as a 1D lookup texture in a WebGL or WebGPU material.\n *\n * The texture is `curveCount × CURVE_RESOLUTION` pixels wide and 1 pixel tall,\n * using a single red-channel (`RedFormat`) 32-bit float format. A GPU shader\n * can sample it with:\n * ```glsl\n * float u = (float(curveIndex * RESOLUTION) + t255 + 0.5)\n * / float(curveCount * RESOLUTION);\n * float val = texture2D(curveTex, vec2(u, 0.5)).r;\n * ```\n *\n * @param bakedCurves - The result of {@link bakeParticleSystemCurves}.\n * @returns A `THREE.DataTexture` ready for use as a material uniform, or\n * `null` when `curveCount` is 0 (no curves to upload).\n *\n * @remarks\n * - `needsUpdate` is set to `true` so Three.js uploads the data on first use.\n * - The caller is responsible for calling `.dispose()` when the particle system\n * is destroyed to release GPU memory.\n * - For WebGPU storage-buffer binding, use `bakedCurves.data` directly instead.\n */\nexport function createCurveDataTexture(\n bakedCurves: BakedCurveMap\n): import('three').DataTexture | null {\n if (bakedCurves.curveCount === 0) {\n return null;\n }\n\n // Deferred import so this module remains usable in non-browser / test\n // environments where the THREE globals may not be set up.\n\n const THREE = require('three') as typeof import('three');\n\n const width = bakedCurves.curveCount * CURVE_RESOLUTION;\n const texture = new THREE.DataTexture(\n bakedCurves.data,\n width,\n 1,\n THREE.RedFormat,\n THREE.FloatType\n );\n\n texture.minFilter = THREE.LinearFilter;\n texture.magFilter = THREE.LinearFilter;\n texture.wrapS = THREE.ClampToEdgeWrapping;\n texture.wrapT = THREE.ClampToEdgeWrapping;\n texture.needsUpdate = true;\n\n return texture;\n}\n","/**\n * 3D Simplex Noise implemented in TSL (Three Shading Language).\n *\n * Adapted from the classic Ashima Arts / Stefan Gustavson simplex noise\n * implementation (MIT licence) for use in Three.js GPU compute shaders.\n *\n * References:\n * - Stefan Gustavson, \"Simplex noise demystified\", 2005\n * - https://github.com/ashima/webgl-noise\n *\n * @module\n */\nimport {\n Fn,\n vec2,\n vec3,\n vec4,\n float,\n floor,\n dot,\n step,\n abs,\n min,\n max,\n mod,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Hash / permutation helper.\n *\n * Equivalent to the classic `mod289` + multiply-and-fold scheme:\n * permute(x) = mod(((x * 34.0) + 10.0) * x, 289.0)\n *\n * The extra `10.0` (vs the original `1.0`) avoids degenerate runs near zero\n * and is widely used in TSL/WGSL ports of the Ashima implementation.\n */\nconst permute = Fn(({ x }: Record<string, ShaderNodeObject<Node>>) => {\n // ((x * 34.0 + 10.0) * x) mod 289.0\n return mod(x.mul(34.0).add(10.0).mul(x), float(289.0));\n});\n\n/**\n * Fast inverse square-root approximation.\n *\n * taylorInvSqrt(r) = 1.79284291400159 - 0.85373472095314 * r\n *\n * Used to normalise gradient vectors without a true sqrt.\n */\nconst taylorInvSqrt = Fn(({ r }: Record<string, ShaderNodeObject<Node>>) => {\n return float(1.79284291400159).sub(float(0.85373472095314).mul(r));\n});\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * 3D simplex noise implemented in TSL.\n *\n * Algorithm overview:\n * 1. Skew the input point into the simplex (tetrahedral) grid.\n * 2. Determine which of the six tetrahedra the skewed point occupies.\n * 3. Un-skew the four integer lattice corners back to real space.\n * 4. Compute pseudo-random gradient vectors at each corner via two rounds of\n * `permute` (one for the iy/iz hash, one for ix), yielding per-corner\n * gradient (gx, gy, gz) extracted using the \"ns\" mapping.\n * 5. Weight each corner's contribution by a radially-symmetric falloff\n * `max(0, 0.5 - |x|²)⁴` and accumulate.\n * 6. Scale the result so the output lies in approximately [-1, 1].\n *\n * @param v - Input position (vec3).\n * @returns Noise value in approximately [-1, 1] (float).\n */\nexport const snoise3D: ReturnType<typeof Fn> = Fn(\n ({ v }: Record<string, ShaderNodeObject<Node>>) => {\n // ── Step 1: Skew input space ──────────────────────────────────────────────\n // F3 = 1/3. Skew the input space to determine which simplex cell we're in.\n const ONE_THIRD = float(1.0 / 3.0);\n const ONE_SIXTH = float(1.0 / 6.0);\n\n // i = floor(v + dot(v, vec3(1/3)))\n const i = floor(\n v.add(dot(v, vec3(ONE_THIRD, ONE_THIRD, ONE_THIRD)))\n ).toVar();\n\n // ── Step 2: Un-skew back to (x0,y0,z0) ──────────────────────────────────\n // x0 = v - i + dot(i, vec3(1/6))\n const x0 = v\n .sub(i)\n .add(dot(i, vec3(ONE_SIXTH, ONE_SIXTH, ONE_SIXTH)))\n .toVar();\n\n // ── Step 3: Determine simplex (tetrahedron) ───────────────────────────────\n // Which of the six tetrahedra is point x0 in?\n // Compare x0 components pairwise to rank them and identify the two\n // intermediate simplex corners (i1, i2).\n //\n // g = step(x0.yzx, x0.xyz): 1 where x0[i] >= x0[i-1 mod 3]\n // l = 1 - g.zxy (complement for the other direction)\n const g = step(x0.yzx, x0.xyz).toVar();\n const l = float(1.0).sub(g).toVar();\n\n // i1: first offset (one unit step toward the dominant axis)\n // i2: second offset (two unit steps)\n const i1 = min(g.xyz, l.zxy).toVar();\n const i2 = max(g.xyz, l.zxy).toVar();\n\n // Un-skew the three corner displacements from x0:\n // x1 = x0 - i1 + G3\n // x2 = x0 - i2 + 2*G3\n // x3 = x0 - 1 + 3*G3\n const x1 = x0.sub(i1).add(ONE_SIXTH).toVar();\n const x2 = x0.sub(i2).add(ONE_SIXTH.mul(2.0)).toVar();\n const x3 = x0.sub(float(1.0)).add(ONE_SIXTH.mul(3.0)).toVar();\n\n // ── Step 4: Permutation hashes → gradient directions ─────────────────────\n // Wrap i into [0, 289) so the hash stays well-behaved.\n const iw = mod(i, float(289.0)).toVar();\n\n // Hash yz layers first, then mix in x.\n // For each of the 4 corners, hash iz then iy:\n // p_yz_c = permute(permute(iz_c) + iy_c)\n // Then hash ix:\n // p_c = permute(p_yz_c + ix_c)\n // Each final p_c is a scalar hash that encodes the full gradient direction.\n\n // Corner z-components: iz, iz+i1.z, iz+i2.z, iz+1\n const p0_yz = permute({\n x: permute({\n x: vec4(\n vec2(iw.z, iw.z.add(i1.z)),\n vec2(iw.z.add(i2.z), iw.z.add(1.0))\n ),\n }).add(\n vec4(vec2(iw.y, iw.y.add(i1.y)), vec2(iw.y.add(i2.y), iw.y.add(1.0)))\n ),\n });\n\n // Final hash: mix in x-components\n const p = permute({\n x: p0_yz.add(\n vec4(vec2(iw.x, iw.x.add(i1.x)), vec2(iw.x.add(i2.x), iw.x.add(1.0)))\n ),\n });\n\n // ── Step 5: Convert hash to gradient directions ───────────────────────────\n // Standard Ashima/Gustavson gradient extraction via octahedral mapping.\n //\n // The hash values `p` are in [0, 289). We reduce them to a 7×7 grid\n // (49 entries) to extract two gradient components (gx, gy), then derive\n // gz via octahedral projection: gz = 1 - |gx| - |gy|.\n //\n // When gz < 0 the gradient lies outside the octahedron's upper hemisphere;\n // a fold-back correction shifts gx/gy inward so the gradient stays on\n // the octahedron surface, giving 12 well-distributed directions that\n // closely approximate the original simplex gradient set.\n //\n // Constants (from Ashima `vec3 ns = n_ * D.wyz - D.xzx`):\n // ns.x = 2/7 ≈ 0.285714 (grid step size)\n // ns.y = -13/14 ≈ -0.928571 (grid offset: 1/14 - 1)\n // ns.z = 1/7 ≈ 0.142857 (used for p mod 49)\n\n const n_ = float(0.142857142857142); // 1/7\n\n // j = p mod 49 — reduces hash to [0, 48]\n // p * ns.z * ns.z = p * (1/7)^2 = p / 49\n const j = p.sub(float(49.0).mul(floor(p.mul(n_).mul(n_)))).toVar();\n\n // x_ = floor(j / 7) ∈ [0, 6]\n const x_ = floor(j.mul(n_)).toVar();\n // y_ = floor(j - 7 * x_) ∈ [0, 6]\n const y_ = floor(j.sub(float(7.0).mul(x_))).toVar();\n\n // Map grid indices to gradient components in approximately [-1, 1]:\n // gx = x_ * (2/7) + (-13/14) ∈ [-0.929, 0.786]\n // gy = y_ * (2/7) + (-13/14) ∈ [-0.929, 0.786]\n const NS_X = float(0.285714285714286); // 2/7\n const NS_Y = float(-0.928571428571429); // 1/14 - 1\n\n const gx = x_.mul(NS_X).add(NS_Y);\n const gy = y_.mul(NS_X).add(NS_Y);\n\n // gz = octahedral budget: 1 - |gx| - |gy|\n const gz = float(1.0).sub(abs(gx)).sub(abs(gy)).toVar();\n\n // Octahedral fold-back: when gz < 0, shift gx/gy toward the origin\n // step(gz, 0) → 1 when gz <= 0, 0 when gz > 0\n // correction = (floor(component) + 0.5) when gz <= 0, else 0\n // gx -= correction_x, gy -= correction_y\n const gz_neg = step(gz, vec4(0.0)); // 1 where gz <= 0\n const ox = gz_neg.mul(floor(gx).add(0.5));\n const oy = gz_neg.mul(floor(gy).add(0.5));\n const gx_final = gx.sub(ox);\n const gy_final = gy.sub(oy);\n\n // Build the four un-normalised gradient vectors\n const g0 = vec3(gx_final.x, gy_final.x, gz.x).toVar();\n const g1 = vec3(gx_final.y, gy_final.y, gz.y).toVar();\n const g2 = vec3(gx_final.z, gy_final.z, gz.z).toVar();\n const g3 = vec3(gx_final.w, gy_final.w, gz.w).toVar();\n\n // Normalise gradients using the Taylor inverse-sqrt approximation\n const norm = taylorInvSqrt({\n r: vec4(vec2(dot(g0, g0), dot(g1, g1)), vec2(dot(g2, g2), dot(g3, g3))),\n });\n g0.assign(g0.mul(norm.x));\n g1.assign(g1.mul(norm.y));\n g2.assign(g2.mul(norm.z));\n g3.assign(g3.mul(norm.w));\n\n // ── Step 6: Compute corner contributions ─────────────────────────────────\n // Falloff: m = max(0, 0.5 - |x|²)⁴ (C² continuity)\n const m = max(\n vec4(\n vec2(float(0.5).sub(dot(x0, x0)), float(0.5).sub(dot(x1, x1))),\n vec2(float(0.5).sub(dot(x2, x2)), float(0.5).sub(dot(x3, x3)))\n ),\n float(0.0)\n ).toVar();\n\n const m2 = m.mul(m).toVar();\n const m4 = m2.mul(m2).toVar();\n\n // Dot the gradients with the un-skewed displacement vectors\n const gdot = vec4(\n vec2(dot(g0, x0), dot(g1, x1)),\n vec2(dot(g2, x2), dot(g3, x3))\n );\n\n // Accumulate: sum(m4 * gdot), then scale to [-1, 1]\n // The scale factor 42.0 is the classical Gustavson normalisation constant\n // for 3D simplex noise with this gradient set.\n return float(42.0).mul(dot(m4, gdot));\n }\n);\n\n/**\n * Evaluates noise at three different input configurations matching\n * the CPU noise modifier pattern from `three-particles-modifiers.ts`:\n *\n * ```\n * noiseX = snoise3D(pos, 0, 0) // pos = (t, 0, 0)\n * noiseY = snoise3D(pos, pos, 0) // pos = (t, t, 0)\n * noiseZ = snoise3D(pos, pos, pos) // pos = (t, t, t)\n * ```\n *\n * where `t` is the scalar noise position (e.g. `lifePercent * strength * 10`).\n *\n * This matches the three calls in the CPU path:\n * ```typescript\n * noiseInput.set(noisePosition, 0, 0 ); // → posX\n * noiseInput.set(noisePosition, noisePosition, 0 ); // → posY\n * noiseInput.set(noisePosition, noisePosition, noisePosition); // → posZ\n * ```\n *\n * @param t - Scalar noise position (float). Typically `lifePercent * strength * 10`.\n * @returns vec3(noiseX, noiseY, noiseZ) — each component in approximately [-1, 1].\n */\nexport const particleNoise3: ReturnType<typeof Fn> = Fn(\n ({ t }: Record<string, ShaderNodeObject<Node>>) => {\n const noiseX = snoise3D({ v: vec3(t, float(0.0), float(0.0)) });\n const noiseY = snoise3D({ v: vec3(t, t, float(0.0)) });\n const noiseZ = snoise3D({ v: vec3(t, t, t) });\n return vec3(noiseX, noiseY, noiseZ);\n }\n);\n\n// Re-export TSL types for callers that need them\nexport type { ShaderNodeObject, Node };\n","/**\n * GPU compute shader for particle lifetime modifiers.\n *\n * Extends the core physics compute (Phase 2) with all 7 modifiers:\n * 1. Size over lifetime (curve lookup)\n * 2. Opacity over lifetime (curve lookup)\n * 3. Color over lifetime (3 curve lookups for R/G/B)\n * 4. Rotation over lifetime (constant speed)\n * 5. Linear velocity (optional curve lookup per axis)\n * 6. Orbital velocity (Euler rotation around emission offset)\n * 7. Noise (simplex noise affecting position, rotation, size)\n *\n * Curves are pre-baked into a Float32Array (256 samples each) and accessed\n * via a storage buffer with linear interpolation on the GPU.\n *\n * @module\n */\nimport { Vector3 } from 'three';\nimport {\n Fn,\n float,\n vec3,\n vec4,\n storage,\n instanceIndex,\n uniform,\n If,\n floor,\n fract,\n mix,\n sin,\n cos,\n min as tslMin,\n compute,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\n\nimport {\n StorageBufferAttribute,\n StorageInstancedBufferAttribute,\n} from 'three/webgpu';\n\nimport {\n createCollisionPlaneTSL,\n COLLISION_PLANE_DATA_SIZE,\n} from './compute-collision-planes.js';\nimport {\n createForceFieldTSL,\n FORCE_FIELD_DATA_SIZE,\n} from './compute-force-fields.js';\nimport { CURVE_RESOLUTION } from './curve-bake.js';\nimport { snoise3D } from './tsl-noise.js';\nimport type { BakedCurveMap } from './curve-bake.js';\n\n// ─── Per-Particle Init Data Constants ────────────────────────────────────────\n\n/**\n * Number of floats per particle in the init-data region of the curveData buffer.\n *\n * Layout (28 floats = 7 vec4s):\n * 0: position.x\n * 1: position.y\n * 2: position.z\n * 3: initFlag (0.0 = no init, 1.0 = needs init)\n * 4: velocity.x\n * 5: velocity.y\n * 6: velocity.z\n * 7: (padding)\n * 8: color.R\n * 9: color.G\n * 10: color.B\n * 11: color.A\n * 12: particleState.x (lifetime = 0)\n * 13: particleState.y (size)\n * 14: particleState.z (rotation)\n * 15: particleState.w (startFrame)\n * 16: orbitalOffset.x\n * 17: orbitalOffset.y\n * 18: orbitalOffset.z\n * 19: isActive (= 1.0)\n * 20: startValues.x (startLifetime)\n * 21: startValues.y (startSize)\n * 22: startValues.z (startOpacity)\n * 23: startValues.w (startColorR)\n * 24: startColorsExt.x (startColorG)\n * 25: startColorsExt.y (startColorB)\n * 26: startColorsExt.z (rotationSpeed)\n * 27: startColorsExt.w (noiseOffset)\n *\n * Each particle has its own fixed slot at `curveLen + particleIndex * INIT_STRIDE`.\n * The compute shader reads `initFlag` (offset 3) for particle `i`; if 1.0 it\n * copies the init data into the main buffers. This is O(1) per particle —\n * no queue scanning needed.\n *\n * startValues and startColorsExt are included in the init block (rather than\n * using full-buffer CPU uploads) to prevent overwriting active particle data\n * when a particle slot is recycled on the CPU before the GPU has processed\n * its death check.\n */\nexport const INIT_STRIDE = 28;\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** Modifier flags set from CPU config — determines which modifiers run on GPU. */\nexport type ModifierFlags = {\n sizeOverLifetime: boolean;\n opacityOverLifetime: boolean;\n colorOverLifetime: boolean;\n rotationOverLifetime: boolean;\n linearVelocity: boolean;\n orbitalVelocity: boolean;\n noise: boolean;\n forceFields: boolean;\n collisionPlanes: boolean;\n};\n\n/** Per-frame modifier uniform values. */\nexport type ModifierUniforms = {\n delta: ShaderNodeObject<Node>;\n deltaMs: ShaderNodeObject<Node>;\n gravityVelocity: ShaderNodeObject<Node>;\n worldPositionChange: ShaderNodeObject<Node>;\n simulationSpaceWorld: ShaderNodeObject<Node>;\n // Noise uniforms\n noiseStrength: ShaderNodeObject<Node>;\n noisePower: ShaderNodeObject<Node>;\n noiseFrequency: ShaderNodeObject<Node>;\n noisePositionAmount: ShaderNodeObject<Node>;\n noiseRotationAmount: ShaderNodeObject<Node>;\n noiseSizeAmount: ShaderNodeObject<Node>;\n};\n\n/**\n * GPU storage buffers for the modifier compute pipeline.\n *\n * 8 bindings (within the WebGPU per-stage limit):\n * 1. position (vec4) — render + compute\n * 2. velocity (vec4) — compute-only\n * 3. color (vec4: R,G,B,A) — render + compute\n * 4. particleState (vec4: lifetime, size, rotation, startFrame) — render + compute\n * 5. startValues (vec4: startLifetime, startSize, startOpacity, startColorR) — compute + render(.x)\n * 6. startColorsExt (vec4: startColorG, startColorB, rotationSpeed, noiseOffset) — compute-only\n * 7. orbitalIsActive (vec4: offsetX, offsetY, offsetZ, isActive) — compute-only\n * 8. curveData (float[]) — compute-only; carries per-particle init data at the end\n *\n * Per-particle init data is appended to the curveData buffer to avoid\n * exceeding the 8-storage-buffer per-stage limit. Layout:\n * [0 .. curveLen-1] baked curve samples\n * [curveLen + i*INIT_STRIDE .. +INIT_STRIDE-1] init data for particle i\n *\n * The compute shader checks initFlag (offset 3 within each particle's slot)\n * and copies init data to the main buffers in O(1) per particle.\n */\nexport type ModifierStorageBuffers = {\n /** Particle position (vec3). Render attribute + compute. */\n position: StorageBufferAttribute | StorageInstancedBufferAttribute;\n /** Particle velocity (vec3). Compute-only. */\n velocity: StorageBufferAttribute;\n /** Packed RGBA color (vec4). Render attribute + compute. */\n color: StorageBufferAttribute | StorageInstancedBufferAttribute;\n /** Packed (lifetime, size, rotation, startFrame). Render attribute + compute. */\n particleState: StorageBufferAttribute | StorageInstancedBufferAttribute;\n /** Packed (startLifetime, startSize, startOpacity, startColorR). Compute + render(.x for startLifetime). */\n startValues: StorageBufferAttribute | StorageInstancedBufferAttribute;\n /** Packed (startColorG, startColorB, rotationSpeed, noiseOffset). Compute-only. */\n startColorsExt: StorageBufferAttribute;\n /** Packed (orbitalOffset.x, .y, .z, isActive). Compute-only. */\n orbitalIsActive: StorageBufferAttribute;\n /** Baked curve data + emit queue tail (float[]). Compute-only. */\n curveData: StorageBufferAttribute;\n};\n\n/** The complete compute pipeline handle. */\nexport type ModifierComputePipeline = {\n computeNode: ReturnType<typeof compute>;\n uniforms: ModifierUniforms;\n buffers: ModifierStorageBuffers;\n /** Offset into curveData where per-particle init data begins (= baked curve data length). */\n curveDataLength: number;\n /** Force field metadata for runtime updates (null if no force fields). */\n forceFieldInfo: {\n /** Float offset into curveData where force field data starts. */\n offset: number;\n /** Uniform for the active force field count. */\n countUniform: ShaderNodeObject<Node>;\n } | null;\n /** Collision plane metadata for runtime updates (null if no collision planes). */\n collisionPlaneInfo: {\n /** Float offset into curveData where collision plane data starts. */\n offset: number;\n /** Uniform for the active collision plane count. */\n countUniform: ShaderNodeObject<Node>;\n } | null;\n};\n\n// ─── Storage Buffer Creation ─────────────────────────────────────────────────\n\n/**\n * Creates GPU storage buffers for the full modifier compute pipeline.\n * Extends the Phase 2 buffers with modifier-specific data.\n *\n * @param hasForceFields - If true, reserves space for force field data at the\n * end of the curveData buffer (after baked curves + per-particle init data).\n * @param hasCollisionPlanes - If true, reserves space for collision plane data\n * at the end of the curveData buffer (after force field data).\n */\nexport function createModifierStorageBuffers(\n maxParticles: number,\n instanced: boolean,\n curveData: Float32Array,\n hasForceFields = false,\n hasCollisionPlanes = false\n): ModifierStorageBuffers {\n const Cls = instanced\n ? StorageInstancedBufferAttribute\n : StorageBufferAttribute;\n\n // curveData buffer layout:\n // [0 .. curveLen-1] baked curve samples\n // [curveLen + i*INIT_STRIDE .. +INIT_STRIDE-1] init data for particle i\n // [curveLen + maxP*INIT_STRIDE .. +FF_SIZE-1] force field data (if enabled)\n // [... + FF_SIZE .. +CP_SIZE-1] collision plane data (if enabled)\n //\n // Each particle has a fixed slot — O(1) lookup in the compute shader.\n // Force field and collision plane data are appended at the end to stay\n // within the 8 storage buffer per-stage limit.\n const curveLen = Math.max(curveData.length, 1);\n const ffSize = hasForceFields ? FORCE_FIELD_DATA_SIZE : 0;\n const cpSize = hasCollisionPlanes ? COLLISION_PLANE_DATA_SIZE : 0;\n const totalLen = curveLen + maxParticles * INIT_STRIDE + ffSize + cpSize;\n const combined = new Float32Array(totalLen);\n combined.set(curveData.length > 0 ? curveData : new Float32Array([0]));\n // All init flags start at 0 (no init needed)\n\n return {\n // Position and velocity use vec4 (w=padding) to avoid WebGPU vec3→vec4\n // storage buffer alignment conversion that breaks itemSize-based type resolution.\n position: new Cls(new Float32Array(maxParticles * 4), 4),\n velocity: new StorageBufferAttribute(new Float32Array(maxParticles * 4), 4),\n color: new Cls(new Float32Array(maxParticles * 4), 4),\n // (lifetime, size, rotation, startFrame)\n particleState: new Cls(new Float32Array(maxParticles * 4), 4),\n // (startLifetime, startSize, startOpacity, startColorR)\n startValues: new Cls(new Float32Array(maxParticles * 4), 4),\n // (startColorG, startColorB, rotationSpeed, noiseOffset)\n startColorsExt: new StorageBufferAttribute(\n new Float32Array(maxParticles * 4),\n 4\n ),\n // (orbitalOffset.x, .y, .z, isActive)\n orbitalIsActive: new StorageBufferAttribute(\n new Float32Array(maxParticles * 4),\n 4\n ),\n // Curve data + emit queue tail (single buffer, 8th binding)\n curveData: new StorageBufferAttribute(combined, 1),\n };\n}\n\n// ─── CPU → GPU Sync Helpers (per-particle init data in curveData tail) ──────\n\n/** Per-pipeline frame-local emit count, keyed by curveData buffer identity. */\nconst _emitCounts = new WeakMap<StorageBufferAttribute, number>();\n/** Per-pipeline stored curveDataLength for init data offset. */\nconst _curveDataLengths = new WeakMap<StorageBufferAttribute, number>();\n/** Particle indices emitted this frame. */\nconst _currentEmitIndices = new WeakMap<StorageBufferAttribute, number[]>();\n/** Particle indices emitted in the previous frame (need their initFlags cleared). */\nconst _previousEmitIndices = new WeakMap<StorageBufferAttribute, number[]>();\n\n/**\n * Writes init data for a newly emitted particle into its per-particle slot\n * in the curveData buffer tail.\n *\n * The compute shader checks `initFlag` (offset 3 within each particle's slot)\n * and copies init data into the main storage buffers in O(1) per particle.\n *\n * Also writes to the CPU-only `startValues` and `startColorsExt` arrays (safe\n * to upload because the GPU only reads them).\n */\nexport function writeParticleToModifierBuffers(\n buffers: ModifierStorageBuffers,\n index: number,\n data: {\n position: { x: number; y: number; z: number };\n velocity: { x: number; y: number; z: number };\n startLifetime: number;\n colorA: number;\n size: number;\n rotation: number;\n colorR: number;\n colorG: number;\n colorB: number;\n startSize: number;\n startOpacity: number;\n startColorR: number;\n startColorG: number;\n startColorB: number;\n startFrame: number;\n rotationSpeed: number;\n noiseOffset: number;\n orbitalOffset: { x: number; y: number; z: number };\n }\n): void {\n // ── Write to per-particle init slot in curveData tail ──\n const curveLen = _curveDataLengths.get(buffers.curveData) ?? 0;\n const arr = buffers.curveData.array as Float32Array;\n const base = curveLen + index * INIT_STRIDE;\n\n // vec4: position.xyz + initFlag\n arr[base] = data.position.x;\n arr[base + 1] = data.position.y;\n arr[base + 2] = data.position.z;\n arr[base + 3] = 1.0; // initFlag = 1 (needs init)\n // vec4: velocity.xyz + padding\n arr[base + 4] = data.velocity.x;\n arr[base + 5] = data.velocity.y;\n arr[base + 6] = data.velocity.z;\n arr[base + 7] = 0;\n // vec4: color RGBA\n arr[base + 8] = data.colorR;\n arr[base + 9] = data.colorG;\n arr[base + 10] = data.colorB;\n arr[base + 11] = data.colorA;\n // vec4: particleState (lifetime=0, size, rotation, startFrame)\n arr[base + 12] = 0;\n arr[base + 13] = data.size;\n arr[base + 14] = data.rotation;\n arr[base + 15] = data.startFrame;\n // vec4: orbitalIsActive (offsetX, offsetY, offsetZ, isActive=1)\n arr[base + 16] = data.orbitalOffset.x;\n arr[base + 17] = data.orbitalOffset.y;\n arr[base + 18] = data.orbitalOffset.z;\n arr[base + 19] = 1.0;\n // vec4: startValues (startLifetime, startSize, startOpacity, startColorR)\n arr[base + 20] = data.startLifetime;\n arr[base + 21] = data.startSize;\n arr[base + 22] = data.startOpacity;\n arr[base + 23] = data.startColorR;\n // vec4: startColorsExt (startColorG, startColorB, rotationSpeed, noiseOffset)\n arr[base + 24] = data.startColorG;\n arr[base + 25] = data.startColorB;\n arr[base + 26] = data.rotationSpeed;\n arr[base + 27] = data.noiseOffset;\n\n _emitCounts.set(\n buffers.curveData,\n (_emitCounts.get(buffers.curveData) ?? 0) + 1\n );\n\n // Track this particle's index so its initFlag can be cleared in the CPU\n // array before the next upload (preventing re-initialization from stale data).\n let indices = _currentEmitIndices.get(buffers.curveData);\n if (!indices) {\n indices = [];\n _currentEmitIndices.set(buffers.curveData, indices);\n }\n indices.push(index);\n\n // ── Write to CPU-side arrays (kept in sync for diagnostics / fallback) ──\n // These arrays are NOT uploaded to the GPU (no needsUpdate); the compute\n // shader's init block scatters the values from the curveData init slot.\n const i4 = index * 4;\n\n const svArr = buffers.startValues.array as Float32Array;\n svArr[i4] = data.startLifetime;\n svArr[i4 + 1] = data.startSize;\n svArr[i4 + 2] = data.startOpacity;\n svArr[i4 + 3] = data.startColorR;\n\n const sceArr = buffers.startColorsExt.array as Float32Array;\n sceArr[i4] = data.startColorG;\n sceArr[i4 + 1] = data.startColorB;\n sceArr[i4 + 2] = data.rotationSpeed;\n sceArr[i4 + 3] = data.noiseOffset;\n}\n\n/**\n * Registers the curveDataLength for a buffer so the init data helpers know\n * where the per-particle init region starts. Called once during pipeline creation.\n */\nexport function registerCurveDataLength(\n buffers: ModifierStorageBuffers,\n curveDataLength: number\n): void {\n _curveDataLengths.set(buffers.curveData, curveDataLength);\n}\n\n/**\n * Flushes pending init data to the GPU and resets the frame-local counter.\n *\n * Must be called once per frame **before** compute dispatch.\n *\n * @returns The number of emits flushed (for diagnostics).\n */\nexport function flushEmitQueue(buffers: ModifierStorageBuffers): number {\n const count = _emitCounts.get(buffers.curveData) ?? 0;\n const curveLen = _curveDataLengths.get(buffers.curveData) ?? 0;\n const arr = buffers.curveData.array as Float32Array;\n\n // Clear initFlags for particles emitted in the PREVIOUS frame that are\n // NOT being re-emitted this frame. This is O(emittedLastFrame) instead\n // of the old O(maxParticles) full-scan approach, while preserving the\n // same correctness guarantee:\n //\n // A particle that dies and is re-emitted within a single frame keeps its\n // fresh initFlag=1 because it appears in the current set and is excluded\n // from clearing. This avoids the race condition where a delayed clear\n // from the first emission would overwrite the fresh initFlag=1 from the\n // re-emission — causing the GPU to never initialise the particle, which\n // then rendered with stale data (wrong color/position).\n const current = _currentEmitIndices.get(buffers.curveData);\n const previous = _previousEmitIndices.get(buffers.curveData);\n\n let clearedAny = false;\n\n if (previous && previous.length > 0) {\n const currentSet = current && current.length > 0 ? new Set(current) : null;\n for (let i = 0; i < previous.length; i++) {\n const p = previous[i];\n if (!currentSet || !currentSet.has(p)) {\n const flagOffset = curveLen + p * INIT_STRIDE + 3;\n if (arr[flagOffset] > 0.5) {\n arr[flagOffset] = 0;\n clearedAny = true;\n }\n }\n }\n }\n\n if (count > 0 || clearedAny) {\n buffers.curveData.needsUpdate = true;\n }\n\n // NOTE: startValues and startColorsExt are NO LONGER uploaded via\n // needsUpdate here. Their init data is now carried inside each\n // particle's curveData init slot and scattered to the GPU storage\n // buffers by the compute shader's init block. This prevents a\n // full-buffer upload from overwriting startValues of particles that\n // are still alive on the GPU but have already been recycled on the\n // CPU (due to CPU/GPU death-timing desync).\n\n // Rotate: current becomes previous for next frame\n if (current && current.length > 0) {\n // Copy current into previous (reuse array if possible)\n let prevArr = _previousEmitIndices.get(buffers.curveData);\n if (!prevArr) {\n prevArr = [];\n _previousEmitIndices.set(buffers.curveData, prevArr);\n }\n prevArr.length = current.length;\n for (let i = 0; i < current.length; i++) {\n prevArr[i] = current[i];\n }\n current.length = 0;\n } else {\n // No emissions this frame — clear previous\n const prevArr = _previousEmitIndices.get(buffers.curveData);\n if (prevArr) prevArr.length = 0;\n if (current) current.length = 0;\n }\n\n _emitCounts.set(buffers.curveData, 0);\n return count;\n}\n\n/**\n * Deactivates a particle on the CPU side for freeList management.\n *\n * The GPU compute shader handles the actual deactivation (zeroing isActive\n * and color) via its death check. This function only updates CPU bookkeeping\n * arrays without triggering a buffer upload that would overwrite GPU state.\n */\nexport function deactivateParticleInModifierBuffers(\n _buffers: ModifierStorageBuffers,\n _index: number\n): void {\n // No-op: GPU handles deactivation in the compute shader death check.\n // Previously this wrote to orbitalIsActive and color buffers with\n // needsUpdate = true, which caused full-buffer uploads that overwrote\n // GPU-computed values for all particles.\n}\n\n// ─── Curve Lookup Helper ─────────────────────────────────────────────────────\n\n/**\n * Creates a TSL function that performs a linear-interpolated lookup into the\n * baked curve storage buffer.\n *\n * @param sCurveData - Storage buffer node for all baked curves.\n * @returns A TSL function: (curveIndex: int, t: float) => float\n */\nfunction createCurveLookup(sCurveData: ShaderNodeObject<Node>) {\n return Fn(\n ({\n curveIndex,\n t,\n }: {\n curveIndex: ShaderNodeObject<Node>;\n t: ShaderNodeObject<Node>;\n }) => {\n const clamped = tslMin(t, float(1.0));\n const pos = clamped.mul(CURVE_RESOLUTION - 1);\n const idx0 = floor(pos);\n const f = fract(pos);\n\n const base = curveIndex.mul(CURVE_RESOLUTION);\n const v0 = sCurveData.element(base.add(idx0));\n const v1 = sCurveData.element(\n base.add(tslMin(idx0.add(1.0), float(CURVE_RESOLUTION - 1)))\n );\n\n return mix(v0, v1, f);\n }\n );\n}\n\n// ─── Compute Shader ──────────────────────────────────────────────────────────\n\n/**\n * Creates the unified GPU compute pipeline that handles both core physics\n * AND modifiers in a single dispatch.\n */\nexport function createModifierComputeUpdate(\n buffers: ModifierStorageBuffers,\n maxParticles: number,\n curveMap: BakedCurveMap,\n flags: ModifierFlags,\n forceFieldCount = 0,\n collisionPlaneCount = 0\n): ModifierComputePipeline {\n // ── Per-frame uniforms ──\n\n const uDelta = uniform(float(0));\n const uDeltaMs = uniform(float(0));\n const uGravityVelocity = uniform(new Vector3(0, 0, 0));\n const uWorldPositionChange = uniform(new Vector3(0, 0, 0));\n const uSimSpaceWorld = uniform(float(0));\n const uNoiseStrength = uniform(float(0));\n const uNoisePower = uniform(float(0));\n const uNoiseFrequency = uniform(float(1));\n const uNoisePosAmount = uniform(float(0));\n const uNoiseRotAmount = uniform(float(0));\n const uNoiseSizeAmount = uniform(float(0));\n\n // ── Storage buffer nodes (8 bindings, within WebGPU per-stage limit) ──\n\n const sPosition = storage(buffers.position, 'vec4', maxParticles);\n const sVelocity = storage(buffers.velocity, 'vec4', maxParticles);\n const sColor = storage(buffers.color, 'vec4', maxParticles);\n // particleState: (lifetime, size, rotation, startFrame)\n const sParticleState = storage(buffers.particleState, 'vec4', maxParticles);\n // startValues: (startLifetime, startSize, startOpacity, startColorR)\n const sStartValues = storage(buffers.startValues, 'vec4', maxParticles);\n // startColorsExt: (startColorG, startColorB, rotationSpeed, noiseOffset)\n const sStartColorsExt = storage(buffers.startColorsExt, 'vec4', maxParticles);\n // orbitalIsActive: (offsetX, offsetY, offsetZ, isActive)\n const sOrbitalIsActive = storage(\n buffers.orbitalIsActive,\n 'vec4',\n maxParticles\n );\n // curveData + per-particle init data tail + force field data (single buffer)\n const sCurveData = storage(\n buffers.curveData,\n 'float',\n buffers.curveData.array.length\n );\n\n // Per-particle init data layout constants (compile-time offsets into sCurveData)\n const curveLen = Math.max(curveMap.data.length, 1);\n\n const lookupCurve = createCurveLookup(sCurveData);\n\n // ── Force field nodes (reads from curveData tail, no extra binding) ──\n const forceFieldOffset = curveLen + maxParticles * INIT_STRIDE;\n const forceFieldNodes = flags.forceFields\n ? createForceFieldTSL(sCurveData, forceFieldOffset, forceFieldCount)\n : null;\n\n // ── Collision plane nodes (reads from curveData tail, after force fields) ──\n const ffSize = flags.forceFields ? FORCE_FIELD_DATA_SIZE : 0;\n const collisionPlaneOffset = forceFieldOffset + ffSize;\n const collisionPlaneNodes = flags.collisionPlanes\n ? createCollisionPlaneTSL(\n sCurveData,\n collisionPlaneOffset,\n collisionPlaneCount\n )\n : null;\n\n // ── Compute kernel ──\n //\n // Packed field mapping:\n // particleState: x=lifetime, y=size, z=rotation, w=startFrame\n // startValues: x=startLifetime, y=startSize, z=startOpacity, w=startColorR\n // startColorsExt: x=startColorG, y=startColorB, z=rotationSpeed, w=noiseOffset\n // orbitalIsActive: xyz=orbitalOffset, w=isActive\n\n const computeKernel = Fn(() => {\n const i = instanceIndex;\n\n // ── Per-particle init: O(1) lookup ──\n // Each particle has a fixed slot in curveData at:\n // base = curveLen + i * INIT_STRIDE\n // If initFlag (offset 3) is 1.0, copy init data into main buffers.\n const initBase = i.mul(INIT_STRIDE).add(curveLen);\n\n const initFlag = sCurveData.element(initBase.add(3));\n If(initFlag.greaterThan(0.5), () => {\n // Position (vec4: xyz + padding)\n sPosition\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase),\n sCurveData.element(initBase.add(1)),\n sCurveData.element(initBase.add(2)),\n 0\n )\n );\n // Velocity (vec4: xyz + padding)\n sVelocity\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(4)),\n sCurveData.element(initBase.add(5)),\n sCurveData.element(initBase.add(6)),\n 0\n )\n );\n // Color (vec4: RGBA)\n sColor\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(8)),\n sCurveData.element(initBase.add(9)),\n sCurveData.element(initBase.add(10)),\n sCurveData.element(initBase.add(11))\n )\n );\n // particleState (vec4: lifetime=0, size, rotation, startFrame)\n sParticleState\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(12)),\n sCurveData.element(initBase.add(13)),\n sCurveData.element(initBase.add(14)),\n sCurveData.element(initBase.add(15))\n )\n );\n // orbitalIsActive (vec4: offsetXYZ, isActive=1)\n sOrbitalIsActive\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(16)),\n sCurveData.element(initBase.add(17)),\n sCurveData.element(initBase.add(18)),\n sCurveData.element(initBase.add(19))\n )\n );\n // startValues (vec4: startLifetime, startSize, startOpacity, startColorR)\n sStartValues\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(20)),\n sCurveData.element(initBase.add(21)),\n sCurveData.element(initBase.add(22)),\n sCurveData.element(initBase.add(23))\n )\n );\n // startColorsExt (vec4: startColorG, startColorB, rotationSpeed, noiseOffset)\n sStartColorsExt\n .element(i)\n .assign(\n vec4(\n sCurveData.element(initBase.add(24)),\n sCurveData.element(initBase.add(25)),\n sCurveData.element(initBase.add(26)),\n sCurveData.element(initBase.add(27))\n )\n );\n\n // Clear the init flag so this particle isn't re-initialized next frame.\n // The CPU array was already uploaded this frame; mutating the GPU copy\n // here is fine because the CPU will only touch this slot again when\n // the particle is re-emitted (at which point it writes initFlag=1 again).\n sCurveData.element(initBase.add(3)).assign(float(0));\n });\n\n // Read orbitalIsActive to check isActive (w component).\n // NOTE: TSL `return` inside an If callback only exits the JS callback,\n // it does NOT generate a WGSL `return`. We must wrap all active-particle\n // logic inside a positive If guard instead.\n const oiaVec = sOrbitalIsActive.element(i).toVar();\n If(oiaVec.w.greaterThanEqual(float(0.5)), () => {\n // Read packed state\n // Position/velocity stored as vec4 (w=padding) for WebGPU alignment;\n // operate on .xyz only.\n const pos = sPosition.element(i).xyz.toVar();\n const vel = sVelocity.element(i).xyz.toVar();\n const ps = sParticleState.element(i).toVar();\n const sv = sStartValues.element(i);\n\n // Aliases for readability\n const life = ps.x; // lifetime\n const startLife = sv.x; // startLifetime\n\n // === CORE PHYSICS ===\n\n // Gravity\n vel.assign(vel.sub(vec3(uGravityVelocity).mul(uDelta)));\n\n // Force fields\n if (forceFieldNodes) {\n forceFieldNodes.apply({ pos, vel, delta: uDelta });\n }\n\n // World-space compensation\n If(uSimSpaceWorld.greaterThan(0.5), () => {\n pos.assign(pos.sub(vec3(uWorldPositionChange)));\n });\n\n // Velocity integration\n pos.assign(pos.add(vel.mul(uDelta)));\n\n // Collision planes — after position update, before modifiers\n if (collisionPlaneNodes) {\n collisionPlaneNodes.apply({\n pos,\n vel,\n oiaVec,\n sColorNode: sColor,\n ps,\n startLife,\n particleIdx: i,\n sOrbitalIsActiveNode: sOrbitalIsActive,\n });\n }\n\n // Lifetime percentage for modifiers (computed before lifetime update\n // to match the CPU path, which reads lifetime before incrementing it)\n const lifePct = tslMin(ps.x.div(startLife), float(1.0));\n\n // Lifetime update\n ps.x.assign(ps.x.add(uDeltaMs));\n\n // === MODIFIERS ===\n\n // 1. Linear Velocity (curve-modulated)\n if (flags.linearVelocity) {\n const lvx =\n curveMap.linearVelX >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.linearVelX),\n t: lifePct,\n })\n : float(0.0);\n const lvy =\n curveMap.linearVelY >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.linearVelY),\n t: lifePct,\n })\n : float(0.0);\n const lvz =\n curveMap.linearVelZ >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.linearVelZ),\n t: lifePct,\n })\n : float(0.0);\n pos.assign(pos.add(vec3(lvx, lvy, lvz).mul(uDelta)));\n }\n\n // 2. Orbital Velocity\n if (flags.orbitalVelocity) {\n const offset = vec3(oiaVec.x, oiaVec.y, oiaVec.z).toVar();\n pos.assign(pos.sub(offset));\n\n const ovx =\n curveMap.orbitalVelX >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.orbitalVelX),\n t: lifePct,\n })\n : float(0.0);\n const ovy =\n curveMap.orbitalVelY >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.orbitalVelY),\n t: lifePct,\n })\n : float(0.0);\n const ovz =\n curveMap.orbitalVelZ >= 0\n ? lookupCurve({\n curveIndex: float(curveMap.orbitalVelZ),\n t: lifePct,\n })\n : float(0.0);\n\n // CPU uses Euler(speedX, speedZ, speedY, 'XYZ'). Intrinsic XYZ is\n // equivalent to extrinsic Z→Y→X, so we apply:\n // 1. Z rotation with angle = speedY (Euler.z)\n // 2. Y rotation with angle = speedZ (Euler.y)\n // 3. X rotation with angle = speedX (Euler.x)\n // We compute the full rotation into fresh variables to avoid\n // read-after-write hazards in TSL assign chains.\n const ax = ovx.mul(uDelta); // speedX\n const ay = ovz.mul(uDelta); // speedZ → Euler.y\n const az = ovy.mul(uDelta); // speedY → Euler.z\n\n // Step 1: Rotate around Z\n const cosAz = cos(az);\n const sinAz = sin(az);\n const zx = offset.x.mul(cosAz).sub(offset.y.mul(sinAz));\n const zy = offset.x.mul(sinAz).add(offset.y.mul(cosAz));\n const zz = offset.z;\n\n // Step 2: Rotate around Y (using Z-rotated values)\n const cosAy = cos(ay);\n const sinAy = sin(ay);\n const yx = zx.mul(cosAy).add(zz.mul(sinAy));\n const yy = zy;\n const yz = zx.negate().mul(sinAy).add(zz.mul(cosAy));\n\n // Step 3: Rotate around X (using Y-rotated values)\n const cosAx = cos(ax);\n const sinAx = sin(ax);\n const fx = yx;\n const fy = yy.mul(cosAx).sub(yz.mul(sinAx));\n const fz = yy.mul(sinAx).add(yz.mul(cosAx));\n\n offset.assign(vec3(fx, fy, fz));\n\n // Write back orbital offset (xyz), keep isActive (w)\n oiaVec.x.assign(offset.x);\n oiaVec.y.assign(offset.y);\n oiaVec.z.assign(offset.z);\n pos.assign(pos.add(offset));\n }\n\n // 3. Size Over Lifetime (ps.y = size, sv.y = startSize)\n if (flags.sizeOverLifetime && curveMap.sizeOverLifetime >= 0) {\n const multiplier = lookupCurve({\n curveIndex: float(curveMap.sizeOverLifetime),\n t: lifePct,\n });\n ps.y.assign(sv.y.mul(multiplier));\n }\n\n // 4. Opacity Over Lifetime (sv.z = startOpacity)\n if (flags.opacityOverLifetime && curveMap.opacityOverLifetime >= 0) {\n const multiplier = lookupCurve({\n curveIndex: float(curveMap.opacityOverLifetime),\n t: lifePct,\n });\n const col = sColor.element(i).toVar();\n col.w.assign(sv.z.mul(multiplier));\n sColor.element(i).assign(col);\n }\n\n // 5. Color Over Lifetime\n // sv.w = startColorR, startColorsExt.x = startColorG, .y = startColorB\n if (flags.colorOverLifetime) {\n const col = sColor.element(i).toVar();\n const sce = sStartColorsExt.element(i);\n if (curveMap.colorR >= 0) {\n const rMul = lookupCurve({\n curveIndex: float(curveMap.colorR),\n t: lifePct,\n });\n col.x.assign(sv.w.mul(rMul));\n }\n if (curveMap.colorG >= 0) {\n const gMul = lookupCurve({\n curveIndex: float(curveMap.colorG),\n t: lifePct,\n });\n col.y.assign(sce.x.mul(gMul));\n }\n if (curveMap.colorB >= 0) {\n const bMul = lookupCurve({\n curveIndex: float(curveMap.colorB),\n t: lifePct,\n });\n col.z.assign(sce.y.mul(bMul));\n }\n sColor.element(i).assign(col);\n }\n\n // 6. Rotation Over Lifetime (startColorsExt.z = rotationSpeed, ps.z = rotation)\n if (flags.rotationOverLifetime) {\n const sce = sStartColorsExt.element(i);\n ps.z.assign(ps.z.add(sce.z.mul(uDelta).mul(float(0.02))));\n }\n\n // 7. Noise (startColorsExt.w = noiseOffset)\n if (flags.noise) {\n const sce = sStartColorsExt.element(i);\n // Match CPU FBM input scaling: FBM internally multiplies by `this._scale = frequency`\n const noisePos = lifePct\n .add(sce.w)\n .mul(10.0)\n .mul(uNoiseStrength)\n .mul(uNoiseFrequency);\n\n const noiseX = snoise3D({ v: vec3(noisePos, float(0), float(0)) });\n const noiseY = snoise3D({\n v: vec3(noisePos, noisePos, float(0)),\n });\n const noiseZ = snoise3D({\n v: vec3(noisePos, noisePos, noisePos),\n });\n\n // Apply to position\n pos.assign(\n pos.add(\n vec3(noiseX, noiseY, noiseZ).mul(uNoisePower).mul(uNoisePosAmount)\n )\n );\n\n // Apply to rotation (ps.z)\n If(uNoiseRotAmount.greaterThan(0.001), () => {\n ps.z.assign(ps.z.add(noiseX.mul(uNoisePower).mul(uNoiseRotAmount)));\n });\n\n // Apply to size (ps.y)\n If(uNoiseSizeAmount.greaterThan(0.001), () => {\n ps.y.assign(ps.y.add(noiseX.mul(uNoisePower).mul(uNoiseSizeAmount)));\n });\n }\n\n // === WRITE BACK ===\n\n sPosition.element(i).assign(vec4(pos, 0));\n sVelocity.element(i).assign(vec4(vel, 0));\n sParticleState.element(i).assign(ps);\n sOrbitalIsActive.element(i).assign(oiaVec);\n\n // Death check\n If(ps.x.greaterThan(startLife), () => {\n // Set isActive = 0 and zero color\n const deadOia = sOrbitalIsActive.element(i).toVar();\n deadOia.w.assign(float(0));\n sOrbitalIsActive.element(i).assign(deadOia);\n sColor.element(i).assign(vec4(0));\n });\n });\n });\n\n const computeNode = compute(computeKernel(), maxParticles);\n\n return {\n computeNode,\n uniforms: {\n delta: uDelta,\n deltaMs: uDeltaMs,\n gravityVelocity: uGravityVelocity,\n worldPositionChange: uWorldPositionChange,\n simulationSpaceWorld: uSimSpaceWorld,\n noiseStrength: uNoiseStrength,\n noisePower: uNoisePower,\n noiseFrequency: uNoiseFrequency,\n noisePositionAmount: uNoisePosAmount,\n noiseRotationAmount: uNoiseRotAmount,\n noiseSizeAmount: uNoiseSizeAmount,\n },\n buffers,\n curveDataLength: curveLen,\n /** Force field offset and count uniform (null if no force fields). */\n forceFieldInfo: forceFieldNodes\n ? {\n offset: forceFieldOffset,\n countUniform: forceFieldNodes.countUniform,\n }\n : null,\n /** Collision plane offset and count uniform (null if no collision planes). */\n collisionPlaneInfo: collisionPlaneNodes\n ? {\n offset: collisionPlaneOffset,\n countUniform: collisionPlaneNodes.countUniform,\n }\n : null,\n };\n}\n\n// ─── Pure-JS orbital rotation (mirrors GPU TSL logic) ───────────────────────\n\n/**\n * Applies the same orbital velocity rotation used by the GPU compute shader.\n *\n * The CPU path uses `Euler(speedX, speedZ, speedY, 'XYZ')` which is intrinsic\n * XYZ = extrinsic Z→Y→X. This function replicates that exact transformation\n * so it can be unit-tested against `Vector3.applyEuler`.\n *\n * @param offset - Mutable orbital offset {x, y, z}. Modified in-place.\n * @param speedX - Orbital velocity around user-facing X axis.\n * @param speedY - Orbital velocity around user-facing Y axis.\n * @param speedZ - Orbital velocity around user-facing Z axis.\n * @param delta - Frame delta in seconds.\n */\nexport function applyOrbitalRotation(\n offset: { x: number; y: number; z: number },\n speedX: number,\n speedY: number,\n speedZ: number,\n delta: number\n): void {\n // Axis-angle mapping matches CPU: Euler(speedX, speedZ, speedY, 'XYZ')\n const ax = speedX * delta; // Euler.x\n const ay = speedZ * delta; // Euler.y\n const az = speedY * delta; // Euler.z\n\n // Extrinsic Z → Y → X (equivalent to intrinsic XYZ)\n\n // Step 1: Rotate around Z\n const cosAz = Math.cos(az);\n const sinAz = Math.sin(az);\n const zx = offset.x * cosAz - offset.y * sinAz;\n const zy = offset.x * sinAz + offset.y * cosAz;\n const zz = offset.z;\n\n // Step 2: Rotate around Y\n const cosAy = Math.cos(ay);\n const sinAy = Math.sin(ay);\n const yx = zx * cosAy + zz * sinAy;\n const yy = zy;\n const yz = -zx * sinAy + zz * cosAy;\n\n // Step 3: Rotate around X\n const cosAx = Math.cos(ax);\n const sinAx = Math.sin(ax);\n offset.x = yx;\n offset.y = yy * cosAx - yz * sinAx;\n offset.z = yy * sinAx + yz * cosAx;\n}\n","/**\n * Interleaved scalar buffer layout constants.\n *\n * All per-particle float attributes (except position and quat) are packed\n * into a single InterleavedBuffer to stay within the WebGPU vertex buffer\n * limit of 8.\n *\n * Buffer layout per particle (stride = 10 floats):\n * ```\n * [isActive, lifetime, startLifetime, startFrame, size, rotation, colorR, colorG, colorB, colorA]\n * ```\n */\n\n/** Number of float32 elements per particle in the interleaved scalar buffer. */\nexport const SCALAR_STRIDE = 10;\n\n/** Offset for the isActive flag (0 = inactive, 1 = active). */\nexport const S_IS_ACTIVE = 0;\n\n/** Offset for the current lifetime of the particle in milliseconds. */\nexport const S_LIFETIME = 1;\n\n/** Offset for the total lifetime of the particle in milliseconds. */\nexport const S_START_LIFETIME = 2;\n\n/** Offset for the texture sheet animation start frame index. */\nexport const S_START_FRAME = 3;\n\n/** Offset for the particle size. */\nexport const S_SIZE = 4;\n\n/** Offset for the particle rotation (radians). */\nexport const S_ROTATION = 5;\n\n/** Offset for the red color channel (0..1). */\nexport const S_COLOR_R = 6;\n\n/** Offset for the green color channel (0..1). */\nexport const S_COLOR_G = 7;\n\n/** Offset for the blue color channel (0..1). */\nexport const S_COLOR_B = 8;\n\n/** Offset for the alpha (opacity) channel (0..1). */\nexport const S_COLOR_A = 9;\n\n// ─── Shared rendering constants ─────────────────────────────────────────────\n\n/** Scale factor converting particle size to pixel-equivalent point size. */\nexport const POINT_SIZE_SCALE = 100.0;\n\n/** Minimum alpha below which fragments are discarded. */\nexport const ALPHA_DISCARD_THRESHOLD = 0.001;\n","/**\n * Shared TSL helper nodes used by multiple particle renderer materials.\n *\n * These functions create reusable shader node fragments for:\n * - Texture sheet animation (frame index + sprite-sheet UV calculation)\n * - Soft particle depth fade\n * - Background color discard\n */\nimport { DataTexture, NoColorSpace, Vector3, type Texture } from 'three';\nimport {\n Fn,\n vec2,\n vec3,\n vec4,\n float,\n uniform,\n texture,\n abs,\n floor,\n mod,\n max,\n min,\n round,\n smoothstep,\n screenUV,\n Discard,\n If,\n length,\n sRGBTransferEOTF,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\n\nimport type * as THREE from 'three';\n\n/** 1x1 white placeholder texture for null uniform slots. */\nlet _dummyTexture: DataTexture | null = null;\nexport function getDummyTexture(): DataTexture {\n if (!_dummyTexture) {\n _dummyTexture = new DataTexture(new Uint8Array([255, 255, 255, 255]), 1, 1);\n _dummyTexture.needsUpdate = true;\n }\n return _dummyTexture;\n}\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type SharedUniforms = {\n map: { value: THREE.Texture | null };\n elapsed: { value: number };\n fps: { value: number };\n useFPSForFrameIndex: { value: boolean };\n tiles: { value: THREE.Vector2 };\n discardBackgroundColor: { value: boolean };\n backgroundColor: { value: { r: number; g: number; b: number } };\n backgroundColorTolerance: { value: number };\n softParticlesEnabled: { value: boolean };\n softParticlesIntensity: { value: number };\n sceneDepthTexture: { value: THREE.Texture | null };\n cameraNearFar: { value: THREE.Vector2 };\n [key: string]: { value: unknown };\n};\n\n// ─── Uniform creators ────────────────────────────────────────────────────────\n\nexport function createParticleUniforms(sharedUniforms: SharedUniforms) {\n const dummy = getDummyTexture();\n\n // Disable automatic sRGB→linear hardware conversion on particle textures.\n // The GLSL ShaderMaterial path reads raw sRGB values via texture2D() and\n // writes them directly without output color-space conversion. To match\n // that behaviour in the TSL/WebGPU path (which applies a full-screen\n // linear→sRGB output pass), we must feed raw values into the shader so\n // the renderer's output transform produces identical results.\n const map = (sharedUniforms.map.value ?? dummy) as Texture;\n if (map) map.colorSpace = NoColorSpace;\n\n return {\n uMap: map,\n uElapsed: uniform(float(sharedUniforms.elapsed.value)),\n uFps: uniform(float(sharedUniforms.fps.value)),\n uUseFPSForFrameIndex: uniform(\n float(sharedUniforms.useFPSForFrameIndex.value ? 1 : 0)\n ),\n uTiles: uniform(sharedUniforms.tiles.value),\n uDiscardBg: uniform(\n float(sharedUniforms.discardBackgroundColor.value ? 1 : 0)\n ),\n uBgColor: uniform(\n new Vector3(\n sharedUniforms.backgroundColor.value.r,\n sharedUniforms.backgroundColor.value.g,\n sharedUniforms.backgroundColor.value.b\n )\n ),\n uBgTolerance: uniform(float(sharedUniforms.backgroundColorTolerance.value)),\n uSoftEnabled: uniform(\n float(sharedUniforms.softParticlesEnabled.value ? 1 : 0)\n ),\n uSoftIntensity: uniform(float(sharedUniforms.softParticlesIntensity.value)),\n uSceneDepthTex: sharedUniforms.sceneDepthTexture.value ?? dummy,\n uCameraNearFar: uniform(sharedUniforms.cameraNearFar.value),\n };\n}\n\n// ─── Texture sheet animation ─────────────────────────────────────────────────\n\n/**\n * Computes the frame index for texture sheet animation.\n *\n * Replicates the GLSL logic:\n * frameIndex = startFrame + (useFPS ? lifetime/1000 * fps : floor(lifePercent * totalFrames))\n */\nexport const computeFrameIndex = Fn(\n ({\n vLifetime,\n vStartLifetime,\n vStartFrame,\n uFps,\n uUseFPSForFrameIndex,\n uTiles,\n }: Record<string, ShaderNodeObject<Node>>) => {\n const totalFrames = uTiles.x.mul(uTiles.y);\n const lifePercent = min(vLifetime.div(vStartLifetime), float(1.0));\n\n // FPS-based: (lifetime / 1000) * fps\n const fpsBased = max(vLifetime.div(1000.0).mul(uFps), float(0.0));\n\n // Lifetime-based: floor(lifePercent * totalFrames), clamped\n const lifetimeBased = max(\n min(floor(lifePercent.mul(totalFrames)), totalFrames.sub(1.0)),\n float(0.0)\n );\n\n // Select based on mode (uUseFPSForFrameIndex: 1.0 = FPS, 0.0 = lifetime)\n // When fps == 0 in FPS mode, use 0\n const fpsResult = uFps.equal(0.0).select(float(0.0), fpsBased);\n const frameOffset = uUseFPSForFrameIndex\n .greaterThan(0.5)\n .select(fpsResult, lifetimeBased);\n\n return round(vStartFrame).add(frameOffset);\n }\n);\n\n/**\n * Computes sprite-sheet UV coordinates from a base UV and the frame index.\n *\n * Replicates:\n * spriteX = floor(mod(frameIndex, tiles.x))\n * spriteY = floor(mod(frameIndex / tiles.x, tiles.y))\n * uv = baseUV / tiles + sprite / tiles\n */\nexport const computeSpriteSheetUV = Fn(\n ({ baseUV, frameIndex, uTiles }: Record<string, ShaderNodeObject<Node>>) => {\n const spriteX = floor(mod(frameIndex, uTiles.x));\n const spriteY = floor(mod(frameIndex.div(uTiles.x), uTiles.y));\n\n return vec2(\n baseUV.x.div(uTiles.x).add(spriteX.div(uTiles.x)),\n baseUV.y.div(uTiles.y).add(spriteY.div(uTiles.y))\n );\n }\n);\n\n// ─── Soft particles ──────────────────────────────────────────────────────────\n\n/**\n * Linearizes a depth buffer sample from [0,1] NDC to view-space distance.\n */\nexport const linearizeDepth = Fn(\n ({ depthSample, near, far }: Record<string, ShaderNodeObject<Node>>) => {\n const zNdc = depthSample.mul(2.0).sub(1.0);\n return near\n .mul(2.0)\n .mul(far)\n .div(far.add(near).sub(zNdc.mul(far.sub(near))));\n }\n);\n\n/**\n * Computes the soft-particle alpha multiplier based on depth difference.\n * Returns 1.0 when soft particles are disabled.\n */\nexport const computeSoftParticleFade = Fn(\n ({\n viewZ,\n uSoftEnabled,\n uSoftIntensity,\n uSceneDepthTex,\n uCameraNearFar,\n }: Record<string, ShaderNodeObject<Node>>) => {\n const softFade = float(1.0).toVar();\n\n If(uSoftEnabled.greaterThan(0.5), () => {\n const depthSample = texture(uSceneDepthTex, screenUV).x;\n const sceneDepthLinear = linearizeDepth({\n depthSample,\n near: uCameraNearFar.x,\n far: uCameraNearFar.y,\n });\n const depthDiff = sceneDepthLinear.sub(viewZ);\n softFade.assign(smoothstep(float(0.0), uSoftIntensity, depthDiff));\n });\n\n return softFade;\n }\n);\n\n// ─── Background color discard ────────────────────────────────────────────────\n\n/**\n * Discards fragments whose texture color is close to the background color.\n */\nexport const applyBackgroundDiscard = Fn(\n ({\n texColor,\n uDiscardBg,\n uBgColor,\n uBgTolerance,\n }: Record<string, ShaderNodeObject<Node>>) => {\n If(uDiscardBg.greaterThan(0.5), () => {\n const diff = vec3(\n texColor.x.sub(uBgColor.x),\n texColor.y.sub(uBgColor.y),\n texColor.z.sub(uBgColor.z)\n );\n If(abs(length(diff)).lessThan(uBgTolerance), () => {\n Discard();\n });\n });\n }\n);\n\n// ─── Output color-space compensation ────────────────────────────────────────\n\n/**\n * Applies sRGB→linear (EOTF) to the RGB channels of a vec4 color.\n *\n * The GLSL ShaderMaterial path writes raw sRGB texture values directly to\n * the framebuffer — no output color-space conversion is applied because the\n * custom fragment shader omits `#include <colorspace_fragment>`.\n *\n * The WebGPU renderer, however, always runs a full-screen output pass that\n * performs a linear→sRGB conversion on the entire framebuffer. To produce\n * identical results we apply the *inverse* transform (sRGB→linear) on the\n * fragment output so the two cancel out: sRGB → linear → sRGB ≡ sRGB.\n */\nexport const compensateOutputSRGB = Fn(\n ({ color }: Record<string, ShaderNodeObject<Node>>) => {\n return vec4(sRGBTransferEOTF(color.rgb), color.a);\n }\n);\n","/**\n * TSL (Three Shading Language) material for the INSTANCED billboard particle renderer.\n *\n * Replicates the behavior of instanced-particle-vertex-shader.glsl.ts and\n * instanced-particle-fragment-shader.glsl.ts using TSL node-based materials.\n *\n * Key differences from the POINTS TSL material ({@link createPointSpriteTSLMaterial}):\n * - Uses `instanceOffset` (vec3) attribute for particle world-space position instead\n * of the built-in `position` node.\n * - All per-particle attributes use the `instance` prefix\n * (`instanceSize`, `instanceColorR`, …).\n * - The base geometry is a -0.5…0.5 quad; UV is derived from the quad vertex position\n * rather than `pointUV` / `gl_PointCoord`.\n * - Perspective-correct size scaling uses `cameraProjectionMatrix[1][1]` and a\n * `viewportHeight` uniform so that billboard quads match POINTS renderer pixel sizes.\n * - The billboard offset is applied in view space:\n * `mvPosition.xy += position.xy * perspectiveSize`\n * - Uses {@link MeshBasicNodeMaterial} because the instanced quads are rendered as a\n * `THREE.Mesh`, not a `THREE.Points`.\n */\nimport {\n Fn,\n attribute,\n vec2,\n vec4,\n float,\n uniform,\n modelViewMatrix,\n positionLocal,\n cameraProjectionMatrix,\n texture,\n cos,\n sin,\n Discard,\n If,\n length,\n varyingProperty,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\n\nimport {\n POINT_SIZE_SCALE,\n ALPHA_DISCARD_THRESHOLD,\n} from '../three-particles-constants.js';\nimport {\n type SharedUniforms,\n createParticleUniforms,\n computeFrameIndex,\n computeSpriteSheetUV,\n computeSoftParticleFade,\n applyBackgroundDiscard,\n compensateOutputSRGB,\n} from './tsl-shared.js';\nimport type * as THREE from 'three';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a TSL-based {@link MeshBasicNodeMaterial} that replicates the GLSL instanced\n * billboard particle shaders. Works with both WebGPURenderer (WGSL output) and\n * WebGLRenderer (GLSL output) when using the node material system.\n *\n * The material is designed for use with an `InstancedBufferGeometry` whose base mesh\n * is a unit quad (vertices at ±0.5 in X and Y). Per-particle data is supplied via\n * `THREE.InstancedBufferAttribute` arrays with the following names:\n *\n * | Attribute name | Type | Description |\n * |----------------------|---------|------------------------------------------|\n * | `instanceOffset` | vec3 | Particle world-space position |\n * | `instanceSize` | float | Uniform scale factor |\n * | `instanceColorR/G/B/A` | float | Per-particle RGBA colour |\n * | `instanceLifetime` | float | Remaining lifetime (ms) |\n * | `instanceStartLifetime` | float | Initial lifetime (ms) |\n * | `instanceRotation` | float | 2-D billboard rotation (radians) |\n * | `instanceStartFrame` | float | Starting frame for texture sheet anim |\n *\n * Size scaling matches the POINTS renderer exactly:\n * ```\n * perspectiveSize = instanceSize * 100.0 / dist\n * * (-mvPosition.z)\n * / (projectionMatrix[1][1] * viewportHeight * 0.5)\n * ```\n *\n * @param sharedUniforms - Live uniform values shared with the particle system.\n * @param rendererConfig - Blending / depth / transparency settings.\n * @returns A configured {@link MeshBasicNodeMaterial}.\n */\nexport function createInstancedBillboardTSLMaterial(\n sharedUniforms: SharedUniforms,\n rendererConfig: {\n transparent: boolean;\n blending: THREE.Blending;\n depthTest: boolean;\n depthWrite: boolean;\n },\n gpuCompute = false\n): MeshBasicNodeMaterial {\n const u = createParticleUniforms(sharedUniforms);\n\n // ── viewportHeight uniform ─────────────────────────────────────────────────\n //\n // The particle system's onBeforeRender hook updates `sharedUniforms.viewportHeight.value`\n // each frame. To propagate those writes into the TSL shader graph without\n // modifying the caller, we create the TSL uniform node and then back-patch\n // `sharedUniforms.viewportHeight` to point at the same node. From that\n // point on, `sharedUniforms.viewportHeight.value = x` directly mutates\n // `uViewportHeight.value`, keeping both paths (GLSL fallback and TSL) in sync.\n const uViewportHeight = uniform(\n typeof (sharedUniforms.viewportHeight as { value?: number })?.value ===\n 'number'\n ? (sharedUniforms.viewportHeight as { value: number }).value\n : 1.0\n );\n\n // Back-patch the shared uniform so the caller's live updates are reflected\n // in the TSL uniform node automatically.\n sharedUniforms.viewportHeight = uViewportHeight as unknown as {\n value: unknown;\n };\n\n // ── Per-instance attributes ────────────────────────────────────────────────\n\n /** Particle world-space centre (vec3). */\n const aInstanceOffset = attribute('instanceOffset');\n /** Packed RGBA color (vec4). */\n const aColor = attribute('instanceColor');\n\n // GPU compute uses packed vec4 buffers; CPU uses individual attributes\n const aParticleState = gpuCompute ? attribute('instanceParticleState') : null;\n const aStartValues = gpuCompute ? attribute('instanceStartValues') : null;\n // CPU fallback: individual attributes\n const aSize = gpuCompute ? null : attribute('instanceSize');\n const aLifetime = gpuCompute ? null : attribute('instanceLifetime');\n const aStartLifetime = gpuCompute ? null : attribute('instanceStartLifetime');\n const aRotation = gpuCompute ? null : attribute('instanceRotation');\n const aStartFrame = gpuCompute ? null : attribute('instanceStartFrame');\n\n // ── Varyings ───────────────────────────────────────────────────────────────\n\n const vColor = varyingProperty('vec4', 'vColor');\n const vLifetime = varyingProperty('float', 'vLifetime');\n const vStartLifetime = varyingProperty('float', 'vStartLifetime');\n const vRotation = varyingProperty('float', 'vRotation');\n const vStartFrame = varyingProperty('float', 'vStartFrame');\n /** View-space UV coordinates derived from the quad vertex position. */\n const vUv = varyingProperty('vec2', 'vUv');\n /** Positive view-space depth for soft-particle depth comparison. */\n const vViewZ = varyingProperty('float', 'vViewZ');\n\n // ── Vertex stage ───────────────────────────────────────────────────────────\n\n /**\n * Replicates the GLSL instanced vertex shader:\n *\n * 1. Transform `instanceOffset` (particle world position) into view space.\n * 2. Compute distance-based perspective scale so the billboard matches POINTS\n * renderer pixel sizes exactly.\n * 3. Offset the quad vertices in view space (`mvPosition.xy += position.xy * scale`).\n * 4. Project to clip space.\n * 5. Derive UV from quad vertex position (flip Y to match `gl_PointCoord` convention).\n *\n * In TSL's `positionNode` the returned value is the local-space position, and the\n * renderer applies the full MVP transform automatically. For instanced billboards\n * we need to manipulate the view-space position directly (to add the billboard\n * offset), so we work in view space and then multiply by the projection matrix\n * ourselves before returning.\n *\n * Because `positionNode` is expected to return the *local-space* position that TSL\n * will then transform via MVP, and we have already applied the full MVP here, we\n * must also suppress the automatic MVP multiplication. The recommended pattern for\n * this in TSL is to assign the result to `vertexNode` (which replaces clip-space\n * output directly) rather than `positionNode`. We therefore store the computed\n * clip-space position and expose it via the material's `vertexNode` property.\n */\n const vertexNode = Fn((): ShaderNodeObject<Node> => {\n // Early-out for dead particles: skip all transforms and emit a degenerate\n // clip-space position that produces zero-area triangles.\n const clipPos = vec4(0.0, 0.0, 0.0, 0.0).toVar();\n\n If(aColor.w.greaterThan(0.0), () => {\n // Populate varyings\n vColor.assign(aColor.toVar());\n if (gpuCompute) {\n // particleState: x=lifetime, y=size, z=rotation, w=startFrame\n vLifetime.assign(aParticleState!.x);\n vStartLifetime.assign(aStartValues!.x);\n vRotation.assign(aParticleState!.z);\n vStartFrame.assign(aParticleState!.w);\n } else {\n vLifetime.assign(aLifetime!);\n vStartLifetime.assign(aStartLifetime!);\n vRotation.assign(aRotation!);\n vStartFrame.assign(aStartFrame!);\n }\n\n // UV: quad vertex ranges from -0.5..0.5; remap to 0..1 and flip Y so the\n // top-left of the texture corresponds to the top-left of the billboard.\n // This matches gl_PointCoord which has Y running top-to-bottom.\n // vUv.x = position.x + 0.5\n // vUv.y = 0.5 - position.y\n vUv.assign(\n vec2(positionLocal.x.add(0.5), float(0.5).sub(positionLocal.y))\n );\n\n // Transform the particle world position into view space\n // Use .xyz to handle vec3→vec4 padding by WebGPU storage buffer alignment\n const mvPosition = modelViewMatrix\n .mul(vec4(aInstanceOffset.xyz, 1.0))\n .toVar();\n\n // Match POINTS renderer pixel size:\n // gl_PointSize = instanceSize * 100.0 / distance\n // Equivalent view-space billboard half-extent (in view units):\n // perspectiveSize = pointSizePx * (-mvPosition.z)\n // / (projectionMatrix[1][1] * viewportHeight * 0.5)\n // projectionMatrix[1][1] == cameraProjectionMatrix.element(1).element(1)\n const dist = length(mvPosition.xyz);\n const sizeVal = gpuCompute ? aParticleState!.y : aSize!;\n const pointSizePx = sizeVal.mul(POINT_SIZE_SCALE).div(dist);\n const projY = cameraProjectionMatrix.element(1).element(1);\n const perspectiveSize = pointSizePx\n .mul(mvPosition.z.negate())\n .div(projY.mul(uViewportHeight).mul(0.5));\n\n // Billboard: expand the quad in view space (no rotation here; rotation is\n // applied to UVs in the fragment stage, identical to the POINTS approach)\n mvPosition.x.addAssign(positionLocal.x.mul(perspectiveSize));\n mvPosition.y.addAssign(positionLocal.y.mul(perspectiveSize));\n\n vViewZ.assign(mvPosition.z.negate());\n\n // Project to clip space\n clipPos.assign(cameraProjectionMatrix.mul(mvPosition));\n });\n\n return clipPos;\n })();\n\n // ── Fragment stage ─────────────────────────────────────────────────────────\n\n /**\n * Replicates the GLSL instanced fragment shader:\n *\n * 1. Apply 2-D rotation to the UV (identical to POINTS material).\n * 2. Discard pixels outside the inscribed circle (radius > 0.5).\n * 3. Compute texture sheet frame index.\n * 4. Sample the sprite-sheet texture at the rotated, tile-offset UV.\n * 5. Background colour discard.\n * 6. Soft-particle depth fade.\n */\n const fragmentColor = Fn((): ShaderNodeObject<Node> => {\n const outColor = vColor.toVar();\n\n // Rotate vUv around (0.5, 0.5) to match the POINTS renderer behaviour\n const center = vec2(0.5, 0.5);\n const centered = vUv.sub(center);\n const cosR = cos(vRotation);\n const sinR = sin(vRotation);\n const rotated = vec2(\n centered.x.mul(cosR).add(centered.y.mul(sinR)),\n centered.x.mul(sinR).negate().add(centered.y.mul(cosR))\n );\n const rotatedUV = rotated.add(center);\n\n // Discard pixels outside the inscribed circle (dist > 0.5 from centre)\n const dist = length(rotatedUV.sub(center));\n If(dist.greaterThan(0.5), () => {\n Discard();\n });\n\n // Texture sheet animation — compute frame index\n const frameIndex = computeFrameIndex({\n vLifetime,\n vStartLifetime,\n vStartFrame,\n uFps: u.uFps,\n uUseFPSForFrameIndex: u.uUseFPSForFrameIndex,\n uTiles: u.uTiles,\n });\n\n // Compute sprite-sheet UV from rotated UV + tile offset\n const uvPoint = computeSpriteSheetUV({\n baseUV: rotatedUV,\n frameIndex,\n uTiles: u.uTiles,\n });\n\n // Sample texture\n const texColor = texture(u.uMap, uvPoint);\n outColor.assign(outColor.mul(texColor));\n\n // Background colour discard\n applyBackgroundDiscard({\n texColor,\n uDiscardBg: u.uDiscardBg,\n uBgColor: u.uBgColor,\n uBgTolerance: u.uBgTolerance,\n });\n\n // Soft particles — fade out fragments close to opaque scene geometry\n const softFade = computeSoftParticleFade({\n viewZ: vViewZ,\n uSoftEnabled: u.uSoftEnabled,\n uSoftIntensity: u.uSoftIntensity,\n uSceneDepthTex: u.uSceneDepthTex,\n uCameraNearFar: u.uCameraNearFar,\n });\n outColor.assign(vec4(outColor.xyz, outColor.w.mul(softFade)));\n Discard(outColor.w.lessThan(ALPHA_DISCARD_THRESHOLD));\n\n return compensateOutputSRGB({ color: outColor });\n })();\n\n // ── Material assembly ──────────────────────────────────────────────────────\n\n const material = new MeshBasicNodeMaterial();\n material.transparent = rendererConfig.transparent;\n material.blending = rendererConfig.blending;\n material.depthTest = rendererConfig.depthTest;\n material.depthWrite = rendererConfig.depthWrite;\n material.toneMapped = false;\n material.fog = false;\n\n // vertexNode replaces the default clip-space position. For instanced\n // billboards we compute the full MVP ourselves (view-space billboard offset +\n // projection), so the result is already in clip space.\n material.vertexNode = vertexNode;\n material.colorNode = fragmentColor;\n\n return material;\n}\n","/**\n * TSL (Three Shading Language) material for the MESH particle renderer.\n *\n * Replicates the behavior of mesh-particle-vertex-shader.glsl.ts and\n * mesh-particle-fragment-shader.glsl.ts using TSL node-based materials.\n *\n * Key differences from the POINTS / INSTANCED TSL materials:\n * - Uses `instanceQuat` (vec4) for full 3D quaternion rotation of mesh vertices.\n * - Transforms normals by the same quaternion for per-fragment directional lighting.\n * - Uses real mesh UVs (`uv`) rather than a derived point-coord or billboard UV.\n * - No circle discard — mesh geometry defines the particle shape.\n * - Texture sheet animation is only applied when tiles > 1×1.\n * - Simple directional lighting: `0.5 + 0.5 * max(dot(vNormal, vec3(0,0,1)), 0.0)`.\n */\nimport {\n Fn,\n attribute,\n vec2,\n vec3,\n vec4,\n float,\n cameraProjectionMatrix,\n modelViewMatrix,\n positionLocal,\n normalLocal,\n texture,\n cos,\n sin,\n Discard,\n If,\n max,\n cross,\n dot,\n varyingProperty,\n uv,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\n\nimport { ALPHA_DISCARD_THRESHOLD } from '../three-particles-constants.js';\nimport {\n type SharedUniforms,\n createParticleUniforms,\n computeFrameIndex,\n computeSpriteSheetUV,\n computeSoftParticleFade,\n applyBackgroundDiscard,\n compensateOutputSRGB,\n} from './tsl-shared.js';\n\nimport type * as THREE from 'three';\n\n// ─── Quaternion helper ────────────────────────────────────────────────────────\n\n/**\n * Applies a unit quaternion `q` to vector `v`.\n *\n * Replicates the GLSL helper:\n * ```glsl\n * vec3 applyQuaternion(vec3 v, vec4 q) {\n * vec3 t = 2.0 * cross(q.xyz, v);\n * return v + q.w * t + cross(q.xyz, t);\n * }\n * ```\n */\nconst applyQuaternion = Fn(\n ({ v, q }: Record<string, ShaderNodeObject<Node>>) => {\n const t = cross(q.xyz, v).mul(2.0);\n return v.add(t.mul(q.w)).add(cross(q.xyz, t));\n }\n);\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Creates a TSL-based {@link MeshBasicNodeMaterial} that replicates the GLSL\n * mesh particle shaders. Works with both WebGPURenderer (WGSL output) and\n * WebGLRenderer (GLSL output) when using the node material system.\n *\n * @param sharedUniforms - Live uniform values shared with the particle system.\n * @param rendererConfig - Blending / depth / transparency settings.\n * @returns A configured {@link MeshBasicNodeMaterial}.\n */\nexport function createMeshParticleTSLMaterial(\n sharedUniforms: SharedUniforms,\n rendererConfig: {\n transparent: boolean;\n blending: THREE.Blending;\n depthTest: boolean;\n depthWrite: boolean;\n },\n gpuCompute = false\n): MeshBasicNodeMaterial {\n const u = createParticleUniforms(sharedUniforms);\n\n // ── Per-instance attributes ────────────────────────────────────────────────\n\n /** Particle world-space position (vec3). */\n const aInstanceOffset = attribute('instanceOffset');\n /** Packed RGBA color (vec4). */\n const aColor = attribute('instanceColor');\n\n // GPU compute uses packed vec4 buffers; CPU uses individual attributes\n const aParticleState = gpuCompute ? attribute('instanceParticleState') : null;\n const aStartValues = gpuCompute ? attribute('instanceStartValues') : null;\n /** Particle orientation as a unit quaternion (vec4: x, y, z, w). CPU path only. */\n const aInstanceQuat = gpuCompute ? null : attribute('instanceQuat');\n const aSize = gpuCompute ? null : attribute('instanceSize');\n const aLifetime = gpuCompute ? null : attribute('instanceLifetime');\n const aStartLifetime = gpuCompute ? null : attribute('instanceStartLifetime');\n const aRotation = gpuCompute ? null : attribute('instanceRotation');\n const aStartFrame = gpuCompute ? null : attribute('instanceStartFrame');\n\n // ── Varyings ───────────────────────────────────────────────────────────────\n\n const vColor = varyingProperty('vec4', 'vColor');\n const vLifetime = varyingProperty('float', 'vLifetime');\n const vStartLifetime = varyingProperty('float', 'vStartLifetime');\n const vStartFrame = varyingProperty('float', 'vStartFrame');\n const vRotation = varyingProperty('float', 'vRotation');\n /** View-space normal, transformed by the instance quaternion. */\n const vNormal = varyingProperty('vec3', 'vNormal');\n /** View-space depth (positive distance from camera). */\n const vViewZ = varyingProperty('float', 'vViewZ');\n\n // ── Vertex stage ───────────────────────────────────────────────────────────\n\n /**\n * Computes the world-space vertex position for each mesh vertex:\n * 1. Rotate the mesh vertex by the instance quaternion.\n * 2. Scale by instanceSize.\n * 3. Translate by instanceOffset.\n * Also populates all varyings for the fragment stage.\n */\n const vertexSetup = Fn(() => {\n // Early-out for dead particles: skip all expensive transforms and emit\n // a degenerate clip-space position that produces zero-area triangles.\n // This avoids quaternion rotation, scaling, normal transforms, and\n // matrix multiplications for every vertex of inactive mesh instances.\n const clipPos = vec4(0.0, 0.0, 0.0, 0.0).toVar();\n\n If(aColor.w.greaterThan(0.0), () => {\n // Populate varyings\n vColor.assign(aColor.toVar());\n if (gpuCompute) {\n vLifetime.assign(aParticleState!.x);\n vStartLifetime.assign(aStartValues!.x);\n vStartFrame.assign(aParticleState!.w);\n vRotation.assign(aParticleState!.z);\n } else {\n vLifetime.assign(aLifetime!);\n vStartLifetime.assign(aStartLifetime!);\n vStartFrame.assign(aStartFrame!);\n vRotation.assign(aRotation!);\n }\n\n // Build quaternion: GPU compute derives it from particleState.z (rotation\n // angle around Z); CPU path reads the pre-computed instanceQuat attribute.\n let quat: ShaderNodeObject<Node>;\n if (gpuCompute) {\n const halfZ = aParticleState!.z.mul(0.5);\n quat = vec4(0.0, 0.0, sin(halfZ), cos(halfZ));\n } else {\n quat = aInstanceQuat!;\n }\n\n // 1. Rotate mesh vertex position by instance quaternion\n const rotatedPos = applyQuaternion({\n v: positionLocal,\n q: quat,\n });\n\n // 2. Scale by particle size\n const scaledPos = rotatedPos.mul(gpuCompute ? aParticleState!.y : aSize!);\n\n // 3. Translate to particle world position\n // Use .xyz to handle vec3→vec4 padding by WebGPU storage buffer alignment\n const worldPos = scaledPos.add(aInstanceOffset.xyz);\n\n // Compute model-view position for depth and normal\n const mvPos = modelViewMatrix.mul(vec4(worldPos, 1.0));\n vViewZ.assign(mvPos.z.negate());\n\n // Transform normal: rotate by quaternion then into view space\n const rotatedNormal = applyQuaternion({\n v: normalLocal,\n q: quat,\n });\n const mvNormal = modelViewMatrix.mul(vec4(rotatedNormal, 0.0)).xyz;\n vNormal.assign(mvNormal.normalize());\n\n clipPos.assign(cameraProjectionMatrix.mul(mvPos));\n });\n\n // Return clip-space position (manual MVP to avoid double-transform)\n return clipPos;\n })();\n\n // ── Fragment stage ─────────────────────────────────────────────────────────\n\n const fragmentColor = Fn(() => {\n const outColor = vColor.toVar();\n\n // Use mesh UVs as the base for texture sampling\n const uvPoint = vec2(uv()).toVar();\n\n // Texture sheet animation — only applied when tiles > 1×1\n If(u.uTiles.x.greaterThan(1.0).or(u.uTiles.y.greaterThan(1.0)), () => {\n const frameIndex = computeFrameIndex({\n vLifetime,\n vStartLifetime,\n vStartFrame,\n uFps: u.uFps,\n uUseFPSForFrameIndex: u.uUseFPSForFrameIndex,\n uTiles: u.uTiles,\n });\n\n // Remap mesh UV into the selected tile\n uvPoint.assign(\n computeSpriteSheetUV({\n baseUV: uv(),\n frameIndex,\n uTiles: u.uTiles,\n })\n );\n });\n\n // Sample texture using the (possibly remapped) UV\n const texColor = texture(u.uMap, uvPoint);\n outColor.assign(outColor.mul(texColor));\n\n // Background color discard\n applyBackgroundDiscard({\n texColor,\n uDiscardBg: u.uDiscardBg,\n uBgColor: u.uBgColor,\n uBgTolerance: u.uBgTolerance,\n });\n\n // Simple directional lighting from camera direction (+Z in view space).\n // lightIntensity = 0.5 + 0.5 * max(dot(vNormal, vec3(0,0,1)), 0.0)\n const lightIntensity = float(0.5).add(\n float(0.5).mul(max(dot(vNormal, vec3(0.0, 0.0, 1.0)), float(0.0)))\n );\n outColor.assign(vec4(outColor.xyz.mul(lightIntensity), outColor.w));\n\n // Soft particles — fade out fragments that are close to opaque scene geometry\n const softFade = computeSoftParticleFade({\n viewZ: vViewZ,\n uSoftEnabled: u.uSoftEnabled,\n uSoftIntensity: u.uSoftIntensity,\n uSceneDepthTex: u.uSceneDepthTex,\n uCameraNearFar: u.uCameraNearFar,\n });\n outColor.assign(vec4(outColor.xyz, outColor.w.mul(softFade)));\n Discard(outColor.w.lessThan(ALPHA_DISCARD_THRESHOLD));\n\n return compensateOutputSRGB({ color: outColor });\n })();\n\n // ── Material assembly ──────────────────────────────────────────────────────\n\n const material = new MeshBasicNodeMaterial();\n material.transparent = rendererConfig.transparent;\n material.blending = rendererConfig.blending;\n material.depthTest = rendererConfig.depthTest;\n material.depthWrite = rendererConfig.depthWrite;\n material.toneMapped = false;\n material.fog = false;\n\n // vertexNode receives a clip-space vec4 (manual MVP to avoid double-transform)\n material.vertexNode = vertexSetup;\n material.colorNode = fragmentColor;\n\n return material;\n}\n","/**\n * TSL (Three Shading Language) material for the POINTS particle renderer.\n *\n * Replicates the behavior of particle-system-vertex-shader.glsl.ts and\n * particle-system-fragment-shader.glsl.ts using TSL node-based materials.\n */\nimport {\n Fn,\n attribute,\n vec2,\n vec4,\n float,\n modelViewMatrix,\n positionLocal,\n pointUV,\n texture,\n cos,\n sin,\n Discard,\n If,\n length,\n varyingProperty,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\nimport { PointsNodeMaterial } from 'three/webgpu';\n\nimport {\n POINT_SIZE_SCALE,\n ALPHA_DISCARD_THRESHOLD,\n} from '../three-particles-constants.js';\nimport {\n type SharedUniforms,\n createParticleUniforms,\n computeFrameIndex,\n computeSpriteSheetUV,\n computeSoftParticleFade,\n applyBackgroundDiscard,\n compensateOutputSRGB,\n} from './tsl-shared.js';\nimport type * as THREE from 'three';\n\n/**\n * Creates a TSL-based PointsNodeMaterial that replicates the GLSL point sprite\n * particle shaders. Works with both WebGPURenderer (WGSL output) and\n * WebGLRenderer (GLSL output) when using the node material system.\n */\nexport function createPointSpriteTSLMaterial(\n sharedUniforms: SharedUniforms,\n rendererConfig: {\n transparent: boolean;\n blending: THREE.Blending;\n depthTest: boolean;\n depthWrite: boolean;\n },\n gpuCompute = false\n): PointsNodeMaterial {\n const u = createParticleUniforms(sharedUniforms);\n\n // Read per-particle attributes\n const aColor = attribute('color');\n\n // GPU compute uses packed vec4 buffers; CPU uses individual attributes\n const aParticleState = gpuCompute ? attribute('particleState') : null;\n const aStartValues = gpuCompute ? attribute('startValues') : null;\n // CPU fallback: individual attributes\n const aSize = gpuCompute ? null : attribute('size');\n const aLifetime = gpuCompute ? null : attribute('lifetime');\n const aStartLifetime = gpuCompute ? null : attribute('startLifetime');\n const aRotation = gpuCompute ? null : attribute('rotation');\n const aStartFrame = gpuCompute ? null : attribute('startFrame');\n\n // ── Vertex stage ────────────────────────────────────────────────────────\n\n // Compute model-view position and distance-based point size\n const mvPos = modelViewMatrix.mul(vec4(positionLocal, 1.0));\n const sizeVal = gpuCompute ? aParticleState!.y : aSize!;\n const sizeNode = aColor.w\n .greaterThan(0.0)\n .select(sizeVal.mul(POINT_SIZE_SCALE).div(length(mvPos.xyz)), float(0.0));\n\n // Pass varyings to fragment\n const vColor = varyingProperty('vec4', 'vColor');\n const vLifetime = varyingProperty('float', 'vLifetime');\n const vStartLifetime = varyingProperty('float', 'vStartLifetime');\n const vRotation = varyingProperty('float', 'vRotation');\n const vStartFrame = varyingProperty('float', 'vStartFrame');\n const vViewZ = varyingProperty('float', 'vViewZ');\n\n // Assign varyings in vertex\n const vertexSetup = Fn(() => {\n // Early-out for dead particles: set size to 0 (produces zero-area points)\n // and skip all varying assignments to save vertex shader work.\n If(aColor.w.greaterThan(0.0), () => {\n vColor.assign(aColor.toVar());\n if (gpuCompute) {\n vLifetime.assign(aParticleState!.x);\n vStartLifetime.assign(aStartValues!.x);\n vRotation.assign(aParticleState!.z);\n vStartFrame.assign(aParticleState!.w);\n } else {\n vLifetime.assign(aLifetime!);\n vStartLifetime.assign(aStartLifetime!);\n vRotation.assign(aRotation!);\n vStartFrame.assign(aStartFrame!);\n }\n vViewZ.assign(mvPos.z.negate());\n });\n return positionLocal;\n })();\n\n // ── Fragment stage ──────────────────────────────────────────────────────\n\n const fragmentColor = Fn(() => {\n // Start with per-particle color\n const outColor = vColor.toVar();\n\n // Compute frame index (texture sheet animation)\n const frameIndex = computeFrameIndex({\n vLifetime,\n vStartLifetime,\n vStartFrame,\n uFps: u.uFps,\n uUseFPSForFrameIndex: u.uUseFPSForFrameIndex,\n uTiles: u.uTiles,\n });\n\n // Apply 2D rotation to pointUV\n const center = vec2(0.5, 0.5);\n const centered = pointUV.sub(center);\n const cosR = cos(vRotation);\n const sinR = sin(vRotation);\n const rotated = vec2(\n centered.x.mul(cosR).add(centered.y.mul(sinR)),\n centered.x.mul(sinR).negate().add(centered.y.mul(cosR))\n );\n const rotatedUV = rotated.add(center);\n\n // Circle discard (inscribed circle in point quad)\n const dist = length(rotatedUV.sub(center));\n Discard(dist.greaterThan(0.5));\n\n // Compute sprite-sheet UV\n const uvPoint = computeSpriteSheetUV({\n baseUV: rotatedUV,\n frameIndex,\n uTiles: u.uTiles,\n });\n\n // Sample texture\n const texColor = texture(u.uMap, uvPoint);\n outColor.assign(outColor.mul(texColor));\n\n // Background color discard\n applyBackgroundDiscard({\n texColor,\n uDiscardBg: u.uDiscardBg,\n uBgColor: u.uBgColor,\n uBgTolerance: u.uBgTolerance,\n });\n\n // Soft particles\n const softFade = computeSoftParticleFade({\n viewZ: vViewZ,\n uSoftEnabled: u.uSoftEnabled,\n uSoftIntensity: u.uSoftIntensity,\n uSceneDepthTex: u.uSceneDepthTex,\n uCameraNearFar: u.uCameraNearFar,\n });\n outColor.assign(vec4(outColor.xyz, outColor.w.mul(softFade)));\n Discard(outColor.w.lessThan(ALPHA_DISCARD_THRESHOLD));\n\n return compensateOutputSRGB({ color: outColor });\n })();\n\n // ── Material assembly ───────────────────────────────────────────────────\n\n const material = new PointsNodeMaterial();\n material.transparent = rendererConfig.transparent;\n material.blending = rendererConfig.blending;\n material.depthTest = rendererConfig.depthTest;\n material.depthWrite = rendererConfig.depthWrite;\n material.toneMapped = false;\n material.fog = false;\n material.sizeNode = sizeNode;\n material.positionNode = vertexSetup;\n material.colorNode = fragmentColor;\n\n return material;\n}\n","/**\n * TSL (Three Shading Language) material for the trail ribbon renderer.\n *\n * Replicates the behavior of trail-vertex-shader.glsl.ts and\n * trail-fragment-shader.glsl.ts using TSL node-based materials.\n *\n * Key characteristics:\n * - Custom geometry with trail-specific per-vertex attributes\n * - Billboard ribbon: perp axis = cross(tangent, viewDir), with edge-on fallback\n * - Soft edge fade along the ribbon width (vUv.x axis)\n * - Optional texture modulation using luminance (dot-product brightness)\n * - DoubleSide rendering — no backface culling for ribbons\n * - NO texture sheet animation\n * - Trail uniforms are completely separate from the main particle uniforms\n */\nimport { DoubleSide, NoColorSpace } from 'three';\nimport {\n Fn,\n attribute,\n cameraPosition,\n cameraViewMatrix,\n vec2,\n vec3,\n vec4,\n float,\n modelViewMatrix,\n positionLocal,\n texture,\n normalize,\n cross,\n dot,\n abs,\n mix,\n smoothstep,\n screenUV,\n Discard,\n If,\n length,\n varyingProperty,\n uniform,\n type ShaderNodeObject,\n type Node,\n} from 'three/tsl';\nimport { MeshBasicNodeMaterial } from 'three/webgpu';\nimport { ALPHA_DISCARD_THRESHOLD } from '../three-particles-constants.js';\nimport {\n getDummyTexture,\n linearizeDepth,\n compensateOutputSRGB,\n} from './tsl-shared.js';\nimport type * as THREE from 'three';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/**\n * Uniforms consumed exclusively by the trail ribbon material.\n * These are managed separately from the main particle system's SharedUniforms.\n */\nexport type TrailUniforms = {\n /** Optional texture to modulate the ribbon color via luminance. */\n map: { value: THREE.Texture | null };\n /** Whether to apply texture modulation. */\n useMap: { value: boolean };\n /** Discard fragments whose color matches the background color. */\n discardBackgroundColor: { value: boolean };\n /** The background color to match for discard. */\n backgroundColor: { value: { r: number; g: number; b: number } };\n /** Tolerance radius in colour space for background discard. */\n backgroundColorTolerance: { value: number };\n /** Enable depth-difference soft-particle fading. */\n softParticlesEnabled: { value: boolean };\n /** Controls the fade distance for soft particles (view-space units). */\n softParticlesIntensity: { value: number };\n /** The pre-rendered scene depth texture, required for soft particles. */\n sceneDepthTexture: { value: THREE.Texture | null };\n /** Camera near/far planes packed as (near, far). */\n cameraNearFar: { value: THREE.Vector2 };\n};\n\n// ─── Internal uniform creators ───────────────────────────────────────────────\n\nfunction createTrailUniforms(trailUniforms: TrailUniforms) {\n const dummy = getDummyTexture();\n // Disable sRGB→linear hardware conversion to match GLSL ShaderMaterial output\n const map = (trailUniforms.map.value ?? dummy) as THREE.Texture;\n if (map) map.colorSpace = NoColorSpace;\n\n return {\n uMap: map,\n uUseMap: uniform(float(trailUniforms.useMap.value ? 1 : 0)),\n uDiscardBg: uniform(\n float(trailUniforms.discardBackgroundColor.value ? 1 : 0)\n ),\n uBgColor: uniform(\n new (trailUniforms.cameraNearFar.value.constructor as new (\n x: number,\n y: number,\n z: number\n ) => THREE.Vector3)(\n trailUniforms.backgroundColor.value.r,\n trailUniforms.backgroundColor.value.g,\n trailUniforms.backgroundColor.value.b\n )\n ),\n uBgTolerance: uniform(float(trailUniforms.backgroundColorTolerance.value)),\n uSoftEnabled: uniform(\n float(trailUniforms.softParticlesEnabled.value ? 1 : 0)\n ),\n uSoftIntensity: uniform(float(trailUniforms.softParticlesIntensity.value)),\n uSceneDepthTex: trailUniforms.sceneDepthTexture.value ?? dummy,\n uCameraNearFar: uniform(trailUniforms.cameraNearFar.value),\n };\n}\n\n// ─── Material factory ─────────────────────────────────────────────────────────\n\n/**\n * Creates a TSL-based MeshBasicNodeMaterial that replicates the GLSL trail\n * ribbon shaders. Works with both WebGPURenderer (WGSL output) and\n * WebGLRenderer (GLSL output) when using the node material system.\n *\n * The returned material expects geometry with these custom attributes:\n * - `trailAlpha` — per-vertex opacity along the trail\n * - `trailColor` — per-vertex RGBA colour\n * - `trailOffset` — ribbon edge side: −0.5 (left) or +0.5 (right)\n * - `trailHalfWidth` — half-width of the ribbon at this vertex\n * - `trailNext` — world-space position of the next trail sample\n * - `trailUV` — UV coordinates (x: along-trail, y: across-ribbon)\n *\n * @param trailUniforms - Per-trail uniform values (map, soft particles, bg discard, …).\n * @param rendererConfig - Blending / depth state forwarded to the material.\n * @returns Configured MeshBasicNodeMaterial ready for use with a trail mesh.\n */\nexport function createTrailRibbonTSLMaterial(\n trailUniforms: TrailUniforms,\n rendererConfig: {\n transparent: boolean;\n blending: THREE.Blending;\n depthTest: boolean;\n depthWrite: boolean;\n }\n): MeshBasicNodeMaterial {\n const u = createTrailUniforms(trailUniforms);\n\n // ── Per-vertex attributes ────────────────────────────────────────────────\n\n const aTrailAlpha = attribute('trailAlpha');\n const aTrailColor = attribute('trailColor', 'vec4');\n const aTrailOffset = attribute('trailOffset');\n const aTrailHalfWidth = attribute('trailHalfWidth');\n const aTrailNext = attribute('trailNext', 'vec3');\n const aTrailUV = attribute('trailUV', 'vec2');\n\n // ── Varyings ─────────────────────────────────────────────────────────────\n\n const vAlpha = varyingProperty('float', 'vAlpha');\n const vColor = varyingProperty('vec4', 'vColor');\n const vUv = varyingProperty('vec2', 'vUv');\n const vViewZ = varyingProperty('float', 'vViewZ');\n\n // ── Vertex stage ──────────────────────────────────────────────────────────\n //\n // Replicates trail-vertex-shader.glsl — billboard ribbon expansion:\n //\n // 1. Compute tangent from current → next position (with degenerate guard).\n // 2. Derive the camera-right fallback from viewMatrix column 0.\n // 3. Compute perp = cross(tangent, viewDir); blend toward fallback when\n // the cross product is near-zero (edge-on view).\n // 4. Offset position along perp by trailOffset * trailHalfWidth.\n // 5. Project and emit vViewZ for soft-particle depth test.\n\n const positionNode = Fn((): ShaderNodeObject<Node> => {\n // Pass varyings to fragment stage\n vAlpha.assign(aTrailAlpha);\n vColor.assign(aTrailColor);\n vUv.assign(aTrailUV);\n\n const current = vec3(positionLocal);\n const next = vec3(aTrailNext);\n\n // Tangent: direction from current to next sample\n const rawTangent = next.sub(current);\n const tangentLen = length(rawTangent);\n // Degenerate guard: fall back to +Y when consecutive samples coincide\n const tangent = normalize(\n tangentLen.lessThan(0.0001).select(vec3(0.0, 1.0, 0.0), rawTangent)\n );\n\n // View-space position for depth (vViewZ)\n const mvCurrent = modelViewMatrix.mul(vec4(current, 1.0));\n\n // View direction in local/world space (tangent is in the same space)\n const viewDir = normalize(cameraPosition.sub(current));\n\n // Primary billboard perpendicular\n const rawPerp = cross(tangent, viewDir);\n const perpLen = length(rawPerp);\n\n // Camera right vector extracted from the view matrix (column 0)\n // Using cameraViewMatrix (view-only, no model transform)\n const camRight = vec3(\n cameraViewMatrix.element(0).element(0),\n cameraViewMatrix.element(1).element(0),\n cameraViewMatrix.element(2).element(0)\n );\n\n // Fallback perpendicular: project camRight onto the plane perpendicular to tangent\n const camRightDotTangent = dot(camRight, tangent);\n const fallbackPerp = normalize(\n camRight.sub(tangent.mul(camRightDotTangent))\n );\n\n // When perpLen is near zero the ribbon is edge-on; use fallback.\n // Otherwise blend smoothly toward fallback over a wide range (0 → 0.7)\n // to prevent abrupt flipping, matching the GLSL shader exactly.\n const perp = normalize(\n perpLen\n .lessThan(0.0001)\n .select(\n fallbackPerp,\n normalize(\n mix(\n fallbackPerp,\n normalize(rawPerp),\n smoothstep(float(0.0), float(0.7), perpLen)\n )\n )\n )\n );\n\n // Expand ribbon vertex by offset side and half-width\n const offsetPos = current.add(perp.mul(aTrailOffset).mul(aTrailHalfWidth));\n\n // Emit view-space depth for soft particles\n const mvOffset = modelViewMatrix.mul(vec4(offsetPos, 1.0));\n vViewZ.assign(mvOffset.z.negate());\n\n // Return the offset position in local space; TSL's positionNode feeds this\n // through the standard MVP transform automatically, so returning offsetPos\n // is sufficient — the engine reconstructs the clip-space position.\n return offsetPos;\n })();\n\n // ── Fragment stage ────────────────────────────────────────────────────────\n //\n // Replicates trail-fragment-shader.glsl:\n // 1. Soft edge fade along the ribbon width using vUv.x.\n // 2. Optional luminance-weighted texture modulation.\n // 3. Alpha = vColor.a * vAlpha * edgeFade (plus optional soft-particle fade).\n // 4. Background color discard.\n\n const colorNode = Fn((): ShaderNodeObject<Node> => {\n const outColor = vColor.toVar();\n\n // Soft edge fade: vUv.x runs [0, 1] across the ribbon width.\n // edgeDist = 1 − |2·x − 1| peaks at 0.5 (centre) and is 0 at edges.\n const edgeDist = float(1.0).sub(abs(vUv.x.mul(2.0).sub(1.0)));\n const edgeFade = smoothstep(float(0.0), float(0.4), edgeDist);\n\n // Optional texture: modulate colour by luminance (perceptual weighting)\n // and multiply alpha by texture alpha — matching the GLSL shader.\n If(u.uUseMap.greaterThan(0.5), () => {\n const texColor = texture(u.uMap, vUv);\n const texBrightness = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));\n outColor.rgb.assign(\n outColor.rgb.mul(float(0.5).add(texBrightness.mul(0.5)))\n );\n outColor.a.assign(outColor.a.mul(texColor.a));\n });\n\n // Combine alpha: particle alpha × ribbon edge fade\n outColor.a.assign(outColor.a.mul(vAlpha).mul(edgeFade));\n\n // Early discard for fully transparent fragments\n Discard(outColor.a.lessThan(ALPHA_DISCARD_THRESHOLD));\n\n // Soft particles — depth-difference fade\n If(u.uSoftEnabled.greaterThan(0.5), () => {\n const depthSample = texture(u.uSceneDepthTex, screenUV).x;\n const sceneDepthLinear = linearizeDepth({\n depthSample,\n near: u.uCameraNearFar.x,\n far: u.uCameraNearFar.y,\n });\n const depthDiff = sceneDepthLinear.sub(vViewZ);\n const softFade = smoothstep(float(0.0), u.uSoftIntensity, depthDiff);\n outColor.a.assign(outColor.a.mul(softFade));\n });\n Discard(outColor.a.lessThan(ALPHA_DISCARD_THRESHOLD));\n\n // Background color discard\n const diff = vec3(\n outColor.r.sub(u.uBgColor.x),\n outColor.g.sub(u.uBgColor.y),\n outColor.b.sub(u.uBgColor.z)\n );\n Discard(\n u.uDiscardBg\n .greaterThan(0.5)\n .and(abs(length(diff)).lessThan(u.uBgTolerance))\n );\n\n return compensateOutputSRGB({ color: outColor });\n })();\n\n // ── Material assembly ─────────────────────────────────────────────────────\n\n const material = new MeshBasicNodeMaterial();\n material.transparent = rendererConfig.transparent;\n material.blending = rendererConfig.blending;\n material.depthTest = rendererConfig.depthTest;\n material.depthWrite = rendererConfig.depthWrite;\n material.toneMapped = false;\n material.fog = false;\n material.side = DoubleSide;\n\n // positionNode replaces the default local-space position fed into MVP\n material.positionNode = positionNode;\n material.colorNode = colorNode;\n\n return material;\n}\n","/**\n * TSL material factory for all particle renderer types.\n *\n * Selects and creates the appropriate TSL NodeMaterial based on the\n * renderer type (POINTS, INSTANCED, MESH, TRAIL).\n *\n * @module\n */\nimport { RendererType } from '../three-particles-enums.js';\nimport { isLifeTimeCurve } from '../three-particles-utils.js';\nimport {\n createModifierStorageBuffers,\n createModifierComputeUpdate,\n type ModifierComputePipeline,\n type ModifierFlags,\n} from './compute-modifiers.js';\n\n// Re-export emit queue helpers so callers can register them via the factory.\nexport {\n writeParticleToModifierBuffers,\n deactivateParticleInModifierBuffers,\n flushEmitQueue,\n registerCurveDataLength,\n} from './compute-modifiers.js';\nimport { bakeParticleSystemCurves } from './curve-bake.js';\nimport { createInstancedBillboardTSLMaterial } from './tsl-instanced-billboard-material.js';\nimport { createMeshParticleTSLMaterial } from './tsl-mesh-particle-material.js';\nimport { createPointSpriteTSLMaterial } from './tsl-point-sprite-material.js';\nimport {\n createTrailRibbonTSLMaterial,\n type TrailUniforms,\n} from './tsl-trail-ribbon-material.js';\nimport type { SharedUniforms } from './tsl-shared.js';\nimport type { NormalizedParticleSystemConfig } from '../types.js';\nimport type * as THREE from 'three';\n\nexport type { TrailUniforms };\n\nexport type RendererConfig = {\n transparent: boolean;\n blending: THREE.Blending;\n depthTest: boolean;\n depthWrite: boolean;\n};\n\n/**\n * Creates a TSL NodeMaterial for the main particle system (non-trail).\n *\n * @param rendererType - The particle renderer type.\n * @param sharedUniforms - Shared uniform values managed by the particle system.\n * @param rendererConfig - Material rendering properties (transparency, blending, etc.).\n * @returns A NodeMaterial instance configured for the specified renderer type.\n */\nexport function createTSLParticleMaterial(\n rendererType: RendererType,\n sharedUniforms: SharedUniforms,\n rendererConfig: RendererConfig,\n gpuCompute = false\n): THREE.Material {\n switch (rendererType) {\n case RendererType.INSTANCED:\n return createInstancedBillboardTSLMaterial(\n sharedUniforms,\n rendererConfig,\n gpuCompute\n );\n case RendererType.MESH:\n return createMeshParticleTSLMaterial(\n sharedUniforms,\n rendererConfig,\n gpuCompute\n );\n case RendererType.POINTS:\n default:\n return createPointSpriteTSLMaterial(\n sharedUniforms,\n rendererConfig,\n gpuCompute\n );\n }\n}\n\n/**\n * Creates a TSL NodeMaterial for the trail ribbon renderer.\n *\n * @param trailUniforms - Trail-specific uniform values.\n * @param rendererConfig - Material rendering properties.\n * @returns A NodeMaterial instance configured for trail ribbon rendering.\n */\nexport function createTSLTrailMaterial(\n trailUniforms: TrailUniforms,\n rendererConfig: RendererConfig\n): THREE.Material {\n return createTrailRibbonTSLMaterial(trailUniforms, rendererConfig);\n}\n\n/**\n * Creates the GPU compute pipeline for particle simulation.\n *\n * Bakes all lifetime curves, determines which modifiers are active,\n * creates GPU storage buffers, and returns the complete compute pipeline.\n * All `three/webgpu` imports are contained here — the caller does not\n * need to import any WebGPU-specific modules.\n *\n * @param maxParticles - Maximum particle count.\n * @param instanced - Whether to use InstancedBufferAttribute.\n * @param normalizedConfig - Fully normalized particle system config.\n * @param particleSystemId - Numeric ID for Bezier caching.\n * @param forceFieldCount - Number of active force fields.\n * @returns The complete modifier compute pipeline.\n */\nexport function createComputePipeline(\n maxParticles: number,\n instanced: boolean,\n normalizedConfig: NormalizedParticleSystemConfig,\n particleSystemId: number,\n forceFieldCount: number,\n collisionPlaneCount = 0\n): ModifierComputePipeline {\n const bakedCurves = bakeParticleSystemCurves(\n normalizedConfig,\n particleSystemId\n );\n\n const { velocityOverLifetime } = normalizedConfig;\n\n const flags: ModifierFlags = {\n sizeOverLifetime: normalizedConfig.sizeOverLifetime.isActive,\n opacityOverLifetime: normalizedConfig.opacityOverLifetime.isActive,\n colorOverLifetime: normalizedConfig.colorOverLifetime.isActive,\n rotationOverLifetime: normalizedConfig.rotationOverLifetime.isActive,\n linearVelocity:\n velocityOverLifetime.isActive &&\n (isLifeTimeCurve(velocityOverLifetime.linear.x ?? 0) ||\n isLifeTimeCurve(velocityOverLifetime.linear.y ?? 0) ||\n isLifeTimeCurve(velocityOverLifetime.linear.z ?? 0) ||\n velocityOverLifetime.linear.x !== 0 ||\n velocityOverLifetime.linear.y !== 0 ||\n velocityOverLifetime.linear.z !== 0),\n orbitalVelocity:\n velocityOverLifetime.isActive &&\n (isLifeTimeCurve(velocityOverLifetime.orbital.x ?? 0) ||\n isLifeTimeCurve(velocityOverLifetime.orbital.y ?? 0) ||\n isLifeTimeCurve(velocityOverLifetime.orbital.z ?? 0) ||\n velocityOverLifetime.orbital.x !== 0 ||\n velocityOverLifetime.orbital.y !== 0 ||\n velocityOverLifetime.orbital.z !== 0),\n noise: normalizedConfig.noise.isActive,\n forceFields: forceFieldCount > 0,\n collisionPlanes: collisionPlaneCount > 0,\n };\n\n const buffers = createModifierStorageBuffers(\n maxParticles,\n instanced,\n bakedCurves.data,\n flags.forceFields,\n flags.collisionPlanes\n );\n\n return createModifierComputeUpdate(\n buffers,\n maxParticles,\n bakedCurves,\n flags,\n forceFieldCount,\n collisionPlaneCount\n );\n}\n","import { registerTSLMaterialFactory } from '@newkrok/three-particles';\nimport { encodeCollisionPlanesForGPU } from './js/effects/three-particles/webgpu/compute-collision-planes.js';\nimport { encodeForceFieldsForGPU } from './js/effects/three-particles/webgpu/compute-force-fields.js';\nimport {\n writeParticleToModifierBuffers,\n deactivateParticleInModifierBuffers,\n flushEmitQueue,\n registerCurveDataLength,\n} from './js/effects/three-particles/webgpu/compute-modifiers.js';\nimport {\n createTSLParticleMaterial,\n createTSLTrailMaterial,\n createComputePipeline,\n} from './js/effects/three-particles/webgpu/tsl-materials.js';\n\n// Re-export all individual functions for power users\nexport {\n createTSLParticleMaterial,\n createTSLTrailMaterial,\n createComputePipeline,\n writeParticleToModifierBuffers,\n deactivateParticleInModifierBuffers,\n flushEmitQueue,\n registerCurveDataLength,\n encodeCollisionPlanesForGPU,\n encodeForceFieldsForGPU,\n};\n\n/**\n * Convenience function that registers all WebGPU TSL material factories\n * and GPU compute helpers in a single call.\n *\n * Call this **once** before creating any particle systems that use WebGPU rendering.\n *\n * @example\n * ```typescript\n * import { enableWebGPU } from '@newkrok/three-particles/webgpu';\n * enableWebGPU();\n * ```\n */\nexport function enableWebGPU(): void {\n // The TSLMaterialFactory interface deliberately uses wider parameter types\n // (Record<string,…>) to avoid pulling WebGPU-specific imports into the\n // main DTS output. The concrete functions are fully type-safe at their\n // own definition sites; the cast here bridges the two type worlds.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const factory: Parameters<typeof registerTSLMaterialFactory>[0] = {\n createTSLParticleMaterial: createTSLParticleMaterial as any,\n createTSLTrailMaterial: createTSLTrailMaterial as any,\n createComputePipeline,\n writeParticleToModifierBuffers,\n deactivateParticleInModifierBuffers,\n flushEmitQueue,\n registerCurveDataLength,\n encodeForceFieldsForGPU,\n encodeCollisionPlanesForGPU,\n };\n registerTSLMaterialFactory(factory);\n}\n"]}
|