回転について

広告

では次は回転についてです。回転の場合であっても自分で適切な値の変換行列を設定すればできるのですが、結構計算が面倒だと思いますので、用意されているメソッドを使います。平行移動の時と同様に回転に関しても2つのメソッドが用意されています。

まずsetToRotationメソッドから見てみます。

この変換を回転変換に設定します。この変換を表現する行列は次のようになり
ます。 

              [   cos(theta)    -sin(theta)    0   ]
              [   sin(theta)     cos(theta)    0   ]
              [       0              0         1   ]
 
正の角度 theta で回転すると、正の x 軸の点が正の y 軸に向かって回転さ
れます。 

パラメータ:
  theta - ラジアンで表した回転角度

ここで指定する角度の単位はラジアンです。角度の指定する時はjava.lang.Mathクラスで定義されているPIを使うと便利です。

java.lang.Math.PI = 180度

Math.PI / 2 = 90度
Math.PI / 3 = 60度
Math.PI / 4 = 45度
Math.PI / 6 = 30度

実際に簡単なサンプルを作成して試してみます。回転させ、その変換行列の値を標準出力に出すようにします。

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;

public class AffineTransformTest extends JPanel{
  public static void main(String[] args){
    JFrame frame = new JFrame();

    AffineTransformTest test = new AffineTransformTest();
    frame.getContentPane().add(test);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds( 0, 0, 200, 200);
    frame.setVisible(true);
  }

  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));

    AffineTransform af = new AffineTransform();

    double[] flatmatrix = new double[6];
    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    System.out.println();

    af.setToRotation(30 * Math.PI/180);

    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    g2.setTransform(af);
    g2.setColor(Color.red);
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));
  }

  public static void printMatrix(double[] flatmatrix){
    System.out.print("[ " + flatmatrix[0] + " ");
    System.out.print(flatmatrix[2] + " ");
    System.out.println(flatmatrix[4] + " ]");

    System.out.print("[ " + flatmatrix[1] + " ");
    System.out.print(flatmatrix[3] + " ");
    System.out.println(flatmatrix[5] + " ]");
  }
}

実行結果は下記のようになります。

[ 1.0 0.0 0.0 ]
[ 0.0 1.0 0.0 ]

[ 0.8660254037844387 -0.49999999999999994 0.0 ]
[ 0.49999999999999994 0.8660254037844387 0.0 ]

setToRotationの場合には、対象となる変換行列にどのような値が設定されていたとしても完全に新しい行列に置き換えてしまいます。

rotateメソッド

回転のもう1つのメソッドにrotateメソッドがあります。

この変換を回転変換に連結します。これは、concatenate(R) を呼び出すこと
に相当します。ただし、R は次の行列で表現される AffineTransform です。 

              [   cos(theta)    -sin(theta)    0   ]
              [   sin(theta)     cos(theta)    0   ]
              [       0              0         1   ]

正の角度 theta で回転すると、正の x 軸の点が正の y 軸に向かって回転さ
れます。

パラメータ:
  theta - ラジアンで表した回転角度

先ほどのsetToRotationの場合は変換行列を完全に置き換えていましたが、rotateの場合は置き換えではなく、既存の変換行列に新たに乗算を行い、その結果を新しい変換行列の値とします。

例えば既存の変換行列が下記左のような行列だった場合、rotateメソッドを実行することで、右の行列のような新しい変換行列になります。

 [ m00 m01 m02 ]    [ cos(t)  -sin(t)  0 ]
 [ m10 m11 m12 ] × [ sin(t)   cos(t)  0 ]
 [ m20 m21 m22 ]    [ 0        0       1 ]

    [ m00 * cos(t) + m01*sin(t)   m00 * (-sin(t)) + m01*cos(t)   m02 ]
 = [ m10 * cos(t) + m11*sin(t)   m10 * (-sin(t)) + m11*cos(t)   m12 ]
    [ m20 * cos(t) + m21*sin(t)   m20 * (-sin(t)) + m21*cos(t)   m22 ]

見ていただくと分かるとおり、rotateメソッドでは、実行前と後で変換行列の右側(平行移動に関する列)の列は影響を受けません。

では、これも簡単なサンプルで試してみます。

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;

