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;

              →解説へ

    ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

9−2 水平軸(x軸)に関する対象移動

上記の例題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;

    ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

9−3 垂直軸(y軸)に関して対称移動

下図のように各頂点の座標が与えられた多角形と、それを垂直軸(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;

    ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

9−5 対称移動の組み合わせ

左図に示す図形を水平軸、垂直軸、原点に関して対称移動を行い、右にあるような図形を描く。

      

プログラミング:


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;

    ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆

9−6 原点回りの回転移動

左図に示す図形を原点の回りに回転させて右にあるような図形を描く。

     

  基本的な考え方:  もとの図形を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 図形の回転と平行移動

上述のように、原点回りの回転の計算は以下の式を用いて行うことができる。

                 

さらに、原点回りに回転した後に、平行移動を行う座標変換の式は、移動先の点の座標をとすると、次のように与えられる。

                     

以下では、これらの式を利用して楕円を使った模様を描いてみる。

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);