| @ -0,0 +1,270 @@ | |||
| /******************************************************************************************* | |||
| * | |||
| * raylib [shapes] example - penrose tile | |||
| * | |||
| * Example complexity rating: [★★★★] 4/4 | |||
| * | |||
| * Example originally created with raylib 5.5 | |||
| * Based on: https://processing.org/examples/penrosetile.html | |||
| * | |||
| * Example contributed by David Buzatto (@davidbuzatto) 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 David Buzatto (@davidbuzatto) | |||
| * | |||
| ********************************************************************************************/ | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <math.h> | |||
| #include "raylib.h" | |||
| #define STR_MAX_SIZE 10000 | |||
| #define TURTLE_STACK_MAX_SIZE 2500 | |||
| typedef struct TurtleState { | |||
| Vector2 origin; | |||
| double angle; | |||
| } TurtleState; | |||
| typedef struct PenroseLSystem { | |||
| int steps; | |||
| char *production; | |||
| const char *ruleW; | |||
| const char *ruleX; | |||
| const char *ruleY; | |||
| const char *ruleZ; | |||
| float drawLength; | |||
| float theta; | |||
| } PenroseLSystem; | |||
| static TurtleState turtleStack[TURTLE_STACK_MAX_SIZE]; | |||
| static int turtleTop = -1; | |||
| void PushTurtleState(TurtleState state) | |||
| { | |||
| if (turtleTop < TURTLE_STACK_MAX_SIZE - 1) | |||
| { | |||
| turtleStack[++turtleTop] = state; | |||
| } | |||
| else | |||
| { | |||
| TraceLog(LOG_WARNING, "TURTLE STACK OVERFLOW!"); | |||
| } | |||
| } | |||
| TurtleState PopTurtleState(void) | |||
| { | |||
| if (turtleTop >= 0) | |||
| { | |||
| return turtleStack[turtleTop--]; | |||
| } | |||
| else | |||
| { | |||
| TraceLog(LOG_WARNING, "TURTLE STACK UNDERFLOW!"); | |||
| } | |||
| return (TurtleState) {0}; | |||
| } | |||
| PenroseLSystem CreatePenroseLSystem(float drawLength) | |||
| { | |||
| PenroseLSystem ls = { | |||
| .steps = 0, | |||
| .ruleW = "YF++ZF4-XF[-YF4-WF]++", | |||
| .ruleX = "+YF--ZF[3-WF--XF]+", | |||
| .ruleY = "-WF++XF[+++YF++ZF]-", | |||
| .ruleZ = "--YF++++WF[+ZF++++XF]--XF", | |||
| .drawLength = drawLength, | |||
| .theta = 36.0f // in degrees | |||
| }; | |||
| ls.production = (char*) malloc(sizeof(char) * STR_MAX_SIZE); | |||
| ls.production[0] = '\0'; | |||
| strcpy(ls.production, "[X]++[X]++[X]++[X]++[X]"); | |||
| return ls; | |||
| } | |||
| void DrawPenroseLSystem(PenroseLSystem *ls) | |||
| { | |||
| Vector2 screenCenter = {GetScreenWidth()/2, GetScreenHeight()/2}; | |||
| TurtleState turtle = { | |||
| .origin = {0}, | |||
| .angle = -90.0f | |||
| }; | |||
| int repeats = 1; | |||
| int productionLength = (int) strlen(ls->production); | |||
| ls->steps += 12; | |||
| if (ls->steps > productionLength) | |||
| { | |||
| ls->steps = productionLength; | |||
| } | |||
| for (int i = 0; i < ls->steps; i++) | |||
| { | |||
| char step = ls->production[i]; | |||
| if ( step == 'F' ) | |||
| { | |||
| for ( int j = 0; j < repeats; j++ ) | |||
| { | |||
| Vector2 startPosWorld = turtle.origin; | |||
| float radAngle = DEG2RAD * turtle.angle; | |||
| turtle.origin.x += ls->drawLength * cosf(radAngle); | |||
| turtle.origin.y += ls->drawLength * sinf(radAngle); | |||
| Vector2 startPosScreen = {startPosWorld.x + screenCenter.x, startPosWorld.y + screenCenter.y}; | |||
| Vector2 endPosScreen = {turtle.origin.x + screenCenter.x, turtle.origin.y + screenCenter.y}; | |||
| DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2)); | |||
| } | |||
| repeats = 1; | |||
| } | |||
| else if ( step == '+' ) | |||
| { | |||
| for ( int j = 0; j < repeats; j++ ) | |||
| { | |||
| turtle.angle += ls->theta; | |||
| } | |||
| repeats = 1; | |||
| } | |||
| else if ( step == '-' ) | |||
| { | |||
| for ( int j = 0; j < repeats; j++ ) | |||
| { | |||
| turtle.angle += -ls->theta; | |||
| } | |||
| repeats = 1; | |||
| } | |||
| else if ( step == '[' ) | |||
| { | |||
| PushTurtleState(turtle); | |||
| } | |||
| else if ( step == ']' ) | |||
| { | |||
| turtle = PopTurtleState(); | |||
| } | |||
| else if ( ( step >= 48 ) && ( step <= 57 ) ) | |||
| { | |||
| repeats = (int) step - 48; | |||
| } | |||
| } | |||
| } | |||
| void BuildProductionStep(PenroseLSystem *ls) | |||
| { | |||
| char *newProduction = (char*) malloc(sizeof(char) * STR_MAX_SIZE); | |||
| newProduction[0] = '\0'; | |||
| int productionLength = strlen(ls->production); | |||
| for (int i = 0; i < productionLength; i++) | |||
| { | |||
| char step = ls->production[i]; | |||
| switch (step) | |||
| { | |||
| case 'W': strcat(newProduction, ls->ruleW); break; | |||
| case 'X': strcat(newProduction, ls->ruleX); break; | |||
| case 'Y': strcat(newProduction, ls->ruleY); break; | |||
| case 'Z': strcat(newProduction, ls->ruleZ); break; | |||
| default: | |||
| { | |||
| if (step != 'F') | |||
| { | |||
| int t = strlen(newProduction); | |||
| newProduction[t] = step; | |||
| newProduction[t+1] = '\0'; | |||
| } | |||
| } break; | |||
| } | |||
| } | |||
| ls->drawLength *= 0.5f; | |||
| strcpy( ls->production, newProduction ); | |||
| free( newProduction ); | |||
| } | |||
| void BuildPenroseLSystem(PenroseLSystem *ls, float drawLength, int generations) | |||
| { | |||
| *ls = CreatePenroseLSystem(drawLength); | |||
| turtleTop = -1; | |||
| for (int i = 0; i < generations; i++) | |||
| { | |||
| BuildProductionStep(ls); | |||
| } | |||
| } | |||
| //------------------------------------------------------------------------------------ | |||
| // 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 - penrose tile"); | |||
| float drawLength = 460.0f; | |||
| int minGenerations = 0; | |||
| int maxGenerations = 4; | |||
| int generations = 0; | |||
| PenroseLSystem ls = {0}; | |||
| BuildPenroseLSystem(&ls, drawLength * (generations / (float) maxGenerations), generations); | |||
| 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 | |||
| //---------------------------------------------------------------------------------- | |||
| bool rebuild = false; | |||
| if (IsKeyPressed(KEY_UP)) | |||
| { | |||
| if (generations < maxGenerations) | |||
| { | |||
| generations++; | |||
| rebuild = true; | |||
| } | |||
| } | |||
| else if (IsKeyPressed(KEY_DOWN)) | |||
| { | |||
| if (generations > minGenerations) | |||
| { | |||
| generations--; | |||
| rebuild = generations > 0; | |||
| } | |||
| } | |||
| if (rebuild) | |||
| { | |||
| BuildPenroseLSystem(&ls, drawLength * (generations / (float) maxGenerations), generations); | |||
| } | |||
| //---------------------------------------------------------------------------------- | |||
| // Draw | |||
| //---------------------------------------------------------------------------------- | |||
| BeginDrawing(); | |||
| ClearBackground( RAYWHITE ); | |||
| if (generations > 0) | |||
| { | |||
| DrawPenroseLSystem(&ls); | |||
| } | |||
| DrawText("penrose l-system", 10, 10, 20, DARKGRAY); | |||
| DrawText("press up or down to change generations", 10, 30, 20, DARKGRAY); | |||
| DrawText(TextFormat("generations: %d", generations), 10, 50, 20, DARKGRAY); | |||
| EndDrawing(); | |||
| //---------------------------------------------------------------------------------- | |||
| } | |||
| // De-Initialization | |||
| //-------------------------------------------------------------------------------------- | |||
| CloseWindow(); // Close window and OpenGL context | |||
| //-------------------------------------------------------------------------------------- | |||
| return 0; | |||
| } | |||