可展面作成アルゴリズム

可展面(かてんめん、developable surface)とは、伸縮することなしに平面に展開することができる計量を持つ曲面である。逆の言い方をすれば、平面を曲げたり切ったり丸めたりつなげたりすることで作ることのできる曲面である。滑らかでない(折りを含む)曲面は可展面として扱わないことが多い。
(中略)
平面を曲げることで形成できるという特徴は、金属板、段ボール、合板などから構造物を製造する上でも重要である。可展面は造船において特に活用されている。

http://ja.wikipedia.org/wiki/%E5%8F%AF%E5%B1%95%E9%9D%A2

こういうツールを発見。
http://en.wiki.mcneel.com/default.aspx/McNeel/DevSrf.html
たぶん船の船体の設計に使うんだろう。車のボディとかなら鉄板をプレスして自由に曲面を作れるが船くらい大きいとそうはいかない。また厚さもあるのでガウス曲率のある部分では厚さが変化して薄い部分に圧力や変形が集中するので好ましくない。変形でなく型を使って最初から鋳造するという手もあるが船は難しい。戦艦大和バルバス・バウとかはどうやって作ったんだろう。日本海軍のも宇宙戦艦もそうだけど、あれは紙工作にとってネックだ。
ステルス機なんかでも、表面に特別な素材を使うだろうから、それが金属みたいに柔軟でないとすればシート状のものを曲げて張り付けることになり、可展面であることが要求される。F-117なんかは出撃のたびに電波吸収材を張りつけてたらしいけど、あれは多面体だから問題ない。多面体である理由は電波の反射を計算する理論が当時は多面体の場合しかなく、コンピュータの性能も高くなかったため。

WikipediaMathworld には可展面の一覧として円錐、円柱のみが書いてあるが、もっとある。前の日記にも書いたけど更にあった。二つの曲線(レールと呼ぶらしい)の間を直線で結ぶ(横木とよぼう)とできる面で、横木二つで囲まれる面がねじれてないものが可展面。

Case1: 二つの曲線が平行な平面にある場合

この場合、二つのレールで曲線の傾きが等しい点同士を結べばよい。簡単化のため、両方の曲線で傾きは区間内で単調増加あるいは減少するとする。そうでない場合でも曲線を変曲点で分割し複数のセグメントを生成し、それぞれのセグメント(内部で傾きが単調増加or減少)で同様の処理を行えばいい。
まずそれぞれのレールをなるべく均等に分割し点をおく。横軸等分割でいい。分割数は2つのレールで異なってもいい。それぞれの点vで、相手のレール上で自分の位置の傾きと同じ傾きをもつ点P(v)を見つける。ない場合は、相手のレール上の傾きの最大値以上である場合は相手レールの終点、最小値以下の場合は起点を選ぶ。(修正:これはいらない)
レール1、2上の点v1, v2 を順にたどっていく。

  • 0: 初期値としてv1, v2両方共起点を選ぶ。v1, v2 を結ぶ線を引く。2つのレールで、変曲点で区切られたセグメントの数を比較。一致しなければアボート。それぞれのセグメントで二回微分の符号が一致しなくてもアボート。
  • 1: ループ開始。v1,v2共にレールの終点に達していたら終了
  • 2: v1, v2 がそれぞれ変曲点で区切られたセグメントの何番目にいるか調べる。レールの終点にいる場合はこの番号を+1する。
    • セグメント番号が異なる場合はv1,v2でセグメント番号が小さいほうを一歩進める。そしてv1,v2の間に線を引いて1へ戻る
  • 3: v1, v2 上での傾き s1, s2 を計算。傾きが単調減少ならそれぞれ-1倍する。
    • s1>s2 であれば、v2を一歩進める。そしてv1,v2の間に線を引いて1へ戻る
    • そうでなければ、v1を一歩進める。そしてv1,v2の間に線を引いて1へ戻る

結んだ線は順番に記録しておき、前回の線と、今回一歩進んだ点からなる三角形を面として登録。

Case2: 二つの曲線が平行でない平面にある場合

二つの平面が交わる直線をz軸とする。二つのレール上の点を結べる条件は、各点の接線を伸ばしてz軸に交わった切片が双方で一致すること。z軸に垂直な、曲線1の横軸座標をx1とする。 x1=0での切片は B1=f(x1)-x1 f'(x1)と計算できる。2も同様。B1=B2 となる点同士を結べばいい。 B1'=-f1'' x1 であるから、f1 が単調増加ならB1 も単調増加。したがって平行な場合と同様にfの変曲点でセグメント分割すればいい。
アルゴリズムは上と同じで、ステップ3において傾きの代わりにB1,B2 を比較すればいい。

Case2':

平面が交わる線を共通の横軸として、別の方向に縦軸をとって曲線を結びたい場合のほうが多い。上図は零戦主翼先端のイメージ。この場合は接線の横軸との切片C1,C2を比較する。C=-B/f' と計算できるが、分母が0になるところで都合が悪い。なので代わりに C=atan2(f', B)を計算して比較する。

曲線の表現

3次スプラインが簡単でよい。傾きは二次式になり、同じ傾きを持つ点を探すときは二次方程式をとけばいい。Bの場合は3次になるんでニュートン法で解く。制御点では値の他に傾きも指定できるようにする。両端では必ず傾きを指定。2つの制御点に挟まれた区間の両端で値と傾きの両方が設定されている場合は3次式の係数は一意に決定できる。どちらか一方の場合はその点で二回微分まで連続になるように決める。

クラス

頂点、稜線、面などはC++のクラスを作成しておき、頂点からそれを含む稜線へのポインタ、稜線からそれを含む2つの面へのポインタ、面の法線ベクトルなどを記録しておく。形状を生成した後で各稜線での折れ具合とか、各頂点でのガウス曲率などを計算できる。これらと紙の弾性を比較して実際には作成が困難な部分などをあらかじめ検出できるかもしれない。またレンダリングにこれらのデータを使うことができる。

追記

Case 1 で生成される面が接線曲面 (Tangent Developable) であることを示す。
二つの曲線を r1(t), r2(t) とおく。dr1(t)/dt と dr2(t)/dt が常に並行となるようにパラメータtをとることが可能。このとき生成される面は r1(t) + s (r1(t)-r2(t))と表される。
ここで曲線 r3(t)=r1(t) - r12(t) (v1(t)・v12(t)/(v12(t))^2) を考える。ただしv1(t)=dr1(t)/dt, v2(t)=dr2(t)/dt, また r12 = r2(t)-r1(t), v12=v2(t)-v1(t)とおいた。r3(t)の接線を計算するとr1(t)、r2(t)を通る直線であることが分かる。したがって Case 1 で生成される面は接線曲線であり、可展面である。

Case 2の場合:
r3(t)= r1(t) + s(t) r21(t), s(t)= (-v1 ×r12(t)/v12(t)×r12(t))とおけば同様にr3の接線はr1, r2 を通る。