# Java: Example - Buffered Analog Clock

The following is an analog (uses hands) clock that uses buffered drawing to increase speed. In buffered drawing some or all of an images is computed once and stored so that it doesn't have to be redrawn later. In this program the clock face is drawn only once, and the hands are computed.

    // An analog clock -- Using Timer and Calendar.
    // Uses a BufferedImage and antialiasing.
    // The second hand creep is from the Timer, which seems to take
    // a little longer than the requested time to call the listener.
    //  -- Fred Swartz, 1 May 1999, 2002-02-21 from applet to application

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import java.util.*;

    ///////////////////////////////////////////////////////////// ClockAnalogBuf
    public class ClockAnalogBuf extends JFrame {

        Clock clockFace;

        //================================================================= main
        public static void main(String[] args) {
            JFrame windo = new ClockAnalogBuf();
            windo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            windo.setVisible(true);
        }//end main

        //========================================================== constructor
        public ClockAnalogBuf() {
            Container content = this.getContentPane();
            content.setLayout(new BorderLayout());
            clockFace = new Clock();
            content.add(clockFace, BorderLayout.CENTER);

            this.setTitle("Analog Clock");
            this.pack();

            clockFace.start();
        }//end constructor
    }//end class ClockAnalogBuf

    //////////////////////////////////////////////////////////////// Clock class
    class Clock extends JPanel {

        private int hours   = 0;
        private int minutes = 0;
        private int seconds = 0;
        private int millis  = 0;

        private static final int   spacing = 10;
        private static final float twoPi = (float)(2.0 * Math.PI);
        private static final float threePi = (float)(3.0 * Math.PI);
        // Angles for the trigonometric functions are measured in radians.
        // The following in the number of radians per sec or min.
        private static final float radPerSecMin = (float)(Math.PI / 30.0);

        private int size;       // height and width of clock face
        private int centerX;    // x coord of middle of clock
        private int centerY;    // y coord of middle of clock
        private BufferedImage clockImage;
        private javax.swing.Timer t;

        //==================================================== Clock constructor
        public Clock() {
            this.setPreferredSize(new Dimension(300,300));
            this.setBackground(Color.white);
            this.setForeground(Color.black);

            t = new javax.swing.Timer(1000,
                  new ActionListener() {
                      public void actionPerformed(ActionEvent e) {
                          update();
                      }
                  });
        }//end constructor

        //=============================================================== update
            // Replace the default update so that the plain background
            // doesn't get drawn.
        public void update() {
            // Hmmm, is this the right way to do this? Or do we just
            // call paintComponent?  Does this coalesce calls, not that
            // this updates often enuf for that to be important.
            this.repaint();
        }//end update

        //================================================================ start
        public void start() {
            t.start();  // start the timer
        }//end start

        //================================================================= stop
        public void stop() {
            t.stop();  // start the timer
        }//end stop

        //======================================================= paintComponent

        public void paintComponent(Graphics g) {
            super.paintComponent(g);  // paint background, borders
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);

            // The panel may have been resized, get current dimensions
            int w = getWidth();
            int h = getHeight();
            size = ((w<h) ? w : h) - 2*spacing;
            centerX = size/2 + spacing;
            centerY = size/2 + spacing;

            // Create the clock face background image if this is the first time,
            // or if the size of the panel has changed
            if (clockImage == null
                    || clockImage.getWidth() != w
                    || clockImage.getHeight() != h) {

                clockImage = (BufferedImage)(this.createImage(w, h));
                // now get a graphics context from this image
                Graphics2D gc = clockImage.createGraphics();
                gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                    RenderingHints.VALUE_ANTIALIAS_ON);
                drawClockFace(gc);
            }

            // Now get the time and draw the hands.
            Calendar now = Calendar.getInstance();
            hours   = now.get(Calendar.HOUR);
            minutes = now.get(Calendar.MINUTE);
            seconds = now.get(Calendar.SECOND);
            millis  = now.get(Calendar.MILLISECOND);

            // Draw the clock face from the precomputed image
            g2.drawImage(clockImage, null, 0, 0);

            // Draw the clock hands
            drawClockHands(g);
        }//end paintComponent

        //======================================================= drawClockHands
        private void drawClockHands(Graphics g) {
            int secondRadius = size/2;
            int minuteRadius = secondRadius * 3/4;
            int hourRadius   = secondRadius/2;

            // second hand
            float fseconds = seconds + (float)millis/1000;
            float secondAngle = threePi - (radPerSecMin * fseconds);
            drawRadius(g, centerX, centerY, secondAngle, 0, secondRadius);

            // minute hand
            float fminutes = (float)(minutes + fseconds/60.0);
            float minuteAngle = threePi - (radPerSecMin * fminutes);
            drawRadius(g, centerX, centerY, minuteAngle, 0, minuteRadius);

            // hour hand
            float fhours = (float)(hours + fminutes/60.0);
            float hourAngle = threePi - (5 * radPerSecMin * fhours);
            drawRadius(g, centerX, centerY, hourAngle, 0, hourRadius);
        }//end drawClockHands

        //======================================================== drawClockFace
        private void drawClockFace(Graphics g) {
            // clock face
            g.setColor(Color.pink);
            g.fillOval(spacing, spacing, size, size);
            g.setColor(Color.black);
            g.drawOval(spacing, spacing, size, size);

            // tic marks
            for (int sec = 0; sec<60; sec++) {
                int ticStart;
                if (sec%5 == 0) {
                    ticStart = size/2-10;
                } else {
                    ticStart = size/2-5;
                }
                drawRadius(g, centerX, centerY, radPerSecMin*sec, ticStart , size/2);
            }
        }//endmethod drawClockFace

        //=========================================================== drawRadius
        private void drawRadius(Graphics g, int x, int y, double angle,
                        int minRadius, int maxRadius) {
            float sine   = (float)Math.sin(angle);
            float cosine = (float)Math.cos(angle);

            int dxmin = (int)(minRadius * sine);
            int dymin = (int)(minRadius * cosine);

            int dxmax = (int)(maxRadius * sine);
            int dymax = (int)(maxRadius * cosine);
            g.drawLine(x+dxmin, y+dymin, x+dxmax, y+dymax);
        }//end drawRadius

    }//endclass Clock