Changeset 21437


Ignore:
Timestamp:
12/09/16 11:09:24 (8 years ago)
Author:
dlcheng
Message:

CHG: Updating js plot routines for improved movie rendering and 2-way computational request compression in generic.js.

Location:
issm/trunk-jpl/src/m
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • issm/trunk-jpl/src/m/classes/clusters/generic.js

    r21065 r21437  
    4343                        fid=fopen(modelname+'.queue','w');
    4444                        fprintf(fid,'#!%s\n',cluster.shell);
    45                         fprintf(fid,'mpiexec -np %i %s/%s %s %s %s 2> %s.errlog >%s.outlog ',cluster.np,cluster.codepath,executable,solution,cluster.executionpath+'/'+dirname,modelname,modelname,modelname);                                 
     45                        fprintf(fid,'mpiexec -np %i %s/%s %s %s %s 2> %s.errlog >%s.outlog ',cluster.np,cluster.codepath,executable,EnumToString(solution),cluster.executionpath+'/'+dirname,modelname,modelname,modelname);                                   
    4646                        fclose(fid);
    4747        } //}}}
     
    9292                        }
    9393                        var responseText = window.atob(request.responseText.slice(request.position + 10).replace(/\s/g, ''));
    94             var buffer = str2ab(responseText);
     94            var buffer = pako.inflate(str2ab(responseText));
    9595                        var returnBuffer = new Uint8Array(buffer);
    9696                        var returnBuffer_size = returnBuffer.byteLength;
     
    115115               
    116116                var npbuffer = this.str2ab(md.cluster.np.toString());
     117                npbuffer = pako.deflate(npbuffer);
    117118                var nplength = new Uint32Array(1);
    118119                nplength[0] = npbuffer.byteLength;
    119120               
    120121                var codeversionbuffer = this.str2ab(md.cluster.codeversion.toString());
     122                codeversionbuffer = pako.deflate(codeversionbuffer);
    121123                var codeversionlength = new Uint32Array(1);
    122124                codeversionlength[0] = codeversionbuffer.byteLength;
    123125               
    124126                var runtimenamebuffer = this.str2ab(runtimename);
     127                runtimenamebuffer = pako.deflate(runtimenamebuffer);
    125128                var runtimenamelength = new Uint32Array(1);
    126129                runtimenamelength[0] = runtimenamebuffer.byteLength;
    127130               
    128131                var namebuffer = this.str2ab(name);
     132                namebuffer = pako.deflate(namebuffer);
    129133                var namelength = new Uint32Array(1);
    130134                namelength[0] = namebuffer.byteLength;
    131135               
    132136                var toolkitsbuffer = this.str2ab(toolkitsstring);
     137                toolkitsbuffer = pako.deflate(toolkitsbuffer);
    133138                var toolkitslength = new Uint32Array(1);
    134139                toolkitslength[0] = toolkitsbuffer.byteLength;
    135140               
    136141                var solutionbuffer = this.str2ab(solutionstring);
     142                solutionbuffer = pako.deflate(solutionbuffer);
    137143                var solutionlength = new Uint32Array(1);
    138144                solutionlength[0] = solutionbuffer.byteLength;
    139145               
    140                 var binbuffer = new Uint8Array(fid.rawbuffer()); //seems that 16 array bytes length could be incompatible.
     146                var binbuffer = new Uint8Array(fid.rawbuffer()); //seems that 16 bits length could be incompatible.
     147                binbuffer = pako.deflate(binbuffer);
    141148                var binlength = new Uint32Array(1);
    142149                binlength[0] = binbuffer.byteLength;
    143150               
    144151                var data = new Blob([nplength,npbuffer,codeversionlength,codeversionbuffer,runtimenamelength,runtimenamebuffer,namelength,namebuffer,toolkitslength,toolkitsbuffer,solutionlength,solutionbuffer,binlength,binbuffer]);
    145        
     152               
    146153                request.open("POST", this.url, true);
    147154                request.responseType = 'application/octet-stream';
  • issm/trunk-jpl/src/m/plot/plot_manager.js

    r21300 r21437  
    1515        var gl = canvas.gl;
    1616        //TODO: each plot_ should add their node to the canvas.node array
    17 
    1817        //figure out if this is a special plot
    1918        if (typeof data === 'string'){
    20 
     19               
    2120                switch(data){
    2221
  • issm/trunk-jpl/src/m/plot/plot_quiver.js

    r21300 r21437  
    1 function plot_quiver(md,options,canvas) { //{{{
    2         //PLOT_QUIVER - quiver plot with colors
     1function plot_unit(md,data,datatype,options,canvas) { //{{{
     2        //PLOT_UNIT - unit plot, display data
    33        //
    44        //   Usage:
    5         //      plot_quiver(md,options,canvas)
     5        //      plot_unit(md,data,options,canvas);
    66        //
    77        //   See also: PLOTMODEL, PLOT_MANAGER
    88
    99        //declare variables:  {{{
    10         var vertices = [];
    11         var indices = [];
    12         var colors = [];
     10        //Process data and model
     11        var meshresults = processmesh(md,data,options);
     12        var x = meshresults[0];
     13        var y = meshresults[1];
     14        var z = meshresults[2];
     15        var elements = meshresults[3];
     16        var is2d = meshresults[4];
     17        var isplanet = meshresults[5];
     18       
     19        var vertices = new Float32Array(x.length * 3);
     20        var texcoords = new Float32Array(x.length * 2);
     21        var indices = new Uint16Array(elements.length * 3);
     22        var nanindices = {};
    1323        var xmin,xmax;
    1424        var ymin,ymax;
    1525        var zmin,zmax;
    16         var scale,matrixscale,vertexscale;
    17        
    18         //Process data and model
    19         var meshresults = processmesh(md,[],options);
    20         var x = meshresults[0];
    21         var y = meshresults[1];
    22         var z = meshresults[2];
    23         var elements = meshresults[3];
    24         var is2d = meshresults[4];
    25         var isplanet = meshresults[5];
    26         var v = md.initialization.vel;
    27         var vx = md.initialization.vx;
    28         var vy = md.initialization.vy;
    29                
     26        var datamin,datamax,datadelta;
     27        var matrixscale,vertexscale;
    3028        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    3129        if (!md.geometry.surface) {
     
    5856        zmin = zlim[0];
    5957        zmax = zlim[1];
     58        var caxis;
    6059
    6160        //Compute gl variables:
     
    6362        var node = Node(gl);
    6463        canvas.nodes[canvas.nodes.length] = node;
    65         node.name = "quiver";
    66         node.shaderName = "Colored";
     64        canvas.unitNode = node;
     65        node.name = "unit";
     66        node.shaderName = "Textured";
    6767        node.shader = gl.shaders[node.shaderName];
    68         node.lineWidth = options.getfieldvalue('linewidth',1);
    6968        node.scale = [1, 1, matrixscale];
    7069        node.rotation = [-90, 0, 0];
    7170        node.translation = [0, 0, 0];
    7271        node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
    73         node.drawMode = gl.LINES;
    74         node.useIndexBuffer = false;
    75         node.drawOrder = 0;
     72        node.alpha = options.getfieldvalue('alpha',1.0);
     73        node.drawOrder = 1;
    7674        node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
    77         node.maskHeight = options.getfieldvalue('innermaskheight',150.0)*options.getfieldvalue('heightscale',1);
     75        node.maskHeight = options.getfieldvalue('innermaskheight',150.0);
    7876        node.maskColor = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
     77        node.enabled = options.getfieldvalue('nodata','off') == 'off';
    7978        updateModelMatrix(node);
    8079
    81         //retrieve some options
    82         var edgecolor=new RGBColor(options.getfieldvalue('edgecolor','black'));
    83         if (edgecolor.ok) edgecolor = [edgecolor.r/255.0, edgecolor.g/255.0, edgecolor.b/255.0, 1.0];
    84         else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ",canvas));
    85 
    86         //node plot {{{
    87         if (elements[0].length==6){ //prisms
    88         }
    89         else if (elements[0].length==4){ //tetras
    90         }
    91         else{ //2D triangular elements
    92                 var xyz = vec3.create();
    93                 var xyz = vec3.create();
    94                 var direction = vec3.create();
    95                 var vertex = vec3.create();
    96                 var vertexBase = vec3.create();
    97                 var verticesArrow = [vec3.fromValues(0.0, 0.0, 0.0), vec3.fromValues(1.0, 0.0, 0.0), vec3.fromValues(0.667, -0.167, 0.0), vec3.fromValues(1.0, 0.0, 0.0), vec3.fromValues(0.667, 0.166, 0.0), vec3.fromValues(1.0, 0.0, 0.0)];
    98                 var magnitude;
    99                 var color = edgecolor;
    100                 var scaling = options.getfieldvalue('scaling',1);
    101                 var scale;
    102                 for(var i = 0; i < x.length; i++){
    103                         //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
    104                         if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i])) continue;
    105                         //Scale vertices
    106                         xyz = vec3.fromValues(x[i], y[i], z[i]);
    107                         magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
    108                         vec3.normalize(direction, xyz);
    109                         vec3.scale(vertex, direction, magnitude);
    110                         vec3.copy(vertexBase, vertex);
    111                        
    112                         scale = scaling*v[i];
    113                         var modelMatrix = mat4.create();
    114                         var scaleMatrix = mat4.create();
    115                         var rotationMatrix = mat4.create();
    116                         mat4.scale(scaleMatrix, scaleMatrix, vec3.fromValues(scale, scale, scale));
    117                         mat4.rotate(rotationMatrix, rotationMatrix, Math.atan2(vy[i], vx[i]), [0.0, 0.0, 1.0]);
    118                         mat4.multiply(modelMatrix, rotationMatrix, scaleMatrix);
    119 
    120                         var temp = vec3.fromValues(0.0, 0.0, 0.0);
    121                         for (var j = 0; j < 6; j++){
    122                                 vec3.transformMat4(vertex, verticesArrow[j], modelMatrix);
    123                                 vec3.add(vertex, vertex, vertexBase);
    124                                 vertices[vertices.length] = vertex[0];
    125                                 vertices[vertices.length] = vertex[1];
    126                                 vertices[vertices.length] = vertex[2];
     80        switch(datatype){
     81                //element plot {{{
     82                case 1:
     83                        pos=ArrayFindNot(data,NaN); //needed for element on water
     84                        if (elements[0].length==6){ //prisms
     85                        }
     86                        else if (elements[0].length==4){ //tetras
     87                        }
     88                        else{ //2D triangular elements
     89                        }
     90                        break;
     91                //}}}
     92                //node plot {{{
     93                case 2:
     94                        if (elements[0].length==6){ //prisms
     95                        }
     96                        else if (elements[0].length==4){ //tetras
     97                        }
     98                        else{ //triangular elements     
     99                                caxis = options.getfieldvalue('caxis',[ArrayMin(data),ArrayMax(data)]);
     100                                if (options.getfieldvalue('log','off')!='off') caxis = [Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log',10)),Math.log10(caxis[1])/Math.log10(options.getfieldvalue('log',10))];
     101                                datamin = caxis[0];
     102                                datamax = caxis[1];
     103                                datadelta = datamax - datamin;
     104
     105                                var xyz = vec3.create();
     106                                var direction = vec3.create();
     107                                var vertex = vec3.create();
     108                                var magnitude;
     109
     110                                for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
     111                                        //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
     112                                        if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i])) {
     113                                                nanindices[i] = i;
     114                                                vertices[vindex++] = vertex[0];
     115                                                vertices[vindex++] = vertex[1];
     116                                                vertices[vindex++] = vertex[2];
     117                                               
     118                                                texcoords[tindex++] = 0.5;
     119                                                texcoords[tindex++] = 0.0;
     120                                                continue;
     121                                        }
     122
     123                                        //Scale vertices
     124                                        xyz = vec3.fromValues(x[i], y[i], z[i]);
     125                                        magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
     126                                        vec3.normalize(direction, xyz);
     127                                        vec3.scale(vertex, direction, magnitude);
     128                                        vertices[vindex++] = vertex[0];
     129                                        vertices[vindex++] = vertex[1];
     130                                        vertices[vindex++] = vertex[2];
     131
     132                                        texcoords[tindex++] = 0.5;
     133                                        texcoords[tindex++] = clamp((data[i] - datamin) / datadelta, 0.0, 1.0);
     134                                }
     135
     136                                //linearize the elements array:
     137                                var element;
     138                                for(var i = 0, iindex = 0; i < elements.length; i++){
     139                                        element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
     140                                        if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
     141                                        indices[iindex++] = element[0];
     142                                        indices[iindex++] = element[1];
     143                                        indices[iindex++] = element[2];
     144                                }
     145                        }
     146                        node.mesh = GL.Mesh.load({vertices:vertices, coords:texcoords, triangles:indices}, null, null, gl);
     147                        node.mesh.octree = new GL.Octree(node.mesh);
     148                        break;
     149                //}}}
     150                //quiver plot {{{
     151                case 3:
     152                        if (is2d){
     153                                //plot_quiver(x,y,data(:,1),data(:,2),options);
     154                        }
     155                        else{
     156                                //plot_quiver3(x,y,z,data(:,1),data(:,2),data(:,3),options);
     157                        }
     158                        break;
     159                //}}}
     160                //node transient plot {{{
     161                case 5:
     162                        if (elements[0].length==6){ //prisms
     163                        }
     164                        else if (elements[0].length==4){//tetras
     165                        }
     166                        else{ //triangular elements
     167                                var xyz = vec3.create();
     168                                var direction = vec3.create();
     169                                var vertex = vec3.create();
     170                                var magnitude;
     171                                var timestamps = data[data.length-1];
     172                                for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
     173                                        //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
     174                                        if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i][0])) {
     175                                                nanindices[i] = i;
     176                                        }
     177                                        else {
     178                                                //Scale vertices
     179                                                xyz = vec3.fromValues(x[i], y[i], z[i]);
     180                                                magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
     181                                                vec3.normalize(direction, xyz);
     182                                                vec3.scale(vertex, direction, magnitude);
     183                                        }
     184                                        vertices[vindex++] = vertex[0];
     185                                        vertices[vindex++] = vertex[1];
     186                                        vertices[vindex++] = vertex[2];
     187                                }       
     188                                //Transpose data to obtain column addressable data matrix
     189                                data = data[0].map(function(col, i) {
     190                                        return data.map(function(row) {
     191                                                return row[i]
     192                                        })
     193                                });
     194                                //Prevent evaluation of datasubarray min/max if caxis exists
     195                                if (options.exist('caxis')) caxis = options.getfieldvalue('caxis');
     196                                else caxis = [ArrayMin(data[0]),ArrayMax(data[0].slice(0,-1))];
     197                                if (options.getfieldvalue('log','off')!='off') caxis = [Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log',10)),Math.log10(caxis[1])/Math.log10(options.getfieldvalue('log',10))];
     198                                //Prepare texcoords to hold array of data values
     199                                texcoords = [];
     200                                for(var i = 0; i < data.length; i++){                                   
     201                                        datamin = caxis[0];
     202                                        datamax = caxis[1];
     203                                        datadelta = datamax - datamin;
     204                                        //Precalculate arrays for each datasubarray, trimming off timestamp value by using x.length instead of data[i].length
     205                                        texcoords[i] = new Float32Array(x.length * 2);
     206                                        for(var j = 0, index = 0; j < x.length; j++){
     207                                                texcoords[i][index++] = 0.5;
     208                                                texcoords[i][index++] = clamp((data[i][j] - datamin) / datadelta, 0.0, 1.0);
     209                                        }
     210                                }
    127211                               
    128                                 colors[colors.length] = color[0];
    129                                 colors[colors.length] = color[1];
    130                                 colors[colors.length] = color[2];
    131                                 colors[colors.length] = color[3];
    132                         }
    133                 }
    134         }
    135         //}}}
    136         node.mesh = GL.Mesh.load({vertices:vertices, colors:colors}, null, null, gl);
     212                                //linearize the elements array:
     213                                var element;
     214                                for(var i = 0, iindex = 0; i < elements.length; i++){
     215                                        element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
     216                                        if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
     217                                        indices[iindex++] = element[0];
     218                                        indices[iindex++] = element[1];
     219                                        indices[iindex++] = element[2];
     220                                }
     221                                var frame =
     222                                //Initialize movie loop
     223                                node.movieLoop = canvas.movieOptions.loop;
     224                                node.movieInterval = 1000 / canvas.movieOptions.fps;
     225                                node.movieTimestamps = timestamps;
     226                                node.movieLength = timestamps.length;
     227                                node.movieFrame = 0;
     228
     229                                var quiverVelFrames = {};
     230                                for(var i=0; i < md.results.length; i++){
     231                                        quiverVelFrames[Math.floor(md.results[i].time)] = md.results[i];
     232                                }
     233
     234                                if (canvas.movieHandler) { clearInterval(canvas.movieHandler); }
     235                                canvas.movieHandler = setInterval(function () {
     236                                                node.movieFrame = canvas.movieFrame;
     237                                                if (canvas.moviePlay && canvas.movieIncrement) {
     238                                                        if (canvas.movieReverse) {
     239                                                                if (node.movieFrame == 0) {
     240                                                                        if (node.movieLoop) {
     241                                                                                node.movieFrame = node.movieLength - 1;
     242                                                                        }
     243                                                                        else {
     244                                                                                toggleMoviePlay(canvas);
     245                                                                        }
     246                                                                }
     247                                                                else {
     248                                                                        node.movieFrame = node.movieFrame - 1;
     249                                                                }
     250                                                        }
     251                                                        else {
     252                                                                if (node.movieFrame == node.movieLength - 1) {
     253                                                                        if (node.movieLoop) {
     254                                                                                node.movieFrame = 0;
     255                                                                        }
     256                                                                        else {
     257                                                                                toggleMoviePlay(canvas);
     258                                                                        }
     259                                                                }
     260                                                                else {
     261                                                                        node.movieFrame = node.movieFrame + 1;
     262                                                                }
     263                                                        }
     264                                                }
     265                                                if (canvas.progressBar) {
     266                                                        canvas.progressBar.val(node.movieFrame);
     267                                                        canvas.progressBar.slider('refresh');
     268                                                }
     269                                                if (canvas.timeLabel) { canvas.timeLabel.html(node.movieTimestamps[node.movieFrame].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr")); }
     270
     271                                                var buffer = node.mesh.getBuffer("coords");
     272                                                buffer.data = texcoords[node.movieFrame];
     273                                                buffer.upload(canvas.gl.DYNAMIC_DRAW);
     274                                                node.mesh.octree = new GL.Octree(node.mesh);
     275                                       
     276                                                if(options.getfieldvalue('quiver') == 'data'){
     277                                                        plot_quiver(md,options,canvas, {vel:quiverVelFrames[node.movieFrame].Vel, vx:quiverVelFrames[node.movieFrame].Vx, vy:quiverVelFrames[node.movieFrame].Vy});
     278
     279                                                }
     280                                                canvas.movieFrame = node.movieFrame;
     281                                        }, node.movieInterval);
     282                                if (canvas.progressBar) {
     283                                        canvas.movieFrame = 0;
     284                                        canvas.progressBar.val(0);
     285                                        canvas.progressBar.attr('max', node.movieLength-1);
     286                                        canvas.progressBar.slider('refresh');
     287                                }
     288                               
     289                        }
     290                        node.mesh = GL.Mesh.load({vertices:vertices, coords:texcoords[0], triangles:indices}, null, null, gl);
     291                        node.mesh.octree = new GL.Octree(node.mesh);
     292                        break;
     293                //}}}
     294                default:
     295                        throw Error(sprintf("%s%i%s\n",'case ',datatype,' not supported'));
     296        }
    137297} //}}}
  • issm/trunk-jpl/src/m/plot/plot_unit.js

    r21300 r21437  
    2626        var datamin,datamax,datadelta;
    2727        var matrixscale,vertexscale;
    28 
    2928        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    3029        if (!md.geometry.surface) {
     
    220219                                        indices[iindex++] = element[2];
    221220                                }
    222                        
     221                                var frame =
    223222                                //Initialize movie loop
    224223                                node.movieLoop = canvas.movieOptions.loop;
     
    227226                                node.movieLength = timestamps.length;
    228227                                node.movieFrame = 0;
     228
     229                                var quiverVelFrames = {};
     230                                for(var i=0; i < md.results.length; i++){
     231                                        quiverVelFrames[Math.floor(md.results[i].time)] = md.results[i];
     232                                }
     233
    229234                                if (canvas.movieHandler) { clearInterval(canvas.movieHandler); }
    230235                                canvas.movieHandler = setInterval(function () {
     
    268273                                                buffer.upload(canvas.gl.DYNAMIC_DRAW);
    269274                                                node.mesh.octree = new GL.Octree(node.mesh);
    270 
     275                                       
     276                                                if(options.getfieldvalue('quiver') == 'data'){
     277                                                        plot_quiver(md,options,canvas, {vel:quiverVelFrames[node.movieFrame].Vel, vx:quiverVelFrames[node.movieFrame].Vx, vy:quiverVelFrames[node.movieFrame].Vy});
     278
     279                                                }
    271280                                                canvas.movieFrame = node.movieFrame;
    272281                                        }, node.movieInterval);
  • issm/trunk-jpl/src/m/plot/webgl.js

    r21300 r21437  
     1/*This is where we have all our webgl relevant functionality for the plotting routines: */
     2
    13/*This is where we have all our webgl relevant functionality for the plotting routines: */
    24
     
    9294        if (backgroundcolor.ok) { canvas.backgroundcolor = [backgroundcolor.r/255.0, backgroundcolor.g/255.0, backgroundcolor.b/255.0, 1.0]; }
    9395        else { throw Error(sprintf('s%s%s\n','initWebGL error message: cound not find out background color for curent canvas ',canvas)); }
    94        
     96
     97        //TODO: Make permalinks more robust and less interdependent on UI
     98        //Override with parameters from URL, if any
     99        if (!canvas.usedparemters) {
     100                function getJsonFromUrl() {
     101                        var query = location.search.substr(1);
     102                        var result = {};
     103                        query.split("&").forEach(function(part) {
     104                                var item = part.split("=");
     105                                result[item[0]] = decodeURIComponent(item[1]);
     106                        });
     107                        return result;
     108                }
     109                parameters = getJsonFromUrl();
     110               
     111                if (parameters["rotation"]) {
     112                        canvas.rotation = JSON.parse(parameters["rotation"]);
     113                }
     114                if (parameters["view"]) {
     115                        canvas.view = JSON.parse(parameters["view"]);
     116                }
     117                if (parameters["zoom"]) {
     118                        canvas.zoom = JSON.parse(parameters["zoom"]);
     119                }
     120                if (parameters["initial"]) {
     121                        initial = JSON.parse(parameters["initial"]);
     122                        if (!initial) {
     123                                if (typeof SolveGlacier == 'function') {
     124                                        SolveGlacier();
     125                                }
     126                                if (typeof SolveSlr == 'function') {
     127                                        SolveSlr();
     128                                }
     129                        }
     130                }
     131                canvas.usedparemters = true;
     132        }
     133
    95134        return gl;
     135} //}}}
     136function generatePermalink() { //{{{
     137        var permalink = window.location.origin + window.location.pathname + "?rotation=" + JSON.stringify(canvas.rotation) + "&view=" + JSON.stringify(canvas.view) + "&zoom=" + JSON.stringify(canvas.zoom) + "&initial=" + JSON.stringify(initial);
     138        window.prompt("Share this simulation: ", permalink);
    96139} //}}}
    97140function loadShaders(gl,rootPath) { //{{{
Note: See TracChangeset for help on using the changeset viewer.