more implementations

master
thatcosmonaut 2019-10-28 18:48:36 -07:00
parent a51e7003fe
commit 611f9b589c
9 changed files with 667 additions and 2 deletions

13
Curve/ArgumentChecker.cs Normal file
View File

@ -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}.");
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace MoonTools.Core.Curve
public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) 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 + return (1f - t) * (1f - t) * (1f - t) * p0 +
3f * (1f - t) * (1f - t) * t * p1 + 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) 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)); 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) 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) + return 3f * (1f - t) * (1f - t) * (p1 - p0) +
6f * (1f - t) * t * (p2 - p1) + 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) 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)); return Velocity(p0, p1, p2, p3, TimeHelper.Normalized(t, minT, maxT));
} }
} }

View File

@ -0,0 +1,148 @@
using Microsoft.Xna.Framework;
using MoonTools.Core.Curve.Extensions;
namespace MoonTools.Core.Curve
{
/// <summary>
/// A 2-dimensional Bezier curve defined by 3 points.
/// </summary>
public struct QuadraticBezierCurve2D
{
/// <summary>
/// The start point.
/// </summary>
public Vector2 p0;
/// <summary>
/// The control point.
/// </summary>
public Vector2 p1;
/// <summary>
/// The end point.
/// </summary>
public Vector2 p2;
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
public QuadraticBezierCurve2D(Vector2 p0, Vector2 p1, Vector2 p2)
{
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
}
/// <summary>
/// Returns the curve coordinate given by t.
/// </summary>
/// <param name="t">A value between 0 and 1.</param>
public Vector2 Point(float t) => Point(p0, p1, p2, t);
/// <summary>
/// Returns the curve coordinate given by a normalized time value.
/// </summary>
/// <param name="t">A time value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
public Vector2 Point(float t, float startTime, float endTime) => Point(p0, p1, p2, t, startTime, endTime);
/// <summary>
/// Returns the instantaneous velocity on the curve given by t.
/// </summary>
/// <param name="t">A value between 0 and 1.</param>
public Vector2 Velocity(float t) => Velocity(p0, p1, p2, t);
/// <summary>
/// Returns the instantaneous velocity on the curve given by a normalized time value.
/// </summary>
/// <param name="t">A value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
public Vector2 Velocity(float t, float startTime, float endTime) => Velocity(p0, p1, p2, t, startTime, endTime);
/// <summary>
/// Performs degree elevation on the curve to turn it into a Cubic Bezier curve.
/// </summary>
public CubicBezierCurve2D AsCubic()
{
var (p0, p1, p2, p3) = AsCubic(this.p0, this.p1, this.p2);
return new CubicBezierCurve2D(p0, p1, p2, p3);
}
/// <summary>
/// Given quadratic control points, returns cubic control points.
/// </summary>
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);
}
/// <summary>
/// Returns the curve coordinate given by 3 points and a time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between 0 and 1.</param>
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();
}
/// <summary>
/// Returns the curve coordinate given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
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));
}
/// <summary>
/// Returns the instantaneous velocity given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between 0 and 1.</param>
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();
}
/// <summary>
/// Returns the instantaneous velocity given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
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));
}
}
}

View File

@ -0,0 +1,142 @@
using Microsoft.Xna.Framework;
namespace MoonTools.Core.Curve
{
/// <summary>
/// A 3-dimensional Bezier curve defined by 3 points.
/// </summary>
public struct QuadraticBezierCurve3D
{
/// <summary>
/// The start point.
/// </summary>
public Vector3 p0;
/// <summary>
/// The control point.
/// </summary>
public Vector3 p1;
/// <summary>
/// The end point.
/// </summary>
public Vector3 p2;
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
public QuadraticBezierCurve3D(Vector3 p0, Vector3 p1, Vector3 p2)
{
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
}
/// <summary>
/// Returns the curve coordinate given by t.
/// </summary>
/// <param name="t">A value between 0 and 1.</param>
public Vector3 Point(float t) => Point(p0, p1, p2, t);
/// <summary>
/// Returns the curve coordinate given by a normalized time value.
/// </summary>
/// <param name="t">A time value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
public Vector3 Point(float t, float startTime, float endTime) => Point(p0, p1, p2, t, startTime, endTime);
/// <summary>
/// Returns the instantaneous velocity on the curve given by t.
/// </summary>
/// <param name="t">A value between 0 and 1.</param>
public Vector3 Velocity(float t) => Velocity(p0, p1, p2, t);
/// <summary>
/// Returns the instantaneous velocity on the curve given by a normalized time value.
/// </summary>
/// <param name="t">An arbitrary value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
public Vector3 Velocity(float t, float startTime, float endTime) => Velocity(p0, p1, p2, t, startTime, endTime);
/// <summary>
/// Performs degree elevation on the curve to turn it into a Cubic Bezier curve.
/// </summary>
/// <returns>The same curve expressed as a cubic curve.</returns>
public CubicBezierCurve3D AsCubic()
{
var (p0, p1, p2, p3) = AsCubic(this.p0, this.p1, this.p2);
return new CubicBezierCurve3D(p0, p1, p2, p3);
}
/// <summary>
/// Given quadratic control points, returns cubic control points.
/// </summary>
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);
}
/// <summary>
/// Returns the curve coordinate given by 3 points and a time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between 0 and 1.</param>
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);
}
/// <summary>
/// Returns the curve coordinate given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
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);
}
/// <summary>
/// Returns the instantaneous velocity given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between 0 and 1.</param>
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);
}
/// <summary>
/// Returns the instantaneous velocity given by 3 points and a normalized time value.
/// </summary>
/// <param name="p0">The start point.</param>
/// <param name="p1">The control point.</param>
/// <param name="p2">The end point.</param>
/// <param name="t">A value between startTime and endTime.</param>
/// <param name="startTime"></param>
/// <param name="endTime"></param>
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);
}
}
}

