Update 'Analog Clock'

sigonasr2 2025-04-08 11:10:56 -05:00
parent 27a00c2486
commit 9353153a7d

@ -55,7 +55,7 @@ We will do this in `OnUserUpdate()`:
```
<a href="https://pgetinker.com/s/9PzMu2bRS6b" title="Click to try it out!" target="_blank">[***Try it out!***]</a>
[<img src="https://pit.pgetinker.com/SaoLD897rXSC.png" width="256" height="240">](https://pgetinker.com/s/9PzMu2bRS6b "Click to try it out!")
<a href="https://pgetinker.com/s/9PzMu2bRS6b" title="Click to try it out!" target="_blank"><img src="https://pit.pgetinker.com/SaoLD897rXSC.png" width="256" height="240"></a>
**Notice:**
> Usage of `GetScreenSize()` and dividing by 2 here means we don't have to use magic numbers to figure out what the center of the screen is. It returns a vector with the width and height for us.
@ -162,7 +162,7 @@ A little cleaner, and an alternative solution! Choose the one you like the most.
<a href="https://pgetinker.com/s/Y50E2kQVebK" title="Click to try it out!" target="_blank">[***Try it out!***]</a>
[<img src="https://pit.pgetinker.com/bnXBYvs1tzb0.png" width="256" height="240">](https://pgetinker.com/s/Y50E2kQVebK "Click to try it out!")
<a href="https://pgetinker.com/s/Y50E2kQVebK" title="Click to try it out!" target="_blank"><img src="https://pit.pgetinker.com/bnXBYvs1tzb0.png" width="256" height="240"></a>
### Rendering the Numbers
@ -236,10 +236,10 @@ Tidied up the code with some separation, comments, and moved the dots slightly f
<a href="https://pgetinker.com/s/y5OwBA5pHJ" title="Click to try it out!" target="_blank">[***Try it out!***]</a>
[<img src="https://pit.pgetinker.com/RccUk1rcxfc4.png" width="256" height="240">](https://pgetinker.com/s/y5OwBA5pHJ "Click to try it out!")
<a href="https://pgetinker.com/s/y5OwBA5pHJ" title="Click to try it out!" target="_blank"><img src="https://pit.pgetinker.com/RccUk1rcxfc4.png" width="256" height="240"></a>
## Getting Current Time
The next step is getting the system time. C++ provides us with a system clock in `std::chrono::system_clock::now()`. Unfortunately there are few ways to extra number of seconds from this form, so we are going to convert it to a classical `time_t` structure (a value holding the number of seconds since the epoch) to where it breaks things down into a `tm` structure which gives us exactly the number of hours, minutes, and seconds this time contains at this moment.
The next step is getting the system time. C++ provides us with a system clock in `std::chrono::system_clock::now()`. Unfortunately there are very few ways to extract number of seconds from this form. The way we will do it is by converting it to a classical `time_t` structure (a value holding the number of seconds since the epoch) to where we can breaks things down into a `tm` structure, giving us exactly the number of hours, minutes, and seconds this time contains.
```cpp
using namespace std::chrono;
std::time_t time{system_clock::to_time_t(system_clock::now())};
@ -253,6 +253,9 @@ The code is a little cryptic (working with the `chrono` library tends to be a ch
```
Again we do some centering of the text so it appears proper.
**NOTE**:
> Using the `->` operator to access members for a pointer structure.
<img src="https://pit.pgetinker.com/fUj1K6CZnlmt.png" width="256" height="240">
I noticed I am repeating some code again so I am going to condense the centered text rendering code into its own function.
@ -292,8 +295,86 @@ I noticed I am repeating some code again so I am going to condense the centered
```
Creating a template function for `DrawCenteredStringDecal` allows it to be compatible with `olc::vi2d` and `olc::vf2d` types together, which just reduces error clutter. Very unnecessary in all honesty, just keeps the error window clean.
We can see the number of lines being reduced down with both center drawing sections now being reduced to one line. Cleaner code for the upcoming final section of this mini project.
We can see the lines of code being condensed with both center text functions reduced to one line. Cleaner code for the upcoming last section of this project.
<a href="https://pgetinker.com/s/PKYcskPM1-i" title="Click to try it out!" target="_blank">[***Try it out!***]</a>
[<img src="https://pit.pgetinker.com/lmKm32AeLCb6.png" width="256" height="240">](https://pgetinker.com/s/PKYcskPM1-i "Click to try it out!")
<a href="https://pgetinker.com/s/PKYcskPM1-i" title="Click to try it out!" target="_blank"><img src="https://pit.pgetinker.com/lmKm32AeLCb6.png" width="256" height="240"></a>
## Clock Hands
It's finally time to render the hands of the clock! Let's start with the second hand. Just like in the previous step, we grabbed the seconds via `localTime->tm_sec`. This determines which direction we will point it in. It can be in one of sixty positions, and a full revolution in radians is `2π`. So `(2*π)/60` gives us how much to rotate per second.
Let's draw a red line pointing to the correct location on the clock now.
```cpp
bool OnUserUpdate(float fElapsedTime) override
{
Clear(olc::VERY_DARK_GREEN);
using namespace std::chrono;
using namespace std::numbers;
std::time_t time{system_clock::to_time_t(system_clock::now())};
std::tm*localTime{std::localtime(&time)};
DrawCenteredStringDecal(GetScreenSize()/2+olc::vi2d{0,15},std::format("{:02}:{:02}:{:02}",localTime->tm_hour,localTime->tm_min,localTime->tm_sec));
DrawCircle(GetScreenSize()/2,100);
FillCircle(GetScreenSize()/2,6,olc::BLACK);
const float secondsHandAngle{float(localTime->tm_sec*((2*pi)/60))};
olc::vf2d secondsHandVec{85,secondsHandAngle};
DrawLine(GetScreenSize()/2,GetScreenSize()/2+secondsHandVec.cart(),olc::RED);
FillCircle(GetScreenSize()/2,4,olc::DARK_GREY);
for(int i{0};i<12;i++){
//Draw decorative dots
olc::vf2d drawVec{60,float(i*(pi/6)-pi/3)};
Draw(GetScreenSize()/2+drawVec.cart());
//Draw clock numbers
olc::vf2d numberVec{drawVec};
numberVec.x=90; //Adjusted further towards the edge of the clock.
DrawCenteredStringDecal(GetScreenSize()/2+numberVec.cart(),std::format("{}",i+1),olc::GREY);
}
return true;
}
```
A little bit of code rearranging and adding a few circles in the center to make it look more like an analog clock. So far so good!
<img src="https://pit.pgetinker.com/XJqtgEn8yPg3.png" width="256" height="240">
You might notice an error with the image above. The seconds value is at 10 seconds but the hand is pointing at the "5" position on the clock, indicating 25 seconds. So it is off by 3 numbers, or 90 degrees. Once again, 0 radians starts to the right, so we have to subtract by `π/2` to make our hands point 90 degrees counter-clockwise.
```cpp
const float secondsHandAngle{float(localTime->tm_sec*2*pi/60-pi/2)};
```
I removed the parenthesis as the <a href="https://en.cppreference.com/w/cpp/language/operator_precedence" title="C++ operator precedence (cppreference)" target="_blank">C++ operator precedence</a> indicates that the `*`,`/`, and `%` operators all have the same precedence level, and have an associativity of "left-to-right". So we can read all these chained operators in a row from left-to-right and interpret them the same way we intended. Just be careful with this.
### Hour and Minute Hands
For the hour and minute hands, we will do similar. We divide the segments into 60 again for the minutes, and 24 for the hours. Since the clock is a 12-hour clock we have to modulus the 24 as well. `localTime->tm_hour%12` will mean when it is 12 the result will be `0`, and for 13 the result will be `1`, etc. Which is the desirable behavior.
**Tip:**
> Use the `%` operator when you have a looping value. This operator will always return the remainder of the division of the operation, which is useful for looping timers, array indices, etc.
```cpp
const float hourHandAngle{float(localTime->tm_hour%12*2*pi/12-pi/2)};
olc::vf2d hourHandVec{60,hourHandAngle};
DrawLine(GetScreenSize()/2,GetScreenSize()/2+hourHandVec.cart(),olc::BLACK);
const float minutesHandAngle{float(localTime->tm_min*2*pi/60-pi/2)};
olc::vf2d minutesHandVec{85,minutesHandAngle};
DrawLine(GetScreenSize()/2,GetScreenSize()/2+minutesHandVec.cart(),olc::BLACK);
const float secondsHandAngle{float(localTime->tm_sec*2*pi/60-pi/2)};
olc::vf2d secondsHandVec{85,secondsHandAngle};
DrawLine(GetScreenSize()/2,GetScreenSize()/2+secondsHandVec.cart(),olc::RED);
```
Similar concept to the seconds hand, drawing a black line and a shorter black line for the minute and hour hand respectively.
<img src="https://pit.pgetinker.com/Hl6c7FACToFf.png" width="256" height="240">
I am going to remove the digital display of the clock now since one is supposed to assume the time based on the positions of the hands. The final result!
<a href="https://pgetinker.com/s/4wS5gtOFiHX" title="Click to try it out!" target="_blank">[***Try it out!***]</a>
<a href="https://pgetinker.com/s/4wS5gtOFiHX" title="Click to try it out!" target="_blank"><img src="https://pit.pgetinker.com/5MumBR308PXW.png" width="256" height="240"></a>