* removed non-required methods from patch
[trakem2.git] / ini / trakem2 / display / Stack.java
blobeb7ae80c84233bf53a86684a0513dd2eabfe0d40
1 /**
2 *
3 */
4 package ini.trakem2.display;
6 import ij.IJ;
7 import ij.ImagePlus;
8 import ij.process.ImageProcessor;
9 import ini.trakem2.Project;
10 import ini.trakem2.utils.Utils;
12 import java.awt.AlphaComposite;
13 import java.awt.Composite;
14 import java.awt.Graphics2D;
15 import java.awt.Image;
16 import java.awt.Rectangle;
17 import java.awt.geom.AffineTransform;
18 import java.awt.geom.Area;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.TreeMap;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.Future;
28 import mpicbg.ij.stack.InverseTransformMapping;
29 import mpicbg.models.AffineModel2D;
30 import mpicbg.models.Boundable;
31 import mpicbg.models.InvertibleCoordinateTransformList;
32 import mpicbg.models.TranslationModel3D;
33 import mpicbg.trakem2.transform.InvertibleCoordinateTransform;
34 import mpicbg.util.Util;
36 /**
37 * @author saalfeld
40 public class Stack extends ZDisplayable
42 private String file_path;
43 private double depth = -1;
44 private double min;
45 private double max;
46 private InvertibleCoordinateTransform ict;
47 final private float[] boundsMin = new float[]{ 0, 0, 0 };
48 final private float[] boundsMax = new float[]{ 0, 0, 0 };
51 * References cached images in Loader whose unique identifier from the
52 * prespective of the stack are double:magnification and double:z.
54 final private HashMap< SliceViewKey, Long > cachedImages = new HashMap< SliceViewKey, Long >();
56 final private HashMap< Long, Future< Image > > futureImages = new HashMap< Long, Future<Image> >();
58 private static class SliceViewKey
60 final double magnification;
61 final double z;
63 SliceViewKey( final double magnification, final double z )
65 this.magnification = magnification;
66 this.z = z;
69 @Override
70 final public boolean equals( Object o )
72 final SliceViewKey k = ( SliceViewKey )o;
73 return k.magnification == magnification && k.z == z;
76 @Override
77 final public int hashCode()
79 return 0;
83 public Stack( final Project project, final String title, double x, double y, Layer initial_layer, final String file_path )
85 super( project, title, x, y );
86 this.file_path = file_path;
87 // ct ==> initial_layer;
88 final ImagePlus imp = project.getLoader().fetchImagePlus( this );
90 /* TODO scale regarding the Calibration and shift regarding x, y and the initial_layer */
91 depth = imp.getNSlices();
92 width = imp.getWidth();
93 height = imp.getHeight();
94 min = imp.getDisplayRangeMin();
95 max = imp.getDisplayRangeMax();
96 at.translate( x, y );
98 boundsMin[ 0 ] = ( float )x;
99 boundsMin[ 1 ] = ( float )y;
100 boundsMin[ 2 ] = ( float )initial_layer.getZ();
102 boundsMax[ 0 ] = ( float )( x + width );
103 boundsMax[ 1 ] = ( float )( y + height );
104 boundsMax[ 2 ] = ( float )( boundsMin[ 2 ] + depth );
106 addToDatabase();
109 /** Construct a Stack from an XML entry. */
110 public Stack(Project project, long id, HashMap ht, HashMap ht_links) {
111 super(project, id, ht, ht_links);
112 // parse specific fields
114 final Iterator it = ht.entrySet().iterator();
115 while (it.hasNext()) {
116 final Map.Entry entry = (Map.Entry)it.next();
117 final String key = (String)entry.getKey();
118 final String data = (String)entry.getValue();
119 if (key.equals("min")) {
120 this.min = Double.parseDouble(data);
121 } else if (key.equals("max")) {
122 this.max = Double.parseDouble(data);
123 } else if (key.equals("file_path")) {
124 this.file_path = data;
125 } else if (key.equals("depth")) {
126 this.depth = Double.parseDouble(data);
129 boundsMin[ 0 ] = 0;
130 boundsMin[ 1 ] = 0;
131 boundsMin[ 2 ] = 0;
133 boundsMax[ 0 ] = ( float )width;
134 boundsMax[ 1 ] = ( float )height;
135 boundsMax[ 2 ] = ( float )depth;
138 /* (non-Javadoc)
139 * @see ini.trakem2.display.ZDisplayable#getFirstLayer()
141 @Override
142 public Layer getFirstLayer()
144 // TODO Auto-generated method stub
145 return null;
148 /* (non-Javadoc)
149 * @see ini.trakem2.display.ZDisplayable#intersects(java.awt.geom.Area, double, double)
151 @Override
152 public boolean intersects( Area area, double z_first, double z_last )
154 // TODO Auto-generated method stub
155 return false;
158 /* (non-Javadoc)
159 * @see ini.trakem2.display.ZDisplayable#linkPatches()
161 @Override
162 public void linkPatches()
164 // TODO Auto-generated method stub
168 /* (non-Javadoc)
169 * @see ini.trakem2.display.Displayable#clone(ini.trakem2.Project, boolean)
171 @Override
172 public Displayable clone( Project pr, boolean copy_id )
174 // TODO Auto-generated method stub
175 return null;
178 /* (non-Javadoc)
179 * @see ini.trakem2.display.Displayable#isDeletable()
181 @Override
182 public boolean isDeletable()
184 // TODO Auto-generated method stub
185 return false;
188 public String getFilePath(){
189 return this.file_path;
192 public long estimateImageFileSize()
194 if ( -1 == depth )
195 return IJ.maxMemory() / 2;
196 return (long) (width * height * depth * 4);
199 @Override
200 public void paint(
201 final Graphics2D g,
202 final double magnification,
203 final boolean active,
204 final int channels,
205 final Layer active_layer )
208 final AffineTransform atp = this.at;
210 //final Image image = project.getLoader().fetchImage(this,0);
211 //Utils.log2("Patch " + id + " painted image " + image);
213 final double currentZ = active_layer.getZ();
214 Image image = null;
215 synchronized ( cachedImages )
217 final SliceViewKey sliceViewKey = new SliceViewKey( magnification, currentZ );
218 final long imageId;
219 Long imageIdL = cachedImages.get( sliceViewKey );
220 if ( imageIdL == null )
222 imageId = project.getLoader().getNextTempId();
223 cachedImages.put( sliceViewKey, imageId );
225 else
227 /* fetch the image from cache---still, it may be that it is not there... */
228 imageId = imageIdL;
229 image = project.getLoader().getCached( cachedImages.get( sliceViewKey ), 0 );
232 if ( image == null )
234 /* image has to be generated */
236 synchronized ( futureImages )
238 Future< Image > fu = futureImages.get( imageId );
239 if (null == fu) {
240 futureImages.put( imageId, project.getLoader().doLater( new Callable< Image >() {
241 public Image call() {
242 final ImagePlus imp = project.getLoader().fetchImagePlus( Stack.this );
243 final ImageProcessor ip = imp.getStack().getProcessor( 1 ).createProcessor( ( int )Math.ceil( boundsMax[ 0 ] - boundsMin[ 0 ] ), ( int )Math.ceil( boundsMax[ 1 ] - boundsMin[ 1 ] ) );
245 final InvertibleCoordinateTransformList< mpicbg.models.InvertibleCoordinateTransform > ictl = new InvertibleCoordinateTransformList< mpicbg.models.InvertibleCoordinateTransform >();
246 if ( ict != null ) ictl.add( ict );
247 final TranslationModel3D sliceShift = new TranslationModel3D();
248 sliceShift.set( 0, 0, ( float )-currentZ );
249 ictl.add( sliceShift );
251 final InverseTransformMapping< InvertibleCoordinateTransformList< mpicbg.models.InvertibleCoordinateTransform > > mapping = new InverseTransformMapping< InvertibleCoordinateTransformList< mpicbg.models.InvertibleCoordinateTransform > >( ictl );
252 mapping.mapInterpolated( imp.getStack(), ip );
253 final Image image = ip.createImage();
255 if (null == image) {
256 Utils.log2("Stack.paint: null image, returning");
257 return null; // TEMPORARY from lazy repaints after closing a Project
260 project.getLoader().cacheAWT( imageId, image );
262 synchronized ( futureImages ) {
263 futureImages.remove( imageId );
266 //Display.repaint( active_layer, Stack.this );
267 Display.repaint( active_layer );
269 return image;
271 }));
272 } //else {
273 // Utils.log2( "fu is not null" );
274 // // We don't do anything: we wait for itself to launch a repaint event
275 // }
280 if ( image != null) {
282 //arrange transparency
283 Composite original_composite = null;
284 if (alpha != 1.0f) {
285 original_composite = g.getComposite();
286 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
289 final AffineTransform backup = g.getTransform();
290 final AffineTransform screenScale = new AffineTransform( backup );
291 screenScale.scale( 1.0 / magnification, 1.0 / magnification );
292 g.setTransform( screenScale );
294 g.drawImage(image, atp, null);
296 g.setTransform( backup );
298 //Transparency: fix composite back to original.
299 if (alpha != 1.0f) {
300 g.setComposite(original_composite);
306 static public final void exportDTD( final StringBuffer sb_header, final HashSet hs, final String indent ) {
307 String type = "t2_stack";
308 if (hs.contains(type)) return;
309 hs.add( type );
310 sb_header.append(indent).append("<!ELEMENT t2_stack (").append(Displayable.commonDTDChildren()).append(",(iict_transform|iict_transform_list)?)>\n");
311 Displayable.exportDTD( type, sb_header, hs, indent );
312 sb_header.append(indent).append(TAG_ATTR1).append(type).append(" file_path CDATA #REQUIRED>\n")
313 .append(indent).append(TAG_ATTR1).append(type).append(" depth CDATA #REQUIRED>\n");
316 /** Opens and closes the tag and exports data. The image is saved in the directory provided in @param any as a String. */
317 public void exportXML(StringBuffer sb_body, String indent, Object any) { // TODO the Loader should handle the saving of images, not this class.
318 String in = indent + "\t";
319 sb_body.append(indent).append("<t2_stack\n");
320 String rel_path = null;
322 super.exportXML(sb_body, in, any);
323 String[] RGB = Utils.getHexRGBColor(color);
325 sb_body.append(in).append("file_path=\"").append(file_path).append("\"\n")
326 .append(in).append("style=\"fill-opacity:").append(alpha).append(";stroke:#").append(RGB[0]).append(RGB[1]).append(RGB[2]).append(";\"\n")
327 .append(in).append("depth=\"").append(depth).append("\"\n")
328 .append(in).append("min=\"").append(min).append("\"\n")
329 .append(in).append("max=\"").append(max).append("\"\n")
332 sb_body.append(indent).append(">\n");
334 if (null != ict) {
335 sb_body.append(ict.toXML(in)).append('\n');
338 super.restXML(sb_body, in, any);
340 sb_body.append(indent).append("</t2_stack>\n");
343 @Override
344 protected Rectangle getBounds( final Rectangle rect )
346 final AffineModel2D a = new AffineModel2D();
347 a.set( at );
349 final float[] rMin = new float[]{ Float.MAX_VALUE, Float.MAX_VALUE };
350 final float[] rMax = new float[]{ -Float.MAX_VALUE, -Float.MAX_VALUE };
352 final float[] l = new float[]{ boundsMin[ 0 ], boundsMin[ 1 ] };
354 a.apply( l );
355 Util.min( rMin, l );
356 Util.max( rMax, l );
358 l[ 0 ] = boundsMin[ 0 ];
359 l[ 1 ] = boundsMax[ 1 ];
360 a.apply( l );
361 Util.min( rMin, l );
362 Util.max( rMax, l );
364 l[ 0 ] = boundsMax[ 0 ];
365 l[ 1 ] = boundsMin[ 1 ];
366 a.apply( l );
367 Util.min( rMin, l );
368 Util.max( rMax, l );
370 l[ 0 ] = boundsMax[ 0 ];
371 l[ 1 ] = boundsMax[ 1 ];
372 a.apply( l );
373 Util.min( rMin, l );
374 Util.max( rMax, l );
376 rect.x = ( int )rMin[ 0 ];
377 rect.y = ( int )rMin[ 1 ];
378 rect.width = ( int )Math.ceil( rMax[ 0 ] - rect.x );
379 rect.height = ( int )Math.ceil( rMax[ 1 ] - rect.y );
381 return rect;
384 private void updateBounds()
386 boundsMin[ 0 ] = 0;
387 boundsMin[ 1 ] = 0;
388 boundsMin[ 2 ] = 0;
390 boundsMax[ 0 ] = ( float )width;
391 boundsMax[ 1 ] = ( float )height;
392 boundsMax[ 2 ] = ( float )depth;
394 if ( ict == null ) return;
395 else if ( Boundable.class.isInstance( ict ) )
396 ( ( Boundable )ict ).estimateBounds( boundsMin, boundsMax );
397 else
399 final ArrayList< Layer > layers = layer_set.getLayers();
400 boundsMax[ 0 ] = ( float )layer_set.width;
401 boundsMax[ 1 ] = ( float )layer_set.height;
402 boundsMax[ 2 ] = ( float )( layers.get( layers.size() - 1 ).getZ() - layers.get( 0 ).getZ() );
406 /** For reconstruction purposes, overwrites the present InvertibleCoordinateTransform, if any, with the given one. */
407 public void setInvertibleCoordinateTransformSilently( final InvertibleCoordinateTransform ict )
409 cachedImages.clear();
410 this.ict = ict;
411 updateBounds();