Go to Contents Go to Java Page

実験室

 
 

グラフィックの座標系 AffineTransform

 
 

AffineTransform とは

 
 

Java 2 では AWT に加えて、JFC (Java Foundation Classes) が加わりました。JFC の中でも Swing が注目されがちですが、2 次元グラフィックスや印刷関連の API の大幅に拡充されました。

そこで、あまり日のあたることのない 2 次元グラフィックス API の Java2D を取り上げてみましょう。

なるべく、簡単な例でいろいろ試してみてみましょう。

まず第一弾として java.awt.geom.AffineTransform クラスを極めてみましょう。

Java 2D では、 2 つの座標系があります。1 つがデバイス空間、他方がユーザ空間です。デバイス空間は文字通りコンピュータで表示できる物理的な座標系です。VGA であれば 640 × 480 pixels の空間になります。

他方のユーザ空間はユーザが自由に決めることができる空間です。

この 2 つの座標系を結びつけるのが AffineTransform クラスです。ユーザはユーザ空間をデバイス空間というのぞき窓からのぞき見るような感じですね。このときにただのぞき見るのではなく、拡大や縮小、回転などを行ってしまうのが AffineTransform クラスです。

デバイス空間とユーザ空間
図 1 デバイス空間とユーザ空間

まあ、むずかしい話はここらへんにしておいて、AffineTransform クラスを使ってみましょう。

まず、基本となる Applet を作ってみました。単に 2 つの四角形を描く Applet です。Java 2 を使用しているので、Netscape 6 以外のブラウザーであれば Java Plug-in が必要です。

Applet の HTML AffineTransformTest1.html
Applet のソース AffineTransformTest1.java

Java 2D で描画をするときも、今までと同じように paint メソッドで記述します。異なるのは paint メソッドの引数である Graphics オブジェクトを Graphics2D にキャストすることです。これだけで、Java 2D を使用することができます。Gaphics2D クラスは Graphics クラスの派生クラスなので、Graphics クラスの機能はすべて受けついでいます。

Graphics2D で描画を行うときは draw メソッドもしくは fill メソッドを使います。前者が単に描画を行い、後者は塗りつぶしを行います。

draw メソッド、fill メソッドとも引数は java.awt.Shape オブジェクトとなっています。Shape オブジェクトは幾何図形を表したインタフェースです。これをインプリメントしたものに長方形をあらわす java.awt.geom.Rectangle2D クラスなどがあります。

AffineTransformTest1.java では 2 つの長方形を描画しています。

まず、init メソッドで 2 つの長方形を生成します。

    private Shape rect1;
    private Shape rect2;
 
    public void init(){
        setBackground(Color.WHITE);
		
        rect1 = new Rectangle2D.Double(0, 0, 500, 400);
        rect2 = new Rectangle2D.Double(200, 100, 200, 150);
    }

Rectangle2D クラスは精度の違いにより Rectangle2D.Double クラスと Rectangle2D.Floag クラスがありますが、ここでは Rectangle2D.Double クラスを使いました。コンストラクタの引数は始点の x 座標、y 座標、幅、高さです。

paint メソッドは単に rect1, rect2 を描画するだけです。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.draw(rect1);
        g2D.draw(rect2);
    }

実行してみると、単に 2 つの四角形を描画するだけです。

デフォルト状態ではデバイス空間とユーザー空間は同一になっています。

 

 
 

回転

 
 

まずは単純に AffineTransform クラスで座標系の変換をしてみましょう。ここでは座標軸を回転させてみます。

Applet の HTML AffineTransformTest2.html
Applet のソース AffineTransformTest2.java

AffineTransorm クラスは普通にコンストラクタを使用して生成することもできますが、生成のための static メソッドも用意されています。今回はそれを使ってみました。AffintTransform オブジェクトも四角形と同じように init メソッドで生成します。

    private Shape rect1;
    private Shape rect2;
    private AffineTransform rotate;
 
    public void init(){
        setBackground(Color.WHITE);
 
        rect1 = new Rectangle2D.Double(0, 0, 500, 400);
        rect2 = new Rectangle2D.Double(200, 100, 200, 150);
        rotate = AffineTransform.getRotateInstance(Math.toRadians(45), 0.0, 0.0);
    }

この AffineTransform オブジェクトは (0, 0) を中心にして 45°の回転を行うものです。このほかにも次のような生成のためのメソッドがあります。

(0, 0) を中心にした回転 getRotateInstance(double theta)
任意の点を中心にした回転 getRotateInstance(double theta, double x, double, y)
拡大、縮小 getScaleInstance(double sx, double sy)
シャーリング (傾き) getShearInstance(double shx, double shy)
平行移動 getTranslateInstance(double tx, double ty)

