Logo Search packages:      
Sourcecode: pixelmed version File versions  Download package

SpectraPanel.java

/* Copyright (c) 2001-2005, David A. Clunie DBA Pixelmed Publishing. All rights reserved. */

package com.pixelmed.display;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;

import javax.swing.JComponent;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.SwingUtilities;

import java.util.Arrays;
import java.util.HashMap;

import com.pixelmed.event.ApplicationEventDispatcher;
import com.pixelmed.event.Event;
import com.pixelmed.event.EventContext;
import com.pixelmed.event.SelfRegisteringListener;
import com.pixelmed.display.event.FrameSelectionChangeEvent;
import com.pixelmed.display.event.FrameSortOrderChangeEvent;
import com.pixelmed.display.event.SourceImageSelectionChangeEvent;
import com.pixelmed.display.event.StatusChangeEvent; 
import com.pixelmed.dicom.*;

// for localizer and background...

import com.pixelmed.geometry.*;
import javax.vecmath.*;

/**
 * <p>Implements a component that can display a single or multi-frame spectra in a
 * single panel, over an optional background image, with scrolling through frames
 * of a multi-frame spectra, resizing to the size of the panel, feedback of cursor
 * position status.</p>
 *
 * @see com.pixelmed.display.SourceSpectra
 * @see com.pixelmed.display.SourceImage
 *
 * @author  dclunie
 */
