formats.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. ## License
  3. Copyright (c) 2016 yoderjen (yoderjen@gmail.com)
  4. Copyright (c) 2014 bebbi (elghatta@gmail.com)
  5. Copyright (c) 2013 Eduard Bespalov (edwbes@gmail.com)
  6. Copyright (c) 2012 Joost Nieuwenhuijse (joost@newhouse.nl)
  7. Copyright (c) 2011 Evan Wallace (http://evanw.github.com/csg.js/)
  8. Copyright (c) 2012 Alexandre Girard (https://github.com/alx)
  9. All code released under MIT license
  10. */
  11. // import the required modules if necessary
  12. if(typeof module !== 'undefined') { // used via nodejs
  13. CSG = require(lib+'csg.js').CSG;
  14. CAG = require(lib+'csg.js').CAG;
  15. Blob = require(lib+'Blob.js').Blob;
  16. }
  17. ////////////////////////////////////////////
  18. // X3D Export
  19. ////////////////////////////////////////////
  20. (function(module) {
  21. CSG.prototype.toX3D = function() {
  22. // materialPolygonLists
  23. // key: a color string (e.g. "0 1 1" for yellow)
  24. // value: an array of strings specifying polygons of this color
  25. // (as space-separated indices into vertexCoords)
  26. var materialPolygonLists = {},
  27. // list of coordinates (as "x y z" strings)
  28. vertexCoords = [],
  29. // map to look up the index in vertexCoords of a given vertex
  30. vertexTagToCoordIndexMap = {};
  31. this.polygons.map(function(p) {
  32. var red = 0,
  33. green = 0,
  34. blue = 1; // default color is blue
  35. if (p.shared && p.shared.color) {
  36. red = p.shared.color[0];
  37. green = p.shared.color[1];
  38. blue = p.shared.color[2];
  39. }
  40. var polygonVertexIndices = [],
  41. numvertices = p.vertices.length,
  42. vertex;
  43. for (var i = 0; i < numvertices; i++) {
  44. vertex = p.vertices[i];
  45. if (!(vertex.getTag() in vertexTagToCoordIndexMap)) {
  46. vertexCoords.push(vertex.pos._x.toString() + " " +
  47. vertex.pos._y.toString() + " " +
  48. vertex.pos._z.toString()
  49. );
  50. vertexTagToCoordIndexMap[vertex.getTag()] = vertexCoords.length - 1;
  51. }
  52. polygonVertexIndices.push(vertexTagToCoordIndexMap[vertex.getTag()]);
  53. }
  54. var polygonString = polygonVertexIndices.join(" ");
  55. var colorString = red.toString() + " " + green.toString() + " " + blue.toString();
  56. if (!(colorString in materialPolygonLists)) {
  57. materialPolygonLists[colorString] = [];
  58. }
  59. // add this polygonString to the list of colorString-colored polygons
  60. materialPolygonLists[colorString].push(polygonString);
  61. });
  62. // create output document
  63. var docType = document.implementation.createDocumentType("X3D",
  64. "ISO//Web3D//DTD X3D 3.1//EN", "http://www.web3d.org/specifications/x3d-3.1.dtd");
  65. var exportDoc = document.implementation.createDocument(null, "X3D", docType);
  66. exportDoc.insertBefore(
  67. exportDoc.createProcessingInstruction('xml', 'version="1.0" encoding="UTF-8"'),
  68. exportDoc.doctype);
  69. var exportRoot = exportDoc.getElementsByTagName("X3D")[0];
  70. exportRoot.setAttribute("profile", "Interchange");
  71. exportRoot.setAttribute("version", "3.1");
  72. exportRoot.setAttribute("xsd:noNamespaceSchemaLocation", "http://www.web3d.org/specifications/x3d-3.1.xsd");
  73. exportRoot.setAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema-instance");
  74. var exportScene = exportDoc.createElement("Scene");
  75. exportRoot.appendChild(exportScene);
  76. /*
  77. For each color, create a shape made of an appropriately colored
  78. material which contains all polygons that are this color.
  79. The first shape will contain the definition of all vertices,
  80. (<Coordinate DEF="coords_mesh"/>), which will be referenced by
  81. subsequent shapes.
  82. */
  83. var coordsMeshDefined = false;
  84. for (var colorString in materialPolygonLists) {
  85. var polygonList = materialPolygonLists[colorString];
  86. var shape = exportDoc.createElement("Shape");
  87. exportScene.appendChild(shape);
  88. var appearance = exportDoc.createElement("Appearance");
  89. shape.appendChild(appearance);
  90. var material = exportDoc.createElement("Material");
  91. appearance.appendChild(material);
  92. material.setAttribute("diffuseColor", colorString);
  93. material.setAttribute("ambientIntensity", "1.0");
  94. var ifs = exportDoc.createElement("IndexedFaceSet");
  95. shape.appendChild(ifs);
  96. ifs.setAttribute("solid", "true");
  97. ifs.setAttribute("coordIndex", polygonList.join(" -1 ") + " -1");
  98. var coordinate = exportDoc.createElement("Coordinate");
  99. ifs.appendChild(coordinate);
  100. if (coordsMeshDefined) {
  101. coordinate.setAttribute("USE", "coords_mesh");
  102. } else {
  103. coordinate.setAttribute("DEF", "coords_mesh");
  104. coordinate.setAttribute("point", vertexCoords.join(" "));
  105. coordsMeshDefined = true;
  106. }
  107. }
  108. var x3dstring = (new XMLSerializer()).serializeToString(exportDoc);
  109. return new Blob([x3dstring], {
  110. type: "model/x3d+xml"
  111. });
  112. };
  113. ////////////////////////////////////////////
  114. // STL Binary Export
  115. ////////////////////////////////////////////
  116. // see http://en.wikipedia.org/wiki/STL_%28file_format%29#Binary_STL
  117. CSG.prototype.toStlBinary = function() {
  118. // first check if the host is little-endian:
  119. var buffer = new ArrayBuffer(4);
  120. var int32buffer = new Int32Array(buffer, 0, 1);
  121. var int8buffer = new Int8Array(buffer, 0, 4);
  122. int32buffer[0] = 0x11223344;
  123. if (int8buffer[0] != 0x44) {
  124. throw new Error("Binary STL output is currently only supported on little-endian (Intel) processors");
  125. }
  126. var numtriangles = 0;
  127. this.polygons.map(function(p) {
  128. var numvertices = p.vertices.length;
  129. var thisnumtriangles = (numvertices >= 3) ? numvertices - 2 : 0;
  130. numtriangles += thisnumtriangles;
  131. });
  132. var headerarray = new Uint8Array(80);
  133. for (var i = 0; i < 80; i++) {
  134. headerarray[i] = 65;
  135. }
  136. var ar1 = new Uint32Array(1);
  137. ar1[0] = numtriangles;
  138. // write the triangles to allTrianglesBuffer:
  139. var allTrianglesBuffer = new ArrayBuffer(50 * numtriangles);
  140. var allTrianglesBufferAsInt8 = new Int8Array(allTrianglesBuffer);
  141. // a tricky problem is that a Float32Array must be aligned at 4-byte boundaries (at least in certain browsers)
  142. // while each triangle takes 50 bytes. Therefore we write each triangle to a temporary buffer, and copy that
  143. // into allTrianglesBuffer:
  144. var triangleBuffer = new ArrayBuffer(50);
  145. var triangleBufferAsInt8 = new Int8Array(triangleBuffer);
  146. // each triangle consists of 12 floats:
  147. var triangleFloat32array = new Float32Array(triangleBuffer, 0, 12);
  148. // and one uint16:
  149. var triangleUint16array = new Uint16Array(triangleBuffer, 48, 1);
  150. var byteoffset = 0;
  151. this.polygons.map(function(p) {
  152. var numvertices = p.vertices.length;
  153. for (var i = 0; i < numvertices - 2; i++) {
  154. var normal = p.plane.normal;
  155. triangleFloat32array[0] = normal._x;
  156. triangleFloat32array[1] = normal._y;
  157. triangleFloat32array[2] = normal._z;
  158. var arindex = 3;
  159. for (var v = 0; v < 3; v++) {
  160. var vv = v + ((v > 0) ? i : 0);
  161. var vertexpos = p.vertices[vv].pos;
  162. triangleFloat32array[arindex++] = vertexpos._x;
  163. triangleFloat32array[arindex++] = vertexpos._y;
  164. triangleFloat32array[arindex++] = vertexpos._z;
  165. }
  166. triangleUint16array[0] = 0;
  167. // copy the triangle into allTrianglesBuffer:
  168. allTrianglesBufferAsInt8.set(triangleBufferAsInt8, byteoffset);
  169. byteoffset += 50;
  170. }
  171. });
  172. return new Blob([headerarray.buffer, ar1.buffer, allTrianglesBuffer], {
  173. type: "application/sla"
  174. });
  175. };
  176. ////////////////////////////////////////////
  177. // STL String Export
  178. ////////////////////////////////////////////
  179. CSG.prototype.toStlString = function() {
  180. var result = "solid csg.js\n";
  181. this.polygons.map(function(p) {
  182. result += p.toStlString();
  183. });
  184. result += "endsolid csg.js\n";
  185. return new Blob([result], {
  186. type: "application/sla"
  187. });
  188. };
  189. CSG.Vector3D.prototype.toStlString = function() {
  190. return this._x + " " + this._y + " " + this._z;
  191. };
  192. CSG.Vertex.prototype.toStlString = function() {
  193. return "vertex " + this.pos.toStlString() + "\n";
  194. };
  195. CSG.Polygon.prototype.toStlString = function() {
  196. var result = "";
  197. if (this.vertices.length >= 3) // should be!
  198. {
  199. // STL requires triangular polygons. If our polygon has more vertices, create
  200. // multiple triangles:
  201. var firstVertexStl = this.vertices[0].toStlString();
  202. for (var i = 0; i < this.vertices.length - 2; i++) {
  203. result += "facet normal " + this.plane.normal.toStlString() + "\nouter loop\n";
  204. result += firstVertexStl;
  205. result += this.vertices[i + 1].toStlString();
  206. result += this.vertices[i + 2].toStlString();
  207. result += "endloop\nendfacet\n";
  208. }
  209. }
  210. return result;
  211. };
  212. //
  213. // OBJ (Wavefront) ASCII Export
  214. //
  215. CSG.prototype.toObj = function() {
  216. var result = "# OBJ file\n";
  217. var vertices = {};
  218. var vertexList = "";
  219. var faces = {};
  220. var count = 1;
  221. var red = 0;
  222. var green = 0;
  223. var blue = 0;
  224. var alpha = 1;
  225. var p = null;
  226. var vstring = "";
  227. var colorString = "";
  228. // I want to put different colored vertices into different groups.
  229. // group names (and keys) will be a stringy version of their color.
  230. // I don't want to list vertices more than once. Hash them and be careful of the count.
  231. for (var i = 0; i < this.polygons.length; i++) {
  232. p = this.polygons[i];
  233. if (p.shared && p.shared.color) {
  234. red = p.shared.color[0] * 255;
  235. green = p.shared.color[1] * 255;
  236. blue = p.shared.color[2] * 255;
  237. if (p.shared.color[3])
  238. alpha = p.shared.color[3];
  239. }
  240. else {
  241. red = 0;
  242. green = 0;
  243. blue = 0;
  244. alpha = 0;
  245. }
  246. colorString = red.toFixed(0) + "-" + green.toFixed(0) + "-" + blue.toFixed(0) + "-" + alpha.toFixed(0);
  247. if (!faces[colorString])
  248. faces[colorString] = "";
  249. faces[colorString] += 'f ';
  250. // now deal with the vertices
  251. for (var j = 0; j < this.polygons[i].vertices.length; j++) {
  252. var v = p.vertices[j];
  253. vstring = 'v ' + v.pos._x + ' ' + v.pos._y + ' ' + v.pos._z + '\n';
  254. if (vertices[vstring]) {
  255. // this vertex was already added to the list. We can get its index in vertexList from vertices.
  256. faces[colorString] += vertices[vstring] + ' ';
  257. }
  258. else {
  259. // this is a new vertex. Add it to the vertex list, increment count, and then add to the face.
  260. // list it as "added at this index" in vertices.
  261. vertices[vstring] = count.toString();
  262. vertexList += vstring;
  263. faces[colorString] += count.toString() + ' ';
  264. count++;
  265. }
  266. }
  267. faces[colorString] += '\n';
  268. }
  269. result += vertexList;
  270. for (i in faces) {
  271. result += 'g ' + i + '\n';
  272. result += faces[i];
  273. }
  274. return result;
  275. }
  276. ////////////////////////////////////////////
  277. // DXF Export
  278. ////////////////////////////////////////////
  279. CAG.PathsToDxf = function(paths) {
  280. var str = "999\nDXF generated by OpenJsCad\n";
  281. str += " 0\nSECTION\n 2\nHEADER\n";
  282. str += " 0\nENDSEC\n";
  283. str += " 0\nSECTION\n 2\nTABLES\n";
  284. str += " 0\nTABLE\n 2\nLTYPE\n 70\n1\n";
  285. str += " 0\nLTYPE\n 2\nCONTINUOUS\n 3\nSolid Line\n 72\n65\n 73\n0\n 40\n0.0\n";
  286. str += " 0\nENDTAB\n";
  287. str += " 0\nTABLE\n 2\nLAYER\n 70\n1\n";
  288. str += " 0\nLAYER\n 2\nOpenJsCad\n 62\n7\n 6\ncontinuous\n";
  289. str += " 0\nENDTAB\n";
  290. str += " 0\nTABLE\n 2\nSTYLE\n 70\n0\n 0\nENDTAB\n";
  291. str += " 0\nTABLE\n 2\nVIEW\n 70\n0\n 0\nENDTAB\n";
  292. str += " 0\nENDSEC\n";
  293. str += " 0\nSECTION\n 2\nBLOCKS\n";
  294. str += " 0\nENDSEC\n";
  295. str += " 0\nSECTION\n 2\nENTITIES\n";
  296. paths.map(function(path) {
  297. var numpoints_closed = path.points.length + (path.closed ? 1 : 0);
  298. str += " 0\nLWPOLYLINE\n 8\nOpenJsCad\n 90\n" + numpoints_closed + "\n 70\n" + (path.closed ? 1 : 0) + "\n";
  299. for (var pointindex = 0; pointindex < numpoints_closed; pointindex++) {
  300. var pointindexwrapped = pointindex;
  301. if (pointindexwrapped >= path.points.length) pointindexwrapped -= path.points.length;
  302. var point = path.points[pointindexwrapped];
  303. str += " 10\n" + point.x + "\n 20\n" + point.y + "\n 30\n0.0\n";
  304. }
  305. });
  306. str += " 0\nENDSEC\n 0\nEOF\n";
  307. return new Blob([str], {
  308. type: "application/dxf"
  309. });
  310. };
  311. CAG.prototype.toDxf = function() {
  312. var paths = this.getOutlinePaths();
  313. return CAG.PathsToDxf(paths);
  314. };
  315. ////////////////////////////////////////////
  316. // AMF Export
  317. ////////////////////////////////////////////
  318. CSG.prototype.toAMFString = function(m) {
  319. var result = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<amf"+(m&&m.unit?" unit=\"+m.unit\"":"")+">\n";
  320. for(var k in m) {
  321. result += "<metadata type=\""+k+"\">"+m[k]+"</metadata>\n";
  322. }
  323. result += "<object id=\"0\">\n<mesh>\n<vertices>\n";
  324. this.polygons.map(function(p) { // first we dump all vertices of all polygons
  325. for(var i=0; i<p.vertices.length; i++) {
  326. result += p.vertices[i].toAMFString();
  327. }
  328. });
  329. result += "</vertices>\n";
  330. var n = 0;
  331. this.polygons.map(function(p) { // then we dump all polygons
  332. result += "<volume>\n";
  333. if(p.vertices.length<3)
  334. return;
  335. var r = 1, g = 0.4, b = 1, a = 1, colorSet = false;
  336. if(p.shared && p.shared.color) {
  337. r = p.shared.color[0];
  338. g = p.shared.color[1];
  339. b = p.shared.color[2];
  340. a = p.shared.color[3];
  341. colorSet = true;
  342. } else if(p.color) {
  343. r = p.color[0];
  344. g = p.color[1];
  345. b = p.color[2];
  346. if(p.color.length()>3) a = p.color[3];
  347. colorSet = true;
  348. }
  349. result += "<color><r>"+r+"</r><g>"+g+"</g><b>"+b+"</b>"+(a!==undefined?"<a>"+a+"</a>":"")+"</color>";
  350. for(var i=0; i<p.vertices.length-2; i++) { // making sure they are all triangles (triangular polygons)
  351. result += "<triangle>";
  352. result += "<v1>" + (n) + "</v1>";
  353. result += "<v2>" + (n+i+1) + "</v2>";
  354. result += "<v3>" + (n+i+2) + "</v3>";
  355. result += "</triangle>\n";
  356. }
  357. n += p.vertices.length;
  358. result += "</volume>\n";
  359. });
  360. result += "</mesh>\n</object>\n";
  361. result += "</amf>\n";
  362. return new Blob([result], {
  363. type: "application/amf+xml"
  364. });
  365. };
  366. CSG.Vector3D.prototype.toAMFString = function() {
  367. return "<x>" + this._x + "</x><y>" + this._y + "</y><z>" + this._z + "</z>";
  368. };
  369. CSG.Vertex.prototype.toAMFString = function() {
  370. return "<vertex><coordinates>" + this.pos.toAMFString() + "</coordinates></vertex>\n";
  371. };
  372. // re-export CSG and CAG with the extended prototypes
  373. module.CSG = CSG;
  374. module.CAG = CAG;
  375. })(this);