summaryrefslogtreecommitdiff
path: root/graphics/asymptote/base
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-30 03:00:43 +0000
committerNorbert Preining <norbert@preining.info>2019-09-30 03:00:43 +0000
commitbbbe8128e7ae9d816a221377dbf5ff3969bb203b (patch)
tree0283a521760b879b30e61872f14f235645745675 /graphics/asymptote/base
parent14ce8b68fe7df49e8a8891bb94c63b9a846da232 (diff)
CTAN sync 201909300300
Diffstat (limited to 'graphics/asymptote/base')
-rw-r--r--graphics/asymptote/base/plain_prethree.asy2
-rw-r--r--graphics/asymptote/base/shaders/fragment.glsl464
-rw-r--r--graphics/asymptote/base/shaders/vertex.glsl98
-rw-r--r--graphics/asymptote/base/three.asy5
-rw-r--r--graphics/asymptote/base/three_surface.asy90
-rw-r--r--graphics/asymptote/base/webgl/WebGLfooter.html10
-rw-r--r--graphics/asymptote/base/webgl/WebGLheader.html180
-rw-r--r--graphics/asymptote/base/webgl/asygl-1.00.js39
8 files changed, 560 insertions, 328 deletions
diff --git a/graphics/asymptote/base/plain_prethree.asy b/graphics/asymptote/base/plain_prethree.asy
index bc40792cf0..968ae2943a 100644
--- a/graphics/asymptote/base/plain_prethree.asy
+++ b/graphics/asymptote/base/plain_prethree.asy
@@ -1,6 +1,6 @@
// Critical definitions for transform3 needed by projection and picture.
-pair viewportmargin=(0.1,0.1); // Horizontal and vertical 3D viewport margins.
+pair viewportmargin=settings.viewportmargin;
typedef real[][] transform3;
restricted transform3 identity4=identity(4);
diff --git a/graphics/asymptote/base/shaders/fragment.glsl b/graphics/asymptote/base/shaders/fragment.glsl
index 38a6a53fbb..b914f7f35a 100644
--- a/graphics/asymptote/base/shaders/fragment.glsl
+++ b/graphics/asymptote/base/shaders/fragment.glsl
@@ -1,237 +1,227 @@
-struct Material
-{
- vec4 diffuse,emissive,specular;
- vec4 parameters;
-};
-
-struct Light
-{
- vec4 direction;
- vec4 diffuse,specular;
-};
-
-uniform int nlights;
-uniform Light lights[Nlights];
-
-uniform MaterialBuffer {
- Material Materials[Nmaterials];
-};
-
-
-#ifdef NORMAL
-in vec3 Normal;
-vec3 normal;
-#endif
-
-#ifdef EXPLICIT_COLOR
-in vec4 Color;
-#endif
-
-flat in int materialIndex;
-out vec4 outColor;
-
-// PBR material parameters
-vec3 PBRBaseColor; // Diffuse for nonmetals, reflectance for metals.
-vec3 PBRSpecular; // Specular tint for nonmetals
-float PBRMetallic; // Metallic/Nonmetals switch flag
-float PBRF0; // Fresnel at zero for nonmetals
-float PBRRoughness; // Roughness.
-float PBRRoughnessSq; // used value of roughness, for a little bit more "smoothing"
-
-uniform sampler2D environmentMap;
-const float PI = acos(-1.0);
-#ifdef ENABLE_TEXTURE
-const float twopi=2*PI;
-const float halfpi=PI/2;
-
-const int numSamples=7;
-
-// (x,y,z) -> (r, theta, phi);
-// theta -> [0,\pi], "height" angle
-// phi -> [0, 2\pi], rotation agnle
-vec3 cart2spher(vec3 cart) {
- float x = cart.z;
- float y = cart.x;
- float z = cart.y;
-
- float r = length(cart);
- float phi = atan(y,x);
- float theta = acos(z/r);
-
- return vec3(r,phi,theta);
-}
-
-vec2 normalizedAngle(vec3 cartVec) {
- vec3 sphericalVec = cart2spher(cartVec);
- sphericalVec.y = sphericalVec.y / (2 * PI) - 0.25;
- sphericalVec.z = sphericalVec.z / PI;
- // sphericalVec.z = - sphericalVec.z;
- return sphericalVec.yz;
-}
-#endif
-
-#ifdef NORMAL
-// h is the halfway vector between normal and light direction
-// GGX Trowbridge-Reitz Approximation
-float NDF_TRG(vec3 h, float roughness) {
- float ndoth = max(dot(normal, h), 0);
- float alpha2 = PBRRoughnessSq * PBRRoughnessSq;
-
- float denom = pow(ndoth * ndoth * (alpha2-1) + 1, 2);
- return alpha2/denom;
-}
-
-float GGX_Geom(vec3 v) {
- float ndotv = max(dot(v,normal), 0);
- float ap = pow((1+PBRRoughness),2);
- float k = ap/8;
-
- return ndotv/((ndotv * (1-k)) + k);
-}
-
-float Geom(vec3 v, vec3 l) {
- return GGX_Geom(v) * GGX_Geom(l);
-}
-
-// Schlick's approximation
-float Fresnel(vec3 h, vec3 v, float F0) {
- float hdotv = max(dot(h,v), 0.0);
-
- return F0 + (1-F0)*pow((1-hdotv),5);
-}
-
-vec3 BRDF(vec3 viewDirection, vec3 lightDirection) {
- // Lambertian diffuse
- vec3 lambertian = PBRBaseColor;
- // Cook-Torrance model
- vec3 h = normalize(lightDirection + viewDirection);
-
- float omegain = max(dot(viewDirection, normal),0);
- float omegaln = max(dot(lightDirection, normal),0);
-
- float D = NDF_TRG(h, PBRRoughness);
- float G = Geom(viewDirection, lightDirection);
- float F = Fresnel(h, viewDirection, PBRF0);
-
- float denom=4*omegain*omegaln;
- float rawReflectance=denom > 0 ? (D*G)/denom : 0;
-
- vec3 dielectric = mix(lambertian, rawReflectance * PBRSpecular, F);
- vec3 metal = rawReflectance * PBRBaseColor;
-
- return mix(dielectric, metal, PBRMetallic);
-}
-#endif
-
-void main()
-{
-vec4 Diffuse;
-vec4 Emissive;
-vec4 Specular;
-vec4 parameters;
-
-#ifdef EXPLICIT_COLOR
- if(materialIndex < 0) {
- int index=-materialIndex-1;
- Material m=Materials[index];
- Diffuse=Color;
- Emissive=vec4(0);
- Specular=m.specular;
- parameters=m.parameters;
- } else {
- Material m=Materials[materialIndex];
- Diffuse=m.diffuse;
- Emissive=m.emissive;
- Specular=m.specular;
- parameters=m.parameters;
- }
-#else
- Material m=Materials[materialIndex];
- Diffuse=m.diffuse;
- Emissive=m.emissive;
- Specular=m.specular;
- parameters=m.parameters;
-#endif
-
- PBRRoughness=1-parameters[0];
- PBRMetallic=parameters[1];
- PBRF0=parameters[2];
-
- PBRBaseColor = Diffuse.rgb;
- PBRRoughnessSq = PBRRoughness * PBRRoughness;
- PBRSpecular = Specular.rgb;
-
- // Formally, the formula given a point x and direction \omega,
- // L_i = \int_{\Omega} f(x, \omega_i, \omega) L(x,\omega_i) (\hat{n}\cdot \omega_i) d \omega_i
- // where \Omega is the hemisphere covering a point, f is the BRDF function
- // L is the radiance from a given angle and position.
-
- vec3 color=Emissive.rgb;
-#ifdef NORMAL
- vec3 Z=vec3(0,0,1);
- vec3 pointLightRadiance=vec3(0,0,0);
-
- normal=normalize(Normal);
- normal=gl_FrontFacing ? normal : -normal;
- // as a finite point light, we have some simplification to the rendering equation.
- if(nlights > 0) {
- for(int i=0; i < nlights; ++i) {
- vec3 L = normalize(lights[i].direction.xyz);
- // what if we use the acutal view from (0,0,0) instead?
- // vec3 viewDirection = Z;
- vec3 viewDirection = -normalize(Z);
- float cosTheta = max(dot(normal, L), 0); // $\omega_i \cdot n$ term
- float attn = 1; // if we have a good light position.
- vec3 radiance = cosTheta * attn * lights[i].diffuse.rgb;
- pointLightRadiance += BRDF(Z, L) * radiance;
- }
- color += pointLightRadiance.rgb;
-
-#ifdef ENABLE_TEXTURE
-#ifndef EXPLICIT_COLOR
- // Experimental environment radiance using Riemann sums;
- // can also do importance sampling.
- vec3 envRadiance=vec3(0,0,0);
-
- vec3 normalPerp = vec3(-normal.y, normal.x, 0);
- if (length(normalPerp) == 0) { // x, y = 0.
-
- normalPerp = vec3(1, 0, 0);
- }
- // we now have a normal basis;
- normalPerp = normalize(normalPerp);
- vec3 normalPerp2 = normalize(cross(normal, normalPerp));
-
- const float step=1.0/numSamples;
- const float phistep=twopi*step;
- const float thetastep=halfpi*step;
- for (int iphi=0; iphi < numSamples; ++iphi) {
- float phi=iphi*phistep;
- for (int itheta=0; itheta < numSamples; ++itheta) {
- float theta=itheta*thetastep;
-
- vec3 azimuth=cos(phi)*normalPerp+sin(phi)*normalPerp2;
- vec3 L=sin(theta)*azimuth+cos(theta)*normal;
-
- vec3 rawRadiance=texture(environmentMap,normalizedAngle(L)).rgb;
- vec3 surfRefl=BRDF(Z,L);
- envRadiance += surfRefl*rawRadiance*sin(2.0*theta);
- }
- }
- envRadiance *= halfpi*step*step;
-
- // vec3 lightVector = normalize(reflect(-Z, normal));
- // vec2 anglemap = normalizedAngle(lightVector);
- // vec3 color = texture(environmentMap, anglemap).rgb;
- color += envRadiance.rgb;
-#endif
-#endif
- outColor=vec4(color,Diffuse.a);
- } else {
- outColor=Diffuse;
- }
-#else
- outColor=Emissive;
-#endif
-}
-
+struct Material
+{
+ vec4 diffuse,emissive,specular;
+ vec4 parameters;
+};
+
+struct Light
+{
+ vec3 direction;
+ vec3 color;
+};
+
+uniform int nlights;
+uniform Light lights[Nlights];
+
+uniform MaterialBuffer {
+ Material Materials[Nmaterials];
+};
+
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+in vec3 ViewPosition;
+#endif
+in vec3 Normal;
+vec3 normal;
+#endif
+
+#ifdef COLOR
+in vec4 Color;
+#endif
+
+flat in int materialIndex;
+out vec4 outColor;
+
+// PBR material parameters
+vec3 Diffuse; // Diffuse for nonmetals, reflectance for metals.
+vec3 Specular; // Specular tint for nonmetals
+float Metallic; // Metallic/Nonmetals parameter
+float Fresnel0; // Fresnel at zero for nonmetals
+float Roughness2; // roughness squared, for smoothing
+
+#ifdef ENABLE_TEXTURE
+uniform sampler2D environmentMap;
+const float PI=acos(-1.0);
+const float twopi=2*PI;
+const float halfpi=PI/2;
+
+const int numSamples=7;
+
+// (x,y,z) -> (r,theta,phi);
+// theta -> [0,\pi]: colatitude
+// phi -> [0, 2\pi]: longitude
+vec3 cart2sphere(vec3 cart)
+{
+ float x=cart.z;
+ float y=cart.x;
+ float z=cart.y;
+
+ float r=length(cart);
+ float phi=atan(y,x);
+ float theta=acos(z/r);
+
+ return vec3(r,phi,theta);
+}
+
+vec2 normalizedAngle(vec3 cartVec)
+{
+ vec3 sphericalVec=cart2sphere(cartVec);
+ sphericalVec.y=sphericalVec.y/(2*PI)-0.25;
+ sphericalVec.z=sphericalVec.z/PI;
+ return sphericalVec.yz;
+}
+#endif
+
+#ifdef NORMAL
+// h is the halfway vector between normal and light direction
+// GGX Trowbridge-Reitz Approximation
+float NDF_TRG(vec3 h)
+{
+ float ndoth=max(dot(normal,h),0.0);
+ float alpha2=Roughness2*Roughness2;
+ float denom=ndoth*ndoth*(alpha2-1.0)+1.0;
+ return denom != 0.0 ? alpha2/(denom*denom) : 0.0;
+}
+
+float GGX_Geom(vec3 v)
+{
+ float ndotv=max(dot(v,normal),0.0);
+ float ap=1.0+Roughness2;
+ float k=0.125*ap*ap;
+ return ndotv/((ndotv*(1.0-k))+k);
+}
+
+float Geom(vec3 v, vec3 l)
+{
+ return GGX_Geom(v)*GGX_Geom(l);
+}
+
+// Schlick's approximation
+float Fresnel(vec3 h, vec3 v, float fresnel0)
+{
+ float a=1.0-max(dot(h,v),0.0);
+ float b=a*a;
+ return fresnel0+(1.0-fresnel0)*b*b*a;
+}
+
+vec3 BRDF(vec3 viewDirection, vec3 lightDirection)
+{
+ vec3 lambertian=Diffuse;
+ // Cook-Torrance model
+ vec3 h=normalize(lightDirection+viewDirection);
+
+ float omegain=max(dot(viewDirection,normal),0.0);
+ float omegaln=max(dot(lightDirection,normal),0.0);
+
+ float D=NDF_TRG(h);
+ float G=Geom(viewDirection,lightDirection);
+ float F=Fresnel(h,viewDirection,Fresnel0);
+
+ float denom=4.0*omegain*omegaln;
+ float rawReflectance=denom > 0.0 ? (D*G)/denom : 0.0;
+
+ vec3 dielectric=mix(lambertian,rawReflectance*Specular,F);
+ vec3 metal=rawReflectance*Diffuse;
+
+ return mix(dielectric,metal,Metallic);
+}
+#endif
+
+void main()
+{
+ vec4 diffuse;
+ vec4 emissive;
+ vec4 parameters;
+
+ Material m;
+#ifdef TRANSPARENT
+ m=Materials[abs(materialIndex)-1];
+ if(materialIndex >= 0) {
+ diffuse=m.diffuse;
+ emissive=m.emissive;
+ } else {
+ diffuse=Color;
+ emissive=vec4(0.0);
+ }
+#else
+ m=Materials[int(materialIndex)];
+#ifdef COLOR
+ diffuse=Color;
+ emissive=vec4(0.0);
+#else
+ diffuse=m.diffuse;
+ emissive=m.emissive;
+#endif
+#endif
+ Specular=m.specular.rgb;
+ parameters=m.parameters;
+ Roughness2=1.0-parameters[0];
+ Roughness2=Roughness2*Roughness2;
+ Metallic=parameters[1];
+ Fresnel0=parameters[2];
+ Diffuse=diffuse.rgb;
+
+ // Given a point x and direction \omega,
+ // L_i=\int_{\Omega}f(x,\omega_i,\omega) L(x,\omega_i)(\hat{n}\cdot \omega_i)
+ // d\omega_i, where \Omega is the hemisphere covering a point,
+ // f is the BRDF function, L is the radiance from a given angle and position.
+
+ vec3 color=emissive.rgb;
+#ifdef NORMAL
+ normal=normalize(Normal);
+ normal=gl_FrontFacing ? normal : -normal;
+#ifdef ORTHOGRAPHIC
+ vec3 viewDir=vec3(0.0,0.0,1.0);
+#else
+ vec3 viewDir=-normalize(ViewPosition);
+#endif
+ // For a finite point light, the rendering equation simplifies.
+ if(nlights > 0) {
+ for(int i=0; i < nlights; ++i) {
+ Light Li=lights[i];
+ vec3 L=Li.direction;
+ float cosTheta=max(dot(normal,L),0.0); // $\omega_i \cdot n$ term
+ vec3 radiance=cosTheta*Li.color;
+ color += BRDF(viewDir,L)*radiance;
+ }
+
+#ifdef ENABLE_TEXTURE
+#ifndef COLOR
+ // Experimental environment radiance using Riemann sums;
+ // can also do importance sampling.
+ vec3 envRadiance=vec3(0.0,0.0,0.0);
+
+ vec3 normalPerp=vec3(-normal.y,normal.x,0.0);
+ if(length(normalPerp) == 0.0)
+ normalPerp=vec3(1.0,0.0,0.0);
+
+ // we now have a normal basis;
+ normalPerp=normalize(normalPerp);
+ vec3 normalPerp2=normalize(cross(normal,normalPerp));
+
+ const float step=1.0/numSamples;
+ const float phistep=twopi*step;
+ const float thetastep=halfpi*step;
+ for (int iphi=0; iphi < numSamples; ++iphi) {
+ float phi=iphi*phistep;
+ for (int itheta=0; itheta < numSamples; ++itheta) {
+ float theta=itheta*thetastep;
+
+ vec3 azimuth=cos(phi)*normalPerp+sin(phi)*normalPerp2;
+ vec3 L=sin(theta)*azimuth+cos(theta)*normal;
+
+ vec3 rawRadiance=texture(environmentMap,normalizedAngle(L)).rgb;
+ vec3 surfRefl=BRDF(Z,L);
+ envRadiance += surfRefl*rawRadiance*sin(2.0*theta);
+ }
+ }
+ envRadiance *= halfpi*step*step;
+ color += envRadiance.rgb;
+#endif
+#endif
+ outColor=vec4(color,diffuse.a);
+ } else outColor=diffuse;
+#else
+ outColor=emissive;
+#endif
+}
diff --git a/graphics/asymptote/base/shaders/vertex.glsl b/graphics/asymptote/base/shaders/vertex.glsl
index 815145461e..9a54d3211c 100644
--- a/graphics/asymptote/base/shaders/vertex.glsl
+++ b/graphics/asymptote/base/shaders/vertex.glsl
@@ -1,49 +1,49 @@
-in vec3 position;
-
-#ifdef NORMAL
-in vec3 normal;
-#endif
-
-#ifdef EXPLICIT_COLOR
-in uint color;
-#endif
-
-#ifdef WIDTH
-in float width;
-#endif
-
-in int material;
-
-uniform mat4 projViewMat;
-uniform mat4 viewMat;
-uniform mat4 normMat;
-
-out vec3 ViewPosition;
-#ifdef NORMAL
-out vec3 Normal;
-#endif
-
-#ifdef EXPLICIT_COLOR
-out vec4 Color;
-#endif
-
-flat out int materialIndex;
-
-void main()
-{
- gl_Position=projViewMat*vec4(position,1.0);
- ViewPosition=(viewMat*vec4(position,1.0)).xyz;
-#ifdef NORMAL
- Normal=(normMat*vec4(normal,0)).xyz;
-#endif
-
-#ifdef EXPLICIT_COLOR
- Color=unpackUnorm4x8(color);
-#endif
-
-#ifdef WIDTH
- gl_PointSize=width;
-#endif
-
- materialIndex=material;
-}
+in vec3 position;
+
+uniform mat3 normMat;
+
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+out vec3 ViewPosition;
+#endif
+in vec3 normal;
+out vec3 Normal;
+#endif
+
+in int material;
+
+#ifdef COLOR
+in vec4 color;
+out vec4 Color;
+#endif
+
+#ifdef WIDTH
+in float width;
+#endif
+
+uniform mat4 projViewMat;
+uniform mat4 viewMat;
+
+flat out int materialIndex;
+
+void main()
+{
+ vec4 v=vec4(position,1.0);
+ gl_Position=projViewMat*v;
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+ ViewPosition=(viewMat*v).xyz;
+#endif
+ Normal=normal*normMat;
+#endif
+
+#ifdef COLOR
+ Color=color;
+#endif
+
+#ifdef WIDTH
+ gl_PointSize=width;
+#endif
+
+ materialIndex=material;
+}
diff --git a/graphics/asymptote/base/three.asy b/graphics/asymptote/base/three.asy
index 866137b1db..2b32fd1612 100644
--- a/graphics/asymptote/base/three.asy
+++ b/graphics/asymptote/base/three.asy
@@ -2887,10 +2887,13 @@ object embed(string prefix=outprefix(), string label=prefix,
m -= margin;
} else if(M.z >= 0) abort("camera too close");
+ if(settings.outformat == "html")
+ format="html";
+
shipout3(prefix,f,preview ? nativeformat() : format,
S.width-defaultrender.margin,S.height-defaultrender.margin,
P.infinity ? 0 : 2aTan(Tan(0.5*P.angle)*P.zoom),
- P.zoom,m,M,P.viewportshift,
+ P.zoom,m,M,P.viewportshift,S.viewportmargin,
tinv*inv*shift(0,0,zcenter),Light.background(),Light.position,
Light.diffuse,Light.specular,
view && !preview);
diff --git a/graphics/asymptote/base/three_surface.asy b/graphics/asymptote/base/three_surface.asy
index bdb56cb0ca..4ad31d6d2d 100644
--- a/graphics/asymptote/base/three_surface.asy
+++ b/graphics/asymptote/base/three_surface.asy
@@ -1402,15 +1402,20 @@ void draw3D(frame f, int type=0, patch s, triple center=O, material m,
light light=currentlight, interaction interaction=Embedded,
bool prc=true)
{
- if(s.colors.length > 0)
+ bool straight=s.straight && s.planar;
+ bool prc=prc();
+ if(s.colors.length > 0) {
+ if(prc && light.on())
+ straight=false; // PRC vertex colors (for quads only) ignore lighting
m=mean(s.colors);
+ }
m=material(m,light);
- real PRCshininess;
- if(prc())
- PRCshininess=PRCshininess(m.shininess);
+ real PRCshininess;
+ if(prc) PRCshininess=PRCshininess(m.shininess);
+
(s.triangular ? drawbeziertriangle : draw)
- (f,s.P,center,s.straight && s.planar,m.p,m.opacity,m.shininess,
+ (f,s.P,center,straight,m.p,m.opacity,m.shininess,
m.metallic,m.fresnel0,PRCshininess,s.colors,interaction.type,prc);
}
@@ -1435,7 +1440,7 @@ void draw(frame f, triple[] v, int[][] vi,
triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={},
int[][] pi={}, light light=currentlight)
{
- bool normals=ni.length > 0;
+ bool normals=n.length > 0;
if(!normals) {
ni=new int[vi.length][3];
normals=computeNormals(v,vi,n,ni) > 0;
@@ -1455,7 +1460,8 @@ void draw(picture pic=currentpicture, triple[] v, int[][] vi,
triple[] n={}, int[][] ni={}, material m=currentpen, pen[] p={},
int[][] pi={}, light light=currentlight)
{
- bool normals=ni.length > 0;
+ bool prc=prc();
+ bool normals=n.length > 0;
if(!normals) {
ni=new int[vi.length][3];
normals=computeNormals(v,vi,n,ni) > 0;
@@ -1501,7 +1507,7 @@ void draw(picture pic=currentpicture, triple[] v, int[][] vi,
project(v[vii[2]],P)--cycle;
pen p=color(n[ni[i][0]],m,light);
fill(pic,g,p);
- if(opacity(m.diffuse()) == 1) // Fill subdivision cracks
+ if(prc && opacity(m.diffuse()) == 1) // Fill subdivision cracks
draw(pic,g,p);
}
}
@@ -1843,6 +1849,7 @@ void label(frame f, Label L, triple position, align align=NoAlign,
interaction interaction=LabelInteraction(),
projection P=currentprojection)
{
+ bool prc=prc();
Label L=L.copy();
L.align(align);
L.p(p);
@@ -1873,7 +1880,7 @@ void label(frame f, Label L, triple position, align align=NoAlign,
S=centering*S;
draw3D(f3,S,position,L.p,light,interaction);
// Fill subdivision cracks
- if(render.labelfill && opacity(L.p) == 1 && !lighton)
+ if(prc && render.labelfill && opacity(L.p) == 1 && !lighton)
_draw(f3,S.external(),position,L.p,interaction.type);
}
endgroup3(f3);
@@ -1893,7 +1900,7 @@ void label(frame f, Label L, triple position, align align=NoAlign,
position;
draw3D(f,S,V,L.p,light,interaction);
// Fill subdivision cracks
- if(render.labelfill && opacity(L.p) == 1 && !lighton)
+ if(prc && render.labelfill && opacity(L.p) == 1 && !lighton)
_draw(f,S.external(),V,L.p,interaction.type);
}
endgroup3(f);
@@ -1927,6 +1934,7 @@ void label(picture pic=currentpicture, Label L, triple position,
pic.add(new void(frame f, transform3 t, picture pic2, projection P) {
// Handle relative projected 3D alignments.
+ bool prc=prc();
Label L=L.copy();
triple v=t*position;
if(!align.is3D && L.align.relative && L.align.dir3 != O &&
@@ -1942,44 +1950,46 @@ void label(picture pic=currentpicture, Label L, triple position,
if(is3D()) {
bool lighton=light.on();
if(name == "") name=L.s;
- if(prc() && interaction.type == Billboard.type) {
+ if(prc && interaction.type == Billboard.type) {
surface s=surface(texpath(L,bbox=P.bboxonly));
- transform3 centering=L.align.is3D ?
- alignshift(s,L.T3,v,L.align.dir3) : identity4;
- transform3 positioning=
- shift(L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v);
- frame f1,f2,f3;
- begingroup3(f1,name,render);
- if(L.defaulttransform3)
- begingroup3(f3,render,v,interaction.type);
- else {
- begingroup3(f2,render,v,interaction.type);
- begingroup3(f3,render,v);
- }
- for(patch S : s.s) {
- S=centering*S;
- draw3D(f3,S,v,L.p,light,interaction);
- // Fill subdivision cracks
- if(render.labelfill && opacity(L.p) == 1 && !lighton)
- _draw(f3,S.external(),v,L.p,interaction.type);
- }
- endgroup3(f3);
- if(L.defaulttransform3)
- add(f1,T*f3);
- else {
- add(f2,inverse(T)*L.T3*f3);
- endgroup3(f2);
- add(f1,T*f2);
+ if(s.s.length > 0) {
+ transform3 centering=L.align.is3D ?
+ alignshift(s,L.T3,v,L.align.dir3) : identity4;
+ transform3 positioning=
+ shift(L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v);
+ frame f1,f2,f3;
+ begingroup3(f1,name,render);
+ if(L.defaulttransform3)
+ begingroup3(f3,render,v,interaction.type);
+ else {
+ begingroup3(f2,render,v,interaction.type);
+ begingroup3(f3,render,v);
+ }
+ for(patch S : s.s) {
+ S=centering*S;
+ draw3D(f3,S,v,L.p,light,interaction);
+ // Fill subdivision cracks
+ if(prc && render.labelfill && opacity(L.p) == 1 && !lighton)
+ _draw(f3,S.external(),v,L.p,interaction.type);
+ }
+ endgroup3(f3);
+ if(L.defaulttransform3)
+ add(f1,T*f3);
+ else {
+ add(f2,inverse(T)*L.T3*f3);
+ endgroup3(f2);
+ add(f1,T*f2);
+ }
+ endgroup3(f1);
+ add(f,positioning*f1);
}
- endgroup3(f1);
- add(f,positioning*f1);
} else {
begingroup3(f,name,render);
for(patch S : surface(L,v,bbox=P.bboxonly).s) {
triple V=L.align.is3D ? v+L.align.dir3*labelmargin(L.p) : v;
draw3D(f,S,V,L.p,light,interaction);
// Fill subdivision cracks
- if(render.labelfill && opacity(L.p) == 1 && !lighton)
+ if(prc && render.labelfill && opacity(L.p) == 1 && !lighton)
_draw(f,S.external(),V,L.p,interaction.type);
}
endgroup3(f);
diff --git a/graphics/asymptote/base/webgl/WebGLfooter.html b/graphics/asymptote/base/webgl/WebGLfooter.html
new file mode 100644
index 0000000000..4aba96372e
--- /dev/null
+++ b/graphics/asymptote/base/webgl/WebGLfooter.html
@@ -0,0 +1,10 @@
+</script>
+
+</head>
+
+
+<body style="overflow: hidden;" onload="webGLStart();">
+<canvas id="Asymptote" style="border: none;" width="0" height="0" />
+</body>
+
+</html>
diff --git a/graphics/asymptote/base/webgl/WebGLheader.html b/graphics/asymptote/base/webgl/WebGLheader.html
new file mode 100644
index 0000000000..6eea5fd1be
--- /dev/null
+++ b/graphics/asymptote/base/webgl/WebGLheader.html
@@ -0,0 +1,180 @@
+<html>
+
+<head>
+<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+<meta name="viewport" content="user-scalable=no"/>
+
+<script id="vertex" type="x-shader/x-vertex">
+attribute vec3 position;
+#ifdef WIDTH
+attribute float width;
+#endif
+#ifdef NORMAL
+attribute vec3 normal;
+#endif
+attribute float materialIndex;
+#ifdef COLOR
+attribute vec4 color;
+#endif
+
+uniform mat3 normMat;
+uniform mat4 viewMat;
+uniform mat4 projViewMat;
+
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+varying vec3 ViewPosition;
+#endif
+varying vec3 Normal;
+#endif
+varying vec4 diffuse;
+varying vec3 specular;
+varying float roughness,metallic,fresnel0;
+varying vec4 emissive;
+
+struct Material {
+ vec4 diffuse,emissive,specular;
+ float shininess,metallic,fresnel0;
+};
+
+uniform Material Materials[nMaterials];
+
+void main(void)
+{
+ vec4 v=vec4(position,1.0);
+ gl_Position=projViewMat*v;
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+ ViewPosition=(viewMat*v).xyz;
+#endif
+ Normal=normal*normMat;
+
+ Material m;
+#ifdef TRANSPARENT
+ m=Materials[int(abs(materialIndex))-1];
+ if(materialIndex >= 0.0) {
+ diffuse=m.diffuse;
+ emissive=m.emissive;
+ } else {
+ diffuse=color;
+ emissive=vec4(0.0);
+ }
+#else
+ m=Materials[int(materialIndex)];
+#ifdef COLOR
+ diffuse=color;
+ emissive=vec4(0.0);
+#else
+ diffuse=m.diffuse;
+ emissive=m.emissive;
+#endif
+#endif
+ specular=m.specular.rgb;
+ roughness=1.0-m.shininess;
+ metallic=m.metallic;
+ fresnel0=m.fresnel0;
+#else
+ emissive=Materials[int(materialIndex)].emissive;
+#endif
+#ifdef WIDTH
+ gl_PointSize=width;
+#endif
+}
+</script>
+
+<script id="fragment" type="x-shader/x-fragment">
+#ifdef NORMAL
+#ifndef ORTHOGRAPHIC
+varying vec3 ViewPosition;
+#endif
+varying vec3 Normal;
+varying vec4 diffuse;
+varying vec3 specular;
+varying float roughness,metallic,fresnel0;
+
+float Roughness2;
+vec3 normal;
+
+struct Light {
+ vec3 direction;
+ vec3 color;
+};
+uniform Light Lights[nLights];
+
+float NDF_TRG(vec3 h)
+{
+ float ndoth=max(dot(normal,h),0.0);
+ float alpha2=Roughness2*Roughness2;
+ float denom=ndoth*ndoth*(alpha2-1.0)+1.0;
+ return denom != 0.0 ? alpha2/(denom*denom) : 0.0;
+}
+
+float GGX_Geom(vec3 v)
+{
+ float ndotv=max(dot(v,normal),0.0);
+ float ap=1.0+Roughness2;
+ float k=0.125*ap*ap;
+ return ndotv/((ndotv*(1.0-k))+k);
+}
+
+float Geom(vec3 v, vec3 l)
+{
+ return GGX_Geom(v)*GGX_Geom(l);
+}
+
+float Fresnel(vec3 h, vec3 v, float fresnel0)
+{
+ float a=1.0-max(dot(h,v),0.0);
+ float b=a*a;
+ return fresnel0+(1.0-fresnel0)*b*b*a;
+}
+
+// physical based shading using UE4 model.
+vec3 BRDF(vec3 viewDirection, vec3 lightDirection)
+{
+ vec3 lambertian=diffuse.rgb;
+ vec3 h=normalize(lightDirection+viewDirection);
+
+ float omegain=max(dot(viewDirection,normal),0.0);
+ float omegali=max(dot(lightDirection,normal),0.0);
+
+ float D=NDF_TRG(h);
+ float G=Geom(viewDirection,lightDirection);
+ float F=Fresnel(h,viewDirection,fresnel0);
+
+ float denom=4.0*omegain*omegali;
+ float rawReflectance=denom > 0.0 ? (D*G)/denom : 0.0;
+
+ vec3 dielectric=mix(lambertian,rawReflectance*specular,F);
+ vec3 metal=rawReflectance*diffuse.rgb;
+
+ return mix(dielectric,metal,metallic);
+}
+#endif
+varying vec4 emissive;
+
+void main(void)
+{
+#ifdef NORMAL
+ normal=normalize(Normal);
+ normal=gl_FrontFacing ? normal : -normal;
+#ifdef ORTHOGRAPHIC
+ vec3 viewDir=vec3(0.0,0.0,1.0);
+#else
+ vec3 viewDir=-normalize(ViewPosition);
+#endif
+ Roughness2=roughness*roughness;
+ vec3 color=emissive.rgb;
+ for (int i=0; i < nLights; ++i) {
+ Light Li=Lights[i];
+ vec3 L=Li.direction;
+ float cosTheta=max(dot(normal,L),0.0);
+ vec3 radiance=cosTheta*Li.color;
+ color += BRDF(viewDir,L)*radiance;
+ }
+ gl_FragColor=vec4(color,diffuse.a);
+#else
+ gl_FragColor=emissive;
+#endif
+}
+</script>
diff --git a/graphics/asymptote/base/webgl/asygl-1.00.js b/graphics/asymptote/base/webgl/asygl-1.00.js
new file mode 100644
index 0000000000..cf29f41c21
--- /dev/null
+++ b/graphics/asymptote/base/webgl/asygl-1.00.js
@@ -0,0 +1,39 @@
+/*@license
+ gl.js: Render Bezier patches 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/>.
+*/
+let gl,canvas,canvasWidth,canvasHeight,halfCanvasWidth,halfCanvasHeight;!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 r in i)("object"==typeof exports?exports:t)[r]=i[r]}}("undefined"!=typeof self?self:this,function(){return function(t){function e(r){if(i[r])return i[r].exports;var a=i[r]={i:r,l:!1,exports:{}};return t[r].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var i={};return e.m=t,e.c=i,e.d=function(t,i,r){e.o(t,i)||Object.defineProperty(t,i,{configurable:!1,enumerable:!0,get:r})},e.n=function(t){var i=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(i,"a",i),i},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.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*a},e.equals=function(t,e){return Math.abs(t-e)<=r*Math.max(1,Math.abs(t),Math.abs(e))};var r=e.EPSILON=1e-6,a=(e.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,e.RANDOM=Math.random,Math.PI/180)},function(t,e,i){"use strict";function r(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}Object.defineProperty(e,"__esModule",{value:!0}),e.mat4=e.mat3=void 0;var a=r(i(2)),s=r(i(3));e.mat3=a,e.mat4=s},function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.create=function(){var t=new r.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],r=e[1],a=e[2],s=e[3],n=e[4],o=e[5],h=e[6],l=e[7],c=e[8],d=c*n-o*l,m=-c*s+o*h,u=l*s-n*h,f=i*d+r*m+a*u;return f?(f=1/f,t[0]=d*f,t[1]=(-c*r+a*l)*f,t[2]=(o*r-a*n)*f,t[3]=m*f,t[4]=(c*i-a*h)*f,t[5]=(-o*i+a*s)*f,t[6]=u*f,t[7]=(-l*i+r*h)*f,t[8]=(n*i-r*s)*f,t):null};var r=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 r.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],r=e[1],a=e[2],s=e[3],n=e[4],o=e[5],h=e[6],l=e[7],c=e[8],d=e[9],m=e[10],u=e[11],f=e[12],v=e[13],p=e[14],g=e[15],x=i*o-r*n,M=i*h-a*n,w=i*l-s*n,A=r*h-a*o,S=r*l-s*o,b=a*l-s*h,P=c*v-d*f,z=c*p-m*f,R=c*g-u*f,y=d*p-m*v,D=d*g-u*v,T=m*g-u*p,E=x*T-M*D+w*y+A*R-S*z+b*P;return E?(E=1/E,t[0]=(o*T-h*D+l*y)*E,t[1]=(a*D-r*T-s*y)*E,t[2]=(v*b-p*S+g*A)*E,t[3]=(m*S-d*b-u*A)*E,t[4]=(h*R-n*T-l*z)*E,t[5]=(i*T-a*R+s*z)*E,t[6]=(p*w-f*b-g*M)*E,t[7]=(c*b-m*w+u*M)*E,t[8]=(n*D-o*R+l*P)*E,t[9]=(r*R-i*D-s*P)*E,t[10]=(f*S-v*w+g*x)*E,t[11]=(d*w-c*S-u*x)*E,t[12]=(o*z-n*y-h*P)*E,t[13]=(i*y-r*z+a*P)*E,t[14]=(v*M-f*A-p*x)*E,t[15]=(c*A-d*M+m*x)*E,t):null},e.multiply=function(t,e,i){var r=e[0],a=e[1],s=e[2],n=e[3],o=e[4],h=e[5],l=e[6],c=e[7],d=e[8],m=e[9],u=e[10],f=e[11],v=e[12],p=e[13],g=e[14],x=e[15],M=i[0],w=i[1],A=i[2],S=i[3];return t[0]=M*r+w*o+A*d+S*v,t[1]=M*a+w*h+A*m+S*p,t[2]=M*s+w*l+A*u+S*g,t[3]=M*n+w*c+A*f+S*x,M=i[4],w=i[5],A=i[6],S=i[7],t[4]=M*r+w*o+A*d+S*v,t[5]=M*a+w*h+A*m+S*p,t[6]=M*s+w*l+A*u+S*g,t[7]=M*n+w*c+A*f+S*x,M=i[8],w=i[9],A=i[10],S=i[11],t[8]=M*r+w*o+A*d+S*v,t[9]=M*a+w*h+A*m+S*p,t[10]=M*s+w*l+A*u+S*g,t[11]=M*n+w*c+A*f+S*x,M=i[12],w=i[13],A=i[14],S=i[15],t[12]=M*r+w*o+A*d+S*v,t[13]=M*a+w*h+A*m+S*p,t[14]=M*s+w*l+A*u+S*g,t[15]=M*n+w*c+A*f+S*x,t},e.translate=function(t,e,i){var r=i[0],a=i[1],s=i[2],n=void 0,o=void 0,h=void 0,l=void 0,c=void 0,d=void 0,m=void 0,u=void 0,f=void 0,v=void 0,p=void 0,g=void 0;return e===t?(t[12]=e[0]*r+e[4]*a+e[8]*s+e[12],t[13]=e[1]*r+e[5]*a+e[9]*s+e[13],t[14]=e[2]*r+e[6]*a+e[10]*s+e[14],t[15]=e[3]*r+e[7]*a+e[11]*s+e[15]):(n=e[0],o=e[1],h=e[2],l=e[3],c=e[4],d=e[5],m=e[6],u=e[7],f=e[8],v=e[9],p=e[10],g=e[11],t[0]=n,t[1]=o,t[2]=h,t[3]=l,t[4]=c,t[5]=d,t[6]=m,t[7]=u,t[8]=f,t[9]=v,t[10]=p,t[11]=g,t[12]=n*r+c*a+f*s+e[12],t[13]=o*r+d*a+v*s+e[13],t[14]=h*r+m*a+p*s+e[14],t[15]=l*r+u*a+g*s+e[15]),t},e.rotate=function(t,e,i,a){var s=a[0],n=a[1],o=a[2],h=Math.sqrt(s*s+n*n+o*o),l=void 0,c=void 0,d=void 0,m=void 0,u=void 0,f=void 0,v=void 0,p=void 0,g=void 0,x=void 0,M=void 0,w=void 0,A=void 0,S=void 0,b=void 0,P=void 0,z=void 0,R=void 0,y=void 0,D=void 0,T=void 0,E=void 0,I=void 0,O=void 0;return Math.abs(h)<r.EPSILON?null:(s*=h=1/h,n*=h,o*=h,l=Math.sin(i),d=1-(c=Math.cos(i)),m=e[0],u=e[1],f=e[2],v=e[3],p=e[4],g=e[5],x=e[6],M=e[7],w=e[8],A=e[9],S=e[10],b=e[11],P=s*s*d+c,z=n*s*d+o*l,R=o*s*d-n*l,y=s*n*d-o*l,D=n*n*d+c,T=o*n*d+s*l,E=s*o*d+n*l,I=n*o*d-s*l,O=o*o*d+c,t[0]=m*P+p*z+w*R,t[1]=u*P+g*z+A*R,t[2]=f*P+x*z+S*R,t[3]=v*P+M*z+b*R,t[4]=m*y+p*D+w*T,t[5]=u*y+g*D+A*T,t[6]=f*y+x*D+S*T,t[7]=v*y+M*D+b*T,t[8]=m*E+p*I+w*O,t[9]=u*E+g*I+A*O,t[10]=f*E+x*I+S*O,t[11]=v*E+M*I+b*O,e!==t&&(t[12]=e[12],t[13]=e[13],t[14]=e[14],t[15]=e[15]),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 a=i[0],s=i[1],n=i[2],o=Math.sqrt(a*a+s*s+n*n),h=void 0,l=void 0,c=void 0;return Math.abs(o)<r.EPSILON?null:(a*=o=1/o,s*=o,n*=o,h=Math.sin(e),c=1-(l=Math.cos(e)),t[0]=a*a*c+l,t[1]=s*a*c+n*h,t[2]=n*a*c-s*h,t[3]=0,t[4]=a*s*c-n*h,t[5]=s*s*c+l,t[6]=n*s*c+a*h,t[7]=0,t[8]=a*n*c+s*h,t[9]=s*n*c-a*h,t[10]=n*n*c+l,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t)},e.frustum=function(t,e,i,r,a,s,n){var o=1/(i-e),h=1/(a-r),l=1/(s-n);return t[0]=2*s*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*s*h,t[6]=0,t[7]=0,t[8]=(i+e)*o,t[9]=(a+r)*h,t[10]=(n+s)*l,t[11]=-1,t[12]=0,t[13]=0,t[14]=n*s*2*l,t[15]=0,t},e.ortho=function(t,e,i,r,a,s,n){var o=1/(e-i),h=1/(r-a),l=1/(s-n);return t[0]=-2*o,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*h,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*l,t[11]=0,t[12]=(e+i)*o,t[13]=(a+r)*h,t[14]=(n+s)*l,t[15]=1,t};var r=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))}])});let Zoom,Zoom0,pixel=.75,BezierFactor=.4,FillFactor=.1,maxViewportWidth=window.innerWidth,maxViewportHeight=window.innerHeight,viewportmargin=0;const windowTrim=10;let zoomFactor,zoomPinchFactor,zoomPinchCap,zoomStep,shiftHoldDistance,shiftWaitTime,vibrateTime,lastzoom,H,zmin,zmax,size2,ArcballFactor,b,B,positionBuffer,materialBuffer,colorBuffer,indexBuffer,resizeStep=1.2,Fuzz2=1e3*Number.EPSILON,Fuzz4=Fuzz2*Fuzz2,third=1/3,P=[],Materials=[],Lights=[],Centers=[],absolute=!1,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,r,a,s){this.diffuse=t,this.emissive=e,this.specular=i,this.shininess=r,this.metallic=a,this.fresnel0=s}setUniform(t,e,i=null){let r;r=null===i?i=>gl.getUniformLocation(t,e+"."+i):r=>gl.getUniformLocation(t,e+"["+i+"]."+r),gl.uniform4fv(r("diffuse"),new Float32Array(this.diffuse)),gl.uniform4fv(r("emissive"),new Float32Array(this.emissive)),gl.uniform4fv(r("specular"),new Float32Array(this.specular)),gl.uniform1f(r("shininess"),this.shininess),gl.uniform1f(r("metallic"),this.metallic),gl.uniform1f(r("fresnel0"),this.fresnel0)}}let enumPointLight=1,enumDirectionalLight=2;class Light{constructor(t,e){this.direction=t,this.color=e}setUniform(t,e,i){let r=r=>gl.getUniformLocation(t,e+"["+i+"]."+r);gl.uniform3fv(r("direction"),new Float32Array(this.direction)),gl.uniform3fv(r("color"),new Float32Array(this.color))}}function initGL(){try{gl=canvas.getContext("webgl",{alpha:!1})}catch(t){}gl||alert("Could not initialize WebGL")}function getShader(t,e,i=[]){let r=document.getElementById(e);if(!r)return null;let a=`#version 100\n#ifdef GL_FRAGMENT_PRECISION_HIGH\n precision highp float;\n#else\n precision mediump float;\n#endif\n const int nLights=${Lights.length};\n const int nMaterials=${Materials.length};\n`;orthographic&&(a+="#define ORTHOGRAPHIC\n"),i.forEach(t=>a+="#define "+t+"\n");let s,n=r.firstChild;for(;n;)3==n.nodeType&&(a+=n.textContent),n=n.nextSibling;if("x-shader/x-fragment"==r.type)s=t.createShader(t.FRAGMENT_SHADER);else{if("x-shader/x-vertex"!=r.type)return null;s=t.createShader(t.VERTEX_SHADER)}return t.shaderSource(s,a),t.compileShader(s),t.getShaderParameter(s,t.COMPILE_STATUS)?s:(alert(t.getShaderInfoLog(s)),null)}function drawBuffer(t,e,i=t.indices){if(0==t.indices.length)return;let r=e==pixelShader,a=!r&&e!=noNormalShader;setUniforms(e),gl.bindBuffer(gl.ARRAY_BUFFER,positionBuffer),gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(t.vertices),gl.STATIC_DRAW),gl.vertexAttribPointer(e.vertexPositionAttribute,3,gl.FLOAT,!1,a?24:r?16:12,0),a?gl.vertexAttribPointer(e.vertexNormalAttribute,3,gl.FLOAT,!1,24,12):r&&gl.vertexAttribPointer(e.vertexWidthAttribute,1,gl.FLOAT,!1,16,12),gl.bindBuffer(gl.ARRAY_BUFFER,materialBuffer),gl.bufferData(gl.ARRAY_BUFFER,new Int16Array(t.materials),gl.STATIC_DRAW),gl.vertexAttribPointer(e.vertexMaterialAttribute,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(e.vertexColorAttribute,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(a?gl.TRIANGLES:r?gl.POINTS:gl.LINES,i.length,indexExt?gl.UNSIGNED_INT:gl.UNSIGNED_SHORT,0)}class vertexBuffer{constructor(){this.clear()}clear(){this.vertices=[],this.materials=[],this.colors=[],this.indices=[],this.nvertices=0}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.materials.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.materials.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.materials.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.materials.push(materialIndex),this.nvertices++}iVertex(t,e,i,r=[0,0,0,0]){let a=6*t;this.vertices[a]=e[0],this.vertices[a+1]=e[1],this.vertices[a+2]=e[2],this.vertices[a+3]=i[0],this.vertices[a+4]=i[1],this.vertices[a+5]=i[2],this.materials[t]=materialIndex;let s=4*t;this.colors[s]=r[0],this.colors[s+1]=r[1],this.colors[s+2]=r[2],this.colors[s+3]=r[3],this.indices.push(t)}append(t){append(this.vertices,t.vertices),append(this.materials,t.materials),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,r=e.length;t.length+=r;for(let a=0;a<r;++a)t[i+a]=e[a]}function appendOffset(t,e,i){let r=t.length,a=e.length;t.length+=e.length;for(let s=0;s<a;++s)t[r+s]=e[s]+i}class Geometry{constructor(){this.data=new vertexBuffer,this.Onscreen=!1,this.m=[]}offscreen(t){let e=projViewMat,i=t[0],r=i[0],a=i[1],s=i[2],n=1/(e[3]*r+e[7]*a+e[11]*s+e[15]);this.x=this.X=(e[0]*r+e[4]*a+e[8]*s+e[12])*n,this.y=this.Y=(e[1]*r+e[5]*a+e[9]*s+e[13])*n;for(let i=1,r=t.length;i<r;++i){let r=t[i],a=r[0],s=r[1],n=r[2],o=1/(e[3]*a+e[7]*s+e[11]*n+e[15]),h=(e[0]*a+e[4]*s+e[8]*n+e[12])*o,l=(e[1]*a+e[5]*s+e[9]*n+e[13])*o;h<this.x?this.x=h:h>this.X&&(this.X=h),l<this.y?this.y=l:l>this.Y&&(this.Y=l)}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],r=this.c[2],a=t[0]-e,s=t[1]-i,n=t[2]-r;return[a*normMat[0]+s*normMat[3]+n*normMat[6]+e,a*normMat[1]+s*normMat[4]+n*normMat[7]+i,a*normMat[2]+s*normMat[5]+n*normMat[8]+r]}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)]}render(){let t;if(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 r=0;r<t;++r)e[r]=this.T(i[r])}materialIndex=this.MaterialIndex;let r=orthographic?1:this.Min[2]/B[2],a=pixel*Math.hypot(r*(viewParam.xmax-viewParam.xmin),r*(viewParam.ymax-viewParam.ymin))/size2;this.res2=a*a,this.Epsilon=FillFactor*a,this.data.clear(),this.Onscreen=!0,this.process(e)}}class BezierPatch extends Geometry{constructor(t,e,i,r,a,s){super(),this.controlpoints=t,this.Min=r,this.Max=a,this.color=s,this.CenterIndex=e;let n=t.length;if(s){let t=s[0][3]+s[1][3]+s[2][3];this.transparent=16==n||4==n?t+s[3][3]<1020:t<765}else this.transparent=Materials[i].diffuse[3]<1;this.transparent?(this.MaterialIndex=s?-1-i:1+i,this.vertex=this.data.Vertex.bind(this.data)):(this.MaterialIndex=i,this.vertex=this.data.vertex.bind(this.data)),this.L2norm(this.controlpoints)}L2norm(t){let e=t[0];this.epsilon=0;let i=t.length;for(let r=1;r<i;++r)this.epsilon=Math.max(this.epsilon,abs2([t[r][0]-e[0],t[r][1]-e[1],t[r][2]-e[2]]));this.epsilon*=Fuzz4}processTriangle(t){let e=t[0],i=t[1],r=t[2],a=unit(cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[r[0]-e[0],r[1]-e[1],r[2]-e[2]]));this.offscreen([e,i,r])||(this.color?(this.data.indices.push(this.data.Vertex(e,a,this.color[0])),this.data.indices.push(this.data.Vertex(i,a,this.color[1])),this.data.indices.push(this.data.Vertex(r,a,this.color[2]))):(this.data.indices.push(this.vertex(e,a)),this.data.indices.push(this.vertex(i,a)),this.data.indices.push(this.vertex(r,a))),this.append())}processQuad(t){let e=t[0],i=t[1],r=t[2],a=t[3],s=cross([i[0]-e[0],i[1]-e[1],i[2]-e[2]],[r[0]-i[0],r[1]-i[1],r[2]-i[2]]),n=cross([r[0]-a[0],r[1]-a[1],r[2]-a[2]],[a[0]-e[0],a[1]-e[1],a[2]-e[2]]),o=unit([s[0]+n[0],s[1]+n[1],s[2]+n[2]]);if(!this.offscreen([e,i,r,a])){let t,s,n,h;this.color?(t=this.data.Vertex(e,o,this.color[0]),s=this.data.Vertex(i,o,this.color[1]),n=this.data.Vertex(r,o,this.color[2]),h=this.data.Vertex(a,o,this.color[3])):(t=this.vertex(e,o),s=this.vertex(i,o),n=this.vertex(r,o),h=this.vertex(a,o)),this.data.indices.push(t),this.data.indices.push(s),this.data.indices.push(n),this.data.indices.push(t),this.data.indices.push(n),this.data.indices.push(h),this.append()}}process(t){if(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],r=t[12],a=t[15],s=this.normal(i,t[2],t[1],e,t[4],t[8],r);iszero(s)&&iszero(s=this.normal(i,t[2],t[1],e,t[13],t[14],a))&&(s=this.normal(a,t[11],t[7],i,t[4],t[8],r));let n=this.normal(e,t[4],t[8],r,t[13],t[14],a);iszero(n)&&iszero(n=this.normal(e,t[4],t[8],r,t[11],t[7],i))&&(n=this.normal(i,t[2],t[1],e,t[13],t[14],a));let o=this.normal(r,t[13],t[14],a,t[11],t[7],i);iszero(o)&&iszero(o=this.normal(r,t[13],t[14],a,t[2],t[1],e))&&(o=this.normal(e,t[4],t[8],r,t[11],t[7],i));let h=this.normal(a,t[11],t[7],i,t[2],t[1],e);if(iszero(h)&&iszero(h=this.normal(a,t[11],t[7],i,t[4],t[8],r))&&(h=this.normal(r,t[13],t[14],a,t[2],t[1],e)),this.color){let l=this.color[0],c=this.color[1],d=this.color[2],m=this.color[3],u=this.data.Vertex(e,s,l),f=this.data.Vertex(r,n,c),v=this.data.Vertex(a,o,d),p=this.data.Vertex(i,h,m);this.Render(t,u,f,v,p,e,r,a,i,!1,!1,!1,!1,l,c,d,m)}else{let l=this.vertex(e,s),c=this.vertex(r,n),d=this.vertex(a,o),m=this.vertex(i,h);this.Render(t,l,c,d,m,e,r,a,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,r,a,s,n,o,h,l,c,d,m,u,f,v,p){if(this.Distance(t)<this.res2)this.offscreen([s,n,o])||(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(r)),this.offscreen([s,o,h])||(this.data.indices.push(e),this.data.indices.push(r),this.data.indices.push(a));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),S=new Split3(t[4],t[5],t[6],t[7]),b=new Split3(t[8],t[9],t[10],t[11]),P=new Split3(M,t[13],t[14],w),z=new Split3(g,t[4],t[8],M),R=new Split3(A.m0,S.m0,b.m0,P.m0),y=new Split3(A.m3,S.m3,b.m3,P.m3),D=new Split3(A.m5,S.m5,b.m5,P.m5),T=new Split3(A.m4,S.m4,b.m4,P.m4),E=new Split3(A.m2,S.m2,b.m2,P.m2),I=new Split3(x,t[7],t[11],w),O=[g,A.m0,A.m3,A.m5,z.m0,R.m0,y.m0,D.m0,z.m3,R.m3,y.m3,D.m3,z.m5,R.m5,y.m5,D.m5],_=[z.m5,R.m5,y.m5,D.m5,z.m4,R.m4,y.m4,D.m4,z.m2,R.m2,y.m2,D.m2,M,P.m0,P.m3,P.m5],B=[D.m5,T.m5,E.m5,I.m5,D.m4,T.m4,E.m4,I.m4,D.m2,T.m2,E.m2,I.m2,P.m5,P.m4,P.m2,w],C=[A.m5,A.m4,A.m2,x,D.m0,T.m0,E.m0,I.m0,D.m3,T.m3,E.m3,I.m3,D.m5,T.m5,E.m5,I.m5],F=O[15],L=this.normal(O[0],O[4],O[8],O[12],O[13],O[14],O[15]);iszero(L)&&iszero(L=this.normal(O[0],O[4],O[8],O[12],O[11],O[7],O[3]))&&(L=this.normal(O[3],O[2],O[1],O[0],O[13],O[14],O[15]));let V=this.normal(_[12],_[13],_[14],_[15],_[11],_[7],_[3]);iszero(V)&&iszero(V=this.normal(_[12],_[13],_[14],_[15],_[2],_[1],_[0]))&&(V=this.normal(_[0],_[4],_[8],_[12],_[11],_[7],_[3]));let N=this.normal(B[15],B[11],B[7],B[3],B[2],B[1],B[0]);iszero(N)&&iszero(N=this.normal(B[15],B[11],B[7],B[3],B[4],B[8],B[12]))&&(N=this.normal(B[12],B[13],B[14],B[15],B[2],B[1],B[0]));let H=this.normal(C[3],C[2],C[1],C[0],C[4],C[8],C[12]);iszero(H)&&iszero(H=this.normal(C[3],C[2],C[1],C[0],C[13],C[14],C[15]))&&(H=this.normal(C[15],C[11],C[7],C[3],C[4],C[8],C[12]));let U=this.normal(B[3],B[2],B[1],F,B[4],B[8],B[12]),W=this.Epsilon,Y=[.5*(s[0]+n[0]),.5*(s[1]+n[1]),.5*(s[2]+n[2])];if(!l)if(l=Straightness(g,t[4],t[8],M)<this.res2){let t=unit(this.derivative(_[0],_[1],_[2],_[3]));Y=[Y[0]-W*t[0],Y[1]-W*t[1],Y[2]-W*t[2]]}else Y=O[12];let j=[.5*(n[0]+o[0]),.5*(n[1]+o[1]),.5*(n[2]+o[2])];if(!c)if(c=Straightness(M,t[13],t[14],w)<this.res2){let t=unit(this.derivative(B[12],B[8],B[4],B[0]));j=[j[0]-W*t[0],j[1]-W*t[1],j[2]-W*t[2]]}else j=_[15];let G=[.5*(o[0]+h[0]),.5*(o[1]+h[1]),.5*(o[2]+h[2])];if(!d)if(d=Straightness(w,t[11],t[7],x)<this.res2){let t=unit(this.derivative(C[15],B[14],B[13],_[12]));G=[G[0]-W*t[0],G[1]-W*t[1],G[2]-W*t[2]]}else G=B[3];let k=[.5*(h[0]+s[0]),.5*(h[1]+s[1]),.5*(h[2]+s[2])];if(!m)if(m=Straightness(g,t[1],t[2],x)<this.res2){let t=unit(this.derivative(O[3],O[7],O[11],O[15]));k=[k[0]-W*t[0],k[1]-W*t[1],k[2]-W*t[2]]}else k=C[0];if(u){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*(u[e]+f[e]),g[e]=.5*(f[e]+v[e]),x[e]=.5*(v[e]+p[e]),M[e]=.5*(p[e]+u[e]),w[e]=.5*(t[e]+x[e]);let A=this.data.Vertex(Y,L,t),S=this.data.Vertex(j,V,g),b=this.data.Vertex(G,N,x),P=this.data.Vertex(k,H,M),z=this.data.Vertex(F,U,w);this.Render(O,e,A,z,P,s,Y,F,k,l,!1,!1,m,u,t,w,M),this.Render(_,A,i,S,z,Y,n,j,F,l,c,!1,!1,t,f,g,w),this.Render(B,z,S,r,b,F,j,o,G,!1,c,d,!1,w,g,v,x),this.Render(C,P,z,b,a,k,F,G,h,!1,!1,d,m,M,w,x,p)}else{let t=this.vertex(Y,L),u=this.vertex(j,V),f=this.vertex(G,N),v=this.vertex(k,H),p=this.vertex(F,U);this.Render(O,e,t,p,v,s,Y,F,k,l,!1,!1,m),this.Render(_,t,i,u,p,Y,n,j,F,l,c,!1,!1),this.Render(B,p,u,r,f,F,j,o,G,!1,c,d,!1),this.Render(C,v,p,f,a,k,F,G,h,!1,!1,d,m)}}}process3(t){this.Res2=BezierFactor*BezierFactor*this.res2;let e=t[0],i=t[6],r=t[9],a=this.normal(r,t[5],t[2],e,t[1],t[3],i),s=this.normal(e,t[1],t[3],i,t[7],t[8],r),n=this.normal(i,t[7],t[8],r,t[5],t[2],e);if(this.color){let o=this.color[0],h=this.color[1],l=this.color[2],c=this.data.Vertex(e,a,o),d=this.data.Vertex(i,s,h),m=this.data.Vertex(r,n,l);this.Render3(t,c,d,m,e,i,r,!1,!1,!1,o,h,l)}else{let o=this.vertex(e,a),h=this.vertex(i,s),l=this.vertex(r,n);this.Render3(t,o,h,l,e,i,r,!1,!1,!1)}this.data.indices.length>0&&this.append()}Render3(t,e,i,r,a,s,n,o,h,l,c,d,m){if(this.Distance3(t)<this.Res2)this.offscreen([a,s,n])||(this.data.indices.push(e),this.data.indices.push(i),this.data.indices.push(r));else{if(this.offscreen(t))return;let u=t[0],f=t[1],v=t[2],p=t[3],g=t[4],x=t[5],M=t[6],w=t[7],A=t[8],S=t[9],b=[.5*(S[0]+x[0]),.5*(S[1]+x[1]),.5*(S[2]+x[2])],P=[.5*(S[0]+A[0]),.5*(S[1]+A[1]),.5*(S[2]+A[2])],z=[.5*(x[0]+v[0]),.5*(x[1]+v[1]),.5*(x[2]+v[2])],R=[.5*(A[0]+g[0]),.5*(A[1]+g[1]),.5*(A[2]+g[2])],y=[.5*(A[0]+w[0]),.5*(A[1]+w[1]),.5*(A[2]+w[2])],D=[.5*(v[0]+g[0]),.5*(v[1]+g[1]),.5*(v[2]+g[2])],T=[.5*(v[0]+u[0]),.5*(v[1]+u[1]),.5*(v[2]+u[2])],E=[.5*(g[0]+p[0]),.5*(g[1]+p[1]),.5*(g[2]+p[2])],I=[.5*(w[0]+M[0]),.5*(w[1]+M[1]),.5*(w[2]+M[2])],O=[.5*(u[0]+f[0]),.5*(u[1]+f[1]),.5*(u[2]+f[2])],_=[.5*(f[0]+p[0]),.5*(f[1]+p[1]),.5*(f[2]+p[2])],B=[.5*(p[0]+M[0]),.5*(p[1]+M[1]),.5*(p[2]+M[2])],C=[.5*(b[0]+z[0]),.5*(b[1]+z[1]),.5*(b[2]+z[2])],F=[.5*(P[0]+y[0]),.5*(P[1]+y[1]),.5*(P[2]+y[2])],L=[.5*(z[0]+T[0]),.5*(z[1]+T[1]),.5*(z[2]+T[2])],V=[.5*R[0]+.25*(g[0]+f[0]),.5*R[1]+.25*(g[1]+f[1]),.5*R[2]+.25*(g[2]+f[2])],N=[.5*(y[0]+I[0]),.5*(y[1]+I[1]),.5*(y[2]+I[2])],H=[.5*D[0]+.25*(g[0]+w[0]),.5*D[1]+.25*(g[1]+w[1]),.5*D[2]+.25*(g[2]+w[2])],U=[.25*(x[0]+g[0])+.5*E[0],.25*(x[1]+g[1])+.5*E[1],.25*(x[2]+g[2])+.5*E[2]],W=[.5*(O[0]+_[0]),.5*(O[1]+_[1]),.5*(O[2]+_[2])],Y=[.5*(_[0]+B[0]),.5*(_[1]+B[1]),.5*(_[2]+B[2])],j=[.5*(H[0]+W[0]),.5*(H[1]+W[1]),.5*(H[2]+W[2])],G=[.5*(H[0]+Y[0]),.5*(H[1]+Y[1]),.5*(H[2]+Y[2])],k=[.5*(W[0]+Y[0]),.5*(W[1]+Y[1]),.5*(W[2]+Y[2])],X=[.5*(U[0]+N[0]),.5*(U[1]+N[1]),.5*(U[2]+N[2])],Z=[.5*(F[0]+U[0]),.5*(F[1]+U[1]),.5*(F[2]+U[2])],q=[.5*(F[0]+N[0]),.5*(F[1]+N[1]),.5*(F[2]+N[2])],K=[.5*(C[0]+V[0]),.5*(C[1]+V[1]),.5*(C[2]+V[2])],Q=[.5*(L[0]+V[0]),.5*(L[1]+V[1]),.5*(L[2]+V[2])],$=[.5*(C[0]+L[0]),.5*(C[1]+L[1]),.5*(C[2]+L[2])],J=[u,O,T,W,[.5*(D[0]+O[0]),.5*(D[1]+O[1]),.5*(D[2]+O[2])],L,k,j,Q,$],tt=[k,Y,G,B,[.5*(E[0]+I[0]),.5*(E[1]+I[1]),.5*(E[2]+I[2])],X,M,I,N,q],et=[$,K,C,Z,[.5*(b[0]+R[0]),.5*(b[1]+R[1]),.5*(b[2]+R[2])],b,q,F,P,S],it=[q,Z,X,K,[.25*(z[0]+y[0]+_[0]+g[0]),.25*(z[1]+y[1]+_[1]+g[1]),.25*(z[2]+y[2]+_[2]+g[2])],G,$,Q,j,k],rt=this.normal(k,G,X,q,Z,K,$),at=this.normal(q,Z,K,$,Q,j,k),st=this.normal($,Q,j,k,G,X,q),nt=this.Epsilon,ot=[.5*(s[0]+n[0]),.5*(s[1]+n[1]),.5*(s[2]+n[2])];if(!o)if(o=Straightness(M,w,A,S)<this.res2){let t=unit(this.sumderivative(it[0],it[2],it[5],it[9],it[1],it[3],it[6]));ot=[ot[0]-nt*t[0],ot[1]-nt*t[1],ot[2]-nt*t[2]]}else ot=q;let ht=[.5*(n[0]+a[0]),.5*(n[1]+a[1]),.5*(n[2]+a[2])];if(!h)if(h=Straightness(u,v,x,S)<this.res2){let t=unit(this.sumderivative(it[6],it[3],it[1],it[0],it[7],it[8],it[9]));ht=[ht[0]-nt*t[0],ht[1]-nt*t[1],ht[2]-nt*t[2]]}else ht=$;let lt=[.5*(a[0]+s[0]),.5*(a[1]+s[1]),.5*(a[2]+s[2])];if(!l)if(l=Straightness(u,f,p,M)<this.res2){let t=unit(this.sumderivative(it[9],it[8],it[7],it[6],it[5],it[2],it[0]));lt=[lt[0]-nt*t[0],lt[1]-nt*t[1],lt[2]-nt*t[2]]}else lt=k;if(c){let t=Array(4),u=Array(4),f=Array(4);for(let e=0;e<4;++e)t[e]=.5*(d[e]+m[e]),u[e]=.5*(m[e]+c[e]),f[e]=.5*(c[e]+d[e]);let v=this.data.Vertex(ot,rt,t),p=this.data.Vertex(ht,at,u),g=this.data.Vertex(lt,st,f);this.Render3(J,e,g,p,a,lt,ht,!1,h,l,c,f,u),this.Render3(tt,g,i,v,lt,s,ot,o,!1,l,f,d,t),this.Render3(et,p,v,r,ht,ot,n,o,h,!1,u,t,m),this.Render3(it,v,p,g,ot,ht,lt,!1,!1,!1,t,u,f)}else{let t=this.vertex(ot,rt),c=this.vertex(ht,at),d=this.vertex(lt,st);this.Render3(J,e,d,c,a,lt,ht,!1,h,l),this.Render3(tt,d,i,t,lt,s,ot,o,!1,l),this.Render3(et,c,t,r,ht,ot,n,o,h,!1),this.Render3(it,t,c,d,ot,ht,lt,!1,!1,!1)}}}Distance(t){let e=t[0],i=t[3],r=t[12],a=t[15],s=Distance2(a,e,this.normal(i,t[2],t[1],e,t[4],t[8],r));return s=Math.max(s,Straightness(e,t[1],t[2],i)),s=Math.max(s,Straightness(e,t[4],t[8],r)),s=Math.max(s,Straightness(i,t[7],t[11],a)),s=Math.max(s,Straightness(r,t[13],t[14],a)),s=Math.max(s,Straightness(t[4],t[5],t[6],t[7])),s=Math.max(s,Straightness(t[8],t[9],t[10],t[11])),s=Math.max(s,Straightness(t[1],t[5],t[9],t[13])),Math.max(s,Straightness(t[2],t[6],t[10],t[14]))}Distance3(t){let e=t[0],i=t[4],r=t[6],a=t[9],s=abs2([(e[0]+r[0]+a[0])*third-i[0],(e[1]+r[1]+a[1])*third-i[1],(e[2]+r[2]+a[2])*third-i[2]]);return s=Math.max(s,Straightness(e,t[1],t[3],r)),s=Math.max(s,Straightness(e,t[2],t[5],a)),Math.max(s,Straightness(r,t[7],t[8],a))}derivative(t,e,i,r){let a=[e[0]-t[0],e[1]-t[1],e[2]-t[2]];if(abs2(a)>this.epsilon)return a;let s=bezierPP(t,e,i);return abs2(s)>this.epsilon?s:bezierPPP(t,e,i,r)}sumderivative(t,e,i,r,a,s,n){let o=this.derivative(t,e,i,r),h=this.derivative(t,a,s,n);return[o[0]+h[0],o[1]+h[1],o[2]+h[2]]}normal(t,e,i,r,a,s,n){let o=a[0]-r[0],h=a[1]-r[1],l=a[2]-r[2],c=i[0]-r[0],d=i[1]-r[1],m=i[2]-r[2],u=[h*m-l*d,l*c-o*m,o*d-h*c];if(abs2(u)>this.epsilon)return unit(u);let f=[c,d,m],v=[o,h,l],p=bezierPP(r,i,e),g=bezierPP(r,a,s),x=cross(g,f),M=cross(v,p);if(abs2(u=[x[0]+M[0],x[1]+M[1],x[2]+M[2]])>this.epsilon)return unit(u);let w=bezierPPP(r,i,e,t),A=bezierPPP(r,a,s,n);x=cross(g,p),M=cross(v,w);let S=cross(A,f),b=cross(A,p),P=cross(g,w),z=cross(A,w);return unit([9*x[0]+3*(M[0]+S[0]+b[0]+P[0])+z[0],9*x[1]+3*(M[1]+S[1]+b[1]+P[1])+z[1],9*x[2]+3*(M[2]+S[2]+b[2]+P[2])+z[2]])}}class BezierCurve extends Geometry{constructor(t,e,i,r,a){super(),this.controlpoints=t,this.Min=r,this.Max=a,this.CenterIndex=e,this.MaterialIndex=i}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 r=t[0],a=t[1],s=t[2],n=t[3];if(Straightness(r,a,s,n)<this.res2)this.offscreen([r,n])||(this.data.indices.push(e),this.data.indices.push(i));else{if(this.offscreen(t))return;let o=[.5*(r[0]+a[0]),.5*(r[1]+a[1]),.5*(r[2]+a[2])],h=[.5*(a[0]+s[0]),.5*(a[1]+s[1]),.5*(a[2]+s[2])],l=[.5*(s[0]+n[0]),.5*(s[1]+n[1]),.5*(s[2]+n[2])],c=[.5*(o[0]+h[0]),.5*(o[1]+h[1]),.5*(o[2]+h[2])],d=[.5*(h[0]+l[0]),.5*(h[1]+l[1]),.5*(h[2]+l[2])],m=[.5*(c[0]+d[0]),.5*(c[1]+d[1]),.5*(c[2]+d[2])],u=[r,o,c,m],f=[m,d,l,n],v=this.data.vertex1(m);this.Render(u,e,v),this.Render(f,v,i)}}}class Pixel extends Geometry{constructor(t,e,i,r,a){super(),this.controlpoint=t,this.width=e,this.CenterIndex=0,this.MaterialIndex=i,this.Min=r,this.Max=a}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}process(t){for(let t=0,e=this.Indices.length;t<e;++t){let e=this.Indices[t],i=e[0],r=this.Positions[i[0]],a=this.Positions[i[1]],s=this.Positions[i[2]];if(!this.offscreen([r,a,s])){let t=e.length>1?e[1]:i;if(t&&0!=t.length||(t=i),this.Colors.length>0){let n=e.length>2?e[2]:i;n&&0!=n.length||(n=i);let o=this.Colors[n[0]],h=this.Colors[n[1]],l=this.Colors[n[2]];this.transparent|=o[3]+h[3]+l[3]<765,materialIndex=-1-this.MaterialIndex,this.data.iVertex(i[0],r,this.Normals[t[0]],o),this.data.iVertex(i[1],a,this.Normals[t[1]],h),this.data.iVertex(i[2],s,this.Normals[t[2]],l)}else materialIndex=1+this.MaterialIndex,this.data.iVertex(i[0],r,this.Normals[t[0]]),this.data.iVertex(i[1],a,this.Normals[t[1]]),this.data.iVertex(i[2],s,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(),redraw=!0}function initShader(t=[]){let e=getShader(gl,"fragment",t),i=getShader(gl,"vertex",t),r=gl.createProgram();return gl.attachShader(r,i),gl.attachShader(r,e),gl.linkProgram(r),gl.getProgramParameter(r,gl.LINK_STATUS)||alert("Could not initialize shaders"),r}class Split3{constructor(t,e,i,r){this.m0=[.5*(t[0]+e[0]),.5*(t[1]+e[1]),.5*(t[2]+e[2])];let a=.5*(e[0]+i[0]),s=.5*(e[1]+i[1]),n=.5*(e[2]+i[2]);this.m2=[.5*(i[0]+r[0]),.5*(i[1]+r[1]),.5*(i[2]+r[2])],this.m3=[.5*(this.m0[0]+a),.5*(this.m0[1]+s),.5*(this.m0[2]+n)],this.m4=[.5*(a+this.m2[0]),.5*(s+this.m2[1]),.5*(n+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,r){return[r[0]-t[0]+3*(e[0]-i[0]),r[1]-t[1]+3*(e[1]-i[1]),r[2]-t[2]+3*(e[2]-i[2])]}function Straightness(t,e,i,r){let a=[third*(r[0]-t[0]),third*(r[1]-t[1]),third*(r[2]-t[2])];return Math.max(abs2([e[0]-a[0]-t[0],e[1]-a[1]-t[1],e[2]-a[2]-t[2]]),abs2([r[0]-a[0]-i[0],r[1]-a[1]-i[1],r[2]-a[2]-i[2]]))}function Distance2(t,e,i){let r=dot([t[0]-e[0],t[1]-e[1],t[2]-e[2]],i);return r*r}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){let e=t==pixelShader;gl.useProgram(t),t.vertexPositionAttribute=gl.getAttribLocation(t,"position"),gl.enableVertexAttribArray(t.vertexPositionAttribute),e&&(t.vertexWidthAttribute=gl.getAttribLocation(t,"width"),gl.enableVertexAttribArray(t.vertexWidthAttribute)),t==noNormalShader||e||(t.vertexNormalAttribute=gl.getAttribLocation(t,"normal"),gl.enableVertexAttribArray(t.vertexNormalAttribute)),t.vertexMaterialAttribute=gl.getAttribLocation(t,"materialIndex"),gl.enableVertexAttribArray(t.vertexMaterialAttribute),t.projViewMatUniform=gl.getUniformLocation(t,"projViewMat"),t.viewMatUniform=gl.getUniformLocation(t,"viewMat"),t.normMatUniform=gl.getUniformLocation(t,"normMat"),t!=colorShader&&t!=transparentShader||(t.vertexColorAttribute=gl.getAttribLocation(t,"color"),gl.enableVertexAttribArray(t.vertexColorAttribute));for(let e=0;e<Materials.length;++e)Materials[e].setUniform(t,"Materials",e);for(let e=0;e<Lights.length;++e)Lights[e].setUniform(t,"Lights",e);gl.uniformMatrix4fv(t.projViewMatUniform,!1,projViewMat),gl.uniformMatrix4fv(t.viewMatUniform,!1,viewMat),gl.uniformMatrix3fv(t.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,r,a){if(t==i&&e==r)return;let[s,n]=arcball([t,-e],[i,-r]);mat4.fromRotation(rotMats,2*a*ArcballFactor*s/lastzoom,n),mat4.multiply(rotMat,rotMats,rotMat)}function shiftScene(t,e,i,r){let a=1/lastzoom;shift.x+=(i-t)*a*halfCanvasWidth,shift.y-=(r-e)*a*halfCanvasHeight}function panScene(t,e,i,r){orthographic?shiftScene(t,e,i,r):(center.x+=(i-t)*(viewParam.xmax-viewParam.xmin),center.y-=(r-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],r=Math.hypot(e,i);return r>1&&(denom=1/r,e*=denom,i*=denom),[e,i,Math.sqrt(Math.max(1-i*i-e*e,0))]}function arcball(t,e){let i=normMouse(t),r=normMouse(e),a=dot(i,r);return a>1?a=1:a<-1&&(a=-1),[Math.acos(a),unit(cross(i,r))]}function zoomScene(t,e,i,r){zoomImage(e-r)}const DRAGMODE_ROTATE=1,DRAGMODE_SHIFT=2,DRAGMODE_ZOOM=3,DRAGMODE_PAN=4;function processDrag(t,e,i,r=1){let a;switch(i){case DRAGMODE_ROTATE:a=rotateScene;break;case DRAGMODE_SHIFT:a=shiftScene;break;case DRAGMODE_ZOOM:a=zoomScene;break;case DRAGMODE_PAN:a=panScene;break;default:a=((t,e,i,r)=>{})}a((lastMouseX-halfCanvasWidth)/halfCanvasWidth,(lastMouseY-halfCanvasHeight)/halfCanvasHeight,(t-halfCanvasWidth)/halfCanvasWidth,(e-halfCanvasHeight)/halfCanvasHeight,r),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.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 indexExt,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,r=t-lastMouseX,a=i-lastMouseY,s=r*r+a*a<=shiftHoldDistance*shiftHoldDistance;if(s&&!swipe&&!rotate&&(new Date).getTime()-touchStartTime>shiftWaitTime&&(navigator.vibrate&&window.navigator.vibrate(vibrateTime),swipe=!0),swipe)processDrag(t,i,DRAGMODE_SHIFT);else if(!s){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}}function setBuffer(){positionBuffer=gl.createBuffer(),materialBuffer=gl.createBuffer(),colorBuffer=gl.createBuffer(),indexBuffer=gl.createBuffer(),indexExt=gl.getExtension("OES_element_index_uint")}let pixelShader,noNormalShader,materialShader,colorShader,transparentShader,zbuffer=[];function transformVertices(t){let e=viewMat[2],i=viewMat[6],r=viewMat[10];zbuffer.length=t.length;for(let a=0;a<t.length;++a){let s=6*a;zbuffer[a]=e*t[s]+i*t[s+1]+r*t[s+2]}}function draw(){gl.clearColor(1,1,1,1),gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT),material0Data.clear(),material1Data.clear(),materialData.clear(),colorData.clear(),triangleData.clear(),transparentData.clear(),P.forEach(function(t){t.render()}),drawBuffer(material0Data,pixelShader),drawBuffer(material1Data,noNormalShader),drawBuffer(materialData,materialShader),drawBuffer(colorData,colorShader),drawBuffer(triangleData,transparentShader);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 r=3*e;Ia=t[r],Ib=t[r+1],Ic=t[r+2];let a=3*i;return IA=t[a],IB=t[a+1],IC=t[a+2],zbuffer[Ia]+zbuffer[Ib]+zbuffer[Ic]<zbuffer[IA]+zbuffer[IB]+zbuffer[IC]?-1:1});let r=Array(t.length);for(let a=0;a<e;++a){let e=3*i[a];r[3*a]=t[e],r[3*a+1]=t[e+1],r[3*a+2]=t[e+2]}gl.depthMask(!1),drawBuffer(transparentData,transparentShader,r),gl.depthMask(!0)}remesh=!1}function tick(){requestAnimationFrame(tick),redraw&&(draw(),redraw=!1)}function setDimensions(t,e,i,r){let a=t/e,s=1/lastzoom,n=i/t*lastzoom,o=r/e*lastzoom;if(orthographic){let t=B[0]-b[0],e=B[1]-b[1];if(t<e*a){let t=.5*e*a*s,i=2*t*n,r=e*s*o;viewParam.xmin=-t-i,viewParam.xmax=t-i,viewParam.ymin=b[1]*s-r,viewParam.ymax=B[1]*s-r}else{let e=.5*t/(a*Zoom),i=t*s*n,r=2*e*o;viewParam.xmin=b[0]*s-i,viewParam.xmax=B[0]*s-i,viewParam.ymin=-e-r,viewParam.ymax=e-r}}else{let t=H*s,e=t*a,i=2*e*n,r=2*t*o;viewParam.xmin=-e-i,viewParam.xmax=e-i,viewParam.ymin=-t-r,viewParam.ymax=t-r}}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),home()}function setCanvas(){canvas.width=canvasWidth,canvas.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()}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"),absolute)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,initGL(),gl.enable(gl.BLEND),gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA),gl.enable(gl.DEPTH_TEST),setViewport(),noNormalShader=initShader(),pixelShader=initShader(["WIDTH"]),materialShader=initShader(["NORMAL"]),colorShader=initShader(["NORMAL","COLOR"]),transparentShader=initShader(["NORMAL","COLOR","TRANSPARENT"]),setBuffer(),canvas.onmousedown=handleMouseDown,document.onmouseup=handleMouseUpOrTouchEnd,document.onmousemove=handleMouseMove,canvas.onkeydown=handleKey,document.onwheel=handleMouseWheel,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()}
+/*@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.*/ \ No newline at end of file