00057 public class SpectraPanel extends PlotGraph implements MouseListener, MouseMotionListener {

      private static final String identString = "@(#) $Header: /userland/cvs/pixelmed/imgbook/com/pixelmed/display/SpectraPanel.java,v 1.25 2007/04/03 13:11:08 dclunie Exp $";
      
      private float[][] srcSpectra;
      private int nSrcSpectra;
      private int currentSrcSpectrumIndex;            // N.B. this is the current scrolling index and MUST ALWAYS BE DEREFERENCED through currentSrcSpectraSortOrder (if non-null) before use
      private int[] currentSrcSpectraSortOrder;
      
      /***/
      private GeometryOfVolume spectroscopyGeometry;
      /***/
      private SpectroscopyVolumeLocalization spectroscopyVolumeLocalization;
      
      /**
       * <p>Get the geometry of the frames currently loaded in the spectroscopy panel.</p>
       *
       * @return  the geometry of the frames
       */
00076       public GeometryOfVolume getSpectroscopyGeometry() {
            return spectroscopyGeometry;
      }

      /**
       * <p>Get the localization volume of the spectra currently loaded in the spectroscopy panel.</p>
       *
       * @return  the localization volume
       */
00085       public SpectroscopyVolumeLocalization getSpectroscopyVolumeLocalization() {
            return spectroscopyVolumeLocalization;
      }

      // Event stuff ...

      int lastmiddley;
      
      /**
       * @param   e
       */
00096       public void mouseClicked(MouseEvent e) {}
      /**
       * @param   e
       */
00100       public void mouseEntered(MouseEvent e) {}
      /**
       * @param   e
       */
00104       public void mouseExited(MouseEvent e) {}

      /**
       * @param   e
       */
00109       public void mouseDragged(MouseEvent e) {
//System.err.println("mouseDragged event"+e);
            if (SwingUtilities.isMiddleMouseButton(e)) {
                  int delta = e.getY()-lastmiddley;
                  int newSrcSpectrumIndex = currentSrcSpectrumIndex + delta;
                  if (newSrcSpectrumIndex >= nSrcSpectra) newSrcSpectrumIndex=nSrcSpectra-1;
                  if (newSrcSpectrumIndex < 0) newSrcSpectrumIndex=0;
                  // don't send an event unless it is actually necessary ...
//System.err.println("mouseDragged middle newSrcSpectrumIndex="+newSrcSpectrumIndex+" currentSrcSpectrumIndex="+currentSrcSpectrumIndex);
                  if (newSrcSpectrumIndex != currentSrcSpectrumIndex)
                        ApplicationEventDispatcher.getApplicationEventDispatcher().processEvent(new FrameSelectionChangeEvent(typeOfPanelEventContext,newSrcSpectrumIndex));
                  lastmiddley=e.getY();         // helps a lot when clipped at top or bottom of range
            }
      }

      /**
       * @param   e
       */
00127       public void mouseMoved(MouseEvent e) {
//System.err.println("SpectraPanel.mouseMoved: "+e.getX()+" "+e.getY());
            {
                  double x = e.getX();
                  double y = e.getY();

                  StringBuffer sbuf = new StringBuffer();
                  sbuf.append("(");
                  sbuf.append((int)(x/widthOfTile));
                  sbuf.append(":");
                  sbuf.append((int)(x%widthOfTile/widthOfTile*samplesPerTile));
                  sbuf.append(",");
                  sbuf.append((int)(y/heightOfTile));
                  sbuf.append(")");

                  ApplicationEventDispatcher.getApplicationEventDispatcher().processEvent(new StatusChangeEvent(sbuf.toString()));
            }
      }

      /**
       * @param   e
       */
00149       public void mousePressed(MouseEvent e) {
//System.err.println("mousePressed event"+e);
            if (SwingUtilities.isMiddleMouseButton(e)) {
                  lastmiddley=e.getY();
            }
      }

      /**
       * @param   e
       */
00159       public void mouseReleased(MouseEvent e) {}

      // Event stuff ...

      /***/
      EventContext typeOfPanelEventContext;
      /***/
      EventContext backgroundImageEventContext;

      // implement FrameSelectionChangeListener to respond to events from self or elsewhere ...
      
      private OurFrameSelectionChangeListener ourFrameSelectionChangeListener;

      class OurFrameSelectionChangeListener extends SelfRegisteringListener {
      
            public OurFrameSelectionChangeListener(EventContext eventContext) {
                  super("com.pixelmed.display.event.FrameSelectionChangeEvent",eventContext);
//System.err.println("SpectraPanel.OurFrameSelectionChangeListener():");
            }
            
            /**
             * @param   e
             */
            public void changed(Event e) {
                  FrameSelectionChangeEvent fse = (FrameSelectionChangeEvent)e;
                  int newCurrentSrcSpectrumIndex = fse.getIndex();
                  if (currentSrcSpectrumIndex != newCurrentSrcSpectrumIndex) {
//System.err.println("SingleImagePanel.OurFrameSelectionChangeListener.changed(): new values");
                        currentSrcSpectrumIndex=newCurrentSrcSpectrumIndex;
                        cachedBackgroundImage=null;                     // since background image may be for different frame
                        repaint();
                  }
                  else {
//System.err.println("SpectraPanel.OurFrameSelectionChangeListener.changed(): same values");
                  }
            }
      }
      
      // implement FrameSortOrderChangeListener to respond to events from self or elsewhere ...
      
      private OurFrameSortOrderChangeListener ourFrameSortOrderChangeListener;

      class OurFrameSortOrderChangeListener extends SelfRegisteringListener {
      
            public OurFrameSortOrderChangeListener(EventContext eventContext) {
                  super("com.pixelmed.display.event.FrameSortOrderChangeEvent",eventContext);
//System.err.println("SingleImagePanel.OurFrameSortOrderChangeListener():");
            }
            
            /**
             * @param   e
             */
            public void changed(Event e) {
                  FrameSortOrderChangeEvent fso = (FrameSortOrderChangeEvent)e;
                  int[] newSrcSpectraSortOrder = fso.getSortOrder();
                  int newCurrentSrcSpectrumIndex = fso.getIndex();
                  if (currentSrcSpectrumIndex != newCurrentSrcSpectrumIndex
                   || currentSrcSpectraSortOrder != newSrcSpectraSortOrder
                   || !Arrays.equals(currentSrcSpectraSortOrder,newSrcSpectraSortOrder)) {
System.err.println("SpectraPanel.OurFrameSortOrderChangeListener.changed(): new values");
                        currentSrcSpectrumIndex=newCurrentSrcSpectrumIndex;
                        currentSrcSpectraSortOrder=newSrcSpectraSortOrder;    // change even if null in event (request to go back to implicit order)
                        cachedBackgroundImage=null;                     // since background image may be for different frame
                        repaint();
                  }
                  else {
System.err.println("SpectraPanel.OurFrameSortOrderChangeListener.changed(): same values");
                  }
            }
      }
      
      // Implement setting and loading of background image in response to event ...

      /***/
      SourceImage backgroundSImg;
      /***/
      int currentBackgroundSrcImageIndex;
      /***/
      int[] currentBackgroundSrcImageSortOrder;
      /***/
      private GeometryOfVolume backgroundSrcImageGeometry;

      // caches of stuff ...
      
      /***/
      BufferedImage cachedBackgroundImage;
      /***/
      private HashMap cacheOfImagesForSpectra;
      /***/
      private Rectangle cacheForBounds;

      private OurBackgroundSourceImageSelectionChangeListener ourBackgroundSourceImageSelectionChangeListener;

      class OurBackgroundSourceImageSelectionChangeListener extends SelfRegisteringListener {
      
            public OurBackgroundSourceImageSelectionChangeListener(EventContext eventContext) {
                  super("com.pixelmed.display.event.SourceImageSelectionChangeEvent",eventContext);
//System.err.println("SpectraPanel.OurBackgroundSourceImageSelectionChangeListener():");
            }
            
            /**
             * @param   e
             */
            public void changed(Event e) {
//System.err.println("SpectraPanel.OurBackgroundSourceImageSelectionChangeListener.changed():");
                  SourceImageSelectionChangeEvent sis = (SourceImageSelectionChangeEvent)e;
                  backgroundSImg = sis.getSourceImage();
                  currentBackgroundSrcImageSortOrder = sis.getSortOrder();
                  currentBackgroundSrcImageIndex = sis.getIndex();      // irrelevant ... will be overridden by nearest selection
                  backgroundSrcImageGeometry = sis.getGeometryOfVolume();
                  cachedBackgroundImage=null;                     // forces application of new values on repaint
                  cacheOfImagesForSpectra=null;
                  cacheForBounds=null;
                  repaint();
            }
      }

      /**
       * <p>Build a panel in which to display the supplied spectra.</p>
       *
       * @param   srcSpectra                    the spectra to display
       * @param   nTilesPerColumn                     the number of tiles per column
       * @param   nTilesPerRow                        the number of tiles per row
       * @param   minimum                             the minimum data value to display (bottom of a tile)
       * @param   maximum                             the maximum data value to display (top of a tile)
       * @param   spectroscopyGeometry                the 3D location of the acquired spectroscopy data
       * @param   spectroscopyVolumeLocalization            the 3D localization performed prior to acquisition of the spectroscopy data
       * @param   typeOfPanelEventContext
       * @param   backgroundImageEventContext
       */
00289       public SpectraPanel(float[][] srcSpectra,int nTilesPerColumn,int nTilesPerRow,float minimum,float maximum,
                  GeometryOfVolume spectroscopyGeometry,SpectroscopyVolumeLocalization spectroscopyVolumeLocalization,
                  EventContext typeOfPanelEventContext,
                  EventContext backgroundImageEventContext) {

            super(nTilesPerColumn,nTilesPerRow,minimum,maximum);
            
            this.srcSpectra=srcSpectra;
            nSrcSpectra=srcSpectra.length;
            currentSrcSpectrumIndex=0;
            this.samples=srcSpectra[currentSrcSpectrumIndex];
            samplesPerRow=samples.length/nTilesPerColumn;
            samplesPerTile=samplesPerRow/nTilesPerRow;
            currentSrcSpectraSortOrder=null;
            
            this.spectroscopyGeometry=spectroscopyGeometry;
            this.spectroscopyVolumeLocalization=spectroscopyVolumeLocalization;
            
            backgroundSImg = null;
            currentBackgroundSrcImageSortOrder = null;
            currentBackgroundSrcImageIndex = 0;
            backgroundSrcImageGeometry=null;
            
            addMouseListener(this);
            addMouseMotionListener(this);
            
            this.typeOfPanelEventContext=typeOfPanelEventContext;
            ourFrameSelectionChangeListener = new OurFrameSelectionChangeListener(typeOfPanelEventContext);
            ourFrameSortOrderChangeListener = new OurFrameSortOrderChangeListener(typeOfPanelEventContext);
            this.backgroundImageEventContext=backgroundImageEventContext;
            ourBackgroundSourceImageSelectionChangeListener = new OurBackgroundSourceImageSelectionChangeListener(backgroundImageEventContext);

            // clear caches ...
            cachedBackgroundImage=null;
            cacheOfImagesForSpectra=null;
            cacheForBounds=null;
      }
      
      /**
       * @param   g
       */
00330       public void paintComponent(Graphics g) {
//System.err.println("SpectraPanel.paintComponent");
            int useSrcSpectrumIndex = currentSrcSpectraSortOrder == null ? currentSrcSpectrumIndex : currentSrcSpectraSortOrder[currentSrcSpectrumIndex];
            samples=srcSpectra[currentSrcSpectrumIndex]; 
            Cursor was = getCursor();
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
            // the handle of the float[] spectral data already selected in "samples" is used as the key for the cache
            Rectangle currentBounds = this.getBounds();
            if (cacheForBounds != null && currentBounds.width == cacheForBounds.width && currentBounds.height == cacheForBounds.height && cacheOfImagesForSpectra != null) {
                  BufferedImage cachedImageOfSpectra = (BufferedImage)cacheOfImagesForSpectra.get(samples);
                  if (cachedImageOfSpectra != null) {
//System.err.println("SpectraPanel.paintComponent: using cached image");
                        g.drawImage(cachedImageOfSpectra,0,0,this);
                        setCursor(was);
                        return;
                  }
            }
            else {
//System.err.println("SpectraPanel.paintComponent: flushing cache");
                  cacheOfImagesForSpectra = null;     // bounds may have changed so flush the cache
            }
            if (cacheOfImagesForSpectra == null) {
//System.err.println("SpectraPanel.paintComponent: creating new cache of spectra as images");
                  cacheOfImagesForSpectra = new HashMap();
                  cacheForBounds = currentBounds;
            }
            
            Rectangle boundsOfSpectra = null;
            Rectangle boundsOfBackgroundImage = null;
            if (cachedBackgroundImage == null) {
//System.err.println("SpectraPanel.paintComponent: trying to create new cached background image");
                  if (spectroscopyGeometry != null && backgroundSrcImageGeometry != null) {
                        int index = backgroundSrcImageGeometry.findClosestSliceInSamePlane(spectroscopyGeometry.getGeometryOfSlices()[useSrcSpectrumIndex]);
                        cachedBackgroundImage = backgroundSImg.getBufferedImage(index);
                        if (cachedBackgroundImage.getColorModel().getNumComponents() == 1) {
//System.err.println("SpectraPanel.paintComponent: building windowed grayscale image");
                              boolean signed=backgroundSImg.isSigned();
                              boolean inverted=backgroundSImg.isInverted();
                              double useSlope;
                              double useIntercept;
                              boolean hasPad = backgroundSImg.isPadded();
                              int pad = hasPad ? backgroundSImg.getPadValue() : 0;
                              int padRangeLimit = hasPad ? backgroundSImg.getPadRangeLimit() : 0;
                              ModalityTransform modalityTransform = backgroundSImg.getModalityTransform();
                              if (modalityTransform != null) {
                                    useSlope = modalityTransform.getRescaleSlope(index);
                                    useIntercept = modalityTransform.getRescaleIntercept(index);
                              }
                              else {
                                    useSlope=1.0;
                                    useIntercept=0.0;
                              }
                              double windowWidth;
                              double windowCenter;
                              VOITransform voiTransform = backgroundSImg.getVOITransform();
                              if (voiTransform != null && voiTransform.getNumberOfTransforms(index) > 0) {
                                    windowWidth=voiTransform.getWidth(index,0);
                                    windowCenter=voiTransform.getCenter(index,0);
                              }
                              else {
                                    double ourMin = backgroundSImg.getMinimum()*useSlope+useIntercept;
                                    double ourMax = backgroundSImg.getMaximum()*useSlope+useIntercept;
                                    windowWidth=(ourMax-ourMin);
                                    windowCenter=(ourMax+ourMin)/2.0;
                              }
                              cachedBackgroundImage = SingleImagePanel.applyWindowCenterAndWidthLinear(cachedBackgroundImage,
                                                      windowCenter,windowWidth,signed,inverted,useSlope,useIntercept,hasPad,pad,padRangeLimit);
                        }
                        
                        GeometryOfSlice geom = spectroscopyGeometry.getGeometryOfSlices()[useSrcSpectrumIndex];
                        Point3d    tlhc = geom.getTLHC();
                        Vector3d    row = geom.getRow();
                        Vector3d column = geom.getColumn();
                        
                        Point3d[] backgroundCorners = LocalizerPoster.getCornersOfSourceRectangleInSourceSpace(backgroundSrcImageGeometry.getGeometryOfSlices()[index]);
                                  backgroundCorners = LocalizerPoster.transformPointsFromSourceSpaceIntoSpecifiedSpace(backgroundCorners,tlhc,row,column);
                        Point3d[]    spectraCorners = LocalizerPoster.getCornersOfSourceRectangleInSourceSpace(geom);
                                     spectraCorners = LocalizerPoster.transformPointsFromSourceSpaceIntoSpecifiedSpace(spectraCorners,tlhc,row,column);
                                   
                        // the corners are now in the same plane and Z can be ignored
                        
                        Point3d[] intersectionCorners = LocalizerPoster.getIntersectionOfRectanglesInXYPlane(backgroundCorners,spectraCorners);
                        
                        boundsOfSpectra = LocalizerPoster.getBoundsOfContainedRectangle(intersectionCorners,spectraCorners,this.getBounds());
                        boundsOfBackgroundImage = LocalizerPoster.getBoundsOfContainedRectangle(intersectionCorners,backgroundCorners,
                              new Rectangle(cachedBackgroundImage.getWidth(),cachedBackgroundImage.getHeight()));

                  }
            }
            actuallyPaintComponent(g,cachedBackgroundImage,boundsOfSpectra,boundsOfBackgroundImage);
//System.err.println("SpectraPanel.paintComponent: adding to cache");
            cacheOfImagesForSpectra.put(samples,imageOfRenderedPlot);
            setCursor(was);
      }
      
      public void deconstruct() {
//System.err.println("SpectraPanel.deconstruct()");
            // avoid "listener leak"
            if (ourFrameSelectionChangeListener != null) {
                  ApplicationEventDispatcher.getApplicationEventDispatcher().removeListener(ourFrameSelectionChangeListener);
                  ourFrameSelectionChangeListener=null;
            }
            if (ourFrameSortOrderChangeListener != null) {
                  ApplicationEventDispatcher.getApplicationEventDispatcher().removeListener(ourFrameSortOrderChangeListener);
                  ourFrameSortOrderChangeListener=null;
            }
            if (ourBackgroundSourceImageSelectionChangeListener != null) {
                  ApplicationEventDispatcher.getApplicationEventDispatcher().removeListener(ourBackgroundSourceImageSelectionChangeListener);
                  ourBackgroundSourceImageSelectionChangeListener=null;
            }

      }
      
      /*
       * @param container
       */
       public static void deconstructAllSpectraPanelsInContainer(Container container) {
            Component[] components = container.getComponents();                     
//System.err.println("SpectraPanel.deconstructAllSpectraPanelsInContainer(): deconstructing old SpectraPanels components.length="+components.length);
            for (int i=0; i<components.length; ++i) {
                  Component component = components[i];
                  if (component instanceof SpectraPanel) {
                        ((SpectraPanel)component).deconstruct();
                  }
            }
      }

      protected void finalize() throws Throwable {
//System.err.println("SpectraPanel.finalize()");
            deconstruct();          // just in case wasn't already called, and garbage collection occurs
            super.finalize();
      }
}


Generated by  Doxygen 1.6.0   Back to index