001// Modified by Hunter Schafer (July 2022) 002 003/** 004 * AUTHOR: NICHOLAS SEWARD 005 * EMAIL: nicholas.seward@gmail.com 006 * LICENSE: MIT (USE THIS HOWEVER YOU SEE FIT.) 007 * DATE: 6/21/2012 008 * VERSION: 2 (THIS SHOULD BE VERSION 0.1 BUT THAT IS A SILLY NUMBERING SYSTEM.) 009 * <p> 010 * <p> 011 * THIS SOFTWARE HAS NO WARRANTY. IF IT WORKS, SUPER. IF IT DOESN'T, LET ME 012 * KNOW AND I MIGHT OR MIGHT NOT DO SOMETHING ABOUT IT. 013 * <p> 014 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 015 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 016 * U U U U U U U U U U U U U U U U 017 * <p> 018 * Additional contributors to the UW version (2022): 019 * Hunter Schafer, Leah Perlmutter 020 * </p> 021 */ 022 023import java.awt.event.*; 024import javax.swing.*; 025import java.awt.*; 026import java.awt.geom.*; 027import java.awt.image.*; 028import java.io.*; 029import java.lang.reflect.Method; 030import java.util.*; 031import javax.imageio.ImageIO; 032import javax.swing.filechooser.FileNameExtensionFilter; 033 034/** 035 * <h1>About Turtle Graphics</h1> 036 * Turtle is a software library for drawing graphics with simple code, approachable 037 * to beginners. 038 * 039 * This library is one incarnation of Turtle Graphics. 040 * Turtle Graphics has been implemented in many programming languages over many 041 * decades, often as a tool to support people in learning to program! 042 * 043 * <h1>Methods Overview </h1> 044 * <p> 045 * Here is an overview of many of the methods in this package. If you 046 * are new to Turtle, you may want to start with the 047 * methods marked by boldface font and the turtle emoji \ud83d\udc22! 048 * </p> 049 * <h4 style="display:inline">Constructing and Copying Turtles</h4> 050 * <div><ul> 051 * <li> \ud83d\udc22 <b>{@link #Turtle()}</b></li> 052 * <li>{@link #Turtle(double, double)}</li> 053 * <li>{@link #clone()}</li> 054 * </ul></div> 055 * <h4 style="display:inline">Moving and Turning a Turtle</h4> 056 * <div><ul> 057 * <li> \ud83d\udc22 <b>{@link #forward(double)}</b> | <b>{@link #backward(double)}</b></li> 058 * <li> \ud83d\udc22 <b>{@link #left(double)}</b> | <b>{@link #right(double)}</b></li> 059 * <li> \ud83d\udc22 <b>{@link #speed(double)}</b></li> 060 * <li>{@link #home()}</li> 061 * <li>{@link #face(double, double)} | {@link #setDirection(double)} </li> 062 * <li>{@link #setPosition(double, double)} | {@link #setPosition(double, double, double)}</li> 063 * </ul></div> 064 * <h4 style="display:inline">Manipulating the Pen and Drawing</h4> 065 * <div><ul> 066 * <li> \ud83d\udc22 <b>{@link #up()}</b> | <b>{@link #down()}</b></li> 067 * <li> \ud83d\udc22 <b>{@link #width(double)}</b></li> 068 * <li> \ud83d\udc22 <b>{@link #penColor(java.awt.Color)}</b> | <b>{@link #penColor(java.lang.String)}</b></li> 069 * <li>{@link #dot()} | {@link #dot(java.awt.Color)} | {@link #dot(java.awt.Color, double)} | {@link #dot(java.lang.String)} | {@link #dot(java.lang.String, double)}</li> 070 * <li>{@link #stamp()}</li> 071 * </ul></div> 072 * <h4 style="display:inline">Console Output</h4> 073 * <div><ul> 074 * <li> \ud83d\udc22 <b>{@link #accessiblePrinting(boolean)}</b></li> 075 * </ul></div> 076 * <h4 style="display:inline">Changing a Turtle's Appearance</h4> 077 * <div><ul> 078 * <li>{@link #hide()} | {@link #show()}</li> 079 * <li>{@link #fillColor(java.awt.Color)} | {@link #fillColor(java.lang.String)}</li> 080 * <li>{@link #outlineColor(java.awt.Color)} | {@link #outlineColor(java.lang.String)}</li> 081 * <li>{@link #tilt(double)} | {@link #setTilt(double)}</li> 082 * <li>{@link #shape(java.lang.String)} | {@link #shapeSize(int, int)}</li> 083 * </ul></div> 084 * <h4 style="display:inline">Getting a Turtle's State</h4> 085 * <div><ul> 086 * <li>{@link #getX()} | {@link #getY()}</li> 087 * <li>{@link #getDirection()}</li> 088 * <li>{@link #getSpeed()}</li> 089 * <li>{@link #getTilt()}</li> 090 * </ul></div> 091 * <h4 style="display:inline">Manipulating and Saving the Canvas</h4> 092 * <div><ul> 093 * <li>{@link #clear()}</li> 094 * <li>{@link #bgcolor(java.awt.Color)} | {@link #bgcolor(java.lang.String)}</li> 095 * <li>{@link #bgpic(java.lang.String)}</li> 096 * <li>{@link #setCanvasSize(int, int)}</li> 097 * <li>{@link #save(java.lang.String)}</li> 098 * </ul></div> 099 * <h4 style="display:inline">Geometric Computations</h4> 100 * <div><ul> 101 * <li>{@link #canvasX(double)} | {@link #canvasY(double)}</li> 102 * <li>{@link #screenX(double)} | {@link #screenY(double)}</li> 103 * <li>{@link #distance(double, double)}</li> 104 * <li>{@link #towards(double, double)}</li> 105 * </ul></div> 106 * 107 * <h1>Coordinate Systems</h1> 108 * This section is to clarify the meaning of numeric coordinates. 109 * 110 * <h4>Canvas Coordinates</h4> 111 * Turtle commands such as <code>forward</code>, <code>setPosition</code>, 112 * and <code>getX</code>/<code>getY</code> 113 * are in terms of canvas coordinates. 114 * Canvas coordinates can be thought of as "model coordinates". 115 * <ul> 116 * <li>X axis points rightward.</li> 117 * <li>Y axis points upward.</li> 118 * <li>When the turtle window first loads, the origin is in the center of the 119 * turtle window.</li> 120 * </ul> 121 * 122 * <h4>Screen Coordinates</h4> 123 * Screen coordinates change relative to the turtle's world as you 124 * zoom or pan the view of the canvas. 125 * Screen coordinates can be thought of as "view coordinates". 126 * <ul> 127 * <li>X axis points rightward.</li> 128 * <li>Y axis points updward.</li> 129 * <li>The origin is the lower left hand corner of the turtle window, regardless 130 * of how much you have zoomed or panned the view.</li> 131 * </ul> 132 * 133 * 134 * @author Nicholas Seward 135 */ 136@SuppressWarnings({"unchecked", "deprecation", "removal"}) 137public class Turtle implements Runnable, ActionListener, MouseListener, MouseMotionListener, KeyListener, ComponentListener, MouseWheelListener { 138 139 private static ArrayList<Turtle> turtles; 140 private static TreeMap<Long, ArrayList> turtleStates; 141 private static TreeMap<Long, ArrayList> redoStates; 142 private static JFrame window; 143 private static JApplet applet; 144 private static JLabel draw; 145 private static int width, height; 146 private static BufferedImage offscreenImage, midscreenImage, onscreenImage; 147 private static Graphics2D offscreen, midscreen, onscreen; 148 private static BufferedImage backgroundImage; 149 private static Color backgroundColor; 150 private static ImageIcon icon; 151 private static Turtle turtle; 152 private static HashMap<String, Polygon> shapes; //You can only add. Never remove. 153 private static HashMap<String, Color> colors; 154 private static HashMap<String, ArrayList<ArrayList>> keyBindings; 155 private static HashMap<Turtle, ArrayList<ArrayList>> mouseBindings; 156 private static double centerX, centerY; 157 private static double scale; 158 private static TreeSet<String> keysDown; 159 private static TreeSet<String> processedKeys; 160 private static TreeSet<String> unprocessedKeys; 161 private static long lastUpdate; 162 private static long fps; 163 private static final Object turtleLock = new Object(); 164 private static int dragx = 0, dragy = 0, x = 0, y = 0, modifiers = 0; 165 private static final Object keyLock = new Object(); 166 private static final int REFRESH_MODE_ANIMATED = 0; 167 private static final int REFRESH_MODE_STATE_CHANGE = 1; 168 private static final int REFRESH_MODE_ON_DEMAND = 2; 169 private static int refreshMode; 170 private static final int BACKGROUND_MODE_STRETCH = 0; 171 private static final int BACKGROUND_MODE_CENTER = 1; 172 private static final int BACKGROUND_MODE_TILE = 2; 173 private static final int BACKGROUND_MODE_CENTER_RELATIVE = 3; 174 private static final int BACKGROUND_MODE_TILE_RELATIVE = 4; 175 private static int backgroundMode; 176 private static Turtle selectedTurtle; 177 private static boolean running; 178 179 // Output if accessibility mode is turned on 180 private static PrintStream out; 181 182 static { 183 init(); 184 } 185 186 /** 187 * This is an internal method that should never be called. 188 */ 189 public void run() { 190 if (Thread.currentThread().getName().equals("render")) renderLoop(); 191 else if (Thread.currentThread().getName().equals("listen")) eventLoop(); 192 } 193 194 private void eventLoop() { 195 //System.out.println("EVENT LOOP STARTED"); 196 long time = 0; 197 while (running) { 198 time = System.nanoTime(); 199 processKeys(); 200 waitUntil(time + 1000000000 / fps); 201 } 202 } 203 204 private void renderLoop() { 205 //System.out.println("RENDER LOOP STARTED"); 206 long time = 0; 207 while (running) { 208 time = System.nanoTime(); 209 //System.out.println(time); 210 if (refreshMode == REFRESH_MODE_ANIMATED) draw(); 211 if (!waitUntil(time + 1000000000 / fps)) fps--; 212 else if (fps < 30) fps++; 213 } 214 } 215 216 private static boolean waitUntil(Long time) { 217 long now = System.nanoTime(); 218 if (now < time) { 219 try { 220 Thread.sleep((time - now) / 1000000); 221 } catch (Exception e) { 222 } 223 return true; 224 } else return false; 225 } 226 227 private static void init() { 228 //mouseBindings.put(null, new ArrayList<ArrayList>()); 229 turtles = new ArrayList<Turtle>(); 230 turtleStates = new TreeMap<Long, ArrayList>(); 231 redoStates = new TreeMap<Long, ArrayList>(); 232 width = 500; 233 height = 500; 234 backgroundColor = Color.WHITE; 235 keyBindings = new HashMap<String, ArrayList<ArrayList>>(); 236 mouseBindings = new HashMap<Turtle, ArrayList<ArrayList>>(); 237 centerX = 0; 238 centerY = 0; 239 scale = 1; 240 keysDown = new TreeSet<String>(); 241 processedKeys = new TreeSet<String>(); 242 unprocessedKeys = new TreeSet<String>(); 243 lastUpdate = 0; 244 fps = 30; 245 dragx = 0; 246 dragy = 0; 247 x = 0; 248 y = 0; 249 modifiers = 0; 250 refreshMode = REFRESH_MODE_ANIMATED; 251 backgroundMode = BACKGROUND_MODE_TILE_RELATIVE; 252 selectedTurtle = null; 253 running = true; 254 255 256 window = new JFrame("Turtle"); 257 icon = new ImageIcon(); 258 setupBuffering(); 259 draw = new JLabel(icon); 260 window.setContentPane(draw); 261 //window.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE); 262 try { 263 window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 264 } catch (Exception e) { 265 } 266 267 JMenuBar menuBar = new JMenuBar(); 268 JMenu menu = new JMenu("File"); 269 menuBar.add(menu); 270 JMenuItem menuItem1 = new JMenuItem("Save..."); 271 272 273 menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, 274 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); 275 menu.add(menuItem1); 276 window.setJMenuBar(menuBar); 277 window.pack(); 278 window.requestFocusInWindow(); 279 280 //! TODO: Fix this :) 281 try { 282 Thread.sleep(5000); 283 } catch (Exception e) { 284 } 285 286 drawTurtleIcon(); 287 window.setVisible(true); 288 289 makeShapes(); 290 turtle = new Turtle(0); 291 draw.setFocusable(true); 292 menuItem1.addActionListener(turtle); 293 window.addComponentListener(turtle); 294 draw.addComponentListener(turtle); 295 draw.addMouseListener(turtle); 296 draw.addMouseMotionListener(turtle); 297 draw.addMouseWheelListener(turtle); 298 window.addKeyListener(turtle); 299 draw.addKeyListener(turtle); 300 draw.requestFocus(); 301 initColors(); 302 303 // Printing for accessibility default is off 304 accessiblePrinting(false); 305 306// GraphicsEnvironment ge; 307// ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 308// String[] fontNames = ge.getAvailableFontFamilyNames(); 309// System.out.println(Arrays.toString(fontNames)); 310 311 (new Thread(turtle, "render")).start(); 312 (new Thread(turtle, "listen")).start(); 313 } 314 315 316 317 /** 318 * Turn accessible printing on or off. 319 * When accessible printing is turned on, every turtle command that could result 320 * in a visual graphical upate will also be printed to the console as text. 321 * 322 * @param printToConsole <code>true</code> to turn on accessible printing, or 323 * <code>false</code> to turn off accessible printing 324 */ 325 public static void accessiblePrinting(boolean printToConsole) { 326 if (printToConsole) { 327 if (out != System.out) { 328 out = System.out; 329 out.println("Printing instructions and position information," 330 + " with position information in two dimensional coordinates"); 331 } 332 } else { 333 out = new PrintStream(new OutputStream() { 334 public void write(int b) { 335 //DO NOTHING 336 } 337 }); 338 } 339 } 340 341 public static void exit() { 342 running = false; 343 window.setVisible(false); 344 window.dispose(); 345 } 346 347 /** 348 * This is an experimental method that should allow you to make turtle 349 * applets in the future. For now, it doesn't work because the key and 350 * mouse bindings require reflection and applets think that allowing 351 * reflection would open a security hole. Theoretically in the init method 352 * of the applet you need to place <code>Turtle.startApplet(this);</code>. 353 * <b>This is not currently working.</b> 354 * 355 * @param applet applet 356 */ 357 public static void startApplet(JApplet applet) { 358 //turtleStates.clear(); 359 //turtles.clear(); 360 //init(); 361 Turtle.applet = applet; 362 applet.setContentPane(window.getContentPane()); 363 window.setVisible(false); 364 try { 365 (new Thread((Runnable) applet, "turtle")).start(); 366 } catch (Exception e) { 367 e.printStackTrace(); 368 } 369 } 370 371 private static void initColors() { 372 colors = new HashMap<String, Color>(); 373 colors.put("aliceblue", new Color(0xf0f8ff)); 374 colors.put("antiquewhite", new Color(0xfaebd7)); 375 colors.put("aqua", new Color(0x00ffff)); 376 colors.put("aquamarine", new Color(0x7fffd4)); 377 colors.put("azure", new Color(0xf0ffff)); 378 colors.put("beige", new Color(0xf5f5dc)); 379 colors.put("bisque", new Color(0xffe4c4)); 380 colors.put("black", new Color(0x000000)); 381 colors.put("blanchedalmond", new Color(0xffebcd)); 382 colors.put("blue", new Color(0x0000ff)); 383 colors.put("blueviolet", new Color(0x8a2be2)); 384 colors.put("brown", new Color(0xa52a2a)); 385 colors.put("burlywood", new Color(0xdeb887)); 386 colors.put("cadetblue", new Color(0x5f9ea0)); 387 colors.put("chartreuse", new Color(0x7fff00)); 388 colors.put("chocolate", new Color(0xd2691e)); 389 colors.put("coral", new Color(0xff7f50)); 390 colors.put("cornflowerblue", new Color(0x6495ed)); 391 colors.put("cornsilk", new Color(0xfff8dc)); 392 colors.put("crimson", new Color(0xdc143c)); 393 colors.put("cyan", new Color(0x00ffff)); 394 colors.put("darkblue", new Color(0x00008b)); 395 colors.put("darkcyan", new Color(0x008b8b)); 396 colors.put("darkgoldenrod", new Color(0xb8860b)); 397 colors.put("darkgray", new Color(0xa9a9a9)); 398 colors.put("darkgreen", new Color(0x006400)); 399 colors.put("darkkhaki", new Color(0xbdb76b)); 400 colors.put("darkmagenta", new Color(0x8b008b)); 401 colors.put("darkolivegreen", new Color(0x556b2f)); 402 colors.put("darkorange", new Color(0xff8c00)); 403 colors.put("darkorchid", new Color(0x9932cc)); 404 colors.put("darkred", new Color(0x8b0000)); 405 colors.put("darksalmon", new Color(0xe9967a)); 406 colors.put("darkseagreen", new Color(0x8fbc8f)); 407 colors.put("darkslateblue", new Color(0x483d8b)); 408 colors.put("darkslategray", new Color(0x2f4f4f)); 409 colors.put("darkturquoise", new Color(0x00ced1)); 410 colors.put("darkviolet", new Color(0x9400d3)); 411 colors.put("deeppink", new Color(0xff1493)); 412 colors.put("deepskyblue", new Color(0x00bfff)); 413 colors.put("dimgray", new Color(0x696969)); 414 colors.put("dodgerblue", new Color(0x1e90ff)); 415 colors.put("firebrick", new Color(0xb22222)); 416 colors.put("floralwhite", new Color(0xfffaf0)); 417 colors.put("forestgreen", new Color(0x228b22)); 418 colors.put("fuchsia", new Color(0xff00ff)); 419 colors.put("gainsboro", new Color(0xdcdcdc)); 420 colors.put("ghostwhite", new Color(0xf8f8ff)); 421 colors.put("gold", new Color(0xffd700)); 422 colors.put("goldenrod", new Color(0xdaa520)); 423 colors.put("gray", new Color(0x808080)); 424 colors.put("green", new Color(0x008000)); 425 colors.put("greenyellow", new Color(0xadff2f)); 426 colors.put("honeydew", new Color(0xf0fff0)); 427 colors.put("hotpink", new Color(0xff69b4)); 428 colors.put("indianred", new Color(0xcd5c5c)); 429 colors.put("indigo", new Color(0x4b0082)); 430 colors.put("ivory", new Color(0xfffff0)); 431 colors.put("khaki", new Color(0xf0e68c)); 432 colors.put("lavender", new Color(0xe6e6fa)); 433 colors.put("lavenderblush", new Color(0xfff0f5)); 434 colors.put("lawngreen", new Color(0x7cfc00)); 435 colors.put("lemonchiffon", new Color(0xfffacd)); 436 colors.put("lightblue", new Color(0xadd8e6)); 437 colors.put("lightcoral", new Color(0xf08080)); 438 colors.put("lightcyan", new Color(0xe0ffff)); 439 colors.put("lightgoldenrodyellow", new Color(0xfafad2)); 440 colors.put("lightgreen", new Color(0x90ee90)); 441 colors.put("lightgrey", new Color(0xd3d3d3)); 442 colors.put("lightpink", new Color(0xffb6c1)); 443 colors.put("lightsalmon", new Color(0xffa07a)); 444 colors.put("lightseagreen", new Color(0x20b2aa)); 445 colors.put("lightskyblue", new Color(0x87cefa)); 446 colors.put("lightslategray", new Color(0x778899)); 447 colors.put("lightsteelblue", new Color(0xb0c4de)); 448 colors.put("lightyellow", new Color(0xffffe0)); 449 colors.put("lime", new Color(0x00ff00)); 450 colors.put("limegreen", new Color(0x32cd32)); 451 colors.put("linen", new Color(0xfaf0e6)); 452 colors.put("magenta", new Color(0xff00ff)); 453 colors.put("maroon", new Color(0x800000)); 454 colors.put("mediumaquamarine", new Color(0x66cdaa)); 455 colors.put("mediumblue", new Color(0x0000cd)); 456 colors.put("mediumorchid", new Color(0xba55d3)); 457 colors.put("mediumpurple", new Color(0x9370db)); 458 colors.put("mediumseagreen", new Color(0x3cb371)); 459 colors.put("mediumslateblue", new Color(0x7b68ee)); 460 colors.put("mediumspringgreen", new Color(0x00fa9a)); 461 colors.put("mediumturquoise", new Color(0x48d1cc)); 462 colors.put("mediumvioletred", new Color(0xc71585)); 463 colors.put("midnightblue", new Color(0x191970)); 464 colors.put("mintcream", new Color(0xf5fffa)); 465 colors.put("mistyrose", new Color(0xffe4e1)); 466 colors.put("moccasin", new Color(0xffe4b5)); 467 colors.put("navajowhite", new Color(0xffdead)); 468 colors.put("navy", new Color(0x000080)); 469 colors.put("oldlace", new Color(0xfdf5e6)); 470 colors.put("olive", new Color(0x808000)); 471 colors.put("olivedrab", new Color(0x6b8e23)); 472 colors.put("orange", new Color(0xffa500)); 473 colors.put("orangered", new Color(0xff4500)); 474 colors.put("orchid", new Color(0xda70d6)); 475 colors.put("palegoldenrod", new Color(0xeee8aa)); 476 colors.put("palegreen", new Color(0x98fb98)); 477 colors.put("paleturquoise", new Color(0xafeeee)); 478 colors.put("palevioletred", new Color(0xdb7093)); 479 colors.put("papayawhip", new Color(0xffefd5)); 480 colors.put("peachpuff", new Color(0xffdab9)); 481 colors.put("peru", new Color(0xcd853f)); 482 colors.put("pink", new Color(0xffc0cb)); 483 colors.put("plum", new Color(0xdda0dd)); 484 colors.put("powderblue", new Color(0xb0e0e6)); 485 colors.put("purple", new Color(0x800080)); 486 colors.put("red", new Color(0xff0000)); 487 colors.put("rosybrown", new Color(0xbc8f8f)); 488 colors.put("royalblue", new Color(0x4169e1)); 489 colors.put("saddlebrown", new Color(0x8b4513)); 490 colors.put("salmon", new Color(0xfa8072)); 491 colors.put("sandybrown", new Color(0xf4a460)); 492 colors.put("seagreen", new Color(0x2e8b57)); 493 colors.put("seashell", new Color(0xfff5ee)); 494 colors.put("sienna", new Color(0xa0522d)); 495 colors.put("silver", new Color(0xc0c0c0)); 496 colors.put("skyblue", new Color(0x87ceeb)); 497 colors.put("slateblue", new Color(0x6a5acd)); 498 colors.put("slategray", new Color(0x708090)); 499 colors.put("snow", new Color(0xfffafa)); 500 colors.put("springgreen", new Color(0x00ff7f)); 501 colors.put("steelblue", new Color(0x4682b4)); 502 colors.put("tan", new Color(0xd2b48c)); 503 colors.put("teal", new Color(0x008080)); 504 colors.put("thistle", new Color(0xd8bfd8)); 505 colors.put("tomato", new Color(0xff6347)); 506 colors.put("turquoise", new Color(0x40e0d0)); 507 colors.put("violet", new Color(0xee82ee)); 508 colors.put("wheat", new Color(0xf5deb3)); 509 colors.put("white", new Color(0xffffff)); 510 colors.put("whitesmoke", new Color(0xf5f5f5)); 511 colors.put("yellow", new Color(0xffff00)); 512 colors.put("yellowgreen", new Color(0x9acd32)); 513 } 514 515 private static Color getColor(String color) { 516 String origColor = color; 517 color = color.toLowerCase().replaceAll("[^a-z]", ""); 518 if (colors.containsKey(color)) { 519 return colors.get(color); 520 } else { 521 return null; 522 } 523 } 524 525 private static void setupBuffering() { 526 synchronized (turtleLock) { 527 lastUpdate = 0; 528 offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 529 midscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 530 onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 531 offscreen = offscreenImage.createGraphics(); 532 midscreen = midscreenImage.createGraphics(); 533 onscreen = onscreenImage.createGraphics(); 534 //offscreen.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY); 535 //offscreen.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC); 536 offscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 537 midscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 538 onscreen.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 539 drawBackground(offscreen); 540 drawBackground(onscreen); 541 icon.setImage(onscreenImage); 542 } 543 } 544 545 private static void drawTurtleIcon() { 546 byte[] imageData = new byte[]{71, 73, 70, 56, 57, 97, 16, 0, 16, 0, -95, 2, 0, 0, -103, 547 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, 548 0, 2, 44, -108, -113, -87, -53, -19, -33, -128, 4, 104, 74, 35, 67, -72, 34, -21, 11, 549 124, 27, -90, -107, -109, 72, 117, -91, -71, 110, 103, -37, 90, -31, -10, -55, -87, 550 122, -34, 74, 72, -15, 17, -56, -127, 8, 33, 5, 0, 59}; 551 try { 552 BufferedImage tmpicon = ImageIO.read(new ByteArrayInputStream(imageData)); 553 window.setIconImage(tmpicon); 554 } catch (Exception e) { 555 } 556 } 557 558 private static void makeShapes() { 559 shapes = new HashMap<String, Polygon>(); 560 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}; 561 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}; 562 Polygon p = new Polygon(xs, ys, xs.length); 563 shapes.put("turtle", p); 564 xs = new int[]{0, 100, 0, 20}; 565 ys = new int[]{0, 50, 100, 50}; 566 p = new Polygon(xs, ys, xs.length); 567 shapes.put("arrow", p); 568 xs = new int[]{0, 100, 100, 0}; 569 ys = new int[]{0, 0, 100, 100}; 570 p = new Polygon(xs, ys, xs.length); 571 shapes.put("rectangle", p); 572 shapes.put("square", p); 573 xs = new int[]{0, 100, 0}; 574 ys = new int[]{0, 50, 100}; 575 p = new Polygon(xs, ys, xs.length); 576 shapes.put("triangle", p); 577 int divisions = 24; 578 xs = new int[divisions]; 579 ys = new int[divisions]; 580 for (int i = 0; i < divisions; i++) { 581 double angle = Math.toRadians(i * 360.0 / divisions); 582 xs[i] = (int) Math.round(50 + 50 * Math.cos(angle)); 583 ys[i] = (int) Math.round(50 + 50 * Math.sin(angle)); 584 } 585 p = new Polygon(xs, ys, xs.length); 586 shapes.put("circle", p); 587 } 588 589 /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 590 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 591 * U U U U U U U U U U U U U U U U 592 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 593 * _/___\/ _/___\/ _/___\/ TURTLE CONSTRUCTION_/___\/ _/___\/ _/___\/ 594 * U U U U U U U U U U U U 595 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 596 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 597 * U U U U U U U U U U U U U U U U 598 */ 599 600 /** 601 * This is a internal constuctor that makes a singleton that does the 602 * listening but is not added to the stack of turtles to be rendered. 603 * You don't need to use this outside of the Turtle.java file. 604 * 605 * @param i Pass this any integer. It doesn't do anything. 606 */ 607 private Turtle(int i) { 608 } 609 610 private Point2D.Double location = new Point2D.Double(0, 0); 611 private double direction = 0; //degrees 612 private String shape = "turtle"; //Stores a key to the shapes hashmap 613 private BufferedImage image = null; 614 private double shapeWidth = 33; 615 private double shapeHeight = 33; 616 private double tilt = 0; 617 private double penWidth = 2; 618 private Color penColor = Color.BLACK; 619 private double outlineWidth = 2; 620 private Color outlineColor = Color.BLACK; 621 private Color fillColor = new Color(0, 255, 0, 128); 622 private double speed = 50; //milliseconds to execute a move 623 private boolean isPenDown = true; 624 private boolean isFilling = false; 625 private boolean isVisible = true; 626 private ArrayList<Point2D.Double> polygon = new ArrayList<Point2D.Double>(); 627 //temporary storage 628 private Long _time; 629 private Point2D.Double _location; 630 private Double _direction; 631 private String _shape; 632 private BufferedImage _image; 633 private Double _shapeWidth; 634 private Double _shapeHeight; 635 private Double _tilt; 636 private Double _penWidth; 637 private Color _penColor; 638 private Double _outlineWidth; 639 private Color _outlineColor; 640 private Color _fillColor; 641 private Double _speed; 642 private Boolean _isPenDown; 643 private Boolean _isFilling; 644 private Boolean _isVisible; 645 private ArrayList<Point2D.Double> _polygon; 646 private Boolean _isFill; 647 private Boolean _isStamp; 648 private Double _dotSize; 649 private Color _dotColor; 650 private Font _font; 651 private String _text; 652 private Integer _justification; 653 private Point2D.Double _textOffset; 654 655 //secondary temporary storage 656 private Long __time; 657 private Point2D.Double __location; 658 private Double __direction; 659 private String __shape; 660 private BufferedImage __image; 661 private Double __shapeWidth; 662 private Double __shapeHeight; 663 private Double __tilt; 664 private Double __penWidth; 665 private Color __penColor; 666 private Double __outlineWidth; 667 private Color __outlineColor; 668 private Color __fillColor; 669 private Double __speed; 670 private Boolean __isPenDown; 671 private Boolean __isFilling; 672 private Boolean __isVisible; 673 private ArrayList<Point2D.Double> __polygon; 674 private Boolean __isFill; 675 private Boolean __isStamp; 676 private Double __dotSize; 677 private Color __dotColor; 678 private Font __font; 679 private String __text; 680 private Integer __justification; 681 private Point2D.Double __textOffset; 682 683 /** 684 * Makes a turtle at the center of the canvas at location (0, 0). 685 * <pre> 686 * Turtle t = new Turtle(); 687 * </pre> 688 */ 689 public Turtle() { 690 if (window == null) init(); 691 synchronized (turtleLock) { 692 turtles.add(this); 693 } 694 long time = storeCurrentState(); 695 updateAll(); 696 } 697 698 /** 699 * Makes a turtle at the specified position. 700 * 701 * @param x x coordinate 702 * @param y y coordinate 703 */ 704 public Turtle(double x, double y) { 705 if (window == null) init(); 706 location = new Point2D.Double(x, y); 707 synchronized (turtleLock) { 708 turtles.add(this); 709 } 710 long time = storeCurrentState(); 711 updateAll(); 712 } 713 714 /** 715 * This creates a cloned copy of a turtle. 716 * 717 * @return a cloned copy of a turtle 718 */ 719 public Turtle clone() { 720 Turtle t = new Turtle(0); 721 t.location = (Point2D.Double) this.location.clone(); 722 t.direction = this.direction; 723 t.shape = t.shape; 724 t.image = this.image; 725 t.shapeWidth = this.shapeWidth; 726 t.shapeHeight = this.shapeHeight; 727 t.tilt = this.tilt; 728 t.penWidth = this.penWidth; 729 t.penColor = this.penColor; 730 t.outlineWidth = this.outlineWidth; 731 t.outlineColor = this.outlineColor; 732 t.fillColor = this.fillColor; 733 t.speed = this.speed; 734 t.isPenDown = this.isPenDown; 735 t.isFilling = this.isFilling; 736 t.isVisible = this.isVisible; 737 if (window == null) init(); 738 synchronized (turtleLock) { 739 turtles.add(t); 740 } 741 long time = t.storeCurrentState(); 742 return t; 743 } 744 745 /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 746 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 747 * U U U U U U U U U U U U U U U U 748 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 749 * _/___\/ _/___\/ _/___\/ STATE MANAGEMENT _/___\/ _/___\/ _/___\/ 750 * U U U U U U U U U U U U 751 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 752 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 753 * U U U U U U U U U U U U U U U U 754 */ 755 756 private long storeCurrentState() { 757 return storeCurrentState(false, false, 0, null, null, null, 0, null); 758 } 759 760 private long storeAnimatedState() { 761 return storeCurrentState(true, false, 0, null, null, null, 0, null); 762 } 763 764 private long storeCurrentState(boolean animate, boolean isStamp, double dotSize, Color dotColor, Font font, String text, int justification, Point2D.Double textOffset) { 765 ArrayList state = new ArrayList(); 766 long time = System.nanoTime(); 767 synchronized (turtleLock) { 768 state.add(0); //0 769 state.add(this); //1 770 state.add(location.clone());//2 771 state.add(direction); //3 772 state.add(shape); //4 773 state.add(image); //5 774 state.add(shapeWidth); //6 775 state.add(shapeHeight); //7 776 state.add(tilt); //8 777 state.add(penWidth); //9 778 state.add(penColor); //10 779 state.add(outlineWidth); //11 780 state.add(outlineColor); //12 781 state.add(fillColor); //13 782 state.add(speed); //14 783 state.add(isPenDown); //15 784 state.add(isFilling); //16 785 state.add(isVisible); //17 786 state.add(isStamp); //18 787 state.add(dotSize); //19 788 state.add(dotColor); //20 789 state.add(font); //21 790 state.add(text); //22 791 state.add(justification); //23 792 state.add(textOffset); //24 793 if (!turtleStates.isEmpty() && turtleStates.lastKey() > time) time = turtleStates.lastKey() + 1; 794 if (animate) time += (long) (speed * 1000000); 795 state.set(0, time); 796 turtleStates.put(time, state); 797 redoStates.clear(); 798 } 799 if (refreshMode == REFRESH_MODE_STATE_CHANGE) draw(); 800 if (refreshMode == REFRESH_MODE_ANIMATED) waitUntil(time); 801 return time; 802 } 803 804 private static void clearStorage() { 805 synchronized (turtleLock) { 806 for (Turtle t : turtles) { 807 t.__time = null; 808 t.__location = null; 809 t.__direction = null; 810 t.__shape = null; 811 t.__image = null; 812 t.__shapeWidth = null; 813 t.__shapeHeight = null; 814 t.__tilt = null; 815 t.__penWidth = null; 816 t.__penColor = null; 817 t.__outlineWidth = null; 818 t.__outlineColor = null; 819 t.__fillColor = null; 820 t.__speed = null; 821 t.__isPenDown = null; 822 t.__isFilling = null; 823 t.__isVisible = null; 824 t.__isStamp = null; 825 t.__dotSize = null; 826 t.__dotColor = null; 827 t.__font = null; 828 t.__text = null; 829 t.__justification = null; 830 t.__textOffset = null; 831 t._time = null; 832 t._location = null; 833 t._direction = null; 834 t._shape = null; 835 t._shapeWidth = null; 836 t._shapeHeight = null; 837 t._image = null; 838 t._tilt = null; 839 t._penWidth = null; 840 t._penColor = null; 841 t._outlineWidth = null; 842 t._outlineColor = null; 843 t._fillColor = null; 844 t._speed = null; 845 t._isPenDown = null; 846 t._isFilling = null; 847 t._isVisible = null; 848 t._isStamp = null; 849 t._dotSize = null; 850 t._dotColor = null; 851 t._font = null; 852 t._text = null; 853 t._justification = null; 854 t._textOffset = null; 855 } 856 } 857 } 858 859 private static void retrieveState(long time) { 860 synchronized (turtleLock) { 861 Turtle t = getStateTurtle(turtleStates.get(time)); 862 t.__time = t._time; 863 t.__location = t._location; 864 t.__direction = t._direction; 865 t.__shape = t._shape; 866 t.__image = t._image; 867 t.__shapeWidth = t._shapeWidth; 868 t.__shapeHeight = t._shapeHeight; 869 t.__tilt = t._tilt; 870 t.__penWidth = t._penWidth; 871 t.__penColor = t._penColor; 872 t.__outlineWidth = t._outlineWidth; 873 t.__outlineColor = t._outlineColor; 874 t.__fillColor = t._fillColor; 875 t.__speed = t._speed; 876 t.__isPenDown = t._isPenDown; 877 t.__isFilling = t._isFilling; 878 t.__isVisible = t._isVisible; 879 t.__isStamp = t._isStamp; 880 t.__dotSize = t._dotSize; 881 t.__dotColor = t._dotColor; 882 t.__font = t._font; 883 t.__text = t._text; 884 t.__justification = t._justification; 885 t.__textOffset = t._textOffset; 886 ArrayList state = turtleStates.get(time); 887 t._time = getStateTime(state); 888 t._location = getStateLocation(state); 889 t._direction = getStateDirection(state); 890 t._shape = getStateShape(state); 891 t._shapeWidth = getStateShapeWidth(state); 892 t._shapeHeight = getStateShapeHeight(state); 893 t._image = getStateImage(state); 894 t._tilt = getStateTilt(state); 895 t._penWidth = getStatePenWidth(state); 896 t._penColor = getStatePenColor(state); 897 t._outlineWidth = getStateOutlineWidth(state); 898 t._outlineColor = getStateOutlineColor(state); 899 t._fillColor = getStateFillColor(state); 900 t._speed = getStateSpeed(state); 901 t._isPenDown = getStateIsPenDown(state); 902 t._isFilling = getStateIsFilling(state); 903 t._isVisible = getStateIsVisible(state); 904 t._isStamp = getStateIsStamp(state); 905 t._dotSize = getStateDotSize(state); 906 t._dotColor = getStateDotColor(state); 907 t._font = getStateFont(state); 908 t._text = getStateText(state); 909 t._justification = getStateJustification(state); 910 t._textOffset = getStateTextOffset(state); 911 } 912 } 913 914 private static long getStateTime(ArrayList state) { 915 return (Long) state.get(0); 916 } 917 918 private static Turtle getStateTurtle(ArrayList state) { 919 return (Turtle) state.get(1); 920 } 921 922 private static Point2D.Double getStateLocation(ArrayList state) { 923 return (Point2D.Double) ((Point2D.Double) state.get(2)).clone(); 924 } 925 926 private static double getStateDirection(ArrayList state) { 927 return (Double) state.get(3); 928 } 929 930 private static String getStateShape(ArrayList state) { 931 return (String) state.get(4); 932 } 933 934 private static BufferedImage getStateImage(ArrayList state) { 935 return (BufferedImage) state.get(5); 936 } 937 938 private static double getStateShapeWidth(ArrayList state) { 939 return (Double) state.get(6); 940 } 941 942 private static double getStateShapeHeight(ArrayList state) { 943 return (Double) state.get(7); 944 } 945 946 private static double getStateTilt(ArrayList state) { 947 return (Double) state.get(8); 948 } 949 950 private static double getStatePenWidth(ArrayList state) { 951 return (Double) state.get(9); 952 } 953 954 private static Color getStatePenColor(ArrayList state) { 955 return (Color) state.get(10); 956 } 957 958 private static double getStateOutlineWidth(ArrayList state) { 959 return (Double) state.get(11); 960 } 961 962 private static Color getStateOutlineColor(ArrayList state) { 963 return (Color) state.get(12); 964 } 965 966 private static Color getStateFillColor(ArrayList state) { 967 return (Color) state.get(13); 968 } 969 970 private static double getStateSpeed(ArrayList state) { 971 return (Double) state.get(14); 972 } 973 974 private static boolean getStateIsPenDown(ArrayList state) { 975 return (Boolean) state.get(15); 976 } 977 978 private static boolean getStateIsFilling(ArrayList state) { 979 return (Boolean) state.get(16); 980 } 981 982 private static boolean getStateIsVisible(ArrayList state) { 983 return (Boolean) state.get(17); 984 } 985 986 private static boolean getStateIsStamp(ArrayList state) { 987 return (Boolean) state.get(18); 988 } 989 990 private static double getStateDotSize(ArrayList state) { 991 return (Double) state.get(19); 992 } 993 994 private static Color getStateDotColor(ArrayList state) { 995 return (Color) state.get(20); 996 } 997 998 private static Font getStateFont(ArrayList state) { 999 return (Font) state.get(21); 1000 } 1001 1002 private static String getStateText(ArrayList state) { 1003 return (String) state.get(22); 1004 } 1005 1006 private static int getStateJustification(ArrayList state) { 1007 return (Integer) state.get(23); 1008 } 1009 1010 private static Point2D.Double getStateTextOffset(ArrayList state) { 1011 return (Point2D.Double) state.get(24); 1012 } 1013 1014 private static void restoreState(long time) { 1015 ArrayList state = turtleStates.get(time); 1016 Turtle t = getStateTurtle(turtleStates.get(time)); 1017 t.location = getStateLocation(state); 1018 t.direction = getStateDirection(state); 1019 t.shape = getStateShape(state); 1020 t.shapeWidth = getStateShapeWidth(state); 1021 t.shapeHeight = getStateShapeHeight(state); 1022 t.image = getStateImage(state); 1023 t.tilt = getStateTilt(state); 1024 t.penWidth = getStatePenWidth(state); 1025 t.penColor = getStatePenColor(state); 1026 t.outlineWidth = getStateOutlineWidth(state); 1027 t.outlineColor = getStateOutlineColor(state); 1028 t.fillColor = getStateFillColor(state); 1029 t.speed = getStateSpeed(state); 1030 t.isPenDown = getStateIsPenDown(state); 1031 t.isFilling = getStateIsFilling(state); 1032 t.isVisible = getStateIsVisible(state); 1033 if (refreshMode == REFRESH_MODE_STATE_CHANGE) draw(); 1034 } 1035 1036 private void select() { 1037 selectedTurtle = this; 1038 } 1039 1040 private void unselect() { 1041 if (selectedTurtle == this) selectedTurtle = null; 1042 } 1043 1044 private void output(String message) { 1045 out.println("Instruction: " + message); 1046 out.printf("Current Pos: (%.3f, %.3f)\n", location.getX(), location.getY()); 1047 } 1048 1049 /** 1050 * Determines if a turtle is covering a screen position 1051 * 1052 * @param x x screen coordinate 1053 * @param y y screen coordinate 1054 * @return true if this turtle is at the indicated screen position. 1055 */ 1056 public boolean contains(int x, int y) { 1057 Point2D.Double point = new Point2D.Double(x, y); 1058 if (_location == null) return false; 1059 AffineTransform m = offscreen.getTransform(); 1060 double x1, y1, dir1; 1061 x1 = _location.x; 1062 y1 = _location.y; 1063 dir1 = _direction; 1064 m.translate(((x1 - centerX) * scale + width / 2), ((y1 - centerY) * (-scale) + height / 2)); 1065 m.scale(scale, scale); 1066 if (image == null) { 1067 m.rotate(-Math.toRadians(dir1)); 1068 m.scale(shapeWidth / 100.0, shapeHeight / 100.0); 1069 m.translate(-50, -50); 1070 Polygon p = shapes.get(shape); 1071 GeneralPath gp = new GeneralPath(); 1072 gp.append(p.getPathIterator(m), false); 1073 return gp.contains(x, y); 1074 } else { 1075 int w = image.getWidth(); 1076 int h = image.getHeight(); 1077 m.rotate(-Math.toRadians(dir1)); 1078 m.scale(shapeWidth / 1.0 / w, shapeHeight / 1.0 / h); 1079 m.translate(-w / 2, -h / 2); 1080 try { 1081 m.inverseTransform(point, point); 1082 } catch (Exception e) { 1083 return false; 1084 } 1085 x = (int) point.x; 1086 y = (int) point.y; 1087 try { 1088 //System.out.println((new Color(image.getRGB(x, y),true)).getAlpha()); 1089 return (new Color(image.getRGB(x, y), true)).getAlpha() > 50; 1090 } catch (Exception e) { 1091 return false; 1092 } 1093 } 1094 } 1095 1096 /* .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 1097 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 1098 * U U U U U U U U U U U U U U U U 1099 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 1100 * _/___\/ _/___\/ _/___\/ TURTLE METHODS _/___\/ _/___\/ _/___\/ 1101 * U U U U U U U U U U U U 1102 * .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) .-./*) 1103 * _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ _/___\/ 1104 * U U U U U U U U U U U U U U U U 1105 */ 1106 1107 /** 1108 * Gets the speed of the animation. 1109 * @return milliseconds it takes to do one action 1110 */ 1111 public double getSpeed() { 1112 return speed; 1113 } 1114 1115 /** 1116 * Sets the speed of the animation. 1117 * 1118 * <pre> 1119 * Turtle t = new Turtle(); 1120 * t.speed(1000); 1121 * t.forward(100); // takes 1000 ms (1 second) to move forward 100 units 1122 * t.forward(5); // takes 1000 ms (1 second) to move forward 5 units 1123 * t.speed(100); 1124 * t.left(90); // takes 100 ms (0.1 second) to turn left 90 degrees 1125 * </pre> 1126 * 1127 * @param delay milliseconds it takes to do one action 1128 * @return state change timestamp 1129 */ 1130 public long speed(double delay) { 1131 this.speed = delay; 1132 long timeStamp = storeCurrentState(); 1133 return timeStamp; 1134 } 1135 1136 /** 1137 * Moves the turtle forward by the given distance. Each unit of distance 1138 * is one pixel at the default zoom level. 1139 * <p> 1140 * The example below constructs a turtle and moves it forward. 1141 * If the canvas is zoomed to the default zoom level, then the 1142 * distance moved is 50 pixels. 1143 * </p> 1144 * <pre> 1145 * Turtle t = new Turtle(); 1146 * t.forward(50); 1147 * </pre> 1148 * 1149 * @param distance number of units to move forward 1150 * @return state change timestamp 1151 */ 1152 public long forward(double distance) { 1153 double angle = Math.toRadians(direction); 1154 Point2D.Double pastLocation = (Point2D.Double) location.clone(); 1155 location.x += distance * Math.cos(angle); 1156 location.y += distance * Math.sin(angle); 1157 long timeStamp = storeAnimatedState(); 1158 1159 output("MOVE FORWARD " + distance); 1160 return timeStamp; 1161 } 1162 1163 /** 1164 * Moves the turtle backward by the given distance. Each unit of distance is one 1165 * pixel at the default zoom level. 1166 * 1167 * @param distance number of units to move forward 1168 * @return state change timestamp 1169 */ 1170 public long backward(double distance) { 1171 double angle = Math.toRadians(direction); 1172 Point2D.Double pastLocation = (Point2D.Double) location.clone(); 1173 location.x -= distance * Math.cos(angle); 1174 location.y -= distance * Math.sin(angle); 1175 long timeStamp = storeAnimatedState(); 1176 1177 output("MOVE BACKWARD " + distance); 1178 return timeStamp; 1179 } 1180 1181 /** 1182 * Turns the turtle left by the number of indicated degrees. 1183 * 1184 * @param angle angle in degrees 1185 * @return state change timestamp 1186 */ 1187 public long left(double angle) { 1188 direction += angle; 1189 long timeStamp = storeAnimatedState(); 1190 1191 output("TURN LEFT " + angle); 1192 return timeStamp; 1193 } 1194 1195 /** 1196 * Turns the turtle right by the number of indicated degrees. 1197 * 1198 * @param angle angle in degrees 1199 * @return state change timestamp 1200 */ 1201 public long right(double angle) { 1202 direction -= angle; 1203 long timeStamp = storeAnimatedState(); 1204 1205 output("TURN RIGHT " + angle); 1206 return timeStamp; 1207 } 1208 1209 /** 1210 * Gets the direction the turtle is facing neglecting tilt. 1211 * 1212 * @return state change timestamp 1213 */ 1214 public double getDirection() { 1215 double a = direction; 1216 while (a >= 360) a -= 360; 1217 while (a < 0) a += 360; 1218 return a; 1219 } 1220 1221 /** 1222 * Sets the direction the turtle is facing neglecting tilt. 1223 * 1224 * @param direction angle counter-clockwise from east 1225 * @return state change timestamp 1226 */ 1227 public long setDirection(double direction) { 1228 double a = direction; 1229 while (this.direction - a > 180) a += 360; 1230 while (this.direction - a < -180) a -= 360; 1231 this.direction = a; 1232 //this.direction=direction; 1233 long timeStamp = storeAnimatedState(); 1234 1235 output("SET DIRECTION " + a); 1236 return timeStamp; 1237 } 1238 1239 /** 1240 * Moves the turtle to (0,0) and facing east. 1241 * 1242 * @return state change timestamp 1243 */ 1244 public long home() { 1245 output("MOVE HOME"); 1246 return setPosition(0, 0, 0); 1247 } 1248 1249 /** 1250 * Hides the turtle but it can still draw. 1251 * 1252 * @return state change timestamp 1253 */ 1254 public long hide() { 1255 isVisible = false; 1256 output("HIDING TURTLE"); 1257 long timeStamp = storeCurrentState(); 1258 return timeStamp; 1259 } 1260 1261 /** 1262 * Makes the turtle visible. 1263 * 1264 * @return state change timestamp 1265 */ 1266 public long show() { 1267 isVisible = true; 1268 output("SHOWING TURTLE"); 1269 long timeStamp = storeCurrentState(); 1270 return timeStamp; 1271 } 1272 1273 /** 1274 * Sets the direction in such a way that it faces (x,y) 1275 * 1276 * @param x x coordinate of target location 1277 * @param y y coordinate of target location 1278 * @return state change timestamp 1279 */ 1280 public long face(double x, double y) { 1281 return setDirection(towards(x, y)); 1282 } 1283 1284 /** 1285 * Gets direction towards (x,y) 1286 * 1287 * @param x x coordinate of target location 1288 * @param y y coordinate of target location 1289 * @return angle in degrees where 0 <= angle < 1290 */ 1291 public double towards(double x, double y) { 1292 return Math.toDegrees(Math.atan2(y - location.y, x - location.x)); 1293 } 1294 1295 /** 1296 * Gets the distance to another position. 1297 * 1298 * @param x x coordinate of target location 1299 * @param y y coordinate of target location 1300 * @return distance between turtle's current location and another position 1301 */ 1302 public double distance(double x, double y) { 1303 return Math.sqrt((y - location.y) * (y - location.y) + (x - location.x) * (x - location.x)); 1304 } 1305 1306 /** 1307 * Gets the x coordinate of the turtle. 1308 * 1309 * @return x coordinate 1310 */ 1311 public double getX() { 1312 return location.x; 1313 } 1314 1315 /** 1316 * Gets the y coordinate of the turtle. 1317 * 1318 * @return y coordinate 1319 */ 1320 public double getY() { 1321 return location.y; 1322 } 1323 1324 /** 1325 * Sets the position and direction of a turtle. 1326 * 1327 * @param x x coordinate 1328 * @param y y coordinate 1329 * @param direction angle counter-clockwise from east in degrees 1330 * @return state change timestamp 1331 */ 1332 public long setPosition(double x, double y, double direction) { 1333 location.x = x; 1334 location.y = y; 1335 double a = direction; 1336 while (this.direction - a > 180) a += 360; 1337 while (this.direction - a < -180) a -= 360; 1338 this.direction = a; 1339 this.direction = direction; 1340 1341 output(String.format("SET POSITION (%f, %f) DIRECTION %f", x, y, direction)); 1342 long timeStamp = storeAnimatedState(); 1343 return timeStamp; 1344 } 1345 1346 /** 1347 * Sets the position of a turtle. 1348 * 1349 * @param x x coordinate 1350 * @param y y coordinate 1351 * @return state change timestamp 1352 */ 1353 public long setPosition(double x, double y) { 1354 return setPosition(x, y, direction); 1355 } 1356 1357 /** 1358 * Adds an additional angle to rotation of the turtle's shape when rendering. 1359 * This is useful when you need to face a different direction than the 1360 * direction you are moving in. 1361 * 1362 * @param angle angle in degrees 1363 * @return state change timestamp 1364 */ 1365 public long tilt(double angle) { 1366 tilt += angle; 1367 1368 output("ADD TILT " + angle); 1369 1370 long timeStamp = storeAnimatedState(); 1371 return timeStamp; 1372 } 1373 1374 /** 1375 * Sets the angle to rotate the turtle's shape when rendering. 1376 * This is useful when you need to face a different direction than the 1377 * direction you are moving in. 1378 * 1379 * @param angle angle in degrees 1380 * @return state change timestamp 1381 */ 1382 public long setTilt(double angle) { 1383 //double a=angle; 1384 //while(tilt-a>180)a+=360; 1385 //while(tilt-a<-180)a-=360; 1386 //tilt=a; 1387 tilt = angle; 1388 1389 output("SET TILT " + angle); 1390 1391 long timeStamp = storeAnimatedState(); 1392 return timeStamp; 1393 } 1394 1395 /** 1396 * Gets the rotation of the turtle's shape away from the turtle's direction. 1397 * 1398 * @return tilt in degrees (positive in counter-clockwise) 1399 */ 1400 public double getTilt() { 1401 return tilt; 1402 } 1403 1404 /** 1405 * Sets the width of the turtle's pen. Each unit of width corresponds to 1406 * 1 pixel at the default zoom level. 1407 * 1408 * 1409 * @param penWidth number of units wide 1410 * @return state change timestamp 1411 */ 1412 public long width(double penWidth) { 1413 this.penWidth = penWidth; 1414 1415 output("SET PEN WIDTH " + penWidth); 1416 long timeStamp = storeCurrentState(); 1417 return timeStamp; 1418 } 1419 1420 /** 1421 * Sets the width of the turtle's outline. 1422 * 1423 * @param width width of the turtle's outline 1424 * @return state change timestamp 1425 */ 1426 public long outlineWidth(double width) { 1427 this.outlineWidth = width; 1428 1429 output("SET OUTLINE WIDTH " + width); 1430 long timeStamp = storeCurrentState(); 1431 return timeStamp; 1432 } 1433 1434 /** 1435 * Picks the turtle's pen up so it won't draw on the screen as it moves. 1436 * 1437 * @return state change timestamp 1438 */ 1439 public long up() { 1440 this.isPenDown = false; 1441 output("LIFT PEN"); 1442 long timeStamp = storeCurrentState(); 1443 return timeStamp; 1444 } 1445 1446 /** 1447 * Puts the turtle's pen down so it will draw on the screen as it moves. 1448 * 1449 * @return state change timestamp 1450 */ 1451 public long down() { 1452 this.isPenDown = true; 1453 output("LOWER PEN"); 1454 long timeStamp = storeCurrentState(); 1455 return timeStamp; 1456 } 1457 1458 public long stab() { 1459 Color c = Turtle.getColor("red"); 1460 if (c != null) this.penColor = c; 1461 this.isPenDown = true; 1462 1463 output("STAB"); 1464 long timeStamp = storeCurrentState(); 1465 return timeStamp; 1466 } 1467 1468 /** 1469 * Sets the turtle's path color. 1470 * 1471 * @param penColor Color of the turtle's path. 1472 * @return state change timestamp 1473 */ 1474 public long penColor(String penColor) { 1475 Color c = Turtle.getColor(penColor); 1476 if (c != null) this.penColor = c; 1477 output("SET PEN COLOR " + c); 1478 long timeStamp = storeCurrentState(); 1479 return timeStamp; 1480 } 1481 1482 /** 1483 * Sets the turtle's path color. 1484 * 1485 * @param penColor Color of the turtle's path. 1486 * @return state change timestamp 1487 */ 1488 public long penColor(Color penColor) { 1489 this.penColor = penColor; 1490 1491 output("SET PEN COLOR " + penColor); 1492 long timeStamp = storeCurrentState(); 1493 return timeStamp; 1494 } 1495 1496 /** 1497 * Sets the turtle's outlineColor color. 1498 * 1499 * @param outlineColor Color of the turtle's outlineColor. 1500 * @return state change timestamp 1501 */ 1502 public long outlineColor(String outlineColor) { 1503 Color c = Turtle.getColor(outlineColor); 1504 if (c != null) this.outlineColor = c; 1505 1506 output("SET OUTLINE COLOR " + c); 1507 long timeStamp = storeCurrentState(); 1508 return timeStamp; 1509 } 1510 1511 /** 1512 * Sets the turtle's outlineColor color. 1513 * 1514 * @param outlineColor Color of the turtle's outlineColor. 1515 * @return state change timestamp 1516 */ 1517 public long outlineColor(Color outlineColor) { 1518 this.outlineColor = outlineColor; 1519 output("SET OUTLINE COLOR " + outlineColor); 1520 long timeStamp = storeCurrentState(); 1521 return timeStamp; 1522 } 1523 1524 /** 1525 * Sets the turtle's fill color. 1526 * 1527 * @param fillColor Color of the turtle's fill. 1528 * @return state change timestamp 1529 */ 1530 public long fillColor(String fillColor) { 1531 Color c = Turtle.getColor(fillColor); 1532 if (c != null) this.fillColor = c; 1533 1534 output("SET FILL COLOR " + c); 1535 long timeStamp = storeCurrentState(); 1536 return timeStamp; 1537 } 1538 1539 /** 1540 * Sets the turtle's fill color. 1541 * 1542 * @param fillColor Color of the turtle's fill. 1543 * @return state change timestamp 1544 */ 1545 public long fillColor(Color fillColor) { 1546 this.fillColor = fillColor; 1547 1548 output("SET FILL COLOR " + fillColor); 1549 long timeStamp = storeCurrentState(); 1550 return timeStamp; 1551 } 1552 1553 /** 1554 * Sets the shape of the turtle using the built in shapes (turtle,square, 1555 * rectangle,triangle,arrow,circle) or to a image. 1556 * 1557 * @param shape shapename or filename of image 1558 * @return state change timestamp 1559 */ 1560 public long shape(String shape) { 1561 try { 1562 image = ImageIO.read(new File(shape)); 1563 output("SET SHAPE " + shape); 1564 this.shapeHeight = image.getHeight(); 1565 this.shapeWidth = image.getWidth(); 1566 } catch (Exception e) { 1567 if (shapes.containsKey(shape)) { 1568 output("SET SHAPE " + shape); 1569 this.shape = shape; 1570 this.shapeHeight = 33; 1571 this.shapeWidth = 33; 1572 image = null; 1573 } else { 1574 System.out.println("Unrecognized filename or shape name."); 1575 } 1576 } 1577 //if(refreshMode!=REFRESH_MODE_ON_DEMAND)updateAll(); 1578 long timeStamp = storeCurrentState(); 1579 return timeStamp; 1580 } 1581 1582 public long shapeSize(int width, int height) { 1583 this.shapeHeight = height; 1584 this.shapeWidth = width; 1585 output(String.format("SET SHAPE SIZE (width=%d, height=%d)", width, height)); 1586 long timeStamp = storeCurrentState(); 1587 return timeStamp; 1588 } 1589 1590 /** 1591 * Put a copy of the current turtle shape on the canvas. 1592 * 1593 * @return state change timestamp 1594 */ 1595 public long stamp() { 1596 long timeStamp = storeCurrentState(true, true, 0, null, null, null, 0, null); 1597 output("LEAVING STAMP"); 1598 return timeStamp; 1599 } 1600 1601 /** 1602 * Put a dot 3 times the size of the penWidth on the canvas. 1603 * 1604 * @return state change timestamp 1605 */ 1606 public long dot() { 1607 long timeStamp = storeCurrentState(true, false, penWidth * 3, penColor, null, null, 0, null); 1608 output("LEAVING DOT"); 1609 return timeStamp; 1610 } 1611 1612 /** 1613 * Put a dot 3 times the size of the penWidth on the canvas. 1614 * 1615 * @param color color of dot 1616 * @return state change timestamp 1617 */ 1618 public long dot(String color) { 1619 Color c = Turtle.getColor(color); 1620 if (c == null) c = penColor; 1621 output("LEAVING DOT WITH COLOR " + c); 1622 long timeStamp = storeCurrentState(true, false, penWidth * 3, c, null, null, 0, null); 1623 return timeStamp; 1624 } 1625 1626 /** 1627 * Put a dot 3 times the size of the penWidth on the canvas. 1628 * 1629 * @param color color of dot 1630 * @return state change timestamp 1631 */ 1632 public long dot(Color color) { 1633 output("LEAVING DOT WITH COLOR " + color); 1634 long timeStamp = storeCurrentState(true, false, penWidth * 3, color, null, null, 0, null); 1635 return timeStamp; 1636 } 1637 1638 /** 1639 * Put a dot on the canvas. 1640 * 1641 * @param color color of dot 1642 * @param dotSize diameter of the dot 1643 * @return state change timestamp 1644 */ 1645 public long dot(String color, double dotSize) { 1646 Color c = Turtle.getColor(color); 1647 if (c == null) c = penColor; 1648 1649 output("LEAVING DOT WITH COLOR " + c + " DOT SIZE " + dotSize); 1650 long timeStamp = storeCurrentState(true, false, dotSize, c, null, null, 0, null); 1651 return timeStamp; 1652 } 1653 1654 /** 1655 * Put a dot on the canvas. 1656 * 1657 * @param color color of dot 1658 * @param dotSize diameter of the dot 1659 * @return state change timestamp 1660 */ 1661 public long dot(Color color, double dotSize) { 1662 output("LEAVING DOT WITH COLOR " + color + " DOT SIZE " + dotSize); 1663 long timeStamp = storeCurrentState(true, false, dotSize, color, null, null, 0, null); 1664 return timeStamp; 1665 } 1666 1667 public long write(String text, String fontName, int fontSize, int justification, double xOffset, double yOffset) { 1668 return 0; 1669 } 1670 1671 /** 1672 * Undo turtle state changes. 1673 * 1674 * @param steps the number of state changes to remove 1675 */ 1676 public void undo(int steps) { 1677 for (int i = 0; i < steps; i++) rollback(); 1678 lastUpdate = 0; 1679 1680 output("UNDOING LAST " + steps + " STEPS"); 1681 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1682 } 1683 1684 /** 1685 * Undo the last turtle state change. 1686 */ 1687 public void undo() { 1688 output("UNDO LAST STEP"); 1689 rollback(); 1690 lastUpdate = 0; 1691 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1692 } 1693 1694 /** 1695 * Redo turtle state changes. 1696 * 1697 * @param steps the number of state changes to restore 1698 */ 1699 public void redo(int steps) { 1700 output("REDO " + steps + " STEPS"); 1701 for (int i = 0; i < steps; i++) rollforward(); 1702 lastUpdate = 0; 1703 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1704 } 1705 1706 /** 1707 * Redo turtle state changes. 1708 */ 1709 public void redo() { 1710 output("REDO STEP"); 1711 rollforward(); 1712 lastUpdate = 0; 1713 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1714 } 1715 1716 /** 1717 * Clears all the drawing that a turtle has done but all the turtle 1718 * settings remain the same. (color, location, direction, shape) 1719 */ 1720 public void clear() { 1721 output("CLEAR DRAWING"); 1722 synchronized (turtleLock) { 1723 long removeKey = 0; 1724 TreeMap<Long, ArrayList> copy_turtleStates = (TreeMap<Long, ArrayList>) turtleStates.clone(); 1725 for (Map.Entry<Long, ArrayList> entry : copy_turtleStates.entrySet()) { 1726 ArrayList state = entry.getValue(); 1727 long time = entry.getKey(); 1728 if (getStateTurtle(state) == this) { 1729 if (removeKey != 0) { 1730 turtleStates.remove(removeKey); 1731 } 1732 removeKey = time; 1733 1734 } 1735 } 1736 redoStates.clear(); 1737 restoreState(removeKey); 1738 } 1739 lastUpdate = 0; 1740 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1741 } 1742 1743 private void rollback() { 1744 int steps = 0; 1745 1746 synchronized (turtleLock) { 1747 long removeKey = 0; 1748 long restoreTime = 0; 1749 for (Map.Entry<Long, ArrayList> entry : turtleStates.descendingMap().entrySet()) { 1750 ArrayList state = entry.getValue(); 1751 long time = entry.getKey(); 1752 if (getStateTurtle(state) == this) { 1753 if (steps == 0) { 1754 removeKey = time; 1755 steps += 1; 1756 } else { 1757 restoreTime = time; 1758 break; 1759 } 1760 } 1761 } 1762 if (removeKey != 0 && restoreTime != 0) { 1763 restoreState(restoreTime); 1764 redoStates.put(removeKey, turtleStates.remove(removeKey)); 1765 } 1766 } 1767 } 1768 1769 private void rollforward() { 1770 synchronized (turtleLock) { 1771 for (Map.Entry<Long, ArrayList> entry : redoStates.entrySet()) { 1772 ArrayList state = entry.getValue(); 1773 long time = entry.getKey(); 1774 if (getStateTurtle(state) == this) { 1775 turtleStates.put(entry.getKey(), redoStates.remove(entry.getKey())); 1776 restoreState(time); 1777 return; 1778 } 1779 } 1780 } 1781 } 1782 1783 /** 1784 * This specifies when the screen gets refreshed. 1785 * 0(default)=Animated (The turtle will slide from one state to another without being jerky.) 1786 * 1=State Change (The turtle will refresh immediately to the last state. Jerky motion.) 1787 * 2=On Demand (The turtle will refresh only when you call update()) 1788 * 1789 * @param mode refresh mode 1790 */ 1791 public static void refreshMode(int mode) { 1792 refreshMode = mode; 1793 updateAll(); 1794 } 1795 1796 /** 1797 * This specifies how the background is drawn. 1798 * 0=The image if present is stretched to fill the screen. 1799 * 1=The image is centered on the middle of the screen and will not scale/pan 1800 * 2=The image is tiled and will not scale/pan 1801 * 3=The image is centered on (0,0) and will scale/pan 1802 * 4(default)=The image is tiled and will scale/pan 1803 * 1804 * @param mode background mode 1805 */ 1806 public static void backgroundMode(int mode) { 1807 backgroundMode = mode; 1808 updateAll(); 1809 } 1810 1811 /** 1812 * Sets the background color. 1813 * 1814 * @param color Color of the background. 1815 */ 1816 public static void bgcolor(String color) { 1817 Color c = Turtle.getColor(color); 1818 if (c != null) backgroundColor = c; 1819 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1820 } 1821 1822 /** 1823 * Sets the background color. 1824 * 1825 * @param color Color of the background. 1826 */ 1827 public static void bgcolor(Color color) { 1828 backgroundColor = color; 1829 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1830 } 1831 1832 /** 1833 * Set the background image. 1834 * 1835 * @param filename filename for a background image 1836 */ 1837 public static void bgpic(String filename) { 1838 try { 1839 backgroundImage = ImageIO.read(new File(filename)); 1840 } catch (Exception e) { 1841 e.printStackTrace(); 1842 } 1843 if (refreshMode != REFRESH_MODE_ON_DEMAND) updateAll(); 1844 } 1845 1846 private static boolean addMouseBinding(String methodName, Turtle t, boolean append, boolean click, boolean repeat) { 1847 String className = ""; 1848 try { 1849 throw new Exception("Who called me?"); 1850 } catch (Exception e) { 1851 className = e.getStackTrace()[2].getClassName(); 1852 } 1853 try { 1854 boolean works = false; 1855 for (Method m : Class.forName(className).getDeclaredMethods()) { 1856 if (m.getName().equals(methodName)) { 1857 //System.out.println(m); 1858 works = true; 1859 for (Class paramType : m.getParameterTypes()) { 1860 //System.out.println(paramType.getName()); 1861 if (!paramType.getName().equals("double") && !paramType.getName().equals("java.lang.Double") && !paramType.getName().equals("Turtle")) { 1862 works = false; 1863 break; 1864 } 1865 } 1866 if (works) break; 1867 } 1868 } 1869 if (works) { 1870 //System.out.println("Method found!"); 1871 } else { 1872 System.out.println("ERROR"); 1873 return false; 1874 } 1875 } catch (Exception e) { 1876 System.out.println("Calling Class not found."); 1877 return false; 1878 } 1879 if (!append || !mouseBindings.containsKey(t)) mouseBindings.put(t, new ArrayList<ArrayList>()); 1880 ArrayList binding = new ArrayList(); 1881 binding.add(t); 1882 binding.add(className); 1883 binding.add(methodName); 1884 binding.add(click); 1885 binding.add(repeat); 1886 mouseBindings.get(t).add(binding); 1887 return true; 1888 } 1889 1890 private boolean addKeyBinding(String methodName, String keyText, boolean append, boolean repeat) { 1891 keyText = keyText.toLowerCase(); 1892 String className = ""; 1893 try { 1894 throw new Exception("Who called me?"); 1895 } catch (Exception e) { 1896 className = e.getStackTrace()[2].getClassName(); 1897 } 1898 try { 1899 boolean works = false; 1900 for (Method m : Class.forName(className).getDeclaredMethods()) { 1901 if (m.getName().equals(methodName)) { 1902 //System.out.println(m); 1903 works = true; 1904 for (Class paramType : m.getParameterTypes()) { 1905 //System.out.println(paramType.getName()); 1906 if (!paramType.getName().equals("java.lang.String") && !paramType.getName().equals("Turtle")) { 1907 works = false; 1908 break; 1909 } 1910 } 1911 if (works) break; 1912 } 1913 } 1914 if (works) { 1915 //System.out.println("Method found!"); 1916 } else { 1917 System.out.println("ERROR"); 1918 return false; 1919 } 1920 } catch (Exception e) { 1921 System.out.println("Calling Class not found."); 1922 return false; 1923 } 1924 if (!append || !keyBindings.containsKey(keyText)) keyBindings.put(keyText, new ArrayList<ArrayList>()); 1925 ArrayList binding = new ArrayList(); 1926 binding.add(this); 1927 binding.add(className); 1928 binding.add(methodName); 1929 binding.add(repeat); 1930 keyBindings.get(keyText).add(binding); 1931 return true; 1932 } 1933 1934 /** 1935 * Links a method to a key. 1936 * 1937 * @param methodName method to be executed when the key is pressed 1938 * @param keyText key that triggers the method 1939 * @return boolean 1940 */ 1941 public boolean onKey(String methodName, String keyText) { 1942 return addKeyBinding(methodName, keyText, false, false); 1943 } 1944 1945 /** 1946 * Links a method to a key. 1947 * 1948 * @param methodName method to be executed when the key is pressed 1949 * @param keyText key that triggers the method 1950 * @param append true if you want to have multiple methods per key 1951 * @return boolean 1952 */ 1953 public boolean onKey(String methodName, String keyText, boolean append) { 1954 return addKeyBinding(methodName, keyText, append, false); 1955 } 1956 1957 /** 1958 * Links a method to a key. 1959 * 1960 * @param methodName method to be executed when the key is pressed 1961 * @param keyText key that triggers the method 1962 * @param append true if you want to have multiple methods per key 1963 * @param repeat true if you want call the method every screen refresh 1964 * @return boolean 1965 */ 1966 public boolean onKey(String methodName, String keyText, boolean append, boolean repeat) { 1967 return addKeyBinding(methodName, keyText, append, repeat); 1968 } 1969 1970 /** 1971 * 1972 * Fits the indicated box in the center of the screen as large as possible. 1973 * 1974 * @param minx left x coordinate of box 1975 * @param miny bottom y coordinate of box 1976 * @param maxx right x coordinate of box 1977 * @param maxy top y coordinate of box 1978 */ 1979 public static void zoom(double minx, double miny, double maxx, double maxy) { 1980 synchronized (turtleLock) { 1981 centerX = (minx + maxx) / 2; 1982 centerY = (miny + maxy) / 2; 1983 if (width / (maxx - minx) > height / (maxy - miny)) scale = height / (maxy - miny); 1984 else scale = width / (maxx - minx); 1985 updateAll(); 1986 } 1987 } 1988 1989 /** 1990 * Fits everything on the screen. 1991 */ 1992 public static void zoomFit() { 1993 synchronized (turtleLock) { 1994 Point2D.Double loc; 1995 if (turtleStates.isEmpty()) return; 1996 else loc = getStateLocation(turtleStates.firstEntry().getValue()); 1997 double minx = loc.x, miny = loc.y; 1998 double maxx = minx, maxy = miny; 1999 double shapeWidth = 0; 2000 double shapeHeight = 0; 2001 long time = System.nanoTime(); 2002 if (refreshMode != REFRESH_MODE_ANIMATED) time = turtleStates.lastKey() + 1; 2003 for (Map.Entry<Long, ArrayList> entry : turtleStates.headMap(time).entrySet()) { 2004 ArrayList state = entry.getValue(); 2005 if (!getStateIsPenDown(state)) continue; 2006 Point2D.Double location = getStateLocation(state); 2007 if (location.x < minx) minx = location.x; 2008 if (location.x > maxx) maxx = location.x; 2009 if (location.y < miny) miny = location.y; 2010 if (location.y > maxy) maxy = location.y; 2011 shapeWidth = getStateShapeWidth(state); 2012 shapeHeight = getStateShapeHeight(state); 2013 } 2014 2015 if (turtleStates.lastKey() > time && getStateSpeed(turtleStates.lastEntry().getValue()) > 0) { 2016 double percent = 1 - (turtleStates.lastKey() - time) / getStateSpeed(turtleStates.lastEntry().getValue()) / 1000000.0; 2017 //System.out.println("trying"); 2018 Turtle t = getStateTurtle(turtleStates.lastEntry().getValue()); 2019 double x1 = t._location.x, y1 = t._location.y, x2 = t.__location.x, y2 = t.__location.y; 2020 x1 = (x2 - x1) * percent + x1; 2021 y1 = (y2 - y1) * percent + y1; 2022 if (x1 < minx) minx = x1; 2023 if (x1 > maxx) maxx = x1; 2024 if (y1 < miny) miny = y1; 2025 if (y1 > maxy) maxy = y1; 2026 } 2027 double shapeMax = Math.max(shapeWidth, shapeHeight); 2028 zoom(minx - shapeMax / 2, miny - shapeMax / 2, maxx + shapeMax / 2, maxy + shapeMax / 2); 2029 } 2030 } 2031 2032 private static void updateAll() { 2033 lastUpdate = 0; 2034 draw(); 2035 } 2036 2037 /** 2038 * Force redraw when the refreshMode is set to on demand. 2039 */ 2040 public static void update() { 2041 if (refreshMode == REFRESH_MODE_ON_DEMAND) draw(); 2042 } 2043 2044 // Hunter: Made public for testing 2045 public static void draw() { 2046 synchronized (turtleLock) { 2047 2048 long renderTime = System.nanoTime(); 2049 if (turtleStates.isEmpty() || lastUpdate == 0) { 2050 clearStorage(); 2051 drawBackground(offscreen); 2052 } 2053 if (turtleStates.isEmpty()) { 2054 onscreen.drawImage(offscreenImage, 0, 0, null); 2055 window.repaint(); 2056 if (applet != null) applet.repaint(); 2057 return; 2058 } 2059 if (refreshMode != REFRESH_MODE_ANIMATED) renderTime = turtleStates.lastKey() + 1; 2060 if (lastUpdate > turtleStates.lastKey()) { 2061 midscreen.drawImage(offscreenImage, 0, 0, null); 2062 for (Turtle t : turtles) { 2063 if (t.isVisible) t.drawStamp(1, midscreen); 2064 //if(t==selectedTurtle)t.drawCrossHairs(1,midscreen); 2065 } 2066 onscreen.drawImage(midscreenImage, 0, 0, null); 2067 window.repaint(); 2068 if (applet != null) applet.repaint(); 2069 return; 2070 } 2071 for (Map.Entry<Long, ArrayList> entry : turtleStates.tailMap(lastUpdate).headMap(renderTime).entrySet()) { 2072 retrieveState(entry.getKey()); 2073 Turtle t = getStateTurtle(entry.getValue()); 2074 t.drawLine(1, offscreen); 2075 if (t._isStamp) t.drawStamp(1, offscreen); 2076 t.drawDot(1, offscreen); 2077 } 2078 2079 midscreen.drawImage(offscreenImage, 0, 0, null); 2080 Turtle animatedTurtle = null; 2081 double percent = 1; 2082 Long t2; 2083 t2 = Long.valueOf(0); 2084 if (renderTime < turtleStates.lastKey()) { 2085 animatedTurtle = getStateTurtle(turtleStates.ceilingEntry(renderTime).getValue()); 2086 t2 = animatedTurtle._time; 2087 retrieveState(turtleStates.ceilingKey(renderTime)); 2088 if (animatedTurtle._speed > 0) { 2089 percent = 1 - (turtleStates.ceilingKey(renderTime) - renderTime) / animatedTurtle._speed / 1000000.0; 2090 } else percent = 1; 2091 if (percent < 0) percent = 0; 2092 } 2093 2094 for (Turtle t : turtles) { 2095 if (t == animatedTurtle) { 2096 //System.out.println(percent); 2097 t.drawLine(percent, midscreen); 2098 if (t._dotSize > 0) t.drawDot(percent, midscreen); 2099 if (t.isVisible) t.drawStamp(percent, midscreen, false); 2100 if (t._isStamp) t.drawStamp(percent, midscreen, true); 2101 //if(t==selectedTurtle)t.drawCrossHairs(percent,midscreen); 2102 try { 2103 retrieveState(t2); 2104 } catch (Exception e) { 2105 } 2106 } else { 2107 if (t.isVisible) t.drawStamp(1, midscreen); 2108 //if(t==selectedTurtle)t.drawCrossHairs(1,midscreen); 2109 } 2110 2111 } 2112 lastUpdate = renderTime; 2113 //zoomFit(); 2114 onscreen.drawImage(midscreenImage, 0, 0, null); 2115 window.repaint(); 2116 if (applet != null) applet.repaint(); 2117 } 2118 2119 2120 } 2121 2122 private void drawLine(double percent, Graphics2D g) { 2123 if (!_isPenDown) return; 2124 g.setColor(_penColor); 2125 g.setStroke(new BasicStroke((float) (scale * _penWidth), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 2126 if (__location != null && !__location.equals(_location)) { 2127 double x1 = _location.x, y1 = _location.y, x2 = __location.x, y2 = __location.y; 2128 if (percent < 1 && percent >= 0) { 2129 x1 = (x1 - x2) * percent + x2; 2130 y1 = (y1 - y2) * percent + y2; 2131 } 2132 //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)); 2133 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)); 2134 } 2135 } 2136 2137 private void drawStamp(double percent, Graphics2D g) { 2138 drawStamp(percent, g, false); 2139 } 2140 2141 private void drawStamp(double percent, Graphics2D g, boolean isStamp) { 2142 if (_location == null) return; 2143 AffineTransform originalTransform = (AffineTransform) g.getTransform().clone(); 2144 AffineTransform m = g.getTransform(); 2145 double x1, x2, y1, y2, dir1, dir2, tilt1, tilt2; 2146 x1 = _location.x; 2147 y1 = _location.y; 2148 dir1 = _direction; 2149 tilt1 = _tilt; 2150 if (__location == null) { 2151 x2 = x1; 2152 y2 = y1; 2153 dir2 = dir1; 2154 tilt2 = tilt1; 2155 } else { 2156 x2 = __location.x; 2157 y2 = __location.y; 2158 dir2 = __direction; 2159 tilt2 = __tilt; 2160 } 2161 if (percent < 1 && percent >= 0) { 2162 x1 = (x1 - x2) * percent + x2; 2163 y1 = (y1 - y2) * percent + y2; 2164 dir1 = (dir1 - dir2) * percent + dir2; 2165 tilt1 = (tilt1 - tilt2) * percent + tilt2; 2166 } 2167 m.translate(((x1 - centerX) * scale + width / 2), ((y1 - centerY) * (-scale) + height / 2)); 2168 if (isStamp) m.scale(scale * percent, scale * percent); 2169 else m.scale(scale, scale); 2170 if (_image == null) { 2171 //_outlineWidth=0.0; 2172 m.rotate(-Math.toRadians(dir1 + tilt1)); 2173 m.scale(_shapeWidth / 100.0, _shapeHeight / 100.0); 2174 m.translate(-50, -50); 2175 g.setTransform(m); 2176 Polygon p = shapes.get(_shape); 2177 g.setColor(_fillColor); 2178 g.fillPolygon(p); 2179 g.setColor(_outlineColor); 2180 if (_outlineWidth > 0) { 2181 g.setStroke(new BasicStroke((float) (_outlineWidth * scale), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 2182 g.setTransform(originalTransform); 2183 GeneralPath gp = new GeneralPath(); 2184 gp.append(p.getPathIterator(m), false); 2185 g.draw(gp); 2186 } 2187 } else { 2188 int w = _image.getWidth(); 2189 int h = _image.getHeight(); 2190 m.rotate(-Math.toRadians(dir1 + tilt1)); 2191 m.scale(_shapeWidth / 1.0 / w, _shapeHeight / 1.0 / h); 2192 m.translate(-w / 2, -h / 2); 2193 g.setTransform(m); 2194 g.drawImage(_image, 0, 0, null); 2195 } 2196 g.setTransform(originalTransform); 2197 } 2198 2199 private void drawDot(double percent, Graphics2D g) { 2200 AffineTransform originalTransform = (AffineTransform) g.getTransform().clone(); 2201 AffineTransform m = g.getTransform(); 2202 m.translate(((_location.x - centerX) * scale + width / 2), ((_location.y - centerY) * (-scale) + height / 2)); 2203 m.scale(scale * percent / 2, scale * percent / 2); 2204 g.setTransform(m); 2205 g.setColor(_dotColor); 2206 int r = (int) (_dotSize * 1.0); 2207 g.fillOval(-r, -r, 2 * r, 2 * r); 2208 g.setTransform(originalTransform); 2209 } 2210 2211 private static void drawBackground(Graphics2D g) { 2212 g.setColor(backgroundColor); 2213 g.fillRect(0, 0, width, height); 2214 if (backgroundImage == null) return; 2215 int w = backgroundImage.getWidth(); 2216 int h = backgroundImage.getHeight(); 2217 if (backgroundMode == BACKGROUND_MODE_CENTER) { 2218 offscreen.drawImage(backgroundImage, (width - w) / 2, (height - h) / 2, w, h, null); 2219 } else if (backgroundMode == BACKGROUND_MODE_STRETCH) { 2220 offscreen.drawImage(backgroundImage, 0, 0, width, height, null); 2221 } else if (backgroundMode == BACKGROUND_MODE_CENTER_RELATIVE) { 2222 offscreen.drawImage(backgroundImage, (int) ((-w / 2 - centerX) * scale + width / 2), (int) ((h / 2 - centerY) * (-scale) + height / 2), (int) (w * scale), (int) (h * scale), null); 2223 } else if (backgroundMode == BACKGROUND_MODE_TILE) { 2224 for (int i = 0; i < width; i += w) 2225 for (int j = 0; j < height; j += h) offscreen.drawImage(backgroundImage, i, j, w, h, null); 2226 } else if (backgroundMode == BACKGROUND_MODE_TILE_RELATIVE) { 2227 double left = centerX - width / 2 / scale; 2228 double top = centerY + height / 2 / scale; 2229 double right = centerX + width / 2 / scale; 2230 double bottom = centerY - height / 2 / scale; 2231 for (double x = ((int) (left / w) - 1) * w; x <= right; x += w) 2232 for (double y = ((int) (bottom / h)) * h; y <= top + h; y += h) 2233 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); 2234 } 2235 } 2236 2237 private void drawCrossHairs(double percent, Graphics2D g) { 2238 if (_location == null) return; 2239 double time = (System.nanoTime() / 100000000) / 10.0; 2240 2241 AffineTransform originalTransform = (AffineTransform) g.getTransform().clone(); 2242 AffineTransform m = g.getTransform(); 2243 double x1, x2, y1, y2, dir1, dir2; 2244 x1 = _location.x; 2245 y1 = _location.y; 2246 if (__location == null) { 2247 x2 = x1; 2248 y2 = y1; 2249 } else { 2250 x2 = __location.x; 2251 y2 = __location.y; 2252 } 2253 if (percent < 1 && percent >= 0) { 2254 x1 = (x1 - x2) * percent + x2; 2255 y1 = (y1 - y2) * percent + y2; 2256 } 2257 m.translate(((x1 - centerX) * scale + width / 2), ((y1 - centerY) * (-scale) + height / 2)); 2258 int f = 10; 2259 m.scale(scale / f, scale / f); 2260 g.setTransform(m); 2261 2262 int period = 50; 2263 int r = (int) (Math.sqrt(shapeWidth * shapeWidth + shapeHeight * shapeHeight) * f / 2); 2264 g.setColor(new Color(255, 255, 255)); 2265 g.setStroke(new BasicStroke((float) (6 * f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 2266 g.drawOval(-r, -r, 2 * r, 2 * r); 2267 r += f; 2268 for (int i = 0; i < 4; i++) 2269 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)), 2270 (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))); 2271 r -= f; 2272 g.setColor(new Color(0, 0, 0)); 2273 g.setStroke(new BasicStroke((float) (3 * f), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 2274 g.drawOval(-r, -r, 2 * r, 2 * r); 2275 r += f; 2276 for (int i = 0; i < 4; i++) 2277 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)), 2278 (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))); 2279 g.setTransform(originalTransform); 2280 } 2281 2282 /** 2283 * Changes the size of the canvas effectively changing the size of the window. 2284 * 2285 * @param width width of the canvas 2286 * @param height height of the canvas 2287 */ 2288 public static void setCanvasSize(int width, int height) { 2289 Turtle.width = width; 2290 Turtle.height = height; 2291 Turtle.setupBuffering(); 2292 window.pack(); 2293 updateAll(); 2294 } 2295 2296 /** 2297 * Saves the visible canvas to an image. 2298 * 2299 * @param filename image filename 2300 */ 2301 public static void save(String filename) { 2302 save(new File(filename)); 2303 } 2304 2305 private static void save(File file) { 2306 WritableRaster raster = onscreenImage.getRaster(); 2307 WritableRaster newRaster; 2308 newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[]{0, 1, 2}); 2309 DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel(); 2310 DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), 2311 cm.getRedMask(), 2312 cm.getGreenMask(), 2313 cm.getBlueMask()); 2314 BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); 2315 try { 2316 String suffix = file.getName().substring(file.getName().lastIndexOf('.') + 1); 2317 if (!ImageIO.write(rgbBuffer, suffix, file)) throw new Exception("Didn't save file."); 2318 } catch (Exception e) { 2319 file.delete(); 2320 JOptionPane.showMessageDialog(window, 2321 "Sorry! We can not process your request at this time.", 2322 "Image Save Failed", 2323 JOptionPane.ERROR_MESSAGE); 2324 } 2325 } 2326 2327 /** 2328 * Demo program 2329 * 2330 * @param a commandline args 2331 */ 2332 public static void main(String[] a) { 2333 //Turtle bob = new Turtle(); 2334 /*for(int i=0;i<360;i++) 2335 { 2336 bob.forward(i*1.25); 2337 bob.left(90.25); 2338 } 2339 */ 2340 /*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.*/ 2341 Turtle bob = new Turtle(); 2342 bgcolor("lightblue"); 2343 bob.penColor("red"); 2344 bob.width(10); 2345 for (int i = 0; i < 200; i++) { 2346 bob.forward(i / 10.); 2347 bob.left(5); 2348 if (i % 10 == 0) bob.dot("orange");//Draws dots when i is a multiple of 10. 2349 } 2350 bob.saveGCODE("test.gcode"); 2351 2352 } 2353 2354 /** 2355 * Internal mehod for handling events. 2356 * @param e event 2357 */ 2358 public void actionPerformed(ActionEvent e) { 2359 if (((JMenuItem) e.getSource()).getText().equals("Save...")) { 2360 JFileChooser chooser = new JFileChooser(System.getProperty("user.dir")); 2361 chooser.setFileFilter(new FileNameExtensionFilter("Image (*.jpg, *.jpeg, *.gif, *.bmp, *.png)", "jpg", "png", "jpeg", "bmp", "gif")); 2362 int option = chooser.showSaveDialog(window); 2363 if (option == JFileChooser.APPROVE_OPTION) { 2364 if (chooser.getSelectedFile() != null) { 2365 File file = chooser.getSelectedFile(); 2366 save(file); 2367 } 2368 } 2369 } 2370 } 2371 2372 /** 2373 * Internal mehod for handling events. 2374 * @param e event 2375 */ 2376 public void mouseClicked(MouseEvent e) { 2377 if (e.getModifiers() == 8 && e.getClickCount() == 2) { 2378 centerX = 0; 2379 centerY = 0; 2380 scale = 1; 2381 updateAll(); 2382 } 2383 } 2384 2385 /** 2386 * Internal mehod for handling events. 2387 * @param e event 2388 */ 2389 public void mouseEntered(MouseEvent e) { 2390 } 2391 2392 /** 2393 * Internal mehod for handling events. 2394 * @param e event 2395 */ 2396 public void mouseExited(MouseEvent e) { 2397 } 2398 2399 /** 2400 * Internal mehod for handling events. 2401 * @param e event 2402 */ 2403 public void mousePressed(MouseEvent e) { 2404 dragx = e.getX(); 2405 dragy = e.getY(); 2406 modifiers += e.getModifiers(); 2407 synchronized (turtleLock) { 2408 for (Turtle t : turtles) { 2409 if (t.contains(dragx, dragy)) t.select(); 2410 else t.unselect(); 2411 } 2412 } 2413 } 2414 2415 /** 2416 * Internal mehod for handling events. 2417 * @param e event 2418 */ 2419 public void mouseReleased(MouseEvent e) { 2420 modifiers -= e.getModifiers(); 2421 } 2422 2423 /** 2424 * Internal mehod for handling events. 2425 * @param e event 2426 */ 2427 public void mouseDragged(MouseEvent e) { 2428 modifiers = e.getModifiers(); 2429 int dx, dy; 2430 if (e.getModifiers() == 8) { 2431 x = e.getX(); 2432 dx = x - dragx; 2433 y = e.getY(); 2434 dy = y - dragy; 2435 dragx = x; 2436 dragy = y; 2437 synchronized (turtleLock) { 2438 centerX -= dx * 1.0 / scale; 2439 centerY += dy * 1.0 / scale; 2440 } 2441 updateAll(); 2442 } 2443 this.x = e.getX(); 2444 this.y = e.getY(); 2445 } 2446 2447 /** 2448 * Internal mehod for handling events. 2449 * @param e event 2450 */ 2451 public void mouseMoved(MouseEvent e) { 2452 modifiers = e.getModifiers(); 2453 x = e.getX(); 2454 y = e.getY(); 2455 2456 } 2457 2458 /** 2459 * Internal mehod for handling events. 2460 * @param e event 2461 */ 2462 public void keyTyped(KeyEvent e) { 2463 } 2464 2465 /** 2466 * Internal mehod for handling events. 2467 * @param e event 2468 */ 2469 public void keyPressed(KeyEvent e) { 2470 String keyText = KeyEvent.getKeyText(e.getKeyCode()).toLowerCase(); 2471 synchronized (keyLock) { 2472 keysDown.add(keyText); 2473 if (keyBindings.containsKey(keyText)) { 2474 unprocessedKeys.add(keyText); 2475 } 2476 } 2477 } 2478 2479 /** 2480 * Internal mehod for handling events. 2481 * @param e event 2482 */ 2483 public void keyReleased(KeyEvent e) { 2484 String keyText = KeyEvent.getKeyText(e.getKeyCode()).toLowerCase(); 2485 synchronized (keyLock) { 2486 keysDown.remove(keyText); 2487 processedKeys.remove(keyText); 2488 } 2489 } 2490 2491 private void processKeys() { 2492 //System.out.println(keysDown); 2493 TreeSet<String> keysDownCopy = new TreeSet<String>(); 2494 synchronized (keyLock) { 2495 keysDownCopy = (TreeSet<String>) keysDown.clone(); 2496 } 2497 keysDownCopy.addAll(unprocessedKeys); 2498 for (String keyText : keysDownCopy) { 2499 if (keyBindings.containsKey(keyText)) { 2500 for (ArrayList binding : keyBindings.get(keyText)) { 2501 Turtle t = (Turtle) binding.get(0); 2502 String className = (String) binding.get(1); 2503 String methodName = (String) binding.get(2); 2504 Boolean repeat = (Boolean) binding.get(3); 2505 if (!repeat && processedKeys.contains(keyText)) break; 2506 unprocessedKeys.remove(keyText); 2507 processedKeys.add(keyText); 2508 try { 2509 Class cls = Class.forName(className); 2510 Object clsInstance = (Object) cls.newInstance(); 2511 Method m = clsInstance.getClass().getMethod(methodName, t.getClass()); 2512 m.invoke(clsInstance, t); 2513 } catch (Exception e1) { 2514 try { 2515 Class cls = Class.forName(className); 2516 Object clsInstance = (Object) cls.newInstance(); 2517 Method m = clsInstance.getClass().getMethod(methodName, t.getClass(), keyText.getClass()); 2518 m.invoke(clsInstance, t, keyText); 2519 } catch (Exception e2) { 2520 try { 2521 Class cls = Class.forName(className); 2522 Object clsInstance = (Object) cls.newInstance(); 2523 Method m = clsInstance.getClass().getMethod(methodName); 2524 m.invoke(clsInstance); 2525 } catch (Exception e3) { 2526 System.out.println("KeyBinding for " + keyText + " has failed."); 2527 e1.printStackTrace(); 2528 e2.printStackTrace(); 2529 e3.printStackTrace(); 2530 } 2531 } 2532 } 2533 } 2534 } 2535 } 2536 } 2537 2538 /** 2539 * Internal mehod for handling events. 2540 * @param e event 2541 */ 2542 public void componentHidden(ComponentEvent e) { 2543 } 2544 2545 /** 2546 * Internal mehod for handling events. 2547 * @param e event 2548 */ 2549 public void componentMoved(ComponentEvent e) { 2550 } 2551 2552 /** 2553 * Internal mehod for handling events. 2554 * @param e event 2555 */ 2556 public void componentResized(ComponentEvent e) { 2557 width = (int) draw.getBounds().getWidth(); 2558 height = (int) draw.getBounds().getHeight(); 2559 setupBuffering(); 2560 updateAll(); 2561 } 2562 2563 /** 2564 * Internal mehod for handling events. 2565 * @param e event 2566 */ 2567 public void componentShown(ComponentEvent e) { 2568 } 2569 2570 /** 2571 * Internal mehod for handling events. 2572 * @param e event 2573 */ 2574 public void mouseWheelMoved(MouseWheelEvent e) { 2575 int notches = e.getWheelRotation(); 2576 double ds = Math.pow(1.1, notches); 2577 x = e.getX(); 2578 y = e.getY(); 2579 double dx = width / 2 - x; 2580 double dy = height / 2 - y; 2581 synchronized (turtleLock) { 2582 centerX -= (dx * ds - dx) / scale / ds; 2583 centerY += (dy * ds - dy) / scale / ds; 2584 scale *= ds; 2585 } 2586 updateAll(); 2587 } 2588 2589 /** 2590 * Get the pressed keys. 2591 * 2592 * @return a list of pressed keys 2593 */ 2594 public static String[] keysDown() { 2595 return keysDown.toArray(new String[]{}); 2596 } 2597 2598 /** 2599 * Test if a key is pressed or not. 2600 * 2601 * @param key key you are testing 2602 * @return true if the key is pressed 2603 */ 2604 public static boolean isKeyDown(String key) { 2605 return keysDown.contains(key); 2606 } 2607 2608 /** 2609 * Get the mouse x coordinate using the screens coordinate system. 2610 * 2611 * @return x coordinate 2612 */ 2613 public static int mouseX() { 2614 return turtle.x; 2615 } 2616 2617 /** 2618 * Get the mouse y coordinate using the screens coordinate system. 2619 * 2620 * @return y coordinate 2621 */ 2622 public static int mouseY() { 2623 return turtle.y; 2624 } 2625 2626 /** 2627 * Check to see if a mouse button is down. 2628 * 2629 * @return true if a button is down 2630 */ 2631 public static boolean mouseButton() { 2632 return mouseButton1() || mouseButton2() || mouseButton3(); 2633 } 2634 2635 /** 2636 * Check to see if the first mouse button is down. 2637 * 2638 * @return true if button 1 is down 2639 */ 2640 public static boolean mouseButton1() { 2641 return (turtle.modifiers & 16) == 16; 2642 } 2643 2644 /** 2645 * Check to see if the second mouse button is down. 2646 * 2647 * @return true if button 2 is down 2648 */ 2649 public static boolean mouseButton2() { 2650 return (turtle.modifiers & 8) == 8; 2651 } 2652 2653 /** 2654 * Check to see if the third mouse button is down. 2655 * 2656 * @return true if button 3 is down 2657 */ 2658 public static boolean mouseButton3() { 2659 return (turtle.modifiers & 4) == 4; 2660 } 2661 2662 /** 2663 * Converts screen coordinates to canvas coordinates. 2664 * 2665 * @param screenX screen x coordinate 2666 * @return canvas x coordinate 2667 */ 2668 public static double canvasX(double screenX) { 2669 return (screenX - width / 2.0) / scale + centerX; 2670 } 2671 2672 /** 2673 * Converts screen coordinates to canvas coordinates. 2674 * 2675 * @param screenY screen y coordinate 2676 * @return canvas y coordinate 2677 */ 2678 public static double canvasY(double screenY) { 2679 return (-screenY + height / 2.0) / scale + centerY; 2680 } 2681 2682 public static double screenX(double canvasX) { 2683 return (canvasX - centerX) * scale + width / 2.0; 2684 } 2685 2686 public static double screenY(double canvasY) { 2687 return (canvasY - centerY) * scale + height / 2.0; 2688 } 2689 2690 2691 private static void saveGCODE(String filename) { 2692 PrintWriter out = new PrintWriter(System.out); 2693 try { 2694 out = new PrintWriter(filename); 2695 } catch (Exception e) { 2696 2697 } 2698 out.println("M104 S200"); 2699 out.println("M109 S200"); 2700 out.println("G21"); 2701 out.println("G90"); 2702 out.println("M82"); 2703 out.println("M106"); 2704 out.println("G28 X0 Y0"); 2705 out.println("G28 Z0"); 2706 out.println("G29"); 2707 out.println("G1 Z15.0 F9000"); 2708 out.println("G92 E0"); 2709 out.println("G1 F200 E5"); 2710 out.println("G92 E0"); 2711 out.println("G1 X50 Y50 F1800"); 2712 2713 double e = 0; 2714 synchronized (turtleLock) { 2715 int i = 0; 2716 for (Map.Entry<Long, ArrayList> entry : turtleStates.entrySet()) { 2717 retrieveState(entry.getKey()); 2718 Turtle t = getStateTurtle(entry.getValue()); 2719 i++; 2720 if (i == 1) continue; 2721 if (t.__location != null && !t.__location.equals(t._location)) { 2722 double x1 = t._location.x, y1 = t._location.y, x2 = t.__location.x, y2 = t.__location.y; 2723 double d = Math.hypot(x1 - x2, y1 - y2); 2724 e += d * 0.05; 2725 //System.out.printf("%f %f %f %f",x1,y1,x2,y2); 2726 if (t._isPenDown) { 2727 out.printf("G1 X%.4f Y%.4f E%.4f\n", screenX(x1) * 1.0 / width * 100, screenY(y1) * 1.0 / height * 100, e); 2728 } else { 2729 2730 } 2731 2732 } 2733 } 2734 out.println("G1 Z15"); 2735 out.println("M104 S0"); 2736 out.println("M140 S0"); 2737 out.close(); 2738 } 2739 } 2740}