View File

@ -3,6 +3,7 @@ using FluentAssertions;
using MoonTools.Core.Curve; using MoonTools.Core.Curve;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
namespace Tests 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.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.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)); 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<ArgumentException>();
} }
[Test] [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, 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, 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)); 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<ArgumentException>();
} }
[Test] [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.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.25f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
CubicBezierCurve2D.Velocity(p0, p1, p2, p3, 0.75f).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<ArgumentException>();
} }
[Test] [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, 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, 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)); 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<ArgumentException>();
} }
} }
@ -77,6 +90,8 @@ namespace Tests
myCurve.Point(0.5f).Should().BeEquivalentTo(new Vector2(0, 0)); 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.25f).Should().BeEquivalentTo(new Vector2(-2.1875f, -0.5f));
myCurve.Point(0.75f).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<ArgumentException>();
} }
[Test] [Test]
@ -92,6 +107,8 @@ namespace Tests
myCurve.Point(3, 2, 4).Should().BeEquivalentTo(new Vector2(0, 0)); 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(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.Point(11, 2, 14).Should().BeEquivalentTo(new Vector2(2.1875f, 0.5f));
myCurve.Invoking(x => x.Point(15, 2, 4)).Should().Throw<ArgumentException>();
} }
[Test] [Test]
@ -107,6 +124,8 @@ namespace Tests
myCurve.Velocity(0.5f).Should().BeEquivalentTo(new Vector2(9, 0)); myCurve.Velocity(0.5f).Should().BeEquivalentTo(new Vector2(9, 0));
myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector2(8.25f, 6f)); myCurve.Velocity(0.25f).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
myCurve.Velocity(0.75f).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<ArgumentException>();
} }
[Test] [Test]
@ -122,6 +141,8 @@ namespace Tests
myCurve.Velocity(3, 2, 4).Should().BeEquivalentTo(new Vector2(9, 0)); 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(2, 1, 5).Should().BeEquivalentTo(new Vector2(8.25f, 6f));
myCurve.Velocity(11, 2, 14).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<ArgumentException>();
} }
} }
} }

View File

@ -3,6 +3,7 @@ using FluentAssertions;
using MoonTools.Core.Curve; using MoonTools.Core.Curve;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
namespace Tests namespace Tests
{ {
@ -16,10 +17,15 @@ namespace Tests
var p2 = new Vector3(2, -4, 3); var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0); 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.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.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, 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<ArgumentException>();
} }
[Test] [Test]
@ -30,9 +36,14 @@ namespace Tests
var p2 = new Vector3(2, -4, 3); var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0); 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, 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, 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, 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<ArgumentException>();
} }
[Test] [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.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.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)); 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<ArgumentException>();
} }
[Test] [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, 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, 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)); 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<ArgumentException>();
} }
} }
@ -74,9 +91,13 @@ namespace Tests
new Vector3(4, 4, 0) 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.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.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(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<ArgumentException>();
} }
[Test] [Test]
@ -89,9 +110,13 @@ namespace Tests
new Vector3(4, 4, 0) 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(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(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(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<ArgumentException>();
} }
[Test] [Test]
@ -107,6 +132,8 @@ namespace Tests
myCurve.Velocity(0.5f).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f)); 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.25f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f)); myCurve.Velocity(0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
myCurve.Invoking(x => x.Velocity(1.5f)).Should().Throw<ArgumentException>();
} }
[Test] [Test]
@ -122,6 +149,8 @@ namespace Tests
myCurve.Velocity(3, 2, 4).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f)); 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(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.Velocity(11, 2, 14).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
myCurve.Invoking(x => x.Velocity(15, 2, 5)).Should().Throw<ArgumentException>();
} }
} }
} }

View File

@ -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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
}
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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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));
}
}
}

View File

@ -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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
}
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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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<ArgumentException>();
}
[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));
}
}
}