From 736430ef70ed9cd3cc2cf39905428556d26d44ec Mon Sep 17 00:00:00 2001 From: Universe Date: Mon, 30 Mar 2026 18:03:20 +0900 Subject: [PATCH 1/7] Fix WASM image fills not rendering after async load The emscripten `add_image` and `add_image_with_rid` methods were not calling `mark_changed(ChangeFlags::IMAGE_LOADED)` after inserting images into the repository. Without this flag, `apply_changes()` never invalidated the PictureCache or compositor cache, so stale pictures recorded before the image loaded (containing no image shader) were replayed indefinitely. This caused image fills to appear only during zoom interactions (where a cache-miss on the unstable variant key forced fresh recording) but disappear on settle and pan frames. Also fix the L0-image fixture: the Transform variant used out-of-range box-relative coordinates (tx=10, ty=20) producing a 1500px offset in a 150px container. Replace with valid values and add a rotation test case. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../examples/fixtures/l0_image.rs | 31 +++++++++++++++--- .../src/window/application_emscripten.rs | 10 ++++-- fixtures/test-grida/L0.grida | Bin 71108 -> 71480 bytes 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/crates/grida-canvas/examples/fixtures/l0_image.rs b/crates/grida-canvas/examples/fixtures/l0_image.rs index 7202b7f777..f6d41e54ea 100644 --- a/crates/grida-canvas/examples/fixtures/l0_image.rs +++ b/crates/grida-canvas/examples/fixtures/l0_image.rs @@ -26,7 +26,7 @@ pub fn build() -> Scene { image_paint_with(img(), ImagePaintFit::Fit(BoxFit::Contain)), ); - // Transform fit (scale + offset) + // Transform fit (zoom 1.5× centered with slight offset) let r3 = rect( gap * 2.0, 0.0, @@ -34,13 +34,34 @@ pub fn build() -> Scene { s, image_paint_with( img(), - ImagePaintFit::Transform(AffineTransform::new(10.0, 20.0, 0.0)), + ImagePaintFit::Transform(AffineTransform { + matrix: [[1.5, 0.0, -0.25], [0.0, 1.5, -0.15]], + }), ), ); + // Transform fit with 15° rotation + let r4 = { + let deg: f32 = 15.0; + let rad = deg.to_radians(); + let (sin, cos) = rad.sin_cos(); + rect( + gap * 3.0, + 0.0, + s, + s, + image_paint_with( + img(), + ImagePaintFit::Transform(AffineTransform { + matrix: [[cos, -sin, 0.0], [sin, cos, 0.0]], + }), + ), + ) + }; + // Quarter turns + alignment + Screen blend - let r4 = rect( - gap * 3.0, + let r5 = rect( + gap * 4.0, 0.0, s, s, @@ -56,5 +77,5 @@ pub fn build() -> Scene { }), ); - flat_scene("L0 Image", vec![r1, r2, r3, r4]) + flat_scene("L0 Image", vec![r1, r2, r3, r4, r5]) } diff --git a/crates/grida-canvas/src/window/application_emscripten.rs b/crates/grida-canvas/src/window/application_emscripten.rs index f969bc9e5f..1bc6414d5a 100644 --- a/crates/grida-canvas/src/window/application_emscripten.rs +++ b/crates/grida-canvas/src/window/application_emscripten.rs @@ -423,12 +423,18 @@ impl EmscriptenApplication { /// Register an image with the renderer and return metadata. pub fn add_image(&mut self, data: &[u8]) -> (String, String, u32, u32, String) { - self.base.renderer.add_image(data) + let result = self.base.renderer.add_image(data); + self.base.renderer.mark_changed(ChangeFlags::IMAGE_LOADED); + result } /// Register image bytes under a caller-specified RID (res:// or system://). pub fn add_image_with_rid(&mut self, data: &[u8], rid: &str) -> Option<(u32, u32, String)> { - self.base.renderer.add_image_with_rid(data, rid) + let result = self.base.renderer.add_image_with_rid(data, rid); + if result.is_some() { + self.base.renderer.mark_changed(ChangeFlags::IMAGE_LOADED); + } + result } pub fn get_image_bytes(&self, id: &str) -> Option> { diff --git a/fixtures/test-grida/L0.grida b/fixtures/test-grida/L0.grida index 8693319103c5b2441d85062b85bef6144da41449..25b8b90178d72811776aa2b666d311176b0ecb46 100644 GIT binary patch literal 71480 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 literal 71108 zcmc${4}6u!btXKLF(y=@Dpgh0)v~v)!m0#Qffb^-1YO-%B{yExRS6|3OC5nFEF4+f z6*5s+mhXo0dG+~YU6o}iVX60)Qc5YMlu|ECDN$VNRlU?psn(?~rG&Cn%Mw;`2&)i+ z_Ib{k^WIml{*VOyetq>DotZf^XU=)fnVI+By?2Il?i0&DS-z>=LWZc_b(x%&AG0%==1X!^BL!E z{uI(rIM-g`+%N{&{)lsbhps0}3S9XQ7Px7%3*61(0yp^m1@6+U0=NBx1ug?UrXkIk z34LaO|5$+=m|ozzpxeD^(C?7~*H>8JP8Jln6RyA=8F4Oi-?_GLJGUCT4}A-|-gR!z zFl@i$-1*xWAF2N9u;XjcbI7>?*jDpZ=g!?k`k&6VAPxS5b6c)M@BiW4fxkz8*nQwD zXou7brAscO-6iyW591)6zUW-}UqSYN2L`~V=z?>(zd+r2;P#Gl7lGB1|LR=-+n|5u z+_C@ST*X_Ed)B%6NN3-~cwctz-j~oGcn+TiK1eJ62<=Wecl#vT{3r0dz~x$BJ}-L=aReO!(EW`?_keP*p1++gnuvnTiA(o@m0v_ft*(` z_Afz?t!VeJAagT(@OkIb-N5g2ko_`n?}D6-&NX(zh79Ni^zT6bmtcQ8@-KqF9_^_cpgVePI5oIlCxF|JAqEnV^-m#V4#xf`(DO$z#^Y%BBWU--@IO*@ zDRe0T=0AwGv!UA$Aa=hWGG?LfgBZ66vG;w*e*k@F0JG`v%cH`c}VgFw~>bn9UyQ?B$)nP zA5D<88VPDR%4Qj)fcZmTyFc=`msZz)^=A80ra$XX*DeDeW;nnpI>B`52b0{IDjcBC zLRa>G3~A${k9?+M;YZzKm0MVWI`ZbDT2nWu-n?Bip` zsBtCG7hN8u{KdaNB6hUFumdPJBVihd-sGFTY3(ES?s;GOgKI~&*4kwv?fRr$ww8GO zdM>f_YePdzyDB(dwlszcW0`sC*n@sg3(sVZPj}?<@%&eR@Qbx* z^(Ei3)|XusWEox-&#%H>jx$j_H|p30(Rl8T$Wgn7QfaX-9n`?KJ(AyzUF1Y?!^?l) z(>&H6aVr!riPOuPpOQ89G-UN5S)8&A6U9fIYtc29I3EU|u~LOZoyLpDb7_~3zV`;JakAyVk&nj(fBaK_ z09igBv$a~r-V7ZKFN?>=Q8!sUUeU2f#p4Xue+#5%*OMcc`maN^rzu^Q5`lI(L8a@ckIp*$`h`O?4s(g%O}v8AYYpl4WSz7lfd zxNL*0$#B`Ex)he7KieJm?W?4|G-v`fEb65W_$Uo7`?l9T#U+?tm*9Fat#%0((+VfX zBBSjDi@y6~vA8E=Il8>G{dn;{!234M85W}+(89RiRN$nEapE?a{7#@;3 z+&?4DK2e_nC3eqv8e+H@_u+JHkhogx=f+fESOQsPNQNQHhG7flGBL7ibmkMko5xUa z`{O@)iU=cmcoTKa&;8IJ z-WFbA{VSjx=QU453~?`ONXHNiH;Zjk6NgQZwHnDVWZ5u0^vKAl^E+bLax{bN0>4G|e7!po$}h4!cFK@Gzysl&Z5p??y*?Z>&`X^3Gp)>_yD z+XTZinxB%nybrQ+NQNQHhT+X=ilJT05W_>im&b6TbL0ldh+}vVHcp1&UL7~Yus0&3 zNqCu5v(Wxj52#_-C3U#xDs)>g3zXxOdKzMg+0qSr48y`l)y}Dy%U2-x8j@kivSC;? zePq=67csnfERW%hWuL3P^-J5&puGcK$};o4kP*l5hPP>=_3*Ne8)7&Rk+DyBnbZNH z{i$=HhT%!6!!@MP!eBWlFV*c_NRtH4XbNXhx_K9 zZUyByuXq|_h5Pn{ehl&tdj18_1j>2c5)E>{Jh39YU(Pmp{@DBJ?|VLO(?{;NuWN7Z z#(jKXg=+E7bko|HQn?{Ur%^VHwhJ$j+95QTIss}J z<)jYx&^>(`l)9xn4Kc!b&jH0n?v)jPd}L$`j5>g}%}5BdG1prI9!JWn_`Z>mzr3(~ z)O6bCrT3^^U#j=V#{>Y$#Ai~d;#hx zZ@jrPr1A?_gKsj*AkSo65#7_lmAlj^J8XSffzneO{=ol%Y&ISL92AHgu$ zy3xti9r=Ep|MWG-C`Z!Rz_p6R2qX*bPc{1TRj>06PeVT;7KkB4$b5fS^98&J&Y<#P z*5eb9brQ+^#xg`oEKmF3*mJA>;M1NeB+5yV(cx~4$bwJwF*`OTfaZkhX5ENUA@_0>$9cVTsbN?FT4k8&| zEK@f1sGc?UJn91Yh8J~;VGet` z7?ip#^)$o?=SexmMPh0mbf+D+@h^k)Ffla)vWk!lBbF(fdNg5PMDJ5^?%#nPh7om& zV^oEH_v88lqwA`Fh|wjK4Wk3XOQ((q?N1GW8b*Cmhj#}&T?Ii>ctKli_0Pz-q`EwsG#R ztdm93;!(_f-p!jK>tsRUv`6q%x2~^b?3&mhvT$F=)775RPD;Jw^}O$C=o|RI8MvWI zA$6LA_N26%Yi-^(JMUU1=S|CeyYcK^>v>$;^Y1rgHf&tqwSGef-pkPOWWKEW0rekV zL3-%h6)lJJ+sHI!E?bU=1C;#6rV3#gBPHl7${y(;r#{_;O!FG$MjAa zF~BiuO`cw=V^?Afi@v4x|RkdLUVNo{kTWt<0BA@VGFtletR`P zCF@N$WNkq*{8+{~f?xj+j$M=2flvIZkf>7}KSI?3d*b-5RJq}L(}1#JR3bcF{|n8f z>OnaMF{+ZfbgD*ZY4Em}eIY&u*5qpFi!OrE8O=`#M*AQuhh!MBjBx~`o0ymJHThNW z4I}Cl$B5YNggtSLHmlqaqYTQ1QH}62sie?cstwdITA_7yt3AEtW#5U9f#7a}z8GHC z!uQ?GJikYxUz}*Icf{ zRI}ex%3%4Tr!Rt1k4{gcYsdg(f+cJ2PR~CBnm}btS*P;)y{>UdsM);ib0P@X9&;&TCFcI_FFfgbsT{VU5xM&1)%UlN|0FwH_c zTZ2wwtoRzlb-EPwhULaZw4=(M*KtQ-IgEDZ2gU~OR;T)f_NPu_oYntiES8K74B>`6 zr3{qn9NJ?F1UpgY*n2(AW4XxPlQxRw)vt@3NwJ&{{2)rO+=4k9f3{N9AbhG59vr(1 z{pJd9VjPL_Dzq~!ry(E5vI?@+LgzS^G%tms`G^a$M?bVK*@#8}J^2_MhDJf#e-1${zy2g^Y@_9{>F@mu9i3mzK3 z`|k)2lg95&Z7-NF!CZ>IbHjah=vk40(eek!uErX6eH>oCmF6BtJB#157;C)veFF8A zsrwby0&KLGRQ4z=r$Lta;hgB52nK|14$gwt_G4qQye)jZZ|x~%V0BLw3C*QmM?J?r z=xL|}p5M;^9$-oQb~QeDAFI*!f@Kfp67kdb9!qPZ@!K3}cMR>w`_|pf;XSq%jK#WW zqK=2qZTPKKGq?oQZFdC`5WLFJCZyDQR{vB7<;Ug+ka8hT%RVl3V* z!pCO^Jf#fA?iOF31@pePiP0Qyr$!h!meoqT;ax6R0⁡)K8|G-WNm_7aV)EJ+)-FIp}%37K%Q|~DYQRT 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�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 Date: Mon, 30 Mar 2026 18:09:29 +0900 Subject: [PATCH 2/7] 0.91.0-canary.16 --- crates/grida-canvas-wasm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/grida-canvas-wasm/package.json b/crates/grida-canvas-wasm/package.json index 7cf7216074..68226fa8c9 100644 --- a/crates/grida-canvas-wasm/package.json +++ b/crates/grida-canvas-wasm/package.json @@ -1,6 +1,6 @@ { "name": "@grida/canvas-wasm", - "version": "0.91.0-canary.15", + "version": "0.91.0-canary.16", "private": false, "description": "WASM bindings for Grida Canvas", "keywords": [ From ea228df12d3d17ff4d8b293e360d6d67fd97c39e Mon Sep 17 00:00:00 2001 From: Universe Date: Mon, 30 Mar 2026 18:19:48 +0900 Subject: [PATCH 3/7] wasm 0.91.0-canary.16 bin --- crates/grida-canvas-wasm/lib/bin/grida-canvas-wasm.js | 2 +- crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/grida-canvas-wasm/lib/bin/grida-canvas-wasm.js b/crates/grida-canvas-wasm/lib/bin/grida-canvas-wasm.js index e72689ec60..ff495309ed 100644 --- a/crates/grida-canvas-wasm/lib/bin/grida-canvas-wasm.js +++ b/crates/grida-canvas-wasm/lib/bin/grida-canvas-wasm.js @@ -1,2 +1,2 @@ -var createGridaCanvas=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else{}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["Yg"]();FS.ignorePermissions=false}function preMain(){}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("grida_canvas_wasm.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_find_matching_catch_4=(arg0,arg1)=>findMatchingCatch([arg0,arg1]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}___cxa_increment_exception_refcount(ptr);exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);___cxa_increment_exception_refcount(ptr);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var ___cxa_uncaught_exceptions=()=>uncaughtExceptionCount;var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>crypto.getRandomValues(view)};var randomFill=view=>{(randomFill=initRandomFill())(view)};var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var zeroMemory=(ptr,size)=>HEAPU8.fill(0,ptr,ptr+size);var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(ptr)zeroMemory(ptr,size);return ptr};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,readFiles:{},ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);setattr(arg,attr)},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}if(Module["logReadFiles"]&&!(flags&1)){if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){data=new Uint8Array(intArrayFromString(data,true))}if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{abort("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)abort("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{return SYSCALLS.writeStat(buf,FS.fstat(fd))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(size>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.lstat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.writeStat(buf,nofollow?FS.lstat(path):FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var __emscripten_throw_longjmp=()=>{throw Infinity};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}function __mmap_js(len,prot,flags,fd,offset,allocated,addr){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetperformance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e3*1e3);HEAP64[ptime>>3]=BigInt(nsec);return 0}var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var GLctx;var webgl_enable_ANGLE_instanced_arrays=ctx=>{var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=(index,divisor)=>ext["vertexAttribDivisorANGLE"](index,divisor);ctx["drawArraysInstanced"]=(mode,first,count,primcount)=>ext["drawArraysInstancedANGLE"](mode,first,count,primcount);ctx["drawElementsInstanced"]=(mode,count,type,indices,primcount)=>ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount);return 1}};var webgl_enable_OES_vertex_array_object=ctx=>{var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=()=>ext["createVertexArrayOES"]();ctx["deleteVertexArray"]=vao=>ext["deleteVertexArrayOES"](vao);ctx["bindVertexArray"]=vao=>ext["bindVertexArrayOES"](vao);ctx["isVertexArray"]=vao=>ext["isVertexArrayOES"](vao);return 1}};var webgl_enable_WEBGL_draw_buffers=ctx=>{var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=(n,bufs)=>ext["drawBuffersWEBGL"](n,bufs);return 1}};var webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"));var webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"));var webgl_enable_EXT_polygon_offset_clamp=ctx=>!!(ctx.extPolygonOffsetClamp=ctx.getExtension("EXT_polygon_offset_clamp"));var webgl_enable_EXT_clip_control=ctx=>!!(ctx.extClipControl=ctx.getExtension("EXT_clip_control"));var webgl_enable_WEBGL_polygon_mode=ctx=>!!(ctx.webglPolygonMode=ctx.getExtension("WEBGL_polygon_mode"));var webgl_enable_WEBGL_multi_draw=ctx=>!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"));var getEmscriptenSupportedExtensions=ctx=>{var supportedExtensions=["ANGLE_instanced_arrays","EXT_blend_minmax","EXT_disjoint_timer_query","EXT_frag_depth","EXT_shader_texture_lod","EXT_sRGB","OES_element_index_uint","OES_fbo_render_mipmap","OES_standard_derivatives","OES_texture_float","OES_texture_half_float","OES_texture_half_float_linear","OES_vertex_array_object","WEBGL_color_buffer_float","WEBGL_depth_texture","WEBGL_draw_buffers","EXT_color_buffer_float","EXT_conservative_depth","EXT_disjoint_timer_query_webgl2","EXT_texture_norm16","NV_shader_noperspective_interpolation","WEBGL_clip_cull_distance","EXT_clip_control","EXT_color_buffer_half_float","EXT_depth_clamp","EXT_float_blend","EXT_polygon_offset_clamp","EXT_texture_compression_bptc","EXT_texture_compression_rgtc","EXT_texture_filter_anisotropic","KHR_parallel_shader_compile","OES_texture_float_linear","WEBGL_blend_func_extended","WEBGL_compressed_texture_astc","WEBGL_compressed_texture_etc","WEBGL_compressed_texture_etc1","WEBGL_compressed_texture_s3tc","WEBGL_compressed_texture_s3tc_srgb","WEBGL_debug_renderer_info","WEBGL_debug_shaders","WEBGL_lose_context","WEBGL_multi_draw","WEBGL_polygon_mode"];return(ctx.getSupportedExtensions()||[]).filter(ext=>supportedExtensions.includes(ext))};var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],stringCache:{},stringiCache:{},unpackAlignment:4,unpackRowLength:0,recordError:errorCode=>{if(!GL.lastError){GL.lastError=errorCode}},getNewId:table=>{var ret=GL.counter++;for(var i=table.length;i{for(var i=0;i>2]=id}},getSource:(shader,count,string,length)=>{var source="";for(var i=0;i>2]:undefined;source+=UTF8ToString(HEAPU32[string+i*4>>2],len)}return source},createContext:(canvas,webGLContextAttributes)=>{if(!canvas.getContextSafariWebGL2Fixed){canvas.getContextSafariWebGL2Fixed=canvas.getContext;function fixedGetContext(ver,attrs){var gl=canvas.getContextSafariWebGL2Fixed(ver,attrs);return ver=="webgl"==gl instanceof WebGLRenderingContext?gl:null}canvas.getContext=fixedGetContext}var ctx=webGLContextAttributes.majorVersion>1?canvas.getContext("webgl2",webGLContextAttributes):canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:(ctx,webGLContextAttributes)=>{var handle=GL.getNewId(GL.contexts);var context={handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault=="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}return handle},makeContextCurrent:contextHandle=>{GL.currentContext=GL.contexts[contextHandle];Module["ctx"]=GLctx=GL.currentContext?.GLctx;return!(contextHandle&&!GLctx)},getContext:contextHandle=>GL.contexts[contextHandle],deleteContext:contextHandle=>{if(GL.currentContext===GL.contexts[contextHandle]){GL.currentContext=null}if(typeof JSEvents=="object"){JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas)}if(GL.contexts[contextHandle]?.GLctx.canvas){GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined}GL.contexts[contextHandle]=null},initExtensions:context=>{context||=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;webgl_enable_WEBGL_multi_draw(GLctx);webgl_enable_EXT_polygon_offset_clamp(GLctx);webgl_enable_EXT_clip_control(GLctx);webgl_enable_WEBGL_polygon_mode(GLctx);webgl_enable_ANGLE_instanced_arrays(GLctx);webgl_enable_OES_vertex_array_object(GLctx);webgl_enable_WEBGL_draw_buffers(GLctx);webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);if(context.version>=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}for(var ext of getEmscriptenSupportedExtensions(GLctx)){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}}}};var _emscripten_glActiveTexture=x0=>GLctx.activeTexture(x0);var _emscripten_glAttachShader=(program,shader)=>{GLctx.attachShader(GL.programs[program],GL.shaders[shader])};var _emscripten_glBeginQuery=(target,id)=>{GLctx.beginQuery(target,GL.queries[id])};var _emscripten_glBeginQueryEXT=(target,id)=>{GLctx.disjointTimerQueryExt["beginQueryEXT"](target,GL.queries[id])};var _emscripten_glBeginTransformFeedback=x0=>GLctx.beginTransformFeedback(x0);var _emscripten_glBindAttribLocation=(program,index,name)=>{GLctx.bindAttribLocation(GL.programs[program],index,UTF8ToString(name))};var _emscripten_glBindBuffer=(target,buffer)=>{if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])};var _emscripten_glBindBufferBase=(target,index,buffer)=>{GLctx.bindBufferBase(target,index,GL.buffers[buffer])};var _emscripten_glBindBufferRange=(target,index,buffer,offset,ptrsize)=>{GLctx.bindBufferRange(target,index,GL.buffers[buffer],offset,ptrsize)};var _emscripten_glBindFramebuffer=(target,framebuffer)=>{GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])};var _emscripten_glBindRenderbuffer=(target,renderbuffer)=>{GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])};var _emscripten_glBindSampler=(unit,sampler)=>{GLctx.bindSampler(unit,GL.samplers[sampler])};var _emscripten_glBindTexture=(target,texture)=>{GLctx.bindTexture(target,GL.textures[texture])};var _emscripten_glBindTransformFeedback=(target,id)=>{GLctx.bindTransformFeedback(target,GL.transformFeedbacks[id])};var _emscripten_glBindVertexArray=vao=>{GLctx.bindVertexArray(GL.vaos[vao])};var _glBindVertexArray=_emscripten_glBindVertexArray;var _emscripten_glBindVertexArrayOES=_glBindVertexArray;var _emscripten_glBlendColor=(x0,x1,x2,x3)=>GLctx.blendColor(x0,x1,x2,x3);var _emscripten_glBlendEquation=x0=>GLctx.blendEquation(x0);var _emscripten_glBlendEquationSeparate=(x0,x1)=>GLctx.blendEquationSeparate(x0,x1);var _emscripten_glBlendFunc=(x0,x1)=>GLctx.blendFunc(x0,x1);var _emscripten_glBlendFuncSeparate=(x0,x1,x2,x3)=>GLctx.blendFuncSeparate(x0,x1,x2,x3);var _emscripten_glBlitFramebuffer=(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)=>GLctx.blitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);var _emscripten_glBufferData=(target,size,data,usage)=>{if(GL.currentContext.version>=2){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}return}GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)};var _emscripten_glBufferSubData=(target,offset,size,data)=>{if(GL.currentContext.version>=2){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))};var _emscripten_glCheckFramebufferStatus=x0=>GLctx.checkFramebufferStatus(x0);var _emscripten_glClear=x0=>GLctx.clear(x0);var _emscripten_glClearBufferfi=(x0,x1,x2,x3)=>GLctx.clearBufferfi(x0,x1,x2,x3);var _emscripten_glClearBufferfv=(buffer,drawbuffer,value)=>{GLctx.clearBufferfv(buffer,drawbuffer,HEAPF32,value>>2)};var _emscripten_glClearBufferiv=(buffer,drawbuffer,value)=>{GLctx.clearBufferiv(buffer,drawbuffer,HEAP32,value>>2)};var _emscripten_glClearBufferuiv=(buffer,drawbuffer,value)=>{GLctx.clearBufferuiv(buffer,drawbuffer,HEAPU32,value>>2)};var _emscripten_glClearColor=(x0,x1,x2,x3)=>GLctx.clearColor(x0,x1,x2,x3);var _emscripten_glClearDepthf=x0=>GLctx.clearDepth(x0);var _emscripten_glClearStencil=x0=>GLctx.clearStencil(x0);var _emscripten_glClientWaitSync=(sync,flags,timeout)=>{timeout=Number(timeout);return GLctx.clientWaitSync(GL.syncs[sync],flags,timeout)};var _emscripten_glClipControlEXT=(origin,depth)=>{GLctx.extClipControl["clipControlEXT"](origin,depth)};var _emscripten_glColorMask=(red,green,blue,alpha)=>{GLctx.colorMask(!!red,!!green,!!blue,!!alpha)};var _emscripten_glCompileShader=shader=>{GLctx.compileShader(GL.shaders[shader])};var _emscripten_glCompressedTexImage2D=(target,level,internalFormat,width,height,border,imageSize,data)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,HEAPU8,data,imageSize);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,HEAPU8.subarray(data,data+imageSize))};var _emscripten_glCompressedTexImage3D=(target,level,internalFormat,width,height,depth,border,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,imageSize,data)}else{GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,HEAPU8,data,imageSize)}};var _emscripten_glCompressedTexSubImage2D=(target,level,xoffset,yoffset,width,height,format,imageSize,data)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data);return}GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize);return}GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,HEAPU8.subarray(data,data+imageSize))};var _emscripten_glCompressedTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}};var _emscripten_glCopyBufferSubData=(x0,x1,x2,x3,x4)=>GLctx.copyBufferSubData(x0,x1,x2,x3,x4);var _emscripten_glCopyTexImage2D=(x0,x1,x2,x3,x4,x5,x6,x7)=>GLctx.copyTexImage2D(x0,x1,x2,x3,x4,x5,x6,x7);var _emscripten_glCopyTexSubImage2D=(x0,x1,x2,x3,x4,x5,x6,x7)=>GLctx.copyTexSubImage2D(x0,x1,x2,x3,x4,x5,x6,x7);var _emscripten_glCopyTexSubImage3D=(x0,x1,x2,x3,x4,x5,x6,x7,x8)=>GLctx.copyTexSubImage3D(x0,x1,x2,x3,x4,x5,x6,x7,x8);var _emscripten_glCreateProgram=()=>{var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id};var _emscripten_glCreateShader=shaderType=>{var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id};var _emscripten_glCullFace=x0=>GLctx.cullFace(x0);var _emscripten_glDeleteBuffers=(n,buffers)=>{for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}};var _emscripten_glDeleteFramebuffers=(n,framebuffers)=>{for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}};var _emscripten_glDeleteProgram=id=>{if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null};var _emscripten_glDeleteQueries=(n,ids)=>{for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx.deleteQuery(query);GL.queries[id]=null}};var _emscripten_glDeleteQueriesEXT=(n,ids)=>{for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx.disjointTimerQueryExt["deleteQueryEXT"](query);GL.queries[id]=null}};var _emscripten_glDeleteRenderbuffers=(n,renderbuffers)=>{for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}};var _emscripten_glDeleteSamplers=(n,samplers)=>{for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx.deleteSampler(sampler);sampler.name=0;GL.samplers[id]=null}};var _emscripten_glDeleteShader=id=>{if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null};var _emscripten_glDeleteSync=id=>{if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null};var _emscripten_glDeleteTextures=(n,textures)=>{for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}};var _emscripten_glDeleteTransformFeedbacks=(n,ids)=>{for(var i=0;i>2];var transformFeedback=GL.transformFeedbacks[id];if(!transformFeedback)continue;GLctx.deleteTransformFeedback(transformFeedback);transformFeedback.name=0;GL.transformFeedbacks[id]=null}};var _emscripten_glDeleteVertexArrays=(n,vaos)=>{for(var i=0;i>2];GLctx.deleteVertexArray(GL.vaos[id]);GL.vaos[id]=null}};var _glDeleteVertexArrays=_emscripten_glDeleteVertexArrays;var _emscripten_glDeleteVertexArraysOES=_glDeleteVertexArrays;var _emscripten_glDepthFunc=x0=>GLctx.depthFunc(x0);var _emscripten_glDepthMask=flag=>{GLctx.depthMask(!!flag)};var _emscripten_glDepthRangef=(x0,x1)=>GLctx.depthRange(x0,x1);var _emscripten_glDetachShader=(program,shader)=>{GLctx.detachShader(GL.programs[program],GL.shaders[shader])};var _emscripten_glDisable=x0=>GLctx.disable(x0);var _emscripten_glDisableVertexAttribArray=index=>{GLctx.disableVertexAttribArray(index)};var _emscripten_glDrawArrays=(mode,first,count)=>{GLctx.drawArrays(mode,first,count)};var _emscripten_glDrawArraysInstanced=(mode,first,count,primcount)=>{GLctx.drawArraysInstanced(mode,first,count,primcount)};var _glDrawArraysInstanced=_emscripten_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedANGLE=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedARB=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedBaseInstanceWEBGL=(mode,first,count,instanceCount,baseInstance)=>{GLctx.dibvbi["drawArraysInstancedBaseInstanceWEBGL"](mode,first,count,instanceCount,baseInstance)};var _emscripten_glDrawArraysInstancedEXT=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedNV=_glDrawArraysInstanced;var tempFixedLengthArray=[];var _emscripten_glDrawBuffers=(n,bufs)=>{var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx.drawBuffers(bufArray)};var _glDrawBuffers=_emscripten_glDrawBuffers;var _emscripten_glDrawBuffersEXT=_glDrawBuffers;var _emscripten_glDrawBuffersWEBGL=_glDrawBuffers;var _emscripten_glDrawElements=(mode,count,type,indices)=>{GLctx.drawElements(mode,count,type,indices)};var _emscripten_glDrawElementsInstanced=(mode,count,type,indices,primcount)=>{GLctx.drawElementsInstanced(mode,count,type,indices,primcount)};var _glDrawElementsInstanced=_emscripten_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedANGLE=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedARB=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL=(mode,count,type,offset,instanceCount,baseVertex,baseinstance)=>{GLctx.dibvbi["drawElementsInstancedBaseVertexBaseInstanceWEBGL"](mode,count,type,offset,instanceCount,baseVertex,baseinstance)};var _emscripten_glDrawElementsInstancedEXT=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedNV=_glDrawElementsInstanced;var _glDrawElements=_emscripten_glDrawElements;var _emscripten_glDrawRangeElements=(mode,start,end,count,type,indices)=>{_glDrawElements(mode,count,type,indices)};var _emscripten_glEnable=x0=>GLctx.enable(x0);var _emscripten_glEnableVertexAttribArray=index=>{GLctx.enableVertexAttribArray(index)};var _emscripten_glEndQuery=x0=>GLctx.endQuery(x0);var _emscripten_glEndQueryEXT=target=>{GLctx.disjointTimerQueryExt["endQueryEXT"](target)};var _emscripten_glEndTransformFeedback=()=>GLctx.endTransformFeedback();var _emscripten_glFenceSync=(condition,flags)=>{var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}return 0};var _emscripten_glFinish=()=>GLctx.finish();var _emscripten_glFlush=()=>GLctx.flush();var _emscripten_glFramebufferRenderbuffer=(target,attachment,renderbuffertarget,renderbuffer)=>{GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])};var _emscripten_glFramebufferTexture2D=(target,attachment,textarget,texture,level)=>{GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)};var _emscripten_glFramebufferTextureLayer=(target,attachment,texture,level,layer)=>{GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)};var _emscripten_glFrontFace=x0=>GLctx.frontFace(x0);var _emscripten_glGenBuffers=(n,buffers)=>{GL.genObject(n,buffers,"createBuffer",GL.buffers)};var _emscripten_glGenFramebuffers=(n,ids)=>{GL.genObject(n,ids,"createFramebuffer",GL.framebuffers)};var _emscripten_glGenQueries=(n,ids)=>{GL.genObject(n,ids,"createQuery",GL.queries)};var _emscripten_glGenQueriesEXT=(n,ids)=>{for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.queries);query.name=id;GL.queries[id]=query;HEAP32[ids+i*4>>2]=id}};var _emscripten_glGenRenderbuffers=(n,renderbuffers)=>{GL.genObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)};var _emscripten_glGenSamplers=(n,samplers)=>{GL.genObject(n,samplers,"createSampler",GL.samplers)};var _emscripten_glGenTextures=(n,textures)=>{GL.genObject(n,textures,"createTexture",GL.textures)};var _emscripten_glGenTransformFeedbacks=(n,ids)=>{GL.genObject(n,ids,"createTransformFeedback",GL.transformFeedbacks)};var _emscripten_glGenVertexArrays=(n,arrays)=>{GL.genObject(n,arrays,"createVertexArray",GL.vaos)};var _glGenVertexArrays=_emscripten_glGenVertexArrays;var _emscripten_glGenVertexArraysOES=_glGenVertexArrays;var _emscripten_glGenerateMipmap=x0=>GLctx.generateMipmap(x0);var __glGetActiveAttribOrUniform=(funcName,program,index,bufSize,length,size,type,name)=>{program=GL.programs[program];var info=GLctx[funcName](program,index);if(info){var numBytesWrittenExclNull=name&&stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull;if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}};var _emscripten_glGetActiveAttrib=(program,index,bufSize,length,size,type,name)=>__glGetActiveAttribOrUniform("getActiveAttrib",program,index,bufSize,length,size,type,name);var _emscripten_glGetActiveUniform=(program,index,bufSize,length,size,type,name)=>__glGetActiveAttribOrUniform("getActiveUniform",program,index,bufSize,length,size,type,name);var _emscripten_glGetActiveUniformBlockName=(program,uniformBlockIndex,bufSize,length,uniformBlockName)=>{program=GL.programs[program];var result=GLctx.getActiveUniformBlockName(program,uniformBlockIndex);if(!result)return;if(uniformBlockName&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(result,uniformBlockName,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}};var _emscripten_glGetActiveUniformBlockiv=(program,uniformBlockIndex,pname,params)=>{if(!params){GL.recordError(1281);return}program=GL.programs[program];if(pname==35393){var name=GLctx.getActiveUniformBlockName(program,uniformBlockIndex);HEAP32[params>>2]=name.length+1;return}var result=GLctx.getActiveUniformBlockParameter(program,uniformBlockIndex,pname);if(result===null)return;if(pname==35395){for(var i=0;i>2]=result[i]}}else{HEAP32[params>>2]=result}};var _emscripten_glGetActiveUniformsiv=(program,uniformCount,uniformIndices,pname,params)=>{if(!params){GL.recordError(1281);return}if(uniformCount>0&&uniformIndices==0){GL.recordError(1281);return}program=GL.programs[program];var ids=[];for(var i=0;i>2])}var result=GLctx.getActiveUniforms(program,ids,pname);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}};var _emscripten_glGetAttachedShaders=(program,maxCount,count,shaders)=>{var result=GLctx.getAttachedShaders(GL.programs[program]);var len=result.length;if(len>maxCount){len=maxCount}HEAP32[count>>2]=len;for(var i=0;i>2]=id}};var _emscripten_glGetAttribLocation=(program,name)=>GLctx.getAttribLocation(GL.programs[program],UTF8ToString(name));var writeI53ToI64=(ptr,num)=>{HEAPU32[ptr>>2]=num;var lower=HEAPU32[ptr>>2];HEAPU32[ptr+4>>2]=(num-lower)/4294967296};var webglGetExtensions=()=>{var exts=getEmscriptenSupportedExtensions(GLctx);exts=exts.concat(exts.map(e=>"GL_"+e));return exts};var emscriptenWebGLGet=(name_,p,type)=>{if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}ret=webglGetExtensions().length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Unknown object returned from WebGL getParameter(${name_})! (error: ${e})`);return}}break;default:GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Native code calling glGet${type}v(${name_}) and it returns ${result} of type ${typeof result}!`);return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p]=ret?1:0;break}};var _emscripten_glGetBooleanv=(name_,p)=>emscriptenWebGLGet(name_,p,4);var _emscripten_glGetBufferParameteri64v=(target,value,data)=>{if(!data){GL.recordError(1281);return}writeI53ToI64(data,GLctx.getBufferParameter(target,value))};var _emscripten_glGetBufferParameteriv=(target,value,data)=>{if(!data){GL.recordError(1281);return}HEAP32[data>>2]=GLctx.getBufferParameter(target,value)};var _emscripten_glGetError=()=>{var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error};var _emscripten_glGetFloatv=(name_,p)=>emscriptenWebGLGet(name_,p,2);var _emscripten_glGetFragDataLocation=(program,name)=>GLctx.getFragDataLocation(GL.programs[program],UTF8ToString(name));var _emscripten_glGetFramebufferAttachmentParameteriv=(target,attachment,pname,params)=>{var result=GLctx.getFramebufferAttachmentParameter(target,attachment,pname);if(result instanceof WebGLRenderbuffer||result instanceof WebGLTexture){result=result.name|0}HEAP32[params>>2]=result};var emscriptenWebGLGetIndexed=(target,index,data,type)=>{if(!data){GL.recordError(1281);return}var result=GLctx.getIndexedParameter(target,index);var ret;switch(typeof result){case"boolean":ret=result?1:0;break;case"number":ret=result;break;case"object":if(result===null){switch(target){case 35983:case 35368:ret=0;break;default:{GL.recordError(1280);return}}}else if(result instanceof WebGLBuffer){ret=result.name|0}else{GL.recordError(1280);return}break;default:GL.recordError(1280);return}switch(type){case 1:writeI53ToI64(data,ret);break;case 0:HEAP32[data>>2]=ret;break;case 2:HEAPF32[data>>2]=ret;break;case 4:HEAP8[data]=ret?1:0;break;default:abort("internal emscriptenWebGLGetIndexed() error, bad type: "+type)}};var _emscripten_glGetInteger64i_v=(target,index,data)=>emscriptenWebGLGetIndexed(target,index,data,1);var _emscripten_glGetInteger64v=(name_,p)=>{emscriptenWebGLGet(name_,p,1)};var _emscripten_glGetIntegeri_v=(target,index,data)=>emscriptenWebGLGetIndexed(target,index,data,0);var _emscripten_glGetIntegerv=(name_,p)=>emscriptenWebGLGet(name_,p,0);var _emscripten_glGetInternalformativ=(target,internalformat,pname,bufSize,params)=>{if(bufSize<0){GL.recordError(1281);return}if(!params){GL.recordError(1281);return}var ret=GLctx.getInternalformatParameter(target,internalformat,pname);if(ret===null)return;for(var i=0;i>2]=ret[i]}};var _emscripten_glGetProgramBinary=(program,bufSize,length,binaryFormat,binary)=>{GL.recordError(1282)};var _emscripten_glGetProgramInfoLog=(program,maxLength,length,infoLog)=>{var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetProgramiv=(program,pname,p)=>{if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){var numActiveAttributes=GLctx.getProgramParameter(program,35721);for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){var numActiveUniformBlocks=GLctx.getProgramParameter(program,35382);for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}};var _emscripten_glGetQueryObjecti64vEXT=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param;if(GL.currentContext.version<2){param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname)}else{param=GLctx.getQueryParameter(query,pname)}var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)};var _emscripten_glGetQueryObjectivEXT=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret};var _glGetQueryObjecti64vEXT=_emscripten_glGetQueryObjecti64vEXT;var _emscripten_glGetQueryObjectui64vEXT=_glGetQueryObjecti64vEXT;var _emscripten_glGetQueryObjectuiv=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx.getQueryParameter(query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret};var _glGetQueryObjectivEXT=_emscripten_glGetQueryObjectivEXT;var _emscripten_glGetQueryObjectuivEXT=_glGetQueryObjectivEXT;var _emscripten_glGetQueryiv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getQuery(target,pname)};var _emscripten_glGetQueryivEXT=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.disjointTimerQueryExt["getQueryEXT"](target,pname)};var _emscripten_glGetRenderbufferParameteriv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getRenderbufferParameter(target,pname)};var _emscripten_glGetSamplerParameterfv=(sampler,pname,params)=>{if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getSamplerParameter(GL.samplers[sampler],pname)};var _emscripten_glGetSamplerParameteriv=(sampler,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getSamplerParameter(GL.samplers[sampler],pname)};var _emscripten_glGetShaderInfoLog=(shader,maxLength,length,infoLog)=>{var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetShaderPrecisionFormat=(shaderType,precisionType,range,precision)=>{var result=GLctx.getShaderPrecisionFormat(shaderType,precisionType);HEAP32[range>>2]=result.rangeMin;HEAP32[range+4>>2]=result.rangeMax;HEAP32[precision>>2]=result.precision};var _emscripten_glGetShaderSource=(shader,bufSize,length,source)=>{var result=GLctx.getShaderSource(GL.shaders[shader]);if(!result)return;var numBytesWrittenExclNull=bufSize>0&&source?stringToUTF8(result,source,bufSize):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetShaderiv=(shader,pname,p)=>{if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}};var stringToNewUTF8=str=>{var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret};var _emscripten_glGetString=name_=>{var ret=GL.stringCache[name_];if(!ret){switch(name_){case 7939:ret=stringToNewUTF8(webglGetExtensions().join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=s?stringToNewUTF8(s):0;break;case 7938:var webGLVersion=GLctx.getParameter(7938);var glVersion=`OpenGL ES 2.0 (${webGLVersion})`;if(GL.currentContext.version>=2)glVersion=`OpenGL ES 3.0 (${webGLVersion})`;ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion=`OpenGL ES GLSL ES ${ver_num[1]} (${glslVersion})`}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280)}GL.stringCache[name_]=ret}return ret};var _emscripten_glGetStringi=(name,index)=>{if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=webglGetExtensions().map(stringToNewUTF8);stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}};var _emscripten_glGetSynciv=(sync,pname,bufSize,length,values)=>{if(bufSize<0){GL.recordError(1281);return}if(!values){GL.recordError(1281);return}var ret=GLctx.getSyncParameter(GL.syncs[sync],pname);if(ret!==null){HEAP32[values>>2]=ret;if(length)HEAP32[length>>2]=1}};var _emscripten_glGetTexParameterfv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getTexParameter(target,pname)};var _emscripten_glGetTexParameteriv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getTexParameter(target,pname)};var _emscripten_glGetTransformFeedbackVarying=(program,index,bufSize,length,size,type,name)=>{program=GL.programs[program];var info=GLctx.getTransformFeedbackVarying(program,index);if(!info)return;if(name&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type};var _emscripten_glGetUniformBlockIndex=(program,uniformBlockName)=>GLctx.getUniformBlockIndex(GL.programs[program],UTF8ToString(uniformBlockName));var _emscripten_glGetUniformIndices=(program,uniformCount,uniformNames,uniformIndices)=>{if(!uniformIndices){GL.recordError(1281);return}if(uniformCount>0&&(uniformNames==0||uniformIndices==0)){GL.recordError(1281);return}program=GL.programs[program];var names=[];for(var i=0;i>2]));var result=GLctx.getUniformIndices(program,names);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}};var jstoi_q=str=>parseInt(str);var webglGetLeftBracePos=name=>name.slice(-1)=="]"&&name.lastIndexOf("[");var webglPrepareUniformLocationsBeforeFirstUse=program=>{var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j{name=UTF8ToString(name);if(program=GL.programs[program]){webglPrepareUniformLocationsBeforeFirstUse(program);var uniformLocsById=program.uniformLocsById;var arrayIndex=0;var uniformBaseName=name;var leftBrace=webglGetLeftBracePos(name);if(leftBrace>0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex{var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?`[${webglLoc}]`:""))}return webglLoc}else{GL.recordError(1282)}};var emscriptenWebGLGetUniform=(program,location,params,type)=>{if(!params){GL.recordError(1281);return}program=GL.programs[program];webglPrepareUniformLocationsBeforeFirstUse(program);var data=GLctx.getUniform(program,webglGetUniformLocation(location));if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break}}}};var _emscripten_glGetUniformfv=(program,location,params)=>{emscriptenWebGLGetUniform(program,location,params,2)};var _emscripten_glGetUniformiv=(program,location,params)=>{emscriptenWebGLGetUniform(program,location,params,0)};var _emscripten_glGetUniformuiv=(program,location,params)=>emscriptenWebGLGetUniform(program,location,params,0);var emscriptenWebGLGetVertexAttrib=(index,pname,params,type)=>{if(!params){GL.recordError(1281);return}var data=GLctx.getVertexAttrib(index,pname);if(pname==34975){HEAP32[params>>2]=data&&data["name"]}else if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break;case 5:HEAP32[params>>2]=Math.fround(data);break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break;case 5:HEAP32[params+i*4>>2]=Math.fround(data[i]);break}}}};var _emscripten_glGetVertexAttribIiv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,0)};var _glGetVertexAttribIiv=_emscripten_glGetVertexAttribIiv;var _emscripten_glGetVertexAttribIuiv=_glGetVertexAttribIiv;var _emscripten_glGetVertexAttribPointerv=(index,pname,pointer)=>{if(!pointer){GL.recordError(1281);return}HEAP32[pointer>>2]=GLctx.getVertexAttribOffset(index,pname)};var _emscripten_glGetVertexAttribfv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,2)};var _emscripten_glGetVertexAttribiv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,5)};var _emscripten_glHint=(x0,x1)=>GLctx.hint(x0,x1);var _emscripten_glInvalidateFramebuffer=(target,numAttachments,attachments)=>{var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx.invalidateFramebuffer(target,list)};var _emscripten_glInvalidateSubFramebuffer=(target,numAttachments,attachments,x,y,width,height)=>{var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx.invalidateSubFramebuffer(target,list,x,y,width,height)};var _emscripten_glIsBuffer=buffer=>{var b=GL.buffers[buffer];if(!b)return 0;return GLctx.isBuffer(b)};var _emscripten_glIsEnabled=x0=>GLctx.isEnabled(x0);var _emscripten_glIsFramebuffer=framebuffer=>{var fb=GL.framebuffers[framebuffer];if(!fb)return 0;return GLctx.isFramebuffer(fb)};var _emscripten_glIsProgram=program=>{program=GL.programs[program];if(!program)return 0;return GLctx.isProgram(program)};var _emscripten_glIsQuery=id=>{var query=GL.queries[id];if(!query)return 0;return GLctx.isQuery(query)};var _emscripten_glIsQueryEXT=id=>{var query=GL.queries[id];if(!query)return 0;return GLctx.disjointTimerQueryExt["isQueryEXT"](query)};var _emscripten_glIsRenderbuffer=renderbuffer=>{var rb=GL.renderbuffers[renderbuffer];if(!rb)return 0;return GLctx.isRenderbuffer(rb)};var _emscripten_glIsSampler=id=>{var sampler=GL.samplers[id];if(!sampler)return 0;return GLctx.isSampler(sampler)};var _emscripten_glIsShader=shader=>{var s=GL.shaders[shader];if(!s)return 0;return GLctx.isShader(s)};var _emscripten_glIsSync=sync=>GLctx.isSync(GL.syncs[sync]);var _emscripten_glIsTexture=id=>{var texture=GL.textures[id];if(!texture)return 0;return GLctx.isTexture(texture)};var _emscripten_glIsTransformFeedback=id=>GLctx.isTransformFeedback(GL.transformFeedbacks[id]);var _emscripten_glIsVertexArray=array=>{var vao=GL.vaos[array];if(!vao)return 0;return GLctx.isVertexArray(vao)};var _glIsVertexArray=_emscripten_glIsVertexArray;var _emscripten_glIsVertexArrayOES=_glIsVertexArray;var _emscripten_glLineWidth=x0=>GLctx.lineWidth(x0);var _emscripten_glLinkProgram=program=>{program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}};var _emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL=(mode,firsts,counts,instanceCounts,baseInstances,drawCount)=>{GLctx.mdibvbi["multiDrawArraysInstancedBaseInstanceWEBGL"](mode,HEAP32,firsts>>2,HEAP32,counts>>2,HEAP32,instanceCounts>>2,HEAPU32,baseInstances>>2,drawCount)};var _emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL=(mode,counts,type,offsets,instanceCounts,baseVertices,baseInstances,drawCount)=>{GLctx.mdibvbi["multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL"](mode,HEAP32,counts>>2,type,HEAP32,offsets>>2,HEAP32,instanceCounts>>2,HEAP32,baseVertices>>2,HEAPU32,baseInstances>>2,drawCount)};var _emscripten_glPauseTransformFeedback=()=>GLctx.pauseTransformFeedback();var _emscripten_glPixelStorei=(pname,param)=>{if(pname==3317){GL.unpackAlignment=param}else if(pname==3314){GL.unpackRowLength=param}GLctx.pixelStorei(pname,param)};var _emscripten_glPolygonModeWEBGL=(face,mode)=>{GLctx.webglPolygonMode["polygonModeWEBGL"](face,mode)};var _emscripten_glPolygonOffset=(x0,x1)=>GLctx.polygonOffset(x0,x1);var _emscripten_glPolygonOffsetClampEXT=(factor,units,clamp)=>{GLctx.extPolygonOffsetClamp["polygonOffsetClampEXT"](factor,units,clamp)};var _emscripten_glProgramBinary=(program,binaryFormat,binary,length)=>{GL.recordError(1280)};var _emscripten_glProgramParameteri=(program,pname,value)=>{GL.recordError(1280)};var _emscripten_glQueryCounterEXT=(id,target)=>{GLctx.disjointTimerQueryExt["queryCounterEXT"](GL.queries[id],target)};var _emscripten_glReadBuffer=x0=>GLctx.readBuffer(x0);var computeUnpackAlignedImageSize=(width,height,sizePerPixel)=>{function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=(GL.unpackRowLength||width)*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,GL.unpackAlignment);return height*alignedRowSize};var colorChannelsInGlTextureFormat=format=>{var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1};var heapObjectForWebGLType=type=>{type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16};var toTypedArrayIndex=(pointer,heap)=>pointer>>>31-Math.clz32(heap.BYTES_PER_ELEMENT);var emscriptenWebGLGetTexPixelData=(type,format,width,height,pixels,internalFormat)=>{var heap=heapObjectForWebGLType(type);var sizePerPixel=colorChannelsInGlTextureFormat(format)*heap.BYTES_PER_ELEMENT;var bytes=computeUnpackAlignedImageSize(width,height,sizePerPixel);return heap.subarray(toTypedArrayIndex(pixels,heap),toTypedArrayIndex(pixels+bytes,heap))};var _emscripten_glReadPixels=(x,y,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels);return}var heap=heapObjectForWebGLType(type);var target=toTypedArrayIndex(pixels,heap);GLctx.readPixels(x,y,width,height,format,type,heap,target);return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)};var _emscripten_glReleaseShaderCompiler=()=>{};var _emscripten_glRenderbufferStorage=(x0,x1,x2,x3)=>GLctx.renderbufferStorage(x0,x1,x2,x3);var _emscripten_glRenderbufferStorageMultisample=(x0,x1,x2,x3,x4)=>GLctx.renderbufferStorageMultisample(x0,x1,x2,x3,x4);var _emscripten_glResumeTransformFeedback=()=>GLctx.resumeTransformFeedback();var _emscripten_glSampleCoverage=(value,invert)=>{GLctx.sampleCoverage(value,!!invert)};var _emscripten_glSamplerParameterf=(sampler,pname,param)=>{GLctx.samplerParameterf(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameterfv=(sampler,pname,params)=>{var param=HEAPF32[params>>2];GLctx.samplerParameterf(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameteri=(sampler,pname,param)=>{GLctx.samplerParameteri(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameteriv=(sampler,pname,params)=>{var param=HEAP32[params>>2];GLctx.samplerParameteri(GL.samplers[sampler],pname,param)};var _emscripten_glScissor=(x0,x1,x2,x3)=>GLctx.scissor(x0,x1,x2,x3);var _emscripten_glShaderBinary=(count,shaders,binaryformat,binary,length)=>{GL.recordError(1280)};var _emscripten_glShaderSource=(shader,count,string,length)=>{var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)};var _emscripten_glStencilFunc=(x0,x1,x2)=>GLctx.stencilFunc(x0,x1,x2);var _emscripten_glStencilFuncSeparate=(x0,x1,x2,x3)=>GLctx.stencilFuncSeparate(x0,x1,x2,x3);var _emscripten_glStencilMask=x0=>GLctx.stencilMask(x0);var _emscripten_glStencilMaskSeparate=(x0,x1)=>GLctx.stencilMaskSeparate(x0,x1);var _emscripten_glStencilOp=(x0,x1,x2)=>GLctx.stencilOp(x0,x1,x2);var _emscripten_glStencilOpSeparate=(x0,x1,x2,x3)=>GLctx.stencilOpSeparate(x0,x1,x2,x3);var _emscripten_glTexImage2D=(target,level,internalFormat,width,height,border,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);var index=toTypedArrayIndex(pixels,heap);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,index);return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null;GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixelData)};var _emscripten_glTexImage3D=(target,level,internalFormat,width,height,depth,border,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,null)}};var _emscripten_glTexParameterf=(x0,x1,x2)=>GLctx.texParameterf(x0,x1,x2);var _emscripten_glTexParameterfv=(target,pname,params)=>{var param=HEAPF32[params>>2];GLctx.texParameterf(target,pname,param)};var _emscripten_glTexParameteri=(x0,x1,x2)=>GLctx.texParameteri(x0,x1,x2);var _emscripten_glTexParameteriv=(target,pname,params)=>{var param=HEAP32[params>>2];GLctx.texParameteri(target,pname,param)};var _emscripten_glTexStorage2D=(x0,x1,x2,x3,x4)=>GLctx.texStorage2D(x0,x1,x2,x3,x4);var _emscripten_glTexStorage3D=(x0,x1,x2,x3,x4,x5)=>GLctx.texStorage3D(x0,x1,x2,x3,x4,x5);var _emscripten_glTexSubImage2D=(target,level,xoffset,yoffset,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,toTypedArrayIndex(pixels,heap));return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0):null;GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)};var _emscripten_glTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}};var _emscripten_glTransformFeedbackVaryings=(program,count,varyings,bufferMode)=>{program=GL.programs[program];var vars=[];for(var i=0;i>2]));GLctx.transformFeedbackVaryings(program,vars,bufferMode)};var _emscripten_glUniform1f=(location,v0)=>{GLctx.uniform1f(webglGetUniformLocation(location),v0)};var miniTempWebGLFloatBuffers=[];var _emscripten_glUniform1fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1fv(webglGetUniformLocation(location),HEAPF32,value>>2,count);return}if(count<=288){var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform1i=(location,v0)=>{GLctx.uniform1i(webglGetUniformLocation(location),v0)};var miniTempWebGLIntBuffers=[];var _emscripten_glUniform1iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1iv(webglGetUniformLocation(location),HEAP32,value>>2,count);return}if(count<=288){var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2]}}else{var view=HEAP32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform1ui=(location,v0)=>{GLctx.uniform1ui(webglGetUniformLocation(location),v0)};var _emscripten_glUniform1uiv=(location,count,value)=>{count&&GLctx.uniform1uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count)};var _emscripten_glUniform2f=(location,v0,v1)=>{GLctx.uniform2f(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*2);return}if(count<=144){count*=2;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform2i=(location,v0,v1)=>{GLctx.uniform2i(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2iv(webglGetUniformLocation(location),HEAP32,value>>2,count*2);return}if(count<=144){count*=2;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform2ui=(location,v0,v1)=>{GLctx.uniform2ui(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2uiv=(location,count,value)=>{count&&GLctx.uniform2uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*2)};var _emscripten_glUniform3f=(location,v0,v1,v2)=>{GLctx.uniform3f(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform3fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*3);return}if(count<=96){count*=3;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*12>>2)}GLctx.uniform3fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform3i=(location,v0,v1,v2)=>{GLctx.uniform3i(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform3iv(webglGetUniformLocation(location),HEAP32,value>>2,count*3);return}if(count<=96){count*=3;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2];view[i+2]=HEAP32[value+(4*i+8)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*12>>2)}GLctx.uniform3iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform3ui=(location,v0,v1,v2)=>{GLctx.uniform3ui(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3uiv=(location,count,value)=>{count&&GLctx.uniform3uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*3)};var _emscripten_glUniform4f=(location,v0,v1,v2,v3)=>{GLctx.uniform4f(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform4fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*4);return}if(count<=72){var view=miniTempWebGLFloatBuffers[4*count];var heap=HEAPF32;value=value>>2;count*=4;for(var i=0;i>2,value+count*16>>2)}GLctx.uniform4fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform4i=(location,v0,v1,v2,v3)=>{GLctx.uniform4i(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform4iv(webglGetUniformLocation(location),HEAP32,value>>2,count*4);return}if(count<=72){count*=4;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2];view[i+2]=HEAP32[value+(4*i+8)>>2];view[i+3]=HEAP32[value+(4*i+12)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*16>>2)}GLctx.uniform4iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform4ui=(location,v0,v1,v2,v3)=>{GLctx.uniform4ui(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4uiv=(location,count,value)=>{count&&GLctx.uniform4uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*4)};var _emscripten_glUniformBlockBinding=(program,uniformBlockIndex,uniformBlockBinding)=>{program=GL.programs[program];GLctx.uniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding)};var _emscripten_glUniformMatrix2fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*4);return}if(count<=72){count*=4;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*16>>2)}GLctx.uniformMatrix2fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix2x3fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix2x3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*6)};var _emscripten_glUniformMatrix2x4fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix2x4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*8)};var _emscripten_glUniformMatrix3fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*9);return}if(count<=32){count*=9;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2];view[i+4]=HEAPF32[value+(4*i+16)>>2];view[i+5]=HEAPF32[value+(4*i+20)>>2];view[i+6]=HEAPF32[value+(4*i+24)>>2];view[i+7]=HEAPF32[value+(4*i+28)>>2];view[i+8]=HEAPF32[value+(4*i+32)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*36>>2)}GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix3x2fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix3x2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*6)};var _emscripten_glUniformMatrix3x4fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix3x4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*12)};var _emscripten_glUniformMatrix4fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*16);return}if(count<=18){var view=miniTempWebGLFloatBuffers[16*count];var heap=HEAPF32;value=value>>2;count*=16;for(var i=0;i>2,value+count*64>>2)}GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix4x2fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix4x2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*8)};var _emscripten_glUniformMatrix4x3fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix4x3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*12)};var _emscripten_glUseProgram=program=>{program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program};var _emscripten_glValidateProgram=program=>{GLctx.validateProgram(GL.programs[program])};var _emscripten_glVertexAttrib1f=(x0,x1)=>GLctx.vertexAttrib1f(x0,x1);var _emscripten_glVertexAttrib1fv=(index,v)=>{GLctx.vertexAttrib1f(index,HEAPF32[v>>2])};var _emscripten_glVertexAttrib2f=(x0,x1,x2)=>GLctx.vertexAttrib2f(x0,x1,x2);var _emscripten_glVertexAttrib2fv=(index,v)=>{GLctx.vertexAttrib2f(index,HEAPF32[v>>2],HEAPF32[v+4>>2])};var _emscripten_glVertexAttrib3f=(x0,x1,x2,x3)=>GLctx.vertexAttrib3f(x0,x1,x2,x3);var _emscripten_glVertexAttrib3fv=(index,v)=>{GLctx.vertexAttrib3f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2])};var _emscripten_glVertexAttrib4f=(x0,x1,x2,x3,x4)=>GLctx.vertexAttrib4f(x0,x1,x2,x3,x4);var _emscripten_glVertexAttrib4fv=(index,v)=>{GLctx.vertexAttrib4f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2],HEAPF32[v+12>>2])};var _emscripten_glVertexAttribDivisor=(index,divisor)=>{GLctx.vertexAttribDivisor(index,divisor)};var _glVertexAttribDivisor=_emscripten_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorANGLE=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorARB=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorEXT=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorNV=_glVertexAttribDivisor;var _emscripten_glVertexAttribI4i=(x0,x1,x2,x3,x4)=>GLctx.vertexAttribI4i(x0,x1,x2,x3,x4);var _emscripten_glVertexAttribI4iv=(index,v)=>{GLctx.vertexAttribI4i(index,HEAP32[v>>2],HEAP32[v+4>>2],HEAP32[v+8>>2],HEAP32[v+12>>2])};var _emscripten_glVertexAttribI4ui=(x0,x1,x2,x3,x4)=>GLctx.vertexAttribI4ui(x0,x1,x2,x3,x4);var _emscripten_glVertexAttribI4uiv=(index,v)=>{GLctx.vertexAttribI4ui(index,HEAPU32[v>>2],HEAPU32[v+4>>2],HEAPU32[v+8>>2],HEAPU32[v+12>>2])};var _emscripten_glVertexAttribIPointer=(index,size,type,stride,ptr)=>{GLctx.vertexAttribIPointer(index,size,type,stride,ptr)};var _emscripten_glVertexAttribPointer=(index,size,type,normalized,stride,ptr)=>{GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)};var _emscripten_glViewport=(x0,x1,x2,x3)=>GLctx.viewport(x0,x1,x2,x3);var _emscripten_glWaitSync=(sync,flags,timeout)=>{timeout=Number(timeout);GLctx.waitSync(GL.syncs[sync],flags,timeout)};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var _emscripten_request_animation_frame_loop=(cb,userData)=>{function tick(timeStamp){if(getWasmTableEntry(cb)(timeStamp,userData)){requestAnimationFrame(tick)}}return requestAnimationFrame(tick)};var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var _glGetIntegerv=_emscripten_glGetIntegerv;var _glGetString=_emscripten_glGetString;var _glGetStringi=_emscripten_glGetStringi;var _llvm_eh_typeid_for=type=>type;function _random_get(buffer,size){try{randomFill(HEAPU8.subarray(buffer,buffer+size));return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();for(let i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));var miniTempWebGLFloatBuffersStorage=new Float32Array(288);for(var i=0;i<=288;++i){miniTempWebGLFloatBuffers[i]=miniTempWebGLFloatBuffersStorage.subarray(0,i)}var miniTempWebGLIntBuffersStorage=new Int32Array(288);for(var i=0;i<=288;++i){miniTempWebGLIntBuffers[i]=miniTempWebGLIntBuffersStorage.subarray(0,i)}{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;Module["GL"]=GL;var _malloc,_add_font,_add_image,_add_image_with_rid,_allocate,_apply_scene_transactions,_command,_deallocate,_destroy,_devtools_rendering_set_show_fps_meter,_devtools_rendering_set_show_hit_testing,_devtools_rendering_set_show_ruler,_devtools_rendering_set_show_stats,_devtools_rendering_set_show_tiles,_drain_missing_images,_export_node_as,_get_default_fallback_fonts,_get_image_bytes,_get_image_size,_get_node_absolute_bounding_box,_get_node_id_from_point,_get_node_ids_from_envelope,_get_node_ids_from_point,_grida_fonts_analyze_family,_grida_fonts_free,_grida_fonts_parse_font,_grida_markdown_to_html,_grida_svg_optimize,_grida_svg_to_document,_has_missing_fonts,_highlight_strokes,_init,_init_with_backend,_list_available_fonts,_list_missing_fonts,_load_benchmark_scene,_load_dummy_scene,_load_scene_grida,_load_scene_grida1,_loaded_scene_ids,_pointer_move,_query_paint_groups,_redraw,_resize_surface,_resolve_image,_runtime_renderer_set_layer_compositing,_runtime_renderer_set_outline_mode,_runtime_renderer_set_pixel_preview_scale,_runtime_renderer_set_pixel_preview_stable,_runtime_renderer_set_render_policy_flags,_runtime_renderer_set_skip_layout,_set_debug,_set_default_fallback_fonts,_set_main_camera_transform,_set_surface_overlay_config,_set_verbose,_surface_get_cursor,_surface_get_hovered_node,_surface_get_selected_nodes,_surface_pointer_down,_surface_pointer_move,_surface_pointer_up,_surface_set_selection,_switch_scene,_text_edit_command,_text_edit_enter,_text_edit_exit,_text_edit_get_caret_rect,_text_edit_get_selected_html,_text_edit_get_selected_text,_text_edit_get_selection_rects,_text_edit_get_text,_text_edit_ime_cancel,_text_edit_ime_commit,_text_edit_ime_set_preedit,_text_edit_is_active,_text_edit_paste_html,_text_edit_paste_text,_text_edit_pointer_down,_text_edit_pointer_move,_text_edit_pointer_up,_text_edit_redo,_text_edit_set_color,_text_edit_set_font_family,_text_edit_set_font_size,_text_edit_tick,_text_edit_toggle_bold,_text_edit_toggle_italic,_text_edit_toggle_strikethrough,_text_edit_toggle_underline,_text_edit_undo,_tick,_to_vector_network,_toggle_debug,_main,_emscripten_builtin_memalign,_setThrew,__emscripten_tempret_set,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,___cxa_decrement_exception_refcount,___cxa_increment_exception_refcount,___cxa_can_catch,___cxa_get_exception_ptr,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_malloc=wasmExports["Zg"];_add_font=Module["_add_font"]=wasmExports["$g"];_add_image=Module["_add_image"]=wasmExports["ah"];_add_image_with_rid=Module["_add_image_with_rid"]=wasmExports["bh"];_allocate=Module["_allocate"]=wasmExports["ch"];_apply_scene_transactions=Module["_apply_scene_transactions"]=wasmExports["dh"];_command=Module["_command"]=wasmExports["eh"];_deallocate=Module["_deallocate"]=wasmExports["fh"];_destroy=Module["_destroy"]=wasmExports["gh"];_devtools_rendering_set_show_fps_meter=Module["_devtools_rendering_set_show_fps_meter"]=wasmExports["hh"];_devtools_rendering_set_show_hit_testing=Module["_devtools_rendering_set_show_hit_testing"]=wasmExports["ih"];_devtools_rendering_set_show_ruler=Module["_devtools_rendering_set_show_ruler"]=wasmExports["jh"];_devtools_rendering_set_show_stats=Module["_devtools_rendering_set_show_stats"]=wasmExports["kh"];_devtools_rendering_set_show_tiles=Module["_devtools_rendering_set_show_tiles"]=wasmExports["lh"];_drain_missing_images=Module["_drain_missing_images"]=wasmExports["mh"];_export_node_as=Module["_export_node_as"]=wasmExports["nh"];_get_default_fallback_fonts=Module["_get_default_fallback_fonts"]=wasmExports["oh"];_get_image_bytes=Module["_get_image_bytes"]=wasmExports["ph"];_get_image_size=Module["_get_image_size"]=wasmExports["qh"];_get_node_absolute_bounding_box=Module["_get_node_absolute_bounding_box"]=wasmExports["rh"];_get_node_id_from_point=Module["_get_node_id_from_point"]=wasmExports["sh"];_get_node_ids_from_envelope=Module["_get_node_ids_from_envelope"]=wasmExports["th"];_get_node_ids_from_point=Module["_get_node_ids_from_point"]=wasmExports["uh"];_grida_fonts_analyze_family=Module["_grida_fonts_analyze_family"]=wasmExports["vh"];_grida_fonts_free=Module["_grida_fonts_free"]=wasmExports["wh"];_grida_fonts_parse_font=Module["_grida_fonts_parse_font"]=wasmExports["xh"];_grida_markdown_to_html=Module["_grida_markdown_to_html"]=wasmExports["yh"];_grida_svg_optimize=Module["_grida_svg_optimize"]=wasmExports["zh"];_grida_svg_to_document=Module["_grida_svg_to_document"]=wasmExports["Ah"];_has_missing_fonts=Module["_has_missing_fonts"]=wasmExports["Bh"];_highlight_strokes=Module["_highlight_strokes"]=wasmExports["Ch"];_init=Module["_init"]=wasmExports["Dh"];_init_with_backend=Module["_init_with_backend"]=wasmExports["Eh"];_list_available_fonts=Module["_list_available_fonts"]=wasmExports["Fh"];_list_missing_fonts=Module["_list_missing_fonts"]=wasmExports["Gh"];_load_benchmark_scene=Module["_load_benchmark_scene"]=wasmExports["Hh"];_load_dummy_scene=Module["_load_dummy_scene"]=wasmExports["Ih"];_load_scene_grida=Module["_load_scene_grida"]=wasmExports["Jh"];_load_scene_grida1=Module["_load_scene_grida1"]=wasmExports["Kh"];_loaded_scene_ids=Module["_loaded_scene_ids"]=wasmExports["Lh"];_pointer_move=Module["_pointer_move"]=wasmExports["Mh"];_query_paint_groups=Module["_query_paint_groups"]=wasmExports["Nh"];_redraw=Module["_redraw"]=wasmExports["Oh"];_resize_surface=Module["_resize_surface"]=wasmExports["Ph"];_resolve_image=Module["_resolve_image"]=wasmExports["Qh"];_runtime_renderer_set_layer_compositing=Module["_runtime_renderer_set_layer_compositing"]=wasmExports["Rh"];_runtime_renderer_set_outline_mode=Module["_runtime_renderer_set_outline_mode"]=wasmExports["Sh"];_runtime_renderer_set_pixel_preview_scale=Module["_runtime_renderer_set_pixel_preview_scale"]=wasmExports["Th"];_runtime_renderer_set_pixel_preview_stable=Module["_runtime_renderer_set_pixel_preview_stable"]=wasmExports["Uh"];_runtime_renderer_set_render_policy_flags=Module["_runtime_renderer_set_render_policy_flags"]=wasmExports["Vh"];_runtime_renderer_set_skip_layout=Module["_runtime_renderer_set_skip_layout"]=wasmExports["Wh"];_set_debug=Module["_set_debug"]=wasmExports["Xh"];_set_default_fallback_fonts=Module["_set_default_fallback_fonts"]=wasmExports["Yh"];_set_main_camera_transform=Module["_set_main_camera_transform"]=wasmExports["Zh"];_set_surface_overlay_config=Module["_set_surface_overlay_config"]=wasmExports["_h"];_set_verbose=Module["_set_verbose"]=wasmExports["$h"];_surface_get_cursor=Module["_surface_get_cursor"]=wasmExports["ai"];_surface_get_hovered_node=Module["_surface_get_hovered_node"]=wasmExports["bi"];_surface_get_selected_nodes=Module["_surface_get_selected_nodes"]=wasmExports["ci"];_surface_pointer_down=Module["_surface_pointer_down"]=wasmExports["di"];_surface_pointer_move=Module["_surface_pointer_move"]=wasmExports["ei"];_surface_pointer_up=Module["_surface_pointer_up"]=wasmExports["fi"];_surface_set_selection=Module["_surface_set_selection"]=wasmExports["gi"];_switch_scene=Module["_switch_scene"]=wasmExports["hi"];_text_edit_command=Module["_text_edit_command"]=wasmExports["ii"];_text_edit_enter=Module["_text_edit_enter"]=wasmExports["ji"];_text_edit_exit=Module["_text_edit_exit"]=wasmExports["ki"];_text_edit_get_caret_rect=Module["_text_edit_get_caret_rect"]=wasmExports["li"];_text_edit_get_selected_html=Module["_text_edit_get_selected_html"]=wasmExports["mi"];_text_edit_get_selected_text=Module["_text_edit_get_selected_text"]=wasmExports["ni"];_text_edit_get_selection_rects=Module["_text_edit_get_selection_rects"]=wasmExports["oi"];_text_edit_get_text=Module["_text_edit_get_text"]=wasmExports["pi"];_text_edit_ime_cancel=Module["_text_edit_ime_cancel"]=wasmExports["qi"];_text_edit_ime_commit=Module["_text_edit_ime_commit"]=wasmExports["ri"];_text_edit_ime_set_preedit=Module["_text_edit_ime_set_preedit"]=wasmExports["si"];_text_edit_is_active=Module["_text_edit_is_active"]=wasmExports["ti"];_text_edit_paste_html=Module["_text_edit_paste_html"]=wasmExports["ui"];_text_edit_paste_text=Module["_text_edit_paste_text"]=wasmExports["vi"];_text_edit_pointer_down=Module["_text_edit_pointer_down"]=wasmExports["wi"];_text_edit_pointer_move=Module["_text_edit_pointer_move"]=wasmExports["xi"];_text_edit_pointer_up=Module["_text_edit_pointer_up"]=wasmExports["yi"];_text_edit_redo=Module["_text_edit_redo"]=wasmExports["zi"];_text_edit_set_color=Module["_text_edit_set_color"]=wasmExports["Ai"];_text_edit_set_font_family=Module["_text_edit_set_font_family"]=wasmExports["Bi"];_text_edit_set_font_size=Module["_text_edit_set_font_size"]=wasmExports["Ci"];_text_edit_tick=Module["_text_edit_tick"]=wasmExports["Di"];_text_edit_toggle_bold=Module["_text_edit_toggle_bold"]=wasmExports["Ei"];_text_edit_toggle_italic=Module["_text_edit_toggle_italic"]=wasmExports["Fi"];_text_edit_toggle_strikethrough=Module["_text_edit_toggle_strikethrough"]=wasmExports["Gi"];_text_edit_toggle_underline=Module["_text_edit_toggle_underline"]=wasmExports["Hi"];_text_edit_undo=Module["_text_edit_undo"]=wasmExports["Ii"];_tick=Module["_tick"]=wasmExports["Ji"];_to_vector_network=Module["_to_vector_network"]=wasmExports["Ki"];_toggle_debug=Module["_toggle_debug"]=wasmExports["Li"];_main=Module["_main"]=wasmExports["Mi"];_emscripten_builtin_memalign=wasmExports["Ni"];_setThrew=wasmExports["Oi"];__emscripten_tempret_set=wasmExports["Pi"];__emscripten_stack_restore=wasmExports["Qi"];__emscripten_stack_alloc=wasmExports["Ri"];_emscripten_stack_get_current=wasmExports["Si"];___cxa_decrement_exception_refcount=wasmExports["Ti"];___cxa_increment_exception_refcount=wasmExports["Ui"];___cxa_can_catch=wasmExports["Vi"];___cxa_get_exception_ptr=wasmExports["Wi"];memory=wasmMemory=wasmExports["Xg"];__indirect_function_table=wasmTable=wasmExports["_g"]}var wasmImports={I:___cxa_begin_catch,Q:___cxa_end_catch,a:___cxa_find_matching_catch_2,n:___cxa_find_matching_catch_3,ka:___cxa_find_matching_catch_4,La:___cxa_rethrow,K:___cxa_throw,ib:___cxa_uncaught_exceptions,e:___resumeException,Oa:___syscall_fcntl64,zb:___syscall_fstat64,vb:___syscall_getcwd,Ab:___syscall_ioctl,wb:___syscall_lstat64,xb:___syscall_newfstatat,Pa:___syscall_openat,yb:___syscall_stat64,Db:__abort_js,kb:__emscripten_throw_longjmp,qb:__gmtime_js,ob:__mmap_js,pb:__munmap_js,Eb:__tzset_js,Cb:_clock_time_get,Bb:_emscripten_date_now,mb:_emscripten_get_heap_max,Ef:_emscripten_glActiveTexture,Ff:_emscripten_glAttachShader,ge:_emscripten_glBeginQuery,ae:_emscripten_glBeginQueryEXT,Ic:_emscripten_glBeginTransformFeedback,Gf:_emscripten_glBindAttribLocation,Hf:_emscripten_glBindBuffer,Fc:_emscripten_glBindBufferBase,Gc:_emscripten_glBindBufferRange,Ee:_emscripten_glBindFramebuffer,Fe:_emscripten_glBindRenderbuffer,me:_emscripten_glBindSampler,If:_emscripten_glBindTexture,Vb:_emscripten_glBindTransformFeedback,_e:_emscripten_glBindVertexArray,bf:_emscripten_glBindVertexArrayOES,Jf:_emscripten_glBlendColor,Kf:_emscripten_glBlendEquation,Md:_emscripten_glBlendEquationSeparate,Lf:_emscripten_glBlendFunc,Ld:_emscripten_glBlendFuncSeparate,ye:_emscripten_glBlitFramebuffer,Mf:_emscripten_glBufferData,Nf:_emscripten_glBufferSubData,Ge:_emscripten_glCheckFramebufferStatus,Of:_emscripten_glClear,jc:_emscripten_glClearBufferfi,kc:_emscripten_glClearBufferfv,mc:_emscripten_glClearBufferiv,lc:_emscripten_glClearBufferuiv,Pf:_emscripten_glClearColor,Kd:_emscripten_glClearDepthf,Qf:_emscripten_glClearStencil,ve:_emscripten_glClientWaitSync,bd:_emscripten_glClipControlEXT,Rf:_emscripten_glColorMask,Sf:_emscripten_glCompileShader,Tf:_emscripten_glCompressedTexImage2D,Uc:_emscripten_glCompressedTexImage3D,Uf:_emscripten_glCompressedTexSubImage2D,Tc:_emscripten_glCompressedTexSubImage3D,xe:_emscripten_glCopyBufferSubData,Jd:_emscripten_glCopyTexImage2D,Vf:_emscripten_glCopyTexSubImage2D,Vc:_emscripten_glCopyTexSubImage3D,Wf:_emscripten_glCreateProgram,Xf:_emscripten_glCreateShader,Yf:_emscripten_glCullFace,Zf:_emscripten_glDeleteBuffers,He:_emscripten_glDeleteFramebuffers,_f:_emscripten_glDeleteProgram,he:_emscripten_glDeleteQueries,be:_emscripten_glDeleteQueriesEXT,Ie:_emscripten_glDeleteRenderbuffers,ne:_emscripten_glDeleteSamplers,$f:_emscripten_glDeleteShader,we:_emscripten_glDeleteSync,ag:_emscripten_glDeleteTextures,Ub:_emscripten_glDeleteTransformFeedbacks,$e:_emscripten_glDeleteVertexArrays,cf:_emscripten_glDeleteVertexArraysOES,Id:_emscripten_glDepthFunc,bg:_emscripten_glDepthMask,Hd:_emscripten_glDepthRangef,Gd:_emscripten_glDetachShader,cg:_emscripten_glDisable,dg:_emscripten_glDisableVertexAttribArray,eg:_emscripten_glDrawArrays,Ye:_emscripten_glDrawArraysInstanced,Pd:_emscripten_glDrawArraysInstancedANGLE,Hb:_emscripten_glDrawArraysInstancedARB,Ve:_emscripten_glDrawArraysInstancedBaseInstanceWEBGL,_c:_emscripten_glDrawArraysInstancedEXT,Ib:_emscripten_glDrawArraysInstancedNV,Te:_emscripten_glDrawBuffers,Yc:_emscripten_glDrawBuffersEXT,Qd:_emscripten_glDrawBuffersWEBGL,fg:_emscripten_glDrawElements,Ze:_emscripten_glDrawElementsInstanced,Od:_emscripten_glDrawElementsInstancedANGLE,Fb:_emscripten_glDrawElementsInstancedARB,We:_emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL,Gb:_emscripten_glDrawElementsInstancedEXT,Zc:_emscripten_glDrawElementsInstancedNV,Ne:_emscripten_glDrawRangeElements,gg:_emscripten_glEnable,hg:_emscripten_glEnableVertexAttribArray,ie:_emscripten_glEndQuery,ce:_emscripten_glEndQueryEXT,Hc:_emscripten_glEndTransformFeedback,se:_emscripten_glFenceSync,ig:_emscripten_glFinish,jg:_emscripten_glFlush,Je:_emscripten_glFramebufferRenderbuffer,Ke:_emscripten_glFramebufferTexture2D,Lc:_emscripten_glFramebufferTextureLayer,kg:_emscripten_glFrontFace,lg:_emscripten_glGenBuffers,Le:_emscripten_glGenFramebuffers,je:_emscripten_glGenQueries,de:_emscripten_glGenQueriesEXT,Me:_emscripten_glGenRenderbuffers,oe:_emscripten_glGenSamplers,mg:_emscripten_glGenTextures,Tb:_emscripten_glGenTransformFeedbacks,Xe:_emscripten_glGenVertexArrays,df:_emscripten_glGenVertexArraysOES,Ae:_emscripten_glGenerateMipmap,Fd:_emscripten_glGetActiveAttrib,Ed:_emscripten_glGetActiveUniform,ec:_emscripten_glGetActiveUniformBlockName,fc:_emscripten_glGetActiveUniformBlockiv,hc:_emscripten_glGetActiveUniformsiv,Dd:_emscripten_glGetAttachedShaders,Cd:_emscripten_glGetAttribLocation,Bd:_emscripten_glGetBooleanv,$b:_emscripten_glGetBufferParameteri64v,ng:_emscripten_glGetBufferParameteriv,og:_emscripten_glGetError,pg:_emscripten_glGetFloatv,vc:_emscripten_glGetFragDataLocation,Be:_emscripten_glGetFramebufferAttachmentParameteriv,ac:_emscripten_glGetInteger64i_v,cc:_emscripten_glGetInteger64v,Jc:_emscripten_glGetIntegeri_v,qg:_emscripten_glGetIntegerv,Lb:_emscripten_glGetInternalformativ,Pb:_emscripten_glGetProgramBinary,rg:_emscripten_glGetProgramInfoLog,sg:_emscripten_glGetProgramiv,Zd:_emscripten_glGetQueryObjecti64vEXT,Sd:_emscripten_glGetQueryObjectivEXT,_d:_emscripten_glGetQueryObjectui64vEXT,ke:_emscripten_glGetQueryObjectuiv,ee:_emscripten_glGetQueryObjectuivEXT,le:_emscripten_glGetQueryiv,fe:_emscripten_glGetQueryivEXT,Ce:_emscripten_glGetRenderbufferParameteriv,Wb:_emscripten_glGetSamplerParameterfv,Yb:_emscripten_glGetSamplerParameteriv,tg:_emscripten_glGetShaderInfoLog,Wd:_emscripten_glGetShaderPrecisionFormat,Ad:_emscripten_glGetShaderSource,ug:_emscripten_glGetShaderiv,vg:_emscripten_glGetString,af:_emscripten_glGetStringi,bc:_emscripten_glGetSynciv,zd:_emscripten_glGetTexParameterfv,yd:_emscripten_glGetTexParameteriv,Dc:_emscripten_glGetTransformFeedbackVarying,gc:_emscripten_glGetUniformBlockIndex,ic:_emscripten_glGetUniformIndices,xg:_emscripten_glGetUniformLocation,xd:_emscripten_glGetUniformfv,wd:_emscripten_glGetUniformiv,wc:_emscripten_glGetUniformuiv,Cc:_emscripten_glGetVertexAttribIiv,Bc:_emscripten_glGetVertexAttribIuiv,td:_emscripten_glGetVertexAttribPointerv,vd:_emscripten_glGetVertexAttribfv,ud:_emscripten_glGetVertexAttribiv,sd:_emscripten_glHint,Xd:_emscripten_glInvalidateFramebuffer,Yd:_emscripten_glInvalidateSubFramebuffer,rd:_emscripten_glIsBuffer,qd:_emscripten_glIsEnabled,pd:_emscripten_glIsFramebuffer,od:_emscripten_glIsProgram,Sc:_emscripten_glIsQuery,Td:_emscripten_glIsQueryEXT,nd:_emscripten_glIsRenderbuffer,_b:_emscripten_glIsSampler,md:_emscripten_glIsShader,te:_emscripten_glIsSync,yg:_emscripten_glIsTexture,Sb:_emscripten_glIsTransformFeedback,Kc:_emscripten_glIsVertexArray,Rd:_emscripten_glIsVertexArrayOES,zg:_emscripten_glLineWidth,Ag:_emscripten_glLinkProgram,Re:_emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL,Se:_emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL,Rb:_emscripten_glPauseTransformFeedback,Bg:_emscripten_glPixelStorei,ad:_emscripten_glPolygonModeWEBGL,ld:_emscripten_glPolygonOffset,cd:_emscripten_glPolygonOffsetClampEXT,Ob:_emscripten_glProgramBinary,Nb:_emscripten_glProgramParameteri,$d:_emscripten_glQueryCounterEXT,Ue:_emscripten_glReadBuffer,Cg:_emscripten_glReadPixels,kd:_emscripten_glReleaseShaderCompiler,De:_emscripten_glRenderbufferStorage,ze:_emscripten_glRenderbufferStorageMultisample,Qb:_emscripten_glResumeTransformFeedback,jd:_emscripten_glSampleCoverage,pe:_emscripten_glSamplerParameterf,Zb:_emscripten_glSamplerParameterfv,qe:_emscripten_glSamplerParameteri,re:_emscripten_glSamplerParameteriv,Dg:_emscripten_glScissor,id:_emscripten_glShaderBinary,Eg:_emscripten_glShaderSource,Fg:_emscripten_glStencilFunc,Gg:_emscripten_glStencilFuncSeparate,Hg:_emscripten_glStencilMask,Ig:_emscripten_glStencilMaskSeparate,Jg:_emscripten_glStencilOp,Kg:_emscripten_glStencilOpSeparate,Lg:_emscripten_glTexImage2D,Xc:_emscripten_glTexImage3D,Mg:_emscripten_glTexParameterf,Ng:_emscripten_glTexParameterfv,Og:_emscripten_glTexParameteri,Pg:_emscripten_glTexParameteriv,Oe:_emscripten_glTexStorage2D,Mb:_emscripten_glTexStorage3D,Qg:_emscripten_glTexSubImage2D,Wc:_emscripten_glTexSubImage3D,Ec:_emscripten_glTransformFeedbackVaryings,Rg:_emscripten_glUniform1f,Sg:_emscripten_glUniform1fv,Af:_emscripten_glUniform1i,Bf:_emscripten_glUniform1iv,uc:_emscripten_glUniform1ui,qc:_emscripten_glUniform1uiv,Cf:_emscripten_glUniform2f,Df:_emscripten_glUniform2fv,yf:_emscripten_glUniform2i,xf:_emscripten_glUniform2iv,tc:_emscripten_glUniform2ui,pc:_emscripten_glUniform2uiv,wf:_emscripten_glUniform3f,vf:_emscripten_glUniform3fv,uf:_emscripten_glUniform3i,tf:_emscripten_glUniform3iv,sc:_emscripten_glUniform3ui,oc:_emscripten_glUniform3uiv,sf:_emscripten_glUniform4f,rf:_emscripten_glUniform4fv,ef:_emscripten_glUniform4i,ff:_emscripten_glUniform4iv,rc:_emscripten_glUniform4ui,nc:_emscripten_glUniform4uiv,dc:_emscripten_glUniformBlockBinding,gf:_emscripten_glUniformMatrix2fv,Rc:_emscripten_glUniformMatrix2x3fv,Pc:_emscripten_glUniformMatrix2x4fv,hf:_emscripten_glUniformMatrix3fv,Qc:_emscripten_glUniformMatrix3x2fv,Nc:_emscripten_glUniformMatrix3x4fv,jf:_emscripten_glUniformMatrix4fv,Oc:_emscripten_glUniformMatrix4x2fv,Mc:_emscripten_glUniformMatrix4x3fv,kf:_emscripten_glUseProgram,hd:_emscripten_glValidateProgram,lf:_emscripten_glVertexAttrib1f,gd:_emscripten_glVertexAttrib1fv,fd:_emscripten_glVertexAttrib2f,mf:_emscripten_glVertexAttrib2fv,ed:_emscripten_glVertexAttrib3f,nf:_emscripten_glVertexAttrib3fv,dd:_emscripten_glVertexAttrib4f,of:_emscripten_glVertexAttrib4fv,Pe:_emscripten_glVertexAttribDivisor,Nd:_emscripten_glVertexAttribDivisorANGLE,Jb:_emscripten_glVertexAttribDivisorARB,$c:_emscripten_glVertexAttribDivisorEXT,Kb:_emscripten_glVertexAttribDivisorNV,Ac:_emscripten_glVertexAttribI4i,yc:_emscripten_glVertexAttribI4iv,zc:_emscripten_glVertexAttribI4ui,xc:_emscripten_glVertexAttribI4uiv,Qe:_emscripten_glVertexAttribIPointer,pf:_emscripten_glVertexAttribPointer,qf:_emscripten_glViewport,ue:_emscripten_glWaitSync,$a:_emscripten_request_animation_frame_loop,lb:_emscripten_resize_heap,sb:_environ_get,tb:_environ_sizes_get,Vg:_exit,ta:_fd_close,nb:_fd_pread,Na:_fd_read,rb:_fd_seek,sa:_fd_write,Tg:_glGetIntegerv,Ta:_glGetString,Ug:_glGetStringi,Ud:invoke_dd,Vd:invoke_dddd,Ja:invoke_diii,Wa:invoke_fdiiii,Va:invoke_fdiiiii,Wg:invoke_fii,Ka:invoke_fiii,w:invoke_fiiidi,Y:invoke_fiiif,x:invoke_fiiiidi,v:invoke_i,j:invoke_ii,J:invoke_iif,db:invoke_iiffi,xa:invoke_iiffiii,f:invoke_iii,ab:invoke_iiif,la:invoke_iiiff,oa:invoke_iiifi,g:invoke_iiii,u:invoke_iiiif,L:invoke_iiiiff,l:invoke_iiiii,hb:invoke_iiiiid,r:invoke_iiiiii,C:invoke_iiiiiii,H:invoke_iiiiiiii,t:invoke_iiiiiiiii,Ua:invoke_iiiiiiiiii,ga:invoke_iiiiiiiiiiii,va:invoke_iiiiiiiiiiiifiij,T:invoke_iij,wg:invoke_iijj,q:invoke_ij,jb:invoke_j,na:invoke_ji,s:invoke_jiii,ha:invoke_jiiii,aa:invoke_jiijj,N:invoke_jjji,k:invoke_v,zf:invoke_vff,b:invoke_vi,S:invoke_vid,X:invoke_vif,y:invoke_viff,G:invoke_viffff,ca:invoke_vifffff,Xa:invoke_viffffff,E:invoke_viffi,wa:invoke_viffiiiiiii,c:invoke_vii,_a:invoke_viidii,R:invoke_viif,F:invoke_viiff,Ra:invoke_viiffii,W:invoke_viifi,Aa:invoke_viififii,B:invoke_viifiiifi,d:invoke_viii,Fa:invoke_viiif,Ca:invoke_viiiff,z:invoke_viiiffi,O:invoke_viiiffiffii,P:invoke_viiififiiiiiiiiiiii,i:invoke_viiii,Za:invoke_viiiidididii,qa:invoke_viiiif,Da:invoke_viiiiff,ra:invoke_viiiiffi,Ba:invoke_viiiifi,h:invoke_viiiii,eb:invoke_viiiiif,Ya:invoke_viiiiiffiii,Ga:invoke_viiiiifi,m:invoke_viiiiii,Qa:invoke_viiiiiiff,p:invoke_viiiiiii,Z:invoke_viiiiiiii,Ia:invoke_viiiiiiiifij,ba:invoke_viiiiiiiii,V:invoke_viiiiiiiiii,gb:invoke_viiiiiiiiiifij,ya:invoke_viiiiiiiiiii,fa:invoke_viiiiiiiiiiiiiii,Sa:invoke_viiiiiiji,M:invoke_viiij,D:invoke_viiijii,U:invoke_viij,za:invoke_viijffiiii,o:invoke_viiji,Ma:invoke_viijiffi,ia:invoke_viijii,Ea:invoke_viijiii,ea:invoke_viijiiiif,ma:invoke_viijiiiii,pa:invoke_viijj,da:invoke_vij,$:invoke_viji,bb:invoke_vijififi,A:invoke_vijii,Ha:invoke_vijiifi,cb:invoke_vijiififi,_:invoke_vijiii,fb:invoke_vijijjiii,ja:invoke_vijjjj,Xb:invoke_vjii,ua:_llvm_eh_typeid_for,ub:_random_get};function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ij(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iijj(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ji(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vff(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viij(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiji(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiff(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiijj(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiji(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iij(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiif(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vid(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viji(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiiifiij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vif(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiff(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffi(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vjii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiffii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiff(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiffi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiifij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiifij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijijjiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viifiiifi(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiffi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiifi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiif(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiififiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiiiif(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffiffii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiff(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiif(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiif(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiffi(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viififii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_vijiififi(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijififi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijj(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jjji(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiif(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiifi(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iif(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viifi(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijffiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vij(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viif(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiffiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viff(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vifffff(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiidi(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viidii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiidididii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiiidi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiffiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiijii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffffff(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiif(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fdiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fdiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_dddd(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_dd(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijjjj(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_j(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiiiid(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_diii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function callMain(args=[]){var entryFunction=_main;args.unshift(thisProgram);var argc=args.length;var argv=stackAlloc((argc+1)*4);var argv_ptr=argv;for(var arg of args){HEAPU32[argv_ptr>>2]=stringToUTF8OnStack(arg);argv_ptr+=4}HEAPU32[argv_ptr>>2]=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(args=arguments_){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();var noInitialRun=Module["noInitialRun"]||false;if(!noInitialRun)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} +var createGridaCanvas=(()=>{var _scriptName=globalThis.document?.currentScript?.src;return async function(moduleArg={}){var moduleRtn;var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};if(typeof __filename!="undefined"){_scriptName=__filename}else{}var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{readAsync=async url=>{var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");var readyPromiseResolve,readyPromiseReject;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var HEAP64,HEAPU64;var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["Zg"]();FS.ignorePermissions=false}function preMain(){}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("grida_canvas_wasm.wasm")}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);var noExitRuntime=true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var exceptionCaught=[];var uncaughtExceptionCount=0;var ___cxa_begin_catch=ptr=>{var info=new ExceptionInfo(ptr);if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(info);return ___cxa_get_exception_ptr(ptr)};var exceptionLast=0;var ___cxa_end_catch=()=>{_setThrew(0,0);var info=exceptionCaught.pop();___cxa_decrement_exception_refcount(info.excPtr);exceptionLast=0};class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var setTempRet0=val=>__emscripten_tempret_set(val);var findMatchingCatch=args=>{var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0}var info=new ExceptionInfo(thrown);info.set_adjusted_ptr(thrown);var thrownType=info.get_type();if(!thrownType){setTempRet0(0);return thrown}for(var caughtType of args){if(caughtType===0||caughtType===thrownType){break}var adjusted_ptr_addr=info.ptr+16;if(___cxa_can_catch(caughtType,thrownType,adjusted_ptr_addr)){setTempRet0(caughtType);return thrown}}setTempRet0(thrownType);return thrown};var ___cxa_find_matching_catch_2=()=>findMatchingCatch([]);var ___cxa_find_matching_catch_3=arg0=>findMatchingCatch([arg0]);var ___cxa_find_matching_catch_4=(arg0,arg1)=>findMatchingCatch([arg0,arg1]);var ___cxa_rethrow=()=>{var info=exceptionCaught.pop();if(!info){abort("no exception to throw")}var ptr=info.excPtr;if(!info.get_rethrown()){exceptionCaught.push(info);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}___cxa_increment_exception_refcount(ptr);exceptionLast=ptr;throw exceptionLast};var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);___cxa_increment_exception_refcount(ptr);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var ___cxa_uncaught_exceptions=()=>uncaughtExceptionCount;var ___resumeException=ptr=>{if(!exceptionLast){exceptionLast=ptr}throw exceptionLast};var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>nodeCrypto.randomFillSync(view)}return view=>crypto.getRandomValues(view)};var randomFill=view=>{(randomFill=initRandomFill())(view)};var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var zeroMemory=(ptr,size)=>HEAPU8.fill(0,ptr,ptr+size);var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{size=alignMemory(size,65536);var ptr=_emscripten_builtin_memalign(65536,size);if(ptr)zeroMemory(ptr,size);return ptr};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack=""}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,readFiles:{},ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);setattr(arg,attr)},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS_modeStringToFlags(flags):flags;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}if(Module["logReadFiles"]&&!(flags&1)){if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){data=new Uint8Array(intArrayFromString(data,true))}if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{abort("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)abort("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{return SYSCALLS.writeStat(buf,FS.fstat(fd))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(size>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_lstat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.lstat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.writeStat(buf,nofollow?FS.lstat(path):FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.writeStat(buf,FS.stat(path))}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var __emscripten_throw_longjmp=()=>{throw Infinity};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function __gmtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getUTCSeconds();HEAP32[tmPtr+4>>2]=date.getUTCMinutes();HEAP32[tmPtr+8>>2]=date.getUTCHours();HEAP32[tmPtr+12>>2]=date.getUTCDate();HEAP32[tmPtr+16>>2]=date.getUTCMonth();HEAP32[tmPtr+20>>2]=date.getUTCFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getUTCDay();var start=Date.UTC(date.getUTCFullYear(),0,1,0,0,0,0);var yday=(date.getTime()-start)/(1e3*60*60*24)|0;HEAP32[tmPtr+28>>2]=yday}function __mmap_js(len,prot,flags,fd,offset,allocated,addr){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);var res=FS.mmap(stream,len,offset,prot,flags);var ptr=res.ptr;HEAP32[allocated>>2]=res.allocated;HEAPU32[addr>>2]=ptr;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function __munmap_js(addr,len,prot,flags,fd,offset){offset=bigintToI53Checked(offset);try{var stream=SYSCALLS.getStreamFromFD(fd);if(prot&2){SYSCALLS.doMsync(addr,stream,len,flags,offset)}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetperformance.now();var _emscripten_date_now=()=>Date.now();var nowIsMonotonic=1;var checkWasiClock=clock_id=>clock_id>=0&&clock_id<=3;function _clock_time_get(clk_id,ignored_precision,ptime){ignored_precision=bigintToI53Checked(ignored_precision);if(!checkWasiClock(clk_id)){return 28}var now;if(clk_id===0){now=_emscripten_date_now()}else if(nowIsMonotonic){now=_emscripten_get_now()}else{return 52}var nsec=Math.round(now*1e3*1e3);HEAP64[ptime>>3]=BigInt(nsec);return 0}var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();var GLctx;var webgl_enable_ANGLE_instanced_arrays=ctx=>{var ext=ctx.getExtension("ANGLE_instanced_arrays");if(ext){ctx["vertexAttribDivisor"]=(index,divisor)=>ext["vertexAttribDivisorANGLE"](index,divisor);ctx["drawArraysInstanced"]=(mode,first,count,primcount)=>ext["drawArraysInstancedANGLE"](mode,first,count,primcount);ctx["drawElementsInstanced"]=(mode,count,type,indices,primcount)=>ext["drawElementsInstancedANGLE"](mode,count,type,indices,primcount);return 1}};var webgl_enable_OES_vertex_array_object=ctx=>{var ext=ctx.getExtension("OES_vertex_array_object");if(ext){ctx["createVertexArray"]=()=>ext["createVertexArrayOES"]();ctx["deleteVertexArray"]=vao=>ext["deleteVertexArrayOES"](vao);ctx["bindVertexArray"]=vao=>ext["bindVertexArrayOES"](vao);ctx["isVertexArray"]=vao=>ext["isVertexArrayOES"](vao);return 1}};var webgl_enable_WEBGL_draw_buffers=ctx=>{var ext=ctx.getExtension("WEBGL_draw_buffers");if(ext){ctx["drawBuffers"]=(n,bufs)=>ext["drawBuffersWEBGL"](n,bufs);return 1}};var webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"));var webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance=ctx=>!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"));var webgl_enable_EXT_polygon_offset_clamp=ctx=>!!(ctx.extPolygonOffsetClamp=ctx.getExtension("EXT_polygon_offset_clamp"));var webgl_enable_EXT_clip_control=ctx=>!!(ctx.extClipControl=ctx.getExtension("EXT_clip_control"));var webgl_enable_WEBGL_polygon_mode=ctx=>!!(ctx.webglPolygonMode=ctx.getExtension("WEBGL_polygon_mode"));var webgl_enable_WEBGL_multi_draw=ctx=>!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"));var getEmscriptenSupportedExtensions=ctx=>{var supportedExtensions=["ANGLE_instanced_arrays","EXT_blend_minmax","EXT_disjoint_timer_query","EXT_frag_depth","EXT_shader_texture_lod","EXT_sRGB","OES_element_index_uint","OES_fbo_render_mipmap","OES_standard_derivatives","OES_texture_float","OES_texture_half_float","OES_texture_half_float_linear","OES_vertex_array_object","WEBGL_color_buffer_float","WEBGL_depth_texture","WEBGL_draw_buffers","EXT_color_buffer_float","EXT_conservative_depth","EXT_disjoint_timer_query_webgl2","EXT_texture_norm16","NV_shader_noperspective_interpolation","WEBGL_clip_cull_distance","EXT_clip_control","EXT_color_buffer_half_float","EXT_depth_clamp","EXT_float_blend","EXT_polygon_offset_clamp","EXT_texture_compression_bptc","EXT_texture_compression_rgtc","EXT_texture_filter_anisotropic","KHR_parallel_shader_compile","OES_texture_float_linear","WEBGL_blend_func_extended","WEBGL_compressed_texture_astc","WEBGL_compressed_texture_etc","WEBGL_compressed_texture_etc1","WEBGL_compressed_texture_s3tc","WEBGL_compressed_texture_s3tc_srgb","WEBGL_debug_renderer_info","WEBGL_debug_shaders","WEBGL_lose_context","WEBGL_multi_draw","WEBGL_polygon_mode"];return(ctx.getSupportedExtensions()||[]).filter(ext=>supportedExtensions.includes(ext))};var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},queries:[],samplers:[],transformFeedbacks:[],syncs:[],stringCache:{},stringiCache:{},unpackAlignment:4,unpackRowLength:0,recordError:errorCode=>{if(!GL.lastError){GL.lastError=errorCode}},getNewId:table=>{var ret=GL.counter++;for(var i=table.length;i{for(var i=0;i>2]=id}},getSource:(shader,count,string,length)=>{var source="";for(var i=0;i>2]:undefined;source+=UTF8ToString(HEAPU32[string+i*4>>2],len)}return source},createContext:(canvas,webGLContextAttributes)=>{if(!canvas.getContextSafariWebGL2Fixed){canvas.getContextSafariWebGL2Fixed=canvas.getContext;function fixedGetContext(ver,attrs){var gl=canvas.getContextSafariWebGL2Fixed(ver,attrs);return ver=="webgl"==gl instanceof WebGLRenderingContext?gl:null}canvas.getContext=fixedGetContext}var ctx=webGLContextAttributes.majorVersion>1?canvas.getContext("webgl2",webGLContextAttributes):canvas.getContext("webgl",webGLContextAttributes);if(!ctx)return 0;var handle=GL.registerContext(ctx,webGLContextAttributes);return handle},registerContext:(ctx,webGLContextAttributes)=>{var handle=GL.getNewId(GL.contexts);var context={handle,attributes:webGLContextAttributes,version:webGLContextAttributes.majorVersion,GLctx:ctx};if(ctx.canvas)ctx.canvas.GLctxObject=context;GL.contexts[handle]=context;if(typeof webGLContextAttributes.enableExtensionsByDefault=="undefined"||webGLContextAttributes.enableExtensionsByDefault){GL.initExtensions(context)}return handle},makeContextCurrent:contextHandle=>{GL.currentContext=GL.contexts[contextHandle];Module["ctx"]=GLctx=GL.currentContext?.GLctx;return!(contextHandle&&!GLctx)},getContext:contextHandle=>GL.contexts[contextHandle],deleteContext:contextHandle=>{if(GL.currentContext===GL.contexts[contextHandle]){GL.currentContext=null}if(typeof JSEvents=="object"){JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas)}if(GL.contexts[contextHandle]?.GLctx.canvas){GL.contexts[contextHandle].GLctx.canvas.GLctxObject=undefined}GL.contexts[contextHandle]=null},initExtensions:context=>{context||=GL.currentContext;if(context.initExtensionsDone)return;context.initExtensionsDone=true;var GLctx=context.GLctx;webgl_enable_WEBGL_multi_draw(GLctx);webgl_enable_EXT_polygon_offset_clamp(GLctx);webgl_enable_EXT_clip_control(GLctx);webgl_enable_WEBGL_polygon_mode(GLctx);webgl_enable_ANGLE_instanced_arrays(GLctx);webgl_enable_OES_vertex_array_object(GLctx);webgl_enable_WEBGL_draw_buffers(GLctx);webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(GLctx);webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(GLctx);if(context.version>=2){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query_webgl2")}if(context.version<2||!GLctx.disjointTimerQueryExt){GLctx.disjointTimerQueryExt=GLctx.getExtension("EXT_disjoint_timer_query")}for(var ext of getEmscriptenSupportedExtensions(GLctx)){if(!ext.includes("lose_context")&&!ext.includes("debug")){GLctx.getExtension(ext)}}}};var _emscripten_glActiveTexture=x0=>GLctx.activeTexture(x0);var _emscripten_glAttachShader=(program,shader)=>{GLctx.attachShader(GL.programs[program],GL.shaders[shader])};var _emscripten_glBeginQuery=(target,id)=>{GLctx.beginQuery(target,GL.queries[id])};var _emscripten_glBeginQueryEXT=(target,id)=>{GLctx.disjointTimerQueryExt["beginQueryEXT"](target,GL.queries[id])};var _emscripten_glBeginTransformFeedback=x0=>GLctx.beginTransformFeedback(x0);var _emscripten_glBindAttribLocation=(program,index,name)=>{GLctx.bindAttribLocation(GL.programs[program],index,UTF8ToString(name))};var _emscripten_glBindBuffer=(target,buffer)=>{if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])};var _emscripten_glBindBufferBase=(target,index,buffer)=>{GLctx.bindBufferBase(target,index,GL.buffers[buffer])};var _emscripten_glBindBufferRange=(target,index,buffer,offset,ptrsize)=>{GLctx.bindBufferRange(target,index,GL.buffers[buffer],offset,ptrsize)};var _emscripten_glBindFramebuffer=(target,framebuffer)=>{GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])};var _emscripten_glBindRenderbuffer=(target,renderbuffer)=>{GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])};var _emscripten_glBindSampler=(unit,sampler)=>{GLctx.bindSampler(unit,GL.samplers[sampler])};var _emscripten_glBindTexture=(target,texture)=>{GLctx.bindTexture(target,GL.textures[texture])};var _emscripten_glBindTransformFeedback=(target,id)=>{GLctx.bindTransformFeedback(target,GL.transformFeedbacks[id])};var _emscripten_glBindVertexArray=vao=>{GLctx.bindVertexArray(GL.vaos[vao])};var _glBindVertexArray=_emscripten_glBindVertexArray;var _emscripten_glBindVertexArrayOES=_glBindVertexArray;var _emscripten_glBlendColor=(x0,x1,x2,x3)=>GLctx.blendColor(x0,x1,x2,x3);var _emscripten_glBlendEquation=x0=>GLctx.blendEquation(x0);var _emscripten_glBlendEquationSeparate=(x0,x1)=>GLctx.blendEquationSeparate(x0,x1);var _emscripten_glBlendFunc=(x0,x1)=>GLctx.blendFunc(x0,x1);var _emscripten_glBlendFuncSeparate=(x0,x1,x2,x3)=>GLctx.blendFuncSeparate(x0,x1,x2,x3);var _emscripten_glBlitFramebuffer=(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)=>GLctx.blitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);var _emscripten_glBufferData=(target,size,data,usage)=>{if(GL.currentContext.version>=2){if(data&&size){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}return}GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)};var _emscripten_glBufferSubData=(target,offset,size,data)=>{if(GL.currentContext.version>=2){size&&GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))};var _emscripten_glCheckFramebufferStatus=x0=>GLctx.checkFramebufferStatus(x0);var _emscripten_glClear=x0=>GLctx.clear(x0);var _emscripten_glClearBufferfi=(x0,x1,x2,x3)=>GLctx.clearBufferfi(x0,x1,x2,x3);var _emscripten_glClearBufferfv=(buffer,drawbuffer,value)=>{GLctx.clearBufferfv(buffer,drawbuffer,HEAPF32,value>>2)};var _emscripten_glClearBufferiv=(buffer,drawbuffer,value)=>{GLctx.clearBufferiv(buffer,drawbuffer,HEAP32,value>>2)};var _emscripten_glClearBufferuiv=(buffer,drawbuffer,value)=>{GLctx.clearBufferuiv(buffer,drawbuffer,HEAPU32,value>>2)};var _emscripten_glClearColor=(x0,x1,x2,x3)=>GLctx.clearColor(x0,x1,x2,x3);var _emscripten_glClearDepthf=x0=>GLctx.clearDepth(x0);var _emscripten_glClearStencil=x0=>GLctx.clearStencil(x0);var _emscripten_glClientWaitSync=(sync,flags,timeout)=>{timeout=Number(timeout);return GLctx.clientWaitSync(GL.syncs[sync],flags,timeout)};var _emscripten_glClipControlEXT=(origin,depth)=>{GLctx.extClipControl["clipControlEXT"](origin,depth)};var _emscripten_glColorMask=(red,green,blue,alpha)=>{GLctx.colorMask(!!red,!!green,!!blue,!!alpha)};var _emscripten_glCompileShader=shader=>{GLctx.compileShader(GL.shaders[shader])};var _emscripten_glCompressedTexImage2D=(target,level,internalFormat,width,height,border,imageSize,data)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,HEAPU8,data,imageSize);return}GLctx.compressedTexImage2D(target,level,internalFormat,width,height,border,HEAPU8.subarray(data,data+imageSize))};var _emscripten_glCompressedTexImage3D=(target,level,internalFormat,width,height,depth,border,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,imageSize,data)}else{GLctx.compressedTexImage3D(target,level,internalFormat,width,height,depth,border,HEAPU8,data,imageSize)}};var _emscripten_glCompressedTexSubImage2D=(target,level,xoffset,yoffset,width,height,format,imageSize,data)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding||!imageSize){GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data);return}GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize);return}GLctx.compressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,HEAPU8.subarray(data,data+imageSize))};var _emscripten_glCompressedTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx.compressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}};var _emscripten_glCopyBufferSubData=(x0,x1,x2,x3,x4)=>GLctx.copyBufferSubData(x0,x1,x2,x3,x4);var _emscripten_glCopyTexImage2D=(x0,x1,x2,x3,x4,x5,x6,x7)=>GLctx.copyTexImage2D(x0,x1,x2,x3,x4,x5,x6,x7);var _emscripten_glCopyTexSubImage2D=(x0,x1,x2,x3,x4,x5,x6,x7)=>GLctx.copyTexSubImage2D(x0,x1,x2,x3,x4,x5,x6,x7);var _emscripten_glCopyTexSubImage3D=(x0,x1,x2,x3,x4,x5,x6,x7,x8)=>GLctx.copyTexSubImage3D(x0,x1,x2,x3,x4,x5,x6,x7,x8);var _emscripten_glCreateProgram=()=>{var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;program.maxUniformLength=program.maxAttributeLength=program.maxUniformBlockNameLength=0;program.uniformIdCounter=1;GL.programs[id]=program;return id};var _emscripten_glCreateShader=shaderType=>{var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id};var _emscripten_glCullFace=x0=>GLctx.cullFace(x0);var _emscripten_glDeleteBuffers=(n,buffers)=>{for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}};var _emscripten_glDeleteFramebuffers=(n,framebuffers)=>{for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}};var _emscripten_glDeleteProgram=id=>{if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null};var _emscripten_glDeleteQueries=(n,ids)=>{for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx.deleteQuery(query);GL.queries[id]=null}};var _emscripten_glDeleteQueriesEXT=(n,ids)=>{for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx.disjointTimerQueryExt["deleteQueryEXT"](query);GL.queries[id]=null}};var _emscripten_glDeleteRenderbuffers=(n,renderbuffers)=>{for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}};var _emscripten_glDeleteSamplers=(n,samplers)=>{for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx.deleteSampler(sampler);sampler.name=0;GL.samplers[id]=null}};var _emscripten_glDeleteShader=id=>{if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null};var _emscripten_glDeleteSync=id=>{if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null};var _emscripten_glDeleteTextures=(n,textures)=>{for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}};var _emscripten_glDeleteTransformFeedbacks=(n,ids)=>{for(var i=0;i>2];var transformFeedback=GL.transformFeedbacks[id];if(!transformFeedback)continue;GLctx.deleteTransformFeedback(transformFeedback);transformFeedback.name=0;GL.transformFeedbacks[id]=null}};var _emscripten_glDeleteVertexArrays=(n,vaos)=>{for(var i=0;i>2];GLctx.deleteVertexArray(GL.vaos[id]);GL.vaos[id]=null}};var _glDeleteVertexArrays=_emscripten_glDeleteVertexArrays;var _emscripten_glDeleteVertexArraysOES=_glDeleteVertexArrays;var _emscripten_glDepthFunc=x0=>GLctx.depthFunc(x0);var _emscripten_glDepthMask=flag=>{GLctx.depthMask(!!flag)};var _emscripten_glDepthRangef=(x0,x1)=>GLctx.depthRange(x0,x1);var _emscripten_glDetachShader=(program,shader)=>{GLctx.detachShader(GL.programs[program],GL.shaders[shader])};var _emscripten_glDisable=x0=>GLctx.disable(x0);var _emscripten_glDisableVertexAttribArray=index=>{GLctx.disableVertexAttribArray(index)};var _emscripten_glDrawArrays=(mode,first,count)=>{GLctx.drawArrays(mode,first,count)};var _emscripten_glDrawArraysInstanced=(mode,first,count,primcount)=>{GLctx.drawArraysInstanced(mode,first,count,primcount)};var _glDrawArraysInstanced=_emscripten_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedANGLE=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedARB=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedBaseInstanceWEBGL=(mode,first,count,instanceCount,baseInstance)=>{GLctx.dibvbi["drawArraysInstancedBaseInstanceWEBGL"](mode,first,count,instanceCount,baseInstance)};var _emscripten_glDrawArraysInstancedEXT=_glDrawArraysInstanced;var _emscripten_glDrawArraysInstancedNV=_glDrawArraysInstanced;var tempFixedLengthArray=[];var _emscripten_glDrawBuffers=(n,bufs)=>{var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx.drawBuffers(bufArray)};var _glDrawBuffers=_emscripten_glDrawBuffers;var _emscripten_glDrawBuffersEXT=_glDrawBuffers;var _emscripten_glDrawBuffersWEBGL=_glDrawBuffers;var _emscripten_glDrawElements=(mode,count,type,indices)=>{GLctx.drawElements(mode,count,type,indices)};var _emscripten_glDrawElementsInstanced=(mode,count,type,indices,primcount)=>{GLctx.drawElementsInstanced(mode,count,type,indices,primcount)};var _glDrawElementsInstanced=_emscripten_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedANGLE=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedARB=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL=(mode,count,type,offset,instanceCount,baseVertex,baseinstance)=>{GLctx.dibvbi["drawElementsInstancedBaseVertexBaseInstanceWEBGL"](mode,count,type,offset,instanceCount,baseVertex,baseinstance)};var _emscripten_glDrawElementsInstancedEXT=_glDrawElementsInstanced;var _emscripten_glDrawElementsInstancedNV=_glDrawElementsInstanced;var _glDrawElements=_emscripten_glDrawElements;var _emscripten_glDrawRangeElements=(mode,start,end,count,type,indices)=>{_glDrawElements(mode,count,type,indices)};var _emscripten_glEnable=x0=>GLctx.enable(x0);var _emscripten_glEnableVertexAttribArray=index=>{GLctx.enableVertexAttribArray(index)};var _emscripten_glEndQuery=x0=>GLctx.endQuery(x0);var _emscripten_glEndQueryEXT=target=>{GLctx.disjointTimerQueryExt["endQueryEXT"](target)};var _emscripten_glEndTransformFeedback=()=>GLctx.endTransformFeedback();var _emscripten_glFenceSync=(condition,flags)=>{var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}return 0};var _emscripten_glFinish=()=>GLctx.finish();var _emscripten_glFlush=()=>GLctx.flush();var _emscripten_glFramebufferRenderbuffer=(target,attachment,renderbuffertarget,renderbuffer)=>{GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])};var _emscripten_glFramebufferTexture2D=(target,attachment,textarget,texture,level)=>{GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)};var _emscripten_glFramebufferTextureLayer=(target,attachment,texture,level,layer)=>{GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)};var _emscripten_glFrontFace=x0=>GLctx.frontFace(x0);var _emscripten_glGenBuffers=(n,buffers)=>{GL.genObject(n,buffers,"createBuffer",GL.buffers)};var _emscripten_glGenFramebuffers=(n,ids)=>{GL.genObject(n,ids,"createFramebuffer",GL.framebuffers)};var _emscripten_glGenQueries=(n,ids)=>{GL.genObject(n,ids,"createQuery",GL.queries)};var _emscripten_glGenQueriesEXT=(n,ids)=>{for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.queries);query.name=id;GL.queries[id]=query;HEAP32[ids+i*4>>2]=id}};var _emscripten_glGenRenderbuffers=(n,renderbuffers)=>{GL.genObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)};var _emscripten_glGenSamplers=(n,samplers)=>{GL.genObject(n,samplers,"createSampler",GL.samplers)};var _emscripten_glGenTextures=(n,textures)=>{GL.genObject(n,textures,"createTexture",GL.textures)};var _emscripten_glGenTransformFeedbacks=(n,ids)=>{GL.genObject(n,ids,"createTransformFeedback",GL.transformFeedbacks)};var _emscripten_glGenVertexArrays=(n,arrays)=>{GL.genObject(n,arrays,"createVertexArray",GL.vaos)};var _glGenVertexArrays=_emscripten_glGenVertexArrays;var _emscripten_glGenVertexArraysOES=_glGenVertexArrays;var _emscripten_glGenerateMipmap=x0=>GLctx.generateMipmap(x0);var __glGetActiveAttribOrUniform=(funcName,program,index,bufSize,length,size,type,name)=>{program=GL.programs[program];var info=GLctx[funcName](program,index);if(info){var numBytesWrittenExclNull=name&&stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull;if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}};var _emscripten_glGetActiveAttrib=(program,index,bufSize,length,size,type,name)=>__glGetActiveAttribOrUniform("getActiveAttrib",program,index,bufSize,length,size,type,name);var _emscripten_glGetActiveUniform=(program,index,bufSize,length,size,type,name)=>__glGetActiveAttribOrUniform("getActiveUniform",program,index,bufSize,length,size,type,name);var _emscripten_glGetActiveUniformBlockName=(program,uniformBlockIndex,bufSize,length,uniformBlockName)=>{program=GL.programs[program];var result=GLctx.getActiveUniformBlockName(program,uniformBlockIndex);if(!result)return;if(uniformBlockName&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(result,uniformBlockName,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}};var _emscripten_glGetActiveUniformBlockiv=(program,uniformBlockIndex,pname,params)=>{if(!params){GL.recordError(1281);return}program=GL.programs[program];if(pname==35393){var name=GLctx.getActiveUniformBlockName(program,uniformBlockIndex);HEAP32[params>>2]=name.length+1;return}var result=GLctx.getActiveUniformBlockParameter(program,uniformBlockIndex,pname);if(result===null)return;if(pname==35395){for(var i=0;i>2]=result[i]}}else{HEAP32[params>>2]=result}};var _emscripten_glGetActiveUniformsiv=(program,uniformCount,uniformIndices,pname,params)=>{if(!params){GL.recordError(1281);return}if(uniformCount>0&&uniformIndices==0){GL.recordError(1281);return}program=GL.programs[program];var ids=[];for(var i=0;i>2])}var result=GLctx.getActiveUniforms(program,ids,pname);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}};var _emscripten_glGetAttachedShaders=(program,maxCount,count,shaders)=>{var result=GLctx.getAttachedShaders(GL.programs[program]);var len=result.length;if(len>maxCount){len=maxCount}HEAP32[count>>2]=len;for(var i=0;i>2]=id}};var _emscripten_glGetAttribLocation=(program,name)=>GLctx.getAttribLocation(GL.programs[program],UTF8ToString(name));var writeI53ToI64=(ptr,num)=>{HEAPU32[ptr>>2]=num;var lower=HEAPU32[ptr>>2];HEAPU32[ptr+4>>2]=(num-lower)/4294967296};var webglGetExtensions=()=>{var exts=getEmscriptenSupportedExtensions(GLctx);exts=exts.concat(exts.map(e=>"GL_"+e));return exts};var emscriptenWebGLGet=(name_,p,type)=>{if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}ret=webglGetExtensions().length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Unknown object returned from WebGL getParameter(${name_})! (error: ${e})`);return}}break;default:GL.recordError(1280);err(`GL_INVALID_ENUM in glGet${type}v: Native code calling glGet${type}v(${name_}) and it returns ${result} of type ${typeof result}!`);return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p]=ret?1:0;break}};var _emscripten_glGetBooleanv=(name_,p)=>emscriptenWebGLGet(name_,p,4);var _emscripten_glGetBufferParameteri64v=(target,value,data)=>{if(!data){GL.recordError(1281);return}writeI53ToI64(data,GLctx.getBufferParameter(target,value))};var _emscripten_glGetBufferParameteriv=(target,value,data)=>{if(!data){GL.recordError(1281);return}HEAP32[data>>2]=GLctx.getBufferParameter(target,value)};var _emscripten_glGetError=()=>{var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error};var _emscripten_glGetFloatv=(name_,p)=>emscriptenWebGLGet(name_,p,2);var _emscripten_glGetFragDataLocation=(program,name)=>GLctx.getFragDataLocation(GL.programs[program],UTF8ToString(name));var _emscripten_glGetFramebufferAttachmentParameteriv=(target,attachment,pname,params)=>{var result=GLctx.getFramebufferAttachmentParameter(target,attachment,pname);if(result instanceof WebGLRenderbuffer||result instanceof WebGLTexture){result=result.name|0}HEAP32[params>>2]=result};var emscriptenWebGLGetIndexed=(target,index,data,type)=>{if(!data){GL.recordError(1281);return}var result=GLctx.getIndexedParameter(target,index);var ret;switch(typeof result){case"boolean":ret=result?1:0;break;case"number":ret=result;break;case"object":if(result===null){switch(target){case 35983:case 35368:ret=0;break;default:{GL.recordError(1280);return}}}else if(result instanceof WebGLBuffer){ret=result.name|0}else{GL.recordError(1280);return}break;default:GL.recordError(1280);return}switch(type){case 1:writeI53ToI64(data,ret);break;case 0:HEAP32[data>>2]=ret;break;case 2:HEAPF32[data>>2]=ret;break;case 4:HEAP8[data]=ret?1:0;break;default:abort("internal emscriptenWebGLGetIndexed() error, bad type: "+type)}};var _emscripten_glGetInteger64i_v=(target,index,data)=>emscriptenWebGLGetIndexed(target,index,data,1);var _emscripten_glGetInteger64v=(name_,p)=>{emscriptenWebGLGet(name_,p,1)};var _emscripten_glGetIntegeri_v=(target,index,data)=>emscriptenWebGLGetIndexed(target,index,data,0);var _emscripten_glGetIntegerv=(name_,p)=>emscriptenWebGLGet(name_,p,0);var _emscripten_glGetInternalformativ=(target,internalformat,pname,bufSize,params)=>{if(bufSize<0){GL.recordError(1281);return}if(!params){GL.recordError(1281);return}var ret=GLctx.getInternalformatParameter(target,internalformat,pname);if(ret===null)return;for(var i=0;i>2]=ret[i]}};var _emscripten_glGetProgramBinary=(program,bufSize,length,binaryFormat,binary)=>{GL.recordError(1282)};var _emscripten_glGetProgramInfoLog=(program,maxLength,length,infoLog)=>{var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetProgramiv=(program,pname,p)=>{if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}program=GL.programs[program];if(pname==35716){var log=GLctx.getProgramInfoLog(program);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){if(!program.maxUniformLength){var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(var i=0;i>2]=program.maxUniformLength}else if(pname==35722){if(!program.maxAttributeLength){var numActiveAttributes=GLctx.getProgramParameter(program,35721);for(var i=0;i>2]=program.maxAttributeLength}else if(pname==35381){if(!program.maxUniformBlockNameLength){var numActiveUniformBlocks=GLctx.getProgramParameter(program,35382);for(var i=0;i>2]=program.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(program,pname)}};var _emscripten_glGetQueryObjecti64vEXT=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param;if(GL.currentContext.version<2){param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname)}else{param=GLctx.getQueryParameter(query,pname)}var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)};var _emscripten_glGetQueryObjectivEXT=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret};var _glGetQueryObjecti64vEXT=_emscripten_glGetQueryObjecti64vEXT;var _emscripten_glGetQueryObjectui64vEXT=_glGetQueryObjecti64vEXT;var _emscripten_glGetQueryObjectuiv=(id,pname,params)=>{if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx.getQueryParameter(query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret};var _glGetQueryObjectivEXT=_emscripten_glGetQueryObjectivEXT;var _emscripten_glGetQueryObjectuivEXT=_glGetQueryObjectivEXT;var _emscripten_glGetQueryiv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getQuery(target,pname)};var _emscripten_glGetQueryivEXT=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.disjointTimerQueryExt["getQueryEXT"](target,pname)};var _emscripten_glGetRenderbufferParameteriv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getRenderbufferParameter(target,pname)};var _emscripten_glGetSamplerParameterfv=(sampler,pname,params)=>{if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getSamplerParameter(GL.samplers[sampler],pname)};var _emscripten_glGetSamplerParameteriv=(sampler,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getSamplerParameter(GL.samplers[sampler],pname)};var _emscripten_glGetShaderInfoLog=(shader,maxLength,length,infoLog)=>{var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetShaderPrecisionFormat=(shaderType,precisionType,range,precision)=>{var result=GLctx.getShaderPrecisionFormat(shaderType,precisionType);HEAP32[range>>2]=result.rangeMin;HEAP32[range+4>>2]=result.rangeMax;HEAP32[precision>>2]=result.precision};var _emscripten_glGetShaderSource=(shader,bufSize,length,source)=>{var result=GLctx.getShaderSource(GL.shaders[shader]);if(!result)return;var numBytesWrittenExclNull=bufSize>0&&source?stringToUTF8(result,source,bufSize):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull};var _emscripten_glGetShaderiv=(shader,pname,p)=>{if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}};var stringToNewUTF8=str=>{var size=lengthBytesUTF8(str)+1;var ret=_malloc(size);if(ret)stringToUTF8(str,ret,size);return ret};var _emscripten_glGetString=name_=>{var ret=GL.stringCache[name_];if(!ret){switch(name_){case 7939:ret=stringToNewUTF8(webglGetExtensions().join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=s?stringToNewUTF8(s):0;break;case 7938:var webGLVersion=GLctx.getParameter(7938);var glVersion=`OpenGL ES 2.0 (${webGLVersion})`;if(GL.currentContext.version>=2)glVersion=`OpenGL ES 3.0 (${webGLVersion})`;ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion=`OpenGL ES GLSL ES ${ver_num[1]} (${glslVersion})`}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280)}GL.stringCache[name_]=ret}return ret};var _emscripten_glGetStringi=(name,index)=>{if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=webglGetExtensions().map(stringToNewUTF8);stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}};var _emscripten_glGetSynciv=(sync,pname,bufSize,length,values)=>{if(bufSize<0){GL.recordError(1281);return}if(!values){GL.recordError(1281);return}var ret=GLctx.getSyncParameter(GL.syncs[sync],pname);if(ret!==null){HEAP32[values>>2]=ret;if(length)HEAP32[length>>2]=1}};var _emscripten_glGetTexParameterfv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getTexParameter(target,pname)};var _emscripten_glGetTexParameteriv=(target,pname,params)=>{if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getTexParameter(target,pname)};var _emscripten_glGetTransformFeedbackVarying=(program,index,bufSize,length,size,type,name)=>{program=GL.programs[program];var info=GLctx.getTransformFeedbackVarying(program,index);if(!info)return;if(name&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type};var _emscripten_glGetUniformBlockIndex=(program,uniformBlockName)=>GLctx.getUniformBlockIndex(GL.programs[program],UTF8ToString(uniformBlockName));var _emscripten_glGetUniformIndices=(program,uniformCount,uniformNames,uniformIndices)=>{if(!uniformIndices){GL.recordError(1281);return}if(uniformCount>0&&(uniformNames==0||uniformIndices==0)){GL.recordError(1281);return}program=GL.programs[program];var names=[];for(var i=0;i>2]));var result=GLctx.getUniformIndices(program,names);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}};var jstoi_q=str=>parseInt(str);var webglGetLeftBracePos=name=>name.slice(-1)=="]"&&name.lastIndexOf("[");var webglPrepareUniformLocationsBeforeFirstUse=program=>{var uniformLocsById=program.uniformLocsById,uniformSizeAndIdsByName=program.uniformSizeAndIdsByName,i,j;if(!uniformLocsById){program.uniformLocsById=uniformLocsById={};program.uniformArrayNamesById={};var numActiveUniforms=GLctx.getProgramParameter(program,35718);for(i=0;i0?nm.slice(0,lb):nm;var id=program.uniformIdCounter;program.uniformIdCounter+=sz;uniformSizeAndIdsByName[arrayName]=[sz,id];for(j=0;j{name=UTF8ToString(name);if(program=GL.programs[program]){webglPrepareUniformLocationsBeforeFirstUse(program);var uniformLocsById=program.uniformLocsById;var arrayIndex=0;var uniformBaseName=name;var leftBrace=webglGetLeftBracePos(name);if(leftBrace>0){arrayIndex=jstoi_q(name.slice(leftBrace+1))>>>0;uniformBaseName=name.slice(0,leftBrace)}var sizeAndId=program.uniformSizeAndIdsByName[uniformBaseName];if(sizeAndId&&arrayIndex{var p=GLctx.currentProgram;if(p){var webglLoc=p.uniformLocsById[location];if(typeof webglLoc=="number"){p.uniformLocsById[location]=webglLoc=GLctx.getUniformLocation(p,p.uniformArrayNamesById[location]+(webglLoc>0?`[${webglLoc}]`:""))}return webglLoc}else{GL.recordError(1282)}};var emscriptenWebGLGetUniform=(program,location,params,type)=>{if(!params){GL.recordError(1281);return}program=GL.programs[program];webglPrepareUniformLocationsBeforeFirstUse(program);var data=GLctx.getUniform(program,webglGetUniformLocation(location));if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break}}}};var _emscripten_glGetUniformfv=(program,location,params)=>{emscriptenWebGLGetUniform(program,location,params,2)};var _emscripten_glGetUniformiv=(program,location,params)=>{emscriptenWebGLGetUniform(program,location,params,0)};var _emscripten_glGetUniformuiv=(program,location,params)=>emscriptenWebGLGetUniform(program,location,params,0);var emscriptenWebGLGetVertexAttrib=(index,pname,params,type)=>{if(!params){GL.recordError(1281);return}var data=GLctx.getVertexAttrib(index,pname);if(pname==34975){HEAP32[params>>2]=data&&data["name"]}else if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break;case 5:HEAP32[params>>2]=Math.fround(data);break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break;case 5:HEAP32[params+i*4>>2]=Math.fround(data[i]);break}}}};var _emscripten_glGetVertexAttribIiv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,0)};var _glGetVertexAttribIiv=_emscripten_glGetVertexAttribIiv;var _emscripten_glGetVertexAttribIuiv=_glGetVertexAttribIiv;var _emscripten_glGetVertexAttribPointerv=(index,pname,pointer)=>{if(!pointer){GL.recordError(1281);return}HEAP32[pointer>>2]=GLctx.getVertexAttribOffset(index,pname)};var _emscripten_glGetVertexAttribfv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,2)};var _emscripten_glGetVertexAttribiv=(index,pname,params)=>{emscriptenWebGLGetVertexAttrib(index,pname,params,5)};var _emscripten_glHint=(x0,x1)=>GLctx.hint(x0,x1);var _emscripten_glInvalidateFramebuffer=(target,numAttachments,attachments)=>{var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx.invalidateFramebuffer(target,list)};var _emscripten_glInvalidateSubFramebuffer=(target,numAttachments,attachments,x,y,width,height)=>{var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx.invalidateSubFramebuffer(target,list,x,y,width,height)};var _emscripten_glIsBuffer=buffer=>{var b=GL.buffers[buffer];if(!b)return 0;return GLctx.isBuffer(b)};var _emscripten_glIsEnabled=x0=>GLctx.isEnabled(x0);var _emscripten_glIsFramebuffer=framebuffer=>{var fb=GL.framebuffers[framebuffer];if(!fb)return 0;return GLctx.isFramebuffer(fb)};var _emscripten_glIsProgram=program=>{program=GL.programs[program];if(!program)return 0;return GLctx.isProgram(program)};var _emscripten_glIsQuery=id=>{var query=GL.queries[id];if(!query)return 0;return GLctx.isQuery(query)};var _emscripten_glIsQueryEXT=id=>{var query=GL.queries[id];if(!query)return 0;return GLctx.disjointTimerQueryExt["isQueryEXT"](query)};var _emscripten_glIsRenderbuffer=renderbuffer=>{var rb=GL.renderbuffers[renderbuffer];if(!rb)return 0;return GLctx.isRenderbuffer(rb)};var _emscripten_glIsSampler=id=>{var sampler=GL.samplers[id];if(!sampler)return 0;return GLctx.isSampler(sampler)};var _emscripten_glIsShader=shader=>{var s=GL.shaders[shader];if(!s)return 0;return GLctx.isShader(s)};var _emscripten_glIsSync=sync=>GLctx.isSync(GL.syncs[sync]);var _emscripten_glIsTexture=id=>{var texture=GL.textures[id];if(!texture)return 0;return GLctx.isTexture(texture)};var _emscripten_glIsTransformFeedback=id=>GLctx.isTransformFeedback(GL.transformFeedbacks[id]);var _emscripten_glIsVertexArray=array=>{var vao=GL.vaos[array];if(!vao)return 0;return GLctx.isVertexArray(vao)};var _glIsVertexArray=_emscripten_glIsVertexArray;var _emscripten_glIsVertexArrayOES=_glIsVertexArray;var _emscripten_glLineWidth=x0=>GLctx.lineWidth(x0);var _emscripten_glLinkProgram=program=>{program=GL.programs[program];GLctx.linkProgram(program);program.uniformLocsById=0;program.uniformSizeAndIdsByName={}};var _emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL=(mode,firsts,counts,instanceCounts,baseInstances,drawCount)=>{GLctx.mdibvbi["multiDrawArraysInstancedBaseInstanceWEBGL"](mode,HEAP32,firsts>>2,HEAP32,counts>>2,HEAP32,instanceCounts>>2,HEAPU32,baseInstances>>2,drawCount)};var _emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL=(mode,counts,type,offsets,instanceCounts,baseVertices,baseInstances,drawCount)=>{GLctx.mdibvbi["multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL"](mode,HEAP32,counts>>2,type,HEAP32,offsets>>2,HEAP32,instanceCounts>>2,HEAP32,baseVertices>>2,HEAPU32,baseInstances>>2,drawCount)};var _emscripten_glPauseTransformFeedback=()=>GLctx.pauseTransformFeedback();var _emscripten_glPixelStorei=(pname,param)=>{if(pname==3317){GL.unpackAlignment=param}else if(pname==3314){GL.unpackRowLength=param}GLctx.pixelStorei(pname,param)};var _emscripten_glPolygonModeWEBGL=(face,mode)=>{GLctx.webglPolygonMode["polygonModeWEBGL"](face,mode)};var _emscripten_glPolygonOffset=(x0,x1)=>GLctx.polygonOffset(x0,x1);var _emscripten_glPolygonOffsetClampEXT=(factor,units,clamp)=>{GLctx.extPolygonOffsetClamp["polygonOffsetClampEXT"](factor,units,clamp)};var _emscripten_glProgramBinary=(program,binaryFormat,binary,length)=>{GL.recordError(1280)};var _emscripten_glProgramParameteri=(program,pname,value)=>{GL.recordError(1280)};var _emscripten_glQueryCounterEXT=(id,target)=>{GLctx.disjointTimerQueryExt["queryCounterEXT"](GL.queries[id],target)};var _emscripten_glReadBuffer=x0=>GLctx.readBuffer(x0);var computeUnpackAlignedImageSize=(width,height,sizePerPixel)=>{function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=(GL.unpackRowLength||width)*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,GL.unpackAlignment);return height*alignedRowSize};var colorChannelsInGlTextureFormat=format=>{var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1};var heapObjectForWebGLType=type=>{type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16};var toTypedArrayIndex=(pointer,heap)=>pointer>>>31-Math.clz32(heap.BYTES_PER_ELEMENT);var emscriptenWebGLGetTexPixelData=(type,format,width,height,pixels,internalFormat)=>{var heap=heapObjectForWebGLType(type);var sizePerPixel=colorChannelsInGlTextureFormat(format)*heap.BYTES_PER_ELEMENT;var bytes=computeUnpackAlignedImageSize(width,height,sizePerPixel);return heap.subarray(toTypedArrayIndex(pixels,heap),toTypedArrayIndex(pixels+bytes,heap))};var _emscripten_glReadPixels=(x,y,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels);return}var heap=heapObjectForWebGLType(type);var target=toTypedArrayIndex(pixels,heap);GLctx.readPixels(x,y,width,height,format,type,heap,target);return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)};var _emscripten_glReleaseShaderCompiler=()=>{};var _emscripten_glRenderbufferStorage=(x0,x1,x2,x3)=>GLctx.renderbufferStorage(x0,x1,x2,x3);var _emscripten_glRenderbufferStorageMultisample=(x0,x1,x2,x3,x4)=>GLctx.renderbufferStorageMultisample(x0,x1,x2,x3,x4);var _emscripten_glResumeTransformFeedback=()=>GLctx.resumeTransformFeedback();var _emscripten_glSampleCoverage=(value,invert)=>{GLctx.sampleCoverage(value,!!invert)};var _emscripten_glSamplerParameterf=(sampler,pname,param)=>{GLctx.samplerParameterf(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameterfv=(sampler,pname,params)=>{var param=HEAPF32[params>>2];GLctx.samplerParameterf(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameteri=(sampler,pname,param)=>{GLctx.samplerParameteri(GL.samplers[sampler],pname,param)};var _emscripten_glSamplerParameteriv=(sampler,pname,params)=>{var param=HEAP32[params>>2];GLctx.samplerParameteri(GL.samplers[sampler],pname,param)};var _emscripten_glScissor=(x0,x1,x2,x3)=>GLctx.scissor(x0,x1,x2,x3);var _emscripten_glShaderBinary=(count,shaders,binaryformat,binary,length)=>{GL.recordError(1280)};var _emscripten_glShaderSource=(shader,count,string,length)=>{var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)};var _emscripten_glStencilFunc=(x0,x1,x2)=>GLctx.stencilFunc(x0,x1,x2);var _emscripten_glStencilFuncSeparate=(x0,x1,x2,x3)=>GLctx.stencilFuncSeparate(x0,x1,x2,x3);var _emscripten_glStencilMask=x0=>GLctx.stencilMask(x0);var _emscripten_glStencilMaskSeparate=(x0,x1)=>GLctx.stencilMaskSeparate(x0,x1);var _emscripten_glStencilOp=(x0,x1,x2)=>GLctx.stencilOp(x0,x1,x2);var _emscripten_glStencilOpSeparate=(x0,x1,x2,x3)=>GLctx.stencilOpSeparate(x0,x1,x2,x3);var _emscripten_glTexImage2D=(target,level,internalFormat,width,height,border,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);var index=toTypedArrayIndex(pixels,heap);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,index);return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null;GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixelData)};var _emscripten_glTexImage3D=(target,level,internalFormat,width,height,depth,border,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texImage3D(target,level,internalFormat,width,height,depth,border,format,type,null)}};var _emscripten_glTexParameterf=(x0,x1,x2)=>GLctx.texParameterf(x0,x1,x2);var _emscripten_glTexParameterfv=(target,pname,params)=>{var param=HEAPF32[params>>2];GLctx.texParameterf(target,pname,param)};var _emscripten_glTexParameteri=(x0,x1,x2)=>GLctx.texParameteri(x0,x1,x2);var _emscripten_glTexParameteriv=(target,pname,params)=>{var param=HEAP32[params>>2];GLctx.texParameteri(target,pname,param)};var _emscripten_glTexStorage2D=(x0,x1,x2,x3,x4)=>GLctx.texStorage2D(x0,x1,x2,x3,x4);var _emscripten_glTexStorage3D=(x0,x1,x2,x3,x4,x5)=>GLctx.texStorage3D(x0,x1,x2,x3,x4,x5);var _emscripten_glTexSubImage2D=(target,level,xoffset,yoffset,width,height,format,type,pixels)=>{if(GL.currentContext.version>=2){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels);return}if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,toTypedArrayIndex(pixels,heap));return}}var pixelData=pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0):null;GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)};var _emscripten_glTexSubImage3D=(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)=>{if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,toTypedArrayIndex(pixels,heap))}else{GLctx.texSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}};var _emscripten_glTransformFeedbackVaryings=(program,count,varyings,bufferMode)=>{program=GL.programs[program];var vars=[];for(var i=0;i>2]));GLctx.transformFeedbackVaryings(program,vars,bufferMode)};var _emscripten_glUniform1f=(location,v0)=>{GLctx.uniform1f(webglGetUniformLocation(location),v0)};var miniTempWebGLFloatBuffers=[];var _emscripten_glUniform1fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1fv(webglGetUniformLocation(location),HEAPF32,value>>2,count);return}if(count<=288){var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform1i=(location,v0)=>{GLctx.uniform1i(webglGetUniformLocation(location),v0)};var miniTempWebGLIntBuffers=[];var _emscripten_glUniform1iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform1iv(webglGetUniformLocation(location),HEAP32,value>>2,count);return}if(count<=288){var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2]}}else{var view=HEAP32.subarray(value>>2,value+count*4>>2)}GLctx.uniform1iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform1ui=(location,v0)=>{GLctx.uniform1ui(webglGetUniformLocation(location),v0)};var _emscripten_glUniform1uiv=(location,count,value)=>{count&&GLctx.uniform1uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count)};var _emscripten_glUniform2f=(location,v0,v1)=>{GLctx.uniform2f(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*2);return}if(count<=144){count*=2;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform2i=(location,v0,v1)=>{GLctx.uniform2i(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform2iv(webglGetUniformLocation(location),HEAP32,value>>2,count*2);return}if(count<=144){count*=2;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*8>>2)}GLctx.uniform2iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform2ui=(location,v0,v1)=>{GLctx.uniform2ui(webglGetUniformLocation(location),v0,v1)};var _emscripten_glUniform2uiv=(location,count,value)=>{count&&GLctx.uniform2uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*2)};var _emscripten_glUniform3f=(location,v0,v1,v2)=>{GLctx.uniform3f(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform3fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*3);return}if(count<=96){count*=3;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*12>>2)}GLctx.uniform3fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform3i=(location,v0,v1,v2)=>{GLctx.uniform3i(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform3iv(webglGetUniformLocation(location),HEAP32,value>>2,count*3);return}if(count<=96){count*=3;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2];view[i+2]=HEAP32[value+(4*i+8)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*12>>2)}GLctx.uniform3iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform3ui=(location,v0,v1,v2)=>{GLctx.uniform3ui(webglGetUniformLocation(location),v0,v1,v2)};var _emscripten_glUniform3uiv=(location,count,value)=>{count&&GLctx.uniform3uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*3)};var _emscripten_glUniform4f=(location,v0,v1,v2,v3)=>{GLctx.uniform4f(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4fv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform4fv(webglGetUniformLocation(location),HEAPF32,value>>2,count*4);return}if(count<=72){var view=miniTempWebGLFloatBuffers[4*count];var heap=HEAPF32;value=value>>2;count*=4;for(var i=0;i>2,value+count*16>>2)}GLctx.uniform4fv(webglGetUniformLocation(location),view)};var _emscripten_glUniform4i=(location,v0,v1,v2,v3)=>{GLctx.uniform4i(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4iv=(location,count,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniform4iv(webglGetUniformLocation(location),HEAP32,value>>2,count*4);return}if(count<=72){count*=4;var view=miniTempWebGLIntBuffers[count];for(var i=0;i>2];view[i+1]=HEAP32[value+(4*i+4)>>2];view[i+2]=HEAP32[value+(4*i+8)>>2];view[i+3]=HEAP32[value+(4*i+12)>>2]}}else{var view=HEAP32.subarray(value>>2,value+count*16>>2)}GLctx.uniform4iv(webglGetUniformLocation(location),view)};var _emscripten_glUniform4ui=(location,v0,v1,v2,v3)=>{GLctx.uniform4ui(webglGetUniformLocation(location),v0,v1,v2,v3)};var _emscripten_glUniform4uiv=(location,count,value)=>{count&&GLctx.uniform4uiv(webglGetUniformLocation(location),HEAPU32,value>>2,count*4)};var _emscripten_glUniformBlockBinding=(program,uniformBlockIndex,uniformBlockBinding)=>{program=GL.programs[program];GLctx.uniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding)};var _emscripten_glUniformMatrix2fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*4);return}if(count<=72){count*=4;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*16>>2)}GLctx.uniformMatrix2fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix2x3fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix2x3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*6)};var _emscripten_glUniformMatrix2x4fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix2x4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*8)};var _emscripten_glUniformMatrix3fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*9);return}if(count<=32){count*=9;var view=miniTempWebGLFloatBuffers[count];for(var i=0;i>2];view[i+1]=HEAPF32[value+(4*i+4)>>2];view[i+2]=HEAPF32[value+(4*i+8)>>2];view[i+3]=HEAPF32[value+(4*i+12)>>2];view[i+4]=HEAPF32[value+(4*i+16)>>2];view[i+5]=HEAPF32[value+(4*i+20)>>2];view[i+6]=HEAPF32[value+(4*i+24)>>2];view[i+7]=HEAPF32[value+(4*i+28)>>2];view[i+8]=HEAPF32[value+(4*i+32)>>2]}}else{var view=HEAPF32.subarray(value>>2,value+count*36>>2)}GLctx.uniformMatrix3fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix3x2fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix3x2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*6)};var _emscripten_glUniformMatrix3x4fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix3x4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*12)};var _emscripten_glUniformMatrix4fv=(location,count,transpose,value)=>{if(GL.currentContext.version>=2){count&&GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*16);return}if(count<=18){var view=miniTempWebGLFloatBuffers[16*count];var heap=HEAPF32;value=value>>2;count*=16;for(var i=0;i>2,value+count*64>>2)}GLctx.uniformMatrix4fv(webglGetUniformLocation(location),!!transpose,view)};var _emscripten_glUniformMatrix4x2fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix4x2fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*8)};var _emscripten_glUniformMatrix4x3fv=(location,count,transpose,value)=>{count&&GLctx.uniformMatrix4x3fv(webglGetUniformLocation(location),!!transpose,HEAPF32,value>>2,count*12)};var _emscripten_glUseProgram=program=>{program=GL.programs[program];GLctx.useProgram(program);GLctx.currentProgram=program};var _emscripten_glValidateProgram=program=>{GLctx.validateProgram(GL.programs[program])};var _emscripten_glVertexAttrib1f=(x0,x1)=>GLctx.vertexAttrib1f(x0,x1);var _emscripten_glVertexAttrib1fv=(index,v)=>{GLctx.vertexAttrib1f(index,HEAPF32[v>>2])};var _emscripten_glVertexAttrib2f=(x0,x1,x2)=>GLctx.vertexAttrib2f(x0,x1,x2);var _emscripten_glVertexAttrib2fv=(index,v)=>{GLctx.vertexAttrib2f(index,HEAPF32[v>>2],HEAPF32[v+4>>2])};var _emscripten_glVertexAttrib3f=(x0,x1,x2,x3)=>GLctx.vertexAttrib3f(x0,x1,x2,x3);var _emscripten_glVertexAttrib3fv=(index,v)=>{GLctx.vertexAttrib3f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2])};var _emscripten_glVertexAttrib4f=(x0,x1,x2,x3,x4)=>GLctx.vertexAttrib4f(x0,x1,x2,x3,x4);var _emscripten_glVertexAttrib4fv=(index,v)=>{GLctx.vertexAttrib4f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2],HEAPF32[v+12>>2])};var _emscripten_glVertexAttribDivisor=(index,divisor)=>{GLctx.vertexAttribDivisor(index,divisor)};var _glVertexAttribDivisor=_emscripten_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorANGLE=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorARB=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorEXT=_glVertexAttribDivisor;var _emscripten_glVertexAttribDivisorNV=_glVertexAttribDivisor;var _emscripten_glVertexAttribI4i=(x0,x1,x2,x3,x4)=>GLctx.vertexAttribI4i(x0,x1,x2,x3,x4);var _emscripten_glVertexAttribI4iv=(index,v)=>{GLctx.vertexAttribI4i(index,HEAP32[v>>2],HEAP32[v+4>>2],HEAP32[v+8>>2],HEAP32[v+12>>2])};var _emscripten_glVertexAttribI4ui=(x0,x1,x2,x3,x4)=>GLctx.vertexAttribI4ui(x0,x1,x2,x3,x4);var _emscripten_glVertexAttribI4uiv=(index,v)=>{GLctx.vertexAttribI4ui(index,HEAPU32[v>>2],HEAPU32[v+4>>2],HEAPU32[v+8>>2],HEAPU32[v+12>>2])};var _emscripten_glVertexAttribIPointer=(index,size,type,stride,ptr)=>{GLctx.vertexAttribIPointer(index,size,type,stride,ptr)};var _emscripten_glVertexAttribPointer=(index,size,type,normalized,stride,ptr)=>{GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)};var _emscripten_glViewport=(x0,x1,x2,x3)=>GLctx.viewport(x0,x1,x2,x3);var _emscripten_glWaitSync=(sync,flags,timeout)=>{timeout=Number(timeout);GLctx.waitSync(GL.syncs[sync],flags,timeout)};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var _emscripten_request_animation_frame_loop=(cb,userData)=>{function tick(timeStamp){if(getWasmTableEntry(cb)(timeStamp,userData)){requestAnimationFrame(tick)}}return requestAnimationFrame(tick)};var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var _glGetIntegerv=_emscripten_glGetIntegerv;var _glGetString=_emscripten_glGetString;var _glGetStringi=_emscripten_glGetStringi;var _llvm_eh_typeid_for=type=>type;function _random_get(buffer,size){try{randomFill(HEAPU8.subarray(buffer,buffer+size));return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var handleException=e=>{if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();for(let i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));var miniTempWebGLFloatBuffersStorage=new Float32Array(288);for(var i=0;i<=288;++i){miniTempWebGLFloatBuffers[i]=miniTempWebGLFloatBuffersStorage.subarray(0,i)}var miniTempWebGLIntBuffersStorage=new Int32Array(288);for(var i=0;i<=288;++i){miniTempWebGLIntBuffers[i]=miniTempWebGLIntBuffersStorage.subarray(0,i)}{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;Module["GL"]=GL;var _malloc,_add_font,_add_image,_add_image_with_rid,_allocate,_apply_scene_transactions,_command,_deallocate,_destroy,_devtools_rendering_set_show_fps_meter,_devtools_rendering_set_show_hit_testing,_devtools_rendering_set_show_ruler,_devtools_rendering_set_show_stats,_devtools_rendering_set_show_tiles,_drain_missing_images,_export_node_as,_get_default_fallback_fonts,_get_image_bytes,_get_image_size,_get_node_absolute_bounding_box,_get_node_id_from_point,_get_node_id_path,_get_node_ids_from_envelope,_get_node_ids_from_point,_grida_fonts_analyze_family,_grida_fonts_free,_grida_fonts_parse_font,_grida_markdown_to_html,_grida_svg_optimize,_grida_svg_to_document,_has_missing_fonts,_highlight_strokes,_init,_init_with_backend,_list_available_fonts,_list_missing_fonts,_load_benchmark_scene,_load_dummy_scene,_load_scene_grida,_load_scene_grida1,_loaded_scene_ids,_pointer_move,_query_paint_groups,_redraw,_resize_surface,_resolve_image,_runtime_renderer_set_layer_compositing,_runtime_renderer_set_outline_mode,_runtime_renderer_set_pixel_preview_scale,_runtime_renderer_set_pixel_preview_stable,_runtime_renderer_set_render_policy_flags,_runtime_renderer_set_skip_layout,_set_debug,_set_default_fallback_fonts,_set_main_camera_transform,_set_surface_overlay_config,_set_verbose,_surface_get_cursor,_surface_get_hovered_node,_surface_get_selected_nodes,_surface_pointer_down,_surface_pointer_move,_surface_pointer_up,_surface_set_selection,_switch_scene,_text_edit_command,_text_edit_enter,_text_edit_exit,_text_edit_get_caret_rect,_text_edit_get_selected_html,_text_edit_get_selected_text,_text_edit_get_selection_rects,_text_edit_get_text,_text_edit_ime_cancel,_text_edit_ime_commit,_text_edit_ime_set_preedit,_text_edit_is_active,_text_edit_paste_html,_text_edit_paste_text,_text_edit_pointer_down,_text_edit_pointer_move,_text_edit_pointer_up,_text_edit_redo,_text_edit_set_color,_text_edit_set_font_family,_text_edit_set_font_size,_text_edit_tick,_text_edit_toggle_bold,_text_edit_toggle_italic,_text_edit_toggle_strikethrough,_text_edit_toggle_underline,_text_edit_undo,_tick,_to_vector_network,_toggle_debug,_main,_emscripten_builtin_memalign,_setThrew,__emscripten_tempret_set,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,___cxa_decrement_exception_refcount,___cxa_increment_exception_refcount,___cxa_can_catch,___cxa_get_exception_ptr,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_malloc=wasmExports["_g"];_add_font=Module["_add_font"]=wasmExports["ah"];_add_image=Module["_add_image"]=wasmExports["bh"];_add_image_with_rid=Module["_add_image_with_rid"]=wasmExports["ch"];_allocate=Module["_allocate"]=wasmExports["dh"];_apply_scene_transactions=Module["_apply_scene_transactions"]=wasmExports["eh"];_command=Module["_command"]=wasmExports["fh"];_deallocate=Module["_deallocate"]=wasmExports["gh"];_destroy=Module["_destroy"]=wasmExports["hh"];_devtools_rendering_set_show_fps_meter=Module["_devtools_rendering_set_show_fps_meter"]=wasmExports["ih"];_devtools_rendering_set_show_hit_testing=Module["_devtools_rendering_set_show_hit_testing"]=wasmExports["jh"];_devtools_rendering_set_show_ruler=Module["_devtools_rendering_set_show_ruler"]=wasmExports["kh"];_devtools_rendering_set_show_stats=Module["_devtools_rendering_set_show_stats"]=wasmExports["lh"];_devtools_rendering_set_show_tiles=Module["_devtools_rendering_set_show_tiles"]=wasmExports["mh"];_drain_missing_images=Module["_drain_missing_images"]=wasmExports["nh"];_export_node_as=Module["_export_node_as"]=wasmExports["oh"];_get_default_fallback_fonts=Module["_get_default_fallback_fonts"]=wasmExports["ph"];_get_image_bytes=Module["_get_image_bytes"]=wasmExports["qh"];_get_image_size=Module["_get_image_size"]=wasmExports["rh"];_get_node_absolute_bounding_box=Module["_get_node_absolute_bounding_box"]=wasmExports["sh"];_get_node_id_from_point=Module["_get_node_id_from_point"]=wasmExports["th"];_get_node_id_path=Module["_get_node_id_path"]=wasmExports["uh"];_get_node_ids_from_envelope=Module["_get_node_ids_from_envelope"]=wasmExports["vh"];_get_node_ids_from_point=Module["_get_node_ids_from_point"]=wasmExports["wh"];_grida_fonts_analyze_family=Module["_grida_fonts_analyze_family"]=wasmExports["xh"];_grida_fonts_free=Module["_grida_fonts_free"]=wasmExports["yh"];_grida_fonts_parse_font=Module["_grida_fonts_parse_font"]=wasmExports["zh"];_grida_markdown_to_html=Module["_grida_markdown_to_html"]=wasmExports["Ah"];_grida_svg_optimize=Module["_grida_svg_optimize"]=wasmExports["Bh"];_grida_svg_to_document=Module["_grida_svg_to_document"]=wasmExports["Ch"];_has_missing_fonts=Module["_has_missing_fonts"]=wasmExports["Dh"];_highlight_strokes=Module["_highlight_strokes"]=wasmExports["Eh"];_init=Module["_init"]=wasmExports["Fh"];_init_with_backend=Module["_init_with_backend"]=wasmExports["Gh"];_list_available_fonts=Module["_list_available_fonts"]=wasmExports["Hh"];_list_missing_fonts=Module["_list_missing_fonts"]=wasmExports["Ih"];_load_benchmark_scene=Module["_load_benchmark_scene"]=wasmExports["Jh"];_load_dummy_scene=Module["_load_dummy_scene"]=wasmExports["Kh"];_load_scene_grida=Module["_load_scene_grida"]=wasmExports["Lh"];_load_scene_grida1=Module["_load_scene_grida1"]=wasmExports["Mh"];_loaded_scene_ids=Module["_loaded_scene_ids"]=wasmExports["Nh"];_pointer_move=Module["_pointer_move"]=wasmExports["Oh"];_query_paint_groups=Module["_query_paint_groups"]=wasmExports["Ph"];_redraw=Module["_redraw"]=wasmExports["Qh"];_resize_surface=Module["_resize_surface"]=wasmExports["Rh"];_resolve_image=Module["_resolve_image"]=wasmExports["Sh"];_runtime_renderer_set_layer_compositing=Module["_runtime_renderer_set_layer_compositing"]=wasmExports["Th"];_runtime_renderer_set_outline_mode=Module["_runtime_renderer_set_outline_mode"]=wasmExports["Uh"];_runtime_renderer_set_pixel_preview_scale=Module["_runtime_renderer_set_pixel_preview_scale"]=wasmExports["Vh"];_runtime_renderer_set_pixel_preview_stable=Module["_runtime_renderer_set_pixel_preview_stable"]=wasmExports["Wh"];_runtime_renderer_set_render_policy_flags=Module["_runtime_renderer_set_render_policy_flags"]=wasmExports["Xh"];_runtime_renderer_set_skip_layout=Module["_runtime_renderer_set_skip_layout"]=wasmExports["Yh"];_set_debug=Module["_set_debug"]=wasmExports["Zh"];_set_default_fallback_fonts=Module["_set_default_fallback_fonts"]=wasmExports["_h"];_set_main_camera_transform=Module["_set_main_camera_transform"]=wasmExports["$h"];_set_surface_overlay_config=Module["_set_surface_overlay_config"]=wasmExports["ai"];_set_verbose=Module["_set_verbose"]=wasmExports["bi"];_surface_get_cursor=Module["_surface_get_cursor"]=wasmExports["ci"];_surface_get_hovered_node=Module["_surface_get_hovered_node"]=wasmExports["di"];_surface_get_selected_nodes=Module["_surface_get_selected_nodes"]=wasmExports["ei"];_surface_pointer_down=Module["_surface_pointer_down"]=wasmExports["fi"];_surface_pointer_move=Module["_surface_pointer_move"]=wasmExports["gi"];_surface_pointer_up=Module["_surface_pointer_up"]=wasmExports["hi"];_surface_set_selection=Module["_surface_set_selection"]=wasmExports["ii"];_switch_scene=Module["_switch_scene"]=wasmExports["ji"];_text_edit_command=Module["_text_edit_command"]=wasmExports["ki"];_text_edit_enter=Module["_text_edit_enter"]=wasmExports["li"];_text_edit_exit=Module["_text_edit_exit"]=wasmExports["mi"];_text_edit_get_caret_rect=Module["_text_edit_get_caret_rect"]=wasmExports["ni"];_text_edit_get_selected_html=Module["_text_edit_get_selected_html"]=wasmExports["oi"];_text_edit_get_selected_text=Module["_text_edit_get_selected_text"]=wasmExports["pi"];_text_edit_get_selection_rects=Module["_text_edit_get_selection_rects"]=wasmExports["qi"];_text_edit_get_text=Module["_text_edit_get_text"]=wasmExports["ri"];_text_edit_ime_cancel=Module["_text_edit_ime_cancel"]=wasmExports["si"];_text_edit_ime_commit=Module["_text_edit_ime_commit"]=wasmExports["ti"];_text_edit_ime_set_preedit=Module["_text_edit_ime_set_preedit"]=wasmExports["ui"];_text_edit_is_active=Module["_text_edit_is_active"]=wasmExports["vi"];_text_edit_paste_html=Module["_text_edit_paste_html"]=wasmExports["wi"];_text_edit_paste_text=Module["_text_edit_paste_text"]=wasmExports["xi"];_text_edit_pointer_down=Module["_text_edit_pointer_down"]=wasmExports["yi"];_text_edit_pointer_move=Module["_text_edit_pointer_move"]=wasmExports["zi"];_text_edit_pointer_up=Module["_text_edit_pointer_up"]=wasmExports["Ai"];_text_edit_redo=Module["_text_edit_redo"]=wasmExports["Bi"];_text_edit_set_color=Module["_text_edit_set_color"]=wasmExports["Ci"];_text_edit_set_font_family=Module["_text_edit_set_font_family"]=wasmExports["Di"];_text_edit_set_font_size=Module["_text_edit_set_font_size"]=wasmExports["Ei"];_text_edit_tick=Module["_text_edit_tick"]=wasmExports["Fi"];_text_edit_toggle_bold=Module["_text_edit_toggle_bold"]=wasmExports["Gi"];_text_edit_toggle_italic=Module["_text_edit_toggle_italic"]=wasmExports["Hi"];_text_edit_toggle_strikethrough=Module["_text_edit_toggle_strikethrough"]=wasmExports["Ii"];_text_edit_toggle_underline=Module["_text_edit_toggle_underline"]=wasmExports["Ji"];_text_edit_undo=Module["_text_edit_undo"]=wasmExports["Ki"];_tick=Module["_tick"]=wasmExports["Li"];_to_vector_network=Module["_to_vector_network"]=wasmExports["Mi"];_toggle_debug=Module["_toggle_debug"]=wasmExports["Ni"];_main=Module["_main"]=wasmExports["Oi"];_emscripten_builtin_memalign=wasmExports["Pi"];_setThrew=wasmExports["Qi"];__emscripten_tempret_set=wasmExports["Ri"];__emscripten_stack_restore=wasmExports["Si"];__emscripten_stack_alloc=wasmExports["Ti"];_emscripten_stack_get_current=wasmExports["Ui"];___cxa_decrement_exception_refcount=wasmExports["Vi"];___cxa_increment_exception_refcount=wasmExports["Wi"];___cxa_can_catch=wasmExports["Xi"];___cxa_get_exception_ptr=wasmExports["Yi"];memory=wasmMemory=wasmExports["Yg"];__indirect_function_table=wasmTable=wasmExports["$g"]}var wasmImports={I:___cxa_begin_catch,S:___cxa_end_catch,a:___cxa_find_matching_catch_2,n:___cxa_find_matching_catch_3,ka:___cxa_find_matching_catch_4,Ma:___cxa_rethrow,K:___cxa_throw,gb:___cxa_uncaught_exceptions,e:___resumeException,Qa:___syscall_fcntl64,xb:___syscall_fstat64,tb:___syscall_getcwd,yb:___syscall_ioctl,ub:___syscall_lstat64,vb:___syscall_newfstatat,Ra:___syscall_openat,wb:___syscall_stat64,Bb:__abort_js,ib:__emscripten_throw_longjmp,ob:__gmtime_js,mb:__mmap_js,nb:__munmap_js,Db:__tzset_js,Ab:_clock_time_get,zb:_emscripten_date_now,kb:_emscripten_get_heap_max,Ef:_emscripten_glActiveTexture,Ff:_emscripten_glAttachShader,ee:_emscripten_glBeginQuery,_d:_emscripten_glBeginQueryEXT,Gc:_emscripten_glBeginTransformFeedback,Gf:_emscripten_glBindAttribLocation,Hf:_emscripten_glBindBuffer,Dc:_emscripten_glBindBufferBase,Ec:_emscripten_glBindBufferRange,De:_emscripten_glBindFramebuffer,Ee:_emscripten_glBindRenderbuffer,ke:_emscripten_glBindSampler,If:_emscripten_glBindTexture,Ub:_emscripten_glBindTransformFeedback,Ze:_emscripten_glBindVertexArray,af:_emscripten_glBindVertexArrayOES,Jf:_emscripten_glBlendColor,Kf:_emscripten_glBlendEquation,Kd:_emscripten_glBlendEquationSeparate,Lf:_emscripten_glBlendFunc,Jd:_emscripten_glBlendFuncSeparate,xe:_emscripten_glBlitFramebuffer,Mf:_emscripten_glBufferData,Nf:_emscripten_glBufferSubData,Fe:_emscripten_glCheckFramebufferStatus,Of:_emscripten_glClear,hc:_emscripten_glClearBufferfi,ic:_emscripten_glClearBufferfv,kc:_emscripten_glClearBufferiv,jc:_emscripten_glClearBufferuiv,Pf:_emscripten_glClearColor,Id:_emscripten_glClearDepthf,Qf:_emscripten_glClearStencil,ue:_emscripten_glClientWaitSync,$c:_emscripten_glClipControlEXT,Rf:_emscripten_glColorMask,Sf:_emscripten_glCompileShader,Tf:_emscripten_glCompressedTexImage2D,Sc:_emscripten_glCompressedTexImage3D,Uf:_emscripten_glCompressedTexSubImage2D,Rc:_emscripten_glCompressedTexSubImage3D,we:_emscripten_glCopyBufferSubData,Hd:_emscripten_glCopyTexImage2D,Vf:_emscripten_glCopyTexSubImage2D,Tc:_emscripten_glCopyTexSubImage3D,Wf:_emscripten_glCreateProgram,Xf:_emscripten_glCreateShader,Yf:_emscripten_glCullFace,Zf:_emscripten_glDeleteBuffers,Ge:_emscripten_glDeleteFramebuffers,_f:_emscripten_glDeleteProgram,fe:_emscripten_glDeleteQueries,$d:_emscripten_glDeleteQueriesEXT,He:_emscripten_glDeleteRenderbuffers,le:_emscripten_glDeleteSamplers,$f:_emscripten_glDeleteShader,ve:_emscripten_glDeleteSync,ag:_emscripten_glDeleteTextures,Tb:_emscripten_glDeleteTransformFeedbacks,_e:_emscripten_glDeleteVertexArrays,bf:_emscripten_glDeleteVertexArraysOES,Gd:_emscripten_glDepthFunc,bg:_emscripten_glDepthMask,Fd:_emscripten_glDepthRangef,Ed:_emscripten_glDetachShader,cg:_emscripten_glDisable,dg:_emscripten_glDisableVertexAttribArray,eg:_emscripten_glDrawArrays,Xe:_emscripten_glDrawArraysInstanced,Nd:_emscripten_glDrawArraysInstancedANGLE,Gb:_emscripten_glDrawArraysInstancedARB,Ue:_emscripten_glDrawArraysInstancedBaseInstanceWEBGL,Yc:_emscripten_glDrawArraysInstancedEXT,Hb:_emscripten_glDrawArraysInstancedNV,Se:_emscripten_glDrawBuffers,Wc:_emscripten_glDrawBuffersEXT,Od:_emscripten_glDrawBuffersWEBGL,fg:_emscripten_glDrawElements,Ye:_emscripten_glDrawElementsInstanced,Md:_emscripten_glDrawElementsInstancedANGLE,Eb:_emscripten_glDrawElementsInstancedARB,Ve:_emscripten_glDrawElementsInstancedBaseVertexBaseInstanceWEBGL,Fb:_emscripten_glDrawElementsInstancedEXT,Xc:_emscripten_glDrawElementsInstancedNV,Me:_emscripten_glDrawRangeElements,gg:_emscripten_glEnable,hg:_emscripten_glEnableVertexAttribArray,ge:_emscripten_glEndQuery,ae:_emscripten_glEndQueryEXT,Fc:_emscripten_glEndTransformFeedback,qe:_emscripten_glFenceSync,ig:_emscripten_glFinish,jg:_emscripten_glFlush,Ie:_emscripten_glFramebufferRenderbuffer,Je:_emscripten_glFramebufferTexture2D,Jc:_emscripten_glFramebufferTextureLayer,kg:_emscripten_glFrontFace,lg:_emscripten_glGenBuffers,Ke:_emscripten_glGenFramebuffers,he:_emscripten_glGenQueries,be:_emscripten_glGenQueriesEXT,Le:_emscripten_glGenRenderbuffers,me:_emscripten_glGenSamplers,mg:_emscripten_glGenTextures,Sb:_emscripten_glGenTransformFeedbacks,We:_emscripten_glGenVertexArrays,cf:_emscripten_glGenVertexArraysOES,ze:_emscripten_glGenerateMipmap,Dd:_emscripten_glGetActiveAttrib,Cd:_emscripten_glGetActiveUniform,cc:_emscripten_glGetActiveUniformBlockName,dc:_emscripten_glGetActiveUniformBlockiv,fc:_emscripten_glGetActiveUniformsiv,Bd:_emscripten_glGetAttachedShaders,Ad:_emscripten_glGetAttribLocation,zd:_emscripten_glGetBooleanv,Zb:_emscripten_glGetBufferParameteri64v,ng:_emscripten_glGetBufferParameteriv,og:_emscripten_glGetError,pg:_emscripten_glGetFloatv,tc:_emscripten_glGetFragDataLocation,Ae:_emscripten_glGetFramebufferAttachmentParameteriv,_b:_emscripten_glGetInteger64i_v,ac:_emscripten_glGetInteger64v,Hc:_emscripten_glGetIntegeri_v,qg:_emscripten_glGetIntegerv,Kb:_emscripten_glGetInternalformativ,Ob:_emscripten_glGetProgramBinary,rg:_emscripten_glGetProgramInfoLog,sg:_emscripten_glGetProgramiv,Xd:_emscripten_glGetQueryObjecti64vEXT,Qd:_emscripten_glGetQueryObjectivEXT,Yd:_emscripten_glGetQueryObjectui64vEXT,ie:_emscripten_glGetQueryObjectuiv,ce:_emscripten_glGetQueryObjectuivEXT,je:_emscripten_glGetQueryiv,de:_emscripten_glGetQueryivEXT,Be:_emscripten_glGetRenderbufferParameteriv,Vb:_emscripten_glGetSamplerParameterfv,Wb:_emscripten_glGetSamplerParameteriv,tg:_emscripten_glGetShaderInfoLog,Ud:_emscripten_glGetShaderPrecisionFormat,yd:_emscripten_glGetShaderSource,ug:_emscripten_glGetShaderiv,vg:_emscripten_glGetString,$e:_emscripten_glGetStringi,$b:_emscripten_glGetSynciv,xd:_emscripten_glGetTexParameterfv,wd:_emscripten_glGetTexParameteriv,Bc:_emscripten_glGetTransformFeedbackVarying,ec:_emscripten_glGetUniformBlockIndex,gc:_emscripten_glGetUniformIndices,wg:_emscripten_glGetUniformLocation,vd:_emscripten_glGetUniformfv,ud:_emscripten_glGetUniformiv,uc:_emscripten_glGetUniformuiv,Ac:_emscripten_glGetVertexAttribIiv,zc:_emscripten_glGetVertexAttribIuiv,rd:_emscripten_glGetVertexAttribPointerv,td:_emscripten_glGetVertexAttribfv,sd:_emscripten_glGetVertexAttribiv,qd:_emscripten_glHint,Vd:_emscripten_glInvalidateFramebuffer,Wd:_emscripten_glInvalidateSubFramebuffer,pd:_emscripten_glIsBuffer,od:_emscripten_glIsEnabled,nd:_emscripten_glIsFramebuffer,md:_emscripten_glIsProgram,Qc:_emscripten_glIsQuery,Rd:_emscripten_glIsQueryEXT,ld:_emscripten_glIsRenderbuffer,Yb:_emscripten_glIsSampler,kd:_emscripten_glIsShader,se:_emscripten_glIsSync,yg:_emscripten_glIsTexture,Rb:_emscripten_glIsTransformFeedback,Ic:_emscripten_glIsVertexArray,Pd:_emscripten_glIsVertexArrayOES,zg:_emscripten_glLineWidth,Ag:_emscripten_glLinkProgram,Qe:_emscripten_glMultiDrawArraysInstancedBaseInstanceWEBGL,Re:_emscripten_glMultiDrawElementsInstancedBaseVertexBaseInstanceWEBGL,Qb:_emscripten_glPauseTransformFeedback,Bg:_emscripten_glPixelStorei,_c:_emscripten_glPolygonModeWEBGL,jd:_emscripten_glPolygonOffset,ad:_emscripten_glPolygonOffsetClampEXT,Nb:_emscripten_glProgramBinary,Mb:_emscripten_glProgramParameteri,Zd:_emscripten_glQueryCounterEXT,Te:_emscripten_glReadBuffer,Cg:_emscripten_glReadPixels,id:_emscripten_glReleaseShaderCompiler,Ce:_emscripten_glRenderbufferStorage,ye:_emscripten_glRenderbufferStorageMultisample,Pb:_emscripten_glResumeTransformFeedback,hd:_emscripten_glSampleCoverage,ne:_emscripten_glSamplerParameterf,Xb:_emscripten_glSamplerParameterfv,oe:_emscripten_glSamplerParameteri,pe:_emscripten_glSamplerParameteriv,Dg:_emscripten_glScissor,gd:_emscripten_glShaderBinary,Eg:_emscripten_glShaderSource,Fg:_emscripten_glStencilFunc,Gg:_emscripten_glStencilFuncSeparate,Hg:_emscripten_glStencilMask,Ig:_emscripten_glStencilMaskSeparate,Jg:_emscripten_glStencilOp,Kg:_emscripten_glStencilOpSeparate,Lg:_emscripten_glTexImage2D,Vc:_emscripten_glTexImage3D,Mg:_emscripten_glTexParameterf,Ng:_emscripten_glTexParameterfv,Og:_emscripten_glTexParameteri,Pg:_emscripten_glTexParameteriv,Ne:_emscripten_glTexStorage2D,Lb:_emscripten_glTexStorage3D,Qg:_emscripten_glTexSubImage2D,Uc:_emscripten_glTexSubImage3D,Cc:_emscripten_glTransformFeedbackVaryings,Rg:_emscripten_glUniform1f,Sg:_emscripten_glUniform1fv,zf:_emscripten_glUniform1i,Bf:_emscripten_glUniform1iv,sc:_emscripten_glUniform1ui,oc:_emscripten_glUniform1uiv,Cf:_emscripten_glUniform2f,Df:_emscripten_glUniform2fv,yf:_emscripten_glUniform2i,xf:_emscripten_glUniform2iv,rc:_emscripten_glUniform2ui,nc:_emscripten_glUniform2uiv,wf:_emscripten_glUniform3f,vf:_emscripten_glUniform3fv,uf:_emscripten_glUniform3i,tf:_emscripten_glUniform3iv,qc:_emscripten_glUniform3ui,mc:_emscripten_glUniform3uiv,sf:_emscripten_glUniform4f,rf:_emscripten_glUniform4fv,df:_emscripten_glUniform4i,ff:_emscripten_glUniform4iv,pc:_emscripten_glUniform4ui,lc:_emscripten_glUniform4uiv,bc:_emscripten_glUniformBlockBinding,gf:_emscripten_glUniformMatrix2fv,Pc:_emscripten_glUniformMatrix2x3fv,Nc:_emscripten_glUniformMatrix2x4fv,hf:_emscripten_glUniformMatrix3fv,Oc:_emscripten_glUniformMatrix3x2fv,Lc:_emscripten_glUniformMatrix3x4fv,jf:_emscripten_glUniformMatrix4fv,Mc:_emscripten_glUniformMatrix4x2fv,Kc:_emscripten_glUniformMatrix4x3fv,kf:_emscripten_glUseProgram,fd:_emscripten_glValidateProgram,lf:_emscripten_glVertexAttrib1f,ed:_emscripten_glVertexAttrib1fv,dd:_emscripten_glVertexAttrib2f,mf:_emscripten_glVertexAttrib2fv,cd:_emscripten_glVertexAttrib3f,nf:_emscripten_glVertexAttrib3fv,bd:_emscripten_glVertexAttrib4f,of:_emscripten_glVertexAttrib4fv,Oe:_emscripten_glVertexAttribDivisor,Ld:_emscripten_glVertexAttribDivisorANGLE,Ib:_emscripten_glVertexAttribDivisorARB,Zc:_emscripten_glVertexAttribDivisorEXT,Jb:_emscripten_glVertexAttribDivisorNV,yc:_emscripten_glVertexAttribI4i,wc:_emscripten_glVertexAttribI4iv,xc:_emscripten_glVertexAttribI4ui,vc:_emscripten_glVertexAttribI4uiv,Pe:_emscripten_glVertexAttribIPointer,pf:_emscripten_glVertexAttribPointer,qf:_emscripten_glViewport,te:_emscripten_glWaitSync,Za:_emscripten_request_animation_frame_loop,jb:_emscripten_resize_heap,qb:_environ_get,rb:_environ_sizes_get,Vg:_exit,ta:_fd_close,lb:_fd_pread,Pa:_fd_read,pb:_fd_seek,sa:_fd_write,Tg:_glGetIntegerv,Sa:_glGetString,Ug:_glGetStringi,Sd:invoke_dd,Td:invoke_dddd,Ka:invoke_diii,Ua:invoke_fdiiii,Xg:invoke_fdiiiii,Wg:invoke_fii,La:invoke_fiii,w:invoke_fiiidi,Z:invoke_fiiif,x:invoke_fiiiidi,v:invoke_i,j:invoke_ii,J:invoke_iif,bb:invoke_iiffi,xa:invoke_iiffiii,f:invoke_iii,_a:invoke_iiif,la:invoke_iiiff,oa:invoke_iiifi,g:invoke_iiii,u:invoke_iiiif,L:invoke_iiiiff,l:invoke_iiiii,fb:invoke_iiiiid,r:invoke_iiiiii,C:invoke_iiiiiii,H:invoke_iiiiiiii,t:invoke_iiiiiiiii,Ta:invoke_iiiiiiiiii,ha:invoke_iiiiiiiiiiii,va:invoke_iiiiiiiiiiiifiij,M:invoke_iij,xg:invoke_iijj,q:invoke_ij,hb:invoke_j,na:invoke_ji,s:invoke_jiii,ia:invoke_jiiii,aa:invoke_jiijj,O:invoke_jjji,k:invoke_v,Af:invoke_vff,b:invoke_vi,U:invoke_vid,Y:invoke_vif,y:invoke_viff,ef:invoke_vifff,G:invoke_viffff,da:invoke_vifffff,Va:invoke_viffffff,E:invoke_viffi,wa:invoke_viffiiiiiii,c:invoke_vii,Ya:invoke_viidii,T:invoke_viif,F:invoke_viiff,Oa:invoke_viiffii,X:invoke_viifi,Aa:invoke_viififii,B:invoke_viifiiifi,d:invoke_viii,Fa:invoke_viiif,Ca:invoke_viiiff,z:invoke_viiiffi,P:invoke_viiiffiffii,Q:invoke_viiififiiiiiiiiiiii,i:invoke_viiii,Xa:invoke_viiiidididii,qa:invoke_viiiif,Da:invoke_viiiiff,ra:invoke_viiiiffi,Ba:invoke_viiiifi,h:invoke_viiiii,cb:invoke_viiiiif,Wa:invoke_viiiiiffiii,Ga:invoke_viiiiifi,m:invoke_viiiiii,Na:invoke_viiiiiiff,p:invoke_viiiiiii,_:invoke_viiiiiiii,Ia:invoke_viiiiiiiifij,ca:invoke_viiiiiiiii,W:invoke_viiiiiiiiii,eb:invoke_viiiiiiiiiifij,ya:invoke_viiiiiiiiiii,ga:invoke_viiiiiiiiiiiiiii,re:invoke_viiiiiiji,N:invoke_viiij,D:invoke_viiijii,R:invoke_viij,za:invoke_viijffiiii,o:invoke_viiji,Ja:invoke_viijiffi,fa:invoke_viijii,Ea:invoke_viijiii,ea:invoke_viijiiiif,ma:invoke_viijiiiii,pa:invoke_viijj,ba:invoke_vij,V:invoke_viji,$a:invoke_vijififi,A:invoke_vijii,Ha:invoke_vijiifi,ab:invoke_vijiififi,$:invoke_vijiii,db:invoke_vijijjiii,ja:invoke_vijjjj,Cb:invoke_vjii,ua:_llvm_eh_typeid_for,sb:_random_get};function invoke_vi(index,a1){var sp=stackSave();try{getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ij(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iijj(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_ji(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiij(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vff(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viji(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vifff(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viij(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iij(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vij(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiji(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiff(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiijj(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiji(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiif(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vid(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiiifiij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vif(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiff(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffi(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vjii(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiffii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiff(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijii(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiffi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiifij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiifij(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijijjiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viifiiifi(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiffi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiifi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiif(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiififiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijiiiif(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffiffii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiff(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiffi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiif(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiif(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiifi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiffi(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viififii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_vijiififi(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijififi(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijj(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jjji(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiif(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiifi(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iif(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viifi(index,a1,a2,a3,a4){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viijffiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viif(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiffiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viff(index,a1,a2,a3){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vifffff(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiidi(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viidii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiidididii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiiidi(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiffiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiijii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffff(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffffff(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fiiif(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fdiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fdiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_fii(index,a1,a2){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viffiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_dddd(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_dd(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_vijjjj(index,a1,a2,a3,a4,a5){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_j(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_iiiiid(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_jiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0);return 0n}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_diii(index,a1,a2,a3){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0)throw e;_setThrew(1,0)}}function callMain(args=[]){var entryFunction=_main;args.unshift(thisProgram);var argc=args.length;var argv=stackAlloc((argc+1)*4);var argv_ptr=argv;for(var arg of args){HEAPU32[argv_ptr>>2]=stringToUTF8OnStack(arg);argv_ptr+=4}HEAPU32[argv_ptr>>2]=0;try{var ret=entryFunction(argc,argv);exitJS(ret,true);return ret}catch(e){return handleException(e)}}function run(args=arguments_){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();var noInitialRun=Module["noInitialRun"]||false;if(!noInitialRun)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}var wasmExports;wasmExports=await (createWasm());run();if(runtimeInitialized){moduleRtn=Module}else{moduleRtn=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject})} ;return moduleRtn}})();if(typeof exports==="object"&&typeof module==="object"){module.exports=createGridaCanvas;module.exports.default=createGridaCanvas}else if(typeof define==="function"&&define["amd"])define([],()=>createGridaCanvas); diff --git a/crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm b/crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm index ced922a6b3..a235a3e022 100755 --- a/crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm +++ b/crates/grida-canvas-wasm/lib/bin/grida_canvas_wasm.wasm @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:91590f518a6549e86f79543e5a03a83b4bcd4ca8253536dea5f976990aeb5ab1 -size 13359335 +oid sha256:03ec32e57b13773b0ed2c83ed1700a5e0c717923bdf51fadd38df96bcf5e83ba +size 13370181 From f3728bc9ba38cf1cc78799614a9790a6ccd132b9 Mon Sep 17 00:00:00 2001 From: Universe Date: Wed, 1 Apr 2026 00:09:42 +0900 Subject: [PATCH 4/7] docs: add Render Cost Prediction reference for 2D GPU rendering This new document provides a comprehensive guide on estimating GPU render costs for 2D scene operations, focusing on memory bandwidth and fill rate dominance. It includes core principles, effect cost constants, and a per-node cost formula, along with insights on cache efficiency and implicit save_layer triggers. This resource aims to assist developers in optimizing rendering performance. --- docs/wg/feat-2d/render-cost-prediction.md | 264 ++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 docs/wg/feat-2d/render-cost-prediction.md diff --git a/docs/wg/feat-2d/render-cost-prediction.md b/docs/wg/feat-2d/render-cost-prediction.md new file mode 100644 index 0000000000..0c4c1f1152 --- /dev/null +++ b/docs/wg/feat-2d/render-cost-prediction.md @@ -0,0 +1,264 @@ +--- +title: Render Cost Prediction +format: md +tags: + - internal + - wg + - canvas + - performance + - rendering + - frame-budget +--- + +# Render Cost Prediction + +Reference sheet for computing GPU render cost of 2D scene operations +**before drawing**. All constants and formulas are derived from GPU +pipeline structure, not empirical tuning. + +Related: + +- [Rendering Optimization Strategies](./optimization.md) — implemented optimizations +- [Chromium Compositor Research](../research/chromium/index.md) — reference architecture + +--- + +## Core Principle: Fill Rate Dominance + +2D GPU rendering is **memory-bandwidth bound**, not compute bound. The +fragment shader for a rect fill is ~1 ALU op; even a Gaussian blur pass +is ~10 ALU ops per pixel. Modern GPUs execute trillions of ALU ops/sec, +but memory bandwidth is 50-200 GB/s. Each pixel read/write is 4-16 bytes. + +Therefore: + +``` +frame_cost ≈ total_pixels_touched / memory_bandwidth +``` + +This relationship is **linear**. Double the pixels, double the time. +No surprises, no non-linear scaling — as long as you stay within VRAM +and don't hit texture cache thrashing (rare in 2D; access is spatially +coherent). + +This means render cost can be pre-computed as an **ALU/pixel budget**: +count the pixels the GPU will touch, apply structural multipliers per +effect, and compare against a calibrated device budget. + +--- + +## Effect Cost Constants + +These are not magic numbers or tuning parameters. They are the +**structural pass counts** of each rendering operation — how many +full-area read-write cycles the GPU performs. + +| Effect | Pixel Multiplier | Derivation | +| ------------------------------------- | -------------------- | ---------------------------------------------------------- | +| Plain shape (rect, ellipse, polygon) | `1×` | Single fill pass | +| Additional fill (N fills on one node) | `+1×` per extra fill | Each fill is a separate pass | +| Additional stroke | `+1×` per stroke | Separate pass | +| Non-rect clip path | `+1×` | Mask pass + masked content | +| Rect clip | `+0×` | Hardware scissor — free | +| Blend mode (non-normal) | `+1×` | Requires offscreen isolation layer | +| Group opacity (alpha < 1.0 on group) | `+1×` | `save_layer` for isolated compositing | +| Gaussian blur | `+3×` | Downsample pyramid (~1.33×) + blur + upsample + composite | +| Drop shadow | `+5×` | Draw shape (1×) + blur pipeline (3×) + composite back (1×) | +| Inner shadow | `+5×` | Same as drop shadow, inverted mask | +| Backdrop filter (background blur) | `+3×` | Snapshot dst + blur + composite | +| Layer blur (on node itself) | `+3×` | Offscreen + blur + composite | +| Image fill | `+0×` over base | Texture sample replaces color fill — same bandwidth | +| Multiple shadows | `+5×` per shadow | Each shadow is independent | + +### Blur Radius Independence + +Skia (and most GPU frameworks) implement Gaussian blur via a **downsample +pyramid**, not a brute-force kernel convolution: + +``` +large sigma → downsample 2× → downsample 2× → ... → blur at reduced size → upsample +``` + +Total pixel work = `area × (1 + 1/4 + 1/16 + ...) ≈ area × 1.33` (geometric +series), plus the blur pass at reduced resolution. The cost is approximately +**constant regardless of blur radius**. The pyramid absorbs the radius. + +### `save_layer` / `save_layer_alpha` — The Hidden Spike Source + +`save_layer` is the single most expensive primitive in Skia. It allocates an +offscreen surface, renders content into it, then composites back. + +``` +save_layer_cost = layer_bounds_area × zoom² × 2 (write to offscreen + read back) +``` + +Critical: **they cascade multiplicatively with nesting depth**. + +``` +save_layer ← offscreen A (full group bounds) + save_layer ← offscreen B (child bounds) + save_layer ← offscreen C (grandchild bounds) + draw rect + restore → composite C into B + restore → composite B into A +restore → composite A into target +``` + +Three nested layers on the same area = `area × 6` bandwidth, not `area × 2`. + +#### Implicit `save_layer` triggers + +Skia inserts `save_layer` implicitly for these conditions. The cost estimator +must account for them even when the application code does not call `save_layer` +explicitly: + +| Trigger | Reason | +| ----------------------------------------- | --------------------------------------------------------------- | +| Non-normal blend mode on a group | Isolated offscreen to blend against dst | +| Group opacity (alpha < 1.0 with children) | Children must composite together first, then alpha applied once | +| Blur / backdrop filter | Reads from dst, needs snapshot | +| Clip + antialiasing on groups | Soft-edge mask requires offscreen | +| `ColorFilter` on a group | Applied after children composite | + +--- + +## Per-Node Cost Formula + +```rust +fn estimated_fill_pixels(node: &Node, zoom: f32, viewport: &Rect) -> f64 { + let screen_area = clipped_area(&node.bounds, viewport) * (zoom * zoom) as f64; + + // Base draw + let mut passes: f64 = 1.0; + + // Extra fills/strokes beyond the first + passes += (node.fill_count.saturating_sub(1)) as f64; + passes += node.stroke_count as f64; + + // Effects + for shadow in &node.shadows { + if shadow.visible { + passes += 5.0; // shape + blur pipeline + composite + } + } + if node.has_blur() { + passes += 3.0; // downsample + blur + composite + } + if node.has_backdrop_blur() { + passes += 3.0; + } + + // Isolation layers (implicit save_layer) + if node.blend_mode != BlendMode::Normal { + passes += 1.0; // offscreen + composite + } + if node.opacity < 1.0 && node.has_children() { + passes += 1.0; // group opacity isolation + } + + // Clip + if node.has_non_rect_clip() { + passes += 1.0; // mask pass + } + + screen_area * passes +} +``` + +### Cache Hit vs. Miss Cost + +A compositor/picture cache **hit** replaces the full rasterization pipeline +with a single texture blit: + +| State | Effective multiplier | What happens | +| ---------- | ----------------------------- | ---------------------------------------------------- | +| Cache miss | `passes ×` (from table above) | Full rasterization: path tessellation, fill, effects | +| Cache hit | `~0.1×` | Single texture-sampled quad draw | + +The cost difference is **100-1000×**. Cache state is a binary signal — the +single largest contributor to per-node cost variance. + +--- + +## Device Fill Rate Reference + +The total pixel budget depends on device fill rate — the one value that +varies per hardware. Everything else is derived from geometry and scene +structure. + +### Calibration + +Render a known workload (e.g., full-screen solid rect) and measure: + +``` +pixels_per_ms = (screen_width × screen_height) / render_time_ms +``` + +### Reference Values (order-of-magnitude) + +| Platform | Expected pixels_per_ms | +| ------------------------ | ---------------------- | +| Desktop GPU (discrete) | ~500M | +| Desktop GPU (integrated) | ~100M | +| WebGL (WASM, desktop) | ~50-100M | +| WebGL (WASM, mobile) | ~10-30M | + +--- + +## Chromium Reference + +Chromium's `cc/` compositor collects similar metrics but uses them differently: + +| Metric | Chromium Location | Chromium Usage | +| ------------------------------------- | ------------------------------ | ------------------------------------------------------------- | +| `TotalOpCount()` | `cc/paint/display_item_list.h` | Solid-color analysis gate | +| `num_slow_paths_up_to_min_for_MSAA()` | `cc/paint/display_item_list.h` | Page-level GPU raster veto | +| `has_save_layer_ops()` | `cc/paint/display_item_list.h` | LCD text decision | +| `has_non_aa_paint()` | `cc/paint/display_item_list.h` | Antialiasing decisions | +| `BytesUsed()` / `OpBytesUsed()` | `cc/paint/display_item_list.h` | Tracing / debugging | +| `AreaOfDrawText()` | `cc/paint/display_item_list.h` | Text coverage statistics | +| Solid color analysis | `cc/tiles/tile_manager.cc` | Skip rasterization for uniform tiles (`kMaxOpsToAnalyze = 5`) | + +Chromium does **not** perform per-tile raster cost prediction. Tile +scheduling is purely spatial (viewport distance + scroll velocity) with +a memory budget constraint. Their architecture tolerates stale tiles +(multi-threaded raster catches up across frames). Ours cannot — we render +single-threaded with a hard per-frame deadline, requiring predictive +budgeting. + +Local source: `/Users/softmarshmallow/Documents/Github/chromium/cc/` + +--- + +## Skia `Picture` Metrics (Available for Free) + +Skia's `Picture` object exposes complexity metrics that are already +computed during recording and cost nothing to query: + +| Method | What it returns | Use | +| -------------------------- | ---------------------------------- | ---------------------------------- | +| `approximate_op_count()` | Number of draw operations recorded | Secondary complexity signal | +| `approximate_bytes_used()` | Serialized size of the picture | Memory pressure / complexity proxy | + +These are stored fields, not computations. They complement the pixel-area +model by capturing path complexity variance (a 1000-op picture with +complex beziers vs. a 3-op picture with simple rects at the same pixel +area). + +--- + +## Linearity Bounds + +The fill-rate model is linear under these conditions: + +| Condition | Linear? | Notes | +| ---------------------------------- | ------------------- | ------------------------------------------------------ | +| Work above ~10K pixels | Yes | Below this, GPU launch overhead dominates (flat floor) | +| Spatial texture access (normal 2D) | Yes | Bandwidth-bound, no cache thrashing | +| Random texture access | Can be super-linear | Rare in 2D rendering | +| Tile-based GPU (mobile) | Mostly | Large nodes spanning many tiles add per-tile overhead | +| Thermal throttling | N/A | Between-frame variance, not within-frame | +| VRAM pressure / swapping | Non-linear | Catastrophic; avoid by staying within budget | + +For typical 2D canvas rendering (spatial access, nodes > 10K pixels), +the linear model holds. From 532d53280c46ba21b152c494ef6b37c49b0d04b7 Mon Sep 17 00:00:00 2001 From: Universe Date: Wed, 1 Apr 2026 00:40:30 +0900 Subject: [PATCH 5/7] feat(cg, grida-dev): native surface UI + dev-only editor with translate/resize Surface UI (cg): - Selection handles: 8 resize knobs + 4 rotation hit zones - Hover/selection highlight model with bounding rect overlay - Directional resize + rotation cursors - Gesture state: Translate, Resize, Rotate (incremental prev_* model) - Deferred selection for drag-on-selected-node - readonly flag (default true, web unaffected) - show_selection_handles overlay config flag - Query API on UTA: hit_test_point, get_node_bounds, get_union_bounds, point_in_node_bounds, view_matrix - Remove legacy hit_overlay (replaced by surface overlay hover) Editor (grida-dev): - New editor/ module: document.rs, mutation.rs - EditorDocument owns a Scene, applies MutationCommand, flushes via load_scene() - Translate + Resize commands with per-node-type dispatch - Text resize: sets paragraph width, preserves auto axes (Option per axis) - Vector resize: explicitly unsupported (no-op, documented) - Resize geometry helpers: axis detection, origin-shift for NW/N/W handles - Gesture-to-mutation translation in NativeApplication Architecture: - cg is renderer-only, zero mutation code - grida-dev editor owns document state, reads via UTA query API, writes via load_scene() - Node::transform_mut/size_mut moved from cg to grida-dev - refresh_node_geo_data stays on SceneGraph (required for geo_data sync) --- .../grida-canvas-wasm/src/wasm_application.rs | 6 + crates/grida-canvas/src/cache/geometry.rs | 5 + .../grida-canvas/src/devtools/hit_overlay.rs | 148 ----- crates/grida-canvas/src/devtools/mod.rs | 1 - .../src/devtools/surface_overlay.rs | 73 ++- crates/grida-canvas/src/hittest/hit_tester.rs | 42 ++ crates/grida-canvas/src/node/scene_graph.rs | 14 + crates/grida-canvas/src/runtime/changes.rs | 8 + crates/grida-canvas/src/surface/cursor.rs | 107 ++++ crates/grida-canvas/src/surface/gesture.rs | 37 +- crates/grida-canvas/src/surface/mod.rs | 2 +- crates/grida-canvas/src/surface/state.rs | 337 ++++++++-- crates/grida-canvas/src/surface/ui/handles.rs | 496 +++++++++++++++ .../grida-canvas/src/surface/ui/hit_region.rs | 15 +- crates/grida-canvas/src/surface/ui/mod.rs | 2 + crates/grida-canvas/src/surface/ui/render.rs | 70 ++ crates/grida-canvas/src/window/application.rs | 152 +++-- .../grida-canvas/tests/surface_interaction.rs | 602 ++++++++++++++++++ crates/grida-dev/src/bench/runner.rs | 1 + crates/grida-dev/src/editor/document.rs | 192 ++++++ crates/grida-dev/src/editor/mod.rs | 15 + crates/grida-dev/src/editor/mutation.rs | 248 ++++++++ crates/grida-dev/src/lib.rs | 1 + .../src/platform/native_application.rs | 55 +- 24 files changed, 2342 insertions(+), 287 deletions(-) delete mode 100644 crates/grida-canvas/src/devtools/hit_overlay.rs create mode 100644 crates/grida-canvas/src/surface/ui/handles.rs create mode 100644 crates/grida-canvas/tests/surface_interaction.rs create mode 100644 crates/grida-dev/src/editor/document.rs create mode 100644 crates/grida-dev/src/editor/mod.rs create mode 100644 crates/grida-dev/src/editor/mutation.rs diff --git a/crates/grida-canvas-wasm/src/wasm_application.rs b/crates/grida-canvas-wasm/src/wasm_application.rs index 91f5941ddc..937334eca7 100644 --- a/crates/grida-canvas-wasm/src/wasm_application.rs +++ b/crates/grida-canvas-wasm/src/wasm_application.rs @@ -389,6 +389,9 @@ pub unsafe extern "C" fn surface_get_cursor(app: *const UnknownTargetApplication cg::surface::CursorIcon::Grabbing => 3, cg::surface::CursorIcon::Crosshair => 4, cg::surface::CursorIcon::Move => 5, + // Resize/Rotate cursors are handled by the native editor surface. + // The web editor manages its own CSS cursors — map to default. + cg::surface::CursorIcon::Resize(_) | cg::surface::CursorIcon::Rotate(_) => 0, }, None => 0, } @@ -563,6 +566,8 @@ pub unsafe extern "C" fn set_surface_overlay_config( show_size_meter: bool, #[serde(default)] show_frame_titles: bool, + #[serde(default)] + show_selection_handles: bool, } fn default_dpr() -> f32 { 1.0 @@ -574,6 +579,7 @@ pub unsafe extern "C" fn set_surface_overlay_config( text_baseline_decoration: cfg.text_baseline_decoration, show_size_meter: cfg.show_size_meter, show_frame_titles: cfg.show_frame_titles, + show_selection_handles: cfg.show_selection_handles, }; } } diff --git a/crates/grida-canvas/src/cache/geometry.rs b/crates/grida-canvas/src/cache/geometry.rs index 9ce66996c3..f484214b03 100644 --- a/crates/grida-canvas/src/cache/geometry.rs +++ b/crates/grida-canvas/src/cache/geometry.rs @@ -500,6 +500,11 @@ impl GeometryCache { } } + /// Access the full geometry entry for a node. + pub fn get_entry(&self, id: &NodeId) -> Option<&GeometryEntry> { + self.entries.get(id) + } + pub fn get_transform(&self, id: &NodeId) -> Option { self.entries.get(id).map(|e| e.transform) } diff --git a/crates/grida-canvas/src/devtools/hit_overlay.rs b/crates/grida-canvas/src/devtools/hit_overlay.rs deleted file mode 100644 index 57cfc0ea8b..0000000000 --- a/crates/grida-canvas/src/devtools/hit_overlay.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::cache::scene::SceneCache; -use crate::devtools::{stroke_overlay, text_overlay}; -use crate::node::schema::NodeId; -use crate::painter::layer::Layer; -use crate::runtime::camera::Camera2D; -use crate::runtime::font_repository::FontRepository; -use crate::sk; -use skia_safe::{Canvas, Color, Font, Paint, PaintStyle, Point, Rect}; - -thread_local! { - static BG_PAINT: Paint = { - let mut p = Paint::default(); - p.set_color(Color::from_argb(160, 0, 0, 0)); - p.set_anti_alias(true); - p - }; - - static TEXT_PAINT: Paint = { - let mut p = Paint::default(); - p.set_color(Color::WHITE); - p.set_anti_alias(true); - p - }; - - static FONT: Font = Font::new(crate::fonts::embedded::typeface(crate::fonts::embedded::geistmono::BYTES), 20.0); - - static STROKE: Paint = { - let mut p = Paint::default(); - p.set_color(Color::from_argb(200, 255, 0, 0)); - p.set_style(PaintStyle::Stroke); - p.set_stroke_width(4.0); - p.set_anti_alias(true); - p - }; - - static FOCUS_STROKE: Paint = { - let mut p = Paint::default(); - p.set_color(Color::from_argb(200, 0, 0, 255)); - p.set_style(PaintStyle::Stroke); - p.set_stroke_width(4.0); - p.set_anti_alias(true); - p - }; -} - -pub struct HitOverlay; - -impl HitOverlay { - pub fn draw( - canvas: &Canvas, - hit: Option<&NodeId>, - focus: Option<&NodeId>, - camera: &Camera2D, - cache: &SceneCache, - fonts: &FontRepository, - ) { - // Render hit if present - if let Some(id) = hit { - if let Some(entry) = cache.layers.layers.iter().find(|e| &e.id == id) { - if let Some(bounds) = cache.geometry.get_render_bounds(id) { - let screen_rect = math2::rect::transform(bounds, &camera.view_matrix()); - let rect = Rect::from_xywh( - screen_rect.x, - screen_rect.y, - screen_rect.width, - screen_rect.height, - ); - - let shape = entry.layer.shape(); - let transform = entry.layer.transform(); - let mut path = if let Some(path_entry) = cache.path.borrow().get(id) { - (*path_entry.path).clone() - } else { - match &entry.layer { - crate::painter::layer::PainterPictureLayer::Text(t) => { - if let Some(text_path) = - text_overlay::TextOverlay::text_layer_baseline(cache, t, fonts) - { - text_path - } else { - // Skip rendering if text path is not available - return; - } - } - _ => shape.to_path(), - } - }; - path = path.make_transform(&sk::sk_matrix(transform.matrix)); - path = path.make_transform(&sk::sk_matrix(camera.view_matrix().matrix)); - let _ = path; // transformed path reserved for future path stroking - - // background for hit text - let hit_text_rect = Rect::from_xywh(10.0, 80.0, 300.0, 40.0); - BG_PAINT.with(|bg| { - canvas.draw_rect(hit_text_rect, bg); - }); - - TEXT_PAINT.with(|paint| { - FONT.with(|font| { - canvas.draw_str( - format!("hit: {}", id), - Point::new(24.0, 104.0), - font, - paint, - ); - }); - }); - - STROKE.with(|stroke| { - canvas.draw_rect(rect, stroke); - }); - - // Use the canvas we already have instead of borrowing surface again - stroke_overlay::StrokeOverlay::draw( - canvas, - std::slice::from_ref(id), - camera, - cache, - fonts, - None, - ); - } - } - } - - // Render focus if present (and different from hit) - if let Some(focus_id) = focus { - if hit.map_or(true, |hit_id| focus_id != hit_id) { - if let Some(_focus_layer) = cache.layers.layers.iter().find(|e| &e.id == focus_id) { - if let Some(focus_bounds) = cache.geometry.get_render_bounds(focus_id) { - let focus_screen_rect = - math2::rect::transform(focus_bounds, &camera.view_matrix()); - let focus_rect = Rect::from_xywh( - focus_screen_rect.x, - focus_screen_rect.y, - focus_screen_rect.width, - focus_screen_rect.height, - ); - - FOCUS_STROKE.with(|focus_stroke| { - canvas.draw_rect(focus_rect, focus_stroke); - }); - } - } - } - } - } -} diff --git a/crates/grida-canvas/src/devtools/mod.rs b/crates/grida-canvas/src/devtools/mod.rs index 88054953e5..d4927f02c2 100644 --- a/crates/grida-canvas/src/devtools/mod.rs +++ b/crates/grida-canvas/src/devtools/mod.rs @@ -1,5 +1,4 @@ pub mod fps_overlay; -pub mod hit_overlay; pub mod ruler_overlay; pub mod stats_overlay; pub mod stroke_overlay; diff --git a/crates/grida-canvas/src/devtools/surface_overlay.rs b/crates/grida-canvas/src/devtools/surface_overlay.rs index 780cfa9b91..fb28362925 100644 --- a/crates/grida-canvas/src/devtools/surface_overlay.rs +++ b/crates/grida-canvas/src/devtools/surface_overlay.rs @@ -31,6 +31,12 @@ pub struct SurfaceOverlayConfig { pub show_size_meter: bool, /// Show type labels above root frames and selected nodes. pub show_frame_titles: bool, + /// Show selection handles (resize knobs, rotation zones) and per-node + /// bounding rects on selected shapes. Defaults to `false`. + /// + /// Enabled by the native host (`grida-dev`). The web side keeps this + /// off because the JS editor renders its own overlay handles. + pub show_selection_handles: bool, } impl Default for SurfaceOverlayConfig { @@ -40,6 +46,7 @@ impl Default for SurfaceOverlayConfig { text_baseline_decoration: false, show_size_meter: false, show_frame_titles: false, + show_selection_handles: false, } } } @@ -76,11 +83,18 @@ impl SurfaceOverlay { } } - // Draw selection outlines + // Draw selection outlines. + // + // The selection highlight model is: + // shape outline (the actual path) + axis-aligned bounding rect + // + text baseline decoration (when enabled). + // + // This differs from hover which only shows the shape outline (or + // text baseline for text nodes). let sel_count = surface.selection.len(); if sel_count >= 1 { for id in surface.selection.iter() { - // Selection: always draw bounding rect + // Draw the shape outline (same as hover but at full alpha). Self::draw_node_outline( canvas, id, @@ -91,6 +105,20 @@ impl SurfaceOverlay { false, fonts, ); + // Draw the axis-aligned bounding rect for each selected node. + // This gives the "declarative box" look that shows the layout + // bounds in addition to the actual shape path. + // Only drawn when selection handles are enabled (native). + if config.show_selection_handles { + Self::draw_node_bounding_rect( + canvas, + id, + &view_sk, + cache, + SELECTION_COLOR, + 1.5, + ); + } // Selection: additionally draw text baseline decoration if use_text_baseline { Self::draw_text_baseline(canvas, id, &view_sk, cache, SELECTION_COLOR, fonts); @@ -164,6 +192,47 @@ impl SurfaceOverlay { canvas.draw_path(&path, &paint); } + /// Draw the axis-aligned bounding rect for a single node in screen space. + /// + /// For shapes whose outline *is* their bounding rect (rectangles), this + /// effectively draws a second outline on top — visually identical, so + /// there's no double-stroke artifact. For non-rectangular shapes (ellipses, + /// polygons, paths) the bounding rect provides the "declarative box" frame + /// around the shape. + fn draw_node_bounding_rect( + canvas: &Canvas, + id: &crate::node::schema::NodeId, + view_sk: &Matrix, + cache: &SceneCache, + color: Color, + stroke_width: f32, + ) { + let world_bounds = match cache.geometry.get_world_bounds(id) { + Some(b) => b, + None => return, + }; + + let p1 = view_sk.map_point((world_bounds.x, world_bounds.y)); + let p2 = view_sk.map_point(( + world_bounds.x + world_bounds.width, + world_bounds.y + world_bounds.height, + )); + let screen_rect = skia_safe::Rect::from_ltrb( + p1.x.min(p2.x), + p1.y.min(p2.y), + p1.x.max(p2.x), + p1.y.max(p2.y), + ); + + let mut paint = Paint::default(); + paint.set_color(color); + paint.set_style(PaintStyle::Stroke); + paint.set_stroke_width(stroke_width); + paint.set_anti_alias(true); + + canvas.draw_rect(screen_rect, &paint); + } + /// Draw text baseline decoration for a node (no-op if not a text layer). fn draw_text_baseline( canvas: &Canvas, diff --git a/crates/grida-canvas/src/hittest/hit_tester.rs b/crates/grida-canvas/src/hittest/hit_tester.rs index d08ff9b44a..7e6520e6ad 100644 --- a/crates/grida-canvas/src/hittest/hit_tester.rs +++ b/crates/grida-canvas/src/hittest/hit_tester.rs @@ -342,4 +342,46 @@ impl<'a> HitTester<'a> { } false } + + /// Test whether a canvas-space point lies inside the union bounding + /// rect of the given node IDs. + /// + /// Returns `true` if at least one node has known bounds and the union + /// rect contains the point. Returns `false` when `ids` is empty or + /// none of the IDs have geometry in the cache. + pub fn point_in_selection_bounds(&self, point: Vector2, ids: &[NodeId]) -> bool { + if ids.is_empty() { + return false; + } + let rects: Vec = ids + .iter() + .filter_map(|id| self.cache.geometry.get_world_bounds(id)) + .collect(); + if rects.is_empty() { + return false; + } + let union = rect::union(&rects); + rect::contains_point(&union, point) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cache::scene::SceneCache; + + #[test] + fn point_in_selection_bounds_empty_ids() { + let cache = SceneCache::new(); + let ht = HitTester::new(&cache); + assert!(!ht.point_in_selection_bounds([50.0, 50.0], &[])); + } + + #[test] + fn point_in_selection_bounds_no_geometry() { + // IDs exist but cache has no geometry for them. + let cache = SceneCache::new(); + let ht = HitTester::new(&cache); + assert!(!ht.point_in_selection_bounds([50.0, 50.0], &[1, 2, 3])); + } } diff --git a/crates/grida-canvas/src/node/scene_graph.rs b/crates/grida-canvas/src/node/scene_graph.rs index f397c56cca..6b0bba4699 100644 --- a/crates/grida-canvas/src/node/scene_graph.rs +++ b/crates/grida-canvas/src/node/scene_graph.rs @@ -798,6 +798,20 @@ impl SceneGraph { self.nodes.iter() } + /// Re-sync the compact `geo_data` entry for a node after its schema + /// data was mutated via [`get_node_mut`](Self::get_node_mut). + /// + /// Required because `geo_data` is extracted at insertion time and + /// becomes stale after in-place mutation. The geometry cache reads + /// from `geo_data` during rebuild, so skipping this produces wrong + /// bounds. + pub fn refresh_node_geo_data(&mut self, id: &NodeId) { + if let Some(node) = self.nodes.get(id) { + let geo = extract_geo_data(node); + self.geo_data.insert(*id, geo); + } + } + /// Access the compact, schema-level geometry data map. /// /// This map is populated at construction time and contains only the fields diff --git a/crates/grida-canvas/src/runtime/changes.rs b/crates/grida-canvas/src/runtime/changes.rs index 4f5d7653f2..e7d2a09283 100644 --- a/crates/grida-canvas/src/runtime/changes.rs +++ b/crates/grida-canvas/src/runtime/changes.rs @@ -57,6 +57,14 @@ impl ChangeFlags { /// Layout inputs changed (node resize, auto-layout property edit, etc.). pub const LAYOUT_DIRTY: Self = Self(1 << 7); + /// Node transform changed (position, rotation) without content change. + /// + /// The property tree handles incremental world-transform resolution. + /// Picture cache does NOT need invalidation (pictures are played back + /// with the updated transform). Only the compositor blit destination + /// and spatial index need updating. + pub const NODE_TRANSFORM: Self = Self(1 << 8); + // -- helpers -- pub const fn is_empty(self) -> bool { diff --git a/crates/grida-canvas/src/surface/cursor.rs b/crates/grida-canvas/src/surface/cursor.rs index 03cf4aca56..342084cbc1 100644 --- a/crates/grida-canvas/src/surface/cursor.rs +++ b/crates/grida-canvas/src/surface/cursor.rs @@ -1,3 +1,56 @@ +/// Cardinal and inter-cardinal resize directions. +/// +/// Used both for resize handle anchors and for directional resize cursors. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ResizeDirection { + N, + NE, + E, + SE, + S, + SW, + W, + NW, +} + +impl ResizeDirection { + /// All eight directions in clockwise order starting from north. + pub const ALL: [Self; 8] = [ + Self::N, + Self::NE, + Self::E, + Self::SE, + Self::S, + Self::SW, + Self::W, + Self::NW, + ]; + + /// The four corner directions (diagonal resize handles, visible knobs). + pub const CORNERS: [Self; 4] = [Self::NW, Self::NE, Self::SE, Self::SW]; + + /// The four side (cardinal) directions (invisible edge handles). + pub const SIDES: [Self; 4] = [Self::N, Self::E, Self::S, Self::W]; + + /// Whether this direction is a corner (diagonal). + pub fn is_corner(self) -> bool { + matches!(self, Self::NW | Self::NE | Self::SE | Self::SW) + } +} + +/// Corner positions for rotation handles. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum RotationCorner { + NW, + NE, + SE, + SW, +} + +impl RotationCorner { + pub const ALL: [Self; 4] = [Self::NW, Self::NE, Self::SE, Self::SW]; +} + /// Cursor icon for the surface. /// /// Maps to platform cursor types (e.g. winit `CursorIcon`). @@ -10,4 +63,58 @@ pub enum CursorIcon { Grabbing, Crosshair, Move, + /// Directional resize cursor (maps to nwse-resize, nesw-resize, ns-resize, ew-resize). + Resize(ResizeDirection), + /// Rotation cursor. The corner identifies which rotation handle is active, + /// allowing the host to choose the appropriate cursor orientation. + Rotate(RotationCorner), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn resize_direction_constants() { + assert_eq!(ResizeDirection::ALL.len(), 8); + assert_eq!(ResizeDirection::CORNERS.len(), 4); + assert_eq!(ResizeDirection::SIDES.len(), 4); + // CORNERS + SIDES = ALL + for d in ResizeDirection::CORNERS { + assert!(ResizeDirection::ALL.contains(&d)); + } + for d in ResizeDirection::SIDES { + assert!(ResizeDirection::ALL.contains(&d)); + } + } + + #[test] + fn cursor_default_is_default() { + let c = CursorIcon::default(); + assert_eq!(c, CursorIcon::Default); + } + + #[test] + fn resize_cursors_are_distinct() { + // Opposite directions should map to the same visual cursor + // (ns, ew, nwse, nesw) — 4 groups of 2. + // But the enum values themselves should be distinct. + assert_ne!( + CursorIcon::Resize(ResizeDirection::N), + CursorIcon::Resize(ResizeDirection::S) + ); + assert_ne!( + CursorIcon::Resize(ResizeDirection::E), + CursorIcon::Resize(ResizeDirection::W) + ); + } + + #[test] + fn cursor_variants_not_equal() { + assert_ne!(CursorIcon::Default, CursorIcon::Pointer); + assert_ne!( + CursorIcon::Resize(ResizeDirection::N), + CursorIcon::Rotate(RotationCorner::NW) + ); + } } diff --git a/crates/grida-canvas/src/surface/gesture.rs b/crates/grida-canvas/src/surface/gesture.rs index 830fa16d85..63026aedaa 100644 --- a/crates/grida-canvas/src/surface/gesture.rs +++ b/crates/grida-canvas/src/surface/gesture.rs @@ -1,12 +1,21 @@ +use crate::surface::cursor::{ResizeDirection, RotationCorner}; use math2::vector2::Vector2; /// Active gesture on the surface. /// /// For readonly mode, only `Idle` and `Pan` are used. /// `MarqueeSelect` is included as a read-only operation (rubber-band selection). -#[derive(Debug, Clone, Copy)] +/// +/// Editing gestures (Translate, Resize, Rotate) use an **incremental** +/// model: each pointer-move carries a delta from the previous position. +/// The application layer applies that delta immediately so the user sees +/// real-time feedback. When the gesture ends (pointer-up), there is no +/// final commit — the last incremental move already brought the scene to +/// the correct state. +#[derive(Debug, Clone, Copy, Default)] pub enum SurfaceGesture { /// No active gesture. + #[default] Idle, /// Panning the canvas (middle-mouse drag or space+drag). Pan { anchor_screen: Vector2 }, @@ -15,12 +24,26 @@ pub enum SurfaceGesture { anchor_canvas: Vector2, current_canvas: Vector2, }, -} - -impl Default for SurfaceGesture { - fn default() -> Self { - Self::Idle - } + /// Dragging the current selection to translate it. + Translate { + /// Canvas-space position from the previous pointer event. + /// Updated on every move so the next delta is incremental. + prev_canvas: Vector2, + }, + /// Dragging a resize handle. + Resize { + /// Which resize handle is being dragged. + direction: ResizeDirection, + /// Screen-space position from the previous pointer event. + prev_screen: Vector2, + }, + /// Dragging a rotation handle. + Rotate { + /// Which corner's rotation handle is being dragged. + corner: RotationCorner, + /// Screen-space position from the previous pointer event. + prev_screen: Vector2, + }, } impl SurfaceGesture { diff --git a/crates/grida-canvas/src/surface/mod.rs b/crates/grida-canvas/src/surface/mod.rs index e0d9651b35..177570005c 100644 --- a/crates/grida-canvas/src/surface/mod.rs +++ b/crates/grida-canvas/src/surface/mod.rs @@ -7,7 +7,7 @@ pub mod selection; pub mod state; pub mod ui; -pub use cursor::CursorIcon; +pub use cursor::{CursorIcon, ResizeDirection, RotationCorner}; pub use event::{ImeEvent, Modifiers, PointerButton, SurfaceEvent}; pub use gesture::SurfaceGesture; pub use hover::HoverState; diff --git a/crates/grida-canvas/src/surface/state.rs b/crates/grida-canvas/src/surface/state.rs index dbe72c0c20..1e5081a0e6 100644 --- a/crates/grida-canvas/src/surface/state.rs +++ b/crates/grida-canvas/src/surface/state.rs @@ -1,4 +1,5 @@ use crate::hittest::HitTester; +use crate::node::schema::NodeId; use crate::query::{self, Hierarchy}; use crate::surface::cursor::CursorIcon; use crate::surface::event::{Modifiers, PointerButton, SurfaceEvent}; @@ -8,13 +9,48 @@ use crate::surface::response::SurfaceResponse; use crate::surface::selection::SelectionState; use crate::surface::ui::hit_region::{HitRegions, OverlayAction}; use crate::text_edit::session::ClickTracker; +use math2::vector2::Vector2; + +/// Pending pointer-down state, stored between pointer-down and the next +/// pointer-move or pointer-up. +/// +/// - **Already-selected node**: selection change is *deferred*. If the +/// user drags, the deferred change is cancelled (preserving the multi- +/// selection for translate). If the user clicks (releases without +/// dragging), the deferred change is applied. +/// - **Newly-selected node**: selection was already applied immediately. +/// The anchor is stored so a subsequent drag can start a Translate +/// gesture. No deferred selection change exists. +#[derive(Debug, Clone, Copy)] +struct PendingPointerDown { + /// Canvas-space point of the pointer-down (used as drag anchor). + anchor_canvas: Vector2, + /// If `Some`, the selection change has been *deferred* and will be + /// applied on pointer-up (click) or cancelled on drag. + deferred: Option, +} + +/// A selection operation that was deferred. +#[derive(Debug, Clone, Copy)] +struct DeferredSelectionOp { + /// The node that was clicked. + node_id: NodeId, + /// Whether shift was held (toggle vs reset-to-one). + shift: bool, +} /// Canvas surface interaction state. /// -/// Manages hover, selection, gesture, and cursor state for readonly -/// canvas interactions. The host calls [`dispatch`](Self::dispatch) with +/// Manages hover, selection, gesture, and cursor state for canvas +/// interactions. The host calls [`dispatch`](Self::dispatch) with /// platform-agnostic [`SurfaceEvent`]s and a [`HitTester`] reference; /// the surface never touches the camera or renderer directly. +/// +/// By default the surface operates in **readonly** mode — hover and +/// click-to-select work, but manipulation affordances (translate gesture, +/// deferred selection, move cursor, selection-rect awareness) are +/// disabled. Set [`readonly`](Self::readonly) to `false` to enable the +/// full native editing surface. #[derive(Debug, Clone)] pub struct SurfaceState { pub hover: HoverState, @@ -25,6 +61,20 @@ pub struct SurfaceState { modifiers: Modifiers, /// Multi-click tracker for double-click detection. pub click_tracker: ClickTracker, + + /// When `true` (the default), the surface only supports hover and + /// click-to-select. Translate gestures, deferred selection, the + /// move cursor, and selection-rect hit testing are all disabled. + /// + /// Set to `false` by the native host (`grida-dev`) to enable the + /// full editing surface. The web side keeps the default so these + /// features don't interfere with the JS-driven editor. + pub readonly: bool, + + /// Set between pointer-down and the next pointer-move/pointer-up. + /// Drives deferred selection and translate gesture initiation. + /// Only used when `readonly == false`. + pending_pointer_down: Option, } impl Default for SurfaceState { @@ -36,6 +86,8 @@ impl Default for SurfaceState { cursor: CursorIcon::Default, modifiers: Modifiers::default(), click_tracker: ClickTracker::new(), + readonly: true, + pending_pointer_down: None, } } } @@ -115,7 +167,7 @@ impl SurfaceState { if let Some(action) = ui_hit_regions.hit_test([screen_point[0], screen_point[1]]) { - return self.handle_overlay_action(action, hierarchy); + return self.handle_overlay_action(action, screen_point, hierarchy); } } self.handle_pointer_down(canvas_point, button, hit_tester, hierarchy) @@ -128,7 +180,7 @@ impl SurfaceState { modifiers, } => { self.modifiers = modifiers; - self.handle_pointer_up(canvas_point, button) + self.handle_pointer_up(canvas_point, button, hierarchy) } SurfaceEvent::ModifiersChanged(mods) => { @@ -144,10 +196,11 @@ impl SurfaceState { } } - /// Handle an overlay UI action (e.g. clicking a frame title bar). + /// Handle an overlay UI action (e.g. clicking a frame title bar or handle). fn handle_overlay_action( &mut self, action: &OverlayAction, + screen_point: Vector2, hierarchy: &impl Hierarchy, ) -> SurfaceResponse { let mut response = SurfaceResponse::none(); @@ -162,24 +215,72 @@ impl SurfaceState { response.selection_changed = true; response.needs_redraw = true; } + OverlayAction::ResizeHandle(dir) if !self.readonly => { + self.gesture = SurfaceGesture::Resize { + direction: *dir, + prev_screen: screen_point, + }; + response.needs_redraw = true; + } + OverlayAction::RotateHandle(corner) if !self.readonly => { + self.gesture = SurfaceGesture::Rotate { + corner: *corner, + prev_screen: screen_point, + }; + response.needs_redraw = true; + } + // Readonly mode: handles are not shown, but if hit regions + // somehow still exist, ignore them. + OverlayAction::ResizeHandle(_) | OverlayAction::RotateHandle(_) => {} } response } fn handle_pointer_move( &mut self, - canvas_point: math2::vector2::Vector2, - screen_point: math2::vector2::Vector2, + canvas_point: Vector2, + screen_point: Vector2, hit_tester: &HitTester, ui_hit_regions: &HitRegions, ) -> SurfaceResponse { let mut response = SurfaceResponse::none(); + // ── Check for pending pointer-down → translate promotion ───────── + // Only in editing mode (!readonly). If the gesture is still Idle + // and we have a pending pointer-down, the user is dragging from a + // node. Cancel any deferred selection and start a Translate gesture. + if !self.readonly { + if let Some(pending) = self.pending_pointer_down.take() { + if matches!(self.gesture, SurfaceGesture::Idle) { + // Start translate. Set prev_canvas to the anchor so the + // application layer can compute the first delta as + // (current_point - anchor). Then immediately fall + // through to the Translate match arm which updates + // prev_canvas to canvas_point. + self.gesture = SurfaceGesture::Translate { + prev_canvas: pending.anchor_canvas, + }; + let new_cursor = CursorIcon::Move; + if new_cursor != self.cursor { + self.cursor = new_cursor; + response.cursor_changed = true; + } + // Don't return — fall through to the Translate arm so + // prev_canvas gets updated to canvas_point on the same + // event that promoted the gesture. + } + } + } + match self.gesture { SurfaceGesture::Idle => { // Check overlay UI regions — hover the associated node if let Some(action) = ui_hit_regions.hit_test([screen_point[0], screen_point[1]]) { - let new_cursor = CursorIcon::Pointer; + let new_cursor = match action { + OverlayAction::SelectNode(_) => CursorIcon::Pointer, + OverlayAction::ResizeHandle(dir) => CursorIcon::Resize(*dir), + OverlayAction::RotateHandle(corner) => CursorIcon::Rotate(*corner), + }; if new_cursor != self.cursor { self.cursor = new_cursor; response.cursor_changed = true; @@ -187,11 +288,17 @@ impl SurfaceState { // Set hover to the node referenced by the overlay action let node_id = match action { OverlayAction::SelectNode(id) => Some(*id), + // Handle regions don't change the hovered node. + OverlayAction::ResizeHandle(_) | OverlayAction::RotateHandle(_) => None, }; - let hover_changed = self.hover.set(node_id, HoverSource::HitTest); - if hover_changed { - response.hover_changed = true; - response.needs_redraw = true; + // Only update hover for SelectNode actions; handle hover + // shouldn't clear the existing hover target. + if matches!(action, OverlayAction::SelectNode(_)) { + let hover_changed = self.hover.set(node_id, HoverSource::HitTest); + if hover_changed { + response.hover_changed = true; + response.needs_redraw = true; + } } return response; } @@ -204,13 +311,36 @@ impl SurfaceState { response.needs_redraw = true; } - // Update cursor based on hover - let new_cursor = CursorIcon::Default; + // In editing mode, show Move cursor when hovering inside + // the selection bounding rect. In readonly mode, always + // use the Default cursor. + let new_cursor = if !self.readonly { + let over_selected = hit.is_some_and(|id| self.selection.contains(&id)); + let inside_bounds = !self.selection.is_empty() + && hit_tester + .point_in_selection_bounds(canvas_point, self.selection.as_slice()); + if over_selected || inside_bounds { + CursorIcon::Move + } else { + CursorIcon::Default + } + } else { + CursorIcon::Default + }; if new_cursor != self.cursor { self.cursor = new_cursor; response.cursor_changed = true; } } + SurfaceGesture::Translate { .. } => { + // Update prev_canvas to current position. The application + // layer computes the delta by comparing the gesture before + // and after dispatch. + self.gesture = SurfaceGesture::Translate { + prev_canvas: canvas_point, + }; + response.needs_redraw = true; + } SurfaceGesture::MarqueeSelect { anchor_canvas, current_canvas: _, @@ -230,6 +360,20 @@ impl SurfaceState { response.selection_changed = true; response.needs_redraw = true; } + SurfaceGesture::Resize { direction, .. } => { + self.gesture = SurfaceGesture::Resize { + direction, + prev_screen: screen_point, + }; + response.needs_redraw = true; + } + SurfaceGesture::Rotate { corner, .. } => { + self.gesture = SurfaceGesture::Rotate { + corner, + prev_screen: screen_point, + }; + response.needs_redraw = true; + } SurfaceGesture::Pan { .. } => { // Pan is handled by the application/camera, not the surface } @@ -240,7 +384,7 @@ impl SurfaceState { fn handle_pointer_down( &mut self, - canvas_point: math2::vector2::Vector2, + canvas_point: Vector2, button: PointerButton, hit_tester: &HitTester, hierarchy: &impl Hierarchy, @@ -258,41 +402,106 @@ impl SurfaceState { let hit = hit_tester.hit_first(canvas_point); - match hit { - Some(id) => { - // Clicked on a node - if self.modifiers.shift { - self.selection.toggle(id); - self.prune_selection(hierarchy); - } else { - self.selection.select_one(id); - } - response.selection_changed = true; - response.needs_redraw = true; - - // Report double-click for text editing activation. - if click_count >= 2 { - response.double_clicked_node = Some(id); + if self.readonly { + // ── Readonly path: immediate selection, no translate ────── + self.pending_pointer_down = None; + match hit { + Some(id) => { + if self.modifiers.shift { + self.selection.toggle(id); + self.prune_selection(hierarchy); + } else { + self.selection.select_one(id); + } + response.selection_changed = true; + response.needs_redraw = true; + if click_count >= 2 { + response.double_clicked_node = Some(id); + } } - } - None => { - if self.modifiers.shift { - // Shift+click on empty space: keep selection, start marquee - // (marquee will union with existing selection — future enhancement) - } else { - // Click on empty space: clear selection, begin marquee - if !self.selection.is_empty() { + None => { + if self.modifiers.shift { + // keep selection + } else if !self.selection.is_empty() { self.selection.clear(); response.selection_changed = true; response.needs_redraw = true; } + self.gesture = SurfaceGesture::MarqueeSelect { + anchor_canvas: canvas_point, + current_canvas: canvas_point, + }; } + } + } else { + // ── Editing path: deferred selection + translate ────────── + match hit { + Some(id) => { + let already_selected = self.selection.contains(&id); + + if already_selected { + // Node is already selected — **defer** the selection + // change so that dragging preserves the full + // selection for translate. + self.pending_pointer_down = Some(PendingPointerDown { + anchor_canvas: canvas_point, + deferred: Some(DeferredSelectionOp { + node_id: id, + shift: self.modifiers.shift, + }), + }); + } else { + // Unselected node — immediate selection change. + if self.modifiers.shift { + self.selection.toggle(id); + self.prune_selection(hierarchy); + } else { + self.selection.select_one(id); + } + response.selection_changed = true; + response.needs_redraw = true; - // Begin marquee gesture - self.gesture = SurfaceGesture::MarqueeSelect { - anchor_canvas: canvas_point, - current_canvas: canvas_point, - }; + // Store the anchor for a potential translate drag. + self.pending_pointer_down = Some(PendingPointerDown { + anchor_canvas: canvas_point, + deferred: None, + }); + } + + if click_count >= 2 { + response.double_clicked_node = Some(id); + } + } + None => { + // No node hit. Check if inside the selection bounding + // rect — if so, set up for translate (the user can + // drag any part of the selection box). + if !self.selection.is_empty() + && hit_tester + .point_in_selection_bounds(canvas_point, self.selection.as_slice()) + { + self.pending_pointer_down = Some(PendingPointerDown { + anchor_canvas: canvas_point, + deferred: None, + }); + } else { + // Truly empty space. + self.pending_pointer_down = None; + + if self.modifiers.shift { + // keep selection + } else if !self.selection.is_empty() { + self.selection.clear(); + response.selection_changed = true; + response.needs_redraw = true; + } + + self.gesture = SurfaceGesture::MarqueeSelect { + anchor_canvas: canvas_point, + current_canvas: canvas_point, + }; + } + } } } @@ -301,8 +510,9 @@ impl SurfaceState { fn handle_pointer_up( &mut self, - _canvas_point: math2::vector2::Vector2, + _canvas_point: Vector2, button: PointerButton, + hierarchy: &impl Hierarchy, ) -> SurfaceResponse { let mut response = SurfaceResponse::none(); @@ -310,9 +520,37 @@ impl SurfaceState { return response; } - if matches!(self.gesture, SurfaceGesture::MarqueeSelect { .. }) { - self.gesture = SurfaceGesture::Idle; - response.needs_redraw = true; + // ── Apply deferred selection on click (no drag occurred) ───────── + // Only relevant in editing mode; readonly never sets pending. + if let Some(pending) = self.pending_pointer_down.take() { + if let Some(deferred) = pending.deferred { + if deferred.shift { + self.selection.toggle(deferred.node_id); + self.prune_selection(hierarchy); + } else { + self.selection.select_one(deferred.node_id); + } + response.selection_changed = true; + response.needs_redraw = true; + } + } + + // ── End active gestures ────────────────────────────────────────── + match self.gesture { + SurfaceGesture::MarqueeSelect { .. } + | SurfaceGesture::Translate { .. } + | SurfaceGesture::Resize { .. } + | SurfaceGesture::Rotate { .. } => { + self.gesture = SurfaceGesture::Idle; + response.needs_redraw = true; + } + _ => {} + } + + // Reset cursor back to default after gesture ends. + if self.cursor == CursorIcon::Move && matches!(self.gesture, SurfaceGesture::Idle) { + self.cursor = CursorIcon::Default; + response.cursor_changed = true; } response @@ -320,7 +558,7 @@ impl SurfaceState { } /// Compute a normalized rectangle from two corner points. -fn marquee_rect(a: math2::vector2::Vector2, b: math2::vector2::Vector2) -> math2::rect::Rectangle { +fn marquee_rect(a: Vector2, b: Vector2) -> math2::rect::Rectangle { let x = a[0].min(b[0]); let y = a[1].min(b[1]); let w = (a[0] - b[0]).abs(); @@ -332,3 +570,6 @@ fn marquee_rect(a: math2::vector2::Vector2, b: math2::vector2::Vector2) -> math2 height: h, } } + +// Unit tests for SurfaceState live in `tests/surface_interaction.rs` +// as integration tests with real scenes and hit testers. diff --git a/crates/grida-canvas/src/surface/ui/handles.rs b/crates/grida-canvas/src/surface/ui/handles.rs new file mode 100644 index 0000000000..8affb6389d --- /dev/null +++ b/crates/grida-canvas/src/surface/ui/handles.rs @@ -0,0 +1,496 @@ +//! Selection handles — resize knobs, side edges, and rotation corners. +//! +//! This module owns: +//! 1. **Geometry computation** — [`SelectionHandles`] computes screen-space +//! rects for 8 resize handles and 4 rotation handles from a screen-space +//! bounding box. +//! 2. **Hit testing** — [`SelectionHandles::hit_test`] checks a screen-space +//! point against all handles (rotation first, then resize). +//! 3. **Rendering** — [`SelectionHandles::draw`] paints visible corner knobs +//! and (optional) rotation areas onto a Skia canvas. +//! +//! The module intentionally does **not** dispatch editing commands — it only +//! reports *which* handle was hit and what cursor to show. The host (or a +//! future gesture layer) decides what to do with that information. + +use crate::surface::cursor::{ResizeDirection, RotationCorner}; +use skia_safe::{Canvas, Color, Contains, Paint, PaintStyle, Rect}; + +// ── Layout constants (logical pixels, scaled by `dpr` at draw time) ────────── + +/// Size of the visible corner resize knob (square, in logical px). +const CORNER_KNOB_SIZE: f32 = 8.0; +/// Minimum hit-target size for any handle (ensures usability on HiDPI). +const MIN_HIT_SIZE: f32 = 16.0; +/// Thickness of the invisible side (edge) resize handles. +const SIDE_THICKNESS: f32 = 8.0; +/// Offset of rotation handles from the selection corner (outward, logical px). +const ROTATION_OFFSET: f32 = 10.0; +/// Size of the rotation hit area (square, logical px). +const ROTATION_HIT_SIZE: f32 = 16.0; + +/// Minimum screen-space dimension (width or height) of the selection box +/// before resize handles become visible. Matches the web editor's +/// `MIN_NODE_OVERLAY_RESIZE_HANDLES_VISIBLE_UI_SIZE` (12 px). +const MIN_HANDLES_VISIBLE_SIZE: f32 = 12.0; + +/// Accent color for knob outlines (matches selection overlay). +const ACCENT_COLOR: Color = Color::from_argb(255, 0, 120, 255); + +// ── Types ──────────────────────────────────────────────────────────────────── + +/// Which handle was hit. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HandleHit { + Resize(ResizeDirection), + Rotate(RotationCorner), +} + +/// A single handle's screen-space hit rect. +#[derive(Debug, Clone, Copy)] +pub struct HandleRect { + pub hit: HandleHit, + pub screen_rect: Rect, +} + +/// Precomputed screen-space handle rects for a single selection bounding box. +/// +/// Created once per frame (or per selection change) and reused for both +/// drawing and hit testing. +#[derive(Debug, Clone)] +pub struct SelectionHandles { + /// 8 resize handle rects (4 corners + 4 sides). + pub resize: [HandleRect; 8], + /// 4 rotation handle rects (one per corner, placed outside the bounds). + pub rotation: [HandleRect; 4], + /// Whether the handles should be visually drawn. + /// `false` when the selection is too small in screen space. + pub visible: bool, + /// DPR-scaled corner knob half-size (for drawing the white square). + knob_half: f32, +} + +impl SelectionHandles { + /// Compute handle positions from an axis-aligned screen-space bounding + /// rect. All coordinates are in physical pixels (already DPR-scaled). + /// + /// `dpr` is used to scale logical constants to physical pixels. + pub fn from_screen_rect(screen_rect: Rect, dpr: f32) -> Self { + let l = screen_rect.left; + let t = screen_rect.top; + let r = screen_rect.right; + let b = screen_rect.bottom; + let w = r - l; + let h = b - t; + + let knob = CORNER_KNOB_SIZE * dpr; + let knob_half = knob * 0.5; + let hit_half = (MIN_HIT_SIZE * dpr) * 0.5; + let side_thick = SIDE_THICKNESS * dpr; + let side_half = side_thick * 0.5; + let rot_off = ROTATION_OFFSET * dpr; + let rot_size = ROTATION_HIT_SIZE * dpr; + let rot_half = rot_size * 0.5; + + let visible = w >= MIN_HANDLES_VISIBLE_SIZE * dpr && h >= MIN_HANDLES_VISIBLE_SIZE * dpr; + + // Helper: centered rect + let centered = |cx: f32, cy: f32, half: f32| -> Rect { + Rect::from_ltrb(cx - half, cy - half, cx + half, cy + half) + }; + + // ── Resize handles ─────────────────────────────────────────────── + // Corners: centered at each corner, hit area = max(knob, MIN_HIT_SIZE). + let resize_nw = HandleRect { + hit: HandleHit::Resize(ResizeDirection::NW), + screen_rect: centered(l, t, hit_half), + }; + let resize_ne = HandleRect { + hit: HandleHit::Resize(ResizeDirection::NE), + screen_rect: centered(r, t, hit_half), + }; + let resize_se = HandleRect { + hit: HandleHit::Resize(ResizeDirection::SE), + screen_rect: centered(r, b, hit_half), + }; + let resize_sw = HandleRect { + hit: HandleHit::Resize(ResizeDirection::SW), + screen_rect: centered(l, b, hit_half), + }; + + // Sides: span the full edge length, side_thick wide, centered on the edge. + let resize_n = HandleRect { + hit: HandleHit::Resize(ResizeDirection::N), + screen_rect: Rect::from_ltrb(l + hit_half, t - side_half, r - hit_half, t + side_half), + }; + let resize_e = HandleRect { + hit: HandleHit::Resize(ResizeDirection::E), + screen_rect: Rect::from_ltrb(r - side_half, t + hit_half, r + side_half, b - hit_half), + }; + let resize_s = HandleRect { + hit: HandleHit::Resize(ResizeDirection::S), + screen_rect: Rect::from_ltrb(l + hit_half, b - side_half, r - hit_half, b + side_half), + }; + let resize_w = HandleRect { + hit: HandleHit::Resize(ResizeDirection::W), + screen_rect: Rect::from_ltrb(l - side_half, t + hit_half, l + side_half, b - hit_half), + }; + + // ── Rotation handles ───────────────────────────────────────────── + // Placed outside each corner, offset outward by `rot_off`. + let rotation_nw = HandleRect { + hit: HandleHit::Rotate(RotationCorner::NW), + screen_rect: centered(l - rot_off, t - rot_off, rot_half), + }; + let rotation_ne = HandleRect { + hit: HandleHit::Rotate(RotationCorner::NE), + screen_rect: centered(r + rot_off, t - rot_off, rot_half), + }; + let rotation_se = HandleRect { + hit: HandleHit::Rotate(RotationCorner::SE), + screen_rect: centered(r + rot_off, b + rot_off, rot_half), + }; + let rotation_sw = HandleRect { + hit: HandleHit::Rotate(RotationCorner::SW), + screen_rect: centered(l - rot_off, b + rot_off, rot_half), + }; + + Self { + resize: [ + resize_nw, resize_ne, resize_se, resize_sw, resize_n, resize_e, resize_s, resize_w, + ], + rotation: [rotation_nw, rotation_ne, rotation_se, rotation_sw], + visible, + knob_half, + } + } + + /// Hit-test a screen-space point against all handles. + /// + /// Returns the topmost hit. Priority order: + /// 1. Rotation handles (outermost) + /// 2. Corner resize handles + /// 3. Side resize handles + /// + /// This matches the web editor's z-index priority for large selections. + pub fn hit_test(&self, screen_point: [f32; 2]) -> Option { + let pt = skia_safe::Point::new(screen_point[0], screen_point[1]); + + // Rotation handles first (outside the bounds, highest priority). + for h in &self.rotation { + if h.screen_rect.contains(pt) { + return Some(h.hit); + } + } + // Corner resize handles (indices 0..4). + for h in &self.resize[..4] { + if h.screen_rect.contains(pt) { + return Some(h.hit); + } + } + // Side resize handles (indices 4..8). + for h in &self.resize[4..] { + if h.screen_rect.contains(pt) { + return Some(h.hit); + } + } + None + } + + /// Draw the visible parts of the handles onto the canvas. + /// + /// Only the 4 corner resize knobs are drawn (white filled squares with + /// an accent-colored border). Side handles and rotation handles are + /// invisible interaction zones — they are hit-tested but not rendered. + pub fn draw(&self, canvas: &Canvas) { + if !self.visible { + return; + } + + let mut fill = Paint::default(); + fill.set_color(Color::WHITE); + fill.set_style(PaintStyle::Fill); + fill.set_anti_alias(true); + + let mut stroke = Paint::default(); + stroke.set_color(ACCENT_COLOR); + stroke.set_style(PaintStyle::Stroke); + stroke.set_stroke_width(1.0); + stroke.set_anti_alias(true); + + // Draw the 4 corner knobs (first 4 entries in `resize`). + for handle in &self.resize[..4] { + // The visible knob is smaller than the hit rect. + let c = handle.screen_rect.center(); + let knob_rect = Rect::from_xywh( + c.x - self.knob_half, + c.y - self.knob_half, + self.knob_half * 2.0, + self.knob_half * 2.0, + ); + canvas.draw_rect(knob_rect, &fill); + canvas.draw_rect(knob_rect, &stroke); + } + } +} + +// ── Tests ──────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + fn make_handles(x: f32, y: f32, w: f32, h: f32, dpr: f32) -> SelectionHandles { + let rect = Rect::from_xywh(x, y, w, h); + SelectionHandles::from_screen_rect(rect, dpr) + } + + #[test] + fn corner_knobs_centered_on_corners() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // NW corner should be centered at (100, 100) + let nw = &h.resize[0]; + let center = nw.screen_rect.center(); + assert!((center.x - 100.0).abs() < 0.01); + assert!((center.y - 100.0).abs() < 0.01); + // SE corner should be centered at (300, 300) + let se = &h.resize[2]; + let center = se.screen_rect.center(); + assert!((center.x - 300.0).abs() < 0.01); + assert!((center.y - 300.0).abs() < 0.01); + } + + #[test] + fn rotation_handles_outside_bounds() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // NW rotation should be to the top-left of (100, 100) + let nw_rot = &h.rotation[0]; + let center = nw_rot.screen_rect.center(); + assert!( + center.x < 100.0, + "rotation NW center.x should be < 100, got {}", + center.x + ); + assert!( + center.y < 100.0, + "rotation NW center.y should be < 100, got {}", + center.y + ); + // SE rotation should be to the bottom-right of (300, 300) + let se_rot = &h.rotation[2]; + let center = se_rot.screen_rect.center(); + assert!( + center.x > 300.0, + "rotation SE center.x should be > 300, got {}", + center.x + ); + assert!( + center.y > 300.0, + "rotation SE center.y should be > 300, got {}", + center.y + ); + } + + #[test] + fn hit_test_rotation_has_priority_over_resize() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // The rotation NW handle is at ~(90, 90). The resize NW handle is at ~(100, 100). + // A point near the rotation handle should hit rotation, not resize. + let rot_center = h.rotation[0].screen_rect.center(); + let hit = h.hit_test([rot_center.x, rot_center.y]); + assert_eq!(hit, Some(HandleHit::Rotate(RotationCorner::NW))); + } + + #[test] + fn hit_test_resize_corner() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // Exactly at the NW corner + let hit = h.hit_test([100.0, 100.0]); + assert_eq!(hit, Some(HandleHit::Resize(ResizeDirection::NW))); + } + + #[test] + fn hit_test_resize_side() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // Middle of the top edge + let hit = h.hit_test([200.0, 100.0]); + assert_eq!(hit, Some(HandleHit::Resize(ResizeDirection::N))); + } + + #[test] + fn hit_test_miss() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // Center of the selection — should miss all handles + let hit = h.hit_test([200.0, 200.0]); + assert_eq!(hit, None); + } + + #[test] + fn handles_not_visible_when_too_small() { + let h = make_handles(100.0, 100.0, 5.0, 5.0, 1.0); + assert!(!h.visible); + } + + #[test] + fn handles_visible_when_large_enough() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + assert!(h.visible); + } + + #[test] + fn dpr_scaling() { + let h1 = make_handles(0.0, 0.0, 100.0, 100.0, 1.0); + let h2 = make_handles(0.0, 0.0, 100.0, 100.0, 2.0); + // The knob visual size should double with 2x DPR. + assert!((h2.knob_half - h1.knob_half * 2.0).abs() < 0.01); + } + + #[test] + fn side_handles_span_between_corners() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // The N side handle should span from (100 + hit_half) to (300 - hit_half) + let n = &h.resize[4]; + assert!(matches!(n.hit, HandleHit::Resize(ResizeDirection::N))); + // Its left should be > 100 (offset by corner hit area) + assert!(n.screen_rect.left > 100.0); + // Its right should be < 300 + assert!(n.screen_rect.right < 300.0); + } + + #[test] + fn all_eight_resize_directions_present() { + let h = make_handles(0.0, 0.0, 200.0, 200.0, 1.0); + let dirs: Vec<_> = h + .resize + .iter() + .map(|hr| match hr.hit { + HandleHit::Resize(d) => d, + _ => panic!("expected resize"), + }) + .collect(); + assert!(dirs.contains(&ResizeDirection::NW)); + assert!(dirs.contains(&ResizeDirection::NE)); + assert!(dirs.contains(&ResizeDirection::SE)); + assert!(dirs.contains(&ResizeDirection::SW)); + assert!(dirs.contains(&ResizeDirection::N)); + assert!(dirs.contains(&ResizeDirection::E)); + assert!(dirs.contains(&ResizeDirection::S)); + assert!(dirs.contains(&ResizeDirection::W)); + } + + #[test] + fn all_four_rotation_corners_present() { + let h = make_handles(0.0, 0.0, 200.0, 200.0, 1.0); + let corners: Vec<_> = h + .rotation + .iter() + .map(|hr| match hr.hit { + HandleHit::Rotate(c) => c, + _ => panic!("expected rotation"), + }) + .collect(); + assert!(corners.contains(&RotationCorner::NW)); + assert!(corners.contains(&RotationCorner::NE)); + assert!(corners.contains(&RotationCorner::SE)); + assert!(corners.contains(&RotationCorner::SW)); + } + + #[test] + fn hit_test_all_four_corners() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // NE corner at (300, 100) + assert_eq!( + h.hit_test([300.0, 100.0]), + Some(HandleHit::Resize(ResizeDirection::NE)) + ); + // SE corner at (300, 300) + assert_eq!( + h.hit_test([300.0, 300.0]), + Some(HandleHit::Resize(ResizeDirection::SE)) + ); + // SW corner at (100, 300) + assert_eq!( + h.hit_test([100.0, 300.0]), + Some(HandleHit::Resize(ResizeDirection::SW)) + ); + } + + #[test] + fn hit_test_all_four_sides() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // Middle of east edge + assert_eq!( + h.hit_test([300.0, 200.0]), + Some(HandleHit::Resize(ResizeDirection::E)) + ); + // Middle of south edge + assert_eq!( + h.hit_test([200.0, 300.0]), + Some(HandleHit::Resize(ResizeDirection::S)) + ); + // Middle of west edge + assert_eq!( + h.hit_test([100.0, 200.0]), + Some(HandleHit::Resize(ResizeDirection::W)) + ); + } + + #[test] + fn hit_test_all_rotation_corners() { + let h = make_handles(100.0, 100.0, 200.0, 200.0, 1.0); + // NE rotation at ~(310, 90) + let ne_center = h.rotation[1].screen_rect.center(); + assert_eq!( + h.hit_test([ne_center.x, ne_center.y]), + Some(HandleHit::Rotate(RotationCorner::NE)) + ); + // SE rotation at ~(310, 310) + let se_center = h.rotation[2].screen_rect.center(); + assert_eq!( + h.hit_test([se_center.x, se_center.y]), + Some(HandleHit::Rotate(RotationCorner::SE)) + ); + // SW rotation at ~(90, 310) + let sw_center = h.rotation[3].screen_rect.center(); + assert_eq!( + h.hit_test([sw_center.x, sw_center.y]), + Some(HandleHit::Rotate(RotationCorner::SW)) + ); + } + + #[test] + fn resize_direction_classification() { + assert!(ResizeDirection::NW.is_corner()); + assert!(ResizeDirection::NE.is_corner()); + assert!(ResizeDirection::SE.is_corner()); + assert!(ResizeDirection::SW.is_corner()); + assert!(!ResizeDirection::N.is_corner()); + assert!(!ResizeDirection::E.is_corner()); + assert!(!ResizeDirection::S.is_corner()); + assert!(!ResizeDirection::W.is_corner()); + } + + #[test] + fn handles_at_origin() { + let h = make_handles(0.0, 0.0, 200.0, 200.0, 1.0); + // NW corner should be at origin + let nw = &h.resize[0]; + let center = nw.screen_rect.center(); + assert!((center.x).abs() < 0.01); + assert!((center.y).abs() < 0.01); + } + + #[test] + fn handles_with_high_dpr() { + let h = make_handles(0.0, 0.0, 400.0, 400.0, 3.0); + // Even at 3x DPR, structure should be valid + assert!(h.visible); + assert_eq!(h.resize.len(), 8); + assert_eq!(h.rotation.len(), 4); + // Hit test should still work + assert_eq!( + h.hit_test([0.0, 0.0]), + Some(HandleHit::Resize(ResizeDirection::NW)) + ); + } +} diff --git a/crates/grida-canvas/src/surface/ui/hit_region.rs b/crates/grida-canvas/src/surface/ui/hit_region.rs index 491d90b653..4601ecfc18 100644 --- a/crates/grida-canvas/src/surface/ui/hit_region.rs +++ b/crates/grida-canvas/src/surface/ui/hit_region.rs @@ -1,4 +1,5 @@ use crate::node::schema::NodeId; +use crate::surface::cursor::{ResizeDirection, RotationCorner}; use skia_safe::{Contains, Rect}; /// Action to perform when an overlay hit region is activated. @@ -6,6 +7,12 @@ use skia_safe::{Contains, Rect}; pub enum OverlayAction { /// Select the given node (replaces current selection, or toggles with shift). SelectNode(NodeId), + /// Pointer is over a resize handle. The surface should show the + /// appropriate directional cursor but should **not** begin a gesture + /// — that will be added later. + ResizeHandle(ResizeDirection), + /// Pointer is over a rotation handle. + RotateHandle(RotationCorner), } /// A screen-space axis-aligned hit region for overlay UI elements. @@ -79,15 +86,11 @@ mod tests { // Point in overlap: should hit node 2 (last pushed) let action = regions.hit_test([75.0, 75.0]).unwrap(); - match action { - OverlayAction::SelectNode(id) => assert_eq!(*id, 2), - } + assert!(matches!(action, OverlayAction::SelectNode(2))); // Point only in bottom region let action = regions.hit_test([25.0, 25.0]).unwrap(); - match action { - OverlayAction::SelectNode(id) => assert_eq!(*id, 1), - } + assert!(matches!(action, OverlayAction::SelectNode(1))); // Point outside all assert!(regions.hit_test([200.0, 200.0]).is_none()); diff --git a/crates/grida-canvas/src/surface/ui/mod.rs b/crates/grida-canvas/src/surface/ui/mod.rs index fdb3949476..b8653ee9fc 100644 --- a/crates/grida-canvas/src/surface/ui/mod.rs +++ b/crates/grida-canvas/src/surface/ui/mod.rs @@ -1,5 +1,7 @@ +pub mod handles; pub mod hit_region; pub mod render; +pub use handles::{HandleHit, SelectionHandles}; pub use hit_region::{HitRegion, HitRegions, OverlayAction}; pub use render::SurfaceUI; diff --git a/crates/grida-canvas/src/surface/ui/render.rs b/crates/grida-canvas/src/surface/ui/render.rs index e31ef795ef..f9b5289acb 100644 --- a/crates/grida-canvas/src/surface/ui/render.rs +++ b/crates/grida-canvas/src/surface/ui/render.rs @@ -6,6 +6,7 @@ use crate::node::schema::{Node, NodeId}; use crate::runtime::camera::Camera2D; use crate::runtime::font_repository::FontRepository; use crate::surface::state::SurfaceState; +use crate::surface::ui::handles::{HandleHit, SelectionHandles}; use crate::surface::ui::hit_region::{HitRegion, HitRegions, OverlayAction}; use skia_safe::textlayout; use skia_safe::{Canvas, Color, Font, Paint, PaintStyle, Point, RRect, Rect}; @@ -169,6 +170,75 @@ impl SurfaceUI { if config.show_size_meter && !surface.selection.is_empty() { Self::draw_size_meter(canvas, surface, camera, cache, dpr); } + + // Selection handles (resize knobs + rotation zones). + // Only shown when enabled (native), there is a selection, and no + // active gesture. + if config.show_selection_handles + && !surface.selection.is_empty() + && !surface.gesture.is_active() + { + Self::draw_selection_handles(canvas, surface, camera, cache, hit_regions, dpr); + } + } + + /// Compute the screen-space bounding rect for the current selection, + /// draw handles, and register their hit regions. + fn draw_selection_handles( + canvas: &Canvas, + surface: &SurfaceState, + camera: &Camera2D, + cache: &SceneCache, + hit_regions: &mut HitRegions, + dpr: f32, + ) { + // Compute union of all selected nodes' world bounds. + let rects: Vec = surface + .selection + .iter() + .filter_map(|id| cache.geometry.get_world_bounds(id)) + .collect(); + + if rects.is_empty() { + return; + } + + let world_rect = math2::rect::union(&rects); + let view = camera.view_matrix(); + + // Transform world-space bounds to screen-space. + let tl = math2::vector2::transform([world_rect.x, world_rect.y], &view); + let br = math2::vector2::transform( + [ + world_rect.x + world_rect.width, + world_rect.y + world_rect.height, + ], + &view, + ); + let screen_rect = Rect::from_ltrb( + tl[0].min(br[0]), + tl[1].min(br[1]), + tl[0].max(br[0]), + tl[1].max(br[1]), + ); + + let handles = SelectionHandles::from_screen_rect(screen_rect, dpr); + + // Draw visible parts (corner knobs). + handles.draw(canvas); + + // Register hit regions for all handles. These are pushed *after* + // frame title regions so they win in the front-to-back hit test. + for hr in handles.resize.iter().chain(handles.rotation.iter()) { + let action = match hr.hit { + HandleHit::Resize(dir) => OverlayAction::ResizeHandle(dir), + HandleHit::Rotate(corner) => OverlayAction::RotateHandle(corner), + }; + hit_regions.push(HitRegion { + screen_rect: hr.screen_rect, + action, + }); + } } /// Draw a dimension label pill below the selection bounding box. diff --git a/crates/grida-canvas/src/window/application.rs b/crates/grida-canvas/src/window/application.rs index ea2f8ad59b..7de297a56f 100644 --- a/crates/grida-canvas/src/window/application.rs +++ b/crates/grida-canvas/src/window/application.rs @@ -1,8 +1,6 @@ use crate::cg::color::CGColor; use crate::cg::types::{Paint, TextAlignVertical}; -use crate::devtools::{ - fps_overlay, hit_overlay, ruler_overlay, stats_overlay, stroke_overlay, surface_overlay, -}; +use crate::devtools::{fps_overlay, ruler_overlay, stats_overlay, stroke_overlay, surface_overlay}; use crate::dummy; use crate::export::{export_node_as, ExportAs, Exported}; use crate::io::io_grida::{self, JSONFlattenResult}; @@ -209,22 +207,20 @@ pub struct UnknownTargetApplication { pub(crate) state: super::state::AnySurfaceState, pub(crate) input: super::input::InputState, pub(crate) document_json: Option, - pub(crate) hit_test_result: Option, - pub(crate) hit_test_last: std::time::Instant, - pub(crate) hit_test_interval: std::time::Duration, + #[cfg(not(target_arch = "wasm32"))] pub(crate) image_rx: mpsc::UnboundedReceiver, #[cfg(not(target_arch = "wasm32"))] pub(crate) font_rx: mpsc::UnboundedReceiver, pub(crate) last_frame_time: std::time::Instant, pub(crate) last_stats: Option, - pub(crate) devtools_selection: Option, + pub(crate) highlight_strokes: Vec, pub(crate) highlight_stroke_style: Option, pub(crate) devtools_rendering_show_fps: bool, pub(crate) devtools_rendering_show_tiles: bool, pub(crate) devtools_rendering_show_stats: bool, - pub(crate) devtools_rendering_show_hit_overlay: bool, + pub(crate) devtools_rendering_show_ruler: bool, /// Unified frame lifecycle controller. frame_loop: FrameLoop, @@ -356,9 +352,8 @@ impl ApplicationApi for UnknownTargetApplication { return true; } ApplicationCommand::TryCopyAsPNG => { - if let Some(internal_id) = self.devtools_selection.as_ref() { - let internal_id = internal_id.clone(); - // Convert internal ID to user ID for API call + // Export the first selected node as PNG. + if let Some(&internal_id) = self.surface.selection.as_slice().first() { if let Some(user_id) = self.internal_id_to_user(internal_id) { let exported = self.export_node_as(&user_id, ExportAs::png()); if let Some(exported) = exported { @@ -443,7 +438,7 @@ impl ApplicationApi for UnknownTargetApplication { self.devtools_rendering_show_fps = self.debug; self.devtools_rendering_show_tiles = self.debug; self.devtools_rendering_show_stats = self.debug; - self.devtools_rendering_show_hit_overlay = self.debug; + self.devtools_rendering_show_ruler = self.debug; } @@ -657,8 +652,8 @@ impl ApplicationApi for UnknownTargetApplication { self.devtools_rendering_show_stats = show; } - fn devtools_rendering_set_show_hit_testing(&mut self, show: bool) { - self.devtools_rendering_show_hit_overlay = show; + fn devtools_rendering_set_show_hit_testing(&mut self, _show: bool) { + // Legacy hit overlay removed — surface overlay handles hover feedback. } fn devtools_rendering_set_show_ruler(&mut self, show: bool) { @@ -810,14 +805,6 @@ impl UnknownTargetApplication { self.input.cursor } - pub fn perform_hit_test_host(&mut self) { - self.perform_hit_test(); - } - - pub fn capture_hit_test_selection(&mut self) { - self.devtools_selection = self.hit_test_result.clone(); - } - /// Dispatch a surface event (hover, select, gesture) and return what changed. /// /// The caller provides screen-space coordinates; this method handles the @@ -826,8 +813,6 @@ impl UnknownTargetApplication { &mut self, event: crate::surface::SurfaceEvent, ) -> crate::surface::SurfaceResponse { - // Build hit tester and hierarchy from renderer fields without - // borrowing `self` as a whole. let (hit_tester, response) = if let Some(scene) = self.renderer.scene.as_ref() { let ht = crate::hittest::HitTester::with_graph(self.renderer.get_cache(), &scene.graph); let r = self @@ -842,17 +827,98 @@ impl UnknownTargetApplication { (ht, r) }; drop(hit_tester); + if response.needs_redraw { self.queue(); } response } + // ── Query API ───────────────────────────────────────────────────── + // + // High-level read-only queries for the host / editor layer. + // These hide cache and hit-tester internals behind the UTA boundary. + + /// Hit-test a canvas-space point against the scene content. + /// Returns the topmost node ID at that point, or `None`. + pub fn hit_test_point(&self, canvas_point: [f32; 2]) -> Option { + if let Some(scene) = self.renderer.scene.as_ref() { + let ht = crate::hittest::HitTester::with_graph(self.renderer.get_cache(), &scene.graph); + ht.hit_first(canvas_point) + } else { + None + } + } + + /// Hit-test a canvas-space rectangle (marquee) against the scene. + /// Returns the topmost ancestors that intersect the rect. + pub fn hit_test_rect(&self, rect: &math2::rect::Rectangle) -> Vec { + if let Some(scene) = self.renderer.scene.as_ref() { + let ht = crate::hittest::HitTester::with_graph(self.renderer.get_cache(), &scene.graph); + ht.intersects_topmost(rect) + } else { + Vec::new() + } + } + + /// Get the world-space bounding rect for a single node. + pub fn get_node_bounds( + &self, + id: &crate::node::schema::NodeId, + ) -> Option { + self.renderer.get_cache().geometry.get_world_bounds(id) + } + + /// Get the union world-space bounding rect for a set of nodes. + /// Returns `None` if no nodes have geometry. + pub fn get_union_bounds( + &self, + ids: &[crate::node::schema::NodeId], + ) -> Option { + let rects: Vec<_> = ids + .iter() + .filter_map(|id| self.renderer.get_cache().geometry.get_world_bounds(id)) + .collect(); + if rects.is_empty() { + None + } else { + Some(math2::rect::union(&rects)) + } + } + + /// Test whether a canvas-space point lies inside the union bounding + /// rect of the given node IDs. + pub fn point_in_node_bounds( + &self, + point: [f32; 2], + ids: &[crate::node::schema::NodeId], + ) -> bool { + if let Some(scene) = self.renderer.scene.as_ref() { + let ht = crate::hittest::HitTester::with_graph(self.renderer.get_cache(), &scene.graph); + ht.point_in_selection_bounds(point, ids) + } else { + false + } + } + + /// Get the camera's current view matrix (for screen ↔ canvas conversion). + pub fn view_matrix(&self) -> math2::transform::AffineTransform { + self.renderer.camera.view_matrix() + } + /// Read-only access to the surface state. pub fn surface(&self) -> &crate::surface::SurfaceState { &self.surface } + /// Mutable access to the surface state. + /// + /// Used by the host to configure flags like + /// [`readonly`](crate::surface::SurfaceState::readonly) at startup. + pub fn surface_mut(&mut self) -> &mut crate::surface::SurfaceState { + &mut self.surface + } + // ---- Surface convenience methods ---- // These handle screen→canvas coordinate conversion internally so that // hosts (native, wasm, emscripten) stay thin and consistent. @@ -998,22 +1064,18 @@ impl UnknownTargetApplication { state, input: super::input::InputState::default(), document_json: None, - hit_test_result: None, - hit_test_last: std::time::Instant::now(), - hit_test_interval: std::time::Duration::from_millis(0), + #[cfg(not(target_arch = "wasm32"))] image_rx, #[cfg(not(target_arch = "wasm32"))] font_rx, last_frame_time: std::time::Instant::now(), last_stats: None, - devtools_selection: None, highlight_strokes: Vec::new(), highlight_stroke_style: None, devtools_rendering_show_fps: debug, devtools_rendering_show_tiles: debug, devtools_rendering_show_stats: debug, - devtools_rendering_show_hit_overlay: debug, devtools_rendering_show_ruler: debug, timer: TimerMgr::new(), frame_loop: FrameLoop::new(), @@ -1372,24 +1434,6 @@ impl UnknownTargetApplication { } /// Hit test the current cursor position and store the result. - pub(crate) fn perform_hit_test(&mut self) { - if self.hit_test_interval != std::time::Duration::ZERO - && self.hit_test_last.elapsed() < self.hit_test_interval - { - return; - } - self.hit_test_last = std::time::Instant::now(); - let camera = &self.renderer.camera; - let point = camera.screen_to_canvas_point(self.input.cursor); - // Get string ID from API, convert to internal ID for storage - let new_hit_result = self - .get_node_id_from_point(point) - .and_then(|user_id| self.user_id_to_internal(&user_id)); - if self.hit_test_result != new_hit_result { - self.queue(); - } - self.hit_test_result = new_hit_result; - } #[cfg(not(target_arch = "wasm32"))] pub(crate) fn resource_loaded(&mut self) { @@ -1519,16 +1563,7 @@ impl UnknownTargetApplication { stats_overlay::StatsOverlay::draw(&canvas, s, &self.clock); } } - if self.devtools_rendering_show_hit_overlay { - hit_overlay::HitOverlay::draw( - &canvas, - self.hit_test_result.as_ref(), - self.devtools_selection.as_ref(), - &self.renderer.camera, - self.renderer.get_cache(), - &self.renderer.fonts, - ); - } + if !self.highlight_strokes.is_empty() { stroke_overlay::StrokeOverlay::draw( &canvas, @@ -1617,11 +1652,10 @@ impl UnknownTargetApplication { self.last_stats = Some(s); } - /// Update the cursor position and run a debounced hit test. + /// Update the cursor position. #[allow(dead_code)] pub fn pointer_move(&mut self, x: f32, y: f32) { self.input.cursor = [x, y]; - self.perform_hit_test(); } // Timer convenience methods diff --git a/crates/grida-canvas/tests/surface_interaction.rs b/crates/grida-canvas/tests/surface_interaction.rs new file mode 100644 index 0000000000..c2e926e5a6 --- /dev/null +++ b/crates/grida-canvas/tests/surface_interaction.rs @@ -0,0 +1,602 @@ +//! Integration tests for the surface interaction state machine. +//! +//! These tests construct real scenes with known node positions, populate +//! the full cache pipeline (`GeometryCache` + `LayerList` + R-tree), and +//! exercise `SurfaceState::dispatch` with real `SurfaceEvent` values. + +use cg::cache::scene::SceneCache; +use cg::hittest::HitTester; +use cg::node::{ + factory::NodeFactory, + scene_graph::{Parent, SceneGraph}, + schema::*, +}; +use cg::resources::ByteStore; +use cg::runtime::font_repository::FontRepository; +use cg::surface::event::{Modifiers, PointerButton, SurfaceEvent}; +use cg::surface::gesture::SurfaceGesture; +use cg::surface::state::SurfaceState; +use cg::surface::ui::hit_region::HitRegions; +use cg::surface::CursorIcon; +use math2::transform::AffineTransform; +use std::sync::{Arc, Mutex}; + +// ── Helpers ────────────────────────────────────────────────────────────── + +/// Build a scene with two non-overlapping rectangles: +/// +/// ```text +/// rect_a: (10,10) 80×80 rect_b: (200,10) 80×80 +/// ┌────────┐ ┌────────┐ +/// │ │ │ │ +/// │ A │ │ B │ +/// │ │ │ │ +/// └────────┘ └────────┘ +/// ``` +/// +/// Returns `(scene, cache, rect_a_id, rect_b_id)`. +fn two_rect_scene() -> (Scene, SceneCache, NodeId, NodeId) { + let nf = NodeFactory::new(); + + let mut rect_a = nf.create_rectangle_node(); + rect_a.transform = AffineTransform::new(10.0, 10.0, 0.0); + rect_a.size = Size { + width: 80.0, + height: 80.0, + }; + + let mut rect_b = nf.create_rectangle_node(); + rect_b.transform = AffineTransform::new(200.0, 10.0, 0.0); + rect_b.size = Size { + width: 80.0, + height: 80.0, + }; + + let mut graph = SceneGraph::new(); + let id_a = graph.append_child(Node::Rectangle(rect_a), Parent::Root); + let id_b = graph.append_child(Node::Rectangle(rect_b), Parent::Root); + + let scene = Scene { + name: "test".into(), + background_color: None, + graph, + }; + + let mut cache = SceneCache::new(); + let fonts = FontRepository::new(Arc::new(Mutex::new(ByteStore::new()))); + cache.update_geometry(&scene, &fonts); + cache.update_layers(&scene); + + (scene, cache, id_a, id_b) +} + +fn pointer_down(canvas_point: [f32; 2]) -> SurfaceEvent { + SurfaceEvent::PointerDown { + canvas_point, + screen_point: canvas_point, + button: PointerButton::Primary, + modifiers: Modifiers::default(), + } +} + +fn pointer_move(canvas_point: [f32; 2]) -> SurfaceEvent { + SurfaceEvent::PointerMove { + canvas_point, + screen_point: canvas_point, + } +} + +fn pointer_up(canvas_point: [f32; 2]) -> SurfaceEvent { + SurfaceEvent::PointerUp { + canvas_point, + screen_point: canvas_point, + button: PointerButton::Primary, + modifiers: Modifiers::default(), + } +} + +fn editing_surface() -> SurfaceState { + let mut s = SurfaceState::new(); + s.readonly = false; + s +} + +// ── Basic selection tests (work in both readonly and editing modes) ─────── + +#[test] +fn click_selects_node() { + let (scene, cache, id_a, _id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); // readonly + + // Click center of rect_a → selects it. + let r = surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert!(r.selection_changed); + assert_eq!(surface.selection.as_slice(), &[id_a]); +} + +#[test] +fn click_empty_clears_selection() { + let (scene, cache, id_a, _) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); + + // Select rect_a first. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_a]); + + // Click on empty space (far away) → clears selection, starts marquee. + let r = surface.dispatch(pointer_down([500.0, 500.0]), &ht, &scene.graph, ®ions); + assert!(r.selection_changed); + assert!(surface.selection.is_empty()); + assert!(matches!( + surface.gesture, + SurfaceGesture::MarqueeSelect { .. } + )); +} + +#[test] +fn click_switches_selection() { + let (scene, cache, id_a, id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); + + // Select rect_a. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_a]); + + // Click rect_b → selection switches. + surface.dispatch(pointer_down([240.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_b]); +} + +// ── Editing mode: deferred selection + translate ───────────────────────── + +#[test] +fn editing_click_already_selected_defers_then_applies() { + let (scene, cache, id_a, _id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select both nodes via two shift-clicks (first click selects A, second + // shift-click adds B). + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + + surface.dispatch( + SurfaceEvent::PointerDown { + canvas_point: [240.0, 50.0], + screen_point: [240.0, 50.0], + button: PointerButton::Primary, + modifiers: Modifiers { + shift: true, + ..Default::default() + }, + }, + &ht, + &scene.graph, + ®ions, + ); + surface.dispatch(pointer_up([240.0, 50.0]), &ht, &scene.graph, ®ions); + + assert_eq!(surface.selection.len(), 2); + + // Now click on rect_a (already selected, no shift) → deferred. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + // Selection should still be [A, B] (deferred, not applied yet). + assert_eq!(surface.selection.len(), 2); + + // Release without dragging → deferred applies, resets to [A]. + let r = surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert!(r.selection_changed); + assert_eq!(surface.selection.len(), 1); + assert!(surface.selection.contains(&id_a)); +} + +#[test] +fn editing_drag_selected_starts_translate_preserves_selection() { + let (scene, cache, id_a, id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select both via shift-click. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch( + SurfaceEvent::PointerDown { + canvas_point: [240.0, 50.0], + screen_point: [240.0, 50.0], + button: PointerButton::Primary, + modifiers: Modifiers { + shift: true, + ..Default::default() + }, + }, + &ht, + &scene.graph, + ®ions, + ); + surface.dispatch(pointer_up([240.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.len(), 2); + + // Pointer-down on rect_a (already selected) → deferred. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.len(), 2); + + // Move → translate starts, deferred cancelled. + let r = surface.dispatch(pointer_move([60.0, 60.0]), &ht, &scene.graph, ®ions); + assert!(matches!(surface.gesture, SurfaceGesture::Translate { .. })); + assert!(r.needs_redraw); + // Both nodes remain selected. + assert_eq!(surface.selection.len(), 2); + assert!(surface.selection.contains(&id_a)); + assert!(surface.selection.contains(&id_b)); +} + +#[test] +fn editing_drag_inside_selection_rect_translates() { + let (scene, cache, _id_a, _id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select both nodes. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch( + SurfaceEvent::PointerDown { + canvas_point: [240.0, 50.0], + screen_point: [240.0, 50.0], + button: PointerButton::Primary, + modifiers: Modifiers { + shift: true, + ..Default::default() + }, + }, + &ht, + &scene.graph, + ®ions, + ); + surface.dispatch(pointer_up([240.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.len(), 2); + + // The union bounding rect of the two rects spans from (10,10) to (280,90). + // Click at (140, 50) — empty space between the two rects, but inside + // the selection bounding rect. + surface.dispatch(pointer_down([140.0, 50.0]), &ht, &scene.graph, ®ions); + // Selection should be preserved (not cleared, no marquee). + assert_eq!(surface.selection.len(), 2); + assert!(matches!(surface.gesture, SurfaceGesture::Idle)); + + // Move → translate starts. + surface.dispatch(pointer_move([150.0, 55.0]), &ht, &scene.graph, ®ions); + assert!(matches!(surface.gesture, SurfaceGesture::Translate { .. })); + assert_eq!(surface.selection.len(), 2); +} + +#[test] +fn editing_drag_outside_selection_rect_marquees() { + let (scene, cache, id_a, _) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select rect_a. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_a]); + + // Click far outside the selection bounding rect → clears + marquee. + surface.dispatch(pointer_down([500.0, 500.0]), &ht, &scene.graph, ®ions); + assert!(surface.selection.is_empty()); + assert!(matches!( + surface.gesture, + SurfaceGesture::MarqueeSelect { .. } + )); +} + +#[test] +fn editing_move_cursor_inside_selection_bounds() { + let (scene, cache, _id_a, _) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select rect_a. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + + // Hover over rect_a (selected) → Move cursor. + surface.dispatch(pointer_move([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.cursor, CursorIcon::Move); + + // Hover over empty space → Default cursor. + surface.dispatch(pointer_move([500.0, 500.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.cursor, CursorIcon::Default); +} + +#[test] +fn editing_translate_gesture_tracks_and_ends() { + let (scene, cache, id_a, _) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = editing_surface(); + + // Select and start translate. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_move([70.0, 70.0]), &ht, &scene.graph, ®ions); + assert!(matches!(surface.gesture, SurfaceGesture::Translate { .. })); + + // Verify position tracking (incremental: prev_canvas = current point). + if let SurfaceGesture::Translate { prev_canvas } = surface.gesture { + assert_eq!(prev_canvas, [70.0, 70.0]); + } + + // Continue moving. + surface.dispatch(pointer_move([90.0, 80.0]), &ht, &scene.graph, ®ions); + if let SurfaceGesture::Translate { prev_canvas } = surface.gesture { + assert_eq!(prev_canvas, [90.0, 80.0]); + } + + // Pointer-up ends translate. + let r = surface.dispatch(pointer_up([90.0, 80.0]), &ht, &scene.graph, ®ions); + assert!(matches!(surface.gesture, SurfaceGesture::Idle)); + assert!(r.needs_redraw); + // Selection preserved. + assert_eq!(surface.selection.as_slice(), &[id_a]); +} + +// ── Readonly mode: no translate, no move cursor ────────────────────────── + +#[test] +fn readonly_no_translate_on_drag() { + let (scene, cache, id_a, _id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); // readonly = true + + // Select rect_a. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_a]); + + // In readonly mode, clicking an already-selected node immediately + // re-selects (no deferral). Simulate by clicking again. + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + + // Move — should NOT start a Translate gesture. + surface.dispatch(pointer_move([60.0, 60.0]), &ht, &scene.graph, ®ions); + assert!( + !matches!(surface.gesture, SurfaceGesture::Translate { .. }), + "readonly mode should never enter Translate gesture" + ); +} + +#[test] +fn readonly_no_move_cursor_on_selected_hover() { + let (scene, cache, id_a, _) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); // readonly + + // Select rect_a. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.as_slice(), &[id_a]); + + // Hover over rect_a → cursor should remain Default in readonly. + surface.dispatch(pointer_move([50.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.cursor, CursorIcon::Default); +} + +#[test] +fn readonly_click_inside_selection_rect_clears() { + let (scene, cache, _id_a, _id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + let regions = HitRegions::new(); + let mut surface = SurfaceState::new(); // readonly + + // Select both via shift-clicks. + surface.dispatch(pointer_down([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch(pointer_up([50.0, 50.0]), &ht, &scene.graph, ®ions); + surface.dispatch( + SurfaceEvent::PointerDown { + canvas_point: [240.0, 50.0], + screen_point: [240.0, 50.0], + button: PointerButton::Primary, + modifiers: Modifiers { + shift: true, + ..Default::default() + }, + }, + &ht, + &scene.graph, + ®ions, + ); + surface.dispatch(pointer_up([240.0, 50.0]), &ht, &scene.graph, ®ions); + assert_eq!(surface.selection.len(), 2); + + // In readonly mode, clicking empty space between rects (inside + // selection union rect) should CLEAR selection, not start translate. + surface.dispatch(pointer_down([140.0, 50.0]), &ht, &scene.graph, ®ions); + assert!( + surface.selection.is_empty(), + "readonly mode: click on empty space inside selection rect should clear" + ); + assert!(matches!( + surface.gesture, + SurfaceGesture::MarqueeSelect { .. } + )); +} + +// ── Selection-rect hit test accuracy ───────────────────────────────────── + +#[test] +fn selection_rect_hit_test_accuracy() { + let (scene, cache, id_a, id_b) = two_rect_scene(); + let ht = HitTester::with_graph(&cache, &scene.graph); + + // Both selected: union rect is (10,10)→(280,90). + assert!(ht.point_in_selection_bounds([140.0, 50.0], &[id_a, id_b])); + assert!(ht.point_in_selection_bounds([10.0, 10.0], &[id_a, id_b])); + assert!(ht.point_in_selection_bounds([279.0, 89.0], &[id_a, id_b])); + + // Outside the union rect. + assert!(!ht.point_in_selection_bounds([5.0, 50.0], &[id_a, id_b])); + assert!(!ht.point_in_selection_bounds([300.0, 50.0], &[id_a, id_b])); + assert!(!ht.point_in_selection_bounds([140.0, 0.0], &[id_a, id_b])); + assert!(!ht.point_in_selection_bounds([140.0, 100.0], &[id_a, id_b])); + + // Single node: rect_a bounds are (10,10)→(90,90). + assert!(ht.point_in_selection_bounds([50.0, 50.0], &[id_a])); + assert!(!ht.point_in_selection_bounds([5.0, 50.0], &[id_a])); + + // Empty selection. + assert!(!ht.point_in_selection_bounds([50.0, 50.0], &[])); +} + +// ── Schema accessor tests ──────────────────────────────────────────────── +// Test Node::transform_mut(), Node::size_mut(), refresh_node_geo_data() +// via direct scene graph manipulation + full geometry rebuild. +// (Mutation e2e tests live in grida-dev/src/editor/.) + +#[test] +fn translate_node_moves_bounds() { + let (mut scene, mut cache, id_a, _id_b) = two_rect_scene(); + let fonts = FontRepository::new(Arc::new(Mutex::new(ByteStore::new()))); + + // Original bounds of rect_a: (10,10)→(90,90). + let before = cache.geometry.get_world_bounds(&id_a).unwrap(); + assert!((before.x - 10.0).abs() < 0.1); + assert!((before.y - 10.0).abs() < 0.1); + + // Mutate: translate rect_a by (+50, +30). + { + let node = scene.graph.get_node_mut(&id_a).unwrap(); + if let Node::Rectangle(n) = node { + n.transform.translate(50.0, 30.0); + } + scene.graph.refresh_node_geo_data(&id_a); + } + + // Rebuild caches. + cache.update_geometry(&scene, &fonts); + cache.update_layers(&scene); + + // Verify bounds moved. + let after = cache.geometry.get_world_bounds(&id_a).unwrap(); + assert!( + (after.x - 60.0).abs() < 0.1, + "expected x≈60, got {}", + after.x + ); + assert!( + (after.y - 40.0).abs() < 0.1, + "expected y≈40, got {}", + after.y + ); + assert!((after.width - 80.0).abs() < 0.1); + assert!((after.height - 80.0).abs() < 0.1); +} + +#[test] +fn resize_node_changes_bounds() { + let (mut scene, mut cache, id_a, _) = two_rect_scene(); + let fonts = FontRepository::new(Arc::new(Mutex::new(ByteStore::new()))); + + // Original size: 80×80. + let before = cache.geometry.get_world_bounds(&id_a).unwrap(); + assert!((before.width - 80.0).abs() < 0.1); + + // Mutate: resize to 120×40. + { + let node = scene.graph.get_node_mut(&id_a).unwrap(); + if let Node::Rectangle(n) = node { + n.size.width = 120.0; + n.size.height = 40.0; + } + scene.graph.refresh_node_geo_data(&id_a); + } + + cache.update_geometry(&scene, &fonts); + cache.update_layers(&scene); + + let after = cache.geometry.get_world_bounds(&id_a).unwrap(); + assert!( + (after.width - 120.0).abs() < 0.1, + "expected w≈120, got {}", + after.width + ); + assert!( + (after.height - 40.0).abs() < 0.1, + "expected h≈40, got {}", + after.height + ); + // Position should be unchanged. + assert!((after.x - 10.0).abs() < 0.1); +} + +#[test] +fn rotate_node_changes_geo_data() { + let (mut scene, _cache, id_a, _) = two_rect_scene(); + + // Original rotation: 0. + let geo_before = scene.graph.geo_data().get(&id_a).unwrap().schema_transform; + assert!((geo_before.rotation()).abs() < 0.01); + + // Mutate: rotate by π/4 radians (45°). + let delta = std::f32::consts::FRAC_PI_4; + { + let node = scene.graph.get_node_mut(&id_a).unwrap(); + if let Node::Rectangle(n) = node { + let tx = n.transform.x(); + let ty = n.transform.y(); + let current = n.transform.rotation(); + n.transform = math2::transform::AffineTransform::new(tx, ty, current + delta); + } + scene.graph.refresh_node_geo_data(&id_a); + } + + let geo_after = scene.graph.geo_data().get(&id_a).unwrap().schema_transform; + assert!( + (geo_after.rotation() - delta).abs() < 0.01, + "expected rotation≈{}, got {}", + delta, + geo_after.rotation() + ); +} + +#[test] +fn translate_updates_hit_test() { + let (mut scene, mut cache, id_a, _) = two_rect_scene(); + let fonts = FontRepository::new(Arc::new(Mutex::new(ByteStore::new()))); + + // Before: rect_a at (10,10)→(90,90). Hit at (50,50) should find it. + let ht = HitTester::with_graph(&cache, &scene.graph); + assert_eq!(ht.hit_first_fast([50.0, 50.0]), Some(id_a)); + // Hit at (150,50) should miss. + assert!(ht.hit_first_fast([150.0, 50.0]).is_none()); + + // Translate rect_a by +100 in x. + { + let node = scene.graph.get_node_mut(&id_a).unwrap(); + if let Node::Rectangle(n) = node { + n.transform.translate(100.0, 0.0); + } + scene.graph.refresh_node_geo_data(&id_a); + } + cache.update_geometry(&scene, &fonts); + cache.update_layers(&scene); + + // After: rect_a at (110,10)→(190,90). + let ht = HitTester::with_graph(&cache, &scene.graph); + // Old position should miss. + assert_ne!(ht.hit_first_fast([50.0, 50.0]), Some(id_a)); + // New position should hit. + assert_eq!(ht.hit_first_fast([150.0, 50.0]), Some(id_a)); +} diff --git a/crates/grida-dev/src/bench/runner.rs b/crates/grida-dev/src/bench/runner.rs index 68f331670e..a5d7922ba0 100644 --- a/crates/grida-dev/src/bench/runner.rs +++ b/crates/grida-dev/src/bench/runner.rs @@ -36,6 +36,7 @@ impl OverlayBenchState { show_frame_titles: true, show_size_meter: false, text_baseline_decoration: false, + show_selection_handles: false, }, hit_regions: HitRegions::new(), } diff --git a/crates/grida-dev/src/editor/document.rs b/crates/grida-dev/src/editor/document.rs new file mode 100644 index 0000000000..684700943a --- /dev/null +++ b/crates/grida-dev/src/editor/document.rs @@ -0,0 +1,192 @@ +//! Editor document — owns a `Scene`, applies mutations, flushes to renderer. +//! +//! The editor maintains its own copy of the scene. On each mutation it +//! modifies the scene in-place and then calls `load_scene()` on the +//! renderer to flush the changes. This matches the web editor's model +//! where the JS side owns the document and pushes full snapshots to +//! the WASM renderer. +//! +//! Performance is not a concern — this is dev-only. A full `load_scene` +//! rebuilds all caches from scratch. + +use cg::node::schema::{NodeId, Scene}; +use cg::surface::gesture::SurfaceGesture; +use cg::window::application::UnknownTargetApplication; + +use super::mutation::{ + self, compute_resize_geometry, node_supports_resize, resize_affected_axes, MutationCommand, +}; + +/// The dev editor document. +pub struct EditorDocument { + /// The editor's own copy of the scene. + scene: Scene, +} + +impl EditorDocument { + pub fn new(scene: Scene) -> Self { + Self { scene } + } + + /// Replace the document with a new scene. + pub fn set_scene(&mut self, scene: Scene) { + self.scene = scene; + } + + /// Read the current scene. + pub fn scene(&self) -> &Scene { + &self.scene + } + + /// Apply a mutation command to the document. + /// Returns `true` if the scene was modified. + pub fn apply(&mut self, cmd: &MutationCommand) -> bool { + mutation::apply(&mut self.scene, cmd) + } + + /// Flush the current scene to the renderer. + /// This is a full reload — all caches are rebuilt. + pub fn flush(&self, app: &mut UnknownTargetApplication) { + app.renderer_mut().load_scene(self.scene.clone()); + } + + /// Process a gesture delta from the surface state. + /// + /// Reads the gesture state before and after a surface dispatch, + /// computes the appropriate mutation, applies it to the document, + /// and flushes to the renderer. + /// + /// Returns `true` if the scene was mutated and flushed. + pub fn handle_gesture_delta( + &mut self, + app: &mut UnknownTargetApplication, + gesture_before: &SurfaceGesture, + ) -> bool { + let gesture_after = app.surface().gesture; + + let mutated = match (gesture_before, &gesture_after) { + // ── Translate ──────────────────────────────────────────── + ( + SurfaceGesture::Translate { + prev_canvas: old_pt, + }, + SurfaceGesture::Translate { + prev_canvas: new_pt, + }, + ) => { + let dx = new_pt[0] - old_pt[0]; + let dy = new_pt[1] - old_pt[1]; + let ids = app.surface_selected_nodes().to_vec(); + self.apply(&MutationCommand::Translate { ids, dx, dy }) + } + // ── Resize ─────────────────────────────────────────────── + ( + SurfaceGesture::Resize { + prev_screen: old_pt, + direction, + }, + SurfaceGesture::Resize { + prev_screen: new_pt, + .. + }, + ) => self.handle_incremental_resize(app, *direction, *old_pt, *new_pt), + _ => false, + }; + + if mutated { + self.flush(app); + } + mutated + } + + fn handle_incremental_resize( + &mut self, + app: &UnknownTargetApplication, + direction: cg::surface::ResizeDirection, + old_screen: [f32; 2], + new_screen: [f32; 2], + ) -> bool { + // Filter to resizable nodes. + let resizable_ids: Vec = app + .surface_selected_nodes() + .iter() + .copied() + .filter(|id| { + self.scene + .graph + .get_node(id) + .ok() + .is_some_and(node_supports_resize) + }) + .collect(); + if resizable_ids.is_empty() { + return false; + } + + // Screen → canvas delta via inverse view matrix. + let inv = match app.view_matrix().inverse() { + Some(inv) => inv, + None => return false, + }; + let ds = [new_screen[0] - old_screen[0], new_screen[1] - old_screen[1]]; + let p0 = math2::vector2::transform([0.0, 0.0], &inv); + let p1 = math2::vector2::transform(ds, &inv); + let dx = p1[0] - p0[0]; + let dy = p1[1] - p0[1]; + if dx.abs() < 0.0001 && dy.abs() < 0.0001 { + return false; + } + + // Current selection bounds (read from renderer's cache via UTA). + let union = match app.get_union_bounds(&resizable_ids) { + Some(u) => u, + None => return false, + }; + if union.width < 0.001 || union.height < 0.001 { + return false; + } + + // Pure geometry. + let (new_w, new_h, tx, ty) = + compute_resize_geometry(direction, dx, dy, union.width, union.height); + let new_w = new_w.max(1.0); + let new_h = new_h.max(1.0); + let scale_x = new_w / union.width; + let scale_y = new_h / union.height; + let (affects_w, affects_h) = resize_affected_axes(direction); + + // Collect per-node sizes before mutation. + let node_sizes: Vec<_> = resizable_ids + .iter() + .filter_map(|id| app.get_node_bounds(id).map(|b| (*id, b.width, b.height))) + .collect(); + + let mut mutated = false; + + // Origin shift. + if tx.abs() > 0.0001 || ty.abs() > 0.0001 { + mutated |= self.apply(&MutationCommand::Translate { + ids: resizable_ids.clone(), + dx: tx, + dy: ty, + }); + } + // Per-node resize. + for (id, w, h) in &node_sizes { + mutated |= self.apply(&MutationCommand::Resize { + id: *id, + width: if affects_w { + Some((w * scale_x).max(1.0)) + } else { + None + }, + height: if affects_h { + Some((h * scale_y).max(1.0)) + } else { + None + }, + }); + } + mutated + } +} diff --git a/crates/grida-dev/src/editor/mod.rs b/crates/grida-dev/src/editor/mod.rs new file mode 100644 index 0000000000..23a5c23665 --- /dev/null +++ b/crates/grida-dev/src/editor/mod.rs @@ -0,0 +1,15 @@ +//! Dev-only editor — owns document state, handles gestures, flushes +//! scenes to the `cg` renderer. +//! +//! This module is the **grida-dev counterpart** to the web editor's +//! React/Redux layer. It: +//! - Owns a mutable copy of the `Scene` (the editor document). +//! - Reads gesture/selection state from the `cg` surface. +//! - Performs mutations on its own document copy. +//! - Flushes the mutated scene to the renderer via `load_scene()`. +//! +//! The `cg` crate remains a pure renderer — it never knows about +//! mutations. This editor is strictly for dev/testing purposes. + +pub mod document; +pub mod mutation; diff --git a/crates/grida-dev/src/editor/mutation.rs b/crates/grida-dev/src/editor/mutation.rs new file mode 100644 index 0000000000..1533b955f4 --- /dev/null +++ b/crates/grida-dev/src/editor/mutation.rs @@ -0,0 +1,248 @@ +//! Scene graph mutation commands and writers. +//! +//! Commands describe *what* to change. The `apply` function writes +//! the change to a `Scene` in-place. The caller is responsible for +//! flushing the mutated scene to the renderer. +//! +//! # Unsupported node types +//! +//! **Vector nodes** do not support `Resize`. Vector geometry is defined +//! by a `VectorNetwork` with no separate `size` field. Future options: +//! - Re-plot: scale all vertices/tangents (lossy under repetition). +//! - Render scale: add `render_scale: [f32; 2]` to `VectorNodeRec`. + +use cg::cg::prelude::*; +use cg::node::scene_graph::SceneGraph; +use cg::node::schema::*; + +/// A scene-graph mutation command. +#[derive(Debug, Clone)] +pub enum MutationCommand { + /// Translate nodes by a canvas-space delta. + Translate { ids: Vec, dx: f32, dy: f32 }, + /// Resize a single node. + /// `None` per axis = leave unchanged (preserves auto-sizing for text). + /// Not supported for Vector nodes (no-op). + Resize { + id: NodeId, + width: Option, + height: Option, + }, +} + +/// Apply a mutation command to a scene in-place. +/// Returns `true` if the scene was actually modified. +pub fn apply(scene: &mut Scene, cmd: &MutationCommand) -> bool { + match cmd { + MutationCommand::Translate { ids, dx, dy } => { + if dx.abs() < 0.0001 && dy.abs() < 0.0001 { + return false; + } + for id in ids { + translate_node(&mut scene.graph, id, *dx, *dy); + } + true + } + MutationCommand::Resize { id, width, height } => { + if let Ok(node) = scene.graph.get_node(id) { + if !node_supports_resize(node) { + return false; + } + } else { + return false; + } + resize_node(scene, id, *width, *height) + } + } +} + +/// Whether a node type supports resize. +pub fn node_supports_resize(node: &Node) -> bool { + !matches!(node, Node::Vector(_)) +} + +// ── Node accessors ─────────────────────────────────────────────────────── +// These are editor-only helpers for reaching into Node variant fields. +// They live here (not on `impl Node` in cg) because cg is a renderer +// and should not expose mutable setters on its data model. + +/// Mutable reference to a node's `AffineTransform`, if it has one. +/// Container/Tray store position/rotation separately — returns `None`. +fn node_transform_mut(node: &mut Node) -> Option<&mut math2::transform::AffineTransform> { + match node { + Node::Rectangle(n) => Some(&mut n.transform), + Node::Ellipse(n) => Some(&mut n.transform), + Node::RegularPolygon(n) => Some(&mut n.transform), + Node::RegularStarPolygon(n) => Some(&mut n.transform), + Node::Line(n) => Some(&mut n.transform), + Node::TextSpan(n) => Some(&mut n.transform), + Node::AttributedText(n) => Some(&mut n.transform), + Node::Path(n) => Some(&mut n.transform), + Node::Polygon(n) => Some(&mut n.transform), + Node::Image(n) => Some(&mut n.transform), + Node::Error(n) => Some(&mut n.transform), + Node::Group(n) => n.transform.as_mut(), + Node::BooleanOperation(n) => n.transform.as_mut(), + Node::Vector(n) => Some(&mut n.transform), + Node::Container(_) | Node::Tray(_) | Node::InitialContainer(_) => None, + } +} + +/// Mutable reference to a node's `Size`, if it has one. +/// Container/Tray use layout_dimensions. Group/BooleanOp derive from children. +fn node_size_mut(node: &mut Node) -> Option<&mut Size> { + match node { + Node::Rectangle(n) => Some(&mut n.size), + Node::Ellipse(n) => Some(&mut n.size), + Node::RegularPolygon(n) => Some(&mut n.size), + Node::RegularStarPolygon(n) => Some(&mut n.size), + Node::Line(n) => Some(&mut n.size), + Node::Image(n) => Some(&mut n.size), + Node::Error(n) => Some(&mut n.size), + _ => None, + } +} + +// ── Scene graph writers ────────────────────────────────────────────────── + +fn translate_node(graph: &mut SceneGraph, id: &NodeId, dx: f32, dy: f32) { + if let Ok(node) = graph.get_node_mut(id) { + match node { + Node::Container(n) => { + let x = n.position.x().unwrap_or(0.0); + let y = n.position.y().unwrap_or(0.0); + n.position = LayoutPositioningBasis::Cartesian(CGPoint { + x: x + dx, + y: y + dy, + }); + } + Node::Tray(n) => { + let x = n.position.x().unwrap_or(0.0); + let y = n.position.y().unwrap_or(0.0); + n.position = LayoutPositioningBasis::Cartesian(CGPoint { + x: x + dx, + y: y + dy, + }); + } + _ => { + if let Some(t) = node_transform_mut(node) { + t.translate(dx, dy); + } + } + } + graph.refresh_node_geo_data(id); + } +} + +fn resize_node(scene: &mut Scene, id: &NodeId, width: Option, height: Option) -> bool { + let mut changed = false; + if let Ok(node) = scene.graph.get_node_mut(id) { + match node { + Node::Container(n) => { + if let Some(w) = width { + n.layout_dimensions.layout_target_width = Some(w); + changed = true; + } + if let Some(h) = height { + n.layout_dimensions.layout_target_height = Some(h); + changed = true; + } + } + Node::Tray(n) => { + if let Some(w) = width { + n.layout_dimensions.layout_target_width = Some(w); + changed = true; + } + if let Some(h) = height { + n.layout_dimensions.layout_target_height = Some(h); + changed = true; + } + } + Node::TextSpan(n) => { + if let Some(w) = width { + n.width = Some(w); + changed = true; + } + if let Some(h) = height { + n.height = Some(h); + changed = true; + } + } + Node::AttributedText(n) => { + if let Some(w) = width { + n.width = Some(w); + changed = true; + } + if let Some(h) = height { + n.height = Some(h); + changed = true; + } + } + Node::Vector(_) => {} // not supported + _ => { + if let Some(s) = node_size_mut(node) { + if let Some(w) = width { + s.width = w; + changed = true; + } + if let Some(h) = height { + s.height = h; + changed = true; + } + } + } + } + if changed { + scene.graph.refresh_node_geo_data(id); + } + } + changed +} + +// ── Resize geometry helpers (pure math) ────────────────────────────────── + +/// Which axes a resize direction affects: `(width, height)`. +pub fn resize_affected_axes(direction: cg::surface::ResizeDirection) -> (bool, bool) { + use cg::surface::ResizeDirection; + let w = matches!( + direction, + ResizeDirection::E + | ResizeDirection::W + | ResizeDirection::NE + | ResizeDirection::NW + | ResizeDirection::SE + | ResizeDirection::SW + ); + let h = matches!( + direction, + ResizeDirection::N + | ResizeDirection::S + | ResizeDirection::NE + | ResizeDirection::NW + | ResizeDirection::SE + | ResizeDirection::SW + ); + (w, h) +} + +/// Compute new size + origin shift from a drag delta and current bounds. +/// Returns `(new_w, new_h, translate_x, translate_y)`. +pub fn compute_resize_geometry( + direction: cg::surface::ResizeDirection, + dx: f32, + dy: f32, + old_w: f32, + old_h: f32, +) -> (f32, f32, f32, f32) { + use cg::surface::ResizeDirection; + match direction { + ResizeDirection::SE => (old_w + dx, old_h + dy, 0.0, 0.0), + ResizeDirection::NW => (old_w - dx, old_h - dy, dx, dy), + ResizeDirection::NE => (old_w + dx, old_h - dy, 0.0, dy), + ResizeDirection::SW => (old_w - dx, old_h + dy, dx, 0.0), + ResizeDirection::E => (old_w + dx, old_h, 0.0, 0.0), + ResizeDirection::W => (old_w - dx, old_h, dx, 0.0), + ResizeDirection::S => (old_w, old_h + dy, 0.0, 0.0), + ResizeDirection::N => (old_w, old_h - dy, 0.0, dy), + } +} diff --git a/crates/grida-dev/src/lib.rs b/crates/grida-dev/src/lib.rs index 3be444786a..586f5b1196 100644 --- a/crates/grida-dev/src/lib.rs +++ b/crates/grida-dev/src/lib.rs @@ -1 +1,2 @@ +pub mod editor; pub mod platform; diff --git a/crates/grida-dev/src/platform/native_application.rs b/crates/grida-dev/src/platform/native_application.rs index 2781638d40..a622eabbec 100644 --- a/crates/grida-dev/src/platform/native_application.rs +++ b/crates/grida-dev/src/platform/native_application.rs @@ -25,6 +25,28 @@ use winit::{ window::Window, }; +/// Map a cg `CursorIcon` to a winit `CursorIcon`. +fn map_cursor_icon(icon: cg::surface::CursorIcon) -> winit::window::CursorIcon { + use cg::surface::ResizeDirection; + match icon { + cg::surface::CursorIcon::Default => winit::window::CursorIcon::Default, + cg::surface::CursorIcon::Pointer => winit::window::CursorIcon::Pointer, + cg::surface::CursorIcon::Grab => winit::window::CursorIcon::Grab, + cg::surface::CursorIcon::Grabbing => winit::window::CursorIcon::Grabbing, + cg::surface::CursorIcon::Crosshair => winit::window::CursorIcon::Crosshair, + cg::surface::CursorIcon::Move => winit::window::CursorIcon::Move, + cg::surface::CursorIcon::Resize(dir) => match dir { + ResizeDirection::N | ResizeDirection::S => winit::window::CursorIcon::NsResize, + ResizeDirection::E | ResizeDirection::W => winit::window::CursorIcon::EwResize, + ResizeDirection::NW | ResizeDirection::SE => winit::window::CursorIcon::NwseResize, + ResizeDirection::NE | ResizeDirection::SW => winit::window::CursorIcon::NeswResize, + }, + // Rotation uses a custom cursor in the web editor; for native dev + // we fall back to a crosshair which is the closest standard cursor. + cg::surface::CursorIcon::Rotate(_) => winit::window::CursorIcon::Alias, + } +} + /// Convert a winit KeyEvent to a SurfaceEvent::KeyDown. fn winit_key_to_surface_key_down( event: &KeyEvent, @@ -175,6 +197,9 @@ pub struct NativeApplication { system_clipboard: Option, file_drop_tx: Option>, fit_scene_on_load: bool, + /// Dev editor document. When `Some`, gesture interactions (translate, + /// resize) mutate this document and flush it to the renderer. + editor: Option, /// Set to `true` after `CloseRequested` to prevent event processing on /// a partially-torn-down application (the tick thread may still deliver /// events between `event_loop.exit()` and actual termination). @@ -261,6 +286,8 @@ impl NativeApplication { uta.surface_overlay_config.text_baseline_decoration = true; uta.surface_overlay_config.show_size_meter = true; uta.surface_overlay_config.show_frame_titles = true; + uta.surface_overlay_config.show_selection_handles = true; + uta.surface_mut().readonly = false; let app = NativeApplication { app: uta, @@ -271,6 +298,7 @@ impl NativeApplication { system_clipboard: arboard::Clipboard::new().ok(), file_drop_tx, fit_scene_on_load, + editor: None, exiting: false, settle_countdown: 0, scenes: Vec::new(), @@ -406,23 +434,20 @@ impl NativeApplicationHandler for NativeApplication { canvas_point, screen_point, }; + let gesture_before = self.app.surface().gesture; let response = self.app.handle_surface_event(surface_event); + if let Some(editor) = self.editor.as_mut() { + if editor.handle_gesture_delta(&mut self.app, &gesture_before) { + self.window.request_redraw(); + } + } if response.cursor_changed { - let cursor = match self.app.surface_cursor() { - cg::surface::CursorIcon::Default => winit::window::CursorIcon::Default, - cg::surface::CursorIcon::Pointer => winit::window::CursorIcon::Pointer, - cg::surface::CursorIcon::Grab => winit::window::CursorIcon::Grab, - cg::surface::CursorIcon::Grabbing => winit::window::CursorIcon::Grabbing, - cg::surface::CursorIcon::Crosshair => winit::window::CursorIcon::Crosshair, - cg::surface::CursorIcon::Move => winit::window::CursorIcon::Move, - }; + let cursor = map_cursor_icon(self.app.surface_cursor()); self.window.set_cursor(cursor); } if response.needs_redraw { self.window.request_redraw(); } - // Keep legacy hit test updated for devtools overlay - self.app.perform_hit_test_host(); } if let WindowEvent::MouseInput { state, button, .. } = &event { @@ -464,11 +489,6 @@ impl NativeApplicationHandler for NativeApplication { } else if !is_editing && was_editing { self.window.set_ime_allowed(false); } - - // Keep legacy selection for devtools - if *state == ElementState::Pressed && *button == MouseButton::Left { - self.app.capture_hit_test_selection(); - } } // -- IME events (Korean, Japanese, Chinese text composition) -- @@ -636,6 +656,8 @@ impl NativeApplicationHandler for NativeApplication { }); } + self.editor = + Some(crate::editor::document::EditorDocument::new(scene.clone())); let renderer = self.app.renderer_mut(); renderer.load_scene(scene); fit_camera_to_scene(renderer); @@ -685,6 +707,8 @@ impl NativeApplicationHandler for NativeApplication { first.name.clone() }; + self.editor = + Some(crate::editor::document::EditorDocument::new(first.clone())); let renderer = self.app.renderer_mut(); renderer.load_scene(first); if self.fit_scene_on_load { @@ -722,6 +746,7 @@ impl NativeApplicationHandler for NativeApplication { } HostEvent::LoadScene(scene) => { { + self.editor = Some(crate::editor::document::EditorDocument::new(scene.clone())); let renderer = self.app.renderer_mut(); renderer.load_scene(scene); if self.fit_scene_on_load { From 9904f7ae37802d46032144dae2a29b672d08acdd Mon Sep 17 00:00:00 2001 From: Universe Date: Wed, 1 Apr 2026 01:50:36 +0900 Subject: [PATCH 6/7] refactor(image): update sampling options for improved pixel art rendering Changed the image sampling options to use Nearest-neighbor filtering for upscaling, ensuring crispness for pixel art and patterns, while still utilizing mipmaps for downscaling. This adjustment enhances the visual quality of images rendered larger than their natural size. --- crates/grida-canvas/src/painter/image.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/grida-canvas/src/painter/image.rs b/crates/grida-canvas/src/painter/image.rs index b1abdad2d1..69cdb02478 100644 --- a/crates/grida-canvas/src/painter/image.rs +++ b/crates/grida-canvas/src/painter/image.rs @@ -36,11 +36,12 @@ pub fn image_shader( (image.width() as f32, image.height() as f32), size, )); - // Use Skia's built-in mipmap support. The image stored in ImageRepository - // has a mipmap chain attached via `with_default_mipmaps()`. Skia evaluates - // the LOD at rasterization time based on the final canvas transform, so - // this works correctly with PictureCache playback at different zoom levels. - let sampling = SamplingOptions::new(FilterMode::Linear, MipmapMode::Nearest); + // Nearest-neighbor keeps every source texel crisp when the image is + // displayed larger than its natural size — essential for pixel art, + // checker patterns, QR codes, etc. Mipmaps are still used for + // downscaling (Skia selects LOD from the mipmap chain built by + // `with_default_mipmaps()` in ImageRepository). + let sampling = SamplingOptions::new(FilterMode::Nearest, MipmapMode::Nearest); // Extract repeat mode based on the fit variant let tile_modes = match &img.fit { From b41918c0a982bbec377c144c4f265152979dc649 Mon Sep 17 00:00:00 2001 From: Universe Date: Wed, 1 Apr 2026 02:05:41 +0900 Subject: [PATCH 7/7] refactor(runtime, surface): improve comments and handling for transform updates and UI interactions - Updated NODE_TRANSFORM comment to clarify its reserved status for future incremental updates. - Added handling to clear the translate anchor on double-click to prevent ghost translations during text editing. - Enhanced comments in hit_region.rs to specify behavior in editing mode for resize and rotate handles. - Added a check in render.rs to only register hit regions for visible handles, improving gesture handling efficiency. - Refined node_supports_resize function to explicitly list supported node types for resizing. --- crates/grida-canvas/src/runtime/changes.rs | 9 ++++----- crates/grida-canvas/src/surface/state.rs | 5 +++++ .../grida-canvas/src/surface/ui/hit_region.rs | 9 +++++---- crates/grida-canvas/src/surface/ui/render.rs | 6 ++++++ crates/grida-dev/src/editor/mutation.rs | 20 ++++++++++++++++++- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/crates/grida-canvas/src/runtime/changes.rs b/crates/grida-canvas/src/runtime/changes.rs index e7d2a09283..922c2aad61 100644 --- a/crates/grida-canvas/src/runtime/changes.rs +++ b/crates/grida-canvas/src/runtime/changes.rs @@ -57,12 +57,11 @@ impl ChangeFlags { /// Layout inputs changed (node resize, auto-layout property edit, etc.). pub const LAYOUT_DIRTY: Self = Self(1 << 7); - /// Node transform changed (position, rotation) without content change. + /// Reserved flag for incremental transform updates. /// - /// The property tree handles incremental world-transform resolution. - /// Picture cache does NOT need invalidation (pictures are played back - /// with the updated transform). Only the compositor blit destination - /// and spatial index need updating. + /// Currently unused — the dev editor flushes full scenes via + /// `load_scene()` instead of incremental patching. Defined here to + /// reserve the bit for a future incremental property-tree path. pub const NODE_TRANSFORM: Self = Self(1 << 8); // -- helpers -- diff --git a/crates/grida-canvas/src/surface/state.rs b/crates/grida-canvas/src/surface/state.rs index 1e5081a0e6..87f44cbf64 100644 --- a/crates/grida-canvas/src/surface/state.rs +++ b/crates/grida-canvas/src/surface/state.rs @@ -470,6 +470,11 @@ impl SurfaceState { if click_count >= 2 { response.double_clicked_node = Some(id); + // Clear translate anchor — double-click enters text + // edit, PointerUp may be swallowed by the text edit + // handler. Without this, the stale anchor causes a + // ghost translate on the next PointerMove. + self.pending_pointer_down = None; } } None => { diff --git a/crates/grida-canvas/src/surface/ui/hit_region.rs b/crates/grida-canvas/src/surface/ui/hit_region.rs index 4601ecfc18..49893646d0 100644 --- a/crates/grida-canvas/src/surface/ui/hit_region.rs +++ b/crates/grida-canvas/src/surface/ui/hit_region.rs @@ -7,11 +7,12 @@ use skia_safe::{Contains, Rect}; pub enum OverlayAction { /// Select the given node (replaces current selection, or toggles with shift). SelectNode(NodeId), - /// Pointer is over a resize handle. The surface should show the - /// appropriate directional cursor but should **not** begin a gesture - /// — that will be added later. + /// Pointer is over a resize handle. Shows the appropriate directional + /// cursor. In editing mode (`readonly = false`), pointer-down starts + /// a `SurfaceGesture::Resize`. ResizeHandle(ResizeDirection), - /// Pointer is over a rotation handle. + /// Pointer is over a rotation handle. In editing mode, pointer-down + /// starts a `SurfaceGesture::Rotate`. RotateHandle(RotationCorner), } diff --git a/crates/grida-canvas/src/surface/ui/render.rs b/crates/grida-canvas/src/surface/ui/render.rs index f9b5289acb..04f883f545 100644 --- a/crates/grida-canvas/src/surface/ui/render.rs +++ b/crates/grida-canvas/src/surface/ui/render.rs @@ -227,6 +227,12 @@ impl SurfaceUI { // Draw visible parts (corner knobs). handles.draw(canvas); + // Only register hit regions when handles are visible. Hidden handles + // (selection too small) should not produce cursors or start gestures. + if !handles.visible { + return; + } + // Register hit regions for all handles. These are pushed *after* // frame title regions so they win in the front-to-back hit test. for hr in handles.resize.iter().chain(handles.rotation.iter()) { diff --git a/crates/grida-dev/src/editor/mutation.rs b/crates/grida-dev/src/editor/mutation.rs index 1533b955f4..157e09e16b 100644 --- a/crates/grida-dev/src/editor/mutation.rs +++ b/crates/grida-dev/src/editor/mutation.rs @@ -57,8 +57,26 @@ pub fn apply(scene: &mut Scene, cmd: &MutationCommand) -> bool { } /// Whether a node type supports resize. +/// +/// Returns `true` only for node types that `resize_node` can actually +/// mutate. Group, BooleanOperation, Vector, Path, Polygon, and +/// InitialContainer are excluded — they derive size from children or +/// geometry, and resize_node no-ops for them. pub fn node_supports_resize(node: &Node) -> bool { - !matches!(node, Node::Vector(_)) + matches!( + node, + Node::Rectangle(_) + | Node::Ellipse(_) + | Node::RegularPolygon(_) + | Node::RegularStarPolygon(_) + | Node::Line(_) + | Node::Image(_) + | Node::Error(_) + | Node::Container(_) + | Node::Tray(_) + | Node::TextSpan(_) + | Node::AttributedText(_) + ) } // ── Node accessors ───────────────────────────────────────────────────────