<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4304703524813488565</id><updated>2012-03-03T23:54:23.640+09:00</updated><category term='Python'/><category term='雑貨'/><category term='ネットワークスペシャリスト'/><category term='セキュリティ'/><category term='MySQL'/><category term='Kinect'/><category term='ラーメン'/><category term='C/C++'/><category term='OpenCV'/><category term='HLSL'/><category term='iPhoneカメラ'/><category term='MathJax'/><category term='XML'/><category term='Windows API'/><category term='GIZMON HALF D'/><category term='電子工作'/><category term='トイカメラ'/><category term='Java'/><category term='NeinGrenze 5000T'/><category term='デジタルハリネズミ'/><category term='PHP'/><category term='Arduino'/><category term='身辺雑記'/><category term='Objective-C'/><category term='Processing'/><category term='Unity'/><category term='DirectX10'/><category term='Visual Basic'/><category term='Qt'/><category term='書評'/><category term='プログラミング'/><category term='Lua'/><category term='就職活動'/><category term='OpenNI'/><category term='Twitter4J'/><category term='正規表現'/><category term='JavaScript'/><category term='Android'/><category term='数学'/><category term='LaTeX'/><category term='OpenGL'/><category term='DirectX9'/><title type='text'>Tercel::Diary</title><subtitle type='html'>ちょっとだけ背伸びをしてみよう ∩( ・ω・)∩</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default?start-index=101&amp;max-results=100'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>124</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-3557052872728193337</id><published>2012-02-28T18:26:00.004+09:00</published><updated>2012-02-28T19:02:24.535+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>ProcessingでXファイルを解析（スタティックメッシュ篇その2）</title><content type='html'>昨日の続きです。ほんのちょっと進歩してマテリアルを反映できるようになりました。&lt;br /&gt;&lt;br /&gt;こういうかっこいい戦車も……&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-2QN1DOd4JeE/T0yPe_4BoWI/AAAAAAAAAko/jm4PJ5zkNMk/s1600/20120228002.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="280" src="http://3.bp.blogspot.com/-2QN1DOd4JeE/T0yPe_4BoWI/AAAAAAAAAko/jm4PJ5zkNMk/s400/20120228002.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Metasequoia にプリセットされている「Tank」&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;ちゃんと&lt;b&gt;質感を保ったまま&lt;/b&gt;&lt;sup&gt;※&lt;/sup&gt; Processing に持ってくる事ができました。やったー！&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rh0QTVR0Z74/T0yPeChyazI/AAAAAAAAAkk/9lOaDL4ptLg/s1600/20120228001.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="318" src="http://4.bp.blogspot.com/-rh0QTVR0Z74/T0yPeChyazI/AAAAAAAAAkk/9lOaDL4ptLg/s400/20120228001.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Processing で表示した結果&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;span style="font-size: x-small;"&gt;※ というのは&lt;b&gt;大ウソ&lt;/b&gt;で、法線無視、フラットシェーディング一択という男らしい割り切り方をしています。P3D モードにおけるレンダリングのクオリティならばこのくらいばっさり行っても許容範囲な気がします（おい&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;ちなみにここだけの話、X ファイル内の&lt;b&gt;ジオメトリの頂点数とテクスチャのUV頂点数が一致していないと表示できない&lt;/b&gt;という問題点があるのですが、Metasequoia が吐く X ファイルでは特に目立った問題は起きていないため、思い切って無視しています。&lt;br /&gt;&lt;br /&gt;それでも泥縄式に拡張しただけあって、いい感じにソースが汚くなって参りました。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【Xファイル解析関連のコード】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;// -------------------------------------------------&lt;br /&gt;// =================================================&lt;br /&gt;// Xファイル形式で記述されたメッシュを表示するテスト&lt;br /&gt;//                スタティックメッシュ篇やや不完全版&lt;br /&gt;// =================================================&lt;br /&gt;// -------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// メッシュ&lt;br /&gt;class Mesh {&lt;br /&gt;  List&amp;lt;Polygon&amp;gt; polygons;&lt;br /&gt;  &lt;br /&gt;  public Mesh() {&lt;br /&gt;    polygons = new ArrayList&amp;lt;Polygon&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void add(Polygon p) {&lt;br /&gt;    polygons.add(p);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public Polygon getPolygon(int index) {&lt;br /&gt;    return polygons.get(index);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void render() {&lt;br /&gt;    if(polygons == null) return;&lt;br /&gt;    &lt;br /&gt;    for(Polygon p : polygons) p.render();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// ポリゴン&lt;br /&gt;class Polygon {&lt;br /&gt;  List&amp;lt;PVector&amp;gt; vertices;&lt;br /&gt;  List&amp;lt;PVector&amp;gt; textureCoords;&lt;br /&gt;  Material      material;&lt;br /&gt;  &lt;br /&gt;  public Polygon() {&lt;br /&gt;    vertices = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void addVertex(PVector v) {&lt;br /&gt;    vertices.add(v);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void addTextureCoord(PVector t) {&lt;br /&gt;    if(textureCoords == null) textureCoords = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;    textureCoords.add(t);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void setMaterial(Material m) {&lt;br /&gt;    material = m;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void render() {&lt;br /&gt;    if(vertices == null) return;&lt;br /&gt;    &lt;br /&gt;    if(vertices.size() == 3)      beginShape(TRIANGLES);&lt;br /&gt;    else if(vertices.size() == 4) beginShape(QUADS);&lt;br /&gt;    else                          beginShape();&lt;br /&gt;    &lt;br /&gt;    pushStyle();&lt;br /&gt;    &lt;br /&gt;    // マテリアルの設定&lt;br /&gt;    if(material != null) {&lt;br /&gt;      noStroke();&lt;br /&gt;      fill(material.faceColor);&lt;br /&gt;&lt;br /&gt;      shininess(material.power);&lt;br /&gt;      specular(material.specularColor);&lt;br /&gt;      emissive(material.emissiveColor);      &lt;br /&gt;      &lt;br /&gt;      if(material.texture != null) {&lt;br /&gt;        // テクスチャが存在する場合&lt;br /&gt;        texture(material.texture);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; vertices.size(); ++i) {&lt;br /&gt;      PVector v = vertices.get(i);&lt;br /&gt;      // 実際の頂点とテクスチャのUVが一致していない場合は表示できないよ&lt;br /&gt;      if(textureCoords != null &amp;amp;&amp;amp; &lt;br /&gt;          textureCoords.size() == vertices.size() &amp;amp;&amp;amp; &lt;br /&gt;            material != null &amp;amp;&amp;amp;&lt;br /&gt;              material.texture != null) {&lt;br /&gt;          &lt;br /&gt;        PVector tex = textureCoords.get(i);&lt;br /&gt;        textureMode(NORMALIZED);&lt;br /&gt;        vertex(v.x, v.y, v.z, tex.x, tex.y);&lt;br /&gt;      } else {&lt;br /&gt;        vertex(v.x, v.y, v.z);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    popStyle();&lt;br /&gt;    endShape();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// マテリアル&lt;br /&gt;class Material {&lt;br /&gt;  int     faceColor;&lt;br /&gt;  float   power;&lt;br /&gt;  int     specularColor;&lt;br /&gt;  int     emissiveColor;&lt;br /&gt;  PImage  texture;&lt;br /&gt;  &lt;br /&gt;  public Material() {&lt;br /&gt;    texture       = null;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// Xファイルローダクラス&lt;br /&gt;class Loader {&lt;br /&gt;  // Xファイルの文字列データが一括して格納される&lt;br /&gt;  private StringBuffer data;&lt;br /&gt;  &lt;br /&gt;  // Xファイル内の部分文字列の先頭を指すインデックス&lt;br /&gt;  private int          index;&lt;br /&gt;&lt;br /&gt;  // メッシュとマテリアルの連想配列&lt;br /&gt;  private HashMap&amp;lt;String, Mesh&amp;gt;     meshMap;&lt;br /&gt;  private HashMap&amp;lt;String, Material&amp;gt; materialMap;&lt;br /&gt;&lt;br /&gt;  // ポリゴンごとの頂点インデックス格納用&lt;br /&gt;  private List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; vertexIndices;&lt;br /&gt;&lt;br /&gt;  // メッシュ&lt;br /&gt;  private Mesh mesh;&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  public Loader() {&lt;br /&gt;    data = new StringBuffer();&lt;br /&gt;    &lt;br /&gt;    vertexIndices = new ArrayList&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    meshMap      = new HashMap&amp;lt;String, Mesh&amp;gt;();&lt;br /&gt;    materialMap  = new HashMap&amp;lt;String, Material&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // ファイル名を指定してメッシュを読み込み &lt;br /&gt;  public Mesh loadX(final String fileName) { &lt;br /&gt;&lt;br /&gt;    // 色々初期化&lt;br /&gt;    mesh = null;&lt;br /&gt;&lt;br /&gt;    vertexIndices.clear();&lt;br /&gt;    meshMap.clear();&lt;br /&gt;    materialMap.clear();&lt;br /&gt;    &lt;br /&gt;    index = 0;&lt;br /&gt;&lt;br /&gt;    data.delete(0, data.length());&lt;br /&gt;    String[] lines = loadStrings(fileName);&lt;br /&gt;    for(String line : lines) {&lt;br /&gt;      // コメント除去の処理&lt;br /&gt;      data.append(line.replaceAll("(##|//).*", "") + "\n");&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    List&amp;lt;PVector&amp;gt; textureCoordsList = null;&lt;br /&gt;    while(index &amp;lt; data.length()) {&lt;br /&gt;      String token = getNextToken();&lt;br /&gt;      &lt;br /&gt;      // テンプレートは読み飛ばす&lt;br /&gt;      if(token.equals("template")) skipBlock();&lt;br /&gt;&lt;br /&gt;      // ====================&lt;br /&gt;      // Material（前方宣言）&lt;br /&gt;      // ====================&lt;br /&gt;      else if(token.equals("Material")) loadMaterialData();&lt;br /&gt;&lt;br /&gt;      // ====================&lt;br /&gt;      // MESH&lt;br /&gt;      // ====================&lt;br /&gt;      else if(token.equals("Mesh")) mesh = loadMeshData();&lt;br /&gt;&lt;br /&gt;      // ====================&lt;br /&gt;      // MeshTextureCoords&lt;br /&gt;      // ====================&lt;br /&gt;      else if(token.equals("MeshTextureCoords")) loadTextureCoords();&lt;br /&gt;      &lt;br /&gt;      // ====================&lt;br /&gt;      // MeshMaterialList&lt;br /&gt;      // ====================&lt;br /&gt;      else if(token.equals("MeshMaterialList")) loadMaterialListData();&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;    return mesh;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // { } で囲まれたブロックを読み飛ばす&lt;br /&gt;  private void skipBlock() {&lt;br /&gt;    try {&lt;br /&gt;      // はじめの { まで読み飛ばす &lt;br /&gt;      while(!(getNextToken().equals("{")) &amp;amp;&amp;amp; index &amp;lt; data.length()) ;&lt;br /&gt;      &lt;br /&gt;      int n = 1;&lt;br /&gt;      while(n &amp;gt; 0 &amp;amp;&amp;amp; index &amp;lt; data.length()) {&lt;br /&gt;        String token = getNextToken();&lt;br /&gt;        if(token.equals("{")) ++n;&lt;br /&gt;        if(token.equals("}")) --n;&lt;br /&gt;      }&lt;br /&gt;    } catch (RuntimeException ex) {&lt;br /&gt;      ex.printStackTrace();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // Meshブロックを読む&lt;br /&gt;  private Mesh loadMeshData() {&lt;br /&gt;    Mesh mesh = new Mesh();&lt;br /&gt;&lt;br /&gt;    String token = getNextToken();&lt;br /&gt;    // トークンが { でない場合&lt;br /&gt;    if(!token.equals("{")) {&lt;br /&gt;      meshMap.put(token, mesh);&lt;br /&gt;      getNextToken();&lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;    // 頂点リストを作成&lt;br /&gt;    List&amp;lt;PVector&amp;gt; verticesList = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;    vertexIndices.clear();&lt;br /&gt;        &lt;br /&gt;    // 全頂点数を取得&lt;br /&gt;    int nVertices = Integer.parseInt(getNextToken());&lt;br /&gt;        &lt;br /&gt;    // 各頂点の座標を取得&lt;br /&gt;    for(int i = 0; i &amp;lt; nVertices; ++i) {&lt;br /&gt;      float x = Float.parseFloat(getNextToken());&lt;br /&gt;      float y = Float.parseFloat(getNextToken());&lt;br /&gt;      float z = Float.parseFloat(getNextToken());          &lt;br /&gt;      verticesList.add(new PVector(x, y, z));&lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;    // ポリゴン数を取得&lt;br /&gt;    int nPolygons = Integer.parseInt(getNextToken());&lt;br /&gt;    for(int i = 0; i &amp;lt; nPolygons; ++i) {&lt;br /&gt;      Polygon poly = new Polygon();&lt;br /&gt;      &lt;br /&gt;      // ポリゴンごとに頂点インデックスリストを作る&lt;br /&gt;      List&amp;lt;Integer&amp;gt; indices = new ArrayList&amp;lt;Integer&amp;gt;();&lt;br /&gt;      &lt;br /&gt;      // 1ポリゴンあたりの頂点数&lt;br /&gt;      int nVerticesPerPolygon = Integer.parseInt(getNextToken());&lt;br /&gt;      for(int j = 0; j &amp;lt; nVerticesPerPolygon; ++j) {&lt;br /&gt;            &lt;br /&gt;        // 頂点インデックスを取得&lt;br /&gt;        int verticesIndex = Integer.parseInt(getNextToken());&lt;br /&gt;&lt;br /&gt;        indices.add(verticesIndex);&lt;br /&gt;        &lt;br /&gt;        // 頂点インデックスから該当する頂点を取得してポリゴンに追加&lt;br /&gt;        poly.addVertex(verticesList.get(verticesIndex));&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      vertexIndices.add(indices);&lt;br /&gt;      mesh.add(poly);&lt;br /&gt;    }&lt;br /&gt;    return mesh;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // Materialブロックを読む&lt;br /&gt;  private Material loadMaterialData() {&lt;br /&gt;    Material material = new Material();&lt;br /&gt;&lt;br /&gt;    String token = getNextToken();&lt;br /&gt;    // トークンが { でない場合&lt;br /&gt;    if(!token.equals("{")) {&lt;br /&gt;      materialMap.put(token, material);&lt;br /&gt;      getNextToken();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    int r = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    int g = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    int b = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    int a = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    &lt;br /&gt;    material.faceColor = a &amp;lt;&amp;lt; 24 | r &amp;lt;&amp;lt; 16 | g &amp;lt;&amp;lt; 8 | b;&lt;br /&gt;    &lt;br /&gt;    material.power = Float.parseFloat(getNextToken());&lt;br /&gt;&lt;br /&gt;    r = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    g = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    b = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;&lt;br /&gt;    material.specularColor = 0xFF &amp;lt;&amp;lt; 24 | r &amp;lt;&amp;lt; 16 | g &amp;lt;&amp;lt; 8 | b;&lt;br /&gt;&lt;br /&gt;    r = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    g = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;    b = 0xFF &amp;amp; round(255 * Float.parseFloat(getNextToken()));&lt;br /&gt;&lt;br /&gt;    material.emissiveColor = 0xFF &amp;lt;&amp;lt; 24 | r &amp;lt;&amp;lt; 16 | g &amp;lt;&amp;lt; 8 | b;&lt;br /&gt;&lt;br /&gt;    if(getNextToken().equals("TextureFilename")) {&lt;br /&gt;      &lt;br /&gt;      getNextToken();  // トークン「{」&lt;br /&gt;      String textureFileName = getNextToken();&lt;br /&gt;      material.texture = loadImage(textureFileName);&lt;br /&gt;    }&lt;br /&gt;    return material;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // MeshTextureCoordsブロックを読む&lt;br /&gt;  private void loadTextureCoords() {&lt;br /&gt;    getNextToken();  // 「{」&lt;br /&gt;    int nCoords = Integer.parseInt(getNextToken());&lt;br /&gt;    List&amp;lt;PVector&amp;gt; coordsList = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    if(mesh == null) return;&lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; nCoords; ++i) {&lt;br /&gt;      float u = Float.parseFloat(getNextToken());&lt;br /&gt;      float v = Float.parseFloat(getNextToken());&lt;br /&gt;      &lt;br /&gt;      PVector texCoords = new PVector(u, v);&lt;br /&gt;      coordsList.add(texCoords);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; vertexIndices.size(); ++i) {&lt;br /&gt;      Polygon       poly    = mesh.getPolygon(i);&lt;br /&gt;      List&amp;lt;Integer&amp;gt; indices = vertexIndices.get(i);&lt;br /&gt;      for(Integer index : indices) {&lt;br /&gt;        poly.addTextureCoord(coordsList.get(index));&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // MeshMaterialListブロックを読む&lt;br /&gt;  private void loadMaterialListData() {&lt;br /&gt;    getNextToken();  // 「{」&lt;br /&gt;    &lt;br /&gt;    int nMaterials = Integer.parseInt(getNextToken());&lt;br /&gt;    int nPolygons  = Integer.parseInt(getNextToken());&lt;br /&gt;    &lt;br /&gt;    // マテリアルインデックスリストを作成&lt;br /&gt;    List&amp;lt;Integer&amp;gt; materialIndicesList = new ArrayList&amp;lt;Integer&amp;gt;();&lt;br /&gt;    for(int i = 0; i &amp;lt; nPolygons; ++i)&lt;br /&gt;      materialIndicesList.add(Integer.parseInt(getNextToken()));&lt;br /&gt;&lt;br /&gt;    // Xで定義されているマテリアルを読み込み    &lt;br /&gt;    List&amp;lt;Material&amp;gt; materialList = new ArrayList&amp;lt;Material&amp;gt;();&lt;br /&gt;    for(int i = 0; i &amp;lt; nMaterials; ++i) {&lt;br /&gt;      String token = getNextToken();&lt;br /&gt;      while(token.equals("{")) token = getNextToken();&lt;br /&gt;      while(token.equals("}")) token = getNextToken();&lt;br /&gt;      &lt;br /&gt;      Material material;&lt;br /&gt;      if(token.equals("Material")) material = loadMaterialData();&lt;br /&gt;      else                         material = materialMap.get(token);&lt;br /&gt;      materialList.add(material);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; nPolygons; ++i) {&lt;br /&gt;      Polygon poly = mesh.getPolygon(i);&lt;br /&gt;      int index    = materialIndicesList.get(i);&lt;br /&gt;      Material m   = materialList.get(index);&lt;br /&gt;      &lt;br /&gt;      poly.setMaterial(m);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // 次のトークンを得る&lt;br /&gt;  private String getNextToken() {&lt;br /&gt;    // 最初にインデックスの範囲チェック&lt;br /&gt;    if(!(index &amp;lt; data.length())) return "";  // ファイルの末尾に達した場合&lt;br /&gt;    &lt;br /&gt;    // デリミタ（区切り文字）の読み飛ばし&lt;br /&gt;    String delimiters = " \t\r\n,;\"";&lt;br /&gt;    while(index &amp;lt; data.length() &amp;amp;&amp;amp; &lt;br /&gt;          strChr(delimiters, data.charAt(index)))  &lt;br /&gt;      ++index;&lt;br /&gt;&lt;br /&gt;    if(!(index &amp;lt; data.length())) return "";  // ファイルの末尾&lt;br /&gt; &lt;br /&gt;    // 中括弧の検出&lt;br /&gt;    char[] c = { data.charAt(index) };&lt;br /&gt;    if(c[0] == '{' || c[0] == '}') {&lt;br /&gt;      ++index;&lt;br /&gt;      return new String(c);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // それ以外のトークン検出&lt;br /&gt;    delimiters += "{}";&lt;br /&gt;    int startIndex = index;&lt;br /&gt;    int endIndex   = index;&lt;br /&gt;    while(endIndex &amp;lt; data.length() &amp;amp;&amp;amp;&lt;br /&gt;          !strChr(delimiters, data.charAt(endIndex)))&lt;br /&gt;      ++endIndex;&lt;br /&gt;    index = endIndex;&lt;br /&gt;    return data.substring(startIndex, endIndex);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // 文字列シーケンスsの中に、cが含まれているかどうかをチェック&lt;br /&gt;  private boolean strChr(final CharSequence s, final char c) {&lt;br /&gt;    for(int i = 0; i &amp;lt; s.length(); ++i)&lt;br /&gt;      if(s.charAt(i) == c) return true;&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【使い方】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java" name="code"&gt;Mesh mesh;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(640, 480, P3D);&lt;br /&gt;  &lt;br /&gt;  // ファイル名を指定してXをロード  &lt;br /&gt;  mesh = new Loader().loadX("3 テクスチャ付き.x");&lt;br /&gt;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  camera();&lt;br /&gt;  lights();&lt;br /&gt;  noStroke();&lt;br /&gt;&lt;br /&gt;  camera(-500, 0, -500, 0, 0, 0, 0, -1, 0);&lt;br /&gt;  scale(200);&lt;br /&gt;  &lt;br /&gt;  // メッシュの描画&lt;br /&gt;  mesh.render();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ちなみにエラー処理とかはあまりしていないので、&lt;b&gt;X ファイルが規格外だったりテクスチャファイルが見つからなかったりすると平気で RuntimeException をぶん投げる&lt;/b&gt;と思いますがそこはご愛嬌という事で。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【MESH GURUのサンプルに挑戦】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;鎌田茂雄 著 『&lt;a href="http://www.northbrain.org/book/NUKE/MESH_GURU_info.html"&gt;MESH GURU&lt;/a&gt;』 の第 8 章で使用されているサンプルのうち、アニメーションを含まない X ファイルをパースしてみる事にします。以下では、Processing における実行結果と、使用した X ファイルをそのまま掲載しています。&lt;br /&gt;&lt;br /&gt;『MESH GURU』 のサンプルは &lt;b&gt;Metasequoia が吐く X ファイルとはじゃっかん構造が違い&lt;/b&gt;、冒頭で名前つきの Material を定義して、以降は識別子で Material を参照する形になっています。なんとも意地の悪い書き方ではありますが、可能な限り対応する事にしました。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【世界最小Xファイル】 &lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-e06zjh8AL78/T0yWIwO6uQI/AAAAAAAAAlE/jUVbD0fe5dM/s1600/20120228_meshguru_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="318" src="http://1.bp.blogspot.com/-e06zjh8AL78/T0yWIwO6uQI/AAAAAAAAAlE/jUVbD0fe5dM/s400/20120228_meshguru_01.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;pre class="c" name="code"&gt;xof 0303txt 0032&lt;br /&gt;&lt;br /&gt;///////////////////メッシュ//////////////////////////&lt;br /&gt;Mesh Mesh_Triangle&lt;br /&gt;{&lt;br /&gt;  //////頂点データ部////////////&lt;br /&gt;   3;&lt;br /&gt;  -1.0;-1.0;0.0;,&lt;br /&gt;  -1.0;1.0;0.0;,&lt;br /&gt;  1.0;-1.0;0.0;;&lt;br /&gt;  //////ポリゴンデータ部///////&lt;br /&gt;  1;&lt;br /&gt;  3;0,1,2;; &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【マテリアル付き】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-nwz2jo3bCJI/T0yXD0XcVtI/AAAAAAAAAlM/tOTEYwMWADw/s1600/20120228_meshguru_02.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="318" src="http://3.bp.blogspot.com/-nwz2jo3bCJI/T0yXD0XcVtI/AAAAAAAAAlM/tOTEYwMWADw/s400/20120228_meshguru_02.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;pre class="c" name="code"&gt;xof 0303txt 0032&lt;br /&gt;//////////////////////////マテリアル/////////////////////&lt;br /&gt;Material Triangle_Blue&lt;br /&gt;{&lt;br /&gt;  0.0;0.0;1.0;1.0;;&lt;br /&gt;  51.2;&lt;br /&gt;  0.0;0.0;0.0;;&lt;br /&gt;  0.0;0.0;0.0;;&lt;br /&gt;}&lt;br /&gt;///////////////////メッシュ//////////////////////////&lt;br /&gt;Mesh Mesh_Triangle&lt;br /&gt;{&lt;br /&gt;   3;&lt;br /&gt;  -1.0;-1.0;0.0;,&lt;br /&gt;  -1.0;1.0;0.0;,&lt;br /&gt;  1.0;-1.0;0.0;;&lt;br /&gt;  1;&lt;br /&gt;  3;0,1,2;;&lt;br /&gt;  ///////////////////マテリアルリスト////////////&lt;br /&gt;  MeshMaterialList &lt;br /&gt;  {&lt;br /&gt;    1;&lt;br /&gt;    1;&lt;br /&gt;    0;&lt;br /&gt;    { Triangle_Blue }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【テクスチャ付き】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-rasrFZOHPb0/T0yYFZwpOqI/AAAAAAAAAlU/Hx885SMD6yc/s1600/20120228_meshguru_03.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="318" src="http://1.bp.blogspot.com/-rasrFZOHPb0/T0yYFZwpOqI/AAAAAAAAAlU/Hx885SMD6yc/s400/20120228_meshguru_03.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;pre class="c" name="code"&gt;xof 0303txt 0032&lt;br /&gt;//////////////////////////マテリアル/////////////////////&lt;br /&gt;Material Triangle_Tex&lt;br /&gt;{&lt;br /&gt;  1.0;1.0;1.0;1.0;;&lt;br /&gt;  51.2;&lt;br /&gt;  0.0;0.0;0.0;;&lt;br /&gt;  0.0;0.0;0.0;;&lt;br /&gt;  TextureFilename&lt;br /&gt;  {&lt;br /&gt;    "コンクリート.bmp";&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;///////////////////メッシュ//////////////////////////&lt;br /&gt;Mesh Mesh_Triangle&lt;br /&gt;{&lt;br /&gt;   3;&lt;br /&gt;  -1.0;-1.0;0.0;,&lt;br /&gt;  -1.0;1.0;0.0;,&lt;br /&gt;  1.0;-1.0;0.0;;&lt;br /&gt;  1;&lt;br /&gt;  3;0,1,2;;&lt;br /&gt;  ///////////////////テクスチャ座標///////////////&lt;br /&gt;  MeshTextureCoords&lt;br /&gt;  {&lt;br /&gt;    3;&lt;br /&gt;    0.0;1.0;,&lt;br /&gt;    0.0;0.0;,&lt;br /&gt;    1.0;1.0;;&lt;br /&gt;  }&lt;br /&gt;  ///////////////////マテリアルリスト////////////&lt;br /&gt;  MeshMaterialList &lt;br /&gt;  {&lt;br /&gt;    1;&lt;br /&gt;    1;&lt;br /&gt;    0;&lt;br /&gt;    { Triangle_Tex }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div style="color: red;"&gt;※ 文字コードが UTF-8 以外の場合、日本語のテクスチャを読み込もうとした瞬間に死にます。&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【おまけ：魔道少女】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-fjTMyVsVi74/T0yPgNYZeuI/AAAAAAAAAk4/vcdFggV7Mso/s1600/20120228004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="280" src="http://1.bp.blogspot.com/-fjTMyVsVi74/T0yPgNYZeuI/AAAAAAAAAk4/vcdFggV7Mso/s400/20120228004.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;↓&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-ppNwyBpAoBs/T0yPfix4yoI/AAAAAAAAAkw/B4Y4ooprLt0/s1600/20120228003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="318" src="http://1.bp.blogspot.com/-ppNwyBpAoBs/T0yPfix4yoI/AAAAAAAAAkw/B4Y4ooprLt0/s400/20120228003.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【おまけその2：どせいさん】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://asura.iaigiri.com/OpenGL/gl44.html"&gt;こちらのサイト&lt;/a&gt;から拝借した「どせいさん」を表示してみました。なお、左右が反転しているのは、座標軸の取り方によるものです。 &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-rAMA3EO6cnA/T0ylM3Hv9lI/AAAAAAAAAlc/TvTfD6MUBKM/s1600/20120228_dosei.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="318" src="http://4.bp.blogspot.com/-rAMA3EO6cnA/T0ylM3Hv9lI/AAAAAAAAAlc/TvTfD6MUBKM/s400/20120228_dosei.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-3557052872728193337?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/3557052872728193337/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingx2.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/3557052872728193337'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/3557052872728193337'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingx2.html' title='ProcessingでXファイルを解析（スタティックメッシュ篇その2）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-2QN1DOd4JeE/T0yPe_4BoWI/AAAAAAAAAko/jm4PJ5zkNMk/s72-c/20120228002.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-5908220681552517602</id><published>2012-02-27T21:04:00.000+09:00</published><updated>2012-02-27T21:04:26.017+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>ProcessingでXファイルを解析（スタティックメッシュ篇その1）</title><content type='html'>今日は、DirectX で標準的に使用されていた &lt;a href="http://msdn.microsoft.com/ja-jp/library/cc372023.aspx"&gt;X&lt;/a&gt; ファイルを読み込んで、Processing で表示してみたいと思います。 すなわち ——&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hLqSkVFZ9PI/T0tdfH5j6JI/AAAAAAAAAkE/Nm8bAt6ebbE/s1600/20120227001.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="287" src="http://4.bp.blogspot.com/-hLqSkVFZ9PI/T0tdfH5j6JI/AAAAAAAAAkE/Nm8bAt6ebbE/s400/20120227001.jpg" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Metasequoia にプリセットされている「日本橋麒麟像」&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;こんなふうに任意のモデリングソフトで作成したデータを、&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-ugd7kT6_nXw/T0teIAhQkoI/AAAAAAAAAkM/m62DGtxMe8k/s1600/20120227002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="316" src="http://3.bp.blogspot.com/-ugd7kT6_nXw/T0teIAhQkoI/AAAAAAAAAkM/m62DGtxMe8k/s400/20120227002.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;上図のように、Processing でそのまま使えるようにしてしまおうという試みです。&lt;br /&gt;&lt;br /&gt;しかし僕がアホすぎるせいで、まだ&lt;b&gt;静止&lt;/b&gt;した 3D モデルの&lt;b&gt;幾何形状だけ&lt;/b&gt;しか読めていませんごめんなさい（アニメーションはおろか物体の色付けやテクスチャも無し）。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;この課題の一番のネックは X ファイルのパーサです。 &lt;br /&gt;&lt;br /&gt;X のフォーマットはそれなりに自由度が高く、使用するモデリングソフトによって構造が変わったりします。 すべての状況に適応できる汎用性の高いコードを本気で書こうとすると、割と本格的な構文解析器を実装する必要があるような気がして心が折れます。&lt;br /&gt;&lt;br /&gt;というわけで、個人的にはひとまず姑息的なパーサを手っ取り早く作り、なんか問題が起きたら適宜設計を見直しつつ修正していこうかと思います。 &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;とりあえずXファイル読み込み関連をまとめたプログラム（長いので折りたたんであります）。&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;// -------------------------------------------------&lt;br /&gt;// =================================================&lt;br /&gt;// Xファイル形式で記述されたメッシュを表示するテスト&lt;br /&gt;//                    スタティックメッシュ篇不完全版&lt;br /&gt;// =================================================&lt;br /&gt;// -------------------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// メッシュ&lt;br /&gt;class Mesh {&lt;br /&gt;  List&amp;lt;Polygon&amp;gt; polygons;&lt;br /&gt;  &lt;br /&gt;  public Mesh() {&lt;br /&gt;    polygons = new ArrayList&amp;lt;Polygon&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void add(Polygon p) {&lt;br /&gt;    polygons.add(p);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void render() {&lt;br /&gt;    if(polygons == null) return;&lt;br /&gt;    &lt;br /&gt;    for(Polygon p : polygons) p.render();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// ポリゴン&lt;br /&gt;class Polygon {&lt;br /&gt;  List&amp;lt;PVector&amp;gt; vertices;&lt;br /&gt;  &lt;br /&gt;  public Polygon() {&lt;br /&gt;    vertices = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void add(PVector v) {&lt;br /&gt;    vertices.add(v);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void render() {&lt;br /&gt;    if(vertices == null) return;&lt;br /&gt;    &lt;br /&gt;    if(vertices.size() == 3)      beginShape(TRIANGLES);&lt;br /&gt;    else if(vertices.size() == 4) beginShape(QUADS);&lt;br /&gt;    else                          beginShape();&lt;br /&gt;    for(PVector v : vertices) vertex(v.x, v.y, v.z);&lt;br /&gt;    endShape();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// =================================================&lt;br /&gt;&lt;br /&gt;// Xファイルローダクラス&lt;br /&gt;class Loader {&lt;br /&gt;  // Xファイルの文字列データが一括して格納される&lt;br /&gt;  private StringBuffer data;&lt;br /&gt;  &lt;br /&gt;  // Xファイル内の部分文字列の先頭を指すインデックス&lt;br /&gt;  private int          index;&lt;br /&gt;&lt;br /&gt;  // 頂点リスト&lt;br /&gt;  List&amp;lt;PVector&amp;gt; verticesList;&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  public Loader() {&lt;br /&gt;    data = new StringBuffer();&lt;br /&gt;    verticesList = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // ファイル名を指定してメッシュをロード &lt;br /&gt;  public Mesh loadX(final String fileName) { &lt;br /&gt;    Mesh mesh = new Mesh();&lt;br /&gt;    &lt;br /&gt;    index = 0;&lt;br /&gt;    data.delete(0, data.length());&lt;br /&gt;    String[] lines = loadStrings(fileName);&lt;br /&gt;    for(String line : lines) {&lt;br /&gt;      // ここで本来はコメント除去の処理が必要&lt;br /&gt;      &lt;br /&gt;      data.append(line + "\n");&lt;br /&gt;    }&lt;br /&gt;    while(index &amp;lt; data.length()) {&lt;br /&gt;      String token = getNextToken();&lt;br /&gt;      &lt;br /&gt;      // テンプレートは読み飛ばす&lt;br /&gt;      if(token.equals("template")) skipBlock();&lt;br /&gt;&lt;br /&gt;      // ====================&lt;br /&gt;      // MESH&lt;br /&gt;      // ====================&lt;br /&gt;      else if(token.equals("Mesh")) {&lt;br /&gt;        token = getNextToken();&lt;br /&gt;        // トークンが { でない場合&lt;br /&gt;        if(!token.equals("{")) {&lt;br /&gt;          getNextToken();&lt;br /&gt;        } &lt;br /&gt;        /*&lt;br /&gt;        else {&lt;br /&gt;          // 名前つきマップに入れたい&lt;br /&gt;        }*/&lt;br /&gt;        &lt;br /&gt;        // 頂点リストを作成&lt;br /&gt;        verticesList.clear();&lt;br /&gt;        &lt;br /&gt;        // 全頂点数を取得&lt;br /&gt;        int nVertices = Integer.parseInt(getNextToken());&lt;br /&gt;        &lt;br /&gt;        // 各頂点の座標を取得&lt;br /&gt;        for(int i = 0; i &amp;lt; nVertices; ++i) {&lt;br /&gt;          float x = Float.parseFloat(getNextToken());&lt;br /&gt;          float y = Float.parseFloat(getNextToken());&lt;br /&gt;          float z = Float.parseFloat(getNextToken());          &lt;br /&gt;          verticesList.add(new PVector(x, y, z));&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        // ポリゴン数を取得&lt;br /&gt;        int nPolygons = Integer.parseInt(getNextToken());&lt;br /&gt;        for(int i = 0; i &amp;lt; nPolygons; ++i) {&lt;br /&gt;          Polygon poly = new Polygon();&lt;br /&gt;          &lt;br /&gt;          // 1ポリゴンあたりの頂点数&lt;br /&gt;          int nVerticesPerPolygon = Integer.parseInt(getNextToken());&lt;br /&gt;          for(int j = 0; j &amp;lt; nVerticesPerPolygon; ++j) {&lt;br /&gt;            &lt;br /&gt;            // 頂点インデックスを取得&lt;br /&gt;            int verticesIndex = Integer.parseInt(getNextToken());&lt;br /&gt;            &lt;br /&gt;            // 頂点インデックスから該当する頂点を取得してポリゴンに追加&lt;br /&gt;            poly.add(verticesList.get(verticesIndex));&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          mesh.add(poly);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    return mesh;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // { } で囲まれたブロックを読み飛ばす&lt;br /&gt;  private void skipBlock() {&lt;br /&gt;    try {&lt;br /&gt;      // はじめの { まで読み飛ばす &lt;br /&gt;      while(!(getNextToken().equals("{")) &amp;amp;&amp;amp; index &amp;lt; data.length()) ;&lt;br /&gt;      &lt;br /&gt;      int n = 1;&lt;br /&gt;      while(n &amp;gt; 0 &amp;amp;&amp;amp; index &amp;lt; data.length()) {&lt;br /&gt;        String token = getNextToken();&lt;br /&gt;        if(token.equals("{")) ++n;&lt;br /&gt;        if(token.equals("}")) --n;&lt;br /&gt;      }&lt;br /&gt;    } catch (RuntimeException ex) {&lt;br /&gt;      ex.printStackTrace();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // 次のトークンを得る&lt;br /&gt;  private String getNextToken() {&lt;br /&gt;    // 最初にインデックスの範囲チェック&lt;br /&gt;    if(!(index &amp;lt; data.length())) return "";  // ファイルの末尾に達した場合は""を返す&lt;br /&gt;    &lt;br /&gt;    // デリミタ（区切り文字）の読み飛ばし&lt;br /&gt;    String delimiters = " \t\r\n,;\"";&lt;br /&gt;    while(index &amp;lt; data.length() &amp;amp;&amp;amp; &lt;br /&gt;          strChr(delimiters, data.charAt(index)))  &lt;br /&gt;      ++index;&lt;br /&gt;&lt;br /&gt;    if(!(index &amp;lt; data.length())) return "";  // ファイルの末尾&lt;br /&gt; &lt;br /&gt;    // 中括弧の検出&lt;br /&gt;    char[] c = { data.charAt(index) };&lt;br /&gt;    if(c[0] == '{' || c[0] == '}') {&lt;br /&gt;      ++index;&lt;br /&gt;      return new String(c);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // それ以外のトークン検出&lt;br /&gt;    delimiters += "{}";&lt;br /&gt;    int startIndex = index;&lt;br /&gt;    int endIndex   = index;&lt;br /&gt;    while(endIndex &amp;lt; data.length() &amp;amp;&amp;amp;&lt;br /&gt;          !strChr(delimiters, data.charAt(endIndex)))&lt;br /&gt;      ++endIndex;&lt;br /&gt;    index = endIndex;&lt;br /&gt;    return data.substring(startIndex, endIndex);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // 文字列シーケンスsの中に、cが含まれているかどうかをチェック&lt;br /&gt;  private boolean strChr(final CharSequence s, final char c) {&lt;br /&gt;    for(int i = 0; i &amp;lt; s.length(); ++i)&lt;br /&gt;      if(s.charAt(i) == c) return true;&lt;br /&gt;    return false;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;で、使い方はこんな感じ。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;Mesh mesh;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(640, 480, P3D);&lt;br /&gt;  &lt;br /&gt;  // ファイル名を指定してXをロード  &lt;br /&gt;  mesh = new Loader().loadX("nihonbasikirin.x");&lt;br /&gt;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  camera();&lt;br /&gt;  lights();&lt;br /&gt;  noStroke();&lt;br /&gt;&lt;br /&gt;  camera(500, 500, 500, 0, 300, 0, 0, -1, 0);&lt;br /&gt;    &lt;br /&gt;  scale(300);&lt;br /&gt;  &lt;br /&gt;  // メッシュの描画&lt;br /&gt;  mesh.render();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;ちなみに、現在の制約では、一つの X ファイルに複数のメッシュ情報が定義されている場合、うまく読み込む事ができません（対応は簡単ですが、それが必要な例を見た事がないのでとりあえずこのままでいいや的な……）。&lt;br /&gt;&lt;br /&gt;ただ、視覚的には複数のように見えるメッシュであっても、内部的には一つのメッシュにまとめられているような場合は、正しくロードする事ができます（下図参照）。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-JYMKOf9MiXI/T0tsu0lekwI/AAAAAAAAAkU/c_lqAy5LAVA/s1600/20120227003.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="287" src="http://3.bp.blogspot.com/-JYMKOf9MiXI/T0tsu0lekwI/AAAAAAAAAkU/c_lqAy5LAVA/s400/20120227003.jpg" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Metasequoia にプリセットされている「魔道少女」&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;これを Processing で表示させると、ちゃんと 2 つのモデルが表示されます。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-r9e7tXI3cx8/T0ttGAiDGZI/AAAAAAAAAkc/YHYnd2dBJX8/s1600/20120227004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="316" src="http://3.bp.blogspot.com/-r9e7tXI3cx8/T0ttGAiDGZI/AAAAAAAAAkc/YHYnd2dBJX8/s400/20120227004.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;これからいろいろ忙しくなりますが、暇を見つけて『色付け』と『アニメーション』に対応していけたらなぁ……と考えています。 うーん……。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-5908220681552517602?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/5908220681552517602/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingx1.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/5908220681552517602'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/5908220681552517602'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingx1.html' title='ProcessingでXファイルを解析（スタティックメッシュ篇その1）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-hLqSkVFZ9PI/T0tdfH5j6JI/AAAAAAAAAkE/Nm8bAt6ebbE/s72-c/20120227001.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-7299626998398796524</id><published>2012-02-24T22:14:00.000+09:00</published><updated>2012-02-24T22:14:58.041+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='雑貨'/><category scheme='http://www.blogger.com/atom/ns#' term='身辺雑記'/><title type='text'>おもちゃであそぶ</title><content type='html'>今日はおとなしくおうちで遊ぶ事にしました。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-EznrnFhHj9c/T0di_-AMWSI/AAAAAAAAAj0/x4LdIYXFK3c/s1600/20120224480.png" /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-7299626998398796524?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/7299626998398796524/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7299626998398796524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7299626998398796524'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/blog-post.html' title='おもちゃであそぶ'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-EznrnFhHj9c/T0di_-AMWSI/AAAAAAAAAj0/x4LdIYXFK3c/s72-c/20120224480.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-1854873043697901599</id><published>2012-02-23T00:21:00.001+09:00</published><updated>2012-02-23T00:22:05.369+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Processingでスキンメッシュアニメーション</title><content type='html'>&lt;b&gt;&lt;span style="font-size: large;"&gt;【とにかく、ぐにゃぐにゃ】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;今まで黙っていましたが、実はこれまで Processing で表示させていた立体オブジェクトは、そのほとんどが&lt;b&gt;剛体&lt;/b&gt;でした（&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/08/processing_26.html"&gt;オーロラ&lt;/a&gt;は例外）。&lt;br /&gt;&lt;br /&gt;というわけで、ブログをご覧の皆さんに僕が剛体しか出せない事がバレる前に、&lt;b&gt;スキンメッシュ&lt;/b&gt;という謎の技術を使って時々刻々と変形する形状の表示に挑戦してみたいと思います。&lt;br /&gt;&lt;br /&gt;……と見せかけて、僕は根性無しなので、小規模のサンプルを作ったところで力尽きました⊂⌒~⊃｡Д｡)⊃ &lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/FKzmA3J6pmQ?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;たいへん気色悪い物体がぐにゃぐにゃ動いているのがお解りいただけたかと思います。&lt;br /&gt;&lt;br /&gt;スキンメッシュは、その名の通り表皮っぽいメッシュの事で、ボーンと呼ばれる仮想的な剛体の動きに追従して頂点が変形します（→&lt;a href="http://www.openprocessing.org/visuals/?visualID=53242"&gt;OpenProcessing&lt;/a&gt;）。&lt;br /&gt;&lt;br /&gt;この記事でも手法を詳しく紹介しようかと思いましたが、理論自体は大した事ないくせに解説がやたら面倒なのでやめました。理論の詳細は『ゲームエンジンアーキテクチャ』の第11章や『MESH GURU with Direct3D 10/11』 の第12章あたりにざっくりまとめられていますし、Web にも良質なドキュメントがあるのでそちらを参照してください（逃&lt;br /&gt;&lt;br /&gt;ただ、OpenProcessing で “Skinning” や “Skin Mesh” で検索してもそれっぽい先行作品がヒットしなかったので、恐らく今回のスキニングは OpenProcessing 史上初の試みだと思います。えっへん。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-1854873043697901599?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/1854873043697901599/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing_23.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1854873043697901599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1854873043697901599'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing_23.html' title='Processingでスキンメッシュアニメーション'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/FKzmA3J6pmQ/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-8578625853974497753</id><published>2012-02-20T23:44:00.000+09:00</published><updated>2012-02-20T23:44:20.118+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Processingで魔法使いの組分け帽子を作る</title><content type='html'>今日はちょっとしたパターン認識をやってみようと思います。あ、&lt;span style="color: red;"&gt;タイトルは釣り&lt;/span&gt;ですよ。&lt;br /&gt;&lt;br /&gt;とりあえずデモ動画をご覧ください。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/EANU0zzGguI?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;こんな感じで、ある「特徴」を手掛かりにして正体をつきとめる処理は「パターン認識」と呼ばれており、文字認識などに応用されている技術です。&lt;br /&gt;&lt;br /&gt;現在に至るまで、様々な認識手法（というか識別手法）が提案されていますが、今日はフィードフォワード型の&lt;b&gt;ニューラルネットワーク&lt;/b&gt;（3層パーセプトロン）にフォーカスを当てたいと思います。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【ニューラルネットワークの構成】&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;ここでは、ニューラルネットワークの理論的背景について、石井 健一郎ほか著『わかりやすいパターン認識』の 3 章に準拠して解説したいと思います。前提知識として、「パターン認識とは何か」という根本的な理解と、&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%97%E3%83%88%E3%83%AD%E3%83%B3"&gt;単純パーセプトロン&lt;/a&gt;を実装できる程度の能力があるとよいと思います。&lt;br /&gt;&lt;br /&gt;まず、ニューロンと呼ばれる神経細胞の働きを、以下に示す閾値論理ユニットでモデル化します。これは、適当なウェイト \( \vect{w} = \left( w_0,\,\cdots,\,w_d\right)^t\) によって重みづけした入力信号 \(\vect{x} = \left(x_0,\,\cdots,\,x_d\right)^t\) の総和を基に、閾値処理を行う単純な仕組みです。出力 \(g_i(\vect{x})\) は 0 か 1 となります。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-SSqU2OiCzgI/T0IjnLfo40I/AAAAAAAAAjc/SuOsKBVBwSo/s1600/thresholdlogicunit.png" style="margin-left: auto; margin-right: auto;" /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;閾値論理ユニット&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;ニューラルネットワークは、複数の閾値論理ユニット（以下、ユニット）を含む層を、入力層から出力層まで多数並べたネットワークの事です。各ユニットは隣接する層とだけで結合しており、かつ入力層から出力層へ向かう一方向のネットワーク構成です（下図参照）。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-TPg0g58VJQM/T0IptDugE4I/AAAAAAAAAjs/JGNs_D1_jFw/s1600/2012022002.png" /&gt;&lt;/div&gt;&lt;br /&gt;ニューラルネットワークのある層における \(j\) 番目のユニットに注目し、これをユニット \(j\) と呼ぶ事にします。また、ユニット \(j\) の1階層前の \(i\) 番目のユニットをユニット \(i\)、1階層後ろの \(k\) 番目のユニットをユニット \(k\) と呼び、さらにユニット \(i\) からユニット \(j\) への結合の重みを \(w_{ij}\)、ユニット \(j\) からユニット \(k\) への結合の重みを \(w_{jk} \) で表します。&lt;br /&gt;&lt;br /&gt;ここで、ネットワークにある学習パターン \(\vect{x}_p\) を入力したとき、ユニット \(i\) からの出力を \(g_{ip}\)、ユニット \(j\) への入力を \(h_{jp}\) とすると、\(h_{jp}\) は \(j\) に結合している1階層前の層内のユニットからの出力の重み付き信号の総和、つまり&lt;br /&gt;\begin{align}&lt;br /&gt;h_{jp}=\sum_{i}w_{ij}g_{ip}&lt;br /&gt;\end{align}となります。&lt;br /&gt;&lt;br /&gt;ユニット \(j\) の出力 \(g_{jp}\) は閾値関数 \(f_j\) を用いて&lt;br /&gt;\begin{align}&lt;br /&gt;g_{jp}=f_j(h_{jp})&lt;br /&gt;\end{align}と表すものとします。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;【誤差の定式化と重みの更新】&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;ニューラルネットワークを構築したら、理想的な識別結果が既知の入力データを用いて&lt;b&gt;学習&lt;/b&gt;（重みの調整）を行う必要があります。 &lt;br /&gt;&lt;br /&gt;ある学習パターン \(\vect{x}_p\) に対する&lt;b&gt;二乗誤差&lt;/b&gt;は、出力層のユニット \(l\) の出力 \( g_{lp} \) と、ユニット \(l\) に対する教師信号（理想的な識別結果）を \(b_{lp}\) を用いて、以下のように表す事ができます。&lt;br /&gt;\begin{align}&lt;br /&gt;J_p = \frac{1}{2}\sum_l \left( g_{lp} - b_{lp} \right)^2&lt;br /&gt;\end{align}従って、全学習パターンに対する二乗誤差 \(J\) は&lt;br /&gt;\begin{align}&lt;br /&gt;J=\sum_p J_p&lt;br /&gt;\end{align}となります。&lt;br /&gt;&lt;br /&gt;識別性能を高めるためには、この誤差 \(J\) を最小化するようにユニット間の重みを調整する必要があります。つまり、ある重み \(w_{ij}\) に対して、以下の更新式を適用する事になります。&lt;br /&gt;\begin{align}&lt;br /&gt;w'_{ij} &amp;amp;= w_{ij} - \rho \frac{\partial J_p}{\partial w_{ij}} \\&lt;br /&gt;&amp;amp;= w_{ij} - \rho \underbrace{ \frac{\partial J_p}{\partial h_{jp}}}_{\varepsilon_{jp}} \cdot \underbrace{\frac{\partial h_{jp}}{\partial w_{ij}}}_{g_{ip}} \\&lt;br /&gt;&amp;amp;= w_{ij} - \rho \varepsilon_{jp}g_{ip}&lt;br /&gt;\end{align}ここで \(\rho\) を学習係数と呼び、任意の正定数とします。&lt;br /&gt;&lt;br /&gt;また、上式右辺の \(\varepsilon_{jp}\) は、合成関数の微分を用いて以下のように式変形できます。&lt;br /&gt;\begin{align}&lt;br /&gt;\varepsilon_{jp} &amp;amp;= \frac{\partial J_p}{\partial h_{jp}} = \frac{\partial J_p}{\partial g_{jp}} \cdot \frac{\partial g_{jp}}{\partial h_{jp}} \\&lt;br /&gt;&amp;amp;= \frac{\partial J_p}{\partial g_{jp}} \cdot f'_j(h_{jp})&lt;br /&gt;\end{align}なお、上式中の \( \dfrac{\partial J_p}{\partial g_{jp}} \) は、ユニット \(j\) が中間層か出力層かで以下のように場合分けされます。&lt;br /&gt;\begin{align}&lt;br /&gt;\frac{\partial J_p}{\partial g_{jp}} =&lt;br /&gt;\begin{cases}&lt;br /&gt;g_{jp} - b_{jp} &amp;amp; \textrm{（出力層）} \\&lt;br /&gt;\sum_{k}\varepsilon_{kp}w_{jk} &amp;amp; \textrm{（中間層）}&lt;br /&gt;\end{cases}&lt;br /&gt;\end{align}&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;【閾値関数の選定】&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;最後に、閾値関数 \(f_j\) を考えます。ここまでの議論で、この関数が微分可能でなければならない事にはお気付きでしょう。&lt;br /&gt;&lt;br /&gt;通常、\(f_j\) には以下のようなシグモイド関数 \(S(u)\) が選ばれます。&lt;br /&gt;\begin{align}&lt;br /&gt;S(u) = \frac{1}{1 + \exp(-u)}&lt;br /&gt;\end{align}&lt;br /&gt;この関数は、微分すると \(S'(u) = S(u)\left( 1 - S(u)\right)\) になりますから、\(f'_j\) は&lt;br /&gt;\begin{align}&lt;br /&gt;f'_j (h_{jp}) = g_{jp} (1 - g_{jp})&lt;br /&gt;\end{align}となります。&lt;br /&gt;&lt;br /&gt;以上を整理すると、\(\varepsilon_{jp}\) に関する再帰式を以下のように得る事ができます。&lt;br /&gt;\begin{align}&lt;br /&gt;\varepsilon_{jp} = &lt;br /&gt;\begin{cases}&lt;br /&gt;\left( g_{jp} - b_{jp} \right) g_{jp} \left( 1 - g_{jp} \right) &amp;amp; \textrm{（出力層）} \\&lt;br /&gt;\left( \sum_k \varepsilon_{kp} w_{jk} \right) g_{jp} \left( 1 - g_{jp} \right) &amp;amp; \textrm{（中間層）}&lt;br /&gt;\end{cases}&lt;br /&gt;\end{align}上式において、\(0 &amp;lt; g_{jp} \left( 1 - g_{jp} \right) &amp;lt; 1\) であり、ユニットの出力値 \(g_{jp} \) が 0.5 のときに重みの修正量が最も大きく、0 か 1 に近づくほど修正量が小さくなります。&lt;br /&gt;&lt;br /&gt;学習パターンが入力されると出力層で各ユニットの出力と教師信号の誤差が計算され、それに基づいて \(\varepsilon_{jp}\) が求められ、出力層での重みの修正が行われます。&lt;br /&gt;&lt;br /&gt;この結果によって、1つ前の階層でも重みの修正が行われます。これを同様に繰り返す事で、すべての層で重みが修正されます。 このように、出力層から入力層に向かって誤差を伝播させる学習こそが、誤差逆伝播法と呼ばれる所以です。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【実装】&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;3層から成るフィードフォワード型ニューラルネットワークの実装は以下の通りです。こちらも、David M.Bourg, Glenn Seemann 著『ゲーム開発者のための AI 入門』を参考にしました&lt;span style="color: yellow;"&gt;（そこそこ長いので展開してご覧ください）&lt;/span&gt;。&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;class NeuralNetworkLayer {&lt;br /&gt;  int        numberOfNodes;       // 当該レイヤのノード数&lt;br /&gt;  int        numberOfChildNodes;  // 子レイヤのノード数&lt;br /&gt;  int        numberOfParentNodes; // 親レイヤのノード数&lt;br /&gt;  &lt;br /&gt;  double[][] weights;             // 親子レイヤを接続するノードの重み値&lt;br /&gt;  double[][] weightChanges;       // 重み値の調整に使われる値&lt;br /&gt;  &lt;br /&gt;  double[]   neuronValues;        // 計算によって得られたニューロンの値&lt;br /&gt;  double[]   desiredValues;       // 目的となるニューロンの値&lt;br /&gt;  double[]   errors;              // 各ニューロンに関連するエラー値&lt;br /&gt;  double[]   biasWeights;         // 各ニューロンに接続するバイアス重み&lt;br /&gt;  double[]   biasValues;          // バイアス値 通常は +1 または -1&lt;br /&gt;  double     learningRate;        // 重みの調整を計算する学習率&lt;br /&gt;  &lt;br /&gt;  boolean    linearOutput;        // 線型活性化関数を使用するかどうか&lt;br /&gt;                                  // false ならロジスティック関数&lt;br /&gt;  boolean    useMomentum;         // 重みの調整にモーメンタムを使用するかどうか&lt;br /&gt;                                  // デフォルトはfalse&lt;br /&gt;  double     momentumFactor;      // モーメンタム因数&lt;br /&gt;  &lt;br /&gt;  NeuralNetworkLayer parentLayer; // 親レイヤへの参照 入力層の場合は null&lt;br /&gt;  NeuralNetworkLayer childLayer;  // 子レイヤへの参照 出力層の場合は null&lt;br /&gt;  &lt;br /&gt;  // コンストラクタ&lt;br /&gt;  NeuralNetworkLayer() {&lt;br /&gt;    parentLayer    = null;&lt;br /&gt;    childLayer     = null;&lt;br /&gt;    linearOutput   = false;&lt;br /&gt;    momentumFactor = 0.9;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // 初期化メソッド&lt;br /&gt;  void initialize( // int numNodes, &lt;br /&gt;                  NeuralNetworkLayer parent, &lt;br /&gt;                  NeuralNetworkLayer child) {&lt;br /&gt;&lt;br /&gt;    neuronValues  = new double[numberOfNodes];&lt;br /&gt;    desiredValues = new double[numberOfNodes];&lt;br /&gt;    errors        = new double[numberOfNodes];&lt;br /&gt;    &lt;br /&gt;    if(parent != null) {&lt;br /&gt;      parentLayer = parent;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    if(child != null) {&lt;br /&gt;      childLayer = child;&lt;br /&gt;      &lt;br /&gt;      weights       = new double[numberOfNodes][numberOfChildNodes];&lt;br /&gt;      weightChanges = new double[numberOfNodes][numberOfChildNodes];&lt;br /&gt;&lt;br /&gt;      biasValues = new double[numberOfChildNodes];&lt;br /&gt;      biasWeights = new double[numberOfChildNodes];&lt;br /&gt;      &lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // すべてに0が含まれるようにする&lt;br /&gt;    for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;      neuronValues[i] = 0;&lt;br /&gt;      desiredValues[i] = 0;&lt;br /&gt;      errors[i] = 0;&lt;br /&gt;      &lt;br /&gt;      if(childLayer != null)&lt;br /&gt;        for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;          weights[i][j] = 0;&lt;br /&gt;          weightChanges[i][j] = 0;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // バイアス値と重みを初期化する&lt;br /&gt;    if(childLayer != null)&lt;br /&gt;      for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;        biasValues[j]  = -1;&lt;br /&gt;        biasWeights[j] = 0;&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void randomizeWeights() {&lt;br /&gt;    for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;      for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;        weights[i][j] = random(-1.0, 1.0);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;      biasWeights[j] = random(-1.0, 1.0);&lt;br /&gt;    }   &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void calculateNeuronValues() {&lt;br /&gt;    if(parentLayer != null) {&lt;br /&gt;      for(int j = 0; j &amp;lt; numberOfNodes; j++) {&lt;br /&gt;        double x = 0;&lt;br /&gt;        for(int i = 0; i &amp;lt; numberOfParentNodes; i++) {&lt;br /&gt;          x += parentLayer.neuronValues[i] * parentLayer.weights[i][j];&lt;br /&gt;        }&lt;br /&gt;        x += parentLayer.biasValues[j] * parentLayer.biasWeights[j];&lt;br /&gt;        &lt;br /&gt;        if((childLayer == null) &amp;amp;&amp;amp; linearOutput)&lt;br /&gt;          neuronValues[j] = x;&lt;br /&gt;        else&lt;br /&gt;          neuronValues[j] = 1.0 / (1.0 + Math.exp(-x));&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void calculateErrors() {&lt;br /&gt;    if(childLayer == null) {&lt;br /&gt;      // 出力層&lt;br /&gt;      for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;        errors[i] = (desiredValues[i] - neuronValues[i]) *&lt;br /&gt;                    neuronValues[i] * (1.0f - neuronValues[i]);&lt;br /&gt;      }&lt;br /&gt;    } else if (parentLayer == null) {&lt;br /&gt;      // 入力層&lt;br /&gt;      for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;        errors[i] = 0.0f;&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      // 隠れ層&lt;br /&gt;      for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;        double sum = 0;&lt;br /&gt;        for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;          sum += childLayer.errors[j] * weights[i][j];&lt;br /&gt;        }&lt;br /&gt;        errors[i] = sum * neuronValues[i] * (1.0f - neuronValues[i]);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void adjustWeights() {&lt;br /&gt;    if(childLayer != null) {&lt;br /&gt;      for(int i = 0; i &amp;lt; numberOfNodes; i++) {&lt;br /&gt;        for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;          double dw = learningRate * childLayer.errors[j] * neuronValues[i];&lt;br /&gt;          if(useMomentum) {&lt;br /&gt;            weights[i][j] += dw + momentumFactor * weightChanges[i][j];&lt;br /&gt;            weightChanges[i][j] = dw;&lt;br /&gt;          } else {&lt;br /&gt;            weights[i][j] += dw;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      for(int j = 0; j &amp;lt; numberOfChildNodes; j++) {&lt;br /&gt;        biasWeights[j] += learningRate * childLayer.errors[j] * biasValues[j];&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class NeuralNetwork {&lt;br /&gt;  NeuralNetworkLayer inputLayer;&lt;br /&gt;  NeuralNetworkLayer hiddenLayer;&lt;br /&gt;  NeuralNetworkLayer outputLayer;&lt;br /&gt;  &lt;br /&gt;  void initialize(int nNodesInput, int nNodesHidden, int nNodesOutput) {&lt;br /&gt;    inputLayer  = new NeuralNetworkLayer();&lt;br /&gt;    hiddenLayer = new NeuralNetworkLayer();&lt;br /&gt;    outputLayer = new NeuralNetworkLayer();&lt;br /&gt;    &lt;br /&gt;    inputLayer.numberOfNodes       = nNodesInput;&lt;br /&gt;    inputLayer.numberOfChildNodes  = nNodesHidden;&lt;br /&gt;    inputLayer.numberOfParentNodes = 0;&lt;br /&gt;    inputLayer.initialize(null, hiddenLayer);&lt;br /&gt;    inputLayer.randomizeWeights();&lt;br /&gt;    &lt;br /&gt;    hiddenLayer.numberOfNodes       = nNodesHidden;&lt;br /&gt;    hiddenLayer.numberOfChildNodes  = nNodesOutput;&lt;br /&gt;    hiddenLayer.numberOfParentNodes = nNodesInput;&lt;br /&gt;    hiddenLayer.initialize(inputLayer, outputLayer);&lt;br /&gt;    hiddenLayer.randomizeWeights();&lt;br /&gt;    &lt;br /&gt;    outputLayer.numberOfNodes       = nNodesOutput;&lt;br /&gt;    outputLayer.numberOfChildNodes  = 0;&lt;br /&gt;    outputLayer.numberOfParentNodes = nNodesHidden;&lt;br /&gt;    outputLayer.initialize(hiddenLayer, null);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void setInput(int i, double value) {&lt;br /&gt;    if((i &amp;gt;= 0) &amp;amp;&amp;amp; ( i &amp;lt; inputLayer.numberOfNodes)) {&lt;br /&gt;      inputLayer.neuronValues[i] = value;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  double getOutput(int i) {&lt;br /&gt;    if((i &amp;gt;= 0) &amp;amp;&amp;amp; (i &amp;lt; outputLayer.numberOfNodes)) {&lt;br /&gt;      return outputLayer.neuronValues[i];&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return Double.MAX_VALUE;  // エラーを示す&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void setDesiredOutput(int i, double value) {&lt;br /&gt;    if((i &amp;gt;= 0) &amp;amp;&amp;amp; (i &amp;lt; outputLayer.numberOfNodes)) {&lt;br /&gt;      outputLayer.desiredValues[i] = value;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void feedForward() {&lt;br /&gt;    inputLayer.calculateNeuronValues();&lt;br /&gt;    hiddenLayer.calculateNeuronValues();&lt;br /&gt;    outputLayer.calculateNeuronValues();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void backPropagate() {&lt;br /&gt;    outputLayer.calculateErrors();&lt;br /&gt;    hiddenLayer.calculateErrors();&lt;br /&gt;    &lt;br /&gt;    hiddenLayer.adjustWeights();&lt;br /&gt;    inputLayer.adjustWeights();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  int getMaxOutputID() {&lt;br /&gt;    double maxval = outputLayer.neuronValues[0];&lt;br /&gt;    int id = 0;&lt;br /&gt;    for(int i = 1; i &amp;lt; outputLayer.numberOfNodes; i++) {&lt;br /&gt;      if(outputLayer.neuronValues[i] &amp;gt; maxval) {&lt;br /&gt;        maxval = outputLayer.neuronValues[i];&lt;br /&gt;        id = i;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    return id;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  double calculateError() {&lt;br /&gt;    double error = 0;&lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; outputLayer.numberOfNodes; i++) {&lt;br /&gt;      error += Math.pow(outputLayer.neuronValues[i] - outputLayer.desiredValues[i], 2);&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    error = error / outputLayer.numberOfNodes;&lt;br /&gt;    return error;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void setLearningRate(double rate) {&lt;br /&gt;    inputLayer.learningRate  = rate;&lt;br /&gt;    hiddenLayer.learningRate = rate;&lt;br /&gt;    outputLayer.learningRate = rate;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void setLinearOutput(boolean useLinear) {&lt;br /&gt;    inputLayer.linearOutput  = useLinear;&lt;br /&gt;    hiddenLayer.linearOutput = useLinear;&lt;br /&gt;    outputLayer.linearOutput = useLinear;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void setMomentum(boolean useMomentum, double factor) {&lt;br /&gt;    inputLayer.useMomentum  = useMomentum;&lt;br /&gt;    hiddenLayer.useMomentum = useMomentum;&lt;br /&gt;    outputLayer.useMomentum = useMomentum;&lt;br /&gt;    &lt;br /&gt;    inputLayer.momentumFactor  = factor;&lt;br /&gt;    hiddenLayer.momentumFactor = factor;&lt;br /&gt;    outputLayer.momentumFactor = factor;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void dumpData(String filename) {&lt;br /&gt;    List&amp;lt;String&amp;gt; data = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("Input Layer");&lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Node Values:");&lt;br /&gt;    for(int i = 0; i &amp;lt; inputLayer.numberOfNodes; i++)&lt;br /&gt;      data.add("(" + i + ") = " + inputLayer.neuronValues[i]);&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Weights:");&lt;br /&gt;    data.add("");&lt;br /&gt;    for(int i = 0; i &amp;lt; inputLayer.numberOfNodes; i++)&lt;br /&gt;      for(int j = 0; j &amp;lt; inputLayer.numberOfChildNodes; j++) &lt;br /&gt;        data.add("(" + i + ", " + j + ") = " + inputLayer.weights[i][j]);&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Bias Weights:");&lt;br /&gt;    for(int j = 0; j &amp;lt; inputLayer.numberOfChildNodes; j++)&lt;br /&gt;      data.add("(" + j + ") = " + inputLayer.biasWeights[j]);&lt;br /&gt;    &lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("");&lt;br /&gt;    &lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("Hidden Layer");&lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Weights:");&lt;br /&gt;    data.add("");&lt;br /&gt;    for(int i = 0; i &amp;lt; hiddenLayer.numberOfNodes; i++)&lt;br /&gt;      for(int j = 0; j &amp;lt; hiddenLayer.numberOfChildNodes; j++) &lt;br /&gt;        data.add("(" + i + ", " + j + ") = " + hiddenLayer.weights[i][j]);&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Bias Weights:");&lt;br /&gt;    for(int j = 0; j &amp;lt; hiddenLayer.numberOfChildNodes; j++)&lt;br /&gt;      data.add("(" + j + ") = " + hiddenLayer.biasWeights[j]);&lt;br /&gt;&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("");&lt;br /&gt;    &lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("Output Layer");&lt;br /&gt;    data.add("--------------------------------------------------------");&lt;br /&gt;    data.add("");&lt;br /&gt;    data.add("Node Values:");&lt;br /&gt;    for(int i = 0; i &amp;lt; outputLayer.numberOfNodes; i++)&lt;br /&gt;      data.add("(" + i + ") = " + outputLayer.neuronValues[i]);&lt;br /&gt;    data.add("");&lt;br /&gt;    &lt;br /&gt;    String[] strings = new String[data.size()];&lt;br /&gt;    data.toArray(strings);&lt;br /&gt;    saveStrings(filename, strings);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;で、使い方はこんな感じです。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;// --------------------------------------&lt;br /&gt;// ======================================&lt;br /&gt;// ニューラルネットワークを用いた&lt;br /&gt;//                  2クラス識別のサンプル&lt;br /&gt;// ======================================&lt;br /&gt;// --------------------------------------&lt;br /&gt;&lt;br /&gt;// NeuralNetworkオブジェクトを宣言&lt;br /&gt;NeuralNetwork theBrain;&lt;br /&gt;&lt;br /&gt;// 学習用データセット&lt;br /&gt;List&amp;lt;PVector&amp;gt; class1;&lt;br /&gt;List&amp;lt;PVector&amp;gt; class2;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(400, 300);&lt;br /&gt;  &lt;br /&gt;  // ======================================&lt;br /&gt;  // 学習用データ（教師信号）のセットアップ&lt;br /&gt;  // ======================================&lt;br /&gt;  class1 = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;  class2 = new ArrayList&amp;lt;PVector&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  for(int i = 0; i &amp;lt; 10; i++) {&lt;br /&gt;    class1.add(new PVector(random(width / 2),        random(height)));&lt;br /&gt;    class2.add(new PVector(random(width / 2, width), random(height)));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ======================================&lt;br /&gt;  // ニューラルネットワークのセットアップ&lt;br /&gt;  // ======================================&lt;br /&gt;  theBrain = new NeuralNetwork();&lt;br /&gt;  &lt;br /&gt;  // 入力層、隠れ層、出力層の数をそれぞれ指定&lt;br /&gt;  theBrain.initialize(2, 10, 3);&lt;br /&gt;  &lt;br /&gt;  // 学習係数を0.2に設定&lt;br /&gt;  theBrain.setLearningRate(0.2);&lt;br /&gt;  &lt;br /&gt;  // 局所解回避のためのモーメンタムを使用&lt;br /&gt;  theBrain.setMomentum(true, 0.9);&lt;br /&gt;  &lt;br /&gt;  // ======================================&lt;br /&gt;  // 学習&lt;br /&gt;  // ======================================&lt;br /&gt;  double error = 1;&lt;br /&gt;  long c = 0;&lt;br /&gt;&lt;br /&gt;  // 収束するか、一定の回数に達するまで反復&lt;br /&gt;  while((error &amp;gt; 0.01) &amp;amp;&amp;amp; (c &amp;lt; 50000)) {&lt;br /&gt;    error = 0;&lt;br /&gt;    c++;&lt;br /&gt;&lt;br /&gt;    // ------------------------------------&lt;br /&gt;    // クラス1の学習&lt;br /&gt;    // ------------------------------------&lt;br /&gt;    for(int i = 0; i &amp;lt; class1.size(); i++) {&lt;br /&gt;      // 入力層に、インデックスとデータの組を与える&lt;br /&gt;      // ※ データは必ず 0 ～ 1 の範囲&lt;br /&gt;      theBrain.setInput(0, (float)class1.get(i).x / width);&lt;br /&gt;      theBrain.setInput(1, (float)class1.get(i).y / height);&lt;br /&gt;      &lt;br /&gt;      // 出力層に、インデックスとデータの組を与える&lt;br /&gt;      // ※ シグモイド関数を使用しているため、&lt;br /&gt;      //      0.1 → 不活性&lt;br /&gt;      //      0.9 → 活性&lt;br /&gt;      //    となるようにデータを与える&lt;br /&gt;      theBrain.setDesiredOutput(0, 0.9);&lt;br /&gt;      theBrain.setDesiredOutput(1, 0.1);&lt;br /&gt;      &lt;br /&gt;      // 誤差の計算&lt;br /&gt;      theBrain.feedForward();&lt;br /&gt;      error += theBrain.calculateError();&lt;br /&gt;      &lt;br /&gt;      // 誤差逆伝播&lt;br /&gt;      theBrain.backPropagate();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // ------------------------------------&lt;br /&gt;    // クラス2の学習&lt;br /&gt;    // ------------------------------------&lt;br /&gt;    for(int i = 0; i &amp;lt; class2.size(); i++) {&lt;br /&gt;      theBrain.setInput(0, (float)class2.get(i).x / width);&lt;br /&gt;      theBrain.setInput(1, (float)class2.get(i).y / height);&lt;br /&gt;      &lt;br /&gt;      theBrain.setDesiredOutput(0, 0.1);&lt;br /&gt;      theBrain.setDesiredOutput(1, 0.9);&lt;br /&gt;      &lt;br /&gt;      theBrain.feedForward();&lt;br /&gt;      error += theBrain.calculateError();&lt;br /&gt;      theBrain.backPropagate();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // 誤差の平均化&lt;br /&gt;    error = error / (float)(class1.size() + class2.size());&lt;br /&gt;  }  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  &lt;br /&gt;  // ======================================&lt;br /&gt;  // 識別境界の視覚化&lt;br /&gt;  // ======================================&lt;br /&gt;  for(int i = 0; i &amp;lt; width; i++) {&lt;br /&gt;    for(int j = 0; j &amp;lt; height; j++) {&lt;br /&gt;      &lt;br /&gt;      // ------------------------------------&lt;br /&gt;      // データ入力と識別&lt;br /&gt;      // ------------------------------------&lt;br /&gt;      theBrain.setInput(0, (float)i / width);&lt;br /&gt;      theBrain.setInput(1, (float)j / height);&lt;br /&gt;      theBrain.feedForward();&lt;br /&gt;      &lt;br /&gt;      // 識別結果の取得&lt;br /&gt;      int ret = theBrain.getMaxOutputID();&lt;br /&gt;      &lt;br /&gt;      if(ret == 0)      stroke(0xFFFF7F7F);  // クラス1の場合&lt;br /&gt;      else if(ret == 1) stroke(0xFF7F7FFF);  // クラス2の場合&lt;br /&gt;      &lt;br /&gt;      // 識別境界の表示&lt;br /&gt;      point(i, j);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // 学習データの表示&lt;br /&gt;  pushStyle();&lt;br /&gt;  strokeWeight(3);&lt;br /&gt;  &lt;br /&gt;  stroke(0xFFFF0000);&lt;br /&gt;  for(PVector c1 : class1) point(c1.x, c1.y);&lt;br /&gt;&lt;br /&gt;  stroke(0xFF0000FF);&lt;br /&gt;  for(PVector c2 : class2) point(c2.x, c2.y);&lt;br /&gt;&lt;br /&gt;  popStyle();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-8578625853974497753?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/8578625853974497753/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing_20.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8578625853974497753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8578625853974497753'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing_20.html' title='Processingで魔法使いの組分け帽子を作る'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/EANU0zzGguI/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-6916310209446016539</id><published>2012-02-18T22:13:00.002+09:00</published><updated>2012-02-18T22:22:07.168+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>ProcessingでAI</title><content type='html'>&lt;b&gt;&lt;span style="font-size: large;"&gt;【Boid 理論を眺める】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://laprasdrum.wordpress.com/2012/02/15/processing%E3%82%AD%E3%83%A9%E3%82%AD%E3%83%A9particle%E3%82%92%E7%9C%BA%E3%82%81%E3%82%8B/"&gt;くろねこさんが Boid 理論を使ってパーティクルを動かしていた&lt;/a&gt;のが気になって、僕もちょっと背伸びをして大風呂敷を広げてみました。……あ、&lt;b&gt;タイトルは釣りです&lt;/b&gt;。&lt;br /&gt;&lt;br /&gt;Boid 理論とは、団体行動を取っているような「群れ」を再現するシミュレーションです。ご存知ない方は、先入観なしに以下の動画をご覧ください。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/DgZnIxUGnAg?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;この理論の元ネタは、Craing Reynolds によって 1987 年に発表された “Flocks, Herds, and Schools: A Distributed Behavioral Model” という論文で、以下の行動パターンを組み合わせるだけで集団行動を巧妙にシミュレートできるというものです。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Cohesion （群れにくっつくよ！）&lt;/li&gt;&lt;li&gt;Alignment （みんなで同じ方向を向くよ！）&lt;/li&gt;&lt;li&gt;Separation （ぶつかりそうになったらちょっと離れるよ） &lt;/li&gt;&lt;/ul&gt;おもしろいですね。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;なお、サンプル動画の中では、個体の「視野角の広さ」を切り替えています。&lt;br /&gt;&lt;br /&gt;各個体が広い視野角を持つ場合、群れは横に大きく膨らむ形になります。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-R5KT5GmRTD4/Tz-gAHK7YZI/AAAAAAAAAjM/f7C82BPNOnA/s1600/2012021801.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-R5KT5GmRTD4/Tz-gAHK7YZI/AAAAAAAAAjM/f7C82BPNOnA/s320/2012021801.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;一方、視野角が狭い場合、個体は一列に並びます。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-RBoDsyTg7co/Tz-g3iRmE6I/AAAAAAAAAjU/K6Yz34BHT5w/s1600/2012021802.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://3.bp.blogspot.com/-RBoDsyTg7co/Tz-g3iRmE6I/AAAAAAAAAjU/K6Yz34BHT5w/s320/2012021802.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;なお、今回の実装にあたって参考にした文献は、David M. Bourg, Glenn Seemann 著『ゲーム開発者のための AI 入門』です。&lt;br /&gt;&lt;br /&gt;ほとんど移植に近いので恐縮ですが、お勉強日記なのでたまにはこういうのもいいかなーと。ちなみに実際に動作するサンプルとソースコードは &lt;a href="http://www.openprocessing.org/visuals/?visualID=52943"&gt;OpenProcessing&lt;/a&gt; の方に上げてありますが、ほとんど C++ 実装からの移植なので微妙かも知れません。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-6916310209446016539?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/6916310209446016539/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingai.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6916310209446016539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6916310209446016539'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingai.html' title='ProcessingでAI'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/DgZnIxUGnAg/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-131192418846533212</id><published>2012-02-16T20:59:00.001+09:00</published><updated>2012-02-16T21:01:04.477+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='電子工作'/><category scheme='http://www.blogger.com/atom/ns#' term='Arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>ProcessingとArduinoをくっつける</title><content type='html'>&lt;b&gt;&lt;span style="font-size: large;"&gt;【本日のお便りコーナー】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.bbpBox{background:url(http://a3.twimg.com/profile_background_images/236808095/toaruNeko.jpg) #ffffff;padding:20px;}&lt;/style&gt;&lt;br /&gt;&lt;div class="bbpBox" id="tweet_169935799343775745" style="background: url(http://a3.twimg.com/profile_background_images/236808095/toaruNeko.jpg) #ffffff; padding: 20px;"&gt;&lt;div class="bbpTweet" style="-moz-border-radius: 5px; -webkit-border-radius: 5px; background: #fff; color: black; font-size: 16px !important; line-height: 22px; margin: 0; min-height: 48px; padding: 10px 12px 10px 12px;"&gt;twitter4Jかー。この前購入してたarduino使うのかと思ってた∩（・ω・）∩&lt;span class="timestamp" style="display: block; font-size: 12px;"&gt;&lt;a href="http://twitter.com/laprasDrum/status/169935799343775745" title="2012年2月16日 9:7"&gt;2012年2月16日 9:7&lt;/a&gt; via &lt;a href="http://www.hootsuite.com/" rel="nofollow"&gt;HootSuite&lt;/a&gt;&lt;/span&gt;&lt;span class="metadata" style="border-top: 1px solid rgb(230, 230, 230); clear: both; display: block; height: 40px; margin-top: 8px; padding-top: 12px; width: 100%;"&gt;&lt;span class="author" style="line-height: 19px;"&gt;&lt;a href="http://twitter.com/laprasDrum"&gt;&lt;img src="http://a1.twimg.com/profile_images/1321835260/CatMusicLogo_normal.jpg" style="border: 0; float: left; height: 38px; margin: 0 7px 0 0px; padding: 0; width: 38px;" /&gt;&lt;/a&gt;&lt;b&gt;&lt;a href="http://twitter.com/laprasDrum"&gt;くろねこ&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;laprasDrum&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;—— というわけで、今日は Processing と Arduino を合体させてこんなの（↓）を作ってみようと思います。わー（歓声） ぱちぱち（拍手） ……あ、ちなみにこれの正体は最後に明らかになりますよ。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ENo2pHLbzWM/TzztWEnhg6I/AAAAAAAAAjE/a7c02vz0ooA/s1600/201202160.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-ENo2pHLbzWM/TzztWEnhg6I/AAAAAAAAAjE/a7c02vz0ooA/s1600/201202160.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;—— とは言ったものの、Processing と Arduino を連繋させる方法が見当もつきません。どこかにいいサンプルはないかと探していたら、&lt;a href="http://kenchiku-kousaku.blogspot.com/2011/09/arduino-processing.html"&gt;こちらの記事&lt;/a&gt;を見つけたので参考にする事にしました。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【配線】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;まずは Arduino の配線です。13 番ピンに LED を挿すらしいです（※ ピンぼけ容赦）。&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ia8qq44v5Lo/TzyYg0_d9tI/AAAAAAAAAiM/2rycYGLhsk8/s1600/PICT0518.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-ia8qq44v5Lo/TzyYg0_d9tI/AAAAAAAAAiM/2rycYGLhsk8/s320/PICT0518.JPG" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;LEDのアノードを13番ピンに、カソードをGNDに接続&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;【スケッチ&lt;/span&gt;&lt;/b&gt;&lt;span style="font-size: small;"&gt;（クリックで拡大）&lt;/span&gt;&lt;b&gt;&lt;span style="font-size: small;"&gt;】&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;左側が Arduino スケッチ、右側が Processing スケッチです。サンプルとほとんど一緒。&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-p3YzO0dMaYs/TzyM2iiLWeI/AAAAAAAAAh0/zQyr3OwoXgc/s1600/20120216_arduino.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="290" src="http://4.bp.blogspot.com/-p3YzO0dMaYs/TzyM2iiLWeI/AAAAAAAAAh0/zQyr3OwoXgc/s320/20120216_arduino.png" width="240" /&gt;&lt;/a&gt;&lt;a href="http://1.bp.blogspot.com/--Pytp5lhF1k/TzyNLqbrsSI/AAAAAAAAAh8/ZJumUpx7_vE/s1600/20120216_processing.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="290" src="http://1.bp.blogspot.com/--Pytp5lhF1k/TzyNLqbrsSI/AAAAAAAAAh8/ZJumUpx7_vE/s320/20120216_processing.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;今回は、配線 → Arduinoスケッチのアップロード → Processing スケッチの実行の順に行いました。あまり厳密にやらなくてもよさそうですが。&lt;br /&gt;&lt;br /&gt;注意点としては、Processing 側の以下の箇所。&lt;br /&gt;&lt;pre class="java:nocontrols:firstline[5]" name="code"&gt;myPort = new Serial(this, Serial.list()[1], 9600);&lt;br /&gt;&lt;/pre&gt;コンストラクタの第2引数には Arduino が接続されているポートを指定してやる必要があります。&lt;br /&gt;&lt;br /&gt;ここで使用している&lt;a href="http://processing.org/reference/libraries/serial/Serial_list_.html"&gt;Serial クラスの list(); メソッド&lt;/a&gt;は、使用可能なポート名の配列を返すメソッドです。その中から適切なポートを選んでやればよいみたいですね（僕の環境では &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;[1]&lt;/span&gt; ）。&lt;br /&gt;&lt;br /&gt;もしも何番のポートを選べばよいか判らない場合は、一度 &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Serial.list();&lt;/span&gt; を実行して有効なポート名を列挙し、Arduino が接続されているポートと一致するインデックスを探すのがよいと思います。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【実行結果】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;マウスボタンを押下すると LED が点灯し、離すと消灯します。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/82rxS_5UuJU?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;次に、Arduino からデータを送ってみる事にします。&lt;br /&gt;&lt;br /&gt;……で、そのサンプルが&lt;a href="http://kousaku-kousaku.blogspot.com/2008/05/arduino-processing.html"&gt;こちら&lt;/a&gt;なのですが、残念ながら僕は可変抵抗器を持っていません。しかも、近所に電子部品を売っているお店がないので、とりあえず CdS セルで代用してみようと思います。&lt;br /&gt;&lt;br /&gt;まずは CdS セルのおさらいも兼ねて、Arduino だけをいじってみる事にします。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【配線】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;CdS セル と LED を使ってこんな感じの配線をします。 &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-tpwJWYpSR-4/TzzAfYL6ZhI/AAAAAAAAAiU/_H2katvjRR4/s1600/2012021601.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="295" src="http://1.bp.blogspot.com/-tpwJWYpSR-4/TzzAfYL6ZhI/AAAAAAAAAiU/_H2katvjRR4/s320/2012021601.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;実写だとこんな感じ。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-39Xho68sBkw/TzzBo51t81I/AAAAAAAAAic/ZfPVvT6DWLg/s1600/PICT0522.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="300" src="http://2.bp.blogspot.com/-39Xho68sBkw/TzzBo51t81I/AAAAAAAAAic/ZfPVvT6DWLg/s400/PICT0522.JPG" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;画像では見づらいですが、CdS セルをアナログ入力用の 0 番ピンに、LED を出力の 9 番ピンにそれぞれ繋いでいます。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【Arduino スケッチ】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;次に、以下のようなコードを書いて、Arduino に送ります。&lt;br /&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#define LED 9&lt;br /&gt;int val = 0;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  pinMode(LED, OUTPUT);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop() {&lt;br /&gt;  val = analogRead(0);&lt;br /&gt;  &lt;br /&gt;  analogWrite(LED, val/4);&lt;br /&gt;  delay(10);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;上記のソースの 9 行目にある&lt;br /&gt;&lt;pre class="c++:nocontrols:firstline[9]" name="code"&gt;val = analogRead(0);&lt;br /&gt;&lt;/pre&gt;は、アナログ入力ピンの電圧を調べる関数です。Arduino は AD コンバータによって、0 ～ 5 [V] の入力電圧を 0 ～ 1023 の数値に変換しているため、変数 &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;val&lt;/span&gt; は 0 ～ 1023 の範囲をとる事になります。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【実行結果】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;これで、明るさに応じて LED の光の強さが変わるものができました。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/caSTxA8vM30?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;このサンプルで得た電圧のデータを Processing に送ってやれば、今までにできなかった事ができるようになるのではないでしょうか。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;やってみましょう。 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="color: yellow;"&gt;【配線】&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;とりあえず、LED はもう&lt;b&gt;要らない子&lt;/b&gt;なのでブレッドボードから外し、以下のような配線に戻します。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-Rq9P3s99QsA/TzzUPOBNT-I/AAAAAAAAAik/QeTKhdkecBk/s1600/2011021602.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-Rq9P3s99QsA/TzzUPOBNT-I/AAAAAAAAAik/QeTKhdkecBk/s320/2011021602.png" width="233" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【Arduino スケッチ】&lt;/b&gt;&lt;/div&gt;&lt;pre class="c++" name="code"&gt;int val = 0;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  Serial.begin(9600);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void loop() {&lt;br /&gt;  val = analogRead(0);&lt;br /&gt;  &lt;br /&gt;  // valを0～255の範囲に正規化してP5に送る&lt;br /&gt;  Serial.write(map(val, 0, 1023, 0, 255));  &lt;br /&gt;  delay(100);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【Processing スケッチ】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java" name="code"&gt;import processing.serial.*;&lt;br /&gt;Serial myPort;&lt;br /&gt;&lt;br /&gt;int val;&lt;br /&gt; &lt;br /&gt;void setup()&lt;br /&gt;{&lt;br /&gt;  size(400, 300);&lt;br /&gt;  &lt;br /&gt;  String port = Serial.list()[1];&lt;br /&gt;  myPort = new Serial(this, port, 9600);&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;void draw()&lt;br /&gt;{&lt;br /&gt;  // 電圧の強さに応じて背景色が変わるよ&lt;br /&gt;  background(val);&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;void serialEvent(Serial p){&lt;br /&gt;  // Arduinoからデータを読み取り&lt;br /&gt;  val = myPort.read();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【実行結果】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;CdS セルに充分な光が当たっているときは下図左、センサを手で完全に覆い隠すと下図右のようになりました。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-FGcesCW3zuI/TzzVpsMU8lI/AAAAAAAAAis/Uz22z3MenZw/s1600/2012021603.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="197" src="http://1.bp.blogspot.com/-FGcesCW3zuI/TzzVpsMU8lI/AAAAAAAAAis/Uz22z3MenZw/s320/2012021603.png" width="240" /&gt;&lt;/a&gt;&lt;a href="http://4.bp.blogspot.com/-1OX7CZTzRNk/TzzVtiwKzAI/AAAAAAAAAi0/bDCi8RCBlQA/s1600/2012021604.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="197" src="http://4.bp.blogspot.com/-1OX7CZTzRNk/TzzVtiwKzAI/AAAAAAAAAi0/bDCi8RCBlQA/s320/2012021604.png" width="240" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;基本的に、一度に Arduino とやり取りできるデータ量が 8 bits なので、簡単のために &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;val&lt;/span&gt; の範囲を 0 ～ 255 に正規化しています（0 ～ 1023 のデータも、一応がんばれば送る事はできますが）。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;【おまけ】&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;最後に、電圧の変化を無駄にビジュアル化してみます。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/1Mi2Np6Wji4?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;Arduino のスケッチはそのままで、Processing の方を以下のように書き換えるだけです。&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【Processing スケッチ】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java" name="code"&gt;import processing.serial.*;&lt;br /&gt;Serial myPort;&lt;br /&gt;&lt;br /&gt;PFont font;&lt;br /&gt;&lt;br /&gt;int   index;&lt;br /&gt;int[] data;&lt;br /&gt;&lt;br /&gt;int   val;&lt;br /&gt;long  time;&lt;br /&gt;&lt;br /&gt;void setup()&lt;br /&gt;{&lt;br /&gt;  size(640, 480, P3D);&lt;br /&gt;  &lt;br /&gt;  font = createFont("Meiryo", 20);&lt;br /&gt;  &lt;br /&gt;  index = 0;&lt;br /&gt;  data  = new int[width];&lt;br /&gt;  for(int i = 0; i &amp;lt; data.length; ++i) data[i] = -1;&lt;br /&gt;  &lt;br /&gt;  String port = Serial.list()[1];&lt;br /&gt;  myPort = new Serial(this, port, 9600);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw()&lt;br /&gt;{&lt;br /&gt;  background(0);&lt;br /&gt;  camera(0, -0xFF,   width, &lt;br /&gt;         0, -0xFF/2, 0, &lt;br /&gt;         0,  1,      0);&lt;br /&gt;         &lt;br /&gt;&lt;br /&gt;  float angle = radians(time) * 0.1f;&lt;br /&gt;  rotateY(angle);&lt;br /&gt;  &lt;br /&gt;  pushMatrix();&lt;br /&gt;  translate(-width/2, -0xFF, 0);&lt;br /&gt;  &lt;br /&gt;  int prevY = 0xFF;&lt;br /&gt;  for(int x = 1; x &amp;lt; width; ++x) {&lt;br /&gt;    int v = data[(x + index) % data.length];  // 電圧: 0 ～ 255&lt;br /&gt;    int y = 0xFF - v;  // y座標値&lt;br /&gt;&lt;br /&gt;    stroke(255, v);&lt;br /&gt;    if(prevY &amp;lt; 0xFF) line(x - 1, prevY, x, y);&lt;br /&gt;    prevY = y;&lt;br /&gt;  }&lt;br /&gt;  popMatrix();&lt;br /&gt;  &lt;br /&gt;  // じめん&lt;br /&gt;  stroke(0, 255, 0, 100);&lt;br /&gt;  for(int i = -width/2; i &amp;lt;= width / 2; i += 40) {&lt;br /&gt;    line(-width/2, 0, i, width/2, 0, i);&lt;br /&gt;    line(i, 0, -width/2, i, 0, width/2);&lt;br /&gt;  } &lt;br /&gt;  &lt;br /&gt;  pushMatrix();&lt;br /&gt;  camera();&lt;br /&gt;  hint(DISABLE_DEPTH_TEST);&lt;br /&gt;  textMode(SCREEN);&lt;br /&gt;  // 1単位あたりの分解能: 5000[mV] / 255 ≒ 19.6[mV]&lt;br /&gt;  text("Voltage: " + (val * 19.6) + " [mV]", 10, 10);&lt;br /&gt;  &lt;br /&gt;  hint(ENABLE_DEPTH_TEST);&lt;br /&gt;  popMatrix();&lt;br /&gt;  &lt;br /&gt;  ++time;&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;void serialEvent(Serial p){&lt;br /&gt;  // Arduinoからデータを読み取り&lt;br /&gt;  val = myPort.read();&lt;br /&gt;  &lt;br /&gt;  index = (index + 1) % data.length;&lt;br /&gt;  data[index] = val;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-131192418846533212?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/131192418846533212/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingarduino.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/131192418846533212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/131192418846533212'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingarduino.html' title='ProcessingとArduinoをくっつける'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-ENo2pHLbzWM/TzztWEnhg6I/AAAAAAAAAjE/a7c02vz0ooA/s72-c/201202160.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-9019230442162483269</id><published>2012-02-15T23:52:00.000+09:00</published><updated>2012-02-15T23:52:34.895+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><category scheme='http://www.blogger.com/atom/ns#' term='Twitter4J'/><title type='text'>ProcessingとTwitterをくっつける</title><content type='html'>くろねこさんが、昨日のサンプルをさっそく fork して下さいました（→&lt;a href="http://laprasdrum.wordpress.com/2012/02/15/processing%E3%82%AD%E3%83%A9%E3%82%AD%E3%83%A9particle%E3%82%92%E7%9C%BA%E3%82%81%E3%82%8B/"&gt;くろねこさんの記事&lt;/a&gt;）。&lt;br /&gt;&lt;br /&gt;パーティクルの動きの改良に加え、ソースコードもリファクタリングされて品質が上がっています。さすが！&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;さてさて、今日は Processing を Twitter に組み合わせてへんなのを作りますよー∩( ・ω・)∩&lt;br /&gt;&lt;br /&gt;なんか、テキスト領域に Twitter のユーザ名を入れると、フォローしているおともだちが3次元空間を漂うというなんとも愉快なスケッチです。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/C4-AlPcrp3s?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;実は、ほとんど昨日のソースコードを流用しただけの手抜き更新というのは内緒だよ。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【Processing と GUI】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;本日一発目のテーマは、Processing スケッチへの GUI コンポーネントの組み込みです。 とりあえず完成すると以下のようになります（少し見づらいですが、動画の下の方にある藍色の矩形がテキストフィールドです）。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/3Plnl0PC5no?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;Processing 公式サイトにはいくつかの&lt;a href="http://processing.org/reference/libraries/#interface"&gt;サードパーティ製ライブラリが紹介されています&lt;/a&gt;。僕はその中から、&lt;a href="http://www.sojamo.de/libraries/controlP5/"&gt;ControlP5&lt;/a&gt; というライブラリを選びました（現時点の最新バージョンは ControlP5 0.5.4）。&lt;br /&gt;&lt;br /&gt;OpenGL レンダラの環境でテキストフィールドを使用するためのミニマルなサンプルコードは以下の通り。なお、外部ライブラリの設置方法に関しては、&lt;a href="http://labs.uechoco.com/blog/2008/03/processingjar.html"&gt;うえちょこさんの記事&lt;/a&gt;に詳しく書かれています。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;import controlP5.*;&lt;br /&gt;&lt;br /&gt;ControlP5 controlP5;&lt;br /&gt;Textfield tfUserName;  // テキストフィールド&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(400, 300, OPENGL);&lt;br /&gt;  controlP5 = new ControlP5(this);&lt;br /&gt;  &lt;br /&gt;  tfUserName = controlP5.addTextfield("userName", 10, 10, 200, 20);&lt;br /&gt;  tfUserName.setFocus(true);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  hint(ENABLE_DEPTH_TEST);&lt;br /&gt;  background(0);&lt;br /&gt;&lt;br /&gt;  /* ============================== */&lt;br /&gt;  /* ここに描画用のコードを書きます */&lt;br /&gt;  /* ============================== */&lt;br /&gt;  &lt;br /&gt;  // GUIコンポーネント描画のためにカメラ位置をリセット&lt;br /&gt;  camera();&lt;br /&gt;  hint(DISABLE_DEPTH_TEST);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// テキストが入力された際にコールバックされるメソッド&lt;br /&gt;public void userName(String txt) {&lt;br /&gt;  if(txt.trim().length() &amp;lt; 1) return;&lt;br /&gt;&lt;br /&gt;  println("「" + txt + "」が入力されました");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;デモ動画のソースコードは……適当に作ったため、あまり見せられる代物ではありませんが、たぶん昔の僕だったら「汚くてもいいからソースコードを知りたい」と思ったでしょうから、こっそり晒しておきますね。&lt;br /&gt;&lt;div style="color: red;"&gt;&lt;br /&gt;&lt;b&gt;【おまけソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;import javax.media.opengl.*;  &lt;br /&gt;import controlP5.*;&lt;br /&gt;&lt;br /&gt;final int FIELD_SIZE = 1000;&lt;br /&gt;final int FIELD_STEP =  100;&lt;br /&gt;&lt;br /&gt;ControlP5 controlP5;&lt;br /&gt;Textfield tfUserName;       // テキストフィールド&lt;br /&gt;&lt;br /&gt;PFont  font;  &lt;br /&gt;long   time;  &lt;br /&gt;&lt;br /&gt;List&amp;lt;Message&amp;gt; messageList;  // メッセージリスト&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(640, 480, OPENGL);&lt;br /&gt;  &lt;br /&gt;  messageList = new LinkedList&amp;lt;Message&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  controlP5 = new ControlP5(this);&lt;br /&gt;  &lt;br /&gt;  tfUserName = controlP5.addTextfield("textfield", 10, 10, 200, 20);&lt;br /&gt;  tfUserName.setFocus(true);&lt;br /&gt;  &lt;br /&gt;  font = createFont("Meiryo", 48);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;&lt;br /&gt;  PGraphicsOpenGL pgl = (PGraphicsOpenGL)g;  &lt;br /&gt;&lt;br /&gt;  // カメラの設定&lt;br /&gt;  float angle = 0.5f * radians(time);      &lt;br /&gt;  camera(300*cos(angle), -300*sin(angle), 300*sin(angle),   &lt;br /&gt;         0,               0,              0,   &lt;br /&gt;         0,               1,              0);&lt;br /&gt;  &lt;br /&gt;  // 地面の描画&lt;br /&gt;  pushStyle();&lt;br /&gt;  stroke(255, 0, 0);  &lt;br /&gt;  for(int i = -FIELD_SIZE; i &amp;lt;= FIELD_SIZE; i += FIELD_STEP) { &lt;br /&gt;    line(          i, 0, -FIELD_SIZE,          i, 0, FIELD_SIZE);   &lt;br /&gt;    line(-FIELD_SIZE, 0,           i, FIELD_SIZE, 0,          i);&lt;br /&gt;  }&lt;br /&gt;  popStyle();&lt;br /&gt;  &lt;br /&gt;  // α合成を有効にする&lt;br /&gt;  GL gl = pgl.beginGL();&lt;br /&gt;  gl.glEnable(GL.GL_BLEND);&lt;br /&gt;  gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);  &lt;br /&gt;  gl.glDisable(GL.GL_DEPTH_TEST);&lt;br /&gt;  pgl.endGL();&lt;br /&gt;  &lt;br /&gt;  // メッセージの描画&lt;br /&gt;  for(Iterator iter = messageList.iterator(); iter.hasNext();) {&lt;br /&gt;    Message msg = (Message)iter.next();&lt;br /&gt;    // 更新して死んでたらリストから削除&lt;br /&gt;    if(!msg.update()) iter.remove(); &lt;br /&gt;  }&lt;br /&gt;  time++;&lt;br /&gt;  &lt;br /&gt;  // GUI コンポーネント描画のための設定&lt;br /&gt;  camera();&lt;br /&gt;  gl = pgl.beginGL();&lt;br /&gt;  gl.glDisable(GL.GL_DEPTH_TEST);  // 深度テストを無効化&lt;br /&gt;  gl.glEnable(GL.GL_BLEND);&lt;br /&gt;  gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);  &lt;br /&gt;  pgl.endGL();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// テキストが入力された際にコールバックされるメソッド&lt;br /&gt;public synchronized void textfield(String txt) {&lt;br /&gt;  // 適当な初期位置を指定して、リストに新たなメッセージを追加する&lt;br /&gt;  messageList.add(new Message(txt,&lt;br /&gt;      new PVector(random(-100, 100), 0, random(-100, 100))));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// メッセージクラス&lt;br /&gt;class Message {&lt;br /&gt;  private final int   BASECOLOR = 0xFFFFFFFF;  // 基本色&lt;br /&gt;  private final int   TTL       = 200;         // 生存時間&lt;br /&gt;  private final float dY        = -0.5;        // y座標の増分&lt;br /&gt;&lt;br /&gt;  private String  message;       // メッセージ&lt;br /&gt;  private PVector pos;           // 位置&lt;br /&gt;  private int     time;          // 経過時間  &lt;br /&gt;  &lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // -----------------------------&lt;br /&gt;  // メッセージと初期位置を指定する&lt;br /&gt;  Message(String msg, PVector pos) {&lt;br /&gt;    this.message = msg;&lt;br /&gt;    this.pos     = pos;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // update()&lt;br /&gt;  // -----------------------------&lt;br /&gt;  // 状態を更新し、生死状態を返す&lt;br /&gt;  // true:  有効（生きている）&lt;br /&gt;  // false: 無効（死んでいる）&lt;br /&gt;  boolean update() {&lt;br /&gt;    if(++time &amp;gt; TTL) return false;&lt;br /&gt;    pos.y += dY;&lt;br /&gt;    &lt;br /&gt;    // ビルボーディング &lt;br /&gt;    pushMatrix();&lt;br /&gt;    translate(pos.x, pos.y, pos.z);  // 物体を並進（ローカル座標系）  &lt;br /&gt;      &lt;br /&gt;    // モデル-ビュー行列を取得  &lt;br /&gt;    PMatrix3D builboardMat = (PMatrix3D)g.getMatrix();  &lt;br /&gt;    // 回転成分を単位行列に  &lt;br /&gt;    builboardMat.m00 = builboardMat.m11 = builboardMat.m22 = 1;  &lt;br /&gt;    builboardMat.m01 = builboardMat.m02 =   &lt;br /&gt;    builboardMat.m10 = builboardMat.m12 =   &lt;br /&gt;    builboardMat.m20 = builboardMat.m21 = 0;  &lt;br /&gt;    &lt;br /&gt;    resetMatrix();  &lt;br /&gt;    applyMatrix(builboardMat);  &lt;br /&gt;&lt;br /&gt;    // アルファ値を計算&lt;br /&gt;    pushStyle();&lt;br /&gt;    int a = 255 - 255 * time / TTL;&lt;br /&gt;    fill(a &amp;lt;&amp;lt; 24 | BASECOLOR &amp;amp; 0xFFFFFF);&lt;br /&gt;    &lt;br /&gt;    // フォントを設定&lt;br /&gt;    textFont(font, 24);  &lt;br /&gt;    textAlign(CENTER);&lt;br /&gt;    &lt;br /&gt;    // テキストを描画&lt;br /&gt;    text(message, 0, 0);&lt;br /&gt;    popStyle();    &lt;br /&gt;       &lt;br /&gt;    popMatrix();&lt;br /&gt;    return true;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【Processing と Twitter】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;GUI は一旦おいといて(つ´∀｀)つ&lt;br /&gt;&lt;br /&gt;今度は Twitter との連繋を考えてみます。ここでのゴールは、ひとまず Twitter でフォローしている方々のアイコンを 3次元空間中に散りばめる事です。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/Km3MNQ0MIAg?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;Twitter API を直叩きするのも面倒なので、Twitter4J という便利なライブラリをちょこっと使います。ライブラリは&lt;a href="http://twitter4j.org/"&gt;公式サイト&lt;/a&gt; あるいは &lt;a href="https://github.com/yusuke/twitter4j/"&gt;GitHub&lt;/a&gt; から入手できます。今回は最新安定版である twitter4j-2.2.5.zip を選択しました。&lt;br /&gt;&lt;br /&gt;今日はもう疲れ始めたので、OAuth 認証の要らないフォロー/フォロワー一覧を取得するところまでやろうかと思います。&lt;br /&gt;&lt;br /&gt;&lt;a href="http://twitter4j.org/ja/javadoc/index.html"&gt;JavaDoc&lt;/a&gt; などを参照しつつ、フレンド（自分がフォローしているアカウント） を調べるプログラムを Processing で書いてみました。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import twitter4j.*;&lt;br /&gt;&lt;br /&gt;final String USER_NAME = "tercel_s";&lt;br /&gt;&lt;br /&gt;Twitter twitter;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  twitter = new TwitterFactory().getInstance();&lt;br /&gt;  &lt;br /&gt;  try {&lt;br /&gt;    &lt;br /&gt;    // 指定したユーザがフォローしているアカウント一覧を取得&lt;br /&gt;    IDs friends = twitter.getFriendsIDs(USER_NAME, -1);&lt;br /&gt;    long[] friendsIDs = friends.getIDs();&lt;br /&gt;    &lt;br /&gt;    // Twitter ID からユーザ情報を逐次取得して表示&lt;br /&gt;    for(long id : friendsIDs) {&lt;br /&gt;      User user = twitter.showUser(id);&lt;br /&gt;      println(user.getName() + " (" + user.getScreenName() + ")");&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;  } catch (TwitterException ex) {&lt;br /&gt;    if(ex.isCausedByNetworkIssue()) {&lt;br /&gt;      // 何らかの事情でネットワークに接続できませんでした&lt;br /&gt;    } else {&lt;br /&gt;      int statusCode = ex.getStatusCode();&lt;br /&gt;      switch(statusCode) {&lt;br /&gt;        case 400:  // Bad Request&lt;br /&gt;          // リクエストに不正があるかレートリミットに達しました&lt;br /&gt;          break;&lt;br /&gt;        case 401:  // Unauthorized&lt;br /&gt;          // 認証情報に間違いがあります&lt;br /&gt;          break;&lt;br /&gt;        case 403:  // Forbidden&lt;br /&gt;          // リクエストが拒否されました&lt;br /&gt;          break;&lt;br /&gt;        case 404:  // Not Found&lt;br /&gt;          // 存在しないリソースにアクセスしました&lt;br /&gt;          break;&lt;br /&gt;        case 500:  // Server Internal Error&lt;br /&gt;          // サーバ障害が発生しています&lt;br /&gt;          break;&lt;br /&gt;        case 502:  // Bad Gateway&lt;br /&gt;          // Twitterサービスに障害が発生しています&lt;br /&gt;          break;&lt;br /&gt;        case 503:  // Service Unavailable&lt;br /&gt;          // Twitterが過負荷状態です&lt;br /&gt;          break;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  noLoop();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  // &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;実行すると、コンソールにフレンド一覧が列挙されます。&lt;br /&gt;&lt;br /&gt;ただこれ、けっこう時間がかかります。ですので、フォロー一覧の読み込み処理は別スレッドに移した方がよいでしょう。適当にマルチスレッド化するとこうなります（本当に適当なので、よい子はマネしないでね）。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import twitter4j.*;&lt;br /&gt;&lt;br /&gt;final String USER_NAME = "tercel_s";&lt;br /&gt;&lt;br /&gt;Twitter twitter;&lt;br /&gt;boolean isLoading;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  twitter = new TwitterFactory().getInstance();&lt;br /&gt;  &lt;br /&gt;  // マルチスレッドだよ∩( ・ω・)∩&lt;br /&gt;  new Thread() {&lt;br /&gt;    public synchronized void run() {&lt;br /&gt;      isLoading = true;&lt;br /&gt;      try {&lt;br /&gt;        // 指定したユーザがフォローしているアカウント一覧を取得&lt;br /&gt;        IDs friends = twitter.getFriendsIDs(USER_NAME, -1);&lt;br /&gt;        long[] friendsIDs = friends.getIDs();&lt;br /&gt;        &lt;br /&gt;        // Twitter ID からユーザ情報を逐次取得して表示&lt;br /&gt;        for(long id : friendsIDs) {&lt;br /&gt;          User user = twitter.showUser(id);&lt;br /&gt;          println(user.getName() + " (" + user.getScreenName() + ")");&lt;br /&gt;        }&lt;br /&gt;        isLoading = false;&lt;br /&gt;      } catch (TwitterException ex) {&lt;br /&gt;        /* 省略 */&lt;br /&gt;      } finally {&lt;br /&gt;        isLoading = false;  // デッドロック回避用&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }.start();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() { &lt;br /&gt;  // &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これさえできれば、デモ動画の作品を作るのは簡単です。ほとんど昨日のパーティクルをユーザのアイコンに差し換えただけです。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: red;"&gt;&lt;b&gt;【おまけソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;import javax.media.opengl.*;&lt;br /&gt;import twitter4j.*;&lt;br /&gt;&lt;br /&gt;final int FIELD_SIZE = 1000;&lt;br /&gt;final int FIELD_STEP =  100;&lt;br /&gt;&lt;br /&gt;final String USER_NAME = "tercel_s";  &lt;br /&gt;boolean      isLoading;&lt;br /&gt;&lt;br /&gt;List&amp;lt;Agent&amp;gt; agentList;  // フレンドアイコンのリスト&lt;br /&gt;&lt;br /&gt;long time;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(800, 600, OPENGL);  &lt;br /&gt;  &lt;br /&gt;  agentList = new ArrayList&amp;lt;Agent&amp;gt;();&lt;br /&gt;&lt;br /&gt;  // マルチスレッドでフレンドを読み込む&lt;br /&gt;  new Thread(new FriendLoader()).start();&lt;br /&gt;  hint(ENABLE_DEPTH_SORT);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() { &lt;br /&gt;  background(0);&lt;br /&gt;  &lt;br /&gt;  /* ================== */&lt;br /&gt;  /* カメラを適当に設定 */&lt;br /&gt;  /* ================== */&lt;br /&gt;  float angle = 0.05f * radians(time);&lt;br /&gt;  &lt;br /&gt;  camera(800, -200,   0, &lt;br /&gt;           0,    0,   0, &lt;br /&gt;           0,    1,   0);&lt;br /&gt;  rotateY(angle);  // ちょっと回す&lt;br /&gt;&lt;br /&gt;  pushStyle();&lt;br /&gt;  &lt;br /&gt;  // テクスチャの加算合成を有効にする&lt;br /&gt;  PGraphicsOpenGL pgl = (PGraphicsOpenGL)g;&lt;br /&gt;  GL gl = pgl.beginGL();&lt;br /&gt;  &lt;br /&gt;  gl.glEnable(GL.GL_BLEND);&lt;br /&gt;  gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);&lt;br /&gt;  pgl.endGL();  &lt;br /&gt;  &lt;br /&gt;  noStroke();&lt;br /&gt;  fill(0);&lt;br /&gt;  &lt;br /&gt;  // フレンド一覧を表示&lt;br /&gt;  for(int i = 0; i &amp;lt; agentList.size(); ++i) {&lt;br /&gt;    agentList.get(i).update();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  popStyle();&lt;br /&gt;&lt;br /&gt;  /* ============== */&lt;br /&gt;  /* 地面とかの描画 */&lt;br /&gt;  /* ============== */&lt;br /&gt;  pushStyle();&lt;br /&gt;  // 地面の描画 &lt;br /&gt;  stroke(0, 255, 0, 100);&lt;br /&gt;  for(int i = -FIELD_SIZE; i &amp;lt;= FIELD_SIZE; i += FIELD_STEP) { &lt;br /&gt;    line(          i, 0, -FIELD_SIZE,          i, 0, FIELD_SIZE);   &lt;br /&gt;    line(-FIELD_SIZE, 0,           i, FIELD_SIZE, 0,          i);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  popStyle();&lt;br /&gt;  &lt;br /&gt;  time++;&lt;br /&gt;  mm.addFrame();&lt;br /&gt;  if(time &amp;gt; 60 * 60) {&lt;br /&gt;    mm.finish();&lt;br /&gt;    background(0);&lt;br /&gt;    noLoop();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// フレンドを読み込むためのクラス&lt;br /&gt;class FriendLoader implements Runnable {&lt;br /&gt;  public void run() {&lt;br /&gt;    isLoading = true;&lt;br /&gt;    Twitter twitter = new TwitterFactory().getInstance();&lt;br /&gt;    try {&lt;br /&gt;      // まずは自分自身をリストに追加&lt;br /&gt;      User me = twitter.showUser(USER_NAME);&lt;br /&gt;      PImage myIcon = loadImage(me.getProfileImageURL().toString());&lt;br /&gt;      myIcon.resize(100, 100);&lt;br /&gt;      agentList.add(new Agent(myIcon, new PVector()));&lt;br /&gt;            &lt;br /&gt;      // 自分がフォローしているアカウント一覧を取得&lt;br /&gt;      IDs friends = twitter.getFriendsIDs(USER_NAME, -1);&lt;br /&gt;      long[] friendsIDs = friends.getIDs();&lt;br /&gt;        &lt;br /&gt;      // Twitter ID からユーザ情報を逐次取得&lt;br /&gt;      for(long id : friendsIDs) {&lt;br /&gt;        User user = twitter.showUser(id);&lt;br /&gt;          &lt;br /&gt;        // アイコン取得とリサイズ&lt;br /&gt;        PImage profileImage = loadImage(user.getProfileImageURL().toString());&lt;br /&gt;        if(profileImage == null) continue;&lt;br /&gt;        &lt;br /&gt;        profileImage.resize(100, 100);&lt;br /&gt;&lt;br /&gt;        // 位置の設定&lt;br /&gt;        float x = random(-1000, 1000);&lt;br /&gt;        float z = random(-1000, 1000);&lt;br /&gt;        agentList.add(new Agent(profileImage, new PVector(x, 0, z)));&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      isLoading = false;&lt;br /&gt;    } catch (TwitterException ex) {&lt;br /&gt;      /* 省略 */&lt;br /&gt;    } finally {&lt;br /&gt;      isLoading = false;  // デッドロック回避用&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// エージェント&lt;br /&gt;class Agent {&lt;br /&gt;  private final float TERRITORY_SIZE = 2000;&lt;br /&gt;  private final float NOISE_SCALE    = 0.001f;&lt;br /&gt;  private final float AGENT_SIZE     = 50; &lt;br /&gt;  &lt;br /&gt;  private PImage  tex;     // テクスチャ&lt;br /&gt;  &lt;br /&gt;  private PVector center;  // 縄張りの中心座標&lt;br /&gt;  private PVector offset;  // 中心からのオフセット&lt;br /&gt;  private PVector pos;     // 座標&lt;br /&gt;  &lt;br /&gt;  private long time;      // 経過時間&lt;br /&gt;  float xOffset, zOffset; // ノイズ生成用&lt;br /&gt;  &lt;br /&gt;  Agent(PImage tex, PVector center) {&lt;br /&gt;    this.center = center;&lt;br /&gt;    this.tex    = tex;&lt;br /&gt;    pos         = new PVector();&lt;br /&gt;    offset      = new PVector();&lt;br /&gt;    xOffset     = random(TERRITORY_SIZE);&lt;br /&gt;    zOffset     = random(TERRITORY_SIZE);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  PVector getPos() {&lt;br /&gt;    return pos;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void update() {&lt;br /&gt;    if (tex == null) return;&lt;br /&gt;    time++;&lt;br /&gt;    &lt;br /&gt;    offset.x = (noise((time + xOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    offset.z = (noise((time + zOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    &lt;br /&gt;    pushMatrix();&lt;br /&gt;    pos.set(center.x + offset.x, center.y, center.z + offset.z);&lt;br /&gt;    translate(pos.x, pos.y, pos.z);&lt;br /&gt;    &lt;br /&gt;    // モデル-ビュー行列を取得&lt;br /&gt;    PMatrix3D builboardMat = (PMatrix3D)g.getMatrix();&lt;br /&gt;    // 回転成分を単位行列に&lt;br /&gt;    builboardMat.m00 = builboardMat.m11 = builboardMat.m22 = 1;&lt;br /&gt;    builboardMat.m01 = builboardMat.m02 = &lt;br /&gt;    builboardMat.m10 = builboardMat.m12 = &lt;br /&gt;    builboardMat.m20 = builboardMat.m21 = 0;&lt;br /&gt;  &lt;br /&gt;    resetMatrix();&lt;br /&gt;    applyMatrix(builboardMat);&lt;br /&gt;        &lt;br /&gt;    beginShape(QUADS);&lt;br /&gt;    texture(tex);&lt;br /&gt;    vertex(-0.5f * AGENT_SIZE, -AGENT_SIZE, 0, 0,         0);&lt;br /&gt;    vertex(-0.5f * AGENT_SIZE,           0, 0, 0,         tex.height-1);&lt;br /&gt;    vertex( 0.5f * AGENT_SIZE,           0, 0, tex.width, tex.height-1);&lt;br /&gt;    vertex( 0.5f * AGENT_SIZE, -AGENT_SIZE, 0, tex.width, 0);    &lt;br /&gt;    endShape();&lt;br /&gt;        &lt;br /&gt;    popMatrix();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;あとはこの 2 つを適当に組み合わせれば、冒頭の動画のようなものが作れると思います。&lt;br /&gt;&lt;br /&gt;なしくずしてきにめでたしめでたし。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;そんなこんなで久しぶりに Twitter ネタを書きましたが、このプログラムにはまだまだアラがあります。&lt;br /&gt;&lt;br /&gt;まず、Twitter の API 呼び出しには制限が設けられており、このプログラムを試しているとあっという間に API 上限に引っかかってしまいます。フォローしているアカウントの情報を一人ずつ取得していくと、&lt;b&gt;アホみたいなスピード&lt;/b&gt;で限界に近付いていきます。&lt;br /&gt;&lt;br /&gt;次に、計算機資源の問題があります。今はとりあえずフレンド情報を全読みしていますが、それこそ何十万人もフォローしているような人が動かしたら、あっという間にメモリを喰い尽くして死ぬでしょう。&lt;br /&gt;&lt;br /&gt;それでもまぁ、とりあえず Twitter と P5 をくっつけてそれっぽい事ができたので今日はよしとします。ふぅ、つかれた(´-ω-`)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-9019230442162483269?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/9019230442162483269/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingtwitter.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/9019230442162483269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/9019230442162483269'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processingtwitter.html' title='ProcessingとTwitterをくっつける'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/C4-AlPcrp3s/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-8114022238840229319</id><published>2012-02-14T20:37:00.003+09:00</published><updated>2012-02-14T22:24:43.874+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenGL'/><title type='text'>Processingできらきら3次元パーティクル</title><content type='html'>今日はこういうものを作ります。たぶんきれいですね。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/tAQ7VK8I5pM?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;このスケッチは、いくつかのテクニックを組み合わせて作ります。&lt;br /&gt;&lt;br /&gt;恐らく、Processing ユーザにとっては馴染みの薄いものもあるかと思いますので、今日はそれらをひとつひとつ紹介しつつ完成形に持っていこうと考えています。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【ビルボードをつくる】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;ビルボードとは、3次元空間中のカメラに対して常に正面を向くような2次元オブジェクトの事です。一昔前のゲームなどではよく応用されていた技術です（今もかな）。これ自体が独立したひとつのトピックなので、まずはここから始めてみましょう。&lt;br /&gt;&lt;br /&gt;以下のサンプルは、テキスト（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Hello, World&lt;/span&gt;）をビルボード上に表示するというものです。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/6H1D9n3QO3Y?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【理論の解説】&lt;/b&gt; &lt;/div&gt;&lt;br /&gt;ビルボードは、以下のような同次形のモデル・ビュー変換行列&lt;br /&gt;\begin{align}&lt;br /&gt;{}^{\Sigma_{\mathrm{View}}}\vect{M}_{\Sigma_{\mathrm{Model}_{i}}} = &lt;br /&gt;\left(\!&lt;br /&gt;\begin{array}{ccc|c}&lt;br /&gt;m_{00} &amp;amp; m_{01} &amp;amp; m_{02} &amp;amp; m_{03} \\&lt;br /&gt;m_{10} &amp;amp; m_{11} &amp;amp; m_{12} &amp;amp; m_{13} \\&lt;br /&gt;m_{20} &amp;amp; m_{21} &amp;amp; m_{22} &amp;amp; m_{23} \\ \hline&lt;br /&gt;m_{30} &amp;amp; m_{31} &amp;amp; m_{32} &amp;amp; m_{33} &lt;br /&gt;\end{array}&lt;br /&gt;\!\right)&lt;br /&gt;\end{align}――のうち、左上の3×3の小行列を単位行列にした結果&lt;br /&gt;\begin{align}&lt;br /&gt;\left( \!&lt;br /&gt;\begin{array}{ccc|c}&lt;br /&gt;1 &amp;amp; 0 &amp;amp; 0 &amp;amp; m_{03} \\&lt;br /&gt;0 &amp;amp; 1 &amp;amp; 0 &amp;amp; m_{13} \\&lt;br /&gt;0 &amp;amp; 0 &amp;amp; 1 &amp;amp; m_{23} \\ \hline&lt;br /&gt;m_{30} &amp;amp; m_{31} &amp;amp; m_{32} &amp;amp; m_{33} &lt;br /&gt;\end{array}&lt;br /&gt;\!\right)&lt;br /&gt;\end{align}を、新たなモデル・ビュー行列として設定します。たったこれだけ。かんたんですね。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;&lt;br /&gt;PFont font;&lt;br /&gt;long  time;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(800, 600, OPENGL);&lt;br /&gt;  &lt;br /&gt;  // フォント生成（メイリオ）&lt;br /&gt;  font = createFont("Meiryo", 48);&lt;br /&gt;  &lt;br /&gt;  // デプスソートを有効にする&lt;br /&gt;  // これがないとテキスト表示がおかしくなる&lt;br /&gt;  hint(ENABLE_DEPTH_SORT);&lt;br /&gt;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  &lt;br /&gt;  /* ================== */&lt;br /&gt;  /* カメラを適当に設定 */&lt;br /&gt;  /* ================== */&lt;br /&gt;  float angle = 0.5f * radians(time);&lt;br /&gt;  &lt;br /&gt;  camera(0, -300*sin(angle), 300, &lt;br /&gt;         0,               0,   0, &lt;br /&gt;         0,               1,   0);&lt;br /&gt;  rotateY(angle);  // ちょっと回す&lt;br /&gt;&lt;br /&gt;  /* ================ */&lt;br /&gt;  /* ビルボーディング */&lt;br /&gt;  /* ================ */&lt;br /&gt;  pushMatrix();&lt;br /&gt;  translate(100, 0, 0);  // 物体を並進（ローカル座標系）&lt;br /&gt;  &lt;br /&gt;  // モデル-ビュー行列を取得&lt;br /&gt;  PMatrix3D builboardMat = (PMatrix3D)g.getMatrix();&lt;br /&gt;  // 回転成分を単位行列に&lt;br /&gt;  builboardMat.m00 = builboardMat.m11 = builboardMat.m22 = 1;&lt;br /&gt;  builboardMat.m01 = builboardMat.m02 = &lt;br /&gt;  builboardMat.m10 = builboardMat.m12 = &lt;br /&gt;  builboardMat.m20 = builboardMat.m21 = 0;&lt;br /&gt;&lt;br /&gt;  resetMatrix();&lt;br /&gt;  applyMatrix(builboardMat);&lt;br /&gt;  &lt;br /&gt;  fill(255);&lt;br /&gt;  stroke(0);&lt;br /&gt;  textFont(font, 24);&lt;br /&gt;  textAlign(CENTER);&lt;br /&gt;  text("Hello, World!", 0, 0);&lt;br /&gt;&lt;br /&gt;  popMatrix();&lt;br /&gt;&lt;br /&gt;  /* ============== */&lt;br /&gt;  /* 地面とかの描画 */&lt;br /&gt;  /* ============== */&lt;br /&gt;  pushStyle();&lt;br /&gt;  // 地面の描画 &lt;br /&gt;  stroke(0, 255, 0);&lt;br /&gt;  for(int x = -1000; x &amp;lt;= 1000; x += 100)&lt;br /&gt;    line(x, 0, -1000, x, 0, 1000); &lt;br /&gt;  for(int z = -1000; z &amp;lt;= 1000; z += 100)&lt;br /&gt;    line(-1000, 0, z, 1000, 0, z);&lt;br /&gt;  &lt;br /&gt;  // 回転軸の描画&lt;br /&gt;  stroke(255, 0, 0);&lt;br /&gt;  line(0, -500, 0, 0, 500, 0);&lt;br /&gt;  popStyle();&lt;br /&gt;  &lt;br /&gt;  time++;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【自然なランダムウォークをつくる】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;自然なランダムウォークというと少しおかしな日本語ですが、あちこちに動きが飛ばず、なめらかな軌跡を描くような動きを作ります。&lt;br /&gt;&lt;br /&gt;タネを明かせば Perlin ノイズを何のひねりもなく使っただけで、当たり判定とかは考慮していません。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/EiHclpIj0HU?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;br /&gt;ここでは、動く個体を Agent クラスにまとめています。&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;&lt;br /&gt;List&amp;lt;Agent&amp;gt; agentList;&lt;br /&gt;long  time;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(800, 600, OPENGL);&lt;br /&gt;  &lt;br /&gt;  agentList = new ArrayList&amp;lt;Agent&amp;gt;();&lt;br /&gt;  for(int i = 0; i &amp;lt; 200; ++i) {&lt;br /&gt;    float x = random(-200, 200);&lt;br /&gt;    float z = random(-200, 200);&lt;br /&gt;    agentList.add(new Agent(new PVector(x, 0, z)));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  camera();&lt;br /&gt;  lights();&lt;br /&gt;    &lt;br /&gt;  /* ================== */&lt;br /&gt;  /* カメラを適当に設定 */&lt;br /&gt;  /* ================== */&lt;br /&gt;  float angle = 0.1f * radians(time);&lt;br /&gt;  &lt;br /&gt;  camera(800, -200,   0, &lt;br /&gt;         0,      0,   0, &lt;br /&gt;         0,      1,   0);&lt;br /&gt;  rotateY(angle);  // ちょっと回す&lt;br /&gt;&lt;br /&gt;  pushStyle();&lt;br /&gt;  noStroke();&lt;br /&gt;  colorMode(HSB, agentList.size() -1, 100, 100);&lt;br /&gt;  for(int i = 0; i &amp;lt; agentList.size(); ++i) {&lt;br /&gt;    fill(i, 100, 100);&lt;br /&gt;    agentList.get(i).update();&lt;br /&gt;  }  &lt;br /&gt;  popStyle();&lt;br /&gt;&lt;br /&gt;  /* ============== */&lt;br /&gt;  /* 地面とかの描画 */&lt;br /&gt;  /* ============== */&lt;br /&gt;  pushStyle();&lt;br /&gt;  // 地面の描画 &lt;br /&gt;  stroke(0, 255, 0);&lt;br /&gt;  for(int x = -1000; x &amp;lt;= 1000; x += 100)&lt;br /&gt;    line(x, 0, -1000, x, 0, 1000); &lt;br /&gt;  for(int z = -1000; z &amp;lt;= 1000; z += 100)&lt;br /&gt;    line(-1000, 0, z, 1000, 0, z);&lt;br /&gt;  &lt;br /&gt;  // 回転軸の描画&lt;br /&gt;  stroke(255, 0, 0);&lt;br /&gt;  line(0, -500, 0, 0, 500, 0);&lt;br /&gt;  popStyle();&lt;br /&gt;  &lt;br /&gt;  time++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Agent {&lt;br /&gt;  private final float TERRITORY_SIZE = 2000;&lt;br /&gt;  private final float NOISE_SCALE    = 0.001f;&lt;br /&gt;  private final float AGENT_SIZE     = 10; &lt;br /&gt;  &lt;br /&gt;  private PVector center;  // 縄張りの中心座標&lt;br /&gt;  private PVector offset;  // 中心からのオフセット&lt;br /&gt;  private PVector pos;     // 座標&lt;br /&gt;  &lt;br /&gt;  private long time;      // 経過時間&lt;br /&gt;  float xOffset, zOffset; // ノイズ生成用&lt;br /&gt;  &lt;br /&gt;  Agent(PVector center) {&lt;br /&gt;    this.center = center;&lt;br /&gt;    pos         = new PVector();&lt;br /&gt;    offset      = new PVector();&lt;br /&gt;    xOffset     = random(TERRITORY_SIZE);&lt;br /&gt;    zOffset     = random(TERRITORY_SIZE);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  PVector getPos() {&lt;br /&gt;    return pos;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void update() {&lt;br /&gt;    time++;&lt;br /&gt;    &lt;br /&gt;    offset.x = (noise((time + xOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    offset.z = (noise((time + zOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    &lt;br /&gt;    pushMatrix();&lt;br /&gt;    pos.set(center.x + offset.x, center.y - AGENT_SIZE * 0.5, center.z + offset.z);&lt;br /&gt;    translate(pos.x, pos.y, pos.z);&lt;br /&gt;    &lt;br /&gt;    box(AGENT_SIZE);&lt;br /&gt;        &lt;br /&gt;    popMatrix();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【合体させる】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;あとは、この2つのテクニックをてきとうに合体させるだけです。&lt;br /&gt;&lt;br /&gt;下図（左）のように、とりあえずランダムに動き回るビルボードを山のように作って、 それにテクスチャを貼れば完成。意外と簡単ですね。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-sNACWPky7Jc/TzpDUPtxPPI/AAAAAAAAAhs/1wVlHqdDvLU/s1600/20110214.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="150" src="http://1.bp.blogspot.com/-sNACWPky7Jc/TzpDUPtxPPI/AAAAAAAAAhs/1wVlHqdDvLU/s400/20110214.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;左： ビルボードの輪郭 / 右：テクスチャを貼った結果&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;ここで、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;glBlendFunc&lt;/span&gt; メソッドを使って加算合成モードにすると、ビルボード同士が重なった際に輝度が上がります。完成形のソースコードを以下に示します。&lt;br /&gt;&lt;br /&gt;&lt;div style="color: yellow;"&gt;&lt;b&gt;【ソースコード&lt;/b&gt;（展開してご覧ください）&lt;b&gt;】&lt;/b&gt;&lt;/div&gt;&lt;pre class="java:collapse" name="code"&gt;import processing.opengl.*;&lt;br /&gt;import javax.media.opengl.*;&lt;br /&gt;&lt;br /&gt;PGraphics   tex;&lt;br /&gt;List&amp;lt;Agent&amp;gt; agentList;&lt;br /&gt;long        time;&lt;br /&gt;&lt;br /&gt;void setup() {&lt;br /&gt;  size(800, 600, OPENGL);&lt;br /&gt; &lt;br /&gt;  // テクスチャ生成&lt;br /&gt;  tex = createTexture();&lt;br /&gt;  &lt;br /&gt;  agentList = new ArrayList&amp;lt;Agent&amp;gt;();&lt;br /&gt;  for(int i = 0; i &amp;lt; 1000; ++i) {&lt;br /&gt;    float x = random(-1000, 1000);&lt;br /&gt;    float z = random(-1000, 1000);&lt;br /&gt;    agentList.add(new Agent(new PVector(x, 0, z)));&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;    &lt;br /&gt;  /* ================== */&lt;br /&gt;  /* カメラを適当に設定 */&lt;br /&gt;  /* ================== */&lt;br /&gt;  float angle = 0.05f * radians(time);&lt;br /&gt;  &lt;br /&gt;  camera(800, -200,   0, &lt;br /&gt;           0,    0,   0, &lt;br /&gt;           0,    1,   0);&lt;br /&gt;  rotateY(angle);  // ちょっと回す&lt;br /&gt;&lt;br /&gt;  pushStyle();&lt;br /&gt;  &lt;br /&gt;  // テクスチャの加算合成を有効にする&lt;br /&gt;  PGraphicsOpenGL pgl = (PGraphicsOpenGL)g;&lt;br /&gt;  GL gl = pgl.beginGL();&lt;br /&gt;  gl.glDisable(GL.GL_DEPTH_TEST);&lt;br /&gt;  gl.glEnable(GL.GL_BLEND);&lt;br /&gt;  gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);&lt;br /&gt;  pgl.endGL();  &lt;br /&gt;  &lt;br /&gt;  noStroke();&lt;br /&gt;  fill(0);&lt;br /&gt;  for(int i = 0; i &amp;lt; agentList.size(); ++i) {&lt;br /&gt;    agentList.get(i).update();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  popStyle();&lt;br /&gt;&lt;br /&gt;  /* ============== */&lt;br /&gt;  /* 地面とかの描画 */&lt;br /&gt;  /* ============== */&lt;br /&gt;  pushStyle();&lt;br /&gt;  // 地面の描画 &lt;br /&gt;  stroke(0, 255, 0, 100);&lt;br /&gt;  for(int x = -1000; x &amp;lt;= 1000; x += 100)&lt;br /&gt;    line(x, 0, -1000, x, 0, 1000); &lt;br /&gt;  for(int z = -1000; z &amp;lt;= 1000; z += 100)&lt;br /&gt;    line(-1000, 0, z, 1000, 0, z);&lt;br /&gt;  &lt;br /&gt;  popStyle();&lt;br /&gt;  &lt;br /&gt;  time++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PGraphics createTexture() {&lt;br /&gt;  PGraphics tex = createGraphics(100, 100, P2D);&lt;br /&gt;  float r = tex.width * 2 / 3;&lt;br /&gt;  &lt;br /&gt;  tex.beginDraw();&lt;br /&gt;  tex.background(0);&lt;br /&gt;  tex.noStroke();&lt;br /&gt;  tex.fill(100, 150, 255);&lt;br /&gt;  tex.ellipse(tex.width/2, tex.height/2, r, r);&lt;br /&gt;  tex.filter(BLUR, 10.0);&lt;br /&gt;  tex.endDraw();&lt;br /&gt;  return tex;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Agent {&lt;br /&gt;  private final float TERRITORY_SIZE = 2000;&lt;br /&gt;  private final float NOISE_SCALE    = 0.001f;&lt;br /&gt;  private final float AGENT_SIZE     = random(50); &lt;br /&gt;  &lt;br /&gt;  private PVector center;  // 縄張りの中心座標&lt;br /&gt;  private PVector offset;  // 中心からのオフセット&lt;br /&gt;  private PVector pos;     // 座標&lt;br /&gt;  &lt;br /&gt;  private long time;      // 経過時間&lt;br /&gt;  float xOffset, zOffset; // ノイズ生成用&lt;br /&gt;  &lt;br /&gt;  Agent(PVector center) {&lt;br /&gt;    this.center = center;&lt;br /&gt;    pos         = new PVector();&lt;br /&gt;    offset      = new PVector();&lt;br /&gt;    xOffset     = random(TERRITORY_SIZE);&lt;br /&gt;    zOffset     = random(TERRITORY_SIZE);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  PVector getPos() {&lt;br /&gt;    return pos;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void update() {&lt;br /&gt;    time++;&lt;br /&gt;    &lt;br /&gt;    offset.x = (noise((time + xOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    offset.z = (noise((time + zOffset) * NOISE_SCALE) - 0.5f) * TERRITORY_SIZE;&lt;br /&gt;    &lt;br /&gt;    pushMatrix();&lt;br /&gt;    pos.set(center.x + offset.x, center.y, center.z + offset.z);&lt;br /&gt;    translate(pos.x, pos.y, pos.z);&lt;br /&gt;    &lt;br /&gt;    // モデル-ビュー行列を取得&lt;br /&gt;    PMatrix3D builboardMat = (PMatrix3D)g.getMatrix();&lt;br /&gt;    // 回転成分を単位行列に&lt;br /&gt;    builboardMat.m00 = builboardMat.m11 = builboardMat.m22 = 1;&lt;br /&gt;    builboardMat.m01 = builboardMat.m02 = &lt;br /&gt;    builboardMat.m10 = builboardMat.m12 = &lt;br /&gt;    builboardMat.m20 = builboardMat.m21 = 0;&lt;br /&gt;  &lt;br /&gt;    resetMatrix();&lt;br /&gt;    applyMatrix(builboardMat);&lt;br /&gt;        &lt;br /&gt;    beginShape(QUADS);&lt;br /&gt;    texture(tex);&lt;br /&gt;    vertex(-0.5f * AGENT_SIZE, -AGENT_SIZE, 0, 0,         0);&lt;br /&gt;    vertex(-0.5f * AGENT_SIZE,           0, 0, 0,         tex.height-1);&lt;br /&gt;    vertex( 0.5f * AGENT_SIZE,           0, 0, tex.width, tex.height-1);&lt;br /&gt;    vertex( 0.5f * AGENT_SIZE, -AGENT_SIZE, 0, tex.width, 0);    &lt;br /&gt;    endShape();&lt;br /&gt;        &lt;br /&gt;    popMatrix();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-8114022238840229319?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/8114022238840229319/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing3.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8114022238840229319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8114022238840229319'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing3.html' title='Processingできらきら3次元パーティクル'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/tAQ7VK8I5pM/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-1657370981604183729</id><published>2012-02-13T13:42:00.003+09:00</published><updated>2012-02-13T13:50:28.670+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='身辺雑記'/><title type='text'>【予告】 Processingで講演します（？）</title><content type='html'>少し先のお話ですが、来たる3月20日～23日に開催される電子情報通信学会・総合大会に参戦する事になりました。講演分野は『パターン認識・メディア理解』だそうです。&lt;br /&gt;&lt;br /&gt;今回は「Processing で『立体おえかきツール』を作ってみたよ」みたいな内容です。とりあえず予告篇のようなデモムービーをてきとうに作ったので、こっそりご紹介しておきますね（&lt;span style="color: red;"&gt;※注意：音が鳴ります&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe width="480" height="360" src="http://www.youtube.com/embed/tyXcWTobmAs?rel=0" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;あまり大規模な制作物ではありませんが、もしよろしければどうぞおたのしみに。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;【関連研究】&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;Robert C. Zeleznik, Kenneth P. Herndon, and John F. Hughes, “SKETCH: An interface for sketching 3D scenes”, ACM Transactions on Graphics (SIGGRAPH Proceedings), pp.163-170, 1996.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;Takeo Igarashi, Satoshi Matsuoka, Hidehiko Tanaka, “Teddy: A Sketching Interface for 3D Freeform Design”, ACM Transactions on Graphics (SIGGRAPH Proceedings), pp.409-416, 1999.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;Dorsey Julie, Xu Songhua, Smedresman Gabe, Rushmeir Holly, McMillan Leonard, “The Mental Canvas: A Tool for Conceptual Architectural Design and Analysis”, Proceedings of Pacific Graphics, pp.201-210, 2007.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt;Schmidt Ryan, Khan Azam, Singh Karan, Kurtenbach Gord, “Analytic Drawing of 3D Scaffolds”, ACM SIGGRAPH Asia 2009 papers 28(5), pp.149:1-149:10, 2009.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-size: x-small;"&gt; “SKETCHUP”, Google, 2009.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="text-align: right;"&gt;……などなど。&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-1657370981604183729?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/1657370981604183729/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1657370981604183729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1657370981604183729'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/processing.html' title='【予告】 Processingで講演します（？）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/tyXcWTobmAs/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-2766311720787943266</id><published>2012-02-09T22:23:00.001+09:00</published><updated>2012-02-09T22:29:49.116+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='電子工作'/><category scheme='http://www.blogger.com/atom/ns#' term='Arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><category scheme='http://www.blogger.com/atom/ns#' term='デジタルハリネズミ'/><title type='text'>Arduinoはじめます</title><content type='html'>研究が一段落して論文を書き終えてしまったためか、なんとも言えない虚脱感に襲われています。&lt;br /&gt;&lt;br /&gt;そこで、新たな生きがいを探すべく、&lt;a href="http://www.arduino.cc/"&gt;Arduino&lt;/a&gt; というおもちゃを買ってきました。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-XhtTvfnR2cI/TzO7ohCdWNI/AAAAAAAAAhc/gIyajEnIfaU/s1600/PICT0494.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="240" src="http://4.bp.blogspot.com/-XhtTvfnR2cI/TzO7ohCdWNI/AAAAAAAAAhc/gIyajEnIfaU/s320/PICT0494.JPG" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;入門用の『&lt;a href="http://www.switch-science.com/products/detail.php?product_id=181"&gt;Arduinoをはじめようキット&lt;/a&gt;』&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Arduino は、自分で回路を組み、マイコンにプログラムを送り込むと、とってもいいものができる素敵な装置なのだそうです（※ 厳密には装置の名称ではないのだけど）。&lt;br /&gt;&lt;br /&gt;電気電子な人じゃなくても電子工作ごっこができ、マイコンボード本体も中高生のお小遣いで買えてしまう程のお手頃価格。 これなら僕にもできるかな。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【開封の儀式】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-3iSUCIzI1jU/TzO8YPcCLBI/AAAAAAAAAhk/B6zODFkK6T4/s1600/PICT0495.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://1.bp.blogspot.com/-3iSUCIzI1jU/TzO8YPcCLBI/AAAAAAAAAhk/B6zODFkK6T4/s320/PICT0495.JPG" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;ちなみに内容物は以下の通りでした。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Arduino ボード（Arduino UNO）&lt;/li&gt;&lt;li&gt;ブレッドボード&lt;/li&gt;&lt;li&gt; ジャンパワイヤ&lt;/li&gt;&lt;li&gt;モメンタリ型プッシュボタンスイッチ（タクトスイッチ）&lt;/li&gt;&lt;li&gt;CdS セル（光センサ）&lt;/li&gt;&lt;li&gt;抵抗（10kΩ×2、1kΩ×3、330Ω×1）&lt;/li&gt;&lt;/ul&gt;電気回路や電子回路は専門外すぎてほとんど分かりません。ちょっとずつ慣れていこう……。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【タクトスイッチの実験】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;とりあえず基本という事で、スイッチを使って LED を点けたり消したりできるように回路を作ってみました。&lt;br /&gt;&lt;br /&gt;押しているときだけ光ります。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/9_fdzl_6lKw?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;ちなみに全く同一の回路でも、プログラムを書き換えると挙動を変える事が可能です。たとえば、スイッチから指を離しても LED の点灯状態が維持されるようにできます。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/EX0Y7iwX5Ac?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【CdS セルの実験】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;続いて、タクトスイッチを CdS セルに接ぎ換えてみる事に。 これは、光に反応して抵抗値が変動するおもしろいセンサだそうです。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/LbVzL2sOaVY?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;実は最初、どれだけ CdS セルに光を当てても何故か LED が点灯せず、『初期不良だな、ぷんぷん』と思ったら、本来 1kΩ の抵抗を使うべきところに 10kΩ の抵抗を使っていた事が発覚(・ω・lll) &lt;br /&gt;&lt;br /&gt;とんでもない凡ミスでした。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【3色 LED の実験】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;最後に、3色の LED を使ってグラデーションを作ろうとして失敗してみました。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/IcX-aC_z6Bk?rel=0" width="480"&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;HSV 表色系の H をじわじわ変動させたらグラデーションっぽい事ができるんじゃね？ と思ったのですが、なんか&lt;b&gt;残念なものができただけ&lt;/b&gt;でした。&lt;br /&gt;&lt;br /&gt;……というわけで、一応セットされている部品はひととおり使ってみましたが、これらを組み合わせて何ができるのかは全くの未知数です。&lt;br /&gt;&lt;br /&gt;慣れてきたら Processing とも組み合わせてみたいなぁ。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: right;"&gt;&lt;span style="font-size: x-small;"&gt;※ 今日の日記に使用した写真・動画はデジタルハリネズミで撮影しました。なんかレトロっぽいよね。&lt;/span&gt; &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-2766311720787943266?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/2766311720787943266/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/arduino.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/2766311720787943266'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/2766311720787943266'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/02/arduino.html' title='Arduinoはじめます'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-XhtTvfnR2cI/TzO7ohCdWNI/AAAAAAAAAhc/gIyajEnIfaU/s72-c/PICT0494.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-7657213317283439735</id><published>2012-01-28T18:20:00.000+09:00</published><updated>2012-01-28T18:20:14.017+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenCV'/><title type='text'>C++と仲直り計画： Cの構造体をスマートポインタで扱う （同時上映： ラムダ式もほんのちょっとかじる）</title><content type='html'>いきなりですが今日は &lt;a href="http://www.kmonos.net/alang/boost/classes/shared_ptr.html"&gt;boost::shared_ptr&lt;/a&gt; を構造体に対して用いるお話です。&lt;b&gt;ここまで読んで、『何を今さら』と思った方はブラウザをそっと閉じると幸せになれますよ。&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;あと、今日の日記は解説というよりは備忘録的な色が強いので、&lt;b&gt;僕と波長が合わないとなかなか内容に共感して頂けない&lt;/b&gt;かも知れません。そういう方もブラウザをそっｔ（ｒｙ&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【だらだら C 言語からスマート C++ へ】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;僕が C / C++ に苦手意識を抱く一つの理由がメモリ管理です。まず、動的に確保したメモリをどのタイミングで解放するか、あるいは解放のし忘れはないかを考えるのが煩わしい。次に、メモリ解放用のコードを書くのが面倒。&lt;br /&gt;&lt;br /&gt;百聞は一見に何とやらで、メモリを動的に確保 / 解放するコードの例を以下に示します。&lt;br /&gt;（ちなみに OpenCV を使っているのは個人的な趣味です）（ごめんね） &lt;br /&gt;&lt;br /&gt;&lt;b&gt;【OpenCV で画像領域を動的確保/解放（生ポインタ）】&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#include&amp;lt;iostream&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;opencv2/core/core.hpp&amp;gt;&lt;br /&gt;#include&amp;lt;opencv2/highgui/highgui.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifdef _DEBUG&lt;br /&gt;#pragma comment(lib, "opencv_core231d.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231d.lib")&lt;br /&gt;#else&lt;br /&gt;#pragma comment(lib, "opencv_core231.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231.lib")&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;    // 400 × 300 で画像を作成&lt;br /&gt;    IplImage* img = cvCreateImage(cvSize(400, 300), IPL_DEPTH_8U, 3);&lt;br /&gt;&lt;br /&gt;    // img を表示&lt;br /&gt;    cvShowImage("作った画像を見せびらかすウィンドウ", img);&lt;br /&gt;    cv::waitKey(0);&lt;br /&gt;&lt;br /&gt;    // img を解放&lt;br /&gt;    cvReleaseImage(&amp;amp;img);&lt;br /&gt;    img = NULL;&lt;br /&gt;    &lt;br /&gt;    cout &amp;lt;&amp;lt; "メモリを適当に解放しました\n";&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;念のため、上のコードを簡単に説明すると、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cvCreateImage&lt;/span&gt; で動的にメモリを確保して、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cvReleaseImage&lt;/span&gt; で解放しています。これ、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;IplImage&lt;/span&gt; 型の変数が 1 つだけしかないからよいものの、変数の数だけ解放用コードが並ぶのはさぞ目障りなものでしょう。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;ところが、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boost&lt;/span&gt; というライブラリには、動的に生成されたオブジェクトを自動的にデストラクトしてくれる便利な&lt;b&gt;スマートポインタ&lt;/b&gt;が用意されているのです。&lt;br /&gt;&lt;br /&gt;スマートポインタにもいくつか種類がありますが、今回は最も汎用性が高い（と個人的に考えている）&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boost::shared_ptr&lt;/span&gt; を使ってみましょう。 &lt;br /&gt;&lt;br /&gt;まず、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boost::shared_ptr&amp;lt;iplimage&amp;gt;&lt;/span&gt; 用のカスタムデリータを&lt;a href="http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88"&gt;ファンクタ&lt;/a&gt;の形で定義します（ここでは &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ImageReleaser&lt;/span&gt; と名付けた）。やっている事は、単に関数呼び出し演算子をオーバーロードして、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cvReleaseImage&lt;/span&gt; を実行しているだけです。簡単ですね。&lt;br /&gt;&lt;br /&gt;次に、このデリータオブジェクトを &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boost::shared_ptr&amp;lt;iplimage&amp;gt;&lt;/span&gt; のコンストラクタ（第2引数）に渡してやります。すると、たったこれだけで生成されたオブジェクトが自動解放されるようになります。やったね！&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【&lt;/b&gt;&lt;b&gt;OpenCV で画像領域を動的確保/解放（&lt;/b&gt;&lt;b&gt;スマートポインタ）】&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#include&amp;lt;iostream&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;opencv2/core/core.hpp&amp;gt;&lt;br /&gt;#include&amp;lt;opencv2/highgui/highgui.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;boost/shared_ptr.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifdef _DEBUG&lt;br /&gt;#pragma comment(lib, "opencv_core231d.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231d.lib")&lt;br /&gt;#else&lt;br /&gt;#pragma comment(lib, "opencv_core231.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231.lib")&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;// 解放用のファンクタ&lt;br /&gt;class ImageReleaser {&lt;br /&gt;public:&lt;br /&gt;    void operator()(IplImage* img) {&lt;br /&gt;        cvReleaseImage(&amp;amp;img);&lt;br /&gt;        cout &amp;lt;&amp;lt; "メモリを適当に解放しました\n";&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;    &lt;br /&gt;    typedef boost::shared_ptr&amp;lt;IplImage&amp;gt; ImgPtr;&lt;br /&gt;    &lt;br /&gt;    // 400 × 300 で画像を作成&lt;br /&gt;    ImgPtr img(cvCreateImage(cvSize(400, 300), IPL_DEPTH_8U, 3), &lt;br /&gt;               ImageReleaser());    // ←解放用ファンクタを指定&lt;br /&gt;&lt;br /&gt;    // img を表示&lt;br /&gt;    cvShowImage("作った画像を見せびらかすウィンドウ", img.get());&lt;br /&gt;    cv::waitKey(0);&lt;br /&gt;&lt;br /&gt;    // img は自動的に解放される&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;おー、できた。でも&lt;b&gt;まだちょっとアレ&lt;/b&gt;ですね。&lt;br /&gt;&lt;br /&gt;プログラム中で一度しか使われないデリータごときのためにわざわざファンクタを定義するのは大仰です。それに、極めて限定的な用途の小さなクラスがソース内に散らかるのはどうも汚く思えます。 &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;ここで、先ほどの実装の改善すべき点を洗ってみましょう。&lt;br /&gt;&lt;br /&gt;デリータは基本的に一度きりの使い捨てなので名前など要らないでしょうし、必要な時・必要な場所に実装を書けば「小さなクラスがソース内に散らかる」といった気持ち悪さもなくなります。&lt;br /&gt;&lt;br /&gt;これを実現するのが &lt;a href="http://www.kmonos.net/alang/boost/classes/lambda.html"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;boost::lambda&lt;/span&gt;&lt;/a&gt; です。そう、&lt;b&gt;みんな大好きラムダ式&lt;/b&gt;のライブラリですね。ここではあまりマニアックな使い方はせず、単に邪魔なファンクタをこの世から消し去る目的のためだけに導入しました。&lt;br /&gt;&lt;br /&gt;先ほどのソースを書き換えると以下のようになります。&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;【&lt;/b&gt;&lt;/span&gt;&lt;b&gt;OpenCV で画像領域を動的確保/解放（&lt;/b&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;スマートポインタ + ラムダ式）】&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;#include&amp;lt;iostream&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;opencv2/core/core.hpp&amp;gt;&lt;br /&gt;#include&amp;lt;opencv2/highgui/highgui.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;boost/shared_ptr.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#include&amp;lt;boost/lambda/bind.hpp&amp;gt;&lt;br /&gt;#include&amp;lt;boost/lambda/lambda.hpp&amp;gt;&lt;br /&gt;&lt;br /&gt;#ifdef _DEBUG&lt;br /&gt;#pragma comment(lib, "opencv_core231d.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231d.lib")&lt;br /&gt;#else&lt;br /&gt;#pragma comment(lib, "opencv_core231.lib")&lt;br /&gt;#pragma comment(lib, "opencv_highgui231.lib")&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;int main() {&lt;br /&gt;    using namespace boost::lambda;&lt;br /&gt;    typedef boost::shared_ptr&amp;lt;IplImage&amp;gt; ImgPtr;&lt;br /&gt;    &lt;br /&gt;    // 400 × 300 で画像を作成&lt;br /&gt;    ImgPtr img(cvCreateImage(cvSize(400, 300), IPL_DEPTH_8U, 3),&lt;br /&gt;        (bind(cvReleaseImage, &amp;amp;_1),&lt;br /&gt;            cout &amp;lt;&amp;lt; constant("メモリを適当に解放しました\n")));&lt;br /&gt;&lt;br /&gt;    // img を表示&lt;br /&gt;    cvShowImage("作った画像を見せびらかすウィンドウ", img.get());&lt;br /&gt;    cv::waitKey(0);&lt;br /&gt;&lt;br /&gt;    // img は自動的に解放される&lt;br /&gt;    return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;この例のほかにも、ラムダ式を使えばソート関数にカスタムコンパレータを与えるコードをスマートに書けるでしょう（&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/11/c-3-delaunay.html"&gt;3次元 Delaunay 分割&lt;/a&gt;では boost レスで作ったので、わざわざコンパレータをファンクタにしていた）。&lt;br /&gt;&lt;br /&gt;ちなみに &lt;b&gt;OpenCV には &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;cv::Ptr&lt;/span&gt; というすごい便利なスマートポインタが用意されています&lt;/b&gt;。というか、僕は boost を入れる前にもっぱらこちらを使っていました。&lt;br /&gt;&lt;br /&gt;これは &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;IplImage&lt;/span&gt; などのカスタムデリータが自動的に適用されるので、それを使えばひとまず上記の議論はことごとく不要になるのですが、たとえば悪名高い &lt;a href="http://ja.wikipedia.org/wiki/Component_Object_Model"&gt;COM オブジェクト&lt;/a&gt;とか、C ベースで書かれた過去の重要な資産とかを安全に扱いたい場合には知っておくと幸せになれるような気がします。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-7657213317283439735?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/7657213317283439735/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/c-c.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7657213317283439735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7657213317283439735'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/c-c.html' title='C++と仲直り計画： Cの構造体をスマートポインタで扱う （同時上映： ラムダ式もほんのちょっとかじる）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-6342667745915444475</id><published>2012-01-25T16:40:00.000+09:00</published><updated>2012-01-25T16:40:31.042+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><title type='text'>Processing.jsで作った「ひこうき」を高速化してみた</title><content type='html'>以前、Processing.js で『3D ひこうき』を作りました。&lt;br /&gt;&lt;br /&gt;これは P3D モードのサポートがアレな &lt;b&gt;Processing.js 環境でも、なんとか 3D っぽいグラフィックを出そう&lt;/b&gt;というチャレンジだったのですが、速度的な意味で非常に残念なものができあがっただけでした。&lt;br /&gt;&lt;br /&gt;今回は、ループの中でオブジェクトを new しないように心がけたり、描画関数の呼び出しを削減したりといろいろ高速化を試みました（→ &lt;a href="http://www.openprocessing.org/visuals/?visualID=50105"&gt;OpenProcessing&lt;/a&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.openprocessing.org/visuals/?visualID=50105"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-952jWcSjaJ0/Tx-vRDAt7mI/AAAAAAAAAhU/jd24tjBSPfo/s320/20120125.PNG" width="213" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;うーん……。&lt;br /&gt;&lt;br /&gt;僕の iPhone 3GS だとまだカクカクしています。&lt;br /&gt;&lt;br /&gt;もうちょっと形状を単純にするとかなり速く動く気がするのですが……。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-6342667745915444475?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/6342667745915444475/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/processingjs.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6342667745915444475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6342667745915444475'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/processingjs.html' title='Processing.jsで作った「ひこうき」を高速化してみた'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-952jWcSjaJ0/Tx-vRDAt7mI/AAAAAAAAAhU/jd24tjBSPfo/s72-c/20120125.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-4429852504561037429</id><published>2012-01-23T17:30:00.000+09:00</published><updated>2012-01-23T17:30:11.488+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Processingでてきとう3次元バンプマッピング</title><content type='html'>学位論文の方が一段落しそうなので、空き時間を使ってバンプマッピング（のようなもの）を実装してみました。ちょっとしたリハビリです。&lt;br /&gt;&lt;br /&gt;バンプマッピングとは、ポリゴン上の凹凸を擬似的に再現する技術で、テクスチャマッピングでは表現できない質感表現を実現できます。&lt;br /&gt;&lt;br /&gt;実際に動作するデモは &lt;a href="http://www.openprocessing.org/visuals/?visualID=49878"&gt;OpenProcessing&lt;/a&gt; で公開していますので、是非ご覧ください。 &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.openprocessing.org/visuals/?visualID=49878"&gt;&lt;img border="0" height="200" src="http://1.bp.blogspot.com/-YXmocKT4s34/Tx0ZMwKpN0I/AAAAAAAAAhM/twxB1hH_0ao/s200/bumpMappingLargeScreenShot.png" width="199" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-4429852504561037429?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/4429852504561037429/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/processing3.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4429852504561037429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4429852504561037429'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/processing3.html' title='Processingでてきとう3次元バンプマッピング'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-YXmocKT4s34/Tx0ZMwKpN0I/AAAAAAAAAhM/twxB1hH_0ao/s72-c/bumpMappingLargeScreenShot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-4255101655235987238</id><published>2012-01-19T16:04:00.005+09:00</published><updated>2012-01-20T09:20:04.217+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='数学'/><category scheme='http://www.blogger.com/atom/ns#' term='MathJax'/><title type='text'>三次元・直線・最小自乗法（vs. ぷで）</title><content type='html'>&lt;a href="http://pudexp.blog.fc2.com/blog-entry-14.html"&gt;ぷでさんが面白い記事を書いていた&lt;/a&gt;ので、僕も同じテーマで書いてみる事にしたよ。&lt;br /&gt;&lt;br /&gt;「3次元における直線 \(L_{i}\: \left( i = 0,1,\cdots, n-1 \right) \) の尤もらしい交点 \( \vect{p} \) を最小自乗法で求める」&lt;br /&gt;&lt;br /&gt;これは、位置と姿勢が既知である多視点画像における対応点3次元位置を推定する際にしばしば用いられる議論だ（下図参照）。3次元空間中では一般的に複数の直線の交点は一意には定まらず、いわゆる “ねじれ” の関係になる。その際、できるだけ誤差を抑えるために、各直線が最接近する点を採用する事が望ましい。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-010V90wvrl4/Txe_p2_VOYI/AAAAAAAAAhE/OrC-BlmB8-s/s1600/20120119.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="222" src="http://3.bp.blogspot.com/-010V90wvrl4/Txe_p2_VOYI/AAAAAAAAAhE/OrC-BlmB8-s/s400/20120119.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;複数カメラの視線を基に被写体の3次元座標を得るイメージ．&lt;br /&gt;カメラ光学中心から各対応点を貫く直線の交点が3次元位置となる．&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;直線 \(L_{i}\) が通る点を \( \vect{b}_{i} \) 、傾き（方向）を \( \vect{d}_{i} \) とすると、\(L_{i}\) 上の任意の点はパラメータ \(t_{i}\) を用いて&lt;br /&gt;\begin{align}&lt;br /&gt;L_{i}(t_{i}) = t_{i}\vect{d}_{i} + \vect{b}_{i}&lt;br /&gt;\end{align}と表す事ができる。&lt;br /&gt;&lt;br /&gt;また、直線上の座標 \(L_{i}(t_{i})\) と点 \(\vect{p}\) の距離の自乗 \(f_{i}(t_{i},\,\vect{p})\) は以下のようになる。&lt;br /&gt;\begin{align}&lt;br /&gt;f_{i}(t_{i},\,\vect{p}) &amp;amp;= \left| L_{i}(t_{i}) - \vect{p} \right|^{2} \\&lt;br /&gt;&amp;amp;= \left| t_{i}\vect{d}_{i} + \vect{b}_{i} - \vect{p} \right|^{2}&lt;br /&gt;\end{align}&lt;br /&gt;&lt;br /&gt;ここで、すべての直線上の点 \(L_{i}(t_{i})\) から \(\vect{p}\) までの距離自乗の総和を&lt;br /&gt;\begin{align}&lt;br /&gt;&amp;amp; F(t_{0},\, \cdots,\, t_{i-1},\, t_{i},\, t_{i+1},\, \cdots, \, t_{n-1}, \, \vect{p}) \\&lt;br /&gt;=&amp;amp; \frac{1}{2} \sum_{i=0}^{n-1} \left| t_{i} \vect{d}_{i} + \vect{b}_{i} - \vect{p} \right|^{2} \\&lt;br /&gt;=&amp;amp; \frac{1}{2} \sum_{i=0}^{n-1} \left( t_{i} \vect{d}_{i} + \vect{b}_{i} - \vect{p} \right)^{t} \left( t_{i} \vect{d}_{i} + \vect{b}_{i} - \vect{p} \right)&lt;br /&gt;\end{align}と置き、関数 \(F(t_{0},\, \cdots,\, t_{i-1},\, t_{i},\, t_{i+1},\, \cdots, \, t_{n-1}, \, \vect{p})\) の最小化問題を解く。&lt;br /&gt;&lt;br /&gt;まず、上式を各 \( t_{i} \) で偏微分して \( 0 \) と置き、&lt;br /&gt;\begin{align}&lt;br /&gt;&amp;amp; \frac{\partial }{\partial t_{i}} F(t_{0},\, \cdots,\, t_{i-1},\, t_{i},\, t_{i+1},\, \cdots, \, t_{n-1}, \, \vect{p}) \\&lt;br /&gt;=&amp;amp; \frac{\partial}{\partial t_{i}} \left[ \frac{1}{2} \sum_{i=0}^{n-1} \left( t_{i} \vect{d}_{i} + \left( \vect{b}_{i} - \vect{p} \right) \right)^{t} \left( t_{i} \vect{d}_{i} + \left( \vect{b}_{i} - \vect{p} \right) \right) \right] \\&lt;br /&gt;=&amp;amp; \frac{\partial}{\partial t_{i}} \left[ \frac{1}{2} \sum_{i=0}^{n-1} t_{i}^{2}\vect{d}_{i}^{t}\vect{d}_{i} + \sum_{i=0}^{n-1} t_{i} \vect{d}_{i}^{t} \left( \vect{b}_{i} - \vect{p} \right) + \frac{1}{2} \sum_{i=0}^{n-1} \left( \vect{b}_{i} - \vect{p} \right)^{t} \left( \vect{b}_{i} - \vect{p} \right) \right] \\&lt;br /&gt;=&amp;amp; t_{i}\vect{d}_{i}^{t}\vect{d}_{i} + \vect{d}_{i}^{t} \left( \vect{b}_{i} - \vect{p} \right) = 0&lt;br /&gt;\end{align}&lt;br /&gt;次に \( \vect{p} \) で偏微分して同様に \( 0 \) と置くと&lt;br /&gt;\begin{align}&lt;br /&gt;&amp;amp;\frac{\partial }{\partial \vect{p}} F(t_{0},\, \cdots,\, t_{i-1},\, t_{i},\, t_{i+1},\, \cdots, \, t_{n-1}, \, \vect{p}) \\&lt;br /&gt;=&amp;amp; \frac{\partial}{\partial \vect{p}} \left[ \frac{1}{2} \sum_{i=0}^{n-1} \left( \left( t_{i} \vect{d}_{i} + \vect{b}_{i} \right) - \vect{p}  \right)^{t} \left( \left( t_{i} \vect{d}_{i} + \vect{b}_{i} \right) - \vect{p} \right) \right] \\&lt;br /&gt;=&amp;amp; \frac{\partial}{\partial \vect{p}} \left[ \frac{1}{2} \sum_{i=0}^{n-1} \left( t_{i} \vect{d}_{i} + \vect{b}_{i}  \right)^{t} \left( t_{i} \vect{d}_{i} + \vect{b}_{i}  \right) - \sum_{i=0}^{n-1} \left( t_{i} \vect{d}_{i} + \vect{b}_{i} \right)^{t} \vect{p} + \frac{1}{2}\sum_{i=0}^{n-1} \vect{p}^{t}\vect{p} \right] \\&lt;br /&gt;=&amp;amp; -\sum_{i=0}^{n-1} \left( t_{i} \vect{d}_{i} + \vect{b}_{i} \right) + n\vect{p} = \vect{0}&lt;br /&gt;\end{align}が得られる。&lt;br /&gt;&lt;br /&gt;これらをまとめると、未知数 \( t_{0},\, \cdots,\, t_{n-1},\, \vect{p} \) に関する次の方程式になる。&lt;br /&gt;\begin{align}&lt;br /&gt;\begin{pmatrix}&lt;br /&gt;\vect{d}_{0}^{t}\vect{d}_{0} &amp;amp; 0      &amp;amp; \cdots &amp;amp; 0                                &amp;amp; -\vect{d}_{0}^{t}   \\&lt;br /&gt;0                            &amp;amp; \vect{d}_{1}^{t}\vect{d}_{1} &amp;amp; \ddots &amp;amp; \vdots     &amp;amp; -\vect{d}_{1}^{t}   \\&lt;br /&gt;\vdots                       &amp;amp; \ddots &amp;amp; \ddots &amp;amp; 0                                &amp;amp;  \vdots             \\&lt;br /&gt;0                            &amp;amp; \cdots &amp;amp;   0    &amp;amp; \vect{d}_{n-1}^{t}\vect{d}_{n-1} &amp;amp; -\vect{d}_{n-1}^{t} \\&lt;br /&gt;-\vect{d}_{0}                &amp;amp; -\vect{d}_{1} &amp;amp; \cdots &amp;amp; -\vect{d}_{n-1} &amp;amp; \vect{E}_{3,\,3}&lt;br /&gt;\end{pmatrix}&lt;br /&gt;\begin{pmatrix}&lt;br /&gt;t_{0} \\&lt;br /&gt;t_{1} \\&lt;br /&gt;\vdots \\&lt;br /&gt;t_{n-1} \\&lt;br /&gt;\vect{p}&lt;br /&gt;\end{pmatrix} = &lt;br /&gt;\begin{pmatrix}&lt;br /&gt;-\vect{d}_{0}^{t} \vect{b}_{0} \\&lt;br /&gt;-\vect{d}_{1}^{t} \vect{b}_{1} \\&lt;br /&gt;\vdots \\&lt;br /&gt;-\vect{d}_{n-1}^{t} \vect{b}_{n-1} \\&lt;br /&gt;\sum_{i=0}^{n-1}\vect{b}_{i}&lt;br /&gt;\end{pmatrix}&lt;br /&gt;\end{align}&lt;br /&gt;&lt;br /&gt;ここで、\( \vect{E}_{3,\,3} \) は 3 階の単位行列である。上記の方程式を LU 分解か何かを使って解けばたぶん最急降下法を用いなくても \( \vect{p} \) を得る事ができる（…んじゃないかな）。&lt;br /&gt;&lt;br /&gt;ちなみに boost を用いた LU 分解の実装例は&lt;a href="http://www.page.sannet.ne.jp/d_takahashi/boost/ublas/#SEC15"&gt;このへん&lt;/a&gt;。OpenCV による実装例は&lt;a href="http://opencv.jp/cookbook/opencv_linalg.html#id23"&gt;このへん&lt;/a&gt;。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-4255101655235987238?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/4255101655235987238/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/vs.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4255101655235987238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4255101655235987238'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/vs.html' title='三次元・直線・最小自乗法（vs. ぷで）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-010V90wvrl4/Txe_p2_VOYI/AAAAAAAAAhE/OrC-BlmB8-s/s72-c/20120119.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-8143114970195132501</id><published>2012-01-08T21:28:00.000+09:00</published><updated>2012-01-08T21:28:33.548+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='身辺雑記'/><title type='text'>素晴らしき日々へ</title><content type='html'>カルボナーラを食べると&lt;a href="http://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AB%E3%83%9C%E3%83%8B%E3%83%AB%E5%9F%BA"&gt;カルボニル基&lt;/a&gt;という言葉を思い出す。&lt;br /&gt;&lt;br /&gt;ボロネーゼを食べると、&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%89%E3%83%AD%E3%83%8D%E3%83%BC%E5%9B%B3"&gt;ドロネー図&lt;/a&gt;を思い出す（ついでに&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%AD%E3%83%8E%E3%82%A4%E5%9B%B3"&gt;ボロノイ図&lt;/a&gt;も思い出す）。&lt;br /&gt;&lt;br /&gt;普段からこのように突飛な方向へと思考回路がスイッチされてしまうため、話し相手はそれなりに苦慮しているらしい。申し訳ない。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;今日は親友の祥月命日だ。&lt;br /&gt;&lt;br /&gt;本当は一緒に連れて逝って欲しかったのだが、僕は生きることになった。&lt;br /&gt;&lt;br /&gt;死ぬというのはどういう事なのだろう。僕はあまり宗教への頓着がないせいか、恐らく『生まれる前と同じ世界』に還るのだろうと考えている。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;最近の気分転換は専らマリオだ。&lt;br /&gt;&lt;br /&gt;小さい頃から高いところがダメで、たとえゲームであっても足が竦む。操作をミスしてマリオが高所から落下する一瞬、心臓がドキっとする。&lt;br /&gt;&lt;br /&gt;まぁマリオにしてみても、こんなヘタクソにプレイされているせいで何度も死ぬ羽目になって、さぞかしいい迷惑だろう。 &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;『はじめてのおつかい』は微笑ましい。&lt;br /&gt;&lt;br /&gt;僕は、はじめておつかいをした日の事も、はじめて補助なしの自転車に乗れるようになった日の事も、よく覚えている。&lt;br /&gt;&lt;br /&gt;ついでに、はじめて C 言語で &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;printf()&lt;/span&gt; 関数を使った日の事も。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-8143114970195132501?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/8143114970195132501/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post_08.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8143114970195132501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/8143114970195132501'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post_08.html' title='素晴らしき日々へ'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-4553810013388782485</id><published>2012-01-07T19:07:00.000+09:00</published><updated>2012-01-07T19:07:53.616+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='正規表現'/><title type='text'>相対参照を適当に考慮したHTMLファイルの移動</title><content type='html'>&lt;a href="http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post_02.html"&gt;1月2日&lt;/a&gt;の続きで、自分用&lt;b&gt;ひとこと日記支援システム&lt;/b&gt;（？）的なものをだらだら作っています。&lt;br /&gt;&lt;br /&gt;ちなみに前回は、「HTML ファイルを生成する仕組みを作ろう」という、なんともややこしい話をしていたのでした。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ ちなみにあの後、&lt;a href="http://twitter.com/#%21/laprasdrum"&gt;くろねこ&lt;/a&gt;さんからも非常に有益なアドバイスを頂きました。記事になるところまでできたらここに書きたいと思います。&lt;/span&gt;&lt;/blockquote&gt;&amp;nbsp;せっかくですから、ひとこと日記を単に書き溜めるだけでなく、月別のアーカイブページなども生成できれば、情報が整理できて閲覧性も高まるのではないかと考えました（というか、至って平凡な思いつきですが）。&lt;br /&gt;&lt;br /&gt;さて、ここで問題になるのが HTML に記述された相対参照の扱いです。&lt;br /&gt;&lt;br /&gt;もしも HTML ファイル中に &lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;lt;img src="baka/aho/img.png" /&amp;gt;&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/blockquote&gt;とか、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;lt;a href="../../hoge/piyo.html" /&amp;gt;&lt;/span&gt;&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;みたいな相対参照が仕組まれていた場合、何も考えずに階層の異なる別ディレクトリ移動・複製すると、リンク切れを起こしてしまうのです。&lt;br /&gt;&lt;br /&gt;どうにかしたいですよね。 &lt;br /&gt;&lt;br /&gt;しかし、この問題を完全に解決しようとすると果てしなく面倒なので、&lt;b&gt;実用上それほど問題が出ないレベルで適当に対処しよう&lt;/b&gt;というのが本日のテーマです。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【テキストファイルの文字コード判定】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;相対リンク云々以前に、テキストファイルを扱う際にまず立ちはだかる第一の関門が文字コードです。テキストに使用される文字コードを適切に判別しないと日本語が化けてしまうのですが、使用されている文字コードを厳密に判断する完全な手段が存在しないのです（たとえば、半角カナのみを特定の配列で使用した場合など）。&lt;br /&gt;&lt;br /&gt;ここではとりあえずテキストを読んでみて&lt;sup&gt;※&lt;/sup&gt;、それを基に文字コードを判定し、それから改めて文字コードを指定してファイルを読みなおす&lt;sup&gt;※※&lt;/sup&gt;…という無駄な事をやります。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ テキストの量が多いほど、精度よく文字コードを判定できるため。&lt;br /&gt;※※ 初回の読み込みでは改行文字を無視していましたが、2回目の読み込みでは考慮します。&lt;/span&gt; &lt;/blockquote&gt;&lt;br /&gt;文字コードを判定するメソッドに関しては、&lt;a href="http://d.hatena.ne.jp/cero-t/20100204/1265302329"&gt;[Javaレビュー]レビューで鍛えるJavaコーディング力 その７（文字コードチェック）&lt;/a&gt;に有用なサンプルがありましたので、そちらを参考にしました。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:nogutter" name="code"&gt;String getCharSet(StringBuffer text) &lt;br /&gt;    throws UnsupportedEncodingException {&lt;br /&gt;  String[] charCodeList = {"SJIS", "EUC-JP", "UTF-8"};&lt;br /&gt;  String str = text.toString();&lt;br /&gt;  for(String code : charCodeList) {&lt;br /&gt;    byte[] bytes = str.getBytes(code);&lt;br /&gt;    if(str.equals(new String(bytes, code))) return code;&lt;br /&gt;  }&lt;br /&gt;  throw new UnsupportedEncodingException();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;今回は割とメジャーな 3 つの文字コード（Shift JIS、EUC-JP、UTF-8）をサポートします。&lt;br /&gt;&lt;br /&gt;上記の &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;getCharSet()&lt;/span&gt; メソッドを用いて、文字コードを考慮した HTML ファイルを読み込む処理を書くと、以下のようになります（コードでは省略していますが、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;IOException&lt;/span&gt; が投げられる可能性があります）。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:nogutter" name="code"&gt;// HTMLファイルを取り敢えず読み込み&lt;br /&gt;File file = new File("hoge.html");&lt;br /&gt;if(!file.exists()) throw new FileNotFoundException();&lt;br /&gt;&lt;br /&gt;BufferedReader br   = new BufferedReader(new FileReader(file));&lt;br /&gt;String         line = br.readLine();&lt;br /&gt;StringBuffer   src  = new StringBuffer("");&lt;br /&gt;&lt;br /&gt;while(line != null) {&lt;br /&gt;  src.append(line);&lt;br /&gt;  line = br.readLine();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// 文字コードを取得&lt;br /&gt;String charset;&lt;br /&gt;try {&lt;br /&gt;  charset = getCharSet(src);&lt;br /&gt;} catch(UnsupportedEncodingException ex) { &lt;br /&gt;  charset = "UTF-8"; &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// 文字コードを指定して再度読み込み&lt;br /&gt;br = new BufferedReader(&lt;br /&gt;       new InputStreamReader(&lt;br /&gt;         new FileInputStream(file), charset));&lt;br /&gt;&lt;br /&gt;line = br.readLine();&lt;br /&gt;src.delete(0, src.length());&lt;br /&gt;while(line != null) {&lt;br /&gt;  src.append(line + "\n");&lt;br /&gt;  line = br.readLine();&lt;br /&gt;}&lt;br /&gt;System.out.println(src);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これを走らせると、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;StringBuffer&lt;/span&gt; オブジェクト &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;src&lt;/span&gt; に HTML ファイルが読み込まれます。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【HTML から相対参照の抜き出し】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;次に、先ほど読み込んだ HTML から相対パスを抽出する処理を考えます。&lt;br /&gt;&lt;br /&gt;これも厳密に考えようとするとどうにも面倒なので、&lt;a href="http://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE"&gt;正規表現&lt;/a&gt;を用いて簡易的に抜く事にしましょう。&lt;br /&gt;&lt;br /&gt;とりあえず、img タグなどの src 属性、a タグの href 属性、そして object タグの data 属性に対応するために、以下の正規表現を書きました。&lt;br /&gt;&lt;blockquote class="tr_bq" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="color: red;"&gt;((?:src|href|data)[\\s]*=[\\s]*\")&lt;/span&gt;&lt;span style="color: orange;"&gt;([^\"]+)&lt;/span&gt;&lt;span style="color: yellow;"&gt;(\")&lt;/span&gt;&lt;/blockquote&gt;この表現は、上記の各属性に対して &lt;span style="color: red;"&gt;href="&lt;/span&gt;&lt;span style="color: orange;"&gt;../../hoge/piyo.html&lt;/span&gt;&lt;span style="color: yellow;"&gt;"&lt;/span&gt; のようにマッチします。&lt;br /&gt;&lt;br /&gt;ただし、このままでは絶対パスで表記された URI も拾ってしまうため、さらに&lt;b&gt;除外用のパターン&lt;/b&gt;が必要です。&lt;br /&gt;&lt;blockquote class="tr_bq" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="color: lime;"&gt;^\\/&lt;/span&gt;|&lt;span style="color: cyan;"&gt;^(?:https?|ftp):\\/\\/&lt;/span&gt;&lt;/blockquote&gt;これは、&lt;span style="color: cyan;"&gt;http://&lt;/span&gt;&lt;span style="color: #666666;"&gt;www.baka.com/hoge.html&lt;/span&gt; という文字列のうち、水色で着色した部分にマッチします。また、 &lt;span style="color: lime;"&gt;/&lt;/span&gt;&lt;span style="color: #666666;"&gt;script/piyo.js&lt;/span&gt; のような、サーバ名を省略した絶対参照にもマッチします。&lt;br /&gt;&lt;br /&gt;本来は、一つのパターンで的確に相対パスのみを抜く事ができればよいのですが、先ほど紹介した&lt;br /&gt;&lt;ul&gt;&lt;li&gt;HTML からパスを抜き出すパターン&lt;/li&gt;&lt;li&gt;抜き出したパスから絶対参照を除外するパターン&lt;/li&gt;&lt;/ul&gt;―― のいずれもが簡易的なものであり、完全に意図した通りには機能しない事&lt;sup&gt;※&lt;/sup&gt;を考えると、2 段階に分割しておいた方が後々の修正もラクになる気がします。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ 不完全というのは聞き捨てならんかも知れませんが、よしんば完全に機能するものを作っても、HTML の仕様変更に追従できなければ意味がありません。修正のしやすさを優先しました。&lt;/span&gt; &lt;/blockquote&gt;加えて、HTML 特有の問題として、&lt;b&gt;ページ内リンク&lt;/b&gt;の扱いを適切に処理する必要があります。たとえば、&lt;span style="color: orange;"&gt;foo.html&lt;/span&gt; 内に、以下のようなタグが含まれていたとしましょう。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&amp;lt;a href="&lt;span style="color: yellow;"&gt;#20120107&lt;/span&gt;" /&amp;gt; &lt;/blockquote&gt;この場合は、先ほどの正規表現で抜き出したパス（&lt;span style="color: yellow;"&gt;#20120107&lt;/span&gt;）の前方に、当該 HTML ファイル名（&lt;span style="color: orange;"&gt;foo.html&lt;/span&gt;）を挿し込み、パス情報を &lt;span style="color: orange;"&gt;foo.html&lt;/span&gt;&lt;span style="color: yellow;"&gt;#20120107&lt;/span&gt; に修正します。&lt;br /&gt;&lt;br /&gt;なお、相対パスを一旦絶対パス表記に直しておくと、後々都合がよくなります。&lt;br /&gt;&lt;br /&gt;ここで、もしも foo.html の所在が判っていれば、参照先の絶対パス表記は以下のように得る事ができます。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: lime;"&gt;[foo.html の親ディレクトリの絶対パス]&lt;/span&gt; + &lt;span style="color: cyan;"&gt;[区切り文字]&lt;/span&gt; + &lt;span style="color: #6fa8dc;"&gt;[foo.html 内の相対パス]&lt;/span&gt;&lt;/blockquote&gt;つまり、foo.html が&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&amp;nbsp;&lt;span style="color: lime;"&gt;C:\Users\Tercel\Documents\html&lt;/span&gt;\foo.html&lt;/blockquote&gt;―― に存在して、そこから &lt;span style="color: #6fa8dc;"&gt;..\img\bg.png&lt;/span&gt; を相対参照している場合、bg.png の絶対パスは&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: lime;"&gt;C:\Users\Tercel\Documents\html&lt;/span&gt;&lt;span style="color: cyan;"&gt;\&lt;/span&gt;&lt;span style="color: #6fa8dc;"&gt;..\img\bg.png&lt;/span&gt;&lt;/blockquote&gt;―― となります。"&lt;span style="color: lime;"&gt;html&lt;/span&gt;&lt;span style="color: cyan;"&gt;\&lt;/span&gt;&lt;span style="color: #6fa8dc;"&gt;..\&lt;/span&gt;" のあたりがちょっと冗長ですが、これは一時的なものですのでこのままにしておいても構いません。&lt;br /&gt;&lt;br /&gt;ソースから相対パスを抜き出して、新たな基準ディレクトリからのパスに変換する Java コードは以下のようになります。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:nogutter" name="code"&gt;// ========================================&lt;br /&gt;&lt;br /&gt;// HTMLテキストからURIパスを抽出するための正規表現&lt;br /&gt;final String pathRegex = "((?:src|href|data)[\\s]*=[\\s]*\")([^\"]+)(\")";&lt;br /&gt;&lt;br /&gt;// URIが絶対パスかどうかを（割と適当に）調べる正規表現&lt;br /&gt;final String absoluteURIRegex = "^\\/|^(?:https?|ftp):\\/\\/";&lt;br /&gt;&lt;br /&gt;final Matcher pathMatcher = Pattern.compile(pathRegex, &lt;br /&gt;               Pattern.CASE_INSENSITIVE).matcher(src);&lt;br /&gt;final Pattern absoluteURLPattern = Pattern.compile(absoluteURIRegex);&lt;br /&gt;&lt;br /&gt;// HTMLファイルの相対パスを抽出&lt;br /&gt;while(pathMatcher.find()) {&lt;br /&gt;            &lt;br /&gt; //String header = pathMatcher.group(1);&lt;br /&gt; String path  = pathMatcher.group(2);&lt;br /&gt; //String footer = pathMatcher.group(3);&lt;br /&gt; if(!absoluteURLPattern.matcher(path).find()) {&lt;br /&gt;  // もし相対パスだったら出力&lt;br /&gt;  System.out.print(path);  &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ちなみに上記コードには、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;pathMatcher.group(&lt;i style="color: #c27ba0;"&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt;);&lt;/span&gt; という意味深なコードがありますが、これは正規表現中の丸括弧 &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;(&lt;/span&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;)&lt;/span&gt; でグループ化された要素を取得するための構文です。&lt;br /&gt;&lt;br /&gt;たとえば先ほどの正規表現 ――&lt;br /&gt;&lt;blockquote class="tr_bq" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="color: red;"&gt;((?:src|href|data)[\\s]*=[\\s]*\")&lt;/span&gt;&lt;span style="color: orange;"&gt;([^\"]+)&lt;/span&gt;&lt;span style="color: yellow;"&gt;(\")&lt;/span&gt;&lt;/blockquote&gt;―― 全体にマッチした文字列のうち、&lt;span style="color: red; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;((?:src|href|data)[\\s]*=[\\s]*\")&lt;/span&gt; にマッチする部分文字列がグループ1、&lt;span style="color: orange; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;([^\"]+)&lt;/span&gt; にマッチする部分文字列がグループ2、残りの &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;(\")&lt;/span&gt; がグループ3となります。&lt;br /&gt;&lt;br /&gt;この性質を使によって、以下のようにパスのみを巧く加工する事ができるのです。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;String newStr = &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="color: red; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;matcher.group(1&lt;/span&gt;&lt;i style="color: red; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/i&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="color: red;"&gt;)&lt;/span&gt; + &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #f9cb9c;"&gt;hoge(&lt;span style="color: orange;"&gt;matcher.group(2)&lt;/span&gt;)&lt;/span&gt; +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: yellow;"&gt;matcher.group(3)&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;あとは、抽出に成功したパスを適切に変換するのみとなりました。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【相対パスの変換】&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;最後に、任意の基点から見たファイルの相対パスを得る方法を考えます。&lt;br /&gt;&lt;br /&gt;ここでは例として、基準ディレクトリ &lt;span style="color: magenta;"&gt;C:\Users\Tercel\Documents\html\aaa\bbb&lt;/span&gt; から見た &lt;span style="color: red;"&gt;C:\Users\Tercel\Documents\html\xxx\yyy\zzz\index.html&lt;/span&gt; の相対パスを得る手続きを一つひとつ確認していく事にします。&lt;br /&gt;&lt;br /&gt;まず、両者を並べます。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: magenta;"&gt;C:\Users\Tercel\Documents\html\aaa\bbb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;C:\Users\Tercel\Documents\html\xxx\yyy\zzz\index.html&lt;/span&gt;&lt;/blockquote&gt;次に、&lt;a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/io/File.html#separator"&gt;名前区切り文字&lt;/a&gt;（Windows 環境では "\" 記号、Unix 環境では "/" 記号）で各ファイルパスを分割します。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: magenta;"&gt;C:&lt;span style="color: black;"&gt;\&lt;/span&gt;Users&lt;span style="color: black;"&gt;\&lt;/span&gt;Tercel&lt;span style="color: black;"&gt;\&lt;/span&gt;Documents&lt;span style="color: black;"&gt;\&lt;/span&gt;html&lt;span style="color: black;"&gt;\&lt;/span&gt;aaa&lt;span style="color: black;"&gt;\&lt;/span&gt;bbb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;C:&lt;span style="color: black;"&gt;\&lt;/span&gt;Users&lt;span style="color: black;"&gt;\&lt;/span&gt;Tercel&lt;span style="color: black;"&gt;\&lt;/span&gt;Documents&lt;span style="color: black;"&gt;\&lt;/span&gt;html&lt;span style="color: black;"&gt;\&lt;/span&gt;xxx&lt;span style="color: black;"&gt;\&lt;/span&gt;yyy&lt;span style="color: black;"&gt;\&lt;/span&gt;zzz&lt;span style="color: black;"&gt;\&lt;/span&gt;index.html&lt;/span&gt;&lt;/blockquote&gt;区切られたパスを先頭から比較し、共通部分を除去します。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: magenta;"&gt;&lt;span style="color: black;"&gt;C:\Users\Tercel\Documents\html\&lt;/span&gt;aaa&lt;span style="color: black;"&gt;\&lt;/span&gt;bbb&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;&lt;span style="color: black;"&gt;C:\Users\Tercel\Documents\html\&lt;/span&gt;xxx&lt;span style="color: black;"&gt;\&lt;/span&gt;yyy&lt;span style="color: black;"&gt;\&lt;/span&gt;zzz&lt;span style="color: black;"&gt;\&lt;/span&gt;index.html&lt;/span&gt;&lt;/blockquote&gt;この時点で残った&lt;span style="color: magenta;"&gt;基点&lt;/span&gt;の階層の深さは 2（&lt;span style="color: magenta;"&gt;aaa&lt;/span&gt; と &lt;span style="color: magenta;"&gt;bbb&lt;/span&gt;）なので、相対パスには親ディレクトリを参照する "&lt;span style="color: magenta;"&gt;..&lt;/span&gt;" を 2 つ並べて、&lt;span style="color: red;"&gt;対象&lt;/span&gt;の残ったパスを続けます。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: magenta;"&gt;..&lt;/span&gt;&lt;span style="color: black;"&gt;/&lt;/span&gt;&lt;span style="color: magenta;"&gt;..&lt;/span&gt;&lt;span style="color: black;"&gt;/&lt;/span&gt;&lt;span style="color: red;"&gt;xxx&lt;/span&gt;&lt;span style="color: black;"&gt;/&lt;/span&gt;&lt;span style="color: red;"&gt;yy&lt;/span&gt;&lt;span style="color: black;"&gt;/&lt;/span&gt;&lt;span style="color: red;"&gt;zzz&lt;/span&gt;&lt;span style="color: black;"&gt;/&lt;/span&gt;&lt;span style="color: red;"&gt;index.html &lt;/span&gt;&lt;/blockquote&gt;あとは、各階層を Unix デフォルトの区切り文字 "/" で区切れば完成。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="color: magenta;"&gt;..&lt;/span&gt;/&lt;span style="color: magenta;"&gt;..&lt;/span&gt;/&lt;span style="color: red;"&gt;xxx&lt;/span&gt;/&lt;span style="color: red;"&gt;yy&lt;/span&gt;/&lt;span style="color: red;"&gt;zzz&lt;/span&gt;/&lt;span style="color: red;"&gt;index.html &lt;/span&gt;&lt;/blockquote&gt;これをメソッド化すると、以下のようなコードになります。 &lt;br /&gt;&lt;br /&gt;&lt;pre class="java:nogutter" name="code"&gt;// ある場所から見たファイルの相対パスを返すよ&lt;br /&gt;//&lt;br /&gt;// 引数1 basisPath  ... 相対パスの基準&lt;br /&gt;// 引数2 targetPath ... 対象ファイルのパス&lt;br /&gt;String getRelativePath(String basisPath, String targetPath) {&lt;br /&gt;  &lt;br /&gt;  File basisFile  = new File(basisPath).getAbsoluteFile();&lt;br /&gt;  File targetFile = new File(targetPath).getAbsoluteFile();&lt;br /&gt;  &lt;br /&gt;  // Java実行環境が使用するファイルの区切り文字の取得&lt;br /&gt;  String separator = File.separator;&lt;br /&gt;  &lt;br /&gt;  // セパレータの正規表現（Windows環境の場合は"\"記号をエスケープ）&lt;br /&gt;  String separatorRegex = separator.replaceAll("\\\\", "\\\\\\\\");&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // 相対パス表現の基準となるディレクトリ&lt;br /&gt;  File basisDir = basisFile.isDirectory() ? &lt;br /&gt;    basisFile : basisFile.getParentFile();&lt;br /&gt;  String basisDirPath = basisDir.getAbsolutePath();&lt;br /&gt;  &lt;br /&gt;  // ターゲットとなるディレクトリ&lt;br /&gt;  File targetDir;&lt;br /&gt;  String targetFileName;&lt;br /&gt;  if(targetFile.isDirectory()) {&lt;br /&gt;    targetDir      = targetFile;&lt;br /&gt;    targetFileName = "";&lt;br /&gt;  } else {&lt;br /&gt;    targetDir = targetFile.getParentFile();&lt;br /&gt;    targetFileName = targetFile.getName();&lt;br /&gt;  }&lt;br /&gt;  String targetDirPath  = targetDir.getAbsolutePath();&lt;br /&gt;&lt;br /&gt;  // ディレクトリ階層を格納&lt;br /&gt;  String[] basis  = basisDirPath.split(separatorRegex);&lt;br /&gt;  String[] target = targetDirPath.split(separatorRegex);&lt;br /&gt;  &lt;br /&gt;  // 相対パスを生成&lt;br /&gt;  StringBuffer path = new StringBuffer("");&lt;br /&gt;  &lt;br /&gt;  int cIndex;&lt;br /&gt;  int limit = Math.min(target.length, basis.length);&lt;br /&gt;  &lt;br /&gt;  for(cIndex = 0; cIndex &amp;lt; limit; ++cIndex)&lt;br /&gt;    if(!target[cIndex].equals(basis[cIndex])) break;&lt;br /&gt;&lt;br /&gt;  for(int i = cIndex; i &amp;lt; basis.length; ++i)&lt;br /&gt;    path.append("../");&lt;br /&gt;&lt;br /&gt;  for(int i = cIndex; i &amp;lt; target.length; ++i)&lt;br /&gt;    path.append(target[i] + "/");&lt;br /&gt;&lt;br /&gt;  path.append(targetFileName);&lt;br /&gt;  &lt;br /&gt;  return path.toString();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;このメソッドは、&lt;b&gt;だいたい&lt;/b&gt;正しく動作します。&lt;br /&gt;&lt;br /&gt;例外的に、&lt;br /&gt;&lt;ul&gt;&lt;li&gt;基準となるディレクトリや対象のファイルが存在しない場合 &lt;/li&gt;&lt;li&gt;相対パスの基準と対象のファイルがそれぞれ異なるドライブである場合&lt;/li&gt;&lt;/ul&gt;―― は、期待した結果を得る事ができない場合があります。&lt;br /&gt;&lt;br /&gt;また、注意すべき点として、&lt;b style="color: red;"&gt;引数に相対パスを与えた場合、（相対参照の）基準はデフォルトで Java 仮想マシンの呼び出し元になります&lt;/b&gt;。何を言っているか分からない場合は（ごめんなさい）、両者ともに絶対パスを与えた方が無難でしょう。&lt;br /&gt;&lt;br /&gt;問題を知っていて放置するのは気が引けるので、一応この 2 点に関する回避策を述べておきます。&lt;br /&gt;&lt;br /&gt;まず前者の場合、基準・あるいは対象&lt;a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/io/File.html#exists%28%29"&gt;ファイルが存在する事を事前に調べる&lt;/a&gt;事ができます。なければ一時的に&lt;a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/io/File.html#mkdir%28%29"&gt;ディレクトリを作り&lt;/a&gt;、パスを得た後で&lt;a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/io/File.html#delete%28%29"&gt;ディレクトリを削除&lt;/a&gt;するといった方法で対処できます（ちなみに&lt;b&gt;今回の場合は、基準と対象はどちらも実在のディレクトリであるため、この問題に関してはあまり考える必要はない&lt;/b&gt;と思います）。&lt;br /&gt;&lt;br /&gt;後者の場合、異なるドライブに対して相対参照する事はできないので、適当な例外を投げて終わるくらいが妥当なのでしょう。それ以上のお節介処理は、「相対パスを得る」という名前から想像できない挙動なので、好ましいとは言えません。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;というわけで、HTML ファイルから相対パス抜き、さらにそれを別の基点から見た相対パスに変換するための道具が一応揃いました。&lt;br /&gt;&lt;br /&gt;めでたし。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-4553810013388782485?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/4553810013388782485/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/html.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4553810013388782485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4553810013388782485'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/html.html' title='相対参照を適当に考慮したHTMLファイルの移動'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-5215850297194988009</id><published>2012-01-02T22:36:00.002+09:00</published><updated>2012-01-03T11:32:30.525+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>冬休みのてきとうアプリケーションづくり</title><content type='html'>&lt;b&gt;&lt;span style="font-size: large;"&gt;【プログラミング初め】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;ちょっと前に『作りたい』とか言っていた Web サイトの更新支援ツールの開発をてきとうに始めました。&lt;br /&gt;&lt;br /&gt;手始めに&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/12/java.html"&gt;前回のネタ&lt;/a&gt;を GUI 化。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-7o1qQUUnMhc/TwGrrtdM-AI/AAAAAAAAAfo/JE4ePCqdmWc/s1600/20120102001.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="144" src="http://3.bp.blogspot.com/-7o1qQUUnMhc/TwGrrtdM-AI/AAAAAAAAAfo/JE4ePCqdmWc/s320/20120102001.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;秘密鍵の設定画面&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;秘密鍵の設定は初回起動時だけ（ただし、変更は可能）。 &lt;br /&gt;&lt;br /&gt;次回の起動時からは下のような認証画面が表示されるようにしました。 GUI めんどくさい。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-bpNCm1Vdipk/TwGut9qMqmI/AAAAAAAAAgw/X1DqE6B9YyU/s1600/20120102005.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="87" src="http://4.bp.blogspot.com/-bpNCm1Vdipk/TwGut9qMqmI/AAAAAAAAAgw/X1DqE6B9YyU/s320/20120102005.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;秘密鍵の認証画面&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;FTP アカウントの設定ダイアログボックスも作りました。 &lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-F7hWspkShAg/TwGsR_bvmgI/AAAAAAAAAgM/aIMuPOQfuhI/s1600/20120102002.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-F7hWspkShAg/TwGsR_bvmgI/AAAAAAAAAgM/aIMuPOQfuhI/s1600/20120102002.png" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;FTP アカウントの設定画面&lt;br /&gt;設定した内容は秘密鍵で暗号化されます&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;で。&lt;br /&gt;&lt;br /&gt;ここからちょっと新ネタ（？）です。&lt;br /&gt;&lt;br /&gt;ただ独り言をつぶやき続けると、それをソーシャルのかけらもない静的な HTML サイトに投稿してくれるという &lt;b&gt;Twitter を超絶的に劣化させたシステム&lt;/b&gt;のメイン画面です。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-6kykeXkBD9k/TwGsYRz5QfI/AAAAAAAAAgY/S8HNknAiwqE/s1600/20120102003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="339" src="http://3.bp.blogspot.com/-6kykeXkBD9k/TwGsYRz5QfI/AAAAAAAAAgY/S8HNknAiwqE/s400/20120102003.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;……うん。&lt;br /&gt;&lt;br /&gt;なんだろうね&lt;b&gt;このインタフェースのデザインセンスの無さ&lt;/b&gt;は。&lt;br /&gt;&lt;br /&gt;とりあえず、ツイート感覚で気軽に Web サイトを更新できれば、放置状態になる事もないかなー……と安直に考えたのです。&lt;br /&gt;&lt;br /&gt;しかし、&lt;b&gt;『アプリケーションがある程度完成した時点で満足して結局使わない』というのがいつものパターン&lt;/b&gt;なので、たぶん今回もそうなるでしょう。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;インタフェースができてくるとテンションが上がるのですが、実は地味なところで壁にぶつかっています。&lt;br /&gt;&lt;br /&gt;たとえば、生成される中間ファイル&lt;sup&gt;※&lt;/sup&gt;の管理方法。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ これらの中間ファイルを、静的な HTML テンプレートに動的に埋め込んでコンテンツを作ろうという魂胆。&lt;/span&gt;&lt;/blockquote&gt;今のところ、ディレクトリを「年」「月」「日」に階層化して、そこにファイルを整理しておこうと考えています。&lt;br /&gt;&lt;br /&gt;ただし、階層的管理を実装しようとすると、新規フォルダを作る際の処理オーバヘッドや、ファイル中に埋め込まれた相対パスの扱いが厄介な点など、課題も多くなります。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-iGm8icccwl0/TwGsjfnNuyI/AAAAAAAAAgk/Wtx5R-KSay8/s1600/20120102004.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="251" src="http://4.bp.blogspot.com/-iGm8icccwl0/TwGsjfnNuyI/AAAAAAAAAgk/Wtx5R-KSay8/s400/20120102004.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;中間ファイルを階層的に管理する様子&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;作れば作るほど課題が見えてきますが、もしも自分の中で納得できる解決策が見つかったら、適宜ご紹介していこうかな……と考えています。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-5215850297194988009?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/5215850297194988009/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post_02.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/5215850297194988009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/5215850297194988009'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post_02.html' title='冬休みのてきとうアプリケーションづくり'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-7o1qQUUnMhc/TwGrrtdM-AI/AAAAAAAAAfo/JE4ePCqdmWc/s72-c/20120102001.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-2463699058633668806</id><published>2012-01-01T18:06:00.000+09:00</published><updated>2012-01-01T18:06:07.036+09:00</updated><title type='text'>明けましておめでとうございます</title><content type='html'>旧年中は大変お世話になりました。&lt;br /&gt;&lt;br /&gt;おかげさまで、旧年中は 47,000PV を超えるアクセスがありました。&lt;br /&gt;&lt;br /&gt;本年は、より一層のご愛顧のほどよろしくお願い申し上げます。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: right;"&gt;2012年1月1日　たーせる &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-2463699058633668806?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/2463699058633668806/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/2463699058633668806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/2463699058633668806'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2012/01/blog-post.html' title='明けましておめでとうございます'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-88224871451808778</id><published>2011-12-30T14:42:00.007+09:00</published><updated>2011-12-30T21:41:03.516+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='セキュリティ'/><category scheme='http://www.blogger.com/atom/ns#' term='身辺雑記'/><title type='text'>Javaでできるだけ安全にデータを直列化するためのてきとう備忘録</title><content type='html'>&lt;b&gt;現在絶賛放置中の Web サイトを今後の発信基地として再開発したいと思い立った&lt;/b&gt;事がきっかけで、最近は Web サイトの簡易更新支援ツールの開発を考えている。&lt;br /&gt;&lt;br /&gt;簡単な HTML ジェネレータと FTP クライアントを一緒にしたようなものを作りたい。 &lt;br /&gt;&lt;br /&gt;特に、FTP クライアントを作る際には、ユーザビリティのためにアカウント情報（ユーザ名とかパスワードとか）をローカルに保存しておき、できるだけワンタッチでサーバにアクセスできる仕様にしたいなと考えている。&lt;br /&gt;&lt;br /&gt;データのセーブとロードは至るところで使いそうだし、今日はそこらへんの実装をステップバイステップで書き残しておこうと思う。&lt;br /&gt;&lt;br /&gt;あ、&lt;b&gt;この記事のサンプルコードには FTP 接続の実装例は含まれていない&lt;/b&gt;ので、そういう内容を期待している方にはごめんなさい。あくまでデータのアーカイブに関する話題に絞ってもそれなりの分量になってしまったので……。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【備忘録その1: 直列化】&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;データのアーカイブに関する最も簡単な方法は、以下のようにしてユーザ名とパスワードをそのまま&lt;a href="http://ja.wikipedia.org/wiki/%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%BA"&gt;直列化&lt;/a&gt;してしまう事であろう。以下に Java のコードを示す。&lt;b&gt;エラー処理が不十分なのはご容赦&lt;/b&gt;。&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;package com.tercel_tech.sample;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.FileInputStream;&lt;br /&gt;import java.io.FileOutputStream;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;import java.io.ObjectInputStream;&lt;br /&gt;import java.io.ObjectOutputStream;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;&lt;br /&gt;public class Main {&lt;br /&gt;  private static final String FILENAME = "Account.txt";  // ファイル名&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {  &lt;br /&gt;    File file = new File(FILENAME);&lt;br /&gt;    &lt;br /&gt;    if(!file.exists()) {&lt;br /&gt;      // 事前に登録したアカウント情報がない場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在しません");&lt;br /&gt;      createUserAccount();&lt;br /&gt;    } else {&lt;br /&gt;      // 事前に登録したアカウント情報がある場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在します");&lt;br /&gt;      loadUserAccount();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // ユーザアカウントを作ってファイルに保存するメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static void createUserAccount() {&lt;br /&gt;    System.out.print("ユーザ名を入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String name;&lt;br /&gt;    try {&lt;br /&gt;      name = new BufferedReader(&lt;br /&gt;               new InputStreamReader(&lt;br /&gt;                 System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      name = "名無しさん";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    System.out.print("パスワードを入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String passwd;&lt;br /&gt;    try {&lt;br /&gt;      passwd = new BufferedReader(&lt;br /&gt;                 new InputStreamReader(&lt;br /&gt;                   System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      passwd = "aaaa";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ユーザ名とパスワードを基に、アカウント情報を作る&lt;br /&gt;    UserData userData = new UserData(name, passwd);&lt;br /&gt;&lt;br /&gt;    // アカウント情報をファイルに保存する&lt;br /&gt;    try {&lt;br /&gt;      new ObjectOutputStream(&lt;br /&gt;        new FileOutputStream(&lt;br /&gt;          FILENAME)).writeObject(userData);&lt;br /&gt;      System.out.println("セーブに成功しました");&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      // 書き込みにミスった&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 既に存在するユーザアカウント情報を&lt;br /&gt;  //ファイルからロードするメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static UserData loadUserAccount() {&lt;br /&gt;    try {&lt;br /&gt;      UserData data = (UserData) new ObjectInputStream(&lt;br /&gt;        new FileInputStream(FILENAME)).readObject();&lt;br /&gt;&lt;br /&gt;      System.out.println("ロードに成功しました");&lt;br /&gt;      System.out.println("ユーザ名: "   + data.getName());&lt;br /&gt;      System.out.println("パスワード: " + data.getPass());&lt;br /&gt;      return data;&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      return null;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ==============================&lt;br /&gt;// ユーザのログイン情報クラス&lt;br /&gt;// ==============================&lt;br /&gt;class UserData implements Serializable {&lt;br /&gt;  private String name;    // ユーザ名&lt;br /&gt;  private String passwd;  // パスワード&lt;br /&gt;&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // --------------------&lt;br /&gt;  // 引数1: ユーザ名&lt;br /&gt;  // 引数2: パスワード&lt;br /&gt;  public UserData(String name, String passwd) {&lt;br /&gt;    this.name   = name;&lt;br /&gt;    this.passwd = passwd;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ゲッタ&lt;br /&gt;  public String getName() { return name;   }&lt;br /&gt;  public String getPass() { return passwd; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;まずは、ユーザアカウントが存在しない状態でプログラムを走らせる。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【実行例1】&lt;/b&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;div style="color: #b4a7d6;"&gt;ユーザアカウントが存在しません&lt;/div&gt;&lt;span style="color: #b4a7d6;"&gt;ユーザ名を入力して下さい:&lt;/span&gt; &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;tercel&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #b4a7d6;"&gt;パスワードを入力して下さい:&lt;/span&gt; &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;hoge&lt;/span&gt;&lt;br /&gt;&lt;div style="color: #b4a7d6;"&gt;セーブに成功しました&lt;/div&gt;&lt;/blockquote&gt;なお、ユーザ名とパスワードは任意で入力する事ができる。 &lt;br /&gt;&lt;br /&gt;この後、プログラムを再起動させると、既に存在するアカウント情報を自動的にロードして、その内容を表示してくれる。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【実行例2】&lt;/b&gt; &lt;br /&gt;&lt;blockquote class="tr_bq" style="color: #b4a7d6;"&gt;ユーザアカウントが存在します&lt;br /&gt;ロードに成功しました&lt;br /&gt;ユーザ名: tercel&lt;br /&gt;パスワード: hoge&lt;/blockquote&gt;&lt;br /&gt;一見するとこれでも充分そうだが、自動生成された &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Account.txt&lt;/span&gt; をメモ帳で開くととんでもない事が分かる。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-lhIW5AXJYrY/Tvx2iTB9O_I/AAAAAAAAAfE/lpl8q-JhAE0/s1600/2011123001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-lhIW5AXJYrY/Tvx2iTB9O_I/AAAAAAAAAfE/lpl8q-JhAE0/s1600/2011123001.png" /&gt;&lt;/a&gt;&lt;/div&gt;ユーザ名とパスワードが平文のまま保存されていて、明らかにまずい。常識的に考えて何らかの暗号化を施すべきであろう。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【備忘録その2: 秘密鍵暗号】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;そこで、アカウント情報を&lt;a href="http://ja.wikipedia.org/wiki/%E5%85%B1%E9%80%9A%E9%8D%B5%E6%9A%97%E5%8F%B7"&gt;秘密鍵暗号方式&lt;/a&gt;で暗号化する（アルゴリズムは &lt;a href="http://ja.wikipedia.org/wiki/Blowfish"&gt;Blowfish&lt;/a&gt;）。ちなみに、暗号化に使用した秘密鍵は復号するまで破棄してはいけない。&lt;br /&gt;&lt;br /&gt;秘密鍵暗号方式を実装するために、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;UserData&lt;/span&gt; クラスを以下のように書き換える（ただし &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;javax.crypto.Cipher&lt;/span&gt; と &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;javax.crypto.spec.SecretKeySpec&lt;/span&gt; を &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;import&lt;/span&gt; する事）。&lt;br /&gt;&lt;br /&gt;この時点で &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;UserData&lt;/span&gt; クラスの互換性が無くなっているので、もし Account.txt が残っている場合は削除する。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse:firstline[94]" name="code"&gt;// ==============================&lt;br /&gt;// ユーザのログイン情報クラス&lt;br /&gt;// ==============================&lt;br /&gt;class UserData implements Serializable {&lt;br /&gt;  private final String SECRET_KEY = "HOGEHOGE_TERCEL";  // 秘密鍵&lt;br /&gt;  private byte[] name;    // ユーザ名&lt;br /&gt;  private byte[] passwd;  // パスワード&lt;br /&gt;&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // --------------------&lt;br /&gt;  // 引数1: ユーザ名&lt;br /&gt;  // 引数2: パスワード&lt;br /&gt;  public UserData(String name, String passwd) {&lt;br /&gt;    try {&lt;br /&gt;      this.name   = encrypt(SECRET_KEY, name);&lt;br /&gt;      this.passwd = encrypt(SECRET_KEY, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.out.println("暗号化に失敗しました");&lt;br /&gt;      this.name   = null;&lt;br /&gt;      this.passwd = null;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ゲッタ&lt;br /&gt;  public String getName() {&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(SECRET_KEY, name);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.out.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;  public String getPass() {&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(SECRET_KEY, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.out.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 暗号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private byte[] encrypt(String key, String text) throws Exception {&lt;br /&gt;&lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.ENCRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] encrypted = cipher.doFinal(text.getBytes());&lt;br /&gt;&lt;br /&gt;    return encrypted;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 復号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private String decrypt(String key, byte[] encrypted) throws Exception {&lt;br /&gt;    &lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.DECRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] decrypted = cipher.doFinal(encrypted);&lt;br /&gt;&lt;br /&gt;    return new String(decrypted);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;実行結果は先程と同じだが、保存されるファイルの内容が異なる。アカウント名とパスワードが傍目には判読できなくなっているのだ。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-nqEAWwfcd80/Tv0psHQP9uI/AAAAAAAAAfQ/_tq_ITZosGs/s1600/2011123002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-nqEAWwfcd80/Tv0psHQP9uI/AAAAAAAAAfQ/_tq_ITZosGs/s1600/2011123002.png" /&gt;&lt;/a&gt;&lt;/div&gt;だが、今度は秘密鍵が平文で保存されている。これでは結局、家の鍵を玄関先に放置しておくようなものだ。&lt;br /&gt;&lt;br /&gt;これに関しては、秘密鍵をクラスの外で持てばとりあえずはよさそうだが、どのみちプログラム中で秘密鍵を埋め込む事になるので気持ちが悪い。&lt;b&gt;逆コンパイルされたらイチコロ&lt;/b&gt;だ。&lt;br /&gt;&lt;br /&gt;それよりも、アプリケーションを起動した瞬間にログイン情報の復号化を試みる現在の実装では、&lt;b&gt;無関係な第三者がサーバに繋げてしまう&lt;/b&gt;事の方が遙かに問題のような気がする。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【備忘録その3: メッセージダイジェスト】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;上記の問題に対処するため、さらに次の方針を採る事にする。&lt;br /&gt;&lt;br /&gt;まず、事前にユーザが入力した秘密鍵は、適当な&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E9%96%A2%E6%95%B0"&gt;ハッシュ関数&lt;/a&gt;（ここでは &lt;a href="http://ja.wikipedia.org/wiki/MD5"&gt;MD5&lt;/a&gt;）でメッセージダイジェスト化した&lt;b&gt;ハッシュ値&lt;/b&gt;として保存しておく。&lt;b&gt;ハッシュ値から元の文字列を復元する事は不可能&lt;/b&gt;とされているため、たとえ漏れてもそれほど心配する必要はない。&lt;br /&gt;&lt;br /&gt;アカウント情報を復元する際には、ユーザに対して改めて秘密鍵の入力を要求する。鍵を知らない第三者を弾くためである。&lt;br /&gt;&lt;br /&gt;システムは入力された鍵をメッセージダイジェスト化し、保存してある鍵のハッシュ値との突き合わせを行う事で同一性のチェックを行う。もし両者が等しければ、入力された鍵の平文を用いてアカウント情報を復号化できる。&lt;br /&gt;&lt;br /&gt;この方法だと、プログラムを起動するたびに秘密鍵の入力（認証）を要求する事になるので、少々操作性が悪くなる。しかし、手間の割に守れる情報量は多いので、それなりに割に合っているとは思う。&lt;br /&gt;&lt;br /&gt;とりあえず、これを実装した結果を以下に示す。前回の Account.txt が残っている場合は削除する。&lt;br /&gt;&lt;br /&gt;まずは &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;UserData&lt;/span&gt; クラスから。特に &lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;hash&lt;/span&gt; メソッドがチャームポイント&lt;/b&gt;である。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse:firstline[109]" name="code"&gt;// ==============================&lt;br /&gt;// ユーザのログイン情報クラス&lt;br /&gt;// ==============================&lt;br /&gt;class UserData implements Serializable {&lt;br /&gt;  //private final String SECRET_KEY = "HOGEHOGE_TERCEL";  // 秘密鍵&lt;br /&gt;  private byte[] secretKey; // 秘密鍵（のハッシュ値）&lt;br /&gt;  private byte[] name;      // ユーザ名&lt;br /&gt;  private byte[] passwd;    // パスワード&lt;br /&gt;&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // --------------------&lt;br /&gt;  // 引数1: 秘密鍵（マスターパスワード）&lt;br /&gt;  // 引数1: ユーザ名&lt;br /&gt;  // 引数2: ログインパスワード&lt;br /&gt;  public UserData(String secretKey, String name, String passwd) {&lt;br /&gt;    try {&lt;br /&gt;      this.secretKey = hash(secretKey);&lt;br /&gt;      this.name      = encrypt(secretKey, name);&lt;br /&gt;      this.passwd    = encrypt(secretKey, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("暗号化に失敗しました");&lt;br /&gt;      this.secretKey = null;&lt;br /&gt;      this.name      = null;&lt;br /&gt;      this.passwd    = null;&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================================&lt;br /&gt;  // 入力された秘密鍵が保存されている秘密鍵と等しいかを&lt;br /&gt;  // ハッシュで比較するよ！&lt;br /&gt;  // ==============================================&lt;br /&gt;  public boolean auth(String secretKey) {&lt;br /&gt;    try {&lt;br /&gt;      byte[] md = hash(secretKey);&lt;br /&gt;      if(md.length != this.secretKey.length) return false;&lt;br /&gt;      for(int i = 0; i &amp;lt; md.length; ++ i) {&lt;br /&gt;        if(md[i] != this.secretKey[i]) return false;&lt;br /&gt;      }&lt;br /&gt;      return true;&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      return false;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ===========================================&lt;br /&gt;  // 秘密鍵の平文を渡して名前とパスワードを取得する&lt;br /&gt;  // ===========================================&lt;br /&gt;  public String getName(String key) {&lt;br /&gt;    // 最初に認証&lt;br /&gt;    if(!auth(key)) {&lt;br /&gt;      System.err.println("秘密鍵が正しくありません");&lt;br /&gt;      System.err.println("復号化を行いませんでした");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(key, name);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public String getPass(String key) {&lt;br /&gt;    // 最初に認証&lt;br /&gt;    if(!auth(key)) {&lt;br /&gt;      System.err.println("秘密鍵が正しくありません");&lt;br /&gt;      System.err.println("復号化を行いませんでした");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(key, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // メッセージダイジェスト化&lt;br /&gt;  // ==============================&lt;br /&gt;  private byte[] hash(String text) throws Exception {&lt;br /&gt;    MessageDigest md = MessageDigest.getInstance("MD5");&lt;br /&gt;    md.update(text.getBytes());&lt;br /&gt;    return md.digest();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 暗号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private byte[] encrypt(String key, String text) throws Exception {&lt;br /&gt;&lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.ENCRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] encrypted = cipher.doFinal(text.getBytes());&lt;br /&gt;&lt;br /&gt;    return encrypted;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 復号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private String decrypt(String key, byte[] encrypted) throws Exception {&lt;br /&gt;    &lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.DECRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] decrypted = cipher.doFinal(encrypted);&lt;br /&gt;&lt;br /&gt;    return new String(decrypted);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;また、これを使用する &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main&lt;/span&gt; クラスも以下のように書き換える。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse:firstline[16]" name="code"&gt;public class Main {&lt;br /&gt;  private static final String FILENAME = "Account.txt";  // ファイル名&lt;br /&gt;&lt;br /&gt;  private static String secretKey;&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {  &lt;br /&gt;    File file = new File(FILENAME);&lt;br /&gt;    &lt;br /&gt;    // 秘密鍵を入力させる&lt;br /&gt;    System.out.print("秘密鍵を入力して下さい: ");&lt;br /&gt;    try {&lt;br /&gt;      secretKey = new BufferedReader(&lt;br /&gt;                    new InputStreamReader(&lt;br /&gt;                      System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      System.err.println("秘密鍵の取得に失敗しました");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if(!file.exists()) {&lt;br /&gt;      // 事前に登録したアカウント情報がない場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在しません");&lt;br /&gt;      createUserAccount();&lt;br /&gt;    } else {&lt;br /&gt;      // 事前に登録したアカウント情報がある場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在します");&lt;br /&gt;      loadUserAccount();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // ユーザアカウントを作ってファイルに保存するメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static void createUserAccount() {&lt;br /&gt;    System.out.print("ユーザ名を入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String name;&lt;br /&gt;    try {&lt;br /&gt;      name = new BufferedReader(&lt;br /&gt;               new InputStreamReader(&lt;br /&gt;                 System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      name = "名無しさん";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    System.out.print("パスワードを入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String passwd;&lt;br /&gt;    try {&lt;br /&gt;      passwd = new BufferedReader(&lt;br /&gt;                 new InputStreamReader(&lt;br /&gt;                   System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      passwd = "aaaa";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ユーザ名とパスワードを基に、アカウント情報を作る&lt;br /&gt;    UserData userData = new UserData(secretKey, name, passwd);&lt;br /&gt;&lt;br /&gt;    // アカウント情報をファイルに保存する&lt;br /&gt;    try {&lt;br /&gt;      new ObjectOutputStream(&lt;br /&gt;        new FileOutputStream(&lt;br /&gt;          FILENAME)).writeObject(userData);&lt;br /&gt;      System.out.println("セーブに成功しました");&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      // 書き込みにミスった&lt;br /&gt;      System.err.println("セーブに失敗しました");&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 既に存在するユーザアカウント情報を&lt;br /&gt;  //ファイルからロードするメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static UserData loadUserAccount() {&lt;br /&gt;    try {&lt;br /&gt;      UserData data = (UserData) new ObjectInputStream(&lt;br /&gt;        new FileInputStream(FILENAME)).readObject();&lt;br /&gt;&lt;br /&gt;      System.out.println("ロードに成功しました");&lt;br /&gt;      System.out.println("ユーザ名: "   + data.getName(secretKey));&lt;br /&gt;      System.out.println("パスワード: " + data.getPass(secretKey));&lt;br /&gt;      return data;&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      return null;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;というか、面倒な人（というか僕）のためにだだ書きしたソースも丸ごと置いておこう。コピペすれば動くと思う。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;package com.tercel_tech.sample;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.io.FileInputStream;&lt;br /&gt;import java.io.FileOutputStream;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;import java.io.ObjectInputStream;&lt;br /&gt;import java.io.ObjectOutputStream;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;import java.security.MessageDigest;&lt;br /&gt;import javax.crypto.Cipher;&lt;br /&gt;import javax.crypto.spec.SecretKeySpec;&lt;br /&gt;&lt;br /&gt;public class Main {&lt;br /&gt;  private static final String FILENAME = "Account.txt";  // ファイル名&lt;br /&gt;&lt;br /&gt;  private static String secretKey;&lt;br /&gt;&lt;br /&gt;  public static void main(String[] args) {  &lt;br /&gt;    File file = new File(FILENAME);&lt;br /&gt;    &lt;br /&gt;    // 秘密鍵を入力させる&lt;br /&gt;    System.out.print("秘密鍵を入力して下さい: ");&lt;br /&gt;    try {&lt;br /&gt;      secretKey = new BufferedReader(&lt;br /&gt;                    new InputStreamReader(&lt;br /&gt;                      System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      System.err.println("秘密鍵の取得に失敗しました");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if(!file.exists()) {&lt;br /&gt;      // 事前に登録したアカウント情報がない場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在しません");&lt;br /&gt;      createUserAccount();&lt;br /&gt;    } else {&lt;br /&gt;      // 事前に登録したアカウント情報がある場合&lt;br /&gt;      System.out.println("ユーザアカウントが存在します");&lt;br /&gt;      loadUserAccount();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // ユーザアカウントを作ってファイルに保存するメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static void createUserAccount() {&lt;br /&gt;    System.out.print("ユーザ名を入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String name;&lt;br /&gt;    try {&lt;br /&gt;      name = new BufferedReader(&lt;br /&gt;               new InputStreamReader(&lt;br /&gt;                 System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      name = "名無しさん";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    System.out.print("パスワードを入力して下さい: ");&lt;br /&gt;&lt;br /&gt;    // 入力を適当に受け付ける&lt;br /&gt;    String passwd;&lt;br /&gt;    try {&lt;br /&gt;      passwd = new BufferedReader(&lt;br /&gt;                 new InputStreamReader(&lt;br /&gt;                   System.in)).readLine();&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      passwd = "aaaa";&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ユーザ名とパスワードを基に、アカウント情報を作る&lt;br /&gt;    UserData userData = new UserData(secretKey, name, passwd);&lt;br /&gt;&lt;br /&gt;    // アカウント情報をファイルに保存する&lt;br /&gt;    try {&lt;br /&gt;      new ObjectOutputStream(&lt;br /&gt;        new FileOutputStream(&lt;br /&gt;          FILENAME)).writeObject(userData);&lt;br /&gt;      System.out.println("セーブに成功しました");&lt;br /&gt;    } catch (IOException ex) {&lt;br /&gt;      // 書き込みにミスった&lt;br /&gt;      System.err.println("セーブに失敗しました");&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 既に存在するユーザアカウント情報を&lt;br /&gt;  //ファイルからロードするメソッド&lt;br /&gt;  // ==============================&lt;br /&gt;  private static UserData loadUserAccount() {&lt;br /&gt;    try {&lt;br /&gt;      UserData data = (UserData) new ObjectInputStream(&lt;br /&gt;        new FileInputStream(FILENAME)).readObject();&lt;br /&gt;&lt;br /&gt;      System.out.println("ロードに成功しました");&lt;br /&gt;      System.out.println("ユーザ名: "   + data.getName(secretKey));&lt;br /&gt;      System.out.println("パスワード: " + data.getPass(secretKey));&lt;br /&gt;      return data;&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      return null;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ==============================&lt;br /&gt;// ユーザのログイン情報クラス&lt;br /&gt;// ==============================&lt;br /&gt;class UserData implements Serializable {&lt;br /&gt;  //private final String SECRET_KEY = "HOGEHOGE_TERCEL";  // 秘密鍵&lt;br /&gt;  private byte[] secretKey; // 秘密鍵（のハッシュ値）&lt;br /&gt;  private byte[] name;      // ユーザ名&lt;br /&gt;  private byte[] passwd;    // パスワード&lt;br /&gt;&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // --------------------&lt;br /&gt;  // 引数1: 秘密鍵（マスターパスワード）&lt;br /&gt;  // 引数1: ユーザ名&lt;br /&gt;  // 引数2: ログインパスワード&lt;br /&gt;  public UserData(String secretKey, String name, String passwd) {&lt;br /&gt;    try {&lt;br /&gt;      this.secretKey = hash(secretKey);&lt;br /&gt;      this.name      = encrypt(secretKey, name);&lt;br /&gt;      this.passwd    = encrypt(secretKey, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("暗号化に失敗しました");&lt;br /&gt;      this.secretKey = null;&lt;br /&gt;      this.name      = null;&lt;br /&gt;      this.passwd    = null;&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================================&lt;br /&gt;  // 入力された秘密鍵が保存されている秘密鍵と等しいかを&lt;br /&gt;  // ハッシュで比較するよ！&lt;br /&gt;  // ==============================================&lt;br /&gt;  public boolean auth(String secretKey) {&lt;br /&gt;    try {&lt;br /&gt;      byte[] md = hash(secretKey);&lt;br /&gt;      if(md.length != this.secretKey.length) return false;&lt;br /&gt;      for(int i = 0; i &amp;lt; md.length; ++ i) {&lt;br /&gt;        if(md[i] != this.secretKey[i]) return false;&lt;br /&gt;      }&lt;br /&gt;      return true;&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      return false;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ===========================================&lt;br /&gt;  // 秘密鍵の平文を渡して名前とパスワードを取得する&lt;br /&gt;  // ===========================================&lt;br /&gt;  public String getName(String key) {&lt;br /&gt;    // 最初に認証&lt;br /&gt;    if(!auth(key)) {&lt;br /&gt;      System.err.println("秘密鍵が正しくありません");&lt;br /&gt;      System.err.println("復号化を行いませんでした");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(key, name);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public String getPass(String key) {&lt;br /&gt;    // 最初に認証&lt;br /&gt;    if(!auth(key)) {&lt;br /&gt;      System.err.println("秘密鍵が正しくありません");&lt;br /&gt;      System.err.println("復号化を行いませんでした");&lt;br /&gt;      System.exit(1);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    String ret;&lt;br /&gt;    try {&lt;br /&gt;      ret = decrypt(key, passwd);&lt;br /&gt;    } catch (Exception ex) {&lt;br /&gt;      System.err.println("復号化に失敗しました");&lt;br /&gt;      ret = null;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // メッセージダイジェスト化&lt;br /&gt;  // ==============================&lt;br /&gt;  private byte[] hash(String text) throws Exception {&lt;br /&gt;    MessageDigest md = MessageDigest.getInstance("MD5");&lt;br /&gt;    md.update(text.getBytes());&lt;br /&gt;    return md.digest();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 暗号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private byte[] encrypt(String key, String text) throws Exception {&lt;br /&gt;&lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.ENCRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] encrypted = cipher.doFinal(text.getBytes());&lt;br /&gt;&lt;br /&gt;    return encrypted;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ==============================&lt;br /&gt;  // 復号化&lt;br /&gt;  // ==============================&lt;br /&gt;  private String decrypt(String key, byte[] encrypted) throws Exception {&lt;br /&gt;    &lt;br /&gt;    SecretKeySpec sksSpec = new SecretKeySpec(key.getBytes(), "Blowfish");&lt;br /&gt;    Cipher cipher = Cipher.getInstance("Blowfish");&lt;br /&gt;&lt;br /&gt;    cipher.init(Cipher.DECRYPT_MODE, sksSpec);&lt;br /&gt;    byte[] decrypted = cipher.doFinal(encrypted);&lt;br /&gt;&lt;br /&gt;    return new String(decrypted);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これを実行してみよう。まずユーザアカウント情報が存在しない状態から。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【実行例1】&lt;/b&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;div style="color: #b4a7d6;"&gt;秘密鍵を入力して下さい: &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;HOGEHOGE_TERCEL&lt;/span&gt;&lt;br /&gt;ユーザアカウントが存在しません&lt;br /&gt;ユーザ名を入力して下さい: &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;tercel&lt;/span&gt;&lt;br /&gt;パスワードを入力して下さい: &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;hoge&lt;/span&gt;&lt;br /&gt;セーブに成功しました&lt;/div&gt;&lt;/blockquote&gt;次に、この状態でアプリケーションを再起動してみる。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【実行例2】&lt;/b&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;div style="color: #b4a7d6;"&gt;秘密鍵を入力して下さい: &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;HOGEHOGE_TERCEL&lt;/span&gt;&lt;br /&gt;ユーザアカウントが存在します&lt;br /&gt;ロードに成功しました&lt;br /&gt;ユーザ名: tercel&lt;br /&gt;パスワード: hoge&lt;/div&gt;&lt;/blockquote&gt;秘密鍵が正しいと、ちゃんと復号に成功する。&lt;br /&gt;&lt;br /&gt;ふたたび再起動し、今度は秘密鍵を間違えてみる。 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;【実行例3】&lt;/b&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;div style="color: #b4a7d6;"&gt;秘密鍵を入力して下さい: &lt;span style="color: yellow; font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;hogehoge_tercel&lt;/span&gt;&lt;br /&gt;ユーザアカウントが存在します&lt;br /&gt;ロードに成功しました&lt;br /&gt;&lt;span style="color: red;"&gt;秘密鍵が正しくありません&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;復号化を行いませんでした&lt;/span&gt;&lt;br /&gt;&lt;span style="color: red;"&gt;Java Result: 1&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;br /&gt;どのみちロードまではしてしまうわけだが（というかロードしないと秘密鍵の突き合わせが行えないので）、異なる秘密鍵の入力をミスると復号化できないので、セキュリティ的にはそこそこ良さげな気がする。&lt;br /&gt;&lt;br /&gt;ちなみに、Account.txt をメモ帳で開くとこんな感じになっている。かろうじて判読できるのはクラス名とフィールドだけだ。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-j5427s49Hwo/Tv1CwVO7-KI/AAAAAAAAAfc/pkNTbDcbLKg/s1600/2011123003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-j5427s49Hwo/Tv1CwVO7-KI/AAAAAAAAAfc/pkNTbDcbLKg/s1600/2011123003.png" /&gt;&lt;/a&gt;&lt;/div&gt;これだけでは解読は困難だろう。&lt;br /&gt;&lt;br /&gt;また、プログラムに秘密鍵を直接埋め込んでいるわけでもないので逆コンパイルされてもそれほど痛くもかゆくもない。&lt;br /&gt;&lt;br /&gt;というわけで、ひとまずこれでログイン情報を外部に保存する最低限の仕組みが一応できたっぽい。&lt;br /&gt;&lt;br /&gt;ふぅ…。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【参考サイト】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.itmedia.co.jp/enterprise/articles/0407/01/news017.html"&gt;Java Tips：手軽に暗号化・復号化するには？&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.atmarkit.co.jp/fjava/javatips/117java021.html"&gt;Javaでダイジェストを生成する&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-88224871451808778?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/88224871451808778/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/java.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/88224871451808778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/88224871451808778'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/java.html' title='Javaでできるだけ安全にデータを直列化するためのてきとう備忘録'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-lhIW5AXJYrY/Tvx2iTB9O_I/AAAAAAAAAfE/lpl8q-JhAE0/s72-c/2011123001.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-6596259748298685837</id><published>2011-12-28T19:30:00.003+09:00</published><updated>2011-12-28T20:14:59.516+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='身辺雑記'/><title type='text'>近況報告</title><content type='html'>先日、研究室の忘年会に参加してきた。 &lt;br /&gt;&lt;br /&gt;仮配属の3年生に、『興味深い研究テーマとかはあるかな？』と訊いたところ、『たーせるさんのテーマは面白いと思います』と言ってもらえた。社交辞令だとは思うけど、なかなか嬉しいものだ。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;現在は、学位論文の執筆・校訂やらプレゼン用のスライドづくりやらで、猫の手も借りたいほど忙しい状態だが、それほど焦る気持ちはない。&lt;br /&gt;&lt;br /&gt;同時進行で複数の課題を片付ける事が、昔ほど苦手ではなくなったのはよい事だと思う。 &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;うちの B4 で気がかりなのが、理屈を知らずに人の成果に乗っかる研究姿勢が治らないという事。 しかもそれを抜いてしまうと、成果といえるほどの成果が残らないのもまた問題。&lt;br /&gt;&lt;br /&gt;既存技術を組み合わせるにしても、誰もが思いつくような組み合わせ方では何の面白みも感じない。 『そうきたか！』と思わせる意外性の一つでもあればよいのだが。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;先日、ついに炭酸飲料を自作できる夢のような道具を買った。コーラとかが作れる。最初は炭酸を吹きこぼしたり、味の加減を失敗したりと色々あったが、今はなんとか上手にできるようになった。&lt;br /&gt;&lt;br /&gt;買い物に行くたびに重たいペットボトルを持って帰らなくてよくなった事と、ペットボトルゴミが減った事は個人的にかなり幸せな事である。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;ネットワークスペシャリストに合格したからと言って、その日から何かが変わるわけではない。&lt;br /&gt;&lt;br /&gt;今にして思えば、資格自体は別にネットワークである必要はなかった。とにかく“苦手な状態からスタートして、短期間でその分野の最難関資格を取った”という事実が自分の中で大きな自信になった。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;今はやりたい事が溢れている状態。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-6596259748298685837?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/6596259748298685837/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/blog-post_28.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6596259748298685837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/6596259748298685837'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/blog-post_28.html' title='近況報告'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-4822574951816899758</id><published>2011-12-21T03:21:00.000+09:00</published><updated>2011-12-21T03:21:56.689+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ネットワークスペシャリスト'/><category scheme='http://www.blogger.com/atom/ns#' term='書評'/><title type='text'>これなら受かる！ 『ネスペ22 （β版）』</title><content type='html'>高度情報処理技術者試験の参考書選びは難しい――。 &lt;br /&gt;&lt;br /&gt;IT パスポートや基本情報技術者のような万人向けの入門資格では、懇切丁寧な解説やかわいいイラストをふんだんに盛り込んだやさしい参考書が充実しており、受験者は自分にあった教材を選ぶ事ができます。&lt;br /&gt;&lt;br /&gt;しかし、試験のレベルが高度化するにつれて、こうしたとっつきやすい参考書が減っていき、よく言えば質実剛健、悪く言えば無味乾燥な参考書ばかりになっていきます。受験層を考えれば致し方のない事かも知れませんが、正直、僕には少々きつい。 &lt;br /&gt;&lt;br /&gt;というわけで、前置きが長くなりましたが、久しぶりの書評ということで、本日は「本物のネットワークスペシャリストになるための最も詳しい過去問解説と合格のコツ」という副題のついた『&lt;b&gt;ネスペ&lt;/b&gt;』シリーズをご紹介したいと思います。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-QSpKqnlrS8o/Tu_qmjiCziI/AAAAAAAAAe4/iNm63jPGJWk/s320/nespe22beta.png" width="215" /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;『ネスペ22 β版』&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;a name='more'&gt;&lt;/a&gt;この『ネスペ』シリーズは、まずβ版が（主に通販限定で）発売され、その後、増補改訂を経て正式版が全国の書店に並びます。 &lt;br /&gt;&lt;br /&gt;数あるネットワークスペシャリストの受験参考書の中でも、『ネスペ』シリーズは&lt;b&gt;試験一回分、それも午後試験のみを徹底的に解説した特殊な構成&lt;/b&gt;になっている事が大きな特徴で、表紙には以下の注意書きがあります。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;●ご購入時の注意事項&lt;br /&gt;・ 向き不向きがあります&lt;br /&gt;・ ある程度の基礎知識が必要です&lt;br /&gt;・ H22年の午後解説のみです&lt;/blockquote&gt;&lt;hr /&gt;&lt;br /&gt;個人的に読んでみて気になった点は、やはり大手出版社が発行する参考書と比較すると推敲が多少甘く、荒削りな文章が残っている事です。&lt;br /&gt;&lt;br /&gt;とはいえ、多くの参考書が前年度版の解説をそのまま使い回しているのに対して、『ネスペ』シリーズはほとんど一から書き直されており、一回の出版にかかる労力がなまなかのものではない事は容易に想像がつきます。&lt;br /&gt;&lt;br /&gt;その上、敢えて『β版』を出版するという事は、文章表現の質よりも速報性を重視する姿勢を貫いているのでしょう。&lt;br /&gt;&lt;br /&gt;そういう意味では、確かに人を選ぶ本かも知れません。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【「問題」と「設問」の2本立て&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;解説&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;『ネスペ』シリーズの特色は、設問とその解答の解説に先立ち、問題そのものの解説がなされる事です。&lt;br /&gt;&lt;br /&gt;それはまるで、完成品のジグソーパズルを丁寧に分解しながら各ピースがどう組み合わさっていくのかを一つひとつ確認する作業に似ています。&lt;br /&gt;&lt;br /&gt;独学では読解が困難な午後 II 試験の問題解説は特に有用です。読者が負担を感じないように、絶妙なタイミングで解説に移る構成は『ネスペ』シリーズの真骨頂でしょう。&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;また、問題を読んだ後、設問を理解して正解を導き出す行為は、持っている知識のピースを組み立てる作業に似ています。『ネスペ』シリーズでは、予め問題文をひととおり理解させるため、設問に入っても何を問われているのかが分かりやすい。&lt;br /&gt;&lt;br /&gt;つまり、解説を2本立て構成にする事によって、&lt;br /&gt;&lt;ul&gt;&lt;li&gt;複雑な問題を読み解く力&lt;/li&gt;&lt;li&gt;知識を組み合わせて解答を作る力&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;の 2 つを効率的に身に付ける事ができるようになっているように思えます。&lt;br /&gt;&lt;br /&gt;集中力が切れて問題文を流し読みしたり、漫然と問題を解いたりする事もなくなるでしょう。 &lt;br /&gt;&lt;br /&gt;これらは決して派手な受験テクニックではないかも知れませんが、&lt;b&gt;正面突破のための強力な武器&lt;/b&gt;にはなります。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【意外と重要、メンタル面のアプローチ】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;『ドラゴン桜』という漫画作品の中で、特進コースの担任・桜木が「東大は簡単だ」と生徒に言い聞かせます。それも自信満々に。何度も何度も。&lt;br /&gt;&lt;br /&gt;実際、あれを読んで「僕でも東大にいけるかも！」と思ってしまった方もいらっしゃると思います。&lt;br /&gt;&lt;br /&gt;同じように、『ネスペ22 β版』には、要所要所に「ネットワークスペシャリスト試験は恐くない」というメッセージが散りばめられています。&lt;br /&gt;&lt;br /&gt;暗示にかかりやすい方なら、これを&lt;b&gt;繰り返し読むだけでもネットワークスペシャリストが恐くなくなるから不思議&lt;/b&gt;。だんだん「僕でも受かりそう」と思えてきます。&lt;br /&gt;&lt;br /&gt;ネットワークスペシャリストは一応難関資格。範囲は広いし難易度は高いしで、いくら勉強しても足りないという考えに囚われがちです。&lt;br /&gt;&lt;br /&gt;でも、「午後 II なんて、ただの小問の集まり&lt;sub&gt;（p.146より）&lt;/sub&gt;」という認識があれば、必要以上に懼れる事もなくなります。言われてみれば当たり前だけど言われるまで気付かないような些細な事でも、意識の中に定着すれば非常に大きな力になります。&lt;br /&gt;&lt;br /&gt;『ネスペ』シリーズは、こうしたメンタル面のサポートが強く、不安の強い受験者を置き去りにしない配慮が感じられます。試験慣れしている方にとっては少しうるさく感じられるかも知れませんが、これも向き不向きの問題でしょう。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;というわけで。&lt;br /&gt;&lt;br /&gt;ご紹介した『ネスペ21』『ネスペ22 β版』は、ネットワークスペシャリスト受験の際に最も役に立った2冊でした。 &lt;br /&gt;&lt;br /&gt;ちなみに『ネスペ21』を初めて手に取った当時（2011年8月）は、正直「&lt;b&gt;何が書いてあるのかさっぱり解らない&lt;/b&gt;」という絶望的なレベルでした。勉強を始めてみるとあまりにも役立ちすぎて、ライバルには教えたくないと思うようになったのはここだけの話です。&lt;br /&gt;&lt;br /&gt;もし、ネットワークの午後対策で何から手をつけてよいのか判らず、途方に暮れている方がいらっしゃいましたら、書店で（もしくは通販で）チェックしてみてはいかがでしょうか。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-4822574951816899758?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/4822574951816899758/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/22.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4822574951816899758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/4822574951816899758'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/22.html' title='これなら受かる！ 『ネスペ22 （β版）』'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-QSpKqnlrS8o/Tu_qmjiCziI/AAAAAAAAAe4/iNm63jPGJWk/s72-c/nespe22beta.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-487502803234833373</id><published>2011-12-16T12:11:00.002+09:00</published><updated>2012-01-06T10:59:09.310+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ネットワークスペシャリスト'/><title type='text'>ネットワークスペシャリスト試験に合格しました</title><content type='html'>応援してくださった皆さん、本当にありがとうございました。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-0o1CIAevXzc/Tuq14tYdo4I/AAAAAAAAAew/qGj3P0dljCo/s1600/nw_result.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://2.bp.blogspot.com/-0o1CIAevXzc/Tuq14tYdo4I/AAAAAAAAAew/qGj3P0dljCo/s400/nw_result.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【1月5日追記】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;合格証書が届きました。応用情報技術者の証書と併せて。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-O5kwRKk89t4/TwZTH5bSAaI/AAAAAAAAAg8/Q5dJHk2QcKg/s1600/%25E8%25A8%25BC%25E6%259B%25B8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-O5kwRKk89t4/TwZTH5bSAaI/AAAAAAAAAg8/Q5dJHk2QcKg/s1600/%25E8%25A8%25BC%25E6%259B%25B8.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-487502803234833373?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/487502803234833373/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/blog-post.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/487502803234833373'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/487502803234833373'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/blog-post.html' title='ネットワークスペシャリスト試験に合格しました'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-0o1CIAevXzc/Tuq14tYdo4I/AAAAAAAAAew/qGj3P0dljCo/s72-c/nw_result.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-975926898505069630</id><published>2011-12-03T00:00:00.876+09:00</published><updated>2011-12-04T19:55:44.929+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Processingでかんたん3Dアニメーション</title><content type='html'>&lt;a href="http://atnd.org/events/22563"&gt;P5 Advent Calendar 2011&lt;/a&gt; 3日目を担当しました。&lt;br /&gt;&lt;br /&gt;本日のテーマは【&lt;b&gt;かんたん！ 3Dアニメーション&lt;/b&gt;】です。みんな大好き Processing で、こんなものを作ってみました。&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;iframe frameborder="0" height="380" scrolling="no" src="http://www.openprocessing.org/visuals/iframe.php?visualID=47342&amp;amp;width=400&amp;amp;height=300&amp;amp;border=true" width="428"&gt;&lt;/iframe&gt;&lt;/div&gt;段ボール製ロボット“&lt;a href="http://blog.livedoor.jp/news99vip/archives/3323319.html"&gt;&lt;b&gt;ダンボー&lt;/b&gt;&lt;/a&gt;”が、ただただ歩き続けるだけのシンプルな作品です。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【どうやって作ったの？】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;この作品は、たくさんの直方体パーツを時間の経過とともに少しずつ動かして『歩いているように』見せています。&lt;br /&gt;&lt;br /&gt;とはいえ、単純に一つひとつのパーツを無秩序に動かせばよいというわけではありません。 パーツ間の幾何構造をきちんと連携させるためにはそれなりの工夫が必要です。&lt;br /&gt;&lt;br /&gt;そこでこの記事では、 3 次元アニメーションをそれっぽく見せるためのささやかなテクニックをご紹介していこうかと思います。 &lt;br /&gt;&lt;br /&gt;記事の前半で技術解説を&lt;b&gt;適当に&lt;/b&gt;やって、後半で全ソースコードリストを公開します（ソースだけ欲しい方は記事の最後まで一気にスクロールして下さい）。&lt;br /&gt;&lt;br /&gt;技術解説は、3 次元を扱う上で基礎中の基礎となるお話から始まり、座標変換の話題からデータ構造の考察へと話が繋がります。高校レベルの簡単なベクトルや行列の概念を知っている方を想定していますが、&lt;b&gt;本文中には数式が登場しません&lt;/b&gt;ので、理系以外の方々にもご理解いただけるかと思います。モーションアニメに限らず、考えを広げるヒントになるかも知れません。&lt;br /&gt;&lt;br /&gt;また、&lt;a href="http://www.openprocessing.org/visuals/?visualID=47342"&gt;OpenProcessing の当該ページ&lt;/a&gt;からはプロジェクトを一括してダウンロードして頂けます。 &lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【3次元オブジェクトを動かす、ということ】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;突然ですが、&lt;b&gt;何があっても形状が変化しないものを、物理用語で『&lt;a href="http://ja.wikipedia.org/wiki/%E5%89%9B%E4%BD%93%E3%81%AE%E5%8A%9B%E5%AD%A6"&gt;剛体&lt;/a&gt;』と言います&lt;/b&gt;。&lt;br /&gt;&lt;br /&gt;作品中のダンボーはすべて剛体のパーツでできています。&lt;br /&gt;&lt;br /&gt;さてさて。剛体の動かし方は以下の 2 種類に分けられます。&lt;br /&gt;&lt;ul&gt;&lt;li&gt;並進移動　（物体のすべての頂点が、同じ方向に同じ量だけ移動すること）&lt;/li&gt;&lt;li&gt;回転移動　（物体のすべての頂点が、同じ軸&lt;sup&gt;※&lt;/sup&gt;のまわりを同じ角度だけ回転すること）&lt;/li&gt;&lt;/ul&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ 回転軸は必ずしも &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt;, &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;y&lt;/span&gt;&lt;/i&gt;, &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;z&lt;/span&gt;&lt;/i&gt; いずれかに一致している必要はなく、任意の（単位）ベクトルを回転軸として設定する事ができます（下図参照）。&lt;/span&gt;&lt;/blockquote&gt;視覚化するとこんな感じです。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-8_Pw0tOY2C4/TteEEu6xswI/AAAAAAAAAbA/MYAZfeDO7Qc/s1600/translateSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="287" src="http://1.bp.blogspot.com/-8_Pw0tOY2C4/TteEEu6xswI/AAAAAAAAAbA/MYAZfeDO7Qc/s400/translateSample.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;並進移動&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-flkn4wYzzxY/TteNfMaUxkI/AAAAAAAAAbQ/RgX37Z3up3E/s1600/rotateSample2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="291" src="http://2.bp.blogspot.com/-flkn4wYzzxY/TteNfMaUxkI/AAAAAAAAAbQ/RgX37Z3up3E/s400/rotateSample2.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;回転移動&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;繰り返しになりますが、どんなに複雑な移動でも上記の 2 つの移動を合成する事で実現できます。&lt;br /&gt;&lt;br /&gt;本質はたったこれだけなのですが、座標軸をどう設定するかによって移動に関する制御のしやすさが全然違ってきます。&lt;br /&gt;&lt;br /&gt;次節では、そこらへんの処理を都合よく行うために 2 つの座標系を導入する事にしましょう。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【世界座標とローカル座標】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;ざっくり言ってしまえば、『ローカル座標』とは移動する物体に常にくっついていく座標系であり、『世界座標』とは決して変化しない座標系の事です。&lt;br /&gt;&lt;br /&gt;解釈としては、“（僕から見て）前後左右”のように極めて局所的で自己中心的な位置表現が『ローカル座標系』、“地球上の緯度経度”のような大局的で一意性のある位置表現が『世界座標系』といったところでしょうか。&lt;br /&gt;&lt;br /&gt;したがって、頭や胴体などそれぞれのパーツが独自のローカル座標を持つのに対して、世界座標は一つの 3 次元空間に一つしかありません（下図は頭部パーツのローカル座標と世界座標の対比。面倒なので他のパーツは省略）。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-JAIv3e50yV8/Tte36yoF9HI/AAAAAAAAAdA/5_4IWQGZoaE/s1600/CoordinateSystemSample1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://1.bp.blogspot.com/-JAIv3e50yV8/Tte36yoF9HI/AAAAAAAAAdA/5_4IWQGZoaE/s400/CoordinateSystemSample1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&amp;nbsp;↓&lt;br /&gt;（ &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;t&lt;/span&gt;&lt;/i&gt; 秒後）&lt;br /&gt;↓&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-5Kk451LURsk/Tte4ClrOxKI/AAAAAAAAAdI/uFwMR4RA9XQ/s1600/CoordinateSystemSample2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="297" src="http://4.bp.blogspot.com/-5Kk451LURsk/Tte4ClrOxKI/AAAAAAAAAdI/uFwMR4RA9XQ/s400/CoordinateSystemSample2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;上図からも判る通り、パーツがどれだけ並進や回転移動を繰り返しても、そのローカル座標系における &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt; 軸は常に剛体の右側を向き続けますし、&lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;z&lt;/span&gt;&lt;/i&gt; 軸は剛体の後方向を向き続けます。したがって、ローカル座標系を使えばパーツ同士の&lt;b&gt;相対的な位置関係を容易に定義する事ができる&lt;/b&gt;のです（たとえば、胴体が移動しても、右腕は常に胴体から見て“右側”に在り続ける事ができます）。&lt;br /&gt;&lt;br /&gt;一方、&lt;b&gt;世界座標系はパーツがどれだけ移動を繰り返しても何の影響も受けません&lt;/b&gt;から、あらゆるパーツの絶対的な位置を定義する唯一の基準として使用する事ができます。 &lt;br /&gt;&lt;br /&gt;なお、ローカル座標と世界座標は相互変換する事ができます。その理論的な背景は &lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/09/processing_27.html"&gt;9 月 27 の日記&lt;/a&gt;で簡単に紹介していますので、興味のある方はご覧下さい。&lt;br /&gt;&lt;br /&gt;先に述べた剛体の 2 つの移動のうち、並進に関しては &lt;a href="http://processing.org/reference/translate_.html" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;translate()&lt;/a&gt; メソッドが用意されています。座標系に注意しさえすればさほど難しい事ではありませんので、特に説明は不要でしょう。&lt;br /&gt;&lt;br /&gt;ただし、回転移動に関してはさらなる補足が必要かも知れません。次節でご紹介します。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【回転 - ヘディング・ピッチ・バンク】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;3 次元剛体のあらゆる回転移動は、ローカル座標系における 3 軸（&lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt;、&lt;i style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;y&lt;/i&gt;、&lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;z&lt;/span&gt;&lt;/i&gt; 軸）まわりの回転に分解することができます。逆に言えば、&lt;b&gt;3 軸まわりの回転の合成で、あらゆる回転移動を表す事ができる&lt;/b&gt;という事です。これを、『&lt;b&gt;&lt;a href="http://ja.wikipedia.org/wiki/%E3%82%AA%E3%82%A4%E3%83%A9%E3%83%BC%E8%A7%92"&gt;オイラー角&lt;/a&gt;表現&lt;/b&gt;』と呼びます。&lt;br /&gt;&lt;br /&gt;たとえば、先ほど例示した回転移動 ――&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-flkn4wYzzxY/TteNfMaUxkI/AAAAAAAAAbQ/RgX37Z3up3E/s1600/rotateSample2.png"&gt;&lt;img border="0" height="232" src="http://2.bp.blogspot.com/-flkn4wYzzxY/TteNfMaUxkI/AAAAAAAAAbQ/RgX37Z3up3E/s320/rotateSample2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;―― は、以下のように分解できます（下図中の水色の破線は回転軸）。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-hYpbEG2jpOE/TteS8d-nGvI/AAAAAAAAAb4/0waH6WcSyt4/s1600/rotateSample6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="240" src="http://2.bp.blogspot.com/-hYpbEG2jpOE/TteS8d-nGvI/AAAAAAAAAb4/0waH6WcSyt4/s320/rotateSample6.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-meU9RTjDy28/TteTAod492I/AAAAAAAAAcA/dGYyS-BWbE8/s1600/rotateSample7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="233" src="http://4.bp.blogspot.com/-meU9RTjDy28/TteTAod492I/AAAAAAAAAcA/dGYyS-BWbE8/s320/rotateSample7.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-PIcxoiKK3Y4/TteTErKDDkI/AAAAAAAAAcI/LRcNvbjVUx0/s1600/rotateSample8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="233" src="http://4.bp.blogspot.com/-PIcxoiKK3Y4/TteTErKDDkI/AAAAAAAAAcI/LRcNvbjVUx0/s320/rotateSample8.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;Processing では、&lt;a href="http://processing.org/reference/rotateX_.html" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;rotateX()&lt;/a&gt;、&lt;a href="http://processing.org/reference/rotateY_.html" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;rotateY()&lt;/a&gt;、&lt;a href="http://processing.org/reference/rotateZ_.html" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;rotateZ()&lt;/a&gt; というメソッドが基本軸まわりの回転移動をサポートしており、それらの合成で大概の回転移動を実現する事が可能です。&lt;br /&gt;&lt;br /&gt;注意点としては、たとえ各軸まわりの回転量が同じでも、合成する順序が変わると得られる結果は異なったものになってしまいます。数学的に解釈するならば、行列の積演算が交換法則を持たない事と一緒です。したがって、剛体の回転移動を一意に表すには各軸まわりの回転量だけでなく、それらを&lt;b&gt;合成する際の順序も定義せねばなりません&lt;/b&gt;。&lt;br /&gt;&lt;br /&gt;各軸まわりの回転を合成する順序には、『&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0"&gt;ロール&lt;/a&gt;・&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%94%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0"&gt;ピッチ&lt;/a&gt;・&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%A8%E3%83%BC%E3%82%A4%E3%83%B3%E3%82%B0"&gt;ヨー&lt;/a&gt;』や『&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%A8%E3%83%BC%E3%82%A4%E3%83%B3%E3%82%B0"&gt;ヘディング&lt;/a&gt;・&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%94%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0"&gt;ピッチ&lt;/a&gt;・&lt;a href="http://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0"&gt;バンク&lt;/a&gt;』など、代表的なものが幾つかあります。この作品では、回転の合成順序を&lt;b&gt;ヘディング → ピッチ → バンク&lt;/b&gt;（下図参照）で統一しています。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-XQn4y6ltsOk/Tteq0FAsgxI/AAAAAAAAAc4/CFRmMUhhEDM/s1600/startSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="238" src="http://1.bp.blogspot.com/-XQn4y6ltsOk/Tteq0FAsgxI/AAAAAAAAAc4/CFRmMUhhEDM/s320/startSample.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;初期状態&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-dkjnRilnWHY/Ttej5REQkDI/AAAAAAAAAcQ/0G9Ny3tlqZg/s1600/headingSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="238" src="http://4.bp.blogspot.com/-dkjnRilnWHY/Ttej5REQkDI/AAAAAAAAAcQ/0G9Ny3tlqZg/s320/headingSample.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;ヘディング（ &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;y&lt;/span&gt;&lt;/i&gt; 軸まわり）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ELo1_ZGlUPM/Ttelidu-sPI/AAAAAAAAAcg/nqoq3qU7nzc/s1600/pitchSample_r.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="238" src="http://4.bp.blogspot.com/-ELo1_ZGlUPM/Ttelidu-sPI/AAAAAAAAAcg/nqoq3qU7nzc/s320/pitchSample_r.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;ピッチ（ &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt; 軸まわり）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-x1KchbTkhUw/Ttep-mxVxvI/AAAAAAAAAco/vgSzgclLEbo/s1600/bankSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="238" src="http://4.bp.blogspot.com/-x1KchbTkhUw/Ttep-mxVxvI/AAAAAAAAAco/vgSzgclLEbo/s320/bankSample.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;バンク（ &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;z&lt;/span&gt;&lt;/i&gt; 軸まわり）&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-I4yndPXG_WY/TteqfHOk9vI/AAAAAAAAAcw/L4EFAHAfx9o/s1600/finishSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="238" src="http://3.bp.blogspot.com/-I4yndPXG_WY/TteqfHOk9vI/AAAAAAAAAcw/L4EFAHAfx9o/s320/finishSample.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;合成完了&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;先に述べた通りにローカル座標系が定義された場合、ヘディングは &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;y&lt;/span&gt;&lt;/i&gt; 軸まわりの回転、ピッチは &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;x&lt;/span&gt;&lt;/i&gt; 軸まわりの回転、そしてバンクは &lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;z&lt;/span&gt;&lt;/i&gt; 軸まわりの回転になります。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【ダンボーのためのデータ構造】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;ここまでは、一つのパーツのみに着目して解説を行なってきました。 &lt;br /&gt;&lt;br /&gt;しかし、ダンボーは複数のパーツの組み合わせでできています。これらの部品はひとまとめにして扱いたいものですが、私たちがよく知っている配列&lt;sup&gt;※&lt;/sup&gt;ではダンボーの身体構造をうまく表現できません。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ そもそも配列は、同類のオブジェクト群に番号（順序）をつけて管理するためのデータ構造です。オブジェクトごとに何らかの階層関係がある場合は、必ずしも適切なデータ構造とは言えません。&lt;/span&gt;&lt;/blockquote&gt;この作品では 2 種類のデータ構造を用いています。&lt;br /&gt;&lt;br /&gt;1 つは各パーツの物理的な親子関係を表す『&lt;b&gt;&lt;a href="http://ja.wikipedia.org/wiki/%E6%9C%A8%E6%A7%8B%E9%80%A0_%28%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0%29"&gt;木構造&lt;/a&gt;&lt;/b&gt;』で、もう 1 つは『&lt;b&gt;&lt;a href="http://ja.wikipedia.org/wiki/%E9%80%A3%E6%83%B3%E9%85%8D%E5%88%97"&gt;連想配列&lt;/a&gt;&lt;/b&gt;』です。&lt;br /&gt;&lt;br /&gt;木構造における各ノードにはパーツの幾何形状や親子間の相対的な位置関係、および前述の角変位が格納されており、親ノードの位置が変わると子ノードもその影響を受けます（&lt;a href="http://en.wikipedia.org/wiki/Scene_graph"&gt;シーングラフ&lt;/a&gt;に近い構造です）。 &lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-iJmIP5_BMmc/TtfBlfy_rRI/AAAAAAAAAdQ/OJw7ooU_0As/s1600/TreeStructureSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-iJmIP5_BMmc/TtfBlfy_rRI/AAAAAAAAAdQ/OJw7ooU_0As/s320/TreeStructureSample.png" width="174" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;胴体をルートとした木構造の概念図: なんかこわくなった&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;このような構造を導入する利点は、Processing に備わっている&lt;a href="http://processing.org/reference/pushMatrix_.html"&gt;行列スタック&lt;/a&gt;を巧妙に用いて再帰的にレンダリングを行える事や、一つひとつのパーツの位置を（それらの親ノードにおける）ローカル座標空間から見た位置で定義でき&lt;sup&gt;※&lt;/sup&gt;、扱いが容易になる事などが挙げられます。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ 繰り返しになりますが、こうする事で『右腕は胴体から見て常に右側、左腕は常に左側』という相対記述が可能になります。&lt;/span&gt;&lt;/blockquote&gt;残念なことに Processing は木構造をサポートしていないため、自力で実装する必要があります。このような場合の常套的なテクニックは、木構造のノードを、子ノードの参照リストを持つ&lt;a href="http://ja.wikipedia.org/wiki/%E5%86%8D%E5%B8%B0%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B"&gt;&lt;b&gt;自己参照クラス&lt;/b&gt;&lt;/a&gt;として定義することです（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Node&lt;/span&gt; クラスのソースコード参照）。これにより、ルートノードから全ての子ノードに対する階層的な参照関係が生じますから、木構造の再現は容易であると言えるでしょう。&lt;br /&gt;&lt;br /&gt;一方、連想配列は各パーツを階層関係から切り離して（つまり単体で）扱いやすくするために用意しました。たとえば、歩行時の腕の振りをプログラムしたい場合は、腕のみに注目したいですね。そこで、腕パーツをすぐさま参照できるように、パーツの（一意な）名称とパーツのデータを紐付けて連想配列に格納しておくのです。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-URnKJ0hBpJU/TtfByR-5-qI/AAAAAAAAAdY/SswyYKnb4TM/s1600/HashMapStructureSample.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="340" src="http://1.bp.blogspot.com/-URnKJ0hBpJU/TtfByR-5-qI/AAAAAAAAAdY/SswyYKnb4TM/s400/HashMapStructureSample.png" width="400" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;連想配列の概念図: 識別用の名称から、ただちに対応するパーツを参照できる&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;連想配列は Processing で用意されている &lt;a href="http://processing.org/reference/HashMap.html" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;HashMap&lt;/a&gt; を利用すれば簡単です。&lt;br /&gt;&lt;br /&gt;なお、描画すべきパーツは総て木構造に格納する必要があるのに対して、連想配列には必ずしもすべてのパーツを格納する必要はありません。また、木構造の“ノード”と連想配列の“要素”は、どちらも同じオブジェクトを参照しています。ですので、連想配列経由でアクセスしたオブジェクトに何らかの変更を加えたからといって、対応する木構造のノードと整合しなくなる心配はありません。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【最後のしあげ：足を地に着けるために】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;胴体パーツを木構造のルートに設定してしまったため、他のパーツは胴体を中心に動く事になります。このままアニメーションさせるとまるで宙ぶらりんですので、きちんとダンボーの足を地に着けてやらねばなりません。&lt;br /&gt;&lt;br /&gt;この作品では、レンダリング直前にダンボーの頂点を世界座標に変換した後、“最も下にある頂点”の値を調べ、それが 0 になるようにモデル全体を鉛直方向に並進させています。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-nEHZJGOLyCQ/TtjawqoWuQI/AAAAAAAAAeY/xKTN8HhLfUc/s1600/DanboSample001.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="194" src="http://2.bp.blogspot.com/-nEHZJGOLyCQ/TtjawqoWuQI/AAAAAAAAAeY/xKTN8HhLfUc/s320/DanboSample001.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;地面にめり込んだダンボー&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&amp;nbsp;↓&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-ku-r262RQs4/Ttja1F8778I/AAAAAAAAAeg/li1UQu7b3gg/s1600/DanboSample002.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="194" src="http://4.bp.blogspot.com/-ku-r262RQs4/Ttja1F8778I/AAAAAAAAAeg/li1UQu7b3gg/s320/DanboSample002.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;適切な高さに並進移動&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;↓&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-NZs75Vh29nI/Ttja6bn7V_I/AAAAAAAAAeo/llYQivQsXqQ/s1600/DanboSample003.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" height="193" src="http://3.bp.blogspot.com/-NZs75Vh29nI/Ttja6bn7V_I/AAAAAAAAAeo/llYQivQsXqQ/s320/DanboSample003.png" width="320" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;完成&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&amp;nbsp;各パーツのローカル座標から世界座標への変換は &lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/09/processing_27.html"&gt;9 月 27 の日記&lt;/a&gt;で述べた通りです。ただし、各頂点を鉛直軸（&lt;i&gt;&lt;span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,serif;"&gt;y&lt;/span&gt;&lt;/i&gt;軸）の要素のみで比較するために &lt;a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/util/Comparator.html"&gt;Comparator&lt;/a&gt; をカスタマイズしました（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Node&lt;/span&gt; クラスのソースコード 99 行目 - 104 行目参照）。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【技術解説あとがき】 &lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Processing Advent Calendar 2011 3日目はいかがでしたでしょうか。 &lt;br /&gt;&lt;br /&gt;実は、参加表明をしてからというもの、題材選びには大いに悩む日々が続きました。『銀河鉄道を走らせよう』とか『クリスマスイルミネーションを作ろう』とかのアイディアは&lt;b&gt;ことごとくボツ&lt;/b&gt;になりました。&lt;br /&gt;&lt;br /&gt;〆切直前にがんばって見つけ出した題材ですので、楽しんで頂けたら非常に嬉しいです。&lt;br /&gt;&lt;br /&gt;さて、プログラム自体は一晩で書き上げたものの、ノウハウを文章化する事が殊のほか難しく、やむなく端折ってしまったトピックがいくつかあります。&lt;br /&gt;&lt;br /&gt;特に&lt;b&gt;回転に関してはいくら説明しても足りない&lt;/b&gt;くらいです。簡単のために本文中では言及を避けましたが、ある角変位を表現する式には一意性がありません。また、オイラー角表現は原理上、&lt;a href="http://en.wikipedia.org/wiki/Gimbal_lock"&gt;ジンバルロック&lt;/a&gt;という弱点を抱えています。&lt;br /&gt;&lt;br /&gt;この作品を改造する場合は、念のためこうした技術上の問題がある点にご留意ください&lt;sup&gt;※&lt;/sup&gt;。&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;&lt;span style="font-size: x-small;"&gt;※ ジンバルロック問題の解消には&lt;a href="http://ja.wikipedia.org/wiki/%E5%9B%9B%E5%85%83%E6%95%B0"&gt;四元数&lt;/a&gt;という&lt;b&gt;虚数のバケモノみたいなやつ&lt;/b&gt;を使います。この話題はどんなに駆け足で説明しても長くなってしまうのでまたの機会に。&lt;/span&gt;&lt;/blockquote&gt;それでは皆様、&lt;span style="font-family: Verdana,sans-serif;"&gt;Let's Enjoy Processing!&lt;/span&gt; ∩( ・ω・)∩&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【おまちかねソースコード】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;…といきたいところですが、今回の作品は&lt;b&gt;ソースコードをコピペしただけでは動きません&lt;/b&gt;。&lt;br /&gt;&lt;br /&gt;ダンボーのモデルにマッピングするテクスチャが必要です。以下のテクスチャ画像に、それぞれ【d_tab.jpg】 【d_face.jpg】 【d_coin.jpg】 【d_normal.jpg】 と名前をつけて、data フォルダに保存して下さい。&lt;br /&gt;&lt;br /&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-qPTJnALec38/TtfFum8I1hI/AAAAAAAAAd0/fipZ2imwUZ8/s1600/d_tab.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-qPTJnALec38/TtfFum8I1hI/AAAAAAAAAd0/fipZ2imwUZ8/s1600/d_tab.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;d_tab.jpg&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-TtHksJdKq0I/TtfFskmtRcI/AAAAAAAAAdk/B3yYCaa9RjU/s1600/d_face.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-TtHksJdKq0I/TtfFskmtRcI/AAAAAAAAAdk/B3yYCaa9RjU/s1600/d_face.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;d_face.jpg&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-YlcNNbxTJ-4/TtfFsCxIfwI/AAAAAAAAAdg/nJ4hXON_8DI/s1600/d_coin.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/-YlcNNbxTJ-4/TtfFsCxIfwI/AAAAAAAAAdg/nJ4hXON_8DI/s1600/d_coin.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;d_coin.jpg&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-xXV3AYg_9rE/TtfFtK7r9wI/AAAAAAAAAdw/ZVm3qTQnRY4/s1600/d_normal.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-xXV3AYg_9rE/TtfFtK7r9wI/AAAAAAAAAdw/ZVm3qTQnRY4/s1600/d_normal.jpg" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;d_normal.jpg&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;これで準備ができました。あとは以下のプログラムを&lt;b&gt;すべて&lt;/b&gt; Processing IDE にコピー &amp;amp; ペーストすれば、ダンボーが動き出します。たぶん。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;【メイン】&lt;/b&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;final int BACKGROUND_COLOR = 0xFFFFEEBB; // 背景色&lt;br /&gt;Danbo danbo;                             // ダンボー&lt;br /&gt;float time;                              // 経過時間の制御用変数&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ========================================&lt;br /&gt;// 初期化&lt;br /&gt;// ========================================&lt;br /&gt;void setup() {&lt;br /&gt;  size(400, 300, P3D);  &lt;br /&gt;  noStroke();  &lt;br /&gt;  &lt;br /&gt;  danbo = new Danbo(); &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// ========================================&lt;br /&gt;// 描画&lt;br /&gt;// ========================================&lt;br /&gt;void draw() {&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // ライトが常に正面から当たるように設定&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  camera(); lights();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // カウンタの更新&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  float angle = radians(time += .05);&lt;br /&gt;  if(angle &amp;gt;= TWO_PI) angle = time = 0;&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // カメラの設置&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  camera(700 * sin(angle), -600, -700 * cos(angle), &lt;br /&gt;         0,                -300,  0, &lt;br /&gt;         0,                 1,    0);&lt;br /&gt;  &lt;br /&gt;  background(BACKGROUND_COLOR);&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // 歩行モーションの更新&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  danbo.update();&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // リフレクション（おまけ）&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  pushMatrix();&lt;br /&gt;  applyMatrix(1.0,  0.0, 0.0, 0.0,&lt;br /&gt;              0.0, -1.0, 0.0, 0.0,&lt;br /&gt;              0.0,  0.0, 1.0, 0.0,&lt;br /&gt;              0.0,  0.0, 0.0, 1.0);&lt;br /&gt;  danbo.render();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  camera();&lt;br /&gt;  pushStyle();&lt;br /&gt;  fill(0xBB &amp;lt;&amp;lt; 24 | 0xFFFFFF &amp;amp; BACKGROUND_COLOR);&lt;br /&gt;  rect(0, 0, width, height);&lt;br /&gt;  popStyle();&lt;br /&gt;  popMatrix();&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // ダンボーの描画&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  fill(0xFFFFFFFF);&lt;br /&gt;  danbo.render();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Node&lt;/span&gt; クラス】&lt;/b&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;class Node {&lt;br /&gt;  public PVector pos;   // 親からの相対位置&lt;br /&gt;  public PVector org;   // 回転の原点座標&lt;br /&gt;  public PVector size;  // 幅、高さ、奥行き&lt;br /&gt;  public PVector rot;   // 各軸まわりの回転角&lt;br /&gt;  &lt;br /&gt;  private List&amp;lt;Node&amp;gt;          child;        // 子ノードの参照&lt;br /&gt;  private Map&amp;lt;String, PImage&amp;gt; texMap;       // テクスチャ&lt;br /&gt;  private PVector[]           localCoords;  // 頂点のローカル座標&lt;br /&gt;  private PVector[]           globalCoords; // 頂点のグローバル座標&lt;br /&gt;  &lt;br /&gt;  private final int NUM_VERTICES   = 8;  // 頂点数&lt;br /&gt;  private final int NUM_PARTITIONS = 5;  // 分割数&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // ========================================&lt;br /&gt;  public Node() {&lt;br /&gt;    pos          = new PVector(0, 0, 0);&lt;br /&gt;    org          = new PVector(0, 0, 0);&lt;br /&gt;    size         = new PVector(1, 1, 1);&lt;br /&gt;    rot          = new PVector(0, 0, 0);&lt;br /&gt;    &lt;br /&gt;    child        = new ArrayList&amp;lt;Node&amp;gt;();&lt;br /&gt;    texMap       = new HashMap&amp;lt;String, PImage&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    localCoords  = new PVector[NUM_VERTICES];&lt;br /&gt;    globalCoords = new PVector[NUM_VERTICES];&lt;br /&gt;    &lt;br /&gt;    for(int i = 0; i &amp;lt; NUM_VERTICES; ++i) {&lt;br /&gt;      localCoords [i] = new PVector();&lt;br /&gt;      globalCoords[i] = new PVector();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // 子要素追加&lt;br /&gt;  // ========================================&lt;br /&gt;  public void addChild(Node child) {&lt;br /&gt;    this.child.add(child);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // テクスチャ設定&lt;br /&gt;  // ========================================&lt;br /&gt;  public void applyTexture(String name, PImage tex) {&lt;br /&gt;    this.texMap.put(name, tex);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // 最も地面に近い頂点の y 座標値を得る&lt;br /&gt;  // ========================================&lt;br /&gt;  public float getGlobalMaxYCoord() {&lt;br /&gt;    return getGlobalMaxYCoord(Float.MIN_VALUE);&lt;br /&gt;  }&lt;br /&gt;  private float getGlobalMaxYCoord(float maxY) {&lt;br /&gt;    float ret = maxY;&lt;br /&gt;    &lt;br /&gt;    // 箱のローカル頂点をセット&lt;br /&gt;    localCoords[0].set(-.5*size.x, -.5*size.y, -.5*size.z);&lt;br /&gt;    localCoords[1].set(-.5*size.x, -.5*size.y,  .5*size.z);&lt;br /&gt;    localCoords[2].set( .5*size.x, -.5*size.y,  .5*size.z);&lt;br /&gt;    localCoords[3].set( .5*size.x, -.5*size.y, -.5*size.z);&lt;br /&gt;    localCoords[4].set(-.5*size.x,  .5*size.y, -.5*size.z);&lt;br /&gt;    localCoords[5].set(-.5*size.x,  .5*size.y,  .5*size.z);&lt;br /&gt;    localCoords[6].set( .5*size.x,  .5*size.y,  .5*size.z);&lt;br /&gt;    localCoords[7].set( .5*size.x,  .5*size.y, -.5*size.z);    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // ワールド行列の計算&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    pushMatrix();&lt;br /&gt;    &lt;br /&gt;    // 並進&lt;br /&gt;    translate(pos.x, pos.y, pos.z);&lt;br /&gt;    &lt;br /&gt;    // 回転&lt;br /&gt;    translate(org.x, org.y, org.z);&lt;br /&gt;    rotateY(rot.y); rotateX(rot.x); rotateZ(rot.z);&lt;br /&gt;    translate(-org.x, -org.y, -org.z);&lt;br /&gt;    &lt;br /&gt;    // ワールド行列を計算&lt;br /&gt;    PMatrix3D worldMat = (PMatrix3D)getMatrix();&lt;br /&gt;    worldMat.preApply(iViewMat);&lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // ローカル座標から世界座標に変換&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    for(int i = 0; i &amp;lt; NUM_VERTICES; i++)&lt;br /&gt;      worldMat.mult(localCoords[i], globalCoords[i]);&lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // y座標値で世界座標配列をソート（降順）&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    Arrays.sort(globalCoords, new java.util.Comparator() {&lt;br /&gt;      public int compare(Object p1, Object p2) {&lt;br /&gt;        return ((PVector)p1).y == ((PVector)p2).y ?  0 :&lt;br /&gt;               ((PVector)p1).y &amp;gt;  ((PVector)p2).y ? -1 : 1;&lt;br /&gt;      }&lt;br /&gt;    });&lt;br /&gt;    // 配列の先頭には最もy座標の大きなベクトルが格納されている&lt;br /&gt;    float tmp = globalCoords[0].y;&lt;br /&gt;    &lt;br /&gt;    if(ret &amp;lt; tmp) ret = tmp;&lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 再帰的に木をトラバース&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    for(Node n : child) {&lt;br /&gt;      tmp = n.getGlobalMaxYCoord(ret);&lt;br /&gt;      if (ret &amp;lt; tmp) ret = tmp;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    popMatrix();&lt;br /&gt;    return ret;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // ノードのレンダリング&lt;br /&gt;  // ========================================&lt;br /&gt;  void render() {&lt;br /&gt;    pushMatrix();&lt;br /&gt;    &lt;br /&gt;    // 並進&lt;br /&gt;    translate(pos.x, pos.y, pos.z);&lt;br /&gt;    &lt;br /&gt;    // 回転&lt;br /&gt;    translate(org.x, org.y, org.z);&lt;br /&gt;    rotateY(rot.y); rotateX(rot.x); rotateZ(rot.z);&lt;br /&gt;    translate(-org.x, -org.y, -org.z);&lt;br /&gt;    &lt;br /&gt;    float dx = size.x / NUM_PARTITIONS;&lt;br /&gt;    float dz = size.z / NUM_PARTITIONS;&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 前面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    PImage tex = texMap.get("FRONT");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, -.5 * size.y, -.5 * size.z, texdx * i, 0);&lt;br /&gt;        vertex(.5 * size.x - i * dx,  .5 * size.y, -.5 * size.z, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, -.5 * size.y, -.5 * size.z);&lt;br /&gt;        vertex(.5 * size.x - i * dx,  .5 * size.y, -.5 * size.z);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 背面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    tex= texMap.get("BACK");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(-.5 * size.x + i * dx, -.5 * size.y, .5 * size.z, texdx * i, 0);&lt;br /&gt;        vertex(-.5 * size.x + i * dx,  .5 * size.y, .5 * size.z, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(-.5 * size.x + i * dx, -.5 * size.y, .5 * size.z);&lt;br /&gt;        vertex(-.5 * size.x + i * dx,  .5 * size.y, .5 * size.z);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 天面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    tex= texMap.get("TOP");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, -.5 * size.y,  .5 * size.z, texdx * i, 0);&lt;br /&gt;        vertex(.5 * size.x - i * dx, -.5 * size.y, -.5 * size.z, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, -.5 * size.y,  .5 * size.z);&lt;br /&gt;        vertex(.5 * size.x - i * dx,  .5 * size.y, -.5 * size.z);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 底面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    tex= texMap.get("BOTTOM");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, .5 * size.y,  .5 * size.z, texdx * i, 0);&lt;br /&gt;        vertex(.5 * size.x - i * dx, .5 * size.y, -.5 * size.z, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x - i * dx, .5 * size.y,  .5 * size.z);&lt;br /&gt;        vertex(.5 * size.x - i * dx, .5 * size.y, -.5 * size.z);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 左面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    tex= texMap.get("LEFT");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(-.5 * size.x, -.5 * size.y, -.5 * size.z + i * dz, texdx * i, 0);&lt;br /&gt;        vertex(-.5 * size.x,  .5 * size.y, -.5 * size.z + i * dz, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(-.5 * size.x, -.5 * size.y, -.5 * size.z + i * dz);&lt;br /&gt;        vertex(-.5 * size.x,  .5 * size.y, -.5 * size.z + i * dz);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 右面の描画&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    tex= texMap.get("RIGHT");&lt;br /&gt;    if(tex == null) tex = texMap.get("DEFAULT");&lt;br /&gt;    &lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    if(tex != null) {&lt;br /&gt;      float texdx = (float)(tex.width-1) / NUM_PARTITIONS;&lt;br /&gt;      texture(tex);&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x, -.5 * size.y, .5 * size.z - i * dz, texdx * i, 0);&lt;br /&gt;        vertex(.5 * size.x,  .5 * size.y, .5 * size.z - i * dz, texdx * i, tex.height-1);&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      for(int i = 0; i &amp;lt;= NUM_PARTITIONS; ++i) {&lt;br /&gt;        vertex(.5 * size.x, -.5 * size.y, .5 * size.z - i * dz);&lt;br /&gt;        vertex(.5 * size.x,  .5 * size.y, .5 * size.z - i * dz);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 再帰的に木をトラバース&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    for(Node n : child) {&lt;br /&gt;      n.render();&lt;br /&gt;    }&lt;br /&gt;    popMatrix();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Danbo&lt;/span&gt; クラス】&lt;/b&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;Map&amp;lt;String, PImage&amp;gt; imgMap;    // 同じ画像を何度もロードしないようにハッシュで管理&lt;br /&gt;PMatrix3D           iViewMat;  // カメラ逆行列&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class Danbo {&lt;br /&gt;  private Map&amp;lt;String, Node&amp;gt; bodyMap;   // ボディパーツ用のハッシュ&lt;br /&gt;  private Node              bodyTree;  // ボディパーツ用の木構造（ルートノード）&lt;br /&gt;  private int               time;      // 経過時間の管理用変数&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // コンストラクタ&lt;br /&gt;  // ========================================&lt;br /&gt;  public Danbo() {&lt;br /&gt;    createBody();&lt;br /&gt;    time = 0;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // 歩行モーションの更新&lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  // パーツごとに回転量を再設定しているだけだよ&lt;br /&gt;  // ========================================&lt;br /&gt;  public void update() {&lt;br /&gt;    float angle = radians(++time * 4);&lt;br /&gt;    if(angle &amp;gt;= TWO_PI) angle = time = 0;&lt;br /&gt;    &lt;br /&gt;    float sinAngle = sin(angle);&lt;br /&gt;    float cosAngle = cos(angle);&lt;br /&gt;&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 胴の振り&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float BODY_AMPLITUDE = radians(1);  // 振り幅の大きさ（胴を振る角度）&lt;br /&gt;&lt;br /&gt;    Node body = bodyMap.get("BODY");&lt;br /&gt;    body.rot.set(0, 0, BODY_AMPLITUDE * cosAngle);&lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 頭の振り&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float HEAD_AMPLITUDE_X = radians(5);  // 振り幅の大きさ（頭を前後に振る角度）&lt;br /&gt;    final float HEAD_AMPLITUDE_Y = radians(2);  // 　　　〃　　　（ 〃 左右　　〃　　）&lt;br /&gt;&lt;br /&gt;    Node head = bodyMap.get("HEAD");&lt;br /&gt;    head.rot.set(HEAD_AMPLITUDE_X * abs(sinAngle), HEAD_AMPLITUDE_Y * sinAngle, 0);&lt;br /&gt;&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 腕の振り&lt;br /&gt;    // ---------------------------------------&lt;br /&gt;    final float ARM_AMPLITUDE = radians(40);  // 振り幅の大きさ（腕を振る角度）&lt;br /&gt;    &lt;br /&gt;    Node leftArm = bodyMap.get("LEFTARM");&lt;br /&gt;    leftArm.rot.set(ARM_AMPLITUDE * sinAngle, 0, 0);&lt;br /&gt;    &lt;br /&gt;    Node rightArm = bodyMap.get("RIGHTARM");&lt;br /&gt;    rightArm.rot.set(-ARM_AMPLITUDE * sinAngle, 0, 0);&lt;br /&gt;  &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 脚の動き&lt;br /&gt;    // ----------------------------------------    &lt;br /&gt;    final float LEG_AMPLITUDE = radians(30);  // 振り幅の大きさ（脚を振る角度）&lt;br /&gt;    &lt;br /&gt;    Node leftLeg = bodyMap.get("LEFTLEG");&lt;br /&gt;    leftLeg.rot.set(-LEG_AMPLITUDE * sinAngle, 0, 0);&lt;br /&gt;    &lt;br /&gt;    Node rightLeg = bodyMap.get("RIGHTLEG");&lt;br /&gt;    rightLeg.rot.set(LEG_AMPLITUDE * sinAngle, 0, 0);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // ダンボーのレンダリング&lt;br /&gt;  // ========================================&lt;br /&gt;  public void render() {&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // カメラ行列を取得し、その逆行列を得る&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    iViewMat = (PMatrix3D)getMatrix();&lt;br /&gt;    iViewMat.invert();&lt;br /&gt;    &lt;br /&gt;    // 脚が地面に着くよう、y 方向の並進量を求める&lt;br /&gt;    float shiftY = bodyTree.getGlobalMaxYCoord();&lt;br /&gt;    &lt;br /&gt;    pushMatrix();&lt;br /&gt;    translate(0, -shiftY, 0);&lt;br /&gt;    &lt;br /&gt;    bodyTree.render();&lt;br /&gt;    &lt;br /&gt;    popMatrix();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ----------------------------------------&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // 画像ファイルを一括ロードして、imgMapで一元管理します&lt;br /&gt;  // これによって、同じ画像を何度もロードしなくてよくなります&lt;br /&gt;  // ========================================&lt;br /&gt;  private void createImageMap() {&lt;br /&gt;    if(imgMap == null) imgMap = new HashMap&amp;lt;String, PImage&amp;gt;();&lt;br /&gt;    &lt;br /&gt;    if(!imgMap.containsKey("NORMAL")) imgMap.put("NORMAL",loadImage("d_normal.jpg"));&lt;br /&gt;    if(!imgMap.containsKey("COIN"))   imgMap.put("COIN",  loadImage("d_coin.jpg"));&lt;br /&gt;    if(!imgMap.containsKey("TAB"))    imgMap.put("TAB",   loadImage("d_tab.jpg"));&lt;br /&gt;    if(!imgMap.containsKey("FACE"))   imgMap.put("FACE",  loadImage("d_face.jpg"));&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;  // ========================================&lt;br /&gt;  // ボディパーツ（幾何情報とマテリアル情報）を、&lt;br /&gt;  // ハッシュ表と木構造に格納します。&lt;br /&gt;  // ハッシュ表は身体部位を高速に検索でき、&lt;br /&gt;  // また、木構造は階層的なアフィン変換を容易に実現できます&lt;br /&gt;  // ========================================&lt;br /&gt;  private void createBody() {&lt;br /&gt;    if(bodyMap == null) bodyMap = new HashMap&amp;lt;String, Node&amp;gt;();&lt;br /&gt;    else bodyMap.clear();&lt;br /&gt;    &lt;br /&gt;    createImageMap();&lt;br /&gt;  &lt;br /&gt;    final float MARGIN = 20;&lt;br /&gt;  &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 胴体&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float BODY_WIDTH  = 130;&lt;br /&gt;    final float BODY_HEIGHT = 155;&lt;br /&gt;    final float BODY_DEPTH  = 130;&lt;br /&gt;    &lt;br /&gt;    // 幾何情報の設定&lt;br /&gt;    Node body = new Node();&lt;br /&gt;    body.size.set(BODY_WIDTH, BODY_HEIGHT, BODY_DEPTH);&lt;br /&gt;    &lt;br /&gt;    // テクスチャの設定&lt;br /&gt;    body.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    body.applyTexture("FRONT",   imgMap.get("COIN"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 頭&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float HEAD_WIDTH  = 275;&lt;br /&gt;    final float HEAD_HEIGHT = 180;&lt;br /&gt;    final float HEAD_DEPTH  = 180;&lt;br /&gt;    &lt;br /&gt;    Node head = new Node();&lt;br /&gt;    head.pos.set (0,          -.5 * (BODY_HEIGHT + HEAD_HEIGHT), 0         );&lt;br /&gt;    head.org.set (0,           .5 * HEAD_HEIGHT,                 0         );&lt;br /&gt;    head.size.set(HEAD_WIDTH,  HEAD_HEIGHT,                      HEAD_DEPTH);&lt;br /&gt;    &lt;br /&gt;    head.applyTexture("TOP",     imgMap.get("TAB"));&lt;br /&gt;    head.applyTexture("FRONT",   imgMap.get("FACE"));&lt;br /&gt;    head.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 腕&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float ARM_WIDTH  =  45;&lt;br /&gt;    final float ARM_HEIGHT = 180;&lt;br /&gt;    final float ARM_DEPTH  =  50;&lt;br /&gt;    &lt;br /&gt;    Node leftArm = new Node();&lt;br /&gt;    leftArm.pos.set(-.5 * (BODY_WIDTH + ARM_WIDTH + MARGIN),    // x&lt;br /&gt;                    -.5 * (BODY_HEIGHT - ARM_HEIGHT) + MARGIN,  // y&lt;br /&gt;                      0);                                       // z&lt;br /&gt;&lt;br /&gt;    leftArm.org.set (0,         -.5 * ARM_HEIGHT, 0        );&lt;br /&gt;    leftArm.size.set(ARM_WIDTH,   ARM_HEIGHT,     ARM_DEPTH);&lt;br /&gt;&lt;br /&gt;    leftArm.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    Node rightArm = new Node();&lt;br /&gt;    rightArm.pos.set( .5 * (BODY_WIDTH + ARM_WIDTH + MARGIN),    // x&lt;br /&gt;                     -.5 * (BODY_HEIGHT - ARM_HEIGHT) + MARGIN,  // y&lt;br /&gt;                       0);                                       // z&lt;br /&gt;&lt;br /&gt;    rightArm.org.set (0,         -.5 * ARM_HEIGHT, 0        );&lt;br /&gt;    rightArm.size.set(ARM_WIDTH,  ARM_HEIGHT,      ARM_DEPTH);&lt;br /&gt;&lt;br /&gt;    rightArm.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 脚&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float LEG_HEIGHT = 130;&lt;br /&gt;    final float LEG_WIDTH  =  50;&lt;br /&gt;    final float LEG_DEPTH  = 100;&lt;br /&gt;    &lt;br /&gt;    Node leftLeg = new Node();&lt;br /&gt;    leftLeg.pos.set(-.5 * (LEG_WIDTH + MARGIN),        // x&lt;br /&gt;                     .5 * (BODY_HEIGHT + LEG_HEIGHT),  // y&lt;br /&gt;                     0);                               // z&lt;br /&gt;                     &lt;br /&gt;    leftLeg.org.set (0,         -.5 * LEG_HEIGHT, 0        );&lt;br /&gt;    leftLeg.size.set(LEG_WIDTH,   LEG_HEIGHT,     LEG_DEPTH);&lt;br /&gt;    &lt;br /&gt;    leftLeg.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;    Node rightLeg = new Node();&lt;br /&gt;    rightLeg.pos.set(.5 * (LEG_WIDTH + MARGIN),        // x&lt;br /&gt;                     .5 * (BODY_HEIGHT + LEG_HEIGHT),  // y&lt;br /&gt;                      0);                              // z&lt;br /&gt;                     &lt;br /&gt;    rightLeg.org.set (0,         -.5 * LEG_HEIGHT, 0        );&lt;br /&gt;    rightLeg.size.set(LEG_WIDTH,   LEG_HEIGHT,     LEG_DEPTH);&lt;br /&gt;    &lt;br /&gt;    rightLeg.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // スカート（？）&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    final float SKIRT_HEIGHT = .4 * BODY_DEPTH;&lt;br /&gt;    final float SKIRT_ANGLE1 =  radians(30);&lt;br /&gt;    final float SKIRT_ANGLE2 =  radians(20);&lt;br /&gt;    &lt;br /&gt;    Node backSkirt = new Node();&lt;br /&gt;    &lt;br /&gt;    backSkirt.pos.set( 0,                                 // x&lt;br /&gt;                      .5 * (BODY_HEIGHT + SKIRT_HEIGHT),  // y&lt;br /&gt;                      .5 * BODY_DEPTH);                   // z&lt;br /&gt;                      &lt;br /&gt;    backSkirt.org.set (0,            -.5 * SKIRT_HEIGHT, 0);&lt;br /&gt;    backSkirt.rot.set (SKIRT_ANGLE1,  0,                 0);&lt;br /&gt;    backSkirt.size.set(BODY_WIDTH,    SKIRT_HEIGHT,      1);&lt;br /&gt;    &lt;br /&gt;    backSkirt.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    Node frontSkirt = new Node();&lt;br /&gt;&lt;br /&gt;    frontSkirt.pos.set(  0,                                 // x&lt;br /&gt;                        .5 * (BODY_HEIGHT + SKIRT_HEIGHT),  // y&lt;br /&gt;                       -.5 * BODY_DEPTH);                   // z&lt;br /&gt;                       &lt;br /&gt;    frontSkirt.org.set ( 0,            -.5 * SKIRT_HEIGHT, 0);&lt;br /&gt;    frontSkirt.rot.set (-SKIRT_ANGLE1,  0,                 0);&lt;br /&gt;    frontSkirt.size.set( BODY_WIDTH,    SKIRT_HEIGHT,      1);&lt;br /&gt;    &lt;br /&gt;    frontSkirt.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;    Node leftSkirt = new Node();&lt;br /&gt;    &lt;br /&gt;    leftSkirt.pos.set(-.5 * BODY_WIDTH,                    // x&lt;br /&gt;                       .5 * (BODY_HEIGHT + SKIRT_HEIGHT),  // y&lt;br /&gt;                        0);                                // z&lt;br /&gt;    &lt;br /&gt;    leftSkirt.org.set (0, -.5 * SKIRT_HEIGHT, 0           );&lt;br /&gt;    leftSkirt.rot.set (0,   0,                SKIRT_ANGLE2);&lt;br /&gt;    leftSkirt.size.set(1,   SKIRT_HEIGHT,     BODY_DEPTH  );&lt;br /&gt;    &lt;br /&gt;    leftSkirt.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;  &lt;br /&gt;  &lt;br /&gt;    Node rightSkirt = new Node();&lt;br /&gt;    &lt;br /&gt;    rightSkirt.pos.set(.5 * BODY_WIDTH,                    // x&lt;br /&gt;                       .5 * (BODY_HEIGHT + SKIRT_HEIGHT),  // y&lt;br /&gt;                        0);                                // z&lt;br /&gt;                       &lt;br /&gt;    rightSkirt.org.set (0, -.5 * SKIRT_HEIGHT,  0           );&lt;br /&gt;    rightSkirt.rot.set (0,   0,                -SKIRT_ANGLE2);&lt;br /&gt;    rightSkirt.size.set(1,   SKIRT_HEIGHT,      BODY_DEPTH  );&lt;br /&gt;    &lt;br /&gt;    rightSkirt.applyTexture("DEFAULT", imgMap.get("NORMAL"));&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // ハッシュ表の構築&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    bodyMap.put("HEAD",     head);&lt;br /&gt;    bodyMap.put("BODY",     body);&lt;br /&gt;    bodyMap.put("LEFTARM",  leftArm);&lt;br /&gt;    bodyMap.put("RIGHTARM", rightArm);&lt;br /&gt;    bodyMap.put("LEFTLEG",  leftLeg);&lt;br /&gt;    bodyMap.put("RIGHTLEG", rightLeg);&lt;br /&gt;    &lt;br /&gt;    &lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    // 木の構築&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    //&lt;br /&gt;    // 　　　　頭&lt;br /&gt;    // 　　　　｜&lt;br /&gt;    // 　　　　胴 ← Root&lt;br /&gt;    // 　　／｜　｜＼ &lt;br /&gt;    // 右腕　右　左　左腕&lt;br /&gt;    // 　　　脚　足&lt;br /&gt;    // ----------------------------------------&lt;br /&gt;    body.addChild(leftLeg);&lt;br /&gt;    body.addChild(rightLeg);&lt;br /&gt;    body.addChild(head);&lt;br /&gt;    body.addChild(leftArm);&lt;br /&gt;    body.addChild(rightArm);&lt;br /&gt;    bodyTree = body;&lt;br /&gt;  &lt;br /&gt;    // おまけ：スカートのようなもの&lt;br /&gt;    // こちらは別にどうでもいいのでハッシュ表には入れない&lt;br /&gt;    body.addChild(backSkirt);&lt;br /&gt;    body.addChild(frontSkirt);&lt;br /&gt;    body.addChild(leftSkirt);&lt;br /&gt;    body.addChild(rightSkirt);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【おまけ】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;主催者からの紹介文&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.bbpBox{background:url(http://a0.twimg.com/images/themes/theme1/bg.png) #EDECE9;padding:20px;}&lt;/style&gt;&lt;br /&gt;&lt;div class="bbpBox" id="tweet_142124702637367297" style="background: url(&amp;quot;http://a0.twimg.com/images/themes/theme1/bg.png&amp;quot;) repeat scroll 0% 0% rgb(237, 236, 233); padding: 20px;"&gt;&lt;div class="bbpTweet" style="-moz-border-radius: 5px 5px 5px 5px; background: none repeat scroll 0% 0% rgb(255, 255, 255); color: black; font-size: 16px ! important; line-height: 22px; margin: 0pt; min-height: 48px; padding: 10px 12px;"&gt;モーション、エフェクト、3D、パーティクル、計算幾何、とP5で幅広く活動していらっしゃる &lt;a href="http://twitter.com/tercel_s" target="_new"&gt;@tercel_s&lt;/a&gt; さんが、Processing Advent Calendar 2011  に参戦！！ 12/3日担当予定 &lt;a href="http://t.co/63T4x3C0" target="_new"&gt;http://t.co/63T4x3C0&lt;/a&gt; &lt;a href="http://search.twitter.com/search?q=%23p5advent" target="_new"&gt;#p5advent&lt;/a&gt;&lt;span class="timestamp" style="display: block; font-size: 12px;"&gt;&lt;a href="http://twitter.com/p5info/status/142124702637367297" title="2011年12月1日 15:15"&gt;2011年12月1日 15:15&lt;/a&gt; via web&lt;/span&gt;&lt;span class="metadata" style="border-top: 1px solid rgb(230, 230, 230); clear: both; display: block; height: 40px; margin-top: 8px; padding-top: 12px; width: 100%;"&gt;&lt;span class="author" style="line-height: 19px;"&gt;&lt;a href="http://twitter.com/p5info"&gt;&lt;img src="http://a1.twimg.com/profile_images/1229940896/icontmp_normal.png" style="background: none repeat scroll 0% 0% rgb(255, 255, 255); border: 0pt none; float: left; height: 38px; margin: 0pt 7px 0pt 0px; padding: 0pt; width: 38px;" /&gt;&lt;/a&gt;&lt;b&gt;&lt;a href="http://twitter.com/p5info"&gt;p5info.com&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;p5info&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;うわぁなにこのプレシャーΣ(･Д･ﾉ)ﾉ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-975926898505069630?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/975926898505069630/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/processing3d.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/975926898505069630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/975926898505069630'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/12/processing3d.html' title='Processingでかんたん3Dアニメーション'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-8_Pw0tOY2C4/TteEEu6xswI/AAAAAAAAAbA/MYAZfeDO7Qc/s72-c/translateSample.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-7621034275559143733</id><published>2011-11-21T20:54:00.014+09:00</published><updated>2011-11-22T16:34:02.548+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='DirectX9'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows API'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>C++ で 3 次元 Delaunay 分割</title><content type='html'>某所にて、『2 次元の Delaunay 分割は簡単だから別にいらない。&lt;b&gt;むしろ 3 次元 Delaunay 分割のソースコードが欲しい&lt;/b&gt;』という要望を間接的に頂いたので、みんなが大好きな C++ で作ってみましたよー (´-ω-`)&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-ef4FWJLJIMg/Tso2svkC4TI/AAAAAAAAAa4/BQlXvLDyMAY/s1600/delaunay3dcpp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="315" src="http://2.bp.blogspot.com/-ef4FWJLJIMg/Tso2svkC4TI/AAAAAAAAAa4/BQlXvLDyMAY/s400/delaunay3dcpp.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;OpenProcessing に投稿した &lt;a href="http://www.openprocessing.org/visuals/?visualID=31295"&gt;3D Delaunay Triangulation&lt;/a&gt; よりも、若干ですがソースコードがブラッシュアップされているような気がします。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;今回は 3D という事もあり、結果表示用のプログラムには DirectX 9 を使用しました。最新バージョンを使わなかったのは、Windows XP でも実行できるようにという配慮からです。あ、あくまで&lt;b&gt;環境依存のプログラムは GUI 部分のみであり、アルゴリズム（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Delaunay3d.h&lt;/span&gt;）は標準 C++ のみで実装している&lt;/b&gt;ため、他の環境にもそのまま移植できると思います。&lt;br /&gt;&lt;br /&gt;使用方法は昨日の 2 次元 Delaunay 分割とほぼ同じで、以下の通りです（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt; のコールバック関数、および &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;render()&lt;/span&gt; 関数に実際のサンプルがあります）。&lt;br /&gt;&lt;br /&gt;（※ただし、2 次元と 3 次元のモジュールを共存させようとすると、いたるところで名前が衝突するので注意）&lt;br /&gt;&lt;ol&gt;&lt;li&gt;頂点セット（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::set&amp;lt;Tercel::Vector&amp;gt;&lt;/span&gt; オブジェクト）を宣言します。&lt;/li&gt;&lt;li&gt;先ほどの頂点セットに、適当な頂点オブジェクトを格納します。&lt;/li&gt;&lt;li&gt;三角形セット（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::set&amp;lt;Tercel::Triangle&amp;gt;&lt;/span&gt; オブジェクト）を宣言します。&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Delaunay3d::getDelaunayTriangles()&lt;/span&gt; 関数を呼びます。&lt;/li&gt;&lt;/ol&gt;このとき、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;getDelaunayTriangles()&lt;/span&gt; の第一引数に頂点セットの『&lt;b&gt;参照&lt;/b&gt;』を、第2引数には三角形セットの『&lt;b&gt;ポインタ&lt;/b&gt;』を渡します。&lt;br /&gt;&lt;br /&gt;例によって、三角形（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Triangle&lt;/span&gt;）は、頂点オブジェクトのポインタを保持していますので、頂点セットから要素を削除した場合（あるいはメモリ上の位置が変わるような事が起きた場合）には、三角形の頂点は不正なポインタになってしまいます。&lt;br /&gt;&lt;br /&gt;ご注意ください。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Delaunay3d.h&lt;/span&gt;】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;//&lt;br /&gt;// 3次元Delaunay分割&lt;br /&gt;//&lt;br /&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#ifndef ___TERCEL_DELAUNAY3D&lt;br /&gt;#define ___TERCEL_DELAUNAY3D&lt;br /&gt;&lt;br /&gt;#include &amp;lt;algorithm&amp;gt;&lt;br /&gt;#include &amp;lt;cfloat&amp;gt;&lt;br /&gt;#include &amp;lt;cmath&amp;gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;map&amp;gt;&lt;br /&gt;#include &amp;lt;set&amp;gt;&lt;br /&gt;&lt;br /&gt;namespace Tercel &lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  // ポインタの参照先を比較するためのファンクタ&lt;br /&gt;  class PtrComp&lt;br /&gt;  {&lt;br /&gt;  public:&lt;br /&gt;    template &amp;lt;class Type&amp;gt; &lt;br /&gt;    bool operator()(const Type* a, const Type* b) const&lt;br /&gt;    {&lt;br /&gt;      return *a &amp;lt; *b;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  struct Vector {&lt;br /&gt;    double x, y, z;&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 等価性の判定&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator==(const Vector&amp;amp; v) const &lt;br /&gt;    {&lt;br /&gt;      return x == v.x &amp;amp;&amp;amp; y == v.y &amp;amp;&amp;amp; z == v.z;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 大小判定（set / mapを構築する際に使用）&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator&amp;lt;(const Vector&amp;amp; v) const &lt;br /&gt;    {&lt;br /&gt;      return x != v.x ? x &amp;lt; v.x :&lt;br /&gt;             y != v.y ? y &amp;lt; v.y :&lt;br /&gt;                        z &amp;lt; v.z;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  struct Circle &lt;br /&gt;  {&lt;br /&gt;    Vector center;  // 中心座標&lt;br /&gt;    double radius;  // 半径&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  class Triangle {&lt;br /&gt;  public:&lt;br /&gt;    const static size_t NUM_VERTEX = 3;&lt;br /&gt;    const Vector* p[NUM_VERTEX];  // 頂点座標&lt;br /&gt;    &lt;br /&gt;    // ======================================  &lt;br /&gt;    // 等価性の判定&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator==(const Triangle&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      const Vector* p1[NUM_VERTEX], *p2[NUM_VERTEX];&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;      { // const Vector const * → const Vector *&lt;br /&gt;        p1[i] = const_cast&amp;lt;const Vector*&amp;gt;(p[i]);&lt;br /&gt;        p2[i] = const_cast&amp;lt;const Vector*&amp;gt;(t.p[i]);&lt;br /&gt;      }&lt;br /&gt;      std::sort(p1, p1 + NUM_VERTEX, PtrComp());&lt;br /&gt;      std::sort(p2, p2 + NUM_VERTEX, PtrComp());&lt;br /&gt;&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;        if(!(p1[i] == p2[i])) return false;&lt;br /&gt;      return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 大小判定（set / mapを構築する際に使用）&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator&amp;lt;(const Triangle&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      const Vector* p1[NUM_VERTEX], *p2[NUM_VERTEX];&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;      { // const Vector const * → const Vector *&lt;br /&gt;        p1[i] = const_cast&amp;lt;const Vector*&amp;gt;(p[i]);&lt;br /&gt;        p2[i] = const_cast&amp;lt;const Vector*&amp;gt;(t.p[i]);&lt;br /&gt;      }&lt;br /&gt;      std::sort(p1, p1 + NUM_VERTEX, PtrComp());&lt;br /&gt;      std::sort(p2, p2 + NUM_VERTEX, PtrComp());&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX - 1; ++i)&lt;br /&gt;        if(!(*p1[i] == *p2[i])) return *p1[i] &amp;lt; *p2[i];&lt;br /&gt;      return *p1[NUM_VERTEX - 1] &amp;lt; *p2[NUM_VERTEX - 1];&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  class Tetrahedron {&lt;br /&gt;  public:&lt;br /&gt;    const static size_t NUM_VERTEX = 4;&lt;br /&gt;    const Vector* p[NUM_VERTEX];&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 等価性の判定&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator==(const Tetrahedron&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      const Vector* p1[NUM_VERTEX], *p2[NUM_VERTEX];&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;      { // const Vector const * → const Vector *&lt;br /&gt;        p1[i] = const_cast&amp;lt;const Vector*&amp;gt;(p[i]);&lt;br /&gt;        p2[i] = const_cast&amp;lt;const Vector*&amp;gt;(t.p[i]);&lt;br /&gt;      }&lt;br /&gt;      std::sort(p1, p1 + NUM_VERTEX, PtrComp());&lt;br /&gt;      std::sort(p2, p2 + NUM_VERTEX, PtrComp());&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;        if(!(p1[i] == p2[i])) return false;&lt;br /&gt;      return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 大小判定（set / mapを構築する際に使用）&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator&amp;lt;(const Tetrahedron&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      const Vector* p1[NUM_VERTEX], *p2[NUM_VERTEX];&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i)&lt;br /&gt;      { // const Vector const * → const Vector *&lt;br /&gt;        p1[i] = const_cast&amp;lt;const Vector*&amp;gt;( p[i] );&lt;br /&gt;        p2[i] = const_cast&amp;lt;const Vector*&amp;gt;(t.p[i]);&lt;br /&gt;      }&lt;br /&gt;      std::sort(p1, p1 + NUM_VERTEX, PtrComp());&lt;br /&gt;      std::sort(p2, p2 + NUM_VERTEX, PtrComp());&lt;br /&gt;&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX - 1; ++i) &lt;br /&gt;        if(!(*p1[i] == *p2[i])) return *p1[i] &amp;lt; *p2[i];&lt;br /&gt;      return *p1[NUM_VERTEX - 1] &amp;lt; *p2[NUM_VERTEX - 1];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 他の四面体と共有点を持つか  &lt;br /&gt;    // ====================================== &lt;br /&gt;    bool hasCommonPoints(const Tetrahedron&amp;amp; t) const&lt;br /&gt;    {&lt;br /&gt;      for(int i = 0; i &amp;lt; NUM_VERTEX; ++i) &lt;br /&gt;        for(int j = 0; j &amp;lt; NUM_VERTEX; ++j)&lt;br /&gt;          if(*p[i] == *t.p[j]) return true;&lt;br /&gt;      return false;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;  &lt;br /&gt;  class Delaunay3d &lt;br /&gt;  {&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 型定義&lt;br /&gt;    // ====================================== &lt;br /&gt;    typedef const std::set&amp;lt;Vector&amp;gt;         ConstVertexSet;&lt;br /&gt;    typedef ConstVertexSet::const_iterator ConstVertexIter;&lt;br /&gt;&lt;br /&gt;    typedef std::set&amp;lt;Triangle&amp;gt;             TriangleSet;&lt;br /&gt;&lt;br /&gt;    typedef std::set&amp;lt;Tetrahedron&amp;gt;          TetraSet;&lt;br /&gt;    typedef TetraSet::iterator             TetraIter;&lt;br /&gt;&lt;br /&gt;    typedef std::map&amp;lt;Tetrahedron, bool&amp;gt;    TetraMap;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  private:&lt;br /&gt;    // ======================================  &lt;br /&gt;    // LU分解による三元一次連立方程式の解法&lt;br /&gt;    // ======================================&lt;br /&gt;    static double lu(double a[3][3], int ip[3]) &lt;br /&gt;    {&lt;br /&gt;      const int n = 3;&lt;br /&gt;      double weight[n];&lt;br /&gt;   &lt;br /&gt;      for(int k = 0; k &amp;lt; n; ++k)&lt;br /&gt;      {&lt;br /&gt;        ip[k] = k;&lt;br /&gt;        double u = 0;&lt;br /&gt;        for(int j = 0; j &amp;lt; n; ++j) &lt;br /&gt;        {&lt;br /&gt;          double t = fabs(a[k][j]);&lt;br /&gt;          if (t &amp;gt; u) u = t;&lt;br /&gt;        }&lt;br /&gt;        if (u == 0) return 0;&lt;br /&gt;        weight[k] = 1/u;&lt;br /&gt;      }&lt;br /&gt;      double det = 1;&lt;br /&gt;      for(int k = 0; k &amp;lt; n; ++k) &lt;br /&gt;      {&lt;br /&gt;        double u = -1;&lt;br /&gt;        int m = 0;&lt;br /&gt;        for(int i = k; i &amp;lt; n; ++i) &lt;br /&gt;        {&lt;br /&gt;          int ii = ip[i];&lt;br /&gt;          double t = fabs(a[ii][k]) * weight[ii];&lt;br /&gt;          if(t&amp;gt;u) &lt;br /&gt;          { &lt;br /&gt;            u = t; &lt;br /&gt;            m = i;&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;        int ik = ip[m];&lt;br /&gt;        if (m != k) &lt;br /&gt;        {&lt;br /&gt;          ip[m] = ip[k]; ip[k] = ik;&lt;br /&gt;          det = -det;&lt;br /&gt;        }&lt;br /&gt;        u = a[ik][k]; det *= u;&lt;br /&gt;        if (u == 0) return 0;&lt;br /&gt;        for (int i = k+1; i &amp;lt; n; ++i) &lt;br /&gt;        {&lt;br /&gt;          int ii = ip[i];&lt;br /&gt;          double t = (a[ii][k] /= u);&lt;br /&gt;          for(int j = k+1; j &amp;lt; n; ++j) &lt;br /&gt;            a[ii][j] -= t * a[ik][j];&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      return det;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static void solve(double a[3][3], double b[3], int ip[3], double x[3]) &lt;br /&gt;    {&lt;br /&gt;      const int n = 3;&lt;br /&gt;&lt;br /&gt;      for(int i = 0; i &amp;lt; n; ++i) &lt;br /&gt;      {&lt;br /&gt;        int ii = ip[i];&lt;br /&gt;        double t = b[ii];&lt;br /&gt;        for (int j = 0; j &amp;lt; i; ++j) &lt;br /&gt;          t -= a[ii][j] * x[j];&lt;br /&gt;        x[i] = t;&lt;br /&gt;      }&lt;br /&gt;      &lt;br /&gt;      for (int i = n-1; i &amp;gt;= 0; --i) &lt;br /&gt;      {&lt;br /&gt;        double t = x[i];&lt;br /&gt;        int ii = ip[i];&lt;br /&gt;        for(int j = i+1; j &amp;lt; n; ++j) &lt;br /&gt;          t -= a[ii][j] * x[j];&lt;br /&gt;        x[i] = t / a[ii][i];&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static double gauss(double a[3][3], double b[3], double x[3]) &lt;br /&gt;    {&lt;br /&gt;      const int n = 3;&lt;br /&gt;      int ip[n];&lt;br /&gt;      double det = lu(a, ip);&lt;br /&gt;   &lt;br /&gt;      if(det != 0) solve(a, b, ip, x);&lt;br /&gt;      return det;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 与えられた四面体の外接球を求める&lt;br /&gt;    // ======================================&lt;br /&gt;    static void getCircumcircle(const Tetrahedron&amp;amp; tetra, Circle *dst) &lt;br /&gt;    {&lt;br /&gt;      const Vector* p0 = tetra.p[0];&lt;br /&gt;      const Vector* p1 = tetra.p[1];&lt;br /&gt;      const Vector* p2 = tetra.p[2];&lt;br /&gt;      const Vector* p3 = tetra.p[3];&lt;br /&gt;&lt;br /&gt;      double A[3][3] = {&lt;br /&gt;        {p1-&amp;gt;x - p0-&amp;gt;x, p1-&amp;gt;y - p0-&amp;gt;y, p1-&amp;gt;z - p0-&amp;gt;z},&lt;br /&gt;        {p2-&amp;gt;x - p0-&amp;gt;x, p2-&amp;gt;y - p0-&amp;gt;y, p2-&amp;gt;z - p0-&amp;gt;z},&lt;br /&gt;        {p3-&amp;gt;x - p0-&amp;gt;x, p3-&amp;gt;y - p0-&amp;gt;y, p3-&amp;gt;z - p0-&amp;gt;z}&lt;br /&gt;      };&lt;br /&gt;&lt;br /&gt;      double b[3] = {&lt;br /&gt;        0.5 * (p1-&amp;gt;x*p1-&amp;gt;x - p0-&amp;gt;x*p0-&amp;gt;x + p1-&amp;gt;y*p1-&amp;gt;y - p0-&amp;gt;y*p0-&amp;gt;y + p1-&amp;gt;z*p1-&amp;gt;z - p0-&amp;gt;z*p0-&amp;gt;z),&lt;br /&gt;        0.5 * (p2-&amp;gt;x*p2-&amp;gt;x - p0-&amp;gt;x*p0-&amp;gt;x + p2-&amp;gt;y*p2-&amp;gt;y - p0-&amp;gt;y*p0-&amp;gt;y + p2-&amp;gt;z*p2-&amp;gt;z - p0-&amp;gt;z*p0-&amp;gt;z),&lt;br /&gt;        0.5 * (p3-&amp;gt;x*p3-&amp;gt;x - p0-&amp;gt;x*p0-&amp;gt;x + p3-&amp;gt;y*p3-&amp;gt;y - p0-&amp;gt;y*p0-&amp;gt;y + p3-&amp;gt;z*p3-&amp;gt;z - p0-&amp;gt;z*p0-&amp;gt;z)&lt;br /&gt;      };&lt;br /&gt;&lt;br /&gt;      double x[3] = {0.0, 0.0, 0.0};&lt;br /&gt;&lt;br /&gt;      if(gauss(A, b, x) == 0)&lt;br /&gt;      {&lt;br /&gt;        dst-&amp;gt;center.x = dst-&amp;gt;center.y = dst-&amp;gt;center.z = 0;&lt;br /&gt;        dst-&amp;gt;radius   = -1;&lt;br /&gt;      } &lt;br /&gt;      else &lt;br /&gt;      {&lt;br /&gt;        dst-&amp;gt;center.x = x[0];&lt;br /&gt;        dst-&amp;gt;center.y = x[1];&lt;br /&gt;        dst-&amp;gt;center.z = x[2];&lt;br /&gt;&lt;br /&gt;        double dx = x[0] - p0-&amp;gt;x;&lt;br /&gt;        double dy = x[1] - p0-&amp;gt;y;&lt;br /&gt;        double dz = x[2] - p0-&amp;gt;z;&lt;br /&gt;        dst-&amp;gt;radius =  sqrt(dx * dx + dy * dy + dz * dz);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    // ======================================  &lt;br /&gt;    // 四面体の重複管理&lt;br /&gt;    // ======================================&lt;br /&gt;    static inline void addElementToRedundanciesMap(TetraMap* pRddcMap,  &lt;br /&gt;        const Tetrahedron&amp;amp; t)   &lt;br /&gt;    {  &lt;br /&gt;      TetraMap::iterator it = pRddcMap-&amp;gt;find(t);  &lt;br /&gt;      if(it != pRddcMap-&amp;gt;end() &amp;amp;&amp;amp; it-&amp;gt;second)   &lt;br /&gt;      {  &lt;br /&gt;        // 値を (t, true) から (t, false) に置換  &lt;br /&gt;        pRddcMap-&amp;gt;erase(it);  &lt;br /&gt;        pRddcMap-&amp;gt;insert(TetraMap::value_type(t, false));  &lt;br /&gt;      } &lt;br /&gt;      else&lt;br /&gt;        pRddcMap-&amp;gt;insert(TetraMap::value_type(t, true));  &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  public:&lt;br /&gt;    static void getDelaunayTriangles(ConstVertexSet&amp;amp; pVertexSet, &lt;br /&gt;        TriangleSet* triangleSet) &lt;br /&gt;    {&lt;br /&gt;&lt;br /&gt;      TetraSet tetraSet;&lt;br /&gt;&lt;br /&gt;      // ======================================  &lt;br /&gt;      // 巨大な外部四面体を作る  &lt;br /&gt;      // ======================================&lt;br /&gt;      Vector p0, p1, p2, p3;&lt;br /&gt;      Tetrahedron hugeTetrahedron = {&amp;amp;p0, &amp;amp;p1, &amp;amp;p2, &amp;amp;p3};&lt;br /&gt;      //               !! 注意 !!&lt;br /&gt;      // --------------------------------------&lt;br /&gt;      // hugeTetrahedronの要素はローカル変数への&lt;br /&gt;      // ポインタなので、これらの頂点を共有して&lt;br /&gt;      // いるTetrahedronオブジェクトは、この関数&lt;br /&gt;      // を抜ける前に必ず除去しておく必要がある。&lt;br /&gt;      {&lt;br /&gt;        double maxX, maxY, maxZ;&lt;br /&gt;        double minX, minY, minZ;&lt;br /&gt;        maxX = maxY = maxZ = DBL_MIN;&lt;br /&gt;        minX = minY = minZ = DBL_MAX;&lt;br /&gt;        &lt;br /&gt;        // まず、全ての頂点を包含する球を得る&lt;br /&gt;&lt;br /&gt;        // 中心座標を求める&lt;br /&gt;        for(ConstVertexIter it = pVertexSet.begin();&lt;br /&gt;          it != pVertexSet.end(); ++it) &lt;br /&gt;        {&lt;br /&gt;          if(maxX  &amp;lt; it-&amp;gt;x) maxX = it-&amp;gt;x; &lt;br /&gt;          if(it-&amp;gt;x &amp;lt; minX ) minX = it-&amp;gt;x;&lt;br /&gt;          &lt;br /&gt;          if(maxY  &amp;lt; it-&amp;gt;y) maxY = it-&amp;gt;y; &lt;br /&gt;          if(it-&amp;gt;y &amp;lt; minY ) minY = it-&amp;gt;y;&lt;br /&gt;          &lt;br /&gt;          if(maxZ  &amp;lt; it-&amp;gt;z) maxZ = it-&amp;gt;z; &lt;br /&gt;          if(it-&amp;gt;z &amp;lt; minZ ) minZ = it-&amp;gt;z;&lt;br /&gt;        }&lt;br /&gt;        double centerX = 0.5 * (maxX - minX);&lt;br /&gt;        double centerY = 0.5 * (maxY - minY);&lt;br /&gt;        double centerZ = 0.5 * (maxZ - minZ);&lt;br /&gt;&lt;br /&gt;        // 半径を求める&lt;br /&gt;        double dx      = centerX - minX;&lt;br /&gt;        double dy      = centerY - minY;&lt;br /&gt;        double dz      = centerZ - minZ;&lt;br /&gt;        double radius  = sqrt(dx * dx + dy * dy + dz * dz) + 0.1;&lt;br /&gt;&lt;br /&gt;        // 4つの頂点&lt;br /&gt;        p0.x = centerX;&lt;br /&gt;        p0.y = centerY + 3.0 * radius;&lt;br /&gt;        p0.z = centerZ;&lt;br /&gt;&lt;br /&gt;        p1.x = centerX - 2.0 * sqrt(2.0) * radius;&lt;br /&gt;        p1.y = centerY - radius;&lt;br /&gt;        p1.z = centerZ;&lt;br /&gt;&lt;br /&gt;        p2.x = centerX + sqrt(2.0) * radius;&lt;br /&gt;        p2.y = centerY - radius;&lt;br /&gt;        p2.z = centerZ + sqrt(6.0) * radius;&lt;br /&gt;&lt;br /&gt;        p3.x = centerX + sqrt(2.0) * radius;&lt;br /&gt;        p3.y = centerY - radius;&lt;br /&gt;        p3.z = centerZ - sqrt(6.0) * radius;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      tetraSet.insert(hugeTetrahedron);&lt;br /&gt;&lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      // 点を逐次添加し、反復的に四面体分割を行う  &lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      for(ConstVertexIter vIter = pVertexSet.begin(); &lt;br /&gt;        vIter != pVertexSet.end(); ++vIter) &lt;br /&gt;      {&lt;br /&gt;        const Vector* p = &amp;amp;*vIter;&lt;br /&gt;&lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        // 追加候補の四面体を保持する一時マップ  &lt;br /&gt;        // --------------------------------------&lt;br /&gt;        TetraMap rddcMap;&lt;br /&gt;&lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        // 現在の四面体セットから要素を一つずつ取り出して、    &lt;br /&gt;        // 与えられた点が各々の四面体の外接球の中に含まれるかどうか判定    &lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        for(TetraIter tIter = tetraSet.begin();   &lt;br /&gt;          tIter != tetraSet.end();) &lt;br /&gt;        {&lt;br /&gt;          // 四面体セットから要素を取りだして…  &lt;br /&gt;          Tetrahedron t = *tIter;&lt;br /&gt;&lt;br /&gt;          // 外接球を求める&lt;br /&gt;          Circle c;&lt;br /&gt;          getCircumcircle(t, &amp;amp;c);&lt;br /&gt;          double dx  = c.center.x - p-&amp;gt;x;&lt;br /&gt;          double dy  = c.center.y - p-&amp;gt;y;&lt;br /&gt;          double dz  = c.center.z - p-&amp;gt;z;&lt;br /&gt;          double len = sqrt(dx * dx + dy * dy + dz * dz);&lt;br /&gt;&lt;br /&gt;          if(len &amp;lt; c.radius)&lt;br /&gt;          {&lt;br /&gt;            Tetrahedron t0 = {p, t.p[0], t.p[1], t.p[2]};&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t0);&lt;br /&gt;&lt;br /&gt;            Tetrahedron t1 = {p, t.p[0], t.p[2], t.p[3]};&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t1);&lt;br /&gt;&lt;br /&gt;            Tetrahedron t2 = {p, t.p[0], t.p[3], t.p[1]};&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t2);&lt;br /&gt;&lt;br /&gt;            Tetrahedron t3 = {p, t.p[1], t.p[2], t.p[3]};&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t3);&lt;br /&gt;&lt;br /&gt;            tetraSet.erase(tIter++);&lt;br /&gt;          } else ++tIter;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        for(TetraMap::iterator iter = rddcMap.begin();&lt;br /&gt;          iter != rddcMap.end(); ++iter) &lt;br /&gt;        {&lt;br /&gt;          if(iter-&amp;gt;second) tetraSet.insert(iter-&amp;gt;first);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      // 最後に、外部三角形の頂点を削除  &lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      for(TetraIter tIter = tetraSet.begin();   &lt;br /&gt;        tIter != tetraSet.end(); )   &lt;br /&gt;      {  &lt;br /&gt;        if(hugeTetrahedron.hasCommonPoints(*tIter))  &lt;br /&gt;           tetraSet.erase(tIter++);  &lt;br /&gt;        else ++tIter;  &lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // そして、伝説へ…&lt;br /&gt;      for(TetraIter tIter = tetraSet.begin();   &lt;br /&gt;        tIter != tetraSet.end(); ++tIter) &lt;br /&gt;      {&lt;br /&gt;        Tetrahedron tetra = *tIter;&lt;br /&gt;&lt;br /&gt;        Triangle t0 = {tetra.p[0], tetra.p[1], tetra.p[2]};&lt;br /&gt;        triangleSet-&amp;gt;insert(t0);&lt;br /&gt;&lt;br /&gt;        Triangle t1 = {tetra.p[0], tetra.p[2], tetra.p[3]};&lt;br /&gt;        triangleSet-&amp;gt;insert(t1);&lt;br /&gt;&lt;br /&gt;        Triangle t2 = {tetra.p[0], tetra.p[3], tetra.p[1]};&lt;br /&gt;        triangleSet-&amp;gt;insert(t2);&lt;br /&gt;&lt;br /&gt;        Triangle t3 = {tetra.p[1], tetra.p[2], tetra.p[3]};&lt;br /&gt;        triangleSet-&amp;gt;insert(t3);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt;】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;//&lt;br /&gt;// 3次元Delaunay分割（表示用GUI）&lt;br /&gt;//&lt;br /&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;#include &amp;lt;windows.h&amp;gt;&lt;br /&gt;#include &amp;lt;d3d9.h&amp;gt;&lt;br /&gt;#include &amp;lt;d3dx9.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include "Delaunay3d.h"&lt;br /&gt;&lt;br /&gt;//------ライブラリの設定とか&lt;br /&gt;#pragma comment(lib, "d3dxof.lib")&lt;br /&gt;#pragma comment(lib, "dxguid.lib")&lt;br /&gt;#pragma comment(lib, "d3dx9.lib")&lt;br /&gt;#pragma comment(lib, "d3d9.lib")&lt;br /&gt;#pragma comment(lib, "winmm.lib")&lt;br /&gt;&lt;br /&gt;//------ウィンドウを出すために必要な物あれこれ&lt;br /&gt;const LPCTSTR APP_NAME    = TEXT("DXT2_BY_TERCEL");&lt;br /&gt;const LPCTSTR MUTEX_NAME  = TEXT("MUTEX_DXT2_BY_TERCEL");&lt;br /&gt;const LPCTSTR WINDOW_NAME = TEXT("3D Delaunay Triangulation");&lt;br /&gt;&lt;br /&gt;const UINT SCREEN_WIDTH  = 800;&lt;br /&gt;const UINT SCREEN_HEIGHT = 600;&lt;br /&gt;const UINT WINDOW_WIDTH  = (SCREEN_WIDTH + &lt;br /&gt;              GetSystemMetrics(SM_CXFIXEDFRAME) * 2);&lt;br /&gt;const UINT WINDOW_HEIGHT = (SCREEN_HEIGHT + &lt;br /&gt;              GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + &lt;br /&gt;              GetSystemMetrics(SM_CYCAPTION));&lt;br /&gt;&lt;br /&gt;// ----------------------------------------&lt;br /&gt;// 頂点集合、およびそれらを基に&lt;br /&gt;// Delaunay 分割された三角形を格納しておくSTLコンテナ&lt;br /&gt;// ----------------------------------------&lt;br /&gt;std::set&amp;lt;Tercel::Vector&amp;gt;   vertices;&lt;br /&gt;std::set&amp;lt;Tercel::Triangle&amp;gt; triangles;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//------Direct3Dに必要なものあれこれ&lt;br /&gt;LPDIRECT3D9 g_pD3D = NULL;&lt;br /&gt;LPDIRECT3DDEVICE9 g_pD3DDev = NULL;&lt;br /&gt;D3DPRESENT_PARAMETERS g_D3DPresentParam;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;//------関数プロトタイプ&lt;br /&gt;LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);&lt;br /&gt;HRESULT InitD3D(HWND);&lt;br /&gt;VOID    Render();&lt;br /&gt;&lt;br /&gt;//------FVF&lt;br /&gt;struct ColorPoint&lt;br /&gt;{&lt;br /&gt;  D3DXVECTOR3 vCoord; // 座標&lt;br /&gt;  DWORD   color;  // 色&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;//------エントリポイント&lt;br /&gt;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, &lt;br /&gt;                   LPSTR lpCmdLine, int nCmdShow)&lt;br /&gt;{&lt;br /&gt;  MSG msg;&lt;br /&gt;&lt;br /&gt;  // 多重起動防止&lt;br /&gt;  HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);&lt;br /&gt;  if(GetLastError() == ERROR_ALREADY_EXISTS)&lt;br /&gt;  {&lt;br /&gt;    // 多重起動を検知した際には、&lt;br /&gt;    // 既存のウィンドウを最前面に表示してプログラムを終了する&lt;br /&gt;    HWND existingWnd = FindWindow(APP_NAME, NULL);&lt;br /&gt;    if(existingWnd != NULL)&lt;br /&gt;    {&lt;br /&gt;      if(IsIconic(existingWnd))&lt;br /&gt;        ShowWindowAsync(existingWnd, SW_RESTORE);&lt;br /&gt;&lt;br /&gt;      SetForegroundWindow(existingWnd);&lt;br /&gt;    }&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ウィンドウクラス登録 &lt;br /&gt;  WNDCLASSEX wc;&lt;br /&gt;&lt;br /&gt;  wc.cbSize = sizeof(WNDCLASSEX);&lt;br /&gt;  wc.style = CS_HREDRAW | CS_VREDRAW;&lt;br /&gt;  wc.lpfnWndProc = WindowProc;&lt;br /&gt;  wc.cbClsExtra = 0;&lt;br /&gt;  wc.cbWndExtra = 0;&lt;br /&gt;  wc.hInstance = hInstance;&lt;br /&gt;  wc.hIcon = (HICON)LoadImage(NULL,&lt;br /&gt;    MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;    IMAGE_ICON,&lt;br /&gt;    0,&lt;br /&gt;    0,&lt;br /&gt;    LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  wc.hCursor = (HCURSOR)LoadImage(NULL,&lt;br /&gt;    MAKEINTRESOURCE(IDC_ARROW),&lt;br /&gt;    IMAGE_CURSOR,&lt;br /&gt;    0,&lt;br /&gt;    0,&lt;br /&gt;    LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  wc.hbrBackground = NULL;&lt;br /&gt;  wc.lpszMenuName = NULL;&lt;br /&gt;  wc.lpszClassName = APP_NAME;&lt;br /&gt;  wc.hIconSm = (HICON)LoadImage(NULL,&lt;br /&gt;    MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;    IMAGE_ICON,&lt;br /&gt;    0,&lt;br /&gt;    0,&lt;br /&gt;    LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  if(!RegisterClassEx(&amp;amp;wc)) return -1;&lt;br /&gt;&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  // 固定サイズのウィンドウ作成&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  HWND hWnd = CreateWindow(APP_NAME,&lt;br /&gt;    WINDOW_NAME,&lt;br /&gt;    WS_OVERLAPPEDWINDOW^WS_THICKFRAME^WS_MAXIMIZEBOX, // サイズ変更禁止&lt;br /&gt;    CW_USEDEFAULT,&lt;br /&gt;    CW_USEDEFAULT,&lt;br /&gt;    WINDOW_WIDTH,&lt;br /&gt;    WINDOW_HEIGHT,&lt;br /&gt;    NULL,&lt;br /&gt;    NULL,&lt;br /&gt;    hInstance,&lt;br /&gt;    NULL);&lt;br /&gt;  if(!hWnd) return -1;&lt;br /&gt;  ValidateRect(hWnd, NULL);&lt;br /&gt;&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  // Direct3Dを初期化&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  if(FAILED(InitD3D(hWnd))) return -1;&lt;br /&gt;&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  // ウィンドウ表示&lt;br /&gt;  // --------------------------------------&lt;br /&gt;  ShowWindow(hWnd, nCmdShow);&lt;br /&gt;  UpdateWindow(hWnd);&lt;br /&gt;&lt;br /&gt;  while(TRUE)&lt;br /&gt;  {&lt;br /&gt;    if (PeekMessage(&amp;amp;msg, NULL, 0U, 0U, PM_REMOVE))&lt;br /&gt;    {&lt;br /&gt;      if (msg.message == WM_QUIT) break;&lt;br /&gt;      TranslateMessage(&amp;amp;msg);&lt;br /&gt;      DispatchMessage(&amp;amp;msg);&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;      Render();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  if (g_pD3DDev) g_pD3DDev-&amp;gt;Release();&lt;br /&gt;  if (g_pD3D)    g_pD3D-&amp;gt;Release();&lt;br /&gt;  CloseHandle(hMutex);&lt;br /&gt;  return (int)msg.wParam;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//------Direct3Dを初期化します&lt;br /&gt;HRESULT InitD3D(HWND hWnd)&lt;br /&gt;{&lt;br /&gt;  if((g_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)&lt;br /&gt;    return E_FAIL;&lt;br /&gt;&lt;br /&gt;  D3DDISPLAYMODE d3ddm;&lt;br /&gt;  if(FAILED(g_pD3D-&amp;gt;GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &amp;amp;d3ddm)))&lt;br /&gt;    return E_FAIL;&lt;br /&gt;&lt;br /&gt;  ZeroMemory(&amp;amp;g_D3DPresentParam, sizeof(g_D3DPresentParam));&lt;br /&gt;  g_D3DPresentParam.BackBufferCount = 1;&lt;br /&gt;  g_D3DPresentParam.Windowed = TRUE;&lt;br /&gt;  g_D3DPresentParam.BackBufferFormat = D3DFMT_UNKNOWN;&lt;br /&gt;  g_D3DPresentParam.SwapEffect = D3DSWAPEFFECT_DISCARD;&lt;br /&gt;  g_D3DPresentParam.EnableAutoDepthStencil = TRUE;&lt;br /&gt;  g_D3DPresentParam.AutoDepthStencilFormat = D3DFMT_D16;&lt;br /&gt;&lt;br /&gt;  if(FAILED(g_pD3D-&amp;gt;CreateDevice(D3DADAPTER_DEFAULT,&lt;br /&gt;    D3DDEVTYPE_HAL,&lt;br /&gt;    hWnd,&lt;br /&gt;    D3DCREATE_HARDWARE_VERTEXPROCESSING,&lt;br /&gt;    &amp;amp;g_D3DPresentParam,&lt;br /&gt;    &amp;amp;g_pD3DDev)))&lt;br /&gt;  {&lt;br /&gt;&lt;br /&gt;    if(FAILED(g_pD3D-&amp;gt;CreateDevice(D3DADAPTER_DEFAULT,&lt;br /&gt;      D3DDEVTYPE_HAL,&lt;br /&gt;      hWnd,&lt;br /&gt;      D3DCREATE_SOFTWARE_VERTEXPROCESSING,&lt;br /&gt;      &amp;amp;g_D3DPresentParam,&lt;br /&gt;      &amp;amp;g_pD3DDev)))&lt;br /&gt;    {&lt;br /&gt;      if(FAILED(g_pD3D-&amp;gt;CreateDevice(D3DADAPTER_DEFAULT,&lt;br /&gt;        D3DDEVTYPE_REF,&lt;br /&gt;        hWnd,&lt;br /&gt;        D3DCREATE_SOFTWARE_VERTEXPROCESSING,&lt;br /&gt;        &amp;amp;g_D3DPresentParam,&lt;br /&gt;        &amp;amp;g_pD3DDev)))&lt;br /&gt;      {&lt;br /&gt;        return E_FAIL;&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return S_OK;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//------コールバック関数&lt;br /&gt;LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, &lt;br /&gt;              WPARAM wParam, LPARAM lParam)&lt;br /&gt;{&lt;br /&gt;  switch(uMsg)&lt;br /&gt;  {&lt;br /&gt;    case WM_DESTROY:&lt;br /&gt;      PostQuitMessage(0);&lt;br /&gt;      return 0;&lt;br /&gt;&lt;br /&gt;    case WM_CREATE:&lt;br /&gt;      // ======================================  &lt;br /&gt;      // 頂点集合にランダムでデータをセット&lt;br /&gt;      // ======================================  &lt;br /&gt;      for(int i = 0; i &amp;lt; 200; ++i) {&lt;br /&gt;        Tercel::Vector v; // ベクトルを宣言&lt;br /&gt;        float r   = 0.6f * SCREEN_WIDTH + 0.1f * (rand() % 10);&lt;br /&gt;        float phi = D3DXToRadian(rand() % 180 - 90);&lt;br /&gt;        float theta = D3DXToRadian(rand() % 360);&lt;br /&gt;&lt;br /&gt;        v.x = r*cos(phi)*cos(theta);&lt;br /&gt;        v.y = r*sin(phi);&lt;br /&gt;        v.z = r*cos(phi)*sin(theta);&lt;br /&gt;&lt;br /&gt;        vertices.insert(v);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // ======================================  &lt;br /&gt;      // Delaunay分割を行う関数&lt;br /&gt;      // ======================================  &lt;br /&gt;      &lt;br /&gt;      // 結果は第2引数（std::set&amp;lt;Tercel::Triangle&amp;gt; 型）に格納される。&lt;br /&gt;      // &lt;br /&gt;      // Tercel::Triangle のメンバには、3つの頂点p1, p2, p3 があって、&lt;br /&gt;      // いずれも Tercel::Vertex オブジェクトへのポインタになっている&lt;br /&gt;      // これらのポインタは、vertices リストの要素を参照している。&lt;br /&gt;      Tercel::Delaunay3d::getDelaunayTriangles(vertices, &amp;amp;triangles);&lt;br /&gt;      //                                       ~~~~~~~~  ~~~~~~~~~~&lt;br /&gt;      //                                       頂点集合  三角形集合&lt;br /&gt;      //                                       （参照） （ポインタ）&lt;br /&gt;      return 0;&lt;br /&gt;  }&lt;br /&gt;  return DefWindowProc(hWnd, uMsg, wParam, lParam);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//------描画関数&lt;br /&gt;VOID Render() {&lt;br /&gt;  if(FAILED(g_pD3DDev-&amp;gt;Clear(0, NULL,&lt;br /&gt;    D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,&lt;br /&gt;    D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f),&lt;br /&gt;    1.0f, 0))) return;&lt;br /&gt;  // ワールド行列&lt;br /&gt;  D3DXMATRIX mWorld;&lt;br /&gt;  D3DXMatrixIdentity(&amp;amp;mWorld);&lt;br /&gt;  D3DXMatrixRotationY(&amp;amp;mWorld, &lt;br /&gt;                      D3DXToRadian(timeGetTime()/50.0f));&lt;br /&gt;  g_pD3DDev-&amp;gt;SetTransform(D3DTS_WORLD, &amp;amp;mWorld);&lt;br /&gt;&lt;br /&gt;  // ビュー行列&lt;br /&gt;  const float PI = 3.1415926535f;&lt;br /&gt;  D3DXVECTOR3 vEyePoint(0.0f, 0.0f, 1500.0f);&lt;br /&gt;  D3DXVECTOR3 vLookAtPt(0.0f, 0.0f, 0.0f);&lt;br /&gt;  D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);&lt;br /&gt;&lt;br /&gt;  D3DXMATRIX mView;&lt;br /&gt;  D3DXMatrixLookAtLH(&amp;amp;mView, &amp;amp;vEyePoint, &amp;amp;vLookAtPt, &amp;amp;vUp);&lt;br /&gt;  g_pD3DDev-&amp;gt;SetTransform(D3DTS_VIEW, &amp;amp;mView);&lt;br /&gt;&lt;br /&gt;  float aspect;&lt;br /&gt;  aspect = (float)SCREEN_WIDTH / (float)SCREEN_HEIGHT;&lt;br /&gt;    &lt;br /&gt;  // 射影行列&lt;br /&gt;  D3DXMATRIX mProj;&lt;br /&gt;  D3DXMatrixPerspectiveFovLH(&amp;amp;mProj,&lt;br /&gt;    D3DXToRadian(45.0f),&lt;br /&gt;    aspect,&lt;br /&gt;    1.0f,&lt;br /&gt;    10000.0f);&lt;br /&gt;  g_pD3DDev-&amp;gt;SetTransform(D3DTS_PROJECTION, &amp;amp;mProj);&lt;br /&gt;&lt;br /&gt;  if(SUCCEEDED(g_pD3DDev-&amp;gt;BeginScene()))&lt;br /&gt;  {&lt;br /&gt;    // ======================================  &lt;br /&gt;    // ここから描画処理&lt;br /&gt;    // ======================================&lt;br /&gt;    ColorPoint vPoint[2];&lt;br /&gt;&lt;br /&gt;    vPoint[0].color = D3DCOLOR_ARGB(255, 0, 100, 200);&lt;br /&gt;    vPoint[1].color = D3DCOLOR_ARGB(255, 0, 100, 200);&lt;br /&gt;&lt;br /&gt;    g_pD3DDev-&amp;gt;SetFVF(D3DFVF_XYZ | D3DFVF_DIFFUSE);&lt;br /&gt;    g_pD3DDev-&amp;gt;SetRenderState(D3DRS_LIGHTING, FALSE);&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // Delaunay分割された三角形の集合を一つ一つ取りだして描画&lt;br /&gt;    // ======================================  &lt;br /&gt;    for(std::set&amp;lt;Tercel::Triangle&amp;gt;::iterator it = triangles.begin();&lt;br /&gt;      it != triangles.end(); ++it) &lt;br /&gt;    {&lt;br /&gt;      Tercel::Triangle t = *it;  // 三角形取得&lt;br /&gt;      for(int i = 0; i &amp;lt; 3; ++i) {&lt;br /&gt;        float x1 = (float)t.p[i]-&amp;gt;x;&lt;br /&gt;        float y1 = (float)t.p[i]-&amp;gt;y;&lt;br /&gt;        float z1 = (float)t.p[i]-&amp;gt;z;&lt;br /&gt;&lt;br /&gt;        float x2 = (float)t.p[(i+1)%3]-&amp;gt;x;&lt;br /&gt;        float y2 = (float)t.p[(i+1)%3]-&amp;gt;y;&lt;br /&gt;        float z2 = (float)t.p[(i+1)%3]-&amp;gt;z;&lt;br /&gt;        vPoint[0].vCoord = D3DXVECTOR3(x1, y1, z1);&lt;br /&gt;        vPoint[1].vCoord = D3DXVECTOR3(x2, y2, z2);&lt;br /&gt;        g_pD3DDev-&amp;gt;DrawPrimitiveUP(D3DPT_LINELIST, 1, &lt;br /&gt;          vPoint, sizeof(ColorPoint));&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    g_pD3DDev-&amp;gt;EndScene();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  //----- デバイスロスト対策&lt;br /&gt;  if(g_pD3DDev-&amp;gt;Present(NULL, NULL, NULL, NULL) == D3DERR_DEVICELOST)&lt;br /&gt;  {&lt;br /&gt;    if(g_pD3DDev-&amp;gt;TestCooperativeLevel() == D3DERR_DEVICELOST)&lt;br /&gt;    {&lt;br /&gt;      return;&lt;br /&gt;    }&lt;br /&gt;    g_pD3DDev-&amp;gt;Reset(&amp;amp;g_D3DPresentParam);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;【おまけ】&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;!-- http://twitter.com/nekomatu/status/138589983735947264 --&gt; &lt;style type='text/css'&gt;.bbpBox{background:url(http://a0.twimg.com/images/themes/theme1/bg.png) #C0DEED;padding:20px;}&lt;/style&gt;&lt;div id='tweet_138589983735947264' class='bbpBox' style='background:url(http://a0.twimg.com/images/themes/theme1/bg.png) #C0DEED;padding:20px;'&gt;&lt;p class='bbpTweet' style='background:#fff;padding:10px 12px 10px 12px;margin:0;min-height:48px;color:#000;font-size:16px !important;line-height:22px;-moz-border-radius:5px;-webkit-border-radius:5px;'&gt;勢いでリツイートしてしまったがコード付きという神記事だった&lt;span class='timestamp' style='font-size:12px;display:block;'&gt;&lt;a title='2011年11月21日 21:10' href='http://twitter.com/nekomatu/status/138589983735947264'&gt;2011年11月21日 21:10&lt;/a&gt; via &lt;a href="http://www.tweetdeck.com" rel="nofollow"&gt;TweetDeck&lt;/a&gt;&lt;/span&gt;&lt;span class='metadata' style='display:block;width:100%;clear:both;margin-top:8px;padding-top:12px;height:40px;border-top:1px solid #fff;border-top:1px solid #e6e6e6;'&gt;&lt;span class='author' style='line-height:19px;'&gt;&lt;a href='http://twitter.com/nekomatu'&gt;&lt;img src='http://a1.twimg.com/profile_images/1202442537/e1edb174-da7c-4631-aba4-95faf1d1e9d0_normal.jpeg' style="border: 0; float: left; height: 38px; margin: 0 7px 0 0px; padding: 0; width: 38px;" /&gt;&lt;/a&gt;&lt;strong&gt;&lt;a href='http://twitter.com/nekomatu'&gt;nekomatu&lt;/a&gt;&lt;/strong&gt;&lt;br /&gt;nekomatu&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;!-- end of tweet --&gt;&lt;br /&gt;&lt;br /&gt;わわっΣ(･Д･ﾉ)ﾉ 恐縮です（照&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-7621034275559143733?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/7621034275559143733/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-3-delaunay.html#comment-form' title='1 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7621034275559143733'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7621034275559143733'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-3-delaunay.html' title='C++ で 3 次元 Delaunay 分割'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-ef4FWJLJIMg/Tso2svkC4TI/AAAAAAAAAa4/BQlXvLDyMAY/s72-c/delaunay3dcpp.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-1931084094132277200</id><published>2011-11-20T19:36:00.002+09:00</published><updated>2011-11-20T19:42:06.395+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows API'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>続： C++ で Delaunay 分割</title><content type='html'>昨日、&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay-2.html"&gt;C++ で Delaunay 分割を実装&lt;/a&gt;しました。&lt;br /&gt;&lt;br /&gt;で。昨日はデータ構造に線形リスト（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::list&lt;/span&gt;）のみを用いており、コーディングしやすい代わりにあまり効率的とはいえない実装になっていました。&lt;br /&gt;&lt;br /&gt;今回は、リストの代わりにセット（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::set&lt;/span&gt;）を用いる事で、内部処理の効率化を図りました。セットの実体は&lt;a href="http://ja.wikipedia.org/wiki/2%E5%88%86%E6%8E%A2%E7%B4%A2%E6%9C%A8"&gt;二分探索木&lt;/a&gt;（おそらくは&lt;a href="http://ja.wikipedia.org/wiki/%E8%B5%A4%E9%BB%92%E6%9C%A8"&gt;赤黒木&lt;/a&gt;）であり、『重複する要素を持たない』『要素の探索が対数時間』というなかなかおいしいデータ構造のため、昨日の段階でネックになっていた冗長な重複判定処理がかなり省かれています。&lt;br /&gt;&lt;br /&gt;ではなぜ最初からそれを使わなかったかというと、ずばり &lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::set&lt;/span&gt; の使い方をよく知らなかったから&lt;/b&gt;です。……恥ずかしい。&lt;br /&gt;&lt;br /&gt;前述の通り、セットの内部構造は二分探索木ですので、&lt;b&gt;セットに格納する要素同士の大小関係を判定できなくてはなりません&lt;/b&gt;。単純に比較できない『頂点』や『三角形』の大小関係をどう定義するかで悩みましたが、結局&lt;a href="http://ja.wikipedia.org/wiki/%E8%BE%9E%E6%9B%B8%E5%BC%8F%E9%A0%86%E5%BA%8F"&gt;辞書式順序&lt;/a&gt;で順序づける事にしました（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Vector&lt;/span&gt; や &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Triangle&lt;/span&gt; 構造体の &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;operator&amp;lt;&lt;/span&gt; が、比較関数として機能します）。&lt;br /&gt;&lt;br /&gt;ソースコードは以下。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Delaunay2d.h&lt;/span&gt;】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;//&lt;br /&gt;// Delaunay分割（やや効率向上？版）&lt;br /&gt;//&lt;br /&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#ifndef ___TERCEL_DELAUNAY2D&lt;br /&gt;#define ___TERCEL_DELAUNAY2D&lt;br /&gt;&lt;br /&gt;#include &amp;lt;cfloat&amp;gt;&lt;br /&gt;#include &amp;lt;cmath&amp;gt;&lt;br /&gt;&lt;br /&gt;#include &amp;lt;map&amp;gt;&lt;br /&gt;#include &amp;lt;set&amp;gt;&lt;br /&gt;&lt;br /&gt;namespace Tercel &lt;br /&gt;{&lt;br /&gt;  struct Vector {&lt;br /&gt;    double x;&lt;br /&gt;    double y;&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 等価性の判定&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator==(const Vector&amp;amp; v) const &lt;br /&gt;    {&lt;br /&gt;      return (x == v.x &amp;amp;&amp;amp; y == v.y);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 大小判定（set / mapを構築する際に使用）&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator&amp;lt;(const Vector&amp;amp; v) const &lt;br /&gt;    {&lt;br /&gt;      return x != v.x ? x &amp;lt; v.x : y &amp;lt; v.y;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  struct Circle &lt;br /&gt;  {&lt;br /&gt;    Vector center;  // 中心座標&lt;br /&gt;    double radius;  // 半径&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  class Triangle {&lt;br /&gt;  public:&lt;br /&gt;    const Vector* p1, * p2, * p3;  // 頂点座標&lt;br /&gt;&lt;br /&gt;  private:&lt;br /&gt;    // 最小の頂点を返す   &lt;br /&gt;    // --------------------------------------&lt;br /&gt;    inline const Vector* getMinVertex() const &lt;br /&gt;    {&lt;br /&gt;      return *p1 &amp;lt; *p2 &amp;amp;&amp;amp; *p1 &amp;lt; *p3 ? p1 : *p2 &amp;lt; *p3 ? p2 : p3;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // まんなかの頂点を返す&lt;br /&gt;    // --------------------------------------&lt;br /&gt;    inline const Vector* getMidVertex() const &lt;br /&gt;    {&lt;br /&gt;      return *p1 &amp;lt; *p2 &amp;amp;&amp;amp; *p2 &amp;lt; *p3 || *p3 &amp;lt; *p2 &amp;amp;&amp;amp; *p2 &amp;lt; *p1 ? p2 : &lt;br /&gt;             *p2 &amp;lt; *p3 &amp;amp;&amp;amp; *p3 &amp;lt; *p1 || *p1 &amp;lt; *p3 &amp;amp;&amp;amp; *p3 &amp;lt; *p2 ? p3 : p1;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // 最大の頂点を返す&lt;br /&gt;    // --------------------------------------&lt;br /&gt;    inline const Vector* getMaxVertex() const &lt;br /&gt;    {&lt;br /&gt;      return *p2 &amp;lt; *p1 &amp;amp;&amp;amp; *p3 &amp;lt; *p1 ? p1 : *p3 &amp;lt; *p2 ? p2 : p3;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  public:&lt;br /&gt;    &lt;br /&gt;    // ======================================  &lt;br /&gt;    // 等価性の判定&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator==(const Triangle&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      return *p1 == *t.p1 &amp;amp;&amp;amp; *p2 == *t.p2 &amp;amp;&amp;amp; *p3 == *t.p3 ||&lt;br /&gt;             *p1 == *t.p2 &amp;amp;&amp;amp; *p2 == *t.p3 &amp;amp;&amp;amp; *p3 == *t.p1 ||&lt;br /&gt;             *p1 == *t.p3 &amp;amp;&amp;amp; *p2 == *t.p1 &amp;amp;&amp;amp; *p3 == *t.p2 ||&lt;br /&gt;&lt;br /&gt;             *p1 == *t.p3 &amp;amp;&amp;amp; *p2 == *t.p2 &amp;amp;&amp;amp; *p3 == *t.p1 ||&lt;br /&gt;             *p1 == *t.p2 &amp;amp;&amp;amp; *p2 == *t.p1 &amp;amp;&amp;amp; *p3 == *t.p3 ||&lt;br /&gt;             *p1 == *t.p1 &amp;amp;&amp;amp; *p2 == *t.p3 &amp;amp;&amp;amp; *p3 == *t.p2;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 大小判定（set / mapを構築する際に使用）&lt;br /&gt;    // ====================================== &lt;br /&gt;    bool operator&amp;lt;(const Triangle&amp;amp; t) const &lt;br /&gt;    {&lt;br /&gt;      return !(*getMinVertex() == *t.getMinVertex()) ? &lt;br /&gt;                 *getMinVertex() &amp;lt; *t.getMinVertex() :&lt;br /&gt;             !(*getMidVertex() == *t.getMidVertex()) ? &lt;br /&gt;                 *getMidVertex() &amp;lt; *t.getMidVertex() :&lt;br /&gt;                 *getMaxVertex() &amp;lt; *t.getMaxVertex();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 他の三角形と共有点を持つか  &lt;br /&gt;    // ====================================== &lt;br /&gt;    bool hasCommonPoints(const Triangle&amp;amp; t) const&lt;br /&gt;    {&lt;br /&gt;      return *p1 == *t.p1 || *p1 == *t.p2 || *p1 == *t.p3 ||&lt;br /&gt;             *p2 == *t.p1 || *p2 == *t.p2 || *p2 == *t.p3 ||&lt;br /&gt;             *p3 == *t.p1 || *p3 == *t.p2 || *p3 == *t.p3;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;  class Delaunay2d &lt;br /&gt;  {&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 型定義&lt;br /&gt;    // ====================================== &lt;br /&gt;    typedef const std::set&amp;lt;Vector&amp;gt;         ConstVertexSet;&lt;br /&gt;    typedef ConstVertexSet::const_iterator ConstVertexIter;&lt;br /&gt;&lt;br /&gt;    typedef std::set&amp;lt;Triangle&amp;gt;             TriangleSet;&lt;br /&gt;    typedef std::set&amp;lt;Triangle&amp;gt;::iterator   TriangleIter;&lt;br /&gt;&lt;br /&gt;    typedef std::map&amp;lt;Triangle, bool&amp;gt;       TriangleMap;&lt;br /&gt;&lt;br /&gt;  private:&lt;br /&gt;    // ======================================  &lt;br /&gt;    // 一時マップを使って重複判定  &lt;br /&gt;    // hashMap  &lt;br /&gt;    //  - Key : 三角形  &lt;br /&gt;    //  - Value : 重複していないかどうか  &lt;br /&gt;    //        - 重複していない : true  &lt;br /&gt;    //        - 重複している   : false  &lt;br /&gt;    // ======================================&lt;br /&gt;    static inline void addElementToRedundanciesMap(TriangleMap* pRddcMap,&lt;br /&gt;                                                   Triangle&amp;amp; t) &lt;br /&gt;    {&lt;br /&gt;      TriangleMap::iterator it = pRddcMap-&amp;gt;find(t);&lt;br /&gt;      if(it != pRddcMap-&amp;gt;end() &amp;amp;&amp;amp; it-&amp;gt;second) &lt;br /&gt;      {&lt;br /&gt;        // 値を (t, true) から (t, false) に置換&lt;br /&gt;        pRddcMap-&amp;gt;erase(it);&lt;br /&gt;        pRddcMap-&amp;gt;insert(TriangleMap::value_type(t, false));&lt;br /&gt;      } &lt;br /&gt;      else &lt;br /&gt;      {&lt;br /&gt;        pRddcMap-&amp;gt;insert(TriangleMap::value_type(t, true));&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  public:&lt;br /&gt;    static void getDelaunayTriangles(ConstVertexSet&amp;amp; pVertexSet, &lt;br /&gt;                                     TriangleSet* triangleSet) &lt;br /&gt;    {&lt;br /&gt;&lt;br /&gt;      Triangle hugeTriangle;&lt;br /&gt;      {&lt;br /&gt;        // ======================================  &lt;br /&gt;        // 外部三角形を作る  &lt;br /&gt;        // ======================================  &lt;br /&gt;        double maxX, maxY; maxX = maxY = DBL_MIN;&lt;br /&gt;        double minX, minY; minX = minY = DBL_MAX;&lt;br /&gt;        for(ConstVertexIter it = pVertexSet.begin(); &lt;br /&gt;            it != pVertexSet.end(); ++it) &lt;br /&gt;        {&lt;br /&gt;          double x = it-&amp;gt;x;&lt;br /&gt;          double y = it-&amp;gt;y;&lt;br /&gt;          if(maxX &amp;lt; x) maxX = x; if(minX &amp;gt; x) minX = x;&lt;br /&gt;          if(maxY &amp;lt; y) maxY = y; if(minY &amp;gt; y) minY = y;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        // すべての点を包含する矩形の外接円&lt;br /&gt;        double centerX  = (maxX - minX) * 0.5;      // 中心x座標&lt;br /&gt;        double centerY  = (maxY - minY) * 0.5;      // 中心y座標&lt;br /&gt;&lt;br /&gt;        double dX   = maxX - centerX;&lt;br /&gt;        double dY   = maxY - centerY;&lt;br /&gt;        double radius = sqrt(dX * dX + dY * dY) + 1.0;  // 半径&lt;br /&gt;&lt;br /&gt;        Vector* p1 = new Vector;  // メモリ確保（314行目で解放）&lt;br /&gt;        p1-&amp;gt;x    = centerX - sqrt(3.0) * radius;&lt;br /&gt;        p1-&amp;gt;y    = centerY - radius;&lt;br /&gt;&lt;br /&gt;        Vector* p2 = new Vector;  // メモリ確保（315行目で解放）&lt;br /&gt;        p2-&amp;gt;x    = centerX + sqrt(3.0) * radius;&lt;br /&gt;        p2-&amp;gt;y    = centerY - radius;&lt;br /&gt;&lt;br /&gt;        Vector* p3 = new Vector;  // メモリ確保（316行目で解放）&lt;br /&gt;        p3-&amp;gt;x    = centerX;&lt;br /&gt;        p3-&amp;gt;y    = centerY + 2.0 * radius;&lt;br /&gt;&lt;br /&gt;        hugeTriangle.p1 = p1;&lt;br /&gt;        hugeTriangle.p2 = p2;&lt;br /&gt;        hugeTriangle.p3 = p3;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      triangleSet-&amp;gt;insert(hugeTriangle);&lt;br /&gt;&lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      // 点を逐次添加し、反復的に三角分割を行う  &lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      for(ConstVertexIter vIter = pVertexSet.begin(); &lt;br /&gt;          vIter != pVertexSet.end(); ++vIter) &lt;br /&gt;      {&lt;br /&gt;        const Vector* p = &amp;amp;*vIter;&lt;br /&gt;&lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        // 追加候補の三角形を保持する一時マップ&lt;br /&gt;        // -------------------------------------- &lt;br /&gt;        TriangleMap rddcMap;&lt;br /&gt;&lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        // 現在の三角形セットから要素を一つずつ取り出して、  &lt;br /&gt;        // 与えられた点が各々の三角形の外接円の中に含まれるかどうか判定  &lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        for(TriangleIter tIter = triangleSet-&amp;gt;begin(); &lt;br /&gt;            tIter != triangleSet-&amp;gt;end();) &lt;br /&gt;        {&lt;br /&gt;          // 三角形セットから三角形を取りだして…&lt;br /&gt;          Triangle t = *tIter;&lt;br /&gt;&lt;br /&gt;          // その外接円を求める。  &lt;br /&gt;          Circle   c;&lt;br /&gt;          {&lt;br /&gt;            // 三角形の各頂点座標を (x1, y1), (x2, y2), (x3, y3) とし、  &lt;br /&gt;            // その外接円の中心座標を (x, y) とすると、  &lt;br /&gt;            //     (x - x1) * (x - x1) + (y - y1) * (y - y1)  &lt;br /&gt;            //   = (x - x2) * (x - x2) + (y - y2) * (y - y2)  &lt;br /&gt;            //   = (x - x3) * (x - x3) + (y - y3) * (y - y3)  &lt;br /&gt;            // より、以下の式が成り立つ  &lt;br /&gt;            //  &lt;br /&gt;            // x = { (y3-y1) * (x2*x2-x1*x1+y2*y2-y1*y1)  &lt;br /&gt;            //     + (y1-y2) * (x3*x3-x1*x1+y3*y3-y1*y1) } / m&lt;br /&gt;            //  &lt;br /&gt;            // y = { (x1-x3) * (x2*x2-x1*x1+y2*y2-y1*y1)  &lt;br /&gt;            //     + (x2-x1) * (x3*x3-x1*x1+y3*y3-y1*y1) } / m  &lt;br /&gt;            //  &lt;br /&gt;            // ただし、  &lt;br /&gt;            // m = 2 * {(x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)} &lt;br /&gt;&lt;br /&gt;            double x1 = t.p1-&amp;gt;x;  double y1 = t.p1-&amp;gt;y;&lt;br /&gt;            double x2 = t.p2-&amp;gt;x;  double y2 = t.p2-&amp;gt;y;&lt;br /&gt;            double x3 = t.p3-&amp;gt;x;  double y3 = t.p3-&amp;gt;y;&lt;br /&gt;&lt;br /&gt;            double m = 2.0*((x2-x1)*(y3-y1)-(y2-y1)*(x3-x1));  &lt;br /&gt;            double x = ((y3-y1)*(x2*x2-x1*x1+y2*y2-y1*y1)&lt;br /&gt;                       +(y1-y2)*(x3*x3-x1*x1+y3*y3-y1*y1))/m;&lt;br /&gt;            double y = ((x1-x3)*(x2*x2-x1*x1+y2*y2-y1*y1)&lt;br /&gt;                       +(x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1))/m;&lt;br /&gt;&lt;br /&gt;            c.center.x = x; &lt;br /&gt;            c.center.y = y;&lt;br /&gt;&lt;br /&gt;            // 外接円の半径 r は、半径から三角形の任意の頂点までの距離に等しい &lt;br /&gt;            double dx   = t.p1-&amp;gt;x - x;&lt;br /&gt;            double dy   = t.p1-&amp;gt;y - y;&lt;br /&gt;            double radius = sqrt(dx * dx + dy * dy);&lt;br /&gt;&lt;br /&gt;            c.radius = radius;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          double dx = c.center.x - p-&amp;gt;x;&lt;br /&gt;          double dy = c.center.y - p-&amp;gt;y;&lt;br /&gt;          double dist = sqrt(dx * dx + dy * dy);&lt;br /&gt;&lt;br /&gt;          // --------------------------------------  &lt;br /&gt;          // 追加された点が外接円内部に存在する場合、  &lt;br /&gt;          // その外接円を持つ三角形をリストから除外し、  &lt;br /&gt;          // 新たに分割し直す  &lt;br /&gt;          // -------------------------------------- &lt;br /&gt;          if(dist &amp;lt; c.radius) &lt;br /&gt;          {&lt;br /&gt;            Triangle t1;&lt;br /&gt;            t1.p1 = p; t1.p2 = t.p1; t1.p3 = t.p2;&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t1);&lt;br /&gt; &lt;br /&gt;            Triangle t2;&lt;br /&gt;            t2.p1 = p; t2.p2 = t.p2; t2.p3 = t.p3;&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t2);&lt;br /&gt; &lt;br /&gt;            Triangle t3;&lt;br /&gt;            t3.p1 = p; t3.p2 = t.p3; t3.p3 = t.p1;&lt;br /&gt;            addElementToRedundanciesMap(&amp;amp;rddcMap, t3);&lt;br /&gt;&lt;br /&gt;            triangleSet-&amp;gt;erase(tIter++);&lt;br /&gt;          } &lt;br /&gt;          else ++tIter;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        // 一時マップのうち、重複のないものを三角形リストに追加   &lt;br /&gt;        // --------------------------------------  &lt;br /&gt;        for(TriangleMap::iterator iter = rddcMap.begin();&lt;br /&gt;          iter != rddcMap.end(); ++iter) &lt;br /&gt;        {&lt;br /&gt;          if(iter-&amp;gt;second) triangleSet-&amp;gt;insert(iter-&amp;gt;first);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      // 最後に、外部三角形の頂点を削除&lt;br /&gt;      // --------------------------------------  &lt;br /&gt;      for(TriangleIter tIter = triangleSet-&amp;gt;begin(); &lt;br /&gt;          tIter != triangleSet-&amp;gt;end(); ) &lt;br /&gt;      {&lt;br /&gt;        if(hugeTriangle.hasCommonPoints(*tIter))&lt;br /&gt;          triangleSet-&amp;gt;erase(tIter++);&lt;br /&gt;        else ++tIter;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // 巨大三角形の頂点を解放&lt;br /&gt;      delete hugeTriangle.p1;&lt;br /&gt;      delete hugeTriangle.p2;&lt;br /&gt;      delete hugeTriangle.p3;&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ちなみに使い方（というか Windows の GUI による結果表示用プログラム）はこちら。&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt;】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++" name="code"&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;//&lt;br /&gt;// Delaunay分割（やや効率化版）&lt;br /&gt;//           結果表示用のウィンドウ&lt;br /&gt;//&lt;br /&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;#include &amp;lt;windows.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include "Delaunay2d.h"&lt;br /&gt;&lt;br /&gt;// アプリケーションとウィンドウの初期化に必要な定数&lt;br /&gt;// ----------------------------------------&lt;br /&gt;const LPCTSTR APP_NAME   = TEXT("APP_TERCEL_TECH");&lt;br /&gt;const LPCTSTR MUTEX_NAME = TEXT("MUTEX_TERCEL_TECH");&lt;br /&gt;&lt;br /&gt;// クライアント領域の幅と高さ&lt;br /&gt;// ----------------------------------------&lt;br /&gt;const UINT WIDTH  = 400;&lt;br /&gt;const UINT HEIGHT = 300;&lt;br /&gt;&lt;br /&gt;// ----------------------------------------&lt;br /&gt;// 頂点集合、およびそれらを基に&lt;br /&gt;// Delaunay 分割された三角形を格納しておくSTLコンテナ&lt;br /&gt;// ----------------------------------------&lt;br /&gt;std::set&amp;lt;Tercel::Vector&amp;gt;   vertices;  // ←型に注意！！&lt;br /&gt;std::set&amp;lt;Tercel::Triangle&amp;gt; triangles; // ←型に注意！！&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// コールバック関数（メッセージ処理）&lt;br /&gt;// ========================================&lt;br /&gt;LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg,&lt;br /&gt;              WPARAM wParam, LPARAM lParam)&lt;br /&gt;{&lt;br /&gt;  switch(uMsg)&lt;br /&gt;  {&lt;br /&gt;  case WM_DESTROY:&lt;br /&gt;    PostQuitMessage(0);&lt;br /&gt;    return 0;&lt;br /&gt;&lt;br /&gt;  case WM_CREATE:&lt;br /&gt;    {&lt;br /&gt;      // ======================================  &lt;br /&gt;      // 頂点集合にランダムでデータをセット&lt;br /&gt;      // ======================================  &lt;br /&gt;      for(int i = 0; i &amp;lt; 200; ++i) {&lt;br /&gt;        Tercel::Vector v; // ベクトルを宣言&lt;br /&gt;        v.x   = rand() % WIDTH;&lt;br /&gt;        v.y   = rand() % HEIGHT;&lt;br /&gt;        vertices.insert(v); // ←変更！！&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // ======================================  &lt;br /&gt;      // Delaunay分割を行う関数&lt;br /&gt;      // ======================================  &lt;br /&gt;      &lt;br /&gt;      // 結果は第2引数（std::set&amp;lt;Tercel::Triangle&amp;gt; 型）に格納される。&lt;br /&gt;      // &lt;br /&gt;      // Tercel::Triangle のメンバには、3つの頂点p1, p2, p3 があって、&lt;br /&gt;      // いずれも Tercel::Vertex オブジェクトへのポインタになっている&lt;br /&gt;      // これらのポインタは、vertices リストの要素を参照している。&lt;br /&gt;      Tercel::Delaunay2d::getDelaunayTriangles(vertices, &amp;amp;triangles);&lt;br /&gt;      //                     ~~~~~~~~  ~~~~~~~~~~&lt;br /&gt;      //                     頂点集合  三角形集合&lt;br /&gt;      //                     （参照） （ポインタ）&lt;br /&gt;    }&lt;br /&gt;    return 0;&lt;br /&gt;&lt;br /&gt;  case WM_PAINT:&lt;br /&gt;    {&lt;br /&gt;      typedef std::set&amp;lt;Tercel::Triangle&amp;gt; TriangleSet;&lt;br /&gt;&lt;br /&gt;      PAINTSTRUCT ps;&lt;br /&gt;      HDC     hdc  = BeginPaint(hWnd, &amp;amp;ps);&lt;br /&gt;      HPEN    hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));&lt;br /&gt;      &lt;br /&gt;      SelectObject(hdc, hPen);&lt;br /&gt;&lt;br /&gt;      // ======================================  &lt;br /&gt;      // Delaunay分割された三角形の集合を一つ一つ描画&lt;br /&gt;      // ======================================  &lt;br /&gt;      for(TriangleSet::iterator it = triangles.begin();&lt;br /&gt;        it != triangles.end(); ++it) &lt;br /&gt;      {&lt;br /&gt;        Tercel::Triangle t = *it;  // 三角形取得&lt;br /&gt;        int x, y;&lt;br /&gt;&lt;br /&gt;        x = int(t.p1-&amp;gt;x + 0.5);&lt;br /&gt;        y = int(t.p1-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;        MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;        x = int(t.p2-&amp;gt;x + 0.5);&lt;br /&gt;        y = int(t.p2-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;        LineTo(hdc, x, y);&lt;br /&gt;        MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;        x = int(t.p3-&amp;gt;x + 0.5);&lt;br /&gt;        y = int(t.p3-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;        LineTo(hdc, x, y);&lt;br /&gt;        MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;        x = int(t.p1-&amp;gt;x + 0.5);&lt;br /&gt;        y = int(t.p1-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;        LineTo(hdc, x, y);&lt;br /&gt;      }&lt;br /&gt;      DeleteObject(hPen);&lt;br /&gt;      EndPaint(hWnd, &amp;amp;ps);      &lt;br /&gt;    }&lt;br /&gt;    return 0;&lt;br /&gt;  }&lt;br /&gt;  return DefWindowProc(hWnd, uMsg, wParam, lParam);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// エントリポイント&lt;br /&gt;// ========================================&lt;br /&gt;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,&lt;br /&gt;           LPSTR lpCmdLine, int nCmdShow)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  // 多重起動防止&lt;br /&gt;  HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);&lt;br /&gt;  if(GetLastError() == ERROR_ALREADY_EXISTS)&lt;br /&gt;  {&lt;br /&gt;    // 多重起動を検知した際には、&lt;br /&gt;    // 既存のウィンドウを最前面に表示してプログラムを終了する&lt;br /&gt;    HWND existingWnd = FindWindow(APP_NAME, NULL);&lt;br /&gt;    if(existingWnd != NULL)&lt;br /&gt;    {&lt;br /&gt;      if(IsIconic(existingWnd))&lt;br /&gt;        ShowWindowAsync(existingWnd, SW_RESTORE);&lt;br /&gt;&lt;br /&gt;      SetForegroundWindow(existingWnd);&lt;br /&gt;    }&lt;br /&gt;    return -1;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // ウィンドウクラス登録&lt;br /&gt;  WNDCLASSEX wc;&lt;br /&gt;  wc.cbSize    = sizeof(WNDCLASSEX);&lt;br /&gt;  wc.style     = CS_HREDRAW | CS_VREDRAW;&lt;br /&gt;  wc.lpfnWndProc   = WindowProc;&lt;br /&gt;  wc.cbClsExtra  = 0;&lt;br /&gt;  wc.cbWndExtra  = sizeof(LONG_PTR);&lt;br /&gt;  wc.hInstance   = hInstance;&lt;br /&gt;  wc.hIcon     = (HICON)LoadImage(NULL,&lt;br /&gt;               MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;               IMAGE_ICON,&lt;br /&gt;               0,&lt;br /&gt;               0,&lt;br /&gt;               LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  wc.hCursor     = (HICON)LoadImage(NULL,&lt;br /&gt;               MAKEINTRESOURCE(IDC_ARROW),&lt;br /&gt;               IMAGE_CURSOR,&lt;br /&gt;               0,&lt;br /&gt;               0,&lt;br /&gt;               LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1;&lt;br /&gt;  wc.lpszMenuName  = NULL;&lt;br /&gt;  wc.lpszClassName = APP_NAME;&lt;br /&gt;  wc.hIconSm = (HICON)LoadImage(NULL,&lt;br /&gt;               MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;               IMAGE_ICON,&lt;br /&gt;               0,&lt;br /&gt;               0,&lt;br /&gt;               LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;  if(!RegisterClassEx(&amp;amp;wc)) return -1;&lt;br /&gt;&lt;br /&gt;  // クライアント領域のサイズから、ウィンドウそのものの幅と高さを計算&lt;br /&gt;  UINT windowWidth  = WIDTH  + GetSystemMetrics(SM_CXFIXEDFRAME) * 2;&lt;br /&gt;  UINT windowHeight = HEIGHT + GetSystemMetrics(SM_CYFIXEDFRAME) * 2&lt;br /&gt;                 + GetSystemMetrics(SM_CYCAPTION);&lt;br /&gt;  // ウィンドウ生成&lt;br /&gt;  HWND hWnd = CreateWindow(APP_NAME,&lt;br /&gt;          TEXT("はじめてのDelaunay分割"),&lt;br /&gt;          WS_OVERLAPPEDWINDOW^WS_THICKFRAME^WS_MAXIMIZEBOX,&lt;br /&gt;          CW_USEDEFAULT,&lt;br /&gt;          CW_USEDEFAULT,&lt;br /&gt;          windowWidth,&lt;br /&gt;          windowHeight,&lt;br /&gt;          NULL,&lt;br /&gt;          NULL,&lt;br /&gt;          hInstance,&lt;br /&gt;          NULL);&lt;br /&gt;  if(!hWnd) return -1;&lt;br /&gt;&lt;br /&gt;  // ウィンドウ表示&lt;br /&gt;  ShowWindow(hWnd, nCmdShow);&lt;br /&gt;  UpdateWindow(hWnd);&lt;br /&gt;&lt;br /&gt;  // メッセージループ&lt;br /&gt;  MSG msg;&lt;br /&gt;  while(TRUE)&lt;br /&gt;  {&lt;br /&gt;    if(PeekMessage(&amp;amp;msg, NULL, 0U, 0U, PM_REMOVE))&lt;br /&gt;    {&lt;br /&gt;      if(msg.message == WM_QUIT) break;&lt;br /&gt;      TranslateMessage(&amp;amp;msg);&lt;br /&gt;      DispatchMessage(&amp;amp;msg);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  CloseHandle(hMutex);&lt;br /&gt;&lt;br /&gt;  return (int)msg.wParam;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;イテレータのお陰で、データ構造が変わっても &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt; 側のソースコードはほとんどそのままです。便利ですね。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-1931084094132277200?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/1931084094132277200/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay.html#comment-form' title='2 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1931084094132277200'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/1931084094132277200'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay.html' title='続： C++ で Delaunay 分割'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-3073540410988707728</id><published>2011-11-19T18:11:00.003+09:00</published><updated>2011-11-20T19:47:50.897+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows API'/><category scheme='http://www.blogger.com/atom/ns#' term='C/C++'/><title type='text'>C++ で Delaunay 分割（ただし2次元）</title><content type='html'>ちょっと学業の方が忙しくなってしまい、ウェブログを放置していました……。&lt;br /&gt;&lt;br /&gt;忘れていたわけじゃないんだよ。ただちょっとモチベーションが足りなかっただけ。&lt;br /&gt;&lt;br /&gt;今日は、某所（CG 系の研究室）で需要がありそうだったので、以前 Processing で書いていた Delaunay 分割を C++ で実装し直してみる事にしました。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-8vUnAHigPMs/TsdwTwUemxI/AAAAAAAAAaw/FuMU2aZ0zG8/s1600/DelaunayCpp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-8vUnAHigPMs/TsdwTwUemxI/AAAAAAAAAaw/FuMU2aZ0zG8/s1600/DelaunayCpp.png" /&gt;&lt;/a&gt;&lt;/div&gt;ただし、C++ は&lt;b&gt;ものすごく苦手&lt;/b&gt;なので、ちょっと残念なソースになってしまっています。 ⇒ &lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay.html"&gt;11月20日追記：もうちょっと&lt;b&gt;内部実装がマシになりました&lt;/b&gt;&lt;/a&gt;。&lt;br /&gt;&lt;br /&gt;ソースコード上部の「view plain」という文字をクリックするとコピペ用の窓が出ますので、使いたい方はご自由にどうぞ。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【アルゴリズム部分（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Delaunay2d.h&lt;/span&gt;）】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++:collapse" name="code"&gt;#ifndef ___TERCEL_DELAUNAY2D&lt;br /&gt;#define ___TERCEL_DELAUNAY2D&lt;br /&gt;&lt;br /&gt;#include &amp;lt;cfloat&amp;gt;&lt;br /&gt;#include &amp;lt;cmath&amp;gt;&lt;br /&gt;#include &amp;lt;list&amp;gt;&lt;br /&gt;&lt;br /&gt;namespace Tercel &lt;br /&gt;{&lt;br /&gt;    struct Vector &lt;br /&gt;    {&lt;br /&gt;        double x, y;&lt;br /&gt;&lt;br /&gt;        bool operator==(const Vector&amp;amp; v) const &lt;br /&gt;        {&lt;br /&gt;            return (x == v.x &amp;amp;&amp;amp; y == v.y);&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    struct Circle &lt;br /&gt;    {&lt;br /&gt;        Vector center;  // 中心座標&lt;br /&gt;        double radius;  // 半径&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    class Triangle &lt;br /&gt;    {&lt;br /&gt;    public:&lt;br /&gt;        const Vector* p1, * p2, * p3;    // 頂点座標&lt;br /&gt;&lt;br /&gt;    public:&lt;br /&gt;        // ======================================  &lt;br /&gt;        // 等価性の判定&lt;br /&gt;        // ======================================   &lt;br /&gt;        bool operator==(const Triangle&amp;amp; t) const &lt;br /&gt;        {&lt;br /&gt;            return(*p1 == *t.p1 &amp;amp;&amp;amp; *p2 == *t.p2 &amp;amp;&amp;amp; *p3 == *t.p3 ||&lt;br /&gt;                   *p1 == *t.p2 &amp;amp;&amp;amp; *p2 == *t.p3 &amp;amp;&amp;amp; *p3 == *t.p1 ||&lt;br /&gt;                   *p1 == *t.p3 &amp;amp;&amp;amp; *p2 == *t.p1 &amp;amp;&amp;amp; *p3 == *t.p2 ||&lt;br /&gt;&lt;br /&gt;                   *p1 == *t.p3 &amp;amp;&amp;amp; *p2 == *t.p2 &amp;amp;&amp;amp; *p3 == *t.p1 ||&lt;br /&gt;                   *p1 == *t.p2 &amp;amp;&amp;amp; *p2 == *t.p1 &amp;amp;&amp;amp; *p3 == *t.p3 ||&lt;br /&gt;                   *p1 == *t.p1 &amp;amp;&amp;amp; *p2 == *t.p3 &amp;amp;&amp;amp; *p3 == *t.p2 );&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // ======================================  &lt;br /&gt;        // 他の三角形と共有点を持つか  &lt;br /&gt;        // ======================================   &lt;br /&gt;        bool hasCommonPoints(const Triangle&amp;amp; t) &lt;br /&gt;        {&lt;br /&gt;            return(*p1 == *t.p1 || *p1 == *t.p2 || *p1 == *t.p3 ||&lt;br /&gt;                   *p2 == *t.p1 || *p2 == *t.p2 || *p2 == *t.p3 ||&lt;br /&gt;                   *p3 == *t.p1 || *p3 == *t.p2 || *p3 == *t.p3 );&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    &lt;br /&gt;    class Delaunay2d &lt;br /&gt;    {&lt;br /&gt;    private:&lt;br /&gt;        static void manageDuplicativeTriangles(std::list&amp;lt;Triangle&amp;gt;* newTriangleList,&lt;br /&gt;                                        std::list&amp;lt;Triangle&amp;gt;* duplicativeTriangleList, &lt;br /&gt;                                        const Triangle&amp;amp; t) &lt;br /&gt;        {&lt;br /&gt;            typedef std::list&amp;lt;Triangle&amp;gt;::iterator triIter;&lt;br /&gt;&lt;br /&gt;            bool existsInNewTriangleList = false;&lt;br /&gt;            for(triIter iter = newTriangleList-&amp;gt;begin();&lt;br /&gt;                iter != newTriangleList-&amp;gt;end(); iter++) &lt;br /&gt;            {&lt;br /&gt;                &lt;br /&gt;                if(*iter == t) {&lt;br /&gt;                    existsInNewTriangleList = true;&lt;br /&gt;                    bool existsInDuplicativeTriangleList = false;&lt;br /&gt;&lt;br /&gt;                    for(triIter iter2 = duplicativeTriangleList-&amp;gt;begin();&lt;br /&gt;                        iter2 != duplicativeTriangleList-&amp;gt;end(); iter2++) &lt;br /&gt;                    {&lt;br /&gt;                        if(*iter2 == t) &lt;br /&gt;                        { &lt;br /&gt;                            existsInDuplicativeTriangleList = true;&lt;br /&gt;                            break;&lt;br /&gt;                        }&lt;br /&gt;&lt;br /&gt;                    }&lt;br /&gt;                    if(!existsInDuplicativeTriangleList) &lt;br /&gt;                    {&lt;br /&gt;                        duplicativeTriangleList-&amp;gt;push_back(t);&lt;br /&gt;                    }&lt;br /&gt;                    break;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            if(!existsInNewTriangleList) newTriangleList-&amp;gt;push_back(t);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    public:&lt;br /&gt;        static void getDelaunayTriangles(const std::list&amp;lt;Vector&amp;gt;&amp;amp; vertexList, &lt;br /&gt;            std::list&amp;lt;Triangle&amp;gt;* triangleList) &lt;br /&gt;        {&lt;br /&gt;            typedef std::list&amp;lt;Vector&amp;gt;::const_iterator cVtxIter;&lt;br /&gt;            typedef std::list&amp;lt;Triangle&amp;gt;::iterator     triIter;&lt;br /&gt;&lt;br /&gt;            Triangle hugeTriangle;&lt;br /&gt;            {&lt;br /&gt;                // ======================================  &lt;br /&gt;                // 外部三角形を作る  &lt;br /&gt;                // ======================================  &lt;br /&gt;                double maxX, maxY; maxX = maxY = DBL_MIN;&lt;br /&gt;                double minX, minY; minX = minY = DBL_MAX;&lt;br /&gt;                for(cVtxIter it = vertexList.begin(); it != vertexList.end(); ++it) &lt;br /&gt;                {&lt;br /&gt;                    double x = it-&amp;gt;x;&lt;br /&gt;                    double y = it-&amp;gt;y;&lt;br /&gt;                    if(maxX &amp;lt; x) maxX = x; if(minX &amp;gt; x) minX = x;&lt;br /&gt;                    if(maxY &amp;lt; y) maxY = y; if(minY &amp;gt; y) minY = y;&lt;br /&gt;                }&lt;br /&gt;                &lt;br /&gt;                // すべての点を包含する矩形の外接円&lt;br /&gt;                double centerX  = (maxX - minX) * 0.5;          // 中心x座標&lt;br /&gt;                double centerY  = (maxY - minY) * 0.5;          // 中心y座標&lt;br /&gt;&lt;br /&gt;                double dX     = maxX - centerX;&lt;br /&gt;                double dY     = maxY - centerY;&lt;br /&gt;                double radius = sqrt(dX * dX + dY * dY) + 1.0;  // 半径&lt;br /&gt;&lt;br /&gt;                Vector* p1 = new Vector;    // メモリ確保（266行目で解放）&lt;br /&gt;                p1-&amp;gt;x      = centerX - sqrt(3.0) * radius;&lt;br /&gt;                p1-&amp;gt;y      = centerY - radius;&lt;br /&gt;&lt;br /&gt;                Vector* p2 = new Vector;    // メモリ確保（267行目で解放）&lt;br /&gt;                p2-&amp;gt;x      = centerX + sqrt(3.0) * radius;&lt;br /&gt;                p2-&amp;gt;y      = centerY - radius;&lt;br /&gt;&lt;br /&gt;                Vector* p3 = new Vector;    // メモリ確保（268行目で解放）&lt;br /&gt;                p3-&amp;gt;x      = centerX;&lt;br /&gt;                p3-&amp;gt;y      = centerY + 2.0 * radius;&lt;br /&gt;&lt;br /&gt;                hugeTriangle.p1 = p1;&lt;br /&gt;                hugeTriangle.p2 = p2;&lt;br /&gt;                hugeTriangle.p3 = p3;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            triangleList-&amp;gt;push_back(hugeTriangle);&lt;br /&gt;&lt;br /&gt;            // --------------------------------------  &lt;br /&gt;            // 点を逐次添加し、反復的に三角分割を行う  &lt;br /&gt;            // --------------------------------------  &lt;br /&gt;            for(cVtxIter vIter = vertexList.begin(); vIter != vertexList.end(); ++vIter) &lt;br /&gt;            {&lt;br /&gt;                const Vector* p = &amp;amp;(*vIter);&lt;br /&gt;&lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                // 追加候補の三角形を保持する一時リスト  &lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                std::list&amp;lt;Triangle&amp;gt; newTriangleList;          // 新規分割された三角形                &lt;br /&gt;                std::list&amp;lt;Triangle&amp;gt; duplicativeTriangleList;  // 重複リスト&lt;br /&gt;                &lt;br /&gt;&lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                // 現在の三角形セットから要素を一つずつ取り出して、  &lt;br /&gt;                // 与えられた点が各々の三角形の外接円の中に含まれるかどうか判定  &lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                for(triIter tIter = triangleList-&amp;gt;begin(); tIter != triangleList-&amp;gt;end(); ) &lt;br /&gt;                {&lt;br /&gt;                    // 三角形セットから三角形を取りだして…&lt;br /&gt;                    Triangle t = *tIter;&lt;br /&gt;&lt;br /&gt;                    // その外接円を求める。  &lt;br /&gt;                    Circle   c;&lt;br /&gt;                    {&lt;br /&gt;                        // 三角形の各頂点座標を (x1, y1), (x2, y2), (x3, y3) とし、  &lt;br /&gt;                        // その外接円の中心座標を (x, y) とすると、  &lt;br /&gt;                        //     (x - x1) * (x - x1) + (y - y1) * (y - y1)  &lt;br /&gt;                        //   = (x - x2) * (x - x2) + (y - y2) * (y - y2)  &lt;br /&gt;                        //   = (x - x3) * (x - x3) + (y - y3) * (y - y3)  &lt;br /&gt;                        // より、以下の式が成り立つ  &lt;br /&gt;                        //  &lt;br /&gt;                        // x = { (y3 - y1) * (x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1)  &lt;br /&gt;                        //     + (y1 - y2) * (x3 * x3 - x1 * x1 + y3 * y3 - y1 * y1)} / c  &lt;br /&gt;                        //  &lt;br /&gt;                        // y = { (x1 - x3) * (x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1)  &lt;br /&gt;                        //     + (x2 - x1) * (x3 * x3 - x1 * x1 + y3 * y3 - y1 * y1)} / c  &lt;br /&gt;                        //  &lt;br /&gt;                        // ただし、  &lt;br /&gt;                        //   c = 2 * {(x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)} &lt;br /&gt;&lt;br /&gt;                        double x1 = t.p1-&amp;gt;x;    double y1 = t.p1-&amp;gt;y;&lt;br /&gt;                        double x2 = t.p2-&amp;gt;x;    double y2 = t.p2-&amp;gt;y;&lt;br /&gt;                        double x3 = t.p3-&amp;gt;x;    double y3 = t.p3-&amp;gt;y;&lt;br /&gt;&lt;br /&gt;                        double m = 2.0 * ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1));  &lt;br /&gt;                        double x = ((y3 - y1) * (x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1)  &lt;br /&gt;                                  + (y1 - y2) * (x3 * x3 - x1 * x1 + y3 * y3 - y1 * y1)) / m;  &lt;br /&gt;                        double y = ((x1 - x3) * (x2 * x2 - x1 * x1 + y2 * y2 - y1 * y1)  &lt;br /&gt;                                  + (x2 - x1) * (x3 * x3 - x1 * x1 + y3 * y3 - y1 * y1)) / m;&lt;br /&gt;&lt;br /&gt;                        c.center.x = x; &lt;br /&gt;                        c.center.y = y;&lt;br /&gt;&lt;br /&gt;                        // 外接円の半径 r は、半径から三角形の任意の頂点までの距離に等しい &lt;br /&gt;                        double dx     = t.p1-&amp;gt;x - x;&lt;br /&gt;                        double dy     = t.p1-&amp;gt;y - y;&lt;br /&gt;                        double radius = sqrt(dx * dx + dy * dy);&lt;br /&gt;&lt;br /&gt;                        c.radius = radius;&lt;br /&gt;                    }&lt;br /&gt;&lt;br /&gt;                    double dx   = c.center.x - p-&amp;gt;x;&lt;br /&gt;                    double dy   = c.center.y - p-&amp;gt;y;&lt;br /&gt;                    double dist = sqrt(dx * dx + dy * dy);&lt;br /&gt;&lt;br /&gt;                    // ======================================  &lt;br /&gt;                    // 一時リストを使って重複判定  &lt;br /&gt;                    // ======================================  &lt;br /&gt;                    if(dist &amp;lt; c.radius) &lt;br /&gt;                    {&lt;br /&gt;                        // 再分割&lt;br /&gt;&lt;br /&gt;                        Triangle t1;&lt;br /&gt;                        t1.p1 = p; t1.p2 = t.p1; t1.p3 = t.p2;&lt;br /&gt;                        manageDuplicativeTriangles(&amp;amp;newTriangleList, &amp;amp;duplicativeTriangleList, t1);&lt;br /&gt;&lt;br /&gt;                        Triangle t2;&lt;br /&gt;                        t2.p1 = p; t2.p2 = t.p2; t2.p3 = t.p3;&lt;br /&gt;                        manageDuplicativeTriangles(&amp;amp;newTriangleList, &amp;amp;duplicativeTriangleList, t2);&lt;br /&gt;&lt;br /&gt;                        Triangle t3;&lt;br /&gt;                        t3.p1 = p; t3.p2 = t.p3; t3.p3 = t.p1;&lt;br /&gt;                        manageDuplicativeTriangles(&amp;amp;newTriangleList, &amp;amp;duplicativeTriangleList, t3);&lt;br /&gt;&lt;br /&gt;                        tIter = triangleList-&amp;gt;erase(tIter);&lt;br /&gt;                    } &lt;br /&gt;                    else ++tIter;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                // 一時リストのうち、重複のないものを三角形リストに追加   &lt;br /&gt;                // --------------------------------------  &lt;br /&gt;                for(triIter iter = newTriangleList.begin();&lt;br /&gt;                    iter != newTriangleList.end(); ++iter) &lt;br /&gt;                {&lt;br /&gt;                    bool exists = false;&lt;br /&gt;                    for(triIter iter2 = duplicativeTriangleList.begin();&lt;br /&gt;                        iter2 != duplicativeTriangleList.end(); ++iter2)&lt;br /&gt;                    {&lt;br /&gt;                        if(*iter == *iter2) &lt;br /&gt;                        {&lt;br /&gt;                            exists = true;&lt;br /&gt;                            break;&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                    if(!exists) triangleList-&amp;gt;push_back(*iter);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            // 最後に、外部三角形の頂点を削除&lt;br /&gt;            for(triIter tIter = triangleList-&amp;gt;begin(); tIter != triangleList-&amp;gt;end(); ) &lt;br /&gt;            {&lt;br /&gt;                if(hugeTriangle.hasCommonPoints(*tIter)) tIter = triangleList-&amp;gt;erase(tIter);&lt;br /&gt;                else ++tIter;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            // 巨大三角形の頂点を解放&lt;br /&gt;            delete hugeTriangle.p1;&lt;br /&gt;            delete hugeTriangle.p2;&lt;br /&gt;            delete hugeTriangle.p3;&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【テスト用の GUI （&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt;）】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre class="c++:collapse" name="code"&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;//&lt;br /&gt;// 結果表示用のウィンドウ&lt;br /&gt;//&lt;br /&gt;// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-&lt;br /&gt;#include &amp;lt;windows.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#include "Delaunay2d.h"&lt;br /&gt;&lt;br /&gt;// アプリケーションとウィンドウの初期化に必要な定数&lt;br /&gt;// ----------------------------------------&lt;br /&gt;const LPCTSTR APP_NAME   = TEXT("APP_TERCEL_TECH");&lt;br /&gt;const LPCTSTR MUTEX_NAME = TEXT("MUTEX_TERCEL_TECH");&lt;br /&gt;&lt;br /&gt;// クライアント領域の幅と高さ&lt;br /&gt;// ----------------------------------------&lt;br /&gt;const UINT WIDTH  = 400;&lt;br /&gt;const UINT HEIGHT = 300;&lt;br /&gt;&lt;br /&gt;// ----------------------------------------&lt;br /&gt;// 頂点集合、およびそれらを基に&lt;br /&gt;// Delaunay 分割された三角形を格納しておくSTLコンテナ&lt;br /&gt;// ----------------------------------------&lt;br /&gt;std::list&amp;lt;Tercel::Vector&amp;gt;   vertices;&lt;br /&gt;std::list&amp;lt;Tercel::Triangle&amp;gt; triangles;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// コールバック関数（メッセージ処理）&lt;br /&gt;// ========================================&lt;br /&gt;LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg,&lt;br /&gt;                            WPARAM wParam, LPARAM lParam)&lt;br /&gt;{&lt;br /&gt;    switch(uMsg)&lt;br /&gt;    {&lt;br /&gt;    case WM_DESTROY:&lt;br /&gt;        PostQuitMessage(0);&lt;br /&gt;        return 0;&lt;br /&gt;&lt;br /&gt;    case WM_CREATE:&lt;br /&gt;        {&lt;br /&gt;            // ======================================  &lt;br /&gt;            // 頂点集合にランダムでデータをセット&lt;br /&gt;            // ======================================  &lt;br /&gt;            for(int i = 0; i &amp;lt; 200; ++i) {&lt;br /&gt;                Tercel::Vector v;  // ベクトルを宣言&lt;br /&gt;                v.x   = rand() % WIDTH;&lt;br /&gt;                v.y   = rand() % HEIGHT;&lt;br /&gt;                vertices.push_back(v);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            // ======================================  &lt;br /&gt;            // Delaunay分割を行う関数&lt;br /&gt;            // ======================================  &lt;br /&gt;            &lt;br /&gt;            // 結果は第2引数（std::list&amp;lt;Tercel::Triangle&amp;gt; 型）に格納される。&lt;br /&gt;            // &lt;br /&gt;            // Tercel::Triangle のメンバには、3つの頂点p1, p2, p3 があって、&lt;br /&gt;            // いずれも Tercel::Vertex オブジェクトへのポインタになっている&lt;br /&gt;            // これらのポインタは、vertices リストの要素を参照している。&lt;br /&gt;            Tercel::Delaunay2d::getDelaunayTriangles(vertices, &amp;amp;triangles);&lt;br /&gt;            //                                       ~~~~~~~~  ~~~~~~~~~~&lt;br /&gt;            //                                       頂点集合  三角形集合&lt;br /&gt;            //                                       （参照） （ポインタ）&lt;br /&gt;        }&lt;br /&gt;        return 0;&lt;br /&gt;&lt;br /&gt;    case WM_PAINT:&lt;br /&gt;        {&lt;br /&gt;            typedef std::list&amp;lt;Tercel::Triangle&amp;gt; triangleList;&lt;br /&gt;&lt;br /&gt;            PAINTSTRUCT ps;&lt;br /&gt;            HDC         hdc  = BeginPaint(hWnd, &amp;amp;ps);&lt;br /&gt;            HPEN        hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));&lt;br /&gt;            &lt;br /&gt;            SelectObject(hdc, hPen);&lt;br /&gt;&lt;br /&gt;            // ======================================  &lt;br /&gt;            // Delaunay分割された三角形の集合を一つ一つ描画&lt;br /&gt;            // ======================================  &lt;br /&gt;            for(triangleList::iterator it = triangles.begin();&lt;br /&gt;                it != triangles.end(); ++it) &lt;br /&gt;            {&lt;br /&gt;                Tercel::Triangle t = *it;  // 三角形取得&lt;br /&gt;                int x, y;&lt;br /&gt;&lt;br /&gt;                x = int(t.p1-&amp;gt;x + 0.5);&lt;br /&gt;                y = int(t.p1-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;                MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;                x = int(t.p2-&amp;gt;x + 0.5);&lt;br /&gt;                y = int(t.p2-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;                LineTo(hdc, x, y);&lt;br /&gt;                MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;                x = int(t.p3-&amp;gt;x + 0.5);&lt;br /&gt;                y = int(t.p3-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;                LineTo(hdc, x, y);&lt;br /&gt;                MoveToEx(hdc, x, y, NULL);&lt;br /&gt;&lt;br /&gt;                x = int(t.p1-&amp;gt;x + 0.5);&lt;br /&gt;                y = int(t.p1-&amp;gt;y + 0.5);&lt;br /&gt;&lt;br /&gt;                LineTo(hdc, x, y);&lt;br /&gt;            }&lt;br /&gt;            DeleteObject(hPen);&lt;br /&gt;            EndPaint(hWnd, &amp;amp;ps);            &lt;br /&gt;        }&lt;br /&gt;        return 0;&lt;br /&gt;    }&lt;br /&gt;    return DefWindowProc(hWnd, uMsg, wParam, lParam);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;// エントリポイント&lt;br /&gt;// ========================================&lt;br /&gt;int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,&lt;br /&gt;                   LPSTR lpCmdLine, int nCmdShow)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    // 多重起動防止&lt;br /&gt;    HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);&lt;br /&gt;    if(GetLastError() == ERROR_ALREADY_EXISTS)&lt;br /&gt;    {&lt;br /&gt;        // 多重起動を検知した際には、&lt;br /&gt;        // 既存のウィンドウを最前面に表示してプログラムを終了する&lt;br /&gt;        HWND existingWnd = FindWindow(APP_NAME, NULL);&lt;br /&gt;        if(existingWnd != NULL)&lt;br /&gt;        {&lt;br /&gt;            if(IsIconic(existingWnd))&lt;br /&gt;                ShowWindowAsync(existingWnd, SW_RESTORE);&lt;br /&gt;&lt;br /&gt;            SetForegroundWindow(existingWnd);&lt;br /&gt;        }&lt;br /&gt;        return -1;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ウィンドウクラス登録&lt;br /&gt;    WNDCLASSEX wc;&lt;br /&gt;    wc.cbSize        = sizeof(WNDCLASSEX);&lt;br /&gt;    wc.style         = CS_HREDRAW | CS_VREDRAW;&lt;br /&gt;    wc.lpfnWndProc   = WindowProc;&lt;br /&gt;    wc.cbClsExtra    = 0;&lt;br /&gt;    wc.cbWndExtra    = sizeof(LONG_PTR);&lt;br /&gt;    wc.hInstance     = hInstance;&lt;br /&gt;    wc.hIcon         = (HICON)LoadImage(NULL,&lt;br /&gt;                           MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;                           IMAGE_ICON,&lt;br /&gt;                           0,&lt;br /&gt;                           0,&lt;br /&gt;                           LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;    wc.hCursor       = (HICON)LoadImage(NULL,&lt;br /&gt;                           MAKEINTRESOURCE(IDC_ARROW),&lt;br /&gt;                           IMAGE_CURSOR,&lt;br /&gt;                           0,&lt;br /&gt;                           0,&lt;br /&gt;                           LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;    wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1;&lt;br /&gt;    wc.lpszMenuName  = NULL;&lt;br /&gt;    wc.lpszClassName = APP_NAME;&lt;br /&gt;    wc.hIconSm = (HICON)LoadImage(NULL,&lt;br /&gt;                           MAKEINTRESOURCE(IDI_APPLICATION),&lt;br /&gt;                           IMAGE_ICON,&lt;br /&gt;                           0,&lt;br /&gt;                           0,&lt;br /&gt;                           LR_DEFAULTSIZE | LR_SHARED);&lt;br /&gt;    if(!RegisterClassEx(&amp;amp;wc)) return -1;&lt;br /&gt;&lt;br /&gt;    // クライアント領域のサイズから、ウィンドウそのものの幅と高さを計算&lt;br /&gt;    UINT windowWidth  = WIDTH  + GetSystemMetrics(SM_CXFIXEDFRAME) * 2;&lt;br /&gt;    UINT windowHeight = HEIGHT + GetSystemMetrics(SM_CYFIXEDFRAME) * 2&lt;br /&gt;                               + GetSystemMetrics(SM_CYCAPTION);&lt;br /&gt;    // ウィンドウ生成&lt;br /&gt;    HWND hWnd = CreateWindow(APP_NAME,&lt;br /&gt;                    TEXT("はじめてのDelaunay分割"),&lt;br /&gt;                    WS_OVERLAPPEDWINDOW^WS_THICKFRAME^WS_MAXIMIZEBOX,&lt;br /&gt;                    CW_USEDEFAULT,&lt;br /&gt;                    CW_USEDEFAULT,&lt;br /&gt;                    windowWidth,&lt;br /&gt;                    windowHeight,&lt;br /&gt;                    NULL,&lt;br /&gt;                    NULL,&lt;br /&gt;                    hInstance,&lt;br /&gt;                    NULL);&lt;br /&gt;    if(!hWnd) return -1;&lt;br /&gt;&lt;br /&gt;    // ウィンドウ表示&lt;br /&gt;    ShowWindow(hWnd, nCmdShow);&lt;br /&gt;    UpdateWindow(hWnd);&lt;br /&gt;&lt;br /&gt;    // メッセージループ&lt;br /&gt;    MSG msg;&lt;br /&gt;    while(TRUE)&lt;br /&gt;    {&lt;br /&gt;        if(PeekMessage(&amp;amp;msg, NULL, 0U, 0U, PM_REMOVE))&lt;br /&gt;        {&lt;br /&gt;            if(msg.message == WM_QUIT) break;&lt;br /&gt;            TranslateMessage(&amp;amp;msg);&lt;br /&gt;            DispatchMessage(&amp;amp;msg);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    CloseHandle(hMutex);&lt;br /&gt;&lt;br /&gt;    return (int)msg.wParam;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;テスト用の GUI プログラムの方は、思いっきり Windows 依存です。&lt;br /&gt;&lt;br /&gt;ただ、Delaunay 分割のアルゴリズムは完全に分離してあるので、他の環境で使いたい場合は Delaunay2d.h だけを持っていって、表示部はがんばって自分で作れば OK です。&lt;br /&gt;&lt;br /&gt;肝心の使い方は以下の通りです（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt; のコールバック関数内に実際のサンプルがあります）。&lt;br /&gt;&lt;ol&gt;&lt;li&gt;頂点リスト（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::list&lt;tercel::vector&gt;&lt;/tercel::vector&gt;&lt;/span&gt; オブジェクト）を宣言します。&lt;/li&gt;&lt;li&gt;先ほどの頂点リストに、適当な頂点オブジェクトを格納します。&lt;/li&gt;&lt;li&gt;三角形リスト（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;std::list&lt;tercel::triangle&gt;&lt;/tercel::triangle&gt;&lt;/span&gt; オブジェクト）を宣言します。&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Delaunay2d::getDelaunayTriangles()&lt;/span&gt; 関数を呼びます。&lt;/li&gt;&lt;/ol&gt;このとき、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;getDelaunayTriangles()&lt;/span&gt; の第一引数に頂点リストの『&lt;b&gt;参照&lt;/b&gt;』を、第2引数には三角形リストの『&lt;b&gt;ポインタ&lt;/b&gt;』を渡します。&lt;br /&gt;&lt;br /&gt;一見すると奇妙な仕様ですが、関数内で中身が変更されるオブジェクトに関してはポインタ渡し、そうでないものは参照渡しで構文を使い分けているためです。&lt;br /&gt;&lt;br /&gt;特に関数を呼び出す側（ここでは &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Main.cpp&lt;/span&gt; ）から見ると、参照渡しは値渡しのように見えるため、関数の中でオブジェクトが変更されないような印象になります。&lt;br /&gt;&lt;br /&gt;Delaunay 分割された結果は三角形リストに格納されます。三角形（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Triangle&lt;/span&gt; 構造体）は、メンバに頂点（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Tercel::Vertex&lt;/span&gt; オブジェクト）のポインタを保持します。ここでは、最初に作った頂点リストの要素を参照する形になります。&lt;br /&gt;&lt;br /&gt;ですので、頂点リストの要素を削除した場合、三角形の頂点は不正なポインタになってしまう事に注意する必要があります。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-3073540410988707728?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/3073540410988707728/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay-2.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/3073540410988707728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/3073540410988707728'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/c-delaunay-2.html' title='C++ で Delaunay 分割（ただし2次元）'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-8vUnAHigPMs/TsdwTwUemxI/AAAAAAAAAaw/FuMU2aZ0zG8/s72-c/DelaunayCpp.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-7331653422570238751</id><published>2011-11-05T17:55:00.005+09:00</published><updated>2011-11-05T19:01:37.678+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Processing'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Processing : P3D モードのヘンなクセ</title><content type='html'>現行バージョンの Processing には、ソフトウェアレンダリングで 3D を描画できる『P3D モード』が備わっています。&lt;br /&gt;&lt;br /&gt;これは、3次元的な情報を視覚化する際にたいへん便利なのですが、実はちょっとした癖もあるため、うまく使うにはそれなりのコツが必要となってきます。&lt;br /&gt;&lt;br /&gt;というわけで今日は、P3D モードで遭遇しがちな不具合と、その回避策を紹介したいと思います。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【地面のグリッド表示がおかしくなる現象】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;以下のように、地面をグリッドで描画したい場合があったとしましょう。 &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-94ZzQwDdF9A/TrTOfzY-SAI/AAAAAAAAAZw/X2WALiXE4zw/s1600/20111104001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="315" src="http://1.bp.blogspot.com/-94ZzQwDdF9A/TrTOfzY-SAI/AAAAAAAAAZw/X2WALiXE4zw/s400/20111104001.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;素直にコードを書くとこんな感じになります（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;void setup() {&lt;br /&gt;  size(800, 600, P3D);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  // カメラを適当に設定&lt;br /&gt;  camera(0, -100, 500, 0, 0, 0, 0, 1, 0);&lt;br /&gt;  &lt;br /&gt;  background(255);&lt;br /&gt;&lt;br /&gt;  /* ====================== */&lt;br /&gt;  /* 地面の描画（てきとう） */&lt;br /&gt;  /* ====================== */&lt;br /&gt;  stroke(100);&lt;br /&gt;  noFill();  // ←塗りつぶさない&lt;br /&gt;  &lt;br /&gt;  final int step = 20;  &lt;br /&gt;  for(int i = -width; i &amp;lt; width; i += step) {&lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    for(int j = -width; j &amp;lt;= width; j += step) {&lt;br /&gt;      vertex(i, 0, j);&lt;br /&gt;      vertex(i + step, 0, j);&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上記のプログラムは、いっけん正しく動きそうですが、いざ実行してみるとこんなふうに&lt;b&gt;ぶっ飛んだ結果&lt;/b&gt;になってしまいます。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-kqvrlf4CTBQ/TrTQAYT2VWI/AAAAAAAAAZ4/LWvLOFc5qtk/s1600/20111104002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="315" src="http://4.bp.blogspot.com/-kqvrlf4CTBQ/TrTQAYT2VWI/AAAAAAAAAZ4/LWvLOFc5qtk/s400/20111104002.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;なぜこんな事になったのでしょうか。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;この現象は、描画すべき線分の端点がカメラ視点の裏側に回り込んでしまった場合に発生します。原因は、Processing 側が 3 次元座標を射影変換する際に、いくつかの例外処理を省略してしまっているためと考えられます。&lt;br /&gt;&lt;br /&gt;これを回避するための比較的簡単な方法は、&lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;noFill()&lt;/span&gt; の代わりに &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;fill(0x00FFFFFF)&lt;/span&gt; を使う&lt;/b&gt;事です。下記のコードならば、意図した結果が得られるはずです。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;void setup() {&lt;br /&gt;  size(800, 600, P3D);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  // カメラを適当に設定&lt;br /&gt;  camera(0, -100, 500, 0, 0, 0, 0, 1, 0);&lt;br /&gt;  &lt;br /&gt;  background(255);&lt;br /&gt;&lt;br /&gt;  /* ====================== */&lt;br /&gt;  /* 地面の描画（てきとう） */&lt;br /&gt;  /* ====================== */&lt;br /&gt;  stroke(100);&lt;br /&gt;  fill(0x00FFFFFF);  // ←変更&lt;br /&gt;  &lt;br /&gt;  final int step = 20;  &lt;br /&gt;  for(int i = -width; i &amp;lt; width; i += step) {&lt;br /&gt;    beginShape(QUAD_STRIP);&lt;br /&gt;    for(int j = -width; j &amp;lt;= width; j += step) {&lt;br /&gt;      vertex(i, 0, j);&lt;br /&gt;      vertex(i + step, 0, j);&lt;br /&gt;    }&lt;br /&gt;    endShape();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;基本的に、『塗りつぶし無しの 3D 図形』と『塗りつぶしつきの 3D 図形』では異なるレンダリングパイプラインを通ります。ちなみに、陰影付けやオクルージョンの判定などを必要とする事から、一般的には後者の方が高度な処理となります。&lt;br /&gt;&lt;br /&gt;上記のプログラムでは、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;fill(0x00FFFFFF)&lt;/span&gt; によって塗りつぶし色を「完全に透明な白」に指定する事によって、塗りつぶし無しと等価の見た目で、なおかつ問題となっていた端点の座標飛びに対処しています。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【半透明テクスチャのレンダリング順序】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Processing では、透過 png 画像をテクスチャとして使用する事ができます。&lt;br /&gt;&lt;br /&gt;これを使えば、以下のように半透明の物体を表現する事ができそうですね。&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-UwfFm3pbFfg/TrTsucM0dZI/AAAAAAAAAaA/VQ9v92InFKY/s1600/20111104003.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="315" src="http://4.bp.blogspot.com/-UwfFm3pbFfg/TrTsucM0dZI/AAAAAAAAAaA/VQ9v92InFKY/s400/20111104003.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;半透明のテクスチャを貼った四角形を並べるコードは以下のように書けますが、これを実行すると何かがおかしくなります。 &lt;br /&gt;&lt;br /&gt;&lt;pre class="java:collapse" name="code"&gt;void setup() {&lt;br /&gt;  size(800, 600, P3D);&lt;br /&gt;  noStroke();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  camera(500, -500, 700, 0, 0, 0, 0, 1, 0);&lt;br /&gt;  &lt;br /&gt;  final int size  = 200;   // 板のサイズ&lt;br /&gt;&lt;br /&gt;  final int depth = 1000;  // 板を並べる際の奥行き量&lt;br /&gt;  final int step  =  100;  // 板と板との間隔&lt;br /&gt;  &lt;br /&gt;  colorMode(HSB, depth / step, 100, 100);&lt;br /&gt;  for(int z = depth / 2; z &amp;gt;= -depth / 2; z -= step) {&lt;br /&gt;    &lt;br /&gt;    int colour = color((z + depth/2) / step, 100, 100);  // 色を計算&lt;br /&gt;    PImage tex = createTranslucentTex(colour);           // 指定した色でテクスチャ作成&lt;br /&gt;&lt;br /&gt;    // 板の描画&lt;br /&gt;    beginShape(QUADS);&lt;br /&gt;    texture(tex);&lt;br /&gt;    vertex(-size, -size, z, 0,         0);&lt;br /&gt;    vertex(-size,  size, z, 0,         tex.height);&lt;br /&gt;    vertex( size,  size, z, tex.width, tex.height);&lt;br /&gt;    vertex( size, -size, z, tex.width, 0);&lt;br /&gt;    endShape(QUADS);&lt;br /&gt;  }&lt;br /&gt;  colorMode(RGB, 0xFF);&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// 指定した色で半透明のテクスチャを作成する&lt;br /&gt;// 引数： 色&lt;br /&gt;PImage createTranslucentTex(int colour) {&lt;br /&gt;  PImage tex = createImage(128, 128, ARGB);&lt;br /&gt;  &lt;br /&gt;  for(int y = 0; y &amp;lt; tex.height; y++) {&lt;br /&gt;    int alphaValue = 0xFF * y / tex.height;  // アルファ値&lt;br /&gt;    for(int x = 0; x &amp;lt; tex.width; x++) {&lt;br /&gt;      int index = y * tex.width + x;&lt;br /&gt;      tex.pixels[index] = alphaValue &amp;lt;&amp;lt; 24 | 0x00FFFFFF &amp;amp; colour;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return tex;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-i8Gry1sDN5w/TrTtj5kcRQI/AAAAAAAAAaI/L0mwTQg9oTA/s1600/20111104004.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="315" src="http://4.bp.blogspot.com/-i8Gry1sDN5w/TrTtj5kcRQI/AAAAAAAAAaI/L0mwTQg9oTA/s400/20111104004.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;何がおかしいか分かりますでしょうか。&lt;br /&gt;&lt;br /&gt;奥の方の板が、なぜか手前の板を覆い隠しているように見えます（ちなみに、テクスチャが不透明であれば、このような問題は発生しません）。&lt;br /&gt;&lt;br /&gt;Processing では、通常『&lt;a href="http://ja.wikipedia.org/wiki/Z%E3%83%90%E3%83%83%E3%83%95%E3%82%A1"&gt;Z バッファリング&lt;/a&gt;』と呼ばれる方式を用いてレンダリング処理を省力化しています。それはよいのですが、この方式には、『半透明の物体が必ずしも正しく描画されない』という大きな問題点があります。&lt;br /&gt;&lt;br /&gt;これを解決するための手段として、『&lt;a href="http://ja.wikipedia.org/wiki/%E7%94%BB%E5%AE%B6%E3%81%AE%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0"&gt;画家のアルゴリズム&lt;/a&gt;（Z ソート法）』というものがあります。アルゴリズムとはいえさして大仰なものではなく、カメラ奥の物体から手前の物体にかけて重ね塗りをするように描画していくという非常に直感的な仕組みです。&lt;br /&gt;&lt;br /&gt;というわけで、描画方式をデフォルトのZ バッファリングから画家のアルゴリズムに変更するには、&lt;a href="http://processing.org/reference/hint_.html"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;hint()&lt;/span&gt;&lt;/a&gt; メソッドに &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ENABLE_DEPTH_SORT&lt;/span&gt; を与えればよいそうです。&lt;br /&gt;&lt;br /&gt;画家のアルゴリズムを有効にしたコードを以下の通りです。&lt;br /&gt;&lt;br /&gt;&lt;pre class="java" name="code"&gt;void setup() {&lt;br /&gt;  size(800, 600, P3D);&lt;br /&gt;  noStroke();&lt;br /&gt;  hint(ENABLE_DEPTH_SORT); // ←追加&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void draw() {&lt;br /&gt;  background(0);&lt;br /&gt;  camera(500, -500, 700, 0, 0, 0, 0, 1, 0);&lt;br /&gt;  lights();&lt;br /&gt;  final int size  = 200;   // 板のサイズ&lt;br /&gt;&lt;br /&gt;  final int depth = 1000;  // 板を並べる際の奥行き量&lt;br /&gt;  final int step  =  100;  // 板と板との間隔&lt;br /&gt;&lt;br /&gt;  colorMode(HSB, depth / step, 100, 100);&lt;br /&gt;  for(int z = depth / 2; z &amp;gt;= - depth / 2; z -= step) {&lt;br /&gt;    &lt;br /&gt;    int colour = color((z + depth/2) / step, 100, 100);  // 色を計算&lt;br /&gt;    PImage tex = createTranslucentTex(colour);           // 指定した色でテクスチャ作成&lt;br /&gt;&lt;br /&gt;    // 板の描画&lt;br /&gt;    beginShape(QUADS);&lt;br /&gt;    texture(tex);&lt;br /&gt;    vertex(-size, -size, z, 0,         0);&lt;br /&gt;    vertex(-size,  size, z, 0,         tex.height);&lt;br /&gt;    vertex( size,  size, z, tex.width, tex.height);&lt;br /&gt;    vertex( size, -size, z, tex.width, 0);&lt;br /&gt;    endShape(QUADS);&lt;br /&gt;  }&lt;br /&gt;  colorMode(RGB, 0xFF);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// 指定した色で半透明のテクスチャを作成する&lt;br /&gt;// 引数： 色&lt;br /&gt;PImage createTranslucentTex(int colour) {&lt;br /&gt;  PImage tex = createImage(128, 128, ARGB);&lt;br /&gt;  &lt;br /&gt;  for(int y = 0; y &amp;lt; tex.height; y++) {&lt;br /&gt;    int alphaValue = 0xFF * y / tex.height;  // アルファ値&lt;br /&gt;    for(int x = 0; x &amp;lt; tex.width; x++) {&lt;br /&gt;      int index = y * tex.width + x;&lt;br /&gt;      tex.pixels[index] = alphaValue &amp;lt;&amp;lt; 24 | 0x00FFFFFF &amp;amp; colour;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  return tex;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;これで、恐らく期待した結果が得られるようになります（『恐らく』と書いたのは、リファレンスに “&lt;b&gt;the algorithm is not yet perfect.&lt;/b&gt;” とあるためです）。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【あとがき・おまけ】&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;&lt;br /&gt;実は、&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/08/processing_29.html"&gt;オーロラ&lt;/a&gt;にも半透明のポリゴンが大量に用いられています。ですが、こちらは画家のアルゴリズムを用いていないため、カメラ視点の位置によってはおかしな事になります。&lt;br /&gt;&lt;br /&gt;『画家のアルゴリズム』の存在自体は知っていましたが、&lt;b&gt;それを有効にするための方法が分からなかった&lt;/b&gt;ので、なるべく違和感を払拭するため、ポリゴンの描画順を少しいじったり、視点を移動できる範囲を強く制限したりと涙ぐましい工夫が凝らされています。&lt;br /&gt;&lt;br /&gt;というわけで、あまり大した事ではありませんが、知っていると少し幸せになれるかも知れない Tips でした。めでたし、めでたし。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4304703524813488565-7331653422570238751?l=tercel-sakuragaoka.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://tercel-sakuragaoka.blogspot.com/feeds/7331653422570238751/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/p3d.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7331653422570238751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4304703524813488565/posts/default/7331653422570238751'/><link rel='alternate' type='text/html' href='http://tercel-sakuragaoka.blogspot.com/2011/11/p3d.html' title='Processing : P3D モードのヘンなクセ'/><author><name>たーせる</name><uri>http://www.blogger.com/profile/10691620061457733907</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_etDXVQu4ywU/TUoIXlGe_-I/AAAAAAAAABU/DtZ_BdtRILU/s220/bloggersumb.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-94ZzQwDdF9A/TrTOfzY-SAI/AAAAAAAAAZw/X2WALiXE4zw/s72-c/20111104001.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4304703524813488565.post-1632580516850461928</id><published>2011-10-24T20:54:00.003+09:00</published><updated>2011-10-24T20:59:02.250+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='プログラミング'/><category scheme='http://www.blogger.com/atom/ns#' term='Visual Basic'/><title type='text'>いまさらVisual Basic（オブジェクト指向篇）</title><content type='html'>本日は Visual Basic のオブジェクト指向篇です。&lt;br /&gt;&lt;br /&gt;とはいえ、今回のお話はどちらかというと Java や C++ の学習による&lt;b&gt;シナジー効果&lt;/b&gt;で身に付けた知識だったりします。『VB しか必要ないから VB だけがんばる』という硬直した方針では、なかなか前に進めないものだなぁと思ったり思わなかったり。&lt;br /&gt;&lt;br /&gt;そんなわけで本日は、GoF のデザインパターンの中から個人的に気に入っているものを&lt;b&gt;独断と偏見で&lt;/b&gt;チョイスして、実際のソースコードとともにご紹介したいと思います。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【Singleton パターン】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;まずは、理解しやすく実装も容易な Singleton パターンからご紹介しましょう。&lt;br /&gt;&lt;br /&gt;これは、&lt;b&gt;プログラム内に存在するオブジェクトが 1 つだけである事を保証する&lt;/b&gt;ための仕組みです。一体それが何の役に立つのでしょうか。その答えの一つが『ゲームプログラマになる前に覚えておきたい技術』の p.183 で述べられていますので、当該箇所を引用します。&lt;br /&gt;&lt;blockquote style="background-color: #666666;"&gt;&lt;i&gt;シングルトンクラスの目的は以下のようにまとめられる。&lt;/i&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;1. グローバル変数の危険さを軽減する &lt;/i&gt;&lt;/li&gt;&lt;li&gt;&lt;i&gt;2. グローバル変数と同じように使えるようにする &lt;/i&gt;&lt;/li&gt;&lt;/ul&gt;&lt;i&gt;要するに、安全性を高めたグローバル変数のことである。&lt;/i&gt;&lt;/blockquote&gt;……だそうです。&lt;br /&gt;&lt;br /&gt;実際には、オブジェクトを生成する際に &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;New&lt;/span&gt; が実行されるたび、（新たなオブジェクトを格納するための）メモリ領域が逐一確保されます。そのため、このままでは『オブジェクトが 1 つである事』を保証できません。&lt;br /&gt;&lt;br /&gt;Singleton クラスでは、コンストラクタを &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Private&lt;/span&gt; にする事によって、外側から &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;New&lt;/span&gt; の実行を禁止します。Singleton クラスは、内部的に静的な唯一の自己参照 &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;_instance&lt;/span&gt; を持ち、外部から Singleton オブジェクトを要求された場合は、その &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;_instance&lt;/span&gt; を返却します。&lt;br /&gt;&lt;br /&gt;以下に実装例を示します（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:collapse" name="code"&gt;'  ========================================&lt;br /&gt;'                 Singleton&lt;br /&gt;'  ========================================&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' Singletonクラス&lt;br /&gt;Public Class Singleton&lt;br /&gt;&lt;br /&gt;    Private Shared _instance As Singleton = New Singleton()&lt;br /&gt;&lt;br /&gt;    ' インスタンス取得&lt;br /&gt;    Public Shared ReadOnly Property Instance() As Singleton&lt;br /&gt;        Get&lt;br /&gt;            Return _instance&lt;br /&gt;        End Get&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Private Sub New()&lt;br /&gt;        Console.WriteLine("オブジェクトを生成しました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' テスト用モジュール&lt;br /&gt;Module Module1&lt;br /&gt;    Sub main()&lt;br /&gt;        ' Singleton であるオブジェクトの取得&lt;br /&gt;        Dim singleton1 = Singleton.Instance&lt;br /&gt;        Dim singleton2 = Singleton.Instance&lt;br /&gt;&lt;br /&gt;        If singleton1 Is singleton2 Then&lt;br /&gt;            Console.WriteLine("同じオブジェクトです")&lt;br /&gt;        Else&lt;br /&gt;            Console.WriteLine("異なるオブジェクトです")&lt;br /&gt;        End If&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;実行すると、コンストラクタが 1 回しか実行されていない事、および &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;main&lt;/span&gt; 内の singleton1 と singleton2 が同じオブジェクトを共有している事が分かります。&lt;br /&gt;&lt;br /&gt;Singleton クラスがデータを適切にカプセル化していれば、前述の通り安全なグローバル変数として使用する事ができますし、また、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;New&lt;/span&gt; が重たいオブジェクトを使い回す際にも効果的でしょう。&lt;br /&gt;&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;hr /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;【Iterator パターン】&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;多くのオブジェクトを一元的に集約したオブジェクトを作ったとしましょう。その際、その&lt;b&gt;集約オブジェクトが持つ要素すべてにアクセスする統一的な手段を用意するのが Iterator パターン&lt;/b&gt;です。&lt;br /&gt;&lt;br /&gt;『統一的な手段』とは、「集約オブジェクト内部の実装がどのように変更されようとも、要素のアクセス方法は変える必要がない」というほどの意味です。&lt;br /&gt;&lt;br /&gt;以下に、Iterator パターンの VB サンプルコードを掲載します（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;これは、複数の名前を集約管理する名簿（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ListOfNames&lt;/span&gt;）クラスを作り、それに対してイテレータを追加したものです。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:collapse" name="code"&gt;'  ========================================&lt;br /&gt;'                  Iterator&lt;br /&gt;'  ========================================&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' オブジェクトの集合体&lt;br /&gt;Public Interface MyAggregate&lt;br /&gt;    Function iterator() As MyIterator&lt;br /&gt;End Interface&lt;br /&gt;&lt;br /&gt;' イテレータ&lt;br /&gt;Public Interface MyIterator&lt;br /&gt;    Function hasNext() As Boolean&lt;br /&gt;    Function getNext() As Object&lt;br /&gt;End Interface&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 名簿クラス&lt;br /&gt;Public Class ListOfNames&lt;br /&gt;    Implements MyAggregate&lt;br /&gt;&lt;br /&gt;    Private names() As String&lt;br /&gt;    Private last As Integer&lt;br /&gt;&lt;br /&gt;    'コンストラクタ&lt;br /&gt;    Public Sub New(ByVal maxSize As Integer)&lt;br /&gt;        ReDim names(maxSize)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Function getNameAt(ByVal index As Integer)&lt;br /&gt;        Return names(index)&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Public Sub appendName(ByRef name As String)&lt;br /&gt;        names(last) = name&lt;br /&gt;        last += 1&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Function getLength() As Integer&lt;br /&gt;        Return last&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Public Function iterator() As MyIterator Implements MyAggregate.iterator&lt;br /&gt;        Return New ListOfNamesIterator(Me)&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' 名簿イテレータクラス&lt;br /&gt;Public Class ListOfNamesIterator&lt;br /&gt;    Implements MyIterator&lt;br /&gt;&lt;br /&gt;    Private listOfNames As ListOfNames&lt;br /&gt;    Private index As Integer&lt;br /&gt;&lt;br /&gt;    Public Sub New(ByRef arg As ListOfNames)&lt;br /&gt;        listOfNames = arg&lt;br /&gt;        index = 0&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Function getNext() As Object Implements MyIterator.getNext&lt;br /&gt;        Dim name As String = listOfNames.getNameAt(index)&lt;br /&gt;        index += 1&lt;br /&gt;        Return name&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Public Function hasNext() As Boolean Implements MyIterator.hasNext&lt;br /&gt;        If index &amp;lt; listOfNames.getLength Then&lt;br /&gt;            Return True&lt;br /&gt;        Else&lt;br /&gt;            Return False&lt;br /&gt;        End If&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' テスト用モジュール&lt;br /&gt;Module Module1&lt;br /&gt;    Sub main()&lt;br /&gt;        Dim listOfNames As New ListOfNames(10)&lt;br /&gt;&lt;br /&gt;        listOfNames.appendName("Tercel")&lt;br /&gt;        listOfNames.appendName("Syusyu_Syarin")&lt;br /&gt;        listOfNames.appendName("pi_cro_s")&lt;br /&gt;        listOfNames.appendName("tsumach")&lt;br /&gt;&lt;br /&gt;        Dim it As MyIterator = listOfNames.iterator()&lt;br /&gt;&lt;br /&gt;        While it.hasNext&lt;br /&gt;            Dim name As String = CStr(it.getNext())&lt;br /&gt;            Console.WriteLine(name)&lt;br /&gt;        End While&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;このプログラムは単独で動作しますので、理解したい方はぜひコピペして動かしてみて下さい。&lt;br /&gt;&lt;br /&gt;Main の中の While ループで、実際にイテレータを使用して名簿オブジェクトの要素にアクセスしています。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:nogutter" name="code"&gt;While it.hasNext&lt;br /&gt;    Dim name As String = CStr(it.getNext())&lt;br /&gt;    Console.WriteLine(name)&lt;br /&gt;End While&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;よく見ると、呼び出されているのは &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;MyIterator&lt;/span&gt; のメソッドだけ ― つまり、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ListOfNames&lt;/span&gt; の実装が、要素へのアクセス方法に影響を及ぼしていない事が判ります。&lt;br /&gt;&lt;br /&gt;Iterator パターンを適用しないと、データ構造が変わるたびに要素へのアクセス方法を切り替える必要があります。&lt;br /&gt;&lt;br /&gt;集約オブジェクトの正体がただの配列であれば、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;For&lt;/span&gt; ループで添字アクセスが可能ですが、もし木構造に格納されている場合は、より複雑なトラバースアルゴリズムをコーディングしなければなりません。&lt;br /&gt;&lt;br /&gt;しかし、Iterator パターンを用いると、外部に対してデータ構造を隠蔽する事ができます。しつこいようですが、内部の実装がどうなっていようとも、同じ方法で要素を走査する事が可能になるという事が、Iterator パターンの大きな利点です。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【State パターン】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;State パターンは、&lt;b&gt;状態に応じて振る舞いを切り替えたいときに真価を発揮する&lt;/b&gt;パターンです。&lt;br /&gt;&lt;br /&gt;『状態に応じた切り替え』と聞くと、わざわざオブジェクト指向などを使わずとも &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;If&lt;/span&gt; 文で単純に処理を分岐させればよいように思えますし、実際それは可能です。&lt;br /&gt;&lt;br /&gt;ですが、状態数が増加すると、制御構造が極めて複雑化してしまいます（&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/03/objective-c_27.html"&gt;3月27日の日記&lt;/a&gt;あたりを適当に参照）。余談ですが、『ゲームプログラマになる前に覚えておきたい技術』でもこの問題点の解決のために 5 章を丸ごと割いています。&lt;br /&gt;&lt;br /&gt;今回は、&lt;a href="http://tercel-sakuragaoka.blogspot.com/2011/03/objective-c_27.html"&gt;3月27日の日記&lt;/a&gt;をより簡単にした状態遷移プログラムを紹介します（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:collapse" name="code"&gt;'  ========================================&lt;br /&gt;'                   State&lt;br /&gt;'  ========================================&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 状態インタフェース&lt;br /&gt;Public Interface State&lt;br /&gt;    Function update() As State&lt;br /&gt;End Interface&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 状態1&lt;br /&gt;Public Class State1&lt;br /&gt;    Implements State&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        Console.WriteLine("状態1に移りました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ' 状態の更新&lt;br /&gt;    ' 戻り値は次状態を返すupdateメソッド&lt;br /&gt;    Public Function update() As State Implements State.update&lt;br /&gt;        Console.WriteLine("※ ここは、状態1です")&lt;br /&gt;        Return New State2() ' 次状態は State2&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 状態2&lt;br /&gt;Public Class State2&lt;br /&gt;    Implements State&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        Console.WriteLine("状態2に移りました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ' 状態の更新&lt;br /&gt;    Public Function update() As State Implements State.update&lt;br /&gt;        Console.WriteLine("※ ここは、状態2です")&lt;br /&gt;        Return New State3() ' 次状態は State3&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 状態3&lt;br /&gt;Public Class State3&lt;br /&gt;    Implements State&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        Console.WriteLine("状態3に移りました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ' 状態の更新&lt;br /&gt;    Public Function update() As State Implements State.update&lt;br /&gt;        Console.WriteLine("※ ここは、状態3です")&lt;br /&gt;        Return New State4 ' 次状態は State4&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 最終状態（番兵）&lt;br /&gt;Public Class State4&lt;br /&gt;    Implements State&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        Console.WriteLine("最終状態に移りました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Function update() As State Implements State.update&lt;br /&gt;        Return Me&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' テスト用モジュール&lt;br /&gt;Module Module1&lt;br /&gt;    Sub main()&lt;br /&gt;        Dim state As State      ' State型の宣言&lt;br /&gt;&lt;br /&gt;        state = New State1()    ' 初期状態の代入&lt;br /&gt;&lt;br /&gt;        Do Until (TypeOf state Is State4)&lt;br /&gt;            state = state.update()&lt;br /&gt;        Loop&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;これは、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;State&lt;/span&gt; インタフェースを実装した各状態クラス（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;State1&lt;/span&gt; ～ &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;State4&lt;/span&gt;）の間を遷移するプログラムです。&lt;br /&gt;&lt;br /&gt;これによって、『状態の切り替え』と、『状態に応じた振る舞い』を巧妙に実装する事ができます。&lt;br /&gt;&lt;br /&gt;状態クラス &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;State1&lt;/span&gt; を見てみましょう。ここには、『その状態における振る舞い』と、『次状態の情報』のみが含まれています。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:nogutter" name="code"&gt;Public Class State1&lt;br /&gt;    Implements State&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        Console.WriteLine("状態1に移りました")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ' 状態の更新&lt;br /&gt;    ' 戻り値は次状態を返すupdateメソッド&lt;br /&gt;    Public Function update() As State Implements State.update&lt;br /&gt;        Console.WriteLine("※ ここは、状態1です")&lt;br /&gt;        Return New State2() ' 次状態は State2&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;次に、状態遷移を実際に行う &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;main&lt;/span&gt; の中身を見てみましょう。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:nogutter" name="code"&gt;Dim state As State      ' State型の宣言&lt;br /&gt;&lt;br /&gt;state = New State1()    ' 初期状態の代入&lt;br /&gt;&lt;br /&gt;Do Until (TypeOf state Is State4)&lt;br /&gt;    state = state.update()&lt;br /&gt;Loop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;条件分岐のための制御構造がソースコードから排除されている事が判ります。つまり、オブジェクトがどの状態にあるのかを意識する必要がなくなるという事です。&lt;br /&gt;&lt;br /&gt;また、遷移先を変更したい場合も、修正箇所は当該状態クラスに限定されるため、ソースコードの更新が容易になります。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【Template Method パターン】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Template Method は、&lt;b&gt;複数の処理を決まった順序で実行させたいときに、その『順序』を前もって定型化しておく&lt;/b&gt;パターンです。&lt;br /&gt;&lt;br /&gt;あくまで処理順序の『雛型』を作り、個々の具体的な処理は切り離して考える事がポイントです。これによって、柔軟で取り回しの利きやすいプログラムになります。&lt;br /&gt;&lt;br /&gt;以下に、Template Method パターンのサンプルを示します（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;これは、プログラムのコメントを自動生成するというものです。コメント生成機能は、VB 用と C 言語用を用意しました。どちらも同じテンプレートを継承している事にご注目ください。&lt;br /&gt;&lt;pre class="vb:collapse" name="code"&gt;'  ========================================&lt;br /&gt;'              Template Method&lt;br /&gt;'  ========================================&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' テンプレート（抽象クラス）&lt;br /&gt;Public MustInherit Class AbstractTemplate&lt;br /&gt;    Public MustOverride Sub printHeader()&lt;br /&gt;    Public MustOverride Sub printMain()&lt;br /&gt;    Public MustOverride Sub printFooter()&lt;br /&gt;    Public Sub print()&lt;br /&gt;        ' 決まった順序で処理を行う&lt;br /&gt;        printHeader()&lt;br /&gt;        printMain()&lt;br /&gt;        printFooter()&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' VBコメント用テンプレート&lt;br /&gt;Public Class VBCommentPrinter&lt;br /&gt;    Inherits AbstractTemplate&lt;br /&gt;&lt;br /&gt;    Private comment As String&lt;br /&gt;    Private width As Integer&lt;br /&gt;&lt;br /&gt;    Public Sub New(ByRef comment As String)&lt;br /&gt;        Me.comment = comment&lt;br /&gt;        Me.width = System.Text.Encoding.Default.GetByteCount(comment)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printHeader()&lt;br /&gt;        printLine("=")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printMain()&lt;br /&gt;        Console.WriteLine("' " &amp;amp; comment)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printFooter()&lt;br /&gt;        printLine("-")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Sub printLine(ByVal c As Char)&lt;br /&gt;        Console.Write("' ")&lt;br /&gt;        For i As Integer = 1 To width&lt;br /&gt;            Console.Write(c)&lt;br /&gt;        Next&lt;br /&gt;        Console.WriteLine()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' C言語コメント用テンプレート&lt;br /&gt;Public Class CCommentPrinter&lt;br /&gt;    Inherits AbstractTemplate&lt;br /&gt;&lt;br /&gt;    Private comment As String&lt;br /&gt;    Private width As Integer&lt;br /&gt;&lt;br /&gt;    Public Sub New(ByRef comment As String)&lt;br /&gt;        Me.comment = comment&lt;br /&gt;        Me.width = System.Text.Encoding.Default.GetByteCount(comment)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printHeader()&lt;br /&gt;        printLine("+")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printMain()&lt;br /&gt;        Console.WriteLine("/* " &amp;amp; comment &amp;amp; " */")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Overrides Sub printFooter()&lt;br /&gt;        printLine("+")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Sub printLine(ByVal c As Char)&lt;br /&gt;        Console.Write("/* ")&lt;br /&gt;        For i As Integer = 1 To width&lt;br /&gt;            Console.Write(c)&lt;br /&gt;        Next&lt;br /&gt;        Console.WriteLine(" */")&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' テスト用モジュール&lt;br /&gt;Module Module1&lt;br /&gt;    Sub main()&lt;br /&gt;        Dim vbComment As New VBCommentPrinter("VB用コメント")&lt;br /&gt;        vbComment.print()&lt;br /&gt;&lt;br /&gt;        Console.WriteLine()&lt;br /&gt;&lt;br /&gt;        Dim cComment As New CCommentPrinter("C言語用コメント")&lt;br /&gt;        cComment.print()&lt;br /&gt;&lt;br /&gt;        Console.WriteLine()&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;上記のプログラムを実行すると、以下のように表示されます。&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-vxgjIEdsrhM/TqU9RT1DKJI/AAAAAAAAAZg/pJhzAIs9VAc/s1600/20111024_tmpmtd.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/-vxgjIEdsrhM/TqU9RT1DKJI/AAAAAAAAAZg/pJhzAIs9VAc/s1600/20111024_tmpmtd.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;VBCommentPrinter&lt;/span&gt; と &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;CCommentPrinter&lt;/span&gt; オブジェクトともに、いずれも &lt;b&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;printHeader()&lt;/span&gt; 、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;printMain()&lt;/span&gt; 、&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;printFooter()&lt;/span&gt; 関数が &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;AbstractTemplate&lt;/span&gt; クラスで定めた順序通りに実行されている&lt;/b&gt;事がわかります。&lt;br /&gt;&lt;br /&gt;繰り返しますが、基底クラスはあくまで雛型の役割しかしておらず、具体的な処理を一切制限しないという点が重要です。&lt;br /&gt;&lt;br /&gt;そのため、よく使う処理の流れだけを前もってテンプレートにしておき、後から具体的に処理を書いていくという使い方ができます。便利ですよ。&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【Facade パターン】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;そろそろ力尽きそうなので、今日はこれで最後。ラストは個人的にかなり気に入っている Facade パターンのご紹介です。&lt;br /&gt;&lt;br /&gt;オブジェクト指向に少し慣れてくると、部品のような細々としたクラスを個別に扱うのが面倒になってきます。なぜかというと、プログラマはそれらの雑多なクラスに関する知識が必要になってくるからです。&lt;br /&gt;&lt;br /&gt;そこで、それらをまとめて扱うための『窓口（Facade）』を作り、以後は&lt;b&gt;個々の部品を直接いじるのではなく、その窓口を通して処理を行うように&lt;/b&gt;しよう、というのが Facade パターンです。&lt;br /&gt;&lt;br /&gt;サンプルは、掃除、洗濯、調理といったあらゆる家事を行う個々のクラスと、それらのまとめ役であるチーフ（これが Facade になります）を作ります（&lt;span style="color: red;"&gt;展開してご覧ください&lt;/span&gt;）。&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb:collapse" name="code"&gt;'  ========================================&lt;br /&gt;'                   Facade&lt;br /&gt;'  ========================================&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 掃除屋さん&lt;br /&gt;Public Class Cleaner&lt;br /&gt;    Public Sub clean()&lt;br /&gt;        Console.WriteLine("お掃除します∩( ・ω・)∩")&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 洗濯屋さん&lt;br /&gt;Public Class Washer&lt;br /&gt;    Public Sub wash()&lt;br /&gt;        Console.WriteLine("お洗濯します(＞Д＜)ゝ""")&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 料理人さん&lt;br /&gt;Public Class Kitchener&lt;br /&gt;    Public Sub cook()&lt;br /&gt;        Console.WriteLine("お料理しますω(ﾟωﾟ)ω ")&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' -----------------------------------------&lt;br /&gt;' 3人をまとめるチーフ&lt;br /&gt;Public Class Chief&lt;br /&gt;    Dim cleaner As Cleaner&lt;br /&gt;    Dim washer As Washer&lt;br /&gt;    Dim kitchener As Kitchener&lt;br /&gt;&lt;br /&gt;    Public Sub New()&lt;br /&gt;        cleaner = New Cleaner()&lt;br /&gt;        washer = New Washer()&lt;br /&gt;        kitchener = New Kitchener()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    ' めんどうな家事をまとめて行う&lt;br /&gt;    Public Sub doHousework()&lt;br /&gt;        cleaner.clean()&lt;br /&gt;        washer.wash()&lt;br /&gt;        kitchener.cook()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;' -----------------------------------------&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;' テスト用モジュール&lt;br /&gt;Module Module1&lt;br /&gt;    Sub main()&lt;br /&gt;&lt;br /&gt;        Dim chief As New Chief()&lt;br /&gt;        chief.doHousework()&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;このプログラムでは、チーフオブジェクトが掃除屋、洗濯屋、料理人といったすべてのオブジェクトを一元的に管理してくれるため、（&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;main&lt;/span&gt; の中身を書く）プログラマは、ごちゃごちゃしたオブジェクトを意識する必要がなくなります。これが一番の利点です。&lt;br /&gt;&lt;br /&gt;そして、チーフにアクセスしている（外部の）オブジェクトは、（チーフが管理している）個々のオブジェクトに対して直接的な依存関係がありませんから、それら雑多なオブジェクトのいずれか一つが変更されても、影響を受ける範囲を制限する事ができます。&lt;br /&gt;&lt;br /&gt;このパターンは比較的簡単ですから、パターンを学ぶ前から当たり前のように行っていた方も多いと思います。 &lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;【てきとうあとがき】&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;なんかちょっと&lt;b&gt;消化不良&lt;/b&gt;ではありますが、VB のための GoF のパターンをいくつかご紹介してきました。&lt;br /&gt;&lt;br /&gt;個人的には、Adapter パターンや Strategy パターン、Composite パターンや Abstract Factory パターンなどについても、実コードを交えつつ紹介
