non vorrei lavorare

昔はおもにプログラミングやガジェット系、今は?

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));

f:id:kjw_junichi:20170722194825g:plain

まとめ

一昔前ならば、この手のテキスト処理は、Perlで自分は書いてましたが、最近は何でもJavaScriptでやることが多く、別件でXMLファイルの生成をHogan.jsで行ったことがあり、今回のJavaScriptによるテンプレートエンジンの利用を思い付きました。

関連資料

関連記事