diff --git a/Body.class b/Body.class new file mode 100644 index 0000000..2ef4398 Binary files /dev/null and b/Body.class differ diff --git a/Body.java b/Body.java new file mode 100644 index 0000000..a56d01f --- /dev/null +++ b/Body.java @@ -0,0 +1,152 @@ +public class Body { + + public static final double G = 6.673e-11; + private double mass; + private double px; + private double py; + private double vx; + private double vy; + private double fx; + private double fy; + + public Body(double mass, double px, double py, double vx, double vy){ + this.px = px; + this.py = py; + this.vx = vx; + this.vy = vy; + this.mass = mass; + } + + + @Override + public String toString() { + return ""; + } + + public double getDistance(Body otherBody){ + + double x1 = this.getXpos(); + double x2 = otherBody.getXpos(); + double y1 = this.getYpos(); + double y2 = otherBody.getYpos(); + + + double distance = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)); + + + return distance; + } + + public double getSpeed(){ + + return Math.sqrt((this.getXvelo() * this.getXvelo()) + (this.getYvelo() * this.getYvelo())); + } + + public double getForceScalar(Body otherBody){ + + double distance = this.getDistance(otherBody); + double m1 = this.getMass(); + double m2 = otherBody.getMass(); + + double force = G * ((m1 * m2) / distance); + + return force; + } + + public double getXforce(Body otherBody){ + + double distance = otherBody.getXpos() - this.getXpos(); + double m1 = this.getMass(); + double m2 = otherBody.getMass(); + + double force = G * ((m1 * m2) / distance); + + return force; + + + } + + public double getYforce(Body otherBody){ + + double distance = otherBody.getYpos() - this.getYpos(); + double m1 = this.getMass(); + double m2 = otherBody.getMass(); + + double force = G * ((m1 * m2) / distance); + + return force; + } + + public void calcNetForce(Body[] bodies){ + double netXForce = 0; + double netYForce = 0; + for(int i = 0; i < bodies.length; i ++){ + if(this != bodies[i]){ + netXForce = netXForce + this.getXforce(bodies[i]); + netYForce = netYForce + this.getYforce(bodies[i]); + } + } + + this.fx = netXForce; + this.fy = netYForce; + } + + public void updateVelocity(){ + // TODO: Implament updateVelocity + } + + public double getXpos(){ + return this.px; + } + + public double getYpos(){ + return this.py; + } + + public double getXvelo(){ + return this.vx; + } + + public double getYvelo(){ + return this.vy; + } + + public double getMass(){ + return this.mass; + } + + public double getNetXforce(){ + return this.fx; + } + + public double getNetYforce(){ + return this.fy; + } + + public static void main(String[] args){ + + // Body testBody = new Body(15000, 0, 0, 10, 10); + // Body testBody2 = new Body(27000, 2, 1, 10, 10); + // Body testBody3 = new Body(29530, 4, 2, 10, 10); + // Body testBody4 = new Body(29530000, -4, 4, 10, 10); + // Body[] bodies = new Body[4]; + // bodies[0] = testBody; + // bodies[1] = testBody2; + // bodies[2] = testBody3; + // bodies[3] = testBody4; + + // System.out.println(testBody.getSpeed()); + // System.out.println(testBody.getDistance(testBody2)); + // System.out.println(testBody.getXforce(testBody2)); + // System.out.println(testBody.getYforce(testBody2)); + + // testBody.calcNetForce(bodies); + // System.out.println(testBody.fx); + // System.out.println(testBody.fy); + + + + + } + +} diff --git a/Space.class b/Space.class new file mode 100644 index 0000000..163a248 Binary files /dev/null and b/Space.class differ diff --git a/Space.java b/Space.java new file mode 100644 index 0000000..2271346 --- /dev/null +++ b/Space.java @@ -0,0 +1,50 @@ +public class Space { + + Body[] bodies; + double height; + double width; + + public Space(Body[] bodies, double height, double width){ + + this.bodies = bodies; + this.height = height; + this.width = width; + } + + public void calculateForces(){ + + //double runningTotal = 0; + for(int i = 0; i < this.bodies.length; i ++){ + this.bodies[i].calcNetForce(this.bodies); + } + } + + public static void main(String args[]){ + + Body testBody = new Body(15000, 0, 0, 10, 10); + Body testBody2 = new Body(27000, 2, 1, 10, 10); + Body testBody3 = new Body(29530, 4, 2, 10, 10); + Body testBody4 = new Body(29530000, -4, 4, 10, 10); + Body[] bodies = new Body[4]; + bodies[0] = testBody; + bodies[1] = testBody2; + bodies[2] = testBody3; + bodies[3] = testBody4; + + Space space = new Space(bodies, 500, 500); + space.calculateForces(); + + System.out.println(space.bodies[0].getNetXforce()); + System.out.println(space.bodies[0].getNetYforce()); + + System.out.println(space.bodies[1].getNetXforce()); + System.out.println(space.bodies[1].getNetYforce()); + + System.out.println(space.bodies[2].getNetXforce()); + System.out.println(space.bodies[2].getNetYforce()); + + System.out.println(space.bodies[3].getNetXforce()); + System.out.println(space.bodies[3].getNetYforce()); + + } +} diff --git a/StdDraw.class b/StdDraw.class new file mode 100644 index 0000000..ef0fcc4 Binary files /dev/null and b/StdDraw.class differ diff --git a/StdDraw.java b/StdDraw.java new file mode 100644 index 0000000..dd93791 --- /dev/null +++ b/StdDraw.java @@ -0,0 +1,1118 @@ +/************************************************************************* + * Compilation: javac StdDraw.java + * Execution: java StdDraw + * + * 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 points, lines, and curves + * in a window on your computer and to save the drawings to a file. + * + * 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 + * + *************************************************************************/ + +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.io.*; +import java.net.*; +import java.util.LinkedList; +import java.util.TreeSet; +import javax.imageio.ImageIO; +import javax.swing.*; + +/** + * Standard draw. 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. + *

+ * For additional documentation, see Section 1.5 of + * Introduction to Programming in Java: An Interdisciplinary Approach by Robert Sedgewick and Kevin Wayne. + * + * @author Robert Sedgewick + * @author Kevin Wayne + */ +public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener { + + // pre-defined colors + public static final Color BLACK = Color.BLACK; + public static final Color BLUE = Color.BLUE; + public static final Color CYAN = Color.CYAN; + public static final Color DARK_GRAY = Color.DARK_GRAY; + public static final Color GRAY = Color.GRAY; + public static final Color GREEN = Color.GREEN; + public static final Color LIGHT_GRAY = Color.LIGHT_GRAY; + public static final Color MAGENTA = Color.MAGENTA; + public static final Color ORANGE = Color.ORANGE; + public static final Color PINK = Color.PINK; + public static final Color RED = Color.RED; + public static final Color WHITE = Color.WHITE; + public static final Color YELLOW = Color.YELLOW; + + /** + * 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); + public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243); + + /** + * 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); + + // default colors + private static final Color DEFAULT_PEN_COLOR = BLACK; + private static final Color DEFAULT_CLEAR_COLOR = WHITE; + + // current pen color + private static Color penColor; + + // 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; + + // show we draw immediately or wait until next show? + private static boolean defer = false; + + // boundary of drawing canvas, 5% border + private static final double BORDER = 0.05; + 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, ymin, xmax, ymax; + + // for synchronization + private static Object mouseLock = new Object(); + private static Object keyLock = new Object(); + + // default font + private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16); + + // current font + private static Font 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; + + // mouse state + private static boolean mousePressed = false; + private static double mouseX = 0; + private static double mouseY = 0; + + // queue of typed key characters + private static LinkedList keysTyped = new LinkedList(); + + // set of key codes currently pressed down + private static TreeSet keysDown = new TreeSet(); + + + // singleton pattern: client can't instantiate + private StdDraw() { } + + + // static initializer + static { init(); } + + /** + * Set the window size to the default size 512-by-512 pixels. + */ + public static void setCanvasSize() { + setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE); + } + + /** + * Set the window size to w-by-h pixels. + * + * @param w the width as a number of pixels + * @param h the height as a number of pixels + * @throws a IllegalArgumentException if the width or height is 0 or negative + */ + public static void setCanvasSize(int w, int h) { + if (w < 1 || h < 1) throw new IllegalArgumentException("width and height must be positive"); + width = w; + height = h; + init(); + } + + // init + private static void init() { + if (frame != null) frame.setVisible(false); + frame = new JFrame(); + offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + offscreen = offscreenImage.createGraphics(); + onscreen = onscreenImage.createGraphics(); + setXscale(); + setYscale(); + offscreen.setColor(DEFAULT_CLEAR_COLOR); + offscreen.fillRect(0, 0, width, height); + setPenColor(); + setPenRadius(); + setFont(); + clear(); + + // add antialiasing + RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + offscreen.addRenderingHints(hints); + + // frame stuff + ImageIcon icon = new ImageIcon(onscreenImage); + JLabel draw = new JLabel(icon); + + draw.addMouseListener(std); + draw.addMouseMotionListener(std); + + frame.setContentPane(draw); + frame.addKeyListener(std); // JLabel cannot get keyboard focus + frame.setResizable(false); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows + // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window + frame.setTitle("Standard Draw"); + frame.setJMenuBar(createMenuBar()); + frame.pack(); + frame.requestFocusInWindow(); + frame.setVisible(true); + } + + // create the menu bar (changed to private) + private static JMenuBar createMenuBar() { + JMenuBar menuBar = new JMenuBar(); + JMenu menu = new JMenu("File"); + menuBar.add(menu); + JMenuItem menuItem1 = new JMenuItem(" Save... "); + menuItem1.addActionListener(std); + menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, + Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + menu.add(menuItem1); + return menuBar; + } + + + /************************************************************************* + * User and screen coordinate systems + *************************************************************************/ + + /** + * Set the x-scale to be the default (between 0.0 and 1.0). + */ + public static void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); } + + /** + * Set the y-scale to be the default (between 0.0 and 1.0). + */ + public static void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); } + + /** + * Set the x-scale (a 10% border is added to the values) + * @param min the minimum value of the x-scale + * @param max the maximum value of the x-scale + */ + public static void setXscale(double min, double max) { + double size = max - min; + synchronized (mouseLock) { + xmin = min - BORDER * size; + xmax = max + BORDER * size; + } + } + + /** + * Set the y-scale (a 10% border is added to the values). + * @param min the minimum value of the y-scale + * @param max the maximum value of the y-scale + */ + public static void setYscale(double min, double max) { + double size = max - min; + synchronized (mouseLock) { + ymin = min - BORDER * size; + ymax = max + BORDER * size; + } + } + + /** + * Set the x-scale and y-scale (a 10% border is added to the values) + * @param min the minimum value of the x- and y-scales + * @param max the maximum value of the x- and y-scales + */ + public static void setScale(double min, double max) { + double size = max - min; + synchronized (mouseLock) { + xmin = min - BORDER * size; + xmax = max + BORDER * size; + ymin = min - BORDER * size; + ymax = max + BORDER * size; + } + } + + // helper functions that scale from user coordinates to screen coordinates and back + private static double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); } + private static double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); } + private static double factorX(double w) { return w * width / Math.abs(xmax - xmin); } + private static double factorY(double h) { return h * height / Math.abs(ymax - ymin); } + private static double userX(double x) { return xmin + x * (xmax - xmin) / width; } + private static double userY(double y) { return ymax - y * (ymax - ymin) / height; } + + + /** + * Clear the screen to the default color (white). + */ + public static void clear() { clear(DEFAULT_CLEAR_COLOR); } + /** + * Clear the screen to the given color. + * @param color the Color to make the background + */ + public static void clear(Color color) { + offscreen.setColor(color); + offscreen.fillRect(0, 0, width, height); + offscreen.setColor(penColor); + draw(); + } + + /** + * Get the current pen radius. + */ + public static double getPenRadius() { return penRadius; } + + /** + * Set the pen size to the default (.002). + */ + public static void setPenRadius() { setPenRadius(DEFAULT_PEN_RADIUS); } + /** + * Set the radius of the pen to the given size. + * @param r the radius of the pen + * @throws IllegalArgumentException if r is negative + */ + public static void setPenRadius(double r) { + if (r < 0) throw new IllegalArgumentException("pen radius must be nonnegative"); + penRadius = r; + float scaledPenRadius = (float) (r * DEFAULT_SIZE); + BasicStroke stroke = new BasicStroke(scaledPenRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + // BasicStroke stroke = new BasicStroke(scaledPenRadius); + offscreen.setStroke(stroke); + } + + /** + * Get the current pen color. + */ + public static Color getPenColor() { return penColor; } + + /** + * Set the pen color to the default color (black). + */ + public static void setPenColor() { setPenColor(DEFAULT_PEN_COLOR); } + + /** + * Set the pen color to the given color. The available pen colors are + * BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA, + * ORANGE, PINK, RED, WHITE, and YELLOW. + * @param color the Color to make the pen + */ + public static void setPenColor(Color color) { + penColor = color; + offscreen.setColor(penColor); + } + + /** + * Set the pen color to the given 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 the amount of red, green, or blue are outside prescribed range + */ + public static void setPenColor(int red, int green, int blue) { + if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); + if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); + if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); + setPenColor(new Color(red, green, blue)); + } + + /** + * Get the current font. + */ + public static Font getFont() { return font; } + + /** + * Set the font to the default font (sans serif, 16 point). + */ + public static void setFont() { setFont(DEFAULT_FONT); } + + /** + * Set the font to the given value. + * @param f the font to make text + */ + public static void setFont(Font f) { font = f; } + + + /************************************************************************* + * Drawing geometric shapes. + *************************************************************************/ + + /** + * Draw a line from (x0, y0) to (x1, y1). + * @param x0 the x-coordinate of the starting point + * @param y0 the y-coordinate of the starting point + * @param x1 the x-coordinate of the destination point + * @param y1 the y-coordinate of the destination point + */ + public static void line(double x0, double y0, double x1, double y1) { + offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1))); + draw(); + } + + /** + * Draw one pixel at (x, y). + * @param x the x-coordinate of the pixel + * @param y the y-coordinate of the pixel + */ + private static void pixel(double x, double y) { + offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1); + } + + /** + * Draw a point at (x, y). + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + */ + public static void point(double x, double 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(); + } + + /** + * Draw a circle of radius r, centered on (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 r the radius of the circle + * @throws IllegalArgumentException if the radius of the circle is negative + */ + public static void circle(double x, double y, double r) { + if (r < 0) throw new IllegalArgumentException("circle radius must be nonnegative"); + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(2*r); + double hs = factorY(2*r); + if (ws <= 1 && hs <= 1) pixel(x, y); + else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); + draw(); + } + + /** + * Draw filled circle of radius r, centered on (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 r the radius of the circle + * @throws IllegalArgumentException if the radius of the circle is negative + */ + public static void filledCircle(double x, double y, double r) { + if (r < 0) throw new IllegalArgumentException("circle radius must be nonnegative"); + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(2*r); + double hs = factorY(2*r); + if (ws <= 1 && hs <= 1) pixel(x, y); + else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); + draw(); + } + + + /** + * Draw an ellipse with given semimajor and semiminor axes, centered on (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 of the axes are negative + */ + public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { + if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); + if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); + 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(); + } + + /** + * Draw an ellipse with given semimajor and semiminor axes, centered on (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 of the axes are negative + */ + public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { + if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); + if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); + 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(); + } + + + /** + * Draw an arc of radius r, centered on (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 r 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 the radius of the circle is negative + */ + public static void arc(double x, double y, double r, double angle1, double angle2) { + if (r < 0) throw new IllegalArgumentException("arc radius must be nonnegative"); + while (angle2 < angle1) angle2 += 360; + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(2*r); + double hs = factorY(2*r); + 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(); + } + + /** + * Draw a square of side length 2r, centered on (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 r radius is half the length of any side of the square + * @throws IllegalArgumentException if r is negative + */ + public static void square(double x, double y, double r) { + if (r < 0) throw new IllegalArgumentException("square side length must be nonnegative"); + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(2*r); + double hs = factorY(2*r); + if (ws <= 1 && hs <= 1) pixel(x, y); + else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); + draw(); + } + + /** + * Draw a filled square of side length 2r, centered on (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 r radius is half the length of any side of the square + * @throws IllegalArgumentException if r is negative + */ + public static void filledSquare(double x, double y, double r) { + if (r < 0) throw new IllegalArgumentException("square side length must be nonnegative"); + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(2*r); + double hs = factorY(2*r); + if (ws <= 1 && hs <= 1) pixel(x, y); + else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); + draw(); + } + + + /** + * Draw a rectangle of given half width and half height, centered on (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 is half the width of the rectangle + * @param halfHeight is half the height of the rectangle + * @throws IllegalArgumentException if halfWidth or halfHeight is negative + */ + public static void rectangle(double x, double y, double halfWidth, double halfHeight) { + if (halfWidth < 0) throw new IllegalArgumentException("half width must be nonnegative"); + if (halfHeight < 0) throw new IllegalArgumentException("half height must be nonnegative"); + 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(); + } + + /** + * Draw a filled rectangle of given half width and half height, centered on (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 is half the width of the rectangle + * @param halfHeight is half the height of the rectangle + * @throws IllegalArgumentException if halfWidth or halfHeight is negative + */ + public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) { + if (halfWidth < 0) throw new IllegalArgumentException("half width must be nonnegative"); + if (halfHeight < 0) throw new IllegalArgumentException("half height must be nonnegative"); + 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(); + } + + + /** + * Draw a polygon with the given (x[i], y[i]) coordinates. + * @param x an array of all the x-coordindates of the polygon + * @param y an array of all the y-coordindates of the polygon + */ + public static void polygon(double[] x, double[] y) { + int N = x.length; + 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(); + } + + /** + * Draw a filled polygon with the given (x[i], y[i]) coordinates. + * @param x an array of all the x-coordindates of the polygon + * @param y an array of all the y-coordindates of the polygon + */ + public static void filledPolygon(double[] x, double[] y) { + int N = x.length; + 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) { + + // to read from file + ImageIcon icon = new ImageIcon(filename); + + // try to read from URL + if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { + try { + URL url = new URL(filename); + icon = new ImageIcon(url); + } catch (Exception e) { /* not a url */ } + } + + // in case file is inside a .jar + if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { + URL url = StdDraw.class.getResource(filename); + if (url == null) throw new IllegalArgumentException("image " + filename + " not found"); + icon = new ImageIcon(url); + } + + return icon.getImage(); + } + + /** + * Draw picture (gif, jpg, or png) centered on (x, y). + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image + * @param s the name of the image/picture, e.g., "ball.gif" + * @throws IllegalArgumentException if the image is corrupt + */ + public static void picture(double x, double y, String s) { + Image image = getImage(s); + double xs = scaleX(x); + double ys = scaleY(y); + int ws = image.getWidth(null); + int hs = image.getHeight(null); + if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + s + " is corrupt"); + + offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); + draw(); + } + + /** + * Draw picture (gif, jpg, or png) centered on (x, y), + * rotated given number of degrees + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image + * @param s 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 is corrupt + */ + public static void picture(double x, double y, String s, double degrees) { + Image image = getImage(s); + double xs = scaleX(x); + double ys = scaleY(y); + int ws = image.getWidth(null); + int hs = image.getHeight(null); + if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + s + " 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(); + } + + /** + * Draw picture (gif, jpg, or png) centered on (x, y), rescaled to w-by-h. + * @param x the center x coordinate of the image + * @param y the center y coordinate of the image + * @param s the name of the image/picture, e.g., "ball.gif" + * @param w the width of the image + * @param h the height of the image + * @throws IllegalArgumentException if the width height are negative + * @throws IllegalArgumentException if the image is corrupt + */ + public static void picture(double x, double y, String s, double w, double h) { + Image image = getImage(s); + double xs = scaleX(x); + double ys = scaleY(y); + if (w < 0) throw new IllegalArgumentException("width is negative: " + w); + if (h < 0) throw new IllegalArgumentException("height is negative: " + h); + double ws = factorX(w); + double hs = factorY(h); + if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + s + " 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(); + } + + + /** + * Draw picture (gif, jpg, or png) centered on (x, y), rotated + * given number of degrees, rescaled to w-by-h. + * @param x the center x-coordinate of the image + * @param y the center y-coordinate of the image + * @param s the name of the image/picture, e.g., "ball.gif" + * @param w the width of the image + * @param h the height of the image + * @param degrees is the number of degrees to rotate counterclockwise + * @throws IllegalArgumentException if the image is corrupt + */ + public static void picture(double x, double y, String s, double w, double h, double degrees) { + Image image = getImage(s); + double xs = scaleX(x); + double ys = scaleY(y); + double ws = factorX(w); + double hs = factorY(h); + if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + s + " 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. + *************************************************************************/ + + /** + * Write the given text string in the current font, centered on (x, y). + * @param x the center x-coordinate of the text + * @param y the center y-coordinate of the text + * @param s the text + */ + public static void text(double x, double y, String s) { + offscreen.setFont(font); + FontMetrics metrics = offscreen.getFontMetrics(); + double xs = scaleX(x); + double ys = scaleY(y); + int ws = metrics.stringWidth(s); + int hs = metrics.getDescent(); + offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); + draw(); + } + + /** + * Write the given text string in the current font, centered on (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 s the text + * @param degrees is the number of degrees to rotate counterclockwise + */ + public static void text(double x, double y, String s, double degrees) { + double xs = scaleX(x); + double ys = scaleY(y); + offscreen.rotate(Math.toRadians(-degrees), xs, ys); + text(x, y, s); + offscreen.rotate(Math.toRadians(+degrees), xs, ys); + } + + + /** + * Write 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 s the text + */ + public static void textLeft(double x, double y, String s) { + offscreen.setFont(font); + FontMetrics metrics = offscreen.getFontMetrics(); + double xs = scaleX(x); + double ys = scaleY(y); + int hs = metrics.getDescent(); + offscreen.drawString(s, (float) (xs), (float) (ys + hs)); + draw(); + } + + /** + * Write 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 s the text + */ + public static void textRight(double x, double y, String s) { + offscreen.setFont(font); + FontMetrics metrics = offscreen.getFontMetrics(); + double xs = scaleX(x); + double ys = scaleY(y); + int ws = metrics.stringWidth(s); + int hs = metrics.getDescent(); + offscreen.drawString(s, (float) (xs - ws), (float) (ys + hs)); + draw(); + } + + + + /** + * Display on screen, pause for t milliseconds, and turn on + * animation mode: subsequent calls to + * drawing methods such as line(), circle(), and square() + * will not be displayed on screen until the next call to show(). + * This is useful for producing animations (clear the screen, draw a bunch of shapes, + * display on screen for a fixed amount of time, and repeat). It also speeds up + * drawing a huge number of shapes (call show(0) to defer drawing + * on screen, draw the shapes, and call show(0) to display them all + * on screen at once). + * @param t number of milliseconds + */ + public static void show(int t) { + defer = false; + draw(); + try { Thread.sleep(t); } + catch (InterruptedException e) { System.out.println("Error sleeping"); } + defer = true; + } + + /** + * Display on-screen and turn off animation mode: + * subsequent calls to + * drawing methods such as line(), circle(), and square() + * will be displayed on screen when called. This is the default. + */ + public static void show() { + defer = false; + draw(); + } + + // draw onscreen if defer is false + private static void draw() { + if (defer) return; + onscreen.drawImage(offscreenImage, 0, 0, null); + frame.repaint(); + } + + + /************************************************************************* + * Save drawing to a file. + *************************************************************************/ + + /** + * Save onscreen image to file - suffix must be png, jpg, or gif. + * @param filename the name of the file with one of the required suffixes + */ + public static void save(String filename) { + File file = new File(filename); + String suffix = filename.substring(filename.lastIndexOf('.') + 1); + + // png files + if (suffix.toLowerCase().equals("png")) { + try { ImageIO.write(onscreenImage, suffix, file); } + catch (IOException e) { e.printStackTrace(); } + } + + // need to change from ARGB to RGB for jpeg + // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727 + else if (suffix.toLowerCase().equals("jpg")) { + WritableRaster raster = onscreenImage.getRaster(); + WritableRaster newRaster; + newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); + DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel(); + DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), + cm.getRedMask(), + cm.getGreenMask(), + cm.getBlueMask()); + BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); + try { ImageIO.write(rgbBuffer, suffix, file); } + catch (IOException e) { e.printStackTrace(); } + } + + else { + System.out.println("Invalid image file type: " + suffix); + } + } + + + /** + * This method cannot be called directly. + */ + public void actionPerformed(ActionEvent e) { + FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE); + chooser.setVisible(true); + String filename = chooser.getFile(); + if (filename != null) { + StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile()); + } + } + + + /************************************************************************* + * Mouse interactions. + *************************************************************************/ + + /** + * Is the mouse being pressed? + * @return true or false + */ + public static boolean mousePressed() { + synchronized (mouseLock) { + return mousePressed; + } + } + + /** + * What is the x-coordinate of the mouse? + * @return the value of the x-coordinate of the mouse + */ + public static double mouseX() { + synchronized (mouseLock) { + return mouseX; + } + } + + /** + * What is the y-coordinate of the mouse? + * @return the value of the y-coordinate of the mouse + */ + public static double mouseY() { + synchronized (mouseLock) { + return mouseY; + } + } + + + /** + * This method cannot be called directly. + */ + public void mouseClicked(MouseEvent e) { } + + /** + * This method cannot be called directly. + */ + public void mouseEntered(MouseEvent e) { } + + /** + * This method cannot be called directly. + */ + public void mouseExited(MouseEvent e) { } + + /** + * This method cannot be called directly. + */ + public void mousePressed(MouseEvent e) { + synchronized (mouseLock) { + mouseX = StdDraw.userX(e.getX()); + mouseY = StdDraw.userY(e.getY()); + mousePressed = true; + } + } + + /** + * This method cannot be called directly. + */ + public void mouseReleased(MouseEvent e) { + synchronized (mouseLock) { + mousePressed = false; + } + } + + /** + * This method cannot be called directly. + */ + public void mouseDragged(MouseEvent e) { + synchronized (mouseLock) { + mouseX = StdDraw.userX(e.getX()); + mouseY = StdDraw.userY(e.getY()); + } + } + + /** + * This method cannot be called directly. + */ + public void mouseMoved(MouseEvent e) { + synchronized (mouseLock) { + mouseX = StdDraw.userX(e.getX()); + mouseY = StdDraw.userY(e.getY()); + } + } + + + /************************************************************************* + * Keyboard interactions. + *************************************************************************/ + + /** + * Has the user typed a key? + * @return true if the user has typed a key, false otherwise + */ + public static boolean hasNextKeyTyped() { + synchronized (keyLock) { + return !keysTyped.isEmpty(); + } + } + + /** + * What is the next key that was typed by the user? This method returns + * a Unicode character corresponding to the key typed (such as 'a' or 'A'). + * It cannot identify action keys (such as F1 + * and arrow keys) or modifier keys (such as control). + * @return the next Unicode key typed + */ + public static char nextKeyTyped() { + synchronized (keyLock) { + return keysTyped.removeLast(); + } + } + + /** + * Is the keycode currently being pressed? 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 KeyEvent.java + * for a description of key codes. + * @return true if keycode is currently being pressed, false otherwise + */ + public static boolean isKeyPressed(int keycode) { + synchronized (keyLock) { + return keysDown.contains(keycode); + } + } + + + /** + * This method cannot be called directly. + */ + public void keyTyped(KeyEvent e) { + synchronized (keyLock) { + keysTyped.addFirst(e.getKeyChar()); + } + } + + /** + * This method cannot be called directly. + */ + public void keyPressed(KeyEvent e) { + synchronized (keyLock) { + keysDown.add(e.getKeyCode()); + } + } + + /** + * This method cannot be called directly. + */ + public void keyReleased(KeyEvent e) { + synchronized (keyLock) { + keysDown.remove(e.getKeyCode()); + } + } + + + + + /** + * Test client. + */ + public static void main(String[] args) { + StdDraw.square(.2, .8, .1); + StdDraw.filledSquare(.8, .8, .2); + StdDraw.circle(.8, .2, .2); + + StdDraw.setPenColor(StdDraw.BOOK_RED); + StdDraw.setPenRadius(.02); + StdDraw.arc(.8, .2, .1, 200, 45); + + // draw a blue diamond + StdDraw.setPenRadius(); + StdDraw.setPenColor(StdDraw.BOOK_BLUE); + double[] x = { .1, .2, .3, .2 }; + double[] y = { .2, .3, .2, .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"); + } + +} \ No newline at end of file