課題
まずサンプルプログラムをダウンロードしてください.
サンプルプログラムは次のように操作します.
- [j],[J]を押すことで,カメラ位置の経度が変わります.
- [k],[K]を押すことで,カメラ位置の緯度が変わります.
- [z],[Z]を押すことでカメラの原点からの距離が変わります.
- [r]を押すとカメラの位置が最初の状態に戻ります.
- [t]を押すと制御点の表示のON/OFFが切り替わります.
- [q],[ESC]を押すとプログラムが終了します.
なおこのプログラムでは,画像を曲面に貼り付けるようになっています. 画像はコマンドラインで指定するようになっています.
具体的にはたとえば「4arrows.jpg」を画像として使うとして,次のように指定します.
今回の課題では,このサンプルプログラムを拡張して,制御点を動かして曲面を変形できるようにして下さい. 制御点は一つずつ動かすことにします.
- 動かす対象となる制御点を選択する仕組みを用意します
- 選択された制御点を動かす仕組みを用意します
- 制御点が変更されたら,曲面を再構成してから再描画します
なお制御点は少なくともz軸上で動かせる,つまり制御点のz座標の値が変えられるようにして下さい. 余裕があれば,あわせてx軸上やy軸上でも動かせるようにして下さい.
以下では制御点の扱いについて説明します.
制御点の扱い
曲面を決定する制御点は{ Pij | 0≦i≦M,0≦j≦N }のような2つの添字をもった点の集合として扱われます. サンプルプログラムでは制御点の集合は次のように定義されています.
__surf_points = CtrlPointSet.new([ [[-3.0, 3.0, -2.0],[-1.0, 3.0,-1.0],[ 1.0, 3.0, 1.0],[ 3.0, 3.0, 2.0]], [[-3.0, 1.0, -2.0],[-1.0, 1.0,-1.0],[ 1.0, 1.0, 1.0],[ 3.0, 1.0, 2.0]], [[-3.0,-1.0, 2.0],[-1.0,-1.0, 1.0],[ 1.0,-1.0, -1.0],[ 3.0,-1.0,-2.0]], [[-3.0,-3.0, 2.0],[-1.0,-3.0, 1.0],[ 1.0,-3.0, -1.0],[ 3.0,-3.0,-2.0]]])
この場合,二つの添字i,jで区別される4x4=16個の制御点で曲面が決定されることになります. 添字は次のようにつけられています.
P00 P10 P20 P30 P01 P11 P21 P31 P02 P12 P22 P32 P03 P13 P23 P33
制御点のうち一つがアクティブ(active)な制御点として扱われるようになっています. そのアクティブな制御点を移動させる対象とします. 初期状態ではP00がアクティブです. アクティブな制御点は変更できます.
制御点と同様に座標軸についてもアクティブな座標軸が予め設定されています. x,y,z軸のうち一つだけがアクティブになっています. 初期状態ではz軸がアクティブです. アクティブな座標軸は変更できます.
今回の課題では,制御点の集合に関して以下のような機能が利用できます.
アクティブな制御点の移動
アクティブな制御点はアクティブな座標軸に沿って,次のようにして移動できます.
# アクティブな制御点をアクティブな座標軸に沿ってdだけ移動する
# d=移動量(正または負)
__surf_points.move(d)
アクティブな制御点の変更
アクティブな制御点は次のようにして変更できます.
# アクティブな制御点の添字を(i,j)から(i+a,j+b)にずらす # ただし添字が範囲を越える場合には,剰余演算を適用して # 巡回的にずらす __surf_points.shift_active(a,b) # アクティブな制御点を(上下左右の)隣に一つずらす __surf_points.shift_active(-1,0) # Pij → Pi-1j __surf_points.shift_active(1,0) # Pij → Pi+1j __surf_points.shift_active(0,-1) # Pij → Pij-1 __surf_points.shift_active(0,1) # Pij → Pij+1
アクティブな座標軸の変更
アクティブな座標軸は次のように変更できます.
# アクティブな座標軸を巡回的に変更する(z軸→x軸→y軸→z軸→x軸→...)
__surf_points.switch_active_axis
その他の機能
以下は必ずしも利用しなくて構いません.参考までに示しておきます.
制御点データの再初期化(reset)
次のメソッドで制御点をすべて初期位置に戻せます. アクティブな制御点,アクティブな座標軸も初期設定に戻ります(P00とz軸).
# 制御点をすべて初期位置に戻す
# アクティブな制御点,アクティブな座標軸も初期設定に戻す
__surf_points.reset
任意の制御点の移動
(アクティブかどうかに関わらず)任意の制御点を任意の座標軸に沿って動かすことができます.
# Pijをa番目の軸に沿ってdだけ移動する
# (a=0→x軸,a=1→y軸,a=2→z軸)
__surf_points.move(d,i,j,a)
なお添字の範囲は次のようになっています.
i = 0,1,...,n_u-1 # (ただしn_u = __surf_points.colsで得られる値) j = 0,1,...,n_v-1 # (ただしn_v = __surf_points.rowsで得られる値)
アクティブな制御点の指定
アクティブな制御点はshift_active(a,b)で変更する(ずらす)だけではなく, 直接指定することもできます. 指定する添字の範囲は上に示した通りです.
# Pijをアクティブにする
__surf_points.active = [i,j]
アクティブな制御点の情報取得
アクティブな制御点の情報を次のようにして得ることもできます.
# アクティブな制御点の座標を得る p_act = __surf_points.active # 上で得られたp_actは3次元座標を並べた配列 p_act[0] # x座標 p_act[1] # y座標 p_act[2] # z座標
制御点の情報取得
アクティブな制御点以外でも制御点を個別に添字i,jで直接指定できます. また制御点の各成分(x,y,z)をそれぞれ独立に処理できます.
pt = __surf_points.point(i,j) # 上で得られたptは3次元座標を並べた配列 pt[0] # x座標 pt[1] # y座標 pt[2] # z座標 pt[0] = pt[0] + 0.5 # x座標を0.5増やす
制御点のコピー
次のように制御点のコピーを作ることで, 制御点の位置を保ったままデータを変更して利用することができます.
p_act = __surf_points.active p_act1 = p_act.dup # p_act1はp_actの複製物(p_act,p_act1は別モノ) p_act1[2] = p_act1[2]-0.5 # p_act1のz座標を変更する.p_actは影響を受けない
なお次のようにした場合は同じ制御点に二つ名前をつけることになります.
# 複製を作らない(dupを使わない)場合 p_act2 = p_act # p_act2はp_actと同一(同一のデータに名前が二つあるだけ) x_0 = p_act[0] # x_0にp_actのx座標を保存する p_act2[0] = -p_act2[0] # p_act2のx座標を反転する p_act[0] == x_0 # ==> false p_act[0] == -x_0 # ==> true
p_act2とp_actは同じ点です. つまり一つの点にp_actとp_act2と二つ名前があるだけで, p_actでもp_act2でも操作する対象は同一です.
アクティブな座標軸の指定・情報取得
アクティブな座標軸は次のように直接指定できます. またアクティブな座標軸の情報を調べることもできます.
# アクティブな座標軸をaに設定する(a == 0,1,2のいずれか) __surf_points.active_axis = a # アクティブな座標軸を得る # (軸は番号で表される: x軸→0,y軸→1,z軸→2) a = __surf_points.active_axis # a == 0,1,2のいずれか