それでは描画してみましょう。座標変換を行うには Graphics2D#setTransform メソッドを使用します。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.setTransform(rotate);
 
        g2D.draw(rect1);
        g2D.draw(rect2);
    }

実行させるとどうなりましたか。長方形が回転して描画されているのが確認できたでしょうか。

ユーザ空間を回転させるということがどういうことかを図 2 で示してみました。

ユーザ空間の回転
図 2 ユーザ空間の回転

デバイス空間というのぞき窓の位置は変わらないため、ユーザ空間とデバイス空間が回転によってずれてしまうわけです。

 

 
 

複数のユーザ空間

 
 

次は、複数のユーザ空間を設定できるかどうかについてです。前章と同様に回転変換で試してみます。

Applet の HTML AffineTransformTest3.html
Applet のソース AffineTransformTest3.java

長方形 rect1 オブジェクトはデフォルトのユーザ空間、rect2 が回転変換をほどこされるユーザ空間に描画します。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.draw(rect1);              // デフォルトのユーザ空間 (=デバイス空間)
 
        g2D.setTransform(rotate);     // ユーザ空間に回転をほどこす

        g2D.draw(rect2);
    }

これを実行すると、rect1 はそのまま描画され、rect2 は回転した形で描画されます。

ということは、次のようなことがいえます。

  • 常に AffineTransform はいつでもユーザ空間に対して変換を行うことができる
  • AffineTransform をほどこす前のユーザ空間に描画されたものは、AffineTransform によって変換されない

 

 
 

ユーザ空間にほどこされた変換をもとにもどす

 
 

座標変換されてしまったユーザ空間は元に戻せるかやってみましょう。

Applet の HTML AffineTransformTest4.html
Applet のソース AffineTransformTest4.java

ユーザ空間を元に戻すには、g2D オブジェクトにセットされた AffineTransform オブジェクトをチャラにしてしまえばいいと考えられます。そこで、ためしに setTransform メソッドの引数に null を入れてみましょう。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.setTransform(rotate);     // ユーザ空間に回転をほどこす
        g2D.draw(rect1);              // デフォルトのユーザ空間 (=デバイス空間)
 
        g2D.setTransform(null);     // ユーザ空間に元に戻す

        g2D.draw(rect2);
    }

これを実行すると、g2D.setTransform(null) のところで NullPointerException が発生してしまいました。

null がだめだとすると、なにもしない AffineTransform オブジェクトを引数にしてみましょう。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.setTransform(rotate);     // ユーザ空間に回転をほどこす
        g2D.draw(rect1);              // デフォルトのユーザ空間 (=デバイス空間)
 
        g2D.setTransform(new AffineTransform());     // ユーザ空間に元に戻す

        g2D.draw(rect2);
    }

引数がないコンストラクタを使用して、AffineTransform オブジェクトを生成すると、なにも変換しないオブジェクトが生成されます。これを setTransform の引数にしてみました。

実行してみると、期待通りユーザ空間は元に戻りました。

これからは、次のようなことがわかりました。

  • 変換前のユーザ空間に戻すには変換前の AffineTransform オブジェクトを setTransform メソッドで指定する
  • setTransform メソッドには null を入れることはできない

 

 
 

AffineTransform を使わない座標変換

 
 

Graphics2D クラスの API を見ていると、下に示したようなメソッドがあります。これらのメソッドを用いても座標変換ができそうです。

rotate(double theta) (0, 0) を中心に theta 回転する
rotate(double theta, double x, double y) (x, y) を中心に theta 回転する
scale(double sx, double sy) x 軸方向に sx, y 軸方向に sy だけ拡大 (縮小) する
shear(double shx, double shy) シャーリング (傾き)
translate(double tx, double ty) x 軸方向に tx, y 軸方向に ty だけ平行移動する
translate(int x, int y) ユーザ空間の原点を (x, y) に平行移動する

さっそく、試してみましょう。

Applet の HTML AffineTransformTest5.html
Applet のソース AffineTransformTest5.java

これまで、AffineTransform オブジェクトを生成していましたが、直接 Graphics2D オブジェクトに対して回転変換をセットします。

    public void paint(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
 
        g2D.draw(rect);
 
        g2D.rotate(Math.toRadians(45.0));
        g2D.setPaint(Color.red);
        g2D.draw(rect);
    }

したがって、次のようなことがわかりました。

  • AffineTransform クラスを直接使用することなく、Graphics2D クラスのメソッドによっても座標変換は可能

(Apr. 2003)

 
 
Go to Contents Go to Java Page