stl.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. Copyright (C) 2014-2015 H3XL, Inc
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 3 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. function importSTL(stl,fn) {
  15. var isAscii = true;
  16. for (var i = 0; i < stl.length; i++) {
  17. if (stl[i].charCodeAt(0) == 0) {
  18. isAscii = false;
  19. break;
  20. }
  21. }
  22. console.log("STL file is ascii: ",isAscii);
  23. var stuff;
  24. if(isAscii) {
  25. stuff = importAsciiSTL(stl,fn);
  26. } else {
  27. stuff = importBinarySTL(stl,fn);
  28. }
  29. return stuff;
  30. }
  31. function importBinarySTL(stl,fn) {
  32. //fn = filename
  33. //http://en.wikipedia.org/wiki/STL_(file_format)#Binary_STL
  34. var vertices = [];
  35. var br = new BinaryReader(stl);
  36. br.seek(80); //skip the header
  37. var totalTriangles = br.readUInt32(); // total num of triangles in the stl
  38. // I want to get a "center" for the stl that I can put in a comment
  39. // for wayward stls.
  40. var minV = [10000,10000,10000];
  41. var maxV = [-10000,-10000,-10000];
  42. for (var tr = 0; tr < totalTriangles; tr++) {
  43. /*
  44. UINT8[80] – Header
  45. UINT32 – Number of triangles
  46. foreach triangle
  47. REAL32[3] – Normal vector
  48. REAL32[3] – Vertex 1
  49. REAL32[3] – Vertex 2
  50. REAL32[3] – Vertex 3
  51. UINT16 – Attribute byte count
  52. end */
  53. // -- read the normal vector, throw it away.
  54. br.readFloat();
  55. br.readFloat();
  56. br.readFloat();
  57. // -- The next three floats are the vertices, so save them
  58. var v1 = []; v1.push(br.readFloat()); v1.push(br.readFloat()); v1.push(br.readFloat());
  59. var v2 = []; v2.push(br.readFloat()); v2.push(br.readFloat()); v2.push(br.readFloat());
  60. var v3 = []; v3.push(br.readFloat()); v3.push(br.readFloat()); v3.push(br.readFloat());
  61. // check to see if any of the vertices were Not A Number
  62. var bad = 0;
  63. for(var i=0; i<3; i++) {
  64. if(isNaN(v1[i])) bad++;
  65. if(isNaN(v2[i])) bad++;
  66. if(isNaN(v3[i])) bad++;
  67. }
  68. if(bad > 0) {
  69. console.log("this many bad vertices: ",bad);
  70. }
  71. // read the attribute byte count.
  72. br.readUInt16();
  73. // if this was a good triangle, push the vertices onto the stack.
  74. if (!bad) {
  75. // got some vertices
  76. vertices.push(v1);
  77. vertices.push(v2);
  78. vertices.push(v3);
  79. // save max/min values for centering purposes
  80. for (var i=0; i<3; i++) {
  81. minV[i] = Math.min(minV[i],v1[i]);
  82. minV[i] = Math.min(minV[i],v2[i]);
  83. minV[i] = Math.min(minV[i],v3[i]);
  84. maxV[i] = Math.max(maxV[i],v1[i]);
  85. maxV[i] = Math.max(maxV[i],v2[i]);
  86. maxV[i] = Math.max(maxV[i],v3[i]);
  87. }
  88. }
  89. }
  90. if (bad)
  91. console.log('WARNING: import errors: expect some missing or bad triangles\n');
  92. var center = [Math.round(maxV[0] - (maxV[0]-minV[0])/2),Math.round(maxV[1] - (maxV[1]-minV[1])/2),Math.round(maxV[2] - (maxV[2]-minV[2])/2)]
  93. // convert vertices to csg.js compatible code
  94. var src = vt2csg(vertices);
  95. return [src,center];
  96. }
  97. function importAsciiSTL(stl,fn) {
  98. var src = "";
  99. var vertices = [];
  100. var bad = 0;
  101. var j = 0;
  102. // I want to get a "center" for the stl that I can put in a comment
  103. // for wayward stls.
  104. var minV = [10000,10000,10000];
  105. var maxV = [-10000,-10000,-10000];
  106. var res = stl.split('\n');
  107. //console.log(res);
  108. //console.log("result length:",res.length);
  109. for (var i=0; i < res.length; i++) {
  110. // line by line we'll hunt down the vertices
  111. if (res[i].match(/facet/)) continue;
  112. if (res[i].match(/endloop/)) continue;
  113. if (res[i].match(/outer/)) {
  114. // get ready for the next triangle
  115. j = 0;
  116. bad = 0;
  117. var vdata = [];
  118. vdata[0] = [];
  119. vdata[1] = [];
  120. vdata[2] = [];
  121. continue;
  122. }
  123. if (res[i].match(/vertex/)) {
  124. // trim whitespace from the beginning and end of the line
  125. res[i] = res[i].replace(/^\s+|\s+$/g,'');
  126. var things = res[i].split(' ');
  127. vdata[j].push(parseFloat(things[1]));
  128. vdata[j].push(parseFloat(things[2]));
  129. vdata[j].push(parseFloat(things[3]));
  130. if(isNaN(vdata[j][0])) bad++;
  131. if(isNaN(vdata[j][1])) bad++;
  132. if(isNaN(vdata[j][2])) bad++;
  133. if(bad > 0) {
  134. console.log("this many bad vertices: ",bad);
  135. console.log("things are:",things);
  136. }
  137. // did I just finish the third vertex, and thus a triangle?
  138. // if this was a good triangle, push the vertices onto the stack.
  139. if (!bad && (++j == 3)) {
  140. vertices.push(vdata[0]);
  141. vertices.push(vdata[1]);
  142. vertices.push(vdata[2]);
  143. // save max/min values for centering purposes
  144. for (var k=0; k<3; k++) {
  145. minV[k] = Math.min(minV[k],vdata[0][k]);
  146. minV[k] = Math.min(minV[k],vdata[1][k]);
  147. minV[k] = Math.min(minV[k],vdata[2][k]);
  148. maxV[k] = Math.max(maxV[k],vdata[0][k]);
  149. maxV[k] = Math.max(maxV[k],vdata[1][k]);
  150. maxV[k] = Math.max(maxV[k],vdata[2][k]);
  151. }
  152. }
  153. continue;
  154. }
  155. }
  156. var center = [Math.round(maxV[0] - (maxV[0]-minV[0])/2),Math.round(maxV[1] - (maxV[1]-minV[1])/2),Math.round(maxV[2] - (maxV[2]-minV[2])/2)];
  157. src += vt2csg(vertices);
  158. return [src,center];
  159. }
  160. function vt2csg(v) { //vertices and triangles
  161. var src = '';
  162. src += "CSG.fromPolygons([";
  163. for (var p=0; p<v.length/3; p++) {
  164. // each new polygon adds three vertex vectors
  165. src += "new CSG.Polygon([";
  166. for (var i=0;i<3;i++) {
  167. src += "new CSG.Vertex(new CSG.Vector3D([";
  168. src += v[p*3 + i] + ']))' ;
  169. if (i < 2) src += ',';
  170. }
  171. src += '])';
  172. if (p < (v.length/3)-1) src += ',';
  173. }
  174. src += '])';
  175. return src;
  176. }
  177. // BinaryReader
  178. // Refactored by Vjeux <vjeuxx@gmail.com>
  179. // http://blog.vjeux.com/2010/javascript/javascript-binary-reader.html
  180. // Original
  181. //+ Jonas Raoni Soares Silva
  182. //@ http://jsfromhell.com/classes/binary-parser [rev. #1]
  183. var BinaryReader = function (data) {
  184. this._buffer = data;
  185. this._pos = 0;
  186. };
  187. BinaryReader.prototype = {
  188. /* Public */
  189. readInt8: function (){ return this._decodeInt(8, true); },
  190. readUInt8: function (){ return this._decodeInt(8, false); },
  191. readInt16: function (){ return this._decodeInt(16, true); },
  192. readUInt16: function (){ return this._decodeInt(16, false); },
  193. readInt32: function (){ return this._decodeInt(32, true); },
  194. readUInt32: function (){ return this._decodeInt(32, false); },
  195. readFloat: function (){ return this._decodeFloat(23, 8); },
  196. readDouble: function (){ return this._decodeFloat(52, 11); },
  197. readChar: function () { return this.readString(1); },
  198. readString: function (length) {
  199. this._checkSize(length * 8);
  200. var result = this._buffer.substr(this._pos, length);
  201. this._pos += length;
  202. return result;
  203. },
  204. seek: function (pos) {
  205. this._pos = pos;
  206. this._checkSize(0);
  207. },
  208. getPosition: function () {
  209. return this._pos;
  210. },
  211. getSize: function () {
  212. return this._buffer.length;
  213. },
  214. /* Private */
  215. _decodeFloat: function(precisionBits, exponentBits){
  216. var length = precisionBits + exponentBits + 1;
  217. var size = length >> 3;
  218. this._checkSize(length);
  219. var bias = Math.pow(2, exponentBits - 1) - 1;
  220. var signal = this._readBits(precisionBits + exponentBits, 1, size);
  221. var exponent = this._readBits(precisionBits, exponentBits, size);
  222. var significand = 0;
  223. var divisor = 2;
  224. var curByte = 0; //length + (-precisionBits >> 3) - 1;
  225. do {
  226. var byteValue = this._readByte(++curByte, size);
  227. var startBit = precisionBits % 8 || 8;
  228. var mask = 1 << startBit;
  229. while (mask >>= 1) {
  230. if (byteValue & mask) {
  231. significand += 1 / divisor;
  232. }
  233. divisor *= 2;
  234. }
  235. } while (precisionBits -= startBit);
  236. this._pos += size;
  237. return exponent == (bias << 1) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity
  238. : (1 + signal * -2) * (exponent || significand ? !exponent ? Math.pow(2, -bias + 1) * significand
  239. : Math.pow(2, exponent - bias) * (1 + significand) : 0);
  240. },
  241. _decodeInt: function(bits, signed){
  242. var x = this._readBits(0, bits, bits / 8), max = Math.pow(2, bits);
  243. var result = signed && x >= max / 2 ? x - max : x;
  244. this._pos += bits / 8;
  245. return result;
  246. },
  247. //shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
  248. _shl: function (a, b){
  249. for (++b; --b; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1){}
  250. return a;
  251. },
  252. _readByte: function (i, size) {
  253. return this._buffer.charCodeAt(this._pos + size - i - 1) & 0xff;
  254. },
  255. _readBits: function (start, length, size) {
  256. var offsetLeft = (start + length) % 8;
  257. var offsetRight = start % 8;
  258. var curByte = size - (start >> 3) - 1;
  259. var lastByte = size + (-(start + length) >> 3);
  260. var diff = curByte - lastByte;
  261. var sum = (this._readByte(curByte, size) >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1);
  262. if (diff && offsetLeft) {
  263. sum += (this._readByte(lastByte++, size) & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight;
  264. }
  265. while (diff) {
  266. sum += this._shl(this._readByte(lastByte++, size), (diff-- << 3) - offsetRight);
  267. }
  268. return sum;
  269. },
  270. _checkSize: function (neededBits) {
  271. if (!(this._pos + Math.ceil(neededBits / 8) < this._buffer.length)) {
  272. //throw new Error("Index out of bound");
  273. }
  274. }
  275. };