diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..82b2b9e
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# the shebang is ignored, but nice for editors
+
+if type -P lorri &>/dev/null; then
+ eval "$(lorri direnv)"
+else
+ echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
+ use nix
+fi
diff --git a/common/Draw.java b/common/Draw.java
new file mode 100644
index 0000000..e7495c6
--- /dev/null
+++ b/common/Draw.java
@@ -0,0 +1,1881 @@
+/******************************************************************************
+ * Compilation: javac Draw.java
+ * Execution: java Draw
+ * Dependencies: none
+ *
+ * Drawing library. This class provides a basic capability for creating
+ * drawings with your programs. It uses a simple graphics model that
+ * allows you to create drawings consisting of points, lines, and curves
+ * in a window on your computer and to save the drawings to a file.
+ * This is the object-oriented version of standard draw; it supports
+ * multiple independent drawing windows.
+ *
+ * Todo
+ * ----
+ * - Add support for gradient fill, etc.
+ *
+ * Remarks
+ * -------
+ * - don't use AffineTransform for rescaling since it inverts
+ * images and strings
+ * - careful using setFont in inner loop within an animation -
+ * it can cause flicker
+ *
+ ******************************************************************************/
+package common;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FileDialog;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.RenderingHints;
+import java.awt.Toolkit;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.TreeSet;
+
+import javax.imageio.ImageIO;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+/**
+ * The Draw data type provides a basic capability for
+ * creating drawings with your programs. It uses a simple graphics model that
+ * allows you to create drawings consisting of points, lines, and curves
+ * in a window on your computer and to save the drawings to a file.
+ * This is the object-oriented version of standard draw; it supports
+ * multiple independent drawing windows.
+ *
+ * For additional documentation, see
+ * Section 3.1 of
+ * Computer Science: An Interdisciplinary Approach by Robert Sedgewick and Kevin Wayne.
+ *
+ * @author Robert Sedgewick
+ * @author Kevin Wayne
+ */
+public final class Draw implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
+
+ /**
+ * The color aqua (0, 255, 255).
+ */
+ public static final Color AQUA = new Color(0, 255, 255);
+
+ /**
+ * The color black (0, 0, 0).
+ */
+ public static final Color BLACK = Color.BLACK;
+
+ /**
+ * The color blue (0, 0, 255).
+ */
+ public static final Color BLUE = Color.BLUE;
+
+ /**
+ * The color cyan (0, 255, 255).
+ */
+ public static final Color CYAN = Color.CYAN;
+
+ /**
+ * The color fuscia (255, 0, 255).
+ */
+ public static final Color FUSCIA = new Color(255, 0, 255);
+
+ /**
+ * The color dark gray (64, 64, 64).
+ */
+ public static final Color DARK_GRAY = Color.DARK_GRAY;
+
+ /**
+ * The color gray (128, 128, 128).
+ */
+ public static final Color GRAY = Color.GRAY;
+
+ /**
+ * The color green (0, 128, 0).
+ */
+ public static final Color GREEN = new Color(0, 128, 0);
+
+ /**
+ * The color light gray (192, 192, 192).
+ */
+ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY;
+
+ /**
+ * The color lime (0, 255, 0).
+ */
+ public static final Color LIME = new Color(0, 255, 0);
+
+ /**
+ * The color magenta (255, 0, 255).
+ */
+ public static final Color MAGENTA = Color.MAGENTA;
+
+ /**
+ * The color maroon (128, 0, 0).
+ */
+ public static final Color MAROON = new Color(128, 0, 0);
+
+ /**
+ * The color navy (0, 0, 128).
+ */
+ public static final Color NAVY = new Color(0, 0, 128);
+
+ /**
+ * The color olive (128, 128, 0).
+ */
+ public static final Color OLIVE = new Color(128, 128, 0);
+
+ /**
+ * The color orange (255, 200, 0).
+ */
+ public static final Color ORANGE = Color.ORANGE;
+
+ /**
+ * The color pink (255, 175, 175).
+ */
+ public static final Color PINK = Color.PINK;
+
+ /**
+ * The color purple (128, 0, 128).
+ */
+ public static final Color PURPLE = new Color(128, 0, 128);
+
+ /**
+ * The color red (255, 0, 0).
+ */
+ public static final Color RED = Color.RED;
+
+ /**
+ * The color silver (192, 192, 192).
+ */
+ public static final Color SILVER = new Color(192, 192, 192);
+
+ /**
+ * The color teal (0, 128, 128).
+ */
+ public static final Color TEAL = new Color(0, 128, 128);
+
+ /**
+ * The color white (255, 255, 255).
+ */
+ public static final Color WHITE = Color.WHITE;
+
+ /**
+ * The color yellow (255, 255, 0).
+ */
+ public static final Color YELLOW = Color.YELLOW;
+
+ /**
+ * A 100% transparent color, for a transparent background.
+ */
+ public static final Color TRANSPARENT = new Color(0, 0, 0, 0);
+
+ /**
+ * The shade of blue used in Introduction to Programming in Java.
+ * It is Pantone 300U. The RGB values are approximately (9, 90, 166).
+ */
+ public static final Color BOOK_BLUE = new Color(9, 90, 166);
+
+ /**
+ * The shade of light blue used in Introduction to Programming in Java.
+ * The RGB values are approximately (103, 198, 243).
+ */
+ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243);
+
+ /**
+ * The shade of red used in Algorithms, 4th edition.
+ * It is Pantone 1805U. The RGB values are approximately (150, 35, 31).
+ */
+ public static final Color BOOK_RED = new Color(150, 35, 31);
+
+ /**
+ * The shade of orange used in Princeton University's identity.
+ * It is PMS 158. The RGB values are approximately (245, 128, 37).
+ */
+ public static final Color PRINCETON_ORANGE = new Color(245, 128, 37);
+
+ // default colors
+ private static final Color DEFAULT_PEN_COLOR = BLACK;
+ private static final Color DEFAULT_BACKGROUND_COLOR = WHITE;
+
+
+ // boundary of drawing canvas, 0% border
+ private static final double BORDER = 0.0;
+ private static final double DEFAULT_XMIN = 0.0;
+ private static final double DEFAULT_XMAX = 1.0;
+ private static final double DEFAULT_YMIN = 0.0;
+ private static final double DEFAULT_YMAX = 1.0;
+
+ // default canvas size is SIZE-by-SIZE
+ private static final int DEFAULT_SIZE = 512;
+
+ // default pen radius
+ private static final double DEFAULT_PEN_RADIUS = 0.002;
+
+ // default font
+ private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16);
+
+ // default title of drawing window
+ private static final String DEFAULT_WINDOW_TITLE = "Draw";
+
+ // current pen color
+ private Color penColor = DEFAULT_PEN_COLOR;
+
+ // background color
+ private Color backgroundColor = DEFAULT_BACKGROUND_COLOR;
+
+ // current title of drawing window
+ private String windowTitle = DEFAULT_WINDOW_TITLE;
+
+ // canvas size
+ private int width = DEFAULT_SIZE;
+ private int height = DEFAULT_SIZE;
+
+ // current pen radius
+ private double penRadius = DEFAULT_PEN_RADIUS;
+
+ // show we draw immediately or wait until next show?
+ private boolean defer = false;
+
+ private double xmin = DEFAULT_XMIN;
+ private double xmax = DEFAULT_XMAX;
+ private double ymin = DEFAULT_YMIN;
+ private double ymax = DEFAULT_YMAX;
+
+ // for synchronization
+ private final Object mouseLock = new Object();
+ private final Object keyLock = new Object();
+
+ // current font
+ private Font font = DEFAULT_FONT;
+
+ // the JLabel for drawing
+ private JLabel draw;
+
+ // double buffered graphics
+ private BufferedImage offscreenImage, onscreenImage;
+ private Graphics2D offscreen, onscreen;
+
+ // the frame for drawing to the screen
+ private JFrame frame;
+
+ // is the JFrame visible (upon calling draw())?
+ private static boolean isJFrameVisible = true;
+
+ // mouse state
+ private boolean isMousePressed = false;
+ private double mouseX = 0;
+ private double mouseY = 0;
+
+ // keyboard state
+ private final LinkedList
+ * This method takes as an argument the keycode (corresponding to a physical key).
+ * It can handle action keys (such as F1 and arrow keys) and modifier keys
+ * (such as shift and control).
+ * See {@link KeyEvent} for a description of key codes.
+ *
+ * @param keycode the keycode to check
+ * @return {@code true} if {@code keycode} is currently being pressed;
+ * {@code false} otherwise
+ */
+ public boolean isKeyPressed(int keycode) {
+ synchronized (keyLock) {
+ return keysDown.contains(keycode);
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyTyped(KeyEvent event) {
+ synchronized (keyLock) {
+ keysTyped.addFirst(event.getKeyChar());
+ }
+
+ // notify all listeners
+ for (DrawListener listener : listeners)
+ listener.keyTyped(event.getKeyChar());
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyPressed(KeyEvent event) {
+ synchronized (keyLock) {
+ keysDown.add(event.getKeyCode());
+ }
+
+ // notify all listeners
+ for (DrawListener listener : listeners)
+ listener.keyPressed(event.getKeyCode());
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyReleased(KeyEvent event) {
+ synchronized (keyLock) {
+ keysDown.remove(event.getKeyCode());
+ }
+
+ // notify all listeners
+ for (DrawListener listener : listeners)
+ listener.keyReleased(event.getKeyCode());
+ }
+
+ /***************************************************************************
+ * Timer events.
+ ***************************************************************************/
+
+ /**
+ * Sets a timer that calls update() method a specified number of times
+ * per second.
+ *
+ * @param callsPerSecond calls per second
+ */
+ public void enableTimer(int callsPerSecond) {
+ disableTimer();
+ timer = new Timer();
+ timer.schedule(new MyTimerTask(), 0, (int) Math.round(1000.0 / callsPerSecond));
+ }
+
+ public void disableTimer() {
+ if (timer != null) timer.cancel();
+ }
+
+ private class MyTimerTask extends TimerTask {
+ public void run() {
+ for (DrawListener listener : listeners)
+ listener.update();
+ }
+ }
+
+ /***************************************************************************
+ * For improved resolution on Mac Retina displays.
+ ***************************************************************************/
+
+ private static class RetinaImageIcon extends ImageIcon {
+
+ public RetinaImageIcon(Image image) {
+ super(image);
+ }
+
+ public int getIconWidth() {
+ return super.getIconWidth() / 2;
+ }
+
+ /**
+ * Returns the height of the icon.
+ *
+ * @return the height in pixels of this icon
+ */
+ public int getIconHeight() {
+ return super.getIconHeight() / 2;
+ }
+
+ public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.scale(0.5, 0.5);
+ super.paintIcon(c, g2, x * 2, y * 2);
+ g2.dispose();
+ }
+ }
+
+ /**
+ * Test client.
+ *
+ * @param args the command-line arguments
+ */
+ public static void main(String[] args) {
+
+ // create one drawing window
+ Draw draw1 = new Draw();
+ draw1.setTitle("Test client 1");
+ draw1.square(0.2, 0.8, 0.1);
+ draw1.filledSquare(0.8, 0.8, 0.2);
+ draw1.circle(0.8, 0.2, 0.2);
+ draw1.setPenColor(Draw.MAGENTA);
+ draw1.setPenRadius(0.02);
+ draw1.arc(0.8, 0.2, 0.1, 200, 45);
+
+
+ // create another one
+ Draw draw2 = new Draw();
+ draw2.setCanvasSize(900, 200);
+ draw2.setTitle("Test client 2");
+ // draw a blue diamond
+ draw2.setPenRadius();
+ draw2.setPenColor(Draw.BLUE);
+ double[] x = { 0.1, 0.2, 0.3, 0.2 };
+ double[] y = { 0.2, 0.3, 0.2, 0.1 };
+ draw2.filledPolygon(x, y);
+
+ // text
+ draw2.setPenColor(Draw.BLACK);
+ draw2.text(0.2, 0.5, "black text");
+ draw2.setPenColor(Draw.WHITE);
+ draw2.text(0.2, 0.2, "white text");
+ }
+
+}
diff --git a/common/StdDraw.java b/common/StdDraw.java
new file mode 100644
index 0000000..bde2c22
--- /dev/null
+++ b/common/StdDraw.java
@@ -0,0 +1,2260 @@
+/******************************************************************************
+ * Compilation: javac StdDraw.java
+ * Execution: java StdDraw
+ * Dependencies: none
+ *
+ * Standard drawing library. This class provides a basic capability for
+ * creating drawings with your programs. It uses a simple graphics model that
+ * allows you to create drawings consisting of geometric shapes (e.g.,
+ * points, lines, circles, rectangles) in a window on your computer
+ * and to save the drawings to a file.
+ *
+ * Todo
+ * ----
+ * - Don't show window until first unbuffered drawing command or call to show()
+ * (with setVisible not set to false).
+ * - Add support for gradient fill, etc.
+ * - Fix setCanvasSize() so that it can be called only once.
+ * - On some systems, drawing a line (or other shape) that extends way
+ * beyond canvas (e.g., to infinity) dimensions does not get drawn.
+ *
+ * Remarks
+ * -------
+ * - don't use AffineTransform for rescaling since it inverts
+ * images and strings
+ *
+ ******************************************************************************/
+package common;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FileDialog;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.RenderingHints;
+import java.awt.Toolkit;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+import java.awt.geom.Arc2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import java.util.LinkedList;
+import java.util.TreeSet;
+import java.util.NoSuchElementException;
+import javax.imageio.ImageIO;
+
+import javax.swing.ImageIcon;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.KeyStroke;
+
+/**
+ * The {@code StdDraw} class provides static methods for creating drawings
+ * with your programs. It uses a simple graphics model that
+ * allows you to create drawings consisting of points, lines, squares,
+ * circles, and other geometric shapes in a window on your computer and
+ * to save the drawings to a file. Standard drawing also includes
+ * facilities for text, color, pictures, and animation, along with
+ * user interaction via the keyboard and mouse.
+ *
+ * Getting started.
+ * To use this class, you must have {@code StdDraw.class} in your
+ * Java classpath. If you used our autoinstaller, you should be all set.
+ * Otherwise, either download
+ * stdlib.jar
+ * and add to your Java classpath or download
+ * StdDraw.java
+ * and put a copy in your working directory.
+ *
+ * Now, cut-and-paste the following short program into your editor:
+ *
+ * Points and lines.
+ * You can draw points and line segments with the following methods:
+ *
+ * The x- and y-coordinates must be in the drawing area
+ * (between 0 and 1 and by default) or the points and lines will not be visible.
+ *
+ * Squares, circles, rectangles, and ellipses.
+ * You can draw squares, circles, rectangles, and ellipses using
+ * the following methods:
+ *
+ * All of these methods take as arguments the location and size of the shape.
+ * The location is always specified by the x- and y-coordinates
+ * of its center.
+ * The size of a circle is specified by its radius and the size of an ellipse is
+ * specified by the lengths of its semi-major and semi-minor axes.
+ * The size of a square or rectangle is specified by its half-width or half-height.
+ * The convention for drawing squares and rectangles is parallel to those for
+ * drawing circles and ellipses, but may be unexpected to the uninitiated.
+ *
+ * The methods above trace outlines of the given shapes. The following methods
+ * draw filled versions:
+ *
+ * Circular arcs.
+ * You can draw circular arcs with the following method:
+ *
+ * The arc is from the circle centered at (x, y) of the specified radius.
+ * The arc extends from angle1 to angle2. By convention, the angles are
+ * polar (counterclockwise angle from the x-axis)
+ * and represented in degrees. For example, {@code StdDraw.arc(0.0, 0.0, 1.0, 0, 90)}
+ * draws the arc of the unit circle from 3 o'clock (0 degrees) to 12 o'clock (90 degrees).
+ *
+ * Polygons.
+ * You can draw polygons with the following methods:
+ *
+ * The points in the polygon are ({@code x[i]}, {@code y[i]}).
+ * For example, the following code fragment draws a filled diamond
+ * with vertices (0.1, 0.2), (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1):
+ *
+ * Pen size.
+ * The pen is circular, so that when you set the pen radius to r
+ * and draw a point, you get a circle of radius r. Also, lines are
+ * of thickness 2r and have rounded ends. The default pen radius
+ * is 0.002 and is not affected by coordinate scaling. This default pen
+ * radius is about 1/500 the width of the default canvas, so that if
+ * you draw 200 points equally spaced along a horizontal or vertical line,
+ * you will be able to see individual circles, but if you draw 250 such
+ * points, the result will look like a line.
+ *
+ * For example, {@code StdDraw.setPenRadius(0.01)} makes
+ * the thickness of the lines and the size of the points to be five times
+ * the 0.002 default.
+ * To draw points with the minimum possible radius (one pixel on typical
+ * displays), set the pen radius to 0.0.
+ *
+ * Pen color.
+ * All geometric shapes (such as points, lines, and circles) are drawn using
+ * the current pen color. By default, it is black.
+ * You can change the pen color with the following methods:
+ *
+ * The first method allows you to specify colors using the RGB color system.
+ * This color picker
+ * is a convenient way to find a desired color.
+ *
+ * The second method allows you to specify colors using the
+ * {@link Color} data type, which is defined in Java's {@link java.awt} package.
+ * Standard drawing defines a number of predefined colors including
+ * {@link #BLACK}, {@link #WHITE}, {@link #RED}, {@link #GREEN},
+ * and {@link #BLUE}.
+ * For example, {@code StdDraw.setPenColor(StdDraw.RED)} sets the
+ * pen color to red.
+ *
+ * Window title.
+ * By default, the standard drawing window title is "Standard Draw".
+ * You can change the title with the following method:
+ *
+ * This sets the standard drawing window title to the specified string.
+ *
+ * Canvas size.
+ * By default, all drawing takes places in a 512-by-512 canvas.
+ * The canvas does not include the window title or window border.
+ * You can change the size of the canvas with the following method:
+ *
+ * This sets the canvas size to be width-by-height pixels.
+ * It also clears the current drawing using the default background color (white).
+ * Ordinarily, this method is called only once, at the very beginning of a program.
+ * For example, {@code StdDraw.setCanvasSize(800, 800)}
+ * sets the canvas size to be 800-by-800 pixels.
+ *
+ * Canvas scale and coordinate system.
+ * By default, all drawing takes places in the unit square, with (0, 0) at
+ * lower left and (1, 1) at upper right. You can change the default
+ * coordinate system with the following methods:
+ *
+ * The arguments are the coordinates of the minimum and maximum
+ * x- or y-coordinates that will appear in the canvas.
+ * For example, if you wish to use the default coordinate system but
+ * leave a small margin, you can call {@code StdDraw.setScale(-.05, 1.05)}.
+ *
+ * These methods change the coordinate system for subsequent drawing
+ * commands; they do not affect previous drawings.
+ * These methods do not change the canvas size; so, if the x-
+ * and y-scales are different, squares will become rectangles
+ * and circles will become ellipses.
+ *
+ * Text.
+ * You can use the following methods to annotate your drawings with text:
+ *
+ * The first two methods write the specified text in the current font,
+ * centered at (x, y).
+ * The second method allows you to rotate the text.
+ * The last two methods either left- or right-align the text at (x, y).
+ *
+ * The default font is a Sans Serif font with point size 16.
+ * You can use the following method to change the font:
+ *
+ * To specify the font, you use the {@link Font} data type,
+ * which is defined in Java's {@link java.awt} package.
+ * This allows you to
+ * choose the face, size, and style of the font. For example, the following
+ * code fragment sets the font to Arial Bold, 60 point.
+ * The
+ * Images.
+ * You can use the following methods to add images to your drawings:
+ *
+ * These methods draw the specified image, centered at (x, y).
+ * The image must be in a supported file format (typically JPEG, PNG, GIF, TIFF, and BMP).
+ * The image will display at its native size, independent of the coordinate system.
+ * Optionally, you can rotate the image a specified number of degrees counterclockwise
+ * or rescale it to fit snugly inside a bounding box.
+ *
+ * Saving to a file.
+ * You can save your image to a file using the File → Save menu option.
+ * You can also save a file programmatically using the following method:
+ *
+ * You can save the drawing to a file in a supported file format
+ * (typically JPEG, PNG, GIF, TIFF, and BMP).
+ *
+ * File formats.
+ * The {@code StdDraw} class supports reading and writing images to any of the
+ * file formats supported by {@link javax.imageio} (typically JPEG, PNG,
+ * GIF, TIFF, and BMP).
+ * The file extensions corresponding to JPEG, PNG, GIF, TIFF, and BMP,
+ * are {@code .jpg}, {@code .png}, {@code .gif}, {@code .tif},
+ * and {@code .bmp}, respectively.
+ *
+ * We recommend using PNG for drawing that consist solely of geometric shapes
+ * and JPEG for drawings that contains pictures.
+ * The JPEG file format does not support transparent backgrounds.
+ *
+ *
+ * Clearing the canvas.
+ * To clear the entire drawing canvas, you can use the following methods:
+ *
+ * The first method clears the canvas to the default background color (white);
+ * the second method allows you to specify the background color. For example,
+ * {@code StdDraw.clear(StdDraw.LIGHT_GRAY)} clears the canvas to a shade
+ * of gray. To make the background transparent,
+ * call {@code StdDraw.clear(StdDraw.TRANSPARENT)}.
+ *
+ *
+ * Computer animations and double buffering.
+ * Double buffering is one of the most powerful features of standard drawing,
+ * enabling computer animations.
+ * The following methods control the way in which objects are drawn:
+ *
+ * By default, double buffering is disabled, which means that as soon as you
+ * call a drawing
+ * method—such as {@code point()} or {@code line()}—the
+ * results appear on the screen.
+ *
+ * When double buffering is enabled by calling {@link #enableDoubleBuffering()},
+ * all drawing takes place on the offscreen canvas. The offscreen canvas
+ * is not displayed. Only when you call
+ * {@link #show()} does your drawing get copied from the offscreen canvas to
+ * the onscreen canvas, where it is displayed in the standard drawing window. You
+ * can think of double buffering as collecting all of the lines, points, shapes,
+ * and text that you tell it to draw, and then drawing them all
+ * simultaneously, upon request.
+ *
+ * The most important use of double buffering is to produce computer
+ * animations, creating the illusion of motion by rapidly
+ * displaying static drawings. To produce an animation, repeat
+ * the following four steps:
+ *
+ * The {@link #clear()}, {@link #show()}, and {@link #pause(int t)} methods
+ * support the first, third, and fourth of these steps, respectively.
+ *
+ * For example, this code fragment animates two balls moving in a circle.
+ *
+ * Keyboard and mouse inputs.
+ * Standard drawing has very basic support for keyboard and mouse input.
+ * It is much less powerful than most user interface libraries provide, but also much simpler.
+ * You can use the following methods to intercept mouse events:
+ *
+ * The first method tells you whether a mouse button is currently being pressed.
+ * The last two methods tells you the x- and y-coordinates of the mouse's
+ * current position, using the same coordinate system as the canvas (the unit square, by default).
+ * You should use these methods in an animation loop that waits a short while before trying
+ * to poll the mouse for its current state.
+ * You can use the following methods to intercept keyboard events:
+ *
+ * If the user types lots of keys, they will be saved in a list until you process them.
+ * The first method tells you whether the user has typed a key (that your program has
+ * not yet processed).
+ * The second method returns the next key that the user typed (that your program has
+ * not yet processed) and removes it from the list of saved keystrokes.
+ * The third method tells you whether a key is currently being pressed.
+ *
+ * Accessing control parameters.
+ * You can use the following methods to access the current pen color, pen radius,
+ * and font:
+ *
+ * These methods are useful when you want to temporarily change a
+ * control parameter and, later, reset it back to its original value.
+ *
+ * Corner cases.
+ * Here are some corner cases.
+ *
+ * Performance tricks.
+ * Standard drawing is capable of drawing large amounts of data.
+ * Here are a few tricks and tips:
+ *
+ * Known bugs and issues.
+ *
+ * Reference.
+ * For additional documentation,
+ * see Section 1.5 of
+ * Computer Science: An Interdisciplinary Approach
+ * by Robert Sedgewick and Kevin Wayne.
+ *
+ * @author Robert Sedgewick
+ * @author Kevin Wayne
+ */
+public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
+
+ /**
+ * The color aqua (0, 255, 255).
+ */
+ public static final Color AQUA = new Color(0, 255, 255);
+
+ /**
+ * The color black (0, 0, 0).
+ */
+ public static final Color BLACK = Color.BLACK;
+
+ /**
+ * The color blue (0, 0, 255).
+ */
+ public static final Color BLUE = Color.BLUE;
+
+ /**
+ * The color cyan (0, 255, 255).
+ */
+ public static final Color CYAN = Color.CYAN;
+
+ /**
+ * The color fuscia (255, 0, 255).
+ */
+ public static final Color FUSCIA = new Color(255, 0, 255);
+
+ /**
+ * The color dark gray (64, 64, 64).
+ */
+ public static final Color DARK_GRAY = Color.DARK_GRAY;
+
+ /**
+ * The color gray (128, 128, 128).
+ */
+ public static final Color GRAY = Color.GRAY;
+
+ /**
+ * The color green (0, 128, 0).
+ */
+ public static final Color GREEN = new Color(0, 128, 0);
+
+ /**
+ * The color light gray (192, 192, 192).
+ */
+ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY;
+
+ /**
+ * The color lime (0, 255, 0).
+ */
+ public static final Color LIME = new Color(0, 255, 0);
+
+ /**
+ * The color magenta (255, 0, 255).
+ */
+ public static final Color MAGENTA = Color.MAGENTA;
+
+ /**
+ * The color maroon (128, 0, 0).
+ */
+ public static final Color MAROON = new Color(128, 0, 0);
+
+ /**
+ * The color navy (0, 0, 128).
+ */
+ public static final Color NAVY = new Color(0, 0, 128);
+
+ /**
+ * The color olive (128, 128, 0).
+ */
+ public static final Color OLIVE = new Color(128, 128, 0);
+
+ /**
+ * The color orange (255, 200, 0).
+ */
+ public static final Color ORANGE = Color.ORANGE;
+
+ /**
+ * The color pink (255, 175, 175).
+ */
+ public static final Color PINK = Color.PINK;
+
+ /**
+ * The color purple (128, 0, 128).
+ */
+ public static final Color PURPLE = new Color(128, 0, 128);
+
+ /**
+ * The color red (255, 0, 0).
+ */
+ public static final Color RED = Color.RED;
+
+ /**
+ * The color silver (192, 192, 192).
+ */
+ public static final Color SILVER = new Color(192, 192, 192);
+
+ /**
+ * The color teal (0, 128, 128).
+ */
+ public static final Color TEAL = new Color(0, 128, 128);
+
+ /**
+ * The color white (255, 255, 255).
+ */
+ public static final Color WHITE = Color.WHITE;
+
+ /**
+ * The color yellow (255, 255, 0).
+ */
+ public static final Color YELLOW = Color.YELLOW;
+
+ /**
+ * A 100% transparent color, for a transparent background.
+ */
+ public static final Color TRANSPARENT = new Color(0, 0, 0, 0);
+
+ /**
+ * The shade of blue used in Introduction to Programming in Java.
+ * It is Pantone 300U. The RGB values are approximately (9, 90, 166).
+ */
+ public static final Color BOOK_BLUE = new Color(9, 90, 166);
+
+ /**
+ * The shade of light blue used in Introduction to Programming in Java.
+ * The RGB values are approximately (103, 198, 243).
+ */
+ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243);
+
+ /**
+ * The shade of red used in Algorithms, 4th edition.
+ * It is Pantone 1805U. The RGB values are approximately (150, 35, 31).
+ */
+ public static final Color BOOK_RED = new Color(150, 35, 31);
+
+ /**
+ * The shade of orange used in Princeton University's identity.
+ * It is PMS 158. The RGB values are approximately (245, 128, 37).
+ */
+ public static final Color PRINCETON_ORANGE = new Color(245, 128, 37);
+
+ // default colors
+ private static final Color DEFAULT_PEN_COLOR = BLACK;
+ private static final Color DEFAULT_BACKGROUND_COLOR = WHITE;
+
+ // current pen color
+ private static Color penColor = DEFAULT_PEN_COLOR;
+
+ // current background color
+ private static Color backgroundColor = DEFAULT_BACKGROUND_COLOR;
+
+ // default title of standard drawing window
+ private static final String DEFAULT_WINDOW_TITLE = "Standard Draw";
+
+ // current title of standard drawing window
+ private static String windowTitle = DEFAULT_WINDOW_TITLE;
+
+ // default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
+ private static final int DEFAULT_SIZE = 512;
+ private static int width = DEFAULT_SIZE;
+ private static int height = DEFAULT_SIZE;
+
+ // default pen radius
+ private static final double DEFAULT_PEN_RADIUS = 0.002;
+
+ // current pen radius
+ private static double penRadius = DEFAULT_PEN_RADIUS;
+
+ // show we draw immediately or wait until next show?
+ private static boolean defer = false;
+
+ // boundary of drawing canvas, 0% border
+ // private static final double BORDER = 0.05;
+ private static final double BORDER = 0.00;
+ private static final double DEFAULT_XMIN = 0.0;
+ private static final double DEFAULT_XMAX = 1.0;
+ private static final double DEFAULT_YMIN = 0.0;
+ private static final double DEFAULT_YMAX = 1.0;
+
+ private static double xmin = DEFAULT_XMIN;
+ private static double xmax = DEFAULT_XMAX;
+ private static double ymin = DEFAULT_YMIN;
+ private static double ymax = DEFAULT_YMAX;
+
+ // for synchronization
+ private static final Object MOUSE_LOCK = new Object();
+ private static final Object KEY_LOCK = new Object();
+
+ // default font
+ private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16);
+
+ // current font
+ private static Font font = DEFAULT_FONT;
+
+ // double buffered graphics
+ private static BufferedImage offscreenImage, onscreenImage;
+ private static Graphics2D offscreen, onscreen;
+
+ // singleton for callbacks: avoids generation of extra .class files
+ private static StdDraw std = new StdDraw();
+
+ // the frame for drawing to the screen
+ private static JFrame frame;
+
+ // is the JFrame visible (upon calling draw())?
+ private static boolean isJFrameVisible = true;
+
+ // mouse state
+ private static boolean isMousePressed = false;
+ private static double mouseX = 0;
+ private static double mouseY = 0;
+
+ // queue of typed key characters
+ private static LinkedList
+ * There are a number predefined pen colors, such as
+ * {@code StdDraw.BLACK}, {@code StdDraw.WHITE}, {@code StdDraw.RED},
+ * {@code StdDraw.GREEN}, and {@code StdDraw.BLUE}.
+ *
+ * @param color the color to make the pen
+ * @throws IllegalArgumentException if {@code color} is {@code null}
+ */
+ public static void setPenColor(Color color) {
+ validateNotNull(color, "color");
+ penColor = color;
+ offscreen.setColor(penColor);
+ }
+
+ /**
+ * Sets the pen color to the specified RGB color.
+ *
+ * @param red the amount of red (between 0 and 255)
+ * @param green the amount of green (between 0 and 255)
+ * @param blue the amount of blue (between 0 and 255)
+ * @throws IllegalArgumentException if {@code red}, {@code green},
+ * or {@code blue} is outside its prescribed range
+ */
+ public static void setPenColor(int red, int green, int blue) {
+ if (red < 0 || red >= 256) throw new IllegalArgumentException("red must be between 0 and 255");
+ if (green < 0 || green >= 256) throw new IllegalArgumentException("green must be between 0 and 255");
+ if (blue < 0 || blue >= 256) throw new IllegalArgumentException("blue must be between 0 and 255");
+ setPenColor(new Color(red, green, blue));
+ }
+
+ /**
+ * Returns the current font.
+ *
+ * @return the current font
+ */
+ public static Font getFont() {
+ return font;
+ }
+
+ /**
+ * Sets the font to the default font (sans serif, 16 point).
+ */
+ public static void setFont() {
+ setFont(DEFAULT_FONT);
+ }
+
+ /**
+ * Sets the font to the specified value.
+ *
+ * @param font the font
+ * @throws IllegalArgumentException if {@code font} is {@code null}
+ */
+ public static void setFont(Font font) {
+ validateNotNull(font, "font");
+ StdDraw.font = font;
+ }
+
+
+ /***************************************************************************
+ * Drawing geometric shapes.
+ ***************************************************************************/
+
+ /**
+ * Draws a line segment between (x0, y0) and
+ * (x1, y1).
+ *
+ * @param x0 the x-coordinate of one endpoint
+ * @param y0 the y-coordinate of one endpoint
+ * @param x1 the x-coordinate of the other endpoint
+ * @param y1 the y-coordinate of the other endpoint
+ * @throws IllegalArgumentException if any coordinate is either NaN or infinite
+ */
+ public static void line(double x0, double y0, double x1, double y1) {
+ validate(x0, "x0");
+ validate(y0, "y0");
+ validate(x1, "x1");
+ validate(y1, "y1");
+ offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1)));
+ draw();
+ }
+
+ /**
+ * Draws one pixel at (x, y).
+ * This method is private because pixels depend on the display.
+ * To achieve the same effect, set the pen radius to 0 and call {@code point()}.
+ *
+ * @param x the x-coordinate of the pixel
+ * @param y the y-coordinate of the pixel
+ * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite
+ */
+ private static void pixel(double x, double y) {
+ validate(x, "x");
+ validate(y, "y");
+ offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
+ }
+
+ /**
+ * Draws a point centered at (x, y).
+ * The point is a filled circle whose radius is equal to the pen radius.
+ * To draw a single-pixel point, first set the pen radius to 0.
+ *
+ * @param x the x-coordinate of the point
+ * @param y the y-coordinate of the point
+ * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite
+ */
+ public static void point(double x, double y) {
+ validate(x, "x");
+ validate(y, "y");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double r = penRadius;
+ float scaledPenRadius = (float) (r * DEFAULT_SIZE);
+
+ // double ws = factorX(2*r);
+ // double hs = factorY(2*r);
+ // if (ws <= 1 && hs <= 1) pixel(x, y);
+ if (scaledPenRadius <= 1) pixel(x, y);
+ else offscreen.fill(new Ellipse2D.Double(xs - scaledPenRadius/2, ys - scaledPenRadius/2,
+ scaledPenRadius, scaledPenRadius));
+ draw();
+ }
+
+ /**
+ * Draws a circle of the specified radius, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the circle
+ * @param y the y-coordinate of the center of the circle
+ * @param radius the radius of the circle
+ * @throws IllegalArgumentException if {@code radius} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void circle(double x, double y, double radius) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(radius, "radius");
+ validateNonnegative(radius, "radius");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*radius);
+ double hs = factorY(2*radius);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+ /**
+ * Draws a filled circle of the specified radius, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the circle
+ * @param y the y-coordinate of the center of the circle
+ * @param radius the radius of the circle
+ * @throws IllegalArgumentException if {@code radius} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void filledCircle(double x, double y, double radius) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(radius, "radius");
+ validateNonnegative(radius, "radius");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*radius);
+ double hs = factorY(2*radius);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+
+ /**
+ * Draws an ellipse with the specified semimajor and semiminor axes,
+ * centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the ellipse
+ * @param y the y-coordinate of the center of the ellipse
+ * @param semiMajorAxis is the semimajor axis of the ellipse
+ * @param semiMinorAxis is the semiminor axis of the ellipse
+ * @throws IllegalArgumentException if either {@code semiMajorAxis}
+ * or {@code semiMinorAxis} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(semiMajorAxis, "semimajor axis");
+ validate(semiMinorAxis, "semiminor axis");
+ validateNonnegative(semiMajorAxis, "semimajor axis");
+ validateNonnegative(semiMinorAxis, "semiminor axis");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*semiMajorAxis);
+ double hs = factorY(2*semiMinorAxis);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+ /**
+ * Draws a filled ellipse with the specified semimajor and semiminor axes,
+ * centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the ellipse
+ * @param y the y-coordinate of the center of the ellipse
+ * @param semiMajorAxis is the semimajor axis of the ellipse
+ * @param semiMinorAxis is the semiminor axis of the ellipse
+ * @throws IllegalArgumentException if either {@code semiMajorAxis}
+ * or {@code semiMinorAxis} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(semiMajorAxis, "semimajor axis");
+ validate(semiMinorAxis, "semiminor axis");
+ validateNonnegative(semiMajorAxis, "semimajor axis");
+ validateNonnegative(semiMinorAxis, "semiminor axis");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*semiMajorAxis);
+ double hs = factorY(2*semiMinorAxis);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+
+ /**
+ * Draws a circular arc of the specified radius,
+ * centered at (x, y), from angle1 to angle2 (in degrees).
+ *
+ * @param x the x-coordinate of the center of the circle
+ * @param y the y-coordinate of the center of the circle
+ * @param radius the radius of the circle
+ * @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock.
+ * @param angle2 the angle at the end of the arc. For example, if
+ * you want a 90 degree arc, then angle2 should be angle1 + 90.
+ * @throws IllegalArgumentException if {@code radius} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void arc(double x, double y, double radius, double angle1, double angle2) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(radius, "arc radius");
+ validate(angle1, "angle1");
+ validate(angle2, "angle2");
+ validateNonnegative(radius, "arc radius");
+
+ while (angle2 < angle1) angle2 += 360;
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*radius);
+ double hs = factorY(2*radius);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN));
+ draw();
+ }
+
+ /**
+ * Draws a square of the specified size, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the square
+ * @param y the y-coordinate of the center of the square
+ * @param halfLength one half the length of any side of the square
+ * @throws IllegalArgumentException if {@code halfLength} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void square(double x, double y, double halfLength) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(halfLength, "halfLength");
+ validateNonnegative(halfLength, "half length");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*halfLength);
+ double hs = factorY(2*halfLength);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+ /**
+ * Draws a filled square of the specified size, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the square
+ * @param y the y-coordinate of the center of the square
+ * @param halfLength one half the length of any side of the square
+ * @throws IllegalArgumentException if {@code halfLength} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void filledSquare(double x, double y, double halfLength) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(halfLength, "halfLength");
+ validateNonnegative(halfLength, "half length");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*halfLength);
+ double hs = factorY(2*halfLength);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+
+ /**
+ * Draws a rectangle of the specified size, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the rectangle
+ * @param y the y-coordinate of the center of the rectangle
+ * @param halfWidth one half the width of the rectangle
+ * @param halfHeight one half the height of the rectangle
+ * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void rectangle(double x, double y, double halfWidth, double halfHeight) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(halfWidth, "halfWidth");
+ validate(halfHeight, "halfHeight");
+ validateNonnegative(halfWidth, "half width");
+ validateNonnegative(halfHeight, "half height");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*halfWidth);
+ double hs = factorY(2*halfHeight);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+ /**
+ * Draws a filled rectangle of the specified size, centered at (x, y).
+ *
+ * @param x the x-coordinate of the center of the rectangle
+ * @param y the y-coordinate of the center of the rectangle
+ * @param halfWidth one half the width of the rectangle
+ * @param halfHeight one half the height of the rectangle
+ * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative
+ * @throws IllegalArgumentException if any argument is either NaN or infinite
+ */
+ public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(halfWidth, "halfWidth");
+ validate(halfHeight, "halfHeight");
+ validateNonnegative(halfWidth, "half width");
+ validateNonnegative(halfHeight, "half height");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(2*halfWidth);
+ double hs = factorY(2*halfHeight);
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
+ draw();
+ }
+
+
+ /**
+ * Draws a polygon with the vertices
+ * (x0, y0),
+ * (x1, y1), ...,
+ * (xn–1, yn–1).
+ *
+ * @param x an array of all the x-coordinates of the polygon
+ * @param y an array of all the y-coordinates of the polygon
+ * @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
+ * are of the same length
+ * @throws IllegalArgumentException if any coordinate is either NaN or infinite
+ * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null}
+ */
+ public static void polygon(double[] x, double[] y) {
+ validateNotNull(x, "x-coordinate array");
+ validateNotNull(y, "y-coordinate array");
+ for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]");
+ for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]");
+
+ int n1 = x.length;
+ int n2 = y.length;
+ if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
+ int n = n1;
+ if (n == 0) return;
+
+ GeneralPath path = new GeneralPath();
+ path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
+ for (int i = 0; i < n; i++)
+ path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
+ path.closePath();
+ offscreen.draw(path);
+ draw();
+ }
+
+ /**
+ * Draws a filled polygon with the vertices
+ * (x0, y0),
+ * (x1, y1), ...,
+ * (xn–1, yn–1).
+ *
+ * @param x an array of all the x-coordinates of the polygon
+ * @param y an array of all the y-coordinates of the polygon
+ * @throws IllegalArgumentException unless {@code x[]} and {@code y[]}
+ * are of the same length
+ * @throws IllegalArgumentException if any coordinate is either NaN or infinite
+ * @throws IllegalArgumentException if either {@code x[]} or {@code y[]} is {@code null}
+ */
+ public static void filledPolygon(double[] x, double[] y) {
+ validateNotNull(x, "x-coordinate array");
+ validateNotNull(y, "y-coordinate array");
+ for (int i = 0; i < x.length; i++) validate(x[i], "x[" + i + "]");
+ for (int i = 0; i < y.length; i++) validate(y[i], "y[" + i + "]");
+
+ int n1 = x.length;
+ int n2 = y.length;
+ if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length");
+ int n = n1;
+ if (n == 0) return;
+
+ GeneralPath path = new GeneralPath();
+ path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
+ for (int i = 0; i < n; i++)
+ path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
+ path.closePath();
+ offscreen.fill(path);
+ draw();
+ }
+
+
+ /***************************************************************************
+ * Drawing images.
+ ***************************************************************************/
+ // get an image from the given filename
+ private static Image getImage(String filename) {
+ if (filename == null) throw new IllegalArgumentException();
+
+ // to read from file
+ ImageIcon icon = new ImageIcon(filename);
+
+ // try to read from URL
+ if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
+ try {
+ URI uri = new URI(filename);
+ if (uri.isAbsolute()) {
+ URL url = uri.toURL();
+ icon = new ImageIcon(url);
+ }
+ }
+ catch (MalformedURLException | URISyntaxException e) {
+ /* not a url */
+ }
+ }
+
+ // in case file is inside a .jar (classpath relative to StdDraw)
+ if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
+ URL url = StdDraw.class.getResource(filename);
+ if (url != null)
+ icon = new ImageIcon(url);
+ }
+
+ // in case file is inside a .jar (classpath relative to root of jar)
+ if (icon.getImageLoadStatus() != MediaTracker.COMPLETE) {
+ URL url = StdDraw.class.getResource("/" + filename);
+ if (url == null) throw new IllegalArgumentException("could not read image: '" + filename + "'");
+ icon = new ImageIcon(url);
+ }
+
+ return icon.getImage();
+ }
+
+ /***************************************************************************
+ * [Summer 2016] Should we update to use ImageIO instead of ImageIcon()?
+ * Seems to have some issues loading images on some systems
+ * and slows things down on other systems.
+ * especially if you don't call ImageIO.setUseCache(false)
+ * One advantage is that it returns a BufferedImage.
+ ***************************************************************************/
+/*
+ private static BufferedImage getImage(String filename) {
+ if (filename == null) throw new IllegalArgumentException();
+
+ // from a file or URL
+ try {
+ URL url = new URL(filename);
+ BufferedImage image = ImageIO.read(url);
+ return image;
+ }
+ catch (IOException e) {
+ // ignore
+ }
+
+ // in case file is inside a .jar (classpath relative to StdDraw)
+ try {
+ URL url = StdDraw.class.getResource(filename);
+ BufferedImage image = ImageIO.read(url);
+ return image;
+ }
+ catch (IOException e) {
+ // ignore
+ }
+
+ // in case file is inside a .jar (classpath relative to root of jar)
+ try {
+ URL url = StdDraw.class.getResource("/" + filename);
+ BufferedImage image = ImageIO.read(url);
+ return image;
+ }
+ catch (IOException e) {
+ // ignore
+ }
+ throw new IllegalArgumentException("image " + filename + " not found");
+ }
+*/
+ /**
+ * Draws the specified image centered at (x, y).
+ * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP.
+ * As an optimization, the picture is cached, so there is no performance
+ * penalty for redrawing the same image multiple times (e.g., in an animation).
+ * However, if you change the picture file after drawing it, subsequent
+ * calls will draw the original picture.
+ *
+ * @param x the center x-coordinate of the image
+ * @param y the center y-coordinate of the image
+ * @param filename the name of the image/picture, e.g., "ball.gif"
+ * @throws IllegalArgumentException if the image filename is invalid
+ * @throws IllegalArgumentException if either {@code x} or {@code y} is either NaN or infinite
+ */
+ public static void picture(double x, double y, String filename) {
+ validate(x, "x");
+ validate(y, "y");
+ validateNotNull(filename, "filename");
+
+ // BufferedImage image = getImage(filename);
+ Image image = getImage(filename);
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ // int ws = image.getWidth(); // can call only if image is a BufferedImage
+ // int hs = image.getHeight();
+ int ws = image.getWidth(null);
+ int hs = image.getHeight(null);
+ if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
+
+ offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
+ draw();
+ }
+
+ /**
+ * Draws the specified image centered at (x, y),
+ * rotated given number of degrees.
+ * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP.
+ *
+ * @param x the center x-coordinate of the image
+ * @param y the center y-coordinate of the image
+ * @param filename the name of the image/picture, e.g., "ball.gif"
+ * @param degrees is the number of degrees to rotate counterclockwise
+ * @throws IllegalArgumentException if the image filename is invalid
+ * @throws IllegalArgumentException if {@code x}, {@code y}, {@code degrees} is NaN or infinite
+ * @throws IllegalArgumentException if {@code filename} is {@code null}
+ */
+ public static void picture(double x, double y, String filename, double degrees) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(degrees, "degrees");
+ validateNotNull(filename, "filename");
+
+ // BufferedImage image = getImage(filename);
+ Image image = getImage(filename);
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ // int ws = image.getWidth(); // can call only if image is a BufferedImage
+ // int hs = image.getHeight();
+ int ws = image.getWidth(null);
+ int hs = image.getHeight(null);
+ if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
+
+ offscreen.rotate(Math.toRadians(-degrees), xs, ys);
+ offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
+ offscreen.rotate(Math.toRadians(+degrees), xs, ys);
+
+ draw();
+ }
+
+ /**
+ * Draws the specified image centered at (x, y),
+ * rescaled to the specified bounding box.
+ * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP.
+ *
+ * @param x the center x-coordinate of the image
+ * @param y the center y-coordinate of the image
+ * @param filename the name of the image/picture, e.g., "ball.gif"
+ * @param scaledWidth the width of the scaled image (in screen coordinates)
+ * @param scaledHeight the height of the scaled image (in screen coordinates)
+ * @throws IllegalArgumentException if either {@code scaledWidth}
+ * or {@code scaledHeight} is negative
+ * @throws IllegalArgumentException if the image filename is invalid
+ * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite
+ * @throws IllegalArgumentException if {@code filename} is {@code null}
+ */
+ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(scaledWidth, "scaled width");
+ validate(scaledHeight, "scaled height");
+ validateNotNull(filename, "filename");
+ validateNonnegative(scaledWidth, "scaled width");
+ validateNonnegative(scaledHeight, "scaled height");
+
+ Image image = getImage(filename);
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(scaledWidth);
+ double hs = factorY(scaledHeight);
+ if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+ else {
+ offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
+ (int) Math.round(ys - hs/2.0),
+ (int) Math.round(ws),
+ (int) Math.round(hs), null);
+ }
+ draw();
+ }
+
+
+ /**
+ * Draws the specified image centered at (x, y), rotated
+ * given number of degrees, and rescaled to the specified bounding box.
+ * The supported image formats are typically JPEG, PNG, GIF, TIFF, and BMP.
+ *
+ * @param x the center x-coordinate of the image
+ * @param y the center y-coordinate of the image
+ * @param filename the name of the image/picture, e.g., "ball.gif"
+ * @param scaledWidth the width of the scaled image (in screen coordinates)
+ * @param scaledHeight the height of the scaled image (in screen coordinates)
+ * @param degrees is the number of degrees to rotate counterclockwise
+ * @throws IllegalArgumentException if either {@code scaledWidth}
+ * or {@code scaledHeight} is negative
+ * @throws IllegalArgumentException if the image filename is invalid
+ */
+ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(scaledWidth, "scaled width");
+ validate(scaledHeight, "scaled height");
+ validate(degrees, "degrees");
+ validateNotNull(filename, "filename");
+ validateNonnegative(scaledWidth, "scaled width");
+ validateNonnegative(scaledHeight, "scaled height");
+
+ Image image = getImage(filename);
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ double ws = factorX(scaledWidth);
+ double hs = factorY(scaledHeight);
+ if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt");
+ if (ws <= 1 && hs <= 1) pixel(x, y);
+
+ offscreen.rotate(Math.toRadians(-degrees), xs, ys);
+ offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
+ (int) Math.round(ys - hs/2.0),
+ (int) Math.round(ws),
+ (int) Math.round(hs), null);
+ offscreen.rotate(Math.toRadians(+degrees), xs, ys);
+
+ draw();
+ }
+
+ /***************************************************************************
+ * Drawing text.
+ ***************************************************************************/
+
+ /**
+ * Writes the given text string in the current font, centered at (x, y).
+ *
+ * @param x the center x-coordinate of the text
+ * @param y the center y-coordinate of the text
+ * @param text the text to write
+ * @throws IllegalArgumentException if {@code text} is {@code null}
+ * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite
+ */
+ public static void text(double x, double y, String text) {
+ validate(x, "x");
+ validate(y, "y");
+ validateNotNull(text, "text");
+
+ offscreen.setFont(font);
+ FontMetrics metrics = offscreen.getFontMetrics();
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ int ws = metrics.stringWidth(text);
+ int hs = metrics.getDescent();
+ offscreen.drawString(text, (float) (xs - ws/2.0), (float) (ys + hs));
+ draw();
+ }
+
+ /**
+ * Writes the given text string in the current font, centered at (x, y) and
+ * rotated by the specified number of degrees.
+ * @param x the center x-coordinate of the text
+ * @param y the center y-coordinate of the text
+ * @param text the text to write
+ * @param degrees is the number of degrees to rotate counterclockwise
+ * @throws IllegalArgumentException if {@code text} is {@code null}
+ * @throws IllegalArgumentException if {@code x}, {@code y}, or {@code degrees} is either NaN or infinite
+ */
+ public static void text(double x, double y, String text, double degrees) {
+ validate(x, "x");
+ validate(y, "y");
+ validate(degrees, "degrees");
+ validateNotNull(text, "text");
+
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ offscreen.rotate(Math.toRadians(-degrees), xs, ys);
+ text(x, y, text);
+ offscreen.rotate(Math.toRadians(+degrees), xs, ys);
+ }
+
+
+ /**
+ * Writes the given text string in the current font, left-aligned at (x, y).
+ * @param x the x-coordinate of the text
+ * @param y the y-coordinate of the text
+ * @param text the text
+ * @throws IllegalArgumentException if {@code text} is {@code null}
+ * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite
+ */
+ public static void textLeft(double x, double y, String text) {
+ validate(x, "x");
+ validate(y, "y");
+ validateNotNull(text, "text");
+
+ offscreen.setFont(font);
+ FontMetrics metrics = offscreen.getFontMetrics();
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ int hs = metrics.getDescent();
+ offscreen.drawString(text, (float) xs, (float) (ys + hs));
+ draw();
+ }
+
+ /**
+ * Writes the given text string in the current font, right-aligned at (x, y).
+ *
+ * @param x the x-coordinate of the text
+ * @param y the y-coordinate of the text
+ * @param text the text to write
+ * @throws IllegalArgumentException if {@code text} is {@code null}
+ * @throws IllegalArgumentException if {@code x} or {@code y} is either NaN or infinite
+ */
+ public static void textRight(double x, double y, String text) {
+ validate(x, "x");
+ validate(y, "y");
+ validateNotNull(text, "text");
+
+ offscreen.setFont(font);
+ FontMetrics metrics = offscreen.getFontMetrics();
+ double xs = scaleX(x);
+ double ys = scaleY(y);
+ int ws = metrics.stringWidth(text);
+ int hs = metrics.getDescent();
+ offscreen.drawString(text, (float) (xs - ws), (float) (ys + hs));
+ draw();
+ }
+
+
+ /**
+ * Copies the offscreen buffer to the onscreen buffer, pauses for t milliseconds
+ * and enables double buffering.
+ * @param t number of milliseconds
+ * @throws IllegalArgumentException if {@code t} is negative
+ * @deprecated replaced by {@link #enableDoubleBuffering()}, {@link #show()}, and {@link #pause(int t)}
+ */
+ @Deprecated
+ public static void show(int t) {
+ validateNonnegative(t, "t");
+ show();
+ pause(t);
+ enableDoubleBuffering();
+ }
+
+ /**
+ * Pauses for t milliseconds. This method is intended to support computer animations.
+ * @param t number of milliseconds
+ * @throws IllegalArgumentException if {@code t} is negative
+ */
+ public static void pause(int t) {
+ validateNonnegative(t, "t");
+ try {
+ Thread.sleep(t);
+ }
+ catch (InterruptedException e) {
+ System.out.println("Error sleeping");
+ }
+ }
+
+ /**
+ * Copies offscreen buffer to onscreen buffer. There is no reason to call
+ * this method unless double buffering is enabled.
+ */
+ public static void show() {
+ onscreen.drawImage(offscreenImage, 0, 0, null);
+
+ // make frame visible upon first call to show()
+ if (frame.isVisible() != isJFrameVisible) {
+ frame.setVisible(isJFrameVisible);
+ }
+
+ frame.repaint();
+ }
+
+ // draw onscreen if defer is false
+ private static void draw() {
+ if (!defer) show();
+ }
+
+ /**
+ * Enables double buffering. All subsequent calls to
+ * drawing methods such as {@code line()}, {@code circle()},
+ * and {@code square()} will be deferred until the next call
+ * to show(). Useful for animations.
+ */
+ public static void enableDoubleBuffering() {
+ defer = true;
+ }
+
+ /**
+ * Disables double buffering. All subsequent calls to
+ * drawing methods such as {@code line()}, {@code circle()},
+ * and {@code square()} will be displayed on screen when called.
+ * This is the default.
+ */
+ public static void disableDoubleBuffering() {
+ defer = false;
+ }
+
+
+ /***************************************************************************
+ * Save drawing to a file.
+ ***************************************************************************/
+
+ /**
+ * Saves the drawing to a file in a supported file format
+ * (typically JPEG, PNG, GIF, TIFF, and BMP).
+ * The filetype extension must be {@code .jpg}, {@code .png}, {@code .gif},
+ * {@code .bmp}, or {@code .tif}.
+ *
+ * @param filename the name of the file
+ * @throws IllegalArgumentException if {@code filename} is {@code null}
+ * @throws IllegalArgumentException if {@code filename} is the empty string
+ * @throws IllegalArgumentException if {@code filename} has invalid filetype extension
+ * @throws IllegalArgumentException if cannot write the file {@code filename}
+ */
+ public static void save(String filename) {
+ validateNotNull(filename, "filename");
+ if (filename.length() == 0) {
+ throw new IllegalArgumentException("argument to save() is the empty string");
+ }
+
+ File file = new File(filename);
+ String suffix = filename.substring(filename.lastIndexOf('.') + 1);
+ if (!filename.contains(".") || suffix.length() == 0) {
+ throw new IllegalArgumentException("The filename '" + filename + "' has no filetype extension, such as .jpg or .png");
+ }
+
+ try {
+ // if the file format supports transparency (such as PNG or GIF)
+ if (ImageIO.write(onscreenImage, suffix, file)) return;
+
+ // if the file format does not support transparency (such as JPEG or BMP)
+ BufferedImage saveImage = new BufferedImage(2*width, 2*height, BufferedImage.TYPE_INT_RGB);
+ saveImage.createGraphics().drawImage(onscreenImage, 0, 0, Color.WHITE, null);
+ if (ImageIO.write(saveImage, suffix, file)) return;
+
+ // failed to save the file; probably wrong format
+ throw new IllegalArgumentException("The filetype '" + suffix + "' is not supported");
+ }
+ catch (IOException e) {
+ throw new IllegalArgumentException("could not write file '" + filename + "'", e);
+ }
+ }
+
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void actionPerformed(ActionEvent event) {
+ FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE);
+ chooser.setVisible(true);
+ String selectedDirectory = chooser.getDirectory();
+ String selectedFilename = chooser.getFile();
+ if (selectedDirectory != null && selectedFilename != null) {
+ try {
+ StdDraw.save(selectedDirectory + selectedFilename);
+ }
+ catch (IllegalArgumentException e) {
+ System.err.println(e.getMessage());
+ }
+ }
+ }
+
+
+ /***************************************************************************
+ * Mouse interactions.
+ ***************************************************************************/
+
+ /**
+ * Returns true if the mouse is being pressed.
+ *
+ * @return {@code true} if the mouse is being pressed; {@code false} otherwise
+ */
+ public static boolean isMousePressed() {
+ synchronized (MOUSE_LOCK) {
+ return isMousePressed;
+ }
+ }
+
+ /**
+ * Returns true if the mouse is being pressed.
+ *
+ * @return {@code true} if the mouse is being pressed; {@code false} otherwise
+ * @deprecated replaced by {@link #isMousePressed()}
+ */
+ @Deprecated
+ public static boolean mousePressed() {
+ synchronized (MOUSE_LOCK) {
+ return isMousePressed;
+ }
+ }
+
+ /**
+ * Returns the x-coordinate of the mouse.
+ *
+ * @return the x-coordinate of the mouse
+ */
+ public static double mouseX() {
+ synchronized (MOUSE_LOCK) {
+ return mouseX;
+ }
+ }
+
+ /**
+ * Returns the y-coordinate of the mouse.
+ *
+ * @return y-coordinate of the mouse
+ */
+ public static double mouseY() {
+ synchronized (MOUSE_LOCK) {
+ return mouseY;
+ }
+ }
+
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseClicked(MouseEvent event) {
+ // this body is intentionally left empty
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseEntered(MouseEvent event) {
+ // this body is intentionally left empty
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseExited(MouseEvent event) {
+ // this body is intentionally left empty
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mousePressed(MouseEvent event) {
+ synchronized (MOUSE_LOCK) {
+ mouseX = StdDraw.userX(event.getX());
+ mouseY = StdDraw.userY(event.getY());
+ isMousePressed = true;
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseReleased(MouseEvent event) {
+ synchronized (MOUSE_LOCK) {
+ isMousePressed = false;
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseDragged(MouseEvent event) {
+ synchronized (MOUSE_LOCK) {
+ mouseX = StdDraw.userX(event.getX());
+ mouseY = StdDraw.userY(event.getY());
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void mouseMoved(MouseEvent event) {
+ synchronized (MOUSE_LOCK) {
+ mouseX = StdDraw.userX(event.getX());
+ mouseY = StdDraw.userY(event.getY());
+ }
+ }
+
+
+ /***************************************************************************
+ * Keyboard interactions.
+ ***************************************************************************/
+
+ /**
+ * Returns true if the user has typed a key (that has not yet been processed).
+ *
+ * @return {@code true} if the user has typed a key (that has not yet been processed
+ * by {@link #nextKeyTyped()}; {@code false} otherwise
+ */
+ public static boolean hasNextKeyTyped() {
+ synchronized (KEY_LOCK) {
+ return !keysTyped.isEmpty();
+ }
+ }
+
+ /**
+ * Returns the next key that was typed by the user (that your program has not already processed).
+ * This method should be preceded by a call to {@link #hasNextKeyTyped()} to ensure
+ * that there is a next key to process.
+ * This method returns a Unicode character corresponding to the key
+ * typed (such as {@code 'a'} or {@code 'A'}).
+ * It cannot identify action keys (such as F1 and arrow keys)
+ * or modifier keys (such as control).
+ *
+ * @return the next key typed by the user (that your program has not already processed).
+ * @throws NoSuchElementException if there is no remaining key
+ */
+ public static char nextKeyTyped() {
+ synchronized (KEY_LOCK) {
+ if (keysTyped.isEmpty()) {
+ throw new NoSuchElementException("your program has already processed all keystrokes");
+ }
+ return keysTyped.remove(keysTyped.size() - 1);
+ // return keysTyped.removeLast();
+ }
+ }
+
+ /**
+ * Returns true if the given key is being pressed.
+ *
+ * This method takes the keycode (corresponding to a physical key)
+ * as an argument. It can handle action keys
+ * (such as F1 and arrow keys) and modifier keys (such as shift and control).
+ * See {@link KeyEvent} for a description of key codes.
+ *
+ * @param keycode the key to check if it is being pressed
+ * @return {@code true} if {@code keycode} is currently being pressed;
+ * {@code false} otherwise
+ */
+ public static boolean isKeyPressed(int keycode) {
+ synchronized (KEY_LOCK) {
+ return keysDown.contains(keycode);
+ }
+ }
+
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyTyped(KeyEvent event) {
+ synchronized (KEY_LOCK) {
+ keysTyped.addFirst(event.getKeyChar());
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyPressed(KeyEvent event) {
+ synchronized (KEY_LOCK) {
+ keysDown.add(event.getKeyCode());
+ }
+ }
+
+ /**
+ * This method cannot be called directly.
+ */
+ @Override
+ public void keyReleased(KeyEvent event) {
+ synchronized (KEY_LOCK) {
+ keysDown.remove(event.getKeyCode());
+ }
+ }
+
+
+ /***************************************************************************
+ * For improved resolution on Mac Retina displays.
+ ***************************************************************************/
+
+ private static class RetinaImageIcon extends ImageIcon {
+
+ public RetinaImageIcon(Image image) {
+ super(image);
+ }
+
+ public int getIconWidth() {
+ return super.getIconWidth() / 2;
+ }
+
+ /**
+ * Returns the height of the icon.
+ *
+ * @return the height in pixels of this icon
+ */
+ public int getIconHeight() {
+ return super.getIconHeight() / 2;
+ }
+
+ public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g2.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.scale(0.5, 0.5);
+ super.paintIcon(c, g2, x * 2, y * 2);
+ g2.dispose();
+ }
+ }
+
+
+ /**
+ * Test client.
+ *
+ * @param args the command-line arguments
+ */
+ public static void main(String[] args) {
+ StdDraw.square(0.2, 0.8, 0.1);
+ StdDraw.filledSquare(0.8, 0.8, 0.2);
+ StdDraw.circle(0.8, 0.2, 0.2);
+
+ StdDraw.setPenColor(StdDraw.BOOK_RED);
+ StdDraw.setPenRadius(0.02);
+ StdDraw.arc(0.8, 0.2, 0.1, 200, 45);
+
+ // draw a blue diamond
+ StdDraw.setPenRadius();
+ StdDraw.setPenColor(StdDraw.BOOK_BLUE);
+ double[] x = { 0.1, 0.2, 0.3, 0.2 };
+ double[] y = { 0.2, 0.3, 0.2, 0.1 };
+ StdDraw.filledPolygon(x, y);
+
+ // text
+ StdDraw.setPenColor(StdDraw.BLACK);
+ StdDraw.text(0.2, 0.5, "black text");
+ StdDraw.setPenColor(StdDraw.WHITE);
+ StdDraw.text(0.8, 0.8, "white text");
+ }
+
+}
diff --git a/common/common.typ b/common/common.typ
index 85f02a7..eae5ece 100644
--- a/common/common.typ
+++ b/common/common.typ
@@ -6,8 +6,12 @@
#doc
]
-#let embedClass(name: str) = {
+#let embedClass(name: str, label: none) = {
+ show figure: set block(width: 100%)
show figure: set align(left)
show figure.caption: set align(center)
- figure(caption: name, raw(read("../" + name + ".java"), lang:"java", block: true))
+ [
+ #figure(caption: name, kind: "Class", supplement: [Class], raw(read("../" + name + ".java"), lang:"java", block: true))
+ #label
+ ]
}
\ No newline at end of file
diff --git a/shell.nix b/shell.nix
new file mode 100644
index 0000000..7631554
--- /dev/null
+++ b/shell.nix
@@ -0,0 +1,11 @@
+{
+ pkgs ? import
+ * public class TestStdDraw {
+ * public static void main(String[] args) {
+ * StdDraw.setPenRadius(0.05);
+ * StdDraw.setPenColor(StdDraw.BLUE);
+ * StdDraw.point(0.5, 0.5);
+ * StdDraw.setPenColor(StdDraw.MAGENTA);
+ * StdDraw.line(0.2, 0.2, 0.8, 0.2);
+ * }
+ * }
+ *
+ * If you compile and execute the program, you should see a window
+ * appear with a thick magenta line and a blue point.
+ * This program illustrates the two main types of methods in standard
+ * drawing—methods that draw geometric shapes and methods that
+ * control drawing parameters.
+ * The methods {@code StdDraw.line()} and {@code StdDraw.point()}
+ * draw lines and points; the methods {@code StdDraw.setPenRadius()}
+ * and {@code StdDraw.setPenColor()} control the line thickness and color.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * double[] x = { 0.1, 0.2, 0.3, 0.2 };
+ * double[] y = { 0.2, 0.3, 0.2, 0.1 };
+ * StdDraw.filledPolygon(x, y);
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * import statement allows you to refer to Font
+ * directly, without needing the fully qualified name java.awt.Font.
+ *
+ * import java.awt.Font;
+ * ...
+ * Font font = new Font("Arial", Font.BOLD, 60);
+ * StdDraw.setFont(font);
+ * StdDraw.text(0.5, 0.5, "Hello, World");
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * StdDraw.setScale(-2.0, +2.0);
+ * StdDraw.enableDoubleBuffering();
+ *
+ * for (double t = 0.0; true; t += 0.02) {
+ * double x = Math.sin(t);
+ * double y = Math.cos(t);
+ * StdDraw.clear();
+ * StdDraw.filledCircle(x, y, 0.1);
+ * StdDraw.filledCircle(-x, -y, 0.1);
+ * StdDraw.show();
+ * StdDraw.pause(20);
+ * }
+ *
+ * Without double buffering, the balls would flicker as they move.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *