SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

サーバサイドではじめるJavaグラフィック講座

Graphics2Dによる描画

サーバサイドではじめるJavaグラフィック講座 2


アフィン変換のポイント

 アフィン変換のポイントは、「Graphics2Dに用意されている変換用メソッドによる座標軸の操作」と、「java.awt.geom.AffineTransformクラスによる変換情報の作成」の2点となります。AffineTransformクラスは、変換に関する行列情報をまとめたもので、これは変換行列の個々の値を手作業で設定して作ることもできますし、あらかじめ用意されているメソッドを使い、平行移動・回転・拡大縮小のためのインスタンスを簡単に用意することもできます。

 では、アフィン変換の流れについてざっと整理してみましょう。

  1. まず、Graphics2Dに設定されているAffineTransformを変数などに保管します。これは、初期状態のAffineTransformのバックアップです。元の状態に戻すのに必要です。
  2. 変換のためのAffineTransformインスタンスを作成します。これは通常、AffineTransformに用意されているメソッドを使ってインスタンスを生成します(これは、用意しなくともいい場合もあります)。
  3. Graphics2Dに用意されている変換用メソッドを呼び出して座標軸変換を行います。これは、単に数値を指定するだけで行なえるものと、AffineTransformインスタンスを適用して行うものがあります。
  4. 描画を行います。変換された座標軸に従って図形が描画されます。
  5. すべての描画が終わったら、バックアップしてあったAffineTransformを使い、座標軸を元に戻します。

 作成したサンプルでは、座標軸の平行移動にAffineTransformを適用する方法を使い、座標軸の回転に直接数値で変換操作を行うメソッドを使う方法を使っています。これは、別に「平行移動と回転でやり方が違う」ということではなく、2通りの操作のサンプルとして挙げただけで、平行移動・回転・拡大縮小ともにいずれの方法でも行うことができます。――では、サンプルのソースコードを見てみましょう。

AffineTransform defaultAT = g.getTransform();

 まず、デフォルトのAffineTransformインスタンスをバックアップします。Graphics2DAffineTransformは、getTransformというメソッドで取得することができます。

g.setTransform(AffineTransform.getTranslateInstance(127, 127));

 座標軸を平行移動しています。ここでは、AffineTransformインスタンスを設定する方式で操作をしています。AffineTransformにある「getTranslateInstance」により、平行移動のためのインスタンスを作成し、それを「setTransform」でGraphics2Dに設定しています。AffineTransformには、平行移動・回転・拡大縮小のためのインスタンスを生成する次のようなメソッドが用意されています。

平行移動
getTranslateInstance(double dx, double dy)
回転
getRotateInstance(double theta)
アンカーポイントを指定した回転
getRotateInstance(double theta, double x, double y)
拡大縮小
getScaleInstance(double sx, double sy)

 回転が2種類ありますが、1つはゼロ座標を中心とした回転、もう1つはアンカーポイントとなる座標を指定し、その地点を中心とした回転のためのものです。これらのメソッドを使ってAffineTransformインスタンスを取得し、それをsetTransformすればよいわけです。

 さて、もう1箇所、変換を行なっているところがあります。for内にある、座標軸を回転させている変換部分です。

g.rotate(Math.PI / 8);

 こちらはAffineTransformインスタンスを使わず、Graphics2Dに用意されている変換用メソッドを呼び出して行なっています。Graphics2Dには、座標軸変換を行うメソッドとして次のようなものが用意されています。

平行移動
translate(double tx, double ty)
回転
rotate(double theta)
アンカーポイントを指定した回転
rotate(double theta, double x, double y)
拡大縮小
scale(double sx, double sy)

 これらは、ただ単にメソッドを呼び出すだけで使えてしまいます。これらのメソッドは、先ほどのsetTransformと同じような働きをします。どちらを使っても基本的には同じですから、その時々で使いやすいほうを使えばよいでしょう。

 こうして座標変換を行なって描画を行い、すべて終わったら、最後に座標軸をバックアップの状態に戻します。

g.setTransform(defaultAT);

 これですべての作業は完了です。意外と簡単でしょう? 座標軸のバックアップ関係は、しなくても何かに影響が出ることはありません。その後でまた描画などを行うような場合には、予想しなかった形で描画されてしまうと困るので「変換したら最後に元に戻す」というのを習慣づけておく、ということでしょう。

 実際にサンプルを見れば分かることですが、このアフィン変換は「変換がどんどん蓄積されていく」という点に注意してください。例えば「右に100移動する」という変換を2度適用すると、座標軸は右に200移動した状態になります。変換した座標軸にさらに変換が適用されていくわけです。

 また、Graphics2Dの座標軸そのものを変更するわけですから、Shapeによる描画に限らず、旧来の描画用メソッドも含めて、すべての描画がこの影響を受けます。

 この座標軸変換は、組み合わせていくことで非常に複雑な図形を簡単に描くことができます。特に、回転を組み合わせた描画は、すべて座標を計算して描画することを考えるとずいぶんと楽です。もう少し複雑なサンプルをあげておきましょう。

public void paint(Graphics2D g){
    g.setColor(new Color(0,0,0));
    g.fillRect(0, 0, 255, 255);
    Ellipse2D.Double elps = new Ellipse2D.Double(0d,0d,25d,25d);
    g.setStroke(new BasicStroke(1.5f));
    AffineTransform defaultAT = g.getTransform();
    g.setTransform(AffineTransform.getTranslateInstance(127, 127));
    AffineTransform tempAT = g.getTransform();
    for(int i = 0;i < 32;i++){
        g.setTransform(tempAT);
        g.rotate(-Math.PI / 8 * i);
        g.scale(1d - (1d / 32 * i), 1d - (1d / 32 * i));
        g.transform(AffineTransform.getTranslateInstance(64, 64));
        for(int j = 0;j < 16;j++){
            Color c = new Color(i * 8,0,255 - i * 8);
            g.setColor(c);
            g.draw(elps);
            g.rotate(Math.PI / 8);
        }
    }
    g.setTransform(defaultAT);
}
アフィン変換を組み合わせると、かなり複雑な図形も簡単に描ける。
アフィン変換を組み合わせると、かなり複雑な図形も簡単に描ける。

 回転して表示される円が縮小しながらスパイラルを描いていきます。これを、座標変換を使わずに描くとなると、少々面倒くさそうです。こうした「決まった手順に沿って位置や大きさ、向きなどを変更しながら描画していく」という方式では、座標変換は威力を発揮するのです。

次のページ
塗りつぶしと線分情報

この記事は参考になりましたか?

サーバサイドではじめるJavaグラフィック講座連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1021 2007/03/28 12:44

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー