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;
}
}