Use internal SNAPSHOT couplings again
[trakem2.git] / TrakEM2_ / src / main / java / ini / trakem2 / display / NonLinearTransformMode.java
blob889c23bf113018b9a4751dc238bb0df78440e4db
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 protected void doPainterUpdate( final Rectangle r, final double m )
42 try
44 final CoordinateTransform mlst = createCT();
45 final SimilarityModel2D toWorld = new SimilarityModel2D();
46 toWorld.set( 1.0f / ( float )m, 0, r.x - ScreenPatchRange.pad / ( float )m, r.y - ScreenPatchRange.pad / ( float )m );
48 final mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform > ctl = new mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform >();
49 ctl.add( toWorld );
50 ctl.add( mlst );
51 ctl.add( toWorld.createInverse() );
53 final CoordinateTransformMesh ctm = new CoordinateTransformMesh( ctl, 32, r.width * ( float )m + 2 * ScreenPatchRange.pad, r.height * ( float )m + 2 * ScreenPatchRange.pad );
54 final TransformMeshMappingWithMasks< CoordinateTransformMesh > mapping = new TransformMeshMappingWithMasks< CoordinateTransformMesh >( ctm );
56 final HashMap<Paintable, GroupingMode.ScreenPatchRange<?>> screenPatchRanges = this.screenPatchRanges; // keep a pointer to the current list
57 for ( final GroupingMode.ScreenPatchRange spr : screenPatchRanges.values())
59 if (screenPatchRanges != this.screenPatchRanges) {
60 // List has been updated; restart painting
61 // TODO should it call itself: doPainterUpdate( r, m );
62 break;
64 spr.update( mapping );
67 catch ( NotEnoughDataPointsException e ) {}
68 catch ( NoninvertibleModelException e ) {}
69 catch ( IllDefinedDataPointsException e ) {}
70 catch ( Exception e ) { e.printStackTrace(); }
73 private class NonLinearTransformSource extends GroupingMode.GroupedGraphicsSource {
74 public void paintOnTop(final Graphics2D g, final Display display, final Rectangle srcRect, final double magnification) {
76 final Stroke original_stroke = g.getStroke();
77 final AffineTransform original = g.getTransform();
78 g.setTransform( new AffineTransform() );
79 g.setStroke( new BasicStroke( 1.0f ) );
80 for ( final Point p : points )
82 final float[] w = p.getW();
83 Utils.drawPoint( g, Math.round( ( float )magnification * ( w[ 0 ] - srcRect.x ) ), Math.round( ( float )magnification * ( w[ 1 ] - srcRect.y ) ) );
86 g.setTransform( original );
87 g.setStroke( original_stroke );
91 protected ScreenPatchRange createScreenPathRange(final PatchRange range, final Rectangle srcRect, final double magnification) {
92 return new NonLinearTransformMode.ScreenPatchRange(range, srcRect, magnification);
95 private static class ScreenPatchRange extends GroupingMode.ScreenPatchRange<TransformMeshMappingWithMasks<?>> {
96 ScreenPatchRange( final PatchRange range, final Rectangle srcRect, final double magnification ) {
97 super(range, srcRect, magnification);
99 public void update( final TransformMeshMappingWithMasks< ? > mapping )
101 ipTransformed.reset();
102 if ( mask == null )
104 mapping.map( ip, ipTransformed );
106 else
108 maskTransformed.reset();
109 final ImageProcessorWithMasks ipm = new ImageProcessorWithMasks( ip, mask, null );
110 final ImageProcessorWithMasks tpm = new ImageProcessorWithMasks( ipTransformed, maskTransformed, null );
111 mapping.map( ipm, tpm );
113 if (null != transformedImage) transformedImage.flush();
114 transformedImage = super.makeImage( ipTransformed, maskTransformed );
118 protected GroupingMode.GroupedGraphicsSource createGroupedGraphicSource() {
119 return new NonLinearTransformSource();
122 public NonLinearTransformMode(final Display display, final List<Displayable> selected) {
123 super(display, selected);
124 ProjectToolbar.setTool(ProjectToolbar.SELECT);
125 super.initThreads();
128 public NonLinearTransformMode(final Display display) {
129 this(display, display.getSelection().getSelected());
132 private Collection< Point > points = new ArrayList< Point >();
134 private Point p_clicked = null;
136 @Override
137 public void mousePressed( MouseEvent me, int x_p, int y_p, double magnification )
139 /* find if clicked on a point */
140 p_clicked = null;
141 float min = Float.MAX_VALUE;
142 final Point mouse = new Point( new float[]{ x_p, y_p } );
143 final float a = ( float )( 64 / magnification / magnification);
144 for ( final Point p : points )
146 final float sd = Point.squareDistance( p, mouse );
147 if ( sd < min && sd < a )
149 p_clicked = p;
150 min = sd;
154 if ( me.isShiftDown() )
156 if ( null == p_clicked )
158 /* add one */
161 if ( points.size() > 0 )
164 * Create a pseudo-invertible (TransformMesh) for the screen.
166 final CoordinateTransform mlst = createCT();
167 final SimilarityModel2D toWorld = new SimilarityModel2D();
168 toWorld.set( 1.0f / ( float )magnification, 0, srcRect.x, srcRect.y );
169 final SimilarityModel2D toScreen = toWorld.createInverse();
171 final mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform > ctl = new mpicbg.models.CoordinateTransformList< mpicbg.models.CoordinateTransform >();
172 ctl.add( toWorld );
173 ctl.add( mlst );
174 ctl.add( toScreen );
176 final CoordinateTransformMesh ctm = new CoordinateTransformMesh(
177 ctl,
179 ( int )Math.ceil( srcRect.width * magnification ),
180 ( int )Math.ceil( srcRect.height * magnification ) );
182 final float[] l = mouse.getL();
183 toScreen.applyInPlace( l );
184 ctm.applyInverseInPlace( l );
185 toWorld.applyInPlace( l );
187 points.add( mouse );
188 p_clicked = mouse;
190 catch ( Exception e )
192 Utils.log( "Could not add point" );
193 e.printStackTrace();
196 else if ( Utils.isControlDown( me ) )
198 // remove it
199 points.remove(p_clicked);
200 p_clicked = null;
205 @Override
206 public void mouseDragged( MouseEvent me, int x_p, int y_p, int x_d, int y_d, int x_d_old, int y_d_old )
208 if ( null != p_clicked )
210 final float[] w = p_clicked.getW();
211 w[ 0 ] += x_d - x_d_old;
212 w[ 1 ] += y_d - y_d_old;
213 painter.update();
217 @Override
218 public void mouseReleased( MouseEvent me, int x_p, int y_p, int x_d, int y_d, int x_r, int y_r )
220 // bring to screen coordinates
221 mouseDragged( me, x_p, y_p, x_r, y_r, x_d, y_d );
222 p_clicked = null; // so isDragging can return the right state
225 public boolean isDragging()
227 return null != p_clicked;
230 private final void setUndoState() {
231 layer.getParent().addEditStep(new Displayable.DoEdits(new HashSet<Displayable>(originalPatches)).init(new String[]{"data", "at", "width", "height"}));
234 final private Future< Boolean > applyToPatch( final Patch patch ) throws Exception
236 final Rectangle pbox = patch.getCoordinateTransformBoundingBox();
237 final AffineTransform pat = new AffineTransform();
238 pat.translate( -pbox.x, -pbox.y );
239 pat.preConcatenate( patch.getAffineTransform() );
241 final AffineModel2D toWorld = new AffineModel2D();
242 toWorld.set( pat );
244 final CoordinateTransform mlst = createCT();
246 final CoordinateTransformList< CoordinateTransform > ctl = new CoordinateTransformList< CoordinateTransform >();
247 ctl.add( toWorld );
248 ctl.add( mlst );
249 ctl.add( toWorld.createInverse() );
251 patch.appendCoordinateTransform( ctl );
252 return patch.updateMipMaps();
255 public boolean apply()
257 return apply( null );
260 public boolean apply( final Set< Layer > sublist )
263 /* Set undo step to reflect initial state before any transformations */
264 setUndoState();
266 Bureaucrat.createAndStart( new Worker.Task( "Applying transformations" )
268 public void exec()
270 final ArrayList< Future< Boolean > > futures = new ArrayList< Future< Boolean > >();
272 synchronized ( updater )
274 /* apply to selected patches */
275 for ( final Paintable p : screenPatchRanges.keySet() )
277 if ( p instanceof Patch )
281 futures.add( applyToPatch( ( Patch )p ) );
283 catch ( Exception e )
285 e.printStackTrace();
289 /* apply to other layers if there are any */
290 if ( !( sublist == null || sublist.isEmpty() ) )
292 for ( final Layer layer : sublist )
294 for ( final Displayable p : layer.getDisplayables( Patch.class ) )
298 futures.add( applyToPatch( ( Patch )p ) );
300 catch ( Exception e )
302 e.printStackTrace();
309 /* Flush images */
310 for ( GroupingMode.ScreenPatchRange<?> spr : new HashSet< GroupingMode.ScreenPatchRange<?> >( screenPatchRanges.values() ) )
312 spr.flush();
315 /* Wait until all mipmaps are regenerated */
316 for ( Future<?> fu : futures )
319 fu.get();
321 catch ( Exception ie )
324 // Set undo step to reflect final state after applying transformations
325 setUndoState();
328 }, layer.getProject() );
330 super.quitThreads();
332 return true;
335 private CoordinateTransform createCT() throws Exception
337 final Collection< PointMatch > pm = new ArrayList<PointMatch>();
338 for ( Point p : points )
340 pm.add( new PointMatch( new Point( p.getL() ), new Point( p.getW() ) ) );
343 * TODO replace this with the desired parameters of the transformation
345 final MovingLeastSquaresTransform2 mlst = new MovingLeastSquaresTransform2();
346 mlst.setAlpha( 1.0f );
347 Class< ? extends AbstractAffineModel2D< ? > > c = AffineModel2D.class;
348 switch (points.size()) {
349 case 1:
350 c = TranslationModel2D.class;
351 break;
352 case 2:
353 c = SimilarityModel2D.class;
354 break;
355 default:
356 break;
358 mlst.setModel( c );
359 mlst.setMatches( pm );
361 return mlst;