home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / pc / java / unuam4cs / slicercontrol.java < prev    next >
Encoding:
Java Source  |  1996-08-14  |  10.8 KB  |  371 lines

  1. /*
  2.  * @(#)/SlicerControl.java    1.4 96/03/31 by Andrew Barclay abb@nuccard.eushc.org
  3.  *
  4.  * Copyright (c) 1995 Andrew B. Barclay All Rights Reserved.
  5.  */
  6.  
  7. import java.applet.Applet ;
  8. import java.awt.Button ;
  9. import java.awt.Color ;
  10. import java.awt.Dimension ;
  11. import java.awt.Event ;
  12. import java.awt.FlowLayout ;
  13. import java.awt.Graphics ;
  14. import java.awt.Image ;
  15. import java.awt.image.ImageObserver ;
  16. import java.util.Enumeration ;
  17. import java.util.Hashtable ;
  18. import java.util.StringTokenizer ;
  19. import java.util.Vector ;
  20.  
  21. import SlicerDisplay ;
  22. import SlicerDisplayGraphic ;
  23. import SlicerDisplayInfo ;
  24. import Volume ;
  25.  
  26. /**
  27.  * An applet for orthogonal slicing of volume images.
  28.  *
  29.  * @see Volume
  30.  * @see SliceVolumeFilter
  31.  * @see SlicerDisplay
  32.  *
  33.  * @version    1.4 96/03/31
  34.  * @author     Andrew Barclay
  35.  *
  36.  * This could evolve into an interface with the register, incrImage,
  37.  * and setSelected methods.  For now, I'm experimenting.
  38.  */
  39.  
  40. public class SlicerControl extends Applet implements ImageObserver {
  41.     Image src = null ;
  42.     int srcwidth = 0, srcheight = 0 ;
  43.     int vdims[] = new int[3] ;
  44.     double vsize[] = new double[3] ;
  45.     Volume vol = null ;
  46.  
  47.     // State info
  48.     Hashtable displayinfo = new Hashtable() ;
  49.     // When updating hash marks, need to have a one-to-one axis->SDI mapping.
  50.     SlicerDisplayInfo axisdisplayinfo[] = new SlicerDisplayInfo[3] ;
  51.     SlicerDisplayInfo selectedSDI = null ;
  52.     final int littlewidth = 20, littleheight = 20 ;
  53.     final boolean debug = false ;
  54.  
  55.     public String getAppletInfo() {
  56.     return "SlicerControl by Andrew Barclay" ;
  57.     }
  58.  
  59.     public String[][] getParameterInfo() {
  60.     String[][] info = {
  61.         {"src",    "url",        "XY multi-slice image"},
  62.         {"vdims",    "WIDTHxHEIGHTxDEPTH","volume dimensions (pixels)"},
  63.         {"vsize",    "WIDTHxHEIGHTxDEPTH","voxel size (mm)"},
  64.     } ;
  65.     return info ;
  66.     }
  67.  
  68.     public void init() {
  69.     // Release src and vol to let getVolume know
  70.     // that we're initializing (important for restarts).
  71.     src = null ;
  72.     vol = null ;
  73.  
  74.     String param = getParameter( "VDIMS" ) ;
  75.     vdims[0] = vdims[1] = vdims[2] = 0 ;
  76.     StringTokenizer st = new StringTokenizer( param, "x", false ) ;
  77.     for( int i = 0 ; st.hasMoreTokens() && i < 3 ; i++ ) {
  78.         vdims[i] = Integer.parseInt( st.nextToken() ) ;
  79.     }
  80.     dbg( "init - param="+param+" vdims="+vdims[0]+","+vdims[1]+","+vdims[2] ) ;
  81.  
  82.     param = getParameter( "VSIZE" ) ;
  83.     vsize[0] = vsize[1] = vsize[2] = 0 ;
  84.     st = new StringTokenizer( param, "x", false ) ;
  85.     for( int i = 0 ; st.hasMoreTokens() && i < 3 ; i++ ) {
  86.         vsize[i] = parseDouble( st.nextToken() ) ;
  87.     }
  88.     dbg( "init - param="+param+" vsize="+vsize[0]+","+vsize[1]+","+vsize[2] ) ;
  89.  
  90.     // do this last, 'cause getVolume() must wait for it
  91.     // before staring consumption (is there a better way?)
  92.     param = getParameter( "SRC" ) ;
  93.     src = getImage( getDocumentBase(), param ) ;
  94.     dbg( "init - param="+param+" src="+src ) ;
  95.     src.getWidth(this) ;
  96.     src.getHeight(this) ;
  97.     prepareImage( src, littlewidth, littleheight, this ) ;
  98.  
  99.     // Let the display applets know that we're ready.
  100.     notifyAll() ;
  101.  
  102.     setLayout( new FlowLayout( FlowLayout.CENTER, 0, 0 ) ) ;
  103.     add( new Button("Stop") ) ;
  104.     }
  105.  
  106.     // why is there no parseDouble??
  107.     private double parseDouble( String s ) {
  108.     return (Double.valueOf(s)).doubleValue() ;
  109.     }
  110.  
  111.     private synchronized void getVolume() {
  112.     while( src == null ) {
  113.         try {
  114.         showStatus( "SlicerControl: waiting for a src image..." ) ;
  115.         wait(1000);
  116.         } catch (InterruptedException ex) { }
  117.     }
  118.  
  119.     // Volume will get the src image's width and height
  120.     // synchronously, so may be waiting here.
  121.     vol = new Volume( src, vdims, vsize ) ;
  122.     }
  123.  
  124.     /*
  125.      * This is a 2D array that maps a slice,axis pair to a volume axis.
  126.      * The first index is the axis number of the slice (x->0, y->1).
  127.      * The second index is the slice type, or the axis to which the
  128.      * slice is orthogonal in the volume (x->0, y->1, z->2).
  129.      * e.g:  The x axis of the x slice, [0][0], maps onto the z axis of
  130.      * the volume.  The y axis of the z slice, [1][2], maps onto the y
  131.      * axis of the volume.
  132.      */
  133.     static int slicetovol[][] = { { 2, 0, 0 }, { 1, 2, 1 } } ;
  134.     // These are the border/hashmark colors (dimmed until selected).
  135.     static Color displaycolor[] = { Color.red, Color.green, Color.yellow } ;
  136.  
  137.     // @return SlicerDisplayInfo to SlicerDisplay's
  138.     public SlicerDisplayInfo register( SlicerDisplay display, int axis, double thickness ) {
  139.     // bug: need to use a string for the axis and do some real
  140.     // error checking here.
  141.     if( axis < 0 || axis >= 3 ) {
  142.         return (SlicerDisplayInfo)null ;
  143.     }
  144.  
  145.     // Must synchronize with the init and make sure we have the 
  146.     // volume sizes before proceeding.
  147.     if( vol == null ) {
  148.         getVolume() ;
  149.     }
  150.  
  151.     if( thickness == 0.0 ) {
  152.         thickness = vsize[axis] ;
  153.     }
  154.  
  155.     // Initialize in the SlicerDisplayInfo object.
  156.     SlicerDisplayInfo di = new SlicerDisplayInfo() ;
  157.  
  158.     di.axis = axis ;
  159.     // border/hashmark colors are darker until they're selected.
  160.     di.color = displaycolor[axis].darker() ;
  161.  
  162.     di.vsize = new double[3] ;
  163.     di.vsize[0] = vsize[slicetovol[0][axis]] ;
  164.     di.vsize[1] = vsize[slicetovol[1][axis]] ;
  165.     di.vsize[2] = thickness ;
  166.  
  167.     // Set position to the center slice to start.
  168.     di.slices = new Hashtable() ;
  169.     di.nslices = (int)Math.ceil(
  170.         ((double)vdims[axis] * vsize[axis])/thickness ) ;
  171.     di.position = di.vsize[2] * (di.nslices - 1)/2 ;
  172.     di.display = display ;
  173.  
  174.     // Add to the hashtable of displays.
  175.     // should this be synchronized?
  176.     displayinfo.put( display, di ) ;
  177.     axisdisplayinfo[axis] = di ;
  178.  
  179.     updateHashmarks( di ) ;
  180.     return di ;
  181.     }
  182.  
  183.     private Object positionkey( double position ) {
  184.     /*
  185.      * controls precision of hashing (1/100th of a mm)
  186.      * (otherwise we get rounding errors)
  187.      */
  188.     return( new Double( Math.rint( 100.0*position ) ) ) ;
  189.     }
  190.  
  191.     // Update all the hash marks in the displays.
  192.     public void updateHashmarks() {
  193.     // Loop through the displayinfo hashtable.
  194.     for( Enumeration en = displayinfo.elements() ; en.hasMoreElements() ; ) {
  195.         SlicerDisplayInfo di = (SlicerDisplayInfo)en.nextElement() ;
  196.         if( di != null ) {
  197.         updateHashmarks( di ) ;
  198.         }
  199.     }
  200.     }
  201.     // Update all the hash marks in the displays.
  202.     public void updateHashmarks( SlicerDisplayInfo di ) {
  203.     SlicerDisplayGraphic dg = new SlicerDisplayGraphic() ;
  204.  
  205.     // Set the x hash mark position and color.
  206.     int xcnum = slicetovol[0][di.axis] ;
  207.     SlicerDisplayInfo dix = axisdisplayinfo[xcnum] ;
  208.     if( dix != null ) {
  209.         // Hash width is proportional to slice thickness.
  210.         dg.xhashwidth = 1.0 / dix.nslices ;
  211.         if( dix == selectedSDI ) {
  212.         // Selected display marked by continuous line...
  213.         dg.xhashlength = 1.0 ;
  214.         } else {
  215.         // otherwise, 1/10th of display width.
  216.         dg.xhashlength = 0.1 ;
  217.         }
  218.         dg.xhashpos = dix.position / 
  219.         (vdims[xcnum] * vsize[xcnum]) ;
  220.         dg.xhashcolor = dix.color ;
  221.     } else {
  222.         dg.xhashcolor = null ;
  223.     }
  224.  
  225.     // Set the y hash mark position and color.
  226.     int ycnum = slicetovol[1][di.axis] ;
  227.     SlicerDisplayInfo diy = axisdisplayinfo[ycnum] ;
  228.     if( diy != null ) {
  229.         // Hash width is proportional to slice thickness.
  230.         dg.yhashwidth = 1.0 / diy.nslices ;
  231.         if( diy == selectedSDI ) {
  232.         // Selected display marked by continuous line...
  233.         dg.yhashlength = 1.0 ;
  234.         } else {
  235.         // otherwise, 1/10th of display width.
  236.         dg.yhashlength = 0.1 ;
  237.         }
  238.         dg.yhashpos = diy.position / 
  239.         (vdims[ycnum] * vsize[ycnum]) ;
  240.         dg.yhashcolor = diy.color ;
  241.     } else {
  242.         dg.yhashcolor = null ;
  243.     }
  244.  
  245.     dg.bordercolor = di.color ;
  246.     di.display.updateHashmarks( dg ) ;
  247.     }
  248.  
  249.     // Get a slice for a display object.  Slices are cached as built.
  250.     // @return the slice at the given position in the volume
  251.     public synchronized Image getSlice( Object key, double position ) {
  252.     SlicerDisplayInfo di = (SlicerDisplayInfo)displayinfo.get( key ) ;
  253.     if( di == null ) {
  254.         return null ;
  255.     }
  256.  
  257.     // bounds check
  258.     if( position < 0.0 ) {
  259.         di.position = 0.0 ;
  260.     } else if( position > di.vsize[2]*(di.nslices-1) ) {
  261.         di.position = di.vsize[2]*(di.nslices-1) ;
  262.     } else {
  263.         di.position = position ;
  264.     }
  265.  
  266.     Object slicekey = positionkey(di.position) ;
  267.     Image theimage = (Image)di.slices.get( slicekey ) ;
  268.     if( theimage == null ) {
  269.         showStatus( "building axis"+di.axis+" at pos="+di.position+" mm" ) ;
  270.         theimage = vol.Slice( this, di.position, di.axis ) ;
  271.         di.slices.put( slicekey, theimage ) ;
  272.     } else {
  273.         showStatus( "moving to axis"+di.axis+" at pos="+di.position+" mm" ) ;
  274.     }
  275.  
  276.     updateHashmarks() ;
  277.     return theimage ;
  278.     }
  279.  
  280.     // Updates the SlicerDisplay's to reflect current volume position.
  281.     public synchronized void setSelected( Object key ) {
  282.     SlicerDisplayInfo di = (SlicerDisplayInfo)displayinfo.get( key ) ;
  283.     if( di == null ) {
  284.         return ;
  285.     }
  286.     dbg( "selected display="+di.axis ) ;
  287.  
  288.     // darken the old.
  289.     if( selectedSDI != null ) {
  290.         selectedSDI.color = displaycolor[selectedSDI.axis].darker() ;
  291.     }
  292.  
  293.     selectedSDI = di ;
  294.     // brighten the new.
  295.     selectedSDI.color = selectedSDI.color.brighter() ;
  296.     updateHashmarks() ;
  297.     }
  298.  
  299.     public synchronized boolean imageUpdate( Image img, int infoflags,
  300.         int x, int y, int w, int h ) {
  301.     if( img != src ) {
  302.         return false ;
  303.     }
  304.  
  305.     if ((infoflags & WIDTH) != 0) {
  306.         dbg( "*** SlicerControl: Got src width = " + w ) ;
  307.         srcwidth = w ;
  308.     }
  309.     if ((infoflags & HEIGHT) != 0) {
  310.         dbg( "*** SlicerControl: Got src height = " + h ) ;
  311.         srcheight = h ;
  312.     }
  313.     if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
  314.         dbg( "*** SlicerControl: Finished loading volume." ) ;
  315.     } else if ((infoflags & SOMEBITS) != 0) {
  316.         //dbg( "Got some bits, x=" + x + " y=" + y ) ;
  317.         dbg( "*** SlicerControl: Loading volume data ..." ) ;
  318.         if( srcwidth > 0 && srcheight > 0 ) {
  319.         int totalk = (srcwidth * srcheight)/1024 ;
  320.         int imsize = littlewidth * littleheight ;
  321.         int sofar = x + y * littlewidth ;
  322.         int pcnt = (int)(100.0 * (double)sofar / (double)imsize ) ;
  323.         showStatus( "SlicerControl "+pcnt+"% of "+totalk+"K") ;
  324.         }
  325.     }
  326.     if( (infoflags & ERROR) != 0 ) {
  327.         dbg( "*** SlicerControl: Error loading volume data." ) ;
  328.     } 
  329.  
  330.     return true ;
  331.     }
  332.  
  333.     // The stop button's handler.
  334.     public boolean action(Event evt, Object arg) {
  335.     dbg( "SlicerControl.action() arg="+arg ) ;
  336.     if( "Stop".equals(arg) && vol != null ) {
  337.         /* I don't see any other way to stop the download???
  338.          * Volume.StopConsuming() didn't work.
  339.          * SliceStack.stopInYourTracks() didn't work.
  340.          */
  341.  
  342.         /*
  343.          * This sometimes takes a couple shots, but it gets
  344.          * the job done.  NOTHING works after that.  If there
  345.          * were only some way to find the image loading threads,
  346.          * but they're not under my control...
  347.          */
  348.         /*
  349.         Thread threads[] = new Thread[100] ;
  350.         int nt = Thread.enumerate( threads ) ;
  351.         showStatus( "SlicerControl: Stopping "+nt+" threads." ) ;
  352.  
  353.         for( int i = 0 ; i < nt ; i++ ) {
  354.         threads[i].stop() ;
  355.         }
  356.          */
  357.  
  358.         showStatus( "Help!!! I can't stop this crazy thing." ) ;
  359.     }
  360.     return true ;
  361.     }
  362.  
  363.     void dbg( String s ) {
  364.     if( debug ) {
  365.         System.out.println( s ) ;
  366.         showStatus( s ) ;
  367.     }
  368.     }
  369. }
  370.  
  371.