public class AffineTransformTest extends JPanel{
  public static void main(String[] args){
    JFrame frame = new JFrame();

    AffineTransformTest test = new AffineTransformTest();
    frame.getContentPane().add(test);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds( 0, 0, 200, 200);
    frame.setVisible(true);
  }

  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));

    AffineTransform af = new AffineTransform();

    double[] flatmatrix = new double[6];
    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    System.out.println();

    af.setTransform(1.0d, 0.0d, 0.0d, 1.0d, 10.0d, 20.0d);
    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    g2.setTransform(af);
    g2.setColor(Color.red);
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));

    System.out.println();

    af.rotate(40 * Math.PI/180);

    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    g2.setTransform(af);
    g2.setColor(Color.blue);
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));
  }

  public static void printMatrix(double[] flatmatrix){
    System.out.print("[ " + flatmatrix[0] + " ");
    System.out.print(flatmatrix[2] + " ");
    System.out.println(flatmatrix[4] + " ]");

    System.out.print("[ " + flatmatrix[1] + " ");
    System.out.print(flatmatrix[3] + " ");
    System.out.println(flatmatrix[5] + " ]");
  }
}

実行結果は下記のようになります。

[ 1.0 0.0 0.0 ]
[ 0.0 1.0 0.0 ]

[ 1.0 0.0 10.0 ]
[ 0.0 1.0 20.0 ]

[ 0.766044443118978 -0.6427876096865393 10.0 ]
[ 0.6427876096865393 0.766044443118978 20.0 ]

もう1つのsetToRotationメソッド

最初に見てみたsetToRotationメソッドには引数が違うもう1つのメソッドが用意されています。

この変換を平行移動後の回転変換に設定します。このオペレーションは、座標
をアンカーポイントが原点 (S1) になるように移動し、これらの座標を新しい
原点 (S2) を中心に回転してから、最後にその仲介的な原点を元のアンカーポ
イント (S3) の座標に戻すために移動することに相当します。 

この操作は、次の一連の呼び出しに相当します。 

      setToTranslation(x, y);     // S3: final translation
      rotate(theta);              // S2: rotate around anchor
      translate(-x, -y);          // S1: translate anchor to origin
 
この変換を表現する行列は次のようになります。 

             [   cos(theta)    -sin(theta)    x-x*cos+y*sin  ]
             [   sin(theta)     cos(theta)    y-x*sin-y*cos  ]
             [       0              0               1        ]
 
正の角度 theta で回転すると、正の x 軸の点が正の y 軸に向かって回転さ
れます。

パラメータ:
  theta - ラジアンで表した回転角度
  x, y - 回転のアンカーポイントの座標

簡単に書いてしまうと、指定した点を中心として回転させたい場合などに使えます。

これも簡単なサンプルで試して見ましょう。最初の図形の真中を中心点として回転させてみます。

import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.*;

public class AffineTransformTest extends JPanel{
  public static void main(String[] args){
    JFrame frame = new JFrame();

    AffineTransformTest test = new AffineTransformTest();
    frame.getContentPane().add(test);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds( 0, 0, 200, 200);
    frame.setVisible(true);
  }

  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));

    AffineTransform af = new AffineTransform();

    double[] flatmatrix = new double[6];
    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    System.out.println();

    af.setToRotation(45 * Math.PI/180, 90d, 40d);

    af.getMatrix(flatmatrix);
    printMatrix(flatmatrix);

    g2.setTransform(af);
    g2.setColor(Color.red);
    g2.draw(new Rectangle2D.Double(60, 20, 60, 40));
  }

  public static void printMatrix(double[] flatmatrix){
    System.out.print("[ " + flatmatrix[0] + " ");
    System.out.print(flatmatrix[2] + " ");
    System.out.println(flatmatrix[4] + " ]");

    System.out.print("[ " + flatmatrix[1] + " ");
    System.out.print(flatmatrix[3] + " ");
    System.out.println(flatmatrix[5] + " ]");
  }
}

実行結果は下記のようになります。

[ 0.7071067811865476 -0.7071067811865475 54.644660940672615 ]
[ 0.7071067811865475 0.7071067811865476 -51.923881554251174 ]

( Written by Tatsuo Ikura )