package net.bioclipse.spectrum.graph2d; import java.awt.*; import java.applet.*; import java.net.URL; import java.util.*; /* ************************************************************************** ** ** Class G2Dint ** ************************************************************************** ** Copyright (C) 1995, 1996 Leigh Brookshaw ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ************************************************************************** ** ** This class is an extension of Graph2D class. ** It adds interactive selection of the plotting range ** and can display the mouse position in user coordinates. ** *************************************************************************/ /** * This class is an extension of Graph2D class. * It adds interactive selection of the plotting range * and can display the mouse position in user coordinates. * * <h4>Mouse Events</h4> * <dl> * <dt>MouseDown * <dd>Starts the range selection * <dt>MouseDrag * <dd>Drag out a rectangular range selection * <dt>MouseUp * <dd>Replot with modified plotting range. * <dt> * </dl> * <h4>KeyDown Events</h4> * <dl> * <dt>R * <dd>Redraw plot with default limits * <dt>r * <dd>Redraw plot using current limits * <dt>m * <dd>Pop window to enter manually plot range * <dt>c * <dd>Toggle pop-up window that displays the mouse position * in user coordinates * <dt>d * <dd>Show coordinates of the closest data point to the cursor * <dt>D * <dd>Hide data coordinates pop-window * <dt>h * <dd>This key pressed in Any pop-window at any-time will hide it. * </dl> * <P> * <B>Note:</B> To hide Any pop-window press the key <B>h</B> in the * window. This will hide the window at any time. Depending on your * windowing system the mouse button might have to be pressed in the * popup window to ensure it has the keyboard focus. * * @version $Revision: 1.9 $, $Date: 1996/07/02 06:01:12 $. * @author Leigh Brookshaw */ public class G2Dint extends Graph2D { /** * Set to true when a rectangle is being dragged out by the mouse */ protected boolean drag = false; /** * User limits. The user has set the limits using the mouse drag option */ protected boolean userlimits = false; /** * Ths popup window for the cursor position command */ private Gin cpgin = null; /** * Ths popup window for the data point command */ private Gin dpgin = null; /** * The popup window to manually set the range */ private Range range = null; /** * Button Down position */ private int x0,y0; /** * Button Drag position */ private int x1,y1; /* ** Previous Button Drag position */ private int x1old, y1old; /** * Attached X Axis which must be registered with this class. * This is one of the axes used to find the drag range. * If no X axis is registered no mouse drag. */ protected Axis xaxis; /** * Attached Y Axis which must be registered with this class. * This is one of the axes used to find the drag range. * If no Y axis is registered no mouse drag. */ protected Axis yaxis; /** * Create Xaxis to be used for the drag scaling */ public Axis createXAxis() { xaxis = super.createAxis(Axis.BOTTOM); return xaxis; } /** * Create Yaxis to be used for the drag scaling */ public Axis createYAxis() { yaxis = super.createAxis(Axis.LEFT); return yaxis; } /** * Attach axis to be used for the drag scaling. X axes are assumed to * have Axis position Axis.BOTTOM or Axis.TOP. Y axes are assumed * to have position Axis.LEFT or Axis.RIGHT. * @param a Axis to attach * @see Axis */ public void attachAxis(Axis a) { if(a==null) return; super.attachAxis(a); if(a.getAxisPos() == Axis.BOTTOM || a.getAxisPos() == Axis.TOP) { xaxis = a; } else { yaxis = a; } } /** * New update method incorporating mouse dragging. */ public void update(Graphics g) { Rectangle r = bounds(); Color c = g.getColor(); /* The r.x and r.y returned from bounds is relative to the ** parents space so set them equal to zero */ r.x = 0; r.y = 0; if(drag) { /** * Set the dragColor. Do it everytime just incase someone * is playing silly buggers with the background color. */ g.setColor(DataBackground); float hsb[] = Color.RGBtoHSB( DataBackground.getRed(), DataBackground.getGreen(), DataBackground.getBlue(), null); if(hsb[2] < 0.5) g.setXORMode(Color.white); else g.setXORMode(Color.black); /* ** Drag out the new box. ** Use drawLine instead of drawRect to avoid problems ** when width and heights become negative. Seems drawRect ** can't handle it! */ /* ** Draw over old box to erase it. This works because XORMode ** has been set. If from one call to the next the background ** color changes going to get some odd results. */ g.drawLine(x0, y0, x1old, y0); g.drawLine(x1old, y0, x1old, y1old); g.drawLine(x1old, y1old, x0, y1old); g.drawLine(x0, y1old, x0, y0); /* ** draw out new box */ g.drawLine(x0, y0, x1, y0); g.drawLine(x1, y0, x1, y1); g.drawLine(x1, y1, x0, y1); g.drawLine(x0, y1, x0, y0); /* ** Set color back to default color */ g.setColor(c); x1old = x1; y1old = y1; return; } if( clearAll ) { g.setColor(getBackground()); g.fillRect(r.x,r.y,r.width,r.height); g.setColor(c); } if( paintAll ) paint(g); } /** * Handle the Key Down events. */ public boolean keyDown(Event e, int key) { if(xaxis==null || yaxis==null) return false; switch ( key ) { case 'R': xaxis.resetRange(); yaxis.resetRange(); userlimits = false; repaint(); return true; case 'r': repaint(); return true; case 'c': if( cpgin == null) cpgin = new Gin("Position"); if( cpgin.isVisible() ) { cpgin.hide(); } else { cpgin.show(); } return true; case 'D': if(dpgin != null) dpgin.hide(); return true; case 'd': if(dpgin == null) dpgin = new Gin("Data Point"); dpgin.show(); double d[] = getClosestPoint(e.x, e.y); dpgin.setXlabel( d[0] ); dpgin.setYlabel( d[1] ); int ix = xaxis.getInteger(d[0]); int iy = yaxis.getInteger(d[1]); if( ix >= datarect.x && ix <= datarect.x +datarect.width && iy >= datarect.y && iy <= datarect.y +datarect.height ) { Graphics g = getGraphics(); g.fillOval(ix-4, iy-4, 8, 8); } return true; case 'm': if(range == null) range = new Range(this); range.show(); range.requestFocus(); userlimits = true; return true; default: // System.out.println("KeyPress "+e.key); return false; } } /** * Handle the Mouse Down events */ public boolean mouseDown(Event e, int x, int y) { if(xaxis==null || yaxis==null) return false; /* ** Soon as the mouse button is pressed request the Focus ** otherwise we will miss key events */ requestFocus(); x0 = x; y0 = y; drag = true; x1old = x0; y1old = y0; if(x0 < datarect.x) x0 = datarect.x; else if(x0 > datarect.x + datarect.width ) x0 = datarect.x + datarect.width; if(y0 < datarect.y) y0 = datarect.y; else if(y0 > datarect.y + datarect.height ) y0 = datarect.y + datarect.height; return true; } /** * Handle the Mouse Up events */ public boolean mouseUp(Event e, int x, int y) { if(xaxis==null || yaxis==null) return false; x1 = x; y1 = y; if(drag) userlimits = true; drag = false; if(x1 < datarect.x) x1 = datarect.x; else if(x1 > datarect.x + datarect.width ) x1 = datarect.x + datarect.width; if(y1 < datarect.y) y1 = datarect.y; else if(y1 > datarect.y + datarect.height ) y1 = datarect.y + datarect.height; if( Math.abs(x0-x1) > 5 && Math.abs(y0-y1) > 5 ) { if(x0 < x1 ) { xaxis.minimum = xaxis.getDouble(x0); xaxis.maximum = xaxis.getDouble(x1); } else { xaxis.maximum = xaxis.getDouble(x0); xaxis.minimum = xaxis.getDouble(x1); } if(y0 >y1 ) { yaxis.minimum = yaxis.getDouble(y0); yaxis.maximum = yaxis.getDouble(y1); } else { yaxis.maximum = yaxis.getDouble(y0); yaxis.minimum = yaxis.getDouble(y1); } repaint(); } return true; } /** * Handle the Mouse Drag events */ public boolean mouseDrag(Event e, int x, int y) { if(xaxis==null || yaxis==null) return false; x1 = x; y1 = y; if(drag) { if(x1 < datarect.x) x1 = datarect.x; else if(x1 > datarect.x + datarect.width ) x1 = datarect.x + datarect.width; if(y1 < datarect.y) y1 = datarect.y; else if(y1 > datarect.y + datarect.height ) y1 = datarect.y + datarect.height; } if(cpgin != null && cpgin.isVisible()) { cpgin.setXlabel( xaxis.getDouble(x1) ); cpgin.setYlabel( yaxis.getDouble(y1) ); } repaint(); return true; } /** * Handle the Mouse Mouve events */ public boolean mouseMove(Event e, int x, int y) { if(xaxis==null || yaxis==null) return false; x1 = e.x; y1 = e.y; if(cpgin != null && cpgin.isVisible()) { cpgin.setXlabel( xaxis.getDouble(x1) ); cpgin.setYlabel( yaxis.getDouble(y1) ); } return true; } /** * Handle the Action Events. * This handler allows external classes (pop-up windows etc.) to * communicate to this class asyncronously. */ public boolean action(Event e, Object a) { if(xaxis==null || yaxis==null) return false; if(e.target instanceof Range && range != null) { Double d; double txmin = xaxis.minimum; double txmax = xaxis.maximum; double tymin = yaxis.minimum; double tymax = yaxis.maximum; d = range.getXmin(); if(d != null) txmin = d.doubleValue(); d = range.getXmax(); if(d != null) txmax = d.doubleValue(); d = range.getYmin(); if(d != null) tymin = d.doubleValue(); d = range.getYmax(); if(d != null) tymax = d.doubleValue(); if( txmax > txmin && tymax > tymin ) { xaxis.minimum = txmin; xaxis.maximum = txmax; yaxis.minimum = tymin; yaxis.maximum = tymax; } repaint(); return true; } return false; } /** * Find the closest data point to the cursor */ protected double[] getClosestPoint(int ix, int iy) { DataSet ds; int i; double a[] = new double[3]; double distsq = -1.0; double data[] = {0.0, 0.0}; double x = xaxis.getDouble(ix); double y = yaxis.getDouble(iy); //System.out.println("getClosestPoint: x="+x+", y="+y); for (i=0; i<dataset.size(); i++) { ds = (DataSet)(dataset.elementAt(i)); a = ds.getClosestPoint(x,y); if( distsq < 0.0 || distsq > a[2] ) { data[0] = a[0]; data[1] = a[1]; distsq = a[2]; } } return data; } } /** * Popup a window to output data after a Graphics Input command * the window contains the following * X value * Y value */ class Gin extends Frame { private Label xlabel = new Label(); private Label ylabel = new Label(); /** * Instantiate the class */ public Gin() { setLayout(new GridLayout(2,1) ) ; xlabel.setAlignment(Label.LEFT); ylabel.setAlignment(Label.LEFT); this.setFont(new Font("Helvetica", Font.PLAIN, 20)); add("x",xlabel); add("y",ylabel); resize(150,100); super.setTitle("Graphics Input"); } /** * Instantiate the class. * @param title the title to use on the pop-window. */ public Gin(String title) { this(); if(title != null) super.setTitle(title); } /** * Set the X value * @param d The value to set it */ public void setXlabel(double d) { xlabel.setText( String.valueOf(d) ); } /** * Set the Y value * @param d The value to set it */ public void setYlabel(double d) { ylabel.setText( String.valueOf(d) ); } /** * Set the both values * @param dx The X value to set * @param dy The Y value to set */ public void setLabels(double dx, double dy) { xlabel.setText( String.valueOf(dx) ); ylabel.setText( String.valueOf(dy) ); } /** * Set the display font */ public void setFont( Font f ) { if ( f == null ) return; xlabel.setFont( f ); ylabel.setFont( f ); } /** * Set the size of the window * @param x width in pixels * @param y height in pixels */ public void resize( int x, int y) { super.resize(x,y); } /** * Catch the Key Down event 'h'. If the key is pressed then * hide this window. * * @param e The event * @param key the key pressed */ public boolean keyDown(Event e, int key) { if( key == 'h' ) { this.hide(); return true; } return false; } } /** * A popup window for altering the range of the plot */ class Range extends Frame { Graph2D g2d = null; private Label xminLabel = new Label("Xmin"); private Label yminLabel = new Label("Ymin"); private Label xmaxLabel = new Label("Xmax"); private Label ymaxLabel = new Label("Ymax"); private TextField xminText = new TextField(20); private TextField yminText = new TextField(20); private TextField xmaxText = new TextField(20); private TextField ymaxText = new TextField(20); private Button cancel = new Button("Cancel"); private Button done = new Button("Done"); public Range(Graph2D g) { g2d = g; setLayout(new GridLayout(5,2,5,10) ) ; xminLabel.setAlignment(Label.LEFT); xmaxLabel.setAlignment(Label.LEFT); yminLabel.setAlignment(Label.LEFT); ymaxLabel.setAlignment(Label.LEFT); add("xminLabel",xminLabel); add("xminText",xminText); add("xmaxLabel",xmaxLabel); add("xmaxText",xmaxText); add("yminLabel",yminLabel); add("yminText",yminText); add("ymaxLabel",ymaxLabel); add("ymaxText",ymaxText); add("cancel", cancel); cancel.setBackground(Color.red); add("done",done); done.setBackground(Color.green); resize(250,250); super.setTitle("Set Plot Range"); } public Double getXmin() { try { return Double.valueOf(xminText.getText()); } catch (Exception ex) { return null; } } public Double getXmax() { try { return Double.valueOf(xmaxText.getText()); } catch (Exception ex) { return null; } } public Double getYmin() { try { return Double.valueOf(yminText.getText()); } catch (Exception ex) { return null; } } public Double getYmax() { try { return Double.valueOf(ymaxText.getText()); } catch (Exception ex) { return null; } } public void resize( int x, int y) { super.resize(x,y); } public void requestFocus() { xminText.requestFocus(); } /* ** Handle the events */ public boolean keyDown(Event e, int key) { if(e.target instanceof TextField) { if( ( key == 10 || e.key == 13 ) ) { if(xminText.equals(e.target)) { xmaxText.requestFocus(); return true; } else if(xmaxText.equals(e.target)) { yminText.requestFocus(); return true; } else if(yminText.equals(e.target)) { ymaxText.requestFocus(); return true; } else if(ymaxText.equals(e.target)) { xminText.requestFocus(); return true; } return true; } } return false; } public boolean action(Event e, Object a) { if(e.target instanceof Button) { if( done.equals(e.target) && g2d != null) { g2d.deliverEvent( new Event(this,Event.ACTION_EVENT,this) ); this.hide(); return true; } else if( cancel.equals(e.target) ) { this.hide(); return true; } } return false; } }