アニメーションによるビジュアライズ
前回まで
航空路上の座標をプロットして空間にデータをビジュアライズした。
今回は
航空路上の座標間を移動する物体をビジュアライズして、アニメーション(動き)のある表現にする。
「補間」について
データは離散値である。そのため、プロットした点と点の間は連続していない。点と点を線で結ぶことはできるが点間を動くアニメーションを作るには間を補う必要がある。これを補間という。
補間にもいろいろな計算方法があるが、今回は最も単純な「線形補間」で点間の座標を補間する。
lerp()関数
lerp(ラープ)と読む。lerpとは「Linear Interpolation」(線形補間)の略。ある値aと値bの間を補間を計算することができる。
lerp関数には3つのパラメータがある。
float a = 0;
flaot b = 100;
float c = lerp(a, b, 0.5);
この場合、cはaとbのちょうど中間の値(50)となる。第3パラメータは必ず0〜1の間の値を指定する。
サンプル(sketch_001_2points)
float start = 50;
float end = 450;
void setup(){
size(500,500);
strokeWeight(10);
}
void draw(){
background(0);
stroke(255);
point(start, height/2);
point(end, height/2);
text("press any keys", 10, 15);
if(keyPressed){
stroke(255,0,0);
point( lerp(start, end, 0.2) , height/2 ); //startとendの20%で補完
point( lerp(start, end, 0.4) , height/2 ); //startとendの40%で補完
point( lerp(start, end, 0.6) , height/2 ); //startとendの60%で補完
point( lerp(start, end, 0.8) , height/2 ); //startとendの80%で補完
}
}
sketch_001_2pointsの実行結果
2点間を移動するアニメーション
drawを実行するごとに、補間するポイントをずらしてアニメーションさせる。
サンプル(sketch_002_2points_animation)
float start = 50;
float end = 450;
float inter = 0; //補間する位置を指定する変数(0〜1に変化させてアニメーションする)
void setup(){
size(500,500);
strokeWeight(10);
}
void draw(){
background(0);
stroke(255);
point(start, height/2);
point(end, height/2);
text("press any keys", 10, 15);
if(keyPressed){
stroke(255,0,0);
point( lerp(start, end, inter) , height/2 );
inter+= 0.01; //補間するポイントを0.01(1%)ずつ動かす
if(inter > 1.0) inter = 0; //1.0になったら0に戻す
}
}
複数の点間を移動するアニメーション
2点間の補間アニメーションを応用して複数の点の間をアニメーションで繋いでみよう。
まずは、最初の点(点0)と次の点(点1)の間を補間する。点1まで移動したら、点1と点2の間を補間する。ある区間の補間が終わったら次の区間に移動し、最後の区間を補間し終えたら終了する。
サンプル(sketch_003_5points_animation)
float [] x = {50,150,250,350,450};
float [] y = {400,230,80,120,370};
int n = 0; //補間する区間の番号
float inter = 0;
void setup(){
size(500,500);
strokeWeight(10);
}
void draw(){
background(0);
stroke(255);
for(int i = 0; i< x.length; i++){
point(x[i], y[i]);
}
text("press any keys", 10, 15);
if(keyPressed){
stroke(255,0,0);
point( lerp(x[n], x[n+1], inter) , lerp(y[n], y[n+1], inter) );
inter+= 0.01; //補間するポイントを0.01(1%)ずつ動かす
if(inter > 1.0){
inter = 0;
n += 1;
if(n >= x.length -1) n = 0; //最後の区間の補間が終わったらnを0に戻す
}
text("n = "+n, 10, 30);
text("inter = "+inter, 10, 45);
}
}
アニメーションの速度を調整できるようにしてみる
実行しながら速度を変更できるように、GUIライブラリをインポートして、スライダーから速度を調整できるようにしてみる。
サンプル(sketch_004_animation_speed)
//GUIライブラリ「controlP5」をインポート
//http://www.sojamo.de/libraries/controlP5/#about
import controlP5.*;
ControlP5 gui;
float [] x = {50,150,250,350,450};
float [] y = {400,230,80,120,370};
int n = 0;
float inter = 0;
float animateSpeed = 0.01;
void setup(){
size(500,500);
strokeWeight(10);
//controlP5のインスタンスを作成して、animeteSpeedがスライダーの値を受け取るようにする
//setPositionはスライダーの表示位置、setRangeはスライダーから受け取りたい最小値と最大値
gui = new ControlP5(this);
gui.setAutoDraw(false);
gui.addSlider("animateSpeed").setPosition(10,50).setRange(0,0.1);
}
void draw(){
background(0);
stroke(255);
//静止している点を描画
for(int i = 0; i< x.length; i++){
point(x[i], y[i]);
}
//補間して動く点を描画
stroke(255,0,0);
point( lerp(x[n], x[n+1], inter) , lerp(y[n], y[n+1], inter) );
//補間の調整
inter += animateSpeed; //補間するポイントを動かす量をスライダで変更する
if(inter > 1.0){
inter = 0;
n += 1;
if(n >= x.length -1) n = 0; //最後の点間の補間が終わったらnを0に戻す
}
text("n = "+n, 10, 15);
text("inter = "+inter, 10, 30);
//GUIの描画
gui.draw();
}
3次元で点間を移動するアニメーション
peasycamを導入して、3次元空間で点間を移動させてみる。
基本は2次元と同じ。ここでは、PVectorをつかってみる。guiの書き方に少し違う部分がある。
サンプル(sketch_005_animation_speed_3d)を確認してみよう。
PVectorクラスが持つlerp関数を使うパターンも確認してみよう(sketch_006_animation_speed_3d_2)
演習
前回実装した、球面へマッピングした航空路上を移動する航空機をアニメーションで表現してみよう。
解説
前回のサンプルに含まれる「plot_route_earth」をベースにアニメーションに必要な改良を加えていこう。完成版のコードと解説は次の通り。
1. Positionクラスの改良
まず、のちのちlerp()関数をx,y,zそれぞれに対して実行する手間を考慮して、3次元空間の座標のをx, y, zではなく、PVectorくらすでまとめて管理する方法に変える。具体的には、Postitionクラスを以下のように変更する。
class Position{
String point;
float lon; //longitude=経度(東西)
float lat; //lattidude=緯度(南北)
float alt; //高度(m)
PVector coord; // 空間上の座標x,y,zを格納するPVector
Position(String _line){
lon = float(_line.split(",")[0]);
lat = float(_line.split(",")[1]);
alt = float(_line.split(",")[2]);
//地球の半径を200pxとする。
pointToEarth(lon, lat, 200 + alt/2000);
}
void pointToEarth(float _lon, float _lat, float _r){
float x = _r * sin(PI/2 - radians(_lat)) * sin(radians(_lon));
float y = -1 * _r * cos(PI/2 - radians(_lat));
float z = _r * sin(PI/2 - radians(_lat)) * cos(radians(_lon));
coord = new PVector(x,y,z);
}
void draw(){
vertex(coord.x, coord.y ,coord.z);
}
}
ポイントは、
これまで使用してきたメンバー変数(注)x, y, zをPVector coordにまとめる。
- x は coord.x
- y は coord.y
- z は coord.z
となる。ちなみに、 coordは「coordinates(座標)」の略。
pointToEarth()で計算した結果x,y,xは、coordに入力する。
drawの実行にもcoordの座標を用いる。
2. Flightクラスの実装
(解説後日)