クラインの壺っぽいもの

足りない機能は即席ツールを作りながらモデルを作ってみる。

STEP1: キャップと 2 本の曲げたパイプをつないでクラインの壺っぽいものを作る

[0] 部品構成
・下図のような cap, 曲げたパイプ(pipe1), 細らせて曲げたパイプ(pipe2) を作成してつなげる。

・パイプを細らせたり, 曲げたりは PLY_interactive.py では思ったようにできなかったので、即席の外部ツールで対応する。

[1] キャップを作る
  ・キャップの断面の座標を手入力し、回転させてキャップを作る。  

  p clear    ・・・ Poinst[ ]を空にする
  p 1  0  0 ・・・ Poinst[ ]に点 (1, 0,  0)を追加
  p 0.9 0.2 0 ・・・ Poinst[ ]に点 (0.9, 0.2, 0)を追加
  p 0.8 0.3 0 ・・・ Poinst[ ]に点 (0.8, 0.3, 0)を追加
  p 0.7 0.3 0 ・・・ Poinst[ ]に点 (0.7, 0.3 ,0)を追加
  p 0.6 0.2 0 ・・・ Poinst[ ]に点 (0.6, 0.2, 0)を追加
  p 0.5 0  0 ・・・ Poinst[ ]に点 (0.5, 0, 0)を追加

  p r 0 360/30 0 30 ・・・ Points[ ]内の点に回転を30回適用
                ⇒ 履歴が P2[ ] に格納される
                  P2[ ] の内容は表示されない
  surface eclose     ・・・ P2[ ]の内容を可視化する
  p2 save cap.npy    ・・・ P2[ ]の内容を cap.npy として保存する

[2] パイプ 1 を作る
  cap.npy を読み込んで path 方向と extrusion 方向を入れ替える。
  ※ path 方向, extrusion 方向はテキトーにつけた名称。u, v の方が良かったかも。

  l cap.npy  ・・・ cap.npy をロードする
  p2 transpose ・・・ path 方向と extrusion 方向を入れ替える
            P2[ ] の最後の要素が Points[ ] にコピーされる
  p2 save cap_transposed.npy ・・・ 転置した cap.npy をセーブ

  Points[ ] を y 軸下方向に伸ばしてパイプ 1 を作る。
  p t 0 -0.2 0 10 ・・・ y 軸方向に -0.2 平行移動を 10 回繰り返す
  surface        ・・・ P2[ ] の内容を可視化
  p2 save pipe1.npy  ・・・ P2[ ] の内容を pipe1.npy にセーブ

  即席のツール (bend.py) で .npy を曲げる
  python bend.py (.npy) (曲げ角度)
  ※ パイプ状のものが y 軸に沿って伸びていることしか想定していない即席ツール。

  python bend.py pipe1.npy 90

  結果を表示する。
  l pipe1_bended.npy
  surface pclose

[3] パイプ 2 を作る
  転置した cap.npy をロード ⇒ transpose ⇒ 外側の円環を選び, y 軸下方向に伸ばす。

  l cap_transposed.npy ・・・ 転置した cap.npy をロードする
  p pop 0       ・・・ P2[0] を Points[ ] にコピーする
  p t 0 -0,2 0 30 ・・・ Points[ ] をy軸方向に -0.2 移動を 30 回繰り返す
  surface pclose    ・・・ P2[ ] を可視化する
  p2 save pipe2.npy  ・・・ P2[ ] を pipe2.npy としてセーブする

  即席ツールでパイプを細らせる。
  python thinning.py (.npy) (最終スケール)
  ※ パイプ状のものが y 軸に沿って伸びていることしか想定していない即席ツール。

  python thinning.py pipe2.npy 0.5

  即席のツール (bend.py) で .npy を曲げる
  python bend.py pipe2_thinned.npy 270

[4] 部品の配置を決める   作成した部品をロードし, surface コマンドでメッシュ化
  t (x オフセット) (y オフセット) (z オフセット) ・・・ 部品の配置を調整する

  l cap_transposed.npy    ・・・ キャップをロード
  surface pclose       ・・・ メッシュ化
  t 0 5 0         ・・・ y軸方向に 5 持ち上げる

  l pipe1_bended.npy     ・・・ 曲げた pipe1 をロード
  surface pclose       ・・・ メッシュ化
  t 0 3 0          ・・・ y軸方向に 5 持ち上げる

  l pipe2_thinned_bended.npy ・・・ 細らせて曲げた pipe2 をロード
  surface pclose       ・・・ メッシュ化
  

  メッシュで調整した配置をそのまま .npy に書き出せないので(^^;)
  即席ツールで npy を平行移動する。

  python translate.py cap_transposed.npy 0 5 0
  python translate.py pipe1_bended.npy 0 3 0

