lavavu 1.9.0__cp312-cp312-macosx_10_13_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. lavavu/LavaVuPython.py +561 -0
  2. lavavu/_LavaVuPython.cpython-312-darwin.so +0 -0
  3. lavavu/__init__.py +15 -0
  4. lavavu/__main__.py +12 -0
  5. lavavu/amalgamate.py +15 -0
  6. lavavu/aserver.py +359 -0
  7. lavavu/control.py +1718 -0
  8. lavavu/convert.py +888 -0
  9. lavavu/dict.json +2517 -0
  10. lavavu/font.bin +0 -0
  11. lavavu/html/LavaVu-amalgamated.css +282 -0
  12. lavavu/html/OK-min.js +99 -0
  13. lavavu/html/baseviewer.js +307 -0
  14. lavavu/html/control.css +104 -0
  15. lavavu/html/control.js +340 -0
  16. lavavu/html/dat-gui-light-theme.css +68 -0
  17. lavavu/html/dat.gui.min.js +2 -0
  18. lavavu/html/draw.js +2259 -0
  19. lavavu/html/drawbox.js +1039 -0
  20. lavavu/html/emscripten-template.js +184 -0
  21. lavavu/html/emscripten.css +92 -0
  22. lavavu/html/favicon.ico +0 -0
  23. lavavu/html/gl-matrix-min.js +47 -0
  24. lavavu/html/gui.css +25 -0
  25. lavavu/html/menu.js +610 -0
  26. lavavu/html/server.js +226 -0
  27. lavavu/html/stats.min.js +5 -0
  28. lavavu/html/styles.css +58 -0
  29. lavavu/html/webview-template.html +43 -0
  30. lavavu/html/webview.html +43 -0
  31. lavavu/lavavu.py +6114 -0
  32. lavavu/osmesa/LavaVuPython.py +561 -0
  33. lavavu/osmesa/__init__.py +0 -0
  34. lavavu/points.py +191 -0
  35. lavavu/server.py +343 -0
  36. lavavu/shaders/default.frag +14 -0
  37. lavavu/shaders/default.vert +17 -0
  38. lavavu/shaders/fontShader.frag +20 -0
  39. lavavu/shaders/fontShader.vert +18 -0
  40. lavavu/shaders/lineShader.frag +39 -0
  41. lavavu/shaders/lineShader.vert +26 -0
  42. lavavu/shaders/pointShader.frag +127 -0
  43. lavavu/shaders/pointShader.vert +53 -0
  44. lavavu/shaders/triShader.frag +153 -0
  45. lavavu/shaders/triShader.vert +51 -0
  46. lavavu/shaders/volumeShader.frag +400 -0
  47. lavavu/shaders/volumeShader.vert +5 -0
  48. lavavu/tracers.py +124 -0
  49. lavavu/vutils.py +211 -0
  50. lavavu-1.9.0.dist-info/LICENSE.md +179 -0
  51. lavavu-1.9.0.dist-info/METADATA +319 -0
  52. lavavu-1.9.0.dist-info/RECORD +55 -0
  53. lavavu-1.9.0.dist-info/WHEEL +5 -0
  54. lavavu-1.9.0.dist-info/entry_points.txt +2 -0
  55. lavavu-1.9.0.dist-info/top_level.txt +1 -0
