commit 8ea81d09ba0036548c233a94d66543a98fc053da Author: Evan Hemsley Date: Fri Oct 25 00:56:28 2019 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7faf36f --- /dev/null +++ b/.gitignore @@ -0,0 +1,224 @@ +# The following command works for downloading when using Git for Windows: +# curl -LOf http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore +# +# Download this file using PowerShell v3 under Windows with the following comand: +# Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore +# +# or wget: +# wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore + +.vscode + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +[Bb]in/ +[Oo]bj/ +# build folder is nowadays used for build scripts and should not be ignored +#build/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# OS generated files # +.DS_Store* +Icon? + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings +modulesbin/ +tempbin/ + +# EPiServer Site file (VPP) +AppData/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# vim +*.txt~ +*.swp +*.swo + +# Temp files when opening LibreOffice on ubuntu +.~lock.* + +# svn +.svn + +# CVS - Source Control +**/CVS/ + +# Remainings from resolving conflicts in Source Control +*.orig + +# SQL Server files +**/App_Data/*.mdf +**/App_Data/*.ldf +**/App_Data/*.sdf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store + +# SASS Compiler cache +.sass-cache + +# Visual Studio 2014 CTP +**/*.sln.ide + +# Visual Studio temp something +.vs/ + +# dotnet stuff +project.lock.json + +# VS 2015+ +*.vc.vc.opendb +*.vc.db + +# Rider +.idea/ + +# Output folder used by Webpack or other FE stuff +**/node_modules/* +**/wwwroot/* + +# SpecFlow specific +*.feature.cs +*.feature.xlsx.* +*.Specs_*.html + +##### +# End of core ignore list, below put you custom 'per project' settings (patterns or path) +##### diff --git a/Curve/CubicBezier3D.cs b/Curve/CubicBezier3D.cs new file mode 100644 index 0000000..7bb1ea1 --- /dev/null +++ b/Curve/CubicBezier3D.cs @@ -0,0 +1,41 @@ +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); + } + } +} diff --git a/Curve/Curve.csproj b/Curve/Curve.csproj new file mode 100644 index 0000000..d0d4370 --- /dev/null +++ b/Curve/Curve.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/MoonTools.Core.Curve.sln b/MoonTools.Core.Curve.sln new file mode 100644 index 0000000..687b422 --- /dev/null +++ b/MoonTools.Core.Curve.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Curve", "Curve\Curve.csproj", "{8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{47899FDF-4550-4966-B1A5-547322E773C0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|x64.ActiveCfg = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|x64.Build.0 = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Debug|x86.Build.0 = Debug|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|Any CPU.Build.0 = Release|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|x64.ActiveCfg = Release|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|x64.Build.0 = Release|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|x86.ActiveCfg = Release|Any CPU + {8515F0FE-EB22-4606-8FBE-6B40D24A7DF7}.Release|x86.Build.0 = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|x64.ActiveCfg = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|x64.Build.0 = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|x86.ActiveCfg = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Debug|x86.Build.0 = Debug|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|Any CPU.Build.0 = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|x64.ActiveCfg = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|x64.Build.0 = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|x86.ActiveCfg = Release|Any CPU + {47899FDF-4550-4966-B1A5-547322E773C0}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/TODO b/TODO new file mode 100644 index 0000000..1bd0e48 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- 2D and 3D +- quadratic and cubic +- easing functions for time \ No newline at end of file diff --git a/Test/Bezier3D.cs b/Test/Bezier3D.cs new file mode 100644 index 0000000..47b538d --- /dev/null +++ b/Test/Bezier3D.cs @@ -0,0 +1,77 @@ +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)); + } + } +} \ No newline at end of file diff --git a/Test/Test.csproj b/Test/Test.csproj new file mode 100644 index 0000000..ece517f --- /dev/null +++ b/Test/Test.csproj @@ -0,0 +1,16 @@ + + + netcoreapp3.0 + false + + + + + + + + + + + + \ No newline at end of file