Hemeshチュートリアル その1

[Hemesh](http://he_mesh.wblut.com/)はProcessingで3Dオブジェクトを作る時に役に立つライブラリです。
githubは[こちら](https://github.com/wblut/HE_Mesh)

#基本的な紹介
Hemeshはhalf-edged mesh geometryの略で、立体を表現する際にhalf-edged mesh geometryという方式を利用しています。
(half-edged mesh geometryについては[こちら](http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml))

Hemeshで扱うクラスには基本的に4種類あって、全ての形を

- HE_ : メッシュ関係のデータ
- HEC_ : 基本的な幾何学・配置

の2種類のクラスで作ります。

そして、HEM_クラスで変形等を指定します。WBクラスというものが演算・レンダリングを担当します。

#基本ロジック
HE_Meshでメッシュ初期化して、HEC_でメッシュを作るというのが基本。
HE_Meshではコアとなるデータにアクセスできるし、HEC_ではいろんな形と、それに関係するメソッドにアクセスできます。
例えばHEC_Cubeクラスを使うとedge, segment height, width, depth, inner/mid/outer radius等のメソッドを使うことができます。

#サンプルプログラム
```java:A_cube_basic_setup.pde
/*
PLEASE READ INTRO TAB
*/

/////////////////////////// GLOBALS ////////////////////////////
// LIBRARY IMPORT
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;
import processing.opengl.*;

// HEMESH CLASSES & OBJECTS
HE_Mesh MESH; // Our mesh object
WB_Render RENDER; // Our render object

// CAM
import peasy.*;
PeasyCam CAM;

/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, OPENGL);
  CAM = new PeasyCam(this, 150);  

  // OUR CREATOR 
  HEC_Cube creator = new HEC_Cube(); 

  //CREATOR PARMAMETERS
  creator.setEdge(70); // edge length in pixels

  MESH = new HE_Mesh(creator); // add our creator object to our mesh object
  RENDER = new WB_Render(this); // RENDER object initialise
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  // CAMERA
  CAM.beginHUD(); // this method disables PeasyCam for the commands between beginHUD & endHUD
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  CAM.endHUD();

  // HEMESH
  // We draw our faces using the RENDER object
  noStroke();
  fill(0, 255, 255);
  RENDER.drawFaces( MESH ); // Draw MESH faces

  stroke(255, 0, 0);
  RENDER.drawEdges( MESH ); // Draw MESH edges
}
```

実行結果
<img width="350" alt="スクリーンショット 2016-08-11 19.57.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/d3f25425-2260-5be2-ab51-ae3ecb1116ca.png">


#セットアップ
まずglobalで

>HE_Mesh MESH;

を宣言。
そして、processingのsetup()関数内で

>HEC_Cube creator = new HEC_Cube();

のようにメッシュを作るobjectをつくった後、

>MESH = new HE_Mesh(creator);

としてHE_Meshインスタンスとしてコールします。

また、WB_Renderクラスはライブラリ内の全ての関数を提供するもので、コードの最初に初期化しておきます。画面への結果表示にも使います。

1. globalで WB_Render RENDER;
2. setup();で RENDER = new WB_Render(this);
3. draw();で RENDER.drawFaces( MESH ); // Notice we add the mesh object within the parentheses.

#PeasyCam
座標変換やマウスとのインタラクション用にPeasyCamライブラリも使うと吉です。
http://mrfeinberg.com/peasycam/

#基本メソッド
HEC_Cube creatorのメソッドを紹介します。

```java:B_cube_basic_setup.pde
/*
PLEASE READ INTRO TAB
*/

/////////////////////////// GLOBALS ////////////////////////////
// LIBRARY IMPORT
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;
import processing.opengl.*;

// HEMESH CLASSES & OBJECTS
HE_Mesh MESH; // Our mesh object
WB_Render RENDER; // Our render object

// CAM
import peasy.*;
PeasyCam CAM;

/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, OPENGL);
  CAM = new PeasyCam(this, 150);  
  
  // OUR CREATOR
  HEC_Cube creator = new HEC_Cube(); 
  
  //CREATOR PARMAMETERS
  creator.setEdge(70); // edge length in pixels
  creator.setWidthSegments(4).setHeightSegments(4).setDepthSegments(4); // keep these small
  
  //alternatively 
  //creator.setRadius(50);
  //creator.setInnerRadius(50);// radius of sphere inscribed in cube
  //Try these two
  //creator.setOuterRadius(25);// radius of sphere circumscribing cube
  //creator.setMidRadius(10);// radius of sphere tangential to edges
  
  // These params set the initial position & need to be initialised seperately 
  // from other params such as height / setWidthSegments / edges ...
  creator.setCenter(0, 0, 0).setZAxis(1, 1, 1).setZAngle(PI/4);
 
  MESH = new HE_Mesh(creator);
  
  //MESH.triangulate(); // this is one of many methods we can access from the HE_Mesh class
  RENDER = new WB_Render(this); // RENDER object initialise
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  // CAMERA
  CAM.beginHUD(); // this method disables PeasyCam for the commands between beginHUD & endHUD
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  CAM.endHUD();
  
  // HEMESH
  // We draw our faces, vertices and edges using the RENDER object
  noStroke();
  fill(0, 0, 255);
  RENDER.drawFaces( MESH ); // Draw MESH faces
  
  fill(0, 255, 0);
  RENDER.drawVertices( 2, MESH ); // Draw MESH vertices
  
  
  stroke(255,0,0);
  RENDER.drawEdges( MESH ); // Draw MESH edges
  //RENDER.drawFaceNormals( 10, MESH );
}

void keyPressed() {
    if (key == 's') {
    saveFrame("screenShot_###.png");
    println("screen shot taken");
   }
  
}
```

実行結果
<img width="497" alt="スクリーンショット 2016-08-11 20.57.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/09d58da4-b69a-0979-9127-3792764a3505.png">

draw();内にfaces, edges, vertices, normals(法線)を見るためのメソッドも含んでいます。

>RENDER.drawFaces( MESH );
RENDER.drawVertices( 2, MESH );
RENDER.drawEdges( MESH );
RENDER.drawFaceNormals( 10, MESH );

setup()でコメントアウトしているtriangulationメソッドをアクティベートすると、cubesが違う方法で描画されるようになるのがわかります。上記メソッドはデパッグにも便利です。
<img width="458" alt="スクリーンショット 2016-08-11 20.58.57.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/28e6f811-4759-6788-752c-6f30ef5287d0.png">

drawVertex, drawVertexNormalsとかも便利そうです。

#Modifires
Modifiersを使うには、まずHEM_modifierクラスを選択して、いろいろパラメータをいじって、modify()を実行してメッシュに反映させます。

```java:C_cube_modif_extrude.pde
/*
PLEASE READ INTRO TAB
 */

/////////////////////////// GLOBALS ////////////////////////////
// LIBRARY IMPORT
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;
import processing.opengl.*;

// HEMESH CLASSES & OBJECTS
HE_Mesh MESH; // Our mesh object
WB_Render RENDER; // Our render object

// CAM
import peasy.*;
PeasyCam CAM;

/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, OPENGL);
  CAM = new PeasyCam(this, 400);  

  // OUR CREATOR
  HEC_Cube creator = new HEC_Cube(); 

  //CREATOR PARMAMETERS
  creator.setEdge(70); 
  creator.setCenter(0, 0, 0).setZAxis(1, 1, 1).setZAngle(PI/4);
  // Activate this line to see what happens
  //creator.setWidthSegments(4).setHeightSegments(4).setDepthSegments(4); // keep these small

  MESH = new HE_Mesh(creator); // add our creator object to our mesh object

  // MODIFIER : SIMPLE EXTRUSION MODIFIER 
  HEM_Extrude extrude = new HEM_Extrude().setDistance(70);
  MESH.modify( extrude ); // ADD OUR MODIFIER TO THE MESH

  RENDER = new WB_Render(this); 
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  // CAMERA
  CAM.beginHUD(); 
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  CAM.endHUD();
  
  // HEMESH
  // To visualise better what is happening, I've activated the edges and disactivated the faces.
  /*
  noStroke();
  fill(0, 0, 255);
  RENDER.drawFaces( MESH ); // Draw MESH faces
  */
  
  stroke(0, 0, 255);
  strokeWeight(5);
  RENDER.drawEdges( MESH ); // Draw MESH edges
}

void keyPressed() {
  if (key == 'r') {
    setup();
  }
  if (key == 's') {
    saveFrame("screenShot.png");
  }
}
```

実行結果
<img width="329" alt="スクリーンショット 2016-08-11 21.03.26.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/b60b2b8c-8794-80ea-719a-fe1d90c2ed3c.png">

要は押し出しです
>HEM_Extrude extrude = new HEM_Extrude().setDistance(70);
MESH.modify( extrude ); // ADD OUR MODIFIER TO THE MESH

segment width, height, depthはデフォルトでは1です。
creator.setWidthSegments(4).setHeightSegments(4);
などと指定すると、
<img width="352" alt="スクリーンショット 2016-08-11 21.07.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/45d5f63c-4b6e-a14a-c2da-68cb3b37ef47.png">
こんな風になります。

#Modifierの組み合わせ

```java:C_cube_modif_chamfer.pde
/*
PLEASE READ INTRO TAB
 */

/////////////////////////// GLOBALS ////////////////////////////
// LIBRARY
import wblut.math.*;
import wblut.processing.*;
import wblut.core.*;
import wblut.hemesh.*;
import wblut.geom.*;
import processing.opengl.*;


// HEMESH CLASSES & OBJECTS
HE_Mesh MESH; // Our mesh object
WB_Render RENDER; // Our render object
HEC_Cube creator; // Our creator object

// CAM
import peasy.*;
PeasyCam CAM;

/////////////////////////// SETUP ////////////////////////////

void setup() {
  size(800, 600, OPENGL);
  CAM = new PeasyCam(this, 150);  

  creator = new HEC_Cube(); // Our creator 

  //CREATOR PARMAMETERS
  creator.setEdge(60); 
  MESH = new HE_Mesh(creator);  // ADD OUR CREATOR PARAMETERS TO OUR MESH

  // SIMPLE CHAMFER MODIFIER
  HEM_ChamferCorners chamfer = new HEM_ChamferCorners().setDistance(20);
  HEM_ChamferEdges edges = new HEM_ChamferEdges().setDistance(5);

  MESH.modify( chamfer ); // ADD OUR MODIFIER TO THE MESH
  MESH.modify( edges );

  RENDER = new WB_Render(this); // RENDER MESH
}

/////////////////////////// DRAW ////////////////////////////
void draw() {
  background(255);
  //CAMERA
  CAM.beginHUD(); // this method disables PeasyCam for the commands between beginHUD & endHUD
  directionalLight(255, 255, 255, 1, 1, -1);
  directionalLight(127, 127, 127, -1, -1, 1);
  CAM.endHUD();

  //HEMESH
  /*
  noStroke();
  fill(0, 0, 255);
  RENDER.drawFaces( MESH ); // Draw MESH faces
*/
  
  stroke(0, 0, 255);
  strokeWeight( 5 );
   RENDER.drawEdges( MESH ); //Draw MESH edges
   
}

// SOME KEYS INTERACTION
void keyPressed() {

  if (key == 'e') {
    // Hemesh includes a method for exporting geometry
    // in stl file format wich is very handy for 3D printing ;–)
    HET_Export.saveToSTL(MESH, sketchPath("export.stl"), "test");
  }
  
   if (key == 's') {
    saveFrame("screenShot_###.png");
    println("screen shot taken");
   }
  if (key == 'o') {
    // reset camera origin positions  - do this before
    // exporting your shape so your shape is positioned
    // on a flat plane ready for 3D printing
    CAM.reset(1000);
  }
  // Print camera position - could be helpful
  if (key == 'p') {
    float[] camPos = CAM.getPosition();
    println(camPos);
  }
}
```

実行結果
<img width="285" alt="スクリーンショット 2016-08-11 21.22.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/fcb88e9c-95b5-b020-737b-33b9a5bb11d1.png">

chamferの数値をいじるとちがった形にもなります。
<img width="212" alt="スクリーンショット 2016-08-11 21.24.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/8e9e5601-0796-e60e-6ada-a3d521665666.png">

ここでstlに書き出そうとすると、壊れてしまいました。
<img width="350" alt="スクリーンショット 2016-08-11 21.24.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/1cf9bd06-56fd-1ea7-b5da-f266058e948d.png">

これは保存する前にMESH.triangulate()を実行することで解決できました。
<img width="300" alt="スクリーンショット 2016-08-11 21.28.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/108483/b064e441-ec5b-6d4e-1a56-fb4fd5c80979.png">


[ここ](http://freeartbureau.org/fab_activity/hemesh-3d-printing/)にチュートリアルがあります。