折れ線グラフの描画

/****************************/
/* 折れ線グラフの描画       */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;
import java.text.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class LineGraph extends JFrame {

	Draw_line pn;

	/*****************************************************/
	/* コンストラクタ(折れ線グラフ1)                  */
	/*      title_i : グラフ,x軸,及び,y軸のタイトル */
	/*      g_title_i : 凡例                             */
	/*      x_title_i : 横軸の表示項目                   */
	/*      y_scale_i : データの最小値,最大値,目盛幅   */
	/*      place_y_i : 小数点以下の桁数(y軸)         */
	/*      data_y_i : グラフのデータ                    */
	/*      d_t_i : タイトル表示の有無                   */
	/*      d_g_i : 凡例表示の有無                       */
	/*****************************************************/
	LineGraph(String title_i[], String g_title_i[], String x_title_i[],
              double y_scale_i[], int place_y_i, double data_y_i[][],
              boolean d_t_i, boolean d_g_i)
	{
					// JFrameクラスのコンストラクタの呼び出し
		super("折れ線グラフ(1)");
					// Windowサイズと表示位置を設定
		int width = 900, height = 600;   // Windowの大きさ(初期サイズ)
		setSize(width, height);
		Toolkit tool = getToolkit();
		Dimension d  = tool.getScreenSize();
		setLocation(d.width / 2 - width / 2, d.height / 2 - height / 2);
					// 描画パネル
		Container cp = getContentPane();
		pn = new Draw_line(title_i, g_title_i, x_title_i, y_scale_i, place_y_i, data_y_i, d_t_i, d_g_i, this);
		cp.add(pn);
					// ウィンドウを表示
		setVisible(true);
					// イベントアダプタ
		addWindowListener(new WinEnd());
		addComponentListener(new ComponentResize());
	}

	/*********************************************************/
	/* コンストラクタ(折れ線グラフ2)                      */
	/*      title_i : グラフ,x軸,及び,y軸のタイトル     */
	/*      g_title_i : 凡例                                 */
	/*      x_scale_i : データの最小値,最大値,目盛幅(y) */
	/*      place_x_i : 小数点以下の桁数(x軸)             */
	/*      y_scale_i : データの最小値,最大値,目盛幅(y) */
	/*      place_y_i : 小数点以下の桁数(y軸)             */
	/*      data_x_i : グラフのデータ(x軸)                */
	/*      data_y_i : グラフのデータ(y軸)                */
	/*      d_t_i : タイトル表示の有無                       */
	/*      d_g_i : 凡例表示の有無                           */
	/*********************************************************/
	LineGraph(String title_i[], String g_title_i[], double x_scale_i[],
              int place_x_i, double y_scale_i[], int place_y_i,
              double data_x_i[][], double data_y_i[][], boolean d_t_i,
              boolean d_g_i)
	{
					// JFrameクラスのコンストラクタの呼び出し
		super("折れ線グラフ(2)");
					// Windowサイズと表示位置を設定
		int width = 900, height = 600;   // Windowの大きさ(初期サイズ)
		setSize(width, height);
		Toolkit tool = getToolkit();
		Dimension d  = tool.getScreenSize();
		setLocation(d.width / 2 - width / 2, d.height / 2 - height / 2);
					// 描画パネル
		Container cp = getContentPane();
		pn = new Draw_line(title_i, g_title_i, x_scale_i, place_x_i, y_scale_i, place_y_i, data_x_i, data_y_i, d_t_i, d_g_i, this);
		cp.add(pn);
					// ウィンドウを表示
		setVisible(true);
					// イベントアダプタ
		addWindowListener(new WinEnd());
		addComponentListener(new ComponentResize());
	}

	/**********************/
	/* Windowのサイズ変化 */
	/**********************/
	class ComponentResize extends ComponentAdapter
	{
		public void componentResized(ComponentEvent e)
		{
			pn.repaint();
		}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			setVisible(false);
		}
	}
}

