228 lines
8.4 KiB
C#
228 lines
8.4 KiB
C#
/*******************************************************************************************
|
|
*
|
|
* raylib [shapes] example - recursive tree
|
|
*
|
|
* Example complexity rating: [★★★☆] 3/4
|
|
*
|
|
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
|
*
|
|
* Example contributed by Jopestpe (@jopestpe)
|
|
*
|
|
* 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 Jopestpe (@jopestpe)
|
|
*
|
|
********************************************************************************************/
|
|
|
|
namespace Examples.Shapes;
|
|
|
|
public partial class RecursiveTree : IExample
|
|
{
|
|
private const int screenWidth = 800;
|
|
private const int screenHeight = 450;
|
|
|
|
public string Name => "Shapes / Recursive Tree";
|
|
|
|
public string Title => "raylib [shapes] example - recursive tree";
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Types and Structures Definition
|
|
//----------------------------------------------------------------------------------
|
|
private struct Branch
|
|
{
|
|
public Vector2 start;
|
|
public Vector2 end;
|
|
public float angle;
|
|
public float length;
|
|
}
|
|
|
|
private Vector2 start;
|
|
private float angle;
|
|
private float thick;
|
|
private float treeDepth;
|
|
private float branchDecay;
|
|
private float length;
|
|
private bool bezier;
|
|
|
|
private Branch[] branches;
|
|
|
|
public void Init()
|
|
{
|
|
start = new Vector2((screenWidth / 2.0f) - 125.0f, (float)screenHeight);
|
|
angle = 40.0f;
|
|
thick = 1.0f;
|
|
treeDepth = 10.0f;
|
|
branchDecay = 0.66f;
|
|
length = 120.0f;
|
|
bezier = false;
|
|
|
|
branches = new Branch[1030];
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
// Update
|
|
//----------------------------------------------------------------------------------
|
|
float theta = angle * DEG2RAD;
|
|
int maxBranches = (int)(MathF.Pow(2, MathF.Floor(treeDepth)));
|
|
int count = 0;
|
|
|
|
Vector2 initialEnd = new Vector2(start.X + length * MathF.Sin(0.0f), start.Y - length * MathF.Cos(0.0f));
|
|
branches[count++] = new Branch { start = start, end = initialEnd, angle = 0.0f, length = length };
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Branch branch = branches[i];
|
|
if (branch.length < 2)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
float nextLength = branch.length * branchDecay;
|
|
|
|
if (count < maxBranches && nextLength >= 2)
|
|
{
|
|
Vector2 branchStart = branch.end;
|
|
|
|
float angle1 = branch.angle + theta;
|
|
Vector2 branchEnd1 = new Vector2(branchStart.X + nextLength * MathF.Sin(angle1), branchStart.Y - nextLength * MathF.Cos(angle1));
|
|
branches[count++] = new Branch { start = branchStart, end = branchEnd1, angle = angle1, length = nextLength };
|
|
|
|
float angle2 = branch.angle - theta;
|
|
Vector2 branchEnd2 = new Vector2(branchStart.X + nextLength * MathF.Sin(angle2), branchStart.Y - nextLength * MathF.Cos(angle2));
|
|
branches[count++] = new Branch { start = branchStart, end = branchEnd2, angle = angle2, length = nextLength };
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------------
|
|
|
|
// Draw
|
|
//----------------------------------------------------------------------------------
|
|
BeginDrawing();
|
|
|
|
ClearBackground(Color.RayWhite);
|
|
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Branch branch = branches[i];
|
|
if (branch.length >= 2)
|
|
{
|
|
if (bezier)
|
|
{
|
|
DrawLineBezier(branch.start, branch.end, thick, Color.Red);
|
|
}
|
|
else
|
|
{
|
|
DrawLineEx(branch.start, branch.end, thick, Color.Red);
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawLine(580, 0, 580, GetScreenHeight(), new Color(218, 218, 218, 255));
|
|
DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), new Color(232, 232, 232, 255));
|
|
|
|
// Draw GUI controls (minimal raygui-like controls, raygui is not bound in raylib-cs)
|
|
//------------------------------------------------------------------------------
|
|
GuiSliderBar(new Rectangle(640, 40, 120, 20), "Angle", $"{angle:F0}", ref angle, 0, 180);
|
|
GuiSliderBar(new Rectangle(640, 70, 120, 20), "Length", $"{length:F0}", ref length, 12.0f, 240.0f);
|
|
GuiSliderBar(new Rectangle(640, 100, 120, 20), "Decay", $"{branchDecay:F2}", ref branchDecay, 0.1f, 0.78f);
|
|
GuiSliderBar(new Rectangle(640, 130, 120, 20), "Depth", $"{treeDepth:F0}", ref treeDepth, 1.0f, 10.0f);
|
|
GuiSliderBar(new Rectangle(640, 160, 120, 20), "Thick", $"{thick:F0}", ref thick, 1, 8);
|
|
GuiCheckBox(new Rectangle(640, 190, 20, 20), "Bezier", ref bezier);
|
|
//------------------------------------------------------------------------------
|
|
|
|
DrawFPS(10, 10);
|
|
|
|
EndDrawing();
|
|
//----------------------------------------------------------------------------------
|
|
}
|
|
|
|
public void Unload()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// Minimal raygui-like widgets (plain raylib re-implementation)
|
|
//----------------------------------------------------------------------------------
|
|
private static void GuiSliderBar(Rectangle bounds, string textLeft, string textRight, ref float value, float minValue, float maxValue)
|
|
{
|
|
Vector2 mouse = GetMousePosition();
|
|
bool hover = CheckCollisionPointRec(mouse, bounds);
|
|
if (hover && IsMouseButtonDown(MouseButton.Left))
|
|
{
|
|
value = minValue + ((mouse.X - bounds.X) / bounds.Width) * (maxValue - minValue);
|
|
if (value < minValue)
|
|
{
|
|
value = minValue;
|
|
}
|
|
|
|
if (value > maxValue)
|
|
{
|
|
value = maxValue;
|
|
}
|
|
}
|
|
|
|
DrawRectangleRec(bounds, Color.LightGray);
|
|
float pct = (value - minValue) / (maxValue - minValue);
|
|
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), Color.SkyBlue);
|
|
DrawRectangleLinesEx(bounds, 1, Color.Gray);
|
|
if (textLeft != null)
|
|
{
|
|
DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
|
}
|
|
|
|
if (textRight != null)
|
|
{
|
|
DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
|
}
|
|
}
|
|
|
|
private static void GuiCheckBox(Rectangle bounds, string text, ref bool active)
|
|
{
|
|
Vector2 mouse = GetMousePosition();
|
|
bool hover = CheckCollisionPointRec(mouse, bounds);
|
|
if (hover && IsMouseButtonPressed(MouseButton.Left))
|
|
{
|
|
active = !active;
|
|
}
|
|
|
|
DrawRectangleLinesEx(bounds, 1, hover ? Color.DarkBlue : Color.Gray);
|
|
if (active)
|
|
{
|
|
DrawRectangle((int)bounds.X + 4, (int)bounds.Y + 4, (int)bounds.Width - 8, (int)bounds.Height - 8, Color.DarkGray);
|
|
}
|
|
|
|
if (text != null)
|
|
{
|
|
DrawText(text, (int)(bounds.X + bounds.Width + 8), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
|
}
|
|
}
|
|
|
|
public static int Main()
|
|
{
|
|
// Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - recursive tree");
|
|
|
|
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
var game = new RecursiveTree();
|
|
game.Init();
|
|
|
|
// Main game loop
|
|
while (!WindowShouldClose()) // Detect window close button or ESC key
|
|
{
|
|
game.Update();
|
|
}
|
|
|
|
game.Unload();
|
|
|
|
// De-Initialization
|
|
//--------------------------------------------------------------------------------------
|
|
CloseWindow(); // Close window and OpenGL context
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
return 0;
|
|
}
|
|
}
|