import java.awt.*; import java.util.Enumeration; import java.util.Hashtable; /** * A class that represents a basic graphical picture of a portion * of some plane, with axes and tickmarks, if so desired. * * @author Jonathan A. Poritz * @version 1.7, 17 Sep 1998 */ public class Plane extends Canvas { /** * The abstract coordinates of the upper-left corner of the visible window. */ public double corner[] = new double[2]; /** * The width and height, in abstract coordinates, of the visible window. */ public double size[] = new double[2]; /** * The width and height, in pixels, of the visible window. */ public int prefsize[] = new int[2]; /** * The original corner coordinates, width and height, in abstract units, * of the visible window, to be used when we wish to reset. */ protected double reset[] = new double[4]; /** * Should we draw the axes on this plane. */ public boolean draw_axes = false; /** * Should we draw tickmarks on the x-border of the visible window. */ public boolean show_xticks = true; /** * Log of ten, used in making tickmarks. */ public static final double LOGOFTEN = 2.3025851D; /** * Constructor for Planes. * * @param cx the abstract coordinates of the left side of the visble window * @param cy the abstract coordinates of the top edge of the visble window * @param sx the width of the visible window, in abstract units * @param sy the height of the visible window, in abstract units * @param x the width of the window in pixels * @param y the height of the window in pixels */ public Plane(double cx, double cy, double sx, double sy, int x, int y) { // make the ambient Canvas super(); // save the input values, including the copy for future resetting corner[0] = cx; corner[1] = cy; size[0] = sx; size[1] = sy; reset[0] = cx; reset[1] = cy; reset[2] = sx; reset[3] = sy; prefsize[0] = x; prefsize[1] = y; } /** * Draws tickmarks on the lower and left edges of this's visible window; * lower edge tickmarks only drawn if show_xticks is true. * * @param g the graphics object upon which to draw */ public void drawTickmarks(Graphics g) { // set the font for the tickmark labels, color for ticks and labels g.setFont(new Font("courier", Font.PLAIN, 10)); g.setColor(Color.gray); // the following is a rather idiosycratic way of choosing // the starting point for the tickmarks and the distance // between successive marks, but it produces pleasing results // perhaps we should really catch the Arithmetic exceptions that could // be thrown by Math.log and Math.pow double powy = Math.floor(Math.log(size[1]/10D)/LOGOFTEN); double incy = Math.pow(10D, powy); double starty = corner[1] - size[1]; if (powy != 0D) { starty *= 10D * powy; starty = Math.round(starty); starty /= 10D * powy; } else starty = Math.round(starty); incy *= Math.floor(size[1] / incy / 6D); // start making the y tickmarks // this will be the index of the tickmark for (double i = corner[1] - size[1]; i < corner[1]; i += incy) { // i is the abstract y coordinate of the tickmark, // compute its pixel coords int xy[] = abstractToPixels(corner[0], i); // now draw the tickmark g.drawLine(xy[0], xy[1], xy[0]+5, xy[1]); // label the tickmark g.drawString(Complex.DoubleToString(i, 3), xy[0]+7, xy[1]+4); } // return if we are not to do the x tickmarks if (!show_xticks) return; // so do the x tickmarks. now ditto on above remark about // the following strange computations and catching exceptions double powx = Math.floor(Math.log(size[0]/10D)/LOGOFTEN); double incx = Math.pow(10D, powx); double startx = corner[0]; if (powx != 0) { startx *= 10D * powx; startx = Math.round(startx); startx /= 10D * powx; } else startx = Math.round(startx); incx *= Math.floor(size[0] / incx / 6D); // here is the index of the tickmarks for (double i = corner[0]; i < corner[0]+size[0]; i += incx) { // i is the abstract x coordinate of the tickmark, // compute its pixel coords int xy[] = abstractToPixels(i, corner[1] - size[1]); // now draw the tickmark g.drawLine(xy[0], xy[1], xy[0], xy[1]-5); // label the tickmark g.drawString(Complex.DoubleToString(i, 3), xy[0], xy[1]-10); } } /** * Paint this Plane. * * @param g the graphics object upon which to paint */ public void paint(Graphics g) { // draw the basic white rectangle of the Plane g.setColor(Color.white); g.fillRect(0, 0, getSize().width, getSize().height); if (draw_axes) { // we should also draw the axes int origin[] = abstractToPixels(0, 0); g.setColor(Color.gray); g.drawLine(origin[0], -getSize().height, origin[0], getSize().height); g.drawLine(-getSize().width, origin[1], getSize().width, origin[1]); } // go back to black for future drawing g.setColor(Color.black); } /** * Gives the preferred size, at which this Plane was created. * * @return a Dimension of the Plane's original size, in pixels. */ public Dimension getPreferredSize() { return new Dimension(prefsize[0], prefsize[1]); } /** * Resets the UL corner of this plane and its width and height to be as * when first created (in abstract units), then repaints. */ public void reset() { corner[0] = reset[0]; corner[1] = reset[1]; size[0] = reset[2]; size[1] = reset[3]; repaint(); } /** * Makes a string containing the ranges of coordinates, in abstract units, * that are currently visible in this Plane; only shows the y range if * show_xticks is false. * * @return the String showing this Plane's abstract dimensions */ public String toString() { if (show_xticks) { return new String("["+Complex.DoubleToString(corner[0], 5)+","+ Complex.DoubleToString(corner[0]+size[0], 5)+"] x ["+ Complex.DoubleToString(corner[1]-size[1], 5)+","+ Complex.DoubleToString(corner[1], 5)+"]"); } return new String("["+Complex.DoubleToString(corner[1]-size[1], 5)+","+ Complex.DoubleToString(corner[1], 5)+"]"); } /** * Convert pixel coordinates to abstract in this Plane's window. * * @param x an input x coordinate, in pixels * @param y an input y coordinate, in pixels * @return an array of two doubles telling the corresponding x and * y abstract coordinates */ public double[] pixelsToAbstract(int x, int y) { double v[] = new double[2]; double factorx = ((double)getSize().width) / size[0]; double factory = ((double)getSize().height) / size[1]; v[0] = ((double)x / factorx) + corner[0]; v[1] = ((double)y - (double)getSize().height) / (-1*factory) + (corner[1] - size[1]); return v; } /** * Convert abstract coordinates to pixels in this Plane's window. * * @param x an input x coordinate, in abstract units * @param y an input y coordinate, in abstract units * @return an array of two ints telling the corresponding x and * y pixel coordinates */ public int[] abstractToPixels(double x, double y) { int v[] = new int[2]; double factorx = ((double)getSize().width) / size[0]; double factory = ((double)getSize().height) / size[1]; v[0] = (int)((x - corner[0]) * factorx); v[1] = (int)(-1*(y - (corner[1] - size[1])) * factory + getSize().height); return v; } }