Friday, February 5, 2016

Fun with Medusa Clocks...

Hi there,

Time for some fun again...
When I was searching the web for cool stuff I stumbled upon the following clock...



The idea is really great and I thought by myself that it might be nice to have that clock in JavaFX.

As you might have seen I was playing around with clocks anyway so why not use the Medusa Clock to create the Clock of Clocks...

First of all it was needed to create a new skin with really fat hands and so I've added the FatClockSkin to Medusa 3.1 which looks very similar to the clocks above, here it is...



With this visualization in place the next challenge was to get the right behavior of the clock which was not that easy. The problem is that in the real clock the hands are controlled separately which means you can place the minute hand to 45 minutes and the hour hand to 6 directly. Well with the Medusa clock that's not possible because I calculate the angle of the hands from the given time which means in the case of 5:45 my clock will look like follows...



As you can see the hour pointer is not pointing exactly to the 6 which would be needed for the Clock of Clocks. To emulate that behavior I've added a property named discreteHours(). If this property is enabled the hour hand will only jump to the next hour when the hour switches from e.g. 5 to 6. So to get the right behavior we need to set the time to 6:45 instead of 5:45 and the clock will look like follows...



That looks much better :)

The last thing that I needed to implement was the possibility to animate the hands from a given time to another time. Usually the clock just has a property named running which will let the clock run automatically with the given time. But in this case I needed the ability to set the clock to a time and animate it in a given duration to another time. Therefore I've implemented the same properties that are also part of the Medusa Gauge. Animated and animationDuration. 

ATTENTION:
If animated == true the clock can NOT be started by calling setRunning(true).

With these new features in place I could start to build the clock. It is too much code to show it all here, so let me try to explain shortly what I did.
The clock is made out of 4 numbers where each number contains 6 clocks. So I've created a GridPane for each number with the 6 clocks.
Each clock is created with the following method...
private Clock createClock() {
    Clock clock = ClockBuilder.create()
                              .skinType(ClockSkinType.FAT)
                              .backgroundPaint(Color.WHITE)
                              .prefSize(100, 100)
                              .animationDuration(7500)
                              .animated(true)
                              .discreteMinutes(false)
                              .discreteHours(true)
                              .hourTickMarkColor(Color.rgb(200, 200, 200))
                              .minuteTickMarkColor(Color.rgb(200, 200, 200))
                              .tickLabelColor(Color.rgb(200, 200, 200))
                              .build();
    clock.setEffect(new DropShadow(5, 0, 5, Color.rgb(0, 0, 0, 0.65)));
    return clock;
}
With this method I can easily create the GridPane with the following method...
private GridPane createNumberGrid() {
    GridPane grid = new GridPane();
    grid.add(createClock(), 0, 0);
    grid.add(createClock(), 1, 0);
    grid.add(createClock(), 0, 1);
    grid.add(createClock(), 1, 1);
    grid.add(createClock(), 0, 2);
    grid.add(createClock(), 1, 2);
    grid.setHgap(10);
    grid.setVgap(10);
    return grid;
}
The most work was needed to create a lookup table with the right time for each clock and for each number (0-9). Therefore I've simply created 6 LocalTime arrays that contains the correct time for each number and clock. This is the array for the upper left clock of each number...
private LocalTime[]    upperLeft  = { LocalTime.of(6, 15),   // 0 
                                      LocalTime.of(8, 40),   // 1 
                                      LocalTime.of(3, 15),   // 2
                                      LocalTime.of(3, 15),   // 3
                                      LocalTime.of(6, 30),   // 4
                                      LocalTime.of(6, 15),   // 5
                                      LocalTime.of(6, 15),   // 6
                                      LocalTime.of(3, 15),   // 7
                                      LocalTime.of(6, 15),   // 8
                                      LocalTime.of(6, 15) }; // 9
And so there is a similar array for the upper left, upper right, mid left, mid right, lower left and lower right clock.
Now I only have to run an AnimationTimer where I check the time every 10 seconds and set the right time for each clock.

If you are interested in the code you can find it on github...

The result of the work looks like this...



Because there are clocks that are not needed to visualize a number (like in 1 or 7) those not needed clocks will always be set to 7:40. This is the same as in the real clock.

Here is also a little video of the clock in action...


That's it for today...so keep coding...

1 comment: