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
{
41 protected void doPainterUpdate( final Rectangle r
, final double m
)
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
>();
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 );
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
{
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
);
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
);
103 public void update( final TransformMeshMappingWithMasks
< ?
> mapping
)
105 ipTransformed
.reset();
108 mapping
.map( ip
, ipTransformed
);
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
);
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
);
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;
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 */
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
)
159 if ( me
.isShiftDown() )
161 if ( null == p_clicked
)
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
>();
181 final CoordinateTransformMesh ctm
= new CoordinateTransformMesh(
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
);
195 catch ( final Exception e
)
197 Utils
.log( "Could not add point" );
201 else if ( Utils
.isControlDown( me
) )
204 points
.remove(p_clicked
);
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
;
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
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();
250 final CoordinateTransform mlst
= createCT();
252 final CoordinateTransformList
< CoordinateTransform
> ctl
= new CoordinateTransformList
< CoordinateTransform
>();
255 ctl
.add( toWorld
.createInverse() );
257 patch
.appendCoordinateTransform( ctl
);
258 return patch
.updateMipMaps();
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 */
273 Bureaucrat
.createAndStart( new Worker
.Task( "Applying transformations" )
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
)
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
)
318 for ( final GroupingMode
.ScreenPatchRange
<?
> spr
: new HashSet
< GroupingMode
.ScreenPatchRange
<?
> >( screenPatchRanges
.values() ) )
323 /* Wait until all mipmaps are regenerated */
324 for ( final Future
<?
> fu
: futures
)
329 catch ( final Exception ie
)
332 // Set undo step to reflect final state after applying transformations
336 }, layer
.getProject() );
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()) {
358 c
= TranslationModel2D
.class;
361 c
= SimilarityModel2D
.class;
367 mlst
.setMatches( pm
);