chore: clean recommit
This commit is contained in:
parent
8024c6ac40
commit
60ad2e7fb1
122 changed files with 23950 additions and 323 deletions
275
Examples/Shapes/BallPhysics.cs
Normal file
275
Examples/Shapes/BallPhysics.cs
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - ball physics
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class BallPhysics : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_BALLS = 5000; // Maximum quantity of balls
|
||||
|
||||
public string Name => "Shapes / Ball Physics";
|
||||
|
||||
public string Title => "raylib [shapes] example - ball physics";
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Ball data type
|
||||
private struct Ball
|
||||
{
|
||||
public Vector2 position;
|
||||
public Vector2 speed;
|
||||
public Vector2 prevPosition;
|
||||
public float radius;
|
||||
public float friction;
|
||||
public float elasticity;
|
||||
public Color color;
|
||||
public bool grabbed;
|
||||
}
|
||||
|
||||
private Ball[] balls;
|
||||
private int ballCount;
|
||||
private int grabbedBallIndex; // Index of the current ball that is grabbed (-1 if none)
|
||||
private Vector2 pressOffset; // Mouse press offset relative to the ball that grabbedd
|
||||
|
||||
private float gravity; // World gravity
|
||||
|
||||
private Vector2 windowPosition;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
balls = new Ball[MAX_BALLS];
|
||||
|
||||
// Init first ball in the array
|
||||
balls[0] = new Ball
|
||||
{
|
||||
position = new Vector2(GetScreenWidth()/2.0f, GetScreenHeight()/2.0f),
|
||||
speed = new Vector2(200, 200),
|
||||
prevPosition = new Vector2(0, 0),
|
||||
radius = 40,
|
||||
friction = 0.99f,
|
||||
elasticity = 0.9f,
|
||||
color = Color.Blue,
|
||||
grabbed = false
|
||||
};
|
||||
|
||||
ballCount = 1;
|
||||
grabbedBallIndex = -1; // A reference to the current ball that is grabbed
|
||||
pressOffset = new Vector2(0, 0); // Mouse press offset relative to the ball that grabbedd
|
||||
|
||||
gravity = 100; // World gravity
|
||||
|
||||
windowPosition = GetWindowPosition();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
float delta = GetFrameTime();
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
|
||||
// Checks if a ball was grabbed
|
||||
if (IsMouseButtonPressed(MouseButton.Left))
|
||||
{
|
||||
for (int i = ballCount - 1; i >= 0; i--)
|
||||
{
|
||||
pressOffset.X = mousePos.X - balls[i].position.X;
|
||||
pressOffset.Y = mousePos.Y - balls[i].position.Y;
|
||||
|
||||
// If the distance between the ball position and the mouse press position
|
||||
// is less than or equal to the ball radius, the event occurred inside the ball
|
||||
if (MathF.Sqrt(pressOffset.X*pressOffset.X + pressOffset.Y*pressOffset.Y) <= balls[i].radius)
|
||||
{
|
||||
balls[i].grabbed = true;
|
||||
grabbedBallIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Releases any ball the was grabbed
|
||||
if (IsMouseButtonReleased(MouseButton.Left))
|
||||
{
|
||||
if (grabbedBallIndex != -1)
|
||||
{
|
||||
balls[grabbedBallIndex].grabbed = false;
|
||||
grabbedBallIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new ball
|
||||
if (IsMouseButtonPressed(MouseButton.Right) || (IsKeyDown(KeyboardKey.LeftControl) && IsMouseButtonDown(MouseButton.Right)))
|
||||
{
|
||||
if (ballCount < MAX_BALLS)
|
||||
{
|
||||
balls[ballCount++] = new Ball
|
||||
{
|
||||
position = mousePos,
|
||||
speed = new Vector2(GetRandomValue(-300, 300), GetRandomValue(-300, 300)),
|
||||
prevPosition = new Vector2(0, 0),
|
||||
radius = 20.0f + GetRandomValue(0, 30),
|
||||
friction = 0.99f,
|
||||
elasticity = 0.9f,
|
||||
color = new Color(GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255),
|
||||
grabbed = false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get window position change for shaking
|
||||
Vector2 windowPositionDelta = Vector2Subtract(windowPosition, GetWindowPosition());
|
||||
|
||||
if (Vector2Length(windowPositionDelta) > 5.0f)
|
||||
{
|
||||
for (int i = 0; i < ballCount; i++)
|
||||
{
|
||||
if (!balls[i].grabbed) balls[i].speed = Vector2Add(balls[i].speed, Vector2Scale(windowPositionDelta, 10.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// Shake balls
|
||||
if (IsMouseButtonPressed(MouseButton.Middle))
|
||||
{
|
||||
for (int i = 0; i < ballCount; i++)
|
||||
{
|
||||
if (!balls[i].grabbed) balls[i].speed = new Vector2(GetRandomValue(-2000, 2000), GetRandomValue(-2000, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
// Changes gravity
|
||||
gravity += GetMouseWheelMove()*5;
|
||||
|
||||
// Updates each ball state
|
||||
for (int i = 0; i < ballCount; i++)
|
||||
{
|
||||
// The ball is not grabbed
|
||||
if (!balls[i].grabbed)
|
||||
{
|
||||
// Ball repositioning using the velocity
|
||||
balls[i].position.X += balls[i].speed.X * delta;
|
||||
balls[i].position.Y += balls[i].speed.Y * delta;
|
||||
|
||||
// Does the ball hit the screen right boundary?
|
||||
if ((balls[i].position.X + balls[i].radius) >= screenWidth)
|
||||
{
|
||||
balls[i].position.X = screenWidth - balls[i].radius; // Ball repositioning
|
||||
balls[i].speed.X = -balls[i].speed.X*balls[i].elasticity; // Elasticity makes the ball lose 10% of its velocity on hit
|
||||
}
|
||||
// Does the ball hit the screen left boundary?
|
||||
else if ((balls[i].position.X - balls[i].radius) <= 0)
|
||||
{
|
||||
balls[i].position.X = balls[i].radius;
|
||||
balls[i].speed.X = -balls[i].speed.X*balls[i].elasticity;
|
||||
}
|
||||
|
||||
// The same for y axis
|
||||
if ((balls[i].position.Y + balls[i].radius) >= screenHeight)
|
||||
{
|
||||
balls[i].position.Y = screenHeight - balls[i].radius;
|
||||
balls[i].speed.Y = -balls[i].speed.Y*balls[i].elasticity;
|
||||
}
|
||||
else if ((balls[i].position.Y - balls[i].radius) <= 0)
|
||||
{
|
||||
balls[i].position.Y = balls[i].radius;
|
||||
balls[i].speed.Y = -balls[i].speed.Y*balls[i].elasticity;
|
||||
}
|
||||
|
||||
// Friction makes the ball lose 1% of its velocity each frame
|
||||
balls[i].speed.X = balls[i].speed.X*balls[i].friction;
|
||||
// Gravity affects only the y axis
|
||||
balls[i].speed.Y = balls[i].speed.Y*balls[i].friction + gravity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ball repositioning using the mouse position
|
||||
balls[i].position.X = mousePos.X - pressOffset.X;
|
||||
balls[i].position.Y = mousePos.Y - pressOffset.Y;
|
||||
|
||||
// While the ball is grabbed, recalculates its velocity
|
||||
balls[i].speed.X = (balls[i].position.X - balls[i].prevPosition.X)/delta;
|
||||
balls[i].speed.Y = (balls[i].position.Y - balls[i].prevPosition.Y)/delta;
|
||||
balls[i].prevPosition = balls[i].position;
|
||||
}
|
||||
}
|
||||
|
||||
windowPosition = GetWindowPosition();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
for (int i = 0; i < ballCount; i++)
|
||||
{
|
||||
DrawCircleV(balls[i].position, balls[i].radius, balls[i].color);
|
||||
DrawCircleLinesV(balls[i].position, balls[i].radius, Color.Black);
|
||||
}
|
||||
|
||||
DrawText("grab a ball by pressing with the mouse and throw it by releasing", 10, 10, 10, Color.DarkGray);
|
||||
DrawText("right click to create new balls (keep left control pressed to create a lot)", 10, 30, 10, Color.DarkGray);
|
||||
DrawText("use mouse wheel to change gravity", 10, 50, 10, Color.DarkGray);
|
||||
DrawText("middle click to shake", 10, 70, 10, Color.DarkGray);
|
||||
DrawText($"BALL COUNT: {ballCount}", 10, GetScreenHeight() - 70, 20, Color.Black);
|
||||
DrawText($"GRAVITY: {gravity:F2}", 10, GetScreenHeight() - 40, 20, Color.Black);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - ball physics");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new BallPhysics();
|
||||
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;
|
||||
}
|
||||
}
|
||||
290
Examples/Shapes/BulletHell.cs
Normal file
290
Examples/Shapes/BulletHell.cs
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - bullet hell
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 5.6, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Zero (@zerohorsepower) 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 Zero (@zerohorsepower)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class BulletHell : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_BULLETS = 500000; // Max bullets to be processed
|
||||
|
||||
public string Name => "Shapes / Bullet Hell";
|
||||
|
||||
public string Title => "raylib [shapes] example - bullet hell";
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
private struct Bullet
|
||||
{
|
||||
public Vector2 position; // Bullet position on screen
|
||||
public Vector2 acceleration; // Amount of pixels to be incremented to position every frame
|
||||
public bool disabled; // Skip processing and draw case out of screen
|
||||
public Color color; // Bullet color
|
||||
}
|
||||
|
||||
// Bullets definition
|
||||
private Bullet[] bullets;
|
||||
private int bulletCount;
|
||||
private int bulletDisabledCount; // Used to calculate how many bullets are on screen
|
||||
private int bulletRadius;
|
||||
private float bulletSpeed;
|
||||
private int bulletRows;
|
||||
private Color[] bulletColor;
|
||||
|
||||
// Spawner variables
|
||||
private float baseDirection;
|
||||
private int angleIncrement; // After spawn all bullet rows, increment this value on the baseDirection for next the frame
|
||||
private float spawnCooldown;
|
||||
private float spawnCooldownTimer;
|
||||
|
||||
// Magic circle
|
||||
private float magicCircleRotation;
|
||||
|
||||
// Used on performance drawing
|
||||
private RenderTexture2D bulletTexture;
|
||||
|
||||
private bool drawInPerformanceMode; // Switch between DrawCircle() and DrawTexture()
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Bullets definition
|
||||
bullets = new Bullet[MAX_BULLETS]; // Bullets array
|
||||
bulletCount = 0;
|
||||
bulletDisabledCount = 0;
|
||||
bulletRadius = 10;
|
||||
bulletSpeed = 3.0f;
|
||||
bulletRows = 6;
|
||||
bulletColor = new[] { Color.Red, Color.Blue };
|
||||
|
||||
// Spawner variables
|
||||
baseDirection = 0;
|
||||
angleIncrement = 5;
|
||||
spawnCooldown = 2;
|
||||
spawnCooldownTimer = spawnCooldown;
|
||||
|
||||
// Magic circle
|
||||
magicCircleRotation = 0;
|
||||
|
||||
// Used on performance drawing
|
||||
bulletTexture = LoadRenderTexture(24, 24);
|
||||
|
||||
// Draw circle to bullet texture, then draw bullet using DrawTexture()
|
||||
// NOTE: This is done to improve the performance, since DrawCircle() is very slow
|
||||
BeginTextureMode(bulletTexture);
|
||||
DrawCircle(12, 12, (float)bulletRadius, Color.White);
|
||||
DrawCircleLines(12, 12, (float)bulletRadius, Color.Black);
|
||||
EndTextureMode();
|
||||
|
||||
drawInPerformanceMode = true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Reset the bullet index
|
||||
// New bullets will replace the old ones that are already disabled due to out-of-screen
|
||||
if (bulletCount >= MAX_BULLETS)
|
||||
{
|
||||
bulletCount = 0;
|
||||
bulletDisabledCount = 0;
|
||||
}
|
||||
|
||||
spawnCooldownTimer--;
|
||||
if (spawnCooldownTimer < 0)
|
||||
{
|
||||
spawnCooldownTimer = spawnCooldown;
|
||||
|
||||
// Spawn bullets
|
||||
float degreesPerRow = 360.0f/bulletRows;
|
||||
for (int row = 0; row < bulletRows; row++)
|
||||
{
|
||||
if (bulletCount < MAX_BULLETS)
|
||||
{
|
||||
bullets[bulletCount].position = new Vector2((float)screenWidth/2, (float)screenHeight/2);
|
||||
bullets[bulletCount].disabled = false;
|
||||
bullets[bulletCount].color = bulletColor[row%2];
|
||||
|
||||
float bulletDirection = baseDirection + (degreesPerRow*row);
|
||||
|
||||
// Bullet speed*bullet direction, this will determine how much pixels will be incremented/decremented
|
||||
// from the bullet position every frame. Since the bullets doesn't change its direction and speed,
|
||||
// only need to calculate it at the spawning time
|
||||
// 0 degrees = right, 90 degrees = down, 180 degrees = left and 270 degrees = up, basically clockwise
|
||||
// Case you want it to be anti-clockwise, add "* -1" at the y acceleration
|
||||
bullets[bulletCount].acceleration = new Vector2(
|
||||
bulletSpeed*MathF.Cos(bulletDirection*DEG2RAD),
|
||||
bulletSpeed*MathF.Sin(bulletDirection*DEG2RAD)
|
||||
);
|
||||
|
||||
bulletCount++;
|
||||
}
|
||||
}
|
||||
|
||||
baseDirection += angleIncrement;
|
||||
}
|
||||
|
||||
// Update bullets position based on its acceleration
|
||||
for (int i = 0; i < bulletCount; i++)
|
||||
{
|
||||
// Only update bullet if inside the screen
|
||||
if (!bullets[i].disabled)
|
||||
{
|
||||
bullets[i].position.X += bullets[i].acceleration.X;
|
||||
bullets[i].position.Y += bullets[i].acceleration.Y;
|
||||
|
||||
// Disable bullet if out of screen
|
||||
if ((bullets[i].position.X < -bulletRadius*2) ||
|
||||
(bullets[i].position.X > screenWidth + bulletRadius*2) ||
|
||||
(bullets[i].position.Y < -bulletRadius*2) ||
|
||||
(bullets[i].position.Y > screenHeight + bulletRadius*2))
|
||||
{
|
||||
bullets[i].disabled = true;
|
||||
bulletDisabledCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input logic
|
||||
if ((IsKeyPressed(KeyboardKey.Right) || IsKeyPressed(KeyboardKey.D)) && (bulletRows < 359)) bulletRows++;
|
||||
if ((IsKeyPressed(KeyboardKey.Left) || IsKeyPressed(KeyboardKey.A)) && (bulletRows > 1)) bulletRows--;
|
||||
if (IsKeyPressed(KeyboardKey.Up) || IsKeyPressed(KeyboardKey.W)) bulletSpeed += 0.25f;
|
||||
if ((IsKeyPressed(KeyboardKey.Down) || IsKeyPressed(KeyboardKey.S)) && (bulletSpeed > 0.50f)) bulletSpeed -= 0.25f;
|
||||
if (IsKeyPressed(KeyboardKey.Z) && (spawnCooldown > 1)) spawnCooldown--;
|
||||
if (IsKeyPressed(KeyboardKey.X)) spawnCooldown++;
|
||||
if (IsKeyPressed(KeyboardKey.Enter)) drawInPerformanceMode = !drawInPerformanceMode;
|
||||
|
||||
if (IsKeyDown(KeyboardKey.Space))
|
||||
{
|
||||
angleIncrement += 1;
|
||||
angleIncrement %= 360;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.C))
|
||||
{
|
||||
bulletCount = 0;
|
||||
bulletDisabledCount = 0;
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw magic circle
|
||||
magicCircleRotation++;
|
||||
DrawRectanglePro(new Rectangle((float)screenWidth/2, (float)screenHeight/2, 120, 120),
|
||||
new Vector2(60.0f, 60.0f), magicCircleRotation, Color.Purple);
|
||||
DrawRectanglePro(new Rectangle((float)screenWidth/2, (float)screenHeight/2, 120, 120),
|
||||
new Vector2(60.0f, 60.0f), magicCircleRotation + 45, Color.Purple);
|
||||
DrawCircleLines(screenWidth/2, screenHeight/2, 70, Color.Black);
|
||||
DrawCircleLines(screenWidth/2, screenHeight/2, 50, Color.Black);
|
||||
DrawCircleLines(screenWidth/2, screenHeight/2, 30, Color.Black);
|
||||
|
||||
// Draw bullets
|
||||
if (drawInPerformanceMode)
|
||||
{
|
||||
// Draw bullets using pre-rendered texture containing circle
|
||||
for (int i = 0; i < bulletCount; i++)
|
||||
{
|
||||
// Do not draw disabled bullets (out of screen)
|
||||
if (!bullets[i].disabled)
|
||||
{
|
||||
DrawTexture(bulletTexture.Texture,
|
||||
(int)(bullets[i].position.X - bulletTexture.Texture.Width*0.5f),
|
||||
(int)(bullets[i].position.Y - bulletTexture.Texture.Height*0.5f),
|
||||
bullets[i].color);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw bullets using DrawCircle(), less performant
|
||||
for (int i = 0; i < bulletCount; i++)
|
||||
{
|
||||
// Do not draw disabled bullets (out of screen)
|
||||
if (!bullets[i].disabled)
|
||||
{
|
||||
DrawCircleV(bullets[i].position, (float)bulletRadius, bullets[i].color);
|
||||
DrawCircleLinesV(bullets[i].position, (float)bulletRadius, Color.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw UI
|
||||
DrawRectangle(10, 10, 280, 150, new Color(0, 0, 0, 200));
|
||||
DrawText("Controls:", 20, 20, 10, Color.LightGray);
|
||||
DrawText("- Right/Left or A/D: Change rows number", 40, 40, 10, Color.LightGray);
|
||||
DrawText("- Up/Down or W/S: Change bullet speed", 40, 60, 10, Color.LightGray);
|
||||
DrawText("- Z or X: Change spawn cooldown", 40, 80, 10, Color.LightGray);
|
||||
DrawText("- Space (Hold): Change the angle increment", 40, 100, 10, Color.LightGray);
|
||||
DrawText("- Enter: Switch draw method (Performance)", 40, 120, 10, Color.LightGray);
|
||||
DrawText("- C: Clear bullets", 40, 140, 10, Color.LightGray);
|
||||
|
||||
DrawRectangle(610, 10, 170, 30, new Color(0, 0, 0, 200));
|
||||
if (drawInPerformanceMode) DrawText("Draw method: DrawTexture(*)", 620, 20, 10, Color.Green);
|
||||
else DrawText("Draw method: DrawCircle(*)", 620, 20, 10, Color.Red);
|
||||
|
||||
DrawRectangle(135, 410, 530, 30, new Color(0, 0, 0, 200));
|
||||
DrawText($"[ FPS: {GetFPS()}, Bullets: {bulletCount - bulletDisabledCount}, Rows: {bulletRows}, Bullet speed: {bulletSpeed:F2}, Angle increment per frame: {angleIncrement}, Cooldown: {spawnCooldown:F0} ]",
|
||||
155, 420, 10, Color.Green);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
UnloadRenderTexture(bulletTexture); // Unload bullet texture
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - bullet hell");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new BulletHell();
|
||||
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;
|
||||
}
|
||||
}
|
||||
240
Examples/Shapes/ClockOfClocks.cs
Normal file
240
Examples/Shapes/ClockOfClocks.cs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - clock of clocks
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 6.0
|
||||
*
|
||||
* Example contributed by JP Mortiboys (@themushroompirates) 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 JP Mortiboys (@themushroompirates)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath; // Required for: Lerp(), Clamp()
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class ClockOfClocks : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Clock of Clocks";
|
||||
|
||||
public string Title => "raylib [shapes] example - clock of clocks";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
private Color bgColor;
|
||||
private Color handsColor;
|
||||
|
||||
private const float clockFaceSize = 24;
|
||||
private const float clockFaceSpacing = 8.0f;
|
||||
private const float sectionSpacing = 16.0f;
|
||||
|
||||
private static readonly Vector2 TL = new(0.0f, 90.0f); // Top-left corner
|
||||
private static readonly Vector2 TR = new(90.0f, 180.0f); // Top-right corner
|
||||
private static readonly Vector2 BR = new(180.0f, 270.0f); // Bottom-right corner
|
||||
private static readonly Vector2 BL = new(0.0f, 270.0f); // Bottom-left corner
|
||||
private static readonly Vector2 HH = new(0.0f, 180.0f); // Horizontal line
|
||||
private static readonly Vector2 VV = new(90.0f, 270.0f); // Vertical line
|
||||
private static readonly Vector2 ZZ = new(135.0f, 135.0f); // Not relevant
|
||||
|
||||
private Vector2[,] digitAngles;
|
||||
|
||||
// Time for the hands to move to the new position (in seconds); this must be <1s
|
||||
private const float handsMoveDuration = 0.5f;
|
||||
|
||||
private int prevSeconds;
|
||||
private Vector2[,] currentAngles;
|
||||
private Vector2[,] srcAngles;
|
||||
private Vector2[,] dstAngles;
|
||||
|
||||
private float handsMoveTimer;
|
||||
private int hourMode;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
bgColor = ColorLerp(Color.DarkBlue, Color.Black, 0.75f);
|
||||
handsColor = ColorLerp(Color.Yellow, Color.RayWhite, .25f);
|
||||
|
||||
digitAngles = new Vector2[10, 24]
|
||||
{
|
||||
/* 0 */ { TL, HH, HH, TR, /* */ VV, TL, TR, VV, /* */ VV, VV, VV, VV, /* */ VV, VV, VV, VV, /* */ VV, BL, BR, VV, /* */ BL, HH, HH, BR },
|
||||
/* 1 */ { TL, HH, TR, ZZ, /* */ BL, TR, VV, ZZ, /* */ ZZ, VV, VV, ZZ, /* */ ZZ, VV, VV, ZZ, /* */ TL, BR, BL, TR, /* */ BL, HH, HH, BR },
|
||||
/* 2 */ { TL, HH, HH, TR, /* */ BL, HH, TR, VV, /* */ TL, HH, BR, VV, /* */ VV, TL, HH, BR, /* */ VV, BL, HH, TR, /* */ BL, HH, HH, BR },
|
||||
/* 3 */ { TL, HH, HH, TR, /* */ BL, HH, TR, VV, /* */ TL, HH, BR, VV, /* */ BL, HH, TR, VV, /* */ TL, HH, BR, VV, /* */ BL, HH, HH, BR },
|
||||
/* 4 */ { TL, TR, TL, TR, /* */ VV, VV, VV, VV, /* */ VV, BL, BR, VV, /* */ BL, HH, TR, VV, /* */ ZZ, ZZ, VV, VV, /* */ ZZ, ZZ, BL, BR },
|
||||
/* 5 */ { TL, HH, HH, TR, /* */ VV, TL, HH, BR, /* */ VV, BL, HH, TR, /* */ BL, HH, TR, VV, /* */ TL, HH, BR, VV, /* */ BL, HH, HH, BR },
|
||||
/* 6 */ { TL, HH, HH, TR, /* */ VV, TL, HH, BR, /* */ VV, BL, HH, TR, /* */ VV, TL, TR, VV, /* */ VV, BL, BR, VV, /* */ BL, HH, HH, BR },
|
||||
/* 7 */ { TL, HH, HH, TR, /* */ BL, HH, TR, VV, /* */ ZZ, ZZ, VV, VV, /* */ ZZ, ZZ, VV, VV, /* */ ZZ, ZZ, VV, VV, /* */ ZZ, ZZ, BL, BR },
|
||||
/* 8 */ { TL, HH, HH, TR, /* */ VV, TL, TR, VV, /* */ VV, BL, BR, VV, /* */ VV, TL, TR, VV, /* */ VV, BL, BR, VV, /* */ BL, HH, HH, BR },
|
||||
/* 9 */ { TL, HH, HH, TR, /* */ VV, TL, TR, VV, /* */ VV, BL, BR, VV, /* */ BL, HH, TR, VV, /* */ TL, HH, BR, VV, /* */ BL, HH, HH, BR },
|
||||
};
|
||||
|
||||
prevSeconds = -1;
|
||||
currentAngles = new Vector2[6, 24];
|
||||
srcAngles = new Vector2[6, 24];
|
||||
dstAngles = new Vector2[6, 24];
|
||||
|
||||
handsMoveTimer = 0.0f;
|
||||
hourMode = 24;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Get the current time
|
||||
DateTime timeinfo = DateTime.Now;
|
||||
|
||||
if (timeinfo.Second != prevSeconds)
|
||||
{
|
||||
// The time has changed, so we need to move the hands to the new positions
|
||||
prevSeconds = timeinfo.Second;
|
||||
|
||||
// Format the current time so we can access the individual digits
|
||||
string clockDigits = $"{timeinfo.Hour % hourMode:D2}{timeinfo.Minute:D2}{timeinfo.Second:D2}";
|
||||
|
||||
// Fetch where we want all the hands to be
|
||||
for (int digit = 0; digit < 6; digit++)
|
||||
{
|
||||
for (int cell = 0; cell < 24; cell++)
|
||||
{
|
||||
srcAngles[digit, cell] = currentAngles[digit, cell];
|
||||
dstAngles[digit, cell] = digitAngles[clockDigits[digit] - '0', cell];
|
||||
|
||||
// Quick exception for 12h mode
|
||||
if ((digit == 0) && (hourMode == 12) && (clockDigits[0] == '0')) dstAngles[digit, cell] = ZZ;
|
||||
if (srcAngles[digit, cell].X > dstAngles[digit, cell].X) srcAngles[digit, cell].X -= 360.0f;
|
||||
if (srcAngles[digit, cell].Y > dstAngles[digit, cell].Y) srcAngles[digit, cell].Y -= 360.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the timer
|
||||
handsMoveTimer = -GetFrameTime();
|
||||
}
|
||||
|
||||
// Now let's animate all the hands if we need to
|
||||
if (handsMoveTimer < handsMoveDuration)
|
||||
{
|
||||
// Increase the timer but don't go above the maximum
|
||||
handsMoveTimer = Clamp(handsMoveTimer + GetFrameTime(), 0, handsMoveDuration);
|
||||
|
||||
// Calculate the % completion of the animation
|
||||
float t = handsMoveTimer / handsMoveDuration;
|
||||
|
||||
// A little cheeky smoothstep
|
||||
t = t * t * (3.0f - 2.0f * t);
|
||||
|
||||
for (int digit = 0; digit < 6; digit++)
|
||||
{
|
||||
for (int cell = 0; cell < 24; cell++)
|
||||
{
|
||||
currentAngles[digit, cell].X = Lerp(srcAngles[digit, cell].X, dstAngles[digit, cell].X, t);
|
||||
currentAngles[digit, cell].Y = Lerp(srcAngles[digit, cell].Y, dstAngles[digit, cell].Y, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle input
|
||||
if (IsKeyPressed(KeyboardKey.Space)) hourMode = 36 - hourMode; // Toggle between 12 and 24 hour mode with space
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(bgColor);
|
||||
|
||||
DrawText($"{hourMode}-h mode, space to change", 10, 30, 20, Color.RayWhite);
|
||||
|
||||
float xOffset = 4.0f;
|
||||
|
||||
for (int digit = 0; digit < 6; digit++)
|
||||
{
|
||||
for (int row = 0; row < 6; row++)
|
||||
{
|
||||
for (int col = 0; col < 4; col++)
|
||||
{
|
||||
Vector2 centre = new(
|
||||
xOffset + col * (clockFaceSize + clockFaceSpacing) + clockFaceSize * 0.5f,
|
||||
100 + row * (clockFaceSize + clockFaceSpacing) + clockFaceSize * 0.5f
|
||||
);
|
||||
|
||||
DrawRing(centre, clockFaceSize * 0.5f - 2.0f, clockFaceSize * 0.5f, 0, 360, 24, Color.DarkGray);
|
||||
|
||||
// Big hand
|
||||
DrawRectanglePro(
|
||||
new Rectangle(centre.X, centre.Y, clockFaceSize * 0.5f + 4.0f, 4.0f),
|
||||
new Vector2(2.0f, 2.0f),
|
||||
currentAngles[digit, row * 4 + col].X,
|
||||
handsColor
|
||||
);
|
||||
|
||||
// Little hand
|
||||
DrawRectanglePro(
|
||||
new Rectangle(centre.X, centre.Y, clockFaceSize * 0.5f + 2.0f, 4.0f),
|
||||
new Vector2(2.0f, 2.0f),
|
||||
currentAngles[digit, row * 4 + col].Y,
|
||||
handsColor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
xOffset += (clockFaceSize + clockFaceSpacing) * 4;
|
||||
if (digit % 2 == 1)
|
||||
{
|
||||
DrawRing(new Vector2(xOffset + 4.0f, 160.0f), 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
|
||||
DrawRing(new Vector2(xOffset + 4.0f, 225.0f), 6.0f, 8.0f, 0.0f, 360.0f, 24, handsColor);
|
||||
xOffset += sectionSpacing;
|
||||
}
|
||||
}
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - clock of clocks");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new ClockOfClocks();
|
||||
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;
|
||||
}
|
||||
}
|
||||
130
Examples/Shapes/DashedLine.cs
Normal file
130
Examples/Shapes/DashedLine.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - dashed line
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
||||
*
|
||||
* Example contributed by Luís Almeida (@luis605)
|
||||
*
|
||||
* 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 Luís Almeida (@luis605)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class DashedLine : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Dashed Line";
|
||||
|
||||
public string Title => "raylib [shapes] example - dashed line";
|
||||
|
||||
// Line Properties
|
||||
private Vector2 lineStartPosition;
|
||||
private Vector2 lineEndPosition;
|
||||
private float dashLength;
|
||||
private float blankLength;
|
||||
|
||||
// Color selection
|
||||
private Color[] lineColors;
|
||||
private int colorIndex;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Line Properties
|
||||
lineStartPosition = new Vector2(20.0f, 50.0f);
|
||||
lineEndPosition = new Vector2(780.0f, 400.0f);
|
||||
dashLength = 25.0f;
|
||||
blankLength = 15.0f;
|
||||
|
||||
// Color selection
|
||||
lineColors = new[] { Color.Red, Color.Orange, Color.Gold, Color.Green, Color.Blue, Color.Violet, Color.Pink, Color.Black };
|
||||
colorIndex = 0;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
lineEndPosition = GetMousePosition(); // Line endpoint follows the mouse
|
||||
|
||||
// Change Dash Length (UP/DOWN arrows)
|
||||
if (IsKeyDown(KeyboardKey.Up)) dashLength += 1.0f;
|
||||
if (IsKeyDown(KeyboardKey.Down) && dashLength > 1.0f) dashLength -= 1.0f;
|
||||
|
||||
// Change Space Length (LEFT/RIGHT arrows)
|
||||
if (IsKeyDown(KeyboardKey.Right)) blankLength += 1.0f;
|
||||
if (IsKeyDown(KeyboardKey.Left) && blankLength > 1.0f) blankLength -= 1.0f;
|
||||
|
||||
// Cycle through colors ('C' key)
|
||||
if (IsKeyPressed(KeyboardKey.C)) colorIndex = (colorIndex + 1)%lineColors.Length;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw the dashed line with the current properties
|
||||
DrawLineDashed(lineStartPosition, lineEndPosition, (int)dashLength, (int)blankLength, lineColors[colorIndex]);
|
||||
|
||||
// Draw UI and Instructions
|
||||
DrawRectangle(5, 5, 265, 95, Fade(Color.SkyBlue, 0.5f));
|
||||
DrawRectangleLines(5, 5, 265, 95, Color.Blue);
|
||||
|
||||
DrawText("CONTROLS:", 15, 15, 10, Color.Black);
|
||||
DrawText("UP/DOWN: Change Dash Length", 15, 35, 10, Color.Black);
|
||||
DrawText("LEFT/RIGHT: Change Space Length", 15, 55, 10, Color.Black);
|
||||
DrawText("C: Cycle Color", 15, 75, 10, Color.Black);
|
||||
|
||||
DrawText($"Dash: {dashLength:F0} | Space: {blankLength:F0}", 15, 115, 10, Color.DarkGray);
|
||||
|
||||
DrawFPS(screenWidth - 80, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - dashed line");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new DashedLine();
|
||||
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;
|
||||
}
|
||||
}
|
||||
327
Examples/Shapes/DigitalClock.cs
Normal file
327
Examples/Shapes/DigitalClock.cs
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - digital clock
|
||||
*
|
||||
* Example complexity rating: [★★★★] 4/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Hamza RAHAL (@hmz-rhl) 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 Hamza RAHAL (@hmz-rhl) and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class DigitalClock : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int CLOCK_ANALOG = 0;
|
||||
private const int CLOCK_DIGITAL = 1;
|
||||
|
||||
public string Name => "Shapes / Digital Clock";
|
||||
|
||||
public string Title => "raylib [shapes] example - digital clock";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Clock hand type
|
||||
private struct ClockHand
|
||||
{
|
||||
public int value; // Time value
|
||||
|
||||
// Visual elements
|
||||
public float angle; // Hand angle
|
||||
public int length; // Hand length
|
||||
public int thickness; // Hand thickness
|
||||
public Color color; // Hand color
|
||||
}
|
||||
|
||||
// Clock hands
|
||||
private struct Clock
|
||||
{
|
||||
public ClockHand second; // Clock hand for seconds
|
||||
public ClockHand minute; // Clock hand for minutes
|
||||
public ClockHand hour; // Clock hand for hours
|
||||
}
|
||||
|
||||
private int clockMode;
|
||||
private Clock clock;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
clockMode = CLOCK_DIGITAL;
|
||||
|
||||
// Initialize clock
|
||||
// NOTE: Includes visual info for analog clock
|
||||
clock = new Clock();
|
||||
|
||||
clock.second.angle = 45;
|
||||
clock.second.length = 140;
|
||||
clock.second.thickness = 3;
|
||||
clock.second.color = Color.Maroon;
|
||||
|
||||
clock.minute.angle = 10;
|
||||
clock.minute.length = 130;
|
||||
clock.minute.thickness = 7;
|
||||
clock.minute.color = Color.DarkGray;
|
||||
|
||||
clock.hour.angle = 0;
|
||||
clock.hour.length = 100;
|
||||
clock.hour.thickness = 7;
|
||||
clock.hour.color = Color.Black;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyPressed(KeyboardKey.Space))
|
||||
{
|
||||
// Toggle clock mode
|
||||
if (clockMode == CLOCK_DIGITAL) clockMode = CLOCK_ANALOG;
|
||||
else if (clockMode == CLOCK_ANALOG) clockMode = CLOCK_DIGITAL;
|
||||
}
|
||||
|
||||
UpdateClock(); // Update clock required data: value and angle
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw clock in selected mode
|
||||
if (clockMode == CLOCK_ANALOG) DrawClockAnalog(clock, new Vector2(400, 240));
|
||||
else if (clockMode == CLOCK_DIGITAL)
|
||||
{
|
||||
DrawClockDigital(clock, new Vector2(30, 60));
|
||||
|
||||
// Draw clock using default raylib font
|
||||
string clockTime = $"{clock.hour.value:D2}:{clock.minute.value:D2}:{clock.second.value:D2}";
|
||||
DrawText(clockTime, GetScreenWidth() / 2 - MeasureText(clockTime, 150) / 2, 300, 150, Color.Black);
|
||||
}
|
||||
|
||||
DrawText($"Press [SPACE] to switch clock mode: {((clockMode == CLOCK_DIGITAL) ? "DIGITAL CLOCK" : "ANALOGUE CLOCK")}",
|
||||
10, 10, 20, Color.DarkGray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Update clock time
|
||||
private void UpdateClock()
|
||||
{
|
||||
DateTime timeinfo = DateTime.Now;
|
||||
|
||||
// Updating time data
|
||||
clock.second.value = timeinfo.Second;
|
||||
clock.minute.value = timeinfo.Minute;
|
||||
clock.hour.value = timeinfo.Hour;
|
||||
|
||||
clock.hour.angle = (timeinfo.Hour % 12) * 180.0f / 6.0f;
|
||||
clock.hour.angle += (timeinfo.Minute % 60) * 30 / 60.0f;
|
||||
clock.hour.angle -= 90;
|
||||
|
||||
clock.minute.angle = (timeinfo.Minute % 60) * 6.0f;
|
||||
clock.minute.angle += (timeinfo.Second % 60) * 6 / 60.0f;
|
||||
clock.minute.angle -= 90;
|
||||
|
||||
clock.second.angle = (timeinfo.Second % 60) * 6.0f;
|
||||
clock.second.angle -= 90;
|
||||
}
|
||||
|
||||
// Draw analog clock
|
||||
// Parameter: position, refers to center position
|
||||
private static void DrawClockAnalog(Clock clock, Vector2 position)
|
||||
{
|
||||
// Draw clock base
|
||||
DrawCircleV(position, clock.second.length + 40.0f, Color.LightGray);
|
||||
DrawCircleV(position, 12.0f, Color.Gray);
|
||||
|
||||
// Draw clock minutes/seconds lines
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
DrawLineEx(new Vector2(position.X + (clock.second.length + ((i % 5) != 0 ? 10 : 6)) * MathF.Cos((6.0f * i - 90.0f) * DEG2RAD),
|
||||
position.Y + (clock.second.length + ((i % 5) != 0 ? 10 : 6)) * MathF.Sin((6.0f * i - 90.0f) * DEG2RAD)),
|
||||
new Vector2(position.X + (clock.second.length + 20) * MathF.Cos((6.0f * i - 90.0f) * DEG2RAD),
|
||||
position.Y + (clock.second.length + 20) * MathF.Sin((6.0f * i - 90.0f) * DEG2RAD)), ((i % 5) != 0 ? 1.0f : 3.0f), Color.DarkGray);
|
||||
}
|
||||
|
||||
// Draw hand seconds
|
||||
DrawRectanglePro(new Rectangle(position.X, position.Y, (float)clock.second.length, (float)clock.second.thickness),
|
||||
new Vector2(0.0f, clock.second.thickness / 2.0f), clock.second.angle, clock.second.color);
|
||||
|
||||
// Draw hand minutes
|
||||
DrawRectanglePro(new Rectangle(position.X, position.Y, (float)clock.minute.length, (float)clock.minute.thickness),
|
||||
new Vector2(0.0f, clock.minute.thickness / 2.0f), clock.minute.angle, clock.minute.color);
|
||||
|
||||
// Draw hand hours
|
||||
DrawRectanglePro(new Rectangle(position.X, position.Y, (float)clock.hour.length, (float)clock.hour.thickness),
|
||||
new Vector2(0.0f, clock.hour.thickness / 2.0f), clock.hour.angle, clock.hour.color);
|
||||
}
|
||||
|
||||
// Draw digital clock
|
||||
// PARAM: position, refers to top-left corner
|
||||
private static void DrawClockDigital(Clock clock, Vector2 position)
|
||||
{
|
||||
// Draw clock using custom 7-segments display (made of shapes)
|
||||
DrawDisplayValue(new Vector2(position.X, position.Y), clock.hour.value / 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
DrawDisplayValue(new Vector2(position.X + 120, position.Y), clock.hour.value % 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
|
||||
DrawCircle((int)position.X + 240, (int)position.Y + 70, 12, (clock.second.value % 2) != 0 ? Color.Red : Fade(Color.LightGray, 0.3f));
|
||||
DrawCircle((int)position.X + 240, (int)position.Y + 150, 12, (clock.second.value % 2) != 0 ? Color.Red : Fade(Color.LightGray, 0.3f));
|
||||
|
||||
DrawDisplayValue(new Vector2(position.X + 260, position.Y), clock.minute.value / 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
DrawDisplayValue(new Vector2(position.X + 380, position.Y), clock.minute.value % 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
|
||||
DrawCircle((int)position.X + 500, (int)position.Y + 70, 12, (clock.second.value % 2) != 0 ? Color.Red : Fade(Color.LightGray, 0.3f));
|
||||
DrawCircle((int)position.X + 500, (int)position.Y + 150, 12, (clock.second.value % 2) != 0 ? Color.Red : Fade(Color.LightGray, 0.3f));
|
||||
|
||||
DrawDisplayValue(new Vector2(position.X + 520, position.Y), clock.second.value / 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
DrawDisplayValue(new Vector2(position.X + 640, position.Y), clock.second.value % 10, Color.Red, Fade(Color.LightGray, 0.3f));
|
||||
}
|
||||
|
||||
// Draw 7-segment display with value
|
||||
private static void DrawDisplayValue(Vector2 position, int value, Color colorOn, Color colorOff)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case 0: Draw7SDisplay(position, 0b00111111, colorOn, colorOff); break;
|
||||
case 1: Draw7SDisplay(position, 0b00000110, colorOn, colorOff); break;
|
||||
case 2: Draw7SDisplay(position, 0b01011011, colorOn, colorOff); break;
|
||||
case 3: Draw7SDisplay(position, 0b01001111, colorOn, colorOff); break;
|
||||
case 4: Draw7SDisplay(position, 0b01100110, colorOn, colorOff); break;
|
||||
case 5: Draw7SDisplay(position, 0b01101101, colorOn, colorOff); break;
|
||||
case 6: Draw7SDisplay(position, 0b01111101, colorOn, colorOff); break;
|
||||
case 7: Draw7SDisplay(position, 0b00000111, colorOn, colorOff); break;
|
||||
case 8: Draw7SDisplay(position, 0b01111111, colorOn, colorOff); break;
|
||||
case 9: Draw7SDisplay(position, 0b01101111, colorOn, colorOff); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw seven segments display
|
||||
// Parameter: position, refers to top-left corner of display
|
||||
// Parameter: segments, defines in binary the segments to be activated
|
||||
private static void Draw7SDisplay(Vector2 position, int segments, Color colorOn, Color colorOff)
|
||||
{
|
||||
int segmentLen = 60;
|
||||
int segmentThick = 20;
|
||||
float offsetYAdjust = segmentThick * 0.3f; // HACK: Adjust gap space between segment limits
|
||||
|
||||
// Segment A
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick + segmentLen / 2.0f, position.Y + segmentThick),
|
||||
segmentLen, segmentThick, false, (segments & 0b00000001) != 0 ? colorOn : colorOff);
|
||||
// Segment B
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick + segmentLen + segmentThick / 2.0f, position.Y + 2 * segmentThick + segmentLen / 2.0f - offsetYAdjust),
|
||||
segmentLen, segmentThick, true, (segments & 0b00000010) != 0 ? colorOn : colorOff);
|
||||
// Segment C
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick + segmentLen + segmentThick / 2.0f, position.Y + 4 * segmentThick + segmentLen + segmentLen / 2.0f - 3 * offsetYAdjust),
|
||||
segmentLen, segmentThick, true, (segments & 0b00000100) != 0 ? colorOn : colorOff);
|
||||
// Segment D
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick + segmentLen / 2.0f, position.Y + 5 * segmentThick + 2 * segmentLen - 4 * offsetYAdjust),
|
||||
segmentLen, segmentThick, false, (segments & 0b00001000) != 0 ? colorOn : colorOff);
|
||||
// Segment E
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick / 2.0f, position.Y + 4 * segmentThick + segmentLen + segmentLen / 2.0f - 3 * offsetYAdjust),
|
||||
segmentLen, segmentThick, true, (segments & 0b00010000) != 0 ? colorOn : colorOff);
|
||||
// Segment F
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick / 2.0f, position.Y + 2 * segmentThick + segmentLen / 2.0f - offsetYAdjust),
|
||||
segmentLen, segmentThick, true, (segments & 0b00100000) != 0 ? colorOn : colorOff);
|
||||
// Segment G
|
||||
DrawDisplaySegment(new Vector2(position.X + segmentThick + segmentLen / 2.0f, position.Y + 3 * segmentThick + segmentLen - 2 * offsetYAdjust),
|
||||
segmentLen, segmentThick, false, (segments & 0b01000000) != 0 ? colorOn : colorOff);
|
||||
}
|
||||
|
||||
// Draw one 7-segment display segment, horizontal or vertical
|
||||
private static void DrawDisplaySegment(Vector2 center, int length, int thick, bool vertical, Color color)
|
||||
{
|
||||
if (!vertical)
|
||||
{
|
||||
// Horizontal segment points
|
||||
/*
|
||||
3___________________________5
|
||||
/ \
|
||||
/1 x 6\
|
||||
\ /
|
||||
\2___________________________4/
|
||||
*/
|
||||
Vector2[] segmentPointsH = new Vector2[6]
|
||||
{
|
||||
new Vector2(center.X - length / 2.0f - thick / 2.0f, center.Y), // Point 1
|
||||
new Vector2(center.X - length / 2.0f, center.Y + thick / 2.0f), // Point 2
|
||||
new Vector2(center.X - length / 2.0f, center.Y - thick / 2.0f), // Point 3
|
||||
new Vector2(center.X + length / 2.0f, center.Y + thick / 2.0f), // Point 4
|
||||
new Vector2(center.X + length / 2.0f, center.Y - thick / 2.0f), // Point 5
|
||||
new Vector2(center.X + length / 2.0f + thick / 2.0f, center.Y), // Point 6
|
||||
};
|
||||
|
||||
DrawTriangleStrip(segmentPointsH, 6, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vertical segment points
|
||||
Vector2[] segmentPointsV = new Vector2[6]
|
||||
{
|
||||
new Vector2(center.X, center.Y - length / 2.0f - thick / 2.0f), // Point 1
|
||||
new Vector2(center.X - thick / 2.0f, center.Y - length / 2.0f), // Point 2
|
||||
new Vector2(center.X + thick / 2.0f, center.Y - length / 2.0f), // Point 3
|
||||
new Vector2(center.X - thick / 2.0f, center.Y + length / 2.0f), // Point 4
|
||||
new Vector2(center.X + thick / 2.0f, center.Y + length / 2.0f), // Point 5
|
||||
new Vector2(center.X, center.Y + (float)length / 2 + thick / 2.0f), // Point 6
|
||||
};
|
||||
|
||||
DrawTriangleStrip(segmentPointsV, 6, color);
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - digital clock");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new DigitalClock();
|
||||
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;
|
||||
}
|
||||
}
|
||||
205
Examples/Shapes/DoublePendulum.cs
Normal file
205
Examples/Shapes/DoublePendulum.cs
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - double pendulum
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
||||
*
|
||||
* Example contributed by JoeCheong (@Joecheong2006) 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 JoeCheong (@Joecheong2006)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class DoublePendulum : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
// Constant for Simulation
|
||||
private const int SIMULATION_STEPS = 30;
|
||||
private const float G = 9.81f;
|
||||
|
||||
public string Name => "Shapes / Double Pendulum";
|
||||
|
||||
public string Title => "raylib [shapes] example - double pendulum";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.HighDpiWindow;
|
||||
|
||||
// Simulation Parameters
|
||||
private float l1, m1, theta1, w1;
|
||||
private float l2, m2, theta2, w2;
|
||||
private float lengthScaler;
|
||||
private float totalM;
|
||||
|
||||
private Vector2 previousPosition;
|
||||
|
||||
// Scale length
|
||||
private float L1;
|
||||
private float L2;
|
||||
|
||||
// Draw parameters
|
||||
private float lineThick, trailThick;
|
||||
private float fateAlpha;
|
||||
|
||||
// Create framebuffer
|
||||
private RenderTexture2D target;
|
||||
|
||||
// Calculate pendulum end point
|
||||
private static Vector2 CalculatePendulumEndPoint(float l, float theta)
|
||||
{
|
||||
return new(10 * l * MathF.Sin(theta), 10 * l * MathF.Cos(theta));
|
||||
}
|
||||
|
||||
// Calculate double pendulum end point
|
||||
private static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2)
|
||||
{
|
||||
Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1);
|
||||
Vector2 endpoint2 = CalculatePendulumEndPoint(l2, theta2);
|
||||
return new(endpoint1.X + endpoint2.X, endpoint1.Y + endpoint2.Y);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Simulation Parameters
|
||||
l1 = 15.0f; m1 = 0.2f; theta1 = DEG2RAD * 170; w1 = 0;
|
||||
l2 = 15.0f; m2 = 0.1f; theta2 = DEG2RAD * 0; w2 = 0;
|
||||
lengthScaler = 0.1f;
|
||||
totalM = m1 + m2;
|
||||
|
||||
previousPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2);
|
||||
previousPosition.X += ((float)screenWidth / 2);
|
||||
previousPosition.Y += ((float)screenHeight / 2 - 100);
|
||||
|
||||
// Scale length
|
||||
L1 = l1 * lengthScaler;
|
||||
L2 = l2 * lengthScaler;
|
||||
|
||||
// Draw parameters
|
||||
lineThick = 20; trailThick = 2;
|
||||
fateAlpha = 0.01f;
|
||||
|
||||
// Create framebuffer
|
||||
target = LoadRenderTexture(screenWidth, screenHeight);
|
||||
SetTextureFilter(target.Texture, TextureFilter.Bilinear);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
float dt = GetFrameTime();
|
||||
float step = dt / SIMULATION_STEPS, step2 = step * step;
|
||||
|
||||
// Update Physics - larger steps = better approximation
|
||||
for (int i = 0; i < SIMULATION_STEPS; i++)
|
||||
{
|
||||
float delta = theta1 - theta2;
|
||||
float sinD = MathF.Sin(delta), cosD = MathF.Cos(delta), cos2D = MathF.Cos(2 * delta);
|
||||
float ww1 = w1 * w1, ww2 = w2 * w2;
|
||||
|
||||
// Calculate a1
|
||||
float a1 = (-G * (2 * m1 + m2) * MathF.Sin(theta1)
|
||||
- m2 * G * MathF.Sin(theta1 - 2 * theta2)
|
||||
- 2 * sinD * m2 * (ww2 * L2 + ww1 * L1 * cosD))
|
||||
/ (L1 * (2 * m1 + m2 - m2 * cos2D));
|
||||
|
||||
// Calculate a2
|
||||
float a2 = (2 * sinD * (ww1 * L1 * totalM
|
||||
+ G * totalM * MathF.Cos(theta1)
|
||||
+ ww2 * L2 * m2 * cosD))
|
||||
/ (L2 * (2 * m1 + m2 - m2 * cos2D));
|
||||
|
||||
// Update thetas
|
||||
theta1 += w1 * step + 0.5f * a1 * step2;
|
||||
theta2 += w2 * step + 0.5f * a2 * step2;
|
||||
|
||||
// Update omegas
|
||||
w1 += a1 * step;
|
||||
w2 += a2 * step;
|
||||
}
|
||||
|
||||
// Calculate position
|
||||
Vector2 currentPosition = CalculateDoublePendulumEndPoint(l1, theta1, l2, theta2);
|
||||
currentPosition.X += (float)screenWidth / 2;
|
||||
currentPosition.Y += (float)screenHeight / 2 - 100;
|
||||
|
||||
// Draw to render texture
|
||||
BeginTextureMode(target);
|
||||
// Draw a transparent rectangle - smaller alpha = longer trails
|
||||
DrawRectangle(0, 0, screenWidth, screenHeight, Fade(Color.Black, fateAlpha));
|
||||
|
||||
// Draw trail
|
||||
DrawCircleV(previousPosition, trailThick, Color.Red);
|
||||
DrawLineEx(previousPosition, currentPosition, trailThick * 2, Color.Red);
|
||||
EndTextureMode();
|
||||
|
||||
// Update previous position
|
||||
previousPosition = currentPosition;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.Black);
|
||||
|
||||
// Draw trails texture
|
||||
DrawTextureRec(target.Texture, new Rectangle(0, 0, (float)target.Texture.Width, (float)-target.Texture.Height), new Vector2(0, 0), Color.White);
|
||||
|
||||
// Draw double pendulum
|
||||
DrawRectanglePro(new Rectangle(screenWidth / 2.0f, screenHeight / 2.0f - 100, 10 * l1, lineThick),
|
||||
new Vector2(0, lineThick * 0.5f), 90 - RAD2DEG * theta1, Color.RayWhite);
|
||||
|
||||
Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1);
|
||||
DrawRectanglePro(new Rectangle(screenWidth / 2.0f + endpoint1.X, screenHeight / 2.0f - 100 + endpoint1.Y, 10 * l2, lineThick),
|
||||
new Vector2(0, lineThick * 0.5f), 90 - RAD2DEG * theta2, Color.RayWhite);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
UnloadRenderTexture(target);
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.HighDpiWindow);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - double pendulum");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new DoublePendulum();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -75,11 +75,11 @@ public partial class DrawCircleSector : IExample
|
|||
|
||||
// Draw GUI controls
|
||||
//------------------------------------------------------------------------------
|
||||
/*GuiSliderBar(new Rectangle( 600, 40, 120, 20), "StartAngle", TextFormat("%.2f", startAngle), ref startAngle, 0, 720);
|
||||
GuiSliderBar(new Rectangle( 600, 70, 120, 20), "EndAngle", TextFormat("%.2f", endAngle), ref endAngle, 0, 720);
|
||||
GuiSliderBar(new Rectangle(600, 40, 120, 20), "StartAngle", $"{startAngle:F2}", ref startAngle, 0, 720);
|
||||
GuiSliderBar(new Rectangle(600, 70, 120, 20), "EndAngle", $"{endAngle:F2}", ref endAngle, 0, 720);
|
||||
|
||||
GuiSliderBar(new Rectangle( 600, 140, 120, 20), "Radius", TextFormat("%.2f", outerRadius), ref outerRadius, 0, 200);
|
||||
GuiSliderBar(new Rectangle( 600, 170, 120, 20), "Segments", TextFormat("%.2f", segments), ref segments, 0, 100);*/
|
||||
GuiSliderBar(new Rectangle(600, 140, 120, 20), "Radius", $"{outerRadius:F2}", ref outerRadius, 0, 200);
|
||||
GuiSliderBar(new Rectangle(600, 170, 120, 20), "Segments", $"{segments:F2}", ref segments, 0, 100);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
minSegments = MathF.Truncate(MathF.Ceiling((endAngle - startAngle) / 90));
|
||||
|
|
@ -96,6 +96,28 @@ public partial class DrawCircleSector : IExample
|
|||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// 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 (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
|
|
|
|||
|
|
@ -86,15 +86,15 @@ public partial class DrawRectangleRounded : IExample
|
|||
|
||||
// Draw GUI controls
|
||||
//------------------------------------------------------------------------------
|
||||
/*GuiSliderBar(new Rectangle( 640, 40, 105, 20 ), "Width", TextFormat("%.2f", width), ref width, 0, (float)GetScreenWidth() - 300);
|
||||
GuiSliderBar(new Rectangle( 640, 70, 105, 20 ), "Height", TextFormat("%.2f", height), ref height, 0, (float)GetScreenHeight() - 50);
|
||||
GuiSliderBar(new Rectangle( 640, 140, 105, 20 ), "Roundness", TextFormat("%.2f", roundness), ref roundness, 0.0f, 1.0f);
|
||||
GuiSliderBar(new Rectangle( 640, 170, 105, 20 ), "Thickness", TextFormat("%.2f", lineThick), ref lineThick, 0, 20);
|
||||
GuiSliderBar(new Rectangle( 640, 240, 105, 20), "Segments", TextFormat("%.2f", segments), ref segments, 0, 60);
|
||||
GuiSliderBar(new Rectangle(640, 40, 105, 20), "Width", $"{width:F2}", ref width, 0, (float)GetScreenWidth() - 300);
|
||||
GuiSliderBar(new Rectangle(640, 70, 105, 20), "Height", $"{height:F2}", ref height, 0, (float)GetScreenHeight() - 50);
|
||||
GuiSliderBar(new Rectangle(640, 140, 105, 20), "Roundness", $"{roundness:F2}", ref roundness, 0.0f, 1.0f);
|
||||
GuiSliderBar(new Rectangle(640, 170, 105, 20), "Thickness", $"{lineThick:F2}", ref lineThick, 0, 20);
|
||||
GuiSliderBar(new Rectangle(640, 240, 105, 20), "Segments", $"{segments:F2}", ref segments, 0, 60);
|
||||
|
||||
GuiCheckBox(new Rectangle( 640, 320, 20, 20 ), "DrawRoundedRect", ref drawRoundedRect);
|
||||
GuiCheckBox(new Rectangle( 640, 350, 20, 20 ), "DrawRoundedLines", ref drawRoundedLines);
|
||||
GuiCheckBox(new Rectangle( 640, 380, 20, 20), "DrawRect", ref drawRect);*/
|
||||
GuiCheckBox(new Rectangle(640, 320, 20, 20), "DrawRoundedRect", ref drawRoundedRect);
|
||||
GuiCheckBox(new Rectangle(640, 350, 20, 20), "DrawRoundedLines", ref drawRoundedLines);
|
||||
GuiCheckBox(new Rectangle(640, 380, 20, 20), "DrawRect", ref drawRect);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var text = $"MODE: {((segments >= 4) ? "MANUAL" : "AUTO")}";
|
||||
|
|
@ -109,6 +109,39 @@ public partial class DrawRectangleRounded : IExample
|
|||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// 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 (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) 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
|
||||
|
|
|
|||
|
|
@ -112,17 +112,17 @@ public partial class DrawRing : IExample
|
|||
|
||||
// Draw GUI controls
|
||||
//------------------------------------------------------------------------------
|
||||
/*GuiSliderBar(new Rectangle( 600, 40, 120, 20 ), "StartAngle", TextFormat("%.2f", startAngle), ref startAngle, -450, 450);
|
||||
GuiSliderBar(new Rectangle( 600, 70, 120, 20 ), "EndAngle", TextFormat("%.2f", endAngle), ref endAngle, -450, 450);
|
||||
GuiSliderBar(new Rectangle(600, 40, 120, 20), "StartAngle", $"{startAngle:F2}", ref startAngle, -450, 450);
|
||||
GuiSliderBar(new Rectangle(600, 70, 120, 20), "EndAngle", $"{endAngle:F2}", ref endAngle, -450, 450);
|
||||
|
||||
GuiSliderBar(new Rectangle( 600, 140, 120, 20 ), "InnerRadius", TextFormat("%.2f", innerRadius), ref innerRadius, 0, 100);
|
||||
GuiSliderBar(new Rectangle( 600, 170, 120, 20 ), "OuterRadius", TextFormat("%.2f", outerRadius), ref outerRadius, 0, 200);
|
||||
GuiSliderBar(new Rectangle(600, 140, 120, 20), "InnerRadius", $"{innerRadius:F2}", ref innerRadius, 0, 100);
|
||||
GuiSliderBar(new Rectangle(600, 170, 120, 20), "OuterRadius", $"{outerRadius:F2}", ref outerRadius, 0, 200);
|
||||
|
||||
GuiSliderBar(new Rectangle( 600, 240, 120, 20 ), "Segments", TextFormat("%.2f", segments), ref segments, 0, 100);
|
||||
GuiSliderBar(new Rectangle(600, 240, 120, 20), "Segments", $"{segments:F2}", ref segments, 0, 100);
|
||||
|
||||
GuiCheckBox(new Rectangle( 600, 320, 20, 20 ), "Draw Ring", ref drawRing);
|
||||
GuiCheckBox(new Rectangle( 600, 350, 20, 20 ), "Draw RingLines", ref drawRingLines);
|
||||
GuiCheckBox(new Rectangle( 600, 380, 20, 20 ), "Draw CircleLines", ref drawCircleLines);*/
|
||||
GuiCheckBox(new Rectangle(600, 320, 20, 20), "Draw Ring", ref drawRing);
|
||||
GuiCheckBox(new Rectangle(600, 350, 20, 20), "Draw RingLines", ref drawRingLines);
|
||||
GuiCheckBox(new Rectangle(600, 380, 20, 20), "Draw CircleLines", ref drawCircleLines);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
var minSegments = (int)MathF.Ceiling((endAngle - startAngle) / 90);
|
||||
|
|
@ -139,6 +139,39 @@ public partial class DrawRing : IExample
|
|||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// 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 (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) 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
|
||||
|
|
|
|||
230
Examples/Shapes/EasingsTestbed.cs
Normal file
230
Examples/Shapes/EasingsTestbed.cs
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - easings testbed
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 2.5, last time updated with raylib 2.5
|
||||
*
|
||||
* Example contributed by Juan Miguel López (@flashback-fx) 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) 2019-2025 Juan Miguel López (@flashback-fx) and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using Examples.Shared;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class EasingsTestbed : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int FONT_SIZE = 20;
|
||||
|
||||
private const float D_STEP = 20.0f;
|
||||
private const float D_STEP_FINE = 2.0f;
|
||||
private const float D_MIN = 1.0f;
|
||||
private const float D_MAX = 10000.0f;
|
||||
|
||||
public string Name => "Shapes / Easings Testbed";
|
||||
|
||||
public string Title => "raylib [shapes] example - easings testbed";
|
||||
|
||||
// Easing types
|
||||
private const int EASE_LINEAR_NONE = 0;
|
||||
private const int NUM_EASING_TYPES = 27;
|
||||
private const int EASING_NONE = NUM_EASING_TYPES;
|
||||
|
||||
// NoEase function, used when "no easing" is selected for any axis
|
||||
// It just ignores all parameters besides b
|
||||
private static float NoEase(float t, float b, float c, float d)
|
||||
{
|
||||
// Hack to avoid compiler warning (about unused variables)
|
||||
float burn = t + b + c + d;
|
||||
d += burn;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
// Easing functions reference data
|
||||
private string[] easingNames;
|
||||
private Func<float, float, float, float, float>[] easingFuncs;
|
||||
|
||||
private Vector2 ballPosition;
|
||||
private float t; // Current time (in any unit measure, but same unit as duration)
|
||||
private float d; // Total time it should take to complete (duration)
|
||||
private bool paused;
|
||||
private bool boundedT; // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop
|
||||
|
||||
private int easingX; // Easing selected for x axis
|
||||
private int easingY; // Easing selected for y axis
|
||||
|
||||
public void Init()
|
||||
{
|
||||
easingNames = new string[]
|
||||
{
|
||||
"EaseLinearNone", "EaseLinearIn", "EaseLinearOut", "EaseLinearInOut",
|
||||
"EaseSineIn", "EaseSineOut", "EaseSineInOut",
|
||||
"EaseCircIn", "EaseCircOut", "EaseCircInOut",
|
||||
"EaseCubicIn", "EaseCubicOut", "EaseCubicInOut",
|
||||
"EaseQuadIn", "EaseQuadOut", "EaseQuadInOut",
|
||||
"EaseExpoIn", "EaseExpoOut", "EaseExpoInOut",
|
||||
"EaseBackIn", "EaseBackOut", "EaseBackInOut",
|
||||
"EaseBounceOut", "EaseBounceIn", "EaseBounceInOut",
|
||||
"EaseElasticIn", "EaseElasticOut", "EaseElasticInOut",
|
||||
"None",
|
||||
};
|
||||
|
||||
easingFuncs = new Func<float, float, float, float, float>[]
|
||||
{
|
||||
Easings.EaseLinearNone, Easings.EaseLinearIn, Easings.EaseLinearOut, Easings.EaseLinearInOut,
|
||||
Easings.EaseSineIn, Easings.EaseSineOut, Easings.EaseSineInOut,
|
||||
Easings.EaseCircIn, Easings.EaseCircOut, Easings.EaseCircInOut,
|
||||
Easings.EaseCubicIn, Easings.EaseCubicOut, Easings.EaseCubicInOut,
|
||||
Easings.EaseQuadIn, Easings.EaseQuadOut, Easings.EaseQuadInOut,
|
||||
Easings.EaseExpoIn, Easings.EaseExpoOut, Easings.EaseExpoInOut,
|
||||
Easings.EaseBackIn, Easings.EaseBackOut, Easings.EaseBackInOut,
|
||||
Easings.EaseBounceOut, Easings.EaseBounceIn, Easings.EaseBounceInOut,
|
||||
Easings.EaseElasticIn, Easings.EaseElasticOut, Easings.EaseElasticInOut,
|
||||
NoEase,
|
||||
};
|
||||
|
||||
ballPosition = new Vector2(100.0f, 100.0f);
|
||||
|
||||
t = 0.0f;
|
||||
d = 300.0f;
|
||||
paused = true;
|
||||
boundedT = true;
|
||||
|
||||
easingX = EASING_NONE;
|
||||
easingY = EASING_NONE;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyPressed(KeyboardKey.T)) boundedT = !boundedT;
|
||||
|
||||
// Choose easing for the X axis
|
||||
if (IsKeyPressed(KeyboardKey.Right))
|
||||
{
|
||||
easingX++;
|
||||
|
||||
if (easingX > EASING_NONE) easingX = 0;
|
||||
}
|
||||
else if (IsKeyPressed(KeyboardKey.Left))
|
||||
{
|
||||
if (easingX == 0) easingX = EASING_NONE;
|
||||
else easingX--;
|
||||
}
|
||||
|
||||
// Choose easing for the Y axis
|
||||
if (IsKeyPressed(KeyboardKey.Down))
|
||||
{
|
||||
easingY++;
|
||||
|
||||
if (easingY > EASING_NONE) easingY = 0;
|
||||
}
|
||||
else if (IsKeyPressed(KeyboardKey.Up))
|
||||
{
|
||||
if (easingY == 0) easingY = EASING_NONE;
|
||||
else easingY--;
|
||||
}
|
||||
|
||||
// Change d (duration) value
|
||||
if (IsKeyPressed(KeyboardKey.W) && (d < D_MAX - D_STEP)) d += D_STEP;
|
||||
else if (IsKeyPressed(KeyboardKey.Q) && (d > D_MIN + D_STEP)) d -= D_STEP;
|
||||
|
||||
if (IsKeyDown(KeyboardKey.S) && (d < D_MAX - D_STEP_FINE)) d += D_STEP_FINE;
|
||||
else if (IsKeyDown(KeyboardKey.A) && (d > D_MIN + D_STEP_FINE)) d -= D_STEP_FINE;
|
||||
|
||||
// Play, pause and restart controls
|
||||
if (IsKeyPressed(KeyboardKey.Space) || IsKeyPressed(KeyboardKey.T) ||
|
||||
IsKeyPressed(KeyboardKey.Right) || IsKeyPressed(KeyboardKey.Left) ||
|
||||
IsKeyPressed(KeyboardKey.Down) || IsKeyPressed(KeyboardKey.Up) ||
|
||||
IsKeyPressed(KeyboardKey.W) || IsKeyPressed(KeyboardKey.Q) ||
|
||||
IsKeyDown(KeyboardKey.S) || IsKeyDown(KeyboardKey.A) ||
|
||||
(IsKeyPressed(KeyboardKey.Enter) && (boundedT == true) && (t >= d)))
|
||||
{
|
||||
t = 0.0f;
|
||||
ballPosition.X = 100.0f;
|
||||
ballPosition.Y = 100.0f;
|
||||
paused = true;
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.Enter)) paused = !paused;
|
||||
|
||||
// Movement computation
|
||||
if (!paused && ((boundedT && t < d) || !boundedT))
|
||||
{
|
||||
ballPosition.X = easingFuncs[easingX](t, 100.0f, 700.0f - 170.0f, d);
|
||||
ballPosition.Y = easingFuncs[easingY](t, 100.0f, 400.0f - 170.0f, d);
|
||||
t += 1.0f;
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw information text
|
||||
DrawText($"Easing x: {easingNames[easingX]}", 20, FONT_SIZE, FONT_SIZE, Color.LightGray);
|
||||
DrawText($"Easing y: {easingNames[easingY]}", 20, FONT_SIZE * 2, FONT_SIZE, Color.LightGray);
|
||||
DrawText($"t ({(boundedT == true ? 'b' : 'u')}) = {t:F2} d = {d:F2}", 20, FONT_SIZE * 3, FONT_SIZE, Color.LightGray);
|
||||
|
||||
// Draw instructions text
|
||||
DrawText("Use ENTER to play or pause movement, use SPACE to restart", 20, GetScreenHeight() - FONT_SIZE * 2, FONT_SIZE, Color.LightGray);
|
||||
DrawText("Use Q and W or A and S keys to change duration", 20, GetScreenHeight() - FONT_SIZE * 3, FONT_SIZE, Color.LightGray);
|
||||
DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 20, GetScreenHeight() - FONT_SIZE * 4, FONT_SIZE, Color.LightGray);
|
||||
DrawText("Use UP or DOWN keys to choose easing for the y axis", 20, GetScreenHeight() - FONT_SIZE * 5, FONT_SIZE, Color.LightGray);
|
||||
|
||||
// Draw ball
|
||||
DrawCircleV(ballPosition, 16.0f, Color.Maroon);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - easings testbed");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new EasingsTestbed();
|
||||
game.Init();
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
game.Update();
|
||||
}
|
||||
|
||||
game.Unload();
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow();
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
195
Examples/Shapes/EllipseCollision.cs
Normal file
195
Examples/Shapes/EllipseCollision.cs
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - ellipse collision
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
||||
*
|
||||
* Example contributed by Ziya (@Monjaris)
|
||||
*
|
||||
* 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 Ziya (@Monjaris)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class EllipseCollision : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Ellipse Collision";
|
||||
|
||||
public string Title => "raylib [shapes] example - collision ellipses";
|
||||
|
||||
private Vector2 ellipseACenter;
|
||||
private float ellipseARx;
|
||||
private float ellipseARy;
|
||||
|
||||
private Vector2 ellipseBCenter;
|
||||
private float ellipseBRx;
|
||||
private float ellipseBRy;
|
||||
|
||||
// 0 = controlling A, 1 = controlling B
|
||||
private int controlled;
|
||||
|
||||
// Check if point is inside ellipse
|
||||
private static bool CheckCollisionPointEllipse(Vector2 point, Vector2 center, float rx, float ry)
|
||||
{
|
||||
float dx = (point.X - center.X) / rx;
|
||||
float dy = (point.Y - center.Y) / ry;
|
||||
return (dx * dx + dy * dy) <= 1.0f;
|
||||
}
|
||||
|
||||
// Check if two ellipses collide
|
||||
// Uses radial boundary distance in the direction between centers — scales correctly with radii
|
||||
private static bool CheckCollisionEllipses(Vector2 c1, float rx1, float ry1, Vector2 c2, float rx2, float ry2)
|
||||
{
|
||||
float dx = c2.X - c1.X;
|
||||
float dy = c2.Y - c1.Y;
|
||||
float dist = MathF.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
// Ellipses are on top of each other
|
||||
if (dist == 0.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float theta = MathF.Atan2(dy, dx);
|
||||
float cosT = MathF.Cos(theta);
|
||||
float sinT = MathF.Sin(theta);
|
||||
|
||||
// Radial distance from center to ellipse boundary in direction theta
|
||||
// r(theta) = (rx * ry) / sqrt((ry*cos)^2 + (rx*sin)^2)
|
||||
float r1 = (rx1 * ry1) / MathF.Sqrt((ry1 * cosT) * (ry1 * cosT) + (rx1 * sinT) * (rx1 * sinT));
|
||||
float r2 = (rx2 * ry2) / MathF.Sqrt((ry2 * cosT) * (ry2 * cosT) + (rx2 * sinT) * (rx2 * sinT));
|
||||
|
||||
return dist <= (r1 + r2);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
ellipseACenter = new((float)screenWidth / 4, (float)screenHeight / 2);
|
||||
ellipseARx = 120.0f;
|
||||
ellipseARy = 70.0f;
|
||||
|
||||
ellipseBCenter = new((float)screenWidth * 3 / 4, (float)screenHeight / 2);
|
||||
ellipseBRx = 90.0f;
|
||||
ellipseBRy = 140.0f;
|
||||
|
||||
// 0 = controlling A, 1 = controlling B
|
||||
controlled = 0;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyPressed(KeyboardKey.A))
|
||||
{
|
||||
controlled = 0;
|
||||
}
|
||||
if (IsKeyPressed(KeyboardKey.B))
|
||||
{
|
||||
controlled = 1;
|
||||
}
|
||||
|
||||
if (controlled == 0)
|
||||
{
|
||||
ellipseACenter = GetMousePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
ellipseBCenter = GetMousePosition();
|
||||
}
|
||||
|
||||
bool ellipsesCollide = CheckCollisionEllipses(
|
||||
ellipseACenter, ellipseARx, ellipseARy,
|
||||
ellipseBCenter, ellipseBRx, ellipseBRy
|
||||
);
|
||||
|
||||
bool mouseInA = CheckCollisionPointEllipse(GetMousePosition(), ellipseACenter, ellipseARx, ellipseARy);
|
||||
bool mouseInB = CheckCollisionPointEllipse(GetMousePosition(), ellipseBCenter, ellipseBRx, ellipseBRy);
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
DrawEllipse((int)ellipseACenter.X, (int)ellipseACenter.Y, ellipseARx, ellipseARy, ellipsesCollide ? Color.Red : Color.Blue);
|
||||
|
||||
DrawEllipse((int)ellipseBCenter.X, (int)ellipseBCenter.Y, ellipseBRx, ellipseBRy, ellipsesCollide ? Color.Red : Color.Green);
|
||||
|
||||
DrawEllipseLines((int)ellipseACenter.X, (int)ellipseACenter.Y, ellipseARx, ellipseARy, Color.White);
|
||||
|
||||
DrawEllipseLines((int)ellipseBCenter.X, (int)ellipseBCenter.Y, ellipseBRx, ellipseBRy, Color.White);
|
||||
|
||||
DrawCircleV(ellipseACenter, 4, Color.White);
|
||||
DrawCircleV(ellipseBCenter, 4, Color.White);
|
||||
|
||||
if (ellipsesCollide)
|
||||
{
|
||||
DrawText("ELLIPSES COLLIDE", screenWidth / 2 - 120, 40, 28, Color.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawText("NO COLLISION", screenWidth / 2 - 80, 40, 28, Color.DarkGray);
|
||||
}
|
||||
|
||||
DrawText(controlled == 0 ? "Controlling: A" : "Controlling: B", 20, screenHeight - 40, 20, Color.Yellow);
|
||||
|
||||
if (mouseInA && controlled != 0)
|
||||
{
|
||||
DrawText("Mouse inside ellipse A", 20, screenHeight - 70, 20, Color.Blue);
|
||||
}
|
||||
if (mouseInB && controlled != 1)
|
||||
{
|
||||
DrawText("Mouse inside ellipse B", 20, screenHeight - 70, 20, Color.Green);
|
||||
}
|
||||
|
||||
DrawText("Press [A] or [B] to switch control", 20, 20, 20, Color.Gray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - collision ellipses");
|
||||
SetTargetFPS(60);
|
||||
|
||||
var game = new EllipseCollision();
|
||||
game.Init();
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose()) // Detect window close button or ESC key
|
||||
{
|
||||
game.Update();
|
||||
}
|
||||
|
||||
game.Unload();
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow();
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
270
Examples/Shapes/HilbertCurve.cs
Normal file
270
Examples/Shapes/HilbertCurve.cs
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - hilbert curve
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 5.6, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Hamza RAHAL (@hmz-rhl) 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 Hamza RAHAL (@hmz-rhl)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class HilbertCurve : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Hilbert Curve";
|
||||
|
||||
public string Title => "raylib [shapes] example - hilbert curve";
|
||||
|
||||
private int order;
|
||||
private float size;
|
||||
private int strokeCount;
|
||||
private Vector2[] hilbertPath;
|
||||
|
||||
private int prevOrder;
|
||||
private int prevSize; // NOTE: Size from slider is float but for comparison we use int
|
||||
private int counter;
|
||||
private float thick;
|
||||
private bool animate;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
order = 2;
|
||||
size = (float)GetScreenHeight();
|
||||
strokeCount = 0;
|
||||
hilbertPath = LoadHilbertPath(order, size, out strokeCount);
|
||||
|
||||
prevOrder = order;
|
||||
prevSize = (int)size;
|
||||
counter = 0;
|
||||
thick = 2.0f;
|
||||
animate = true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Check if order or size have changed to regenerate
|
||||
// NOTE: Size from slider is float but for comparison we use int
|
||||
if ((prevOrder != order) || (prevSize != (int)size))
|
||||
{
|
||||
hilbertPath = LoadHilbertPath(order, size, out strokeCount);
|
||||
|
||||
if (animate) counter = 0;
|
||||
else counter = strokeCount;
|
||||
|
||||
prevOrder = order;
|
||||
prevSize = (int)size;
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//--------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
if (counter < strokeCount)
|
||||
{
|
||||
// Draw Hilbert path animation, one stroke every frame
|
||||
for (int i = 1; i <= counter; i++)
|
||||
{
|
||||
DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i / strokeCount) * 360.0f, 1.0f, 1.0f));
|
||||
}
|
||||
|
||||
counter += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw full Hilbert path
|
||||
for (int i = 1; i < strokeCount; i++)
|
||||
{
|
||||
DrawLineEx(hilbertPath[i], hilbertPath[i - 1], thick, ColorFromHSV(((float)i / strokeCount) * 360.0f, 1.0f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// Draw UI (minimal raygui-like controls, raygui is not bound in raylib-cs)
|
||||
GuiCheckBox(new Rectangle(450, 50, 20, 20), "ANIMATE GENERATION ON CHANGE", ref animate);
|
||||
GuiSpinner(new Rectangle(585, 100, 180, 30), "HILBERT CURVE ORDER: ", ref order, 2, 8);
|
||||
GuiSlider(new Rectangle(524, 150, 240, 24), "THICKNESS: ", null, ref thick, 1.0f, 10.0f);
|
||||
GuiSlider(new Rectangle(524, 190, 240, 24), "TOTAL SIZE: ", null, ref size, 10.0f, GetScreenHeight() * 1.5f);
|
||||
|
||||
EndDrawing();
|
||||
//--------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//------------------------------------------------------------------------------------
|
||||
// Load the whole Hilbert Path (including each U and their link)
|
||||
private static Vector2[] LoadHilbertPath(int order, float size, out int strokeCount)
|
||||
{
|
||||
int N = 1 << order;
|
||||
float len = size / N;
|
||||
strokeCount = N * N;
|
||||
|
||||
Vector2[] hilbertPath = new Vector2[strokeCount];
|
||||
|
||||
for (int i = 0; i < strokeCount; i++)
|
||||
{
|
||||
hilbertPath[i] = ComputeHilbertStep(order, i);
|
||||
hilbertPath[i].X = hilbertPath[i].X * len + len / 2.0f;
|
||||
hilbertPath[i].Y = hilbertPath[i].Y * len + len / 2.0f;
|
||||
}
|
||||
|
||||
return hilbertPath;
|
||||
}
|
||||
|
||||
// Compute Hilbert path U positions
|
||||
private static Vector2 ComputeHilbertStep(int order, int index)
|
||||
{
|
||||
// Hilbert points base pattern
|
||||
Vector2[] hilbertPoints = new Vector2[4]
|
||||
{
|
||||
new Vector2(0, 0),
|
||||
new Vector2(0, 1),
|
||||
new Vector2(1, 1),
|
||||
new Vector2(1, 0),
|
||||
};
|
||||
|
||||
int hilbertIndex = index & 3;
|
||||
Vector2 vect = hilbertPoints[hilbertIndex];
|
||||
float temp = 0.0f;
|
||||
int len = 0;
|
||||
|
||||
for (int j = 1; j < order; j++)
|
||||
{
|
||||
index = index >> 2;
|
||||
hilbertIndex = index & 3;
|
||||
len = 1 << j;
|
||||
|
||||
switch (hilbertIndex)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
temp = vect.X;
|
||||
vect.X = vect.Y;
|
||||
vect.Y = temp;
|
||||
} break;
|
||||
case 2:
|
||||
{
|
||||
vect.X += len;
|
||||
vect.Y += len;
|
||||
} break;
|
||||
case 1: vect.Y += len; break;
|
||||
case 3:
|
||||
{
|
||||
temp = len - 1 - vect.X;
|
||||
vect.X = 2 * len - 1 - vect.Y;
|
||||
vect.Y = temp;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return vect;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Minimal raygui-like widgets (plain raylib re-implementation)
|
||||
//----------------------------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
||||
private static void GuiSpinner(Rectangle bounds, string text, ref int value, int minValue, int maxValue)
|
||||
{
|
||||
Vector2 mouse = GetMousePosition();
|
||||
Rectangle left = new Rectangle(bounds.X, bounds.Y, bounds.Height, bounds.Height);
|
||||
Rectangle right = new Rectangle(bounds.X + bounds.Width - bounds.Height, bounds.Y, bounds.Height, bounds.Height);
|
||||
Rectangle mid = new Rectangle(bounds.X + bounds.Height, bounds.Y, bounds.Width - 2 * bounds.Height, bounds.Height);
|
||||
|
||||
if (CheckCollisionPointRec(mouse, left) && IsMouseButtonPressed(MouseButton.Left) && value > minValue) value--;
|
||||
if (CheckCollisionPointRec(mouse, right) && IsMouseButtonPressed(MouseButton.Left) && value < maxValue) value++;
|
||||
|
||||
DrawRectangleRec(mid, Color.RayWhite);
|
||||
DrawRectangleLinesEx(mid, 1, Color.Gray);
|
||||
DrawRectangleRec(left, Color.LightGray);
|
||||
DrawRectangleLinesEx(left, 1, Color.Gray);
|
||||
DrawRectangleRec(right, Color.LightGray);
|
||||
DrawRectangleLinesEx(right, 1, Color.Gray);
|
||||
DrawText("-", (int)(left.X + left.Width / 2 - 2), (int)(left.Y + left.Height / 2 - 5), 10, Color.DarkGray);
|
||||
DrawText("+", (int)(right.X + right.Width / 2 - 3), (int)(right.Y + right.Height / 2 - 5), 10, Color.DarkGray);
|
||||
string vs = value.ToString();
|
||||
DrawText(vs, (int)(mid.X + mid.Width / 2 - MeasureText(vs, 10) / 2), (int)(mid.Y + mid.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (text != null) DrawText(text, (int)bounds.X - MeasureText(text, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
private static void GuiSlider(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);
|
||||
float handleX = bounds.X + pct * bounds.Width;
|
||||
DrawRectangle((int)(handleX - 5), (int)bounds.Y, 10, (int)bounds.Height, Color.DarkGray);
|
||||
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);
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - hilbert curve");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new HilbertCurve();
|
||||
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;
|
||||
}
|
||||
}
|
||||
224
Examples/Shapes/Kaleidoscope.cs
Normal file
224
Examples/Shapes/Kaleidoscope.cs
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - kaleidoscope
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Hugo ARNAL (@hugoarnal) 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 Hugo ARNAL (@hugoarnal) and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class Kaleidoscope : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_DRAW_LINES = 8192;
|
||||
|
||||
// Line data type
|
||||
private struct Line
|
||||
{
|
||||
public Vector2 Start;
|
||||
public Vector2 End;
|
||||
}
|
||||
|
||||
public string Name => "Shapes / Kaleidoscope";
|
||||
|
||||
public string Title => "raylib [shapes] example - kaleidoscope";
|
||||
|
||||
public int TargetFps => 20;
|
||||
|
||||
// Lines array as a global static variable to be stored
|
||||
// in heap and avoid potential stack overflow (on Web platform)
|
||||
private Line[] lines;
|
||||
|
||||
// Line drawing properties
|
||||
private int symmetry;
|
||||
private float angle;
|
||||
private float thickness;
|
||||
private Rectangle resetButtonRec;
|
||||
private Rectangle backButtonRec;
|
||||
private Rectangle nextButtonRec;
|
||||
private Vector2 mousePos;
|
||||
private Vector2 prevMousePos;
|
||||
private Vector2 scaleVector;
|
||||
private Vector2 offset;
|
||||
|
||||
private Camera2D camera;
|
||||
|
||||
private int currentLineCounter;
|
||||
private int totalLineCounter;
|
||||
private bool resetButtonClicked;
|
||||
private bool backButtonClicked;
|
||||
private bool nextButtonClicked;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
lines = new Line[MAX_DRAW_LINES];
|
||||
|
||||
// Line drawing properties
|
||||
symmetry = 6;
|
||||
angle = 360.0f / (float)symmetry;
|
||||
thickness = 3.0f;
|
||||
resetButtonRec = new(screenWidth - 55.0f, 5.0f, 50, 25);
|
||||
backButtonRec = new(screenWidth - 55.0f, screenHeight - 30.0f, 25, 25);
|
||||
nextButtonRec = new(screenWidth - 30.0f, screenHeight - 30.0f, 25, 25);
|
||||
mousePos = new(0, 0);
|
||||
prevMousePos = new(0, 0);
|
||||
scaleVector = new(1.0f, -1.0f);
|
||||
offset = new((float)screenWidth / 2.0f, (float)screenHeight / 2.0f);
|
||||
|
||||
camera = new();
|
||||
camera.Target = new(0, 0);
|
||||
camera.Offset = offset;
|
||||
camera.Rotation = 0.0f;
|
||||
camera.Zoom = 1.0f;
|
||||
|
||||
currentLineCounter = 0;
|
||||
totalLineCounter = 0;
|
||||
resetButtonClicked = false;
|
||||
backButtonClicked = false;
|
||||
nextButtonClicked = false;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
prevMousePos = mousePos;
|
||||
mousePos = GetMousePosition();
|
||||
|
||||
Vector2 lineStart = Vector2Subtract(mousePos, offset);
|
||||
Vector2 lineEnd = Vector2Subtract(prevMousePos, offset);
|
||||
|
||||
if (
|
||||
IsMouseButtonDown(MouseButton.Left)
|
||||
&& !CheckCollisionPointRec(mousePos, resetButtonRec)
|
||||
&& !CheckCollisionPointRec(mousePos, backButtonRec)
|
||||
&& !CheckCollisionPointRec(mousePos, nextButtonRec)
|
||||
)
|
||||
{
|
||||
for (int s = 0; (s < symmetry) && (totalLineCounter < (MAX_DRAW_LINES - 1)); s++)
|
||||
{
|
||||
lineStart = Vector2Rotate(lineStart, angle * DEG2RAD);
|
||||
lineEnd = Vector2Rotate(lineEnd, angle * DEG2RAD);
|
||||
|
||||
// Store mouse line
|
||||
lines[totalLineCounter].Start = lineStart;
|
||||
lines[totalLineCounter].End = lineEnd;
|
||||
|
||||
// Store reflective line
|
||||
lines[totalLineCounter + 1].Start = Vector2Multiply(lineStart, scaleVector);
|
||||
lines[totalLineCounter + 1].End = Vector2Multiply(lineEnd, scaleVector);
|
||||
|
||||
totalLineCounter += 2;
|
||||
currentLineCounter = totalLineCounter;
|
||||
}
|
||||
}
|
||||
|
||||
if (resetButtonClicked)
|
||||
{
|
||||
Array.Clear(lines, 0, MAX_DRAW_LINES);
|
||||
currentLineCounter = 0;
|
||||
totalLineCounter = 0;
|
||||
}
|
||||
|
||||
if (backButtonClicked && (currentLineCounter > 0))
|
||||
{
|
||||
currentLineCounter -= 1;
|
||||
}
|
||||
|
||||
if (nextButtonClicked && (currentLineCounter < MAX_DRAW_LINES) && ((currentLineCounter + 1) <= totalLineCounter))
|
||||
{
|
||||
currentLineCounter += 1;
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
BeginMode2D(camera);
|
||||
|
||||
for (int s = 0; s < symmetry; s++)
|
||||
{
|
||||
for (int i = 0; i < currentLineCounter; i += 2)
|
||||
{
|
||||
DrawLineEx(lines[i].Start, lines[i].End, thickness, Color.Black);
|
||||
DrawLineEx(lines[i + 1].Start, lines[i + 1].End, thickness, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
EndMode2D();
|
||||
|
||||
// NOTE: raygui is not bound in raylib-cs, so the on-screen back/next/reset
|
||||
// controls are unavailable; drawing with the mouse still works.
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
if ((currentLineCounter - 1) < 0) GuiDisable();
|
||||
|
||||
backButtonClicked = GuiButton(backButtonRec, "<");
|
||||
GuiEnable();
|
||||
|
||||
if ((currentLineCounter + 1) > totalLineCounter) GuiDisable();
|
||||
|
||||
nextButtonClicked = GuiButton(nextButtonRec, ">");
|
||||
GuiEnable();
|
||||
resetButtonClicked = GuiButton(resetButtonRec, "Reset");
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DrawText($"LINES: {currentLineCounter}/{MAX_DRAW_LINES}", 10, screenHeight - 30, 20, Color.Maroon);
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - kaleidoscope");
|
||||
|
||||
SetTargetFPS(20);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new Kaleidoscope();
|
||||
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;
|
||||
}
|
||||
}
|
||||
173
Examples/Shapes/LinesDrawing.cs
Normal file
173
Examples/Shapes/LinesDrawing.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - lines drawing
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class LinesDrawing : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Lines Drawing";
|
||||
|
||||
public string Title => "raylib [shapes] example - lines drawing";
|
||||
|
||||
// Hint text that shows before you click the screen
|
||||
private bool startText;
|
||||
|
||||
// The mouse's position on the previous frame
|
||||
private Vector2 mousePositionPrevious;
|
||||
|
||||
// The canvas to draw lines on
|
||||
private RenderTexture2D canvas;
|
||||
|
||||
// The line's thickness
|
||||
private float lineThickness;
|
||||
// The lines hue (in HSV, from 0-360)
|
||||
private float lineHue;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Hint text that shows before you click the screen
|
||||
startText = true;
|
||||
|
||||
// The mouse's position on the previous frame
|
||||
mousePositionPrevious = GetMousePosition();
|
||||
|
||||
// The canvas to draw lines on
|
||||
canvas = LoadRenderTexture(screenWidth, screenHeight);
|
||||
|
||||
// The line's thickness
|
||||
lineThickness = 8.0f;
|
||||
// The lines hue (in HSV, from 0-360)
|
||||
lineHue = 0.0f;
|
||||
|
||||
// Clear the canvas to the background color
|
||||
BeginTextureMode(canvas);
|
||||
ClearBackground(Color.RayWhite);
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Disable the hint text once the user clicks
|
||||
if (IsMouseButtonPressed(MouseButton.Left) && startText) startText = false;
|
||||
|
||||
// Clear the canvas when the user middle-clicks
|
||||
if (IsMouseButtonPressed(MouseButton.Middle))
|
||||
{
|
||||
BeginTextureMode(canvas);
|
||||
ClearBackground(Color.RayWhite);
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
// Store whether the left and right buttons are down
|
||||
bool leftButtonDown = IsMouseButtonDown(MouseButton.Left);
|
||||
bool rightButtonDown = IsMouseButtonDown(MouseButton.Right);
|
||||
|
||||
if (leftButtonDown || rightButtonDown)
|
||||
{
|
||||
// The color for the line
|
||||
Color drawColor = Color.White;
|
||||
|
||||
if (leftButtonDown)
|
||||
{
|
||||
// Increase the hue value by the distance our cursor has moved since the last frame (divided by 3)
|
||||
lineHue += Vector2Distance(mousePositionPrevious, GetMousePosition())/3.0f;
|
||||
|
||||
// While the hue is >=360, subtract it to bring it down into the range 0-360
|
||||
// This is more visually accurate than resetting to zero
|
||||
while (lineHue >= 360.0f) lineHue -= 360.0f;
|
||||
|
||||
// Create the final color
|
||||
drawColor = ColorFromHSV(lineHue, 1.0f, 1.0f);
|
||||
}
|
||||
else if (rightButtonDown) drawColor = Color.RayWhite; // Use the background color as an "eraser"
|
||||
|
||||
// Draw the line onto the canvas
|
||||
BeginTextureMode(canvas);
|
||||
// Circles act as "caps", smoothing corners
|
||||
DrawCircleV(mousePositionPrevious, lineThickness/2.0f, drawColor);
|
||||
DrawCircleV(GetMousePosition(), lineThickness/2.0f, drawColor);
|
||||
DrawLineEx(mousePositionPrevious, GetMousePosition(), lineThickness, drawColor);
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
// Update line thickness based on mousewheel
|
||||
lineThickness += GetMouseWheelMove();
|
||||
lineThickness = Clamp(lineThickness, 1.0f, 500.0f);
|
||||
|
||||
// Update mouse's previous position
|
||||
mousePositionPrevious = GetMousePosition();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
// Draw the render texture to the screen, flipped vertically to make it appear top-side up
|
||||
DrawTextureRec(canvas.Texture, new Rectangle(0.0f, 0.0f, (float)canvas.Texture.Width, (float)-canvas.Texture.Height), Vector2Zero(), Color.White);
|
||||
|
||||
// Draw the preview circle
|
||||
if (!leftButtonDown) DrawCircleLinesV(GetMousePosition(), lineThickness/2.0f, new Color(127, 127, 127, 127));
|
||||
|
||||
// Draw the hint text
|
||||
if (startText) DrawText("try clicking and dragging!", 275, 215, 20, Color.LightGray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
UnloadRenderTexture(canvas); // Unload the canvas render texture
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - lines drawing");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new LinesDrawing();
|
||||
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;
|
||||
}
|
||||
}
|
||||
141
Examples/Shapes/MathAngleRotation.cs
Normal file
141
Examples/Shapes/MathAngleRotation.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - math angle rotation
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Kris (@krispy-snacc) 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 Kris (@krispy-snacc)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class MathAngleRotation : IExample
|
||||
{
|
||||
private const int screenWidth = 720;
|
||||
private const int screenHeight = 400;
|
||||
|
||||
public string Name => "Shapes / Math Angle Rotation";
|
||||
|
||||
public string Title => "raylib [shapes] example - math angle rotation";
|
||||
|
||||
public int Width => screenWidth;
|
||||
|
||||
public int Height => screenHeight;
|
||||
|
||||
private Vector2 center;
|
||||
private const float lineLength = 150.0f;
|
||||
|
||||
// Predefined angles for fixed lines
|
||||
private int[] angles;
|
||||
private int numAngles;
|
||||
|
||||
private float totalAngle; // Animated rotation angle
|
||||
|
||||
public void Init()
|
||||
{
|
||||
center = new Vector2(screenWidth/2.0f, screenHeight/2.0f);
|
||||
|
||||
// Predefined angles for fixed lines
|
||||
angles = new[] { 0, 30, 60, 90 };
|
||||
numAngles = angles.Length;
|
||||
|
||||
totalAngle = 0.0f; // Animated rotation angle
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
totalAngle += 1.0f; // degrees per frame
|
||||
if (totalAngle >= 360.0f) totalAngle -= 360.0f;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.White);
|
||||
|
||||
DrawText("Fixed angles + rotating line", 10, 10, 20, Color.LightGray);
|
||||
|
||||
// Draw fixed-angle lines with colorful gradient
|
||||
for (int i = 0; i < numAngles; i++)
|
||||
{
|
||||
float rad = angles[i]*DEG2RAD;
|
||||
Vector2 end = new Vector2(center.X + MathF.Cos(rad)*lineLength,
|
||||
center.Y + MathF.Sin(rad)*lineLength);
|
||||
|
||||
// Gradient color from green → cyan → blue → magenta
|
||||
Color col;
|
||||
switch(i)
|
||||
{
|
||||
case 0: col = Color.Green; break;
|
||||
case 1: col = Color.Orange; break;
|
||||
case 2: col = Color.Blue; break;
|
||||
case 3: col = Color.Magenta; break;
|
||||
default: col = Color.White; break;
|
||||
}
|
||||
|
||||
DrawLineEx(center, end, 5.0f, col);
|
||||
|
||||
// Draw angle label slightly offset along the line
|
||||
Vector2 textPos = new Vector2(center.X + MathF.Cos(rad)*(lineLength + 20),
|
||||
center.Y + MathF.Sin(rad)*(lineLength + 20));
|
||||
DrawText($"{angles[i]}°", (int)textPos.X, (int)textPos.Y, 20, col);
|
||||
}
|
||||
|
||||
// Draw animated rotating line with changing color
|
||||
float animRad = totalAngle*DEG2RAD;
|
||||
Vector2 animEnd = new Vector2(center.X + MathF.Cos(animRad)*lineLength,
|
||||
center.Y + MathF.Sin(animRad)*lineLength);
|
||||
|
||||
// Cycle through HSV colors for animated line
|
||||
Color animCol = ColorFromHSV(totalAngle % 360.0f, 0.8f, 0.9f);
|
||||
DrawLineEx(center, animEnd, 5.0f, animCol);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math angle rotation");
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new MathAngleRotation();
|
||||
game.Init();
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
game.Update();
|
||||
}
|
||||
|
||||
game.Unload();
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow();
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
212
Examples/Shapes/MathSineCosine.cs
Normal file
212
Examples/Shapes/MathSineCosine.cs
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - math sine cosine
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
||||
*
|
||||
* Example contributed by Jopestpe (@jopestpe) 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 Jopestpe (@jopestpe)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class MathSineCosine : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
// Wave points for sine/cosine visualization
|
||||
private const int WAVE_POINTS = 36;
|
||||
|
||||
public string Name => "Shapes / Math Sine Cosine";
|
||||
|
||||
public string Title => "raylib [shapes] example - math sine cosine";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
private Vector2[] sinePoints;
|
||||
private Vector2[] cosPoints;
|
||||
private Vector2 center;
|
||||
private Rectangle start;
|
||||
private float radius;
|
||||
private float angle;
|
||||
private bool pause;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
sinePoints = new Vector2[WAVE_POINTS];
|
||||
cosPoints = new Vector2[WAVE_POINTS];
|
||||
center = new((screenWidth / 2.0f) - 30.0f, screenHeight / 2.0f);
|
||||
start = new(20.0f, screenHeight - 120.0f, 200.0f, 100.0f);
|
||||
radius = 130.0f;
|
||||
angle = 0.0f;
|
||||
pause = false;
|
||||
|
||||
for (int i = 0; i < WAVE_POINTS; i++)
|
||||
{
|
||||
float t = i / (float)(WAVE_POINTS - 1);
|
||||
float currentAngle = t * 360.0f * DEG2RAD;
|
||||
sinePoints[i] = new(start.X + t * start.Width, start.Y + start.Height / 2.0f - MathF.Sin(currentAngle) * (start.Height / 2.0f));
|
||||
cosPoints[i] = new(start.X + t * start.Width, start.Y + start.Height / 2.0f - MathF.Cos(currentAngle) * (start.Height / 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
float angleRad = angle * DEG2RAD;
|
||||
float cosRad = MathF.Cos(angleRad);
|
||||
float sinRad = MathF.Sin(angleRad);
|
||||
|
||||
Vector2 point = new(center.X + cosRad * radius, center.Y - sinRad * radius);
|
||||
Vector2 limitMin = new(center.X - radius, center.Y - radius);
|
||||
Vector2 limitMax = new(center.X + radius, center.Y + radius);
|
||||
|
||||
float complementary = 90.0f - angle;
|
||||
float supplementary = 180.0f - angle;
|
||||
float explementary = 360.0f - angle;
|
||||
|
||||
float tangent = Clamp(MathF.Tan(angleRad), -10.0f, 10.0f);
|
||||
float cotangent = (MathF.Abs(tangent) > 0.001f) ? Clamp(1.0f / tangent, -radius, radius) : 0.0f;
|
||||
Vector2 tangentPoint = new(center.X + radius, center.Y - tangent * radius);
|
||||
Vector2 cotangentPoint = new(center.X + cotangent * radius, center.Y - radius);
|
||||
|
||||
angle = Wrap(angle + (!pause ? 1.0f : 0.0f), 0.0f, 360.0f);
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Cotangent (orange)
|
||||
DrawLineEx(new Vector2(center.X, limitMin.Y), new Vector2(cotangentPoint.X, limitMin.Y), 2.0f, Color.Orange);
|
||||
DrawLineDashed(center, cotangentPoint, 10, 4, Color.Orange);
|
||||
|
||||
// Side background
|
||||
DrawLine(580, 0, 580, GetScreenHeight(), new Color(218, 218, 218, 255));
|
||||
DrawRectangle(580, 0, GetScreenWidth(), GetScreenHeight(), new Color(232, 232, 232, 255));
|
||||
|
||||
// Base circle and axes
|
||||
DrawCircleLinesV(center, radius, Color.Gray);
|
||||
DrawLineEx(new Vector2(center.X, limitMin.Y), new Vector2(center.X, limitMax.Y), 1.0f, Color.Gray);
|
||||
DrawLineEx(new Vector2(limitMin.X, center.Y), new Vector2(limitMax.X, center.Y), 1.0f, Color.Gray);
|
||||
|
||||
// Wave graph axes
|
||||
DrawLineEx(new Vector2(start.X, start.Y), new Vector2(start.X, start.Y + start.Height), 2.0f, Color.Gray);
|
||||
DrawLineEx(new Vector2(start.X + start.Width, start.Y), new Vector2(start.X + start.Width, start.Y + start.Height), 2.0f, Color.Gray);
|
||||
DrawLineEx(new Vector2(start.X, start.Y + start.Height / 2), new Vector2(start.X + start.Width, start.Y + start.Height / 2), 2.0f, Color.Gray);
|
||||
|
||||
// Wave graph axis labels
|
||||
DrawText("1", (int)start.X - 8, (int)start.Y, 6, Color.Gray);
|
||||
DrawText("0", (int)start.X - 8, (int)start.Y + (int)start.Height / 2 - 6, 6, Color.Gray);
|
||||
DrawText("-1", (int)start.X - 12, (int)start.Y + (int)start.Height - 8, 6, Color.Gray);
|
||||
DrawText("0", (int)start.X - 2, (int)start.Y + (int)start.Height + 4, 6, Color.Gray);
|
||||
DrawText("360", (int)start.X + (int)start.Width - 8, (int)start.Y + (int)start.Height + 4, 6, Color.Gray);
|
||||
|
||||
// Sine (red - vertical)
|
||||
DrawLineEx(new Vector2(center.X, center.Y), new Vector2(center.X, point.Y), 2.0f, Color.Red);
|
||||
DrawLineDashed(new Vector2(point.X, center.Y), new Vector2(point.X, point.Y), 10, 4, Color.Red);
|
||||
DrawText($"Sine {sinRad:0.00}", 640, 190, 6, Color.Red);
|
||||
DrawCircleV(new Vector2(start.X + (angle / 360.0f) * start.Width, start.Y + ((-sinRad + 1) * start.Height / 2.0f)), 4.0f, Color.Red);
|
||||
fixed (Vector2* p = sinePoints) DrawSplineLinear(p, WAVE_POINTS, 1.0f, Color.Red);
|
||||
|
||||
// Cosine (blue - horizontal)
|
||||
DrawLineEx(new Vector2(center.X, center.Y), new Vector2(point.X, center.Y), 2.0f, Color.Blue);
|
||||
DrawLineDashed(new Vector2(center.X, point.Y), new Vector2(point.X, point.Y), 10, 4, Color.Blue);
|
||||
DrawText($"Cosine {cosRad:0.00}", 640, 210, 6, Color.Blue);
|
||||
DrawCircleV(new Vector2(start.X + (angle / 360.0f) * start.Width, start.Y + ((-cosRad + 1) * start.Height / 2.0f)), 4.0f, Color.Blue);
|
||||
fixed (Vector2* p = cosPoints) DrawSplineLinear(p, WAVE_POINTS, 1.0f, Color.Blue);
|
||||
|
||||
// Tangent (purple)
|
||||
DrawLineEx(new Vector2(limitMax.X, center.Y), new Vector2(limitMax.X, tangentPoint.Y), 2.0f, Color.Purple);
|
||||
DrawLineDashed(center, tangentPoint, 10, 4, Color.Purple);
|
||||
DrawText($"Tangent {tangent:0.00}", 640, 230, 6, Color.Purple);
|
||||
|
||||
// Cotangent (orange)
|
||||
DrawText($"Cotangent {cotangent:0.00}", 640, 250, 6, Color.Orange);
|
||||
|
||||
// Complementary angle (beige)
|
||||
DrawCircleSectorLines(center, radius * 0.6f, -angle, -90.0f, 36, Color.Beige);
|
||||
DrawText($"Complementary {complementary:0}°", 640, 150, 6, Color.Beige);
|
||||
|
||||
// Supplementary angle (darkblue)
|
||||
DrawCircleSectorLines(center, radius * 0.5f, -angle, -180.0f, 36, Color.DarkBlue);
|
||||
DrawText($"Supplementary {supplementary:0}°", 640, 130, 6, Color.DarkBlue);
|
||||
|
||||
// Explementary angle (pink)
|
||||
DrawCircleSectorLines(center, radius * 0.4f, -angle, -360.0f, 36, Color.Pink);
|
||||
DrawText($"Explementary {explementary:0}°", 640, 170, 6, Color.Pink);
|
||||
|
||||
// Current angle - arc (lime), radius (black), endpoint (black)
|
||||
DrawCircleSectorLines(center, radius * 0.7f, -angle, 0.0f, 36, Color.Lime);
|
||||
DrawLineEx(new Vector2(center.X, center.Y), point, 2.0f, Color.Black);
|
||||
DrawCircleV(point, 4.0f, Color.Black);
|
||||
|
||||
// Draw GUI controls
|
||||
// NOTE: raygui is not bound in raylib-cs, so the Pause toggle and Angle slider
|
||||
// are unavailable; the angle animates continuously.
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(GRAY));
|
||||
GuiToggle((Rectangle){ 640, 70, 120, 20}, TextFormat("Pause"), &pause);
|
||||
GuiSetStyle(LABEL, TEXT_COLOR_NORMAL, ColorToInt(LIME));
|
||||
GuiSliderBar((Rectangle){ 640, 40, 120, 20}, "Angle", TextFormat("%.0f°", angle), &angle, 0.0f, 360.0f);
|
||||
|
||||
// Angle values panel
|
||||
GuiGroupBox((Rectangle){ 620, 110, 140, 170}, "Angle Values");
|
||||
*/
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - math sine cosine");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new MathSineCosine();
|
||||
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;
|
||||
}
|
||||
}
|
||||
127
Examples/Shapes/MouseTrail.cs
Normal file
127
Examples/Shapes/MouseTrail.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - Draw a mouse trail (position history)
|
||||
*
|
||||
* Example complexity rating: [★☆☆☆] 1/4
|
||||
*
|
||||
* Example originally created with raylib 5.6
|
||||
*
|
||||
* Example contributed by Balamurugan R (@Bala050814]) 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 Balamurugan R (@Bala050814)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class MouseTrail : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
// Define the maximum number of positions to store in the trail
|
||||
private const int MAX_TRAIL_LENGTH = 30;
|
||||
|
||||
public string Name => "Shapes / Mouse Trail";
|
||||
|
||||
public string Title => "raylib [shapes] example - mouse trail";
|
||||
|
||||
// Array to store the history of mouse positions (our fixed-size queue)
|
||||
private Vector2[] trailPositions;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Array to store the history of mouse positions (our fixed-size queue)
|
||||
trailPositions = new Vector2[MAX_TRAIL_LENGTH];
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
Vector2 mousePosition = GetMousePosition();
|
||||
|
||||
// Shift all existing positions backward by one slot in the array
|
||||
// The last element (the oldest position) is dropped
|
||||
for (int i = MAX_TRAIL_LENGTH - 1; i > 0; i--)
|
||||
{
|
||||
trailPositions[i] = trailPositions[i - 1];
|
||||
}
|
||||
|
||||
// Store the new, current mouse position at the start of the array (Index 0)
|
||||
trailPositions[0] = mousePosition;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.Black);
|
||||
|
||||
// Draw the trail by looping through the history array
|
||||
for (int i = 0; i < MAX_TRAIL_LENGTH; i++)
|
||||
{
|
||||
// Ensure we skip drawing if the array hasn't been fully filled on startup
|
||||
if ((trailPositions[i].X != 0.0f) || (trailPositions[i].Y != 0.0f))
|
||||
{
|
||||
// Calculate relative trail strength (ratio is near 1.0 for new, near 0.0 for old)
|
||||
float ratio = (float)(MAX_TRAIL_LENGTH - i)/MAX_TRAIL_LENGTH;
|
||||
|
||||
// Fade effect: oldest positions are more transparent
|
||||
// Fade (color, alpha) - alpha is 0.5 to 1.0 based on ratio
|
||||
Color trailColor = Fade(Color.SkyBlue, ratio*0.5f + 0.5f);
|
||||
|
||||
// Size effect: oldest positions are smaller
|
||||
float trailRadius = 15.0f*ratio;
|
||||
|
||||
DrawCircleV(trailPositions[i], trailRadius, trailColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a distinct white circle for the current mouse position (Index 0)
|
||||
DrawCircleV(mousePosition, 15.0f, Color.White);
|
||||
|
||||
DrawText("Move the mouse to see the trail effect!", 10, screenHeight - 30, 20, Color.LightGray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - mouse trail");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new MouseTrail();
|
||||
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;
|
||||
}
|
||||
}
|
||||
283
Examples/Shapes/PenroseTile.cs
Normal file
283
Examples/Shapes/PenroseTile.cs
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - penrose tile
|
||||
*
|
||||
* Example complexity rating: [★★★★] 4/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 6.0
|
||||
* 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)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class PenroseTile : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Penrose Tile";
|
||||
|
||||
public string Title => "raylib [shapes] example - penrose tile";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
public int TargetFps => 120;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
private struct TurtleState
|
||||
{
|
||||
public Vector2 origin;
|
||||
public float angle;
|
||||
}
|
||||
|
||||
private class PenroseLSystem
|
||||
{
|
||||
public int steps;
|
||||
public StringBuilder production;
|
||||
public string ruleW;
|
||||
public string ruleX;
|
||||
public string ruleY;
|
||||
public string ruleZ;
|
||||
public float drawLength;
|
||||
public float theta;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Global Variables Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
private Stack<TurtleState> turtleStack;
|
||||
|
||||
private const float drawLength = 460.0f;
|
||||
private int minGenerations;
|
||||
private int maxGenerations;
|
||||
private int generations;
|
||||
private PenroseLSystem ls;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
turtleStack = new Stack<TurtleState>();
|
||||
|
||||
minGenerations = 0;
|
||||
maxGenerations = 4;
|
||||
generations = 0;
|
||||
|
||||
// Initialize new penrose tile
|
||||
ls = CreatePenroseLSystem(drawLength * (generations / (float)maxGenerations));
|
||||
for (int i = 0; i < generations; i++) BuildProductionStep(ls);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
bool rebuild = false;
|
||||
if (IsKeyPressed(KeyboardKey.Up))
|
||||
{
|
||||
if (generations < maxGenerations)
|
||||
{
|
||||
generations++;
|
||||
rebuild = true;
|
||||
}
|
||||
}
|
||||
else if (IsKeyPressed(KeyboardKey.Down))
|
||||
{
|
||||
if (generations > minGenerations)
|
||||
{
|
||||
generations--;
|
||||
if (generations > 0) rebuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rebuild)
|
||||
{
|
||||
ls = CreatePenroseLSystem(drawLength * (generations / (float)maxGenerations));
|
||||
for (int i = 0; i < generations; i++) BuildProductionStep(ls);
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
if (generations > 0) DrawPenroseLSystem(ls);
|
||||
|
||||
DrawText("penrose l-system", 10, 10, 20, Color.DarkGray);
|
||||
DrawText("press up or down to change generations", 10, 30, 20, Color.DarkGray);
|
||||
DrawText($"generations: {generations}", 10, 50, 20, Color.DarkGray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Push turtle state for next step
|
||||
private void PushTurtleState(TurtleState state)
|
||||
{
|
||||
turtleStack.Push(state);
|
||||
}
|
||||
|
||||
// Pop turtle state step
|
||||
private TurtleState PopTurtleState()
|
||||
{
|
||||
if (turtleStack.Count > 0) return turtleStack.Pop();
|
||||
else TraceLog(TraceLogLevel.Warning, "TURTLE STACK UNDERFLOW!");
|
||||
|
||||
return new TurtleState();
|
||||
}
|
||||
|
||||
// Create a new penrose tile structure
|
||||
private static PenroseLSystem CreatePenroseLSystem(float drawLength)
|
||||
{
|
||||
PenroseLSystem ls = new PenroseLSystem
|
||||
{
|
||||
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 // Degrees
|
||||
};
|
||||
|
||||
ls.production = new StringBuilder("[X]++[X]++[X]++[X]++[X]");
|
||||
|
||||
return ls;
|
||||
}
|
||||
|
||||
// Build next penrose step
|
||||
private static void BuildProductionStep(PenroseLSystem ls)
|
||||
{
|
||||
StringBuilder newProduction = new StringBuilder();
|
||||
|
||||
string production = ls.production.ToString();
|
||||
|
||||
for (int i = 0; i < production.Length; i++)
|
||||
{
|
||||
char step = production[i];
|
||||
switch (step)
|
||||
{
|
||||
case 'W': newProduction.Append(ls.ruleW); break;
|
||||
case 'X': newProduction.Append(ls.ruleX); break;
|
||||
case 'Y': newProduction.Append(ls.ruleY); break;
|
||||
case 'Z': newProduction.Append(ls.ruleZ); break;
|
||||
default:
|
||||
{
|
||||
if (step != 'F') newProduction.Append(step);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
ls.drawLength *= 0.5f;
|
||||
ls.production = newProduction;
|
||||
}
|
||||
|
||||
// Draw penrose tile lines
|
||||
private void DrawPenroseLSystem(PenroseLSystem ls)
|
||||
{
|
||||
Vector2 screenCenter = new Vector2(GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f);
|
||||
|
||||
TurtleState turtle = new TurtleState
|
||||
{
|
||||
origin = new Vector2(0, 0),
|
||||
angle = -90.0f
|
||||
};
|
||||
|
||||
int repeats = 1;
|
||||
string production = ls.production.ToString();
|
||||
int productionLength = production.Length;
|
||||
ls.steps += 12;
|
||||
|
||||
if (ls.steps > productionLength) ls.steps = productionLength;
|
||||
|
||||
for (int i = 0; i < ls.steps; i++)
|
||||
{
|
||||
char step = 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 * MathF.Cos(radAngle);
|
||||
turtle.origin.Y += ls.drawLength * MathF.Sin(radAngle);
|
||||
Vector2 startPosScreen = new Vector2(startPosWorld.X + screenCenter.X, startPosWorld.Y + screenCenter.Y);
|
||||
Vector2 endPosScreen = new Vector2(turtle.origin.X + screenCenter.X, turtle.origin.Y + screenCenter.Y);
|
||||
|
||||
DrawLineEx(startPosScreen, endPosScreen, 2, Fade(Color.Black, 0.2f));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
turtleStack.Clear();
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - penrose tile");
|
||||
|
||||
SetTargetFPS(120); // Set our game to run at 120 frames-per-second
|
||||
//---------------------------------------------------------------------------------------
|
||||
|
||||
var game = new PenroseTile();
|
||||
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;
|
||||
}
|
||||
}
|
||||
384
Examples/Shapes/PieChart.cs
Normal file
384
Examples/Shapes/PieChart.cs
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - pie chart
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Gideon Serfontein (@GideonSerf) 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 Gideon Serfontein (@GideonSerf)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class PieChart : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_PIE_SLICES = 10; // Max pie slices
|
||||
|
||||
public string Name => "Shapes / Pie Chart";
|
||||
|
||||
public string Title => "raylib [shapes] example - pie chart";
|
||||
|
||||
private int sliceCount;
|
||||
private float donutInnerRadius;
|
||||
private float[] values;
|
||||
private StringBuilder[] labels;
|
||||
private bool[] editingLabel;
|
||||
|
||||
private bool showValues;
|
||||
private bool showPercentages;
|
||||
private bool showDonut;
|
||||
private int hoveredSlice;
|
||||
private Vector2 scrollContentOffset;
|
||||
|
||||
// UI layout parameters
|
||||
private const int panelWidth = 270;
|
||||
private const int panelMargin = 5;
|
||||
|
||||
private Vector2 panelPos;
|
||||
private Rectangle panelRect;
|
||||
private Rectangle canvas;
|
||||
private Vector2 center;
|
||||
private const float radius = 205.0f;
|
||||
|
||||
// Total value for percentage calculations
|
||||
private float totalValue;
|
||||
|
||||
private int framesCounter;
|
||||
|
||||
// Minimal raygui-like global state
|
||||
private static bool guiDisabled;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
sliceCount = 7;
|
||||
donutInnerRadius = 25.0f;
|
||||
values = new float[MAX_PIE_SLICES] { 300.0f, 100.0f, 450.0f, 350.0f, 600.0f, 380.0f, 750.0f, 0.0f, 0.0f, 0.0f };
|
||||
labels = new StringBuilder[MAX_PIE_SLICES];
|
||||
editingLabel = new bool[MAX_PIE_SLICES];
|
||||
|
||||
for (int i = 0; i < MAX_PIE_SLICES; i++) labels[i] = new StringBuilder($"Slice {i + 1:D2}");
|
||||
|
||||
showValues = true;
|
||||
showPercentages = false;
|
||||
showDonut = false;
|
||||
hoveredSlice = -1;
|
||||
scrollContentOffset = new Vector2(0, 0);
|
||||
|
||||
// UI Panel top-left anchor
|
||||
panelPos = new Vector2(
|
||||
(float)screenWidth - panelMargin - panelWidth,
|
||||
(float)panelMargin
|
||||
);
|
||||
|
||||
// UI Panel rectangle
|
||||
panelRect = new Rectangle(
|
||||
panelPos.X, panelPos.Y,
|
||||
(float)panelWidth,
|
||||
(float)screenHeight - 2.0f * panelMargin
|
||||
);
|
||||
|
||||
// Pie chart geometry
|
||||
canvas = new Rectangle(0, 0, panelPos.X, (float)screenHeight);
|
||||
center = new Vector2(canvas.Width / 2.0f, canvas.Height / 2.0f);
|
||||
|
||||
totalValue = 0.0f;
|
||||
|
||||
framesCounter = 0;
|
||||
guiDisabled = false;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
framesCounter++;
|
||||
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Calculate total value for percentage calculations
|
||||
totalValue = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++) totalValue += values[i];
|
||||
|
||||
// Check for mouse hover over slices
|
||||
hoveredSlice = -1; // Reset hovered slice
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
if (CheckCollisionPointRec(mousePos, canvas)) // Only check if mouse is inside the canvas
|
||||
{
|
||||
float dx = mousePos.X - center.X;
|
||||
float dy = mousePos.Y - center.Y;
|
||||
float distance = MathF.Sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (distance <= radius) // Inside the pie radius
|
||||
{
|
||||
float angle = MathF.Atan2(dy, dx) * RAD2DEG;
|
||||
if (angle < 0) angle += 360;
|
||||
|
||||
float currentAngle = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
float sweep = (totalValue > 0) ? (values[i] / totalValue) * 360.0f : 0.0f;
|
||||
|
||||
if ((angle >= currentAngle) && (angle < (currentAngle + sweep)))
|
||||
{
|
||||
hoveredSlice = i;
|
||||
break;
|
||||
}
|
||||
|
||||
currentAngle += sweep;
|
||||
}
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw the pie chart on the canvas
|
||||
float startAngle = 0.0f;
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
float sweepAngle = (totalValue > 0) ? (values[i] / totalValue) * 360.0f : 0.0f;
|
||||
float midAngle = startAngle + sweepAngle / 2.0f; // Middle angle for label positioning
|
||||
|
||||
Color color = ColorFromHSV((float)i / sliceCount * 360.0f, 0.75f, 0.9f);
|
||||
float currentRadius = radius;
|
||||
|
||||
// Make the hovered slice pop out by adding pixels to its radius
|
||||
if (i == hoveredSlice) currentRadius += 20.0f;
|
||||
|
||||
// Draw the pie slice using raylib's DrawCircleSector function
|
||||
DrawCircleSector(center, currentRadius, startAngle, startAngle + sweepAngle, 120, color);
|
||||
|
||||
// Draw the label for the current slice
|
||||
if (values[i] > 0)
|
||||
{
|
||||
string labelText;
|
||||
if (showValues && showPercentages) labelText = $"{values[i]:F1} ({(values[i] / totalValue) * 100.0f:F0}%)";
|
||||
else if (showValues) labelText = $"{values[i]:F1}";
|
||||
else if (showPercentages) labelText = $"{(values[i] / totalValue) * 100.0f:F0}%";
|
||||
else labelText = "";
|
||||
|
||||
Vector2 textSize = MeasureTextEx(GetFontDefault(), labelText, 20, 1);
|
||||
float labelRadius = radius * 0.7f;
|
||||
Vector2 labelPos = new Vector2(center.X + MathF.Cos(midAngle * DEG2RAD) * labelRadius - textSize.X / 2.0f,
|
||||
center.Y + MathF.Sin(midAngle * DEG2RAD) * labelRadius - textSize.Y / 2.0f);
|
||||
DrawText(labelText, (int)labelPos.X, (int)labelPos.Y, 20, Color.White);
|
||||
}
|
||||
|
||||
// Draw inner circle to create donut effect
|
||||
// TODO: This is a hacky solution, better use DrawRing()
|
||||
if (showDonut) DrawCircleV(center, donutInnerRadius, Color.RayWhite);
|
||||
|
||||
startAngle += sweepAngle;
|
||||
}
|
||||
|
||||
// UI control panel (minimal raygui-like controls, raygui is not bound in raylib-cs)
|
||||
DrawRectangleRec(panelRect, Fade(Color.LightGray, 0.5f));
|
||||
DrawRectangleLinesEx(panelRect, 1.0f, Color.Gray);
|
||||
|
||||
GuiSpinner(new Rectangle(panelPos.X + 95, (float)panelPos.Y + 12, 125, 25), "Slices ", ref sliceCount, 1, MAX_PIE_SLICES);
|
||||
GuiCheckBox(new Rectangle(panelPos.X + 20, (float)panelPos.Y + 12 + 40, 20, 20), "Show Values", ref showValues);
|
||||
GuiCheckBox(new Rectangle(panelPos.X + 20, (float)panelPos.Y + 12 + 70, 20, 20), "Show Percentages", ref showPercentages);
|
||||
GuiCheckBox(new Rectangle(panelPos.X + 20, (float)panelPos.Y + 12 + 100, 20, 20), "Make Donut", ref showDonut);
|
||||
|
||||
if (showDonut) GuiDisable();
|
||||
GuiSliderBar(new Rectangle(panelPos.X + 80, (float)panelPos.Y + 12 + 130, panelRect.Width - 100, 30),
|
||||
"Inner Radius", null, ref donutInnerRadius, 5.0f, radius - 10.0f);
|
||||
GuiEnable();
|
||||
|
||||
GuiLine(new Rectangle(panelPos.X + 10, (float)panelPos.Y + 12 + 170, panelRect.Width - 20, 1));
|
||||
|
||||
// Scrollable area for slice editors
|
||||
float scrollTop = (float)panelPos.Y + 12 + 190;
|
||||
Rectangle scrollPanelBounds = new Rectangle(
|
||||
panelPos.X + panelMargin,
|
||||
scrollTop,
|
||||
panelRect.Width - panelMargin * 2,
|
||||
(panelRect.Y + panelRect.Height) - scrollTop - panelMargin);
|
||||
int contentHeight = sliceCount * 35;
|
||||
|
||||
// Simple vertical scroll via mouse wheel while hovering the panel
|
||||
if (CheckCollisionPointRec(GetMousePosition(), scrollPanelBounds))
|
||||
{
|
||||
scrollContentOffset.Y += GetMouseWheelMove() * 20.0f;
|
||||
float minOffset = MathF.Min(0.0f, scrollPanelBounds.Height - contentHeight);
|
||||
if (scrollContentOffset.Y < minOffset) scrollContentOffset.Y = minOffset;
|
||||
if (scrollContentOffset.Y > 0.0f) scrollContentOffset.Y = 0.0f;
|
||||
}
|
||||
|
||||
Rectangle view = scrollPanelBounds;
|
||||
float contentX = view.X + scrollContentOffset.X; // Left of content
|
||||
float contentY = view.Y + scrollContentOffset.Y; // Top of content
|
||||
|
||||
BeginScissorMode((int)view.X, (int)view.Y, (int)view.Width, (int)view.Height);
|
||||
|
||||
for (int i = 0; i < sliceCount; i++)
|
||||
{
|
||||
int rowY = (int)(contentY + 5 + i * 35);
|
||||
|
||||
// Color indicator
|
||||
Color color = ColorFromHSV((float)i / sliceCount * 360.0f, 0.75f, 0.9f);
|
||||
DrawRectangle((int)(contentX + 15), rowY + 5, 20, 20, color);
|
||||
|
||||
// Label textbox
|
||||
if (GuiTextBox(new Rectangle(contentX + 45, (float)rowY, 75, 30), labels[i], 32, editingLabel[i])) editingLabel[i] = !editingLabel[i];
|
||||
|
||||
GuiSliderBar(new Rectangle(contentX + 130, (float)rowY, 110, 30), null, null, ref values[i], 0.0f, 1000.0f);
|
||||
}
|
||||
|
||||
EndScissorMode();
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Minimal raygui-like widgets (plain raylib re-implementation)
|
||||
//----------------------------------------------------------------------------------
|
||||
private static void GuiDisable() => guiDisabled = true;
|
||||
private static void GuiEnable() => guiDisabled = false;
|
||||
|
||||
private static void GuiLine(Rectangle bounds)
|
||||
{
|
||||
int y = (int)(bounds.Y + bounds.Height / 2);
|
||||
DrawLine((int)bounds.X, y, (int)(bounds.X + bounds.Width), y, Color.Gray);
|
||||
}
|
||||
|
||||
private static void GuiCheckBox(Rectangle bounds, string text, ref bool active)
|
||||
{
|
||||
Vector2 mouse = GetMousePosition();
|
||||
bool hover = CheckCollisionPointRec(mouse, bounds);
|
||||
if (!guiDisabled && 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);
|
||||
}
|
||||
|
||||
private static void GuiSpinner(Rectangle bounds, string text, ref int value, int minValue, int maxValue)
|
||||
{
|
||||
Vector2 mouse = GetMousePosition();
|
||||
Rectangle left = new Rectangle(bounds.X, bounds.Y, bounds.Height, bounds.Height);
|
||||
Rectangle right = new Rectangle(bounds.X + bounds.Width - bounds.Height, bounds.Y, bounds.Height, bounds.Height);
|
||||
Rectangle mid = new Rectangle(bounds.X + bounds.Height, bounds.Y, bounds.Width - 2 * bounds.Height, bounds.Height);
|
||||
|
||||
if (!guiDisabled && CheckCollisionPointRec(mouse, left) && IsMouseButtonPressed(MouseButton.Left) && value > minValue) value--;
|
||||
if (!guiDisabled && CheckCollisionPointRec(mouse, right) && IsMouseButtonPressed(MouseButton.Left) && value < maxValue) value++;
|
||||
|
||||
DrawRectangleRec(mid, Color.RayWhite);
|
||||
DrawRectangleLinesEx(mid, 1, Color.Gray);
|
||||
DrawRectangleRec(left, Color.LightGray);
|
||||
DrawRectangleLinesEx(left, 1, Color.Gray);
|
||||
DrawRectangleRec(right, Color.LightGray);
|
||||
DrawRectangleLinesEx(right, 1, Color.Gray);
|
||||
DrawText("-", (int)(left.X + left.Width / 2 - 2), (int)(left.Y + left.Height / 2 - 5), 10, Color.DarkGray);
|
||||
DrawText("+", (int)(right.X + right.Width / 2 - 3), (int)(right.Y + right.Height / 2 - 5), 10, Color.DarkGray);
|
||||
string vs = value.ToString();
|
||||
DrawText(vs, (int)(mid.X + mid.Width / 2 - MeasureText(vs, 10) / 2), (int)(mid.Y + mid.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (text != null) DrawText(text, (int)bounds.X - MeasureText(text, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
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 (!guiDisabled && 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, guiDisabled ? Color.Gray : Color.LightGray);
|
||||
float pct = (value - minValue) / (maxValue - minValue);
|
||||
DrawRectangleRec(new Rectangle(bounds.X, bounds.Y, bounds.Width * pct, bounds.Height), guiDisabled ? Color.DarkGray : Color.SkyBlue);
|
||||
DrawRectangleLinesEx(bounds, 1, Color.Gray);
|
||||
if (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
private bool GuiTextBox(Rectangle bounds, StringBuilder text, int maxChars, bool editMode)
|
||||
{
|
||||
bool pressed = false;
|
||||
Vector2 mouse = GetMousePosition();
|
||||
bool hover = CheckCollisionPointRec(mouse, bounds);
|
||||
|
||||
if (editMode)
|
||||
{
|
||||
int key = GetCharPressed();
|
||||
while (key > 0)
|
||||
{
|
||||
if ((key >= 32) && (key <= 125) && (text.Length < maxChars - 1))
|
||||
{
|
||||
text.Append((char)key);
|
||||
}
|
||||
|
||||
key = GetCharPressed();
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.Backspace) && (text.Length > 0)) text.Remove(text.Length - 1, 1);
|
||||
}
|
||||
|
||||
DrawRectangleRec(bounds, Color.RayWhite);
|
||||
DrawRectangleLinesEx(bounds, editMode ? 2 : 1, editMode ? Color.Red : (hover ? Color.Blue : Color.Gray));
|
||||
|
||||
string content = text.ToString();
|
||||
DrawText(content, (int)bounds.X + 4, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
|
||||
if (editMode && ((framesCounter / 20) % 2 == 0))
|
||||
{
|
||||
DrawText("_", (int)bounds.X + 4 + MeasureText(content, 10), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
if (hover && IsMouseButtonPressed(MouseButton.Left)) pressed = true;
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - pie chart");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new PieChart();
|
||||
game.Init();
|
||||
|
||||
// Main game loop
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
game.Update();
|
||||
}
|
||||
|
||||
game.Unload();
|
||||
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
CloseWindow(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
252
Examples/Shapes/RectangleAdvanced.cs
Normal file
252
Examples/Shapes/RectangleAdvanced.cs
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - rectangle advanced
|
||||
*
|
||||
* Example complexity rating: [★★★★] 4/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 5.5
|
||||
*
|
||||
* Example contributed by Everton Jr. (@evertonse) 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) 2024-2025 Everton Jr. (@evertonse) and Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Rlgl;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class RectangleAdvanced : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Rectangle Advanced";
|
||||
|
||||
public string Title => "raylib [shapes] example - rectangle advanced";
|
||||
|
||||
public void Init()
|
||||
{
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update rectangle bounds
|
||||
//----------------------------------------------------------------------------------
|
||||
float width = GetScreenWidth() / 2.0f, height = GetScreenHeight() / 6.0f;
|
||||
Rectangle rec = new Rectangle(
|
||||
GetScreenWidth() / 2.0f - width / 2,
|
||||
GetScreenHeight() / 2.0f - 5 * (height / 2),
|
||||
width, height
|
||||
);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Draw All Rectangles with different roundess for each side and different gradients
|
||||
DrawRectangleRoundedGradientH(rec, 0.8f, 0.8f, 36, Color.Blue, Color.Red);
|
||||
|
||||
rec.Y += rec.Height + 1;
|
||||
DrawRectangleRoundedGradientH(rec, 0.5f, 1.0f, 36, Color.Red, Color.Pink);
|
||||
|
||||
rec.Y += rec.Height + 1;
|
||||
DrawRectangleRoundedGradientH(rec, 1.0f, 0.5f, 36, Color.Red, Color.Blue);
|
||||
|
||||
rec.Y += rec.Height + 1;
|
||||
DrawRectangleRoundedGradientH(rec, 0.0f, 1.0f, 36, Color.Blue, Color.Black);
|
||||
|
||||
rec.Y += rec.Height + 1;
|
||||
DrawRectangleRoundedGradientH(rec, 1.0f, 0.0f, 36, Color.Blue, Color.Pink);
|
||||
EndDrawing();
|
||||
//--------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Draw rectangle with rounded edges and horizontal gradient, with options to choose side of roundness
|
||||
// NOTE: Adapted from both 'DrawRectangleRounded()' and 'DrawRectangleGradientH()' raylib [rshapes] implementations
|
||||
private static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, float roundnessRight, int segments, Color left, Color right)
|
||||
{
|
||||
// Neither side is rounded
|
||||
if ((roundnessLeft <= 0.0f && roundnessRight <= 0.0f) || (rec.Width < 1) || (rec.Height < 1))
|
||||
{
|
||||
DrawRectangleGradientEx(rec, left, left, right, right);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roundnessLeft >= 1.0f) roundnessLeft = 1.0f;
|
||||
if (roundnessRight >= 1.0f) roundnessRight = 1.0f;
|
||||
|
||||
// Calculate corner radius both from right and left
|
||||
float recSize = rec.Width > rec.Height ? rec.Height : rec.Width;
|
||||
float radiusLeft = (recSize * roundnessLeft) / 2;
|
||||
float radiusRight = (recSize * roundnessRight) / 2;
|
||||
|
||||
if (radiusLeft <= 0.0f) radiusLeft = 0.0f;
|
||||
if (radiusRight <= 0.0f) radiusRight = 0.0f;
|
||||
|
||||
if (radiusRight <= 0.0f && radiusLeft <= 0.0f) return;
|
||||
|
||||
float stepLength = 90.0f / (float)segments;
|
||||
|
||||
/*
|
||||
Diagram Copied here for reference, original at 'DrawRectangleRounded()' source code
|
||||
|
||||
P0____________________P1
|
||||
/| |\
|
||||
/1| 2 |3\
|
||||
P7 /__|____________________|__\ P2
|
||||
| |P8 P9| |
|
||||
| 8 | 9 | 4 |
|
||||
| __|____________________|__ |
|
||||
P6 \ |P11 P10| / P3
|
||||
\7| 6 |5/
|
||||
\|____________________|/
|
||||
P5 P4
|
||||
*/
|
||||
|
||||
// Coordinates of the 12 points also adapted from `DrawRectangleRounded`
|
||||
Vector2[] point = new Vector2[12]
|
||||
{
|
||||
// PO, P1, P2
|
||||
new Vector2(rec.X + radiusLeft, rec.Y), new Vector2((rec.X + rec.Width) - radiusRight, rec.Y), new Vector2(rec.X + rec.Width, rec.Y + radiusRight),
|
||||
// P3, P4
|
||||
new Vector2(rec.X + rec.Width, (rec.Y + rec.Height) - radiusRight), new Vector2((rec.X + rec.Width) - radiusRight, rec.Y + rec.Height),
|
||||
// P5, P6, P7
|
||||
new Vector2(rec.X + radiusLeft, rec.Y + rec.Height), new Vector2(rec.X, (rec.Y + rec.Height) - radiusLeft), new Vector2(rec.X, rec.Y + radiusLeft),
|
||||
// P8, P9
|
||||
new Vector2(rec.X + radiusLeft, rec.Y + radiusLeft), new Vector2((rec.X + rec.Width) - radiusRight, rec.Y + radiusRight),
|
||||
// P10, P11
|
||||
new Vector2((rec.X + rec.Width) - radiusRight, (rec.Y + rec.Height) - radiusRight), new Vector2(rec.X + radiusLeft, (rec.Y + rec.Height) - radiusLeft)
|
||||
};
|
||||
|
||||
Vector2[] centers = new Vector2[4] { point[8], point[9], point[10], point[11] };
|
||||
float[] angles = new float[4] { 180.0f, 270.0f, 0.0f, 90.0f };
|
||||
|
||||
// Here we use the 'Diagram' to guide ourselves to which point receives what color
|
||||
// By choosing the color correctly associated with a point the gradient effect
|
||||
// will naturally come from OpenGL interpolation
|
||||
// But this time instead of Quad, we think in triangles
|
||||
|
||||
Begin(DrawMode.Triangles);
|
||||
// Draw all of the 4 corners: [1] Upper Left Corner, [3] Upper Right Corner, [5] Lower Right Corner, [7] Lower Left Corner
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
Color color = new Color(0, 0, 0, 0);
|
||||
float radius = 0.0f;
|
||||
if (k == 0) { color = left; radius = radiusLeft; } // [1] Upper Left Corner
|
||||
if (k == 1) { color = right; radius = radiusRight; } // [3] Upper Right Corner
|
||||
if (k == 2) { color = right; radius = radiusRight; } // [5] Lower Right Corner
|
||||
if (k == 3) { color = left; radius = radiusLeft; } // [7] Lower Left Corner
|
||||
|
||||
float angle = angles[k];
|
||||
Vector2 center = centers[k];
|
||||
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
Color4ub(color.R, color.G, color.B, color.A);
|
||||
Vertex2f(center.X, center.Y);
|
||||
Vertex2f(center.X + MathF.Cos(DEG2RAD * (angle + stepLength)) * radius, center.Y + MathF.Sin(DEG2RAD * (angle + stepLength)) * radius);
|
||||
Vertex2f(center.X + MathF.Cos(DEG2RAD * angle) * radius, center.Y + MathF.Sin(DEG2RAD * angle) * radius);
|
||||
angle += stepLength;
|
||||
}
|
||||
}
|
||||
|
||||
// [2] Upper Rectangle
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[0].X, point[0].Y);
|
||||
Vertex2f(point[8].X, point[8].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[9].X, point[9].Y);
|
||||
Vertex2f(point[1].X, point[1].Y);
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[0].X, point[0].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[9].X, point[9].Y);
|
||||
|
||||
// [4] Right Rectangle
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[9].X, point[9].Y);
|
||||
Vertex2f(point[10].X, point[10].Y);
|
||||
Vertex2f(point[3].X, point[3].Y);
|
||||
Vertex2f(point[2].X, point[2].Y);
|
||||
Vertex2f(point[9].X, point[9].Y);
|
||||
Vertex2f(point[3].X, point[3].Y);
|
||||
|
||||
// [6] Bottom Rectangle
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[11].X, point[11].Y);
|
||||
Vertex2f(point[5].X, point[5].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[4].X, point[4].Y);
|
||||
Vertex2f(point[10].X, point[10].Y);
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[11].X, point[11].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[4].X, point[4].Y);
|
||||
|
||||
// [8] Left Rectangle
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[7].X, point[7].Y);
|
||||
Vertex2f(point[6].X, point[6].Y);
|
||||
Vertex2f(point[11].X, point[11].Y);
|
||||
Vertex2f(point[8].X, point[8].Y);
|
||||
Vertex2f(point[7].X, point[7].Y);
|
||||
Vertex2f(point[11].X, point[11].Y);
|
||||
|
||||
// [9] Middle Rectangle
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[8].X, point[8].Y);
|
||||
Vertex2f(point[11].X, point[11].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[10].X, point[10].Y);
|
||||
Vertex2f(point[9].X, point[9].Y);
|
||||
Color4ub(left.R, left.G, left.B, left.A);
|
||||
Vertex2f(point[8].X, point[8].Y);
|
||||
Color4ub(right.R, right.G, right.B, right.A);
|
||||
Vertex2f(point[10].X, point[10].Y);
|
||||
End();
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rectangle advanced");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new RectangleAdvanced();
|
||||
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;
|
||||
}
|
||||
}
|
||||
199
Examples/Shapes/RecursiveTree.cs
Normal file
199
Examples/Shapes/RecursiveTree.cs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
331
Examples/Shapes/RlglColorWheel.cs
Normal file
331
Examples/Shapes/RlglColorWheel.cs
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - rlgl color wheel
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
||||
*
|
||||
* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
using static Raylib_cs.Rlgl;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class RlglColorWheel : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / RLGL Color Wheel";
|
||||
|
||||
public string Title => "raylib [shapes] example - rlgl color wheel";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
// The minimum/maximum points the circle can have
|
||||
private const int pointsMin = 3;
|
||||
private const int pointsMax = 256;
|
||||
|
||||
// The current number of points and the radius of the circle
|
||||
private int triangleCount;
|
||||
private float pointScale;
|
||||
|
||||
// Slider value, literally maps to value in HSV
|
||||
private float value;
|
||||
|
||||
// The center of the screen
|
||||
private Vector2 center;
|
||||
// The location of the color wheel
|
||||
private Vector2 circlePosition;
|
||||
|
||||
// The currently selected color
|
||||
private Color color;
|
||||
|
||||
// Indicates if the slider is being clicked
|
||||
private bool sliderClicked;
|
||||
|
||||
// Indicates if the current color going to be updated, as well as the handle position
|
||||
private bool settingColor;
|
||||
|
||||
// How the color wheel will be rendered
|
||||
private DrawMode renderType;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
triangleCount = 64;
|
||||
pointScale = 150.0f;
|
||||
value = 1.0f;
|
||||
|
||||
center = new Vector2((float)screenWidth / 2.0f, (float)screenHeight / 2.0f);
|
||||
circlePosition = center;
|
||||
|
||||
color = new Color(255, 255, 255, 255);
|
||||
|
||||
sliderClicked = false;
|
||||
settingColor = false;
|
||||
|
||||
renderType = DrawMode.Triangles;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
triangleCount += (int)GetMouseWheelMove();
|
||||
triangleCount = (int)Clamp((float)triangleCount, (float)pointsMin, (float)pointsMax);
|
||||
|
||||
Rectangle sliderRectangle = new Rectangle(42.0f, 16.0f + 64.0f + 45.0f, 64.0f, 16.0f);
|
||||
Vector2 mousePosition = GetMousePosition();
|
||||
|
||||
// Checks if the user is hovering over the value slider
|
||||
bool sliderHover = (mousePosition.X >= sliderRectangle.X && mousePosition.Y >= sliderRectangle.Y && mousePosition.X < sliderRectangle.X + sliderRectangle.Width && mousePosition.Y < sliderRectangle.Y + sliderRectangle.Height);
|
||||
|
||||
// Copy color as hex
|
||||
if (IsKeyDown(KeyboardKey.LeftControl) && IsKeyDown(KeyboardKey.C))
|
||||
{
|
||||
if (IsKeyPressed(KeyboardKey.C))
|
||||
{
|
||||
SetClipboardText($"#{color.R:X2}{color.G:X2}{color.B:X2}");
|
||||
}
|
||||
}
|
||||
|
||||
// Scale up the color wheel, adjusting the handle visually
|
||||
if (IsKeyDown(KeyboardKey.Up))
|
||||
{
|
||||
pointScale *= 1.025f;
|
||||
|
||||
if (pointScale > (float)screenHeight / 2.0f)
|
||||
{
|
||||
pointScale = (float)screenHeight / 2.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), new Vector2(1.025f, 1.025f)), center);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale down the wheel, adjusting the handle visually
|
||||
if (IsKeyDown(KeyboardKey.Down))
|
||||
{
|
||||
pointScale *= 0.975f;
|
||||
|
||||
if (pointScale < 32.0f)
|
||||
{
|
||||
pointScale = 32.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
circlePosition = Vector2Add(Vector2Multiply(Vector2Subtract(circlePosition, center), new Vector2(0.975f, 0.975f)), center);
|
||||
}
|
||||
|
||||
float distanceDown = Vector2Distance(center, circlePosition) / pointScale;
|
||||
float angleDown = ((Vector2Angle(new Vector2(0.0f, -pointScale), Vector2Subtract(center, circlePosition)) / MathF.PI + 1.0f) / 2.0f);
|
||||
|
||||
if (distanceDown > 1.0f)
|
||||
{
|
||||
circlePosition = Vector2Add(new Vector2(MathF.Sin(angleDown * (MathF.PI * 2.0f)) * pointScale, -MathF.Cos(angleDown * (MathF.PI * 2.0f)) * pointScale), center);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the user clicked on the color wheel
|
||||
if (IsMouseButtonPressed(MouseButton.Left) && Vector2Distance(GetMousePosition(), center) <= pointScale + 10.0f)
|
||||
{
|
||||
settingColor = true;
|
||||
}
|
||||
|
||||
// Update flag when mouse button is released
|
||||
if (IsMouseButtonReleased(MouseButton.Left)) settingColor = false;
|
||||
|
||||
// Check if the user clicked/released the slider for the color's value
|
||||
if (sliderHover && IsMouseButtonPressed(MouseButton.Left)) sliderClicked = true;
|
||||
|
||||
if (sliderClicked && IsMouseButtonReleased(MouseButton.Left)) sliderClicked = false;
|
||||
|
||||
// Update render mode accordingly
|
||||
if (IsKeyPressed(KeyboardKey.Space)) renderType = DrawMode.Lines;
|
||||
|
||||
if (IsKeyReleased(KeyboardKey.Space)) renderType = DrawMode.Triangles;
|
||||
|
||||
// If the slider or the wheel was clicked, update the current color
|
||||
if (settingColor || sliderClicked)
|
||||
{
|
||||
if (settingColor) circlePosition = GetMousePosition();
|
||||
|
||||
float distance = Vector2Distance(center, circlePosition) / pointScale;
|
||||
|
||||
float angle = ((Vector2Angle(new Vector2(0.0f, -pointScale), Vector2Subtract(center, circlePosition)) / MathF.PI + 1.0f) / 2.0f);
|
||||
if (settingColor && distance > 1.0f) circlePosition = Vector2Add(new Vector2(MathF.Sin(angle * (MathF.PI * 2.0f)) * pointScale, -MathF.Cos(angle * (MathF.PI * 2.0f)) * pointScale), center);
|
||||
|
||||
float angle360 = angle * 360.0f;
|
||||
float valueActual = Clamp(distance, 0.0f, 1.0f);
|
||||
color = ColorLerp(new Color((int)(value * 255.0f), (int)(value * 255.0f), (int)(value * 255.0f), 255), ColorFromHSV(angle360, Clamp(distance, 0.0f, 1.0f), 1.0f), valueActual);
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Begin rendering color wheel
|
||||
Begin(renderType);
|
||||
for (int i = 0; i < triangleCount; i++)
|
||||
{
|
||||
float angleOffset = ((MathF.PI * 2.0f) / (float)triangleCount);
|
||||
float angle = angleOffset * (float)i;
|
||||
float angleOffsetCalculated = ((float)i + 1) * angleOffset;
|
||||
Vector2 scale = new Vector2(pointScale, pointScale);
|
||||
|
||||
Vector2 offset = Vector2Multiply(new Vector2(MathF.Sin(angle), -MathF.Cos(angle)), scale);
|
||||
Vector2 offset2 = Vector2Multiply(new Vector2(MathF.Sin(angleOffsetCalculated), -MathF.Cos(angleOffsetCalculated)), scale);
|
||||
|
||||
Vector2 position = Vector2Add(center, offset);
|
||||
Vector2 position2 = Vector2Add(center, offset2);
|
||||
|
||||
float angleNonRadian = (angle / (2.0f * MathF.PI)) * 360.0f;
|
||||
float angleNonRadianOffset = (angleOffset / (2.0f * MathF.PI)) * 360.0f;
|
||||
|
||||
Color currentColor = ColorFromHSV(angleNonRadian, 1.0f, 1.0f);
|
||||
Color offsetColor = ColorFromHSV(angleNonRadian + angleNonRadianOffset, 1.0f, 1.0f);
|
||||
|
||||
// Input vertices differently depending on mode
|
||||
if (renderType == DrawMode.Triangles)
|
||||
{
|
||||
// RL_TRIANGLES expects three vertices per triangle
|
||||
Color4ub(currentColor.R, currentColor.G, currentColor.B, currentColor.A);
|
||||
Vertex2f(position.X, position.Y);
|
||||
Color4f(value, value, value, 1.0f);
|
||||
Vertex2f(center.X, center.Y);
|
||||
Color4ub(offsetColor.R, offsetColor.G, offsetColor.B, offsetColor.A);
|
||||
Vertex2f(position2.X, position2.Y);
|
||||
}
|
||||
else if (renderType == DrawMode.Lines)
|
||||
{
|
||||
// RL_LINES expects two vertices per line
|
||||
Color4ub(currentColor.R, currentColor.G, currentColor.B, currentColor.A);
|
||||
Vertex2f(position.X, position.Y);
|
||||
Color4ub(Color.White.R, Color.White.G, Color.White.B, Color.White.A);
|
||||
Vertex2f(center.X, center.Y);
|
||||
|
||||
Vertex2f(center.X, center.Y);
|
||||
Color4ub(offsetColor.R, offsetColor.G, offsetColor.B, offsetColor.A);
|
||||
Vertex2f(position2.X, position2.Y);
|
||||
|
||||
Vertex2f(position2.X, position2.Y);
|
||||
Color4ub(currentColor.R, currentColor.G, currentColor.B, currentColor.A);
|
||||
Vertex2f(position.X, position.Y);
|
||||
}
|
||||
}
|
||||
End();
|
||||
|
||||
// Make the handle slightly more visible overtop darker colors
|
||||
Color handleColor = Color.Black;
|
||||
|
||||
if (Vector2Distance(center, circlePosition) / pointScale <= 0.5f && value <= 0.5f)
|
||||
{
|
||||
handleColor = Color.DarkGray;
|
||||
}
|
||||
|
||||
// Draw the color handle
|
||||
DrawCircleLinesV(circlePosition, 4.0f, handleColor);
|
||||
|
||||
// Draw the color in a preview, with a darkened outline.
|
||||
DrawRectangleV(new Vector2(8.0f, 8.0f), new Vector2(64.0f, 64.0f), color);
|
||||
DrawRectangleLinesEx(new Rectangle(8.0f, 8.0f, 64.0f, 64.0f), 2.0f, ColorLerp(color, Color.Black, 0.5f));
|
||||
|
||||
// Draw current color as hex and decimal
|
||||
DrawText($"#{color.R:X2}{color.G:X2}{color.B:X2}\n({color.R}, {color.G}, {color.B})", 8, 8 + 64 + 8, 20, Color.DarkGray);
|
||||
|
||||
// Update the visuals for the copying text
|
||||
Color copyColor = Color.DarkGray;
|
||||
int textOffset = 0;
|
||||
if (IsKeyDown(KeyboardKey.LeftControl) && IsKeyDown(KeyboardKey.C))
|
||||
{
|
||||
copyColor = Color.DarkGreen;
|
||||
textOffset = 4;
|
||||
}
|
||||
|
||||
// Draw the copying text
|
||||
DrawText("press ctrl+c to copy!", 8, 425 - textOffset, 20, copyColor);
|
||||
|
||||
// Display the number of rendered triangles
|
||||
DrawText($"triangle count: {triangleCount}", 8, 395, 20, Color.DarkGray);
|
||||
|
||||
// Slider to change color's value (minimal raygui-like control, raygui is not bound in raylib-cs)
|
||||
GuiSliderBar(sliderRectangle, "value: ", "", ref value, 0.0f, 1.0f);
|
||||
|
||||
// Draw FPS next to outlined color preview
|
||||
DrawFPS(64 + 16, 8);
|
||||
|
||||
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 (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) DrawText(textRight, (int)(bounds.X + bounds.Width + 5), (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl color wheel");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new RlglColorWheel();
|
||||
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;
|
||||
}
|
||||
}
|
||||
201
Examples/Shapes/RlglTriangle.cs
Normal file
201
Examples/Shapes/RlglTriangle.cs
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - rlgl triangle
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 6.0, last time updated with raylib 6.0
|
||||
*
|
||||
* Example contributed by Robin (@RobinsAviary) 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 Robin (@RobinsAviary)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Rlgl;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class RlglTriangle : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / RLGL Triangle";
|
||||
|
||||
public string Title => "raylib [shapes] example - rlgl triangle";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
// Starting postions and rendered triangle positions
|
||||
private Vector2[] startingPositions;
|
||||
private Vector2[] trianglePositions;
|
||||
|
||||
// Currently selected vertex, -1 means none
|
||||
private int triangleIndex;
|
||||
private bool linesMode;
|
||||
private float handleRadius;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Starting postions and rendered triangle positions
|
||||
startingPositions = [new(400.0f, 150.0f), new(300.0f, 300.0f), new(500.0f, 300.0f)];
|
||||
trianglePositions = [startingPositions[0], startingPositions[1], startingPositions[2]];
|
||||
|
||||
// Currently selected vertex, -1 means none
|
||||
triangleIndex = -1;
|
||||
linesMode = false;
|
||||
handleRadius = 8.0f;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
if (IsKeyPressed(KeyboardKey.Space)) linesMode = !linesMode;
|
||||
|
||||
// Check selected vertex
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// If the mouse is within the handle circle
|
||||
if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius) &&
|
||||
IsMouseButtonDown(MouseButton.Left))
|
||||
{
|
||||
triangleIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the user has selected a vertex, offset it by the mouse's delta this frame
|
||||
if (triangleIndex != -1)
|
||||
{
|
||||
Vector2 mouseDelta = GetMouseDelta();
|
||||
trianglePositions[triangleIndex].X += mouseDelta.X;
|
||||
trianglePositions[triangleIndex].Y += mouseDelta.Y;
|
||||
}
|
||||
|
||||
// Reset index on release
|
||||
if (IsMouseButtonReleased(MouseButton.Left)) triangleIndex = -1;
|
||||
|
||||
// Enable/disable backface culling (2-sided triangles, slower to render)
|
||||
if (IsKeyPressed(KeyboardKey.Left)) EnableBackfaceCulling();
|
||||
if (IsKeyPressed(KeyboardKey.Right)) DisableBackfaceCulling();
|
||||
|
||||
// Reset triangle vertices to starting positions and reset backface culling
|
||||
if (IsKeyPressed(KeyboardKey.R))
|
||||
{
|
||||
trianglePositions[0] = startingPositions[0];
|
||||
trianglePositions[1] = startingPositions[1];
|
||||
trianglePositions[2] = startingPositions[2];
|
||||
|
||||
EnableBackfaceCulling();
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
if (linesMode)
|
||||
{
|
||||
// Draw triangle with lines
|
||||
Begin(DrawMode.Lines);
|
||||
// Three lines, six points
|
||||
// Define color for next vertex
|
||||
Color4ub(255, 0, 0, 255);
|
||||
// Define vertex
|
||||
Vertex2f(trianglePositions[0].X, trianglePositions[0].Y);
|
||||
Color4ub(0, 255, 0, 255);
|
||||
Vertex2f(trianglePositions[1].X, trianglePositions[1].Y);
|
||||
|
||||
Color4ub(0, 255, 0, 255);
|
||||
Vertex2f(trianglePositions[1].X, trianglePositions[1].Y);
|
||||
Color4ub(0, 0, 255, 255);
|
||||
Vertex2f(trianglePositions[2].X, trianglePositions[2].Y);
|
||||
|
||||
Color4ub(0, 0, 255, 255);
|
||||
Vertex2f(trianglePositions[2].X, trianglePositions[2].Y);
|
||||
Color4ub(255, 0, 0, 255);
|
||||
Vertex2f(trianglePositions[0].X, trianglePositions[0].Y);
|
||||
End();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw triangle as a triangle
|
||||
Begin(DrawMode.Triangles);
|
||||
// One triangle, three points
|
||||
// Define color for next vertex
|
||||
Color4ub(255, 0, 0, 255);
|
||||
// Define vertex
|
||||
Vertex2f(trianglePositions[0].X, trianglePositions[0].Y);
|
||||
Color4ub(0, 255, 0, 255);
|
||||
Vertex2f(trianglePositions[1].X, trianglePositions[1].Y);
|
||||
Color4ub(0, 0, 255, 255);
|
||||
Vertex2f(trianglePositions[2].X, trianglePositions[2].Y);
|
||||
End();
|
||||
}
|
||||
|
||||
// Render the vertex handles, reacting to mouse movement/input
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// Draw handle fill focused by mouse
|
||||
if (CheckCollisionPointCircle(GetMousePosition(), trianglePositions[i], handleRadius))
|
||||
DrawCircleV(trianglePositions[i], handleRadius, ColorAlpha(Color.DarkGray, 0.5f));
|
||||
|
||||
// Draw handle fill selected
|
||||
if (i == triangleIndex) DrawCircleV(trianglePositions[i], handleRadius, Color.DarkGray);
|
||||
|
||||
// Draw handle outline
|
||||
DrawCircleLinesV(trianglePositions[i], handleRadius, Color.Black);
|
||||
}
|
||||
|
||||
// Draw controls
|
||||
DrawText("SPACE: Toggle lines mode", 10, 10, 20, Color.DarkGray);
|
||||
DrawText("LEFT-RIGHT: Toggle backface culling", 10, 40, 20, Color.DarkGray);
|
||||
DrawText("MOUSE: Click and drag vertex points", 10, 70, 20, Color.DarkGray);
|
||||
DrawText("R: Reset triangle to start positions", 10, 100, 20, Color.DarkGray);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - rlgl triangle");
|
||||
|
||||
SetTargetFPS(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new RlglTriangle();
|
||||
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;
|
||||
}
|
||||
}
|
||||
308
Examples/Shapes/SimpleParticles.cs
Normal file
308
Examples/Shapes/SimpleParticles.cs
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - simple particles
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.6, last time updated with raylib 5.6
|
||||
*
|
||||
* Example contributed by Jordi Santonja (@JordSant)
|
||||
*
|
||||
* 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 Jordi Santonja (@JordSant)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class SimpleParticles : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_PARTICLES = 3000; // Max number of particles
|
||||
|
||||
public string Name => "Shapes / Simple Particles";
|
||||
|
||||
public string Title => "raylib [shapes] example - simple particles";
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
private enum ParticleType
|
||||
{
|
||||
Water = 0,
|
||||
Smoke,
|
||||
Fire
|
||||
}
|
||||
|
||||
private static readonly string[] particleTypeNames = ["WATER", "SMOKE", "FIRE"];
|
||||
|
||||
private struct Particle
|
||||
{
|
||||
public ParticleType type; // Particle type (WATER, SMOKE, FIRE)
|
||||
public Vector2 position; // Particle position on screen
|
||||
public Vector2 velocity; // Particle current speed and direction
|
||||
public float radius; // Particle radius
|
||||
public Color color; // Particle color
|
||||
|
||||
public float lifeTime; // Particle life time
|
||||
public bool alive; // Particle alive: inside screen and life time
|
||||
}
|
||||
|
||||
// Circular buffer state
|
||||
private int head; // Index for the next write
|
||||
private int tail; // Index for the next read
|
||||
private Particle[] buffer; // Particle buffer array
|
||||
|
||||
// Particle emitter parameters
|
||||
private int emissionRate; // Negative: on average every -X frames. Positive: particles per frame
|
||||
private ParticleType currentType;
|
||||
private Vector2 emitterPosition;
|
||||
|
||||
private Random random;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
// Definition of particles
|
||||
buffer = new Particle[MAX_PARTICLES]; // Particle array
|
||||
head = 0;
|
||||
tail = 0;
|
||||
|
||||
// Particle emitter parameters
|
||||
emissionRate = -2; // Negative: on average every -X frames. Positive: particles per frame
|
||||
currentType = ParticleType.Water;
|
||||
emitterPosition = new(screenWidth / 2.0f, screenHeight / 2.0f);
|
||||
|
||||
random = new Random();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Emit new particles: when emissionRate is 1, emit every frame
|
||||
if (emissionRate < 0)
|
||||
{
|
||||
if (random.Next(-emissionRate) == 0) EmitParticle(emitterPosition, currentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i <= emissionRate; i++) EmitParticle(emitterPosition, currentType);
|
||||
}
|
||||
|
||||
// Update the parameters of each particle
|
||||
UpdateParticles(screenWidth, screenHeight);
|
||||
|
||||
// Remove dead particles from the circular buffer
|
||||
UpdateCircularBuffer();
|
||||
|
||||
// Change Particle Emission Rate (UP/DOWN arrows)
|
||||
if (IsKeyPressed(KeyboardKey.Up)) emissionRate++;
|
||||
if (IsKeyPressed(KeyboardKey.Down)) emissionRate--;
|
||||
|
||||
// Change Particle Type (LEFT/RIGHT arrows)
|
||||
if (IsKeyPressed(KeyboardKey.Right)) currentType = (currentType == ParticleType.Fire) ? ParticleType.Water : (ParticleType)((int)currentType + 1);
|
||||
if (IsKeyPressed(KeyboardKey.Left)) currentType = (currentType == ParticleType.Water) ? ParticleType.Fire : (ParticleType)((int)currentType - 1);
|
||||
|
||||
if (IsMouseButtonDown(MouseButton.Left)) emitterPosition = GetMousePosition();
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
// Call the function with a loop to draw all particles
|
||||
DrawParticles();
|
||||
|
||||
// Draw UI and Instructions
|
||||
DrawRectangle(5, 5, 315, 75, Fade(Color.SkyBlue, 0.5f));
|
||||
DrawRectangleLines(5, 5, 315, 75, Color.Blue);
|
||||
|
||||
DrawText("CONTROLS:", 15, 15, 10, Color.Black);
|
||||
DrawText("UP/DOWN: Change Particle Emission Rate", 15, 35, 10, Color.Black);
|
||||
DrawText("LEFT/RIGHT: Change Particle Type (Water, Smoke, Fire)", 15, 55, 10, Color.Black);
|
||||
|
||||
if (emissionRate < 0) DrawText($"Particles every {-emissionRate} frames | Type: {particleTypeNames[(int)currentType]}", 15, 95, 10, Color.DarkGray);
|
||||
else DrawText($"{emissionRate + 1} Particles per frame | Type: {particleTypeNames[(int)currentType]}", 15, 95, 10, Color.DarkGray);
|
||||
|
||||
DrawFPS(screenWidth - 80, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
private void EmitParticle(Vector2 emitterPosition, ParticleType type)
|
||||
{
|
||||
int index = AddToCircularBuffer();
|
||||
|
||||
// If buffer is full, index is -1
|
||||
if (index != -1)
|
||||
{
|
||||
ref Particle newParticle = ref buffer[index];
|
||||
|
||||
// Fill particle properties
|
||||
newParticle.position = emitterPosition;
|
||||
newParticle.alive = true;
|
||||
newParticle.lifeTime = 0.0f;
|
||||
newParticle.type = type;
|
||||
float speed = (float)(random.Next(10)) / 5.0f;
|
||||
switch (type)
|
||||
{
|
||||
case ParticleType.Water:
|
||||
{
|
||||
newParticle.radius = 5.0f;
|
||||
newParticle.color = Color.Blue;
|
||||
} break;
|
||||
case ParticleType.Smoke:
|
||||
{
|
||||
newParticle.radius = 7.0f;
|
||||
newParticle.color = Color.Gray;
|
||||
} break;
|
||||
case ParticleType.Fire:
|
||||
{
|
||||
newParticle.radius = 10.0f;
|
||||
newParticle.color = Color.Yellow;
|
||||
speed /= 10.0f;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
float direction = (float)(random.Next(360));
|
||||
newParticle.velocity = new(speed * MathF.Cos(direction * DEG2RAD), speed * MathF.Sin(direction * DEG2RAD));
|
||||
}
|
||||
}
|
||||
|
||||
private int AddToCircularBuffer()
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
// Check if buffer full
|
||||
if (((head + 1) % MAX_PARTICLES) != tail)
|
||||
{
|
||||
// Add new particle to the head position and advance head
|
||||
index = head;
|
||||
head = (head + 1) % MAX_PARTICLES;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private void UpdateParticles(int screenWidth, int screenHeight)
|
||||
{
|
||||
for (int i = tail; i != head; i = (i + 1) % MAX_PARTICLES)
|
||||
{
|
||||
// Update particle life and positions
|
||||
buffer[i].lifeTime += 1.0f / 60.0f; // 60 FPS -> 1/60 seconds per frame
|
||||
|
||||
switch (buffer[i].type)
|
||||
{
|
||||
case ParticleType.Water:
|
||||
{
|
||||
buffer[i].position.X += buffer[i].velocity.X;
|
||||
buffer[i].velocity.Y += 0.2f; // Gravity
|
||||
buffer[i].position.Y += buffer[i].velocity.Y;
|
||||
} break;
|
||||
case ParticleType.Smoke:
|
||||
{
|
||||
buffer[i].position.X += buffer[i].velocity.X;
|
||||
buffer[i].velocity.Y -= 0.05f; // Upwards
|
||||
buffer[i].position.Y += buffer[i].velocity.Y;
|
||||
buffer[i].radius += 0.5f; // Increment radius: smoke expands
|
||||
buffer[i].color.A -= 4; // Decrement alpha: smoke fades
|
||||
|
||||
// If alpha transparent, particle dies
|
||||
if (buffer[i].color.A < 4) buffer[i].alive = false;
|
||||
} break;
|
||||
case ParticleType.Fire:
|
||||
{
|
||||
// Add a little horizontal oscillation to fire particles
|
||||
buffer[i].position.X += buffer[i].velocity.X + MathF.Cos(buffer[i].lifeTime * 215.0f);
|
||||
buffer[i].velocity.Y -= 0.05f; // Upwards
|
||||
buffer[i].position.Y += buffer[i].velocity.Y;
|
||||
buffer[i].radius -= 0.15f; // Decrement radius: fire shrinks
|
||||
buffer[i].color.G -= 3; // Decrement green: fire turns reddish starting from yellow
|
||||
|
||||
// If radius too small, particle dies
|
||||
if (buffer[i].radius <= 0.02f) buffer[i].alive = false;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// Disable particle when out of screen
|
||||
Vector2 center = buffer[i].position;
|
||||
float radius = buffer[i].radius;
|
||||
|
||||
if ((center.X < -radius) || (center.X > (screenWidth + radius)) ||
|
||||
(center.Y < -radius) || (center.Y > (screenHeight + radius)))
|
||||
{
|
||||
buffer[i].alive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCircularBuffer()
|
||||
{
|
||||
// Update circular buffer: advance tail over dead particles
|
||||
while ((tail != head) && !buffer[tail].alive)
|
||||
{
|
||||
tail = (tail + 1) % MAX_PARTICLES;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawParticles()
|
||||
{
|
||||
for (int i = tail; i != head; i = (i + 1) % MAX_PARTICLES)
|
||||
{
|
||||
if (buffer[i].alive)
|
||||
{
|
||||
DrawCircleV(buffer[i].position,
|
||||
buffer[i].radius,
|
||||
buffer[i].color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - simple particles");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new SimpleParticles();
|
||||
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;
|
||||
}
|
||||
}
|
||||
387
Examples/Shapes/SplinesDrawing.cs
Normal file
387
Examples/Shapes/SplinesDrawing.cs
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - splines drawing
|
||||
*
|
||||
* Example complexity rating: [★★★☆] 3/4
|
||||
*
|
||||
* Example originally created with raylib 5.0, last time updated with raylib 5.0
|
||||
*
|
||||
* 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) 2023-2025 Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class SplinesDrawing : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int MAX_SPLINE_POINTS = 32;
|
||||
|
||||
public string Name => "Shapes / Splines Drawing";
|
||||
|
||||
public string Title => "raylib [shapes] example - splines drawing";
|
||||
|
||||
public ConfigFlags ConfigFlags => ConfigFlags.Msaa4xHint;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Cubic Bezier spline control points
|
||||
// NOTE: Every segment has two control points
|
||||
private struct ControlPoint
|
||||
{
|
||||
public Vector2 start;
|
||||
public Vector2 end;
|
||||
}
|
||||
|
||||
// Spline types
|
||||
private const int SPLINE_LINEAR = 0; // Linear
|
||||
private const int SPLINE_BASIS = 1; // B-Spline
|
||||
private const int SPLINE_CATMULLROM = 2; // Catmull-Rom
|
||||
private const int SPLINE_BEZIER = 3; // Cubic Bezier
|
||||
|
||||
private Vector2[] points;
|
||||
private Vector2[] pointsInterleaved;
|
||||
|
||||
private int pointCount;
|
||||
private int selectedPoint;
|
||||
private int focusedPoint;
|
||||
private int selectedControlIndex; // -1 when none
|
||||
private bool selectedControlStart;
|
||||
private int focusedControlIndex; // -1 when none
|
||||
private bool focusedControlStart;
|
||||
|
||||
private ControlPoint[] control;
|
||||
|
||||
// Spline config variables
|
||||
private float splineThickness;
|
||||
private int splineTypeActive; // 0-Linear, 1-BSpline, 2-CatmullRom, 3-Bezier
|
||||
private bool splineTypeEditMode;
|
||||
private bool splineHelpersActive;
|
||||
|
||||
// Minimal raygui-like global lock
|
||||
private static bool guiLocked;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
points = new Vector2[MAX_SPLINE_POINTS];
|
||||
points[0] = new Vector2(50.0f, 400.0f);
|
||||
points[1] = new Vector2(160.0f, 220.0f);
|
||||
points[2] = new Vector2(340.0f, 380.0f);
|
||||
points[3] = new Vector2(520.0f, 60.0f);
|
||||
points[4] = new Vector2(710.0f, 260.0f);
|
||||
|
||||
// Array required for spline bezier-cubic,
|
||||
// including control points interleaved with start-end segment points
|
||||
pointsInterleaved = new Vector2[3 * (MAX_SPLINE_POINTS - 1) + 1];
|
||||
|
||||
pointCount = 5;
|
||||
selectedPoint = -1;
|
||||
focusedPoint = -1;
|
||||
selectedControlIndex = -1;
|
||||
focusedControlIndex = -1;
|
||||
|
||||
// Cubic Bezier control points initialization
|
||||
control = new ControlPoint[MAX_SPLINE_POINTS - 1];
|
||||
for (int i = 0; i < pointCount - 1; i++)
|
||||
{
|
||||
control[i].start = new Vector2(points[i].X + 50, points[i].Y);
|
||||
control[i].end = new Vector2(points[i + 1].X - 50, points[i + 1].Y);
|
||||
}
|
||||
|
||||
splineThickness = 8.0f;
|
||||
splineTypeActive = SPLINE_LINEAR;
|
||||
splineTypeEditMode = false;
|
||||
splineHelpersActive = true;
|
||||
|
||||
guiLocked = false;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Spline points creation logic (at the end of spline)
|
||||
if (IsMouseButtonPressed(MouseButton.Right) && (pointCount < MAX_SPLINE_POINTS))
|
||||
{
|
||||
points[pointCount] = GetMousePosition();
|
||||
int i = pointCount - 1;
|
||||
control[i].start = new Vector2(points[i].X + 50, points[i].Y);
|
||||
control[i].end = new Vector2(points[i + 1].X - 50, points[i + 1].Y);
|
||||
pointCount++;
|
||||
}
|
||||
|
||||
// Spline point focus and selection logic
|
||||
if ((selectedPoint == -1) && ((splineTypeActive != SPLINE_BEZIER) || (selectedControlIndex == -1)))
|
||||
{
|
||||
focusedPoint = -1;
|
||||
for (int i = 0; i < pointCount; i++)
|
||||
{
|
||||
if (CheckCollisionPointCircle(GetMousePosition(), points[i], 8.0f))
|
||||
{
|
||||
focusedPoint = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsMouseButtonPressed(MouseButton.Left)) selectedPoint = focusedPoint;
|
||||
}
|
||||
|
||||
// Spline point movement logic
|
||||
if (selectedPoint >= 0)
|
||||
{
|
||||
points[selectedPoint] = GetMousePosition();
|
||||
if (IsMouseButtonReleased(MouseButton.Left)) selectedPoint = -1;
|
||||
}
|
||||
|
||||
// Cubic Bezier spline control points logic
|
||||
if ((splineTypeActive == SPLINE_BEZIER) && (focusedPoint == -1))
|
||||
{
|
||||
// Spline control point focus and selection logic
|
||||
if (selectedControlIndex == -1)
|
||||
{
|
||||
focusedControlIndex = -1;
|
||||
for (int i = 0; i < pointCount - 1; i++)
|
||||
{
|
||||
if (CheckCollisionPointCircle(GetMousePosition(), control[i].start, 6.0f))
|
||||
{
|
||||
focusedControlIndex = i;
|
||||
focusedControlStart = true;
|
||||
break;
|
||||
}
|
||||
else if (CheckCollisionPointCircle(GetMousePosition(), control[i].end, 6.0f))
|
||||
{
|
||||
focusedControlIndex = i;
|
||||
focusedControlStart = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsMouseButtonPressed(MouseButton.Left))
|
||||
{
|
||||
selectedControlIndex = focusedControlIndex;
|
||||
selectedControlStart = focusedControlStart;
|
||||
}
|
||||
}
|
||||
|
||||
// Spline control point movement logic
|
||||
if (selectedControlIndex != -1)
|
||||
{
|
||||
if (selectedControlStart) control[selectedControlIndex].start = GetMousePosition();
|
||||
else control[selectedControlIndex].end = GetMousePosition();
|
||||
if (IsMouseButtonReleased(MouseButton.Left)) selectedControlIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Spline selection logic
|
||||
if (IsKeyPressed(KeyboardKey.One)) splineTypeActive = 0;
|
||||
else if (IsKeyPressed(KeyboardKey.Two)) splineTypeActive = 1;
|
||||
else if (IsKeyPressed(KeyboardKey.Three)) splineTypeActive = 2;
|
||||
else if (IsKeyPressed(KeyboardKey.Four)) splineTypeActive = 3;
|
||||
|
||||
// Clear selection when changing to a spline without control points
|
||||
if (IsKeyPressed(KeyboardKey.One) || IsKeyPressed(KeyboardKey.Two) || IsKeyPressed(KeyboardKey.Three)) selectedControlIndex = -1;
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
if (splineTypeActive == SPLINE_LINEAR)
|
||||
{
|
||||
// Draw spline: linear
|
||||
DrawSplineLinear(points, pointCount, splineThickness, Color.Red);
|
||||
}
|
||||
else if (splineTypeActive == SPLINE_BASIS)
|
||||
{
|
||||
// Draw spline: basis
|
||||
DrawSplineBasis(points, pointCount, splineThickness, Color.Red); // Provide connected points array
|
||||
}
|
||||
else if (splineTypeActive == SPLINE_CATMULLROM)
|
||||
{
|
||||
// Draw spline: catmull-rom
|
||||
DrawSplineCatmullRom(points, pointCount, splineThickness, Color.Red); // Provide connected points array
|
||||
}
|
||||
else if (splineTypeActive == SPLINE_BEZIER)
|
||||
{
|
||||
// NOTE: Cubic-bezier spline requires the 2 control points of each segment to be
|
||||
// provided interleaved with the start and end point of every segment
|
||||
for (int i = 0; i < (pointCount - 1); i++)
|
||||
{
|
||||
pointsInterleaved[3 * i] = points[i];
|
||||
pointsInterleaved[3 * i + 1] = control[i].start;
|
||||
pointsInterleaved[3 * i + 2] = control[i].end;
|
||||
}
|
||||
|
||||
pointsInterleaved[3 * (pointCount - 1)] = points[pointCount - 1];
|
||||
|
||||
// Draw spline: cubic-bezier (with control points)
|
||||
DrawSplineBezierCubic(pointsInterleaved, 3 * (pointCount - 1) + 1, splineThickness, Color.Red);
|
||||
|
||||
// Draw spline control points
|
||||
for (int i = 0; i < pointCount - 1; i++)
|
||||
{
|
||||
// Every cubic bezier point have two control points
|
||||
DrawCircleV(control[i].start, 6, Color.Gold);
|
||||
DrawCircleV(control[i].end, 6, Color.Gold);
|
||||
if (focusedControlIndex == i && focusedControlStart) DrawCircleV(control[i].start, 8, Color.Green);
|
||||
else if (focusedControlIndex == i && !focusedControlStart) DrawCircleV(control[i].end, 8, Color.Green);
|
||||
DrawLineEx(points[i], control[i].start, 1.0f, Color.LightGray);
|
||||
DrawLineEx(points[i + 1], control[i].end, 1.0f, Color.LightGray);
|
||||
|
||||
// Draw spline control lines
|
||||
DrawLineV(points[i], control[i].start, Color.Gray);
|
||||
DrawLineV(control[i].end, points[i + 1], Color.Gray);
|
||||
}
|
||||
}
|
||||
|
||||
if (splineHelpersActive)
|
||||
{
|
||||
// Draw spline point helpers
|
||||
for (int i = 0; i < pointCount; i++)
|
||||
{
|
||||
DrawCircleLinesV(points[i], (focusedPoint == i) ? 12.0f : 8.0f, (focusedPoint == i) ? Color.Blue : Color.DarkBlue);
|
||||
if ((splineTypeActive != SPLINE_LINEAR) &&
|
||||
(splineTypeActive != SPLINE_BEZIER) &&
|
||||
(i < pointCount - 1)) DrawLineV(points[i], points[i + 1], Color.Gray);
|
||||
|
||||
DrawText($"[{points[i].X:F0}, {points[i].Y:F0}]", (int)points[i].X, (int)points[i].Y + 10, 10, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
// Check all possible UI states that require controls lock
|
||||
if (splineTypeEditMode || (selectedPoint != -1) || (selectedControlIndex != -1)) GuiLock();
|
||||
|
||||
// Draw spline config
|
||||
GuiLabel(new Rectangle(12, 62, 140, 24), $"Spline thickness: {(int)splineThickness}");
|
||||
GuiSliderBar(new Rectangle(12, 60 + 24, 140, 16), null, null, ref splineThickness, 1.0f, 40.0f);
|
||||
|
||||
GuiCheckBox(new Rectangle(12, 110, 20, 20), "Show point helpers", ref splineHelpersActive);
|
||||
|
||||
if (splineTypeEditMode) GuiUnlock();
|
||||
|
||||
GuiLabel(new Rectangle(12, 10, 140, 24), "Spline type:");
|
||||
if (GuiDropdownBox(new Rectangle(12, 8 + 24, 140, 28), "LINEAR;BSPLINE;CATMULLROM;BEZIER", ref splineTypeActive, splineTypeEditMode)) splineTypeEditMode = !splineTypeEditMode;
|
||||
|
||||
GuiUnlock();
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Minimal raygui-like widgets (plain raylib re-implementation)
|
||||
//----------------------------------------------------------------------------------
|
||||
private static void GuiLock() => guiLocked = true;
|
||||
private static void GuiUnlock() => guiLocked = false;
|
||||
|
||||
private static void GuiLabel(Rectangle bounds, string text)
|
||||
{
|
||||
DrawText(text, (int)bounds.X, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
}
|
||||
|
||||
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 (!guiLocked && 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 (!string.IsNullOrEmpty(textLeft)) DrawText(textLeft, (int)bounds.X - MeasureText(textLeft, 10) - 5, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (!string.IsNullOrEmpty(textRight)) 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 (!guiLocked && 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);
|
||||
}
|
||||
|
||||
private static bool GuiDropdownBox(Rectangle bounds, string text, ref int active, bool editMode)
|
||||
{
|
||||
string[] items = text.Split(';');
|
||||
bool pressed = false;
|
||||
Vector2 mouse = GetMousePosition();
|
||||
bool clickable = !guiLocked;
|
||||
|
||||
bool hoverMain = CheckCollisionPointRec(mouse, bounds);
|
||||
DrawRectangleRec(bounds, editMode ? Color.SkyBlue : (hoverMain ? Color.LightGray : Color.RayWhite));
|
||||
DrawRectangleLinesEx(bounds, 1, editMode ? Color.Blue : Color.Gray);
|
||||
DrawText(items[active], (int)bounds.X + 6, (int)(bounds.Y + bounds.Height / 2 - 5), 10, Color.DarkGray);
|
||||
|
||||
if (editMode)
|
||||
{
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
Rectangle itemRec = new Rectangle(bounds.X, bounds.Y + bounds.Height * (i + 1), bounds.Width, bounds.Height);
|
||||
bool hoverItem = CheckCollisionPointRec(mouse, itemRec);
|
||||
DrawRectangleRec(itemRec, hoverItem ? Color.SkyBlue : Color.RayWhite);
|
||||
DrawRectangleLinesEx(itemRec, 1, Color.Gray);
|
||||
DrawText(items[i], (int)itemRec.X + 6, (int)(itemRec.Y + itemRec.Height / 2 - 5), 10, Color.DarkGray);
|
||||
if (clickable && hoverItem && IsMouseButtonPressed(MouseButton.Left))
|
||||
{
|
||||
active = i;
|
||||
pressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clickable && hoverMain && IsMouseButtonPressed(MouseButton.Left)) pressed = true;
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
SetConfigFlags(ConfigFlags.Msaa4xHint);
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - splines drawing");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new SplinesDrawing();
|
||||
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;
|
||||
}
|
||||
}
|
||||
180
Examples/Shapes/StarfieldEffect.cs
Normal file
180
Examples/Shapes/StarfieldEffect.cs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - starfield effect
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 5.5, last time updated with raylib 6.0
|
||||
*
|
||||
* Example contributed by JP Mortiboys (@themushroompirates) 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 JP Mortiboys (@themushroompirates)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath; // Required for: Lerp()
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class StarfieldEffect : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
private const int STAR_COUNT = 420;
|
||||
|
||||
public string Name => "Shapes / Starfield Effect";
|
||||
|
||||
public string Title => "raylib [shapes] example - starfield effect";
|
||||
|
||||
private Color bgColor;
|
||||
|
||||
// Speed at which we fly forward
|
||||
private float speed;
|
||||
|
||||
// We're either drawing lines or circles
|
||||
private bool drawLines;
|
||||
|
||||
private Vector3[] stars;
|
||||
private Vector2[] starsScreenPos;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
bgColor = ColorLerp(Color.DarkBlue, Color.Black, 0.69f);
|
||||
|
||||
// Speed at which we fly forward
|
||||
speed = 10.0f / 9.0f;
|
||||
|
||||
// We're either drawing lines or circles
|
||||
drawLines = true;
|
||||
|
||||
stars = new Vector3[STAR_COUNT];
|
||||
starsScreenPos = new Vector2[STAR_COUNT];
|
||||
|
||||
// Setup the stars with a random position
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
stars[i].X = (float)GetRandomValue(-screenWidth / 2, (int)screenWidth / 2);
|
||||
stars[i].Y = (float)GetRandomValue(-screenHeight / 2, (int)screenHeight / 2);
|
||||
stars[i].Z = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Change speed based on mouse
|
||||
float mouseMove = GetMouseWheelMove();
|
||||
if ((int)mouseMove != 0) speed += 2.0f * mouseMove / 9.0f;
|
||||
if (speed < 0.0f) speed = 0.1f;
|
||||
else if (speed > 2.0f) speed = 2.0f;
|
||||
|
||||
// Toggle lines / points with space bar
|
||||
if (IsKeyPressed(KeyboardKey.Space)) drawLines = !drawLines;
|
||||
|
||||
float dt = GetFrameTime();
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
// Update star's timer
|
||||
stars[i].Z -= dt * speed;
|
||||
|
||||
// Calculate the screen position
|
||||
starsScreenPos[i] = new Vector2(
|
||||
screenWidth * 0.5f + stars[i].X / stars[i].Z,
|
||||
screenHeight * 0.5f + stars[i].Y / stars[i].Z
|
||||
);
|
||||
|
||||
// If the star is too old, or offscreen, it dies and we make a new random one
|
||||
if ((stars[i].Z < 0.0f) || (starsScreenPos[i].X < 0) || (starsScreenPos[i].Y < 0.0f) ||
|
||||
(starsScreenPos[i].X > screenWidth) || (starsScreenPos[i].Y > screenHeight))
|
||||
{
|
||||
stars[i].X = (float)GetRandomValue(-screenWidth / 2, screenWidth / 2);
|
||||
stars[i].Y = (float)GetRandomValue(-screenHeight / 2, screenHeight / 2);
|
||||
stars[i].Z = 1.0f;
|
||||
}
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(bgColor);
|
||||
|
||||
for (int i = 0; i < STAR_COUNT; i++)
|
||||
{
|
||||
if (drawLines)
|
||||
{
|
||||
// Get the time a little while ago for this star, but clamp it
|
||||
float t = Clamp(stars[i].Z + 1.0f / 32.0f, 0.0f, 1.0f);
|
||||
|
||||
// If it's different enough from the current time, we proceed
|
||||
if ((t - stars[i].Z) > 1e-3)
|
||||
{
|
||||
// Calculate the screen position of the old point
|
||||
Vector2 startPos = new Vector2(
|
||||
screenWidth * 0.5f + stars[i].X / t,
|
||||
screenHeight * 0.5f + stars[i].Y / t
|
||||
);
|
||||
|
||||
// Draw a line connecting the old point to the current point
|
||||
DrawLineV(startPos, starsScreenPos[i], Color.RayWhite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make the radius grow as the star ages
|
||||
float radius = Lerp(stars[i].Z, 1.0f, 5.0f);
|
||||
|
||||
// Draw the circle
|
||||
DrawCircleV(starsScreenPos[i], radius, Color.RayWhite);
|
||||
}
|
||||
}
|
||||
|
||||
DrawText($"[MOUSE WHEEL] Current Speed: {9.0f * speed / 2.0f:F0}", 10, 40, 20, Color.RayWhite);
|
||||
DrawText($"[SPACE] Current draw mode: {(drawLines ? "Lines" : "Circles")}", 10, 70, 20, Color.RayWhite);
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - starfield effect");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new StarfieldEffect();
|
||||
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;
|
||||
}
|
||||
}
|
||||
406
Examples/Shapes/TopDownLights.cs
Normal file
406
Examples/Shapes/TopDownLights.cs
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - top down lights
|
||||
*
|
||||
* Example complexity rating: [★★★★] 4/4
|
||||
*
|
||||
* Example originally created with raylib 4.2, last time updated with raylib 4.2
|
||||
*
|
||||
* Example contributed by Jeffery Myers (@JeffM2501) 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) 2022-2025 Jeffery Myers (@JeffM2501)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath;
|
||||
using static Raylib_cs.Rlgl;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class TopDownLights : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
// Custom Blend Modes
|
||||
private const int RLGL_SRC_ALPHA = 0x0302;
|
||||
private const int RLGL_MIN = 0x8007;
|
||||
private const int RLGL_MAX = 0x8008;
|
||||
|
||||
private const int MAX_BOXES = 20;
|
||||
private const int MAX_SHADOWS = MAX_BOXES * 3; // MAX_BOXES*3 - Each box can cast up to two shadow volumes for the edges it is away from, and one for the box itself
|
||||
private const int MAX_LIGHTS = 16;
|
||||
|
||||
public string Name => "Shapes / Top Down Lights";
|
||||
|
||||
public string Title => "raylib [shapes] example - top down lights";
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
// Shadow geometry type
|
||||
private class ShadowGeometry
|
||||
{
|
||||
public Vector2[] vertices = new Vector2[4];
|
||||
}
|
||||
|
||||
// Light info type
|
||||
private class LightInfo
|
||||
{
|
||||
public bool active; // Is this light slot active?
|
||||
public bool dirty; // Does this light need to be updated?
|
||||
public bool valid; // Is this light in a valid position?
|
||||
|
||||
public Vector2 position; // Light position
|
||||
public RenderTexture2D mask; // Alpha mask for the light
|
||||
public float outerRadius; // The distance the light touches
|
||||
public Rectangle bounds; // A cached rectangle of the light bounds to help with culling
|
||||
|
||||
public ShadowGeometry[] shadows = new ShadowGeometry[MAX_SHADOWS];
|
||||
public int shadowCount;
|
||||
|
||||
public LightInfo()
|
||||
{
|
||||
for (int i = 0; i < MAX_SHADOWS; i++) shadows[i] = new ShadowGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Global Variables Definition
|
||||
//------------------------------------------------------------------------------------
|
||||
private LightInfo[] lights;
|
||||
|
||||
private int boxCount;
|
||||
private Rectangle[] boxes;
|
||||
private Texture2D backgroundTexture;
|
||||
private RenderTexture2D lightMask;
|
||||
private int nextLight;
|
||||
private bool showLines;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
lights = new LightInfo[MAX_LIGHTS];
|
||||
for (int i = 0; i < MAX_LIGHTS; i++) lights[i] = new LightInfo();
|
||||
|
||||
// Initialize our 'world' of boxes
|
||||
boxCount = 0;
|
||||
boxes = new Rectangle[MAX_BOXES];
|
||||
SetupBoxes();
|
||||
|
||||
// Create a checkerboard ground texture
|
||||
Image img = GenImageChecked(64, 64, 32, 32, Color.DarkBrown, Color.DarkGray);
|
||||
backgroundTexture = LoadTextureFromImage(img);
|
||||
UnloadImage(img);
|
||||
|
||||
// Create a global light mask to hold all the blended lights
|
||||
lightMask = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
|
||||
|
||||
// Setup initial light
|
||||
SetupLight(0, 600, 400, 300);
|
||||
nextLight = 1;
|
||||
|
||||
showLines = false;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
// Drag light 0
|
||||
if (IsMouseButtonDown(MouseButton.Left)) MoveLight(0, GetMousePosition().X, GetMousePosition().Y);
|
||||
|
||||
// Make a new light
|
||||
if (IsMouseButtonPressed(MouseButton.Right) && (nextLight < MAX_LIGHTS))
|
||||
{
|
||||
SetupLight(nextLight, GetMousePosition().X, GetMousePosition().Y, 200);
|
||||
nextLight++;
|
||||
}
|
||||
|
||||
// Toggle debug info
|
||||
if (IsKeyPressed(KeyboardKey.F1)) showLines = !showLines;
|
||||
|
||||
// Update the lights and keep track if any were dirty so we know if we need to update the master light mask
|
||||
bool dirtyLights = false;
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
{
|
||||
if (UpdateLight(i, boxes, boxCount)) dirtyLights = true;
|
||||
}
|
||||
|
||||
// Update the light mask
|
||||
if (dirtyLights)
|
||||
{
|
||||
// Build up the light mask
|
||||
BeginTextureMode(lightMask);
|
||||
|
||||
ClearBackground(Color.Black);
|
||||
|
||||
// Force the blend mode to only set the alpha of the destination
|
||||
SetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MIN);
|
||||
SetBlendMode(BlendMode.Custom);
|
||||
|
||||
// Merge in all the light masks
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
{
|
||||
if (lights[i].active) DrawTextureRec(lights[i].mask.Texture, new Rectangle(0, 0, (float)GetScreenWidth(), -(float)GetScreenHeight()), Vector2Zero(), Color.White);
|
||||
}
|
||||
|
||||
DrawRenderBatchActive();
|
||||
|
||||
// Go back to normal blend
|
||||
SetBlendMode(BlendMode.Alpha);
|
||||
EndTextureMode();
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.Black);
|
||||
|
||||
// Draw the tile background
|
||||
DrawTextureRec(backgroundTexture, new Rectangle(0, 0, (float)GetScreenWidth(), (float)GetScreenHeight()), Vector2Zero(), Color.White);
|
||||
|
||||
// Overlay the shadows from all the lights
|
||||
DrawTextureRec(lightMask.Texture, new Rectangle(0, 0, (float)GetScreenWidth(), -(float)GetScreenHeight()), Vector2Zero(), ColorAlpha(Color.White, showLines ? 0.75f : 1.0f));
|
||||
|
||||
// Draw the lights
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
{
|
||||
if (lights[i].active) DrawCircle((int)lights[i].position.X, (int)lights[i].position.Y, 10, (i == 0) ? Color.Yellow : Color.White);
|
||||
}
|
||||
|
||||
if (showLines)
|
||||
{
|
||||
for (int s = 0; s < lights[0].shadowCount; s++)
|
||||
{
|
||||
DrawTriangleFan(lights[0].shadows[s].vertices, 4, Color.DarkPurple);
|
||||
}
|
||||
|
||||
for (int b = 0; b < boxCount; b++)
|
||||
{
|
||||
if (CheckCollisionRecs(boxes[b], lights[0].bounds)) DrawRectangleRec(boxes[b], Color.Purple);
|
||||
|
||||
DrawRectangleLines((int)boxes[b].X, (int)boxes[b].Y, (int)boxes[b].Width, (int)boxes[b].Height, Color.DarkBlue);
|
||||
}
|
||||
|
||||
DrawText("(F1) Hide Shadow Volumes", 10, 50, 10, Color.Green);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawText("(F1) Show Shadow Volumes", 10, 50, 10, Color.Green);
|
||||
}
|
||||
|
||||
DrawFPS(screenWidth - 80, 10);
|
||||
DrawText("Drag to move light #1", 10, 10, 10, Color.DarkGreen);
|
||||
DrawText("Right click to add new light", 10, 30, 10, Color.DarkGreen);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
UnloadTexture(backgroundTexture);
|
||||
UnloadRenderTexture(lightMask);
|
||||
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||
{
|
||||
if (lights[i].active) UnloadRenderTexture(lights[i].mask);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Module Functions Definition
|
||||
//------------------------------------------------------------------------------------
|
||||
// Move a light and mark it as dirty so that we update it's mask next frame
|
||||
private void MoveLight(int slot, float x, float y)
|
||||
{
|
||||
lights[slot].dirty = true;
|
||||
lights[slot].position.X = x;
|
||||
lights[slot].position.Y = y;
|
||||
|
||||
// update the cached bounds
|
||||
lights[slot].bounds.X = x - lights[slot].outerRadius;
|
||||
lights[slot].bounds.Y = y - lights[slot].outerRadius;
|
||||
}
|
||||
|
||||
// Compute a shadow volume for the edge
|
||||
// It takes the edge and projects it back by the light radius and turns it into a quad
|
||||
private void ComputeShadowVolumeForEdge(int slot, Vector2 sp, Vector2 ep)
|
||||
{
|
||||
if (lights[slot].shadowCount >= MAX_SHADOWS) return;
|
||||
|
||||
float extension = lights[slot].outerRadius * 2;
|
||||
|
||||
Vector2 spVector = Vector2Normalize(Vector2Subtract(sp, lights[slot].position));
|
||||
Vector2 spProjection = Vector2Add(sp, Vector2Scale(spVector, extension));
|
||||
|
||||
Vector2 epVector = Vector2Normalize(Vector2Subtract(ep, lights[slot].position));
|
||||
Vector2 epProjection = Vector2Add(ep, Vector2Scale(epVector, extension));
|
||||
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[0] = sp;
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[1] = ep;
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[2] = epProjection;
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[3] = spProjection;
|
||||
|
||||
lights[slot].shadowCount++;
|
||||
}
|
||||
|
||||
// Setup a light
|
||||
private void SetupLight(int slot, float x, float y, float radius)
|
||||
{
|
||||
lights[slot].active = true;
|
||||
lights[slot].valid = false; // The light must prove it is valid
|
||||
lights[slot].mask = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
|
||||
lights[slot].outerRadius = radius;
|
||||
|
||||
lights[slot].bounds.Width = radius * 2;
|
||||
lights[slot].bounds.Height = radius * 2;
|
||||
|
||||
MoveLight(slot, x, y);
|
||||
|
||||
// Force the render texture to have something in it
|
||||
DrawLightMask(slot);
|
||||
}
|
||||
|
||||
// See if a light needs to update it's mask
|
||||
private bool UpdateLight(int slot, Rectangle[] boxes, int count)
|
||||
{
|
||||
if (!lights[slot].active || !lights[slot].dirty) return false;
|
||||
|
||||
lights[slot].dirty = false;
|
||||
lights[slot].shadowCount = 0;
|
||||
lights[slot].valid = false;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
// Are we in a box? if so we are not valid
|
||||
if (CheckCollisionPointRec(lights[slot].position, boxes[i])) return false;
|
||||
|
||||
// If this box is outside our bounds, we can skip it
|
||||
if (!CheckCollisionRecs(lights[slot].bounds, boxes[i])) continue;
|
||||
|
||||
// Check the edges that are on the same side we are, and cast shadow volumes out from them
|
||||
|
||||
// Top
|
||||
Vector2 sp = new Vector2(boxes[i].X, boxes[i].Y);
|
||||
Vector2 ep = new Vector2(boxes[i].X + boxes[i].Width, boxes[i].Y);
|
||||
|
||||
if (lights[slot].position.Y > ep.Y) ComputeShadowVolumeForEdge(slot, sp, ep);
|
||||
|
||||
// Right
|
||||
sp = ep;
|
||||
ep.Y += boxes[i].Height;
|
||||
if (lights[slot].position.X < ep.X) ComputeShadowVolumeForEdge(slot, sp, ep);
|
||||
|
||||
// Bottom
|
||||
sp = ep;
|
||||
ep.X -= boxes[i].Width;
|
||||
if (lights[slot].position.Y < ep.Y) ComputeShadowVolumeForEdge(slot, sp, ep);
|
||||
|
||||
// Left
|
||||
sp = ep;
|
||||
ep.Y -= boxes[i].Height;
|
||||
if (lights[slot].position.X > ep.X) ComputeShadowVolumeForEdge(slot, sp, ep);
|
||||
|
||||
// The box itself
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[0] = new Vector2(boxes[i].X, boxes[i].Y);
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[1] = new Vector2(boxes[i].X, boxes[i].Y + boxes[i].Height);
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[2] = new Vector2(boxes[i].X + boxes[i].Width, boxes[i].Y + boxes[i].Height);
|
||||
lights[slot].shadows[lights[slot].shadowCount].vertices[3] = new Vector2(boxes[i].X + boxes[i].Width, boxes[i].Y);
|
||||
lights[slot].shadowCount++;
|
||||
}
|
||||
|
||||
lights[slot].valid = true;
|
||||
|
||||
DrawLightMask(slot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Draw the light and shadows to the mask for a light
|
||||
private void DrawLightMask(int slot)
|
||||
{
|
||||
// Use the light mask
|
||||
BeginTextureMode(lights[slot].mask);
|
||||
|
||||
ClearBackground(Color.White);
|
||||
|
||||
// Force the blend mode to only set the alpha of the destination
|
||||
SetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MIN);
|
||||
SetBlendMode(BlendMode.Custom);
|
||||
|
||||
// If we are valid, then draw the light radius to the alpha mask
|
||||
if (lights[slot].valid) DrawCircleGradient(lights[slot].position, lights[slot].outerRadius, ColorAlpha(Color.White, 0), Color.White);
|
||||
|
||||
DrawRenderBatchActive();
|
||||
|
||||
// Cut out the shadows from the light radius by forcing the alpha to maximum
|
||||
SetBlendMode(BlendMode.Alpha);
|
||||
SetBlendFactors(RLGL_SRC_ALPHA, RLGL_SRC_ALPHA, RLGL_MAX);
|
||||
SetBlendMode(BlendMode.Custom);
|
||||
|
||||
// Draw the shadows to the alpha mask
|
||||
for (int i = 0; i < lights[slot].shadowCount; i++)
|
||||
{
|
||||
DrawTriangleFan(lights[slot].shadows[i].vertices, 4, Color.White);
|
||||
}
|
||||
|
||||
DrawRenderBatchActive();
|
||||
|
||||
// Go back to normal blend mode
|
||||
SetBlendMode(BlendMode.Alpha);
|
||||
|
||||
EndTextureMode();
|
||||
}
|
||||
|
||||
// Set up some boxes
|
||||
private void SetupBoxes()
|
||||
{
|
||||
boxes[0] = new Rectangle(150, 80, 40, 40);
|
||||
boxes[1] = new Rectangle(1200, 700, 40, 40);
|
||||
boxes[2] = new Rectangle(200, 600, 40, 40);
|
||||
boxes[3] = new Rectangle(1000, 50, 40, 40);
|
||||
boxes[4] = new Rectangle(500, 350, 40, 40);
|
||||
|
||||
for (int i = 5; i < MAX_BOXES; i++)
|
||||
{
|
||||
boxes[i] = new Rectangle((float)GetRandomValue(0, GetScreenWidth()), (float)GetRandomValue(0, GetScreenHeight()), (float)GetRandomValue(10, 100), (float)GetRandomValue(10, 100));
|
||||
}
|
||||
|
||||
boxCount = MAX_BOXES;
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - top down lights");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new TopDownLights();
|
||||
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;
|
||||
}
|
||||
}
|
||||
141
Examples/Shapes/TriangleStrip.cs
Normal file
141
Examples/Shapes/TriangleStrip.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - triangle strip
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/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)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class TriangleStrip : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Triangle Strip";
|
||||
|
||||
public string Title => "raylib [shapes] example - triangle strip";
|
||||
|
||||
private Vector2[] points;
|
||||
private Vector2 center;
|
||||
private float segments;
|
||||
private float insideRadius;
|
||||
private float outsideRadius;
|
||||
private bool outline;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
points = new Vector2[122];
|
||||
center = new((screenWidth / 2.0f) - 125.0f, screenHeight / 2.0f);
|
||||
segments = 6.0f;
|
||||
insideRadius = 100.0f;
|
||||
outsideRadius = 150.0f;
|
||||
outline = true;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
int pointCount = (int)(segments);
|
||||
float angleStep = (360.0f / pointCount) * DEG2RAD;
|
||||
|
||||
for (int i = 0, i2 = 0; i < pointCount; i++, i2 += 2)
|
||||
{
|
||||
float angle1 = i * angleStep;
|
||||
points[i2] = new Vector2(center.X + MathF.Cos(angle1) * insideRadius, center.Y + MathF.Sin(angle1) * insideRadius);
|
||||
float angle2 = angle1 + angleStep / 2.0f;
|
||||
points[i2 + 1] = new Vector2(center.X + MathF.Cos(angle2) * outsideRadius, center.Y + MathF.Sin(angle2) * outsideRadius);
|
||||
}
|
||||
|
||||
points[pointCount * 2] = points[0];
|
||||
points[pointCount * 2 + 1] = points[1];
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
for (int i = 0; i < pointCount; i++)
|
||||
{
|
||||
Vector2 a = points[i * 2];
|
||||
Vector2 b = points[i * 2 + 1];
|
||||
Vector2 c = points[i * 2 + 2];
|
||||
Vector2 d = points[i * 2 + 3];
|
||||
|
||||
float angle1 = i * angleStep;
|
||||
DrawTriangle(c, b, a, ColorFromHSV(angle1 * RAD2DEG, 1.0f, 1.0f));
|
||||
DrawTriangle(d, b, c, ColorFromHSV((angle1 + angleStep / 2) * RAD2DEG, 1.0f, 1.0f));
|
||||
|
||||
if (outline)
|
||||
{
|
||||
DrawTriangleLines(a, b, c, Color.Black);
|
||||
DrawTriangleLines(c, b, d, Color.Black);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
//------------------------------------------------------------------------------
|
||||
// NOTE: raygui is not bound in raylib-cs, so the interactive controls are omitted
|
||||
// and 'segments'/'outline' keep their initial values.
|
||||
//GuiSliderBar(new Rectangle(640, 40, 120, 20), "Segments", TextFormat("%.0f", segments), ref segments, 6.0f, 60.0f);
|
||||
//GuiCheckBox(new Rectangle(640, 70, 20, 20), "Outline", ref outline);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
DrawFPS(10, 10);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - triangle strip");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new TriangleStrip();
|
||||
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;
|
||||
}
|
||||
}
|
||||
154
Examples/Shapes/VectorAngle.cs
Normal file
154
Examples/Shapes/VectorAngle.cs
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*******************************************************************************************
|
||||
*
|
||||
* raylib [shapes] example - vector angle
|
||||
*
|
||||
* Example complexity rating: [★★☆☆] 2/4
|
||||
*
|
||||
* Example originally created with raylib 1.0, last time updated with raylib 5.0
|
||||
*
|
||||
* 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) 2023-2025 Ramon Santamaria (@raysan5)
|
||||
*
|
||||
********************************************************************************************/
|
||||
|
||||
using System.Numerics;
|
||||
using static Raylib_cs.Raylib;
|
||||
using static Raylib_cs.Raymath; // Required for: Vector2LineAngle()
|
||||
|
||||
namespace Examples.Shapes;
|
||||
|
||||
public partial class VectorAngle : IExample
|
||||
{
|
||||
private const int screenWidth = 800;
|
||||
private const int screenHeight = 450;
|
||||
|
||||
public string Name => "Shapes / Vector Angle";
|
||||
|
||||
public string Title => "raylib [shapes] example - vector angle";
|
||||
|
||||
private Vector2 v0;
|
||||
private Vector2 v1;
|
||||
private Vector2 v2; // Updated with mouse position
|
||||
|
||||
private float angle; // Angle in degrees
|
||||
private int angleMode; // 0-Vector2Angle(), 1-Vector2LineAngle()
|
||||
|
||||
public void Init()
|
||||
{
|
||||
v0 = new(screenWidth / 2.0f, screenHeight / 2.0f);
|
||||
v1 = Vector2Add(v0, new Vector2(100.0f, 80.0f));
|
||||
v2 = new(0, 0); // Updated with mouse position
|
||||
|
||||
angle = 0.0f; // Angle in degrees
|
||||
angleMode = 0; // 0-Vector2Angle(), 1-Vector2LineAngle()
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// Update
|
||||
//----------------------------------------------------------------------------------
|
||||
float startangle = 0.0f;
|
||||
|
||||
if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1) * RAD2DEG;
|
||||
if (angleMode == 1) startangle = 0.0f;
|
||||
|
||||
v2 = GetMousePosition();
|
||||
|
||||
if (IsKeyPressed(KeyboardKey.Space)) angleMode = (angleMode == 0) ? 1 : 0;
|
||||
|
||||
if ((angleMode == 0) && IsMouseButtonDown(MouseButton.Right)) v1 = GetMousePosition();
|
||||
|
||||
if (angleMode == 0)
|
||||
{
|
||||
// Calculate angle between two vectors, considering a common origin (v0)
|
||||
Vector2 v1Normal = Vector2Normalize(Vector2Subtract(v1, v0));
|
||||
Vector2 v2Normal = Vector2Normalize(Vector2Subtract(v2, v0));
|
||||
|
||||
angle = Vector2Angle(v1Normal, v2Normal) * RAD2DEG;
|
||||
}
|
||||
else if (angleMode == 1)
|
||||
{
|
||||
// Calculate angle defined by a two vectors line, in reference to horizontal line
|
||||
angle = Vector2LineAngle(v0, v2) * RAD2DEG;
|
||||
}
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
// Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
BeginDrawing();
|
||||
|
||||
ClearBackground(Color.RayWhite);
|
||||
|
||||
if (angleMode == 0)
|
||||
{
|
||||
DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, Color.Black);
|
||||
DrawText("Right Click to Move V2", 10, 30, 20, Color.DarkGray);
|
||||
|
||||
DrawLineEx(v0, v1, 2.0f, Color.Black);
|
||||
DrawLineEx(v0, v2, 2.0f, Color.Red);
|
||||
|
||||
DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(Color.Green, 0.6f));
|
||||
}
|
||||
else if (angleMode == 1)
|
||||
{
|
||||
DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, Color.Black);
|
||||
|
||||
DrawLine(0, screenHeight / 2, screenWidth, screenHeight / 2, Color.LightGray);
|
||||
DrawLineEx(v0, v2, 2.0f, Color.Red);
|
||||
|
||||
DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(Color.Green, 0.6f));
|
||||
}
|
||||
|
||||
DrawText("v0", (int)v0.X, (int)v0.Y, 10, Color.DarkGray);
|
||||
|
||||
// If the line from v0 to v1 would overlap the text, move it's position up 10
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).Y > 0.0f) DrawText("v1", (int)v1.X, (int)v1.Y - 10, 10, Color.DarkGray);
|
||||
if (angleMode == 0 && Vector2Subtract(v0, v1).Y < 0.0f) DrawText("v1", (int)v1.X, (int)v1.Y, 10, Color.DarkGray);
|
||||
|
||||
// If angle mode 1, use v1 to emphasize the horizontal line
|
||||
if (angleMode == 1) DrawText("v1", (int)v0.X + 40, (int)v0.Y, 10, Color.DarkGray);
|
||||
|
||||
// position adjusted by -10 so it isn't hidden by cursor
|
||||
DrawText("v2", (int)v2.X - 10, (int)v2.Y - 10, 10, Color.DarkGray);
|
||||
|
||||
DrawText("Press SPACE to change MODE", 460, 10, 20, Color.DarkGray);
|
||||
DrawText($"ANGLE: {angle:F2}", 10, 70, 20, Color.Lime);
|
||||
|
||||
EndDrawing();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
}
|
||||
|
||||
public static int Main()
|
||||
{
|
||||
// Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
InitWindow(screenWidth, screenHeight, "raylib [shapes] example - vector angle");
|
||||
|
||||
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
var game = new VectorAngle();
|
||||
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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue