配列、座標変換その2
 

   

 今回の目標:
  1. 配列、参照型
  2. メソッドその2(パラメータの受け渡し法)
  3. 座標変換その2(拡大/縮小、回転、対称移動、平行移動)


 配列

 配列とは英語の "array" の訳語であり、array は「ずらりと並んだもの」という意味の語である。ほとんどすべてのプログラミング言語は配列を使えるような言語仕様になっており、その名の通り、添字(そえじ)を付けた変数を「ずらりと並べたもの」である。

  1. 添字は 0 から始まる連続する整数値であり、上限値は配列を定義するときに指定する。例えば、
          int[] a = new int[10];   // この場合、添字の上限は 10-1=9 になる。 
    
    のように書く。a は配列の名前である。
  2. 配列につける名前を配列名といい、配列名が a のとき、a の添字は
           a[添字を表す式]、  例えば a[5] とか a[2n+1]
    
    のように書く。これを添字付き変数 という。
  3. 添字を表す式は、定数であってもよいし変数だけであってもよいし、一般の式であってもよい。ただし、添字を表す式の値は 1. で指定した範囲内の整数値でなければならない。
  4. 一群の添字付き変数
          a[0], a[1], a[2], ・・・, a[上限値]
    
    (これらは、その配列の配列要素ともいう)は、コンピュータ内部のメモリ上でも連続した領域に割り当てられる。
  5. a は、これら一群の添字付き変数全体(配列 a)の名前(配列名)である。
  6. これらの添字付き変数はどれも同じデータ型の値だけを取る。どのようなデータ型の値をとるかは配列を定義するときに指定する(1. の例では int 型)。
● 配列についての詳細は「Javaの基本(7) 配列 を参照のこと。


 メソッド

 メソッドの概略については前回学んだ。普通数学では、x,y をパラメータとする関数 f を定義するときには f(x,y) のように書くが、関数に相当するものであるメソッドを定義するときに Java ではどのように書くのであろうか? 今回は、その書き方と、そうして定義されたメソッドを使う(呼び出す)ときの書き方について学ぶ。

● メソッドについての詳細は「Javaの基本(8) メソッド(その1)」 を参照のこと。

 メソッドを呼び出すとき、呼び出す側と呼ばれる側の間でデータを遣り取りする手段として(たいていのプログラミング言語が持っている)代表的なものは次の2つである(それ以外に、大域的変数(クラスのフィールド)を用いるやり方もある)。

  1. 値呼び出し(call by value)
  2. 参照呼出し(call by reference, call by address)

 値呼び出しは、

 一方、参照呼出し

 繰り返すが、配列は参照型変数である

● 参照型についての詳細は「Javaの基本(12) 参照 を参照のこと。


(例) 配列名は参照型

    public static void mult(int[] a, int n) {  // @ a は int  型の配列(仮引数が配列の場合はこのように書く)
        for (int i=0; i<a.length; i++) 
            a[i] *= n;                         // A
    }

    public static void main(String[] args) {   // args は String 型の配列 
        int[] b = {0, 10, 20};
        System.out.println(b[0]+b[1]+b[2]);    // B 30 が出力される
        mult(b, b[2]);                         // C
        System.out.println(b[0]+b[1]+b[2]);    // D 60 が出力される
    }  

 例えば、mult の定義@において、1番目の仮引数は配列の宣言 int[ ] a であり、それを呼び出しているCにおいて、対応する1番目の実引数 b は配列名になっていることに注意する(配列名は参照型の変数である)。
 mult の呼び出しCにおいて、第1引数 b が参照呼出しであるのに対し、第2引数 b[2] は値呼出しであることに注意する。よって、Cが実行されると、@の ab がコピーされ、Aが実行されると自動的に b の値も変わることになるから、Bの時点では b[0]=0, b[1]=10, b[2]=20 であったものが、Dの時点では b[0]=b[1]=b[2]=20 となり、その結果Cでは 60 が出力される。

 メソッドは通常は値(return 文によって返される値)を1つしか返せないが、参照渡しを使うことにより、複数の値を返す手段としても使える。 すなわち、参照渡しにおいては、呼ばれたメソッド内で仮引数の値が変化すると、呼んだ側の対応する実引数の値も変化することを利用するのである。



 座標変換

 グラフィックス画面の座標を通常の xy-座標に変換する方法については第3回ですでに述べ、以後、活用している。また、極座標についてもすでに第6回で扱った。

(a) 回転

原点を中心に点 (x, y) を反時計回りにθラジアン回転させると、新しい座標 (x', y') は

で与えられる。

(b) 対称変換

点 (x, y) を x 軸 (y 軸) に関して対称移動すると、新しい座標 (x', y') は

で与えられる。

(c) 拡大/縮小(スケーリング)

点 (x, y) の x 成分を s 倍、y 成分を t 倍すると、新しい座標 (x', y') は

で与えられる。

