import java.awt.*; // import statements to make necessary classes available import java.awt.event.*; import java.awt.geom.*; import javax.swing.*; /** * This class shows the setup for drawing animated images using Java Graphics2D. * The drawing code goes in the paintComponent() method. When the program * is run, the drawing is shown in a window on the screen. The paintComponent() * method will be called about 60 times per second, and the value of the global * variables frameNumber and elapsedTimeMillis will increase. These variables * can be used in paintComponent(), so that the image will change each time * it is drawn. */ public class AnimationStarter extends JPanel { /** * This main() routine makes it possible to run the class AnimationStarter * as an application. It simply creates a window that contains a panel * of type AnimationStarter. The program ends when the user closed the * window by clicking its close box. */ public static void main(String[] args) { JFrame window; window = new JFrame("Java Animation"); // The parameter shows in the window title bar. final AnimationStarter panel = new AnimationStarter(); // The drawing area. window.setContentPane( panel ); // Show the panel in the window. window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // End program when window closes. window.pack(); // Set window size based on the preferred sizes of its contents. window.setResizable(false); // Don't let user resize window. Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); window.setLocation( // Center window on screen. (screen.width - window.getWidth())/2, (screen.height - window.getHeight())/2 ); Timer animationTimer; // A Timer that will emit events to drive the animation. final long startTime = System.currentTimeMillis(); animationTimer = new Timer(16, new ActionListener() { public void actionPerformed(ActionEvent arg0) { panel.frameNumber++; panel.elapsedTimeMillis = System.currentTimeMillis() - startTime; panel.repaint(); } }); window.setVisible(true); // Open the window, making it visible on the screen. animationTimer.start(); // Start the animation running. } private int frameNumber; // A counter that increases by one in each frame. private long elapsedTimeMillis; // The time, in milliseconds, since the animation started. private float pixelSize; // This is the measure of a pixel in the coordinate system // set up by calling the applyLimits method. It can be used // for setting line widths, for example. /** * This constructor sets up an AnimationStarter when it is created. Here, it * sets the size of the drawing area. (The size is set as a "preferred size," * which will be used by the pack() command in the main() routine.) */ public AnimationStarter() { setPreferredSize( new Dimension(800,600) ); // Set size of drawing area, in pixels. } /** * The paintComponent method draws the content of the JPanel. The parameter * is a graphics context that can be used for drawing on the panel. Note that * it is declared to be of type Graphics but is actually of type Graphics2D, * which is a subclass of Graphics. */ protected void paintComponent(Graphics g) { /* First, create a Graphics2D drawing context for drawing on the panel. * (g.create() makes a copy of g, which will draw to the same place as g, * but changes to the returned copy will not affect the original.) */ Graphics2D g2 = (Graphics2D)g.create(); /* Turn on antialiasing in this graphics context, for better drawing. */ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); /* Fill in the entire drawing area with white. */ g2.setPaint(Color.WHITE); g2.fillRect(0,0,getWidth(),getHeight()); // From the old graphics API! /* Here, I set up a new coordinate system on the drawing area, by calling * the applyLimits() method that is defined below. Without this call, I * would be using regular pixel coordinates. This function sets the value * of the global variable pixelSize, which I need for stroke widths in the * transformed coordinate system. */ applyWindowToViewportTransformation(g2, -5, 5, -5, 5, true); /* Finish by drawing the content of the current frame, using frameNumber * and/or elapsedTimeMillis in the drawing so that the image will change * from frame to frame. Here, a simple hierarchical scene is drawn as * an example. */ Path2D bars ; // Will the outline of a hexagon, with spokes from center to vetices. bars = new Path2D.Double(); bars.moveTo(3,0); for (int i = 1; i < 6; i++) { double angle = (2*Math.PI/6) * i; bars.lineTo( 3*Math.cos(angle), 3*Math.sin(angle) ); } bars.closePath(); for (int i = 0; i < 6; i++) { double angle = (2*Math.PI/6) * i; bars.moveTo(0,0); bars.lineTo( 3*Math.cos(angle), 3*Math.sin(angle) ); } g2.rotate(frameNumber * 0.001); // makes the whole picture rotate slowly g2.setStroke( new BasicStroke(4*pixelSize) ); g2.setPaint( Color.BLUE ); g2.draw(bars); // Now, draw a quickly rotating square at each vertex of the hexaon. Rectangle2D square = new Rectangle2D.Double(-0.5,-0.5,1,1); // six copies of this square will be drawn g2.setStroke( new BasicStroke(2*pixelSize) ); for (int i = 0; i < 6; i++) { AffineTransform savedTransform = g2.getTransform(); // save the current transform double angle = (2*Math.PI/6) * i; // (NOTE: Remember that transforms are applied in the reverse of their order in the code!) g2.rotate(angle); // rotate the translated square onto the i-th vertex of the hexagon. g2.translate(3,0); // translate the quickly rotating square to (3,0) g2.rotate( frameNumber*0.02 ); // make the square rotate quickly about its center. g2.setPaint( new Color(255,0,0,100) ); // translucent red. g2.fill(square); // draw the 1-by-1 square, centered at (0,0). g2.setPaint( Color.RED ); g2.draw(square); g2.setTransform(savedTransform); // restore the saved transform } } /** * Applies a coordinate transform to a Graphics2D graphics context. The upper * left corner of the viewport where the graphics context draws is assumed to * be (0,0). The coordinate transform will make a requested view window visible * in the drawing area. The requested limits might be adjusted to preserve the * aspect ratio. * This method sets the value of the global variable pixelSize, which is defined as the * maximum of the width of a pixel and the height of a pixel as measured in the * coordinate system. (If the aspect ratio is preserved, then the width and * height will agree. * @param g2 The drawing context whose transform will be set. * @param left requested x-value at left of drawing area. * @param right requested x-value at right of drawing area. * @param bottom requested y-value at bottom of drawing area; can be less than * top, which will reverse the orientation of the y-axis to make the positive * direction point upwards. * @param top requested y-value at top of drawing area. * @param preserveAspect if preserveAspect is false, then the requested view window * rectangle will exactly fill the viewport; if it is true, then the limits will be * expanded in one direction, horizontally or vertically, if necessary, to make the * aspect ratio of the view window match the aspect ratio of the viewport. * Note that when preserveAspect is false, the units of measure in the horizontal * and vertical directions will be different. */ private void applyWindowToViewportTransformation(Graphics2D g2, double left, double right, double bottom, double top, boolean preserveAspect) { int width = getWidth(); // The width of this drawing area, in pixels. int height = getHeight(); // The height of this drawing area, in pixels. if (preserveAspect) { // Adjust the limits to match the aspect ratio of the drawing area. double displayAspect = Math.abs((double)height / width); double requestedAspect = Math.abs(( bottom-top ) / ( right-left )); if (displayAspect > requestedAspect) { // Expand the viewport vertically. double excess = (bottom-top) * (displayAspect/requestedAspect - 1); bottom += excess/2; top -= excess/2; } else if (displayAspect < requestedAspect) { // Expand the viewport vertically. double excess = (right-left) * (requestedAspect/displayAspect - 1); right += excess/2; left -= excess/2; } } g2.scale( width / (right-left), height / (bottom-top) ); g2.translate( -left, -top ); double pixelWidth = Math.abs(( right - left ) / width); double pixelHeight = Math.abs(( bottom - top ) / height); pixelSize = (float)Math.max(pixelWidth,pixelHeight); } }