diff --git a/Curve/ArgumentChecker.cs b/Curve/ArgumentChecker.cs
new file mode 100644
index 0000000..005d6e6
--- /dev/null
+++ b/Curve/ArgumentChecker.cs
@@ -0,0 +1,13 @@
+namespace MoonTools.Core.Curve
+{
+ public static class ArgumentChecker
+ {
+ public static void CheckT(float t, float startTime = 0, float endTime = 1)
+ {
+ if (t < startTime || t > endTime)
+ {
+ throw new System.ArgumentException($"{t} is not a valid value for t. Must be between {startTime} and {endTime}.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Curve/CubicBezierCurve3D.cs b/Curve/CubicBezierCurve3D.cs
index c079f95..c206083 100644
--- a/Curve/CubicBezierCurve3D.cs
+++ b/Curve/CubicBezierCurve3D.cs
@@ -27,7 +27,7 @@ namespace MoonTools.Core.Curve
public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
- if (t < 0 || t > 1) { throw new System.ArgumentException($"{t} is an invalid value. Must be between 0 and 1"); }
+ ArgumentChecker.CheckT(t);
return (1f - t) * (1f - t) * (1f - t) * p0 +
3f * (1f - t) * (1f - t) * t * p1 +
@@ -37,12 +37,13 @@ namespace MoonTools.Core.Curve
public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float maxT)
{
+ ArgumentChecker.CheckT(t, minT, maxT);
return Point(p0, p1, p2, p3, TimeHelper.Normalized(t, minT, maxT));
}
public static Vector3 Velocity(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
- if (t < 0 || t > 1) { throw new System.ArgumentException($"{t} is an invalid value. Must be between 0 and 1"); }
+ ArgumentChecker.CheckT(t);
return 3f * (1f - t) * (1f - t) * (p1 - p0) +
6f * (1f - t) * t * (p2 - p1) +
@@ -51,6 +52,7 @@ namespace MoonTools.Core.Curve
public static Vector3 Velocity(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float maxT)
{
+ ArgumentChecker.CheckT(t, minT, maxT);
return Velocity(p0, p1, p2, p3, TimeHelper.Normalized(t, minT, maxT));
}
}
diff --git a/Curve/QuadraticBezierCurve2D.cs b/Curve/QuadraticBezierCurve2D.cs
new file mode 100644
index 0000000..0fad27c
--- /dev/null
+++ b/Curve/QuadraticBezierCurve2D.cs
@@ -0,0 +1,148 @@
+using Microsoft.Xna.Framework;
+using MoonTools.Core.Curve.Extensions;
+
+namespace MoonTools.Core.Curve
+{
+ ///
+ /// A 2-dimensional Bezier curve defined by 3 points.
+ ///
+ public struct QuadraticBezierCurve2D
+ {
+ ///
+ /// The start point.
+ ///
+ public Vector2 p0;
+
+ ///
+ /// The control point.
+ ///
+ public Vector2 p1;
+
+ ///
+ /// The end point.
+ ///
+ public Vector2 p2;
+
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ public QuadraticBezierCurve2D(Vector2 p0, Vector2 p1, Vector2 p2)
+ {
+ this.p0 = p0;
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ ///
+ /// Returns the curve coordinate given by t.
+ ///
+ /// A value between 0 and 1.
+ public Vector2 Point(float t) => Point(p0, p1, p2, t);
+
+ ///
+ /// Returns the curve coordinate given by a normalized time value.
+ ///
+ /// A time value between startTime and endTime.
+ ///
+ ///
+ public Vector2 Point(float t, float startTime, float endTime) => Point(p0, p1, p2, t, startTime, endTime);
+
+ ///
+ /// Returns the instantaneous velocity on the curve given by t.
+ ///
+ /// A value between 0 and 1.
+ public Vector2 Velocity(float t) => Velocity(p0, p1, p2, t);
+
+ ///
+ /// Returns the instantaneous velocity on the curve given by a normalized time value.
+ ///
+ /// A value between startTime and endTime.
+ ///
+ ///
+ public Vector2 Velocity(float t, float startTime, float endTime) => Velocity(p0, p1, p2, t, startTime, endTime);
+
+ ///
+ /// Performs degree elevation on the curve to turn it into a Cubic Bezier curve.
+ ///
+ public CubicBezierCurve2D AsCubic()
+ {
+ var (p0, p1, p2, p3) = AsCubic(this.p0, this.p1, this.p2);
+ return new CubicBezierCurve2D(p0, p1, p2, p3);
+ }
+
+ ///
+ /// Given quadratic control points, returns cubic control points.
+ ///
+ public static (Vector2, Vector2, Vector2, Vector2) AsCubic(Vector2 p0, Vector2 p1, Vector2 p2)
+ {
+ var cubicP0 = p0;
+ var cubicP1 = (2f / 3f * p1) + ((1f / 3f) * p0);
+ var cubicP2 = (2f / 3f * p1) + ((1f / 3f) * p2);
+ var cubicP3 = p2;
+
+ return (cubicP0, cubicP1, cubicP2, cubicP3);
+ }
+
+ ///
+ /// Returns the curve coordinate given by 3 points and a time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between 0 and 1.
+ public static Vector2 Point(Vector2 p0, Vector2 p1, Vector2 p2, float t)
+ {
+ return QuadraticBezierCurve3D.Point(
+ new Vector3(p0.X, p0.Y, 0),
+ new Vector3(p1.X, p1.Y, 0),
+ new Vector3(p2.X, p2.Y, 0),
+ t
+ ).XY();
+ }
+
+ ///
+ /// Returns the curve coordinate given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between startTime and endTime.
+ ///
+ ///
+ public static Vector2 Point(Vector2 p0, Vector2 p1, Vector2 p2, float t, float startTime, float endTime)
+ {
+ return Point(p0, p1, p2, TimeHelper.Normalized(t, startTime, endTime));
+ }
+
+ ///
+ /// Returns the instantaneous velocity given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between 0 and 1.
+ public static Vector2 Velocity(Vector2 p0, Vector2 p1, Vector2 p2, float t)
+ {
+ return QuadraticBezierCurve3D.Velocity(
+ new Vector3(p0.X, p0.Y, 0),
+ new Vector3(p1.X, p1.Y, 0),
+ new Vector3(p2.X, p2.Y, 0),
+ t
+ ).XY();
+ }
+
+ ///
+ /// Returns the instantaneous velocity given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between startTime and endTime.
+ ///
+ ///
+ public static Vector2 Velocity(Vector2 p0, Vector2 p1, Vector2 p2, float t, float startTime, float endTime)
+ {
+ return Velocity(p0, p1, p2, TimeHelper.Normalized(t, startTime, endTime));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Curve/QuadraticBezierCurve3D.cs b/Curve/QuadraticBezierCurve3D.cs
new file mode 100644
index 0000000..e710fb3
--- /dev/null
+++ b/Curve/QuadraticBezierCurve3D.cs
@@ -0,0 +1,142 @@
+using Microsoft.Xna.Framework;
+
+namespace MoonTools.Core.Curve
+{
+ ///
+ /// A 3-dimensional Bezier curve defined by 3 points.
+ ///
+ public struct QuadraticBezierCurve3D
+ {
+ ///
+ /// The start point.
+ ///
+ public Vector3 p0;
+
+ ///
+ /// The control point.
+ ///
+ public Vector3 p1;
+
+ ///
+ /// The end point.
+ ///
+ public Vector3 p2;
+
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ public QuadraticBezierCurve3D(Vector3 p0, Vector3 p1, Vector3 p2)
+ {
+ this.p0 = p0;
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ ///
+ /// Returns the curve coordinate given by t.
+ ///
+ /// A value between 0 and 1.
+ public Vector3 Point(float t) => Point(p0, p1, p2, t);
+
+ ///
+ /// Returns the curve coordinate given by a normalized time value.
+ ///
+ /// A time value between startTime and endTime.
+ ///
+ ///
+ public Vector3 Point(float t, float startTime, float endTime) => Point(p0, p1, p2, t, startTime, endTime);
+
+ ///
+ /// Returns the instantaneous velocity on the curve given by t.
+ ///
+ /// A value between 0 and 1.
+ public Vector3 Velocity(float t) => Velocity(p0, p1, p2, t);
+
+ ///
+ /// Returns the instantaneous velocity on the curve given by a normalized time value.
+ ///
+ /// An arbitrary value between startTime and endTime.
+ ///
+ ///
+ public Vector3 Velocity(float t, float startTime, float endTime) => Velocity(p0, p1, p2, t, startTime, endTime);
+
+ ///
+ /// Performs degree elevation on the curve to turn it into a Cubic Bezier curve.
+ ///
+ /// The same curve expressed as a cubic curve.
+ public CubicBezierCurve3D AsCubic()
+ {
+ var (p0, p1, p2, p3) = AsCubic(this.p0, this.p1, this.p2);
+ return new CubicBezierCurve3D(p0, p1, p2, p3);
+ }
+
+ ///
+ /// Given quadratic control points, returns cubic control points.
+ ///
+ public static (Vector3, Vector3, Vector3, Vector3) AsCubic(Vector3 p0, Vector3 p1, Vector3 p2)
+ {
+ var cubicP0 = p0;
+ var cubicP1 = (2f / 3f * p1) + ((1f / 3f) * p0);
+ var cubicP2 = (2f / 3f * p1) + ((1f / 3f) * p2);
+ var cubicP3 = p2;
+
+ return (cubicP0, cubicP1, cubicP2, cubicP3);
+ }
+
+ ///
+ /// Returns the curve coordinate given by 3 points and a time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between 0 and 1.
+ public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, float t)
+ {
+ var (cubicP0, cubicP1, cubicP2, cubicP3) = AsCubic(p0, p1, p2);
+ return CubicBezierCurve3D.Point(cubicP0, cubicP1, cubicP2, cubicP3, t);
+ }
+
+ ///
+ /// Returns the curve coordinate given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between startTime and endTime.
+ ///
+ ///
+ public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, float t, float startTime, float endTime)
+ {
+ var (cubicP0, cubicP1, cubicP2, cubicP3) = AsCubic(p0, p1, p2);
+ return CubicBezierCurve3D.Point(cubicP0, cubicP1, cubicP2, cubicP3, t, startTime, endTime);
+ }
+
+ ///
+ /// Returns the instantaneous velocity given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between 0 and 1.
+ public static Vector3 Velocity(Vector3 p0, Vector3 p1, Vector3 p2, float t)
+ {
+ var (cubicP0, cubicP1, cubicP2, cubicP3) = AsCubic(p0, p1, p2);
+ return CubicBezierCurve3D.Velocity(cubicP0, cubicP1, cubicP2, cubicP3, t);
+ }
+
+ ///
+ /// Returns the instantaneous velocity given by 3 points and a normalized time value.
+ ///
+ /// The start point.
+ /// The control point.
+ /// The end point.
+ /// A value between startTime and endTime.
+ ///
+ ///
+ public static Vector3 Velocity(Vector3 p0, Vector3 p1, Vector3 p2, float t, float startTime, float endTime)
+ {
+ var (cubicP0, cubicP1, cubicP2, cubicP3) = AsCubic(p0, p1, p2);
+ return CubicBezierCurve3D.Velocity(cubicP0, cubicP1, cubicP2, cubicP3, t, startTime, endTime);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Curve/Helper.cs b/Curve/TimeHelper.cs
similarity index 100%
rename from Curve/Helper.cs
rename to Curve/TimeHelper.cs
diff --git a/Test/CubicBezierCurve2D.cs b/Test/CubicBezierCurve2D.cs
index dd19a21..08f06ac 100644
--- a/Test/CubicBezierCurve2D.cs
+++ b/Test/CubicBezierCurve2D.cs
@@ -3,6 +3,7 @@ using FluentAssertions;
using MoonTools.Core.Curve;
using Microsoft.Xna.Framework;
+using System;
namespace Tests
{
@@ -20,6 +21,9 @@ namespace Tests
CubicBezierCurve2D.Point(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector2(0, 0));
CubicBezierCurve2D.Point(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector2(-2.1875f, -0.5f));
CubicBezierCurve2D.Point(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector2(2.1875f, 0.5f));
+
+ Action invalidTime = () => CubicBezierCurve2D.Point(p0, p1, p2, p3, 1.5f);
+ invalidTime.Should().Throw();
}
[Test]
@@ -33,6 +37,9 @@ namespace Tests
CubicBezierCurve2D.Point(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector2(0, 0));
CubicBezierCurve2D.Point(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector2(-2.1875f, -0.5f));
CubicBezierCurve2D.Point(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector2(2.1875f, 0.5f));
+
+ Action invalidTime = () => CubicBezierCurve2D.Point(p0, p1, p2, p3, 15, 2, 5);
+ invalidTime.Should().Throw();
}
[Test]
@@ -46,6 +53,9 @@ namespace Tests
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector2(9, 0));
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
+
+ Action invalidTime = () => CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 1.5f);
+ invalidTime.Should().Throw();
}
[Test]
@@ -59,6 +69,9 @@ namespace Tests
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector2(9, 0));
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
+
+ Action invalidTime = () => CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 15, 2, 5);
+ invalidTime.Should().Throw();
}
}
@@ -77,6 +90,8 @@ namespace Tests
myCurve.Point(0.5f).Should().BeEquivalentTo(new Vector2(0, 0));
myCurve.Point(0.25f).Should().BeEquivalentTo(new Vector2(-2.1875f, -0.5f));
myCurve.Point(0.75f).Should().BeEquivalentTo(new Vector2(2.1875f, 0.5f));
+
+ myCurve.Invoking(x => x.Point(1.5f)).Should().Throw();
}
[Test]
@@ -92,6 +107,8 @@ namespace Tests
myCurve.Point(3, 2, 4).Should().BeEquivalentTo(new Vector2(0, 0));
myCurve.Point(2, 1, 5).Should().BeEquivalentTo(new Vector2(-2.1875f, -0.5f));
myCurve.Point(11, 2, 14).Should().BeEquivalentTo(new Vector2(2.1875f, 0.5f));
+
+ myCurve.Invoking(x => x.Point(15, 2, 4)).Should().Throw();
}
[Test]
@@ -107,6 +124,8 @@ namespace Tests
myCurve.Velocity(0.5f).Should().BeEquivalentTo(new Vector2(9, 0));
myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
+
+ myCurve.Invoking(x => x.Velocity(1.5f)).Should().Throw();
}
[Test]
@@ -122,6 +141,8 @@ namespace Tests
myCurve.Velocity(3, 2, 4).Should().BeEquivalentTo(new Vector2(9, 0));
myCurve.Velocity(2, 1, 5).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
myCurve.Velocity(11, 2, 14).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
+
+ myCurve.Invoking(x => x.Velocity(15, 2, 5)).Should().Throw();
}
}
}
\ No newline at end of file
diff --git a/Test/CubicBezierCurve3D.cs b/Test/CubicBezierCurve3D.cs
index 912006a..baa89d1 100644
--- a/Test/CubicBezierCurve3D.cs
+++ b/Test/CubicBezierCurve3D.cs
@@ -3,6 +3,7 @@ using FluentAssertions;
using MoonTools.Core.Curve;
using Microsoft.Xna.Framework;
+using System;
namespace Tests
{
@@ -16,10 +17,15 @@ namespace Tests
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
+ CubicBezierCurve3D.Point(p0, p1, p2, p3, 0f).Should().BeEquivalentTo(p0);
CubicBezierCurve3D.Point(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezierCurve3D.Point(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezierCurve3D.Point(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
CubicBezierCurve3D.Point(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
+ CubicBezierCurve3D.Point(p0, p1, p2, p3, 1f).Should().BeEquivalentTo(p3);
+
+ Action invalidTime = () => CubicBezierCurve3D.Point(p0, p1, p2, p3, 1.5f);
+ invalidTime.Should().Throw();
}
[Test]
@@ -30,9 +36,14 @@ namespace Tests
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
+ CubicBezierCurve3D.Point(p0, p1, p2, p3, 15, 15, 17).Should().BeEquivalentTo(p0);
CubicBezierCurve3D.Point(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezierCurve3D.Point(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
CubicBezierCurve3D.Point(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
+ CubicBezierCurve3D.Point(p0, p1, p2, p3, 1, -8, 1).Should().BeEquivalentTo(p3);
+
+ Action invalidTime = () => CubicBezierCurve3D.Point(p0, p1, p2, p3, 15, 2, 5);
+ invalidTime.Should().Throw();
}
[Test]
@@ -46,6 +57,9 @@ namespace Tests
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
+
+ Action invalidTime = () => CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 1.5f);
+ invalidTime.Should().Throw();
}
[Test]
@@ -59,6 +73,9 @@ namespace Tests
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
+
+ Action invalidTime = () => CubicBezierCurve3D.Velocity(p0, p1, p2, p3, 15, 2, 5);
+ invalidTime.Should().Throw();
}
}
@@ -74,9 +91,13 @@ namespace Tests
new Vector3(4, 4, 0)
);
+ myCurve.Point(0f).Should().BeEquivalentTo(myCurve.p0);
myCurve.Point(0.5f).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
myCurve.Point(0.25f).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
myCurve.Point(0.75f).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
+ myCurve.Point(1f).Should().BeEquivalentTo(myCurve.p3);
+
+ myCurve.Invoking(x => x.Point(1.5f)).Should().Throw();
}
[Test]
@@ -89,9 +110,13 @@ namespace Tests
new Vector3(4, 4, 0)
);
+ myCurve.Point(15, 15, 17).Should().BeEquivalentTo(myCurve.p0);
myCurve.Point(3, 2, 4).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
myCurve.Point(2, 1, 5).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
myCurve.Point(11, 2, 14).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
+ myCurve.Point(1, -8, 1).Should().BeEquivalentTo(myCurve.p3);
+
+ myCurve.Invoking(x => x.Point(15, 2, 5)).Should().Throw();
}
[Test]
@@ -107,6 +132,8 @@ namespace Tests
myCurve.Velocity(0.5f).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
+
+ myCurve.Invoking(x => x.Velocity(1.5f)).Should().Throw();
}
[Test]
@@ -122,6 +149,8 @@ namespace Tests
myCurve.Velocity(3, 2, 4).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
myCurve.Velocity(2, 1, 5).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
myCurve.Velocity(11, 2, 14).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
+
+ myCurve.Invoking(x => x.Velocity(15, 2, 5)).Should().Throw();
}
}
}
\ No newline at end of file
diff --git a/Test/QuadraticBezierCurve2D.cs b/Test/QuadraticBezierCurve2D.cs
new file mode 100644
index 0000000..360f8a8
--- /dev/null
+++ b/Test/QuadraticBezierCurve2D.cs
@@ -0,0 +1,155 @@
+using NUnit.Framework;
+using FluentAssertions;
+
+using MoonTools.Core.Curve;
+using Microsoft.Xna.Framework;
+using System;
+
+namespace Tests
+{
+ public class QuadraticBezierCurve2DMathTests
+ {
+ [Test]
+ public void Point()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 0).Should().BeEquivalentTo(p0);
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 0.25f).Should().BeEquivalentTo(new Vector2(-1.25f, -3.5f));
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 0.75f).Should().BeEquivalentTo(new Vector2(2.75f, 0.5f));
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 1).Should().BeEquivalentTo(p2);
+
+ Action invalidTime = () => QuadraticBezierCurve2D.Point(p0, p1, p2, 1.5f);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void PointNormalized()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 15, 15, 17).Should().BeEquivalentTo(p0);
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 2, 1, 5).Should().BeEquivalentTo(new Vector2(-1.25f, -3.5f));
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 8, 2, 10).Should().BeEquivalentTo(new Vector2(2.75f, 0.5f));
+ QuadraticBezierCurve2D.Point(p0, p1, p2, 1, -8, 1).Should().BeEquivalentTo(p2);
+
+ Action invalidTime = () => QuadraticBezierCurve2D.Point(p0, p1, p2, 15, 2, 5);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void Velocity()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ QuadraticBezierCurve2D.Velocity(p0, p1, p2, 0.25f).Should().BeEquivalentTo(new Vector2(10f, 3.9999998f));
+ QuadraticBezierCurve2D.Velocity(p0, p1, p2, 0.75f).Should().BeEquivalentTo(new Vector2(6, 12));
+
+ Action invalidTime = () => QuadraticBezierCurve2D.Velocity(p0, p1, p2, 1.5f);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void VelocityNormalized()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ QuadraticBezierCurve2D.Velocity(p0, p1, p2, 2, 1, 5).Should().BeEquivalentTo(new Vector2(10f, 3.9999998f));
+ QuadraticBezierCurve2D.Velocity(p0, p1, p2, 8, 2, 10).Should().BeEquivalentTo(new Vector2(6, 12f));
+
+ Action invalidTime = () => QuadraticBezierCurve2D.Velocity(p0, p1, p1, 15, 2, 5);
+ invalidTime.Should().Throw();
+ }
+ }
+
+ public class QuadraticBezierCurve2DStructTests
+ {
+ [Test]
+ public void Point()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ var myCurve = new QuadraticBezierCurve2D(p0, p1, p2);
+
+ myCurve.Point(0).Should().BeEquivalentTo(p0);
+ myCurve.Point(0.25f).Should().BeEquivalentTo(new Vector2(-1.25f, -3.5f));
+ myCurve.Point(0.75f).Should().BeEquivalentTo(new Vector2(2.75f, 0.5f));
+ myCurve.Point(1).Should().BeEquivalentTo(p2);
+
+ myCurve.Invoking(x => x.Point(1.5f)).Should().Throw();
+ }
+
+ [Test]
+ public void PointNormalized()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ var myCurve = new QuadraticBezierCurve2D(p0, p1, p2);
+
+ myCurve.Point(15, 15, 17).Should().BeEquivalentTo(p0);
+ myCurve.Point(2, 1, 5).Should().BeEquivalentTo(new Vector2(-1.25f, -3.5f));
+ myCurve.Point(8, 2, 10).Should().BeEquivalentTo(new Vector2(2.75f, 0.5f));
+ myCurve.Point(1, -8, 1).Should().BeEquivalentTo(p2);
+
+ myCurve.Invoking(x => x.Point(15, 2, 5)).Should().Throw();
+ }
+
+ [Test]
+ public void Velocity()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ var myCurve = new QuadraticBezierCurve2D(p0, p1, p2);
+
+ myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector2(10f, 3.9999998f));
+ myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector2(6, 12));
+
+ myCurve.Invoking(x => x.Velocity(1.5f)).Should().Throw();
+ }
+
+ [Test]
+ public void VelocityNormalized()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ var myCurve = new QuadraticBezierCurve2D(p0, p1, p2);
+
+ myCurve.Velocity(2, 1, 5).Should().BeEquivalentTo(new Vector2(10f, 3.9999998f));
+ myCurve.Velocity(8, 2, 10).Should().BeEquivalentTo(new Vector2(6, 12));
+
+ myCurve.Invoking(x => x.Velocity(15, 2, 5)).Should().Throw();
+ }
+
+ [Test]
+ public void AsCubic()
+ {
+ var p0 = new Vector2(-4, -4);
+ var p1 = new Vector2(2, -4);
+ var p2 = new Vector2(4, 4);
+
+ var myCurve = new QuadraticBezierCurve2D(p0, p1, p2);
+ var myCubicCurve = myCurve.AsCubic();
+
+ myCurve.Point(0f).Should().BeEquivalentTo(myCubicCurve.Point(0f));
+ myCurve.Point(0.25f).Should().BeEquivalentTo(myCubicCurve.Point(0.25f));
+ myCurve.Point(0.75f).Should().BeEquivalentTo(myCubicCurve.Point(0.75f));
+ myCurve.Point(1f).Should().BeEquivalentTo(myCubicCurve.Point(1f));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Test/QuadraticBezierCurve3D.cs b/Test/QuadraticBezierCurve3D.cs
new file mode 100644
index 0000000..9662095
--- /dev/null
+++ b/Test/QuadraticBezierCurve3D.cs
@@ -0,0 +1,155 @@
+using NUnit.Framework;
+using FluentAssertions;
+
+using MoonTools.Core.Curve;
+using Microsoft.Xna.Framework;
+using System;
+
+namespace Tests
+{
+ public class QuadraticBezierCurve3DMathTests
+ {
+ [Test]
+ public void Point()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 0).Should().BeEquivalentTo(p0);
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 0.25f).Should().BeEquivalentTo(new Vector3(-1.25f, -3.5f, -0.5625f));
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 0.75f).Should().BeEquivalentTo(new Vector3(2.75f, 0.5f, 0.9375f));
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 1).Should().BeEquivalentTo(p2);
+
+ Action invalidTime = () => QuadraticBezierCurve3D.Point(p0, p1, p2, 1.5f);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void PointNormalized()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 15, 15, 17).Should().BeEquivalentTo(p0);
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 2, 1, 5).Should().BeEquivalentTo(new Vector3(-1.25f, -3.5f, -0.5625f));
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 8, 2, 10).Should().BeEquivalentTo(new Vector3(2.75f, 0.5f, 0.9375f));
+ QuadraticBezierCurve3D.Point(p0, p1, p2, 1, -8, 1).Should().BeEquivalentTo(p2);
+
+ Action invalidTime = () => QuadraticBezierCurve3D.Point(p0, p1, p2, 15, 2, 5);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void Velocity()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ QuadraticBezierCurve3D.Velocity(p0, p1, p2, 0.25f).Should().BeEquivalentTo(new Vector3(10f, 3.9999998f, 7.5f));
+ QuadraticBezierCurve3D.Velocity(p0, p1, p2, 0.75f).Should().BeEquivalentTo(new Vector3(6, 12, -1.5f));
+
+ Action invalidTime = () => QuadraticBezierCurve3D.Velocity(p0, p1, p2, 1.5f);
+ invalidTime.Should().Throw();
+ }
+
+ [Test]
+ public void VelocityNormalized()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ QuadraticBezierCurve3D.Velocity(p0, p1, p2, 2, 1, 5).Should().BeEquivalentTo(new Vector3(10f, 3.9999998f, 7.5f));
+ QuadraticBezierCurve3D.Velocity(p0, p1, p2, 8, 2, 10).Should().BeEquivalentTo(new Vector3(6, 12, -1.5f));
+
+ Action invalidTime = () => QuadraticBezierCurve3D.Velocity(p0, p1, p1, 15, 2, 5);
+ invalidTime.Should().Throw();
+ }
+ }
+
+ public class QuadraticBezierCurve3DStructTests
+ {
+ [Test]
+ public void Point()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ var myCurve = new QuadraticBezierCurve3D(p0, p1, p2);
+
+ myCurve.Point(0).Should().BeEquivalentTo(p0);
+ myCurve.Point(0.25f).Should().BeEquivalentTo(new Vector3(-1.25f, -3.5f, -0.5625f));
+ myCurve.Point(0.75f).Should().BeEquivalentTo(new Vector3(2.75f, 0.5f, 0.9375f));
+ myCurve.Point(1).Should().BeEquivalentTo(p2);
+
+ myCurve.Invoking(x => x.Point(1.5f)).Should().Throw();
+ }
+
+ [Test]
+ public void PointNormalized()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ var myCurve = new QuadraticBezierCurve3D(p0, p1, p2);
+
+ myCurve.Point(15, 15, 17).Should().BeEquivalentTo(p0);
+ myCurve.Point(2, 1, 5).Should().BeEquivalentTo(new Vector3(-1.25f, -3.5f, -0.5625f));
+ myCurve.Point(8, 2, 10).Should().BeEquivalentTo(new Vector3(2.75f, 0.5f, 0.9375f));
+ myCurve.Point(1, -8, 1).Should().BeEquivalentTo(p2);
+
+ myCurve.Invoking(x => x.Point(15, 2, 5)).Should().Throw();
+ }
+
+ [Test]
+ public void Velocity()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ var myCurve = new QuadraticBezierCurve3D(p0, p1, p2);
+
+ myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector3(10f, 3.9999998f, 7.5f));
+ myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector3(6, 12, -1.5f));
+
+ myCurve.Invoking(x => x.Velocity(1.5f)).Should().Throw();
+ }
+
+ [Test]
+ public void VelocityNormalized()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ var myCurve = new QuadraticBezierCurve3D(p0, p1, p2);
+
+ myCurve.Velocity(2, 1, 5).Should().BeEquivalentTo(new Vector3(10f, 3.9999998f, 7.5f));
+ myCurve.Velocity(8, 2, 10).Should().BeEquivalentTo(new Vector3(6, 12, -1.5f));
+
+ myCurve.Invoking(x => x.Velocity(15, 2, 5)).Should().Throw();
+ }
+
+ [Test]
+ public void AsCubic()
+ {
+ var p0 = new Vector3(-4, -4, -3);
+ var p1 = new Vector3(2, -4, 3);
+ var p2 = new Vector3(4, 4, 0);
+
+ var myCurve = new QuadraticBezierCurve3D(p0, p1, p2);
+ var myCubicCurve = myCurve.AsCubic();
+
+ myCurve.Point(0f).Should().BeEquivalentTo(myCubicCurve.Point(0f));
+ myCurve.Point(0.25f).Should().BeEquivalentTo(myCubicCurve.Point(0.25f));
+ myCurve.Point(0.75f).Should().BeEquivalentTo(myCubicCurve.Point(0.75f));
+ myCurve.Point(1f).Should().BeEquivalentTo(myCubicCurve.Point(1f));
+ }
+ }
+}
\ No newline at end of file