package gve.calc;
import java.awt.*;
import gve.calc.formula.*;
public class PlotCanvas extends Canvas
{
public static final byte DOTS = 0;
public static final byte CONNECTDOTS = 1;
public static final byte INTERVALS = 2;
private byte plotMode;
private Formula [] funcs;
private double xmin = -10, xmax = 10, ymin = -10, ymax = 10;
private Color [] defaultColors = { Color.black, Color.green,Color.blue };
private Layer userlayer;
Animation threads can use isPlotting() to check when the
picture has been updated |
private boolean plotting = false;
private boolean dbuffer = false;
private Image offscreen;
public PlotCanvas() {
setBackground(Color.white);
setForeground(Color.black);
plotMode = CONNECTDOTS;
}
public PlotCanvas(Formula f) {
this();
setFunction(f);
}
public void addFunction(Formula f) {
Formula [] newfuncs;
if (funcs == null) newfuncs = new Formula[1];
else newfuncs = new Formula[funcs.length+1];
System.arraycopy(funcs,0,newfuncs,0,funcs.length);
newfuncs[funcs.length] = f;
funcs = newfuncs;
}
public void setFunction(Formula f) {
funcs = new Formula[1];
funcs[0] = f;
repaint();
}
public void setDbuffer(boolean b) { dbuffer = b; }
public void setLayer(Layer l) { userlayer = l; }
public void removeFunctions() { funcs = null; repaint(); }
public void setPlotMode(byte mode) { plotMode = mode; repaint(); }
public void setXmin(double d) { xmin = d; repaint(); }
public void setXmax(double d) { xmax = d; repaint(); }
public void setYmin(double d) { ymin = d; repaint(); }
public void setYmax(double d) { ymax = d; repaint(); }
public void setXrange(double min,double max) {
xmin = min; xmax = max; repaint();
}
public void setYrange(double min,double max) {
ymin = min; ymax = max; repaint();
}
public void setRanges(double xmin,double xmax,double ymin,double ymax) {
this.xmin = xmin; this.xmax = xmax;
this.ymin = ymin; this.ymax = ymax;
repaint();
}
public double getXmin() { return xmin; }
public double getXmax() { return xmax; }
public double getYmin() { return ymin; }
public double getYmax() { return ymax; }
public boolean isPlotting() { return plotting; }
Zoom in on a detail. (factor < 1) or zoom out to show more (zoom > 1) |
public void zoom(double factor) {
double width = xmax-xmin;
double height = ymax-ymin;
xmin = xmin + width/2 - width*factor/2;
xmax = xmin + width*factor;
ymin = ymin + height/2 - height*factor/2;
ymax = ymin + height*factor;
repaint();
}
This is basically a stripped down version of paintConnectDots() |
private void paintDots(Graphics g,Formula func) {
Dimension bound = getSize();
double step = (xmax-xmin) / bound.width;
Evaluator ev = new MultiEvaluator();
StandardFunctions.registerFunctions(ev);
double x;
int screenX = 0;
Real dv = new Real(0.0);
ev.defineVariable("x",dv);
for (x=xmin; x<=xmax; x+=step,screenX++) {
dv.doubleValue = x;
Part res = func.evaluate(ev);
if (res instanceof Real) {
double y = ((Real)res).doubleValue;
if (y<ymin || y>ymax) continue;
int screenY = (int)((ymax-y)/(ymax-ymin)*bound.height);
g.drawLine(screenX,screenY,screenX,screenY);
}
}
}
private void paintConnectDots(Graphics g,Formula func) {
Dimension bound = getSize();
double step = (xmax-xmin) / bound.width;
Evaluator ev = new MultiEvaluator();
StandardFunctions.registerFunctions(ev);
// initialization of prevY is not necessary, but else
// the compiler complains
int prevY = 0;
boolean previous = false;
double x;
int screenX = 0;
Real dv = new Real(0.0);
ev.defineVariable("x",dv);
for (x=xmin; x<=xmax; x+=step,screenX++) {
dv.doubleValue = x;
Part res = func.evaluate(ev);
if (res instanceof Real) {
double y = ((Real)res).doubleValue;
if (y<ymin || y>ymax) {
previous = false;
continue;
}
int screenY = (int)((ymax-y)/(ymax-ymin)*bound.height);
if (previous) {
g.drawLine(screenX-1,prevY,screenX,screenY);
} else {
previous = true;
// silly, but plots a pixel
g.drawLine(screenX,screenY,screenX,screenY);
}
prevY = screenY;
} else previous = false;
}
}
private void paintIntervals(Graphics g,Formula func) {
Dimension bound = getSize();
double step = (xmax-xmin) / bound.width;
int height = bound.height;
Evaluator ev = new MultiEvaluator();
StandardFunctions.registerFunctions(ev);
double x;
int screenX = 0;
Interval interv = new Interval(0,1,false,true);
ev.defineVariable("x",interv);
for (x=xmin; x<=xmax; x+=step,screenX++) {
interv.low = x;
interv.high = x + step;
//System.out.print(interv);
Part res = func.evaluate(ev);
paintInterval(res,g,screenX,height);
}
}
Helper function for paintIntervals() |
private void paintInterval(Part obj,Graphics g,int screenX,int height) {
//System.out.println("\t->"+obj);
if (obj instanceof Interval) {
Interval interv = (Interval)obj;
int screenL;
if (interv.low < ymin) screenL = height;
else if (interv.low > ymax) return;
else screenL = (int)((ymax-interv.low)/(ymax-ymin)*height);
int screenH;
if (interv.high < ymin) return;
else if (interv.high > ymax) screenH = 0;
else screenH = (int)((ymax-interv.high)/(ymax-ymin)*height);
//System.out.println("->>> plot pixels "+screenL+" --- "+screenH);
g.drawLine(screenX,screenL,screenX,screenH);
} else if (obj instanceof OperatorUnion) {
OperatorUnion op = (OperatorUnion)obj;
paintInterval(op.left,g,screenX,height);
paintInterval(op.right,g,screenX,height);
}
}
public int xToPix(double x) {
return (int)((x-xmin)/(xmax-xmin)*getSize().width);
}
public int yToPix(double y) {
return (int)((ymax-y)/(ymax-ymin)*getSize().height);
}
public void invalidate() {
super.invalidate();
offscreen = null;
}
public void update(Graphics g) {
if (dbuffer) paint(g);
else super.update(g);
}
public void paint(Graphics g) {
if (funcs == null) return;
plotting = true;
Graphics oldg = null;
Dimension siz = getSize();
if (dbuffer) {
if (offscreen == null)
offscreen = createImage(siz.width,siz.height);
oldg = g;
g = offscreen.getGraphics();
g.setClip(0,0,siz.width,siz.height);
g.setColor(getBackground());
g.fillRect(0,0,siz.width,siz.height);
g.setColor(Color.black);
}
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
if (userlayer != null) {
userlayer.paintLayer(g,siz.width,siz.height);
}
for (int i = funcs.length-1; i>=0; i--) {
Formula f = funcs[i];
if (f == null) continue;
if (i < defaultColors.length) g.setColor(defaultColors[i]);
else g.setColor(Color.black);
switch (plotMode) {
case DOTS: paintDots(g,f); break;
case CONNECTDOTS: paintConnectDots(g,f); break;
case INTERVALS: paintIntervals(g,f); break;
}
}
if (dbuffer) {
oldg.drawImage(offscreen,0,0,null);
g.dispose();
}
setCursor(Cursor.getDefaultCursor());
plotting = false;
}
}