(a), (b), (c) を一般化し、


で与えられる変換を一次変換という。原点は原点に、直線は直線に、平行な直線は平行な直線に変換され、直線上の点の比は変わらない。

(d) 平行移動

点 (x, y) を x 軸の正方向に a、y 軸の正方向に b 移動させると、新しい座標 (x', y') は

で与えられる。一般に

で与えられる変換をアフィン変換という。一次変換に平行移動を加えたものである。

(a’) 任意の点を中心とする回転

(a, b) を中心に点 (x, y) を反時計回りにθラジアン回転させると、新しい座標 (x', y') は

で与えられる。すなわち、まず、点 (x, y) を原点に平行移動し(x 軸の負の方向に a、y 軸の負の方向に b 移動させ)、原点でθラジアン回転させ、再び平行移動して元の位置に戻す(x 軸の正の方向に a、y 軸の正の方向に b 移動させる)。

(b’) 任意の直線を対称軸とする対称変換

直線 y = ax + b を対称軸として (x, y) を対称移動させると、新しい座標 (x', y') は

で与えられる。

(c’) 任意の点を中心とする拡大/縮小(スケーリング)

(a, b) を中心として点 (x, y) を s 倍に拡大/縮小すると、新しい座標 (x', y') は

で与えられる。x 成分を s 倍、y 成分を t 倍する場合も同様。


課題7でやること



1.課題7のためのディレクトリ

    ~/public_html/infomath6/

2.ギザギザ葉の花を描く

 今回は、このような ギザギザの葉を持つ花 を描いてみよう。これには、元になるギザギザの葉の 上半分 を描き、これに対称変換を施して このような 1枚の葉 とする。さらに、これを拡大/縮小して回転して平行移動したものを3つ描き、それに花と茎をつけると これ が描ける。

  1. まず、基礎となる 葉の上半分の輪郭 を描くことから始める(ソースコードは ここ)。 葉のギザギザの尖端の座標を2つの配列
            final int[] x = {0,12,10,20,18,30,25,40,34,44,41,47,45,50};
            final int[] y = {0, 9, 5,10, 7,12, 5,10, 5, 6, 3, 4, 2, 0};
            int N = x.length;  // 葉の尖端の個数
    
    で与えることにする。(x[i], y[i]), 1≦i≦N, が尖端の座標である。これらの配列の長さ N は尖端の個数である。尖端を
          (x[0], y[0]), (x[1], y[1]), (x[2], y[2]), ..., (x[n], y[n]) 
    
    の順に線分で結んだものが これ である。
  2. 次に、1. で描いたギザギザ曲線を倍率(スケーリング)を徐々に小さくしながら描くと ギザギザ葉の上半分 が得られる(ソースコードは ここ)。
  3. 2. で描いた葉と、それを x 軸に関して対称移動したものを合わせて描くと、このような1枚の葉 になる(ソースは これ)。
  4. 次に、3. で描いた葉を回転させる。葉の上半分だけを30度回転したものが これ(葉の輪郭)これ(葉の内部も塗り潰したもの) (2. と同じ方法で描く)である。 実は、この方法には問題がある。実際、回転角を60度にして描いてみると問題点が明瞭になる。このように(輪郭線を描いた場合内部を塗り潰した場合)なってしまうのである。なぜか?
  5. 葉の下半分も 4. と同じ方法で描いて1枚の葉とし、さらにスケーリングで縮小したものが これ である。実は、この場合にも問題がある。4. と同じ方法で描いた葉の上半分と下半分が(x 軸に関しては対称になっていたが)回転後の軸に関して対称になっていない。さらに、スケーリングを原点を中心にして行なっていることも問題である(なぜか? それは次回の課題で明らかになる)。
  6. 以上で茎の右に付いている葉の1枚が描けた。これと対になる茎の左側の葉は、y 軸に関して対称なものを描けばよい。こうして描いた1対の葉を葉の大きさと茎からの角度を変えて3対描いたものが これ である。大雑把な花弁もつけてみた。
  7. 4., 5. の問題については ここ に詳しい説明があるが、現段階では気にしなくてもよい(後程あらためて学ぶ)。
 今回の課題は配列を使うことにあるので、配列を使って、例題とは違う葉や花などを描いてみよう。
 完成した Java のソースファイルを task7.java とし、それを表示するための html ファイルを task7.html とし、ディレクトリ ~/public_html/infomath6/ に置くこと。

3.プラスα

  1. 回転とスケーリングも行い、 このような花 にしてみよう。他の工夫は?
  2. 回転を記述するために、配列 x, y で表された点(ギザギザ葉の尖端の x 座標と y 座標)の集合を theta ラジアンだけ反時計回りに回転させて得られる点の集合を配列 xx, yy に書き込むメソッド rotate を定義して使え:
        void rotate(double[] x, double[] y, double theta, double[] xx, double[] yy) {
    
             ここを完成させること
    
        }