11
11
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
12
12
* BSD-like license that allows static linking with closed source software
13
13
*
14
- * Copyright (c) 2025 Hamza RAHAL (@hmz-rhl)
14
+ * Copyright (c) 2025 Hamza RAHAL (@hmz-rhl) and Ramon Santamaria (@raysan5)
15
15
*
16
16
********************************************************************************************/
17
17
20
20
#include <math.h> // Required for: cosf(), sinf()
21
21
#include <time.h> // Required for: time(), localtime()
22
22
23
- #define DIGIT_SIZE 30
23
+ #define CLOCK_ANALOG 0
24
+ #define CLOCK_DIGITAL 1
24
25
25
26
//----------------------------------------------------------------------------------
26
27
// Types and Structures Definition
27
28
//----------------------------------------------------------------------------------
28
- typedef enum {
29
- MODE_NORMAL = 0 ,
30
- MODE_HANDS_FREE ,
31
- } ClockMode ;
32
-
29
+ // Clock hand type
33
30
typedef struct {
34
- int value ;
35
- Vector2 origin ;
36
- float angle ;
37
- int length ;
38
- int thickness ;
39
- Color color ;
31
+ int value ; // Time value
32
+
33
+ // Visual elements
34
+ float angle ; // Hand angle
35
+ int length ; // Hand length
36
+ int thickness ; // Hand thickness
37
+ Color color ; // Hand color
40
38
} ClockHand ;
41
39
40
+ // Clock hands
42
41
typedef struct {
43
- ClockMode mode ;
44
- ClockHand second ;
45
- ClockHand minute ;
46
- ClockHand hour ;
42
+ ClockHand second ; // Clock hand for seconds
43
+ ClockHand minute ; // Clock hand for minutes
44
+ ClockHand hour ; // Clock hand for hours
47
45
} Clock ;
48
46
49
47
//----------------------------------------------------------------------------------
50
48
// Module Functions Declaration
51
49
//----------------------------------------------------------------------------------
52
50
static void UpdateClock (Clock * clock ); // Update clock time
53
- static void DrawClock (Clock clock , Vector2 centerPos ); // Draw clock at desired position
51
+ static void DrawClockAnalog (Clock clock , Vector2 position ); // Draw analog clock at desired center position
52
+ static void DrawClockDigital (Clock clock , Vector2 position ); // Draw digital clock at desired position
53
+
54
+ static void DrawDisplayValue (Vector2 position , int value , Color colorOn , Color colorOff );
55
+ static void Draw7SDisplay (Vector2 position , char segments , Color colorOn , Color colorOff );
56
+ static void DrawDisplaySegment (Vector2 center , int length , int thick , bool vertical , Color color );
54
57
55
58
//------------------------------------------------------------------------------------
56
59
// Program main entry point
@@ -62,16 +65,18 @@ int main(void)
62
65
const int screenWidth = 800 ;
63
66
const int screenHeight = 450 ;
64
67
68
+ SetConfigFlags (FLAG_MSAA_4X_HINT );
65
69
InitWindow (screenWidth , screenHeight , "raylib [shapes] example - digital clock" );
66
70
67
- // Initialize clock
68
- Clock myClock = {
69
- .mode = MODE_NORMAL ,
71
+ int clockMode = CLOCK_DIGITAL ;
70
72
73
+ // Initialize clock
74
+ // NOTE: Includes visual info for anlaog clock
75
+ Clock clock = {
71
76
.second .angle = 45 ,
72
77
.second .length = 140 ,
73
78
.second .thickness = 3 ,
74
- .second .color = BEIGE ,
79
+ .second .color = MAROON ,
75
80
76
81
.minute .angle = 10 ,
77
82
.minute .length = 130 ,
@@ -94,11 +99,12 @@ int main(void)
94
99
//----------------------------------------------------------------------------------
95
100
if (IsKeyPressed (KEY_SPACE ))
96
101
{
97
- if (myClock .mode == MODE_HANDS_FREE ) myClock .mode = MODE_NORMAL ;
98
- else if (myClock .mode == MODE_NORMAL ) myClock .mode = MODE_HANDS_FREE ;
102
+ // Toggle clock mode
103
+ if (clockMode == CLOCK_DIGITAL ) clockMode = CLOCK_ANALOG ;
104
+ else if (clockMode == CLOCK_ANALOG ) clockMode = CLOCK_DIGITAL ;
99
105
}
100
106
101
- UpdateClock (& myClock );
107
+ UpdateClock (& clock ); // Update clock required data: value and angle
102
108
//----------------------------------------------------------------------------------
103
109
104
110
// Draw
@@ -107,11 +113,21 @@ int main(void)
107
113
108
114
ClearBackground (RAYWHITE );
109
115
110
- DrawCircle (400 , 225 , 5 , BLACK ); // Clock center dot
116
+ // Draw clock in selected mode
117
+ if (clockMode == CLOCK_ANALOG ) DrawClockAnalog (clock , (Vector2 ){ 400 , 240 });
118
+ else if (clockMode == CLOCK_DIGITAL )
119
+ {
120
+ DrawClockDigital (clock , (Vector2 ){ 30 , 60 });
111
121
112
- DrawClock (myClock , (Vector2 ){ 400 , 225 }); // Clock in selected mode
122
+ // Draw clock using default raylib font
123
+ // Get pointer to formated clock time string
124
+ // WARNING: Pointing to an internal static string that is reused between TextFormat() calls
125
+ const char * clockTime = TextFormat ("%02i:%02i:%02i" , clock .hour .value , clock .minute .value , clock .second .value );
126
+ DrawText (clockTime , GetScreenWidth ()/2 - MeasureText (clockTime , 150 )/2 , 300 , 150 , BLACK );
127
+ }
113
128
114
- DrawText ("Press [SPACE] to switch clock mode" , 10 , 10 , 20 , DARKGRAY );
129
+ DrawText (TextFormat ("Press [SPACE] to switch clock mode: %s" ,
130
+ (clockMode == CLOCK_DIGITAL )? "DIGITAL CLOCK" : "ANALOGUE CLOCK" ), 10 , 10 , 20 , DARKGRAY );
115
131
116
132
EndDrawing ();
117
133
//----------------------------------------------------------------------------------
@@ -154,31 +170,146 @@ static void UpdateClock(Clock *clock)
154
170
clock -> second .angle -= 90 ;
155
171
}
156
172
157
- // Draw clock
158
- static void DrawClock (Clock clock , Vector2 centerPosition )
173
+ // Draw analog clock
174
+ // Parameter: position, refers to center position
175
+ static void DrawClockAnalog (Clock clock , Vector2 position )
159
176
{
160
- if ( clock . mode == MODE_HANDS_FREE )
161
- {
162
- DrawCircleLinesV ( centerPosition , clock . minute . length , LIGHTGRAY );
177
+ // Draw clock base
178
+ DrawCircleV ( position , clock . second . length + 40 , LIGHTGRAY );
179
+ DrawCircleV ( position , 12 , GRAY );
163
180
164
- DrawText (TextFormat ("%i" , clock .second .value ), centerPosition .x + (clock .second .length - 10 )* cosf (clock .second .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , centerPosition .y + clock .second .length * sinf (clock .second .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , DIGIT_SIZE , GRAY );
165
-
166
- DrawText (TextFormat ("%i" , clock .minute .value ), centerPosition .x + clock .minute .length * cosf (clock .minute .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , centerPosition .y + clock .minute .length * sinf (clock .minute .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , DIGIT_SIZE , RED );
181
+ // Draw clock minutes/seconds lines
182
+ for (int i = 0 ; i < 60 ; i ++ )
183
+ {
184
+ DrawLineEx ((Vector2 ){ position .x + (clock .second .length + ((i %5 )? 10 : 6 ))* cosf ((6.0f * i - 90.0f )* DEG2RAD ),
185
+ position .y + (clock .second .length + ((i %5 )? 10 : 6 ))* sinf ((6.0f * i - 90.0f )* DEG2RAD ) },
186
+ (Vector2 ){ position .x + (clock .second .length + 20 )* cosf ((6.0f * i - 90.0f )* DEG2RAD ),
187
+ position .y + (clock .second .length + 20 )* sinf ((6.0f * i - 90.0f )* DEG2RAD ) }, ((i %5 )? 1.0f : 3.0f ), DARKGRAY );
188
+
189
+ // Draw seconds numbers
190
+ //DrawText(TextFormat("%02i", i), centerPosition.x + (clock.second.length + 50)*cosf((6.0f*i - 90.0f)*DEG2RAD) - 10/2,
191
+ // centerPosition.y + (clock.second.length + 50)*sinf((6.0f*i - 90.0f)*DEG2RAD) - 10/2, 10, GRAY);
192
+ }
167
193
168
- DrawText (TextFormat ("%i" , clock .hour .value ), centerPosition .x + clock .hour .length * cosf (clock .hour .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , centerPosition .y + clock .hour .length * sinf (clock .hour .angle * (float )(PI /180 )) - DIGIT_SIZE /2 , DIGIT_SIZE , GOLD );
169
- }
170
- else if (clock .mode == MODE_NORMAL )
171
- {
172
194
// Draw hand seconds
173
- DrawRectanglePro ((Rectangle ){ centerPosition .x , centerPosition .y , clock .second .length , clock .second .thickness },
195
+ DrawRectanglePro ((Rectangle ){ position .x , position .y , clock .second .length , clock .second .thickness },
174
196
(Vector2 ){ 0.0f , clock .second .thickness /2.0f }, clock .second .angle , clock .second .color );
175
197
176
198
// Draw hand minutes
177
- DrawRectanglePro ((Rectangle ){ centerPosition .x , centerPosition .y , clock .minute .length , clock .minute .thickness },
199
+ DrawRectanglePro ((Rectangle ){ position .x , position .y , clock .minute .length , clock .minute .thickness },
178
200
(Vector2 ){ 0.0f , clock .minute .thickness /2.0f }, clock .minute .angle , clock .minute .color );
179
201
180
202
// Draw hand hours
181
- DrawRectanglePro ((Rectangle ){ centerPosition .x , centerPosition .y , clock .hour .length , clock .hour .thickness },
203
+ DrawRectanglePro ((Rectangle ){ position .x , position .y , clock .hour .length , clock .hour .thickness },
182
204
(Vector2 ){ 0.0f , clock .hour .thickness /2.0f }, clock .hour .angle , clock .hour .color );
205
+ }
206
+
207
+ // Draw digital clock
208
+ // PARAM: position, refers to top-left corner
209
+ static void DrawClockDigital (Clock clock , Vector2 position )
210
+ {
211
+ // Draw clock using custom 7-segments display (made of shapes)
212
+ DrawDisplayValue ((Vector2 ){ position .x , position .y }, clock .hour .value /10 , RED , Fade (LIGHTGRAY , 0.3f ));
213
+ DrawDisplayValue ((Vector2 ){ position .x + 120 , position .y }, clock .hour .value %10 , RED , Fade (LIGHTGRAY , 0.3f ));
214
+
215
+ DrawCircle (position .x + 240 , position .y + 70 , 12 , (clock .second .value %2 )? RED : Fade (LIGHTGRAY , 0.3f ));
216
+ DrawCircle (position .x + 240 , position .y + 150 , 12 , (clock .second .value %2 )? RED : Fade (LIGHTGRAY , 0.3f ));
217
+
218
+ DrawDisplayValue ((Vector2 ){ position .x + 260 , position .y }, clock .minute .value /10 , RED , Fade (LIGHTGRAY , 0.3f ));
219
+ DrawDisplayValue ((Vector2 ){ position .x + 380 , position .y }, clock .minute .value %10 , RED , Fade (LIGHTGRAY , 0.3f ));
220
+
221
+ DrawCircle (position .x + 500 , position .y + 70 , 12 , (clock .second .value %2 )? RED : Fade (LIGHTGRAY , 0.3f ));
222
+ DrawCircle (position .x + 500 , position .y + 150 , 12 , (clock .second .value %2 )? RED : Fade (LIGHTGRAY , 0.3f ));
223
+
224
+ DrawDisplayValue ((Vector2 ){ position .x + 520 , position .y }, clock .second .value /10 , RED , Fade (LIGHTGRAY , 0.3f ));
225
+ DrawDisplayValue ((Vector2 ){ position .x + 640 , position .y }, clock .second .value %10 , RED , Fade (LIGHTGRAY , 0.3f ));
226
+ }
227
+
228
+ // Draw 7-segment display with value
229
+ static void DrawDisplayValue (Vector2 position , int value , Color colorOn , Color colorOff )
230
+ {
231
+ switch (value )
232
+ {
233
+ case 0 : Draw7SDisplay (position , 0b00111111 , colorOn , colorOff ); break ;
234
+ case 1 : Draw7SDisplay (position , 0b00000110 , colorOn , colorOff ); break ;
235
+ case 2 : Draw7SDisplay (position , 0b01011011 , colorOn , colorOff ); break ;
236
+ case 3 : Draw7SDisplay (position , 0b01001111 , colorOn , colorOff ); break ;
237
+ case 4 : Draw7SDisplay (position , 0b01100110 , colorOn , colorOff ); break ;
238
+ case 5 : Draw7SDisplay (position , 0b01101101 , colorOn , colorOff ); break ;
239
+ case 6 : Draw7SDisplay (position , 0b01111101 , colorOn , colorOff ); break ;
240
+ case 7 : Draw7SDisplay (position , 0b00000111 , colorOn , colorOff ); break ;
241
+ case 8 : Draw7SDisplay (position , 0b01111111 , colorOn , colorOff ); break ;
242
+ case 9 : Draw7SDisplay (position , 0b01101111 , colorOn , colorOff ); break ;
243
+ default : break ;
244
+ }
245
+ }
246
+
247
+ // Draw seven segments display
248
+ // Parameter: position, refers to top-left corner of display
249
+ // Parameter: segments, defines in binary the segments to be activated
250
+ static void Draw7SDisplay (Vector2 position , char segments , Color colorOn , Color colorOff )
251
+ {
252
+ int segmentLen = 60 ;
253
+ int segmentThick = 20 ;
254
+ float offsetYAdjust = segmentThick * 0.3f ; // HACK: Adjust gap space between segment limits
255
+
256
+ // Segment A
257
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick + segmentLen /2.0f , position .y + segmentThick },
258
+ segmentLen , segmentThick , false, (segments & 0b00000001 )? colorOn : colorOff );
259
+ // Segment B
260
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick + segmentLen + segmentThick /2.0f , position .y + 2 * segmentThick + segmentLen /2.0f - offsetYAdjust },
261
+ segmentLen , segmentThick , true, (segments & 0b00000010 )? colorOn : colorOff );
262
+ // Segment C
263
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick + segmentLen + segmentThick /2.0f , position .y + 4 * segmentThick + segmentLen + segmentLen /2.0f - 3 * offsetYAdjust },
264
+ segmentLen , segmentThick , true, (segments & 0b00000100 )? colorOn : colorOff );
265
+ // Segment D
266
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick + segmentLen /2.0f , position .y + 5 * segmentThick + 2 * segmentLen - 4 * offsetYAdjust },
267
+ segmentLen , segmentThick , false, (segments & 0b00001000 )? colorOn : colorOff );
268
+ // Segment E
269
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick /2.0f , position .y + 4 * segmentThick + segmentLen + segmentLen /2.0f - 3 * offsetYAdjust },
270
+ segmentLen , segmentThick , true, (segments & 0b00010000 )? colorOn : colorOff );
271
+ // Segment F
272
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick /2.0f , position .y + 2 * segmentThick + segmentLen /2.0f - offsetYAdjust },
273
+ segmentLen , segmentThick , true, (segments & 0b00100000 )? colorOn : colorOff );
274
+ // Segment G
275
+ DrawDisplaySegment ((Vector2 ){ position .x + segmentThick + segmentLen /2.0f , position .y + 3 * segmentThick + segmentLen - 2 * offsetYAdjust },
276
+ segmentLen , segmentThick , false, (segments & 0b01000000 )? colorOn : colorOff );
277
+ }
278
+
279
+ // Draw one 7-segment display segment, horizontal or vertical
280
+ static void DrawDisplaySegment (Vector2 center , int length , int thick , bool vertical , Color color )
281
+ {
282
+ if (!vertical )
283
+ {
284
+ // Horizontal segment points
285
+ // 3___________________________5
286
+ // / \
287
+ // /1 x 6\
288
+ // \ /
289
+ // \2___________________________4/
290
+ Vector2 segmentPointsH [6 ] = {
291
+ (Vector2 ){ center .x - length /2.0f - thick /2.0f , center .y }, // Point 1
292
+ (Vector2 ){ center .x - length /2.0f , center .y + thick /2.0f }, // Point 2
293
+ (Vector2 ){ center .x - length /2.0f , center .y - thick /2.0f }, // Point 3
294
+ (Vector2 ){ center .x + length /2.0f , center .y + thick /2.0f }, // Point 4
295
+ (Vector2 ){ center .x + length /2.0f , center .y - thick /2.0f }, // Point 5
296
+ (Vector2 ){ center .x + length /2.0f + thick /2.0f , center .y }, // Point 6
297
+ };
298
+
299
+ DrawTriangleStrip (segmentPointsH , 6 , color );
300
+ }
301
+ else
302
+ {
303
+ // Vertical segment points
304
+ Vector2 segmentPointsV [6 ] = {
305
+ (Vector2 ){ center .x , center .y - length /2.0f - thick /2.0f }, // Point 1
306
+ (Vector2 ){ center .x - thick /2.0f , center .y - length /2.0f }, // Point 2
307
+ (Vector2 ){ center .x + thick /2.0f , center .y - length /2.0f }, // Point 3
308
+ (Vector2 ){ center .x - thick /2.0f , center .y + length /2.0f }, // Point 4
309
+ (Vector2 ){ center .x + thick /2.0f , center .y + length /2.0f }, // Point 5
310
+ (Vector2 ){ center .x , center .y + length /2 + thick /2.0f }, // Point 6
311
+ };
312
+
313
+ DrawTriangleStrip (segmentPointsV , 6 , color );
183
314
}
184
315
}
0 commit comments