lavavu/html/menu.js ADDED
@@ -0,0 +1,610 @@
1
+ /* dat.gui menu */
2
+
3
+ function parseColour(input) {
4
+ if (typeof(input) != 'object') {
5
+ //var div = document.createElement('div');
6
+ var div = document.getElementById('hidden_style_div');
7
+ div.style.color = input;
8
+ //This triggers a full layout calc - can slow things down
9
+ c = getComputedStyle(div).color;
10
+ o = getComputedStyle(div).opacity;
11
+ C = new Colour(c);
12
+ //c.alpha = o;
13
+ //toFixed() / 1 rounds to fixed position and removes trailing zeros
14
+ c.alpha = parseFloat(o).toFixed(2) / 1;
15
+ } else {
16
+ C = new Colour(input);
17
+ }
18
+ return C.html();
19
+ }
20
+
21
+ //Attempts to get range from data/volume objects for range controls
22
+ function menu_getrange(obj, ctrl) {
23
+ //Ranged?
24
+ if (ctrl.length > 1 && ctrl[1].length == 3) {
25
+ var range = [ctrl[1][0], ctrl[1][1]];
26
+ //Mapped range?
27
+ if (ctrl[1][0] === ctrl[1][1] && ctrl[1][0] === 0.0) {
28
+ //Try volume range
29
+ if (obj.volume && obj.volume.minimum < obj.volume.maximum)
30
+ range = [obj.volume.minimum, obj.volume.maximum];
31
+ //Or value data range
32
+ else if (obj.values && obj.values.minimum < obj.values.maximum)
33
+ range = [obj.values.minimum, obj.values.maximum];
34
+ }
35
+ if (range[1] <= range[0]) {
36
+ //console.log(obj.name + " : range (invalid) = " + range);
37
+ return [0.0, 1.0]; //No valid range data, use a default
38
+ }
39
+ //console.log(obj.name + " : range = " + range);
40
+ return range;
41
+ }
42
+ return null;
43
+ }
44
+
45
+ //Add controls to menu, using property metadata
46
+ function menu_addctrl(menu, obj, viewer, prop, changefn) {
47
+ //TODO: implement delayed high quality render for faster interaction
48
+ //var changefn = function(value) {viewer.delayedRender(250);};
49
+ var ctrl = viewer.dict[prop].control;
50
+
51
+ //Query prop dict for data type, default, min/max/step etc
52
+ var dtype = viewer.dict[prop].type;
53
+ if (prop === 'colourmap') {
54
+ var maps = ['']
55
+ for (var i=0; i<viewer.vis.colourmaps.length; i++)
56
+ maps.push(viewer.vis.colourmaps[i].name);
57
+ //console.log(JSON.stringify(maps));
58
+ menu.add(obj, prop, maps).onFinishChange(changefn);
59
+
60
+ } else if (ctrl.length > 2 && ctrl[2] != null) {
61
+ //Select from list of options
62
+ menu.add(obj, prop, ctrl[2]).onFinishChange(changefn);
63
+
64
+ } else if ((dtype.indexOf('real') >= 0 || dtype.indexOf('integer') >= 0)) { // && typeof(obj[prop]) == 'number') {
65
+
66
+ //Ranged?
67
+ var range = menu_getrange(obj, ctrl);
68
+ var addnumeric = function(menu, obj, prop, range, changefn) {
69
+ if (range) {
70
+ if (obj[prop] < range[0]) obj[prop] = range[0];
71
+ if (obj[prop] > range[1]) obj[prop] = range[1];
72
+ return menu.add(obj, prop, range[0], range[1], ctrl[1][2]).onFinishChange(changefn);
73
+ } else {
74
+ return menu.add(obj, prop).onFinishChange(changefn);
75
+ }
76
+ }
77
+
78
+ //Array quantities
79
+ var dims = 1;
80
+ var p0 = dtype.indexOf('[');
81
+ var p1 = dtype.indexOf(']');
82
+ if (p0 >= 0 && p1 >= 0)
83
+ dims = parseInt(dtype.slice(p0+1, p1));
84
+ if (dims > 1) {
85
+ //2d, 3d ?
86
+ for (var d=0; d<dims; d++) {
87
+ //console.log("Adding DIM " + d);
88
+ var added = addnumeric(menu, obj[prop], "" + d, range, changefn)
89
+ added.name(prop + '[' + d + ']');
90
+ }
91
+
92
+ //Only add if a number, anything else will cause dat.gui to error
93
+ } else if (typeof(obj[prop]) == 'number') {
94
+ //1d
95
+ console.log("Adding ", prop, typeof(obj[prop]));
96
+ addnumeric(menu, obj, prop, range, changefn)
97
+ }
98
+
99
+ } else if (dtype === 'string' || dtype === 'boolean') {
100
+ menu.add(obj, prop).onFinishChange(changefn);
101
+ } else if (dtype === 'boolean') {
102
+ menu.add(obj, prop).onFinishChange(changefn);
103
+ } else if (dtype === 'colour') {
104
+ try {
105
+ //Convert to html colour first
106
+ obj[prop] = parseColour(obj[prop]);
107
+ menu.addColor(obj, prop).onChange(changefn);
108
+ } catch(e) {
109
+ console.log(e);
110
+ }
111
+ }
112
+ }
113
+
114
+ function menu_addctrls(menu, obj, viewer, onchange) {
115
+ //Loop through every available property
116
+ var extras = [];
117
+ for (var prop in viewer.dict) {
118
+
119
+ //Check if it is enabled in GUI
120
+ var ctrl = viewer.dict[prop].control;
121
+ if (!ctrl || ctrl[0] === false) continue; //Control disabled
122
+
123
+ //Check if it has been set on the target object
124
+ if (prop in obj) {
125
+ //console.log(prop + " ==> " + JSON.stringify(viewer.dict[prop]));
126
+ menu_addctrl(menu, obj, viewer, prop, onchange);
127
+
128
+ } else {
129
+ //Save list of properties without controls
130
+ extras.push(prop);
131
+ continue;
132
+ }
133
+ }
134
+
135
+ //Sort into alphabetical order
136
+ extras.sort();
137
+
138
+ //Add the extra properties to a dropdown list
139
+ //Selecting a property will add a controller for it
140
+ var propadd = {"properties" : ""};
141
+ var propselfn = function(prop) {
142
+ //Set the property to the default value
143
+ propadd.ref[prop] = viewer.dict[prop]["default"];
144
+ //Add new property controller
145
+ menu_addctrl(propadd.menu, propadd.ref, viewer, prop, onchange);
146
+ //Remove, then add the props list back at the bottom, minus new prop
147
+ propadd.menu.remove(propadd.controller);
148
+ //Save the new filtered list
149
+ propadd.list = propadd.list.filter(word => word != prop);
150
+ propadd.controller = propadd.menu.add(propadd, "properties", propadd.list).name("More properties").onFinishChange(propadd.fn);
151
+ }
152
+ propadd.controller = menu.add(propadd, "properties", extras).name("More properties").onFinishChange(propselfn);
153
+ propadd.ref = obj;
154
+ propadd.menu = menu;
155
+ propadd.fn = propselfn;
156
+ propadd.list = extras;
157
+ }
158
+
159
+
160
+ function menu_addcmaps(menu, obj, viewer, onchange) {
161
+ //Colourmap editing menu
162
+ if (viewer.cgui.prmenu) viewer.cgui.removeFolder(viewer.cgui.prmenu);
163
+ if (viewer.cgui.cmenu) viewer.cgui.removeFolder(viewer.cgui.cmenu);
164
+ if (viewer.cgui.pomenu) viewer.cgui.removeFolder(viewer.cgui.pomenu);
165
+ viewer.cgui.prmenu = viewer.cgui.addFolder("Properties");
166
+ viewer.cgui.cmenu = viewer.cgui.addFolder("Colours");
167
+ viewer.cgui.pomenu = viewer.cgui.addFolder("Positions");
168
+ //Loop through every available property
169
+ for (var prop in viewer.dict) {
170
+ //Check if it is enabled in GUI
171
+ var ctrl = viewer.dict[prop].control;
172
+ if (!ctrl || ctrl[0] === false) continue; //Control disabled
173
+
174
+ //Check if it applies to colourmap object
175
+ if (viewer.dict[prop]["target"] == 'colourmap') {
176
+ //console.log(prop + " ==> " + JSON.stringify(viewer.dict[prop]));
177
+ //Need to add property default if not set
178
+ if (!obj[prop])
179
+ obj[prop] = viewer.dict[prop].default;
180
+ menu_addctrl(menu.prmenu, obj, viewer, prop, onchange);
181
+ }
182
+ }
183
+
184
+ var reload_onchange = function() {onchange("", true);};
185
+
186
+ //Loop through colours
187
+ menu.cmenu.add({"Add Colour" : function() {obj.colours.unshift({"colour" : "rgba(0,0,0,1)", "position" : 0.0}); viewer.gui.close(); reload_onchange(); }}, "Add Colour");
188
+ //Need to add delete buttons in closure to get correct pos/index
189
+ function del_btn(pos) {menu.pomenu.add({"Delete" : function() {obj.colours.splice(pos, 1); viewer.gui.close(); reload_onchange(); }}, 'Delete').name('Delete ' + pos);}
190
+ for (var c in obj.colours) {
191
+ var o = obj.colours[c];
192
+ //Convert to html colour first
193
+ o.colour = parseColour(o.colour);
194
+ o.opacity = 1.0;
195
+ menu.cmenu.addColor(o, 'colour').onChange(reload_onchange).name('Colour ' + c);
196
+ menu.pomenu.add(o, 'position', 0.0, 1.0, 0.01).onFinishChange(reload_onchange).name('Position ' + c);
197
+ del_btn(c);
198
+ }
199
+ }
200
+
201
+ function createMenu(viewer, onchange, webglmode, global) {
202
+ if (!dat) return null;
203
+ //var t0 = performance.now();
204
+
205
+ //Exists? Destroy
206
+ if (viewer.gui)
207
+ viewer.gui.destroy();
208
+
209
+ var el = null;
210
+ //Insert within element rather than whole document
211
+ if (!global && viewer.canvas && viewer.canvas.parentElement != document.body && viewer.canvas.parentElement.parentElement != document.body) {
212
+ var pel = viewer.canvas.parentElement;
213
+ //A bit of a hack to detect converted HTML output and skip going up two parent elements
214
+ if (pel.parentElement.className != 'section')
215
+ pel = pel.parentElement;
216
+ pel.style.position = 'relative'; //Parent element relative prevents menu escaping wrapper div
217
+ //Jupyter notebook overflow hacks (allows gui to be larger than cell)
218
+ var e = pel;
219
+ //These parent elements need 'overflow: visible' to prevent scrolling or cut-off of menu
220
+ //Add the "pgui" class to all the parent containers
221
+ while (e != document.body) {
222
+ e.classList.add("pgui");
223
+ e = e.parentElement;
224
+ }
225
+ //pel.style.overflow = 'visible'; //Parent element relative prevents menu escaping wrapper div
226
+ //pel.parentElement.style.overflow = 'visible'; //Parent element relative prevents menu escaping wrapper div
227
+ var id = 'dat_gui_menu_' + (viewer.canvas ? viewer.canvas.id : 'default');
228
+ el = document.getElementById(id)
229
+ if (el)
230
+ el.parentNode.removeChild(el);
231
+ el = document.createElement("div");
232
+ el.id = id;
233
+ el.style.cssText = "position: absolute; top: 0em; right: 0em; z-index: 255;";
234
+ pel.insertBefore(el, pel.childNodes[0]);
235
+ }
236
+
237
+ var gui;
238
+ //Insert within element rather than whole document
239
+ if (el) {
240
+ gui = new dat.GUI({ autoPlace: false, width: 275, hideable: false });
241
+ el.appendChild(gui.domElement);
242
+ } else {
243
+ gui = new dat.GUI({ hideable: false, width: 275 });
244
+ }
245
+
246
+ //Re-create menu on element mouse down if we need to reload
247
+ //(Instead of calling whenever state changes, re-creation is slow!)
248
+ gui.domElement.onmousedown = function(e) {
249
+ if (viewer.reloadgui)
250
+ viewer.menu();
251
+ return true;
252
+ }
253
+
254
+ //Horrible hack to stop codemirror stealing events when menu is over an editor element
255
+ gui.domElement.onmouseenter = function(e) {
256
+ //console.log('mouseenter');
257
+ var stylesheet = document.styleSheets[0];
258
+ stylesheet.insertRule('.CodeMirror-code { pointer-events: none;}', 0);
259
+ };
260
+
261
+ gui.domElement.onmouseleave = function(e) {
262
+ //console.log('mouseleave');
263
+ var stylesheet = document.styleSheets[0];
264
+ if (stylesheet.cssRules[0].cssText.indexOf('CodeMirror') >= 0)
265
+ stylesheet.deleteRule(0);
266
+ };
267
+
268
+ //Hidden element for style compute, required to be on page for chrome, can't just use createElement
269
+ var elem = document.getElementById('hidden_style_div');
270
+ if (!elem) {
271
+ elem = document.createElement('div');
272
+ elem.style.display = 'none';
273
+ elem.id = 'hidden_style_div';
274
+ document.body.appendChild(elem);
275
+ }
276
+
277
+ //Move above other controls
278
+ gui.domElement.parentElement.style.zIndex = '255';
279
+ //Save close button for hide menu check
280
+ gui.closebtn = gui.domElement.getElementsByClassName('close-button')[0];
281
+ //Hide/show on mouseover (only if overlapping)
282
+ if (!viewer.reloadgui) {
283
+ //Create closed and hidden for the first time, leave as is otherwise
284
+ gui.close();
285
+ hideMenu(viewer.canvas, gui);
286
+ }
287
+
288
+ gui.add({"Reload" : function() {viewer.reload();}}, "Reload");
289
+ //VR supported? (WebGL only)
290
+ if (webglmode === 1 && navigator.getVRDisplays) {
291
+ viewer.vrDisplay = null;
292
+ viewer.inVR = false;
293
+ gui.add({"VR Mode" : function() {start_VR(viewer);}}, 'VR Mode');
294
+ }
295
+
296
+ if (!!window.chrome) //Stupid chrome disabled data URL open
297
+ gui.add({"Export" : function() {var w = window.open(); w.document.write('<pre>' + viewer.toString() + '</pre>');}}, 'Export');
298
+ else
299
+ gui.add({"Export" : function() {window.open('data:application/json;base64,' + window.btoa(viewer.toString()));}}, 'Export');
300
+ //gui.add({"loadFile" : function() {document.getElementById('fileupload').click();}}, 'loadFile'). name('Load Image file');
301
+
302
+ //Non-persistent settings
303
+ gui.add(viewer, "mode", ['Rotate', 'Translate', 'Zoom']);
304
+ if (webglmode === 0) {
305
+ viewer.cmd = '';
306
+ gui.add(viewer, "cmd").onFinishChange(function(cmd) {if (cmd.length) {viewer.command(cmd); viewer.cmd = '';}}).name('Command');
307
+ }
308
+
309
+ //var s = gui.addFolder('Settings');
310
+ if (webglmode === 1) {
311
+ //Old WebGL 1.0 mode
312
+ gui.add(viewer.vis, "interactive").name("Interactive Render");
313
+ gui.add(viewer.vis, "immediatesort").name("Immediate Sort");
314
+ gui.add(viewer.vis, "sortenabled").name('Sort Enabled');
315
+ } else if (webglmode === 2) {
316
+ //Emscripten WebGL2 mode
317
+ gui.add({"Full Screen" : function() {Module.requestFullscreen(false,true);}}, 'Full Screen');
318
+ var params = {loadBrowserFile : function() { document.getElementById('fileinput').click(); } };
319
+ gui.add(params, 'loadBrowserFile').name('Load file');
320
+ gui.add({"Export GLDB" : function() {window.commands.push('export');}}, 'Export GLDB');
321
+ } else if (viewer.canvas) {
322
+ //Server render
323
+ gui.add(viewer, "alwaysdraw");
324
+ var url = viewer.canvas.imgtarget.baseurl;
325
+ if (url)
326
+ gui.add({"Popup Viewer" : function() {window.open(url, "LavaVu", "toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=no,resizable=no,width=1024,height=768");}}, 'Popup Viewer');
327
+ }
328
+
329
+ var g = gui.addFolder('Globals/Defaults');
330
+ menu_addctrls(g, viewer.vis.properties, viewer, onchange);
331
+
332
+ var v = gui.addFolder('Views');
333
+ var ir2 = 1.0 / Math.sqrt(2.0);
334
+ v.add({"Reset" : function() {viewer.reset(); }}, 'Reset');
335
+ v.add({"XY" : function() {viewer.syncRotation([0, 0, 0, 1]); }}, 'XY');
336
+ v.add({"YX" : function() {viewer.syncRotation([0, 1, 0, 0]);}}, 'YX');
337
+ v.add({"XZ" : function() {viewer.syncRotation([ir2, 0, 0, -ir2]);}}, 'XZ');
338
+ v.add({"ZX" : function() {viewer.syncRotation([ir2, 0, 0, ir2]);}}, 'ZX');
339
+ v.add({"YZ" : function() {viewer.syncRotation([0, -ir2, 0, -ir2]);}}, 'YZ');
340
+ v.add({"ZY" : function() {viewer.syncRotation([0, -ir2, 0, ir2]);}}, 'ZY');
341
+ //console.log(JSON.stringify(viewer.view));
342
+ if (viewer.view)
343
+ menu_addctrls(v, viewer.view, viewer, onchange);
344
+ else
345
+ menu_addctrls(v, viewer.vis.views[0], viewer, onchange);
346
+
347
+ var o = gui.addFolder('Objects');
348
+ for (var id in viewer.vis.objects) {
349
+ var of = o.addFolder(viewer.vis.objects[id].name);
350
+ menu_addctrls(of, viewer.vis.objects[id], viewer, onchange);
351
+ }
352
+
353
+ viewer.gui = gui;
354
+ if (!viewer.selectedcolourmap) {
355
+ viewer.selectedcolourmap = "";
356
+ viewer.newcolourmap = "";
357
+ }
358
+ viewer.cgui = viewer.gui.addFolder('ColourMaps');
359
+
360
+ createColourMapMenu(viewer, onchange, webglmode === 1);
361
+
362
+ //var t1 = performance.now();
363
+ //console.log("Call to menu() took " + (t1 - t0) + " milliseconds.")
364
+ }
365
+
366
+ function createColourMapMenu(viewer, onchange, webglmode) {
367
+ //Remove existing entries
368
+ for (var i in viewer.cgui.__controllers)
369
+ viewer.cgui.__controllers[i].remove();
370
+
371
+ var cms = [];
372
+
373
+ //Dropdown to select a colourmap, when selected the editing menu will be populated
374
+ for (var id in viewer.vis.colourmaps)
375
+ cms.push(viewer.vis.colourmaps[id].name);
376
+
377
+ if (webglmode) {
378
+ //Add a default colourmap
379
+ viewer.cgui.add({"Add" : function() {viewer.addColourMap(); viewer.gui.close();}}, 'Add');
380
+ } else {
381
+ //Add a colourmap from list of defaults
382
+ cmapaddfn = function(value) {
383
+ if (!value || !value.length) return;
384
+ viewer.command("select; colourmap " + value + " " + value);
385
+ viewer.gui.close();
386
+ };
387
+
388
+ viewer.cgui.add(viewer, "newcolourmap", viewer.defaultcolourmaps).onFinishChange(cmapaddfn).name("Add");
389
+ }
390
+
391
+ //Do the rest only if colourmaps exist...
392
+ if (cms.length == 0) return;
393
+
394
+ //When selected, populate the Colours & Positions menus
395
+ cmapselfn = function(value) {
396
+ if (!value) return;
397
+ for (var id in viewer.vis.colourmaps) {
398
+ if (value == viewer.vis.colourmaps[id].name)
399
+ menu_addcmaps(viewer.cgui, viewer.vis.colourmaps[id], viewer, onchange);
400
+ }
401
+ };
402
+
403
+ viewer.cgui.cmap = viewer.cgui.add(viewer, "selectedcolourmap", cms).onFinishChange(cmapselfn).name("Colourmap");
404
+
405
+ //Re-select if previous value if any
406
+ if (viewer.selectedcolourmap)
407
+ cmapselfn(viewer.selectedcolourmap);
408
+ }
409
+
410
+ function updateMenu(viewer, onchange) {
411
+ //Attempt to update DAT.GUI controls
412
+ function updateDisplay(gui) {
413
+ for (var i in gui.__controllers)
414
+ gui.__controllers[i].updateDisplay();
415
+ for (var f in gui.__folders)
416
+ updateDisplay(gui.__folders[f]);
417
+ }
418
+ updateDisplay(viewer.gui);
419
+ updateDisplay(viewer.cgui);
420
+ }
421
+
422
+ function hideMenu(canvas, gui) {
423
+ //No menu, but hide the mode controls
424
+ if (!gui) {
425
+ if (canvas && canvas.imgtarget)
426
+ canvas.imgtarget.nextElementSibling.style.display = "none";
427
+ return;
428
+ }
429
+
430
+ //Requires menu to be closed and hiding enabled
431
+ if (!gui.closed) return;
432
+
433
+ //Only hide if overlapping the canvas (unless no canvas passed)
434
+ if (canvas) {
435
+ var rect0 = gui.closebtn.getBoundingClientRect();
436
+ var rect1 = canvas.getBoundingClientRect();
437
+ if (rect0.right < rect1.left || rect0.left > rect1.right ||
438
+ rect0.bottom < rect1.top || rect0.top > rect1.bottom) {
439
+ //No overlap, don't hide
440
+ return;
441
+ }
442
+ }
443
+
444
+ //Reached this point? Menu needs hiding
445
+ gui.domElement.style.display = "none";
446
+ }
447
+
448
+ //https://hacks.mozilla.org/2018/09/converting-a-webgl-application-to-webvr/
449
+ //https://github.com/Manishearth/webgl-to-webvr/
450
+ // This function is triggered when the user clicks the "enter VR" button
451
+ function start_VR(viewer) {
452
+ if (viewer.vrDisplay != null) {
453
+ if (!viewer.inVR) {
454
+ viewer.inVR = true;
455
+ // hand the canvas to the WebVR API
456
+ viewer.vrDisplay.requestPresent([{ source: viewer.canvas }]);
457
+ // requestPresent() will request permission to enter VR mode,
458
+ // and once the user has done this our `vrdisplaypresentchange`
459
+ // callback will be triggered
460
+
461
+ //Show stats
462
+ if (!document.getElementById("stats_info")) {
463
+ var stats = new Stats();
464
+ document.body.appendChild(stats.dom);
465
+ requestAnimationFrame(function loop() {
466
+ stats.update();
467
+ requestAnimationFrame(loop)
468
+ });
469
+ }
470
+
471
+ } else {
472
+ stop_VR(viewer);
473
+ }
474
+ }
475
+ }
476
+
477
+ function stop_VR(viewer) {
478
+ viewer.inVR = false;
479
+ // resize canvas to regular non-VR size if necessary
480
+ viewer.width = 0; //Auto resize
481
+ viewer.height = 0;
482
+ viewer.canvas.style.width = "100%";
483
+ viewer.canvas.style.height = "100%";
484
+
485
+ viewer.drawFrame();
486
+ viewer.draw();
487
+ }
488
+
489
+ function setup_VR(viewer) {
490
+ if (!navigator.getVRDisplays) {
491
+ alert("Your browser does not support WebVR");
492
+ return;
493
+ }
494
+
495
+ function display_setup(displays) {
496
+ if (displays.length === 0)
497
+ return;
498
+ //Use last in list
499
+ viewer.vrDisplay = displays[displays.length-1];
500
+ }
501
+
502
+ navigator.getVRDisplays().then(display_setup);
503
+
504
+ function VR_change() {
505
+ // no VR display, exit
506
+ if (viewer.vrDisplay == null)
507
+ return;
508
+
509
+ // are we entering or exiting VR?
510
+ if (viewer.vrDisplay.isPresenting) {
511
+ // optional, but recommended
512
+ viewer.vrDisplay.depthNear = viewer.near_clip;
513
+ viewer.vrDisplay.depthFar = viewer.far_clip;
514
+
515
+ // We should make our canvas the size expected
516
+ // by WebVR
517
+ const eye = viewer.vrDisplay.getEyeParameters("left");
518
+ // multiply by two since we're rendering both eyes side
519
+ // by side
520
+ viewer.width = eye.renderWidth * 2;
521
+ viewer.height = eye.renderHeight;
522
+ viewer.canvas.style.width = viewer.width + "px";
523
+ viewer.canvas.style.height = viewer.height + "px";
524
+ viewer.canvas.width = viewer.canvas.clientWidth;
525
+ viewer.canvas.height = viewer.canvas.clientHeight;
526
+
527
+ const vrCallback = () => {
528
+ if (viewer.vrDisplay == null || !viewer.inVR)
529
+ return;
530
+
531
+ // reregister callback if we're still in VR
532
+ viewer.vrDisplay.requestAnimationFrame(vrCallback);
533
+
534
+ // render scene
535
+ renderVR(viewer);
536
+ };
537
+
538
+ // register callback
539
+ viewer.vrDisplay.requestAnimationFrame(vrCallback);
540
+
541
+ } else {
542
+ stop_VR(viewer);
543
+ }
544
+ }
545
+
546
+ window.addEventListener('vrdisplaypresentchange', VR_change);
547
+ }
548
+
549
+ function renderVR(viewer) {
550
+ //Clear full canvas
551
+ viewer.gl.viewport(0, 0, viewer.canvas.width, viewer.canvas.height);
552
+ viewer.gl.clear(viewer.gl.COLOR_BUFFER_BIT | viewer.gl.DEPTH_BUFFER_BIT);
553
+
554
+ //Left eye
555
+ renderEye(viewer, true);
556
+
557
+ //Right eye
558
+ renderEye(viewer, false);
559
+
560
+ viewer.vrDisplay.submitFrame();
561
+ }
562
+
563
+ function renderEye(viewer, isLeft) {
564
+ let projection, mview;
565
+ let frameData = new VRFrameData();
566
+ var gl = viewer.webgl.gl;
567
+ var width = viewer.canvas.width / 2;
568
+ var height = viewer.canvas.height;
569
+
570
+ viewer.vrDisplay.getFrameData(frameData);
571
+
572
+ // choose which half of the canvas to draw on
573
+ if (isLeft) {
574
+ viewer.webgl.viewport = new Viewport(0, 0, width, height);
575
+ gl.viewport(0, 0, width, height);
576
+ //Apply the default camera
577
+ viewer.webgl.apply(viewer);
578
+
579
+ projection = frameData.leftProjectionMatrix;
580
+ mview = frameData.leftViewMatrix;
581
+ } else {
582
+ viewer.webgl.viewport = new Viewport(width, 0, width, height);
583
+ gl.viewport(width, 0, width, height);
584
+ //viewer.webgl.viewport = new Viewport(0, 0, width, height);
585
+ //gl.viewport(0, 0, width, height);
586
+ projection = frameData.rightProjectionMatrix;
587
+ mview = frameData.rightViewMatrix;
588
+ }
589
+
590
+ //Apply the default camera
591
+ viewer.webgl.apply(viewer);
592
+
593
+ //Update matrices with VR modified versions
594
+ //mat4.multiply(mview, viewer.webgl.modelView.matrix);
595
+ //viewer.webgl.modelView.matrix = mview;
596
+ mat4.multiply(viewer.webgl.modelView.matrix, mview);
597
+ //console.log(isLeft ? "LEFT " : "RIGHT "); printMatrix(mview);
598
+ //gl.uniformMatrix4fv(viewer.webgl.program.mvMatrixUniform, false, mview);
599
+
600
+ viewer.webgl.projection.matrix = projection;
601
+
602
+ //Render objects
603
+ for (var r in viewer.renderers) {
604
+ if (viewer.renderers[r].border && !viewer.showBorder) continue;
605
+ viewer.renderers[r].draw();
606
+ }
607
+
608
+ viewer.rotated = false; //Clear rotation flag
609
+ }
610
+