| @ -0,0 +1,227 @@ | |||||
| /******************************************************************************************* | |||||
| * | |||||
| * raylib [shapes] example - clock of clocks | |||||
| * | |||||
| * Example complexity rating: [★★☆☆] 2/4 | |||||
| * | |||||
| * Example originally created with raylib 5.5 | |||||
| * | |||||
| * Example contributed by JP Mortiboys (@themushroompirates) and reviewed by Ramon Santamaria (@raysan5) | |||||
| * | |||||
| * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, | |||||
| * BSD-like license that allows static linking with closed source software | |||||
| * | |||||
| * Copyright (c) 2025 JP Mortiboys (@themushroompirates) | |||||
| * | |||||
| ********************************************************************************************/ | |||||
| #include "raylib.h" | |||||
| #include "raymath.h" // Required for: Lerp() | |||||
| #include <time.h> // Required for: time(), localtime() | |||||
| //------------------------------------------------------------------------------------ | |||||
| // Program main entry point | |||||
| //------------------------------------------------------------------------------------ | |||||
| int main(void) | |||||
| { | |||||
| // Initialization | |||||
| //-------------------------------------------------------------------------------------- | |||||
| const int screenWidth = 800; | |||||
| const int screenHeight = 450; | |||||
| SetConfigFlags(FLAG_MSAA_4X_HINT); | |||||
| InitWindow(screenWidth, screenHeight, "raylib [shapes] example - clock of clocks"); | |||||
| const Color bgColor = ColorLerp(DARKBLUE, BLACK, 0.75f); | |||||
| const Color handsColor = ColorLerp(YELLOW, RAYWHITE, .25f); | |||||
| const float clockFaceSize = 24; | |||||
| const float clockFaceSpacing = 8.0f; | |||||
| const float sectionSpacing = 16.0f; | |||||
| const Vector2 TL = (Vector2){ 0.0f, 90.0f }; // Top-left corner | |||||
| const Vector2 TR = (Vector2){ 90.0f, 180.0f }; // Top-right corner | |||||
| const Vector2 BR = (Vector2){ 180.0f, 270.0f }; // Bottom-right corner | |||||
| const Vector2 BL = (Vector2){ 0.0f, 270.0f }; // Bottom-left corner | |||||
| const Vector2 HH = (Vector2){ 0.0f, 180.0f }; // Horizontal line | |||||
| const Vector2 VV = (Vector2){ 90.0f, 270.0f }; // Vertical line | |||||
| const Vector2 ZZ = (Vector2){ 135.0f, 135.0f }; // Not relevant | |||||
| const Vector2 digitAngles[10][24] = { | |||||
| /* 0 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,VV,VV,VV,/* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| /* 1 */ { TL,HH,TR,ZZ, /* */ BL,TR,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ ZZ,VV,VV,ZZ,/* */ TL,BR,BL,TR,/* */ BL,HH,HH,BR }, | |||||
| /* 2 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,HH,BR }, | |||||
| /* 3 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| /* 4 */ { TL,TR,TL,TR, /* */ VV,VV,VV,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR }, | |||||
| /* 5 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| /* 6 */ { TL,HH,HH,TR, /* */ VV,TL,HH,BR,/* */ VV,BL,HH,TR,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| /* 7 */ { TL,HH,HH,TR, /* */ BL,HH,TR,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,VV,VV,/* */ ZZ,ZZ,BL,BR }, | |||||
| /* 8 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| /* 9 */ { TL,HH,HH,TR, /* */ VV,TL,TR,VV,/* */ VV,BL,BR,VV,/* */ BL,HH,TR,VV,/* */ TL,HH,BR,VV,/* */ BL,HH,HH,BR }, | |||||
| }; | |||||
| // Time for the hands to move to the new position (in seconds); this must be <1s | |||||
| const float handsMoveDuration = .5f; | |||||
| // We store the previous seconds value so we can see if the time has changed | |||||
| int prevSeconds = -1; | |||||
| // This represents the real position where the hands are right now | |||||
| Vector2 currentAngles[6][24] = { 0 }; | |||||
| // This is the position where the hands were moving from | |||||
| Vector2 srcAngles[6][24] = { 0 }; | |||||
| // This is the position where the hands are moving to | |||||
| Vector2 dstAngles[6][24] = { 0 }; | |||||
| // Current animation timer | |||||
| float handsMoveTimer = 0.0f; | |||||
| // 12 or 24 hour mode | |||||
| int hourMode = 24; | |||||
| SetTargetFPS(60); // Set our game to run at 60 frames-per-second | |||||
| //-------------------------------------------------------------------------------------- | |||||
| // Main game loop | |||||
| while (!WindowShouldClose()) // Detect window close button or ESC key | |||||
| { | |||||
| // Update | |||||
| //---------------------------------------------------------------------------------- | |||||
| // Get the current time | |||||
| time_t rawtime; | |||||
| struct tm *timeinfo; | |||||
| time(&rawtime); | |||||
| timeinfo = localtime(&rawtime); | |||||
| if (timeinfo->tm_sec != prevSeconds) { | |||||
| // The time has changed, so we need to move the hands to the new positions | |||||
| prevSeconds = timeinfo->tm_sec; | |||||
| // Format the current time so we can access the individual digits | |||||
| const char *clockDigits = TextFormat("%02d%02d%02d", timeinfo->tm_hour % hourMode, timeinfo->tm_min, timeinfo->tm_sec); | |||||
| // Fetch where we want all the hands to be | |||||
| for (int digit = 0; digit < 6; digit++) { | |||||
| for (int cell = 0; cell < 24; cell++) { | |||||
| srcAngles[digit][cell] = currentAngles[digit][cell]; | |||||
| dstAngles[digit][cell] = digitAngles[ clockDigits[digit] - '0' ][cell]; | |||||
| // Quick exception for 12h mode | |||||
| if (digit == 0 && hourMode == 12 && clockDigits[0] == '0') { | |||||
| dstAngles[digit][cell] = ZZ; | |||||
| } | |||||
| if (srcAngles[digit][cell].x > dstAngles[digit][cell].x) { | |||||
| srcAngles[digit][cell].x -= 360.0f; | |||||
| } | |||||
| if (srcAngles[digit][cell].y > dstAngles[digit][cell].y) { | |||||
| srcAngles[digit][cell].y -= 360.0f; | |||||
| } | |||||
| } | |||||
| } | |||||
| // Reset the timer | |||||
| handsMoveTimer = -GetFrameTime(); | |||||
| } | |||||
| // Now let's animate all the hands if we need to | |||||
| if (handsMoveTimer < handsMoveDuration) { | |||||
| // Increase the timer but don't go above the maximum | |||||
| handsMoveTimer = Clamp(handsMoveTimer + GetFrameTime(), 0, handsMoveDuration); | |||||
| // Calculate the % completion of the animation | |||||
| float t = handsMoveTimer / handsMoveDuration; | |||||
| // A little cheeky smoothstep | |||||
| t = t * t * (3.0f - 2.0f * t); | |||||
| for (int digit = 0; digit < 6; digit++) { | |||||
| for (int cell = 0; cell < 24; cell++) { | |||||
| currentAngles[digit][cell].x = Lerp(srcAngles[digit][cell].x, dstAngles[digit][cell].x, t); | |||||
| currentAngles[digit][cell].y = Lerp(srcAngles[digit][cell].y, dstAngles[digit][cell].y, t); | |||||
| } | |||||
| } | |||||
| if (handsMoveTimer == handsMoveDuration) { | |||||
| // The animation has now finished | |||||
| } | |||||
| } | |||||
| // Handle input | |||||
| // Toggle between 12 and 24 hour mode with space | |||||
| if (IsKeyPressed(KEY_SPACE)) { | |||||
| hourMode = 36 - hourMode; | |||||
| } | |||||
| //---------------------------------------------------------------------------------- | |||||
| // Draw | |||||
| //---------------------------------------------------------------------------------- | |||||
| BeginDrawing(); | |||||
| ClearBackground(bgColor); | |||||
| DrawText(TextFormat("%d-h mode, space to change", hourMode), 10, 30, 20, RAYWHITE); | |||||
| float xOffset = 4.0f; | |||||
| for (int digit = 0; digit < 6; digit++) { | |||||
| for (int row = 0; row < 6; row++) { | |||||
| for (int col = 0; col < 4; col++) { | |||||
| Vector2 centre = (Vector2){ | |||||
| xOffset + col*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f, | |||||
| 100 + row*(clockFaceSize+clockFaceSpacing) + clockFaceSize * .5f | |||||
| }; | |||||
| DrawRing(centre, clockFaceSize * 0.5f - 2.0f, clockFaceSize * 0.5f, 0, 360, 24, DARKGRAY); | |||||
| // Big hand | |||||
| DrawRectanglePro( | |||||
| (Rectangle){centre.x, centre.y, clockFaceSize*.5f+4.0f, 4.0f}, | |||||
| (Vector2){ 2.0f, 2.0f }, | |||||
| currentAngles[digit][row*4+col].x, | |||||
| handsColor | |||||
| ); | |||||
| // Little hand | |||||
| DrawRectanglePro( | |||||
| (Rectangle){centre.x, centre.y, clockFaceSize*.5f+2.0f, 4.0f}, | |||||
| (Vector2){ 2.0f, 2.0f }, | |||||
| currentAngles[digit][row*4+col].y, | |||||
| handsColor | |||||
| ); | |||||
| } | |||||
| } | |||||
| xOffset += (clockFaceSize+clockFaceSpacing) * 4; | |||||
| if (digit % 2 == 1) { | |||||
| DrawRing((Vector2){xOffset + 4.0f, 160.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor); | |||||
| DrawRing((Vector2){xOffset + 4.0f, 225.0f}, 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor); | |||||
| xOffset += sectionSpacing; | |||||
| } | |||||
| } | |||||
| DrawFPS(10, 10); | |||||
| EndDrawing(); | |||||
| //---------------------------------------------------------------------------------- | |||||
| } | |||||
| // De-Initialization | |||||
| //-------------------------------------------------------------------------------------- | |||||
| CloseWindow(); // Close window and OpenGL context | |||||
| //-------------------------------------------------------------------------------------- | |||||
| return 0; | |||||
| } | |||||