/* * Class Plot * * Superclass for the plotting subclasses: * PlotGraph and PlotPoleZero * * WRITTEN BY: Dr Michael Thomas Flanagan * * DATE: February 2002 * REVISED: 20 July 2005, 7 July 2008, 27 July 2008, 11 August 2008, 5 February 2011, 21 February 2011 * * Copyright (c) 2002 - 2008 * * DOCUMENTATION * http://www.ee.ucl.ac.uk/~mflanaga/java/PlotGraph.html * http://www.ee.ucl.ac.uk/~mflanaga/java/ * * PERMISSION TO COPY: * Permission to use, copy and modify this software and its documentation for * NON-COMMERCIAL purposes is granted, without fee, provided that an acknowledgement * to the author, Michael Thomas Flanagan at www.ee.ucl.ac.uk/~mflanaga, appears in all copies. * * Dr Michael Thomas Flanagan makes no representations about the suitability * or fitness of the software for any or for a particular purpose. * Michael Thomas Flanagan shall not be liable for any damages suffered * as a result of using, modifying or distributing this software or its derivatives. * ***************************************************************************************/ package flanagan.plot; import java.awt.*; import java.io.Serializable; import flanagan.math.Fmath; import flanagan.math.ArrayMaths; import flanagan.interpolation.CubicSpline; public class Plot extends Canvas implements Serializable{ protected static final long serialVersionUID = 1L; // serial version unique identifier protected double[][] data = null; // data to be plotted // data[i][] i = 0, 2, 4 . . . x values // data[i][] i = 1, 3, 5 . . . y values for x[i-1][] protected double[][] copy = null; // copy of original data to be plotted protected int nCurves = 0; // number of curves protected int[] nPoints = null; // number of points points on curve each curve protected int nmPoints = 0; // number of points points on curve with most points protected int niPoints = 200; // number of cubic spline interpolation points protected int[] pointOpt = null; // point plotting option for each curve // pointOpt = 0: no points plotted // pointOpt = i where i = 1,2,3,4,5,6,7,8: points plotted // default options // curve 1 - open circles // curve 2 - open squares // curve 3 - open diamonds // curve 4 - filled circles // curve 5 - filled squares // curve 6 - filled diamonds // curve 7 - x crosses // curve 8 - + crosses // further curves - above sequence repeated protected int[] pointSize = null; // point size in pixels for each curve protected int npTypes = 8; // number of point types protected boolean[] errorBar = null; // true - error bar plotted, flase no error bar plotted - default = false protected double[][] errors = null; // error bar values - should be an estimate of the sd of the variable protected double[][] errorsCopy = null; // copy of error bar values protected int[] lineOpt = null; // line drawing option for each curve // lineOpt = 0: no line plotted // lineOpt = 1: cubic spline interpolation line plotted as a continuous line // lineOpt = 2: cubic spline interpolation line plotted as a dashed line // lineOpt = 3: line plotted by joining points // lineOpt = 4: dashed line plotted by joining points // default - lineOpt = 1 protected int[] dashLength = null; // dash length in lineOpt = 2 protected boolean[] minMaxOpt = null; // true - curve included in maximum and minimum axes value calculation protected boolean[] trimOpt = null; // true - curve trimmed to fit axes rectangle protected int fontSize = 14; // text font size protected int xLen = 625; // length of the x axis in pixels protected int yLen = 375; // length of the y axis in pixels protected int xBot = 100; // x coordinate of the bottom of the x axis in pixels protected int xTop = xBot+xLen; // x coordinate of the top of the x axis in pixels protected int yTop = 110; // y coordinate of the top of the y axis in pixels protected int yBot = yTop+yLen; // y coordinate of the bottom of the y axis in pixels protected double xLow = 0; // scaled lower limit data value of the x axis protected double xHigh = 0; // scaled upper limit data value of the x axis protected double yLow = 0; // scaled lower limit data value of the y axis protected double yHigh = 0; // scaled upper limit data value of the y axis protected int xFac = 0; // decadic exponent of x axis scaling factor protected int yFac = 0; // decadic exponent of y axis scaling factor protected int xTicks = 0; // number of x axis ticks protected int yTicks = 0; // number of y axis ticks protected double xMin = 0.0D; // minimum x data value protected double xMax = 0.0D; // maximum x data value protected double yMin = 0.0D; // minimum y data value protected double yMax = 0.0D; // maximum y data value protected double xOffset = 0.0D; // xaxis data value offset protected double yOffset = 0.0D; // y axis data value offset protected boolean noXoffset = false; // no x axis offset allowed if true protected boolean noYoffset = false; // no y axis offset allowed if true protected double xLowFac = 0.75D; // x axis data setting low factor protected double yLowFac = 0.75D; // y axis data setting low factor protected String graphTitle = " "; // graph title protected String graphTitle2 = " "; // graph title (secondline) protected String xAxisLegend = " "; // x axis legend title protected String xAxisUnits = " "; // x axis unit name, e.g. V, ohm protected String yAxisLegend = " "; // y axis legend title protected String yAxisUnits = " "; // x axis unit name protected boolean xZero = false; // if true - a (x=0) zero line is required protected boolean yZero = false; // if true - a (y=0) zero line required protected boolean noXunits = true; // if true - no x axis units protected boolean noYunits = true; // if true - no y axis units protected double[] xAxisNo = new double[50]; // x axis legend numbers as double protected double[] yAxisNo = new double[50]; // y axis legend numbers as double protected String[] xAxisChar = new String[50]; // x axis legend numbers as char protected String[] yAxisChar = new String[50]; // y axis legend numbers as char protected int[] axisTicks = new int[50]; // no of ticks for scaled lengths protected static double dataFill = Double.NaN; // value used to initialise data array by Plot.data() // Constructor //One 2-dimensional data arrays public Plot(double[][] data){ this.initialise(data); } // Constructor //Two 1-dimensional data arrays public Plot(double[] xdata, double[] ydata){ int xl = xdata.length; int yl = ydata.length; if(xl!=yl)throw new IllegalArgumentException("x-data length is not equal to the y-data length"); double[][] data = new double[2][xl]; for(int i=0; inmPoints)nmPoints=ll; } // Initialize class 2D arrays this.data = new double[2*nCurves][nmPoints]; this.copy = new double[2*nCurves][nmPoints]; this.errors = new double[nCurves][nmPoints]; this.errorsCopy = new double[nCurves][nmPoints]; // Calculate curve lengths // and check all individual curves have an equal number of abscissae and ordinates int k = 0, l1 = 0, l2 = 0; boolean testlen=true; for(int i=0; inpTypes)kk = 1; } } // sort x elements into ascending order with matching switches of y elements // using selection sort method public static double[][] doubleSelectionSort(double[][] aa){ int index = 0; int lastIndex = -1; int n = aa[0].length; double holdx = 0.0D; double holdy = 0.0D; double[][] bb = new double[2][n]; for(int i=0; i4)throw new IllegalArgumentException("lineOpt must be 0, 1, 2, 3 or 4"); this.lineOpt=lineOpt; // check if data supports cubic spline interpolation if lineOpt = 1 or 2 for(int i=0; idata[2*i][j-1])rev++; } if(rev==nPoints[i]){ lineOpt[i]=-lineOpt[i]; } else{ // check if y all in descending order rev = 1; for(int j=1; j3)throw new IllegalArgumentException("lineOpt must be 0, 1, 2 or 3"); for(int i=0; i8)throw new IllegalArgumentException("pointOpt must be 0, 1, 2, 3, 4, 5, 6, 7, or 8"); this.pointOpt=pointOpt; } // Overwrite point plotting option with a single option for all curves public void setPoint(int spointOpt){ if(spointOpt<0 || spointOpt>8)throw new IllegalArgumentException("pointOpt must be 0, 1, 2, 3, 4, 5, 6, 7, or 8"); for(int i=0; i=0.0 && mmax>0.0){ big=mmax; test=true; } else{ if(mmin<0.0 && mmax<=0.0){ big=-mmin; test=true; } else{ if(mmax>0.0 && mmin<0.0){ big=Math.max(mmax, -mmin); test=true; } } } if(test){ if(big>100.0){ while(big>1.0){ big/=10.0; fac--; } } if(big<=0.01){ while(big<=0.10){ big*=10.0; fac++; } } } return fac; } // Set low value on axis public static void limits(double low, double high, double lowfac, double[]limits){ double facl = 1.0D; double fach = 1.0D; if(Math.abs(low)<1.0D)facl=10.0D; if(Math.abs(low)<0.1D)facl=100.0D; if(Math.abs(high)<1.0D)fach=10.0D; if(Math.abs(high)<0.1D)fach=100.0D; double ld=Math.floor(10.0*low*facl)/facl; double hd=Math.ceil(10.0*high*fach)/fach; if(ld>=0.0D && hd>0.0D){ if(ld1)offset = Math.floor(low*Math.pow(10, -ed))*Math.pow(10,ed); } else{ eh=(int)Math.floor(Fmath.log10(Math.abs(low))); if(eh-ed>1)offset = Math.floor(high*Math.pow(10, -ed))*Math.pow(10,ed); } } return offset; } // Calculate scaling and offset values for both axes public void axesScaleOffset(){ double[] limit = new double[2]; // tranfer data from copy to enable redrawing int k=0; for(int i=0; i0)xZero=true; // y axis limits(this.yMin, this.yMax, this.yLowFac, limit); this.yLow = limit[0]; this.yHigh = limit[1]; if(yLow<0 && yHigh>0)yZero=true; // Calculate tick parameters // x axis this.xTicks = ticks(this.xLow, this.xHigh, this.xAxisNo, this.xAxisChar); this.xHigh = this.xAxisNo[this.xTicks-1]; if(this.xLow!=this.xAxisNo[0]){ if(this.xOffset!=0.0D){ this.xOffset = this.xOffset - this.xLow + this.xAxisNo[0]; } this.xLow = this.xAxisNo[0]; } // y axis this.yTicks = ticks(this.yLow, this.yHigh, this.yAxisNo, this.yAxisChar); this.yHigh = this.yAxisNo[this.yTicks-1]; if(this.yLow!=this.yAxisNo[0]){ if(this.yOffset!=0.0D){ this.yOffset = this.yOffset - this.yLow + this.yAxisNo[0]; } this.yLow = this.yAxisNo[0]; } } // Calculate axis ticks and tick values public static int ticks(double low, double high, double[] tickval, String[] tickchar){ // Find range int[] trunc = {1, 1, 1, 2, 3}; double[] scfac1 = {1.0, 10.0, 1.0, 0.1, 0.01}; double[] scfac2 = {1.0, 1.0, 0.1, 0.01, 0.001}; double rmax = Math.abs(high); double temp = Math.abs(low); if(temp>rmax)rmax = temp; int range = 0; if(rmax<=100.0D){ range = 1; } if(rmax<=10.0D){ range = 2; } if(rmax<=1.0D){ range = 3; } if(rmax<=0.1D){ range = 4; } if(rmax>100.0D || rmax<0.01)range = 0; // Calculate number of ticks double inc = 0.0D; double bot = 0.0D; double top = 0.0D; int sgn = 0; int dirn = 0; if(high>0.0D && low>=0.0D){ inc = Math.ceil((high-low)/scfac1[range])*scfac2[range]; dirn = 1; bot = low; top = high; sgn = 1; } else{ if(high<=0 && low<0.0D){ inc = Math.ceil((high-low)/scfac1[range])*scfac2[range]; dirn = -1; bot = high; top = low; sgn = -1; } else{ double up = Math.abs(Math.ceil(high)); double down = Math.abs(Math.floor(low)); int np = 0; if(up>=down){ dirn = 2; np = (int)Math.rint(10.0*up/(up+down)); inc = Math.ceil((high*10/np)/scfac1[range])*scfac2[range]; bot = 0.0D; top = high; sgn = 1; } else{ dirn = -2; np = (int)Math.rint(10.0D*down/(up+down)); inc = Math.ceil((Math.abs(low*10/np))/scfac1[range])*scfac2[range]; bot = 0.0D; top = low; sgn = -1; } } } int nticks = 1; double sum = bot; boolean test = true; while(test){ sum = sum + sgn*inc; nticks++; if(Math.abs(sum)>=Math.abs(top))test=false; } // Calculate tick values int npExtra = 0; double[] ttickval = null;; switch(dirn){ case 1: ttickval = new double[nticks]; tickval[0]=Fmath.truncate(low, trunc[range]); for(int i=1; i=nticks)testZero = false; } } // set String form of tick values for(int i=0; inCurves)throw new IllegalArgumentException("At least one curve must be included in the maximum/minimum calculation"); } } int k=0; double yMint=0.0D, yMaxt=0.0D; for(int i=0; ithis.data[k][j])this.xMin=this.data[k][j]; if(this.xMaxyMint)this.yMin=yMint; yMaxt=this.data[k+1][j]; if(errorBar[i])yMaxt=errors[i][j]; if(this.yMax=0){ for(int i=0; ixTop)btest2=false; if(xnewpointxTop)btest2=false; if(yoldpoint>yBot)btest2=false; if(yoldpointyBot)btest2=false; if(ynewpointyCharLenMax)yCharLenMax=yAxisChar[ii].length(); int shift = (yCharLenMax-3)*5; double ytep=(double)(-yTop+yBot)/((double)(this.yTicks-1)); for(int ii=0; iidashLength[i]){ dsum=0; if(dcheck){ dcheck=false; } else{ dcheck=true; } } } if(dcheck)g.drawLine(xoldpoint,yoldpoint,xnewpoint,ynewpoint); } xoldpoint=xnewpoint; yoldpoint=ynewpoint; } } if(lineOpt[i]==-1 || lineOpt[i]==-2){ CubicSpline cs = new CubicSpline(this.nPoints[i]); for(int ii=0; iidashLength[i]){ dsum=0; if(dcheck){ dcheck=false; } else{ dcheck=true; } } } if(dcheck)g.drawLine(xoldpoint,yoldpoint,xnewpoint,ynewpoint); } xoldpoint=xnewpoint; yoldpoint=ynewpoint; } } if(lineOpt[i]==3){ // Join points option dsum=0; dcheck=true; xoldpoint=xBot+(int)((((this.data[kk][0])-xLow)/xdenom)*xLen); yoldpoint=yBot-(int)((((this.data[kk+1][0])-yLow)/ydenom)*yLen); for(int ii=1; iidashLength[i]){ dsum=0; if(dcheck){ dcheck=false; } else{ dcheck=true; } } xnewpoint=xBot+(int)((((this.data[kk][ii])-xLow)/xdenom)*xLen); ynewpoint=yBot-(int)((((this.data[kk+1][ii])-yLow)/ydenom)*yLen); btest2=printCheck(trimOpt[i], xoldpoint, xnewpoint, yoldpoint, ynewpoint); if(dcheck)g.drawLine(xoldpoint,yoldpoint,xnewpoint,ynewpoint); xoldpoint=xnewpoint; yoldpoint=ynewpoint; } } // Plot points if(pointOpt[i]>0){ for(int ii=0; ii