diff --git a/.gitignore b/.gitignore index 9eee571d0dd8122e8838586c3eea8145168466f4..40bbaf46a7d36d07b6073c8a6e901299bfbfbcc9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ target/ *.iml *.class .project -.classpath \ No newline at end of file +.classpath +**/.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml index ab4d3722f651d4a832b8a8df3357b9a4345c58ae..c0643018cb9882f40de4ad698e4bebf26bf2aa13 100644 --- a/pom.xml +++ b/pom.xml @@ -7,19 +7,19 @@ <!-- Inherited Icy Parent POM --> <parent> <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>parent-pom-plugin</artifactId> - <version>1.0.8</version> + <artifactId>pom-icy</artifactId> + <version>2.2.0</version> </parent> <!-- Project Information --> <artifactId>angle-helper</artifactId> - <version>1.1.0</version> + <version>1.1.1</version> <packaging>jar</packaging> <name>Angle Helper</name> <description>This repo serves as a template for you to implement new Icy plugins.</description> - <url>http://icy.bioimageanalysis.org/plugin/angle-helper/</url> + <url>https://icy.bioimageanalysis.org/plugin/angle-helper/</url> <inceptionYear>2020</inceptionYear> <organization> @@ -53,34 +53,11 @@ </developer> </developers> - <!-- Project properties --> - <properties> - - </properties> - - <!-- Project build configuration --> - <build> - - </build> - <!-- List of project's dependencies --> <dependencies> - <!-- The core of Icy --> - <dependency> - <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>icy-kernel</artifactId> - </dependency> - - <dependency> - <groupId>org.bioimageanalysis.icy</groupId> - <artifactId>xuggler</artifactId> - <version>5.5.2</version> - </dependency> - <dependency> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>ruler-helper</artifactId> - <version>1.2.1</version> </dependency> </dependencies> diff --git a/src/main/java/plugins/fab/anglehelper/AngleHelper.java b/src/main/java/plugins/fab/anglehelper/AngleHelper.java index 58f19050f4d66183e261b9b8aff033b5a71653db..cbd3287ee383396f714981571875ecd4716e3379 100644 --- a/src/main/java/plugins/fab/anglehelper/AngleHelper.java +++ b/src/main/java/plugins/fab/anglehelper/AngleHelper.java @@ -1,28 +1,22 @@ package plugins.fab.anglehelper; import icy.gui.dialog.MessageDialog; -import icy.plugin.abstract_.Plugin; -import icy.plugin.interface_.PluginImageAnalysis; +import icy.plugin.abstract_.PluginActionable; import icy.sequence.Sequence; /** * @author Fabrice de Chaumont */ -public class AngleHelper extends Plugin implements PluginImageAnalysis { - +public class AngleHelper extends PluginActionable { @Override - public void compute() { + public void run() { + Sequence sequence = getActiveSequence(); - Sequence sequence = getFocusedSequence(); - - if ( sequence!=null ) - { - new AnglePainter( getFocusedSequence() ); - }else - { + if (sequence != null) { + new AnglePainter(sequence); + } + else { MessageDialog.showDialog("Please open an image first.", MessageDialog.INFORMATION_MESSAGE ); } - } - } diff --git a/src/main/java/plugins/fab/anglehelper/AnglePainter.java b/src/main/java/plugins/fab/anglehelper/AnglePainter.java index b6714ed2ae3a5979369700e5d96d76fddf8aac36..3cb9eb6288afa1dca31ab47efa4efdcfa017eea3 100644 --- a/src/main/java/plugins/fab/anglehelper/AnglePainter.java +++ b/src/main/java/plugins/fab/anglehelper/AnglePainter.java @@ -2,14 +2,9 @@ package plugins.fab.anglehelper; import icy.canvas.IcyCanvas; import icy.canvas.IcyCanvas2D; -import icy.gui.util.GuiUtil; import icy.main.Icy; import icy.math.Scaler; -import icy.painter.Anchor2D; -import icy.painter.Anchor2D.Anchor2DListener; -import icy.painter.Painter; -import icy.painter.PainterEvent; -import icy.roi.ROI2D; +import icy.painter.*; import icy.sequence.Sequence; import java.awt.AlphaComposite; @@ -22,315 +17,295 @@ import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.Line2D; -import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; +import icy.type.point.Point5D; +import icy.util.GraphicsUtil; import plugins.fab.Ruler.Anchor2DTarget; import plugins.fab.Ruler.ShapeDefinition; /** * @author Fabrice de Chaumont */ -public class AnglePainter implements Painter, Anchor2DListener { - - Anchor2DTarget a1 = new Anchor2DTarget( 0 , 0 ); - Anchor2DTarget a2 = new Anchor2DTarget( 0 , 0 ); - Anchor2DTarget a3 = new Anchor2DTarget( 0 , 0 ); - - ArrayList<ShapeDefinition> shapeDefinitionList = new ArrayList<ShapeDefinition>(); - ArrayList<AffineTransform> affineTransformList = new ArrayList<AffineTransform>(); - - public AnglePainter(Sequence sequence) { - - if ( sequence == null ) return; - - a1.setPosition( sequence.getWidth() / 2 , sequence.getHeight() / 2 ); - a2.setPosition( 3 * sequence.getWidth() / 4 , sequence.getHeight() / 2 ); - a3.setPosition( 3 * sequence.getWidth() / 4 , sequence.getHeight() / 3 ); - - sequence.addPainter( this ); - a1.addListener( this ); - a2.addListener( this ); - a3.addListener( this ); - - } - - // Listener of Anchor - - @Override - public void painterChanged(PainterEvent event) { - - for ( Sequence sequence : Icy.getMainInterface().getSequencesContaining( this ) ) - { - sequence.painterChanged( this ); - } - - } - - @Override - public void positionChanged(Anchor2D source) { - - - } - - void pushTransform( Graphics2D g ) - { - affineTransformList.add( g.getTransform() ); - } - - void popTransform( Graphics2D g ) - { - g.setTransform( affineTransformList.get( affineTransformList.size() -1 ) ); - affineTransformList.remove( affineTransformList.size() -1 ); - } - - public double CleanAngle( double angle ) - { - double a = Math.abs( angle ); - a = Math.round(a*10)/10.; - return a; - } - - // Painter Section: - - @Override - public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) { - - if ( ! ( canvas instanceof IcyCanvas2D ) ) return; - - g= (Graphics2D) g.create(); - - BasicStroke[] stroke = new BasicStroke[4]; - - stroke[0] = new BasicStroke((float) ROI2D.canvasToImageLogDeltaX(canvas, 2 ) ); - stroke[1] = new BasicStroke((float) ROI2D.canvasToImageLogDeltaX(canvas, 3 ) ); - stroke[2] = new BasicStroke((float) ROI2D.canvasToImageLogDeltaX(canvas, 4 ) ); - stroke[3] = new BasicStroke((float) ROI2D.canvasToImageLogDeltaX(canvas, 5 ) ); - - // transform and display ticks - - shapeDefinitionList.clear(); - - pushTransform( g ); - - double vx12 = ( a2.getX() - a1.getX() ) ; - double vy12 = ( a2.getY() - a1.getY() ) ; - double angle12 = Math.atan2( vy12 , vx12 ) * ( 180d / Math.PI ) ; - - double vx13 = ( a3.getX() - a1.getX() ) ; - double vy13 = ( a3.getY() - a1.getY() ) ; - double angle13 = Math.atan2( vy13 , vx13 ) * ( 180d / Math.PI ); - - double angle1 = angle12-angle13; - - if ( angle1 < 0 ) angle1+=360; - - double angle2 = 360-angle1; - - double dis1 = a1.getPosition().distance( a2.getPosition() ); - double dis2 = a1.getPosition().distance( a3.getPosition() ); - - double minDistance = Math.min( dis1 , dis2 ); - - double distanceAngle1 = minDistance ; - double distanceAngle2 = minDistance / 2; - - double distanceTextAngle1 = minDistance; - double distanceTextAngle2 = minDistance/2; - - // Lines - - shapeDefinitionList.add( new ShapeDefinition( 2 , new Line2D.Double( a1.getPosition() , a2.getPosition() ) ) ); - shapeDefinitionList.add( new ShapeDefinition( 2 , new Line2D.Double( a1.getPosition() , a3.getPosition() ) ) ); - - // angle 1 - { - double arcSize = distanceAngle1; - Arc2D arc = new Arc2D.Double( - a1.getX() - arcSize/2d, - a1.getY() - arcSize/2d, - arcSize, - arcSize, - -angle12 , - angle1,//angle13, - Arc2D.OPEN - ); - - shapeDefinitionList.add( new ShapeDefinition( 2 , arc ) ); - } - - // angle 2 - { - double arcSize = distanceAngle2; - Arc2D arc = new Arc2D.Double( - a1.getX() - arcSize/2d, - a1.getY() - arcSize/2d, - arcSize, - arcSize, - -angle13 , - angle2,//angle13, - Arc2D.OPEN - ); - - shapeDefinitionList.add( new ShapeDefinition( 2 , arc ) ); - } - - // draw lines ( black background, then white ) - - float oldAlpha= -1; - g.setColor( Color.black ); - for ( ShapeDefinition ld : shapeDefinitionList ) - { - if (oldAlpha!=ld.alpha) - { - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ld.alpha ) ); - oldAlpha = ld.alpha; - } - g.setStroke( stroke[ld.stroke] ); - g.draw( ld.shape ); - } - - g.setColor( Color.white ); - for ( ShapeDefinition ld : shapeDefinitionList ) - { - if (oldAlpha!=ld.alpha) - { - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ld.alpha ) ); - oldAlpha = ld.alpha; - } - g.setStroke( stroke[ld.stroke-1] ); - g.draw( ld.shape ); - } - - // Display angle text - - { - int fontSize = (int)convertScale(canvas, 15 ); - Font font = new Font( "Arial" , Font.PLAIN , fontSize ); - - pushTransform( g ); - String pixelString = " "+ (int)( Math.round( angle1 ) ) + "° "; - Rectangle2D pixelBounds = GuiUtil.getStringBounds( g , font, pixelString ); - g.translate( a1.getX() , a1.getY() ); - - double angleText = Math.toRadians( angle12 - angle1 / 2d ); - g.translate( Math.cos( angleText ) * distanceTextAngle1 /2 , Math.sin( angleText ) * distanceTextAngle1 /2 ); - g.translate( -pixelBounds.getWidth() / 2 , 0 ); - - g.setFont( font ); - g.setColor( Color.white ); - g.fill( pixelBounds ); - g.setColor( Color.black ); - g.drawString( pixelString , 0, 0 ); - popTransform( g ); - } - - { - int fontSize = (int)convertScale(canvas, 15 ); - Font font = new Font( "Arial" , Font.PLAIN , fontSize ); - - pushTransform( g ); - String pixelString = " "+ (int)(Math.round( angle2 ) ) + "� "; - Rectangle2D pixelBounds = GuiUtil.getStringBounds( g , font, pixelString ); - g.translate( a1.getX() , a1.getY() ); - - double angleText = Math.toRadians( 180 + angle12 - angle1 / 2d ); - g.translate( Math.cos( angleText ) * distanceTextAngle2 / 2d , Math.sin( angleText ) * distanceTextAngle2 / 2d ); - g.translate( -pixelBounds.getWidth() / 2 , 0 ); - - float alpha = 1 ; - double distance = canvas.getScaleX() * distanceTextAngle2 / 2d ; - - Scaler scaler = new Scaler( 0 , 10 , 0, 1 , false ); - alpha = (float)scaler.scale( distance - pixelBounds.getWidth() / 10d ); - - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha ) ); - - g.setFont( font ); - g.setColor( Color.white ); - g.fill( pixelBounds ); - g.setColor( Color.black ); - g.drawString( pixelString , 0, 0 ); - - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1 ) ); - popTransform( g ); - } - - // get back to original transform - - popTransform( g ); - - // display anchors - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1 ) ); - a1.paint( g, sequence, canvas); - a2.paint( g, sequence, canvas); - a3.paint( g, sequence, canvas); - } - - double convertScale( IcyCanvas canvas , double value ) - { - return ROI2D.canvasToImageLogDeltaX(canvas, value ); - } - - @Override - public void mousePressed(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.mousePressed(e, imagePoint, canvas); - a2.mousePressed(e, imagePoint, canvas); - a3.mousePressed(e, imagePoint, canvas); - } - - @Override - public void mouseReleased(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.mouseReleased(e, imagePoint, canvas); - a2.mouseReleased(e, imagePoint, canvas); - a3.mouseReleased(e, imagePoint, canvas); - } - - @Override - public void mouseClick(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.mouseClick(e, imagePoint, canvas); - a2.mouseClick(e, imagePoint, canvas); - a3.mouseClick(e, imagePoint, canvas); - - } - - @Override - public void mouseMove(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.mouseMove(e, imagePoint, canvas); - a2.mouseMove(e, imagePoint, canvas); - a3.mouseMove(e, imagePoint, canvas); - } - - @Override - public void mouseDrag(MouseEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.mouseDrag(e, imagePoint, canvas); - a2.mouseDrag(e, imagePoint, canvas); - a3.mouseDrag(e, imagePoint, canvas); - } - - @Override - public void keyPressed(KeyEvent e, Point2D imagePoint, IcyCanvas canvas) { - - if ( ( e.getKeyCode() == KeyEvent.VK_DELETE && a1.isSelected() ) || ( e.getKeyCode() == KeyEvent.VK_DELETE && a2.isSelected() ) ) - { - for ( Sequence sequence : Icy.getMainInterface().getSequencesContaining( this ) ) - { - sequence.removePainter( this ); - } - - } - - a1.keyPressed(e, imagePoint, canvas); - a2.keyPressed(e, imagePoint, canvas); - a3.keyPressed(e, imagePoint, canvas); - } - - @Override - public void keyReleased(KeyEvent e, Point2D imagePoint, IcyCanvas canvas) { - a1.keyReleased(e, imagePoint, canvas); - a2.keyReleased(e, imagePoint, canvas); - a3.keyReleased(e, imagePoint, canvas); - } - +public class AnglePainter extends Overlay implements OverlayListener { + + Anchor2DTarget a1 = new Anchor2DTarget(0, 0); + Anchor2DTarget a2 = new Anchor2DTarget(0, 0); + Anchor2DTarget a3 = new Anchor2DTarget(0, 0); + + ArrayList<ShapeDefinition> shapeDefinitionList = new ArrayList<>(); + ArrayList<AffineTransform> affineTransformList = new ArrayList<>(); + + public AnglePainter(Sequence sequence) { + super("Angle painter", OverlayPriority.SHAPE_NORMAL); + + if (sequence == null) return; + + a1.setPosition(sequence.getWidth() / 2f, sequence.getHeight() / 2f); + a2.setPosition(3 * sequence.getWidth() / 4f, sequence.getHeight() / 2f); + a3.setPosition(3 * sequence.getWidth() / 4f, sequence.getHeight() / 3f); + + sequence.addOverlay(this); + a1.addOverlayListener(this); + a2.addOverlayListener(this); + a3.addOverlayListener(this); + + } + + // Listener of Anchor + @Override + public void overlayChanged(OverlayEvent overlayEvent) { + for (Sequence sequence : Icy.getMainInterface().getSequencesContaining(this)) { + sequence.overlayChanged(this); + } + } + + void pushTransform(Graphics2D g) { + affineTransformList.add(g.getTransform()); + } + + void popTransform(Graphics2D g) { + g.setTransform(affineTransformList.get(affineTransformList.size() - 1)); + affineTransformList.remove(affineTransformList.size() - 1); + } + + public double CleanAngle(double angle) { + double a = Math.abs(angle); + a = Math.round(a * 10) / 10.; + return a; + } + + // Painter Section: + + @Override + public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) { + + if (!(canvas instanceof IcyCanvas2D)) return; + + g = (Graphics2D) g.create(); + + BasicStroke[] stroke = new BasicStroke[4]; + + stroke[0] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(2)); + stroke[1] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(3)); + stroke[2] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(4)); + stroke[3] = new BasicStroke((float) canvas.canvasToImageLogDeltaX(5)); + + // transform and display ticks + + shapeDefinitionList.clear(); + + pushTransform(g); + + double vx12 = (a2.getX() - a1.getX()); + double vy12 = (a2.getY() - a1.getY()); + double angle12 = Math.atan2(vy12, vx12) * (180d / Math.PI); + + double vx13 = (a3.getX() - a1.getX()); + double vy13 = (a3.getY() - a1.getY()); + double angle13 = Math.atan2(vy13, vx13) * (180d / Math.PI); + + double angle1 = angle12 - angle13; + + if (angle1 < 0) angle1 += 360; + + double angle2 = 360 - angle1; + + double dis1 = a1.getPosition().distance(a2.getPosition()); + double dis2 = a1.getPosition().distance(a3.getPosition()); + + double minDistance = Math.min(dis1, dis2); + + double distanceAngle1 = minDistance; + double distanceAngle2 = minDistance / 2; + + double distanceTextAngle1 = minDistance; + double distanceTextAngle2 = minDistance / 2; + + // Lines + + shapeDefinitionList.add(new ShapeDefinition(2, new Line2D.Double(a1.getPosition(), a2.getPosition()))); + shapeDefinitionList.add(new ShapeDefinition(2, new Line2D.Double(a1.getPosition(), a3.getPosition()))); + + // angle 1 + { + double arcSize = distanceAngle1; + Arc2D arc = new Arc2D.Double( + a1.getX() - arcSize / 2d, + a1.getY() - arcSize / 2d, + arcSize, + arcSize, + -angle12, + angle1,//angle13, + Arc2D.OPEN + ); + + shapeDefinitionList.add(new ShapeDefinition(2, arc)); + } + + // angle 2 + { + double arcSize = distanceAngle2; + Arc2D arc = new Arc2D.Double( + a1.getX() - arcSize / 2d, + a1.getY() - arcSize / 2d, + arcSize, + arcSize, + -angle13, + angle2,//angle13, + Arc2D.OPEN + ); + + shapeDefinitionList.add(new ShapeDefinition(2, arc)); + } + + // draw lines ( black background, then white ) + + float oldAlpha = -1; + g.setColor(Color.black); + for (ShapeDefinition ld : shapeDefinitionList) { + if (oldAlpha != ld.alpha) { + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ld.alpha)); + oldAlpha = ld.alpha; + } + g.setStroke(stroke[ld.stroke]); + g.draw(ld.shape); + } + + g.setColor(Color.white); + for (ShapeDefinition ld : shapeDefinitionList) { + if (oldAlpha != ld.alpha) { + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, ld.alpha)); + oldAlpha = ld.alpha; + } + g.setStroke(stroke[ld.stroke - 1]); + g.draw(ld.shape); + } + + // Display angle text + + { + int fontSize = (int) convertScale(canvas, 15); + Font font = new Font("Arial", Font.PLAIN, fontSize); + + pushTransform(g); + String pixelString = " " + (int) (Math.round(angle1)) + "° "; + Rectangle2D pixelBounds = GraphicsUtil.getStringBounds(g, font, pixelString); + g.translate(a1.getX(), a1.getY()); + + double angleText = Math.toRadians(angle12 - angle1 / 2d); + g.translate(Math.cos(angleText) * distanceTextAngle1 / 2, Math.sin(angleText) * distanceTextAngle1 / 2); + g.translate(-pixelBounds.getWidth() / 2, 0); + + g.setFont(font); + g.setColor(Color.white); + g.fill(pixelBounds); + g.setColor(Color.black); + g.drawString(pixelString, 0, 0); + popTransform(g); + } + + { + int fontSize = (int) convertScale(canvas, 15); + Font font = new Font("Arial", Font.PLAIN, fontSize); + + pushTransform(g); + String pixelString = " " + (int) (Math.round(angle2)) + "� "; + Rectangle2D pixelBounds = GraphicsUtil.getStringBounds(g, font, pixelString); + g.translate(a1.getX(), a1.getY()); + + double angleText = Math.toRadians(180 + angle12 - angle1 / 2d); + g.translate(Math.cos(angleText) * distanceTextAngle2 / 2d, Math.sin(angleText) * distanceTextAngle2 / 2d); + g.translate(-pixelBounds.getWidth() / 2, 0); + + float alpha = 1; + double distance = canvas.getScaleX() * distanceTextAngle2 / 2d; + + Scaler scaler = new Scaler(0, 10, 0, 1, false); + alpha = (float) scaler.scale(distance - pixelBounds.getWidth() / 10d); + + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); + + g.setFont(font); + g.setColor(Color.white); + g.fill(pixelBounds); + g.setColor(Color.black); + g.drawString(pixelString, 0, 0); + + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); + popTransform(g); + } + + // get back to original transform + + popTransform(g); + + // display anchors + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); + a1.paint(g, sequence, canvas); + a2.paint(g, sequence, canvas); + a3.paint(g, sequence, canvas); + } + + double convertScale(IcyCanvas canvas, int value) { + return canvas.canvasToImageLogDeltaX(value); + } + + @Override + public void mousePressed(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.mousePressed(e, imagePoint, canvas); + a2.mousePressed(e, imagePoint, canvas); + a3.mousePressed(e, imagePoint, canvas); + } + + @Override + public void mouseReleased(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.mouseReleased(e, imagePoint, canvas); + a2.mouseReleased(e, imagePoint, canvas); + a3.mouseReleased(e, imagePoint, canvas); + } + + @Override + public void mouseClick(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.mouseClick(e, imagePoint, canvas); + a2.mouseClick(e, imagePoint, canvas); + a3.mouseClick(e, imagePoint, canvas); + + } + + @Override + public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.mouseMove(e, imagePoint, canvas); + a2.mouseMove(e, imagePoint, canvas); + a3.mouseMove(e, imagePoint, canvas); + } + + @Override + public void mouseDrag(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.mouseDrag(e, imagePoint, canvas); + a2.mouseDrag(e, imagePoint, canvas); + a3.mouseDrag(e, imagePoint, canvas); + } + + @Override + public void keyPressed(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + + if ((e.getKeyCode() == KeyEvent.VK_DELETE && a1.isSelected()) || (e.getKeyCode() == KeyEvent.VK_DELETE && a2.isSelected())) { + for (Sequence sequence : Icy.getMainInterface().getSequencesContaining(this)) { + sequence.removeOverlay(this); + } + } + + a1.keyPressed(e, imagePoint, canvas); + a2.keyPressed(e, imagePoint, canvas); + a3.keyPressed(e, imagePoint, canvas); + } + + @Override + public void keyReleased(KeyEvent e, Point5D.Double imagePoint, IcyCanvas canvas) { + a1.keyReleased(e, imagePoint, canvas); + a2.keyReleased(e, imagePoint, canvas); + a3.keyReleased(e, imagePoint, canvas); + } }