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