オブジェクトを移動、回転、拡大縮小するには、オブジェクトそのものを操作する以外に「座標系」を変換する方法があります。
一般的にプログラミングの場合、直交座標系(デカルト座標系)を使いますが、この座標系とはある点を空間上に置くための規則です。
通常は必ず画面の左上が(0, 0)になりますが、座標を移動すると、左上が(0, 0)でなくなる場合もあります。
例えば、translate() 移動、rotate() 回転、scale() 拡大縮小を順番に実行する場合、概念的には【図4.1-a】になります。
4.1.1 translate(移動)
それでは、まず座標系を移動するところから始めます。
右方向に100ピクセル、下方向に100ピクセル移動してみます。その場合にはtranslate(x, y)を使います。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//座標系を(x座標100, y座標100に移動
translate(100, 100);
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 40, 40); //4角形を描画
ここで注意しなければいけないのは、一見四角形が動いているかに見えますが、実は、座標全体が動いているのです。つまり、「世界」全体が動いているのです。
次に、translate()を2回続けてみます。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//1つ目の4角形/////////////////////////////////
//座標系を(x座標100, y座標100)に移動
translate(100, 100);
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 40, 40); //4角形を描画
//2つ目の4角形/////////////////////////////////
//座標系を1つ前の移動位置から(x座標103, y座標103)移動
translate(103, 103);
//座標系の確認用に青のグリッドを作成
stroke(100, 100, 255, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(0, 0, 255, 127); //半透明の青
rect(0, 0, 40, 40); //4角形を描画
ここでのポイントは、一度translateすると、次のtranslateはすでに移動した地点を基準点としてさらに移動するということです。
4.1.2 rotate(回転)
同じ要領で座標系を回転してみます。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//座標系を基準点を中心に時計方向に30度回転
rotate(radians(30));
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 50, 50); //4角形を描画
次にrotateを2回おこなってみます。translateの時と同じように、はじめに30度回転して次にさらに30度回転することによって、合計60度回転することになります。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//1つ目の4角形/////////////////////////////////
//座標系を基準点を中心に時計方向に30度回転
rotate(radians(30));
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 50, 50); //4角形を描画
//2つ目の4角形/////////////////////////////////
//座標系を基準点を中心に時計方向に30度回転
rotate(radians(30));
//座標系の確認用に青のグリッドを作成
stroke(100, 100, 255, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(0, 0, 255, 127); //半透明の青
rect(0, 0, 40, 40); //4角形を描画
4.1.3 scale(拡大/縮小)
さらに座標系を拡大/縮小してみます。等倍は1.0、最小値は0.0になります。2倍の場合は2.0になりますね。
size(400, 400);
rectMode(CENTER); //四角形の基準点を中心に変更
background(255);
scale(0.8); //座標系を80%縮小
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 40, 40); //四角形を描画
scaleを0.8(80%)に変換するの作業を2回おこなった場合、元の大きさの64%(0.8 x 0.8 = 0.64)の大きさとなります。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//1つ目の4角形/////////////////////////////////
scale(0.8); //座標系を80%縮小
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 40, 40); //4角形を描画
//2つ目の4角形/////////////////////////////////
//座標系をさらに80%縮小 → 元からは0.8 x 0.8 = 0.64 = 64%になる
scale(0.8);
//座標系の確認用に青のグリッドを作成
stroke(100, 100, 255, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(0, 0, 255, 127); //半透明の青
rect(0, 0, 40, 40); //4角形を描画
4.1.4 for文を使って複数の座標系を表示する
前述のサンプルも、2つの座標系を同時表示していましたが、for文を使えば複数表示することができます。グリッドを非表示にするとオブジェクトがたくさん生成されているように見えますが、重要なのはオブジェクトが複製されているのではなく、「オブジェクトが存在する世界(座標)が複製されている」ということなのです。
【リスト4.1-g】を実行してみましょう。縦横43ピクセルずつ移動して、その移動を10回繰り返します。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
for (int i = 0; i < 10; i ++) { //10回繰り返す
translate(43, 43); //座標系を(x座標43, y座標43に移動
int r = int(random(255)); //ランダムに色を決定
int g = int(random(255));
int b = int(random(255));
//座標系の確認用に赤のグリッドを作成
stroke(r, g, b, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(r, g, b, 127);
rect(0, 0, 40, 40); //4角形を描画
}
4.1.5 pushMatrix, popMatrix(座標系の保存と呼び出し)
複製する場合はこれでも問題ないのですが、座標を加算減算していく方法は、直感的にはかなり分かりにくいといえます。しかし、今まで学習した座標の指定に近い方法もあり、それはpushMatrix, popMatrixによって可能になります。
pushMatrixによって現在の座標系を保存して、popMatrixによって保存した座標系を呼び出すという手順を踏みます。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
//1つ目の4角形/////////////////////////////////
pushMatrix(); //現在の座標系を保存
translate(100, 100); //座標系を(x座標100, y座標100)に移動
//座標系の確認用に赤のグリッドを作成
stroke(255, 150, 150, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(255, 0, 0, 127); //半透明の赤
rect(0, 0, 40, 40); //4角形を描画
popMatrix(); //前の座標系を呼び出す
//2つ目の4角形/////////////////////////////////
pushMatrix(); //現在の座標系を保存
//座標系を元の基準点から(x座標103, y座標103)移動
translate(103, 103);
//座標系の確認用に青のグリッドを作成
stroke(100, 100, 255, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(0, 0, 255, 127); //半透明の青
rect(0, 0, 40, 40); //4角形を描画
popMatrix(); //前の座標系を呼び出す
ここでのポイントは、1回目の移動の後、2回目の移動の前にまた基準点が(0, 0)に戻っているという点です。このことによって、2回目も、(103, 103)という直感的に位置を確認しやすい座標に四角形を移動することができます。for文を使って描画すると【図4.1-j】のようになります。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
for (int i = 43; i < 400; i += 43) { //10回繰り返す
pushMatrix(); //現在の座標系を保存
//座標系を(x座標43*i, y座標43*iに移動
translate(i, i);
int r = int(random(255)); //ランダムに色を決定
int g = int(random(255));
int b = int(random(255));
//座標系の確認用に赤のグリッドを作成
stroke(r, g, b, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(r, g, b, 127);
rect(0, 0, 40, 40); //4角形を描画
popMatrix(); //前の座標系を呼び出す
}
4.1.6 translate, rotate, pushMatrix, popMatrixを組み合わせる
これらを応用すると図形を円状に配置することが簡単にできます。
size(400, 400);
rectMode(CENTER); //4角形の基準点を中心に変更
background(255);
translate(width/2, height/2); //座標系を画面中央に移動
for (int i = 0; i <= 180; i += 30) {
pushMatrix(); //現在の座標系を保存
rotate(radians(i)); //座標系を回転
int r = int(random(255)); //ランダムに色を決定
int g = int(random(255));
int b = int(random(255));
//座標系の確認用に赤のグリッドを作成
stroke(r, g, b, 127);
for (int x = 0; x <= 400; x += 20) {
line(x, 0, x, 400); //縦の線を描画
}
for (int y = 0; y <= 400; y += 20) {
line(0, y, 400, y); //横の線を描画
}
fill(r, g, b, 127); //半透明の赤
//4角形を基準点から100ピクセルx軸方向に移動して描画
rect(100, 0, 40, 40);
popMatrix(); //前の座標系を呼び出す
}
4.1.7 アニメーション
角度をdraw()関数の中で変更することによって、アニメーションもできます。
int angle = 0; //角度の初期値は0
void setup() {
size(500, 500);
rectMode(CENTER);
noFill();
}
void draw() {
background(255);
translate(width/2, height/2); //座標系を画面中央に移動
pushMatrix(); //現在の座標系を保存
rotate(radians(angle)); //座標系を回転
stroke(200);
//座標系の確認用にグレーのグリッドを作成
for (int x = 0; x <= width; x += 10)
line(x, 0, x, height);
for (int y = 0; y <= height; y += 10)
line(0, y, width, y);
stroke(0);
//4角形を基準点から100ピクセルx軸方向に移動して描画
rect(100, 0, 40, 40);
popMatrix(); //前の座標系を呼び出す
//angleを加算。360度になったら0度に戻す
angle ++;
if (angle >= 360) angle = 0;
}
4.1.8 複製しながらアニメーション
さらに、for文を使って複製しながらアニメーションすることもできます。
float angle = 0.0; //角度の初期値は0.0
void setup() {
size(500, 500);
rectMode(CENTER);
noFill();
stroke(0);
}
void draw() {
background(255);
translate(width/2, height/2); //座標系を画面中央に移動
for (int i = 0; i < 360; i += 30) {
pushMatrix(); //現在の座標系を保存
rotate(radians(angle + i)); //座標系を回転
//座標系の確認用にグレーのグリッドを作成
/*
stroke(200);
for (int x = 0; x <= width; x += 10)
line(x, 0, x, height);
for (int y = 0; y <= height; y += 10)
line(0, y, width, y);
*/
//4角形を基準点から100ピクセルx軸方向に移動して描画
rect(100, 0, 40, 40);
popMatrix(); //前の座標系を呼び出す
}
//angleを加算。360.0度になったら0.0度に戻す
angle += 0.5;
if (angle >= 360.0) angle = 0.0;
}