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 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": [ 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/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/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/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 { diff --git a/crates/grida-canvas/src/runtime/changes.rs b/crates/grida-canvas/src/runtime/changes.rs index 4f5d7653f2..922c2aad61 100644 --- a/crates/grida-canvas/src/runtime/changes.rs +++ b/crates/grida-canvas/src/runtime/changes.rs @@ -57,6 +57,13 @@ impl ChangeFlags { /// Layout inputs changed (node resize, auto-layout property edit, etc.). pub const LAYOUT_DIRTY: Self = Self(1 << 7); + /// Reserved flag for incremental transform updates. + /// + /// 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 -- 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..87f44cbf64 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,111 @@ 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); + // 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 => { + // 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 +515,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 +525,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 +563,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 +575,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..49893646d0 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,13 @@ 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. Shows the appropriate directional + /// cursor. In editing mode (`readonly = false`), pointer-down starts + /// a `SurfaceGesture::Resize`. + ResizeHandle(ResizeDirection), + /// Pointer is over a rotation handle. In editing mode, pointer-down + /// starts a `SurfaceGesture::Rotate`. + RotateHandle(RotationCorner), } /// A screen-space axis-aligned hit region for overlay UI elements. @@ -79,15 +87,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..04f883f545 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,81 @@ 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); + + // 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()) { + 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/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/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..157e09e16b --- /dev/null +++ b/crates/grida-dev/src/editor/mutation.rs @@ -0,0 +1,266 @@ +//! 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. +/// +/// 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::Rectangle(_) + | Node::Ellipse(_) + | Node::RegularPolygon(_) + | Node::RegularStarPolygon(_) + | Node::Line(_) + | Node::Image(_) + | Node::Error(_) + | Node::Container(_) + | Node::Tray(_) + | Node::TextSpan(_) + | Node::AttributedText(_) + ) +} + +// ── 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 { 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. diff --git a/fixtures/test-grida/L0.grida b/fixtures/test-grida/L0.grida index 8693319103..25b8b90178 100644 Binary files a/fixtures/test-grida/L0.grida and b/fixtures/test-grida/L0.grida differ