Comments
Description
Transcript
オブジェクト
Chapter 17. Java2Dライブラリ 17−1.Java2Dの特徴 Java2Dは、2次元のコンピュータグラフィックス用に特化されたクラスライブラリになっています。AWTの Graphicsオブジェクトでも、ある程度の描画ができましたが、それ以上に精度の高い描画を可能としていま す。一番の特徴は、オブジェクトとして、各図形を描画できるということです。オブジェクトが、図形の位置 や大きさ、色などの特性を覚えていますから、それを利用して、継続的に描画することができます。AWTの Graphicsオブジェクトを使ったこれまでの描画は、そのような特性を変数などを用意して、別に覚えていない といけませんでした。 もちろん、このようなグラフィックスのパラダイムは、最初の強力なオブジェクト指向言語であるSmalltalk と、その実行マシンであるAltoでは、既に実装されていました。Java言語も、Java2Dを持ってして、やっと そのパラダイムに近づいたと言えるでしょう。実は、AWTやSwingなどのクラスライブラリは、Java2Dを ベースに書き直されていますので、実は既にある程度のJava2Dの内容は使っているのですが、ここでは Java2Dとしての用意されている強力な描画機構を利用することに致します。 また、ベジェ曲線もアフィン変換もクラスとして用意されていますから、自分で折れ線で近似したり、行列の 積を計算しなくても、頂点だけ用意して、メソッドを呼び出すだけで描画したり、計算してくれます。さら に、単純なアフィン変換については、オブジェクトにメソッドが用意されていますので、それを呼び出すこと によって位置やサイズを自動的に変えることができます。 精密な描画な描画を期すために、Java2Dでは、実数ベースで座標を指定しています。整数ベースになってい ると、小数点を切り捨てる部分が出てきましたので、どうしても拡大・縮小、あるいは回転時に、端数が切り 捨てられ、精度が悪くなってしまいました。もちろん、整数で座標を指定することもできます。その場合、実 数から整数への変換は、自動的にやってくれます。また、座標値を整数で取得することもできます。なお、あ まり情報量を大きくしないため、多くは実数として単精度浮動小数点数が用いられています。Javaでは、通常 に実数を使った場合、倍精度になるため、単精度を指定するFや小文字のfが良く用いられます。また、型の強 制変換をするキャストの記述を使って、(float)なども良く用いれます。 Java2Dのその他のメリットとして幾つか紹介しましょう。まず、印刷への対応が挙げられます。メソッドを 呼び出すことで、画面への描画をプリンタへそのまま印刷できるようにもなっています。次に、読み込んだ画 像に対して、それをただ単に表示するだけでなく、画像処理が可能となりました。Photoshopのフィルタみた いな感じで、画像加工ができます。Mac OS Xでは、既にそうなっているので、あまり恩恵がないのですが、 SwingベースでJava2Dを使うと、ダブルバッファ導入によるちらつきの解消が行なえます。アニメーション などを行なった際に、ちらつきがないので、非常に見やすくなります。なぜ、恩恵がないかと言えば、Mac OS Xは、すべてがQuartzとライブラリで実装されており、ダブルバッファはその時点で基本的な描画手法と して実現されていますので、通常にプログラムを書いた時点で、ちらつきはまったく起こらないからです。 最後に、Java2Dはその考え方を、Java3Dへ発展させていきました。ここでは扱いませんが、これらの2つの ライブラリの間には連続性があります。Mac OS Xでは、Sun Microsystemsからやっとライセンスが取得で き、Java3DはPanther(10.3)から利用することができます。10.3以前のOSでは利用できないのは、技術 的な制約ではありませんから、たぶんライセンス関係の制約だと思われます。 Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 1 17−2.共通クラスとメソッド 17−2−1.オブジェクトとインスタンス変数への外部からの参照 クラスのインスタンスであるオブジェクトについて、ここではその利用の仕方をもう一度整理してみましょ う。ここでは、インスタンス変数を外部から参照する仕方について説明していきます。 オブジェクトの外部から、そのオブジェクトの持つインスタンス変数を参照するためには、次のようにオブ ジェクトを指定してからメソッドと同じようにドットで区切って指定します(注1)。 ▼インスタンス変数の参照の書式 オブジェクト.変数名 この参照の書式は、代入のときも、参照のときも用います。実際の例をAWTのクラスのオブジェクトで以降 に紹介していきましょう。 オブジェクトに対して、今までインスタンス変数を参照する例がほとんどなかったのはなぜなのでしょうか? それは、インスタンス変数がオブジェクトの状態を保持しているからです。オブジェクト指向のパラダイムで は、そのような状態を変更するのは、インスタンス変数を直接書き換えるよりも、オブジェクトが持つメソッ ドを通じて書き換えた方が良いとされています。外部に公開されているメソッドは変わることはなくても、プ ログラムを開発していく上で内部的な状態の表現の変更はされ得るからです。詳しくは次章で説明しましょ う。 ※注1 Java言語では、このように外部からオブジェクトのインスタンス変数を参照する場合、そのインスタ ンス変数のことを、オブジェクトのフィールド(Field)と呼んでいます。 17−2−2.共通オブジェクト インスタンス変数を参照する実際の例として、AWTやJava2Dの中に定義されている幾つかのクラスのオブ ジェクトの利用方法についてみていきましょう。以下に挙げるPoint、Dimension、Rectangleクラスのオブ ジェクトは、そのインスタンス変数が外部に公開されているものです。 ★Pointクラスのオブジェクト Pointクラスのオブジェクトは、2次元座標上の1つの点を表すために用いられます。そのため、このクラスの オブジェクトには、フィールド(インスタンス変数)として次のようなものがあります(注1)。 int x; // 点のx座標を表します int y; // 点のy座標を表します コンストラクタは、オブジェクトを作り出すためのメソッドですが、次のようなものがあります。 new Point( ) // 座標を指定しないで点を作成します(座標は原点になる) new Point( int x, int y ) // 座標を指定して点を作成します new Point( Point original ) // 別の点と同じ座標の点を作成します コンストラクタと、インスタンス変数を利用して、Pointクラスのオブジェクトを利用してみましょう。次の記 述は、(45,33)の座標を持つ点を表すオブジェクトを作りだし、その後、そのx座標とy座標に変更を加えていま す。 Point centerpoint = new Point( 45, 33 ); // 1つオブジェクトを作って変数で参照します centerpoint.x = centerpoint.x+ 34; Copyright by Tatsuo Minohara © 2003 Rev A. // centerpoint.x += 34; と記述しても構いません Macintosh Java Primer Chapter 17- 2 centerpoint.y = 50; これらの代入を行なった結果、centerpointのx座標は89、y座標は50となります。このように、いくつかの関連 する情報が1つのオブジェクトによって表されていると、そのインスタンス変数を操作するときに、対象とな るオブジェクトが指定されていますので、プログラムがわかりやすくなります。 また、Pointクラスのオブジェクトのメソッドには、次のようなものがあります。これらは、インスタンス変数 を操作するものです。インスタンス変数を直接操作するための替わりとして用いることができます。 boolean equals( Object another ) 別の点が等しい座標を持つかどうか調べます void setLocation( int x, int y ) 指定した座標に点を設定します void translate( int dx, int dy ) 差分の分だけ点の座標を移動します これらのメソッドに関して、それぞれ直接インスタンス変数を操作する方法と、メソッドを用いる方法で、プ ログラムの断片を記述してみましょう。次のような2つの点があるとしましょう。 Point mypoint = new Point( 10, 100 ), yourpoint = new Point( 20, 100 ); これらの点を用いて、それぞれインスタンス変数を用いた形とメソッドを用いた形で記述してみます。それぞ れ上の行がメソッドを用いたもので、下の行がインスタンス変数を直接操作するものです。 // オブジェクト指向版 if ( mypoint.equals( yourpoint2 ) ) { System.out.println( "Same Points." ); } if ( mypoint.x == yourpoint.x && mypoint.y == yourpoint.y ) { // フィールドの参照版 System.out.println( "Same points." ); mypoint.setLocation( 20, 30 ); // オブジェクト指向版 mypoint.x = 20; mypoint.y = 30; // フィールドの参照版 mypoint.translate( 5, -10 ); // オブジェクト指向版 mypoint.x += 5; mypoint.y -= 10; // フィールドの参照版 どちらも同じ記述なのですが、やはりメソッドを用いた方が若干わかりやすいのではないかと思われます。そ れもあり、オブジェクト指向プログラミング言語では、インスタンス変数を外部から操作する方法は、それほ ど用いられないのです。 ★Dimensionクラスのオブジェクト Dimensionクラスのオブジェクトは、矩形領域を表します。そのため次のような幅と高さをフィールド(イン スタンス変数)として保持しています。 int width; // 領域の幅を示します int height; // 領域の高さを示します また、このクラスのコンストラクタには、次のようなものがあります。 new Dimension( ) // 幅0、高さ0の領域を作ります new Dimension( int width, int height ) // 指定された幅と高さの領域を作ります new Dimension( Dimension original ) // 別の領域と同じ幅と高さの領域を作ります Pointクラスとほとんど同じような使い方をするためにメソッドも似通っていて、次のようなものがあります。 boolean equals( Object another ) // 別の領域と同じ幅・高さであるか調べます void setSize( int width, int height ) // 幅と高さを指定します Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 3 ★Rectangleクラスのオブジェクト Rectangleクラスは、PointクラスとDimensionクラスの両方の情報を持っています。このクラスのオブジェク トは、座標が固定された1つの矩形領域を示します。インスタンス変数としては、次のようなものが定義され ています。 int x; // 領域の左上の角のx座標を表します int y; // 領域の左上の角のy座標を表します int width; // 領域の幅を示します int height; // 領域の高さを示します コンストラクタには、次のようなものがあります。 new Rectangle( ) // 領域を作ります new Rectangle( int x, int y, int width, int height ) // 座標と幅・高さを指定して領域を作ります new Rectangle( Rectangle original ) // 別の領域の同じ領域を作ります メソッドには、次のようなものがあります。 boolean equals( Object another ) // 別の領域と同じ座標、幅・高さかどうか boolean contains( Point p ) // 指定した点が領域の中に含まれているか void setLocation( int x, int y ) // 左上の角の座標を設定して移動させます void setSize( int width, int height ) // 幅と高さを設定します void setBounds( int x, int y, int width, int height ) void translate( int dx, int dy ) // 指定した分だけ座標を移動させます void grow( int dw, int dh ) // 指定した分だけ幅と高さを変更します Point getLocation( ) // 座標値を得ます Dimension getSize( ) // 座標と幅・高さを設定します // 幅と高さを得ます ★メソッドの戻り値としてオブジェクトを利用する方法 上記のRectangleクラスのメソッドのように、Pointクラス、Dimensionクラス、Rectangleクラスのオブジェ クトは、メソッドの戻り値として使われます。メソッドが呼出し側に情報を返すときに、1つの情報しか返せ ないからです。複数の情報があるときに、それらを1つにまとめてオブジェクトとして返すのです。 たとえば、Rectangleクラスのオブジェクトを利用して、領域を管理するような場合を考えてみましょう。途 中で領域の大きさを変えたとします。その後の大きさを求めるときには、Dimensionクラスのオブジェクトが 返されます。このオブジェクトを利用すれば、幅と高さを1回のメソッド呼出しで得ることができます。 Rectangle rect = new Rectangle( 20, 30, 100, 100 ); rect.grow( 30, 40 ); Dimension area = rect.getSize( ); System.out.println( "width :" + area.width + "height :" + area.height + "after growing" ); ※注1 インスタンス変数を外部から参照して使うときは、イタリック体で表記しません。 17−3.Java2D用の準備 Java2Dは、AWTクラスライブラリの拡張として実装されており、AWTとの互換性を保つ関係から、パッ ケージ的には、追加しなくても良いようになっています。しかし、Java2D特有のオブジェクトの場合には、 パッケージを使うことを明示する必要があります。以下は、AWTの中に定義されているJava2Dのクラスの用 い方になっています。 Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 4 17−3−1.Java2D用のグラフィックスコンテキスト ★スーパークラスとサブクラス間のクラス変換 ★グラフィックスコンテキストに変換するための操作 public void paint( Graphics g ) { Graphics2D g2 = (Graphics2D) g; } あるいは、アプレットやウィンドウ(Frame)にて、 Graphics2D g = (Graphics2D) getGraphics( ); 17−4.Shape オブジェクト Shapeオブジェクトは、Java2D固有のオブジェクトなので、geom(Geometoryの略で、いくつかの形を持 つオブジェクトが定義されています)パッケージの中に入っています。これらのオブジェクト利用する場合 は、以下のように指定してやる必要があります。 import java.awt.geom.*; // geom パッケージを使うことを指示する 17−4−1.Shapeオブジェクトに用いることができるメソッド ★グラフィックスコンテキストにおいて使える主要なメソッド draw( Shape s )…sが示す形の枠を描画する fill( Shape s )…sが示す形を塗りつぶす ★グラフィックスコンテキストの描画属性に関するメソッド setPaint( Paint paint )…描画のためのペイントの設定 setComposite( Composite comp )…コンポジションの設定 setStroke( Stroke s )…線描画のための設定 setBackgound( Color color )…背景色の設定 setTransform( AffineTransform t )…座標軸のアフィン変換の設定 ★直接描画をするメソッド Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 5 独自に定義されたメソッド…drawImage, drawString, draw3DRect, drawGlyphVectorなど その他、Graphicsで使えた、drawRect,drawOval などは、すべて使うことができる。 ★Shapeクラスのオブジェクトに対して、アフィン変換をするメソッド translate( double dx, double dy )あるいはtranslate( int dx, int dy )…原点の移動 scale( double sx, double sy )…座標の拡大縮小 rotate( double theta )あるいはrotate( double theta, double x, double y )…座標軸の回転 shear( double shx, double shy )…座標のシアー変形 17−4−2.Shapeクラスのオブジェクト達 import java.awt.geom.*; Shapeクラス(抽象クラス:インターフェースの形で定義されている) →Polygon, Rectangle, Line2D, CubicCurve2D, Area, GeneralPath, QuadCurve2D →RectangularShape →Arc2D, Ellipse2D, Rectangule2D, RoundRectangle2D ★Shapeクラスの共通メソッド boolean contains( double x, double y )…指定された座標が、形の中にあるかどうか判定する Rectangle getBounds( )…その形が入る矩形領域を得る。 ★Point2D ★Line2D サブクラスにLine2D.FloatとLine2D.Doubleがある。どちらかのサブクラスを使う。 また、Point2Dを両端の点の座標に使うが、Point2Dのサブクラスも、Point2D.Float、Point2D.Double、そ して、整数が使えるPointがある。このうちのどれかを使う。 Point p1 = new Point( 10, 10 ); Point p2 = new Point( 100, 100 ); Line2D line = new Line2D.Double( p1, p2 ); g.draw( line ); ★QuadCurve2D ★CubicCurve2D Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 6 ★GeneralPath パスの描画のために以下のようなメソッドが用意されている。基本的には、PostScriptと同じで一筆書き。 moveTo( float x, float y )…(x, y)に単に移動 curveTo( float x1, float y1, float x2, float y2, float x3, float y3 )…(x1, y1), (x2, y2)を方向線と して(x3, y3)に至る線を描き、移動 lineTo( float x, float y )…(x, y )に至る線を描き、移動 quadTo( float x1, float y1, float x2, float y2 )…(x1, y1 )を経由ポイントとして、(x2, y2)に至る 曲線を描き、移動する エッフェル塔を描いてみる GeneralPath eiffel = null; eiffel.moveTo( 20.0f, 0.0f ); eiffel.lineTo( 30.0f, 0.0f ); eiffel.quadTo( 30.0f, 30.0f, 50.0f, 60.0f ); eiffel.lineTo( 35.0f, 60.0f ); eiffel.curveTo(35.0f, 40.0f, 15.0f, 40.0f, 50.0f, 60.0f ); eiffel.lineTo( 0.0f, 60.0f ); eiffel.quadTo( 20.0f, 30.0f, 20.0f, 0.0f ); ★Arc2D ★Elliipse2D ★Rectangle2D ★RoundedRectangle2D ★Area(複合Shape) 17−5.アフィン変換 translate, rotate, scale, shearは、グラフィックス領域の座標系そのものを変換するものでした。個々に、オ ブジェクトを変換したい場合は、オブジェクトに対して、transformメソッドを用います。そのときに使われ るのが、AffineTransformクラスです。 17−6.画像イメージの描画 BufferedImageを使うと、画像イメージの加工ができます。 17−7.描画時の属性 ★Strokeクラス(BasicStrokeクラス) 枠や線の属性を決定する Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 7 コンストラクタ BasicStroke( ); // new BasicStroke( ); BasicStroke( float width ); // new BasicStroke( 0.3f ); BasicStroke( float width, int cap, int join ); // new BasicStroke( 2f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL ); BasicStroke( float width, int cap, int join, float miterlimit ); BasicStroke( float width, int cap, int join, float miterlimit, float dash [ ] , float dash_phase ); 端点Capと結合点Joinのための定数 CAP_BUTT, CAP_ROUND, CAP_SQUARE JOIN_MITER, JOIN_ROUND, JOIN_BEVEL 描画のために現在のストロークをセットする setStroke( ストローク ); 例: Graphics2D g2 = (Graphics2D) g; Stroke s = new BasicStroke( 0.5f ); g2.setStroke( s ); そのストロークを持ったシェイプを作る Stroke stroke = new BasicStrock( 3f ); Shape strokedshape = stroke.createStrokedShape( shape ); ★Paintクラス Color, GradientPaint, TexturePaintがあります。 GradientPaintクラスでは、グラデーションを設定できる。循環状(cyclic)、線状(acyclic)のグラデー ションを設定できるコンストラクタが用意されている。以下のコンストラクタで、cyclicの値が省略された場 合は、線状のグラデーションになる。 GradientPaint( float x1, float y1, Color c1, float x2, float y2, Color c2, booelan cyclic ); GradientPaint( Point p1, Color c1, Point p2, Color c2, boolean cyclic ); 例: Paint p =new GradientPaint( 0f, 0f, Color.red, 100f, 0f, Color.blue, true ); TexturePaintでは、イメージを元に塗りつぶしのためのパターンの設定ができる。以下のコンストラクタで、 anchorは、パターンの繰り返しのための、四角形の位置と大きさを決定するために用いられる。 TexturePaint( BufferedImage b, Rectangle2D anchor ); 例: Rectangle2D r = new Rectangle2D.Double( ); r.setRect( 1, 1, 100, 100 ); Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 8 Paint p = new TexturePaint( b, r ); 描画のためのペイントの設定 setPaint( ペイント ); 例: Graphics2D g2 = (Graphics2D) g; g2.setPaint( Color.blue ); 17−8.ペイントの組み合わせ ★Composite ★Clip 17−9.フォントとテキストレイアウト システムで使えるすべてのフォントの名前を表示する。 Mac OS Xに固有なフォントで表示する。 LineMetrics Copyright by Tatsuo Minohara © 2003 Rev A. Macintosh Java Primer Chapter 17- 9