From b5847693f95969e6a2540055916343f22ca9c755 Mon Sep 17 00:00:00 2001 From: Ne1gh Date: Tue, 3 Jun 2025 14:46:30 +0300 Subject: [PATCH 1/4] Added Bezier curve and showed it in UtilsDemo. --- Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs | 207 ++++++++++++++++++ Samples/Monofoxe.Samples/Demos/UtilsDemo.cs | 35 ++- Samples/Monofoxe.Samples/GameController.cs | 2 +- 3 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs diff --git a/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs b/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs new file mode 100644 index 0000000..81a045d --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Drawing; + +namespace Monofoxe.Engine.Utils +{ + /// + /// Beizer curve. + /// Stolen from: https://github.com/shamim-akhtar/bezier-curve. + /// + public class BezierCurve + { + + // A look up table for factorials. Capped to 16. + private static float[] Factorial = new float[] + { + 1.0f, + 1.0f, + 2.0f, + 6.0f, + 24.0f, + 120.0f, + 720.0f, + 5040.0f, + 40320.0f, + 362880.0f, + 3628800.0f, + 39916800.0f, + 479001600.0f, + 6227020800.0f, + 87178291200.0f, + 1307674368000.0f, + 20922789888000.0f, + }; + + private static float Binomial(int n, int i) + { + float ni; + float a1 = Factorial[n]; + float a2 = Factorial[i]; + float a3 = Factorial[n - i]; + + ni = a1 / (a2 * a3); + + return ni; + } + + private static float Bernstein(int n, int i, float t) + { + float t_i = MathF.Pow(t, i); + float t_n_minus_i = MathF.Pow((1 - t), (n - i)); + + float basis = Binomial(n, i) * t_i * t_n_minus_i; + + return basis; + } + + + /// + /// Returns an interpolated point. t must be between 0 and 1. + /// + public static Vector3 GetVector3Point(float t, Vector3[] controlPoints) + { + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + if (t <= 0) + { + return controlPoints[0]; + } + if (t >= 1) + { + return controlPoints[controlPoints.Length - 1]; + } + + + Vector3 p = new Vector3(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector3 bn = Bernstein(N, i, t) * controlPoints[i]; + + p += bn; + } + + return p; + } + + + /// + /// Returns an interpolated point. t must be between 0 and 1. + /// + public static Vector2 GetVector2Point(float t, Vector2[] controlPoints) + { + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + if (t <= 0) + { + return controlPoints[0]; + } + if (t >= 1) + { + return controlPoints[controlPoints.Length - 1]; + } + + + Vector2 p = new Vector2(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector2 bn = Bernstein(N, i, t) * controlPoints[i]; + + p += bn; + } + + return p; + } + + + /// + /// Returns an array of points spaced with passed interval. + /// + public static Vector2[] GetVector2Points(Vector2[] controlPoints, float interval = 0.01f) + { + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + List points = new List(); + + for (float t = 0.0f; t <= 1.0f + interval - 0.0001f; t += interval) + { + Vector2 p = new Vector2(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector2 bn = Bernstein(N, i, t) * controlPoints[i]; + + p += bn; + } + + points.Add(p); + } + + return points.ToArray(); + } + + + /// + /// Returns an array of points spaced with passed interval. + /// + public static Vector3[] GetVector3Points(Vector3[] controlPoints, float interval = 0.01f) + { + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + List points = new List(); + + for (float t = 0.0f; t <= 1.0f + interval - 0.0001f; t += interval) + { + Vector3 p = new Vector3(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector3 bn = Bernstein(N, i, t) * controlPoints[i]; + + p += bn; + } + + points.Add(p); + } + + return points.ToArray(); + } + + public static void Draw(Vector2[] controlPoints, float interval = 0.01f) + { + var points = GetVector2Points(controlPoints, interval); + + for (var i = 0; i < points.Length - 1; i += 1) + { + LineShape.Draw(points[i], points[i + 1]); + } + } + } +} diff --git a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs index 1755d64..004e70e 100644 --- a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs @@ -56,7 +56,7 @@ public class UtilsDemo : Entity Sprite _fireSprite; - + Vector2[] _bezierCurvePoints; public UtilsDemo(Layer layer) : base(layer) { @@ -118,6 +118,18 @@ public UtilsDemo(Layer layer) : base(layer) _stateMachine.AddState(TestStates.Blue, Blue); _stateMachine.AddState(TestStates.Red, Red); // State machine. + + + // Bezier curve. + _bezierCurvePoints = new Vector2[] + { + new Vector2(57, 410), + new Vector2(70, 500), + new Vector2(291, 500), + new Vector2(330, 431) + }; + // Bezier curve. + } /// @@ -313,6 +325,8 @@ public override void Draw() DrawDampers(); + + DrawBeizerCurve(); } private FloatDamper _floatDamper1 = new FloatDamper(0, 1, 1, 0); @@ -345,6 +359,25 @@ private void DrawDampers() LineShape.Draw(_baseAnglePos, _baseAnglePos + _angleDamper.Value.ToVector2() * 50); } + private void DrawBeizerCurve() + { + for (var i = 0; i < _bezierCurvePoints.Length - 1; i += 1) + { + CircleShape.Draw(_bezierCurvePoints[i], 3, ShapeFill.Solid); + + LineShape.Draw(_bezierCurvePoints[i], _bezierCurvePoints[i + 1]); + } + CircleShape.Draw(_bezierCurvePoints[_bezierCurvePoints.Length - 1], 3, ShapeFill.Solid); + + + var prevColor = GraphicsMgr.CurrentColor; + GraphicsMgr.CurrentColor = Color.Red; + + BezierCurve.Draw(_bezierCurvePoints); + + GraphicsMgr.CurrentColor = prevColor; + } + public override void Destroy() { base.Destroy(); diff --git a/Samples/Monofoxe.Samples/GameController.cs b/Samples/Monofoxe.Samples/GameController.cs index fab4371..734ff24 100644 --- a/Samples/Monofoxe.Samples/GameController.cs +++ b/Samples/Monofoxe.Samples/GameController.cs @@ -75,7 +75,7 @@ private void OnPreDraw() => private void OnPostDraw() { _stopwatch.Stop(); - GameMgr.WindowManager.WindowTitle = "Rendering time: " + _stopwatch.Elapsed; + GameMgr.WindowManager.WindowTitle = Input.ScreenMousePosition.ToString(); _stopwatch.Reset(); } From 66b4d9f6a94284b6067faa04c74611264ca83d7a Mon Sep 17 00:00:00 2001 From: Ne1gh_ Date: Tue, 3 Jun 2025 16:26:00 +0300 Subject: [PATCH 2/4] Whoopsie-doopsie... --- Samples/Monofoxe.Samples/GameController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Samples/Monofoxe.Samples/GameController.cs b/Samples/Monofoxe.Samples/GameController.cs index 734ff24..fab4371 100644 --- a/Samples/Monofoxe.Samples/GameController.cs +++ b/Samples/Monofoxe.Samples/GameController.cs @@ -75,7 +75,7 @@ private void OnPreDraw() => private void OnPostDraw() { _stopwatch.Stop(); - GameMgr.WindowManager.WindowTitle = Input.ScreenMousePosition.ToString(); + GameMgr.WindowManager.WindowTitle = "Rendering time: " + _stopwatch.Elapsed; _stopwatch.Reset(); } From 705b4c069ac9950474a5694721e6880f3285e29e Mon Sep 17 00:00:00 2001 From: Ne1gh Date: Sun, 15 Jun 2025 11:08:02 +0300 Subject: [PATCH 3/4] Cut excessive things and placed remains in right places. --- .../Monofoxe.Engine/Drawing/BezierCurve.cs | 18 ++ Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs | 207 ------------------ Monofoxe/Monofoxe.Engine/Utils/GameMath.cs | 116 +++++++++- Samples/Monofoxe.Samples/Demos/UtilsDemo.cs | 6 +- 4 files changed, 129 insertions(+), 218 deletions(-) create mode 100644 Monofoxe/Monofoxe.Engine/Drawing/BezierCurve.cs delete mode 100644 Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs diff --git a/Monofoxe/Monofoxe.Engine/Drawing/BezierCurve.cs b/Monofoxe/Monofoxe.Engine/Drawing/BezierCurve.cs new file mode 100644 index 0000000..25e59f4 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Drawing/BezierCurve.cs @@ -0,0 +1,18 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Utils; + +namespace Monofoxe.Engine.Drawing +{ + public class BezierCurve + { + public static void Draw(Vector2[] controlPoints, float interval = 0.01f) + { + var points = GameMath.BezierCurvePoints(controlPoints, interval); + + for (var i = 0; i < points.Length - 1; i += 1) + { + LineShape.Draw(points[i], points[i + 1]); + } + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs b/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs deleted file mode 100644 index 81a045d..0000000 --- a/Monofoxe/Monofoxe.Engine/Utils/BezierCurve.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Xna.Framework; -using Monofoxe.Engine.Drawing; - -namespace Monofoxe.Engine.Utils -{ - /// - /// Beizer curve. - /// Stolen from: https://github.com/shamim-akhtar/bezier-curve. - /// - public class BezierCurve - { - - // A look up table for factorials. Capped to 16. - private static float[] Factorial = new float[] - { - 1.0f, - 1.0f, - 2.0f, - 6.0f, - 24.0f, - 120.0f, - 720.0f, - 5040.0f, - 40320.0f, - 362880.0f, - 3628800.0f, - 39916800.0f, - 479001600.0f, - 6227020800.0f, - 87178291200.0f, - 1307674368000.0f, - 20922789888000.0f, - }; - - private static float Binomial(int n, int i) - { - float ni; - float a1 = Factorial[n]; - float a2 = Factorial[i]; - float a3 = Factorial[n - i]; - - ni = a1 / (a2 * a3); - - return ni; - } - - private static float Bernstein(int n, int i, float t) - { - float t_i = MathF.Pow(t, i); - float t_n_minus_i = MathF.Pow((1 - t), (n - i)); - - float basis = Binomial(n, i) * t_i * t_n_minus_i; - - return basis; - } - - - /// - /// Returns an interpolated point. t must be between 0 and 1. - /// - public static Vector3 GetVector3Point(float t, Vector3[] controlPoints) - { - int N = controlPoints.Length - 1; - - if (N > 16) - { - throw new Exception("The maximum control points allowed is 16."); - } - - - if (t <= 0) - { - return controlPoints[0]; - } - if (t >= 1) - { - return controlPoints[controlPoints.Length - 1]; - } - - - Vector3 p = new Vector3(); - - for (int i = 0; i < controlPoints.Length; ++i) - { - Vector3 bn = Bernstein(N, i, t) * controlPoints[i]; - - p += bn; - } - - return p; - } - - - /// - /// Returns an interpolated point. t must be between 0 and 1. - /// - public static Vector2 GetVector2Point(float t, Vector2[] controlPoints) - { - int N = controlPoints.Length - 1; - - if (N > 16) - { - throw new Exception("The maximum control points allowed is 16."); - } - - - if (t <= 0) - { - return controlPoints[0]; - } - if (t >= 1) - { - return controlPoints[controlPoints.Length - 1]; - } - - - Vector2 p = new Vector2(); - - for (int i = 0; i < controlPoints.Length; ++i) - { - Vector2 bn = Bernstein(N, i, t) * controlPoints[i]; - - p += bn; - } - - return p; - } - - - /// - /// Returns an array of points spaced with passed interval. - /// - public static Vector2[] GetVector2Points(Vector2[] controlPoints, float interval = 0.01f) - { - int N = controlPoints.Length - 1; - - if (N > 16) - { - throw new Exception("The maximum control points allowed is 16."); - } - - - List points = new List(); - - for (float t = 0.0f; t <= 1.0f + interval - 0.0001f; t += interval) - { - Vector2 p = new Vector2(); - - for (int i = 0; i < controlPoints.Length; ++i) - { - Vector2 bn = Bernstein(N, i, t) * controlPoints[i]; - - p += bn; - } - - points.Add(p); - } - - return points.ToArray(); - } - - - /// - /// Returns an array of points spaced with passed interval. - /// - public static Vector3[] GetVector3Points(Vector3[] controlPoints, float interval = 0.01f) - { - int N = controlPoints.Length - 1; - - if (N > 16) - { - throw new Exception("The maximum control points allowed is 16."); - } - - - List points = new List(); - - for (float t = 0.0f; t <= 1.0f + interval - 0.0001f; t += interval) - { - Vector3 p = new Vector3(); - - for (int i = 0; i < controlPoints.Length; ++i) - { - Vector3 bn = Bernstein(N, i, t) * controlPoints[i]; - - p += bn; - } - - points.Add(p); - } - - return points.ToArray(); - } - - public static void Draw(Vector2[] controlPoints, float interval = 0.01f) - { - var points = GetVector2Points(controlPoints, interval); - - for (var i = 0; i < points.Length - 1; i += 1) - { - LineShape.Draw(points[i], points[i + 1]); - } - } - } -} diff --git a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs index 8537853..db852f2 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs @@ -208,24 +208,124 @@ public static float GetArea(List vertices) } + + #region Bezier curve stuff. + // A look up table for factorials. Capped to 16. + private static float[] Factorial = new float[] + { + 1.0f, + 1.0f, + 2.0f, + 6.0f, + 24.0f, + 120.0f, + 720.0f, + 5040.0f, + 40320.0f, + 362880.0f, + 3628800.0f, + 39916800.0f, + 479001600.0f, + 6227020800.0f, + 87178291200.0f, + 1307674368000.0f, + 20922789888000.0f, + }; + + private static float Binomial(int n, int i) + { + float ni; + float a1 = Factorial[n]; + float a2 = Factorial[i]; + float a3 = Factorial[n - i]; + + ni = a1 / (a2 * a3); + + return ni; + } + + private static float Bernstein(int n, int i, float value) + { + float value_i = MathF.Pow(value, i); + float value_n_minus_i = MathF.Pow((1 - value), (n - i)); + + float basis = Binomial(n, i) * value_i * value_n_minus_i; + + return basis; + } + #endregion + + /// - /// Calculates three-point bezier curve. + /// Calculates bezier curve. /// /// Should be in 0..1 range. - public static Vector2 BezierCurve(Vector2 startPoint, Vector2 controlPoint, Vector2 endPoint, float value) + public static Vector2 GetVector2Point(Vector2[] controlPoints, float value) { - float u = 1 - value; - float tt = value * value; - float uu = u * u; + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + if (value <= 0) + { + return controlPoints[0]; + } + if (value >= 1) + { + return controlPoints[controlPoints.Length - 1]; + } + - Vector2 pointOnCurve = uu * startPoint; - pointOnCurve += 2 * u * value * controlPoint; - pointOnCurve += tt * endPoint; + Vector2 pointOnCurve = new Vector2(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector2 bn = Bernstein(N, i, value) * controlPoints[i]; + + pointOnCurve += bn; + } return pointOnCurve; } + /// + /// Returns an array of points spaced with passed interval. + /// + public static Vector2[] BezierCurvePoints(Vector2[] controlPoints, float interval = 0.01f) + { + int N = controlPoints.Length - 1; + + if (N > 16) + { + throw new Exception("The maximum control points allowed is 16."); + } + + + List points = new List(); + + for (float t = 0.0f; t <= 1.0f + interval - 0.0001f; t += interval) + { + Vector2 p = new Vector2(); + + for (int i = 0; i < controlPoints.Length; ++i) + { + Vector2 bn = Bernstein(N, i, t) * controlPoints[i]; + + p += bn; + } + + points.Add(p); + } + + return points.ToArray(); + } + + /// /// Returns a projection aligned to the Y axis. /// diff --git a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs index 004e70e..e1dc4ac 100644 --- a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs @@ -124,9 +124,9 @@ public UtilsDemo(Layer layer) : base(layer) _bezierCurvePoints = new Vector2[] { new Vector2(57, 410), - new Vector2(70, 500), - new Vector2(291, 500), - new Vector2(330, 431) + new Vector2(170, 500), + new Vector2(291, 410), + new Vector2(330, 500) }; // Bezier curve. From 9799531a5f07087aa83dc70ac1b3af5766151f45 Mon Sep 17 00:00:00 2001 From: Ne1gh Date: Sun, 15 Jun 2025 11:11:56 +0300 Subject: [PATCH 4/4] Fixed naming. --- Monofoxe/Monofoxe.Engine/Utils/GameMath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs index db852f2..17d2bdf 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs @@ -260,7 +260,7 @@ private static float Bernstein(int n, int i, float value) /// Calculates bezier curve. /// /// Should be in 0..1 range. - public static Vector2 GetVector2Point(Vector2[] controlPoints, float value) + public static Vector2 BezierCurve(Vector2[] controlPoints, float value) { int N = controlPoints.Length - 1;