From edf8be1460097a7af777aa066727e25409f25364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Thu, 10 Jul 2025 15:34:05 +0200 Subject: [PATCH 1/8] Turtle graphics example using aplu5.jar --- examples/turtle.java | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 examples/turtle.java diff --git a/examples/turtle.java b/examples/turtle.java new file mode 100755 index 0000000..ed7c8bc --- /dev/null +++ b/examples/turtle.java @@ -0,0 +1,53 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? + +// Turtle demo using the aplu5 library +// JBang script to run a Java program that uses the Turtle graphics library +// Usage: jbang turtle.java +// Requires: JDK 8 or higher + +//DEPS ch.aplu.turtle:aplu5:0.1.9 + +import ch.aplu.turtle.Turtle; +import ch.aplu.turtle.TurtleFrame; + +// aplu5.jar is not published to Maven Central +// +// To make aplu5.jar available to Jython, install it in +// the local Maven repository by following these steps: +// +// (1 - Download file) $ wget https://www.java-online.ch/download/aplu5.jar +// +// (2 - Install file ) $ mvn install:install-file -Dfile=./aplu5.jar -DgroupId=ch.aplu.turtle -DartifactId=aplu5 -Dversion=0.1.9 -Dpackaging=jar +// + +public class turtle { + + public static Turtle joe = null; + + public static void drawSquare(int sideLength) + { + for (int i = 0; i < 4; i++) + { + joe.forward(sideLength); + joe.right(90); + } + } + + public static void drawing() + { + joe.setPenColor("red"); + // repeat 10 times + for (int i = 0; i < 10; i++) + { + drawSquare(150); + joe.left(36); // rotate 36 degrees for the next square + } + } + + public static void main(String[] args) + { + TurtleFrame frame = new TurtleFrame("Turtle Demo", 500, 500); + turtle.joe = new Turtle(frame); + turtle.drawing(); + } +} From e8e536c86ac2e133b82b756132d92d4669caa313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Thu, 10 Jul 2025 15:34:05 +0200 Subject: [PATCH 2/8] feat: turtle graphics example using aplu5.jar --- examples/turtle.java | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 examples/turtle.java diff --git a/examples/turtle.java b/examples/turtle.java new file mode 100755 index 0000000..ed7c8bc --- /dev/null +++ b/examples/turtle.java @@ -0,0 +1,53 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? + +// Turtle demo using the aplu5 library +// JBang script to run a Java program that uses the Turtle graphics library +// Usage: jbang turtle.java +// Requires: JDK 8 or higher + +//DEPS ch.aplu.turtle:aplu5:0.1.9 + +import ch.aplu.turtle.Turtle; +import ch.aplu.turtle.TurtleFrame; + +// aplu5.jar is not published to Maven Central +// +// To make aplu5.jar available to Jython, install it in +// the local Maven repository by following these steps: +// +// (1 - Download file) $ wget https://www.java-online.ch/download/aplu5.jar +// +// (2 - Install file ) $ mvn install:install-file -Dfile=./aplu5.jar -DgroupId=ch.aplu.turtle -DartifactId=aplu5 -Dversion=0.1.9 -Dpackaging=jar +// + +public class turtle { + + public static Turtle joe = null; + + public static void drawSquare(int sideLength) + { + for (int i = 0; i < 4; i++) + { + joe.forward(sideLength); + joe.right(90); + } + } + + public static void drawing() + { + joe.setPenColor("red"); + // repeat 10 times + for (int i = 0; i < 10; i++) + { + drawSquare(150); + joe.left(36); // rotate 36 degrees for the next square + } + } + + public static void main(String[] args) + { + TurtleFrame frame = new TurtleFrame("Turtle Demo", 500, 500); + turtle.joe = new Turtle(frame); + turtle.drawing(); + } +} From 48b324554d8eb7851d30b6ddb47847c8c3685b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Thu, 14 Aug 2025 20:07:16 +0200 Subject: [PATCH 3/8] fix: updated turtle program to use Java 25 --- examples/turtle.java | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/examples/turtle.java b/examples/turtle.java index ed7c8bc..be0eff0 100755 --- a/examples/turtle.java +++ b/examples/turtle.java @@ -3,16 +3,17 @@ // Turtle demo using the aplu5 library // JBang script to run a Java program that uses the Turtle graphics library // Usage: jbang turtle.java -// Requires: JDK 8 or higher +// Requires: Java 25 or higher //DEPS ch.aplu.turtle:aplu5:0.1.9 +//JAVA 25+ import ch.aplu.turtle.Turtle; import ch.aplu.turtle.TurtleFrame; // aplu5.jar is not published to Maven Central // -// To make aplu5.jar available to Jython, install it in +// To make aplu5.jar available to JBang, install it in // the local Maven repository by following these steps: // // (1 - Download file) $ wget https://www.java-online.ch/download/aplu5.jar @@ -20,34 +21,26 @@ // (2 - Install file ) $ mvn install:install-file -Dfile=./aplu5.jar -DgroupId=ch.aplu.turtle -DartifactId=aplu5 -Dversion=0.1.9 -Dpackaging=jar // -public class turtle { +Turtle joe = null; - public static Turtle joe = null; - - public static void drawSquare(int sideLength) - { - for (int i = 0; i < 4; i++) - { - joe.forward(sideLength); - joe.right(90); +void drawSquare(int sideLength) { + for (int i = 0; i < 4; i++) { + joe.forward(sideLength); + joe.right(90); } - } +} - public static void drawing() - { +void drawing() { joe.setPenColor("red"); // repeat 10 times - for (int i = 0; i < 10; i++) - { - drawSquare(150); - joe.left(36); // rotate 36 degrees for the next square + for (int i = 0; i < 10; i++) { + drawSquare(150); + joe.left(36); // rotate 36 degrees for the next square } - } +} - public static void main(String[] args) - { +void main() { TurtleFrame frame = new TurtleFrame("Turtle Demo", 500, 500); - turtle.joe = new Turtle(frame); - turtle.drawing(); - } -} + joe = new Turtle(frame); + drawing(); +} \ No newline at end of file From 4a71ead65dcb39cfeec38cc45a7f90e6432c47e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Fri, 15 Aug 2025 19:32:56 +0200 Subject: [PATCH 4/8] fix: created a standalone turtle demo program --- examples/Turtle.java | 2656 ++++++++++++++++++++++++++++++++++++++ examples/TurtleDemo.java | 22 + examples/turtle.java | 46 - 3 files changed, 2678 insertions(+), 46 deletions(-) create mode 100755 examples/Turtle.java create mode 100755 examples/TurtleDemo.java delete mode 100755 examples/turtle.java diff --git a/examples/Turtle.java b/examples/Turtle.java new file mode 100755 index 0000000..63d1548 --- /dev/null +++ b/examples/Turtle.java @@ -0,0 +1,2656 @@ +/**AUTHOR: NICHOLAS SEWARD + * EMAIL: nicholas.seward@gmail.com + * LICENSE: MIT (USE THIS HOWEVER YOU SEE FIT.) + * DATE: 6/21/2012 + * VERSION: 2 (THIS SHOULD BE VERSION 0.1 BUT THAT IS A SILLY NUMBERING SYSTEM.) + * + * + * THIS SOFTWARE HAS NO WARRANTY. IF IT WORKS, SUPER. IF IT DOESN'T, LET ME + * KNOW AND I MIGHT OR MIGHT NOT DO SOMETHING ABOUT IT. + * + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + */ + +import java.awt.event.*; +import javax.swing.*; +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.io.*; +import java.lang.reflect.Method; +import java.util.*; +import javax.imageio.ImageIO; +import javax.swing.filechooser.FileNameExtensionFilter; + +/** + * Turtle is a selfcontained class that will allow students to make + * beautiful turtle graphics with ease. + * + * @author Nicholas Seward + */ +@SuppressWarnings("unchecked") +public class Turtle implements Runnable, ActionListener, MouseListener, MouseMotionListener, KeyListener, ComponentListener, MouseWheelListener +{ + + private static ArrayList turtles; + private static TreeMap turtleStates; + private static TreeMap redoStates; + private static JFrame window; + private static JApplet applet; + private static JLabel draw; + private static int width, height; + private static BufferedImage offscreenImage, midscreenImage, onscreenImage; + private static Graphics2D offscreen, midscreen, onscreen; + private static BufferedImage backgroundImage; + private static Color backgroundColor; + private static ImageIcon icon; + private static Turtle turtle; + private static HashMap shapes; //You can only add. Never remove. + private static HashMap colors; + private static HashMap> keyBindings; + private static HashMap> mouseBindings; + private static double centerX, centerY; + private static double scale; + private static TreeSet keysDown; + private static TreeSet processedKeys; + private static TreeSet unprocessedKeys; + private static long lastUpdate; + private static long fps; + private static final Object turtleLock=new Object(); + private static int dragx=0,dragy=0,x=0,y=0,modifiers=0; + private static final Object keyLock = new Object(); + private static final int REFRESH_MODE_ANIMATED=0; + private static final int REFRESH_MODE_STATE_CHANGE=1; + private static final int REFRESH_MODE_ON_DEMAND=2; + private static int refreshMode; + private static final int BACKGROUND_MODE_STRETCH=0; + private static final int BACKGROUND_MODE_CENTER=1; + private static final int BACKGROUND_MODE_TILE=2; + private static final int BACKGROUND_MODE_CENTER_RELATIVE=3; + private static final int BACKGROUND_MODE_TILE_RELATIVE=4; + private static int backgroundMode; + private static Turtle selectedTurtle; + private static boolean running; + + + + + static { init(); } + + /** + * This is an internal method that should never be called. + */ + public void run() + { + if(Thread.currentThread().getName().equals("render")) renderLoop(); + else if(Thread.currentThread().getName().equals("listen")) eventLoop(); + } + + private void eventLoop() + { + //System.out.println("EVENT LOOP STARTED"); + long time=0; + while(running) + { + time=System.nanoTime(); + processKeys(); + waitUntil(time+1000000000/fps); + } + } + + private void renderLoop() + { + //System.out.println("RENDER LOOP STARTED"); + long time=0; + while(running) + { + time=System.nanoTime(); + //System.out.println(time); + if(refreshMode==REFRESH_MODE_ANIMATED) draw(); + if (!waitUntil(time+1000000000/fps)) fps--; + else if (fps<30) fps++; + } + } + + private static boolean waitUntil(Long time) + { + long now=System.nanoTime(); + if (now()); + turtles=new ArrayList(); + turtleStates=new TreeMap(); + redoStates=new TreeMap(); + width=500; + height=500; + backgroundColor=Color.WHITE; + keyBindings=new HashMap>(); + mouseBindings=new HashMap>(); + centerX=0; + centerY=0; + scale=1; + keysDown=new TreeSet(); + processedKeys=new TreeSet(); + unprocessedKeys=new TreeSet(); + lastUpdate=0; + fps=30; + dragx=0; + dragy=0; + x=0; + y=0; + modifiers=0; + refreshMode=REFRESH_MODE_ANIMATED; + backgroundMode=BACKGROUND_MODE_TILE_RELATIVE; + selectedTurtle=null; + running=true; + + + window = new JFrame("Turtle"); + icon = new ImageIcon(); + setupBuffering(); + draw = new JLabel(icon); + window.setContentPane(draw); + //window.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE); + try{window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);} + catch(Exception e){} + + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu("File"); + menuBar.add(menu); + JMenuItem menuItem1 = new JMenuItem("Save..."); + + menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menu.add(menuItem1); + window.setJMenuBar(menuBar); + window.pack(); + window.requestFocusInWindow(); + drawTurtleIcon(); + window.setVisible(true); + makeShapes(); + turtle=new Turtle(0); + draw.setFocusable(true); + menuItem1.addActionListener(turtle); + window.addComponentListener(turtle); + draw.addComponentListener(turtle); + draw.addMouseListener(turtle); + draw.addMouseMotionListener(turtle); + draw.addMouseWheelListener(turtle); + window.addKeyListener(turtle); + draw.addKeyListener(turtle); + draw.requestFocus(); + initColors(); + +// GraphicsEnvironment ge; +// ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); +// String[] fontNames = ge.getAvailableFontFamilyNames(); +// System.out.println(Arrays.toString(fontNames)); + + (new Thread(turtle,"render")).start(); + (new Thread(turtle,"listen")).start(); + } + + public static void exit() + { + running=false; + window.setVisible(false); + window.dispose(); + } + + /** + * This is an experimental method that should allow you to make turtle + * applets in the future. For now, it doesn't work because the key and + * mouse bindings require reflection and applets think that allowing + * reflection would open a security hole. Theoretically in the init method + * of the applet you need to place Turtle.startApplet(this);. + * This is not currently working. + * + * @param applet + */ + public static void startApplet(JApplet applet) + { + //turtleStates.clear(); + //turtles.clear(); + //init(); + Turtle.applet=applet; + applet.setContentPane(window.getContentPane()); + window.setVisible(false); + try{(new Thread((Runnable)applet,"turtle")).start();} + catch(Exception e) + { + e.printStackTrace(); + } + } + + private static void initColors() + { + colors = new HashMap(); + colors.put("aliceblue", new Color(0xf0f8ff)); + colors.put("antiquewhite", new Color(0xfaebd7)); + colors.put("aqua", new Color(0x00ffff)); + colors.put("aquamarine", new Color(0x7fffd4)); + colors.put("azure", new Color(0xf0ffff)); + colors.put("beige", new Color(0xf5f5dc)); + colors.put("bisque", new Color(0xffe4c4)); + colors.put("black", new Color(0x000000)); + colors.put("blanchedalmond", new Color(0xffebcd)); + colors.put("blue", new Color(0x0000ff)); + colors.put("blueviolet", new Color(0x8a2be2)); + colors.put("brown", new Color(0xa52a2a)); + colors.put("burlywood", new Color(0xdeb887)); + colors.put("cadetblue", new Color(0x5f9ea0)); + colors.put("chartreuse", new Color(0x7fff00)); + colors.put("chocolate", new Color(0xd2691e)); + colors.put("coral", new Color(0xff7f50)); + colors.put("cornflowerblue", new Color(0x6495ed)); + colors.put("cornsilk", new Color(0xfff8dc)); + colors.put("crimson", new Color(0xdc143c)); + colors.put("cyan", new Color(0x00ffff)); + colors.put("darkblue", new Color(0x00008b)); + colors.put("darkcyan", new Color(0x008b8b)); + colors.put("darkgoldenrod", new Color(0xb8860b)); + colors.put("darkgray", new Color(0xa9a9a9)); + colors.put("darkgreen", new Color(0x006400)); + colors.put("darkkhaki", new Color(0xbdb76b)); + colors.put("darkmagenta", new Color(0x8b008b)); + colors.put("darkolivegreen", new Color(0x556b2f)); + colors.put("darkorange", new Color(0xff8c00)); + colors.put("darkorchid", new Color(0x9932cc)); + colors.put("darkred", new Color(0x8b0000)); + colors.put("darksalmon", new Color(0xe9967a)); + colors.put("darkseagreen", new Color(0x8fbc8f)); + colors.put("darkslateblue", new Color(0x483d8b)); + colors.put("darkslategray", new Color(0x2f4f4f)); + colors.put("darkturquoise", new Color(0x00ced1)); + colors.put("darkviolet", new Color(0x9400d3)); + colors.put("deeppink", new Color(0xff1493)); + colors.put("deepskyblue", new Color(0x00bfff)); + colors.put("dimgray", new Color(0x696969)); + colors.put("dodgerblue", new Color(0x1e90ff)); + colors.put("firebrick", new Color(0xb22222)); + colors.put("floralwhite", new Color(0xfffaf0)); + colors.put("forestgreen", new Color(0x228b22)); + colors.put("fuchsia", new Color(0xff00ff)); + colors.put("gainsboro", new Color(0xdcdcdc)); + colors.put("ghostwhite", new Color(0xf8f8ff)); + colors.put("gold", new Color(0xffd700)); + colors.put("goldenrod", new Color(0xdaa520)); + colors.put("gray", new Color(0x808080)); + colors.put("green", new Color(0x008000)); + colors.put("greenyellow", new Color(0xadff2f)); + colors.put("honeydew", new Color(0xf0fff0)); + colors.put("hotpink", new Color(0xff69b4)); + colors.put("indianred", new Color(0xcd5c5c)); + colors.put("indigo", new Color(0x4b0082)); + colors.put("ivory", new Color(0xfffff0)); + colors.put("khaki", new Color(0xf0e68c)); + colors.put("lavender", new Color(0xe6e6fa)); + colors.put("lavenderblush", new Color(0xfff0f5)); + colors.put("lawngreen", new Color(0x7cfc00)); + colors.put("lemonchiffon", new Color(0xfffacd)); + colors.put("lightblue", new Color(0xadd8e6)); + colors.put("lightcoral", new Color(0xf08080)); + colors.put("lightcyan", new Color(0xe0ffff)); + colors.put("lightgoldenrodyellow", new Color(0xfafad2)); + colors.put("lightgreen", new Color(0x90ee90)); + colors.put("lightgrey", new Color(0xd3d3d3)); + colors.put("lightpink", new Color(0xffb6c1)); + colors.put("lightsalmon", new Color(0xffa07a)); + colors.put("lightseagreen", new Color(0x20b2aa)); + colors.put("lightskyblue", new Color(0x87cefa)); + colors.put("lightslategray", new Color(0x778899)); + colors.put("lightsteelblue", new Color(0xb0c4de)); + colors.put("lightyellow", new Color(0xffffe0)); + colors.put("lime", new Color(0x00ff00)); + colors.put("limegreen", new Color(0x32cd32)); + colors.put("linen", new Color(0xfaf0e6)); + colors.put("magenta", new Color(0xff00ff)); + colors.put("maroon", new Color(0x800000)); + colors.put("mediumaquamarine", new Color(0x66cdaa)); + colors.put("mediumblue", new Color(0x0000cd)); + colors.put("mediumorchid", new Color(0xba55d3)); + colors.put("mediumpurple", new Color(0x9370db)); + colors.put("mediumseagreen", new Color(0x3cb371)); + colors.put("mediumslateblue", new Color(0x7b68ee)); + colors.put("mediumspringgreen", new Color(0x00fa9a)); + colors.put("mediumturquoise", new Color(0x48d1cc)); + colors.put("mediumvioletred", new Color(0xc71585)); + colors.put("midnightblue", new Color(0x191970)); + colors.put("mintcream", new Color(0xf5fffa)); + colors.put("mistyrose", new Color(0xffe4e1)); + colors.put("moccasin", new Color(0xffe4b5)); + colors.put("navajowhite", new Color(0xffdead)); + colors.put("navy", new Color(0x000080)); + colors.put("oldlace", new Color(0xfdf5e6)); + colors.put("olive", new Color(0x808000)); + colors.put("olivedrab", new Color(0x6b8e23)); + colors.put("orange", new Color(0xffa500)); + colors.put("orangered", new Color(0xff4500)); + colors.put("orchid", new Color(0xda70d6)); + colors.put("palegoldenrod", new Color(0xeee8aa)); + colors.put("palegreen", new Color(0x98fb98)); + colors.put("paleturquoise", new Color(0xafeeee)); + colors.put("palevioletred", new Color(0xdb7093)); + colors.put("papayawhip", new Color(0xffefd5)); + colors.put("peachpuff", new Color(0xffdab9)); + colors.put("peru", new Color(0xcd853f)); + colors.put("pink", new Color(0xffc0cb)); + colors.put("plum", new Color(0xdda0dd)); + colors.put("powderblue", new Color(0xb0e0e6)); + colors.put("purple", new Color(0x800080)); + colors.put("red", new Color(0xff0000)); + colors.put("rosybrown", new Color(0xbc8f8f)); + colors.put("royalblue", new Color(0x4169e1)); + colors.put("saddlebrown", new Color(0x8b4513)); + colors.put("salmon", new Color(0xfa8072)); + colors.put("sandybrown", new Color(0xf4a460)); + colors.put("seagreen", new Color(0x2e8b57)); + colors.put("seashell", new Color(0xfff5ee)); + colors.put("sienna", new Color(0xa0522d)); + colors.put("silver", new Color(0xc0c0c0)); + colors.put("skyblue", new Color(0x87ceeb)); + colors.put("slateblue", new Color(0x6a5acd)); + colors.put("slategray", new Color(0x708090)); + colors.put("snow", new Color(0xfffafa)); + colors.put("springgreen", new Color(0x00ff7f)); + colors.put("steelblue", new Color(0x4682b4)); + colors.put("tan", new Color(0xd2b48c)); + colors.put("teal", new Color(0x008080)); + colors.put("thistle", new Color(0xd8bfd8)); + colors.put("tomato", new Color(0xff6347)); + colors.put("turquoise", new Color(0x40e0d0)); + colors.put("violet", new Color(0xee82ee)); + colors.put("wheat", new Color(0xf5deb3)); + colors.put("white", new Color(0xffffff)); + colors.put("whitesmoke", new Color(0xf5f5f5)); + colors.put("yellow", new Color(0xffff00)); + colors.put("yellowgreen", new Color(0x9acd32)); + } + + private static Color getColor(String color) + { + String origColor=color; + color = color.toLowerCase().replaceAll("[^a-z]", ""); + if (colors.containsKey(color)) + { + return colors.get(color); + } + else + { + return null; + } + } + + private static void setupBuffering() + { + synchronized(turtleLock) + { + lastUpdate=0; + offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + midscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + offscreen = offscreenImage.createGraphics(); + midscreen = midscreenImage.createGraphics(); + onscreen = onscreenImage.createGraphics(); + //offscreen.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); + //offscreen.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC); + offscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + midscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + onscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); + drawBackground(offscreen); + drawBackground(onscreen); + icon.setImage(onscreenImage); + } + } + + private static void drawTurtleIcon() + { + byte[] imageData= new byte[]{71,73,70,56,57,97,16,0,16,0,-95,2,0,0,-103, + 0,0,-1,0,-1,-1,-1,-1,-1,-1,33,-7,4,1,10,0,2,0,44,0,0,0,0,16,0,16,0, + 0,2,44,-108,-113,-87,-53,-19,-33,-128,4,104,74,35,67,-72,34,-21,11, + 124,27,-90,-107,-109,72,117,-91,-71,110,103,-37,90,-31,-10,-55,-87, + 122,-34,74,72,-15,17,-56,-127,8,33,5,0,59}; + try + { + BufferedImage tmpicon = ImageIO.read(new ByteArrayInputStream(imageData)); + window.setIconImage(tmpicon); + } + catch (Exception e) {} + } + + private static void makeShapes() + { + shapes=new HashMap(); + int[] xs=new int[]{66, 65, 63, 61, 53, 44, 33, 24, 23, 19, 17, 14, 9, 8, 8, 10, 13, 11, 10, 2, 9, 11, 15, 11, 11, 10, 12, 18, 20, 22, 23, 26, 35, 44, 53, 61, 62, 64, 66, 71, 77, 78, 77, 76, 72, 77, 81, 86, 91, 94, 97, 98, 97, 95, 92, 87, 82, 77, 72, 74, 77, 78, 76, 70}; + int[] ys=new int[]{18, 19, 21, 25, 23, 22, 23, 27, 25, 21, 20, 21, 27, 30, 32, 34, 37, 42, 47, 50, 53, 59, 65, 68, 69, 71, 74, 79, 80, 80, 78, 74, 77, 78, 77, 75, 79, 81, 82, 81, 76, 73, 71, 69, 66, 59, 60, 61, 60, 58, 54, 50, 46, 42, 40, 39, 40, 41, 34, 32, 28, 27, 24, 19}; + Polygon p = new Polygon(xs,ys,xs.length); + shapes.put("turtle", p); + xs=new int[]{0,100,0,20}; + ys=new int[]{0,50,100,50}; + p = new Polygon(xs,ys,xs.length); + shapes.put("arrow", p); + xs=new int[]{0,100,100,0}; + ys=new int[]{0,0,100,100}; + p = new Polygon(xs,ys,xs.length); + shapes.put("rectangle", p); + shapes.put("square", p); + xs=new int[]{0,100,0}; + ys=new int[]{0,50,100}; + p = new Polygon(xs,ys,xs.length); + shapes.put("triangle", p); + int divisions=24; + xs=new int[divisions]; + ys=new int[divisions]; + for(int i=0;i polygon=new ArrayList(); + //temporary storage + private Long _time; + private Point2D.Double _location; + private Double _direction; + private String _shape; + private BufferedImage _image; + private Double _shapeWidth; + private Double _shapeHeight; + private Double _tilt; + private Double _penWidth; + private Color _penColor; + private Double _outlineWidth; + private Color _outlineColor; + private Color _fillColor; + private Double _speed; + private Boolean _isPenDown; + private Boolean _isFilling; + private Boolean _isVisible; + private ArrayList _polygon; + private Boolean _isFill; + private Boolean _isStamp; + private Double _dotSize; + private Color _dotColor; + private Font _font; + private String _text; + private Integer _justification; + private Point2D.Double _textOffset; + + //secondary temporary storage + private Long __time; + private Point2D.Double __location; + private Double __direction; + private String __shape; + private BufferedImage __image; + private Double __shapeWidth; + private Double __shapeHeight; + private Double __tilt; + private Double __penWidth; + private Color __penColor; + private Double __outlineWidth; + private Color __outlineColor; + private Color __fillColor; + private Double __speed; + private Boolean __isPenDown; + private Boolean __isFilling; + private Boolean __isVisible; + private ArrayList __polygon; + private Boolean __isFill; + private Boolean __isStamp; + private Double __dotSize; + private Color __dotColor; + private Font __font; + private String __text; + private Integer __justification; + private Point2D.Double __textOffset; + + /** + * Makes a default turtle. + */ + public Turtle() + { + if(window==null)init(); + synchronized(turtleLock){turtles.add(this);} + long time=storeCurrentState(); + updateAll(); + } + + /** + * Makes a default turtle at the specified position. + * + * @param x x coordinate + * @param y y coordinate + */ + public Turtle(double x,double y) + { + if(window==null)init(); + location=new Point2D.Double(x,y); + synchronized(turtleLock){turtles.add(this);} + long time=storeCurrentState(); + updateAll(); + } + + /** + * This creates a cloned copy of a turtle. + * + * @return a cloned copy of a turtle + */ + public Turtle clone() + { + Turtle t=new Turtle(0); + t.location=(Point2D.Double)this.location.clone(); + t.direction=this.direction; + t.shape=t.shape; + t.image=this.image; + t.shapeWidth=this.shapeWidth; + t.shapeHeight=this.shapeHeight; + t.tilt=this.tilt; + t.penWidth=this.penWidth; + t.penColor=this.penColor; + t.outlineWidth=this.outlineWidth; + t.outlineColor=this.outlineColor; + t.fillColor=this.fillColor; + t.speed=this.speed; + t.isPenDown=this.isPenDown; + t.isFilling=this.isFilling; + t.isVisible=this.isVisible; + if(window==null)init(); + synchronized(turtleLock){turtles.add(t);} + long time=t.storeCurrentState(); + return t; + } + +/* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ STATE MANAGEMENT _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + */ + + private long storeCurrentState() + { + return storeCurrentState(false, false, 0,null,null,null,0,null); + } + private long storeAnimatedState() + { + return storeCurrentState(true, false, 0,null,null,null,0,null); + } + private long storeCurrentState(boolean animate, boolean isStamp,double dotSize,Color dotColor,Font font,String text,int justification,Point2D.Double textOffset) + { + ArrayList state = new ArrayList(); + long time=System.nanoTime(); + synchronized(turtleLock) + { + state.add(0); //0 + state.add(this); //1 + state.add(location.clone());//2 + state.add(direction); //3 + state.add(shape); //4 + state.add(image); //5 + state.add(shapeWidth); //6 + state.add(shapeHeight); //7 + state.add(tilt); //8 + state.add(penWidth); //9 + state.add(penColor); //10 + state.add(outlineWidth); //11 + state.add(outlineColor); //12 + state.add(fillColor); //13 + state.add(speed); //14 + state.add(isPenDown); //15 + state.add(isFilling); //16 + state.add(isVisible); //17 + state.add(isStamp); //18 + state.add(dotSize); //19 + state.add(dotColor); //20 + state.add(font); //21 + state.add(text); //22 + state.add(justification); //23 + state.add(textOffset); //24 + if (!turtleStates.isEmpty() && turtleStates.lastKey()>time) time=turtleStates.lastKey()+1; + if (animate) time+=(long)(speed*1000000); + state.set(0, time); + turtleStates.put(time, state); + redoStates.clear(); + } + if(refreshMode==REFRESH_MODE_STATE_CHANGE) draw(); + if(refreshMode==REFRESH_MODE_ANIMATED)waitUntil(time); + return time; + } + + private static void clearStorage() + { + synchronized(turtleLock) + { + for(Turtle t:turtles) + { + t.__time=null; + t.__location=null; + t.__direction=null; + t.__shape=null; + t.__image=null; + t.__shapeWidth=null; + t.__shapeHeight=null; + t.__tilt=null; + t.__penWidth=null; + t.__penColor=null; + t.__outlineWidth=null; + t.__outlineColor=null; + t.__fillColor=null; + t.__speed=null; + t.__isPenDown=null; + t.__isFilling=null; + t.__isVisible=null; + t.__isStamp=null; + t.__dotSize=null; + t.__dotColor=null; + t.__font=null; + t.__text=null; + t.__justification=null; + t.__textOffset=null; + t._time=null; + t._location=null; + t._direction=null; + t._shape=null; + t._shapeWidth=null; + t._shapeHeight=null; + t._image=null; + t._tilt=null; + t._penWidth=null; + t._penColor=null; + t._outlineWidth=null; + t._outlineColor=null; + t._fillColor=null; + t._speed=null; + t._isPenDown=null; + t._isFilling=null; + t._isVisible=null; + t._isStamp=null; + t._dotSize=null; + t._dotColor=null; + t._font=null; + t._text=null; + t._justification=null; + t._textOffset=null; + } + } + } + + private static void retrieveState(long time) + { + synchronized(turtleLock) + { + Turtle t=getStateTurtle(turtleStates.get(time)); + t.__time=t._time; + t.__location=t._location; + t.__direction=t._direction; + t.__shape=t._shape; + t.__image=t._image; + t.__shapeWidth=t._shapeWidth; + t.__shapeHeight=t._shapeHeight; + t.__tilt=t._tilt; + t.__penWidth=t._penWidth; + t.__penColor=t._penColor; + t.__outlineWidth=t._outlineWidth; + t.__outlineColor=t._outlineColor; + t.__fillColor=t._fillColor; + t.__speed=t._speed; + t.__isPenDown=t._isPenDown; + t.__isFilling=t._isFilling; + t.__isVisible=t._isVisible; + t.__isStamp=t._isStamp; + t.__dotSize=t._dotSize; + t.__dotColor=t._dotColor; + t.__font=t._font; + t.__text=t._text; + t.__justification=t._justification; + t.__textOffset=t._textOffset; + ArrayList state=turtleStates.get(time); + t._time=getStateTime(state); + t._location=getStateLocation(state); + t._direction=getStateDirection(state); + t._shape=getStateShape(state); + t._shapeWidth=getStateShapeWidth(state); + t._shapeHeight=getStateShapeHeight(state); + t._image=getStateImage(state); + t._tilt=getStateTilt(state); + t._penWidth=getStatePenWidth(state); + t._penColor=getStatePenColor(state); + t._outlineWidth=getStateOutlineWidth(state); + t._outlineColor=getStateOutlineColor(state); + t._fillColor=getStateFillColor(state); + t._speed=getStateSpeed(state); + t._isPenDown=getStateIsPenDown(state); + t._isFilling=getStateIsFilling(state); + t._isVisible=getStateIsVisible(state); + t._isStamp=getStateIsStamp(state); + t._dotSize=getStateDotSize(state); + t._dotColor=getStateDotColor(state); + t._font=getStateFont(state); + t._text=getStateText(state); + t._justification=getStateJustification(state); + t._textOffset=getStateTextOffset(state); + } + } + + private static long getStateTime(ArrayList state){return (Long)state.get(0);} + private static Turtle getStateTurtle(ArrayList state){return (Turtle)state.get(1);} + private static Point2D.Double getStateLocation(ArrayList state){return (Point2D.Double)((Point2D.Double)state.get(2)).clone();} + private static double getStateDirection(ArrayList state){return (Double)state.get(3);} + private static String getStateShape(ArrayList state){return (String)state.get(4);} + private static BufferedImage getStateImage(ArrayList state){return (BufferedImage)state.get(5);} + private static double getStateShapeWidth(ArrayList state){return (Double)state.get(6);} + private static double getStateShapeHeight(ArrayList state){return (Double)state.get(7);} + private static double getStateTilt(ArrayList state){return (Double)state.get(8);} + private static double getStatePenWidth(ArrayList state){return (Double)state.get(9);} + private static Color getStatePenColor(ArrayList state){return (Color)state.get(10);} + private static double getStateOutlineWidth(ArrayList state){return (Double)state.get(11);} + private static Color getStateOutlineColor(ArrayList state){return (Color)state.get(12);} + private static Color getStateFillColor(ArrayList state){return (Color)state.get(13);} + private static double getStateSpeed(ArrayList state){return (Double)state.get(14);} + private static boolean getStateIsPenDown(ArrayList state){return (Boolean)state.get(15);} + private static boolean getStateIsFilling(ArrayList state){return (Boolean)state.get(16);} + private static boolean getStateIsVisible(ArrayList state){return (Boolean)state.get(17);} + private static boolean getStateIsStamp(ArrayList state){return (Boolean)state.get(18);} + private static double getStateDotSize(ArrayList state){return (Double)state.get(19);} + private static Color getStateDotColor(ArrayList state){return (Color)state.get(20);} + private static Font getStateFont(ArrayList state){return (Font)state.get(21);} + private static String getStateText(ArrayList state){return (String)state.get(22);} + private static int getStateJustification(ArrayList state){return (Integer)state.get(23);} + private static Point2D.Double getStateTextOffset(ArrayList state){return (Point2D.Double)state.get(24);} + + private static void restoreState(long time) + { + ArrayList state=turtleStates.get(time); + Turtle t=getStateTurtle(turtleStates.get(time)); + t.location=getStateLocation(state); + t.direction=getStateDirection(state); + t.shape=getStateShape(state); + t.shapeWidth=getStateShapeWidth(state); + t.shapeHeight=getStateShapeHeight(state); + t.image=getStateImage(state); + t.tilt=getStateTilt(state); + t.penWidth=getStatePenWidth(state); + t.penColor=getStatePenColor(state); + t.outlineWidth=getStateOutlineWidth(state); + t.outlineColor=getStateOutlineColor(state); + t.fillColor=getStateFillColor(state); + t.speed=getStateSpeed(state); + t.isPenDown=getStateIsPenDown(state); + t.isFilling=getStateIsFilling(state); + t.isVisible=getStateIsVisible(state); + if(refreshMode==REFRESH_MODE_STATE_CHANGE) draw(); + } + + private void select() + { + selectedTurtle=this; + } + + private void unselect() + { + if (selectedTurtle==this) selectedTurtle=null; + } + + /** + * Determines if a turtle is covering a screen position + * + * @param x x screen coordinate + * @param y y screen coordinate + * @return true if this turtle is at the indicated screen position. + */ + public boolean contains(int x, int y) + { + Point2D.Double point=new Point2D.Double(x,y); + if (_location==null)return false; + AffineTransform m = offscreen.getTransform(); + double x1,y1,dir1; + x1=_location.x; + y1=_location.y; + dir1=_direction; + m.translate(((x1-centerX)*scale+width/2),((y1-centerY)*(-scale)+height/2)); + m.scale(scale, scale); + if (image==null) + { + m.rotate(-Math.toRadians(dir1)); + m.scale(shapeWidth/100.0, shapeHeight/100.0); + m.translate(-50,-50); + Polygon p =shapes.get(shape); + GeneralPath gp=new GeneralPath(); + gp.append(p.getPathIterator(m),false); + return gp.contains(x, y); + } + else + { + int w=image.getWidth(); + int h=image.getHeight(); + m.rotate(-Math.toRadians(dir1)); + m.scale(shapeWidth/1.0/w, shapeHeight/1.0/h); + m.translate(-w/2,-h/2); + try {m.inverseTransform(point, point);} + catch(Exception e){return false;} + x=(int)point.x; + y=(int)point.y; + try + { + //System.out.println((new Color(image.getRGB(x, y),true)).getAlpha()); + return (new Color(image.getRGB(x, y),true)).getAlpha()>50; + } + catch(Exception e){return false;} + } + } + + /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ TURTLE METHODS _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + */ + + /** + * Gets the speed of the animation. + * @return milliseconds it takes to do one action + */ + public double getSpeed() + { + return speed; + } + + /** + * Sets the speed of the animation. + * @param delay milliseconds it takes to do one action + * @return state change timestamp + */ + public long speed(double delay) + { + this.speed=delay; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Moves the turtle forward by the distance. + * + * @param distance distance to move forward + * @return state change timestamp + */ + public long forward(double distance) + { + double angle=Math.toRadians(direction); + Point2D.Double pastLocation=(Point2D.Double)location.clone(); + location.x+=distance*Math.cos(angle); + location.y+=distance*Math.sin(angle); + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Moves the turtle backward by the distance. + * + * @param distance distance to move backward + * @return state change timestamp + */ + public long backward(double distance) + { + double angle=Math.toRadians(direction); + Point2D.Double pastLocation=(Point2D.Double)location.clone(); + location.x-=distance*Math.cos(angle); + location.y-=distance*Math.sin(angle); + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Turns the turtle left by the number of indicated degrees. + * + * @param angle angle in degrees + * @return state change timestamp + */ + public long left(double angle) + { + direction+=angle; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Turns the turtle right by the number of indicated degrees. + * + * @param angle angle in degrees + * @return state change timestamp + */ + public long right(double angle) + { + direction-=angle; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Gets the direction the turtle is facing neglecting tilt. + * + * @return state change timestamp + */ + public double getDirection() + { + double a=direction; + while(a>=360)a-=360; + while(a<0)a+=360; + return a; + } + + /** + * Sets the direction the turtle is facing neglecting tilt. + * + * @param direction angle counter-clockwise from east + * @return state change timestamp + */ + public long setDirection(double direction) + { + double a=direction; + while(this.direction-a>180)a+=360; + while(this.direction-a<-180)a-=360; + this.direction=a; + //this.direction=direction; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Moves the turtle to (0,0) and facing east. + * + * @return state change timestamp + */ + public long home() + { + return setPosition(0,0,0); + } + + /** + * Hides the turtle but it can still draw. + * + * @return state change timestamp + */ + public long hide() + { + isVisible=false; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Makes the turtle visible. + * + * @return state change timestamp + */ + public long show() + { + isVisible=true; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the direction in such a way that it faces (x,y) + * + * @param x x coordinate of target location + * @param y y coordinate of target location + * @return state change timestamp + */ + public long face(double x, double y) + { + return setDirection(towards(x,y)); + } + + /** + * Gets direction towards (x,y) + * + * @param x x coordinate of target location + * @param y y coordinate of target location + * @return angle in degrees where 0<=angle<360 + */ + public double towards(double x, double y) + { + return Math.toDegrees(Math.atan2(y-location.y, x-location.x)); + } + + /** + * Gets the distance to another position. + * + * @param x x coordinate of target location + * @param y y coordinate of target location + * @return distance between turtle's current location and another position + */ + public double distance(double x, double y) + { + return Math.sqrt((y-location.y)*(y-location.y)+(x-location.x)*(x-location.x)); + } + + /** + * Gets the x coordinate of the turtle. + * + * @return x coordinate + */ + public double getX() + { + return location.x; + } + + /** + * Gets the y coordinate of the turtle. + * + * @return y coordinate + */ + public double getY() + { + return location.y; + } + + /** + * Sets the position and direction of a turtle. + * + * @param x x coordinate + * @param y y coordinate + * @param direction angle counter-clockwise from east in degrees + * @return state change timestamp + */ + public long setPosition(double x, double y, double direction) + { + location.x=x; + location.y=y; + double a=direction; + while(this.direction-a>180)a+=360; + while(this.direction-a<-180)a-=360; + this.direction=a; + this.direction=direction; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Sets the position of a turtle. + * + * @param x x coordinate + * @param y y coordinate + * @return state change timestamp + */ + public long setPosition(double x, double y) + { + return setPosition(x, y, direction); + } + + /** + * Adds an additional angle to rotation of the turtle's shape when rendering. + * This is useful when you need to face a different direction than the + * direction you are moving in. + * + * @param angle angle in degrees + * @return state change timestamp + */ + public long tilt(double angle) + { + tilt+=angle; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Sets the angle to rotate the turtle's shape when rendering. + * This is useful when you need to face a different direction than the + * direction you are moving in. + * + * @param angle angle in degrees + * @return state change timestamp + */ + public long setTilt(double angle) + { + //double a=angle; + //while(tilt-a>180)a+=360; + //while(tilt-a<-180)a-=360; + //tilt=a; + tilt=angle; + long timeStamp=storeAnimatedState(); + return timeStamp; + } + + /** + * Gets the rotation of the turtle's shape away from the turtle's direction. + * + * @return tilt in degrees (positive in counter-clockwise) + */ + public double getTilt() + { + return tilt; + } + + /** + * Sets the width of the turtles path + * + * @param penWidth width of the turtles path + * @return state change timestamp + */ + public long width(double penWidth) + { + this.penWidth=penWidth; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the width of the turtle's outline. + * + * @param width width of the turtle's outline + * @return state change timestamp + */ + public long outlineWidth(double width) + { + this.outlineWidth=width; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Picks the turtle's tail up so it won't draw on the screen as it moves. + * + * @return state change timestamp + */ + public long up() + { + this.isPenDown=false; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Puts the turtle's tail down so it will draw on the screen as it moves. + * + * @return state change timestamp + */ + public long down() + { + this.isPenDown=true; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + public long stab() + { + Color c=Turtle.getColor("red"); + if(c!=null)this.penColor=c; + this.isPenDown=true; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's path color. + * + * @param penColor Color of the turtle's path. + * @return state change timestamp + */ + public long penColor(String penColor) + { + Color c=Turtle.getColor(penColor); + if(c!=null)this.penColor=c; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's path color. + * + * @param penColor Color of the turtle's path. + * @return state change timestamp + */ + public long penColor(Color penColor) + { + this.penColor=penColor; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's outlineColor color. + * + * @param outlineColor Color of the turtle's outlineColor. + * @return state change timestamp + */ + public long outlineColor(String outlineColor) + { + Color c=Turtle.getColor(outlineColor); + if(c!=null)this.outlineColor=c; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's outlineColor color. + * + * @param outlineColor Color of the turtle's outlineColor. + * @return state change timestamp + */ + public long outlineColor(Color outlineColor) + { + this.outlineColor=outlineColor; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's fill color. + * + * @param fillColor Color of the turtle's fill. + * @return state change timestamp + */ + public long fillColor(String fillColor) + { + Color c=Turtle.getColor(fillColor); + if(c!=null)this.fillColor=c; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the turtle's fill color. + * + * @param fillColor Color of the turtle's fill. + * @return state change timestamp + */ + public long fillColor(Color fillColor) + { + this.fillColor=fillColor; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Sets the shape of the turtle using the built in shapes (turtle,square, + * rectangle,triangle,arrow,circle) or to a image. + * + * @param shape shapename or filename of image + * @return state change timestamp + */ + public long shape(String shape) + { + try + { + image=ImageIO.read(new File(shape)); + this.shapeHeight=image.getHeight(); + this.shapeWidth=image.getWidth(); + } + catch(Exception e) + { + if(shapes.containsKey(shape)) + { + this.shape=shape; + this.shapeHeight=33; + this.shapeWidth=33; + image=null; + } + else {System.out.println("Unrecognized filename or shape name.");} + } + //if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); + long timeStamp=storeCurrentState(); + return timeStamp; + } + + public long shapeSize(int width, int height) + { + this.shapeHeight=height; + this.shapeWidth=width; + long timeStamp=storeCurrentState(); + return timeStamp; + } + + /** + * Put a copy of the current turtle shape on the canvas. + * + * @return state change timestamp + */ + public long stamp() + { + long timeStamp=storeCurrentState(true, true,0,null,null,null,0,null); + return timeStamp; + } + + /** + * Put a dot 3 times the size of the penWidth on the canvas. + * + * @return state change timestamp + */ + public long dot() + { + long timeStamp=storeCurrentState(true, false,penWidth*3,penColor,null,null,0,null); + return timeStamp; + } + + /** + * Put a dot 3 times the size of the penWidth on the canvas. + * + * @param color color of dot + * @return state change timestamp + */ + public long dot(String color) + { + Color c=Turtle.getColor(color); + if(c==null)c=penColor; + long timeStamp=storeCurrentState(true, false,penWidth*3,c,null,null,0,null); + return timeStamp; + } + + /** + * Put a dot 3 times the size of the penWidth on the canvas. + * + * @param color color of dot + * @return state change timestamp + */ + public long dot(Color color) + { + long timeStamp=storeCurrentState(true, false,penWidth*3,color,null,null,0,null); + return timeStamp; + } + + /** + * Put a dot on the canvas. + * + * @param color color of dot + * @param dotSize diameter of the dot + * @return state change timestamp + */ + public long dot(String color,double dotSize) + { + Color c=Turtle.getColor(color); + if(c==null)c=penColor; + long timeStamp=storeCurrentState(true, false,dotSize,c,null,null,0,null); + return timeStamp; + } + + /** + * Put a dot on the canvas. + * + * @param color color of dot + * @param dotSize diameter of the dot + * @return state change timestamp + */ + public long dot(Color color,double dotSize) + { + long timeStamp=storeCurrentState(true, false,dotSize,color,null,null,0,null); + return timeStamp; + } + + public long write(String text, String fontName, int fontSize, int justification, double xOffset, double yOffset) + { + return 0; + } + + /** + * Undo turtle state changes. + * + * @param steps the number of state changes to remove + */ + public void undo(int steps) + { + for(int i=0;i copy_turtleStates=(TreeMap)turtleStates.clone(); + for (Map.Entry entry:copy_turtleStates.entrySet()) + { + ArrayList state=entry.getValue(); + long time=entry.getKey(); + if(getStateTurtle(state)==this) + { + if(removeKey!=0) + { + turtleStates.remove(removeKey); + } + removeKey=time; + + } + } + redoStates.clear(); + restoreState(removeKey); + } + lastUpdate=0; + if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); + } + + private void rollback() + { + int steps=0; + + synchronized(turtleLock) + { + long removeKey=0; + long restoreTime=0; + for (Map.Entry entry:turtleStates.descendingMap().entrySet()) + { + ArrayList state=entry.getValue(); + long time=entry.getKey(); + if(getStateTurtle(state)==this) + { + if (steps==0) + { + removeKey=time; + steps+=1; + } + else + { + restoreTime=time; + break; + } + } + } + if (removeKey!=0 && restoreTime!=0) + { + restoreState(restoreTime); + redoStates.put(removeKey, turtleStates.remove(removeKey)); + } + } + } + + private void rollforward() + { + synchronized(turtleLock) + { + for (Map.Entry entry:redoStates.entrySet()) + { + ArrayList state=entry.getValue(); + long time=entry.getKey(); + if(getStateTurtle(state)==this) + { + turtleStates.put(entry.getKey(), redoStates.remove(entry.getKey())); + restoreState(time); + return; + } + } + } + } + + /** + * This specifies when the screen gets refreshed. + * 0(default)=Animated (The turtle will slide from one state to another without being jerky.) + * 1=State Change (The turtle will refresh immediately to the last state. Jerky motion.) + * 2=On Demand (The turtle will refresh only when you call update()) + * + * @param mode refresh mode + */ + public static void refreshMode(int mode) + { + refreshMode=mode; + updateAll(); + } + + /** + * This specifies how the background is drawn. + * 0=The image if present is stretched to fill the screen. + * 1=The image is centered on the middle of the screen and will not scale/pan + * 2=The image is tiled and will not scale/pan + * 3=The image is centered on (0,0) and will scale/pan + * 4(default)=The image is tiled and will scale/pan + * + * @param mode background mode + */ + public static void backgroundMode(int mode) + { + backgroundMode=mode; + updateAll(); + } + + /** + * Sets the background color. + * + * @param color Color of the background. + */ + public static void bgcolor(String color) + { + Color c=Turtle.getColor(color); + if(c!=null)backgroundColor=c; + if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); + } + + /** + * Sets the background color. + * + * @param color Color of the background. + */ + public static void bgcolor(Color color) + { + backgroundColor=color; + if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); + } + + /** + * Set the background image. + * + * @param filename filename for a background image + */ + public static void bgpic(String filename) + { + try{backgroundImage=ImageIO.read(new File(filename));} + catch(Exception e){e.printStackTrace();} + if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); + } + + private static boolean addMouseBinding(String methodName,Turtle t,boolean append,boolean click, boolean repeat) + { + String className=""; + try + { + throw new Exception("Who called me?"); + } + catch( Exception e ) + { + className=e.getStackTrace()[2].getClassName(); + } + try + { + boolean works=false; + for(Method m : Class.forName(className).getDeclaredMethods()) + { + if (m.getName().equals(methodName)) + { + //System.out.println(m); + works=true; + for(Class paramType : m.getParameterTypes()) + { + //System.out.println(paramType.getName()); + if (!paramType.getName().equals("double") && !paramType.getName().equals("java.lang.Double") && !paramType.getName().equals("Turtle")) + { + works=false; + break; + } + } + if (works) break; + } + } + if(works) + { + //System.out.println("Method found!"); + } + else + { + System.out.println("ERROR"); + return false; + } + } + catch(Exception e) + { + System.out.println("Calling Class not found."); + return false; + } + if(!append || !mouseBindings.containsKey(t)) mouseBindings.put(t,new ArrayList()); + ArrayList binding=new ArrayList(); + binding.add(t); + binding.add(className); + binding.add(methodName); + binding.add(click); + binding.add(repeat); + mouseBindings.get(t).add(binding); + return true; + } + + private boolean addKeyBinding(String methodName,String keyText,boolean append,boolean repeat) + { + keyText=keyText.toLowerCase(); + String className=""; + try + { + throw new Exception("Who called me?"); + } + catch( Exception e ) + { + className=e.getStackTrace()[2].getClassName(); + } + try + { + boolean works=false; + for(Method m : Class.forName(className).getDeclaredMethods()) + { + if (m.getName().equals(methodName)) + { + //System.out.println(m); + works=true; + for(Class paramType : m.getParameterTypes()) + { + //System.out.println(paramType.getName()); + if (!paramType.getName().equals("java.lang.String") && !paramType.getName().equals("Turtle")) + { + works=false; + break; + } + } + if (works) break; + } + } + if(works) + { + //System.out.println("Method found!"); + } + else + { + System.out.println("ERROR"); + return false; + } + } + catch(Exception e) + { + System.out.println("Calling Class not found."); + return false; + } + if(!append || !keyBindings.containsKey(keyText)) keyBindings.put(keyText,new ArrayList()); + ArrayList binding=new ArrayList(); + binding.add(this); + binding.add(className); + binding.add(methodName); + binding.add(repeat); + keyBindings.get(keyText).add(binding); + return true; + } + + /** + * Links a method to a key. + * + * @param methodName method to be executed when the key is pressed + * @param keyText key that triggers the method + * @return + */ + public boolean onKey(String methodName,String keyText) + { + return addKeyBinding(methodName,keyText,false,false); + } + + /** + * Links a method to a key. + * + * @param methodName method to be executed when the key is pressed + * @param keyText key that triggers the method + * @param append true if you want to have multiple methods per key + * @return + */ + public boolean onKey(String methodName,String keyText,boolean append) + { + return addKeyBinding(methodName,keyText,append,false); + } + + /** + * Links a method to a key. + * + * @param methodName method to be executed when the key is pressed + * @param keyText key that triggers the method + * @param append true if you want to have multiple methods per key + * @param repeat true if you want call the method every screen refresh + * @return + */ + public boolean onKey(String methodName,String keyText,boolean append,boolean repeat) + { + return addKeyBinding(methodName,keyText,append,repeat); + } + + /** + * + * Fits the indicated box in the center of the screen as large as possible. + * + * @param minx left x coordinate of box + * @param miny bottom y coordinate of box + * @param maxx right x coordinate of box + * @param maxy top y coordinate of box + */ + public static void zoom(double minx, double miny, double maxx, double maxy) + { + synchronized(turtleLock) + { + centerX=(minx+maxx)/2; + centerY=(miny+maxy)/2; + if (width/(maxx-minx)>height/(maxy-miny)) scale=height/(maxy-miny); + else scale=width/(maxx-minx); + updateAll(); + } + } + + /** + * Fits everything on the screen. + */ + public static void zoomFit() + { + synchronized(turtleLock) + { + Point2D.Double loc; + if (turtleStates.isEmpty())return; + else loc=getStateLocation(turtleStates.firstEntry().getValue()); + double minx=loc.x, miny=loc.y; + double maxx=minx, maxy=miny; + double shapeWidth=0; + double shapeHeight=0; + long time=System.nanoTime(); + if(refreshMode!=REFRESH_MODE_ANIMATED)time=turtleStates.lastKey()+1; + for (Map.Entry entry : turtleStates.headMap(time).entrySet()) + { + ArrayList state=entry.getValue(); + if(!getStateIsPenDown(state))continue; + Point2D.Double location =getStateLocation(state); + if (location.xmaxx) maxx=location.x; + if (location.ymaxy) maxy=location.y; + shapeWidth=getStateShapeWidth(state); + shapeHeight=getStateShapeHeight(state); + } + + if(turtleStates.lastKey()>time && getStateSpeed(turtleStates.lastEntry().getValue())>0) + { + double percent = 1 - (turtleStates.lastKey() - time) / getStateSpeed(turtleStates.lastEntry().getValue()) / 1000000.0; + //System.out.println("trying"); + Turtle t=getStateTurtle(turtleStates.lastEntry().getValue()); + double x1=t._location.x,y1=t._location.y,x2=t.__location.x,y2=t.__location.y; + x1=(x2-x1)*percent+x1; + y1=(y2-y1)*percent+y1; + if (x1maxx) maxx=x1; + if (y1maxy) maxy=y1; + } + double shapeMax=Math.max(shapeWidth, shapeHeight); + zoom(minx-shapeMax/2, miny-shapeMax/2, maxx+shapeMax/2, maxy+shapeMax/2); + } + } + + private static void updateAll() + { + lastUpdate=0; + draw(); + } + + /** + * Force redraw when the refreshMode is set to on demand. + */ + public static void update() + { + if(refreshMode==REFRESH_MODE_ON_DEMAND) draw(); + } + + private static void draw() + { + synchronized(turtleLock) + { + + long renderTime=System.nanoTime(); + if (turtleStates.isEmpty() || lastUpdate==0) + { + clearStorage(); + drawBackground(offscreen); + } + if(turtleStates.isEmpty()) + { + onscreen.drawImage(offscreenImage,0,0, null); + window.repaint(); + if(applet!=null)applet.repaint(); + return; + } + if(refreshMode!=REFRESH_MODE_ANIMATED)renderTime=turtleStates.lastKey()+1; + if (lastUpdate>turtleStates.lastKey()) + { + midscreen.drawImage(offscreenImage,0,0, null); + for(Turtle t:turtles) + { + if(t.isVisible) t.drawStamp(1, midscreen); + //if(t==selectedTurtle)t.drawCrossHairs(1,midscreen); + } + onscreen.drawImage(midscreenImage,0,0, null); + window.repaint(); + if(applet!=null)applet.repaint(); + return; + } + for (Map.Entry entry : turtleStates.tailMap(lastUpdate).headMap(renderTime).entrySet()) + { + retrieveState(entry.getKey()); + Turtle t=getStateTurtle(entry.getValue()); + t.drawLine(1,offscreen); + if(t._isStamp)t.drawStamp(1, offscreen); + t.drawDot(1, offscreen); + } + + midscreen.drawImage(offscreenImage,0,0, null); + Turtle animatedTurtle=null; + double percent=1; + Long t2; + t2=Long.valueOf(0); + if (renderTime0) + { + percent = 1 - (turtleStates.ceilingKey(renderTime) - renderTime) / animatedTurtle._speed / 1000000.0; + } + else percent=1; + if (percent<0)percent=0; + } + + for(Turtle t:turtles) + { + if(t==animatedTurtle) + { + //System.out.println(percent); + t.drawLine(percent, midscreen); + if(t._dotSize>0)t.drawDot(percent, midscreen); + if(t.isVisible)t.drawStamp(percent, midscreen,false); + if(t._isStamp)t.drawStamp(percent, midscreen,true); + //if(t==selectedTurtle)t.drawCrossHairs(percent,midscreen); + try{retrieveState(t2);} + catch(Exception e){} + } + else + { + if(t.isVisible)t.drawStamp(1, midscreen); + //if(t==selectedTurtle)t.drawCrossHairs(1,midscreen); + } + + } + lastUpdate=renderTime; + //zoomFit(); + onscreen.drawImage(midscreenImage,0,0, null); + window.repaint(); + if(applet!=null)applet.repaint(); + } + + + } + + private void drawLine(double percent,Graphics2D g) + { + if(!_isPenDown) return; + g.setColor(_penColor); + g.setStroke(new BasicStroke((float)(scale*_penWidth),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); + if(__location!=null && !__location.equals(_location)) + { + double x1=_location.x,y1=_location.y,x2=__location.x,y2=__location.y; + if(percent<1 && percent>=0) + { + x1=(x1-x2)*percent+x2; + y1=(y1-y2)*percent+y2; + } + //g.draw(new Line2D.Double((x1-centerX)*scale+width/2, (y1-centerY)*(-scale)+height/2, (x2-centerX)*scale+width/2, (y2-centerY)*(-scale)+height/2)); + g.drawLine((int)((x1-centerX)*scale+width/2), (int)((y1-centerY)*(-scale)+height/2), (int)((x2-centerX)*scale+width/2), (int)((y2-centerY)*(-scale)+height/2)); + } + } + + private void drawStamp(double percent,Graphics2D g) + { + drawStamp(percent,g,false); + } + + private void drawStamp(double percent,Graphics2D g,boolean isStamp) + { + if (_location==null)return; + AffineTransform originalTransform=(AffineTransform)g.getTransform().clone(); + AffineTransform m = g.getTransform(); + double x1,x2,y1,y2,dir1,dir2,tilt1,tilt2; + x1=_location.x; + y1=_location.y; + dir1=_direction; + tilt1=_tilt; + if(__location==null) + { + x2=x1; + y2=y1; + dir2=dir1; + tilt2=tilt1; + } + else + { + x2=__location.x; + y2=__location.y; + dir2=__direction; + tilt2=__tilt; + } + if(percent<1 && percent>=0) + { + x1=(x1-x2)*percent+x2; + y1=(y1-y2)*percent+y2; + dir1=(dir1-dir2)*percent+dir2; + tilt1=(tilt1-tilt2)*percent+tilt2; + } + m.translate(((x1-centerX)*scale+width/2),((y1-centerY)*(-scale)+height/2)); + if(isStamp)m.scale(scale*percent, scale*percent); + else m.scale(scale, scale); + if (_image==null) + { + //_outlineWidth=0.0; + m.rotate(-Math.toRadians(dir1+tilt1)); + m.scale(_shapeWidth/100.0, _shapeHeight/100.0); + m.translate(-50,-50); + g.setTransform(m); + Polygon p =shapes.get(_shape); + g.setColor(_fillColor); + g.fillPolygon(p); + g.setColor(_outlineColor); + if(_outlineWidth>0) + { + g.setStroke(new BasicStroke((float)(_outlineWidth*scale),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); + g.setTransform(originalTransform); + GeneralPath gp=new GeneralPath(); + gp.append(p.getPathIterator(m),false); + g.draw(gp); + } + } + else + { + int w=_image.getWidth(); + int h=_image.getHeight(); + m.rotate(-Math.toRadians(dir1+tilt1)); + m.scale(_shapeWidth/1.0/w, _shapeHeight/1.0/h); + m.translate(-w/2,-h/2); + g.setTransform(m); + g.drawImage(_image,0,0,null); + } + g.setTransform(originalTransform); + } + + private void drawDot(double percent,Graphics2D g) + { + AffineTransform originalTransform=(AffineTransform)g.getTransform().clone(); + AffineTransform m = g.getTransform(); + m.translate(((_location.x-centerX)*scale+width/2),((_location.y-centerY)*(-scale)+height/2)); + m.scale(scale*percent/2, scale*percent/2); + g.setTransform(m); + g.setColor(_dotColor); + int r=(int)(_dotSize*1.0); + g.fillOval(-r, -r, 2*r, 2*r); + g.setTransform(originalTransform); + } + + private static void drawBackground(Graphics2D g) + { + g.setColor(backgroundColor); + g.fillRect(0, 0, width, height); + if(backgroundImage==null) return; + int w=backgroundImage.getWidth(); + int h=backgroundImage.getHeight(); + if(backgroundMode==BACKGROUND_MODE_CENTER) + { + offscreen.drawImage(backgroundImage, (width-w)/2, (height-h)/2, w, h, null); + } + else if(backgroundMode==BACKGROUND_MODE_STRETCH) + { + offscreen.drawImage(backgroundImage, 0, 0, width, height, null); + } + else if(backgroundMode==BACKGROUND_MODE_CENTER_RELATIVE) + { + offscreen.drawImage(backgroundImage, (int)((-w/2-centerX)*scale+width/2),(int)((h/2-centerY)*(-scale)+height/2), (int)(w*scale), (int)(h*scale), null); + } + else if(backgroundMode==BACKGROUND_MODE_TILE) + { + for(int i=0;i=0) + { + x1=(x1-x2)*percent+x2; + y1=(y1-y2)*percent+y2; + } + m.translate(((x1-centerX)*scale+width/2),((y1-centerY)*(-scale)+height/2)); + int f=10; + m.scale(scale/f, scale/f); + g.setTransform(m); + + int period=50; + int r=(int)(Math.sqrt(shapeWidth*shapeWidth+shapeHeight*shapeHeight)*f/2); + g.setColor(new Color(255,255,255)); + g.setStroke(new BasicStroke((float)(6*f),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); + g.drawOval(-r, -r, 2*r, 2*r); + r+=f; + for(int i=0;i<4;i++) g.drawLine((int)(r*Math.cos(Math.PI/2*i+2*Math.PI*time/period)),(int)(r*Math.sin(Math.PI/2*i+2*Math.PI*time/period)), + (int)((r+r/5)*Math.cos(Math.PI/2*i+2*Math.PI*time/period)),(int)((r+r/5)*Math.sin(Math.PI/2*i+2*Math.PI*time/period))); + r-=f; + g.setColor(new Color(0,0,0)); + g.setStroke(new BasicStroke((float)(3*f),BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND)); + g.drawOval(-r, -r, 2*r, 2*r); + r+=f; + for(int i=0;i<4;i++) g.drawLine((int)(r*Math.cos(Math.PI/2*i+2*Math.PI*time/period)),(int)(r*Math.sin(Math.PI/2*i+2*Math.PI*time/period)), + (int)((r+r/5)*Math.cos(Math.PI/2*i+2*Math.PI*time/period)),(int)((r+r/5)*Math.sin(Math.PI/2*i+2*Math.PI*time/period))); + g.setTransform(originalTransform); + } + + /** + * Changes the size of the canvas effectively changing the size of the window. + * + * @param width width of the canvas + * @param height height of the canvas + */ + public static void setCanvasSize(int width,int height) + { + Turtle.width=width; + Turtle.height=height; + Turtle.setupBuffering(); + window.pack(); + updateAll(); + } + + /** + * Saves the visible canvas to an image. + * + * @param filename image filename + */ + public static void save(String filename) + { + save(new File(filename)); + } + + private static void save(File file) + { + WritableRaster raster = onscreenImage.getRaster(); + WritableRaster newRaster; + newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); + DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel(); + DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), + cm.getRedMask(), + cm.getGreenMask(), + cm.getBlueMask()); + BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); + try + { + String suffix = file.getName().substring(file.getName().lastIndexOf('.') + 1); + if (!ImageIO.write(rgbBuffer, suffix,file)) throw new Exception("Didn't save file."); + } + catch(Exception e) + { + file.delete(); + JOptionPane.showMessageDialog(window, + "Sorry! We can not process your request at this time.", + "Image Save Failed", + JOptionPane.ERROR_MESSAGE); + } + } + + /** + * Demo program + * + * @param a commandline args + */ + public static void main(String[] a) + { + //Turtle bob = new Turtle(); + /*for(int i=0;i<360;i++) + { + bob.forward(i*1.25); + bob.left(90.25); + } + */ + /*If you don't know what a for loop is yet this is equivalent to repeating the middle 4 lines 5 times in a row.*/ + Turtle bob = new Turtle(); + bgcolor("lightblue"); + bob.penColor("red"); + bob.width(10); + for(int i=0;i<200;i++) + { + bob.forward(i/10.); + bob.left(5); + if(i%10==0)bob.dot("orange");//Draws dots when i is a multiple of 10. + } + bob.saveGCODE("test.gcode"); + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void actionPerformed(ActionEvent e) + { + if(((JMenuItem)e.getSource()).getText().equals("Save...")) + { + JFileChooser chooser = new JFileChooser(System.getProperty("user.dir")); + chooser.setFileFilter(new FileNameExtensionFilter("Image (*.jpg, *.jpeg, *.gif, *.bmp, *.png)", "jpg", "png", "jpeg", "bmp", "gif")); + int option = chooser.showSaveDialog(window); + if(option == JFileChooser.APPROVE_OPTION) + { + if(chooser.getSelectedFile()!=null) + { + File file = chooser.getSelectedFile(); + save(file); + } + } + } + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseClicked(MouseEvent e) + { + if(e.getModifiers()==8 && e.getClickCount()==2) + { + centerX=0; + centerY=0; + scale=1; + updateAll(); + } + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseEntered(MouseEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseExited(MouseEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mousePressed(MouseEvent e) + { + dragx=e.getX(); + dragy=e.getY(); + modifiers+=e.getModifiers(); + synchronized (turtleLock) + { + for(Turtle t:turtles) + { + if(t.contains(dragx,dragy))t.select(); + else t.unselect(); + } + } + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseReleased(MouseEvent e) + { + modifiers-=e.getModifiers(); + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseDragged(MouseEvent e) + { + modifiers=e.getModifiers(); + int dx,dy; + if (e.getModifiers()==8) + { + x=e.getX(); + dx=x-dragx; + y=e.getY(); + dy=y-dragy; + dragx=x; + dragy=y; + synchronized(turtleLock) + { + centerX-=dx*1.0/scale; + centerY+=dy*1.0/scale; + } + updateAll(); + } + this.x=e.getX(); + this.y=e.getY(); + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseMoved(MouseEvent e) + { + modifiers=e.getModifiers(); + x=e.getX(); + y=e.getY(); + + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void keyTyped(KeyEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void keyPressed(KeyEvent e) + { + String keyText=KeyEvent.getKeyText(e.getKeyCode()).toLowerCase(); + synchronized (keyLock) + { + keysDown.add(keyText); + if (keyBindings.containsKey(keyText)) + { + unprocessedKeys.add(keyText); + } + } + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void keyReleased(KeyEvent e) + { + String keyText=KeyEvent.getKeyText(e.getKeyCode()).toLowerCase(); + synchronized (keyLock) {keysDown.remove(keyText);processedKeys.remove(keyText);} + } + + private void processKeys() + { + //System.out.println(keysDown); + TreeSet keysDownCopy=new TreeSet(); + synchronized (keyLock) {keysDownCopy=(TreeSet)keysDown.clone();} + keysDownCopy.addAll(unprocessedKeys); + for(String keyText:keysDownCopy) + { + if (keyBindings.containsKey(keyText)) + { + for(ArrayList binding:keyBindings.get(keyText)) + { + Turtle t=(Turtle)binding.get(0); + String className=(String)binding.get(1); + String methodName=(String)binding.get(2); + Boolean repeat=(Boolean)binding.get(3); + if(!repeat && processedKeys.contains(keyText)) break; + unprocessedKeys.remove(keyText); + processedKeys.add(keyText); + try + { + Class cls = Class.forName(className); + Object clsInstance = (Object) cls.newInstance(); + Method m = clsInstance.getClass().getMethod(methodName, t.getClass()); + m.invoke(clsInstance, t); + } + catch(Exception e1) + { + try + { + Class cls = Class.forName(className); + Object clsInstance = (Object) cls.newInstance(); + Method m = clsInstance.getClass().getMethod(methodName, t.getClass(), keyText.getClass()); + m.invoke(clsInstance, t, keyText); + } + catch(Exception e2) + { + try + { + Class cls = Class.forName(className); + Object clsInstance = (Object) cls.newInstance(); + Method m = clsInstance.getClass().getMethod(methodName); + m.invoke(clsInstance); + } + catch(Exception e3) + { + System.out.println("KeyBinding for "+keyText+" has failed."); + e1.printStackTrace(); + e2.printStackTrace(); + e3.printStackTrace(); + } + } + } + } + } + } + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void componentHidden(ComponentEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void componentMoved(ComponentEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void componentResized(ComponentEvent e) + { + width=(int)draw.getBounds().getWidth(); + height=(int)draw.getBounds().getHeight(); + setupBuffering(); + updateAll(); + } + + /** + * Internal mehod for handling events. + * @param e event + */ + public void componentShown(ComponentEvent e) {} + + /** + * Internal mehod for handling events. + * @param e event + */ + public void mouseWheelMoved(MouseWheelEvent e) + { + int notches = e.getWheelRotation(); + double ds=Math.pow(1.1,notches); + x=e.getX(); + y=e.getY(); + double dx=width/2-x; + double dy=height/2-y; + synchronized(turtleLock) + { + centerX-=(dx*ds-dx)/scale/ds; + centerY+=(dy*ds-dy)/scale/ds; + scale*=ds; + } + updateAll(); + } + + /** + * Get the pressed keys. + * + * @return a list of pressed keys + */ + public static String[] keysDown() + { + return keysDown.toArray(new String[]{}); + } + + /** + * Test if a key is pressed or not. + * + * @param key key you are testing + * @return true if the key is pressed + */ + public static boolean isKeyDown(String key) + { + return keysDown.contains(key); + } + + /** + * Get the mouse x coordinate using the screens coordinate system. + * + * @return x coordinate + */ + public static int mouseX() + { + return turtle.x; + } + + /** + * Get the mouse y coordinate using the screens coordinate system. + * + * @return y coordinate + */ + public static int mouseY() + { + return turtle.y; + } + + /** + * Check to see if a mouse button is down. + * + * @return true if a button is down + */ + public static boolean mouseButton() + { + return mouseButton1() || mouseButton2() || mouseButton3(); + } + + /** + * Check to see if the first mouse button is down. + * + * @return true if button 1 is down + */ + public static boolean mouseButton1() + { + return (turtle.modifiers&16)==16; + } + + /** + * Check to see if the second mouse button is down. + * + * @return true if button 2 is down + */ + public static boolean mouseButton2() + { + return (turtle.modifiers&8)==8; + } + + /** + * Check to see if the third mouse button is down. + * + * @return true if button 3 is down + */ + public static boolean mouseButton3() + { + return (turtle.modifiers&4)==4; + } + + /** + * Converts screen coordinates to canvas coordinates. + * + * @param screenX screen x coordinate + * @return canvas x coordinate + */ + public static double canvasX(double screenX) + { + return (screenX-width/2.0)/scale+centerX; + } + + /** + * Converts screen coordinates to canvas coordinates. + * + * @param screenY screen y coordinate + * @return canvas y coordinate + */ + public static double canvasY(double screenY) + { + return (-screenY+height/2.0)/scale+centerY; + } + public static double screenX(double canvasX) + { + return (canvasX-centerX)*scale+width/2.0; + } + public static double screenY(double canvasY) + { + return (canvasY-centerY)*scale+height/2.0; + } + + + private static void saveGCODE(String filename) + { + PrintWriter out=new PrintWriter(System.out); + try + { + out=new PrintWriter(filename); + } + catch(Exception e) + { + + } + out.println("M104 S200"); + out.println("M109 S200"); + out.println("G21"); + out.println("G90"); + out.println("M82"); + out.println("M106"); + out.println("G28 X0 Y0"); + out.println("G28 Z0"); + out.println("G29"); + out.println("G1 Z15.0 F9000"); + out.println("G92 E0"); + out.println("G1 F200 E5"); + out.println("G92 E0"); + out.println("G1 X50 Y50 F1800"); + + double e=0; + synchronized(turtleLock) + { + int i=0; + for (Map.Entry entry : turtleStates.entrySet()) + { + retrieveState(entry.getKey()); + Turtle t=getStateTurtle(entry.getValue()); + i++; + if(i==1)continue; + if(t.__location!=null && !t.__location.equals(t._location)) + { + double x1=t._location.x,y1=t._location.y,x2=t.__location.x,y2=t.__location.y; + double d=Math.hypot(x1-x2,y1-y2); + e+=d*0.05; + //System.out.printf("%f %f %f %f",x1,y1,x2,y2); + if(t._isPenDown) + { + out.printf("G1 X%.4f Y%.4f E%.4f\n",screenX(x1)*1.0/width*100,screenY(y1)*1.0/height*100,e); + } + else + { + + } + + } + } + out.println("G1 Z15"); + out.println("M104 S0"); + out.println("M140 S0"); + out.close(); + } + + + } +} \ No newline at end of file diff --git a/examples/TurtleDemo.java b/examples/TurtleDemo.java new file mode 100755 index 0000000..ea12bb9 --- /dev/null +++ b/examples/TurtleDemo.java @@ -0,0 +1,22 @@ + +//SOURCES Turtle.java + +import java.awt.Color; + +public class TurtleDemo +{ + public static void main(String[] args) + { + Turtle joe = new Turtle(); + joe.penColor(Color.RED); + for(int i = 0; i < 10; i++) + { + for (int j = 0; j < 4; j++) { + joe.forward(150); + joe.right(90); + } + joe.left(36); + } + joe.hide(); + } +} \ No newline at end of file diff --git a/examples/turtle.java b/examples/turtle.java deleted file mode 100755 index be0eff0..0000000 --- a/examples/turtle.java +++ /dev/null @@ -1,46 +0,0 @@ -///usr/bin/env jbang "$0" "$@" ; exit $? - -// Turtle demo using the aplu5 library -// JBang script to run a Java program that uses the Turtle graphics library -// Usage: jbang turtle.java -// Requires: Java 25 or higher - -//DEPS ch.aplu.turtle:aplu5:0.1.9 -//JAVA 25+ - -import ch.aplu.turtle.Turtle; -import ch.aplu.turtle.TurtleFrame; - -// aplu5.jar is not published to Maven Central -// -// To make aplu5.jar available to JBang, install it in -// the local Maven repository by following these steps: -// -// (1 - Download file) $ wget https://www.java-online.ch/download/aplu5.jar -// -// (2 - Install file ) $ mvn install:install-file -Dfile=./aplu5.jar -DgroupId=ch.aplu.turtle -DartifactId=aplu5 -Dversion=0.1.9 -Dpackaging=jar -// - -Turtle joe = null; - -void drawSquare(int sideLength) { - for (int i = 0; i < 4; i++) { - joe.forward(sideLength); - joe.right(90); - } -} - -void drawing() { - joe.setPenColor("red"); - // repeat 10 times - for (int i = 0; i < 10; i++) { - drawSquare(150); - joe.left(36); // rotate 36 degrees for the next square - } -} - -void main() { - TurtleFrame frame = new TurtleFrame("Turtle Demo", 500, 500); - joe = new Turtle(frame); - drawing(); -} \ No newline at end of file From 9efcc5e97410eab5693ea9799bf312f3378ba707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Fri, 15 Aug 2025 19:40:03 +0200 Subject: [PATCH 5/8] fix: reformatted source code --- examples/TurtleDemo.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/TurtleDemo.java b/examples/TurtleDemo.java index ea12bb9..e90d91a 100755 --- a/examples/TurtleDemo.java +++ b/examples/TurtleDemo.java @@ -3,19 +3,16 @@ import java.awt.Color; -public class TurtleDemo -{ - public static void main(String[] args) - { +public class TurtleDemo { + public static void main(String[] args) { Turtle joe = new Turtle(); joe.penColor(Color.RED); - for(int i = 0; i < 10; i++) - { - for (int j = 0; j < 4; j++) { + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 4; j++) { joe.forward(150); joe.right(90); - } - joe.left(36); + } + joe.left(36); } joe.hide(); } From 53703327066754c5609d8a74665d7ac80c2ca540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Fri, 15 Aug 2025 19:47:41 +0200 Subject: [PATCH 6/8] fix: updated first line --- examples/TurtleDemo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/TurtleDemo.java b/examples/TurtleDemo.java index e90d91a..20b4983 100755 --- a/examples/TurtleDemo.java +++ b/examples/TurtleDemo.java @@ -1,3 +1,4 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? //SOURCES Turtle.java @@ -16,4 +17,4 @@ public static void main(String[] args) { } joe.hide(); } -} \ No newline at end of file +} From e36f3041b91cdfdd25c52ab2ec488bf9ce91ee77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Fri, 15 Aug 2025 20:12:27 +0200 Subject: [PATCH 7/8] fix: point turtle north --- examples/TurtleDemo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/TurtleDemo.java b/examples/TurtleDemo.java index 20b4983..af7e8b5 100755 --- a/examples/TurtleDemo.java +++ b/examples/TurtleDemo.java @@ -15,6 +15,6 @@ public static void main(String[] args) { } joe.left(36); } - joe.hide(); + joe.left(90); } } From ae3947cda944f2e354cf65a970d15b97a1084964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Werner=20Fouch=C3=A9?= Date: Sun, 17 Aug 2025 19:01:31 +0200 Subject: [PATCH 8/8] fix: moved files to turtle folder --- examples/{ => turtle}/Turtle.java | 0 examples/{ => turtle}/TurtleDemo.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename examples/{ => turtle}/Turtle.java (100%) rename examples/{ => turtle}/TurtleDemo.java (100%) diff --git a/examples/Turtle.java b/examples/turtle/Turtle.java similarity index 100% rename from examples/Turtle.java rename to examples/turtle/Turtle.java diff --git a/examples/TurtleDemo.java b/examples/turtle/TurtleDemo.java similarity index 100% rename from examples/TurtleDemo.java rename to examples/turtle/TurtleDemo.java