diff --git a/.gitignore b/.gitignore index a7c0acc3..06a311c7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build/* plugins/* node_modules __pycache__ +.DS_Store diff --git a/assets/icons/download.svg b/assets/icons/download.svg index 03716d38..087d946b 100644 --- a/assets/icons/download.svg +++ b/assets/icons/download.svg @@ -1,4 +1,4 @@ - + + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 96585b0c..64d532f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "online-3d-viewer", - "version": "0.15.0", + "version": "0.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "online-3d-viewer", - "version": "0.15.0", + "version": "0.16.0", "license": "MIT", "dependencies": { "@simonwep/pickr": "1.9.0", diff --git a/source/engine/Release/occt-import-js.js b/source/engine/Release/occt-import-js.js new file mode 100644 index 00000000..1c941f29 --- /dev/null +++ b/source/engine/Release/occt-import-js.js @@ -0,0 +1,2 @@ +var occtimportjs=(()=>{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 if(ENVIRONMENT_IS_WORKER){_scriptName=self.location.href}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("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{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}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);HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);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["X"]();FS.ignorePermissions=false}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.";if(runtimeInitialized){___trap()}var e=new WebAssembly.RuntimeError(what);readyPromiseReject?.(e);throw e}var wasmBinaryFile;function findWasmBinary(){return locateFile("occt-import-js.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&&!isFileURI(binaryFile)&&!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 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("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)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},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);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];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={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];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={DEFAULT_POLLMASK:5,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_chmod(path,mode){try{path=SYSCALLS.getStr(path);FS.chmod(path,mode);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;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}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>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_rmdir(path){try{path=SYSCALLS.getStr(path);FS.rmdir(path);return 0}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}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(!flags){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{return-28}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var AsciiToString=ptr=>{var str="";while(1){var ch=HEAPU8[ptr++];if(!ch)return str;str+=String.fromCharCode(ch)}};var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var BindingError=class BindingError extends Error{constructor(message){super(message);this.name="BindingError"}};var throwBindingError=message=>{throw new BindingError(message)};function sharedRegisterType(rawType,registeredInstance,options={}){var name=registeredInstance.name;if(!rawType){throwBindingError(`type "${name}" must have a positive integer typeid pointer`)}if(registeredTypes.hasOwnProperty(rawType)){if(options.ignoreDuplicateRegistrations){return}else{throwBindingError(`Cannot register type '${name}' twice`)}}registeredTypes[rawType]=registeredInstance;delete typeDependencies[rawType];if(awaitingDependencies.hasOwnProperty(rawType)){var callbacks=awaitingDependencies[rawType];delete awaitingDependencies[rawType];callbacks.forEach(cb=>cb())}}function registerType(rawType,registeredInstance,options={}){return sharedRegisterType(rawType,registeredInstance,options)}var integerReadValueFromPointer=(name,width,signed)=>{switch(width){case 1:return signed?pointer=>HEAP8[pointer]:pointer=>HEAPU8[pointer];case 2:return signed?pointer=>HEAP16[pointer>>1]:pointer=>HEAPU16[pointer>>1];case 4:return signed?pointer=>HEAP32[pointer>>2]:pointer=>HEAPU32[pointer>>2];case 8:return signed?pointer=>HEAP64[pointer>>3]:pointer=>HEAPU64[pointer>>3];default:throw new TypeError(`invalid integer width (${width}): ${name}`)}};var __embind_register_bigint=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0n;let fromWireType=value=>value;if(isUnsignedType){const bitSize=size*8;fromWireType=value=>BigInt.asUintN(bitSize,value);maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>{if(typeof value=="number"){value=BigInt(value)}return value},readValueFromPointer:integerReadValueFromPointer(name,size,!isUnsignedType),destructorFunction:null})};var __embind_register_bool=(rawType,name,trueValue,falseValue)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:function(wt){return!!wt},toWireType:function(destructors,o){return o?trueValue:falseValue},readValueFromPointer:function(pointer){return this.fromWireType(HEAPU8[pointer])},destructorFunction:null})};var emval_freelist=[];var emval_handles=[0,1,,1,null,1,true,1,false,1];var __emval_decref=handle=>{if(handle>9&&0===--emval_handles[handle+1]){emval_handles[handle]=undefined;emval_freelist.push(handle)}};var Emval={toValue:handle=>{if(!handle){throwBindingError(`Cannot use deleted val. handle = ${handle}`)}return emval_handles[handle]},toHandle:value=>{switch(value){case undefined:return 2;case null:return 4;case true:return 6;case false:return 8;default:{const handle=emval_freelist.pop()||emval_handles.length;emval_handles[handle]=value;emval_handles[handle+1]=1;return handle}}}};function readPointer(pointer){return this.fromWireType(HEAPU32[pointer>>2])}var EmValType={name:"emscripten::val",fromWireType:handle=>{var rv=Emval.toValue(handle);__emval_decref(handle);return rv},toWireType:(destructors,value)=>Emval.toHandle(value),readValueFromPointer:readPointer,destructorFunction:null};var __embind_register_emval=rawType=>registerType(rawType,EmValType);var floatReadValueFromPointer=(name,width)=>{switch(width){case 4:return function(pointer){return this.fromWireType(HEAPF32[pointer>>2])};case 8:return function(pointer){return this.fromWireType(HEAPF64[pointer>>3])};default:throw new TypeError(`invalid float width (${width}): ${name}`)}};var __embind_register_float=(rawType,name,size)=>{name=AsciiToString(name);registerType(rawType,{name,fromWireType:value=>value,toWireType:(destructors,value)=>value,readValueFromPointer:floatReadValueFromPointer(name,size),destructorFunction:null})};var createNamedFunction=(name,func)=>Object.defineProperty(func,"name",{value:name});var runDestructors=destructors=>{while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}};function usesDestructorStack(argTypes){for(var i=1;i{if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(...args){if(!proto[methodName].overloadTable.hasOwnProperty(args.length)){throwBindingError(`Function '${humanName}' called with an invalid number of arguments (${args.length}) - expects one of (${proto[methodName].overloadTable})!`)}return proto[methodName].overloadTable[args.length].apply(this,args)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}};var exposePublicSymbol=(name,value,numArguments)=>{if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError(`Cannot register public name '${name}' twice`)}ensureOverloadTable(Module,name,name);if(Module[name].overloadTable.hasOwnProperty(numArguments)){throwBindingError(`Cannot register multiple overloads of a function with the same number of arguments (${numArguments})!`)}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var heap32VectorToArray=(count,firstElement)=>{var array=[];for(var i=0;i>2])}return array};var InternalError=class InternalError extends Error{constructor(message){super(message);this.name="InternalError"}};var throwInternalError=message=>{throw new InternalError(message)};var replacePublicSymbol=(name,value,numArguments)=>{if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistent public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var embind__requireFunction=(signature,rawFunction,isAsync=false)=>{signature=AsciiToString(signature);function makeDynCaller(){var rtn=getWasmTableEntry(rawFunction);return rtn}var fp=makeDynCaller();if(typeof fp!="function"){throwBindingError(`unknown function pointer with signature ${signature}: ${rawFunction}`)}return fp};class UnboundTypeError extends Error{}var getTypeName=type=>{var ptr=___getTypeName(type);var rv=AsciiToString(ptr);_free(ptr);return rv};var throwUnboundTypeError=(message,types)=>{var unboundTypes=[];var seen={};function visit(type){if(seen[type]){return}if(registeredTypes[type]){return}if(typeDependencies[type]){typeDependencies[type].forEach(visit);return}unboundTypes.push(type);seen[type]=true}types.forEach(visit);throw new UnboundTypeError(`${message}: `+unboundTypes.map(getTypeName).join([", "]))};var whenDependentTypesAreResolved=(myTypes,dependentTypes,getTypeConverters)=>{myTypes.forEach(type=>typeDependencies[type]=dependentTypes);function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i{if(registeredTypes.hasOwnProperty(dt)){typeConverters[i]=registeredTypes[dt]}else{unregisteredTypes.push(dt);if(!awaitingDependencies.hasOwnProperty(dt)){awaitingDependencies[dt]=[]}awaitingDependencies[dt].push(()=>{typeConverters[i]=registeredTypes[dt];++registered;if(registered===unregisteredTypes.length){onComplete(typeConverters)}})}});if(0===unregisteredTypes.length){onComplete(typeConverters)}};var getFunctionName=signature=>{signature=signature.trim();const argsIndex=signature.indexOf("(");if(argsIndex===-1)return signature;return signature.slice(0,argsIndex)};var __embind_register_function=(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn,isAsync,isNonnullReturn)=>{var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=AsciiToString(name);name=getFunctionName(name);rawInvoker=embind__requireFunction(signature,rawInvoker,isAsync);exposePublicSymbol(name,function(){throwUnboundTypeError(`Cannot call ${name} due to unbound types`,argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,argTypes=>{var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn,isAsync),argCount-1);return[]})};var __embind_register_integer=(primitiveType,name,size,minRange,maxRange)=>{name=AsciiToString(name);const isUnsignedType=minRange===0;let fromWireType=value=>value;if(isUnsignedType){var bitshift=32-8*size;fromWireType=value=>value<>>bitshift;maxRange=fromWireType(maxRange)}registerType(primitiveType,{name,fromWireType,toWireType:(destructors,value)=>value,readValueFromPointer:integerReadValueFromPointer(name,size,minRange!==0),destructorFunction:null})};var __embind_register_memory_view=(rawType,dataTypeIndex,name)=>{var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,BigInt64Array,BigUint64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){var size=HEAPU32[handle>>2];var data=HEAPU32[handle+4>>2];return new TA(HEAP8.buffer,data,size)}name=AsciiToString(name);registerType(rawType,{name,fromWireType:decodeMemoryView,readValueFromPointer:decodeMemoryView},{ignoreDuplicateRegistrations:true})};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __embind_register_std_string=(rawType,name)=>{name=AsciiToString(name);var stdStringIsUTF8=true;registerType(rawType,{name,fromWireType(value){var length=HEAPU32[value>>2];var payload=value+4;var str;if(stdStringIsUTF8){str=UTF8ToString(payload,length,true)}else{str="";for(var i=0;i>2]=length;if(valueIsOfTypeString){if(stdStringIsUTF8){stringToUTF8(value,ptr,length+1)}else{for(var i=0;i255){_free(base);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+i]=charCode}}}else{HEAPU8.set(value,ptr)}if(destructors!==null){destructors.push(_free,base)}return base},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var UTF16Decoder=globalThis.TextDecoder?new TextDecoder("utf-16le"):undefined;var UTF16ToString=(ptr,maxBytesToRead,ignoreNul)=>{var idx=ptr>>1;var endIdx=findStringEnd(HEAPU16,idx,maxBytesToRead/2,ignoreNul);if(endIdx-idx>16&&UTF16Decoder)return UTF16Decoder.decode(HEAPU16.subarray(idx,endIdx));var str="";for(var i=idx;i{maxBytesToWrite??=2147483647;if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr};var lengthBytesUTF16=str=>str.length*2;var UTF32ToString=(ptr,maxBytesToRead,ignoreNul)=>{var str="";var startIdx=ptr>>2;for(var i=0;!(i>=maxBytesToRead/4);i++){var utf32=HEAPU32[startIdx+i];if(!utf32&&!ignoreNul)break;str+=String.fromCodePoint(utf32)}return str};var stringToUTF32=(str,outPtr,maxBytesToWrite)=>{maxBytesToWrite??=2147483647;if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i65535){i++}HEAP32[outPtr>>2]=codePoint;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr};var lengthBytesUTF32=str=>{var len=0;for(var i=0;i65535){i++}len+=4}return len};var __embind_register_std_wstring=(rawType,charSize,name)=>{name=AsciiToString(name);var decodeString,encodeString,lengthBytesUTF;if(charSize===2){decodeString=UTF16ToString;encodeString=stringToUTF16;lengthBytesUTF=lengthBytesUTF16}else{decodeString=UTF32ToString;encodeString=stringToUTF32;lengthBytesUTF=lengthBytesUTF32}registerType(rawType,{name,fromWireType:value=>{var length=HEAPU32[value>>2];var str=decodeString(value+4,length*charSize,true);_free(value);return str},toWireType:(destructors,value)=>{if(!(typeof value=="string")){throwBindingError(`Cannot pass non-string to C++ string type ${name}`)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length/charSize;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},readValueFromPointer:readPointer,destructorFunction(ptr){_free(ptr)}})};var __embind_register_void=(rawType,name)=>{name=AsciiToString(name);registerType(rawType,{isVoid:true,name,fromWireType:()=>undefined,toWireType:(destructors,o)=>undefined})};var inetPton4=str=>{var b=str.split(".");for(var i=0;i<4;i++){var tmp=Number(b[i]);if(isNaN(tmp))return null;b[i]=tmp}return(b[0]|b[1]<<8|b[2]<<16|b[3]<<24)>>>0};var inetPton6=str=>{var words;var w,offset,z;var valid6regx=/^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i;var parts=[];if(!valid6regx.test(str)){return null}if(str==="::"){return[0,0,0,0,0,0,0,0]}if(str.startsWith("::")){str=str.replace("::","Z:")}else{str=str.replace("::",":Z:")}if(str.indexOf(".")>0){str=str.replace(new RegExp("[.]","g"),":");words=str.split(":");words[words.length-4]=Number(words[words.length-4])+Number(words[words.length-3])*256;words[words.length-3]=Number(words[words.length-2])+Number(words[words.length-1])*256;words=words.slice(0,words.length-2)}else{words=str.split(":")}offset=0;z=0;for(w=0;w{var nameString=UTF8ToString(name);return inetPton4(DNS.lookup_name(nameString))};var emval_methodCallers=[];var emval_addMethodCaller=caller=>{var id=emval_methodCallers.length;emval_methodCallers.push(caller);return id};var requireRegisteredType=(rawType,humanName)=>{var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(`${humanName} has unknown type ${getTypeName(rawType)}`)}return impl};var emval_lookupTypes=(argCount,argTypes)=>{var a=new Array(argCount);for(var i=0;i>2],`parameter ${i}`)}return a};var emval_returnValue=(toReturnWire,destructorsRef,handle)=>{var destructors=[];var result=toReturnWire(destructors,handle);if(destructors.length){HEAPU32[destructorsRef>>2]=Emval.toHandle(destructors)}return result};var emval_symbols={};var getStringOrSymbol=address=>{var symbol=emval_symbols[address];if(symbol===undefined){return AsciiToString(address)}return symbol};var __emval_create_invoker=(argCount,argTypesPtr,kind)=>{var GenericWireTypeSize=8;var[retType,...argTypes]=emval_lookupTypes(argCount,argTypesPtr);var toReturnWire=retType.toWireType.bind(retType);var argFromPtr=argTypes.map(type=>type.readValueFromPointer.bind(type));argCount--;var captures={toValue:Emval.toValue};var args=argFromPtr.map((argFromPtr,i)=>{var captureName=`argFromPtr${i}`;captures[captureName]=argFromPtr;return`${captureName}(args${i?"+"+i*GenericWireTypeSize:""})`});var functionBody;switch(kind){case 0:functionBody="toValue(handle)";break;case 2:functionBody="new (toValue(handle))";break;case 3:functionBody="";break;case 1:captures["getStringOrSymbol"]=getStringOrSymbol;functionBody="toValue(handle)[getStringOrSymbol(methodName)]";break}functionBody+=`(${args})`;if(!retType.isVoid){captures["toReturnWire"]=toReturnWire;captures["emval_returnValue"]=emval_returnValue;functionBody=`return emval_returnValue(toReturnWire, destructorsRef, ${functionBody})`}functionBody=`return function (handle, methodName, destructorsRef, args) {\n ${functionBody}\n }`;var invokerFunction=new Function(Object.keys(captures),functionBody)(...Object.values(captures));var functionName=`methodCaller<(${argTypes.map(t=>t.name)}) => ${retType.name}>`;return emval_addMethodCaller(createNamedFunction(functionName,invokerFunction))};var __emval_get_global=name=>{if(!name){return Emval.toHandle(globalThis)}name=getStringOrSymbol(name);return Emval.toHandle(globalThis[name])};var __emval_get_property=(handle,key)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);return Emval.toHandle(handle[key])};var __emval_incref=handle=>{if(handle>9){emval_handles[handle+1]+=1}};var __emval_invoke=(caller,handle,methodName,destructorsRef,args)=>emval_methodCallers[caller](handle,methodName,destructorsRef,args);var __emval_new_array=()=>Emval.toHandle([]);var __emval_new_cstring=v=>Emval.toHandle(getStringOrSymbol(v));var __emval_new_object=()=>Emval.toHandle({});var __emval_run_destructors=handle=>{var destructors=Emval.toValue(handle);runDestructors(destructors);__emval_decref(handle)};var __emval_set_property=(handle,key,value)=>{handle=Emval.toValue(handle);key=Emval.toValue(key);value=Emval.toValue(value);handle[key]=value};var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>numINT53_MAX?NaN:Number(num);function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}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 jsStackTrace=()=>(new Error).stack.toString();var getCallstack=flags=>{var callstack=jsStackTrace();var lines=callstack.split("\n");callstack="";var firefoxRe=new RegExp("\\s*(.*?)@(.*?):([0-9]+):([0-9]+)");var chromeRe=new RegExp("\\s*at (.*?) \\((.*):(.*):(.*)\\)");for(var line of lines){var symbolName="";var file="";var lineno=0;var column=0;var parts=chromeRe.exec(line);if(parts?.length==5){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]}else{parts=firefoxRe.exec(line);if(parts?.length>=4){symbolName=parts[1];file=parts[2];lineno=parts[3];column=parts[4]|0}else{callstack+=line+"\n";continue}}if(symbolName=="_emscripten_log"||symbolName=="_emscripten_get_callstack"){callstack="";continue}if(flags&24){if(flags&64){file=file.substring(file.replace(/\\/g,"/").lastIndexOf("/")+1)}callstack+=` at ${symbolName} (${file}:${lineno}:${column})\n`}}callstack=callstack.replace(/\s+$/,"");return callstack};var _emscripten_get_callstack=(flags,str,maxbytes)=>{var callstack=getCallstack(flags);if(!str||maxbytes<=0){return lengthBytesUTF8(callstack)+1}var bytesWrittenExcludingNull=stringToUTF8(callstack,str,maxbytes);return bytesWrittenExcludingNull+1};var getHeapMax=()=>2147483648;var _emscripten_get_heap_max=()=>getHeapMax();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=(typeof navigator=="object"&&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_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}}FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{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()()}}}var ___getTypeName,_malloc,_free,_htons,_emscripten_builtin_memalign,___trap,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){___getTypeName=wasmExports["Y"];_malloc=wasmExports["_"];_free=wasmExports["$"];_htons=wasmExports["aa"];_emscripten_builtin_memalign=wasmExports["ba"];___trap=wasmExports["ca"];memory=wasmMemory=wasmExports["W"];__indirect_function_table=wasmTable=wasmExports["Z"]}var wasmImports={I:___syscall_chmod,J:___syscall_faccessat,h:___syscall_fcntl64,F:___syscall_fstat64,M:___syscall_ioctl,C:___syscall_lstat64,D:___syscall_newfstatat,j:___syscall_openat,v:___syscall_rmdir,E:___syscall_stat64,w:___syscall_unlinkat,u:__abort_js,n:__embind_register_bigint,S:__embind_register_bool,Q:__embind_register_emval,m:__embind_register_float,f:__embind_register_function,d:__embind_register_integer,a:__embind_register_memory_view,R:__embind_register_std_string,i:__embind_register_std_wstring,T:__embind_register_void,r:__emscripten_lookup_name,c:__emval_create_invoker,P:__emval_decref,z:__emval_get_global,k:__emval_get_property,o:__emval_incref,b:__emval_invoke,U:__emval_new_array,q:__emval_new_cstring,V:__emval_new_object,p:__emval_run_destructors,e:__emval_set_property,x:__localtime_js,K:__tzset_js,H:_clock_time_get,G:_emscripten_date_now,N:_emscripten_get_callstack,t:_emscripten_get_heap_max,s:_emscripten_resize_heap,A:_environ_get,B:_environ_sizes_get,O:_exit,g:_fd_close,L:_fd_read,y:_fd_seek,l:_fd_write};function run(){if(runDependencies>0){dependenciesFulfilled=run;return}preRun();if(runDependencies>0){dependenciesFulfilled=run;return}function doRun(){Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve?.(Module);Module["onRuntimeInitialized"]?.();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=occtimportjs;module.exports.default=occtimportjs}else if(typeof define==="function"&&define["amd"])define([],()=>occtimportjs); diff --git a/source/engine/Release/occt-import-js.wasm b/source/engine/Release/occt-import-js.wasm new file mode 100755 index 00000000..4c73b602 Binary files /dev/null and b/source/engine/Release/occt-import-js.wasm differ diff --git a/source/engine/viewer/viewer.js b/source/engine/viewer/viewer.js index 789c01d0..0e11e4e7 100644 --- a/source/engine/viewer/viewer.js +++ b/source/engine/viewer/viewer.js @@ -10,51 +10,48 @@ import { ViewerModel, ViewerMainModel } from './viewermodel.js'; import * as THREE from 'three'; -export function GetDefaultCamera (direction) -{ +export function GetDefaultCamera(direction) { let fieldOfView = 45.0; if (direction === Direction.X) { - return new Camera ( - new Coord3D (2.0, -3.0, 1.5), - new Coord3D (0.0, 0.0, 0.0), - new Coord3D (1.0, 0.0, 0.0), + return new Camera( + new Coord3D(2.0, -3.0, 1.5), + new Coord3D(0.0, 0.0, 0.0), + new Coord3D(1.0, 0.0, 0.0), fieldOfView ); } else if (direction === Direction.Y) { - return new Camera ( - new Coord3D (-1.5, 2.0, 3.0), - new Coord3D (0.0, 0.0, 0.0), - new Coord3D (0.0, 1.0, 0.0), + return new Camera( + new Coord3D(-1.5, 2.0, 3.0), + new Coord3D(0.0, 0.0, 0.0), + new Coord3D(0.0, 1.0, 0.0), fieldOfView ); } else if (direction === Direction.Z) { - return new Camera ( - new Coord3D (-1.5, -3.0, 2.0), - new Coord3D (0.0, 0.0, 0.0), - new Coord3D (0.0, 0.0, 1.0), + return new Camera( + new Coord3D(-1.5, -3.0, 2.0), + new Coord3D(0.0, 0.0, 0.0), + new Coord3D(0.0, 0.0, 1.0), fieldOfView ); } return null; } -export function TraverseThreeObject (object, processor) -{ - if (!processor (object)) { +export function TraverseThreeObject(object, processor) { + if (!processor(object)) { return false; } for (let child of object.children) { - if (!TraverseThreeObject (child, processor)) { + if (!TraverseThreeObject(child, processor)) { return false; } } return true; } -export function GetShadingTypeOfObject (mainObject) -{ +export function GetShadingTypeOfObject(mainObject) { let shadingType = null; - TraverseThreeObject (mainObject, (obj) => { + TraverseThreeObject(mainObject, (obj) => { if (obj.isMesh) { for (const material of obj.material) { if (material.type === 'MeshPhongMaterial') { @@ -70,21 +67,17 @@ export function GetShadingTypeOfObject (mainObject) return shadingType; } -export class CameraValidator -{ - constructor () - { +export class CameraValidator { + constructor() { this.eyeCenterDistance = 0.0; this.forceUpdate = true; } - ForceUpdate () - { + ForceUpdate() { this.forceUpdate = true; } - ValidatePerspective () - { + ValidatePerspective() { if (this.forceUpdate) { this.forceUpdate = false; return false; @@ -92,9 +85,8 @@ export class CameraValidator return true; } - ValidateOrthographic (eyeCenterDistance) - { - if (this.forceUpdate || !IsEqual (this.eyeCenterDistance, eyeCenterDistance)) { + ValidateOrthographic(eyeCenterDistance) { + if (this.forceUpdate || !IsEqual(this.eyeCenterDistance, eyeCenterDistance)) { this.eyeCenterDistance = eyeCenterDistance; this.forceUpdate = false; return false; @@ -103,62 +95,55 @@ export class CameraValidator } } -export class UpVector -{ - constructor () - { +export class UpVector { + constructor() { this.direction = Direction.Y; this.isFixed = true; this.isFlipped = false; } - SetDirection (newDirection, oldCamera) - { + SetDirection(newDirection, oldCamera) { this.direction = newDirection; this.isFlipped = false; - let defaultCamera = GetDefaultCamera (this.direction); - let defaultDir = SubCoord3D (defaultCamera.eye, defaultCamera.center); + let defaultCamera = GetDefaultCamera(this.direction); + let defaultDir = SubCoord3D(defaultCamera.eye, defaultCamera.center); - let distance = CoordDistance3D (oldCamera.center, oldCamera.eye); - let newEye = oldCamera.center.Clone ().Offset (defaultDir, distance); + let distance = CoordDistance3D(oldCamera.center, oldCamera.eye); + let newEye = oldCamera.center.Clone().Offset(defaultDir, distance); - let newCamera = oldCamera.Clone (); + let newCamera = oldCamera.Clone(); if (this.direction === Direction.X) { - newCamera.up = new Coord3D (1.0, 0.0, 0.0); + newCamera.up = new Coord3D(1.0, 0.0, 0.0); newCamera.eye = newEye; } else if (this.direction === Direction.Y) { - newCamera.up = new Coord3D (0.0, 1.0, 0.0); + newCamera.up = new Coord3D(0.0, 1.0, 0.0); newCamera.eye = newEye; } else if (this.direction === Direction.Z) { - newCamera.up = new Coord3D (0.0, 0.0, 1.0); + newCamera.up = new Coord3D(0.0, 0.0, 1.0); newCamera.eye = newEye; } return newCamera; } - SetFixed (isFixed, oldCamera) - { + SetFixed(isFixed, oldCamera) { this.isFixed = isFixed; if (this.isFixed) { - return this.SetDirection (this.direction, oldCamera); + return this.SetDirection(this.direction, oldCamera); } return null; } - Flip (oldCamera) - { + Flip(oldCamera) { this.isFlipped = !this.isFlipped; - let newCamera = oldCamera.Clone (); - newCamera.up.MultiplyScalar (-1.0); + let newCamera = oldCamera.Clone(); + newCamera.up.MultiplyScalar(-1.0); return newCamera; } } -export class Viewer -{ - constructor () - { +export class Viewer { + constructor() { THREE.ColorManagement.enabled = false; this.canvas = null; @@ -173,165 +158,148 @@ export class Viewer this.navigation = null; this.upVector = null; this.settings = { - animationSteps : 40 + animationSteps: 40 }; } - Init (canvas) - { + Init(canvas) { this.canvas = canvas; this.canvas.id = 'viewer'; let parameters = { - canvas : this.canvas, - antialias : true + canvas: this.canvas, + antialias: true }; - this.renderer = new THREE.WebGLRenderer (parameters); + this.renderer = new THREE.WebGLRenderer(parameters); this.renderer.outputColorSpace = THREE.LinearSRGBColorSpace; if (window.devicePixelRatio) { - this.renderer.setPixelRatio (window.devicePixelRatio); + this.renderer.setPixelRatio(window.devicePixelRatio); } - this.renderer.setClearColor ('#ffffff', 1.0); - this.renderer.setSize (this.canvas.width, this.canvas.height); + this.renderer.setClearColor('#ffffff', 1.0); + this.renderer.setSize(this.canvas.width, this.canvas.height); - this.scene = new THREE.Scene (); - this.mainModel = new ViewerMainModel (this.scene); - this.extraModel = new ViewerModel (this.scene); + this.scene = new THREE.Scene(); + this.mainModel = new ViewerMainModel(this.scene); + this.extraModel = new ViewerModel(this.scene); - this.InitNavigation (); - this.InitShading (); + this.InitNavigation(); + this.InitShading(); - this.Render (); + this.Render(); } - SetMouseClickHandler (onMouseClick) - { - this.navigation.SetMouseClickHandler (onMouseClick); + SetMouseClickHandler(onMouseClick) { + this.navigation.SetMouseClickHandler(onMouseClick); } - SetMouseMoveHandler (onMouseMove) - { - this.navigation.SetMouseMoveHandler (onMouseMove); + SetMouseMoveHandler(onMouseMove) { + this.navigation.SetMouseMoveHandler(onMouseMove); } - SetContextMenuHandler (onContext) - { - this.navigation.SetContextMenuHandler (onContext); + SetContextMenuHandler(onContext) { + this.navigation.SetContextMenuHandler(onContext); } - SetEdgeSettings (edgeSettings) - { - let newEdgeSettings = edgeSettings.Clone (); - this.mainModel.SetEdgeSettings (newEdgeSettings); - this.Render (); + SetEdgeSettings(edgeSettings) { + let newEdgeSettings = edgeSettings.Clone(); + this.mainModel.SetEdgeSettings(newEdgeSettings); + this.Render(); } - SetEnvironmentMapSettings (environmentSettings) - { - let newEnvironmentSettings = environmentSettings.Clone (); - this.shadingModel.SetEnvironmentMapSettings (newEnvironmentSettings, () => { - this.Render (); + SetEnvironmentMapSettings(environmentSettings) { + let newEnvironmentSettings = environmentSettings.Clone(); + this.shadingModel.SetEnvironmentMapSettings(newEnvironmentSettings, () => { + this.Render(); }); - this.shadingModel.UpdateShading (); - this.Render (); + this.shadingModel.UpdateShading(); + this.Render(); } - SetBackgroundColor (color) - { - let bgColor = new THREE.Color ( - ColorComponentToFloat (color.r), - ColorComponentToFloat (color.g), - ColorComponentToFloat (color.b) + SetBackgroundColor(color) { + let bgColor = new THREE.Color( + ColorComponentToFloat(color.r), + ColorComponentToFloat(color.g), + ColorComponentToFloat(color.b) ); - let alpha = ColorComponentToFloat (color.a); - this.renderer.setClearColor (bgColor, alpha); - this.Render (); + let alpha = ColorComponentToFloat(color.a); + this.renderer.setClearColor(bgColor, alpha); + this.Render(); } - GetCanvas () - { + GetCanvas() { return this.canvas; } - GetCamera () - { - return this.navigation.GetCamera (); + GetCamera() { + return this.navigation.GetCamera(); } - GetProjectionMode () - { + GetProjectionMode() { return this.projectionMode; } - SetCamera (camera) - { - this.navigation.SetCamera (camera); - this.cameraValidator.ForceUpdate (); - this.Render (); + SetCamera(camera) { + this.navigation.SetCamera(camera); + this.cameraValidator.ForceUpdate(); + this.Render(); } - SetProjectionMode (projectionMode) - { + SetProjectionMode(projectionMode) { if (this.projectionMode === projectionMode) { return; } - this.scene.remove (this.camera); + this.scene.remove(this.camera); if (projectionMode === ProjectionMode.Perspective) { - this.camera = new THREE.PerspectiveCamera (45.0, 1.0, 0.1, 1000.0); + this.camera = new THREE.PerspectiveCamera(45.0, 1.0, 0.1, 1000.0); } else if (projectionMode === ProjectionMode.Orthographic) { - this.camera = new THREE.OrthographicCamera (-1.0, 1.0, 1.0, -1.0, 0.1, 1000.0); + this.camera = new THREE.OrthographicCamera(-1.0, 1.0, 1.0, -1.0, 0.1, 1000.0); } - this.scene.add (this.camera); + this.scene.add(this.camera); this.projectionMode = projectionMode; - this.shadingModel.SetProjectionMode (projectionMode); - this.cameraValidator.ForceUpdate (); + this.shadingModel.SetProjectionMode(projectionMode); + this.cameraValidator.ForceUpdate(); - this.AdjustClippingPlanes (); - this.Render (); + this.AdjustClippingPlanes(); + this.Render(); } - Resize (width, height) - { - let innerSize = GetDomElementInnerDimensions (this.canvas, width, height); - this.ResizeRenderer (innerSize.width, innerSize.height); + Resize(width, height) { + let innerSize = GetDomElementInnerDimensions(this.canvas, width, height); + this.ResizeRenderer(innerSize.width, innerSize.height); } - ResizeRenderer (width, height) - { + ResizeRenderer(width, height) { if (window.devicePixelRatio) { - this.renderer.setPixelRatio (window.devicePixelRatio); + this.renderer.setPixelRatio(window.devicePixelRatio); } - this.renderer.setSize (width, height); - this.cameraValidator.ForceUpdate (); - this.Render (); + this.renderer.setSize(width, height); + this.cameraValidator.ForceUpdate(); + this.Render(); } - FitSphereToWindow (boundingSphere, animation) - { + FitSphereToWindow(boundingSphere, animation) { if (boundingSphere === null) { return; } - let center = new Coord3D (boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z); + let center = new Coord3D(boundingSphere.center.x, boundingSphere.center.y, boundingSphere.center.z); let radius = boundingSphere.radius; - let newCamera = this.navigation.GetFitToSphereCamera (center, radius); - this.navigation.MoveCamera (newCamera, animation ? this.settings.animationSteps : 0); + let newCamera = this.navigation.GetFitToSphereCamera(center, radius); + this.navigation.MoveCamera(newCamera, animation ? this.settings.animationSteps : 0); } - AdjustClippingPlanes () - { - let boundingSphere = this.GetBoundingSphere ((meshUserData) => { + AdjustClippingPlanes() { + let boundingSphere = this.GetBoundingSphere((meshUserData) => { return true; }); - this.AdjustClippingPlanesToSphere (boundingSphere); + this.AdjustClippingPlanesToSphere(boundingSphere); } - AdjustClippingPlanesToSphere (boundingSphere) - { + AdjustClippingPlanesToSphere(boundingSphere) { if (boundingSphere === null) { return; } @@ -349,129 +317,118 @@ export class Viewer this.camera.far = 1000000.0; } - this.cameraValidator.ForceUpdate (); - this.Render (); + this.cameraValidator.ForceUpdate(); + this.Render(); } - GetNavigationMode () - { - return this.navigation.GetNavigationMode (); + GetNavigationMode() { + return this.navigation.GetNavigationMode(); } - SetNavigationMode (navigationMode) - { - let oldCamera = this.navigation.GetCamera (); - let newCamera = this.upVector.SetFixed (navigationMode === NavigationMode.FixedUpVector, oldCamera); - this.navigation.SetNavigationMode (navigationMode); + SetNavigationMode(navigationMode) { + let oldCamera = this.navigation.GetCamera(); + let newCamera = this.upVector.SetFixed(navigationMode === NavigationMode.FixedUpVector, oldCamera); + this.navigation.SetNavigationMode(navigationMode); if (newCamera !== null) { - this.navigation.MoveCamera (newCamera, this.settings.animationSteps); + this.navigation.MoveCamera(newCamera, this.settings.animationSteps); } - this.Render (); + this.Render(); } - SetUpVector (upDirection, animate) - { - let oldCamera = this.navigation.GetCamera (); - let newCamera = this.upVector.SetDirection (upDirection, oldCamera); + SetUpVector(upDirection, animate) { + let oldCamera = this.navigation.GetCamera(); + let newCamera = this.upVector.SetDirection(upDirection, oldCamera); let animationSteps = animate ? this.settings.animationSteps : 0; - this.navigation.MoveCamera (newCamera, animationSteps); - this.Render (); + this.navigation.MoveCamera(newCamera, animationSteps); + this.Render(); } - FlipUpVector () - { - let oldCamera = this.navigation.GetCamera (); - let newCamera = this.upVector.Flip (oldCamera); - this.navigation.MoveCamera (newCamera, 0); - this.Render (); + FlipUpVector() { + let oldCamera = this.navigation.GetCamera(); + let newCamera = this.upVector.Flip(oldCamera); + this.navigation.MoveCamera(newCamera, 0); + this.Render(); } - Render () - { - let navigationCamera = this.navigation.GetCamera (); + Render() { + let navigationCamera = this.navigation.GetCamera(); - this.camera.position.set (navigationCamera.eye.x, navigationCamera.eye.y, navigationCamera.eye.z); - this.camera.up.set (navigationCamera.up.x, navigationCamera.up.y, navigationCamera.up.z); - this.camera.lookAt (new THREE.Vector3 (navigationCamera.center.x, navigationCamera.center.y, navigationCamera.center.z)); + this.camera.position.set(navigationCamera.eye.x, navigationCamera.eye.y, navigationCamera.eye.z); + this.camera.up.set(navigationCamera.up.x, navigationCamera.up.y, navigationCamera.up.z); + this.camera.lookAt(new THREE.Vector3(navigationCamera.center.x, navigationCamera.center.y, navigationCamera.center.z)); if (this.projectionMode === ProjectionMode.Perspective) { - if (!this.cameraValidator.ValidatePerspective ()) { + if (!this.cameraValidator.ValidatePerspective()) { this.camera.aspect = this.canvas.width / this.canvas.height; this.camera.fov = navigationCamera.fov; - this.camera.updateProjectionMatrix (); + this.camera.updateProjectionMatrix(); } } else if (this.projectionMode === ProjectionMode.Orthographic) { - let eyeCenterDistance = CoordDistance3D (navigationCamera.eye, navigationCamera.center); - if (!this.cameraValidator.ValidateOrthographic (eyeCenterDistance)) { + let eyeCenterDistance = CoordDistance3D(navigationCamera.eye, navigationCamera.center); + if (!this.cameraValidator.ValidateOrthographic(eyeCenterDistance)) { let aspect = this.canvas.width / this.canvas.height; - let eyeCenterDistance = CoordDistance3D (navigationCamera.eye, navigationCamera.center); - let frustumHalfHeight = eyeCenterDistance * Math.tan (0.5 * navigationCamera.fov * DegRad); + let eyeCenterDistance = CoordDistance3D(navigationCamera.eye, navigationCamera.center); + let frustumHalfHeight = eyeCenterDistance * Math.tan(0.5 * navigationCamera.fov * DegRad); this.camera.left = -frustumHalfHeight * aspect; this.camera.right = frustumHalfHeight * aspect; this.camera.top = frustumHalfHeight; this.camera.bottom = -frustumHalfHeight; - this.camera.updateProjectionMatrix (); + this.camera.updateProjectionMatrix(); } } - this.shadingModel.UpdateByCamera (navigationCamera); - this.renderer.render (this.scene, this.camera); + this.shadingModel.UpdateByCamera(navigationCamera); + this.renderer.render(this.scene, this.camera); } - SetMainObject (object) - { - const shadingType = GetShadingTypeOfObject (object); - this.mainModel.SetMainObject (object); - this.shadingModel.SetShadingType (shadingType); + SetMainObject(object) { + const shadingType = GetShadingTypeOfObject(object); + this.mainModel.SetMainObject(object); + this.shadingModel.SetShadingType(shadingType); - this.Render (); + this.Render(); } - AddExtraObject (object) - { - this.extraModel.AddObject (object); - this.Render (); + AddExtraObject(object) { + this.extraModel.AddObject(object); + this.Render(); } - Clear () - { - this.mainModel.Clear (); - this.extraModel.Clear (); - this.Render (); + Clear() { + this.mainModel.Clear(); + this.extraModel.Clear(); + this.Render(); } - ClearExtra () - { - this.extraModel.Clear (); - this.Render (); + ClearExtra() { + this.extraModel.Clear(); + this.Render(); } - SetMeshesVisibility (isVisible) - { - this.mainModel.EnumerateMeshesAndLines ((mesh) => { - let visible = isVisible (mesh.userData); + SetMeshesVisibility(isVisible) { + this.mainModel.EnumerateMeshesAndLines((mesh) => { + let visible = isVisible(mesh.userData); if (mesh.visible !== visible) { mesh.visible = visible; } }); - this.mainModel.EnumerateEdges ((edge) => { - let visible = isVisible (edge.userData); + this.mainModel.EnumerateEdges((edge) => { + let visible = isVisible(edge.userData); if (edge.visible !== visible) { edge.visible = visible; } }); - this.Render (); + this.Render(); } - SetMeshesHighlight (highlightColor, isHighlighted) - { - let withPolygonOffset = this.mainModel.HasLinesOrEdges (); - this.mainModel.EnumerateMeshesAndLines ((mesh) => { - let highlighted = isHighlighted (mesh.userData); + SetMeshesHighlight(highlightColor, isHighlighted) { + let withPolygonOffset = this.mainModel.HasLinesOrEdges(); + this.mainModel.EnumerateMeshesAndLines((mesh) => { + let highlighted = isHighlighted(mesh.userData); if (highlighted) { if (mesh.userData.threeMaterials === null) { mesh.userData.threeMaterials = mesh.material; - mesh.material = CreateHighlightMaterials (mesh.userData.threeMaterials, highlightColor, withPolygonOffset); + mesh.material = CreateHighlightMaterials(mesh.userData.threeMaterials, highlightColor, withPolygonOffset); } } else { if (mesh.userData.threeMaterials !== null) { @@ -481,85 +438,87 @@ export class Viewer } }); - this.Render (); + this.Render(); } - GetMeshUserDataUnderMouse (intersectionMode, mouseCoords) - { - let intersection = this.GetMeshIntersectionUnderMouse (intersectionMode, mouseCoords); + GetMeshUserDataUnderMouse(intersectionMode, mouseCoords) { + let intersection = this.GetMeshIntersectionUnderMouse(intersectionMode, mouseCoords); if (intersection === null) { return null; } return intersection.object.userData; } - GetMeshIntersectionUnderMouse (intersectionMode, mouseCoords) - { - let canvasSize = this.GetCanvasSize (); - let intersection = this.mainModel.GetMeshIntersectionUnderMouse (intersectionMode, mouseCoords, this.camera, canvasSize.width, canvasSize.height); + GetMeshIntersectionUnderMouse(intersectionMode, mouseCoords) { + let canvasSize = this.GetCanvasSize(); + let intersection = this.mainModel.GetMeshIntersectionUnderMouse(intersectionMode, mouseCoords, this.camera, canvasSize.width, canvasSize.height); if (intersection === null) { return null; } return intersection; } - GetBoundingBox (needToProcess) - { - return this.mainModel.GetBoundingBox (needToProcess); + GetBoundingBox(needToProcess) { + return this.mainModel.GetBoundingBox(needToProcess); } - GetBoundingSphere (needToProcess) - { - return this.mainModel.GetBoundingSphere (needToProcess); + GetBoundingSphere(needToProcess) { + return this.mainModel.GetBoundingSphere(needToProcess); } - EnumerateMeshesAndLinesUserData (enumerator) - { - this.mainModel.EnumerateMeshesAndLines ((mesh) => { - enumerator (mesh.userData); + EnumerateMeshesAndLinesUserData(enumerator) { + this.mainModel.EnumerateMeshesAndLines((mesh) => { + enumerator(mesh.userData); }); } - InitNavigation () - { - let camera = GetDefaultCamera (Direction.Y); - this.camera = new THREE.PerspectiveCamera (45.0, 1.0, 0.1, 1000.0); + EnumerateMeshesAndLines(enumerator) { + this.mainModel.EnumerateMeshesAndLines((mesh) => { + enumerator(mesh); + }); + } + + EnumerateEdges(enumerator) { + this.mainModel.EnumerateEdges((edge) => { + enumerator(edge); + }); + } + + InitNavigation() { + let camera = GetDefaultCamera(Direction.Y); + this.camera = new THREE.PerspectiveCamera(45.0, 1.0, 0.1, 1000.0); this.projectionMode = ProjectionMode.Perspective; - this.cameraValidator = new CameraValidator (); - this.scene.add (this.camera); + this.cameraValidator = new CameraValidator(); + this.scene.add(this.camera); let canvasElem = this.renderer.domElement; - this.navigation = new Navigation (canvasElem, camera, { - onUpdate : () => { - this.Render (); + this.navigation = new Navigation(canvasElem, camera, { + onUpdate: () => { + this.Render(); } }); - this.upVector = new UpVector (); + this.upVector = new UpVector(); } - InitShading () - { - this.shadingModel = new ShadingModel (this.scene); + InitShading() { + this.shadingModel = new ShadingModel(this.scene); } - GetShadingType () - { + GetShadingType() { return this.shadingModel.type; } - GetImageSize () - { - let originalSize = new THREE.Vector2 (); - this.renderer.getSize (originalSize); + GetImageSize() { + let originalSize = new THREE.Vector2(); + this.renderer.getSize(originalSize); return { - width : parseInt (originalSize.x, 10), - height : parseInt (originalSize.y, 10) + width: parseInt(originalSize.x, 10), + height: parseInt(originalSize.y, 10) }; } - GetCanvasSize () - { + GetCanvasSize() { let width = this.canvas.width; let height = this.canvas.height; if (window.devicePixelRatio) { @@ -567,35 +526,33 @@ export class Viewer height /= window.devicePixelRatio; } return { - width : width, - height : height + width: width, + height: height }; } - GetImageAsDataUrl (width, height, isTransparent) - { - let originalSize = this.GetImageSize (); + GetImageAsDataUrl(width, height, isTransparent) { + let originalSize = this.GetImageSize(); let renderWidth = width; let renderHeight = height; if (window.devicePixelRatio) { renderWidth /= window.devicePixelRatio; renderHeight /= window.devicePixelRatio; } - let clearAlpha = this.renderer.getClearAlpha (); + let clearAlpha = this.renderer.getClearAlpha(); if (isTransparent) { - this.renderer.setClearAlpha (0.0); + this.renderer.setClearAlpha(0.0); } - this.ResizeRenderer (renderWidth, renderHeight); - this.Render (); - let url = this.renderer.domElement.toDataURL (); - this.ResizeRenderer (originalSize.width, originalSize.height); - this.renderer.setClearAlpha (clearAlpha); + this.ResizeRenderer(renderWidth, renderHeight); + this.Render(); + let url = this.renderer.domElement.toDataURL(); + this.ResizeRenderer(originalSize.width, originalSize.height); + this.renderer.setClearAlpha(clearAlpha); return url; } - Destroy () - { - this.Clear (); - this.renderer.dispose (); + Destroy() { + this.Clear(); + this.renderer.dispose(); } } diff --git a/source/website/css/O3DVIcons/O3DVIcons.woff b/source/website/css/O3DVIcons/O3DVIcons.woff index 2e28dd7c..6c73da0c 100644 Binary files a/source/website/css/O3DVIcons/O3DVIcons.woff and b/source/website/css/O3DVIcons/O3DVIcons.woff differ diff --git a/source/website/css/controls.css b/source/website/css/controls.css index 61893ae7..df0b3255 100644 --- a/source/website/css/controls.css +++ b/source/website/css/controls.css @@ -1,58 +1,48 @@ -div.ov_svg_icon -{ +div.ov_svg_icon { color: var(--ov_icon_color); font-size: 18px; width: 18px; height: 18px; } -div.ov_svg_icon.left -{ +div.ov_svg_icon.left { margin-right: 10px; float: left; } -div.ov_svg_icon.left_inline -{ +div.ov_svg_icon.left_inline { margin-right: 10px; margin-top: 2px; float: left; } -div.ov_svg_icon.light -{ +div.ov_svg_icon.light { color: var(--ov_light_icon_color); } -div.ov_svg_icon.selected -{ +div.ov_svg_icon.selected { color: var(--ov_selected_icon_color); } -div.ov_svg_icon.disabled -{ +div.ov_svg_icon.disabled { color: var(--ov_disabled_icon_color); } -div.ov_thin_scrollbar -{ +div.ov_thin_scrollbar { scrollbar-color: var(--ov_border_color) transparent; scrollbar-width: thin; } -div.ov_thin_scrollbar::-webkit-scrollbar -{ +div.ov_thin_scrollbar::-webkit-scrollbar { width: 3px; height: 3px; } -div.ov_thin_scrollbar::-webkit-scrollbar-thumb -{ +div.ov_thin_scrollbar::-webkit-scrollbar-thumb { background: #cccccc; } -div.ov_button -{ +div.ov_button { color: var(--ov_button_text_color); background: var(--ov_button_color); text-align: center; @@ -62,15 +52,13 @@ div.ov_button cursor: pointer; } -div.ov_button.outline -{ +div.ov_button.outline { color: var(--ov_outline_button_text_color); background: transparent; border: 1px solid var(--ov_outline_button_color); } -div.ov_tooltip -{ +div.ov_tooltip { color: var(--ov_dialog_foreground_color); background: var(--ov_dialog_background_color); padding: 5px 10px; @@ -79,25 +67,21 @@ div.ov_tooltip box-shadow: var(--ov_shadow); } -input[type=text] -{ +input[type=text] { color: var(--ov_dialog_foreground_color); background: var(--ov_dialog_background_color); } -input[type=text]:disabled -{ +input[type=text]:disabled { color: var(--ov_disabled_foreground_color); } -textarea -{ +textarea { color: var(--ov_dialog_foreground_color); background: var(--ov_dialog_background_color); } -input.ov_radio_button -{ +input.ov_radio_button { position: relative; top: 2px; width: 14px; @@ -111,13 +95,11 @@ input.ov_radio_button appearance: none; } -input.ov_radio_button:checked -{ +input.ov_radio_button:checked { border: 5px solid var(--ov_button_color); } -input.ov_checkbox -{ +input.ov_checkbox { position: relative; top: 4px; width: 14px; @@ -131,32 +113,28 @@ input.ov_checkbox appearance: none; } -input.ov_checkbox:checked -{ +input.ov_checkbox:checked { background-color: var(--ov_button_color); background-image: url('O3DVIcons/checkmark.svg'); background-position: center; border: 0px; } -div.ov_select_container -{ +div.ov_select_container { position: relative; } -div.ov_select_container:after -{ +div.ov_select_container:after { font-family: "O3DVIcons"; font-size: 18px; - content: "\f101"; + content: "\f101"; position: absolute; - right: 6px; - top: 6px; + right: 6px; + top: 6px; pointer-events: none; } -select.ov_select -{ +select.ov_select { color: var(--ov_dialog_foreground_color); background: var(--ov_dialog_background_color); font-size: 16px; @@ -169,8 +147,7 @@ select.ov_select appearance: none; } -input.ov_slider -{ +input.ov_slider { height: 1px; background: var(--ov_border_color); outline: none; @@ -179,8 +156,7 @@ input.ov_slider appearance: none; } -input.ov_slider::-webkit-slider-thumb -{ +input.ov_slider::-webkit-slider-thumb { background: var(--ov_button_color); width: 14px; height: 14px; @@ -190,8 +166,7 @@ input.ov_slider::-webkit-slider-thumb appearance: none; } -input.ov_slider::-moz-range-thumb -{ +input.ov_slider::-moz-range-thumb { background: var(--ov_button_color); width: 14px; height: 14px; @@ -200,15 +175,13 @@ input.ov_slider::-moz-range-thumb cursor: pointer; } -span.ov_slider_label -{ +span.ov_slider_label { margin-left: 10px; position: relative; bottom: -4px; } -div.ov_toggle -{ +div.ov_toggle { width: 24px; height: 8px; padding: 2px; @@ -218,8 +191,7 @@ div.ov_toggle cursor: pointer; } -div.ov_toggle_slider -{ +div.ov_toggle_slider { width: 6px; height: 6px; transition: .4s; @@ -227,36 +199,30 @@ div.ov_toggle_slider border: 1px solid var(--ov_foreground_color); } -div.ov_toggle.on -{ +div.ov_toggle.on { background: var(--ov_foreground_color); } -div.ov_toggle.on div.ov_toggle_slider -{ +div.ov_toggle.on div.ov_toggle_slider { background: var(--ov_background_color); transform: translateX(16px); border: 1px solid var(--ov_background_color); } -@media (hover) -{ +@media (hover) { -div.ov_svg_icon.selected:hover -{ - color: var(--ov_hover_text_color); -} + div.ov_svg_icon.selected:hover { + color: var(--ov_hover_text_color); + } -div.ov_button:hover -{ - background: var(--ov_button_hover_color); - border: 1px solid var(--ov_button_hover_color); -} + div.ov_button:hover { + background: var(--ov_button_hover_color); + border: 1px solid var(--ov_button_hover_color); + } -div.ov_button.outline:hover -{ - background: var(--ov_outline_button_hover_color); - border: 1px solid var(--ov_outline_button_color); -} + div.ov_button.outline:hover { + background: var(--ov_outline_button_hover_color); + border: 1px solid var(--ov_outline_button_color); + } -} +} \ No newline at end of file diff --git a/source/website/css/icons.css b/source/website/css/icons.css index 589f4440..c6f07c28 100644 --- a/source/website/css/icons.css +++ b/source/website/css/icons.css @@ -1,6 +1,6 @@ @font-face { font-family: "O3DVIcons"; - src: url("O3DVIcons/O3DVIcons.woff?d27bdb5af135068ed4a9350e285e132e") format("woff"); + src: url("O3DVIcons/O3DVIcons.woff?a183607fabf1ae69b129cd0cbb17c310") format("woff"); } i[class^="icon-"]:before, i[class*=" icon-"]:before { @@ -56,108 +56,111 @@ i[class^="icon-"]:before, i[class*=" icon-"]:before { .icon-expand:before { content: "\f10e"; } -.icon-export:before { +.icon-explode:before { content: "\f10f"; } -.icon-feedback:before { +.icon-export:before { content: "\f110"; } -.icon-file_download:before { +.icon-feedback:before { content: "\f111"; } -.icon-files:before { +.icon-file_download:before { content: "\f112"; } -.icon-fit:before { +.icon-files:before { content: "\f113"; } -.icon-fix_up_off:before { +.icon-fit:before { content: "\f114"; } -.icon-fix_up_on:before { +.icon-fix_up_off:before { content: "\f115"; } -.icon-flat_list:before { +.icon-fix_up_on:before { content: "\f116"; } -.icon-flip:before { +.icon-flat_list:before { content: "\f117"; } -.icon-github:before { +.icon-flip:before { content: "\f118"; } -.icon-hidden:before { +.icon-github:before { content: "\f119"; } -.icon-info:before { +.icon-hidden:before { content: "\f11a"; } -.icon-isolate:before { +.icon-info:before { content: "\f11b"; } -.icon-light_mode:before { +.icon-isolate:before { content: "\f11c"; } -.icon-materials:before { +.icon-light_mode:before { content: "\f11d"; } -.icon-measure_angle:before { +.icon-materials:before { content: "\f11e"; } -.icon-measure_distance_parallel:before { +.icon-measure_angle:before { content: "\f11f"; } -.icon-measure_distance:before { +.icon-measure_distance_parallel:before { content: "\f120"; } -.icon-measure:before { +.icon-measure_distance:before { content: "\f121"; } -.icon-meshes:before { +.icon-measure:before { content: "\f122"; } -.icon-missing_files:before { +.icon-meshes:before { content: "\f123"; } -.icon-model:before { +.icon-missing_files:before { content: "\f124"; } -.icon-open_url:before { +.icon-model:before { content: "\f125"; } -.icon-open:before { +.icon-open_url:before { content: "\f126"; } -.icon-print3d:before { +.icon-open:before { content: "\f127"; } -.icon-settings:before { +.icon-print3d:before { content: "\f128"; } -.icon-share:before { +.icon-settings:before { content: "\f129"; } -.icon-snapshot:before { +.icon-share:before { content: "\f12a"; } -.icon-tree_mesh:before { +.icon-snapshot:before { content: "\f12b"; } -.icon-tree_view:before { +.icon-tree_mesh:before { content: "\f12c"; } -.icon-twitter:before { +.icon-tree_view:before { content: "\f12d"; } -.icon-up_y:before { +.icon-twitter:before { content: "\f12e"; } -.icon-up_z:before { +.icon-up_y:before { content: "\f12f"; } -.icon-visible:before { +.icon-up_z:before { content: "\f130"; } -.icon-warning:before { +.icon-visible:before { content: "\f131"; } +.icon-warning:before { + content: "\f132"; +} diff --git a/source/website/css/website.css b/source/website/css/website.css index 62c214d7..e153abaa 100644 --- a/source/website/css/website.css +++ b/source/website/css/website.css @@ -1,5 +1,4 @@ -div.ov_color_circle -{ +div.ov_color_circle { background: #ffffff; border: 1px solid #000000; width: 14px; @@ -10,32 +9,27 @@ div.ov_color_circle border-radius: 10px; } -div.header -{ +div.header { overflow: auto; display: none; } -div.title -{ +div.title { padding: 6px 10px; overflow: auto; } -div.title div.title_left -{ +div.title div.title_left { float: left; } -div.title svg.logo_image -{ +div.title svg.logo_image { width: 190px; height: 40px; float: left; } -div.title div.logo_text -{ +div.title div.logo_text { color: var(--ov_foreground_color); font-size: 18px; font-weight: bold; @@ -43,13 +37,11 @@ div.title div.logo_text float: left; } -div.title div.title_right -{ +div.title div.title_right { float: right; } -div.title_right a -{ +div.title_right a { color: var(--ov_foreground_color); padding: 11px 5px; display: block; @@ -57,13 +49,11 @@ div.title_right a text-decoration: none; } -div.toolbar -{ +div.toolbar { background: var(--ov_toolbar_background_color); } -div.intro -{ +div.intro { margin: 10px; padding: 10px; text-align: center; @@ -72,47 +62,40 @@ div.intro display: none; } -div.intro_content -{ +div.intro_content { width: 500px; max-width: 90%; margin: 10px auto; position: relative; } -div.intro div.intro_logo -{ +div.intro div.intro_logo { border-bottom: 1px solid var(--ov_border_color); padding-bottom: 30px; margin-bottom: 30px; } -div.intro svg.intro_logo -{ +div.intro svg.intro_logo { width: 381px; height: 80px; max-width: 90%; margin-bottom: 20px; } -div.intro div.intro_dragdrop_text -{ +div.intro div.intro_dragdrop_text { font-size: 30px; } -div.intro div.intro_formats_title -{ +div.intro div.intro_formats_title { font-size: 25px; margin-bottom: 15px; } -div.intro div.intro_file_formats -{ +div.intro div.intro_file_formats { margin: 0px auto; } -div.intro div.intro_file_formats a -{ +div.intro div.intro_file_formats a { color: var(--ov_outline_button_text_color); text-decoration: none; font-size: 17px; @@ -125,31 +108,26 @@ div.intro div.intro_file_formats a cursor: pointer; } -div.intro div.intro_file_formats a:hover -{ +div.intro div.intro_file_formats a:hover { background: var(--ov_outline_button_hover_color); } -div.noembed -{ +div.noembed { text-align: center; padding: 10px; } -div.noembed a -{ +div.noembed a { padding: 10px 0px; display: block; } -div.main -{ +div.main { overflow: hidden; display: none; } -div.main_file_name -{ +div.main_file_name { margin: 10px auto; white-space: nowrap; text-align: center; @@ -157,80 +135,68 @@ div.main_file_name overflow: hidden; } -div.main_left_container -{ +div.main_left_container { float: left; overflow: auto; } -div.main_navigator -{ +div.main_navigator { width: 280px; margin: 10px 0px 10px 0px; overflow: none; float: left; } -div.main_splitter -{ +div.main_splitter { width: 10px; overflow: none; float: left; cursor: w-resize; } -div.main_viewer -{ +div.main_viewer { float: left; } -div.main_right_container -{ +div.main_right_container { float: left; overflow: auto; } -div.main_sidebar -{ +div.main_sidebar { width: 280px; margin: 10px 0px 10px 0px; overflow: none; float: left; } -div.main_viewer canvas -{ +div.main_viewer canvas { margin: 10px 0px 10px 0px; border: 1px solid var(--ov_border_color); outline: none; display: block; } -div.ov_toolbar -{ +div.ov_toolbar { overflow: auto; user-select: none; } -div.ov_toolbar div.ov_toolbar_button -{ +div.ov_toolbar div.ov_toolbar_button { float: left; cursor: pointer; padding: 10px; } -div.ov_toolbar div.ov_toolbar_button.align_right -{ +div.ov_toolbar div.ov_toolbar_button.align_right { float: right; } -div.ov_toolbar div.ov_toolbar_button.selected -{ +div.ov_toolbar div.ov_toolbar_button.selected { background: var(--ov_toolbar_selected_color); } -div.ov_toolbar div.ov_toolbar_separator -{ +div.ov_toolbar div.ov_toolbar_separator { background: var(--ov_toolbar_separator_color); width: 1px; height: 28px; @@ -238,50 +204,42 @@ div.ov_toolbar div.ov_toolbar_separator float: left; } -div.pcr-app -{ +div.pcr-app { color: var(--ov_dialog_foreground_color); background: var(--ov_dialog_background_color); } -div.pcr-app input.pcr-result -{ +div.pcr-app input.pcr-result { color: var(--ov_foreground_color) !important; background: var(--ov_background_color) !important; } -div.ov_property_table -{ +div.ov_property_table { overflow: auto; } -div.ov_property_table_custom -{ +div.ov_property_table_custom { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--ov_border_color); } -div.ov_property_table div.ov_property_table_row -{ +div.ov_property_table div.ov_property_table_row { overflow: auto; } -div.ov_property_table div.ov_property_table_row.group -{ +div.ov_property_table div.ov_property_table_row.group { padding: 4px 0px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } -div.ov_property_table div.ov_property_table_row.ingroup -{ +div.ov_property_table div.ov_property_table_row.ingroup { margin-left: 15px; } -div.ov_property_table div.ov_property_table_cell -{ +div.ov_property_table div.ov_property_table_cell { padding: 4px 0px; white-space: nowrap; text-overflow: ellipsis; @@ -289,28 +247,24 @@ div.ov_property_table div.ov_property_table_cell box-sizing: border-box; } -div.ov_property_table div.ov_property_table_name -{ +div.ov_property_table div.ov_property_table_name { width: 49%; padding-right: 2%; float: left; } -div.ov_property_table div.ov_property_table_value -{ +div.ov_property_table div.ov_property_table_value { width: 49%; text-align: left; float: left; } -div.ov_property_table div.ov_property_table_button -{ +div.ov_property_table div.ov_property_table_button { color: var(--ov_button_color); cursor: pointer; } -div.ov_bottom_floating_panel -{ +div.ov_bottom_floating_panel { background: var(--ov_background_color); border-top: 1px solid var(--ov_border_color); width: 100%; @@ -320,97 +274,103 @@ div.ov_bottom_floating_panel bottom: 0px; } -div.ov_bottom_floating_panel div.ov_floating_panel_text -{ +div.ov_bottom_floating_panel div.ov_floating_panel_text { padding: 3px; margin-bottom: 10px; float: left; } -div.ov_bottom_floating_panel div.ov_floating_panel_button -{ +div.ov_bottom_floating_panel div.ov_floating_panel_button { width: 120px; float: right; } -div.ov_measure_panel -{ +div.ov_measure_panel { padding: 6px 15px; position: absolute; border-radius: 30px; - left : 0px; - top : 0px; + left: 0px; + top: 0px; } -div.ov_measure_panel div.ov_svg_icon -{ +div.ov_measure_panel div.ov_svg_icon { color: inherit; margin-bottom: 2px; } -div.ov_measure_panel div.ov_measure_value -{ +div.ov_measure_panel div.ov_measure_value { float: left; margin-right: 10px; } -@media (hover) -{ - -div.title_right div.header_button:hover -{ - color: var(--ov_button_color); +div.ov_explode_panel { + padding: 15px 20px; + position: absolute; + border-radius: 10px; + background: var(--ov_background_color); + border: 1px solid var(--ov_border_color); + left: 0px; + bottom: 0px; + min-width: 300px; + z-index: 100; } -div.ov_toolbar div.ov_toolbar_button:hover -{ - background: var(--ov_hover_color); +div.ov_explode_slider { + width: 250px; + margin: 0px 10px; } +div.ov_toolbar_button.ov_hidden, +div.ov_toolbar_separator.ov_hidden { + display: none; } -@media (max-width: 350px), (max-height: 620px) -{ +@media (hover) { -div.intro_content -{ - margin: 0px auto; -} + div.title_right div.header_button:hover { + color: var(--ov_button_color); + } -div.intro div.intro_logo -{ - display: none; -} + div.ov_toolbar div.ov_toolbar_button:hover { + background: var(--ov_hover_color); + } } -@media (max-width: 800px) -{ +@media (max-width: 350px), +(max-height: 620px) { -div.intro_content -{ - width: auto; -} + div.intro_content { + margin: 0px auto; + } -div.main_viewer canvas -{ - border: 0px; - margin: 0px; -} + div.intro div.intro_logo { + display: none; + } -div.ov_dialog -{ - max-width: 80%; } -div.ov_progress -{ - max-width: 80%; -} +@media (max-width: 800px) { -div.ov_bottom_floating_panel -{ - padding: 10px; -} + div.intro_content { + width: auto; + } -} + div.main_viewer canvas { + border: 0px; + margin: 0px; + } + + div.ov_dialog { + max-width: 80%; + } + + div.ov_progress { + max-width: 80%; + } + + div.ov_bottom_floating_panel { + padding: 10px; + } + +} \ No newline at end of file diff --git a/source/website/explodetool.js b/source/website/explodetool.js new file mode 100644 index 00000000..4a5fc121 --- /dev/null +++ b/source/website/explodetool.js @@ -0,0 +1,445 @@ +import { AddDiv, AddDomElement } from '../engine/viewer/domutils.js'; +import { Loc } from '../engine/core/localization.js'; +import * as THREE from 'three'; + +export class ExplodeTool { + constructor(viewer, settings) { + this.viewer = viewer; + this.settings = settings; + this.isActive = false; + this.explodeValue = 0.0; + this.originalPositions = new Map(); + this.originalEdgePositions = new Map(); + this.meshToEdgeMap = new Map(); // Maps mesh uuid to edge uuid + this.modelCenter = new THREE.Vector3(); + this.boundingSphereRadius = 0.0; + + this.panel = null; + this.slider = null; + this.button = null; + this.separator = null; + } + + SetButton(button) { + this.button = button; + } + + SetSeparator(separator) { + this.separator = separator; + } + + IsActive() { + return this.isActive; + } + + SetActive(isActive) { + if (this.isActive === isActive) { + return; + } + this.isActive = isActive; + this.button.SetSelected(isActive); + if (this.isActive) { + let meshCount = this.InitializeExplode(); + if (meshCount <= 1) { + // Not enough meshes to explode, deactivate + this.isActive = false; + this.button.SetSelected(false); + return; + } + this.CreatePanel(); + this.Resize(); + } else { + this.ResetExplode(); + this.panel.remove(); + this.panel = null; + this.slider = null; + } + } + + GetMeshCount() { + let meshCount = 0; + this.viewer.EnumerateMeshesAndLines((obj) => { + obj.traverse((child) => { + if (child.isMesh) { + meshCount++; + } + }); + }); + return meshCount; + } + + UpdateButtonVisibility() { + let meshCount = this.GetMeshCount(); + if (meshCount <= 1) { + this.button.AddClass('ov_hidden'); + if (this.separator) { + this.separator.classList.add('ov_hidden'); + } + } else { + this.button.RemoveClass('ov_hidden'); + if (this.separator) { + this.separator.classList.remove('ov_hidden'); + } + } + } + + InitializeExplode() { + // Calculate the center of the model + let boundingSphere = this.viewer.GetBoundingSphere((meshUserData) => { + return true; + }); + + this.modelCenter.copy(boundingSphere.center); + this.boundingSphereRadius = boundingSphere.radius; + + // Store original positions and mesh centers for ALL meshes (including nested ones) + this.originalPositions.clear(); + this.originalEdgePositions.clear(); + this.meshToEdgeMap.clear(); + let meshCount = 0; + + // Get the scene's root object and traverse EVERYTHING + this.viewer.EnumerateMeshesAndLines((obj) => { + // Traverse this object and all its children + obj.traverse((child) => { + if (child.isMesh) { + meshCount++; + + // Update world matrices first! + child.updateMatrixWorld(true); + + // Store original LOCAL position + let originalLocalPos = new THREE.Vector3(); + originalLocalPos.copy(child.position); + + // Get the mesh's world position + let worldPos = new THREE.Vector3(); + child.getWorldPosition(worldPos); + + // Calculate the mesh's geometric center (in world space) + if (!child.geometry.boundingBox) { + child.geometry.computeBoundingBox(); + } + let boundingBox = child.geometry.boundingBox; + let meshCenter = new THREE.Vector3(); + boundingBox.getCenter(meshCenter); + + // Transform to world space + let worldCenter = meshCenter.clone(); + child.localToWorld(worldCenter); + + // Calculate direction from model center to mesh center + let direction = new THREE.Vector3(); + direction.subVectors(worldCenter, this.modelCenter); + + // If the mesh is at the center, generate a random direction NOW (not later) + if (direction.length() < 0.001) { + direction.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5); + direction.normalize(); + } else { + direction.normalize(); + } + + this.originalPositions.set(child.uuid, { + localPosition: originalLocalPos, + worldPosition: worldPos, + center: worldCenter, + direction: direction, + parent: child.parent, + userData: child.userData + }); + } + }); + }); + + // Store original edge positions and map them to their meshes + this.viewer.EnumerateEdges((edge) => { + edge.updateMatrixWorld(true); + + // Store original position + let originalLocalPos = new THREE.Vector3(); + originalLocalPos.copy(edge.position); + + // Get world position + let worldPos = new THREE.Vector3(); + edge.getWorldPosition(worldPos); + + this.originalEdgePositions.set(edge.uuid, { + localPosition: originalLocalPos, + worldPosition: worldPos, + parent: edge.parent, + userData: edge.userData + }); + + // Find the corresponding mesh using userData + // Edge userData should match mesh userData + for (let [meshUuid, meshData] of this.originalPositions.entries()) { + if (meshData.userData === edge.userData) { + this.meshToEdgeMap.set(meshUuid, edge.uuid); + break; + } + } + }); + + return meshCount; + } + + SetExplodeValue(value) { + this.explodeValue = value; + this.ApplyExplode(); + this.viewer.Render(); + } + + RefreshEdges() { + // This method should be called when edges are toggled on during explode + // It will initialize the edge positions and apply the current explode state + if (!this.isActive) { + return; + } + + // Clear existing edge data + this.originalEdgePositions.clear(); + this.meshToEdgeMap.clear(); + + // Store original edge positions and map them to their meshes + this.viewer.EnumerateEdges((edge) => { + edge.updateMatrixWorld(true); + + // Store original position (in current exploded state) + let originalLocalPos = new THREE.Vector3(); + originalLocalPos.copy(edge.position); + + // Get world position + let worldPos = new THREE.Vector3(); + edge.getWorldPosition(worldPos); + + // We need to "unexplode" the world position to get the true original + // Find the corresponding mesh + let correspondingMeshData = null; + for (let [meshUuid, meshData] of this.originalPositions.entries()) { + if (meshData.userData === edge.userData) { + correspondingMeshData = meshData; + this.meshToEdgeMap.set(meshUuid, edge.uuid); + break; + } + } + + // If we found the mesh, calculate the original edge position + if (correspondingMeshData) { + // The edge was just generated at the mesh's current (exploded) position + // We need to store what the original position would have been + let maxDistance = this.boundingSphereRadius * 2.0; + let direction = correspondingMeshData.direction.clone(); + let offset = direction.multiplyScalar(maxDistance * this.explodeValue); + + // The current world position includes the explode offset + // So the original world position is current minus offset + let originalWorldPos = new THREE.Vector3(); + originalWorldPos.subVectors(worldPos, offset); + + this.originalEdgePositions.set(edge.uuid, { + localPosition: originalLocalPos, + worldPosition: originalWorldPos, + parent: edge.parent, + userData: edge.userData + }); + + // Now apply the explode to this edge (it will be applied in ApplyExplode) + } else { + // No corresponding mesh found, just store current position + this.originalEdgePositions.set(edge.uuid, { + localPosition: originalLocalPos, + worldPosition: worldPos, + parent: edge.parent, + userData: edge.userData + }); + } + }); + + // Apply current explode state to the new edges + if (this.explodeValue > 0) { + this.ApplyExplode(); + } + } + + ApplyExplode() { + let maxDistance = this.boundingSphereRadius * 2.0; + + // Traverse ALL objects including nested children + this.viewer.EnumerateMeshesAndLines((obj) => { + obj.traverse((child) => { + if (child.isMesh) { + let meshData = this.originalPositions.get(child.uuid); + if (meshData) { + // Use the pre-calculated direction (already normalized) + let direction = meshData.direction.clone(); + + // Apply explode offset in world space + let offset = direction.multiplyScalar(maxDistance * this.explodeValue); + let newWorldPos = new THREE.Vector3(); + newWorldPos.addVectors(meshData.worldPosition, offset); + + // Convert world position back to local position + if (child.parent) { + let parentWorldMatrix = new THREE.Matrix4(); + child.parent.matrixWorld.decompose( + new THREE.Vector3(), + new THREE.Quaternion(), + new THREE.Vector3() + ); + + // Transform world position to parent's local space + let parentInverse = new THREE.Matrix4(); + parentInverse.copy(child.parent.matrixWorld).invert(); + let newLocalPos = newWorldPos.clone(); + newLocalPos.applyMatrix4(parentInverse); + + child.position.copy(newLocalPos); + } else { + // No parent, world = local + child.position.copy(newWorldPos); + } + + child.updateMatrix(); + } + } + }); + }); + + // Apply the same transformation to edges + this.viewer.EnumerateEdges((edge) => { + let edgeData = this.originalEdgePositions.get(edge.uuid); + if (edgeData) { + // Find the corresponding mesh for this edge + let correspondingMeshUuid = null; + for (let [meshUuid, edgeUuid] of this.meshToEdgeMap.entries()) { + if (edgeUuid === edge.uuid) { + correspondingMeshUuid = meshUuid; + break; + } + } + + if (correspondingMeshUuid) { + let meshData = this.originalPositions.get(correspondingMeshUuid); + if (meshData) { + // Use the same direction and offset as the mesh + let direction = meshData.direction.clone(); + let offset = direction.multiplyScalar(maxDistance * this.explodeValue); + let newWorldPos = new THREE.Vector3(); + newWorldPos.addVectors(edgeData.worldPosition, offset); + + // Convert world position back to local position + if (edge.parent) { + let parentInverse = new THREE.Matrix4(); + parentInverse.copy(edge.parent.matrixWorld).invert(); + let newLocalPos = newWorldPos.clone(); + newLocalPos.applyMatrix4(parentInverse); + + edge.position.copy(newLocalPos); + } else { + // No parent, world = local + edge.position.copy(newWorldPos); + } + + edge.updateMatrix(); + } + } + } + }); + } + + ResetExplode() { + // Reset all meshes to their original positions + this.viewer.EnumerateMeshesAndLines((obj) => { + obj.traverse((child) => { + if (child.isMesh) { + let meshData = this.originalPositions.get(child.uuid); + if (meshData) { + child.position.copy(meshData.localPosition); + child.updateMatrix(); + } + } + }); + }); + + // Reset all edges to their original positions + this.viewer.EnumerateEdges((edge) => { + let edgeData = this.originalEdgePositions.get(edge.uuid); + if (edgeData) { + edge.position.copy(edgeData.localPosition); + edge.updateMatrix(); + } + }); + + this.originalPositions.clear(); + this.originalEdgePositions.clear(); + this.meshToEdgeMap.clear(); + this.explodeValue = 0.0; + this.viewer.Render(); + } + + CreatePanel() { + this.panel = AddDiv(document.body, 'ov_explode_panel'); + + // Add label + let label = AddDiv(this.panel, null); + label.innerHTML = Loc('Explode View'); + label.style.display = 'inline-block'; + label.style.marginRight = '10px'; + label.style.fontWeight = 'bold'; + + // Add slider + this.slider = AddDomElement(this.panel, 'input', 'ov_explode_slider'); + this.slider.setAttribute('type', 'range'); + this.slider.setAttribute('min', '0'); + this.slider.setAttribute('max', '100'); + this.slider.setAttribute('value', '0'); + this.slider.style.verticalAlign = 'middle'; + + // Add value display + let valueDisplay = AddDiv(this.panel, null); + valueDisplay.innerHTML = '0%'; + valueDisplay.style.display = 'inline-block'; + valueDisplay.style.marginLeft = '10px'; + valueDisplay.style.minWidth = '40px'; + + this.slider.addEventListener('input', (ev) => { + let value = parseInt(ev.target.value, 10) / 100.0; + this.SetExplodeValue(value); + valueDisplay.innerHTML = parseInt(ev.target.value, 10) + '%'; + }); + + this.UpdatePanelStyle(); + } + + UpdatePanelStyle() { + if (!this.panel) { + return; + } + + if (this.settings.backgroundIsEnvMap) { + this.panel.style.color = '#ffffff'; + this.panel.style.backgroundColor = 'rgba(0,0,0,0.7)'; + } else { + this.panel.style.color = 'var(--ov_foreground_color)'; + this.panel.style.backgroundColor = 'var(--ov_background_color)'; + } + } + + Resize() { + if (!this.isActive || !this.panel) { + return; + } + + let canvas = this.viewer.GetCanvas(); + let canvasRect = canvas.getBoundingClientRect(); + let panelRect = this.panel.getBoundingClientRect(); + let canvasWidth = canvasRect.right - canvasRect.left; + let panelWidth = panelRect.right - panelRect.left; + + // Center horizontally at the bottom of the canvas + this.panel.style.left = (canvasRect.left + (canvasWidth - panelWidth) / 2) + 'px'; + this.panel.style.bottom = '20px'; + } +} + diff --git a/source/website/index.js b/source/website/index.js index 8b089746..4b108763 100644 --- a/source/website/index.js +++ b/source/website/index.js @@ -9,6 +9,7 @@ import { ShowMessageDialog } from './dialogs.js'; import * as Engine from '../engine/main.js'; export { Engine }; +export { Website }; import './css/icons.css'; import './css/themes.css'; @@ -30,63 +31,59 @@ export const UI = { Loc }; -export function SetWebsiteEventHandler (eventHandler) -{ - SetEventHandler (eventHandler); +export function SetWebsiteEventHandler(eventHandler) { + SetEventHandler(eventHandler); } -export function RegisterHeaderPlugin (plugin) -{ - RegisterPlugin (PluginType.Header, plugin); +export function RegisterHeaderPlugin(plugin) { + RegisterPlugin(PluginType.Header, plugin); } -export function RegisterToolbarPlugin (plugin) -{ - RegisterPlugin (PluginType.Toolbar, plugin); +export function RegisterToolbarPlugin(plugin) { + RegisterPlugin(PluginType.Toolbar, plugin); } -export function StartWebsite () -{ - window.addEventListener ('load', () => { +export function StartWebsite() { + window.addEventListener('load', () => { if (window.self !== window.top) { - let noEmbeddingDiv = AddDiv (document.body, 'noembed'); - AddDiv (noEmbeddingDiv, null, Loc ('Embedding Online 3D Viewer in an iframe is not supported.')); - let link = AddDomElement (noEmbeddingDiv, 'a', null, Loc ('Open Online 3D Viewer')); + let noEmbeddingDiv = AddDiv(document.body, 'noembed'); + AddDiv(noEmbeddingDiv, null, Loc('Embedding Online 3D Viewer in an iframe is not supported.')); + let link = AddDomElement(noEmbeddingDiv, 'a', null, Loc('Open Online 3D Viewer')); link.target = '_blank'; link.href = window.self.location; return; } - document.getElementById ('intro_dragdrop_text').innerHTML = Loc ('Drag and drop 3D models here.'); - document.getElementById ('intro_formats_title').innerHTML = Loc ('Check an example file:'); + document.getElementById('intro_dragdrop_text').innerHTML = Loc('Drag and drop 3D models here.'); + document.getElementById('intro_formats_title').innerHTML = Loc('Check an example file:'); - let website = new Website ({ - headerDiv : document.getElementById ('header'), - headerButtonsDiv : document.getElementById ('header_buttons'), - toolbarDiv : document.getElementById ('toolbar'), - mainDiv : document.getElementById ('main'), - introDiv : document.getElementById ('intro'), - fileNameDiv : document.getElementById ('main_file_name'), - leftContainerDiv : document.getElementById ('main_left_container'), - navigatorDiv : document.getElementById ('main_navigator'), - navigatorSplitterDiv : document.getElementById ('main_navigator_splitter'), - rightContainerDiv : document.getElementById ('main_right_container'), - sidebarDiv : document.getElementById ('main_sidebar'), - sidebarSplitterDiv : document.getElementById ('main_sidebar_splitter'), - viewerDiv : document.getElementById ('main_viewer'), - fileInput : document.getElementById ('open_file') + let website = new Website({ + headerDiv: document.getElementById('header'), + headerButtonsDiv: document.getElementById('header_buttons'), + toolbarDiv: document.getElementById('toolbar'), + mainDiv: document.getElementById('main'), + introDiv: document.getElementById('intro'), + introContentDiv: document.getElementById('intro_content'), + fileNameDiv: document.getElementById('main_file_name'), + leftContainerDiv: document.getElementById('main_left_container'), + navigatorDiv: document.getElementById('main_navigator'), + navigatorSplitterDiv: document.getElementById('main_navigator_splitter'), + rightContainerDiv: document.getElementById('main_right_container'), + sidebarDiv: document.getElementById('main_sidebar'), + sidebarSplitterDiv: document.getElementById('main_sidebar_splitter'), + viewerDiv: document.getElementById('main_viewer'), + fileInput: document.getElementById('open_file') }); - website.Load (); + website.Load(); }); } -export function StartEmbed () -{ - window.addEventListener ('load', () => { - let embed = new Embed ({ - viewerDiv : document.getElementById ('embed_viewer'), - websiteLinkDiv : document.getElementById ('website_link') +export function StartEmbed() { + window.addEventListener('load', () => { + let embed = new Embed({ + viewerDiv: document.getElementById('embed_viewer'), + websiteLinkDiv: document.getElementById('website_link') }); - embed.Load (); + embed.Load(); }); } diff --git a/source/website/website.js b/source/website/website.js index 682cc26b..1dbde994 100644 --- a/source/website/website.js +++ b/source/website/website.js @@ -23,6 +23,7 @@ import { GetDefaultMaterials, ReplaceDefaultMaterialsColor } from '../engine/mod import { Direction } from '../engine/geometry/geometry.js'; import { CookieGetBoolVal, CookieSetBoolVal } from './cookiehandler.js'; import { MeasureTool } from './measuretool.js'; +import { ExplodeTool } from './explodetool.js'; import { CloseAllDialogs } from './dialog.js'; import { CreateVerticalSplitter } from './splitter.js'; import { EnumeratePlugins, PluginType } from './pluginregistry.js'; @@ -32,71 +33,67 @@ import { Loc } from '../engine/core/localization.js'; const WebsiteUIState = { - Undefined : 0, - Intro : 1, - Model : 2, - Loading : 3 + Undefined: 0, + Intro: 1, + Model: 2, + Loading: 3 }; -class WebsiteLayouter -{ - constructor (parameters, navigator, sidebar, viewer, measureTool) - { +class WebsiteLayouter { + constructor(parameters, navigator, sidebar, viewer, measureTool, explodeTool) { this.parameters = parameters; this.navigator = navigator; this.sidebar = sidebar; this.viewer = viewer; this.measureTool = measureTool; + this.explodeTool = explodeTool; this.limits = { - minPanelWidth : 290, - minCanvasWidth : 100 + minPanelWidth: 290, + minCanvasWidth: 100 }; } - Init () - { - this.InstallSplitter (this.parameters.navigatorSplitterDiv, this.parameters.navigatorDiv, (originalWidth, xDiff) => { + Init() { + this.InstallSplitter(this.parameters.navigatorSplitterDiv, this.parameters.navigatorDiv, (originalWidth, xDiff) => { let newWidth = originalWidth + xDiff; - this.OnSplitterDragged (newWidth - this.navigator.GetWidth (), 0); + this.OnSplitterDragged(newWidth - this.navigator.GetWidth(), 0); }); - this.InstallSplitter (this.parameters.sidebarSplitterDiv, this.parameters.sidebarDiv, (originalWidth, xDiff) => { + this.InstallSplitter(this.parameters.sidebarSplitterDiv, this.parameters.sidebarDiv, (originalWidth, xDiff) => { let newWidth = originalWidth - xDiff; - this.OnSplitterDragged (0, newWidth - this.sidebar.GetWidth ()); + this.OnSplitterDragged(0, newWidth - this.sidebar.GetWidth()); }); - this.Resize (); + this.Resize(); } - InstallSplitter (splitterDiv, resizedDiv, onSplit) - { + InstallSplitter(splitterDiv, resizedDiv, onSplit) { let originalWidth = null; - CreateVerticalSplitter (splitterDiv, { - onSplitStart : () => { - originalWidth = GetDomElementOuterWidth (resizedDiv); + CreateVerticalSplitter(splitterDiv, { + onSplitStart: () => { + originalWidth = GetDomElementOuterWidth(resizedDiv); }, - onSplit : (xDiff) => { - onSplit (originalWidth, xDiff); + onSplit: (xDiff) => { + onSplit(originalWidth, xDiff); } }); } - OnSplitterDragged (leftDiff, rightDiff) - { + OnSplitterDragged(leftDiff, rightDiff) { let windowWidth = window.innerWidth; - let navigatorWidth = this.navigator.GetWidth (); - let sidebarWidth = this.sidebar.GetWidth (); + let navigatorWidth = this.navigator.GetWidth(); + let sidebarWidth = this.sidebar.GetWidth(); - let leftWidth = GetDomElementOuterWidth (this.parameters.leftContainerDiv); - let rightWidth = GetDomElementOuterWidth (this.parameters.rightContainerDiv); + let leftWidth = GetDomElementOuterWidth(this.parameters.leftContainerDiv); + let rightWidth = GetDomElementOuterWidth(this.parameters.rightContainerDiv); let newLeftWidth = leftWidth + leftDiff; let newRightWidth = rightWidth + rightDiff; let contentNewWidth = windowWidth - newLeftWidth - newRightWidth; - let isNavigatorVisible = this.navigator.IsPanelsVisible (); - let isSidebarVisible = this.sidebar.IsPanelsVisible (); + let isNavigatorVisible = this.navigator.IsPanelsVisible(); + let isSidebarVisible = this.sidebar.IsPanelsVisible(); if (isNavigatorVisible && newLeftWidth < this.limits.minPanelWidth) { newLeftWidth = this.limits.minPanelWidth; @@ -116,18 +113,17 @@ class WebsiteLayouter if (isNavigatorVisible) { let newNavigatorWidth = navigatorWidth + (newLeftWidth - leftWidth); - this.navigator.SetWidth (newNavigatorWidth); + this.navigator.SetWidth(newNavigatorWidth); } if (isSidebarVisible) { let newSidebarWidth = sidebarWidth + (newRightWidth - rightWidth); - this.sidebar.SetWidth (newSidebarWidth); + this.sidebar.SetWidth(newSidebarWidth); } - this.Resize (); + this.Resize(); } - Resize () - { + Resize() { let windowWidth = window.innerWidth; let windowHeight = window.innerHeight; let headerHeight = this.parameters.headerDiv.offsetHeight; @@ -135,9 +131,9 @@ class WebsiteLayouter let leftWidth = 0; let rightWidth = 0; let safetyMargin = 0; - if (!IsSmallWidth ()) { - leftWidth = GetDomElementOuterWidth (this.parameters.leftContainerDiv); - rightWidth = GetDomElementOuterWidth (this.parameters.rightContainerDiv); + if (!IsSmallWidth()) { + leftWidth = GetDomElementOuterWidth(this.parameters.leftContainerDiv); + rightWidth = GetDomElementOuterWidth(this.parameters.rightContainerDiv); safetyMargin = 1; } @@ -147,108 +143,108 @@ class WebsiteLayouter if (contentWidth < this.limits.minCanvasWidth) { let neededIncrease = this.limits.minCanvasWidth - contentWidth; - let isNavigatorVisible = this.navigator.IsPanelsVisible (); - let isSidebarVisible = this.sidebar.IsPanelsVisible (); + let isNavigatorVisible = this.navigator.IsPanelsVisible(); + let isSidebarVisible = this.sidebar.IsPanelsVisible(); if (neededIncrease > 0 && isNavigatorVisible) { - let navigatorDecrease = Math.min (neededIncrease, leftWidth - this.limits.minPanelWidth); - this.navigator.SetWidth (this.navigator.GetWidth () - navigatorDecrease); + let navigatorDecrease = Math.min(neededIncrease, leftWidth - this.limits.minPanelWidth); + this.navigator.SetWidth(this.navigator.GetWidth() - navigatorDecrease); neededIncrease = neededIncrease - navigatorDecrease; } if (neededIncrease > 0 && isSidebarVisible) { - let sidebarDecrease = Math.min (neededIncrease, rightWidth - this.limits.minPanelWidth); - this.sidebar.SetWidth (this.sidebar.GetWidth () - sidebarDecrease); + let sidebarDecrease = Math.min(neededIncrease, rightWidth - this.limits.minPanelWidth); + this.sidebar.SetWidth(this.sidebar.GetWidth() - sidebarDecrease); } - leftWidth = GetDomElementOuterWidth (this.parameters.leftContainerDiv); - rightWidth = GetDomElementOuterWidth (this.parameters.rightContainerDiv); + leftWidth = GetDomElementOuterWidth(this.parameters.leftContainerDiv); + rightWidth = GetDomElementOuterWidth(this.parameters.rightContainerDiv); contentWidth = windowWidth - leftWidth - rightWidth; } - this.navigator.Resize (contentHeight); - SetDomElementOuterHeight (this.parameters.navigatorSplitterDiv, contentHeight); + this.navigator.Resize(contentHeight); + SetDomElementOuterHeight(this.parameters.navigatorSplitterDiv, contentHeight); + + this.sidebar.Resize(contentHeight); + SetDomElementOuterHeight(this.parameters.sidebarSplitterDiv, contentHeight); - this.sidebar.Resize (contentHeight); - SetDomElementOuterHeight (this.parameters.sidebarSplitterDiv, contentHeight); + SetDomElementOuterHeight(this.parameters.introDiv, contentHeight); + this.viewer.Resize(contentWidth - safetyMargin, contentHeight); - SetDomElementOuterHeight (this.parameters.introDiv, contentHeight); - this.viewer.Resize (contentWidth - safetyMargin, contentHeight); + let introContentHeight = GetDomElementOuterHeight(this.parameters.introContentDiv); + let introContentTop = (contentHeight - introContentHeight) / 3.0; + this.parameters.introContentDiv.style.top = introContentTop.toString() + 'px'; - this.measureTool.Resize (); + this.measureTool.Resize(); + this.explodeTool.Resize(); } } -export class Website -{ - constructor (parameters) - { +export class Website { + constructor(parameters) { this.parameters = parameters; - this.settings = new Settings (Theme.Light); - this.cameraSettings = new CameraSettings (); - this.viewer = new Viewer (); - this.measureTool = new MeasureTool (this.viewer, this.settings); - this.hashHandler = new HashHandler (); - this.toolbar = new Toolbar (this.parameters.toolbarDiv); - this.navigator = new Navigator (this.parameters.navigatorDiv); - this.sidebar = new Sidebar (this.parameters.sidebarDiv, this.settings); - this.modelLoaderUI = new ThreeModelLoaderUI (); - this.themeHandler = new ThemeHandler (); - this.highlightColor = new RGBColor (142, 201, 240); + this.settings = new Settings(Theme.Light); + this.cameraSettings = new CameraSettings(); + this.viewer = new Viewer(); + this.measureTool = new MeasureTool(this.viewer, this.settings); + this.explodeTool = new ExplodeTool(this.viewer, this.settings); + this.hashHandler = new HashHandler(); + this.toolbar = new Toolbar(this.parameters.toolbarDiv); + this.navigator = new Navigator(this.parameters.navigatorDiv); + this.sidebar = new Sidebar(this.parameters.sidebarDiv, this.settings); + this.modelLoaderUI = new ThreeModelLoaderUI(); + this.themeHandler = new ThemeHandler(); + this.highlightColor = new RGBColor(142, 201, 240); this.uiState = WebsiteUIState.Undefined; - this.layouter = new WebsiteLayouter (this.parameters, this.navigator, this.sidebar, this.viewer, this.measureTool); + this.layouter = new WebsiteLayouter(this.parameters, this.navigator, this.sidebar, this.viewer, this.measureTool, this.explodeTool); this.model = null; } - Load () - { - this.settings.LoadFromCookies (); - this.cameraSettings.LoadFromCookies (); + Load() { + this.settings.LoadFromCookies(); + this.cameraSettings.LoadFromCookies(); - this.SwitchTheme (this.settings.themeId, false); - HandleEvent ('theme_on_load', this.settings.themeId === Theme.Light ? 'light' : 'dark'); + this.SwitchTheme(this.settings.themeId, false); + HandleEvent('theme_on_load', this.settings.themeId === Theme.Light ? 'light' : 'dark'); - EnumeratePlugins (PluginType.Header, (plugin) => { - plugin.registerButtons ({ - createHeaderButton : (icon, title, link) => { - this.CreateHeaderButton (icon, title, link); + EnumeratePlugins(PluginType.Header, (plugin) => { + plugin.registerButtons({ + createHeaderButton: (icon, title, link) => { + this.CreateHeaderButton(icon, title, link); } }); }); - this.InitViewer (); - this.InitToolbar (); - this.InitDragAndDrop (); - this.InitSidebar (); - this.InitNavigator (); - this.InitCookieConsent (); + this.InitViewer(); + this.InitToolbar(); + this.InitDragAndDrop(); + this.InitSidebar(); + this.InitNavigator(); + this.InitCookieConsent(); - this.viewer.SetMouseClickHandler (this.OnModelClicked.bind (this)); - this.viewer.SetMouseMoveHandler (this.OnModelMouseMoved.bind (this)); - this.viewer.SetContextMenuHandler (this.OnModelContextMenu.bind (this)); + this.viewer.SetMouseClickHandler(this.OnModelClicked.bind(this)); + this.viewer.SetMouseMoveHandler(this.OnModelMouseMoved.bind(this)); + this.viewer.SetContextMenuHandler(this.OnModelContextMenu.bind(this)); - this.layouter.Init (); - this.SetUIState (WebsiteUIState.Intro); + this.layouter.Init(); + this.SetUIState(WebsiteUIState.Intro); - this.hashHandler.SetEventListener (this.OnHashChange.bind (this)); - this.OnHashChange (); + this.hashHandler.SetEventListener(this.OnHashChange.bind(this)); + this.OnHashChange(); - window.addEventListener ('resize', () => { - this.layouter.Resize (); - }); + window.addEventListener('resize', () => { + this.layouter.Resize(); + }); } - HasLoadedModel () - { + HasLoadedModel() { return this.model !== null; } - SetUIState (uiState) - { - function ShowOnlyOnModelElements (show) - { - let root = document.querySelector (':root'); - root.style.setProperty ('--ov_only_on_model_display', show ? 'inherit' : 'none'); + SetUIState(uiState) { + function ShowOnlyOnModelElements(show) { + let root = document.querySelector(':root'); + root.style.setProperty('--ov_only_on_model_display', show ? 'inherit' : 'none'); } if (this.uiState === uiState) { @@ -257,301 +253,282 @@ export class Website this.uiState = uiState; if (this.uiState === WebsiteUIState.Intro) { - ShowDomElement (this.parameters.introDiv, true); - ShowDomElement (this.parameters.headerDiv, true); - ShowDomElement (this.parameters.mainDiv, false); - ShowOnlyOnModelElements (false); + ShowDomElement(this.parameters.introDiv, true); + ShowDomElement(this.parameters.headerDiv, true); + ShowDomElement(this.parameters.mainDiv, false); + ShowOnlyOnModelElements(false); } else if (this.uiState === WebsiteUIState.Model) { - ShowDomElement (this.parameters.introDiv, false); - ShowDomElement (this.parameters.headerDiv, true); - ShowDomElement (this.parameters.mainDiv, true); - ShowOnlyOnModelElements (true); - this.UpdatePanelsVisibility (); + ShowDomElement(this.parameters.introDiv, false); + ShowDomElement(this.parameters.headerDiv, true); + ShowDomElement(this.parameters.mainDiv, true); + ShowOnlyOnModelElements(true); + this.UpdatePanelsVisibility(); } else if (this.uiState === WebsiteUIState.Loading) { - ShowDomElement (this.parameters.introDiv, false); - ShowDomElement (this.parameters.headerDiv, true); - ShowDomElement (this.parameters.mainDiv, false); - ShowOnlyOnModelElements (false); + ShowDomElement(this.parameters.introDiv, false); + ShowDomElement(this.parameters.headerDiv, true); + ShowDomElement(this.parameters.mainDiv, false); + ShowOnlyOnModelElements(false); } - this.layouter.Resize (); + this.layouter.Resize(); } - ClearModel () - { - CloseAllDialogs (); + ClearModel() { + CloseAllDialogs(); this.model = null; - this.viewer.Clear (); + this.viewer.Clear(); this.parameters.fileNameDiv.innerHTML = ''; - this.navigator.Clear (); - this.sidebar.Clear (); + this.navigator.Clear(); + this.sidebar.Clear(); - this.measureTool.SetActive (false); + this.measureTool.SetActive(false); + this.explodeTool.SetActive(false); } - OnModelLoaded (importResult, threeObject) - { + OnModelLoaded(importResult, threeObject) { this.model = importResult.model; this.parameters.fileNameDiv.innerHTML = importResult.mainFile; - this.viewer.SetMainObject (threeObject); - this.viewer.SetUpVector (Direction.Y, false); - this.navigator.FillTree (importResult); - this.sidebar.UpdateControlsVisibility (); - this.FitModelToWindow (true); + this.viewer.SetMainObject(threeObject); + this.viewer.SetUpVector(Direction.Y, false); + this.navigator.FillTree(importResult); + this.sidebar.UpdateControlsVisibility(); + this.explodeTool.UpdateButtonVisibility(); + this.FitModelToWindow(true); } - OnModelClicked (button, mouseCoordinates) - { + OnModelClicked(button, mouseCoordinates) { if (button !== 1) { return; } - if (this.measureTool.IsActive ()) { - this.measureTool.Click (mouseCoordinates); + if (this.measureTool.IsActive()) { + this.measureTool.Click(mouseCoordinates); return; } - let meshUserData = this.viewer.GetMeshUserDataUnderMouse (IntersectionMode.MeshAndLine, mouseCoordinates); + let meshUserData = this.viewer.GetMeshUserDataUnderMouse(IntersectionMode.MeshAndLine, mouseCoordinates); if (meshUserData === null) { - this.navigator.SetSelection (null); + this.navigator.SetSelection(null); } else { - this.navigator.SetSelection (new Selection (SelectionType.Mesh, meshUserData.originalMeshInstance.id)); + this.navigator.SetSelection(new Selection(SelectionType.Mesh, meshUserData.originalMeshInstance.id)); } } - OnModelMouseMoved (mouseCoordinates) - { - if (this.measureTool.IsActive ()) { - this.measureTool.MouseMove (mouseCoordinates); + OnModelMouseMoved(mouseCoordinates) { + if (this.measureTool.IsActive()) { + this.measureTool.MouseMove(mouseCoordinates); } } - OnModelContextMenu (globalMouseCoordinates, mouseCoordinates) - { - let meshUserData = this.viewer.GetMeshUserDataUnderMouse (IntersectionMode.MeshAndLine, mouseCoordinates); + OnModelContextMenu(globalMouseCoordinates, mouseCoordinates) { + let meshUserData = this.viewer.GetMeshUserDataUnderMouse(IntersectionMode.MeshAndLine, mouseCoordinates); let items = []; if (meshUserData === null) { - items.push ({ - name : Loc ('Fit model to window'), - icon : 'fit', - onClick : () => { - this.FitModelToWindow (false); + items.push({ + name: Loc('Fit model to window'), + icon: 'fit', + onClick: () => { + this.FitModelToWindow(false); } }); - if (this.navigator.HasHiddenMesh ()) { - items.push ({ - name : Loc ('Show all meshes'), - icon : 'visible', - onClick : () => { - this.navigator.ShowAllMeshes (true); + if (this.navigator.HasHiddenMesh()) { + items.push({ + name: Loc('Show all meshes'), + icon: 'visible', + onClick: () => { + this.navigator.ShowAllMeshes(true); } }); } } else { - items.push ({ - name : Loc ('Hide mesh'), - icon : 'hidden', - onClick : () => { - this.navigator.ToggleMeshVisibility (meshUserData.originalMeshInstance.id); + items.push({ + name: Loc('Hide mesh'), + icon: 'hidden', + onClick: () => { + this.navigator.ToggleMeshVisibility(meshUserData.originalMeshInstance.id); } }); - items.push ({ - name : Loc ('Fit mesh to window'), - icon : 'fit', - onClick : () => { - this.navigator.FitMeshToWindow (meshUserData.originalMeshInstance.id); + items.push({ + name: Loc('Fit mesh to window'), + icon: 'fit', + onClick: () => { + this.navigator.FitMeshToWindow(meshUserData.originalMeshInstance.id); } }); - if (this.navigator.MeshItemCount () > 1) { - let isMeshIsolated = this.navigator.IsMeshIsolated (meshUserData.originalMeshInstance.id); - items.push ({ - name : isMeshIsolated ? Loc ('Remove isolation') : Loc ('Isolate mesh'), - icon : isMeshIsolated ? 'deisolate' : 'isolate', - onClick : () => { + if (this.navigator.MeshItemCount() > 1) { + let isMeshIsolated = this.navigator.IsMeshIsolated(meshUserData.originalMeshInstance.id); + items.push({ + name: isMeshIsolated ? Loc('Remove isolation') : Loc('Isolate mesh'), + icon: isMeshIsolated ? 'deisolate' : 'isolate', + onClick: () => { if (isMeshIsolated) { - this.navigator.ShowAllMeshes (true); + this.navigator.ShowAllMeshes(true); } else { - this.navigator.IsolateMesh (meshUserData.originalMeshInstance.id); + this.navigator.IsolateMesh(meshUserData.originalMeshInstance.id); } } }); } } - ShowListPopup (items, { - calculatePosition : (contentDiv) => { - return CalculatePopupPositionToScreen (globalMouseCoordinates, contentDiv); + ShowListPopup(items, { + calculatePosition: (contentDiv) => { + return CalculatePopupPositionToScreen(globalMouseCoordinates, contentDiv); }, - onClick : (index) => { + onClick: (index) => { let clickedItem = items[index]; - clickedItem.onClick (); + clickedItem.onClick(); } }); } - OnHashChange () - { - if (this.hashHandler.HasHash ()) { - let urls = this.hashHandler.GetModelFilesFromHash (); + OnHashChange() { + if (this.hashHandler.HasHash()) { + let urls = this.hashHandler.GetModelFilesFromHash(); if (urls === null) { return; } - TransformFileHostUrls (urls); - let importSettings = new ImportSettings (); + TransformFileHostUrls(urls); + let importSettings = new ImportSettings(); importSettings.defaultLineColor = this.settings.defaultLineColor; importSettings.defaultColor = this.settings.defaultColor; - let defaultColor = this.hashHandler.GetDefaultColorFromHash (); + let defaultColor = this.hashHandler.GetDefaultColorFromHash(); if (defaultColor !== null) { importSettings.defaultColor = defaultColor; } - HandleEvent ('model_load_started', 'hash'); - this.LoadModelFromUrlList (urls, importSettings); + HandleEvent('model_load_started', 'hash'); + this.LoadModelFromUrlList(urls, importSettings); } else { - this.ClearModel (); - this.SetUIState (WebsiteUIState.Intro); + this.ClearModel(); + this.SetUIState(WebsiteUIState.Intro); } } - OpenFileBrowserDialog () - { - this.parameters.fileInput.click (); + OpenFileBrowserDialog() { + this.parameters.fileInput.click(); } - FitModelToWindow (onLoad) - { + FitModelToWindow(onLoad) { let animation = !onLoad; - let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { - return this.navigator.IsMeshVisible (meshUserData.originalMeshInstance.id); + let boundingSphere = this.viewer.GetBoundingSphere((meshUserData) => { + return this.navigator.IsMeshVisible(meshUserData.originalMeshInstance.id); }); if (onLoad) { - this.viewer.AdjustClippingPlanesToSphere (boundingSphere); + this.viewer.AdjustClippingPlanesToSphere(boundingSphere); } - this.viewer.FitSphereToWindow (boundingSphere, animation); + this.viewer.FitSphereToWindow(boundingSphere, animation); } - FitMeshToWindow (meshInstanceId) - { - let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { - return meshUserData.originalMeshInstance.id.IsEqual (meshInstanceId); + FitMeshToWindow(meshInstanceId) { + let boundingSphere = this.viewer.GetBoundingSphere((meshUserData) => { + return meshUserData.originalMeshInstance.id.IsEqual(meshInstanceId); }); - this.viewer.FitSphereToWindow (boundingSphere, true); + this.viewer.FitSphereToWindow(boundingSphere, true); } - FitMeshesToWindow (meshInstanceIdSet) - { - let meshInstanceIdKeys = new Set (); + FitMeshesToWindow(meshInstanceIdSet) { + let meshInstanceIdKeys = new Set(); for (let meshInstanceId of meshInstanceIdSet) { - meshInstanceIdKeys.add (meshInstanceId.GetKey ()); + meshInstanceIdKeys.add(meshInstanceId.GetKey()); } - let boundingSphere = this.viewer.GetBoundingSphere ((meshUserData) => { - return meshInstanceIdKeys.has (meshUserData.originalMeshInstance.id.GetKey ()); + let boundingSphere = this.viewer.GetBoundingSphere((meshUserData) => { + return meshInstanceIdKeys.has(meshUserData.originalMeshInstance.id.GetKey()); }); - this.viewer.FitSphereToWindow (boundingSphere, true); + this.viewer.FitSphereToWindow(boundingSphere, true); } - UpdateMeshesVisibility () - { - this.viewer.SetMeshesVisibility ((meshUserData) => { - return this.navigator.IsMeshVisible (meshUserData.originalMeshInstance.id); + UpdateMeshesVisibility() { + this.viewer.SetMeshesVisibility((meshUserData) => { + return this.navigator.IsMeshVisible(meshUserData.originalMeshInstance.id); }); } - UpdateMeshesSelection () - { - let selectedMeshId = this.navigator.GetSelectedMeshId (); - this.viewer.SetMeshesHighlight (this.highlightColor, (meshUserData) => { - if (selectedMeshId !== null && meshUserData.originalMeshInstance.id.IsEqual (selectedMeshId)) { + UpdateMeshesSelection() { + let selectedMeshId = this.navigator.GetSelectedMeshId(); + this.viewer.SetMeshesHighlight(this.highlightColor, (meshUserData) => { + if (selectedMeshId !== null && meshUserData.originalMeshInstance.id.IsEqual(selectedMeshId)) { return true; } return false; }); } - LoadModelFromUrlList (urls, settings) - { - let inputFiles = InputFilesFromUrls (urls); - this.LoadModelFromInputFiles (inputFiles, settings); - this.ClearHashIfNotOnlyUrlList (); + LoadModelFromUrlList(urls, settings) { + let inputFiles = InputFilesFromUrls(urls); + this.LoadModelFromInputFiles(inputFiles, settings); + this.ClearHashIfNotOnlyUrlList(); } - LoadModelFromFileList (files) - { - let importSettings = new ImportSettings (); + LoadModelFromFileList(files) { + let importSettings = new ImportSettings(); importSettings.defaultLineColor = this.settings.defaultLineColor; importSettings.defaultColor = this.settings.defaultColor; - let inputFiles = InputFilesFromFileObjects (files); - this.LoadModelFromInputFiles (inputFiles, importSettings); - this.ClearHashIfNotOnlyUrlList (); - } - - LoadModelFromInputFiles (files, settings) - { - this.modelLoaderUI.LoadModel (files, settings, { - onStart : () => - { - this.SetUIState (WebsiteUIState.Loading); - this.ClearModel (); + let inputFiles = InputFilesFromFileObjects(files); + this.LoadModelFromInputFiles(inputFiles, importSettings); + this.ClearHashIfNotOnlyUrlList(); + } + + LoadModelFromInputFiles(files, settings) { + this.modelLoaderUI.LoadModel(files, settings, { + onStart: () => { + this.SetUIState(WebsiteUIState.Loading); + this.ClearModel(); }, - onFinish : (importResult, threeObject) => - { - this.SetUIState (WebsiteUIState.Model); - this.OnModelLoaded (importResult, threeObject); - let importedExtension = GetFileExtension (importResult.mainFile); - HandleEvent ('model_loaded', importedExtension); + onFinish: (importResult, threeObject) => { + this.SetUIState(WebsiteUIState.Model); + this.OnModelLoaded(importResult, threeObject); + let importedExtension = GetFileExtension(importResult.mainFile); + HandleEvent('model_loaded', importedExtension); }, - onRender : () => - { - this.viewer.Render (); + onRender: () => { + this.viewer.Render(); }, - onError : (importError) => - { - this.SetUIState (WebsiteUIState.Intro); + onError: (importError) => { + this.SetUIState(WebsiteUIState.Intro); let extensionStr = null; if (importError.mainFile !== null) { - extensionStr = GetFileExtension (importError.mainFile); + extensionStr = GetFileExtension(importError.mainFile); } else { let extensions = []; - let importer = this.modelLoaderUI.GetImporter (); - let fileList = importer.GetFileList ().GetFiles (); + let importer = this.modelLoaderUI.GetImporter(); + let fileList = importer.GetFileList().GetFiles(); for (let i = 0; i < fileList.length; i++) { let extension = fileList[i].extension; - extensions.push (extension); + extensions.push(extension); } - extensionStr = extensions.join (','); + extensionStr = extensions.join(','); } if (importError.code === ImportErrorCode.NoImportableFile) { - HandleEvent ('no_importable_file', extensionStr); + HandleEvent('no_importable_file', extensionStr); } else if (importError.code === ImportErrorCode.FailedToLoadFile) { - HandleEvent ('failed_to_load_file', extensionStr); + HandleEvent('failed_to_load_file', extensionStr); } else if (importError.code === ImportErrorCode.ImportFailed) { - HandleEvent ('import_failed', extensionStr, { - error_message : importError.message + HandleEvent('import_failed', extensionStr, { + error_message: importError.message }); } } }); } - ClearHashIfNotOnlyUrlList () - { - let importer = this.modelLoaderUI.GetImporter (); - let isOnlyUrl = importer.GetFileList ().IsOnlyUrlSource (); - if (!isOnlyUrl && this.hashHandler.HasHash ()) { - this.hashHandler.SkipNextEventHandler (); - this.hashHandler.ClearHash (); + ClearHashIfNotOnlyUrlList() { + let importer = this.modelLoaderUI.GetImporter(); + let isOnlyUrl = importer.GetFileList().IsOnlyUrlSource(); + if (!isOnlyUrl && this.hashHandler.HasHash()) { + this.hashHandler.SkipNextEventHandler(); + this.hashHandler.ClearHash(); } } - UpdateEdgeDisplay () - { - this.settings.SaveToCookies (); - this.viewer.SetEdgeSettings (this.settings.edgeSettings); + UpdateEdgeDisplay() { + this.settings.SaveToCookies(); + this.viewer.SetEdgeSettings(this.settings.edgeSettings); + this.explodeTool.RefreshEdges(); } - UpdateEnvironmentMap () - { + UpdateEnvironmentMap() { let envMapPath = 'assets/envmaps/' + this.settings.environmentMapName + '/'; let envMapTextures = [ envMapPath + 'posx.jpg', @@ -561,416 +538,410 @@ export class Website envMapPath + 'posz.jpg', envMapPath + 'negz.jpg' ]; - let environmentSettings = new EnvironmentSettings (envMapTextures, this.settings.backgroundIsEnvMap); - this.viewer.SetEnvironmentMapSettings (environmentSettings); + let environmentSettings = new EnvironmentSettings(envMapTextures, this.settings.backgroundIsEnvMap); + this.viewer.SetEnvironmentMapSettings(environmentSettings); } - SwitchTheme (newThemeId, resetColors) - { + SwitchTheme(newThemeId, resetColors) { this.settings.themeId = newThemeId; - this.themeHandler.SwitchTheme (this.settings.themeId); + this.themeHandler.SwitchTheme(this.settings.themeId); if (resetColors) { - let defaultSettings = new Settings (this.settings.themeId); + let defaultSettings = new Settings(this.settings.themeId); this.settings.backgroundColor = defaultSettings.backgroundColor; this.settings.defaultLineColor = defaultSettings.defaultLineColor; this.settings.defaultColor = defaultSettings.defaultColor; - this.sidebar.UpdateControlsStatus (); + this.sidebar.UpdateControlsStatus(); - this.viewer.SetBackgroundColor (this.settings.backgroundColor); - let modelLoader = this.modelLoaderUI.GetModelLoader (); - if (modelLoader.GetDefaultMaterials () !== null) { - ReplaceDefaultMaterialsColor (this.model, this.settings.defaultColor, this.settings.defaultLineColor); - modelLoader.ReplaceDefaultMaterialsColor (this.settings.defaultColor, this.settings.defaultLineColor); + this.viewer.SetBackgroundColor(this.settings.backgroundColor); + let modelLoader = this.modelLoaderUI.GetModelLoader(); + if (modelLoader.GetDefaultMaterials() !== null) { + ReplaceDefaultMaterialsColor(this.model, this.settings.defaultColor, this.settings.defaultLineColor); + modelLoader.ReplaceDefaultMaterialsColor(this.settings.defaultColor, this.settings.defaultLineColor); } } - this.settings.SaveToCookies (); + this.settings.SaveToCookies(); } - InitViewer () - { - let canvas = AddDomElement (this.parameters.viewerDiv, 'canvas'); - this.viewer.Init (canvas); - this.viewer.SetEdgeSettings (this.settings.edgeSettings); - this.viewer.SetBackgroundColor (this.settings.backgroundColor); - this.viewer.SetNavigationMode (this.cameraSettings.navigationMode); - this.viewer.SetProjectionMode (this.cameraSettings.projectionMode); - this.UpdateEnvironmentMap (); + InitViewer() { + let canvas = AddDomElement(this.parameters.viewerDiv, 'canvas'); + this.viewer.Init(canvas); + this.viewer.SetEdgeSettings(this.settings.edgeSettings); + this.viewer.SetBackgroundColor(this.settings.backgroundColor); + this.viewer.SetNavigationMode(this.cameraSettings.navigationMode); + this.viewer.SetProjectionMode(this.cameraSettings.projectionMode); + this.UpdateEnvironmentMap(); } - InitToolbar () - { - function AddButton (toolbar, imageName, imageTitle, classNames, onClick) - { - let button = toolbar.AddImageButton (imageName, imageTitle, () => { - onClick (); + InitToolbar() { + function AddButton(toolbar, imageName, imageTitle, classNames, onClick) { + let button = toolbar.AddImageButton(imageName, imageTitle, () => { + onClick(); }); for (let className of classNames) { - button.AddClass (className); + button.AddClass(className); } return button; } - function AddPushButton (toolbar, imageName, imageTitle, classNames, onClick) - { - let button = toolbar.AddImagePushButton (imageName, imageTitle, false, (isSelected) => { - onClick (isSelected); + function AddPushButton(toolbar, imageName, imageTitle, classNames, onClick) { + let button = toolbar.AddImagePushButton(imageName, imageTitle, false, (isSelected) => { + onClick(isSelected); }); for (let className of classNames) { - button.AddClass (className); + button.AddClass(className); } return button; } - function AddRadioButton (toolbar, imageNames, imageTitles, selectedIndex, classNames, onClick) - { + function AddRadioButton(toolbar, imageNames, imageTitles, selectedIndex, classNames, onClick) { let imageData = []; for (let i = 0; i < imageNames.length; i++) { let imageName = imageNames[i]; let imageTitle = imageTitles[i]; - imageData.push ({ - image : imageName, - title : imageTitle + imageData.push({ + image: imageName, + title: imageTitle }); } - let buttons = toolbar.AddImageRadioButton (imageData, selectedIndex, (buttonIndex) => { - onClick (buttonIndex); + let buttons = toolbar.AddImageRadioButton(imageData, selectedIndex, (buttonIndex) => { + onClick(buttonIndex); }); for (let className of classNames) { for (let button of buttons) { - button.AddClass (className); + button.AddClass(className); } } } - function AddSeparator (toolbar, classNames) - { - let separator = toolbar.AddSeparator (); + function AddSeparator(toolbar, classNames) { + let separator = toolbar.AddSeparator(); if (classNames !== null) { for (let className of classNames) { - separator.classList.add (className); + separator.classList.add(className); } } + return separator; } - let importer = this.modelLoaderUI.GetImporter (); + let importer = this.modelLoaderUI.GetImporter(); let navigationModeIndex = (this.cameraSettings.navigationMode === NavigationMode.FixedUpVector ? 0 : 1); let projectionModeIndex = (this.cameraSettings.projectionMode === ProjectionMode.Perspective ? 0 : 1); - AddButton (this.toolbar, 'open', Loc ('Open from your device'), [], () => { - this.OpenFileBrowserDialog (); + AddButton(this.toolbar, 'open', Loc('Open from your device'), [], () => { + this.OpenFileBrowserDialog(); }); - AddButton (this.toolbar, 'open_url', Loc ('Open from url'), [], () => { - ShowOpenUrlDialog ((urls) => { + AddButton(this.toolbar, 'open_url', Loc('Open from url'), [], () => { + ShowOpenUrlDialog((urls) => { if (urls.length > 0) { - this.hashHandler.SetModelFilesToHash (urls); + this.hashHandler.SetModelFilesToHash(urls); } }); }); - AddSeparator (this.toolbar, ['only_on_model']); - AddButton (this.toolbar, 'fit', Loc ('Fit model to window'), ['only_on_model'], () => { - this.FitModelToWindow (false); + AddSeparator(this.toolbar, ['only_on_model']); + AddButton(this.toolbar, 'fit', Loc('Fit model to window'), ['only_on_model'], () => { + this.FitModelToWindow(false); }); - AddButton (this.toolbar, 'up_y', Loc ('Set Y axis as up vector'), ['only_on_model'], () => { - this.viewer.SetUpVector (Direction.Y, true); + AddButton(this.toolbar, 'up_y', Loc('Set Y axis as up vector'), ['only_on_model'], () => { + this.viewer.SetUpVector(Direction.Y, true); }); - AddButton (this.toolbar, 'up_z', Loc ('Set Z axis as up vector'), ['only_on_model'], () => { - this.viewer.SetUpVector (Direction.Z, true); + AddButton(this.toolbar, 'up_z', Loc('Set Z axis as up vector'), ['only_on_model'], () => { + this.viewer.SetUpVector(Direction.Z, true); }); - AddButton (this.toolbar, 'flip', Loc ('Flip up vector'), ['only_on_model'], () => { - this.viewer.FlipUpVector (); + AddButton(this.toolbar, 'flip', Loc('Flip up vector'), ['only_on_model'], () => { + this.viewer.FlipUpVector(); }); - AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); - AddRadioButton (this.toolbar, ['fix_up_on', 'fix_up_off'], [Loc ('Fixed up vector'), Loc ('Free orbit')], navigationModeIndex, ['only_full_width', 'only_on_model'], (buttonIndex) => { + AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + AddRadioButton(this.toolbar, ['fix_up_on', 'fix_up_off'], [Loc('Fixed up vector'), Loc('Free orbit')], navigationModeIndex, ['only_full_width', 'only_on_model'], (buttonIndex) => { if (buttonIndex === 0) { this.cameraSettings.navigationMode = NavigationMode.FixedUpVector; } else if (buttonIndex === 1) { this.cameraSettings.navigationMode = NavigationMode.FreeOrbit; } - this.cameraSettings.SaveToCookies (); - this.viewer.SetNavigationMode (this.cameraSettings.navigationMode); + this.cameraSettings.SaveToCookies(); + this.viewer.SetNavigationMode(this.cameraSettings.navigationMode); }); - AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); - AddRadioButton (this.toolbar, ['camera_perspective', 'camera_orthographic'], [Loc ('Perspective camera'), Loc ('Orthographic camera')], projectionModeIndex, ['only_full_width', 'only_on_model'], (buttonIndex) => { + AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + AddRadioButton(this.toolbar, ['camera_perspective', 'camera_orthographic'], [Loc('Perspective camera'), Loc('Orthographic camera')], projectionModeIndex, ['only_full_width', 'only_on_model'], (buttonIndex) => { if (buttonIndex === 0) { this.cameraSettings.projectionMode = ProjectionMode.Perspective; } else if (buttonIndex === 1) { this.cameraSettings.projectionMode = ProjectionMode.Orthographic; } - this.cameraSettings.SaveToCookies (); - this.viewer.SetProjectionMode (this.cameraSettings.projectionMode); - this.sidebar.UpdateControlsVisibility (); - }); - AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); - let measureToolButton = AddPushButton (this.toolbar, 'measure', Loc ('Measure'), ['only_full_width', 'only_on_model'], (isSelected) => { - HandleEvent ('measure_tool_activated', isSelected ? 'on' : 'off'); - this.navigator.SetSelection (null); - this.measureTool.SetActive (isSelected); - }); - this.measureTool.SetButton (measureToolButton); - AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); - AddButton (this.toolbar, 'download', Loc ('Download'), ['only_full_width', 'only_on_model'], () => { - HandleEvent ('model_downloaded', ''); - let importer = this.modelLoaderUI.GetImporter (); - DownloadModel (importer); - }); - AddButton (this.toolbar, 'export', Loc ('Export'), ['only_full_width', 'only_on_model'], () => { - ShowExportDialog (this.model, this.viewer, { - isMeshVisible : (meshInstanceId) => { - return this.navigator.IsMeshVisible (meshInstanceId); + this.cameraSettings.SaveToCookies(); + this.viewer.SetProjectionMode(this.cameraSettings.projectionMode); + this.sidebar.UpdateControlsVisibility(); + }); + AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + let measureToolButton = AddPushButton(this.toolbar, 'measure', Loc('Measure'), ['only_full_width', 'only_on_model'], (isSelected) => { + HandleEvent('measure_tool_activated', isSelected ? 'on' : 'off'); + this.navigator.SetSelection(null); + this.measureTool.SetActive(isSelected); + }); + this.measureTool.SetButton(measureToolButton); + let explodeSeparator = AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + let explodeToolButton = AddPushButton(this.toolbar, 'explode', Loc('Explode View'), ['only_full_width', 'only_on_model'], (isSelected) => { + this.navigator.SetSelection(null); + this.explodeTool.SetActive(isSelected); + }); + this.explodeTool.SetButton(explodeToolButton); + this.explodeTool.SetSeparator(explodeSeparator); + // Initially hide if there's no model or only one mesh + explodeToolButton.AddClass('ov_hidden'); + explodeSeparator.classList.add('ov_hidden'); + AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + AddButton(this.toolbar, 'download', Loc('Download'), ['only_full_width', 'only_on_model'], () => { + HandleEvent('model_downloaded', ''); + let importer = this.modelLoaderUI.GetImporter(); + DownloadModel(importer); + }); + AddButton(this.toolbar, 'export', Loc('Export'), ['only_full_width', 'only_on_model'], () => { + ShowExportDialog(this.model, this.viewer, { + isMeshVisible: (meshInstanceId) => { + return this.navigator.IsMeshVisible(meshInstanceId); } }); }); - AddButton (this.toolbar, 'share', Loc ('Share'), ['only_full_width', 'only_on_model'], () => { - ShowSharingDialog (importer.GetFileList (), this.settings, this.viewer); + AddButton(this.toolbar, 'share', Loc('Share'), ['only_full_width', 'only_on_model'], () => { + ShowSharingDialog(importer.GetFileList(), this.settings, this.viewer); }); - AddSeparator (this.toolbar, ['only_full_width', 'only_on_model']); - AddButton (this.toolbar, 'snapshot', Loc ('Create snapshot'), ['only_full_width', 'only_on_model'], () => { - ShowSnapshotDialog (this.viewer); + AddSeparator(this.toolbar, ['only_full_width', 'only_on_model']); + AddButton(this.toolbar, 'snapshot', Loc('Create snapshot'), ['only_full_width', 'only_on_model'], () => { + ShowSnapshotDialog(this.viewer); }); - EnumeratePlugins (PluginType.Toolbar, (plugin) => { - plugin.registerButtons ({ - createSeparator : (classNames) => { - AddSeparator (this.toolbar, classNames); + EnumeratePlugins(PluginType.Toolbar, (plugin) => { + plugin.registerButtons({ + createSeparator: (classNames) => { + AddSeparator(this.toolbar, classNames); }, - createButton : (icon, title, classNames, onClick) => { - AddButton (this.toolbar, icon, title, classNames, onClick); + createButton: (icon, title, classNames, onClick) => { + AddButton(this.toolbar, icon, title, classNames, onClick); }, - getModel : () => { + getModel: () => { return this.model; } }); }); let selectedTheme = (this.settings.themeId === Theme.Light ? 1 : 0); - AddRadioButton (this.toolbar, ['dark_mode', 'light_mode'], [Loc ('Dark mode'), Loc ('Light mode')], selectedTheme, ['align_right'], (buttonIndex) => { + AddRadioButton(this.toolbar, ['dark_mode', 'light_mode'], [Loc('Dark mode'), Loc('Light mode')], selectedTheme, ['align_right'], (buttonIndex) => { if (buttonIndex === 0) { this.settings.themeId = Theme.Dark; } else if (buttonIndex === 1) { this.settings.themeId = Theme.Light; } - HandleEvent ('theme_changed', this.settings.themeId === Theme.Light ? 'light' : 'dark'); - this.SwitchTheme (this.settings.themeId, true); + HandleEvent('theme_changed', this.settings.themeId === Theme.Light ? 'light' : 'dark'); + this.SwitchTheme(this.settings.themeId, true); }); - this.parameters.fileInput.addEventListener ('change', (ev) => { + this.parameters.fileInput.addEventListener('change', (ev) => { if (ev.target.files.length > 0) { - HandleEvent ('model_load_started', 'open_file'); - this.LoadModelFromFileList (ev.target.files); + HandleEvent('model_load_started', 'open_file'); + this.LoadModelFromFileList(ev.target.files); } }); } - InitDragAndDrop () - { - window.addEventListener ('dragstart', (ev) => { - ev.preventDefault (); + InitDragAndDrop() { + window.addEventListener('dragstart', (ev) => { + ev.preventDefault(); }, false); - window.addEventListener ('dragover', (ev) => { - ev.stopPropagation (); - ev.preventDefault (); + window.addEventListener('dragover', (ev) => { + ev.stopPropagation(); + ev.preventDefault(); ev.dataTransfer.dropEffect = 'copy'; }, false); - window.addEventListener ('drop', (ev) => { - ev.stopPropagation (); - ev.preventDefault (); - GetFilesFromDataTransfer (ev.dataTransfer, (files) => { + window.addEventListener('drop', (ev) => { + ev.stopPropagation(); + ev.preventDefault(); + GetFilesFromDataTransfer(ev.dataTransfer, (files) => { if (files.length > 0) { - HandleEvent ('model_load_started', 'drop'); - this.LoadModelFromFileList (files); + HandleEvent('model_load_started', 'drop'); + this.LoadModelFromFileList(files); } }); }, false); } - InitSidebar () - { - this.sidebar.Init ({ - getShadingType : () => { - return this.viewer.GetShadingType (); + InitSidebar() { + this.sidebar.Init({ + getShadingType: () => { + return this.viewer.GetShadingType(); }, - getProjectionMode : () => { - return this.viewer.GetProjectionMode (); + getProjectionMode: () => { + return this.viewer.GetProjectionMode(); }, - getDefaultMaterials : () => { - return GetDefaultMaterials (this.model); + getDefaultMaterials: () => { + return GetDefaultMaterials(this.model); }, - onEnvironmentMapChanged : () => { - this.settings.SaveToCookies (); - this.UpdateEnvironmentMap (); - if (this.measureTool.IsActive ()) { - this.measureTool.UpdatePanel (); + onEnvironmentMapChanged: () => { + this.settings.SaveToCookies(); + this.UpdateEnvironmentMap(); + if (this.measureTool.IsActive()) { + this.measureTool.UpdatePanel(); } }, - onBackgroundColorChanged : () => { - this.settings.SaveToCookies (); - this.viewer.SetBackgroundColor (this.settings.backgroundColor); - if (this.measureTool.IsActive ()) { - this.measureTool.UpdatePanel (); + onBackgroundColorChanged: () => { + this.settings.SaveToCookies(); + this.viewer.SetBackgroundColor(this.settings.backgroundColor); + if (this.measureTool.IsActive()) { + this.measureTool.UpdatePanel(); } }, - onDefaultColorChanged : () => { - this.settings.SaveToCookies (); - let modelLoader = this.modelLoaderUI.GetModelLoader (); - if (modelLoader.GetDefaultMaterials () !== null) { - ReplaceDefaultMaterialsColor (this.model, this.settings.defaultColor, this.settings.defaultLineColor); - modelLoader.ReplaceDefaultMaterialsColor (this.settings.defaultColor, this.settings.defaultLineColor); + onDefaultColorChanged: () => { + this.settings.SaveToCookies(); + let modelLoader = this.modelLoaderUI.GetModelLoader(); + if (modelLoader.GetDefaultMaterials() !== null) { + ReplaceDefaultMaterialsColor(this.model, this.settings.defaultColor, this.settings.defaultLineColor); + modelLoader.ReplaceDefaultMaterialsColor(this.settings.defaultColor, this.settings.defaultLineColor); } - this.viewer.Render (); + this.viewer.Render(); }, - onEdgeDisplayChanged : () => { - HandleEvent ('edge_display_changed', this.settings.showEdges ? 'on' : 'off'); - this.UpdateEdgeDisplay (); + onEdgeDisplayChanged: () => { + HandleEvent('edge_display_changed', this.settings.showEdges ? 'on' : 'off'); + this.UpdateEdgeDisplay(); }, - onResizeRequested : () => { - this.layouter.Resize (); + onResizeRequested: () => { + this.layouter.Resize(); }, - onShowHidePanels : (show) => { - ShowDomElement (this.parameters.sidebarSplitterDiv, show); - CookieSetBoolVal ('ov_show_sidebar', show); + onShowHidePanels: (show) => { + ShowDomElement(this.parameters.sidebarSplitterDiv, show); + CookieSetBoolVal('ov_show_sidebar', show); } }); } - InitNavigator () - { - function GetMeshUserDataArray (viewer, meshInstanceId) - { + InitNavigator() { + function GetMeshUserDataArray(viewer, meshInstanceId) { let userDataArr = []; - viewer.EnumerateMeshesAndLinesUserData ((meshUserData) => { - if (meshUserData.originalMeshInstance.id.IsEqual (meshInstanceId)) { - userDataArr.push (meshUserData); + viewer.EnumerateMeshesAndLinesUserData((meshUserData) => { + if (meshUserData.originalMeshInstance.id.IsEqual(meshInstanceId)) { + userDataArr.push(meshUserData); } }); return userDataArr; } - function GetMeshesForMaterial (viewer, materialIndex) - { + function GetMeshesForMaterial(viewer, materialIndex) { let usedByMeshes = []; - viewer.EnumerateMeshesAndLinesUserData ((meshUserData) => { - if (materialIndex === null || meshUserData.originalMaterials.indexOf (materialIndex) !== -1) { - usedByMeshes.push (meshUserData.originalMeshInstance); + viewer.EnumerateMeshesAndLinesUserData((meshUserData) => { + if (materialIndex === null || meshUserData.originalMaterials.indexOf(materialIndex) !== -1) { + usedByMeshes.push(meshUserData.originalMeshInstance); } }); return usedByMeshes; } - function GetMaterialReferenceInfo (model, materialIndex) - { - const material = model.GetMaterial (materialIndex); + function GetMaterialReferenceInfo(model, materialIndex) { + const material = model.GetMaterial(materialIndex); return { - index : materialIndex, - name : material.name, - color : material.color.Clone () + index: materialIndex, + name: material.name, + color: material.color.Clone() }; } - function GetMaterialsForMesh (viewer, model, meshInstanceId) - { + function GetMaterialsForMesh(viewer, model, meshInstanceId) { let usedMaterials = []; if (meshInstanceId === null) { - for (let materialIndex = 0; materialIndex < model.MaterialCount (); materialIndex++) { - usedMaterials.push (GetMaterialReferenceInfo (model, materialIndex)); + for (let materialIndex = 0; materialIndex < model.MaterialCount(); materialIndex++) { + usedMaterials.push(GetMaterialReferenceInfo(model, materialIndex)); } } else { - let userDataArr = GetMeshUserDataArray (viewer, meshInstanceId); - let addedMaterialIndices = new Set (); + let userDataArr = GetMeshUserDataArray(viewer, meshInstanceId); + let addedMaterialIndices = new Set(); for (let userData of userDataArr) { for (let materialIndex of userData.originalMaterials) { - if (addedMaterialIndices.has (materialIndex)) { + if (addedMaterialIndices.has(materialIndex)) { continue; } - usedMaterials.push (GetMaterialReferenceInfo (model, materialIndex)); - addedMaterialIndices.add (materialIndex); + usedMaterials.push(GetMaterialReferenceInfo(model, materialIndex)); + addedMaterialIndices.add(materialIndex); } } } - usedMaterials.sort ((a, b) => { + usedMaterials.sort((a, b) => { return a.index - b.index; }); return usedMaterials; } - this.navigator.Init ({ - openFileBrowserDialog : () => { - this.OpenFileBrowserDialog (); + this.navigator.Init({ + openFileBrowserDialog: () => { + this.OpenFileBrowserDialog(); }, - fitMeshToWindow : (meshInstanceId) => { - this.FitMeshToWindow (meshInstanceId); + fitMeshToWindow: (meshInstanceId) => { + this.FitMeshToWindow(meshInstanceId); }, - fitMeshesToWindow : (meshInstanceIdSet) => { - this.FitMeshesToWindow (meshInstanceIdSet); + fitMeshesToWindow: (meshInstanceIdSet) => { + this.FitMeshesToWindow(meshInstanceIdSet); }, - getMeshesForMaterial : (materialIndex) => { - return GetMeshesForMaterial (this.viewer, materialIndex); + getMeshesForMaterial: (materialIndex) => { + return GetMeshesForMaterial(this.viewer, materialIndex); }, - getMaterialsForMesh : (meshInstanceId) => { - return GetMaterialsForMesh (this.viewer, this.model, meshInstanceId); + getMaterialsForMesh: (meshInstanceId) => { + return GetMaterialsForMesh(this.viewer, this.model, meshInstanceId); }, - onMeshVisibilityChanged : () => { - this.UpdateMeshesVisibility (); + onMeshVisibilityChanged: () => { + this.UpdateMeshesVisibility(); }, - onMeshSelectionChanged : () => { - this.UpdateMeshesSelection (); + onMeshSelectionChanged: () => { + this.UpdateMeshesSelection(); }, - onSelectionCleared : () => { - this.sidebar.AddObject3DProperties (this.model, this.model); + onSelectionCleared: () => { + this.sidebar.AddObject3DProperties(this.model, this.model); }, - onMeshSelected : (meshInstanceId) => { - let meshInstance = this.model.GetMeshInstance (meshInstanceId); - this.sidebar.AddObject3DProperties (this.model, meshInstance); + onMeshSelected: (meshInstanceId) => { + let meshInstance = this.model.GetMeshInstance(meshInstanceId); + this.sidebar.AddObject3DProperties(this.model, meshInstance); }, - onMaterialSelected : (materialIndex) => { - this.sidebar.AddMaterialProperties (this.model.GetMaterial (materialIndex)); + onMaterialSelected: (materialIndex) => { + this.sidebar.AddMaterialProperties(this.model.GetMaterial(materialIndex)); }, - onResizeRequested : () => { - this.layouter.Resize (); + onResizeRequested: () => { + this.layouter.Resize(); }, - onShowHidePanels : (show) => { - ShowDomElement (this.parameters.navigatorSplitterDiv, show); - CookieSetBoolVal ('ov_show_navigator', show); + onShowHidePanels: (show) => { + ShowDomElement(this.parameters.navigatorSplitterDiv, show); + CookieSetBoolVal('ov_show_navigator', show); } }); } - UpdatePanelsVisibility () - { - let showNavigator = CookieGetBoolVal ('ov_show_navigator', true); - let showSidebar = CookieGetBoolVal ('ov_show_sidebar', true); - this.navigator.ShowPanels (showNavigator); - this.sidebar.ShowPanels (showSidebar); + UpdatePanelsVisibility() { + let showNavigator = CookieGetBoolVal('ov_show_navigator', true); + let showSidebar = CookieGetBoolVal('ov_show_sidebar', true); + this.navigator.ShowPanels(showNavigator); + this.sidebar.ShowPanels(showSidebar); } - CreateHeaderButton (icon, title, link) - { - let buttonLink = CreateDomElement ('a'); - buttonLink.setAttribute ('href', link); - buttonLink.setAttribute ('target', '_blank'); - buttonLink.setAttribute ('rel', 'noopener noreferrer'); - InstallTooltip (buttonLink, title); - AddSvgIconElement (buttonLink, icon, 'header_button'); - this.parameters.headerButtonsDiv.appendChild (buttonLink); + CreateHeaderButton(icon, title, link) { + let buttonLink = CreateDomElement('a'); + buttonLink.setAttribute('href', link); + buttonLink.setAttribute('target', '_blank'); + buttonLink.setAttribute('rel', 'noopener noreferrer'); + InstallTooltip(buttonLink, title); + AddSvgIconElement(buttonLink, icon, 'header_button'); + this.parameters.headerButtonsDiv.appendChild(buttonLink); return buttonLink; } - InitCookieConsent () - { - let accepted = CookieGetBoolVal ('ov_cookie_consent', false); + InitCookieConsent() { + let accepted = CookieGetBoolVal('ov_cookie_consent', false); if (accepted) { return; } - let text = Loc ('This website uses cookies to offer you better user experience. See the details at the Cookies Policy page.'); - let popupDiv = AddDiv (document.body, 'ov_bottom_floating_panel'); - AddDiv (popupDiv, 'ov_floating_panel_text', text); - let acceptButton = AddDiv (popupDiv, 'ov_button ov_floating_panel_button', Loc ('Accept')); - acceptButton.addEventListener ('click', () => { - CookieSetBoolVal ('ov_cookie_consent', true); - popupDiv.remove (); + let text = Loc('This website uses cookies to offer you better user experience. See the details at the Cookies Policy page.'); + let popupDiv = AddDiv(document.body, 'ov_bottom_floating_panel'); + AddDiv(popupDiv, 'ov_floating_panel_text', text); + let acceptButton = AddDiv(popupDiv, 'ov_button ov_floating_panel_button', Loc('Accept')); + acceptButton.addEventListener('click', () => { + CookieSetBoolVal('ov_cookie_consent', true); + popupDiv.remove(); }); } } diff --git a/website/info/css/O3DVIcons.woff b/website/info/css/O3DVIcons.woff index 2e28dd7c..6c73da0c 100644 Binary files a/website/info/css/O3DVIcons.woff and b/website/info/css/O3DVIcons.woff differ diff --git a/website/info/css/icons.css b/website/info/css/icons.css index 05a1f523..82a4d46b 100644 --- a/website/info/css/icons.css +++ b/website/info/css/icons.css @@ -1,6 +1,6 @@ @font-face { font-family: "O3DVIcons"; - src: url("./O3DVIcons.woff?d27bdb5af135068ed4a9350e285e132e") format("woff"); + src: url("./O3DVIcons.woff?a183607fabf1ae69b129cd0cbb17c310") format("woff"); } i[class^="icon-"]:before, i[class*=" icon-"]:before { @@ -56,108 +56,111 @@ i[class^="icon-"]:before, i[class*=" icon-"]:before { .icon-expand:before { content: "\f10e"; } -.icon-export:before { +.icon-explode:before { content: "\f10f"; } -.icon-feedback:before { +.icon-export:before { content: "\f110"; } -.icon-file_download:before { +.icon-feedback:before { content: "\f111"; } -.icon-files:before { +.icon-file_download:before { content: "\f112"; } -.icon-fit:before { +.icon-files:before { content: "\f113"; } -.icon-fix_up_off:before { +.icon-fit:before { content: "\f114"; } -.icon-fix_up_on:before { +.icon-fix_up_off:before { content: "\f115"; } -.icon-flat_list:before { +.icon-fix_up_on:before { content: "\f116"; } -.icon-flip:before { +.icon-flat_list:before { content: "\f117"; } -.icon-github:before { +.icon-flip:before { content: "\f118"; } -.icon-hidden:before { +.icon-github:before { content: "\f119"; } -.icon-info:before { +.icon-hidden:before { content: "\f11a"; } -.icon-isolate:before { +.icon-info:before { content: "\f11b"; } -.icon-light_mode:before { +.icon-isolate:before { content: "\f11c"; } -.icon-materials:before { +.icon-light_mode:before { content: "\f11d"; } -.icon-measure_angle:before { +.icon-materials:before { content: "\f11e"; } -.icon-measure_distance_parallel:before { +.icon-measure_angle:before { content: "\f11f"; } -.icon-measure_distance:before { +.icon-measure_distance_parallel:before { content: "\f120"; } -.icon-measure:before { +.icon-measure_distance:before { content: "\f121"; } -.icon-meshes:before { +.icon-measure:before { content: "\f122"; } -.icon-missing_files:before { +.icon-meshes:before { content: "\f123"; } -.icon-model:before { +.icon-missing_files:before { content: "\f124"; } -.icon-open_url:before { +.icon-model:before { content: "\f125"; } -.icon-open:before { +.icon-open_url:before { content: "\f126"; } -.icon-print3d:before { +.icon-open:before { content: "\f127"; } -.icon-settings:before { +.icon-print3d:before { content: "\f128"; } -.icon-share:before { +.icon-settings:before { content: "\f129"; } -.icon-snapshot:before { +.icon-share:before { content: "\f12a"; } -.icon-tree_mesh:before { +.icon-snapshot:before { content: "\f12b"; } -.icon-tree_view:before { +.icon-tree_mesh:before { content: "\f12c"; } -.icon-twitter:before { +.icon-tree_view:before { content: "\f12d"; } -.icon-up_y:before { +.icon-twitter:before { content: "\f12e"; } -.icon-up_z:before { +.icon-up_y:before { content: "\f12f"; } -.icon-visible:before { +.icon-up_z:before { content: "\f130"; } -.icon-warning:before { +.icon-visible:before { content: "\f131"; } +.icon-warning:before { + content: "\f132"; +}