home *** CD-ROM | disk | FTP | other *** search
Java Source | 1996-08-14 | 10.8 KB | 371 lines |
- /*
- * @(#)/SlicerControl.java 1.4 96/03/31 by Andrew Barclay abb@nuccard.eushc.org
- *
- * Copyright (c) 1995 Andrew B. Barclay All Rights Reserved.
- */
-
- import java.applet.Applet ;
- import java.awt.Button ;
- import java.awt.Color ;
- import java.awt.Dimension ;
- import java.awt.Event ;
- import java.awt.FlowLayout ;
- import java.awt.Graphics ;
- import java.awt.Image ;
- import java.awt.image.ImageObserver ;
- import java.util.Enumeration ;
- import java.util.Hashtable ;
- import java.util.StringTokenizer ;
- import java.util.Vector ;
-
- import SlicerDisplay ;
- import SlicerDisplayGraphic ;
- import SlicerDisplayInfo ;
- import Volume ;
-
- /**
- * An applet for orthogonal slicing of volume images.
- *
- * @see Volume
- * @see SliceVolumeFilter
- * @see SlicerDisplay
- *
- * @version 1.4 96/03/31
- * @author Andrew Barclay
- *
- * This could evolve into an interface with the register, incrImage,
- * and setSelected methods. For now, I'm experimenting.
- */
-
- public class SlicerControl extends Applet implements ImageObserver {
- Image src = null ;
- int srcwidth = 0, srcheight = 0 ;
- int vdims[] = new int[3] ;
- double vsize[] = new double[3] ;
- Volume vol = null ;
-
- // State info
- Hashtable displayinfo = new Hashtable() ;
- // When updating hash marks, need to have a one-to-one axis->SDI mapping.
- SlicerDisplayInfo axisdisplayinfo[] = new SlicerDisplayInfo[3] ;
- SlicerDisplayInfo selectedSDI = null ;
- final int littlewidth = 20, littleheight = 20 ;
- final boolean debug = false ;
-
- public String getAppletInfo() {
- return "SlicerControl by Andrew Barclay" ;
- }
-
- public String[][] getParameterInfo() {
- String[][] info = {
- {"src", "url", "XY multi-slice image"},
- {"vdims", "WIDTHxHEIGHTxDEPTH","volume dimensions (pixels)"},
- {"vsize", "WIDTHxHEIGHTxDEPTH","voxel size (mm)"},
- } ;
- return info ;
- }
-
- public void init() {
- // Release src and vol to let getVolume know
- // that we're initializing (important for restarts).
- src = null ;
- vol = null ;
-
- String param = getParameter( "VDIMS" ) ;
- vdims[0] = vdims[1] = vdims[2] = 0 ;
- StringTokenizer st = new StringTokenizer( param, "x", false ) ;
- for( int i = 0 ; st.hasMoreTokens() && i < 3 ; i++ ) {
- vdims[i] = Integer.parseInt( st.nextToken() ) ;
- }
- dbg( "init - param="+param+" vdims="+vdims[0]+","+vdims[1]+","+vdims[2] ) ;
-
- param = getParameter( "VSIZE" ) ;
- vsize[0] = vsize[1] = vsize[2] = 0 ;
- st = new StringTokenizer( param, "x", false ) ;
- for( int i = 0 ; st.hasMoreTokens() && i < 3 ; i++ ) {
- vsize[i] = parseDouble( st.nextToken() ) ;
- }
- dbg( "init - param="+param+" vsize="+vsize[0]+","+vsize[1]+","+vsize[2] ) ;
-
- // do this last, 'cause getVolume() must wait for it
- // before staring consumption (is there a better way?)
- param = getParameter( "SRC" ) ;
- src = getImage( getDocumentBase(), param ) ;
- dbg( "init - param="+param+" src="+src ) ;
- src.getWidth(this) ;
- src.getHeight(this) ;
- prepareImage( src, littlewidth, littleheight, this ) ;
-
- // Let the display applets know that we're ready.
- notifyAll() ;
-
- setLayout( new FlowLayout( FlowLayout.CENTER, 0, 0 ) ) ;
- add( new Button("Stop") ) ;
- }
-
- // why is there no parseDouble??
- private double parseDouble( String s ) {
- return (Double.valueOf(s)).doubleValue() ;
- }
-
- private synchronized void getVolume() {
- while( src == null ) {
- try {
- showStatus( "SlicerControl: waiting for a src image..." ) ;
- wait(1000);
- } catch (InterruptedException ex) { }
- }
-
- // Volume will get the src image's width and height
- // synchronously, so may be waiting here.
- vol = new Volume( src, vdims, vsize ) ;
- }
-
- /*
- * This is a 2D array that maps a slice,axis pair to a volume axis.
- * The first index is the axis number of the slice (x->0, y->1).
- * The second index is the slice type, or the axis to which the
- * slice is orthogonal in the volume (x->0, y->1, z->2).
- * e.g: The x axis of the x slice, [0][0], maps onto the z axis of
- * the volume. The y axis of the z slice, [1][2], maps onto the y
- * axis of the volume.
- */
- static int slicetovol[][] = { { 2, 0, 0 }, { 1, 2, 1 } } ;
- // These are the border/hashmark colors (dimmed until selected).
- static Color displaycolor[] = { Color.red, Color.green, Color.yellow } ;
-
- // @return SlicerDisplayInfo to SlicerDisplay's
- public SlicerDisplayInfo register( SlicerDisplay display, int axis, double thickness ) {
- // bug: need to use a string for the axis and do some real
- // error checking here.
- if( axis < 0 || axis >= 3 ) {
- return (SlicerDisplayInfo)null ;
- }
-
- // Must synchronize with the init and make sure we have the
- // volume sizes before proceeding.
- if( vol == null ) {
- getVolume() ;
- }
-
- if( thickness == 0.0 ) {
- thickness = vsize[axis] ;
- }
-
- // Initialize in the SlicerDisplayInfo object.
- SlicerDisplayInfo di = new SlicerDisplayInfo() ;
-
- di.axis = axis ;
- // border/hashmark colors are darker until they're selected.
- di.color = displaycolor[axis].darker() ;
-
- di.vsize = new double[3] ;
- di.vsize[0] = vsize[slicetovol[0][axis]] ;
- di.vsize[1] = vsize[slicetovol[1][axis]] ;
- di.vsize[2] = thickness ;
-
- // Set position to the center slice to start.
- di.slices = new Hashtable() ;
- di.nslices = (int)Math.ceil(
- ((double)vdims[axis] * vsize[axis])/thickness ) ;
- di.position = di.vsize[2] * (di.nslices - 1)/2 ;
- di.display = display ;
-
- // Add to the hashtable of displays.
- // should this be synchronized?
- displayinfo.put( display, di ) ;
- axisdisplayinfo[axis] = di ;
-
- updateHashmarks( di ) ;
- return di ;
- }
-
- private Object positionkey( double position ) {
- /*
- * controls precision of hashing (1/100th of a mm)
- * (otherwise we get rounding errors)
- */
- return( new Double( Math.rint( 100.0*position ) ) ) ;
- }
-
- // Update all the hash marks in the displays.
- public void updateHashmarks() {
- // Loop through the displayinfo hashtable.
- for( Enumeration en = displayinfo.elements() ; en.hasMoreElements() ; ) {
- SlicerDisplayInfo di = (SlicerDisplayInfo)en.nextElement() ;
- if( di != null ) {
- updateHashmarks( di ) ;
- }
- }
- }
- // Update all the hash marks in the displays.
- public void updateHashmarks( SlicerDisplayInfo di ) {
- SlicerDisplayGraphic dg = new SlicerDisplayGraphic() ;
-
- // Set the x hash mark position and color.
- int xcnum = slicetovol[0][di.axis] ;
- SlicerDisplayInfo dix = axisdisplayinfo[xcnum] ;
- if( dix != null ) {
- // Hash width is proportional to slice thickness.
- dg.xhashwidth = 1.0 / dix.nslices ;
- if( dix == selectedSDI ) {
- // Selected display marked by continuous line...
- dg.xhashlength = 1.0 ;
- } else {
- // otherwise, 1/10th of display width.
- dg.xhashlength = 0.1 ;
- }
- dg.xhashpos = dix.position /
- (vdims[xcnum] * vsize[xcnum]) ;
- dg.xhashcolor = dix.color ;
- } else {
- dg.xhashcolor = null ;
- }
-
- // Set the y hash mark position and color.
- int ycnum = slicetovol[1][di.axis] ;
- SlicerDisplayInfo diy = axisdisplayinfo[ycnum] ;
- if( diy != null ) {
- // Hash width is proportional to slice thickness.
- dg.yhashwidth = 1.0 / diy.nslices ;
- if( diy == selectedSDI ) {
- // Selected display marked by continuous line...
- dg.yhashlength = 1.0 ;
- } else {
- // otherwise, 1/10th of display width.
- dg.yhashlength = 0.1 ;
- }
- dg.yhashpos = diy.position /
- (vdims[ycnum] * vsize[ycnum]) ;
- dg.yhashcolor = diy.color ;
- } else {
- dg.yhashcolor = null ;
- }
-
- dg.bordercolor = di.color ;
- di.display.updateHashmarks( dg ) ;
- }
-
- // Get a slice for a display object. Slices are cached as built.
- // @return the slice at the given position in the volume
- public synchronized Image getSlice( Object key, double position ) {
- SlicerDisplayInfo di = (SlicerDisplayInfo)displayinfo.get( key ) ;
- if( di == null ) {
- return null ;
- }
-
- // bounds check
- if( position < 0.0 ) {
- di.position = 0.0 ;
- } else if( position > di.vsize[2]*(di.nslices-1) ) {
- di.position = di.vsize[2]*(di.nslices-1) ;
- } else {
- di.position = position ;
- }
-
- Object slicekey = positionkey(di.position) ;
- Image theimage = (Image)di.slices.get( slicekey ) ;
- if( theimage == null ) {
- showStatus( "building axis"+di.axis+" at pos="+di.position+" mm" ) ;
- theimage = vol.Slice( this, di.position, di.axis ) ;
- di.slices.put( slicekey, theimage ) ;
- } else {
- showStatus( "moving to axis"+di.axis+" at pos="+di.position+" mm" ) ;
- }
-
- updateHashmarks() ;
- return theimage ;
- }
-
- // Updates the SlicerDisplay's to reflect current volume position.
- public synchronized void setSelected( Object key ) {
- SlicerDisplayInfo di = (SlicerDisplayInfo)displayinfo.get( key ) ;
- if( di == null ) {
- return ;
- }
- dbg( "selected display="+di.axis ) ;
-
- // darken the old.
- if( selectedSDI != null ) {
- selectedSDI.color = displaycolor[selectedSDI.axis].darker() ;
- }
-
- selectedSDI = di ;
- // brighten the new.
- selectedSDI.color = selectedSDI.color.brighter() ;
- updateHashmarks() ;
- }
-
- public synchronized boolean imageUpdate( Image img, int infoflags,
- int x, int y, int w, int h ) {
- if( img != src ) {
- return false ;
- }
-
- if ((infoflags & WIDTH) != 0) {
- dbg( "*** SlicerControl: Got src width = " + w ) ;
- srcwidth = w ;
- }
- if ((infoflags & HEIGHT) != 0) {
- dbg( "*** SlicerControl: Got src height = " + h ) ;
- srcheight = h ;
- }
- if ((infoflags & (FRAMEBITS | ALLBITS)) != 0) {
- dbg( "*** SlicerControl: Finished loading volume." ) ;
- } else if ((infoflags & SOMEBITS) != 0) {
- //dbg( "Got some bits, x=" + x + " y=" + y ) ;
- dbg( "*** SlicerControl: Loading volume data ..." ) ;
- if( srcwidth > 0 && srcheight > 0 ) {
- int totalk = (srcwidth * srcheight)/1024 ;
- int imsize = littlewidth * littleheight ;
- int sofar = x + y * littlewidth ;
- int pcnt = (int)(100.0 * (double)sofar / (double)imsize ) ;
- showStatus( "SlicerControl "+pcnt+"% of "+totalk+"K") ;
- }
- }
- if( (infoflags & ERROR) != 0 ) {
- dbg( "*** SlicerControl: Error loading volume data." ) ;
- }
-
- return true ;
- }
-
- // The stop button's handler.
- public boolean action(Event evt, Object arg) {
- dbg( "SlicerControl.action() arg="+arg ) ;
- if( "Stop".equals(arg) && vol != null ) {
- /* I don't see any other way to stop the download???
- * Volume.StopConsuming() didn't work.
- * SliceStack.stopInYourTracks() didn't work.
- */
-
- /*
- * This sometimes takes a couple shots, but it gets
- * the job done. NOTHING works after that. If there
- * were only some way to find the image loading threads,
- * but they're not under my control...
- */
- /*
- Thread threads[] = new Thread[100] ;
- int nt = Thread.enumerate( threads ) ;
- showStatus( "SlicerControl: Stopping "+nt+" threads." ) ;
-
- for( int i = 0 ; i < nt ; i++ ) {
- threads[i].stop() ;
- }
- */
-
- showStatus( "Help!!! I can't stop this crazy thing." ) ;
- }
- return true ;
- }
-
- void dbg( String s ) {
- if( debug ) {
- System.out.println( s ) ;
- showStatus( s ) ;
- }
- }
- }
-
-