|
|
(4 intermediate revisions not shown) |
Line 3: |
Line 3: |
| <title>stl viewer</title> | | <title>stl viewer</title> |
| <meta charset="utf-8"> | | <meta charset="utf-8"> |
- | <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> | + | <meta name="viewport" content="width=device-width, user-scalable=yes, minimum-scale=0.1, maximum-scale=1.0"> |
| <style> | | <style> |
| body { | | body { |
Line 12: |
Line 12: |
| } | | } |
| </style> | | </style> |
- | </head>
| + | |
- | <body>
| + | |
| <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/three.js?action=raw&ctype=text/javascript"></script> | | <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/three.js?action=raw&ctype=text/javascript"></script> |
| <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/stats.js?action=raw&ctype=text/javascript"></script> | | <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/stats.js?action=raw&ctype=text/javascript"></script> |
| <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/detector.js?action=raw&ctype=text/javascript"></script> | | <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/detector.js?action=raw&ctype=text/javascript"></script> |
- | <script>
| + | <script type="text/javascript" src="https://2014.igem.org/Template:Team:Aachen/showCuvetteStl.js?action=raw&ctype=text/javascript"></script> |
| | | |
- | var camera, scene, renderer,
| + | </head> |
- | geometry, material, mesh, light1, stats;
| + | <body> |
| | | |
- | function trim (str) {
| |
- | str = str.replace(/^\s+/, '');
| |
- | for (var i = str.length - 1; i >= 0; i--) {
| |
- | if (/\S/.test(str.charAt(i))) {
| |
- | str = str.substring(0, i + 1);
| |
- | break;
| |
- | }
| |
- | }
| |
- | return str;
| |
- | }
| |
| | | |
- | // Notes:
| |
- | // - STL file format: http://en.wikipedia.org/wiki/STL_(file_format)
| |
- | // - 80 byte unused header
| |
- | // - All binary STLs are assumed to be little endian, as per wiki doc
| |
- | var parseStlBinary = function(stl) {
| |
- | var geo = new THREE.Geometry();
| |
- | var dv = new DataView(stl, 80); // 80 == unused header
| |
- | var isLittleEndian = true;
| |
- | var triangles = dv.getUint32(0, isLittleEndian);
| |
- |
| |
- | // console.log('arraybuffer length: ' + stl.byteLength);
| |
- | // console.log('number of triangles: ' + triangles);
| |
- |
| |
- | var offset = 4;
| |
- | for (var i = 0; i < triangles; i++) {
| |
- | // Get the normal for this triangle
| |
- | var normal = new THREE.Vector3(
| |
- | dv.getFloat32(offset, isLittleEndian),
| |
- | dv.getFloat32(offset+4, isLittleEndian),
| |
- | dv.getFloat32(offset+8, isLittleEndian)
| |
- | );
| |
- | offset += 12;
| |
- |
| |
- | // Get all 3 vertices for this triangle
| |
- | for (var j = 0; j < 3; j++) {
| |
- | geo.vertices.push(
| |
- | new THREE.Vector3(
| |
- | dv.getFloat32(offset, isLittleEndian),
| |
- | dv.getFloat32(offset+4, isLittleEndian),
| |
- | dv.getFloat32(offset+8, isLittleEndian)
| |
- | )
| |
- | );
| |
- | offset += 12
| |
- | }
| |
- |
| |
- | // there's also a Uint16 "attribute byte count" that we
| |
- | // don't need, it should always be zero.
| |
- | offset += 2;
| |
- |
| |
- | // Create a new face for from the vertices and the normal
| |
- | geo.faces.push(new THREE.Face3(i*3, i*3+1, i*3+2, normal));
| |
- | }
| |
- |
| |
- | // The binary STL I'm testing with seems to have all
| |
- | // zeroes for the normals, unlike its ASCII counterpart.
| |
- | // We can use three.js to compute the normals for us, though,
| |
- | // once we've assembled our geometry. This is a relatively
| |
- | // expensive operation, but only needs to be done once.
| |
- | geo.computeFaceNormals();
| |
- |
| |
- | mesh = new THREE.Mesh(
| |
- | geo,
| |
- | // new THREE.MeshNormalMaterial({
| |
- | // overdraw:true
| |
- | // }
| |
- | new THREE.MeshLambertMaterial({
| |
- | overdraw:true,
| |
- | color: 0xaa0000,
| |
- | shading: THREE.FlatShading
| |
- | }
| |
- | ));
| |
- | scene.add(mesh);
| |
- |
| |
- | stl = null;
| |
- | };
| |
- |
| |
- | var parseStl = function(stl) {
| |
- | var state = '';
| |
- | var lines = stl.split('\n');
| |
- | var geo = new THREE.Geometry();
| |
- | var name, parts, line, normal, done, vertices = [];
| |
- | var vCount = 0;
| |
- | stl = null;
| |
- |
| |
- | for (var len = lines.length, i = 0; i < len; i++) {
| |
- | if (done) {
| |
- | break;
| |
- | }
| |
- | line = trim(lines[i]);
| |
- | parts = line.split(' ');
| |
- | switch (state) {
| |
- | case '':
| |
- | if (parts[0] !== 'solid') {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "solid"');
| |
- | return;
| |
- | } else {
| |
- | name = parts[1];
| |
- | state = 'solid';
| |
- | }
| |
- | break;
| |
- | case 'solid':
| |
- | if (parts[0] !== 'facet' || parts[1] !== 'normal') {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "facet normal"');
| |
- | return;
| |
- | } else {
| |
- | normal = [
| |
- | parseFloat(parts[2]),
| |
- | parseFloat(parts[3]),
| |
- | parseFloat(parts[4])
| |
- | ];
| |
- | state = 'facet normal';
| |
- | }
| |
- | break;
| |
- | case 'facet normal':
| |
- | if (parts[0] !== 'outer' || parts[1] !== 'loop') {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "outer loop"');
| |
- | return;
| |
- | } else {
| |
- | state = 'vertex';
| |
- | }
| |
- | break;
| |
- | case 'vertex':
| |
- | if (parts[0] === 'vertex') {
| |
- | geo.vertices.push(new THREE.Vector3(
| |
- | parseFloat(parts[1]),
| |
- | parseFloat(parts[2]),
| |
- | parseFloat(parts[3])
| |
- | ));
| |
- | } else if (parts[0] === 'endloop') {
| |
- | geo.faces.push( new THREE.Face3( vCount*3, vCount*3+1, vCount*3+2, new THREE.Vector3(normal[0], normal[1], normal[2]) ) );
| |
- | vCount++;
| |
- | state = 'endloop';
| |
- | } else {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "vertex" or "endloop"');
| |
- | return;
| |
- | }
| |
- | break;
| |
- | case 'endloop':
| |
- | if (parts[0] !== 'endfacet') {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "endfacet"');
| |
- | return;
| |
- | } else {
| |
- | state = 'endfacet';
| |
- | }
| |
- | break;
| |
- | case 'endfacet':
| |
- | if (parts[0] === 'endsolid') {
| |
- | //mesh = new THREE.Mesh( geo, new THREE.MeshNormalMaterial({overdraw:true}));
| |
- | mesh = new THREE.Mesh(
| |
- | geo,
| |
- | new THREE.MeshLambertMaterial({
| |
- | overdraw:true,
| |
- | color: 0xaa0000,
| |
- | shading: THREE.FlatShading
| |
- | }
| |
- | ));
| |
- | scene.add(mesh);
| |
- | done = true;
| |
- | } else if (parts[0] === 'facet' && parts[1] === 'normal') {
| |
- | normal = [
| |
- | parseFloat(parts[2]),
| |
- | parseFloat(parts[3]),
| |
- | parseFloat(parts[4])
| |
- | ];
| |
- | if (vCount % 1000 === 0) {
| |
- | console.log(normal);
| |
- | }
| |
- | state = 'facet normal';
| |
- | } else {
| |
- | console.error(line);
| |
- | console.error('Invalid state "' + parts[0] + '", should be "endsolid" or "facet normal"');
| |
- | return;
| |
- | }
| |
- | break;
| |
- | default:
| |
- | console.error('Invalid state "' + state + '"');
| |
- | break;
| |
- | }
| |
- | }
| |
- | };
| |
- |
| |
- |
| |
- |
| |
- | init();
| |
- | animate();
| |
- |
| |
- | function arrayBufferToString(buffer)
| |
- | {
| |
- | var bufView = new Uint8Array(buffer);
| |
- | var length = bufView.length;
| |
- | var result = '';
| |
- | for(var i = 0;i<length;i+=65535)
| |
- | {
| |
- | var addition = 65535;
| |
- | if(i + 65535 > length)
| |
- | {
| |
- | addition = length - i;
| |
- | }
| |
- | result += String.fromCharCode.apply(null, bufView.subarray(i,i+addition));
| |
- | }
| |
- |
| |
- | return result;
| |
- | }
| |
- |
| |
- | function init() {
| |
- |
| |
- | //Detector.addGetWebGLMessage();
| |
- |
| |
- | scene = new THREE.Scene();
| |
- |
| |
- | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
| |
- | camera.position.z = 70;
| |
- | camera.position.y = 0;
| |
- | scene.add( camera );
| |
- |
| |
- | var directionalLight = new THREE.DirectionalLight( 0xffffff );
| |
- | directionalLight.position.x = 0;
| |
- | directionalLight.position.y = 0;
| |
- | directionalLight.position.z = 1;
| |
- | directionalLight.position.normalize();
| |
- | scene.add( directionalLight );
| |
- |
| |
- | var xhr = new XMLHttpRequest();
| |
- | xhr.onreadystatechange = function () {
| |
- | if ( xhr.readyState == 4 ) {
| |
- | if ( xhr.status == 200 || xhr.status == 0 ) {
| |
- | var rep = xhr.response; // || xhr.mozResponseArrayBuffer;
| |
- | console.log(rep);
| |
- | //parseStlBinary(rep);
| |
- | parseStl(arrayBufferToString(rep));
| |
- | mesh.rotation.x = 5;
| |
- | mesh.rotation.z = .25;
| |
- | console.log('done parsing');
| |
- | }
| |
- | }
| |
- | }
| |
- | xhr.onerror = function(e) {
| |
- | console.log(e);
| |
- | }
| |
- |
| |
- | xhr.open( "GET", 'https://2014.igem.org/Template:Team:Aachen/cuvette.stl?action=raw&ctype=text/plain', true );
| |
- |
| |
- | xhr.responseType = "arraybuffer";
| |
- | //xhr.setRequestHeader("Accept","text/plain");
| |
- | //xhr.setRequestHeader("Content-Type","text/plain");
| |
- | //xhr.setRequestHeader('charset', 'x-user-defined');
| |
- | xhr.send( null );
| |
- |
| |
- | renderer = new THREE.WebGLRenderer(); //new THREE.CanvasRenderer();
| |
- | renderer.setSize( window.innerWidth, window.innerHeight );
| |
- |
| |
- | document.body.appendChild( renderer.domElement );
| |
- |
| |
- | stats = new Stats();
| |
- | stats.domElement.style.position = 'absolute';
| |
- | stats.domElement.style.top = '0px';
| |
- | document.body.appendChild(stats.domElement);
| |
- | }
| |
- |
| |
- | function animate() {
| |
- |
| |
- | // note: three.js includes requestAnimationFrame shim
| |
- | requestAnimationFrame( animate );
| |
- | render();
| |
- | stats.update();
| |
- |
| |
- | }
| |
- |
| |
- | function render() {
| |
- |
| |
- | //mesh.rotation.x += 0.01;
| |
- | if (mesh) {
| |
- | mesh.rotation.z += 0.02;
| |
- | }
| |
- | //light1.position.z -= 1;
| |
- |
| |
- | renderer.render( scene, camera );
| |
- |
| |
- | }
| |
| | | |
| </script> | | </script> |
| </body> | | </body> |
| </html> | | </html> |