fixed improper generic parameter use in Tree.duplicateAs > Map, necessary
[trakem2.git] / TrakEM2_ / src / main / java / ini / trakem2 / display / NonLinearTransformMode.java
blob3933aa97c6f0279c6771554a849844ea4daae62f
1 package ini.trakem2.display;
3 import ini.trakem2.utils.Bureaucrat;
4 import ini.trakem2.utils.ProjectToolbar;
5 import ini.trakem2.utils.Utils;
6 import ini.trakem2.utils.Worker;
8 import java.awt.BasicStroke;
9 import java.awt.Graphics2D;
10 import java.awt.Rectangle;
11 import java.awt.Stroke;
12 import java.awt.event.MouseEvent;
13 import java.awt.geom.AffineTransform;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Set;
20 import java.util.concurrent.Future;
22 import mpicbg.models.AbstractAffineModel2D;
23 import mpicbg.models.CoordinateTransformMesh;
24 import mpicbg.models.IllDefinedDataPointsException;
25 import mpicbg.models.NoninvertibleModelException;
26 import mpicbg.models.NotEnoughDataPointsException;
27 import mpicbg.models.Point;
28 import mpicbg.models.PointMatch;
29 import mpicbg.models.SimilarityModel2D;
30 import mpicbg.models.TranslationModel2D;
31 import mpicbg.trakem2.transform.AffineModel2D;
32 import mpicbg.trakem2.transform.CoordinateTransform;
33 import mpicbg.trakem2.transform.CoordinateTransformList;
34 import mpicbg.trakem2.transform.MovingLeastSquaresTransform2;
35 import mpicbg.trakem2.transform.TransformMeshMappingWithMasks;
36 import mpicbg.trakem2.transform.TransformMeshMappingWithMasks.ImageProcessorWithMasks;
38 public class NonLinearTransformMode extends GroupingMode {
40 @Override
41 protected void doPainterUpdate( final Rectangle r, final double m )
43 try
45 final CoordinateTransform mlst = createCT();
46 final SimilarityModel2D toWorld = new SimilarityModel2D();
47 toWorld.set( 1.0 / m, 0, r.x - ScreenPatchRange.pad / m, r.y - ScreenPatchRange.pad / m );
49 final mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform > ctl = new mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform >();
50 ctl.add( toWorld );
51 ctl.add( mlst );
52 ctl.add( toWorld.createInverse() );
54 final CoordinateTransformMesh ctm = new CoordinateTransformMesh( ctl, 32, r.width * m + 2 * ScreenPatchRange.pad, r.height * m + 2 * ScreenPatchRange.pad );
55 final TransformMeshMappingWithMasks< CoordinateTransformMesh > mapping = new TransformMeshMappingWithMasks< CoordinateTransformMesh >( ctm );
57 final HashMap<Paintable, GroupingMode.ScreenPatchRange<?>> screenPatchRanges = this.screenPatchRanges; // keep a pointer to the current list
58 for ( final GroupingMode.ScreenPatchRange spr : screenPatchRanges.values())
60 if (screenPatchRanges != this.screenPatchRanges) {
61 // List has been updated; restart painting
62 // TODO should it call itself: doPainterUpdate( r, m );
63 break;
65 spr.update( mapping );
68 catch ( final NotEnoughDataPointsException e ) {}
69 catch ( final NoninvertibleModelException e ) {}
70 catch ( final IllDefinedDataPointsException e ) {}
71 catch ( final Exception e ) { e.printStackTrace(); }
74 private class NonLinearTransformSource extends GroupingMode.GroupedGraphicsSource {
75 @Override
76 public void paintOnTop(final Graphics2D g, final Display display, final Rectangle srcRect, final double magnification) {
78 final Stroke original_stroke = g.getStroke();
79 final AffineTransform original = g.getTransform();
80 g.setTransform( new AffineTransform() );
81 g.setStroke( new BasicStroke( 1.0f ) );
82 for ( final Point p : points )
84 final double[] w = p.getW();
85 Utils.drawPoint( g, ( int )Math.round( magnification * ( w[ 0 ] - srcRect.x ) ), ( int )Math.round( magnification * ( w[ 1 ] - srcRect.y ) ) );
88 g.setTransform( original );
89 g.setStroke( original_stroke );
93 @Override
94 protected ScreenPatchRange createScreenPathRange(final PatchRange range, final Rectangle srcRect, final double magnification) {
95 return new NonLinearTransformMode.ScreenPatchRange(range, srcRect, magnification);
98 private static class ScreenPatchRange extends GroupingMode.ScreenPatchRange<TransformMeshMappingWithMasks<?>> {
99 ScreenPatchRange( final PatchRange range, final Rectangle srcRect, final double magnification ) {
100 super(range, srcRect, magnification);
102 @Override
103 public void update( final TransformMeshMappingWithMasks< ? > mapping )
105 ipTransformed.reset();
106 if ( mask == null )
108 mapping.map( ip, ipTransformed );
110 else
112 maskTransformed.reset();
113 final ImageProcessorWithMasks ipm = new ImageProcessorWithMasks( ip, mask, null );
114 final ImageProcessorWithMasks tpm = new ImageProcessorWithMasks( ipTransformed, maskTransformed, null );
115 mapping.map( ipm, tpm );
117 if (null != transformedImage) transformedImage.flush();
118 transformedImage = super.makeImage( ipTransformed, maskTransformed );
122 @Override
123 protected GroupingMode.GroupedGraphicsSource createGroupedGraphicSource() {
124 return new NonLinearTransformSource();
127 public NonLinearTransformMode(final Display display, final List<Displayable> selected) {
128 super(display, selected);
129 ProjectToolbar.setTool(ProjectToolbar.SELECT);
130 super.initThreads();
133 public NonLinearTransformMode(final Display display) {
134 this(display, display.getSelection().getSelected());
137 private final Collection< Point > points = new ArrayList< Point >();
139 private Point p_clicked = null;
141 @Override
142 public void mousePressed( final MouseEvent me, final int x_p, final int y_p, final double magnification )
144 /* find if clicked on a point */
145 p_clicked = null;
146 double min = Double.MAX_VALUE;
147 final Point mouse = new Point( new double[]{ x_p, y_p } );
148 final double a = 64.0 / magnification / magnification;
149 for ( final Point p : points )
151 final double sd = Point.squareDistance( p, mouse );
152 if ( sd < min && sd < a )
154 p_clicked = p;
155 min = sd;
159 if ( me.isShiftDown() )
161 if ( null == p_clicked )
163 /* add one */
166 if ( points.size() > 0 )
169 * Create a pseudo-invertible (TransformMesh) for the screen.
171 final CoordinateTransform mlst = createCT();
172 final SimilarityModel2D toWorld = new SimilarityModel2D();
173 toWorld.set( 1.0 / magnification, 0, srcRect.x, srcRect.y );
174 final SimilarityModel2D toScreen = toWorld.createInverse();
176 final mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform > ctl = new mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform >();
177 ctl.add( toWorld );
178 ctl.add( mlst );
179 ctl.add( toScreen );
181 final CoordinateTransformMesh ctm = new CoordinateTransformMesh(
182 ctl,
184 ( int )Math.ceil( srcRect.width * magnification ),
185 ( int )Math.ceil( srcRect.height * magnification ) );
187 final double[] l = mouse.getL();
188 toScreen.applyInPlace( l );
189 ctm.applyInverseInPlace( l );
190 toWorld.applyInPlace( l );
192 points.add( mouse );
193 p_clicked = mouse;
195 catch ( final Exception e )
197 Utils.log( "Could not add point" );
198 e.printStackTrace();
201 else if ( Utils.isControlDown( me ) )
203 // remove it
204 points.remove(p_clicked);
205 p_clicked = null;
210 @Override
211 public void mouseDragged( final MouseEvent me, final int x_p, final int y_p, final int x_d, final int y_d, final int x_d_old, final int y_d_old )
213 if ( null != p_clicked )
215 final double[] w = p_clicked.getW();
216 w[ 0 ] += x_d - x_d_old;
217 w[ 1 ] += y_d - y_d_old;
218 painter.update();
222 @Override
223 public void mouseReleased( final MouseEvent me, final int x_p, final int y_p, final int x_d, final int y_d, final int x_r, final int y_r )
225 // bring to screen coordinates
226 mouseDragged( me, x_p, y_p, x_r, y_r, x_d, y_d );
227 p_clicked = null; // so isDragging can return the right state
230 @Override
231 public boolean isDragging()
233 return null != p_clicked;
236 private final void setUndoState() {
237 layer.getParent().addEditStep(new Displayable.DoEdits(new HashSet<Displayable>(originalPatches)).init(new String[]{"data", "at", "width", "height"}));
240 final private Future< Boolean > applyToPatch( final Patch patch ) throws Exception
242 final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
243 final AffineTransform pat = new AffineTransform();
244 pat.translate( -pbox.x, -pbox.y );
245 pat.preConcatenate( patch.getAffineTransform() );
247 final AffineModel2D toWorld = new AffineModel2D();
248 toWorld.set( pat );
250 final CoordinateTransform mlst = createCT();
252 final CoordinateTransformList< CoordinateTransform > ctl = new CoordinateTransformList< CoordinateTransform >();
253 ctl.add( toWorld );
254 ctl.add( mlst );
255 ctl.add( toWorld.createInverse() );
257 patch.appendCoordinateTransform( ctl );
258 return patch.updateMipMaps();
261 @Override
262 public boolean apply()
264 return apply( null );
267 public boolean apply( final Set< Layer > sublist )
270 /* Set undo step to reflect initial state before any transformations */
271 setUndoState();
273 Bureaucrat.createAndStart( new Worker.Task( "Applying transformations" )
275 @Override
276 public void exec()
278 final ArrayList< Future< Boolean > > futures = new ArrayList< Future< Boolean > >();
280 synchronized ( updater )
282 /* apply to selected patches */
283 for ( final Paintable p : screenPatchRanges.keySet() )
285 if ( p instanceof Patch )
289 futures.add( applyToPatch( ( Patch )p ) );
291 catch ( final Exception e )
293 e.printStackTrace();
297 /* apply to other layers if there are any */
298 if ( !( sublist == null || sublist.isEmpty() ) )
300 for ( final Layer layer : sublist )
302 for ( final Displayable p : layer.getDisplayables( Patch.class ) )
306 futures.add( applyToPatch( ( Patch )p ) );
308 catch ( final Exception e )
310 e.printStackTrace();
317 /* Flush images */
318 for ( final GroupingMode.ScreenPatchRange<?> spr : new HashSet< GroupingMode.ScreenPatchRange<?> >( screenPatchRanges.values() ) )
320 spr.flush();
323 /* Wait until all mipmaps are regenerated */
324 for ( final Future<?> fu : futures )
327 fu.get();
329 catch ( final Exception ie )
332 // Set undo step to reflect final state after applying transformations
333 setUndoState();
336 }, layer.getProject() );
338 super.quitThreads();
340 return true;
343 private CoordinateTransform createCT() throws Exception
345 final Collection< PointMatch > pm = new ArrayList<PointMatch>();
346 for ( final Point p : points )
348 pm.add( new PointMatch( new Point( p.getL() ), new Point( p.getW() ) ) );
351 * TODO replace this with the desired parameters of the transformation
353 final MovingLeastSquaresTransform2 mlst = new MovingLeastSquaresTransform2();
354 mlst.setAlpha( 1.0f );
355 Class< ? extends AbstractAffineModel2D< ? > > c = AffineModel2D.class;
356 switch (points.size()) {
357 case 1:
358 c = TranslationModel2D.class;
359 break;
360 case 2:
361 c = SimilarityModel2D.class;
362 break;
363 default:
364 break;
366 mlst.setModel( c );
367 mlst.setMatches( pm );
369 return mlst;