import java.awt.*; import java.awt.event.*; /** * A class which shows the fundamental domain corresponding to the current * trace value and display parameters, on a portion of the boundary of * hyperbolic space. * * @author Jonathan A. Poritz * @version 1.7, 17 Sep 1998 */ public class BoundaryPanel extends Plane { /** * The (1,1) entry of the element cannonically associated with the current * trace. */ public Complex z11 = new Complex(); /** * The currently selected axis distance. */ public double dist; /** * A reference to the label in outputpanel declaring the portion of * the boundary of hyperbolic space which is currently visible. * * @see OutputPanel#boundarypanellabel */ public Label boundarypanellabel; /** * The maxpow calculated for a Ford domain with the current trace -- not * necessarily valid for a Dirichlet domain with the same trace, but used * nonetheless. */ public int calcmaxpow; /** * The overriding maxpow chosen by the user if so desired taken from * maxpowchoice. */ public int chosenmaxpow; /** * A boolean telling if we should use the calcmaxpow or some other * choice, to be found in chosenmaxpow. */ public boolean usecalcmaxpow; /** * A reference to the choice in outputpanel of displaying disks, * circles or circle with labeling indices. * * @see OutputPanel#diskorcircle */ public Choice diskorcircle; /** * A reference to the choice in outputpanel of computing Ford or * Dirichlet domains. * * @see OutputPanel#fordordirichlet */ public Choice fordordirichlet; /** * An array of BoundaryCircles with positive indices. */ protected BoundaryCircle pc[] = null; /** * An array of BoundaryCircles with negative indices. */ protected BoundaryCircle nc[] = null; /** * The location of a mousedown in this plane, to compute the extent of * a drag that may occur; in abstract coordinates. */ protected double[] prev = new double[2]; /** * The location of a mousedown in this plane, to compute the extent of * a drag that may occur; in pixel coordinates. */ protected int[] previ = new int[2]; /** * The abstract x-coordinate of the left side of the boundary plane display. */ public static final double CX = -1.5D; /** * The abstract y-coordinate of the upper edge of the boundary plane display. */ public static final double CY = 2D; /** * The width, in abstract units, of the boundary plane display. */ public static final double SX = 4D; /** * The height, in abstract units, of the boundary plane display. */ public static final double SY = 4D; /** * The width, in pixels, of the boundary plane display. Good defaults are: * */ public static final int X = 300; /** * The height, in pixels, of the boundary plane display. Good defaults are: * */ public static final int Y = 300; /** * Constructor for BoundaryPanels. * * @param bdrypnllbl the label describing the current window on the boundary * plane * @param dskrcrcl the choice of display styles of the isometric or * equidistant circles * @param frdrdrchlt the choice of making Ford or Dirichlet domains. */ public BoundaryPanel(Label bdrypnllbl, Choice dskrcrcl, Choice frdrdrchlt) { // creat the ambient plane, with axes and xticks shown super(CX, CY, SX, SY, X, Y); draw_axes = true; show_xticks = true; // we start using the calculated maxpows usecalcmaxpow = true; // save the references to the boundary panel label, disk or circle choice // and Ford or Dirichlet domain choice boundarypanellabel = bdrypnllbl; diskorcircle = dskrcrcl; fordordirichlet = frdrdrchlt; // set the boundary panel label correctly boundarypanellabel.setText(this.toString()); // deal with mouse presses this.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { // get the pixel coordinates of the press int x = e.getX(); int y = e.getY(); // always save pixel location of mouseDown previ[0] = x; previ[1] = y; if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { // right button zoom, all we needed was to save location of // mouseDown, can now return return; } // left or middle button translate, save abstract coordinates as well prev[0] = (pixelsToAbstract(x, y))[0]; prev[1] = (pixelsToAbstract(x, y))[1]; } }); // deal with mouse drags this.addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { // get the current pixel coordinates int x = e.getX(); int y = e.getY(); // convert to abstract coordinates double xx = (pixelsToAbstract(x, y))[0]; double yy = (pixelsToAbstract(x, y))[1]; if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { // right button zoom zoom(1D - ((double)(previ[1] - y)) / 50D); } else { // left or middle mouse translate corner[0] += prev[0] - xx; corner[1] += prev[1] - yy; boundarypanellabel.setText(this.toString()); repaint(); } previ[0] = x; previ[1] = y; } }); } /** * Paint the BoundaryPanel by painting the basic plane, calling the paint * on each of the current negative and positive BoundaryCircles, then * drawing the tickmarks. * * @param g the Graphics object upon which to paint the TracePlanePanel */ public synchronized void paint(Graphics g) { // paint the basic plane super.paint(g); // paint the positive and negative BoundaryCircles, in a rainbow of // colors, up to the maxpow in use at the moment int max = usecalcmaxpow ? calcmaxpow : chosenmaxpow; for (int i=0; i < max; i++) { int j = 255*i/max; g.setColor(new Color(j,0,j)); pc[i].paint(g, this, diskorcircle); g.setColor(new Color(0,j,255-j)); nc[i].paint(g, this, diskorcircle); } drawTickmarks(g); } /** * Scales the size of the visible window onto the boundary of * hyperbolic space. * * @param factor the scaling factor, can be any positive number */ public void zoom(double factor) { corner[0] = corner[0] + .5D * size[0] - .5D * size[0] * factor; size[0] *= factor; corner[1] = corner[1] - .5D * size[1] + .5D * size[1] * factor; size[1] *= factor; boundarypanellabel.setText(this.toString()); repaint(); } /** * Recreates the current positive and negative BoundaryCircles and then * repaints, called when some relevant parameter has changed. */ public synchronized void update() { // make a arrays of positive and negative BoundaryCircles // of the appropriate length int max = usecalcmaxpow ? calcmaxpow : chosenmaxpow; pc = new BoundaryCircle[max]; nc = new BoundaryCircle[max]; if ((z11.y == 0D) && ((z11.x == 1D) || (z11.x == -1D))) { // parabolic generator, call the appropriate BoundaryCircle constructor for (int i=0; i < max; i++) { pc[i] = new BoundaryCircle(i+1); nc[i] = new BoundaryCircle(-i-1); } } else { // elliptic or loxodromic generator int domaintype = fordordirichlet.getSelectedIndex(); Complex z11powers = new Complex(Complex.ONE); for (int i=0; i < max; i++) { // take the power of the generator z11powers = Complex.multiply(z11powers, z11); // make a positive BoundaryCircle with the right constructor, // then make a negative version of it BoundaryCircle poscirc = new BoundaryCircle(z11powers, dist, i+1, domaintype); pc[i] = poscirc; nc[i] = BoundaryCircle.makeNeg(poscirc); } } repaint(); } }