Update: Added rotation and reset control.
Having some fun with Raylib. This runs in fullscreen windowed mode and utilizes color lerping to smoothly transition colors. Two configuration constants, MAXDEPTH and LERPSPEED, are at the top. See notes below for recomendations.
- Press ESC to quit. If you don’t follow the MAXDEPTH recomendations below, ESC may not register due to GPU compute time.
- Press R to togle rotation.
- Press I to rest triangle to upright position.
MAXDEPTH
- Compute time is O(3^MAXDEPTH), so the amount of work your GPU will do grows very fast.
- A good value is between 6-8. After about 10 the triangles are so small, your eye can no longer see them.
- Be carefull when changing this value. Recomend incrementing it 1 at a time. After a certain point your GPU will no longer keep up. If you see FPS fluctuate below 60, then you’ve likely reached the max optimum depth for your setup.
- FPS target is set to 60. On my laptop rig, I can get MAXDEPTH at 10 before I see fps fluctuate between 59-60.
LERPSPEED
- Lower the number, the slower the color change.
- Range 0.N - 1.0. Recomend 0.1 or 0.2
package sierpinski
import "core:math"
import rl "vendor:raylib"
MAXDEPTH :: 6 // do not recommend going past 8 - compute time is O(3^MAXDEPTH)
LERPSPEED :: 0.08 // ColorLerp speed - adjust as needed
ROTATEANGLE :: 0.25 // In DEG - lower number == slower rotation speed
triangle: tVector
tVector :: struct {
v1: rl.Vector2,
v2: rl.Vector2,
v3: rl.Vector2,
}
render_size: rl.Vector2
start_color: rl.Color
end_color: rl.Color
lerp_time: f32
rotate: bool
main :: proc() { using rl
init_screen()
init_triangle()
defer CloseWindow()
for !WindowShouldClose() {
if IsKeyPressed(.I) { init_triangle() } // re-init triangle
if IsKeyPressed(.R) { rotate = !rotate } // toggle rotation
color := lerp_color()
BeginDrawing()
ClearBackground(BLACK)
update_text(color)
sierpinsky(triangle, 0, color) //recurse to MAXDEPTH
EndDrawing()
triangle_rotate()
}
}
init_screen :: proc() { using rl
SetConfigFlags({.WINDOW_UNDECORATED, .BORDERLESS_WINDOWED_MODE, .WINDOW_MAXIMIZED})
InitWindow(0, 0, "Sierpinski")
SetTargetFPS(60)
render_size = {f32(GetRenderWidth()), f32(GetRenderHeight())}
}
// h = ( s * sqrt(3) ) / 2 || s = ( 2 * h ) / sqrt(3) -- 2 from top and bottom
init_triangle :: proc() { using rl
triangle = {
{(render_size.x / 2), 2}, //v1
{(render_size.x / 2) - ((render_size.y - 4) / math.sqrt(f32(3))), render_size.y - 2}, //v2
{(render_size.x / 2) + ((render_size.y - 4) / math.sqrt(f32(3))), render_size.y - 2} //v3
}
start_color = Color{ u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), 255 }
end_color = Color{ u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), 255 }
}
lerp_color :: proc() -> rl.Color { using rl
lerp_time += GetFrameTime() * LERPSPEED
if lerp_time > 1.0 { // Clamp time
lerp_time = 0.0
start_color = end_color
end_color = Color{ u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), u8(GetRandomValue(0,255)), 255 }
}
return ColorLerp(start_color, end_color, lerp_time)
}
update_text :: proc(color: rl.Color) { using rl
if rotate {DrawText(TextFormat("ROTATE ANGLE: %f°", ROTATEANGLE), 20, 20, 50, GRAY)}
else {DrawText(TextFormat("ROTATE ANGLE: OFF"), 20, 20, 50, GRAY)}
DrawText(TextFormat("LERP SPEED: %f", LERPSPEED), 20, 75, 50, GRAY)
DrawText(TextFormat("DEPTH: %i", MAXDEPTH), 20, 130, 50, GRAY)
DrawText(TextFormat("FPS: %i", GetFPS()), 20, 185, 50, GRAY)
DrawText(TextFormat("R: %03i", color.r), 20, 240, 50, GRAY)
DrawText(TextFormat("G: %03i", color.g), 20, 295, 50, GRAY)
DrawText(TextFormat("B: %03i", color.b), 20, 350, 50, GRAY)
}
sierpinsky :: proc(t: tVector, depth: i32, color: rl.Color) { using rl
DrawTriangleLines(t.v1, t.v2, t.v3, color)
if depth + 1 <= MAXDEPTH && !IsKeyPressed(.ESCAPE) {
sierpinsky({t.v1, (t.v1 + t.v2) / 2, (t.v1 + t.v3) / 2}, depth + 1, color) //top
sierpinsky({(t.v1 + t.v2) / 2, t.v2, (t.v2 + t.v3) / 2}, depth + 1, color) //left
sierpinsky({(t.v1 + t.v3) / 2, (t.v2 + t.v3) / 2, t.v3}, depth + 1, color) //right
}
}
triangle_rotate :: proc() { using rl
if rotate {
center := (triangle.v1 + triangle.v2 + triangle.v3) / 3
triangle = {
Vector2Rotate(triangle.v1 - center, ROTATEANGLE * DEG2RAD) + center, //v1
Vector2Rotate(triangle.v2 - center, ROTATEANGLE * DEG2RAD) + center, //v2
Vector2Rotate(triangle.v3 - center, ROTATEANGLE * DEG2RAD) + center //v3
}
}
}