// Modified by Hunter Schafer (July 2022)
/**
* AUTHOR: NICHOLAS SEWARD
* EMAIL: nicholas.seward@gmail.com
* LICENSE: MIT (USE THIS HOWEVER YOU SEE FIT.)
* DATE: 6/21/2012
* VERSION: 2 (THIS SHOULD BE VERSION 0.1 BUT THAT IS A SILLY NUMBERING SYSTEM.)
*
*
* THIS SOFTWARE HAS NO WARRANTY. IF IT WORKS, SUPER. IF IT DOESN'T, LET ME
* KNOW AND I MIGHT OR MIGHT NOT DO SOMETHING ABOUT IT.
*
* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*)
* _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/
* U U U U U U U U U U U U U U U U
*
* Additional contributors to the UW version (2022):
* Hunter Schafer, Leah Perlmutter
*
*/
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.Method;
import java.util.*;
import javax.imageio.ImageIO;
import javax.swing.filechooser.FileNameExtensionFilter;
/**
* About Turtle Graphics
* Turtle is a software library for drawing graphics with simple code, approachable
* to beginners.
*
* This library is one incarnation of Turtle Graphics.
* Turtle Graphics has been implemented in many programming languages over many
* decades, often as a tool to support people in learning to program!
*
* Methods Overview
*
* Here is an overview of many of the methods in this package. If you
* are new to Turtle, you may want to start with the
* methods marked by boldface font and the turtle emoji \ud83d\udc22!
*
* Constructing and Copying Turtles
*
* - \ud83d\udc22 {@link #Turtle()}
* - {@link #Turtle(double, double)}
* - {@link #clone()}
*
* Moving and Turning a Turtle
*
* - \ud83d\udc22 {@link #forward(double)} | {@link #backward(double)}
* - \ud83d\udc22 {@link #left(double)} | {@link #right(double)}
* - \ud83d\udc22 {@link #speed(double)}
* - {@link #home()}
* - {@link #face(double, double)} | {@link #setDirection(double)}
* - {@link #setPosition(double, double)} | {@link #setPosition(double, double, double)}
*
* Manipulating the Pen and Drawing
*
* - \ud83d\udc22 {@link #up()} | {@link #down()}
* - \ud83d\udc22 {@link #width(double)}
* - \ud83d\udc22 {@link #penColor(java.awt.Color)} | {@link #penColor(java.lang.String)}
* - {@link #dot()} | {@link #dot(java.awt.Color)} | {@link #dot(java.awt.Color, double)} | {@link #dot(java.lang.String)} | {@link #dot(java.lang.String, double)}
* - {@link #stamp()}
*
* Console Output
*
* - \ud83d\udc22 {@link #accessiblePrinting(boolean)}
*
* Changing a Turtle's Appearance
*
* - {@link #hide()} | {@link #show()}
* - {@link #fillColor(java.awt.Color)} | {@link #fillColor(java.lang.String)}
* - {@link #outlineColor(java.awt.Color)} | {@link #outlineColor(java.lang.String)}
* - {@link #tilt(double)} | {@link #setTilt(double)}
* - {@link #shape(java.lang.String)} | {@link #shapeSize(int, int)}
*
* Getting a Turtle's State
*
* - {@link #getX()} | {@link #getY()}
* - {@link #getDirection()}
* - {@link #getSpeed()}
* - {@link #getTilt()}
*
* Manipulating and Saving the Canvas
*
* - {@link #clear()}
* - {@link #bgcolor(java.awt.Color)} | {@link #bgcolor(java.lang.String)}
* - {@link #bgpic(java.lang.String)}
* - {@link #setCanvasSize(int, int)}
* - {@link #save(java.lang.String)}
*
* Geometric Computations
*
* - {@link #canvasX(double)} | {@link #canvasY(double)}
* - {@link #screenX(double)} | {@link #screenY(double)}
* - {@link #distance(double, double)}
* - {@link #towards(double, double)}
*
*
* Coordinate Systems
* This section is to clarify the meaning of numeric coordinates.
*
* Canvas Coordinates
* Turtle commands such as forward
, setPosition
,
* and getX
/getY
* are in terms of canvas coordinates.
* Canvas coordinates can be thought of as "model coordinates".
*
* - X axis points rightward.
* - Y axis points upward.
* - 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
* zoom or pan the view of the canvas.
* Screen coordinates can be thought of as "view coordinates".
*
* - X axis points rightward.
* - Y axis points updward.
* - The origin is the lower left hand corner of the turtle window, regardless
* of how much you have zoomed or panned the view.
*
*
*
* @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);
}
lastUpdate = 0;
if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll();
}
private void rollback() {
int steps = 0;
synchronized (turtleLock) {
long removeKey = 0;
long restoreTime = 0;
for (Map.Entry entry : turtleStates.descendingMap().entrySet()) {
ArrayList state = entry.getValue();
long time = entry.getKey();
if (getStateTurtle(state) == this) {
if (steps == 0) {
removeKey = time;
steps += 1;
} else {
restoreTime = time;
break;
}
}
}
if (removeKey != 0 && restoreTime != 0) {
restoreState(restoreTime);
redoStates.put(removeKey, turtleStates.remove(removeKey));
}
}
}
private void rollforward() {
synchronized (turtleLock) {
for (Map.Entry entry : redoStates.entrySet()) {
ArrayList state = entry.getValue();
long time = entry.getKey();
if (getStateTurtle(state) == this) {
turtleStates.put(entry.getKey(), redoStates.remove(entry.getKey()));
restoreState(time);
return;
}
}
}
}
/**
* This specifies when the screen gets refreshed.
* 0(default)=Animated (The turtle will slide from one state to another without being jerky.)
* 1=State Change (The turtle will refresh immediately to the last state. Jerky motion.)
* 2=On Demand (The turtle will refresh only when you call update())
*
* @param mode refresh mode
*/
public static void refreshMode(int mode) {
refreshMode = mode;
updateAll();
}
/**
* This specifies how the background is drawn.
* 0=The image if present is stretched to fill the screen.
* 1=The image is centered on the middle of the screen and will not scale/pan
* 2=The image is tiled and will not scale/pan
* 3=The image is centered on (0,0) and will scale/pan
* 4(default)=The image is tiled and will scale/pan
*
* @param mode background mode
*/
public static void backgroundMode(int mode) {
backgroundMode = mode;
updateAll();
}
/**
* Sets the background color.
*
* @param color Color of the background.
*/
public static void bgcolor(String color) {
Color c = Turtle.getColor(color);
if (c != null) backgroundColor = c;
if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll();
}
/**
* Sets the background color.
*
* @param color Color of the background.
*/
public static void bgcolor(Color color) {
backgroundColor = color;
if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll();
}
/**
* Set the background image.
*
* @param filename filename for a background image
*/
public static void bgpic(String filename) {
try {
backgroundImage = ImageIO.read(new File(filename));
} catch (Exception e) {
e.printStackTrace();
}
if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll();
}
private static boolean addMouseBinding(String methodName, Turtle t, boolean append, boolean click, boolean repeat) {
String className = "";
try {
throw new Exception("Who called me?");
} catch (Exception e) {
className = e.getStackTrace()[2].getClassName();
}
try {
boolean works = false;
for (Method m : Class.forName(className).getDeclaredMethods()) {
if (m.getName().equals(methodName)) {
//System.out.println(m);
works = true;
for (Class paramType : m.getParameterTypes()) {
//System.out.println(paramType.getName());
if (!paramType.getName().equals("double") && !paramType.getName().equals("java.lang.Double") && !paramType.getName().equals("Turtle")) {
works = false;
break;
}
}
if (works) break;
}
}
if (works) {
//System.out.println("Method found!");
} else {
System.out.println("ERROR");
return false;
}
} catch (Exception e) {
System.out.println("Calling Class not found.");
return false;
}
if (!append || !mouseBindings.containsKey(t)) mouseBindings.put(t, new ArrayList());
ArrayList binding = new ArrayList();
binding.add(t);
binding.add(className);
binding.add(methodName);
binding.add(click);
binding.add(repeat);
mouseBindings.get(t).add(binding);
return true;
}
private boolean addKeyBinding(String methodName, String keyText, boolean append, boolean repeat) {
keyText = keyText.toLowerCase();
String className = "";
try {
throw new Exception("Who called me?");
} catch (Exception e) {
className = e.getStackTrace()[2].getClassName();
}
try {
boolean works = false;
for (Method m : Class.forName(className).getDeclaredMethods()) {
if (m.getName().equals(methodName)) {
//System.out.println(m);
works = true;
for (Class paramType : m.getParameterTypes()) {
//System.out.println(paramType.getName());
if (!paramType.getName().equals("java.lang.String") && !paramType.getName().equals("Turtle")) {
works = false;
break;
}
}
if (works) break;
}
}
if (works) {
//System.out.println("Method found!");
} else {
System.out.println("ERROR");
return false;
}
} catch (Exception e) {
System.out.println("Calling Class not found.");
return false;
}
if (!append || !keyBindings.containsKey(keyText)) keyBindings.put(keyText, new ArrayList());
ArrayList binding = new ArrayList();
binding.add(this);
binding.add(className);
binding.add(methodName);
binding.add(repeat);
keyBindings.get(keyText).add(binding);
return true;
}
/**
* Links a method to a key.
*
* @param methodName method to be executed when the key is pressed
* @param keyText key that triggers the method
* @return 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);
}
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;
}
m.translate(((x1 - centerX) * scale + width / 2), ((y1 - centerY) * (-scale) + height / 2));
if (isStamp) m.scale(scale * percent, scale * percent);
else m.scale(scale, scale);
if (_image == null) {
//_outlineWidth=0.0;
m.rotate(-Math.toRadians(dir1 + tilt1));
m.scale(_shapeWidth / 100.0, _shapeHeight / 100.0);
m.translate(-50, -50);
g.setTransform(m);
Polygon p = shapes.get(_shape);
g.setColor(_fillColor);
g.fillPolygon(p);
g.setColor(_outlineColor);
if (_outlineWidth > 0) {
g.setStroke(new BasicStroke((float) (_outlineWidth * scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
g.setTransform(originalTransform);
GeneralPath gp = new GeneralPath();
gp.append(p.getPathIterator(m), false);
g.draw(gp);
}
} else {
int w = _image.getWidth();
int h = _image.getHeight();
m.rotate(-Math.toRadians(dir1 + tilt1));
m.scale(_shapeWidth / 1.0 / w, _shapeHeight / 1.0 / h);
m.translate(-w / 2, -h / 2);
g.setTransform(m);
g.drawImage(_image, 0, 0, null);
}
g.setTransform(originalTransform);
}
private void drawDot(double percent, Graphics2D g) {
AffineTransform originalTransform = (AffineTransform) g.getTransform().clone();
AffineTransform m = g.getTransform();
m.translate(((_location.x - centerX) * scale + width / 2), ((_location.y - centerY) * (-scale) + height / 2));
m.scale(scale * percent / 2, scale * percent / 2);
g.setTransform(m);
g.setColor(_dotColor);
int r = (int) (_dotSize * 1.0);
g.fillOval(-r, -r, 2 * r, 2 * r);
g.setTransform(originalTransform);
}
private static void drawBackground(Graphics2D g) {
g.setColor(backgroundColor);
g.fillRect(0, 0, width, height);
if (backgroundImage == null) return;
int w = backgroundImage.getWidth();
int h = backgroundImage.getHeight();
if (backgroundMode == BACKGROUND_MODE_CENTER) {
offscreen.drawImage(backgroundImage, (width - w) / 2, (height - h) / 2, w, h, null);
} else if (backgroundMode == BACKGROUND_MODE_STRETCH) {
offscreen.drawImage(backgroundImage, 0, 0, width, height, null);
} else if (backgroundMode == BACKGROUND_MODE_CENTER_RELATIVE) {
offscreen.drawImage(backgroundImage, (int) ((-w / 2 - centerX) * scale + width / 2), (int) ((h / 2 - centerY) * (-scale) + height / 2), (int) (w * scale), (int) (h * scale), null);
} else if (backgroundMode == BACKGROUND_MODE_TILE) {
for (int i = 0; i < 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();
}
}
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void mouseReleased(MouseEvent e) {
modifiers -= e.getModifiers();
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void mouseDragged(MouseEvent e) {
modifiers = e.getModifiers();
int dx, dy;
if (e.getModifiers() == 8) {
x = e.getX();
dx = x - dragx;
y = e.getY();
dy = y - dragy;
dragx = x;
dragy = y;
synchronized (turtleLock) {
centerX -= dx * 1.0 / scale;
centerY += dy * 1.0 / scale;
}
updateAll();
}
this.x = e.getX();
this.y = e.getY();
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void mouseMoved(MouseEvent e) {
modifiers = e.getModifiers();
x = e.getX();
y = e.getY();
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void keyTyped(KeyEvent e) {
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void keyPressed(KeyEvent e) {
String keyText = KeyEvent.getKeyText(e.getKeyCode()).toLowerCase();
synchronized (keyLock) {
keysDown.add(keyText);
if (keyBindings.containsKey(keyText)) {
unprocessedKeys.add(keyText);
}
}
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void keyReleased(KeyEvent e) {
String keyText = KeyEvent.getKeyText(e.getKeyCode()).toLowerCase();
synchronized (keyLock) {
keysDown.remove(keyText);
processedKeys.remove(keyText);
}
}
private void processKeys() {
//System.out.println(keysDown);
TreeSet keysDownCopy = new TreeSet();
synchronized (keyLock) {
keysDownCopy = (TreeSet) keysDown.clone();
}
keysDownCopy.addAll(unprocessedKeys);
for (String keyText : keysDownCopy) {
if (keyBindings.containsKey(keyText)) {
for (ArrayList binding : keyBindings.get(keyText)) {
Turtle t = (Turtle) binding.get(0);
String className = (String) binding.get(1);
String methodName = (String) binding.get(2);
Boolean repeat = (Boolean) binding.get(3);
if (!repeat && processedKeys.contains(keyText)) break;
unprocessedKeys.remove(keyText);
processedKeys.add(keyText);
try {
Class cls = Class.forName(className);
Object clsInstance = (Object) cls.newInstance();
Method m = clsInstance.getClass().getMethod(methodName, t.getClass());
m.invoke(clsInstance, t);
} catch (Exception e1) {
try {
Class cls = Class.forName(className);
Object clsInstance = (Object) cls.newInstance();
Method m = clsInstance.getClass().getMethod(methodName, t.getClass(), keyText.getClass());
m.invoke(clsInstance, t, keyText);
} catch (Exception e2) {
try {
Class cls = Class.forName(className);
Object clsInstance = (Object) cls.newInstance();
Method m = clsInstance.getClass().getMethod(methodName);
m.invoke(clsInstance);
} catch (Exception e3) {
System.out.println("KeyBinding for " + keyText + " has failed.");
e1.printStackTrace();
e2.printStackTrace();
e3.printStackTrace();
}
}
}
}
}
}
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void componentHidden(ComponentEvent e) {
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void componentMoved(ComponentEvent e) {
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void componentResized(ComponentEvent e) {
width = (int) draw.getBounds().getWidth();
height = (int) draw.getBounds().getHeight();
setupBuffering();
updateAll();
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void componentShown(ComponentEvent e) {
}
/**
* Internal mehod for handling events.
* @param e event
*/
public void mouseWheelMoved(MouseWheelEvent e) {
int notches = e.getWheelRotation();
double ds = Math.pow(1.1, notches);
x = e.getX();
y = e.getY();
double dx = width / 2 - x;
double dy = height / 2 - y;
synchronized (turtleLock) {
centerX -= (dx * ds - dx) / scale / ds;
centerY += (dy * ds - dy) / scale / ds;
scale *= ds;
}
updateAll();
}
/**
* Get the pressed keys.
*
* @return a list of pressed keys
*/
public static String[] keysDown() {
return keysDown.toArray(new String[]{});
}
/**
* Test if a key is pressed or not.
*
* @param key key you are testing
* @return true if the key is pressed
*/
public static boolean isKeyDown(String key) {
return keysDown.contains(key);
}
/**
* Get the mouse x coordinate using the screens coordinate system.
*
* @return x coordinate
*/
public static int mouseX() {
return turtle.x;
}
/**
* Get the mouse y coordinate using the screens coordinate system.
*
* @return y coordinate
*/
public static int mouseY() {
return turtle.y;
}
/**
* Check to see if a mouse button is down.
*
* @return true if a button is down
*/
public static boolean mouseButton() {
return mouseButton1() || mouseButton2() || mouseButton3();
}
/**
* Check to see if the first mouse button is down.
*
* @return true if button 1 is down
*/
public static boolean mouseButton1() {
return (turtle.modifiers & 16) == 16;
}
/**
* Check to see if the second mouse button is down.
*
* @return true if button 2 is down
*/
public static boolean mouseButton2() {
return (turtle.modifiers & 8) == 8;
}
/**
* Check to see if the third mouse button is down.
*
* @return true if button 3 is down
*/
public static boolean mouseButton3() {
return (turtle.modifiers & 4) == 4;
}
/**
* Converts screen coordinates to canvas coordinates.
*
* @param screenX screen x coordinate
* @return canvas x coordinate
*/
public static double canvasX(double screenX) {
return (screenX - width / 2.0) / scale + centerX;
}
/**
* Converts screen coordinates to canvas coordinates.
*
* @param screenY screen y coordinate
* @return canvas y coordinate
*/
public static double canvasY(double screenY) {
return (-screenY + height / 2.0) / scale + centerY;
}
public static double screenX(double canvasX) {
return (canvasX - centerX) * scale + width / 2.0;
}
public static double screenY(double canvasY) {
return (canvasY - centerY) * scale + height / 2.0;
}
private static void saveGCODE(String filename) {
PrintWriter out = new PrintWriter(System.out);
try {
out = new PrintWriter(filename);
} catch (Exception e) {
}
out.println("M104 S200");
out.println("M109 S200");
out.println("G21");
out.println("G90");
out.println("M82");
out.println("M106");
out.println("G28 X0 Y0");
out.println("G28 Z0");
out.println("G29");
out.println("G1 Z15.0 F9000");
out.println("G92 E0");
out.println("G1 F200 E5");
out.println("G92 E0");
out.println("G1 X50 Y50 F1800");
double e = 0;
synchronized (turtleLock) {
int i = 0;
for (Map.Entry entry : turtleStates.entrySet()) {
retrieveState(entry.getKey());
Turtle t = getStateTurtle(entry.getValue());
i++;
if (i == 1) continue;
if (t.__location != null && !t.__location.equals(t._location)) {
double x1 = t._location.x, y1 = t._location.y, x2 = t.__location.x, y2 = t.__location.y;
double d = Math.hypot(x1 - x2, y1 - y2);
e += d * 0.05;
//System.out.printf("%f %f %f %f",x1,y1,x2,y2);
if (t._isPenDown) {
out.printf("G1 X%.4f Y%.4f E%.4f\n", screenX(x1) * 1.0 / width * 100, screenY(y1) * 1.0 / height * 100, e);
} else {
}
}
}
out.println("G1 Z15");
out.println("M104 S0");
out.println("M140 S0");
out.close();
}
}
}