class Draw_line extends JPanel {

	String title[];   // グラフのタイトル
	String g_title[];   // 凡例(グラフの内容)
	String x_title[];   // x軸への表示
	double x_scale[];   // y軸目盛り
	double y_scale[];   // y軸目盛り
	double data_x[][], data_y[][];   // データ
	boolean d_t;   // タイトル表示の有無
	boolean d_g;   // 凡例表示の有無
	boolean type = true;   // 横軸が項目かデータか
	boolean ver = true;   // 縦か横か
	int place_x;   // 小数点以下の桁数(x軸)
	int place_y;   // 小数点以下の桁数(y軸)
	int width = 900, height = 600;   // Windowの大きさ(初期サイズ)
	int bx1, bx2, by1, by2;   // 表示切り替えボタンの位置
	LineGraph line;
	String change = "横 色";   // 表示切り替えボタン
	float line_w = 1.0f;   // 折れ線グラフ等の線の太さ
	boolean line_m = true;   // 折れ線グラフ等にマークを付けるか否か
	Color cl[] = {Color.black, Color.magenta, Color.blue, Color.orange, Color.cyan,
	              Color.pink, Color.green, Color.yellow, Color.darkGray, Color.red};   // グラフの色
	int n_g;   // グラフの数

	/*****************************************************/
	/* コンストラクタ(折れ線グラフ1)                  */
	/*      title_i : グラフ,x軸,及び,y軸のタイトル */
	/*      g_title_i : 凡例                             */
	/*      x_title_i : 横軸の表示項目                   */
	/*      y_scale_i : データの最小値,最大値,目盛幅   */
	/*      place_y_i : 小数点以下の桁数(y軸)         */
	/*      data_y_i : グラフのデータ                    */
	/*      d_t_i : タイトル表示の有無                   */
	/*      d_g_i : 凡例表示の有無                       */
	/*****************************************************/
	Draw_line(String title_i[], String g_title_i[], String x_title_i[],
              double y_scale_i[], int place_y_i, double data_y_i[][],
              boolean d_t_i, boolean d_g_i, LineGraph line_i)
	{
					// 背景色
		setBackground(Color.white);
					// テーブルデータの保存
		title   = title_i;
		g_title = g_title_i;
		x_title = x_title_i;
		y_scale = y_scale_i;
		place_y = place_y_i;
		data_y  = data_y_i;
		d_t     = d_t_i;
		d_g     = d_g_i;
		line    = line_i;
					// イベントアダプタ
		addMouseListener(new ClickMouse(this));
	}

	/*********************************************************/
	/* コンストラクタ(折れ線グラフ2)                      */
	/*      title_i : グラフ,x軸,及び,y軸のタイトル     */
	/*      g_title_i : 凡例                                 */
	/*      x_scale_i : データの最小値,最大値,目盛幅(y) */
	/*      place_x_i : 小数点以下の桁数(x軸)             */
	/*      y_scale_i : データの最小値,最大値,目盛幅(y) */
	/*      place_y_i : 小数点以下の桁数(y軸)             */
	/*      data_x_i : グラフのデータ(x軸)                */
	/*      data_y_i : グラフのデータ(y軸)                */
	/*      d_t_i : タイトル表示の有無                       */
	/*      d_g_i : 凡例表示の有無                           */
	/*********************************************************/
	Draw_line(String title_i[], String g_title_i[], double x_scale_i[],
              int place_x_i, double y_scale_i[], int place_y_i,
              double data_x_i[][], double data_y_i[][], boolean d_t_i,
              boolean d_g_i, LineGraph line_i)
	{
					// 背景色
		setBackground(Color.white);
					// テーブルデータの保存
		title   = title_i;
		g_title = g_title_i;
		x_scale = x_scale_i;
		place_x = place_x_i;
		y_scale = y_scale_i;
		place_y = place_y_i;
		data_x  = data_x_i;
		data_y  = data_y_i;
		d_t     = d_t_i;
		d_g     = d_g_i;
		type    = false;
		line    = line_i;
					// イベントアダプタ
		addMouseListener(new ClickMouse(this));
	}

