|
|
|
@ -0,0 +1,273 @@ |
|
|
|
/******************************************************************************************* |
|
|
|
* |
|
|
|
* 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 50 |
|
|
|
|
|
|
|
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'; |
|
|
|
strncpy(ls.production, "[X]++[X]++[X]++[X]++[X]", STR_MAX_SIZE); |
|
|
|
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) strnlen(ls->production, STR_MAX_SIZE); |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
turtleTop = -1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void BuildProductionStep(PenroseLSystem *ls) |
|
|
|
{ |
|
|
|
char *newProduction = (char*) malloc(sizeof(char) * STR_MAX_SIZE); |
|
|
|
newProduction[0] = '\0'; |
|
|
|
|
|
|
|
int productionLength = strnlen(ls->production, STR_MAX_SIZE); |
|
|
|
|
|
|
|
for (int i = 0; i < productionLength; i++) |
|
|
|
{ |
|
|
|
char step = ls->production[i]; |
|
|
|
int remainingSpace = STR_MAX_SIZE - strnlen(newProduction, STR_MAX_SIZE) - 1; |
|
|
|
switch (step) |
|
|
|
{ |
|
|
|
case 'W': strncat(newProduction, ls->ruleW, remainingSpace); break; |
|
|
|
case 'X': strncat(newProduction, ls->ruleX, remainingSpace); break; |
|
|
|
case 'Y': strncat(newProduction, ls->ruleY, remainingSpace); break; |
|
|
|
case 'Z': strncat(newProduction, ls->ruleZ, remainingSpace); break; |
|
|
|
default: |
|
|
|
{ |
|
|
|
if (step != 'F') |
|
|
|
{ |
|
|
|
int t = strnlen(newProduction, STR_MAX_SIZE); |
|
|
|
newProduction[t] = step; |
|
|
|
newProduction[t+1] = '\0'; |
|
|
|
} |
|
|
|
} break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ls->drawLength *= 0.5f; |
|
|
|
strncpy(ls->production, newProduction, STR_MAX_SIZE); |
|
|
|
free( newProduction ); |
|
|
|
} |
|
|
|
|
|
|
|
void BuildPenroseLSystem(PenroseLSystem *ls, float drawLength, int generations) |
|
|
|
{ |
|
|
|
*ls = CreatePenroseLSystem(drawLength); |
|
|
|
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; |
|
|
|
} |