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); - - 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); + // 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.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 229392531..35ac56b93 100644 Binary files a/examples/shapes/shapes_digital_clock.png and b/examples/shapes/shapes_digital_clock.png differ