	/********/
	/* 描画 */
	/********/
	public void paintComponent (Graphics g)
	{
		super.paintComponent(g);   // 親クラスの描画(必ず必要)

		double r, x1, y1, sp;
		int i1, i2, cr, k, k_x, k_y, k1, k2, kx, kx1, ky, ky1, han, len;
		int x_l, x_r, y_u, y_d;   // 描画領域
		int f_size;   // フォントサイズ
		int n_p;   // データの数
		String s1;
		Font f;
		FontMetrics fm;
		Graphics2D g2 = (Graphics2D)g;
					//
					// Windowサイズの取得
					//
		Insets insets = line.getInsets();
		Dimension d = line.getSize();
		width  = d.width - (insets.left + insets.right);
		height = d.height - (insets.top + insets.bottom);
		x_l    = insets.left + 10;
		x_r    = d.width - insets.right - 10;
		y_u    = 20;
		y_d    = d.height - insets.bottom - insets.top;
					//
					// グラフタイトルの表示
					//
		r      = 0.05;   // タイトル領域の割合
		f_size = ((y_d - y_u) < (x_r - x_l)) ? (int)((y_d - y_u) * r) : (int)((x_r - x_l) * r);
		if (f_size < 5)
			f_size = 5;
		if (d_t) {
			f = new Font("TimesRoman", Font.BOLD, f_size);
			g.setFont(f);
			fm  = g.getFontMetrics(f);
			len = fm.stringWidth(title[0]);
			g.drawString(title[0], (x_l+x_r)/2-len/2, y_d-f_size/2);
			y_d -= f_size;
		}
					//
					// 表示切り替えボタンの設置
					//
		f_size = (int)(0.8 * f_size);
		if (f_size < 5)
			f_size = 5;
		f  = new Font("TimesRoman", Font.PLAIN, f_size);
		fm = g.getFontMetrics(f);
		g.setFont(f);
		g.setColor(Color.yellow);
		len = fm.stringWidth(change);
		bx1 = x_r - len - 7 * f_size / 10;
		by1 = y_u - f_size / 2;
		bx2 = bx1 + len + f_size / 2;
		by2 = by1 + 6 * f_size / 5;
		g.fill3DRect(bx1, by1, len+f_size/2, 6*f_size/5, true);
		g.setColor(Color.black);
		g.drawString(change, x_r-len-f_size/2, y_u+f_size/2);
					//
					// 凡例の表示
					//
		n_g = g_title.length;
		if (d_g) {
			han = 0;
			for (i1 = 0; i1 < n_g; i1++) {
				len = fm.stringWidth(g_title[i1]);
				if (len > han)
					han = len;
			}
			han += 15;
			r    = 0.2;   // 凡例領域の割合
			k1   = (int)((x_r - x_l) * r);
			if (han > k1)
				han = k1;
			kx = x_r - han;
			ky = y_u + 3 * f_size / 2;
			k  = 0;
			g2.setStroke(new BasicStroke(7.0f));
			for (i1 = 0; i1 < n_g; i1++) {
				g.setColor(cl[k]);
				g.drawLine(kx, ky, kx+10, ky);
				g.setColor(Color.black);
				g.drawString(g_title[i1], kx+15, ky+2*f_size/5);
				k++;
				if (k >= cl.length)
					k = 0;
				ky += f_size;
			}
			g2.setStroke(new BasicStroke(1.0f));
			x_r -= (han + 10);
		}
		else
			x_r -= (int)(0.03 * (x_r - x_l));
					//
					// x軸及びy軸のタイトルの表示
					//
		if (ver) {   // 縦
			if (title[1].length() > 0 && !title[1].equals("-")) {
				len = fm.stringWidth(title[1]);
				g.drawString(title[1], (x_l+x_r)/2-len/2, y_d-4*f_size/5);
				y_d -= 7 * f_size / 4;
			}
			else
				y_d -= f_size / 2;
			if (title[2].length() > 0 && !title[2].equals("-")) {
				g.drawString(title[2], x_l, y_u+f_size/2);
				y_u += f_size;
			}
		}
		else {   // 横
			if (title[2].length() > 0 && !title[2].equals("-")) {
				len = fm.stringWidth(title[2]);
				g.drawString(title[2], (x_l+x_r)/2-len/2, y_d-4*f_size/5);
				y_d -= 7 * f_size / 4;
			}
			else
				y_d -= f_size / 2;
			if (title[1].length() > 0 && !title[1].equals("-")) {
				g.drawString(title[1], x_l, y_u+f_size/2);
				y_u += f_size;
			}
		}
					//
					// x軸,y軸,及び,各軸の目盛り
					//
		f_size = (int)(0.8 * f_size);
		if (f_size < 5)
			f_size = 5;
		f    = new Font("TimesRoman", Font.PLAIN, f_size);
		fm   = g.getFontMetrics(f);
		y_d -= 3 * f_size / 2;
		k_y  = (int)((y_scale[1] - y_scale[0]) / (0.99 * y_scale[2]));
		k_x  = 0;
		if (!type)
			k_x = (int)((x_scale[1] - x_scale[0]) / (0.99 * x_scale[2]));
		g.setFont(f);

		DecimalFormat df_x, df_y;
		df_x = new DecimalFormat("#");
		df_y = new DecimalFormat("#");
		if (!type) {
			if (place_x != 0) {
				s1 = "#.";
				for (i1 = 0; i1 < place_x; i1++)
					s1 += "0";
				df_x = new DecimalFormat(s1);
			}
		}
		if (place_y != 0) {
			s1 = "#.";
			for (i1 = 0; i1 < place_y; i1++)
				s1 += "0";
			df_y = new DecimalFormat(s1);
		}
						// 縦表示
		if (ver) {
							// y軸
			y1  = y_scale[0];
			len = 0;
			for (i1 = 0; i1 < k_y+1; i1++) {
				s1 = df_y.format(y1);
				k1 = fm.stringWidth(s1);
				if (k1 > len)
					len = k1;
				y1 += y_scale[2];
			}
			g.drawLine(x_l+len+5, y_u, x_l+len+5, y_d);
			g.drawLine(x_r, y_u, x_r, y_d);
			y1 = y_scale[0];
			x1 = y_d;
			sp = (double)(y_d - y_u) / k_y;
			for (i1 = 0; i1 < k_y+1; i1++) {
				ky = (int)Math.round(x1);
				s1 = df_y.format(y1);
				k1 = fm.stringWidth(s1);
				g.drawString(s1, x_l+len-k1, ky+f_size/2);
				g.drawLine(x_l+len+5, ky, x_r, ky);
				y1 += y_scale[2];
				x1 -= sp;
			}
			x_l += (len + 5);
							// x軸
			if (type) {
				n_p = x_title.length;
				sp  = (double)(x_r - x_l) / n_p;
				x1  = x_l + sp / 2.0;
				for (i1 = 0; i1 < n_p; i1++) {
					kx = (int)Math.round(x1);
					k1 = fm.stringWidth(x_title[i1]);
					g.drawString(x_title[i1], kx-k1/2, y_d+6*f_size/5);
					g.drawLine(kx, y_d, kx, y_d-5);
					x1 += sp;
				}
			}
			else {
				x1 = x_scale[0];
				y1 = x_l;
				sp = (double)(x_r - x_l) / k_x;
				for (i1 = 0; i1 < k_x+1; i1++) {
					kx = (int)Math.round(y1);
					s1 = df_x.format(x1);
					k1 = fm.stringWidth(s1);
					g.drawString(s1, kx-k1/2, y_d+6*f_size/5);
					g.drawLine(kx, y_d, kx, y_u);
					x1 += x_scale[2];
					y1 += sp;
				}
			}
		}
						// 横表示
		else {
							// y軸
			if (type) {
				n_p = x_title.length;
				len = 0;
				for (i1 = 0; i1 < n_p; i1++) {
					k1 = fm.stringWidth(x_title[i1]);
					if (k1 > len)
						len = k1;
				}
				g.drawLine(x_l+len+5, y_u, x_l+len+5, y_d);
				g.drawLine(x_r, y_u, x_r, y_d);
				sp = (double)(y_d - y_u) / n_p;
				x1 = y_d - sp / 2.0;
				for (i1 = 0; i1 < n_p; i1++) {
					ky = (int)Math.round(x1);
					k1 = fm.stringWidth(x_title[n_p-1-i1]);
					g.drawString(x_title[n_p-1-i1], x_l+len-k1, ky+f_size/2);
					g.drawLine(x_l+len+5, ky, x_l+len+10, ky);
					x1 -= sp;
				}
				g.drawLine(x_l+len+5, y_u, x_r, y_u);
				g.drawLine(x_l+len+5, y_d, x_r, y_d);
				x_l += (len + 5);
			}
			else {
				y1  = x_scale[0];
				len = 0;
				for (i1 = 0; i1 < k_x+1; i1++) {
					s1 = df_x.format(y1);
					k1 = fm.stringWidth(s1);
					if (k1 > len)
						len = k1;
					y1 += x_scale[2];
				}
				g.drawLine(x_l+len+5, y_u, x_l+len+5, y_d);
				g.drawLine(x_r, y_u, x_r, y_d);
				y1 = x_scale[0];
				x1 = y_d;
				sp = (double)(y_d - y_u) / k_x;
				for (i1 = 0; i1 < k_x+1; i1++) {
					ky = (int)Math.round(x1);
					s1 = df_x.format(y1);
					k1 = fm.stringWidth(s1);
					g.drawString(s1, x_l+len-k1, ky+f_size/2);
					g.drawLine(x_l+len+5, ky, x_r, ky);
					y1 += x_scale[2];
					x1 -= sp;
				}
				x_l += (len + 5);
			}
							// x軸
			x1 = y_scale[0];
			y1 = x_l;
			sp = (double)(x_r - x_l) / k_y;
			for (i1 = 0; i1 < k_y+1; i1++) {
				kx = (int)Math.round(y1);
				s1 = df_y.format(x1);
				k1 = fm.stringWidth(s1);
				g.drawString(s1, kx-k1/2, y_d+6*f_size/5);
				g.drawLine(kx, y_d, kx, y_u);
				x1 += y_scale[2];
				y1 += sp;
			}
		}
					//
					// グラフの表示
					//
		g2.setStroke(new BasicStroke(line_w));
		cr = (int)line_w + 6;
						// 縦表示
		if (ver) {
			if (type) {
				n_p = x_title.length;
				sp  = (double)(x_r - x_l) / n_p;
				k1  = 0;
				for (i1 = 0; i1 < n_g; i1++) {
					g.setColor(cl[k1]);
					x1  = x_l + sp / 2.0;
					kx1 = 0;
					ky1 = 0;
					for (i2 = 0; i2 < n_p; i2++) {
						kx = (int)Math.round(x1);
						ky = y_d - (int)((y_d - y_u) * (data_y[i1][i2] - y_scale[0]) / (y_scale[1] - y_scale[0]));
						if (line_m)
							g.fillOval(kx-cr/2, ky-cr/2, cr, cr);
						if (i2 > 0)
							g.drawLine(kx1, ky1, kx, ky);
						kx1  = kx;
						ky1  = ky;
						x1  += sp;
					}
					k1++;
					if (k1 >= cl.length)
						k1 = 0;
				}
			}
			else {
				n_p = data_x[0].length;
				k1  = 0;
				for (i1 = 0; i1 < n_g; i1++) {
					g.setColor(cl[k1]);
					kx1 = 0;
					ky1 = 0;
					for (i2 = 0; i2 < n_p; i2++) {
						kx = x_l + (int)((x_r - x_l) * (data_x[i1][i2] - x_scale[0]) / (x_scale[1] - x_scale[0]));
						ky = y_d - (int)((y_d - y_u) * (data_y[i1][i2] - y_scale[0]) / (y_scale[1] - y_scale[0]));
						if (line_m)
							g.fillOval(kx-cr/2, ky-cr/2, cr, cr);
						if (i2 > 0)
							g.drawLine(kx1, ky1, kx, ky);
						kx1 = kx;
						ky1 = ky;
					}
					k1++;
					if (k1 >= cl.length)
						k1 = 0;
				}
			}
		}
						// 横表示
		else {
			if (type) {
				n_p = x_title.length;
				sp = (double)(y_d - y_u) / n_p;
				k1 = 0;
				for (i1 = 0; i1 < n_g; i1++) {
					g.setColor(cl[k1]);
					y1  = y_d - sp / 2.0;
					kx1 = 0;
					ky1 = 0;
					for (i2 = 0; i2 < n_p; i2++) {
						ky = (int)Math.round(y1);
						kx = x_l + (int)((x_r - x_l) * (data_y[i1][n_p-1-i2] - y_scale[0]) / (y_scale[1] - y_scale[0]));
						if (line_m)
							g.fillOval(kx-cr/2, ky-cr/2, cr, cr);
						if (i2 > 0)
							g.drawLine(kx1, ky1, kx, ky);
						kx1  = kx;
						ky1  = ky;
						y1  -= sp;
					}
					k1++;
					if (k1 >= cl.length)
						k1 = 0;
				}
			}
			else {
				n_p = data_x[0].length;
				k1  = 0;
				for (i1 = 0; i1 < n_g; i1++) {
					g.setColor(cl[k1]);
					kx1 = 0;
					ky1 = 0;
					for (i2 = 0; i2 < n_p; i2++) {
						kx = x_l + (int)((x_r - x_l) * (data_y[i1][i2] - y_scale[0]) / (y_scale[1] - y_scale[0]));
						ky = y_d - (int)((y_d - y_u) * (data_x[i1][i2] - x_scale[0]) / (x_scale[1] - x_scale[0]));
						if (line_m)
							g.fillOval(kx-cr/2, ky-cr/2, cr, cr);
						if (i2 > 0)
							g.drawLine(kx1, ky1, kx, ky);
						kx1 = kx;
						ky1 = ky;
					}
					k1++;
					if (k1 >= cl.length)
						k1 = 0;
				}
			}
		}
		g2.setStroke(new BasicStroke(1.0f));
	}

	/************************************/
	/* マウスがクリックされたときの処理 */
	/************************************/
	class ClickMouse extends MouseAdapter
	{
		Draw_line dr;

		ClickMouse(Draw_line dr1)
		{
			dr = dr1;
		}

		public void mouseClicked(MouseEvent e)
		{
			int xp = e.getX();
			int yp = e.getY();
					// 縦表示と横表示の変換
			if (xp > bx1 && xp < bx1+(bx2-bx1)/2 && yp > by1 && yp < by2) {
				if (ver) {
					ver    = false;
					change = "縦 色";
				}
				else {
					ver    = true;
					change = "横 色";
				}
				repaint();
			}
					// グラフの色,線の太さ等
			else if (xp > bx1+(bx2-bx1)/2 && xp < bx2 && yp > by1 && yp < by2) {
				Modify md = new Modify(dr.line, dr);
				md.setVisible(true);
			}
		}
	}
}