source: issm/oecreview/Archive/21724-22754/ISSM-21767-21768.diff@ 22755

Last change on this file since 22755 was 22755, checked in by Mathieu Morlighem, 7 years ago

CHG: added 21724-22754

File size: 131.7 KB
RevLine 
[22755]1Index: ../trunk-jpl/src/m/plot/webgl.js
2===================================================================
3--- ../trunk-jpl/src/m/plot/webgl.js (revision 21767)
4+++ ../trunk-jpl/src/m/plot/webgl.js (revision 21768)
5@@ -5,12 +5,10 @@
6 canvas = document.getElementById(options.getfieldvalue('canvasid'));
7 //var canvas = document.getElementById(options.getfieldvalue('canvasid'));
8 if (!canvas.initialized) {
9- typedArraySliceSupport();
10 if (!isEmptyOrUndefined(canvas.draw) && canvas.draw.handler !== 0) { window.cancelAnimationFrame(canvas.draw.handler); }
11 if (!isEmptyOrUndefined(canvas.animation) && canvas.animation.handler !== 0) { clearInterval(canvas.animation.handler); }
12 initWebGL(canvas, options);
13 initializeMarker(canvas);
14- canvas.nodes = [];
15 draw(canvas);
16 canvas.initialized = true;
17 }
18@@ -18,8 +16,8 @@
19 }
20 function initWebGL(canvas, options) { //{{{
21 //Initialize canvas.gl on page load, reusing gl context on additional runs
22- var gl;
23- if (!canvas.gl) {
24+ var gl = canvas.gl;
25+ if (isEmptyOrUndefined(gl)) {
26 gl = GL.create({canvas: canvas});
27 gl.enable(gl.DEPTH_TEST); // Enable depth testing
28 gl.depthFunc(gl.LEQUAL); // Near things obscure far things
29@@ -30,34 +28,35 @@
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+ var displayview = options.getfieldvalue('displayview', 'off') === 'on';
39+ var displayzoom = options.getfieldvalue('displayzoom', 'off') === 'on';
40+ var mc = new Hammer(canvas);
41 mc.add(new Hammer.Pan({threshold: 0, pointers: 0}));
42 mc.add(new Hammer.Pinch({threshold: 0})).recognizeWith(mc.get('pan'));
43- mc.on('singletap', function (ev) {onTap(ev, canvas);});
44+ mc.on('tap press', function (ev) {onTap(ev, canvas);});
45 mc.on('panstart panmove', function (ev) {onPan(ev, canvas, displayview);});
46- mc.on('pinchstart pinchmove', function (ev) {onPinch(ev, canvas, displayview);});
47-
48+ mc.on('pinchstart pinchmove', function (ev) {onPinch(ev, canvas, displayview);});
49 canvas.addEventListener('mousewheel', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
50 canvas.addEventListener('DOMMouseScroll', function (ev) {onZoom(ev, canvas, displayzoom)}, false);
51+
52+ //Add persistent state variables
53+ canvas.nodes = {};
54+ canvas.octrees = {};
55+ canvas.unitNode = {};
56+ canvas.unitData = {};
57+ canvas.unitMovieData = {};
58+ canvas.gl = gl;
59+ canvas.rootPath = options.getfieldvalue('rootpath', '../../../js/');
60+ canvas.id = options.getfieldvalue('canvasid', '.sim-canvas');
61+ canvas.selector = $('#' + canvas.id);
62+ canvas.textcanvas = null;
63+ canvas.overlaycanvas = null;
64+
65+ typedArraySliceSupport();
66 }
67- else {
68- gl = canvas.gl;
69- }
70
71- // Add context state variables
72- canvas.gl = gl;
73- canvas.textcanvas = null;
74- canvas.overlaycanvas = null;
75- canvas.unitNode = {};
76- canvas.unitData = {};
77+ //Add context state variables
78 canvas.controlSensitivity = options.getfieldvalue('controlsensitivity', 1);
79- canvas.id = options.getfieldvalue('canvasid', '.sim-canvas');
80- canvas.rootPath = options.getfieldvalue('rootpath', '../../../js/');
81- canvas.selector = $('#' + canvas.id);
82 var backgroundcolor = new RGBColor(options.getfieldvalue('backgroundcolor', 'lightcyan'));
83 if (backgroundcolor.ok) { canvas.backgroundcolor = [backgroundcolor.r/255.0, backgroundcolor.g/255.0, backgroundcolor.b/255.0, 1.0]; }
84 else { throw Error(sprintf('s%s%s\n','initWebGL error message: cound not find out background color for curent canvas ', canvas)); }
85@@ -69,6 +68,7 @@
86 play: defaultFor(animation.play, true),
87 increment: defaultFor(animation.increment, true),
88 fps: defaultFor(animation.fps, 4),
89+ interval: defaultFor(animation.interval, 1000 / animation.fps),
90 loop: defaultFor(animation.loop, true),
91 handler: defaultFor(animation.handler, 0)
92 }
93@@ -98,7 +98,8 @@
94 canvas.clouds = {
95 enabled: defaultFor(clouds.enabled, false),
96 height: defaultFor(clouds.height, 7500),
97- quantity: defaultFor(clouds.quantity, 10)
98+ quantity: defaultFor(clouds.quantity, 10),
99+ hit: defaultFor(clouds.hit, {})
100 };
101 var dataMarkers = options.getfieldvalue('datamarkers', {});
102 canvas.dataMarkers = {
103@@ -110,7 +111,8 @@
104 animated: defaultFor(dataMarkers.animated, false),
105 labels: defaultFor(dataMarkers.labels, []),
106 font: defaultFor(dataMarkers.font, ''),
107- marker: defaultFor(dataMarkers.marker, document.getElementById('sim-data-marker-' + canvas.id))
108+ marker: defaultFor(dataMarkers.marker, document.getElementById('sim-data-marker-' + canvas.id)),
109+ reposition: defaultFor(dataMarkers.reposition, true)
110 };
111 var draw = options.getfieldvalue('draw', {});
112 canvas.draw = {
113@@ -133,27 +135,23 @@
114 //Override with parameters from URL, if any
115 //TODO: Make permalinks more robust and less interdependent on UI
116 if (!canvas.usedparemters) {
117- function getJsonFromUrl() {
118- var query = location.search.substr(1);
119- var result = {};
120- query.split('&').forEach(function(part) {
121- var item = part.split('=');
122- result[item[0]] = decodeURIComponent(item[1]);
123- });
124- return result;
125- }
126- parameters = getJsonFromUrl();
127-
128- if (parameters['view']) {
129- canvas.view = JSON.parse(parameters['view']);
130- }
131+ var parameters = {};
132+ location.search.substr(1).split('&').forEach(function(part) {
133+ var item = part.split('=');
134+ parameters[item[0]] = decodeURIComponent(item[1]);
135+ });
136+
137+ if (parameters['pos']) { canvas.view.position = JSON.parse(parameters['pos']); }
138+ if (parameters['rot']) { canvas.view.rotation = JSON.parse(parameters['rot']); }
139+ if (parameters['zoom']) { canvas.view.zoom = JSON.parse(parameters['zoom']); }
140+ if (parameters['twod']) { canvas.view.twod = JSON.parse(parameters['twod']); }
141 if (parameters['initial']) {
142 initial = JSON.parse(parameters['initial']);
143 if (!initial) {
144- if (typeof SolveGlacier == 'function') {
145+ if (typeof SolveGlacier === 'function') {
146 SolveGlacier();
147 }
148- if (typeof SolveSlr == 'function') {
149+ if (typeof SolveSlr === 'function') {
150 SolveSlr();
151 }
152 }
153@@ -162,7 +160,11 @@
154 }
155 } //}}}
156 function generatePermalink() { //{{{
157- var permalink = window.location.origin + window.location.pathname + '&view=' + JSON.stringify(canvas.view) + '&initial=' + JSON.stringify(initial);
158+ var permalink = window.location.origin + window.location.pathname + '?'
159+ + '&pos=' + encodeURIComponent(JSON.stringify(canvas.view.position))
160+ + '&rot=' + encodeURIComponent(JSON.stringify(canvas.view.rotation))
161+ + '&zoom=' + encodeURIComponent(JSON.stringify(canvas.view.zoom))
162+ + '&twod=' + encodeURIComponent(JSON.stringify(canvas.view.twod));
163 window.prompt('Share this simulation: ', permalink);
164 } //}}}
165 function loadShaders(gl, rootPath) { //{{{
166@@ -181,83 +183,6 @@
167 }
168 return gl.textures[imageSource];
169 } //}}}
170-function Node(gl) { //{{{
171- //Returns a Node object that contains default display states for webgl object. center represents pivot point of rotation.
172- return {
173- alpha: 1.0,
174- buffers: [],
175- cullFace: gl.BACK,
176- disableDepthTest: false,
177- drawMode: gl.TRIANGLES,
178- drawOrder: 0,
179- enabled: true,
180- enableCullFace: true,
181- hideOcean: false,
182- lineWidth: 1.0,
183- maskEnabled: false,
184- maskHeight: 150.0,
185- maskColor: vec4.fromValues(0.0, 0.0, 1.0, 1.0),
186- mesh: null,
187- name: 'node',
188- shaderName: 'Colored',
189- shader: gl.shaders.Colored,
190- texture: null,
191- useIndexBuffer: true,
192- center: vec3.create(),
193- scale: vec3.fromValues(1, 1, 1),
194- rotation: vec3.create(),
195- translation: vec3.create(),
196- modelMatrix: mat4.create(),
197- rotationMatrix: mat4.create(),
198- inverseModelMatrix: mat4.create(),
199- inverseRotationMatrix: mat4.create()
200- };
201-} //}}}
202-function debugNodes(canvasid) { //{{{
203- var canvasid = canvasid || '.sim-canvas';
204- var nodes = document.getElementById(canvasid).nodes;
205- console.log(canvasid, 'Nodes:');
206- for (var node in nodes) {
207- console.log('name: ', nodes[node].name, ' node: ', nodes[node], ' mesh: ', nodes[node].mesh, ' translation: ', nodes[node].translation, ' center:', nodes[node].center, ' rotation:', nodes[node].rotation);
208- }
209- return nodes;
210-} //}}}
211-function updateModelMatrix(node) { //{{{
212- var modelMatrix = mat4.create();
213-
214- var translationMatrix = mat4.create();
215- mat4.translate(translationMatrix, translationMatrix, vec3.negate(vec3.create(), node.center)); //scale/rotation centering
216- mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
217-
218- var scaleMatrix = mat4.create();
219- mat4.scale(scaleMatrix, scaleMatrix, node.scale);
220- mat4.multiply(modelMatrix, scaleMatrix, modelMatrix);
221-
222- var rotationMatrix = mat4.create();
223- var zRotationMatrix = mat4.create();
224- mat4.rotate(zRotationMatrix, zRotationMatrix, DEG2RAD * node.rotation[2], [0.0, 0.0, 1.0]);
225- mat4.multiply(rotationMatrix, zRotationMatrix, rotationMatrix);
226- var yRotationMatrix = mat4.create();
227- mat4.rotate(yRotationMatrix, yRotationMatrix, DEG2RAD * node.rotation[1], [0.0, 1.0, 0.0]);
228- mat4.multiply(rotationMatrix, yRotationMatrix, rotationMatrix);
229- var xRotationMatrix = mat4.create();
230- mat4.rotate(xRotationMatrix, xRotationMatrix, DEG2RAD * node.rotation[0], [1.0, 0.0, 0.0]);
231- mat4.multiply(rotationMatrix, xRotationMatrix, rotationMatrix);
232- mat4.multiply(modelMatrix, rotationMatrix, modelMatrix);
233-
234- mat4.identity(translationMatrix);
235- mat4.translate(translationMatrix, translationMatrix, node.center); //relative translation
236- mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
237-
238- mat4.identity(translationMatrix);
239- mat4.translate(translationMatrix, translationMatrix, node.translation); //absolute translation
240- mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
241-
242- node.modelMatrix = modelMatrix;
243- node.inverseModelMatrix = mat4.invert(mat4.create(), modelMatrix);
244- node.rotationMatrix = rotationMatrix;
245- node.inverseRotationMatrix = mat4.invert(mat4.create(), rotationMatrix);
246-} //}}}
247 function clamp(value, min, max) { //{{{
248 return Math.max(min, Math.min(value, max));
249 } //}}}
250@@ -265,15 +190,21 @@
251 return typeof name !== 'undefined' ? name : value;
252 } //}}}
253 function isEmptyOrUndefined(object) { //{{{
254- return object === undefined || Object.getOwnPropertyNames(object).length === 0;
255+ return object === undefined || isEmpty(object);
256 } //}}}
257+function isEmpty(object) { //{{{
258+ for (var key in object) {
259+ return false;
260+ }
261+ return true;
262+} //}}}
263 function recover(canvasid, name, value) { //{{{
264 //Traverse canvas object tree for property defined by dot delimited string, returning it, or a default value if it is not found.
265 var object = document.getElementById(canvasid);
266 var properties = name.split('.');
267- for (var i = 0; i < properties.length; ++i) {
268+ for (var i = 0; i < properties.length; i++) {
269 object = object[properties[i]];
270- if (typeof object === 'undefined') { break; }
271+ if (isEmptyOrUndefined(object)) { break; }
272 }
273 return defaultFor(object, value);
274 } //}}}
275@@ -325,26 +256,51 @@
276 function onTap(ev, canvas) { //{{{
277 //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.
278 ev.preventDefault();
279- if (!canvas.dataMarkers.enabled) { return; }
280- var hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
281- canvas.dataMarkers.marker.hit = hit;
282+
283+ if (canvas.clouds.enabled) {
284+ for (var i = 0; i < canvas.clouds.quantity; i++) {
285+ raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes["clouds" + i]);
286+ }
287+ }
288+ var hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.unitNode);
289+ if ('cities' in canvas.nodes) {
290+ var hitCities = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes.cities);
291+ canvas.nodes.cities.hit = hitCities;
292+ updateCities(canvas);
293+ }
294+
295 canvas.brush.hit = hit;
296- updateMarker(canvas, true);
297+
298+ if (canvas.dataMarkers.enabled) {
299+ canvas.dataMarkers.marker.selector.closed = false;
300+ canvas.dataMarkers.marker.hit = hit;
301+ updateMarker(canvas, true);
302+ }
303+
304 brushModify(canvas);
305 } //}}}
306 function onPan(ev, canvas, displaylog) { //{{{
307 ev.preventDefault();
308
309- if (canvas.dataMarkers.enabled == 'on') {
310- canvas.brush.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY);
311- brushModify(canvas);
312+ if (canvas.dataMarkers.enabled) {
313+ if (!isEmptyOrUndefined(canvas.unitNode)) {
314+ canvas.brush.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.unitNode);
315+ brushModify(canvas);
316+ }
317 }
318
319- if (ev.type == 'panstart') {
320+ if (canvas.clouds.enabled) {
321+ if (!isEmptyOrUndefined(canvas.nodes['overlay'])) {
322+ canvas.clouds.hit = raycast(canvas, ev.srcEvent.layerX, ev.srcEvent.layerY, canvas.nodes['overlay']);
323+ updateClouds(canvas);
324+ }
325+ }
326+
327+ if (ev.type === 'panstart') {
328 canvas.lastDeltaX = 0;
329 canvas.lastDeltaY = 0;
330 }
331- if (ev.srcEvent.shiftKey || ev.pointers.length == 2) {
332+ if (ev.srcEvent.shiftKey || ev.pointers.length === 2) {
333 if (!canvas.view.panningEnabled) return;
334 var deltaX = (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
335 var deltaY = (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight / canvas.view.zoom * 2 * canvas.controlSensitivity * 6.371e6;
336@@ -360,7 +316,7 @@
337 }
338
339 else {
340- canvas.view.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * -2 * canvas.controlSensitivity * RAD2DEG;
341+ canvas.view.rotation[0] += (canvas.lastDeltaX - ev.deltaX) / canvas.clientWidth * 2 * canvas.controlSensitivity * RAD2DEG;
342 canvas.view.rotation[1] += (canvas.lastDeltaY - ev.deltaY) / canvas.clientHeight * -2 * canvas.controlSensitivity * RAD2DEG;
343
344 if (canvas.view.rotation[0] > 360) { canvas.view.rotation[0] -= 360; };
345@@ -374,13 +330,13 @@
346 canvas.lastDeltaX = ev.deltaX;
347 canvas.lastDeltaY = ev.deltaY;
348
349- repositionMarker(canvas);
350+ canvas.dataMarkers.reposition = true;
351
352 if (displaylog) { console.log(canvas.view.rotation); }
353 } //}}}
354 function onPinch(ev, canvas, displaylog) { //{{{
355 ev.preventDefault();
356- if (ev.type == 'pinchstart') { canvas.view.lastZoom = canvas.view.zoom; }
357+ if (ev.type === 'pinchstart') { canvas.view.lastZoom = canvas.view.zoom; }
358 else { modifyZoom(ev.scale * canvas.view.lastZoom, canvas, displaylog); }
359 } //}}}
360 function onZoom(ev, canvas, displaylog) { //{{{
361@@ -390,7 +346,7 @@
362 } //}}}
363 function modifyZoom(value, canvas, displaylog) { //{{{
364 canvas.view.zoom = clamp(value, canvas.view.zoomLimits[0], canvas.view.zoomLimits[1]);
365- repositionMarker(canvas);
366+ canvas.dataMarkers.reposition = true;
367 if (displaylog) { console.log(canvas.view.zoom); }
368 } //}}}
369 function modifyDataMarkersEnabled(value, canvas) { //{{{
370@@ -407,53 +363,45 @@
371 canvas.playButton.find('span').addClass('fa-play');
372 }
373 } //}}}
374-function onSlideStart(canvas, progressBar) { //{{{
375- if (!isEmptyOrUndefined(canvas.animation)) {
376- canvas.animation.increment = false;
377- canvas.animation.frame = parseInt($(progressBar).val());
378- //console.log(canvas.animation.frame);
379- //updateMarker(canvas, false);
380- }
381-} //}}}
382-function onSlideChange(canvas, progressBar) { //{{{
383- if (!isEmptyOrUndefined(canvas.animation)) {
384- canvas.animation.frame = parseInt($(progressBar).val());
385- //console.log("change");
386- updateMarker(canvas, false);
387- }
388-} //}}}
389-function onSlideStop(canvas, progressBar) { //{{{
390- if (!isEmptyOrUndefined(canvas.animation)) {
391- canvas.animation.increment = true;
392- canvas.animation.frame = parseInt($(progressBar).val());
393- //console.log(canvas.animation.frame);
394- //updateMarker(canvas, false);
395- }
396-} //}}}
397 //}}}
398 //{{{ Interaction Functions
399-function raycast(canvas, x, y) { //{{{
400+function raycast(canvas, x, y, node) { //{{{
401 //Performs raycast on canvas.unitNode.mesh using x/y screen coordinates. Returns hit objects with hit position, coords, and indicies of ray-triangle intersection.
402 //TODO: Diagnose marker issues with orthographic views and slr-eustatic updates when switching between basins.
403- var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.camera.vpMatrix, canvas.unitNode.modelMatrix));
404+ var inverseMVPMatrix = mat4.invert(mat4.create(), mat4.multiply(mat4.create(), canvas.camera.vpMatrix, node.modelMatrix));
405 var origin = vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 0], inverseMVPMatrix);
406 var far = far || vec3.transformMat4(vec3.create(), [(x - canvas.width / 2) / (canvas.width / 2), (canvas.height / 2 - y) / (canvas.height / 2), 1.0], inverseMVPMatrix);
407 var ray = vec3.subtract(vec3.create(), far, origin);
408
409- var mesh = canvas.unitNode.mesh;
410+ var mesh = node.mesh;
411+
412 if (!mesh) { return; }
413- if (!mesh.octree) { mesh.octree = new GL.Octree(mesh); }
414+ if (!node.octree) { node.octree = new GL.Octree(mesh); }
415
416- var hit = mesh.octree.testRay(origin, ray, 1e3, 1e10);
417+ var hit = node.octree.testRay(origin, ray, 1e3, 1e10);
418
419 if(!hit) { return; }
420-
421+
422+ if (node.name.startsWith("clouds")) { canvas.clouds.selected = node.name; alert("New selected cloud: " + canvas.clouds.selected); }
423+
424 hit.modelPos = vec3.copy(vec3.create(), hit.pos);
425- vec3.transformMat4(hit.pos, hit.pos, canvas.unitNode.modelMatrix);
426- vec3.transformMat4(hit.normal, hit.normal, canvas.unitNode.modelMatrix);
427+ vec3.transformMat4(hit.pos, hit.pos, node.modelMatrix);
428+ vec3.transformMat4(hit.normal, hit.normal, node.modelMatrix);
429
430 return hit;
431 } //}}}
432+function updateCities(canvas) {
433+ //Update selected city
434+ var hit = canvas.nodes.cities.hit;
435+ if (hit) {
436+ citiesIndex = Math.floor(hit.indices[0] / ((ArrayMax(canvas.nodes.cities.mesh.getIndexBuffer('triangles').data) + 1) / cities.length));
437+ cityName = cities[citiesIndex];
438+ if (cityName !== $('#gfm-sim-controls-select-city').val()) {
439+ $('#gfm-sim-controls-select-city').val(cityName).selectmenu("refresh");
440+ changeCity(canvas);
441+ }
442+ }
443+}
444 function brushModify(canvas) { //{{{
445 //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.
446 //Currently the brush extends to the raycasted element and its immediate neighbors.
447@@ -506,27 +454,26 @@
448 //Update mesh on GPU
449 bufferVertices.upload(canvas.gl.DYNAMIC_DRAW);
450 bufferCoords.upload(canvas.gl.DYNAMIC_DRAW);
451- canvas.unitNode.mesh.octree = new GL.Octree(canvas.unitNode.mesh);
452-
453- //Update clouds if rendered
454- //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.
455- for (var i = 0; i < canvas.clouds.quantity; i++) {
456- if (canvas.nodes['clouds' + i]) {
457- var v1 = vec3.fromValues(vertices[hit.indices[0] * 3], vertices[hit.indices[0] * 3 + 1], vertices[hit.indices[0] * 3 + 2]);
458- var v2 = vec3.fromValues(vertices[hit.indices[1] * 3], vertices[hit.indices[1] * 3 + 1], vertices[hit.indices[1] * 3 + 2]);
459- var v3 = vec3.fromValues(vertices[hit.indices[2] * 3], vertices[hit.indices[2] * 3 + 1], vertices[hit.indices[2] * 3 + 2]);
460- vec3.transformMat4(v1, v1, canvas.unitNode.modelMatrix);
461- vec3.transformMat4(v2, v2, canvas.unitNode.modelMatrix);
462- vec3.transformMat4(v3, v3, canvas.unitNode.modelMatrix);
463- var x = (v1[0] + v2[0] + v3[0]) / 3 + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
464- var y = (v1[1] + v2[1] + v3[1]) / 3 + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
465- var z = (v1[2] + v2[2] + v3[2]) / 3;
466- canvas.nodes['clouds' + i].translation = [x, y + canvas.clouds.height, z];
467- updateModelMatrix(canvas.nodes['clouds' + i]);
468- }
469- }
470+ canvas.unitNode.octree = new GL.Octree(canvas.unitNode.mesh);
471 }
472 } //}}}
473+function updateClouds(canvas) {
474+ //Update clouds if rendered
475+ //TODO: Steven, the hit now queries the radaroverlay.
476+ if (canvas.nodes[canvas.clouds.selected]) {
477+ var v1 = vec3.fromValues(vertices[hit.indices[0] * 3], vertices[hit.indices[0] * 3 + 1], vertices[hit.indices[0] * 3 + 2]);
478+ var v2 = vec3.fromValues(vertices[hit.indices[1] * 3], vertices[hit.indices[1] * 3 + 1], vertices[hit.indices[1] * 3 + 2]);
479+ var v3 = vec3.fromValues(vertices[hit.indices[2] * 3], vertices[hit.indices[2] * 3 + 1], vertices[hit.indices[2] * 3 + 2]);
480+ vec3.transformMat4(v1, v1, canvas.unitNode.modelMatrix);//move out of brushModify, perhaps onto onPan
481+ vec3.transformMat4(v2, v2, canvas.unitNode.modelMatrix);
482+ vec3.transformMat4(v3, v3, canvas.unitNode.modelMatrix);
483+ var x = (v1[0] + v2[0] + v3[0]) / 3;// + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
484+ var y = (v1[1] + v2[1] + v3[1]) / 3;// + Math.floor((Math.random() * (1 + 10000 - (-10000)) + (-10000)));
485+ var z = (v1[2] + v2[2] + v3[2]) / 3;
486+ canvas.nodes[canvas.clouds.selected].translation = [x, y + canvas.clouds.height, z];
487+ updateModelMatrix(canvas.nodes[canvas.clouds.selected]);
488+ }
489+}
490 function initializeMarker(canvas) { //{{{
491 //Initialize data marker and tooltip display once per page load
492 var marker = $('#' + canvas.dataMarkers.marker.id);
493@@ -562,7 +509,9 @@
494 marker.on('click touch', function () {
495 marker.fadeOut(175);
496 marker.tooltipster('close');
497+ marker.closed = true;
498 });
499+ marker.closed = false;
500 canvas.dataMarkers.marker.selector = marker;
501 }
502 updateMarker(canvas, true);
503@@ -569,10 +518,10 @@
504 } //}}}
505 function repositionMarker(canvas) { //{{{
506 //Mover marker to point to mouse position, offset in y by 1 to enable immediate clicking.
507- if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit) || !canvas.camera.ready) { return; }
508+ //Return if no marker hit exists, the camera is not rendering, or if no reposition has been scheduled.
509+ if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit) || !canvas.camera.ready || !canvas.dataMarkers.reposition) { return; }
510 var size = canvas.dataMarkers.size;
511 var screenPoint = vec3.transformMat4(vec3.create(), canvas.dataMarkers.marker.hit.pos, canvas.camera.vpMatrix);
512- //console.log(canvas, canvas.selector, $(canvas.id)
513 var x = (screenPoint[0] + 1.0) * (canvas.width / 2) + canvas.selector.offset().left;
514 var y = (-screenPoint[1] + 1.0) * (canvas.height / 2) + canvas.selector.offset().top;
515 canvas.dataMarkers.marker.selector.css({
516@@ -579,15 +528,15 @@
517 'left': (Math.round(x) - size[0] / 2) + 'px',
518 'top': (Math.round(y) - size[1] + 1) + 'px'
519 });
520-
521 if (canvas.dataMarkers.marker.selector.tooltipster('status').state != 'closed') { canvas.dataMarkers.marker.selector.tooltipster('reposition'); }
522+ canvas.dataMarkers.reposition = false;
523 } //}}}
524 function updateMarker(canvas, reset) { //{{{
525 //Retrieve data value fields and plots them on data marker popup if a hit has been registered.
526 //TODO: Automatically pick up any field of size md.mesh.numberofelements
527 //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.
528- if (isEmptyOrUndefined(canvas.dataMarkers.marker.hit)) { return; }
529- if (isEmptyOrUndefined(canvas.unitNode)) { setTimeout( function(){ updateMarker(canvas, reset); }, 750); return; }
530+ if (!canvas.dataMarkers.enabled || isEmptyOrUndefined(canvas.dataMarkers.marker.hit)) { return; }
531+ if (isEmptyOrUndefined(canvas.unitNode) || isEmptyOrUndefined(canvas.unitNode.mesh)) { setTimeout( function(){ updateMarker(canvas, reset); }, 750); return; }
532
533 var hit = canvas.dataMarkers.marker.hit;
534
535@@ -605,41 +554,49 @@
536 velocity = md.initialization.vel;
537 }
538
539- //Determine data values at hit position.
540- 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]];
541- var hitLatitude = [latitude[hit.indices[0]], latitude[hit.indices[1]], latitude[hit.indices[2]]];
542- var hitLongitude = [longitude[hit.indices[0]], longitude[hit.indices[1]], longitude[hit.indices[2]]];
543- var hitThickness = [thickness[hit.indices[0]], thickness[hit.indices[1]], thickness[hit.indices[2]]];
544- var hitVelocity = [velocity[hit.indices[0]], velocity[hit.indices[1]], velocity[hit.indices[2]]];
545- var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
546- var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
547- var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
548- var valueLatitude = Math.abs(hitLatitude[0] * hit.uvw[0] + hitLatitude[1] * hit.uvw[1] + hitLatitude[2] * hit.uvw[2]);
549- var valueLongitude = Math.abs(hitLongitude[0] * hit.uvw[0] + hitLongitude[1] * hit.uvw[1] + hitLongitude[2] * hit.uvw[2]);
550- var valueThickness = hitThickness[0] * hit.uvw[0] + hitThickness[1] * hit.uvw[1] + hitThickness[2] * hit.uvw[2];
551- var valueVelocity = hitVelocity[0] * hit.uvw[0] + hitVelocity[1] * hit.uvw[1] + hitVelocity[2] * hit.uvw[2];
552-
553 //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.
554- var format = canvas.dataMarkers.format.slice();
555- for (var i = 1; i < format.length; i++) {
556- if (format[i].toLowerCase() == 'x') { format[i] = hit.modelPos[0]; }
557- else if (format[i].toLowerCase() == 'y') { format[i] = hit.modelPos[1]; }
558- else if (format[i].toLowerCase() == 'z') { format[i] = hit.modelPos[2]; }
559- else if (format[i].toLowerCase() == 'lat') { format[i] = valueLatitude; }
560- else if (format[i].toLowerCase() == 'long') { format[i] = valueLongitude; }
561- else if (format[i].toLowerCase() == 'thickness') { format[i] = valueThickness; }
562- else if (format[i].toLowerCase() == 'vel') { format[i] = valueVelocity; }
563- else if (format[i].toLowerCase() == 'value') { format[i] = value; }
564+ var labels = canvas.dataMarkers.labels.slice();
565+ for (var i = 0; i < labels.length; i++) {
566+ if (labels[i].toLowerCase() === 'x') { labels[i] = hit.modelPos[0]; }
567+ else if (labels[i].toLowerCase() === 'y') { labels[i] = hit.modelPos[1]; }
568+ else if (labels[i].toLowerCase() === 'z') { labels[i] = hit.modelPos[2]; }
569+ else if (labels[i].toLowerCase() === 'latitude') {
570+ var hitLatitude = [latitude[hit.indices[0]], latitude[hit.indices[1]], latitude[hit.indices[2]]];
571+ var valueLatitude = Math.abs(hitLatitude[0] * hit.uvw[0] + hitLatitude[1] * hit.uvw[1] + hitLatitude[2] * hit.uvw[2]);
572+ labels[i] = valueLatitude;
573+ }
574+ else if (labels[i].toLowerCase() === 'longitude') {
575+ var hitLongitude = [longitude[hit.indices[0]], longitude[hit.indices[1]], longitude[hit.indices[2]]];
576+ var valueLongitude = Math.abs(hitLongitude[0] * hit.uvw[0] + hitLongitude[1] * hit.uvw[1] + hitLongitude[2] * hit.uvw[2]);
577+ labels[i] = valueLongitude;
578+ }
579+ else if (labels[i].toLowerCase() === 'thickness') {
580+ var hitThickness = [thickness[hit.indices[0]], thickness[hit.indices[1]], thickness[hit.indices[2]]];
581+ var valueThickness = hitThickness[0] * hit.uvw[0] + hitThickness[1] * hit.uvw[1] + hitThickness[2] * hit.uvw[2];
582+ labels[i] = valueThickness;
583+ }
584+ else if (labels[i].toLowerCase() === 'velocity') {
585+ var hitVelocity = [velocity[hit.indices[0]], velocity[hit.indices[1]], velocity[hit.indices[2]]];
586+ var valueVelocity = hitVelocity[0] * hit.uvw[0] + hitVelocity[1] * hit.uvw[1] + hitVelocity[2] * hit.uvw[2];
587+ labels[i] = valueVelocity;
588+ }
589+ else if (labels[i].toLowerCase() === 'value') {
590+ 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]];
591+ var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
592+ var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
593+ var value = canvas.unitNode.caxis[0] * (1.0 - v) + canvas.unitNode.caxis[1] * v;
594+ labels[i] = value;
595+ }
596 }
597
598 //Apply changes to tooltip
599- $('#tooltip-content-data-marker-' + canvas.id).html(sprintf.apply(null, format));
600+ $('#tooltip-content-data-marker-' + canvas.id).html(sprintf.apply(null, canvas.dataMarkers.format.concat(labels)));
601 $('#tooltip-content-data-marker-' + canvas.id).css({'font': canvas.dataMarkers.font});
602
603 //If animated, setup animation loop to update plot as movie plays.
604 if (canvas.dataMarkers.animated) {
605- var isEmpty = (canvas.dataMarkers.values.length == 0);
606- var lastUpdatedIndex = (canvas.dataMarkers.values.length-1);
607+ var isEmpty = (canvas.dataMarkers.values.length === 0);
608+ var lastUpdatedIndex = (canvas.dataMarkers.values.length - 1);
609 var newMovieFrame = (!isEmpty && canvas.dataMarkers.values[lastUpdatedIndex][0] != canvas.animation.frame);
610 //If new data marker has been placed, reinitialize plot. If not, push new value into plot value array.
611 if (reset) {
612@@ -646,7 +603,7 @@
613 canvas.dataMarkers.values = [];
614 newMovieFrame = true;
615 for (var currentFrame = 0; currentFrame < (canvas.unitNode.movieLength); currentFrame++) {
616- coords = canvas.unitNode.texcoords[currentFrame];
617+ coords = canvas.unitMovieData[currentFrame];
618 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]];
619 var u = hitCoords[0] * hit.uvw[0] + hitCoords[2] * hit.uvw[1] + hitCoords[4] * hit.uvw[2];
620 var v = hitCoords[1] * hit.uvw[0] + hitCoords[3] * hit.uvw[1] + hitCoords[5] * hit.uvw[2];
621@@ -655,7 +612,13 @@
622 }
623 }
624 else {
625+ if (canvas.animation.lastFrame > canvas.animation.frame) {
626+ canvas.dataMarkers.values = [];
627+ }
628 if (isEmpty || newMovieFrame) {
629+ coords = canvas.unitMovieData[canvas.animation.frame];
630+ var hitValues = [coords[hit.indices[0]], coords[hit.indices[1]], coords[hit.indices[2]]];
631+ var value = hitValues[0] * hit.uvw[0] + hitValues[1] * hit.uvw[1] + hitValues[2] * hit.uvw[2];
632 canvas.dataMarkers.values.push([canvas.animation.frame, value]);
633 }
634 }
635@@ -663,7 +626,7 @@
636 //Replot data marker popup using update data value array.
637 if (isEmpty || newMovieFrame) {
638 var dataLabels = {'latitude': valueLatitude, 'longitude': valueLongitude, 'thickness': valueThickness, 'velocity': valueVelocity, 'value': value};
639- var dataDisplay = canvas.dataMarkers.values.slice(0, canvas.animation.frame+1);
640+ var dataDisplay = canvas.dataMarkers.values.slice(0, canvas.animation.frame + 1);
641 plot(
642 'id', '#sim-plot',
643 'type', 'bar',
644@@ -679,12 +642,21 @@
645 );
646 }
647 }
648- repositionMarker(canvas);
649- if (reset) {
650+ canvas.dataMarkers.reposition = true;
651+ if (reset && !canvas.dataMarkers.marker.selector.closed) {
652 canvas.dataMarkers.marker.selector.fadeIn(175);
653 canvas.dataMarkers.marker.selector.tooltipster('open');
654 }
655 } //}}}
656+function animateValue(current, target, duration, stepCallback, doneCallback) { //{{{
657+ //Animates scalar value for length duration, calling callback each step.
658+ //TODO: Specify lerp easing as option (cubic, linear, cosine).
659+ $({'value':current}).animate({'value':target}, {
660+ duration: duration,
661+ step: stepCallback,
662+ done: doneCallback
663+ });
664+} //}}}
665 //}}}
666 //{{{ Drawing Functions
667 function updateCameraMatrix(canvas) { //{{{
668@@ -706,12 +678,12 @@
669
670 //Calculate rotation around camera focal point about worldspace origin
671 if (canvas.view.twod) {
672- mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, DEG2RAD * 0, [0, 1, 0]);
673+ mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, -DEG2RAD * 0, [0, 1, 0]);
674 mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * 90, [1, 0, 0]);
675 mat4.multiply(rotationMatrix, elevationRotationMatrix, azimuthRotationMatrix);
676 }
677 else {
678- mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, DEG2RAD * canvas.view.rotation[0], [0, 1, 0]);
679+ mat4.rotate(azimuthRotationMatrix, azimuthRotationMatrix, -DEG2RAD * (canvas.view.rotation[0] + 90), [0, 1, 0]);
680 mat4.rotate(elevationRotationMatrix, elevationRotationMatrix, DEG2RAD * canvas.view.rotation[1], [1, 0, 0]);
681 mat4.multiply(rotationMatrix, elevationRotationMatrix, azimuthRotationMatrix);
682 }
683@@ -734,6 +706,7 @@
684
685 vec3.transformMat4(canvas.camera.position, cameraPosition, canvas.camera.vpInverseMatrix);
686 canvas.camera.ready = true;
687+ repositionMarker(canvas);
688 }//}}}
689 function drawSceneGraphNode(canvas, node) { //{{{
690 if (!node.enabled) { return; }
691@@ -748,7 +721,7 @@
692 mat4.multiply(mvMatrix, canvas.camera.vMatrix, node.modelMatrix);
693
694 var normalMatrix = mat4.create();
695- mat4.invert(normalMatrix, mvMatrix);
696+ mat4.invert(normalMatrix, node.modelMatrix);
697 mat4.transpose(normalMatrix, normalMatrix);
698
699 if (node.texture) { node.texture.bind(0); }
700@@ -768,23 +741,23 @@
701 vec3.sub(cameraPositionRelative, origin, node.translation);
702 cameraHeight = vec3.length(cameraPositionRelative);
703
704- var atm = { //Default Values
705- wavelength_r: 0.65, //0.65 Red wavelength (micrometers)
706- wavelength_g: 0.57, //0.57 Green wavelength (micrometers)
707- wavelength_b: 0.475, //0.475 Green wavelength (micrometers)
708- eSun: 100.0, //20.0 Sun intensity
709- kRayleigh: 0.0025, //0.0025 Rayleigh scattering amount
710- kMie: 0.000, //0.01 Mie scattering amount
711- g: -0.99, //-0.99 Mie phase asymmetry/direction factor
712- hdr_exposure: 0.8, //0.8 High Dynamic Range Exposure
713- scale: 1.25, //1.025 Scale of atmosphere. WARNING: Change atmosphereScale in applyoptions.js, and scaling constants.
714- scaleDepth: 0.25, //0.25 Percentage altitude at which the atmosphere's average density is found
715- a: -0.00287, //-0.00287 Scaling constant a
716- b: 0.459, //0.459 Scaling constant b
717- c: 3.83, //3.83 Scaling constant c
718- d: -6.80, //-6.80 Scaling constant d
719- e: 3.6, //5.25 Scaling constant e. Lower when increasing atmosphere scale.
720- attenuation: 0.5 //0.5 Strength of atmospheric scattering on ground shading.
721+ var atm = { //Default Values
722+ wavelength_r: 0.65, //0.65 Red wavelength (micrometers)
723+ wavelength_g: 0.57, //0.57 Green wavelength (micrometers)
724+ wavelength_b: 0.475, //0.475 Green wavelength (micrometers)
725+ eSun: 100.0, //20.0 Sun intensity
726+ kRayleigh: 0.0025, //0.0025 Rayleigh scattering amount
727+ kMie: 0.000, //0.01 Mie scattering amount
728+ g: -0.99, //-0.99 Mie phase asymmetry/direction factor
729+ hdr_exposure: 0.8, //0.8 High Dynamic Range Exposure
730+ scale: 1.25, //1.025 Scale of atmosphere. WARNING: Change atmosphereScale in applyoptions.js, and scaling constants.
731+ scaleDepth: 0.25, //0.25 Percentage altitude at which the atmosphere's average density is found
732+ a: -0.00287, //-0.00287 Scaling constant a
733+ b: 0.459, //0.459 Scaling constant b
734+ c: 3.83, //3.83 Scaling constant c
735+ d: -6.80, //-6.80 Scaling constant d
736+ e: 3.6, //5.25 Scaling constant e. Lower when increasing atmosphere scale.
737+ attenuation: 0.5 //0.5 Strength of atmospheric scattering on ground shading.
738 };
739
740 var inv_wavelength4 = [1.0 / Math.pow(atm.wavelength_r, 4), 1.0 / Math.pow(atm.wavelength_g, 4), 1.0 / Math.pow(atm.wavelength_b, 4)];
741@@ -797,14 +770,19 @@
742 m4MVP: mvpMatrix,
743 m4Normal: normalMatrix,
744 m4Model: node.modelMatrix,
745- //u_lightPosition: [-lightOrigin[0], -lightOrigin[1], -lightOrigin[2]],
746- u_lightPosition: [1.0, 1.0, 1.0],
747- u_diffuseColor: [1.0, 0.9, 0.9],
748+ u_lightPosition: lightOrigin,
749+ //u_lightPosition: [1.0, 1.0, 1.0],
750+ u_diffuseColor: node.diffuseColor,
751 u_texture: 0,
752 u_alpha: node.alpha,
753+ u_maskZerosColor: node.maskZerosColor,
754+ u_maskZerosEnabled: node.maskZerosEnabled,
755+ u_maskZerosTolerance: node.maskZerosTolerance,
756+ u_maskZerosZeroValue: node.maskZerosZeroValue,
757 u_maskEnabled: node.maskEnabled,
758 u_maskHeight: node.maskHeight,
759 u_maskColor: node.maskColor,
760+ u_pointSize: node.pointSize,
761 v3CameraPosition: origin,
762 v3Translate: node.translation,
763 v3LightPos: lightOrigin,
764@@ -840,10 +818,10 @@
765 //TODO: Come up with better way to check if shaders are ready, or move outside of main draw function
766 var nodes = canvas.nodes;
767 if (!canvas.draw.ready) {
768- if (nodes.length !== 0) {
769+ if (Object.keys(nodes).length !== 0) {
770 canvas.draw.ready = true;
771 for (var node in nodes) {
772- if (nodes[node].shader.ready == false) {
773+ if (nodes[node].shader.ready === false) {
774 canvas.draw.ready = false;
775 break;
776 }
777@@ -854,9 +832,6 @@
778
779 //Begin rendering nodes
780 if (canvas.draw.ready) {
781- if (canvas.textcanvas) { canvas.textcanvas.draw(canvas); }
782- if (canvas.overlaycanvas) { canvas.overlaycanvas.draw(canvas); }
783-
784 var rect = canvas.getBoundingClientRect();
785 canvas.width = rect.width;
786 canvas.height = rect.height;
787@@ -869,10 +844,13 @@
788
789 updateCameraMatrix(canvas);
790
791+ if (canvas.textcanvas) { canvas.textcanvas.draw(canvas); }
792+ if (canvas.overlaycanvas) { canvas.overlaycanvas.draw(canvas); }
793+
794 var drawPassNumber = 3;
795- for (var i = drawPassNumber - 1; i >= 0; i--) {
796+ for (var i = drawPassNumber - 1; i >= 0; --i) {
797 for (var node in nodes) {
798- if (nodes[node].drawOrder == i) { drawSceneGraphNode(canvas, nodes[node]); }
799+ if (nodes[node].drawOrder === i) { drawSceneGraphNode(canvas, nodes[node]); }
800 }
801 }
802 }
803Index: ../trunk-jpl/src/m/plot/applyoptions.js
804===================================================================
805--- ../trunk-jpl/src/m/plot/applyoptions.js (revision 21767)
806+++ ../trunk-jpl/src/m/plot/applyoptions.js (revision 21768)
807@@ -1,4 +1,4 @@
808-function applyoptions(md, data, datatype, options, canvas, gl, node){ //{{{
809+function applyoptions(md, data, options, canvas){ //{{{
810 //APPLYOPTIONS - apply colobar, text, cloud, and expdisp options to current plot
811 //
812 // Usage:
813@@ -7,14 +7,11 @@
814 // See also: PLOTMODEL, PARSE_OPTIONS
815
816 //{{{ colorbar
817+ var gl = canvas.gl;
818 if (options.exist('colorbar')) {
819 if (options.getfieldvalue('colorbar')==1) {
820- //{{{ Handle movie data
821- if (datatype == 5) {
822- data = data[0];
823- } //}}}
824 //{{{ Variable options initialization
825- var caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
826+ var caxis = options.getfieldvalue('caxis');
827 var colorbarinnerlabels = options.getfieldvalue('colorbarinnerlabels','off');
828 var ccanvasid, ctitleid, clabelsid, ccanvas, ctitle, clabels, ccontext, cmap, colorbar, cwidth, cheight, cgradient, color, y, x;
829 //}}}
830@@ -23,14 +20,31 @@
831 var cdivisions = options.getfieldvalue('colorbarnticks', 6);
832 var caxisdelta = caxis[1] - caxis[0];
833 var precision = options.getfieldvalue('colorbarprecision', 3);
834+ var format = options.getfieldvalue('colorbarformat', 'f').toLowerCase();
835 if (options.getfieldvalue('log','off')!='off') {
836 for (var i=cdivisions; i >= 0; i--) {
837 var scale = (Math.log10(caxis[1])-Math.log10(caxis[0]))/Math.log10(options.getfieldvalue('log', 10));
838- labels[i] = (Math.pow(options.getfieldvalue('log', 10), Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log', 10))+scale*(cdivisions-i)/cdivisions)).toFixed(precision);
839+ if (format === 'f') {
840+ labels[i] = (Math.pow(options.getfieldvalue('log', 10), Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log', 10))+scale*(cdivisions-i)/cdivisions)).toFixed(precision);
841+ }
842+ else if (format === 'e') {
843+ labels[i] = (Math.pow(options.getfieldvalue('log', 10), Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log', 10))+scale*(cdivisions-i)/cdivisions)).toPrecision(precision);
844+ }
845+ else {
846+ labels[i] = (Math.pow(options.getfieldvalue('log', 10), Math.log10(caxis[0])/Math.log10(options.getfieldvalue('log', 10))+scale*(cdivisions-i)/cdivisions)).toFixed(precision);
847+ }
848 }
849 } else {
850 for (var i=cdivisions; i >= 0; i--) {
851- labels[i] = (caxisdelta*(cdivisions-i)/cdivisions+caxis[0]).toFixed(precision);
852+ if (format === 'f') {
853+ labels[i] = (caxisdelta*(cdivisions-i)/cdivisions+caxis[0]).toFixed(precision);
854+ }
855+ else if (format === 'e') {
856+ labels[i] = (caxisdelta*(cdivisions-i)/cdivisions+caxis[0]).toPrecision(precision);
857+ }
858+ else {
859+ labels[i] = (caxisdelta*(cdivisions-i)/cdivisions+caxis[0]).toFixed(precision);
860+ }
861 }
862 } //}}}
863 //{{{ Initialize colorbar canvas
864@@ -116,166 +130,8 @@
865 tcontext.fillStyle = tgradient;
866 tcontext.fillRect(0, 0, 256, 256);
867 tURL = tcanvas.toDataURL();
868- node.texture = initTexture(gl, tURL);
869- node.textureCanvas = tcanvas;
870- node.caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
871+ canvas.unitNode.texture = initTexture(canvas.gl, tURL);
872 //}}}
873- //{{{ expdisp contours
874- if (options.exist('expdisp')) {
875- canvas.nodes.expdisp = Node(gl, options);
876- var node = canvas.nodes.expdisp;
877-
878- //declare variables: {{{
879- var vertices = [];
880- var indices = [];
881- var colors = [];
882- var rgbcolor = [];
883- var xmin, xmax;
884- var ymin, ymax;
885- var zmin, zmax;
886- var scale;
887-
888- //Process data and model
889- var x = options.getfieldvalue('expdisp').x;
890- var y = options.getfieldvalue('expdisp').y;
891- var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf, 0);
892-
893- if (options.getfieldvalue('expdisp').z) {
894- z = options.getfieldvalue('expdisp').z;
895- }
896- //}}}
897-
898- //Compute coordinates and data range: //{{{
899- var modelxlim = [ArrayMin(x), ArrayMax(x)];
900- var modelylim = [ArrayMin(y), ArrayMax(y)];
901- var modelzlim = [ArrayMin(z), ArrayMax(z)];
902- var xlim = options.getfieldvalue('xlim', modelxlim);
903- var ylim = options.getfieldvalue('ylim', modelylim);
904- var zlim = options.getfieldvalue('zlim', modelzlim);
905- xmin = xlim[0];
906- xmax = xlim[1];
907- ymin = ylim[0];
908- ymax = ylim[1];
909- zmin = zlim[0];
910- zmax = zlim[1];
911- //}}}
912-
913- //Compute scaling: //{{{
914- var scale = 1 / (xmax - xmin);
915- node.shaderName = 'colored';
916- node.shader = gl.shaders[node.shaderName].program;
917- node.scale = [scale, scale, scale*options.getfieldvalue('heightscale', 1)];
918- node.translation = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (-2 / scale)];
919- node.modelMatrix = updateModelMatrix(node);
920- node.drawMode = gl.LINE_LOOP;
921- node.drawOrder = 0;
922- node.useIndexBuffer = false;
923- node.disableDepthTest = true;
924- //}}}
925-
926- //some defaults:
927- colors.itemSize = 4;
928-
929- //retrieve some options
930- var linewidth=options.getfieldvalue('linewidth', 1);
931- var edgecolor=options.getfieldvalue('edgecolor','black'); //RGBCOLOR?
932-
933- vertices.itemSize = 3;
934- for(var i=0; i < x.length; i++){
935- vertices[vertices.length] = x[i];
936- vertices[vertices.length] = y[i];
937- vertices[vertices.length] = z[i];
938-
939- //edgecolor
940- rgbcolor = [0.0, 0.0, 0.0];
941- colors[colors.length] = rgbcolor[0];
942- colors[colors.length] = rgbcolor[1];
943- colors[colors.length] = rgbcolor[2];
944- colors[colors.length] = 1.0;
945- }
946-
947- //Initalize buffers:
948- node.arrays = [vertices, colors];
949- node.buffers = initBuffers(gl, node.arrays);
950- } //}}}
951- //{{{ cloud of points
952- if (options.exist('cloud')) {
953- canvas.nodes.cloud = Node(gl, options);
954- var node = canvas.nodes.cloud;
955-
956- //declare variables: {{{
957- var vertices = [];
958- var indices = [];
959- var colors = [];
960- var rgbcolor = [];
961- var xmin, xmax;
962- var ymin, ymax;
963- var zmin, zmax;
964- var scale;
965-
966- //Process data and model
967- var x = options.getfieldvalue('cloud').x;
968- var y = options.getfieldvalue('cloud').y;
969- var z = Array.apply(null, Array(x.length)).map(Number.prototype.valueOf, 0);
970-
971- if (options.getfieldvalue('cloud').z) {
972- z = options.getfieldvalue('cloud').z;
973- }
974- //}}}
975-
976- //Compute coordinates and data range: //{{{
977- var modelxlim = [ArrayMin(x), ArrayMax(x)];
978- var modelylim = [ArrayMin(y), ArrayMax(y)];
979- var modelzlim = [ArrayMin(z), ArrayMax(z)];
980- var xlim = options.getfieldvalue('xlim', modelxlim);
981- var ylim = options.getfieldvalue('ylim', modelylim);
982- var zlim = options.getfieldvalue('zlim', modelzlim);
983- xmin = xlim[0];
984- xmax = xlim[1];
985- ymin = ylim[0];
986- ymax = ylim[1];
987- zmin = zlim[0];
988- zmax = zlim[1];
989- //}}}
990-
991- //Compute scaling: //{{{
992- var scale = 1 / (xmax - xmin);
993- node.shaderName = 'colored';
994- node.shader = gl.shaders[node.shaderName].program;
995- node.scale = [scale, scale, scale*options.getfieldvalue('heightscale', 1)];
996- node.translation = [(xmin + xmax) / (-2 / scale), (ymin + ymax) / (-2 / scale), (zmin + zmax) / (-2 / scale)];
997- node.modelMatrix = updateModelMatrix(node);
998- node.drawMode = gl.POINTS;
999- node.drawOrder = 0;
1000- node.useIndexBuffer = false;
1001- node.disableDepthTest = true;
1002- //}}}
1003-
1004- //some defaults:
1005- colors.itemSize = 4;
1006-
1007- //retrieve some options
1008- var linewidth=options.getfieldvalue('linewidth', 1);
1009- var edgecolor=options.getfieldvalue('edgecolor','black'); //RGBCOLOR?
1010-
1011- vertices.itemSize = 3;
1012- for(var i=0; i < x.length; i++){
1013- vertices[vertices.length] = x[i];
1014- vertices[vertices.length] = y[i];
1015- vertices[vertices.length] = z[i];
1016-
1017- //edgecolor
1018- rgbcolor = [0.0, 0.0, 0.0];
1019- colors[colors.length] = rgbcolor[0];
1020- colors[colors.length] = rgbcolor[1];
1021- colors[colors.length] = rgbcolor[2];
1022- colors[colors.length] = 1.0;
1023- }
1024-
1025- //Initalize buffers:
1026- node.arrays = [vertices, colors];
1027- node.buffers = initBuffers(gl, node.arrays);
1028- } //}}}
1029 //{{{ text display
1030 if (options.exist('textlabels')) {
1031 var textcanvas, textcanvasid;
1032@@ -324,39 +180,39 @@
1033 var overlaycanvasid = options.getfieldvalue('latlongoverlayid', options.getfieldvalue('canvasid')+'-overlay');
1034 var overlaycanvas = $('#'+overlaycanvasid)[0];
1035 var latitudes = {
1036- //"-90": 1,
1037- //"-65": .999,
1038- "-60": 0.994046875,
1039- //"-55": 0.983187500000002,
1040- //"-50": 0.97173550854167,
1041- "-45": 0.955729166666666,
1042- //"-40": 0.94218750000000218,
1043- //"-35": 0.94218750000000218,
1044- "-30": 0.9226562500000024,
1045- //"-25": 0.87934895833333526,
1046- //"-20": 0.856572916666669,
1047- //"-15": 0.830729166666665,
1048- //"-10": 0.803552708333336,
1049- //"-5": 0.77395833333333541,
1050- "0": 0.74218749999999811,
1051- //"5": 0.70950364583333347,
1052- //"10": 0.67479166666666823,
1053- //"15": 0.63932291666666663,
1054- //"20": 0.60171875,
1055- //"25": 0.563453125,
1056- "30": 0.523390625000001,
1057- //"35": 0.48401875,
1058- //"40": 0.44296875,
1059- "45": 0.4020001,
1060- //"50": 0.3578125,
1061- //"55": 0.311875,
1062- "60": 0.26953124999999978,
1063- //"65": 0.225390625,
1064- //"70": 0.18125,
1065- //"75": 0.13541666666666671,
1066- //"80": 0.08953125,
1067- //"85": 0.046250000000000013,
1068- //"90": 0.0,
1069+ //'-90': 1,
1070+ //'-65': .999,
1071+ '-60': 0.994046875,
1072+ //'-55': 0.983187500000002,
1073+ //'-50': 0.97173550854167,
1074+ '-45': 0.955729166666666,
1075+ //'-40': 0.94218750000000218,
1076+ //'-35': 0.94218750000000218,
1077+ '-30': 0.9226562500000024,
1078+ //'-25': 0.87934895833333526,
1079+ //'-20': 0.856572916666669,
1080+ //'-15': 0.830729166666665,
1081+ //'-10': 0.803552708333336,
1082+ //'-5': 0.77395833333333541,
1083+ '0': 0.74218749999999811,
1084+ //'5': 0.70950364583333347,
1085+ //'10': 0.67479166666666823,
1086+ //'15': 0.63932291666666663,
1087+ //'20': 0.60171875,
1088+ //'25': 0.563453125,
1089+ '30': 0.523390625000001,
1090+ //'35': 0.48401875,
1091+ //'40': 0.44296875,
1092+ '45': 0.4020001,
1093+ //'50': 0.3578125,
1094+ //'55': 0.311875,
1095+ '60': 0.26953124999999978,
1096+ //'65': 0.225390625,
1097+ //'70': 0.18125,
1098+ //'75': 0.13541666666666671,
1099+ //'80': 0.08953125,
1100+ //'85': 0.046250000000000013,
1101+ //'90': 0.0,
1102 }
1103 var longitudes = [-150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150, 180];
1104 overlaycanvas.draw = function(canvas) {
1105@@ -400,96 +256,197 @@
1106 var is2d = meshresults[4];
1107 var isplanet = meshresults[5];
1108
1109- var modelxlim = [ArrayMin(x), ArrayMax(x)];
1110- var modelylim = [ArrayMin(y), ArrayMax(y)];
1111- var modelzlim = [ArrayMin(z), ArrayMax(z)];
1112- var xlim = options.getfieldvalue('xlim', modelxlim);
1113- var ylim = options.getfieldvalue('ylim', modelylim);
1114- var zlim = options.getfieldvalue('zlim', modelzlim);
1115- xmin = xlim[0];
1116- xmax = xlim[1];
1117- ymin = ylim[0];
1118- ymax = ylim[1];
1119- zmin = zlim[0];
1120- zmax = zlim[1];
1121+ var xlim = options.getfieldvalue('xlim', [ArrayMin(x), ArrayMax(x)]);
1122+ var ylim = options.getfieldvalue('ylim', [ArrayMin(y), ArrayMax(y)]);
1123+ var zlim = options.getfieldvalue('zlim', [ArrayMin(z), ArrayMax(z)]);
1124+
1125+ var global = vec3.length([(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2]) < 6371000/10; //tolerance for global models = center is 637100 meters away from center of earth
1126+ var translation = global ? [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2] : [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) - 6371000, (zlim[0] + zlim[1]) / 2];
1127
1128- var global = vec3.length([(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2]) < 6371000/10; //tolerance for global models = center is 637100 meters away from center of earth
1129- var atmosphereScale = 1.25;
1130- var translation = global ? [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2] : [(xmin + xmax) / 2, (ymin + ymax) - 6371000, (zmin + zmax) / 2];
1131-
1132- if (options.getfieldvalue('render',[]).indexOf('sky')!=-1) {
1133- //atmosphere
1134- var node = Node(gl);
1135- node.name = "atmosphere";
1136- node.shaderName = "SkyFromSpace";
1137- node.shader = gl.shaders[node.shaderName];
1138- node.drawOrder = 1;
1139- node.cullFace = gl.FRONT;
1140- node.enableCullFace = true;
1141- node.mesh = GL.Mesh.icosahedron({size: 6371000*atmosphereScale, subdivisions: 6});
1142- node.rotation = [0, 0, 0];
1143- node.translation = translation;
1144- node.center = [0, 0, 0];
1145- updateModelMatrix(node);
1146- canvas.nodes[node.name] = node;
1147+ var renderObjects = options.getfieldvalue('render',{});
1148+ if ('sky' in renderObjects && !('sky' in canvas.nodes)) {
1149+ var object = renderObjects.sky;
1150+ object = {
1151+ enabled: defaultFor(object.enabled, true),
1152+ scale: defaultFor(object.scale, 1),
1153+ };
1154+ if (object.enabled) {
1155+ var atmosphereScale = 1.25;
1156+ var mesh = GL.Mesh.icosahedron({size: 6371000 * atmosphereScale, subdivisions: 5});
1157+ var texture = initTexture(gl, canvas.rootPath + 'textures/TychoSkymapII_t4_2k.jpg');
1158+ node = new Node(
1159+ 'canvas', canvas,
1160+ 'options', options,
1161+ 'name', 'sky',
1162+ 'shaderName', 'SkyFromSpace',
1163+ 'cullFace', gl.FRONT,
1164+ 'mesh', mesh,
1165+ 'texture',texture,
1166+ 'translation',translation
1167+ );
1168+ }
1169 }
1170- if (options.getfieldvalue('render',[]).indexOf('space')!=-1) {
1171- //skysphere
1172- node = Node(gl);
1173- node.name = "skysphere";
1174- node.shaderName = "Textured";
1175- node.shader = gl.shaders[node.shaderName];
1176- node.drawOrder = 2;
1177- node.cullFace = gl.FRONT;
1178- node.enableCullFace = true;
1179- node.mesh = GL.Mesh.sphere({size: 6371000*20});
1180- node.texture = initTexture(gl, canvas.rootPath+'textures/TychoSkymapII_t4_2k.jpg');
1181- node.rotation = [0, 0, 0];
1182- node.translation = translation;
1183- node.center = [0, 0, 0];
1184- updateModelMatrix(node);
1185- canvas.nodes[node.name] = node;
1186+ if ('space' in renderObjects && !('space' in canvas.nodes)) {
1187+ var object = renderObjects.space;
1188+ object = {
1189+ enabled: defaultFor(object.enabled, true),
1190+ scale: defaultFor(object.scale, 1),
1191+ };
1192+ if (object.enabled) {
1193+ var mesh = GL.Mesh.sphere({size: 6371000 * 20});
1194+ var texture = initTexture(gl, canvas.rootPath + 'textures/TychoSkymapII_t4_2k.jpg');
1195+ node = new Node(
1196+ 'canvas', canvas,
1197+ 'options', options,
1198+ 'name', 'space',
1199+ 'shaderName', 'Textured',
1200+ 'cullFace', gl.FRONT,
1201+ 'drawOrder', 2,
1202+ 'mesh', mesh,
1203+ 'texture',texture,
1204+ 'translation',translation
1205+ );
1206+ }
1207 }
1208+ if ('coastlines' in renderObjects && !('coastlines' in canvas.nodes)) {
1209+ var object = renderObjects.coastlines;
1210+ object = {
1211+ enabled: defaultFor(object.enabled, false),
1212+ scale: defaultFor(object.scale, 1),
1213+ x: defaultFor(object.x, {}),
1214+ y: defaultFor(object.y, {}),
1215+ z: defaultFor(object.z, {})
1216+ };
1217+ if (object.enabled) {
1218+ node = new Node(
1219+ 'canvas', canvas,
1220+ 'options', options,
1221+ 'name', 'coastlines',
1222+ 'shaderName', 'Colored',
1223+ 'drawMode', gl.LINE_STRIP,
1224+ 'lineWidth', options.getfieldvalue('linewidth', 1),
1225+ 'scale', [object.scale, object.scale, object.scale],
1226+ 'rotation', [-90, 0, 0]
1227+ );
1228+ node.patch('Vertices', [object.x, object.y, object.z], 'FaceColor', 'none');
1229+ }
1230+ }
1231+ if ('graticule' in renderObjects && !('graticule' in canvas.nodes)) {
1232+ var object = renderObjects.graticule;
1233+ object = {
1234+ enabled: defaultFor(object.enabled, false),
1235+ scale: defaultFor(object.scale, 1),
1236+ x: defaultFor(object.x, {}),
1237+ y: defaultFor(object.y, {}),
1238+ z: defaultFor(object.z, {})
1239+ };
1240+ if (object.enabled) {
1241+ node = new Node(
1242+ 'canvas', canvas,
1243+ 'options', options,
1244+ 'name', 'graticule',
1245+ 'shaderName', 'Colored',
1246+ 'drawMode', gl.LINE_STRIP,
1247+ 'lineWidth', options.getfieldvalue('linewidth', 1),
1248+ 'scale', [object.scale, object.scale, object.scale],
1249+ 'rotation', [-90, 0, 0]
1250+ );
1251+ node.patch('Vertices', [object.x, object.y, object.z], 'FaceColor', 'none');
1252+ }
1253+ }
1254+ if ('cities' in renderObjects && !('cities' in canvas.nodes)) {
1255+ var object = renderObjects.cities;
1256+ object = {
1257+ enabled: defaultFor(object.enabled, false),
1258+ scale: defaultFor(object.scale, 1),
1259+ size: defaultFor(object.size, 1.5),
1260+ color: defaultFor(object.color, 'black'),
1261+ x: defaultFor(object.x, {}),
1262+ y: defaultFor(object.y, {}),
1263+ z: defaultFor(object.z, {}),
1264+ indices: defaultFor(object.indices, {})
1265+ };
1266+ if (object.enabled) {
1267+ var mesh = GL.Mesh.icosahedron({size: object.size, subdivisions: 1});
1268+ node = new Node(
1269+ 'canvas', canvas,
1270+ 'options', options,
1271+ 'name', 'cities',
1272+ 'shaderName', 'Colored',
1273+ 'diffuseColor', object.color,
1274+ 'lineWidth', options.getfieldvalue('linewidth', 1),
1275+ 'scale', [object.scale, object.scale, object.scale],
1276+ 'rotation', [-90, 0, 0]
1277+ );
1278+ node.geometryShader('Mesh', mesh, 'Vertices', [object.x, object.y, object.z], 'Indices', object.indices);
1279+ }
1280+ }
1281+ if ('city' in renderObjects) {
1282+ var object = renderObjects.city;
1283+ object = {
1284+ enabled: defaultFor(object.enabled, false),
1285+ name: defaultFor(object.name, 'NY'),
1286+ size: defaultFor(object.size, 15000),
1287+ color: defaultFor(object.color, 'magenta'),
1288+ x: defaultFor(object.x, {}),
1289+ y: defaultFor(object.y, {}),
1290+ z: defaultFor(object.z, {})
1291+ };
1292+ if (object.enabled) {
1293+ //city
1294+ var mesh = GL.Mesh.sphere({size: object.size});
1295+ node = new Node(
1296+ 'canvas', canvas,
1297+ 'options', options,
1298+ 'name', 'city',
1299+ 'shaderName', 'Colored',
1300+ 'diffuseColor', object.color,
1301+ 'mesh', mesh,
1302+ 'translation', [object.x, object.z, -object.y]
1303+ );
1304+
1305+ var overlaycanvasid = options.getfieldvalue('overlayid', options.getfieldvalue('canvasid')+'-overlay');
1306+ var overlaycanvas = $('#'+overlaycanvasid)[0];
1307+ overlaycanvas.draw = function(canvas) {
1308+ var rect = overlaycanvas.getBoundingClientRect();
1309+ overlaycanvas.width = rect.width;
1310+ overlaycanvas.height = rect.height;
1311+ var ctx = overlaycanvas.getContext('2d');
1312+ var node = canvas.nodes['city'];
1313+ node.translation = [object.x, object.z, -object.y];
1314+ node.updateModelMatrix();
1315+ var screenPoint = vec3.transformMat4(vec3.create(), node.translation, canvas.camera.vpMatrix);
1316+ var x = (screenPoint[0] + 1.0) * (canvas.width / 2) + canvas.selector.offset().left;
1317+ var y = (-screenPoint[1] + 1.0) * (canvas.height / 2) + canvas.selector.offset().top;
1318+
1319+ ctx.font = String(options.getfieldvalue('colorbarfontsize', 22))+'px Arial Black, sans-serif';
1320+ ctx.fillStyle = options.getfieldvalue('colorbarfontcolor','black');
1321+ ctx.strokeStyle = 'white';
1322+ ctx.textAlign = 'center';
1323+ ctx.textBaseline = 'middle';
1324+ ctx.fillText(object.name, x, y);
1325+ ctx.strokeText(object.name, x, y);
1326+ }
1327+ canvas.overlaycanvas = overlaycanvas;
1328+ }
1329+ }
1330 if (canvas.clouds.enabled) {
1331 //clouds
1332 for (var i = 0; i < canvas.clouds.quantity; i++) {
1333- node = Node(gl);
1334- node.name = "clouds" + i;
1335- node.shaderName = "Clouds";
1336- node.shader = gl.shaders[node.shaderName];
1337- node.drawOrder = 2;
1338- node.cullFace = gl.BACK;
1339- node.enableCullFace = true;
1340- node.mesh = GL.Mesh.fromURL(canvas.rootPath+'obj/cloud.obj');
1341- node.rotation = [0, 0, 0];
1342- node.scale = [2500, 2500, 2500];
1343- node.translation = [translation[0], translation[1] - 405000, translation[2]];
1344- node.center = [0, 0, 0];
1345- node.animation = {"time": Date.now(),"target": node.translation,"current": node.translation};
1346- updateModelMatrix(node);
1347- canvas.nodes[node.name] = node;
1348- //canvas.clouds.list
1349+ var mesh = GL.Mesh.fromURL(canvas.rootPath+'obj/cloud.obj');
1350+ translation = [translation[0] + Math.floor((Math.random() * (1 + 12000 - (-12000)) + (-12000))), translation[1] - 405000, translation[2] + Math.floor((Math.random() * (1 + 12000 - (-12000)) + (-12000)))];
1351+ node = new Node(
1352+ 'canvas', canvas,
1353+ 'options', options,
1354+ 'name', 'clouds' + i,
1355+ 'shaderName', 'Textured',
1356+ 'animation', {'time': Date.now(),'target': translation,'current': translation},
1357+ 'diffuseColor', [0.7,0.7,0.7,1.0],
1358+ 'mesh', mesh,
1359+ 'scale', [2500, 2500, 2500],
1360+ 'translation', translation
1361+ );
1362 }
1363- //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.
1364-
1365 }
1366- if (options.getfieldvalue('render',[]).indexOf('latlong')!=-1) {
1367- //latlong
1368- node = Node(gl);
1369- node.name = "clouds";
1370- node.shaderName = "Clouds";
1371- node.shader = gl.shaders[node.shaderName];
1372- node.drawOrder = 2;
1373- node.cullFace = gl.BACK;
1374- node.enableCullFace = true;
1375- node.mesh = GL.Mesh.fromURL(canvas.rootPath+'obj/cloud.obj');
1376- node.rotation = [0, 0, 0];
1377- node.scale = [2500, 2500, 2500];
1378- node.translation = [translation[0], translation[1] - 405000, translation[2]];
1379- node.center = [0, 0, 0];
1380- node.animation = {"time": Date.now(),"target": node.translation,"current": node.translation};
1381- updateModelMatrix(node);
1382- canvas.nodes[node.name] = node;
1383- }
1384 } //}}}
1385 } //}}}
1386Index: ../trunk-jpl/src/m/plot/plot_transient_movie.m
1387===================================================================
1388--- ../trunk-jpl/src/m/plot/plot_transient_movie.m (revision 21767)
1389+++ ../trunk-jpl/src/m/plot/plot_transient_movie.m (revision 21768)
1390@@ -6,7 +6,7 @@
1391 % See also: PLOTMODEL, PLOT_UNIT, PLOT_MANAGER
1392
1393 %prepare subplot
1394- subplot(width,width,i);
1395+ subplot(width,width,i);
1396
1397 %xlim
1398 if exist(options,'transient_movie_field'),
1399Index: ../trunk-jpl/src/m/plot/webgl_node.js
1400===================================================================
1401--- ../trunk-jpl/src/m/plot/webgl_node.js (nonexistent)
1402+++ ../trunk-jpl/src/m/plot/webgl_node.js (revision 21768)
1403@@ -0,0 +1,464 @@
1404+//NODE class definition //{{{
1405+// Description:
1406+// Contains all information for rendering an object in WebGL, including mesh, materials, shaders, and other attributes.
1407+// Usage:
1408+// node=new Node('canvas', canvas);
1409+
1410+function Node() { //{{{
1411+ //properties
1412+ // {{{
1413+ var args = Array.prototype.slice.call(arguments);
1414+ var options = new pairoptions(args.slice(0,args.length));
1415+
1416+ this.canvas = options.getfieldvalue('canvas', null),
1417+ this.options = options.getfieldvalue('options', null),
1418+ this.gl = canvas.gl;
1419+
1420+ this.alpha = options.getfieldvalue('alpha', 1.0),
1421+ this.animation = options.getfieldvalue('animation', {}),
1422+ this.arrays = options.getfieldvalue('arrays', {}),
1423+ this.caxis = options.getfieldvalue('caxis', [0.0, 1.0]),
1424+ this.center = options.getfieldvalue('center', vec3.create()),
1425+ this.cullFace = options.getfieldvalue('cullFace', this.gl.BACK),
1426+ this.computeIndices = options.getfieldvalue('computeIndices', true),
1427+ this.disableDepthTest = options.getfieldvalue('disableDepthTest', false),
1428+ this.diffuseColor = options.getfieldvalue('diffuseColor', [0.0, 0.0, 0.0, 1.0]),
1429+ this.drawMode = options.getfieldvalue('drawMode', this.gl.TRIANGLES),
1430+ this.drawOrder = options.getfieldvalue('drawOrder', 1),
1431+ this.enabled = options.getfieldvalue('enabled', true),
1432+ this.enableCullFace = options.getfieldvalue('enableCullFace', true),
1433+ this.hideOcean = options.getfieldvalue('hideOcean', false),
1434+ this.lineWidth = options.getfieldvalue('lineWidth', 1.0),
1435+ this.log = options.getfieldvalue('log', false),
1436+ this.maskColor = options.getfieldvalue('maskColor', vec4.fromValues(0.0, 0.0, 1.0, 1.0)),
1437+ this.maskEnabled = options.getfieldvalue('maskEnabled', false),
1438+ this.maskHeight = options.getfieldvalue('maskHeight', 150.0),
1439+ this.maskZerosColor = options.getfieldvalue('maskZerosColor', [1.0, 1.0, 1.0, 1.0]),
1440+ this.maskZerosEnabled = options.getfieldvalue('maskZerosEnabled', false),
1441+ this.maskZerosTolerance = options.getfieldvalue('maskZerosTolerance', 1e-6),
1442+ this.maskZerosZeroValue = options.getfieldvalue('maskZerosZeroValue', 0.5),
1443+ this.mesh = options.getfieldvalue('mesh', undefined),
1444+ this.name = options.getfieldvalue('name', 'node'),
1445+ this.nanIndices = options.getfieldvalue('nanIndices', new Set()),
1446+ this.octree = options.getfieldvalue('octree', undefined),
1447+ this.pointSize = options.getfieldvalue('pointSize', 15.0),
1448+ this.shaderName = options.getfieldvalue('shaderName', 'Colored'),
1449+ this.shader = options.getfieldvalue('shader', this.gl.shaders[this.shaderName]),
1450+ this.texture = options.getfieldvalue('texture', undefined),
1451+ this.scale = options.getfieldvalue('scale', vec3.fromValues(1, 1, 1)),
1452+ this.rotation = options.getfieldvalue('rotation', vec3.create()),
1453+ this.translation = options.getfieldvalue('translation', vec3.create()),
1454+ this.modelMatrix = options.getfieldvalue('modelMatrix', mat4.create()),
1455+ this.rotationMatrix = options.getfieldvalue('rotationMatrix', mat4.create()),
1456+ this.inverseModelMatrix = options.getfieldvalue('inverseModelMatrix', mat4.create()),
1457+ this.inverseRotationMatrix = options.getfieldvalue('inverseRotationMatrix', mat4.create())
1458+ //}}}
1459+ //initialize {{{
1460+ //if (this.name in canvas.nodes) abort?
1461+ this.updateModelMatrix();
1462+ this.updateDiffuseColor();
1463+ canvas.nodes[this.name] = this;
1464+ //}}}
1465+} //}}}
1466+Node.prototype.updateModelMatrix = function() { //{{{
1467+ //Update the model matrix if rotation, scale, or translation have been manually modified.
1468+ var modelMatrix = mat4.create();
1469+
1470+ var translationMatrix = mat4.create();
1471+ mat4.translate(translationMatrix, translationMatrix, vec3.negate(vec3.create(), this.center)); //scale/rotation centering
1472+ mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
1473+
1474+ var scaleMatrix = mat4.create();
1475+ mat4.scale(scaleMatrix, scaleMatrix, this.scale);
1476+ mat4.multiply(modelMatrix, scaleMatrix, modelMatrix);
1477+
1478+ var rotationMatrix = mat4.create();
1479+ var zRotationMatrix = mat4.create();
1480+ mat4.rotate(zRotationMatrix, zRotationMatrix, DEG2RAD * this.rotation[2], [0.0, 0.0, 1.0]);
1481+ mat4.multiply(rotationMatrix, zRotationMatrix, rotationMatrix);
1482+ var yRotationMatrix = mat4.create();
1483+ mat4.rotate(yRotationMatrix, yRotationMatrix, DEG2RAD * this.rotation[1], [0.0, 1.0, 0.0]);
1484+ mat4.multiply(rotationMatrix, yRotationMatrix, rotationMatrix);
1485+ var xRotationMatrix = mat4.create();
1486+ mat4.rotate(xRotationMatrix, xRotationMatrix, DEG2RAD * this.rotation[0], [1.0, 0.0, 0.0]);
1487+ mat4.multiply(rotationMatrix, xRotationMatrix, rotationMatrix);
1488+ mat4.multiply(modelMatrix, rotationMatrix, modelMatrix);
1489+
1490+ mat4.identity(translationMatrix);
1491+ mat4.translate(translationMatrix, translationMatrix, this.center); //relative translation
1492+ mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
1493+
1494+ mat4.identity(translationMatrix);
1495+ mat4.translate(translationMatrix, translationMatrix, this.translation); //absolute translation
1496+ mat4.multiply(modelMatrix, translationMatrix, modelMatrix);
1497+
1498+ this.modelMatrix = modelMatrix;
1499+ this.inverseModelMatrix = mat4.invert(mat4.create(), modelMatrix);
1500+ this.rotationMatrix = rotationMatrix;
1501+ this.inverseRotationMatrix = mat4.invert(mat4.create(), rotationMatrix);
1502+} //}}}
1503+Node.prototype.updateDiffuseColor = function() { //{{{
1504+ //Update the diffuse color with an RGB color name or vec4 containing r, g, b, and alpha values from 0.0 to 1.0
1505+ var color = this.diffuseColor;
1506+ if (typeof color === 'string') {
1507+ color = new RGBColor(color);
1508+ if (color.ok) color = [color.r/255.0, color.g/255.0, color.b/255.0, 1.0];
1509+ else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ", canvas));
1510+ }
1511+ this.diffuseColor = color;
1512+} //}}}
1513+Node.prototype.transform = function() { //{{{
1514+ //Transforms the translation, rotation, or scle fo the node and updates the model matrix.
1515+ var args = Array.prototype.slice.call(arguments);
1516+ var options = new pairoptions(args.slice(0,args.length));
1517+
1518+ var translation = options.getfieldvalue('translation', undefined);
1519+ var rotation = options.getfieldvalue('rotation', undefined);
1520+ var scale = options.getfieldvalue('scale', undefined);
1521+
1522+ if (!isEmptyOrUndefined(translation)) this.translation = translation;
1523+ if (!isEmptyOrUndefined(rotation)) this.rotation = rotation;
1524+ if (!isEmptyOrUndefined(scale)) this.scale = scale;
1525+ this.updateModelMatrix();
1526+} //}}}
1527+Node.prototype.patch = function() { //{{{
1528+ //Emulates the behavior of MATLAB patch function by constructing a mesh from arguments.
1529+ //Limitations:
1530+ // -Expects pair labeled arguments ('FaceColor','none',...).
1531+ // -Only handles Face/Vertices/FaceVertexCData element/node plots.
1532+ // -Only supports FaceColor 'interp' and 'none'.
1533+
1534+ var args = Array.prototype.slice.call(arguments);
1535+ var options = new pairoptions(args.slice(0,args.length));
1536+
1537+ var faces = options.getfieldvalue('Faces', undefined);
1538+ var vertices = options.getfieldvalue('Vertices', undefined);
1539+ var faceVertexCData = options.getfieldvalue('FaceVertexCData', undefined);
1540+ var faceColor = options.getfieldvalue('FaceColor', 'interp');
1541+ var edgeColor = options.getfieldvalue('EdgeColor', this.diffuseColor);
1542+ var lineWidth = options.getfieldvalue('linewidth', 1);
1543+
1544+ this.faces = faces;
1545+ this.vertices = vertices;
1546+ this.diffuseColor = edgeColor;
1547+ this.updateDiffuseColor();
1548+
1549+ this.patchVertices(faceVertexCData, faces, vertices);
1550+ this.patchCoords(faceVertexCData, faces, vertices);
1551+ this.patchIndices(faces, faceColor);
1552+
1553+ this.mesh = GL.Mesh.load(this.arrays, null, null, this.gl);
1554+ //this.mesh.computeNormals();
1555+ this.computeOctree();
1556+} //}}}
1557+Node.prototype.patchVertices = function(faceVertexCData, faces, vertices) { //{{{
1558+ //Patch subfunction for processing xyz vertices.
1559+ var vertexArray;
1560+ var face;
1561+
1562+ if (isEmptyOrUndefined(faceVertexCData)) {
1563+ vertexArray = new Float32Array(vertices[0].length * 3);
1564+ for(var i = 0, v = 0; i < vertices[0].length; i++) {
1565+ vertexArray[v++] = vertices[0][i];
1566+ vertexArray[v++] = vertices[1][i];
1567+ vertexArray[v++] = vertices[2][i];
1568+ }
1569+ }
1570+ else if (!Array.isArray(faceVertexCData[0])) { //indexed plot - faceVertexCData = [0, 2.32, 231.1, ...]
1571+ if (faceVertexCData.length === faces.length) { //element plot
1572+ vertexArray = new Float32Array(faces.length * 3 * 3);
1573+ for(var i = 0, v = 0, t = 0; i < faces.length; i++) {
1574+ face = [faces[i][0] - 1, faces[i][1] - 1, faces[i][2] - 1];
1575+ for(var j = 0; j < face.length; j++) {
1576+ if (isNaN(faceVertexCData[i])) {
1577+ this.nanIndices.add(i);
1578+ vertexArray[v++] = 0.0;
1579+ vertexArray[v++] = 0.0;
1580+ vertexArray[v++] = 0.0;
1581+ }
1582+ else {
1583+ vertexArray[v++] = vertices[0][face[j]];
1584+ vertexArray[v++] = vertices[1][face[j]];
1585+ vertexArray[v++] = vertices[2][face[j]];
1586+ }
1587+ }
1588+ }
1589+ this.computeIndices = false;
1590+ }
1591+ else if (faceVertexCData.length === vertices[0].length) { //node plot
1592+ vertexArray = new Float32Array(vertices[0].length * 3);
1593+ for(var i = 0, v = 0, t = 0; i < vertices[0].length; i++) {
1594+ if (isNaN(faceVertexCData[i])) {
1595+ this.nanIndices.add(i);
1596+ vertexArray[v++] = 0.0;
1597+ vertexArray[v++] = 0.0;
1598+ vertexArray[v++] = 0.0;
1599+ }
1600+ else {
1601+ vertexArray[v++] = vertices[0][i];
1602+ vertexArray[v++] = vertices[1][i];
1603+ vertexArray[v++] = vertices[2][i];
1604+ }
1605+ }
1606+ }
1607+ }
1608+ else if (Array.isArray(faceVertexCData[0])) { //JS specific: precomputed UV coord plot - faceVertexCData = [[0, 0.99, 0.4, ...],[0, 0.99, 0.4, ...]]
1609+ if (faceVertexCData[0].length === faces.length) { //element plot
1610+ vertexArray = new Float32Array(faces.length * 3 * 3);
1611+ for(var i = 0, v = 0, t = 0; i < faces.length; i++) {
1612+ face = [faces[i][0] - 1, faces[i][1] - 1, faces[i][2] - 1];
1613+ for(var j = 0; j < face.length; j++) {
1614+ vertexArray[v++] = vertices[0][face[j]];
1615+ vertexArray[v++] = vertices[1][face[j]];
1616+ vertexArray[v++] = vertices[2][face[j]];
1617+ }
1618+ }
1619+ this.computeIndices = false;
1620+ }
1621+ else if (faceVertexCData[0].length === vertices[0].length) { //node plot
1622+ vertexArray = new Float32Array(vertices[0].length * 3);
1623+ for(var i = 0, v = 0, t = 0; i < vertices[0].length; i++) {
1624+ vertexArray[v++] = vertices[0][i];
1625+ vertexArray[v++] = vertices[1][i];
1626+ vertexArray[v++] = vertices[2][i];
1627+ }
1628+ }
1629+ }
1630+ this.arrays.vertices = vertexArray;
1631+} //}}}
1632+Node.prototype.patchCoords = function(faceVertexCData, faces, vertices) { //{{{
1633+ //Patch subfunction for processing texture coords/UVs.
1634+ var coordArray;
1635+ var cramge;
1636+ var caxis = this.caxis;
1637+ var face;
1638+
1639+ if (isEmptyOrUndefined(faceVertexCData)) { return; }
1640+
1641+ //Use logarithmic scaling if it is valid
1642+ if (this.log !== false && this.log !== 'off') {
1643+ caxis = [
1644+ Math.log10(caxis[0]) / Math.log10(this.log),
1645+ Math.log10(caxis[1]) / Math.log10(this.log)
1646+ ];
1647+ }
1648+
1649+ if (!Array.isArray(faceVertexCData[0])) { //indexed plot - faceVertexCData = [0, 2.32, 231.1, ...]
1650+ if (faceVertexCData.length === faces.length) { //element plot
1651+ coordArray = new Float32Array(faces.length * 3 * 2);
1652+ crange = caxis[1] - caxis[0];
1653+ for(var i = 0, v = 0, t = 0; i < faces.length; i++) {
1654+ face = [faces[i][0] - 1, faces[i][1] - 1, faces[i][2] - 1];
1655+ for(var j = 0; j < face.length; j++) {
1656+ if (isNaN(faceVertexCData[i])) {
1657+ this.nanIndices.add(i);
1658+ coordArray[t++] = 0.0;
1659+ coordArray[t++] = 0.0;
1660+ }
1661+ else {
1662+ coordArray[t++] = 0.5;
1663+ coordArray[t++] = clamp((faceVertexCData[i] - caxis[0]) / crange, 0.0, 1.0);
1664+ }
1665+ }
1666+ }
1667+ this.computeIndices = false;
1668+ }
1669+ else if (faceVertexCData.length === vertices[0].length) { //node plot
1670+ coordArray = new Float32Array(vertices[0].length * 2);
1671+ crange = caxis[1] - caxis[0];
1672+ for(var i = 0, v = 0, t = 0; i < vertices[0].length; i++) {
1673+ if (isNaN(faceVertexCData[i])) {
1674+ this.nanIndices.add(i);
1675+ coordArray[t++] = 0.0;
1676+ coordArray[t++] = 0.0;
1677+ }
1678+ else {
1679+ coordArray[t++] = 0.5;
1680+ coordArray[t++] = clamp((faceVertexCData[i] - caxis[0]) / crange, 0.0, 1.0);
1681+ }
1682+ }
1683+ }
1684+ }
1685+ else if (Array.isArray(faceVertexCData[0])) { //JS specific: precomputed UV coord plot - faceVertexCData = [[0, 0.99, 0.4, ...],[0, 0.99, 0.4, ...]]
1686+ if (faceVertexCData[0].length === faces.length) { //element plot
1687+ coordArray = new Float32Array(faces.length * 3 * 2);
1688+ for(var i = 0, v = 0, t = 0; i < faces.length; i++) {
1689+ face = [faces[i][0] - 1, faces[i][1] - 1, faces[i][2] - 1];
1690+ for(var j = 0; j < face.length; j++) {
1691+ coordArray[t++] = faceVertexCData[0][i];
1692+ coordArray[t++] = faceVertexCData[1][i];
1693+ }
1694+ }
1695+ this.computeIndices = false;
1696+ }
1697+ else if (faceVertexCData[0].length === vertices[0].length) { //node plot
1698+ coordArray = new Float32Array(vertices[0].length * 2);
1699+ for(var i = 0, v = 0, t = 0; i < vertices[0].length; i++) {
1700+ coordArray[t++] = faceVertexCData[0][i];
1701+ coordArray[t++] = faceVertexCData[1][i];
1702+ }
1703+ }
1704+ }
1705+ this.arrays.coords = coordArray;
1706+} //}}}
1707+Node.prototype.patchIndices = function(faces, faceColor) { //{{{
1708+ //Patch subfunction for processing faces/elements/triangles/indices.
1709+ var indexArray;
1710+ var face;
1711+
1712+ if (faceColor === 'none') { //Check for wireframe mesh rendering
1713+ if (this.drawMode === this.gl.TRIANGLES) { //NOTE: Stopgap to allow gl.LINE_STRIP nodes render normally. Only use case for faceColor === 'none' is for plot_mesh
1714+ this.drawMode = this.gl.LINES;
1715+ }
1716+ }
1717+
1718+ if (this.computeIndices === true && !isEmptyOrUndefined(faces)) {
1719+ if (!isEmptyOrUndefined(faces[0])) { //Check for 2D format and process if needed
1720+ if (faceColor !== 'none') { //Check for triangle rendering
1721+ indexArray = new Uint16Array(faces.length * 3);
1722+ for(var i = 0, f = 0; i < faces.length; i++) {
1723+ face = [faces[i][0] - 1, faces[i][1] - 1, faces[i][2] - 1];
1724+ if (this.nanIndices.has(face[0]) || this.nanIndices.has(face[1]) || this.nanIndices.has(face[2])) continue; //Skip triangle if contains NaN value.
1725+ indexArray[f++] = faces[i][0] - 1;
1726+ indexArray[f++] = faces[i][1] - 1;
1727+ indexArray[f++] = faces[i][2] - 1;
1728+ }
1729+ }
1730+ else { //Check for wireframe mesh rendering
1731+ indexArray = new Uint16Array(faces.length * 6);
1732+ for(var i = 0, f = 0; i < faces.length; i++) {
1733+ indexArray[f++] = faces[i][0] - 1;
1734+ indexArray[f++] = faces[i][1] - 1;
1735+ indexArray[f++] = faces[i][1] - 1;
1736+ indexArray[f++] = faces[i][2] - 1;
1737+ indexArray[f++] = faces[i][2] - 1;
1738+ indexArray[f++] = faces[i][0] - 1;
1739+ }
1740+ }
1741+ }
1742+ else { //Else, assume face indices have already been processed
1743+ indexArray = faces;
1744+ }
1745+ this.arrays.triangles = indexArray;
1746+ }
1747+} //}}}
1748+Node.prototype.updateBuffer = function() { //{{{
1749+ //Updates the mesh buffers provided in place.
1750+ //NOTE: Only support coord buffers currently.
1751+ var args = Array.prototype.slice.call(arguments);
1752+ var options = new pairoptions(args.slice(0,args.length));
1753+
1754+ var coords = options.getfieldvalue('Coords', undefined);
1755+ var cacheIndex = options.getfieldvalue('CacheIndex', false);
1756+
1757+ if (!isEmptyOrUndefined(coords)) {
1758+ this.patchCoords(coords, this.faces, this.vertices);
1759+ var buffer = this.mesh.getBuffer("coords");
1760+ buffer.data = this.arrays.coords;
1761+ buffer.upload(this.gl.DYNAMIC_DRAW);
1762+ }
1763+} //}}}
1764+Node.prototype.computeOctree = function() { //{{{
1765+ //Computes and caches octrees for a node.
1766+ var octree = this.canvas.octrees[this.name];
1767+ if (isEmptyOrUndefined(octree)) {
1768+ octree = new GL.Octree(this.mesh);
1769+ }
1770+ this.canvas.octrees[this.name] = octree;
1771+ this.octree = octree;
1772+} //}}}
1773+Node.prototype.geometryShader = function() { //{{{
1774+ //Emulates OpenGL geometry shaders by rendering each point as a mesh.
1775+ //Parameters:
1776+ // Mesh - the geometry to duplicate for each point.
1777+ // Vertices - the list of points to shade.
1778+ // Indices - (optional) ordered list for non-ordered Vertices objects.
1779+
1780+ var args = Array.prototype.slice.call(arguments);
1781+ var options = new pairoptions(args.slice(0,args.length));
1782+
1783+ var mesh = options.getfieldvalue('Mesh', undefined);
1784+ var vertices = options.getfieldvalue('Vertices', undefined);
1785+ var indices = options.getfieldvalue('Indices', undefined);
1786+
1787+ //For handling key-value object arrays, like xcity
1788+ for (var i = 0; i < vertices.length; i++) {
1789+ if (!Array.isArray(vertices[i])) {
1790+ var array = [];
1791+ var coordinateObject = vertices[i];
1792+ var j = 0;
1793+ if (isEmptyOrUndefined(indices)) {
1794+ for (var key in object) {
1795+ array[j++] = coordinateObject[key];
1796+ }
1797+ }
1798+ else {
1799+ for (var k = 0; k < indices.length; k++) {
1800+ array[j++] = coordinateObject[indices[k]];
1801+ }
1802+ }
1803+ vertices[i] = array;
1804+ }
1805+ }
1806+
1807+ var x = vertices[0];
1808+ var y = vertices[1];
1809+ var z = vertices[2];
1810+ var meshVertices = mesh.getBuffer('vertices').data;
1811+ var meshIndicies = mesh.getIndexBuffer('triangles').data;
1812+ var indicies = new Uint16Array(meshIndicies.length * x.length);
1813+ var size = meshVertices.length * x.length / 3;
1814+ newX = new Float32Array(size);
1815+ newY = new Float32Array(size);
1816+ newZ = new Float32Array(size);
1817+
1818+ //For each vertex in vertices, instantiate mesh geomtry centered around that point.
1819+ for(var i = 0, v = 0, e = 0; i < x.length; i++){
1820+ var vector = [x[i], y[i], z[i]];
1821+ for (var j = 0; j < meshVertices.length;) {
1822+ newX[v] = meshVertices[j++] + x[i];
1823+ newY[v] = meshVertices[j++] + y[i];
1824+ newZ[v++] = meshVertices[j++] + z[i];
1825+ }
1826+ var offset = i * meshVertices.length / 3;
1827+ for (var j = 0; j < meshIndicies.length;) {
1828+ indicies[e++] = meshIndicies[j++] + offset;
1829+ }
1830+ }
1831+
1832+ this.patch('Faces', indicies, 'Vertices', [newX, newY, newZ], 'FaceColor', 'interp');
1833+} //}}}
1834+Node.prototype.scaleVertices = function(md, x, y, z, scale) { //{{{
1835+ //Scales and returns vertices x, y, and z by factor scale. Uses md.geometry.scale for heightscaling in 3d meshes.
1836+ if (md.mesh.classname() === 'mesh3dsurface') {
1837+ var xyz, magnitude;
1838+ x = x.slice();
1839+ y = y.slice();
1840+ z = z.slice();
1841+ for(var i = 0; i < x.length; i++) {
1842+ xyz = vec3.fromValues(x[i], y[i], z[i]);
1843+ magnitude = 1 + md.geometry.surface[i] * scale / vec3.length(xyz);
1844+ vec3.scale(xyz, xyz, magnitude);
1845+ x[i] = xyz[0];
1846+ y[i] = xyz[1];
1847+ z[i] = xyz[2];
1848+ }
1849+ }
1850+ else {
1851+ z = z.slice();
1852+ var zMin = ArrayMin(md.geometry.surface);
1853+ for(var i = 0; i < z.length; i++) {
1854+ z[i] = (z[i] - zMin) * scale + zMin;
1855+ }
1856+ }
1857+ return [x, y, z];
1858+} //}}}
1859+Node.prototype.mergeVertices = function(x1, y1, z1, elements1, x2, y2, z2, elements2) { //{{{
1860+ //Merges and returns two sets of indexed xyz vertices.
1861+ elements2 = elements2.slice();
1862+ for (var i = 0, offset = x1.length; i < elements2.length; i++) {
1863+ elements2[i] = [elements2[i][0] + offset, elements2[i][1] + offset, elements2[i][2] + offset];
1864+ }
1865+ return {x:x1.concat(x2), y:y1.concat(y2), z:z1.concat(z2), elements:elements1.concat(elements2)};
1866+} //}}}
1867+//}}}
1868Index: ../trunk-jpl/src/m/plot/plot_manager.js
1869===================================================================
1870--- ../trunk-jpl/src/m/plot/plot_manager.js (revision 21767)
1871+++ ../trunk-jpl/src/m/plot/plot_manager.js (revision 21768)
1872@@ -13,7 +13,7 @@
1873 var data = options.getfieldvalue('data');
1874 var canvas = initCanvas(options);
1875 var gl = canvas.gl;
1876- //TODO: each plot_ should add their node to the canvas.node array
1877+
1878 //figure out if this is a special plot
1879 if (typeof data === 'string'){
1880
1881@@ -80,9 +80,9 @@
1882 plot_mesh(md,options,canvas);
1883 return;
1884 case 'none':
1885- if (!(options.exist('overlay'))){
1886- plot_none(md,options,nlines,ncols,i);
1887- }
1888+ //if (!(options.exist('overlay'))){
1889+ // plot_none(md,options,nlines,ncols,i);
1890+ //}
1891 return;
1892 case 'penalties':
1893 plot_penalties(md,options,subplotwidth,i);
1894@@ -150,7 +150,7 @@
1895 plot_thermaltransient_results(md,options,subplotwidth,i);
1896 return;
1897 case 'transient_movie':
1898- plot_transient_movie(md,options,subplotwidth,i);
1899+ plot_transient_movie(md,options,canvas);
1900 return;
1901 case 'transient_results':
1902 plot_transient_results(md,options,subplotwidth,i);
1903@@ -204,5 +204,5 @@
1904 //plot unit
1905 plot_unit(md,data2,datatype,options,canvas);
1906
1907- applyoptions(md,data2,datatype,options,canvas,gl,canvas.nodes[canvas.nodes.length-1]);
1908+ applyoptions(md,data2,options,canvas);
1909 } //}}}
1910Index: ../trunk-jpl/src/m/plot/plot_unit.js
1911===================================================================
1912--- ../trunk-jpl/src/m/plot/plot_unit.js (revision 21767)
1913+++ ../trunk-jpl/src/m/plot/plot_unit.js (revision 21768)
1914@@ -6,7 +6,14 @@
1915 //
1916 // See also: PLOTMODEL, PLOT_MANAGER
1917
1918- //{{{ declare variables:
1919+ //if ('unit' in canvas.nodes) {
1920+ // if (
1921+ // canvas.nodes.unit.updateBuffer('Coords', data);
1922+ // return;
1923+ //}
1924+ //else {
1925+
1926+ //{{{ declare variables:
1927 //Process data and model
1928 var meshresults = processmesh(md, data, options);
1929 var x = meshresults[0];
1930@@ -15,78 +22,64 @@
1931 var elements = meshresults[3];
1932 var is2d = meshresults[4];
1933 var isplanet = meshresults[5];
1934+ if (md.mesh.classname() !== 'mesh3dsurface') z = md.geometry.surface;
1935
1936- var vertices = new Float32Array(x.length * 3);
1937- var texcoords = new Float32Array(x.length * 2);
1938- var indices = new Uint16Array(elements.length * 3);
1939- var nanindices = {};
1940- var xmin, xmax;
1941- var ymin, ymax;
1942- var zmin, zmax;
1943- var datamin, datamax, datadelta;
1944- var matrixscale, vertexscale;
1945- //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
1946- if (!md.geometry.surface) {
1947- md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
1948+ //Compute coordinates and data range:
1949+ var xlim = options.getfieldvalue('xlim', [ArrayMin(x), ArrayMax(x)]);
1950+ var ylim = options.getfieldvalue('ylim', [ArrayMin(y), ArrayMax(y)]);
1951+ var zlim = options.getfieldvalue('zlim', [ArrayMin(z), ArrayMax(z)]);
1952+
1953+ //Handle heightscale
1954+ var vertices, scale;
1955+ if (md.mesh.classname() !== 'mesh3dsurface') {
1956+ vertices = [x, y, z];
1957+ scale = [1, 1, options.getfieldvalue('heightscale', 1)];
1958 }
1959- if (md.mesh.classname() == 'mesh3dsurface') {
1960- matrixscale = 1;
1961- vertexscale = options.getfieldvalue('heightscale', 1);
1962- }
1963 else {
1964- if (md.geometry.surface) {
1965- z=md.geometry.surface;
1966- }
1967- matrixscale = options.getfieldvalue('heightscale', 1);
1968- vertexscale = 0;
1969+ vertices = Node.prototype.scaleVertices(md, x, y, z, options.getfieldvalue('heightscale', 1));
1970+ scale = [1, 1, 1];
1971 }
1972- //}}}
1973-
1974- //Compute coordinates and data range:
1975- var modelxlim = [ArrayMin(x), ArrayMax(x)];
1976- var modelylim = [ArrayMin(y), ArrayMax(y)];
1977- var modelzlim = [ArrayMin(z), ArrayMax(z)];
1978- var xlim = options.getfieldvalue('xlim', modelxlim);
1979- var ylim = options.getfieldvalue('ylim', modelylim);
1980- var zlim = options.getfieldvalue('zlim', modelzlim);
1981- xmin = xlim[0];
1982- xmax = xlim[1];
1983- ymin = ylim[0];
1984- ymax = ylim[1];
1985- zmin = zlim[0];
1986- zmax = zlim[1];
1987- var caxis;
1988-
1989+
1990 //Compute gl variables:
1991- var gl = canvas.gl;
1992- var node = Node(gl);
1993- canvas.nodes[canvas.nodes.length] = node;
1994+ var edgecolor = options.getfieldvalue('edgecolor', 'black');
1995+ var maskzeros = options.getfieldvalue('maskzeros', {});
1996+ var node = new Node(
1997+ 'canvas', canvas,
1998+ 'options', options,
1999+ 'name', 'unit',
2000+ 'shaderName', 'Textured',
2001+ 'alpha', options.getfieldvalue('alpha', 1.0),
2002+ 'caxis', options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]),
2003+ //'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, md.mesh.classname() === 'mesh3dsurface' ? (zlim[0] + zlim[1]) / 2 : zlim[0]],
2004+ 'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2],
2005+ 'diffuseColor', edgecolor,
2006+ 'enabled', options.getfieldvalue('nodata','off') == 'off',
2007+ 'log', options.getfieldvalue('log',false),
2008+ 'maskEnabled', options.getfieldvalue('innermask','off') == 'on',
2009+ 'maskHeight', options.getfieldvalue('innermaskheight', 150.0) / options.getfieldvalue('heightscale', 1),
2010+ 'maskColor', options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]),
2011+ 'maskZerosColor', defaultFor(maskzeros.color,[1.0, 1.0, 1.0, 1.0]),
2012+ 'maskZerosEnabled', defaultFor(maskzeros.enabled,false),
2013+ 'maskZerosTolerance', defaultFor(maskzeros.tolerance,1e-3),
2014+ 'maskZerosZeroValue', defaultFor(maskzeros.zeroValue,0.5),
2015+ 'rotation', [-90, 0, 0],
2016+ 'scale', scale
2017+ );
2018+ //}
2019 canvas.unitNode = node;
2020 canvas.unitData = data;
2021- node.name = "unit";
2022- node.shaderName = "Textured";
2023- node.shader = gl.shaders[node.shaderName];
2024- node.scale = [1, 1, matrixscale];
2025- node.rotation = [-90, 0, 0];
2026- node.translation = [0, 0, 0];
2027- node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
2028- node.alpha = options.getfieldvalue('alpha', 1.0);
2029- node.drawOrder = 1;
2030- node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
2031- node.maskHeight = options.getfieldvalue('innermaskheight', 150.0);
2032- node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
2033- node.enabled = options.getfieldvalue('nodata','off') == 'off';
2034- updateModelMatrix(node);
2035-
2036+ //}}}
2037 switch(datatype){
2038 //{{{ element plot
2039 case 1:
2040+ //WARNING: NaN are not properly found (NaN != NaN = true)
2041 pos=ArrayFindNot(data, NaN); //needed for element on water
2042 if (elements[0].length==6){ //prisms
2043 }
2044 else if (elements[0].length==4){ //tetras
2045 }
2046- else{ //2D triangular elements
2047+ else{ //triangular elements
2048+ node.patch('Faces', elements, 'Vertices', vertices, 'FaceVertexCData', data, 'FaceColor', 'flat', 'EdgeColor', edgecolor);
2049 }
2050 break;
2051 //}}}
2052@@ -97,55 +90,8 @@
2053 else if (elements[0].length==4){ //tetras
2054 }
2055 else{ //triangular elements
2056- caxis = options.getfieldvalue('caxis',[ArrayMin(data), ArrayMax(data)]);
2057- 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))];
2058- datamin = caxis[0];
2059- datamax = caxis[1];
2060- datadelta = datamax - datamin;
2061-
2062- var xyz = vec3.create();
2063- var direction = vec3.create();
2064- var vertex = vec3.create();
2065- var magnitude;
2066-
2067- for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
2068- //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
2069- if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i])) {
2070- nanindices[i] = i;
2071- vertices[vindex++] = vertex[0];
2072- vertices[vindex++] = vertex[1];
2073- vertices[vindex++] = vertex[2];
2074-
2075- texcoords[tindex++] = 0.5;
2076- texcoords[tindex++] = 0.0;
2077- continue;
2078- }
2079-
2080- //Scale vertices
2081- xyz = vec3.fromValues(x[i], y[i], z[i]);
2082- magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
2083- vec3.normalize(direction, xyz);
2084- vec3.scale(vertex, direction, magnitude);
2085- vertices[vindex++] = vertex[0];
2086- vertices[vindex++] = vertex[1];
2087- vertices[vindex++] = vertex[2];
2088-
2089- texcoords[tindex++] = 0.5;
2090- texcoords[tindex++] = clamp((data[i] - datamin) / datadelta, 0.0, 1.0);
2091- }
2092-
2093- //linearize the elements array:
2094- var element;
2095- for(var i = 0, iindex = 0; i < elements.length; i++){
2096- element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
2097- if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
2098- indices[iindex++] = element[0];
2099- indices[iindex++] = element[1];
2100- indices[iindex++] = element[2];
2101- }
2102+ node.patch('Faces', elements, 'Vertices', vertices, 'FaceVertexCData', data, 'FaceColor', 'interp', 'EdgeColor', edgecolor);
2103 }
2104- node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords, triangles: indices}, null, null, gl);
2105- node.mesh.octree = new GL.Octree(node.mesh);
2106 break;
2107 //}}}
2108 //{{{ quiver plot
2109@@ -158,135 +104,7 @@
2110 }
2111 break;
2112 //}}}
2113- //{{{ node transient plot
2114- case 5:
2115- if (elements[0].length==6){ //prisms
2116- }
2117- else if (elements[0].length==4){//tetras
2118- }
2119- else{ //triangular elements
2120- var xyz = vec3.create();
2121- var direction = vec3.create();
2122- var vertex = vec3.create();
2123- var magnitude;
2124- var timestamps = data[data.length-1];
2125- for(var i = 0, vindex = 0, tindex = 0; i < x.length; i++){
2126- //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
2127- if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i]) || isNaN(data[i][0])) {
2128- nanindices[i] = i;
2129- }
2130- else {
2131- //Scale vertices
2132- xyz = vec3.fromValues(x[i], y[i], z[i]);
2133- magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
2134- vec3.normalize(direction, xyz);
2135- vec3.scale(vertex, direction, magnitude);
2136- }
2137- vertices[vindex++] = vertex[0];
2138- vertices[vindex++] = vertex[1];
2139- vertices[vindex++] = vertex[2];
2140- }
2141- //Transpose data to obtain column addressable data matrix
2142- data = data[0].map(function(col, i) {
2143- return data.map(function(row) {
2144- return row[i]
2145- })
2146- });
2147- //Prevent evaluation of datasubarray min/max if caxis exists
2148- if (options.exist('caxis')) caxis = options.getfieldvalue('caxis');
2149- else caxis = [ArrayMin(data[0]), ArrayMax(data[0].slice(0,-1))];
2150- 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))];
2151- //Prepare texcoords to hold array of data values
2152- texcoords = [];
2153- for(var i = 0; i < data.length; i++){
2154- datamin = caxis[0];
2155- datamax = caxis[1];
2156- datadelta = datamax - datamin;
2157- //Precalculate arrays for each datasubarray, trimming off timestamp value by using x.length instead of data[i].length
2158- texcoords[i] = new Float32Array(x.length * 2);
2159- for(var j = 0, index = 0; j < x.length; j++){
2160- texcoords[i][index++] = 0.5;
2161- texcoords[i][index++] = clamp((data[i][j] - datamin) / datadelta, 0.0, 1.0);
2162- }
2163- }
2164-
2165- //linearize the elements array:
2166- var element;
2167- for(var i = 0, iindex = 0; i < elements.length; i++){
2168- element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
2169- if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
2170- indices[iindex++] = element[0];
2171- indices[iindex++] = element[1];
2172- indices[iindex++] = element[2];
2173- }
2174-
2175- //Initialize movie loop
2176- node.movieLoop = canvas.animation.loop;
2177- node.movieInterval = 1000 / canvas.animation.fps;
2178- node.movieTimestamps = timestamps;
2179- node.movieLength = timestamps.length;
2180- node.movieFrame = 0;
2181- canvas.dataMarkers.values = [];
2182- var quiverVelFrames = {};
2183- for(var i=0; i < md.results.length; i++){
2184- quiverVelFrames[Math.floor(md.results[i].time)] = md.results[i];
2185- }
2186-
2187- if (canvas.animation.handler !== 0) {
2188- console.log("clearing...");
2189- clearInterval(canvas.animation.handler)
2190- }
2191- //TODO: Move this into webgl.js
2192- canvas.animation.handler = setInterval(function () {
2193- node.movieFrame = canvas.animation.frame;
2194- if (canvas.animation.play && canvas.animation.increment) {
2195- if (node.movieFrame == node.movieLength - 1) {
2196- if (node.movieLoop) {
2197- node.movieFrame = 0;
2198- }
2199- else {
2200- toggleMoviePlay(canvas);
2201- }
2202- }
2203- else {
2204- node.movieFrame = node.movieFrame + 1;
2205- }
2206- if (canvas.animation.lastFrame != canvas.animation.frame) {
2207- updateMarker(canvas, false);
2208- }
2209- }
2210-
2211- if (canvas.progressBar) {
2212- canvas.progressBar.val(node.movieFrame);
2213- canvas.progressBar.slider('refresh');
2214- }
2215- if (canvas.timeLabel) { canvas.timeLabel.html(node.movieTimestamps[node.movieFrame].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr")); }
2216-
2217- var buffer = node.mesh.getBuffer("coords");
2218- buffer.data = texcoords[node.movieFrame];
2219- buffer.upload(canvas.gl.DYNAMIC_DRAW);
2220- node.mesh.octree = new GL.Octree(node.mesh);
2221- node.texcoords = texcoords;
2222- if(options.getfieldvalue('quiver') == 'on'){
2223- plot_quiver(md, options, canvas, {vel: quiverVelFrames[node.movieFrame].Vel, vx: quiverVelFrames[node.movieFrame].Vx, vy: quiverVelFrames[node.movieFrame].Vy});
2224- }
2225- canvas.animation.lastFrame = canvas.animation.frame;
2226- canvas.animation.frame = node.movieFrame;
2227- }, node.movieInterval);
2228-
2229- if (canvas.progressBar) {
2230- canvas.animation.frame = 0;
2231- canvas.progressBar.val(0);
2232- canvas.progressBar.attr('max', node.movieLength-1);
2233- canvas.progressBar.slider('refresh');
2234- }
2235-
2236- }
2237- node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords[0], triangles: indices}, null, null, gl);
2238- node.mesh.octree = new GL.Octree(node.mesh);
2239- break;
2240- //}}}
2241 default:
2242- throw Error(sprintf("%s%i%s\n",'case ', datatype,' not supported'));
2243+ throw Error(sprintf('%s%i%s\n','case ', datatype,' not supported'));
2244 }
2245 } //}}}
2246Index: ../trunk-jpl/src/m/plot/plot_overlay.js
2247===================================================================
2248--- ../trunk-jpl/src/m/plot/plot_overlay.js (revision 21767)
2249+++ ../trunk-jpl/src/m/plot/plot_overlay.js (revision 21768)
2250@@ -6,18 +6,11 @@
2251 //
2252 // See also: PLOTMODEL, PLOT_MANAGER
2253
2254+ if ('overlay' in canvas.nodes && options.getfieldvalue('cachenodes','on') === 'on') return;
2255+
2256 //{{{ declare variables:
2257- var vertices = [];
2258- var indices = [];
2259- var texcoords = [];
2260- var nanindices = {};
2261- var xmin, xmax;
2262- var ymin, ymax;
2263- var zmin, zmax;
2264- var matrixscale, vertexscale;
2265-
2266 //Process data and model
2267- var meshresults = processmesh(md, data, options);
2268+ var meshresults = processmesh(md, [], options);
2269 var x = meshresults[0];
2270 var y = meshresults[1];
2271 var z = meshresults[2];
2272@@ -24,139 +17,74 @@
2273 var elements = meshresults[3];
2274 var is2d = meshresults[4];
2275 var isplanet = meshresults[5];
2276-
2277- //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
2278- if (!md.geometry.surface) {
2279- md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
2280+ if (md.mesh.classname() !== 'mesh3dsurface') z = md.geometry.surface;
2281+
2282+ //Compute coordinates and data range:
2283+ var xlim = options.getfieldvalue('xlim', [ArrayMin(x), ArrayMax(x)]);
2284+ var ylim = options.getfieldvalue('ylim', [ArrayMin(y), ArrayMax(y)]);
2285+ var zlim = options.getfieldvalue('zlim', [ArrayMin(md.geometry.surface), ArrayMax(md.geometry.surface)]);
2286+
2287+ //Handle radaroverlay
2288+ if (md.radaroverlay.outerx) {
2289+ var result = Node.prototype.mergeVertices(x, y, z, elements, md.radaroverlay.outerx, md.radaroverlay.outery, md.radaroverlay.outerheight, md.radaroverlay.outerindex);
2290+ x = result.x;
2291+ y = result.y;
2292+ z = result.z;
2293+ elements = result.elements;
2294 }
2295- if (md.mesh.classname() == 'mesh3dsurface') {
2296- matrixscale = 1;
2297- vertexscale = options.getfieldvalue('heightscale', 1);
2298+
2299+ //Handle heightscale
2300+ var vertices, scale;
2301+ if (md.mesh.classname() !== 'mesh3dsurface') {
2302+ vertices = [x, y, z];
2303+ scale = [1, 1, options.getfieldvalue('heightscale', 1)];
2304 }
2305 else {
2306- if (md.geometry.surface) {
2307- z=md.geometry.surface;
2308- }
2309- matrixscale = options.getfieldvalue('heightscale', 1);
2310- vertexscale = 0;
2311+ vertices = Node.prototype.scaleVertices(md, x, y, z, options.getfieldvalue('heightscale', 1));
2312+ scale = [1, 1, 1];
2313 }
2314- //}}}
2315
2316- //Compute coordinates and data range:
2317- var modelxlim = [ArrayMin(x), ArrayMax(x)];
2318- var modelylim = [ArrayMin(y), ArrayMax(y)];
2319- var modelzlim = [ArrayMin(z), ArrayMax(z)];
2320- var xlim = options.getfieldvalue('xlim', modelxlim);
2321- var ylim = options.getfieldvalue('ylim', modelylim);
2322- var zlim = options.getfieldvalue('zlim', modelzlim);
2323- xmin = xlim[0];
2324- xmax = xlim[1];
2325- ymin = ylim[0];
2326- ymax = ylim[1];
2327- zmin = zlim[0];
2328- zmax = zlim[1];
2329-
2330 //Compute gl variables:
2331- var gl = canvas.gl;
2332- var node = Node(gl);
2333- canvas.nodes[canvas.nodes.length] = node;
2334- node.name = "overlay";
2335- node.shaderName = (options.getfieldvalue('render',[]).indexOf('ground')!=-1) ? "GroundFromSpace" : "Textured";
2336- node.shader = gl.shaders[node.shaderName];
2337- node.scale = [1, 1, matrixscale];
2338- node.rotation = [-90, 0, 0];
2339- node.translation = [0, 0, 0];
2340- node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
2341- node.texture = initTexture(gl, options.getfieldvalue('overlay_image'));
2342- node.alpha = options.getfieldvalue('outeralpha', 1.0);
2343- node.drawOrder = 1;
2344- node.maskEnabled = options.getfieldvalue('outermask','off') == 'on';
2345- node.maskHeight = options.getfieldvalue('outermaskheight', 150.0);
2346- node.maskColor = options.getfieldvalue('outermaskcolor',[0.0, 0.0, 1.0, 1.0]);
2347- updateModelMatrix(node);
2348+ var edgecolor = options.getfieldvalue('edgecolor', 'black');
2349+ var texture = initTexture(gl, options.getfieldvalue('overlay_image'));
2350+ var node = new Node(
2351+ 'canvas', canvas,
2352+ 'options', options,
2353+ 'name', 'overlay',
2354+ 'shaderName', 'ground' in options.getfieldvalue('render', {}) ? 'GroundFromSpace' : 'Textured',
2355+ 'alpha', options.getfieldvalue('outeralpha', 1.0),
2356+ //'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, md.mesh.classname() === 'mesh3dsurface' ? (zlim[0] + zlim[1]) / 2 : zlim[0]],
2357+ 'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2],
2358+ 'diffuseColor', 'white',
2359+ 'maskEnabled', options.getfieldvalue('outermask','off') == 'on',
2360+ 'maskHeight', options.getfieldvalue('outermaskheight', 150.0),
2361+ 'maskColor', options.getfieldvalue('outermaskcolor',[0.0, 0.0, 1.0, 1.0]),
2362+ 'texture', texture,
2363+ 'rotation', [-90, 0, 0],
2364+ 'scale', scale
2365+ );
2366+ //}}}
2367
2368- //Handle outer radaroverlay
2369- if (md.radaroverlay.outerx) {
2370- var newelements = [];
2371- for (var i = 0; i < md.radaroverlay.outerindex.length; i++) {
2372- newelements[newelements.length] = [md.radaroverlay.outerindex[i][0] + x.length, md.radaroverlay.outerindex[i][1] + y.length, md.radaroverlay.outerindex[i][2] + z.length];
2373- }
2374- x = [].concat(x, md.radaroverlay.outerx);
2375- y = [].concat(y, md.radaroverlay.outery);
2376- z = [].concat(z, md.radaroverlay.outerheight);
2377- elements = [].concat(elements, newelements);
2378-
2379- //Reclaculate bounds based on otuer radaroverlay
2380- modelxlim = [ArrayMin(x), ArrayMax(x)];
2381- modelylim = [ArrayMin(y), ArrayMax(y)];
2382- modelzlim = [ArrayMin(z), ArrayMax(z)];
2383- xmin = xlim[0];
2384- xmax = xlim[1];
2385- ymin = ylim[0];
2386- ymax = ylim[1];
2387- zmin = zlim[0];
2388- zmax = zlim[1];
2389-
2390- node.center = [node.center[0], node.center[1], -zmax];
2391- }
2392+ var xRange = xlim[1] - xlim[0];
2393+ var yRange = ylim[1] - ylim[0];
2394+ var coordArray = [new Array(x.length), new Array(x.length)];
2395+ //generate mesh:
2396
2397- var xrange = modelxlim[1] - modelxlim[0];
2398- var yrange = modelylim[1] - modelylim[0];
2399-
2400- var xyz = vec3.create();
2401- var direction = vec3.create();
2402- var vertex = vec3.create();
2403- var magnitude;
2404-
2405- //generate mesh:
2406- for(var i = 0; i < x.length; i++){
2407- //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
2408- if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i])) {
2409- nanindices[i] = i;
2410- vertices[vertices.length] = vertex[0];
2411- vertices[vertices.length] = vertex[1];
2412- vertices[vertices.length] = vertex[2];
2413-
2414- texcoords[texcoords.length] = 0.0;
2415- texcoords[texcoords.length] = 0.0;
2416- continue;
2417- }
2418-
2419- if (md.mesh.classname() == 'mesh3dsurface') {
2420- //Scale vertices
2421- xyz = vec3.fromValues(x[i], y[i], z[i]);
2422- magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
2423- vec3.normalize(direction, xyz);
2424- vec3.scale(vertex, direction, magnitude);
2425- vertices[vertices.length] = vertex[0];
2426- vertices[vertices.length] = vertex[1];
2427- vertices[vertices.length] = vertex[2];
2428-
2429- texcoords[texcoords.length] = Math.atan2(vertex[1], vertex[0]) / (2 * Math.PI) + 0.5;
2430- texcoords[texcoords.length] = Math.asin(vertex[2] / magnitude) / Math.PI + 0.5;
2431- }
2432- else {
2433- //Scale vertices
2434- xyz = vec3.fromValues(x[i], y[i], z[i]);
2435+ if (md.mesh.classname() == 'mesh3dsurface') {
2436+ var xyz, magnitude;
2437+ for(var i = 0; i < x.length; i++){
2438+ xyz = vec3.fromValues(vertices[0][i], vertices[1][i], vertices[2][i]);
2439 magnitude = vec3.length(xyz);
2440- vec3.normalize(direction, xyz);
2441- vec3.scale(vertex, direction, magnitude);
2442- vertices[vertices.length] = vertex[0];
2443- vertices[vertices.length] = vertex[1];
2444- vertices[vertices.length] = vertex[2];
2445-
2446- texcoords[texcoords.length] = (x[i] - modelxlim[0]) / xrange;
2447- texcoords[texcoords.length] = (y[i] - modelylim[0]) / yrange;
2448+
2449+ coordArray[0][i] = Math.atan2(xyz[1], xyz[0]) / (2 * Math.PI) + 0.5;
2450+ coordArray[1][i] = Math.asin(xyz[2] / magnitude) / Math.PI + 0.5;
2451 }
2452 }
2453- //linearize the elements array:
2454- var element;
2455- for(var i = 0; i < elements.length; i++){
2456- element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
2457- if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
2458- indices[indices.length] = element[0];
2459- indices[indices.length] = element[1];
2460- indices[indices.length] = element[2];
2461+ else {
2462+ for(var i = 0; i < x.length; i++){
2463+ coordArray[0][i] = (vertices[0][i] - xlim[0]) / xRange;
2464+ coordArray[1][i] = (vertices[1][i] - ylim[0]) / yRange;
2465+ }
2466 }
2467- node.mesh = GL.Mesh.load({vertices: vertices, coords: texcoords, triangles: indices}, null, null, gl);
2468+ node.patch('Faces', elements, 'Vertices', vertices, 'FaceVertexCData', coordArray, 'FaceColor', 'interp', 'EdgeColor', edgecolor);
2469 } //}}}
2470Index: ../trunk-jpl/src/m/plot/plot_transient_movie.js
2471===================================================================
2472--- ../trunk-jpl/src/m/plot/plot_transient_movie.js (nonexistent)
2473+++ ../trunk-jpl/src/m/plot/plot_transient_movie.js (revision 21768)
2474@@ -0,0 +1,100 @@
2475+function plot_transient_movie(md, options, canvas) { //{{{
2476+ //PLOT_TRANSIENT_MOVIE - plot a transient result as a movie
2477+ //
2478+ // Usage:
2479+ // plot_transient_movie(md, options, canvas);
2480+ //
2481+ // See also: PLOTMODEL, PLOT_MANAGER
2482+
2483+ //loop over the time steps
2484+ var data = options.getfieldvalue('transient_field_data');
2485+ var datatype;
2486+ var steps = new Array(data.length);
2487+ for (i = 0; i < steps.length; i++) {
2488+ steps[i] = i;
2489+ }
2490+
2491+ //calculate caxis
2492+ if (!options.exist('caxis')) {
2493+ var range = [Infinity, -Infinity];
2494+ var dataresults;
2495+ for (var i in steps) {
2496+ dataresults = processdata(md, data[i], options);
2497+ range[1] = Math.min(range[0], ArrayMin(dataresults[0]));
2498+ range[2] = Math.max(range[1], ArrayMax(dataresults[0]));
2499+ }
2500+ datatype = dataresults[1];
2501+ options.addfielddefault('caxis', range);
2502+
2503+ }
2504+
2505+ //Create unit node if it does not already exist
2506+ if (!('unit' in canvas.nodes)) {
2507+ var dataresults = processdata(md, data[i],options);
2508+ var data2 = dataresults[0];
2509+ var datatype = dataresults[1];
2510+
2511+ //plot unit
2512+ plot_unit(md,data2,datatype,options,canvas);
2513+ }
2514+
2515+ //display movie
2516+ var node = canvas.nodes.unit;
2517+ node.options = options;
2518+ node.alpha = options.getfieldvalue('alpha', 1.0);
2519+ node.caxis = options.getfieldvalue('caxis');
2520+ node.enabled = options.getfieldvalue('nodata', 'off') == 'off';
2521+ node.log = options.getfieldvalue('log', false);
2522+ canvas.unitMovieData = data;
2523+ canvas.animation.frame = 0;
2524+ canvas.animation.handler = setInterval(function () {
2525+ //Update current animation frame
2526+ var frame = canvas.animation.frame;
2527+ if (canvas.animation.play) {
2528+ if (canvas.animation.increment) {
2529+ if (frame > steps.length - 1) {
2530+ if (node.movieLoop) {
2531+ frame = 0;
2532+ }
2533+ else {
2534+ toggleMoviePlay(canvas);
2535+ }
2536+ }
2537+ else {
2538+ frame = (frame + 1) % steps.length;
2539+ }
2540+ }
2541+ }
2542+
2543+ //If frame has changed, update unit node and data marker display.
2544+ if (frame !== canvas.animation.lastFrame) {
2545+ node.updateBuffer('Coords', data[frame]);
2546+ canvas.unitData = data[frame];
2547+ if (canvas.dataMarkers.enabled) {
2548+ updateMarker(canvas, false);
2549+ }
2550+ if (canvas.progressBar) {
2551+ canvas.progressBar.val(frame).slider('refresh');
2552+ }
2553+ if (canvas.timeLabel) {
2554+ canvas.timeLabel.html(steps[frame].toFixed(0) + " " + options.getfieldvalue("movietimeunit","yr"));
2555+ }
2556+ if (!isEmptyOrUndefined(canvas.nodes.quiver)) {
2557+ plot_quiver(md,options,canvas,false);
2558+ }
2559+ }
2560+
2561+ //Save new frame info.
2562+ canvas.animation.lastFrame = canvas.animation.frame;
2563+ canvas.animation.frame = frame;
2564+ }, canvas.animation.interval);
2565+
2566+ //Update progress bar with new frame info.
2567+ if (canvas.progressBar) {
2568+ canvas.progressBar.val(canvas.animation.frame);
2569+ canvas.progressBar.attr('max', steps.length - 1);
2570+ canvas.progressBar.slider('refresh');
2571+ }
2572+
2573+ applyoptions(md, [], options, canvas);
2574+} //}}}
2575Index: ../trunk-jpl/src/m/plot/plotdoc.js
2576===================================================================
2577--- ../trunk-jpl/src/m/plot/plotdoc.js (revision 21767)
2578+++ ../trunk-jpl/src/m/plot/plotdoc.js (revision 21768)
2579@@ -4,20 +4,25 @@
2580 // Usage:
2581 // plotdoc()
2582
2583- //TODO: Standardize image to overlay_image, heightscale to scaling, colorbarfontsize/color, clarify innermask/outermask, edgecolor implementation, check colormap,
2584+ //TODO: rename innermask/outermask/maskzero and combine
2585
2586- console.log(' WARNING: starred methods (*) are experimental and not guarenteed to be stable');
2587 console.log(' Plot usage: plotmodel(model,varargin)');
2588 console.log(' Options: ');
2589 console.log(' "canvasid": canvas id');
2590 console.log(' "data" : what we want to plot');
2591- console.log(' Available values for "data" are: ');
2592- console.log(' - any field of the model structure. ex: plot(md,"data","vel"), or plot(md,"data",md.initialization.vel)');
2593- console.log(' - "mesh": draw mesh using trisurf');
2594- console.log(' - "quiver": quiver plot');
2595- console.log(' "2d": renders orthographic camera with view set to [0, 90] (default "off", ex: "on", "off")');
2596+ console.log(' Available values for "data" are: ');
2597+ console.log(' - any field of the model structure. ex: plot(md,"data","vel"), or plot(md,"data",md.initialization.vel)');
2598+ console.log(' - "mesh": draw mesh using trisurf');
2599+ console.log(' - "quiver": quiver plot');
2600 console.log(' "backgroundcolor": plot background color. (default "lightcyan", ex: "green","blue")');
2601 console.log(' "brush": specify brush options (default {"strength":0.075,"falloff":0.5})');
2602+ console.log(' "enabled": toggle brush (default false, ex: true)');
2603+ console.log(' "strength": value that brush will change data points by (ex: 0.075)');
2604+ console.log(' "falloff": multiplier that brush will decrease strength by for each successive point away from brush center (ex: 0.5)');
2605+ console.log(' "clouds": specify brush options (default {"strength":0.075,"falloff":0.5})');
2606+ console.log(' "enabled": toggle clouds (default false, ex: true)');
2607+ console.log(' "height": height to spawn clouds at (ex: 7500)');
2608+ console.log(' "quantity": quantity of clouds to spawn (ex: 10)');
2609 console.log(' "caxis": modify colorbar range. (array of type [a, b] where b>=a)');
2610 console.log(' "colorbar": add colorbar (default "off", ex: "on", "off")');
2611 console.log(' "colorbarid": colorbar canvas id (string)');
2612@@ -31,18 +36,26 @@
2613 console.log(' "colorbarheight": multiplier (default 1) to the default height colorbar');
2614 console.log(' "colormap": same as standard matlab option (default "jet", ex: "hsv","cool","spring","gray","Ala","Rignot",...)');
2615 console.log(' "controlsensitivity": sensitivty of view/zoom changes as a percentage of default (default 1, ex: 0.5, 2.75)');
2616- console.log(' "datamarkers": toggle data marker displays (default "on", ex: "on", "off")');
2617- console.log(' "datamarkers_image": toggle data marker displays (default "on", ex: "on", "off")');
2618- console.log(' "datamarkerssize": specifiy the width and height of the data markers (default [32,32], ex: [24,32], [105,10])');
2619- console.log(' "datamarkersoptions": specifiy options for data markers (default {"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"]}');
2620+ console.log(' "datamarkers": object cotaining data marker parameters. See webgl.js for defaults. (ex: {"enabled":true,"format":["<div id="sim-plot"></div>"],"labels":["thickness","velocity","value"],"animated":true})');
2621+ console.log(' "enabled": toggle data marker displays (default true, ex: false)');
2622+ console.log(' "image": image used for marking the clicked point (ex: "/textures/data_marker.svg")');
2623+ console.log(' "labels": when displaying a sim-plot graph, display these model fields. (ex: ["thickness","velocity","value"])');
2624+ console.log(' "font": font to be used for display (ex: "24px "Comic Sans MS", cursive")');
2625+ console.log(' "format": an array compatible with sprintf that will be displayed as html (ex: ["X: %.2e<br>Y: %.2e<br>Z: %.2e]<br>Value: %0.1f","x","y","z","value"])');
2626+ console.log(' "size": specifiy the width and height of the data markers (default [32,32], ex: [24,32], [105,10])');
2627 console.log(' "displayview": print view value to console (default "off", ex: "on", "off")');
2628 console.log(' "displayzoom": print zoom value to console (default "off", ex: "on", "off")');
2629 console.log(' "edgecolor": same as standard matlab option EdgeColor (default "black", ex: color name: "blue" or RGB array: [0.5, 0.2, 0.8])');
2630 console.log(' "heightscale": scaling factor to accentuate height. (default 1, ex: 0.5, 100)');
2631- console.log(' "linewidth*": line width for mesh, quiver, and contour plots, currently limited by WebGL to 1. (default 1, ex: 2, 5)');
2632+ console.log(' "linewidth": line width for mesh, quiver, and contour plots, currently limited by WebGL to 1. (default 1, ex: 2, 5)');
2633 console.log(' "log": value of log (default 10, ex: 2, Math.E)');
2634 console.log(' "mask": list of flags of size numberofnodes or numberofelements. Only "true" values are plotted ');
2635- console.log(' "movieoptions": specify movie options (default {"fps":4,"loop":true})');
2636+ console.log(' "movies": object cotaining transient plot animation options (ex: {"fps":4,"loop":true})');
2637+ console.log(' "maskzeros": object cotaining transient plot animation options (ex: "enabled":true,"color":[1.0, 1.0, 1.0, 1.0],"tolerance":1e-3,"zeroValue":0.5})');
2638+ console.log(' "enabled": toggle maskzeros (default false, ex: true)');
2639+ console.log(' "color": RGBA color value array with ranges 0.0 to 1.0 (ex: [1.0, 1.0, 1.0, 1.0])');
2640+ console.log(' "tolerance": values within this tolerance of the zeroValue will be masked. (default: 1e-3, ex: 2.5e-2)');
2641+ console.log(' "zeroValue": the percentage value with range 0.0, to 1.0 of the caxis value around which the data will be masked with the color. (default: 0.5, ex: 0, 1.0, 0.75)');
2642 console.log(' "innermask*": Special mask that colors all parts of a data mesh below a height a certain color. provide innermaskheight and innermaskcolor options also (default "off", ex: "on", "off")');
2643 console.log(' "outermask*": Special mask that colors all parts of a overlay mesh below a height a certain color. provide outermaskheight and outermaskcolor options also (default "off", ex: "on", "off")');
2644 console.log(' "overlay": overlay a radar amplitude image behind (default "off", ex: "on", "off")');
2645@@ -50,20 +63,51 @@
2646 console.log(' "quiver": add quiver plot overlay for velocities. (default "off", ex: "on", "off")');
2647 console.log(' "scaling": scaling factor used by quiver plots. Default is 0.4');
2648 console.log(' "alpha": transparency coefficient 0.0 to 1.0, the lower, the more transparent. (default 1.0, ex: 0.5, 0.25)');
2649- console.log(' "azlim": azimuth view limits (ex: [0, 180])');
2650- console.log(' "ellim": elevation view limits (ex: [-90, 90])');
2651- console.log(' "origin": initial camera offset from model center (default [0,0,0.0], ex: [-2, 1.5, 0.01])');
2652- console.log(' "render": toggle sky, ground, and space rendering. (default [], ex: ["sky", "space"], ["ground"])');
2653- console.log(' "viewPanning": enable view origin panning with two-finger touch or shift+mouse drag. (default "off", ex: "on", "off")');
2654- console.log(' "view": initial azimuth and elevation angles for camera (default [0,90], ex: [90, 180]');
2655+ console.log(' "render": a object containing a list of default object to render. (default {}, ex: {"sky", "space"})');
2656+ console.log(' "sky": render the atmosphere. (ex: {"enabled":true})');
2657+ console.log(' "enabled": toggle sky (default false, ex: true)');
2658+ console.log(' "space": render space. (ex: {"enabled":true})');
2659+ console.log(' "enabled": toggle space (default false, ex: true)');
2660+ console.log(' "coastlines": render coastlines. (ex: {"enabled":true})');
2661+ console.log(' "enabled": toggle coastlines (default false, ex: true)');
2662+ console.log(' "scale": scale coastlines factor (default 1.0, ex: 1.004)');
2663+ console.log(' "x": x coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2664+ console.log(' "y": y coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2665+ console.log(' "z": z coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2666+ console.log(' "city": render city. (ex: {"enabled":true})');
2667+ console.log(' "enabled": toggle city (default false, ex: true)');
2668+ console.log(' "size": radius of city sphere, in meters (default 1.0, ex: 150000)');
2669+ console.log(' "color": color of city sphere (ex: "magenta")');
2670+ console.log(' "x": x coordinate of city. (ex: 0.0)');
2671+ console.log(' "y": y coordinate of city. (ex: 0.0)');
2672+ console.log(' "z": z coordinate of city. (ex: 6356700.0)');
2673+ console.log(' "cities": render cities. (ex: {"enabled":true})');
2674+ console.log(' "enabled": toggle cities (default false, ex: true)');
2675+ console.log(' "size": radius of cities spheres, in meters (default 1.0, ex: 80000)');
2676+ console.log(' "color": color of cities spheres (ex: "darkviolet")');
2677+ console.log(' "x": x coordinate array of cities. (ex: [0.0, 10.0, -25.0,...])');
2678+ console.log(' "y": y coordinate array of cities. (ex: [0.0, 10.0, -25.0,...])');
2679+ console.log(' "z": z coordinate array of cities. (ex: [0.0, 10.0, -25.0,...])');
2680+ console.log(' "graticule": render graticule. (ex: {"enabled":true})');
2681+ console.log(' "enabled": toggle graticule (default false, ex: true)');
2682+ console.log(' "scale": scale graticule factor (default 1.0, ex: 1.004)');
2683+ console.log(' "x": x coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2684+ console.log(' "y": y coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2685+ console.log(' "z": z coordinate array. (ex: [0.0, 10.0, -25.0,...])');
2686+ console.log(' "view": object cotaining view parameters. See webgl.js for defaults. (ex: {"position":[0.0,0.0,0.0],"rotation":[0.0,0.0,0.0],"zoom":1.0,"zoomLimits":[0.01,100.0],"azimuthLimits":[-180,180.0],"elevationLimits":[-90,90.0],"panningEnabled":false,"twod":false})');
2687+ console.log(' "position": camera position (ex: [0.0,0.0,0.0])');
2688+ console.log(' "rotation": camera rotation (ex: [0.0,0.0,0.0])');
2689+ console.log(' "zoom": initial camera zoom as a percentage of default (default 1, ex: 1.5, 0.01)');
2690+ console.log(' "zoomLimits": zoom view limits (ex: [0.05, 10])');
2691+ console.log(' "azimuthLimits": zoom view limits (ex: [0.05, 10])');
2692+ console.log(' "elevationLimits": zoom view limits (ex: [0.05, 10])');
2693+ console.log(' "panningEnabled": controls panning with shift + drag mouse or pan gestures (default: false, ex: true)');
2694+ console.log(' "twod": controls twod orthographic view (default: false, ex: true)');
2695 console.log(' "xlim": x coordinates to fit inside camera (ex: [0, 500])');
2696 console.log(' "ylim": y coordinates to fit inside camera (ex: [0, 500])');
2697 console.log(' "zlim": z coordinates to fit inside camera (ex: [0, 500])');
2698- console.log(' "zoomlim": zoom view limits (ex: [0.05, 10])');
2699- console.log(' "zoom": initial camera zoom as a percentage of default (default 1, ex: 1.5, 0.01)');
2700- console.log(' "cloud*": plot a cloud of points, given a flat array of 3d coordinates (ex: [0.0, 0.0, 0.0, 1.0, 1.0, 1.0])');
2701- console.log(' "expdisp*": plot exp file on top of a data plot. provide exp file as an argument (use a cell of strings if more than one)');
2702- console.log(' "textlabels*": plot text labels rendered in 3d space, using an array of text/coordinate pairs (ex: [{"pos":[0.0,0.0,0.0],"text":"origin"}])');
2703+ console.log(' "transient_field_data": array of data objects (ex: [[0.0,1.0, 2.5, 12.0...],[0.0,1.0, 2.5, 12.0...],...])');
2704+ console.log(' "textlabels": plot text labels rendered in 3d space, using an array of text/coordinate pairs (ex: [{"pos":[0.0,0.0,0.0],"text":"origin"}])');
2705
2706 console.log(' ');
2707 console.log(' Examples:');
2708Index: ../trunk-jpl/src/m/plot/processdata.js
2709===================================================================
2710--- ../trunk-jpl/src/m/plot/processdata.js (revision 21767)
2711+++ ../trunk-jpl/src/m/plot/processdata.js (revision 21768)
2712@@ -14,9 +14,6 @@
2713 //
2714 // See also: PLOTMODEL, PROCESSMESH
2715
2716- //variables:
2717- var datatype;
2718-
2719 //check format
2720 if ( data.length ==0 | data === [] | typeof data === 'number' | ArrayAnyNaN(data) ){
2721 throw Error('plotmodel error message: data provided is empty');
2722@@ -34,6 +31,7 @@
2723 }
2724
2725 //needed later on
2726+ var numberofvertices2d, numberofelements2d;
2727 if ('numberofvertices2d' in md.mesh){
2728 numberofvertices2d=md.mesh.numberofvertices2d;
2729 numberofelements2d=md.mesh.numberofelements2d;
2730@@ -44,10 +42,10 @@
2731 }
2732
2733 //initialize datatype
2734- datatype=0;
2735+ var datatype=0;
2736
2737 //get datasize
2738- datasize=data.length;
2739+ var datasize=data.length;
2740
2741 //transpose data if necessary
2742 if (data[0].length > data.length){
2743@@ -63,7 +61,7 @@
2744
2745
2746 //quiver?
2747- if (data[0].length>1){
2748+ if (Array.isArray(data[0])){
2749 datatype=3;
2750
2751 //check number of columns, add zeros if necessary,
2752@@ -114,7 +112,7 @@
2753 }
2754
2755 //element data
2756- if (datasize==md.mesh.numberofelements & data[0].length==1){
2757+ if (datasize==md.mesh.numberofelements & !Array.isArray(data[0])){
2758
2759 //Initialize datatype if non patch
2760 if(datatype!=4 & datatype!=5){
2761@@ -157,7 +155,7 @@
2762 }
2763
2764 //node data
2765- if (datasize==md.mesh.numberofvertices){
2766+ if (datasize==md.mesh.numberofvertices & !Array.isArray(data[0])){
2767 datatype=2;
2768
2769 //Mask?
2770Index: ../trunk-jpl/src/m/plot/plot_quiver.js
2771===================================================================
2772--- ../trunk-jpl/src/m/plot/plot_quiver.js (revision 21767)
2773+++ ../trunk-jpl/src/m/plot/plot_quiver.js (revision 21768)
2774@@ -1,4 +1,4 @@
2775-function plot_quiver(md, options, canvas, updateVel) { //{{{
2776+function plot_quiver(md, options, canvas, noCacheNodesOverride) { //{{{
2777 //PLOT_QUIVER - quiver plot with colors
2778 //
2779 // Usage:
2780@@ -6,18 +6,11 @@
2781 //
2782 // See also: PLOTMODEL, PLOT_MANAGER
2783
2784+ if ('quiver' in canvas.nodes && noCacheNodesOverride && options.getfieldvalue('cachenodes','on') === 'on') return;
2785+
2786 //{{{ declare variables:
2787- var vertices = [];
2788- var indices = [];
2789- var colors = [];
2790- var xmin, xmax;
2791- var ymin, ymax;
2792- var zmin, zmax;
2793- var scale, matrixscale, vertexscale;
2794-
2795 //Process data and model
2796- var meshresults = processmesh(md,[], options);
2797-
2798+ var meshresults = processmesh(md, [], options);
2799 var x = meshresults[0];
2800 var y = meshresults[1];
2801 var z = meshresults[2];
2802@@ -24,66 +17,50 @@
2803 var elements = meshresults[3];
2804 var is2d = meshresults[4];
2805 var isplanet = meshresults[5];
2806- var v = updateVel != undefined ? updateVel.vel : md.initialization.vel;
2807- var vx = updateVel != undefined ? updateVel.vx : md.initialization.vx;
2808- var vy = updateVel != undefined ? updateVel.vy : md.initialization.vy;
2809+ if (md.mesh.classname() !== 'mesh3dsurface') z = md.geometry.surface;
2810
2811- //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
2812- if (!md.geometry.surface) {
2813- md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
2814+ //Compute coordinates and data range:
2815+ var xlim = options.getfieldvalue('xlim', [ArrayMin(x), ArrayMax(x)]);
2816+ var ylim = options.getfieldvalue('ylim', [ArrayMin(y), ArrayMax(y)]);
2817+ var zlim = options.getfieldvalue('zlim', [ArrayMin(z), ArrayMax(z)]);
2818+
2819+ //Only displaying velocity fields for now
2820+ var v = isEmptyOrUndefined(md.results) ? md.initialization.vel : md.results[canvas.animation.frame].Vel;
2821+ var vx = isEmptyOrUndefined(md.results) ? md.initialization.vx : md.results[canvas.animation.frame].Vx;
2822+ var vy = isEmptyOrUndefined(md.results) ? md.initialization.vy : md.results[canvas.animation.frame].Vy;
2823+
2824+ //Handle heightscale
2825+ var vertices, scale;
2826+ if (md.mesh.classname() !== 'mesh3dsurface') {
2827+ vertices = [x, y, z];
2828+ scale = [1, 1, options.getfieldvalue('heightscale', 1)];
2829 }
2830- if (md.mesh.classname() == 'mesh3dsurface') {
2831- matrixscale = 1;
2832- vertexscale = options.getfieldvalue('heightscale', 1);
2833- }
2834 else {
2835- if (md.geometry.surface) {
2836- z=md.geometry.surface;
2837- }
2838- matrixscale = options.getfieldvalue('heightscale', 1);
2839- vertexscale = 0;
2840+ vertices = Node.prototype.scaleVertices(md, x, y, z, options.getfieldvalue('heightscale', 1));
2841+ scale = [1, 1, 1];
2842 }
2843- //}}}
2844-
2845- //Compute coordinates and data range:
2846- var modelxlim = [ArrayMin(x), ArrayMax(x)];
2847- var modelylim = [ArrayMin(y), ArrayMax(y)];
2848- var modelzlim = [ArrayMin(z), ArrayMax(z)];
2849- var xlim = options.getfieldvalue('xlim', modelxlim);
2850- var ylim = options.getfieldvalue('ylim', modelylim);
2851- var zlim = options.getfieldvalue('zlim', modelzlim);
2852- xmin = xlim[0];
2853- xmax = xlim[1];
2854- ymin = ylim[0];
2855- ymax = ylim[1];
2856- zmin = zlim[0];
2857- zmax = zlim[1];
2858-
2859+
2860 //Compute gl variables:
2861- var gl = canvas.gl;
2862- var node = Node(gl);
2863- canvas.nodes["velocity"] = node;
2864- node.name = "quiver";
2865- node.shaderName = "Colored";
2866- node.shader = gl.shaders[node.shaderName];
2867- node.lineWidth = options.getfieldvalue('linewidth', 1);
2868- node.scale = [1, 1, matrixscale];
2869- node.rotation = [-90, 0, 0];
2870- node.translation = [0, 0, 0];
2871- node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
2872- node.drawMode = gl.LINES;
2873- node.useIndexBuffer = false;
2874- node.drawOrder = 0;
2875- node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
2876- node.maskHeight = options.getfieldvalue('innermaskheight', 150.0)*options.getfieldvalue('heightscale', 1);
2877- node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
2878- updateModelMatrix(node);
2879-
2880- //retrieve some options
2881- var edgecolor=new RGBColor(options.getfieldvalue('edgecolor','black'));
2882- if (edgecolor.ok) edgecolor = [edgecolor.r/255.0, edgecolor.g/255.0, edgecolor.b/255.0, 1.0];
2883- else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ", canvas));
2884-
2885+ var edgecolor = options.getfieldvalue('edgecolor', 'black');
2886+ var node = new Node(
2887+ 'canvas', canvas,
2888+ 'options', options,
2889+ 'name', 'quiver',
2890+ 'shaderName', 'Colored',
2891+ 'alpha', options.getfieldvalue('alpha', 1.0),
2892+ //'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, md.mesh.classname() === 'mesh3dsurface' ? (zlim[0] + zlim[1]) / 2 : zlim[0]],
2893+ 'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2],
2894+ 'drawMode', canvas.gl.LINES,
2895+ 'diffuseColor', edgecolor,
2896+ 'lineWidth', options.getfieldvalue('linewidth', 1),
2897+ 'maskEnabled', options.getfieldvalue('innermask','off') == 'on',
2898+ 'maskHeight', options.getfieldvalue('innermaskheight', 150.0) / options.getfieldvalue('heightscale', 1),
2899+ 'maskColor', options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]),
2900+ 'rotation', [-90, 0, 0],
2901+ 'scale', scale
2902+ );
2903+
2904+
2905 //{{{ node plot
2906 if (elements[0].length==6){ //prisms
2907 }
2908@@ -90,49 +67,37 @@
2909 else if (elements[0].length==4){ //tetras
2910 }
2911 else{ //2D triangular elements
2912+ //Create arow vertices, and use vx/vy to determine rotation before adding to quiver mesh.
2913+ 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)];
2914+
2915+ var newX = [];
2916+ var newY = [];
2917+ var newZ = [];
2918 var xyz = vec3.create();
2919- var xyz = vec3.create();
2920- var direction = vec3.create();
2921 var vertex = vec3.create();
2922- var vertexBase = vec3.create();
2923- 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)];
2924- var magnitude;
2925- var color = edgecolor;
2926 var scaling = options.getfieldvalue('scaling', 1);
2927- var scale;
2928- for(var i = 0; i < x.length; i++){
2929- //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
2930- if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i])) continue;
2931- //Scale vertices
2932+ var heightScale = options.getfieldvalue('heightscale', 1);
2933+ var arrowScale;
2934+ var modelMatrix = mat4.create();
2935+ var scaleMatrix = mat4.create();
2936+ var rotationMatrix = mat4.create();
2937+
2938+ for(var i = 0, iX = 0, iY = 0, iZ = 0; i < x.length; i++){
2939 xyz = vec3.fromValues(x[i], y[i], z[i]);
2940- magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
2941- vec3.normalize(direction, xyz);
2942- vec3.scale(vertex, direction, magnitude);
2943- vec3.copy(vertexBase, vertex);
2944-
2945- scale = scaling*v[i];
2946- var modelMatrix = mat4.create();
2947- var scaleMatrix = mat4.create();
2948- var rotationMatrix = mat4.create();
2949- mat4.scale(scaleMatrix, scaleMatrix, vec3.fromValues(scale, scale, scale));
2950- mat4.rotate(rotationMatrix, rotationMatrix, Math.atan2(vy[i], vx[i]), [0.0, 0.0, 1.0]);
2951+ arrowScale = v[i] * scaling;
2952+ scaleMatrix = mat4.create();
2953+ mat4.scale(scaleMatrix, mat4.create(), vec3.fromValues(arrowScale, arrowScale, arrowScale));
2954+ mat4.rotate(rotationMatrix, mat4.create(), Math.atan2(vy[i], vx[i]), [0.0, 0.0, 1.0]);
2955 mat4.multiply(modelMatrix, rotationMatrix, scaleMatrix);
2956-
2957- var temp = vec3.fromValues(0.0, 0.0, 0.0);
2958 for (var j = 0; j < 6; j++){
2959 vec3.transformMat4(vertex, verticesArrow[j], modelMatrix);
2960- vec3.add(vertex, vertex, vertexBase);
2961- vertices[vertices.length] = vertex[0];
2962- vertices[vertices.length] = vertex[1];
2963- vertices[vertices.length] = vertex[2];
2964-
2965- colors[colors.length] = color[0];
2966- colors[colors.length] = color[1];
2967- colors[colors.length] = color[2];
2968- colors[colors.length] = color[3];
2969+ vec3.add(vertex, vertex, xyz);
2970+ newX[iX++] = vertex[0];
2971+ newY[iY++] = vertex[1];
2972+ newZ[iZ++] = vertex[2];
2973 }
2974 }
2975+ node.patch('Vertices', [newX, newY, newZ], 'FaceColor', 'none');
2976 }
2977 //}}}
2978- node.mesh = GL.Mesh.load({vertices: vertices, colors: colors}, null, null, gl);
2979 } //}}}
2980Index: ../trunk-jpl/src/m/plot/plot_mesh.js
2981===================================================================
2982--- ../trunk-jpl/src/m/plot/plot_mesh.js (revision 21767)
2983+++ ../trunk-jpl/src/m/plot/plot_mesh.js (revision 21768)
2984@@ -6,18 +6,11 @@
2985 //
2986 // See also: PLOTMODEL, PLOT_MANAGER
2987
2988+ if ('mesh' in canvas.nodes && options.getfieldvalue('cachenodes','on') === 'on') return;
2989+
2990 //{{{ declare variables:
2991- var vertices = [];
2992- var indices = [];
2993- var colors = [];
2994- var nanindices = {};
2995- var xmin, xmax;
2996- var ymin, ymax;
2997- var zmin, zmax;
2998- var scale, matrixscale, vertexscale;
2999-
3000 //Process data and model
3001- var meshresults = processmesh(md,[], options);
3002+ var meshresults = processmesh(md, [], options);
3003 var x = meshresults[0];
3004 var y = meshresults[1];
3005 var z = meshresults[2];
3006@@ -25,62 +18,42 @@
3007 var is2d = meshresults[4];
3008 var isplanet = meshresults[5];
3009
3010- //Compue scaling through matrices for 2d meshes and vertices for 3d meshes
3011- if (!md.geometry.surface) {
3012- md.geometry.surface=NewArrayFill(md.mesh.x.length, 0);
3013+ //Compute coordinates and data range:
3014+ var xlim = options.getfieldvalue('xlim', [ArrayMin(x), ArrayMax(x)]);
3015+ var ylim = options.getfieldvalue('ylim', [ArrayMin(y), ArrayMax(y)]);
3016+ var zlim = options.getfieldvalue('zlim', [ArrayMin(z), ArrayMax(z)]);
3017+
3018+ //Handle heightscale
3019+ var vertices, scale;
3020+ if (md.mesh.classname() !== 'mesh3dsurface') {
3021+ vertices = [x, y, z];
3022+ scale = [1, 1, options.getfieldvalue('heightscale', 1)];
3023 }
3024- if (md.mesh.classname() == 'mesh3dsurface') {
3025- matrixscale = 1;
3026- vertexscale = options.getfieldvalue('heightscale', 1);
3027- }
3028 else {
3029- if (md.geometry.surface) {
3030- z=md.geometry.surface;
3031- }
3032- matrixscale = options.getfieldvalue('heightscale', 1);
3033- vertexscale = 0;
3034+ vertices = Node.prototype.scaleVertices(md, x, y, z, options.getfieldvalue('heightscale', 1));
3035+ scale = [1, 1, 1];
3036 }
3037+
3038+ //Compute gl variables:
3039+ var edgecolor = options.getfieldvalue('edgecolor', 'black');
3040+ var node = new Node(
3041+ 'canvas', canvas,
3042+ 'options', options,
3043+ 'name', 'mesh',
3044+ 'shaderName', 'Colored',
3045+ 'alpha', options.getfieldvalue('alpha', 1.0),
3046+ //'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, md.mesh.classname() === 'mesh3dsurface' ? (zlim[0] + zlim[1]) / 2 : zlim[0]],
3047+ 'center', [(xlim[0] + xlim[1]) / 2, (ylim[0] + ylim[1]) / 2, (zlim[0] + zlim[1]) / 2],
3048+ 'drawMode', canvas.gl.LINES,
3049+ 'diffuseColor', edgecolor,
3050+ 'lineWidth', options.getfieldvalue('linewidth', 1),
3051+ 'maskEnabled', options.getfieldvalue('innermask','off') == 'on',
3052+ 'maskHeight', options.getfieldvalue('innermaskheight', 150.0) / options.getfieldvalue('heightscale', 1),
3053+ 'maskColor', options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]),
3054+ 'rotation', [-90, 0, 0],
3055+ 'scale', scale
3056+ );
3057 //}}}
3058-
3059- //Compute coordinates and data range:
3060- var modelxlim = [ArrayMin(x), ArrayMax(x)];
3061- var modelylim = [ArrayMin(y), ArrayMax(y)];
3062- var modelzlim = [ArrayMin(z), ArrayMax(z)];
3063- var xlim = options.getfieldvalue('xlim', modelxlim);
3064- var ylim = options.getfieldvalue('ylim', modelylim);
3065- var zlim = options.getfieldvalue('zlim', modelzlim);
3066- xmin = xlim[0];
3067- xmax = xlim[1];
3068- ymin = ylim[0];
3069- ymax = ylim[1];
3070- zmin = zlim[0];
3071- zmax = zlim[1];
3072-
3073- //Compute gl variables:
3074- var gl = canvas.gl;
3075- gl.makeCurrent();
3076- var node = Node(gl);
3077- canvas.nodes[canvas.nodes.length] = node;
3078- node.name = "mesh";
3079- node.shaderName = "Colored";
3080- node.shader = gl.shaders[node.shaderName];
3081- node.lineWidth = options.getfieldvalue('linewidth', 1);
3082- node.scale = [1, 1, matrixscale];
3083- node.rotation = [-90, 0, 0];
3084- node.translation = [0, 0, 0];
3085- node.center = [(xmin + xmax) / 2, (ymin + ymax) / 2, (zmin + zmax) / 2];
3086- node.drawMode = gl.LINES;
3087- node.drawOrder = 0;
3088- node.maskEnabled = options.getfieldvalue('innermask','off') == 'on';
3089- node.maskHeight = options.getfieldvalue('innermaskheight', 150.0)*options.getfieldvalue('heightscale', 1);
3090- node.maskColor = options.getfieldvalue('innermaskcolor',[0.0, 0.0, 1.0, 1.0]);
3091- updateModelMatrix(node);
3092-
3093- //retrieve some options
3094- var edgecolor = new RGBColor(options.getfieldvalue('edgecolor','black'));
3095- if (edgecolor.ok) edgecolor = [edgecolor.r/255.0, edgecolor.g/255.0, edgecolor.b/255.0, 1.0];
3096- else throw Error(sprintf("s%s%s\n","initWebGL error message: cound not find out edgecolor color for curent canvas ", canvas));
3097-
3098 //{{{ node plot
3099 if (elements[0].length==6){ //prisms
3100 }
3101@@ -87,53 +60,10 @@
3102 else if (elements[0].length==4){ //tetras
3103 }
3104 else{ //2D triangular elements
3105- var xyz = vec3.create();
3106- var direction = vec3.create();
3107- var vertex = vec3.create();
3108- var magnitude;
3109- var color = edgecolor;
3110- for(var i = 0; i < x.length; i++){
3111- //Check for NaN values and remove from indices array as necessary, but preserve vertex array spacing
3112- if (isNaN(x[i]) || isNaN(y[i]) || isNaN(z[i])) {
3113- nanindices[i] = i;
3114- vertices[vertices.length] = vertex[0];
3115- vertices[vertices.length] = vertex[1];
3116- vertices[vertices.length] = vertex[2];
3117-
3118- colors[colors.length] = color[0];
3119- colors[colors.length] = color[1];
3120- colors[colors.length] = color[2];
3121- colors[colors.length] = color[3];
3122- continue;
3123- }
3124- //Scale vertices
3125- xyz = vec3.fromValues(x[i], y[i], z[i]);
3126- magnitude = vec3.length(xyz) + md.geometry.surface[i] * vertexscale;
3127- vec3.normalize(direction, xyz);
3128- vec3.scale(vertex, direction, magnitude);
3129- vertices[vertices.length] = vertex[0];
3130- vertices[vertices.length] = vertex[1];
3131- vertices[vertices.length] = vertex[2];
3132-
3133- colors[colors.length] = color[0];
3134- colors[colors.length] = color[1];
3135- colors[colors.length] = color[2];
3136- colors[colors.length] = color[3];
3137- }
3138-
3139- //linearize the elements array:
3140- var element;
3141- for(var i = 0; i < elements.length; i++){
3142- element = [elements[i][0] - 1, elements[i][1] - 1, elements[i][2] - 1];
3143- if (element[0] in nanindices || element[1] in nanindices || element[2] in nanindices) continue;
3144- indices[indices.length] = element[0];
3145- indices[indices.length] = element[1];
3146- indices[indices.length] = element[1];
3147- indices[indices.length] = element[2];
3148- indices[indices.length] = element[2];
3149- indices[indices.length] = element[0];
3150- }
3151+ node.patch('Faces', elements, 'Vertices', vertices, 'FaceColor', 'none', 'EdgeColor', edgecolor);
3152 }
3153 //}}}
3154- node.mesh = GL.Mesh.load({vertices: vertices, colors: colors, triangles: indices}, null, null, gl);
3155+ //options=options.addfielddefault('title','Mesh');
3156+ //options=addfielddefault('colorbar','off');
3157+ //applyoptions(md,[],options,canvas);
3158 } //}}}
3159Index: ../trunk-jpl/src/m/classes/clusters/generic.js
3160===================================================================
3161--- ../trunk-jpl/src/m/classes/clusters/generic.js (revision 21767)
3162+++ ../trunk-jpl/src/m/classes/clusters/generic.js (revision 21768)
3163@@ -171,4 +171,4 @@
3164 }
3165 return buf;
3166 } //}}}
3167-}
3168+}
Note: See TracBrowser for help on using the repository browser.