package com.indago.iddea.view.display;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.concurrent.CopyOnWriteArrayList;
import com.indago.iddea.view.overlay.TransformOverlay;
import net.imglib2.realtransform.AffineTransform2D;
import net.imglib2.ui.InteractiveDisplayCanvas;
import net.imglib2.ui.OverlayRenderer;
import net.imglib2.ui.TransformEventHandler;
import net.imglib2.ui.TransformEventHandlerFactory;
import net.imglib2.ui.TransformListener;
/**
* JHotDrawInteractiveDisplay2D gives 2D display view with the buffered image.
* This class uses MultiResolutionRenderer for realtime scaling view.
*
* @version 0.1beta
* @since 8/12/13 5:09 PM
* @author TobiasPietzsch <tobias.pietzsch@gmail.com>
* @author HongKee Moon
*/
public class JHotDrawInteractiveDisplay2D<T> extends InteractiveDrawingView
implements InteractiveDisplayCanvas<T>
{
/**
* Mouse/Keyboard handler that manipulates the view transformation.
*/
protected TransformEventHandler< T > handler;
/**
* Listeners that we have to notify about view transformation changes.
*/
final protected CopyOnWriteArrayList< TransformListener< T > > transformListeners;
/**
* The {@link OverlayRenderer} that draws on top of the current {@link #bufferedImage}.
*/
final protected CopyOnWriteArrayList<OverlayRenderer> overlayRenderers;
AffineTransform originTransform;
/**
* The {@link BufferedImage} that is actually drawn on the canvas. Depending
* on {@link #discardAlpha} this is either the {@link BufferedImage}
* obtained from {@link #screenImage}, or {@link #screenImage}s buffer
* re-wrapped using a RGB color model.
*/
protected BufferedImage bufferedImage;
public JHotDrawInteractiveDisplay2D( final int width, final int height, final T sourceTransform, final TransformEventHandlerFactory< T > factory)
{
super();
if(sourceTransform != null)
{
final AffineTransform2D trsf = (AffineTransform2D)sourceTransform;
final double[] tr = trsf.getRowPackedCopy();
this.originTransform = new AffineTransform(tr[0], tr[3], tr[1], tr[4], tr[2], tr[5]);
}
setPreferredSize( new Dimension( width, height ) );
setFocusable( true );
this.bufferedImage = null;
this.overlayRenderers = new CopyOnWriteArrayList< OverlayRenderer >();
this.transformListeners = new CopyOnWriteArrayList< TransformListener< T > >();
addTransformListener(new TransformListener<T>(){
@Override
public void transformChanged(final T transform) {
if(AffineTransform2D.class.isInstance(transform))
{
// Convert AffineTransform2D to java.awt.geo.AffineTransform object
final AffineTransform2D trsf = (AffineTransform2D)transform;
// array design is different
final double[] tr = trsf.getRowPackedCopy();
preTransform = new AffineTransform(tr[0], tr[3], tr[1], tr[4], tr[2], tr[5]);
invalidateHandles();
}
}
});
addComponentListener( new ComponentAdapter()
{
@Override
public void componentResized( final ComponentEvent e )
{
final int w = getWidth();
final int h = getHeight();
handler.setCanvasSize( w, h, true );
for ( final OverlayRenderer or : overlayRenderers )
or.setCanvasSize( w, h );
//setBounds(0,0,w,h);
// enableEvents( AWTEvent.MOUSE_MOTION_EVENT_MASK );
}
} );
addMouseListener( new MouseAdapter()
{
@Override
public void mousePressed( final MouseEvent e )
{
requestFocusInWindow();
}
} );
handler = factory.create( this );
handler.setCanvasSize( width, height, false );
//transformChanged(sourceTransform);
//activateHandler will call addHandler(handler) in case of changing to DisplayTool
//addHandler( handler );
}
protected Dimension imageDim;
public void setImageDim(final Dimension dim)
{
imageDim = dim;
}
public void resetTransform()
{
final AffineTransform2D reset = new AffineTransform2D();
handler.setTransform((T) reset);
handler.setCanvasSize( imageDim.width, imageDim.height, false );
final int w = Math.max( getWidth(), imageDim.width );
final int h = Math.max( getHeight(), imageDim.height );
handler.setCanvasSize( w, h, true );
for ( final OverlayRenderer or : overlayRenderers )
or.setCanvasSize( w, h );
}
public void updateTransform(final T trf)
{
if(trf != null)
{
final AffineTransform2D trsf = (AffineTransform2D)trf;
final double[] tr = trsf.getRowPackedCopy();
this.originTransform = new AffineTransform(tr[0], tr[3], tr[1], tr[4], tr[2], tr[5]);
handler.setTransform((T) trsf);
handler.setCanvasSize( imageDim.width, imageDim.height, false );
final int w = Math.max( getWidth(), imageDim.width );
final int h = Math.max( getHeight(), imageDim.height );
handler.setCanvasSize( w, h, true );
for ( final OverlayRenderer or : overlayRenderers )
or.setCanvasSize( w, h );
}
}
public void activateHandler()
{
addHandler( handler );
}
public void deactivateHandler()
{
removeHandler( handler );
}
@Override
public void drawImage(final Graphics2D g) {
final BufferedImage bi;
synchronized ( this )
{
bi = bufferedImage;
}
if ( bi != null )
{
g.drawImage( bi, 0, 0, getWidth(), getHeight(), null );
}
for ( final OverlayRenderer or : overlayRenderers ) {
if(or instanceof TransformOverlay)
{
((TransformOverlay) or).setupTransform(preTransform);
}
or.drawOverlays(g);
}
}
public Point2D.Double viewToOrigin(final Point2D.Double p) {
Point2D point = null;
if(originTransform != null)
{
try {
point = originTransform.inverseTransform(p, null);
} catch (final NoninvertibleTransformException e) {
e.printStackTrace();
}
}
return (Point2D.Double) point;
}
public Point2D.Double originToView(final double x, final double y) {
final Point2D.Double p = new Point2D.Double(x, y);
Point2D point = null;
if(originTransform != null)
{
point = originTransform.transform(p, null);
}
return (Point2D.Double) point;
}
public Rectangle2D.Double viewToOrigin(final Rectangle2D.Double r) {
final double[] drawing = {r.x, r.y, r.x + r.width, r.y, r.x + r.width, r.y + r.height, r.x, r.y + r.height};
final double[] view = new double[8];
if(originTransform != null)
{
try {
originTransform.inverseTransform(drawing, 0, view, 0, 4);
} catch (final NoninvertibleTransformException e) {
e.printStackTrace();
}
}
return new Rectangle2D.Double(view[0], view[1], view[2] - view[0], view[5] - view[1]);
}
public Rectangle2D.Double originToView(final Rectangle2D.Double r)
{
final double[] drawing = {r.x, r.y, r.x + r.width, r.y, r.x + r.width, r.y + r.height, r.x, r.y + r.height};
final double[] view = new double[8];
preTransform.transform(drawing, 0, view, 0, 4);
return new Rectangle2D.Double(drawing[0], drawing[1], drawing[2] - drawing[0], drawing[5] - drawing[1]);
}
/**
* Set the {@link BufferedImage} that is to be drawn on the canvas.
*
* @param bufferedImage image to draw (may be null).
*/
public synchronized void setBufferedImage( final BufferedImage bufferedImage )
{
this.bufferedImage = bufferedImage;
}
/**
* Add an {@link OverlayRenderer} that draws on top of the current {@link #bufferedImage}.
*
* @param renderer overlay renderer to add.
*/
@Override
public void addOverlayRenderer( final OverlayRenderer renderer )
{
overlayRenderers.add( renderer );
renderer.setCanvasSize( getWidth(), getHeight() );
}
/**
* Remove an {@link OverlayRenderer}.
*
* @param renderer overlay renderer to remove.
*/
@Override
public void removeOverlayRenderer( final OverlayRenderer renderer )
{
overlayRenderers.remove( renderer );
}
/**
* Add a {@link TransformListener} to notify about view transformation changes.
*
* @param listener the transform listener to add.
*/
@Override
public void addTransformListener( final TransformListener< T > listener )
{
transformListeners.add( listener );
}
/**
* Remove a {@link TransformListener}.
*
* @param listener the transform listener to remove.
*/
@Override
public void removeTransformListener( final TransformListener< T > listener )
{
transformListeners.remove( listener );
}
/**
* Add new event handler. Depending on the interfaces implemented by
* <code>handler</code> calls {@link Component#addKeyListener(KeyListener)},
* {@link Component#addMouseListener(MouseListener)},
* {@link Component#addMouseMotionListener(MouseMotionListener)},
* {@link Component#addMouseWheelListener(MouseWheelListener)}.
*/
@Override
public void addHandler( final Object handler )
{
if ( KeyListener.class.isInstance( handler ) )
addKeyListener( ( KeyListener ) handler );
if ( MouseMotionListener.class.isInstance( handler ) )
addMouseMotionListener( ( MouseMotionListener ) handler );
if ( MouseListener.class.isInstance( handler ) )
addMouseListener( ( MouseListener ) handler );
if ( MouseWheelListener.class.isInstance( handler ) )
addMouseWheelListener( ( MouseWheelListener ) handler );
}
/**
* Remove an event handler.
* Add new event handler. Depending on the interfaces implemented by
* <code>handler</code> calls {@link Component#removeKeyListener(KeyListener)},
* {@link Component#removeMouseListener(MouseListener)},
* {@link Component#removeMouseMotionListener(MouseMotionListener)},
* {@link Component#removeMouseWheelListener(MouseWheelListener)}.
*/
@Override
public void removeHandler( final Object handler )
{
if ( KeyListener.class.isInstance( handler ) )
removeKeyListener( ( KeyListener ) handler );
if ( MouseMotionListener.class.isInstance( handler ) )
removeMouseMotionListener( ( MouseMotionListener ) handler );
if ( MouseListener.class.isInstance( handler ) )
removeMouseListener( ( MouseListener ) handler );
if ( MouseWheelListener.class.isInstance( handler ) )
removeMouseWheelListener( ( MouseWheelListener ) handler );
}
/**
* Get the {@link TransformEventHandler} that handles mouse and key events
* to update our view transform.
*
* @return handles mouse and key events to update the view transform.
*/
@Override
public TransformEventHandler< T > getTransformEventHandler()
{
return handler;
}
/**
* Set the {@link TransformEventHandler} that handles mouse and key events
* to update our view transform.
*
* @param transformEventHandler mouse and key events to update the view transform
*/
@Override
public synchronized void setTransformEventHandler( final TransformEventHandler< T > transformEventHandler )
{
removeHandler( handler );
handler = transformEventHandler;
handler.setCanvasSize( getWidth(), getHeight(), false );
addHandler( handler );
}
/**
* This is called by our {@link #getTransformEventHandler() transform event
* handler} when the transform is changed. In turn, we notify all our
* {@link TransformListener TransformListeners} that the view transform has
* changed.
*/
@Override
public void transformChanged( final T transform )
{
for ( final TransformListener< T > l : transformListeners )
l.transformChanged( transform );
}
@Override
public void setPreferredSize( final Dimension dim )
{
super.setPreferredSize(dim);
setImageDim(dim);
}
}