DEV Community

Cover image for Case Study: The ClockPane Class
Paul Ngugi
Paul Ngugi

Posted on

Case Study: The ClockPane Class

This case study develops a class that displays a clock on a pane. The contract of the ClockPane class is shown in Figure below.

Image description

Assume ClockPane is available; we write a test program in the program below to display an analog clock and use a label to display the hour, minute, and second, as shown in Figure below.

Image description

Image description

The rest of this section explains how to implement the ClockPane class. Since you can use the class without knowing how it is implemented, you may skip the implementation if you wish.

To draw a clock, you need to draw a circle and three hands for the second, minute, and hour. To draw a hand, you need to specify the two ends of the line. As shown in Figure 14.42b, one end is the center of the clock at (centerX, centerY); the other end, at (endX, endY), is
determined by the following formula:

endX = centerX + handLength × sin(θ)
endY = centerY - handLength × cos(θ)

Since there are 60 seconds in one minute, the angle for the second hand is

second × (2π/60)

The position of the minute hand is determined by the minute and second. The exact minute value combined with seconds is minute + second/60. For example, if the time is 3 minutes and 30 seconds, the total minutes are 3.5. Since there are 60 minutes in one hour, the angle for the minute hand is

(minute + second/60) × (2π/60)

Since one circle is divided into 12 hours, the angle for the hour hand is

(hour + minute/60 + second/(60 × 60)) × (2π/12)

For simplicity in computing the angles of the minute hand and hour hand, you can omit the seconds, because they are negligibly small. Therefore, the endpoints for the second hand, minute hand, and hour hand can be computed as:

secondX = centerX + secondHandLength × sin(second × (2π/60))
secondY = centerY - secondHandLength × cos(second × (2π/60))
minuteX = centerX + minuteHandLength × sin(minute × (2π/60))
minuteY = centerY - minuteHandLength × cos(minute × (2π/60))
hourX = centerX + hourHandLength × sin((hour + minute/60) × (2π/12))
hourY = centerY - hourHandLength × cos((hour + minute/60) × (2π/12))

The ClockPane class is implemented in the program below.

package application;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;

public class ClockPane extends Pane {
    private int hour;
    private int minute;
    private int second;

    // Clock pane's width and height
    private double w = 250, h = 250;

    /** Construct a default clock with the current time */
    public ClockPane() {
        setCurrentTime();
    }

    /** Construct a clock with specified hour, minute, and second */
    public ClockPane(int hour, int minute, int second) {
        this.hour = hour;
        this.minute = minute;
        this.second = second;
        paintClock();
    }

    /** Return hour */
    public int getHour() {
        return hour;
    }

    /** Set a new hour */
    public void setHour(int hour) {
        this.hour = hour;
        paintClock();
    }

    /** return minute */
    public int getMinute() {
        return minute;
    }

    /** Set a new minute */
    public void setMinute(int minute) {
        this.minute = minute;
        paintClock();
    }

    /** Return second */
    public int getSecond() {
        return second;
    }

    /** Set a new second */
    public void setSecond(int second) {
        this.second = second;
        paintClock();
    }

    /** Return clock pane's width */
    public double getW() {
        return w;
    }

    /** Set clock pane's width */
    public void setW(double w) {
        this.w = w;
        paintClock();
    }

    /** Return clock pane's height */
    public double getH() {
        return h;
    }

    /** Set clock pane's height */
    public void setH(double h) {
        this.h = h;
        paintClock();
    }

    /** Set the current time for the clock */
    public void setCurrentTime() {
        // Construct a calendar for the current date and time
        Calendar calendar = new GregorianCalendar();

        // Set current hour, minute and second
        this.hour = calendar.get(Calendar.HOUR_OF_DAY);
        this.minute = calendar.get(Calendar.MINUTE);
        this.second = calendar.get(Calendar.SECOND);

        paintClock(); // Repaint the clock
    }

    /** Paint the clock */
    protected void paintClock() {
        // Initialize clock parameters
        double clockRadius = Math.min(w, h) * 0.8 * 0.5;
        double centerX = w / 2;
        double centerY = h / 2;

        // Draw circle
        Circle circle = new Circle(centerX, centerY, clockRadius);
        circle.setFill(Color.WHITE);
        circle.setStroke(Color.BLACK);
        Text t1 = new Text(centerX - 5, centerY - clockRadius + 12, "12");
        Text t2 = new Text(centerX - clockRadius + 3, centerY + 5, "9");
        Text t3 = new Text(centerX + clockRadius - 10, centerY + 3, "3");
        Text t4 = new Text(centerX - 3, centerY + clockRadius - 3, "6");

        // Draw second hand
        double sLength = clockRadius * 0.8;
        double secondX = centerX + sLength * Math.sin(second * (2 * Math.PI / 60));
        double secondY = centerY - sLength * Math.cos(second * (2 * Math.PI / 60));
        Line sLine = new Line(centerX, centerY, secondX, secondY);
        sLine.setStroke(Color.RED);

        // Draw minute hand
        double mLength = clockRadius * 0.65;
        double xMinute = centerX + mLength * Math.sin(minute * (2 * Math.PI / 60));
        double minuteY = centerY - mLength * Math.cos(minute * (2 * Math.PI / 60));
        Line mLine = new Line(centerX, centerY, xMinute, minuteY);
        mLine.setStroke(Color.BLUE);

        // Draw hour hand
        double hLength = clockRadius * 0.5;
        double hourX = centerX + hLength * Math.sin((hour % 12 + minute / 60.0) * (2 * Math.PI / 12));
        double hourY = centerY - hLength * Math.cos((hour % 12 + minute / 60.0) * (2 * Math.PI / 12));
        Line hLine = new Line(centerX, centerY, hourX, hourY);
        hLine.setStroke(Color.GREEN);

        getChildren().clear();
        getChildren().addAll(circle, t1, t2, t3, t4, sLine, mLine, hLine);
    }
}

Enter fullscreen mode Exit fullscreen mode

The program displays a clock for the current time using the no-arg constructor (lines 19–21) and displays a clock for the specified hour, minute, and second using the other constructor (lines 24–29). The current hour, minute, and second is obtained by using the GregorianCalendar class (lines 87–97). The GregorianCalendar class in the Java API enables you to create a Calendar instance for the current time using its no-arg constructor. You can then use its methods get(Calendar.HOUR), get(Calendar.MINUTE), and get(Calendar.SECOND) to return the hour, minute, and second from a Calendar object.

The class defines the properties hour, minute, and second to store the time represented in the clock (lines 11–13) and uses the w and h properties to represent the width and height of the clock pane (line 16). The initial values of w and h are set to 250. The w and h values can be reset using the setW and setH methods (lines 70, 81). These values are used to draw a clock in the pane in the paintClock() method.

The paintClock() method paints the clock (lines 100–138). The clock radius is proportional to the width and height of the pane (line 102). A circle for the clock is created at the center of the pane (line 107). The text for showing the hours 12, 3, 6, 9 are created in lines 110–113. The second hand, minute hand, and hour hand are the lines created in lines 115–134. The paintClock() method places all these shapes in the pane using the addAll method in a list (line 137). Because the paintClock() method is invoked whenever a new property (hour, minute, second, w, and h) is set (lines 28, 39, 50, 61, 72, 83, 96), before adding new contents into the pane, the old contents are cleared from the pane (line 136).

Top comments (0)