## Monday, March 21, 2011

### How to display an analog clock ?

The previous article demonstrated a digital clock. An obvious follow-up question is how to display an analog clock.

To display an analog clock, the first thing is of course to draw a circle.

The class Graphics2D contains a method for drawing circular arc.

```java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;
gr.drawArc(0,0,200,200,0,360);
```

That means to draw a elliptical arc in the bounding rectangle (0,0,200,200).
Since the bounding rectangle is a square, it will actually draw a circular arc.
The arc starts from 0 degree and ends at 360 degree. That would draw a full circle.
The radius of the circle is 100 and the center is at (100,100)

The following is the first version of the 'analog clock'.

```/******************************************************************************
* File : AnalogClock.java (version 1)
* Author : http://java.macteki.com/
* Description :
*   Display an analog clock. (Draw a circle only)
* Tested with : JDK 1.6
******************************************************************************/

class AnalogClock extends javax.swing.JPanel
{
public static AnalogClock getInstance()
{
AnalogClock panel=new AnalogClock();
panel.setPreferredSize(new java.awt.Dimension(300,300));
return panel;
}

public void paintComponent(java.awt.Graphics graphics)
{
super.paintComponent(graphics);

java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

gr.drawArc(0,0,200,200,0,360);
}

public static void main(String[] args) throws Exception
{
// create frame
javax.swing.JFrame frame=new javax.swing.JFrame();
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

frame.setTitle("Macteki AnalogClock");

AnalogClock clockPanel=AnalogClock.getInstance();

frame.pack();  // set the window size big enough to hold its components
frame.setVisible(true);

}
}
```

The next thing to do is to draw a clock arm. Let's start with the 'seconds' arm since it is moving every second and its motion is obvious.

This arm rotates a circle every 60 seconds, hence it should move 6 degree per second.

We can easily define this arm using polar coordinates.

Two variables are defined :

```radius = length of arm
degree = angle between the arm and the positive x-axis
```

First read the 'seconds' value from the current time, then calculate the degree it should have moved, then transform the polar coordinates to screen coordinates.

The transformation is done by :
```x = radius*cos(angle);
```

Note that the java coordinates system is 'upside down', whereas the Cartesian coordinates has the positive y-axis pointing upwards. Hence a transformation need to be applied :
```y = -y;
```

The following sample is an analog clock with one moving arm.

```/******************************************************************************
* File : AnalogClock.java (version 2)
* Author : http://java.macteki.com/
* Description :
*   Display an analog clock with a moving arm (seconds)
* Tested with : JDK 1.6
******************************************************************************/

class AnalogClock extends javax.swing.JPanel
{
public static AnalogClock getInstance()
{
AnalogClock panel=new AnalogClock();
panel.setPreferredSize(new java.awt.Dimension(300,300));
return panel;
}

public void paintComponent(java.awt.Graphics graphics)
{
super.paintComponent(graphics);

java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

gr.drawArc(0,0,200,200,0,360);

int xCenter=100,yCenter=100;

java.util.Calendar calendar=java.util.Calendar.getInstance();
int second=calendar.get(java.util.Calendar.SECOND);

// polar coordinate of the 'second' arm
// This arm start at 90 degree and it should move 6 degree per second
int degree = 90-second*6;
double pi=3.141592654;

y=-y;  // positive y-axis should point upwards.

// translate the whole coordinate system.
x+=xCenter;
y+=yCenter;

gr.drawLine(xCenter,yCenter,x,y);
}

public static void main(String[] args) throws Exception
{
// create frame
javax.swing.JFrame frame=new javax.swing.JFrame();
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

frame.setTitle("Macteki AnalogClock");

AnalogClock clockPanel=AnalogClock.getInstance();

frame.pack();  // set the window size big enough for its components.
frame.setVisible(true);

while (true)
{
clockPanel.repaint();
}

}
}
```

Next we add a digital clock for comparison. The previous article has explained how to do this.

```/******************************************************************************
* File : AnalogClock.java (version 3)
* Author : http://java.macteki.com/
* Description :
*   Display an analog clock with a digital label
* Tested with : JDK 1.6
******************************************************************************/

import javax.swing.JLabel;

class AnalogClock extends javax.swing.JPanel
{
public static AnalogClock getInstance()
{
AnalogClock panel=new AnalogClock();
panel.setPreferredSize(new java.awt.Dimension(300,300));
return panel;
}

public void paintComponent(java.awt.Graphics graphics)
{
super.paintComponent(graphics);

java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

gr.drawArc(0,0,200,200,0,360);

int xCenter=100,yCenter=100;

java.util.Calendar calendar=java.util.Calendar.getInstance();
int second=calendar.get(java.util.Calendar.SECOND);

// polar coordinate of the 'second' arm
// This arm start at 90 degree and it should move 6 degree per second
int degree = 90-second*6;
double pi=3.141592654;

y=-y;  // positive y-axis should point upwards.

// translate the whole coordinate system.
x+=xCenter;
y+=yCenter;

gr.drawLine(xCenter,yCenter,x,y);
}

public static void main(String[] args) throws Exception
{
// create frame
javax.swing.JFrame frame=new javax.swing.JFrame();
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

frame.setTitle("Macteki AnalogClock");

AnalogClock clockPanel=AnalogClock.getInstance();

// create a image label
JLabel digitalLabel=new JLabel("", JLabel.CENTER);
digitalLabel.setBounds(0,250,300,30);
clockPanel.setLayout(null);

frame.pack();  // set the window size big enough for its components
frame.setVisible(true);

while (true)
{
java.util.Date date=new java.util.Date();
digitalLabel.setText(""+date);
clockPanel.repaint();
}

}
}
```

