Compare commits

...

3 Commits

Author SHA1 Message Date
Christoph Ruegg 425e4dae22 Spatial: remove obsolete functionality 8 years ago
Christoph Ruegg 34b6c232d3 Spatial: import tests 8 years ago
Christoph Ruegg 05407a8e86 Spatial: import Math.NET Spatial: euclidean 2D and 3D 8 years ago
  1. 1
      paket.dependencies
  2. 136
      paket.lock
  3. 3
      src/Numerics.Tests/Numerics.Tests.csproj
  4. 338
      src/Numerics.Tests/Spatial/AngleTests.cs
  5. 164
      src/Numerics.Tests/Spatial/AssertGeometry.cs
  6. 117
      src/Numerics.Tests/Spatial/AssertXml.cs
  7. 111
      src/Numerics.Tests/Spatial/AssertXmlTests.cs
  8. 58
      src/Numerics.Tests/Spatial/Euclidean2D/Circle2DTests.cs
  9. 214
      src/Numerics.Tests/Spatial/Euclidean2D/Line2DTests.cs
  10. 167
      src/Numerics.Tests/Spatial/Euclidean2D/LineSegment2DTests.cs
  11. 283
      src/Numerics.Tests/Spatial/Euclidean2D/Point2DTests.cs
  12. 72
      src/Numerics.Tests/Spatial/Euclidean2D/PolyLine2DTests.cs
  13. 186
      src/Numerics.Tests/Spatial/Euclidean2D/Polygon2DTests.cs
  14. 469
      src/Numerics.Tests/Spatial/Euclidean2D/Vector2DTests.cs
  15. 69
      src/Numerics.Tests/Spatial/Euclidean3D/Circle3DTests.cs
  16. 311
      src/Numerics.Tests/Spatial/Euclidean3D/CoordinateSystem3DTests.cs
  17. 198
      src/Numerics.Tests/Spatial/Euclidean3D/Line3DTests.cs
  18. 143
      src/Numerics.Tests/Spatial/Euclidean3D/LineSegment3DTests.cs
  19. 238
      src/Numerics.Tests/Spatial/Euclidean3D/Plane3DTests.cs
  20. 294
      src/Numerics.Tests/Spatial/Euclidean3D/Point3DTests.cs
  21. 72
      src/Numerics.Tests/Spatial/Euclidean3D/PolyLine3DTests.cs
  22. 59
      src/Numerics.Tests/Spatial/Euclidean3D/Ray3DTests.cs
  23. 220
      src/Numerics.Tests/Spatial/Euclidean3D/UnitVector3DTests.cs
  24. 492
      src/Numerics.Tests/Spatial/Euclidean3D/Vector3DTests.cs
  25. 2
      src/Numerics/Random/RandomSeed.cs
  26. 397
      src/Numerics/Spatial/Angle.cs
  27. 137
      src/Numerics/Spatial/Euclidean2D/Circle2D.cs
  28. 270
      src/Numerics/Spatial/Euclidean2D/Line2D.cs
  29. 207
      src/Numerics/Spatial/Euclidean2D/LineSegment2D.cs
  30. 36
      src/Numerics/Spatial/Euclidean2D/Matrix2D.cs
  31. 366
      src/Numerics/Spatial/Euclidean2D/Point2D.cs
  32. 313
      src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs
  33. 334
      src/Numerics/Spatial/Euclidean2D/Polygon2D.cs
  34. 568
      src/Numerics/Spatial/Euclidean2D/Vector2D.cs
  35. 167
      src/Numerics/Spatial/Euclidean3D/Circle3D.cs
  36. 714
      src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs
  37. 344
      src/Numerics/Spatial/Euclidean3D/Line3D.cs
  38. 298
      src/Numerics/Spatial/Euclidean3D/LineSegment3D.cs
  39. 126
      src/Numerics/Spatial/Euclidean3D/Matrix3D.cs
  40. 498
      src/Numerics/Spatial/Euclidean3D/Plane3D.cs
  41. 494
      src/Numerics/Spatial/Euclidean3D/Point3D.cs
  42. 227
      src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs
  43. 196
      src/Numerics/Spatial/Euclidean3D/Ray3D.cs
  44. 931
      src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs
  45. 740
      src/Numerics/Spatial/Euclidean3D/Vector3D.cs
  46. 109
      src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs
  47. 92
      src/Numerics/Spatial/Internal/AvlTreeSet/AvlNodeItemEnumerator.cs
  48. 943
      src/Numerics/Spatial/Internal/AvlTreeSet/AvlTreeSet.cs
  49. 1522
      src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs
  50. 65
      src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs
  51. 36
      src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs
  52. 188
      src/Numerics/Spatial/Internal/ConvexHull/Quadrant.cs
  53. 240
      src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific1.cs
  54. 241
      src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific2.cs
  55. 243
      src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific3.cs
  56. 243
      src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific4.cs
  57. 380
      src/Numerics/Spatial/Internal/HashCode.cs
  58. 43
      src/Numerics/Spatial/Internal/ImmutableList.cs
  59. 113
      src/Numerics/Spatial/Internal/ImmutableListOfT.cs
  60. 429
      src/Numerics/Spatial/Internal/Text.cs
  61. 261
      src/Numerics/Spatial/Internal/XmlReaderExt.cs
  62. 58
      src/Numerics/Spatial/Internal/XmlWriterExt.cs
  63. 1
      src/Numerics/paket.references

1
paket.dependencies

@ -2,6 +2,7 @@ source https://api.nuget.org/v3/index.json
storage: packages
nuget FSharp.Core 4.3.3
nuget System.Diagnostics.Contracts framework:netstandard1.3
// Testing
nuget NUnit ~> 3.0 framework:net461,netcoreapp1.1,netcoreapp2.0

136
paket.lock

@ -91,12 +91,12 @@ NUGET
System.Xml.XDocument (>= 4.3) - restriction: >= netstandard1.3
System.Xml.XmlDocument (>= 4.3) - restriction: >= netstandard1.3
System.Xml.XPath.XDocument (>= 4.3) - restriction: >= netstandard1.3
Microsoft.CodeAnalysis.CSharp (2.6.1) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netcoreapp1.1))
Microsoft.CodeAnalysis.CSharp (2.6.1) - restriction: || (== net461) (== netcoreapp1.1) (&& (== netcoreapp2.0) (>= net46)) (&& (== netcoreapp2.0) (< netcoreapp1.1))
Microsoft.CodeAnalysis.Common (2.6.1)
Microsoft.CodeAnalysis.VisualBasic (1.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netcoreapp1.1))
Microsoft.CodeAnalysis.Common (>= 1.3)
Microsoft.CodeCoverage (1.0.3) - restriction: || (>= net45) (>= netcoreapp1.0)
Microsoft.CSharp (4.3) - restriction: == netcoreapp1.1
Microsoft.CSharp (4.3) - restriction: || (== netcoreapp1.1) (&& (< netstandard1.3) (>= uap10.0)) (&& (< netstandard2.0) (>= uap10.0))
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Dynamic.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -114,7 +114,7 @@ NUGET
System.Runtime.InteropServices (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.DiaSymReader.Native (1.4.1) - restriction: == netcoreapp1.1
Microsoft.DotNet.InternalAbstractions (1.0) - restriction: >= netcoreapp1.0
Microsoft.DotNet.InternalAbstractions (1.0) - restriction: || (== net461) (>= netcoreapp1.0)
System.AppContext (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
System.Collections (>= 4.0.11) - restriction: && (< net451) (>= netstandard1.3)
System.IO (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
@ -123,7 +123,7 @@ NUGET
System.Runtime.Extensions (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
System.Runtime.InteropServices (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
System.Runtime.InteropServices.RuntimeInformation (>= 4.0) - restriction: && (< net451) (>= netstandard1.3)
Microsoft.DotNet.PlatformAbstractions (1.1.1) - content: none, restriction: || (== net461) (== netcoreapp2.0)
Microsoft.DotNet.PlatformAbstractions (1.1.1) - content: none, restriction: || (== net461) (>= netcoreapp1.0)
System.AppContext (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
System.Collections (>= 4.0.11) - restriction: && (< net451) (>= netstandard1.3)
System.IO (>= 4.1) - restriction: && (< net451) (>= netstandard1.3)
@ -210,7 +210,7 @@ NUGET
Microsoft.NETCore.Platforms (2.0.2) - restriction: || (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Runtime.CoreCLR (2.0.7) - restriction: || (== netcoreapp1.1) (== netcoreapp2.0)
Microsoft.NETCore.Jit (>= 2.0.7)
Microsoft.NETCore.Targets (1.1.2) - restriction: == netcoreapp1.1
Microsoft.NETCore.Targets (1.1.2) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
Microsoft.TestPlatform.ObjectModel (15.7) - restriction: >= netcoreapp1.0
NETStandard.Library (>= 1.6) - restriction: && (< net451) (>= netstandard1.5)
System.ComponentModel.EventBasedAsync (>= 4.0.11) - restriction: && (< net451) (>= netstandard1.5)
@ -251,7 +251,7 @@ NUGET
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.NETCore.Targets (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.Win32.Registry (4.3) - content: none, restriction: || (== net461) (== netcoreapp2.0)
Microsoft.Win32.Registry (4.3) - content: none, restriction: || (== net461) (== netcoreapp1.1) (== netcoreapp2.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< net46) (>= netstandard1.3)
System.Collections (>= 4.3) - restriction: && (< net46) (>= netstandard1.3)
System.Globalization (>= 4.3) - restriction: && (< net46) (>= netstandard1.3)
@ -305,7 +305,7 @@ NUGET
System.Threading.Timer (>= 4.3) - restriction: || (&& (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.3) (< netstandard1.4) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.4) (< netstandard1.5) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.5) (< netstandard1.6) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.6) (< netstandard2.0) (< win8) (< wpa81)) (&& (>= uap10.0) (< uap10.1))
System.Xml.ReaderWriter (>= 4.3) - restriction: || (&& (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.3) (< netstandard1.4) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.4) (< netstandard1.5) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.5) (< netstandard1.6) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.6) (< netstandard2.0) (< win8) (< wpa81)) (&& (>= net46) (< netstandard1.4)) (&& (>= netstandard1.0) (< portable-net45+win8+wpa81) (< wp8)) (&& (>= uap10.0) (< uap10.1))
System.Xml.XDocument (>= 4.3) - restriction: || (&& (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.3) (< netstandard1.4) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.4) (< netstandard1.5) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.5) (< netstandard1.6) (< win8) (< wpa81)) (&& (< net45) (>= netstandard1.6) (< netstandard2.0) (< win8) (< wpa81)) (&& (>= netstandard1.0) (< portable-net45+win8+wpa81) (< wp8)) (&& (>= uap10.0) (< uap10.1))
Newtonsoft.Json (11.0.2) - restriction: >= uap10.0
Newtonsoft.Json (11.0.2) - restriction: || (>= netcoreapp1.0) (>= uap10.0)
Microsoft.CSharp (>= 4.3) - restriction: || (&& (< net20) (>= netstandard1.0) (< netstandard1.3)) (&& (< net20) (>= netstandard1.3) (< netstandard2.0))
NETStandard.Library (>= 1.6.1) - restriction: || (&& (< net20) (>= netstandard1.0) (< netstandard1.3)) (&& (< net20) (>= netstandard1.3) (< netstandard2.0))
System.ComponentModel.TypeConverter (>= 4.3) - restriction: || (&& (< net20) (>= netstandard1.0) (< netstandard1.3)) (&& (< net20) (>= netstandard1.3) (< netstandard2.0))
@ -335,10 +335,10 @@ NUGET
runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: == netcoreapp1.1
runtime.fedora.24-x64.runtime.native.System.Security.Cryptography (4.3.3) - restriction: == netcoreapp1.1
runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: == netcoreapp1.1
runtime.native.System (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
runtime.native.System (4.3) - restriction: || (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1)
Microsoft.NETCore.Targets (>= 1.1)
runtime.native.System.IO.Compression (4.3.1) - restriction: == netcoreapp1.1
runtime.native.System.IO.Compression (4.3.1) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1)
Microsoft.NETCore.Targets (>= 1.1.1)
runtime.native.System.Net.Http (4.3) - restriction: == netcoreapp1.1
@ -358,9 +358,9 @@ NUGET
runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography (>= 4.3.3)
runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography (>= 4.3.3)
runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography (>= 4.3.3)
runtime.native.System.Security.Cryptography.Apple (4.3.1) - restriction: == netcoreapp1.1
runtime.native.System.Security.Cryptography.Apple (4.3.1) - restriction: || (== netcoreapp1.1) (&& (== netstandard1.3) (>= netstandard1.6)) (== netstandard1.6)
runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple (>= 4.3.1)
runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: == netcoreapp1.1
runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: || (== netcoreapp1.1) (&& (== netstandard1.3) (>= netstandard1.6)) (== netstandard1.6)
runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2)
runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2)
runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3.2)
@ -386,15 +386,15 @@ NUGET
runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: == netcoreapp1.1
runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography (4.3.3) - restriction: == netcoreapp1.1
runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.2) - restriction: == netcoreapp1.1
System.AppContext (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.AppContext (4.3) - restriction: || (&& (== net461) (< net451)) (&& (== netcoreapp2.0) (< netcoreapp1.1)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.6)) (&& (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Buffers (4.3) - restriction: == netcoreapp1.1
System.Buffers (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Diagnostics.Debug (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Tracing (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Resources.ResourceManager (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Collections (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Collections (4.3) - restriction: || (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -433,7 +433,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.Extensions (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.ComponentModel (4.3) - restriction: == netcoreapp1.1
System.ComponentModel (4.3) - restriction: >= netcoreapp1.0
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.ComponentModel.Annotations (4.3) - restriction: == netcoreapp1.1
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.4) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -452,8 +452,8 @@ NUGET
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading.Tasks (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.ComponentModel.Primitives (4.3) - restriction: >= uap10.0
System.ComponentModel.TypeConverter (4.3) - restriction: >= netcoreapp1.0
System.ComponentModel.Primitives (4.3) - restriction: || (>= netcoreapp1.0) (>= uap10.0)
System.ComponentModel.TypeConverter (4.3) - restriction: || (>= netcoreapp1.0) (>= uap10.0)
System.Collections (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.5) (< win8) (< wp8) (< wpa81))
System.Collections.NonGeneric (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net462)
System.Collections.Specialized (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -469,13 +469,15 @@ NUGET
System.Runtime (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.5) (< win8) (< wp8) (< wpa81))
System.Runtime.Extensions (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.5) (< win8) (< wp8) (< wpa81))
System.Threading (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.5) (< win8) (< wp8) (< wpa81))
System.Console (4.3.1) - restriction: == netcoreapp1.1
System.Console (4.3.1) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.NETCore.Targets (>= 1.1.2) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Debug (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Diagnostics.Contracts (4.3) - restriction: == netstandard1.3
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (4.3) - restriction: || (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -485,7 +487,7 @@ NUGET
System.Reflection (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Threading (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Diagnostics.FileVersionInfo (4.3) - content: none, restriction: || (== net461) (== netcoreapp2.0)
System.Diagnostics.FileVersionInfo (4.3) - content: none, restriction: || (== net461) (== netcoreapp1.1) (== netcoreapp2.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -495,7 +497,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime.InteropServices (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Process (4.3) - restriction: == netcoreapp1.1
System.Diagnostics.Process (4.3) - restriction: >= netcoreapp1.0
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.4) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.Win32.Primitives (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.4) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.Win32.Registry (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.4) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -529,7 +531,7 @@ NUGET
System.Resources.ResourceManager (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Tools (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Diagnostics.Tools (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netcoreapp1.1)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -547,7 +549,7 @@ NUGET
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Dynamic.Runtime (4.3) - restriction: == netcoreapp1.1
System.Dynamic.Runtime (4.3) - restriction: >= netcoreapp1.0
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Linq (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -562,7 +564,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Globalization (4.3) - restriction: || (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -571,14 +573,14 @@ NUGET
Microsoft.NETCore.Targets (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization.Extensions (4.3) - restriction: == netcoreapp1.1
System.Globalization.Extensions (4.3) - restriction: >= netcoreapp1.0
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Resources.ResourceManager (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.Extensions (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.InteropServices (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.IO (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
@ -610,7 +612,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.Extensions (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO.FileSystem (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.IO.FileSystem (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.NETCore.Targets (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -619,7 +621,7 @@ NUGET
System.Runtime.Handles (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Tasks (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO.FileSystem.Primitives (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.IO.FileSystem.Primitives (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net46)) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO.FileSystem.Watcher (4.3) - restriction: == netcoreapp1.1
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -662,13 +664,13 @@ NUGET
System.Runtime.InteropServices (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Tasks (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Linq (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Linq (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.6) (< win8) (< wp8) (< wpa81)) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Resources.ResourceManager (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.6) (< win8) (< wp8) (< wpa81)) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Linq.Expressions (4.3) - restriction: == netcoreapp1.1
System.Linq.Expressions (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -697,7 +699,7 @@ NUGET
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading.Tasks (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Linq.Queryable (4.3) - restriction: == netcoreapp1.1
System.Linq.Queryable (4.3) - restriction: || (== netcoreapp1.1) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Linq (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -706,7 +708,7 @@ NUGET
System.Reflection.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Resources.ResourceManager (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Net.Http (4.3.3) - restriction: == netcoreapp1.1
System.Net.Http (4.3.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.6) (< win8) (< wpa81))
runtime.native.System (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
runtime.native.System.Net.Http (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -753,7 +755,7 @@ NUGET
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (>= netstandard1.0) (< netstandard1.1) (< win8) (< wp8))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (>= netstandard1.0) (< netstandard1.1) (< win8) (< wp8))
System.Runtime.Handles (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Net.Requests (4.3) - restriction: == netcoreapp1.1
System.Net.Requests (4.3) - restriction: || (== netcoreapp1.1) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -819,7 +821,7 @@ NUGET
System.Resources.ResourceManager (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Private.DataContractSerialization (4.3) - restriction: || (== netcoreapp1.1) (== netstandard1.3) (== netstandard1.6)
System.Private.DataContractSerialization (4.3) - restriction: || (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Collections.Concurrent (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -845,7 +847,7 @@ NUGET
System.Xml.XDocument (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Xml.XmlDocument (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net46)
System.Xml.XmlSerializer (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Reflection (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Reflection (4.3) - restriction: || (&& (== net461) (>= netcoreapp1.1)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.IO (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
@ -862,27 +864,27 @@ NUGET
System.Resources.ResourceManager (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Emit (4.3) - restriction: == netcoreapp1.1
System.Reflection.Emit (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (== netcoreapp1.1) (== netcoreapp2.0)
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Emit.ILGeneration (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Primitives (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Emit.ILGeneration (4.3) - restriction: == netcoreapp1.1
System.Reflection.Emit.ILGeneration (4.3) - restriction: || (&& (== net461) (>= netcoreapp1.1)) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
System.Reflection (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Primitives (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Emit.Lightweight (4.3) - content: none, restriction: || (&& (== net461) (>= netcoreapp1.1)) (== netcoreapp2.0)
System.Reflection.Emit.Lightweight (4.3) - content: none, restriction: || (&& (== net461) (>= netcoreapp1.1)) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
System.Reflection (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Emit.ILGeneration (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Primitives (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< wp8) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Extensions (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Reflection.Extensions (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Reflection (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Reflection.Metadata (1.4.2) - restriction: == netcoreapp1.1
System.Reflection.Metadata (1.4.2) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net46)) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Collections.Immutable (>= 1.3.1) - restriction: || (>= monoandroid) (>= monotouch) (>= net45) (&& (>= netstandard1.1) (< win8)) (&& (< netstandard1.1) (>= win8)) (>= wpa81) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos)
System.Diagnostics.Debug (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -899,11 +901,11 @@ NUGET
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Text.Encoding.Extensions (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection.Primitives (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Reflection.Primitives (4.3) - restriction: || (&& (== net461) (>= netcoreapp1.1)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Reflection.TypeExtensions (4.3) - restriction: == netcoreapp1.1
System.Reflection.TypeExtensions (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
System.Reflection (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.5) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.5)) (>= net462)
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.5) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.5))
System.Resources.Reader (4.3) - restriction: == netcoreapp1.1
@ -912,7 +914,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Resources.ResourceManager (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Resources.ResourceManager (4.3) - restriction: || (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -921,22 +923,22 @@ NUGET
System.Runtime (4.3) - restriction: || (== netcoreapp1.1) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.2) (< win8) (< wp8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.2) (< win8) (< wp8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Runtime.Extensions (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Runtime.Extensions (4.3) - restriction: || (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81))
System.Runtime.Handles (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Runtime.Handles (4.3) - restriction: || (&& (== net461) (< net46)) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
Microsoft.NETCore.Targets (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.InteropServices (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Runtime.InteropServices (4.3) - restriction: || (&& (== net461) (< net46)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= netcoreapp1.1)
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= netcoreapp1.1)
System.Reflection (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= netcoreapp1.1)
System.Reflection.Primitives (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= netcoreapp1.1)
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.2) (< win8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= net462) (>= netcoreapp1.1)
System.Runtime.Handles (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (>= netcoreapp1.1)
System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.0)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Runtime.InteropServices.RuntimeInformation (4.3) - restriction: || (&& (== net461) (< net451)) (&& (== netcoreapp2.0) (< netstandard1.0)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0) (>= uap10.0)
runtime.native.System (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Reflection.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -944,7 +946,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime.InteropServices (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime.Loader (4.3) - restriction: == netcoreapp1.1
System.Runtime.Loader (4.3) - restriction: >= netcoreapp1.0
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net462) (>= netstandard1.5) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Reflection (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net462) (>= netstandard1.5) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net462) (>= netstandard1.5) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1022,7 +1024,7 @@ NUGET
System.Security.Cryptography.Primitives (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net46)
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Security.Cryptography.Encoding (4.3) - restriction: == netcoreapp1.1
System.Security.Cryptography.Encoding (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
runtime.native.System.Security.Cryptography.OpenSsl (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1049,7 +1051,7 @@ NUGET
System.Security.Cryptography.Encoding (>= 4.3) - restriction: || (&& (< monotouch) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net463)
System.Security.Cryptography.Primitives (>= 4.3) - restriction: || (&& (< monotouch) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net463)
System.Text.Encoding (>= 4.3) - restriction: && (< monotouch) (< net463) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Security.Cryptography.Primitives (4.3) - restriction: == netcoreapp1.1
System.Security.Cryptography.Primitives (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Diagnostics.Debug (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1057,7 +1059,7 @@ NUGET
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Tasks (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Security.Cryptography.X509Certificates (4.3.2) - restriction: == netcoreapp1.1
System.Security.Cryptography.X509Certificates (4.3.2) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
runtime.native.System (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
runtime.native.System.Net.Http (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1100,7 +1102,7 @@ NUGET
System.Security.Principal (>= 4.3) - restriction: && (< net46) (>= netstandard1.3)
System.Text.Encoding (>= 4.3) - restriction: && (< net46) (>= netstandard1.3)
System.Threading (>= 4.3) - restriction: && (< net46) (>= netstandard1.3)
System.Text.Encoding (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Text.Encoding (4.3) - restriction: || (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -1122,14 +1124,14 @@ NUGET
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Text.Encoding (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Text.RegularExpressions (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Text.RegularExpressions (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (< netcoreapp1.1) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (< netcoreapp1.1) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Resources.ResourceManager (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (< netcoreapp1.1) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.6) (< win8) (< wpa81)) (&& (< monotouch) (< net45) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= netcoreapp1.1)
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (< netcoreapp1.1) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monotouch) (< net45) (< netcoreapp1.1) (>= netstandard1.6) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Threading (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (&& (== netcoreapp2.0) (>= dnxcore50)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Threading.Tasks (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Threading.Overlapped (4.3) - restriction: == netcoreapp1.1
@ -1137,7 +1139,7 @@ NUGET
System.Resources.ResourceManager (>= 4.3) - restriction: || (>= dnxcore50) (&& (< net46) (>= netstandard1.3))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< net46) (>= netstandard1.3))
System.Runtime.Handles (>= 4.3) - restriction: || (>= dnxcore50) (&& (< net46) (>= netstandard1.3))
System.Threading.Tasks (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Threading.Tasks (4.3) - restriction: || (== net461) (&& (== netcoreapp2.0) (>= net46)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -1153,11 +1155,11 @@ NUGET
System.Runtime.Extensions (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (>= netstandard1.0) (< netstandard1.1) (< win8) (< wp8))
System.Threading (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (>= netstandard1.0) (< netstandard1.1) (< win8) (< wp8))
System.Threading.Tasks (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.1) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (>= netstandard1.0) (< netstandard1.1) (< win8) (< wp8))
System.Threading.Tasks.Extensions (4.3) - restriction: == netcoreapp1.1
System.Threading.Tasks.Extensions (4.3) - restriction: || (== net461) (== netcoreapp1.1) (&& (== netcoreapp2.0) (>= net46))
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Tasks (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Tasks.Parallel (4.3) - restriction: == netcoreapp1.1
System.Threading.Tasks.Parallel (4.3) - restriction: || (== netcoreapp1.1) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
System.Collections.Concurrent (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Tracing (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -1166,20 +1168,20 @@ NUGET
System.Runtime.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading.Tasks (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard1.3) (< win8) (< wpa81))
System.Threading.Thread (4.3) - restriction: == netcoreapp1.1
System.Threading.Thread (4.3) - restriction: || (&& (< net45) (>= netstandard1.6) (< netstandard2.0)) (>= netcoreapp1.0)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.ThreadPool (4.3) - restriction: == netcoreapp1.1
System.Threading.ThreadPool (4.3) - restriction: || (== netcoreapp1.1) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime.Handles (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading.Timer (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Threading.Timer (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (&& (< net45) (>= netstandard1.6) (< netstandard2.0))
Microsoft.NETCore.Platforms (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net451) (>= netstandard1.2) (< win81) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
Microsoft.NETCore.Targets (>= 1.1) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net451) (>= netstandard1.2) (< win81) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Runtime (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net451) (>= netstandard1.2) (< win81) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.ValueTuple (4.3) - content: none, restriction: || (== net461) (== netcoreapp2.0)
System.ValueTuple (4.3) - content: none, restriction: || (== net461) (== netcoreapp1.1) (== netcoreapp2.0)
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Resources.ResourceManager (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Xml.ReaderWriter (4.3.1) - restriction: == netcoreapp1.1
System.Xml.ReaderWriter (4.3.1) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -1195,7 +1197,7 @@ NUGET
System.Text.RegularExpressions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading.Tasks (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Threading.Tasks.Extensions (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Xml.XDocument (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Xml.XDocument (4.3) - restriction: || (== netcoreapp1.1) (&& (== netcoreapp2.0) (< netcoreapp1.1)) (&& (== netcoreapp2.0) (< netstandard1.2)) (&& (== netcoreapp2.0) (< netstandard1.3)) (&& (== netcoreapp2.0) (< netstandard1.4)) (&& (== netcoreapp2.0) (< netstandard1.5)) (&& (== netcoreapp2.0) (< netstandard1.6)) (&& (== netcoreapp2.0) (< netstandard2.0)) (&& (== netcoreapp2.0) (< portable-net45+win8+wpa81)) (&& (== netcoreapp2.0) (>= uap10.0)) (== netstandard1.3) (== netstandard1.6)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Debug (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Diagnostics.Tools (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
@ -1208,7 +1210,7 @@ NUGET
System.Text.Encoding (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Xml.ReaderWriter (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Xml.XmlDocument (4.3) - restriction: >= netcoreapp1.0
System.Xml.XmlDocument (4.3) - restriction: || (&& (== net461) (>= dnxcore50)) (&& (== net461) (< net45)) (&& (== netcoreapp2.0) (< netcoreapp1.1)) (== netstandard1.3) (== netstandard1.6) (>= netcoreapp1.0) (&& (< netstandard2.0) (>= uap10.0))
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Debug (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1219,7 +1221,7 @@ NUGET
System.Text.Encoding (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Threading (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Xml.ReaderWriter (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Xml.XmlSerializer (4.3) - content: none, restriction: || (== net461) (== netcoreapp2.0)
System.Xml.XmlSerializer (4.3) - content: none, restriction: || (== net461) (== netcoreapp1.1) (== netcoreapp2.0) (== netstandard1.3) (== netstandard1.6)
System.Collections (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Globalization (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.IO (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
@ -1237,7 +1239,7 @@ NUGET
System.Threading (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Xml.ReaderWriter (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.3) (< win8) (< wp8) (< wpa81))
System.Xml.XmlDocument (>= 4.3) - restriction: || (>= dnxcore50) (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.3) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos))
System.Xml.XPath (4.3) - restriction: >= netcoreapp1.0
System.Xml.XPath (4.3) - restriction: || (&& (== netcoreapp2.0) (>= net46)) (&& (== netcoreapp2.0) (< netcoreapp1.1)) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Diagnostics.Debug (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
@ -1257,7 +1259,7 @@ NUGET
System.Xml.ReaderWriter (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Xml.XDocument (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Xml.XPath (>= 4.3) - restriction: || (&& (< monoandroid) (< monotouch) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net46)
System.Xml.XPath.XmlDocument (4.3) - restriction: >= netcoreapp1.0
System.Xml.XPath.XmlDocument (4.3) - restriction: || (&& (== net461) (>= netcoreapp1.1)) (>= netcoreapp1.0)
System.Collections (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.Globalization (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)
System.IO (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net46) (>= netstandard1.3) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)

3
src/Numerics.Tests/Numerics.Tests.csproj

@ -27,5 +27,8 @@
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="Spatial\Euclidean2D\" />
</ItemGroup>
<Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>

338
src/Numerics.Tests/Spatial/AngleTests.cs

@ -0,0 +1,338 @@
using System;
using System.Globalization;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial
{
[TestFixture]
public class AngleTests
{
private const double Tolerance = 1e-6;
private const double DegToRad = Math.PI / 180;
[Test]
public void OperatorCompare()
{
var one = Angle.FromRadians(1);
var two = Angle.FromRadians(2);
Assert.AreEqual(true, one < two);
Assert.AreEqual(true, one <= two);
Assert.AreEqual(true, one <= Angle.FromRadians(1));
Assert.AreEqual(false, one < Angle.FromRadians(1));
Assert.AreEqual(false, one > Angle.FromRadians(1));
Assert.AreEqual(true, one >= Angle.FromRadians(1));
}
[TestCase("1.5707 rad", "1.5707 rad", 1.5707 + 1.5707)]
[TestCase("1.5707 rad", "2 °", 1.5707 + (2 * DegToRad))]
public void OperatorAdd(string lvs, string rvs, double ev)
{
var lv = Angle.Parse(lvs);
var rv = Angle.Parse(rvs);
var sum = lv + rv;
Assert.AreEqual(ev, sum.Radians, Tolerance);
Assert.IsInstanceOf<Angle>(sum);
}
[TestCase("1.5707 rad", "1.5706 rad", 1.5707 - 1.5706)]
[TestCase("1.5707 rad", "2 °", 1.5707 - (2 * DegToRad))]
public void OperatorSubtract(string lvs, string rvs, double ev)
{
var lv = Angle.Parse(lvs);
var rv = Angle.Parse(rvs);
var diff = lv - rv;
Assert.AreEqual(ev, diff.Radians, Tolerance);
Assert.IsInstanceOf<Angle>(diff);
}
[TestCase("15 °", 5, 15 * 5 * DegToRad)]
[TestCase("-10 °", 0, 0)]
[TestCase("-10 °", 2, -10 * 2 * DegToRad)]
[TestCase("1 rad", 2, 2)]
public void OperatorMultiply(string lvs, double rv, double ev)
{
var lv = Angle.Parse(lvs);
var prods = new[] { lv * rv, rv * lv };
foreach (var prod in prods)
{
Assert.AreEqual(ev, prod.Radians, 1e-3);
Assert.IsInstanceOf<Angle>(prod);
}
}
[Test]
public void OperatorNegate()
{
Assert.AreEqual(-1, (-Angle.FromRadians(1)).Radians);
}
[TestCase("3.141596 rad", 2, 1.570797999)]
public void DivisionTest(string s, double rv, double expected)
{
var angle = Angle.Parse(s);
var actual = angle / rv;
Assert.AreEqual(expected, actual.Radians, Tolerance);
Assert.IsInstanceOf<Angle>(actual);
}
[TestCase("90 °", 90, Math.PI / 2, true)]
[TestCase("1 rad", 1 * 180 / Math.PI, 1, true)]
[TestCase("1.1 rad", 1 * 180 / Math.PI, Math.PI / 2, false)]
public void Equals(string s, double degrees, double radians, bool expected)
{
var a = Angle.Parse(s);
var deg = Angle.FromDegrees(degrees);
Assert.AreEqual(expected, deg.Equals(a));
Assert.AreEqual(expected, deg.Equals(a, Tolerance));
Assert.AreEqual(expected, deg == a);
Assert.AreEqual(!expected, deg != a);
var rad = Angle.FromRadians(radians);
Assert.AreEqual(expected, rad.Equals(a));
Assert.AreEqual(expected, rad.Equals(a, Tolerance));
Assert.AreEqual(expected, rad == a);
Assert.AreEqual(!expected, rad != a);
}
[Test]
public void EqualsWithTolerance()
{
var one = Angle.FromRadians(1);
var two = Angle.FromRadians(2);
Assert.AreEqual(true, one.Equals(two, 2));
Assert.AreEqual(false, one.Equals(two, 0.1));
Assert.AreEqual(true, one.Equals(two, Angle.FromRadians(2)));
Assert.AreEqual(false, one.Equals(two, Angle.FromRadians(0.1)));
}
[TestCase(90, 1.5707963267948966)]
public void FromDegrees(double degrees, double expected)
{
Assert.AreEqual(expected, Angle.FromDegrees(degrees).Radians);
Assert.AreEqual(degrees, Angle.FromDegrees(degrees).Degrees, 1E-6);
}
[TestCase(1, 1)]
public void FromRadians(double radians, double expected)
{
Assert.AreEqual(expected, Angle.FromRadians(radians).Radians);
}
[TestCase(20, 33, 49, 0.35890271998857842)]
public void FromSexagesimal(int degrees, int minutes, double seconds, double expected)
{
Assert.AreEqual(expected, Angle.FromSexagesimal(degrees, minutes, seconds).Radians, 1E-6);
}
[TestCase("5 °", 5 * DegToRad)]
[TestCase("5°", 5 * DegToRad)]
[TestCase("-5.34 rad", -5.34)]
[TestCase("-5,34 rad", -5.34)]
[TestCase("1e-4 rad", 0.0001)]
[TestCase("1e-4 °", 0.0001 * DegToRad)]
public void Parse(string s, double expected)
{
Assert.AreEqual(true, Angle.TryParse(s, out var angle));
Assert.AreEqual(expected, angle.Radians, Tolerance);
angle = Angle.Parse(s);
Assert.AreEqual(expected, angle.Radians, Tolerance);
Assert.IsInstanceOf<Angle>(angle);
// ReSharper disable once RedundantToStringCall
angle = Angle.Parse(s.ToString());
Assert.AreEqual(expected, angle.Radians, Tolerance);
Assert.IsInstanceOf<Angle>(angle);
}
[Test]
public void FailParse()
{
bool result = Angle.TryParse("test", out var angle);
Assert.AreEqual(default(Angle), angle);
Assert.IsFalse(result);
}
[Test]
public void FailParseDirect()
{
Assert.Throws<FormatException>(() => Angle.Parse("Test"), "Expected FormatException", null);
}
[TestCase(".1 rad", 0.1)]
[TestCase("1.2 rad", 1.2)]
[TestCase("1.2\u00A0rad", 1.2)]
[TestCase("1.2radians", 1.2)]
[TestCase("1.2 radians", 1.2)]
[TestCase("1.2\u00A0radians", 1.2)]
[TestCase("1.2\u00A0Radians", 1.2)]
public void ParseRadians(string text, double expected)
{
Assert.AreEqual(true, Angle.TryParse(text, out var angle));
Assert.AreEqual(expected, angle.Radians);
Assert.AreEqual(expected, Angle.Parse(text).Radians);
}
[TestCase("1°", 1)]
[TestCase("1 °", 1)]
[TestCase("1deg", 1)]
[TestCase("1 deg", 1)]
[TestCase("1\u00A0deg", 1)]
[TestCase("1\u00A0DEG", 1)]
[TestCase("1degrees", 1)]
[TestCase("1 degrees", 1)]
[TestCase("1\u00A0degrees", 1)]
[TestCase("1\u00A0Degrees", 1)]
public void ParseDegrees(string text, double expected)
{
Assert.AreEqual(true, Angle.TryParse(text, out var angle));
Assert.AreEqual(expected, angle.Degrees);
Assert.AreEqual(expected, Angle.Parse(text).Degrees);
}
[TestCase(@"<Angle Value=""1"" />")]
[TestCase(@"<Angle><Value>1</Value></Angle>")]
public void ReadFrom(string xml)
{
var v = Angle.FromRadians(1);
Assert.AreEqual(v, Angle.ReadFrom(XmlReader.Create(new StringReader(xml))));
}
[Test]
public void Compare()
{
var small = Angle.FromDegrees(1);
var big = Angle.FromDegrees(2);
Assert.IsTrue(small < big);
Assert.IsTrue(small <= big);
Assert.IsFalse(small > big);
Assert.IsFalse(small >= big);
Assert.AreEqual(-1, small.CompareTo(big));
Assert.AreEqual(0, small.CompareTo(small));
Assert.AreEqual(1, big.CompareTo(small));
}
[TestCase("15 °", "0.261799387799149\u00A0rad")]
public void ToString(string s, string expected)
{
var angle = Angle.Parse(s);
var toString = angle.ToString(CultureInfo.InvariantCulture);
Assert.AreEqual(expected, toString);
Assert.IsTrue(angle.Equals(Angle.Parse(toString), Tolerance));
Assert.IsTrue(angle.Equals(Angle.Parse(toString), Angle.FromRadians(Tolerance)));
}
//[TestCase("15°", "F2", "15.00°")]
//public void ToString(string s, string format, string expected)
//{
// var angle = Angle.Parse(s);
// var toString = angle.ToString(format, CultureInfo.InvariantCulture, AngleUnit.Degrees);
// Assert.AreEqual(expected, toString);
// Assert.AreEqual(angle.Radians, Angle.Parse(angle.ToString(format)).Radians, 1E-2);
// Assert.IsTrue(angle.Equals(Angle.Parse(toString), Tolerance));
//}
[TestCase("15°", @"<Angle Value=""0.26179938779914941"" />")]
public void XmlRoundTrips(string vs, string xml)
{
var angle = Angle.Parse(vs);
AssertXml.XmlRoundTrips(angle, xml, (e, a) =>
{
Assert.AreEqual(e.Radians, a.Radians, Tolerance);
});
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<Angle>
{
Value1 = Angle.FromRadians(1),
Value2 = Angle.FromRadians(2),
};
var expected = "<ContainerOfAngle>\r\n" +
" <Value1 Value=\"1\"></Value1>\r\n" +
" <Value2 Value=\"2\"></Value2>\r\n" +
"</ContainerOfAngle>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
Assert.AreEqual(container.Value1, roundTrip.Value1);
Assert.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void ReadXmlContainerElementValues()
{
var container = new AssertXml.Container<Angle>
{
Value1 = Angle.FromRadians(1),
Value2 = Angle.FromRadians(2),
};
var xml = "<ContainerOfAngle>\r\n" +
" <Value1>1</Value1>\r\n" +
" <Value2>2</Value2>\r\n" +
"</ContainerOfAngle>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<Angle>));
using (var reader = new StringReader(xml))
{
var deserialized = (AssertXml.Container<Angle>)serializer.Deserialize(reader);
Assert.AreEqual(container.Value1, deserialized.Value1);
Assert.AreEqual(container.Value2, deserialized.Value2);
}
}
[TestCase("15°", @"<Angle><Value>0.261799387799149</Value></Angle>")]
[TestCase("15°", @"<Angle><Radians>0.261799387799149</Radians></Angle>")]
[TestCase("15°", @"<Angle><Degrees>15</Degrees></Angle>")]
[TestCase("15°", @"<Angle Radians=""0.26179938779914941"" />")]
[TestCase("180°", @"<Angle Degrees=""180"" />")]
public void XmlElement(string vs, string xml)
{
var angle = Angle.Parse(vs);
var serializer = new XmlSerializer(typeof(Angle));
using (var reader = new StringReader(xml))
{
var fromElements = (Angle)serializer.Deserialize(reader);
Assert.AreEqual(angle.Radians, fromElements.Radians, 1e-6);
}
}
[Test]
public void ToStringTest()
{
int number = 1;
var angle = Angle.FromRadians(number);
string expected = number + " rad";
Assert.AreEqual(expected, angle.ToString());
}
[Test]
public void ObjectEqualsTest()
{
var angle = Angle.FromRadians(1);
Assert.IsTrue(angle.Equals((object)angle));
}
[Test]
public void ObjectNullTest()
{
var angle = Angle.FromRadians(1);
Assert.IsFalse(angle.Equals(null));
}
[Test]
public void HashCodeTest()
{
string test = "test";
var angle = Angle.FromRadians(1);
var lookup = new System.Collections.Generic.Dictionary<Angle, string>
{
{ angle, test }
};
Assert.AreEqual(test, lookup[angle]);
}
}
}

164
src/Numerics.Tests/Spatial/AssertGeometry.cs

@ -0,0 +1,164 @@
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Euclidean2D;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial
{
public static class AssertGeometry
{
public static void AreEqual(CoordinateSystem3D coordinateSystem, Point3D origin, Vector3D xAxis, Vector3D yAxis, Vector3D zAxis, double tolerance = 1e-6)
{
AreEqual(xAxis, coordinateSystem.XAxis, tolerance);
AreEqual(yAxis, coordinateSystem.YAxis, tolerance);
AreEqual(zAxis, coordinateSystem.ZAxis, tolerance);
AreEqual(origin, coordinateSystem.Origin, tolerance);
AreEqual(new double[] { xAxis.X, xAxis.Y, xAxis.Z, 0 }, coordinateSystem.Column(0).ToArray(), tolerance);
AreEqual(new double[] { yAxis.X, yAxis.Y, yAxis.Z, 0 }, coordinateSystem.Column(1).ToArray(), tolerance);
AreEqual(new double[] { zAxis.X, zAxis.Y, zAxis.Z, 0 }, coordinateSystem.Column(2).ToArray(), tolerance);
AreEqual(new double[] { origin.X, origin.Y, origin.Z, 1 }, coordinateSystem.Column(3).ToArray(), tolerance);
}
public static void AreEqual(UnitVector3D expected, UnitVector3D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
Assert.AreEqual(expected.X, actual.X, tolerance, message);
Assert.AreEqual(expected.Y, actual.Y, tolerance, message);
Assert.AreEqual(expected.Z, actual.Z, tolerance, message);
}
public static void AreEqual(Vector3D expected, Vector3D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
Assert.AreEqual(expected.X, actual.X, tolerance, message);
Assert.AreEqual(expected.Y, actual.Y, tolerance, message);
Assert.AreEqual(expected.Z, actual.Z, tolerance, message);
}
public static void AreEqual(UnitVector3D expected, Vector3D actual, double tolerance = 1e-6, string message = "")
{
AreEqual(expected.ToVector3D(), actual, tolerance, message);
}
public static void AreEqual(Vector3D expected, UnitVector3D actual, double tolerance = 1e-6, string message = "")
{
AreEqual(expected, actual.ToVector3D(), tolerance, message);
}
public static void AreEqual(Vector2D expected, Vector2D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
Assert.AreEqual(expected.X, actual.X, tolerance, message);
Assert.AreEqual(expected.Y, actual.Y, tolerance, message);
}
public static void AreEqual(Point3D expected, Point3D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
Assert.AreEqual(expected.X, actual.X, tolerance, message);
Assert.AreEqual(expected.Y, actual.Y, tolerance, message);
Assert.AreEqual(expected.Z, actual.Z, tolerance, message);
}
public static void AreEqual(CoordinateSystem3D expected, CoordinateSystem3D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
if (expected.Values.Length != actual.Values.Length)
{
Assert.Fail();
}
for (var i = 0; i < expected.Values.Length; i++)
{
Assert.AreEqual(expected.Values[i], actual.Values[i], tolerance);
}
}
public static void AreEqual(double[] expected, double[] actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", "{" + string.Join(",", expected) + "}", "{" + string.Join(",", actual) + "}");
}
if (expected.Length != actual.Length)
{
Assert.Fail();
}
for (var i = 0; i < expected.Length; i++)
{
Assert.AreEqual(expected[i], actual[i], tolerance);
}
}
public static void AreEqual(Line3D expected, Line3D actual, double tolerance = 1e-6)
{
AreEqual(expected.StartPoint, actual.StartPoint, tolerance);
AreEqual(expected.EndPoint, actual.EndPoint, tolerance);
}
public static void AreEqual(LineSegment3D expected, LineSegment3D actual, double tolerance = 1e-6)
{
AreEqual(expected.StartPoint, actual.StartPoint, tolerance);
AreEqual(expected.EndPoint, actual.EndPoint, tolerance);
}
public static void AreEqual(Ray3D expected, Ray3D actual, double tolerance = 1e-6, string message = "")
{
AreEqual(expected.ThroughPoint, actual.ThroughPoint, tolerance, message);
AreEqual(expected.Direction, actual.Direction, tolerance, message);
}
public static void AreEqual(Plane3D expected, Plane3D actual, double tolerance = 1e-6, string message = "")
{
AreEqual(expected.Normal, actual.Normal, tolerance, message);
AreEqual(expected.RootPoint, actual.RootPoint, tolerance, message);
Assert.AreEqual(expected.D, actual.D, tolerance, message);
}
public static void AreEqual(Matrix<double> expected, Matrix<double> actual, double tolerance = 1e-6)
{
Assert.AreEqual(expected.RowCount, actual.RowCount);
Assert.AreEqual(expected.ColumnCount, actual.ColumnCount);
var expectedRowWiseArray = expected.ToRowMajorArray();
var actualRowWiseArray = actual.ToRowMajorArray();
for (var i = 0; i < expectedRowWiseArray.Length; i++)
{
Assert.AreEqual(expectedRowWiseArray[i], actualRowWiseArray[i], tolerance);
}
}
public static void AreEqual(Point2D expected, Point2D actual, double tolerance = 1e-6, string message = "")
{
if (string.IsNullOrEmpty(message))
{
message = string.Format("Expected {0} but was {1}", expected, actual);
}
Assert.AreEqual(expected.X, actual.X, tolerance, message);
Assert.AreEqual(expected.Y, actual.Y, tolerance, message);
}
}
}

117
src/Numerics.Tests/Spatial/AssertXml.cs

@ -0,0 +1,117 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial
{
public static class AssertXml
{
public static XmlWriterSettings Settings
{
get
{
var settings = new XmlWriterSettings
{
Indent = true,
NewLineHandling = NewLineHandling.Entitize,
OmitXmlDeclaration = true,
////NamespaceHandling = NamespaceHandling.Default
};
return settings;
}
}
public static void AreEqual(string first, string other)
{
var x1 = CleanupXml(first);
var x2 = CleanupXml(other);
Assert.AreEqual(x1, x2);
}
/// <summary>
/// Serializes using XmlSerializer & DataContractSerializer
/// Compares the generated xml
/// Then asserts that the deserialized is the same as input (item)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <param name="expectedXml"></param>
/// <param name="assert"></param>
public static void XmlRoundTrips<T>(T item, string expectedXml, Action<T, T> assert)
{
var roundtrips = new[]
{
XmlSerializerRoundTrip(item, expectedXml)
};
foreach (var roundtrip in roundtrips)
{
assert(item, roundtrip);
}
}
public static T XmlSerializerRoundTrip<T>(T item, string expected)
{
var serializer = new XmlSerializer(item.GetType());
string xml;
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, Settings))
{
serializer.Serialize(writer, item);
xml = sw.ToString();
Debug.WriteLine("XmlSerializer");
Debug.Write(xml);
Debug.WriteLine(string.Empty);
AreEqual(expected, xml);
}
using (var reader = new StringReader(xml))
{
return (T)serializer.Deserialize(reader);
}
}
private static string Normalize(XElement e)
{
using (var sw = new StringWriter())
using (var writer = XmlWriter.Create(sw, Settings))
{
e.WriteTo(writer);
writer.Flush();
return sw.ToString();
}
}
private static string CleanupXml(string xml)
{
var e = XElement.Parse(xml);
var clean = RemoveAllNamespaces(e);
return Normalize(clean);
}
/// <summary>
/// Core recursion function
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
private static XElement RemoveAllNamespaces(XElement e)
{
var ne = new XElement(e.Name.LocalName, e.HasElements ? null : e.Value);
ne.Add(e.Attributes().Where(a => !a.IsNamespaceDeclaration));
ne.Add(e.Elements().Select(RemoveAllNamespaces));
return ne;
}
public class Container<T>
{
public T Value1 { get; set; }
public T Value2 { get; set; }
}
}
}

111
src/Numerics.Tests/Spatial/AssertXmlTests.cs

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial
{
#if !NETCOREAPP1_1
public class AssertXmlTests
{
[Test]
public void XmlSerializerRoundTripTest()
{
var dummy = new XmlSerializableDummy("Meh", 14);
var roundTrip = AssertXml.XmlSerializerRoundTrip(dummy, @"<XmlSerializableDummy Age=""14""><Name>Meh</Name></XmlSerializableDummy>");
Assert.AreEqual(dummy.Name, roundTrip.Name);
Assert.AreEqual(dummy.Age, roundTrip.Age);
}
public class XmlSerializableDummy : IXmlSerializable
{
private readonly string name;
public XmlSerializableDummy(string name, int age)
{
this.Age = age;
this.name = name;
}
// ReSharper disable once UnusedMember.Local
private XmlSerializableDummy()
{
}
public string Name => this.name;
public int Age { get; set; }
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader)
{
var e = (XElement)XNode.ReadFrom(reader);
this.Age = XmlConvert.ToInt32(e.Attribute("Age").Value);
var name = ReadAttributeOrElement(e, "Name");
WriteValueToReadonlyField(this, name, () => this.name);
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("Age", this.Age.ToString(CultureInfo.InvariantCulture));
writer.WriteElementString("Name", this.Name);
}
private static string ReadAttributeOrElement(XElement e, string localName)
{
XAttribute xattribute = e.Attributes()
.SingleOrDefault(x => x.Name.LocalName == localName);
if (xattribute != null)
{
return xattribute.Value;
}
XElement xelement = e.Elements()
.SingleOrDefault(x => x.Name.LocalName == localName);
if (xelement != null)
{
return xelement.Value;
}
throw new XmlException($"Attribute or element {localName} not found");
}
private static void WriteValueToReadonlyField<TClass, TProperty>(
TClass item,
TProperty value,
Expression<Func<TProperty>> fieldExpression)
{
string name = ((MemberExpression)fieldExpression.Body).Member.Name;
GetAllFields(item.GetType())
.Single(x => x.Name == name)
.SetValue(item, value);
}
private static IEnumerable<FieldInfo> GetAllFields(Type t)
{
if (t == null)
{
return Enumerable.Empty<FieldInfo>();
}
BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic;
return t.GetFields(bindingAttr)
.Concat(GetAllFields(t.BaseType));
}
}
}
#endif
}

58
src/Numerics.Tests/Spatial/Euclidean2D/Circle2DTests.cs

@ -0,0 +1,58 @@
using System;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class Circle2DTests
{
[TestCase("0, 0", 2.5)]
[TestCase("2, -4", 4.7)]
public void CircleCenterRadius(string p1s, double radius)
{
var center = Point2D.Parse(p1s);
var circle = new Circle2D(center, radius);
Assert.AreEqual(2 * radius, circle.Diameter, double.Epsilon);
Assert.AreEqual(2 * Math.PI * radius, circle.Circumference, double.Epsilon);
Assert.AreEqual(Math.PI * radius * radius, circle.Area, double.Epsilon);
}
[TestCase("0, 0", 1)]
[TestCase("2, -4", 4.7)]
public void CircleEquality(string center, double radius)
{
var cp = Point2D.Parse(center);
var c = new Circle2D(cp, radius);
var c2 = new Circle2D(cp, radius);
Assert.True(c == c2);
Assert.True(c.Equals(c2));
}
[TestCase("-7,4", "-4,5", "0,3", "-4,0", 5)]
[TestCase("1,1", "2,4", "5,3", "3,2", 2.2360679775)]
[TestCase("-1,0", "0,1", "1,0", "0,0", 1)]
public void CircleFromThreePoints(string p1s, string p2s, string p3s, string centers, double radius)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var p3 = Point2D.Parse(p3s);
var center = Point2D.Parse(centers);
var circle = Circle2D.FromPoints(p1, p2, p3);
AssertGeometry.AreEqual(center, circle.Center);
Assert.AreEqual(radius, circle.Radius, 1e-6);
}
[Test]
public void CircleFromThreePointsArgumentException()
{
var p1 = new Point2D(0, 0);
var p2 = new Point2D(-1, 0);
var p3 = new Point2D(1, 0);
Assert.Throws<ArgumentException>(() => { Circle2D.FromPoints(p1, p2, p3); });
}
}
}

214
src/Numerics.Tests/Spatial/Euclidean2D/Line2DTests.cs

@ -0,0 +1,214 @@
using System;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class Line2DTests
{
[Test]
public void Constructor()
{
var p1 = new Point2D(0, 0);
var p2 = new Point2D(1, 1);
var line = new Line2D(p1, p2);
AssertGeometry.AreEqual(p1, line.StartPoint);
AssertGeometry.AreEqual(p2, line.EndPoint);
}
[Test]
public void ConstructorThrowsErrorOnSamePoint()
{
var p1 = new Point2D(1, -1);
var p2 = new Point2D(1, -1);
Assert.Throws<ArgumentException>(() => new Line2D(p1, p2));
}
[TestCase("0,0", "1,0", 1)]
[TestCase("0,0", "0,1", 1)]
[TestCase("0,0", "-1,0", 1)]
[TestCase("0,-1", "0,1", 2)]
[TestCase("-1,-1", "2,2", 4.24264068711)]
public void LineLength(string p1s, string p2s, double expected)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var line = new Line2D(p1, p2);
var len = line.Length;
Assert.AreEqual(expected, len, 1e-7);
}
[TestCase("0,0", "4,0", "1,0")]
[TestCase("3,0", "0,0", "-1,0")]
[TestCase("2.7,-2.7", "0,0", "-0.707106781,0.707106781")]
[TestCase("11,-1", "11,1", "0,1")]
public void LineDirection(string p1s, string p2s, string exs)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var ex = Vector2D.Parse(exs);
var line = new Line2D(p1, p2);
AssertGeometry.AreEqual(ex, line.Direction);
}
[TestCase("0,0", "10,10", "0,0", "10,10", true)]
[TestCase("0,0", "10,10", "0,0", "10,11", false)]
public void EqualityOperator(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var l1 = Line2D.Parse(p1s, p2s);
var l2 = Line2D.Parse(p3s, p4s);
Assert.AreEqual(expected, l1 == l2);
}
[TestCase("0,0", "10,10", "0,0", "10,10", false)]
[TestCase("0,0", "10,10", "0,0", "10,11", true)]
public void InequalityOperator(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var l1 = new Line2D(Point2D.Parse(p1s), Point2D.Parse(p2s));
var l2 = new Line2D(Point2D.Parse(p3s), Point2D.Parse(p4s));
Assert.AreEqual(expected, l1 != l2);
}
[Test]
public void EqualityComparisonFalseAgainstNull()
{
var line = new Line2D(default(Point2D), new Point2D(1, 1));
Assert.IsFalse(line.Equals(null));
}
[Test]
public void AdditionOperator()
{
var l1 = Line2D.Parse("0,0", "1,1");
var ex = Line2D.Parse("-1,-1", "0,0");
Assert.AreEqual(ex, l1 + new Vector2D(-1, -1));
}
[Test]
public void SubtractionOperator()
{
var l1 = Line2D.Parse("0,0", "1,1");
var ex = Line2D.Parse("-1,-1", "0,0");
Assert.AreEqual(ex, l1 - new Vector2D(1, 1));
}
[TestCase("0,0", "1,-1", Description = "Check start point")]
[TestCase("1,0", "1,-1")]
[TestCase("1,-2", "1,-1")]
[TestCase("4,0", "3,-1", Description = "Check end point")]
[TestCase("3,0", "3,-1")]
[TestCase("3,-3", "3,-1")]
[TestCase("1.5,0", "1.5,-1", Description = "Check near middle")]
[TestCase("1.5,-2", "1.5,-1")]
public void LineToBetweenEndPoints(string ptest, string exs)
{
var line = Line2D.Parse("1,-1", "3,-1");
var point = Point2D.Parse(ptest);
var expPoint = Point2D.Parse(exs);
var expLine = new Line2D(expPoint, point);
Assert.AreEqual(expLine, line.LineTo(point, true));
}
[TestCase("0,0", "0,-1", Description = "Check start point")]
[TestCase("1,0", "1,-1")]
[TestCase("1,-2", "1,-1")]
[TestCase("4,0", "4,-1", Description = "Check end pointt")]
[TestCase("3,0", "3,-1")]
[TestCase("3,-3", "3,-1")]
[TestCase("1.5,0", "1.5,-1", Description = "Check near middle")]
[TestCase("1.5,-2", "1.5,-1")]
public void LineToIgnoreEndPoints(string ptest, string exs)
{
var line = Line2D.Parse("1,-1", "3,-1");
var point = Point2D.Parse(ptest);
var expPoint = Point2D.Parse(exs);
var expLine = new Line2D(expPoint, point);
Assert.AreEqual(expLine, line.LineTo(point, false));
}
[TestCase("0,0", "1,0", "0,0", "0,0")]
[TestCase("0,0", "1,0", "1,0", "1,0")]
[TestCase("0,0", "1,0", ".25,1", ".25,0")]
[TestCase("0,0", "1,0", "-1,0", "0,0")]
[TestCase("0,0", "1,0", "3,0", "1,0")]
public void ClosestPointToWithinSegment(string start, string end, string point, string expected)
{
var line = Line2D.Parse(start, end);
var p = Point2D.Parse(point);
var e = Point2D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p, true));
}
[TestCase("0,0", "1,0", "0,0", "0,0")]
[TestCase("0,0", "1,0", "1,0", "1,0")]
[TestCase("0,0", "1,0", ".25,1", ".25,0")]
[TestCase("0,0", "1,0", "-1,1", "-1,0")]
[TestCase("0,0", "1,0", "3,0", "3,0")]
public void ClosestPointToOutsideSegment(string start, string end, string point, string expected)
{
var line = Line2D.Parse(start, end);
var p = Point2D.Parse(point);
var e = Point2D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p, false));
}
[TestCase("0,0", "2,2", "1,0", "1,2", "1,1")]
[TestCase("0,0", "2,2", "0,1", "2,1", "1,1")]
[TestCase("0,0", "2,2", "-1,-5", "-1,0", "-1,-1")]
[TestCase("0,0", "2,2", "0,1", "1,2", null)]
public void IntersectWithTest(string s1, string e1, string s2, string e2, string expected)
{
var line1 = Line2D.Parse(s1, e1);
var line2 = Line2D.Parse(s2, e2);
var e = string.IsNullOrEmpty(expected) ? (Point2D?)null : Point2D.Parse(expected);
Assert.AreEqual(e, line1.IntersectWith(line2));
Assert.AreEqual(e, line1.IntersectWith(line2, Angle.FromRadians(0.001)));
}
[TestCase("0,0", "0,1", "1,1", "1,2", true)]
[TestCase("0,0", "0,-1", "1,1", "1,2", true)]
[TestCase("0,0", "0.5,-1", "1,1", "1,2", false)]
[TestCase("0,0", "0.00001,-1.0000", "1,1", "1,2", false)]
public void IsParallelToWithinDoubleTol(string s1, string e1, string s2, string e2, bool expected)
{
var line1 = Line2D.Parse(s1, e1);
var line2 = Line2D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2));
}
[TestCase("0,0", "0,1", "1,1", "1,2", 0.01, true)]
[TestCase("0,0", "0,-1", "1,1", "1,2", 0.01, true)]
[TestCase("0,0", "0.5,-1", "1,1", "1,2", 0.01, false)]
[TestCase("0,0", "0.001,-1.0000", "1,1", "1,2", 0.05, false)]
[TestCase("0,0", "0.001,-1.0000", "1,1", "1,2", 0.06, true)]
public void IsParallelToWithinAngleTol(string s1, string e1, string s2, string e2, double degreesTol, bool expected)
{
var line1 = Line2D.Parse(s1, e1);
var line2 = Line2D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2, Angle.FromDegrees(degreesTol)));
}
[Test]
public void ToStringCheck()
{
var check = Line2D.Parse("0,0", "1,1").ToString();
Assert.AreEqual("StartPoint: (0,\u00A00), EndPoint: (1,\u00A01)", check);
}
}
}

167
src/Numerics.Tests/Spatial/Euclidean2D/LineSegment2DTests.cs

@ -0,0 +1,167 @@
using System;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class LineSegment2DTests
{
[Test]
public void Constructor()
{
var p1 = new Point2D(0, 0);
var p2 = new Point2D(1, 1);
var line = new LineSegment2D(p1, p2);
AssertGeometry.AreEqual(p1, line.StartPoint);
AssertGeometry.AreEqual(p2, line.EndPoint);
}
[Test]
public void ConstructorThrowsErrorOnSamePoint()
{
var p1 = new Point2D(1, -1);
var p2 = new Point2D(1, -1);
Assert.Throws<ArgumentException>(() => new LineSegment2D(p1, p2));
}
[TestCase("0,0", "1,0", 1)]
[TestCase("0,0", "0,1", 1)]
[TestCase("0,0", "-1,0", 1)]
[TestCase("0,-1", "0,1", 2)]
[TestCase("-1,-1", "2,2", 4.24264068711)]
public void LineLength(string p1s, string p2s, double expected)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var line = new LineSegment2D(p1, p2);
var len = line.Length;
Assert.AreEqual(expected, len, 1e-7);
}
[TestCase("0,0", "4,0", "1,0")]
[TestCase("3,0", "0,0", "-1,0")]
[TestCase("2.7,-2.7", "0,0", "-0.707106781,0.707106781")]
[TestCase("11,-1", "11,1", "0,1")]
public void LineDirection(string p1s, string p2s, string exs)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var ex = Vector2D.Parse(exs);
var line = new LineSegment2D(p1, p2);
AssertGeometry.AreEqual(ex, line.Direction);
}
[TestCase("0,0", "10,10", "0,0", "10,10", true)]
[TestCase("0,0", "10,10", "0,0", "10,11", false)]
public void EqualityOperator(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var l1 = LineSegment2D.Parse(p1s, p2s);
var l2 = LineSegment2D.Parse(p3s, p4s);
Assert.AreEqual(expected, l1 == l2);
}
[TestCase("0,0", "10,10", "0,0", "10,10", false)]
[TestCase("0,0", "10,10", "0,0", "10,11", true)]
public void InequalityOperator(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var l1 = new LineSegment2D(Point2D.Parse(p1s), Point2D.Parse(p2s));
var l2 = new LineSegment2D(Point2D.Parse(p3s), Point2D.Parse(p4s));
Assert.AreEqual(expected, l1 != l2);
}
[Test]
public void EqualityComparisonFalseAgainstNull()
{
var line = new LineSegment2D(default(Point2D), new Point2D(1, 1));
Assert.IsFalse(line.Equals(null));
}
[TestCase("0,0", "1,-1", Description = "Check start point")]
[TestCase("1,0", "1,-1")]
[TestCase("1,-2", "1,-1")]
[TestCase("4,0", "3,-1", Description = "Check end point")]
[TestCase("3,0", "3,-1")]
[TestCase("3,-3", "3,-1")]
[TestCase("1.5,0", "1.5,-1", Description = "Check near middle")]
[TestCase("1.5,-2", "1.5,-1")]
public void LineToBetweenEndPoints(string ptest, string exs)
{
var line = LineSegment2D.Parse("1,-1", "3,-1");
var point = Point2D.Parse(ptest);
var expPoint = Point2D.Parse(exs);
var expLine = new LineSegment2D(expPoint, point);
Assert.AreEqual(expLine, line.LineTo(point));
}
[TestCase("1,1", "3,1", "1,1", "2,2", "4,2")]
[TestCase("1,1", "3,1", "-1,-1", "0,0", "2,0")]
public void TranslateBy(string spoint1, string spoint2, string svector, string spoint3, string spoint4)
{
var line = LineSegment2D.Parse(spoint1, spoint2);
var expected = LineSegment2D.Parse(spoint3, spoint4);
var vector = Vector2D.Parse(svector);
Assert.AreEqual(expected.Length, line.Length);
Assert.AreEqual(expected, line.TranslateBy(vector));
}
[TestCase("0,0", "1,0", "0,0", "0,0")]
[TestCase("0,0", "1,0", "1,0", "1,0")]
[TestCase("0,0", "1,0", ".25,1", ".25,0")]
[TestCase("0,0", "1,0", "-1,0", "0,0")]
[TestCase("0,0", "1,0", "3,0", "1,0")]
public void ClosestPointToWithinSegment(string start, string end, string point, string expected)
{
var line = LineSegment2D.Parse(start, end);
var p = Point2D.Parse(point);
var e = Point2D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p));
}
[TestCase("0,0", "2,2", "1,0", "1,2", "1,1")]
[TestCase("0,0", "2,2", "0,1", "2,1", "1,1")]
[TestCase("0,0", "2,2", "-1,-5", "-1,0", "-1,-1")]
public void IntersectWithTest(string s1, string e1, string s2, string e2, string expected)
{
var line1 = LineSegment2D.Parse(s1, e1);
var line2 = LineSegment2D.Parse(s2, e2);
var e = string.IsNullOrEmpty(expected) ? (Point2D?)null : Point2D.Parse(expected);
bool success = line1.TryIntersect(line2, out var result, Angle.FromRadians(0.001));
Assert.IsTrue(success);
Assert.AreEqual(e, result);
}
[TestCase("0,0", "0,1", "1,1", "1,2", 0.0001, true)]
[TestCase("0,0", "0,-1", "1,1", "1,2", 0.0001, true)]
[TestCase("0,0", "0.5,-1", "1,1", "1,2", 0.0001, false)]
[TestCase("0,0", "0.00001,-1.0000", "1,1", "1,2", 0.0001, false)]
[TestCase("0,0", "0,1", "1,1", "1,2", 0.01, true)]
[TestCase("0,0", "0,-1", "1,1", "1,2", 0.01, true)]
[TestCase("0,0", "0.5,-1", "1,1", "1,2", 0.01, false)]
[TestCase("0,0", "0.001,-1.0000", "1,1", "1,2", 0.05, false)]
[TestCase("0,0", "0.001,-1.0000", "1,1", "1,2", 0.06, true)]
public void IsParallelToWithinAngleTol(string s1, string e1, string s2, string e2, double degreesTol, bool expected)
{
var line1 = LineSegment2D.Parse(s1, e1);
var line2 = LineSegment2D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2, Angle.FromDegrees(degreesTol)));
}
[Test]
public void ToStringCheck()
{
var check = LineSegment2D.Parse("0,0", "1,1").ToString();
Assert.AreEqual("StartPoint: (0,\u00A00), EndPoint: (1,\u00A01)", check);
}
}
}

283
src/Numerics.Tests/Spatial/Euclidean2D/Point2DTests.cs

@ -0,0 +1,283 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class Point2DTests
{
[Test]
public void Ctor()
{
var p = new Point2D(1, 2);
Assert.AreEqual(1, p.X);
Assert.AreEqual(2, p.Y);
}
[TestCase(5, "90 °", "0, 5")]
[TestCase(3, "-90 °", "0, -3")]
[TestCase(1, "45 °", "0.71, 0.71")]
[TestCase(1, "-45 °", "0.71, -0.71")]
[TestCase(1, "0 °", "1, 0")]
[TestCase(1, "180 °", "-1, 0")]
public void FromPolar(int radius, string avs, string eps)
{
var angle = Angle.Parse(avs);
var p = Point2D.FromPolar(radius, angle);
var ep = Point2D.Parse(eps);
AssertGeometry.AreEqual(ep, p, 1e-2);
}
[Test]
public void FromPolarFailsWhenNegativeRadius()
{
Assert.Throws<ArgumentOutOfRangeException>(() => Point2D.FromPolar(-1.0, Angle.FromRadians(0)));
}
[TestCase("-1, -2", "1, 2", "0, 0")]
public void OperatorAddVector2D(string ps, string vs, string eps)
{
var p = Point2D.Parse(ps);
var v = Vector2D.Parse(vs);
var actual = p + v;
var expected = Point2D.Parse(eps);
Assert.AreEqual(expected, actual);
}
[TestCase("-1, -2", "1, 2", "-2, -4")]
public void OperatorSubtractVector2D(string ps, string vs, string eps)
{
var p = Point2D.Parse(ps);
var v = Vector2D.Parse(vs);
var actual = p - v;
var expected = Point2D.Parse(eps);
Assert.AreEqual(expected, actual);
}
[TestCase("-1, -2", "1, 2", "-2, -4")]
public void OperatorSubtractPoint2D(string p1s, string p2s, string eps)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var actual = p1 - p2;
var expected = Vector2D.Parse(eps);
Assert.AreEqual(expected, actual);
}
[TestCase("-1,1", -1, 1)]
[TestCase("-1,-1", -1, -1)]
[TestCase("1, 2", 1, 2)]
[TestCase("1.2; 3.4", 1.2, 3.4)]
[TestCase("1.2;3.4", 1.2, 3.4)]
[TestCase("1,2; 3,4", 1.2, 3.4)]
[TestCase("1.2, 3.4", 1.2, 3.4)]
[TestCase("1.2 3.4", 1.2, 3.4)]
[TestCase("1.2,\u00A03.4", 1.2, 3.4)]
[TestCase("1.2\u00A03.4", 1.2, 3.4)]
[TestCase("(1.2, 3.4)", 1.2, 3.4)]
[TestCase("(.1, 2.3e-4)", 0.1, 0.00023000000000000001)]
public void Parse(string text, double expectedX, double expectedY)
{
Assert.AreEqual(true, Point2D.TryParse(text, out var p));
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
p = Point2D.Parse(text);
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
p = Point2D.Parse(p.ToString());
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
}
[TestCase("1.2")]
[TestCase("1; 2; 3")]
public void ParseFails(string text)
{
Assert.AreEqual(false, Point2D.TryParse(text, out _));
Assert.Throws<FormatException>(() => Point2D.Parse(text));
}
[TestCase(@"<Point2D X=""1"" Y=""2"" />")]
[TestCase(@"<Point2D><X>1</X><Y>2</Y></Point2D>")]
public void ReadFrom(string xml)
{
var v = new Point2D(1, 2);
AssertGeometry.AreEqual(v, Point2D.ReadFrom(XmlReader.Create(new StringReader(xml))));
}
[Test]
public void OfVector()
{
var p = Point2D.OfVector(DenseVector.OfArray(new[] { 1, 2.0 }));
Assert.AreEqual(1, p.X);
Assert.AreEqual(2, p.Y);
Assert.Throws<ArgumentException>(() => Point2D.OfVector(DenseVector.OfArray(new[] { 1, 2, 3.0 })));
}
[Test]
public void ToDenseVector()
{
var p = new Point2D(1, 2);
var v = p.ToVector();
Assert.AreEqual(2, v.Count);
Assert.AreEqual(1, v[0]);
Assert.AreEqual(2, v[1]);
}
[TestCase("0, 0", "1, 0", 1.0)]
[TestCase("2, 0", "1, 0", 1.0)]
[TestCase("2, 0", "-1, 0", 3.0)]
[TestCase("0, 2", "0, -1", 3.0)]
public void DistanceTo(string p1s, string p2s, double expected)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
Assert.AreEqual(expected, p1.DistanceTo(p2));
Assert.AreEqual(expected, p2.DistanceTo(p1));
}
[TestCase("0, 0", "1, 2", "0.5, 1")]
[TestCase("-1, -2", "1, 2", "0, 0")]
public void MidPoint(string p1s, string p2s, string eps)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var centroids = new[]
{
Point2D.Centroid(p1, p2),
Point2D.MidPoint(p1, p2),
};
var expected = Point2D.Parse(eps);
foreach (var centroid in centroids)
{
AssertGeometry.AreEqual(expected, centroid);
}
}
[TestCase("1, 0", "90 °", "0, 1")]
[TestCase("1, 0", "-90 °", "0, -1")]
[TestCase("1, 0", "45 °", "0.71, 0.71")]
[TestCase("1, 0", "-45 °", "0.71, -0.71")]
[TestCase("1, 0", "30 °", "0.87, 0.5")]
[TestCase("-5, 0", "30 °", "-4.33, -2.5")]
public void RotateTest(string ps, string avs, string eps)
{
var p = Point2D.Parse(ps);
var av = Angle.Parse(avs);
var expected = Point2D.Parse(eps);
var rm = Matrix2D.Rotation(av);
var actual = p.TransformBy(rm);
AssertGeometry.AreEqual(expected, actual, 1e-2);
}
[TestCase("0, 0", "1, 2", "1, 2")]
public void VectorTo(string p1s, string p2s, string evs)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
var actual = p1.VectorTo(p2);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, actual);
}
[TestCase("1, 2", "1, 2")]
public void ToVector(string ps, string evs)
{
var p1 = Point2D.Parse(ps);
var actual = p1.ToVector2D();
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, actual);
}
[TestCase("1, 2", "1, 2", 1e-4, true)]
[TestCase("-1, 2", "-1, 2", 1e-4, true)]
[TestCase("1, 2", "3, 4", 1e-4, false)]
public void Equals(string p1s, string p2s, double tol, bool expected)
{
var p1 = Point2D.Parse(p1s);
var p2 = Point2D.Parse(p2s);
Assert.AreEqual(expected, p1 == p2);
Assert.AreEqual(expected, p2 == p1);
Assert.AreEqual(!expected, p1 != p2);
Assert.AreEqual(!expected, p2 != p1);
Assert.AreEqual(expected, p1.Equals(p2));
Assert.AreEqual(expected, p1.Equals((object)p2));
Assert.AreEqual(expected, Equals(p1, p2));
Assert.AreEqual(expected, p1.Equals(p2, tol));
Assert.AreNotEqual(expected, p1 != p2);
Assert.AreEqual(false, p1.Equals(null));
}
[TestCase("-2, 0", null, "(-2,\u00A00)")]
[TestCase("-2, 0", "N2", "(-2.00,\u00A00.00)")]
public void ToString(string vs, string format, string expected)
{
var v = Point2D.Parse(vs);
var actual = v.ToString(format);
Assert.AreEqual(expected, actual);
Assert.AreEqual(v, Point2D.Parse(actual));
}
[Test]
public void XmlRoundtrip()
{
var v = new Point2D(1, 2);
AssertXml.XmlRoundTrips(v, @"<Point2D X=""1"" Y=""2"" />", (e, a) => AssertGeometry.AreEqual(e, a));
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<Point2D>
{
Value1 = new Point2D(1, 2),
Value2 = new Point2D(3, 4)
};
var expected = "<ContainerOfPoint2D>\r\n" +
" <Value1 X=\"1\" Y=\"2\"></Value1>\r\n" +
" <Value2 X=\"3\" Y=\"4\"></Value2>\r\n" +
"</ContainerOfPoint2D>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
AssertGeometry.AreEqual(container.Value1, roundTrip.Value1);
AssertGeometry.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void XmlElements()
{
var v = new Point2D(1, 2);
var serializer = new XmlSerializer(typeof(Point2D));
AssertGeometry.AreEqual(v, (Point2D)serializer.Deserialize(new StringReader(@"<Point2D><X>1</X><Y>2</Y></Point2D>")));
}
[Test]
public void XmlContainerElements()
{
var container = new AssertXml.Container<Point2D>
{
Value1 = new Point2D(1, 2),
Value2 = new Point2D(3, 4)
};
var xml = "<ContainerOfPoint2D>\r\n" +
" <Value1><X>1</X><Y>2</Y></Value1>\r\n" +
" <Value2><X>3</X><Y>4</Y></Value2>\r\n" +
"</ContainerOfPoint2D>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<Point2D>));
var deserialized = (AssertXml.Container<Point2D>)serializer.Deserialize(new StringReader(xml));
AssertGeometry.AreEqual(container.Value1, deserialized.Value1);
AssertGeometry.AreEqual(container.Value2, deserialized.Value2);
}
}
}

72
src/Numerics.Tests/Spatial/Euclidean2D/PolyLine2DTests.cs

@ -0,0 +1,72 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class PolyLine2DTests
{
[TestCase("0,0;1,1;2,2;3,3", 1, "1,1")]
[TestCase("0,0;1,1;2,2;3,3", 0, "0,0")]
[TestCase("0,0;1,1;2,2;3,3", 3, "3,3")]
public void IndexAccessorTest(string points, int index, string expected)
{
var testElement = new PolyLine2D(from x in points.Split(';') select Point2D.Parse(x));
var checkElement = Point2D.Parse(expected);
AssertGeometry.AreEqual(checkElement, testElement.Vertices.Skip(index).First());
}
[TestCase("0,0;0,1", 1.0)]
[TestCase("0,0;0,1;1,1", 2.0)]
[TestCase("0,-1.5;0,1;1,1", 3.5)]
public void GetPolyLineLengthTests(string points, double expected)
{
var testElement = new PolyLine2D(from x in points.Split(';') select Point2D.Parse(x));
Assert.AreEqual(expected, testElement.Length);
}
[TestCase("0,-1.5;0,1;1,1", 1.0, "1,1")]
[TestCase("0,-1.5;0,1;1,1", 0.0, "0,-1.5")]
[TestCase("0,0;0,1;1,1", 0.25, "0,0.5")]
[TestCase("0,0;0,1;1,1", 0.5, "0,1")]
[TestCase("0,0;0,1;1,1", 0.75, "0.5,1")]
public void GetPointAtFractionAlongCurve(string points, double fraction, string expected)
{
// Note that this method also tests GetPointAtLengthFromStart(...)
var testElement = new PolyLine2D(from x in points.Split(';') select Point2D.Parse(x));
var checkElement = Point2D.Parse(expected);
Assert.AreEqual(checkElement, testElement.GetPointAtFractionAlongCurve(fraction));
}
[TestCase("0,-1.5;0,1;1,1", 2.0, "1,1")]
[TestCase("0,-1.5;0,1;1,1", -5, "0,-1.5")]
public void GetPointAtFractionAlongCurveThrowsArgumentException(string points, double fraction, string expected)
{
var testElement = new PolyLine2D(from x in points.Split(';') select Point2D.Parse(x));
Assert.Throws<ArgumentException>(() => { testElement.GetPointAtFractionAlongCurve(fraction); });
}
[TestCase("0,0;0,1;1,1", "0,-1", "0,0")] // Off Endpoint
[TestCase("0,0;0,1;1,1", "2,1", "1,1")] // Off Endpoint
[TestCase("0,0;0,1;1,1", "-1,2", "0,1")] // Off Corner
[TestCase("0,0;0,1;1,1", "0,0", "0,0")] // On Endpoint
[TestCase("0,0;0,1;1,1", "1,1", "1,1")] // On Endpoint
[TestCase("0,0;0,1;1,1", "0,1", "0,1")] // On Corner
[TestCase("0,0;0,1;1,1", "0,0.5", "0,0.5")] // On Curve
[TestCase("0,0;0,1;1,1", "-1,0.5", "0,0.5")] // Off curve
[TestCase("0,0;0,1;1,1", "0.5,1", "0.5,1")] // On Curve
[TestCase("0,0;0,1;1,1", "0.5,1.5", "0.5,1")] // Off curve
public void ClosestPointToTest(string points, string testPoint, string expectedPoint)
{
var testCurve = new PolyLine2D(from x in points.Split(';') select Point2D.Parse(x));
var test = Point2D.Parse(testPoint);
var expected = Point2D.Parse(expectedPoint);
Assert.AreEqual(expected, testCurve.ClosestPointTo(test));
}
}
}

186
src/Numerics.Tests/Spatial/Euclidean2D/Polygon2DTests.cs

@ -0,0 +1,186 @@
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class Polygon2DTests
{
[Test]
public void ConstructorTest()
{
var polygon = TestPolygon1();
var checkList = new List<Point2D> { new Point2D(0, 0), new Point2D(0.25, 0.5), new Point2D(1, 1), new Point2D(-1, 1), new Point2D(0.5, -0.5) };
CollectionAssert.AreEqual(checkList, polygon.Vertices);
}
[TestCase("0,0;2,2;3,1;2,0", "1,1", "1,1;3,3;4,2;3,1")]
[TestCase("0,0;2,2;3,1;2,0", "-1,-1", "-1,-1;1,1;2,0;1,-1")]
public void TranslatePolygon(string points, string vectorString, string expectedPolygon)
{
var testElement = new Polygon2D(from x in points.Split(';') select Point2D.Parse(x));
var expected = new Polygon2D(from x in expectedPolygon.Split(';') select Point2D.Parse(x));
Vector2D vector = Vector2D.Parse(vectorString);
var result = testElement.TranslateBy(vector);
Assert.AreEqual(expected, result);
}
[TestCase("0,0;1,2;-1,2", System.Math.PI, "0,0;-1,-2;1,-2")]
public void RotatePolygon(string points, double angle, string expectedPolygon)
{
var testElement = new Polygon2D(from x in points.Split(';') select Point2D.Parse(x));
var expected = new Polygon2D(from x in expectedPolygon.Split(';') select Point2D.Parse(x));
Angle a = Angle.FromRadians(angle);
var result = testElement.Rotate(a);
Assert.IsTrue(expected.Equals(result, 0.001));
}
[TestCase("0,0;1,2;-1,2", "0,0:1,2;1,2:-1,2;-1,2:0,0")]
public void PolygonEdges(string stringpoints, string lines)
{
List<Point2D> points = (from x in stringpoints.Split(';') select Point2D.Parse(x)).ToList();
var lineset = lines.Split(';').Select(t => LineSegment2D.Parse(t.Split(':').First(), t.Split(':').Last())).ToList();
var poly = new Polygon2D(points);
CollectionAssert.AreEquivalent(lineset, poly.Edges);
}
[Test]
public void ConstructorTest_ClipsStartOnDuplicate()
{
// Test to make sure that if the constructor point list is given to the polygon constructor with the first and last points
// being duplicates, the point at the beginning of the list is removed
var polygon = TestPolygon2();
var checkList = new List<Point2D> { new Point2D(0.25, 0.5), new Point2D(1, 1), new Point2D(-1, 1), new Point2D(0.5, -0.5), new Point2D(0, 0) };
CollectionAssert.AreEqual(checkList, polygon.Vertices);
}
[TestCase(0.5, 0, true)]
[TestCase(0.35, 0, true)]
[TestCase(0.5, 0.5, true)]
[TestCase(0.75, 0.1, false)]
[TestCase(0.75, -0.1, true)]
[TestCase(0.5, -0.5, false)]
[TestCase(0.25, 0.5, false)]
[TestCase(0.25, -0.5, false)]
[TestCase(0.0, 0, false)]
[TestCase(1.5, 0, false)]
public void IsPointInPolygonTest1(double x, double y, bool outcome)
{
var testPoint = new Point2D(x, y);
var testPoly = TestPolygon3();
Assert.AreEqual(outcome, testPoly.EnclosesPoint(testPoint));
}
[TestCase(0.5, 0, true)]
[TestCase(0.35, 0, true)]
[TestCase(0.5, 0.5, true)]
[TestCase(0.75, 0.1, false)]
[TestCase(0.75, -0.1, true)]
[TestCase(0.5, -0.5, false)]
[TestCase(0.25, 0.5, false)]
[TestCase(0.25, -0.5, false)]
[TestCase(0.0, 0, false)]
[TestCase(1.5, 0, false)]
public void IsPointInPolygonTest2(double x, double y, bool outcome)
{
var testPoint = new Point2D(x, y);
var testPoly = TestPolygon4();
Assert.AreEqual(outcome, testPoly.EnclosesPoint(testPoint));
}
// These test cases were generated using scipy.spatial's ConvexHull method
[TestCase("0.27,0.41;0.87,0.67;0.7,0.33;0.5,0.61;0.04,0.23;0.73,0.14;0.84,0.02;0.25,0.23;0.12,0.2;0.37,0.78", "0.87,0.67;0.37,0.78;0.04,0.23;0.12,0.2;0.84,0.02")]
[TestCase("0.81,0.25;0.77,0.15;0.17,0.48;0.4,0.58;0.29,0.92;0.37,0.26;0.7,0.91;0.04,0.1;0.39,0.73;0.7,0.12", "0.7,0.91;0.29,0.92;0.04,0.1;0.7,0.12;0.77,0.15;0.81,0.25")]
[TestCase("0.87,0.39;0.83,0.42;0.75,0.62;0.91,0.49;0.18,0.63;0.17,0.95;0.22,0.5;0.93,0.41;0.66,0.79;0.32,0.42", "0.66,0.79;0.17,0.95;0.18,0.63;0.22,0.5;0.32,0.42;0.87,0.39;0.93,0.41;0.91,0.49")]
[TestCase("0.18,0.39;0.91,0.3;0.35,0.53;0.91,0.38;0.49,0.28;0.61,0.22;0.27,0.18;0.44,0.06;0.5,0.79;0.78,0.22", "0.91,0.38;0.5,0.79;0.18,0.39;0.27,0.18;0.44,0.06;0.78,0.22;0.91,0.3")]
[TestCase("0.89,0.55;0.98,0.24;0.03,0.2;0.51,0.99;0.72,0.32;0.56,0.87;0.1,0.75;0.64,0.16;0.82,0.73;0.17,0.46", "0.89,0.55;0.82,0.73;0.51,0.99;0.1,0.75;0.03,0.2;0.64,0.16;0.98,0.24")]
[TestCase("-201.573,100.940;197.083,21.031;161.021,-29.414;114.223,-23.998;230.290,-68.246;-32.272,182.239;-173.345,72.736;-175.435,-176.273;90.810,-97.350;-196.942,216.594;67.759,-162.464;67.454,-174.844;-89.116,171.982;-18.421,11.935;73.816,-180.169;-103.560,-36.297;-233.800,194.296;-64.463,166.811;-17.182,83.403;-72.010,219.944", "-72.01,219.944;-196.942,216.594;-233.8,194.296;-175.435,-176.273;73.816,-180.169;230.29,-68.246;197.083,21.031")]
public void ConvexHullTest(string points, string expected)
{
var testPoints = (from x in points.Split(';') select Point2D.Parse(x)).ToList();
var expectedPoints = (from x in expected.Split(';') select Point2D.Parse(x)).ToList();
var hullClockwise = Polygon2D.GetConvexHullFromPoints(testPoints, true);
var clockwiseVertices = hullClockwise.Vertices;
CollectionAssert.AreEqual(expectedPoints, clockwiseVertices);
/*
for (var i = 0; i < hullClockwise.VertexCount; i++)
{
Assert.That(ClockwiseVerticies[i], Is.EqualTo(expectedPoints[i]));
}
*/
var hullCounterClockwise = Polygon2D.GetConvexHullFromPoints(testPoints, false);
var counterClockwiseVertices = hullCounterClockwise.Vertices;
expectedPoints.Reverse();
CollectionAssert.AreEqual(expectedPoints, counterClockwiseVertices);
/*
for (var i = 0; i < hullCounterClockwise.VertexCount; i++)
{
Assert.That(counterClockwiseVerticies[i], Is.EqualTo(expectedPoints[hullCounterClockwise.VertexCount - 1 - i]));
}
*/
var pointsNotOnConvexHull = testPoints.Except(hullCounterClockwise.Vertices);
foreach (var pointNotOnConvexHull in pointsNotOnConvexHull)
{
var pointIsInsideConvexHull = hullCounterClockwise.EnclosesPoint(pointNotOnConvexHull);
Assert.That(pointIsInsideConvexHull);
}
// second check: if we remove any point from the convex hull and build a new convex hull
// then that point should be outside the new convex hull; if it's inside then our new
// convex hull is the actual convex hull, which means the original one wasn't!
foreach (var pointToRemove in counterClockwiseVertices)
{
var convexHullWithPointRemoved = new Polygon2D(hullCounterClockwise.Vertices.Except(new[] { pointToRemove }));
var pointIsInsideConvexHull =
convexHullWithPointRemoved.EnclosesPoint(pointToRemove);
Assert.That(pointIsInsideConvexHull, Is.Not.True);
}
}
[TestCase("0,0;0.4,0;0.5,0;0.6,0;1,0;1,.25;1,.75;1,1;0,1;0,0.5", "1,0;1,1;0,1;0,0")]
public void ReduceComplexity(string points, string reduced)
{
var testPoints = from x in points.Split(';') select Point2D.Parse(x);
var expectedPoints = from x in reduced.Split(';') select Point2D.Parse(x);
var poly = new Polygon2D(testPoints);
var expected = new Polygon2D(expectedPoints);
var thinned = poly.ReduceComplexity(0.00001);
CollectionAssert.AreEqual(expected.Vertices, thinned.Vertices);
}
private static Polygon2D TestPolygon1()
{
var points = from x in new[] { "0,0", "0.25,0.5", "1,1", "-1,1", "0.5,-0.5" } select Point2D.Parse(x);
return new Polygon2D(points);
}
private static Polygon2D TestPolygon2()
{
var points = from x in new[] { "0,0", "0.25,0.5", "1,1", "-1,1", "0.5,-0.5", "0,0" } select Point2D.Parse(x);
return new Polygon2D(points);
}
private static Polygon2D TestPolygon3()
{
var points = from x in new[] { "0.25,0", "0.5,1", "1,-1" } select Point2D.Parse(x);
return new Polygon2D(points);
}
private static Polygon2D TestPolygon4()
{
var points = from x in new[] { "0.5,1", "1,-1", "0.25,0" } select Point2D.Parse(x);
return new Polygon2D(points);
}
}
}

469
src/Numerics.Tests/Spatial/Euclidean2D/Vector2DTests.cs

@ -0,0 +1,469 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean2D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean2D
{
[TestFixture]
public class Vector2DTests
{
[Test]
public void Ctor()
{
var v = new Vector2D(1, 2);
Assert.AreEqual(1, v.X);
Assert.AreEqual(2, v.Y);
}
[TestCase("-1, -2", "1, 2", "0, 0")]
public void OperatorAdd(string v1s, string v2s, string evs)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v1 + v2);
Assert.AreEqual(expected, v2 + v1);
}
[TestCase("-1, -2", "1, 2", "-2, -4")]
public void OperatorSubtract(string v1s, string v2s, string evs)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v1 - v2);
}
[TestCase("-1, -2", "1, 2")]
public void OperatorNegate(string vs, string evs)
{
var v = Vector2D.Parse(vs);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, -v);
}
[TestCase("-1, -2", 2, "-2, -4")]
public void OperatorMultiply(string vs, double d, string evs)
{
var v = Vector2D.Parse(vs);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v * d);
Assert.AreEqual(expected, d * v);
}
[TestCase("-1, -2", 2, "-0.5, -1")]
public void OperatorDivide(string vs, double d, string evs)
{
var v = Vector2D.Parse(vs);
var actual = v / d;
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, actual);
}
[TestCase(5, "90 °", "0, 5")]
[TestCase(3, "-90 °", "0, -3")]
[TestCase(1, "45 °", "0.71, 0.71")]
[TestCase(1, "-45 °", "0.71, -0.71")]
[TestCase(1, "0 °", "1, 0")]
[TestCase(1, "180 °", "-1, 0")]
public void FromPolar(int radius, string avs, string eps)
{
var angle = Angle.Parse(avs);
var v = Vector2D.FromPolar(radius, angle);
var ep = Vector2D.Parse(eps);
AssertGeometry.AreEqual(ep, v, 1e-2);
}
[Test]
public void FromPolarFailsWhenNegativeRadius()
{
Assert.Throws<ArgumentOutOfRangeException>(() => Vector2D.FromPolar(-1.0, Angle.FromRadians(0)));
}
[Test]
public void OfVector()
{
var v = Vector2D.OfVector(Vector<double>.Build.Dense(new[] { 1.0, 2 }));
Assert.AreEqual(1, v.X);
Assert.AreEqual(2, v.Y);
Assert.Throws<ArgumentException>(() => Vector2D.OfVector(Vector<double>.Build.Dense(new[] { 1.0 })));
Assert.Throws<ArgumentException>(() => Vector2D.OfVector(Vector<double>.Build.Dense(new[] { 1.0, 2, 3 })));
}
[TestCase("-1,1", -1, 1)]
[TestCase("1, 2", 1, 2)]
[TestCase("1.2; 3.4", 1.2, 3.4)]
[TestCase("1.2;3.4", 1.2, 3.4)]
[TestCase("1.2 ; 3.4", 1.2, 3.4)]
[TestCase("1,2; 3,4", 1.2, 3.4)]
[TestCase("1.2, 3.4", 1.2, 3.4)]
[TestCase("1.2 3.4", 1.2, 3.4)]
[TestCase("1.2,\u00A03.4", 1.2, 3.4)]
[TestCase("1.2\u00A03.4", 1.2, 3.4)]
[TestCase("(1.2, 3.4)", 1.2, 3.4)]
[TestCase("1,2\u00A03,4", 1.2, 3.4)]
[TestCase("(.1, 2.3e-4)", 0.1, 0.00023000000000000001)]
public void Parse(string text, double expectedX, double expectedY)
{
Assert.AreEqual(true, Vector2D.TryParse(text, out var p));
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
p = Vector2D.Parse(text);
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
p = Vector2D.Parse(p.ToString());
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
}
[TestCase("1.2")]
[TestCase("1; 2; 3")]
public void ParseFails(string text)
{
Assert.AreEqual(false, Vector2D.TryParse(text, out _));
Assert.Throws<FormatException>(() => Vector2D.Parse(text));
}
[TestCase(@"<Vector2D X=""1"" Y=""2"" />")]
[TestCase(@"<Vector2D Y=""2"" X=""1""/>")]
[TestCase(@"<Vector2D><X>1</X><Y>2</Y></Vector2D>")]
[TestCase(@"<Vector2D><Y>2</Y><X>1</X></Vector2D>")]
public void ReadFrom(string xml)
{
var v = new Vector2D(1, 2);
AssertGeometry.AreEqual(v, Vector2D.ReadFrom(XmlReader.Create(new StringReader(xml))));
}
[TestCase("-1, -2", "1, 2", "0, 0")]
public void Add(string v1s, string v2s, string evs)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v1.Add(v2));
Assert.AreEqual(expected, v2.Add(v1));
}
[TestCase("-1, -2", "1, 2", "-2, -4")]
public void Subtract(string v1s, string v2s, string evs)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v1.Subtract(v2));
}
[TestCase("-1, -2", "1, 2")]
public void Negate(string vs, string evs)
{
var v = Vector2D.Parse(vs);
var expected = Vector2D.Parse(evs);
Assert.AreEqual(expected, v.Negate());
}
[TestCase("-1, -2", 2, "-2, -4")]
public void ScaleBy(string vs, double d, string evs)
{
var v = Vector2D.Parse(vs);
Assert.AreEqual(Vector2D.Parse(evs), v.ScaleBy(d));
}
[TestCase("2, 0", 2)]
[TestCase("-2, 0", 2)]
[TestCase("0, 2", 2)]
public void Length(string vs, double expected)
{
var v = Vector2D.Parse(vs);
Assert.AreEqual(expected, v.Length, 1e-6);
}
[Test]
public void ToDenseVector()
{
var v = new Vector2D(1, 2);
var actual = v.ToVector();
Assert.AreEqual(2, actual.Count);
Assert.AreEqual(1, actual[0]);
Assert.AreEqual(2, actual[1]);
}
[TestCase("1, 0", "1, 0", 1e-4, false)]
[TestCase("1, 0", "0, -1", 1e-4, true)]
[TestCase("1, 0", "0, 1", 1e-4, true)]
[TestCase("0, 1", "1, 0", 1e-4, true)]
[TestCase("0, 1", "0, 1", 1e-4, false)]
public void IsPerpendicularTo(string v1s, string v2s, double tol, bool expected)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
Assert.AreEqual(expected, v1.IsPerpendicularTo(v2, tol));
Assert.AreEqual(expected, v2.IsPerpendicularTo(v1, tol));
Assert.AreEqual(expected, v1.IsPerpendicularTo(v2, Angle.FromRadians(tol)));
Assert.AreEqual(expected, v2.IsPerpendicularTo(v1, Angle.FromRadians(tol)));
}
[TestCase("1, 0", "1, 0", 1e-10, true)]
[TestCase("1, 0", "-1, 0", 1e-10, true)]
[TestCase("1, 0", "1, 1", 1e-10, false)]
[TestCase("1, 1", "1, 1", 1e-10, true)]
[TestCase("1, -1", "-1, 1", 1e-10, true)]
[TestCase("1, 0.5", "-1, -0.5", 1e-10, true)]
[TestCase("1, 0.5", "1, 0.5001", 1e-10, false)]
[TestCase("1, 0.5", "1, 0.5001", 1e-8, true)] // Demonstration of the effect of tolerance
public void IsParallelToByDoubleTolerance(string v1s, string v2s, double tol, bool expected)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
Assert.AreEqual(expected, v1.IsParallelTo(v2, tol));
Assert.AreEqual(expected, v2.IsParallelTo(v1, tol));
}
[TestCase("1, 0", "1, 0", 1e-4, true)]
[TestCase("1, 0", "-1, 0", 1e-4, true)]
[TestCase("1, 0", "1, 1", 1e-4, false)]
[TestCase("1, 1", "1, 1", 1e-4, true)]
[TestCase("1, -1", "-1, 1", 1e-4, true)]
[TestCase("1, 0", "1, 0.001", 0.06, true)]
[TestCase("1, 0", "1, -0.001", 0.06, true)]
[TestCase("-1, 0", "1, 0.001", 0.06, true)]
[TestCase("-1, 0", "1, -0.001", 0.06, true)]
[TestCase("1, 0", "1, 0.001", 0.05, false)]
[TestCase("1, 0", "1, -0.001", 0.05, false)]
[TestCase("-1, 0", "1, 0.001", 0.05, false)]
[TestCase("-1, 0", "1, -0.001", 0.05, false)]
[TestCase("1, 0.5", "-1, -0.5", 1e-4, true)]
public void IsParallelToByAngleTolerance(string v1s, string v2s, double degreesTolerance, bool expected)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
Assert.AreEqual(expected, v1.IsParallelTo(v2, Angle.FromDegrees(degreesTolerance)));
Assert.AreEqual(expected, v2.IsParallelTo(v1, Angle.FromDegrees(degreesTolerance)));
}
[TestCase("1, 0", "90°", "0, 1")]
[TestCase("1, 0", "-270°", "0, 1")]
[TestCase("1, 0", "-90°", "0, -1")]
[TestCase("1, 0", "270°", "0, -1")]
[TestCase("1, 0", "180°", "-1, 0")]
[TestCase("1, 0", "0°", "1, 0")]
[TestCase("0, 1", "-90°", "1, 0")]
public void Rotate(string vs, string @as, string evs)
{
var v = Vector2D.Parse(vs);
var angle = Angle.Parse(@as);
var expected = Vector2D.Parse(evs);
AssertGeometry.AreEqual(expected, v.Rotate(angle), 0.01);
}
[TestCase("1, 2", "3, 4", 11)]
public void DotProduct(string vs, string evs, double expected)
{
var v1 = Vector2D.Parse(vs);
var v2 = Vector2D.Parse(evs);
Assert.AreEqual(expected, v1.DotProduct(v2));
}
[TestCase("2, 3", "0.55470019, 0.83205029")]
public void Normalize(string vs, string evs)
{
var v1 = Vector2D.Parse(vs);
var expected = Vector2D.Parse(evs);
AssertGeometry.AreEqual(expected, v1.Normalize());
}
[TestCase("1,0", "0,1", "270°", "-90°")]
[TestCase("0,1", "1,0", "90°", "90°")]
[TestCase("-0.99985, 0.01745", "-1, 0", "359°", "-1°")]
[TestCase("-0.99985, -0.01745", "-1, 0", "1°", "1°")]
[TestCase("0.99985, 0.01745", "1, 0", "1°", "1°")]
[TestCase("0.99985, -0.01745", "1, 0", "359°", "-1°")]
public void SignedAngleTo(string v1s, string v2s, string expectedClockWise, string expectedNegative)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var cw = v1.SignedAngleTo(v2, true);
var expected = Angle.Parse(expectedClockWise);
Assert.AreEqual(expected.Degrees, cw.Degrees, 1e-3);
var cwNeg = v1.SignedAngleTo(v2, true, true);
Assert.AreEqual(Angle.Parse(expectedNegative).Degrees, cwNeg.Degrees, 1e-3);
var ccw = v1.SignedAngleTo(v2, false);
Assert.AreEqual(360 - expected.Degrees, ccw.Degrees, 1e-3);
}
[TestCase("1, 0", "0, 1", false, 90)]
[TestCase("1, 0", "0, 1", true, 270)]
[TestCase("1, 0", "0, -1", true, 90)]
[TestCase("1, 0", "0, -1", false, 270)]
[TestCase("1, 0", "-1, 0", true, 180)]
[TestCase("1, 0", "-1, 0", false, 180)]
[TestCase("1, 0", "1, 0", false, 0)]
[TestCase("0, 1", "1, 0", true, 90)]
public void SignedAngleTo(string v1s, string v2s, bool clockWise, float expected)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var av = v1.SignedAngleTo(v2, clockWise);
Assert.AreEqual(expected, av.Degrees, 0.1);
}
[Test]
public void CheckCachedXAxis()
{
AssertGeometry.AreEqual(new Vector2D(1, 0), Vector2D.XAxis);
}
[Test]
public void CheckCachedYAxis()
{
AssertGeometry.AreEqual(new Vector2D(0, 1), Vector2D.YAxis);
}
[TestCase("1,0", "0,1", "90°")]
[TestCase("2,0", "0,3", "90°")]
[TestCase("1,0", "0,-1", "90°")]
[TestCase("0,1", "1,0", "90°")]
[TestCase("-0.99985, 0.01745", "-1, 0", "1°")]
[TestCase("-0.99985, -0.01745", "-1, 0", "1°")]
[TestCase("0.99985, 0.01745", "1, 0", "1°")]
[TestCase("0.99985, -0.01745", "1, 0", "1°")]
[TestCase("-0.99985, -0.01745", "1, 0", "179°")]
[TestCase("-0.99985, 0.01745", "1, 0", "179°")]
public void AngleTo(string v1s, string v2s, string expectedAngle)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var angle = v1.AngleTo(v2);
var expected = Angle.Parse(expectedAngle);
Assert.AreEqual(expected.Degrees, angle.Degrees, 1e-3);
angle = v2.AngleTo(v1);
Assert.AreEqual(expected.Degrees, angle.Degrees, 1e-3);
}
[TestCase("1,0", "0,1", 1)]
[TestCase("-1,0", "0,1", -1)]
[TestCase("0.5003,-0.7066", "0.0739,0.7981", 0.452)]
[TestCase("0.7097,0.6059", "0.0142,-0.7630", -0.550)]
[TestCase("-0.6864,0.7036", "-0.8541,-0.1124", 0.678)]
[TestCase("-0.2738,0.6783", "0.1695,0.9110", -0.364)]
public void CrossProducts(string v1s, string v2s, double expected)
{
// Generated test data from http://calculator.tutorvista.com/math/8/cross-product-calculator.html
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var cross = v1.CrossProduct(v2);
Assert.AreEqual(expected, cross, 1e-3);
}
[TestCase("1,0", "2,0", "1,0")]
[TestCase("1,1", "2,0", "1,0")]
[TestCase("1,0", "-2,0", "1,0")]
[TestCase("-1,1", "2,0", "-1,0")]
[TestCase("-0.0563,-0.2904", "-0.3671,-0.7945", "-0.120,-0.261")]
[TestCase("0.4610,0.9067", "-0.7948,-0.7748", "0.690,0.672")]
[TestCase("0.3916,-0.9644", "-0.0873,0.0978", "0.653,-0.731")]
public void VectorProjection(string v1s, string v2s, string exs)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
var ex = Vector2D.Parse(exs);
AssertGeometry.AreEqual(ex, v1.ProjectOn(v2), 1e-3);
}
[TestCase("1, 0", "1, 0", 1e-4, true)]
[TestCase("-1, 1", "-1, 1", 1e-4, true)]
[TestCase("1, 0", "1, 1", 1e-4, false)]
public void Equals(string v1s, string v2s, double tol, bool expected)
{
var v1 = Vector2D.Parse(v1s);
var v2 = Vector2D.Parse(v2s);
Assert.AreEqual(expected, v1 == v2);
Assert.AreEqual(expected, v2 == v1);
Assert.AreNotEqual(expected, v1 != v2);
Assert.AreNotEqual(expected, v2 != v1);
Assert.AreEqual(expected, v1.Equals(v2));
Assert.AreEqual(expected, v1.Equals((object)v2));
Assert.AreEqual(expected, Equals(v1, v2));
Assert.AreEqual(expected, v1.Equals(v2, tol));
Assert.IsFalse(default(Vector2D).Equals(null));
}
[Test]
public void EqualsFailsWhenNegativeTolerance()
{
var v1 = new Vector2D(0, 0);
var v2 = new Vector2D(1, 1);
Assert.Throws<ArgumentException>(() => v1.Equals(v2, -0.01));
}
[TestCase("-2, 0", null, "(-2,\u00A00)")]
[TestCase("-2, 0", "N2", "(-2.00,\u00A00.00)")]
public void ToString(string vs, string format, string expected)
{
var v = Vector2D.Parse(vs);
var actual = v.ToString(format);
Assert.AreEqual(expected, actual);
Assert.AreEqual(v, Vector2D.Parse(actual));
}
[Test]
public void XmlRoundtrip()
{
var v = new Vector2D(1, 2);
AssertXml.XmlRoundTrips(v, @"<Vector2D X=""1"" Y=""2"" />", (e, a) => AssertGeometry.AreEqual(e, a));
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<Vector2D>
{
Value1 = new Vector2D(1, 2),
Value2 = new Vector2D(3, 4)
};
var expected = "<ContainerOfVector2D>\r\n" +
" <Value1 X=\"1\" Y=\"2\"></Value1>\r\n" +
" <Value2 X=\"3\" Y=\"4\"></Value2>\r\n" +
"</ContainerOfVector2D>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
AssertGeometry.AreEqual(container.Value1, roundTrip.Value1);
AssertGeometry.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void XmlElements()
{
var v = new Vector2D(1, 2);
var serializer = new XmlSerializer(typeof(Vector2D));
AssertGeometry.AreEqual(v, (Vector2D)serializer.Deserialize(new StringReader(@"<Vector2D><X>1</X><Y>2</Y></Vector2D>")));
}
[Test]
public void XmlContainerElements()
{
var xml = "<ContainerOfVector2D>\r\n" +
" <Value1><X>1</X><Y>2</Y></Value1>\r\n" +
" <Value2><X>3</X><Y>4</Y></Value2>\r\n" +
"</ContainerOfVector2D>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<Vector2D>));
var deserialized = (AssertXml.Container<Vector2D>)serializer.Deserialize(new StringReader(xml));
AssertGeometry.AreEqual(new Vector2D(1, 2), deserialized.Value1);
AssertGeometry.AreEqual(new Vector2D(3, 4), deserialized.Value2);
}
}
}

69
src/Numerics.Tests/Spatial/Euclidean3D/Circle3DTests.cs

@ -0,0 +1,69 @@
// ReSharper disable InconsistentNaming
using System;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Circle3DTests
{
[TestCase("0, 0, 0", 2.5)]
[TestCase("2, -4, 0", 4.7)]
public void CircleCenterRadius(string p1s, double radius)
{
var center = Point3D.Parse(p1s);
var circle = new Circle3D(center, UnitVector3D.ZAxis, radius);
Assert.AreEqual(2 * radius, circle.Diameter, double.Epsilon);
Assert.AreEqual(2 * Math.PI * radius, circle.Circumference, double.Epsilon);
Assert.AreEqual(Math.PI * radius * radius, circle.Area, double.Epsilon);
}
[TestCase("0,0,0", "5,0,0", "2.5,0,0", 2.5)]
[TestCase("23.56,15.241,0", "62.15,-12.984,0", "42.8550,1.1285,0", 23.90522289)]
public void Circle2Points(string p1s, string p2s, string centers, double radius)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var circle3D = Circle3D.FromPointsAndAxis(p1, p2, UnitVector3D.ZAxis);
AssertGeometry.AreEqual(circle3D.CenterPoint, Point3D.Parse(centers));
Assert.AreEqual(circle3D.Radius, radius, 1e-6);
}
// Test cases randomly generated from GOM Inspect Professional v8
[TestCase("-1,0,0", "0,1,0", "1,0,0", "0,0,0", 1)]
[TestCase("3.5184432,0.3072766,1.5733767", "2.6311562,3.8537550,4.1648900", "1.7918408,3.3085796,0.1773063", "2.7223188,2.4699561,2.2155024", 2.3923465)]
[TestCase("3.9940300,4.7087055,3.0356712", "1.7448842,3.5524049,2.9346235", "3.3923391,0.6820635,2.8792665", "3.6015409,2.7091562,2.9554706", 2.0392835)]
[TestCase("1.1572695,1.7427232,3.8991802", "3.8499789,2.6866347,1.1215183", "4.4666442,3.1640346,2.6081508", "2.6534515,2.3079965,2.6873057", 2.0066725)]
[TestCase("2.8983684,2.6350014,3.6266714", "3.9347082,1.2248094,3.9586336", "1.3540560,4.4935097,4.4614937", "2.5949041,2.2684548,7.7958547", 4.1962526)]
[TestCase("3.2522525,3.4579030,2.5739476", "2.5922429,2.5575788,4.4536494", "0.9654999,3.9766348,1.5029361", "1.4925488,3.2060788,3.1067942", 1.8557743)]
[TestCase("1.4907420,4.4495954,1.2951978", "2.3796522,3.6326081,0.4917802", "3.6332795,0.3779172,3.4856655", "2.3105721,2.5275616,2.8479118", 2.6033163)]
[TestCase("4.0537333,0.7137006,1.8633552", "0.7591453,2.7025442,4.5177595", "2.4149884,2.4450949,0.7413665", "2.3306759,1.8283553,3.0064357", 2.3490455)]
[TestCase("2.3488295,3.4356098,2.3024682", "2.0004679,2.0740716,4.8011107", "4.1253165,3.8784045,1.5045145", "4.3383477,2.6362129,3.7888115", 2.6089145)]
[TestCase("2.7730017,4.7353519,1.6764519", "4.4640538,3.8917330,1.7939499", "2.9657457,0.7421344,0.7106418", "2.8705245,2.7387444,1.1937714", 2.0564369)]
[TestCase("0.3309697,1.4510555,4.3876068", "3.3591227,3.2904666,1.6724443", "0.6636883,0.8606021,3.9895073", "1.7839226,2.6021881,3.1186384", 2.2464326)]
public void CircleFromThreePoints(string p1s, string p2s, string p3s, string centers, double radius)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var p3 = Point3D.Parse(p3s);
var center = Point3D.Parse(centers);
var circle = Circle3D.FromPoints(p1, p2, p3);
AssertGeometry.AreEqual(center, circle.CenterPoint);
Assert.AreEqual(radius, circle.Radius, 1e-6);
}
[Test]
public void CircleFromThreePointsArgumentException()
{
var p1 = new Point3D(0, 0, 0);
var p2 = new Point3D(-1, 0, 0);
var p3 = new Point3D(1, 0, 0);
Assert.Throws<InvalidOperationException>(() => Circle3D.FromPoints(p1, p2, p3));
}
}
}

311
src/Numerics.Tests/Spatial/Euclidean3D/CoordinateSystem3DTests.cs

@ -0,0 +1,311 @@
using System;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
// ReSharper disable InconsistentNaming
[TestFixture]
public class CoordinateSystem3DTests
{
private const string X = "1; 0 ; 0";
private const string Y = "0; 1; 0";
private const string Z = "0; 0; 1";
private const string NegativeX = "-1; 0; 0";
private const string NegativeY = "0; -1; 0";
private const string NegativeZ = "0; 0; -1";
[TestCase("1, 2, 3", "4, 5, 6", "7, 8, 9", "-1, -2, -3")]
public void ConstructorTest(string ps, string xs, string ys, string zs)
{
var origin = Point3D.Parse(ps);
var xAxis = Vector3D.Parse(xs);
var yAxis = Vector3D.Parse(ys);
var zAxis = Vector3D.Parse(zs);
var css = new[]
{
new CoordinateSystem3D(origin, xAxis, yAxis, zAxis),
new CoordinateSystem3D(xAxis, yAxis, zAxis, origin)
};
foreach (var cs in css)
{
AssertGeometry.AreEqual(origin, cs.Origin);
AssertGeometry.AreEqual(xAxis, cs.XAxis);
AssertGeometry.AreEqual(yAxis, cs.YAxis);
AssertGeometry.AreEqual(zAxis, cs.ZAxis);
}
}
[TestCase("o:{1, 2e-6, -3} x:{1, 2, 3} y:{3, 3, 3} z:{4, 4, 4}", "1, 2e-6, -3", "1, 2, 3", "3, 3, 3", "4, 4, 4")]
public void ParseTests(string s, string ops, string xs, string ys, string zs)
{
var cs = CoordinateSystem3D.Parse(s);
AssertGeometry.AreEqual(Point3D.Parse(ops), cs.Origin);
AssertGeometry.AreEqual(Vector3D.Parse(xs), cs.XAxis);
AssertGeometry.AreEqual(Vector3D.Parse(ys), cs.YAxis);
AssertGeometry.AreEqual(Vector3D.Parse(zs), cs.ZAxis);
}
[TestCase("1, 2, 3", "90°", "0, 0, 1", "-2, 1, 3")]
[TestCase("1, 2, 3", "90°", "0, 0, -1", "2, -1, 3")]
[TestCase("1, 2, 3", "-90°", "0, 0, 1", "2, -1, 3")]
[TestCase("1, 2, 3", "180°", "0, 0, 1", "-1, -2, 3")]
[TestCase("1, 2, 3", "270°", "0, 0, 1", "2, -1, 3")]
[TestCase("1, 2, 3", "90°", "1, 0, 0", "1, -3, 2")]
[TestCase("1, 2, 3", "-90°", "1, 0, 0", "1, 3, -2")]
[TestCase("1, 2, 3", "90°", "-1, 0, 0", "1, 3, -2")]
[TestCase("1, 2, 3", "90°", "0, 1, 0", "3, 2, -1")]
[TestCase("1, 2, 3", "-90°", "0, 1, 0", "-3, 2, 1")]
public void RotationAroundVector(string ps, string @as, string vs, string eps)
{
var p = Point3D.Parse(ps);
var angle = Angle.Parse(@as);
var coordinateSystems = new[]
{
CoordinateSystem3D.Rotation(angle, UnitVector3D.Parse(vs)),
CoordinateSystem3D.Rotation(angle, Vector3D.Parse(vs)),
};
var expected = Point3D.Parse(eps);
foreach (var coordinateSystem in coordinateSystems)
{
var rotatedPoint = coordinateSystem.Transform(p);
AssertGeometry.AreEqual(expected, rotatedPoint);
}
}
[TestCase("0°", "0°", "0°", "1, 2, 3", "1, 2, 3")]
[TestCase("90°", "0°", "0°", "1, 2, 3", "-2, 1, 3")]
[TestCase("-90°", "0°", "0°", "1, 2, 3", "2, -1, 3")]
[TestCase("0°", "90°", "0°", "1, 2, 3", "3, 2, -1")]
[TestCase("0°", "-90°", "0°", "1, 2, 3", "-3, 2, 1")]
[TestCase("0°", "0°", "90°", "1, 2, 3", "1, -3, 2")]
[TestCase("0°", "0°", "-90°", "1, 2, 3", "1, 3, -2")]
public void RotationYawPitchRoll(string yaws, string pitchs, string rolls, string ps, string eps)
{
var p = Point3D.Parse(ps);
var yaw = Angle.Parse(yaws);
var pitch = Angle.Parse(pitchs);
var roll = Angle.Parse(rolls);
var coordinateSystems = new[]
{
CoordinateSystem3D.Rotation(yaw, pitch, roll),
};
var expected = Point3D.Parse(eps);
foreach (var coordinateSystem in coordinateSystems)
{
var rotatedPoint = coordinateSystem.Transform(p);
AssertGeometry.AreEqual(expected, rotatedPoint);
}
}
[TestCase("1, 2, 3", "0, 0, 1", "1, 2, 4")]
[TestCase("1, 2, 3", "0, 0, -1", "1, 2, 2")]
[TestCase("1, 2, 3", "0, 0, 0", "1, 2, 3")]
[TestCase("1, 2, 3", "0, 1, 0", "1, 3, 3")]
[TestCase("1, 2, 3", "0, -1, 0", "1, 1, 3")]
[TestCase("1, 2, 3", "1, 0, 0", "2, 2, 3")]
[TestCase("1, 2, 3", "-1, 0, 0", "0, 2, 3")]
public void Translation(string ps, string vs, string eps)
{
var p = Point3D.Parse(ps);
var cs = CoordinateSystem3D.Translation(Vector3D.Parse(vs));
var tp = cs.Transform(p);
Console.WriteLine(cs.ToString());
AssertGeometry.AreEqual(Point3D.Parse(eps), tp);
}
[TestCase(X, X, null)]
[TestCase(X, X, X)]
[TestCase(X, X, Y)]
[TestCase(X, X, Z)]
[TestCase(X, NegativeX, null)]
[TestCase(X, NegativeX, Z)]
[TestCase(X, NegativeX, Y)]
[TestCase(X, Y, null)]
[TestCase(X, Z, null)]
[TestCase(Y, Y, null)]
[TestCase(Y, Y, X)]
[TestCase(Y, NegativeY, null)]
[TestCase(Y, NegativeY, X)]
[TestCase(Y, NegativeY, Z)]
[TestCase(Z, NegativeZ, null)]
[TestCase(Z, NegativeZ, X)]
[TestCase(Z, NegativeZ, Y)]
[TestCase("1, 2, 3", "-1, 0, -1", null)]
public void RotateToTest(string v1s, string v2s, string @as)
{
var axis = string.IsNullOrEmpty(@as) ? (UnitVector3D?)null : Vector3D.Parse(@as).Normalize();
var v1 = Vector3D.Parse(v1s).Normalize();
var v2 = Vector3D.Parse(v2s).Normalize();
var actual = CoordinateSystem3D.RotateTo(v1, v2, axis);
Console.WriteLine(actual);
var rv = actual.Transform(v1);
AssertGeometry.AreEqual(v2, rv);
actual = CoordinateSystem3D.RotateTo(v2, v1, axis);
rv = actual.Transform(v2);
AssertGeometry.AreEqual(v1, rv);
}
[Test]
public void InvertTest()
{
Assert.Inconclusive("Test this?");
}
[Test]
public void EqualityNullOperator()
{
string test = "o:{1, 2e-6, -3} x:{1, 2, 3} y:{3, 3, 3} z:{4, 4, 4}";
var cs = CoordinateSystem3D.Parse(test);
Assert.IsFalse(cs == null);
}
[Test]
public void EqualityNullOperatorTrue()
{
CoordinateSystem3D cs = null;
Assert.IsTrue(cs == null);
}
[Test]
public void EqualityNotNullOperator()
{
string test = "o:{1, 2e-6, -3} x:{1, 2, 3} y:{3, 3, 3} z:{4, 4, 4}";
var cs = CoordinateSystem3D.Parse(test);
Assert.IsTrue(cs != null);
}
[Test]
public void EqualityNotNullOperatorFalse()
{
CoordinateSystem3D cs = null;
Assert.IsFalse(cs != null);
}
[Test]
public void EqualityNull()
{
string test = "o:{1, 2e-6, -3} x:{1, 2, 3} y:{3, 3, 3} z:{4, 4, 4}";
var cs = CoordinateSystem3D.Parse(test);
Assert.IsFalse(cs.Equals(null));
}
[TestCase("1; -5; 3", "1; -5; 3", "o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
public void TransformPoint(string ps, string eps, string css)
{
var p = Point3D.Parse(ps);
var cs = CoordinateSystem3D.Parse(css);
var actual = p.TransformBy(cs);
var expected = Point3D.Parse(eps);
AssertGeometry.AreEqual(expected, actual, float.Epsilon);
}
[TestCase("1; 2; 3", "1; 2; 3", "o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
[TestCase("1; 2; 3", "1; 2; 3", "o:{3, 4, 5} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
public void TransformVector(string vs, string evs, string css)
{
var v = Vector3D.Parse(vs);
var cs = CoordinateSystem3D.Parse(css);
var actual = cs.Transform(v);
var expected = Vector3D.Parse(evs);
AssertGeometry.AreEqual(expected, actual);
}
[Test]
public void TransformUnitVector()
{
var cs = CoordinateSystem3D.Rotation(Angle.FromDegrees(90), UnitVector3D.ZAxis);
var uv = UnitVector3D.XAxis;
var actual = cs.Transform(uv);
AssertGeometry.AreEqual(UnitVector3D.YAxis, actual);
}
[TestCase("o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}", "o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
[TestCase("o:{0, 0, 0} x:{10, 0, 0} y:{0, 1, 0} z:{0, 0, 1}", "o:{1, 0, 0} x:{0.1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
[TestCase("o:{0, 0, 0} x:{10, 0, 0} y:{0, 1, 0} z:{0, 0, 1}", "o:{1, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
[TestCase("o:{1, 2, -7} x:{10, 0, 0} y:{0, 1, 0} z:{0, 0, 1}", "o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
[TestCase("o:{1, 2, -7} x:{10, 0.1, 0} y:{0, 1.2, 0.1} z:{0.1, 0, 1}", "o:{2, 5, 1} x:{0.1, 2, 0} y:{0.2, -1, 0} z:{0, 0.4, 1}")]
public void SetToAlignCoordinateSystemsTest(string fcss, string tcss)
{
var fcs = CoordinateSystem3D.Parse(fcss);
var tcs = CoordinateSystem3D.Parse(tcss);
var css = new[]
{
CoordinateSystem3D.SetToAlignCoordinateSystems(fcs.Origin, fcs.XAxis, fcs.YAxis, fcs.ZAxis, tcs.Origin, tcs.XAxis, tcs.YAxis, tcs.ZAxis),
CoordinateSystem3D.CreateMappingCoordinateSystem(fcs, tcs)
};
foreach (var cs in css)
{
var aligned = cs.Transform(fcs);
AssertGeometry.AreEqual(tcs.Origin, aligned.Origin);
AssertGeometry.AreEqual(tcs.XAxis, aligned.XAxis);
AssertGeometry.AreEqual(tcs.YAxis, aligned.YAxis);
AssertGeometry.AreEqual(tcs.ZAxis, aligned.ZAxis);
}
}
[TestCase(X, Y, Z)]
[TestCase(NegativeX, Y, Z)]
[TestCase(NegativeX, Y, null)]
[TestCase(X, Y, null)]
[TestCase(X, Y, "0,0,1")]
[TestCase("1,-1, 1", "0, 1, 1", null)]
[TestCase(X, Z, Y)]
public void SetToRotateToTest(string vs, string vts, string axisString)
{
var v = UnitVector3D.Parse(vs, tolerance: 1);
var vt = UnitVector3D.Parse(vts, tolerance: 1);
UnitVector3D? axis = null;
if (axisString != null)
{
axis = UnitVector3D.Parse(axisString);
}
var cs = CoordinateSystem3D.RotateTo(v, vt, axis);
var rv = cs.Transform(v);
AssertGeometry.AreEqual(vt, rv);
var invert = cs.Invert();
var rotateBack = invert.Transform(rv);
AssertGeometry.AreEqual(v, rotateBack);
cs = CoordinateSystem3D.RotateTo(vt, v, axis);
rotateBack = cs.Transform(rv);
AssertGeometry.AreEqual(v, rotateBack);
}
[TestCase("o:{1, 2, -7} x:{10, 0, 0} y:{0, 1, 0} z:{0, 0, 1}", "o:{0, 0, 0} x:{1, 0, 0} y:{0, 1, 0} z:{0, 0, 1}")]
public void Transform(string cs1s, string cs2s)
{
var cs1 = CoordinateSystem3D.Parse(cs1s);
var cs2 = CoordinateSystem3D.Parse(cs2s);
var actual = cs1.Transform(cs2);
var expected = new CoordinateSystem3D(cs1.Multiply(cs2));
AssertGeometry.AreEqual(expected, actual);
}
[Test]
public void XmlRoundTrips()
{
var cs = new CoordinateSystem3D(new Point3D(1, -2, 3), new Vector3D(0, 1, 0), new Vector3D(0, 0, 1), new Vector3D(1, 0, 0));
string expected = @"
<CoordinateSystem3D>
<Origin X=""1"" Y=""-2"" Z=""3"" />
<XAxis X=""0"" Y=""1"" Z=""0"" />
<YAxis X=""0"" Y=""0"" Z=""1"" />
<ZAxis X=""1"" Y=""0"" Z=""0"" />
</CoordinateSystem3D>";
AssertXml.XmlRoundTrips(cs, expected, (e, a) => AssertGeometry.AreEqual(e, a));
}
}
}

198
src/Numerics.Tests/Spatial/Euclidean3D/Line3DTests.cs

@ -0,0 +1,198 @@
using System;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Line3DTests
{
[Test]
public void Ctor()
{
Assert.Throws<ArgumentException>(() => new Line3D(Point3D.Origin, Point3D.Origin));
}
[TestCase("0, 0, 0", "1, -1, 1", "1, -1, 1")]
public void DirectionsTest(string p1s, string p2s, string evs)
{
var l = Line3D.Parse(p1s, p2s);
var excpected = UnitVector3D.Parse(evs, tolerance: 1);
AssertGeometry.AreEqual(excpected, l.Direction);
}
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "1, 0, 0", "0, 0, 0", "0, -1, 1")]
public void ProjectOn(string p1s, string p2s, string rootPoint, string unitVector, string ep1s, string ep2s)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var line = new Line3D(p1, p2);
var plane = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
var expected = new Line3D(Point3D.Parse(ep1s), Point3D.Parse(ep2s));
AssertGeometry.AreEqual(expected, line.ProjectOn(plane));
}
[TestCase("0, 0, 0", "1, -2, 3", 3.741657)]
public void Length(string p1s, string p2s, double expected)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var l = new Line3D(p1, p2);
Assert.AreEqual(expected, l.Length, 1e-6);
}
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "1, -1, 1", true)]
[TestCase("0, 0, 2", "1, -1, 1", "0, 0, 0", "1, -1, 1", false)]
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "2, -1, 1", false)]
public void Equals(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var line1 = new Line3D(Point3D.Parse(p1s), Point3D.Parse(p2s));
var line2 = new Line3D(Point3D.Parse(p3s), Point3D.Parse(p4s));
Assert.AreEqual(expected, line1.Equals(line2));
Assert.AreEqual(expected, line1 == line2);
Assert.AreEqual(!expected, line1 != line2);
}
[TestCase("0, 0, 0", "1, 0, 0", "0.5, 1, 0", true, "0.5, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "0.5, 1, 0", false, "0.5, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "2, 1, 0", true, "1, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "2, 1, 0", false, "2, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "-2, 1, 0", true, "0, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "-2, 1, 0", false, "-2, 0, 0")]
public void LineToTest(string p1s, string p2s, string ps, bool mustStartFromLine, string sps)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var l = new Line3D(p1, p2);
var p = Point3D.Parse(ps);
var actual = l.LineTo(p, mustStartFromLine);
AssertGeometry.AreEqual(Point3D.Parse(sps), actual.StartPoint, 1e-6);
AssertGeometry.AreEqual(p, actual.EndPoint, 1e-6);
}
[TestCase("1, 2, 3", "4, 5, 6", @"<Line3D><StartPoint X=""1"" Y=""2"" Z=""3"" /><EndPoint X=""4"" Y=""5"" Z=""6"" /></Line3D>")]
public void XmlTests(string p1s, string p2s, string xml)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var l = new Line3D(p1, p2);
AssertXml.XmlRoundTrips(l, xml, (e, a) => AssertGeometry.AreEqual(e, a));
}
[TestCase("0,0,0", "0,0,1", "0,0,0", "0,0,0", Description = "Start point")]
[TestCase("0,0,0", "0,0,1", "0,0,1", "0,0,1", Description = "End point")]
[TestCase("0,0,0", "0,0,1", "1,0,.25", "0,0,.25")]
[TestCase("0,0,0", "0,0,1", "0,0,-1", "0,0,0")]
[TestCase("0,0,0", "0,0,1", "0,0,3", "0,0,1")]
public void ClosestPointToWithinSegment(string start, string end, string point, string expected)
{
var line = Line3D.Parse(start, end);
var p = Point3D.Parse(point);
var e = Point3D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p, true));
}
[TestCase("0,0,0", "0,0,1", "0,0,0", "0,0,0", Description = "Start point")]
[TestCase("0,0,0", "0,0,1", "0,0,1", "0,0,1", Description = "End point")]
[TestCase("0,0,0", "0,0,1", "1,0,.25", "0,0,.25")]
[TestCase("0,0,0", "0,0,1", "0,0,-1", "0,0,-1")]
[TestCase("0,0,0", "0,0,1", "0,0,3", "0,0,3")]
public void ClosestPointToOutsideSegment(string start, string end, string point, string expected)
{
var line = Line3D.Parse(start, end);
var p = Point3D.Parse(point);
var e = Point3D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p, false));
}
[TestCase("0,0,0", "0,0,1", "0,1,1", "0,1,2", true)]
[TestCase("0,0,0", "0,0,-1", "0,1,1", "0,1,2", true)]
[TestCase("0,0,0", "0,0.5,-1", "0,1,1", "0,1,2", false)]
[TestCase("0,0,0", "0,0.00001,-1.0000", "0,1,1", "0,1,2", false)]
public void IsParallelToWithinDoubleTol(string s1, string e1, string s2, string e2, bool expected)
{
var line1 = Line3D.Parse(s1, e1);
var line2 = Line3D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2));
}
[TestCase("0,0,0", "0,0,1", "0,1,1", "0,1,2", 0.01, true)]
[TestCase("0,0,0", "0,0,-1", "0,1,1", "0,1,2", 0.01, true)]
[TestCase("0,0,0", "0,0.5,-1", "0,1,1", "0,1,2", 0.01, false)]
[TestCase("0,0,0", "0,0.001,-1.0000", "0,1,1", "0,1,2", 0.05, false)]
[TestCase("0,0,0", "0,0.001,-1.0000", "0,1,1", "0,1,2", 0.06, true)]
public void IsParallelToWithinAngleTol(string s1, string e1, string s2, string e2, double degreesTol, bool expected)
{
var line1 = Line3D.Parse(s1, e1);
var line2 = Line3D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2, Angle.FromDegrees(degreesTol)));
}
[TestCase("0,0,0", "1,0,0", "1,1,1", "2,1,1", "0,0,0", "0,1,1")] // Parallel case
[TestCase("0,0,0", "1,0,0", "2,-1,0", "2,0,0", "2,0,0", "2,0,0")] // Intersecting case
[TestCase("0.3097538,3.0725982,3.9317042", "1.3945620,6.4927958,2.1094821", "2.4486204,5.1947760,6.3369721", "8.4010954,9.5691708,5.1665254", "-0.1891954,1.4995044,4.7698213", "-0.7471721,2.8462306,6.9653670")] // Randomly generated in GOM Inspect Professional V8
[TestCase("6.7042836,5.1490163,0.3655590", "7.6915457,0.8511235,0.8627290", "0.6890053,6.6207933,3.4147472", "5.6116149,1.7160727,5.4461589", "7.5505158,1.4650754,0.7917085", "5.7356234,1.5925149,5.4973334")] // Randomly generated in GOM Inspect Professional V8
[TestCase("2.9663973,7.1338954,9.7732130", "6.4625826,8.7716408,3.7015737", "1.8924447,4.9060507,1.8755412", "1.9542505,5.8440396,8.2251863", "1.8254726,6.5994432,11.7545962", "1.9889190,6.3701828,11.7868723")] // Randomly generated in GOM Inspect Professional V8
[TestCase("7.3107741,0.0835261,3.0516366", "1.0013621,9.6730070,3.6824080", "3.2195097,0.8726049,1.0448256", "4.5620367,8.6714351,3.3311693", "3.7856774,5.4412119,3.4040514", "4.0462563,5.6752319,2.4527875")] // Randomly generated in GOM Inspect Professional V8
[TestCase("6.8171425,2.0728553,1.9235196", "3.0360117,0.0371047,0.5410118", "0.4798755,2.8536110,0.4402877", "9.4038162,7.5597521,0.3068954", "2.9867513,0.0105830,0.5230005", "1.2671009,3.2687632,0.4285205")] // Randomly generated in GOM Inspect Professional V8
[TestCase("0.9826196,9.8736629,3.7966845", "3.9607685,8.7036678,0.3921888", "0.2333178,4.9282166,5.6478261", "5.8182917,0.7224496,6.8152397", "-0.7871493,10.5689341,5.8198105", "-3.0149659,7.3743380,4.9688451")] // Randomly generated in GOM Inspect Professional V8
[TestCase("3.1369768,8.4887731,0.1371429", "4.9427402,3.5584831,9.4250139", "1.0628142,7.4582519,3.7246752", "3.7045709,0.1811190,4.7211734", "3.8813087,6.4565178,3.9655839", "1.7121301,5.6696095,3.9696039")] // Randomly generated in GOM Inspect Professional V8
[TestCase("8.7598151,0.2378638,3.0353259", "0.5266014,9.6548588,2.7893143", "1.8918146,3.4742242,7.0646539", "7.3796941,7.0882850,4.4899767", "4.5791946,5.0195789,2.9104073", "5.3106500,5.7257093,5.4606833")] // Randomly generated in GOM Inspect Professional V8
[TestCase("7.6132801,6.8593303,3.7729401", "7.7434088,2.0184714,6.0037938", "4.1910423,0.6022826,6.7846734", "1.6554785,3.0341358,8.7351072", "7.8041730,-0.2419887,7.0455007", "5.8721457,-1.0100597,5.4915169")] // Randomly generated in GOM Inspect Professional V8
[TestCase("0.3818483,0.4893437,8.1438438", "4.6619509,7.8881811,4.7600744", "0.7792658,4.6454081,8.3927247", "0.2002105,7.4120903,3.5542593", "2.2611504,3.7380159,6.6581026", "0.6866122,5.0880999,7.6185307")] // Randomly generated in GOM Inspect Professional V8
public void ClosestPointsBetween(string s1, string e1, string s2, string e2, string cp1, string cp2)
{
var l1 = Line3D.Parse(s1, e1);
var l2 = Line3D.Parse(s2, e2);
var result = l1.ClosestPointsBetween(l2);
AssertGeometry.AreEqual(Point3D.Parse(cp1), result.Item1);
AssertGeometry.AreEqual(Point3D.Parse(cp2), result.Item2);
}
[TestCase("0,0,0", "1,0,0", "0.5,1,0", "1.5,1,0", "1,0,0", "1,1,0")] // Parallel case
[TestCase("0,0,0", "1,0,0", "3,1,0", "3,2,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("1,0,0", "0,0,0", "3,1,0", "3,2,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("0,0,0", "1,0,0", "3,2,0", "3,1,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("1,0,0", "0,0,0", "3,2,0", "3,1,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("5.6925969,1.3884847,7.1713834", "5.1573193,9.7184415,0.8644498", "0.6567836,8.3850115,1.5273528", "7.1182449,9.0049546,9.1872098", "5.3056396,7.4102899,2.6120410", "3.0759605,8.6171187,4.3952101")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("3.0803549,8.1101503,4.2072541", "0.9167489,5.4057168,0.0942629", "3.6443155,1.9841677,2.1280020", "4.0865344,8.8738039,9.2944797", "3.0803549,8.1101503,4.2072541", "3.8982350,5.9401568,6.2429519")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("2.0809966,9.3100446,9.4661138", "6.0883386,5.5240161,2.7490910", "8.4523738,2.6004881,8.8473518", "5.5868380,5.4932213,1.1649868", "6.0883386,5.5240161,2.7490910", "6.0992239,4.9759722,2.5386692")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("6.6149320,7.8081445,4.6267089", "5.3733678,7.5372568,0.4121304", "7.9879025,7.5486791,5.8931379", "1.4971100,6.2860737,3.2138409", "6.6149320,7.8081445,4.6267089", "6.4606595,7.2515959,5.2627161")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("4.7238306,4.7424963,7.9590086", "9.2276709,8.3299427,1.0349775", "7.3828132,6.3559129,8.7078245", "4.6487651,2.8181310,8.5972384", "4.7238306,4.7424963,7.9590086", "5.5976910,4.0460147,8.6356203")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("7.1035997,1.9299120,3.4688193", "8.5433252,5.8883905,9.7941707", "3.3053692,6.3729100,3.5626868", "8.4883669,8.1557493,7.2000211", "8.3560381,5.3734507,8.9713356", "8.4883669,8.1557493,7.2000211")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("7.4520661,5.7569419,4.1686608", "4.2367431,3.5840889,2.7405165", "1.3188110,5.7542366,2.7702002", "7.7144529,5.0792324,0.2292819", "4.7448074,3.9274289,2.9661826", "4.3479012,5.4345425,1.5667759")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("7.5543831,9.7934598,9.5348209", "6.5205418,0.3092162,8.7907210", "0.4877286,0.3419443,2.5644342", "8.6578287,6.1098998,5.8827401", "7.1211660,5.8192172,9.2230160", "8.4262398,5.9464019,5.7886797")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.0543887,5.4347267,4.3429352", "4.2117265,4.7630853,1.3218313", "8.9537706,1.5933994,0.6307145", "2.7936886,7.2837201,1.8965656", "4.5061578,4.8704041,1.8045609", "4.8831652,5.3535848,1.4671937")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.5771629,2.8557455,9.8087521", "5.4359776,8.6172816,5.7508093", "7.8904712,4.3099454,2.4107493", "8.1402295,0.9894932,3.8694855", "5.7508984,7.0273316,6.8706367", "7.8904712,4.3099454,2.4107493")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("1.1294829,5.4847586,8.1420946", "7.2489863,0.3206420,0.8259188", "5.8457205,6.7040761,3.0411085", "8.9017428,0.9704561,3.8471667", "5.7071649,1.6217517,2.6692442", "7.8717570,2.9028855,3.5754970")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("0.8765316,4.0262533,7.1988316", "4.3383388,7.0189039,1.4430865", "1.9687345,6.4066677,1.9041603", "0.6533722,5.3636394,1.3877450", "3.5259020,6.3165714,2.7938779", "1.9687345,6.4066677,1.9041603")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("0.3580051,9.3271145,8.5768069", "2.3779489,4.3772771,1.6443451", "1.0661185,9.0362165,3.9415240", "7.6388414,1.7341324,0.4120660", "1.8279811,5.7249636,3.5318384", "2.9136360,6.9836839,2.9494336")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.1264627,6.5812576,3.9222538", "1.5068838,5.6589456,0.0446154", "0.8368111,1.7808290,7.1053143", "8.6670395,3.8812950,1.4077528", "5.4376749,6.4437392,3.3440907", "6.1998837,3.2194782,3.2029458")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("5.2548631,0.4534686,7.9484333", "2.8554822,5.3465480,5.8755276", "0.3950940,1.3010814,5.5699850", "6.0302455,6.0816738,9.9995164", "3.7687405,3.4841320,6.6645221", "2.9986425,3.5098071,7.6165137")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("4.7098248,1.2821556,4.9827025", "6.4808992,9.9281018,2.2789106", "9.6036733,2.5927984,2.8488436", "7.2373861,1.4947206,2.8305463", "4.9620442,2.5134284,4.5976544", "7.2373861,1.4947206,2.8305463")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("0.5036993,0.6001582,2.2439019", "9.0252221,6.6637385,5.1333177", "6.9023714,0.8286511,9.3846113", "7.8635188,7.6077266,0.1778680", "6.9835948,5.2109969,4.4410576", "7.4521485,4.7062875,4.1183470")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("4.1414680,4.8821393,2.4051430", "2.1773492,6.4060895,2.8305709", "0.5806575,3.1182178,9.4735442", "9.3828570,0.6330684,7.6857961", "4.1414680,4.8821393,2.4051430", "4.5936441,1.9852201,8.6584969")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("3.6062333,0.6118218,5.2241603", "0.7544416,1.5864715,8.4712397", "2.3437474,4.9755332,1.7418572", "9.8825574,1.3070092,8.6204338", "3.6062333,0.6118218,5.2241603", "5.5154683,3.4321153,4.6358053")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("2.2607133,8.3082403,6.7904628", "5.0325175,8.7431170,3.9781037", "6.4334028,4.8699270,1.1961501", "1.7809363,2.4707254,6.2966301", "5.0325175,8.7431170,3.9781037", "5.4392405,4.3572536,2.2860462")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("6.1786725,3.6854264,9.2902405", "2.6667579,9.5505050,9.5018463", "2.0599944,1.6033445,0.6954832", "3.1884883,6.4163288,9.0715930", "4.1912356,7.0045487,9.4099909", "3.1884883,6.4163288,9.0715930")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("8.8292667,0.7124560,8.2423649", "0.3649094,7.1453826,3.0669636", "2.5889872,1.1761708,7.2524548", "5.4661666,6.7986776,4.9964301", "4.3314255,4.1308233,5.4922289", "4.2263025,4.3757686,5.9686197")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.0241017,5.1715162,5.7250655", "5.6868388,6.0031583,1.2902594", "3.4800129,9.7922534,2.4761596", "0.0589551,3.4081038,0.9383102", "5.6945715,5.9840905,1.3919397", "2.3316866,7.6493234,1.9599588")] // projection between segments, generated in GOM Inspect Professional V8
public void ClosestPointsBetweenOnSegment(string s1, string e1, string s2, string e2, string cp1, string cp2)
{
var l1 = Line3D.Parse(s1, e1);
var l2 = Line3D.Parse(s2, e2);
var result = l1.ClosestPointsBetween(l2, true);
AssertGeometry.AreEqual(Point3D.Parse(cp1), result.Item1);
AssertGeometry.AreEqual(Point3D.Parse(cp2), result.Item2);
}
}
}

143
src/Numerics.Tests/Spatial/Euclidean3D/LineSegment3DTests.cs

@ -0,0 +1,143 @@
using System;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
/// <summary>
/// Tests for LineSegment3D
/// </summary>
[TestFixture]
public class LineSegment3DTests
{
[Test]
public void Ctor()
{
Assert.Throws<ArgumentException>(() => new LineSegment3D(Point3D.Origin, Point3D.Origin));
}
[TestCase("0, 0, 0", "1, -1, 1", "1, -1, 1")]
public void DirectionsTest(string p1s, string p2s, string evs)
{
var l = LineSegment3D.Parse(p1s, p2s);
var excpected = UnitVector3D.Parse(evs, tolerance: 1);
AssertGeometry.AreEqual(excpected, l.Direction);
}
[TestCase("0, 0, 0", "1, -2, 3", 3.741657)]
public void Length(string p1s, string p2s, double expected)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var l = new LineSegment3D(p1, p2);
Assert.AreEqual(expected, l.Length, 1e-6);
}
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "1, -1, 1", true)]
[TestCase("0, 0, 2", "1, -1, 1", "0, 0, 0", "1, -1, 1", false)]
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "2, -1, 1", false)]
public void Equals(string p1s, string p2s, string p3s, string p4s, bool expected)
{
var line1 = new LineSegment3D(Point3D.Parse(p1s), Point3D.Parse(p2s));
var line2 = new LineSegment3D(Point3D.Parse(p3s), Point3D.Parse(p4s));
Assert.AreEqual(expected, line1.Equals(line2));
Assert.AreEqual(expected, line1 == line2);
Assert.AreEqual(!expected, line1 != line2);
}
[TestCase("1,1,1", "3,1,1", "1,1,0", "2,2,1", "4,2,1")]
[TestCase("1,1,1", "3,1,1", "-1,-1,0", "0,0,1", "2,0,1")]
public void TranslateBy(string spoint1, string spoint2, string svector, string spoint3, string spoint4)
{
var line = LineSegment3D.Parse(spoint1, spoint2);
var expected = LineSegment3D.Parse(spoint3, spoint4);
var vector = Vector3D.Parse(svector);
Assert.AreEqual(expected.Length, line.Length);
Assert.AreEqual(expected, line.TranslateBy(vector));
}
[TestCase("0, 0, 0", "1, 0, 0", "0.5, 1, 0", "0.5, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "2, 1, 0", "1, 0, 0")]
[TestCase("0, 0, 0", "1, 0, 0", "-2, 1, 0", "0, 0, 0")]
public void LineToTest(string p1s, string p2s, string ps, string sps)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var l = new LineSegment3D(p1, p2);
var p = Point3D.Parse(ps);
var actual = l.LineTo(p);
AssertGeometry.AreEqual(Point3D.Parse(sps), actual.StartPoint, 1e-6);
AssertGeometry.AreEqual(p, actual.EndPoint, 1e-6);
}
[TestCase("0,0,0", "0,0,1", "0,0,0", "0,0,0", Description = "Start point")]
[TestCase("0,0,0", "0,0,1", "0,0,1", "0,0,1", Description = "End point")]
[TestCase("0,0,0", "0,0,1", "1,0,.25", "0,0,.25")]
[TestCase("0,0,0", "0,0,1", "0,0,-1", "0,0,0")]
[TestCase("0,0,0", "0,0,1", "0,0,3", "0,0,1")]
public void ClosestPointTo(string start, string end, string point, string expected)
{
var line = LineSegment3D.Parse(start, end);
var p = Point3D.Parse(point);
var e = Point3D.Parse(expected);
Assert.AreEqual(e, line.ClosestPointTo(p));
}
[TestCase("0,0,0", "0,0,1", "0,1,1", "0,1,2", 0.00001, true)]
[TestCase("0,0,0", "0,0,-1", "0,1,1", "0,1,2", 0.00001, true)]
[TestCase("0,0,0", "0,0.5,-1", "0,1,1", "0,1,2", 0.00001, false)]
[TestCase("0,0,0", "0,0.00001,-1.0000", "0,1,1", "0,1,2", 0.00001, false)]
[TestCase("0,0,0", "0,0,1", "0,1,1", "0,1,2", 0.01, true)]
[TestCase("0,0,0", "0,0,-1", "0,1,1", "0,1,2", 0.01, true)]
[TestCase("0,0,0", "0,0.5,-1", "0,1,1", "0,1,2", 0.01, false)]
[TestCase("0,0,0", "0,0.001,-1.0000", "0,1,1", "0,1,2", 0.05, false)]
[TestCase("0,0,0", "0,0.001,-1.0000", "0,1,1", "0,1,2", 0.06, true)]
public void IsParallelToWithinAngleTol(string s1, string e1, string s2, string e2, double degreesTol, bool expected)
{
var line1 = LineSegment3D.Parse(s1, e1);
var line2 = LineSegment3D.Parse(s2, e2);
Assert.AreEqual(expected, line1.IsParallelTo(line2, Angle.FromDegrees(degreesTol)));
}
[TestCase("0,0,0", "1,0,0", "0.5,1,0", "1.5,1,0", "1,0,0", "1,1,0")] // Parallel case
[TestCase("0,0,0", "1,0,0", "3,1,0", "3,2,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("1,0,0", "0,0,0", "3,1,0", "3,2,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("0,0,0", "1,0,0", "3,2,0", "3,1,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("1,0,0", "0,0,0", "3,2,0", "3,1,0", "1,0,0", "3,1,0")] // Endpoint Case
[TestCase("5.6925969,1.3884847,7.1713834", "5.1573193,9.7184415,0.8644498", "0.6567836,8.3850115,1.5273528", "7.1182449,9.0049546,9.1872098", "5.3056396,7.4102899,2.6120410", "3.0759605,8.6171187,4.3952101")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("3.0803549,8.1101503,4.2072541", "0.9167489,5.4057168,0.0942629", "3.6443155,1.9841677,2.1280020", "4.0865344,8.8738039,9.2944797", "3.0803549,8.1101503,4.2072541", "3.8982350,5.9401568,6.2429519")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("2.0809966,9.3100446,9.4661138", "6.0883386,5.5240161,2.7490910", "8.4523738,2.6004881,8.8473518", "5.5868380,5.4932213,1.1649868", "6.0883386,5.5240161,2.7490910", "6.0992239,4.9759722,2.5386692")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("6.6149320,7.8081445,4.6267089", "5.3733678,7.5372568,0.4121304", "7.9879025,7.5486791,5.8931379", "1.4971100,6.2860737,3.2138409", "6.6149320,7.8081445,4.6267089", "6.4606595,7.2515959,5.2627161")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("4.7238306,4.7424963,7.9590086", "9.2276709,8.3299427,1.0349775", "7.3828132,6.3559129,8.7078245", "4.6487651,2.8181310,8.5972384", "4.7238306,4.7424963,7.9590086", "5.5976910,4.0460147,8.6356203")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("7.1035997,1.9299120,3.4688193", "8.5433252,5.8883905,9.7941707", "3.3053692,6.3729100,3.5626868", "8.4883669,8.1557493,7.2000211", "8.3560381,5.3734507,8.9713356", "8.4883669,8.1557493,7.2000211")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("7.4520661,5.7569419,4.1686608", "4.2367431,3.5840889,2.7405165", "1.3188110,5.7542366,2.7702002", "7.7144529,5.0792324,0.2292819", "4.7448074,3.9274289,2.9661826", "4.3479012,5.4345425,1.5667759")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("7.5543831,9.7934598,9.5348209", "6.5205418,0.3092162,8.7907210", "0.4877286,0.3419443,2.5644342", "8.6578287,6.1098998,5.8827401", "7.1211660,5.8192172,9.2230160", "8.4262398,5.9464019,5.7886797")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.0543887,5.4347267,4.3429352", "4.2117265,4.7630853,1.3218313", "8.9537706,1.5933994,0.6307145", "2.7936886,7.2837201,1.8965656", "4.5061578,4.8704041,1.8045609", "4.8831652,5.3535848,1.4671937")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.5771629,2.8557455,9.8087521", "5.4359776,8.6172816,5.7508093", "7.8904712,4.3099454,2.4107493", "8.1402295,0.9894932,3.8694855", "5.7508984,7.0273316,6.8706367", "7.8904712,4.3099454,2.4107493")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("1.1294829,5.4847586,8.1420946", "7.2489863,0.3206420,0.8259188", "5.8457205,6.7040761,3.0411085", "8.9017428,0.9704561,3.8471667", "5.7071649,1.6217517,2.6692442", "7.8717570,2.9028855,3.5754970")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("0.8765316,4.0262533,7.1988316", "4.3383388,7.0189039,1.4430865", "1.9687345,6.4066677,1.9041603", "0.6533722,5.3636394,1.3877450", "3.5259020,6.3165714,2.7938779", "1.9687345,6.4066677,1.9041603")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("0.3580051,9.3271145,8.5768069", "2.3779489,4.3772771,1.6443451", "1.0661185,9.0362165,3.9415240", "7.6388414,1.7341324,0.4120660", "1.8279811,5.7249636,3.5318384", "2.9136360,6.9836839,2.9494336")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.1264627,6.5812576,3.9222538", "1.5068838,5.6589456,0.0446154", "0.8368111,1.7808290,7.1053143", "8.6670395,3.8812950,1.4077528", "5.4376749,6.4437392,3.3440907", "6.1998837,3.2194782,3.2029458")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("5.2548631,0.4534686,7.9484333", "2.8554822,5.3465480,5.8755276", "0.3950940,1.3010814,5.5699850", "6.0302455,6.0816738,9.9995164", "3.7687405,3.4841320,6.6645221", "2.9986425,3.5098071,7.6165137")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("4.7098248,1.2821556,4.9827025", "6.4808992,9.9281018,2.2789106", "9.6036733,2.5927984,2.8488436", "7.2373861,1.4947206,2.8305463", "4.9620442,2.5134284,4.5976544", "7.2373861,1.4947206,2.8305463")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("0.5036993,0.6001582,2.2439019", "9.0252221,6.6637385,5.1333177", "6.9023714,0.8286511,9.3846113", "7.8635188,7.6077266,0.1778680", "6.9835948,5.2109969,4.4410576", "7.4521485,4.7062875,4.1183470")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("4.1414680,4.8821393,2.4051430", "2.1773492,6.4060895,2.8305709", "0.5806575,3.1182178,9.4735442", "9.3828570,0.6330684,7.6857961", "4.1414680,4.8821393,2.4051430", "4.5936441,1.9852201,8.6584969")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("3.6062333,0.6118218,5.2241603", "0.7544416,1.5864715,8.4712397", "2.3437474,4.9755332,1.7418572", "9.8825574,1.3070092,8.6204338", "3.6062333,0.6118218,5.2241603", "5.5154683,3.4321153,4.6358053")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("2.2607133,8.3082403,6.7904628", "5.0325175,8.7431170,3.9781037", "6.4334028,4.8699270,1.1961501", "1.7809363,2.4707254,6.2966301", "5.0325175,8.7431170,3.9781037", "5.4392405,4.3572536,2.2860462")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("6.1786725,3.6854264,9.2902405", "2.6667579,9.5505050,9.5018463", "2.0599944,1.6033445,0.6954832", "3.1884883,6.4163288,9.0715930", "4.1912356,7.0045487,9.4099909", "3.1884883,6.4163288,9.0715930")] // projection from endpoint, generated in GOM Inspect Professional V8
[TestCase("8.8292667,0.7124560,8.2423649", "0.3649094,7.1453826,3.0669636", "2.5889872,1.1761708,7.2524548", "5.4661666,6.7986776,4.9964301", "4.3314255,4.1308233,5.4922289", "4.2263025,4.3757686,5.9686197")] // projection between segments, generated in GOM Inspect Professional V8
[TestCase("6.0241017,5.1715162,5.7250655", "5.6868388,6.0031583,1.2902594", "3.4800129,9.7922534,2.4761596", "0.0589551,3.4081038,0.9383102", "5.6945715,5.9840905,1.3919397", "2.3316866,7.6493234,1.9599588")] // projection between segments, generated in GOM Inspect Professional V8
public void ClosestPointsBetweenOnSegment(string s1, string e1, string s2, string e2, string cp1, string cp2)
{
var l1 = LineSegment3D.Parse(s1, e1);
var l2 = LineSegment3D.Parse(s2, e2);
Assert.AreEqual(true, l1.TryShortestLineTo(l2, Angle.FromRadians(0.00001), out var result));
AssertGeometry.AreEqual(Point3D.Parse(cp1), result.StartPoint);
AssertGeometry.AreEqual(Point3D.Parse(cp2), result.EndPoint);
}
}
}

238
src/Numerics.Tests/Spatial/Euclidean3D/Plane3DTests.cs

@ -0,0 +1,238 @@
// ReSharper disable InconsistentNaming
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Plane3DTests
{
private const string X = "1; 0 ; 0";
private const string Z = "0; 0; 1";
private const string NegativeZ = "0; 0; -1";
private const string ZeroPoint = "0; 0; 0";
[Test]
public void Ctor()
{
var plane1 = new Plane3D(new Point3D(0, 0, 3), UnitVector3D.ZAxis);
var plane2 = new Plane3D(0, 0, 3, -3);
var plane3 = new Plane3D(UnitVector3D.ZAxis, 3);
var plane4 = Plane3D.FromPoints(new Point3D(0, 0, 3), new Point3D(5, 3, 3), new Point3D(-2, 1, 3));
AssertGeometry.AreEqual(plane1, plane2);
AssertGeometry.AreEqual(plane1, plane3);
AssertGeometry.AreEqual(plane1, plane4);
}
[TestCase("0, 0, 0", "1, 0, 0", "0, 0, 0", "1, 0, 0")]
public void Parse(string rootPoint, string unitVector, string pds, string vds)
{
var plane = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
AssertGeometry.AreEqual(Point3D.Parse(pds), plane.RootPoint);
AssertGeometry.AreEqual(Vector3D.Parse(vds), plane.Normal);
}
[TestCase("1, 0, 0, 0", "0, 0, 0", "1, 0, 0")]
public void Parse2(string s, string pds, string vds)
{
var plane = this.GetPlaneFrom4Doubles(s);
AssertGeometry.AreEqual(Point3D.Parse(pds), plane.RootPoint);
AssertGeometry.AreEqual(Vector3D.Parse(vds), plane.Normal);
}
[TestCase(ZeroPoint, "0, 0, 0", "0, 0, 1", ZeroPoint)]
[TestCase(ZeroPoint, "0, 0, -1", "0, 0, 1", "0; 0;-1")]
[TestCase(ZeroPoint, "0, 0, 1", "0, 0, -1", "0; 0; 1")]
[TestCase("1; 2; 3", "0, 0, 0", "0, 0, 1", "1; 2; 0")]
public void ProjectPointOn(string ps, string rootPoint, string unitVector, string eps)
{
var plane = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
var projectedPoint = plane.Project(Point3D.Parse(ps));
var expected = Point3D.Parse(eps);
AssertGeometry.AreEqual(expected, projectedPoint, float.Epsilon);
}
[TestCase(ZeroPoint, Z, ZeroPoint, 0)]
[TestCase(ZeroPoint, Z, "1; 2; 0", 0)]
[TestCase(ZeroPoint, Z, "1; -2; 0", 0)]
[TestCase(ZeroPoint, Z, "1; 2; 3", 3)]
[TestCase(ZeroPoint, Z, "-1; 2; -3", -3)]
[TestCase(ZeroPoint, NegativeZ, ZeroPoint, 0)]
[TestCase(ZeroPoint, NegativeZ, "1; 2; 1", -1)]
[TestCase(ZeroPoint, NegativeZ, "1; 2; -1", 1)]
[TestCase("0; 0; -1", NegativeZ, ZeroPoint, -1)]
[TestCase("0; 0; 1", NegativeZ, ZeroPoint, 1)]
[TestCase(ZeroPoint, X, "1; 0; 0", 1)]
[TestCase("188,6578; 147,0620; 66,0170", Z, "118,6578; 147,0620; 126,1170", 60.1)]
public void SignedDistanceToPoint(string prps, string pns, string ps, double expected)
{
var plane = new Plane3D(UnitVector3D.Parse(pns), Point3D.Parse(prps));
var p = Point3D.Parse(ps);
Assert.AreEqual(expected, plane.SignedDistanceTo(p), 1E-6);
}
[TestCase(ZeroPoint, Z, ZeroPoint, Z, 0)]
[TestCase(ZeroPoint, Z, "0;0;1", Z, 1)]
[TestCase(ZeroPoint, Z, "0;0;-1", Z, -1)]
[TestCase(ZeroPoint, NegativeZ, "0;0;-1", Z, 1)]
public void SignedDistanceToOtherPlane(string prps, string pns, string otherPlaneRootPointString, string otherPlaneNormalString, double expectedValue)
{
var plane = new Plane3D(UnitVector3D.Parse(pns), Point3D.Parse(prps));
var otherPlane = new Plane3D(UnitVector3D.Parse(otherPlaneNormalString), Point3D.Parse(otherPlaneRootPointString));
Assert.AreEqual(expectedValue, plane.SignedDistanceTo(otherPlane), 1E-6);
}
[TestCase(ZeroPoint, Z, ZeroPoint, Z, 0)]
[TestCase(ZeroPoint, Z, ZeroPoint, X, 0)]
[TestCase(ZeroPoint, Z, "0;0;1", X, 1)]
public void SignedDistanceToRay(string prps, string pns, string rayThroughPointString, string rayDirectionString, double expectedValue)
{
var plane = new Plane3D(UnitVector3D.Parse(pns), Point3D.Parse(prps));
var otherPlane = new Ray3D(Point3D.Parse(rayThroughPointString), UnitVector3D.Parse(rayDirectionString));
Assert.AreEqual(expectedValue, plane.SignedDistanceTo(otherPlane), 1E-6);
}
[Test]
public void ProjectLineOn()
{
var unitVector = UnitVector3D.ZAxis;
var rootPoint = new Point3D(0, 0, 1);
var plane = new Plane3D(unitVector, rootPoint);
var line = new LineSegment3D(new Point3D(0, 0, 0), new Point3D(1, 0, 0));
var projectOn = plane.Project(line);
AssertGeometry.AreEqual(new LineSegment3D(new Point3D(0, 0, 1), new Point3D(1, 0, 1)), projectOn, float.Epsilon);
}
[Test]
public void ProjectVectorOn()
{
var unitVector = UnitVector3D.ZAxis;
var rootPoint = new Point3D(0, 0, 1);
var plane = new Plane3D(unitVector, rootPoint);
var vector = new Vector3D(1, 0, 0);
var projectOn = plane.Project(vector);
AssertGeometry.AreEqual(new Vector3D(1, 0, 0), projectOn.Direction, float.Epsilon);
AssertGeometry.AreEqual(new Point3D(0, 0, 1), projectOn.ThroughPoint, float.Epsilon);
}
[TestCase("0, 0, 0", "0, 0, 1", "0, 0, 0", "0, 1, 0", "0, 0, 0", "-1, 0, 0")]
[TestCase("0, 0, 2", "0, 0, 1", "0, 0, 0", "0, 1, 0", "0, 0, 2", "-1, 0, 0")]
public void InterSectionWithPlane(string rootPoint1, string unitVector1, string rootPoint2, string unitVector2, string eps, string evs)
{
var plane1 = new Plane3D(Point3D.Parse(rootPoint1), UnitVector3D.Parse(unitVector1));
var plane2 = new Plane3D(Point3D.Parse(rootPoint2), UnitVector3D.Parse(unitVector2));
var intersections = new[]
{
plane1.IntersectionWith(plane2),
plane2.IntersectionWith(plane1)
};
foreach (var intersection in intersections)
{
AssertGeometry.AreEqual(Point3D.Parse(eps), intersection.ThroughPoint);
AssertGeometry.AreEqual(UnitVector3D.Parse(evs), intersection.Direction);
}
}
[TestCase("0, 0, 0", "0, 0, 1", "0, 0, 0", "0, 0, 1", "0, 0, 0", "0, 0, 0")]
public void InterSectionWithPlaneTest_BadArgument(string rootPoint1, string unitVector1, string rootPoint2, string unitVector2, string eps, string evs)
{
var plane1 = new Plane3D(Point3D.Parse(rootPoint1), UnitVector3D.Parse(unitVector1));
var plane2 = new Plane3D(Point3D.Parse(rootPoint2), UnitVector3D.Parse(unitVector2));
Assert.Throws<ArgumentException>(() => plane1.IntersectionWith(plane2));
Assert.Throws<ArgumentException>(() => plane2.IntersectionWith(plane1));
}
[Test]
public void MirrorPoint()
{
var plane = new Plane3D(UnitVector3D.ZAxis, new Point3D(0, 0, 0));
var point3D = new Point3D(1, 2, 3);
var mirrorAbout = plane.MirrorAbout(point3D);
AssertGeometry.AreEqual(new Point3D(1, 2, -3), mirrorAbout, float.Epsilon);
}
[Test]
public void SignOfD()
{
var plane1 = new Plane3D(UnitVector3D.ZAxis, new Point3D(0, 0, 100));
Assert.AreEqual(-100, plane1.D);
}
[Test]
public void InterSectionPointDifferentOrder()
{
var plane1 = new Plane3D(UnitVector3D.Create(0.8, 0.3, 0.01), new Point3D(20, 0, 0));
var plane2 = new Plane3D(UnitVector3D.Create(0.002, 1, 0.1), new Point3D(0, 0, 0));
var plane3 = new Plane3D(UnitVector3D.Create(0.5, 0.5, 1), new Point3D(0, 0, -30));
var pointFromPlanes1 = Plane3D.PointFromPlanes(plane1, plane2, plane3);
var pointFromPlanes2 = Plane3D.PointFromPlanes(plane2, plane1, plane3);
var pointFromPlanes3 = Plane3D.PointFromPlanes(plane3, plane1, plane2);
AssertGeometry.AreEqual(pointFromPlanes1, pointFromPlanes2, 1E-10);
AssertGeometry.AreEqual(pointFromPlanes3, pointFromPlanes2, 1E-10);
}
[TestCase("0, 0, 0", "1, 0, 0", "0, 0, 0", "0, 1, 0", "0, 0, 0", "0, 0, 1", "0, 0, 0")]
[TestCase("0, 0, 0", "-1, 0, 0", "0, 0, 0", "0, 1, 0", "0, 0, 0", "0, 0, 1", "0, 0, 0")]
[TestCase("20, 0, 0", "1, 0, 0", "0, 0, 0", "0, 1, 0", "0, 0, -30", "0, 0, 1", "20, 0, -30")]
public void PointFromPlanes(string rootPoint1, string unitVector1, string rootPoint2, string unitVector2, string rootPoint3, string unitVector3, string eps)
{
var plane1 = new Plane3D(Point3D.Parse(rootPoint1), UnitVector3D.Parse(unitVector1));
var plane2 = new Plane3D(Point3D.Parse(rootPoint2), UnitVector3D.Parse(unitVector2));
var plane3 = new Plane3D(Point3D.Parse(rootPoint3), UnitVector3D.Parse(unitVector3));
var points = new[]
{
Plane3D.PointFromPlanes(plane1, plane2, plane3),
Plane3D.PointFromPlanes(plane2, plane1, plane3),
Plane3D.PointFromPlanes(plane1, plane3, plane2),
Plane3D.PointFromPlanes(plane2, plane3, plane1),
Plane3D.PointFromPlanes(plane3, plane2, plane1),
Plane3D.PointFromPlanes(plane3, plane1, plane2),
};
var expected = Point3D.Parse(eps);
foreach (var point in points)
{
AssertGeometry.AreEqual(expected, point);
}
}
[TestCase("1, 1, 0, -12", "-1, 1, 0, -12", "0, 0, 1, -5", "0, 16.970563, 5")]
public void PointFromPlanes2(string planeString1, string planeString2, string planeString3, string eps)
{
var plane1 = this.GetPlaneFrom4Doubles(planeString1);
var plane2 = this.GetPlaneFrom4Doubles(planeString2);
var plane3 = this.GetPlaneFrom4Doubles(planeString3);
var points = new[]
{
Plane3D.PointFromPlanes(plane1, plane2, plane3),
Plane3D.PointFromPlanes(plane2, plane1, plane3),
Plane3D.PointFromPlanes(plane1, plane3, plane2),
Plane3D.PointFromPlanes(plane2, plane3, plane1),
Plane3D.PointFromPlanes(plane3, plane2, plane1),
Plane3D.PointFromPlanes(plane3, plane1, plane2),
};
var expected = Point3D.Parse(eps);
foreach (var point in points)
{
AssertGeometry.AreEqual(expected, point);
}
}
[TestCase("0, 0, 0", "0, 0, 1", @"<Plane3D><RootPoint X=""0"" Y=""0"" Z=""0"" /><Normal X=""0"" Y=""0"" Z=""1"" /></Plane3D>")]
public void XmlRoundTrips(string rootPoint, string unitVector, string xml)
{
var plane = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
AssertXml.XmlRoundTrips(plane, xml, (e, a) => AssertGeometry.AreEqual(e, a));
}
private Plane3D GetPlaneFrom4Doubles(string inputstring)
{
var numbers = inputstring.Split(',').Select(t => double.Parse(t)).ToArray();
return new Plane3D(numbers[0], numbers[1], numbers[2], numbers[3]);
}
}
}

294
src/Numerics.Tests/Spatial/Euclidean3D/Point3DTests.cs

@ -0,0 +1,294 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Point3DTests
{
[Test]
public void Ctor()
{
var actual = new Point3D(1, 2, 3);
Assert.AreEqual(1, actual.X, 1e-6);
Assert.AreEqual(2, actual.Y, 1e-6);
Assert.AreEqual(3, actual.Z, 1e-6);
}
[TestCase("-1,1,-1", -1, 1, -1)]
[TestCase("1, 2, 3", 1, 2, 3)]
[TestCase("1.2; 3.4; 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2;3.4;5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2 ; 3.4 ; 5.6", 1.2, 3.4, 5.6)]
[TestCase("1,2; 3,4; 5,6", 1.2, 3.4, 5.6)]
[TestCase("1.2, 3.4, 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2 3.4 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2,\u00A03.4\u00A05.6", 1.2, 3.4, 5.6)]
[TestCase("1.2\u00A03.4\u00A05.6", 1.2, 3.4, 5.6)]
[TestCase("(1.2, 3.4 5.6)", 1.2, 3.4, 5.6)]
[TestCase("1,2\u00A03,4\u00A05,6", 1.2, 3.4, 5.6)]
[TestCase("(.1, 2.3e-4,1)", 0.1, 0.00023000000000000001, 1)]
[TestCase("1.0 , 2.5,3.3", 1, 2.5, 3.3)]
[TestCase("1,0 ; 2,5;3,3", 1, 2.5, 3.3)]
[TestCase("1.0 ; 2.5;3.3", 1, 2.5, 3.3)]
[TestCase("1.0,2.5,-3.3", 1, 2.5, -3.3)]
[TestCase("1;2;3", 1, 2, 3)]
public void Parse(string text, double expectedX, double expectedY, double expectedZ)
{
Assert.AreEqual(true, Point3D.TryParse(text, out var p));
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = Point3D.Parse(text);
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = Point3D.Parse(p.ToString());
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
}
[TestCase("1.2")]
[TestCase("1,2; 2.3; 3")]
[TestCase("1; 2; 3; 4")]
public void ParseFails(string text)
{
Assert.AreEqual(false, Point3D.TryParse(text, out _));
Assert.Throws<FormatException>(() => Point3D.Parse(text));
}
[TestCase("<Point3D X=\"1\" Y=\"-2\" Z=\"3\" />")]
[TestCase("<Point3D Y=\"-2\" Z=\"3\" X=\"1\"/>")]
[TestCase("<Point3D Z=\"3\" X=\"1\" Y=\"-2\" />")]
[TestCase("<Point3D><X>1</X><Y>-2</Y><Z>3</Z></Point3D>")]
[TestCase("<Point3D><Y>-2</Y><Z>3</Z><X>1</X></Point3D>")]
[TestCase("<Point3D><Z>3</Z><X>1</X><Y>-2</Y></Point3D>")]
public void ReadFrom(string xml)
{
using (var reader = new StringReader(xml))
{
var actual = Point3D.ReadFrom(XmlReader.Create(reader));
Assert.AreEqual(new Point3D(1, -2, 3), actual);
}
}
[Test]
public void ToDenseVector()
{
var p = new Point3D(1, 2, 3);
var vector = p.ToVector();
Assert.AreEqual(3, vector.Count);
Assert.AreEqual(1, vector[0], 1e-6);
Assert.AreEqual(2, vector[1], 1e-6);
Assert.AreEqual(3, vector[2], 1e-6);
var roundtripped = Point3D.OfVector(vector);
Assert.AreEqual(1, roundtripped.X, 1e-6);
Assert.AreEqual(2, roundtripped.Y, 1e-6);
Assert.AreEqual(3, roundtripped.Z, 1e-6);
}
[TestCase("1, 2, 3", "1, 2, 3", 1e-4, true)]
[TestCase("1, 2, 3", "4, 5, 6", 1e-4, false)]
public void Equals(string p1s, string p2s, double tol, bool expected)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
Assert.AreEqual(expected, p1 == p2);
Assert.AreEqual(expected, p1.Equals(p2));
Assert.AreEqual(expected, p1.Equals((object)p2));
Assert.AreEqual(expected, Equals(p1, p2));
Assert.AreEqual(expected, p1.Equals(p2, tol));
Assert.AreNotEqual(expected, p1 != p2);
}
[TestCase("0, 0, 0", "0, 0, 1", "0, 0, 0.5")]
[TestCase("0, 0, 1", "0, 0, 0", "0, 0, 0.5")]
[TestCase("0, 0, 0", "0, 0, 0", "0, 0, 0")]
[TestCase("1, 1, 1", "3, 3, 3", "2, 2, 2")]
[TestCase("-3, -3, -3", "3, 3, 3", "0, 0, 0")]
public void MidPoint(string p1s, string p2s, string eps)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var ep = Point3D.Parse(eps);
var mp = Point3D.MidPoint(p1, p2);
AssertGeometry.AreEqual(ep, mp, 1e-9);
var centroid = Point3D.Centroid(p1, p2);
AssertGeometry.AreEqual(ep, centroid, 1e-9);
}
[TestCase("0, 0, 0", "0, 0, 1", "0, 0, 0", "0, 1, 0", "0, 0, 0", "1, 0, 0", "0, 0, 0")]
[TestCase("0, 0, 5", "0, 0, 1", "0, 4, 0", "0, 1, 0", "3, 0, 0", "1, 0, 0", "3, 4, 5")]
public void FromPlanes(string rootPoint1, string unitVector1, string rootPoint2, string unitVector2, string rootPoint3, string unitVector3, string eps)
{
var plane1 = new Plane3D(Point3D.Parse(rootPoint1), UnitVector3D.Parse(unitVector1));
var plane2 = new Plane3D(Point3D.Parse(rootPoint2), UnitVector3D.Parse(unitVector2));
var plane3 = new Plane3D(Point3D.Parse(rootPoint3), UnitVector3D.Parse(unitVector3));
var p1 = Point3D.IntersectionOf(plane1, plane2, plane3);
var p2 = Point3D.IntersectionOf(plane2, plane1, plane3);
var p3 = Point3D.IntersectionOf(plane2, plane3, plane1);
var p4 = Point3D.IntersectionOf(plane3, plane1, plane2);
var p5 = Point3D.IntersectionOf(plane3, plane2, plane1);
var ep = Point3D.Parse(eps);
foreach (var p in new[] { p1, p2, p3, p4, p5 })
{
AssertGeometry.AreEqual(ep, p);
}
}
[TestCase("0, 0, 0", "0, 0, 0", "0, 0, 1", "0, 0, 0")]
[TestCase("0, 0, 1", "0, 0, 0", "0, 0, 1", "0, 0, -1")]
public void MirrorAbout(string ps, string rootPoint, string unitVector, string eps)
{
var p = Point3D.Parse(ps);
var p2 = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
var actual = p.MirrorAbout(p2);
var ep = Point3D.Parse(eps);
AssertGeometry.AreEqual(ep, actual);
}
[TestCase("0, 0, 0", "0, 0, 0", "0, 0, 1", "0, 0, 0")]
[TestCase("0, 0, 1", "0, 0, 0", "0, 0, 1", "0, 0, 0")]
[TestCase("0, 0, 1", "0, 10, 0", "0, 1, 0", "0, 10, 1")]
public void ProjectOnTests(string ps, string rootPoint, string unitVector, string eps)
{
var p = Point3D.Parse(ps);
var p2 = new Plane3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
var actual = p.ProjectOn(p2);
var ep = Point3D.Parse(eps);
AssertGeometry.AreEqual(ep, actual);
}
[TestCase("1, 2, 3", "1, 0, 0", "2, 2, 3")]
[TestCase("1, 2, 3", "0, 1, 0", "1, 3, 3")]
[TestCase("1, 2, 3", "0, 0, 1", "1, 2, 4")]
public void AddVector(string ps, string vs, string eps)
{
var p = Point3D.Parse(ps);
var actuals = new[]
{
p + Vector3D.Parse(vs),
p + UnitVector3D.Parse(vs)
};
var expected = Point3D.Parse(eps);
foreach (var actual in actuals)
{
Assert.AreEqual(expected, actual);
}
}
[TestCase("1, 2, 3", "1, 0, 0", "0, 2, 3")]
[TestCase("1, 2, 3", "0, 1, 0", "1, 1, 3")]
[TestCase("1, 2, 3", "0, 0, 1", "1, 2, 2")]
public void SubtractVector(string ps, string vs, string eps)
{
var p = Point3D.Parse(ps);
var actuals = new[]
{
p - Vector3D.Parse(vs),
p - UnitVector3D.Parse(vs)
};
var expected = Point3D.Parse(eps);
foreach (var actual in actuals)
{
Assert.AreEqual(expected, actual);
}
}
[TestCase("1, 2, 3", "4, 8, 16", "-3, -6, -13")]
public void SubtractPoint(string p1s, string p2s, string evs)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
var expected = Vector3D.Parse(evs);
Assert.AreEqual(expected, p1 - p2);
}
[TestCase("0,0,0", "1,0,0", 1)]
[TestCase("1,1,1", "2,1,1", 1)]
public void DistanceTo(string p1s, string p2s, double d)
{
var p1 = Point3D.Parse(p1s);
var p2 = Point3D.Parse(p2s);
Assert.AreEqual(d, p1.DistanceTo(p2), 1e-6);
Assert.AreEqual(d, p2.DistanceTo(p1), 1e-6);
}
[TestCase("-1 ; 2;-3")]
public void ToVectorAndBack(string ps)
{
var p = Point3D.Parse(ps);
AssertGeometry.AreEqual(p, p.ToVector3D().ToPoint3D(), 1e-9);
}
[TestCase("-2, 0, 1e-4", null, "(-2, 0, 0.0001)", 1e-4)]
[TestCase("-2, 0, 1e-4", "F2", "(-2.00, 0.00, 0.00)", 1e-4)]
public void ToString(string vs, string format, string expected, double tolerance)
{
var p = Point3D.Parse(vs);
var actual = p.ToString(format);
Assert.AreEqual(expected, actual);
AssertGeometry.AreEqual(p, Point3D.Parse(actual), tolerance);
}
[Test]
public void XmlRoundtrip()
{
var p = new Point3D(1, -2, 3);
var xml = @"<Point3D X=""1"" Y=""-2"" Z=""3"" />";
AssertXml.XmlRoundTrips(p, xml, (expected, actual) => AssertGeometry.AreEqual(expected, actual));
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<Point3D>
{
Value1 = new Point3D(1, 2, 3),
Value2 = new Point3D(4, 5, 6)
};
var expected = "<ContainerOfPoint3D>\r\n" +
" <Value1 X=\"1\" Y=\"2\" Z=\"3\"></Value1>\r\n" +
" <Value2 X=\"4\" Y=\"5\" Z=\"6\"></Value2>\r\n" +
"</ContainerOfPoint3D>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
AssertGeometry.AreEqual(container.Value1, roundTrip.Value1);
AssertGeometry.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void XmlElements()
{
var v = new Point3D(1, 2, 3);
var serializer = new XmlSerializer(typeof(Point3D));
AssertGeometry.AreEqual(v, (Point3D)serializer.Deserialize(new StringReader(@"<Point3D><X>1</X><Y>2</Y><Z>3</Z></Point3D>")));
}
[Test]
public void XmlContainerElements()
{
var xml = "<ContainerOfPoint3D>\r\n" +
" <Value1><X>1</X><Y>2</Y><Z>3</Z></Value1>\r\n" +
" <Value2><X>4</X><Y>5</Y><Z>6</Z></Value2>\r\n" +
"</ContainerOfPoint3D>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<Point3D>));
var deserialized = (AssertXml.Container<Point3D>)serializer.Deserialize(new StringReader(xml));
AssertGeometry.AreEqual(new Point3D(1, 2, 3), deserialized.Value1);
AssertGeometry.AreEqual(new Point3D(4, 5, 6), deserialized.Value2);
}
}
}

72
src/Numerics.Tests/Spatial/Euclidean3D/PolyLine3DTests.cs

@ -0,0 +1,72 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class PolyLine3DTests
{
[TestCase("0,0,1;1,1,0;2,2,1;3,3,0", 1, "1,1,0")]
[TestCase("0,0,1;1,1,0;2,2,1;3,3,0", 0, "0,0,1")]
[TestCase("0,0,1;1,1,0;2,2,1;3,3,0", 3, "3,3,0")]
public void IndexAccessorTest(string points, int index, string expected)
{
var testElement = new PolyLine3D(from x in points.Split(';') select Point3D.Parse(x));
var checkElement = Point3D.Parse(expected);
AssertGeometry.AreEqual(checkElement, testElement.Vertices.Skip(index).First());
}
[TestCase("0,0,0;0,1,0", 1.0)]
[TestCase("0,0,0;0,1,0;1,1,0", 2.0)]
[TestCase("0,-1.5,0;0,1,0;1,1,0", 3.5)]
public void GetPolyLineLengthTests(string points, double expected)
{
var testElement = new PolyLine3D(from x in points.Split(';') select Point3D.Parse(x));
Assert.AreEqual(expected, testElement.Length, 1e-10);
}
[TestCase("0,-1.5,0;0,1,0;1,1,0", 1.0, "1,1,0")]
[TestCase("0,-1.5,0;0,1,0;1,1,0", 0.0, "0,-1.5,0")]
[TestCase("0,0,0;0,1,0;1,1,0", 0.25, "0,0.5,0")]
[TestCase("0,0,0;0,1,0;1,1,0", 0.5, "0,1,0")]
[TestCase("0,0,0;0,1,0;1,1,0", 0.75, "0.5,1,0")]
public void GetPointAtFractionAlongCurve(string points, double fraction, string expected)
{
// Note that this method also tests GetPointAtLengthFromStart(...)
var testElement = new PolyLine3D(from x in points.Split(';') select Point3D.Parse(x));
var checkElement = Point3D.Parse(expected);
AssertGeometry.AreEqual(checkElement, testElement.GetPointAtFractionAlongCurve(fraction));
}
[TestCase("0,-1.5,0;0,1,0;1,1,0", 2.0, "1,1,0")]
[TestCase("0,-1.5,0;0,1,0;1,1,0", -5, "0,-1.5,0")]
public void GetPointAtFractionAlongCurveThrowsArgumentException(string points, double fraction, string expected)
{
var testElement = new PolyLine3D(from x in points.Split(';') select Point3D.Parse(x));
Assert.Throws<ArgumentException>(() => { testElement.GetPointAtFractionAlongCurve(fraction); });
}
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0,-1,0", "0,0,0")] // Off Endpoint
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "2,1,2", "1,1,2")] // Off Endpoint
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "-1,2,1", "0,1,1")] // Off Corner
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0,0,0", "0,0,0")] // On Endpoint
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "1,1,2", "1,1,2")] // On Endpoint
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0,1,1", "0,1,1")] // On Corner
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0,0.5,0.5", "0,0.5,0.5")] // On Curve
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "-1,0.5,0.5", "0,0.5,0.5")] // Off curve
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0.5,1,1.5", "0.5,1,1.5")] // On Curve
[TestCase("0,0,0 ; 0,1,1 ; 1,1,2", "0.5,1.5,1.5", "0.5,1,1.5")] // Off curve
public void ClosestPointToTest(string points, string testPoint, string expectedPoint)
{
var testCurve = new PolyLine3D(from x in points.Split(';') select Point3D.Parse(x));
var test = Point3D.Parse(testPoint);
var expected = Point3D.Parse(expectedPoint);
AssertGeometry.AreEqual(expected, testCurve.ClosestPointTo(test), 1e-06);
}
}
}

59
src/Numerics.Tests/Spatial/Euclidean3D/Ray3DTests.cs

@ -0,0 +1,59 @@
// ReSharper disable InconsistentNaming
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Ray3DTests
{
[TestCase("1, 2, 3", "0, 0, 1", "1, 2, 3", "0, 0, 1")]
public void Parse(string rootPoint, string unitVector, string eps, string evs)
{
var ray = new Ray3D(Point3D.Parse(rootPoint), UnitVector3D.Parse(unitVector));
AssertGeometry.AreEqual(Point3D.Parse(eps), ray.ThroughPoint);
AssertGeometry.AreEqual(Vector3D.Parse(evs), ray.Direction);
}
[TestCase("0, 0, 0", "0, 0, 1", "0, 0, 0", "0, 1, 0", "0, 0, 0", "-1, 0, 0")]
[TestCase("0, 0, 2", "0, 0, 1", "0, 0, 0", "0, 1, 0", "0, 0, 2", "-1, 0, 0")]
public void IntersectionOf(string rootPoint1, string unitVector1, string rootPoint2, string unitVector2, string eps, string evs)
{
var plane1 = new Plane3D(Point3D.Parse(rootPoint1), UnitVector3D.Parse(unitVector1));
var plane2 = new Plane3D(Point3D.Parse(rootPoint2), UnitVector3D.Parse(unitVector2));
var actual = Ray3D.IntersectionOf(plane1, plane2);
var expected = Ray3D.Parse(eps, evs);
AssertGeometry.AreEqual(expected, actual);
}
[Test]
public void LineToTest()
{
var ray = new Ray3D(new Point3D(0, 0, 0), UnitVector3D.ZAxis);
var point3D = new Point3D(1, 0, 0);
var line3DTo = ray.ShortestLineTo(point3D);
AssertGeometry.AreEqual(new Point3D(0, 0, 0), line3DTo.StartPoint);
AssertGeometry.AreEqual(point3D, line3DTo.EndPoint, float.Epsilon);
}
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "1, -1, 1", true)]
[TestCase("0, 0, 2", "1, -1, 1", "0, 0, 0", "1, -1, 1", false)]
[TestCase("0, 0, 0", "1, -1, 1", "0, 0, 0", "2, -1, 1", false)]
public void Equals(string p1s, string v1s, string p2s, string v2s, bool expected)
{
var ray1 = new Ray3D(Point3D.Parse(p1s), UnitVector3D.Parse(v1s, tolerance: 2));
var ray2 = new Ray3D(Point3D.Parse(p2s), UnitVector3D.Parse(v2s, tolerance: 2));
Assert.AreEqual(expected, ray1.Equals(ray2));
Assert.AreEqual(expected, ray1 == ray2);
Assert.AreEqual(!expected, ray1 != ray2);
}
[TestCase("1, 2, 3", "-0.2672612419124244, 0.53452248382484879, 0.80178372573727319", false, @"<Ray3D><ThroughPoint X=""1"" Y=""2"" Z=""3"" /><Direction X=""-0.2672612419124244"" Y=""0.53452248382484879"" Z=""0.80178372573727319"" /></Ray3D>")]
public void XmlTests(string ps, string vs, bool asElements, string xml)
{
var ray = new Ray3D(Point3D.Parse(ps), UnitVector3D.Parse(vs));
AssertXml.XmlRoundTrips(ray, xml, (e, a) => AssertGeometry.AreEqual(e, a, 1e-6));
}
}
}

220
src/Numerics.Tests/Spatial/Euclidean3D/UnitVector3DTests.cs

@ -0,0 +1,220 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class UnitVector3DTests
{
[Test]
public void Create()
{
var actual = UnitVector3D.Create(1, -2, 3);
Assert.AreEqual(0.2672612419124244, actual.X);
Assert.AreEqual(-0.53452248382484879, actual.Y);
Assert.AreEqual(0.80178372573727319, actual.Z);
actual = UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319);
Assert.AreEqual(0.2672612419124244, actual.X);
Assert.AreEqual(-0.53452248382484879, actual.Y);
Assert.AreEqual(0.80178372573727319, actual.Z);
Assert.Throws<ArgumentOutOfRangeException>(() => UnitVector3D.Create(double.NaN, 2, 3));
Assert.Throws<ArgumentOutOfRangeException>(() => UnitVector3D.Create(double.PositiveInfinity, 2, 3));
Assert.Throws<ArgumentOutOfRangeException>(() => UnitVector3D.Create(double.NegativeInfinity, 2, 3));
}
[TestCase("1,0; 0; 0,0", 1, 0, 0)]
[TestCase("0; 1,0; 0,0", 0, 1, 0)]
[TestCase("0; 0,0; 1,0", 0, 0, 1)]
[TestCase("1.0; 0; 0.0", 1, 0, 0)]
[TestCase("0; 1.0; 0.0", 0, 1, 0)]
[TestCase("0; 0.0; 1.0", 0, 0, 1)]
public void Parse(string text, double expectedX, double expectedY, double expectedZ)
{
Assert.AreEqual(true, UnitVector3D.TryParse(text, out var p));
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = UnitVector3D.Parse(text);
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = UnitVector3D.Parse(p.ToString());
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
}
[TestCase("1; 2; 3")]
[TestCase("1.2")]
[TestCase("1,2; 2.3; 3")]
[TestCase("1; 2; 3; 4")]
public void ParseFails(string text)
{
Assert.AreEqual(false, UnitVector3D.TryParse(text, out _));
Assert.Throws<FormatException>(() => UnitVector3D.Parse(text));
}
[Test]
public void ToDenseVector()
{
var uv = UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319);
var vector = uv.ToVector();
Assert.AreEqual(3, vector.Count);
Assert.AreEqual(0.2672612419124244, vector[0]);
Assert.AreEqual(-0.53452248382484879, vector[1]);
Assert.AreEqual(0.80178372573727319, vector[2]);
var roundtripped = UnitVector3D.OfVector(vector);
Assert.AreEqual(0.2672612419124244, roundtripped.X);
Assert.AreEqual(-0.53452248382484879, roundtripped.Y);
Assert.AreEqual(0.80178372573727319, roundtripped.Z);
}
[TestCase("1, 0, 0", "1, 0, 0", 1e-4, true)]
[TestCase("0, 1, 0", "0, 1, 0", 1e-4, true)]
[TestCase("0, 0, 1", "0, 0, 1", 1e-4, true)]
[TestCase("1, 0, 0", "0, 1, 0", 1e-4, false)]
[TestCase("0, 1, 0", "1, 0, 0", 1e-4, false)]
[TestCase("0, 0, 1", "0, 1, 0", 1e-4, false)]
public void Equals(string p1s, string p2s, double tol, bool expected)
{
var v1 = UnitVector3D.Parse(p1s);
var v2 = UnitVector3D.Parse(p2s);
var vector3D = v1.ToVector3D();
Assert.AreEqual(expected, v1 == v2);
Assert.IsTrue(v1 == vector3D);
Assert.IsTrue(vector3D == v1);
Assert.AreEqual(expected, v1.Equals(v2));
Assert.IsTrue(v1.Equals(vector3D));
Assert.IsTrue(vector3D.Equals(v1));
Assert.AreEqual(expected, v1.Equals(v2.ToVector3D()));
Assert.AreEqual(expected, v2.ToVector3D().Equals(v1));
Assert.AreEqual(expected, v1.Equals((object)v2));
Assert.AreEqual(expected, Equals(v1, v2));
Assert.AreEqual(expected, v1.Equals(v2, tol));
Assert.AreNotEqual(expected, v1 != v2);
Assert.AreNotEqual(expected, v1 != v2.ToVector3D());
Assert.AreNotEqual(expected, v2.ToVector3D() != v1);
}
[TestCase("1; 0; 0", 5, "5; 0; 0")]
[TestCase("1; 0; 0", -5, "-5; 0; 0")]
[TestCase("-1; 0; 0", 5, "-5; 0; 0")]
[TestCase("-1; 0; 0", -5, "5; 0; 0")]
[TestCase("0; 1; 0", 5, "0; 5; 0")]
[TestCase("0; 0; 1", 5, "0; 0; 5")]
public void Scale(string ivs, double s, string exs)
{
var uv = UnitVector3D.Parse(ivs);
var v = uv.ScaleBy(s);
AssertGeometry.AreEqual(Vector3D.Parse(exs), v, float.Epsilon);
}
[TestCase("1; 0; 0", "1; 0; 0", 1)]
[TestCase("1; 0; 0", "-1; 0; 0", -1)]
[TestCase("1; 0; 0", "0; -1; 0", 0)]
public void DotProduct(string v1s, string v2s, double expected)
{
var uv1 = UnitVector3D.Parse(v1s);
var uv2 = UnitVector3D.Parse(v2s);
var dp = uv1.DotProduct(uv2);
Assert.AreEqual(dp, expected, 1e-9);
Assert.IsTrue(dp <= 1);
Assert.IsTrue(dp >= -1);
}
[TestCase("-1, 0, 0", null, "(-1, 0, 0)", 1e-4)]
[TestCase("-1, 0, 1e-4", "F2", "(-1.00, 0.00, 0.00)", 1e-3)]
public void ToString(string vs, string format, string expected, double tolerance)
{
var v = UnitVector3D.Parse(vs);
var actual = v.ToString(format);
Assert.AreEqual(expected, actual);
AssertGeometry.AreEqual(v, UnitVector3D.Parse(actual), tolerance);
}
[TestCase("1,0,0", 3, "3,0,0")]
public void MultiplyTest(string unitVectorAsString, double multiplier, string expected)
{
var unitVector3D = UnitVector3D.Parse(unitVectorAsString);
Assert.AreEqual(Vector3D.Parse(expected), multiplier * unitVector3D);
}
[TestCase("<UnitVector3D X=\"0.2672612419124244\" Y=\"-0.53452248382484879\" Z=\"0.80178372573727319\" />")]
[TestCase("<UnitVector3D Y=\"-0.53452248382484879\" Z=\"0.80178372573727319\" X=\"0.2672612419124244\"/>")]
[TestCase("<UnitVector3D Z=\"0.80178372573727319\" X=\"0.2672612419124244\" Y=\"-0.53452248382484879\" />")]
[TestCase("<UnitVector3D><X>0.2672612419124244</X><Y>-0.53452248382484879</Y><Z>0.80178372573727319</Z></UnitVector3D>")]
[TestCase("<UnitVector3D><Y>-0.53452248382484879</Y><Z>0.80178372573727319</Z><X>0.2672612419124244</X></UnitVector3D>")]
[TestCase("<UnitVector3D><Z>0.80178372573727319</Z><X>0.2672612419124244</X><Y>-0.53452248382484879</Y></UnitVector3D>")]
public void ReadFrom(string xml)
{
using (var reader = new StringReader(xml))
{
var actual = UnitVector3D.ReadFrom(XmlReader.Create(reader));
Assert.AreEqual(UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319), actual);
}
}
[Test]
public void XmlRoundtrip()
{
var uv = UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319);
var xml = "<UnitVector3D X=\"0.2672612419124244\" Y=\"-0.53452248382484879\" Z=\"0.80178372573727319\" />";
AssertXml.XmlRoundTrips(uv, xml, (expected, actual) => AssertGeometry.AreEqual(expected, actual));
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<UnitVector3D>
{
Value1 = UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319),
Value2 = UnitVector3D.Create(1, 0, 0)
};
var expected = "<ContainerOfUnitVector3D>\r\n" +
" <Value1 X=\"0.2672612419124244\" Y=\"-0.53452248382484879\" Z=\"0.80178372573727319\"></Value1>\r\n" +
" <Value2 X=\"1\" Y=\"0\" Z=\"0\"></Value2>\r\n" +
"</ContainerOfUnitVector3D>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
AssertGeometry.AreEqual(container.Value1, roundTrip.Value1);
AssertGeometry.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void XmlElements()
{
var v = UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319);
var serializer = new XmlSerializer(typeof(UnitVector3D));
using (var reader = new StringReader("<UnitVector3D><X>0.2672612419124244</X><Y>-0.53452248382484879</Y><Z>0.80178372573727319</Z></UnitVector3D>"))
{
AssertGeometry.AreEqual(v, (UnitVector3D)serializer.Deserialize(reader));
}
}
[Test]
public void XmlContainerElements()
{
var xml = "<ContainerOfUnitVector3D>\r\n" +
" <Value1><X>0.2672612419124244</X><Y>-0.53452248382484879</Y><Z>0.80178372573727319</Z></Value1>\r\n" +
" <Value2><X>1</X><Y>0</Y><Z>0</Z></Value2>\r\n" +
"</ContainerOfUnitVector3D>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<UnitVector3D>));
var deserialized = (AssertXml.Container<UnitVector3D>)serializer.Deserialize(new StringReader(xml));
AssertGeometry.AreEqual(UnitVector3D.Create(0.2672612419124244, -0.53452248382484879, 0.80178372573727319), deserialized.Value1);
AssertGeometry.AreEqual(UnitVector3D.Create(1, 0, 0), deserialized.Value2);
}
}
}

492
src/Numerics.Tests/Spatial/Euclidean3D/Vector3DTests.cs

@ -0,0 +1,492 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial;
using MathNet.Numerics.Spatial.Euclidean3D;
using NUnit.Framework;
namespace MathNet.Numerics.Tests.Spatial.Euclidean3D
{
[TestFixture]
public class Vector3DTests
{
private const string X = "1; 0 ; 0";
private const string Y = "0; 1; 0";
private const string Z = "0; 0; 1";
private const string NegativeX = "-1; 0; 0";
private const string NegativeY = "0; -1; 0";
private const string NegativeZ = "0; 0; -1";
[Test]
public void Ctor()
{
var v = new Vector3D(1, 2, 3);
Assert.AreEqual(1, v.X);
Assert.AreEqual(2, v.Y);
Assert.AreEqual(3, v.Z);
}
[TestCase("1,2,-3", 3, "3,6,-9")]
public void OperatorMultiply(string vectorAsString, double multiplier, string expected)
{
var vector = Vector3D.Parse(vectorAsString);
AssertGeometry.AreEqual(Vector3D.Parse(expected), multiplier * vector, 1e-6);
}
[TestCase("-1,1,-1", -1, 1, -1)]
[TestCase("1, 2, 3", 1, 2, 3)]
[TestCase("1.2; 3.4; 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2;3.4;5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2 ; 3.4 ; 5.6", 1.2, 3.4, 5.6)]
[TestCase("1,2; 3,4; 5,6", 1.2, 3.4, 5.6)]
[TestCase("1.2, 3.4, 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2 3.4 5.6", 1.2, 3.4, 5.6)]
[TestCase("1.2,\u00A03.4\u00A05.6", 1.2, 3.4, 5.6)]
[TestCase("1.2\u00A03.4\u00A05.6", 1.2, 3.4, 5.6)]
[TestCase("(1.2, 3.4 5.6)", 1.2, 3.4, 5.6)]
[TestCase("1,2\u00A03,4\u00A05,6", 1.2, 3.4, 5.6)]
[TestCase("(.1, 2.3e-4,1)", 0.1, 0.00023000000000000001, 1)]
[TestCase("1.0 , 2.5,3.3", 1, 2.5, 3.3)]
[TestCase("1,0 ; 2,5;3,3", 1, 2.5, 3.3)]
[TestCase("1.0 ; 2.5;3.3", 1, 2.5, 3.3)]
[TestCase("1.0,2.5,-3.3", 1, 2.5, -3.3)]
[TestCase("1;2;3", 1, 2, 3)]
public void Parse(string text, double expectedX, double expectedY, double expectedZ)
{
Assert.AreEqual(true, Vector3D.TryParse(text, out var p));
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = Vector3D.Parse(text);
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
p = Vector3D.Parse(p.ToString());
Assert.AreEqual(expectedX, p.X);
Assert.AreEqual(expectedY, p.Y);
Assert.AreEqual(expectedZ, p.Z);
}
[TestCase("1.2")]
[TestCase("1,2; 2.3; 3")]
[TestCase("1; 2; 3; 4")]
public void ParseFails(string text)
{
Assert.AreEqual(false, Vector3D.TryParse(text, out _));
Assert.Throws<FormatException>(() => Vector3D.Parse(text));
}
[TestCase("<Vector3D X=\"1\" Y=\"-2\" Z=\"3\" />")]
[TestCase("<Vector3D Y=\"-2\" Z=\"3\" X=\"1\"/>")]
[TestCase("<Vector3D Z=\"3\" X=\"1\" Y=\"-2\" />")]
[TestCase("<Vector3D><X>1</X><Y>-2</Y><Z>3</Z></Vector3D>")]
[TestCase("<Vector3D><Y>-2</Y><Z>3</Z><X>1</X></Vector3D>")]
[TestCase("<Vector3D><Z>3</Z><X>1</X><Y>-2</Y></Vector3D>")]
public void ReadFrom(string xml)
{
using (var reader = new StringReader(xml))
{
var actual = Vector3D.ReadFrom(XmlReader.Create(reader));
Assert.AreEqual(new Vector3D(1, -2, 3), actual);
}
}
[Test]
public void ToDenseVector()
{
var v = new Vector3D(1, 2, 3);
var vector = v.ToVector();
Assert.AreEqual(3, vector.Count);
Assert.AreEqual(1, vector[0]);
Assert.AreEqual(2, vector[1]);
Assert.AreEqual(3, vector[2]);
var roundtripped = Vector3D.OfVector(vector);
Assert.AreEqual(1, roundtripped.X);
Assert.AreEqual(2, roundtripped.Y);
Assert.AreEqual(3, roundtripped.Z);
}
[TestCase("1; 0 ; 0")]
[TestCase("1; 1 ; 0")]
[TestCase("1; -1 ; 0")]
public void Orthogonal(string vs)
{
var v = Vector3D.Parse(vs);
var orthogonal = v.Orthogonal;
Assert.IsTrue(orthogonal.DotProduct(v) < 1e-6);
}
[TestCase("0; 0 ; 0")]
public void Orthogonal_BadArgument(string vs)
{
var v = Vector3D.Parse(vs);
#pragma warning disable SA1312 // Variable names must begin with lower-case letter
Assert.Throws<InvalidOperationException>(() => { var _ = v.Orthogonal; });
#pragma warning restore SA1312 // Variable names must begin with lower-case letter
}
[TestCase(X, Y, Z)]
[TestCase(X, "1, 1, 0", Z)]
[TestCase(X, NegativeY, NegativeZ)]
[TestCase(Y, Z, X)]
[TestCase(Y, "0.1, 0.1, 1", "1, 0, -0.1", Description = "Almost Z")]
[TestCase(Y, "-0.1, -0.1, 1", "1, 0, 0.1", Description = "Almost Z men minus")]
public void CrossProduct(string v1s, string v2s, string ves)
{
var vector1 = Vector3D.Parse(v1s);
var vector2 = Vector3D.Parse(v2s);
var expected = Vector3D.Parse(ves);
var crossProduct = vector1.CrossProduct(vector2);
AssertGeometry.AreEqual(expected, crossProduct, 1E-6);
}
[TestCase(X, Y, Z, 90)]
[TestCase(X, X, Z, 0)]
[TestCase(X, NegativeY, Z, -90)]
[TestCase(X, NegativeX, Z, 180)]
public void SignedAngleTo(string fromString, string toString, string axisString, double degreeAngle)
{
var fromVector = Vector3D.Parse(fromString);
var toVector = Vector3D.Parse(toString);
var aboutVector = Vector3D.Parse(axisString);
Assert.AreEqual(degreeAngle, fromVector.SignedAngleTo(toVector, aboutVector.Normalize()).Degrees, 1E-6);
}
[TestCase("1; 0; 1", Y, "-1; 0; 1", "90°")]
public void SignedAngleToArbitraryVector(string fromString, string toString, string axisString, string @as)
{
var fromVector = Vector3D.Parse(fromString);
var toVector = Vector3D.Parse(toString);
var aboutVector = Vector3D.Parse(axisString);
var angle = Angle.Parse(@as);
Assert.AreEqual(angle.Degrees, fromVector.SignedAngleTo(toVector.Normalize(), aboutVector.Normalize()).Degrees, 1E-6);
}
[TestCase(X, 5)]
[TestCase(Y, 5)]
[TestCase("1; 1; 0", 5)]
[TestCase("1; 0; 1", 5)]
[TestCase("0; 1; 1", 5)]
[TestCase("1; 1; 1", 5)]
[TestCase(X, 90)]
[TestCase(Y, 90)]
[TestCase("1; 1; 0", 90)]
[TestCase("1; 0; 1", 90)]
[TestCase("0; 1; 1", 90)]
[TestCase("1; 1; 1", 90)]
[TestCase("1; 0; 1", -90)]
[TestCase("1; 0; 1", 180)]
[TestCase("1; 0; 1", 0)]
public void SignedAngleTo_RotationAroundZ(string vectorDoubles, double rotationInDegrees)
{
var vector = Vector3D.Parse(vectorDoubles);
var angle = Angle.FromDegrees(rotationInDegrees);
var rotated = Vector3D.OfVector(Matrix3D.RotationAroundZAxis(angle).Multiply(vector.ToVector()));
var actual = vector.SignedAngleTo(rotated, Vector3D.Parse(Z).Normalize());
Assert.AreEqual(rotationInDegrees, actual.Degrees, 1E-6);
}
[TestCase(X, Z, 90, Y)]
public void Rotate(string vs, string avs, double deg, string evs)
{
var v = Vector3D.Parse(vs);
var about = Vector3D.Parse(avs);
var expected = Vector3D.Parse(evs);
var rotated = v.Rotate(about, Angle.FromDegrees(deg));
AssertGeometry.AreEqual(expected, rotated, 1E-6);
rotated = v.Rotate(about.Normalize(), Angle.FromDegrees(deg));
AssertGeometry.AreEqual(expected, rotated, 1E-6);
}
[TestCase("X", X)]
[TestCase("Y", Y)]
[TestCase("Z", Z)]
public void SignedAngleTo_Itself(string axisDummy, string aboutDoubles)
{
var vector = new Vector3D(1, 1, 1);
var aboutVector = Vector3D.Parse(aboutDoubles);
var angle = vector.SignedAngleTo(vector, aboutVector.Normalize());
Assert.AreEqual(0, angle.Degrees, 1E-6);
}
[TestCase(X, Y, "90°")]
[TestCase(Y, X, "90°")]
[TestCase(X, Z, "90°")]
[TestCase(Z, X, "90°")]
[TestCase(Y, Z, "90°")]
[TestCase(Z, Y, "90°")]
[TestCase(X, X, "0°")]
[TestCase(Y, Y, "0°")]
[TestCase(Z, Z, "0°")]
[TestCase(X, NegativeY, "90°")]
[TestCase(Y, NegativeY, "180°")]
[TestCase(Z, NegativeZ, "180°")]
[TestCase("1; 1; 0", X, "45°")]
[TestCase("1; 1; 0", Y, "45°")]
[TestCase("1; 1; 0", Z, "90°")]
[TestCase("2; 2; 0", "0; 0; 2", "90°")]
[TestCase("1; 1; 1", X, "54.74°")]
[TestCase("1; 1; 1", Y, "54.74°")]
[TestCase("1; 1; 1", Z, "54.74°")]
[TestCase("1; 0; 0", "1; 0; 0", "0°")]
[TestCase("-1; -1; 1", "-1; -1; 1", "0°")]
[TestCase("1; 1; 1", "-1; -1; -1", "180°")]
public void AngleTo(string v1s, string v2s, string ea)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s);
var angles = new[]
{
v1.AngleTo(v2),
v2.AngleTo(v1)
};
var expected = Angle.Parse(ea);
foreach (var angle in angles)
{
Assert.AreEqual(expected.Radians, angle.Radians, 1E-2);
}
}
[TestCase("5; 0; 0", "1; 0 ; 0")]
[TestCase("-5; 0; 0", "-1; 0 ; 0")]
[TestCase("0; 5; 0", "0; 1 ; 0")]
[TestCase("0; -5; 0", "0; -1 ; 0")]
[TestCase("0; 0; 5", "0; 0 ; 1")]
[TestCase("0; 0; -5", "0; 0 ; -1")]
[TestCase("2; 2; 2", "0,577350269189626; 0,577350269189626; 0,577350269189626")]
[TestCase("-2; 15; 2", "-0,131024356416084; 0,982682673120628; 0,131024356416084")]
public void Normalize(string vs, string evs)
{
var vector = Vector3D.Parse(vs);
var uv = vector.Normalize();
var expected = UnitVector3D.Parse(evs);
AssertGeometry.AreEqual(expected, uv, 1E-6);
}
[TestCase("0; 0; 0", "0; 0 ; 0")]
public void Normalize_BadArgument(string vs, string evs)
{
var vector = Vector3D.Parse(vs);
//// ReSharper disable once ReturnValueOfPureMethodIsNotUsed
Assert.Throws<InvalidOperationException>(() => vector.Normalize());
}
[TestCase("1, -1, 10", 5, "5, -5, 50")]
public void Scale(string vs, double s, string evs)
{
var v = Vector3D.Parse(vs);
var actual = v.ScaleBy(s);
AssertGeometry.AreEqual(Vector3D.Parse(evs), actual, 1e-6);
}
[TestCase("5;0;0", 5)]
[TestCase("-5;0;0", 5)]
[TestCase("-3;0;4", 5)]
public void Length(string vectorString, double length)
{
var vector = Vector3D.Parse(vectorString);
Assert.AreEqual(length, vector.Length);
}
[TestCase(X, X, true)]
[TestCase(X, NegativeX, true)]
[TestCase(Y, Y, true)]
[TestCase(Y, NegativeY, true)]
[TestCase(Z, NegativeZ, true)]
[TestCase(Z, Z, true)]
[TestCase("1;-8;7", "1;-8;7", true)]
[TestCase(X, "1;-8;7", false)]
[TestCase("1;-1.2;0", Z, false)]
public void IsParallelTo(string vector1, string vector2, bool expected)
{
var v1 = Vector3D.Parse(vector1);
var v2 = Vector3D.Parse(vector2);
Assert.AreEqual(true, v1.IsParallelTo(v1, 1E-6));
Assert.AreEqual(true, v2.IsParallelTo(v2, 1E-6));
Assert.AreEqual(expected, v1.IsParallelTo(v2, 1E-6));
Assert.AreEqual(expected, v2.IsParallelTo(v1, 1E-6));
}
[TestCase("0,1,0", "0,1, 0", 1e-10, true)]
[TestCase("0,1,0", "0,-1, 0", 1e-10, true)]
[TestCase("0,1,0", "0,1, 1", 1e-10, false)]
[TestCase("0,1,1", "0,1, 1", 1e-10, true)]
[TestCase("0,1,-1", "0,-1, 1", 1e-10, true)]
[TestCase("0,1,0", "0,1, 0.001", 1e-10, false)]
[TestCase("0,1,0", "0,1, -0.001", 1e-10, false)]
[TestCase("0,-1,0", "0,1, 0.001", 1e-10, false)]
[TestCase("0,-1,0", "0,1, -0.001", 1e-10, false)]
[TestCase("0,1,0", "0,1, 0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,1,0", "0,1, -0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,-1,0", "0,1, 0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,-1,0", "0,1, -0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,1,0.5", "0,-1, -0.5", 1e-10, true)]
public void IsParallelToByDoubleTolerance(string v1s, string v2s, double tolerance, bool expected)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s);
Assert.AreEqual(expected, v1.IsParallelTo(v2, tolerance));
Assert.AreEqual(expected, v2.IsParallelTo(v1, tolerance));
}
[TestCase("0,1,0", "0,1, 0", 1e-10, true)]
[TestCase("0,1,0", "0,-1, 0", 1e-10, true)]
[TestCase("0,1,0", "0,1, 1", 1e-10, false)]
[TestCase("0,1,1", "0,1, 1", 1e-10, true)]
[TestCase("0,1,-1", "0,-1, 1", 1e-10, true)]
[TestCase("0,1,0", "0,1, 0.001", 1e-10, false)]
[TestCase("0,1,0", "0,1, -0.001", 1e-10, false)]
[TestCase("0,-1,0", "0,1, 0.001", 1e-10, false)]
[TestCase("0,-1,0", "0,1, -0.001", 1e-10, false)]
[TestCase("0,1,0", "0,1, 0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,1,0", "0,1, -0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,-1,0", "0,1, 0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,-1,0", "0,1, -0.001", 1e-6, true, Description = "These test cases demonstrate the effect of the tolerance")]
[TestCase("0,1,0.5", "0,-1, -0.5", 1e-10, true)]
public void IsParallelToUnitVectorByDoubleTolerance(string v1s, string v2s, double tolerance, bool expected)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s).Normalize();
Assert.AreEqual(expected, v1.IsParallelTo(v2, tolerance));
Assert.AreEqual(expected, v2.IsParallelTo(v1, tolerance));
}
[TestCase("0,1,0", "0,1, 0", 1e-4, true)]
[TestCase("0,1,0", "0,-1, 0", 1e-4, true)]
[TestCase("0,1,0", "0,1, 1", 1e-4, false)]
[TestCase("0,1,1", "0,1, 1", 1e-4, true)]
[TestCase("0,1,-1", "0,-1, 1", 1e-4, true)]
[TestCase("0,1,0", "0,1, 0.001", 0.06, true)]
[TestCase("0,1,0", "0,1, -0.001", 0.06, true)]
[TestCase("0,-1,0", "0,1, 0.001", 0.06, true)]
[TestCase("0,-1,0", "0,1, -0.001", 0.06, true)]
[TestCase("0,1,0", "0,1, 0.001", 0.05, false)]
[TestCase("0,1,0", "0,1, -0.001", 0.05, false)]
[TestCase("0,-1,0", "0,1, 0.001", 0.05, false)]
[TestCase("0,-1,0", "0,1, -0.001", 0.05, false)]
[TestCase("0,1,0.5", "0,-1, -0.5", 1e-4, true)]
public void IsParallelToByAngleTolerance(string v1s, string v2s, double degreesTolerance, bool expected)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s);
Assert.AreEqual(expected, v1.IsParallelTo(v2, Angle.FromDegrees(degreesTolerance)));
Assert.AreEqual(expected, v2.IsParallelTo(v1, Angle.FromDegrees(degreesTolerance)));
}
[TestCase("0,1,0", "0,1, 0", 1e-4, true)]
[TestCase("0,1,0", "0,-1, 0", 1e-4, true)]
[TestCase("0,1,0", "0,1, 1", 1e-4, false)]
[TestCase("0,1,1", "0,1, 1", 1e-4, true)]
[TestCase("0,1,-1", "0,-1, 1", 1e-4, true)]
[TestCase("0,1,0", "0,1, 0.001", 0.06, true)]
[TestCase("0,1,0", "0,1, -0.001", 0.06, true)]
[TestCase("0,-1,0", "0,1, 0.001", 0.06, true)]
[TestCase("0,-1,0", "0,1, -0.001", 0.06, true)]
[TestCase("0,1,0", "0,1, 0.001", 0.05, false)]
[TestCase("0,1,0", "0,1, -0.001", 0.05, false)]
[TestCase("0,-1,0", "0,1, 0.001", 0.05, false)]
[TestCase("0,-1,0", "0,1, -0.001", 0.05, false)]
[TestCase("0,1,0.5", "0,-1, -0.5", 1e-4, true)]
public void IsParallelToUnitVectorByAngleTolerance(string v1s, string v2s, double degreesTolerance, bool expected)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s).Normalize();
Assert.AreEqual(expected, v1.IsParallelTo(v2, Angle.FromDegrees(degreesTolerance)));
Assert.AreEqual(expected, v2.IsParallelTo(v1, Angle.FromDegrees(degreesTolerance)));
}
[TestCase(X, X, false)]
[TestCase(NegativeX, X, false)]
[TestCase("-11;0;0", X, false)]
[TestCase("1;1;0", X, false)]
[TestCase(X, Y, true)]
[TestCase(X, Z, true)]
[TestCase(Y, X, true)]
[TestCase(Y, Z, true)]
[TestCase(Z, Y, true)]
[TestCase(Z, X, true)]
public void IsPerpendicularTo(string v1s, string v2s, bool expected)
{
var v1 = Vector3D.Parse(v1s);
var v2 = Vector3D.Parse(v2s);
Assert.AreEqual(expected, v1.IsPerpendicularTo(v2));
}
[TestCase("1, 2, 3", "1, 2, 3", 1e-4, true)]
[TestCase("1, 2, 3", "4, 5, 6", 1e-4, false)]
public void Equals(string p1s, string p2s, double tol, bool expected)
{
var v1 = Vector3D.Parse(p1s);
var v2 = Vector3D.Parse(p2s);
Assert.AreEqual(expected, v1 == v2);
Assert.AreEqual(expected, v1.Equals(v2));
Assert.AreEqual(expected, v1.Equals((object)v2));
Assert.AreEqual(expected, Equals(v1, v2));
Assert.AreEqual(expected, v1.Equals(v2, tol));
Assert.AreNotEqual(expected, v1 != v2);
}
[TestCase("-2, 0, 1e-4", null, "(-2, 0, 0.0001)", 1e-4)]
[TestCase("-2, 0, 1e-4", "F2", "(-2.00, 0.00, 0.00)", 1e-4)]
public void ToString(string vs, string format, string expected, double tolerance)
{
var v = Vector3D.Parse(vs);
var actual = v.ToString(format);
Assert.AreEqual(expected, actual);
AssertGeometry.AreEqual(v, Vector3D.Parse(actual), tolerance);
}
[Test]
public void XmlRoundtrip()
{
var p = new Vector3D(1, -2, 3);
var xml = @"<Vector3D X=""1"" Y=""-2"" Z=""3"" />";
AssertXml.XmlRoundTrips(p, xml, (expected, actual) => AssertGeometry.AreEqual(expected, actual));
}
[Test]
public void XmlContainerRoundtrip()
{
var container = new AssertXml.Container<Vector3D>
{
Value1 = new Vector3D(1, 2, 3),
Value2 = new Vector3D(4, 5, 6)
};
var expected = "<ContainerOfVector3D>\r\n" +
" <Value1 X=\"1\" Y=\"2\" Z=\"3\"></Value1>\r\n" +
" <Value2 X=\"4\" Y=\"5\" Z=\"6\"></Value2>\r\n" +
"</ContainerOfVector3D>";
var roundTrip = AssertXml.XmlSerializerRoundTrip(container, expected);
AssertGeometry.AreEqual(container.Value1, roundTrip.Value1);
AssertGeometry.AreEqual(container.Value2, roundTrip.Value2);
}
[Test]
public void XmlElements()
{
var v = new Vector3D(1, 2, 3);
var serializer = new XmlSerializer(typeof(Vector3D));
AssertGeometry.AreEqual(v, (Vector3D)serializer.Deserialize(new StringReader(@"<Vector3D><X>1</X><Y>2</Y><Z>3</Z></Vector3D>")));
}
[Test]
public void XmlContainerElements()
{
var xml = "<ContainerOfVector3D>\r\n" +
" <Value1><X>1</X><Y>2</Y><Z>3</Z></Value1>\r\n" +
" <Value2><X>4</X><Y>5</Y><Z>6</Z></Value2>\r\n" +
"</ContainerOfVector3D>";
var serializer = new XmlSerializer(typeof(AssertXml.Container<Vector3D>));
var deserialized = (AssertXml.Container<Vector3D>)serializer.Deserialize(new StringReader(xml));
AssertGeometry.AreEqual(new Vector3D(1, 2, 3), deserialized.Value1);
AssertGeometry.AreEqual(new Vector3D(4, 5, 6), deserialized.Value2);
}
}
}

2
src/Numerics/Random/RandomSeed.cs

@ -28,7 +28,7 @@ namespace MathNet.Numerics.Random
}
/// <summary>
/// Provides a seed based on an internal random number generator (crypto if available), time and unique GUIDs.
/// Provides a seed based on the internal crypto random number generator.
/// WARNING: There is only medium randomness in this seed, but quick repeated
/// calls will result in different seed values. Do not use for cryptography!
/// </summary>

397
src/Numerics/Spatial/Angle.cs

@ -0,0 +1,397 @@
using System;
using System.Globalization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial
{
/// <summary>
/// An angle
/// </summary>
[Serializable]
public struct Angle : IComparable<Angle>, IEquatable<Angle>, IFormattable, IXmlSerializable
{
/// <summary>
/// The value in radians
/// </summary>
public readonly double Radians;
/// <summary>
/// Conversion factor for converting Radians to Degrees
/// </summary>
private const double RadToDeg = 180.0 / Math.PI;
/// <summary>
/// Conversion factor for converting Degrees to Radians
/// </summary>
private const double DegToRad = Math.PI / 180.0;
/// <summary>
/// Initializes a new instance of the <see cref="Angle"/> struct.
/// </summary>
/// <param name="radians">The value in Radians</param>
private Angle(double radians)
{
this.Radians = radians;
}
/// <summary>
/// Gets the value in degrees
/// </summary>
public double Degrees => this.Radians * RadToDeg;
/// <summary>
/// Returns a value that indicates whether two specified Angles are equal.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the angles are the same; otherwise false.</returns>
public static bool operator ==(Angle left, Angle right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether two specified Angles are not equal.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the angles are different; otherwise false.</returns>
public static bool operator !=(Angle left, Angle right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a value that indicates if a specified Angles is less than another.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the first angle is less than the second angle; otherwise false.</returns>
public static bool operator <(Angle left, Angle right)
{
return left.Radians < right.Radians;
}
/// <summary>
/// Returns a value that indicates if a specified Angles is greater than another.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the first angle is greater than the second angle; otherwise false.</returns>
public static bool operator >(Angle left, Angle right)
{
return left.Radians > right.Radians;
}
/// <summary>
/// Returns a value that indicates if a specified Angles is less than or equal to another.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the first angle is less than or equal to the second angle; otherwise false.</returns>
public static bool operator <=(Angle left, Angle right)
{
return left.Radians <= right.Radians;
}
/// <summary>
/// Returns a value that indicates if a specified Angles is greater than or equal to another.
/// </summary>
/// <param name="left">The first angle to compare</param>
/// <param name="right">The second angle to compare</param>
/// <returns>True if the first angle is greater than or equal to the second angle; otherwise false.</returns>
public static bool operator >=(Angle left, Angle right)
{
return left.Radians >= right.Radians;
}
/// <summary>
/// Multiplies an Angle by a scalar
/// </summary>
/// <param name="left">The scalar.</param>
/// <param name="right">The angle.</param>
/// <returns>A new angle equal to the product of the angle and the scalar.</returns>
public static Angle operator *(double left, Angle right)
{
return new Angle(left * right.Radians);
}
/// <summary>
/// Multiplies an Angle by a scalar
/// </summary>
/// <param name="left">The angle.</param>
/// <param name="right">The scalar.</param>
/// <returns>A new angle equal to the product of the angle and the scalar.</returns>
public static Angle operator *(Angle left, double right)
{
return new Angle(left.Radians * right);
}
/// <summary>
/// Divides an Angle by a scalar
/// </summary>
/// <param name="left">The angle.</param>
/// <param name="right">The scalar.</param>
/// <returns>A new angle equal to the division of the angle by the scalar.</returns>
public static Angle operator /(Angle left, double right)
{
return new Angle(left.Radians / right);
}
/// <summary>
/// Adds two angles together
/// </summary>
/// <param name="left">The first angle.</param>
/// <param name="right">The second angle.</param>
/// <returns>A new Angle equal to the sum of the provided angles.</returns>
public static Angle operator +(Angle left, Angle right)
{
return new Angle(left.Radians + right.Radians);
}
/// <summary>
/// Subtracts the second angle from the first
/// </summary>
/// <param name="left">The first angle.</param>
/// <param name="right">The second angle.</param>
/// <returns>A new Angle equal to the difference of the provided angles.</returns>
public static Angle operator -(Angle left, Angle right)
{
return new Angle(left.Radians - right.Radians);
}
/// <summary>
/// Negates the angle
/// </summary>
/// <param name="angle">The angle to negate.</param>
/// <returns>The negated angle.</returns>
public static Angle operator -(Angle angle)
{
return new Angle(-1 * angle.Radians);
}
/// <summary>
/// Attempts to convert a string into an <see cref="Angle"/>
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">Am <see cref="Angle"/></param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out Angle result)
{
return TryParse(text, null, out result);
}
/// <summary>
/// Attempts to convert a string into an <see cref="Angle"/>
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">An <see cref="Angle"/></param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out Angle result)
{
if (Text.TryParseAngle(text, formatProvider, out result))
{
return true;
}
result = default(Angle);
return false;
}
/// <summary>
/// Attempts to convert a string into an <see cref="Angle"/>
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <returns>An <see cref="Angle"/></returns>
public static Angle Parse(string value, IFormatProvider formatProvider = null)
{
if (TryParse(value, formatProvider, out var p))
{
return p;
}
throw new FormatException($"Could not parse an Angle from the string {value}");
}
/// <summary>
/// Creates a new instance of Angle.
/// </summary>
/// <param name="value">The value in degrees.</param>
/// <returns> A new instance of the <see cref="Angle"/> struct.</returns>
public static Angle FromDegrees(double value)
{
return new Angle(value * DegToRad);
}
/// <summary>
/// Creates a new instance of Angle.
/// </summary>
/// <param name="value">The value in radians.</param>
/// <returns> A new instance of the <see cref="Angle"/> struct.</returns>
public static Angle FromRadians(double value)
{
return new Angle(value);
}
/// <summary>
/// Creates a new instance of Angle from the sexagesimal format of the angle in degrees, minutes, seconds
/// </summary>
/// <param name="degrees">The degrees of the angle</param>
/// <param name="minutes">The minutes of the angle</param>
/// <param name="seconds">The seconds of the angle</param>
/// <returns>A new instance of the <see cref="Angle"/> struct.</returns>
public static Angle FromSexagesimal(int degrees, int minutes, double seconds)
{
return Angle.FromDegrees(degrees + (minutes / 60.0F) + (seconds / 3600.0F));
}
/// <summary>
/// Creates an <see cref="Angle"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="Angle"/>.</param>
/// <returns>An <see cref="Angle"/> that contains the data read from the reader.</returns>
public static Angle ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<Angle>();
}
/// <inheritdoc />
public override string ToString()
{
return this.ToString(null, NumberFormatInfo.CurrentInfo);
}
/// <summary>
/// Returns a string representation of the Angle using the provided format
/// </summary>
/// <param name="format">a string indicating the desired format of the double.</param>
/// <returns>The string representation of this instance.</returns>
public string ToString(string format)
{
return this.ToString(format, NumberFormatInfo.CurrentInfo);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
public string ToString(IFormatProvider provider)
{
return this.ToString(null, NumberFormatInfo.GetInstance(provider));
}
/// <inheritdoc />
public string ToString(string format, IFormatProvider provider)
{
return $"{this.Radians.ToString(format, provider)}\u00A0rad";
}
/// <inheritdoc />
public int CompareTo(Angle value)
{
return this.Radians.CompareTo(value.Radians);
}
/// <summary>
/// Returns a value indicating whether this instance is equal to a specified <see cref="T:MathNet.Numerics.Spatial.Angle"/> object within the given tolerance.
/// </summary>
/// <returns>
/// true if <paramref name="other"/> represents the same angle as this instance; otherwise, false.
/// </returns>
/// <param name="other">An <see cref="T:MathNet.Numerics.Spatial.Angle"/> object to compare with this instance.</param>
/// <param name="tolerance">The maximum difference for being considered equal</param>
public bool Equals(Angle other, double tolerance)
{
return Math.Abs(this.Radians - other.Radians) < tolerance;
}
/// <summary>
/// Returns a value indicating whether this instance is equal to a specified <see cref="T:MathNet.Numerics.Spatial.Angle"/> object within the given tolerance.
/// </summary>
/// <returns>
/// true if <paramref name="other"/> represents the same angle as this instance; otherwise, false.
/// </returns>
/// <param name="other">An <see cref="T:MathNet.Numerics.Spatial.Angle"/> object to compare with this instance.</param>
/// <param name="tolerance">The maximum difference for being considered equal</param>
public bool Equals(Angle other, Angle tolerance)
{
return Math.Abs(this.Radians - other.Radians) < tolerance.Radians;
}
/// <inheritdoc />
public bool Equals(Angle other) => this.Radians.Equals(other.Radians);
/// <inheritdoc />
public override bool Equals(object obj) => obj is Angle a && this.Equals(a);
/// <inheritdoc />
public override int GetHashCode() => HashCode.Combine(this.Radians);
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.TryReadAttributeAsDouble("Value", out var value) ||
reader.TryReadAttributeAsDouble("Radians", out value))
{
reader.Skip();
this = FromRadians(value);
return;
}
if (reader.TryReadAttributeAsDouble("Degrees", out value))
{
reader.Skip();
this = FromDegrees(value);
return;
}
if (reader.Read())
{
if (reader.HasValue)
{
this = FromRadians(reader.ReadContentAsDouble());
reader.Skip();
return;
}
if (reader.MoveToContent() == XmlNodeType.Element)
{
if (reader.TryReadElementContentAsDouble("Value", out value) ||
reader.TryReadElementContentAsDouble("Radians", out value))
{
reader.Skip();
this = FromRadians(value);
return;
}
if (reader.TryReadElementContentAsDouble("Degrees", out value))
{
reader.Skip();
this = FromDegrees(value);
return;
}
}
}
throw new XmlException("Could not read an Angle");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("Value", this.Radians);
}
}
}

137
src/Numerics/Spatial/Euclidean2D/Circle2D.cs

@ -0,0 +1,137 @@
using System;
using System.Diagnostics.Contracts;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// Describes a standard 2 dimensional circle
/// </summary>
public struct Circle2D : IEquatable<Circle2D>
{
/// <summary>
/// Initializes a new instance of the <see cref="Circle2D"/> struct.
/// Creates a Circle of a given radius from a center point
/// </summary>
/// <param name="center">The location of the center</param>
/// <param name="radius">The radius of the circle</param>
public Circle2D(Point2D center, double radius)
{
this.Center = center;
this.Radius = radius;
}
/// <summary>
/// Gets the center point of the circle
/// </summary>
public Point2D Center { get; }
/// <summary>
/// Gets the radius of the circle
/// </summary>
public double Radius { get; }
/// <summary>
/// Gets the circumference of the circle
/// </summary>
[Pure]
public double Circumference => 2 * this.Radius * Math.PI;
/// <summary>
/// Gets the diameter of the circle
/// </summary>
[Pure]
public double Diameter => 2 * this.Radius;
/// <summary>
/// Gets the area of the circle
/// </summary>
[Pure]
public double Area => this.Radius * this.Radius * Math.PI;
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified circles is equal.
/// </summary>
/// <param name="left">The first circle to compare.</param>
/// <param name="right">The second circle to compare.</param>
/// <returns>True if the circles are the same; otherwise false.</returns>
public static bool operator ==(Circle2D left, Circle2D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified circles is not equal.
/// </summary>
/// <param name="left">The first circle to compare.</param>
/// <param name="right">The second circle to compare</param>
/// <returns>True if the circles are different; otherwise false.</returns>
public static bool operator !=(Circle2D left, Circle2D right)
{
return !left.Equals(right);
}
/// <summary>
/// Creates a <see cref="Circle2D"/> circle from three points which lie along its circumference.
/// Points may not be collinear
/// </summary>
/// <param name="pointA">The first point on the circle.</param>
/// <param name="pointB">The second point on the circle.</param>
/// <param name="pointC">The third point on the circle.</param>
/// <returns>A Circle which is defined by the three specified points</returns>
/// <exception cref="ArgumentException">An exception is thrown if no possible circle can be formed from the points</exception>
public static Circle2D FromPoints(Point2D pointA, Point2D pointB, Point2D pointC)
{
// ReSharper disable InconsistentNaming
var midpointAB = Point2D.MidPoint(pointA, pointB);
var midpointBC = Point2D.MidPoint(pointB, pointC);
var gradientAB = (pointB.Y - pointA.Y) / (pointB.X - pointA.X);
var gradientBC = (pointC.Y - pointB.Y) / (pointC.X - pointB.X);
var gradientl1 = -1 / gradientAB;
var gradientl2 = -1 / gradientBC;
// ReSharper restore InconsistentNaming
var denominator = gradientl2 - gradientl1;
var nominator = midpointAB.Y - (gradientl1 * midpointAB.X) + (gradientl2 * midpointBC.X) - midpointBC.Y;
var centerX = nominator / denominator;
var centerY = (gradientl1 * (centerX - midpointAB.X)) + midpointAB.Y;
var center = new Point2D(centerX, centerY);
if (double.IsNaN(center.X) || double.IsNaN(center.Y) || double.IsInfinity(center.X) || double.IsInfinity(center.Y))
{
throw new ArgumentException("Points cannot form a circle, are they collinear?");
}
return new Circle2D(center, center.DistanceTo(pointA));
}
/// <summary>
/// Returns a value to indicate if a pair of circles are equal
/// </summary>
/// <param name="c">The circle to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the points are equal; otherwise false</returns>
[Pure]
public bool Equals(Circle2D c, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(c.Radius - this.Radius) < tolerance && this.Center.Equals(c.Center, tolerance);
}
/// <inheritdoc />
[Pure]
public bool Equals(Circle2D c) => this.Radius.Equals(c.Radius) && this.Center.Equals(c.Center);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Circle2D c && this.Equals(c);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.Center, this.Radius);
}
}

270
src/Numerics/Spatial/Euclidean2D/Line2D.cs

@ -0,0 +1,270 @@
using System;
using System.Diagnostics.Contracts;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// This structure represents a line between two points in 2-space. It allows for operations such as
/// computing the length, direction, projections to, comparisons, and shifting by a vector.
/// </summary>
public struct Line2D : IEquatable<Line2D>
{
/// <summary>
/// The starting point of the line segment
/// </summary>
public readonly Point2D StartPoint;
/// <summary>
/// The end point of the line segment
/// </summary>
public readonly Point2D EndPoint;
/// <summary>
/// Initializes a new instance of the <see cref="Line2D"/> struct.
/// Throws an ArgumentException if the <paramref name="startPoint"/> is equal to the <paramref name="endPoint"/>.
/// </summary>
/// <param name="startPoint">the starting point of the line segment.</param>
/// <param name="endPoint">the ending point of the line segment</param>
public Line2D(Point2D startPoint, Point2D endPoint)
{
if (startPoint == endPoint)
{
throw new ArgumentException("The Line2D starting and ending points cannot be identical");
}
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
/// <summary>
/// Gets the distance from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public double Length => this.StartPoint.DistanceTo(this.EndPoint);
/// <summary>
/// Gets a normalized vector in the direction from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public Vector2D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize();
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(Line2D left, Line2D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(Line2D left, Line2D right)
{
return !left.Equals(right);
}
/// <summary>
/// Adds a vector to the start point and end point of the line
/// </summary>
/// <param name="offset">The vector to add</param>
/// <param name="line">The line</param>
/// <returns>A new <see cref="Line2D"/> at the adjusted points</returns>
public static Line2D operator +(Vector2D offset, Line2D line)
{
return new Line2D(line.StartPoint + offset, line.EndPoint + offset);
}
/// <summary>
/// Adds a vector to the start point and end point of the line
/// </summary>
/// <param name="line">The line</param>
/// <param name="offset">The vector to add</param>
/// <returns>A new line at the adjusted points</returns>
public static Line2D operator +(Line2D line, Vector2D offset)
{
return offset + line;
}
/// <summary>
/// Subtracts a vector from the start point and end point of the line
/// </summary>
/// <param name="line">The line</param>
/// <param name="offset">The vector to subtract</param>
/// <returns>A new line at the adjusted points</returns>
public static Line2D operator -(Line2D line, Vector2D offset)
{
return line + (-offset);
}
/// <summary>
/// Returns a new <see cref="Line2D"/> from a pair of strings which represent points.
/// See <see cref="Point2D.Parse(string, IFormatProvider)" /> for details on acceptable formats.
/// </summary>
/// <param name="startPointString">The string representation of the first point.</param>
/// <param name="endPointString">The string representation of the second point.</param>
/// <returns>A line segment from the first point to the second point.</returns>
public static Line2D Parse(string startPointString, string endPointString)
{
return new Line2D(Point2D.Parse(startPointString), Point2D.Parse(endPointString));
}
/// <summary>
/// Returns the shortest line between this line and a point.
/// </summary>
/// <param name="p">the point to create a line to</param>
/// <param name="mustStartBetweenAndEnd">If false the start point can extend beyond the start and endpoint of the line</param>
/// <returns>The shortest line between the line and the point</returns>
[Pure]
public Line2D LineTo(Point2D p, bool mustStartBetweenAndEnd)
{
return new Line2D(this.ClosestPointTo(p, mustStartBetweenAndEnd), p);
}
/// <summary>
/// Returns the closest point on the line to the given point.
/// </summary>
/// <param name="p">The point that the returned point is the closest point on the line to</param>
/// <param name="mustBeOnSegment">If true the returned point is contained by the segment ends, otherwise it can be anywhere on the projected line</param>
/// <returns>The closest point on the line to the provided point</returns>
[Pure]
public Point2D ClosestPointTo(Point2D p, bool mustBeOnSegment)
{
var v = this.StartPoint.VectorTo(p);
var dotProduct = v.DotProduct(this.Direction);
if (mustBeOnSegment)
{
if (dotProduct < 0)
{
dotProduct = 0;
}
var l = this.Length;
if (dotProduct > l)
{
dotProduct = l;
}
}
var alongVector = dotProduct * this.Direction;
return this.StartPoint + alongVector;
}
/// <summary>
/// Compute the intersection between two lines with parallelism considered by the double floating point precision
/// on the cross product of the two directions.
/// </summary>
/// <param name="other">The other line to compute the intersection with</param>
/// <returns>The point at the intersection of two lines, or null if the lines are parallel.</returns>
[Pure]
public Point2D? IntersectWith(Line2D other)
{
if (this.IsParallelTo(other))
{
return null;
}
// http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
var p = this.StartPoint;
var q = other.StartPoint;
var r = this.StartPoint.VectorTo(this.EndPoint);
var s = other.StartPoint.VectorTo(other.EndPoint);
var t = (q - p).CrossProduct(s) / r.CrossProduct(s);
return p + (t * r);
}
/// <summary>
/// Compute the intersection between two lines if the angle between them is greater than a specified
/// angle tolerance.
/// </summary>
/// <param name="other">The other line to compute the intersection with</param>
/// <param name="tolerance">The tolerance used when checking if the lines are parallel</param>
/// <returns>The point at the intersection of two lines, or null if the lines are parallel.</returns>
[Pure]
public Point2D? IntersectWith(Line2D other, Angle tolerance)
{
if (this.IsParallelTo(other, tolerance))
{
return null;
}
// http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
var p = this.StartPoint;
var q = other.StartPoint;
var r = this.StartPoint.VectorTo(this.EndPoint);
var s = other.StartPoint.VectorTo(other.EndPoint);
var t = (q - p).CrossProduct(s) / r.CrossProduct(s);
return p + (t * r);
}
/// <summary>
/// Checks to determine whether or not two lines are parallel to each other, using the dot product within
/// the double precision specified in the MathNet.Numerics package.
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <returns>True if the lines are parallel, false if they are not</returns>
[Pure]
public bool IsParallelTo(Line2D other)
{
return this.Direction.IsParallelTo(other.Direction, Precision.DoublePrecision * 2);
}
/// <summary>
/// Checks to determine whether or not two lines are parallel to each other within a specified angle tolerance
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <param name="tolerance">If the angle between line directions is less than this value, the method returns true</param>
/// <returns>True if the lines are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(Line2D other, Angle tolerance)
{
return this.Direction.IsParallelTo(other.Direction, tolerance);
}
/// <inheritdoc/>
[Pure]
public override string ToString()
{
return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}";
}
/// <inheritdoc/>
[Pure]
public bool Equals(Line2D other)
{
return this.StartPoint.Equals(other.StartPoint) && this.EndPoint.Equals(other.EndPoint);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is Line2D d && this.Equals(d);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode()
{
unchecked
{
return (this.StartPoint.GetHashCode() * 397) ^ this.EndPoint.GetHashCode();
}
}
}
}

207
src/Numerics/Spatial/Euclidean2D/LineSegment2D.cs

@ -0,0 +1,207 @@
using System;
using System.Diagnostics.Contracts;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// This structure represents a line between two points in 2-space. It allows for operations such as
/// computing the length, direction, comparisons, and shifting by a vector.
/// </summary>
public struct LineSegment2D : IEquatable<LineSegment2D>
{
/// <summary>
/// The starting point of the line segment
/// </summary>
public readonly Point2D StartPoint;
/// <summary>
/// The end point of the line segment
/// </summary>
public readonly Point2D EndPoint;
/// <summary>
/// Initializes a new instance of the <see cref="LineSegment2D"/> struct.
/// Throws an ArgumentException if the <paramref name="startPoint"/> is equal to the <paramref name="endPoint"/>.
/// </summary>
/// <param name="startPoint">the starting point of the line segment.</param>
/// <param name="endPoint">the ending point of the line segment</param>
public LineSegment2D(Point2D startPoint, Point2D endPoint)
{
if (startPoint == endPoint)
{
throw new ArgumentException("The segment starting and ending points cannot be identical");
}
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
/// <summary>
/// Gets the distance from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public double Length => this.StartPoint.DistanceTo(this.EndPoint);
/// <summary>
/// Gets a normalized vector in the direction from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public Vector2D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize();
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(LineSegment2D left, LineSegment2D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(LineSegment2D left, LineSegment2D right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a new <see cref="Line2D"/> from a pair of strings which represent points.
/// See <see cref="Point2D.Parse(string, IFormatProvider)" /> for details on acceptable formats.
/// </summary>
/// <param name="startPointString">The string representation of the first point.</param>
/// <param name="endPointString">The string representation of the second point.</param>
/// <returns>A line segment from the first point to the second point.</returns>
public static LineSegment2D Parse(string startPointString, string endPointString)
{
return new LineSegment2D(Point2D.Parse(startPointString), Point2D.Parse(endPointString));
}
/// <summary>
/// Translates a line according to a provided vector
/// </summary>
/// <param name="vector">A vector to apply</param>
/// <returns>A new translated line segment</returns>
public LineSegment2D TranslateBy(Vector2D vector)
{
var startVector = this.StartPoint.ToVector2D().Add(vector);
var endVector = this.EndPoint.ToVector2D().Add(vector);
return new LineSegment2D(new Point2D(startVector.X, startVector.Y), new Point2D(endVector.X, endVector.Y));
}
/// <summary>
/// Returns a new line segment between the closest point on this line segment and a point.
/// </summary>
/// <param name="p">the point to create a line to</param>
/// <returns>A line segment between the point and the nearest point on this segment.</returns>
[Pure]
public LineSegment2D LineTo(Point2D p)
{
return new LineSegment2D(this.ClosestPointTo(p), p);
}
/// <summary>
/// Returns the closest point on the line to the given point.
/// </summary>
/// <param name="p">The point that the returned point is the closest point on the line to</param>
/// <returns>The closest point on the line to the provided point</returns>
[Pure]
public Point2D ClosestPointTo(Point2D p)
{
var v = this.StartPoint.VectorTo(p);
var dotProduct = v.DotProduct(this.Direction);
if (dotProduct < 0)
{
dotProduct = 0;
}
var l = this.Length;
if (dotProduct > l)
{
dotProduct = l;
}
var alongVector = dotProduct * this.Direction;
return this.StartPoint + alongVector;
}
/// <summary>
/// Compute the intersection between two lines if the angle between them is greater than a specified
/// angle tolerance.
/// </summary>
/// <param name="other">The other line to compute the intersection with</param>
/// <param name="intersection">When this method returns, contains the intersection point, if the conversion succeeded, or the default point if the conversion failed.</param>
/// <param name="tolerance">The tolerance used when checking if the lines are parallel</param>
/// <returns>True if an intersection exists; otherwise false</returns>
[Pure]
public bool TryIntersect(LineSegment2D other, out Point2D intersection, Angle tolerance)
{
if (this.IsParallelTo(other, tolerance))
{
intersection = default(Point2D);
return false;
}
// http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
var p = this.StartPoint;
var q = other.StartPoint;
var r = this.StartPoint.VectorTo(this.EndPoint);
var s = other.StartPoint.VectorTo(other.EndPoint);
var t = (q - p).CrossProduct(s) / r.CrossProduct(s);
intersection = p + (t * r);
return true;
}
/// <summary>
/// Checks to determine whether or not two line segments are parallel to each other within a specified angle tolerance
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <param name="tolerance">If the angle between line directions is less than this value, the method returns true</param>
/// <returns>True if the lines are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(LineSegment2D other, Angle tolerance)
{
return this.Direction.IsParallelTo(other.Direction, tolerance);
}
/// <inheritdoc/>
[Pure]
public override string ToString()
{
return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}";
}
/// <summary>
/// Returns a value to indicate if a pair of line segments are equal
/// </summary>
/// <param name="other">The line segment to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the line segments are equal; otherwise false</returns>
[Pure]
public bool Equals(LineSegment2D other, double tolerance)
{
return this.StartPoint.Equals(other.StartPoint, tolerance) && this.EndPoint.Equals(other.EndPoint, tolerance);
}
/// <inheritdoc/>
[Pure]
public bool Equals(LineSegment2D l) => this.StartPoint.Equals(l.StartPoint) && this.EndPoint.Equals(l.EndPoint);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is LineSegment2D l && this.Equals(l);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.StartPoint, this.EndPoint);
}
}

36
src/Numerics/Spatial/Euclidean2D/Matrix2D.cs

@ -0,0 +1,36 @@
using System;
using MathNet.Numerics.LinearAlgebra.Double;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// Helper class for creating matrices for manipulating 2D-elements
/// </summary>
public static class Matrix2D
{
/// <summary>
/// Creates a rotation about the z-axis
/// </summary>
/// <param name="rotation">The angle of rotation</param>
/// <returns>A transform matrix</returns>
public static DenseMatrix Rotation(Angle rotation)
{
double c = Math.Cos(rotation.Radians);
double s = Math.Sin(rotation.Radians);
return Create(c, -s, s, c);
}
/// <summary>
/// Creates an arbitrary 2D transform
/// </summary>
/// <param name="m11">Element at m[1,1]</param>
/// <param name="m12">Element at m[1,2]</param>
/// <param name="m21">Element at m[2,1]</param>
/// <param name="m22">Element at m[2,2]</param>
/// <returns>A transform matrix</returns>
public static DenseMatrix Create(double m11, double m12, double m21, double m22)
{
return new DenseMatrix(2, 2, new[] { m11, m21, m12, m22 });
}
}
}

366
src/Numerics/Spatial/Euclidean2D/Point2D.cs

@ -0,0 +1,366 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// Represents a point in 2 dimensional space
/// </summary>
[Serializable]
public struct Point2D : IXmlSerializable, IEquatable<Point2D>, IFormattable
{
/// <summary>
/// The x coordinate
/// </summary>
public readonly double X;
/// <summary>
/// The y coordinate
/// </summary>
public readonly double Y;
/// <summary>
/// Initializes a new instance of the <see cref="Point2D"/> struct.
/// Creates a point for given coordinates (x, y)
/// </summary>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
public Point2D(double x, double y)
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Gets a point at the origin (0,0)
/// </summary>
public static Point2D Origin => new Point2D(0, 0);
/// <summary>
/// Adds a point and a vector together
/// </summary>
/// <param name="point">A point</param>
/// <param name="vector">A vector</param>
/// <returns>A new point at the summed location</returns>
public static Point2D operator +(Point2D point, Vector2D vector)
{
return new Point2D(point.X + vector.X, point.Y + vector.Y);
}
/// <summary>
/// Subtracts a vector from a point
/// </summary>
/// <param name="left">A point</param>
/// <param name="right">A vector</param>
/// <returns>A new point at the difference</returns>
public static Point2D operator -(Point2D left, Vector2D right)
{
return new Point2D(left.X - right.X, left.Y - right.Y);
}
/// <summary>
/// Subtracts the first point from the second point
/// </summary>
/// <param name="left">The first point</param>
/// <param name="right">The second point</param>
/// <returns>A vector pointing to the difference</returns>
public static Vector2D operator -(Point2D left, Point2D right)
{
return new Vector2D(left.X - right.X, left.Y - right.Y);
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified points is equal.
/// </summary>
/// <param name="left">The first point to compare</param>
/// <param name="right">The second point to compare</param>
/// <returns>True if the points are the same; otherwise false.</returns>
public static bool operator ==(Point2D left, Point2D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified points is not equal.
/// </summary>
/// <param name="left">The first point to compare</param>
/// <param name="right">The second point to compare</param>
/// <returns>True if the points are different; otherwise false.</returns>
public static bool operator !=(Point2D left, Point2D right)
{
return !left.Equals(right);
}
/// <summary>
/// Initializes a new instance of the <see cref="Point2D"/> struct.
/// Creates a point r from origin rotated a counterclockwise from X-Axis
/// </summary>
/// <param name="radius">distance from origin</param>
/// <param name="angle">the angle</param>
/// <returns>The <see cref="Point2D"/></returns>
public static Point2D FromPolar(double radius, Angle angle)
{
if (radius < 0)
{
throw new ArgumentOutOfRangeException(nameof(radius), radius, "Expected a radius greater than or equal to zero.");
}
return new Point2D(
radius * Math.Cos(angle.Radians),
radius * Math.Sin(angle.Radians));
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out Point2D result)
{
return TryParse(text, null, out result);
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out Point2D result)
{
if (Text.TryParse2D(text, formatProvider, out var x, out var y))
{
result = new Point2D(x, y);
return true;
}
result = default(Point2D);
return false;
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <returns>A point at the coordinates specified</returns>
public static Point2D Parse(string value, IFormatProvider formatProvider = null)
{
if (TryParse(value, formatProvider, out var p))
{
return p;
}
throw new FormatException($"Could not parse a Point2D from the string {value}");
}
/// <summary>
/// Creates an <see cref="Point2D"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="Point2D"/>.</param>
/// <returns>An <see cref="Point2D"/> that contains the data read from the reader.</returns>
public static Point2D ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<Point2D>();
}
/// <summary>
/// Returns the centeroid or center of mass of any set of points
/// </summary>
/// <param name="points">a list of points</param>
/// <returns>the centeroid point</returns>
public static Point2D Centroid(IEnumerable<Point2D> points)
{
return Centroid(points.ToArray());
}
/// <summary>
/// Returns the centeroid or center of mass of any set of points
/// </summary>
/// <param name="points">a list of points</param>
/// <returns>the centeroid point</returns>
public static Point2D Centroid(params Point2D[] points)
{
return new Point2D(
points.Average(point => point.X),
points.Average(point => point.Y));
}
/// <summary>
/// Returns a point midway between the provided points <paramref name="point1"/> and <paramref name="point2"/>
/// </summary>
/// <param name="point1">point A</param>
/// <param name="point2">point B</param>
/// <returns>a new point midway between the provided points</returns>
public static Point2D MidPoint(Point2D point1, Point2D point2)
{
return Centroid(point1, point2);
}
/// <summary>
/// Create a new Point2D from a Math.NET Numerics vector of length 2.
/// </summary>
/// <param name="vector"> A vector with length 2 to populate the created instance with.</param>
/// <returns> A <see cref="Point2D"/></returns>
public static Point2D OfVector(Vector<double> vector)
{
if (vector.Count != 2)
{
throw new ArgumentException("The vector length must be 2 in order to convert it to a Point2D");
}
return new Point2D(vector.At(0), vector.At(1));
}
/// <summary>
/// Applies a transform matrix to the point
/// </summary>
/// <param name="m">A transform matrix</param>
/// <returns>A new point</returns>
public Point2D TransformBy(Matrix<double> m)
{
return OfVector(m.Multiply(this.ToVector()));
}
/// <summary>
/// Gets a vector from this point to another point
/// </summary>
/// <param name="otherPoint">The point to which the vector should go</param>
/// <returns>A vector pointing to the other point.</returns>
[Pure]
public Vector2D VectorTo(Point2D otherPoint)
{
return otherPoint - this;
}
/// <summary>
/// Finds the straight line distance to another point
/// </summary>
/// <param name="otherPoint">The other point</param>
/// <returns>a distance measure</returns>
[Pure]
public double DistanceTo(Point2D otherPoint)
{
var vector = this.VectorTo(otherPoint);
return vector.Length;
}
/// <summary>
/// Converts this point into a vector from the origin
/// </summary>
/// <returns>A vector equivalent to this point</returns>
[Pure]
public Vector2D ToVector2D()
{
return new Vector2D(this.X, this.Y);
}
/// <summary>
/// Convert to a Math.NET Numerics dense vector of length 2.
/// </summary>
/// <returns> A <see cref="Vector{Double}"/> with the x and y values from this instance.</returns>
[Pure]
public Vector<double> ToVector()
{
return Vector<double>.Build.Dense(new[] { this.X, this.Y });
}
/// <inheritdoc />
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
[Pure]
public string ToString(IFormatProvider provider)
{
return this.ToString(null, provider);
}
/// <inheritdoc />
[Pure]
public string ToString(string format, IFormatProvider provider = null)
{
var numberFormatInfo = provider != null ? NumberFormatInfo.GetInstance(provider) : CultureInfo.InvariantCulture.NumberFormat;
var separator = numberFormatInfo.NumberDecimalSeparator == "," ? ";" : ",";
return $"({this.X.ToString(format, numberFormatInfo)}{separator}\u00A0{this.Y.ToString(format, numberFormatInfo)})";
}
/// <summary>
/// Returns a value to indicate if a pair of points are equal
/// </summary>
/// <param name="other">The point to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the points are equal; otherwise false</returns>
[Pure]
public bool Equals(Point2D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance;
}
/// <inheritdoc />
[Pure]
public bool Equals(Point2D other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Point2D p && this.Equals(p);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema() => null;
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.TryReadAttributeAsDouble("X", out var x) &&
reader.TryReadAttributeAsDouble("Y", out var y))
{
reader.Skip();
this = new Point2D(x, y);
return;
}
if (reader.TryReadChildElementsAsDoubles("X", "Y", out x, out y))
{
reader.Skip();
this = new Point2D(x, y);
return;
}
throw new XmlException("Could not read a Point2D");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("X", this.X);
writer.WriteAttribute("Y", this.Y);
}
}
}

313
src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs

@ -0,0 +1,313 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// The PolyLine2D class represents a 2D curve in space made up of line segments joined end-to-end, and is
/// stored as a sequential list of 2D points.
/// </summary>
public class PolyLine2D : IEquatable<PolyLine2D>
{
/// <summary>
/// Internal storage for the points
/// </summary>
private readonly List<Point2D> points;
/// <summary>
/// Initializes a new instance of the <see cref="PolyLine2D"/> class.
/// Creates a PolyLine2D from a pre-existing IEnumerable of Point2Ds
/// </summary>
/// <param name="points">A list of points.</param>
public PolyLine2D(IEnumerable<Point2D> points)
{
this.points = new List<Point2D>(points);
}
/// <summary>
/// Gets the number of vertices in the polyline.
/// </summary>
public int VertexCount => this.points.Count;
/// <summary>
/// Gets the length of the polyline as the sum of the length of the individual segments
/// </summary>
public double Length => this.GetPolyLineLength();
/// <summary>
/// Gets a list of vertices
/// </summary>
public IEnumerable<Point2D> Vertices
{
get
{
foreach (var point in this.points)
{
yield return point;
}
}
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(PolyLine2D left, PolyLine2D right)
{
return left?.Equals(right) == true;
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(PolyLine2D left, PolyLine2D right)
{
return left?.Equals(right) != true;
}
/// <summary>
/// Reduce the complexity of a manifold of points represented as an IEnumerable of Point2D objects by
/// iteratively removing all nonadjacent points which would each result in an error of less than the
/// single step tolerance if removed. Iterate until no further changes are made.
/// </summary>
/// <param name="points">A list of points.</param>
/// <param name="singleStepTolerance">The tolerance (epsilon) for comparing sameness of line segments</param>
/// <returns>A new PolyLine2D with same segments merged.</returns>
public static PolyLine2D ReduceComplexity(IEnumerable<Point2D> points, double singleStepTolerance)
{
var manifold = points.ToList();
var n = manifold.Count;
manifold = ReduceComplexitySingleStep(manifold, singleStepTolerance).ToList();
var n1 = manifold.Count;
while (n1 != n)
{
n = n1;
manifold = ReduceComplexitySingleStep(manifold, singleStepTolerance).ToList();
n1 = manifold.Count;
}
return new PolyLine2D(manifold);
}
/// <summary>
/// Get the point at a fractional distance along the curve. For instance, fraction=0.5 will return
/// the point halfway along the length of the polyline.
/// </summary>
/// <param name="fraction">The fractional length at which to compute the point</param>
/// <returns>A point a fraction of the way along the line.</returns>
public Point2D GetPointAtFractionAlongCurve(double fraction)
{
if (fraction > 1 || fraction < 0)
{
throw new ArgumentException("fraction must be between 0 and 1");
}
return this.GetPointAtLengthFromStart(fraction * this.Length);
}
/// <summary>
/// Get the point at a specified distance along the curve. A negative argument will return the first point,
/// an argument greater than the length of the curve will return the last point.
/// </summary>
/// <param name="lengthFromStart">The distance from the first point along the curve at which to return a point</param>
/// <returns>A point which is the specified distance along the line</returns>
public Point2D GetPointAtLengthFromStart(double lengthFromStart)
{
var length = this.Length;
if (lengthFromStart >= length)
{
return this.points.Last();
}
if (lengthFromStart <= 0)
{
return this.points.First();
}
double cumulativeLength = 0;
var i = 0;
while (true)
{
var nextLength = cumulativeLength + this.points[i].DistanceTo(this.points[i + 1]);
if (cumulativeLength <= lengthFromStart && nextLength > lengthFromStart)
{
var leftover = lengthFromStart - cumulativeLength;
var direction = this.points[i].VectorTo(this.points[i + 1]).Normalize();
return this.points[i] + (direction * leftover);
}
else
{
cumulativeLength = nextLength;
i++;
}
}
}
/// <summary>
/// Returns the closest point on the polyline to the given point.
/// </summary>
/// <param name="p">a point</param>
/// <returns>A point which is the closest to the given point but still on the line.</returns>
public Point2D ClosestPointTo(Point2D p)
{
var minError = double.MaxValue;
var closest = default(Point2D);
for (var i = 0; i < this.VertexCount - 1; i++)
{
var segment = new LineSegment2D(this.points[i], this.points[i + 1]);
var projected = segment.ClosestPointTo(p);
var error = p.DistanceTo(projected);
if (error < minError)
{
minError = error;
closest = projected;
}
}
return closest;
}
/// <summary>
/// Returns a value to indicate if a pair of polylines are equal
/// </summary>
/// <param name="other">The polyline to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the polylines are equal; otherwise false</returns>
[Pure]
public bool Equals(PolyLine2D other, double tolerance)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i], tolerance))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public bool Equals(PolyLine2D other)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i]))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
return obj is PolyLine2D polyLine2D &&
this.Equals(polyLine2D);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode()
{
return HashCode.CombineMany(this.points);
}
/// <summary>
/// Reduce the complexity of a manifold of points represented as an IEnumerable of Point2D objects.
/// This algorithm goes through each point in the manifold and computes the error that would be introduced
/// from the original if that point were removed. Then it removes nonadjacent points to produce a
/// reduced size manifold.
/// </summary>
/// <param name="points">A list of points</param>
/// <param name="tolerance">Tolerance (Epsilon) to apply to determine if segments are to be merged.</param>
/// <returns>A new list of points minus any segment which was merged.</returns>
private static IEnumerable<Point2D> ReduceComplexitySingleStep(IEnumerable<Point2D> points, double tolerance)
{
var manifold = points.ToList();
var errorByIndex = new double[manifold.Count];
// At this point we will loop through the list of points (excluding the first and the last) and
// examine every adjacent triplet. The middle point is tested against the segment created by
// the two end points, and the error that would result in its deletion is computed as the length
// of the point's projection onto the segment.
for (var i = 1; i < manifold.Count - 1; i++)
{
// TODO: simplify this to remove all of the value copying
var v0 = manifold[i - 1];
var v1 = manifold[i];
var v2 = manifold[i + 1];
var projected = new LineSegment2D(v0, v2).ClosestPointTo(v1);
var error = v1.VectorTo(projected).Length;
errorByIndex[i] = error;
}
// Now go through the list of errors and remove nonadjacent points with less than the error tolerance
var thinnedPoints = new List<Point2D>();
var preserveMe = 0;
for (var i = 0; i < errorByIndex.Length - 1; i++)
{
if (i == preserveMe)
{
thinnedPoints.Add(manifold[i]);
}
else
{
if (errorByIndex[i] < tolerance)
{
preserveMe = i + 1;
}
else
{
thinnedPoints.Add(manifold[i]);
}
}
}
thinnedPoints.Add(manifold.Last());
return thinnedPoints;
}
/// <summary>
/// Computes the length of the polyline by summing the lengths of the individual segments
/// </summary>
/// <returns>The length of the line</returns>
private double GetPolyLineLength()
{
double length = 0;
for (var i = 0; i < this.points.Count - 1; ++i)
{
length += this.points[i].DistanceTo(this.points[i + 1]);
}
return length;
}
}
}

334
src/Numerics/Spatial/Euclidean2D/Polygon2D.cs

@ -0,0 +1,334 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using MathNet.Numerics.Spatial.Internal;
using MathNet.Numerics.Spatial.Internal.ConvexHull;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// Class to represent a closed polygon.
/// </summary>
public class Polygon2D : IEquatable<Polygon2D>
{
/// <summary>
/// A list of vertices.
/// </summary>
private readonly ImmutableList<Point2D> points;
/// <summary>
/// A list of edges. This list is lazy loaded on demand.
/// </summary>
private ImmutableList<LineSegment2D> edges;
/// <summary>
/// Initializes a new instance of the <see cref="Polygon2D"/> class.
/// At least three points are needed to construct a polygon. If less are passed an ArgumentException is thrown.
/// </summary>
/// <param name="vertices">A list of vertices.</param>
public Polygon2D(IEnumerable<Point2D> vertices)
: this(vertices.ToArray())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Polygon2D"/> class.
/// At least three points are needed to construct a polygon. If less are passed an ArgumentException is thrown.
/// </summary>
/// <param name="vertices">A list of vertices.</param>
public Polygon2D(params Point2D[] vertices)
{
if (vertices.Length < 3)
{
throw new ArgumentException("Cannot create a polygon out of less than three points");
}
if (vertices[0].Equals(vertices[vertices.Length - 1]))
{
this.points = ImmutableList.Create(vertices.Skip(1).ToArray());
}
else
{
this.points = ImmutableList.Create(vertices);
}
}
/// <summary>
/// Gets a list of vertices
/// </summary>
public IEnumerable<Point2D> Vertices
{
get
{
foreach (var point in this.points)
{
yield return point;
}
}
}
/// <summary>
/// Gets a list of Edges
/// </summary>
public IEnumerable<LineSegment2D> Edges
{
get
{
if (this.edges == null)
{
this.PopulateEdgeList();
}
foreach (var edge in this.edges)
{
yield return edge;
}
}
}
/// <summary>
/// Gets the number of vertices in the polygon.
/// </summary>
public int VertexCount => this.points.Count;
/// <summary>
/// Returns a value that indicates whether each point in two specified polygons is equal.
/// </summary>
/// <param name="left">The first polygon to compare</param>
/// <param name="right">The second polygon to compare</param>
/// <returns>True if the polygons are the same; otherwise false.</returns>
public static bool operator ==(Polygon2D left, Polygon2D right)
{
return left?.Equals(right) == true;
}
/// <summary>
/// Returns a value that indicates whether any point in two specified polygons is not equal.
/// </summary>
/// <param name="left">The first polygon to compare</param>
/// <param name="right">The second polygon to compare</param>
/// <returns>True if the polygons are different; otherwise false.</returns>
public static bool operator !=(Polygon2D left, Polygon2D right)
{
return left?.Equals(right) != true;
}
/// <summary>
/// Compute whether or not two polygons are colliding based on whether or not the vertices of
/// either are enclosed within the shape of the other. This is a simple means of detecting collisions
/// that can fail if the two polygons are heavily overlapped in such a way that one protrudes through
/// the other and out its opposing side without any vertices being enclosed.
/// </summary>
/// <param name="a">The first polygon.</param>
/// <param name="b">The second polygon</param>
/// <returns>True if the vertices collide; otherwise false.</returns>
public static bool ArePolygonVerticesColliding(Polygon2D a, Polygon2D b)
{
return a.points.Any(b.EnclosesPoint) || b.points.Any(a.EnclosesPoint);
}
/// <summary>
/// Using algorithm from Ouellet - https://www.codeproject.com/Articles/1210225/Fast-and-improved-D-Convex-Hull-algorithm-and-its, take an IEnumerable of Point2Ds and computes the
/// two dimensional convex hull, returning it as a Polygon2D object.
/// </summary>
/// <param name="pointList">A list of points</param>
/// <param name="clockWise">
/// In which direction to return the points on the convex hull.
/// If true, clockwise. Otherwise counter clockwise
/// </param>
/// <returns>A polygon.</returns>
public static Polygon2D GetConvexHullFromPoints(IEnumerable<Point2D> pointList, bool clockWise = true)
{
int count = pointList.Count();
// Perform basic validation of the input point cloud for cases of less than
// four points being given
if (count <= 2)
{
throw new ArgumentException("Must have at least 3 points in the polygon to compute the convex hull");
}
if (count <= 3)
{
return new Polygon2D(pointList);
}
var chull = new ConvexHull(pointList, false);
chull.CalcConvexHull();
var hullPoints = chull.GetResultsAsArrayOfPoint();
// Order the hull points by angle to the centroid
var centroid = Point2D.Centroid(hullPoints);
var xAxis = new Vector2D(1, 0);
var results = (from x in hullPoints select new Tuple<Angle, Point2D>(centroid.VectorTo(x).SignedAngleTo(xAxis, clockWise), x)).ToList();
results.Sort((a, b) => a.Item1.CompareTo(b.Item1));
return new Polygon2D(from x in results select x.Item2);
}
/// <summary>
/// Test whether a point is enclosed within a polygon. Points on the polygon edges are not
/// counted as contained within the polygon.
/// </summary>
/// <param name="p">A point.</param>
/// <returns>True if the point is inside the polygon; otherwise false.</returns>
public bool EnclosesPoint(Point2D p)
{
var c = false;
for (int i = 0, j = this.points.Count - 1; i < this.points.Count; j = i++)
{
if (((this.points[i].Y > p.Y) != (this.points[j].Y > p.Y)) &&
(p.X < ((this.points[j].X - this.points[i].X) * (p.Y - this.points[i].Y) / (this.points[j].Y - this.points[i].Y)) + this.points[i].X))
{
c = !c;
}
}
return c;
}
/// <summary>
/// Creates a new polygon from the existing polygon by removing any edges whose adjacent segments are considered colinear within the provided tolerance
/// </summary>
/// <param name="singleStepTolerance">The tolerance by which adjacent edges should be considered collinear.</param>
/// <returns>A polygon</returns>
public Polygon2D ReduceComplexity(double singleStepTolerance)
{
return new Polygon2D(PolyLine2D.ReduceComplexity(this.ToPolyLine2D().Vertices, singleStepTolerance).Vertices);
}
/// <summary>
/// Returns a polygon rotated around the origin
/// </summary>
/// <param name="angle">The angle by which to rotate.</param>
/// <returns>A new polygon that has been rotated.</returns>
public Polygon2D Rotate(Angle angle)
{
var rotated = this.points.Select(t => Point2D.Origin + t.ToVector2D().Rotate(angle)).ToArray();
return new Polygon2D(rotated);
}
/// <summary>
/// Returns a new polygon which is translated (moved) by a vector
/// </summary>
/// <param name="vector">A vector.</param>
/// <returns>A new polygon that has been translated.</returns>
public Polygon2D TranslateBy(Vector2D vector)
{
var newPoints = from p in this.points select p + vector;
return new Polygon2D(newPoints);
}
/// <summary>
/// Rotate the polygon around the specified point
/// </summary>
/// <param name="angle">The angle by which to rotate</param>
/// <param name="center">A point at which to rotate around</param>
/// <returns>A new polygon that has been rotated.</returns>
public Polygon2D RotateAround(Angle angle, Point2D center)
{
// Shift to the origin
var shiftVector = center.VectorTo(Point2D.Origin);
var tempPoly = this.TranslateBy(shiftVector);
// Rotate
var rotatedPoly = tempPoly.Rotate(angle);
// Shift back
return rotatedPoly.TranslateBy(-shiftVector);
}
/// <summary>
/// Converts the polygon into a PolyLine2D
/// </summary>
/// <returns>A polyline</returns>
public PolyLine2D ToPolyLine2D()
{
var points = this.points.ToList();
points.Add(points.First());
return new PolyLine2D(points);
}
/// <summary>
/// Returns a value to indicate if a pair of polygons are equal
/// </summary>
/// <param name="other">The polygon to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the polygons are equal; otherwise false</returns>
[Pure]
public bool Equals(Polygon2D other, double tolerance)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i], tolerance))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public bool Equals(Polygon2D other)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i]))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
return obj is Polygon2D d && this.Equals(d);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode()
{
return HashCode.CombineMany(this.points);
}
/// <summary>
/// Populates the edge list
/// </summary>
private void PopulateEdgeList()
{
var localedges = new List<LineSegment2D>(this.points.Count);
for (var i = 0; i < this.points.Count - 1; i++)
{
var edge = new LineSegment2D(this.points[i], this.points[i + 1]);
localedges.Add(edge);
}
localedges.Add(new LineSegment2D(this.points[this.points.Count - 1], this.points[0])); // complete loop
this.edges = ImmutableList.Create(localedges);
}
}
}

568
src/Numerics/Spatial/Euclidean2D/Vector2D.cs

@ -0,0 +1,568 @@
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean2D
{
/// <summary>
/// A struct representing a vector in 2D space
/// </summary>
[Serializable]
public struct Vector2D : IXmlSerializable, IEquatable<Vector2D>, IFormattable
{
/// <summary>
/// The x component.
/// </summary>
public readonly double X;
/// <summary>
/// The y component.
/// </summary>
public readonly double Y;
/// <summary>
/// Initializes a new instance of the <see cref="Vector2D"/> struct.
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
public Vector2D(double x, double y)
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Gets a vector representing the X Axis
/// </summary>
public static Vector2D XAxis { get; } = new Vector2D(1, 0);
/// <summary>
/// Gets a vector representing the Y Axis
/// </summary>
public static Vector2D YAxis { get; } = new Vector2D(0, 1);
/// <summary>
/// Gets the length of the vector
/// </summary>
[Pure]
public double Length => Math.Sqrt((this.X * this.X) + (this.Y * this.Y));
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are the same; otherwise false.</returns>
public static bool operator ==(Vector2D left, Vector2D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified vectors is not equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are different; otherwise false.</returns>
public static bool operator !=(Vector2D left, Vector2D right)
{
return !left.Equals(right);
}
/// <summary>
/// Adds two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A new summed vector</returns>
public static Vector2D operator +(Vector2D left, Vector2D right)
{
return left.Add(right);
}
/// <summary>
/// Subtracts two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A new difference vector</returns>
public static Vector2D operator -(Vector2D left, Vector2D right)
{
return left.Subtract(right);
}
/// <summary>
/// Negates the vector
/// </summary>
/// <param name="v">A vector to negate</param>
/// <returns>A new negated vector</returns>
public static Vector2D operator -(Vector2D v)
{
return v.Negate();
}
/// <summary>
/// Multiplies a vector by a scalar
/// </summary>
/// <param name="d">A scalar</param>
/// <param name="v">A vector</param>
/// <returns>A scaled vector</returns>
public static Vector2D operator *(double d, Vector2D v)
{
return new Vector2D(d * v.X, d * v.Y);
}
/// <summary>
/// Multiplies a vector by a scalar
/// </summary>
/// <param name="v">A vector</param>
/// <param name="d">A scalar</param>
/// <returns>A scaled vector</returns>
public static Vector2D operator *(Vector2D v, double d)
{
return d * v;
}
/// <summary>
/// Divides a vector by a scalar
/// </summary>
/// <param name="v">A vector</param>
/// <param name="d">A scalar</param>
/// <returns>A scaled vector</returns>
public static Vector2D operator /(Vector2D v, double d)
{
return new Vector2D(v.X / d, v.Y / d);
}
/// <summary>
/// Creates a Vector from Polar coordinates
/// </summary>
/// <param name="radius">The distance of the point from the origin</param>
/// <param name="angle">The angle of the point as measured from the X Axis</param>
/// <returns>A vector.</returns>
public static Vector2D FromPolar(double radius, Angle angle)
{
if (radius < 0)
{
throw new ArgumentOutOfRangeException(nameof(radius), radius, "Expected a radius greater than or equal to zero.");
}
return new Vector2D(
radius * Math.Cos(angle.Radians),
radius * Math.Sin(angle.Radians));
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out Vector2D result)
{
return TryParse(text, null, out result);
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out Vector2D result)
{
if (Text.TryParse2D(text, formatProvider, out var x, out var y))
{
result = new Vector2D(x, y);
return true;
}
result = default(Vector2D);
return false;
}
/// <summary>
/// Attempts to convert a string of the form x,y into a point
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <returns>A point at the coordinates specified</returns>
public static Vector2D Parse(string value, IFormatProvider formatProvider = null)
{
if (TryParse(value, formatProvider, out var p))
{
return p;
}
throw new FormatException($"Could not parse a Vector2D from the string {value}");
}
/// <summary>
/// Creates an <see cref="Vector2D"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="Vector2D"/>.</param>
/// <returns>An <see cref="Vector2D"/> that contains the data read from the reader.</returns>
public static Vector2D ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<Vector2D>();
}
/// <summary>
/// Create a new <see cref="Vector2D"/> from a Math.NET Numerics vector of length 2.
/// </summary>
/// <param name="vector"> A vector with length 2 to populate the created instance with.</param>
/// <returns> A <see cref="Vector2D"/></returns>
[Pure]
public static Vector2D OfVector(Vector<double> vector)
{
if (vector.Count != 2)
{
throw new ArgumentException("The vector length must be 2 in order to convert it to a Vector2D");
}
return new Vector2D(vector.At(0), vector.At(1));
}
/// <summary>
/// Computes whether or not this vector is perpendicular to <paramref name="other"/> vector by:
/// 1. Normalizing both
/// 2. Computing the dot product.
/// 3. Comparing 1- Math.Abs(dot product) to <paramref name="tolerance"/>
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="tolerance">The tolerance for when vectors are said to be parallel</param>
/// <returns>True if the vector dot product is within the given double tolerance of unity, false if not</returns>
[Pure]
public bool IsParallelTo(Vector2D other, double tolerance = 1e-10)
{
var dp = Math.Abs(this.Normalize().DotProduct(other.Normalize()));
return Math.Abs(1 - dp) <= tolerance;
}
/// <summary>
/// Computes whether or not this vector is parallel to another vector within a given angle tolerance.
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="tolerance">The tolerance for when vectors are said to be parallel</param>
/// <returns>True if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(Vector2D other, Angle tolerance)
{
// Compute the angle between these vectors
var angle = this.AngleTo(other);
if (angle < tolerance)
{
return true;
}
// Compute the 180° opposite of the angle
return Angle.FromRadians(Math.PI) - angle < tolerance;
}
/// <summary>
/// Computes whether or not this vector is perpendicular to <paramref name="other"/> vector by:
/// 1. Normalizing both
/// 2. Computing the dot product.
/// 3. Comparing Math.Abs(dot product) to <paramref name="tolerance"/>
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="tolerance">The tolerance for when vectors are said to be parallel</param>
/// <returns>True if the vector dot product is within the given double tolerance of unity, false if not</returns>
[Pure]
public bool IsPerpendicularTo(Vector2D other, double tolerance = 1e-10)
{
return Math.Abs(this.Normalize().DotProduct(other.Normalize())) < tolerance;
}
/// <summary>
/// Computes whether or not this vector is parallel to another vector within a given angle tolerance.
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="tolerance">The tolerance for when vectors are said to be parallel</param>
/// <returns>True if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsPerpendicularTo(Vector2D other, Angle tolerance)
{
var angle = this.AngleTo(other);
const double Perpendicular = Math.PI / 2;
return Math.Abs(angle.Radians - Perpendicular) < tolerance.Radians;
}
/// <summary>
/// Compute the signed angle to another vector.
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="clockWise">Positive in clockwise direction</param>
/// <param name="returnNegative">When true and the result is > 180° a negative value is returned</param>
/// <returns>The angle between the vectors.</returns>
[Pure]
public Angle SignedAngleTo(Vector2D other, bool clockWise = false, bool returnNegative = false)
{
var sign = clockWise ? -1 : 1;
var a1 = Math.Atan2(this.Y, this.X);
if (a1 < 0)
{
a1 += 2 * Math.PI;
}
var a2 = Math.Atan2(other.Y, other.X);
if (a2 < 0)
{
a2 += 2 * Math.PI;
}
var a = sign * (a2 - a1);
if (a < 0 && !returnNegative)
{
a += 2 * Math.PI;
}
if (a > Math.PI && returnNegative)
{
a -= 2 * Math.PI;
}
return Angle.FromRadians(a);
}
/// <summary>
/// Compute the angle between this vector and another using the arccosine of the dot product.
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <returns>The angle between vectors, with a range between 0° and 180°</returns>
[Pure]
public Angle AngleTo(Vector2D other)
{
return Angle.FromRadians(
Math.Abs(
Math.Atan2(
(this.X * other.Y) - (other.X * this.Y),
(this.X * other.X) + (this.Y * other.Y))));
}
/// <summary>
/// Rotates a Vector by an angle
/// </summary>
/// <param name="angle">The angle.</param>
/// <returns>A new rotated vector.</returns>
[Pure]
public Vector2D Rotate(Angle angle)
{
var cs = Math.Cos(angle.Radians);
var sn = Math.Sin(angle.Radians);
var x = (this.X * cs) - (this.Y * sn);
var y = (this.X * sn) + (this.Y * cs);
return new Vector2D(x, y);
}
/// <summary>
/// Perform the dot product on a pair of vectors
/// </summary>
/// <param name="other">The second vector</param>
/// <returns>The result of the dot product.</returns>
[Pure]
public double DotProduct(Vector2D other)
{
return (this.X * other.X) + (this.Y * other.Y);
}
/// <summary>
/// Performs the 2D 'cross product' as if the 2D vectors were really 3D vectors in the z=0 plane, returning
/// the scalar magnitude and direction of the resulting z value.
/// Formula: (this.X * other.Y) - (this.Y * other.X)
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <returns>(this.X * other.Y) - (this.Y * other.X)</returns>
[Pure]
public double CrossProduct(Vector2D other)
{
// Though the cross product is undefined in 2D space, this is a useful mathematical operation to
// determine angular direction and to compute the area of 2D shapes
return (this.X * other.Y) - (this.Y * other.X);
}
/// <summary>
/// Projects this vector onto another vector
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <returns>A <see cref="Vector2D"/> representing this vector projected on <paramref name="other"/></returns>
[Pure]
public Vector2D ProjectOn(Vector2D other)
{
return other * (this.DotProduct(other) / other.DotProduct(other));
}
/// <summary>
/// Creates a new unit vector from the existing vector.
/// </summary>
/// <returns>A new unit vector in the same direction as the original vector</returns>
[Pure]
public Vector2D Normalize()
{
var l = this.Length;
return new Vector2D(this.X / l, this.Y / l);
}
/// <summary>
/// Scales the vector by the provided value
/// </summary>
/// <param name="d">a scaling factor</param>
/// <returns>A new scale adjusted vector</returns>
[Pure]
public Vector2D ScaleBy(double d)
{
return new Vector2D(d * this.X, d * this.Y);
}
/// <summary>
/// Returns the negative of the vector
/// </summary>
/// <returns>A new negated vector.</returns>
[Pure]
public Vector2D Negate()
{
return new Vector2D(-1 * this.X, -1 * this.Y);
}
/// <summary>
/// Subtracts a vector from this vector.
/// </summary>
/// <param name="v">A vector to subtract</param>
/// <returns>A new vector which is the difference of the current vector and the provided vector</returns>
[Pure]
public Vector2D Subtract(Vector2D v)
{
return new Vector2D(this.X - v.X, this.Y - v.Y);
}
/// <summary>
/// Adds a vector to this vector
/// </summary>
/// <param name="v">A vector to add</param>
/// <returns>A new vector which is the sum of the existing vector and the provided vector</returns>
[Pure]
public Vector2D Add(Vector2D v)
{
return new Vector2D(this.X + v.X, this.Y + v.Y);
}
/// <summary>
/// Transforms a vector by multiplying it against a provided matrix
/// </summary>
/// <param name="m">The matrix to multiply</param>
/// <returns>A new transformed vector</returns>
[Pure]
public Vector2D TransformBy(Matrix<double> m)
{
var transformed = m.Multiply(this.ToVector());
return new Vector2D(transformed.At(0), transformed.At(1));
}
/// <summary>
/// Convert to a Math.NET Numerics dense vector of length 2.
/// </summary>
/// <returns> A <see cref="Vector{Double}"/> with the x and y values from this instance.</returns>
[Pure]
public Vector<double> ToVector()
{
return Vector<double>.Build.Dense(new[] { this.X, this.Y });
}
/// <summary>
/// Compare this instance with <paramref name="other"/>
/// </summary>
/// <param name="other">The other <see cref="Vector2D"/></param>
/// <param name="tolerance">The tolerance when comparing the x and y components</param>
/// <returns>True if found to be equal.</returns>
[Pure]
public bool Equals(Vector2D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance;
}
/// <inheritdoc />
[Pure]
public bool Equals(Vector2D other) => this.X.Equals(other.X) && this.Y.Equals(other.Y);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Vector2D v && this.Equals(v);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y);
/// <inheritdoc />
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
[Pure]
public string ToString(IFormatProvider provider)
{
return this.ToString(null, provider);
}
/// <inheritdoc />
[Pure]
public string ToString(string format, IFormatProvider provider = null)
{
var numberFormatInfo = provider != null ? NumberFormatInfo.GetInstance(provider) : CultureInfo.InvariantCulture.NumberFormat;
var separator = numberFormatInfo.NumberDecimalSeparator == "," ? ";" : ",";
return $"({this.X.ToString(format, numberFormatInfo)}{separator}\u00A0{this.Y.ToString(format, numberFormatInfo)})";
}
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.TryReadAttributeAsDouble("X", out var x) &&
reader.TryReadAttributeAsDouble("Y", out var y))
{
reader.Skip();
this = new Vector2D(x, y);
return;
}
if (reader.TryReadChildElementsAsDoubles("X", "Y", out x, out y))
{
reader.Skip();
this = new Vector2D(x, y);
return;
}
throw new XmlException("Could not read a Vector2D");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("X", this.X);
writer.WriteAttribute("Y", this.Y);
}
}
}

167
src/Numerics/Spatial/Euclidean3D/Circle3D.cs

@ -0,0 +1,167 @@
using System;
using System.Diagnostics.Contracts;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// Describes a 3 dimensional circle
/// </summary>
[Serializable]
public struct Circle3D : IEquatable<Circle3D>
{
/// <summary>
/// The center of the circle
/// </summary>
public readonly Point3D CenterPoint;
/// <summary>
/// the axis of the circle
/// </summary>
public readonly UnitVector3D Axis;
/// <summary>
/// the radius of the circle
/// </summary>
public readonly double Radius;
/// <summary>
/// Initializes a new instance of the <see cref="Circle3D"/> struct.
/// Constructs a Circle3D with a given <paramref name="radius"/> at a <paramref name="centerPoint"/> orientated to the <paramref name="axis"/>
/// </summary>
/// <param name="centerPoint">The center of the circle</param>
/// <param name="axis">the axis of the circle</param>
/// <param name="radius">the radius of the circle</param>
public Circle3D(Point3D centerPoint, UnitVector3D axis, double radius)
{
this.CenterPoint = centerPoint;
this.Axis = axis;
this.Radius = radius;
}
/// <summary>
/// Gets the diameter of the circle
/// </summary>
[Pure]
public double Diameter => 2 * this.Radius;
/// <summary>
/// Gets the circumference of the circle
/// </summary>
[Pure]
public double Circumference => 2 * Math.PI * this.Radius;
/// <summary>
/// Gets the area of the circle
/// </summary>
[Pure]
public double Area => this.Radius * this.Radius * Math.PI;
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified circles is equal.
/// </summary>
/// <param name="left">The first circle to compare</param>
/// <param name="right">The second circle to compare</param>
/// <returns>True if the circles are the same; otherwise false.</returns>
public static bool operator ==(Circle3D left, Circle3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified circles is not equal.
/// </summary>
/// <param name="left">The first circle to compare</param>
/// <param name="right">The second circle to compare</param>
/// <returns>True if the circles are different; otherwise false.</returns>
public static bool operator !=(Circle3D left, Circle3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Initializes a new instance of the <see cref="Circle3D"/> struct.
/// Create a circle from three points which lie along its circumference.
/// </summary>
/// <param name="p1">The first point on the circle</param>
/// <param name="p2">The second point on the circle</param>
/// <param name="p3">The third point on the circle</param>
/// <returns>A <see cref="Circle3D"/></returns>
public static Circle3D FromPoints(Point3D p1, Point3D p2, Point3D p3)
{
// https://www.physicsforums.com/threads/equation-of-a-circle-through-3-points-in-3d-space.173847/
//// ReSharper disable InconsistentNaming
var p1p2 = p2 - p1;
var p2p3 = p3 - p2;
//// ReSharper restore InconsistentNaming
var axis = p1p2.CrossProduct(p2p3).Normalize();
var midPointA = p1 + (0.5 * p1p2);
var midPointB = p2 + (0.5 * p2p3);
var directionA = p1p2.CrossProduct(axis);
var directionB = p2p3.CrossProduct(axis);
var bisectorA = new Ray3D(midPointA, directionA);
var bisectorB = Plane3D.FromPoints(midPointB, midPointB + directionB.Normalize(), midPointB + axis);
var center = bisectorA.IntersectionWith(bisectorB);
if (center == null)
{
throw new ArgumentException("A circle cannot be created from these points, are they collinear?");
}
return new Circle3D(center.Value, axis, center.Value.DistanceTo(p1));
}
/// <summary>
/// Initializes a new instance of the <see cref="Circle3D"/> struct.
/// Create a circle from the midpoint between two points, in a direction along a specified axis
/// </summary>
/// <param name="p1">First point on the circumference of the circle</param>
/// <param name="p2">Second point on the circumference of the circle</param>
/// <param name="axis">Direction of the plane in which the circle lies</param>
/// <returns>A <see cref="Circle3D"/></returns>
public static Circle3D FromPointsAndAxis(Point3D p1, Point3D p2, UnitVector3D axis)
{
var cp = Point3D.MidPoint(p1, p2);
return new Circle3D(cp, axis, cp.DistanceTo(p1));
}
/// <summary>
/// Returns a value to indicate if a pair of circles are equal
/// </summary>
/// <param name="c">The circle to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the points are equal; otherwise false</returns>
[Pure]
public bool Equals(Circle3D c, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(c.Radius - this.Radius) < tolerance
&& this.Axis.Equals(c.Axis, tolerance)
&& this.CenterPoint.Equals(c.CenterPoint, tolerance);
}
/// <inheritdoc />
[Pure]
public bool Equals(Circle3D c)
{
return this.CenterPoint.Equals(c.CenterPoint)
&& this.Axis.Equals(c.Axis)
&& this.Radius.Equals(c.Radius);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Circle3D c && this.Equals(c);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.CenterPoint, this.Axis, this.Radius);
}
}

714
src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs

@ -0,0 +1,714 @@
using System;
using System.Diagnostics.Contracts;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A coordinate system
/// </summary>
[Serializable]
public class CoordinateSystem3D : LinearAlgebra.Double.DenseMatrix, IEquatable<CoordinateSystem3D>, IXmlSerializable
{
/// <summary>
/// A local regex pattern for 3D items
/// </summary>
private static readonly string Item3DPattern = string.Format(@"^ *\(?(?<x>{0}){1}(?<y>{0}){1}(?<z>{0})\)? *$", @"[+-]?\d*(?:[.,]\d+)?(?:[eE][+-]?\d+)?", @" *[,;] *").Trim('^', '$');
/// <summary>
/// A local regex pattern for a coordinate system
/// </summary>
private static readonly string CsPattern = string.Format(@"^ *o: *{{(?<op>{0})}} *x: *{{(?<xv>{0})}} *y: *{{(?<yv>{0})}} *z: *{{(?<zv>{0})}} *$", Item3DPattern);
/// <summary>
/// Initializes a new instance of the <see cref="CoordinateSystem3D"/> class.
/// </summary>
public CoordinateSystem3D()
: this(new Point3D(0, 0, 0), UnitVector3D.XAxis.ToVector3D(), UnitVector3D.YAxis.ToVector3D(), UnitVector3D.ZAxis.ToVector3D())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CoordinateSystem3D"/> class.
/// </summary>
/// <param name="xAxis">The x axis</param>
/// <param name="yAxis">The y axis</param>
/// <param name="zAxis">The z axis</param>
/// <param name="origin">The origin</param>
public CoordinateSystem3D(Vector3D xAxis, Vector3D yAxis, Vector3D zAxis, Point3D origin)
: this(origin, xAxis, yAxis, zAxis)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CoordinateSystem3D"/> class.
/// </summary>
/// <param name="origin">The origin</param>
/// <param name="xAxis">The x axis</param>
/// <param name="yAxis">The y axis</param>
/// <param name="zAxis">The z axis</param>
public CoordinateSystem3D(Point3D origin, UnitVector3D xAxis, UnitVector3D yAxis, UnitVector3D zAxis)
: this(origin, xAxis.ToVector3D(), yAxis.ToVector3D(), zAxis.ToVector3D())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CoordinateSystem3D"/> class.
/// </summary>
/// <param name="origin">The origin</param>
/// <param name="xAxis">The x axis</param>
/// <param name="yAxis">The y axis</param>
/// <param name="zAxis">The z axis</param>
public CoordinateSystem3D(Point3D origin, Vector3D xAxis, Vector3D yAxis, Vector3D zAxis)
: base(4)
{
this.SetColumn(0, new[] { xAxis.X, xAxis.Y, xAxis.Z, 0 });
this.SetColumn(1, new[] { yAxis.X, yAxis.Y, yAxis.Z, 0 });
this.SetColumn(2, new[] { zAxis.X, zAxis.Y, zAxis.Z, 0 });
this.SetColumn(3, new[] { origin.X, origin.Y, origin.Z, 1 });
}
////public CoordinateSystem(Vector3D x, Vector3D y, Vector3D z, Vector3D offsetToBase)
//// : this(x, y, z, offsetToBase.ToPoint3D())
////{
////}
/// <summary>
/// Initializes a new instance of the <see cref="CoordinateSystem3D"/> class.
/// </summary>
/// <param name="matrix">A matrix</param>
public CoordinateSystem3D(Matrix<double> matrix)
: base(4, 4, matrix.ToColumnMajorArray())
{
if (matrix.RowCount != 4)
{
throw new ArgumentException("RowCount must be 4");
}
if (matrix.ColumnCount != 4)
{
throw new ArgumentException("ColumnCount must be 4");
}
}
/// <summary>
/// Gets the X Axis
/// </summary>
public Vector3D XAxis
{
get
{
var row = this.SubMatrix(0, 3, 0, 1).ToRowMajorArray();
return new Vector3D(row[0], row[1], row[2]);
}
}
/// <summary>
/// Gets the Y Axis
/// </summary>
public Vector3D YAxis
{
get
{
var row = this.SubMatrix(0, 3, 1, 1).ToRowMajorArray();
return new Vector3D(row[0], row[1], row[2]);
}
}
/// <summary>
/// Gets the z Axis
/// </summary>
public Vector3D ZAxis
{
get
{
var row = this.SubMatrix(0, 3, 2, 1).ToRowMajorArray();
return new Vector3D(row[0], row[1], row[2]);
}
}
/// <summary>
/// Gets the point of origin
/// </summary>
public Point3D Origin
{
get
{
var row = this.SubMatrix(0, 3, 3, 1).ToRowMajorArray();
return new Point3D(row[0], row[1], row[2]);
}
}
/// <summary>
/// Gets the offset to origin
/// </summary>
public Vector3D OffsetToBase
{
get { return this.Origin.ToVector3D(); }
}
/// <summary>
/// Gets the base change matrix
/// </summary>
public CoordinateSystem3D BaseChangeMatrix
{
get
{
var matrix = Build.DenseOfColumnVectors(this.XAxis.ToVector(), this.YAxis.ToVector(), this.ZAxis.ToVector());
var cs = new CoordinateSystem3D(this);
cs.SetRotationSubMatrix(matrix.Transpose());
return cs;
}
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified coordinate system is equal.
/// </summary>
/// <param name="left">The first coordinate system to compare</param>
/// <param name="right">The second coordinate system to compare</param>
/// <returns>True if the coordinate system are the same; otherwise false.</returns>
public static bool operator ==(CoordinateSystem3D left, CoordinateSystem3D right)
{
return CoordinateSystem3D.Equals(left, right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified coordinate system is not equal.
/// </summary>
/// <param name="left">The first coordinate system to compare</param>
/// <param name="right">The second coordinate system to compare</param>
/// <returns>True if the coordinate systems are different; otherwise false.</returns>
public static bool operator !=(CoordinateSystem3D left, CoordinateSystem3D right)
{
return !CoordinateSystem3D.Equals(left, right);
}
/// <summary>
/// Creates a coordinate system from a string
/// </summary>
/// <param name="s">The string</param>
/// <returns>A coordinate system</returns>
public static CoordinateSystem3D Parse(string s)
{
var match = Regex.Match(s, CsPattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Singleline);
var o = Point3D.Parse(match.Groups["op"].Value);
var x = Vector3D.Parse(match.Groups["xv"].Value);
var y = Vector3D.Parse(match.Groups["yv"].Value);
var z = Vector3D.Parse(match.Groups["zv"].Value);
return new CoordinateSystem3D(o, x, y, z);
}
/// <summary>
/// Sets to the matrix of rotation that aligns the 'from' vector with the 'to' vector.
/// The optional Axis argument may be used when the two vectors are perpendicular and in opposite directions to specify a specific solution, but is otherwise ignored.
/// </summary>
/// <param name="fromVector3D">Input Vector object to align from.</param>
/// <param name="toVector3D">Input Vector object to align to.</param>
/// <param name="axis">Input Vector object. </param>
/// <returns>A rotated coordinate system </returns>
public static CoordinateSystem3D RotateTo(UnitVector3D fromVector3D, UnitVector3D toVector3D, UnitVector3D? axis = null)
{
var r = Matrix3D.RotationTo(fromVector3D, toVector3D, axis);
var coordinateSystem = new CoordinateSystem3D();
var cs = SetRotationSubMatrix(r, coordinateSystem);
return cs;
}
/// <summary>
/// Creates a coordinate system that rotates
/// </summary>
/// <param name="angle">Angle to rotate</param>
/// <param name="v">Vector to rotate about</param>
/// <returns>A rotating coordinate system</returns>
public static CoordinateSystem3D Rotation(Angle angle, UnitVector3D v)
{
var m = Build.Dense(4, 4);
m.SetSubMatrix(0, 3, 0, 3, Matrix3D.RotationAroundArbitraryVector(v, angle));
m[3, 3] = 1;
return new CoordinateSystem3D(m);
}
/// <summary>
/// Creates a coordinate system that rotates
/// </summary>
/// <param name="angle">Angle to rotate</param>
/// <param name="v">Vector to rotate about</param>
/// <returns>A rotated coordinate system</returns>
public static CoordinateSystem3D Rotation(Angle angle, Vector3D v)
{
return Rotation(angle, v.Normalize());
}
/// <summary>
/// Rotation around Z (yaw) then around Y (pitch) and then around X (roll)
/// http://en.wikipedia.org/wiki/Aircraft_principal_axes
/// </summary>
/// <param name="yaw">Rotates around Z</param>
/// <param name="pitch">Rotates around Y</param>
/// <param name="roll">Rotates around X</param>
/// <returns>A rotated coordinate system</returns>
public static CoordinateSystem3D Rotation(Angle yaw, Angle pitch, Angle roll)
{
var cs = new CoordinateSystem3D();
var yt = Yaw(yaw);
var pt = Pitch(pitch);
var rt = Roll(roll);
return rt.Transform(pt.Transform(yt.Transform(cs)));
}
/// <summary>
/// Rotates around Z
/// </summary>
/// <param name="av">An angle</param>
/// <returns>A rotated coordinate system</returns>
public static CoordinateSystem3D Yaw(Angle av)
{
return Rotation(av, UnitVector3D.ZAxis);
}
/// <summary>
/// Rotates around Y
/// </summary>
/// <param name="av">An angle</param>
/// <returns>A rotated coordinate system</returns>
public static CoordinateSystem3D Pitch(Angle av)
{
return Rotation(av, UnitVector3D.YAxis);
}
/// <summary>
/// Rotates around X
/// </summary>
/// <param name="av">An angle</param>
/// <returns>A rotated coordinate system</returns>
public static CoordinateSystem3D Roll(Angle av)
{
return Rotation(av, UnitVector3D.XAxis);
}
/// <summary>
/// Creates a coordinate system that maps from the 'from' coordinate system to the 'to' coordinate system.
/// </summary>
/// <param name="fromCs">The from coordinate system</param>
/// <param name="toCs">The to coordinate system</param>
/// <returns>A mapping coordinate system</returns>
public static CoordinateSystem3D CreateMappingCoordinateSystem(CoordinateSystem3D fromCs, CoordinateSystem3D toCs)
{
var m = toCs.Multiply(fromCs.Inverse());
m[3, 3] = 1;
return new CoordinateSystem3D(m);
}
/// <summary>
/// Sets this matrix to be the matrix that maps from the 'from' coordinate system to the 'to' coordinate system.
/// </summary>
/// <param name="fromOrigin">Input Point3D that defines the origin to map the coordinate system from.</param>
/// <param name="fromXAxis">Input Vector3D object that defines the X-axis to map the coordinate system from.</param>
/// <param name="fromYAxis">Input Vector3D object that defines the Y-axis to map the coordinate system from.</param>
/// <param name="fromZAxis">Input Vector3D object that defines the Z-axis to map the coordinate system from.</param>
/// <param name="toOrigin">Input Point3D object that defines the origin to map the coordinate system to.</param>
/// <param name="toXAxis">Input Vector3D object that defines the X-axis to map the coordinate system to.</param>
/// <param name="toYAxis">Input Vector3D object that defines the Y-axis to map the coordinate system to.</param>
/// <param name="toZAxis">Input Vector3D object that defines the Z-axis to map the coordinate system to.</param>
/// <returns>A mapping coordinate system</returns>
public static CoordinateSystem3D SetToAlignCoordinateSystems(Point3D fromOrigin, Vector3D fromXAxis, Vector3D fromYAxis, Vector3D fromZAxis, Point3D toOrigin, Vector3D toXAxis, Vector3D toYAxis, Vector3D toZAxis)
{
var cs1 = new CoordinateSystem3D(fromOrigin, fromXAxis, fromYAxis, fromZAxis);
var cs2 = new CoordinateSystem3D(toOrigin, toXAxis, toYAxis, toZAxis);
var mcs = CreateMappingCoordinateSystem(cs1, cs2);
return mcs;
}
/// <summary>
/// Creates a translation
/// </summary>
/// <param name="translation">A translation vector</param>
/// <returns>A translated coordinate system</returns>
public static CoordinateSystem3D Translation(Vector3D translation)
{
return new CoordinateSystem3D(translation.ToPoint3D(), UnitVector3D.XAxis, UnitVector3D.YAxis, UnitVector3D.ZAxis);
}
/// <summary>
/// Creates a rotating coordinate system
/// </summary>
/// <param name="r">A 3×3 matrix with the rotation portion</param>
/// <param name="coordinateSystem">A rotated coordinate system</param>
/// <returns>A rotating coordinate system</returns>
public static CoordinateSystem3D SetRotationSubMatrix(Matrix<double> r, CoordinateSystem3D coordinateSystem)
{
if (r.RowCount != 3 || r.ColumnCount != 3)
{
throw new ArgumentOutOfRangeException();
}
var cs = new CoordinateSystem3D(coordinateSystem.Origin, coordinateSystem.XAxis, coordinateSystem.YAxis, coordinateSystem.ZAxis);
cs.SetSubMatrix(0, r.RowCount, 0, r.ColumnCount, r);
return cs;
}
/// <summary>
/// Gets a rotation submatrix from a coordinate system
/// </summary>
/// <param name="coordinateSystem">a coordinate system</param>
/// <returns>A rotation matrix</returns>
public static Matrix<double> GetRotationSubMatrix(CoordinateSystem3D coordinateSystem)
{
return coordinateSystem.SubMatrix(0, 3, 0, 3);
}
////public CoordinateSystem SetCoordinateSystem(Matrix<double> matrix)
////{
//// if (matrix.ColumnCount != 4 || matrix.RowCount != 4)
//// throw new ArgumentException("Not a 4x4 matrix!");
//// return new CoordinateSystem(matrix);
////}
/// <summary>
/// Resets rotations preserves scales
/// </summary>
/// <returns>A coordinate system with reset rotation</returns>
public CoordinateSystem3D ResetRotations()
{
var x = this.XAxis.Length * UnitVector3D.XAxis;
var y = this.YAxis.Length * UnitVector3D.YAxis;
var z = this.ZAxis.Length * UnitVector3D.ZAxis;
return new CoordinateSystem3D(x, y, z, this.Origin);
}
/// <summary>
/// Rotates a coordinate system around a vector
/// </summary>
/// <param name="about">The vector</param>
/// <param name="angle">An angle</param>
/// <returns>A rotated coordinate system</returns>
public CoordinateSystem3D RotateCoordSysAroundVector(UnitVector3D about, Angle angle)
{
var rcs = Rotation(angle, about);
return rcs.Transform(this);
}
/// <summary>
/// Rotate without Reset
/// </summary>
/// <param name="yaw">The yaw</param>
/// <param name="pitch">The pitch</param>
/// <param name="roll">The roll</param>
/// <returns>A rotated coordinate system</returns>
public CoordinateSystem3D RotateNoReset(Angle yaw, Angle pitch, Angle roll)
{
var rcs = Rotation(yaw, pitch, roll);
return rcs.Transform(this);
}
/// <summary>
/// Translates a coordinate system
/// </summary>
/// <param name="v">a translation vector</param>
/// <returns>A translated coordinate system</returns>
public CoordinateSystem3D OffsetBy(Vector3D v)
{
return new CoordinateSystem3D(this.Origin + v, this.XAxis, this.YAxis, this.ZAxis);
}
/// <summary>
/// Translates a coordinate system
/// </summary>
/// <param name="v">a translation vector</param>
/// <returns>A translated coordinate system</returns>
public CoordinateSystem3D OffsetBy(UnitVector3D v)
{
return new CoordinateSystem3D(this.Origin + v, this.XAxis, this.YAxis, this.ZAxis);
}
/// <summary>
/// Transforms a ray according to this change matrix
/// </summary>
/// <param name="r">a ray</param>
/// <returns>a transformed ray</returns>
public Ray3D TransformToCoordSys(Ray3D r)
{
var p = r.ThroughPoint;
var uv = r.Direction;
// The position and the vector are transformed
var baseChangeMatrix = this.BaseChangeMatrix;
var point = baseChangeMatrix.Transform(p) + this.OffsetToBase;
var direction = uv.TransformBy((Matrix<double>)baseChangeMatrix);
return new Ray3D(point, direction);
}
/// <summary>
/// Transforms a point according to this change matrix
/// </summary>
/// <param name="p">a point</param>
/// <returns>a transformed point</returns>
public Point3D TransformToCoordSys(Point3D p)
{
var baseChangeMatrix = this.BaseChangeMatrix;
var point = baseChangeMatrix.Transform(p) + this.OffsetToBase;
return point;
}
/// <summary>
/// Transforms a ray according to the inverse of this change matrix
/// </summary>
/// <param name="r">a ray</param>
/// <returns>a transformed ray</returns>
public Ray3D TransformFromCoordSys(Ray3D r)
{
var p = r.ThroughPoint;
var uv = r.Direction;
// The position and the vector are transformed
var point = this.BaseChangeMatrix.Invert().Transform(p) + this.OffsetToBase;
var direction = this.BaseChangeMatrix.Invert().Transform(uv);
return new Ray3D(point, direction);
}
/// <summary>
/// Transforms a point according to the inverse of this change matrix
/// </summary>
/// <param name="p">a point</param>
/// <returns>a transformed point</returns>
public Point3D TransformFromCoordSys(Point3D p)
{
var point = this.BaseChangeMatrix.Invert().Transform(p) + this.OffsetToBase;
return point;
}
/// <summary>
/// Creates a rotation submatrix
/// </summary>
/// <param name="r">a matrix</param>
/// <returns>a coordinate system</returns>
public CoordinateSystem3D SetRotationSubMatrix(Matrix<double> r)
{
return SetRotationSubMatrix(r, this);
}
/// <summary>
/// Returns a translation coordinate system
/// </summary>
/// <param name="v">a vector</param>
/// <returns>a coordinate system</returns>
public CoordinateSystem3D SetTranslation(Vector3D v)
{
return new CoordinateSystem3D(v.ToPoint3D(), this.XAxis, this.YAxis, this.ZAxis);
}
/// <summary>
/// Returns a rotation sub matrix
/// </summary>
/// <returns>a rotation sub matrix</returns>
public Matrix<double> GetRotationSubMatrix()
{
return GetRotationSubMatrix(this);
}
/// <summary>
/// Transforms a vector and returns the transformed vector
/// </summary>
/// <param name="v">A vector</param>
/// <returns>A transformed vector</returns>
public Vector3D Transform(Vector3D v)
{
var v3 = Vector<double>.Build.Dense(new[] { v.X, v.Y, v.Z });
this.GetRotationSubMatrix().Multiply(v3, v3);
return new Vector3D(v3[0], v3[1], v3[2]);
}
/// <summary>
/// Transforms a vector and returns the transformed vector
/// </summary>
/// <param name="v">a unit vector</param>
/// <returns>A transformed vector</returns>
public Vector3D Transform(UnitVector3D v)
{
var v3 = Vector<double>.Build.Dense(new[] { v.X, v.Y, v.Z });
this.GetRotationSubMatrix().Multiply(v3, v3);
return new Vector3D(v3[0], v3[1], v3[2]);
}
/// <summary>
/// Transforms a point and returns the transformed point
/// </summary>
/// <param name="p">a point</param>
/// <returns>A transformed point</returns>
public Point3D Transform(Point3D p)
{
var v4 = Vector<double>.Build.Dense(new[] { p.X, p.Y, p.Z, 1 });
this.Multiply(v4, v4);
return new Point3D(v4[0], v4[1], v4[2]);
}
/// <summary>
/// Transforms a coordinate system and returns the transformed
/// </summary>
/// <param name="cs">a coordinate system</param>
/// <returns>A transformed coordinate system</returns>
public CoordinateSystem3D Transform(CoordinateSystem3D cs)
{
return new CoordinateSystem3D(this.Multiply(cs));
}
/// <summary>
/// Transforms a line segment.
/// </summary>
/// <param name="l">A line segment</param>
/// <returns>The transformed line segment</returns>
public LineSegment3D Transform(LineSegment3D l)
{
return new LineSegment3D(this.Transform(l.StartPoint), this.Transform(l.EndPoint));
}
/// <summary>
/// Transforms a ray and returns the transformed.
/// </summary>
/// <param name="ray">A ray</param>
/// <returns>A transformed ray</returns>
public Ray3D Transform(Ray3D ray)
{
return new Ray3D(this.Transform(ray.ThroughPoint), this.Transform(ray.Direction));
}
/// <summary>
/// Transforms a coordinate system
/// </summary>
/// <param name="matrix">a matrix</param>
/// <returns>A transformed coordinate system</returns>
public CoordinateSystem3D TransformBy(Matrix<double> matrix)
{
return new CoordinateSystem3D(matrix.Multiply(this));
}
/// <summary>
/// Transforms this by the coordinate system and returns the transformed.
/// </summary>
/// <param name="cs">a coordinate system</param>
/// <returns>a transformed coordinate system</returns>
public CoordinateSystem3D TransformBy(CoordinateSystem3D cs)
{
return cs.Transform(this);
}
/// <summary>
/// Inverts this coordinate system
/// </summary>
/// <returns>An inverted coordinate system</returns>
public CoordinateSystem3D Invert()
{
return new CoordinateSystem3D(this.Inverse());
}
/// <summary>
/// Returns a value to indicate if this CoordinateSystem is equivalent to a another CoordinateSystem
/// </summary>
/// <param name="other">The CoordinateSystem to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the CoordinateSystems are equal; otherwise false</returns>
[Pure]
public bool Equals(CoordinateSystem3D other, double tolerance)
{
if (this.Values.Length != other?.Values.Length)
{
return false;
}
for (var i = 0; i < this.Values.Length; i++)
{
if (Math.Abs(this.Values[i] - other.Values[i]) > tolerance)
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public bool Equals(CoordinateSystem3D other)
{
if (this.Values.Length != other?.Values.Length)
{
return false;
}
for (var i = 0; i < this.Values.Length; i++)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (this.Values[i] != other.Values[i])
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
if (obj is null)
{
return false;
}
return obj is CoordinateSystem3D cs && this.Equals(cs);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.CombineMany(this.Values);
/// <summary>
/// Returns a string representation of the coordinate system
/// </summary>
/// <returns>a string</returns>
public new string ToString()
{
return $"Origin: {this.Origin}, XAxis: {this.XAxis}, YAxis: {this.YAxis}, ZAxis: {this.ZAxis}";
}
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
var e = (XElement)XNode.ReadFrom(reader);
var xAxis = Vector3D.ReadFrom(e.SingleElementReader("XAxis"));
this.SetColumn(0, new[] { xAxis.X, xAxis.Y, xAxis.Z, 0 });
var yAxis = Vector3D.ReadFrom(e.SingleElementReader("YAxis"));
this.SetColumn(1, new[] { yAxis.X, yAxis.Y, yAxis.Z, 0 });
var zAxis = Vector3D.ReadFrom(e.SingleElementReader("ZAxis"));
this.SetColumn(2, new[] { zAxis.X, zAxis.Y, zAxis.Z, 0 });
var origin = Point3D.ReadFrom(e.SingleElementReader("Origin"));
this.SetColumn(3, new[] { origin.X, origin.Y, origin.Z, 1 });
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteElement("Origin", this.Origin);
writer.WriteElement("XAxis", this.XAxis);
writer.WriteElement("YAxis", this.YAxis);
writer.WriteElement("ZAxis", this.ZAxis);
}
}
}

344
src/Numerics/Spatial/Euclidean3D/Line3D.cs

@ -0,0 +1,344 @@
using System;
using System.Diagnostics.Contracts;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A line between two points
/// </summary>
[Serializable]
public struct Line3D : IEquatable<Line3D>, IXmlSerializable
{
/// <summary>
/// The start point of the line
/// </summary>
public readonly Point3D StartPoint;
/// <summary>
/// The end point of the line
/// </summary>
public readonly Point3D EndPoint;
/// <summary>
/// Initializes a new instance of the <see cref="Line3D"/> struct.
/// Throws an ArgumentException if the <paramref name="startPoint"/> is equal to the <paramref name="endPoint"/>.
/// </summary>
/// <param name="startPoint">The starting point of the line segment.</param>
/// <param name="endPoint">The ending point of the line segment.</param>
public Line3D(Point3D startPoint, Point3D endPoint)
{
if (startPoint == endPoint)
{
throw new ArgumentException("StartPoint == EndPoint");
}
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
/// <summary>
/// Gets distance from <see cref="StartPoint"/> to <see cref="EndPoint"/>, the length of the line
/// </summary>
[Pure]
public double Length => this.StartPoint.DistanceTo(this.EndPoint);
/// <summary>
/// Gets the direction from the <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public UnitVector3D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize();
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(Line3D left, Line3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(Line3D left, Line3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a new <see cref="Line3D"/> from a pair of strings which represent points.
/// See <see cref="Point3D.Parse(string, IFormatProvider)" /> for details on acceptable formats.
/// </summary>
/// <param name="startPoint">The string representation of the first point.</param>
/// <param name="endPoint">The string representation of the second point.</param>
/// <returns>A line segment from the first point to the second point.</returns>
public static Line3D Parse(string startPoint, string endPoint)
{
return new Line3D(Point3D.Parse(startPoint), Point3D.Parse(endPoint));
}
/// <summary>
/// Returns the shortest line between this line and a point.
/// </summary>
/// <param name="p">the point to create a line to</param>
/// <param name="mustStartBetweenStartAndEnd">If false the start point can extend beyond the start and endpoint of the line</param>
/// <returns>The shortest line between the line and the point</returns>
[Pure]
public Line3D LineTo(Point3D p, bool mustStartBetweenStartAndEnd)
{
return new Line3D(this.ClosestPointTo(p, mustStartBetweenStartAndEnd), p);
}
/// <summary>
/// Returns the closest point on the line to the given point.
/// </summary>
/// <param name="p">The point that the returned point is the closest point on the line to</param>
/// <param name="mustBeOnSegment">If true the returned point is contained by the segment ends, otherwise it can be anywhere on the projected line</param>
/// <returns>The closest point on the line to the provided point</returns>
[Pure]
public Point3D ClosestPointTo(Point3D p, bool mustBeOnSegment)
{
var v = p - this.StartPoint;
var dotProduct = v.DotProduct(this.Direction);
if (mustBeOnSegment)
{
if (dotProduct < 0)
{
dotProduct = 0;
}
if (dotProduct > this.Length)
{
dotProduct = this.Length;
}
}
var alongVector = dotProduct * this.Direction;
return this.StartPoint + alongVector;
}
/// <summary>
/// The line projected on a plane
/// </summary>
/// <param name="plane">The plane.</param>
/// <returns>A projected line.</returns>
[Pure]
public Line3D ProjectOn(Plane3D plane)
{
return plane.Project(this);
}
/// <summary>
/// Find the intersection between the line and a plane
/// </summary>
/// <param name="plane">The plane.</param>
/// <param name="tolerance">A tolerance (epsilon) to compensate for floating point error</param>
/// <returns>A point where the line and plane intersect; null if no such point exists</returns>
[Pure]
public Point3D? IntersectionWith(Plane3D plane, double tolerance = double.Epsilon)
{
return plane.IntersectionWith(this, tolerance);
}
/// <summary>
/// Checks to determine whether or not two lines are parallel to each other, using the dot product within
/// the double precision specified in the MathNet.Numerics package.
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <returns>True if the lines are parallel, false if they are not</returns>
[Pure]
public bool IsParallelTo(Line3D other)
{
return this.Direction.IsParallelTo(other.Direction, Precision.DoublePrecision * 2);
}
/// <summary>
/// Checks to determine whether or not two lines are parallel to each other within a specified angle tolerance
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <param name="angleTolerance">If the angle between line directions is less than this value, the method returns true</param>
/// <returns>True if the lines are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(Line3D other, Angle angleTolerance)
{
return this.Direction.IsParallelTo(other.Direction, angleTolerance);
}
/// <summary>
/// Computes the pair of points which represent the closest distance between this Line3D and another Line3D, with the first
/// point being the point on this Line3D, and the second point being the corresponding point on the other Line3D. If the lines
/// intersect the points will be identical, if the lines are parallel the first point will be the start point of this line.
/// </summary>
/// <param name="other">line to compute the closest points with</param>
/// <returns>A tuple of two points representing the endpoints of the shortest distance between the two lines</returns>
[Pure]
public Tuple<Point3D, Point3D> ClosestPointsBetween(Line3D other)
{
if (this.IsParallelTo(other))
{
return Tuple.Create(this.StartPoint, other.ClosestPointTo(this.StartPoint, false));
}
// http://geomalgorithms.com/a07-_distance.html
var point0 = this.StartPoint;
var u = this.Direction;
var point1 = other.StartPoint;
var v = other.Direction;
var w0 = point0 - point1;
var a = u.DotProduct(u);
var b = u.DotProduct(v);
var c = v.DotProduct(v);
var d = u.DotProduct(w0);
var e = v.DotProduct(w0);
var sc = ((b * e) - (c * d)) / ((a * c) - (b * b));
var tc = ((a * e) - (b * d)) / ((a * c) - (b * b));
return Tuple.Create(point0 + (sc * u), point1 + (tc * v));
}
/// <summary>
/// Computes the pair of points which represents the closest distance between this Line3D and another Line3D, with the option
/// of treating the lines as segments bounded by their start and end points.
/// </summary>
/// <param name="other">line to compute the closest points with</param>
/// <param name="mustBeOnSegments">if true, the lines are treated as segments bounded by the start and end point</param>
/// <returns>A tuple of two points representing the endpoints of the shortest distance between the two lines or segments</returns>
[Pure]
public Tuple<Point3D, Point3D> ClosestPointsBetween(Line3D other, bool mustBeOnSegments)
{
// If the segments are parallel and the answer must be on the segments, we can skip directly to the ending
// algorithm where the endpoints are projected onto the opposite segment and the smallest distance is
// taken. Otherwise we must first check if the infinite length line solution is valid.
// If the lines aren't parallel OR it doesn't have to be constrained to the segments
if (!this.IsParallelTo(other) || !mustBeOnSegments)
{
// Compute the unbounded result, and if mustBeOnSegments is false we can directly return the results
// since this is the same as calling the other method.
var result = this.ClosestPointsBetween(other);
if (!mustBeOnSegments)
{
return result;
}
// A point that is known to be collinear with the line start and end points is on the segment if
// its distance to both endpoints is less than the segment length. If both projected points lie
// within their segment, we can directly return the result.
if (result.Item1.DistanceTo(this.StartPoint) <= this.Length &&
result.Item1.DistanceTo(this.EndPoint) <= this.Length &&
result.Item2.DistanceTo(other.StartPoint) <= other.Length &&
result.Item2.DistanceTo(other.EndPoint) <= other.Length)
{
return result;
}
}
//// If we got here, we know that either we're doing a bounded distance on two parallel segments or one
//// of the two closest span points is outside of the segment of the line it was projected on. In either
//// case we project each of the four endpoints onto the opposite segments and select the one with the
//// smallest projected distance.
var checkPoint = other.ClosestPointTo(this.StartPoint, true);
var distance = checkPoint.DistanceTo(this.StartPoint);
var closestPair = Tuple.Create(this.StartPoint, checkPoint);
var minDistance = distance;
checkPoint = other.ClosestPointTo(this.EndPoint, true);
distance = checkPoint.DistanceTo(this.EndPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(this.EndPoint, checkPoint);
minDistance = distance;
}
checkPoint = this.ClosestPointTo(other.StartPoint, true);
distance = checkPoint.DistanceTo(other.StartPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(checkPoint, other.StartPoint);
minDistance = distance;
}
checkPoint = this.ClosestPointTo(other.EndPoint, true);
distance = checkPoint.DistanceTo(other.EndPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(checkPoint, other.EndPoint);
}
return closestPair;
}
/// <inheritdoc />
[Pure]
public bool Equals(Line3D other)
{
return this.StartPoint.Equals(other.StartPoint) && this.EndPoint.Equals(other.EndPoint);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is Line3D d && this.Equals(d);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode()
{
unchecked
{
var hashCode = this.StartPoint.GetHashCode();
hashCode = (hashCode * 397) ^ this.EndPoint.GetHashCode();
return hashCode;
}
}
/// <inheritdoc />
[Pure]
public override string ToString()
{
return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}";
}
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc/>
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.MoveToContent();
var e = (XElement)XNode.ReadFrom(reader);
this = new Line3D(
Point3D.ReadFrom(e.SingleElement("StartPoint").CreateReader()),
Point3D.ReadFrom(e.SingleElement("EndPoint").CreateReader()));
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteElement("StartPoint", this.StartPoint);
writer.WriteElement("EndPoint", this.EndPoint);
}
}
}

298
src/Numerics/Spatial/Euclidean3D/LineSegment3D.cs

@ -0,0 +1,298 @@
using System;
using System.Diagnostics.Contracts;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// This structure represents a line between two points in 3D-space. It allows for operations such as
/// computing the length, direction, comparisons, and shifting by a vector.
/// </summary>
public struct LineSegment3D : IEquatable<LineSegment3D>
{
/// <summary>
/// The starting point of the line segment
/// </summary>
public readonly Point3D StartPoint;
/// <summary>
/// The end point of the line segment
/// </summary>
public readonly Point3D EndPoint;
/// <summary>
/// Initializes a new instance of the <see cref="LineSegment3D"/> struct.
/// Throws an ArgumentException if the <paramref name="startPoint"/> is equal to the <paramref name="endPoint"/>.
/// </summary>
/// <param name="startPoint">the starting point of the line segment.</param>
/// <param name="endPoint">the ending point of the line segment</param>
public LineSegment3D(Point3D startPoint, Point3D endPoint)
{
if (startPoint == endPoint)
{
throw new ArgumentException("The segment starting and ending points cannot be identical");
}
this.StartPoint = startPoint;
this.EndPoint = endPoint;
}
/// <summary>
/// Gets the distance from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public double Length => this.StartPoint.DistanceTo(this.EndPoint);
/// <summary>
/// Gets a normalized vector in the direction from <see cref="StartPoint"/> to <see cref="EndPoint"/>
/// </summary>
[Pure]
public UnitVector3D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize();
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(LineSegment3D left, LineSegment3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(LineSegment3D left, LineSegment3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a new <see cref="LineSegment3D"/> from a pair of strings which represent points.
/// See <see cref="Point3D" /> for details on acceptable formats.
/// </summary>
/// <param name="startPointString">The string representation of the first point.</param>
/// <param name="endPointString">The string representation of the second point.</param>
/// <returns>A line segment from the first point to the second point.</returns>
public static LineSegment3D Parse(string startPointString, string endPointString)
{
return new LineSegment3D(Point3D.Parse(startPointString), Point3D.Parse(endPointString));
}
/// <summary>
/// Translates a line according to a provided vector
/// </summary>
/// <param name="vector">A vector to apply</param>
/// <returns>A new translated line segment</returns>
public LineSegment3D TranslateBy(Vector3D vector)
{
return new LineSegment3D(this.StartPoint + vector, this.EndPoint + vector);
}
/// <summary>
/// Returns the closest point on the line segment to the given point.
/// </summary>
/// <param name="p">The point that the returned point is the closest point on the line to</param>
/// <returns>The closest point on the line to the provided point</returns>
[Pure]
public Point3D ClosestPointTo(Point3D p)
{
var v = this.StartPoint.VectorTo(p);
var dotProduct = v.DotProduct(this.Direction);
if (dotProduct < 0)
{
dotProduct = 0;
}
if (dotProduct > this.Length)
{
dotProduct = this.Length;
}
var alongVector = dotProduct * this.Direction;
return this.StartPoint + alongVector;
}
/// <summary>
/// Returns a new line segment between the closest point on this line segment and a point.
/// </summary>
/// <param name="p">the point to create a line to</param>
/// <returns>A line segment between the nearest point on this segment and the provided point.</returns>
[Pure]
public LineSegment3D LineTo(Point3D p)
{
return new LineSegment3D(this.ClosestPointTo(p), p);
}
/// <summary>
/// Computes the pair of points which represents the closest distance between this Line3D and another Line3D, with the option
/// of treating the lines as segments bounded by their start and end points.
/// </summary>
/// <param name="other">line to compute the closest points with</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <param name="closestLine">A line representing the endpoints of the shortest distance between the two segments</param>
/// <returns>True if a line could be found, false if the lines intersect</returns>
[Pure]
public bool TryShortestLineTo(LineSegment3D other, Angle tolerance, out LineSegment3D closestLine)
{
// If the segments are parallel and the answer must be on the segments, we can skip directly to the ending
// algorithm where the endpoints are projected onto the opposite segment and the smallest distance is
// taken. Otherwise we must first check if the infinite length line solution is valid.
// If the lines aren't parallel
if (!this.IsParallelTo(other, tolerance))
{
// Compute the unbounded result
var result = this.ClosestPointsBetweenLines(other, tolerance);
// A point that is known to be collinear with the line start and end points is on the segment if
// its distance to both endpoints is less than the segment length. If both projected points lie
// within their segment, we can directly return the result.
if (result.Item1.DistanceTo(this.StartPoint) <= this.Length &&
result.Item1.DistanceTo(this.EndPoint) <= this.Length &&
result.Item2.DistanceTo(other.StartPoint) <= other.Length &&
result.Item2.DistanceTo(other.EndPoint) <= other.Length)
{
closestLine = new LineSegment3D(result.Item1, result.Item2);
return true;
}
}
//// If we got here, we know that either we're doing a bounded distance on two parallel segments or one
//// of the two closest span points is outside of the segment of the line it was projected on. In either
//// case we project each of the four endpoints onto the opposite segments and select the one with the
//// smallest projected distance.
var checkPoint = other.ClosestPointTo(this.StartPoint);
var distance = checkPoint.DistanceTo(this.StartPoint);
var closestPair = Tuple.Create(this.StartPoint, checkPoint);
var minDistance = distance;
checkPoint = other.ClosestPointTo(this.EndPoint);
distance = checkPoint.DistanceTo(this.EndPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(this.EndPoint, checkPoint);
minDistance = distance;
}
checkPoint = this.ClosestPointTo(other.StartPoint);
distance = checkPoint.DistanceTo(other.StartPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(checkPoint, other.StartPoint);
minDistance = distance;
}
checkPoint = this.ClosestPointTo(other.EndPoint);
distance = checkPoint.DistanceTo(other.EndPoint);
if (distance < minDistance)
{
closestPair = Tuple.Create(checkPoint, other.EndPoint);
}
if (closestPair.Item1 == closestPair.Item2)
{
closestLine = default(LineSegment3D);
return false;
}
closestLine = new LineSegment3D(closestPair.Item1, closestPair.Item2);
return true;
}
/// <summary>
/// Checks to determine whether or not two line segments are parallel to each other within a specified angle tolerance
/// </summary>
/// <param name="other">The other line to check this one against</param>
/// <param name="tolerance">If the angle between line directions is less than this value, the method returns true</param>
/// <returns>True if the lines are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(LineSegment3D other, Angle tolerance)
{
return this.Direction.IsParallelTo(other.Direction, tolerance);
}
/// <inheritdoc/>
[Pure]
public override string ToString()
{
return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}";
}
/// <summary>
/// Returns a value to indicate if a pair of line segments are equal
/// </summary>
/// <param name="other">The line segment to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the line segments are equal; otherwise false</returns>
[Pure]
public bool Equals(LineSegment3D other, double tolerance)
{
return this.StartPoint.Equals(other.StartPoint, tolerance) && this.EndPoint.Equals(other.EndPoint, tolerance);
}
/// <inheritdoc/>
[Pure]
public bool Equals(LineSegment3D l) => this.StartPoint.Equals(l.StartPoint) && this.EndPoint.Equals(l.EndPoint);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is LineSegment3D l && this.Equals(l);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.StartPoint, this.EndPoint);
/// <summary>
/// Extends the segment to a infinite line and finds the closest point on that line to the provided point.
/// </summary>
/// <param name="p">a point</param>
/// <returns>A point on the infinite line which extends the segment</returns>
[Pure]
private Point3D ClosestLinePointTo(Point3D p)
{
var alongVector = this.StartPoint.VectorTo(p).DotProduct(this.Direction) * this.Direction;
return this.StartPoint + alongVector;
}
/// <summary>
/// Computes the pair of points which represent the closest distance between this Line3D and another Line3D, with the first
/// point being the point on this Line3D, and the second point being the corresponding point on the other Line3D. If the lines
/// intersect the points will be identical, if the lines are parallel the first point will be the start point of this line.
/// </summary>
/// <param name="other">line to compute the closest points with</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>A tuple of two points representing the endpoints of the shortest distance between the two lines</returns>
[Pure]
private Tuple<Point3D, Point3D> ClosestPointsBetweenLines(LineSegment3D other, Angle tolerance)
{
if (this.IsParallelTo(other, tolerance))
{
return Tuple.Create(this.StartPoint, other.ClosestLinePointTo(this.StartPoint));
}
// http://geomalgorithms.com/a07-_distance.html
var point0 = this.StartPoint;
var u = this.Direction;
var point1 = other.StartPoint;
var v = other.Direction;
var w0 = point0 - point1;
var a = u.DotProduct(u);
var b = u.DotProduct(v);
var c = v.DotProduct(v);
var d = u.DotProduct(w0);
var e = v.DotProduct(w0);
var sc = ((b * e) - (c * d)) / ((a * c) - (b * b));
var tc = ((a * e) - (b * d)) / ((a * c) - (b * b));
return Tuple.Create(point0 + (sc * u), point1 + (tc * v));
}
}
}

126
src/Numerics/Spatial/Euclidean3D/Matrix3D.cs

@ -0,0 +1,126 @@
using System;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// Helper class for working with 3D matrixes
/// </summary>
public static class Matrix3D
{
/// <summary>
/// Creates a rotation matrix around the X axis
/// </summary>
/// <param name="angle">The angle to rotate</param>
/// <returns>A rotation matrix</returns>
public static DenseMatrix RotationAroundXAxis(Angle angle)
{
var rotationMatrix = new DenseMatrix(3, 3);
rotationMatrix[0, 0] = 1;
rotationMatrix[1, 1] = Math.Cos(angle.Radians);
rotationMatrix[1, 2] = -Math.Sin(angle.Radians);
rotationMatrix[2, 1] = Math.Sin(angle.Radians);
rotationMatrix[2, 2] = Math.Cos(angle.Radians);
return rotationMatrix;
}
/// <summary>
/// Creates a rotation matrix around the Y axis
/// </summary>
/// <param name="angle">The angle to rotate</param>
/// <returns>A rotation matrix</returns>
public static DenseMatrix RotationAroundYAxis(Angle angle)
{
var rotationMatrix = new DenseMatrix(3, 3);
rotationMatrix[0, 0] = Math.Cos(angle.Radians);
rotationMatrix[0, 2] = Math.Sin(angle.Radians);
rotationMatrix[1, 1] = 1;
rotationMatrix[2, 0] = -Math.Sin(angle.Radians);
rotationMatrix[2, 2] = Math.Cos(angle.Radians);
return rotationMatrix;
}
/// <summary>
/// Creates a rotation matrix around the Z axis
/// </summary>
/// <param name="angle">The angle to rotate</param>
/// <returns>A rotation matrix</returns>
public static Matrix<double> RotationAroundZAxis(Angle angle)
{
var rotationMatrix = new DenseMatrix(3, 3);
rotationMatrix[0, 0] = Math.Cos(angle.Radians);
rotationMatrix[0, 1] = -Math.Sin(angle.Radians);
rotationMatrix[1, 0] = Math.Sin(angle.Radians);
rotationMatrix[1, 1] = Math.Cos(angle.Radians);
rotationMatrix[2, 2] = 1;
return rotationMatrix;
}
/// <summary>
/// Sets to the matrix of rotation that would align the 'from' vector with the 'to' vector.
/// The optional Axis argument may be used when the two vectors are parallel and in opposite directions to specify a specific solution, but is otherwise ignored.
/// </summary>
/// <param name="fromVector">Input Vector object to align from.</param>
/// <param name="toVector">Input Vector object to align to.</param>
/// <param name="axis">Input Vector object.</param>
/// <returns>A transform matrix</returns>
public static Matrix<double> RotationTo(
Vector3D fromVector,
Vector3D toVector,
UnitVector3D? axis = null)
{
return RotationTo(fromVector.Normalize(), toVector.Normalize(), axis);
}
/// <summary>
/// Sets to the matrix of rotation that would align the 'from' vector with the 'to' vector.
/// The optional Axis argument may be used when the two vectors are parallel and in opposite directions to specify a specific solution, but is otherwise ignored.
/// </summary>
/// <param name="fromVector">Input Vector object to align from.</param>
/// <param name="toVector">Input Vector object to align to.</param>
/// <param name="axis">Input Vector object. </param>
/// <returns>A transform matrix</returns>
public static Matrix<double> RotationTo(UnitVector3D fromVector, UnitVector3D toVector, UnitVector3D? axis = null)
{
if (fromVector == toVector)
{
return DenseMatrix.CreateIdentity(3);
}
if (fromVector.IsParallelTo(toVector))
{
if (axis == null)
{
axis = fromVector.Orthogonal;
}
}
else
{
axis = fromVector.CrossProduct(toVector);
}
var signedAngleTo = fromVector.SignedAngleTo(toVector, axis.Value);
return RotationAroundArbitraryVector(axis.Value, signedAngleTo);
}
/// <summary>
/// Creates a rotation matrix around an arbitrary vector
/// </summary>
/// <param name="aboutVector">The vector</param>
/// <param name="angle">Angle in degrees</param>
/// <returns>A transform matrix</returns>
public static Matrix<double> RotationAroundArbitraryVector(UnitVector3D aboutVector, Angle angle)
{
// http://en.wikipedia.org/wiki/Rotation_matrix
var unitTensorProduct = aboutVector.GetUnitTensorProduct();
var crossproductMatrix = aboutVector.CrossProductMatrix; // aboutVector.Clone().CrossProduct(aboutVector.Clone());
var r1 = DenseMatrix.CreateIdentity(3).Multiply(Math.Cos(angle.Radians));
var r2 = crossproductMatrix.Multiply(Math.Sin(angle.Radians));
var r3 = unitTensorProduct.Multiply(1 - Math.Cos(angle.Radians));
var totalR = r1.Add(r2).Add(r3);
return totalR;
}
}
}

498
src/Numerics/Spatial/Euclidean3D/Plane3D.cs

@ -0,0 +1,498 @@
using System;
using System.Diagnostics.Contracts;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A geometric plane
/// </summary>
[Serializable]
public struct Plane3D : IEquatable<Plane3D>, IXmlSerializable
{
/// <summary>
/// The normal vector of the Plane.
/// </summary>
public readonly UnitVector3D Normal;
/// <summary>
/// The distance to the Plane along its normal from the origin.
/// </summary>
public readonly double D;
/// <summary>
/// Initializes a new instance of the <see cref="Plane3D"/> struct.
/// Constructs a Plane from the X, Y, and Z components of its normal, and its distance from the origin on that normal.
/// </summary>
/// <param name="x">The X-component of the normal.</param>
/// <param name="y">The Y-component of the normal.</param>
/// <param name="z">The Z-component of the normal.</param>
/// <param name="d">The distance of the Plane along its normal from the origin.</param>
public Plane3D(double x, double y, double z, double d)
: this(UnitVector3D.Create(x, y, z), -d)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Plane3D"/> struct.
/// Constructs a Plane from the given normal and distance along the normal from the origin.
/// </summary>
/// <param name="normal">The Plane's normal vector.</param>
/// <param name="offset">The Plane's distance from the origin along its normal vector.</param>
public Plane3D(UnitVector3D normal, double offset = 0)
{
this.Normal = normal;
this.D = -offset;
}
/// <summary>
/// Initializes a new instance of the <see cref="Plane3D"/> struct.
/// Constructs a Plane from the given normal and distance along the normal from the origin.
/// </summary>
/// <param name="normal">The Plane's normal vector.</param>
/// <param name="rootPoint">A point in the plane.</param>
public Plane3D(UnitVector3D normal, Point3D rootPoint)
: this(normal, normal.DotProduct(rootPoint))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Plane3D"/> struct.
/// Constructs a Plane from the given normal and distance along the normal from the origin.
/// </summary>
/// <param name="normal">The Plane's normal vector.</param>
/// <param name="rootPoint">A point in the plane.</param>
public Plane3D(Point3D rootPoint, UnitVector3D normal)
: this(normal, normal.DotProduct(rootPoint))
{
}
/// <summary>
/// Gets the <see cref="Normal"/> x component.
/// </summary>
public double A => this.Normal.X;
/// <summary>
/// Gets the <see cref="Normal"/> y component.
/// </summary>
public double B => this.Normal.Y;
/// <summary>
/// Gets the <see cref="Normal"/> y component.
/// </summary>
public double C => this.Normal.Z;
/// <summary>
/// Gets the point on the plane closest to origin.
/// </summary>
public Point3D RootPoint => (-this.D * this.Normal).ToPoint3D();
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified geometric planes is equal.
/// </summary>
/// <param name="left">The first plane to compare.</param>
/// <param name="right">The second plane to compare.</param>
/// <returns>True if the geometric planes are the same; otherwise false.</returns>
public static bool operator ==(Plane3D left, Plane3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified geometric planes is not equal.
/// </summary>
/// <param name="left">The first plane to compare.</param>
/// <param name="right">The second plane to compare.</param>
/// <returns>True if the geometric planes are different; otherwise false.</returns>
public static bool operator !=(Plane3D left, Plane3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Initializes a new instance of the <see cref="Plane3D"/> struct.
/// Creates a plane that contains the three given points.
/// </summary>
/// <param name="p1">The first point on the plane.</param>
/// <param name="p2">The second point on the plane.</param>
/// <param name="p3">The third point on the plane.</param>
/// <returns>The plane containing the three points.</returns>
public static Plane3D FromPoints(Point3D p1, Point3D p2, Point3D p3)
{
// http://www.had2know.com/academics/equation-plane-through-3-points.html
if (p1 == p2 || p1 == p3 || p2 == p3)
{
throw new ArgumentException("Must use three different points");
}
var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
var v2 = new Vector3D(p3.X - p1.X, p3.Y - p1.Y, p3.Z - p1.Z);
var cross = v1.CrossProduct(v2);
if (cross.Length <= float.Epsilon)
{
throw new ArgumentException("The 3 points should not be on the same line");
}
return new Plane3D(cross.Normalize(), p1);
}
/// <summary>
/// Returns a point of intersection between three planes
/// </summary>
/// <param name="plane1">The first plane</param>
/// <param name="plane2">The second plane</param>
/// <param name="plane3">The third plane</param>
/// <returns>The intersection point</returns>
public static Point3D PointFromPlanes(Plane3D plane1, Plane3D plane2, Plane3D plane3)
{
return Point3D.IntersectionOf(plane1, plane2, plane3);
}
/// <summary>
/// Get the distance to the point along the <see cref="Normal"/>
/// </summary>
/// <param name="point">The <see cref="Point3D"/></param>
/// <returns>The distance.</returns>
[Pure]
public double SignedDistanceTo(Point3D point)
{
var p = this.Project(point);
var v = p.VectorTo(point);
return v.DotProduct(this.Normal);
}
/// <summary>
/// Get the distance to the plane along the <see cref="Normal"/>
/// This assumes the planes are parallel
/// </summary>
/// <param name="other">The <see cref="Point3D"/></param>
/// <returns>The distance.</returns>
[Pure]
public double SignedDistanceTo(Plane3D other)
{
if (!this.Normal.IsParallelTo(other.Normal, tolerance: 1E-15))
{
throw new ArgumentException("Planes are not parallel");
}
return this.SignedDistanceTo(other.RootPoint);
}
/// <summary>
/// Get the distance to the ThroughPoint of <paramref name="ray"/> along the <see cref="Normal"/>
/// This assumes the ray is parallel to the plane.
/// </summary>
/// <param name="ray">The <see cref="Point3D"/></param>
/// <returns>The distance.</returns>
[Pure]
public double SignedDistanceTo(Ray3D ray)
{
if (Math.Abs(ray.Direction.DotProduct(this.Normal) - 0) < 1E-15)
{
return this.SignedDistanceTo(ray.ThroughPoint);
}
return 0;
}
/// <summary>
/// Get the distance to the point.
/// </summary>
/// <param name="point">The <see cref="Point3D"/></param>
/// <returns>The distance.</returns>
[Pure]
public double AbsoluteDistanceTo(Point3D point)
{
return Math.Abs(this.SignedDistanceTo(point));
}
/// <summary>
/// Projects a point onto the plane
/// </summary>
/// <param name="p">A point</param>
/// <param name="projectionDirection">The direction of projection</param>
/// <returns>a projected point</returns>
[Pure]
public Point3D Project(Point3D p, UnitVector3D? projectionDirection = null)
{
var dotProduct = this.Normal.DotProduct(p.ToVector3D());
var projectiononNormal = projectionDirection == null ? this.Normal : projectionDirection.Value;
var projectionVector = (dotProduct + this.D) * projectiononNormal;
return p - projectionVector;
}
/// <summary>
/// Projects a line onto the plane
/// </summary>
/// <param name="line3DToProject">The line to project</param>
/// <returns>A projected line</returns>
public Line3D Project(Line3D line3DToProject)
{
var projectedStartPoint = this.Project(line3DToProject.StartPoint);
var projectedEndPoint = this.Project(line3DToProject.EndPoint);
return new Line3D(projectedStartPoint, projectedEndPoint);
}
/// <summary>
/// Projects a line onto the plane
/// </summary>
/// <param name="line3DToProject">The line to project</param>
/// <returns>A projected line</returns>
[Pure]
public LineSegment3D Project(LineSegment3D line3DToProject)
{
var projectedStartPoint = this.Project(line3DToProject.StartPoint);
var projectedEndPoint = this.Project(line3DToProject.EndPoint);
return new LineSegment3D(projectedStartPoint, projectedEndPoint);
}
/// <summary>
/// Projects a ray onto the plane
/// </summary>
/// <param name="rayToProject">The ray to project</param>
/// <returns>A projected ray</returns>
[Pure]
public Ray3D Project(Ray3D rayToProject)
{
var projectedThroughPoint = this.Project(rayToProject.ThroughPoint);
var projectedDirection = this.Project(rayToProject.Direction.ToVector3D());
return new Ray3D(projectedThroughPoint, projectedDirection.Direction);
}
/// <summary>
/// Project Vector3D onto this plane
/// </summary>
/// <param name="vector3DToProject">The Vector3D to project</param>
/// <returns>The projected Vector3D</returns>
[Pure]
public Ray3D Project(Vector3D vector3DToProject)
{
var projectedEndPoint = this.Project(vector3DToProject.ToPoint3D());
var projectedZero = this.Project(new Point3D(0, 0, 0));
return new Ray3D(projectedZero, projectedZero.VectorTo(projectedEndPoint).Normalize());
}
/// <summary>
/// Project Vector3D onto this plane
/// </summary>
/// <param name="vector3DToProject">The Vector3D to project</param>
/// <returns>The projected Vector3D</returns>
[Pure]
public Ray3D Project(UnitVector3D vector3DToProject)
{
return this.Project(vector3DToProject.ToVector3D());
}
/// <summary>
/// Finds the intersection of the two planes, throws if they are parallel
/// http://mathworld.wolfram.com/Plane-PlaneIntersection.html
/// </summary>
/// <param name="intersectingPlane">a plane which intersects</param>
/// <param name="tolerance">A tolerance (epsilon) to account for floating point error.</param>
/// <returns>A ray at the intersection.</returns>
[Pure]
public Ray3D IntersectionWith(Plane3D intersectingPlane, double tolerance = float.Epsilon)
{
var a = new DenseMatrix(2, 3);
a.SetRow(0, this.Normal.ToVector());
a.SetRow(1, intersectingPlane.Normal.ToVector());
var svd = a.Svd(true);
if (svd.S[1] < tolerance)
{
throw new ArgumentException("Planes are parallel");
}
var y = new DenseMatrix(2, 1)
{
[0, 0] = -1 * this.D,
[1, 0] = -1 * intersectingPlane.D
};
var pointOnIntersectionLine = svd.Solve(y);
var throughPoint = Point3D.OfVector(pointOnIntersectionLine.Column(0));
var direction = UnitVector3D.OfVector(svd.VT.Row(2));
return new Ray3D(throughPoint, direction);
}
/// <summary>
/// Find intersection between Line3D and Plane
/// http://geomalgorithms.com/a05-_intersect-1.html
/// </summary>
/// <param name="line">A line segment</param>
/// <param name="tolerance">A tolerance (epsilon) to account for floating point error.</param>
/// <returns>Intersection Point or null</returns>
public Point3D? IntersectionWith(Line3D line, double tolerance = float.Epsilon)
{
if (line.Direction.IsPerpendicularTo(this.Normal, tolerance))
{
// either parallel or lies in the plane
var projectedPoint = this.Project(line.StartPoint, line.Direction);
if (projectedPoint == line.StartPoint)
{
throw new InvalidOperationException("Line lies in the plane");
}
// Line and plane are parallel
return null;
}
var d = this.SignedDistanceTo(line.StartPoint);
var u = line.StartPoint.VectorTo(line.EndPoint);
var t = -1 * d / u.DotProduct(this.Normal);
if (t > 1 || t < 0)
{
// They are not intersected
return null;
}
return line.StartPoint + (t * u);
}
/// <summary>
/// Find intersection between LineSegment3D and Plane
/// http://geomalgorithms.com/a05-_intersect-1.html
/// </summary>
/// <param name="line">A line segment</param>
/// <param name="tolerance">A tolerance (epsilon) to account for floating point error.</param>
/// <returns>Intersection Point or null</returns>
[Pure]
public Point3D? IntersectionWith(LineSegment3D line, double tolerance = float.Epsilon)
{
if (line.Direction.IsPerpendicularTo(this.Normal, tolerance))
{
// either parallel or lies in the plane
var projectedPoint = this.Project(line.StartPoint, line.Direction);
if (projectedPoint == line.StartPoint)
{
throw new InvalidOperationException("Line lies in the plane");
}
// Line and plane are parallel
return null;
}
var d = this.SignedDistanceTo(line.StartPoint);
var u = line.StartPoint.VectorTo(line.EndPoint);
var t = -1 * d / u.DotProduct(this.Normal);
if (t > 1 || t < 0)
{
// They are not intersected
return null;
}
return line.StartPoint + (t * u);
}
/// <summary>
/// http://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm
/// </summary>
/// <param name="ray">A ray</param>
/// <param name="tolerance">A tolerance (epsilon) to account for floating point error.</param>
/// <returns>The point of intersection.</returns>
[Pure]
public Point3D IntersectionWith(Ray3D ray, double tolerance = float.Epsilon)
{
if (this.Normal.IsPerpendicularTo(ray.Direction, tolerance))
{
throw new InvalidOperationException("Ray is parallel to the plane.");
}
var d = this.SignedDistanceTo(ray.ThroughPoint);
var t = -1 * d / ray.Direction.DotProduct(this.Normal);
return ray.ThroughPoint + (t * ray.Direction);
}
/// <summary>
/// Returns <paramref name="p"/> mirrored about the plane.
/// </summary>
/// <param name="p">The <see cref="Point3D"/></param>
/// <returns>The mirrored point.</returns>
[Pure]
public Point3D MirrorAbout(Point3D p)
{
var p2 = this.Project(p);
var d = this.SignedDistanceTo(p);
return p2 - (1 * d * this.Normal);
}
/// <summary>
/// Rotates a plane
/// </summary>
/// <param name="aboutVector">The vector about which to rotate</param>
/// <param name="angle">An angle to rotate</param>
/// <returns>A rotated plane</returns>
[Pure]
public Plane3D Rotate(UnitVector3D aboutVector, Angle angle)
{
var rootPoint = this.RootPoint;
var rotatedPoint = rootPoint.Rotate(aboutVector, angle);
var rotatedPlaneVector = this.Normal.Rotate(aboutVector, angle);
return new Plane3D(rotatedPlaneVector, rotatedPoint);
}
/// <summary>
/// Returns a value to indicate if a pair of geometric planes are equal
/// </summary>
/// <param name="other">The geometric plane to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the geometric planes are equal; otherwise false</returns>
[Pure]
public bool Equals(Plane3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.D - this.D) < tolerance && this.Normal.Equals(other.Normal, tolerance);
}
/// <inheritdoc />
[Pure]
public bool Equals(Plane3D p) => this.D.Equals(p.D) && this.Normal.Equals(p.Normal);
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Plane3D p && this.Equals(p);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.Normal, this.D);
/// <inheritdoc />
[Pure]
public override string ToString()
{
return $"A:{Math.Round(this.A, 4)} B:{Math.Round(this.B, 4)} C:{Math.Round(this.C, 4)} D:{Math.Round(this.D, 4)}";
}
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.MoveToContent();
var e = (XElement)XNode.ReadFrom(reader);
this = new Plane3D(
UnitVector3D.ReadFrom(e.SingleElement("Normal").CreateReader()),
Point3D.ReadFrom(e.SingleElement("RootPoint").CreateReader()));
}
/// <inheritdoc/>
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteElement("RootPoint", this.RootPoint);
writer.WriteElement("Normal", this.Normal);
}
}
}

494
src/Numerics/Spatial/Euclidean3D/Point3D.cs

@ -0,0 +1,494 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// Represents a point in 3 dimensional space
/// </summary>
[Serializable]
public struct Point3D : IXmlSerializable, IEquatable<Point3D>, IFormattable
{
/// <summary>
/// The x component.
/// </summary>
public readonly double X;
/// <summary>
/// The y component.
/// </summary>
public readonly double Y;
/// <summary>
/// The z component.
/// </summary>
public readonly double Z;
/// <summary>
/// Initializes a new instance of the <see cref="Point3D"/> struct.
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
/// <param name="z">The z component.</param>
public Point3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
/// <summary>
/// Gets a point at the origin
/// </summary>
public static Point3D Origin { get; } = new Point3D(0, 0, 0);
/// <summary>
/// Gets a point where all values are NAN
/// </summary>
public static Point3D NaN { get; } = new Point3D(double.NaN, double.NaN, double.NaN);
/// <summary>
/// Multiplies a matrix and a vector representation of the point together
/// </summary>
/// <param name="left">A matrix</param>
/// <param name="right">A point</param>
/// <returns>A Mathnet.Numerics vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(Matrix<double> left, Point3D right)
{
return left * right.ToVector();
}
/// <summary>
/// Multiplies a matrix and a vector representation of the point together
/// </summary>
/// <param name="left">A point</param>
/// <param name="right">A matrix</param>
/// <returns>A Mathnet.Numerics vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(Point3D left, Matrix<double> right)
{
return left.ToVector() * right;
}
/// <summary>
/// Adds a point and a vector together
/// </summary>
/// <param name="point">A point</param>
/// <param name="vector">A vector</param>
/// <returns>A new point at the summed location</returns>
public static Point3D operator +(Point3D point, Vector3D vector)
{
return new Point3D(point.X + vector.X, point.Y + vector.Y, point.Z + vector.Z);
}
/// <summary>
/// Adds a point and a vector together
/// </summary>
/// <param name="point">A point</param>
/// <param name="vector">A vector</param>
/// <returns>A new point at the summed location</returns>
public static Point3D operator +(Point3D point, UnitVector3D vector)
{
return new Point3D(point.X + vector.X, point.Y + vector.Y, point.Z + vector.Z);
}
/// <summary>
/// Subtracts a vector from a point
/// </summary>
/// <param name="point">A point</param>
/// <param name="vector">A vector</param>
/// <returns>A new point at the difference</returns>
public static Point3D operator -(Point3D point, Vector3D vector)
{
return new Point3D(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z);
}
/// <summary>
/// Subtracts a vector from a point
/// </summary>
/// <param name="point">A point</param>
/// <param name="vector">A vector</param>
/// <returns>A new point at the difference</returns>
public static Point3D operator -(Point3D point, UnitVector3D vector)
{
return new Point3D(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z);
}
/// <summary>
/// Subtracts the first point from the second point
/// </summary>
/// <param name="left">The first point</param>
/// <param name="right">The second point</param>
/// <returns>A vector pointing to the difference</returns>
public static Vector3D operator -(Point3D left, Point3D right)
{
return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified points is equal.
/// </summary>
/// <param name="left">The first point to compare</param>
/// <param name="right">The second point to compare</param>
/// <returns>True if the points are the same; otherwise false.</returns>
public static bool operator ==(Point3D left, Point3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified points is not equal.
/// </summary>
/// <param name="left">The first point to compare</param>
/// <param name="right">The second point to compare</param>
/// <returns>True if the points are different; otherwise false.</returns>
public static bool operator !=(Point3D left, Point3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">A point with the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out Point3D result)
{
return TryParse(text, null, out result);
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a point
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out Point3D result)
{
if (Text.TryParse3D(text, formatProvider, out var x, out var y, out var z))
{
result = new Point3D(x, y, z);
return true;
}
result = default(Point3D);
return false;
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a point
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <returns>A point at the coordinates specified</returns>
public static Point3D Parse(string value, IFormatProvider formatProvider = null)
{
if (TryParse(value, formatProvider, out var p))
{
return p;
}
throw new FormatException($"Could not parse a Point3D from the string {value}");
}
/// <summary>
/// Create a new <see cref="Point3D"/> from a Math.NET Numerics vector of length 3.
/// </summary>
/// <param name="vector"> A vector with length 2 to populate the created instance with.</param>
/// <returns> A <see cref="Point3D"/></returns>
public static Point3D OfVector(Vector<double> vector)
{
if (vector.Count != 3)
{
throw new ArgumentException("The vector length must be 3 in order to convert it to a Point3D");
}
return new Point3D(vector.At(0), vector.At(1), vector.At(2));
}
/// <summary>
/// Creates an <see cref="Point3D"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="Point3D"/>.</param>
/// <returns>An <see cref="Point3D"/> that contains the data read from the reader.</returns>
public static Point3D ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<Point3D>();
}
/// <summary>
/// Returns the centroid of an arbitrary collection of points
/// </summary>
/// <param name="points">a list of points</param>
/// <returns>The centroid of the points</returns>
public static Point3D Centroid(IEnumerable<Point3D> points)
{
return Centroid(points.ToArray());
}
/// <summary>
/// Returns the centroid of an arbitrary collection of points
/// </summary>
/// <param name="points">a list of points</param>
/// <returns>The centroid of the points</returns>
public static Point3D Centroid(params Point3D[] points)
{
return new Point3D(
points.Average(point => point.X),
points.Average(point => point.Y),
points.Average(point => point.Z));
}
/// <summary>
/// Returns the midpoint of two points
/// </summary>
/// <param name="p1">The first point</param>
/// <param name="p2">The second point</param>
/// <returns>The midpoint of the points</returns>
public static Point3D MidPoint(Point3D p1, Point3D p2)
{
return Centroid(p1, p2);
}
/// <summary>
/// Returns the point at which three planes intersect
/// </summary>
/// <param name="plane1">The first plane</param>
/// <param name="plane2">The second plane</param>
/// <param name="plane3">The third plane</param>
/// <returns>The point of intersection</returns>
public static Point3D IntersectionOf(Plane3D plane1, Plane3D plane2, Plane3D plane3)
{
var ray = plane1.IntersectionWith(plane2);
return plane3.IntersectionWith(ray);
}
/// <summary>
/// Returns the point of intersection between a plane and a ray
/// </summary>
/// <param name="plane">A geometric plane</param>
/// <param name="ray">a ray</param>
/// <returns>The point of intersection</returns>
public static Point3D IntersectionOf(Plane3D plane, Ray3D ray)
{
return plane.IntersectionWith(ray);
}
/// <summary>
/// Returns the mirror point of this point across a plane
/// </summary>
/// <param name="plane">A plane</param>
/// <returns>The mirrored point</returns>
[Pure]
public Point3D MirrorAbout(Plane3D plane)
{
return plane.MirrorAbout(this);
}
/// <summary>
/// Projects a point onto a plane
/// </summary>
/// <param name="plane">a plane</param>
/// <returns>The projected point</returns>
[Pure]
public Point3D ProjectOn(Plane3D plane)
{
return plane.Project(this);
}
/// <summary>
/// Rotates the point about a given vector
/// </summary>
/// <param name="aboutVector">A vector</param>
/// <param name="angle">The angle to rotate</param>
/// <returns>The rotated point</returns>
[Pure]
public Point3D Rotate(Vector3D aboutVector, Angle angle)
{
return this.Rotate(aboutVector.Normalize(), angle);
}
/// <summary>
/// Rotates the point about a given vector
/// </summary>
/// <param name="aboutVector">A vector</param>
/// <param name="angle">The angle to rotate</param>
/// <returns>The rotated point</returns>
[Pure]
public Point3D Rotate(UnitVector3D aboutVector, Angle angle)
{
var cs = CoordinateSystem3D.Rotation(angle, aboutVector);
return cs.Transform(this);
}
/// <summary>
/// Gets a vector from this point to another point
/// </summary>
/// <param name="p">The point to which the vector should go</param>
/// <returns>A vector pointing to the other point.</returns>
[Pure]
public Vector3D VectorTo(Point3D p)
{
return p - this;
}
/// <summary>
/// Finds the straight line distance to another point
/// </summary>
/// <param name="p">The other point</param>
/// <returns>a distance measure</returns>
[Pure]
public double DistanceTo(Point3D p)
{
var vector = this.VectorTo(p);
return vector.Length;
}
/// <summary>
/// Converts this point into a vector from the origin
/// </summary>
/// <returns>A vector equivalent to this point</returns>
[Pure]
public Vector3D ToVector3D()
{
return new Vector3D(this.X, this.Y, this.Z);
}
/// <summary>
/// Applies a transform coordinate system to the point
/// </summary>
/// <param name="cs">A coordinate system</param>
/// <returns>A new 3D point</returns>
[Pure]
public Point3D TransformBy(CoordinateSystem3D cs)
{
return cs.Transform(this);
}
/// <summary>
/// Applies a transform matrix to the point
/// </summary>
/// <param name="m">A transform matrix</param>
/// <returns>A new point</returns>
[Pure]
public Point3D TransformBy(Matrix<double> m)
{
return OfVector(m.Multiply(this.ToVector()));
}
/// <summary>
/// Convert to a Math.NET Numerics dense vector of length 3.
/// </summary>
/// <returns>A Math.Net Numerics vector</returns>
[Pure]
public Vector<double> ToVector()
{
return Vector<double>.Build.Dense(new[] { this.X, this.Y, this.Z });
}
/// <inheritdoc />
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
[Pure]
public string ToString(IFormatProvider provider)
{
return this.ToString(null, provider);
}
/// <inheritdoc />
[Pure]
public string ToString(string format, IFormatProvider provider = null)
{
var numberFormatInfo = provider != null ? NumberFormatInfo.GetInstance(provider) : CultureInfo.InvariantCulture.NumberFormat;
var separator = numberFormatInfo.NumberDecimalSeparator == "," ? ";" : ",";
return string.Format("({0}{1} {2}{1} {3})", this.X.ToString(format, numberFormatInfo), separator, this.Y.ToString(format, numberFormatInfo), this.Z.ToString(format, numberFormatInfo));
}
/// <summary>
/// Returns a value to indicate if a pair of points are equal
/// </summary>
/// <param name="other">The point to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the points are equal; otherwise false</returns>
[Pure]
public bool Equals(Point3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance &&
Math.Abs(other.Z - this.Z) < tolerance;
}
/// <inheritdoc />
[Pure]
public bool Equals(Point3D other)
{
return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj) => obj is Point3D p && this.Equals(p);
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema() => null;
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.TryReadAttributeAsDouble("X", out var x) &&
reader.TryReadAttributeAsDouble("Y", out var y) &&
reader.TryReadAttributeAsDouble("Z", out var z))
{
reader.Skip();
this = new Point3D(x, y, z);
return;
}
if (reader.TryReadChildElementsAsDoubles("X", "Y", "Z", out x, out y, out z))
{
reader.Skip();
this = new Point3D(x, y, z);
return;
}
throw new XmlException($"Could not read a {this.GetType()}");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("X", this.X);
writer.WriteAttribute("Y", this.Y);
writer.WriteAttribute("Z", this.Z);
}
}
}

227
src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs

@ -0,0 +1,227 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A PolyLine is an ordered series of line segments in space represented as list of connected Point3Ds.
/// </summary>
public class PolyLine3D : IEquatable<PolyLine3D>
{
/// <summary>
/// An internal list of points
/// </summary>
private readonly List<Point3D> points;
/// <summary>
/// Initializes a new instance of the <see cref="PolyLine3D"/> class.
/// Creates a PolyLine3D from a pre-existing IEnumerable of Point3Ds
/// </summary>
/// <param name="points">A list of points.</param>
public PolyLine3D(IEnumerable<Point3D> points)
{
this.points = new List<Point3D>(points);
}
/// <summary>
/// Gets the number of vertices in the polyline.
/// </summary>
public int VertexCount => this.points.Count;
/// <summary>
/// Gets the length of the polyline, computed as the sum of the lengths of every segment
/// </summary>
public double Length => this.GetPolyLineLength();
/// <summary>
/// Gets a list of vertices
/// </summary>
public IEnumerable<Point3D> Vertices
{
get
{
foreach (var point in this.points)
{
yield return point;
}
}
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified lines is equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are the same; otherwise false.</returns>
public static bool operator ==(PolyLine3D left, PolyLine3D right)
{
return left?.Equals(right) == true;
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified lines is not equal.
/// </summary>
/// <param name="left">The first line to compare</param>
/// <param name="right">The second line to compare</param>
/// <returns>True if the lines are different; otherwise false.</returns>
public static bool operator !=(PolyLine3D left, PolyLine3D right)
{
return left?.Equals(right) != true;
}
/// <summary>
/// Get the point at a fractional distance along the curve. For instance, fraction=0.5 will return
/// the point halfway along the length of the polyline.
/// </summary>
/// <param name="fraction">The fractional length at which to compute the point</param>
/// <returns>A point a fraction of the way along the line.</returns>
public Point3D GetPointAtFractionAlongCurve(double fraction)
{
if (fraction > 1 || fraction < 0)
{
throw new ArgumentException("fraction must be between 0 and 1");
}
return this.GetPointAtLengthFromStart(fraction * this.Length);
}
/// <summary>
/// Get the point at a specified distance along the curve. A negative argument will return the first point,
/// an argument greater than the length of the curve will return the last point.
/// </summary>
/// <param name="lengthFromStart">The distance from the first point along the curve at which to return a point</param>
/// <returns>A point which is the specified distance along the line</returns>
public Point3D GetPointAtLengthFromStart(double lengthFromStart)
{
var length = this.Length;
if (lengthFromStart >= length)
{
return this.points.Last();
}
if (lengthFromStart <= 0)
{
return this.points.First();
}
double cumulativeLength = 0;
var i = 0;
while (true)
{
var nextLength = cumulativeLength + this.points[i].DistanceTo(this.points[i + 1]);
if (cumulativeLength <= lengthFromStart && nextLength > lengthFromStart)
{
var leftover = lengthFromStart - cumulativeLength;
var direction = this.points[i].VectorTo(this.points[i + 1]).Normalize();
return this.points[i] + (leftover * direction);
}
cumulativeLength = nextLength;
i++;
}
}
/// <summary>
/// Returns the closest point on the polyline to the given point.
/// </summary>
/// <param name="p">A point</param>
/// <returns>A point which is the closest to the given point but still on the line.</returns>
public Point3D ClosestPointTo(Point3D p)
{
var minError = double.MaxValue;
var closest = default(Point3D);
for (var i = 0; i < this.VertexCount - 1; i++)
{
var segment = new LineSegment3D(this.points[i], this.points[i + 1]);
var projected = segment.ClosestPointTo(p);
var error = p.DistanceTo(projected);
if (error < minError)
{
minError = error;
closest = projected;
}
}
return closest;
}
/// <summary>
/// Returns a value to indicate if a pair of polylines are equal
/// </summary>
/// <param name="other">The polyline to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the polylines are equal; otherwise false</returns>
[Pure]
public bool Equals(PolyLine3D other, double tolerance)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i], tolerance))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public bool Equals(PolyLine3D other)
{
if (this.VertexCount != other?.VertexCount)
{
return false;
}
for (var i = 0; i < this.points.Count; i++)
{
if (!this.points[i].Equals(other.points[i]))
{
return false;
}
}
return true;
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
return obj is PolyLine3D polyLine3D &&
this.Equals(polyLine3D);
}
/// <inheritdoc />
[Pure]
public override int GetHashCode()
{
return HashCode.CombineMany(this.points);
}
/// <summary>
/// Returns the length of the polyline by summing the lengths of the individual segments
/// </summary>
/// <returns>The length of the line.</returns>
private double GetPolyLineLength()
{
double length = 0;
for (var i = 0; i < this.points.Count - 1; ++i)
{
length += this.points[i].DistanceTo(this.points[i + 1]);
}
return length;
}
}
}

196
src/Numerics/Spatial/Euclidean3D/Ray3D.cs

@ -0,0 +1,196 @@
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A ray in 3D space
/// </summary>
[Serializable]
public struct Ray3D : IEquatable<Ray3D>, IXmlSerializable, IFormattable
{
/// <summary>
/// The start point of the ray
/// </summary>
public readonly Point3D ThroughPoint;
/// <summary>
/// The direction of the ray
/// </summary>
public readonly UnitVector3D Direction;
/// <summary>
/// Initializes a new instance of the <see cref="Ray3D"/> struct.
/// </summary>
/// <param name="throughPoint">The start point of the ray.</param>
/// <param name="direction">The direction of the ray.</param>
public Ray3D(Point3D throughPoint, UnitVector3D direction)
{
this.ThroughPoint = throughPoint;
this.Direction = direction;
}
/// <summary>
/// Initializes a new instance of the <see cref="Ray3D"/> struct.
/// </summary>
/// <param name="throughPoint">The start point of the ray.</param>
/// <param name="direction">A vector indicating the direction of the ray.</param>
public Ray3D(Point3D throughPoint, Vector3D direction)
: this(throughPoint, direction.Normalize())
{
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified rays is equal.
/// </summary>
/// <param name="left">The first ray to compare</param>
/// <param name="right">The second ray to compare</param>
/// <returns>True if the rays are the same; otherwise false.</returns>
public static bool operator ==(Ray3D left, Ray3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified rays is not equal.
/// </summary>
/// <param name="left">The first ray to compare</param>
/// <param name="right">The second ray to compare</param>
/// <returns>True if the rays are different; otherwise false.</returns>
public static bool operator !=(Ray3D left, Ray3D right)
{
return !left.Equals(right);
}
/// <summary>
/// The intersection of the two planes
/// </summary>
/// <param name="plane1">The first plane</param>
/// <param name="plane2">The second plane</param>
/// <returns>A ray at the intersection of two planes</returns>
public static Ray3D IntersectionOf(Plane3D plane1, Plane3D plane2)
{
return plane1.IntersectionWith(plane2);
}
/// <summary>
/// Parses string representation of throughpoint and direction
/// See <see cref="Point3D.Parse(string, IFormatProvider)" /> and <see cref="UnitVector3D.Parse(string, IFormatProvider, double)" /> for details on acceptable formats.
/// This is mainly meant for tests
/// </summary>
/// <param name="point">a string representing a start point for the ray.</param>
/// <param name="direction">a string representing a direction for the ray.</param>
/// <returns>A ray.</returns>
public static Ray3D Parse(string point, string direction)
{
return new Ray3D(Point3D.Parse(point), UnitVector3D.Parse(direction));
}
/// <summary>
/// Returns the shortest line from a point to the ray
/// </summary>
/// <param name="point3D">A point.</param>
/// <returns>A line segment from the point to the closest point on the ray</returns>
[Pure]
public LineSegment3D ShortestLineTo(Point3D point3D)
{
var v = this.ThroughPoint.VectorTo(point3D);
var alongVector = v.ProjectOn(this.Direction);
return new LineSegment3D(this.ThroughPoint + alongVector, point3D);
}
/// <summary>
/// Returns the point at which this ray intersects with the plane
/// </summary>
/// <param name="plane">A geometric plane.</param>
/// <returns>A point of intersection if such an intersection exists; otherwise null.</returns>
[Pure]
public Point3D? IntersectionWith(Plane3D plane)
{
return plane.IntersectionWith(this);
}
/// <summary>
/// Returns a value to indicate if a pair of rays are collinear
/// </summary>
/// <param name="otherRay">The ray to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the rays are collinear; otherwise false.</returns>
[Pure]
public bool IsCollinear(Ray3D otherRay, double tolerance = float.Epsilon)
{
return this.Direction.IsParallelTo(otherRay.Direction, tolerance);
}
/// <summary>
/// Returns a value to indicate if a pair of rays are equal
/// </summary>
/// <param name="other">The ray to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the rays are equal; otherwise false</returns>
[Pure]
public bool Equals(Ray3D other, double tolerance)
{
return this.Direction.Equals(other.Direction, tolerance) &&
this.ThroughPoint.Equals(other.ThroughPoint, tolerance);
}
/// <inheritdoc/>
[Pure]
public bool Equals(Ray3D r) => this.Direction.Equals(r.Direction) && this.ThroughPoint.Equals(r.ThroughPoint);
/// <inheritdoc/>
[Pure]
public override bool Equals(object obj) => obj is Ray3D r && this.Equals(r);
/// <inheritdoc/>
[Pure]
public override int GetHashCode() => HashCode.Combine(this.ThroughPoint, this.Direction);
/// <inheritdoc/>
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <inheritdoc/>
[Pure]
public string ToString(string format, IFormatProvider formatProvider)
{
return string.Format(
"ThroughPoint: {0}, Direction: {1}",
this.ThroughPoint.ToString(format, formatProvider),
this.Direction.ToString(format, formatProvider));
}
/// <inheritdoc/>
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc/>
void IXmlSerializable.ReadXml(XmlReader reader)
{
reader.MoveToContent();
var e = (XElement)XNode.ReadFrom(reader);
this = new Ray3D(
Point3D.ReadFrom(e.SingleElement("ThroughPoint").CreateReader()),
UnitVector3D.ReadFrom(e.SingleElement("Direction").CreateReader()));
}
/// <inheritdoc/>
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteElement("ThroughPoint", this.ThroughPoint);
writer.WriteElement("Direction", this.Direction);
}
}
}

931
src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs

@ -0,0 +1,931 @@
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A unit vector, this is used to describe a direction in 3D
/// </summary>
[Serializable]
public struct UnitVector3D : IXmlSerializable, IEquatable<UnitVector3D>, IEquatable<Vector3D>, IFormattable
{
/// <summary>
/// The x component.
/// </summary>
public readonly double X;
/// <summary>
/// The y component.
/// </summary>
public readonly double Y;
/// <summary>
/// The z component.
/// </summary>
public readonly double Z;
/// <summary>
/// Initializes a new instance of the <see cref="UnitVector3D"/> struct.
/// The provided values are scaled to L2 norm == 1
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
/// <param name="z">The z component.</param>
private UnitVector3D(double x, double y, double z)
{
if (double.IsNaN(x) || double.IsInfinity(x))
{
throw new ArgumentOutOfRangeException(nameof(x), x, "Invalid value.");
}
if (double.IsNaN(y) || double.IsInfinity(y))
{
throw new ArgumentOutOfRangeException(nameof(y), y, "Invalid value.");
}
if (double.IsNaN(z) || double.IsInfinity(z))
{
throw new ArgumentOutOfRangeException(nameof(z), z, "Invalid value.");
}
var norm = Math.Sqrt((x * x) + (y * y) + (z * z));
if (norm < float.Epsilon)
{
throw new ArgumentException("l < float.Epsilon");
}
this.X = x / norm;
this.Y = y / norm;
this.Z = z / norm;
}
/// <summary>
/// Gets the X axis
/// </summary>
public static UnitVector3D XAxis { get; } = Create(1, 0, 0);
/// <summary>
/// Gets the Y axis
/// </summary>
public static UnitVector3D YAxis { get; } = Create(0, 1, 0);
/// <summary>
/// Gets the z Axis
/// </summary>
public static UnitVector3D ZAxis { get; } = Create(0, 0, 1);
/// <summary>
/// Gets a vector orthogonal to this
/// </summary>
[Pure]
public UnitVector3D Orthogonal
{
get
{
if (-this.X - this.Y > 0.1)
{
return UnitVector3D.Create(this.Z, this.Z, -this.X - this.Y);
}
return UnitVector3D.Create(-this.Y - this.Z, this.X, this.X);
}
}
/// <summary>
/// Gets the length of the vector not the count of elements
/// </summary>
[Pure]
public double Length => 1;
/// <summary>
/// Gets the cross product matrix
/// </summary>
[Pure]
internal Matrix<double> CrossProductMatrix => Matrix<double>.Build.Dense(3, 3, new[] { 0d, this.Z, -this.Y, -this.Z, 0d, this.X, this.Y, -this.X, 0d });
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// </summary>
/// <param name="left">The first vector to compare</param>
/// <param name="right">The second vector to compare</param>
/// <returns>True if the vectors are the same; otherwise false.</returns>
public static bool operator ==(UnitVector3D left, UnitVector3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// </summary>
/// <param name="left">The first vector to compare</param>
/// <param name="right">The second vector to compare</param>
/// <returns>True if the vectors are the same; otherwise false.</returns>
public static bool operator ==(Vector3D left, UnitVector3D right)
{
return left.Equals((object)right);
}
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// </summary>
/// <param name="left">The first vector to compare</param>
/// <param name="right">The second vector to compare</param>
/// <returns>True if the vectors are the same; otherwise false.</returns>
public static bool operator ==(UnitVector3D left, Vector3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified vectors is not equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are different; otherwise false.</returns>
public static bool operator !=(UnitVector3D left, UnitVector3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified vectors is not equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are different; otherwise false.</returns>
public static bool operator !=(Vector3D left, UnitVector3D right)
{
return !left.Equals((object)right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified vectors is not equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are different; otherwise false.</returns>
public static bool operator !=(UnitVector3D left, Vector3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Adds two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new summed vector</returns>
public static Vector3D operator +(UnitVector3D v1, UnitVector3D v2)
{
return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
/// <summary>
/// Adds two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new summed vector</returns>
public static Vector3D operator +(Vector3D v1, UnitVector3D v2)
{
return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
/// <summary>
/// Adds two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new summed vector</returns>
public static Vector3D operator +(UnitVector3D v1, Vector3D v2)
{
return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
}
/// <summary>
/// Subtracts two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new difference vector</returns>
public static Vector3D operator -(UnitVector3D v1, UnitVector3D v2)
{
return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
/// <summary>
/// Subtracts two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new difference vector</returns>
public static Vector3D operator -(Vector3D v1, UnitVector3D v2)
{
return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
/// <summary>
/// Subtracts two vectors
/// </summary>
/// <param name="v1">The first vector</param>
/// <param name="v2">The second vector</param>
/// <returns>A new difference vector</returns>
public static Vector3D operator -(UnitVector3D v1, Vector3D v2)
{
return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
}
/// <summary>
/// Negates the vector
/// </summary>
/// <param name="v">A vector to negate</param>
/// <returns>A new negated vector</returns>
public static Vector3D operator -(UnitVector3D v)
{
return new Vector3D(-1 * v.X, -1 * v.Y, -1 * v.Z);
}
/// <summary>
/// Multiplies a vector by a scalar
/// </summary>
/// <param name="d">A scalar</param>
/// <param name="v">A vector</param>
/// <returns>A scaled vector</returns>
public static Vector3D operator *(double d, UnitVector3D v)
{
return new Vector3D(d * v.X, d * v.Y, d * v.Z);
}
/// <summary>
/// Divides a vector by a scalar
/// </summary>
/// <param name="v">A vector</param>
/// <param name="d">A scalar</param>
/// <returns>A scaled vector</returns>
public static Vector3D operator /(UnitVector3D v, double d)
{
return new Vector3D(v.X / d, v.Y / d, v.Z / d);
}
/// <summary>
/// Multiplies a Matrix by a Vector
/// </summary>
/// <param name="left">A Matrix</param>
/// <param name="right">A Vector</param>
/// <returns>A new vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(Matrix<double> left, UnitVector3D right)
{
return left * right.ToVector();
}
/// <summary>
/// Multiplies a vector by a matrix
/// </summary>
/// <param name="left">A Vector</param>
/// <param name="right">A Matrix</param>
/// <returns>A new vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(UnitVector3D left, Matrix<double> right)
{
return left.ToVector() * right;
}
/// <summary>
/// Returns the dot product of two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A scalar result</returns>
public static double operator *(UnitVector3D left, UnitVector3D right)
{
return left.DotProduct(right);
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitVector3D"/> struct.
/// The provided values are scaled to L2 norm == 1
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
/// <param name="z">The z component.</param>
/// <param name="tolerance">The allowed deviation from 1 for the L2-norm of x,y,z</param>
/// <returns>The <see cref="UnitVector3D"/></returns>
public static UnitVector3D Create(double x, double y, double z, double tolerance = double.PositiveInfinity)
{
var norm = Math.Sqrt((x * x) + (y * y) + (z * z));
if (norm < float.Epsilon)
{
throw new InvalidOperationException("The Euclidean norm of x, y, z is less than float.Epsilon");
}
if (Math.Abs(norm - 1) > tolerance)
{
throw new InvalidOperationException("The Euclidean norm of x, y, z differs more than tolerance from 1");
}
#pragma warning disable 618
return new UnitVector3D(x / norm, y / norm, z / norm);
#pragma warning restore 618
}
/// <summary>
/// Create a new <see cref="UnitVector3D"/> from a Math.NET Numerics vector of length 3.
/// </summary>
/// <param name="vector"> A vector with length 2 to populate the created instance with.</param>
/// <returns> A <see cref="UnitVector3D"/></returns>
public static UnitVector3D OfVector(Vector<double> vector)
{
if (vector.Count != 3)
{
throw new ArgumentException("The vector length must be 3 in order to convert it to a Vector3D");
}
return Create(vector.At(0), vector.At(1), vector.At(2));
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a vector
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">A vector with the coordinates specified</param>
/// <param name="tolerance">The tolerance for how big deviation from Length = 1 is accepted</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out UnitVector3D result, double tolerance = 0.1)
{
return TryParse(text, null, out result, tolerance);
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a unit vector
/// First it is parsed to a vector then the length of the vector is compared to the tolerance and normalized if within.
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">A point at the coordinates specified</param>
/// <param name="tolerance">The tolerance for how big deviation from Length = 1 is accepted</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out UnitVector3D result, double tolerance = 0.1)
{
if (Text.TryParse3D(text, formatProvider, out var x, out var y, out var z))
{
var temp = new Vector3D(x, y, z);
if (Math.Abs(temp.Length - 1) < tolerance)
{
result = temp.Normalize();
return true;
}
}
result = default(UnitVector3D);
return false;
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a vector
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="tolerance">The tolerance for how big deviation from Length = 1 is accepted</param>
/// <returns>A point at the coordinates specified</returns>
public static UnitVector3D Parse(string value, IFormatProvider formatProvider = null, double tolerance = 0.1)
{
if (TryParse(value, formatProvider, out var p, tolerance))
{
return p;
}
throw new FormatException($"Could not parse a UnitVector3D from the string {value}");
}
/// <summary>
/// Creates an <see cref="UnitVector3D"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="UnitVector3D"/>.</param>
/// <returns>An <see cref="UnitVector3D"/> that contains the data read from the reader.</returns>
public static UnitVector3D ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<UnitVector3D>();
}
/// <summary>
/// Scale this instance by <paramref name="factor"/>
/// </summary>
/// <param name="factor">The plane to project on.</param>
/// <returns>The projected <see cref="Ray3D"/></returns>
[Pure]
public Vector3D ScaleBy(double factor)
{
return factor * this;
}
/// <summary>
/// Project this instance onto the plane
/// </summary>
/// <param name="plane">The plane to project on.</param>
/// <returns>The projected <see cref="Ray3D"/></returns>
[Pure]
public Ray3D ProjectOn(Plane3D plane)
{
return plane.Project(this.ToVector3D());
}
/// <summary>
/// Returns the Dot product of the current vector and a unit vector
/// </summary>
/// <param name="uv">A unit vector</param>
/// <returns>Returns a new vector</returns>
[Pure]
public Vector3D ProjectOn(UnitVector3D uv)
{
var pd = this.DotProduct(uv);
return pd * this;
}
/// <summary>
/// Computes whether or not this vector is parallel to another vector using the dot product method and comparing it
/// to within a specified tolerance.
/// </summary>
/// <param name="othervector">The other <see cref="Vector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of unity, false if it is not</returns>
[Pure]
public bool IsParallelTo(Vector3D othervector, double tolerance = 1e-10)
{
var other = othervector.Normalize();
return this.IsParallelTo(other, tolerance);
}
/// <summary>
/// Computes whether or not this vector is parallel to a unit vector using the dot product method and comparing it
/// to within a specified tolerance.
/// </summary>
/// <param name="othervector">The other <see cref="UnitVector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of unity, false if not</returns>
[Pure]
public bool IsParallelTo(UnitVector3D othervector, double tolerance = 1e-10)
{
// This is the master method for all Vector3D and UnitVector3D IsParallelTo comparisons. Everything else
// ends up here sooner or later.
var dp = Math.Abs(this.DotProduct(othervector));
return Math.Abs(1 - dp) <= tolerance;
}
/// <summary>
/// Determine whether or not this vector is parallel to another vector within a given angle tolerance.
/// </summary>
/// <param name="othervector">The other <see cref="Vector3D"/></param>
/// <param name="angleTolerance">The tolerance for when the vectors are considered parallel.</param>
/// <returns>true if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(UnitVector3D othervector, Angle angleTolerance)
{
// Compute the angle between these vectors
var angle = this.AngleTo(othervector);
// Compute the 180° opposite of the angle
var opposite = Angle.FromDegrees(180) - angle;
// Check against the smaller of the two
return ((angle < opposite) ? angle : opposite) < angleTolerance;
}
/// <summary>
/// Determine whether or not this vector is parallel to a unit vector within a given angle tolerance.
/// </summary>
/// <param name="othervector">The other <see cref="UnitVector3D"/></param>
/// <param name="angleTolerance">The tolerance for when the vectors are considered parallel.</param>
/// <returns>true if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(Vector3D othervector, Angle angleTolerance)
{
var other = othervector.Normalize();
return this.IsParallelTo(other, angleTolerance);
}
/// <summary>
/// Computes whether or not this vector is perpendicular to another vector using the dot product method and
/// comparing it to within a specified tolerance
/// </summary>
/// <param name="othervector">The other <see cref="Vector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of zero, false if not</returns>
[Pure]
public bool IsPerpendicularTo(Vector3D othervector, double tolerance = 1e-10)
{
var other = othervector.Normalize();
return Math.Abs(this.DotProduct(other)) < tolerance;
}
/// <summary>
/// Computes whether or not this vector is perpendicular to another vector using the dot product method and
/// comparing it to within a specified tolerance
/// </summary>
/// <param name="othervector">The other <see cref="UnitVector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of zero, false if not</returns>
[Pure]
public bool IsPerpendicularTo(UnitVector3D othervector, double tolerance = 1e-10)
{
return Math.Abs(this.DotProduct(othervector)) < tolerance;
}
/// <summary>
/// Inverses the direction of the vector, equivalent to multiplying by -1
/// </summary>
/// <returns>A <see cref="Vector3D"/> pointing in the opposite direction.</returns>
[Pure]
public UnitVector3D Negate()
{
return UnitVector3D.Create(-1 * this.X, -1 * this.Y, -1 * this.Z);
}
/// <summary>
/// Returns the dot product of two vectors.
/// </summary>
/// <param name="v">The second vector.</param>
/// <returns>The dot product.</returns>
[Pure]
public double DotProduct(Vector3D v)
{
return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z);
}
/// <summary>
/// Returns the dot product of two vectors.
/// </summary>
/// <param name="v">The second vector.</param>
/// <returns>The dot product.</returns>
[Pure]
public double DotProduct(UnitVector3D v)
{
var dp = (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z);
return Math.Max(-1, Math.Min(dp, 1));
}
/// <summary>
/// Subtracts a vector from this
/// </summary>
/// <param name="v">a vector to subtract</param>
/// <returns>A new vector</returns>
[Pure]
public Vector3D Subtract(UnitVector3D v)
{
return new Vector3D(this.X - v.X, this.Y - v.Y, this.Z - v.Z);
}
/// <summary>
/// Adds a vector to this
/// </summary>
/// <param name="v">a vector to add</param>
/// <returns>A new vector</returns>
[Pure]
public Vector3D Add(UnitVector3D v)
{
return new Vector3D(this.X + v.X, this.Y + v.Y, this.Z + v.Z);
}
/// <summary>
/// Returns the cross product of this vector and a vector
/// </summary>
/// <param name="other">A vector</param>
/// <returns>A new vector with the cross product result</returns>
[Pure]
public UnitVector3D CrossProduct(UnitVector3D other)
{
var x = (this.Y * other.Z) - (this.Z * other.Y);
var y = (this.Z * other.X) - (this.X * other.Z);
var z = (this.X * other.Y) - (this.Y * other.X);
var v = Create(x, y, z);
return v;
}
/// <summary>
/// Returns the cross product of this vector and a unit vector
/// </summary>
/// <param name="inVector3D">A vector</param>
/// <returns>A new vector with the cross product result</returns>
[Pure]
public Vector3D CrossProduct(Vector3D inVector3D)
{
var x = (this.Y * inVector3D.Z) - (this.Z * inVector3D.Y);
var y = (this.Z * inVector3D.X) - (this.X * inVector3D.Z);
var z = (this.X * inVector3D.Y) - (this.Y * inVector3D.X);
var v = new Vector3D(x, y, z);
return v;
}
/// <summary>
/// Returns a dense Matrix with the unit tensor product
/// </summary>
/// <returns>a dense matrix</returns>
[Pure]
public Matrix<double> GetUnitTensorProduct()
{
// unitTensorProduct:matrix([ux^2,ux*uy,ux*uz],[ux*uy,uy^2,uy*uz],[ux*uz,uy*uz,uz^2]),
var xy = this.X * this.Y;
var xz = this.X * this.Z;
var yz = this.Y * this.Z;
return Matrix<double>.Build.Dense(3, 3, new[] { this.X * this.X, xy, xz, xy, this.Y * this.Y, yz, xz, yz, this.Z * this.Z });
}
/// <summary>
/// Returns signed angle
/// </summary>
/// <param name="v">The vector to calculate the signed angle to </param>
/// <param name="about">The vector around which to rotate to get the correct sign</param>
/// <returns>A signed Angle</returns>
[Pure]
public Angle SignedAngleTo(Vector3D v, UnitVector3D about)
{
return this.SignedAngleTo(v.Normalize(), about);
}
/// <summary>
/// Returns signed angle
/// </summary>
/// <param name="v">The vector to calculate the signed angle to </param>
/// <param name="about">The vector around which to rotate to get the correct sign</param>
/// <returns>A signed Angle</returns>
[Pure]
public Angle SignedAngleTo(UnitVector3D v, UnitVector3D about)
{
if (this.IsParallelTo(about))
{
throw new ArgumentException("FromVector parallel to aboutVector");
}
if (v.IsParallelTo(about))
{
throw new ArgumentException("FromVector parallel to aboutVector");
}
var rp = new Plane3D(new Point3D(0, 0, 0), about);
var pfv = this.ProjectOn(rp).Direction;
var ptv = v.ProjectOn(rp).Direction;
var dp = pfv.DotProduct(ptv);
if (Math.Abs(dp - 1) < 1E-15)
{
return Angle.FromRadians(0);
}
if (Math.Abs(dp + 1) < 1E-15)
{
return Angle.FromRadians(Math.PI);
}
var angle = Math.Acos(dp);
var cpv = pfv.CrossProduct(ptv);
var sign = cpv.DotProduct(rp.Normal);
var signedAngle = sign * angle;
return Angle.FromRadians(signedAngle);
}
/// <summary>
/// The nearest angle between the vectors
/// </summary>
/// <param name="v">The other vector</param>
/// <returns>The angle</returns>
[Pure]
public Angle AngleTo(Vector3D v)
{
return this.AngleTo(v.Normalize());
}
/// <summary>
/// Compute the angle between this vector and a unit vector using the arccosine of the dot product.
/// </summary>
/// <param name="v">The other vector</param>
/// <returns>The angle between the vectors, with a range between 0° and 180°</returns>
[Pure]
public Angle AngleTo(UnitVector3D v)
{
var dp = this.DotProduct(v);
var angle = Math.Acos(dp);
return Angle.FromRadians(angle);
}
/// <summary>
/// Returns a vector that is this vector rotated the signed angle around the about vector
/// </summary>
/// <param name="about">The vector to rotate around.</param>
/// <param name="angle">The angle positive according to right hand rule.</param>
/// <returns>A rotated vector.</returns>
[Pure]
public UnitVector3D Rotate(UnitVector3D about, Angle angle)
{
var cs = CoordinateSystem3D.Rotation(angle, about);
return cs.Transform(this).Normalize();
}
/// <summary>
/// Returns a point equivalent to the vector
/// </summary>
/// <returns>A point</returns>
[Pure]
public Point3D ToPoint3D()
{
return new Point3D(this.X, this.Y, this.Z);
}
/// <summary>
/// Returns a Vector3D equivalent to this unit vector
/// </summary>
/// <returns>A vector</returns>
[Pure]
public Vector3D ToVector3D()
{
return new Vector3D(this.X, this.Y, this.Z);
}
/// <summary>
/// Transforms the vector by a coordinate system and returns the transformed.
/// </summary>
/// <param name="coordinateSystem">A coordinate system</param>
/// <returns>A new transformed vector</returns>
[Pure]
public Vector3D TransformBy(CoordinateSystem3D coordinateSystem)
{
return coordinateSystem.Transform(this.ToVector3D());
}
/// <summary>
/// Transforms a vector by multiplying it against a provided matrix
/// </summary>
/// <param name="m">The matrix to multiply</param>
/// <returns>A new transformed vector</returns>
[Pure]
public Vector3D TransformBy(Matrix<double> m)
{
return Vector3D.OfVector(m.Multiply(this.ToVector()));
}
/// <summary>
/// Convert to a Math.NET Numerics dense vector of length 3.
/// </summary>
/// <returns>A dense vector</returns>
[Pure]
public Vector<double> ToVector()
{
return Vector<double>.Build.Dense(new[] { this.X, this.Y, this.Z });
}
/// <inheritdoc />
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
[Pure]
public string ToString(IFormatProvider provider)
{
return this.ToString(null, provider);
}
/// <inheritdoc/>
[Pure]
public string ToString(string format, IFormatProvider provider = null)
{
var numberFormatInfo = provider != null ? NumberFormatInfo.GetInstance(provider) : CultureInfo.InvariantCulture.NumberFormat;
var separator = numberFormatInfo.NumberDecimalSeparator == "," ? ";" : ",";
return string.Format("({0}{1} {2}{1} {3})", this.X.ToString(format, numberFormatInfo), separator, this.Y.ToString(format, numberFormatInfo), this.Z.ToString(format, numberFormatInfo));
}
/// <summary>
/// Returns a value to indicate if a pair of vectors are equal
/// </summary>
/// <param name="other">The vector to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the vectors are equal; otherwise false</returns>
[Pure]
public bool Equals(UnitVector3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance &&
Math.Abs(other.Z - this.Z) < tolerance;
}
/// <summary>
/// Returns a value to indicate if a pair of vectors are equal
/// </summary>
/// <param name="other">The vector to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>True if the vectors are equal; otherwise false</returns>
[Pure]
public bool Equals(Vector3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance &&
Math.Abs(other.Z - this.Z) < tolerance;
}
/// <inheritdoc />
[Pure]
public bool Equals(Vector3D other)
{
return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
}
/// <inheritdoc />
[Pure]
public bool Equals(UnitVector3D other)
{
return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
return (obj is UnitVector3D u && this.Equals(u)) || (obj is Vector3D v && this.Equals(v));
}
/// <inheritdoc/>
[Pure]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
bool TryGetUnitVector(double xv, double yv, double zv, out UnitVector3D result)
{
var temp = new Vector3D(xv, yv, zv);
if (Math.Abs(temp.Length - 1) < 1E-3)
{
result = temp.Normalize();
return true;
}
result = default(UnitVector3D);
return false;
}
if (reader.TryReadAttributeAsDouble("X", out var x) &&
reader.TryReadAttributeAsDouble("Y", out var y) &&
reader.TryReadAttributeAsDouble("Z", out var z) &&
TryGetUnitVector(x, y, z, out var uv))
{
reader.Skip();
this = uv;
return;
}
if (reader.TryReadChildElementsAsDoubles("X", "Y", "Z", out x, out y, out z) &&
TryGetUnitVector(x, y, z, out uv))
{
reader.Skip();
this = uv;
return;
}
throw new XmlException($"Could not read a {this.GetType()}");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("X", this.X);
writer.WriteAttribute("Y", this.Y);
writer.WriteAttribute("Z", this.Z);
}
/// <summary>
/// Returns the dot product of this vector with a second
/// </summary>
/// <param name="v">a second vector</param>
/// <returns>The dot product</returns>
[Pure]
internal double DotProduct(Point3D v)
{
return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z);
}
}
}

740
src/Numerics/Spatial/Euclidean3D/Vector3D.cs

@ -0,0 +1,740 @@
using System;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Spatial.Internal;
namespace MathNet.Numerics.Spatial.Euclidean3D
{
/// <summary>
/// A struct representing a vector in 3D space
/// </summary>
[Serializable]
public struct Vector3D : IXmlSerializable, IEquatable<Vector3D>, IEquatable<UnitVector3D>, IFormattable
{
/// <summary>
/// The x component.
/// </summary>
public readonly double X;
/// <summary>
/// The y component.
/// </summary>
public readonly double Y;
/// <summary>
/// The z component.
/// </summary>
public readonly double Z;
/// <summary>
/// Initializes a new instance of the <see cref="Vector3D"/> struct.
/// </summary>
/// <param name="x">The x component.</param>
/// <param name="y">The y component.</param>
/// <param name="z">The z component.</param>
public Vector3D(double x, double y, double z)
{
this.X = x;
this.Y = y;
this.Z = z;
}
/// <summary>
/// Gets an invalid vector with no values
/// </summary>
public static Vector3D NaN => new Vector3D(double.NaN, double.NaN, double.NaN);
/// <summary>
/// Gets the Euclidean Norm.
/// </summary>
[Pure]
public double Length => Math.Sqrt((this.X * this.X) + (this.Y * this.Y) + (this.Z * this.Z));
/// <summary>
/// Gets a unit vector orthogonal to this
/// </summary>
[Pure]
public UnitVector3D Orthogonal
{
get
{
if (-this.X - this.Y > 0.1)
{
return UnitVector3D.Create(this.Z, this.Z, -this.X - this.Y);
}
return UnitVector3D.Create(-this.Y - this.Z, this.X, this.X);
}
}
/// <summary>
/// Gets a dense matrix containing the cross product of this vector
/// </summary>
[Pure]
internal Matrix<double> CrossProductMatrix => Matrix<double>.Build.Dense(3, 3, new[] { 0d, this.Z, -this.Y, -this.Z, 0d, this.X, this.Y, -this.X, 0d });
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified vectors is equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are the same; otherwise false.</returns>
public static bool operator ==(Vector3D left, Vector3D right)
{
return left.Equals(right);
}
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified vectors is not equal.
/// </summary>
/// <param name="left">The first vector to compare.</param>
/// <param name="right">The second vector to compare.</param>
/// <returns>True if the vectors are different; otherwise false.</returns>
public static bool operator !=(Vector3D left, Vector3D right)
{
return !left.Equals(right);
}
/// <summary>
/// Multiplies a Matrix by a Vector
/// </summary>
/// <param name="left">A Matrix</param>
/// <param name="right">A Vector</param>
/// <returns>A new vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(Matrix<double> left, Vector3D right)
{
return left * right.ToVector();
}
/// <summary>
/// Multiplies a vector by a matrix
/// </summary>
/// <param name="left">A Vector</param>
/// <param name="right">A Matrix</param>
/// <returns>A new vector</returns>
[Obsolete("Not sure this is nice")]
public static Vector<double> operator *(Vector3D left, Matrix<double> right)
{
return left.ToVector() * right;
}
/// <summary>
/// Returns the dot product of two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A scalar result</returns>
public static double operator *(Vector3D left, Vector3D right)
{
return left.DotProduct(right);
}
/// <summary>
/// Adds two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A new summed vector</returns>
public static Vector3D operator +(Vector3D left, Vector3D right)
{
return new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
}
/// <summary>
/// Subtracts two vectors
/// </summary>
/// <param name="left">The first vector</param>
/// <param name="right">The second vector</param>
/// <returns>A new difference vector</returns>
public static Vector3D operator -(Vector3D left, Vector3D right)
{
return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
}
/// <summary>
/// Negates the vector
/// </summary>
/// <param name="v">A vector to negate</param>
/// <returns>A new negated vector</returns>
public static Vector3D operator -(Vector3D v)
{
return v.Negate();
}
/// <summary>
/// Multiplies a vector by a scalar
/// </summary>
/// <param name="d">A scalar</param>
/// <param name="v">A vector</param>
/// <returns>A scaled vector</returns>
public static Vector3D operator *(double d, Vector3D v)
{
return new Vector3D(d * v.X, d * v.Y, d * v.Z);
}
/// <summary>
/// Divides a vector by a scalar
/// </summary>
/// <param name="v">A vector</param>
/// <param name="d">A scalar</param>
/// <returns>A scaled vector</returns>
public static Vector3D operator /(Vector3D v, double d)
{
return new Vector3D(v.X / d, v.Y / d, v.Z / d);
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a vector
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="result">A vector with the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, out Vector3D result)
{
return TryParse(text, null, out result);
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a vector
/// </summary>
/// <param name="text">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <param name="result">A point at the coordinates specified</param>
/// <returns>True if <paramref name="text"/> could be parsed.</returns>
public static bool TryParse(string text, IFormatProvider formatProvider, out Vector3D result)
{
if (Text.TryParse3D(text, formatProvider, out var x, out var y, out var z))
{
result = new Vector3D(x, y, z);
return true;
}
result = default(Vector3D);
return false;
}
/// <summary>
/// Attempts to convert a string of the form x,y,z into a vector
/// </summary>
/// <param name="value">The string to be converted</param>
/// <param name="formatProvider">The <see cref="IFormatProvider"/></param>
/// <returns>A point at the coordinates specified</returns>
public static Vector3D Parse(string value, IFormatProvider formatProvider = null)
{
if (TryParse(value, formatProvider, out var p))
{
return p;
}
throw new FormatException($"Could not parse a Vector3D from the string {value}");
}
/// <summary>
/// Create a new <see cref="Vector3D"/> from a Math.NET Numerics vector of length 3.
/// </summary>
/// <param name="vector"> A vector with length 2 to populate the created instance with.</param>
/// <returns> A <see cref="Vector3D"/></returns>
public static Vector3D OfVector(Vector<double> vector)
{
if (vector.Count != 3)
{
throw new ArgumentException("The vector length must be 3 in order to convert it to a Vector3D");
}
return new Vector3D(vector.At(0), vector.At(1), vector.At(2));
}
/// <summary>
/// Creates an <see cref="Vector3D"/> from an <see cref="XmlReader"/>.
/// </summary>
/// <param name="reader">An <see cref="XmlReader"/> positioned at the node to read into this <see cref="Vector3D"/>.</param>
/// <returns>An <see cref="Vector3D"/> that contains the data read from the reader.</returns>
public static Vector3D ReadFrom(XmlReader reader)
{
return reader.ReadElementAs<Vector3D>();
}
////public static explicit operator Vector3D(System.Windows.Media.Media3D.Vector3D v)
////{
//// return new Vector3D(v.X, v.Y, v.Z);
////}
////public static explicit operator System.Windows.Media.Media3D.Vector3D(Vector3D p)
////{
//// return new System.Windows.Media.Media3D.Vector3D(p.X, p.Y, p.Z);
////}
/// <summary>
/// Compute and return a unit vector from this vector
/// </summary>
/// <returns>a normalized unit vector</returns>
[Pure]
public UnitVector3D Normalize()
{
return UnitVector3D.Create(this.X, this.Y, this.Z);
}
/// <summary>
/// Multiplies the current vector by a scalar
/// </summary>
/// <param name="scaleFactor">a scalar</param>
/// <returns>A new scaled vector</returns>
[Pure]
public Vector3D ScaleBy(double scaleFactor)
{
return scaleFactor * this;
}
/// <summary>
/// Projects the vector onto a plane
/// </summary>
/// <param name="planeToProjectOn">A geometric plane</param>
/// <returns>A ray</returns>
[Pure]
public Ray3D ProjectOn(Plane3D planeToProjectOn)
{
return planeToProjectOn.Project(this);
}
/// <summary>
/// Returns the Dot product of the current vector and a unit vector
/// </summary>
/// <param name="uv">A unit vector</param>
/// <returns>Returns a new vector</returns>
[Pure]
public Vector3D ProjectOn(UnitVector3D uv)
{
var pd = this.DotProduct(uv);
return pd * uv;
}
/// <summary>
/// Computes whether or not this vector is parallel to another vector using the dot product method and comparing it
/// to within a specified tolerance.
/// </summary>
/// <param name="other">The other <see cref="Vector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of unity, false if it is not</returns>
[Pure]
public bool IsParallelTo(Vector3D other, double tolerance = 1e-10)
{
var @this = this.Normalize();
return @this.IsParallelTo(other, tolerance);
}
/// <summary>
/// Computes whether or not this vector is parallel to a unit vector using the dot product method and comparing it
/// to within a specified tolerance.
/// </summary>
/// <param name="other">The other <see cref="UnitVector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of unity, false if not</returns>
[Pure]
public bool IsParallelTo(UnitVector3D other, double tolerance = 1e-10)
{
return this.Normalize().IsParallelTo(other, tolerance);
}
/// <summary>
/// Determine whether or not this vector is parallel to another vector within a given angle tolerance.
/// </summary>
/// <param name="other">The other <see cref="Vector3D"/></param>
/// <param name="tolerance">The tolerance for when the vectors are considered parallel.</param>
/// <returns>true if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(Vector3D other, Angle tolerance)
{
return this.Normalize().IsParallelTo(other, tolerance);
}
/// <summary>
/// Determine whether or not this vector is parallel to a unit vector within a given angle tolerance.
/// </summary>
/// <param name="other">The other <see cref="UnitVector3D"/></param>
/// <param name="tolerance">The tolerance for when the vectors are considered parallel.</param>
/// <returns>true if the vectors are parallel within the angle tolerance, false if they are not</returns>
[Pure]
public bool IsParallelTo(UnitVector3D other, Angle tolerance)
{
var @this = this.Normalize();
return @this.IsParallelTo(other, tolerance);
}
/// <summary>
/// Computes whether or not this vector is perpendicular to another vector using the dot product method and
/// comparing it to within a specified tolerance
/// </summary>
/// <param name="other">The other <see cref="Vector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of zero, false if not</returns>
[Pure]
public bool IsPerpendicularTo(Vector3D other, double tolerance = 1e-6)
{
return Math.Abs(this.Normalize().DotProduct(other.Normalize())) < tolerance;
}
/// <summary>
/// Computes whether or not this vector is perpendicular to another vector using the dot product method and
/// comparing it to within a specified tolerance
/// </summary>
/// <param name="other">The other <see cref="UnitVector3D"/></param>
/// <param name="tolerance">A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues.</param>
/// <returns>true if the vector dot product is within the given tolerance of zero, false if not</returns>
[Pure]
public bool IsPerpendicularTo(UnitVector3D other, double tolerance = 1e-6)
{
return Math.Abs(this.Normalize().DotProduct(other)) < tolerance;
}
/// <summary>
/// Inverses the direction of the vector, equivalent to multiplying by -1
/// </summary>
/// <returns>A <see cref="Vector3D"/> pointing in the opposite direction.</returns>
[Pure]
public Vector3D Negate()
{
return new Vector3D(-1 * this.X, -1 * this.Y, -1 * this.Z);
}
/// <summary>
/// Returns the dot product of two vectors.
/// </summary>
/// <param name="v">The second vector.</param>
/// <returns>The dot product.</returns>
[Pure]
public double DotProduct(Vector3D v)
{
return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z);
}
/// <summary>
/// Returns the dot product of two vectors.
/// </summary>
/// <param name="v">The second vector.</param>
/// <returns>The dot product.</returns>
[Pure]
public double DotProduct(UnitVector3D v)
{
return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z);
}
/// <summary>
/// Subtracts a vector from this
/// </summary>
/// <param name="v">a vector to subtract</param>
/// <returns>A new vector</returns>
[Pure]
public Vector3D Subtract(Vector3D v)
{
return new Vector3D(this.X - v.X, this.Y - v.Y, this.Z - v.Z);
}
/// <summary>
/// Adds a vector to this
/// </summary>
/// <param name="v">a vector to add</param>
/// <returns>A new vector</returns>
[Pure]
public Vector3D Add(Vector3D v)
{
return new Vector3D(this.X + v.X, this.Y + v.Y, this.Z + v.Z);
}
/// <summary>
/// Returns the cross product of this vector and <paramref name="other"/>
/// </summary>
/// <param name="other">A vector</param>
/// <returns>A new vector with the cross product result</returns>
[Pure]
public Vector3D CrossProduct(Vector3D other)
{
var x = (this.Y * other.Z) - (this.Z * other.Y);
var y = (this.Z * other.X) - (this.X * other.Z);
var z = (this.X * other.Y) - (this.Y * other.X);
var v = new Vector3D(x, y, z);
return v;
}
/// <summary>
/// Returns the cross product of this vector and <paramref name="other"/>
/// </summary>
/// <param name="other">A vector</param>
/// <returns>A new vector with the cross product result</returns>
[Pure]
public Vector3D CrossProduct(UnitVector3D other)
{
var x = (this.Y * other.Z) - (this.Z * other.Y);
var y = (this.Z * other.X) - (this.X * other.Z);
var z = (this.X * other.Y) - (this.Y * other.X);
var v = new Vector3D(x, y, z);
return v;
}
/// <summary>
/// Returns a dense Matrix with the unit tensor product
/// [ux^2, ux*uy, ux*uz],
/// [ux*uy, uy^2, uy*uz],
/// [ux*uz, uy*uz, uz^2]
/// </summary>
/// <returns>a dense matrix</returns>
[Pure]
public Matrix<double> GetUnitTensorProduct()
{
// unitTensorProduct:matrix([ux^2,ux*uy,ux*uz],[ux*uy,uy^2,uy*uz],[ux*uz,uy*uz,uz^2]),
var xy = this.X * this.Y;
var xz = this.X * this.Z;
var yz = this.Y * this.Z;
return Matrix<double>.Build.Dense(3, 3, new[] { this.X * this.X, xy, xz, xy, this.Y * this.Y, yz, xz, yz, this.Z * this.Z });
}
/// <summary>
/// Returns signed angle
/// </summary>
/// <param name="v">The vector to calculate the signed angle to </param>
/// <param name="about">The vector around which to rotate to get the correct sign</param>
/// <returns>A signed Angle</returns>
[Pure]
public Angle SignedAngleTo(Vector3D v, UnitVector3D about)
{
return this.Normalize().SignedAngleTo(v.Normalize(), about);
}
/// <summary>
/// Returns signed angle
/// </summary>
/// <param name="v">The vector to calculate the signed angle to </param>
/// <param name="about">The vector around which to rotate to get the correct sign</param>
/// <returns>A signed angle</returns>
[Pure]
public Angle SignedAngleTo(UnitVector3D v, UnitVector3D about)
{
return this.Normalize().SignedAngleTo(v, about);
}
/// <summary>
/// Compute the angle between this vector and another using the arccosine of the dot product.
/// </summary>
/// <param name="v">The other vector</param>
/// <returns>The angle between the vectors, with a range between 0° and 180°</returns>
[Pure]
public Angle AngleTo(Vector3D v)
{
var uv1 = this.Normalize();
var uv2 = v.Normalize();
return uv1.AngleTo(uv2);
}
/// <summary>
/// Compute the angle between this vector and a unit vector using the arccosine of the dot product.
/// </summary>
/// <param name="v">The other vector</param>
/// <returns>The angle between the vectors, with a range between 0° and 180°</returns>
[Pure]
public Angle AngleTo(UnitVector3D v)
{
var uv = this.Normalize();
return uv.AngleTo(v);
}
/// <summary>
/// Returns a vector that is this vector rotated the signed angle around the about vector
/// </summary>
/// <param name="about">A vector to rotate about</param>
/// <param name="angle">A signed angle</param>
/// <returns>A rotated vector.</returns>
[Pure]
public Vector3D Rotate(Vector3D about, Angle angle)
{
return this.Rotate(about.Normalize(), angle);
}
/// <summary>
/// Returns a vector that is this vector rotated the signed angle around the about vector
/// </summary>
/// <param name="about">A unit vector to rotate about</param>
/// <param name="angle">A signed angle</param>
/// <returns>A rotated vector.</returns>
[Pure]
public Vector3D Rotate(UnitVector3D about, Angle angle)
{
var cs = CoordinateSystem3D.Rotation(angle, about);
return cs.Transform(this);
}
/// <summary>
/// Returns a point equivalent to the vector
/// </summary>
/// <returns>A point</returns>
public Point3D ToPoint3D()
{
return new Point3D(this.X, this.Y, this.Z);
}
/// <summary>
/// Transforms the vector by a coordinate system and returns the transformed.
/// </summary>
/// <param name="coordinateSystem">A coordinate system</param>
/// <returns>A new transformed vector</returns>
[Pure]
public Vector3D TransformBy(CoordinateSystem3D coordinateSystem)
{
return coordinateSystem.Transform(this);
}
/// <summary>
/// Transforms a vector by multiplying it against a provided matrix
/// </summary>
/// <param name="m">The matrix to multiply</param>
/// <returns>A new transformed vector</returns>
[Pure]
public Vector3D TransformBy(Matrix<double> m)
{
return Vector3D.OfVector(m.Multiply(this.ToVector()));
}
/// <summary>
/// Convert to a Math.NET Numerics dense vector of length 3.
/// </summary>
/// <returns>A dense vector</returns>
[Pure]
public Vector<double> ToVector()
{
return Vector<double>.Build.Dense(new[] { this.X, this.Y, this.Z });
}
/// <inheritdoc />
[Pure]
public override string ToString()
{
return this.ToString(null, CultureInfo.InvariantCulture);
}
/// <summary>
/// Returns a string representation of this instance using the provided <see cref="IFormatProvider"/>
/// </summary>
/// <param name="provider">A <see cref="IFormatProvider"/></param>
/// <returns>The string representation of this instance.</returns>
[Pure]
public string ToString(IFormatProvider provider)
{
return this.ToString(null, provider);
}
/// <inheritdoc />
public string ToString(string format, IFormatProvider provider = null)
{
var numberFormatInfo = provider != null ? NumberFormatInfo.GetInstance(provider) : CultureInfo.InvariantCulture.NumberFormat;
var separator = numberFormatInfo.NumberDecimalSeparator == "," ? ";" : ",";
return string.Format(
"({1}{0} {2}{0} {3})",
separator,
this.X.ToString(format, numberFormatInfo),
this.Y.ToString(format, numberFormatInfo),
this.Z.ToString(format, numberFormatInfo));
}
/// <summary>
/// Returns a value to indicate if a pair of vectors are equal
/// </summary>
/// <param name="other">The vector to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the vectors are equal; otherwise false</returns>
[Pure]
public bool Equals(Vector3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance &&
Math.Abs(other.Z - this.Z) < tolerance;
}
/// <summary>
/// Returns a value to indicate if this vector is equivalent to a given unit vector
/// </summary>
/// <param name="other">The unit vector to compare against.</param>
/// <param name="tolerance">A tolerance (epsilon) to adjust for floating point error</param>
/// <returns>true if the vectors are equal; otherwise false</returns>
[Pure]
public bool Equals(UnitVector3D other, double tolerance)
{
if (tolerance < 0)
{
throw new ArgumentException("epsilon < 0");
}
return Math.Abs(other.X - this.X) < tolerance &&
Math.Abs(other.Y - this.Y) < tolerance &&
Math.Abs(other.Z - this.Z) < tolerance;
}
/// <inheritdoc />
[Pure]
public bool Equals(Vector3D other)
{
return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
}
/// <inheritdoc />
[Pure]
public bool Equals(UnitVector3D other)
{
return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z);
}
/// <inheritdoc />
[Pure]
public override bool Equals(object obj)
{
return (obj is UnitVector3D u && this.Equals(u)) || (obj is Vector3D v && this.Equals(v));
}
/// <inheritdoc />
[Pure]
public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z);
/// <inheritdoc />
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <inheritdoc />
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.TryReadAttributeAsDouble("X", out var x) &&
reader.TryReadAttributeAsDouble("Y", out var y) &&
reader.TryReadAttributeAsDouble("Z", out var z))
{
reader.Skip();
this = new Vector3D(x, y, z);
return;
}
if (reader.TryReadChildElementsAsDoubles("X", "Y", "Z", out x, out y, out z))
{
reader.Skip();
this = new Vector3D(x, y, z);
return;
}
throw new XmlException($"Could not read a {this.GetType()}");
}
/// <inheritdoc />
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteAttribute("X", this.X);
writer.WriteAttribute("Y", this.Y);
writer.WriteAttribute("Z", this.Z);
}
}
}

109
src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs

@ -0,0 +1,109 @@
using System.Diagnostics.CodeAnalysis;
namespace MathNet.Numerics.Spatial.Internal.AvlTreeSet
{
/// <summary>
/// A node of the Avl Tree
/// </summary>
/// <typeparam name="T">Any type</typeparam>
[SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "By design")]
internal sealed class AvlNode<T>
{
/// <summary>
/// Gets or sets the parent node
/// </summary>
public AvlNode<T> Parent;
/// <summary>
/// Gets or sets the left node
/// </summary>
public AvlNode<T> Left;
/// <summary>
/// Gets or sets the right node
/// </summary>
public AvlNode<T> Right;
/// <summary>
/// Gets or sets the item
/// </summary>
public T Item;
/// <summary>
/// Gets or sets the Avl balance
/// </summary>
public int Balance;
/// <summary>
/// Non recursive function that return the next ordered node
/// </summary>
/// <returns>The next node</returns>
public AvlNode<T> GetNextNode()
{
AvlNode<T> current;
if (this.Right != null)
{
current = this.Right;
while (current.Left != null)
{
current = current.Left;
}
return current;
}
current = this;
while (current.Parent != null)
{
if (current.Parent.Left == current)
{
return current.Parent;
}
current = current.Parent;
}
return null;
}
/// <summary>
/// Non recursive function that return the previous ordered node
/// </summary>
/// <returns>The previous node</returns>
public AvlNode<T> GetPreviousNode()
{
AvlNode<T> current;
if (this.Left != null)
{
current = this.Left;
while (current.Right != null)
{
current = current.Right;
}
return current;
}
current = this;
while (current.Parent != null)
{
if (current.Parent.Right == current)
{
return current.Parent;
}
current = current.Parent;
}
return null;
}
/// <inheritdoc/>
public override string ToString()
{
return $"AvlNode [{this.Item}], balance: {this.Balance}, Parent: {this.Parent?.Item.ToString()}, Left: {this.Left?.Item.ToString()}, Right: {this.Right?.Item.ToString()},";
}
}
}

92
src/Numerics/Spatial/Internal/AvlTreeSet/AvlNodeItemEnumerator.cs

@ -0,0 +1,92 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace MathNet.Numerics.Spatial.Internal.AvlTreeSet
{
/// <summary>
/// An enumerator for AvlNodeItems
/// </summary>
/// <typeparam name="T">Any type which is also a node type</typeparam>
internal class AvlNodeItemEnumerator<T> : IEnumerator<T>
{
/// <summary>
/// A reference to the tree
/// </summary>
private readonly AvlTreeSet<T> avlTree;
/// <summary>
/// The current node
/// </summary>
private AvlNode<T> current = null;
/// <summary>
/// Initializes a new instance of the <see cref="AvlNodeItemEnumerator{T}"/> class.
/// </summary>
/// <param name="avlTree">The tree to enumerate</param>
public AvlNodeItemEnumerator(AvlTreeSet<T> avlTree)
{
this.avlTree = avlTree ?? throw new ArgumentNullException("avlTree can't be null");
}
/// <summary>
/// Gets the current node
/// </summary>
public T Current
{
get
{
if (this.current == null)
{
throw new InvalidOperationException("Current is invalid");
}
return this.current.Item;
}
}
/// <summary>
/// Gets the current node
/// </summary>
object IEnumerator.Current => this.Current;
/// <summary>
/// Dispose of the enumerator
/// </summary>
public void Dispose()
{
}
/// <summary>
/// Moves to the next node
/// </summary>
/// <returns>True if the move to the next node was successful; otherwise false</returns>
public bool MoveNext()
{
if (this.current == null)
{
this.current = this.avlTree.GetFirstNode();
}
else
{
this.current = this.current.GetNextNode();
}
// Should check for an empty tree too :-)
if (this.current == null)
{
return false;
}
return true;
}
/// <summary>
/// Resets the enumerator
/// </summary>
public void Reset()
{
this.current = null;
}
}
}

943
src/Numerics/Spatial/Internal/AvlTreeSet/AvlTreeSet.cs

@ -0,0 +1,943 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
namespace MathNet.Numerics.Spatial.Internal.AvlTreeSet
{
/// <summary>
/// 2016-12-08, Eric Ouellet
/// The code is an adapted version of BitLush AvlTree: https://bitlush.com/blog/efficient-avl-tree-in-c-sharp
/// </summary>
/// <typeparam name="T">Any type</typeparam>
internal class AvlTreeSet<T> : IEnumerable<T>, IEnumerable, ICollection<T>, ICollection
{
/// <summary>
/// A comparer
/// </summary>
private readonly IComparer<T> comparer;
/// <summary>
/// The root node of the tree
/// </summary>
private AvlNode<T> root;
/// <summary>
/// a sync object
/// </summary>
private object syncRoot;
/// <summary>
/// A count of nodes
/// </summary>
private int count = 0;
/// <summary>
/// Initializes a new instance of the <see cref="AvlTreeSet{T}"/> class.
/// </summary>
/// <param name="comparer">a comparer for nodes</param>
public AvlTreeSet(IComparer<T> comparer)
{
this.comparer = comparer;
}
/// <summary>
/// Initializes a new instance of the <see cref="AvlTreeSet{T}"/> class.
/// </summary>
public AvlTreeSet()
: this(Comparer<T>.Default)
{
}
/// <summary>
/// Gets the root node
/// </summary>
public AvlNode<T> Root => this.root;
/// <summary>
/// Gets the count of nodes
/// </summary>
public int Count => this.count;
/// <summary>
/// Gets the sync object
/// </summary>
public object SyncRoot
{
get
{
if (this.syncRoot == null)
{
Interlocked.CompareExchange(ref this.syncRoot, new object(), (object)null);
}
return this.syncRoot;
}
}
/// <summary>
/// Gets a value indicating whether the tree is synchronized;
/// </summary>
public bool IsSynchronized => true;
/// <summary>
/// Gets a value indicating whether the tree is read only;
/// </summary>
public bool IsReadOnly => false;
/// <summary>
/// Gets an enumerator for the tree
/// </summary>
/// <returns>An enumerator</returns>
public IEnumerator<T> GetEnumerator()
{
return new AvlNodeItemEnumerator<T>(this);
}
/// <summary>
/// Gets a value indicating whether the tree contains an item
/// </summary>
/// <param name="item">The item to find</param>
/// <returns>True if the item is found; otherwise false;</returns>
public bool Contains(T item)
{
AvlNode<T> node = this.root;
while (node != null)
{
int compareResult = this.comparer.Compare(item, node.Item);
if (compareResult < 0)
{
node = node.Left;
}
else if (compareResult > 0)
{
node = node.Right;
}
else
{
return true;
}
}
return false;
}
/// <summary>
/// Adds an item to the tree
/// </summary>
/// <param name="item">The item to add</param>
/// <returns>True if adding was successful; otherwise false</returns>
public virtual bool Add(T item)
{
AvlNode<T> node = this.root;
while (node != null)
{
int compare = this.comparer.Compare(item, node.Item);
if (compare < 0)
{
AvlNode<T> left = node.Left;
if (left == null)
{
node.Left = new AvlNode<T> { Item = item, Parent = node };
this.AddBalance(node, 1);
return true;
}
else
{
node = left;
}
}
else if (compare > 0)
{
AvlNode<T> right = node.Right;
if (right == null)
{
node.Right = new AvlNode<T> { Item = item, Parent = node };
this.AddBalance(node, -1);
return true;
}
else
{
node = right;
}
}
else
{
return false;
}
}
this.root = new AvlNode<T> { Item = item };
this.count++;
return true;
}
/// <inheritdoc/>
public void Clear()
{
this.root = null;
this.count = 0;
}
/// <summary>
/// Gets an enumerator
/// </summary>
/// <returns>an enumerator</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/// <summary>
/// Copies to an array
/// </summary>
/// <param name="array">The array to copy to</param>
/// <param name="index">start point</param>
/// <param name="count">number of items to copy</param>
public void CopyTo(T[] array, int index, int count)
{
if (array == null)
{
throw new ArgumentNullException("'array' can't be null");
}
if (index < 0)
{
throw new ArgumentException("'index' can't be null");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("'count' should be greater or equal to 0");
}
if (index > array.Length || count > array.Length - index)
{
throw new ArgumentException("The array size is not big enough to get all items");
}
if (count == 0)
{
return;
}
int indexIter = 0;
int indexArray = 0;
AvlNode<T> current = this.GetFirstNode();
while (current.GetNextNode() != null)
{
if (indexIter >= index)
{
array[indexArray] = current.Item;
indexArray++;
count--;
if (count == 0)
{
return;
}
}
indexIter++;
}
/*
foreach (AvlNode<T> node in this.Nodes())
{
if (indexIter >= index)
{
array[indexArray] = node.Item;
indexArray++;
count--;
if (count == 0)
{
return;
}
}
indexIter++;
}*/
}
/// <inheritdoc/>
public void CopyTo(T[] array, int arrayIndex)
{
this.CopyTo(array, arrayIndex, this.Count);
}
/// <inheritdoc/>
public void CopyTo(Array array, int index)
{
this.CopyTo(array as T[], index, this.Count);
}
/// <inheritdoc/>
void ICollection<T>.Add(T item)
{
this.Add(item);
}
/// <summary>
/// Gets the first item
/// </summary>
/// <returns>The first item</returns>
public T GetFirstItem()
{
AvlNode<T> node = this.GetFirstNode();
if (node != null)
{
return node.Item;
}
return default(T);
}
/// <summary>
/// Gets the first node
/// </summary>
/// <returns>The first node</returns>
public AvlNode<T> GetFirstNode()
{
if (this.Root != null)
{
AvlNode<T> current = this.Root;
while (current.Left != null)
{
current = current.Left;
}
return current;
}
return null;
}
/// <summary>
/// gets the last item
/// </summary>
/// <returns>The last item</returns>
public T GetLastItem()
{
AvlNode<T> node = this.GetLastNode();
if (node != null)
{
return node.Item;
}
return default(T);
}
/// <summary>
/// Gets the last node
/// </summary>
/// <returns>returns the last node</returns>
public AvlNode<T> GetLastNode()
{
if (this.Root != null)
{
AvlNode<T> current = this.Root;
while (current.Right != null)
{
current = current.Right;
}
return current;
}
return null;
}
/// <summary>
/// Removes a node
/// </summary>
/// <param name="item">item to remove</param>
/// <returns>True if successful; otherwise false</returns>
public virtual bool Remove(T item)
{
AvlNode<T> node = this.root;
while (node != null)
{
if (this.comparer.Compare(item, node.Item) < 0)
{
node = node.Left;
}
else if (this.comparer.Compare(item, node.Item) > 0)
{
node = node.Right;
}
else
{
this.RemoveNode(node);
return true;
}
}
return false;
}
/// <summary>
/// Gets a node with the provided item
/// </summary>
/// <param name="item">An item to find</param>
/// <returns>The node with that item</returns>
protected AvlNode<T> GetNode(T item)
{
AvlNode<T> node = this.root;
while (node != null)
{
int compareResult = this.comparer.Compare(item, node.Item);
if (compareResult < 0)
{
node = node.Left;
}
else if (compareResult > 0)
{
node = node.Right;
}
else
{
return node;
}
}
return null;
}
/// <summary>
/// Should always be called for any inserted node
/// </summary>
/// <param name="node">A node</param>
/// <param name="balance">The balance measure</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void AddBalance(AvlNode<T> node, int balance)
{
this.count++;
while (node != null)
{
balance = node.Balance += balance;
if (balance == 0)
{
break;
}
if (balance == 2)
{
if (node.Left.Balance == 1)
{
this.RotateRight(node);
}
else
{
this.RotateLeftRight(node);
}
break;
}
if (balance == -2)
{
if (node.Right.Balance == -1)
{
this.RotateLeft(node);
}
else
{
this.RotateRightLeft(node);
}
break;
}
AvlNode<T> parent = node.Parent;
if (parent != null)
{
balance = parent.Left == node ? 1 : -1;
}
node = parent;
}
}
/// <summary>
/// Rotate the tree node left
/// </summary>
/// <param name="node">A node</param>
/// <returns>The rotated node</returns>
protected AvlNode<T> RotateLeft(AvlNode<T> node)
{
AvlNode<T> right = node.Right;
AvlNode<T> rightLeft = right.Left;
AvlNode<T> parent = node.Parent;
right.Parent = parent;
right.Left = node;
node.Right = rightLeft;
node.Parent = right;
if (rightLeft != null)
{
rightLeft.Parent = node;
}
if (node == this.root)
{
this.root = right;
}
else if (parent.Right == node)
{
parent.Right = right;
}
else
{
parent.Left = right;
}
right.Balance++;
node.Balance = -right.Balance;
return right;
}
/// <summary>
/// Rotate a tree node right
/// </summary>
/// <param name="node">a node</param>
/// <returns>The rotated tree node</returns>
protected AvlNode<T> RotateRight(AvlNode<T> node)
{
AvlNode<T> left = node.Left;
AvlNode<T> leftRight = left.Right;
AvlNode<T> parent = node.Parent;
left.Parent = parent;
left.Right = node;
node.Left = leftRight;
node.Parent = left;
if (leftRight != null)
{
leftRight.Parent = node;
}
if (node == this.root)
{
this.root = left;
}
else if (parent.Left == node)
{
parent.Left = left;
}
else
{
parent.Right = left;
}
left.Balance--;
node.Balance = -left.Balance;
return left;
}
/// <summary>
/// Rotate a tree node leftright
/// </summary>
/// <param name="node">a node</param>
/// <returns>a rotated tree node</returns>
protected AvlNode<T> RotateLeftRight(AvlNode<T> node)
{
AvlNode<T> left = node.Left;
AvlNode<T> leftRight = left.Right;
AvlNode<T> parent = node.Parent;
AvlNode<T> leftRightRight = leftRight.Right;
AvlNode<T> leftRightLeft = leftRight.Left;
leftRight.Parent = parent;
node.Left = leftRightRight;
left.Right = leftRightLeft;
leftRight.Left = left;
leftRight.Right = node;
left.Parent = leftRight;
node.Parent = leftRight;
if (leftRightRight != null)
{
leftRightRight.Parent = node;
}
if (leftRightLeft != null)
{
leftRightLeft.Parent = left;
}
if (node == this.root)
{
this.root = leftRight;
}
else if (parent.Left == node)
{
parent.Left = leftRight;
}
else
{
parent.Right = leftRight;
}
if (leftRight.Balance == -1)
{
node.Balance = 0;
left.Balance = 1;
}
else if (leftRight.Balance == 0)
{
node.Balance = 0;
left.Balance = 0;
}
else
{
node.Balance = -1;
left.Balance = 0;
}
leftRight.Balance = 0;
return leftRight;
}
/// <summary>
/// Rotate a tree node rightleft
/// </summary>
/// <param name="node">a node</param>
/// <returns>a rotated tree node</returns>
protected AvlNode<T> RotateRightLeft(AvlNode<T> node)
{
AvlNode<T> right = node.Right;
AvlNode<T> rightLeft = right.Left;
AvlNode<T> parent = node.Parent;
AvlNode<T> rightLeftLeft = rightLeft.Left;
AvlNode<T> rightLeftRight = rightLeft.Right;
rightLeft.Parent = parent;
node.Right = rightLeftLeft;
right.Left = rightLeftRight;
rightLeft.Right = right;
rightLeft.Left = node;
right.Parent = rightLeft;
node.Parent = rightLeft;
if (rightLeftLeft != null)
{
rightLeftLeft.Parent = node;
}
if (rightLeftRight != null)
{
rightLeftRight.Parent = right;
}
if (node == this.root)
{
this.root = rightLeft;
}
else if (parent.Right == node)
{
parent.Right = rightLeft;
}
else
{
parent.Left = rightLeft;
}
if (rightLeft.Balance == 1)
{
node.Balance = 0;
right.Balance = -1;
}
else if (rightLeft.Balance == 0)
{
node.Balance = 0;
right.Balance = 0;
}
else
{
node.Balance = 1;
right.Balance = 0;
}
rightLeft.Balance = 0;
return rightLeft;
}
/// <summary>
/// Removes a node
/// </summary>
/// <param name="node">node to remove</param>
protected void RemoveNode(AvlNode<T> node)
{
this.count--;
AvlNode<T> left = node.Left;
AvlNode<T> right = node.Right;
if (left == null)
{
if (right == null)
{
if (node == this.root)
{
this.root = null;
}
else
{
if (node.Parent.Left == node)
{
node.Parent.Left = null;
this.RemoveBalance(node.Parent, -1);
}
else if (node.Parent.Right == node)
{
node.Parent.Right = null;
this.RemoveBalance(node.Parent, 1);
}
}
}
else
{
Replace(node, right);
this.RemoveBalance(node, 0);
}
}
else if (right == null)
{
Replace(node, left);
this.RemoveBalance(node, 0);
}
else
{
AvlNode<T> successor = right;
if (successor.Left == null)
{
AvlNode<T> parent = node.Parent;
successor.Parent = parent;
successor.Left = left;
successor.Balance = node.Balance;
left.Parent = successor;
if (node == this.root)
{
this.root = successor;
}
else
{
if (parent.Left == node)
{
parent.Left = successor;
}
else
{
parent.Right = successor;
}
}
this.RemoveBalance(successor, 1);
}
else
{
while (successor.Left != null)
{
successor = successor.Left;
}
AvlNode<T> parent = node.Parent;
AvlNode<T> successorParent = successor.Parent;
AvlNode<T> successorRight = successor.Right;
if (successorParent.Left == successor)
{
successorParent.Left = successorRight;
}
else
{
successorParent.Right = successorRight;
}
if (successorRight != null)
{
successorRight.Parent = successorParent;
}
successor.Parent = parent;
successor.Left = left;
successor.Balance = node.Balance;
successor.Right = right;
right.Parent = successor;
left.Parent = successor;
if (node == this.root)
{
this.root = successor;
}
else
{
if (parent.Left == node)
{
parent.Left = successor;
}
else
{
parent.Right = successor;
}
}
this.RemoveBalance(successorParent, -1);
}
}
}
/// <summary>
/// Should always be called for any removed node
/// </summary>
/// <param name="node">a node</param>
/// <param name="balance">the node balance</param>
protected void RemoveBalance(AvlNode<T> node, int balance)
{
while (node != null)
{
balance = node.Balance += balance;
if (balance == 2)
{
if (node.Left.Balance >= 0)
{
node = this.RotateRight(node);
if (node.Balance == -1)
{
return;
}
}
else
{
node = this.RotateLeftRight(node);
}
}
else if (balance == -2)
{
if (node.Right.Balance <= 0)
{
node = this.RotateLeft(node);
if (node.Balance == 1)
{
return;
}
}
else
{
node = this.RotateRightLeft(node);
}
}
else if (balance != 0)
{
return;
}
AvlNode<T> parent = node.Parent;
if (parent != null)
{
balance = parent.Left == node ? -1 : 1;
}
node = parent;
}
}
/// <summary>
/// Replace a node
/// </summary>
/// <param name="target">The node to replace</param>
/// <param name="source">The replacing node</param>
private static void Replace(AvlNode<T> target, AvlNode<T> source)
{
AvlNode<T> left = source.Left;
AvlNode<T> right = source.Right;
target.Balance = source.Balance;
target.Item = source.Item;
target.Left = left;
target.Right = right;
if (left != null)
{
left.Parent = target;
}
if (right != null)
{
right.Parent = target;
}
}
/// <summary>
/// Counts the nodes recursively
/// </summary>
/// <param name="node">A node in the tree</param>
/// <returns>A count of nodes</returns>
private int RecursiveCount(AvlNode<T> node)
{
if (node == null)
{
return 0;
}
return 1 + this.RecursiveCount(node.Left) + this.RecursiveCount(node.Right);
}
/// <summary>
/// Find child max height
/// </summary>
/// <param name="node">A node in the tree</param>
/// <returns>The height</returns>
private int RecursiveGetChildMaxHeight(AvlNode<T> node)
{
if (node == null)
{
return 0;
}
int leftHeight = 0;
if (node.Left != null)
{
leftHeight = this.RecursiveGetChildMaxHeight(node.Left);
}
int rightHeight = 0;
if (node.Right != null)
{
rightHeight = this.RecursiveGetChildMaxHeight(node.Right);
}
return 1 + Math.Max(leftHeight, rightHeight);
}
}
}

1522
src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs

File diff suppressed because it is too large

65
src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs

@ -0,0 +1,65 @@
using System;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// Provides a working surface for points for ConvextHull - do not use otherwise
/// </summary>
internal struct MutablePoint : IEquatable<MutablePoint>
{
/// <summary>
/// Initializes a new instance of the <see cref="MutablePoint"/> struct.
/// </summary>
/// <param name="x">The x value</param>
/// <param name="y">The y value</param>
internal MutablePoint(double x, double y)
{
this.X = x;
this.Y = y;
}
/// <summary>
/// Gets or sets the X value
/// </summary>
internal double X { get; set; }
/// <summary>
/// Gets or sets the Y Value
/// </summary>
internal double Y { get; set; }
/// <summary>
/// Returns a value that indicates whether each pair of elements in two specified points is equal.
/// </summary>
/// <param name="p1">The first point to compare</param>
/// <param name="p2">The second point to compare</param>
/// <returns>True if the points are the same; otherwise false.</returns>
public static bool operator ==(MutablePoint p1, MutablePoint p2) => p1.Equals(p2);
/// <summary>
/// Returns a value that indicates whether any pair of elements in two specified points is not equal.
/// </summary>
/// <param name="p1">The first point to compare</param>
/// <param name="p2">The second point to compare</param>
/// <returns>True if the points are different; otherwise false.</returns>
public static bool operator !=(MutablePoint p1, MutablePoint p2) => !p1.Equals(p2);
/// <inheritdoc/>
public bool Equals(MutablePoint other)
{
return this.X == other.X && this.Y == other.Y;
}
/// <inheritdoc/>
public override bool Equals(object obj)
{
return base.Equals(obj);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}

36
src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// A comparer for convex hull's use of an Avl tree
/// </summary>
internal class QComparer : IComparer<MutablePoint>
{
/// <summary>
/// A function to compare with
/// </summary>
private readonly Func<MutablePoint, MutablePoint, int> comparer;
/// <summary>
/// Initializes a new instance of the <see cref="QComparer"/> class.
/// </summary>
/// <param name="comparer">a function to use for comparing</param>
public QComparer(Func<MutablePoint, MutablePoint, int> comparer)
{
this.comparer = comparer;
}
/// <summary>
/// Compares two points using the provided function
/// </summary>
/// <param name="pt1">the first point</param>
/// <param name="pt2">the second point</param>
/// <returns>A value of -1 if less than, a value of 1 is greater than; otherwise a value of 0</returns>
public int Compare(MutablePoint pt1, MutablePoint pt2)
{
return this.comparer(pt1, pt2);
}
}
}

188
src/Numerics/Spatial/Internal/ConvexHull/Quadrant.cs

@ -0,0 +1,188 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using MathNet.Numerics.Spatial.Internal.AvlTreeSet;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// An avl node for convex hull representing a quadrant
/// </summary>
[SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "By design")]
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "Reviewed.")]
internal abstract class Quadrant : AvlTreeSet<MutablePoint>
{
/// <summary>
/// The first point
/// </summary>
public MutablePoint FirstPoint;
/// <summary>
/// The last point
/// </summary>
public MutablePoint LastPoint;
/// <summary>
/// The root point
/// </summary>
public MutablePoint RootPoint;
/// <summary>
/// The current node
/// </summary>
protected AvlNode<MutablePoint> CurrentNode = null;
/// <summary>
/// A list of points
/// </summary>
protected MutablePoint[] ListOfPoint;
/// <summary>
/// Initializes a new instance of the <see cref="Quadrant"/> class.
/// </summary>
/// <param name="listOfPoint">a list of points</param>
/// <param name="comparer">Comparer is only used to add the second point (the last point, which is compared against the first one).</param>
internal Quadrant(MutablePoint[] listOfPoint, IComparer<MutablePoint> comparer)
: base(comparer)
{
this.ListOfPoint = listOfPoint;
}
/// <summary>
/// An enum for the side
/// </summary>
internal enum Side
{
/// <summary>
/// Unknown side
/// </summary>
Unknown = 0,
/// <summary>
/// left side
/// </summary>
Left = 1,
/// <summary>
/// right side
/// </summary>
Right = 2
}
/// <summary>
/// Prepares the node
/// </summary>
public void Prepare()
{
if (!this.ListOfPoint.Any())
{
// There is no points at all. Hey don't try to crash me.
return;
}
// Begin : General Init
this.Add(this.FirstPoint);
if (this.FirstPoint.Equals(this.LastPoint))
{
return; // Case where for weird distribution like triangle or diagonal. This quadrant will have no point
}
this.Add(this.LastPoint);
}
/// <summary>
/// Tell if should try to add and where. -1 ==> Should not add.
/// </summary>
/// <param name="point">A point</param>
internal abstract void ProcessPoint(ref MutablePoint point);
/// <summary>
/// Initialize every values needed to extract values that are parts of the convex hull.
/// This is where the first pass of all values is done the get maximum in every directions (x and y).
/// </summary>
protected abstract void SetQuadrantLimits();
/// <summary>
/// To know if to the right. It is meaningful when p1 is first and p2 is next.
/// </summary>
/// <param name="p1">The first point</param>
/// <param name="p2">The second point</param>
/// <param name="pointtToCheck">The point to check</param>
/// <returns>Equivalent of tracing a line from p1 to p2 and tell if ptToCheck
/// is to the right or left of that line taking p1 as reference point.</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool IsPointToTheRightOfOthers(MutablePoint p1, MutablePoint p2, MutablePoint pointtToCheck)
{
return ((p2.X - p1.X) * (pointtToCheck.Y - p1.Y)) - ((p2.Y - p1.Y) * (pointtToCheck.X - p1.X)) < 0;
}
/// <summary>
/// Checks if point should be in quadrant
/// </summary>
/// <param name="pt">a point</param>
/// <returns>True if it is a good quadrant for the point; otherwise false</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected abstract bool IsGoodQuadrantForPoint(MutablePoint pt);
/// <summary>
/// Called after insertion in order to see if the newly added point invalidate one
/// or more neighbors and if so, remove it/them from the tree.
/// </summary>
/// <param name="pointPrevious">Previous point</param>
/// <param name="pointNew">New point</param>
/// <param name="pointNext">Next point</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1002:SemicolonsMustBeSpacedCorrectly", Justification = "Reviewed.")]
protected void InvalidateNeighbors(AvlNode<MutablePoint> pointPrevious, AvlNode<MutablePoint> pointNew, AvlNode<MutablePoint> pointNext)
{
bool invalidPoint;
if (pointPrevious != null)
{
AvlNode<MutablePoint> previousPrevious = pointPrevious.GetPreviousNode();
for (; ;)
{
if (previousPrevious == null)
{
break;
}
invalidPoint = !this.IsPointToTheRightOfOthers(previousPrevious.Item, pointNew.Item, pointPrevious.Item);
if (!invalidPoint)
{
break;
}
MutablePoint pointPrevPrev = previousPrevious.Item;
this.RemoveNode(pointPrevious);
pointPrevious = this.GetNode(pointPrevPrev);
previousPrevious = pointPrevious.GetPreviousNode();
}
}
// Invalidate next(s)
if (pointNext != null)
{
AvlNode<MutablePoint> nextNext = pointNext.GetNextNode();
for (; ;)
{
if (nextNext == null)
{
break;
}
invalidPoint = !this.IsPointToTheRightOfOthers(pointNew.Item, nextNext.Item, pointNext.Item);
if (!invalidPoint)
{
break;
}
MutablePoint pointNextNext = nextNext.Item;
this.RemoveNode(pointNext);
pointNext = this.GetNode(pointNextNext);
nextNext = pointNext.GetNextNode();
}
}
}
}
}

240
src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific1.cs

@ -0,0 +1,240 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Internal.AvlTreeSet;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// Class to process quadrant 1
/// </summary>
internal class QuadrantSpecific1 : Quadrant
{
/// <summary>
/// Initializes a new instance of the <see cref="QuadrantSpecific1"/> class.
/// </summary>
/// <param name="listOfPoint">a list of points</param>
/// <param name="comparer">a comparer</param>
internal QuadrantSpecific1(MutablePoint[] listOfPoint, Func<MutablePoint, MutablePoint, int> comparer)
: base(listOfPoint, new QComparer(comparer))
{
}
/// <summary>
/// Check if we can quickly reject a point
/// </summary>
/// <param name="point">a point</param>
/// <param name="pointHull">a point on the hull</param>
/// <returns>True if can quickly reject; otherwise false</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull)
{
return point.X <= pointHull.X && point.Y <= pointHull.Y;
}
/// <summary>
/// Iterate over each points to see if we can add it has a ConvexHull point.
/// It is specific by Quadrant to improve efficiency.
/// </summary>
/// <param name="point">a point</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override void ProcessPoint(ref MutablePoint point)
{
this.CurrentNode = this.Root;
AvlNode<MutablePoint> currentPrevious = null;
AvlNode<MutablePoint> currentNext = null;
while (this.CurrentNode != null)
{
var insertionSide = Side.Unknown;
if (point.X > this.CurrentNode.Item.X)
{
if (this.CurrentNode.Left != null)
{
this.CurrentNode = this.CurrentNode.Left;
continue;
}
currentPrevious = this.CurrentNode.GetPreviousNode();
if (CanQuickReject(ref point, ref currentPrevious.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(currentPrevious.Item, this.CurrentNode.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
return;
}
insertionSide = Side.Left;
}
else if (point.X < this.CurrentNode.Item.X)
{
if (this.CurrentNode.Right != null)
{
this.CurrentNode = this.CurrentNode.Right;
continue;
}
currentNext = this.CurrentNode.GetNextNode();
if (CanQuickReject(ref point, ref currentNext.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(this.CurrentNode.Item, currentNext.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
return;
}
insertionSide = Side.Right;
}
else
{
if (point.Y <= this.CurrentNode.Item.Y)
{
return; // invalid point
}
// Replace CurrentNode point with point
this.CurrentNode.Item = point;
this.InvalidateNeighbors(this.CurrentNode.GetPreviousNode(), this.CurrentNode, this.CurrentNode.GetNextNode());
return;
}
// We should insert the point
// Try to optimize and verify if can replace a node instead insertion to minimize tree balancing
if (insertionSide == Side.Right)
{
currentPrevious = this.CurrentNode.GetPreviousNode();
if (currentPrevious != null && !this.IsPointToTheRightOfOthers(currentPrevious.Item, point, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var nextNext = currentNext.GetNextNode();
if (nextNext != null && !this.IsPointToTheRightOfOthers(point, nextNext.Item, currentNext.Item))
{
currentNext.Item = point;
this.InvalidateNeighbors(null, currentNext, nextNext);
return;
}
}
else
{
// Left
currentNext = this.CurrentNode.GetNextNode();
if (currentNext != null && !this.IsPointToTheRightOfOthers(point, currentNext.Item, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var previousPrevious = currentPrevious.GetPreviousNode();
if (previousPrevious != null && !this.IsPointToTheRightOfOthers(previousPrevious.Item, point, currentPrevious.Item))
{
currentPrevious.Item = point;
this.InvalidateNeighbors(previousPrevious, currentPrevious, null);
return;
}
}
// Should insert but no invalidation is required. (That's why we need to insert... can't replace an adjacent neighbor)
AvlNode<MutablePoint> newNode = new AvlNode<MutablePoint>();
if (insertionSide == Side.Right)
{
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Right = newNode;
this.AddBalance(newNode.Parent, -1);
}
else
{
// Left
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Left = newNode;
this.AddBalance(newNode.Parent, 1);
}
}
}
/// <inheritdoc />
protected override void SetQuadrantLimits()
{
MutablePoint firstPoint = this.ListOfPoint.First();
double rightX = firstPoint.X;
double rightY = firstPoint.Y;
double topX = rightX;
double topY = rightY;
foreach (var point in this.ListOfPoint)
{
if (point.X >= rightX)
{
if (point.X == rightX)
{
if (point.Y > rightY)
{
rightY = point.Y;
}
}
else
{
rightX = point.X;
rightY = point.Y;
}
}
if (point.Y >= topY)
{
if (point.Y == topY)
{
if (point.X > topX)
{
topX = point.X;
}
}
else
{
topX = point.X;
topY = point.Y;
}
}
}
this.FirstPoint = new MutablePoint(rightX, rightY);
this.LastPoint = new MutablePoint(topX, topY);
this.RootPoint = new MutablePoint(topX, rightY);
}
/// <inheritdoc />
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsGoodQuadrantForPoint(MutablePoint pt)
{
if (pt.X > this.RootPoint.X && pt.Y > this.RootPoint.Y)
{
return true;
}
return false;
}
}
}

241
src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific2.cs

@ -0,0 +1,241 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Internal.AvlTreeSet;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// Class to process quadrant 2
/// </summary>
internal class QuadrantSpecific2 : Quadrant
{
/// <summary>
/// Initializes a new instance of the <see cref="QuadrantSpecific2"/> class.
/// </summary>
/// <param name="listOfPoint">a list of points</param>
/// <param name="comparer">a comparer</param>
internal QuadrantSpecific2(MutablePoint[] listOfPoint, Func<MutablePoint, MutablePoint, int> comparer)
: base(listOfPoint, new QComparer(comparer))
{
}
/// <summary>
/// Check if we can quickly reject a point
/// </summary>
/// <param name="point">a point</param>
/// <param name="pointHull">a point on the hull</param>
/// <returns>True if can quickly reject; otherwise false</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull)
{
return point.X >= pointHull.X && point.Y <= pointHull.Y;
}
/// <summary>
/// Iterate over each points to see if we can add it has a ConvexHull point.
/// It is specific by Quadrant to improve efficiency.
/// </summary>
/// <param name="point">a point</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override void ProcessPoint(ref MutablePoint point)
{
this.CurrentNode = this.Root;
AvlNode<MutablePoint> currentPrevious = null;
AvlNode<MutablePoint> currentNext = null;
while (this.CurrentNode != null)
{
var insertionSide = Side.Unknown;
if (point.X > this.CurrentNode.Item.X)
{
if (this.CurrentNode.Left != null)
{
this.CurrentNode = this.CurrentNode.Left;
continue;
}
currentPrevious = this.CurrentNode.GetPreviousNode();
if (CanQuickReject(ref point, ref currentPrevious.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(currentPrevious.Item, this.CurrentNode.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
return;
}
insertionSide = Side.Left;
}
else if (point.X < this.CurrentNode.Item.X)
{
if (this.CurrentNode.Right != null)
{
this.CurrentNode = this.CurrentNode.Right;
continue;
}
currentNext = this.CurrentNode.GetNextNode();
if (CanQuickReject(ref point, ref currentNext.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(this.CurrentNode.Item, currentNext.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
continue;
}
insertionSide = Side.Right;
}
else
{
if (point.Y <= this.CurrentNode.Item.Y)
{
return; // invalid point
}
// Replace CurrentNode point with point
this.CurrentNode.Item = point;
this.InvalidateNeighbors(this.CurrentNode.GetPreviousNode(), this.CurrentNode, this.CurrentNode.GetNextNode());
return;
}
// We should insert the point
// Try to optimize and verify if can replace a node instead insertion to minimize tree balancing
if (insertionSide == Side.Right)
{
currentPrevious = this.CurrentNode.GetPreviousNode();
if (currentPrevious != null && !this.IsPointToTheRightOfOthers(currentPrevious.Item, point, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var nextNext = currentNext.GetNextNode();
if (nextNext != null && !this.IsPointToTheRightOfOthers(point, nextNext.Item, currentNext.Item))
{
currentNext.Item = point;
this.InvalidateNeighbors(null, currentNext, nextNext);
return;
}
}
else
{
// Left
currentNext = this.CurrentNode.GetNextNode();
if (currentNext != null && !this.IsPointToTheRightOfOthers(point, currentNext.Item, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var previousPrevious = currentPrevious.GetPreviousNode();
if (previousPrevious != null && !this.IsPointToTheRightOfOthers(previousPrevious.Item, point, currentPrevious.Item))
{
currentPrevious.Item = point;
this.InvalidateNeighbors(previousPrevious, currentPrevious, null);
return;
}
}
// Should insert but no invalidation is required. (That's why we need to insert... can't replace an adjacent neighbor)
AvlNode<MutablePoint> newNode = new AvlNode<MutablePoint>();
if (insertionSide == Side.Right)
{
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Right = newNode;
this.AddBalance(newNode.Parent, -1);
}
else
{
// Left
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Left = newNode;
this.AddBalance(newNode.Parent, 1);
}
return;
}
}
/// <inheritdoc />
protected override void SetQuadrantLimits()
{
MutablePoint firstPoint = this.ListOfPoint.First();
double leftX = firstPoint.X;
double leftY = firstPoint.Y;
double topX = leftX;
double topY = leftY;
foreach (var point in this.ListOfPoint)
{
if (point.X <= leftX)
{
if (point.X == leftX)
{
if (point.Y > leftY)
{
leftY = point.Y;
}
}
else
{
leftX = point.X;
leftY = point.Y;
}
}
if (point.Y >= topY)
{
if (point.Y == topY)
{
if (point.X < topX)
{
topX = point.X;
}
}
else
{
topX = point.X;
topY = point.Y;
}
}
}
this.FirstPoint = new MutablePoint(topX, topY);
this.LastPoint = new MutablePoint(leftX, leftY);
this.RootPoint = new MutablePoint(topX, leftY);
}
/// <inheritdoc />
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsGoodQuadrantForPoint(MutablePoint pt)
{
if (pt.X < this.RootPoint.X && pt.Y > this.RootPoint.Y)
{
return true;
}
return false;
}
}
}

243
src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific3.cs

@ -0,0 +1,243 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Internal.AvlTreeSet;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// Class to process quadrant 3
/// </summary>
internal class QuadrantSpecific3 : Quadrant
{
/// <summary>
/// Initializes a new instance of the <see cref="QuadrantSpecific3"/> class.
/// </summary>
/// <param name="listOfPoint">a list of points</param>
/// <param name="comparer">a comparer</param>
internal QuadrantSpecific3(MutablePoint[] listOfPoint, Func<MutablePoint, MutablePoint, int> comparer)
: base(listOfPoint, new QComparer(comparer))
{
}
/// <summary>
/// Check if we can quickly reject a point
/// </summary>
/// <param name="point">a point</param>
/// <param name="pointHull">a point on the hull</param>
/// <returns>True if can quickly reject; otherwise false</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull)
{
return point.X >= pointHull.X && point.Y >= pointHull.Y;
}
/// <summary>
/// Iterate over each points to see if we can add it has a ConvexHull point.
/// It is specific by Quadrant to improve efficiency.
/// </summary>
/// <param name="point">a point</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override void ProcessPoint(ref MutablePoint point)
{
this.CurrentNode = this.Root;
AvlNode<MutablePoint> currentPrevious = null;
AvlNode<MutablePoint> currentNext = null;
while (this.CurrentNode != null)
{
var insertionSide = Side.Unknown;
if (point.X > this.CurrentNode.Item.X)
{
if (this.CurrentNode.Right != null)
{
this.CurrentNode = this.CurrentNode.Right;
continue;
}
currentNext = this.CurrentNode.GetNextNode();
if (CanQuickReject(ref point, ref currentNext.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(this.CurrentNode.Item, currentNext.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
continue;
}
insertionSide = Side.Right;
}
else if (point.X < this.CurrentNode.Item.X)
{
if (this.CurrentNode.Left != null)
{
this.CurrentNode = this.CurrentNode.Left;
continue;
}
currentPrevious = this.CurrentNode.GetPreviousNode();
if (CanQuickReject(ref point, ref currentPrevious.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(currentPrevious.Item, this.CurrentNode.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
return;
}
insertionSide = Side.Left;
}
else
{
if (point.Y >= this.CurrentNode.Item.Y)
{
return; // invalid point
}
// Replace CurrentNode point with point
this.CurrentNode.Item = point;
this.InvalidateNeighbors(this.CurrentNode.GetPreviousNode(), this.CurrentNode, this.CurrentNode.GetNextNode());
return;
}
// We should insert the point
// Try to optimize and verify if can replace a node instead insertion to minimize tree balancing
if (insertionSide == Side.Right)
{
currentPrevious = this.CurrentNode.GetPreviousNode();
if (currentPrevious != null && !this.IsPointToTheRightOfOthers(currentPrevious.Item, point, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var nextNext = currentNext.GetNextNode();
if (nextNext != null && !this.IsPointToTheRightOfOthers(point, nextNext.Item, currentNext.Item))
{
currentNext.Item = point;
this.InvalidateNeighbors(null, currentNext, nextNext);
return;
}
}
else
{
// Left
currentNext = this.CurrentNode.GetNextNode();
if (currentNext != null && !this.IsPointToTheRightOfOthers(point, currentNext.Item, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var previousPrevious = currentPrevious.GetPreviousNode();
if (previousPrevious != null && !this.IsPointToTheRightOfOthers(previousPrevious.Item, point, currentPrevious.Item))
{
currentPrevious.Item = point;
this.InvalidateNeighbors(previousPrevious, currentPrevious, null);
return;
}
}
// Should insert but no invalidation is required. (That's why we need to insert... can't replace an adjacent neighbor)
AvlNode<MutablePoint> newNode = new AvlNode<MutablePoint>();
if (insertionSide == Side.Right)
{
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Right = newNode;
this.AddBalance(newNode.Parent, -1);
}
else
{
// Left
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Left = newNode;
this.AddBalance(newNode.Parent, 1);
}
return;
}
return;
}
/// <inheritdoc />
protected override void SetQuadrantLimits()
{
MutablePoint firstPoint = this.ListOfPoint.First();
double leftX = firstPoint.X;
double leftY = firstPoint.Y;
double bottomX = leftX;
double bottomY = leftY;
foreach (var point in this.ListOfPoint)
{
if (point.X <= leftX)
{
if (point.X == leftX)
{
if (point.Y < leftY)
{
leftY = point.Y;
}
}
else
{
leftX = point.X;
leftY = point.Y;
}
}
if (point.Y <= bottomY)
{
if (point.Y == bottomY)
{
if (point.X < bottomX)
{
bottomX = point.X;
}
}
else
{
bottomX = point.X;
bottomY = point.Y;
}
}
}
this.FirstPoint = new MutablePoint(leftX, leftY);
this.LastPoint = new MutablePoint(bottomX, bottomY);
this.RootPoint = new MutablePoint(bottomX, leftY);
}
/// <inheritdoc />
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsGoodQuadrantForPoint(MutablePoint pt)
{
if (pt.X < this.RootPoint.X && pt.Y < this.RootPoint.Y)
{
return true;
}
return false;
}
}
}

243
src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific4.cs

@ -0,0 +1,243 @@
using System;
using System.Linq;
using MathNet.Numerics.Spatial.Internal.AvlTreeSet;
namespace MathNet.Numerics.Spatial.Internal.ConvexHull
{
/// <summary>
/// Class to process quadrant 4
/// </summary>
internal class QuadrantSpecific4 : Quadrant
{
/// <summary>
/// Initializes a new instance of the <see cref="QuadrantSpecific4"/> class.
/// </summary>
/// <param name="listOfPoint">a list of points</param>
/// <param name="comparer">a comparer</param>
internal QuadrantSpecific4(MutablePoint[] listOfPoint, Func<MutablePoint, MutablePoint, int> comparer)
: base(listOfPoint, new QComparer(comparer))
{
}
/// <summary>
/// Check if we can quickly reject a point
/// </summary>
/// <param name="point">a point</param>
/// <param name="pointHull">a point on the hull</param>
/// <returns>True if can quickly reject; otherwise false</returns>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull)
{
return point.X <= pointHull.X && point.Y >= pointHull.Y;
}
/// <summary>
/// Iterate over each points to see if we can add it has a ConvexHull point.
/// It is specific by Quadrant to improve efficiency.
/// </summary>
/// <param name="point">a point</param>
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal override void ProcessPoint(ref MutablePoint point)
{
this.CurrentNode = this.Root;
AvlNode<MutablePoint> currentPrevious = null;
AvlNode<MutablePoint> currentNext = null;
while (this.CurrentNode != null)
{
var insertionSide = Side.Unknown;
if (point.X > this.CurrentNode.Item.X)
{
if (this.CurrentNode.Right != null)
{
this.CurrentNode = this.CurrentNode.Right;
continue;
}
currentNext = this.CurrentNode.GetNextNode();
if (CanQuickReject(ref point, ref currentNext.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(this.CurrentNode.Item, currentNext.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
continue;
}
insertionSide = Side.Right;
}
else if (point.X < this.CurrentNode.Item.X)
{
if (this.CurrentNode.Left != null)
{
this.CurrentNode = this.CurrentNode.Left;
continue;
}
currentPrevious = this.CurrentNode.GetPreviousNode();
if (CanQuickReject(ref point, ref currentPrevious.Item))
{
return;
}
if (!this.IsPointToTheRightOfOthers(currentPrevious.Item, this.CurrentNode.Item, point))
{
return;
}
// Ensure to have no duplicate
if (this.CurrentNode.Item == point)
{
continue;
}
insertionSide = Side.Left;
}
else
{
if (point.Y >= this.CurrentNode.Item.Y)
{
return; // invalid point
}
// Replace CurrentNode point with point
this.CurrentNode.Item = point;
this.InvalidateNeighbors(this.CurrentNode.GetPreviousNode(), this.CurrentNode, this.CurrentNode.GetNextNode());
return;
}
// We should insert the point
// Try to optimize and verify if can replace a node instead insertion to minimize tree balancing
if (insertionSide == Side.Right)
{
currentPrevious = this.CurrentNode.GetPreviousNode();
if (currentPrevious != null && !this.IsPointToTheRightOfOthers(currentPrevious.Item, point, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var nextNext = currentNext.GetNextNode();
if (nextNext != null && !this.IsPointToTheRightOfOthers(point, nextNext.Item, currentNext.Item))
{
currentNext.Item = point;
this.InvalidateNeighbors(null, currentNext, nextNext);
return;
}
}
else
{
// Left
currentNext = this.CurrentNode.GetNextNode();
if (currentNext != null && !this.IsPointToTheRightOfOthers(point, currentNext.Item, this.CurrentNode.Item))
{
this.CurrentNode.Item = point;
this.InvalidateNeighbors(currentPrevious, this.CurrentNode, currentNext);
return;
}
var previousPrevious = currentPrevious.GetPreviousNode();
if (previousPrevious != null && !this.IsPointToTheRightOfOthers(previousPrevious.Item, point, currentPrevious.Item))
{
currentPrevious.Item = point;
this.InvalidateNeighbors(previousPrevious, currentPrevious, null);
return;
}
}
// Should insert but no invalidation is required. (That's why we need to insert... can't replace an adjacent neighbor)
AvlNode<MutablePoint> newNode = new AvlNode<MutablePoint>();
if (insertionSide == Side.Right)
{
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Right = newNode;
this.AddBalance(newNode.Parent, -1);
}
else
{
// Left
newNode.Parent = this.CurrentNode;
newNode.Item = point;
this.CurrentNode.Left = newNode;
this.AddBalance(newNode.Parent, 1);
}
return;
}
return;
}
/// <inheritdoc />
protected override void SetQuadrantLimits()
{
MutablePoint firstPoint = this.ListOfPoint.First();
double rightX = firstPoint.X;
double rightY = firstPoint.Y;
double bottomX = rightX;
double bottomY = rightY;
foreach (var point in this.ListOfPoint)
{
if (point.X >= rightX)
{
if (point.X == rightX)
{
if (point.Y < rightY)
{
rightY = point.Y;
}
}
else
{
rightX = point.X;
rightY = point.Y;
}
}
if (point.Y <= bottomY)
{
if (point.Y == bottomY)
{
if (point.X > bottomX)
{
bottomX = point.X;
}
}
else
{
bottomX = point.X;
bottomY = point.Y;
}
}
}
this.FirstPoint = new MutablePoint(bottomX, bottomY);
this.LastPoint = new MutablePoint(rightX, rightY);
this.RootPoint = new MutablePoint(bottomX, rightY);
}
/// <inheritdoc />
//// [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override bool IsGoodQuadrantForPoint(MutablePoint pt)
{
if (pt.X > this.RootPoint.X && pt.Y < this.RootPoint.Y)
{
return true;
}
return false;
}
}
}

380
src/Numerics/Spatial/Internal/HashCode.cs

@ -0,0 +1,380 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using MathNet.Numerics.Random;
namespace MathNet.Numerics.Spatial.Internal
{
// The contents of this file is taken from https://github.com/dotnet/coreclr/blob/master/src/mscorlib/shared/System/HashCode.cs
// To be replaced by the framework implementation when released for the appropriate builds
#pragma warning disable SA1203, SA1101, SA1600, SA1512, SA1515, SA1503, SA1028, SA1308, SA1512, SA1202, SA1311, SA1028, SA1132, SA1309, SA1108, SA1520
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
/*
The xxHash32 implementation is based on the code published by Yann Collet:
https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c
xxHash - Fast Hash algorithm
Copyright (C) 2012-2016, Yann Collet
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
You can contact the author at :
- xxHash homepage: http://www.xxhash.com
- xxHash source repository : https://github.com/Cyan4973/xxHash
*/
// xxHash32 is used for the hash code.
// https://github.com/Cyan4973/xxHash
/// <summary>
/// Generates a hashcode
/// </summary>
internal static class HashCode
{
private static readonly uint Seed = GenerateGlobalSeed();
private const uint Prime1 = 2654435761U;
private const uint Prime2 = 2246822519U;
private const uint Prime3 = 3266489917U;
private const uint Prime4 = 668265263U;
private const uint Prime5 = 374761393U;
private static uint GenerateGlobalSeed()
{
// NOTE: Modified from original unsafe implementation.
int seed = RandomSeed.Robust();
return unchecked((uint)seed);
}
public static int Combine<T1>(T1 value1)
{
// Provide a way of diffusing bits from something with a limited
// input hash space. For example, many enums only have a few
// possible hashes, only using the bottom few bits of the code. Some
// collections are built on the assumption that hashes are spread
// over a larger space, so diffusing the bits may help the
// collection work more efficiently.
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
uint hash = MixEmptyState();
hash += 4;
hash = QueueRound(hash, hc1);
hash = MixFinal(hash);
return (int)hash;
}
public static int CombineMany<T>(T[] items)
{
if (items == null)
{
return 0;
}
unchecked
{
int hashcode = 0;
for (var i = 0; i < items.Length; i++)
{
// HashCode.Combine(single) is partially diffuse so should be ok for this.
hashcode += Combine(items[i]);
}
return hashcode;
}
}
public static int CombineMany<T>(List<T> items)
{
if (items == null)
{
return 0;
}
unchecked
{
int hashcode = 0;
for (var i = 0; i < items.Count; i++)
{
// HashCode.Combine(single) is partially diffuse so should be ok for this.
hashcode += Combine(items[i]);
}
return hashcode;
}
}
public static int CombineMany<T>(IEnumerable<T> items)
{
if (items == null)
{
return 0;
}
unchecked
{
int hashcode = 0;
foreach (var item in items)
{
// HashCode.Combine(single) is partially diffuse so should be ok for this.
hashcode += Combine(item);
}
return hashcode;
}
}
public static int Combine<T1, T2>(T1 value1, T2 value2)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
uint hash = MixEmptyState();
hash += 8;
hash = QueueRound(hash, hc1);
hash = QueueRound(hash, hc2);
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
uint hash = MixEmptyState();
hash += 12;
hash = QueueRound(hash, hc1);
hash = QueueRound(hash, hc2);
hash = QueueRound(hash, hc3);
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
var hc4 = (uint)(value4?.GetHashCode() ?? 0);
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
v1 = Round(v1, hc1);
v2 = Round(v2, hc2);
v3 = Round(v3, hc3);
v4 = Round(v4, hc4);
uint hash = MixState(v1, v2, v3, v4);
hash += 16;
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
var hc4 = (uint)(value4?.GetHashCode() ?? 0);
var hc5 = (uint)(value5?.GetHashCode() ?? 0);
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
v1 = Round(v1, hc1);
v2 = Round(v2, hc2);
v3 = Round(v3, hc3);
v4 = Round(v4, hc4);
uint hash = MixState(v1, v2, v3, v4);
hash += 20;
hash = QueueRound(hash, hc5);
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
var hc4 = (uint)(value4?.GetHashCode() ?? 0);
var hc5 = (uint)(value5?.GetHashCode() ?? 0);
var hc6 = (uint)(value6?.GetHashCode() ?? 0);
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
v1 = Round(v1, hc1);
v2 = Round(v2, hc2);
v3 = Round(v3, hc3);
v4 = Round(v4, hc4);
uint hash = MixState(v1, v2, v3, v4);
hash += 24;
hash = QueueRound(hash, hc5);
hash = QueueRound(hash, hc6);
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
var hc4 = (uint)(value4?.GetHashCode() ?? 0);
var hc5 = (uint)(value5?.GetHashCode() ?? 0);
var hc6 = (uint)(value6?.GetHashCode() ?? 0);
var hc7 = (uint)(value7?.GetHashCode() ?? 0);
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
v1 = Round(v1, hc1);
v2 = Round(v2, hc2);
v3 = Round(v3, hc3);
v4 = Round(v4, hc4);
uint hash = MixState(v1, v2, v3, v4);
hash += 28;
hash = QueueRound(hash, hc5);
hash = QueueRound(hash, hc6);
hash = QueueRound(hash, hc7);
hash = MixFinal(hash);
return (int)hash;
}
public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8)
{
var hc1 = (uint)(value1?.GetHashCode() ?? 0);
var hc2 = (uint)(value2?.GetHashCode() ?? 0);
var hc3 = (uint)(value3?.GetHashCode() ?? 0);
var hc4 = (uint)(value4?.GetHashCode() ?? 0);
var hc5 = (uint)(value5?.GetHashCode() ?? 0);
var hc6 = (uint)(value6?.GetHashCode() ?? 0);
var hc7 = (uint)(value7?.GetHashCode() ?? 0);
var hc8 = (uint)(value8?.GetHashCode() ?? 0);
Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
v1 = Round(v1, hc1);
v2 = Round(v2, hc2);
v3 = Round(v3, hc3);
v4 = Round(v4, hc4);
v1 = Round(v1, hc5);
v2 = Round(v2, hc6);
v3 = Round(v3, hc7);
v4 = Round(v4, hc8);
uint hash = MixState(v1, v2, v3, v4);
hash += 32;
hash = MixFinal(hash);
return (int)hash;
}
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static uint Rol(uint value, int count)
=> (value << count) | (value >> (32 - count));
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4)
{
v1 = Seed + Prime1 + Prime2;
v2 = Seed + Prime2;
v3 = Seed;
v4 = Seed - Prime1;
}
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static uint Round(uint hash, uint input)
{
hash += input * Prime2;
hash = Rol(hash, 13);
hash *= Prime1;
return hash;
}
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static uint QueueRound(uint hash, uint queuedValue)
{
hash += queuedValue * Prime3;
return Rol(hash, 17) * Prime4;
}
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static uint MixState(uint v1, uint v2, uint v3, uint v4)
{
return Rol(v1, 1) + Rol(v2, 7) + Rol(v3, 12) + Rol(v4, 18);
}
private static uint MixEmptyState()
{
return Seed + Prime5;
}
#if !NET40
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
private static uint MixFinal(uint hash)
{
hash ^= hash >> 15;
hash *= Prime2;
hash ^= hash >> 13;
hash *= Prime3;
hash ^= hash >> 16;
return hash;
}
}
}

43
src/Numerics/Spatial/Internal/ImmutableList.cs

@ -0,0 +1,43 @@
using System.Collections.Generic;
using System.Linq;
namespace MathNet.Numerics.Spatial.Internal
{
/// <summary>
/// Internal implementation of an immutable list
/// </summary>
internal static class ImmutableList
{
/// <summary>
/// Factory Construction
/// </summary>
/// <typeparam name="T">The list type</typeparam>
/// <param name="data">A list of items to initialize with</param>
/// <returns>An immutable list</returns>
internal static ImmutableList<T> Create<T>(IEnumerable<T> data)
{
return ImmutableList<T>.Empty.AddRange(data.AsCollection());
}
/// <summary>
/// Gets the passed source as a collection
/// </summary>
/// <typeparam name="T">the list type</typeparam>
/// <param name="source">the list source</param>
/// <returns>A collection</returns>
private static ICollection<T> AsCollection<T>(this IEnumerable<T> source)
{
if (source is ICollection<T> collection)
{
return collection;
}
if (source is ImmutableList<T> list)
{
return list.GetRawData();
}
return source.ToList();
}
}
}

113
src/Numerics/Spatial/Internal/ImmutableListOfT.cs

@ -0,0 +1,113 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace MathNet.Numerics.Spatial.Internal
{
/// <summary>
/// An internal implementation of ImmutableList
/// </summary>
/// <typeparam name="T">A type for the list</typeparam>
internal sealed class ImmutableList<T> : IEnumerable<T>
{
/// <summary>
/// An empty list
/// </summary>
internal static readonly ImmutableList<T> Empty = new ImmutableList<T>(new T[0]);
/// <summary>
/// The list data
/// </summary>
private readonly T[] data;
/// <summary>
/// Initializes a new instance of the <see cref="ImmutableList{T}"/> class.
/// </summary>
/// <param name="data">The data to initialize the list with</param>
private ImmutableList(T[] data)
{
this.data = data;
}
/// <summary>
/// Gets the number of items in the list
/// </summary>
internal int Count => this.data.Length;
/// <summary>
/// An 0 based index into the list
/// </summary>
/// <param name="index">the index</param>
/// <returns>A list item</returns>
internal T this[int index] => this.data[index];
/// <inheritdoc />
public IEnumerator<T> GetEnumerator() => ((IList<T>)this.data).GetEnumerator();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => this.data.GetEnumerator();
/// <summary>
/// Adds an item to the list
/// </summary>
/// <param name="value">An item</param>
/// <returns>A new list with the item added</returns>
[Pure]
internal ImmutableList<T> Add(T value)
{
var newData = new T[this.data.Length + 1];
Array.Copy(this.data, newData, this.data.Length);
newData[this.data.Length] = value;
return new ImmutableList<T>(newData);
}
/// <summary>
/// Adds a range of items to the list
/// </summary>
/// <param name="values">The items to add</param>
/// <returns>A new list with the items added</returns>
[Pure]
internal ImmutableList<T> AddRange(ICollection<T> values)
{
var newData = new T[this.data.Length + values.Count];
Array.Copy(this.data, newData, this.data.Length);
values.CopyTo(newData, this.data.Length);
return new ImmutableList<T>(newData);
}
/// <summary>
/// Removes an item from the list
/// </summary>
/// <param name="value">The item to remove</param>
/// <returns>A new list with the item removed</returns>
[Pure]
internal ImmutableList<T> Remove(T value)
{
var i = Array.IndexOf(this.data, value);
if (i < 0)
{
return this;
}
var length = this.data.Length;
if (length == 1)
{
return Empty;
}
var newData = new T[length - 1];
Array.Copy(this.data, 0, newData, 0, i);
Array.Copy(this.data, i + 1, newData, i, length - i - 1);
return new ImmutableList<T>(newData);
}
/// <summary>
/// An internal method to access the underlying data. To be used with care.
/// </summary>
/// <returns>The backing data array</returns>
internal T[] GetRawData() => this.data;
}
}

429
src/Numerics/Spatial/Internal/Text.cs

@ -0,0 +1,429 @@
using System;
using System.Globalization;
using System.Text.RegularExpressions;
namespace MathNet.Numerics.Spatial.Internal
{
/// <summary>
/// Internal text processing
/// </summary>
internal static class Text
{
/// <summary>
/// regex pattern with period
/// </summary>
private const string DoublePatternPointProvider = "[+-]?\\d*(?:[.]\\d+)?(?:[eE][+-]?\\d+)?";
/// <summary>
/// regex pattern with comma
/// </summary>
private const string DoublePatternCommaProvider = "[+-]?\\d*(?:[,]\\d+)?(?:[eE][+-]?\\d+)?";
/// <summary>
/// Separator with period
/// </summary>
private const string SeparatorPatternPointProvider = " ?[,;]?( |\u00A0)?";
/// <summary>
/// Separator with comma
/// </summary>
private const string SeparatorPatternCommaProvider = " ?[;]?( |\u00A0)?";
/// <summary>
/// Attempts to parse a string into x, y coordinates
/// </summary>
/// <param name="text">a string</param>
/// <param name="provider">a format provider</param>
/// <param name="x">The x value</param>
/// <param name="y">The y value</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool TryParse2D(string text, IFormatProvider provider, out double x, out double y)
{
x = 0;
y = 0;
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
if (Regex2D.TryMatch(text, provider, out var match) &&
match.Groups.Count == 3 &&
match.Groups[0].Captures.Count == 1 &&
match.Groups[1].Captures.Count == 1 &&
match.Groups[2].Captures.Count == 1)
{
return TryParseDouble(match.Groups["x"].Value, provider, out x) &&
TryParseDouble(match.Groups["y"].Value, provider, out y);
}
return false;
}
/// <summary>
/// Attempts to parse a string into x, y, z coordinates
/// </summary>
/// <param name="text">A string</param>
/// <param name="provider">A format provider</param>
/// <param name="x">The x value</param>
/// <param name="y">The y value</param>
/// <param name="z">The z value</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool TryParse3D(string text, IFormatProvider provider, out double x, out double y, out double z)
{
x = 0;
y = 0;
z = 0;
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
if (Regex3D.TryMatch(text, provider, out var match) &&
match.Groups.Count == 4 &&
match.Groups[0].Captures.Count == 1 &&
match.Groups[1].Captures.Count == 1 &&
match.Groups[2].Captures.Count == 1 &&
match.Groups[3].Captures.Count == 1)
{
return TryParseDouble(match.Groups["x"].Value, provider, out x) &&
TryParseDouble(match.Groups["y"].Value, provider, out y) &&
TryParseDouble(match.Groups["z"].Value, provider, out z);
}
return false;
}
/// <summary>
/// Attempts to parse a string into an Angle
/// </summary>
/// <param name="text">A string</param>
/// <param name="provider">A format provider</param>
/// <param name="a">An angle</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool TryParseAngle(string text, IFormatProvider provider, out Angle a)
{
a = default(Angle);
if (string.IsNullOrWhiteSpace(text))
{
return false;
}
if (RegexAngle.TryMatchDegrees(text, provider, out var value))
{
a = Angle.FromDegrees(value);
return true;
}
if (RegexAngle.TryMatchRadians(text, provider, out value))
{
a = Angle.FromRadians(value);
return true;
}
return false;
}
/// <summary>
/// Attempts to parse a double
/// </summary>
/// <param name="s">A string</param>
/// <param name="formatProvider">A format provider</param>
/// <param name="result">A double</param>
/// <returns>True if successful; otherwise false</returns>
private static bool TryParseDouble(string s, IFormatProvider formatProvider, out double result)
{
if (formatProvider == null)
{
// This is for legacy reasons, we allow any culture, not nice.
// Fixing would break
return double.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out result);
}
return double.TryParse(s, NumberStyles.Float, formatProvider, out result);
}
/// <summary>
/// A class providing regex matching for 2D values
/// </summary>
private static class Regex2D
{
/// <summary>
/// The pattern for this regex
/// </summary>
private const string Pattern2D = "^ *\\(?(?<x>{0}){1}(?<y>{0})\\)? *$";
/// <summary>
/// The standard options
/// </summary>
private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline;
/// <summary>
/// A regex containing a .
/// </summary>
private static readonly Regex Point = new Regex(
string.Format(Pattern2D, DoublePatternPointProvider, SeparatorPatternPointProvider),
RegexOptions);
/// <summary>
/// a regex containing a ,
/// </summary>
private static readonly Regex Comma = new Regex(
string.Format(Pattern2D, DoublePatternCommaProvider, SeparatorPatternCommaProvider),
RegexOptions);
/// <summary>
/// Attempts to match a string
/// </summary>
/// <param name="text">a string</param>
/// <param name="formatProvider">a format provider</param>
/// <param name="match">the match</param>
/// <returns>True if successful; Otherwise false</returns>
internal static bool TryMatch(string text, IFormatProvider formatProvider, out Match match)
{
if (formatProvider != null &&
NumberFormatInfo.GetInstance(formatProvider) is NumberFormatInfo formatInfo)
{
if (formatInfo.NumberDecimalSeparator == ".")
{
match = Point.Match(text);
return match.Success;
}
if (formatInfo.NumberDecimalSeparator == ",")
{
match = Comma.Match(text);
return match.Success;
}
}
match = Point.Match(text);
if (match.Success)
{
return true;
}
match = Comma.Match(text);
return match.Success;
}
}
/// <summary>
/// A class providing regex matching for 3D values
/// </summary>
private static class Regex3D
{
/// <summary>
/// The pattern for this regex
/// </summary>
private const string Pattern3D = "^ *\\(?(?<x>{0}){1}(?<y>{0}){1}(?<z>{0})\\)? *$";
/// <summary>
/// The standard options
/// </summary>
private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline;
/// <summary>
/// A regex containing a .
/// </summary>
private static readonly Regex Point = new Regex(
string.Format(Pattern3D, DoublePatternPointProvider, SeparatorPatternPointProvider),
RegexOptions);
/// <summary>
/// A regex containing a ,
/// </summary>
private static readonly Regex Comma = new Regex(
string.Format(Pattern3D, DoublePatternCommaProvider, SeparatorPatternCommaProvider),
RegexOptions);
/// <summary>
/// Attempts to match a string
/// </summary>
/// <param name="text">a string</param>
/// <param name="formatProvider">a format provider</param>
/// <param name="match">the match</param>
/// <returns>True if successful; Otherwise false</returns>
internal static bool TryMatch(string text, IFormatProvider formatProvider, out Match match)
{
if (formatProvider != null &&
NumberFormatInfo.GetInstance(formatProvider) is NumberFormatInfo formatInfo)
{
if (formatInfo.NumberDecimalSeparator == ".")
{
match = Point.Match(text);
return match.Success;
}
if (formatInfo.NumberDecimalSeparator == ",")
{
match = Comma.Match(text);
return match.Success;
}
}
match = Point.Match(text);
if (match.Success)
{
return true;
}
match = Comma.Match(text);
return match.Success;
}
}
/// <summary>
/// A class providing regex matching for angle values
/// </summary>
private static class RegexAngle
{
/// <summary>
/// A regex for radians angles
/// </summary>
private const string RadiansPattern = "^(?<value>{0})( |\u00A0)?(°|rad|radians) *$";
/// <summary>
/// A regex for degrees angles
/// </summary>
private const string DegreesPattern = "^(?<value>{0})( |\u00A0)?(°|deg|degrees) *$";
/// <summary>
/// Standard regex options
/// </summary>
private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase;
/// <summary>
/// Radians with a point
/// </summary>
private static readonly Regex RadiansPoint = new Regex(
string.Format(RadiansPattern, DoublePatternPointProvider),
RegexOptions);
/// <summary>
/// Radians with a comma
/// </summary>
private static readonly Regex RadiansComma = new Regex(
string.Format(RadiansPattern, DoublePatternCommaProvider),
RegexOptions);
/// <summary>
/// Degrees with a point
/// </summary>
private static readonly Regex DegreesPoint = new Regex(
string.Format(DegreesPattern, DoublePatternPointProvider),
RegexOptions);
/// <summary>
/// Degrees with a comma
/// </summary>
private static readonly Regex DegreesComma = new Regex(
string.Format(DegreesPattern, DoublePatternCommaProvider),
RegexOptions);
/// <summary>
/// Attempts to match Degrees
/// </summary>
/// <param name="text">a string</param>
/// <param name="provider">a format provider</param>
/// <param name="value">a double</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool TryMatchDegrees(string text, IFormatProvider provider, out double value)
{
if (TryMatchDegrees(text, provider, out Match match))
{
return TryParseDouble(match.Groups["value"].Value, provider, out value);
}
value = 0;
return false;
}
/// <summary>
/// Attempts to match Radians
/// </summary>
/// <param name="text">a string</param>
/// <param name="provider">a format provider</param>
/// <param name="value">a double</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool TryMatchRadians(string text, IFormatProvider provider, out double value)
{
if (TryMatchRadians(text, provider, out Match match))
{
return TryParseDouble(match.Groups["value"].Value, provider, out value);
}
value = 0;
return false;
}
/// <summary>
/// Attempts to match Radians with either . or , separators
/// </summary>
/// <param name="text">a string</param>
/// <param name="formatProvider">a format provider</param>
/// <param name="match">a list of matches</param>
/// <returns>True if successful; otherwise false</returns>
private static bool TryMatchRadians(string text, IFormatProvider formatProvider, out Match match)
{
if (formatProvider != null &&
NumberFormatInfo.GetInstance(formatProvider) is NumberFormatInfo formatInfo)
{
if (formatInfo.NumberDecimalSeparator == ".")
{
match = RadiansPoint.Match(text);
return match.Success;
}
if (formatInfo.NumberDecimalSeparator == ",")
{
match = RadiansComma.Match(text);
return match.Success;
}
}
match = RadiansPoint.Match(text);
if (match.Success)
{
return true;
}
match = RadiansComma.Match(text);
return match.Success;
}
/// <summary>
/// Attempts to match Degrees with either . or , separators
/// </summary>
/// <param name="text">a string</param>
/// <param name="provider">a format provider</param>
/// <param name="match">a list of matches</param>
/// <returns>True if successful; otherwise false</returns>
private static bool TryMatchDegrees(string text, IFormatProvider provider, out Match match)
{
if (provider != null && NumberFormatInfo.GetInstance(provider) is NumberFormatInfo formatInfo)
{
if (formatInfo.NumberDecimalSeparator == ".")
{
match = DegreesPoint.Match(text);
return match.Success;
}
if (formatInfo.NumberDecimalSeparator == ",")
{
match = DegreesComma.Match(text);
return match.Success;
}
}
match = DegreesPoint.Match(text);
if (match.Success)
{
return true;
}
match = DegreesComma.Match(text);
return match.Success;
}
}
}
}

261
src/Numerics/Spatial/Internal/XmlReaderExt.cs

@ -0,0 +1,261 @@
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace MathNet.Numerics.Spatial.Internal
{
/// <summary>
/// An extension class for XmlReader
/// </summary>
internal static class XmlReaderExt
{
/// <summary>
/// Creates a default(T) and calls ReadXml(reader) on it.
/// </summary>
/// <typeparam name="T">The type of the instance to read from the current position of the reader.</typeparam>
/// <param name="reader">A <see cref="XmlReader"/></param>
/// <returns> A new instance of {T} with values from <paramref name="reader"/></returns>
internal static T ReadElementAs<T>(this XmlReader reader)
where T : struct, IXmlSerializable
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
var instance = default(T);
instance.ReadXml(reader);
return instance;
}
/// <summary>
/// Reads the attribute named <paramref name="localName"/> if it exists on the current element.
/// This is not a proper try method as it checks if <paramref name="reader"/> is null and throws.
/// </summary>
/// <param name="reader">A <see cref="XmlReader"/></param>
/// <param name="localName">The name of the attribute to read the value of.</param>
/// <param name="value">The value read from <paramref name="reader"/></param>
/// <returns>True if the attribute was found.</returns>
internal static bool TryReadAttributeAsDouble(this XmlReader reader, string localName, out double value)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
if (reader.MoveToContent() == XmlNodeType.Element &&
reader.HasAttributes &&
reader.MoveToAttribute(localName))
{
value = XmlConvert.ToDouble(reader.Value);
return true;
}
value = 0;
return false;
}
/// <summary>
/// Reads the values of the elements named <paramref name="localName"/> and <paramref name="localName"/> if they exist on the current element.
/// This is not a proper try method as it checks if <paramref name="reader"/> is null and throws.
/// </summary>
/// <remarks>
/// Calling this method has side effects as it changes the position of the reader.
/// </remarks>
/// <param name="reader">A <see cref="XmlReader"/></param>
/// <param name="localName">The local name of the element to read value from.</param>
/// <param name="value">The value read from <paramref name="reader"/></param>
/// <returns>True if both elements were found.</returns>
internal static bool TryReadElementContentAsDouble(this XmlReader reader, string localName, out double value)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
if (reader.MoveToContent() == XmlNodeType.Element &&
!reader.IsEmptyElement &&
reader.LocalName == localName)
{
value = reader.ReadElementContentAsDouble();
return true;
}
value = 0;
return false;
}
/// <summary>
/// Reads the values of the elements named <paramref name="xName"/> and <paramref name="xName"/> if they exist on the current element.
/// This is not a proper try method as it checks if <paramref name="reader"/> is null and throws.
/// </summary>
/// <remarks>
/// Calling this method has side effects as it changes the position of the reader.
/// </remarks>
/// <param name="reader">A <see cref="XmlReader"/></param>
/// <param name="xName">The local name of the x element.</param>
/// <param name="yName">The local name of the y element.</param>
/// <param name="x">The x value read from <paramref name="reader"/></param>
/// <param name="y">The y value read from <paramref name="reader"/></param>
/// <returns>True if both elements were found.</returns>
internal static bool TryReadChildElementsAsDoubles(this XmlReader reader, string xName, string yName, out double x, out double y)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
x = 0;
y = 0;
if (reader.MoveToContent() == XmlNodeType.Element &&
!reader.IsEmptyElement &&
!reader.HasValue)
{
var subtree = reader.ReadSubtree();
if (subtree.ReadToFirstDescendant())
{
if (subtree.TryReadSiblingElementsAsDoubles(xName, yName, out x, out y))
{
subtree.Skip();
return true;
}
}
}
return false;
}
/// <summary>
/// Reads the values of the elements named <paramref name="xName"/> and <paramref name="xName"/> if they exist on the current element.
/// This is not a proper try method as it checks if <paramref name="reader"/> is null and throws.
/// </summary>
/// <remarks>
/// Calling this method has side effects as it changes the position of the reader.
/// </remarks>
/// <param name="reader">A <see cref="XmlReader"/></param>
/// <param name="xName">The local name of the x element.</param>
/// <param name="yName">The local name of the y element.</param>
/// <param name="zName">The local name of the z element.</param>
/// <param name="x">The x value read from <paramref name="reader"/></param>
/// <param name="y">The y value read from <paramref name="reader"/></param>
/// <param name="z">The z value read from <paramref name="reader"/></param>
/// <returns>True if both elements were found.</returns>
internal static bool TryReadChildElementsAsDoubles(this XmlReader reader, string xName, string yName, string zName, out double x, out double y, out double z)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
x = 0;
y = 0;
z = 0;
if (reader.MoveToContent() == XmlNodeType.Element &&
!reader.IsEmptyElement &&
!reader.HasValue)
{
var subtree = reader.ReadSubtree();
if (subtree.ReadToFirstDescendant())
{
if (subtree.TryReadElementContentAsDouble(xName, out x))
{
if (subtree.TryReadSiblingElementsAsDoubles(yName, zName, out y, out z))
{
subtree.Skip();
return true;
}
return false;
}
if (subtree.TryReadElementContentAsDouble(yName, out y))
{
if (subtree.TryReadSiblingElementsAsDoubles(xName, zName, out x, out z))
{
subtree.Skip();
return true;
}
return false;
}
if (subtree.TryReadElementContentAsDouble(zName, out z))
{
if (subtree.TryReadSiblingElementsAsDoubles(xName, yName, out x, out y))
{
subtree.Skip();
return true;
}
return false;
}
}
}
return false;
}
/// <summary>
/// Reads until first descendant
/// </summary>
/// <param name="reader">An xml reader</param>
/// <returns>True if successful; otherwise false</returns>
internal static bool ReadToFirstDescendant(this XmlReader reader)
{
var depth = reader.Depth;
if (reader.MoveToContent() != XmlNodeType.Element)
{
return reader.Depth > depth &&
reader.NodeType == XmlNodeType.Element;
}
while (reader.Read() &&
reader.Depth <= depth)
{
}
return reader.Depth > depth &&
reader.NodeType == XmlNodeType.Element;
}
/// <summary>
/// Attempts to read sibling elements as a pair of doubles
/// </summary>
/// <param name="subtree">an xml reader</param>
/// <param name="xName">The name of the x element</param>
/// <param name="yName">The name of the y element</param>
/// <param name="x">The x value</param>
/// <param name="y">The y value</param>
/// <returns>True if successful; otherwise false</returns>
private static bool TryReadSiblingElementsAsDoubles(this XmlReader subtree, string xName, string yName, out double x, out double y)
{
if (subtree.TryReadElementContentAsDouble(xName, out x) &&
subtree.TryReadElementContentAsDouble(yName, out y))
{
return true;
}
if (subtree.TryReadElementContentAsDouble(yName, out y) &&
subtree.TryReadElementContentAsDouble(xName, out x))
{
return true;
}
return false;
}
public static XElement SingleElement(this XElement e, string localName)
{
return e.Elements()
.Single(x => x.Name.LocalName == localName);
}
public static XmlReader SingleElementReader(this XElement e, string localName)
{
return e.SingleElement(localName)
.CreateReader();
}
}
}

58
src/Numerics/Spatial/Internal/XmlWriterExt.cs

@ -0,0 +1,58 @@
using System.Globalization;
using System.Xml;
using System.Xml.Serialization;
namespace MathNet.Numerics.Spatial.Internal
{
/// <summary>
/// An extension class for XmlWriter
/// </summary>
internal static class XmlWriterExt
{
/// <summary>
/// Writes an element
/// </summary>
/// <param name="writer">An Xml Writer</param>
/// <param name="name">The element name</param>
/// <param name="value">The value</param>
internal static void WriteElement(this XmlWriter writer, string name, IXmlSerializable value)
{
writer.WriteStartElement(name);
value.WriteXml(writer);
writer.WriteEndElement();
}
/// <summary>
/// Writes an element
/// </summary>
/// <param name="writer">An Xml Writer</param>
/// <param name="name">The element name</param>
/// <param name="value">The value</param>
internal static void WriteElement(this XmlWriter writer, string name, double value)
{
writer.WriteStartElement(name);
writer.WriteValue(value);
writer.WriteEndElement();
}
/// <summary>
/// Writes an element
/// </summary>
/// <param name="writer">An Xml Writer</param>
/// <param name="name">The element name</param>
/// <param name="value">The value</param>
/// <param name="format">a format to apply to the value</param>
internal static void WriteElement(this XmlWriter writer, string name, double value, string format)
{
writer.WriteElementString(name, value.ToString(format, CultureInfo.InvariantCulture));
}
internal static XmlWriter WriteAttribute<T>(this XmlWriter writer, string name, T value)
{
writer.WriteStartAttribute(name);
writer.WriteValue(value);
writer.WriteEndAttribute();
return writer;
}
}
}

1
src/Numerics/paket.references

@ -4,3 +4,4 @@ System.Runtime.Serialization.Xml framework:netstandard1.3
System.Runtime.Serialization.Primitives framework:netstandard1.3
System.Runtime.Serialization.Formatters framework:netstandard1.3
System.Security.Cryptography.Algorithms framework:netstandard1.3
System.Diagnostics.Contracts framework:netstandard1.3

Loading…
Cancel
Save