From a42eb2f79b807c9a7fb0d91228fc6b90b509af6e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 9 Sep 2025 17:32:13 +0200 Subject: [PATCH] REDESIGNED: example: `shapes_digital_clock` --- examples/shapes/shapes_digital_clock.c | 217 ++++++++++++++++++----- examples/shapes/shapes_digital_clock.png | Bin 3651 -> 18697 bytes 2 files changed, 174 insertions(+), 43 deletions(-) diff --git a/examples/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c index aad933be0..48e19e2be 100644 --- a/examples/shapes/shapes_digital_clock.c +++ b/examples/shapes/shapes_digital_clock.c @@ -11,7 +11,7 @@ * 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 Hamza RAHAL (@hmz-rhl) +* Copyright (c) 2025 Hamza RAHAL (@hmz-rhl) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -20,37 +20,40 @@ #include // Required for: cosf(), sinf() #include // Required for: time(), localtime() -#define DIGIT_SIZE 30 +#define CLOCK_ANALOG 0 +#define CLOCK_DIGITAL 1 //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { - MODE_NORMAL = 0, - MODE_HANDS_FREE, -} ClockMode; - +// Clock hand type typedef struct { - int value; - Vector2 origin; - float angle; - int length; - int thickness; - Color color; + int value; // Time value + + // Visual elements + float angle; // Hand angle + int length; // Hand length + int thickness; // Hand thickness + Color color; // Hand color } ClockHand; +// Clock hands typedef struct { - ClockMode mode; - ClockHand second; - ClockHand minute; - ClockHand hour; + ClockHand second; // Clock hand for seconds + ClockHand minute; // Clock hand for minutes + ClockHand hour; // Clock hand for hours } Clock; //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- static void UpdateClock(Clock *clock); // Update clock time -static void DrawClock(Clock clock, Vector2 centerPos); // Draw clock at desired position +static void DrawClockAnalog(Clock clock, Vector2 position); // Draw analog clock at desired center position +static void DrawClockDigital(Clock clock, Vector2 position); // Draw digital clock at desired position + +static void DrawDisplayValue(Vector2 position, int value, Color colorOn, Color colorOff); +static void Draw7SDisplay(Vector2 position, char segments, Color colorOn, Color colorOff); +static void DrawDisplaySegment(Vector2 center, int length, int thick, bool vertical, Color color); //------------------------------------------------------------------------------------ // Program main entry point @@ -62,16 +65,18 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; + SetConfigFlags(FLAG_MSAA_4X_HINT); InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock"); - // Initialize clock - Clock myClock = { - .mode = MODE_NORMAL, + int clockMode = CLOCK_DIGITAL; + // Initialize clock + // NOTE: Includes visual info for anlaog clock + Clock clock = { .second.angle = 45, .second.length = 140, .second.thickness = 3, - .second.color = BEIGE, + .second.color = MAROON, .minute.angle = 10, .minute.length = 130, @@ -94,11 +99,12 @@ int main(void) //---------------------------------------------------------------------------------- if (IsKeyPressed(KEY_SPACE)) { - if (myClock.mode == MODE_HANDS_FREE) myClock.mode = MODE_NORMAL; - else if (myClock.mode == MODE_NORMAL) myClock.mode = MODE_HANDS_FREE; + // Toggle clock mode + if (clockMode == CLOCK_DIGITAL) clockMode = CLOCK_ANALOG; + else if (clockMode == CLOCK_ANALOG) clockMode = CLOCK_DIGITAL; } - UpdateClock(&myClock); + UpdateClock(&clock); // Update clock required data: value and angle //---------------------------------------------------------------------------------- // Draw @@ -107,11 +113,21 @@ int main(void) ClearBackground(RAYWHITE); - DrawCircle(400, 225, 5, BLACK); // Clock center dot + // Draw clock in selected mode + if (clockMode == CLOCK_ANALOG) DrawClockAnalog(clock, (Vector2){ 400, 240 }); + else if (clockMode == CLOCK_DIGITAL) + { + DrawClockDigital(clock, (Vector2){ 30, 60 }); - DrawClock(myClock, (Vector2){ 400, 225 }); // Clock in selected mode + // Draw clock using default raylib font + // Get pointer to formated clock time string + // WARNING: Pointing to an internal static string that is reused between TextFormat() calls + const char *clockTime = TextFormat("%02i:%02i:%02i", clock.hour.value, clock.minute.value, clock.second.value); + DrawText(clockTime, GetScreenWidth()/2 - MeasureText(clockTime, 150)/2, 300, 150, BLACK); + } - DrawText("Press [SPACE] to switch clock mode", 10, 10, 20, DARKGRAY); + DrawText(TextFormat("Press [SPACE] to switch clock mode: %s", + (clockMode == CLOCK_DIGITAL)? "DIGITAL CLOCK" : "ANALOGUE CLOCK"), 10, 10, 20, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -154,31 +170,146 @@ static void UpdateClock(Clock *clock) clock->second.angle -= 90; } -// Draw clock -static void DrawClock(Clock clock, Vector2 centerPosition) +// Draw analog clock +// Parameter: position, refers to center position +static void DrawClockAnalog(Clock clock, Vector2 position) { - if (clock.mode == MODE_HANDS_FREE) - { - DrawCircleLinesV(centerPosition, clock.minute.length, LIGHTGRAY); + // Draw clock base + DrawCircleV(position, clock.second.length + 40, LIGHTGRAY); + DrawCircleV(position, 12, GRAY); - 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); + // Draw clock minutes/seconds lines + for (int i = 0; i < 60; i++) + { + DrawLineEx((Vector2){ position.x + (clock.second.length + ((i%5)? 10 : 6))*cosf((6.0f*i - 90.0f)*DEG2RAD), + position.y + (clock.second.length + ((i%5)? 10 : 6))*sinf((6.0f*i - 90.0f)*DEG2RAD) }, + (Vector2){ position.x + (clock.second.length + 20)*cosf((6.0f*i - 90.0f)*DEG2RAD), + position.y + (clock.second.length + 20)*sinf((6.0f*i - 90.0f)*DEG2RAD) }, ((i%5)? 1.0f : 3.0f), DARKGRAY); + + // Draw seconds numbers + //DrawText(TextFormat("%02i", i), centerPosition.x + (clock.second.length + 50)*cosf((6.0f*i - 90.0f)*DEG2RAD) - 10/2, + // centerPosition.y + (clock.second.length + 50)*sinf((6.0f*i - 90.0f)*DEG2RAD) - 10/2, 10, GRAY); + } - 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); - - 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); - } - else if (clock.mode == MODE_NORMAL) - { // Draw hand seconds - DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.second.length, clock.second.thickness }, + DrawRectanglePro((Rectangle){ position.x, position.y, clock.second.length, clock.second.thickness }, (Vector2){ 0.0f, clock.second.thickness/2.0f }, clock.second.angle, clock.second.color); // Draw hand minutes - DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.minute.length, clock.minute.thickness }, + DrawRectanglePro((Rectangle){ position.x, position.y, clock.minute.length, clock.minute.thickness }, (Vector2){ 0.0f, clock.minute.thickness/2.0f }, clock.minute.angle, clock.minute.color); // Draw hand hours - DrawRectanglePro((Rectangle){ centerPosition.x, centerPosition.y, clock.hour.length, clock.hour.thickness }, + DrawRectanglePro((Rectangle){ position.x, position.y, clock.hour.length, clock.hour.thickness }, (Vector2){ 0.0f, clock.hour.thickness/2.0f }, clock.hour.angle, clock.hour.color); +} + +// Draw digital clock +// PARAM: position, refers to top-left corner +static void DrawClockDigital(Clock clock, Vector2 position) +{ + // Draw clock using custom 7-segments display (made of shapes) + DrawDisplayValue((Vector2){ position.x, position.y }, clock.hour.value/10, RED, Fade(LIGHTGRAY, 0.3f)); + DrawDisplayValue((Vector2){ position.x + 120, position.y }, clock.hour.value%10, RED, Fade(LIGHTGRAY, 0.3f)); + + DrawCircle(position.x + 240, position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f)); + DrawCircle(position.x + 240, position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f)); + + DrawDisplayValue((Vector2){ position.x + 260, position.y }, clock.minute.value/10, RED, Fade(LIGHTGRAY, 0.3f)); + DrawDisplayValue((Vector2){ position.x + 380, position.y }, clock.minute.value%10, RED, Fade(LIGHTGRAY, 0.3f)); + + DrawCircle(position.x + 500, position.y + 70, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f)); + DrawCircle(position.x + 500, position.y + 150, 12, (clock.second.value%2)? RED : Fade(LIGHTGRAY, 0.3f)); + + DrawDisplayValue((Vector2){ position.x + 520, position.y }, clock.second.value/10, RED, Fade(LIGHTGRAY, 0.3f)); + DrawDisplayValue((Vector2){ position.x + 640, position.y }, clock.second.value%10, RED, Fade(LIGHTGRAY, 0.3f)); +} + +// Draw 7-segment display with value +static void DrawDisplayValue(Vector2 position, int value, Color colorOn, Color colorOff) +{ + switch (value) + { + case 0: Draw7SDisplay(position, 0b00111111, colorOn, colorOff); break; + case 1: Draw7SDisplay(position, 0b00000110, colorOn, colorOff); break; + case 2: Draw7SDisplay(position, 0b01011011, colorOn, colorOff); break; + case 3: Draw7SDisplay(position, 0b01001111, colorOn, colorOff); break; + case 4: Draw7SDisplay(position, 0b01100110, colorOn, colorOff); break; + case 5: Draw7SDisplay(position, 0b01101101, colorOn, colorOff); break; + case 6: Draw7SDisplay(position, 0b01111101, colorOn, colorOff); break; + case 7: Draw7SDisplay(position, 0b00000111, colorOn, colorOff); break; + case 8: Draw7SDisplay(position, 0b01111111, colorOn, colorOff); break; + case 9: Draw7SDisplay(position, 0b01101111, colorOn, colorOff); break; + default: break; + } +} + +// Draw seven segments display +// Parameter: position, refers to top-left corner of display +// Parameter: segments, defines in binary the segments to be activated +static void Draw7SDisplay(Vector2 position, char segments, Color colorOn, Color colorOff) +{ + int segmentLen = 60; + int segmentThick = 20; + float offsetYAdjust = segmentThick*0.3f; // HACK: Adjust gap space between segment limits + + // Segment A + DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + segmentThick }, + segmentLen, segmentThick, false, (segments & 0b00000001)? colorOn : colorOff); + // Segment B + DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust }, + segmentLen, segmentThick, true, (segments & 0b00000010)? colorOn : colorOff); + // Segment C + DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust }, + segmentLen, segmentThick, true, (segments & 0b00000100)? colorOn : colorOff); + // Segment D + DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 5*segmentThick + 2*segmentLen - 4*offsetYAdjust }, + segmentLen, segmentThick, false, (segments & 0b00001000)? colorOn : colorOff); + // Segment E + DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 4*segmentThick + segmentLen + segmentLen/2.0f - 3*offsetYAdjust }, + segmentLen, segmentThick, true, (segments & 0b00010000)? colorOn : colorOff); + // Segment F + DrawDisplaySegment((Vector2){ position.x + segmentThick/2.0f, position.y + 2*segmentThick + segmentLen/2.0f - offsetYAdjust }, + segmentLen, segmentThick, true, (segments & 0b00100000)? colorOn : colorOff); + // Segment G + DrawDisplaySegment((Vector2){ position.x + segmentThick + segmentLen/2.0f, position.y + 3*segmentThick + segmentLen - 2*offsetYAdjust }, + segmentLen, segmentThick, false, (segments & 0b01000000)? colorOn : colorOff); +} + +// Draw one 7-segment display segment, horizontal or vertical +static void DrawDisplaySegment(Vector2 center, int length, int thick, bool vertical, Color color) +{ + if (!vertical) + { + // Horizontal segment points + // 3___________________________5 + // / \ + // /1 x 6\ + // \ / + // \2___________________________4/ + Vector2 segmentPointsH[6] = { + (Vector2){ center.x - length/2.0f - thick/2.0f, center.y }, // Point 1 + (Vector2){ center.x - length/2.0f, center.y + thick/2.0f }, // Point 2 + (Vector2){ center.x - length/2.0f, center.y - thick/2.0f }, // Point 3 + (Vector2){ center.x + length/2.0f, center.y + thick/2.0f }, // Point 4 + (Vector2){ center.x + length/2.0f, center.y - thick/2.0f }, // Point 5 + (Vector2){ center.x + length/2.0f + thick/2.0f, center.y }, // Point 6 + }; + + DrawTriangleStrip(segmentPointsH, 6, color); + } + else + { + // Vertical segment points + Vector2 segmentPointsV[6] = { + (Vector2){ center.x, center.y - length/2.0f - thick/2.0f }, // Point 1 + (Vector2){ center.x - thick/2.0f, center.y - length/2.0f }, // Point 2 + (Vector2){ center.x + thick/2.0f, center.y - length/2.0f }, // Point 3 + (Vector2){ center.x - thick/2.0f, center.y + length/2.0f }, // Point 4 + (Vector2){ center.x + thick/2.0f, center.y + length/2.0f }, // Point 5 + (Vector2){ center.x, center.y + length/2 + thick/2.0f }, // Point 6 + }; + + DrawTriangleStrip(segmentPointsV, 6, color); } } diff --git a/examples/shapes/shapes_digital_clock.png b/examples/shapes/shapes_digital_clock.png index 22939253176be52b334d781d99a2e7c23a750ddd..35ac56b93ba45b3d2b725c19f345a48305c50bce 100644 GIT binary patch literal 18697 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 literal 3651 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 A#sB~S