Changeset 21683


Ignore:
Timestamp:
04/21/17 11:06:42 (8 years ago)
Author:
dlcheng
Message:

CHG (JS): js plots code refactoring, texture caching, and more organized webgl engine. Adding network connectivity detection to generic.js. DEL: download.js saveAsFile not needed anymore, covered by writetofile.js.

Location:
issm/trunk-jpl/src/m
Files:
2 deleted
8 edited

Legend:

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

    r21641 r21683  
    4747        } //}}}
    4848        this.UploadAndRun = function (md,callbackfunction,callbackerrorfunction,callbackid,fid,toolkitsstring,solutionstring,name,runtimename) { //{{{
    49 
     49                if (!navigator.onLine) { //{{{
     50                        $(callbackid).html(sprintf("%-16s", "NO CONNECTION")).prop("disabled", false);
     51                        callbackerrorfunction();
     52                        return;
     53                } //}}}
    5054                var request = new XMLHttpRequest();
    5155                $(callbackid).html(sprintf("%-16s", "CONNECTING...")).prop("disabled", true);
  • issm/trunk-jpl/src/m/plot/applyoptions.js

    r21641 r21683  
    1 function applyoptions(md,data,datatype,options,canvas,gl,node){ //{{{
     1function applyoptions(md, data, datatype, options, canvas, gl, node){ //{{{
    22        //APPLYOPTIONS - apply colobar, text, cloud, and expdisp options to current plot
    33        //
    44        //   Usage:
    5         //      applyoptions(md,data,options)
     5        //      applyoptions(md, data, options)
    66        //
    77        //   See also: PLOTMODEL, PARSE_OPTIONS
    88       
    9         //colorbar {{{
     9        //{{{ colorbar
    1010        if (options.exist('colorbar')) {
    1111                if (options.getfieldvalue('colorbar')==1) {
    12                         //Handle movie data {{{
     12                        //{{{ Handle movie data
    1313                        if (datatype == 5) {
    1414                                data = data[0];
    1515                        } //}}}
    16                         //Variable options initialization {{{
    17                         var caxis = options.getfieldvalue('caxis',[ArrayMin(data),ArrayMax(data)]);
     16                        //{{{ Variable options initialization
     17                        var caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
    1818                        var colorbarinnerlabels = options.getfieldvalue('colorbarinnerlabels','off');
    19                         var ccanvasid,ctitleid,clabelsid,ccanvas,ctitle,clabels,ccontext,cmap,colorbar,cwidth,cheight,cgradient,color,y,x;
    20                         //}}}
    21                         //Create colorbar labels {{{
     19                        var ccanvasid, ctitleid, clabelsid, ccanvas, ctitle, clabels, ccontext, cmap, colorbar, cwidth, cheight, cgradient, color, y, x;
     20                        //}}}
     21                        //{{{ Create colorbar labels
    2222                        var labels = [];
    23                         var cdivisions = options.getfieldvalue('colorbarnticks',6);
     23                        var cdivisions = options.getfieldvalue('colorbarnticks', 6);
    2424                        var caxisdelta = caxis[1] - caxis[0];
    25                         var precision = options.getfieldvalue('colorbarprecision',3);
     25                        var precision = options.getfieldvalue('colorbarprecision', 3);
    2626                        if (options.getfieldvalue('log','off')!='off') {
    2727                                for (var i=cdivisions; i >= 0; i--) {
    28                                         var scale = (Math.log10(caxis[1])-Math.log10(caxis[0]))/Math.log10(options.getfieldvalue('log',10));
    29                                         labels[i] = (Math.pow(options.getfieldvalue('log',10),Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log',10))+scale*(cdivisions-i)/cdivisions)).toFixed(precision);
     28                                        var scale = (Math.log10(caxis[1])-Math.log10(caxis[0]))/Math.log10(options.getfieldvalue('log', 10));
     29                                        labels[i] = (Math.pow(options.getfieldvalue('log', 10), Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log', 10))+scale*(cdivisions-i)/cdivisions)).toFixed(precision);
    3030                                }
    3131                        } else {
     
    3434                                }
    3535                        } //}}}
    36                         //Initialize colorbar canvas {{{
    37                         ccanvasid = options.getfieldvalue('colorbarid',options.getfieldvalue('canvasid').replace('canvas','colorbar-canvas'));                 
     36                        //{{{ Initialize colorbar canvas
     37                        ccanvasid = options.getfieldvalue('colorbarid', options.getfieldvalue('canvasid').replace('canvas','colorbar-canvas'));                 
    3838                        ccanvas = $('#'+ccanvasid)[0];
    39                         cwidth = ccanvas.width*options.getfieldvalue('colorbarwidth',1);
    40                         cheight = ccanvas.height*options.getfieldvalue('colorbarheight',1);
     39                        cwidth = ccanvas.width*options.getfieldvalue('colorbarwidth', 1);
     40                        cheight = ccanvas.height*options.getfieldvalue('colorbarheight', 1);
    4141                        ccontext = ccanvas.getContext('2d');
    42                         ccontext.clearRect(0,0, cwidth, cheight);
     42                        ccontext.clearRect(0, 0, cwidth, cheight);
    4343                        ccontext.beginPath();
    4444                        cmap = options.getfieldvalue('colormap','jet');
    4545                        colorbar = colorbars[cmap];
    46                         cgradient = ccontext.createLinearGradient(0,0,0,cheight);
    47                         //}}}
    48                         //Draw colorbar gradient {{{
     46                        cgradient = ccontext.createLinearGradient(0, 0, 0, cheight);
     47                        //}}}
     48                        //{{{ Draw colorbar gradient
    4949                        for (var i=0; i < colorbar.length; i++) {
    5050                                color = colorbar[colorbar.length-i-1];
    51                                 color = [Math.round(color[0]*255),Math.round(color[1]*255),Math.round(color[2]*255)];   
    52                                 cgradient.addColorStop(i/colorbar.length*(cdivisions/(cdivisions+1.0))+(1.0/(cdivisions+1.0)),'rgba('+color.toString()+',1.0)');
     51                                color = [Math.round(color[0]*255), Math.round(color[1]*255), Math.round(color[2]*255)];
     52                                cgradient.addColorStop(i/colorbar.length*(cdivisions/(cdivisions+1.0))+(1.0/(cdivisions+1.0)),'rgba('+color.toString()+', 1.0)');
    5353                        }
    5454                        ccontext.fillStyle=cgradient;
    55                         ccontext.fillRect(0,0,cwidth,cheight);
    56                         //}}}
    57                         //Draw colorbar border {{{
     55                        ccontext.fillRect(0, 0, cwidth, cheight);
     56                        //}}}
     57                        //{{{ Draw colorbar border
    5858                        ccontext.beginPath();
    5959                        ccontext.lineWidth='1';
    6060                        ccontext.strokeStyle=options.getfieldvalue('colorbarfontcolor','black');
    61                         ccontext.rect(0,0,cwidth,cheight);
     61                        ccontext.rect(0, 0, cwidth, cheight);
    6262                        ccontext.stroke();
    6363                        //}}}
    64                         //Draw colorbar labels {{{
     64                        //{{{ Draw colorbar labels
    6565                        clabelsid = options.getfieldvalue('colorbarid', ccanvasid).replace('canvas','labels');
    6666                        clabels = $('#'+clabelsid);
     
    8080                                clabelstring += '<li><span>'+labels[i]+'</span></li>';
    8181                                ccontext.beginPath();
    82                                 ccontext.moveTo(0,y);
    83                                 ccontext.lineTo(x,y);
    84                                 ccontext.moveTo(cwidth-x,y);
    85                                 ccontext.lineTo(cwidth,y);
     82                                ccontext.moveTo(0, y);
     83                                ccontext.lineTo(x, y);
     84                                ccontext.moveTo(cwidth-x, y);
     85                                ccontext.lineTo(cwidth, y);
    8686                                ccontext.stroke();
    8787                        }
    8888                        clabels.append(clabelstring);
    8989                        //}}}
    90                         //Draw colorbar title {{{
     90                        //{{{ Draw colorbar title
    9191                        ctitleid = options.getfieldvalue('colorbarid', ccanvasid).replace('canvas','heading');
    9292                        ctitle = $('#'+ctitleid);
     
    9595                }
    9696        } //}}}
    97         //texture canvas //{{{
    98         var tcontext,tcanvas,tcanvasid,tURL,tgradient;
     97        //{{{ texture canvas
     98        var tcontext, tcanvas, tcanvasid, tURL, tgradient;
    9999        tcanvasid = 'texturecanvas';
    100100        var tcanvas = document.getElementById(tcanvasid);
     
    104104        }
    105105        tcontext = tcanvas.getContext('2d');
    106         tgradient = tcontext.createLinearGradient(0,0,0,256);
     106        tgradient = tcontext.createLinearGradient(0, 0, 0, 256);
    107107               
    108108        var cmap = options.getfieldvalue('colormap','jet');
     
    110110        for (var i=0; i < colorbar.length; i++) {
    111111                color = colorbar[colorbar.length-i-1];
    112                 color = [Math.round(color[0]*255),Math.round(color[1]*255),Math.round(color[2]*255)];   
    113                 tgradient.addColorStop(i/colorbar.length,'rgba('+color.toString()+',1.0)');
     112                color = [Math.round(color[0]*255), Math.round(color[1]*255), Math.round(color[2]*255)];
     113                tgradient.addColorStop(i/colorbar.length,'rgba('+color.toString()+', 1.0)');
    114114        }
    115115       
    116116        tcontext.fillStyle = tgradient;
    117         tcontext.fillRect(0,0,256,256);
     117        tcontext.fillRect(0, 0, 256, 256);
    118118        tURL = tcanvas.toDataURL();
    119         node.texture = initTexture(gl,tURL);
     119        node.texture = initTexture(gl, tURL);
    120120        node.textureCanvas = tcanvas;
    121         node.caxis = options.getfieldvalue('caxis',[ArrayMin(data),ArrayMax(data)]);
     121        node.caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
    122122        //}}}
    123         //expdisp contours {{{
     123        //{{{ expdisp contours
    124124        if (options.exist('expdisp')) {
    125                 canvas.nodes.expdisp = Node(gl,options);
     125                canvas.nodes.expdisp = Node(gl, options);
    126126                var node = canvas.nodes.expdisp;
    127127               
     
    131131                var colors = [];
    132132                var rgbcolor = [];
    133                 var xmin,xmax;
    134                 var ymin,ymax;
    135                 var zmin,zmax;
     133                var xmin, xmax;
     134                var ymin, ymax;
     135                var zmin, zmax;
    136136                var scale;
    137137               
     
    139139                var x = options.getfieldvalue('expdisp').x;
    140140                var y = options.getfieldvalue('expdisp').y;
    141                 var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf,0);
     141                var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf, 0);
    142142               
    143143                if (options.getfieldvalue('expdisp').z) {
     
    147147
    148148                //Compute coordinates and data range: //{{{
    149                 var modelxlim = [ArrayMin(x),ArrayMax(x)];
    150                 var modelylim = [ArrayMin(y),ArrayMax(y)];
    151                 var modelzlim = [ArrayMin(z),ArrayMax(z)];
    152                 var xlim = options.getfieldvalue('xlim',modelxlim);
    153                 var ylim = options.getfieldvalue('ylim',modelylim);
    154                 var zlim = options.getfieldvalue('zlim',modelzlim);
     149                var modelxlim = [ArrayMin(x), ArrayMax(x)];
     150                var modelylim = [ArrayMin(y), ArrayMax(y)];
     151                var modelzlim = [ArrayMin(z), ArrayMax(z)];
     152                var xlim = options.getfieldvalue('xlim', modelxlim);
     153                var ylim = options.getfieldvalue('ylim', modelylim);
     154                var zlim = options.getfieldvalue('zlim', modelzlim);
    155155                xmin = xlim[0];
    156156                xmax = xlim[1];
     
    165165                node.shaderName = 'colored';
    166166                node.shader = gl.shaders[node.shaderName].program;
    167                 node.scale = [scale, scale, scale*options.getfieldvalue('heightscale',1)];
     167                node.scale = [scale, scale, scale*options.getfieldvalue('heightscale', 1)];
    168168                node.translation = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (-2 / scale)];
    169169                node.modelMatrix = updateModelMatrix(node);
     
    178178
    179179                //retrieve some options
    180                 var linewidth=options.getfieldvalue('linewidth',1);
     180                var linewidth=options.getfieldvalue('linewidth', 1);
    181181                var edgecolor=options.getfieldvalue('edgecolor','black'); //RGBCOLOR?
    182182
     
    199199                node.buffers = initBuffers(gl, node.arrays);
    200200        } //}}}
    201         //cloud of points {{{
     201        //{{{ cloud of points
    202202        if (options.exist('cloud')) {
    203                 canvas.nodes.cloud = Node(gl,options);
     203                canvas.nodes.cloud = Node(gl, options);
    204204                var node = canvas.nodes.cloud;
    205205
     
    209209                var colors = [];
    210210                var rgbcolor = [];
    211                 var xmin,xmax;
    212                 var ymin,ymax;
    213                 var zmin,zmax;
     211                var xmin, xmax;
     212                var ymin, ymax;
     213                var zmin, zmax;
    214214                var scale;
    215215               
     
    217217                var x = options.getfieldvalue('cloud').x;
    218218                var y = options.getfieldvalue('cloud').y;
    219                 var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf,0);
     219                var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf, 0);
    220220               
    221221                if (options.getfieldvalue('cloud').z) {
     
    225225
    226226                //Compute coordinates and data range: //{{{
    227                 var modelxlim = [ArrayMin(x),ArrayMax(x)];
    228                 var modelylim = [ArrayMin(y),ArrayMax(y)];
    229                 var modelzlim = [ArrayMin(z),ArrayMax(z)];
    230                 var xlim = options.getfieldvalue('xlim',modelxlim);
    231                 var ylim = options.getfieldvalue('ylim',modelylim);
    232                 var zlim = options.getfieldvalue('zlim',modelzlim);
     227                var modelxlim = [ArrayMin(x), ArrayMax(x)];
     228                var modelylim = [ArrayMin(y), ArrayMax(y)];
     229                var modelzlim = [ArrayMin(z), ArrayMax(z)];
     230                var xlim = options.getfieldvalue('xlim', modelxlim);
     231                var ylim = options.getfieldvalue('ylim', modelylim);
     232                var zlim = options.getfieldvalue('zlim', modelzlim);
    233233                xmin = xlim[0];
    234234                xmax = xlim[1];
     
    243243                node.shaderName = 'colored';
    244244                node.shader = gl.shaders[node.shaderName].program;
    245                 node.scale = [scale, scale, scale*options.getfieldvalue('heightscale',1)];
     245                node.scale = [scale, scale, scale*options.getfieldvalue('heightscale', 1)];
    246246                node.translation = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (-2 / scale)];
    247247                node.modelMatrix = updateModelMatrix(node);
     
    256256
    257257                //retrieve some options
    258                 var linewidth=options.getfieldvalue('linewidth',1);
     258                var linewidth=options.getfieldvalue('linewidth', 1);
    259259                var edgecolor=options.getfieldvalue('edgecolor','black'); //RGBCOLOR?
    260260
     
    277277                node.buffers = initBuffers(gl, node.arrays);
    278278        } //}}}
    279        
    280         //text display //{{{
     279        //{{{ text display
    281280        if (options.exist('textlabels')) {
    282                 var textcanvas,textcanvasid;   
    283                 textcanvasid = options.getfieldvalue('textcanvasid',options.getfieldvalue('canvasid')+'-text');
     281                var textcanvas, textcanvasid;   
     282                textcanvasid = options.getfieldvalue('textcanvasid', options.getfieldvalue('canvasid')+'-text');
    284283                textcanvas = $('#'+textcanvasid);
    285284                textcanvas.textlabels = options.getfieldvalue('textlabels',[]);
     
    287286                //setup drawing function for text canvas draw calls
    288287                textcanvas.draw = function(canvas) {
    289                         var textcontext,textlabels,textlabel,textcanvaswidth,textcanvasheight,textcoordinates; 
     288                        var textcontext, textlabels, textlabel, textcanvaswidth, textcanvasheight, textcoordinates;     
    290289                        var textposition = vec3.create();
    291290                        var mvpMatrix = mat4.create();
     
    304303                        for (text in textlabels) {
    305304                                textlabel = textlabels[text];
    306                                 mat4.multiply(mvpMatrix, canvas.cameraMatrix, canvas.nodes.overlay.modelMatrix);
     305                                mat4.multiply(mvpMatrix, canvas.camera.vpMatrix, canvas.nodes.overlay.modelMatrix);
    307306                                textposition = vec3.transformMat4(textposition, textlabel.pos, mvpMatrix);
    308307                                if (textposition[2] > 1) { //clip coordinates with z > 1
     
    310309                                }
    311310                                textcoordinates = [(textposition[0]+1.0)/2.0*textcanvaswidth, (-textposition[1]+1.0)/2.0*textcanvasheight]; //NDC to screenspace
    312                                 textcontext.font = String(options.getfieldvalue('colorbarfontsize',18))+'px "Lato",Helvetica,Arial,sans-serif';
     311                                textcontext.font = String(options.getfieldvalue('colorbarfontsize', 18))+'px "Lato", Helvetica, Arial, sans-serif';
    313312                                textcontext.fillStyle = options.getfieldvalue('colorbarfontcolor','black');
    314313                                textcontext.strokeStyle = options.getfieldvalue('colorbarfontcolor','black');
     
    321320                canvas.textcanvas = textcanvas;
    322321        } //}}}
    323         //Atmosphere {{{
     322        //{{{ lat long overlay
     323        if (options.exist('latlongoverlay')) {
     324                var overlaycanvasid = options.getfieldvalue('latlongoverlayid', options.getfieldvalue('canvasid')+'-overlay');
     325                var overlaycanvas = $('#'+overlaycanvasid)[0];
     326                var latitudes = {
     327                        //"-90": 1,
     328                        //"-65": .999,
     329                        "-60": 0.994046875,
     330                        //"-55": 0.983187500000002,
     331                        //"-50": 0.97173550854167,
     332                        "-45": 0.955729166666666,
     333                        //"-40": 0.94218750000000218,
     334                        //"-35": 0.94218750000000218,
     335                        "-30": 0.9226562500000024,
     336                        //"-25": 0.87934895833333526,
     337                        //"-20": 0.856572916666669,
     338                        //"-15": 0.830729166666665,
     339                        //"-10": 0.803552708333336,
     340                        //"-5": 0.77395833333333541,
     341                        "0": 0.74218749999999811,
     342                        //"5": 0.70950364583333347,
     343                        //"10": 0.67479166666666823,
     344                        //"15": 0.63932291666666663,
     345                        //"20": 0.60171875,
     346                        //"25": 0.563453125,
     347                        "30": 0.523390625000001,
     348                        //"35": 0.48401875,
     349                        //"40": 0.44296875,
     350                        "45": 0.4020001,
     351                        //"50": 0.3578125,
     352                        //"55": 0.311875,
     353                        "60": 0.26953124999999978,
     354                        //"65": 0.225390625,
     355                        //"70": 0.18125,
     356                        //"75": 0.13541666666666671,
     357                        //"80": 0.08953125,
     358                        //"85": 0.046250000000000013,
     359                        //"90": 0.0,
     360                }
     361                var longitudes = [-150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150, 180];
     362                overlaycanvas.draw = function(canvas) {
     363                        var rect = overlaycanvas.getBoundingClientRect();
     364                        overlaycanvas.width  = rect.width;
     365                        overlaycanvas.height = rect.height;
     366                        var ctx = overlaycanvas.getContext('2d');
     367                        var centerx = overlaycanvas.width / 2;
     368                        var centery = overlaycanvas.height / 2;
     369                        var radius = (overlaycanvas.height) / 2;
     370                        ctx.setLineDash([5, 10]);
     371                        for(latitude in latitudes) {
     372                                ctx.beginPath();
     373                                ctx.arc(centerx, centery, radius * latitudes[latitude], 0, 2 * Math.PI);
     374                                ctx.stroke();
     375                                ctx.font = String(options.getfieldvalue('colorbarfontsize', 18))+'px "Lato", Helvetica, Arial, sans-serif';
     376                                ctx.fillStyle = options.getfieldvalue('colorbarfontcolor','black');
     377                                ctx.strokeStyle = options.getfieldvalue('colorbarfontcolor','black');
     378                                ctx.textAlign = 'center';
     379                                ctx.textBaseline = 'middle';
     380                                ctx.fillText(latitude, centerx, centery + radius * latitudes[latitude]);
     381                                ctx.strokeText(latitude, centerx, centery + radius * latitudes[latitude]);
     382                        }
     383                        ctx.setLineDash([1, 0]);
     384                        for (longitude in longitudes) {
     385                                ctx.beginPath();
     386                                ctx.moveTo(centerx, centery);
     387                                ctx.lineTo(centerx + radius * Math.sin(longitudes[longitude] * DEG2RAD), centery + radius * Math.cos(longitudes[longitude] * DEG2RAD));
     388                                ctx.stroke();
     389                        }
     390                }
     391                canvas.overlaycanvas = overlaycanvas;
     392        } //}}}
     393        //{{{ additional rendering nodes
    324394        if (options.exist('render')) {
    325                 var meshresults = processmesh(md,data,options);
     395                var meshresults = processmesh(md, data, options);
    326396                var x = meshresults[0];
    327397                var y = meshresults[1];
     
    331401                var isplanet = meshresults[5];
    332402               
    333                 var modelxlim = [ArrayMin(x),ArrayMax(x)];
    334                 var modelylim = [ArrayMin(y),ArrayMax(y)];
    335                 var modelzlim = [ArrayMin(z),ArrayMax(z)];
    336                 var xlim = options.getfieldvalue('xlim',modelxlim);
    337                 var ylim = options.getfieldvalue('ylim',modelylim);
    338                 var zlim = options.getfieldvalue('zlim',modelzlim);
     403                var modelxlim = [ArrayMin(x), ArrayMax(x)];
     404                var modelylim = [ArrayMin(y), ArrayMax(y)];
     405                var modelzlim = [ArrayMin(z), ArrayMax(z)];
     406                var xlim = options.getfieldvalue('xlim', modelxlim);
     407                var ylim = options.getfieldvalue('ylim', modelylim);
     408                var zlim = options.getfieldvalue('zlim', modelzlim);
    339409                xmin = xlim[0];
    340410                xmax = xlim[1];
     
    351421                        //atmosphere
    352422                        var node = Node(gl);
    353                         canvas.nodes[canvas.nodes.length] = node;
    354423                        node.name = "atmosphere";
    355424                        node.shaderName = "SkyFromSpace";
     
    358427                        node.cullFace = gl.FRONT;
    359428                        node.enableCullFace = true;
    360                         node.mesh = GL.Mesh.icosahedron({size:6371000*atmosphereScale,subdivisions:6});
    361                         node.useIndexBuffer = false;
     429                        node.mesh = GL.Mesh.icosahedron({size: 6371000*atmosphereScale, subdivisions: 6});
    362430                        node.rotation = [0, 0, 0];
    363431                        node.translation = translation;
    364432                        node.center = [0, 0, 0];
    365433                        updateModelMatrix(node);
     434                        canvas.nodes[node.name] = node;
    366435                }
    367436                if (options.getfieldvalue('render',[]).indexOf('space')!=-1) { 
    368437                        //skysphere
    369438                        node = Node(gl);
    370                         canvas.nodes[canvas.nodes.length] = node;
    371439                        node.name = "skysphere";
    372440                        node.shaderName = "Textured";
     
    375443                        node.cullFace = gl.FRONT;
    376444                        node.enableCullFace = true;
    377                         node.mesh = GL.Mesh.sphere({size:6371000*20});
    378                         node.texture = initTexture(gl,canvas.rootPath+'textures/TychoSkymapII_t4_2k.jpg');
    379                         node.useIndexBuffer = false;
     445                        node.mesh = GL.Mesh.sphere({size: 6371000*20});
     446                        node.texture = initTexture(gl, canvas.rootPath+'textures/TychoSkymapII_t4_2k.jpg');
    380447                        node.rotation = [0, 0, 0];
    381448                        node.translation = translation;
    382449                        node.center = [0, 0, 0];
    383450                        updateModelMatrix(node);
     451                        canvas.nodes[node.name] = node;
     452                }
     453                if (canvas.clouds.enabled) {
     454                        //clouds
     455                        for (var i = 0; i < canvas.clouds.quantity; i++) {
     456                                node = Node(gl);
     457                                node.name = "clouds" + i;
     458                                node.shaderName = "Clouds";
     459                                node.shader = gl.shaders[node.shaderName];
     460                                node.drawOrder = 2;
     461                                node.cullFace = gl.BACK;
     462                                node.enableCullFace = true;
     463                                node.mesh = GL.Mesh.fromURL(canvas.rootPath+'obj/cloud.obj');
     464                                node.rotation = [0, 0, 0];
     465                                node.scale = [2500, 2500, 2500];
     466                                node.translation = [translation[0], translation[1] - 405000, translation[2]];
     467                                node.center = [0, 0, 0];
     468                                node.animation = {"time": Date.now(),"target": node.translation,"current": node.translation};
     469                                updateModelMatrix(node);
     470                                canvas.nodes[node.name] = node;
     471                                //canvas.clouds.list
     472                        }
     473                        //TODO: Steven, please add <canvas.clouds.quantity> total cloud nodes, randomly spread over the mesh, giving each one a new name and adding them to the canvas.clouds.list so that we can track them later.
     474                       
     475                }
     476                if (options.getfieldvalue('render',[]).indexOf('latlong')!=-1) {       
     477                        //latlong
     478                        node = Node(gl);
     479                        node.name = "clouds";
     480                        node.shaderName = "Clouds";
     481                        node.shader = gl.shaders[node.shaderName];
     482                        node.drawOrder = 2;
     483                        node.cullFace = gl.BACK;
     484                        node.enableCullFace = true;
     485                        node.mesh = GL.Mesh.fromURL(canvas.rootPath+'obj/cloud.obj');
     486                        node.rotation = [0, 0, 0];
     487                        node.scale = [2500, 2500, 2500];
     488                        node.translation = [translation[0], translation[1] - 405000, translation[2]];
     489                        node.center = [0, 0, 0];
     490                        node.animation = {"time": Date.now(),"target": node.translation,"current": node.translation};
     491                        updateModelMatrix(node);
     492                        canvas.nodes[node.name] = node;
    384493                }
    385494        } //}}}
  • issm/trunk-jpl/src/m/plot/plot_mesh.js

    r21300 r21683  
    1 function plot_mesh(md,options,canvas) { //{{{
     1function plot_mesh(md, options, canvas) { //{{{
    22        //PLOT_MESH - Function for plotting wireframe mesh.
    33        //
    44        //   Usage:
    5         //      plot_mesh(md,options,canvas);
     5        //      plot_mesh(md, options, canvas);
    66        //
    77        //   See also: PLOTMODEL, PLOT_MANAGER
    88
    9         //declare variables:  {{{
     9        //{{{ declare variables:
    1010        var vertices = [];
    1111        var indices = [];
    1212        var colors = [];
    1313        var nanindices = {};
    14         var xmin,xmax;
    15         var ymin,ymax;
    16         var zmin,zmax;
    17         var scale,matrixscale,vertexscale;
     14        var xmin, xmax;
     15        var ymin, ymax;
     16        var zmin, zmax;
     17        var scale, matrixscale, vertexscale;
    1818       
    1919        //Process data and model
    20         var meshresults = processmesh(md,[],options);
     20        var meshresults = processmesh(md,[], options);
    2121        var x = meshresults[0];
    2222        var y = meshresults[1];
     
    2828        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    2929        if (!md.geometry.surface) {
    30                 md.geometry.surface=NewArrayFill(md.mesh.x.length,0);
     30                md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
    3131        }
    3232        if (md.mesh.classname() == 'mesh3dsurface') {
    3333                matrixscale = 1;
    34                 vertexscale = options.getfieldvalue('heightscale',1);
     34                vertexscale = options.getfieldvalue('heightscale', 1);
    3535        }
    3636        else {
     
    3838                        z=md.geometry.surface;
    3939                }       
    40                 matrixscale = options.getfieldvalue('heightscale',1);
     40                matrixscale = options.getfieldvalue('heightscale', 1);
    4141                vertexscale = 0;
    4242        }
     
    4444
    4545        //Compute coordinates and data range:
    46         var modelxlim = [ArrayMin(x),ArrayMax(x)];
    47         var modelylim = [ArrayMin(y),ArrayMax(y)];
    48         var modelzlim = [ArrayMin(z),ArrayMax(z)];
    49         var xlim = options.getfieldvalue('xlim',modelxlim);
    50         var ylim = options.getfieldvalue('ylim',modelylim);
    51         var zlim = options.getfieldvalue('zlim',modelzlim);
     46        var modelxlim = [ArrayMin(x), ArrayMax(x)];
     47        var modelylim = [ArrayMin(y), ArrayMax(y)];
     48        var modelzlim = [ArrayMin(z), ArrayMax(z)];
     49        var xlim = options.getfieldvalue('xlim', modelxlim);
     50        var ylim = options.getfieldvalue('ylim', modelylim);
     51        var zlim = options.getfieldvalue('zlim', modelzlim);
    5252        xmin = xlim[0];
    5353        xmax = xlim[1];
     
    6565        node.shaderName = "Colored";
    6666        node.shader = gl.shaders[node.shaderName];
    67         node.lineWidth = options.getfieldvalue('linewidth',1);
     67        node.lineWidth = options.getfieldvalue('linewidth', 1);
    6868        node.scale = [1, 1, matrixscale];
    6969        node.rotation = [-90, 0, 0];
     
    7373        node.drawOrder = 0;
    7474        node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
    75         node.maskHeight = options.getfieldvalue('innermaskheight',150.0)*options.getfieldvalue('heightscale',1);
    76         node.maskColor = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
     75        node.maskHeight = options.getfieldvalue('innermaskheight', 150.0)*options.getfieldvalue('heightscale', 1);
     76        node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
    7777        updateModelMatrix(node);
    7878
     
    8080        var edgecolor = new RGBColor(options.getfieldvalue('edgecolor','black'));
    8181        if (edgecolor.ok) edgecolor = [edgecolor.r/255.0, edgecolor.g/255.0, edgecolor.b/255.0, 1.0];
    82         else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ",canvas));
     82        else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ", canvas));
    8383
    84         //node plot {{{
     84        //{{{ node plot
    8585        if (elements[0].length==6){ //prisms
    8686        }
     
    136136        }
    137137        //}}}
    138         node.mesh = GL.Mesh.load({vertices:vertices, colors:colors, triangles:indices}, null, null, gl);
     138        node.mesh = GL.Mesh.load({vertices: vertices, colors: colors, triangles: indices}, null, null, gl);
    139139} //}}}
  • issm/trunk-jpl/src/m/plot/plot_overlay.js

    r21300 r21683  
    1 function plot_overlay(md,data,options,canvas){ //{{{
     1function plot_overlay(md, data, options, canvas){ //{{{
    22        //PLOT_OVERLAY - Function for plotting a georeferenced image. 
    33        //
    44        //   Usage:
    5         //      plot_overlay(md,data,options,canvas);
     5        //      plot_overlay(md, data, options, canvas);
    66        //
    77        //   See also: PLOTMODEL, PLOT_MANAGER
    88
    9         //declare variables:  {{{
     9        //{{{ declare variables:
    1010        var vertices = [];
    1111        var indices = [];
    1212        var texcoords = [];
    1313        var nanindices = {};
    14         var xmin,xmax;
    15         var ymin,ymax;
    16         var zmin,zmax;
    17         var matrixscale,vertexscale;
     14        var xmin, xmax;
     15        var ymin, ymax;
     16        var zmin, zmax;
     17        var matrixscale, vertexscale;
    1818
    1919        //Process data and model
    20         var meshresults = processmesh(md,data,options);
     20        var meshresults = processmesh(md, data, options);
    2121        var x = meshresults[0];
    2222        var y = meshresults[1];
     
    2828        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    2929        if (!md.geometry.surface) {
    30                 md.geometry.surface=NewArrayFill(md.mesh.x.length,0);
     30                md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
    3131        }
    3232        if (md.mesh.classname() == 'mesh3dsurface') {
    3333                matrixscale = 1;
    34                 vertexscale = options.getfieldvalue('heightscale',1);
     34                vertexscale = options.getfieldvalue('heightscale', 1);
    3535        }
    3636        else {
     
    3838                        z=md.geometry.surface;
    3939                }       
    40                 matrixscale = options.getfieldvalue('heightscale',1);
     40                matrixscale = options.getfieldvalue('heightscale', 1);
    4141                vertexscale = 0;
    4242        }
     
    4444       
    4545        //Compute coordinates and data range:
    46         var modelxlim = [ArrayMin(x),ArrayMax(x)];
    47         var modelylim = [ArrayMin(y),ArrayMax(y)];
    48         var modelzlim = [ArrayMin(z),ArrayMax(z)];
    49         var xlim = options.getfieldvalue('xlim',modelxlim);
    50         var ylim = options.getfieldvalue('ylim',modelylim);
    51         var zlim = options.getfieldvalue('zlim',modelzlim);
     46        var modelxlim = [ArrayMin(x), ArrayMax(x)];
     47        var modelylim = [ArrayMin(y), ArrayMax(y)];
     48        var modelzlim = [ArrayMin(z), ArrayMax(z)];
     49        var xlim = options.getfieldvalue('xlim', modelxlim);
     50        var ylim = options.getfieldvalue('ylim', modelylim);
     51        var zlim = options.getfieldvalue('zlim', modelzlim);
    5252        xmin = xlim[0];
    5353        xmax = xlim[1];
     
    6868        node.translation = [0, 0, 0];
    6969        node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
    70         node.texture = initTexture(gl,options.getfieldvalue('overlay_image'));
    71         node.alpha = options.getfieldvalue('outeralpha',1.0);
     70        node.texture = initTexture(gl, options.getfieldvalue('overlay_image'));
     71        node.alpha = options.getfieldvalue('outeralpha', 1.0);
    7272        node.drawOrder = 1;
    7373        node.maskEnabled = options.getfieldvalue('outermask','off') == 'on';
    74         node.maskHeight = options.getfieldvalue('outermaskheight',150.0);
    75         node.maskColor = options.getfieldvalue('outermaskcolor',[0.0,0.0,1.0,1.0]);
     74        node.maskHeight = options.getfieldvalue('outermaskheight', 150.0);
     75        node.maskColor = options.getfieldvalue('outermaskcolor',[0.0, 0.0, 1.0, 1.0]);
    7676        updateModelMatrix(node);
    7777       
     
    8888               
    8989                //Reclaculate bounds based on otuer radaroverlay
    90                 modelxlim = [ArrayMin(x),ArrayMax(x)];
    91                 modelylim = [ArrayMin(y),ArrayMax(y)];
    92                 modelzlim = [ArrayMin(z),ArrayMax(z)];
     90                modelxlim = [ArrayMin(x), ArrayMax(x)];
     91                modelylim = [ArrayMin(y), ArrayMax(y)];
     92                modelzlim = [ArrayMin(z), ArrayMax(z)];
    9393                xmin = xlim[0];
    9494                xmax = xlim[1];
     
    159159                indices[indices.length] = element[2];
    160160        }
    161         node.mesh = GL.Mesh.load({vertices:vertices, coords:texcoords, triangles:indices}, null, null, gl);
     161        node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords, triangles: indices}, null, null, gl);
    162162} //}}}
  • issm/trunk-jpl/src/m/plot/plot_quiver.js

    r21437 r21683  
    1 function plot_unit(md,data,datatype,options,canvas) { //{{{
    2         //PLOT_UNIT - unit plot, display data
     1function plot_quiver(md, options, canvas, updateVel) { //{{{
     2        //PLOT_QUIVER - quiver plot with colors
    33        //
    44        //   Usage:
    5         //      plot_unit(md,data,options,canvas);
     5        //      plot_quiver(md, options, canvas)
    66        //
    77        //   See also: PLOTMODEL, PLOT_MANAGER
    88
    9         //declare variables:  {{{
     9        //{{{ declare variables:
     10        var vertices = [];
     11        var indices = [];
     12        var colors = [];
     13        var xmin, xmax;
     14        var ymin, ymax;
     15        var zmin, zmax;
     16        var scale, matrixscale, vertexscale;
     17       
    1018        //Process data and model
    11         var meshresults = processmesh(md,data,options);
     19        var meshresults = processmesh(md,[], options);
     20
    1221        var x = meshresults[0];
    1322        var y = meshresults[1];
    1423        var z = meshresults[2];
    15         var elements = meshresults[3];
     24        var elements = meshresults[3]; 
    1625        var is2d = meshresults[4];
    1726        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 = {};
    23         var xmin,xmax;
    24         var ymin,ymax;
    25         var zmin,zmax;
    26         var datamin,datamax,datadelta;
    27         var matrixscale,vertexscale;
     27        var v = updateVel != undefined ? updateVel.vel : md.initialization.vel;
     28        var vx = updateVel != undefined ? updateVel.vx : md.initialization.vx;
     29        var vy = updateVel != undefined ? updateVel.vy : md.initialization.vy;
     30               
    2831        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    2932        if (!md.geometry.surface) {
    30                 md.geometry.surface=NewArrayFill(md.mesh.x.length,0);
     33                md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
    3134        }
    3235        if (md.mesh.classname() == 'mesh3dsurface') {
    3336                matrixscale = 1;
    34                 vertexscale = options.getfieldvalue('heightscale',1);
     37                vertexscale = options.getfieldvalue('heightscale', 1);
    3538        }
    3639        else {
     
    3841                        z=md.geometry.surface;
    3942                }       
    40                 matrixscale = options.getfieldvalue('heightscale',1);
     43                matrixscale = options.getfieldvalue('heightscale', 1);
    4144                vertexscale = 0;
    4245        }
     
    4447
    4548        //Compute coordinates and data range:
    46         var modelxlim = [ArrayMin(x),ArrayMax(x)];
    47         var modelylim = [ArrayMin(y),ArrayMax(y)];
    48         var modelzlim = [ArrayMin(z),ArrayMax(z)];
    49         var xlim = options.getfieldvalue('xlim',modelxlim);
    50         var ylim = options.getfieldvalue('ylim',modelylim);
    51         var zlim = options.getfieldvalue('zlim',modelzlim);
     49        var modelxlim = [ArrayMin(x), ArrayMax(x)];
     50        var modelylim = [ArrayMin(y), ArrayMax(y)];
     51        var modelzlim = [ArrayMin(z), ArrayMax(z)];
     52        var xlim = options.getfieldvalue('xlim', modelxlim);
     53        var ylim = options.getfieldvalue('ylim', modelylim);
     54        var zlim = options.getfieldvalue('zlim', modelzlim);
    5255        xmin = xlim[0];
    5356        xmax = xlim[1];
     
    5659        zmin = zlim[0];
    5760        zmax = zlim[1];
    58         var caxis;
    5961
    6062        //Compute gl variables:
    6163        var gl = canvas.gl;
    6264        var node = Node(gl);
    63         canvas.nodes[canvas.nodes.length] = node;
    64         canvas.unitNode = node;
    65         node.name = "unit";
    66         node.shaderName = "Textured";
     65        canvas.nodes["velocity"] = node;
     66        node.name = "quiver";
     67        node.shaderName = "Colored";
    6768        node.shader = gl.shaders[node.shaderName];
     69        node.lineWidth = options.getfieldvalue('linewidth', 1);
    6870        node.scale = [1, 1, matrixscale];
    6971        node.rotation = [-90, 0, 0];
    7072        node.translation = [0, 0, 0];
    7173        node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
    72         node.alpha = options.getfieldvalue('alpha',1.0);
    73         node.drawOrder = 1;
     74        node.drawMode = gl.LINES;
     75        node.useIndexBuffer = false;
     76        node.drawOrder = 0;
    7477        node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
    75         node.maskHeight = options.getfieldvalue('innermaskheight',150.0);
    76         node.maskColor = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
    77         node.enabled = options.getfieldvalue('nodata','off') == 'off';
     78        node.maskHeight = options.getfieldvalue('innermaskheight', 150.0)*options.getfieldvalue('heightscale', 1);
     79        node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
    7880        updateModelMatrix(node);
    7981
    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
     82        //retrieve some options
     83        var edgecolor=new RGBColor(options.getfieldvalue('edgecolor','black'));
     84        if (edgecolor.ok) edgecolor = [edgecolor.r/255.0, edgecolor.g/255.0, edgecolor.b/255.0, 1.0];
     85        else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ", canvas));
     86
     87        //{{{ node plot
     88        if (elements[0].length==6){ //prisms
     89        }
     90        else if (elements[0].length==4){ //tetras
     91        }
     92        else{ //2D triangular elements
     93                var xyz = vec3.create();
     94                var xyz = vec3.create();
     95                var direction = vec3.create();
     96                var vertex = vec3.create();
     97                var vertexBase = vec3.create();
     98                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)];
     99                var magnitude;
     100                var color = edgecolor;
     101                var scaling = options.getfieldvalue('scaling', 1);
     102                var scale;
     103                for(var i = 0; i < x.length; i++){
     104                        //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
     105                        if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i])) continue;
     106                        //Scale vertices
     107                        xyz = vec3.fromValues(x[i], y[i], z[i]);
     108                        magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
     109                        vec3.normalize(direction, xyz);
     110                        vec3.scale(vertex, direction, magnitude);
     111                        vec3.copy(vertexBase, vertex);
     112                       
     113                        scale = scaling*v[i];
     114                        var modelMatrix = mat4.create();
     115                        var scaleMatrix = mat4.create();
     116                        var rotationMatrix = mat4.create();
     117                        mat4.scale(scaleMatrix, scaleMatrix, vec3.fromValues(scale, scale, scale));
     118                        mat4.rotate(rotationMatrix, rotationMatrix, Math.atan2(vy[i], vx[i]), [0.0, 0.0, 1.0]);
     119                        mat4.multiply(modelMatrix, rotationMatrix, scaleMatrix);
     120
     121                        var temp = vec3.fromValues(0.0, 0.0, 0.0);
     122                        for (var j = 0; j < 6; j++){
     123                                vec3.transformMat4(vertex, verticesArrow[j], modelMatrix);
     124                                vec3.add(vertex, vertex, vertexBase);
     125                                vertices[vertices.length] = vertex[0];
     126                                vertices[vertices.length] = vertex[1];
     127                                vertices[vertices.length] = vertex[2];
     128                               
     129                                colors[colors.length] = color[0];
     130                                colors[colors.length] = color[1];
     131                                colors[colors.length] = color[2];
     132                                colors[colors.length] = color[3];
    85133                        }
    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                                 }
    211                                
    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'));
     134                }
    296135        }
     136        //}}}
     137        node.mesh = GL.Mesh.load({vertices: vertices, colors: colors}, null, null, gl);
    297138} //}}}
  • issm/trunk-jpl/src/m/plot/plot_unit.js

    r21641 r21683  
    1 function plot_unit(md,data,datatype,options,canvas) {
     1function plot_unit(md, data, datatype, options, canvas) { //{{{
    22        //PLOT_UNIT - unit plot, display data
    33        //
    44        //   Usage:
    5         //      plot_unit(md,data,options,canvas);
     5        //      plot_unit(md, data, options, canvas);
    66        //
    77        //   See also: PLOTMODEL, PLOT_MANAGER
    88
    9         //declare variables:  {{{
     9        //{{{ declare variables:
    1010        //Process data and model
    11         var meshresults = processmesh(md,data,options);
     11        var meshresults = processmesh(md, data, options);
    1212        var x = meshresults[0];
    1313        var y = meshresults[1];
     
    2121        var indices = new Uint16Array(elements.length * 3);
    2222        var nanindices = {};
    23         var xmin,xmax;
    24         var ymin,ymax;
    25         var zmin,zmax;
    26         var datamin,datamax,datadelta;
    27         var matrixscale,vertexscale;
     23        var xmin, xmax;
     24        var ymin, ymax;
     25        var zmin, zmax;
     26        var datamin, datamax, datadelta;
     27        var matrixscale, vertexscale;
    2828        //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
    2929        if (!md.geometry.surface) {
    30                 md.geometry.surface=NewArrayFill(md.mesh.x.length,0);
     30                md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
    3131        }
    3232        if (md.mesh.classname() == 'mesh3dsurface') {
    3333                matrixscale = 1;
    34                 vertexscale = options.getfieldvalue('heightscale',1);
     34                vertexscale = options.getfieldvalue('heightscale', 1);
    3535        }
    3636        else {
     
    3838                        z=md.geometry.surface;
    3939                }       
    40                 matrixscale = options.getfieldvalue('heightscale',1);
     40                matrixscale = options.getfieldvalue('heightscale', 1);
    4141                vertexscale = 0;
    4242        }
     
    4444
    4545        //Compute coordinates and data range:
    46         var modelxlim = [ArrayMin(x),ArrayMax(x)];
    47         var modelylim = [ArrayMin(y),ArrayMax(y)];
    48         var modelzlim = [ArrayMin(z),ArrayMax(z)];
    49         var xlim = options.getfieldvalue('xlim',modelxlim);
    50         var ylim = options.getfieldvalue('ylim',modelylim);
    51         var zlim = options.getfieldvalue('zlim',modelzlim);
     46        var modelxlim = [ArrayMin(x), ArrayMax(x)];
     47        var modelylim = [ArrayMin(y), ArrayMax(y)];
     48        var modelzlim = [ArrayMin(z), ArrayMax(z)];
     49        var xlim = options.getfieldvalue('xlim', modelxlim);
     50        var ylim = options.getfieldvalue('ylim', modelylim);
     51        var zlim = options.getfieldvalue('zlim', modelzlim);
    5252        xmin = xlim[0];
    5353        xmax = xlim[1];
     
    7171        node.translation = [0, 0, 0];
    7272        node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
    73         node.alpha = options.getfieldvalue('alpha',1.0);
     73        node.alpha = options.getfieldvalue('alpha', 1.0);
    7474        node.drawOrder = 1;
    7575        node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
    76         node.maskHeight = options.getfieldvalue('innermaskheight',150.0);
    77         node.maskColor = options.getfieldvalue('innermaskcolor',[0.0,0.0,1.0,1.0]);
     76        node.maskHeight = options.getfieldvalue('innermaskheight', 150.0);
     77        node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
    7878        node.enabled = options.getfieldvalue('nodata','off') == 'off';
    7979        updateModelMatrix(node);
    8080
    8181        switch(datatype){
    82                 //element plot {{{
     82                //{{{ element plot
    8383                case 1:
    84                         pos=ArrayFindNot(data,NaN); //needed for element on water
     84                        pos=ArrayFindNot(data, NaN); //needed for element on water
    8585                        if (elements[0].length==6){ //prisms
    8686                        }
     
    9191                        break;
    9292                //}}}
    93                 //node plot {{{
     93                //{{{ node plot
    9494                case 2:
    9595                        if (elements[0].length==6){ //prisms
     
    9898                        }
    9999                        else{ //triangular elements     
    100                                 caxis = options.getfieldvalue('caxis',[ArrayMin(data),ArrayMax(data)]);
    101                                 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))];
     100                                caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
     101                                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))];
    102102                                datamin = caxis[0];
    103103                                datamax = caxis[1];
     
    145145                                }
    146146                        }
    147                         node.mesh = GL.Mesh.load({vertices:vertices, coords:texcoords, triangles:indices}, null, null, gl);
     147                        node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords, triangles: indices}, null, null, gl);
    148148                        node.mesh.octree = new GL.Octree(node.mesh);
    149149                        break;
    150150                //}}}
    151                 //quiver plot {{{
     151                //{{{ quiver plot
    152152                case 3:
    153153                        if (is2d){
    154                                 //plot_quiver(x,y,data(:,1),data(:,2),options);
     154                                //plot_quiver(x, y, data(:, 1), data(:, 2), options);
    155155                        }
    156156                        else{
    157                                 //plot_quiver3(x,y,z,data(:,1),data(:,2),data(:,3),options);
    158                         }
    159                         break;
    160                 //}}}
    161                 //node transient plot {{{
     157                                //plot_quiver3(x, y, z, data(:, 1), data(:, 2), data(:, 3), options);
     158                        }
     159                        break;
     160                //}}}
     161                //{{{ node transient plot
    162162                case 5:
    163163                        if (elements[0].length==6){ //prisms
     
    195195                                //Prevent evaluation of datasubarray min/max if caxis exists
    196196                                if (options.exist('caxis')) caxis = options.getfieldvalue('caxis');
    197                                 else caxis = [ArrayMin(data[0]),ArrayMax(data[0].slice(0,-1))];
    198                                 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))];
     197                                else caxis = [ArrayMin(data[0]), ArrayMax(data[0].slice(0,-1))];
     198                                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))];
    199199                                //Prepare texcoords to hold array of data values
    200200                                texcoords = [];
     
    222222                       
    223223                                //Initialize movie loop
    224                                 node.movieLoop = canvas.movieOptions.loop;
    225                                 node.movieInterval = 1000 / canvas.movieOptions.fps;
     224                                node.movieLoop = canvas.animation.loop;
     225                                node.movieInterval = 1000 / canvas.animation.fps;
    226226                                node.movieTimestamps = timestamps;
    227227                                node.movieLength = timestamps.length;
    228228                                node.movieFrame = 0;
    229                                 canvas.dataArray = [];
     229                                canvas.dataMarkers.values = [];
    230230                                var quiverVelFrames = {};
    231231                                for(var i=0; i < md.results.length; i++){
     
    233233                                }
    234234
    235                                 if (canvas.movieHandler) { clearInterval(canvas.movieHandler); }
    236                                 canvas.movieHandler = setInterval(function () {
    237                                                 node.movieFrame = canvas.movieFrame;
    238                                                 if (canvas.moviePlay && canvas.movieIncrement) {
    239                                                         if (canvas.movieReverse) {
    240                                                                 if (node.movieFrame == 0) {
    241                                                                         if (node.movieLoop) {
    242                                                                                 node.movieFrame = node.movieLength - 1;
    243                                                                         }
    244                                                                         else {
    245                                                                                 toggleMoviePlay(canvas);
    246                                                                         }
    247                                                                 }
    248                                                                 else {
    249                                                                         node.movieFrame = node.movieFrame - 1;
    250                                                                 }
     235                                if (canvas.animation.handler !== 0) {
     236                                        console.log("clearing...");
     237                                        clearInterval(canvas.animation.handler)
     238                                }
     239                                //TODO: Move this into webgl.js
     240                                canvas.animation.handler = setInterval(function () {
     241                                        node.movieFrame = canvas.animation.frame;
     242                                        if (canvas.animation.play && canvas.animation.increment) {
     243                                                if (node.movieFrame == node.movieLength - 1) {
     244                                                        if (node.movieLoop) {
     245                                                                node.movieFrame = 0;
    251246                                                        }
    252247                                                        else {
    253                                                                 if (node.movieFrame == node.movieLength - 1) {
    254                                                                         if (node.movieLoop) {
    255                                                                                 node.movieFrame = 0;
    256                                                                         }
    257                                                                         else {
    258                                                                                 toggleMoviePlay(canvas);
    259                                                                         }
    260                                                                 }
    261                                                                 else {
    262                                                                         node.movieFrame = node.movieFrame + 1;
    263                                                                 }
     248                                                                toggleMoviePlay(canvas);
    264249                                                        }
    265250                                                }
    266                                                 if (canvas.progressBar) {
    267                                                         canvas.progressBar.val(node.movieFrame);
    268                                                         canvas.progressBar.slider('refresh');
     251                                                else {
     252                                                        node.movieFrame = node.movieFrame + 1;
    269253                                                }
    270                                                 if (canvas.timeLabel) { canvas.timeLabel.html(node.movieTimestamps[node.movieFrame].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr")); }
    271 
    272                                                 var buffer = node.mesh.getBuffer("coords");
    273                                                 buffer.data = texcoords[node.movieFrame];
    274                                                 buffer.upload(canvas.gl.DYNAMIC_DRAW);
    275                                                 node.mesh.octree = new GL.Octree(node.mesh);
    276                                                 node.texcoords = texcoords;
    277                                                 if(options.getfieldvalue('quiver') == 'on'){
    278                                                         plot_quiver(md,options,canvas, {vel:quiverVelFrames[node.movieFrame].Vel, vx:quiverVelFrames[node.movieFrame].Vx, vy:quiverVelFrames[node.movieFrame].Vy});
    279 
     254                                                if (canvas.animation.lastFrame != canvas.animation.frame) {
     255                                                        updateMarker(canvas, false);
    280256                                                }
    281                                                 canvas.movieFrame = node.movieFrame;
    282 
    283                                                 if (canvas.moviePlay || canvas.lastMovieFrame != canvas.movieFrame) {
    284                                                         updatePlot(true);
    285                                                 }
    286 
    287                                         }, node.movieInterval);
     257                                        }
     258                                       
     259                                        if (canvas.progressBar) {
     260                                                canvas.progressBar.val(node.movieFrame);
     261                                                canvas.progressBar.slider('refresh');
     262                                        }
     263                                        if (canvas.timeLabel) { canvas.timeLabel.html(node.movieTimestamps[node.movieFrame].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr")); }
     264                                       
     265                                        var buffer = node.mesh.getBuffer("coords");
     266                                        buffer.data = texcoords[node.movieFrame];
     267                                        buffer.upload(canvas.gl.DYNAMIC_DRAW);
     268                                        node.mesh.octree = new GL.Octree(node.mesh);
     269                                        node.texcoords = texcoords;
     270                                        if(options.getfieldvalue('quiver') == 'on'){
     271                                                plot_quiver(md, options, canvas, {vel: quiverVelFrames[node.movieFrame].Vel, vx: quiverVelFrames[node.movieFrame].Vx, vy: quiverVelFrames[node.movieFrame].Vy});
     272                                        }
     273                                        canvas.animation.lastFrame = canvas.animation.frame;
     274                                        canvas.animation.frame = node.movieFrame;                                       
     275                                }, node.movieInterval);
     276                               
    288277                                if (canvas.progressBar) {
    289                                         canvas.movieFrame = 0;
     278                                        canvas.animation.frame = 0;
    290279                                        canvas.progressBar.val(0);
    291280                                        canvas.progressBar.attr('max', node.movieLength-1);
     
    294283                               
    295284                        }
    296                         node.mesh = GL.Mesh.load({vertices:vertices, coords:texcoords[0], triangles:indices}, null, null, gl);
     285                        node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords[0], triangles: indices}, null, null, gl);
    297286                        node.mesh.octree = new GL.Octree(node.mesh);
    298287                        break;
    299288                //}}}
    300289                default:
    301                         throw Error(sprintf("%s%i%s\n",'case ',datatype,' not supported'));
    302         }
    303 }
     290                        throw Error(sprintf("%s%i%s\n",'case ', datatype,' not supported'));
     291        }
     292} //}}}
  • issm/trunk-jpl/src/m/plot/slider.js

    r21300 r21683  
    6363                callback(parseFloat($(selector).val()));
    6464        });
    65 
    66         /*
    67                 NOTE:   Slider must be "refreshed" after any JavaScript change to it, as
    68                                 it is an AJAX object.
    69         */
    70         $(selector).slider('refresh');
     65        $(selector).slider('refresh'); //Slider must be "refreshed" after any JavaScript change to it, as it is an AJAX object.
    7166} //}}}
    7267
     
    8984        */
    9085        $(selector).appendTo(selector + '-value');
    91        
    92         /*
    93                 NOTE:   Slider must be "refreshed" after any JavaScript change to it, as
    94                                 it is an AJAX object.
    95         */
    96         $(selector).slider('refresh');
     86        $(selector).slider('refresh'); //Slider must be "refreshed" after any JavaScript change to it, as it is an AJAX object.
    9787} //}}}
    9888
     
    130120        $(progressBar).attr('max', 1);
    131121        $(progressBar).attr('step', 1);
    132         $(progressBar).on('slidestop', function(event, ui){
    133                 canvas.movieIncrement = true;
    134                 canvas.movieFrame = parseInt($(progressBar).val());
     122        $(progressBar).on('slidestart', function(event, ui){
     123                onSlideStart(canvas, progressBar);
    135124        });
    136125        $(progressBar).on('change', function(event, ui){
    137                 canvas.movieFrame = parseInt($(progressBar).val());
     126                onSlideChange(canvas, progressBar);
    138127        });
    139         $(progressBar).on('slidestart', function(event, ui){
    140                 canvas.movieIncrement = false; 
    141                 canvas.movieFrame = parseInt($(progressBar).val());
     128        $(progressBar).on('slidestop', function(event, ui){
     129                onSlideStop(canvas, progressBar);
    142130        });
    143 
    144         /*
    145                 NOTE:   Slider must be "refreshed" after any JavaScript change to it, as
    146                                 it is an AJAX object.
    147         */
    148         $(progressBar).slider('refresh');
    149        
    150         // Attach progress bar slider to simulation.
    151         canvas.progressBar = progressBar;
     131        $(progressBar).slider('refresh'); //Slider must be "refreshed" after any JavaScript change to it, as it is an AJAX object.     
    152132
    153133        playButton.click(function(){
    154                 canvas.moviePlay = !canvas.moviePlay;
    155                 if (canvas.moviePlay){
    156                         playButton.find("span").removeClass("fa-play");
    157                         playButton.find("span").addClass("fa-pause");
    158                 }
    159                 else{
    160                         playButton.find("span").removeClass("fa-pause");
    161                         playButton.find("span").addClass("fa-play");
    162                 }
     134                toggleMoviePlay(canvas);
    163135        });
    164136       
     137        canvas.progressBar = progressBar;
     138        canvas.playButton = playButton;
    165139        canvas.timeLabel = timeText;
    166140} //}}}
  • issm/trunk-jpl/src/m/plot/webgl.js

    r21641 r21683  
    11/*This is where we have all our webgl relevant functionality for the plotting routines: */
    2 
    3 /*This is where we have all our webgl relevant functionality for the plotting routines: */
    4 
    52//{{{ Canvas Initialization
    63function initCanvas(options) {
    7         //Initialize open Gl for each canvas, if needed:
     4        //Initialize open Gl for each canvas and clear any previous animation handlers, once per plotmodel call:
    85        canvas = document.getElementById(options.getfieldvalue('canvasid'));
    96        //var canvas = document.getElementById(options.getfieldvalue('canvasid'));
    107        if (!canvas.initialized) {
    118                typedArraySliceSupport();
    12                 canvas.gl = initWebGL(canvas,options);
     9                if (!isEmptyOrUndefined(canvas.draw) && canvas.draw.handler !== 0)      { window.cancelAnimationFrame(canvas.draw.handler); }
     10                if (!isEmptyOrUndefined(canvas.animation) && canvas.animation.handler !== 0) { clearInterval(canvas.animation.handler); }
     11                initWebGL(canvas, options);
     12                initializeMarker(canvas);
    1313                canvas.nodes = [];
    14                 if (canvas.drawHandler) { window.cancelAnimationFrame(canvas.drawHandler); }
    15                 draw(canvas,options);
     14                draw(canvas);
    1615                canvas.initialized = true;
    1716        }
    1817        return canvas;
    1918}
    20 function initWebGL(canvas,options) { //{{{
     19function initWebGL(canvas, options) { //{{{
     20        //Initialize canvas.gl on page load, reusing gl context on additional runs
    2121        var gl;
    22         try {
    23                 if (!canvas.gl) {
    24                         gl = GL.create({canvas:canvas});
    25                         // Enable depth testing
    26                         gl.enable(gl.DEPTH_TEST);
    27                         // Near things obscure far things
    28                         gl.depthFunc(gl.LEQUAL);
    29                         // Enable color blending/overlay
    30                         gl.enable(gl.BLEND);
    31                         // Enable face culling
    32                         gl.enable(gl.CULL_FACE);
    33                         gl.cullFace(gl.FRONT);
    34                         // Load shaders and store them in gl object
    35                         gl.shaders = loadShaders(gl,options.getfieldvalue('rootpath','../../../js/'));
    36                        
    37                         // Add event listeners for canvas
    38                         var displayview = options.getfieldvalue('displayview','off') == 'on';
    39                         var displayzoom = options.getfieldvalue('displayzoom','off') == 'on';
    40                         var mc = new Hammer.Manager(canvas);
    41                        
    42                         mc.add( new Hammer.Tap({ event: 'singletap' }) );
    43                         mc.add(new Hammer.Pan({threshold:0, pointers:0}));
    44                         mc.add(new Hammer.Pinch({threshold:0})).recognizeWith(mc.get('pan'));
    45                         mc.on('singletap', function (ev) {onTap(ev,canvas);});
    46                         mc.on('panstart panmove', function (ev) {onPan(ev,canvas,displayview);});
    47                         mc.on('pinchstart pinchmove', function (ev) {onPinch(ev,canvas,displayview);});
    48                        
    49                         //canvas.addEventListener('mousemove', function (ev) {onTap(ev,canvas);}, false);
    50                         canvas.addEventListener('mousewheel', function (ev) {onZoom(ev,canvas,displayzoom)}, false);
    51                         canvas.addEventListener('DOMMouseScroll', function (ev) {onZoom(ev,canvas,displayzoom)}, false);
    52                 }
    53                 else {
    54                         gl = canvas.gl;
    55                 }
    56         }
    57         catch(e) {
    58                 console.log(e);
    59                 return;
     22        if (!canvas.gl) {
     23                gl = GL.create({canvas: canvas});
     24                gl.enable(gl.DEPTH_TEST); // Enable depth testing
     25                gl.depthFunc(gl.LEQUAL); // Near things obscure far things
     26                gl.enable(gl.BLEND); // Enable color blending/overlay
     27                gl.enable(gl.CULL_FACE); // Enable face culling
     28                gl.cullFace(gl.FRONT);
     29                gl.shaders = loadShaders(gl, options.getfieldvalue('rootpath', '../../../js/')); // Load shaders and store them in gl object
     30                gl.textures = {};
     31               
     32                // Add event listeners for canvas
     33                var displayview = options.getfieldvalue('displayview', 'off') == 'on';
     34                var displayzoom = options.getfieldvalue('displayzoom', 'off') == 'on';
     35                var mc = new Hammer.Manager(canvas);
     36               
     37                mc.add( new Hammer.Tap({event: 'singletap' }) );
     38                mc.add(new Hammer.Pan({threshold: 0, pointers: 0}));
     39                mc.add(new Hammer.Pinch({threshold: 0})).recognizeWith(mc.get('pan'));
     40                mc.on('singletap', function (ev) {onTap(ev, canvas);});
     41                mc.on('panstart panmove', function (ev) {onPan(ev, canvas, displayview);});
     42                mc.on('pinchstart pinchmove', function (ev) {onPinch(ev, canvas, displayview);});
     43               
     44                canvas.addEventListener('mousewheel', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
     45                canvas.addEventListener('DOMMouseScroll', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
     46        }
     47        else {
     48                gl = canvas.gl;
    6049        }
    6150       
    6251        // Add context state variables
    63         //TODO:Group variables in objects for organization and naming
    6452        canvas.gl = gl;
    65         canvas.rootPath = options.getfieldvalue('rootpath','../../../js/');
    66         canvas.brush = options.getfieldvalue('brush',{'enabled':'off','strength':0.075,'falloff':0.5});
    67         canvas.cameraPosition = vec3.create();
    68         canvas.cameraMatrix = mat4.create();
    69         canvas.controlSensitivity = options.getfieldvalue('controlsensitivity',1);
    70         canvas.dataArray = [];
    71         canvas.dataMarkersAllowed = options.getfieldvalue('datamarkers','off') == 'on';
    72         canvas.dataMarkersEnabled = true; //if data marker feature is on, user can toggle feature on and off
    73         canvas.dataMarkerImage = options.getfieldvalue('datamarkers_image',canvas.rootPath+'textures/data_marker.svg');
    74         canvas.dataMarkerSize = options.getfieldvalue('datamarkerssize',[32,32]);
    75         canvas.dataMarkerOptions = options.getfieldvalue('datamarkersoptions',{'enabled':'on','image':canvas.rootPath+'textures/data_marker.svg','size':[32,32],'format':['X: %.2e<br>Y: %.2e<br>Z: %.2e]<br>Value: %0.1f','x','y','z','value'],'animated':false});
    76         canvas.inverseCameraMatrix = mat4.create();
    77         canvas.id = options.getfieldvalue('canvasid','.sim-canvas');
    78         canvas.movieFrame = 0;
    79         canvas.moviePlay = true;
    80         canvas.movieReverse = false;
    81         canvas.movieIncrement = true;
    82         canvas.movieOptions = options.getfieldvalue('movieoptions',{'fps':4,'loop':true});
    83         canvas.moviefps = options.getfieldvalue('moviefps',5);
    84         canvas.rotation = options.getfieldvalue('view',[0,90]); //0 azimuth, 90 elevation
    85         canvas.rotationAzimuthBounds = options.getfieldvalue('azlim',[0,360]);
    86         canvas.rotationElevationBounds = options.getfieldvalue('ellim',[-180,180]);
    87         canvas.translation = options.getfieldvalue('origin',[0,0,0]);
    88         canvas.twod = options.getfieldvalue('2d','off') == 'on';
    89         canvas.view = options.getfieldvalue('view',[0,90]);
    90         canvas.viewPanning = options.getfieldvalue('enablepanning','off') == 'on';
    91         canvas.vInverseMatrix = mat4.create();
    92         canvas.zoomBounds = options.getfieldvalue('zoomlim',[0.001,100.0]);
    93         canvas.zoom = clamp(options.getfieldvalue('zoom',1.0), canvas.zoomBounds[0], canvas.zoomBounds[1]);
    94         canvas.zoomLast = canvas.zoom;
    95         var backgroundcolor = new RGBColor(options.getfieldvalue('backgroundcolor','lightcyan'));
     53        canvas.textcanvas = null;
     54        canvas.overlaycanvas = null;
     55        canvas.unitNode = {};
     56        canvas.unitData = {};
     57        canvas.controlSensitivity = options.getfieldvalue('controlsensitivity', 1);
     58        canvas.id = options.getfieldvalue('canvasid', '.sim-canvas');
     59        canvas.rootPath = options.getfieldvalue('rootpath', '../../../js/');
     60        canvas.selector = $('#' + canvas.id);
     61        var backgroundcolor = new RGBColor(options.getfieldvalue('backgroundcolor', 'lightcyan'));
    9662        if (backgroundcolor.ok) { canvas.backgroundcolor = [backgroundcolor.r/255.0, backgroundcolor.g/255.0, backgroundcolor.b/255.0, 1.0]; }
    97         else { throw Error(sprintf('s%s%s\n','initWebGL error message: cound not find out background color for curent canvas ',canvas)); }
    98 
     63        else { throw Error(sprintf('s%s%s\n','initWebGL error message: cound not find out background color for curent canvas ', canvas)); }
     64       
     65        //Property intiialization, using values from options first, then from default values.
     66        var animation = options.getfieldvalue('movies', {});
     67        canvas.animation = {
     68                frame: defaultFor(animation.frame, 0),
     69                play: defaultFor(animation.play, true),
     70                increment: defaultFor(animation.increment, true),
     71                fps: defaultFor(animation.fps, 4),
     72                loop: defaultFor(animation.loop, true),
     73                handler: defaultFor(animation.handler, 0)
     74        }
     75        var brush = options.getfieldvalue('brush', {});
     76        canvas.brush = {
     77                enabled: defaultFor(brush.enabled, false),
     78                strength: defaultFor(brush.strength, 0.075),
     79                falloff: defaultFor(brush.falloff, 0.5),
     80                hit: defaultFor(brush.hit, {})
     81        };
     82        var camera = options.getfieldvalue('camera', {});
     83        canvas.camera = {
     84                position: defaultFor(camera.position, vec3.create()),
     85                rotation: defaultFor(camera.rotation, vec3.create()),
     86                near: defaultFor(camera.near, 1e3),
     87                far: defaultFor(camera.far, 1e10),
     88                fov: defaultFor(camera.fov, 45),
     89                vMatrix: defaultFor(camera.vMatrix, mat4.create()),
     90                pMatrix: defaultFor(camera.pMatrix, mat4.create()),
     91                vpMatrix: defaultFor(camera.vpMatrix, mat4.create()),
     92                vInverseMatrix: defaultFor(camera.vInverseMatrix, mat4.create()),
     93                pInverseMatrix: defaultFor(camera.pInverseMatrix, mat4.create()),
     94                vpInverseMatrix: defaultFor(camera.vpInverseMatrix, mat4.create()),
     95                ready: defaultFor(camera.ready, false)
     96        };
     97        var clouds = options.getfieldvalue('clouds', {});
     98        canvas.clouds = {
     99                enabled: defaultFor(clouds.enabled, false),
     100                height: defaultFor(clouds.height, 7500),
     101                quantity: defaultFor(clouds.quantity, 10)
     102        };
     103        var dataMarkers = options.getfieldvalue('datamarkers', {});
     104        canvas.dataMarkers = {
     105                enabled: defaultFor(dataMarkers.enabled, true),
     106                values: defaultFor(dataMarkers.values, []),
     107                image: defaultFor(dataMarkers.image, canvas.rootPath+'textures/data_marker.svg'),
     108                size: defaultFor(dataMarkers.size, [32, 32]),
     109                format: defaultFor(dataMarkers.format, ['X: %.2e<br>Y: %.2e<br>Z: %.2e<br>Value: %0.1f', 'x', 'y', 'z', 'value']),
     110                animated: defaultFor(dataMarkers.animated, false),
     111                labels: defaultFor(dataMarkers.labels, []),
     112                font: defaultFor(dataMarkers.font, ''),
     113                marker: defaultFor(dataMarkers.marker, document.getElementById('sim-data-marker-' + canvas.id))
     114        };
     115        var draw = options.getfieldvalue('draw', {});
     116        canvas.draw = {
     117                ready: defaultFor(draw.ready, false),
     118                handler: defaultFor(draw.handler, null)
     119        };
     120        var view = options.getfieldvalue('view', {});
     121        canvas.view = {
     122                position: defaultFor(view.position, [0.0, 0.0, 0.0]),
     123                rotation: defaultFor(view.rotation, [0, 90]),
     124                zoom: defaultFor(view.zoom, 1.0),
     125                zoomLimits: defaultFor(view.zoomLimits, [0.001, 100.0]),
     126                lastZoom: defaultFor(view.lastZoom, 1.0),
     127                azimuthLimits: defaultFor(view.azimuthLimits, [0, 360]),
     128                elevationLimits: defaultFor(view.elevationLimits, [-180, 180]),
     129                panningEnabled: defaultFor(view.panningEnabled, false),
     130                twod: defaultFor(view.twod, false)
     131        };
     132
     133        //Override with parameters from URL, if any
    99134        //TODO: Make permalinks more robust and less interdependent on UI
    100         //Override with parameters from URL, if any
    101135        if (!canvas.usedparemters) {
    102136                function getJsonFromUrl() {
    103137                        var query = location.search.substr(1);
    104138                        var result = {};
    105                         query.split("&").forEach(function(part) {
    106                                 var item = part.split("=");
     139                        query.split('&').forEach(function(part) {
     140                                var item = part.split('=');
    107141                                result[item[0]] = decodeURIComponent(item[1]);
    108142                        });
     
    111145                parameters = getJsonFromUrl();
    112146               
    113                 if (parameters["rotation"]) {
    114                         canvas.rotation = JSON.parse(parameters["rotation"]);
    115                 }
    116                 if (parameters["view"]) {
    117                         canvas.view = JSON.parse(parameters["view"]);
    118                 }
    119                 if (parameters["zoom"]) {
    120                         canvas.zoom = JSON.parse(parameters["zoom"]);
    121                 }
    122                 if (parameters["initial"]) {
    123                         initial = JSON.parse(parameters["initial"]);
     147                if (parameters['view']) {
     148                        canvas.view = JSON.parse(parameters['view']);
     149                }
     150                if (parameters['initial']) {
     151                        initial = JSON.parse(parameters['initial']);
    124152                        if (!initial) {
    125153                                if (typeof SolveGlacier == 'function') {
     
    133161                canvas.usedparemters = true;
    134162        }
    135 
    136         return gl;
    137163} //}}}
    138164function generatePermalink() { //{{{
    139         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);
    140         window.prompt("Share this simulation: ", permalink);
    141 } //}}}
    142 function loadShaders(gl,rootPath) { //{{{
     165        var permalink = window.location.origin + window.location.pathname + '&view=' + JSON.stringify(canvas.view) + '&initial=' + JSON.stringify(initial);
     166        window.prompt('Share this simulation: ', permalink);
     167} //}}}
     168function loadShaders(gl, rootPath) { //{{{
    143169        var shaders = {};
    144170        shaders.Colored = new GL.Shader.fromURL(rootPath+'shaders/Colored.vsh', rootPath+'shaders/Colored.fsh', null, gl);
     
    146172        shaders.SkyFromSpace = new GL.Shader.fromURL(rootPath+'shaders/SkyFromSpace.vert', rootPath+'shaders/SkyFromSpace.frag', null, gl);
    147173        shaders.GroundFromSpace = new GL.Shader.fromURL(rootPath+'shaders/GroundFromSpace.vert', rootPath+'shaders/GroundFromSpace.frag', null, gl);
     174        shaders.Clouds = new GL.Shader.fromURL(rootPath+'shaders/Clouds.vert', rootPath+'shaders/Clouds.frag', null, gl);
    148175        return shaders;
    149176} //}}}
    150 function initTexture(gl,imageSource) { //{{{
    151         return GL.Texture.fromURL(imageSource, {minFilter:gl.LINEAR_MIPMAP_LINEAR, magFilter:gl.LINEAR}, null, gl);
     177function initTexture(gl, imageSource) { //{{{
     178        //Initialize textures, or load from memory if they already exist.
     179        if (isEmptyOrUndefined(gl.textures[imageSource])) {
     180                gl.textures[imageSource] = GL.Texture.fromURL(imageSource, {minFilter: gl.LINEAR_MIPMAP_LINEAR, magFilter: gl.LINEAR}, null, gl);
     181        }
     182        return gl.textures[imageSource];
    152183} //}}}
    153184function Node(gl) { //{{{
    154185        //Returns a Node object that contains default display states for webgl object. center represents pivot point of rotation.
    155186        return {
    156                 alpha:1.0,
    157                 buffers:[],
    158                 cullFace:gl.BACK,
    159                 disableDepthTest:false,
    160                 drawMode:gl.TRIANGLES,
    161                 drawOrder:0,
    162                 enabled:true,
    163                 enableCullFace:true,
    164                 hideOcean:false,
    165                 lineWidth:1.0,
    166                 maskEnabled:false,
    167                 maskHeight:150.0,
    168                 maskColor:vec4.fromValues(0.0, 0.0, 1.0, 1.0),
    169                 mesh:null,
    170                 name:'node',
    171                 shaderName:'Colored',
    172                 shader:gl.shaders.Colored,
    173                 texture:null,
    174                 useIndexBuffer:true,
    175                 center:vec3.create(),
    176                 scale:vec3.fromValues(1, 1, 1),
    177                 rotation:vec3.create(),
    178                 translation:vec3.create(),
    179                 modelMatrix:mat4.create(),
    180                 rotationMatrix:mat4.create(),
    181                 inverseModelMatrix:mat4.create(),
    182                 inverseRotationMatrix:mat4.create()
     187                alpha: 1.0,
     188                buffers: [],
     189                cullFace: gl.BACK,
     190                disableDepthTest: false,
     191                drawMode: gl.TRIANGLES,
     192                drawOrder: 0,
     193                enabled: true,
     194                enableCullFace: true,
     195                hideOcean: false,
     196                lineWidth: 1.0,
     197                maskEnabled: false,
     198                maskHeight: 150.0,
     199                maskColor: vec4.fromValues(0.0, 0.0, 1.0, 1.0),
     200                mesh: null,
     201                name: 'node',
     202                shaderName: 'Colored',
     203                shader: gl.shaders.Colored,
     204                texture: null,
     205                useIndexBuffer: true,
     206                center: vec3.create(),
     207                scale: vec3.fromValues(1, 1, 1),
     208                rotation: vec3.create(),
     209                translation: vec3.create(),
     210                modelMatrix: mat4.create(),
     211                rotationMatrix: mat4.create(),
     212                inverseModelMatrix: mat4.create(),
     213                inverseRotationMatrix: mat4.create()
    183214        };
    184215} //}}}
    185216function debugNodes(canvasid) { //{{{
    186217        var canvasid = canvasid || '.sim-canvas';
    187         var nodes = $(canvasid)[0].nodes;
     218        var nodes = document.getElementById(canvasid).nodes;
    188219        console.log(canvasid, 'Nodes:');
    189220        for (var node in nodes) {
     
    196227
    197228        var translationMatrix = mat4.create();
    198         mat4.translate(translationMatrix, translationMatrix, [-node.center[0],-node.center[1],-node.center[2]]); //scale/rotation centering
     229        mat4.translate(translationMatrix, translationMatrix, vec3.negate(vec3.create(), node.center)); //scale/rotation centering
    199230        mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
    200231       
     
    226257        node.inverseModelMatrix = mat4.invert(mat4.create(), modelMatrix);
    227258        node.rotationMatrix = rotationMatrix;
    228         node.inverseRotationMatrix = mat4.invert(mat4.create(), rotationMatrix);;
     259        node.inverseRotationMatrix = mat4.invert(mat4.create(), rotationMatrix);
    229260} //}}}
    230261function clamp(value, min, max) { //{{{
    231262        return Math.max(min, Math.min(value, max));
    232263} //}}}
    233 function recover(canvasid,name,defaultvalue) { //{{{
    234         var canvas = document.getElementById(canvasid);
    235         if (canvas && canvas.hasOwnProperty(name)) { return canvas[name]; }
    236         return defaultvalue;
     264function defaultFor(name, value) { //{{{
     265        return typeof name !== 'undefined' ? name : value;
     266} //}}}
     267function isEmptyOrUndefined(object) { //{{{
     268        return object === undefined || Object.getOwnPropertyNames(object).length === 0;
     269} //}}}
     270function recover(canvasid, name, value) { //{{{
     271        //Traverse canvas object tree for property defined by dot delimited string, returning it, or a default value if it is not found.
     272        var object = document.getElementById(canvasid);
     273        var properties = name.split('.');
     274        for (var i = 0; i < properties.length; ++i) {
     275                object = object[properties[i]];
     276                if (typeof object === 'undefined') { break; }
     277    }
     278        return defaultFor(object, value);
    237279} //}}}
    238280function typedArraySliceSupport() { //{{{
     
    279321        }
    280322} //}}}
    281 function raycast(canvas, x, y) { //{{{
    282         var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.cameraMatrix, canvas.unitNode.modelMatrix));
    283         var origin = origin || vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 0], inverseMVPMatrix);
    284         var far = far || vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 1.0], inverseMVPMatrix);
    285         var ray = vec3.subtract(vec3.create(), far, origin);
    286 
    287         var mesh = canvas.unitNode.mesh;
    288         if (!mesh || mesh.ready == false) { return; }
    289         if (!mesh.octree) { mesh.octree = new GL.Octree(mesh); }
    290        
    291         var hit = mesh.octree.testRay(origin, ray, 1e3, 1e10);
    292        
    293         if(!hit) { return; }
    294        
    295         hit.modelPos = vec3.copy(vec3.create(), hit.pos);
    296         vec3.transformMat4(hit.pos, hit.pos, canvas.unitNode.modelMatrix);
    297         vec3.transformMat4(hit.normal, hit.normal, canvas.unitNode.modelMatrix);
    298 
    299         return hit;
    300 } //}}}
    301323//}}}
    302324//{{{ Interface Functions
     
    304326        //Sets up a marker on a canvas that will track a point on the mesh. Can be dismissed by closing the display or clicking the marker.
    305327        ev.preventDefault();
    306         if (!(canvas.dataMarkersAllowed && canvas.dataMarkersEnabled)) { return; }
    307         initializeMarker(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, true);
    308         brushModify(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
    309 } //}}}
    310 function brushModify(canvas, x, y) { //{{{
     328        if (!canvas.dataMarkers.enabled) { return; }
     329        var hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
     330        canvas.dataMarkers.marker.hit = hit;
     331        canvas.brush.hit = hit;
     332        updateMarker(canvas, true);
     333        brushModify(canvas);
     334} //}}}
     335function onPan(ev, canvas, displaylog) { //{{{
     336        ev.preventDefault();
     337       
     338        if (canvas.dataMarkers.enabled == 'on') {
     339                canvas.brush.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
     340                brushModify(canvas);
     341        }
     342       
     343        if (ev.type == 'panstart') {
     344                canvas.lastDeltaX = 0;
     345                canvas.lastDeltaY = 0;
     346        }
     347        if (ev.srcEvent.shiftKey || ev.pointers.length == 2) {
     348                if (!canvas.view.panningEnabled) return;
     349                var deltaX = (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
     350                var deltaY = (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
     351               
     352                if (canvas.view.twod) {
     353                        canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * 0) * deltaY;
     354                        canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * 0) * deltaY;
     355                }
     356                else {
     357                        canvas.view.position[0] += Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaX - Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaY;
     358                        canvas.view.position[2] += Math.sin(DEG2RAD * canvas.view.rotation[0]) * deltaX + Math.cos(DEG2RAD * canvas.view.rotation[0]) * deltaY;
     359                }
     360        }
     361       
     362        else {
     363                canvas.view.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * -2 * canvas.controlSensitivity * RAD2DEG;
     364                canvas.view.rotation[1] += (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight * -2 * canvas.controlSensitivity * RAD2DEG;
     365               
     366                if (canvas.view.rotation[0] > 360) { canvas.view.rotation[0] -= 360; };
     367                if (canvas.view.rotation[0] < -360) { canvas.view.rotation[0] += 360; };
     368                if (canvas.view.rotation[1] > 180) { canvas.view.rotation[1] -= 360; };
     369                if (canvas.view.rotation[1] < -180) { canvas.view.rotation[1] += 360; };
     370               
     371                canvas.view.rotation[0] = clamp(canvas.view.rotation[0], canvas.view.azimuthLimits[0], canvas.view.azimuthLimits[1]);
     372                canvas.view.rotation[1] = clamp(canvas.view.rotation[1], canvas.view.elevationLimits[0], canvas.view.elevationLimits[1])
     373        }
     374        canvas.lastDeltaX = ev.deltaX;
     375        canvas.lastDeltaY = ev.deltaY;
     376       
     377        repositionMarker(canvas);
     378       
     379        if (displaylog) { console.log(canvas.view.rotation); }
     380} //}}}
     381function onPinch(ev, canvas, displaylog) { //{{{
     382        ev.preventDefault();
     383        if (ev.type == 'pinchstart') { canvas.view.lastZoom = canvas.view.zoom; }
     384        else { modifyZoom(ev.scale * canvas.view.lastZoom, canvas, displaylog); }
     385} //}}}
     386function onZoom(ev, canvas, displaylog) { //{{{
     387        ev.preventDefault();
     388        var delta = clamp(ev.scale || ev.wheelDelta || -ev.detail, -1, 1) * canvas.controlSensitivity * canvas.view.zoom / 20;
     389        modifyZoom(canvas.view.zoom + delta, canvas, displaylog);
     390} //}}}
     391function modifyZoom(value, canvas, displaylog) { //{{{
     392        canvas.view.zoom = clamp(value, canvas.view.zoomLimits[0], canvas.view.zoomLimits[1]);
     393        repositionMarker(canvas);
     394        if (displaylog) { console.log(canvas.view.zoom); }
     395} //}}}
     396function modifyDataMarkersEnabled(value, canvas) { //{{{
     397        canvas.dataMarkers.enabled = value;
     398} //}}}
     399function toggleMoviePlay(canvas) { //{{{
     400        canvas.animation.play = !canvas.animation.play;
     401        if (canvas.animation.play){
     402                canvas.playButton.find('span').removeClass('fa-play');
     403                canvas.playButton.find('span').addClass('fa-pause');
     404        }
     405        else{
     406                canvas.playButton.find('span').removeClass('fa-pause');
     407                canvas.playButton.find('span').addClass('fa-play');
     408        }
     409} //}}}
     410function onSlideStart(canvas, progressBar) { //{{{
     411        if (!isEmptyOrUndefined(canvas.animation)) {
     412                canvas.animation.increment = false;     
     413                canvas.animation.frame = parseInt($(progressBar).val());
     414                //console.log(canvas.animation.frame);
     415                //updateMarker(canvas, false);
     416        }
     417} //}}}
     418function onSlideChange(canvas, progressBar) { //{{{
     419        if (!isEmptyOrUndefined(canvas.animation)) {
     420                canvas.animation.frame = parseInt($(progressBar).val());
     421                //console.log("change");
     422                updateMarker(canvas, false);
     423        }
     424} //}}}
     425function onSlideStop(canvas, progressBar) { //{{{
     426        if (!isEmptyOrUndefined(canvas.animation)) {
     427                canvas.animation.increment = true;     
     428                canvas.animation.frame = parseInt($(progressBar).val());
     429                //console.log(canvas.animation.frame);
     430                //updateMarker(canvas, false);
     431        }
     432} //}}}
     433//}}}
     434//{{{ Interaction Functions
     435function raycast(canvas, x, y) { //{{{
     436        //Performs raycast on canvas.unitNode.mesh using x/y screen coordinates. Returns hit objects with hit position, coords, and indicies of ray-triangle intersection.
     437        //TODO: Diagnose marker issues with orthographic views and slr-eustatic updates when switching between basins.
     438        var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.camera.vpMatrix, canvas.unitNode.modelMatrix));
     439        var origin = vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 0], inverseMVPMatrix);
     440        var far = far || vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 1.0], inverseMVPMatrix);
     441        var ray = vec3.subtract(vec3.create(), far, origin);
     442
     443        var mesh = canvas.unitNode.mesh;
     444        if (!mesh) { return; }
     445        if (!mesh.octree) { mesh.octree = new GL.Octree(mesh); }
     446       
     447        var hit = mesh.octree.testRay(origin, ray, 1e3, 1e10);
     448       
     449        if(!hit) { return; }
     450       
     451        hit.modelPos = vec3.copy(vec3.create(), hit.pos);
     452        vec3.transformMat4(hit.pos, hit.pos, canvas.unitNode.modelMatrix);
     453        vec3.transformMat4(hit.normal, hit.normal, canvas.unitNode.modelMatrix);
     454
     455        return hit;
     456} //}}}
     457function brushModify(canvas) { //{{{
    311458        //This function takes in the canvas and x/y coordinates, performing a raycast against the mesh, and modifies the mesh using a the canvas.brush.strength and canvas.brush.falloff properties.
    312459        //Currently the brush extends to the raycasted element and its immediate neighbors.
     
    314461        if (!canvas.unitNode || canvas.brush.enabled != 'on') { return; }
    315462       
    316         var hit = raycast(canvas, x, y);
     463        var hit = canvas.brush.hit;
    317464
    318465        if (hit) {
    319                 var bufferVertices = canvas.unitNode.mesh.getBuffer("vertices");
     466                var bufferVertices = canvas.unitNode.mesh.getBuffer('vertices');
    320467                var vertices = bufferVertices.data;
    321                 var bufferCoords = canvas.unitNode.mesh.getBuffer("coords");
     468                var bufferCoords = canvas.unitNode.mesh.getBuffer('coords');
    322469                var coords = bufferCoords.data;
    323470
     
    361508                bufferCoords.upload(canvas.gl.DYNAMIC_DRAW);
    362509                canvas.unitNode.mesh.octree = new GL.Octree(canvas.unitNode.mesh);     
    363         }
    364 }
    365 function initializeMarker(canvas, x, y, reset, origin, far) { //{{{
    366         //Can be called by onTap to create/reuse a marker, or by the marker's update function. Origin and far are optional and only used by the update function for recreating the raycast.
    367         if (!canvas.unitNode) { return; }
    368 
    369         var hit = raycast(canvas, x, y);
    370 
    371         if (hit) {
    372                 canvas.lastHit = hit;
    373                 var dataMarkerSize = canvas.dataMarkerSize;
    374                 if (!canvas.marker) {
    375                         $('#' + canvas.id).after( '<img src=' + canvas.dataMarkerImage + ' alt="data marker" width="' + dataMarkerSize[0] + '" height="' + dataMarkerSize[1] + '" id="sim-data-marker-' + canvas.id + '" class="sim-data-marker noselect tooltip" data-tooltip-content="#tooltip-content-data-marker-' + canvas.id + '"></img><span id="tooltip-content-data-marker-' + canvas.id + '"></span>');
    376                         $('#sim-data-marker-' + canvas.id).css({
    377                                 'position': 'absolute',
    378                                 'left': (Math.round(x) - dataMarkerSize[0] / 2) + 'px',
    379                                 'top': (Math.round(y) - dataMarkerSize[1]) + 'px',
    380                                 'width': dataMarkerSize[0] + 'px',
    381                                 'height': dataMarkerSize[1] + 'px',
    382                                 'pointer-events': 'all',
    383                                 'cursor': 'pointer',
    384                                 'display': 'none'
    385                         });
    386                         $('#sim-data-marker-' + canvas.id).tooltipster({
    387                                 contentAsHTML: 'true',
    388                                 animation: 'grow',
    389                                 maxWidth: 320,
    390                                 maxHeight: 320,
    391                                 zIndex: 1000,
    392                                 trigger: 'custom',
    393                                 triggerOpen: {
    394                                         mouseenter: false,
    395                                         originClick: true,
    396                                         touchstart: false
    397                                 },
    398                                 triggerClose: {
    399                                         mouseleave: false,
    400                                         originClick: true,
    401                                         touchleave: false
    402                                 },
    403                         });
    404                         canvas.marker = $('#sim-data-marker-' + canvas.id);
    405                         canvas.marker.on('click touch', function () {
    406                                 canvas.marker.fadeOut(175);
    407                                 canvas.dataMarkerDisplay.tooltipster('close');
    408                         });
    409                         canvas.marker.fadeIn(175);
    410                 }
    411                
    412                 canvas.marker.hit = hit;
    413 
    414                 if (!canvas.dataMarkerDisplay) {
    415                         canvas.dataMarkerDisplay = $('#sim-data-marker-' + canvas.id);
    416                         canvas.dataMarkerDisplay.tooltipster('open');
    417                 }
    418 
    419                 updatePlot(true);
    420                 repositionMarker();
    421                 if (reset) { modifyDataMarkersEnabled(true,canvas); }
    422         }
    423 } //}}}
    424 function updatePlot(reset) {
    425         if (!canvas.lastHit) { return; }
    426         var hit = canvas.lastHit;
     510               
     511                //Update clouds if rendered
     512                //TODO: Steven, once you update the cloud generation in applyoptions.js, modify this code block to move the clouds as well. We'll want to move them individually later, but moving them all is ok for now.
     513                for (var i = 0; i < canvas.clouds.quantity; i++) {
     514                        if (canvas.nodes['clouds' + i]) {
     515                                var v1 = vec3.fromValues(vertices[hit.indices[0] * 3], vertices[hit.indices[0] * 3 + 1], vertices[hit.indices[0] * 3 + 2]);
     516                                var v2 = vec3.fromValues(vertices[hit.indices[1] * 3], vertices[hit.indices[1] * 3 + 1], vertices[hit.indices[1] * 3 + 2]);
     517                                var v3 = vec3.fromValues(vertices[hit.indices[2] * 3], vertices[hit.indices[2] * 3 + 1], vertices[hit.indices[2] * 3 + 2]);
     518                                vec3.transformMat4(v1, v1, canvas.unitNode.modelMatrix);
     519                                vec3.transformMat4(v2, v2, canvas.unitNode.modelMatrix);
     520                                vec3.transformMat4(v3, v3, canvas.unitNode.modelMatrix);
     521                                var x  = (v1[0] + v2[0] + v3[0]) / 3 + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
     522                                var y  = (v1[1] + v2[1] + v3[1]) / 3 + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
     523                                var z  = (v1[2] + v2[2] + v3[2]) / 3;
     524                                canvas.nodes['clouds' + i].translation = [x, y + canvas.clouds.height, z];
     525                                updateModelMatrix(canvas.nodes['clouds' + i]);
     526                        }
     527                }
     528        }
     529} //}}}
     530function initializeMarker(canvas) { //{{{
     531        //Initialize data marker and tooltip display once per page load
     532        var marker = $('#' + canvas.dataMarkers.marker.id);
     533        var size = canvas.dataMarkers.size;
     534        if (!marker.hasClass('tooltipstered')) {
     535                marker.css({
     536                        'position': 'absolute',
     537                        'left': -size[0] + 'px',
     538                        'top': -size[1] + '0px',
     539                        'width': size[0] + 'px',
     540                        'height': size[1] + 'px',
     541                        'pointer-events': 'all',
     542                        'cursor': 'pointer',
     543                        'display': 'none'
     544                });
     545                marker.tooltipster({
     546                        contentAsHTML: true,
     547                        maxWidth: 320,
     548                        maxHeight: 320,
     549                        zIndex: 1000,
     550                        trigger: 'custom',
     551                        triggerOpen: {
     552                                mouseenter: false,
     553                                originClick: true,
     554                                touchstart: false
     555                        },
     556                        triggerClose: {
     557                                mouseleave: false,
     558                                originClick: true,
     559                                touchleave: false
     560                        },
     561                });
     562                marker.on('click touch', function () {
     563                        marker.fadeOut(175);
     564                        marker.tooltipster('close');
     565                });
     566                canvas.dataMarkers.marker.selector = marker;
     567        }
     568        updateMarker(canvas, true);
     569} //}}}
     570function repositionMarker(canvas) { //{{{
     571        //Mover marker to point to mouse position, offset in y by 1 to enable immediate clicking.
     572        if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit) || !canvas.camera.ready) { return; }
     573        var size = canvas.dataMarkers.size;
     574        var screenPoint = vec3.transformMat4(vec3.create(), canvas.dataMarkers.marker.hit.pos, canvas.camera.vpMatrix);
     575        //console.log(canvas, canvas.selector, $(canvas.id)
     576        var x = (screenPoint[0] + 1.0) * (canvas.width / 2) + canvas.selector.offset().left;
     577        var y = (-screenPoint[1] + 1.0) * (canvas.height / 2) + canvas.selector.offset().top;
     578        canvas.dataMarkers.marker.selector.css({
     579                'left': (Math.round(x) - size[0] / 2) + 'px',
     580                'top': (Math.round(y) - size[1] + 1) + 'px'
     581        });
     582       
     583        if (canvas.dataMarkers.marker.selector.tooltipster('status').state != 'closed') { canvas.dataMarkers.marker.selector.tooltipster('reposition'); }
     584} //}}}
     585function updateMarker(canvas, reset) { //{{{
     586        //Retrieve data value fields and plots them on data marker popup if a hit has been registered.
     587        //TODO: Automatically pick up any field of size md.mesh.numberofelements
     588        //If no marker has been placed, no update is needed. If canvas is resimulating and unitNode has not been set yet, wait and try again.
     589        if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit)) { return; }
     590        if (isEmptyOrUndefined(canvas.unitNode)) { setTimeout( function(){ updateMarker(canvas, reset); }, 750); return; }
     591       
     592        var hit = canvas.dataMarkers.marker.hit;
     593       
    427594        var coords = canvas.unitNode.mesh.vertexBuffers.coords.data;
    428595        var latitude = md.mesh.lat;
     
    431598        var velocity;
    432599        if (md.results[0]) {
    433                 thickness = md.results[canvas.movieFrame].Thickness;
    434                 velocity = md.results[canvas.movieFrame].Vel;
     600                thickness = md.results[canvas.animation.frame].Thickness;
     601                velocity = md.results[canvas.animation.frame].Vel;
    435602        }
    436603        else {
     
    439606        }
    440607       
    441         var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1],coords[hit.indices[1]*2], coords[hit.indices[1]*2+1],coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
     608        //Determine data values at hit position.
     609        var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1], coords[hit.indices[1]*2], coords[hit.indices[1]*2+1], coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
    442610        var hitLatitude = [latitude[hit.indices[0]], latitude[hit.indices[1]], latitude[hit.indices[2]]];
    443611        var hitLongitude = [longitude[hit.indices[0]], longitude[hit.indices[1]], longitude[hit.indices[2]]];
     
    451619        var valueThickness = hitThickness[0] * hit.uvw[0] + hitThickness[1] * hit.uvw[1] + hitThickness[2] * hit.uvw[2];
    452620        var valueVelocity = hitVelocity[0] * hit.uvw[0] + hitVelocity[1] * hit.uvw[1] + hitVelocity[2] * hit.uvw[2];   
    453         if (canvas.dataMarkerOptions) {
    454                 var format = [canvas.dataMarkerOptions.format[0]];     
    455                 for (var i = 1; i < canvas.dataMarkerOptions.format.length; i++) {
    456                         var formatString = canvas.dataMarkerOptions.format[i];
    457                         if (formatString.toLowerCase() == 'x') { format.push(hit.modelPos[0]); }
    458                         else if (formatString.toLowerCase() == 'y') { format.push(hit.modelPos[1]); }
    459                         else if (formatString.toLowerCase() == 'z') { format.push(hit.modelPos[2]); }
    460                         else if (formatString.toLowerCase() == 'lat') { format.push(valueLatitude); }
    461                         else if (formatString.toLowerCase() == 'long') { format.push(valueLongitude); }
    462                         else if (formatString.toLowerCase() == 'thickness') { format.push(valueThickness); }
    463                         else if (formatString.toLowerCase() == 'vel') { format.push(valueVelocity); }
    464                         else if (formatString.toLowerCase() == 'value') { format.push(value); }
    465                         else {format.push(formatString); }
    466                 }
    467                 if (canvas.dataMarkerOptions.animated) {
    468                         var isEmpty = (canvas.dataArray.length == 0);
    469                         var lastUpdatedIndex = (canvas.dataArray.length-1);
    470                         var newMovieFrame = (!isEmpty && canvas.dataArray[lastUpdatedIndex][0] != canvas.movieFrame);
    471                         if (reset) {
    472                                 canvas.dataArray = [];
    473                                 newMovieFrame = true;
    474                                 for (var currentFrame = 0; currentFrame < (canvas.unitNode.movieLength); currentFrame++) {
    475                                         coords = canvas.unitNode.texcoords[currentFrame];
    476                                         var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1],coords[hit.indices[1]*2], coords[hit.indices[1]*2+1],coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
    477                                         var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
    478                                         var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
    479                                         var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
    480                                         canvas.dataArray.push([currentFrame, value]);
    481                                 }
     621
     622        //Construct new argument array of the data display format for sprintf using first first argument as the formatSpecifier string and the rest as the additional arguments.
     623        var format = canvas.dataMarkers.format.slice();
     624        for (var i = 1; i < format.length; i++) {
     625                if (format[i].toLowerCase() == 'x') { format[i] = hit.modelPos[0]; }
     626                else if (format[i].toLowerCase() == 'y') { format[i] = hit.modelPos[1]; }
     627                else if (format[i].toLowerCase() == 'z') { format[i] = hit.modelPos[2]; }
     628                else if (format[i].toLowerCase() == 'lat') { format[i] = valueLatitude; }
     629                else if (format[i].toLowerCase() == 'long') { format[i] = valueLongitude; }
     630                else if (format[i].toLowerCase() == 'thickness') { format[i] = valueThickness; }
     631                else if (format[i].toLowerCase() == 'vel') { format[i] = valueVelocity; }
     632                else if (format[i].toLowerCase() == 'value') { format[i] = value; }
     633        }
     634       
     635        //Apply changes to tooltip
     636        $('#tooltip-content-data-marker-' + canvas.id).html(sprintf.apply(null, format));
     637        $('#tooltip-content-data-marker-' + canvas.id).css({'font': canvas.dataMarkers.font});                         
     638       
     639        //If animated, setup animation loop to update plot as movie plays.
     640        if (canvas.dataMarkers.animated) {
     641                var isEmpty = (canvas.dataMarkers.values.length == 0);
     642                var lastUpdatedIndex = (canvas.dataMarkers.values.length-1);
     643                var newMovieFrame = (!isEmpty && canvas.dataMarkers.values[lastUpdatedIndex][0] != canvas.animation.frame);
     644                //If new data marker has been placed, reinitialize plot. If not, push new value into plot value array.
     645                if (reset) {
     646                        canvas.dataMarkers.values = [];
     647                        newMovieFrame = true;
     648                        for (var currentFrame = 0; currentFrame < (canvas.unitNode.movieLength); currentFrame++) {
     649                                coords = canvas.unitNode.texcoords[currentFrame];
     650                                var hitCoords = [coords[hit.indices[0]*2], coords[hit.indices[0]*2+1], coords[hit.indices[1]*2], coords[hit.indices[1]*2+1], coords[hit.indices[2]*2], coords[hit.indices[2]*2+1]];
     651                                var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
     652                                var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
     653                                var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
     654                                canvas.dataMarkers.values.push([currentFrame, value]);
    482655                        }
    483                         else {
    484                                 if (isEmpty || newMovieFrame) {
    485                                         canvas.dataArray.push([canvas.movieFrame,value]);
    486                                 }
     656                }
     657                else {
     658                        if (isEmpty || newMovieFrame) {
     659                                canvas.dataMarkers.values.push([canvas.animation.frame, value]);
    487660                        }
    488                         if (isEmpty || newMovieFrame) {
    489                                 $('#tooltip-content-data-marker-' + canvas.id).html(sprintf.apply(null, format));
    490                                 $('#tooltip-content-data-marker-' + canvas.id).css({
    491                                         'font': canvas.dataMarkerOptions.font
    492                                 });                             
    493                                 var dataLabels = {'latitude':valueLatitude,'longitude':valueLongitude,'thickness':valueThickness,'velocity':valueVelocity,'value':value};
    494                                 var dataDisplay = canvas.dataArray.slice(0,canvas.movieFrame+1);                                       
    495                                 plot('id','#sim-plot','type','bar','width',400,'height',300,'nticks',25,'xlabel','Time','ylabel',
    496                                         'Value','title','Changes Over Time','datalabels',canvas.dataMarkerOptions.labels,'labelvalues',dataLabels,'data',dataDisplay);
    497                         }
    498                 }
    499         }
    500 }
    501 
    502 function repositionMarker() {
    503         if (!canvas.unitNode) { return; }
    504         if (!canvas.dataMarkerDisplay) { return; }
    505         var dataMarkerSize = canvas.dataMarkerSize;
    506         var screenPoint = vec3.transformMat4(vec3.create(), canvas.marker.hit.pos, canvas.cameraMatrix);
    507         var x = screenPoint[0] * (canvas.width / 2) + canvas.width / 2;
    508         var y = -screenPoint[1] * (canvas.height / 2) + canvas.height / 2;
    509         canvas.marker.css({
    510                 'left': (Math.round(x) - dataMarkerSize[0] / 2) + 'px',
    511                 'top': (Math.round(y) - dataMarkerSize[1]) + 'px'
    512         });
    513         if (canvas.dataMarkerDisplay.tooltipster('status').state != 'closed') { canvas.dataMarkerDisplay.tooltipster('reposition'); }
    514 }
    515 
    516 function onPan(ev,canvas,displaylog) { //{{{
    517         ev.preventDefault();
    518         repositionMarker();
    519         brushModify(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
    520         if (ev.type == 'panstart') {
    521                 canvas.lastDeltaX = 0;
    522                 canvas.lastDeltaY = 0;
    523         }
    524         if (ev.srcEvent.shiftKey || ev.pointers.length == 2) {
    525                 if (!canvas.viewPanning) return;
    526                 var deltaX = (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth / canvas.zoom * 2 * canvas.controlSensitivity * 6.371e6;
    527                 var deltaY = (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight / canvas.zoom * 2 * canvas.controlSensitivity * 6.371e6;
    528                
    529                 if (canvas.twod) {
    530                         canvas.translation[0] += Math.cos(DEG2RAD * canvas.rotation[0]) * deltaX - Math.sin(DEG2RAD * 0) * deltaY;
    531                         canvas.translation[2] += Math.sin(DEG2RAD * canvas.rotation[0]) * deltaX + Math.cos(DEG2RAD * 0) * deltaY;
    532                 }
    533                 else {
    534                         canvas.translation[0] += Math.cos(DEG2RAD * canvas.rotation[0]) * deltaX - Math.sin(DEG2RAD * canvas.rotation[0]) * deltaY;
    535                         canvas.translation[2] += Math.sin(DEG2RAD * canvas.rotation[0]) * deltaX + Math.cos(DEG2RAD * canvas.rotation[0]) * deltaY;
    536                 }
    537         }
    538         else {
    539                 canvas.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * -2 * canvas.controlSensitivity * RAD2DEG;
    540                 canvas.rotation[1] += (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight * -2 * canvas.controlSensitivity * RAD2DEG;
    541                
    542                 if (canvas.rotation[0] > 360) { canvas.rotation[0] -= 360; };
    543                 if (canvas.rotation[0] < -360) { canvas.rotation[0] += 360; };
    544                 if (canvas.rotation[1] > 180) { canvas.rotation[1] -= 360; };
    545                 if (canvas.rotation[1] < -180) { canvas.rotation[1] += 360; };
    546                
    547                 canvas.rotation[0] = clamp(canvas.rotation[0], canvas.rotationAzimuthBounds[0], canvas.rotationAzimuthBounds[1]);
    548                 canvas.rotation[1] = clamp(canvas.rotation[1], canvas.rotationElevationBounds[0], canvas.rotationElevationBounds[1])
    549         }
    550         canvas.lastDeltaX = ev.deltaX;
    551         canvas.lastDeltaY = ev.deltaY;
    552 
    553         if (displaylog) { console.log(canvas.rotation); }
    554 } //}}}
    555 function onPinch(ev,canvas,displaylog) { //{{{
    556         ev.preventDefault();
    557         repositionMarker();
    558         if (ev.type == 'pinchstart') { canvas.zoomLast = canvas.zoom; }
    559         else { modifyZoom(ev.scale * canvas.zoomLast, canvas, displaylog); }
    560 } //}}}
    561 function onZoom(ev,canvas,displaylog) { //{{{
    562         ev.preventDefault();
    563         repositionMarker();
    564         var delta = clamp(ev.scale || ev.wheelDelta || -ev.detail, -1, 1) * canvas.controlSensitivity * canvas.zoom / 20;
    565         modifyZoom(canvas.zoom + delta, canvas, displaylog);
    566 } //}}}
    567 function modifyZoom(value,canvas,displaylog) { //{{{
    568         canvas.zoom = clamp(value, canvas.zoomBounds[0], canvas.zoomBounds[1]);
    569         if (displaylog) { console.log(canvas.zoom); }
    570 } //}}}
    571 function modifyDataMarkersEnabled(value,canvas) { //{{{
    572         canvas.dataMarkersEnabled = value;
    573         if (!canvas.dataMarkersEnabled && canvas.marker) {
    574                 canvas.marker.fadeOut(175);
    575                 canvas.dataMarkerDisplay.tooltipster('close');
    576         }
    577         else if (canvas.dataMarkersEnabled && canvas.marker) {
    578                 canvas.marker.fadeIn(175);
    579                 canvas.dataMarkerDisplay.tooltipster('open');
    580         }
    581 } //}}}
    582 function toggleMoviePlay(canvas) { //{{{
    583         canvas.moviePlay = !canvas.moviePlay;
    584         if (canvas.moviePlay){
    585                 canvas.playButton.find("span").removeClass("fa-play");
    586                 canvas.playButton.find("span").addClass("fa-pause");
    587         }
    588         else{
    589                 canvas.playButton.find("span").removeClass("fa-pause");
    590                 canvas.playButton.find("span").addClass("fa-play");
     661                }
     662               
     663                //Replot data marker popup using update data value array.
     664                if (isEmpty || newMovieFrame) {
     665                        var dataLabels = {'latitude': valueLatitude, 'longitude': valueLongitude, 'thickness': valueThickness, 'velocity': valueVelocity, 'value': value};
     666                        var dataDisplay = canvas.dataMarkers.values.slice(0, canvas.animation.frame+1);                                 
     667                        plot(
     668                                'id', '#sim-plot',
     669                                'type', 'bar',
     670                                'width', 400,
     671                                'height', 300,
     672                                'nticks', 25,
     673                                'xlabel', 'Time',
     674                                'ylabel', 'Value',
     675                                'title', 'Changes Over Time',
     676                                'datalabels', canvas.dataMarkers.labels,
     677                                'labelvalues', dataLabels,
     678                                'data', dataDisplay
     679                        );
     680                }
     681        }
     682        repositionMarker(canvas);
     683        if (reset) {
     684                canvas.dataMarkers.marker.selector.fadeIn(175);
     685                canvas.dataMarkers.marker.selector.tooltipster('open');
    591686        }
    592687} //}}}
     
    594689//{{{ Drawing Functions
    595690function updateCameraMatrix(canvas) { //{{{
    596     //Update view matrix and multiply with projection matrix to get the view-projection (camera) matrix.
     691    //Update view matrix and multiply with projection matrix to get the view-projection matrix.
    597692        var vMatrix = mat4.create();
    598693        var pMatrix = mat4.create();
     
    604699        var cameraPosition = vec3.create();
    605700
    606         if (canvas.twod) { mat4.ortho(pMatrix, -aspectRatio*6.371e6/canvas.zoom, aspectRatio*6.371e6/canvas.zoom, -6.371e6/canvas.zoom, 6.371e6/canvas.zoom, -1.0, 1e10); }
    607         else { mat4.perspective(pMatrix, 45 * DEG2RAD, aspectRatio, 1e3, 1e10); }
     701        if (canvas.view.twod) { mat4.ortho(pMatrix, -aspectRatio*6.371e6/canvas.view.zoom, aspectRatio*6.371e6/canvas.view.zoom, -6.371e6/canvas.view.zoom, 6.371e6/canvas.view.zoom, canvas.camera.near, canvas.camera.far); }
     702        else { mat4.perspective(pMatrix, canvas.camera.fov * DEG2RAD, aspectRatio, canvas.camera.near, canvas.camera.far); }
    608703       
    609704        //Apply worldspace translation
    610         mat4.translate(translateMatrix, translateMatrix, [-canvas.translation[0],-canvas.translation[1],-canvas.translation[2]]);
    611         mat4.multiply(vMatrix, translateMatrix, vMatrix);
     705        mat4.translate(vMatrix, translateMatrix, vec3.negate(vec3.create(), canvas.view.position));
    612706       
    613707        //Calculate rotation around camera focal point about worldspace origin
    614         if (canvas.twod) {
     708        if (canvas.view.twod) {
    615709                mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, DEG2RAD * 0, [0, 1, 0]);
    616710                mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * 90, [1, 0, 0]);
     
    618712        }
    619713        else {
    620                 mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, DEG2RAD * canvas.rotation[0], [0, 1, 0]);
    621                 mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * canvas.rotation[1], [1, 0, 0]);
     714                mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, DEG2RAD * canvas.view.rotation[0], [0, 1, 0]);
     715                mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * canvas.view.rotation[1], [1, 0, 0]);
    622716                mat4.multiply(rotationMatrix, elevationRotationMatrix, azimuthRotationMatrix);
    623717        }
    624        
     718
    625719        //Apply rotation transform
    626720        mat4.multiply(vMatrix, rotationMatrix, vMatrix);
    627 
    628         //Apply screenspace translation
     721       
     722        //Apply screenspace translation to emulate rotation around point
    629723        mat4.identity(translateMatrix);
    630         mat4.translate(translateMatrix, translateMatrix, [0.0, 0.0, -6.371e6/canvas.zoom]);
     724        mat4.translate(translateMatrix, translateMatrix, [0.0, 0.0, -6.371e6/canvas.view.zoom]);
    631725        mat4.multiply(vMatrix, translateMatrix, vMatrix);
    632726       
    633         //Calculate fields for lighting and raycasts
    634         mat4.invert(canvas.vInverseMatrix, vMatrix);
    635        
    636727        //Apply projection matrix to get camera matrix
    637         mat4.multiply(canvas.cameraMatrix, pMatrix, vMatrix);
    638         mat4.invert(canvas.inverseCameraMatrix, canvas.cameraMatrix);
    639         vec3.transformMat4(canvas.cameraPosition, cameraPosition, canvas.inverseCameraMatrix);
     728        mat4.copy(canvas.camera.vMatrix, vMatrix);
     729        mat4.multiply(canvas.camera.vpMatrix, pMatrix, vMatrix);
     730       
     731        //Calculate inverse view matrix fields for lighting and raycasts
     732        mat4.invert(canvas.camera.vInverseMatrix, canvas.camera.vMatrix);
     733        mat4.invert(canvas.camera.vpInverseMatrix, canvas.camera.vpMatrix);
     734       
     735        vec3.transformMat4(canvas.camera.position, cameraPosition, canvas.camera.vpInverseMatrix);
     736        canvas.camera.ready = true;
    640737}//}}}
    641 function drawSceneGraphNode(canvas,node) { //{{{
     738function drawSceneGraphNode(canvas, node) { //{{{
    642739        if (!node.enabled) { return; }
    643740
     
    646743       
    647744        var mvpMatrix = mat4.create();
    648         mat4.multiply(mvpMatrix, canvas.cameraMatrix, node.modelMatrix);
     745        mat4.multiply(mvpMatrix, canvas.camera.vpMatrix, node.modelMatrix);
     746       
     747        var mvMatrix = mat4.create();
     748        mat4.multiply(mvMatrix, canvas.camera.vMatrix, node.modelMatrix);
     749       
     750        var normalMatrix = mat4.create();
     751        mat4.invert(normalMatrix, mvMatrix);
     752        mat4.transpose(normalMatrix, normalMatrix);
    649753       
    650754        if (node.texture) { node.texture.bind(0); }
     
    660764        var lightOrigin = vec3.fromValues(0, 0, 0);
    661765        var cameraPositionRelative = vec3.create();
    662         vec3.transformMat4(origin, origin, canvas.vInverseMatrix);
     766        vec3.transformMat4(origin, origin, canvas.camera.vInverseMatrix);
    663767        vec3.normalize(lightOrigin, lightOrigin);
    664768        vec3.sub(cameraPositionRelative, origin, node.translation);
     
    692796        node.shader.uniforms({
    693797                m4MVP: mvpMatrix,
     798                m4Normal: normalMatrix,
    694799                m4Model: node.modelMatrix,
     800                //u_lightPosition: [-lightOrigin[0], -lightOrigin[1], -lightOrigin[2]],
     801                u_lightPosition: [1.0, 1.0, 1.0],
     802                u_diffuseColor: [1.0, 0.9, 0.9],
    695803                u_texture: 0,
    696804                u_alpha: node.alpha,
     
    728836        gl.disable(gl.CULL_FACE);
    729837} //}}}
    730 function draw(canvas,options) { //{{{
    731         if (canvas.textcanvas) { canvas.textcanvas.draw(canvas); }
    732 
     838function draw(canvas) { //{{{
     839        //Ensure all nodes are ready to render
     840        //TODO: Come up with better way to check if shaders are ready, or move outside of main draw function
    733841        var nodes = canvas.nodes;
    734         if (nodes.length < 1) {
    735                 canvas.drawHandler = window.requestAnimationFrame(function(time) { draw(canvas,options); });
    736                 return;
    737         }
    738         for (var node in nodes) {       
    739                 if ((nodes[node].texture && nodes[node].texture.ready == false) || nodes[node].shader.ready == false || nodes[node].mesh.ready == false) {
    740                         canvas.drawHandler = window.requestAnimationFrame(function(time) { draw(canvas,options); });
    741                         return;
    742                 }
    743         }
    744        
    745         var rect = canvas.getBoundingClientRect();
    746         canvas.width  = rect.width;
    747         canvas.height = rect.height;
    748        
    749         var gl = canvas.gl;
    750         gl.makeCurrent(); //litegl function to handle switching between multiple canvases
    751         gl.viewport(0, 0, rect.width, rect.height);
    752         gl.clearColor(canvas.backgroundcolor[0], canvas.backgroundcolor[1], canvas.backgroundcolor[2], canvas.backgroundcolor[3]);
    753         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    754        
    755         updateCameraMatrix(canvas);
    756        
    757         //if (canvas.marker) { canvas.marker.update(); }
    758        
    759         var drawPassNumber = 3;
    760         for (var i = drawPassNumber - 1; i >= 0; i--) {
    761                 for (var node in nodes) {
    762                         if (nodes[node].drawOrder == i) { drawSceneGraphNode(canvas,nodes[node]); }
    763                 }
    764         }
    765 
    766         canvas.drawHandler = window.requestAnimationFrame(function(time) { draw(canvas,options); });
     842        if (!canvas.draw.ready) {
     843                if (nodes.length !== 0) {
     844                        canvas.draw.ready = true;
     845                        for (var node in nodes) {
     846                                if (nodes[node].shader.ready == false) {
     847                                        canvas.draw.ready = false;
     848                                        break;
     849                                }
     850                        }
     851                       
     852                }
     853        }
     854       
     855        //Begin rendering nodes
     856        if (canvas.draw.ready) {
     857                if (canvas.textcanvas) { canvas.textcanvas.draw(canvas); }
     858                if (canvas.overlaycanvas) { canvas.overlaycanvas.draw(canvas); }
     859       
     860                var rect = canvas.getBoundingClientRect();
     861                canvas.width  = rect.width;
     862                canvas.height = rect.height;
     863               
     864                var gl = canvas.gl;
     865                gl.makeCurrent(); //litegl function to handle switching between multiple canvases
     866                gl.viewport(0, 0, canvas.width, canvas.height);
     867                gl.clearColor(canvas.backgroundcolor[0], canvas.backgroundcolor[1], canvas.backgroundcolor[2], canvas.backgroundcolor[3]);
     868                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
     869               
     870                updateCameraMatrix(canvas);
     871               
     872                var drawPassNumber = 3;
     873                for (var i = drawPassNumber - 1; i >= 0; i--) {
     874                        for (var node in nodes) {
     875                                if (nodes[node].drawOrder == i) { drawSceneGraphNode(canvas, nodes[node]); }
     876                        }
     877                }
     878        }
     879       
     880        //Regardless of ready state, schedule next frame to check for ready state and render
     881        canvas.draw.handler = window.requestAnimationFrame(function(time) { draw(canvas); });
    767882} //}}}
    768883//}}}
Note: See TracChangeset for help on using the changeset viewer.