diff --git a/projects/Turtle/Turtle.java b/projects/Turtle/Turtle.java index 977ff9b..364c0e1 100644 --- a/projects/Turtle/Turtle.java +++ b/projects/Turtle/Turtle.java @@ -20,15 +20,15 @@ *

*/ -import java.awt.event.*; -import javax.swing.*; import java.awt.*; +import java.awt.event.*; 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.*; import javax.swing.filechooser.FileNameExtensionFilter; /** @@ -105,7 +105,7 @@ * * *

Coordinate Systems

- * This section is to clarify the meaning of numeric coordinates. + * This section is to clarify the meaning of numeric coordinates. * *

Canvas Coordinates

* Turtle commands such as forward, setPosition, @@ -118,9 +118,9 @@ *
  • When the turtle window first loads, the origin is in the center of the * turtle window.
  • * - * + * *

    Screen Coordinates

    - * Screen coordinates change relative to the turtle's world as you + * Screen coordinates change relative to the turtle's world as you * zoom or pan the view of the canvas. * Screen coordinates can be thought of as "view coordinates". * - * + * * * @author Nicholas Seward */ -@SuppressWarnings({"unchecked", "deprecation", "removal"}) -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; - - // Output if accessibility mode is turned on - private static PrintStream out; - - 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 < time) { - try { - Thread.sleep((time - now) / 1000000); - } catch (Exception e) { - } - return true; - } else return false; - } - - private static void init() { - //mouseBindings.put(null, new ArrayList()); - 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(); - - //! TODO: Fix this :) - try { - Thread.sleep(5000); - } catch (Exception e) { - } - - 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(); - - // Printing for accessibility default is off - accessiblePrinting(false); - -// 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(); - } - - - - /** - * Turn accessible printing on or off. - * When accessible printing is turned on, every turtle command that could result - * in a visual graphical upate will also be printed to the console as text. - * - * @param printToConsole true to turn on accessible printing, or - * false to turn off accessible printing - */ - public static void accessiblePrinting(boolean printToConsole) { - if (printToConsole) { - if (out != System.out) { - out = System.out; - out.println("Printing instructions and position information," - + " with position information in two dimensional coordinates"); - } - } else { - out = new PrintStream(new OutputStream() { - public void write(int b) { - //DO NOTHING - } - }); - } - } - - 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 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 < divisions; i++) { - double angle = Math.toRadians(i * 360.0 / divisions); - xs[i] = (int) Math.round(50 + 50 * Math.cos(angle)); - ys[i] = (int) Math.round(50 + 50 * Math.sin(angle)); - } - p = new Polygon(xs, ys, xs.length); - shapes.put("circle", p); - } - - /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) - * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ - * U U U U U U U U U U U U U U U U - * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) - * _/___\/ _/___\/ _/___\/ TURTLE CONSTRUCTION_/___\/ _/___\/ _/___\/ - * 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 - */ - - /** - * This is a internal constuctor that makes a singleton that does the - * listening but is not added to the stack of turtles to be rendered. - * You don't need to use this outside of the Turtle.java file. - * - * @param i Pass this any integer. It doesn't do anything. - */ - private Turtle(int i) { - } - - private Point2D.Double location = new Point2D.Double(0, 0); - private double direction = 0; //degrees - private String shape = "turtle"; //Stores a key to the shapes hashmap - private BufferedImage image = null; - private double shapeWidth = 33; - private double shapeHeight = 33; - private double tilt = 0; - private double penWidth = 2; - private Color penColor = Color.BLACK; - private double outlineWidth = 2; - private Color outlineColor = Color.BLACK; - private Color fillColor = new Color(0, 255, 0, 128); - private double speed = 50; //milliseconds to execute a move - private boolean isPenDown = true; - private boolean isFilling = false; - private boolean isVisible = true; - private ArrayList 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 turtle at the center of the canvas at location (0, 0). - *
    -     * Turtle t = new Turtle();
    -     * 
    - */ - public Turtle() { - if (window == null) init(); - synchronized (turtleLock) { - turtles.add(this); - } - long time = storeCurrentState(); - updateAll(); - } - - /** - * Makes a 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; - } - - private void output(String message) { - out.println("Instruction: " + message); - out.printf("Current Pos: (%.3f, %.3f)\n", location.getX(), location.getY()); - } - - /** - * 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. - * - *
    -     * Turtle t = new Turtle();
    -     * t.speed(1000);
    -     * t.forward(100); // takes 1000 ms (1 second) to move forward 100 units
    -     * t.forward(5); // takes 1000 ms (1 second) to move forward 5 units
    -     * t.speed(100);
    -     * t.left(90); // takes 100 ms (0.1 second) to turn left 90 degrees
    -     * 
    - * - * @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 given distance. Each unit of distance - * is one pixel at the default zoom level. - *

    - * The example below constructs a turtle and moves it forward. - * If the canvas is zoomed to the default zoom level, then the - * distance moved is 50 pixels. - *

    - *
    -     * Turtle t = new Turtle();
    -     * t.forward(50);
    -     * 
    - * - * @param distance number of units 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(); - - output("MOVE FORWARD " + distance); - return timeStamp; - } - - /** - * Moves the turtle backward by the given distance. Each unit of distance is one - * pixel at the default zoom level. - * - * @param distance number of units to move forward - * @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(); - - output("MOVE BACKWARD " + distance); - 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(); - - output("TURN LEFT " + angle); - 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(); - - output("TURN RIGHT " + angle); - 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(); - - output("SET DIRECTION " + a); - return timeStamp; - } - - /** - * Moves the turtle to (0,0) and facing east. - * - * @return state change timestamp - */ - public long home() { - output("MOVE HOME"); - return setPosition(0, 0, 0); - } - - /** - * Hides the turtle but it can still draw. - * - * @return state change timestamp - */ - public long hide() { - isVisible = false; - output("HIDING TURTLE"); - long timeStamp = storeCurrentState(); - return timeStamp; - } - - /** - * Makes the turtle visible. - * - * @return state change timestamp - */ - public long show() { - isVisible = true; - output("SHOWING TURTLE"); - 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 < - */ - 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; - - output(String.format("SET POSITION (%f, %f) DIRECTION %f", x, y, 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; - - output("ADD 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; - - output("SET 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 turtle's pen. Each unit of width corresponds to - * 1 pixel at the default zoom level. - * - * - * @param penWidth number of units wide - * @return state change timestamp - */ - public long width(double penWidth) { - this.penWidth = penWidth; - - output("SET PEN WIDTH " + 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; - - output("SET OUTLINE WIDTH " + width); - long timeStamp = storeCurrentState(); - return timeStamp; - } - - /** - * Picks the turtle's pen up so it won't draw on the screen as it moves. - * - * @return state change timestamp - */ - public long up() { - this.isPenDown = false; - output("LIFT PEN"); - long timeStamp = storeCurrentState(); - return timeStamp; - } - - /** - * Puts the turtle's pen down so it will draw on the screen as it moves. - * - * @return state change timestamp - */ - public long down() { - this.isPenDown = true; - output("LOWER PEN"); - long timeStamp = storeCurrentState(); - return timeStamp; - } - - public long stab() { - Color c = Turtle.getColor("red"); - if (c != null) this.penColor = c; - this.isPenDown = true; - - output("STAB"); - 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; - output("SET PEN COLOR " + 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; - - output("SET PEN COLOR " + 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; - - output("SET OUTLINE COLOR " + 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; - output("SET OUTLINE COLOR " + 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; - - output("SET FILL COLOR " + 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; - - output("SET FILL COLOR " + 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)); - output("SET SHAPE " + shape); - this.shapeHeight = image.getHeight(); - this.shapeWidth = image.getWidth(); - } catch (Exception e) { - if (shapes.containsKey(shape)) { - output("SET SHAPE " + 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; - output(String.format("SET SHAPE SIZE (width=%d, height=%d)", width, height)); - 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); - output("LEAVING STAMP"); - 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); - output("LEAVING DOT"); - 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; - output("LEAVING DOT WITH COLOR " + c); - 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) { - output("LEAVING DOT WITH 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; - - output("LEAVING DOT WITH COLOR " + c + " DOT SIZE " + dotSize); - 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) { - output("LEAVING DOT WITH COLOR " + color + " DOT SIZE " + 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 < steps; i++) rollback(); - lastUpdate = 0; - - output("UNDOING LAST " + steps + " STEPS"); - if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); - } - - /** - * Undo the last turtle state change. - */ - public void undo() { - output("UNDO LAST STEP"); - rollback(); - lastUpdate = 0; - if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); - } - - /** - * Redo turtle state changes. - * - * @param steps the number of state changes to restore - */ - public void redo(int steps) { - output("REDO " + steps + " STEPS"); - for (int i = 0; i < steps; i++) rollforward(); - lastUpdate = 0; - if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); - } - - /** - * Redo turtle state changes. - */ - public void redo() { - output("REDO STEP"); - rollforward(); - lastUpdate = 0; - if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); - } - - /** - * Clears all the drawing that a turtle has done but all the turtle - * settings remain the same. (color, location, direction, shape) - */ - public void clear() { - output("CLEAR DRAWING"); - synchronized (turtleLock) { - long removeKey = 0; - TreeMap 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); +@SuppressWarnings({ "unchecked", "deprecation", "removal" }) +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; + + // Output if accessibility mode is turned on + private static PrintStream out; + + 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 < time) { + try { + Thread.sleep((time - now) / 1000000); + } catch (Exception e) {} + return true; + } else return false; + } + + private static void init() { + //mouseBindings.put(null, new ArrayList()); + 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(); + + //! TODO: Fix this :) + try { + Thread.sleep(5000); + } catch (Exception e) {} + + 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(); + + // Printing for accessibility default is off + accessiblePrinting(false); + + // 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(); + } + + /** + * Turn accessible printing on or off. + * When accessible printing is turned on, every turtle command that could result + * in a visual graphical upate will also be printed to the console as text. + * + * @param printToConsole true to turn on accessible printing, or + * false to turn off accessible printing + */ + public static void accessiblePrinting(boolean printToConsole) { + if (printToConsole) { + if (out != System.out) { + out = System.out; + out.println( + "Printing instructions and position information," + + " with position information in two dimensional coordinates" + ); + } + } else { + out = new PrintStream( + new OutputStream() { + public void write(int b) { + //DO NOTHING + } } - 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)); - } + ); + } + } + + 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 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 < divisions; i++) { + double angle = Math.toRadians((i * 360.0) / divisions); + xs[i] = (int) Math.round(50 + 50 * Math.cos(angle)); + ys[i] = (int) Math.round(50 + 50 * Math.sin(angle)); + } + p = new Polygon(xs, ys, xs.length); + shapes.put("circle", p); + } + + /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ + * U U U U U U U U U U U U U U U U + * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) + * _/___\/ _/___\/ _/___\/ TURTLE CONSTRUCTION_/___\/ _/___\/ _/___\/ + * 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 + */ + + /** + * This is a internal constuctor that makes a singleton that does the + * listening but is not added to the stack of turtles to be rendered. + * You don't need to use this outside of the Turtle.java file. + * + * @param i Pass this any integer. It doesn't do anything. + */ + private Turtle(int i) {} + + private Point2D.Double location = new Point2D.Double(0, 0); + private double direction = 0; //degrees + private String shape = "turtle"; //Stores a key to the shapes hashmap + private BufferedImage image = null; + private double shapeWidth = 33; + private double shapeHeight = 33; + private double tilt = 0; + private double penWidth = 2; + private Color penColor = Color.BLACK; + private double outlineWidth = 2; + private Color outlineColor = Color.BLACK; + private Color fillColor = new Color(0, 255, 0, 128); + private double speed = 50; //milliseconds to execute a move + private boolean isPenDown = true; + private boolean isFilling = false; + private boolean isVisible = true; + private ArrayList 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 turtle at the center of the canvas at location (0, 0). + *
    +   * Turtle t = new Turtle();
    +   * 
    + */ + public Turtle() { + if (window == null) init(); + synchronized (turtleLock) { + turtles.add(this); + } + long time = storeCurrentState(); + updateAll(); + } + + /** + * Makes a 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; + } + + private void output(String message) { + out.println("Instruction: " + message); + out.printf("Current Pos: (%.3f, %.3f)\n", location.getX(), location.getY()); + } + + /** + * 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. + * + *
    +   * Turtle t = new Turtle();
    +   * t.speed(1000);
    +   * t.forward(100); // takes 1000 ms (1 second) to move forward 100 units
    +   * t.forward(5); // takes 1000 ms (1 second) to move forward 5 units
    +   * t.speed(100);
    +   * t.left(90); // takes 100 ms (0.1 second) to turn left 90 degrees
    +   * 
    + * + * @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 given distance. Each unit of distance + * is one pixel at the default zoom level. + *

    + * The example below constructs a turtle and moves it forward. + * If the canvas is zoomed to the default zoom level, then the + * distance moved is 50 pixels. + *

    + *
    +   * Turtle t = new Turtle();
    +   * t.forward(50);
    +   * 
    + * + * @param distance number of units 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(); + + output("MOVE FORWARD " + distance); + return timeStamp; + } + + /** + * Moves the turtle backward by the given distance. Each unit of distance is one + * pixel at the default zoom level. + * + * @param distance number of units to move forward + * @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(); + + output("MOVE BACKWARD " + distance); + 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(); + + output("TURN LEFT " + angle); + 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(); + + output("TURN RIGHT " + angle); + 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(); + + output("SET DIRECTION " + a); + return timeStamp; + } + + /** + * Moves the turtle to (0,0) and facing east. + * + * @return state change timestamp + */ + public long home() { + output("MOVE HOME"); + return setPosition(0, 0, 0); + } + + /** + * Hides the turtle but it can still draw. + * + * @return state change timestamp + */ + public long hide() { + isVisible = false; + output("HIDING TURTLE"); + long timeStamp = storeCurrentState(); + return timeStamp; + } + + /** + * Makes the turtle visible. + * + * @return state change timestamp + */ + public long show() { + isVisible = true; + output("SHOWING TURTLE"); + 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 < + */ + 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; + + output( + String.format("SET POSITION (%f, %f) DIRECTION %f", x, y, 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; + + output("ADD 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; + + output("SET 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 turtle's pen. Each unit of width corresponds to + * 1 pixel at the default zoom level. + * + * + * @param penWidth number of units wide + * @return state change timestamp + */ + public long width(double penWidth) { + this.penWidth = penWidth; + + output("SET PEN WIDTH " + 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; + + output("SET OUTLINE WIDTH " + width); + long timeStamp = storeCurrentState(); + return timeStamp; + } + + /** + * Picks the turtle's pen up so it won't draw on the screen as it moves. + * + * @return state change timestamp + */ + public long up() { + this.isPenDown = false; + output("LIFT PEN"); + long timeStamp = storeCurrentState(); + return timeStamp; + } + + /** + * Puts the turtle's pen down so it will draw on the screen as it moves. + * + * @return state change timestamp + */ + public long down() { + this.isPenDown = true; + output("LOWER PEN"); + long timeStamp = storeCurrentState(); + return timeStamp; + } + + public long stab() { + Color c = Turtle.getColor("red"); + if (c != null) this.penColor = c; + this.isPenDown = true; + + output("STAB"); + 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; + output("SET PEN COLOR " + 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; + + output("SET PEN COLOR " + 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; + + output("SET OUTLINE COLOR " + 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; + output("SET OUTLINE COLOR " + 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; + + output("SET FILL COLOR " + 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; + + output("SET FILL COLOR " + 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)); + output("SET SHAPE " + shape); + this.shapeHeight = image.getHeight(); + this.shapeWidth = image.getWidth(); + } catch (Exception e) { + if (shapes.containsKey(shape)) { + output("SET SHAPE " + 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; + output( + String.format("SET SHAPE SIZE (width=%d, height=%d)", width, height) + ); + 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 + ); + output("LEAVING STAMP"); + 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 + ); + output("LEAVING DOT"); + 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; + output("LEAVING DOT WITH COLOR " + c); + 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) { + output("LEAVING DOT WITH 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; + + output("LEAVING DOT WITH COLOR " + c + " DOT SIZE " + dotSize); + 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) { + output("LEAVING DOT WITH COLOR " + color + " DOT SIZE " + 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 < steps; i++) rollback(); + lastUpdate = 0; + + output("UNDOING LAST " + steps + " STEPS"); + if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); + } + + /** + * Undo the last turtle state change. + */ + public void undo() { + output("UNDO LAST STEP"); + rollback(); + lastUpdate = 0; + if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); + } + + /** + * Redo turtle state changes. + * + * @param steps the number of state changes to restore + */ + public void redo(int steps) { + output("REDO " + steps + " STEPS"); + for (int i = 0; i < steps; i++) rollforward(); + lastUpdate = 0; + if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); + } + + /** + * Redo turtle state changes. + */ + public void redo() { + output("REDO STEP"); + rollforward(); + lastUpdate = 0; + if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); + } + + /** + * Clears all the drawing that a turtle has done but all the turtle + * settings remain the same. (color, location, direction, shape) + */ + public void clear() { + output("CLEAR DRAWING"); + synchronized (turtleLock) { + long removeKey = 0; + TreeMap copy_turtleStates = (TreeMap< + Long, + ArrayList + >) 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; } - } - - 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; - } - } + } + 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; + } } - } - - /** - * 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 (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; } - 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 boolean - */ - 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 boolean - */ - 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 boolean - */ - 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.x < minx) minx = location.x; - if (location.x > maxx) maxx = location.x; - if (location.y < miny) miny = location.y; - if (location.y > maxy) maxy = location.y; - shapeWidth = getStateShapeWidth(state); - shapeHeight = getStateShapeHeight(state); + } + } + } + + /** + * 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 (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 (x1 < minx) minx = x1; - if (x1 > maxx) maxx = x1; - if (y1 < miny) miny = y1; - if (y1 > maxy) 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(); - } - - // Hunter: Made public for testing - public 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 (renderTime < turtleStates.lastKey()) { - animatedTurtle = getStateTurtle(turtleStates.ceilingEntry(renderTime).getValue()); - t2 = animatedTurtle._time; - retrieveState(turtleStates.ceilingKey(renderTime)); - if (animatedTurtle._speed > 0) { - 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; + } + if (works) break; } - 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); + } + 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; } - } 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); + } + if (works) break; } - 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 < width; i += w) - for (int j = 0; j < height; j += h) offscreen.drawImage(backgroundImage, i, j, w, h, null); - } else if (backgroundMode == BACKGROUND_MODE_TILE_RELATIVE) { - double left = centerX - width / 2 / scale; - double top = centerY + height / 2 / scale; - double right = centerX + width / 2 / scale; - double bottom = centerY - height / 2 / scale; - for (double x = ((int) (left / w) - 1) * w; x <= right; x += w) - for (double y = ((int) (bottom / h)) * h; y <= top + h; y += h) - offscreen.drawImage(backgroundImage, (int) ((x - centerX) * scale + width / 2), (int) ((y - centerY) * (-scale) + height / 2), (int) Math.ceil(w * scale), (int) Math.ceil(h * scale), null); + } + 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 boolean + */ + 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 boolean + */ + 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 boolean + */ + 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.x < minx) minx = location.x; + if (location.x > maxx) maxx = location.x; + if (location.y < miny) miny = location.y; + if (location.y > maxy) 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 (x1 < minx) minx = x1; + if (x1 > maxx) maxx = x1; + if (y1 < miny) miny = y1; + if (y1 > maxy) 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(); + } + + // Hunter: Made public for testing + public 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); } - } - - private void drawCrossHairs(double percent, Graphics2D g) { - if (_location == null) return; - double time = (System.nanoTime() / 100000000) / 10.0; - - AffineTransform originalTransform = (AffineTransform) g.getTransform().clone(); - AffineTransform m = g.getTransform(); - double x1, x2, y1, y2, dir1, dir2; - x1 = _location.x; - y1 = _location.y; - if (__location == null) { - x2 = x1; - y2 = y1; + 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 (renderTime < turtleStates.lastKey()) { + animatedTurtle = getStateTurtle( + turtleStates.ceilingEntry(renderTime).getValue() + ); + t2 = animatedTurtle._time; + retrieveState(turtleStates.ceilingKey(renderTime)); + if (animatedTurtle._speed > 0) { + 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 { - x2 = __location.x; - y2 = __location.y; + if (t.isVisible) t.drawStamp(1, midscreen); + //if(t==selectedTurtle)t.drawCrossHairs(1,midscreen); } - if (percent < 1 && percent >= 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))); + } + 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); - } - - /** - * 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++) + 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 < width; i += w) for ( + int j = 0; + j < height; + j += h + ) offscreen.drawImage(backgroundImage, i, j, w, h, null); + } else if (backgroundMode == BACKGROUND_MODE_TILE_RELATIVE) { + double left = centerX - width / 2 / scale; + double top = centerY + height / 2 / scale; + double right = centerX + width / 2 / scale; + double bottom = centerY - height / 2 / scale; + for (double x = ((int) (left / w) - 1) * w; x <= right; x += w) for ( + double y = ((int) (bottom / h)) * h; + y <= top + h; + y += h + ) offscreen.drawImage( + backgroundImage, + (int) ((x - centerX) * scale + width / 2), + (int) ((y - centerY) * (-scale) + height / 2), + (int) Math.ceil(w * scale), + (int) Math.ceil(h * scale), + null + ); + } + } + + private void drawCrossHairs(double percent, Graphics2D g) { + if (_location == null) return; + double time = (System.nanoTime() / 100000000) / 10.0; + + AffineTransform originalTransform = (AffineTransform) g + .getTransform() + .clone(); + AffineTransform m = g.getTransform(); + double x1, x2, y1, y2, dir1, dir2; + x1 = _location.x; + y1 = _location.y; + if (__location == null) { + x2 = x1; + y2 = y1; + } else { + x2 = __location.x; + y2 = __location.y; + } + if (percent < 1 && percent >= 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(); - } + /*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 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 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; + } + } + } + + /** + * 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 {} } - 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 + } + out.println("G1 Z15"); + out.println("M104 S0"); + out.println("M140 S0"); + out.close(); + } + } +}