自定义基本java类-StdDraw.java

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