cubic bezier curve 3d implementation

master
Evan Hemsley 2019-10-25 01:25:39 -07:00
parent 8ea81d09ba
commit a4123f9c03
3 changed files with 186 additions and 118 deletions

View File

@ -1,9 +1,30 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
namespace MoonTools.Core.Curve namespace MoonTools.Core.Curve
{ {
public static class CubicBezier3D public struct CubicBezierCurve3D
{ {
public Vector3 p0;
public Vector3 p1;
public Vector3 p2;
public Vector3 p3;
public CubicBezierCurve3D(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
public Vector3 Point(float t) => Point(p0, p1, p2, p3, t);
public Vector3 Point(float t, float minT, float maxT) => Point(p0, p1, p2, p3, t, minT, maxT);
public Vector3 Velocity(float t) => FirstDerivative(p0, p1, p2, p3, t);
public Vector3 Velocity(float t, float minT, float maxT) => FirstDerivative(p0, p1, p2, p3, t, minT, maxT);
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"); } if (t < 0 || t > 1) { throw new System.ArgumentException($"{t} is an invalid value. Must be between 0 and 1"); }
@ -16,7 +37,7 @@ 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)
{ {
return Point(p0, p1, p2, p3, NormalizedT(t, minT, maxT)); return Point(p0, p1, p2, p3, Normalized(t, minT, maxT));
} }
public static Vector3 FirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) public static Vector3 FirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
@ -30,12 +51,9 @@ namespace MoonTools.Core.Curve
public static Vector3 FirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float maxT) public static Vector3 FirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float maxT)
{ {
return FirstDerivative(p0, p1, p2, p3, NormalizedT(t, minT, maxT)); return FirstDerivative(p0, p1, p2, p3, Normalized(t, minT, maxT));
} }
private static float NormalizedT(float t, float minT, float maxT) private static float Normalized(float t, float minT, float maxT) => ((t - minT)) / (maxT - minT);
{
return ((t - minT)) / (maxT - minT);
}
} }
} }

View File

@ -1,77 +0,0 @@
using NUnit.Framework;
using FluentAssertions;
using MoonTools.Core.Curve;
using Microsoft.Xna.Framework;
namespace Tests.TestExtensions
{
static class TestExtensions
{
public static bool ApproximatelyEquals(this Vector3 v1, Vector3 v2)
{
return (v1 - v2).Length() <= 0.001f;
}
}
}
namespace Tests
{
using TestExtensions;
public class Bezier3DTests
{
[Test]
public void Point()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezier3D.Point(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezier3D.Point(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezier3D.Point(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
CubicBezier3D.Point(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
}
[Test]
public void PointNormalized()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezier3D.Point(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector3(0, 0, 0.75f));
CubicBezier3D.Point(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector3(-2.1875f, -0.5f, -0.84375f));
CubicBezier3D.Point(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector3(2.1875f, 0.5f, 1.21875f));
}
[Test]
public void FirstDerivative()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
}
[Test]
public void FirstDerivativeNormalized()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezier3D.FirstDerivative(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
}
}
}

127
Test/CubicBezierCurve3D.cs Normal file
View File

@ -0,0 +1,127 @@
using NUnit.Framework;
using FluentAssertions;
using MoonTools.Core.Curve;
using Microsoft.Xna.Framework;
namespace Tests
{
public class CubicBezierCurve3DMathTests
{
[Test]
public void Point()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
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));
}
[Test]
public void PointNormalized()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
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));
}
[Test]
public void FirstDerivative()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 0.5f).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 0.25f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 0.75f).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
}
[Test]
public void FirstDerivativeNormalized()
{
var p0 = new Vector3(-4, -4, -3);
var p1 = new Vector3(-2, 4, 0);
var p2 = new Vector3(2, -4, 3);
var p3 = new Vector3(4, 4, 0);
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 3, 2, 4).Should().BeEquivalentTo(new Vector3(9, 0, 4.5f));
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 2, 1, 5).Should().BeEquivalentTo(new Vector3(8.25f, 6f, 7.875f));
CubicBezierCurve3D.FirstDerivative(p0, p1, p2, p3, 11, 2, 14).Should().BeEquivalentTo(new Vector3(8.25f, 6f, -1.125f));
}
}
public class CubicBezierCurve3DStructTests
{
[Test]
public void Point()
{
var myCurve = new CubicBezierCurve3D(
new Vector3(-4, -4, -3),
new Vector3(-2, 4, 0),
new Vector3(2, -4, 3),
new Vector3(4, 4, 0)
);
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));
}
[Test]
public void PointNormalized()
{
var myCurve = new CubicBezierCurve3D(
new Vector3(-4, -4, -3),
new Vector3(-2, 4, 0),
new Vector3(2, -4, 3),
new Vector3(4, 4, 0)
);
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));
}
[Test]
public void Velocity()
{
var myCurve = new CubicBezierCurve3D(
new Vector3(-4, -4, -3),
new Vector3(-2, 4, 0),
new Vector3(2, -4, 3),
new Vector3(4, 4, 0)
);
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));
}
[Test]
public void VelocityNormalized()
{
var myCurve = new CubicBezierCurve3D(
new Vector3(-4, -4, -3),
new Vector3(-2, 4, 0),
new Vector3(2, -4, 3),
new Vector3(4, 4, 0)
);
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));
}
}
}