Changeset 1348


Ignore:
Timestamp:
08/24/06 12:47:06 (14 years ago)
Author:
curtis
Message:

Improve curve fitting functionality.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/loci/apps/slim/SlimPlotter.java

    r1343 r1348  
    7575  // GUI components for decay pane 
    7676  private JLabel decayLabel; 
    77   private JRadioButton surface, lines; 
    7877  private JRadioButton linear, log; 
    7978  private JRadioButton perspective, parallel; 
     79  private JRadioButton dataSurface, dataLines; 
     80  private JRadioButton fitSurface, fitLines; 
     81  private JRadioButton resSurface, resLines; 
    8082  private JCheckBox showData, showLog; 
    8183  private JCheckBox showBox, showScale; 
     
    8890  // VisAD objects 
    8991  private RealType cType; 
    90   private FunctionType bcvFunc; 
     92  private FunctionType bcvFunc, bcvFuncFit, bcvFuncRes; 
    9193  private Linear2DSet bcSet; 
    92   private ScalarMap zMap, vMap; 
     94  private ScalarMap zMap, zMapFit, zMapRes, vMap, vMapFit, vMapRes; 
    9395  private DataRenderer decayRend, fitRend, resRend; 
    9496  private DataReferenceImpl decayRef, fitRef, resRef; 
     
    100102  public SlimPlotter(String[] args) throws Exception { 
    101103    console = new OutputConsole("Log"); 
    102     System.setErr(new PrintStream(console)); 
     104    System.setErr(new ConsoleStream(new PrintStream(console))); 
    103105 
    104106    ProgressMonitor progress = new ProgressMonitor(null, 
     
    257259    paramPane.add(ok); 
    258260    paramDialog.pack(); 
    259     Dimension ss = Toolkit.getDefaultToolkit().getScreenSize(); 
     261    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
    260262    Dimension ps = paramDialog.getSize(); 
    261     paramDialog.setLocation((ss.width - ps.width) / 2, 
    262       (ss.height - ps.height) / 2); 
     263    paramDialog.setLocation((screenSize.width - ps.width) / 2, 
     264      (screenSize.height - ps.height) / 2); 
    263265    paramDialog.setVisible(true); 
    264266    int maxWave = minWave + (channels - 1) * waveStep; 
     
    297299    bcSet = new Linear2DSet(bc, 0, timeRange, timeBins, 
    298300      minWave, maxWave, channels, null, new Unit[] {ns, nm}, null); 
     301    RealType vTypeFit = RealType.getRealType("value_fit"); 
     302    bcvFuncFit = new FunctionType(bc, vTypeFit); 
     303    RealType vTypeRes = RealType.getRealType("value_res"); 
     304    bcvFuncRes = new FunctionType(bc, vTypeRes); 
    299305    progress.setProgress(++p); 
    300306    if (progress.isCanceled()) System.exit(0); 
     
    365371    ScalarMap yMap = new ScalarMap(cType, Display.YAxis); 
    366372    zMap = new ScalarMap(vType, Display.ZAxis); 
     373    zMapFit = new ScalarMap(vTypeFit, Display.ZAxis); 
     374    zMapRes = new ScalarMap(vTypeRes, Display.ZAxis); 
     375    vMap = new ScalarMap(vType, Display.RGB); 
     376    //vMapFit = new ScalarMap(vTypeFit, Display.RGB); 
     377    vMapRes = new ScalarMap(vTypeRes, Display.RGB); 
    367378    decayPlot.addMap(xMap); 
    368379    decayPlot.addMap(yMap); 
    369380    decayPlot.addMap(zMap); 
    370     vMap = new ScalarMap(vType, Display.RGB); 
     381    decayPlot.addMap(zMapFit); 
     382    decayPlot.addMap(zMapRes); 
    371383    decayPlot.addMap(vMap); 
     384    //decayPlot.addMap(vMapFit); 
     385    decayPlot.addMap(vMapRes); 
    372386 
    373387    decayRend = new DefaultRendererJ3D(); 
     
    395409    xScale.setFont(font); 
    396410    xScale.setTitle("Time (ns)"); 
     411    xScale.setSnapToBox(true); 
    397412    AxisScale yScale = yMap.getAxisScale(); 
    398413    yScale.setFont(font); 
    399414    yScale.setTitle("Wavelength (nm)"); 
    400415    yScale.setSide(AxisScale.SECONDARY); 
     416    yScale.setSnapToBox(true); 
    401417    AxisScale zScale = zMap.getAxisScale(); 
    402418    zScale.setFont(font); 
    403419    zScale.setTitle("Count"); 
     420    zScale.setSnapToBox(true); // workaround for weird axis spacing issue 
     421    zMapFit.getAxisScale().setVisible(false); 
     422    zMapRes.getAxisScale().setVisible(false); 
    404423    GraphicsModeControl gmc = decayPlot.getGraphicsModeControl(); 
    405424    gmc.setScaleEnable(true); 
     
    550569    options.setLayout(new BoxLayout(options, BoxLayout.X_AXIS)); 
    551570 
    552     surface = new JRadioButton("Surface", true); 
    553     lines = new JRadioButton("Lines", false); 
    554571    linear = new JRadioButton("Linear", true); 
    555572    log = new JRadioButton("Log", false); 
    556573    perspective = new JRadioButton("Perspective", true); 
    557574    parallel = new JRadioButton("Parallel", false); 
     575    dataSurface = new JRadioButton("Surface", true); 
     576    dataLines = new JRadioButton("Lines", false); 
     577    fitSurface = new JRadioButton("Surface", false); 
     578    fitLines = new JRadioButton("Lines", true); 
     579    resSurface = new JRadioButton("Surface", false); 
     580    resLines = new JRadioButton("Lines", true); 
    558581 
    559582    JPanel showPanel = new JPanel(); 
     
    573596    showData.addActionListener(this); 
    574597    showPanel1.add(showData); 
    575     showLog = new JCheckBox("Log", false); 
     598    showLog = new JCheckBox("Log", true); 
    576599    showLog.addActionListener(this); 
    577600    showPanel1.add(showLog); 
     
    595618    }); 
    596619 
    597     options.add(makeRadioPanel("Data", surface, lines)); 
    598620    options.add(makeRadioPanel("Scale", linear, log)); 
    599621    options.add(makeRadioPanel("Projection", perspective, parallel)); 
     622    options.add(makeRadioPanel("Data", dataSurface, dataLines)); 
     623    options.add(makeRadioPanel("Fit", fitSurface, fitLines)); 
     624    options.add(makeRadioPanel("Residuals", resSurface, resLines)); 
    600625    options.add(showPanel); 
    601626    decayPane.add(options, BorderLayout.SOUTH); 
    602627    decayFrame.setContentPane(decayPane); 
    603628 
    604     cSlider.setValue(maxChan + 1); 
    605  
    606     // show windows on screen 
    607     decayFrame.pack(); 
    608     int decayWidth = decayFrame.getSize().width; 
    609     int decayHeight = 100 * decayWidth / 100; // 100% of width 
    610     int intensityHeight = decayHeight; 
    611     int intensityWidth = 85 * intensityHeight / 100; // 85% of height 
    612     // enlarge 3D window to fill most of the screen 
    613     int availWidth = ss.width - intensityWidth - 30; 
    614     int availHeight = ss.height - 70; 
     629    // adjust window sizes 
     630    intensityFrame.pack(); 
     631    int intensityWidth = intensityFrame.getSize().width; 
     632    int intensityHeight = intensityFrame.getSize().height; 
     633    int decayHeight = intensityHeight; 
     634    int decayWidth = 100 * decayHeight / 100; // 100% of width 
     635 
     636    // enlarge 3D window to fill more of the screen 
     637    int padWidth = 30, padHeight = 70; 
     638    int pw = padWidth / 2, ph = padHeight / 2; 
     639    int availWidth = screenSize.width - intensityWidth - padWidth; 
     640    int availHeight = screenSize.height - padHeight; 
    615641    int growWidth = availWidth - decayWidth; 
    616642    int growHeight = availHeight - decayHeight; 
     
    620646      decayHeight += grow; 
    621647    } 
    622     intensityFrame.setBounds(0, 0, intensityWidth, intensityHeight); 
     648 
     649    // widen 2D window to fill any leftover space 
     650    grow = screenSize.width - intensityWidth - decayWidth - padWidth; 
     651    intensityWidth += grow; 
     652    intensityHeight += grow; 
     653    decayFrame.setBounds(pw + intensityWidth, ph, decayWidth, decayHeight); 
     654    intensityFrame.setBounds(pw, ph, intensityWidth, intensityHeight); 
     655 
     656    // adjust console window to match 
     657    console.getWindow().setBounds(pw, ph + intensityHeight, 
     658      intensityWidth, decayHeight - intensityHeight); 
     659 
     660    // show windows on screen 
    623661    intensityFrame.setVisible(true); 
    624     decayFrame.setBounds(intensityWidth, 0, decayWidth, decayHeight); 
    625662    decayFrame.setVisible(true); 
     663    console.setVisible(true); 
    626664    progress.setProgress(++p); 
    627665    progress.close(); 
    628     plotData(true); 
     666    plotData(true, true); 
     667 
     668    try { Thread.sleep(200); } 
     669    catch (InterruptedException exc) { exc.printStackTrace(); } 
     670    cSlider.setValue(maxChan + 1); 
    629671  } 
    630672 
     
    633675  private Thread plotThread; 
    634676  private boolean plotCanceled; 
    635   private boolean rescale; 
     677  private boolean rescale, refit; 
    636678 
    637679  /** Plots the data in a separate thread. */ 
    638   public void plotData(boolean rescale) { 
     680  public void plotData(boolean rescale, boolean refit) { 
    639681    final boolean doRescale = rescale; 
     682    final boolean doRefit = refit; 
    640683    final SlimPlotter sp = this; 
    641684    new Thread("PlotSpawner") { 
     
    649692          } 
    650693          sp.rescale = doRescale; 
     694          sp.refit = doRefit; 
    651695          plotCanceled = false; 
    652696          plotThread = new Thread(sp, "Plotter"); 
     
    658702 
    659703  /** Handles cursor updates. */ 
    660   public void doCursor(double[] cursor, boolean rescale) { 
     704  public void doCursor(double[] cursor, boolean rescale, boolean refit) { 
    661705    double[] domain = cursorToDomain(iPlot, cursor); 
    662706    roiX = (int) Math.round(domain[0]); 
     
    667711    if (roiY >= height) roiY = height - 1; 
    668712    roiCount = 1; 
    669     plotData(rescale); 
     713    plotData(rescale, refit); 
    670714  } 
    671715 
     
    683727  public void actionPerformed(ActionEvent e) { 
    684728    Object src = e.getSource(); 
    685     if (src == surface || src == lines) plotData(false); 
    686     else if (src == linear || src == log) plotData(true); 
     729    if (src == dataSurface || src == dataLines) plotData(false, false); 
     730    else if (src == fitSurface || src == fitLines || 
     731      src == resSurface || src == resLines) 
     732    { 
     733      plotData(false, true); 
     734    } 
     735    else if (src == linear || src == log) plotData(true, true); 
    687736    else if (src == perspective || src == parallel) { 
    688737      try { 
     
    745794      drag = true; 
    746795      decayPlot.getDisplayRenderer(); 
    747       doCursor(iPlot.getDisplayRenderer().getCursor(), false); 
     796      doCursor(iPlot.getDisplayRenderer().getCursor(), false, false); 
    748797    } 
    749798    else if (id == DisplayEvent.MOUSE_RELEASED_CENTER) { 
    750799      drag = false; 
    751       doCursor(iPlot.getDisplayRenderer().getCursor(), true); 
     800      doCursor(iPlot.getDisplayRenderer().getCursor(), true, true); 
    752801    } 
    753802    else if (id == DisplayEvent.MOUSE_RELEASED_LEFT) { 
     
    757806        if (roiSet == null) { 
    758807          roiRef.setData(new Real(0)); 
    759           doCursor(pixelToCursor(iPlot, e.getX(), e.getY()), true); 
     808          doCursor(pixelToCursor(iPlot, e.getX(), e.getY()), true, true); 
    760809          iPlot.reAutoScale(); 
    761810        } 
     
    777826          } 
    778827          roiPercent = 100000 * roiCount / (width * height) / 1000.0; 
    779           plotData(true); 
     828          plotData(true, true); 
    780829        } 
    781830      } 
     
    793842    else if (id == DisplayEvent.MOUSE_DRAGGED) { 
    794843      if (!drag) return; // not a center mouse drag 
    795       doCursor(iPlot.getDisplayRenderer().getCursor(), false); 
     844      doCursor(iPlot.getDisplayRenderer().getCursor(), false, false); 
    796845    } 
    797846  } 
     
    800849 
    801850  public void run() { 
    802     ProgressMonitor progress = new ProgressMonitor(null, 
    803       "Plotting data", null, 0, channels * timeBins + 2); 
     851    ProgressMonitor progress = new ProgressMonitor(null, "Plotting data", 
     852      "Calculating sums", 0, 
     853      channels * timeBins + (refit ? channels : 0) + 1); 
    804854    progress.setMillisToPopup(100); 
    805855    progress.setMillisToDecideToPopup(50); 
     
    812862        " pixels (" + roiPercent + "%)"); 
    813863    } 
    814     boolean doLines = !surface.isSelected(); 
     864    boolean doDataLines = dataLines.isSelected(); 
     865    boolean doFitLines = fitLines.isSelected(); 
     866    boolean doResLines = resLines.isSelected(); 
    815867    boolean doLog = log.isSelected(); 
    816868 
     
    841893 
    842894    double[][] fitResults = null; 
    843     if (adjustPeaks) { 
     895    if (adjustPeaks && refit) { 
    844896      // perform exponential curve fitting: y(x) = a * e^(-b*t) + c 
    845897      progress.setNote("Fitting curves"); 
    846       progress.setProgress(++p); 
    847898      fitResults = new double[channels][]; 
    848899      ExpFunction func = new ExpFunction(); 
     
    850901      int num = timeBins - maxPeak; 
    851902      float[] xVals = new float[num]; 
    852       for (int i=0; i<xVals.length; i++) xVals[i] = i; 
     903      for (int i=0; i<num; i++) xVals[i] = i; 
    853904      float[] yVals = new float[num]; 
    854905      float[] weights = new float[num]; 
    855906      Arrays.fill(weights, 1); // no weighting 
    856       log("Computing exponential curve fit parameters"); 
     907      log("Computing fit parameters: y(t) = a * e^(-t/" + TAU + ") + c"); 
    857908      for (int c=0; c<channels; c++) { 
     909        log("\tChannel #" + (c + 1) + ":"); 
    858910        System.arraycopy(samps, timeBins * c + maxPeak, yVals, 0, num); 
    859         LMA lma = new LMA(func, params, new float[][] {xVals, yVals}, weights, 
    860           new JAMAMatrix(params.length, params.length)); 
     911        LMA lma = null; 
     912        lma = new LMA(func, params, new float[][] {xVals, yVals}, 
     913          weights, new JAMAMatrix(params.length, params.length)); 
    861914        lma.fit(); 
    862         log("\tChannel #" + (c + 1) + ":"); 
    863915        log("\t\titerations=" + lma.iterationCount); 
    864916        log("\t\tchi2=" + lma.chi2); 
     
    867919        log("\t\tc=" + lma.parameters[2]); 
    868920        fitResults[c] = lma.parameters; 
     921        progress.setProgress(++p); 
    869922      } 
    870923    } 
     
    874927      FlatField ff = new FlatField(bcvFunc, bcSet); 
    875928      ff.setSamples(new float[][] {samps}, false); 
    876       if (rescale) { 
    877         zMap.setRange(0, maxVal == 0 ? 1 : maxVal); 
    878         vMap.setRange(0, maxVal == 0 ? 1 : maxVal); 
    879       } 
    880       decayRef.setData(doLines ? ff.domainFactor(cType) : ff); 
     929      decayRef.setData(doDataLines ? ff.domainFactor(cType) : ff); 
    881930 
    882931      if (fitResults != null) { 
     
    894943              residuals[ndx] = samps[ndx] - fitSamps[ndx]; 
    895944            } 
    896             //if (residuals[ndx] < 0) residuals[ndx] = -residuals[ndx]; // abs 
    897945          } 
    898946        } 
    899947 
    900948        // construct "Fit" plot 
    901         FlatField fit = new FlatField(bcvFunc, bcSet); 
     949        FlatField fit = new FlatField(bcvFuncFit, bcSet); 
    902950        fit.setSamples(new float[][] {fitSamps}, false); 
    903         fitRef.setData(fit.domainFactor(cType)); // always lines for the fit 
     951        fitRef.setData(doFitLines ? fit.domainFactor(cType) : fit); 
    904952 
    905953        // construct "Residuals" plot 
    906         FlatField res = new FlatField(bcvFunc, bcSet); 
     954        FlatField res = new FlatField(bcvFuncRes, bcSet); 
    907955        res.setSamples(new float[][] {residuals}, false); 
    908         resRef.setData(res.domainFactor(cType)); // always lines for residuals 
     956        resRef.setData(doResLines ? res.domainFactor(cType) : res); 
     957      } 
     958 
     959      if (rescale) { 
     960        float max = maxVal == 0 ? 1 : maxVal; 
     961        zMap.setRange(0, max); 
     962        zMapFit.setRange(0, max); 
     963        zMapRes.setRange(0, max); 
     964        vMap.setRange(0, max); 
     965        //vMapFit.setRange(0, max); 
     966        //vMapRes.setRange(0, max); 
     967        //decayPlot.reAutoScale(); 
    909968      } 
    910969    } 
     
    10501109  } 
    10511110 
     1111  /**  
     1112   * HACK - OutputStream extension for filtering out hardcoded 
     1113   * RuntimeException.printStackTrace() exceptions within LMA library. 
     1114   */ 
     1115  public class ConsoleStream extends PrintStream { 
     1116    private PrintStream ps; 
     1117    private boolean ignore; 
     1118 
     1119    public ConsoleStream(OutputStream out) { 
     1120      super(out); 
     1121      ps = (PrintStream) out; 
     1122    } 
     1123 
     1124    public void println(String s) { 
     1125      if (s.equals("java.lang.RuntimeException: Matrix is singular.")) { 
     1126        ignore = true; 
     1127      } 
     1128      else if (ignore && !s.startsWith("\tat ")) ignore = false; 
     1129      if (!ignore) super.println(s); 
     1130    } 
     1131 
     1132    public void println(Object o) { 
     1133      String s = o.toString(); 
     1134      println(s); 
     1135    } 
     1136  } 
     1137 
    10521138  // -- Main method -- 
    10531139 
Note: See TracChangeset for help on using the changeset viewer.