package ini.trakem2.display.inspect; import ini.trakem2.display.Display; import ini.trakem2.display.Displayable; import ini.trakem2.display.Layer; import ini.trakem2.display.LayerSet; import ini.trakem2.display.Mode; import ini.trakem2.display.Patch; import ini.trakem2.display.graphics.DefaultGraphicsSource; import ini.trakem2.display.graphics.GraphicsSource; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Rectangle; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.util.ArrayList; import java.util.Collection; import mpicbg.models.AffineModel2D; import mpicbg.models.PointMatch; import mpicbg.trakem2.transform.TransformMesh; /** * A view-only mode that shows, using {@link LayerSet#getOverlay()}, the * triangle of the transform mesh in the {@link Patch} under the cursor. * * @author Stephan Saalfeld and Albert Cardona */ public class InspectPatchTrianglesMode implements Mode { protected final Display display; final private DefaultGraphicsSource gs = new DefaultGraphicsSource(); public InspectPatchTrianglesMode(final Display display) { this.display = display; } @Override public GraphicsSource getGraphicsSource() { return gs; } @Override public boolean canChangeLayer() { return false; } @Override public boolean canZoom() { return true; } @Override public boolean canPan() { return true; } @Override public boolean isDragging() { return false; } @Override public void undoOneStep() {} @Override public void redoOneStep() {} // Initialize with a Shape that can never be painted protected final ShapeProxy proxy = new ShapeProxy(new Rectangle(-1, -1, 0, 0)); protected final Inspector inspector = new Inspector(); protected class Inspector extends Thread { private boolean quit = false; private double wx, wy; private Layer layer; Inspector() { setPriority(Thread.NORM_PRIORITY); start(); } private void quit() { this.quit = true; } private void viewAt(final double wX, final double wY, final Layer l) { synchronized (this) { this.wx = wX; this.wy = wY; this.layer = l; notify(); } } @Override public void run() { double wX = 0, wY = 0; Layer l = null; while (!isInterrupted()) { try { synchronized (this) { if (this.wx == wX && this.wy == wY && this.layer == l) { // Nothing changed wait(); } } } catch (final InterruptedException ie) { ie.printStackTrace(); return; } if (quit) return; // Acquire local copy synchronized (this) { wX = this.wx; wY = this.wy; l = this.layer; } // Find a Patch under wx, wy final Collection<Displayable> ps = l.find(Patch.class, wX, wY, true, false); if (ps.isEmpty()) { continue; } final Patch patch = (Patch) ps.iterator().next(); // Find the triangle, if any if (null != patch.getCoordinateTransform()) { // TODO update this to hasCoordinateTransform final double[] f = new double[]{wx, wy}; final AffineTransform ai = patch.getAffineTransformCopy(); final AffineTransform aiInverse = new AffineTransform( ai ); try { aiInverse.invert(); } catch ( final NoninvertibleTransformException x ) {} aiInverse.transform( f, 0, f, 0, 1 ); final TransformMesh mesh = new TransformMesh( patch.getCoordinateTransform(), patch.getMeshResolution(), patch.getOWidth(), patch.getOHeight() ); final AffineModel2D triangle = mesh.closestTargetAffine( f ); final ArrayList< PointMatch > pm = mesh.getAV().get( triangle ); final GeneralPath path = new GeneralPath(); final double[] p1 = pm.get( 0 ).getP2().getW(); final double[] q = new double[ 2 ]; ai.transform( p1, 0, q, 0, 1 ); path.moveTo( q[ 0 ], q[ 1 ] ); for ( int i = 1; i < pm.size(); ++i ) { final double[] p = pm.get( i ).getP2().getW(); ai.transform( p, 0, q, 0, 1 ); path.lineTo( q[ 0 ], q[ 1 ] ); } path.closePath(); proxy.set( path ); display.getCanvas().repaint(proxy.getBounds(), 0, false); } } } } @Override public void mousePressed(final MouseEvent me, final int x_p, final int y_p, final double magnification) { display.getLayerSet().getOverlay().add( proxy, Color.YELLOW, new BasicStroke( 1 ) ); } @Override 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) { synchronized (inspector) { inspector.viewAt(x_d, y_d, display.getLayer()); inspector.notifyAll(); } } @Override 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) { display.getLayerSet().getOverlay().remove(proxy); } @Override public void srcRectUpdated(final Rectangle srcRect, final double magnification) {} @Override public void magnificationUpdated(final Rectangle srcRect, final double magnification) {} @Override public boolean apply() { return cancel(); } @Override public boolean cancel() { display.getLayerSet().getOverlay().remove(proxy); synchronized (inspector) { inspector.quit(); inspector.notifyAll(); } return true; } @Override public Rectangle getRepaintBounds() { return display.getCanvas().getSrcRect(); } }