/* PlotCanvas.java * Basic 2-D Plotting Canvas * 20-July-1997 * Author: Bryan Lewis * Kent State University * Department of Mathematics & Computer Science * mail: blewis@rocketcalc.com * url: http://www.mcs.kent.edu/~blewis/ * For use with JVM 1.1x and higher * Revisions: * October, 98: Added a fixed plot scale option, * a text component (plotText), a clear method * September, 1998: Added aspect ratio control variable, * plotStyle variable for lines or dots, multi-color plotting, * addapted for plotting ellipses for dots. * Revised: August, 2003 * * January, 2004: Added linear interpolant style, fade method, displayData * catesian(j,k) and screen(x,y) functions * */ import java.awt.*; import java.lang.*; import java.util.Vector; import java.util.Enumeration; class PlotCanvas extends Canvas { // coordinate system: double xmax, xmin, ymax, ymin; int xcenter, ycenter; /* The plot area dimensions */ Dimension d; /* Scaling */ // change autoScale to false to set scale manually boolean autoScale=true; // set squareAspectRatio to true for a square plot boolean squareAspectRatio = false; // set ticks to true to display tick marks on the axes boolean ticks = true; boolean gridlines=false; int ntick=4; Color axesColor = new Color(180,180,180); Color tickColor = new Color(180,180,180); Vector points; PlotCanvas() { /* The constructor method for this object: most initialization * steps take place here. */ super(); points=new Vector(); } public void clear(){ /* The clear method clears the list of points and * redraws the plot (effectively clearing the plot). */ points=new Vector(); repaint(); } public void plot(MPoint p) { /* Add an MPoint object p to the list of points * and redraw the plot. */ points.addElement(p); repaint(); } public void plot(double x, double y) { /* Create a point object with the given x and y coordinates, * add it to the list of points and redraw the plot. */ MPoint p=new MPoint(x,y); points.addElement(p); repaint(); } public void plot(Vector v) { /* Add a whole list of points to the list of points * to be plotted and redraw the plot. */ for(Enumeration e=v.elements();e.hasMoreElements();) points.addElement((MPoint)e.nextElement()); repaint(); } public void plot1D(Vector v) { /* Plot a vector of MPoints using the position in the list * as the x-coordinate. Don't count points that have non-null * text fields. */ double j=1; MPoint p; points=new Vector(); for (Enumeration e=v.elements();e.hasMoreElements();) { p=(MPoint)e.nextElement(); p.x=(double)j; p.setSize(5); points.addElement(p); if(p.text=="") j=j+1; } repaint(); } public void setSquareAspectRatio (boolean b) { squareAspectRatio = b; } public void setAutoScale(boolean b) { autoScale=b; } public void setScale(double xmn,double xmx, double ymn, double ymx) { autoScale=false; xmin=xmn; xmax=xmx; ymin=ymn; ymax=ymx; } public MPoint cartesian(int j, int k) { /* Convert the screen pixel coordinates j,k to Cartesian coordinates */ MPoint p=new MPoint(); p.x = (double)((j - xcenter)*(xmax - xmin)) / (double)d.width; p.y = (double)((ycenter - k)*(ymax - ymin)) / (double)d.height; return p; } public java.awt.Point screen(double x, double y) { /* Convert the Cartesian coordinates into screen coordinates */ java.awt.Point p=new java.awt.Point(); p.x = xcenter + (int)(x*d.width/(xmax-xmin)); p.y = ycenter - (int)(y*d.height/(ymax-ymin)); return p; } public void setTicks(boolean b) { ticks = b; } public void setGridlines(boolean b){ gridlines=b; } public void lighter() { /* Lighten the color of all existing points */ Vector v=new Vector(); Enumeration e=points.elements(); while(e.hasMoreElements()) { MPoint p=(MPoint)e.nextElement(); if(p.style != MPoint.STYLE_TEXT) { p.brighter(); } v.addElement(p); } points=v; } public void fade() { /* Lighten the color of all existing points */ Vector v=new Vector(); Color c; int r,g,b; Enumeration e=points.elements(); while(e.hasMoreElements()) { MPoint p=(MPoint)e.nextElement(); if(p.style != MPoint.STYLE_TEXT) { c=p.getColor(); g=c.getGreen(); r=c.getRed(); b=c.getBlue(); if( (r-b)*2 + (b-g)*3 + (r-g) !=0) { /* Not already gray...make it so */ p.setColor(new Color(190,190,190)); } else { r=Math.min(r+5,255); p.setColor(new Color(r,r,r)); } } c=p.getColor(); g=c.getGreen(); r=c.getRed(); b=c.getBlue(); /* Remove faded out points from the plot */ if( (r<255) || (g<255) || (b<255) ) v.addElement(p); } points=v; } public void setColor(Color c) { /* Set the color of all existing points */ Vector v=new Vector(); Enumeration e=points.elements(); while(e.hasMoreElements()) { MPoint p=(MPoint)e.nextElement(); p.setColor(c); v.addElement(p); } points=v; } public void paint(Graphics g) { /* The paint method is responsible for displaying the plot * on the screen. We will add double-buffering to this later * if the plot exhibits flicker or other display problems. */ /* draw border, ticks, first * checking coordinate system bounds */ d = getSize(); if(autoScale){ xmax=0;xmin=0;ymax=0;ymin=0; for(Enumeration e=points.elements();e.hasMoreElements();){ MPoint p=(MPoint)e.nextElement(); if(p.style != MPoint.STYLE_TEXT){ if(p.x > xmax) xmax=p.x; if(p.x < xmin) xmin=p.x; if(p.y > ymax) ymax=p.y; if(p.y < ymin) ymin=p.y; } } /* This is a little arbitrary... */ xmax=xmax+1; ymax=ymax+1; xmin=xmin-1; ymin=ymin-1; } if(xmax==0 && xmin==0) { xmax=1; xmin=-1; } if(ymax==0 && ymin==0) { ymax=1; ymin=-1; } if(squareAspectRatio){ // make square... double s; s=Math.max(xmax,ymax); s=Math.max(s,Math.abs(xmin)); s=Math.max(s,Math.abs(ymin)); xmax=s; xmin=-s; ymax=s; ymin=-s; } double x,y,xx,yy; x=(double)d.width; y=(double)d.height; xcenter=(int)Math.abs(xmin*x/(xmax-xmin)); ycenter=(int)Math.abs(ymax*y/(ymin-ymax)); g.setColor(Color.white); g.fillRect(0,0,d.width-1, d.height-1); g.setColor(Color.black); g.draw3DRect(0,0, d.width-1, d.height-1, true); plotAxes(g); plotData(g); } void plotAxes(Graphics g) { double x,y,xx,yy; d = getSize(); /* Draw the tick marks XXX add labels too * XXX make more flexible */ g.setColor(tickColor); if(ticks){ double dx=Math.pow(10,(int)(Math.log(xmax)/Math.log(10)))/ntick; double dy=Math.pow(10,(int)(Math.log(ymax)/Math.log(10)))/ntick; xx=dx; while(xxxmin){ x = xx*d.width/(xmax-xmin); if(gridlines) g.drawLine((int)(xcenter+x),0, (int)(xcenter+x),d.height); else g.drawLine((int)(xcenter+x),(int)(ycenter-2), (int)(xcenter+x),(int)(ycenter+2)); xx-=dx; } yy=dy; while(yyymin){ y = yy*d.height/(ymax-ymin); if(gridlines) g.drawLine(0,(int)(ycenter-y), d.width,(int)(ycenter-y)); else g.drawLine((int)(xcenter+2),(int)(ycenter-y), (int)(xcenter-2),(int)(ycenter-y)); yy-=dy; } } /* Draw the axes */ g.setColor(axesColor); g.drawLine(xcenter, 0, xcenter, d.height ); g.drawLine(0, ycenter, d.width, ycenter ); } void plotData(Graphics g) { /* Plot the data points */ double x,y,x0,y0; MPoint p,p0=null; for(Enumeration e=points.elements();e.hasMoreElements();){ p=(MPoint)e.nextElement(); if(p0==null) p0=p; g.setColor(p.color); /* XXX Add additional point styles here */ switch(p.style){ case MPoint.STYLE_TEXT: /* text */ x = p.x; y = p.y - getFontMetrics(getFont()).getAscent() - 2*getFontMetrics(getFont()).getDescent(); g.drawString(p.getText(),(int)x,(int)y); break; case MPoint.STYLE_RECT: /* rectangle */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); g.drawRect((int)(xcenter+x-p.size),(int)(ycenter-y-p.size), 2*p.size, 2*p.size); break; case MPoint.STYLE_CROSS: /* cross */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); g.drawLine((int)(xcenter+x),(int)(ycenter-y-p.size), (int)(xcenter+x),(int)(ycenter-y+p.size)); g.drawLine((int)(xcenter+x-p.size),(int)(ycenter-y), (int)(xcenter+x+p.size),(int)(ycenter-y)); break; case MPoint.STYLE_LINTERP: /* linear interpolation between points -- size ignored */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); x0 = p0.x*d.width/(xmax-xmin); y0 = p0.y*d.height/(ymax-ymin); g.drawLine((int)(xcenter+x),(int)(ycenter-y), (int)(xcenter+x0),(int)(ycenter-y0)); break; default: /* disc */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); if(p.size<2) g.drawLine((int)(xcenter+x),(int)(ycenter-y), (int)(xcenter+x),(int)(ycenter-y)); else { x = x - p.size/2; y = y + p.size/2; g.fillOval((int)(xcenter+x),(int)(ycenter-y),p.size,p.size); } break; } p0=p; } } void displayData(Graphics g, Vector v) { displayData(g,v,null); } void displayData(Graphics g, Vector v, Color fixedColor) { /* Plot non-saved data points */ double x,y,x0,y0; MPoint p,p0=null; if(fixedColor!=null) g.setColor(fixedColor); for(Enumeration e=v.elements();e.hasMoreElements();){ p=(MPoint)e.nextElement(); if(p0==null) p0=p; if(fixedColor==null) g.setColor(p.color); /* XXX Add additional point styles here */ switch(p.style){ case MPoint.STYLE_TEXT: /* text */ x = p.x; y = p.y - getFontMetrics(getFont()).getAscent() - 2*getFontMetrics(getFont()).getDescent(); g.drawString(p.getText(),(int)x,(int)y); break; case MPoint.STYLE_RECT: /* cross */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); g.drawRect((int)(xcenter+x-p.size),(int)(ycenter-y-p.size), 2*p.size, 2*p.size); break; case MPoint.STYLE_CROSS: /* cross */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); g.drawLine((int)(xcenter+x),(int)(ycenter-y-p.size), (int)(xcenter+x),(int)(ycenter-y+p.size)); g.drawLine((int)(xcenter+x-p.size),(int)(ycenter-y), (int)(xcenter+x+p.size),(int)(ycenter-y)); break; case MPoint.STYLE_LINTERP: /* linear interpolation between points -- size ignored */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); x0 = p0.x*d.width/(xmax-xmin); y0 = p0.y*d.height/(ymax-ymin); g.drawLine((int)(xcenter+x),(int)(ycenter-y), (int)(xcenter+x0),(int)(ycenter-y0)); break; default: /* disc */ x = p.x*d.width/(xmax-xmin); y = p.y*d.height/(ymax-ymin); if(p.size<2) g.drawLine((int)(xcenter+x),(int)(ycenter-y), (int)(xcenter+x),(int)(ycenter-y)); else { x = x - p.size/2; y = y + p.size/2; g.fillOval((int)(xcenter+x),(int)(ycenter-y),p.size,p.size); } break; } p0=p; } } }