9章 図形の移動(座標変換)
与えられた図形を移動させて描く方法について説明する。ここでは図形が座標系の
(1) 水平軸(x軸)に関して対称移動
(2) 垂直軸(y軸)に関して対称移動
(3) 原点に関して対称移動
(4) 原点回りに回転
する場合の作図方法について順に示す。
9−1 多角形の頂点を配列に格納する
ここでは、配列に点の座標を格納しておいて、それらを線分で結ぶことで図形を描く。まず、以下のような多角形を描いてみよう。
基本的な考え方:
頂点間を順に線分で結んでいけば図を描けるように、頂点の座標を配列に格納する。
プログラミング:
var pnt, pt : array [1..6,1..2] of integer; i, xc, yc, x1, y1 : integer; begin pnt[1,1] := 0; pnt[1,2] := 0; pnt[2,1] := 50; pnt[2,2] := 50; pnt[3,1] := 50; pnt[3,2] := 150; pnt[4,1] := 0; pnt[4,2] := 100; pnt[5,1] := -50; pnt[5,2] := 150; pnt[6,1] := -50; pnt[6,2] := 50; xc := 200; (作図中心位置) yc := 200; (作図中心位置) for i := 1 to 6 do begin (画面上での作図位置の計算) pt[i,1] := xc + pnt[i,1]; pt[i,2] := yc - pnt[i,2]; end; Canvas.Polygon([ Point(pt[1,1],pt[1,2]), Point(pt[2,1],pt[2,2]), Point(pt[3,1],pt[3,2]), Point(pt[4,1],pt[4,2]), Point(pt[5,1],pt[5,2]), Point(pt[6,1],pt[6,2]) ] ); end;
Canvas.Polygon([ Point(pt[1,1],pt[1,2]),
Point(pt[2,1],pt[2,2]),
Point(pt[3,1],pt[3,2]),
Point(pt[4,1],pt[4,2]),
Point(pt[5,1],pt[5,2]),
Point(pt[6,1],pt[6,2]) ] );
end;
補足説明1: Delphi(Pascal)で配列を宣言する方法
var pnt, pt : array [ 1..6, 1..2 ] of integer;
pnt, ptは各々配列名である。 arrayは配列を意味し、[ ] の中に配列の要素を指定する。
上記の例では6行2列の2次元配列を定義している。integerはこの配列の要素の値が整数であることを示している。
補足説明2: 多角形の点群データの格納方法
上記の配列内に多角形の点群データを格納する。配列pntはもとの図形データ、配列ptはもとの図形データに何らかの操作を加えた結果を格納するために用いている。
ここでは以下のように、各配列の要素に点群データの各値を直接代入している。
pnt[1,1] := 0; pnt[1,2] := 0;
pnt[2,1] := 50; pnt[2,2] := 50;
pnt[3,1] := 50; pnt[3,2] := 150;
pnt[4,1] := 0; pnt[4,2] := 100;
pnt[5,1] := -50; pnt[5,2] := 150;
pnt[6,1] := -50; pnt[6,2] := 50;
補足説明3: 画面上に描くために点群データを変換する
与えられた図形のデータを、画面の中心に描くために、次のように点群データをまとめて変換する。
for i := 1 to 6 do begin
pt[i,1] := xc + pnt[i,1];
pt[i,2] := yc - pnt[i,2];
end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
上記の例題1の図形を水平軸(x軸)に関して対称移動する。もとの図形も同時に描く。
プログラミング:
var pnt, pt : array [1..6,1..2] of integer; i, n, xc, yc, x1, y1 : integer; begin pnt[1,1] := 0; pnt[1,2] := 0; // ( 中 略 ) pnt[6,1] := -50; pnt[6,2] := 50; xc := 200; yc := 200; for n := 1 to 2 do //(2回繰り返し) begin for i := 1 to 6 do //(6個 begin pt[i,1] := pnt[i,1]; if n = 1 then pt[i,2] := pnt[i,2] else pt[i,2] := - pnt[i,2]; end; for i := 1 to 6 do //(画面上での作図位置の計算) begin pt[i,1] := xc + pt[i,1]; pt[i,2] := yc - pt[i,2]; end; Canvas.Polygon([ Point(pt[1,1],pt[1,2]), // ( 中 略 ) Point(pt[6,1],pt[6,2]) ] ); end; end;
補足説明: 座標変換の方法
nが1のときにはもとの図形、2のときにはx軸に関して対称移動した図形を描いている。この場合にはy座標の値を反転すればよいので、以下に示すように、nが2のときに配列pntの第2列の要素の値の符号を逆にし、配列ptに格納している。x座標の値は変化しないので、pntの第1列の要素の値をそのまま配列ptに代入している。
for n := 1 to 2 do begin for i := 1 to 6 do begin pt[i,1] := pnt[i,1]; if n = 1 then pt[i,2] := pnt[i,2] else pt[i,2] := - pnt[i,2]; end; end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
下図のように各頂点の座標が与えられた多角形と、それを垂直軸(y軸)に関して対称移動した図を描く。
プログラミング:
var pnt, pt : array [1..6,1..2] of integer; i, n, xc, yc, x1, y1 : integer; begin pnt[1,1] := 0; pnt[1,2] := 0; pnt[2,1] := 50; pnt[2,2] := -50; pnt[3,1] := 150; pnt[3,2] := -50; pnt[4,1] := 100; pnt[4,2] := 0; pnt[5,1] := 150; pnt[5,2] := 50; pnt[6,1] := 50; pnt[6,2] := 50; xc := 200; yc := 200; for n := 1 to 2 do begin for i := 1 to 6 do begin if n = 1 then pt[i,1] := pnt[i,1] else pt[i,1] := - pnt[i,1]; pt[i,2] := pnt[i,2]; end; for i := 1 to 6 do begin pt[i,1] := xc + pt[i,1]; pt[i,2] := yc - pt[i,2]; end; Canvas.Polygon([ Point(pt[1,1],pt[1,2]), Point(pt[2,1],pt[2,2]), Point(pt[3,1],pt[3,2]), Point(pt[4,1],pt[4,2]), Point(pt[5,1],pt[5,2]), Point(pt[6,1],pt[6,2]) ] ); end; end;
補足説明: 垂直軸(y軸)に関して対称移動するには、xの値の符号を反転すればよい。y座標の値はそのままである。
for i := 1 to 6 do begin
if n = 1 then pt[i,1] := pnt[i,1]
else pt[i,1] := - pnt[i,1];
pt[i,2] := pnt[i,2];
end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
9−4 原点に関して対称移動
下図のように各頂点の座標が与えられた多角形と、それを原点に関して対称移動した図を描く。
プログラミング:
var pnt, pt : array [1..4,1..2] of integer; i, n, xc, yc, x1, y1 : integer; begin pnt[1,1] := 0; pnt[1,2] := 0; pnt[2,1] := 50; pnt[2,2] := 50; pnt[3,1] := 50; pnt[3,2] := 150; pnt[4,1] := 0; pnt[4,2] := 100; xc := 200; yc := 200; for n := 1 to 2 do begin for i := 1 to 4 do begin if n = 1 then begin pt[i,1] := pnt[i,1]; pt[i,2] := pnt[i,2]; end else begin pt[i,1] := - pnt[i,1]; pt[i,2] := - pnt[i,2]; end; end; for i := 1 to 6 do begin pt[i,1] := xc + pt[i,1]; pt[i,2] := yc - pt[i,2]; end; Canvas.Polygon([ Point(pt[1,1],pt[1,2]), Point(pt[2,1],pt[2,2]), Point(pt[3,1],pt[3,2]), Point(pt[4,1],pt[4,2]) ] ); end; end;
補足説明: 原点対称の図形を描くには、x座標、y座標の各値の符号を反転する。
if n = 1 then begin
pt[i,1] := pnt[i,1];
pt[i,2] := pnt[i,2];
end
else begin
pt[i,1] := - pnt[i,1];
pt[i,2] := - pnt[i,2];
end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
左図に示す図形を水平軸、垂直軸、原点に関して対称移動を行い、右にあるような図形を描く。
プログラミング:
procedure TForm1.Button1Click(Sender: TObject); var pnt, pt : array [1..4,1..2] of integer; i, n, xc, yc, x1, y1 : integer; begin pnt[1,1] := 0; pnt[1,2] := 0; pnt[2,1] := 50; pnt[2,2] := 50; pnt[3,1] := 50; pnt[3,2] := 150; pnt[4,1] := 0; pnt[4,2] := 100; xc := 200; yc := 200; for n := 1 to 4 do begin for i := 1 to 4 do begin if n = 1 then //( もとの図形 ) begin pt[i,1] := pnt[i,1]; pt[i,2] := pnt[i,2]; end else if n = 2 then //( 水平軸に関して対称な図形 ) begin pt[i,1] := pnt[i,1]; pt[i,2] := - pnt[i,2]; end else if n = 3 then //( 垂直軸に関して対称な図形 ) begin pt[i,1] := - pnt[i,1]; pt[i,2] := pnt[i,2]; end else if n = 4 then //( 原点に関して対称な図形 ) begin pt[i,1] := - pnt[i,1]; pt[i,2] := - pnt[i,2]; end; end; for i := 1 to 6 do begin pt[i,1] := xc + pt[i,1]; pt[i,2] := yc - pt[i,2]; end; Canvas.Polygon([ Point(pt[1,1],pt[1,2]), Point(pt[2,1],pt[2,2]), Point(pt[3,1],pt[3,2]), Point(pt[4,1],pt[4,2]) ] ); end; end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
左図に示す図形を原点の回りに回転させて右にあるような図形を描く。
基本的な考え方: もとの図形を90度ずつ回転させて移動後の図形を順に描いていく。
原点回りの回転は以下の式を用いて行うことが出来る。
プログラミング:
procedure TForm1.Button1Click(Sender: TObject); const pi = 3.141592; var pnt, pt : array [1..6,1..2] of real; pt_int : array [1..6, 1..2] of integer; i, n, xc, yc, x1, y1 : integer; angle, th : real; begin pnt[1,1] := 0; pnt[1,2] := 0; pnt[2,1] := 50; pnt[2,2] := 50; pnt[3,1] := 50; pnt[3,2] := 150; pnt[4,1] := 0; pnt[4,2] := 100; pnt[5,1] := -50; pnt[5,2] := 150; pnt[6,1] := -50; pnt[6,2] := 50; xc := 200; yc := 200; angle := 0; for n := 1 to 4 do begin th := pi * angle / 180; for i := 1 to 6 do begin pt[i,1] := pnt[i,1] * cos(th) - pnt[i,2] * sin(th); pt[i,2] := pnt[i,1] * sin(th) + pnt[i,2] * cos(th); end; for i := 1 to 6 do begin pt_int[i,1] := xc + round(pt[i,1]); pt_int[i,2] := yc - round(pt[i,2]); end; Canvas.Polygon([ Point(pt_int[1,1],pt_int[1,2]), Point(pt_int[2,1],pt_int[2,2]), Point(pt_int[3,1],pt_int[3,2]), Point(pt_int[4,1],pt_int[4,2]), Point(pt_int[5,1],pt_int[5,2]), Point(pt_int[6,1],pt_int[6,2]) ] ); angle := angle + 90; end; end;
補足説明1: 三角関数(sin,cosなど)の計算に関係する変数は実数(real)として定義しておく。
var pnt, pt : array [1..6,1..2] of real;
angle, th : real;
補足説明2: 作図時の画面上の位置を表す変数は整数として定義しておく。
pt_int : array [1..6, 1..2] of integer;
もとの座標が実数で与えられているときには、次のようにround命令を使って、作図のための座標値を整数化しておく。
for i := 1 to 6 do begin
pt_int[i,1] := xc + round(pt[i,1]);
pt_int[i,2] := yc - round(pt[i,2]);
end;
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
上述のように、原点回りの回転の計算は以下の式を用いて行うことができる。
さらに、原点回りに回転した後に、平行移動を行う座標変換の式は、移動先の点の座標をとすると、次のように与えられる。
以下では、これらの式を利用して楕円を使った模様を描いてみる。
9−7−1 楕円を用いて模様を描く
長径、短径がそれぞれ、a=150,b=20の楕円を0度から180度まで、原点の回りに30度ずつ傾けながら描く。
作図結果:
プログラム
(先頭部分省略)
procedure TForm1.Button1Click(Sender: TObject); const pi = 3.1415; var x, y, xt, yt, a, b, incr, th, angle, th2, angle2: real; i, j, n, xc, yc, xi, yi : integer; begin n := 200; a := 150; b := 15; xc := 300; yc := 200; incr := 360 / n; angle2 := 0;//( 図形の傾き角の初期化 ) for j := 1 to 6 do begin th2 := pi * angle2 / 180; //( 度からラジアンへの変換 ) angle := 0; //( 楕円上の点の位置の初期化 ) for i := 0 to n do begin th := pi * angle / 180; //( 度からラジアンへの変換 ) x := a * cos(th); y := b * sin(th); xt := x * cos(th2) - y * sin(th2); yt := x * sin(th2) + y * cos(th2); xi := round(xc + xt); yi := round(yc - yt); if ( i = 0 ) then begin Canvas.MoveTo(xi,yi) end else begin Canvas.LineTo(xi,yi); end; angle := angle + incr; end; angle2 := angle2 + 30; end; end; end.
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
9−7−2 単純に図形を「回転+移動」した結果を組み合わせていろいろな図形を作る
楕円の回転と移動:
楕円の回転と移動を行った結果の図形を組み合わせることによって、以下に示すように花びらに似た図形を作り出すことが出来る。
プログラム:
var Form1: TForm1; pi : real = 3.1415; xc , yc : integer; procedure draw_ellipse(xa:real;ya:real;a:real;b:real;r1:real;r2:real;m:integer); implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); begin xc := 60; yc := 300; draw_ellipse(100,100,30,5,50,20,40); xc := 230; yc := 300; draw_ellipse(100,100,30,5,40,20,40); xc := 400; yc := 300; draw_ellipse(100,100,30,5,50,20,20); end; procedure draw_ellipse(xa:real;ya:real;a:real;b:real;r1:real;r2:real;m:integer); var i,j, n_div,xp, yp: integer; th, delta, th2, delta2, x, y, xt, yt : real; begin n_div := 100; delta := 2 * pi / n_div; // draw circle th := 0; Form1.Canvas.MoveTo(xc+round(r2*cos(th)+xa),yc-round(r2*sin(th)+ya)); for i := 1 to n_div + 1 do begin Form1.Canvas.LineTo(xc+round(r2*cos(th)+xa),yc-round(r2*sin(th)+ya)); th := th + delta; end; // draw ellipses around the circle delta2 := 2 * pi / m; th2 := 0; for j := 1 to m do begin th := 0; for i := 1 to n_div + 1 do begin x := a * cos(th); y := b * sin(th); xt := x*cos(th2)-y*sin(th2); yt := x*sin(th2)+y*cos(th2); xp := xc+round(xt+xa+r1*cos(th2)); yp := yc-round(yt+ya+r1*sin(th2)); if i = 1 then Form1.Canvas.MoveTo(xp,yp) else Form1.Canvas.LineTo(xp,yp); th := th + delta; end; th2 := th2 + delta2; end; end; end.
補足説明1: 作図方法
長径a,短径bを持つ楕円の中心が、から一定の距離になるようにして、一定角度ずつ傾けて描いていく。
補足説明2: 手続きに渡す引数
procedure draw_ellipse(xa:real;ya:real;a:real;b:real;r1:real;r2:real;m:integer);
の各引数の内容は次のようになっている。
xa,ya:回転の中心座標
a,b:楕円の長径と短径
r1:回転の中心座標の位置から楕円の中心までの距離
r2:円の半径(任意)
m:描く楕円の個数
確認:最初に示した出力図は次のような引数を与えて描いた。図形との対応を各自確かめよ。
draw_ellipse(100,100,30,5,50,20,40);
draw_ellipse(100,100,30,5,40,20,40);
draw_ellipse(100,100,30,5,50,20,20);
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
上記の関数で次のような指定を行って、同じ位置に異なる図形を重ねて描くと次のような図形が得られる。(長径と短径の値を等しくして、円を描いていることに注意)
xc := 200; yc := 300;
draw_ellipse(100,100,5,5,45,40,30);
xc := 200; yc := 300;
draw_ellipse(100,100,30,7,80,35,60);