[5] 部品をつなぐ

  pipe1 と pipe2 はオモテとウラの接続になるので, surfaceコマンドに任せたい。
  ⇒ pipe1 と pipe2 の間が始点、終点となるように .npy を(即席ツールで)連結する。

  python cat_npy.py pipe1_bended_translated.npy cap_transposed_translated.npy pipe2_thinned_bended.npy

  連結した .npy をロードして表示する。
  l stacked.npy
  surface pclose

  pipe1 の 終端(end)と cap の 始端(start) をつなぐので, こうなった。
  pipe1 の始端と終端を入れ替える。
  l pipe1_bended_translated.npy
  p2 reverse e ・・・ 押し出し方向の順番を逆順にする
  p2 save _pipe1.npy

  頂点の並びが変わり、法線の向きが変わるため, 表示のウラとオモテが反対になる。
  が,気にせず進める。

  cap.npy も始端と終端を入れ替える。
  l cap_transposed_translated.npy
  p2 reverse e
  p2 save _cap.npy

  連結しなおす。
  python cat_npy.py _pipe1.npy _cap.npy pipe2_thinned_bended.npy


STEP2: pipe1 がふさがらないように穴をあける

面と面の交線を求めて穴をあけるのも大変そうなので、manifold3d のブーリアン演算をありがたく使わせてもらう。

Open3D で作ったシリンダー(*)同士だと、簡単にブーリアン演算できたが、自前のメッシュだとなぜかブーリアン演算に失敗する・・・
( * o3d.geometry.TriangleMesh.create_cylinder で作ったシリンダーのこと)
ので, Open3D でシリンダーを作るコマンド( cylinder コマンド)を追加した。

[1] Open3D で作ったシリンダーを使って、切り口を求める。

  l _pipe1.npy  ・・・ パイプ 1 データをロード
  surface pclose ・・・ 可視化(メッシュ化)
  p pop 0   ・・・ 始端の切り口を Points[ ]にコピーする。(終端の場合は、p pop -1 )

  p i       ・・・ Points[ ]の情報を表示

  (例)
  x  1.000000  -0.500000 -  0.500000 ・・・ 半径 0.5
  y  0.994522  1.508217  -  2.502739
  z  0.000000  0.994522  -  0.994522

  cylinder (半径) (高さ) [(解像度 ・・・ 天板, 底面の円周上の点の数)]

  横向きのシリンダーを作る。
  ・半径は 0.5。
  ・高さ(長さ)は縦向きのシリンダーと "ぶつかるけど貫通しない" 長さに調整する。
  ・解像度は、pipe1, pipe2 の断面の点の数に合わせる。

  cylinder 0.5 3 ・・・ 半径 0.5, 高さ 3 のシリンダーを作る
  t 0 2 0    ・・・ y 軸方向に 1.5 持ち上げる
  save yoko.ply  ・・・ セーブする

  縦向きのシリンダーを作る。
  ・半径は 1.0。
  ・長さは   cylinder 1 3  ・・・ 半径 1, 高さ 3 のシリンダーを作る
  r 90 0 0   ・・・ x 軸周りに90°回転する
  t 0 1.5 0  ・・・ y 軸方向に 1.5 持ち上げる
  save tate.ply ・・・ セーブする

  python boolean.py tate.ply yoko.ply    ・・・ 即席ツールで tate.ply - yoko.ply のブール演算を行う。
  python ply2skeleton.py tate_minus_yoko.ply ・・・ 即席ツールでブール演算結果をスケルトン化する。
  l tate_minu_yoko_skeleton.npy        ・・・ スケルトン化したデータをロード
  skeleton fast                ・・・ スケルトン表示する

  円筒の表面から離れた頂点を含むメッシュを削除してやると、穴の開いた円筒の皮ができた。
  python triangles_filter.py tate_minus_yoko.ply

  が・・・
  ・ウラ面がない(ウラ側が透明)
   ⇒ オモテ面の ply を読み込んで裏返しした三角形を作ればよいだけ。

  ・横向きシリンダーが長すぎて反対がにも穴が開いた。
   ⇒ 横シリンダーの長さを調整してブーリアンをやり直せば解決可能。

  ・P2[ ]配列も surface コマンドも, 穴に対応していない
   ⇒ P2[ ]配列, surface コマンドの穴対応を考えるか・・・
     座標値が nan の行と列を挟む?
     transpose しても, 並びを逆順にしても穴を穴と認識できるようにしないと・・・
   ⇒ とりあえず、薄皮に色を塗って、裏面を作って surfaceコマンドで作ったメッシュと並べてみる。

   python create_back_face_from_front_face.py tate_minu_yoko_filtered.ply
   l tate_minus_yoko_filtered.ply      ・・・ 薄皮のオモテをロード
   c 128 128 255              ・・・ オモテを濃い青に設定
   l tate_minus_yoko_filtered_back_face.ply ・・・ 薄皮のウラをロード
   c 200 200 255              ・・・ ウラを薄い青に設定



ひとまず終了