2019.09.14
Javaプログラミング
数学グラフ
Swingパッケージを使って数式のグラフを表示する方法を解説します。
ここで扱う数式は、
y = f(x)
と表すもので、xを与えてyを求める関数です。以下はその例です。
y = x * x … 放物線
以上が、xからyを求める関数の例です。
それでは、y = f(x)で表される関数をグラフ表示する方法について考えていきます。
この図から、以下のような問題があることがわかります。
このため、数式の値で直接グラフを表示することはできません。
サインカーブを表示するJavaソースコード
このプログラムは、サインカーブの表示を行う「GR_SinCurve.java」と座標変換を行う「Transformation2d.java」の2つのソースコードから成り立っています。
サインカーブを表示するソース
GR_SinCurve.java
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
import java.awt.*; import javax.swing.*; public class GR_SinCurve { public static void main(String[] args) { // フレームの大きさを設定 int w = 400; int h = 320; // フレームを作成 JFrame frame = new JFrame(); // タイトル名を設定 frame.setTitle( "サインカーブ" ); // 内側フレームの大きさを設定 frame.getContentPane().setPreferredSize( new Dimension( w, h ) ); frame.pack(); // ”×”ボタンを押した時の処理を設定 frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); // フレームにパネルを追加 MyPanel panel = new MyPanel(); frame.getContentPane().add( panel ); // フレームを表示 frame.setVisible( true ); } } // JPanelを継承したMyPanelを作成 class MyPanel extends JPanel { public MyPanel() { // 背景色を白(white)に設定 setBackground( Color.white ); } // 関数 private double f( double x ) { // 角度degをラジアンradに変換 double rad = Math.toRadians( x ); // サインを計算 return Math.sin( rad ); } // ラインの開始座標 private int line_x1 = 0; private int line_y1 = 0; // ラインの開始座標の設定 private void moveTo( int x, int y ) { line_x1 = x; line_y1 = y; } // ラインの表示 private void lineTo(Graphics g, int x, int y ) { g.drawLine( line_x1, line_y1, x, y ); line_x1 = x; line_y1 = y; } // 描画 public void paintComponent( Graphics g ) { super.paintComponent( g ); // フレームの大きさを取得 int frame_w = getWidth(); int frame_h = getHeight(); // 座標変換クラスの作成 Transformation2d trans = new Transformation2d(); // 座標を設定 // (-360.0,1.2)-(360.0,-1.2)の範囲がフレーム全体に表示する trans.set( -360.0, 1.2, 360, -1.2, 0.0, 0.0, (double)( frame_w - 1 ), (double)( frame_h - 1 ) ); // 原点の画像座標を取得 double origin_x = trans.getX( 0.0 ); double origin_y = trans.getY( 0.0 ); // 座標軸の色を黒 g.setColor( Color.black ); // x軸の描画 g.drawLine( 0, (int)origin_y, frame_w - 1, (int)origin_y ); // y軸の描画 g.drawLine( (int)origin_x, 0, (int)origin_x, frame_h - 1 ); // サイン(sin)カーブの描画 // サインカーブの色を赤 g.setColor( Color.red ); // 角度-360から360度までのサインカーブを表示 boolean isStart = true; for ( int x = -360; x <= 360; x++ ) { // 計算結果をyに代入 double y = f( (double)x ); // 数学座標( deg, value )を // 画像座標( line_x2, line_x2 )に変換 int px = (int)trans.getX( (double)x ); int py = (int)trans.getY( y ); // ラインの表示 if ( true == isStart ) { moveTo( px, py ); isStart = false; } else lineTo( g, px, py ); } } }
座標変換を行うソース
Transformation2d.java
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045
public class Transformation2d { // 変換パラメータ a,b,c,d private double a = 0.0; private double b = 0.0; private double c = 0.0; private double d = 0.0; // パラメータを計算 boolean set( double x1, double y1, double x2, double y2, double px1, double py1, double px2, double py2 ) { // ゼロとみなす値 double e = 0.0000000001; // 初期値を代入 a = b = c = d = 0.0; // x1とx2が同じ値であればエラー if ( e > Math.abs( x1 - x2 ) ) return false; // y1とy2が同じ値であればエラー if ( e > Math.abs( y1 - y2 ) ) return false; // パラメータの計算 a = ( px1 - px2 ) / ( x1 - x2 ); b = ( px1 * x2 - px2 * x1 ) / ( x2 - x1 ); c = ( py1 - py2 ) / ( y1 - y2 ); d = ( py1 * y2 - py2 * y1 ) / ( y2 - y1 ); return true; } // 変換後のx座標を取得 public double getX( double x ) { return a * x + b; } // 変換後のy座標を取得 public double getY( double y ) { return c * y + d; } }
ソースコードの解説
ここから、「GR_SinCurve.java」を解説していきます。
001 002
import java.awt.*; import javax.swing.*;
004
public class GR_SinCurve {
クラス名を、GR_SinCurveとしています。
005
public static void main(String[] args) {
このmainメソッドからプログラムを実行します。
006 007 008
// フレームの大きさを設定 int w = 400; int h = 320;
010 011
// フレームを作成
JFrame frame = new JFrame();
JFrameオブジェクトを作成しています。
013 014
// タイトル名を設定 frame.setTitle( "サインカーブ" );
017 018 019
// 内側フレームの大きさを設定
frame.getContentPane().setPreferredSize( new Dimension( w, h ) );
frame.pack();
021 022
// ”×”ボタンを押した時の処理を設定
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
"×"ボタンをクリックしたときに、フレームを閉じる処理を設定しています。
024 025 026
// フレームにパネルを追加
MyPanel panel = new MyPanel();
frame.getContentPane().add( panel );
フレームにパネルに追加しています。
このパネルで、サインカーブのグラフを表示いています。34行目以降を参考にしてください。
028 029
// フレームを表示
frame.setVisible( true );
034 035
// JPanelを継承したMyPanelを作成 class MyPanel extends JPanel {
JPanelを継承したMyPanelクラスを作成しています。
036 037 038
public MyPanel() { // 背景色を白(white)に設定 setBackground( Color.white );
クラス名と同じ名前のメソッドMyPanel()はコンストラクタと呼ばれ、クラス作成時に呼ばれます。そのメソッドの中に初期化の処理を記述します。
ここでは、setBackgroundメソッドにColor.whiteを渡して、背景色を白にする処理を記述しています。
041 042 043 044 045 046 047 048
// 関数 private double f( double x ) { // 角度degをラジアンradに変換 double rad = Math.toRadians( x ); // サインを計算 return Math.sin( rad ); }
変数xを渡してyを戻すメソッドです。ここでは、角度xからsin(x)を計算するメソッドにしています。xの単位が度です。
単位が度の変数xの値をMath.toRadiansメソッドでラジアンに変換した値を変数radに代入し、Math.sinメソッドにradを渡してsinの値を計算しています。
このメソッドの中を変えることで、いろいろな種類のグラフを表示することができます。
051 052 053
// ラインの開始座標 private int line_x1 = 0; private int line_y1 = 0;
055 056 057 058 059 060
// ラインの開始座標の設定 private void moveTo( int x, int y ) { line_x1 = x; line_y1 = y; }
062 063 064 065 066 067 068
// ラインの表示 private void lineTo(Graphics g, int x, int y ) { g.drawLine( line_x1, line_y1, x, y ); line_x1 = x; line_y1 = y; }
ラインを表示するメソッドです。メンバ変数の開始座標( line_x1, line_y1 )と引数( x, y )を結ぶラインを表示しています。
表示した後、line_x1にx、line_y1にyを代入しています。
070 071
// 描画 public void paintComponent( Graphics g ) {
072
super.paintComponent( g );
074 075 076
// フレームの大きさを取得 int frame_w = getWidth(); int frame_h = getHeight();
078 079 080 081 082 083 084
// 座標変換クラスの作成 Transformation2d trans = new Transformation2d(); // 座標を設定 // (-360.0,1.2)-(360.0,-1.2)の範囲がフレーム全体に表示する trans.set( -360.0, 1.2, 360, -1.2, 0.0, 0.0, (double)( frame_w - 1 ), (double)( frame_h - 1 ) );
座標を変換するクラスtransを作成しています。このクラスは、数学座標からグラフィック座標に変換するためのものです。
予め設定した変換前の数学座標の範囲と、変換後のグラフィック座標の範囲から、任意の数学座標(x,y)に対応するグラフィック座標を求めることができます。
ここでは、trans.setメソッドで、変換前の数学座標を(-360.0,1.2)-(360.0,-1.2)、変換後のグラフィック座標の範囲を(0,0)-(frame_w-1,frame_h-1)としています。
x座標とy座標を別々にみていくと、数学座標xの範囲は-360.0~360.0、グラフィック座標xの範囲は0~frame_w-1です。このソースのx座標は単位が度の角度を表しており、角度が-360度は画面の左端、角度は360度は画面の右端の値が取得できるようにしています。
数学座標yの範囲は1.2~-1.2、グラフィック座標yの範囲は0~frame_h-1です。y座標はsin(x)の値を表しており、sin(x)の最小値、最大値が画面内に収まるように余裕をもって設定しています。sin(x)の計算値の最小値は-1.0、最大値が1.0なので、ここで設定している1.2~-1.2の範囲に収まります。
数学座標yの範囲を1.0~-1.0にすると、sin(x)の最大値が画面の上端、最小値が下端になります。
Transformation2dクラスのソースコードは、以下の記事で詳しく解説しています。
086 087 088
// 原点の画像座標を取得 double origin_x = trans.getX( 0.0 ); double origin_y = trans.getY( 0.0 );
090 091
// 座標軸の色を黒
g.setColor( Color.black );
g.SetColorメソッドで、これから表示する図形の色を黒に設定しています。
093 094
// x軸の描画 g.drawLine( 0, (int)origin_y, frame_w - 1, (int)origin_y );
g.drawLineメソッドで、グラフィックの原点y座標origin_yを通る水平線を表示しています。これがx軸です。
096 097
// y軸の描画 g.drawLine( (int)origin_x, 0, (int)origin_x, frame_h - 1 );
g.drawLineメソッドで、グラフィックの原点x座標origin_xを通る垂直線を表示しています。これがy軸です。
099 100 101
// サイン(sin)カーブの描画 // サインカーブの色を赤 g.setColor( Color.red );
103 104
// 角度-360から360度までのサインカーブを表示
boolean isStart = true;
105
for ( int x = -360; x <= 360; x++ ) {
xを-360から360まで1ずつ変化されるfor文を作成しています。
106 107
// 計算結果をyに代入 double y = f( (double)x );
109 110 111 112
// 数学座標( deg, value )を // 画像座標( line_x2, line_x2 )に変換 int px = (int)trans.getX( (double)x ); int py = (int)trans.getY( y );
114 115 116 117 118
// ラインの表示
if ( true == isStart ) {
moveTo( px, py );
isStart = false;
}
119 120
else lineTo( g, px, py );
表示の開始を表す変数isStartがfalseの場合、lineToメソッドに(px,py)を与えてラインを表示します。表示するラインは(px,py)とライン表示開始位置の座標を結ぶラインが表示されます。ラインを表示した後に、(px,py)がライン表示開始位置に設定されます。
このように、moveToを呼んだ後、lineToを連続して呼ぶと折れ線が表示できるようになっています。
GR_SinCurveのコンパイル
コンパイルするときは、javacコマンドに"GR_SinCurve.java"と"Transformation2d.java"の2つのソースコードを指定してください。
コンパイル方法
javac GR_SinCurve.java Transformation2d.java
コンパイル方法(ソースの文字コードが"ANSI"の場合)
javac -encoding sjis GR_SinCurve.java Transformation2d.java
コンパイル方法(ソースの文字コードが"utf-8"の場合)
javac -encoding utf-8 GR_SinCurve.java Transformation2d.java
以上です。