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
;
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
)
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
>();
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 );
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();
104 mapping
.map( ip
, ipTransformed
);
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
);
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;
137 public void mousePressed( MouseEvent me
, int x_p
, int y_p
, double magnification
)
139 /* find if clicked on a point */
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
)
154 if ( me
.isShiftDown() )
156 if ( null == p_clicked
)
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
>();
176 final CoordinateTransformMesh ctm
= new CoordinateTransformMesh(
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
);
190 catch ( Exception e
)
192 Utils
.log( "Could not add point" );
196 else if ( Utils
.isControlDown( me
) )
199 points
.remove(p_clicked
);
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
;
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();
244 final CoordinateTransform mlst
= createCT();
246 final CoordinateTransformList
< CoordinateTransform
> ctl
= new CoordinateTransformList
< CoordinateTransform
>();
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 */
266 Bureaucrat
.createAndStart( new Worker
.Task( "Applying transformations" )
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
)
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
)
310 for ( GroupingMode
.ScreenPatchRange
<?
> spr
: new HashSet
< GroupingMode
.ScreenPatchRange
<?
> >( screenPatchRanges
.values() ) )
315 /* Wait until all mipmaps are regenerated */
316 for ( Future
<?
> fu
: futures
)
321 catch ( Exception ie
)
324 // Set undo step to reflect final state after applying transformations
328 }, layer
.getProject() );
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()) {
350 c
= TranslationModel2D
.class;
353 c
= SimilarityModel2D
.class;
359 mlst
.setMatches( pm
);