import java.awt.*; import java.util.*; /** Program: H2 Purpose: do click-and-drag drawing + drag-isometries ("sticky interface") in hyperbolic 2-space @author: Paul Garrett, garrett@math.umn.edu @version: Mon Jul 6 20:18:45 CDT 1998 */ public class H2 extends java.applet.Applet { final static int radius = 150; final static int xinset = 30; final static int yinset = 25; final static Point center = new Point(xinset+radius, yinset+radius); int height, width; Color background_color, foreground_color; Point first, second; Vector v; // of Points, connected in pairs Graphics osg; Image osi; Button clear, draw, move; boolean queryDraw; // in draw mode or not Point basePt, newPt; // from mousedown, for "sticky" motion... Complex[][] mapMx; // the cumulative linear fractional transformation // applied to the original points /* * //__EDIT__ what about additions to already-drawn + already-rotated points!? * * Some possible overflow/roundoff problems...?! */ /**************************** * * Make it run also from command line... * */ public static void main(String[] argv) { Frame fr = new Frame("at command line"); fr.addNotify(); fr.setLayout(null); // fr needs space for buttons, too fr.resize(2*xinset+2*radius+50,2*yinset+2*radius); // would be setSize() in 1.1 fr.show(); H2 hyp = new H2(); fr.add(hyp); hyp.resize(2*xinset+2*radius+50,2*yinset+2*radius); // would be setSize() in 1.1 hyp.init(); } /******************* end of command-line main(), etc. ******************/ public void init() { setFont(new Font("TimesRoman", Font.PLAIN, 14)); background_color = new Color(0xbaaaaa); foreground_color = new Color(0x503030); width = 2*xinset+2*radius; height = 2*yinset+2*radius; setBackground(background_color); setForeground(foreground_color); v = new Vector(); osi = createImage(2*radius+1+50+xinset, 2*radius+1); osg = osi.getGraphics(); setLayout(null); clear = new Button("clear"); add(clear); clear.move(30 + 2*radius, 40); clear.resize(60,30); move = new Button("move"); add(move); move.move(30 + 2*radius, 80); move.resize(60,30); draw = new Button("draw"); add(draw); draw.move(30 + 2*radius, 120); draw.resize(60,30); queryDraw = true; mapMx = new Complex[2][2]; mapMx[0][0] = new Complex(1,0); mapMx[0][1] = new Complex(0,0); mapMx[1][0] = new Complex(0,0); mapMx[1][1] = new Complex(1,0); // starts as identity matrix } /****************************************************************/ public void update(Graphics g) { paint(g); // avoid flicker... } public synchronized void paint(Graphics g) { if (osg != null) { // otherwise may get null pointer exception... osg.setColor(background_color); osg.fillRect(0,0,2*radius+1+xinset+ 50, 2*radius+1); osg.setColor(Color.black); osg.drawOval(0, 0, 2*radius, 2*radius); Enumeration en = v.elements(); while (en.hasMoreElements()) { Complex.drawGeodesic(osg, (Point[]) en.nextElement(), mapMx, radius); } if (second != null && queryDraw == true) { Point[] pp = {first, second}; Complex.drawGeodesic(osg, pp, mapMx, radius); } g.drawImage(osi, xinset, yinset, this); } } /************************************************************/ public boolean mouseDown(Event e, int x, int y) { if ((x-center.x)*(x-center.x) + (y-center.y)*(y-center.y) < radius*radius) { if (queryDraw) { second = null; //__EDIT__ note this! first = new Point(x-xinset,y-yinset); return true; } else { // non-draw = move mode basePt = new Point(x-xinset,y-yinset); newPt = null; return true; } } else { // outside the picture return false; } } public boolean mouseDrag(Event e, int x, int y) { if ((x-center.x)*(x-center.x) + (y-center.y)*(y-center.y) < radius*radius) { if (queryDraw) { if (first != null) { second = new Point(x-xinset,y-yinset); repaint(); return true; } else { // trying to draw, but no first point initialized return true; } } else { // non-drawing case if (basePt != null) { newPt = new Point(x-xinset,y-yinset); Complex.updateMapMx(mapMx, basePt, newPt, (double) radius); repaint(); basePt = new Point(x-xinset,y-yinset); //__EDIT__ to repeatedly increment return true; } else { return true; // trying to drag, but no legal base point } } } else { // outside of the model entirely return false; } } public boolean mouseUp(Event e, int x, int y) { if ((x-center.x)*(x-center.x) + (y-center.y)*(y-center.y) < radius*radius) { if (queryDraw) { if (first != null && second != null) { Point[] pp = {first, new Point(x-xinset,y-yinset)}; v.addElement(pp); repaint(); first = null; second = null; return true; } else { return true; // no legitimate pair of points to push onto Vector v } } else { // non-draw case basePt = null; newPt = null; return true; } } else { // outside of model return false; } } public final synchronized boolean action(Event e, Object arg) { if (e.target instanceof Button) { if (arg.toString().equals("clear")) { v.removeAllElements(); repaint(); } else if (arg.toString().equals("move")) { queryDraw = false; repaint(); } else if (arg.toString().equals("draw")) { queryDraw = true; mapMx[0][0] = new Complex(1,0); mapMx[1][0] = new Complex(0,0); mapMx[0][1] = new Complex(0,0); mapMx[1][1] = new Complex(1,0); repaint(); } } return false; } } /************************************************** * * The End * ***************************************************/