// 多機能グラフシートパネル // JDK 1.0.2 // 1998 N.Inoue import java.awt.*; public class GraphSheet extends Canvas { // ペンタイプ定数 public final static int FREEHAND = 0; public final static int LINE = 1; public final static int TANGENTLINE = 2; public final static int PERPLINE = 3; public final static int ERASER = 4; // 消しゴムの大きさ private final int ERASERWIDTH = 40; // レーダークロスの大きさ private final int POINTCROSS = 10; // グラフでの1の大きさ private final int ONE = 50; // 色設定 private final Color GRID_COLOR = QuickColor.silver; private final Color XYBAR_COLOR = QuickColor.blue; private final Color NUMBER_COLOR = QuickColor.black; private final Color BG_COLOR = QuickColor.white; // 変数、クラス private int pentype = FREEHAND; private int beforex = 0; private int beforey = 0; private int startx = 0; private int starty = 0; private double tval = 0.0; private boolean indirect = false; private Image sheetimg; private Graphics graph; private Color pencolor = QuickColor.green; private Color graphcolor = QuickColor.red; private String funcstr = "x"; private String funcstrx = "t"; private String funcstry = "t"; private String diffuncstr = "1"; /** * グラフシートのコンストラクタ */ public GraphSheet() { super(); this.setBackground(BG_COLOR); } public boolean mouseDown(Event evt, int x, int y) { Graphics g = this.getGraphics(); switch(pentype) { case FREEHAND: beforex = x; beforey = y; g.setColor(pencolor); g.drawRect(x, y, 1, 1); break; case LINE: startx = x; starty = y; beforex = x; beforey = y; drawXORLine(x, y, x, y); break; case TANGENTLINE: // fall through case PERPLINE: Point p[]; int px, py; if(indirect) { tval = 0.0; if(pentype == TANGENTLINE) p = tangentline(tval); else p = perpline(tval); // tの表示 int fh = g.getFontMetrics().getHeight(); g.clearRect(20, 40-fh, 160, fh); g.drawString("t="+tval, 20, 40); // 導関数の表示 g.drawString("t'="+diffuncstr, 20, 20); px = gx(Calc.calc(funcstrx, tval, 't')); py = gy(Calc.calc(funcstry, tval, 't')); } else { if(pentype == TANGENTLINE) p = tangentline(x); else p = perpline(x); // 導関数の表示 g.drawString("y'="+diffuncstr, 20, 20); px = x; py = gy(Calc.calc(funcstr, mx(x))); } // ゴーストラインの表示 drawXORLine(p[0].x, p[0].y, p[1].x, p[1].y); drawXORLine(px, py-POINTCROSS, px, py+POINTCROSS); drawXORLine(px-POINTCROSS, py, px+POINTCROSS, py); beforex = x; beforey = y; break; case ERASER: repaint(x-ERASERWIDTH/2, y-ERASERWIDTH/2, ERASERWIDTH, ERASERWIDTH); break; } return true; } public boolean mouseDrag(Event evt, int x, int y) { switch(pentype) { case FREEHAND: drawPenLine(beforex, beforey, x, y); beforex = x; beforey = y; break; case LINE: // ラバーバンドの更新 drawXORLine(startx, starty, beforex, beforey); drawXORLine(startx, starty, x, y); beforex = x; beforey = y; break; case TANGENTLINE: // fall through case PERPLINE: Point bp[] = new Point[2]; Point p[] = new Point[2]; int px, py, bpx, bpy; if(indirect) { // 以前のtの値の消去 Graphics g = getGraphics(); int fh = g.getFontMetrics().getHeight(); g.clearRect(20, 40-fh, 160, fh); bpx = gx(Calc.calc(funcstrx, tval, 't')); bpy = gy(Calc.calc(funcstry, tval, 't')); if(pentype == TANGENTLINE) { bp = tangentline(tval); tval = inctvalue(tval, beforex, beforey, x, y); p = tangentline(tval); } else // pentype == PERPLINE { bp = perpline(tval); tval = inctvalue(tval, beforex, beforey, x, y); p = perpline(tval); } px = gx(Calc.calc(funcstrx, tval, 't')); py = gy(Calc.calc(funcstry, tval, 't')); // 現在のtの値を表示 g.drawString("t=" + tval, 20, 40); } else { bpx = beforex; bpy = gy(Calc.calc(funcstr, mx(beforex))); px = x; py = gy(Calc.calc(funcstr, mx(x))); if(pentype == TANGENTLINE) { bp = tangentline(beforex); p = tangentline(x); } else // pentype == PERPLINE { bp = perpline(beforex); p = perpline(x); } } // ゴーストラインの更新 drawXORLine(bp[0].x, bp[0].y, bp[1].x, bp[1].y); drawXORLine(p[0].x, p[0].y, p[1].x, p[1].y); drawXORLine(bpx, bpy-POINTCROSS, bpx, bpy+POINTCROSS); drawXORLine(bpx-POINTCROSS, bpy, bpx+POINTCROSS, bpy); drawXORLine(px, py-POINTCROSS, px, py+POINTCROSS); drawXORLine(px-POINTCROSS, py, px+POINTCROSS, py); beforex = x; beforey = y; break; case ERASER: repaint(x-ERASERWIDTH/2, y-ERASERWIDTH/2, ERASERWIDTH, ERASERWIDTH); break; } return true; } public boolean mouseUp(Event evt, int x, int y) { switch(pentype) { case LINE: drawPenLine(startx, starty, x, y); break; case TANGENTLINE: // fall through case PERPLINE: Point p[]; int px,py; if(indirect) { px = gx(Calc.calc(funcstrx, tval, 't')); py = gy(Calc.calc(funcstry, tval, 't')); if(pentype == TANGENTLINE) p = tangentline(tval); else p = perpline(tval); } else { px = x; py = gy(Calc.calc(funcstr, mx(x))); if(pentype == TANGENTLINE) p = tangentline(x); else p = perpline(x); } // クロスを消去 drawXORLine(px, py-POINTCROSS, px, py+POINTCROSS); drawXORLine(px-POINTCROSS, py, px+POINTCROSS, py); // 決定線を引く drawPenLine(p[0].x, p[0].y, p[1].x, p[1].y); break; } return true; } public void paint(Graphics g) { if(sheetimg != null) g.drawImage(sheetimg, 0, 0, null); } private double inctvalue(double tval, int beforex, int beforey, int x, int y) { double br = Math.atan2(my(beforey), mx(beforex)); double r = Math.atan2(my(y), mx(x)); if(Math.abs(r-br) > Math.PI) { if(br < 0) tval += -(Math.PI*2-Math.abs(r)-Math.abs(br))/10; else tval += (Math.PI*2-Math.abs(r)-Math.abs(br))/10; } else tval += (r-br)/10; return tval; } private Point[] tangentline(int x) { double px = mx(x); double py = Calc.calc(funcstr, px); double tilt = Calc.calc(diffuncstr, px); return getTiltLine(tilt, px, py); } private Point[] tangentline(double t) { return getTiltLine(Calc.calc(diffuncstr, t, 't'), Calc.calc(funcstrx, t, 't'), Calc.calc(funcstry, t, 't')); } private Point[] perpline(int x) { double px = mx(x); double py = Calc.calc(funcstr, px); double tilt = -1/Calc.calc(diffuncstr, px); return getTiltLine(tilt, px, py); } private Point[] perpline(double t) { return getTiltLine(-1/Calc.calc(diffuncstr, t, 't'), Calc.calc(funcstrx, t, 't'), Calc.calc(funcstry, t, 't')); } private Point[] getTiltLine(double tilt, double px, double py) { double fromx, fromy, tox, toy; if(Math.abs(tilt)<1) { fromx = mx(0); tox = mx(this.size().width); fromy = tilt*(fromx - px) + py; toy = tilt*(tox - px) + py; } else { fromy = my(0); toy = my(this.size().height); fromx = (1/tilt)*(fromy - py) + px; tox = (1/tilt)*(toy - py) + px; } Point result[] = { new Point(gx(fromx), gy(fromy)), new Point(gx(tox), gy(toy)) }; return result; } private void drawPenLine(int fromx, int fromy, int tox, int toy) { Graphics g = this.getGraphics(); g.setColor(pencolor); g.drawLine(fromx, fromy, tox, toy); g.drawLine(fromx+1, fromy, tox+1, toy); g.drawLine(fromx, fromy+1, tox, toy+1); g.drawLine(fromx+1, fromy+1, tox+1, toy+1); } private void drawXORLine(int fromx, int fromy, int tox, int toy) { Graphics g = this.getGraphics(); g.setXORMode(pencolor); g.drawLine(fromx, fromy, tox, toy); g.setPaintMode(); } /** * 落書きペンの色を設定 * @param c 色 */ public void setPenColor(Color c) { pencolor = c; } /** * グラフの色を設定 * @param c 色 */ public void setGraphColor(Color c) { graphcolor = c; } /** * 落書きペンのツールを設定 * @param pt ペンの種類 */ public void setPentype(int pt) { pentype = pt; } /** * グラフシートの初期化 */ public void clearGraph() { Dimension d = this.size(); if(d.width == 0) d = new Dimension(100, 100); int center = d.width/2; int middle = d.height/2; // グラフシートに新規イメージを割り当てる sheetimg = createImage(d.width, d.height); graph = sheetimg.getGraphics(); int ed = Math.max(center, middle)/ONE+1; for(int i = 1; i < ed; i++) { // グリッドラインを引く graph.setColor(GRID_COLOR); graph.drawLine( i*ONE+center, 0, i*ONE+center, d.height); graph.drawLine(-i*ONE+center, 0, -i*ONE+center, d.height); graph.drawLine(0, i*ONE+middle, d.width, i*ONE+middle); graph.drawLine(0, -i*ONE+middle, d.width, -i*ONE+middle); // 座標軸の数字を書く graph.setColor(NUMBER_COLOR); graph.drawString(Integer.toString( i), i*ONE+center, middle); graph.drawString(Integer.toString(-i), -i*ONE+center, middle); graph.drawString(Integer.toString(-i), center, i*ONE+middle); graph.drawString(Integer.toString( i), center, -i*ONE+middle); } // 原点Oを書く graph.drawString("O", center, middle); // x軸y軸を引く graph.setColor(XYBAR_COLOR); graph.drawLine(center, 0, center, d.height); graph.drawLine(0, middle, d.width, middle); repaint(); } /** * y=xの式のグラフの描画 * @param func xの式 * @param line 線で繋ぐならtrue * @param jump プロットの間隔 */ public void drawGraph(String func, boolean line, double jump) { drawGraph(func, line, jump, mx(0), mx(this.size().width)); } /** * y=xの式のグラフの描画 * @param func xの式 * @param line 線で繋ぐならtrue * @param jump プロットの間隔 * @param fromx xの開始値 * @param tox xの終了値 */ public void drawGraph(String func, boolean line, double jump, double fromx, double tox) { // グラフシートが作成されていない場合は初期化 if(sheetimg == null) clearGraph(); // 接線描画のための微分 funcstr = func; diffuncstr = Differentiate.differentiate(funcstr); indirect = false; // 進行状況表示のためフォントサイズを取得 Graphics g = this.getGraphics(); int fh = g.getFontMetrics().getHeight(); int fa = g.getFontMetrics().getAscent(); Dimension d = this.size(); // グラフ描画 int ed = (int)((tox-fromx)/jump); double x; double y; graph.setColor(graphcolor); if(line) { // ラインを引く場合 // 最初の点を保存する double bx = fromx; double by = Calc.calc(func, bx); int fx, fy, tx, ty; // 2つめの点からループ開始 for(int i = 1; i < ed; i++) { x = fromx + i*jump; y = Calc.calc(func, x); // 進行状況の表示 g.clearRect(0, 0, d.width, fh*2); g.drawString("x =" + Double.toString(x), 0, fa); g.drawString("y =" + Double.toString(y), 0, fh+fa); // ペン幅2の線を引く fx = gx(bx); fy = gy(by); tx = gx(x); ty = gy(y); graph.drawLine(fx, fy, tx, ty); graph.drawLine(fx+1, fy, tx+1, ty); graph.drawLine(fx, fy+1, tx, ty+1); graph.drawLine(fx+1, fy+1, tx+1, ty+1); bx = x; by = y; } } else for(int i = 0; i < ed; i++) { // 点を打つ場合 x = fromx + i*jump; y = Calc.calc(func, x); // 進行状況の表示 g.clearRect(0, 0, d.width, fh*2); g.drawString("x =" + Double.toString(x), 0, fa); g.drawString("y =" + Double.toString(y), 0, fh+fa); // 点を打つ graph.drawRect(gx(x), gy(y), 1, 1); } repaint(); } /** * 媒介変数表示のグラフの描画 * @param xstr x=tの式 * @param ystr x=tの式 * @param line 線で繋ぐならtrue * @param jump tの間隔 * @param fromt tの開始 * @param tot tの終了 */ public void drawGraph(String xstr, String ystr, boolean line, double jump, double fromt, double tot) { // グラフシートが作成されていない場合は初期化 if(sheetimg == null) clearGraph(); // 接線描画のための微分 funcstrx = xstr; funcstry = ystr; diffuncstr = "(" + Differentiate.differentiate(ystr, 't') + ")/(" + Differentiate.differentiate(xstr, 't') + ")"; indirect = true; // 進行状況表示のためフォントサイズを取得 Graphics g = this.getGraphics(); int fh = g.getFontMetrics().getHeight(); int fa = g.getFontMetrics().getAscent(); Dimension d = this.size(); // グラフ描画 int ed = (int)((tot-fromt)/jump); double x; double y; double t; graph.setColor(graphcolor); if(line) { // ラインを引く場合 // 最初の点を保存する t = fromt; double bx = Calc.calc(xstr, t, 't'); double by = Calc.calc(ystr, t, 't'); int fx, fy, tx, ty; // 2つめの点からループ開始 for(int i = 1; i < ed; i++) { t = fromt+i*jump; x = Calc.calc(xstr, t, 't'); y = Calc.calc(ystr, t, 't'); // 進行状況の表示 g.clearRect(0, 0, d.width, fh*3); g.drawString("t =" + Double.toString(t), 0, fa); g.drawString("x =" + Double.toString(x), 0, fh+fa); g.drawString("y =" + Double.toString(y), 0, fh*2+fa); fx = gx(bx); fy = gy(by); tx = gx(x); ty = gy(y); // ペン幅2の線を引く graph.drawLine(fx, fy, tx, ty); graph.drawLine(fx+1, fy, tx+1, ty); graph.drawLine(fx, fy+1, tx, ty+1); graph.drawLine(fx+1, fy+1, tx+1, ty+1); bx = x; by = y; } } else for(int i = 0; i < ed; i++) { t = fromt+i*jump; x = Calc.calc(xstr, t, 't'); y = Calc.calc(ystr, t, 't'); // 進行状況の表示 g.clearRect(0, 0, d.width, fh*3); g.drawString("t =" + Double.toString(t), 0, fa); g.drawString("x =" + Double.toString(x), 0, fh+fa); g.drawString("y =" + Double.toString(y), 0, fh*2+fa); // 点を打つ graph.drawRect(gx(x), gy(y), 1, 1); } repaint(); } // 数学座標 to スクリーン座標 private int gx(double x) { Dimension d = this.size(); return (int)(x*ONE+d.width/2); } private int gy(double y) { Dimension d = this.size(); // 正負の無限大またはNot a numberのときは点を画面外に飛ばす if(Double.isInfinite(y)) return -10; if(Double.isNaN(y)) return -20; return (int)(-y*ONE+d.height/2); } // スクリーン座標 to 数学座標 private double mx(int x) { Dimension d = this.size(); return (double)(x-d.width/2)/ONE; } private double my(int y) { Dimension d = this.size(); return (double)-(y-d.height/2)/ONE; } }