Node.jsで簡単にGitHubの3Dビューアー用のSTLファイルを作る
こんばんは、保育園の夕涼み会に次男は高熱を出して行けませんでしたが、週末で元気なり、週明けからは保育園に通えました。@kjunichiです。
背景
Dockerで遊んでいたらDocker Meetup Tokyo #3 を開催しましたというページを見つけ、このページにDockerのマスコットがポリゴン化されている画像が貼ってあり、ページにリンクを辿ると、そこはGithubのページでこのDockerのマスコットがグリグリ回っており、ここでSTLファイルなるものの存在を初めて知りました。
Hogan.jsを使えば超簡単にSTLが吐ける
STLのフォーマットはアスキー形式でもOKであり、形状の情報のみなので、かなり単純です。Hogan.jsに限らずテンプレート化が超簡単です。
solid {{objName}} {{#facets}} facet normal {{ni}} {{nj}} {{nk}} outer loop vertex {{v1x}} {{v1y}} {{v1z}} vertex {{v2x}} {{v2y}} {{v2z}} vertex {{v3x}} {{v3y}} {{v3z}} endloop endfacet {{/facets}}
といった、Hogan.js用のテンプレートファイルを用意して、後は、このテンプレートに対応したJSONを作成して、Hogan.jsに渡せば、あっという間に、STLファイルが出来上がり!
まずは1枚のポリゴン
npm install hogan.js
var fs = require("fs"); var hogan = require("hogan.js"); // エンコード指定することで、文字列として読み込む。 var template = fs.readFileSync("./stl.hjs","utf8"); var outObj = {}; var facets = []; outObj.objName = "Test STL file"; i = 0; facets[i] = {}; facets[i].ni = 0; facets[i].nj = 1; facets[i].nk = 0; facets[i].v1x = 1; facets[i].v1y = 0; facets[i].v1z = 1; facets[i].v2x = 1; facets[i].v2y = 0; facets[i].v2z = -1; facets[i].v3x = -1; facets[i].v3y = 0; facets[i].v3z = -1; outObj.facets = facets; console.log(hogan.compile(template).render(outObj));
シュタイナーのローマ曲面をSTLで吐きだす
npm install hogan.js
に加えて、ベクトル演算を楽する為、Three.jsを入れます。
npm install three
var fs = require("fs"); var THREE = require("three"); var hogan = require("hogan.js"); // エンコード指定することで、文字列として読み込む。 var template = fs.readFileSync("./stl.hjs", "utf8"); // calculate Roman surface by u,v parameter. function RomanSurface(u, v) { var x = Math.sin(u) * Math.cos(u) * Math.sin(v) * Math.sin(v); var y = Math.sin(u) * Math.sin(v) * Math.cos(v); var z = Math.cos(u) * Math.sin(v) * Math.cos(v); return new THREE.Vector3(x, y, z); } var ustep = 0.1; var vstep = 0.2; var pCount = 0; var outObj = {}; var facets = []; var i = 0; for (v = 0; v < Math.PI / 2; v += vstep) { for (u = 0; u < 2 * Math.PI; u += ustep) { v1 = RomanSurface(u, v); v2 = RomanSurface(u + ustep, v); v3 = RomanSurface(u + ustep, v + vstep); v4 = RomanSurface(u, v + vstep); var nvx1 = v1.x - v2.x; var nvy1 = v1.y - v2.y; var nvz1 = v1.z - v2.z; var nvx2 = v4.x - v2.x; var nvy2 = v4.y - v2.y; var nvz2 = v4.z - v2.z; var faceNormal = new THREE.Vector3(); faceNormal.crossVectors(new THREE.Vector3(nvx1, nvy1, nvz1), new THREE.Vector3(nvx2, nvy2, nvz2)); faceNormal.normalize(); facets[i] = {}; facets[i].ni = faceNormal.x; facets[i].nj = faceNormal.y; facets[i].nk = faceNormal.z; facets[i].v1x = v2.x; facets[i].v1y = v2.y; facets[i].v1z = v2.z; facets[i].v2x = v3.x; facets[i].v2y = v3.y; facets[i].v2z = v3.z; facets[i].v3x = v1.x; facets[i].v3y = v1.y; facets[i].v3z = v1.z; i++; facets[i] = {}; facets[i].ni = faceNormal.x; facets[i].nj = faceNormal.y; facets[i].nk = faceNormal.z; facets[i].v1x = v3.x; facets[i].v1y = v3.y; facets[i].v1z = v3.z; facets[i].v2x = v4.x; facets[i].v2y = v4.y; facets[i].v2z = v4.z; facets[i].v3x = v1.x; facets[i].v3y = v1.y; facets[i].v3z = v1.z; i++; } } outObj.objName = "romansurface"; outObj.facets = facets; console.log(hogan.compile(template).render(outObj));
まとめ
一昔前ならば、この手のテキスト処理は、Perlで自分は書いてましたが、最近は何でもJavaScriptでやることが多く、別件でXMLファイルの生成をHogan.jsで行ったことがあり、今回のJavaScriptによるテンプレートエンジンの利用を思い付きました。