Finally we add the minute and the hour arm.

They are similar to the 'second' arm.

The minute arm rotates a full circle every 60 minutes, that is 3600 seconds.
That means it moves 360 degree in 3600 seconds.
That is 0.1 degree per second.

The hour arm rotates a full circle every 43200 seconds.
That is 0.008333 degree per second.

Full sample below :

```/******************************************************************************
* File : AnalogClock.java
* Author : http://java.macteki.com/
* Description :
*   Display an analog clock.
* Tested with : JDK 1.6
******************************************************************************/

import javax.swing.JLabel;

class AnalogClock extends javax.swing.JPanel
{
public static AnalogClock getInstance()
{
AnalogClock panel=new AnalogClock();
panel.setPreferredSize(new java.awt.Dimension(300,300));
return panel;
}

public void paintComponent(java.awt.Graphics graphics)
{
super.paintComponent(graphics);

java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

gr.drawArc(0,0,200,200,0,360);

int xCenter=100,yCenter=100;

java.util.Calendar calendar=java.util.Calendar.getInstance();
int second=calendar.get(java.util.Calendar.SECOND);

// polar coordinate of the 'second' arm
// This arm start at 90 degree and it should move 6 degree per second
double degree = 90-second*6;
double pi=3.141592654;

y=-y;  // positive y-axis should point upwards.

// translate the whole coordinate system.
x+=xCenter;
y+=yCenter;

gr.drawLine(xCenter,yCenter,x,y);

// draw the minute arm, it should move 0.1 degree every second.
int minute=calendar.get(java.util.Calendar.MINUTE);
double secondPassedInThisHour = minute*60+second;
degree = 90.0 - secondPassedInThisHour*(0.1);

y=-y;  // positive y-axis should point upwards.

// translate the whole coordinate system.
x+=xCenter;
y+=yCenter;

gr.drawLine(xCenter,yCenter,x,y);

// draw the hour arm, since this arm should move 360 degree in 12 hour
// 12 hour = 43200 seconds
// it should move 360/43200 degree every second.
int hour=calendar.get(java.util.Calendar.HOUR);
if (hour>12) hour-=12;
double secondPassed = hour*3600+minute*60+second;
degree = 90.0 - secondPassed*(360.0/43200.0);
radius = 60;  // make it shorter

y=-y;  // positive y-axis should point upwards.

// translate the whole coordinate system.
x+=xCenter;
y+=yCenter;

gr.drawLine(xCenter,yCenter,x,y);

}

public static void main(String[] args) throws Exception
{
// create frame
javax.swing.JFrame frame=new javax.swing.JFrame();
frame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);

frame.setTitle("Macteki AnalogClock");

AnalogClock clockPanel=AnalogClock.getInstance();

// create a image label
JLabel digitalLabel=new JLabel("", JLabel.CENTER);
digitalLabel.setBounds(0,250,300,30);
clockPanel.setLayout(null);

frame.pack();  // set the window size big enough to hold its component
frame.setVisible(true);

while (true)
{
java.util.Date date=new java.util.Date();
digitalLabel.setText(""+date);
clockPanel.repaint();
}

}
}
```

This is not a pretty clock but it serves the purpose of a simple demonstration. You may draw a clock with better appearance if you wish.

1. Thank you, GREAT HELP

1. My pleasure. Glad to know that it is useful to some readers.

2. I am trying to make a calendar program and want to add this clock to it. How would I implement the clock to my main class? I'm kinda new to programming so I apologize if the answer is rather simple.

1. 1.Put AnalogClock.java to the same path as your main class

2. If your main class is contained inside a package, (e.g. package projectx), then "package projectx" must be added at the beginning of AnalogClock.java

3. In getInstance(), just before returning, add this :

public void run()
{
while (true)
{
java.util.Date date=new java.util.Date();
panel.repaint();
try { Thread.sleep(500); } catch (Exception ignore) {}
}
}
}).start();

4. In the main program, after creating the main window (e.g. JFrame frame), just write:

AnalogClock clockPanel=AnalogClock.getInstance();
3. 1. 