BruceSherwood avatar BruceSherwood committed 8969cc7

Restructure fragment shaders to work on smartphone

Comments (0)

Files changed (7)

 shader_file = "\n".join(shader_file)
 open("lib/glow/shaders.gen.js", "wb").write(shader_file)
 
-version = "1.0"
+version = "1.1dev"
 # TODO: Extract this information from run.js
 glowscript_libraries = {
     "run": [

docs/GlowScriptDocs/index.html

 <p class="Normal"><strong>Safari</strong>: To activate WebGL in Safari 5.1 (included with OSX Lion), go to the Advanced section of Safari preferences and check &quot;Show Develop menu in menu bar&quot;, then on the Develop menu check &quot;Enable WebGL&quot;. Prior to Safari 5.1, to get WebGL required downloading and installing WebKit from Apple (<a href="http://nightly.webkit.org" target="_blank">http://nightly.webkit.org</a>).</p>
       <p class="Normal"> <strong>Internet Explorer</strong>: Although the basic version of Internet Explorer does not include WebGL, there is a WebGL plug-in available at <a href="http://code.google.com/chrome/chromeframe/">http://code.google.com/chrome/chromeframe</a> which lets you run GlowScript. You can simply try running a GlowScript program and you'll be prompted to allow this plug-in to be installed.</p>
       <p class="Normal"> <strong>Firefox</strong>: To activate WebGL in Firefox, go to the fake URL <strong>about:config</strong>, search for webgl, and set <strong>webgl.force-enabled=true</strong> (this may not be necessary on Ubuntu Linux). To find out what version of Firefox is installed, use the fake URL <strong>about:</strong>.</p>
-      <p class="Normal"><strong>Tablets and smart phones</strong>: Most tablets and smart phones do not yet support WebGL, though this is likely to change.</p>
+      <p class="Normal"><strong>Tablets and smart phones</strong>: Most tablets and smart phones do not yet support WebGL, though this is likely to change. On the Samsung Galaxy S3 smartphone, Firefox and Opera do run GlowScript programs, though animations are slow, transparency is buggy, and currently there is no way to zoom and rotate. There are reports that GlowScript also works on the Sony Experia smartphone.</p>
       <p class="Normal"><strong><font color="#0000A0">Learning JavaScript</font></strong><br />
       </p>
       <p class="Normal">GlowScript uses JavaScript, a programming language that is widely used in web browsers, though it has other uses as well. Despite its name, JavaScript is not related to the Java programming language.</p>

lib/glow/WebGLRenderer.js

     var MERGE = 13  // merge C0, C1, C2, C3, C4 onto a quad
 
     function WebGLRenderer(canvas, canvasElement, overlay) {
-    	//canvas.title.text("0.8dev 11:25")
+    	//canvas.caption.text("1.1dev 10:45")
     	
         var renderer = this
         var gl = WebGLUtils.setupWebGL(canvasElement) // main canvas

lib/glow/shaders.gen.js

 "final_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D FINAL;\nvarying vec2 mat_pos;\n\nvoid main(void) {\n    gl_FragColor = vec4( texture2D(FINAL, mat_pos).xyz, 1.0);\n}\n',
 "merge_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D C0; // TEXTURE2 - opaque color map (minormode 4)\nuniform sampler2D C1; // TEXTURE6 - color map for transparency render 1 (minormode 9)\nuniform sampler2D C2; // TEXTURE7 - color map for transparency render 2 (minormode 10)\nuniform sampler2D C3; // TEXTURE8 - color map for transparency render 3 (minormode 11)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 8); also used for C4\nuniform vec2 canvas_size;\n\nvoid main(void) {\n    // need to combine colors from C0, C1, C2, C3, C4\n    vec2 loc = vec2( gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    vec4 c0 = texture2D(C0, loc);\n    vec4 c1 = texture2D(C1, loc);\n    vec4 c2 = texture2D(C2, loc);\n    vec4 c3 = texture2D(C3, loc);\n    vec4 c4 = texture2D(D2, loc);\n    vec3 mcolor = c1.rgb*c1.a + \n                 (1.0-c1.a)*(c2.rgb*c2.a +\n                 (1.0-c2.a)*(c3.rgb*c3.a +\n                 (1.0-c3.a)*(c4.rgb*c4.a + \n                 (1.0-c4.a)*c0.rgb)));\n    gl_FragColor = vec4 (mcolor, 1.0);\n}\n',
 "merge_vertex":"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nvoid main(void) {\n    gl_Position = vec4(pos, 1.0);\n}\n",
-"opaque_render_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )\n{\n    vec3 color;\n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return color;\n    }\n    color = light_ambient * diffuse_color;\n\n    for(int i=0; i<8; i++) {\n        if (i >= light_count) break;\n        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light\n        L = normalize(L);\n        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;\n        if (shininess > 0.0) {\n            vec3 R = reflect(L,normal);\n            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n        }\n    }\n    return color;\n}\n\nvoid main(void) {\n    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    gl_FragColor = vec4( color, 1.0 );\n}\n',
-"peel_color_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\nuniform vec2 canvas_size;\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )\n{\n    vec3 color;\n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return color;\n    }\n    color = light_ambient * diffuse_color;\n\n    for(int i=0; i<8; i++) {\n        if (i >= light_count) break;\n        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light\n        L = normalize(L);\n        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;\n        if (shininess > 0.0) {\n            vec3 R = reflect(L,normal);\n            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n        }\n    }\n    return color;\n}\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create transparency color map - C1 (9), C2 (10), C3 (11), C4 (12)\n    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    float z = decode(encode(1.0-gl_FragCoord.z)); // bigger number => closer to camera; distance out of screen\n    float zmin = decode(texture2D(D0, loc));\n    float zmax;\n    if (minormode == 9) { // C1\n        if (z > zmin) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    } else {\n        if (minormode == 11) { // C3 (11)\n            zmax = decode(texture2D(D2, loc));\n        } else { // C2 (10) or C4 (12)\n            zmax = decode(texture2D(D1, loc));\n        }\n        if (zmin < z && z < zmax) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    }\n}\n',
+"opaque_render_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n#define LP(i) light_pos[i]\n#define LC(i) light_color[i]\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\nvec3 normal;\nvec3 pos;\nvec3 diffuse_color;\nvec3 specular_color;\nvec3 color;\n\nvoid calc_color(vec4 lpos, vec3 lcolor)\n{\n    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light\n    L = normalize(L);\n    float N = max(dot(normal,L), 0.0);\n    color += (lcolor * N)*diffuse_color;\n    if (shininess > 0.0) {\n        vec3 R = reflect(L,normal);\n        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n    }\n}\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvoid lightAt()\n{    \n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return;\n    }\n    \n    color = light_ambient * diffuse_color;\n    \n    // It was necessary to restructure this shader completely in order to\n    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler\n    // does not handle for loops correctly.\n    if (light_count == 0) return;\n    calc_color(LP(0), LC(0));\n    if (light_count == 1) return;\n    calc_color(LP(1), LC(1));\n    if (light_count == 2) return;\n    calc_color(LP(2), LC(2));\n    if (light_count == 3) return;\n    calc_color(LP(3), LC(3));\n    if (light_count == 4) return;\n    calc_color(LP(4), LC(4));\n    if (light_count == 5) return;\n    calc_color(LP(5), LC(5));\n    if (light_count == 6) return;\n    calc_color(LP(6), LC(6));\n    if (light_count == 7) return;\n    calc_color(LP(7), LC(7));\n}\n\nvoid main(void) {\n    normal = normalize(es_normal);\n    pos = es_position;\n    diffuse_color = vcolor.rgb;\n    specular_color = vec3(.8,.8,.8);\n    lightAt(); // determine color from lighting\n    //vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    gl_FragColor = vec4( color, 1.0 );\n}\n',
+"peel_color_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n#define LP(i) light_pos[i]\n#define LC(i) light_color[i]\nuniform vec2 canvas_size;\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\nvec3 normal;\nvec3 pos;\nvec3 diffuse_color;\nvec3 specular_color;\nvec3 color;\n\n\nvoid calc_color(vec4 lpos, vec3 lcolor)\n{\n    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light\n    L = normalize(L);\n    float N = max(dot(normal,L), 0.0);\n    color += (lcolor * N)*diffuse_color;\n    if (shininess > 0.0) {\n        vec3 R = reflect(L,normal);\n        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n    }\n}\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvoid lightAt()\n{    \n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return;\n    }\n    \n    color = light_ambient * diffuse_color;\n    \n    // It was necessary to restructure this shader completely in order to\n    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler\n    // does not handle for loops correctly.\n    if (light_count == 0) return;\n    calc_color(LP(0), LC(0));\n    if (light_count == 1) return;\n    calc_color(LP(1), LC(1));\n    if (light_count == 2) return;\n    calc_color(LP(2), LC(2));\n    if (light_count == 3) return;\n    calc_color(LP(3), LC(3));\n    if (light_count == 4) return;\n    calc_color(LP(4), LC(4));\n    if (light_count == 5) return;\n    calc_color(LP(5), LC(5));\n    if (light_count == 6) return;\n    calc_color(LP(6), LC(6));\n    if (light_count == 7) return;\n    calc_color(LP(7), LC(7));\n}\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create transparency color map - C1 (9), C2 (10), C3 (11), C4 (12)\n    normal = normalize(es_normal);\n    pos = es_position;\n    diffuse_color = vcolor.rgb;\n    specular_color = vec3(.8,.8,.8);\n    lightAt(); // determine color from lighting\n    vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    float z = decode(encode(1.0-gl_FragCoord.z)); // bigger number => closer to camera; distance out of screen\n    float zmin = decode(texture2D(D0, loc));\n    float zmax;\n    if (minormode == 9) { // C1\n        if (z > zmin) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    } else {\n        if (minormode == 11) { // C3 (11)\n            zmax = decode(texture2D(D2, loc));\n        } else { // C2 (10) or C4 (12)\n            zmax = decode(texture2D(D1, loc));\n        }\n        if (zmin < z && z < zmax) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    }\n}\n',
 "peel_depth_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\n// Construct depth maps for depth peeling handling of opacity\n\nuniform vec2 canvas_size;\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create depth map, D0 (5); ping-pong D1 (6) and D2 (7)\n    vec4 c = encode(1.0-gl_FragCoord.z);\n    float z = decode(c);\n    if (minormode == 5) { // D0\n        gl_FragColor = c;\n    } else {\n        vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n        float zmin = decode(texture2D(D0, loc));\n        float zmax;\n        if (minormode == 6) { // first creation of D1\n            if (z > zmin) {\n                gl_FragColor = c;\n            } else {\n                discard;\n            }\n        } else {\n            if (minormode == 7) {\n                zmax = decode(texture2D(D1, loc)); // create D2\n            } else {\n                zmax = decode(texture2D(D2, loc)); // D1b; create D1 again\n            }\n            if (zmin < z && z < zmax) {\n                gl_FragColor = c;\n            } else {\n                discard; // All pixels are discarded, since (zmax < z && z < zmax) is always false\n            }\n        }\n    }\n}',
 "peel_depth_vertex":"// Vertex shader for picking standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectAxis objectData[1].xyz\n#define objectUp objectData[2].xyz\n#define objectScale objectData[3].xyz\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nmat3 getObjectRotation() {\n  // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n}",
 "pick_fragment":'#ifdef GL_ES\nprecision highp float;\n#endif\n\nvarying vec4 vcolor;\n\nvoid main(void) {\n    gl_FragColor = vcolor;\n}\n',

package/glow.1.0.min.js

 .getData=function(){return g},w.getAxes=function(){var b={},c;return a.each(o.concat(p),function(a,c){c&&(b[c.direction+(c.n!=1?c.n:"")+"axis"]=c)}),b},w.getXAxes=function(){return o},w.getYAxes=function(){return p},w.c2p=E,w.p2c=F,w.getOptions=function(){return h},w.highlight=br,w.unhighlight=bs,w.triggerRedrawOverlay=bp,w.pointOffset=function(a){return{left:parseInt(o[C(a,"x")-1].p2c(+a.x)+q.left),top:parseInt(p[C(a,"y")-1].p2c(+a.y)+q.top)}},w.shutdown=O,w.resize=function(){K(),L(j),L(k)},w.hooks=v,y(w),z(e),M(),A(d),T(),Y(),N();var bi=[],bj=null}function c(a,b){return b*Math.floor(a/b)}a.plot=function(c,d,e){var f=new b(a(c),d,e,a.plot.plugins);return f},a.plot.version="0.7",a.plot.plugins=[],a.plot.formatDate=function(a,b,c){var d=function(a){return a=""+a,a.length==1?"0"+a:a},e=[],f=!1,g=!1,h=a.getUTCHours(),i=h<12;c==null&&(c=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),b.search(/%p|%P/)!=-1&&(h>12?h=h-12:h==0&&(h=12));for(var j=0;j<b.length;++j){var k=b.charAt(j);if(f){switch(k){case"h":k=""+h;break;case"H":k=d(h);break;case"M":k=d(a.getUTCMinutes());break;case"S":k=d(a.getUTCSeconds());break;case"d":k=""+a.getUTCDate();break;case"m":k=""+(a.getUTCMonth()+1);break;case"y":k=""+a.getUTCFullYear();break;case"b":k=""+c[a.getUTCMonth()];break;case"p":k=i?"am":"pm";break;case"P":k=i?"AM":"PM";break;case"0":k="",g=!0}k&&g&&(k=d(k),g=!1),e.push(k),g||(f=!1)}else k=="%"?f=!0:e.push(k)}return e.join("")}}(jQuery),function(a){function c(a){return Math.log(a)/Math.LN10}function d(a,b){b===undefined&&(b=3);if(a===0)return 0;if(b<=0)throw new Error("Significant figures must be greater than zero.");b=Math.floor(b+.5);var d=a>=0?"":"-";a=Math.abs(a);var e=Math.floor(c(a))+1;if(e>b)return a=a.toPrecision(b),d+a.replace("+","");if(e<0){var f=a*pow(10,abs(e)+1);return e-=1,d+f.toFixed(b-1)+"e"+e}return d+a.toFixed(b-e)}function e(a){function c(c){if(b.locked)return;b.x!=-1&&(b.x=-1,a.triggerRedrawOverlay())}function e(c){if(b.locked)return;if(a.getSelection&&a.getSelection()){b.x=-1;return}var d=a.offset();b.x=Math.max(0,Math.min(c.pageX-d.left,a.width())),b.y=Math.max(0,Math.min(c.pageY-d.top,a.height())),a.triggerRedrawOverlay()}var b={x:-1,y:-1,locked:!1};a.setCrosshair=function(c){if(!c)b.x=-1;else{var d=a.p2c(c);b.x=Math.max(0,Math.min(d.left,a.width())),b.y=Math.max(0,Math.min(d.top,a.height()))}a.triggerRedrawOverlay()},a.clearCrosshair=a.setCrosshair,a.lockCrosshair=function(c){c&&a.setCrosshair(c),b.locked=!0},a.unlockCrosshair=function(){b.locked=!1},a.hooks.bindEvents.push(function(a,b){if(!a.getOptions().crosshair.mode)return;b.mouseout(c),b.mousemove(e)}),a.hooks.drawOverlay.push(function(a,c){var e=a.getOptions().crosshair;if(!e.mode)return;var f=a.getPlotOffset();c.save(),c.translate(f.left,f.top);if(b.x!=-1){var g=a.c2p({left:b.x,top:b.y});text=d(g.x,3)+","+d(g.y,3);var h=13;c.fillStyle=e.color,c.font=h+"px Verdana";var i=c.measureText(text).width,j=0,k=0;b.y<h+3?(i+=14,k=h,b.x<=a.width()-(i+5)&&(j+=14)):k=-5,b.x>a.width()-(i+5)?(c.textAlign="right",j+=-3):(c.textAlign="left",j+=3),c.fillText(text,b.x+j,b.y+k),c.strokeStyle=e.color,c.lineWidth=e.lineWidth,c.lineJoin="round",c.beginPath(),e.mode.indexOf("x")!=-1&&(c.moveTo(b.x,0),c.lineTo(b.x,a.height())),e.mode.indexOf("y")!=-1&&(c.moveTo(0,b.y),c.lineTo(a.width(),b.y)),c.stroke()}c.restore()}),a.hooks.shutdown.push(function(a,b){b.unbind("mouseout",c),b.unbind("mousemove",e)})}var b={crosshair:{mode:null,color:"rgba(0, 0, 0, 0.5)",lineWidth:1}};a.plot.plugins.push({init:e,options:b,name:"crosshair_GS",version:"1.0"})}(jQuery),typeof Float32Array!="undefined"?glMatrixArrayType=Float32Array:typeof WebGLFloatArray!="undefined"?glMatrixArrayType=WebGLFloatArray:glMatrixArrayType=Array;var vec3={};vec3.create=function(a){var b=new glMatrixArrayType(3);return a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2]),b},vec3.set=function(a,b){return b[0]=a[0],b[1]=a[1],b[2]=a[2],b},vec3.add=function(a,b,c){return!c||a==c?(a[0]+=b[0],a[1]+=b[1],a[2]+=b[2],a):(c[0]=a[0]+b[0],c[1]=a[1]+b[1],c[2]=a[2]+b[2],c)},vec3.subtract=function(a,b,c){return!c||a==c?(a[0]-=b[0],a[1]-=b[1],a[2]-=b[2],a):(c[0]=a[0]-b[0],c[1]=a[1]-b[1],c[2]=a[2]-b[2],c)},vec3.negate=function(a,b){return b||(b=a),b[0]=-a[0],b[1]=-a[1],b[2]=-a[2],b},vec3.scale=function(a,b,c){return!c||a==c?(a[0]*=b,a[1]*=b,a[2]*=b,a):(c[0]=a[0]*b,c[1]=a[1]*b,c[2]=a[2]*b,c)},vec3.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],f=Math.sqrt(c*c+d*d+e*e);return f?f==1?(b[0]=c,b[1]=d,b[2]=e,b):(f=1/f,b[0]=c*f,b[1]=d*f,b[2]=e*f,b):(b[0]=0,b[1]=0,b[2]=0,b)},vec3.cross=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=b[0],h=b[1],i=b[2];return c[0]=e*i-f*h,c[1]=f*g-d*i,c[2]=d*h-e*g,c},vec3.length=function(a){var b=a[0],c=a[1],d=a[2];return Math.sqrt(b*b+c*c+d*d)},vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]},vec3.direction=function(a,b,c){c||(c=a);var d=a[0]-b[0],e=a[1]-b[1],f=a[2]-b[2],g=Math.sqrt(d*d+e*e+f*f);return g?(g=1/g,c[0]=d*g,c[1]=e*g,c[2]=f*g,c):(c[0]=0,c[1]=0,c[2]=0,c)},vec3.lerp=function(a,b,c,d){return d||(d=a),d[0]=a[0]+c*(b[0]-a[0]),d[1]=a[1]+c*(b[1]-a[1]),d[2]=a[2]+c*(b[2]-a[2]),d},vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"};var mat3={};mat3.create=function(a){var b=new glMatrixArrayType(9);return a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8]),b},mat3.set=function(a,b){return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b},mat3.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=1,a[5]=0,a[6]=0,a[7]=0,a[8]=1,a},mat3.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[5];return a[1]=a[3],a[2]=a[6],a[3]=c,a[5]=a[7],a[6]=d,a[7]=e,a}return b[0]=a[0],b[1]=a[3],b[2]=a[6],b[3]=a[1],b[4]=a[4],b[5]=a[7],b[6]=a[2],b[7]=a[5],b[8]=a[8],b},mat3.toMat4=function(a,b){return b||(b=mat4.create()),b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=0,b[4]=a[3],b[5]=a[4],b[6]=a[5],b[7]=0,b[8]=a[6],b[9]=a[7],b[10]=a[8],b[11]=0,b[12]=0,b[13]=0,b[14]=0,b[15]=1,b},mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};var mat4={};mat4.create=function(a){var b=new glMatrixArrayType(16);return a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15]),b},mat4.set=function(a,b){return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=a[12],b[13]=a[13],b[14]=a[14],b[15]=a[15],b},mat4.identity=function(a){return a[0]=1,a[1]=0,a[2]=0,a[3]=0,a[4]=0,a[5]=1,a[6]=0,a[7]=0,a[8]=0,a[9]=0,a[10]=1,a[11]=0,a[12]=0,a[13]=0,a[14]=0,a[15]=1,a},mat4.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[3],f=a[6],g=a[7],h=a[11];return a[1]=a[4],a[2]=a[8],a[3]=a[12],a[4]=c,a[6]=a[9],a[7]=a[13],a[8]=d,a[9]=f,a[11]=a[14],a[12]=e,a[13]=g,a[14]=h,a}return b[0]=a[0],b[1]=a[4],b[2]=a[8],b[3]=a[12],b[4]=a[1],b[5]=a[5],b[6]=a[9],b[7]=a[13],b[8]=a[2],b[9]=a[6],b[10]=a[10],b[11]=a[14],b[12]=a[3],b[13]=a[7],b[14]=a[11],b[15]=a[15],b},mat4.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],f=a[4],g=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],m=a[11],n=a[12],o=a[13],p=a[14],q=a[15];return n*k*h*e-j*o*h*e-n*g*l*e+f*o*l*e+j*g*p*e-f*k*p*e-n*k*d*i+j*o*d*i+n*c*l*i-b*o*l*i-j*c*p*i+b*k*p*i+n*g*d*m-f*o*d*m-n*c*h*m+b*o*h*m+f*c*p*m-b*g*p*m-j*g*d*q+f*k*d*q+j*c*h*q-b*k*h*q-f*c*l*q+b*g*l*q},mat4.inverse=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],m=a[10],n=a[11],o=a[12],p=a[13],q=a[14],r=a[15],s=c*h-d*g,t=c*i-e*g,u=c*j-f*g,v=d*i-e*h,w=d*j-f*h,x=e*j-f*i,y=k*p-l*o,z=k*q-m*o,A=k*r-n*o,B=l*q-m*p,C=l*r-n*p,D=m*r-n*q,E=1/(s*D-t*C+u*B+v*A-w*z+x*y);return b[0]=(h*D-i*C+j*B)*E,b[1]=(-d*D+e*C-f*B)*E,b[2]=(p*x-q*w+r*v)*E,b[3]=(-l*x+m*w-n*v)*E,b[4]=(-g*D+i*A-j*z)*E,b[5]=(c*D-e*A+f*z)*E,b[6]=(-o*x+q*u-r*t)*E,b[7]=(k*x-m*u+n*t)*E,b[8]=(g*C-h*A+j*y)*E,b[9]=(-c*C+d*A-f*y)*E,b[10]=(o*w-p*u+r*s)*E,b[11]=(-k*w+l*u-n*s)*E,b[12]=(-g*B+h*z-i*y)*E,b[13]=(c*B-d*z+e*y)*E,b[14]=(-o*v+p*t-q*s)*E,b[15]=(k*v-l*t+m*s)*E,b},mat4.toRotationMat=function(a,b){return b||(b=mat4.create()),b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b[4]=a[4],b[5]=a[5],b[6]=a[6],b[7]=a[7],b[8]=a[8],b[9]=a[9],b[10]=a[10],b[11]=a[11],b[12]=0,b[13]=0,b[14]=0,b[15]=1,b},mat4.toMat3=function(a,b){return b||(b=mat3.create()),b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[4],b[4]=a[5],b[5]=a[6],b[6]=a[8],b[7]=a[9],b[8]=a[10],b},mat4.toInverseMat3=function(a,b){var c=a[0],d=a[1],e=a[2],f=a[4],g=a[5],h=a[6],i=a[8],j=a[9],k=a[10],l=k*g-h*j,m=-k*f+h*i,n=j*f-g*i,o=c*l+d*m+e*n;if(!o)return null;var p=1/o;return b||(b=mat3.create()),b[0]=l*p,b[1]=(-k*d+e*j)*p,b[2]=(h*d-e*g)*p,b[3]=m*p,b[4]=(k*c-e*i)*p,b[5]=(-h*c+e*f)*p,b[6]=n*p,b[7]=(-j*c+d*i)*p,b[8]=(g*c-d*f)*p,b},mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],m=a[9],n=a[10],o=a[11],p=a[12],q=a[13],r=a[14],s=a[15],t=b[0],u=b[1],v=b[2],w=b[3],x=b[4],y=b[5],z=b[6],A=b[7],B=b[8],C=b[9],D=b[10],E=b[11],F=b[12],G=b[13],H=b[14],I=b[15];return c[0]=t*d+u*h+v*l+w*p,c[1]=t*e+u*i+v*m+w*q,c[2]=t*f+u*j+v*n+w*r,c[3]=t*g+u*k+v*o+w*s,c[4]=x*d+y*h+z*l+A*p,c[5]=x*e+y*i+z*m+A*q,c[6]=x*f+y*j+z*n+A*r,c[7]=x*g+y*k+z*o+A*s,c[8]=B*d+C*h+D*l+E*p,c[9]=B*e+C*i+D*m+E*q,c[10]=B*f+C*j+D*n+E*r,c[11]=B*g+C*k+D*o+E*s,c[12]=F*d+G*h+H*l+I*p,c[13]=F*e+G*i+H*m+I*q,c[14]=F*f+G*j+H*n+I*r,c[15]=F*g+G*k+H*o+I*s,c},mat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],f=b[2];return c[0]=a[0]*d+a[4]*e+a[8]*f+a[12],c[1]=a[1]*d+a[5]*e+a[9]*f+a[13],c[2]=a[2]*d+a[6]*e+a[10]*f+a[14],c},mat4.multiplyVec4=function(a,b,c){c||(c=b);var d=b[0],e=b[1],f=b[2],g=b[3];return c[0]=a[0]*d+a[4]*e+a[8]*f+a[12]*g,c[1]=a[1]*d+a[5]*e+a[9]*f+a[13]*g,c[2]=a[2]*d+a[6]*e+a[10]*f+a[14]*g,c[3]=a[3]*d+a[7]*e+a[11]*f+a[15]*g,c},mat4.translate=function(a,b,c){var d=b[0],e=b[1],f=b[2];if(!c||a==c)return a[12]=a[0]*d+a[4]*e+a[8]*f+a[12],a[13]=a[1]*d+a[5]*e+a[9]*f+a[13],a[14]=a[2]*d+a[6]*e+a[10]*f+a[14],a[15]=a[3]*d+a[7]*e+a[11]*f+a[15],a;var g=a[0],h=a[1],i=a[2],j=a[3],k=a[4],l=a[5],m=a[6],n=a[7],o=a[8],p=a[9],q=a[10],r=a[11];return c[0]=g,c[1]=h,c[2]=i,c[3]=j,c[4]=k,c[5]=l,c[6]=m,c[7]=n,c[8]=o,c[9]=p,c[10]=q,c[11]=r,c[12]=g*d+k*e+o*f+a[12],c[13]=h*d+l*e+p*f+a[13],c[14]=i*d+m*e+q*f+a[14],c[15]=j*d+n*e+r*f+a[15],c},mat4.scale=function(a,b,c){var d=b[0],e=b[1],f=b[2];return!c||a==c?(a[0]*=d,a[1]*=d,a[2]*=d,a[3]*=d,a[4]*=e,a[5]*=e,a[6]*=e,a[7]*=e,a[8]*=f,a[9]*=f,a[10]*=f,a[11]*=f,a):(c[0]=a[0]*d,c[1]=a[1]*d,c[2]=a[2]*d,c[3]=a[3]*d,c[4]=a[4]*e,c[5]=a[5]*e,c[6]=a[6]*e,c[7]=a[7]*e,c[8]=a[8]*f,c[9]=a[9]*f,c[10]=a[10]*f,c[11]=a[11]*f,c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15],c)},mat4.rotate=function(a,b,c,d){var e=c[0],f=c[1],g=c[2],h=Math.sqrt(e*e+f*f+g*g);if(!h)return null;h!=1&&(h=1/h,e*=h,f*=h,g*=h);var i=Math.sin(b),j=Math.cos(b),k=1-j,l=a[0],m=a[1],n=a[2],o=a[3],p=a[4],q=a[5],r=a[6],s=a[7],t=a[8],u=a[9],v=a[10],w=a[11],x=e*e*k+j,y=f*e*k+g*i,z=g*e*k-f*i,A=e*f*k-g*i,B=f*f*k+j,C=g*f*k+e*i,D=e*g*k+f*i,E=f*g*k-e*i,F=g*g*k+j;return d?a!=d&&(d[12]=a[12],d[13]=a[13],d[14]=a[14],d[15]=a[15]):d=a,d[0]=l*x+p*y+t*z,d[1]=m*x+q*y+u*z,d[2]=n*x+r*y+v*z,d[3]=o*x+s*y+w*z,d[4]=l*A+p*B+t*C,d[5]=m*A+q*B+u*C,d[6]=n*A+r*B+v*C,d[7]=o*A+s*B+w*C,d[8]=l*D+p*E+t*F,d[9]=m*D+q*E+u*F,d[10]=n*D+r*E+v*F,d[11]=o*D+s*E+w*F,d},mat4.rotateX=function(a,b,c){var d=Math.sin(b),e=Math.cos(b),f=a[4],g=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],m=a[11];return c?a!=c&&(c[0]=a[0],c[1]=a[1],c[2]=a[2],c[3]=a[3],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a,c[4]=f*e+j*d,c[5]=g*e+k*d,c[6]=h*e+l*d,c[7]=i*e+m*d,c[8]=f*-d+j*e,c[9]=g*-d+k*e,c[10]=h*-d+l*e,c[11]=i*-d+m*e,c},mat4.rotateY=function(a,b,c){var d=Math.sin(b),e=Math.cos(b),f=a[0],g=a[1],h=a[2],i=a[3],j=a[8],k=a[9],l=a[10],m=a[11];return c?a!=c&&(c[4]=a[4],c[5]=a[5],c[6]=a[6],c[7]=a[7],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a,c[0]=f*e+j*-d,c[1]=g*e+k*-d,c[2]=h*e+l*-d,c[3]=i*e+m*-d,c[8]=f*d+j*e,c[9]=g*d+k*e,c[10]=h*d+l*e,c[11]=i*d+m*e,c},mat4.rotateZ=function(a,b,c){var d=Math.sin(b),e=Math.cos(b),f=a[0],g=a[1],h=a[2],i=a[3],j=a[4],k=a[5],l=a[6],m=a[7];return c?a!=c&&(c[8]=a[8],c[9]=a[9],c[10]=a[10],c[11]=a[11],c[12]=a[12],c[13]=a[13],c[14]=a[14],c[15]=a[15]):c=a,c[0]=f*e+j*d,c[1]=g*e+k*d,c[2]=h*e+l*d,c[3]=i*e+m*d,c[4]=f*-d+j*e,c[5]=g*-d+k*e,c[6]=h*-d+l*e,c[7]=i*-d+m*e,c},mat4.frustum=function(a,b,c,d,e,f,g){g||(g=mat4.create());var h=b-a,i=d-c,j=f-e;return g[0]=e*2/h,g[1]=0,g[2]=0,g[3]=0,g[4]=0,g[5]=e*2/i,g[6]=0,g[7]=0,g[8]=(b+a)/h,g[9]=(d+c)/i,g[10]=-(f+e)/j,g[11]=-1,g[12]=0,g[13]=0,g[14]=-(f*e*2)/j,g[15]=0,g},mat4.perspective=function(a,b,c,d,e){var f=c*Math.tan(a*Math.PI/360),g=f*b;return mat4.frustum(-g,g,-f,f,c,d,e)},mat4.ortho=function(a,b,c,d,e,f,g){g||(g=mat4.create());var h=b-a,i=d-c,j=f-e;return g[0]=2/h,g[1]=0,g[2]=0,g[3]=0,g[4]=0,g[5]=2/i,g[6]=0,g[7]=0,g[8]=0,g[9]=0,g[10]=-2/j,g[11]=0,g[12]=-(a+b)/h,g[13]=-(d+c)/i,g[14]=-(f+e)/j,g[15]=1,g},mat4.lookAt=function(a,b,c,d){d||(d=mat4.create());var e=a[0],f=a[1],g=a[2],h=c[0],i=c[1],j=c[2],k=b[0],l=b[1],m=b[2];if(e==k&&f==l&&g==m)return mat4.identity(d);var n,o,p,q,r,s,t,u,v,w;return n=e-b[0],o=f-b[1],p=g-b[2],w=1/Math.sqrt(n*n+o*o+p*p),n*=w,o*=w,p*=w,q=i*p-j*o,r=j*n-h*p,s=h*o-i*n,w=Math.sqrt(q*q+r*r+s*s),w?(w=1/w,q*=w,r*=w,s*=w):(q=0,r=0,s=0),t=o*s-p*r,u=p*q-n*s,v=n*r-o*q,w=Math.sqrt(t*t+u*u+v*v),w?(w=1/w,t*=w,u*=w,v*=w):(t=0,u=0,v=0),d[0]=q,d[1]=t,d[2]=n,d[3]=0,d[4]=r,d[5]=u,d[6]=o,d[7]=0,d[8]=s,d[9]=v,d[10]=p,d[11]=0,d[12]=-(q*e+r*f+s*g),d[13]=-(t*e+u*f+v*g),d[14]=-(n*e+o*f+p*g),d[15]=1,d},mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"},quat4={},quat4.create=function(a){var b=new glMatrixArrayType(4);return a&&(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3]),b},quat4.set=function(a,b){return b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b},quat4.calculateW=function(a,b){var c=a[0],d=a[1],e=a[2];return!b||a==b?(a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e)),a):(b[0]=c,b[1]=d,b[2]=e,b[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e)),b)},quat4.inverse=function(a,b){return!b||a==b?(a[0]*=-1,a[1]*=-1,a[2]*=-1,a):(b[0]=-a[0],b[1]=-a[1],b[2]=-a[2],b[3]=a[3],b)},quat4.length=function(a){var b=a[0],c=a[1],d=a[2],e=a[3];return Math.sqrt(b*b+c*c+d*d+e*e)},quat4.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],f=a[3],g=Math.sqrt(c*c+d*d+e*e+f*f);return g==0?(b[0]=0,b[1]=0,b[2]=0,b[3]=0,b):(g=1/g,b[0]=c*g,b[1]=d*g,b[2]=e*g,b[3]=f*g,b)},quat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],f=a[2],g=a[3],h=b[0],i=b[1],j=b[2],k=b[3];return c[0]=d*k+g*h+e*j-f*i,c[1]=e*k+g*i+f*h-d*j,c[2]=f*k+g*j+d*i-e*h,c[3]=g*k-d*h-e*i-f*j,c},quat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],f=b[2],g=a[0],h=a[1],i=a[2],j=a[3],k=j*d+h*f-i*e,l=j*e+i*d-g*f,m=j*f+g*e-h*d,n=-g*d-h*e-i*f;return c[0]=k*j+n*-g+l*-i-m*-h,c[1]=l*j+n*-h+m*-g-k*-i,c[2]=m*j+n*-i+k*-h-l*-g,c},quat4.toMat3=function(a,b){b||(b=mat3.create());var c=a[0],d=a[1],e=a[2],f=a[3],g=c+c,h=d+d,i=e+e,j=c*g,k=c*h,l=c*i,m=d*h,n=d*i,o=e*i,p=f*g,q=f*h,r=f*i;return b[0]=1-(m+o),b[1]=k-r,b[2]=l+q,b[3]=k+r,b[4]=1-(j+o),b[5]=n-p,b[6]=l-q,b[7]=n+p,b[8]=1-(j+m),b},quat4.toMat4=function(a,b){b||(b=mat4.create());var c=a[0],d=a[1],e=a[2],f=a[3],g=c+c,h=d+d,i=e+e,j=c*g,k=c*h,l=c*i,m=d*h,n=d*i,o=e*i,p=f*g,q=f*h,r=f*i;return b[0]=1-(m+o),b[1]=k-r,b[2]=l+q,b[3]=0,b[4]=k+r,b[5]=1-(j+o),b[6]=n-p,b[7]=0,b[8]=l-q,b[9]=n+p,b[10]=1-(j+m),b[11]=0,b[12]=0,b[13]=0,b[14]=0,b[15]=1,b},quat4.slerp=function(a,b,c,d){d||(d=a);var e=a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];if(Math.abs(e)>=1)return d!=a&&(d[0]=a[0],d[1]=a[1],d[2]=a[2],d[3]=a[3]),d;var f=Math.acos(e),g=Math.sqrt(1-e*e);if(Math.abs(g)<.001)return d[0]=a[0]*.5+b[0]*.5,d[1]=a[1]*.5+b[1]*.5,d[2]=a[2]*.5+b[2]*.5,d[3]=a[3]*.5+b[3]*.5,d;var h=Math.sin((1-c)*f)/g,i=Math.sin(c*f)/g;return d[0]=a[0]*h+b[0]*i,d[1]=a[1]*h+b[1]*i,d[2]=a[2]*h+b[2]*i,d[3]=a[3]*h+b[3]*i,d},quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"},WebGLUtils=function(){var a=function(a){return'<table style="background-color: #8CE; width: 100%; height: 100%;"><tr><td align="center"><div style="display: table-cell; vertical-align: middle;"><div style="">'+a+"</div>"+"</div>"+"</td></tr></table>"},b='This page requires a browser that supports WebGL.<br/><a href="http://get.webgl.org" target="_blank">Click here to upgrade your browser.</a>',c='It doesn\'t appear your computer can support WebGL.<br/><a href="http://get.webgl.org/troubleshooting/" target="_blank">Click here for more information.</a>',d=function(d,f){function g(b){var c=d.parentNode;c&&(c.innerHTML=a(b))}if(!window.WebGLRenderingContext)return g(b),null;var h=e(d,f);return h||g(c),h},e=function(a,b){var c=["webgl","experimental-webgl","webkit-3d","moz-webgl"],d=null;for(var e=0;e<c.length;++e){try{d=a.getContext(c[e],b)}catch(f){}if(d)break}return d};return{create3DContext:e,setupWebGL:d}}(),window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a,b){window.setTimeout(a,1e3/60)}}(),function(){function d(b){for(var d in b)a[d]=b[d],c[d]=b[d]}"use strict";var a={version:"1.0"},b={declare:function(a,c){$.each(c,function(c,d){function g(){throw new Error("Property '"+c+"' is read-only.")}if(d===null||d.value===undefined&&!d.get)d={value:d};d.name=c;var e=d.internal||c.substr(0,2)==="__";d.enumerable===undefined&&(d.enumerable=!e);if(d.type===undefined&&!d.get&&!e){var f=typeof d.value;f==="number"?d.type=b.check_number:f==="string"?d.type=b.check_string:d.value instanceof attributeVector?d.type=b.check_attributeVector:d.value instanceof vec&&(d.type=b.check_vec)}if(d.readonly&&d.set||d.onchanged&&d.set||d.value!==undefined&&d.get)throw new Error("Erroneous property definition '"+c+"'.");if(d.get)Object.defineProperty(a,c,{enumerable:d.enumerable,get:d.get,set:d.set||g});else if(!d.type&&!d.onchanged)Object.defineProperty(a,c,{enumerable:d.enumerable,writable:!d.readonly,value:d.value});else{var e="__"+c,h={enumerable:d.enumerable,get:function(){return this[e]}};d.set?h.set=d.set:d.onchanged&&d.type?h.set=function(a){var b=this[e];this[e]=d.type.call(this,a,d),d.onchanged.call(this,b)}:d.onchanged?h.set=function(a){var b=this[e];this[e]=a,d.onchanged.call(this,b)}:d.type&&(h.set=function(a){this[e]=d.type.call(this,a,d)}),Object.defineProperty(a,e,{enumerable:!1,writable:!0,value:d.value}),Object.defineProperty(a,c,h)}})},nullable_attributeVector:function(a,c){return a===null?null:b.check_attributeVector.call(this,a,c)},check_attributeVector:function(a,b){if(a instanceof vec)return new attributeVector(this,a.x,a.y,a.z);throw new Error("Property '"+b.name+"' must be a vec.")},check_vec:function(a,b){if(!a instanceof vec)throw new Error("Property '"+b.name+"' must be a vec.");return a},check_number:function(a,b){return a},check_string:function(a,b){return a}},c=window,e={glowscript:a,property:b,Export:d};d(e)}(),function(){function a(b,c,d){if(!(this instanceof a))return arguments.length==1&&b.x!==undefined?new a(b.x,b.y,b.z):new a(b,c,d);this.x=b,this.y=c,this.z=d;if(arguments.length!==3)throw new Error("vec() requires 3 arguments: x, y, and z.")}function b(a,b,c,d){this.__parent=a,this.__x=b,this.__y=c,this.__z=d,a&&a.__change()}"use strict",b.prototype=new a(0,0,0),b.prototype.constructor=b,Object.defineProperty(b.prototype,"__x",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(b.prototype,"x",{enumerable:!0,get:function(){return this.__x},set:function(a){this.__x=a,this.__parent.__change()}}),Object.defineProperty(b.prototype,"__y",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(b.prototype,"y",{enumerable:!0,get:function(){return this.__y},set:function(a){this.__y=a,this.__parent.__change()}}),Object.defineProperty(b.prototype,"__z",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(b.prototype,"z",{enumerable:!0,get:function(){return this.__z},set:function(a){this.__z=a,this.__parent.__change()}}),a.prototype.toString=function(){var a=[this.x,this.y,this.z],b=[],c,d,e,f,g;for(var h=0;h<3;h++){var c=a[h];if(c==0){b.push("0");continue}Math.abs(c)<1e-4?c=c.toExponential(5):c=c.toPrecision(6),e=c.indexOf(".");if(e>=0){g=c.indexOf("e"),g<0&&(g=c.length),f=g;for(;;){f--;if(c.charAt(f)=="0")continue;if(f==e){b.push(c.slice(0,e).concat(c.slice(g,c.length)));break}g==c.length?b.push(c.slice(0,f+1)):b.push(c.slice(0,f+1).concat(c.slice(g,c.length)));break}}else b.push(c)}return"< "+b[0]+", "+b[1]+", "+b[2]+" >"},a.prototype.add=function(b){return new a(this.x+b.x,this.y+b.y,this.z+b.z)},a.prototype.sub=function(b){return new a(this.x-b.x,this.y-b.y,this.z-b.z)},a.prototype.multiply=function(b){return new a(this.x*b,this.y*b,this.z*b)},a.prototype.divide=function(b){return new a(this.x/b,this.y/b,this.z/b)},a.prototype.mag=function(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)},a.prototype.mag2=function(){return this.x*this.x+this.y*this.y+this.z*this.z},a.prototype.norm=function(){var b=this.mag();return b==0?new a(0,0,0):new a(this.x/b,this.y/b,this.z/b)},a.prototype.dot=function(a){return this.x*a.x+this.y*a.y+this.z*a.z},a.prototype.equals=function(a){return this.x==a.x&&this.y==a.y&&this.z==a.z},a.prototype.proj=function(a){var b=norm(a);return b.multiply(this.dot(b))},a.prototype.comp=function(a){return this.dot(norm(a))},a.prototype.cross=function(b){return new a(this.y*b.z-this.z*b.y,this.z*b.x-this.x*b.z,this.x*b.y-this.y*b.x)},a.prototype.diff_angle=function(a){return Math.acos(this.norm().dot(a.norm()))},a.prototype.rotate=function(b){if(b.angle===undefined)throw new Error("To rotate a vector you must specify angle:..");var c=b.angle,d;b.axis===undefined?d=new a(0,0,1):d=b.axis;var e=this.diff_angle(d);if(e==0)return new a(this.x,this.y,this.z);var d=d.norm(),f=d.multiply(d.dot(this)),g=d.cross(this),h=g.mag();g=g.norm();var i=g.cross(d),j=i.multiply(h*Math.cos(c)).add(g.multiply(h*Math.sin(c)));return f.add(j)},a.random=function(){return new a(-1+2*Math.random(),-1+2*Math.random(),-1+2*Math.random())},String.prototype["+"]=function(a){return this+a},Number.prototype["+"]=function(a){return this+a},Number.prototype["-"]=function(a){return this-a},Number.prototype["*"]=function(a){return a["*r"](this)},Number.prototype["*r"]=function(a){return a*this},Number.prototype["/"]=function(a){return this/a},Number.prototype["-u"]=function(){return-this},a.prototype["+"]=a.prototype.add,a.prototype["-"]=a.prototype.sub,a.prototype["*"]=a.prototype.multiply,a.prototype["*r"]=a.prototype.multiply,a.prototype["/"]=function(a){return this.divide(a)},a.prototype["-u"]=function(){return new a(-this.x,-this.y,-this.z)};var c={vec:a,attributeVector:b};Export(c)}(),function(){function a(a){return a.mag()}function b(a){return a.mag2()}function c(a){return a.norm()}"use strict";var d={mag:a,mag2:b,norm:c};Export(d)}(),function(){function a(){this.pos=[],this.normal=[],this.color=[],this.opacity=[],this.texpos=[],this.bumpaxis=[],this.index=[],this.model_transparent=!1}"use strict",$.extend(a.prototype,{merge:function c(a,b,c){var d=null,e=null,f=null,g=null,h=null,i=null,j=this.pos.length/3;if(b instanceof vertex)if(c<0)this.index.push(j+c);else{if(d===null||b.__pos.x<d)d=b.__pos.x;if(e===null||b.__pos.x>e)e=b.__pos.x;if(f===null||b.__pos.y<f)f=b.__pos.y;if(g===null||b.__pos.y>g)g=b.__pos.y;if(h===null||b.__pos.z<h)h=b.__pos.z;if(i===null||b.__pos.z>i)i=b.__pos.z;this.pos.push(b.__pos.x,b.__pos.y,b.__pos.z),this.normal.push(b.__normal.x,b.__normal.y,b.__normal.z),this.color.push(b.__color.x,b.__color.y,b.__color.z),b.__opacity<1&&(this.model_transparent=!0),this.opacity.push(b.__opacity),this.texpos.push(b.__texpos.x,b.__texpos.y),this.bumpaxis.push(b.__bumpaxis.x,b.__bumpaxis.y,b.__bumpaxis.z),this.index.push(j)}else{var k=[b.__color.x,b.__color.y,b.__color.z];for(var l=0;l<a.pos.length;l++){if(l%3===0){if(d===null||a.pos[l]<d)d=a.pos[l];if(e===null||a.pos[l]>e)e=a.pos[l]}else if(l%3===1){if(f===null||a.pos[l]<f)f=a.pos[l];if(g===null||a.pos[l]>g)g=a.pos[l]}else if(l%3===2){if(h===null||a.pos[l]<h)h=a.pos[l];if(i===null||a.pos[l]>i)i=a.pos[l]}this.pos.push(a.pos[l])}for(var l=0;l<a.normal.length;l++)this.normal.push(a.normal[l]);for(var l=0;l<a.color.length;l++)this.color.push(k[l%3]*a.color[l]);for(var l=0;l<a.opacity.length;l++){var m=b.__opacity*a.opacity[l];m<1&&(this.model_transparent=!0),this.opacity.push(m)}for(var l=0;l<a.texpos.length;l++)this.texpos.push(a.texpos[l]);for(var l=0;l<a.bumpaxis.length;l++)this.bumpaxis.push(a.bumpaxis[l]);for(var l=0;l<a.index.length;l++)this.index.push(j+a.index[l])}return{__xmin:d,__ymin:f,__zmin:h,__xmax:e,__ymax:g,__zmax:i}},transformed:function d(b){var c=mat3.toMat4(mat3.transpose(mat4.toInverseMat3(b))),d=new a;d.index=this.index,d.color=this.color,d.opacity=this.opacity,d.texpos=this.texpos;for(var e=0;e<this.pos.length;e+=3){var f=[this.pos[e],this.pos[e+1],this.pos[e+2]],g=[this.normal[e],this.normal[e+1],this.normal[e+2],0],h=[this.bumpaxis[e],this.bumpaxis[e+1],this.bumpaxis[e+2]];mat4.multiplyVec3(b,f),mat4.multiplyVec4(c,g),mat4.multiplyVec3(b,h),d.pos.push(f[0],f[1],f[2]),d.normal.push(g[0],g[1],g[2]),d.bumpaxis.push(h[0],h[1],h[2])}return d}}),$.extend(a,{makeCube:function(){var b=new a,c=.5;return b.pos.push(+c,+c,+c,+c,-c,+c,+c,-c,-c,+c,+c,-c,-c,+c,-c,-c,-c,-c,-c,-c,+c,-c,+c,+c,-c,-c,+c,-c,-c,-c,+c,-c,-c,+c,-c,+c,-c,+c,-c,-c,+c,+c,+c,+c,+c,+c,+c,-c,-c,+c,+c,-c,-c,+c,+c,-c,+c,+c,+c,+c,+c,+c,-c,+c,-c,-c,-c,-c,-c,-c,+c,-c),b.normal.push(1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1),b.color.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),b.opacity.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),b.texpos.push(0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1),b.bumpaxis.push(0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0),b.index.push(0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23),b},makeQuad:function(){var b=new a;return b.pos.push(-1,-1,0,1,-1,0,1,1,0,-1,1,0),b.normal.push(0,0,1,0,0,1,0,0,1,0,0,1),b.color.push(1,1,1,1,1,1,1,1,1,1,1,1),b.opacity.push(1,1,1,1),b.texpos.push(0,0,1,0,1,1,0,1),b.bumpaxis.push(1,0,0,1,0,0,1,0,0,1,0,0),b.index.push(0,1,2,0,2,3),b},makeCylinder:function(b){var c=50,d=2*Math.PI/c,e=Math.sin(d),f=Math.cos(d),g=0,h=-b,i,j,k=new a;k.pos.push(0,0,0,1,0,0),k.normal.push(-1,0,0,1,0,0),k.color.push(1,1,1,1,1,1),k.opacity.push(1,1,1,1,1,1),k.texpos.push(.5,.5,.5,.5),k.bumpaxis.push(0,0,-1,0,0,-1);for(var l=2;l<=2+4*c;l+=4)k.pos.push(0,g,h,0,g,h,1,g,h,1,g,h),k.normal.push(-1,0,0,0,g,h,1,0,0,0,g,h),k.color.push(1,1,1,1,1,1,1,1,1,1,1,1),k.opacity.push(1,1,1,1),k.texpos.push(.5*(1+h/b),.5+.5*g/b,1-(l-2)/c/4,0,.5*(1-h/b),.5+.5*g/b,1-(l-2)/c/4,1),k.bumpaxis.push(1,0,0,1,0,0,1,0,0,1,0,0),l!=2+4*c&&k.index.push(0,l,l+4,l+1,l+3,l+7,l+1,l+7,l+5,1,l+6,l+2),i=g*f+h*e,j=h*f-g*e,g=i,h=j;return k},makeSphere:function(b,c){var d=c,e=c,f=Math.PI/d,g=2*Math.PI/e,h=Math.sin(f),i=Math.cos(f),j=Math.sin(g),k=Math.cos(g),l=new a,m,n,o,p,q,r,s,t,u,v,w,x;m=b,o=0,q=0;for(w=0;w<d;w++){n=m*i-q*h,p=0,v=r=q*i+m*h;for(x=0;x<=e;x+=1)l.pos.push(m,o,-q),l.normal.push(m/b,o/b,-q/b),l.color.push(1,1,1),l.opacity.push(1),l.texpos.push(1-x/e,1-w/d),l.bumpaxis.push(0,q/b,o/b),u=w*(e+1),x!=e&&l.index.push(u+x,u+x+e+2,u+x+1,u+x,u+x+e+1,u+x+e+2),s=o*k+q*j,t=q*k-o*j,o=s,q=t;m=n,o=0,q=v;if(w==d-1){q=b;for(x=0;x<=e;x+=1)l.pos.push(-b,0,0),l.normal.push(-1,0,0),l.color.push(1,1,1),l.opacity.push(1),l.texpos.push(1-x/e,0),l.bumpaxis.push(0,q/b,o/b),s=o*k+q*j,t=q*k-o*j,o=s,q=t}}return l},makeCone:function(b){var c=100,d=2*Math.PI/c,e=Math.sin(d),f=Math.cos(d),g=1/Math.sqrt(2),h=0,i=-b,j,k,l=new a;l.pos.push(0,0,0),l.normal.push(-1,0,0),l.color.push(1,1,1),l.opacity.push(1),l.texpos.push(.5,.5),l.bumpaxis.push(0,0,1);for(var m=1;m<=1+3*c;m+=3)j=h*f+i*e,k=i*f-h*e,l.pos.push(0,h,i,0,h,i,1,0,0),l.normal.push(-1,0,0,g,g*h,g*i,g,g*(h+j)/2,g*(i+k)/2),l.color.push(1,1,1,1,1,1,1,1,1),l.opacity.push(1,1,1),l.texpos.push(.5*(1+i/b),.5*(1+h/b),1-(m-1)/c/3,0,1-(m-1)/c/3,1),l.bumpaxis.push(0,0,1,0,0,1,0,-i,h),m!=1+3*c&&l.index.push(0,m,m+3,m+1,m+2,m+4),h=j,i=k;return l},makePyramid:function(){var b=new a;return b.pos.push(0,.5,.5,0,.5,-0.5,0,-0.5,-0.5,0,-0.5,.5,0,.5,-0.5,0,.5,.5,1,0,0,0,-0.5,-0.5,0,.5,-0.5,1,0,0,0,-0.5,.5,0,-0.5,-0.5,1,0,0,0,.5,.5,0,-0.5,.5,1,0,0),b.normal.push(-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,2,0,1,2,0,1,2,0,1,0,-2,1,0,-2,1,0,-2,1,-2,0,1,-2,0,1,-2,0,1,0,2,1,0,2,1,0,2),b.color.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),b.opacity.push(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1),b.texpos.push(1,1,0,1,0,0,1,0,0,0,.25,0,.125,1,1,0,.75,0,.875,1,.5,0,.75,0,.625,1,.25,0,.5,0,.375,1),b.bumpaxis.push(0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0),b.index.push(0,1,2,0,2,3,4,5,6,7,8,9,10,11,12,13,14,15),b},makeCurveSegment:function(b){var c=16,d=2*Math.PI/c,e=Math.sin(d),f=Math.cos(d),g=0,h=-b,i,j,k=new a;for(var l=0;l<=2*c;l+=2)k.pos.push(0,g,h,0,0,g,h,1),k.normal.push(0,g,h,0,g,h),k.color.push(1,1,1,1,1,1),k.opacity.push(1,1),k.texpos.push(0,0,0,0),k.bumpaxis.push(0,0,0,0,0,0),l!=2*c&&k.index.push(l,l+2,l+1,l+1,l+2,l+3),i=g*f+h*e,j=h*f-g*e,g=i,h=j;var m=k.pos.length/4,n=a.makeSphere(b,c);for(var l=0;l<(c/2+1)*(c+1);l+=1)k.pos.push(n.pos[3*l],n.pos[3*l+1],n.pos[3*l+2],1),k.normal.push(n.normal[3*l],n.normal[3*l+1],n.normal[3*l+2]),k.color.push(1,1,1),k.opacity.push(1),k.texpos.push(n.texpos[2*l],n.texpos[2*l+1]),k.bumpaxis.push(n.bumpaxis[3*l],n.bumpaxis[3*l+1],n.bumpaxis[3*l+2]);for(var l=0;l<n.index.length/2;l++)k.index.push(n.index[l]+m);return k}});var b={Mesh:a};Export(b)}(),function(){function a(c){if(!(this instanceof a))return new a(c);c||(c={}),a.all.push(this),a.selected=this;for(var d in c)this[d]=c[d];this.events=$("<div/>"),this.wrapper=$("<div/>"),this.title=$("<div/>"),this.caption=$("<div/>"),this.__canvas_element=document.createElement("canvas"),this.__overlay_element=document.createElement("canvas"),this.elements=$([this.__canvas_element,this.__overlay_element]),this.__overlay_objects={objects:[],__changed:!1},this.__visiblePrimitives={},this.lights=[{direction:vec(.21821789,.4364357,.8728715),color:vec(.8,.8,.8)},{direction:vec(-0.872872,-0.218218,-0.436436),color:vec(.3,.3,.3)}],this.trails=[],this.arrows=[],this.__opaque_objects={},this.__transparent_objects={},this.vertex_id=1;var e=100;this.__vertices={Nalloc:e,pos:new Float32Array(3*e),normal:new Float32Array(3*e),color:new Float32Array(3*e),opacity:new Float32Array(e),texpos:new Float32Array(2*e),bumpaxis:new Float32Array(3*e),index:new Uint16Array(e),model_transparent:!1,params:new Float32Array(2*e),object_info:{}},this.__sort_objects={opaque:{plain:{},textures:{},bumpmaps:{},textures_and_bumpmaps:{}},transparent:{plain:{},textures:{},bumpmaps:{},textures_and_bumpmaps:{}}},this.camera=orbital_camera(this),this.mouse=new b(this),this.__range=10,this.__autoscale=!0,this.textures={},this.textures_requested={},this.__changed={},this.__vertex_changed={},this.visible=!0,this.waitfor_textures=!1}function b(a){this.canvas=a}"use strict",property.declare(a.prototype,{__activate:function(){this.__activated=!0,this.__activate=function(){};var b=a.container;this.title.css("white-space","pre").appendTo(b),this.wrapper.addClass("glowscript-canvas-wrapper").appendTo(b),this.caption.css("white-space","pre").appendTo(b),this.wrapper.css("position","relative");var c=this.__canvas_element;c.style.position="absolute";var d=this.__overlay_element;d.style.position="relative",d.style.backgroundColor="transparent",this.__change_size(),this.wrapper.append(c),this.wrapper.append(d),this.__renderer=new WebGLRenderer(this,c,d),this.camera.__activate&&this.camera.__activate(),this.__handleEvents()},__change_size:function(){this.__activated&&(this.__canvas_element.setAttribute("width",this.width),this.__overlay_element.setAttribute("width",this.width),this.__canvas_element.setAttribute("height",this.height),this.__overlay_element.setAttribute("height",this.height))},__handleEvents:function(){var a=this,b=a.elements,c=!1;b.bind("click",function(b){a.mouse.__update(b),b.which==1&&!a.camera.mouse_locked&&a.trigger("click",b)}),b.bind("mousedown",function(b){return b.which==1&&!a.camera.mouse_locked&&
 (c=!0,a.mouse.__update(b),a.trigger("mousedown",b)),b.preventDefault(),b.stopPropagation(),!1}),b.bind("mouseenter mouseleave",function(b){a.mouse.__update(b),a.trigger(b.type,b)}),$(document).bind("mousemove",function(b){a.mouse.__update(b),a.trigger("mousemove",b)}),$(document).bind("mouseup",function(b){a.mouse.__update(b),b.which==1&&c&&(c=!1,a.trigger("mouseup",b))});var d={shift:16,ctrl:17,alt:18};$(document).bind("keydown keyup",function(b){for(var c in d)if(d[c]==b.which){a.mouse[c]=b.type=="keydown";break}})},__change:function(){},waitfor:function(a,b){return a=="textures"&&(this.waitfor_textures=!0),this.events.waitfor(a,b)},pause:function(a,b){if(arguments.length>1)this.__prompt==undefined&&(this.__prompt=label({align:"right",pixel_pos:!0,height:14,color:color.black,background:color.white,opacity:1,box:!1})),this.__prompt.pos=vec(this.__width,this.__height-12,0),this.__prompt.text=a,this.__prompt.visible=!0,this.events.pause(this.__prompt,b);else{this.__draw==undefined&&(this.__draw=draw());var c=this.width-5,d=this.height-20;this.__draw.points=[vec(c,d,0),vec(c-30,d-13,0),vec(c-30,d+15,0),vec(c,d,0)],this.__draw.opacity=1,this.__draw.color=color.black,this.__draw.fillcolor=color.white,this.__draw.visible=!0,this.events.pause(this.__draw,a)}},bind:function(a,b){return this.events.bind(a,b)},unbind:function(a,b){return this.events.unbind(a,b)},one:function(a,b){return this.events.one(a,b)},trigger:function(a,b){var c=new $.Event(a,b);c.stopPropagation(),c.preventDefault(),this.events.trigger(c)},background:new attributeVector(null,0,0,0),ambient:new attributeVector(null,.2,.2,.2),center:new attributeVector(null,0,0,0),forward:new attributeVector(null,0,0,-1),up:new attributeVector(null,0,1,0),__last_forward:null,__activated:!1,userzoom:!0,userspin:!0,fov:60*Math.PI/180,width:{value:640,onchanged:function(){this.__change_size()}},height:{value:400,onchanged:function(){this.__change_size()}},autoscale:{get:function(){return this.__autoscale},set:function(a){this.__autoscale&&!a&&Autoscale.compute_autoscale(this),this.__autoscale=a}},range:{get:function(){return this.__autoscale&&Autoscale.compute_autoscale(this),this.__range},set:function(a){this.__autoscale=!1,this.__range=a}},pixel_to_world:{get:function(){var a=this.__width,b=this.__height,c=2*this.range;return a>=b?c/b:c/a},set:function(a){throw new Error("Cannot assign a value to pixel_to_world.")}},objects:{get:function(){var a=[];for(var b in this.__visiblePrimitives)a.push(this.__visiblePrimitives[b]);for(var b in this.__overlay_objects.objects){var c=this.__overlay_objects.objects[b];c instanceof label&&a.push(c)}return a}}}),property.declare(a,{selected:{get:function(){return window.__context.canvas_selected||null},set:function(a){window.__context.canvas_selected=a}},all:{get:function(){var a=window.__context.canvas_all;return a===undefined&&(a=window.__context.canvas_all=[]),a}},container:{get:function(){return window.__context.glowscript_container||null},set:function(a){window.__context.glowscript_container=$(a)}}}),property.declare(b.prototype,{canvas:null,pos:null,ray:null,__pickx:null,__picky:null,pick:function(){return this.canvas.__renderer.render(1)},project:function(a){if(a.normal===undefined)throw new Error("scene.mouse.project() must specify a normal in {normal:..}");var b=a.normal,c;a.d===undefined&&a.point===undefined?c=b.dot(this.canvas.__center):a.d!==undefined?c=a.d:a.point!==undefined&&(c=b.dot(a.point));var d=b.dot(this.canvas.camera.pos)-c,e=b.dot(this.ray);if(e==0)return null;var f=-d/e;return this.canvas.camera.pos.add(this.ray.multiply(f))},alt:!1,ctrl:!1,shift:!1,__update:function(a){var b=this.canvas,c;b.__width>b.__height?c=2*b.__range/b.__height:c=2*b.__range/b.__width;var d=$(b.__canvas_element).offset();this.__pickx=a.pageX-d.left,this.__picky=b.__height-(a.pageY-d.top);var e=(this.__pickx-b.__width/2)*c,f=(this.__picky-b.__height/2)*c,g=b.__forward.norm().cross(b.__up).norm(),h=g.cross(b.__forward.norm());this.pos=b.__center.add(g.multiply(e).add(h.multiply(f))),this.ray=this.pos.sub(b.camera.pos).norm()}});var c={canvas:a};Export(c)}(),function(){function a(b,c){if(this instanceof a)this.canvas=b,this.follower=null;else return new a(b,c)}"use strict",property.declare(a.prototype,{pos:{get:function(){var a=this.canvas;return a.center.sub(a.forward.norm().multiply(a.range/Math.tan(a.fov/2)))}},mouse_locked:{value:!1,type:null},follow:function(a){this.follower=a},__activate:function(){var a=this.canvas,b=this,c=!1,d=null,e=null,f=null,g=null,h=!1,i=!1,j=!1,k=!1,l=function(b,c){var d=Math.exp(-c*.05);a.range=a.range*d};$(document).bind("contextmenu",function(a){return!c}),a.elements.mousewheel(function(b,c){return a.userzoom&&l(b,c),!1}),a.elements.mousedown(function(f){f.which==1&&(h=!0),f.which==3&&(i=!0),j=a.userspin&&(f.which==3||f.which==1&&a.mouse.ctrl&&!a.mouse.alt),k=a.userzoom&&(f.which==2||f.which==1&&a.mouse.alt&&!a.mouse.ctrl||h&&i);if(j||k)return b.mouse_locked=!0,c=!0,d=f.pageX,e=f.pageY,f.preventDefault(),f.stopPropagation(),!1}),$(document).mousemove(function(b){if(k){var c=e-b.pageY;e=b.pageY,l(b,.1*c)}else if(j){var h=b.pageX-d,c=b.pageY-e;d=b.pageX,e=b.pageY,f+=h*.01,g+=c*.01,g<-1.4&&(g=-1.4),g>1.4&&(g=1.4);var i=a.range/Math.tan(a.fov/2);a.forward=a.forward.rotate({angle:-0.01*h,axis:a.up});var m=a.up.diff_angle(a.forward.multiply(-1)),n=.01*c;n>=m||n<=m-Math.PI||(a.__forward=a.__forward.rotate({angle:-n,axis:a.__forward.cross(a.__up)}))}}),$(document).mouseup(function(a){a.which==1&&(h=!1),a.which==3&&(i=!1),a.which==3&&c&&setTimeout(function(){c=!1},0);if(j||k)return j=k=!1,b.mouse_locked=!1,!1})}});var b={orbital_camera:a};Export(b)}(),function(){function a(){}"use strict",$.extend(a.prototype,{xmin:null,ymin:null,zmin:null,xmax:null,ymax:null,zmax:null,zx_camera:0,zy_camera:0,last_zx_camera:-1,last_zy_camera:-1,find_autocenter:!1,point_extent:function(a,b){this.xmin=Math.min(b.x,this.xmin),this.ymin=Math.min(b.y,this.ymin),this.zmin=Math.min(b.z,this.zmin),this.xmax=Math.max(b.x,this.xmax),this.ymax=Math.max(b.y,this.ymax),this.zmax=Math.max(b.z,this.zmax),a.__xmin=Math.min(b.x,a.__xmin),a.__ymin=Math.min(b.y,a.__ymin),a.__zmin=Math.min(b.z,a.__zmin),a.__xmax=Math.max(b.x,a.__xmax),a.__ymax=Math.max(b.y,a.__ymax),a.__zmax=Math.max(b.z,a.__zmax)}});var b={Autoscale:{compute_autocenter:function c(b){var c=b.__extent;c||(c=b.__extent=new a),c.find_autocenter=!0,c.xmin=null,c.ymin=null,c.zmin=null,c.xmax=null,c.ymax=null,c.zmax=null,c.zx_camera=0,c.zy_camera=0;var d=1/Math.tan(b.__fov/2);c.__cot_hfov=d,c.__centerx=b.center.x,c.__centery=b.center.y,c.__centerz=b.center.z;var e=!1,f;for(var g in b.__visiblePrimitives)f=b.__visiblePrimitives[g],e=!0,b.__changed[f.__id]?f.__get_extent(c):(c.xmin=Math.min(c.xmin,f.__xmin),c.ymin=Math.min(c.ymin,f.__ymin),c.zmin=Math.min(c.zmin,f.__zmin),c.xmax=Math.max(c.xmax,f.__xmax),c.ymax=Math.max(c.ymax,f.__ymax),c.zmax=Math.max(c.zmax,f.__zmax));e&&(b.center=vec((c.xmin+c.xmax)/2,(c.ymin+c.ymax)/2,(c.zmin+c.zmax)/2)),c.find_autocenter=!1},compute_autoscale:function d(b){var c=b.__extent;c||(c=b.__extent=new a);var d=b.center.x,e=b.center.y,f=b.center.z,g=b.__visiblePrimitives;c.zx_camera=0,c.zy_camera=0;var h=1/Math.tan(b.__fov/2);c.__cot_hfov=h,c.__centerx=b.center.x,c.__centery=b.center.y,c.__centerz=b.center.z;var i=!1,j;for(var k in g){j=g[k];if(j.constructor.name=="point")continue;i=!0;if(b.__changed[j.__id]||j.__zx_camera==null||j.__zy_camera==null){j.__get_extent(c);var l=Math.max(Math.abs(j.__xmin-d),Math.abs(j.__xmax-d)),m=Math.max(Math.abs(j.__ymin-e),Math.abs(j.__ymax-e)),n=Math.max(Math.abs(j.__zmin-f),Math.abs(j.__zmax-f));j.__zx_camera=l*h+n,j.__zy_camera=m*h+n}c.zx_camera=Math.max(c.zx_camera,j.__zx_camera),c.zy_camera=Math.max(c.zy_camera,j.__zy_camera)}if(i)if(c.zx_camera>c.last_zx_camera||c.zx_camera<c.last_zx_camera/3||c.zy_camera>c.last_zy_camera||c.zy_camera<c.last_zy_camera/3){var o=c.zx_camera*b.__height/b.__width;o>c.zy_camera?b.__width>=b.__height?b.__range=1.1*(b.__height/b.__width)*c.zx_camera/h:b.__range=1.1*c.zx_camera/h:b.__width>=b.__height?b.__range=1.1*c.zy_camera/h:b.__range=1.1*(b.__width/b.__height)*c.zy_camera/h,c.last_zx_camera=c.zx_camera,c.last_zy_camera=c.zy_camera}},find_extent:function e(a,b){var c=a.__size,d=c.__x,e=c.__y,f=c.__z,g=a.__pos,h=g.__x,i=g.__y,j=g.__z,k=a.__hasPosAtCenter,l;k?l=Math.sqrt(d*d+e*e+f*f)/2:l=Math.sqrt(d*d+e*e/4+f*f/4);if(!b.find_autocenter){var m=h-b.__centerx,n=i-b.__centery,o=j-b.__centerz,p=(Math.abs(m)+l)*b.__cot_hfov+Math.abs(o)+l,q=(Math.abs(n)+l)*b.__cot_hfov+Math.abs(o)+l;if(p<b.zx_camera&&q<b.zy_camera){a.__zx_camera=null,a.__zy_camera=null;return}}var r=a.__axis.norm(),s=a.__up.norm();k&&(g=g.sub(r.multiply(d/2)));var t=r.multiply(d),u=r.cross(s).norm();u.dot(u)<1e-10&&(u=r.cross(vec(1,0,0)).norm(),u.dot(u)<1e-10&&(u=r.cross(vec(0,1,0)).norm()));var v=u.cross(r),w=g.add(v.multiply(-e/2).add(u.multiply(-f/2))),x=w.add(v.multiply(e)),y=w.add(u.multiply(f)),z=x.add(u.multiply(f)),A=w.add(t),B=x.add(t),C=y.add(t),D=z.add(t);b.point_extent(a,w),b.point_extent(a,x),b.point_extent(a,y),b.point_extent(a,z),b.point_extent(a,A),b.point_extent(a,B),b.point_extent(a,C),b.point_extent(a,D)}}};Export(b)}(),function(){function o(o,p,q){function L(a){J[a]=s.createTexture(),s.bindTexture(s.TEXTURE_2D,J[a]),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_S,s.CLAMP_TO_EDGE),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_WRAP_T,s.CLAMP_TO_EDGE),s.texImage2D(s.TEXTURE_2D,0,s.RGBA,H,I,0,s.RGBA,s.UNSIGNED_BYTE,null),s.bindTexture(s.TEXTURE_2D,null)}function P(a,b,c){function d(a,b,c){var d=c.createShader(b);c.shaderSource(d,a),c.compileShader(d);if(!c.getShaderParameter(d,c.COMPILE_STATUS))throw alert(c.getShaderInfoLog(d)),new Error("Shader compile error");return d}var e=d(b,c.VERTEX_SHADER,c),f=d(a,c.FRAGMENT_SHADER,c),g=c.createProgram();c.attachShader(g,e),c.attachShader(g,f),c.linkProgram(g);if(!c.getProgramParameter(g,c.LINK_STATUS))throw alert(c.getProgramInfoLog(g)),new Error("Shader link error");var h=c.getProgramParameter(g,c.ACTIVE_UNIFORMS);g.uniforms={};for(var i=0;i<h;i++){var j=c.getActiveUniform(g,i),k=j.name;k.substring(k.length-3)=="[0]"&&(k=k.substring(0,k.length-3)),g.uniforms[k]=c.getUniformLocation(g,k)}var l=c.getProgramParameter(g,c.ACTIVE_ATTRIBUTES);g.attributes={};for(var i=0;i<l;i++){var j=c.getActiveAttrib(g,i);g.attributes[j.name]=c.getAttribLocation(g,j.name)}return g}function V(a,b,c){var d,e,f;c?(d=b.__tex.bumpmap,f=b.__tex.bumpmap_ref,e=b.__tex.bumpmap_t0):(d=b.__tex.file,f=b.__tex.texture_ref,e=b.__tex.texture_t0);var g=(new Date).getTime();g=g-e,d in o.textures?f.reference=o.textures[d]:(o.textures[d]=f.reference=s.createTexture(),s.bindTexture(s.TEXTURE_2D,f.reference),s.pixelStorei(s.UNPACK_FLIP_Y_WEBGL,!0),s.texImage2D(s.TEXTURE_2D,0,s.RGBA,s.RGBA,s.UNSIGNED_BYTE,a),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MAG_FILTER,s.LINEAR),s.texParameteri(s.TEXTURE_2D,s.TEXTURE_MIN_FILTER,s.LINEAR_MIPMAP_NEAREST),s.generateMipmap(s.TEXTURE_2D),s.bindTexture(s.TEXTURE_2D,null));if(d in o.textures_requested){var h=o.textures_requested[d];while(h.length>0){var i=h.pop();i[1]?i[0].__tex.bumpmap_ref.reference=f.reference:i[0].__tex.texture_ref.reference=f.reference,i[0].__change()}}}function ba(){window.requestAnimFrame(ba,p);var a=(new Date).getTime()*.001,b=a-Z;Z=a,o.trigger("redraw",{dt:b});var c=(new Date).getTime()*.001;r.render(!1);var a=(new Date).getTime()*.001,b=a-Y;Y=a,X=X*.95+(a-c)*1e3*.05,W=W*.95+1/(b+.001)*.05,$("#fps").text(W.toFixed(1)+" fps; "+X.toFixed(1)+" ms render"),o.trigger("draw_complete",{dt:b}),o.__last_forward=o.__forward,o.__last_range=o.__range}var r=this,s=WebGLUtils.setupWebGL(p);if(!s)throw new Error("Can't create canvas: WebGL not supported");o.overlay_context=q.getContext("2d");var t=null,u=null,v=null,w=null,x=null,y=null,z=null,A=null,B=null,C=null,D=null,E=null,F=null,G=1,H=G*o.__width,I=G*o.__height,J={C0:null,D0:null,D1:null,D2:null,C1:null,C2:null,C3:null,EXTENT_TEXTURE:null},K={C0:s.TEXTURE2,D0:s.TEXTURE3,D1:s.TEXTURE4,D2:s.TEXTURE5,C1:s.TEXTURE6,C2:s.TEXTURE7,C3:s.TEXTURE8,EXTENT_TEXTURE:s.TEXTURE9};for(var M in J)L(M);var N=s.createFramebuffer();s.bindFramebuffer(s.FRAMEBUFFER,N);var O=s.createRenderbuffer();s.bindRenderbuffer(s.RENDERBUFFER,O),s.renderbufferStorage(s.RENDERBUFFER,s.DEPTH_COMPONENT16,H,I),s.framebufferRenderbuffer(s.FRAMEBUFFER,s.DEPTH_ATTACHMENT,s.RENDERBUFFER,O),s.bindRenderbuffer(s.RENDERBUFFER,null),s.bindFramebuffer(s.FRAMEBUFFER,null);var Q=function(a,b){b?this.dynamism=s.DYNAMIC_DRAW:this.dynamism=s.STATIC_DRAW,this.elementType=s.TRIANGLES,this.mesh=a,this.model_transparent=a.model_transparent,this.pos=new Float32Array(a.pos),this.normal=new Float32Array(a.normal),this.color=new Float32Array(a.color),this.opacity=new Float32Array(a.opacity),this.texpos=new Float32Array(a.texpos),this.bumpaxis=new Float32Array(a.bumpaxis),a.params!==undefined&&(this.params=new Float32Array(a.params)),this.index=new Uint16Array(a.index),this.posBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.posBuffer),s.bufferData(s.ARRAY_BUFFER,this.pos,this.dynamism),this.normalBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.normalBuffer),s.bufferData(s.ARRAY_BUFFER,this.normal,this.dynamism),this.colorBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.colorBuffer),s.bufferData(s.ARRAY_BUFFER,this.color,this.dynamism),this.opacityBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.opacityBuffer),s.bufferData(s.ARRAY_BUFFER,this.opacity,this.dynamism),this.texposBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.texposBuffer),s.bufferData(s.ARRAY_BUFFER,this.texpos,this.dynamism),this.bumpaxisBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.bumpaxisBuffer),s.bufferData(s.ARRAY_BUFFER,this.bumpaxis,this.dynamism),a.params!==undefined&&(this.paramsBuffer=s.createBuffer(),s.bindBuffer(s.ARRAY_BUFFER,this.paramsBuffer),s.bufferData(s.ARRAY_BUFFER,this.params,this.dynamism)),this.indexBuffer=s.createBuffer(),s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,this.indexBuffer),s.bufferData(s.ELEMENT_ARRAY_BUFFER,this.index,this.dynamism)},R={box:new Q(Mesh.makeCube(),!1),pyramid:new Q(Mesh.makePyramid(),!1),cylinder:new Q(Mesh.makeCylinder(.5),!1),cone:new Q(Mesh.makeCone(.5),!1),sphere:new Q(Mesh.makeSphere(.5,30),!1),triangle:new Q(o.__vertices,!0),quad:new Q(Mesh.makeQuad(),!1),curve:new Q(Mesh.makeCurveSegment(1),!1)},S=this.models={};for(var T in R)S[T]=R[T];this.add_model=function(a,b){var c=a.__mesh_id;S[c]=R[c]=new Q(a,b),S[c].id_object={}},this.screenshot=function(a){o.waitfor("draw_complete",function(b){var c=new Image;c.src=p.toDataURL(),a(b,c)})},this.reset=function(){for(var a in R)R[a].id_object={}};var U={target:vec3.create([0,0,0]),up:vec3.create([0,1,0]),fovy:60,angleX:0,angleY:0,distance:1};this.reset(),this.initTexture=function(a,b,c){c?b.__tex.bumpmap=a:b.__tex.file=a;if(a in o.textures){c?b.__tex.bumpmap_ref.reference=o.textures[a]:b.__tex.texture_ref.reference=o.textures[a];return}if(a in o.textures_requested){o.textures_requested[a].push([b,c]);return}o.textures_requested[a]=[[b,c]];var d=(new Date).getTime();c?b.__tex.bumpmap_t0=d:b.__tex.texture_t0=d;var e=new Image;e.crossOrigin="anonymous",e.src=a,e.onload=function(){V(e,b,c)}};var W=0,X=0,Y=(new Date).getTime()*.001,Z=(new Date).getTime()*.001,_=0;this.render=function(q){function bs(a,b,c){var d=a[b];d===undefined&&(d=a[b]=[c]),bh==3?d.push(c.v0.__id,c.v1.__id,c.v2.__id):d.push(c.v0.__id,c.v1.__id,c.v2.__id,c.v0.__id,c.v2.__id,c.v3.__id)}function bD(b,c){bB=b,s.useProgram(b),s.enableVertexAttribArray(b.attributes.pos),(q==n||c>e)&&s.uniform2fv(b.uniforms.canvas_size,bC);if(c!=n){if(q==a||c==e||c>=j)s.uniform1i(b.uniforms.light_count,Z),s.uniform4fv(b.uniforms.light_pos,X),s.uniform3fv(b.uniforms.light_color,Y),s.uniform3fv(b.uniforms.light_ambient,bc),s.enableVertexAttribArray(b.attributes.normal),b!=u&&(s.enableVertexAttribArray(b.attributes.color),s.enableVertexAttribArray(b.attributes.opacity),s.enableVertexAttribArray(b.attributes.texpos),s.enableVertexAttribArray(b.attributes.bumpaxis),s.uniform1i(b.uniforms.texmap,0),s.uniform1i(b.uniforms.bumpmap,1));s.uniformMatrix4fv(b.uniforms.viewMatrix,!1,V),s.uniformMatrix4fv(b.uniforms.projMatrix,!1,T),c>=f&&s.uniform1i(b.uniforms.minormode,c),(b==y||b==v)&&s.enableVertexAttribArray(b.attributes.params)}c==n?(s.uniform1i(b.uniforms.C0,2),s.uniform1i(b.uniforms.C1,6),s.uniform1i(b.uniforms.C2,7),s.uniform1i(b.uniforms.C3,8),s.uniform1i(b.uniforms.D2,5)):c>f&&(s.uniform1i(b.uniforms.D0,3),c>g&&s.uniform1i(b.uniforms.D1,4),c>h&&s.uniform1i(b.uniforms.D2,5))}function bE(g,h,j){function m(){var d=R.curve,f=d.id_object,h=d.elementType,i=d.index.length,j=!0;for(var k in f){if(g>e)break;if(j){q==a||g==e?(u==null&&(u=P(shaders.opaque_render_fragment,shaders.curve_render_vertex,s)),bD(u,g)):q==b&&(B==null&&(B=P(shaders.pick_fragment,shaders.curve_pick_vertex,s)),bD(B,g)),s.bindBuffer(s.ARRAY_BUFFER,d.posBuffer),s.vertexAttribPointer(bB.attributes.pos,4,s.FLOAT,!1,0,0),q!=b&&(s.bindBuffer(s.ARRAY_BUFFER,d.normalBuffer),s.vertexAttribPointer(bB.attributes.normal,3,s.FLOAT,!1,0,0)),s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,d.indexBuffer),q==c&&(h=s.POINTS);var l=new Array(4);j=!1}var m=f[k],n=m.__points,o=n.length;s.uniform4fv(bB.uniforms.objectData,m.__data);for(var p=1;p<o;p++){var r=n[p];if(!r.visible)continue;var t=r.__prevsegment;if(q==b){var v=r.__falsecolor;for(var w=0;w<4;w++)l[w]=t[8+w],l[w+4]=t[12+w],t[12+w]=t[8+w]=v[w]}s.uniform4fv(bB.uniforms.segmentData,t),s.drawElements(h,i,s.UNSIGNED_SHORT,0);if(q==b)for(var w=0;w<8;w++)t[8+w]=l[w]}}}function p(){var h=R.triangle,j=h.elementType,k=o.__vertices;if(q!=b)f<=g&&g<=i?(z==null&&(z=P(shaders.peel_depth_fragment,shaders.tri_render_vertex,s)),bD(z,g)):g>e?(y==null&&(y=P(shaders.peel_color_fragment,shaders.tri_render_vertex,s)),bD(y,g)):q==a||q==d?(v==null&&(v=P(shaders.opaque_render_fragment,shaders.tri_render_vertex,s)),bD(v,g)):q==c&&(D==null&&(D=P(shaders.pick_fragment,shaders.extent_vertex,s)),bD(D,g));else{C==null&&(C=P(shaders.pick_fragment,shaders.tri_pick_vertex,s)),bD(C,g),k={},k.pos=new Float32Array(bt.pos),k.color=new Float32Array(bt.color),u=new Uint16Array(bt.index),w=u.length,s.bindBuffer(s.ARRAY_BUFFER,h.posBuffer),s.bufferData(s.ARRAY_BUFFER,k.pos,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.pos,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.colorBuffer),s.bufferData(s.ARRAY_BUFFER,k.color,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.color,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,h.indexBuffer),s.bufferData(s.ELEMENT_ARRAY_BUFFER,u,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.color,4,s.FLOAT,!1,0,0),s.drawElements(j,w,s.UNSIGNED_SHORT,0),s.bindBuffer(s.ARRAY_BUFFER,h.posBuffer),s.bufferData(s.ARRAY_BUFFER,o.__vertices.pos,s.DYNAMIC_DRAW),s.bindBuffer(s.ARRAY_BUFFER,h.colorBuffer),s.bufferData(s.ARRAY_BUFFER,o.__vertices.color,s.DYNAMIC_DRAW);return}s.bindBuffer(s.ARRAY_BUFFER,h.posBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.pos,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.pos,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.colorBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.color,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.color,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.normalBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.normal,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.normal,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.opacityBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.opacity,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.opacity,1,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.texposBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.texpos,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.texpos,2,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.bumpaxisBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.bumpaxis,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.bumpaxis,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,h.paramsBuffer),_&&s.bufferData(s.ARRAY_BUFFER,k.params,s.DYNAMIC_DRAW),s.vertexAttribPointer(bB.attributes.params,2,s.FLOAT,!1,0,0),_=0;var l=o.__sort_objects;for(var m in l){if(g>f){if(m=="opaque")continue}else if(m=="transparent")continue;for(var n in l[m])for(var p in l[m][n]){var r=l[m][n][p],t=r[0],u=new Uint16Array(r.slice(1)),w=u.length;s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,h.indexBuffer),s.bufferData(s.ELEMENT_ARRAY_BUFFER,u,s.DYNAMIC_DRAW),q==c&&(j=s.POINTS);var x=0,A=0;if(n=="textures"){if((q==a||q==d)&&t.__tex.file!==null)if(t.__tex.texture_ref.reference!==null)s.activeTexture(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,t.__tex.texture_ref.reference),x=1;else continue}else if(n=="bumpmaps"){if((q==a||q==d)&&t.__tex.bumpmap!==null)if(t.__tex.bumpmap_ref.reference!==null)s.activeTexture(s.TEXTURE1),s.bindTexture(s.TEXTURE_2D,t.__tex.bumpmap_ref.reference),A=1;else continue}else if(n=="textures_and_bumpmaps"){if((q==a||q==d)&&t.__tex.file!==null)if(t.__tex.texture_ref.reference!==null)s.activeTexture(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,t.__tex.texture_ref.reference),x=1;else continue;if((q==a||q==d)&&t.__tex.bumpmap!==null)if(t.__tex.bumpmap_ref.reference!==null)s.activeTexture(s.TEXTURE1),s.bindTexture(s.TEXTURE_2D,t.__tex.bumpmap_ref.reference),A=1;else continue}s.uniform1f(bB.uniforms.T,x),s.uniform1f(bB.uniforms.B,A),s.drawElements(j,w,s.UNSIGNED_SHORT,0)}}}function r(){var a=R.quad,b=a.elementType,c=a.index.length;F==null&&(F=P(shaders.merge_fragment,shaders.merge_vertex,s)),bD(F,g),s.bindBuffer(s.ARRAY_BUFFER,a.posBuffer),s.vertexAttribPointer(bB.attributes.pos,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,a.indexBuffer),s.drawElements(b,c,s.UNSIGNED_SHORT,0)}if(q==d)for(var k in j){var l=j[k];if(l==h)continue;s.activeTexture(K[l]),s.bindTexture(s.TEXTURE_2D,J[l])}h===null?s.bindFramebuffer(s.FRAMEBUFFER,null):(s.bindFramebuffer(s.FRAMEBUFFER,N),s.framebufferTexture2D(s.FRAMEBUFFER,s.COLOR_ATTACHMENT0,s.TEXTURE_2D,J[h],0)),s.viewport(0,0,H,I),s.enable(s.DEPTH_TEST),q==b||g>e?s.clearColor(0,0,0,0):q==c?s.clearColor(0,0,0,1):s.clearColor(o.__background.x,o.__background.y,o.__background.z,1),q==c?(s.depthFunc(s.GREATER),s.clearDepth(0)):(s.depthFunc(s.LEQUAL),s.clearDepth(1)),s.clear(s.COLOR_BUFFER_BIT|s.DEPTH_BUFFER_BIT),bu&&p();for(var E in R){if(E=="quad"||E=="triangle")continue;if(E=="curve"){m();continue}if(g>=n){r();break}var G=R[E],L=G.elementType,M=G.index.length,O;if(g>f){if(o.__transparent_objects[E]===undefined)continue;O=o.__transparent_objects[E]}else{if(o.__opaque_objects[E]===undefined)continue;O=o.__opaque_objects[E]}var Q=!0;for(var S in O){var T=O[S],U=T.__data;if(Q){f<=g&&g<=i?(w==null&&(w=P(shaders.peel_depth_fragment,shaders.peel_depth_vertex,s)),bD(w,g)):g>e?(x==null&&(x=P(shaders.peel_color_fragment,shaders.render_vertex,s)),bD(x,g)):q==a||g==e?(t==null&&(t=P(shaders.opaque_render_fragment,shaders.render_vertex,s)),bD(t,g)):q==b?(A==null&&(A=P(shaders.pick_fragment,shaders.pick_vertex,s)),bD(A,g)):q==c&&(D==null&&(D=P(shaders.pick_fragment,shaders.extent_vertex,s)),bD(D,g)),s.bindBuffer(s.ARRAY_BUFFER,G.posBuffer),s.vertexAttribPointer(bB.attributes.pos,3,s.FLOAT,!1,0,0),q!=b&&!(f<=g&&g<=i)&&(s.bindBuffer(s.ARRAY_BUFFER,G.normalBuffer),s.vertexAttribPointer(bB.attributes.normal,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,G.colorBuffer),s.vertexAttribPointer(bB.attributes.color,3,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,G.opacityBuffer),s.vertexAttribPointer(bB.attributes.opacity,1,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,G.texposBuffer),s.vertexAttribPointer(bB.attributes.texpos,2,s.FLOAT,!1,0,0),s.bindBuffer(s.ARRAY_BUFFER,G.bumpaxisBuffer),s.vertexAttribPointer(bB.attributes.bumpaxis,3,s.FLOAT,!1,0,0)),s.bindBuffer(s.ELEMENT_ARRAY_BUFFER,G.indexBuffer),q==c&&(L=s.POINTS);var V=new Array(4);Q=!1}if(g<n&&q!=b){if((q==a||q==d)&&T.__tex.file!==null)if(T.__tex.texture_ref.reference!==null)s.activeTexture(s.TEXTURE0),s.bindTexture(s.TEXTURE_2D,T.__tex.texture_ref.reference);else continue;if((q==a||q==d)&&T.__tex.bumpmap!==null)if(T.__tex.bumpmap_ref.reference!==null)s.activeTexture(s.TEXTURE1),s.bindTexture(s.TEXTURE_2D,T.__tex.bumpmap_ref.reference);else continue}if(q==b){var W=T.__falsecolor;for(var k=0;k<4;k++)V[k]=U[16+k],U[16+k]=W[k]}s.uniform4fv(bB.uniforms.objectData,U),s.drawElements(L,M,s.UNSIGNED_SHORT,0);if(q==b)for(var k=0;k<4;k++)U[16+k]=V[k]}}}if(q==a){var r=o.objects;if(o.waitfor_textures){for(var E in r){var G=r[E];if(G.__tex===undefined)continue;if(!G.ready)return}o.waitfor_textures=!1,o.trigger("textures",null)}}if(!o.visible){if(q==a)return;return null}o.autoscale&&Autoscale.compute_autoscale(o),o.camera.follower!==null&&(o.__center=typeof o.camera.follower!="function"?o.camera.follower.pos:o.camera.follower());if(q==a){for(var L in o.arrows){var M=o.arrows[L];if(!M.run)continue;if(M.obj!==undefined)if(M.obj.pos!==undefined)M.arrow.pos=M.obj.pos;else continue;if(M.last_pos!==null&&O.equals(M.last_pos))continue;M.arrow.visible||(M.arrow.visible=!0),M.arrow.axis_and_length=M.obj[M.attr].multiply(M.scale)}for(var L in o.trails){var O,M=o.trails[L];if(!M.run)continue;if(typeof M.obj!="function"){if(M.obj!==undefined)if(M.obj.pos!==undefined)O=M.obj.pos;else continue}else O=M.obj();if(M.last_pos!==null&&O.equals(M.last_pos))continue;if(M.pps>0){var Q=(new Date).getTime();M.last_time===null&&(M.last_time=Q);if(Q-M.last_time>1e3/M.pps)M.last_time=Q;else if(Q!=M.last_time)continue}if(M.retain>0&&M.elements==M.retain)if(M.type=="curve")M.curve.shift(),M.curve.push(O);else{for(var L=0;L<M.retain-1;L++)M.spheres[L].pos=M.spheres[L+1].pos;M.spheres[M.retain-1].pos=O}else M.type=="curve"?M.curve.push(O):(M.options.pos=O,M.spheres.push(sphere(M.options))),M.elements+=1;M.last_pos=vec(O)}}U.target=vec3.create([o.__center.x,o.__center.y,o.__center.z]),U.up=vec3.create([o.__up.x,o.__up.y,o.__up.z]),U.fovy=o.__fov*180/Math.PI;var S=vec(o.__forward.x,0,o.__forward.z).norm();U.angleX=Math.atan2(S.x,-S.z),U.angleY=Math.PI/2-Math.acos(-o.__forward.norm().y),p.clientWidth>=p.clientHeight?U.distance=o.__range/Math.tan(o.__fov/2):U.distance=o.__range*(p.clientHeight/p.clientWidth)/Math.tan(o.__fov/2),U.pos=mat4.multiplyVec3(mat4.rotateX(mat4.rotateY(mat4.identity(mat4.create()),-U.angleX),-U.angleY),vec3.create([0,0,U.distance])),U.pos=vec3.create([o.__center.x+U.pos[0],o.__center.y+U.pos[1],o.__center.z+U.pos[2]]),U.zNear=U.distance/100,U.zFar=U.distance*10;var T=mat4.perspective(U.fovy,p.clientWidth/p.clientHeight,U.zNear,U.zFar),V=mat4.lookAt(U.pos,U.target,U.up),W=8,X=new Float32Array(W*4),Y=new Float32Array(W*3),Z=Math.min(o.lights.length,W);for(var L=0;L<Z;L++){var $=o.lights[L];if($.direction===undefined)var ba=[$.pos.x,$.pos.y,$.pos.z,1];else var ba=[$.direction.x,$.direction.y,$.direction.z,0];$.transformed=ba,mat4.multiplyVec4(V,ba);for(var bb=0;bb<4;bb++)X[L*4+bb]=ba[bb];Y[L*3]=$.color.x,Y[L*3+1]=$.color.y,Y[L*3+2]=$.color.z}var bc=new Float32Array(3);bc[0]=o.ambient.x,bc[1]=o.ambient.y,bc[2]=o.ambient.z;if(q==a&&o.__overlay_objects.objects.length>0&&(o.__overlay_objects.__changed||!o.__forward.equals(o.__last_forward)||o.__range!=o.__last_range)){o.__overlay_objects.__changed=!1;var bd=o.overlay_context;bd.clearRect(0,0,o.__width,o.__height);for(var L=0;L<o.__overlay_objects.objects.length;L++){var G=o.__overlay_objects.objects[L];if(!G.visible)continue;G.__update(bd,U)}}var be={pos:3,normal:3,color:3,opacity:1,texpos:2,bumpaxis:3},bb=o.__vertices;for(var bf in o.__vertex_changed){var bg=o.__vertex_changed[bf],bh=bg.__id;_++,bb.params[2*bh]=bg.__shininess,bb.params[2*bh+1]=bg.__emissive;for(var bi in be){var bj=bg["__"+bi];be[bi]==1?bb[bi][bh]=bj:be[bi]==2?(bb[bi][2*bh]=bj.x,bb[bi][2*bh+1]=bj.y):(bb[bi][3*bh]=bj.x,bb[bi][3*bh+1]=bj.y,bb[bi][3*bh+2]=bj.z)}}o.__vertex_changed={};for(var bf in o.__changed)o.__changed[bf].__update(),delete o.__changed[bf];for(var bf in o.__changed)o.__changed[bf].__update();o.__changed={};for(var bk in R)o.__opaque_objects[bk]={},o.__transparent_objects[bk]={};var bl=!1;for(var bk in R){if(bk=="triangle"||bk=="quad"||bk=="point")continue;var bm=R[bk],bn=bm.id_object;for(var bf in bn){var G=bn[bf];if(bk=="curve"){o.__opaque_objects[bk][bf]=G;continue}var bo=G.__data;q==a&&(bo[19]<1||bm.model_transparent)?(o.__transparent_objects[bk][bf]=G,bl=!0):o.__opaque_objects[bk][bf]=G}}var bp=o.__sort_objects;for(var bq in bp)for(var M in bp[bq])bp[bq][M]={};var bh,br=["v0","v1","v2","v3"],bt={pos:[],color:[],index:[]},bu=!1,bm;for(var bk=0;bk<2;bk++){bk===0?(bh=3,bm=R.triangle):(bh=4,bm=R.quad);var bn=bm.id_object;for(var bf in bn){bu=!0;var G=bn[bf];if(q==b){var bv=G.__falsecolor,bw;for(var L=0;L<3;L++)bw=G[br[L]].pos,bt.pos.push(bw.x,bw.y,bw.z),bt.color.push(bv[0],bv[1],bv[2],bv[3]),bt.index.push(bt.index.length);if(bh==4){var bx=[0,2,3];for(var by in bx){var L=bx[by];bw=G[br[L]].pos,bt.pos.push(bw.x,bw.y,bw.z),bt.color.push(bv[0],bv[1],bv[2],bv[3]),bt.index.push(bt.index.length)}}}else if(q==a){var bz=!0;for(var L=0;L<bh;L++)if(G[br[L]].opacity<1){bz=!1;break}var bi=G.__tex.file,bA=G.__tex.bumpmap;bz?bi!==null?(bs(bp.opaque.textures,bi,G),bA!=null&&bs(bp.opaque.textures_and_bumpmaps,bA,G)):bA!=null?bs(bp.opaque.bumpmaps,bA,G):bs(bp.opaque.plain,"all",G):(bl=!0,bi!==null?(bs(bp.transparent.textures,bi,G),bA!=null&&bs(bp.transparent.textures_and_bumpmaps,bA,G)):bA!=null?bs(bp.transparent.bumpmaps,bA,G):bs(bp.transparent.plain,"all",G))}}}bl&&(q=d);var bB,bC=new Float32Array(2);bC[0]=H,bC[1]=I,q==a?bE(q,null,0,[]):q==b?bE(q,"C0",0,[]):q==c?bE(q,"EXTENT_TEXTURE",0,[]):q==d&&(bE(e,"C0",[]),bE(f,"D0",[]),bE(j,"C1",["D0"]),bE(g,"D1",["D0"]),bE(k,"C2",["D0","D1"]),bE(h,"D2",["D0","D1"]),bE(l,"C3",["D0","D2"]),bE(i,"D1",["D0","D2"]),bE(m,"D2",["D0","D1"]),bE(n,null,["C0","C1","C2","C3","D2"]));if(q==c){var bF=new Uint8Array(4);s.readPixels(0,0,1,1,s.RGBA,s.UNSIGNED_BYTE,bF);var bG=(256*bF[1]+bF[2])/65536,bH=bF[0];bH>128&&(bH=-(bH-128),bG=-bG);var bI;return bG==0&&bH==0?bI=0:bI=Math.exp(bG+bH),null}if(q==b){var bF=new Uint8Array(4);s.readPixels(o.mouse.__pickx,o.mouse.__picky,1,1,s.RGBA,s.UNSIGNED_BYTE,bF);var bf=16777216*bF[0]+65536*bF[1]+256*bF[2]+bF[3],G=o.__visiblePrimitives[bf];if(G===undefined)return null;if(G.constructor.name=="point"){if(!G.__curve.pickable||!G.pickable)return null;var bJ=G.__curve.__points,bK=bJ.length;for(var L=0;L<bK;L++)if(bJ[L]===G)return G=G.__curve,G.pick=L,G;return null}return G.pickable?G:null}},this.reset(),ba()}"use strict";var a=0,b=1,c=2,d=3,e=4,f=5,g=6,h=7,i=8,j=9,k=10,l=11,m=12,n=13,p={WebGLRenderer:o};Export(p)}(),function(){function a(a){return Math.log(a)/Math.LN10}function e(e,f){if(f.ticks.length==0){var g=f.tickSize,h=f.min,i=f.max,j=Math.floor((i-h)/g+.5)+1,k,l;for(var m=0;m<j;m++){l=abs(h+m*g);if(k===undefined||l>k&&l!=0)k=l}d=Math.floor(a(k))+1;var n=Math.floor(a(g))+1;n>3?(b=!0,c=n):n>0?(b=!1,c=0):n<0?(b=!0,c=n,d>=0&&(b=!1,c=-n+1)):(b=!1,c=1)}if(e==0)return"0";if(b){var o,p,q=e*pow(10,-c+1);return o=0,p=c-1,d>c&&(q*=.1,o+=1,p+=1),q.toFixed(o)+"e"+p}return e.toFixed(c)}function f(b){if(!(this instanceof f))return new f(b);b===undefined&&(b={}),this.graph_options={series:{shadowSize:0},crosshair:{mode:"xy",color:"rgba(0,0,0,1)"}},this.__width=640,this.__height=200,this.__xmin=this.__xmax=this.__ymin=this.__ymax=null,b.width!==undefined&&(this.__width=b.width,delete b.width),b.height!==undefined&&(this.__height=b.height,delete b.height),this.graph_options.xaxis={min:null,max:null,tickFormatter:e},this.graph_options.yaxis={min:null,max:null,tickFormatter:e},b.xmin!==undefined&&(this.__xmin=this.graph_options.xaxis.min=b.xmin,delete b.xmin),b.xmax!==undefined&&(this.__xmax=this.graph_options.xaxis.max=b.xmax,delete b.xmax),b.ymin!==undefined&&(this.__ymin=this.graph_options.yaxis.min=b.ymin,delete b.ymin),b.ymax!==undefined&&(this.__ymax=this.graph_options.yaxis.max=b.ymax,delete b.ymax),this.__logx=this.__logy=!1,b.logx!==undefined&&(this.__logx=graph_options.logx,delete b.logx),b.logy!==undefined&&(this.__logy=graph_options.logy,delete b.logy),this.__logx&&(this.graph_options.xaxis.transform=function(b){return a(b)},this.graph_options.xaxis.inverseTransform=function(a){return pow(10,a)}),this.__logy&&(this.graph_options.yaxis.transform=function(b){return a(b)},this.graph_options.yaxis.inverseTransform=function(a){return pow(10,a)});var c="",d=0;for(var g in b)d+=1,c+=g+", ";if(c.length>0)throw d==1?new Error(c.slice(0,c.length-2)+" is not an attribute of a graph"):new Error("These are not attributes of a graph: "+c.slice(0,c.length-2));f.selected=this,this.wrapper=$("<div/>"),this.wrapper.addClass("glowscript-graph").css("width",this.__width).css("height",this.__height).appendTo(canvas.container),this.graph_series=[],this.__update()}function h(a){a===undefined?a={data:[]}:a.data===undefined&&(a.data=[]),a.graph!==undefined?(this.__graph=a.graph,delete a.graph):(this.__graph=f.selected
 ,this.__graph||(this.__graph=f())),this.__type="line",a.type!==undefined&&(this.__type=a.type,delete a.type);var b=g[this.__type];if(!b)throw new Error("Unknown series type: "+this.__type);this.options={},this.options.data=a.data,delete a.data,this.options[b]={show:!0,align:"center",horizontal:!1,barWidth:1},a.horizontal!==undefined&&(this.__horizontal=this.options[b].horizontal=a.horizontal,delete a.horizontal),a.delta!==undefined&&(this.__delta=this.options[b].barWidth=a.delta,delete a.delta),a.width!==undefined&&(this.__width=this.options[b].lineWidth=a.width,delete a.width),a.radius!==undefined&&(this.__radius=this.options[b].radius=a.radius,delete a.radius),a.dot!==undefined&&(this.__dot=a.dot,delete a.dot),a.dot_color!==undefined&&(this.__dot_color=a.dot_color,delete a.dot_color),a.dot_radius!==undefined&&(this.__dot_radius=a.dot_radius,delete a.dot_radius),a.color!==undefined&&(this.__color=a.color,this.options.color=color.to_html(a.color),delete a.color),a.label!==undefined&&(this.__label=this.options.label=a.label,delete a.label),this.options.data.length>0&&(this.__graph.changed=!0),this.__visible=!0,a.visible!==undefined&&(this.__visible=a.visible,delete a.visible);var c="",d=0;for(var e in a)d+=1,c+=e+", ";if(c.length>0)throw d==1?new Error(c.slice(0,c.length-2)+" is not an attribute of a series"):new Error("These are not attributes of a series: "+c.slice(0,c.length-2));this.__graph.add_to_graph(this),this.plot=function(a){this.__graph.changed=!0;if(typeof arguments[0]=="number")this.options.data.push([arguments[0],arguments[1]]);else if(typeof arguments[0][0]=="number"){var b;for(var c=0;c<arguments.length;c++)b=arguments[c],this.options.data.push(b)}else{if(arguments.length!=1)throw new Error("must be plot(x,y) or plot([x,y]) or plot([x,y], ...) or plot([ [x,y], ... ])");var b;for(var c=0;c<a.length;c++)b=a[c],this.options.data.push(b)}}}function i(a){return new h(a)}"use strict";var b=!1,c=0,d=0;property.declare(f,{selected:{get:function(){return window.__context.graph_selected||null},set:function(a){window.__context.graph_selected=a}}}),property.declare(f.prototype,{__update:function(){var a=this;window.requestAnimFrame(function(){a.__update.call(a)},this.wrapper.get(0));if(!this.changed)return;var b=[];for(var c=0;c<this.graph_series.length;c++){var d=this.graph_series[c];if(d.__visible){b.push(d.options);if(d.__dot&&d.__type=="line"&&d.options.data.length>0){var e={points:{show:!0}};d.__dot_radius!==null?e.points.radius=d.__dot_radius:e.points.radius=d.__width+1,d.__dot_color!==null?e.color=color.to_html(d.__dot_color):e.color=color.to_html(d.__color),e.data=[d.options.data[d.options.data.length-1]],b.push(e)}}}this.changed=!1;if(b.length==0)return;var f=$.plot(this.wrapper,b,this.graph_options);f.draw()},add_to_graph:function(a){this.graph_series.push(a)},changed:!1}),Object.defineProperty(f.prototype,"__width",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"width",{enumerable:!0,get:function(){return this.__width},set:function(a){this.__width=a,this.wrapper.css("width",a);var b=$.plot(this.wrapper,[],this.options);b.resize(),b.setupGrid(),this.changed=!0}}),Object.defineProperty(f.prototype,"__height",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"height",{enumerable:!0,get:function(){return this.__height},set:function(a){this.__height=a,this.wrapper.css("height",a);var b=$.plot(this.wrapper,[],this.options);b.resize(),b.setupGrid(),this.changed=!0}}),Object.defineProperty(f.prototype,"__xmin",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"xmin",{enumerable:!0,get:function(){return this.__xmin},set:function(a){this.__xmin=this.options.xaxis.min=a,this.changed=!0}}),Object.defineProperty(f.prototype,"__xmax",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"xmax",{enumerable:!0,get:function(){return this.__xmax},set:function(a){this.__xmax=this.options.xaxis.max=a,this.changed=!0}}),Object.defineProperty(f.prototype,"__ymin",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"ymin",{enumerable:!0,get:function(){return this.__ymin},set:function(a){this.__ymin=this.options.yaxis.min=a,this.changed=!0}}),Object.defineProperty(f.prototype,"__ymax",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"ymax",{enumerable:!0,get:function(){return this.__ymax},set:function(a){this.__ymax=this.options.yaxis.max=a,this.changed=!0}}),Object.defineProperty(f.prototype,"__logx",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"logx",{enumerable:!0,get:function(){return this.__logx},set:function(b){if(this.__logx==b)return;b?(this.options.xaxis.transform=function(b){return a(b)},this.options.xaxis.inverseTransform=function(a){return pow(10,a)}):(delete this.options.xaxis.transform,delete this.options.xaxis.inverseTransform),this.__logx=b,this.changed=!0}}),Object.defineProperty(f.prototype,"__logy",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(f.prototype,"logy",{enumerable:!0,get:function(){return this.__logy},set:function(b){if(this.__logy==b)return;b?(this.options.yaxis.transform=function(b){return a(b)},this.options.yaxis.inverseTransform=function(a){return pow(10,a)}):(delete this.options.yaxis.transform,delete this.options.yaxis.inverseTransform),this.__logy=b,this.changed=!0}});var g={line:"lines",scatter:"points",bar:"bars",__proto__:null};Object.defineProperty(h.prototype,"__graph",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(h.prototype,"graph",{enumerable:!0,get:function(){return this.__graph},set:function(a){this.__graph.changed=!0,this.__graph.graph_series.splice(this.__graph.graph_series.indexOf(this),1),this.__graph=a,this.__graph.add_to_graph(this),this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__type",{enumerable:!1,writable:!0,value:"line"}),Object.defineProperty(h.prototype,"type",{enumerable:!0,get:function(){return this.__type},set:function(a){var b=this.__type;if(a==b)return;var c=g[b],d=g[a];if(!d)throw new Error("Unknown series type: "+a);this.options[d]=this.options[c],delete this.options[c],this.__type=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__color",{enumerable:!1,writable:!0,value:vec(0,0,0)}),Object.defineProperty(h.prototype,"color",{enumerable:!0,get:function(){return this.__color},set:function(a){if(this.__color.equals(a))return;this.__color=a,this.options.color=color.to_html(a),this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__label",{enumerable:!1,writable:!0,value:null}),Object.defineProperty(h.prototype,"label",{enumerable:!0,get:function(){return this.options.label===undefined?"":this.options.label},set:function(a){if(this.options.label==a)return;this.options.label=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__delta",{enumerable:!1,writable:!0,value:1}),Object.defineProperty(h.prototype,"delta",{enumerable:!0,get:function(){return this.__delta},set:function(a){if(this.__delta==a)return;this.__delta=a;var b=g[this.__type];this.options[b].barWidth=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__width",{enumerable:!1,writable:!0,value:1}),Object.defineProperty(h.prototype,"width",{enumerable:!0,get:function(){return this.__width},set:function(a){if(this.__width==a)return;this.__width=a;var b=g[this.__type];this.options[b].lineWidth=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__radius",{enumerable:!1,writable:!0,value:3}),Object.defineProperty(h.prototype,"radius",{enumerable:!0,get:function(){return this.__radius},set:function(a){if(this.__radius==a)return;this.__radius=a;var b=g[this.__type];this.options[b].radius=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__horizontal",{enumerable:!1,writable:!0,value:!1}),Object.defineProperty(h.prototype,"horizontal",{enumerable:!0,get:function(){return this.__delta},set:function(a){if(this.__horizontal==a)return;this.__horizontal=a;var b=g[this.__type];this.options[b].horizontal=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__dot",{enumerable:!1,writable:!0,value:!1}),Object.defineProperty(h.prototype,"dot",{enumerable:!0,get:function(){return this.__dot},set:function(a){if(this.__dot==a)return;this.__dot=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__dot_color",{enumerable:!1,writable:!0,value:null}),Object.defineProperty(h.prototype,"dot_color",{enumerable:!0,get:function(){return this.__dot_color},set:function(a){if(this.__dot_color.equals(a))return;this.__dot_color=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__dot_radius",{enumerable:!1,writable:!0,value:null}),Object.defineProperty(h.prototype,"dot_radius",{enumerable:!0,get:function(){return this.__dot_radius},set:function(a){if(this.__dot_radius==a)return;this.__dot_radius=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__visible",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(h.prototype,"visible",{enumerable:!0,get:function(){return this.__visible},set:function(a){if(this.__visible==a)return;this.__visible=a,this.__graph.changed=!0}}),Object.defineProperty(h.prototype,"__data",{enumerable:!1,writable:!0,value:0}),Object.defineProperty(h.prototype,"data",{enumerable:!0,get:function(){return this.options.data},set:function(a){this.options.data=[],this.plot(a)}});var j={graph:f,series:i};Export(j)}(),function(){"use strict";var a={red:vec(1,0,0),green:vec(0,1,0),blue:vec(0,0,1),yellow:vec(1,1,0),orange:vec(1,.6,0),cyan:vec(0,1,1),magenta:vec(1,0,1),white:vec(1,1,1),black:vec(0,0,0),gray:function(a){return vec(a,a,a)},hsv_to_rgb:function(a){var b=a.x,c=a.y,d=a.z;if(c==0)return vec(d,d,d);var e=Math.floor(6*b),f=6*b-e,g=d*(1-c),h=d*(1-c*f),i=d*(1-c*(1-f)),e=e%6;switch(e){case 0:return vec(d,i,g);case 1:return vec(h,d,g);case 2:return vec(g,d,i);case 3:return vec(g,h,d);case 4:return vec(i,g,d);case 5:return vec(d,g,h)}},rgb_to_hsv:function(a){var b=a.x,c=a.y,d=a.z,e=Math.max(b,c,d),f=Math.min(b,c,d),g=e;if(f==e)return vec(0,0,g);var h=(e-f)/e,i=(e-b)/(e-f),j=(e-c)/(e-f),k=(e-d)/(e-f),l;return b==e?l=k-j:c==e?l=2+i-k:l=4+j-i,l=l/6,l<0&&l++,vec(l,h,g)},to_html:function(a){var b=Math.floor(255*a.x),c=Math.floor(255*a.y),d=Math.floor(255*a.z);return"rgb("+b+","+c+","+d+")"},to_html_rgba:function(a,b){var c=Math.floor(255*a.x),d=Math.floor(255*a.y),e=Math.floor(255*a.z);return"rgba("+c+","+d+","+e+","+b+")"}},b={color:a};Export(b)}(),function(){function subclass(a,b){a.prototype=new b({visible:!1,canvas:null}),a.prototype.constructor=a}function id_to_falsecolor(a){var b=0,c=0,d=0;return a>=16777216&&(b=Math.floor(a/16777216),a-=b*16777216),a>=65536&&(c=Math.floor(a/65536),a-=c*65536),a>=256&&(d=Math.floor(a/256),a-=d*256),[b/255,c/255,d/255,a/255]}function init(a,b){b.canvas!==undefined?(a.canvas=b.canvas,delete b.canvas):a.canvas=canvas.selected,a.canvas&&(a.canvas.__activate(),a.__model=a.__get_model());for(var c in b)a[c]=b[c];b.visible===undefined&&a.canvas!==null&&(a.visible=!0)}function initObject(a,b,c){if(a instanceof b)c===undefined&&(c={}),a.__tex={file:null,bumpmap:null,texture_ref:{reference:null},bumpmap_ref:{reference:null},left:!1,right:!1,sides:!1,flipx:!1,flipy:!1,turn:0,flags:0},c.pos===undefined&&(a.pos=a.pos),c.color===undefined&&(a.color=a.color),c.axis===undefined&&(a.axis=a.axis),c.up===undefined&&(a.up=a.up),c.size===undefined&&(a.size=a.size),c.opacity===undefined&&(a.__opacity=1),a.__opacity_change=!0,init(a,c);else return new b(c)}function setup_texture(a,b,c){a.slice(0,1)==":"&&(a="images/"+a.slice(1)),b.canvas.__renderer.initTexture(a,b,c)}function Primitive(){}function box(a){return initObject(this,box,a)}function cylinder(a){return initObject(this,cylinder,a)}function cone(a){return initObject(this,cone,a)}function pyramid(a){return initObject(this,pyramid,a)}function sphere(a){return initObject(this,sphere,a)}function arrow(a){return initObject(this,arrow,a)}function vertex(a){if(!(this instanceof vertex))return new vertex(a);a=a||{},a.canvas!==undefined?(this.canvas=a.canvas,delete a.canvas):this.canvas=canvas.selected;for(var b in a)this[b]=a[b];a.opacity===undefined&&(this.opacity=1);if(this.__texpos.z!==0)throw new Error("In a vertex the z component of texpos must be zero.");if(this.canvas.vertex_id>=65536)throw new Error("Currently the number of vertices is limited to 65536.");var c={pos:3,normal:3,color:3,opacity:1,texpos:2,bumpaxis:3,params:2};this.__id=this.canvas.vertex_id;var d=this.canvas.__vertices;if(this.canvas.vertex_id%d.Nalloc===0){var e;for(var f in c)e=new Float32Array(c[f]*(this.canvas.vertex_id+d.Nalloc)),e.set(d[f],0),d[f]=e}this.canvas.vertex_id++,this.canvas.__vertices.object_info[this.__id]={},this.__change()}function tri_quad_error(a,b){throw new Error("A "+a+" has no "+b+" attribute.")}function triangle(a){if(!(this instanceof triangle))return new triangle(a);a=a||{};var b=["v0","v1","v2"];for(var c=0;c<3;c++)if(a[b[c]]===undefined)throw new Error("A triangle must have a vertex "+b[c]+".");this.__tex={file:null,bumpmap:null,texture_ref:{reference:null},bumpmap_ref:{reference:null},left:!1,right:!1,sides:!1,flipx:!1,flipy:!1,turn:0,flags:0},init(this,a)}function quad(a){if(!(this instanceof quad))return new quad(a);a=a||{};var b=["v0","v1","v2","v3"];for(var c=0;c<4;c++)if(a[b[c]]===undefined)throw new Error("A quad must have a vertex "+b[c]+".");this.__tex={file:null,bumpmap:null,texture_ref:{reference:null},bumpmap_ref:{reference:null},left:!1,right:!1,sides:!1,flipx:!1,flipy:!1,turn:0,flags:0},init(this,a)}function compound(a,b){function g(a,b){for(var c in b){var d=b[c];if(c.slice(-3)=="min"){if(a[c]===null||d<a[c])a[c]=d}else if(a[c]===null||d>a[c])a[c]=d}}if(!(this instanceof compound))return new compound(a,b);b=b||{};if(a.length===undefined)throw new Error("compound takes a list of objects");initObject(this,compound,b);var c=!1;if(b.__cloning){c=!0;var d=b.__cloning;delete b.__cloning}var e=!0;if(b!==undefined){for(var f in b)this[f]=b[f];e=b.visible===undefined?!0:b.visible}if(!c){var d=new Mesh;for(var h=0;h<a.length;h++){var i=a[h];if(i instanceof arrow)throw new Error("Currently cannot include an arrow in a compound.");if(i.__tex.file!==null)throw new Error("Currently objects in a compound cannot have their own texture.");if(i.__tex.bumpmap!==null)throw new Error("Currently objects in a compound cannot have their own bumpmap.");i.visible=!1,i instanceof triangle||i instanceof quad?(g(this,d.merge(i.v0,i.v0,0)),g(this,d.merge(i.v1,i.v1,0)),g(this,d.merge(i.v2,i.v2,0)),i instanceof quad&&(g(this,d.merge(i.v0,i.v0,-3)),g(this,d.merge(i.v2,i.v2,-1)),g(this,d.merge(i.v3,i.v3,0)))):g(this,d.merge(i.getTransformedMesh(),i,0))}this.__center=vec((this.__xmin+this.__xmax)/2,(this.__ymin+this.__ymax)/2,(this.__zmin+this.__zmax)/2),this.__pseudosize=vec(this.__xmax-this.__xmin,this.__ymax-this.__ymin,this.__zmax-this.__zmin),compound_id++,d.__mesh_id="compound"+compound_id,this.canvas.__renderer.add_model(d,!1)}this.__mesh=d,this.__model=this.canvas.__renderer.models[d.__mesh_id],this.visible=e}function curve(a){if(!(this instanceof curve))return new curve(a);a=a||{};if(a.texture!==undefined)throw new Error("Textures are not available for curve objects.");if(a.opacity!==undefined)throw new Error("Opacity is not available for curve objects.");initObject(this,curve,a),this.__points=[],this.radius||(this.radius=.001*this.canvas.__range)}function point(a){if(!(this instanceof point))return new point(a);a instanceof vec&&(a={pos:a});for(var b in a)this[b]=a[b];if(this.pos===undefined)throw new Error("Must specify pos for a point on a curve")}function helix(a){return initObject(this,helix,a)}function ring(a){return initObject(this,ring,a)}function distant_light(a){if(!(this instanceof distant_light))return new distant_light(a);if(a.direction===undefined)throw new Error("Must specify the distant_light, direction:..");init(this,a),this.canvas.lights.push(this)}function local_light(a){if(!(this instanceof local_light))return new local_light(a);if(a.pos===undefined)throw new Error("Must specify the local_light position, pos:..");init(this,a),this.canvas.lights.push(this)}function draw(a){if(this instanceof draw)a=a||{},this.points=[],init(this,a),this.canvas.__overlay_objects.objects.push(this);else return new draw(a)}function label(a){if(this instanceof label)a=a||{},this.pos=this.pos,this.color=this.color,init(this,a),this.canvas.__overlay_objects.objects.push(this);else return new label(a)}function attach_trail(a,b){if(!(this instanceof attach_trail))return new attach_trail(a,b);b===undefined&&(b={}),b.canvas===undefined&&(b.canvas=canvas.selected,typeof a!="function"&&(b.canvas=a.canvas));if(b.type===undefined)this.type="curve";else switch(b.type){case"curve":this.type=b.type,delete b.type;break;case"spheres":this.type=b.type,delete b.type,b.pickable=!1;break;default:throw new Error("attach_trail type must be 'curve' or 'spheres'")}this.retain=-1,b.retain!==undefined&&(this.retain=b.retain,delete b.retain),this.pps=0,b.pps!==undefined&&(this.pps=b.pps,delete b.pps);var c;this.obj=a,typeof a!="function"?(b.color===undefined&&(b.color=a.color),b.radius===undefined?c=.05*a.size.y:(c=b.radius,delete b.radius)):b.radius===undefined?c=.001*b.canvas.range:(c=b.radius,delete b.radius),this.type=="curve"?b.radius=c:b.size=vec(2*c,2*c,2*c),b.pickable=!1,this.options=b;var d=b.canvas;this.type=="curve"?this.curve=curve(this.options):this.spheres=[],this.last_pos=null,this.last_time=null,this.run=!0,this.elements=0,d.trails.push(this),this.start=function(){this.run=!0,this.type==="curve"&&(this.curve=curve(this.options))},this.stop=function(){this.run=!1},this.clear=function(){if(this.type=="curve")this.curve.clear();else{var a=this.spheres;for(var b=0;b<a.length;b++)a[b].visible=!1;this.spheres=[]}}}function attach_arrow(a,b,c){if(!(this instanceof attach_arrow))return new attach_arrow(a,b,c);c===undefined&&(c={}),c.canvas===undefined&&(c.canvas=a.canvas),this.obj=a,this.attr=b,this.scale=1,c.scale!==undefined&&(this.scale=c.scale,delete c.scale),c.color===undefined&&(c.color=a.color),this.options=c;var d=c.canvas;this.arrow=arrow(this.options),this.arrow.visible=!1,this.arrow.pickable=!1,this.last_pos=null,this.run=!0,d.arrows.push(this),this.start=function(){this.arrow.visible=this.run=!0},this.stop=function(){this.arrow.visible=this.run=!1}}"use strict";var nextVisibleId=1,textures={flower:":flower_texture.jpg",granite:":granite_texture.jpg",gravel:":gravel_texture.jpg",metal:":metal_texture.jpg",rock:":rock_texture.jpg",rough:":rough_texture.jpg",rug:":rug_texture.jpg",stones:":stones_texture.jpg",stucco:":stucco_texture.jpg",wood:":wood_texture.jpg",wood_old:":wood_old_texture.jpg"},bumpmaps={gravel:":gravel_bumpmap.jpg",rock:":rock_bumpmap.jpg",stones:":stones_bumpmap.jpg",stucco:":stucco_bumpmap.jpg",wood_old:":wood_old_bumpmap.jpg"};property.declare(Primitive.prototype,{__id:null,__hasPosAtCenter:!1,__zx_camera:null,__zy_camera:null,__xmin:null,__ymin:null,__zmin:null,__xmax:null,__ymax:null,__zmax:null,pos:new attributeVector(null,0,0,0),color:new attributeVector(null,1,1,1),size:new attributeVector(null,1,1,1),axis:new attributeVector(null,1,0,0),up:new attributeVector(null,0,1,0),opacity:{get:function(){return this.__opacity},set:function(a){if(a==this.__opacity)return;if(this.__opacity<1&&a==1||this.__opacity==1&&a<1)this.__opacity_change=!0;this.__opacity=a,this.__change}},__opacity_change:!1,__prev_opacity:null,shininess:{value:.6,onchanged:function(){this.__change()}},emissive:{value:!1,onchanged:function(){this.__change()}},pickable:{value:!0,onchanged:function(){this.__change()}},ready:{get:function(){return this.__tex.file===null||this.__tex.texture_ref.reference!==null&&this.__tex.bumpmap===null||this.__tex.bumpmap_ref.reference!==null}},texture:{get:function(){return{file:this.__tex.file,bumpmap:this.__tex.bumpmap,left:this.__tex.left,right:this.__tex.right,sides:this.__tex.sides,flipx:this.__tex.flipx,flipy:this.__tex.flipy,turn:this.__tex.turn}},set:function(a){this.__tex={file:null,bumpmap:null,texture_ref:{reference:null},bumpmap_ref:{reference:null},left:!1,right:!1,sides:!1,flipx:!1,flipy:!1,turn:0,flags:0};if(a!==null)if(typeof a=="string")this.__tex.left=this.__tex.right=this.__tex.sides=!0,setup_texture(a,this,!1);else{if(a.file!==undefined&&typeof a.file=="string")setup_texture(a.file,this,!1);else throw new Error("You must specify a file name for a texture.");if(a.bumpmap!==undefined&&a.bumpmap!==null){if(typeof a.bumpmap!="string")throw new Error("You must specify a file name for a bumpmap.");setup_texture(a.bumpmap,this,!0)}a.flipx!==undefined&&(this.__tex.flipx=a.flipx),a.flipy!==undefined&&(this.__tex.flipy=a.flipy),a.turn!==undefined&&(this.__tex.turn=Math.round(a.turn));if(a.place!==undefined){typeof a.place=="string"&&(a.place=[a.place]);for(var b=0;b<a.place.length;b++)switch(a.place[b]){case"left":this.__tex.left=!0;break;case"right":this.__tex.right=!0;break;case"sides":this.__tex.sides=!0;break;case"ends":this.__tex.left=this.__tex.right=!0;break;case"all":this.__tex.left=this.__tex.right=this.__tex.sides=!0}}else this.__tex.left=this.__tex.right=this.__tex.sides=!0}this.__tex.flags=0,this.__tex.file!==null&&(this.__tex.flags+=1),this.__tex.bumpmap!==null&&(this.__tex.flags+=2),this.__tex.left&&(this.__tex.flags+=4),this.__tex.right&&(this.__tex.flags+=8),this.__tex.sides&&(this.__tex.flags+=16),this.__tex.flipx&&(this.__tex.flags+=32),this.__tex.flipy&&(this.__tex.flags+=64);var c=this.__tex.turn%4;c<0&&(c+=4),this.__tex.flags+=128*c,this.__change()}},visible:{get:function(){return this.__id!=null},set:function(a){if(a==(this.__id!=null))return;if(a){this.__id=nextVisibleId,nextVisibleId++,this.canvas.__visiblePrimitives[this.__id]=this,this.__falsecolor=id_to_falsecolor(this.__id),this.canvas.__changed[this.__id]=this;if(this instanceof triangle||this instanceof quad){var b=["__v0","__v1","__v2","__v3"],c=3;this instanceof quad&&(c=4);for(var d=0;d<c;d++)this.canvas.__vertices.object_info[this[b[d]].__id][this.__id]=this}}else{delete this.canvas.__visiblePrimitives[this.__id],delete this.canvas.__changed[this.__id],this.__model&&delete this.__model.id_object[this.__id];if(this.__components)for(var d=0;d<this.__components.length;d++)delete this.__components[d].__model.id_object[this.__components[d].__id];if(this instanceof triangle||this instanceof quad){var b=["__v0","__v1","__v2","__v3"],c=3;this instanceof quad&&(c=4);for(var d=0;d<c;d++)delete this.canvas.__vertices.object_info[this[b[d]].__id][this.__id]}this.__id=null}}},clone:function(a){if(this instanceof triangle||this instanceof quad)throw new Error("Cannot clone a "+this.constructor.name+" object.");var b={pos:this.__pos,color:this.__color,opacity:this.__opacity,size:this.__size,axis:this.__axis,up:this.__up,textures:this.__texture,shininess:this.__shininess,emissive:this.__emissive,visible:!0,pickable:this.__pickable};for(var c in a)b[c]=a[c];return new this.constructor(b)},__change:function(){this.__id&&(this.canvas.__changed[this.__id]=this)},__get_extent:function(a){Autoscale.find_extent(this,a)},__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__update:function(){var a=this.__pos,b=this.__size,c=this.__color,d=this.__axis,e=this.__up,f=this.__data;f||(this.__data=f=new Float32Array(20)),this.__model.id_object[this.__id]=this,f[0]=a.__x,f[1]=a.__y,f[2]=a.__z,f[3]=this.__shininess,f[4]=d.__x,f[5]=d.__y,f[6]=d.__z,f[7]=this.__emissive?1:0,f[8]=e.__x,f[9]=e.__y,f[10]=e.__z,f[12]=b.__x,f[13]=b.__y,f[14]=b.__z,f[16]=c.__x,f[17]=c.__y,f[18]=c.__z,f[19]=this.__opacity,this.constructor==curve?f[15]=this.__radius:f[11]=this.__tex.flags},rotate:function(a){if(a===undefined||a.angle===undefined)throw new Error("object.rotate() requires angle:...");var b=a.angle,c,d;a.axis===undefined?c=this.__axis:c=a.axis.norm(),a.origin===undefined?d=this.__pos:d=a.origin;var e=this.__axis.norm(),f=this.__up.norm(),g=e.cross(f);g.dot(g)<1e-10&&(f=vec(1,0,0),g=e.cross(f),g.dot(g)<1e-10&&(f=vec(0,1,0))),this.pos=d.add(this.__pos.sub(d).rotate({angle:b,axis:c})),this.axis=this.__axis.rotate({angle:b,axis:c}),this.up=f.rotate({angle:b,axis:c})},getTransformedMesh:function(){var a=this.__axis.norm(),b=this.__up.norm(),c=a.cross(b);c.dot(c)<1e-10&&(b=vec(1,0,0),c=a.cross(b),c.dot(c)<1e-10&&(b=vec(0,1,0)),c=a.cross(b)),c=c.norm();var b=c.cross(a).norm();a=a.multiply(this.__size.x),b=b.multiply(this.__size.y),c=c.multiply(this.__size.z);var d=this.__pos,e=[a.x,a.y,a.z,0,b.x,b.y,b.z,0,c.x,c.y,c.z,0,d.x,d.y,d.z,1];return this.__model.mesh.transformed(e)}}),subclass(box,Primitive),box.prototype.__hasPosAtCenter=!0,subclass(cylinder,Primitive),subclass(cone,cylinder),subclass(pyramid,box),subclass(sphere,Primitive),sphere.prototype.__hasPosAtCenter=!0,subclass(arrow,Primitive),property.declare(arrow.prototype,{__primitiveCount:2,shaftwidth:0,headwidth:0,headlength:0,axis_and_length:{get:function(){return this.__axis.norm().multiply(this.__size.x)},set:function(a){this.axis=a,this.__size.x=a.mag()}},__update:function(){var a=this.__pos,b=this.__color,c=this.__axis,d=this.__size,e=this.__up,f=d.__x,g=c.norm(),h=this.__shaftwidth||f*.1,i=this.__headwidth||h*2,j=this.__headlength||h*3;if(h<f*.02){var k=f*.02/h;this.__shaftwidth||(h*=k),this.__headwidth||(i*=k),this.__headlength||(j*=k)}if(j>f*.5){var k=f*.5/j;this.__shaftwidth||(h*=k),this.__headwidth||(i*=k),this.__headlength||(j*=k)}var l=this.__components;if(!l){l=this.__components=[box({canvas:this.canvas,visible:0}),pyramid({canvas:this.canvas,visible:0})];for(var m=0;m<l.length;m++)l[m].__id=nextVisibleId++,l[m].__falsecolor=this.__falsecolor}var n=l[0],o=l[1];n.pos=a.add(g.multiply(.5*(f-j))),o.pos=a.add(g.multiply(f-j)),n.axis=o.axis=c,n.up=o.up=e,n.size=vec(f-j,h,h),o.size=vec(j,i,i),n.color=o.color=this.color,n.opacity=o.opacity=this.opacity,this.size=vec(f,i,i),n.__update(),o.__update()}}),property.declare(vertex.prototype,{__id:null,__hasPosAtCenter:!0,pos:new attributeVector(null,0,0,0),normal:new attributeVector(null,0,0,1),color:new attributeVector(null,1,1,1),opacity:{get:function(){return this.__opacity},set:function(a){if(a==this.__opacity)return;if(this.__opacity<1&&a==1||this.__opacity==1&&a<1){var b=this.canvas.__vertices.object_info[this.__id];for(var c in b)b[c].__change(),b[c].__opacity_change=!0}this.__opacity=a,this.canvas.__vertex_changed[this.__id]=this}},texpos:new attributeVector(null,0,0,0),bumpaxis:new attributeVector(null,1,0,0),shininess:{value:.6,onchanged:function(){this.__change()}},emissive:{value:!1,onchanged:function(){this.__change()}},__change:function(){if(this.__id){this.canvas.__vertex_changed[this.__id]=this;if(this.canvas.__autoscale){var a=this.canvas.__vertices.object_info[this.__id];for(var b in a)a[b].__change()}}},rotate:function(a){if(a.angle===undefined)throw new Error("vertex.rotate() requires angle:...");var b=a.angle;if(a.axis===undefined)throw new Error("vertex.rotate() requires axis:...");var c=a.axis.norm(),d;a.origin===undefined?d=vec(0,0,0):d=a.origin,this.pos=d.add(this.__pos.sub(d).rotate({angle:b,axis:c})),this.__change()}}),subclass(triangle,box),property.declare(triangle.prototype,{v0:{get:function(){return this.__v0},set:function(a){if(a instanceof vertex)this.__v0=a,this.__change();else throw new Error("v0 must be a vertex object.")}},v1:{get:function(){return this.__v1},set:function(a){if(a instanceof vertex)this.__v1=a,this.__change();else throw new Error("v1 must be a vertex object.")}},v2:{get:function(){return this.__v2},set:function(a){if(a instanceof vertex)this.__v2=a,this.__change();else throw new Error("v2 must be a vertex object.")}},pos:{get:function(){tri_quad_error("triangle","pos")},set:function(a){tri_quad_error("triangle","pos")}},color:{get:function(){tri_quad_error("triangle","color")},set:function(a){tri_quad_error("triangle","color")}},size:{get:function(){tri_quad_error("triangle","size")},set:function(a){tri_quad_error("triangle","size")}},axis:{get:function(){tri_quad_error("triangle","axis")},set:function(a){tri_quad_error("triangle","axis")}},up:{get:function(){tri_quad_error("triangle","up")},set:function(a){tri_quad_error("triangle","up")}},opacity:{get:function(){tri_quad_error("triangle","opacity")},set:function(a){tri_quad_error("triangle","opacity")}},shininess:{get:function(){tri_quad_error("triangle","shininess")},set:function(a){tri_quad_error("triangle","shininess")}},emissive:{get:function(){tri_quad_error("triangle","emissive")},set:function(a){tri_quad_error("triangle","emissive")}},__prev_texture:null,__prev_bumpmap:null,__update:function(){this.__model.id_object[this.__id]=this},__get_extent:function(a){var b=["__v0","__v1","__v2"];for(var c=0;c<3;c++)a.point_extent(this,this[b[c]].pos)},rotate:function(a){throw new Error("A triangle has no rotate method; rotate the vertices instead.")}}),subclass(quad,box),property.declare(quad.prototype,{v0:{get:function(){return this.__v0},set:function(a){if(a instanceof vertex)this.__v0=a,this.__change();else throw new Error("v0 must be a vertex object.")}},v1:{get:function(){return this.__v1},set:function(a){if(a instanceof vertex)this.__v1=a,this.__change();else throw new Error("v1 must be a vertex object.")}},v2:{get:function(){return this.__v2},set:function(a){if(a instanceof vertex)this.__v2=a,this.__change();else throw new Error("v2 must be a vertex object.")}},v3:{get:function(){return this.__v3},set:function(a){if(a instanceof vertex)this.__v3=a,this.__change();else throw new Error("v3 must be a vertex object.")}},pos:{get:function(){tri_quad_error("quad","pos")},set:function(a){tri_quad_error("quad","pos")}},color:{get:function(){tri_quad_error("quad","color")},set:function(a){tri_quad_error("quad","color")}},size:{get:function(){tri_quad_error("quad","size")},set:function(a){tri_quad_error("quad","size")}},axis:{get:function(){tri_quad_error("quad","axis")},set:function(a){tri_quad_error("quad","axis")}},up:{get:function(){tri_quad_error("quad","up")},set:function(a){tri_quad_error("quad","up")}},opacity:{get:function(){tri_quad_error("quad","opacity")},set:function(a){tri_quad_error("quad","opacity")}},shininess:{get:function(){tri_quad_error("quad","shininess")},set:function(a){tri_quad_error("quad","shininess")}},__prev_texture:null,__prev_bumpmap:null,__update:function(){this.__model.id_object[this.__id]=this},__get_extent:function(a){var b=["__v0","__v1","__v2","__v3"];for(var c=0;c<4;c++)a.point_extent(this,this[b[c]].pos)},rotate:function(a){throw new Error("A quad has no rotate method; rotate the vertices instead.")}});var compound_id=0;subclass(compound,box),property.declare(compound.prototype,{clone:function(a){var b={pos:this.__pos,color:this.__color,opacity:this.__opacity,size:this.__size,axis:this.__axis,up:this.__up,textures:this.__texture,shininess:this.__shininess,emissive:this.__emissive,visible:!0,pickable:this.__pickable,__center:this.__center,__pseudosize:this.__pseudosize};for(var c in a)b[c]=a[c];return b.__cloning=this.__mesh,new this.constructor([],b)},_world_zaxis:function(){var a=this.__axis,b=this.__up,c;return Math.abs(a.dot(b))/Math.sqrt(b.mag2()*a.mag2())>.98?Math.abs(a.norm().dot(vec(-1,0,0)))>.98?c=a.cross(vec(0,0,1)).norm():c=a.cross(vec(-1,0,0)).norm():c=a.cross(b).norm(),c},world_to_compound:function(a){var b=this.__axis,c=this._world_zaxis(),d=c.cross(b).norm(),e=b.norm(),a=a.sub(this.__pos);return vec(a.dot(e),a.dot(d),a.dot(c))},compound_to_world:function(a){var b=this.__axis,c=this._world_zaxis(),d=c.cross(b).norm(),e=b.norm();return this.__pos.add(e.multiply(a.x)).add(d.multiply(a.y)).add(c.multiply(a.z))},__get_model:function(){return this.__model},__get_extent:function(a){var b=this.__pos,c=this.__size,d=vec(this.__size.x*this.__center.x,this.__size.y*this.__center.y,this.__size.z*this.__center.z),e=d.add(this.__pos),f=vec(this.__size.x*this.__pseudosize.x,this.__size.y*this.__pseudosize.y,this.__size.z*this.__pseudosize.z);this.__pos=e,this.__pos.__x=e.x,this.__pos.__y=e.y,this.__pos.__z=e.z,this.__size=f,this.__size.__x=f.x,this.__size.__y=f.y,this.__size.__z=f.z,Autoscale.find_extent(this,a),this.__pos=b,this.__size=c}}),subclass(curve,Primitive),property.declare(curve.prototype,{radius:0,__no_autoscale:!1,__get_extent:function(a){if(this.__no_autoscale)return;var b=null,c=null,d=null,e=null,f=null,g=null,h=this.__points.length,i=this.__points,j;for(var k=0;k<h;k++){j=i[k].__pos;if(b===null||j.x<b)b=j.x;if(c===null||j.y<c)c=j.y;if(d===null||j.z<d)d=j.z;if(e===null||j.x>e)e=j.x;if(f===null||j.y>f)f=j.y;if(g===null||j.z>g)g=j.z}var l=vec((b+e)/2,(c+f)/2,(d+g)/2),m=vec(e-b,f-c,g-d),n=this.__pos,o=this.__size,p=vec(this.__size.x*l.x,this.__size.y*l.y,this.__size.z*l.z),q=p.add(this.__pos),r=vec(this.__size.x*m.x,this.__size.y*m.y,this
-.__size.z*m.z);this.__pos=q,this.__pos.__x=q.x,this.__pos.__y=q.y,this.__pos.__z=q.z,this.__size=r,this.__size.__x=r.x,this.__size.__y=r.y,this.__size.__z=r.z,Autoscale.find_extent(this,a),this.__pos=n,this.__size=o},push:function(a){var b=[],c;if(a.length!==undefined)b=a;else for(var d=0;d<arguments.length;d++)b.push(arguments[d]);for(var d=0;d<b.length;d++){c=point(b[d]),c.__curve=this,c.__id=nextVisibleId++,this.canvas.__visiblePrimitives[c.__id]=c,c.__falsecolor=id_to_falsecolor(c.__id);if(this.__points.length){var e=this.__points[this.__points.length-1],f=c.__prevsegment=e.__nextsegment=new Float32Array(16);f[11]=f[15]=1,e.__change()}this.__points.push(c),c.__change()}this.__change()},pop:function(){var a=this.__points.pop();return a.visible=!1,this.__change(),{pos:a.pos,color:a.color,radius:a.radius,visible:a.visible}},clear:function(){this.splice(0,this.__points.length),this.__change()},shift:function(){var a=this.__points.shift();return a.visible=!1,this.__change(),{pos:a.pos,color:a.color,radius:a.radius,visible:a.visible}},unshift:function(a){var b=[];if(a.length!==undefined)b=a;else for(var c=0;c<arguments.length;c++)b.push(arguments[c]);this.splice(0,0,b),this.__change()},splice:function(a){var b=arguments[0],c=arguments[1],d=[];for(var e=2;e<arguments.length;e++)d.push(arguments[e]);d.length==1&&d[0].length!==undefined&&(d=d[0]);var f=this.__points.slice(b+c),g=[];for(var e=0;e<f.length;e++)g.push({pos:f[e].pos,color:f[e].color,radius:f[e].radius,visible:f[e].visible});for(var e=b;e<this.__points.length;e++)this.__points[e].visible=!1;this.__points.splice(b),this.push(d),this.push(g),this.__change()},modify:function(a,b){b instanceof vec&&(b={pos:b});for(var c in b)c=="x"&&(this.__points[a].pos.x=b[c]),c=="y"&&(this.__points[a].pos.y=b[c]),c=="z"?this.__points[a].pos.z=b[c]:this.__points[a][c]=b[c];this.__points[a].__change(),this.__change()},slice:function(a,b){var c=this.__points.slice(a,b),d=[];for(var e=0;e<c.length;e++)d.push({pos:c[e].pos,color:c[e].color,radius:c[e].radius,visible:c[e].visible});return d}}),property.declare(point.prototype,{__curve:null,pos:new attributeVector(null,0,0,0),color:new attributeVector(null,-1,-1,-1),radius:{value:-1,onchanged:function(){this.__change()}},pickable:{value:!0,onchanged:function(){this.__change()}},visible:{value:!0,onchanged:function(){this.__change()}},__change:function(){this.__id&&(this.__curve.canvas.__changed[this.__id]=this,this.__curve.canvas.__changed[this.__curve.__id]=this.__curve)},__update:function(){var a=this.__pos,b=this.radius||-1,c=this.color||vec(-1,-1,-1),d=this.__prevsegment;d&&(d[4]=a.x,d[5]=a.y,d[6]=a.z,d[7]=b,d[12]=c.x,d[13]=c.y,d[14]=c.z),d=this.__nextsegment,d&&(d[0]=a.x,d[1]=a.y,d[2]=a.z,d[3]=b,d[8]=c.x,d[9]=c.y,d[10]=c.z)}}),subclass(helix,cylinder),property.declare(helix.prototype,{__initialize:!0,thickness:{value:0,onchanged:function(){this.__change()}},coils:{value:5,onchanged:function(){this.__initialize=!0,this.__change()}},__update:function(){var a=20;this.__initialize&&(this.__curve!==undefined?this.__curve.clear():this.__curve=curve({__no_autoscale:!0}));var b=this.__curve;b.pos=this.__pos,b.axis=this.__axis,b.up=this.__up,b.size=this.__size,b.color=this.__color,b.radius=this.__thickness?this.__thickness/2:this.__size.y/40;if(!this.__initialize)return;var c=vec(1,0,0),d=vec(0,1,0),e=vec(0,0,1),f=.5,g=this.__coils*a,h=1/g,i=Math.sin(2*Math.PI/a),j=Math.cos(2*Math.PI/a),k=0,l=0,m=f,n;for(var o=0;o<g+1;o++)b.push(vec(k,l,m)),k+=h,n=m*j-l*i,l=l*j+m*i,m=n;this.__initialize=!1}}),subclass(ring,box),property.declare(ring.prototype,{__initialize:!0,__hasPosAtCenter:!0,__size:new attributeVector(null,.1,1,1),__update:function(){if(this.__initialize)if(this.__ring!==undefined){for(var a=0;a<this.__ring.__points.length;a++)this.__ring.__points[a].pos=this.__pos;this.__ring.__update(),this.__ring.__points=[]}else this.__ring=curve({__no_autoscale:!0}),this.__ring.__id=nextVisibleId++;var b=this.__ring;b.color=this.__color,b.radius=this.__size.x/2;var c=this.__axis.norm(),d,e;e=c.cross(this.__up),Math.abs(e.dot(e))<1e-10&&(e=c.cross(vec(1,0,0)),Math.abs(e.dot(e))<1e-10&&(e=c.cross(vec(0,1,0)))),e=e.norm(),d=e.cross(c);var f=100,g=(this.__size.y-this.__size.x)/2,h=g,i=0,j,k=Math.sin(2*Math.PI/f),l=Math.cos(2*Math.PI/f),m=this.__pos;for(var a=0;a<=f;a++){var n=m.add(d.multiply(i)).add(e.multiply(h));this.__initialize?b.push(point(n)):b.__points[a].pos=n,j=i*l+h*k,h=h*l-i*k,i=j}b.__update(),this.__initialize=!1}}),property.declare(distant_light.prototype,{direction:new attributeVector(null,0,0,1),color:new attributeVector(null,1,1,1),__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__change:function(){}}),property.declare(local_light.prototype,{pos:new attributeVector(null,0,0,0),color:new attributeVector(null,1,1,1),__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__change:function(){}}),property.declare(draw.prototype,{color:{value:null,type:property.nullable_attributeVector},fillcolor:{value:null,type:property.nullable_attributeVector},linewidth:{value:1,onchanged:function(){this.__change()}},opacity:{value:.66,onchanged:function(){this.__change()}},visible:{value:!1,onchanged:function(){this.__change()}},__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__update:function(a,b){var c=this.points.length;if(c<2)return;if(this.fillcolor!=null){a.lineWidth=1,a.fillStyle=color.to_html_rgba(this.fillcolor,this.opacity),a.beginPath();for(var d=0;d<c;d++)d==0?a.moveTo(this.points[d].x,this.points[d].y):a.lineTo(this.points[d].x,this.points[d].y);a.fill()}if(this.color!=null){a.lineWidth=this.linewidth,a.strokeStyle=color.to_html_rgba(this.color,this.opacity),a.beginPath();for(var d=0;d<c;d++)d==0?a.moveTo(this.points[d].x,this.points[d].y):d==c-1&&this.points[d].equals(this.points[0])?a.closePath():a.lineTo(this.points[d].x,this.points[d].y);a.stroke()}},__change:function(){this.canvas.__overlay_objects.__changed=!0}}),property.declare(label.prototype,{pos:new attributeVector(null,0,0,0),color:{value:null,type:property.nullable_attributeVector},line:{value:!0,onchanged:function(){this.__change()}},linecolor:{value:null,type:property.nullable_attributeVector},background:{value:null,type:property.nullable_attributeVector},opacity:{value:.66,onchanged:function(){this.__change()}},text:{value:"",onchanged:function(){this.__change()}},font:{value:"Verdana",onchanged:function(){this.__change()}},height:{value:13,onchanged:function(){this.__change()}},visible:{value:!1,onchanged:function(){this.__change()}},align:{value:"center",onchanged:function(){this.__change()}},box:{value:!0,onchanged:function(){this.__change()}},border:{value:5,onchanged:function(){this.__change()}},linewidth:{value:1,onchanged:function(){this.__change()}},xoffset:{value:0,onchanged:function(){this.__change()}},yoffset:{value:0,onchanged:function(){this.__change()}},space:{value:0,onchanged:function(){this.__change()}},pixel_pos:{value:!1,onchanged:function(){this.__change()}},__get_model:function(){return null},__update:function(a,b){var c=this.__xoffset,d=this.__yoffset,e,f;if(this.__pixel_pos)e=this.__pos.x,f=this.__pos.y,d=-d;else{if(this.canvas.__width>=this.canvas.__height)var g=2*this.canvas.__range/this.canvas.__height;else var g=2*this.canvas.__range/this.canvas.__width;var h=mat4.multiplyVec3(mat4.rotateY(mat4.rotateX(mat4.identity(mat4.create()),b.angleY),b.angleX),vec3.create([this.pos.x-this.canvas.center.x,this.pos.y-this.canvas.center.y,this.pos.z-this.canvas.center.z])),i=b.distance,j=(1+h[2]/(i-h[2]))/g;e=Math.round(j*h[0]+this.canvas.__width/2),f=Math.round(-j*h[1]+this.canvas.__height/2)}a.font=this.__height+"px "+this.__font,a.textAlign="left",a.textBaseline="middle",a.lineWidth=this.__linewidth;var k=vec(1,1,1);this.canvas.__background.equals(vec(1,1,1))&&(k=vec(0,0,0)),a.strokeStyle=color.to_html(this.__linecolor||this.__color||k);var l=e,m=f,n=a.measureText(this.__text).width,o,p,q,r,s,t;o=Math.ceil(n),p=Math.ceil(this.__height+2*this.__border),q=Math.floor(l-this.__border)+.5,r=Math.ceil(m-.4*p)-.5;if(c||d){Math.abs(d)>Math.abs(c)?(d>0?(r-=d+p/2,m-=d+p/2,t=r+p):(r-=d-p/2,m-=d-p/2,t=r),l+=c-o/2,q=l-this.border,s=l+o/2):(c>0?(q+=c,l+=c,s=q):(l+=c-o,q=l-this.border,s=l+o+this.border),m-=d,r-=d,t=r+p/2);if(this.__line){a.beginPath();if(this.space>0){var u=vec(s-e,t-f,0).norm().multiply(j*this.space);u=u.add(vec(e,f,0)),a.moveTo(u.x,u.y)}else a.moveTo(e,f);a.lineTo(s,t),a.stroke()}}else switch(this.__align){case"center":l-=n/2,q-=n/2;break;case"right":l-=n,q-=n}o+=2*this.__border;var v;this.__background==null?v=this.canvas.__background:v=this.__background,a.fillStyle=color.to_html_rgba(v,this.__opacity),a.fillRect(q,r,o,p),this.__box&&(a.beginPath(),a.moveTo(q,r),a.lineTo(q+o,r),a.lineTo(q+o,r+p),a.lineTo(q,r+p),a.closePath(),a.stroke()),a.fillStyle=color.to_html(this.__color||k),a.fillText(this.__text,l,m)},__change:function(){this.canvas!==undefined&&(this.canvas.__overlay_objects.__changed=!0)}}),eval("0");var exports={box:box,cylinder:cylinder,cone:cone,pyramid:pyramid,sphere:sphere,arrow:arrow,curve:curve,helix:helix,ring:ring,compound:compound,vertex:vertex,triangle:triangle,quad:quad,draw:draw,label:label,distant_light:distant_light,local_light:local_light,attach_trail:attach_trail,attach_arrow:attach_arrow,textures:textures,bumpmaps:bumpmaps};Export(exports)}(),function(){function a(a,b){function g(){if(d){b();return}var c=(new Date).getTime();if(c-e>3e3){var h=confirm("Timed out trying to access the library\n  "+a+"\nTry again?");if(h)e=(new Date).getTime();else{b();return}}f(50,g)}var c=0;if(b===undefined)throw new Error("get_display(URL, wait) called without wait");var d=!1,e=(new Date).getTime();$.getScript(a).done(function(a,b){d=!0}).fail(function(c,d,e){alert("Could not access the library\n  "+a),b();return}),g()}function b(a,b){function e(a){var b,e;b=a.target.files[0];if(b)return e=new FileReader,e.onload=function(a){d=a.target.result;var e=b.lastModifiedDate?b.lastModifiedDate.toLocaleDateString():"";c={name:b.name,text:d,size:b.size,type:b.type,date:e},$("#read_local_file").remove()},e.readAsText(b);alert("Failed to load file");return}function g(){if(d!==null){b(null,c);return}f(50,g)}var c;if(arguments.length===0)throw new Error("read_local_file(place, wait) called with no arguments");if(arguments.length==1){b=a;if(m(b)!=="function")throw new Error("Should be 'read_local_file(wait)'");a=$("body")}else if(arguments.length>2||m(b)!=="function")throw new Error("Should be 'read_local_file(place, wait)'");a.append('<input type="file" id="read_local_file"/>');var d=null;document.getElementById("read_local_file").addEventListener("change",e,!1),g()}function e(a){f(0,a)}function f(a,b){function c(){b()}setTimeout(c,a)}function l(a,b){d++;if(b===undefined)throw new Error("rate(iterations_per_second, wait) called without wait");var c=Math.round(1e3/a),e=(new Date).getTime();if(c>=h)g+c>e?(g+=c,f(g-e,b)):(k++,g+=c,e>g&&(g=e),f(0,b));else{var l=g+h-e;if(i!==null||0<l&&l<=h){if(i===null){i=0,j=Math.round(a/100)-1,b();return}i++;if(i<j&&e<g+h){b();return}i=null,g+=h,l>0?f(l,b):(g=e,b())}else k++,i=null,g+=h,e>g+100&&(g=e),b()}}function n(a){return a instanceof vec?a=a.toString():a===null?a="null":a===undefined?a="undefined":m(a)=="object"?a="<Object>":m(a)=="number"?(a=a.toPrecision(6),a.match(/e/)?(a=a.replace(/0*e/,"e"),a=a.replace(/\.e/,"e")):a.match(/\./)&&(a=a.replace(/0*$/,"")),a=a.replace(/\.$/,"")):a=a.toString(),a}function q(){var a=p.width===undefined?400:p.width,b=p.height===undefined?100:p.height,c=p.readonly===undefined?!0:p.readonly;o.css("width",a).css("height",b),c?o.attr("readonly","readonly"):o.attr("readonly",null)}function r(a){o===null&&(o=$("<textarea id=print/>").appendTo($("body")),q());var b=" ",c="\n",d=arguments.length,e=arguments[d-1];if(e!==undefined){var f=!1;e.sep!==undefined&&(b=e.sep,f=!0),e.end!==undefined&&(c=e.end,f=!0),f&&d--}var g="";for(var h=0;h<d;h++){var e=arguments[h];if(m(e)=="array"){var i="[";for(var h=0;h<e.length;h++)i+=n(e[h]),h<e.length-1&&(i+=", ");i+="]",e=i}else e=n(e);g.length===0?g+=e:g+=b+e}g+=c,o.val(o.val()+g)}function s(a){var b="";for(var c in a)p[c]=a[c];return a.clear!==undefined&&o!==null&&o.val(""),o!==null&&(a.clear!==undefined&&a.clear&&o.val(""),q(),a.contents!==undefined&&a.contents&&(b=o.val()),a.delete!==undefined&&a.delete&&(o.remove(),o=null)),b}"use strict",$.fn.waitfor=function(a,b){function d(e){c.unbind(a,d),b(null,e)}var c=this;this.bind(a,d)},$.fn.pause=function(a,b){function d(e){a.visible=!1,c.unbind("click",d),b(null,e)}var c=this;this.bind("click",d)};var c=0,d=0,g=0,h=10,i=null,j,k=0,m=function(a){return{}.toString.call(a).match(/\s([a-zA-Z]+)/)[1].toLowerCase()},o=null,p={width:400,height:100,readonly:!0},t={sqrt:Math.sqrt,pi:Math.PI,abs:Math.abs,sin:Math.sin,cos:Math.cos,tan:Math.tan,asin:Math.asin,acos:Math.acos,atan:Math.atan,atan2:Math.atan2,exp:Math.exp,log:Math.log,pow:Math.pow,radians:function(a){return a*Math.PI/180},degrees:function(a){return a*180/Math.PI},sleep:f,rate:l,update:e,print:r,print_options:s,get_library:a,read_local_file:b};Export(t)}(),Export({shaders:{curve_pick_vertex:'// Vertex shader for rendering curve segments, parameterized by\n// pos1, pos2, radius, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec4 pos;       // pos.w is 0 at the beginning of the segment and 1 at the end; \n                          // pos.xyz are relative to that end in an normal basis with x pointing along the segment and scaled by radius\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectRadius objectData[3].w\n#define objectColor objectData[4].rgb\n\nuniform vec4 segmentData[4];\n#define segmentPosR(i) segmentData[i]\n#define segmentColor(i) segmentData[2+i]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nvec4 start;\nvec4 end;\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nmat3 getSegmentRotation() {\n    // Construct the object rotation matrix.\n    vec3 v = end.xyz - start.xyz;\n    float vmax = max( max( abs(v.x), abs(v.y) ), abs(v.z) );\n    vec3 X = normalize(v/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    vec4 start_color = segmentColor(0);\n    vec4 end_color = segmentColor(1);\n    if (start_color.r < 0.0) start_color.rgb = objectColor;\n    if (end_color.r < 0.0) end_color.rgb = objectColor.rgb;\n    \n    // The following code looks very clumsy, but all other more sensible schemes \n    // failed due to what might be bugs in shader compiling or execution.\n    // Specifically, trying to set start or end inside the if statement fails\n    // if the curve radius is less than about 1e-7 !!??\n    float sw = 0.0;\n    if (segmentPosR(0).w < 0.0) {\n        sw = 1.0;\n    }\n    start = vec4(segmentPosR(0).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(0).w);\n    sw = 0.0;\n    if (segmentPosR(1).w < 0.0) {\n        sw = 1.0;\n    }\n    end = vec4(segmentPosR(1).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(1).w);\n    \n    mat3 rotObject = getObjectRotation();\n    start.xyz = rotObject*(objectScale*start.xyz) + objectPos;\n    end.xyz = rotObject*(objectScale*end.xyz) + objectPos;\n\n    // A rotation matrix with x pointed along the segment\n    mat3 rot = getSegmentRotation();\n\n    // The position and radius of "this" end of the segment in world space\n    vec4 ws_segmentEnd = start * (1.-pos.w) + end * pos.w;\n\n    // The position of this vertex in world space\n    vec3 ws_pos = ws_segmentEnd.xyz + rot * (ws_segmentEnd.w*pos.xyz);\n\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vcolor = start_color * (1.-pos.w) + end_color * pos.w;\n    gl_Position = projMatrix * pos4;\n}\n',curve_render_vertex:'// Vertex shader for rendering curve segments, parameterized by\n// pos1, pos2, radius, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec4 pos;       // pos.w is 0 at the beginning of the segment and 1 at the end; \n                          // pos.xyz are relative to that end in a normal basis with x pointing along the segment and scaled by radius\nattribute vec3 normal;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectRadius objectData[3].w\n#define objectColor objectData[4].rgb\n\nuniform vec4 segmentData[4];\n#define segmentPosR(i) segmentData[i]\n#define segmentColor(i) segmentData[2+i]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nvec4 start;\nvec4 end;\n   \nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nmat3 getSegmentRotation() {\n  // Construct the segment rotation matrix.\n    vec3 v = end.xyz - start.xyz;\n    float vmax = max( max( abs(v.x), abs(v.y) ), abs(v.z) );\n    vec3 X = normalize(v/vmax);\n    vec3 Z = cross(X,normalize(vec3(0,1,0)));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    vec4 start_color = segmentColor(0);\n    vec4 end_color = segmentColor(1);\n    if (start_color.r < 0.0) start_color.rgb = objectColor;\n    if (end_color.r < 0.0) end_color.rgb = objectColor.rgb;\n    \n    // The following code looks very clumsy, but all other more sensible schemes \n    // failed due to what might be bugs in shader compiling or execution.\n    // Specifically, trying to set start.w or end.w inside the if statement fails\n    // if the curve radius is less than about 1e-7. After setting the value\n    // inside the if, it\'s zero upon exit from the if. !!?!\n    float sw = 0.0;\n    if (segmentPosR(0).w < 0.0) { // -1 means use the curve global radius\n        sw = 1.0;\n    }\n    start = vec4(segmentPosR(0).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(0).w);\n    sw = 0.0;\n    if (segmentPosR(1).w < 0.0) {\n        sw = 1.0;\n    }\n    end = vec4(segmentPosR(1).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(1).w);\n    \n    mat3 rotObject = getObjectRotation();\n    start.xyz = rotObject*(objectScale*start.xyz) + objectPos;\n    end.xyz = rotObject*(objectScale*end.xyz) + objectPos;\n\n    // A rotation matrix with x pointed along the segment\n    mat3 rot = getSegmentRotation();\n\n    // The position and radius of "this" end of the segment in world space\n    vec4 ws_segmentEnd = start * (1.-pos.w) + end * pos.w;\n\n    // The position of this vertex in world space\n    vec3 ws_pos = ws_segmentEnd.xyz + rot * (ws_segmentEnd.w*pos.xyz);\n\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot * normal, 0.0)).xyz;\n    \n    // no texture or bump map yet for curve object:\n    parameters = vec4(objectShininess, objectEmissive, 0.0, 0.0);\n    mat_pos = vec2(0.0, 0.0);\n    bumpX = vec3(1.0, 0.0, 0.0);\n    \n    vcolor = start_color * (1.-pos.w) + end_color * pos.w;\n    gl_Position = projMatrix * pos4;\n}',extent_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, scale, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\nuniform vec3 center;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nvec3 encode_float(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec3(0.0, 0.0, 0.0);\n    float logk = log(k);\n    if (logk < 0.0) {\n        logk = -logk + 128.0;\n    }\n    return vec3(\n        floor(logk)/255.0,\n        floor(256.0*fract(logk))/255.0,\n        floor(256.0*fract(256.0*logk))/255.0);\n}\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.  A waste to do this per vertex, but GPU >> CPU\n    vec3 X = normalize(objectAxis);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*position) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot*normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(rot*bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    float extent = abs(ws_pos.x-center.x);\n    extent = max(abs(ws_pos.y-center.y), extent);\n    extent = max(abs(ws_pos.z-center.z), extent);\n    mat_color = vec4(encode_float(extent), 1.0);\n    // Setting gl_Position.xy to (-1.0, -1.0) should store into pixel (0, 0), but doesn't work:\n    gl_Position = vec4(-1.0, -1.0, 1e-20*extent, 1.0);\n    //gl_Position = posp;\n    parameters = vec4(objectShininess, objectEmissive, 0.0, 0.0);\n}\n",final_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D FINAL;\nvarying vec2 mat_pos;\n\nvoid main(void) {\n    gl_FragColor = vec4( texture2D(FINAL, mat_pos).xyz, 1.0);\n}\n",merge_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D C0; // TEXTURE2 - opaque color map (minormode 4)\nuniform sampler2D C1; // TEXTURE6 - color map for transparency render 1 (minormode 9)\nuniform sampler2D C2; // TEXTURE7 - color map for transparency render 2 (minormode 10)\nuniform sampler2D C3; // TEXTURE8 - color map for transparency render 3 (minormode 11)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 8); also used for C4\nuniform vec2 canvas_size;\n\nvoid main(void) {\n    // need to combine colors from C0, C1, C2, C3, C4\n    vec2 loc = vec2( gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    vec4 c0 = texture2D(C0, loc);\n    vec4 c1 = texture2D(C1, loc);\n    vec4 c2 = texture2D(C2, loc);\n    vec4 c3 = texture2D(C3, loc);\n    vec4 c4 = texture2D(D2, loc);\n    vec3 mcolor = c1.rgb*c1.a + \n                 (1.0-c1.a)*(c2.rgb*c2.a +\n                 (1.0-c2.a)*(c3.rgb*c3.a +\n                 (1.0-c3.a)*(c4.rgb*c4.a + \n                 (1.0-c4.a)*c0.rgb)));\n    gl_FragColor = vec4 (mcolor, 1.0);\n}\n",merge_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nvoid main(void) {\n    gl_Position = vec4(pos, 1.0);\n}\n",opaque_render_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )\n{\n    vec3 color;\n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return color;\n    }\n    color = light_ambient * diffuse_color;\n\n    for(int i=0; i<8; i++) {\n        if (i >= light_count) break;\n        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light\n        L = normalize(L);\n        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;\n        if (shininess > 0.0) {\n            vec3 R = reflect(L,normal);\n            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n        }\n    }\n    return color;\n}\n\nvoid main(void) {\n    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    gl_FragColor = vec4( color, 1.0 );\n}\n",peel_color_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\nuniform vec2 canvas_size;\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )\n{\n    vec3 color;\n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return color;\n    }\n    color = light_ambient * diffuse_color;\n\n    for(int i=0; i<8; i++) {\n        if (i >= light_count) break;\n        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light\n        L = normalize(L);\n        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;\n        if (shininess > 0.0) {\n            vec3 R = reflect(L,normal);\n            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n        }\n    }\n    return color;\n}\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create transparency color map - C1 (9), C2 (10), C3 (11), C4 (12)\n    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    float z = decode(encode(1.0-gl_FragCoord.z)); // bigger number => closer to camera; distance out of screen\n    float zmin = decode(texture2D(D0, loc));\n    float zmax;\n    if (minormode == 9) { // C1\n        if (z > zmin) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    } else {\n        if (minormode == 11) { // C3 (11)\n            zmax = decode(texture2D(D2, loc));\n        } else { // C2 (10) or C4 (12)\n            zmax = decode(texture2D(D1, loc));\n        }\n        if (zmin < z && z < zmax) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    }\n}\n",peel_depth_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\n// Construct depth maps for depth peeling handling of opacity\n\nuniform vec2 canvas_size;\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create depth map, D0 (5); ping-pong D1 (6) and D2 (7)\n    vec4 c = encode(1.0-gl_FragCoord.z);\n    float z = decode(c);\n    if (minormode == 5) { // D0\n        gl_FragColor = c;\n    } else {\n        vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n        float zmin = decode(texture2D(D0, loc));\n        float zmax;\n        if (minormode == 6) { // first creation of D1\n            if (z > zmin) {\n                gl_FragColor = c;\n            } else {\n                discard;\n            }\n        } else {\n            if (minormode == 7) {\n                zmax = decode(texture2D(D1, loc)); // create D2\n            } else {\n                zmax = decode(texture2D(D2, loc)); // D1b; create D1 again\n            }\n            if (zmin < z && z < zmax) {\n                gl_FragColor = c;\n            } else {\n                discard; // All pixels are discarded, since (zmax < z && z < zmax) is always false\n            }\n        }\n    }\n}"
-,peel_depth_vertex:"// Vertex shader for picking standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectAxis objectData[1].xyz\n#define objectUp objectData[2].xyz\n#define objectScale objectData[3].xyz\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nmat3 getObjectRotation() {\n  // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n}",pick_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nvarying vec4 vcolor;\n\nvoid main(void) {\n    gl_FragColor = vcolor;\n}\n",pick_vertex:"// Vertex shader for picking standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectAxis objectData[1].xyz\n#define objectUp objectData[2].xyz\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nmat3 getObjectRotation() {\n  // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n    vcolor = objectColor;\n}\n",render_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot*normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(rot*bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    vcolor = vec4(color*objectColor.rgb, opacity*objectColor.a);\n    gl_Position = posp;\n    float f = flags; // turn, flipy, flipx, sides, right, left, bumpmap, texture\n    float turn = floor(f/128.0);\n    f -= 128.0*turn;\n    float flipy = floor(f/64.0);\n    f -= 64.0*flipy;\n    float flipx = floor(f/32.0);\n    f -= 32.0*flipx;\n    float sides = floor(f/16.0);\n    f -= 16.0*sides;\n    float right = floor(f/8.0);\n    f -= 8.0*right;\n    float left = floor(f/4.0);\n    f -= 4.0*left;\n    float B = floor(f/2.0);\n    f -= 2.0*B;\n    float T = f;\n    if (T != 0.0) {\n        if (flipx != 0.0) {\n            mat_pos.x = 1.0 - mat_pos.x;\n        }\n        if (flipy != 0.0) {\n            mat_pos.y = 1.0 - mat_pos.y;\n        }\n        if (turn > 0.0 && turn <= 3.0) {\n            if (turn == 1.0) {\n                mat_pos = vec2(mat_pos.y,1.0 - mat_pos.x);\n            } else if (turn == 2.0) {\n                mat_pos = vec2(1.0 - mat_pos.x,1.0 - mat_pos.y);\n            } else {\n                mat_pos = vec2(1.0 - mat_pos.y,mat_pos.x);\n            }\n        }\n        T = 0.0;\n        bool L = (normal.x == -1.0);\n        bool R = (normal.x == 1.0);\n        bool S = !L && !R;\n        if (L && left == 1.0) T = 1.0;\n        if (R && right == 1.0) T = 1.0;\n        if (S && sides == 1.0) T = 1.0;\n        if (T == 0.0) {\n            B = 0.0;\n        } else if (left == 0.0 || right == 0.0 || sides == 0.0) {\n            // don't mix texture and object color if texture doesn't cover entire object\n            vcolor = vec4(1.0, 1.0, 1.0, 1.0);\n        }\n    }\n    parameters = vec4(objectShininess, objectEmissive, T, B);\n}\n",tri_pick_vertex:"// Vertex shader for picking triangles\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec4 color;\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nvoid main(void) {\n    vec3 normal = vec3(0.0, 0.0, 1.0);\n    vec4 pos4 = viewMatrix * vec4( pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n    vcolor = color;\n}\n",tri_render_vertex:"// Vertex shader for rendering triangles\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\nattribute vec2 params; // shininess, emissive\n\n#define shininess params[0]\n#define emissive params[1]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\nuniform float T; // 1.0 if there is a texture, else 0.0\nuniform float B; // 1.0 if there is a bumpmap, else 0.0\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n\nvoid main(void) {\n    vec4 pos4 = viewMatrix * vec4( pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    vcolor = vec4(color, opacity);\n    gl_Position = posp;\n    parameters = vec4(shininess, emissive, T, B);\n}\n"}})/*This is a combined, compressed file.  Look at https://bitbucket.org/davidscherer/glowscript for source code and copyright information.*/
+.__size.z*m.z);this.__pos=q,this.__pos.__x=q.x,this.__pos.__y=q.y,this.__pos.__z=q.z,this.__size=r,this.__size.__x=r.x,this.__size.__y=r.y,this.__size.__z=r.z,Autoscale.find_extent(this,a),this.__pos=n,this.__size=o},push:function(a){var b=[],c;if(a.length!==undefined)b=a;else for(var d=0;d<arguments.length;d++)b.push(arguments[d]);for(var d=0;d<b.length;d++){c=point(b[d]),c.__curve=this,c.__id=nextVisibleId++,this.canvas.__visiblePrimitives[c.__id]=c,c.__falsecolor=id_to_falsecolor(c.__id);if(this.__points.length){var e=this.__points[this.__points.length-1],f=c.__prevsegment=e.__nextsegment=new Float32Array(16);f[11]=f[15]=1,e.__change()}this.__points.push(c),c.__change()}this.__change()},pop:function(){var a=this.__points.pop();return a.visible=!1,this.__change(),{pos:a.pos,color:a.color,radius:a.radius,visible:a.visible}},clear:function(){this.splice(0,this.__points.length),this.__change()},shift:function(){var a=this.__points.shift();return a.visible=!1,this.__change(),{pos:a.pos,color:a.color,radius:a.radius,visible:a.visible}},unshift:function(a){var b=[];if(a.length!==undefined)b=a;else for(var c=0;c<arguments.length;c++)b.push(arguments[c]);this.splice(0,0,b),this.__change()},splice:function(a){var b=arguments[0],c=arguments[1],d=[];for(var e=2;e<arguments.length;e++)d.push(arguments[e]);d.length==1&&d[0].length!==undefined&&(d=d[0]);var f=this.__points.slice(b+c),g=[];for(var e=0;e<f.length;e++)g.push({pos:f[e].pos,color:f[e].color,radius:f[e].radius,visible:f[e].visible});for(var e=b;e<this.__points.length;e++)this.__points[e].visible=!1;this.__points.splice(b),this.push(d),this.push(g),this.__change()},modify:function(a,b){b instanceof vec&&(b={pos:b});for(var c in b)c=="x"&&(this.__points[a].pos.x=b[c]),c=="y"&&(this.__points[a].pos.y=b[c]),c=="z"?this.__points[a].pos.z=b[c]:this.__points[a][c]=b[c];this.__points[a].__change(),this.__change()},slice:function(a,b){var c=this.__points.slice(a,b),d=[];for(var e=0;e<c.length;e++)d.push({pos:c[e].pos,color:c[e].color,radius:c[e].radius,visible:c[e].visible});return d}}),property.declare(point.prototype,{__curve:null,pos:new attributeVector(null,0,0,0),color:new attributeVector(null,-1,-1,-1),radius:{value:-1,onchanged:function(){this.__change()}},pickable:{value:!0,onchanged:function(){this.__change()}},visible:{value:!0,onchanged:function(){this.__change()}},__change:function(){this.__id&&(this.__curve.canvas.__changed[this.__id]=this,this.__curve.canvas.__changed[this.__curve.__id]=this.__curve)},__update:function(){var a=this.__pos,b=this.radius||-1,c=this.color||vec(-1,-1,-1),d=this.__prevsegment;d&&(d[4]=a.x,d[5]=a.y,d[6]=a.z,d[7]=b,d[12]=c.x,d[13]=c.y,d[14]=c.z),d=this.__nextsegment,d&&(d[0]=a.x,d[1]=a.y,d[2]=a.z,d[3]=b,d[8]=c.x,d[9]=c.y,d[10]=c.z)}}),subclass(helix,cylinder),property.declare(helix.prototype,{__initialize:!0,thickness:{value:0,onchanged:function(){this.__change()}},coils:{value:5,onchanged:function(){this.__initialize=!0,this.__change()}},__update:function(){var a=20;this.__initialize&&(this.__curve!==undefined?this.__curve.clear():this.__curve=curve({__no_autoscale:!0}));var b=this.__curve;b.pos=this.__pos,b.axis=this.__axis,b.up=this.__up,b.size=this.__size,b.color=this.__color,b.radius=this.__thickness?this.__thickness/2:this.__size.y/40;if(!this.__initialize)return;var c=vec(1,0,0),d=vec(0,1,0),e=vec(0,0,1),f=.5,g=this.__coils*a,h=1/g,i=Math.sin(2*Math.PI/a),j=Math.cos(2*Math.PI/a),k=0,l=0,m=f,n;for(var o=0;o<g+1;o++)b.push(vec(k,l,m)),k+=h,n=m*j-l*i,l=l*j+m*i,m=n;this.__initialize=!1}}),subclass(ring,box),property.declare(ring.prototype,{__initialize:!0,__hasPosAtCenter:!0,__size:new attributeVector(null,.1,1,1),__update:function(){if(this.__initialize)if(this.__ring!==undefined){for(var a=0;a<this.__ring.__points.length;a++)this.__ring.__points[a].pos=this.__pos;this.__ring.__update(),this.__ring.__points=[]}else this.__ring=curve({__no_autoscale:!0}),this.__ring.__id=nextVisibleId++;var b=this.__ring;b.color=this.__color,b.radius=this.__size.x/2;var c=this.__axis.norm(),d,e;e=c.cross(this.__up),Math.abs(e.dot(e))<1e-10&&(e=c.cross(vec(1,0,0)),Math.abs(e.dot(e))<1e-10&&(e=c.cross(vec(0,1,0)))),e=e.norm(),d=e.cross(c);var f=100,g=(this.__size.y-this.__size.x)/2,h=g,i=0,j,k=Math.sin(2*Math.PI/f),l=Math.cos(2*Math.PI/f),m=this.__pos;for(var a=0;a<=f;a++){var n=m.add(d.multiply(i)).add(e.multiply(h));this.__initialize?b.push(point(n)):b.__points[a].pos=n,j=i*l+h*k,h=h*l-i*k,i=j}b.__update(),this.__initialize=!1}}),property.declare(distant_light.prototype,{direction:new attributeVector(null,0,0,1),color:new attributeVector(null,1,1,1),__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__change:function(){}}),property.declare(local_light.prototype,{pos:new attributeVector(null,0,0,0),color:new attributeVector(null,1,1,1),__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__change:function(){}}),property.declare(draw.prototype,{color:{value:null,type:property.nullable_attributeVector},fillcolor:{value:null,type:property.nullable_attributeVector},linewidth:{value:1,onchanged:function(){this.__change()}},opacity:{value:.66,onchanged:function(){this.__change()}},visible:{value:!1,onchanged:function(){this.__change()}},__get_model:function(){return this.canvas.__renderer.models[this.constructor.name]},__update:function(a,b){var c=this.points.length;if(c<2)return;if(this.fillcolor!=null){a.lineWidth=1,a.fillStyle=color.to_html_rgba(this.fillcolor,this.opacity),a.beginPath();for(var d=0;d<c;d++)d==0?a.moveTo(this.points[d].x,this.points[d].y):a.lineTo(this.points[d].x,this.points[d].y);a.fill()}if(this.color!=null){a.lineWidth=this.linewidth,a.strokeStyle=color.to_html_rgba(this.color,this.opacity),a.beginPath();for(var d=0;d<c;d++)d==0?a.moveTo(this.points[d].x,this.points[d].y):d==c-1&&this.points[d].equals(this.points[0])?a.closePath():a.lineTo(this.points[d].x,this.points[d].y);a.stroke()}},__change:function(){this.canvas.__overlay_objects.__changed=!0}}),property.declare(label.prototype,{pos:new attributeVector(null,0,0,0),color:{value:null,type:property.nullable_attributeVector},line:{value:!0,onchanged:function(){this.__change()}},linecolor:{value:null,type:property.nullable_attributeVector},background:{value:null,type:property.nullable_attributeVector},opacity:{value:.66,onchanged:function(){this.__change()}},text:{value:"",onchanged:function(){this.__change()}},font:{value:"Verdana",onchanged:function(){this.__change()}},height:{value:13,onchanged:function(){this.__change()}},visible:{value:!1,onchanged:function(){this.__change()}},align:{value:"center",onchanged:function(){this.__change()}},box:{value:!0,onchanged:function(){this.__change()}},border:{value:5,onchanged:function(){this.__change()}},linewidth:{value:1,onchanged:function(){this.__change()}},xoffset:{value:0,onchanged:function(){this.__change()}},yoffset:{value:0,onchanged:function(){this.__change()}},space:{value:0,onchanged:function(){this.__change()}},pixel_pos:{value:!1,onchanged:function(){this.__change()}},__get_model:function(){return null},__update:function(a,b){var c=this.__xoffset,d=this.__yoffset,e,f;if(this.__pixel_pos)e=this.__pos.x,f=this.__pos.y,d=-d;else{if(this.canvas.__width>=this.canvas.__height)var g=2*this.canvas.__range/this.canvas.__height;else var g=2*this.canvas.__range/this.canvas.__width;var h=mat4.multiplyVec3(mat4.rotateY(mat4.rotateX(mat4.identity(mat4.create()),b.angleY),b.angleX),vec3.create([this.pos.x-this.canvas.center.x,this.pos.y-this.canvas.center.y,this.pos.z-this.canvas.center.z])),i=b.distance,j=(1+h[2]/(i-h[2]))/g;e=Math.round(j*h[0]+this.canvas.__width/2),f=Math.round(-j*h[1]+this.canvas.__height/2)}a.font=this.__height+"px "+this.__font,a.textAlign="left",a.textBaseline="middle",a.lineWidth=this.__linewidth;var k=vec(1,1,1);this.canvas.__background.equals(vec(1,1,1))&&(k=vec(0,0,0)),a.strokeStyle=color.to_html(this.__linecolor||this.__color||k);var l=e,m=f,n=a.measureText(this.__text).width,o,p,q,r,s,t;o=Math.ceil(n),p=Math.ceil(this.__height+2*this.__border),q=Math.floor(l-this.__border)+.5,r=Math.ceil(m-.4*p)-.5;if(c||d){Math.abs(d)>Math.abs(c)?(d>0?(r-=d+p/2,m-=d+p/2,t=r+p):(r-=d-p/2,m-=d-p/2,t=r),l+=c-o/2,q=l-this.border,s=l+o/2):(c>0?(q+=c,l+=c,s=q):(l+=c-o,q=l-this.border,s=l+o+this.border),m-=d,r-=d,t=r+p/2);if(this.__line){a.beginPath();if(this.space>0){var u=vec(s-e,t-f,0).norm().multiply(j*this.space);u=u.add(vec(e,f,0)),a.moveTo(u.x,u.y)}else a.moveTo(e,f);a.lineTo(s,t),a.stroke()}}else switch(this.__align){case"center":l-=n/2,q-=n/2;break;case"right":l-=n,q-=n}o+=2*this.__border;var v;this.__background==null?v=this.canvas.__background:v=this.__background,a.fillStyle=color.to_html_rgba(v,this.__opacity),a.fillRect(q,r,o,p),this.__box&&(a.beginPath(),a.moveTo(q,r),a.lineTo(q+o,r),a.lineTo(q+o,r+p),a.lineTo(q,r+p),a.closePath(),a.stroke()),a.fillStyle=color.to_html(this.__color||k),a.fillText(this.__text,l,m)},__change:function(){this.canvas!==undefined&&(this.canvas.__overlay_objects.__changed=!0)}}),eval("0");var exports={box:box,cylinder:cylinder,cone:cone,pyramid:pyramid,sphere:sphere,arrow:arrow,curve:curve,helix:helix,ring:ring,compound:compound,vertex:vertex,triangle:triangle,quad:quad,draw:draw,label:label,distant_light:distant_light,local_light:local_light,attach_trail:attach_trail,attach_arrow:attach_arrow,textures:textures,bumpmaps:bumpmaps};Export(exports)}(),function(){function a(a,b){function g(){if(d){b();return}var c=(new Date).getTime();if(c-e>3e3){var h=confirm("Timed out trying to access the library\n  "+a+"\nTry again?");if(h)e=(new Date).getTime();else{b();return}}f(50,g)}var c=0;if(b===undefined)throw new Error("get_display(URL, wait) called without wait");var d=!1,e=(new Date).getTime();$.getScript(a).done(function(a,b){d=!0}).fail(function(c,d,e){alert("Could not access the library\n  "+a),b();return}),g()}function b(a,b){function e(a){var b,e;b=a.target.files[0];if(b)return e=new FileReader,e.onload=function(a){d=a.target.result;var e=b.lastModifiedDate?b.lastModifiedDate.toLocaleDateString():"";c={name:b.name,text:d,size:b.size,type:b.type,date:e},$("#read_local_file").remove()},e.readAsText(b);alert("Failed to load file");return}function g(){if(d!==null){b(null,c);return}f(50,g)}var c;if(arguments.length===0)throw new Error("read_local_file(place, wait) called with no arguments");if(arguments.length==1){b=a;if(m(b)!=="function")throw new Error("Should be 'read_local_file(wait)'");a=$("body")}else if(arguments.length>2||m(b)!=="function")throw new Error("Should be 'read_local_file(place, wait)'");a.append('<input type="file" id="read_local_file"/>');var d=null;document.getElementById("read_local_file").addEventListener("change",e,!1),g()}function e(a){f(0,a)}function f(a,b){function c(){b()}setTimeout(c,a)}function l(a,b){d++;if(b===undefined)throw new Error("rate(iterations_per_second, wait) called without wait");var c=Math.round(1e3/a),e=(new Date).getTime();if(c>=h)g+c>e?(g+=c,f(g-e,b)):(k++,g+=c,e>g&&(g=e),f(0,b));else{var l=g+h-e;if(i!==null||0<l&&l<=h){if(i===null){i=0,j=Math.round(a/100)-1,b();return}i++;if(i<j&&e<g+h){b();return}i=null,g+=h,l>0?f(l,b):(g=e,b())}else k++,i=null,g+=h,e>g+100&&(g=e),b()}}function n(a){return a instanceof vec?a=a.toString():a===null?a="null":a===undefined?a="undefined":m(a)=="object"?a="<Object>":m(a)=="number"?(a=a.toPrecision(6),a.match(/e/)?(a=a.replace(/0*e/,"e"),a=a.replace(/\.e/,"e")):a.match(/\./)&&(a=a.replace(/0*$/,"")),a=a.replace(/\.$/,"")):a=a.toString(),a}function q(){var a=p.width===undefined?400:p.width,b=p.height===undefined?100:p.height,c=p.readonly===undefined?!0:p.readonly;o.css("width",a).css("height",b),c?o.attr("readonly","readonly"):o.attr("readonly",null)}function r(a){o===null&&(o=$("<textarea id=print/>").appendTo($("body")),q());var b=" ",c="\n",d=arguments.length,e=arguments[d-1];if(e!==undefined){var f=!1;e.sep!==undefined&&(b=e.sep,f=!0),e.end!==undefined&&(c=e.end,f=!0),f&&d--}var g="";for(var h=0;h<d;h++){var e=arguments[h];if(m(e)=="array"){var i="[";for(var h=0;h<e.length;h++)i+=n(e[h]),h<e.length-1&&(i+=", ");i+="]",e=i}else e=n(e);g.length===0?g+=e:g+=b+e}g+=c,o.val(o.val()+g)}function s(a){var b="";for(var c in a)p[c]=a[c];return a.clear!==undefined&&o!==null&&o.val(""),o!==null&&(a.clear!==undefined&&a.clear&&o.val(""),q(),a.contents!==undefined&&a.contents&&(b=o.val()),a.delete!==undefined&&a.delete&&(o.remove(),o=null)),b}"use strict",$.fn.waitfor=function(a,b){function d(e){c.unbind(a,d),b(null,e)}var c=this;this.bind(a,d)},$.fn.pause=function(a,b){function d(e){a.visible=!1,c.unbind("click",d),b(null,e)}var c=this;this.bind("click",d)};var c=0,d=0,g=0,h=10,i=null,j,k=0,m=function(a){return{}.toString.call(a).match(/\s([a-zA-Z]+)/)[1].toLowerCase()},o=null,p={width:400,height:100,readonly:!0},t={sqrt:Math.sqrt,pi:Math.PI,abs:Math.abs,sin:Math.sin,cos:Math.cos,tan:Math.tan,asin:Math.asin,acos:Math.acos,atan:Math.atan,atan2:Math.atan2,exp:Math.exp,log:Math.log,pow:Math.pow,radians:function(a){return a*Math.PI/180},degrees:function(a){return a*180/Math.PI},sleep:f,rate:l,update:e,print:r,print_options:s,get_library:a,read_local_file:b};Export(t)}(),Export({shaders:{curve_pick_vertex:'// Vertex shader for rendering curve segments, parameterized by\n// pos1, pos2, radius, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec4 pos;       // pos.w is 0 at the beginning of the segment and 1 at the end; \n                          // pos.xyz are relative to that end in an normal basis with x pointing along the segment and scaled by radius\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectRadius objectData[3].w\n#define objectColor objectData[4].rgb\n\nuniform vec4 segmentData[4];\n#define segmentPosR(i) segmentData[i]\n#define segmentColor(i) segmentData[2+i]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nvec4 start;\nvec4 end;\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nmat3 getSegmentRotation() {\n    // Construct the object rotation matrix.\n    vec3 v = end.xyz - start.xyz;\n    float vmax = max( max( abs(v.x), abs(v.y) ), abs(v.z) );\n    vec3 X = normalize(v/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    vec4 start_color = segmentColor(0);\n    vec4 end_color = segmentColor(1);\n    if (start_color.r < 0.0) start_color.rgb = objectColor;\n    if (end_color.r < 0.0) end_color.rgb = objectColor.rgb;\n    \n    // The following code looks very clumsy, but all other more sensible schemes \n    // failed due to what might be bugs in shader compiling or execution.\n    // Specifically, trying to set start or end inside the if statement fails\n    // if the curve radius is less than about 1e-7 !!??\n    float sw = 0.0;\n    if (segmentPosR(0).w < 0.0) {\n        sw = 1.0;\n    }\n    start = vec4(segmentPosR(0).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(0).w);\n    sw = 0.0;\n    if (segmentPosR(1).w < 0.0) {\n        sw = 1.0;\n    }\n    end = vec4(segmentPosR(1).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(1).w);\n    \n    mat3 rotObject = getObjectRotation();\n    start.xyz = rotObject*(objectScale*start.xyz) + objectPos;\n    end.xyz = rotObject*(objectScale*end.xyz) + objectPos;\n\n    // A rotation matrix with x pointed along the segment\n    mat3 rot = getSegmentRotation();\n\n    // The position and radius of "this" end of the segment in world space\n    vec4 ws_segmentEnd = start * (1.-pos.w) + end * pos.w;\n\n    // The position of this vertex in world space\n    vec3 ws_pos = ws_segmentEnd.xyz + rot * (ws_segmentEnd.w*pos.xyz);\n\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vcolor = start_color * (1.-pos.w) + end_color * pos.w;\n    gl_Position = projMatrix * pos4;\n}\n',curve_render_vertex:'// Vertex shader for rendering curve segments, parameterized by\n// pos1, pos2, radius, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec4 pos;       // pos.w is 0 at the beginning of the segment and 1 at the end; \n                          // pos.xyz are relative to that end in a normal basis with x pointing along the segment and scaled by radius\nattribute vec3 normal;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectRadius objectData[3].w\n#define objectColor objectData[4].rgb\n\nuniform vec4 segmentData[4];\n#define segmentPosR(i) segmentData[i]\n#define segmentColor(i) segmentData[2+i]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nvec4 start;\nvec4 end;\n   \nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nmat3 getSegmentRotation() {\n  // Construct the segment rotation matrix.\n    vec3 v = end.xyz - start.xyz;\n    float vmax = max( max( abs(v.x), abs(v.y) ), abs(v.z) );\n    vec3 X = normalize(v/vmax);\n    vec3 Z = cross(X,normalize(vec3(0,1,0)));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    vec4 start_color = segmentColor(0);\n    vec4 end_color = segmentColor(1);\n    if (start_color.r < 0.0) start_color.rgb = objectColor;\n    if (end_color.r < 0.0) end_color.rgb = objectColor.rgb;\n    \n    // The following code looks very clumsy, but all other more sensible schemes \n    // failed due to what might be bugs in shader compiling or execution.\n    // Specifically, trying to set start.w or end.w inside the if statement fails\n    // if the curve radius is less than about 1e-7. After setting the value\n    // inside the if, it\'s zero upon exit from the if. !!?!\n    float sw = 0.0;\n    if (segmentPosR(0).w < 0.0) { // -1 means use the curve global radius\n        sw = 1.0;\n    }\n    start = vec4(segmentPosR(0).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(0).w);\n    sw = 0.0;\n    if (segmentPosR(1).w < 0.0) {\n        sw = 1.0;\n    }\n    end = vec4(segmentPosR(1).xyz, sw*objectRadius + (1.0-sw)*segmentPosR(1).w);\n    \n    mat3 rotObject = getObjectRotation();\n    start.xyz = rotObject*(objectScale*start.xyz) + objectPos;\n    end.xyz = rotObject*(objectScale*end.xyz) + objectPos;\n\n    // A rotation matrix with x pointed along the segment\n    mat3 rot = getSegmentRotation();\n\n    // The position and radius of "this" end of the segment in world space\n    vec4 ws_segmentEnd = start * (1.-pos.w) + end * pos.w;\n\n    // The position of this vertex in world space\n    vec3 ws_pos = ws_segmentEnd.xyz + rot * (ws_segmentEnd.w*pos.xyz);\n\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot * normal, 0.0)).xyz;\n    \n    // no texture or bump map yet for curve object:\n    parameters = vec4(objectShininess, objectEmissive, 0.0, 0.0);\n    mat_pos = vec2(0.0, 0.0);\n    bumpX = vec3(1.0, 0.0, 0.0);\n    \n    vcolor = start_color * (1.-pos.w) + end_color * pos.w;\n    gl_Position = projMatrix * pos4;\n}',extent_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, scale, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\nuniform vec3 center;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nvec3 encode_float(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec3(0.0, 0.0, 0.0);\n    float logk = log(k);\n    if (logk < 0.0) {\n        logk = -logk + 128.0;\n    }\n    return vec3(\n        floor(logk)/255.0,\n        floor(256.0*fract(logk))/255.0,\n        floor(256.0*fract(256.0*logk))/255.0);\n}\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.  A waste to do this per vertex, but GPU >> CPU\n    vec3 X = normalize(objectAxis);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*position) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot*normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(rot*bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    float extent = abs(ws_pos.x-center.x);\n    extent = max(abs(ws_pos.y-center.y), extent);\n    extent = max(abs(ws_pos.z-center.z), extent);\n    mat_color = vec4(encode_float(extent), 1.0);\n    // Setting gl_Position.xy to (-1.0, -1.0) should store into pixel (0, 0), but doesn't work:\n    gl_Position = vec4(-1.0, -1.0, 1e-20*extent, 1.0);\n    //gl_Position = posp;\n    parameters = vec4(objectShininess, objectEmissive, 0.0, 0.0);\n}\n",final_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D FINAL;\nvarying vec2 mat_pos;\n\nvoid main(void) {\n    gl_FragColor = vec4( texture2D(FINAL, mat_pos).xyz, 1.0);\n}\n",merge_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform sampler2D C0; // TEXTURE2 - opaque color map (minormode 4)\nuniform sampler2D C1; // TEXTURE6 - color map for transparency render 1 (minormode 9)\nuniform sampler2D C2; // TEXTURE7 - color map for transparency render 2 (minormode 10)\nuniform sampler2D C3; // TEXTURE8 - color map for transparency render 3 (minormode 11)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 8); also used for C4\nuniform vec2 canvas_size;\n\nvoid main(void) {\n    // need to combine colors from C0, C1, C2, C3, C4\n    vec2 loc = vec2( gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    vec4 c0 = texture2D(C0, loc);\n    vec4 c1 = texture2D(C1, loc);\n    vec4 c2 = texture2D(C2, loc);\n    vec4 c3 = texture2D(C3, loc);\n    vec4 c4 = texture2D(D2, loc);\n    vec3 mcolor = c1.rgb*c1.a + \n                 (1.0-c1.a)*(c2.rgb*c2.a +\n                 (1.0-c2.a)*(c3.rgb*c3.a +\n                 (1.0-c3.a)*(c4.rgb*c4.a + \n                 (1.0-c4.a)*c0.rgb)));\n    gl_FragColor = vec4 (mcolor, 1.0);\n}\n",merge_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nvoid main(void) {\n    gl_Position = vec4(pos, 1.0);\n}\n",opaque_render_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n#define LP(i) light_pos[i]\n#define LC(i) light_color[i]\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\nvec3 normal;\nvec3 pos;\nvec3 diffuse_color;\nvec3 specular_color;\nvec3 color;\n\nvoid calc_color(vec4 lpos, vec3 lcolor)\n{\n    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light\n    L = normalize(L);\n    float N = max(dot(normal,L), 0.0);\n    color += (lcolor * N)*diffuse_color;\n    if (shininess > 0.0) {\n        vec3 R = reflect(L,normal);\n        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n    }\n}\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvoid lightAt()\n{    \n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return;\n    }\n    \n    color = light_ambient * diffuse_color;\n    \n    // It was necessary to restructure this shader completely in order to\n    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler\n    // does not handle for loops correctly.\n    if (light_count == 0) return;\n    calc_color(LP(0), LC(0));\n    if (light_count == 1) return;\n    calc_color(LP(1), LC(1));\n    if (light_count == 2) return;\n    calc_color(LP(2), LC(2));\n    if (light_count == 3) return;\n    calc_color(LP(3), LC(3));\n    if (light_count == 4) return;\n    calc_color(LP(4), LC(4));\n    if (light_count == 5) return;\n    calc_color(LP(5), LC(5));\n    if (light_count == 6) return;\n    calc_color(LP(6), LC(6));\n    if (light_count == 7) return;\n    calc_color(LP(7), LC(7));\n}\n\nvoid main(void) {\n    normal = normalize(es_normal);\n    pos = es_position;\n    diffuse_color = vcolor.rgb;\n    specular_color = vec3(.8,.8,.8);\n    lightAt(); // determine color from lighting\n    //vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));\n    gl_FragColor = vec4( color, 1.0 );\n}\n",peel_color_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nuniform int light_count;\nuniform vec4 light_pos[8];\nuniform vec3 light_color[8];\nuniform vec3 light_ambient;\n#define LP(i) light_pos[i]\n#define LC(i) light_color[i]\nuniform vec2 canvas_size;\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nuniform sampler2D texmap;  // TEXTURE0 - user texture\nuniform sampler2D bumpmap; // TEXTURE1 - user bumpmap\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n#define shininess parameters[0]\n#define emissive parameters[1]\n#define hasTexture parameters[2]\n#define hasBump parameters[3]\n\nvec3 normal;\nvec3 pos;\nvec3 diffuse_color;\nvec3 specular_color;\nvec3 color;\n\n\nvoid calc_color(vec4 lpos, vec3 lcolor)\n{\n    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light\n    L = normalize(L);\n    float N = max(dot(normal,L), 0.0);\n    color += (lcolor * N)*diffuse_color;\n    if (shininess > 0.0) {\n        vec3 R = reflect(L,normal);\n        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);\n    }\n}\n\n// Return lit surface color based on the given surface properties and the lights\n//   specified by the light_* uniforms.\nvoid lightAt()\n{    \n    if (hasTexture != 0.0) {\n        diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;\n    }\n    if (hasBump != 0.0) {\n        vec3 Y = cross(normal, bumpX);\n        vec3 Nb = texture2D(bumpmap, mat_pos).xyz;\n        Nb = 2.0*Nb - 1.0;\n        normal = normalize(Nb.x*bumpX + Nb.y*Y + Nb.z*normal);\n    }\n    if (emissive != 0.0) {\n        // From VPython materials.emissive:\n        float d = dot(normalize(-pos), normal);\n        d = pow(d * 1.5, 0.4) * 1.1;\n        if (d > 1.0) d = 1.0;\n        color = diffuse_color * d;\n        return;\n    }\n    \n    color = light_ambient * diffuse_color;\n    \n    // It was necessary to restructure this shader completely in order to\n    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler\n    // does not handle for loops correctly.\n    if (light_count == 0) return;\n    calc_color(LP(0), LC(0));\n    if (light_count == 1) return;\n    calc_color(LP(1), LC(1));\n    if (light_count == 2) return;\n    calc_color(LP(2), LC(2));\n    if (light_count == 3) return;\n    calc_color(LP(3), LC(3));\n    if (light_count == 4) return;\n    calc_color(LP(4), LC(4));\n    if (light_count == 5) return;\n    calc_color(LP(5), LC(5));\n    if (light_count == 6) return;\n    calc_color(LP(6), LC(6));\n    if (light_count == 7) return;\n    calc_color(LP(7), LC(7));\n}\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create transparency color map - C1 (9), C2 (10), C3 (11), C4 (12)\n    normal = normalize(es_normal);\n    pos = es_position;\n    diffuse_color = vcolor.rgb;\n    specular_color = vec3(.8,.8,.8);\n    lightAt(); // determine color from lighting\n    vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n    float z = decode(encode(1.0-gl_FragCoord.z)); // bigger number => closer to camera; distance out of screen\n    float zmin = decode(texture2D(D0, loc));\n    float zmax;\n    if (minormode == 9) { // C1\n        if (z > zmin) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    } else {\n        if (minormode == 11) { // C3 (11)\n            zmax = decode(texture2D(D2, loc));\n        } else { // C2 (10) or C4 (12)\n            zmax = decode(texture2D(D1, loc));\n        }\n        if (zmin < z && z < zmax) {\n            gl_FragColor = vec4( color, vcolor.a );\n        } else {\n            discard;\n        }\n    }\n}\n"
+,peel_depth_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\n// Construct depth maps for depth peeling handling of opacity\n\nuniform vec2 canvas_size;\nuniform sampler2D D0; // TEXTURE3 - opaque depth map (minormode 5)\nuniform sampler2D D1; // TEXTURE4 - 1st of two ping-pong depth maps (minormode 6)\nuniform sampler2D D2; // TEXTURE5 - 2nd of two ping-pong depth maps (minormode 7); also used for C4\n\n// minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4\nuniform int minormode;\n\nvec4 encode(float k) { // assumes k is >= 0\n    if (k <= 0.0) return vec4(0.0, 0.0, 0.0, 0.0);\n    return vec4(\n        floor(256.0*k)/255.0,\n        floor(256.0*fract(256.0*k))/255.0,\n        floor(256.0*fract(65536.0*k))/255.0,\n        0.0);\n}\n\nfloat decode(vec4 d) {\n    if (length(d) == 0.0) return 0.0;\n    return (65536.0*d[0] + 256.0*d[1] + d[2])/16777216.0;\n}\n\nvoid main(void) {\n    // create depth map, D0 (5); ping-pong D1 (6) and D2 (7)\n    vec4 c = encode(1.0-gl_FragCoord.z);\n    float z = decode(c);\n    if (minormode == 5) { // D0\n        gl_FragColor = c;\n    } else {\n        vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);\n        float zmin = decode(texture2D(D0, loc));\n        float zmax;\n        if (minormode == 6) { // first creation of D1\n            if (z > zmin) {\n                gl_FragColor = c;\n            } else {\n                discard;\n            }\n        } else {\n            if (minormode == 7) {\n                zmax = decode(texture2D(D1, loc)); // create D2\n            } else {\n                zmax = decode(texture2D(D2, loc)); // D1b; create D1 again\n            }\n            if (zmin < z && z < zmax) {\n                gl_FragColor = c;\n            } else {\n                discard; // All pixels are discarded, since (zmax < z && z < zmax) is always false\n            }\n        }\n    }\n}",peel_depth_vertex:"// Vertex shader for picking standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectAxis objectData[1].xyz\n#define objectUp objectData[2].xyz\n#define objectScale objectData[3].xyz\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nmat3 getObjectRotation() {\n  // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n}",pick_fragment:"#ifdef GL_ES\nprecision highp float;\n#endif\n\nvarying vec4 vcolor;\n\nvoid main(void) {\n    gl_FragColor = vcolor;\n}\n",pick_vertex:"// Vertex shader for picking standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectAxis objectData[1].xyz\n#define objectUp objectData[2].xyz\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nmat3 getObjectRotation() {\n  // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n    vcolor = objectColor;\n}\n",render_vertex:"// Vertex shader for rendering standard 'objects' parameterized by\n// pos, axis, up, size, color\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\n\nuniform vec4 objectData[5];\n#define objectPos objectData[0].xyz\n#define objectShininess objectData[0].w\n#define objectAxis objectData[1].xyz\n#define objectEmissive objectData[1].w\n#define objectUp objectData[2].xyz\n#define flags objectData[2].w\n#define objectScale objectData[3].xyz\n#define objectColor objectData[4].rgba\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump, flipx, flipy, turn\n\nmat3 getObjectRotation() {\n    // Construct the object rotation matrix.\n    float vmax = max( max( abs(objectAxis.x), abs(objectAxis.y) ), abs(objectAxis.z) );\n    vec3 X = normalize(objectAxis/vmax);\n    vec3 Z = cross(X,normalize(objectUp));\n    if ( dot(Z,Z) < 1e-10 ) {\n        Z = cross(X, vec3(1,0,0));\n        if (dot(Z,Z) < 1e-10 ) {\n            Z = cross(X, vec3(0,1,0));\n        }\n    }\n    Z = normalize(Z);\n    return mat3( X, normalize(cross(Z,X)), Z );\n}\n\nvoid main(void) {\n    mat3 rot = getObjectRotation();\n    // The position of this vertex in world space\n    vec3 ws_pos = rot*(objectScale*pos) + objectPos;\n    vec4 pos4 = viewMatrix * vec4( ws_pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(rot*normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(rot*bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    vcolor = vec4(color*objectColor.rgb, opacity*objectColor.a);\n    gl_Position = posp;\n    float f = flags; // turn, flipy, flipx, sides, right, left, bumpmap, texture\n    float turn = floor(f/128.0);\n    f -= 128.0*turn;\n    float flipy = floor(f/64.0);\n    f -= 64.0*flipy;\n    float flipx = floor(f/32.0);\n    f -= 32.0*flipx;\n    float sides = floor(f/16.0);\n    f -= 16.0*sides;\n    float right = floor(f/8.0);\n    f -= 8.0*right;\n    float left = floor(f/4.0);\n    f -= 4.0*left;\n    float B = floor(f/2.0);\n    f -= 2.0*B;\n    float T = f;\n    if (T != 0.0) {\n        if (flipx != 0.0) {\n            mat_pos.x = 1.0 - mat_pos.x;\n        }\n        if (flipy != 0.0) {\n            mat_pos.y = 1.0 - mat_pos.y;\n        }\n        if (turn > 0.0 && turn <= 3.0) {\n            if (turn == 1.0) {\n                mat_pos = vec2(mat_pos.y,1.0 - mat_pos.x);\n            } else if (turn == 2.0) {\n                mat_pos = vec2(1.0 - mat_pos.x,1.0 - mat_pos.y);\n            } else {\n                mat_pos = vec2(1.0 - mat_pos.y,mat_pos.x);\n            }\n        }\n        T = 0.0;\n        bool L = (normal.x == -1.0);\n        bool R = (normal.x == 1.0);\n        bool S = !L && !R;\n        if (L && left == 1.0) T = 1.0;\n        if (R && right == 1.0) T = 1.0;\n        if (S && sides == 1.0) T = 1.0;\n        if (T == 0.0) {\n            B = 0.0;\n        } else if (left == 0.0 || right == 0.0 || sides == 0.0) {\n            // don't mix texture and object color if texture doesn't cover entire object\n            vcolor = vec4(1.0, 1.0, 1.0, 1.0);\n        }\n    }\n    parameters = vec4(objectShininess, objectEmissive, T, B);\n}\n",tri_pick_vertex:"// Vertex shader for picking triangles\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec4 color;\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\n\nvarying vec4 vcolor;\n\nvoid main(void) {\n    vec3 normal = vec3(0.0, 0.0, 1.0);\n    vec4 pos4 = viewMatrix * vec4( pos, 1.0);\n    vec4 posp = projMatrix * pos4;\n    gl_Position = posp;\n    vcolor = color;\n}\n",tri_render_vertex:"// Vertex shader for rendering triangles\n\n#ifdef GL_ES\nprecision highp float;\n#endif\n\nattribute vec3 pos;\nattribute vec3 normal;\nattribute vec3 color;\nattribute float opacity;\nattribute vec2 texpos;\nattribute vec3 bumpaxis;\nattribute vec2 params; // shininess, emissive\n\n#define shininess params[0]\n#define emissive params[1]\n\nuniform mat4 viewMatrix;\nuniform mat4 projMatrix;\nuniform float T; // 1.0 if there is a texture, else 0.0\nuniform float B; // 1.0 if there is a bumpmap, else 0.0\n\nvarying vec3 es_position;     // eye space surface position\nvarying vec3 es_normal;       // eye space surface normal\nvarying vec2 mat_pos;         // surface material position in [0,1]^2\nvarying vec4 vcolor;\nvarying vec3 bumpX;\nvarying vec4 parameters; // shininess, emissive, hasTexture, hasBump\n\nvoid main(void) {\n    vec4 pos4 = viewMatrix * vec4( pos, 1.0);\n    es_position = pos4.xyz;\n    es_normal = (viewMatrix * vec4(normal, 0.0)).xyz;\n    vec4 posp = projMatrix * pos4;\n    bumpX = (viewMatrix * vec4(bumpaxis, 0.0)).xyz;\n    mat_pos = texpos;\n    vcolor = vec4(color, opacity);\n    gl_Position = posp;\n    parameters = vec4(shininess, emissive, T, B);\n}\n"}})/*This is a combined, compressed file.  Look at https://bitbucket.org/davidscherer/glowscript for source code and copyright information.*/
 ;(function(){})();

shaders/opaque_render_fragment.shader

 uniform vec4 light_pos[8];
 uniform vec3 light_color[8];
 uniform vec3 light_ambient;
+#define LP(i) light_pos[i]
+#define LC(i) light_color[i]
 
 uniform sampler2D texmap;  // TEXTURE0 - user texture
 uniform sampler2D bumpmap; // TEXTURE1 - user bumpmap
 #define hasTexture parameters[2]
 #define hasBump parameters[3]
 
+vec3 normal;
+vec3 pos;
+vec3 diffuse_color;
+vec3 specular_color;
+vec3 color;
+
+void calc_color(vec4 lpos, vec3 lcolor)
+{
+    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light
+    L = normalize(L);
+    float N = max(dot(normal,L), 0.0);
+    color += (lcolor * N)*diffuse_color;
+    if (shininess > 0.0) {
+        vec3 R = reflect(L,normal);
+        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);
+    }
+}
+
 // Return lit surface color based on the given surface properties and the lights
 //   specified by the light_* uniforms.
-vec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )
-{
-    vec3 color;
+void lightAt()
+{    
     if (hasTexture != 0.0) {
         diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;
     }
         d = pow(d * 1.5, 0.4) * 1.1;
         if (d > 1.0) d = 1.0;
         color = diffuse_color * d;
-        return color;
+        return;
     }
+    
     color = light_ambient * diffuse_color;
-
-    for(int i=0; i<8; i++) {
-        if (i >= light_count) break;
-        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light
-        L = normalize(L);
-        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;
-        if (shininess > 0.0) {
-            vec3 R = reflect(L,normal);
-            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);
-        }
-    }
-    return color;
+    
+    // It was necessary to restructure this shader completely in order to
+    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler
+    // does not handle for loops correctly.
+    if (light_count == 0) return;
+    calc_color(LP(0), LC(0));
+    if (light_count == 1) return;
+    calc_color(LP(1), LC(1));
+    if (light_count == 2) return;
+    calc_color(LP(2), LC(2));
+    if (light_count == 3) return;
+    calc_color(LP(3), LC(3));
+    if (light_count == 4) return;
+    calc_color(LP(4), LC(4));
+    if (light_count == 5) return;
+    calc_color(LP(5), LC(5));
+    if (light_count == 6) return;
+    calc_color(LP(6), LC(6));
+    if (light_count == 7) return;
+    calc_color(LP(7), LC(7));
 }
 
 void main(void) {
-    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));
+    normal = normalize(es_normal);
+    pos = es_position;
+    diffuse_color = vcolor.rgb;
+    specular_color = vec3(.8,.8,.8);
+    lightAt(); // determine color from lighting
+    //vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));
     gl_FragColor = vec4( color, 1.0 );
 }

shaders/peel_color_fragment.shader

 uniform vec4 light_pos[8];
 uniform vec3 light_color[8];
 uniform vec3 light_ambient;
+#define LP(i) light_pos[i]
+#define LC(i) light_color[i]
 uniform vec2 canvas_size;
 // minormode = 0 render, 1 pick, 2 autoscale, 4 C0, 5 D0, 6 D1, 7 D2, 8 D1b, 9 C1, 10 C2, 11 C3, 12 C4
 uniform int minormode;
 #define hasTexture parameters[2]
 #define hasBump parameters[3]
 
+vec3 normal;
+vec3 pos;
+vec3 diffuse_color;
+vec3 specular_color;
+vec3 color;
+
+
+void calc_color(vec4 lpos, vec3 lcolor)
+{
+    vec3 L = lpos.xyz - pos*lpos.w; // w == 0 for distant_light
+    L = normalize(L);
+    float N = max(dot(normal,L), 0.0);
+    color += (lcolor * N)*diffuse_color;
+    if (shininess > 0.0) {
+        vec3 R = reflect(L,normal);
+        color += specular_color * LC(0) * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);
+    }
+}
+
 // Return lit surface color based on the given surface properties and the lights
 //   specified by the light_* uniforms.
-vec3 lightAt( vec3 normal, vec3 pos, vec3 diffuse_color, vec3 specular_color )
-{
-    vec3 color;
+void lightAt()
+{    
     if (hasTexture != 0.0) {
         diffuse_color = diffuse_color * texture2D(texmap, mat_pos).xyz;
     }
         d = pow(d * 1.5, 0.4) * 1.1;
         if (d > 1.0) d = 1.0;
         color = diffuse_color * d;
-        return color;
+        return;
     }
+    
     color = light_ambient * diffuse_color;
-
-    for(int i=0; i<8; i++) {
-        if (i >= light_count) break;
-        vec3 L = light_pos[i].xyz - pos*light_pos[i].w; // w == 0 for distant_light
-        L = normalize(L);
-        color += (light_color[i].rgb * max(dot(normal,L), 0.0))*diffuse_color;
-        if (shininess > 0.0) {
-            vec3 R = reflect(L,normal);
-            color += specular_color * light_color[i].rgb * pow(max(dot(R,normalize(pos)),0.0),100.0*shininess);
-        }
-    }
-    return color;
+    
+    // It was necessary to restructure this shader completely in order to
+    // run on the Samsung Galaxy S3 smartphone. Apparently its compiler
+    // does not handle for loops correctly.
+    if (light_count == 0) return;
+    calc_color(LP(0), LC(0));
+    if (light_count == 1) return;
+    calc_color(LP(1), LC(1));
+    if (light_count == 2) return;
+    calc_color(LP(2), LC(2));
+    if (light_count == 3) return;
+    calc_color(LP(3), LC(3));
+    if (light_count == 4) return;
+    calc_color(LP(4), LC(4));
+    if (light_count == 5) return;
+    calc_color(LP(5), LC(5));
+    if (light_count == 6) return;
+    calc_color(LP(6), LC(6));
+    if (light_count == 7) return;
+    calc_color(LP(7), LC(7));
 }
 
 vec4 encode(float k) { // assumes k is >= 0
 
 void main(void) {
     // create transparency color map - C1 (9), C2 (10), C3 (11), C4 (12)
-    vec3 color = lightAt(normalize(es_normal), es_position, vcolor.rgb, vec3(.8,.8,.8));
+    normal = normalize(es_normal);
+    pos = es_position;
+    diffuse_color = vcolor.rgb;
+    specular_color = vec3(.8,.8,.8);
+    lightAt(); // determine color from lighting
     vec2 loc = vec2(gl_FragCoord.x/canvas_size.x, gl_FragCoord.y/canvas_size.y);
     float z = decode(encode(1.0-gl_FragCoord.z)); // bigger number => closer to camera; distance out of screen
     float zmin = decode(texture2D(D0, loc));
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.