Work around lack of scikits.audiolab support on Python 3.
[mediagoblin.git] / extlib / thingiview.js / thingiview.js
1 Thingiview = function(containerId) {
2 scope = this;
3
4 this.containerId = containerId;
5 var container = document.getElementById(containerId);
6
7 // var stats = null;
8 var camera = null;
9 var scene = null;
10 var renderer = null;
11 var object = null;
12 var plane = null;
13
14 var ambientLight = null;
15 var directionalLight = null;
16 var pointLight = null;
17
18 var targetXRotation = 0;
19 var targetXRotationOnMouseDown = 0;
20 var mouseX = 0;
21 var mouseXOnMouseDown = 0;
22
23 var targetYRotation = 0;
24 var targetYRotationOnMouseDown = 0;
25 var mouseY = 0;
26 var mouseYOnMouseDown = 0;
27
28 var mouseDown = false;
29 var mouseOver = false;
30
31 var windowHalfX = window.innerWidth / 2;
32 var windowHalfY = window.innerHeight / 2
33
34 var view = null;
35 var infoMessage = null;
36 var progressBar = null;
37 var alertBox = null;
38
39 var timer = null;
40
41 var rotateTimer = null;
42 var rotateListener = null;
43 var wasRotating = null;
44
45 var cameraView = 'diagonal';
46 var cameraZoom = 0;
47 var rotate = false;
48 var backgroundColor = '#606060';
49 var objectMaterial = 'solid';
50 var objectColor = 0xffffff;
51 var showPlane = true;
52 var isWebGl = false;
53
54 if (document.defaultView && document.defaultView.getComputedStyle) {
55 var width = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('width'));
56 var height = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('height'));
57 } else {
58 var width = parseFloat(container.currentStyle.width);
59 var height = parseFloat(container.currentStyle.height);
60 }
61
62 var geometry;
63
64 this.initScene = function() {
65 container.style.position = 'relative';
66 container.innerHTML = '';
67
68 camera = new THREE.Camera(45, width/ height, 1, 100000);
69 camera.updateMatrix();
70
71 scene = new THREE.Scene();
72
73 ambientLight = new THREE.AmbientLight(0x202020);
74 scene.addLight(ambientLight);
75
76 directionalLight = new THREE.DirectionalLight(0xffffff, 0.75);
77 directionalLight.position.x = 1;
78 directionalLight.position.y = 1;
79 directionalLight.position.z = 2;
80 directionalLight.position.normalize();
81 scene.addLight(directionalLight);
82
83 pointLight = new THREE.PointLight(0xffffff, 0.3);
84 pointLight.position.x = 0;
85 pointLight.position.y = -25;
86 pointLight.position.z = 10;
87 scene.addLight(pointLight);
88
89 progressBar = document.createElement('div');
90 progressBar.style.position = 'absolute';
91 progressBar.style.top = '0px';
92 progressBar.style.left = '0px';
93 progressBar.style.backgroundColor = 'red';
94 progressBar.style.padding = '5px';
95 progressBar.style.display = 'none';
96 progressBar.style.overflow = 'visible';
97 progressBar.style.whiteSpace = 'nowrap';
98 progressBar.style.zIndex = 100;
99 container.appendChild(progressBar);
100
101 alertBox = document.createElement('div');
102 alertBox.id = 'alertBox';
103 alertBox.style.position = 'absolute';
104 alertBox.style.top = '25%';
105 alertBox.style.left = '25%';
106 alertBox.style.width = '50%';
107 alertBox.style.height = '50%';
108 alertBox.style.backgroundColor = '#dddddd';
109 alertBox.style.padding = '10px';
110 // alertBox.style.overflowY = 'scroll';
111 alertBox.style.display = 'none';
112 alertBox.style.zIndex = 100;
113 container.appendChild(alertBox);
114
115 // load a blank object
116 // this.loadSTLString('');
117
118 if (showPlane) {
119 loadPlaneGeometry();
120 }
121
122 this.setCameraView(cameraView);
123 this.setObjectMaterial(objectMaterial);
124
125 testCanvas = document.createElement('canvas');
126 try {
127 if (testCanvas.getContext('experimental-webgl')) {
128 // showPlane = false;
129 isWebGl = true;
130 renderer = new THREE.WebGLRenderer();
131 // renderer = new THREE.CanvasRenderer();
132 } else {
133 renderer = new THREE.CanvasRenderer();
134 }
135 } catch(e) {
136 renderer = new THREE.CanvasRenderer();
137 // log("failed webgl detection");
138 }
139
140 // renderer.setSize(container.innerWidth, container.innerHeight);
141
142 renderer.setSize(width, height);
143 renderer.domElement.style.backgroundColor = backgroundColor;
144 container.appendChild(renderer.domElement);
145
146 // stats = new Stats();
147 // stats.domElement.style.position = 'absolute';
148 // stats.domElement.style.top = '0px';
149 // container.appendChild(stats.domElement);
150
151 // TODO: figure out how to get the render window to resize when window resizes
152 // window.addEventListener('resize', onContainerResize(), false);
153 // container.addEventListener('resize', onContainerResize(), false);
154
155 // renderer.domElement.addEventListener('mousemove', onRendererMouseMove, false);
156 window.addEventListener('mousemove', onRendererMouseMove, false);
157 renderer.domElement.addEventListener('mouseover', onRendererMouseOver, false);
158 renderer.domElement.addEventListener('mouseout', onRendererMouseOut, false);
159 renderer.domElement.addEventListener('mousedown', onRendererMouseDown, false);
160 // renderer.domElement.addEventListener('mouseup', onRendererMouseUp, false);
161 window.addEventListener('mouseup', onRendererMouseUp, false);
162
163 renderer.domElement.addEventListener('touchstart', onRendererTouchStart, false);
164 renderer.domElement.addEventListener('touchend', onRendererTouchEnd, false);
165 renderer.domElement.addEventListener('touchmove', onRendererTouchMove, false);
166
167 renderer.domElement.addEventListener('DOMMouseScroll', onRendererScroll, false);
168 renderer.domElement.addEventListener('mousewheel', onRendererScroll, false);
169 renderer.domElement.addEventListener('gesturechange', onRendererGestureChange, false);
170 }
171
172 // FIXME
173 // onContainerResize = function(event) {
174 // width = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('width'));
175 // height = parseFloat(document.defaultView.getComputedStyle(container,null).getPropertyValue('height'));
176 //
177 // // log("resized width: " + width + ", height: " + height);
178 //
179 // if (renderer) {
180 // renderer.setSize(width, height);
181 // camera.projectionMatrix = THREE.Matrix4.makePerspective(70, width / height, 1, 10000);
182 // sceneLoop();
183 // }
184 // };
185
186 onRendererScroll = function(event) {
187 event.preventDefault();
188
189 var rolled = 0;
190
191 if (event.wheelDelta === undefined) {
192 // Firefox
193 // The measurement units of the detail and wheelDelta properties are different.
194 rolled = -40 * event.detail;
195 } else {
196 rolled = event.wheelDelta;
197 }
198
199 if (rolled > 0) {
200 // up
201 scope.setCameraZoom(+10);
202 } else {
203 // down
204 scope.setCameraZoom(-10);
205 }
206 }
207
208 onRendererGestureChange = function(event) {
209 event.preventDefault();
210
211 if (event.scale > 1) {
212 scope.setCameraZoom(+5);
213 } else {
214 scope.setCameraZoom(-5);
215 }
216 }
217
218 onRendererMouseOver = function(event) {
219 mouseOver = true;
220 // targetRotation = object.rotation.z;
221 if (timer == null) {
222 // log('starting loop');
223 timer = setInterval(sceneLoop, 1000/60);
224 }
225 }
226
227 onRendererMouseDown = function(event) {
228 // log("down");
229
230 event.preventDefault();
231 mouseDown = true;
232
233 if(scope.getRotation()){
234 wasRotating = true;
235 scope.setRotation(false);
236 } else {
237 wasRotating = false;
238 }
239
240 mouseXOnMouseDown = event.clientX - windowHalfX;
241 mouseYOnMouseDown = event.clientY - windowHalfY;
242
243 targetXRotationOnMouseDown = targetXRotation;
244 targetYRotationOnMouseDown = targetYRotation;
245 }
246
247 onRendererMouseMove = function(event) {
248 // log("move");
249
250 if (mouseDown) {
251 mouseX = event.clientX - windowHalfX;
252 // targetXRotation = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
253 xrot = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;
254
255 mouseY = event.clientY - windowHalfY;
256 // targetYRotation = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
257 yrot = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.02;
258
259 targetXRotation = xrot;
260 targetYRotation = yrot;
261 }
262 }
263
264 onRendererMouseUp = function(event) {
265 // log("up");
266 if (mouseDown) {
267 mouseDown = false;
268 if (!mouseOver) {
269 clearInterval(timer);
270 timer = null;
271 }
272 if (wasRotating) {
273 scope.setRotation(true);
274 }
275 }
276 }
277
278 onRendererMouseOut = function(event) {
279 if (!mouseDown) {
280 clearInterval(timer);
281 timer = null;
282 }
283 mouseOver = false;
284 }
285
286 onRendererTouchStart = function(event) {
287 targetXRotation = object.rotation.z;
288 targetYRotation = object.rotation.x;
289
290 timer = setInterval(sceneLoop, 1000/60);
291
292 if (event.touches.length == 1) {
293 event.preventDefault();
294
295 mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
296 targetXRotationOnMouseDown = targetXRotation;
297
298 mouseYOnMouseDown = event.touches[0].pageY - windowHalfY;
299 targetYRotationOnMouseDown = targetYRotation;
300 }
301 }
302
303 onRendererTouchEnd = function(event) {
304 clearInterval(timer);
305 timer = null;
306 // targetXRotation = object.rotation.z;
307 // targetYRotation = object.rotation.x;
308 }
309
310 onRendererTouchMove = function(event) {
311 if (event.touches.length == 1) {
312 event.preventDefault();
313
314 mouseX = event.touches[0].pageX - windowHalfX;
315 targetXRotation = targetXRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;
316
317 mouseY = event.touches[0].pageY - windowHalfY;
318 targetYRotation = targetYRotationOnMouseDown + (mouseY - mouseYOnMouseDown) * 0.05;
319 }
320 }
321
322 sceneLoop = function() {
323 if (object) {
324 // if (view == 'bottom') {
325 // if (showPlane) {
326 // plane.rotation.z = object.rotation.z -= (targetRotation + object.rotation.z) * 0.05;
327 // } else {
328 // object.rotation.z -= (targetRotation + object.rotation.z) * 0.05;
329 // }
330 // } else {
331 // if (showPlane) {
332 // plane.rotation.z = object.rotation.z += (targetRotation - object.rotation.z) * 0.05;
333 // } else {
334 // object.rotation.z += (targetRotation - object.rotation.z) * 0.05;
335 // }
336 // }
337
338 if (showPlane) {
339 plane.rotation.z = object.rotation.z = (targetXRotation - object.rotation.z) * 0.2;
340 plane.rotation.x = object.rotation.x = (targetYRotation - object.rotation.x) * 0.2;
341 } else {
342 object.rotation.z = (targetXRotation - object.rotation.z) * 0.2;
343 object.rotation.x = (targetYRotation - object.rotation.x) * 0.2;
344 }
345
346 // log(object.rotation.x);
347
348 camera.updateMatrix();
349 object.updateMatrix();
350
351 if (showPlane) {
352 plane.updateMatrix();
353 }
354
355 renderer.render(scene, camera);
356 // stats.update();
357 }
358 }
359
360 rotateLoop = function() {
361 // targetRotation += 0.01;
362 targetXRotation += 0.05;
363 sceneLoop();
364 }
365
366 this.getShowPlane = function(){
367 return showPlane;
368 }
369
370 this.setShowPlane = function(show) {
371 showPlane = show;
372
373 if (show) {
374 if (scene && !plane) {
375 loadPlaneGeometry();
376 }
377 plane.material[0].opacity = 1;
378 // plane.updateMatrix();
379 } else {
380 if (scene && plane) {
381 // alert(plane.material[0].opacity);
382 plane.material[0].opacity = 0;
383 // plane.updateMatrix();
384 }
385 }
386
387 sceneLoop();
388 }
389
390 this.getRotation = function() {
391 return rotateTimer !== null;
392 }
393
394 this.resetRotation = function () {
395 if (rotate) {
396 this.setRotation(false);
397 this.setRotation(true);
398 }
399 }
400
401 this.setRotation = function(rotate) {
402 rotation = rotate;
403
404 if (rotate) {
405 rotateTimer = setInterval(rotateLoop, 1000/60);
406 } else {
407 clearInterval(rotateTimer);
408 rotateTimer = null;
409 }
410
411 scope.onSetRotation();
412 }
413
414 this.onSetRotation = function(callback) {
415 if(callback === undefined){
416 if(rotateListener !== null){
417 try{
418 rotateListener(scope.getRotation());
419 } catch(ignored) {}
420 }
421 } else {
422 rotateListener = callback;
423 }
424 }
425
426 this.setCameraView = function(dir) {
427 cameraView = dir;
428
429 targetXRotation = 0;
430 targetYRotation = 0;
431
432 if (object) {
433 object.rotation.x = 0;
434 object.rotation.y = 0;
435 object.rotation.z = 0;
436 }
437
438 if (showPlane && object) {
439 plane.rotation.x = object.rotation.x;
440 plane.rotation.y = object.rotation.y;
441 plane.rotation.z = object.rotation.z;
442 }
443
444 if (dir == 'top') {
445 // camera.position.y = 0;
446 // camera.position.z = 100;
447 // camera.target.position.z = 0;
448 if (showPlane) {
449 plane.flipSided = false;
450 }
451 } else if (dir == 'side') {
452 // camera.position.y = -70;
453 // camera.position.z = 70;
454 // camera.target.position.z = 0;
455 targetYRotation = -4.5;
456 if (showPlane) {
457 plane.flipSided = false;
458 }
459 } else if (dir == 'bottom') {
460 // camera.position.y = 0;
461 // camera.position.z = -100;
462 // camera.target.position.z = 0;
463 if (showPlane) {
464 plane.flipSided = true;
465 }
466 } else {
467 // camera.position.y = -70;
468 // camera.position.z = 70;
469 // camera.target.position.z = 0;
470 if (showPlane) {
471 plane.flipSided = false;
472 }
473 }
474
475 mouseX = targetXRotation;
476 mouseXOnMouseDown = targetXRotation;
477
478 mouseY = targetYRotation;
479 mouseYOnMouseDown = targetYRotation;
480
481 scope.centerCamera();
482
483 sceneLoop();
484 }
485
486 this.setCameraZoom = function(factor) {
487 cameraZoom = factor;
488
489 if (cameraView == 'bottom') {
490 if (camera.position.z + factor > 0) {
491 factor = 0;
492 }
493 } else {
494 if (camera.position.z - factor < 0) {
495 factor = 0;
496 }
497 }
498
499 if (cameraView == 'top') {
500 camera.position.z -= factor;
501 } else if (cameraView == 'bottom') {
502 camera.position.z += factor;
503 } else if (cameraView == 'side') {
504 camera.position.y += factor;
505 camera.position.z -= factor;
506 } else {
507 camera.position.y += factor;
508 camera.position.z -= factor;
509 }
510
511 sceneLoop();
512 }
513
514 this.getObjectMaterial = function() {
515 return objectMaterial;
516 }
517
518 this.setObjectMaterial = function(type) {
519 objectMaterial = type;
520
521 loadObjectGeometry();
522 }
523
524 this.setBackgroundColor = function(color) {
525 backgroundColor = color
526
527 if (renderer) {
528 renderer.domElement.style.backgroundColor = color;
529 }
530 }
531
532 this.setObjectColor = function(color) {
533 objectColor = parseInt(color.replace(/\#/g, ''), 16);
534
535 loadObjectGeometry();
536 }
537
538 this.loadSTL = function(url) {
539 scope.newWorker('loadSTL', url);
540 }
541
542 this.loadOBJ = function(url) {
543 scope.newWorker('loadOBJ', url);
544 }
545
546 this.loadSTLString = function(STLString) {
547 scope.newWorker('loadSTLString', STLString);
548 }
549
550 this.loadSTLBinary = function(STLBinary) {
551 scope.newWorker('loadSTLBinary', STLBinary);
552 }
553
554 this.loadOBJString = function(OBJString) {
555 scope.newWorker('loadOBJString', OBJString);
556 }
557
558 this.loadJSON = function(url) {
559 scope.newWorker('loadJSON', url);
560 }
561
562 this.loadPLY = function(url) {
563 scope.newWorker('loadPLY', url);
564 }
565
566 this.loadPLYString = function(PLYString) {
567 scope.newWorker('loadPLYString', PLYString);
568 }
569
570 this.loadPLYBinary = function(PLYBinary) {
571 scope.newWorker('loadPLYBinary', PLYBinary);
572 }
573
574 this.centerCamera = function() {
575 if (geometry) {
576 // Using method from http://msdn.microsoft.com/en-us/library/bb197900(v=xnagamestudio.10).aspx
577 // log("bounding sphere radius = " + geometry.boundingSphere.radius);
578
579 // look at the center of the object
580 camera.target.position.x = geometry.center_x;
581 camera.target.position.y = geometry.center_y;
582 camera.target.position.z = geometry.center_z;
583
584 // set camera position to center of sphere
585 camera.position.x = geometry.center_x;
586 camera.position.y = geometry.center_y;
587 camera.position.z = geometry.center_z;
588
589 // find distance to center
590 distance = geometry.boundingSphere.radius / Math.sin((camera.fov/2) * (Math.PI / 180));
591
592 // zoom backwards about half that distance, I don't think I'm doing the math or backwards vector calculation correctly?
593 // scope.setCameraZoom(-distance/1.8);
594 // scope.setCameraZoom(-distance/1.5);
595 scope.setCameraZoom(-distance/1.9);
596
597 directionalLight.position.x = geometry.min_y * 2;
598 directionalLight.position.y = geometry.min_y * 2;
599 directionalLight.position.z = geometry.max_z * 2;
600
601 pointLight.position.x = geometry.center_y;
602 pointLight.position.y = geometry.center_y;
603 pointLight.position.z = geometry.max_z * 2;
604 } else {
605 // set to any valid position so it doesn't fail before geometry is available
606 camera.position.y = -70;
607 camera.position.z = 70;
608 camera.target.position.z = 0;
609 }
610 }
611
612 this.loadArray = function(array) {
613 log("loading array...");
614 geometry = new STLGeometry(array);
615 loadObjectGeometry();
616 scope.resetRotation();
617 scope.centerCamera();
618 log("finished loading " + geometry.faces.length + " faces.");
619 }
620
621 this.newWorker = function(cmd, param) {
622 scope.setRotation(false);
623
624 var worker = new WorkerFacade(thingiurlbase + '/thingiloader.js');
625
626 worker.onmessage = function(event) {
627 if (event.data.status == "complete") {
628 progressBar.innerHTML = 'Initializing geometry...';
629 // scene.removeObject(object);
630 geometry = new STLGeometry(event.data.content);
631 loadObjectGeometry();
632 progressBar.innerHTML = '';
633 progressBar.style.display = 'none';
634
635 scope.resetRotation();
636 log("finished loading " + geometry.faces.length + " faces.");
637 scope.centerCamera();
638 } else if (event.data.status == "complete_points") {
639 progressBar.innerHTML = 'Initializing points...';
640
641 geometry = new THREE.Geometry();
642
643 var material = new THREE.ParticleBasicMaterial( { color: 0xff0000, opacity: 1 } );
644
645 // material = new THREE.ParticleBasicMaterial( { size: 35, sizeAttenuation: false} );
646 // material.color.setHSV( 1.0, 0.2, 0.8 );
647
648 for (i in event.data.content[0]) {
649 // for (var i=0; i<10; i++) {
650 vector = new THREE.Vector3( event.data.content[0][i][0], event.data.content[0][i][1], event.data.content[0][i][2] );
651 geometry.vertices.push( new THREE.Vertex( vector ) );
652 }
653
654 particles = new THREE.ParticleSystem( geometry, material );
655 particles.sortParticles = true;
656 particles.updateMatrix();
657 scene.addObject( particles );
658
659 camera.updateMatrix();
660 renderer.render(scene, camera);
661
662 progressBar.innerHTML = '';
663 progressBar.style.display = 'none';
664
665 scope.resetRotation();
666 log("finished loading " + event.data.content[0].length + " points.");
667 // scope.centerCamera();
668 } else if (event.data.status == "progress") {
669 progressBar.style.display = 'block';
670 progressBar.style.width = event.data.content;
671 // log(event.data.content);
672 } else if (event.data.status == "message") {
673 progressBar.style.display = 'block';
674 progressBar.innerHTML = event.data.content;
675 log(event.data.content);
676 } else if (event.data.status == "alert") {
677 scope.displayAlert(event.data.content);
678 } else {
679 alert('Error: ' + event.data);
680 log('Unknown Worker Message: ' + event.data);
681 }
682 }
683
684 worker.onerror = function(error) {
685 log(error);
686 error.preventDefault();
687 }
688
689 worker.postMessage({'cmd':cmd, 'param':param});
690 }
691
692 this.displayAlert = function(msg) {
693 msg = msg + "<br/><br/><center><input type=\"button\" value=\"Ok\" onclick=\"document.getElementById('alertBox').style.display='none'\"></center>"
694
695 alertBox.innerHTML = msg;
696 alertBox.style.display = 'block';
697
698 // log(msg);
699 }
700
701 function loadPlaneGeometry() {
702 // TODO: switch to lines instead of the Plane object so we can get rid of the horizontal lines in canvas renderer...
703 plane = new THREE.Mesh(new Plane(100, 100, 10, 10), new THREE.MeshBasicMaterial({color:0xafafaf,wireframe:true}));
704 scene.addObject(plane);
705 }
706
707 function loadObjectGeometry() {
708 if (scene && geometry) {
709 if (objectMaterial == 'wireframe') {
710 // material = new THREE.MeshColorStrokeMaterial(objectColor, 1, 1);
711 material = new THREE.MeshBasicMaterial({color:objectColor,wireframe:true});
712 } else {
713 if (isWebGl) {
714 // material = new THREE.MeshPhongMaterial(objectColor, objectColor, 0xffffff, 50, 1.0);
715 // material = new THREE.MeshColorFillMaterial(objectColor);
716 // material = new THREE.MeshLambertMaterial({color:objectColor});
717 material = new THREE.MeshLambertMaterial({color:objectColor, shading: THREE.FlatShading});
718 } else {
719 // material = new THREE.MeshColorFillMaterial(objectColor);
720 material = new THREE.MeshLambertMaterial({color:objectColor, shading: THREE.FlatShading});
721 }
722 }
723
724 // scene.removeObject(object);
725
726 if (object) {
727 // shouldn't be needed, but this fixes a bug with webgl not removing previous object when loading a new one dynamically
728 object.materials = [new THREE.MeshBasicMaterial({color:0xffffff, opacity:0})];
729 scene.removeObject(object);
730 // object.geometry = geometry;
731 // object.materials = [material];
732 }
733
734 object = new THREE.Mesh(geometry, material);
735 scene.addObject(object);
736
737 if (objectMaterial != 'wireframe') {
738 object.overdraw = true;
739 object.doubleSided = true;
740 }
741
742 object.updateMatrix();
743
744 targetXRotation = 0;
745 targetYRotation = 0;
746
747 sceneLoop();
748 }
749 }
750
751 };
752
753 var STLGeometry = function(stlArray) {
754 // log("building geometry...");
755 THREE.Geometry.call(this);
756
757 var scope = this;
758
759 // var vertexes = stlArray[0];
760 // var normals = stlArray[1];
761 // var faces = stlArray[2];
762
763 for (var i=0; i<stlArray[0].length; i++) {
764 v(stlArray[0][i][0], stlArray[0][i][1], stlArray[0][i][2]);
765 }
766
767 for (var i=0; i<stlArray[1].length; i++) {
768 f3(stlArray[1][i][0], stlArray[1][i][1], stlArray[1][i][2]);
769 }
770
771 function v(x, y, z) {
772 // log("adding vertex: " + x + "," + y + "," + z);
773 scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
774 }
775
776 function f3(a, b, c) {
777 // log("adding face: " + a + "," + b + "," + c)
778 scope.faces.push( new THREE.Face3( a, b, c ) );
779 }
780
781 // log("computing centroids...");
782 this.computeCentroids();
783 // log("computing normals...");
784 // this.computeNormals();
785 this.computeFaceNormals();
786 this.sortFacesByMaterial();
787 // log("finished building geometry");
788
789 scope.min_x = 0;
790 scope.min_y = 0;
791 scope.min_z = 0;
792
793 scope.max_x = 0;
794 scope.max_y = 0;
795 scope.max_z = 0;
796
797 for (var v = 0, vl = scope.vertices.length; v < vl; v ++) {
798 scope.max_x = Math.max(scope.max_x, scope.vertices[v].position.x);
799 scope.max_y = Math.max(scope.max_y, scope.vertices[v].position.y);
800 scope.max_z = Math.max(scope.max_z, scope.vertices[v].position.z);
801
802 scope.min_x = Math.min(scope.min_x, scope.vertices[v].position.x);
803 scope.min_y = Math.min(scope.min_y, scope.vertices[v].position.y);
804 scope.min_z = Math.min(scope.min_z, scope.vertices[v].position.z);
805 }
806
807 scope.center_x = (scope.max_x + scope.min_x)/2;
808 scope.center_y = (scope.max_y + scope.min_y)/2;
809 scope.center_z = (scope.max_z + scope.min_z)/2;
810 }
811
812 STLGeometry.prototype = new THREE.Geometry();
813 STLGeometry.prototype.constructor = STLGeometry;
814
815 function log(msg) {
816 if (this.console) {
817 console.log(msg);
818 }
819 }
820
821 /* A facade for the Web Worker API that fakes it in case it's missing.
822 Good when web workers aren't supported in the browser, but it's still fast enough, so execution doesn't hang too badly (e.g. Opera 10.5).
823 By Stefan Wehrmeyer, licensed under MIT
824 */
825
826 var WorkerFacade;
827 if(!!window.Worker){
828 WorkerFacade = (function(){
829 return function(path){
830 return new window.Worker(path);
831 };
832 }());
833 } else {
834 WorkerFacade = (function(){
835 var workers = {}, masters = {}, loaded = false;
836 var that = function(path){
837 var theworker = {}, loaded = false, callings = [];
838 theworker.postToWorkerFunction = function(args){
839 try{
840 workers[path]({"data":args});
841 }catch(err){
842 theworker.onerror(err);
843 }
844 };
845 theworker.postMessage = function(params){
846 if(!loaded){
847 callings.push(params);
848 return;
849 }
850 theworker.postToWorkerFunction(params);
851 };
852 masters[path] = theworker;
853 var scr = document.createElement("SCRIPT");
854 scr.src = path;
855 scr.type = "text/javascript";
856 scr.onload = function(){
857 loaded = true;
858 while(callings.length > 0){
859 theworker.postToWorkerFunction(callings[0]);
860 callings.shift();
861 }
862 };
863 document.body.appendChild(scr);
864
865 var binaryscr = document.createElement("SCRIPT");
866 binaryscr.src = thingiurlbase + '/binaryReader.js';
867 binaryscr.type = "text/javascript";
868 document.body.appendChild(binaryscr);
869
870 return theworker;
871 };
872 that.fake = true;
873 that.add = function(pth, worker){
874 workers[pth] = worker;
875 return function(param){
876 masters[pth].onmessage({"data": param});
877 };
878 };
879 that.toString = function(){
880 return "FakeWorker('"+path+"')";
881 };
882 return that;
883 }());
884 }
885
886 /* Then just use WorkerFacade instead of Worker (or alias it)
887
888 The Worker code must should use a custom function (name it how you want) instead of postMessage.
889 Put this at the end of the Worker:
890
891 if(typeof(window) === "undefined"){
892 onmessage = nameOfWorkerFunction;
893 customPostMessage = postMessage;
894 } else {
895 customPostMessage = WorkerFacade.add("path/to/thisworker.js", nameOfWorkerFunction);
896 }
897
898 */