Skip to content

Commit 3ba186f

Browse files
authored
[examples] Added: shapes_penrose_tile (#5376)
* new shapes example - penrose tile * stack cleanup * proper use of strnlen, strncat and strncpy * typo correction * update screenshot of shapes_penrose_tile example
1 parent 4d9df33 commit 3ba186f

File tree

2 files changed

+273
-0
lines changed

2 files changed

+273
-0
lines changed
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
/*******************************************************************************************
2+
*
3+
* raylib [shapes] example - penrose tile
4+
*
5+
* Example complexity rating: [★★★★] 4/4
6+
*
7+
* Example originally created with raylib 5.5
8+
* Based on: https://processing.org/examples/penrosetile.html
9+
*
10+
* Example contributed by David Buzatto (@davidbuzatto) and reviewed by Ramon Santamaria (@raysan5)
11+
*
12+
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
13+
* BSD-like license that allows static linking with closed source software
14+
*
15+
* Copyright (c) 2025 David Buzatto (@davidbuzatto)
16+
*
17+
********************************************************************************************/
18+
19+
#include <stdlib.h>
20+
#include <string.h>
21+
#include <math.h>
22+
#include "raylib.h"
23+
24+
#define STR_MAX_SIZE 10000
25+
#define TURTLE_STACK_MAX_SIZE 50
26+
27+
typedef struct TurtleState {
28+
Vector2 origin;
29+
double angle;
30+
} TurtleState;
31+
32+
typedef struct PenroseLSystem {
33+
int steps;
34+
char *production;
35+
const char *ruleW;
36+
const char *ruleX;
37+
const char *ruleY;
38+
const char *ruleZ;
39+
float drawLength;
40+
float theta;
41+
} PenroseLSystem;
42+
43+
static TurtleState turtleStack[TURTLE_STACK_MAX_SIZE];
44+
static int turtleTop = -1;
45+
46+
void PushTurtleState(TurtleState state)
47+
{
48+
if (turtleTop < TURTLE_STACK_MAX_SIZE - 1)
49+
{
50+
turtleStack[++turtleTop] = state;
51+
}
52+
else
53+
{
54+
TraceLog(LOG_WARNING, "TURTLE STACK OVERFLOW!");
55+
}
56+
}
57+
58+
TurtleState PopTurtleState(void)
59+
{
60+
if (turtleTop >= 0)
61+
{
62+
return turtleStack[turtleTop--];
63+
}
64+
else
65+
{
66+
TraceLog(LOG_WARNING, "TURTLE STACK UNDERFLOW!");
67+
}
68+
return (TurtleState) {0};
69+
}
70+
71+
PenroseLSystem CreatePenroseLSystem(float drawLength)
72+
{
73+
PenroseLSystem ls = {
74+
.steps = 0,
75+
.ruleW = "YF++ZF4-XF[-YF4-WF]++",
76+
.ruleX = "+YF--ZF[3-WF--XF]+",
77+
.ruleY = "-WF++XF[+++YF++ZF]-",
78+
.ruleZ = "--YF++++WF[+ZF++++XF]--XF",
79+
.drawLength = drawLength,
80+
.theta = 36.0f // in degrees
81+
};
82+
ls.production = (char*) malloc(sizeof(char) * STR_MAX_SIZE);
83+
ls.production[0] = '\0';
84+
strncpy(ls.production, "[X]++[X]++[X]++[X]++[X]", STR_MAX_SIZE);
85+
return ls;
86+
}
87+
88+
void DrawPenroseLSystem(PenroseLSystem *ls)
89+
{
90+
Vector2 screenCenter = {GetScreenWidth()/2, GetScreenHeight()/2};
91+
92+
TurtleState turtle = {
93+
.origin = {0},
94+
.angle = -90.0f
95+
};
96+
97+
int repeats = 1;
98+
int productionLength = (int) strnlen(ls->production, STR_MAX_SIZE);
99+
ls->steps += 12;
100+
101+
if (ls->steps > productionLength)
102+
{
103+
ls->steps = productionLength;
104+
}
105+
106+
for (int i = 0; i < ls->steps; i++)
107+
{
108+
char step = ls->production[i];
109+
if ( step == 'F' )
110+
{
111+
for ( int j = 0; j < repeats; j++ )
112+
{
113+
Vector2 startPosWorld = turtle.origin;
114+
float radAngle = DEG2RAD * turtle.angle;
115+
turtle.origin.x += ls->drawLength * cosf(radAngle);
116+
turtle.origin.y += ls->drawLength * sinf(radAngle);
117+
Vector2 startPosScreen = {startPosWorld.x + screenCenter.x, startPosWorld.y + screenCenter.y};
118+
Vector2 endPosScreen = {turtle.origin.x + screenCenter.x, turtle.origin.y + screenCenter.y};
119+
DrawLineEx(startPosScreen, endPosScreen, 2, Fade(BLACK, 0.2));
120+
}
121+
repeats = 1;
122+
}
123+
else if ( step == '+' )
124+
{
125+
for ( int j = 0; j < repeats; j++ )
126+
{
127+
turtle.angle += ls->theta;
128+
}
129+
repeats = 1;
130+
}
131+
else if ( step == '-' )
132+
{
133+
for ( int j = 0; j < repeats; j++ )
134+
{
135+
turtle.angle += -ls->theta;
136+
}
137+
repeats = 1;
138+
}
139+
else if ( step == '[' )
140+
{
141+
PushTurtleState(turtle);
142+
}
143+
else if ( step == ']' )
144+
{
145+
turtle = PopTurtleState();
146+
}
147+
else if ( ( step >= 48 ) && ( step <= 57 ) )
148+
{
149+
repeats = (int) step - 48;
150+
}
151+
}
152+
153+
turtleTop = -1;
154+
155+
}
156+
157+
void BuildProductionStep(PenroseLSystem *ls)
158+
{
159+
char *newProduction = (char*) malloc(sizeof(char) * STR_MAX_SIZE);
160+
newProduction[0] = '\0';
161+
162+
int productionLength = strnlen(ls->production, STR_MAX_SIZE);
163+
164+
for (int i = 0; i < productionLength; i++)
165+
{
166+
char step = ls->production[i];
167+
int remainingSpace = STR_MAX_SIZE - strnlen(newProduction, STR_MAX_SIZE) - 1;
168+
switch (step)
169+
{
170+
case 'W': strncat(newProduction, ls->ruleW, remainingSpace); break;
171+
case 'X': strncat(newProduction, ls->ruleX, remainingSpace); break;
172+
case 'Y': strncat(newProduction, ls->ruleY, remainingSpace); break;
173+
case 'Z': strncat(newProduction, ls->ruleZ, remainingSpace); break;
174+
default:
175+
{
176+
if (step != 'F')
177+
{
178+
int t = strnlen(newProduction, STR_MAX_SIZE);
179+
newProduction[t] = step;
180+
newProduction[t+1] = '\0';
181+
}
182+
} break;
183+
}
184+
}
185+
186+
ls->drawLength *= 0.5f;
187+
strncpy(ls->production, newProduction, STR_MAX_SIZE);
188+
free( newProduction );
189+
}
190+
191+
void BuildPenroseLSystem(PenroseLSystem *ls, float drawLength, int generations)
192+
{
193+
*ls = CreatePenroseLSystem(drawLength);
194+
for (int i = 0; i < generations; i++)
195+
{
196+
BuildProductionStep(ls);
197+
}
198+
}
199+
200+
//------------------------------------------------------------------------------------
201+
// Program main entry point
202+
//------------------------------------------------------------------------------------
203+
int main(void)
204+
{
205+
// Initialization
206+
//--------------------------------------------------------------------------------------
207+
const int screenWidth = 800;
208+
const int screenHeight = 450;
209+
210+
SetConfigFlags( FLAG_MSAA_4X_HINT );
211+
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - penrose tile");
212+
213+
float drawLength = 460.0f;
214+
int minGenerations = 0;
215+
int maxGenerations = 4;
216+
int generations = 0;
217+
218+
PenroseLSystem ls = {0};
219+
BuildPenroseLSystem(&ls, drawLength * (generations / (float) maxGenerations), generations);
220+
221+
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
222+
//---------------------------------------------------------------------------------------
223+
224+
// Main game loop
225+
while (!WindowShouldClose()) // Detect window close button or ESC key
226+
{
227+
// Update
228+
//----------------------------------------------------------------------------------
229+
bool rebuild = false;
230+
if (IsKeyPressed(KEY_UP))
231+
{
232+
if (generations < maxGenerations)
233+
{
234+
generations++;
235+
rebuild = true;
236+
}
237+
}
238+
else if (IsKeyPressed(KEY_DOWN))
239+
{
240+
if (generations > minGenerations)
241+
{
242+
generations--;
243+
rebuild = generations > 0;
244+
}
245+
}
246+
if (rebuild)
247+
{
248+
BuildPenroseLSystem(&ls, drawLength * (generations / (float) maxGenerations), generations);
249+
}
250+
//----------------------------------------------------------------------------------
251+
252+
// Draw
253+
//----------------------------------------------------------------------------------
254+
BeginDrawing();
255+
ClearBackground( RAYWHITE );
256+
if (generations > 0)
257+
{
258+
DrawPenroseLSystem(&ls);
259+
}
260+
DrawText("penrose l-system", 10, 10, 20, DARKGRAY);
261+
DrawText("press up or down to change generations", 10, 30, 20, DARKGRAY);
262+
DrawText(TextFormat("generations: %d", generations), 10, 50, 20, DARKGRAY);
263+
EndDrawing();
264+
//----------------------------------------------------------------------------------
265+
}
266+
267+
// De-Initialization
268+
//--------------------------------------------------------------------------------------
269+
CloseWindow(); // Close window and OpenGL context
270+
//--------------------------------------------------------------------------------------
271+
272+
return 0;
273+
}
24.9 KB
Loading

0 commit comments

Comments
 (0)