cubic bezier curve 3d implementation
parent
8ea81d09ba
commit
a4123f9c03
|
@ -1,41 +1,59 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MoonTools.Core.Curve
|
||||
{
|
||||
public static class CubicBezier3D
|
||||
{
|
||||
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"); }
|
||||
|
||||
return (1f - t) * (1f - t) * (1f - t) * p0 +
|
||||
3f * (1f - t) * (1f - t) * t * p1 +
|
||||
3f * (1f - t) * t * t * p2 +
|
||||
t * t * t * p3;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public static Vector3 FirstDerivative(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"); }
|
||||
|
||||
return 3f * (1f - t) * (1f - t) * (p1 - p0) +
|
||||
6f * (1f - t) * t * (p2 - p1) +
|
||||
3f * t * t * (p3 - p2);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
private static float NormalizedT(float t, float minT, float maxT)
|
||||
{
|
||||
return ((t - minT)) / (maxT - minT);
|
||||
}
|
||||
}
|
||||
}
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MoonTools.Core.Curve
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (t < 0 || t > 1) { throw new System.ArgumentException($"{t} is an invalid value. Must be between 0 and 1"); }
|
||||
|
||||
return (1f - t) * (1f - t) * (1f - t) * p0 +
|
||||
3f * (1f - t) * (1f - t) * t * p1 +
|
||||
3f * (1f - t) * t * t * p2 +
|
||||
t * t * t * p3;
|
||||
}
|
||||
|
||||
public static Vector3 Point(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float 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)
|
||||
{
|
||||
if (t < 0 || t > 1) { throw new System.ArgumentException($"{t} is an invalid value. Must be between 0 and 1"); }
|
||||
|
||||
return 3f * (1f - t) * (1f - t) * (p1 - p0) +
|
||||
6f * (1f - t) * t * (p2 - p1) +
|
||||
3f * t * t * (p3 - p2);
|
||||
}
|
||||
|
||||
public static Vector3 FirstDerivative(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t, float minT, float maxT)
|
||||
{
|
||||
return FirstDerivative(p0, p1, p2, p3, Normalized(t, minT, maxT));
|
||||
}
|
||||
|
||||
private static float Normalized(float t, float minT, float maxT) => ((t - minT)) / (maxT - minT);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue