From 6dfaf9fe7edfee08eac7bfabcc3dabcc5147530d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 Dec 2025 21:19:54 +0100 Subject: [PATCH] REVIEWED: example `shapes_hilbert_curve` #5454 Make it more didactic and dynamic, avoid global variables --- examples/shapes/shapes_hilbert_curve.c | 233 ++++++++++++----------- examples/shapes/shapes_hilbert_curve.png | Bin 15288 -> 16975 bytes 2 files changed, 121 insertions(+), 112 deletions(-) diff --git a/examples/shapes/shapes_hilbert_curve.c b/examples/shapes/shapes_hilbert_curve.c index 1af263b34..8ea03c7c5 100644 --- a/examples/shapes/shapes_hilbert_curve.c +++ b/examples/shapes/shapes_hilbert_curve.c @@ -6,7 +6,7 @@ * * Example originally created with raylib 5.6, last time updated with raylib 5.6 * -* Example contributed by Hamza RAHAL (@hmz-rhl) +* Example contributed by Hamza RAHAL (@hmz-rhl) 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 @@ -17,48 +17,18 @@ #include "raylib.h" -#include "raymath.h" -#include -#include -const int screenWidth = 800; +#define RAYGUI_IMPLEMENTATION +#include "raygui.h" -const int screenHeight = 450; - -int order = 2; - -int total; - -int counter = 0; - -Vector2 *hilbertPath = 0; - -const Vector2 hilbertPoints[4] = -{ - [0] = { - .x = 0, - .y = 0 - }, - [1] = { - .x = 0, - .y = 1 - }, - [2] = { - .x = 1, - .y = 1 - }, - [3] = { - .x = 1, - .y = 0 - }, -}; +#include // Required for: calloc(), free() //------------------------------------------------------------------------------------ // Module Functions Declaration //------------------------------------------------------------------------------------ -Vector2 Hilbert(int index); - -void InitHilbertPath(void); +static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount); +static void UnloadHilbertPath(Vector2 *hilbertPath); +static Vector2 ComputeHilbertStep(int order, int index); //------------------------------------------------------------------------------------ // Program main entry point @@ -67,13 +37,23 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve example"); + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve"); - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - - InitHilbertPath(); + int order = 2; + float size = GetScreenHeight(); + int strokeCount = 0; + Vector2 *hilbertPath = LoadHilbertPath(order, size, &strokeCount); + int prevOrder = order; + int prevSize = (int)size; // NOTE: Size from slider is float but for comparison we use int + int counter = 0; + float thick = 2.0f; + bool animate = true; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- // Main game loop @@ -82,34 +62,52 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - if ((IsKeyPressed(KEY_UP)) && (order < 8)) + // Check if order or size have changed to regenerate + // NOTE: Size from slider is float but for comparison we use int + if ((prevOrder != order) || (prevSize != (int)size)) { - counter = 0; - ++order; - InitHilbertPath(); - } - else if((IsKeyPressed(KEY_DOWN)) && (order > 1)) - { - counter = 0; - --order; - InitHilbertPath(); + UnloadHilbertPath(hilbertPath); + hilbertPath = LoadHilbertPath(order, size, &strokeCount); + + if (animate) counter = 0; + else counter = strokeCount; + + prevOrder = order; + prevSize = size; } //---------------------------------------------------------------------------------- // Draw //-------------------------------------------------------------------------- BeginDrawing(); - DrawText(TextFormat("(press UP or DOWN to change)\norder : %d", order), screenWidth/2 + 70, 25, 20, WHITE); - if(counter < total) - { - ClearBackground(BLACK); - for (int i = 1; i <= counter; i++) + ClearBackground(RAYWHITE); + + if (counter < strokeCount) { - DrawLineV(hilbertPath[i], hilbertPath[i-1], ColorFromHSV(((float)i / total) * 360.0f, 1.0f, 1.0f)); + // Draw Hilbert path animation, one stroke every frame + for (int i = 1; i <= counter; i++) + { + DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); + } + + counter += 1; } - counter += 1; - } + else + { + // Draw full Hilbert path + for (int i = 1; i < strokeCount; i++) + { + DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i/strokeCount)*360.0f, 1.0f, 1.0f)); + } + } + + // Draw UI using raygui + GuiCheckBox((Rectangle){ 450, 50, 20, 20 }, "ANIMATE GENERATION ON CHANGE", &animate); + GuiSpinner((Rectangle){ 585, 100, 180, 30 }, "HILBERT CURVE ORDER: ", &order, 2, 8, false); + GuiSlider((Rectangle){ 524, 150, 240, 24 }, "THICKNESS: ", NULL, &thick, 1.0f, 10.0f); + GuiSlider((Rectangle){ 524, 190, 240, 24 }, "TOTAL SIZE: ", NULL, &size, 10.0f, GetScreenHeight()*1.5f); + EndDrawing(); //-------------------------------------------------------------------------- } @@ -117,8 +115,9 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- + UnloadHilbertPath(hilbertPath); + CloseWindow(); // Close window and OpenGL context - MemFree(hilbertPath); //-------------------------------------------------------------------------------------- return 0; } @@ -126,62 +125,72 @@ int main(void) //------------------------------------------------------------------------------------ // Module Functions Definition //------------------------------------------------------------------------------------ - -// calculate U positions -Vector2 Hilbert(int index) +// Load the whole Hilbert Path (including each U and their link) +static Vector2 *LoadHilbertPath(int order, float size, int *strokeCount) { + int N = 1 << order; + float len = size/N; + *strokeCount = N*N; - int hiblertIndex = index&3; - Vector2 vect = hilbertPoints[hiblertIndex]; - float temp; - int len; - - for (int j = 1; j < order; j++) + Vector2 *hilbertPath = (Vector2 *)RL_CALLOC(*strokeCount, sizeof(Vector2)); + + for (int i = 0; i < *strokeCount; i++) { - index = index>>2; - hiblertIndex = index&3; - len = 1<> 2; + hilbertIndex = index&3; + len = 1 << j; + + switch (hilbertIndex) + { + case 0: + { + temp = vect.x; + vect.x = vect.y; + vect.y = temp; + } break; + case 2: vect.x += len; + case 1: vect.y += len; break; + case 3: + { + temp = len - 1 - vect.x; + vect.x = 2*len - 1 - vect.y; + vect.y = temp; + } break; + default: break; + } + } + + return vect; } diff --git a/examples/shapes/shapes_hilbert_curve.png b/examples/shapes/shapes_hilbert_curve.png index cbb3d0753b154bac0cc160d140ab5c27e59dbddb..af99cbc493115d0de96f50ce8391f5f6aae85028 100644 GIT binary patch literal 16975 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