Advertisement
calcpage

LACS_StdDraw.java

May 31st, 2012
493
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 39.92 KB | None | 0 0
  1. /*************************************************************************
  2.  *  Compilation:  javac StdDraw.java
  3.  *  Execution:    java StdDraw
  4.  *
  5.  *  Standard drawing library. This class provides a basic capability for
  6.  *  creating drawings with your programs. It uses a simple graphics model that
  7.  *  allows you to create drawings consisting of points, lines, and curves
  8.  *  in a window on your computer and to save the drawings to a file.
  9.  *
  10.  *  Todo
  11.  *  ----
  12.  *    -  Add support for gradient fill, etc.
  13.  *
  14.  *  Remarks
  15.  *  -------
  16.  *    -  don't use AffineTransform for rescaling since it inverts
  17.  *       images and strings
  18.  *    -  careful using setFont in inner loop within an animation -
  19.  *       it can cause flicker
  20.  *
  21.  *************************************************************************/
  22.  
  23. import java.awt.*;
  24. import java.awt.event.*;
  25. import java.awt.geom.*;
  26. import java.awt.image.*;
  27. import java.io.*;
  28. import java.net.*;
  29. import java.util.LinkedList;
  30. import java.util.TreeSet;
  31. import javax.imageio.ImageIO;
  32. import javax.swing.*;
  33.  
  34. /**
  35.  *  <i>Standard draw</i>. This class provides a basic capability for
  36.  *  creating drawings with your programs. It uses a simple graphics model that
  37.  *  allows you to create drawings consisting of points, lines, and curves
  38.  *  in a window on your computer and to save the drawings to a file.
  39.  *  <p>
  40.  *  For additional documentation, see <a href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
  41.  *  <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by Robert Sedgewick and Kevin Wayne.
  42.  */
  43. public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener {
  44.  
  45.     // pre-defined colors
  46.     public static final Color BLACK      = Color.BLACK;
  47.     public static final Color BLUE       = Color.BLUE;
  48.     public static final Color CYAN       = Color.CYAN;
  49.     public static final Color DARK_GRAY  = Color.DARK_GRAY;
  50.     public static final Color GRAY       = Color.GRAY;
  51.     public static final Color GREEN      = Color.GREEN;
  52.     public static final Color LIGHT_GRAY = Color.LIGHT_GRAY;
  53.     public static final Color MAGENTA    = Color.MAGENTA;
  54.     public static final Color ORANGE     = Color.ORANGE;
  55.     public static final Color PINK       = Color.PINK;
  56.     public static final Color RED        = Color.RED;
  57.     public static final Color WHITE      = Color.WHITE;
  58.     public static final Color YELLOW     = Color.YELLOW;
  59.  
  60.     /**
  61.      * Shade of blue used in Introduction to Programming in Java.
  62.      * It is Pantone 300U. The RGB values are approximately (9, 90, 166).
  63.      */
  64.     public static final Color BOOK_BLUE       = new Color(  9,  90, 166);
  65.     public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243);
  66.  
  67.     /**
  68.      * Shade of red used in Algorithms 4th edition.
  69.      * It is Pantone 1805U. The RGB values are approximately (150, 35, 31).
  70.      */
  71.     public static final Color BOOK_RED = new Color(150, 35, 31);
  72.  
  73.     // default colors
  74.     private static final Color DEFAULT_PEN_COLOR   = BLACK;
  75.     private static final Color DEFAULT_CLEAR_COLOR = WHITE;
  76.  
  77.     // current pen color
  78.     private static Color penColor;
  79.  
  80.     // default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE
  81.     private static final int DEFAULT_SIZE = 512;
  82.     private static int width  = DEFAULT_SIZE;
  83.     private static int height = DEFAULT_SIZE;
  84.  
  85.     // default pen radius
  86.     private static final double DEFAULT_PEN_RADIUS = 0.002;
  87.  
  88.     // current pen radius
  89.     private static double penRadius;
  90.  
  91.     // show we draw immediately or wait until next show?
  92.     private static boolean defer = false;
  93.  
  94.     // boundary of drawing canvas, 5% border
  95.     private static final double BORDER = 0.05;
  96.     private static final double DEFAULT_XMIN = 0.0;
  97.     private static final double DEFAULT_XMAX = 1.0;
  98.     private static final double DEFAULT_YMIN = 0.0;
  99.     private static final double DEFAULT_YMAX = 1.0;
  100.     private static double xmin, ymin, xmax, ymax;
  101.  
  102.     // for synchronization
  103.     private static Object mouseLock = new Object();
  104.     private static Object keyLock = new Object();
  105.  
  106.     // default font
  107.     private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16);
  108.  
  109.     // current font
  110.     private static Font font;
  111.  
  112.     // double buffered graphics
  113.     private static BufferedImage offscreenImage, onscreenImage;
  114.     private static Graphics2D offscreen, onscreen;
  115.  
  116.     // singleton for callbacks: avoids generation of extra .class files
  117.     private static StdDraw std = new StdDraw();
  118.  
  119.     // the frame for drawing to the screen
  120.     private static JFrame frame;
  121.  
  122.     // mouse state
  123.     private static boolean mousePressed = false;
  124.     private static double mouseX = 0;
  125.     private static double mouseY = 0;
  126.  
  127.     // queue of typed key characters
  128.     private static LinkedList<Character> keysTyped = new LinkedList<Character>();
  129.  
  130.     // set of key codes currently pressed down
  131.     private static TreeSet<Integer> keysDown = new TreeSet<Integer>();
  132.  
  133.  
  134.     // not instantiable
  135.     private StdDraw() { }
  136.  
  137.  
  138.     // static initializer
  139.     static { init(); }
  140.  
  141.     /**
  142.      * Set the window size to the default size 512-by-512 pixels.
  143.      */
  144.     public static void setCanvasSize() {
  145.         setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE);
  146.     }
  147.  
  148.     /**
  149.      * Set the window size to w-by-h pixels.
  150.      *
  151.      * @param w the width as a number of pixels
  152.      * @param h the height as a number of pixels
  153.      * @throws a RunTimeException if the width or height is 0 or negative
  154.      */
  155.     public static void setCanvasSize(int w, int h) {
  156.         if (w < 1 || h < 1) throw new RuntimeException("width and height must be positive");
  157.         width = w;
  158.         height = h;
  159.         init();
  160.     }
  161.  
  162.     // init
  163.     private static void init() {
  164.         if (frame != null) frame.setVisible(false);
  165.         frame = new JFrame();
  166.         offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  167.         onscreenImage  = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  168.         offscreen = offscreenImage.createGraphics();
  169.         onscreen  = onscreenImage.createGraphics();
  170.         setXscale();
  171.         setYscale();
  172.         offscreen.setColor(DEFAULT_CLEAR_COLOR);
  173.         offscreen.fillRect(0, 0, width, height);
  174.         setPenColor();
  175.         setPenRadius();
  176.         setFont();
  177.         clear();
  178.  
  179.         // add antialiasing
  180.         RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
  181.                                                   RenderingHints.VALUE_ANTIALIAS_ON);
  182.         hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  183.         offscreen.addRenderingHints(hints);
  184.  
  185.         // frame stuff
  186.         ImageIcon icon = new ImageIcon(onscreenImage);
  187.         JLabel draw = new JLabel(icon);
  188.  
  189.         draw.addMouseListener(std);
  190.         draw.addMouseMotionListener(std);
  191.  
  192.         frame.setContentPane(draw);
  193.         frame.addKeyListener(std);    // JLabel cannot get keyboard focus
  194.         frame.setResizable(false);
  195.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);            // closes all windows
  196.         // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);      // closes only current window
  197.         frame.setTitle("Standard Draw");
  198.         frame.setJMenuBar(createMenuBar());
  199.         frame.pack();
  200.         frame.requestFocusInWindow();
  201.         frame.setVisible(true);
  202.     }
  203.  
  204.     // create the menu bar (changed to private)
  205.     private static JMenuBar createMenuBar() {
  206.         JMenuBar menuBar = new JMenuBar();
  207.         JMenu menu = new JMenu("File");
  208.         menuBar.add(menu);
  209.         JMenuItem menuItem1 = new JMenuItem(" Save...   ");
  210.         menuItem1.addActionListener(std);
  211.         menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
  212.                                 Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
  213.         menu.add(menuItem1);
  214.         return menuBar;
  215.     }
  216.  
  217.  
  218.    /*************************************************************************
  219.     *  User and screen coordinate systems
  220.     *************************************************************************/
  221.  
  222.     /**
  223.      * Set the x-scale to be the default (between 0.0 and 1.0).
  224.      */
  225.     public static void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); }
  226.  
  227.     /**
  228.      * Set the y-scale to be the default (between 0.0 and 1.0).
  229.      */
  230.     public static void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); }
  231.  
  232.     /**
  233.      * Set the x-scale (a 10% border is added to the values)
  234.      * @param min the minimum value of the x-scale
  235.      * @param max the maximum value of the x-scale
  236.      */
  237.     public static void setXscale(double min, double max) {
  238.         double size = max - min;
  239.         xmin = min - BORDER * size;
  240.         xmax = max + BORDER * size;
  241.     }
  242.  
  243.     /**
  244.      * Set the y-scale (a 10% border is added to the values).
  245.      * @param min the minimum value of the y-scale
  246.      * @param max the maximum value of the y-scale
  247.      */
  248.     public static void setYscale(double min, double max) {
  249.         double size = max - min;
  250.         ymin = min - BORDER * size;
  251.         ymax = max + BORDER * size;
  252.     }
  253.  
  254.     /**
  255.      * Set the x-scale and y-scale (a 10% border is added to the values)
  256.      * @param min the minimum value of the x- and y-scales
  257.      * @param max the maximum value of the x- and y-scales
  258.      */
  259.     public static void setScale(double min, double max) {
  260.         setXscale(min, max);
  261.         setYscale(min, max);
  262.     }
  263.  
  264.     // helper functions that scale from user coordinates to screen coordinates and back
  265.     private static double  scaleX(double x) { return width  * (x - xmin) / (xmax - xmin); }
  266.     private static double  scaleY(double y) { return height * (ymax - y) / (ymax - ymin); }
  267.     private static double factorX(double w) { return w * width  / Math.abs(xmax - xmin);  }
  268.     private static double factorY(double h) { return h * height / Math.abs(ymax - ymin);  }
  269.     private static double   userX(double x) { return xmin + x * (xmax - xmin) / width;    }
  270.     private static double   userY(double y) { return ymax - y * (ymax - ymin) / height;   }
  271.  
  272.  
  273.     /**
  274.      * Clear the screen to the default color (white).
  275.      */
  276.     public static void clear() { clear(DEFAULT_CLEAR_COLOR); }
  277.     /**
  278.      * Clear the screen to the given color.
  279.      * @param color the Color to make the background
  280.      */
  281.     public static void clear(Color color) {
  282.         offscreen.setColor(color);
  283.         offscreen.fillRect(0, 0, width, height);
  284.         offscreen.setColor(penColor);
  285.         draw();
  286.     }
  287.  
  288.     /**
  289.      * Get the current pen radius.
  290.      */
  291.     public static double getPenRadius() { return penRadius; }
  292.  
  293.     /**
  294.      * Set the pen size to the default (.002).
  295.      */
  296.     public static void setPenRadius() { setPenRadius(DEFAULT_PEN_RADIUS); }
  297.     /**
  298.      * Set the radius of the pen to the given size.
  299.      * @param r the radius of the pen
  300.      * @throws RuntimeException if r is negative
  301.      */
  302.     public static void setPenRadius(double r) {
  303.         if (r < 0) throw new RuntimeException("pen radius must be positive");
  304.         penRadius = r * DEFAULT_SIZE;
  305.         BasicStroke stroke = new BasicStroke((float) penRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
  306.         // BasicStroke stroke = new BasicStroke((float) penRadius);
  307.         offscreen.setStroke(stroke);
  308.     }
  309.  
  310.     /**
  311.      * Get the current pen color.
  312.      */
  313.     public static Color getPenColor() { return penColor; }
  314.  
  315.     /**
  316.      * Set the pen color to the default color (black).
  317.      */
  318.     public static void setPenColor() { setPenColor(DEFAULT_PEN_COLOR); }
  319.     /**
  320.      * Set the pen color to the given color. The available pen colors are
  321.      * BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA,
  322.      * ORANGE, PINK, RED, WHITE, and YELLOW.
  323.      * @param color the Color to make the pen
  324.      */
  325.     public static void setPenColor(Color color) {
  326.         penColor = color;
  327.         offscreen.setColor(penColor);
  328.     }
  329.  
  330.     /**
  331.      * Get the current font.
  332.      */
  333.     public static Font getFont() { return font; }
  334.  
  335.     /**
  336.      * Set the font to the default font (sans serif, 16 point).
  337.      */
  338.     public static void setFont() { setFont(DEFAULT_FONT); }
  339.  
  340.     /**
  341.      * Set the font to the given value.
  342.      * @param f the font to make text
  343.      */
  344.     public static void setFont(Font f) { font = f; }
  345.  
  346.  
  347.    /*************************************************************************
  348.     *  Drawing geometric shapes.
  349.     *************************************************************************/
  350.  
  351.     /**
  352.      * Draw a line from (x0, y0) to (x1, y1).
  353.      * @param x0 the x-coordinate of the starting point
  354.      * @param y0 the y-coordinate of the starting point
  355.      * @param x1 the x-coordinate of the destination point
  356.      * @param y1 the y-coordinate of the destination point
  357.      */
  358.     public static void line(double x0, double y0, double x1, double y1) {
  359.         offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1)));
  360.         draw();
  361.     }
  362.  
  363.     /**
  364.      * Draw one pixel at (x, y).
  365.      * @param x the x-coordinate of the pixel
  366.      * @param y the y-coordinate of the pixel
  367.      */
  368.     private static void pixel(double x, double y) {
  369.         offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1);
  370.     }
  371.  
  372.     /**
  373.      * Draw a point at (x, y).
  374.      * @param x the x-coordinate of the point
  375.      * @param y the y-coordinate of the point
  376.      */
  377.     public static void point(double x, double y) {
  378.         double xs = scaleX(x);
  379.         double ys = scaleY(y);
  380.         double r = penRadius;
  381.         // double ws = factorX(2*r);
  382.         // double hs = factorY(2*r);
  383.         // if (ws <= 1 && hs <= 1) pixel(x, y);
  384.         if (r <= 1) pixel(x, y);
  385.         else offscreen.fill(new Ellipse2D.Double(xs - r/2, ys - r/2, r, r));
  386.         draw();
  387.     }
  388.  
  389.     /**
  390.      * Draw a circle of radius r, centered on (x, y).
  391.      * @param x the x-coordinate of the center of the circle
  392.      * @param y the y-coordinate of the center of the circle
  393.      * @param r the radius of the circle
  394.      * @throws RuntimeException if the radius of the circle is negative
  395.      */
  396.     public static void circle(double x, double y, double r) {
  397.         if (r < 0) throw new RuntimeException("circle radius can't be negative");
  398.         double xs = scaleX(x);
  399.         double ys = scaleY(y);
  400.         double ws = factorX(2*r);
  401.         double hs = factorY(2*r);
  402.         if (ws <= 1 && hs <= 1) pixel(x, y);
  403.         else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  404.         draw();
  405.     }
  406.  
  407.     /**
  408.      * Draw filled circle of radius r, centered on (x, y).
  409.      * @param x the x-coordinate of the center of the circle
  410.      * @param y the y-coordinate of the center of the circle
  411.      * @param r the radius of the circle
  412.      * @throws RuntimeException if the radius of the circle is negative
  413.      */
  414.     public static void filledCircle(double x, double y, double r) {
  415.         if (r < 0) throw new RuntimeException("circle radius can't be negative");
  416.         double xs = scaleX(x);
  417.         double ys = scaleY(y);
  418.         double ws = factorX(2*r);
  419.         double hs = factorY(2*r);
  420.         if (ws <= 1 && hs <= 1) pixel(x, y);
  421.         else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  422.         draw();
  423.     }
  424.  
  425.  
  426.     /**
  427.      * Draw an ellipse with given semimajor and semiminor axes, centered on (x, y).
  428.      * @param x the x-coordinate of the center of the ellipse
  429.      * @param y the y-coordinate of the center of the ellipse
  430.      * @param semiMajorAxis is the semimajor axis of the ellipse
  431.      * @param semiMinorAxis is the semiminor axis of the ellipse
  432.      * @throws RuntimeException if either of the axes are negative
  433.      */
  434.     public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
  435.         if (semiMajorAxis < 0) throw new RuntimeException("ellipse semimajor axis can't be negative");
  436.         if (semiMinorAxis < 0) throw new RuntimeException("ellipse semiminor axis can't be negative");
  437.         double xs = scaleX(x);
  438.         double ys = scaleY(y);
  439.         double ws = factorX(2*semiMajorAxis);
  440.         double hs = factorY(2*semiMinorAxis);
  441.         if (ws <= 1 && hs <= 1) pixel(x, y);
  442.         else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  443.         draw();
  444.     }
  445.  
  446.     /**
  447.      * Draw an ellipse with given semimajor and semiminor axes, centered on (x, y).
  448.      * @param x the x-coordinate of the center of the ellipse
  449.      * @param y the y-coordinate of the center of the ellipse
  450.      * @param semiMajorAxis is the semimajor axis of the ellipse
  451.      * @param semiMinorAxis is the semiminor axis of the ellipse
  452.      * @throws RuntimeException if either of the axes are negative
  453.      */
  454.     public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) {
  455.         if (semiMajorAxis < 0) throw new RuntimeException("ellipse semimajor axis can't be negative");
  456.         if (semiMinorAxis < 0) throw new RuntimeException("ellipse semiminor axis can't be negative");
  457.         double xs = scaleX(x);
  458.         double ys = scaleY(y);
  459.         double ws = factorX(2*semiMajorAxis);
  460.         double hs = factorY(2*semiMinorAxis);
  461.         if (ws <= 1 && hs <= 1) pixel(x, y);
  462.         else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  463.         draw();
  464.     }
  465.  
  466.  
  467.     /**
  468.      * Draw an arc of radius r, centered on (x, y), from angle1 to angle2 (in degrees).
  469.      * @param x the x-coordinate of the center of the circle
  470.      * @param y the y-coordinate of the center of the circle
  471.      * @param r the radius of the circle
  472.      * @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock.
  473.      * @param angle2 the angle at the end of the arc. For example, if
  474.      *        you want a 90 degree arc, then angle2 should be angle1 + 90.
  475.      * @throws RuntimeException if the radius of the circle is negative
  476.      */
  477.     public static void arc(double x, double y, double r, double angle1, double angle2) {
  478.         if (r < 0) throw new RuntimeException("arc radius can't be negative");
  479.         while (angle2 < angle1) angle2 += 360;
  480.         double xs = scaleX(x);
  481.         double ys = scaleY(y);
  482.         double ws = factorX(2*r);
  483.         double hs = factorY(2*r);
  484.         if (ws <= 1 && hs <= 1) pixel(x, y);
  485.         else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN));
  486.         draw();
  487.     }
  488.  
  489.     /**
  490.      * Draw a square of side length 2r, centered on (x, y).
  491.      * @param x the x-coordinate of the center of the square
  492.      * @param y the y-coordinate of the center of the square
  493.      * @param r radius is half the length of any side of the square
  494.      * @throws RuntimeException if r is negative
  495.      */
  496.     public static void square(double x, double y, double r) {
  497.         if (r < 0) throw new RuntimeException("square side length can't be negative");
  498.         double xs = scaleX(x);
  499.         double ys = scaleY(y);
  500.         double ws = factorX(2*r);
  501.         double hs = factorY(2*r);
  502.         if (ws <= 1 && hs <= 1) pixel(x, y);
  503.         else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  504.         draw();
  505.     }
  506.  
  507.     /**
  508.      * Draw a filled square of side length 2r, centered on (x, y).
  509.      * @param x the x-coordinate of the center of the square
  510.      * @param y the y-coordinate of the center of the square
  511.      * @param r radius is half the length of any side of the square
  512.      * @throws RuntimeException if r is negative
  513.      */
  514.     public static void filledSquare(double x, double y, double r) {
  515.         if (r < 0) throw new RuntimeException("square side length can't be negative");
  516.         double xs = scaleX(x);
  517.         double ys = scaleY(y);
  518.         double ws = factorX(2*r);
  519.         double hs = factorY(2*r);
  520.         if (ws <= 1 && hs <= 1) pixel(x, y);
  521.         else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  522.         draw();
  523.     }
  524.  
  525.  
  526.     /**
  527.      * Draw a rectangle of given half width and half height, centered on (x, y).
  528.      * @param x the x-coordinate of the center of the rectangle
  529.      * @param y the y-coordinate of the center of the rectangle
  530.      * @param halfWidth is half the width of the rectangle
  531.      * @param halfHeight is half the height of the rectangle
  532.      * @throws RuntimeException if halfWidth or halfHeight is negative
  533.      */
  534.     public static void rectangle(double x, double y, double halfWidth, double halfHeight) {
  535.         if (halfWidth  < 0) throw new RuntimeException("half width can't be negative");
  536.         if (halfHeight < 0) throw new RuntimeException("half height can't be negative");
  537.         double xs = scaleX(x);
  538.         double ys = scaleY(y);
  539.         double ws = factorX(2*halfWidth);
  540.         double hs = factorY(2*halfHeight);
  541.         if (ws <= 1 && hs <= 1) pixel(x, y);
  542.         else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  543.         draw();
  544.     }
  545.  
  546.     /**
  547.      * Draw a filled rectangle of given half width and half height, centered on (x, y).
  548.      * @param x the x-coordinate of the center of the rectangle
  549.      * @param y the y-coordinate of the center of the rectangle
  550.      * @param halfWidth is half the width of the rectangle
  551.      * @param halfHeight is half the height of the rectangle
  552.      * @throws RuntimeException if halfWidth or halfHeight is negative
  553.      */
  554.     public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) {
  555.         if (halfWidth  < 0) throw new RuntimeException("half width can't be negative");
  556.         if (halfHeight < 0) throw new RuntimeException("half height can't be negative");
  557.         double xs = scaleX(x);
  558.         double ys = scaleY(y);
  559.         double ws = factorX(2*halfWidth);
  560.         double hs = factorY(2*halfHeight);
  561.         if (ws <= 1 && hs <= 1) pixel(x, y);
  562.         else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs));
  563.         draw();
  564.     }
  565.  
  566.  
  567.     /**
  568.      * Draw a polygon with the given (x[i], y[i]) coordinates.
  569.      * @param x an array of all the x-coordindates of the polygon
  570.      * @param y an array of all the y-coordindates of the polygon
  571.      */
  572.     public static void polygon(double[] x, double[] y) {
  573.         int N = x.length;
  574.         GeneralPath path = new GeneralPath();
  575.         path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
  576.         for (int i = 0; i < N; i++)
  577.             path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
  578.         path.closePath();
  579.         offscreen.draw(path);
  580.         draw();
  581.     }
  582.  
  583.     /**
  584.      * Draw a filled polygon with the given (x[i], y[i]) coordinates.
  585.      * @param x an array of all the x-coordindates of the polygon
  586.      * @param y an array of all the y-coordindates of the polygon
  587.      */
  588.     public static void filledPolygon(double[] x, double[] y) {
  589.         int N = x.length;
  590.         GeneralPath path = new GeneralPath();
  591.         path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0]));
  592.         for (int i = 0; i < N; i++)
  593.             path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i]));
  594.         path.closePath();
  595.         offscreen.fill(path);
  596.         draw();
  597.     }
  598.  
  599.  
  600.  
  601.    /*************************************************************************
  602.     *  Drawing images.
  603.     *************************************************************************/
  604.  
  605.     // get an image from the given filename
  606.     private static Image getImage(String filename) {
  607.  
  608.         // to read from file
  609.         ImageIcon icon = new ImageIcon(filename);
  610.  
  611.         // try to read from URL
  612.         if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
  613.             try {
  614.                 URL url = new URL(filename);
  615.                 icon = new ImageIcon(url);
  616.             } catch (Exception e) { /* not a url */ }
  617.         }
  618.  
  619.         // in case file is inside a .jar
  620.         if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) {
  621.             URL url = StdDraw.class.getResource(filename);
  622.             if (url == null) throw new RuntimeException("image " + filename + " not found");
  623.             icon = new ImageIcon(url);
  624.         }
  625.  
  626.         return icon.getImage();
  627.     }
  628.  
  629.     /**
  630.      * Draw picture (gif, jpg, or png) centered on (x, y).
  631.      * @param x the center x-coordinate of the image
  632.      * @param y the center y-coordinate of the image
  633.      * @param s the name of the image/picture, e.g., "ball.gif"
  634.      * @throws RuntimeException if the image is corrupt
  635.      */
  636.     public static void picture(double x, double y, String s) {
  637.         Image image = getImage(s);
  638.         double xs = scaleX(x);
  639.         double ys = scaleY(y);
  640.         int ws = image.getWidth(null);
  641.         int hs = image.getHeight(null);
  642.         if (ws < 0 || hs < 0) throw new RuntimeException("image " + s + " is corrupt");
  643.  
  644.         offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
  645.         draw();
  646.     }
  647.  
  648.     /**
  649.      * Draw picture (gif, jpg, or png) centered on (x, y),
  650.      * rotated given number of degrees
  651.      * @param x the center x-coordinate of the image
  652.      * @param y the center y-coordinate of the image
  653.      * @param s the name of the image/picture, e.g., "ball.gif"
  654.      * @param degrees is the number of degrees to rotate counterclockwise
  655.      * @throws RuntimeException if the image is corrupt
  656.      */
  657.     public static void picture(double x, double y, String s, double degrees) {
  658.         Image image = getImage(s);
  659.         double xs = scaleX(x);
  660.         double ys = scaleY(y);
  661.         int ws = image.getWidth(null);
  662.         int hs = image.getHeight(null);
  663.         if (ws < 0 || hs < 0) throw new RuntimeException("image " + s + " is corrupt");
  664.  
  665.         offscreen.rotate(Math.toRadians(-degrees), xs, ys);
  666.         offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null);
  667.         offscreen.rotate(Math.toRadians(+degrees), xs, ys);
  668.  
  669.         draw();
  670.     }
  671.  
  672.     /**
  673.      * Draw picture (gif, jpg, or png) centered on (x, y), rescaled to w-by-h.
  674.      * @param x the center x coordinate of the image
  675.      * @param y the center y coordinate of the image
  676.      * @param s the name of the image/picture, e.g., "ball.gif"
  677.      * @param w the width of the image
  678.      * @param h the height of the image
  679.      * @throws RuntimeException if the width height are negative
  680.      * @throws RuntimeException if the image is corrupt
  681.      */
  682.     public static void picture(double x, double y, String s, double w, double h) {
  683.         Image image = getImage(s);
  684.         double xs = scaleX(x);
  685.         double ys = scaleY(y);
  686.         if (w < 0) throw new RuntimeException("width is negative: " + w);
  687.         if (h < 0) throw new RuntimeException("height is negative: " + h);
  688.         double ws = factorX(w);
  689.         double hs = factorY(h);
  690.         if (ws < 0 || hs < 0) throw new RuntimeException("image " + s + " is corrupt");
  691.         if (ws <= 1 && hs <= 1) pixel(x, y);
  692.         else {
  693.             offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
  694.                                        (int) Math.round(ys - hs/2.0),
  695.                                        (int) Math.round(ws),
  696.                                        (int) Math.round(hs), null);
  697.         }
  698.         draw();
  699.     }
  700.  
  701.  
  702.     /**
  703.      * Draw picture (gif, jpg, or png) centered on (x, y), rotated
  704.      * given number of degrees, rescaled to w-by-h.
  705.      * @param x the center x-coordinate of the image
  706.      * @param y the center y-coordinate of the image
  707.      * @param s the name of the image/picture, e.g., "ball.gif"
  708.      * @param w the width of the image
  709.      * @param h the height of the image
  710.      * @param degrees is the number of degrees to rotate counterclockwise
  711.      * @throws RuntimeException if the image is corrupt
  712.      */
  713.     public static void picture(double x, double y, String s, double w, double h, double degrees) {
  714.         Image image = getImage(s);
  715.         double xs = scaleX(x);
  716.         double ys = scaleY(y);
  717.         double ws = factorX(w);
  718.         double hs = factorY(h);
  719.         if (ws < 0 || hs < 0) throw new RuntimeException("image " + s + " is corrupt");
  720.         if (ws <= 1 && hs <= 1) pixel(x, y);
  721.  
  722.         offscreen.rotate(Math.toRadians(-degrees), xs, ys);
  723.         offscreen.drawImage(image, (int) Math.round(xs - ws/2.0),
  724.                                    (int) Math.round(ys - hs/2.0),
  725.                                    (int) Math.round(ws),
  726.                                    (int) Math.round(hs), null);
  727.         offscreen.rotate(Math.toRadians(+degrees), xs, ys);
  728.  
  729.         draw();
  730.     }
  731.  
  732.  
  733.    /*************************************************************************
  734.     *  Drawing text.
  735.     *************************************************************************/
  736.  
  737.     /**
  738.      * Write the given text string in the current font, centered on (x, y).
  739.      * @param x the center x-coordinate of the text
  740.      * @param y the center y-coordinate of the text
  741.      * @param s the text
  742.      */
  743.     public static void text(double x, double y, String s) {
  744.         offscreen.setFont(font);
  745.         FontMetrics metrics = offscreen.getFontMetrics();
  746.         double xs = scaleX(x);
  747.         double ys = scaleY(y);
  748.         int ws = metrics.stringWidth(s);
  749.         int hs = metrics.getDescent();
  750.         offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs));
  751.         draw();
  752.     }
  753.  
  754.     /**
  755.      * Write the given text string in the current font, centered on (x, y) and
  756.      * rotated by the specified number of degrees  
  757.      * @param x the center x-coordinate of the text
  758.      * @param y the center y-coordinate of the text
  759.      * @param s the text
  760.      * @param degrees is the number of degrees to rotate counterclockwise
  761.      */
  762.     public static void text(double x, double y, String s, double degrees) {
  763.         double xs = scaleX(x);
  764.         double ys = scaleY(y);
  765.         offscreen.rotate(Math.toRadians(-degrees), xs, ys);
  766.         text(x, y, s);
  767.         offscreen.rotate(Math.toRadians(+degrees), xs, ys);
  768.     }
  769.  
  770.  
  771.     /**
  772.      * Write the given text string in the current font, left-aligned at (x, y).
  773.      * @param x the x-coordinate of the text
  774.      * @param y the y-coordinate of the text
  775.      * @param s the text
  776.      */
  777.     public static void textLeft(double x, double y, String s) {
  778.         offscreen.setFont(font);
  779.         FontMetrics metrics = offscreen.getFontMetrics();
  780.         double xs = scaleX(x);
  781.         double ys = scaleY(y);
  782.         int hs = metrics.getDescent();
  783.         offscreen.drawString(s, (float) (xs), (float) (ys + hs));
  784.         draw();
  785.     }
  786.  
  787.     /**
  788.      * Write the given text string in the current font, right-aligned at (x, y).
  789.      * @param x the x-coordinate of the text
  790.      * @param y the y-coordinate of the text
  791.      * @param s the text
  792.      */
  793.     public static void textRight(double x, double y, String s) {
  794.         offscreen.setFont(font);
  795.         FontMetrics metrics = offscreen.getFontMetrics();
  796.         double xs = scaleX(x);
  797.         double ys = scaleY(y);
  798.         int ws = metrics.stringWidth(s);
  799.         int hs = metrics.getDescent();
  800.         offscreen.drawString(s, (float) (xs - ws), (float) (ys + hs));
  801.         draw();
  802.     }
  803.  
  804.  
  805.  
  806.     /**
  807.      * Display on screen, pause for t milliseconds, and turn on
  808.      * <em>animation mode</em>: subsequent calls to
  809.      * drawing methods such as <tt>line()</tt>, <tt>circle()</tt>, and <tt>square()</tt>
  810.      * will not be displayed on screen until the next call to <tt>show()</tt>.
  811.      * This is useful for producing animations (clear the screen, draw a bunch of shapes,
  812.      * display on screen for a fixed amount of time, and repeat). It also speeds up
  813.      * drawing a huge number of shapes (call <tt>show(0)</tt> to defer drawing
  814.      * on screen, draw the shapes, and call <tt>show(0)</tt> to display them all
  815.      * on screen at once).
  816.      * @param t number of milliseconds
  817.      */
  818.     public static void show(int t) {
  819.         defer = false;
  820.         draw();
  821.         try { Thread.currentThread().sleep(t); }
  822.         catch (InterruptedException e) { System.out.println("Error sleeping"); }
  823.         defer = true;
  824.     }
  825.  
  826.     /**
  827.      * Display on-screen and turn off animation mode:
  828.      * subsequent calls to
  829.      * drawing methods such as <tt>line()</tt>, <tt>circle()</tt>, and <tt>square()</tt>
  830.      * will be displayed on screen when called. This is the default.
  831.      */
  832.     public static void show() {
  833.         defer = false;
  834.         draw();
  835.     }
  836.  
  837.     // draw onscreen if defer is false
  838.     private static void draw() {
  839.         if (defer) return;
  840.         onscreen.drawImage(offscreenImage, 0, 0, null);
  841.         frame.repaint();
  842.     }
  843.  
  844.  
  845.    /*************************************************************************
  846.     *  Save drawing to a file.
  847.     *************************************************************************/
  848.  
  849.     /**
  850.      * Save onscreen image to file - suffix must be png, jpg, or gif.
  851.      * @param filename the name of the file with one of the required suffixes
  852.      */
  853.     public static void save(String filename) {
  854.         File file = new File(filename);
  855.         String suffix = filename.substring(filename.lastIndexOf('.') + 1);
  856.  
  857.         // png files
  858.         if (suffix.toLowerCase().equals("png")) {
  859.             try { ImageIO.write(onscreenImage, suffix, file); }
  860.             catch (IOException e) { e.printStackTrace(); }
  861.         }
  862.  
  863.         // need to change from ARGB to RGB for jpeg
  864.         // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727
  865.         else if (suffix.toLowerCase().equals("jpg")) {
  866.             WritableRaster raster = onscreenImage.getRaster();
  867.             WritableRaster newRaster;
  868.             newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2});
  869.             DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel();
  870.             DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(),
  871.                                                           cm.getRedMask(),
  872.                                                           cm.getGreenMask(),
  873.                                                           cm.getBlueMask());
  874.             BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false,  null);
  875.             try { ImageIO.write(rgbBuffer, suffix, file); }
  876.             catch (IOException e) { e.printStackTrace(); }
  877.         }
  878.  
  879.         else {
  880.             System.out.println("Invalid image file type: " + suffix);
  881.         }
  882.     }
  883.  
  884.  
  885.     /**
  886.      * This method cannot be called directly.
  887.      */
  888.     public void actionPerformed(ActionEvent e) {
  889.         FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE);
  890.         chooser.setVisible(true);
  891.         String filename = chooser.getFile();
  892.         if (filename != null) {
  893.             StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile());
  894.         }
  895.     }
  896.  
  897.  
  898.    /*************************************************************************
  899.     *  Mouse interactions.
  900.     *************************************************************************/
  901.  
  902.     /**
  903.      * Is the mouse being pressed?
  904.      * @return true or false
  905.      */
  906.     public static boolean mousePressed() {
  907.         synchronized (mouseLock) {
  908.             return mousePressed;
  909.         }
  910.     }
  911.  
  912.     /**
  913.      * What is the x-coordinate of the mouse?
  914.      * @return the value of the x-coordinate of the mouse
  915.      */
  916.     public static double mouseX() {
  917.         synchronized (mouseLock) {
  918.             return mouseX;
  919.         }
  920.     }
  921.  
  922.     /**
  923.      * What is the y-coordinate of the mouse?
  924.      * @return the value of the y-coordinate of the mouse
  925.      */
  926.     public static double mouseY() {
  927.         synchronized (mouseLock) {
  928.             return mouseY;
  929.         }
  930.     }
  931.  
  932.  
  933.     /**
  934.      * This method cannot be called directly.
  935.      */
  936.     public void mouseClicked(MouseEvent e) { }
  937.  
  938.     /**
  939.      * This method cannot be called directly.
  940.      */
  941.     public void mouseEntered(MouseEvent e) { }
  942.  
  943.     /**
  944.      * This method cannot be called directly.
  945.      */
  946.     public void mouseExited(MouseEvent e) { }
  947.  
  948.     /**
  949.      * This method cannot be called directly.
  950.      */
  951.     public void mousePressed(MouseEvent e) {
  952.         synchronized (mouseLock) {
  953.             mouseX = StdDraw.userX(e.getX());
  954.             mouseY = StdDraw.userY(e.getY());
  955.             mousePressed = true;
  956.         }
  957.     }
  958.  
  959.     /**
  960.      * This method cannot be called directly.
  961.      */
  962.     public void mouseReleased(MouseEvent e) {
  963.         synchronized (mouseLock) {
  964.             mousePressed = false;
  965.         }
  966.     }
  967.  
  968.     /**
  969.      * This method cannot be called directly.
  970.      */
  971.     public void mouseDragged(MouseEvent e)  {
  972.         synchronized (mouseLock) {
  973.             mouseX = StdDraw.userX(e.getX());
  974.             mouseY = StdDraw.userY(e.getY());
  975.         }
  976.     }
  977.  
  978.     /**
  979.      * This method cannot be called directly.
  980.      */
  981.     public void mouseMoved(MouseEvent e) {
  982.         synchronized (mouseLock) {
  983.             mouseX = StdDraw.userX(e.getX());
  984.             mouseY = StdDraw.userY(e.getY());
  985.         }
  986.     }
  987.  
  988.  
  989.    /*************************************************************************
  990.     *  Keyboard interactions.
  991.     *************************************************************************/
  992.  
  993.     /**
  994.      * Has the user typed a key?
  995.      * @return true if the user has typed a key, false otherwise
  996.      */
  997.     public static boolean hasNextKeyTyped() {
  998.         synchronized (keyLock) {
  999.             return !keysTyped.isEmpty();
  1000.         }
  1001.     }
  1002.  
  1003.     /**
  1004.      * What is the next key that was typed by the user? This method returns
  1005.      * a Unicode character corresponding to the key typed (such as 'a' or 'A').
  1006.      * It cannot identify action keys (such as F1
  1007.      * and arrow keys) or modifier keys (such as control).
  1008.      * @return the next Unicode key typed
  1009.      */
  1010.     public static char nextKeyTyped() {
  1011.         synchronized (keyLock) {
  1012.             return keysTyped.removeLast();
  1013.         }
  1014.     }
  1015.  
  1016.     /**
  1017.      * Is the keycode currently being pressed? This method takes as an argument
  1018.      * the keycode (corresponding to a physical key). It can handle action keys
  1019.      * (such as F1 and arrow keys) and modifier keys (such as shift and control).
  1020.      * See <a href = "http://download.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html">KeyEvent.java</a>
  1021.      * for a description of key codes.
  1022.      * @return true if keycode is currently being pressed, false otherwise
  1023.      */
  1024.     public static boolean isKeyPressed(int keycode) {
  1025.         return keysDown.contains(keycode);
  1026.     }
  1027.  
  1028.  
  1029.     /**
  1030.      * This method cannot be called directly.
  1031.      */
  1032.     public void keyTyped(KeyEvent e) {
  1033.         synchronized (keyLock) {
  1034.             keysTyped.addFirst(e.getKeyChar());
  1035.         }
  1036.     }
  1037.  
  1038.     /**
  1039.      * This method cannot be called directly.
  1040.      */
  1041.     public void keyPressed(KeyEvent e) {
  1042.         keysDown.add(e.getKeyCode());
  1043.     }
  1044.  
  1045.     /**
  1046.      * This method cannot be called directly.
  1047.      */
  1048.     public void keyReleased(KeyEvent e) {
  1049.         keysDown.remove(e.getKeyCode());
  1050.     }
  1051.  
  1052.  
  1053.  
  1054.  
  1055.     /**
  1056.      * Test client.
  1057.      */
  1058.     public static void main(String[] args) {
  1059.         StdDraw.square(.2, .8, .1);
  1060.         StdDraw.filledSquare(.8, .8, .2);
  1061.         StdDraw.circle(.8, .2, .2);
  1062.  
  1063.         StdDraw.setPenColor(StdDraw.BOOK_RED);
  1064.         StdDraw.setPenRadius(.02);
  1065.         StdDraw.arc(.8, .2, .1, 200, 45);
  1066.  
  1067.         // draw a blue diamond
  1068.         StdDraw.setPenRadius();
  1069.         StdDraw.setPenColor(StdDraw.BOOK_BLUE);
  1070.         double[] x = { .1, .2, .3, .2 };
  1071.         double[] y = { .2, .3, .2, .1 };
  1072.         StdDraw.filledPolygon(x, y);
  1073.  
  1074.         // text
  1075.         StdDraw.setPenColor(StdDraw.BLACK);
  1076.         StdDraw.text(0.2, 0.5, "black text");
  1077.         StdDraw.setPenColor(StdDraw.WHITE);
  1078.         StdDraw.text(0.8, 0.8, "white text");
  1079.     }
  1080.  
  1081. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement