lavavu-osmesa 1.9.9__cp313-cp313-manylinux_2_28_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.
- lavavu/LavaVuPython.py +561 -0
- lavavu/_LavaVuPython.cpython-313-x86_64-linux-gnu.so +0 -0
- lavavu/__init__.py +15 -0
- lavavu/__main__.py +12 -0
- lavavu/amalgamate.py +15 -0
- lavavu/aserver.py +359 -0
- lavavu/control.py +1731 -0
- lavavu/convert.py +888 -0
- lavavu/dict.json +2528 -0
- lavavu/font.bin +0 -0
- lavavu/html/LavaVu-amalgamated.css +282 -0
- lavavu/html/OK-min.js +99 -0
- lavavu/html/baseviewer.js +307 -0
- lavavu/html/control.css +104 -0
- lavavu/html/control.js +340 -0
- lavavu/html/dat-gui-light-theme.css +68 -0
- lavavu/html/dat.gui.min.js +2 -0
- lavavu/html/draw.js +2259 -0
- lavavu/html/drawbox.js +1039 -0
- lavavu/html/emscripten-template.js +184 -0
- lavavu/html/emscripten.css +92 -0
- lavavu/html/favicon.ico +0 -0
- lavavu/html/gl-matrix-min.js +47 -0
- lavavu/html/gui.css +25 -0
- lavavu/html/menu.js +615 -0
- lavavu/html/server.js +226 -0
- lavavu/html/stats.min.js +5 -0
- lavavu/html/styles.css +58 -0
- lavavu/html/webview-template.html +43 -0
- lavavu/html/webview.html +43 -0
- lavavu/lavavu.py +6200 -0
- lavavu/osmesa/LavaVuPython.py +561 -0
- lavavu/osmesa/_LavaVuPython.cpython-313-x86_64-linux-gnu.so +0 -0
- lavavu/osmesa/__init__.py +0 -0
- lavavu/points.py +191 -0
- lavavu/server.py +343 -0
- lavavu/shaders/default.frag +14 -0
- lavavu/shaders/default.vert +17 -0
- lavavu/shaders/fontShader.frag +20 -0
- lavavu/shaders/fontShader.vert +18 -0
- lavavu/shaders/lineShader.frag +39 -0
- lavavu/shaders/lineShader.vert +26 -0
- lavavu/shaders/pointShader.frag +127 -0
- lavavu/shaders/pointShader.vert +53 -0
- lavavu/shaders/triShader.frag +153 -0
- lavavu/shaders/triShader.vert +49 -0
- lavavu/shaders/volumeShader.frag +400 -0
- lavavu/shaders/volumeShader.vert +5 -0
- lavavu/tracers.py +207 -0
- lavavu/vutils.py +211 -0
- lavavu_osmesa-1.9.9.dist-info/METADATA +323 -0
- lavavu_osmesa-1.9.9.dist-info/RECORD +65 -0
- lavavu_osmesa-1.9.9.dist-info/WHEEL +5 -0
- lavavu_osmesa-1.9.9.dist-info/entry_points.txt +2 -0
- lavavu_osmesa-1.9.9.dist-info/licenses/LICENSE.md +179 -0
- lavavu_osmesa-1.9.9.dist-info/top_level.txt +1 -0
- lavavu_osmesa.libs/libLLVM-17-51492e70.so +0 -0
- lavavu_osmesa.libs/libOSMesa-f6a8f160.so.8.0.0 +0 -0
- lavavu_osmesa.libs/libdrm-b0291a67.so.2.4.0 +0 -0
- lavavu_osmesa.libs/libffi-3a37023a.so.6.0.2 +0 -0
- lavavu_osmesa.libs/libglapi-520b284c.so.0.0.0 +0 -0
- lavavu_osmesa.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
- lavavu_osmesa.libs/libselinux-d0805dcb.so.1 +0 -0
- lavavu_osmesa.libs/libtinfo-3a2cb85b.so.6.1 +0 -0
- lavavu_osmesa.libs/libzstd-76b78bac.so.1.4.4 +0 -0
lavavu/html/drawbox.js
ADDED
@@ -0,0 +1,1039 @@
|
|
1
|
+
//Bounding box only in WebGL
|
2
|
+
//Urgent TODO: move shared code with draw.js into common file
|
3
|
+
//maintaining two copies currently!
|
4
|
+
//Re-init using previous callback data
|
5
|
+
|
6
|
+
function initBox(el, cmd_callback, fixedsize) {
|
7
|
+
//console.log("INITBOX: " + el.id);
|
8
|
+
var canvas = document.createElement("canvas");
|
9
|
+
canvas.classList.add("resized");
|
10
|
+
if (!el) el = document.body.firstChild;
|
11
|
+
canvas.id = "canvas_" + el.id;
|
12
|
+
canvas.imgtarget = el
|
13
|
+
el.parentElement.appendChild(canvas);
|
14
|
+
canvas.style.cssText = "position: absolute; width: 100%; height: 100%; margin: 0px; padding: 0px; top: 0; left: 0; bottom: 0; right: 0; z-index: 11; border: none;"
|
15
|
+
var viewer = new BoxViewer(canvas);
|
16
|
+
|
17
|
+
//Canvas event handling
|
18
|
+
canvas.mouse = new Mouse(canvas, new MouseEventHandler(canvasBoxMouseClick, canvasBoxMouseWheel, canvasBoxMouseMove, canvasBoxMouseDown, null, null, canvasBoxMousePinch));
|
19
|
+
//Following two settings should probably be defaults?
|
20
|
+
canvas.mouse.moveUpdate = true; //Continual update of deltaX/Y
|
21
|
+
//canvas.mouse.setDefault();
|
22
|
+
canvas.mouse.wheelTimer = true; //Accumulate wheel scroll (prevents too many events backing up)
|
23
|
+
defaultMouse = document.mouse = canvas.mouse;
|
24
|
+
|
25
|
+
//Attach viewer object to canvas
|
26
|
+
canvas.viewer = viewer;
|
27
|
+
|
28
|
+
//Disable context menu and prevent bubbling
|
29
|
+
canvas.addEventListener('contextmenu', e => {e.preventDefault(); e.stopPropagation();});
|
30
|
+
|
31
|
+
//Command callback function
|
32
|
+
viewer.command = cmd_callback;
|
33
|
+
|
34
|
+
//Data dict and colourmap names stored in globals
|
35
|
+
viewer.dict = window.dictionary;
|
36
|
+
viewer.defaultcolourmaps = window.defaultcolourmaps;
|
37
|
+
|
38
|
+
//Enable to forward key presses to server directly
|
39
|
+
//(only used for full size viewers)
|
40
|
+
if (fixedsize == 1)
|
41
|
+
document.addEventListener('keyup', function(event) {keyPress(event, viewer);});
|
42
|
+
|
43
|
+
return viewer;
|
44
|
+
}
|
45
|
+
|
46
|
+
function keyModifiers(event) {
|
47
|
+
modifiers = '';
|
48
|
+
if (event.ctrlKey) modifiers += 'C';
|
49
|
+
if (event.shiftKey) modifiers += 'S';
|
50
|
+
if (event.altKey) modifiers += 'A';
|
51
|
+
if (event.metaKey) modifiers += 'M';
|
52
|
+
return modifiers;
|
53
|
+
}
|
54
|
+
|
55
|
+
function keyPress(event, viewer) {
|
56
|
+
// space and arrow keys, prevent scrolling
|
57
|
+
if ([' ', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key))
|
58
|
+
event.preventDefault();
|
59
|
+
|
60
|
+
if (event.target.tagName == 'INPUT') return;
|
61
|
+
|
62
|
+
//Special key codes
|
63
|
+
if (event.key == 'ArrowUp') key = 17;
|
64
|
+
else if (event.key == 'ArrowDown') key = 18;
|
65
|
+
else if (event.key == 'ArrowLeft') key = 20;
|
66
|
+
else if (event.key == 'ArrowRight') key = 19;
|
67
|
+
else if (event.key == 'PageUp') key = 24;
|
68
|
+
else if (event.key == 'PageDown') key = 25;
|
69
|
+
else if (event.key == 'PageUp') key = 24;
|
70
|
+
else if (event.key == 'Home') key = 22;
|
71
|
+
else if (event.key == 'End') key = 23;
|
72
|
+
else if (event.key == 'Backspace') key = 8;
|
73
|
+
else if (event.key == 'Tab') key = 9;
|
74
|
+
else if (event.key == 'Escape') key = 27;
|
75
|
+
else if (event.key == 'Enter') key = 13;
|
76
|
+
else if (event.key == 'Insert') key = 21;
|
77
|
+
else if (event.key == 'Delete') key = 127;
|
78
|
+
else if (event.key[0] == 'F' && event.key.length > 1)
|
79
|
+
key = 189 + parseInt(event.key.substr(1));
|
80
|
+
else if (event.key.length == 1) {
|
81
|
+
key = event.key.charCodeAt(0);
|
82
|
+
if (key > 127 && event.code.length == 4 && event.code.substr(0,3) == 'Key')
|
83
|
+
key = event.code.charCodeAt(3);
|
84
|
+
} else
|
85
|
+
return;
|
86
|
+
|
87
|
+
//console.log(event.key);
|
88
|
+
//console.log(event.code);
|
89
|
+
//console.log(key);
|
90
|
+
|
91
|
+
var modifiers = keyModifiers(event);
|
92
|
+
|
93
|
+
//Ignore CTRL+R, CTRL+SHIFT+R
|
94
|
+
if (key == 114 && modifiers == 'C' || key == 82 && modifiers == 'CS') return;
|
95
|
+
|
96
|
+
cmd = 'key key=' + key + ',modifiers=' + modifiers + ",x=" + defaultMouse.x + ",y=" + defaultMouse.y;
|
97
|
+
//console.log(cmd);
|
98
|
+
viewer.command(cmd);
|
99
|
+
}
|
100
|
+
|
101
|
+
function canvasBoxMouseClick(event, mouse) {
|
102
|
+
mouse.element.viewer.check_context(mouse);
|
103
|
+
|
104
|
+
if (mouse.element.viewer.rotating)
|
105
|
+
mouse.element.viewer.command('' + mouse.element.viewer.getRotationString());
|
106
|
+
else
|
107
|
+
mouse.element.viewer.command('' + mouse.element.viewer.getTranslationString());
|
108
|
+
|
109
|
+
if (mouse.element.viewer.rotating) {
|
110
|
+
mouse.element.viewer.rotating = false;
|
111
|
+
//mouse.element.viewer.reload = true;
|
112
|
+
}
|
113
|
+
|
114
|
+
//Clear the webgl box
|
115
|
+
mouse.element.viewer.clear();
|
116
|
+
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
|
120
|
+
function canvasBoxMouseDown(event, mouse) {
|
121
|
+
mouse.element.viewer.check_context(mouse);
|
122
|
+
//Just draw so the box appears
|
123
|
+
mouse.element.viewer.draw();
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
|
127
|
+
var hideBoxTimer;
|
128
|
+
|
129
|
+
function canvasBoxMouseMove(event, mouse) {
|
130
|
+
//GUI elements to show on mouseover
|
131
|
+
if (mouse.element && mouse.element.viewer && Object.keys(mouse.element.viewer.vis).length) {
|
132
|
+
var gui = mouse.element.viewer.gui;
|
133
|
+
var rect = mouse.element.getBoundingClientRect();
|
134
|
+
x = event.clientX-rect.left;
|
135
|
+
y = event.clientY-rect.top;
|
136
|
+
if (x >= 0 && y >= 0 && x < rect.width && y < rect.height) {
|
137
|
+
if (!gui && mouse.element.imgtarget)
|
138
|
+
mouse.element.imgtarget.nextElementSibling.style.display = "block";
|
139
|
+
|
140
|
+
if (gui) {
|
141
|
+
if (mouse.element.imgtarget) mouse.element.imgtarget.nextElementSibling.style.display = "none";
|
142
|
+
gui.domElement.style.display = "block";
|
143
|
+
}
|
144
|
+
|
145
|
+
if (hideBoxTimer)
|
146
|
+
clearTimeout(hideBoxTimer);
|
147
|
+
|
148
|
+
hideBoxTimer = setTimeout(function () { hideMenu(mouse.element, gui);}, 1000 );
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
if (!mouse.isdown || !mouse.element.viewer) return true;
|
153
|
+
mouse.element.viewer.rotating = false;
|
154
|
+
|
155
|
+
//Switch buttons for translate/rotate
|
156
|
+
var button = mouse.button;
|
157
|
+
|
158
|
+
if (mouse.element.viewer.mode == "Translate") {
|
159
|
+
//Swap rotate/translate buttons
|
160
|
+
if (button == 0)
|
161
|
+
button = 2
|
162
|
+
else if (button == 2)
|
163
|
+
button = 0;
|
164
|
+
} else if (button==0 && mouse.element.viewer.mode == "Zoom") {
|
165
|
+
button = 100;
|
166
|
+
}
|
167
|
+
|
168
|
+
//console.log(mouse.deltaX + "," + mouse.deltaY);
|
169
|
+
switch (button)
|
170
|
+
{
|
171
|
+
case 0:
|
172
|
+
mouse.element.viewer.rotateY(mouse.deltaX/5);
|
173
|
+
mouse.element.viewer.rotateX(mouse.deltaY/5);
|
174
|
+
mouse.element.viewer.rotating = true;
|
175
|
+
break;
|
176
|
+
case 1:
|
177
|
+
mouse.element.viewer.rotateZ(Math.sqrt(mouse.deltaX*mouse.deltaX + mouse.deltaY*mouse.deltaY)/5);
|
178
|
+
mouse.element.viewer.rotating = true;
|
179
|
+
break;
|
180
|
+
case 2:
|
181
|
+
var adjust = mouse.element.viewer.modelsize / 1000; //1/1000th of size
|
182
|
+
mouse.element.viewer.translate[0] += mouse.deltaX * adjust;
|
183
|
+
mouse.element.viewer.translate[1] -= mouse.deltaY * adjust;
|
184
|
+
break;
|
185
|
+
case 100:
|
186
|
+
var adjust = mouse.element.viewer.modelsize / 1000; //1/1000th of size
|
187
|
+
mouse.element.viewer.translate[2] += mouse.deltaX * adjust;
|
188
|
+
break;
|
189
|
+
}
|
190
|
+
|
191
|
+
mouse.element.viewer.draw();
|
192
|
+
|
193
|
+
//Instant updates
|
194
|
+
if (mouse.element.viewer.alwaysdraw) {
|
195
|
+
if (mouse.element.viewer.rotating)
|
196
|
+
mouse.element.viewer.command('' + mouse.element.viewer.getRotationString());
|
197
|
+
else
|
198
|
+
mouse.element.viewer.command('' + mouse.element.viewer.getTranslationString());
|
199
|
+
}
|
200
|
+
|
201
|
+
return false;
|
202
|
+
}
|
203
|
+
|
204
|
+
function canvasBoxMouseWheel(event, mouse) {
|
205
|
+
mouse.element.viewer.check_context(mouse);
|
206
|
+
var scroll = event.deltaY < 0 ? 0.01 : -0.01;
|
207
|
+
//console.log(event.shiftKey,event.deltaY * -0.01);
|
208
|
+
if (event.shiftKey) {
|
209
|
+
mouse.element.viewer.zoomClip(scroll);
|
210
|
+
//mouse.element.viewer.zoomClip(event.spin*0.01);
|
211
|
+
} else {
|
212
|
+
mouse.element.viewer.zoom(scroll);
|
213
|
+
//mouse.element.viewer.zoom(event.spin*0.01);
|
214
|
+
}
|
215
|
+
return false; //Prevent default
|
216
|
+
}
|
217
|
+
|
218
|
+
function canvasBoxMousePinch(event, mouse) {
|
219
|
+
mouse.element.viewer.check_context(mouse);
|
220
|
+
if (event.distance != 0) {
|
221
|
+
var factor = event.distance * 0.0001;
|
222
|
+
mouse.element.viewer.zoom(factor);
|
223
|
+
//Clear the box after a second
|
224
|
+
setTimeout(function() {mouse.element.viewer.clear();}, 1000);
|
225
|
+
}
|
226
|
+
return false; //Prevent default
|
227
|
+
}
|
228
|
+
|
229
|
+
//This object encapsulates a vertex buffer and shader set
|
230
|
+
function BoxRenderer(gl, colour) {
|
231
|
+
this.gl = gl;
|
232
|
+
if (colour)
|
233
|
+
this.colour = colour;
|
234
|
+
else
|
235
|
+
this.colour = [0.5, 0.5, 0.5, 1.0];
|
236
|
+
|
237
|
+
//Line renderer
|
238
|
+
this.attribSizes = [3 * Float32Array.BYTES_PER_ELEMENT];
|
239
|
+
|
240
|
+
this.elements = 0;
|
241
|
+
this.elementSize = 0;
|
242
|
+
for (var i=0; i<this.attribSizes.length; i++)
|
243
|
+
this.elementSize += this.attribSizes[i];
|
244
|
+
}
|
245
|
+
|
246
|
+
var vs = "precision highp float; \n \
|
247
|
+
attribute vec3 aVertexPosition; \n \
|
248
|
+
attribute vec4 aVertexColour; \n \
|
249
|
+
uniform mat4 uMVMatrix; \n \
|
250
|
+
uniform mat4 uPMatrix; \n \
|
251
|
+
uniform vec4 uColour; \n \
|
252
|
+
varying vec4 vColour; \n \
|
253
|
+
void main(void) \n \
|
254
|
+
{ \n \
|
255
|
+
vec4 mvPosition = uMVMatrix * vec4(aVertexPosition, 1.0); \n \
|
256
|
+
gl_Position = uPMatrix * mvPosition; \n \
|
257
|
+
vColour = uColour; \n \
|
258
|
+
}";
|
259
|
+
|
260
|
+
var fs = "precision highp float; \n \
|
261
|
+
varying vec4 vColour; \n \
|
262
|
+
void main(void) \n \
|
263
|
+
{ \n \
|
264
|
+
gl_FragColor = vColour; \n \
|
265
|
+
}";
|
266
|
+
|
267
|
+
BoxRenderer.prototype.init = function() {
|
268
|
+
//Compile the shaders
|
269
|
+
this.program = new WebGLProgram(this.gl, vs, fs);
|
270
|
+
if (this.program.errors) console.log(this.program.errors);
|
271
|
+
//Setup attribs/uniforms (flag set to skip enabling attribs)
|
272
|
+
this.program.setup(undefined, undefined, true);
|
273
|
+
|
274
|
+
return true;
|
275
|
+
}
|
276
|
+
|
277
|
+
BoxRenderer.prototype.updateBuffers = function(view) {
|
278
|
+
//Create buffer if not yet allocated
|
279
|
+
if (this.vertexBuffer == undefined) {
|
280
|
+
//Init shaders etc...
|
281
|
+
if (!this.init()) return;
|
282
|
+
this.vertexBuffer = this.gl.createBuffer();
|
283
|
+
this.indexBuffer = this.gl.createBuffer();
|
284
|
+
}
|
285
|
+
|
286
|
+
//Bind buffers
|
287
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
|
288
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
289
|
+
|
290
|
+
this.box(view.min, view.max);
|
291
|
+
}
|
292
|
+
|
293
|
+
BoxRenderer.prototype.box = function(min, max) {
|
294
|
+
var zero = [min[0]+0.5*(max[0] - min[0]), min[1]+0.5*(max[1] - min[1]), min[2]+0.5*(max[2] - min[2])];
|
295
|
+
var min10 = [min[0] + 0.45*(max[0] - min[0]), min[1]+0.45*(max[1] - min[1]), min[2]+0.45*(max[2] - min[2])];
|
296
|
+
var max10 = [min[0] + 0.55*(max[0] - min[0]), min[1]+0.55*(max[1] - min[1]), min[2]+0.55*(max[2] - min[2])];
|
297
|
+
var vertices = new Float32Array(
|
298
|
+
[
|
299
|
+
/* Bounding box */
|
300
|
+
min[0], min[1], max[2],
|
301
|
+
min[0], max[1], max[2],
|
302
|
+
max[0], max[1], max[2],
|
303
|
+
max[0], min[1], max[2],
|
304
|
+
min[0], min[1], min[2],
|
305
|
+
min[0], max[1], min[2],
|
306
|
+
max[0], max[1], min[2],
|
307
|
+
max[0], min[1], min[2],
|
308
|
+
/* 10% box */
|
309
|
+
min10[0], min10[1], max10[2],
|
310
|
+
min10[0], max10[1], max10[2],
|
311
|
+
max10[0], max10[1], max10[2],
|
312
|
+
max10[0], min10[1], max10[2],
|
313
|
+
min10[0], min10[1], min10[2],
|
314
|
+
min10[0], max10[1], min10[2],
|
315
|
+
max10[0], max10[1], min10[2],
|
316
|
+
max10[0], min10[1], min10[2],
|
317
|
+
/* Axis lines */
|
318
|
+
min[0], zero[1], zero[2],
|
319
|
+
max[0], zero[1], zero[2],
|
320
|
+
zero[0], min[1], zero[2],
|
321
|
+
zero[0], max[1], zero[2],
|
322
|
+
zero[0], zero[1], min[2],
|
323
|
+
zero[0], zero[1], max[2]
|
324
|
+
]);
|
325
|
+
|
326
|
+
var indices = new Uint16Array(
|
327
|
+
[
|
328
|
+
/* Bounding box */
|
329
|
+
0, 1, 1, 2, 2, 3, 3, 0,
|
330
|
+
4, 5, 5, 6, 6, 7, 7, 4,
|
331
|
+
0, 4, 3, 7, 1, 5, 2, 6,
|
332
|
+
/* 10% box */
|
333
|
+
8, 9, 9, 10, 10, 11, 11, 8,
|
334
|
+
12, 13, 13, 14, 14, 15, 15, 12,
|
335
|
+
8, 12, 11, 15, 9, 13, 10, 14,
|
336
|
+
/* Axis lines */
|
337
|
+
16, 17,
|
338
|
+
18, 19,
|
339
|
+
20, 21
|
340
|
+
]
|
341
|
+
);
|
342
|
+
this.gl.bufferData(this.gl.ARRAY_BUFFER, vertices, this.gl.STATIC_DRAW);
|
343
|
+
this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, indices, this.gl.STATIC_DRAW);
|
344
|
+
this.elements = 24+24+6;
|
345
|
+
}
|
346
|
+
|
347
|
+
BoxRenderer.prototype.draw = function(webgl, nobox, noaxes) {
|
348
|
+
if (!this.elements) return;
|
349
|
+
|
350
|
+
if (this.program.attributes["aVertexPosition"] == undefined) return; //Require vertex buffer
|
351
|
+
|
352
|
+
webgl.use(this.program);
|
353
|
+
webgl.setMatrices();
|
354
|
+
|
355
|
+
//Bind buffers
|
356
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer);
|
357
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
|
358
|
+
|
359
|
+
//Enable attributes
|
360
|
+
for (var key in this.program.attributes)
|
361
|
+
this.gl.enableVertexAttribArray(this.program.attributes[key]);
|
362
|
+
|
363
|
+
|
364
|
+
//Line box render
|
365
|
+
this.gl.vertexAttribPointer(this.program.attributes["aVertexPosition"], 3, this.gl.FLOAT, false, 0, 0);
|
366
|
+
//Bounding box
|
367
|
+
if (!nobox) {
|
368
|
+
this.gl.uniform4f(this.program.uniforms["uColour"], this.colour[0], this.colour[1], this.colour[2], this.colour[3]);
|
369
|
+
this.gl.drawElements(this.gl.LINES, 24, this.gl.UNSIGNED_SHORT, 0);
|
370
|
+
}
|
371
|
+
|
372
|
+
//10% box (always draw)
|
373
|
+
this.gl.drawElements(this.gl.LINES, 24, this.gl.UNSIGNED_SHORT, 24 * 2);
|
374
|
+
|
375
|
+
//Axes (2 bytes per unsigned short)
|
376
|
+
if (!noaxes) {
|
377
|
+
this.gl.uniform4f(this.program.uniforms["uColour"], 1.0, 0.0, 0.0, 1.0);
|
378
|
+
this.gl.drawElements(this.gl.LINES, 2, this.gl.UNSIGNED_SHORT, (24+24) * 2);
|
379
|
+
this.gl.uniform4f(this.program.uniforms["uColour"], 0.0, 1.0, 0.0, 1.0);
|
380
|
+
this.gl.drawElements(this.gl.LINES, 2, this.gl.UNSIGNED_SHORT, (24+24+2) * 2);
|
381
|
+
this.gl.uniform4f(this.program.uniforms["uColour"], 0.0, 0.0, 1.0, 1.0);
|
382
|
+
this.gl.drawElements(this.gl.LINES, 2, this.gl.UNSIGNED_SHORT, (24+24+4) * 2);
|
383
|
+
}
|
384
|
+
|
385
|
+
//Disable attribs
|
386
|
+
for (var key in this.program.attributes)
|
387
|
+
this.gl.disableVertexAttribArray(this.program.attributes[key]);
|
388
|
+
|
389
|
+
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
|
390
|
+
this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, null);
|
391
|
+
this.gl.useProgram(null);
|
392
|
+
}
|
393
|
+
|
394
|
+
//This object holds the viewer details and calls the renderers
|
395
|
+
function BoxViewer(canvas) {
|
396
|
+
this.vis = {};
|
397
|
+
this.vis.objects = [];
|
398
|
+
this.vis.colourmaps = [];
|
399
|
+
this.canvas = canvas;
|
400
|
+
if (!canvas) {alert("Invalid Canvas"); return;}
|
401
|
+
try {
|
402
|
+
this.webgl = new WebGL(this.canvas, {antialias: true}); //, premultipliedAlpha: false});
|
403
|
+
this.gl = this.webgl.gl;
|
404
|
+
canvas.addEventListener("webglcontextlost", function(event) {
|
405
|
+
//event.preventDefault();
|
406
|
+
console.log("Context loss detected, clearing data/state and flagging defunct");
|
407
|
+
this.mouse.lostContext = true;
|
408
|
+
}, false);
|
409
|
+
|
410
|
+
canvas.addEventListener("webglcontextrestored", function(event) {
|
411
|
+
console.log("Context restored");
|
412
|
+
}, false);
|
413
|
+
|
414
|
+
} catch(e) {
|
415
|
+
//No WebGL
|
416
|
+
console.log("No WebGL: " + e);
|
417
|
+
}
|
418
|
+
|
419
|
+
this.rotating = false;
|
420
|
+
this.translate = [0,0,0];
|
421
|
+
this.rotate = quat4.create();
|
422
|
+
quat4.identity(this.rotate);
|
423
|
+
this.fov = 45;
|
424
|
+
this.focus = [0,0,0];
|
425
|
+
this.centre = [0,0,0];
|
426
|
+
this.near_clip = this.far_clip = 0.0;
|
427
|
+
this.modelsize = 1;
|
428
|
+
this.scale = [1, 1, 1];
|
429
|
+
this.orientation = 1.0; //1.0 for RH, -1.0 for LH
|
430
|
+
|
431
|
+
//Non-persistant settings
|
432
|
+
this.mode = 'Rotate';
|
433
|
+
this.alwaysdraw = false;
|
434
|
+
if (!this.gl) return;
|
435
|
+
|
436
|
+
//Create the renderers
|
437
|
+
this.border = new BoxRenderer(this.gl, [0.5,0.5,0.5,1]);
|
438
|
+
|
439
|
+
this.gl.enable(this.gl.DEPTH_TEST);
|
440
|
+
this.gl.depthFunc(this.gl.LEQUAL);
|
441
|
+
//this.gl.depthMask(this.gl.FALSE);
|
442
|
+
this.gl.enable(this.gl.BLEND);
|
443
|
+
//this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
|
444
|
+
//this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ZERO, this.gl.ONE);
|
445
|
+
this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
|
446
|
+
}
|
447
|
+
|
448
|
+
BoxViewer.prototype.check_context = function(mouse) {
|
449
|
+
if (mouse.lostContext) {
|
450
|
+
console.log("Recreating viewer after context loss");
|
451
|
+
//Reinit on timer? on click?
|
452
|
+
var box = initBox(this.canvas.imgtarget, this.command);
|
453
|
+
box.loadFile(JSON.stringify(this.vis));
|
454
|
+
//Delete the old canvas now
|
455
|
+
this.canvas.parentNode.removeChild(this.canvas);
|
456
|
+
this.vis = null;
|
457
|
+
mouse.lostContext = false;
|
458
|
+
this.deleted = true;
|
459
|
+
}
|
460
|
+
}
|
461
|
+
|
462
|
+
BoxViewer.prototype.reload = function() {
|
463
|
+
this.command('reload');
|
464
|
+
}
|
465
|
+
|
466
|
+
BoxViewer.prototype.checkPointMinMax = function(coord) {
|
467
|
+
for (var i=0; i<3; i++) {
|
468
|
+
this.view.min[i] = Math.min(coord[i], this.view.min[i]);
|
469
|
+
this.view.max[i] = Math.max(coord[i], this.view.max[i]);
|
470
|
+
}
|
471
|
+
//console.log(JSON.stringify(this.view.min) + " -- " + JSON.stringify(this.view.max));
|
472
|
+
}
|
473
|
+
|
474
|
+
function Merge(obj1, obj2) {
|
475
|
+
for (var p in obj2) {
|
476
|
+
try {
|
477
|
+
//console.log(p + " ==> " + obj2[p].constructor);
|
478
|
+
// Property in destination object set; update its value.
|
479
|
+
if (!obj2.hasOwnProperty(p)) continue;
|
480
|
+
if (obj2[p].constructor == Object || obj2[p].constructor == Array) {
|
481
|
+
obj1[p] = Merge(obj1[p], obj2[p]);
|
482
|
+
} else {
|
483
|
+
//Just copy
|
484
|
+
obj1[p] = obj2[p];
|
485
|
+
}
|
486
|
+
} catch(e) {
|
487
|
+
// Property in destination object not set; create it and set its value.
|
488
|
+
obj1[p] = obj2[p];
|
489
|
+
}
|
490
|
+
}
|
491
|
+
|
492
|
+
//Clear any keys in obj1 that are not in obj2
|
493
|
+
for (var p in obj1) {
|
494
|
+
if (!obj1.hasOwnProperty(p)) continue; //Ignore built in keys
|
495
|
+
if (p in obj2) continue;
|
496
|
+
if (typeof(obj1[p]) != 'object') {
|
497
|
+
//Just delete
|
498
|
+
delete obj1[p]
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
return obj1;
|
503
|
+
}
|
504
|
+
|
505
|
+
BoxViewer.prototype.exportJson = function(nocam) {
|
506
|
+
return {"objects" : this.exportObjects(),
|
507
|
+
"colourmaps" : this.vis.colourmaps,
|
508
|
+
"views" : this.exportView(nocam),
|
509
|
+
"properties" : this.vis.properties};
|
510
|
+
}
|
511
|
+
|
512
|
+
BoxViewer.prototype.sendState = function(reload) {
|
513
|
+
var exp = this.exportJson();
|
514
|
+
exp.reload = reload;
|
515
|
+
this.command(JSON.stringify(exp));
|
516
|
+
}
|
517
|
+
|
518
|
+
BoxViewer.prototype.toString = function(nocam, reload, noformat) {
|
519
|
+
var exp = this.exportJson(nocam);
|
520
|
+
exp.exported = true;
|
521
|
+
exp.reload = reload ? true : false;
|
522
|
+
|
523
|
+
if (noformat)
|
524
|
+
return JSON.stringify(exp);
|
525
|
+
//Export with 2 space indentation
|
526
|
+
return JSON.stringify(exp, undefined, 2);
|
527
|
+
}
|
528
|
+
|
529
|
+
BoxViewer.prototype.exportView = function(nocam) {
|
530
|
+
//Update camera settings of current view
|
531
|
+
if (nocam)
|
532
|
+
this.view = {};
|
533
|
+
else {
|
534
|
+
this.view.rotate = this.getRotation();
|
535
|
+
this.view.focus = this.focus;
|
536
|
+
this.view.translate = this.translate;
|
537
|
+
//Allows scale editing in UI by commenting this
|
538
|
+
//this.view.scale = this.scale;
|
539
|
+
}
|
540
|
+
/*
|
541
|
+
* If we overwrite these, changes from menu will not apply
|
542
|
+
*
|
543
|
+
this.view.fov = this.fov;
|
544
|
+
this.view.near = this.near_clip;
|
545
|
+
this.view.far = this.far_clip;
|
546
|
+
this.view.border = this.showBorder ? 1 : 0;
|
547
|
+
this.view.axis = this.axes;
|
548
|
+
*/
|
549
|
+
//this.view.background = this.background.toString();
|
550
|
+
|
551
|
+
//Never export min/max
|
552
|
+
var V = Object.assign(this.view);
|
553
|
+
V.min = undefined;
|
554
|
+
V.max = undefined;
|
555
|
+
return [V];
|
556
|
+
}
|
557
|
+
|
558
|
+
BoxViewer.prototype.exportObjects = function() {
|
559
|
+
objs = [];
|
560
|
+
for (var id in this.vis.objects) {
|
561
|
+
objs[id] = {};
|
562
|
+
//Skip geometry data
|
563
|
+
for (var type in this.vis.objects[id]) {
|
564
|
+
if (type != "triangles" && type != "points" && type != 'lines' && type != "volume") {
|
565
|
+
objs[id][type] = this.vis.objects[id][type];
|
566
|
+
}
|
567
|
+
}
|
568
|
+
}
|
569
|
+
//console.log("OBJECTS: " + JSON.stringify(objs));
|
570
|
+
|
571
|
+
return objs;
|
572
|
+
}
|
573
|
+
|
574
|
+
BoxViewer.prototype.exportColourMaps = function() {
|
575
|
+
return this.vis.colourmaps;
|
576
|
+
//DEPRECATED
|
577
|
+
//Below extracts map from gui editor, not needed unless editor hasn't updated vis object
|
578
|
+
cmaps = [];
|
579
|
+
if (this.vis.colourmaps) {
|
580
|
+
for (var i=0; i<this.vis.colourmaps.length; i++) {
|
581
|
+
console.log(i + " - " + this.vis.colourmaps[i].palette);
|
582
|
+
if (!this.vis.colourmaps[i].palette) continue;
|
583
|
+
cmaps[i] = this.vis.colourmaps[i].palette.get();
|
584
|
+
//Copy additional properties
|
585
|
+
for (var type in this.vis.colourmaps[i]) {
|
586
|
+
if (type != "palette" && type != "colours")
|
587
|
+
cmaps[i][type] = this.vis.colourmaps[i][type];
|
588
|
+
}
|
589
|
+
}
|
590
|
+
}
|
591
|
+
return cmaps;
|
592
|
+
}
|
593
|
+
|
594
|
+
BoxViewer.prototype.exportFile = function() {
|
595
|
+
window.open('data:text/plain;base64,' + window.btoa(this.toString(false, true)));
|
596
|
+
}
|
597
|
+
|
598
|
+
BoxViewer.prototype.loadFile = function(source) {
|
599
|
+
//Skip update to rotate/translate etc if in process of updating
|
600
|
+
//if (document.mouse.isdown) return;
|
601
|
+
if (source.length < 3) {
|
602
|
+
console.log('Invalid source data, ignoring');
|
603
|
+
console.log(source);
|
604
|
+
console.log(BoxViewer.prototype.loadFile.caller);
|
605
|
+
return; //Invalid
|
606
|
+
}
|
607
|
+
|
608
|
+
if (!this.vis)
|
609
|
+
this.vis = {};
|
610
|
+
|
611
|
+
//Parse data
|
612
|
+
var src = {};
|
613
|
+
try {
|
614
|
+
src = JSON.parse(source);
|
615
|
+
} catch(e) {
|
616
|
+
console.log(source);
|
617
|
+
console.log("Parse Error: " + e);
|
618
|
+
return;
|
619
|
+
}
|
620
|
+
|
621
|
+
//Before merge, delete all colourmap data if changed
|
622
|
+
for (var c in this.vis.colourmaps) {
|
623
|
+
//Name or colour count mismatch? delete so can be recreated
|
624
|
+
if (this.vis.colourmaps[c].name != src.colourmaps[c].name ||
|
625
|
+
this.vis.colourmaps[c].colours.length != src.colourmaps[c].colours.length) {
|
626
|
+
//Delete the colourmap folder for this map, will be re-created
|
627
|
+
if (this.cgui && this.cgui.__folders[this.vis.colourmaps[c].name]) {
|
628
|
+
this.cgui.removeFolder(this.cgui.__folders[this.vis.colourmaps[c].name]);
|
629
|
+
this.cgui.__folders[this.vis.colourmaps[c].name] = undefined;
|
630
|
+
}
|
631
|
+
//Clear all the colours so new data will replace in merge
|
632
|
+
this.vis.colourmaps[c].colours = undefined;
|
633
|
+
}
|
634
|
+
}
|
635
|
+
|
636
|
+
//Merge keys - preserves original objects for gui access
|
637
|
+
Merge(this.vis, src);
|
638
|
+
|
639
|
+
//No model loaded? Prevents errors so default are loaded
|
640
|
+
if (!this.vis.properties)
|
641
|
+
this.vis.properties = {};
|
642
|
+
|
643
|
+
//Set active view (always first for now)
|
644
|
+
this.view = this.vis.views[0];
|
645
|
+
|
646
|
+
//Always set a bounding box, get from objects if not in view
|
647
|
+
var objbb = false;
|
648
|
+
if (!this.view.min || !this.view.max) {
|
649
|
+
this.view.min = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
|
650
|
+
this.view.max = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
|
651
|
+
objbb = true;
|
652
|
+
}
|
653
|
+
//console.log(JSON.stringify(this.view,null, 2));
|
654
|
+
|
655
|
+
if (this.view) {
|
656
|
+
this.fov = this.view.fov || 45;
|
657
|
+
this.near_clip = this.view.near || 0;
|
658
|
+
this.far_clip = this.view.far || 0;
|
659
|
+
this.orientation = this.view.orientation || 1;
|
660
|
+
this.axes = this.view.axis == undefined ? true : this.view.axis;
|
661
|
+
}
|
662
|
+
|
663
|
+
if (this.vis.properties.resolution && this.vis.properties.resolution[0] && this.vis.properties.resolution[1]) {
|
664
|
+
this.width = this.vis.properties.resolution[0];
|
665
|
+
this.height = this.vis.properties.resolution[1];
|
666
|
+
//this.canvas.style.width = "";
|
667
|
+
//this.canvas.style.height = "";
|
668
|
+
} else {
|
669
|
+
//Autosize when img loaded
|
670
|
+
this.width = 0;
|
671
|
+
this.height = 0;
|
672
|
+
}
|
673
|
+
|
674
|
+
//Process object data
|
675
|
+
for (var id in this.vis.objects) {
|
676
|
+
//Apply object bounding box
|
677
|
+
if (objbb && this.vis.objects[id].min)
|
678
|
+
this.checkPointMinMax(this.vis.objects[id].min);
|
679
|
+
if (objbb && this.vis.objects[id].max)
|
680
|
+
this.checkPointMinMax(this.vis.objects[id].max);
|
681
|
+
}
|
682
|
+
|
683
|
+
this.updateDims(this.view);
|
684
|
+
|
685
|
+
//Update display
|
686
|
+
if (!this.gl) return;
|
687
|
+
this.draw();
|
688
|
+
this.clear();
|
689
|
+
|
690
|
+
//Create UI - disable by omitting dat.gui.min.js
|
691
|
+
//(If menu doesn't exist or is open, update immediately
|
692
|
+
// otherwise, wait until clicked/opened as update is slow)
|
693
|
+
if (!this.gui || !this.gui.closed)
|
694
|
+
this.menu();
|
695
|
+
else
|
696
|
+
this.reloadgui = true;
|
697
|
+
|
698
|
+
//Copy updated values to all generated controls
|
699
|
+
//This allows data changed by other means (ie: through python) to
|
700
|
+
// be reflected in the HTML control values
|
701
|
+
var that = this;
|
702
|
+
var getValWithIndex = function(prop, val, idx) {
|
703
|
+
if (val === undefined) return;
|
704
|
+
if (idx != null && idx >= 0 && val.length > idx)
|
705
|
+
val = val[idx];
|
706
|
+
//Round off floating point numbers
|
707
|
+
if (that.dict[property].type.indexOf('real') >= 0)
|
708
|
+
return val.toFixed(6) / 1; //toFixed() / 1 rounds to fixed position and removes trailing zeros
|
709
|
+
return val;
|
710
|
+
}
|
711
|
+
var pel = this.canvas.parentElement.parentElement;
|
712
|
+
var children = pel.getElementsByTagName('*');
|
713
|
+
for (var i=0; i<children.length; i++) {
|
714
|
+
//Process property controls
|
715
|
+
var id = children[i].id;
|
716
|
+
if (id.indexOf('lvctrl') >= 0) {
|
717
|
+
var target = children[i].getAttribute("data-target");
|
718
|
+
var property = children[i].getAttribute("data-property");
|
719
|
+
var idx = children[i].getAttribute("data-index");
|
720
|
+
//console.log(id + " : " + target + " : " + property + " [" + idx + "]");
|
721
|
+
if (property) {
|
722
|
+
if (target) {
|
723
|
+
//Loop through objects, find those whose name matches element target
|
724
|
+
for (var o in this.vis.objects) {
|
725
|
+
var val = this.vis.objects[o][property];
|
726
|
+
if (val === undefined) continue;
|
727
|
+
if (this.vis.objects[o].name == target) {
|
728
|
+
if (idx != null && idx >= 0)
|
729
|
+
val = val[idx];
|
730
|
+
//console.log(target + " ==> SET " + id + " : ['" + property + "'] VALUE TO " + val);
|
731
|
+
//console.log("TAG: " + children[i].tagName);
|
732
|
+
if (children[i].type == 'checkbox')
|
733
|
+
children[i].checked = val;
|
734
|
+
else if (children[i].tagName == 'SELECT') {
|
735
|
+
//If integer, set by index, otherwise set by value
|
736
|
+
var parsed = parseInt(val);
|
737
|
+
if (isNaN(parsed) || parsed.toString() != val) {
|
738
|
+
//If the value is in the options, set it, otherwise leave as is
|
739
|
+
for (var c in children[i].options) {
|
740
|
+
if (children[i].options[c].value == val) {
|
741
|
+
children[i].selectedIndex = c;
|
742
|
+
break;
|
743
|
+
}
|
744
|
+
}
|
745
|
+
} else {
|
746
|
+
children[i].selectedIndex = parsed;
|
747
|
+
}
|
748
|
+
} else if (children[i].tagName == 'DIV') {
|
749
|
+
children[i].style.background = new Colour(val).html();
|
750
|
+
} else if (children[i].tagName == 'CANVAS' && children[i].gradient) {
|
751
|
+
//Store full list of colourmaps
|
752
|
+
var el = children[i]; //canvas element
|
753
|
+
//Ensure we aren't merging the same object
|
754
|
+
el.colourmaps = Merge(JSON.parse(JSON.stringify(el.colourmaps)), this.vis.colourmaps);
|
755
|
+
//Load the initial colourmap
|
756
|
+
if (!el.selectedIndex >= 0) {
|
757
|
+
//Get the selected index from the property value
|
758
|
+
for (var c in this.vis.colourmaps) {
|
759
|
+
if (this.vis.colourmaps[c].name == val) {
|
760
|
+
el.selectedIndex = c;
|
761
|
+
//Copy, don't link
|
762
|
+
el.currentmap = JSON.parse(JSON.stringify(this.vis.colourmaps[c]));
|
763
|
+
//el.currentmap = Object.assign(this.vis.colourmaps[c]);
|
764
|
+
break;
|
765
|
+
}
|
766
|
+
}
|
767
|
+
}
|
768
|
+
//Can't use gradient.read() here as it triggers another state load,
|
769
|
+
//looping infinitely - so load the palette change directly
|
770
|
+
el.gradient.palette = new Palette(this.vis.colourmaps[el.selectedIndex].colours, true);
|
771
|
+
//el.gradient.reset(); //For some reason this screws up colour editing
|
772
|
+
el.gradient.update(true); //Update without triggering callback that triggers a state reload
|
773
|
+
} else {
|
774
|
+
//console.log(id + " : " + target + " : " + property + " = " + val);
|
775
|
+
children[i].value = getValWithIndex(property, val, idx);
|
776
|
+
}
|
777
|
+
}
|
778
|
+
}
|
779
|
+
} else if (this.vis.views[0][property] != null) {
|
780
|
+
//View property
|
781
|
+
var val = this.vis.views[0][property];
|
782
|
+
//console.log("SET " + id + " : ['" + property + "'] VIEW PROP VALUE TO " + val);
|
783
|
+
children[i].value = getValWithIndex(property, val, idx);
|
784
|
+
} else if (this.vis.properties[property] != null) {
|
785
|
+
//Global property
|
786
|
+
var val = this.vis.properties[property];
|
787
|
+
//console.log("SET " + id + " : ['" + property + "'] GLOBAL VALUE TO " + val);
|
788
|
+
children[i].value = getValWithIndex(property, val, idx);
|
789
|
+
}
|
790
|
+
}
|
791
|
+
}
|
792
|
+
}
|
793
|
+
}
|
794
|
+
|
795
|
+
BoxViewer.prototype.menu = function() {
|
796
|
+
//Create UI - disable by omitting dat.gui.min.js
|
797
|
+
//This is slow! Don't call while animating
|
798
|
+
var viewer = this;
|
799
|
+
var changefn = function(value, reload) {
|
800
|
+
//console.log(JSON.stringify(Object.keys(this)));
|
801
|
+
//console.log(value);
|
802
|
+
if (reload == undefined) {
|
803
|
+
reload = true;
|
804
|
+
if (this.property && viewer.dict[this.property])
|
805
|
+
{
|
806
|
+
//Get reload level from prop dict
|
807
|
+
//console.log(this.property + " REDRAW: " + viewer.dict[this.property].redraw);
|
808
|
+
reload = viewer.dict[this.property].redraw;
|
809
|
+
}
|
810
|
+
}
|
811
|
+
|
812
|
+
//Sync state and reload
|
813
|
+
viewer.sendState(reload);
|
814
|
+
};
|
815
|
+
|
816
|
+
if (!this.gui || this.gui.closed) {
|
817
|
+
//Re-create from scratch if closed or not present
|
818
|
+
createMenu(this, changefn);
|
819
|
+
} else {
|
820
|
+
updateMenu(this, changefn);
|
821
|
+
}
|
822
|
+
this.reloadgui = false;
|
823
|
+
}
|
824
|
+
|
825
|
+
BoxViewer.prototype.clear = function() {
|
826
|
+
if (!this.gl) return;
|
827
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
828
|
+
}
|
829
|
+
|
830
|
+
BoxViewer.prototype.draw = function() {
|
831
|
+
if (!this.canvas) return;
|
832
|
+
|
833
|
+
//Get the dimensions from the current canvas
|
834
|
+
if (this.width != this.canvas.offsetWidth || this.height != this.canvas.offsetHeight) {
|
835
|
+
this.width = this.canvas.offsetWidth;
|
836
|
+
this.height = this.canvas.offsetHeight;
|
837
|
+
//Need to set this too for some reason
|
838
|
+
this.canvas.width = this.width;
|
839
|
+
this.canvas.height = this.height;
|
840
|
+
if (this.gl) {
|
841
|
+
this.gl.viewportWidth = this.width;
|
842
|
+
this.gl.viewportHeight = this.height;
|
843
|
+
this.webgl.viewport = new Viewport(0, 0, this.width, this.height);
|
844
|
+
}
|
845
|
+
}
|
846
|
+
|
847
|
+
/*/Attempt to prevent element resizing to 0,0 when a frame is dropped
|
848
|
+
if (this.width)
|
849
|
+
this.canvas.parentElement.style.minWidth = this.width + 'px';
|
850
|
+
if (this.height)
|
851
|
+
this.canvas.parentElement.style.minHeight = this.height + 'px';
|
852
|
+
*/
|
853
|
+
|
854
|
+
if (!this.gl) return;
|
855
|
+
|
856
|
+
//Check isContextLost
|
857
|
+
if (this.gl.isContextLost()) {
|
858
|
+
console.log("boxviewer_draw_: context lost");
|
859
|
+
this.mouse.lostContext = true;
|
860
|
+
return;
|
861
|
+
}
|
862
|
+
|
863
|
+
this.gl.viewport(0, 0, this.gl.viewportWidth, this.gl.viewportHeight);
|
864
|
+
//this.gl.clearColor(1, 1, 1, 0);
|
865
|
+
this.gl.clearColor(0, 0, 0, 0);
|
866
|
+
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
|
867
|
+
|
868
|
+
this.webgl.view(this);
|
869
|
+
|
870
|
+
//Render objects
|
871
|
+
this.border.draw(this.webgl, false, true);
|
872
|
+
|
873
|
+
//Remove translation and re-render axes
|
874
|
+
var tr = this.translate.slice();
|
875
|
+
this.translate = [0,0,-this.modelsize*1.25];
|
876
|
+
this.webgl.apply(this);
|
877
|
+
this.border.draw(this.webgl, true, false);
|
878
|
+
this.translate = tr;
|
879
|
+
}
|
880
|
+
|
881
|
+
BoxViewer.prototype.syncRotation = function(rot) {
|
882
|
+
if (rot)
|
883
|
+
this.rotate = quat4.create(rot);
|
884
|
+
this.rotated = true;
|
885
|
+
this.draw();
|
886
|
+
rstr = '' + this.getRotationString();
|
887
|
+
that = this;
|
888
|
+
window.requestAnimationFrame(function() {that.command(rstr);});
|
889
|
+
}
|
890
|
+
|
891
|
+
BoxViewer.prototype.rotateX = function(deg) {
|
892
|
+
this.rotation(deg, [1,0,0]);
|
893
|
+
}
|
894
|
+
|
895
|
+
BoxViewer.prototype.rotateY = function(deg) {
|
896
|
+
this.rotation(deg, [0,1,0]);
|
897
|
+
}
|
898
|
+
|
899
|
+
BoxViewer.prototype.rotateZ = function(deg) {
|
900
|
+
this.rotation(deg, [0,0,1]);
|
901
|
+
}
|
902
|
+
|
903
|
+
BoxViewer.prototype.rotation = function(deg, axis) {
|
904
|
+
//Quaterion rotate
|
905
|
+
var arad = deg * Math.PI / 180.0;
|
906
|
+
var rotation = quat4.fromAngleAxis(arad, axis);
|
907
|
+
rotation = quat4.normalize(rotation);
|
908
|
+
this.rotate = quat4.multiply(rotation, this.rotate);
|
909
|
+
}
|
910
|
+
|
911
|
+
BoxViewer.prototype.getRotation = function() {
|
912
|
+
return [this.rotate[0], this.rotate[1], this.rotate[2], this.rotate[3]];
|
913
|
+
}
|
914
|
+
|
915
|
+
BoxViewer.prototype.getRotationString = function() {
|
916
|
+
//Return current rotation quaternion as string
|
917
|
+
var q = this.getRotation();
|
918
|
+
return 'rotation ' + q[0] + ' ' + q[1] + ' ' + q[2] + ' ' + q[3];
|
919
|
+
}
|
920
|
+
|
921
|
+
BoxViewer.prototype.getTranslationString = function() {
|
922
|
+
return 'translation ' + this.translate[0] + ' ' + this.translate[1] + ' ' + this.translate[2];
|
923
|
+
}
|
924
|
+
|
925
|
+
BoxViewer.prototype.reset = function() {
|
926
|
+
if (this.gl) {
|
927
|
+
this.updateDims(this.view);
|
928
|
+
this.draw();
|
929
|
+
}
|
930
|
+
|
931
|
+
this.command('reset');
|
932
|
+
}
|
933
|
+
|
934
|
+
BoxViewer.prototype.zoom = function(factor) {
|
935
|
+
if (this.gl) {
|
936
|
+
this.translate[2] += factor * Math.max(0.01*this.modelsize, Math.abs(this.translate[2]));
|
937
|
+
this.draw();
|
938
|
+
}
|
939
|
+
|
940
|
+
var that = this;
|
941
|
+
if (this.zoomTimer)
|
942
|
+
clearTimeout(this.zoomTimer);
|
943
|
+
this.zoomTimer = setTimeout(function () {that.command('' + that.getTranslationString()); that.clear(); that.zoomTimer = null;}, 500 );
|
944
|
+
|
945
|
+
//this.command('' + this.getTranslationString());
|
946
|
+
//this.command('zoom ' + factor);
|
947
|
+
}
|
948
|
+
|
949
|
+
BoxViewer.prototype.zoomClip = function(factor) {
|
950
|
+
var near_clip = this.near_clip + factor * this.modelsize;
|
951
|
+
if (near_clip >= this.modelsize * 0.001)
|
952
|
+
this.near_clip = near_clip;
|
953
|
+
|
954
|
+
if (this.gl) this.draw();
|
955
|
+
|
956
|
+
var that = this;
|
957
|
+
if (this.zoomTimer) clearTimeout(this.zoomTimer);
|
958
|
+
//this.zoomTimer = setTimeout(function () {that.command('zoomclip ' + factor); that.clear();}, 500 );
|
959
|
+
this.zoomTimer = setTimeout(function () {that.command('nearclip ' + that.near_clip); that.clear();}, 500 );
|
960
|
+
|
961
|
+
//this.command('zoomclip ' + factor);
|
962
|
+
}
|
963
|
+
|
964
|
+
BoxViewer.prototype.updateDims = function(view) {
|
965
|
+
if (!view) return;
|
966
|
+
var oldsize = this.modelsize;
|
967
|
+
|
968
|
+
//Check for valid dims
|
969
|
+
for (var i=0; i<3; i++) {
|
970
|
+
if (view.bounds && view.max[i] < view.bounds.max[i])
|
971
|
+
view.max[i] = view.bounds.max[i];
|
972
|
+
if (view.bounds && view.min[i] > view.bounds.min[i])
|
973
|
+
view.min[i] = view.bounds.min[i];
|
974
|
+
/*if (view.max[i] < view.min[i] <= 0.0) {
|
975
|
+
view.max[i] = 1.0;
|
976
|
+
view.min[i] = -1.0;
|
977
|
+
}
|
978
|
+
}*/
|
979
|
+
}
|
980
|
+
|
981
|
+
this.dims = [view.max[0] - view.min[0], view.max[1] - view.min[1], view.max[2] - view.min[2]];
|
982
|
+
this.modelsize = Math.sqrt(this.dims[0]*this.dims[0] + this.dims[1]*this.dims[1] + this.dims[2]*this.dims[2]);
|
983
|
+
|
984
|
+
this.focus = [view.min[0] + 0.5*this.dims[0], view.min[1] + 0.5*this.dims[1], view.min[2] + 0.5*this.dims[2]];
|
985
|
+
this.centre = [this.focus[0],this.focus[1],this.focus[2]];
|
986
|
+
|
987
|
+
this.translate = [0,0,0];
|
988
|
+
if (this.modelsize != oldsize) this.translate[2] = -this.modelsize*1.25;
|
989
|
+
|
990
|
+
if (this.near_clip == 0.0) this.near_clip = this.modelsize / 10.0;
|
991
|
+
if (this.far_clip == 0.0) this.far_clip = this.modelsize * 10.0;
|
992
|
+
|
993
|
+
quat4.identity(this.rotate);
|
994
|
+
|
995
|
+
if (view) {
|
996
|
+
//Initial rotation
|
997
|
+
if (view.rotate) {
|
998
|
+
if (view.rotate.length == 3) {
|
999
|
+
this.rotateZ(-view.rotate[2]);
|
1000
|
+
this.rotateY(-view.rotate[1]);
|
1001
|
+
this.rotateX(-view.rotate[0]);
|
1002
|
+
} else if (view.rotate.length == 4) {
|
1003
|
+
this.rotate = quat4.create(view.rotate);
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
//Translate
|
1008
|
+
if (view.translate) {
|
1009
|
+
this.translate[0] = view.translate[0];
|
1010
|
+
this.translate[1] = view.translate[1];
|
1011
|
+
this.translate[2] = view.translate[2];
|
1012
|
+
}
|
1013
|
+
|
1014
|
+
//Scale
|
1015
|
+
if (view.scale) {
|
1016
|
+
this.scale[0] = view.scale[0];
|
1017
|
+
this.scale[1] = view.scale[1];
|
1018
|
+
this.scale[2] = view.scale[2];
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
//Focal point
|
1022
|
+
if (view.focus) {
|
1023
|
+
this.focus[0] = this.centre[0] = view.focus[0];
|
1024
|
+
this.focus[1] = this.centre[1] = view.focus[1];
|
1025
|
+
this.focus[2] = this.centre[2] = view.focus[2];
|
1026
|
+
}
|
1027
|
+
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
//console.log("DIMS: " + view.min[0] + " to " + view.max[0] + "," + view.min[1] + " to " + view.max[1] + "," + view.min[2] + " to " + view.max[2]);
|
1031
|
+
//console.log("New model size: " + this.modelsize + ", Focal point: " + this.focus[0] + "," + this.focus[1] + "," + this.focus[2]);
|
1032
|
+
//console.log("Translate: " + this.translate[0] + "," + this.translate[1] + "," + this.translate[2]);
|
1033
|
+
|
1034
|
+
if (!this.gl) return;
|
1035
|
+
|
1036
|
+
//Create the bounding box vertex buffer
|
1037
|
+
this.border.updateBuffers(this.view);
|
1038
|
+
}
|
1039
|
+
|