package net.bioclipse.spectrum.graph2d; import java.awt.*; import java.applet.*; import java.net.URL; import java.util.*; import java.io.InputStream; /* ************************************************************************** ** ** Class Contour ** ************************************************************************** ** Copyright (C) 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 extends the interactive graphics class to incorporate ** contours. ** *************************************************************************/ /** * This class extends the interactive graphics class G2Dint to incorporate * contouring. * * @version $Revision: 1.11 $, $Date: 1996/08/12 23:37:08 $. * @author Leigh Brookshaw */ public class Contour extends G2Dint { /* ************** ** ** Constants ** *************/ /* ** The minimum length of a curve before it gets a label */ static final int MINCELLS = 30; /* ** Default number of contour levels */ static final int NLEVELS = 12; /********************** ** ** Protected Variables ** ***********************/ /** * Dimension of the contour grid in the X direction */ protected int nx; /** * Dimension of the contour grid in the Y direction */ protected int ny; /** * Vector array containing the Contour curves. * Each index in the array contains curves at a given * contour level */ protected Vector curves[]; /** * If set the class calculates the contour levels based on * the data minimum and maximum. Default value <i>true</i>. */ protected boolean autoLevels; /* * If true the contour levels are calculated in * logarithmic intervals */ protected boolean logLevels; /* * If true the limits of the plot are the limits of the * data grid not the limits of the contours! */ protected boolean gridLimits; /* * The array of contour levels */ protected double levels[]; /** * The label for each contour level */ protected TextLine labels[]; /** * Font to use in drawing Labels */ protected Font labelfont; /** * Color to use in drawing Labels */ protected Color labelcolor; /** * Style to use in drawing Labels. TextLine.SCIENTIFIC or * TextLine.ALGEBRAIC. */ protected int labelStyle; /** * Precision to use in drawing Labels. */ protected int labelPrecision; /** * Number of Significant figures to use in drawing Labels. */ protected int labelSignificant; /** * Which levels will get labels. If it is equal to 1 every level * gets a label, equal to 2 every second level etc. If it is equal to 0 * no labels are displayed. */ protected int labelLevels; /** * If false labels are not drawn */ protected boolean drawlabels; /** * If true the labels will be calculated for each * contour level. These might not look all that hot. */ protected boolean autoLabels; /** * Color to draw non labelled contour line */ protected Color contourColor; /** * Color to draw labelled contour line */ protected Color labelledColor; /** * The data grid, a 2D array stored in linear form. * It is assumed that [0,0] is the bottom left corner * and the data is ordered by row. */ protected double grid[]; /** * The X minimum limit of the data grid */ protected double xmin; /** * The X maximum limit of the data grid */ protected double xmax; /** * The Y minimum limit of the data grid */ protected double ymin; /** * The Y maximum limit of the data grid */ protected double ymax; /** * The minimum value of the grid values */ protected double zmin; /** * The maximum value of the grid values */ protected double zmax; /** * Boolean value if true Contours will not be calculated */ public boolean noContours = false; /* ***************** ** ** Constructors ** ****************/ /** * Instantaite the class */ public Contour() { grid = null; xmin = 0.0; xmax = 0.0; ymin = 0.0; ymax = 0.0; zmin = 0.0; zmax = 0.0; nx = 0; ny = 0; levels = new double[NLEVELS]; labels = new TextLine[NLEVELS]; autoLevels = true; logLevels = false; gridLimits = false; autoLabels = true; labelfont = new Font("Helvetica",Font.PLAIN,12); labelcolor = Color.blue; labelLevels = 1; labelStyle = TextLine.ALGEBRAIC; labelPrecision = 2; labelSignificant = 3; drawlabels = true; contourColor = null; labelledColor = null; curves = null; } /* ************ ** ** Methods ** ***********/ /** * Load the grid to contour from a URL. There are 2 formats for the data * optionally the limits of the grid can be parsed.<BR> * <PRE> * The expected format of the data * 1st Number: nx * 2nd Number: ny * nx*ny numbers following * * Optionally * 1st Number: nx * 2nd Number: ny * 3rd Number: xmin * 4th Number: xmax * 5th Number: ymin * 6th Number: ymax * nx*ny numbers following * </PRE><BR> * If xmin, xmax, ymin, ymax are not specified they are assumed * to be [1.0,nx,1.0,ny] * * @param file URL of the file to load * @return <i>true</I> of the load was successful. * */ public boolean loadGrid( URL file) { byte b[] = new byte[50]; int nbytes = 0; int max = 100; int inc = 100; int n = 0; double data[] = new double[max]; InputStream is = null; boolean comment = false; int c; try { is = file.openStream(); while( (c=is.read()) > -1 ) { switch (c) { case '#': comment = true; break; case '\r': case '\n': comment = false; case ' ': case '\t': if( nbytes > 0 ) { String s = new String(b,0,0,nbytes); data[n] = Double.valueOf(s).doubleValue(); n++; if( n >= max ) { max += inc; double d[] = new double[max]; System.arraycopy(data, 0, d, 0, n); data = d; } nbytes = 0; } break; default: if( !comment ) { b[nbytes] = (byte)c; nbytes++; } break; } } if (is != null) is.close(); } catch(Exception e) { System.out.println("Failed to load Grid from file "); e.printStackTrace(); if (is != null) try { is.close(); } catch (Exception ev) { } return false; } if(n < 1 ) { System.out.println("Failed to load Grid from file "); return false; } nx = (int)(data[0] + 0.5); ny = (int)(data[1] + 0.5); if( n == nx*ny+6 ) { xmin = data[2]; xmax = data[3]; ymin = data[4]; ymax = data[5]; grid = new double[nx*ny]; System.arraycopy(data, 6, grid, 0, nx*ny); } else if( n == nx*ny+2 ) { xmin = 1.0; xmax = (double)(nx); ymin = 1.0; ymax = (double)(ny); grid = new double[nx*ny]; System.arraycopy(data, 2, grid, 0, nx*ny); } else { System.out.println("Error loading grid, Wrong number of points "); grid = null; return false; } zrange(); calcLevels(); detachCurves(); curves = null; return true; } /** * Set the range of the grid * @param xmin Minimum X value * @param xmax Maximum X value * @param ymin Minimum Y value * @param ymax Maximum Y value */ public void setRange(double xmin,double xmax,double ymin,double ymax) { if( xmin >= xmax || ymin >= ymax ) return; this.xmin = xmin; this.xmax = xmax; this.ymin = ymin; this.ymax = ymax; } /** * Return the range of the grid * @return An array contining xmin,xmax,ymin,ymax. */ public double[] getRange() { double d[] = new double[4]; d[0] = xmin; d[1] = xmax; d[2] = ymin; d[3] = ymax; return d; } /** * return the dimensions of the grid * @return An array containing the number of columns, number of rows. */ public int[] getDim() { int i[] = new int[2]; i[0] = nx; i[1] = ny; return i; } /** * Return the grid * @return An array of size nx by ny contining the data grid. */ public double[] getGrid() { return grid; } /** * Manually set the contour levels. * @param levels An array containing the contour levels * @param nl The number of contour levels in the arrray */ public void setLevels(double levels[], int nl) { int i; if( levels == null || nl <= 0 ) return; detachCurves(); curves = null; autoLevels = false; this.levels = new double[nl]; System.arraycopy(levels,0,this.levels,0,nl); labels = new TextLine[nl]; for(i=0; i<labels.length; i++) { labels[i] = new TextLine( String.valueOf( (float)levels[i] ) ); } } /** * Manually set the Contour labels. * @param labels An array containing the labels. * @param nl Number of labels in the Array. */ public void setLabels(TextLine labels[], int nl) { if( labels == null || nl <= 0 ) return; autoLabels = false; this.labels = new TextLine[nl]; System.arraycopy(labels,0,this.labels,0,nl); } /** * Set the font to be used with All the labels * @param f Font */ public void setLabelFont(Font f) { labelfont = f; } /** * Set the Color to be used with all the labels. * @param c Color */ public void setLabelColor(Color c) { labelcolor = c; } /** * Set the grid to be contoured. * @param grid Array of values * @param nx Number of columns * @param ny Number of rows */ public void setGrid(double grid[],int nx,int ny) { this.grid = grid; this.nx = nx; this.ny = ny; zrange(); calcLevels(); } /** * Delete all the Contours */ public void deleteContours() { if(curves == null) return; detachCurves(); curves = null; } /** * Detach contours so that they will not be plotted. */ public void detachContours() { if(curves == null) return; detachCurves(); } /** * Attach contours so that they will be plotted. */ public void attachContours() { if(curves == null) return; attachCurves(); } /** * Set the contour's color. * @param c Color */ public void setContourColor(Color c) { contourColor = c; } /** * Set the labelled contour's color. * @param c Color */ public void setLabelledContourColor(Color c) { labelledColor = c; } /** * Return the contour levels. * @return An array containing the contour levels */ public double[] getLevels() { return levels; } /** * If true the limits of the plot will be the grid limits. * If false the limits of the plot will be the contours. * @param b boolean */ public void setLimitsToGrid(boolean b) { gridLimits = b; } /** * Set the contour levels that are to have labels. * <pre> * if 0 no labels are drawn * if 1 every level gets a label * If 2 every 2nd level gets a label * etc. * </pre> */ public void setLabelLevels(int i) { if(i<=0) labelLevels = 0; else labelLevels = i; } /** * If true contour levels are calculated on a log scale. * @param b boolean */ public void setLogLevels(boolean b) { logLevels = b; if( zmin <= 0.0 || zmax <= 0.0 ) logLevels = false; } /** * Set the number of contour levels. * @@param l Number of contour levels */ public void setNLevels(int l) { if(l <= 0) return; levels = new double[l]; calcLevels(); detachCurves(); curves = null; } /** * If true contour levels are calculated automatically. * @param b boolean */ public void setAutoLevels(boolean b) { autoLevels = b; } /** * If true contour levels are not labeled. * @param b boolean */ public void setDrawLabels(boolean b) { drawlabels = b; } /** * Set the label style, either TextLine.SCIENTIFIC or * TextLine.ALGEBRAIC. * @param s Style */ public void setLabelStyle(int s) { labelStyle = s; calcLabels(); } /** * Get the label style, either TextLine.SCIENTIFIC or * TextLine.ALGEBRAIC. * @return style */ public int getLabelStyle() { return labelStyle; } /** * Set the label precision. * @param s Precision */ public void setLabelPrecision(int p) { labelPrecision = p; calcLabels(); } /** * Get the label precision. * @return precision */ public int getLabelPrecision() { return labelPrecision; } /** * Set the label significant figures. * @param s number of significant figures */ public void setLabelSignificance(int s) { labelSignificant = s; calcLabels(); } /** * Get the number of significant figures for labels. * @return number of significant figures */ public int getLabelSignificance() { return labelSignificant; } /** * Add extra events to the G2Dint event handler. * * If 'l' is pressed repaint without the labels * If 'L' is pressed repaint with the labels. */ public boolean keyDown(Event e, int key) { if(xaxis==null || yaxis==null) return false; if( super.keyDown(e,key) ) return true; switch ( key ) { case 'l': drawlabels = false; repaint(); return true; case 'L': drawlabels = true; repaint(); return true; } return false; } /* ******************** ** ** Private Methods ** *******************/ /* ** calcLevels() ** Calculate the contour levels */ private void calcLevels() { int i; int l; if(!autoLevels) return; if(levels == null) levels = new double[NLEVELS]; labels = new TextLine[levels.length]; // Nice label steps not implemented yet //levelStep(); if( logLevels ) { double inc = Math.log(zmax-zmin)/ (double)(levels.length+1); try { for(i=0; i<levels.length; i++) levels[i] = zmin + Math.pow(Math.E,(double)(i+1)*inc); } catch (Exception e) { System.out.println("Error calculateing Log levels!"); System.out.println("... calculating linear levels instead"); logLevels = false; calcLevels(); } } else { double inc = (zmax-zmin)/(double)(levels.length+1); for(i=0; i<levels.length; i++) levels[i] = zmin + (double)(i+1)*inc; } } /* ** calcLabels() ** Calculate the labels */ private void calcLabels() { int i; if( !autoLabels ) return; if(levels==null || levels.length <= 0) return; labels = new TextLine[levels.length]; for(i=0; i<labels.length; i++) { labels[i] = new TextLine(); labels[i].parseDouble(levels[i], labelSignificant,labelPrecision,labelStyle); } } /* ** zrange() ** Calculate the range of the grid */ private void zrange() { int i; zmin = grid[0]; zmax = grid[1]; for( i=0; i<grid.length; i++) { zmin = Math.min(zmin,grid[i]); zmax = Math.max(zmax,grid[i]); } System.out.println("Data range: zmin="+zmin+", zmax="+zmax); if(zmin == zmax) { System.out.println("Cannot produce contours of a constant surface!"); } if(zmin <= 0 || zmax <= 0) logLevels = false; } /* ** paintFirst(Graphics g, Rectangle r) ** before anything is painted calculate the contours. */ public void paintFirst(Graphics g, Rectangle r) { //System.out.println("paintFirst called"); if( curves == null && !noContours ) { calculateCurves(); calcLabels(); } setContourColors(); if(gridLimits && !userlimits ) { if( xaxis != null ) { if(xaxis.minimum > xmin ) xaxis.minimum = xmin; if(xaxis.maximum < xmax ) xaxis.maximum = xmax; } if( yaxis != null ) { if(yaxis.minimum > ymin ) yaxis.minimum = ymin; if(yaxis.maximum < ymax ) yaxis.maximum = ymax; } } else if( dataset.isEmpty() ) { if( xaxis != null ) { xaxis.minimum = xmin; xaxis.maximum = xmax; } if( yaxis != null ) { yaxis.minimum = ymin; yaxis.maximum = ymax; } } } /** * Set the colors for the contour lines */ private void setContourColors() { int i; int j; Vector v; if(curves == null || (contourColor==null && labelledColor==null) ) return; for(i=0; i<curves.length; i++) { setContourColors(curves[i],null); } if(contourColor != null) { for(i=0; i<curves.length; i++) { setContourColors(curves[i],contourColor); } } if(labelledColor != null) { for(i=0; i<curves.length; i++) { if(i%labelLevels == 0) { setContourColors(curves[i],labelledColor); } } } } /** * Set the colors for the contour lines */ private void setContourColors(Vector v, Color c) { int i; DataSet d; if(v == null) return; for(i=0; i<v.size(); i++) { d = (DataSet)(v.elementAt(i)); if(d != null) d.linecolor = c; } } /* ** attachCurves() ** Attach all the curves to the graph and to the axes */ private void attachCurves() { int i; if(curves == null) return; for(i=0; i<curves.length; i++) attachCurves(curves[i]); } /* ** attachCurves(Vector v) ** Attach all the curves from a given level to the graph and to the axes */ private void attachCurves(Vector v) { int j; if(v == null) return; for(j=0; j<v.size(); j++) { attachDataSet((DataSet)(v.elementAt(j))); if(xaxis != null) xaxis.attachDataSet((DataSet)(v.elementAt(j))); if(yaxis != null) yaxis.attachDataSet((DataSet)(v.elementAt(j))); } } /* ** detachCurves() ** Detach All the curves from the graph and the axes. */ private void detachCurves() { int i; if(curves == null) return; for(i=0; i<curves.length; i++) detachCurves(curves[i]); } /* ** detachCurves() ** Detach all the curves from a given level from ** the graph and the axes. */ private void detachCurves(Vector v) { int j; if(v == null) return; for(j=0; j<v.size(); j++) { detachDataSet((DataSet)(v.elementAt(j))); if(xaxis != null) xaxis.detachDataSet((DataSet)(v.elementAt(j))); if(yaxis != null) yaxis.detachDataSet((DataSet)(v.elementAt(j))); } } /* ** paintLast(Graphics g, Rectangle rect) ** Last thing to be done is to draw the contour labels if required. */ public void paintLast(Graphics g, Rectangle rect) { int i, j; int points; int index; Vector v; DataSet ds; double point[] = new double[2]; int x; int y; Color current = g.getColor(); Rectangle r = new Rectangle(); if( xaxis == null || yaxis == null || labels == null || labelLevels == 0 || !drawlabels || curves == null ) { super.paintLast(g,rect); return; } for(i=0; i<levels.length; i++) { if( labels[i] != null && !labels[i].isNull() && i%labelLevels == 0 ) { labels[i].setFont(labelfont); labels[i].setColor(labelcolor); v = curves[i]; for(j=0; j<v.size(); j++) { ds = (DataSet)(v.elementAt(j)); points = ds.dataPoints(); index = (int)(Math.random()*(double)MINCELLS); while ( points > MINCELLS ) { point = ds.getPoint(index); x = xaxis.getInteger(point[0]); y = yaxis.getInteger(point[1]); r.width = labels[i].getWidth(g); r.height = labels[i].getAscent(g); r.x = x - r.width/2; r.y = y - r.height/2; g.setColor(DataBackground); g.fillRect(r.x, r.y, r.width, r.height); g.setColor(current); labels[i].draw(g, r.x, r.y+r.height, TextLine.LEFT); points -= MINCELLS; index += MINCELLS; } } } } super.paintLast(g,rect); } /* ** calculateCurves() ** Calculate the contours and attach them to the graph and axes. */ protected void calculateCurves() { int i; int j; double data[]; double xscale = (xmax-xmin)/(double)(nx-1); double yscale = (ymax-ymin)/(double)(ny-1); IsoCurve isocurve; isocurve = new IsoCurve(grid,nx,ny); if( curves != null) { detachCurves(); curves = null; } if( zmin == zmax ) return; curves = new Vector[levels.length]; for(i=0; i<levels.length; i++) { System.out.println("Calculating Contours: level="+levels[i]); isocurve.setValue(levels[i]); curves[i] = new Vector(); while( (data = isocurve.getCurve()) != null ) { for(j=0; j<data.length; ) { data[j] = xmin + data[j]*xscale; j++; data[j] = ymin + data[j]*yscale; j++; } try { curves[i].addElement(new DataSet(data, data.length/2)); } catch (Exception e) { System.out.println("Error loading contour into DataSet!"); System.out.println("...Contour Level "+levels[i]); } } attachCurves(curves[i]); //repaint(); } } }