summaryrefslogtreecommitdiff
path: root/graphics/asymptote/base/webgl/asygl.js
blob: 4c053eb47879cf9e4f5080b1fcc8ef09dd68b984 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*@license
 AsyGL: Render Bezier patches and triangles via subdivision with WebGL.
  Copyright 2019: John C. Bowman and Supakorn "Jamie" Rassameemasmuang
  University of Alberta

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*@license for gl-matrix mat3 and mat4 functions:
Copyright (c) 2015, Brandon Jones, Colin MacKenzie IV.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/
let vertex="\nattribute vec3 position;\n#ifdef WIDTH\nattribute float width;\n#endif\n#ifdef NORMAL\nattribute vec3 normal;\n#endif\nattribute float materialIndex;\n#ifdef COLOR\nattribute vec4 color;\n#endif\n\nuniform mat3 normMat;\nuniform mat4 viewMat;\nuniform mat4 projViewMat;\n\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\nvarying vec3 ViewPosition;\n#endif\nvarying vec3 Normal;\n#endif\nvarying vec4 diffuse;\nvarying vec3 specular;\nvarying float roughness,metallic,fresnel0;\nvarying vec4 emissive;\n\nstruct Material {\n  vec4 diffuse,emissive,specular;\n  vec4 parameters;\n};\n\nuniform Material Materials[Nmaterials];\n\nvoid main(void)\n{\n  vec4 v=vec4(position,1.0);\n  gl_Position=projViewMat*v;\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\n  ViewPosition=(viewMat*v).xyz;\n#endif      \n  Normal=normal*normMat;\n        \n  Material m;\n#ifdef TRANSPARENT\n  m=Materials[int(abs(materialIndex))-1];\n  if(materialIndex >= 0.0) {\n    diffuse=m.diffuse;\n    emissive=m.emissive;\n  } else {\n    diffuse=color;\n#if nlights > 0\n    emissive=vec4(0.0);\n#else\n    emissive=color;\n#endif\n  }\n#else\n  m=Materials[int(materialIndex)];\n#ifdef COLOR\n  diffuse=color;\n#if nlights > 0\n  emissive=vec4(0.0);\n#else\n  emissive=color;\n#endif\n#else\n  diffuse=m.diffuse;\n  emissive=m.emissive;\n#endif\n#endif\n  specular=m.specular.rgb;\n  vec4 parameters=m.parameters;\n  roughness=1.0-parameters[0];\n  metallic=parameters[1];\n  fresnel0=parameters[2];\n#else\n  emissive=Materials[int(materialIndex)].emissive;\n#endif\n#ifdef WIDTH\n  gl_PointSize=width;\n#endif\n}\n",fragment="\n#ifdef NORMAL\n#ifndef ORTHOGRAPHIC\nvarying vec3 ViewPosition;\n#endif\nvarying vec3 Normal;\nvarying vec4 diffuse;\nvarying vec3 specular;\nvarying float roughness,metallic,fresnel0;\n\nfloat Roughness2;\nvec3 normal;\n\nstruct Light {\n  vec3 direction;\n  vec3 color;\n};\n\nuniform Light Lights[Nlights];\n\nfloat NDF_TRG(vec3 h)\n{\n  float ndoth=max(dot(normal,h),0.0);\n  float alpha2=Roughness2*Roughness2;\n  float denom=ndoth*ndoth*(alpha2-1.0)+1.0;\n  return denom != 0.0 ? alpha2/(denom*denom) : 0.0;\n}\n    \nfloat GGX_Geom(vec3 v)\n{\n  float ndotv=max(dot(v,normal),0.0);\n  float ap=1.0+Roughness2;\n  float k=0.125*ap*ap;\n  return ndotv/((ndotv*(1.0-k))+k);\n}\n    \nfloat Geom(vec3 v, vec3 l)\n{\n  return GGX_Geom(v)*GGX_Geom(l);\n}\n    \nfloat Fresnel(vec3 h, vec3 v, float fresnel0)\n{\n  float a=1.0-max(dot(h,v),0.0);\n  float b=a*a;\n  return fresnel0+(1.0-fresnel0)*b*b*a;\n}\n    \n// physical based shading using UE4 model.\nvec3 BRDF(vec3 viewDirection, vec3 lightDirection)\n{\n  vec3 lambertian=diffuse.rgb;\n  vec3 h=normalize(lightDirection+viewDirection);\n      \n  float omegain=max(dot(viewDirection,normal),0.0);\n  float omegali=max(dot(lightDirection,normal),0.0);\n      \n  float D=NDF_TRG(h);\n  float G=Geom(viewDirection,lightDirection);\n  float F=Fresnel(h,viewDirection,fresnel0);\n      \n  float denom=4.0*omegain*omegali;\n  float rawReflectance=denom > 0.0 ? (D*G)/denom : 0.0;\n      \n  vec3 dielectric=mix(lambertian,rawReflectance*specular,F);\n  vec3 metal=rawReflectance*diffuse.rgb;\n      \n  return mix(dielectric,metal,metallic);\n}\n#endif\nvarying vec4 emissive;\n    \nvoid main(void)\n{\n#if defined(NORMAL) && nlights > 0\n  normal=normalize(Normal);\n  normal=gl_FrontFacing ? normal : -normal;\n#ifdef ORTHOGRAPHIC\n  vec3 viewDir=vec3(0.0,0.0,1.0);\n#else\n  vec3 viewDir=-normalize(ViewPosition);\n#endif\n  Roughness2=roughness*roughness;\n  vec3 color=emissive.rgb;\n  for(int i=0; i < nlights; ++i) {\n    Light Li=Lights[i];\n    vec3 L=Li.direction;\n    float cosTheta=max(dot(normal,L),0.0);\n    vec3 radiance=cosTheta*Li.color;\n    color += BRDF(viewDir,L)*radiance;\n  }\n  gl_FragColor=vec4(color,diffuse.a);\n#else\n  gl_FragColor=emissive;\n#endif\n}\n";!function(t,e){if("object"==typeof exports&&"object"==typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var i=e();for(var a in i)("object"==typeof exports?exports:t)[a]=i[a]}}("undefined"!=typeof self?self:this,function(){return function(t){var e={};function i(a){if(e[a])return e[a].exports;var r=e[a]={i:a,l:!1,exports:{}};return t[a].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=t,i.c=e,i.d=function(t,e,a){i.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:a})},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=1)}([function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.setMatrixArrayType=function(t){e.ARRAY_TYPE=t},e.toRadian=function(t){return t*r},e.equals=function(t,e){return Math.abs(t-e)<=a*Math.max(1,Math.abs(t),Math.abs(e))};var a=e.EPSILON=1e-6;e.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,e.RANDOM=Math.random;var r=Math.PI/180},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.mat4=e.mat3=void 0;var a=n(i(2)),r=n(i(3));function n(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}e.mat3=a,e.mat4=r},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.create=function(){var t=new a.ARRAY_TYPE(9);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},e.fromMat4=function(t,e){return t[0]=e[0],t[1]=e[1],t[2]=e[2],t[3]=e[4],t[4]=e[5],t[5]=e[6],t[6]=e[8],t[7]=e[9],t[8]=e[10],t},e.invert=function(t,e){var i=e[0],a=e[1],r=e[2],n=e[3],s=e[4],o=e[5],l=e[6],h=e[7],c=e[8],d=c*s-o*h,m=-c*n+o*l,f=h*n-s*l,u=i*d+a*m+r*f;if(!u)return null;return u=1/u,t[0]=d*u,t[1]=(-c*a+r*h)*u,t[2]=(o*a-r*s)*u,t[3]=m*u,t[4]=(c*i-r*l)*u,t[5]=(-o*i+r*n)*u,t[6]=f*u,t[7]=(-h*i+a*l)*u,t[8]=(s*i-a*n)*u,t};var a=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}(i(0))},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.create=function(){var t=new a.ARRAY_TYPE(16);return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.invert=function(t,e){var i=e[0],a=e[1],r=e[2],n=e[3],s=e[4],o=e[5],l=e[6],h=e[7],c=e[8],d=e[9],m=e[10],f=e[11],u=e[12],v=e[13],p=e[14],g=e[15],x=i*o-a*s,M=i*l-r*s,w=i*h-n*s,A=a*l-r*o,b=a*h-n*o,S=r*h-n*l,R=c*v-d*u,D=c*p-m*u,P=c*g-f*u,T=d*p-m*v,y=d*g-f*v,I=m*g-f*p,z=x*I-M*y+w*T+A*P-b*D+S*R;if(!z)return null;return z=1/z,t[0]=(o*I-l*y+h*T)*z,t[1]=(r*y-a*I-n*T)*z,t[2]=(v*S-p*b+g*A)*z,t[3]=(m*b-d*S-f*A)*z,t[4]=(l*P-s*I-h*D)*z,t[5]=(i*I-r*P+n*D)*z,t[6]=(p*w-u*S-g*M)*z,t[7]=(c*S-m*w+f*M)*z,t[8]=(s*y-o*P+h*R)*z,t[9]=(a*P-i*y-n*R)*z,t[10]=(u*b-v*w+g*x)*z,t[11]=(d*w-c*b-f*x)*z,t[12]=(o*D-s*T-l*R)*z,t[13]=(i*T-a*D+r*R)*z,t[14]=(v*M-u*A-p*x)*z,t[15]=(c*A-d*M+m*x)*z,t},e.multiply=r,e.translate=function(t,e,i){var a=i[0],r=i[1],n=i[2],s=void 0,o=void 0,l=void 0,h=void 0,c=void 0,d=void 0,m=void 0,f=void 0,u=void 0,v=void 0,p=void 0,g=void 0;e===t?(t[12]=e[0]*a+e[4]*r+e[8]*n+e[12],t[13]=e[1]*a+e[5]*r+e[9]*n+e[13],t[14]=e[2]*a+e[6]*r+e[10]*n+e[14],t[15]=e[3]*a+e[7]*r+e[11]*n+e[15]):(s=e[0],o=e[1],l=e[2],h=e[3],c=e[4],d=e[5],m=e[6],f=e[7],u=e[8],v=e[9],p=e[10],g=e[11],t[0]=s,t[1]=o,t[2]=l,t[3]=h,t[4]=c,t[5]=d,t[6]=m,t[7]=f,t[8]=u,t[9]=v,t[10]=p,t[11]=g,t[12]=s*a+c*r+u*n+e[12],t[13]=o*a+d*r+v*n+e[13],t[14]=l*a+m*r+p*n+e[14],t[15]=h*a+f*r+g*n+e[15]);return t},e.rotate=function(t,e,i,r){var n=r[0],s=r[1],o=r[2],l=Math.sqrt(n*n+s*s+o*o),h=void 0,c=void 0,d=void 0,m=void 0,f=void 0,u=void 0,v=void 0,p=void 0,g=void 0,x=void 0,M=void 0,w=void 0,A=void 0,b=void 0,S=void 0,R=void 0,D=void 0,P=void 0,T=void 0,y=void 0,I=void 0,z=void 0,O=void 0,E=void 0;if(Math.abs(l)<a.EPSILON)return null;n*=l=1/l,s*=l,o*=l,h=Math.sin(i),c=Math.cos(i),d=1-c,m=e[0],f=e[1],u=e[2],v=e[3],p=e[4],g=e[5],x=e[6],M=e[7],w=e[8],A=e[9],b=e[10],S=e[11],R=n*n*d+c,D=s*n*d+o*h,P=o*n*d-s*h,T=n*s*d-o*h,y=s*s*d+c,I=o*s*d+n*h,z=n*o*d+s*h,O=s*o*d-n*h,E=o*o*d+c,t[0]=m*R+p*D+w*P,t[1]=f*R+g*D+A*P,t[2]=u*R+x*D+b*P,t[3]=v*R+M*D+S*P,t[4]=m*T+p*y+w*I,t[5]=f*T+g*y+A*I,t[6]=u*T+x*y+b*I,t[7]=v*T+M*y+S*I,t[8]=m*z+p*O+w*E,t[9]=f*z+g*O+A*E,t[10]=u*z+x*O+b*E,t[11]=v*z+M*O+S*E,e!==t&&(t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]);return t},e.fromTranslation=function(t,e){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=e[0],t[13]=e[1],t[14]=e[2],t[15]=1,t},e.fromRotation=function(t,e,i){var r=i[0],n=i[1],s=i[2],o=Math.sqrt(r*r+n*n+s*s),l=void 0,h=void 0,c=void 0;if(Math.abs(o)<a.EPSILON)return null;return r*=o=1/o,n*=o,s*=o,l=Math.sin(e),h=Math.cos(e),c=1-h,t[0]=r*r*c+h,t[1]=n*r*c+s*l,t[2]=s*r*c-n*l,t[3]=0,t[4]=r*n*c-s*l,t[5]=n*n*c+h,t[6]=s*n*c+r*l,t[7]=0,t[8]=r*s*c+n*l,t[9]=n*s*c-r*l,t[10]=s*s*c+h,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},e.frustum=function(t,e,i,a,r,n,s){var o=1/(i-e),l=1/(r-a),h=1/(n-s);return t[0]=2*n*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*n*l,t[6]=0,t[7]=0,t[8]=(i+e)*o,t[9]=(r+a)*l,t[10]=(s+n)*h,t[11]=-1,t[12]=0,t[13]=0,t[14]=s*n*2*h,t[15]=0,t},e.ortho=function(t,e,i,a,r,n,s){var o=1/(e-i),l=1/(a-r),h=1/(n-s);return t[0]=-2*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*l,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*h,t[11]=0,t[12]=(e+i)*o,t[13]=(r+a)*l,t[14]=(s+n)*h,t[15]=1,t};var a=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e.default=t,e}(i(0));function r(t,e,i){var a=e[0],r=e[1],n=e[2],s=e[3],o=e[4],l=e[5],h=e[6],c=e[7],d=e[8],m=e[9],f=e[10],u=e[11],v=e[12],p=e[13],g=e[14],x=e[15],M=i[0],w=i[1],A=i[2],b=i[3];return t[0]=M*a+w*o+A*d+b*v,t[1]=M*r+w*l+A*m+b*p,t[2]=M*n+w*h+A*f+b*g,t[3]=M*s+w*c+A*u+b*x,M=i[4],w=i[5],A=i[6],b=i[7],t[4]=M*a+w*o+A*d+b*v,t[5]=M*r+w*l+A*m+b*p,t[6]=M*n+w*h+A*f+b*g,t[7]=M*s+w*c+A*u+b*x,M=i[8],w=i[9],A=i[10],b=i[11],t[8]=M*a+w*o+A*d+b*v,t[9]=M*r+w*l+A*m+b*p,t[10]=M*n+w*h+A*f+b*g,t[11]=M*s+w*c+A*u+b*x,M=i[12],w=i[13],A=i[14],b=i[15],t[12]=M*a+w*o+A*d+b*v,t[13]=M*r+w*l+A*m+b*p,t[14]=M*n+w*h+A*f+b*g,t[15]=M*s+w*c+A*u+b*x,t}}])});let canvasWidth,canvasHeight,b,B,angle,Zoom0,viewportmargin,zoomFactor,zoomPinchFactor,zoomPinchCap,zoomStep,shiftHoldDistance,shiftWaitTime,vibrateTime,embedded,canvas,gl,alpha,offscreen,context,maxMaterials,halfCanvasWidth,halfCanvasHeight,Zoom,P=[],Materials=[],Lights=[],Centers=[],Background=[1,1,1,1],absolute=!1,viewportshift=[0,0],nlights=0,Nmaterials=2,materials=[],pixel=.75,BezierFactor=.4,FillFactor=.1,maxViewportWidth=window.innerWidth,maxViewportHeight=window.innerHeight;const windowTrim=10;let lastzoom,H,zmin,zmax,size2,ArcballFactor,positionBuffer,materialBuffer,colorBuffer,indexBuffer,resizeStep=1.2,Fuzz2=1e3*Number.EPSILON,Fuzz4=Fuzz2*Fuzz2,third=1/3,rotMat=mat4.create(),projMat=mat4.create(),viewMat=mat4.create(),projViewMat=mat4.create(),normMat=mat3.create(),viewMat3=mat3.create(),rotMats=mat4.create(),cjMatInv=mat4.create(),translMat=mat4.create(),center={x:0,y:0,z:0},shift={x:0,y:0},viewParam={xmin:0,xmax:0,ymin:0,ymax:0,zmin:0,zmax:0},redraw=!0,remesh=!0,mouseDownOrTouchActive=!1,lastMouseX=null,lastMouseY=null,touchID=null,Positions=[],Normals=[],Colors=[],Indices=[];class Material{constructor(t,e,i,a,r,n){this.diffuse=t,this.emissive=e,this.specular=i,this.shininess=a,this.metallic=r,this.fresnel0=n}setUniform(t,e){let i=i=>gl.getUniformLocation(t,"Materials["+e+"]."+i);gl.uniform4fv(i("diffuse"),new Float32Array(this.diffuse)),gl.uniform4fv(i("emissive"),new Float32Array(this.emissive)),gl.uniform4fv(i("specular"),new Float32Array(this.specular)),gl.uniform4f(i("parameters"),this.shininess,this.metallic,this.fresnel0,0)}}let indexExt,enumPointLight=1,enumDirectionalLight=2;class Light{constructor(t,e){this.direction=t,this.color=e}setUniform(t,e){let i=i=>gl.getUniformLocation(t,"Lights["+e+"]."+i);gl.uniform3fv(i("direction"),new Float32Array(this.direction)),gl.uniform3fv(i("color"),new Float32Array(this.color))}}function initShaders(){let t=gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS);maxMaterials=Math.floor((t-14)/4),Nmaterials=Math.min(Math.max(Nmaterials,Materials.length),maxMaterials),noNormalShader=initShader(),pixelShader=initShader(["WIDTH"]),materialShader=initShader(["NORMAL"]),colorShader=initShader(["NORMAL","COLOR"]),transparentShader=initShader(["NORMAL","COLOR","TRANSPARENT"])}function setBuffers(){positionBuffer=gl.createBuffer(),materialBuffer=gl.createBuffer(),colorBuffer=gl.createBuffer(),indexBuffer=gl.createBuffer()}function noGL(){gl||alert("Could not initialize WebGL")}function saveAttributes(){let t=window.parent.document.asygl[alpha];t.gl=gl,t.nlights=Lights.length,t.Nmaterials=Nmaterials,t.maxMaterials=maxMaterials,t.noNormalShader=noNormalShader,t.pixelShader=pixelShader,t.materialShader=materialShader,t.colorShader=colorShader,t.transparentShader=transparentShader}function restoreAttributes(){let t=window.parent.document.asygl[alpha];gl=t.gl,nlights=t.nlights,Nmaterials=t.Nmaterials,maxMaterials=t.maxMaterials,noNormalShader=t.noNormalShader,pixelShader=t.pixelShader,materialShader=t.materialShader,colorShader=t.colorShader,transparentShader=t.transparentShader}function initGL(){if(alpha=Background[3]<1,embedded){let t=window.parent.document;null==t.asygl&&(t.asygl=Array(2)),context=canvas.getContext("2d"),(offscreen=t.offscreen)||(offscreen=t.createElement("canvas"),t.offscreen=offscreen),t.asygl[alpha]&&t.asygl[alpha].gl?(restoreAttributes(),(Lights.length!=nlights||Math.min(Materials.length,maxMaterials)>Nmaterials)&&(initShaders(),saveAttributes())):((gl=offscreen.getContext("webgl",{alpha:alpha}))||noGL(),initShaders(),t.asygl[alpha]={},saveAttributes())}else(gl=canvas.getContext("webgl",{alpha:alpha}))||noGL(),initShaders();setBuffers(),indexExt=gl.getExtension("OES_element_index_uint")}function getShader(t,e,i,a=[]){let r=`#version 100\n#ifdef GL_FRAGMENT_PRECISION_HIGH\n  precision highp float;\n#else\n  precision mediump float;\n#endif\n  #define nlights ${Lights.length}\n\n  const int Nlights=${Math.max(Lights.length,1)};\n\n  #define Nmaterials ${Nmaterials}\n`;orthographic&&(r+="#define ORTHOGRAPHIC\n"),a.forEach(t=>r+="#define "+t+"\n");let n=t.createShader(i);return t.shaderSource(n,r+e),t.compileShader(n),t.getShaderParameter(n,t.COMPILE_STATUS)?n:(alert(t.getShaderInfoLog(n)),null)}function drawBuffer(t,e,i=t.indices){if(0==t.indices.length)return;let a=e==pixelShader,r=e!=noNormalShader&&!a;setUniforms(t,e),gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(t.vertices),gl.STATIC_DRAW),gl.vertexAttribPointer(positionAttribute,3,gl.FLOAT,!1,r?24:a?16:12,0),r&&Lights.length>0?gl.vertexAttribPointer(normalAttribute,3,gl.FLOAT,!1,24,12):a&&gl.vertexAttribPointer(widthAttribute,1,gl.FLOAT,!1,16,12),gl.bindBuffer(gl.ARRAY_BUFFER,materialBuffer),gl.bufferData(gl.ARRAY_BUFFER,new Int16Array(t.materialIndices),gl.STATIC_DRAW),gl.vertexAttribPointer(materialAttribute,1,gl.SHORT,!1,2,0),e!=colorShader&&e!=transparentShader||(gl.bindBuffer(gl.ARRAY_BUFFER,colorBuffer),gl.bufferData(gl.ARRAY_BUFFER,new Uint8Array(t.colors),gl.STATIC_DRAW),gl.vertexAttribPointer(colorAttribute,4,gl.UNSIGNED_BYTE,!0,0,0)),gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer),gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexExt?new Uint32Array(i):new Uint16Array(i),gl.STATIC_DRAW),gl.drawElements(r?gl.TRIANGLES:a?gl.POINTS:gl.LINES,i.length,indexExt?gl.UNSIGNED_INT:gl.UNSIGNED_SHORT,0)}class vertexBuffer{constructor(){this.clear()}clear(){this.vertices=[],this.materialIndices=[],this.colors=[],this.indices=[],this.nvertices=0,this.materials=[],this.materialTable=[]}vertex(t,e){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e[0]),this.vertices.push(e[1]),this.vertices.push(e[2]),this.materialIndices.push(materialIndex),this.nvertices++}Vertex(t,e,i=[0,0,0,0]){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e[0]),this.vertices.push(e[1]),this.vertices.push(e[2]),this.materialIndices.push(materialIndex),this.colors.push(i[0]),this.colors.push(i[1]),this.colors.push(i[2]),this.colors.push(i[3]),this.nvertices++}vertex1(t){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.materialIndices.push(materialIndex),this.nvertices++}vertex0(t,e){return this.vertices.push(t[0]),this.vertices.push(t[1]),this.vertices.push(t[2]),this.vertices.push(e),this.materialIndices.push(materialIndex),this.nvertices++}iVertex(t,e,i,a=[0,0,0,0]){let r=6*t;this.vertices[r]=e[0],this.vertices[r+1]=e[1],this.vertices[r+2]=e[2],this.vertices[r+3]=i[0],this.vertices[r+4]=i[1],this.vertices[r+5]=i[2],this.materialIndices[t]=materialIndex;let n=4*t;this.colors[n]=a[0],this.colors[n+1]=a[1],this.colors[n+2]=a[2],this.colors[n+3]=a[3],this.indices.push(t)}append(t){append(this.vertices,t.vertices),append(this.materialIndices,t.materialIndices),append(this.colors,t.colors),appendOffset(this.indices,t.indices,this.nvertices),this.nvertices+=t.nvertices}}let materialIndex,material0Data=new vertexBuffer,material1Data=new vertexBuffer,materialData=new vertexBuffer,colorData=new vertexBuffer,transparentData=new vertexBuffer,triangleData=new vertexBuffer;function append(t,e){let i=t.length,a=e.length;t.length+=a;for(let r=0;r<a;++r)t[i+r]=e[r]}function appendOffset(t,e,i){let a=t.length,r=e.length;t.length+=e.length;for(let n=0;n<r;++n)t[a+n]=e[n]+i}class Geometry{constructor(){this.data=new vertexBuffer,this.Onscreen=!1,this.m=[]}offscreen(t){let e=projViewMat,i=t[0],a=i[0],r=i[1],n=i[2],s=1/(e[3]*a+e[7]*r+e[11]*n+e[15]);this.x=this.X=(e[0]*a+e[4]*r+e[8]*n+e[12])*s,this.y=this.Y=(e[1]*a+e[5]*r+e[9]*n+e[13])*s;for(let i=1,a=t.length;i<a;++i){let a=t[i],r=a[0],n=a[1],s=a[2],o=1/(e[3]*r+e[7]*n+e[11]*s+e[15]),l=(e[0]*r+e[4]*n+e[8]*s+e[12])*o,h=(e[1]*r+e[5]*n+e[9]*s+e[13])*o;l<this.x?this.x=l:l>this.X&&(this.X=l),h<this.y?this.y=h:h>this.Y&&(this.Y=h)}return(this.X<-1.01||this.x>1.01||this.Y<-1.01||this.y>1.01)&&(this.Onscreen=!1,!0)}T(t){let e=this.c[0],i=this.c[1],a=this.c[2],r=t[0]-e,n=t[1]-i,s=t[2]-a;return[r*normMat[0]+n*normMat[3]+s*normMat[6]+e,r*normMat[1]+n*normMat[4]+s*normMat[7]+i,r*normMat[2]+n*normMat[5]+s*normMat[8]+a]}Tcorners(t,e){return[this.T(t),this.T([t[0],t[1],e[2]]),this.T([t[0],e[1],t[2]]),this.T([t[0],e[1],e[2]]),this.T([e[0],t[1],t[2]]),this.T([e[0],t[1],e[2]]),this.T([e[0],e[1],t[2]]),this.T(e)]}setMaterial(t,e){null==t.materialTable[this.MaterialIndex]&&(t.materials.length>=Nmaterials&&e(),t.materialTable[this.MaterialIndex]=t.materials.length,t.materials.push(Materials[this.MaterialIndex])),materialIndex=t.materialTable[this.MaterialIndex]}render(){let t;if(this.setMaterialIndex(),0==this.CenterIndex?t=corners(this.Min,this.Max):(this.c=Centers[this.CenterIndex-1],t=this.Tcorners(this.Min,this.Max)),this.offscreen(t))return void this.data.clear();let e,i=this.controlpoints;if(0==this.CenterIndex){if(!remesh&&this.Onscreen)return void this.append();e=i}else{let t=i.length;e=Array(t);for(let a=0;a<t;++a)e[a]=this.T(i[a])}let a=orthographic?1:this.Min[2]/B[2],r=pixel*Math.hypot(a*(viewParam.xmax-viewParam.xmin),a*(viewParam.ymax-viewParam.ymin))/size2;this.res2=r*r,this.Epsilon=FillFactor*r,this.data.clear(),this.Onscreen=!0,this.process(e)}}class BezierPatch extends Geometry{constructor(t,e,i,a,r,n){super(),this.controlpoints=t,this.Min=a,this.Max=r,this.color=n,this.CenterIndex=e;let s=t.length;if(n){let t=n[0][3]+n[1][3]+n[2][3];this.transparent=16==s||4==s?t+n[3][3]<1020:t<765}else this.transparent=Materials[i].diffuse[3]<1;this.MaterialIndex=i,this.vertex=this.transparent?this.data.Vertex.bind(this.data):this.data.vertex.bind(this.data),this.L2norm(this.controlpoints)}setMaterialIndex(){this.transparent?this.setMaterial(transparentData,drawTransparent):this.color?this.setMaterial(colorData,drawColor):this.setMaterial(materialData,drawMaterial)}L2norm(t){let e=t[0];this.epsilon=0;let i=t.length;for(let a=1;a<i;++a)this.epsilon=Math.max(this.epsilon,abs2([t[a][0]-e[0],t[a][1]-e[1],t[a][2]-e[2]]));this.epsilon*=Fuzz4}processTriangle(t){let e=t[0],i=t[1],a=t[2],r=unit(cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[a[0]-e[0],a[1]-e[1],a[2]-e[2]]));this.offscreen([e,i,a])||(this.color?(this.data.indices.push(this.data.Vertex(e,r,this.color[0])),this.data.indices.push(this.data.Vertex(i,r,this.color[1])),this.data.indices.push(this.data.Vertex(a,r,this.color[2]))):(this.data.indices.push(this.vertex(e,r)),this.data.indices.push(this.vertex(i,r)),this.data.indices.push(this.vertex(a,r))),this.append())}processQuad(t){let e=t[0],i=t[1],a=t[2],r=t[3],n=cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[a[0]-i[0],a[1]-i[1],a[2]-i[2]]),s=cross([a[0]-r[0],a[1]-r[1],a[2]-r[2]],[r[0]-e[0],r[1]-e[1],r[2]-e[2]]),o=unit([n[0]+s[0],n[1]+s[1],n[2]+s[2]]);if(!this.offscreen([e,i,a,r])){let t,n,s,l;this.color?(t=this.data.Vertex(e,o,this.color[0]),n=this.data.Vertex(i,o,this.color[1]),s=this.data.Vertex(a,o,this.color[2]),l=this.data.Vertex(r,o,this.color[3])):(t=this.vertex(e,o),n=this.vertex(i,o),s=this.vertex(a,o),l=this.vertex(r,o)),this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(s),this.data.indices.push(t),this.data.indices.push(s),this.data.indices.push(l),this.append()}}process(t){if(this.transparent&&(materialIndex=this.color?-1-materialIndex:1+materialIndex),10==t.length)return this.process3(t);if(3==t.length)return this.processTriangle(t);if(4==t.length)return this.processQuad(t);let e=t[0],i=t[3],a=t[12],r=t[15],n=this.normal(i,t[2],t[1],e,t[4],t[8],a);iszero(n)&&iszero(n=this.normal(i,t[2],t[1],e,t[13],t[14],r))&&(n=this.normal(r,t[11],t[7],i,t[4],t[8],a));let s=this.normal(e,t[4],t[8],a,t[13],t[14],r);iszero(s)&&iszero(s=this.normal(e,t[4],t[8],a,t[11],t[7],i))&&(s=this.normal(i,t[2],t[1],e,t[13],t[14],r));let o=this.normal(a,t[13],t[14],r,t[11],t[7],i);iszero(o)&&iszero(o=this.normal(a,t[13],t[14],r,t[2],t[1],e))&&(o=this.normal(e,t[4],t[8],a,t[11],t[7],i));let l=this.normal(r,t[11],t[7],i,t[2],t[1],e);if(iszero(l)&&iszero(l=this.normal(r,t[11],t[7],i,t[4],t[8],a))&&(l=this.normal(a,t[13],t[14],r,t[2],t[1],e)),this.color){let h=this.color[0],c=this.color[1],d=this.color[2],m=this.color[3],f=this.data.Vertex(e,n,h),u=this.data.Vertex(a,s,c),v=this.data.Vertex(r,o,d),p=this.data.Vertex(i,l,m);this.Render(t,f,u,v,p,e,a,r,i,!1,!1,!1,!1,h,c,d,m)}else{let h=this.vertex(e,n),c=this.vertex(a,s),d=this.vertex(r,o),m=this.vertex(i,l);this.Render(t,h,c,d,m,e,a,r,i,!1,!1,!1,!1)}this.data.indices.length>0&&this.append()}append(){this.transparent?transparentData.append(this.data):this.color?colorData.append(this.data):materialData.append(this.data)}Render(t,e,i,a,r,n,s,o,l,h,c,d,m,f,u,v,p){if(this.Distance(t)<this.res2)this.offscreen([n,s,o])||(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(a)),this.offscreen([n,o,l])||(this.data.indices.push(e),this.data.indices.push(a),this.data.indices.push(r));else{if(this.offscreen(t))return;let g=t[0],x=t[3],M=t[12],w=t[15],A=new Split3(g,t[1],t[2],x),b=new Split3(t[4],t[5],t[6],t[7]),S=new Split3(t[8],t[9],t[10],t[11]),R=new Split3(M,t[13],t[14],w),D=new Split3(g,t[4],t[8],M),P=new Split3(A.m0,b.m0,S.m0,R.m0),T=new Split3(A.m3,b.m3,S.m3,R.m3),y=new Split3(A.m5,b.m5,S.m5,R.m5),I=new Split3(A.m4,b.m4,S.m4,R.m4),z=new Split3(A.m2,b.m2,S.m2,R.m2),O=new Split3(x,t[7],t[11],w),E=[g,A.m0,A.m3,A.m5,D.m0,P.m0,T.m0,y.m0,D.m3,P.m3,T.m3,y.m3,D.m5,P.m5,T.m5,y.m5],_=[D.m5,P.m5,T.m5,y.m5,D.m4,P.m4,T.m4,y.m4,D.m2,P.m2,T.m2,y.m2,M,R.m0,R.m3,R.m5],L=[y.m5,I.m5,z.m5,O.m5,y.m4,I.m4,z.m4,O.m4,y.m2,I.m2,z.m2,O.m2,R.m5,R.m4,R.m2,w],N=[A.m5,A.m4,A.m2,x,y.m0,I.m0,z.m0,O.m0,y.m3,I.m3,z.m3,O.m3,y.m5,I.m5,z.m5,O.m5],B=E[15],C=this.normal(E[0],E[4],E[8],E[12],E[13],E[14],E[15]);iszero(C)&&iszero(C=this.normal(E[0],E[4],E[8],E[12],E[11],E[7],E[3]))&&(C=this.normal(E[3],E[2],E[1],E[0],E[13],E[14],E[15]));let F=this.normal(_[12],_[13],_[14],_[15],_[11],_[7],_[3]);iszero(F)&&iszero(F=this.normal(_[12],_[13],_[14],_[15],_[2],_[1],_[0]))&&(F=this.normal(_[0],_[4],_[8],_[12],_[11],_[7],_[3]));let V=this.normal(L[15],L[11],L[7],L[3],L[2],L[1],L[0]);iszero(V)&&iszero(V=this.normal(L[15],L[11],L[7],L[3],L[4],L[8],L[12]))&&(V=this.normal(L[12],L[13],L[14],L[15],L[2],L[1],L[0]));let H=this.normal(N[3],N[2],N[1],N[0],N[4],N[8],N[12]);iszero(H)&&iszero(H=this.normal(N[3],N[2],N[1],N[0],N[13],N[14],N[15]))&&(H=this.normal(N[15],N[11],N[7],N[3],N[4],N[8],N[12]));let G=this.normal(L[3],L[2],L[1],B,L[4],L[8],L[12]),U=this.Epsilon,W=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])];if(!h)if(h=Straightness(g,t[4],t[8],M)<this.res2){let t=unit(this.derivative(_[0],_[1],_[2],_[3]));W=[W[0]-U*t[0],W[1]-U*t[1],W[2]-U*t[2]]}else W=E[12];let Y=[.5*(s[0]+o[0]),.5*(s[1]+o[1]),.5*(s[2]+o[2])];if(!c)if(c=Straightness(M,t[13],t[14],w)<this.res2){let t=unit(this.derivative(L[12],L[8],L[4],L[0]));Y=[Y[0]-U*t[0],Y[1]-U*t[1],Y[2]-U*t[2]]}else Y=_[15];let j=[.5*(o[0]+l[0]),.5*(o[1]+l[1]),.5*(o[2]+l[2])];if(!d)if(d=Straightness(w,t[11],t[7],x)<this.res2){let t=unit(this.derivative(N[15],L[14],L[13],_[12]));j=[j[0]-U*t[0],j[1]-U*t[1],j[2]-U*t[2]]}else j=L[3];let k=[.5*(l[0]+n[0]),.5*(l[1]+n[1]),.5*(l[2]+n[2])];if(!m)if(m=Straightness(g,t[1],t[2],x)<this.res2){let t=unit(this.derivative(E[3],E[7],E[11],E[15]));k=[k[0]-U*t[0],k[1]-U*t[1],k[2]-U*t[2]]}else k=N[0];if(f){let t=Array(4),g=Array(4),x=Array(4),M=Array(4),w=Array(4);for(let e=0;e<4;++e)t[e]=.5*(f[e]+u[e]),g[e]=.5*(u[e]+v[e]),x[e]=.5*(v[e]+p[e]),M[e]=.5*(p[e]+f[e]),w[e]=.5*(t[e]+x[e]);let A=this.data.Vertex(W,C,t),b=this.data.Vertex(Y,F,g),S=this.data.Vertex(j,V,x),R=this.data.Vertex(k,H,M),D=this.data.Vertex(B,G,w);this.Render(E,e,A,D,R,n,W,B,k,h,!1,!1,m,f,t,w,M),this.Render(_,A,i,b,D,W,s,Y,B,h,c,!1,!1,t,u,g,w),this.Render(L,D,b,a,S,B,Y,o,j,!1,c,d,!1,w,g,v,x),this.Render(N,R,D,S,r,k,B,j,l,!1,!1,d,m,M,w,x,p)}else{let t=this.vertex(W,C),f=this.vertex(Y,F),u=this.vertex(j,V),v=this.vertex(k,H),p=this.vertex(B,G);this.Render(E,e,t,p,v,n,W,B,k,h,!1,!1,m),this.Render(_,t,i,f,p,W,s,Y,B,h,c,!1,!1),this.Render(L,p,f,a,u,B,Y,o,j,!1,c,d,!1),this.Render(N,v,p,u,r,k,B,j,l,!1,!1,d,m)}}}process3(t){this.Res2=BezierFactor*BezierFactor*this.res2;let e=t[0],i=t[6],a=t[9],r=this.normal(a,t[5],t[2],e,t[1],t[3],i),n=this.normal(e,t[1],t[3],i,t[7],t[8],a),s=this.normal(i,t[7],t[8],a,t[5],t[2],e);if(this.color){let o=this.color[0],l=this.color[1],h=this.color[2],c=this.data.Vertex(e,r,o),d=this.data.Vertex(i,n,l),m=this.data.Vertex(a,s,h);this.Render3(t,c,d,m,e,i,a,!1,!1,!1,o,l,h)}else{let o=this.vertex(e,r),l=this.vertex(i,n),h=this.vertex(a,s);this.Render3(t,o,l,h,e,i,a,!1,!1,!1)}this.data.indices.length>0&&this.append()}Render3(t,e,i,a,r,n,s,o,l,h,c,d,m){if(this.Distance3(t)<this.Res2)this.offscreen([r,n,s])||(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(a));else{if(this.offscreen(t))return;let f=t[0],u=t[1],v=t[2],p=t[3],g=t[4],x=t[5],M=t[6],w=t[7],A=t[8],b=t[9],S=[.5*(b[0]+x[0]),.5*(b[1]+x[1]),.5*(b[2]+x[2])],R=[.5*(b[0]+A[0]),.5*(b[1]+A[1]),.5*(b[2]+A[2])],D=[.5*(x[0]+v[0]),.5*(x[1]+v[1]),.5*(x[2]+v[2])],P=[.5*(A[0]+g[0]),.5*(A[1]+g[1]),.5*(A[2]+g[2])],T=[.5*(A[0]+w[0]),.5*(A[1]+w[1]),.5*(A[2]+w[2])],y=[.5*(v[0]+g[0]),.5*(v[1]+g[1]),.5*(v[2]+g[2])],I=[.5*(v[0]+f[0]),.5*(v[1]+f[1]),.5*(v[2]+f[2])],z=[.5*(g[0]+p[0]),.5*(g[1]+p[1]),.5*(g[2]+p[2])],O=[.5*(w[0]+M[0]),.5*(w[1]+M[1]),.5*(w[2]+M[2])],E=[.5*(f[0]+u[0]),.5*(f[1]+u[1]),.5*(f[2]+u[2])],_=[.5*(u[0]+p[0]),.5*(u[1]+p[1]),.5*(u[2]+p[2])],L=[.5*(p[0]+M[0]),.5*(p[1]+M[1]),.5*(p[2]+M[2])],N=[.5*(S[0]+D[0]),.5*(S[1]+D[1]),.5*(S[2]+D[2])],B=[.5*(R[0]+T[0]),.5*(R[1]+T[1]),.5*(R[2]+T[2])],C=[.5*(D[0]+I[0]),.5*(D[1]+I[1]),.5*(D[2]+I[2])],F=[.5*P[0]+.25*(g[0]+u[0]),.5*P[1]+.25*(g[1]+u[1]),.5*P[2]+.25*(g[2]+u[2])],V=[.5*(T[0]+O[0]),.5*(T[1]+O[1]),.5*(T[2]+O[2])],H=[.5*y[0]+.25*(g[0]+w[0]),.5*y[1]+.25*(g[1]+w[1]),.5*y[2]+.25*(g[2]+w[2])],G=[.25*(x[0]+g[0])+.5*z[0],.25*(x[1]+g[1])+.5*z[1],.25*(x[2]+g[2])+.5*z[2]],U=[.5*(E[0]+_[0]),.5*(E[1]+_[1]),.5*(E[2]+_[2])],W=[.5*(_[0]+L[0]),.5*(_[1]+L[1]),.5*(_[2]+L[2])],Y=[.5*(H[0]+U[0]),.5*(H[1]+U[1]),.5*(H[2]+U[2])],j=[.5*(H[0]+W[0]),.5*(H[1]+W[1]),.5*(H[2]+W[2])],k=[.5*(U[0]+W[0]),.5*(U[1]+W[1]),.5*(U[2]+W[2])],X=[.5*(G[0]+V[0]),.5*(G[1]+V[1]),.5*(G[2]+V[2])],Z=[.5*(B[0]+G[0]),.5*(B[1]+G[1]),.5*(B[2]+G[2])],q=[.5*(B[0]+V[0]),.5*(B[1]+V[1]),.5*(B[2]+V[2])],K=[.5*(N[0]+F[0]),.5*(N[1]+F[1]),.5*(N[2]+F[2])],$=[.5*(C[0]+F[0]),.5*(C[1]+F[1]),.5*(C[2]+F[2])],Q=[.5*(N[0]+C[0]),.5*(N[1]+C[1]),.5*(N[2]+C[2])],J=[f,E,I,U,[.5*(y[0]+E[0]),.5*(y[1]+E[1]),.5*(y[2]+E[2])],C,k,Y,$,Q],tt=[k,W,j,L,[.5*(z[0]+O[0]),.5*(z[1]+O[1]),.5*(z[2]+O[2])],X,M,O,V,q],et=[Q,K,N,Z,[.5*(S[0]+P[0]),.5*(S[1]+P[1]),.5*(S[2]+P[2])],S,q,B,R,b],it=[q,Z,X,K,[.25*(D[0]+T[0]+_[0]+g[0]),.25*(D[1]+T[1]+_[1]+g[1]),.25*(D[2]+T[2]+_[2]+g[2])],j,Q,$,Y,k],at=this.normal(k,j,X,q,Z,K,Q),rt=this.normal(q,Z,K,Q,$,Y,k),nt=this.normal(Q,$,Y,k,j,X,q),st=this.Epsilon,ot=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])];if(!o)if(o=Straightness(M,w,A,b)<this.res2){let t=unit(this.sumderivative(it[0],it[2],it[5],it[9],it[1],it[3],it[6]));ot=[ot[0]-st*t[0],ot[1]-st*t[1],ot[2]-st*t[2]]}else ot=q;let lt=[.5*(s[0]+r[0]),.5*(s[1]+r[1]),.5*(s[2]+r[2])];if(!l)if(l=Straightness(f,v,x,b)<this.res2){let t=unit(this.sumderivative(it[6],it[3],it[1],it[0],it[7],it[8],it[9]));lt=[lt[0]-st*t[0],lt[1]-st*t[1],lt[2]-st*t[2]]}else lt=Q;let ht=[.5*(r[0]+n[0]),.5*(r[1]+n[1]),.5*(r[2]+n[2])];if(!h)if(h=Straightness(f,u,p,M)<this.res2){let t=unit(this.sumderivative(it[9],it[8],it[7],it[6],it[5],it[2],it[0]));ht=[ht[0]-st*t[0],ht[1]-st*t[1],ht[2]-st*t[2]]}else ht=k;if(c){let t=Array(4),f=Array(4),u=Array(4);for(let e=0;e<4;++e)t[e]=.5*(d[e]+m[e]),f[e]=.5*(m[e]+c[e]),u[e]=.5*(c[e]+d[e]);let v=this.data.Vertex(ot,at,t),p=this.data.Vertex(lt,rt,f),g=this.data.Vertex(ht,nt,u);this.Render3(J,e,g,p,r,ht,lt,!1,l,h,c,u,f),this.Render3(tt,g,i,v,ht,n,ot,o,!1,h,u,d,t),this.Render3(et,p,v,a,lt,ot,s,o,l,!1,f,t,m),this.Render3(it,v,p,g,ot,lt,ht,!1,!1,!1,t,f,u)}else{let t=this.vertex(ot,at),c=this.vertex(lt,rt),d=this.vertex(ht,nt);this.Render3(J,e,d,c,r,ht,lt,!1,l,h),this.Render3(tt,d,i,t,ht,n,ot,o,!1,h),this.Render3(et,c,t,a,lt,ot,s,o,l,!1),this.Render3(it,t,c,d,ot,lt,ht,!1,!1,!1)}}}Distance(t){let e=t[0],i=t[3],a=t[12],r=t[15],n=Distance2(r,e,this.normal(i,t[2],t[1],e,t[4],t[8],a));return n=Math.max(n,Straightness(e,t[1],t[2],i)),n=Math.max(n,Straightness(e,t[4],t[8],a)),n=Math.max(n,Straightness(i,t[7],t[11],r)),n=Math.max(n,Straightness(a,t[13],t[14],r)),n=Math.max(n,Straightness(t[4],t[5],t[6],t[7])),n=Math.max(n,Straightness(t[8],t[9],t[10],t[11])),n=Math.max(n,Straightness(t[1],t[5],t[9],t[13])),Math.max(n,Straightness(t[2],t[6],t[10],t[14]))}Distance3(t){let e=t[0],i=t[4],a=t[6],r=t[9],n=abs2([(e[0]+a[0]+r[0])*third-i[0],(e[1]+a[1]+r[1])*third-i[1],(e[2]+a[2]+r[2])*third-i[2]]);return n=Math.max(n,Straightness(e,t[1],t[3],a)),n=Math.max(n,Straightness(e,t[2],t[5],r)),Math.max(n,Straightness(a,t[7],t[8],r))}derivative(t,e,i,a){let r=[e[0]-t[0],e[1]-t[1],e[2]-t[2]];if(abs2(r)>this.epsilon)return r;let n=bezierPP(t,e,i);return abs2(n)>this.epsilon?n:bezierPPP(t,e,i,a)}sumderivative(t,e,i,a,r,n,s){let o=this.derivative(t,e,i,a),l=this.derivative(t,r,n,s);return[o[0]+l[0],o[1]+l[1],o[2]+l[2]]}normal(t,e,i,a,r,n,s){let o=r[0]-a[0],l=r[1]-a[1],h=r[2]-a[2],c=i[0]-a[0],d=i[1]-a[1],m=i[2]-a[2],f=[l*m-h*d,h*c-o*m,o*d-l*c];if(abs2(f)>this.epsilon)return unit(f);let u=[c,d,m],v=[o,l,h],p=bezierPP(a,i,e),g=bezierPP(a,r,n),x=cross(g,u),M=cross(v,p);if(abs2(f=[x[0]+M[0],x[1]+M[1],x[2]+M[2]])>this.epsilon)return unit(f);let w=bezierPPP(a,i,e,t),A=bezierPPP(a,r,n,s);x=cross(g,p),M=cross(v,w);let b=cross(A,u),S=cross(A,p),R=cross(g,w),D=cross(A,w);return unit([9*x[0]+3*(M[0]+b[0]+S[0]+R[0])+D[0],9*x[1]+3*(M[1]+b[1]+S[1]+R[1])+D[1],9*x[2]+3*(M[2]+b[2]+S[2]+R[2])+D[2]])}}class BezierCurve extends Geometry{constructor(t,e,i,a,r){super(),this.controlpoints=t,this.Min=a,this.Max=r,this.CenterIndex=e,this.MaterialIndex=i}setMaterialIndex(){this.setMaterial(material1Data,drawMaterial1)}processLine(t){let e=t[0],i=t[1];this.offscreen([e,i])||(this.data.indices.push(this.data.vertex1(e)),this.data.indices.push(this.data.vertex1(i)),this.append())}process(t){if(2==t.length)return this.processLine(t);let e=this.data.vertex1(t[0]),i=this.data.vertex1(t[3]);this.Render(t,e,i),this.data.indices.length>0&&this.append()}append(){material1Data.append(this.data)}Render(t,e,i){let a=t[0],r=t[1],n=t[2],s=t[3];if(Straightness(a,r,n,s)<this.res2)this.offscreen([a,s])||(this.data.indices.push(e),this.data.indices.push(i));else{if(this.offscreen(t))return;let o=[.5*(a[0]+r[0]),.5*(a[1]+r[1]),.5*(a[2]+r[2])],l=[.5*(r[0]+n[0]),.5*(r[1]+n[1]),.5*(r[2]+n[2])],h=[.5*(n[0]+s[0]),.5*(n[1]+s[1]),.5*(n[2]+s[2])],c=[.5*(o[0]+l[0]),.5*(o[1]+l[1]),.5*(o[2]+l[2])],d=[.5*(l[0]+h[0]),.5*(l[1]+h[1]),.5*(l[2]+h[2])],m=[.5*(c[0]+d[0]),.5*(c[1]+d[1]),.5*(c[2]+d[2])],f=[a,o,c,m],u=[m,d,h,s],v=this.data.vertex1(m);this.Render(f,e,v),this.Render(u,v,i)}}}class Pixel extends Geometry{constructor(t,e,i,a,r){super(),this.controlpoint=t,this.width=e,this.CenterIndex=0,this.MaterialIndex=i,this.Min=a,this.Max=r}setMaterialIndex(){this.setMaterial(material0Data,drawMaterial0)}process(t){this.data.indices.push(this.data.vertex0(this.controlpoint,this.width)),this.append()}append(){material0Data.append(this.data)}}class Triangles extends Geometry{constructor(t,e,i){super(),this.CenterIndex=0,this.MaterialIndex=t,this.Min=e,this.Max=i,this.Positions=Positions,this.Normals=Normals,this.Colors=Colors,this.Indices=Indices,Positions=[],Normals=[],Colors=[],Indices=[],this.transparent=Materials[t].diffuse[3]<1}setMaterialIndex(){this.transparent?this.setMaterial(transparentData,drawTransparent):this.setMaterial(triangleData,drawTriangle)}process(t){materialIndex=this.Colors.length>0?-1-materialIndex:1+materialIndex;for(let t=0,e=this.Indices.length;t<e;++t){let e=this.Indices[t],i=e[0],a=this.Positions[i[0]],r=this.Positions[i[1]],n=this.Positions[i[2]];if(!this.offscreen([a,r,n])){let t=e.length>1?e[1]:i;if(t&&0!=t.length||(t=i),this.Colors.length>0){let s=e.length>2?e[2]:i;s&&0!=s.length||(s=i);let o=this.Colors[s[0]],l=this.Colors[s[1]],h=this.Colors[s[2]];this.transparent|=o[3]+l[3]+h[3]<765,this.data.iVertex(i[0],a,this.Normals[t[0]],o),this.data.iVertex(i[1],r,this.Normals[t[1]],l),this.data.iVertex(i[2],n,this.Normals[t[2]],h)}else this.data.iVertex(i[0],a,this.Normals[t[0]]),this.data.iVertex(i[1],r,this.Normals[t[1]]),this.data.iVertex(i[2],n,this.Normals[t[2]])}}this.data.nvertices=this.Positions.length,this.data.indices.length>0&&this.append()}append(){this.transparent?transparentData.append(this.data):triangleData.append(this.data)}}function home(){mat4.identity(rotMat),initProjection(),setProjection(),remesh=!0,redraw=!0}let positionAttribute=0,normalAttribute=1,materialAttribute=2,colorAttribute=3,widthAttribute=4;function initShader(t=[]){let e=getShader(gl,vertex,gl.VERTEX_SHADER,t),i=getShader(gl,fragment,gl.FRAGMENT_SHADER,t),a=gl.createProgram();return gl.attachShader(a,e),gl.attachShader(a,i),gl.bindAttribLocation(a,positionAttribute,"position"),gl.bindAttribLocation(a,normalAttribute,"normal"),gl.bindAttribLocation(a,materialAttribute,"materialIndex"),gl.bindAttribLocation(a,colorAttribute,"color"),gl.bindAttribLocation(a,widthAttribute,"width"),gl.linkProgram(a),gl.getProgramParameter(a,gl.LINK_STATUS)||alert("Could not initialize shaders"),a}class Split3{constructor(t,e,i,a){this.m0=[.5*(t[0]+e[0]),.5*(t[1]+e[1]),.5*(t[2]+e[2])];let r=.5*(e[0]+i[0]),n=.5*(e[1]+i[1]),s=.5*(e[2]+i[2]);this.m2=[.5*(i[0]+a[0]),.5*(i[1]+a[1]),.5*(i[2]+a[2])],this.m3=[.5*(this.m0[0]+r),.5*(this.m0[1]+n),.5*(this.m0[2]+s)],this.m4=[.5*(r+this.m2[0]),.5*(n+this.m2[1]),.5*(s+this.m2[2])],this.m5=[.5*(this.m3[0]+this.m4[0]),.5*(this.m3[1]+this.m4[1]),.5*(this.m3[2]+this.m4[2])]}}function iszero(t){return 0==t[0]&&0==t[1]&&0==t[2]}function unit(t){let e=1/(Math.sqrt(t[0]*t[0]+t[1]*t[1]+t[2]*t[2])||1);return[t[0]*e,t[1]*e,t[2]*e]}function abs2(t){return t[0]*t[0]+t[1]*t[1]+t[2]*t[2]}function dot(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function cross(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function bezierPP(t,e,i){return[t[0]+i[0]-2*e[0],t[1]+i[1]-2*e[1],t[2]+i[2]-2*e[2]]}function bezierPPP(t,e,i,a){return[a[0]-t[0]+3*(e[0]-i[0]),a[1]-t[1]+3*(e[1]-i[1]),a[2]-t[2]+3*(e[2]-i[2])]}function Straightness(t,e,i,a){let r=[third*(a[0]-t[0]),third*(a[1]-t[1]),third*(a[2]-t[2])];return Math.max(abs2([e[0]-r[0]-t[0],e[1]-r[1]-t[1],e[2]-r[2]-t[2]]),abs2([a[0]-r[0]-i[0],a[1]-r[1]-i[1],a[2]-r[2]-i[2]]))}function Distance2(t,e,i){let a=dot([t[0]-e[0],t[1]-e[1],t[2]-e[2]],i);return a*a}function corners(t,e){return[t,[t[0],t[1],e[2]],[t[0],e[1],t[2]],[t[0],e[1],e[2]],[e[0],t[1],t[2]],[e[0],t[1],e[2]],[e[0],e[1],t[2]],e]}function COBTarget(t,e){mat4.fromTranslation(translMat,[center.x,center.y,center.z]),mat4.invert(cjMatInv,translMat),mat4.multiply(t,e,cjMatInv),mat4.multiply(t,translMat,t)}function setUniforms(t,e){let i=e==pixelShader;gl.useProgram(e),gl.enableVertexAttribArray(positionAttribute),i&&gl.enableVertexAttribArray(widthAttribute);let a=e!=noNormalShader&&!i&&Lights.length>0;if(a&&gl.enableVertexAttribArray(normalAttribute),gl.enableVertexAttribArray(materialAttribute),e.projViewMatUniform=gl.getUniformLocation(e,"projViewMat"),e.viewMatUniform=gl.getUniformLocation(e,"viewMat"),e.normMatUniform=gl.getUniformLocation(e,"normMat"),e!=colorShader&&e!=transparentShader||gl.enableVertexAttribArray(colorAttribute),a)for(let t=0;t<Lights.length;++t)Lights[t].setUniform(e,t);for(let i=0;i<t.materials.length;++i)t.materials[i].setUniform(e,i);gl.uniformMatrix4fv(e.projViewMatUniform,!1,projViewMat),gl.uniformMatrix4fv(e.viewMatUniform,!1,viewMat),gl.uniformMatrix3fv(e.normMatUniform,!1,normMat)}function handleMouseDown(t){mouseDownOrTouchActive=!0,lastMouseX=t.clientX,lastMouseY=t.clientY}let pinchStart,touchStartTime,pinch=!1;function pinchDistance(t){return Math.hypot(t[0].pageX-t[1].pageX,t[0].pageY-t[1].pageY)}function handleTouchStart(t){t.preventDefault();let e=t.targetTouches;swipe=rotate=pinch=!1,zooming||(1!=e.length||mouseDownOrTouchActive||(touchStartTime=(new Date).getTime(),touchId=e[0].identifier,lastMouseX=e[0].pageX,lastMouseY=e[0].pageY),2!=e.length||mouseDownOrTouchActive||(touchId=e[0].identifier,pinchStart=pinchDistance(e),pinch=!0))}function handleMouseUpOrTouchEnd(t){mouseDownOrTouchActive=!1}function rotateScene(t,e,i,a,r){if(t==i&&e==a)return;let[n,s]=arcball([t,-e],[i,-a]);mat4.fromRotation(rotMats,2*r*ArcballFactor*n/lastzoom,s),mat4.multiply(rotMat,rotMats,rotMat)}function shiftScene(t,e,i,a){let r=1/lastzoom;shift.x+=(i-t)*r*halfCanvasWidth,shift.y-=(a-e)*r*halfCanvasHeight}function panScene(t,e,i,a){orthographic?shiftScene(t,e,i,a):(center.x+=(i-t)*(viewParam.xmax-viewParam.xmin),center.y-=(a-e)*(viewParam.ymax-viewParam.ymin))}function updateViewMatrix(){COBTarget(viewMat,rotMat),mat4.translate(viewMat,viewMat,[center.x,center.y,0]),mat3.fromMat4(viewMat3,viewMat),mat3.invert(normMat,viewMat3),mat4.multiply(projViewMat,projMat,viewMat)}function capzoom(){let t=Math.sqrt(Number.MAX_VALUE),e=1/t;Zoom<=e&&(Zoom=e),Zoom>=t&&(Zoom=t),Zoom!=lastzoom&&(remesh=!0),lastzoom=Zoom}function zoomImage(t){let e=zoomStep*halfCanvasHeight*t;const i=Math.log(.1*Number.MAX_VALUE)/Math.log(zoomFactor);Math.abs(e)<i&&(Zoom*=zoomFactor**e,capzoom())}function normMouse(t){let e=t[0],i=t[1],a=Math.hypot(e,i);return a>1&&(denom=1/a,e*=denom,i*=denom),[e,i,Math.sqrt(Math.max(1-i*i-e*e,0))]}function arcball(t,e){let i=normMouse(t),a=normMouse(e),r=dot(i,a);return r>1?r=1:r<-1&&(r=-1),[Math.acos(r),unit(cross(i,a))]}function zoomScene(t,e,i,a){zoomImage(e-a)}const DRAGMODE_ROTATE=1,DRAGMODE_SHIFT=2,DRAGMODE_ZOOM=3,DRAGMODE_PAN=4;function processDrag(t,e,i,a=1){let r;switch(i){case DRAGMODE_ROTATE:r=rotateScene;break;case DRAGMODE_SHIFT:r=shiftScene;break;case DRAGMODE_ZOOM:r=zoomScene;break;case DRAGMODE_PAN:r=panScene;break;default:r=((t,e,i,a)=>{})}r((lastMouseX-halfCanvasWidth)/halfCanvasWidth,(lastMouseY-halfCanvasHeight)/halfCanvasHeight,(t-halfCanvasWidth)/halfCanvasWidth,(e-halfCanvasHeight)/halfCanvasHeight,a),lastMouseX=t,lastMouseY=e,setProjection(),redraw=!0}function handleKey(t){let e=[];switch(t.key){case"x":e=[1,0,0];break;case"y":e=[0,1,0];break;case"z":e=[0,0,1];break;case"h":home();break;case"+":case"=":case">":expand();break;case"-":case"_":case"<":shrink()}e.length>0&&(mat4.rotate(rotMat,rotMat,.1,e),updateViewMatrix(),redraw=!0)}function handleMouseWheel(t){t.preventDefault(),t.deltaY<0?Zoom*=zoomFactor:Zoom/=zoomFactor,capzoom(),setProjection(),redraw=!0}function handleMouseMove(t){if(!mouseDownOrTouchActive)return;let e;processDrag(t.clientX,t.clientY,e=t.getModifierState("Control")?DRAGMODE_SHIFT:t.getModifierState("Shift")?DRAGMODE_ZOOM:t.getModifierState("Alt")?DRAGMODE_PAN:DRAGMODE_ROTATE)}let zooming=!1,swipe=!1,rotate=!1;function handleTouchMove(t){if(t.preventDefault(),zooming)return;let e=t.targetTouches;if(!pinch&&1==e.length&&touchId==e[0].identifier){let t=e[0].pageX,i=e[0].pageY,a=t-lastMouseX,r=i-lastMouseY,n=a*a+r*r<=shiftHoldDistance*shiftHoldDistance;if(n&&!swipe&&!rotate&&(new Date).getTime()-touchStartTime>shiftWaitTime&&(navigator.vibrate&&window.navigator.vibrate(vibrateTime),swipe=!0),swipe)processDrag(t,i,DRAGMODE_SHIFT);else if(!n){rotate=!0,processDrag(e[0].pageX,e[0].pageY,DRAGMODE_ROTATE,.5)}}if(pinch&&!swipe&&2==e.length&&touchId==e[0].identifier){let t=pinchDistance(e),i=t-pinchStart;zooming=!0,(i*=zoomPinchFactor)>zoomPinchCap&&(i=zoomPinchCap),i<-zoomPinchCap&&(i=-zoomPinchCap),zoomImage(i/size2),pinchStart=t,swipe=rotate=zooming=!1,setProjection(),redraw=!0}}let pixelShader,noNormalShader,materialShader,colorShader,transparentShader,zbuffer=[];function transformVertices(t){let e=viewMat[2],i=viewMat[6],a=viewMat[10];zbuffer.length=t.length;for(let r=0;r<t.length;++r){let n=6*r;zbuffer[r]=e*t[n]+i*t[n+1]+a*t[n+2]}}function drawMaterial0(){drawBuffer(material0Data,pixelShader),material0Data.clear()}function drawMaterial1(){drawBuffer(material1Data,noNormalShader),material1Data.clear()}function drawMaterial(){drawBuffer(materialData,materialShader),materialData.clear()}function drawColor(){drawBuffer(colorData,colorShader),colorData.clear()}function drawTriangle(){drawBuffer(triangleData,transparentShader),triangleData.clear()}function drawTransparent(){let t=transparentData.indices;if(t.length>0){transformVertices(transparentData.vertices);let e=t.length/3,i=Array(e).fill().map((t,e)=>e);i.sort(function(e,i){let a=3*e;Ia=t[a],Ib=t[a+1],Ic=t[a+2];let r=3*i;return IA=t[r],IB=t[r+1],IC=t[r+2],zbuffer[Ia]+zbuffer[Ib]+zbuffer[Ic]<zbuffer[IA]+zbuffer[IB]+zbuffer[IC]?-1:1});let a=Array(t.length);for(let r=0;r<e;++r){let e=3*i[r];a[3*r]=t[e],a[3*r+1]=t[e+1],a[3*r+2]=t[e+2]}gl.depthMask(!1),drawBuffer(transparentData,transparentShader,a),gl.depthMask(!0)}transparentData.clear()}function drawBuffers(){drawMaterial0(),drawMaterial1(),drawMaterial(),drawColor(),drawTriangle(),drawTransparent()}function draw(){embedded&&(offscreen.width=canvas.width,offscreen.height=canvas.height,setViewport()),gl.clearColor(Background[0],Background[1],Background[2],Background[3]),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);for(let t=0;t<P.length;++t)P[t].render();drawBuffers(),embedded&&(context.clearRect(0,0,canvas.width,canvas.height),context.drawImage(offscreen,0,0)),remesh=!1}function tick(){requestAnimationFrame(tick),redraw&&(draw(),redraw=!1)}function setDimensions(t,e,i,a){let r=t/e,n=1/lastzoom,s=(i/t+viewportshift[0])*lastzoom,o=(a/e+viewportshift[1])*lastzoom;if(orthographic){let t=B[0]-b[0],e=B[1]-b[1];if(t<e*r){let t=.5*e*r*n,i=2*t*s,a=e*n*o;viewParam.xmin=-t-i,viewParam.xmax=t-i,viewParam.ymin=b[1]*n-a,viewParam.ymax=B[1]*n-a}else{let e=.5*t/(r*Zoom),i=t*n*s,a=2*e*o;viewParam.xmin=b[0]*n-i,viewParam.xmax=B[0]*n-i,viewParam.ymin=-e-a,viewParam.ymax=e-a}}else{let t=H*n,e=t*r,i=2*e*s,a=2*t*o;viewParam.xmin=-e-i,viewParam.xmax=e-i,viewParam.ymin=-t-a,viewParam.ymax=t-a}}function setProjection(){setDimensions(canvasWidth,canvasHeight,shift.x,shift.y),(orthographic?mat4.ortho:mat4.frustum)(projMat,viewParam.xmin,viewParam.xmax,viewParam.ymin,viewParam.ymax,-viewParam.zmax,-viewParam.zmin),updateViewMatrix()}function initProjection(){H=-Math.tan(.5*angle)*B[2],center.x=center.y=0,center.z=.5*(b[2]+B[2]),lastzoom=Zoom=Zoom0,viewParam.zmin=b[2],viewParam.zmax=B[2],shift.x=shift.y=0}function setViewport(){gl.viewportWidth=canvasWidth,gl.viewportHeight=canvasHeight,gl.viewport(0,0,gl.viewportWidth,gl.viewportHeight),gl.scissor(0,0,gl.viewportWidth,gl.viewportHeight)}function setCanvas(){canvas.width=canvasWidth,canvas.height=canvasHeight,embedded&&(offscreen.width=canvasWidth,offscreen.height=canvasHeight),size2=Math.hypot(canvasWidth,canvasHeight),halfCanvasWidth=.5*canvasWidth,halfCanvasHeight=.5*canvasHeight}function setsize(t,e){t>maxViewportWidth&&(t=maxViewportWidth),e>maxViewportHeight&&(e=maxViewportHeight),shift.x*=t/canvasWidth,shift.y*=e/canvasHeight,canvasWidth=t,canvasHeight=e,setCanvas(),setViewport(),home()}function expand(){setsize(canvasWidth*resizeStep+.5,canvasHeight*resizeStep+.5)}function shrink(){setsize(Math.max(canvasWidth/resizeStep+.5,1),Math.max(canvasHeight/resizeStep+.5,1))}function webGLStart(){if(canvas=document.getElementById("Asymptote"),embedded=window.parent.document!=document,initGL(),absolute&&!embedded)canvasWidth*=window.devicePixelRatio,canvasHeight*=window.devicePixelRatio;else{0==canvas.width&&(canvas.width=Math.max(window.innerWidth-windowTrim,windowTrim)),0==canvas.height&&(canvas.height=Math.max(window.innerHeight-windowTrim,windowTrim));let t=canvasWidth/canvasHeight;canvas.width>canvas.height*t?canvas.width=Math.min(canvas.height*t,canvas.width):canvas.height=Math.min(canvas.width/t,canvas.height),canvas.width>0&&(canvasWidth=canvas.width),canvas.height>0&&(canvasHeight=canvas.height)}setCanvas(),ArcballFactor=1+8*Math.hypot(viewportmargin[0],viewportmargin[1])/size2,viewportshift[0]/=Zoom0,viewportshift[1]/=Zoom0,gl.enable(gl.BLEND),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA),gl.enable(gl.DEPTH_TEST),gl.enable(gl.SCISSOR_TEST),setViewport(),home(),canvas.onmousedown=handleMouseDown,document.onmouseup=handleMouseUpOrTouchEnd,document.onmousemove=handleMouseMove,canvas.onkeydown=handleKey,canvas.addEventListener("wheel",handleMouseWheel,!1),canvas.addEventListener("touchstart",handleTouchStart,!1),canvas.addEventListener("touchend",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchcancel",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchleave",handleMouseUpOrTouchEnd,!1),canvas.addEventListener("touchmove",handleTouchMove,!1),document.addEventListener("keydown",handleKey,!1),tick()}