From 05407a8e8617d15f0769d1a74f17545bf175c832 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sun, 24 Jun 2018 13:18:49 +0200 Subject: [PATCH] Spatial: import Math.NET Spatial: euclidean 2D and 3D --- paket.dependencies | 1 + paket.lock | 136 +- src/Numerics/Spatial/Angle.cs | 397 +++++ src/Numerics/Spatial/Euclidean2D/Circle2D.cs | 137 ++ src/Numerics/Spatial/Euclidean2D/Line2D.cs | 270 +++ .../Spatial/Euclidean2D/LineSegment2D.cs | 207 +++ src/Numerics/Spatial/Euclidean2D/Matrix2D.cs | 36 + src/Numerics/Spatial/Euclidean2D/Point2D.cs | 410 +++++ .../Spatial/Euclidean2D/PolyLine2D.cs | 342 ++++ src/Numerics/Spatial/Euclidean2D/Polygon2D.cs | 419 +++++ src/Numerics/Spatial/Euclidean2D/Vector2D.cs | 612 +++++++ src/Numerics/Spatial/Euclidean3D/Circle3D.cs | 193 +++ .../Spatial/Euclidean3D/CoordinateSystem3D.cs | 725 ++++++++ src/Numerics/Spatial/Euclidean3D/Line3D.cs | 344 ++++ .../Spatial/Euclidean3D/LineSegment3D.cs | 298 ++++ src/Numerics/Spatial/Euclidean3D/Matrix3D.cs | 126 ++ src/Numerics/Spatial/Euclidean3D/Plane3D.cs | 526 ++++++ src/Numerics/Spatial/Euclidean3D/Point3D.cs | 522 ++++++ .../Spatial/Euclidean3D/PolyLine3D.cs | 256 +++ src/Numerics/Spatial/Euclidean3D/Ray3D.cs | 222 +++ .../Spatial/Euclidean3D/UnitVector3D.cs | 958 +++++++++++ src/Numerics/Spatial/Euclidean3D/Vector3D.cs | 767 +++++++++ .../Spatial/Internal/AvlTreeSet/AvlNode.cs | 109 ++ .../AvlTreeSet/AvlNodeItemEnumerator.cs | 92 + .../Spatial/Internal/AvlTreeSet/AvlTreeSet.cs | 943 ++++++++++ .../Spatial/Internal/ConvexHull/ConvexHull.cs | 1522 +++++++++++++++++ .../Internal/ConvexHull/MutablePoint.cs | 65 + .../Spatial/Internal/ConvexHull/QComparer.cs | 36 + .../Spatial/Internal/ConvexHull/Quadrant.cs | 188 ++ .../Internal/ConvexHull/QuadrantSpecific1.cs | 240 +++ .../Internal/ConvexHull/QuadrantSpecific2.cs | 241 +++ .../Internal/ConvexHull/QuadrantSpecific3.cs | 243 +++ .../Internal/ConvexHull/QuadrantSpecific4.cs | 243 +++ src/Numerics/Spatial/Internal/HashCode.cs | 497 ++++++ .../Spatial/Internal/ImmutableList.cs | 43 + .../Spatial/Internal/ImmutableListOfT.cs | 113 ++ src/Numerics/Spatial/Internal/Parser.cs | 63 + src/Numerics/Spatial/Internal/Text.cs | 429 +++++ src/Numerics/Spatial/Internal/XmlReaderExt.cs | 261 +++ src/Numerics/Spatial/Internal/XmlWriterExt.cs | 58 + src/Numerics/paket.references | 1 + 41 files changed, 13224 insertions(+), 67 deletions(-) create mode 100644 src/Numerics/Spatial/Angle.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Circle2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Line2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/LineSegment2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Matrix2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Point2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Polygon2D.cs create mode 100644 src/Numerics/Spatial/Euclidean2D/Vector2D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Circle3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Line3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/LineSegment3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Matrix3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Plane3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Point3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Ray3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs create mode 100644 src/Numerics/Spatial/Euclidean3D/Vector3D.cs create mode 100644 src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs create mode 100644 src/Numerics/Spatial/Internal/AvlTreeSet/AvlNodeItemEnumerator.cs create mode 100644 src/Numerics/Spatial/Internal/AvlTreeSet/AvlTreeSet.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/Quadrant.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific1.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific2.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific3.cs create mode 100644 src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific4.cs create mode 100644 src/Numerics/Spatial/Internal/HashCode.cs create mode 100644 src/Numerics/Spatial/Internal/ImmutableList.cs create mode 100644 src/Numerics/Spatial/Internal/ImmutableListOfT.cs create mode 100644 src/Numerics/Spatial/Internal/Parser.cs create mode 100644 src/Numerics/Spatial/Internal/Text.cs create mode 100644 src/Numerics/Spatial/Internal/XmlReaderExt.cs create mode 100644 src/Numerics/Spatial/Internal/XmlWriterExt.cs diff --git a/paket.dependencies b/paket.dependencies index 8a2d166d..1960fc38 100644 --- a/paket.dependencies +++ b/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 diff --git a/paket.lock b/paket.lock index b35276a2..e3d47627 100644 --- a/paket.lock +++ b/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) diff --git a/src/Numerics/Spatial/Angle.cs b/src/Numerics/Spatial/Angle.cs new file mode 100644 index 00000000..6e122b55 --- /dev/null +++ b/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 +{ + /// + /// An angle + /// + [Serializable] + public struct Angle : IComparable, IEquatable, IFormattable, IXmlSerializable + { + /// + /// The value in radians + /// + public readonly double Radians; + + /// + /// Conversion factor for converting Radians to Degrees + /// + private const double RadToDeg = 180.0 / Math.PI; + + /// + /// Conversion factor for converting Degrees to Radians + /// + private const double DegToRad = Math.PI / 180.0; + + /// + /// Initializes a new instance of the struct. + /// + /// The value in Radians + private Angle(double radians) + { + this.Radians = radians; + } + + /// + /// Gets the value in degrees + /// + public double Degrees => this.Radians * RadToDeg; + + /// + /// Returns a value that indicates whether two specified Angles are equal. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the angles are the same; otherwise false. + public static bool operator ==(Angle left, Angle right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether two specified Angles are not equal. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the angles are different; otherwise false. + public static bool operator !=(Angle left, Angle right) + { + return !left.Equals(right); + } + + /// + /// Returns a value that indicates if a specified Angles is less than another. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the first angle is less than the second angle; otherwise false. + public static bool operator <(Angle left, Angle right) + { + return left.Radians < right.Radians; + } + + /// + /// Returns a value that indicates if a specified Angles is greater than another. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the first angle is greater than the second angle; otherwise false. + public static bool operator >(Angle left, Angle right) + { + return left.Radians > right.Radians; + } + + /// + /// Returns a value that indicates if a specified Angles is less than or equal to another. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the first angle is less than or equal to the second angle; otherwise false. + public static bool operator <=(Angle left, Angle right) + { + return left.Radians <= right.Radians; + } + + /// + /// Returns a value that indicates if a specified Angles is greater than or equal to another. + /// + /// The first angle to compare + /// The second angle to compare + /// True if the first angle is greater than or equal to the second angle; otherwise false. + public static bool operator >=(Angle left, Angle right) + { + return left.Radians >= right.Radians; + } + + /// + /// Multiplies an Angle by a scalar + /// + /// The scalar. + /// The angle. + /// A new angle equal to the product of the angle and the scalar. + public static Angle operator *(double left, Angle right) + { + return new Angle(left * right.Radians); + } + + /// + /// Multiplies an Angle by a scalar + /// + /// The angle. + /// The scalar. + /// A new angle equal to the product of the angle and the scalar. + public static Angle operator *(Angle left, double right) + { + return new Angle(left.Radians * right); + } + + /// + /// Divides an Angle by a scalar + /// + /// The angle. + /// The scalar. + /// A new angle equal to the division of the angle by the scalar. + public static Angle operator /(Angle left, double right) + { + return new Angle(left.Radians / right); + } + + /// + /// Adds two angles together + /// + /// The first angle. + /// The second angle. + /// A new Angle equal to the sum of the provided angles. + public static Angle operator +(Angle left, Angle right) + { + return new Angle(left.Radians + right.Radians); + } + + /// + /// Subtracts the second angle from the first + /// + /// The first angle. + /// The second angle. + /// A new Angle equal to the difference of the provided angles. + public static Angle operator -(Angle left, Angle right) + { + return new Angle(left.Radians - right.Radians); + } + + /// + /// Negates the angle + /// + /// The angle to negate. + /// The negated angle. + public static Angle operator -(Angle angle) + { + return new Angle(-1 * angle.Radians); + } + + /// + /// Attempts to convert a string into an + /// + /// The string to be converted + /// Am + /// True if could be parsed. + public static bool TryParse(string text, out Angle result) + { + return TryParse(text, null, out result); + } + + /// + /// Attempts to convert a string into an + /// + /// The string to be converted + /// The + /// An + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string into an + /// + /// The string to be converted + /// The + /// An + 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}"); + } + + /// + /// Creates a new instance of Angle. + /// + /// The value in degrees. + /// A new instance of the struct. + public static Angle FromDegrees(double value) + { + return new Angle(value * DegToRad); + } + + /// + /// Creates a new instance of Angle. + /// + /// The value in radians. + /// A new instance of the struct. + public static Angle FromRadians(double value) + { + return new Angle(value); + } + + /// + /// Creates a new instance of Angle from the sexagesimal format of the angle in degrees, minutes, seconds + /// + /// The degrees of the angle + /// The minutes of the angle + /// The seconds of the angle + /// A new instance of the struct. + public static Angle FromSexagesimal(int degrees, int minutes, double seconds) + { + return Angle.FromDegrees(degrees + (minutes / 60.0F) + (seconds / 3600.0F)); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static Angle ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + /// + public override string ToString() + { + return this.ToString(null, NumberFormatInfo.CurrentInfo); + } + + /// + /// Returns a string representation of the Angle using the provided format + /// + /// a string indicating the desired format of the double. + /// The string representation of this instance. + public string ToString(string format) + { + return this.ToString(format, NumberFormatInfo.CurrentInfo); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + public string ToString(IFormatProvider provider) + { + return this.ToString(null, NumberFormatInfo.GetInstance(provider)); + } + + /// + public string ToString(string format, IFormatProvider provider) + { + return $"{this.Radians.ToString(format, provider)}\u00A0rad"; + } + + /// + public int CompareTo(Angle value) + { + return this.Radians.CompareTo(value.Radians); + } + + /// + /// Returns a value indicating whether this instance is equal to a specified object within the given tolerance. + /// + /// + /// true if represents the same angle as this instance; otherwise, false. + /// + /// An object to compare with this instance. + /// The maximum difference for being considered equal + public bool Equals(Angle other, double tolerance) + { + return Math.Abs(this.Radians - other.Radians) < tolerance; + } + + /// + /// Returns a value indicating whether this instance is equal to a specified object within the given tolerance. + /// + /// + /// true if represents the same angle as this instance; otherwise, false. + /// + /// An object to compare with this instance. + /// The maximum difference for being considered equal + public bool Equals(Angle other, Angle tolerance) + { + return Math.Abs(this.Radians - other.Radians) < tolerance.Radians; + } + + /// + public bool Equals(Angle other) => this.Radians.Equals(other.Radians); + + /// + public override bool Equals(object obj) => obj is Angle a && this.Equals(a); + + /// + public override int GetHashCode() => HashCode.Combine(this.Radians); + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("Value", this.Radians); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Circle2D.cs b/src/Numerics/Spatial/Euclidean2D/Circle2D.cs new file mode 100644 index 00000000..7a9af158 --- /dev/null +++ b/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 +{ + /// + /// Describes a standard 2 dimensional circle + /// + public struct Circle2D : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// Creates a Circle of a given radius from a center point + /// + /// The location of the center + /// The radius of the circle + public Circle2D(Point2D center, double radius) + { + this.Center = center; + this.Radius = radius; + } + + /// + /// Gets the center point of the circle + /// + public Point2D Center { get; } + + /// + /// Gets the radius of the circle + /// + public double Radius { get; } + + /// + /// Gets the circumference of the circle + /// + [Pure] + public double Circumference => 2 * this.Radius * Math.PI; + + /// + /// Gets the diameter of the circle + /// + [Pure] + public double Diameter => 2 * this.Radius; + + /// + /// Gets the area of the circle + /// + [Pure] + public double Area => this.Radius * this.Radius * Math.PI; + + /// + /// Returns a value that indicates whether each pair of elements in two specified circles is equal. + /// + /// The first circle to compare. + /// The second circle to compare. + /// True if the circles are the same; otherwise false. + public static bool operator ==(Circle2D left, Circle2D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified circles is not equal. + /// + /// The first circle to compare. + /// The second circle to compare + /// True if the circles are different; otherwise false. + public static bool operator !=(Circle2D left, Circle2D right) + { + return !left.Equals(right); + } + + /// + /// Creates a circle from three points which lie along its circumference. + /// Points may not be collinear + /// + /// The first point on the circle. + /// The second point on the circle. + /// The third point on the circle. + /// A Circle which is defined by the three specified points + /// An exception is thrown if no possible circle can be formed from the points + 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)); + } + + /// + /// Returns a value to indicate if a pair of circles are equal + /// + /// The circle to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the points are equal; otherwise false + [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); + } + + /// + [Pure] + public bool Equals(Circle2D c) => this.Radius.Equals(c.Radius) && this.Center.Equals(c.Center); + + /// + [Pure] + public override bool Equals(object obj) => obj is Circle2D c && this.Equals(c); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.Center, this.Radius); + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Line2D.cs b/src/Numerics/Spatial/Euclidean2D/Line2D.cs new file mode 100644 index 00000000..17ad6394 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/Line2D.cs @@ -0,0 +1,270 @@ +using System; +using System.Diagnostics.Contracts; + +namespace MathNet.Numerics.Spatial.Euclidean2D +{ + /// + /// 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. + /// + public struct Line2D : IEquatable + { + /// + /// The starting point of the line segment + /// + public readonly Point2D StartPoint; + + /// + /// The end point of the line segment + /// + public readonly Point2D EndPoint; + + /// + /// Initializes a new instance of the struct. + /// Throws an ArgumentException if the is equal to the . + /// + /// the starting point of the line segment. + /// the ending point of the line segment + 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; + } + + /// + /// Gets the distance from to + /// + [Pure] + public double Length => this.StartPoint.DistanceTo(this.EndPoint); + + /// + /// Gets a normalized vector in the direction from to + /// + [Pure] + public Vector2D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize(); + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(Line2D left, Line2D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(Line2D left, Line2D right) + { + return !left.Equals(right); + } + + /// + /// Adds a vector to the start point and end point of the line + /// + /// The vector to add + /// The line + /// A new at the adjusted points + public static Line2D operator +(Vector2D offset, Line2D line) + { + return new Line2D(line.StartPoint + offset, line.EndPoint + offset); + } + + /// + /// Adds a vector to the start point and end point of the line + /// + /// The line + /// The vector to add + /// A new line at the adjusted points + public static Line2D operator +(Line2D line, Vector2D offset) + { + return offset + line; + } + + /// + /// Subtracts a vector from the start point and end point of the line + /// + /// The line + /// The vector to subtract + /// A new line at the adjusted points + public static Line2D operator -(Line2D line, Vector2D offset) + { + return line + (-offset); + } + + /// + /// Returns a new from a pair of strings which represent points. + /// See for details on acceptable formats. + /// + /// The string representation of the first point. + /// The string representation of the second point. + /// A line segment from the first point to the second point. + public static Line2D Parse(string startPointString, string endPointString) + { + return new Line2D(Point2D.Parse(startPointString), Point2D.Parse(endPointString)); + } + + /// + /// Returns the shortest line between this line and a point. + /// + /// the point to create a line to + /// If false the start point can extend beyond the start and endpoint of the line + /// The shortest line between the line and the point + [Pure] + public Line2D LineTo(Point2D p, bool mustStartBetweenAndEnd) + { + return new Line2D(this.ClosestPointTo(p, mustStartBetweenAndEnd), p); + } + + /// + /// Returns the closest point on the line to the given point. + /// + /// The point that the returned point is the closest point on the line to + /// If true the returned point is contained by the segment ends, otherwise it can be anywhere on the projected line + /// The closest point on the line to the provided point + [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; + } + + /// + /// Compute the intersection between two lines with parallelism considered by the double floating point precision + /// on the cross product of the two directions. + /// + /// The other line to compute the intersection with + /// The point at the intersection of two lines, or null if the lines are parallel. + [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); + } + + /// + /// Compute the intersection between two lines if the angle between them is greater than a specified + /// angle tolerance. + /// + /// The other line to compute the intersection with + /// The tolerance used when checking if the lines are parallel + /// The point at the intersection of two lines, or null if the lines are parallel. + [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); + } + + /// + /// 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. + /// + /// The other line to check this one against + /// True if the lines are parallel, false if they are not + [Pure] + public bool IsParallelTo(Line2D other) + { + return this.Direction.IsParallelTo(other.Direction, Precision.DoublePrecision * 2); + } + + /// + /// Checks to determine whether or not two lines are parallel to each other within a specified angle tolerance + /// + /// The other line to check this one against + /// If the angle between line directions is less than this value, the method returns true + /// True if the lines are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(Line2D other, Angle tolerance) + { + return this.Direction.IsParallelTo(other.Direction, tolerance); + } + + /// + [Pure] + public override string ToString() + { + return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}"; + } + + /// + [Pure] + public bool Equals(Line2D other) + { + return this.StartPoint.Equals(other.StartPoint) && this.EndPoint.Equals(other.EndPoint); + } + + /// + [Pure] + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is Line2D d && this.Equals(d); + } + + /// + [Pure] + public override int GetHashCode() + { + unchecked + { + return (this.StartPoint.GetHashCode() * 397) ^ this.EndPoint.GetHashCode(); + } + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/LineSegment2D.cs b/src/Numerics/Spatial/Euclidean2D/LineSegment2D.cs new file mode 100644 index 00000000..56897793 --- /dev/null +++ b/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 +{ + /// + /// 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. + /// + public struct LineSegment2D : IEquatable + { + /// + /// The starting point of the line segment + /// + public readonly Point2D StartPoint; + + /// + /// The end point of the line segment + /// + public readonly Point2D EndPoint; + + /// + /// Initializes a new instance of the struct. + /// Throws an ArgumentException if the is equal to the . + /// + /// the starting point of the line segment. + /// the ending point of the line segment + 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; + } + + /// + /// Gets the distance from to + /// + [Pure] + public double Length => this.StartPoint.DistanceTo(this.EndPoint); + + /// + /// Gets a normalized vector in the direction from to + /// + [Pure] + public Vector2D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize(); + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(LineSegment2D left, LineSegment2D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(LineSegment2D left, LineSegment2D right) + { + return !left.Equals(right); + } + + /// + /// Returns a new from a pair of strings which represent points. + /// See for details on acceptable formats. + /// + /// The string representation of the first point. + /// The string representation of the second point. + /// A line segment from the first point to the second point. + public static LineSegment2D Parse(string startPointString, string endPointString) + { + return new LineSegment2D(Point2D.Parse(startPointString), Point2D.Parse(endPointString)); + } + + /// + /// Translates a line according to a provided vector + /// + /// A vector to apply + /// A new translated line segment + 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)); + } + + /// + /// Returns a new line segment between the closest point on this line segment and a point. + /// + /// the point to create a line to + /// A line segment between the point and the nearest point on this segment. + [Pure] + public LineSegment2D LineTo(Point2D p) + { + return new LineSegment2D(this.ClosestPointTo(p), p); + } + + /// + /// Returns the closest point on the line to the given point. + /// + /// The point that the returned point is the closest point on the line to + /// The closest point on the line to the provided point + [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; + } + + /// + /// Compute the intersection between two lines if the angle between them is greater than a specified + /// angle tolerance. + /// + /// The other line to compute the intersection with + /// When this method returns, contains the intersection point, if the conversion succeeded, or the default point if the conversion failed. + /// The tolerance used when checking if the lines are parallel + /// True if an intersection exists; otherwise false + [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; + } + + /// + /// Checks to determine whether or not two line segments are parallel to each other within a specified angle tolerance + /// + /// The other line to check this one against + /// If the angle between line directions is less than this value, the method returns true + /// True if the lines are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(LineSegment2D other, Angle tolerance) + { + return this.Direction.IsParallelTo(other.Direction, tolerance); + } + + /// + [Pure] + public override string ToString() + { + return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}"; + } + + /// + /// Returns a value to indicate if a pair of line segments are equal + /// + /// The line segment to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the line segments are equal; otherwise false + [Pure] + public bool Equals(LineSegment2D other, double tolerance) + { + return this.StartPoint.Equals(other.StartPoint, tolerance) && this.EndPoint.Equals(other.EndPoint, tolerance); + } + + /// + [Pure] + public bool Equals(LineSegment2D l) => this.StartPoint.Equals(l.StartPoint) && this.EndPoint.Equals(l.EndPoint); + + /// + [Pure] + public override bool Equals(object obj) => obj is LineSegment2D l && this.Equals(l); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.StartPoint, this.EndPoint); + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Matrix2D.cs b/src/Numerics/Spatial/Euclidean2D/Matrix2D.cs new file mode 100644 index 00000000..7e7ba705 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/Matrix2D.cs @@ -0,0 +1,36 @@ +using System; +using MathNet.Numerics.LinearAlgebra.Double; + +namespace MathNet.Numerics.Spatial.Euclidean2D +{ + /// + /// Helper class for creating matrices for manipulating 2D-elements + /// + public static class Matrix2D + { + /// + /// Creates a rotation about the z-axis + /// + /// The angle of rotation + /// A transform matrix + public static DenseMatrix Rotation(Angle rotation) + { + double c = Math.Cos(rotation.Radians); + double s = Math.Sin(rotation.Radians); + return Create(c, -s, s, c); + } + + /// + /// Creates an arbitrary 2D transform + /// + /// Element at m[1,1] + /// Element at m[1,2] + /// Element at m[2,1] + /// Element at m[2,2] + /// A transform matrix + public static DenseMatrix Create(double m11, double m12, double m21, double m22) + { + return new DenseMatrix(2, 2, new[] { m11, m21, m12, m22 }); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Point2D.cs b/src/Numerics/Spatial/Euclidean2D/Point2D.cs new file mode 100644 index 00000000..cc83508a --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/Point2D.cs @@ -0,0 +1,410 @@ +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 +{ + /// + /// Represents a point in 2 dimensional space + /// + [Serializable] + public struct Point2D : IXmlSerializable, IEquatable, IFormattable + { + /// + /// The x coordinate + /// + public readonly double X; + + /// + /// The y coordinate + /// + public readonly double Y; + + /// + /// Initializes a new instance of the struct. + /// Creates a point for given coordinates (x, y) + /// + /// The x coordinate + /// The y coordinate + public Point2D(double x, double y) + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point r from origin rotated a counterclockwise from X-Axis + /// + /// distance from origin + /// the angle + [Obsolete("This constructor will be removed, use FromPolar. Made obsolete 2017-12-03.")] + public Point2D(double r, Angle a) + : this(r * Math.Cos(a.Radians), r * Math.Sin(a.Radians)) + { + if (r < 0) + { + throw new ArgumentOutOfRangeException(nameof(r), r, "Expected a radius greater than or equal to zero."); + } + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point from a list of coordinates (x, y) + /// + /// a pair of coordinates in the order x, y + /// Exception thrown if more than 2 coordinates are passed + [Obsolete("This constructor will be removed. Made obsolete 2017-12-03.")] + public Point2D(IEnumerable data) + : this(data.ToArray()) + { + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point from a list of coordinates (x, y) + /// + /// a pair of coordinates in the order x, y + /// Exception thrown if more than 2 coordinates are passed + [Obsolete("This constructor will be removed. Made obsolete 2017-12-03.")] + public Point2D(double[] data) + : this(data[0], data[1]) + { + if (data.Length != 2) + { + throw new ArgumentException("data.Length != 2!"); + } + } + + /// + /// Gets a point at the origin (0,0) + /// + public static Point2D Origin => new Point2D(0, 0); + + /// + /// Adds a point and a vector together + /// + /// A point + /// A vector + /// A new point at the summed location + public static Point2D operator +(Point2D point, Vector2D vector) + { + return new Point2D(point.X + vector.X, point.Y + vector.Y); + } + + /// + /// Subtracts a vector from a point + /// + /// A point + /// A vector + /// A new point at the difference + public static Point2D operator -(Point2D left, Vector2D right) + { + return new Point2D(left.X - right.X, left.Y - right.Y); + } + + /// + /// Subtracts the first point from the second point + /// + /// The first point + /// The second point + /// A vector pointing to the difference + public static Vector2D operator -(Point2D left, Point2D right) + { + return new Vector2D(left.X - right.X, left.Y - right.Y); + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified points is equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are the same; otherwise false. + public static bool operator ==(Point2D left, Point2D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified points is not equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are different; otherwise false. + public static bool operator !=(Point2D left, Point2D right) + { + return !left.Equals(right); + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point r from origin rotated a counterclockwise from X-Axis + /// + /// distance from origin + /// the angle + /// The + 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)); + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// A point at the coordinates specified + /// True if could be parsed. + public static bool TryParse(string text, out Point2D result) + { + return TryParse(text, null, out result); + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + 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}"); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static Point2D ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + /// + /// Returns the centeroid or center of mass of any set of points + /// + /// a list of points + /// the centeroid point + public static Point2D Centroid(IEnumerable points) + { + return Centroid(points.ToArray()); + } + + /// + /// Returns the centeroid or center of mass of any set of points + /// + /// a list of points + /// the centeroid point + public static Point2D Centroid(params Point2D[] points) + { + return new Point2D( + points.Average(point => point.X), + points.Average(point => point.Y)); + } + + /// + /// Returns a point midway between the provided points and + /// + /// point A + /// point B + /// a new point midway between the provided points + public static Point2D MidPoint(Point2D point1, Point2D point2) + { + return Centroid(point1, point2); + } + + /// + /// Create a new Point2D from a Math.NET Numerics vector of length 2. + /// + /// A vector with length 2 to populate the created instance with. + /// A + public static Point2D OfVector(Vector 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)); + } + + /// + /// Applies a transform matrix to the point + /// + /// A transform matrix + /// A new point + public Point2D TransformBy(Matrix m) + { + return OfVector(m.Multiply(this.ToVector())); + } + + /// + /// Gets a vector from this point to another point + /// + /// The point to which the vector should go + /// A vector pointing to the other point. + [Pure] + public Vector2D VectorTo(Point2D otherPoint) + { + return otherPoint - this; + } + + /// + /// Finds the straight line distance to another point + /// + /// The other point + /// a distance measure + [Pure] + public double DistanceTo(Point2D otherPoint) + { + var vector = this.VectorTo(otherPoint); + return vector.Length; + } + + /// + /// Converts this point into a vector from the origin + /// + /// A vector equivalent to this point + [Pure] + public Vector2D ToVector2D() + { + return new Vector2D(this.X, this.Y); + } + + /// + /// Convert to a Math.NET Numerics dense vector of length 2. + /// + /// A with the x and y values from this instance. + [Pure] + public Vector ToVector() + { + return Vector.Build.Dense(new[] { this.X, this.Y }); + } + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + [Pure] + public string ToString(IFormatProvider provider) + { + return this.ToString(null, provider); + } + + /// + [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)})"; + } + + /// + /// Returns a value to indicate if a pair of points are equal + /// + /// The point to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the points are equal; otherwise false + [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; + } + + /// + [Pure] + public bool Equals(Point2D other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + + /// + [Pure] + public override bool Equals(object obj) => obj is Point2D p && this.Equals(p); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + + /// + XmlSchema IXmlSerializable.GetSchema() => null; + + /// + 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"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("X", this.X); + writer.WriteAttribute("Y", this.Y); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs b/src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs new file mode 100644 index 00000000..1191dda3 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/PolyLine2D.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using MathNet.Numerics.Spatial.Internal; + +namespace MathNet.Numerics.Spatial.Euclidean2D +{ + /// + /// 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. + /// + public class PolyLine2D : IEnumerable, IEquatable + { + /// + /// Internal storage for the points + /// + private readonly List points; + + /// + /// Initializes a new instance of the class. + /// Creates a PolyLine2D from a pre-existing IEnumerable of Point2Ds + /// + /// A list of points. + public PolyLine2D(IEnumerable points) + { + this.points = new List(points); + } + + /// + /// Gets the number of points in the polyline + /// + [Obsolete("Use VertexCount instead, obsolete since 2018-01-12")] + public int Count => this.points.Count; + + /// + /// Gets the number of vertices in the polyline. + /// + public int VertexCount => this.points.Count; + + /// + /// Gets the length of the polyline as the sum of the length of the individual segments + /// + public double Length => this.GetPolyLineLength(); + + /// + /// Gets a list of vertices + /// + public IEnumerable Vertices + { + get + { + foreach (var point in this.points) + { + yield return point; + } + } + } + + /// + /// Returns a point in the polyline by index number + /// + /// The index of a point + /// The indexed point + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + public Point2D this[int key] => this.points[key]; + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(PolyLine2D left, PolyLine2D right) + { + return left?.Equals(right) == true; + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(PolyLine2D left, PolyLine2D right) + { + return left?.Equals(right) != true; + } + + /// + /// 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. + /// + /// A list of points. + /// The tolerance (epsilon) for comparing sameness of line segments + /// A new PolyLine2D with same segments merged. + public static PolyLine2D ReduceComplexity(IEnumerable 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); + } + + /// + /// 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. + /// + /// The fractional length at which to compute the point + /// A point a fraction of the way along the line. + 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); + } + + /// + /// 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. + /// + /// The distance from the first point along the curve at which to return a point + /// A point which is the specified distance along the line + 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++; + } + } + } + + /// + /// Returns the closest point on the polyline to the given point. + /// + /// a point + /// A point which is the closest to the given point but still on the line. + 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; + } + + /// + /// Returns a value to indicate if a pair of polylines are equal + /// + /// The polyline to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the polylines are equal; otherwise false + [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; + } + + /// + [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; + } + + /// + [Pure] + public override bool Equals(object obj) + { + return obj is PolyLine2D polyLine2D && + this.Equals(polyLine2D); + } + + /// + [Pure] + public override int GetHashCode() + { + return HashCode.CombineMany(this.points); + } + + /// + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + public IEnumerator GetEnumerator() + { + return this.points.GetEnumerator(); + } + + /// + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// 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. + /// + /// A list of points + /// Tolerance (Epsilon) to apply to determine if segments are to be merged. + /// A new list of points minus any segment which was merged. + private static IEnumerable ReduceComplexitySingleStep(IEnumerable 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(); + 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; + } + + /// + /// Computes the length of the polyline by summing the lengths of the individual segments + /// + /// The length of the line + 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; + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Polygon2D.cs b/src/Numerics/Spatial/Euclidean2D/Polygon2D.cs new file mode 100644 index 00000000..daefc935 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/Polygon2D.cs @@ -0,0 +1,419 @@ +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 +{ + /// + /// Class to represent a closed polygon. + /// + public class Polygon2D : IEnumerable, IEquatable + { + /// + /// A list of vertices. + /// + private readonly ImmutableList points; + + /// + /// A list of edges. This list is lazy loaded on demand. + /// + private ImmutableList edges; + + /// + /// Initializes a new instance of the class. + /// At least three points are needed to construct a polygon. If less are passed an ArgumentException is thrown. + /// + /// A list of vertices. + public Polygon2D(IEnumerable vertices) + : this(vertices.ToArray()) + { + } + + /// + /// Initializes a new instance of the class. + /// At least three points are needed to construct a polygon. If less are passed an ArgumentException is thrown. + /// + /// A list of vertices. + 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); + } + } + + /// + /// Gets the number of vertices in the polygon. + /// + [Obsolete("Use VertexCount instead, obsolete since 6/12/2017")] + public int Count => this.points.Count; + + /// + /// Gets a list of vertices + /// + public IEnumerable Vertices + { + get + { + foreach (var point in this.points) + { + yield return point; + } + } + } + + /// + /// Gets a list of Edges + /// + public IEnumerable Edges + { + get + { + if (this.edges == null) + { + this.PopulateEdgeList(); + } + + foreach (var edge in this.edges) + { + yield return edge; + } + } + } + + /// + /// Gets the number of vertices in the polygon. + /// + public int VertexCount => this.points.Count; + + /// + /// A index into the list of vertices + /// + /// An index for the vertex number + /// A Vertex + [Obsolete("Use Vertices instead, obsolete since 6/12/2017")] + public Point2D this[int key] => this.points[key]; + + /// + /// Adds a vector to the each point on the polygon + /// + /// The vector to add + /// The polygon + /// A new at the adjusted points + [Obsolete("Use Translate instance method instead, obsolete since 6/12/2017")] + public static Polygon2D operator +(Vector2D shift, Polygon2D poly) + { + var newPoints = from p in poly select p + shift; + return new Polygon2D(newPoints); + } + + /// + /// Adds a vector to the each point on the polygon + /// + /// The polygon + /// The vector to add + /// A new at the adjusted points + [Obsolete("Use Translate instance method instead, obsolete since 6/12/2017")] + public static Polygon2D operator +(Polygon2D poly, Vector2D shift) + { + return shift + poly; + } + + /// + /// Returns a value that indicates whether each point in two specified polygons is equal. + /// + /// The first polygon to compare + /// The second polygon to compare + /// True if the polygons are the same; otherwise false. + public static bool operator ==(Polygon2D left, Polygon2D right) + { + return left?.Equals(right) == true; + } + + /// + /// Returns a value that indicates whether any point in two specified polygons is not equal. + /// + /// The first polygon to compare + /// The second polygon to compare + /// True if the polygons are different; otherwise false. + public static bool operator !=(Polygon2D left, Polygon2D right) + { + return left?.Equals(right) != true; + } + + /// + /// 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. + /// + /// The first polygon. + /// The second polygon + /// True if the vertices collide; otherwise false. + public static bool ArePolygonVerticesColliding(Polygon2D a, Polygon2D b) + { + return a.points.Any(b.EnclosesPoint) || b.points.Any(a.EnclosesPoint); + } + + /// + /// Determine whether or not a point is inside a polygon using the intersection counting + /// method. Return true if the point is contained, false if it is not. Points which lie + /// on the edge are not counted as inside the polygon. + /// + /// A point + /// A polygon + /// True if the point is inside the polygon; otherwise false. + [Obsolete("Use instance method EnclosesPoint instead, obsolete since 6/12/2017")] + public static bool IsPointInPolygon(Point2D p, Polygon2D poly) + { + // Algorithm from http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + // translated into C# + var c = false; + for (int i = 0, j = poly.Count - 1; i < poly.Count; j = i++) + { + if (((poly[i].Y > p.Y) != (poly[j].Y > p.Y)) && + (p.X < ((poly[j].X - poly[i].X) * (p.Y - poly[i].Y) / (poly[j].Y - poly[i].Y)) + poly[i].X)) + { + c = !c; + } + } + + return c; + } + + /// + /// 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. + /// + /// A list of points + /// + /// In which direction to return the points on the convex hull. + /// If true, clockwise. Otherwise counter clockwise + /// + /// A polygon. + public static Polygon2D GetConvexHullFromPoints(IEnumerable 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(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); + } + + /// + /// Test whether a point is enclosed within a polygon. Points on the polygon edges are not + /// counted as contained within the polygon. + /// + /// A point. + /// True if the point is inside the polygon; otherwise false. + 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; + } + + /// + /// Creates a new polygon from the existing polygon by removing any edges whose adjacent segments are considered colinear within the provided tolerance + /// + /// The tolerance by which adjacent edges should be considered collinear. + /// A polygon + public Polygon2D ReduceComplexity(double singleStepTolerance) + { + return new Polygon2D(PolyLine2D.ReduceComplexity(this.ToPolyLine2D(), singleStepTolerance)); + } + + /// + /// Returns a polygon rotated around the origin + /// + /// The angle by which to rotate. + /// A new polygon that has been rotated. + public Polygon2D Rotate(Angle angle) + { + var rotated = this.points.Select(t => Point2D.Origin + t.ToVector2D().Rotate(angle)).ToArray(); + return new Polygon2D(rotated); + } + + /// + /// Returns a new polygon which is translated (moved) by a vector + /// + /// A vector. + /// A new polygon that has been translated. + public Polygon2D TranslateBy(Vector2D vector) + { + var newPoints = from p in this.points select p + vector; + return new Polygon2D(newPoints); + } + + /// + /// Rotate the polygon around the specified point + /// + /// The angle by which to rotate + /// A point at which to rotate around + /// A new polygon that has been rotated. + 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); + } + + /// + /// Converts the polygon into a PolyLine2D + /// + /// A polyline + public PolyLine2D ToPolyLine2D() + { + var points = this.points.ToList(); + points.Add(points.First()); + return new PolyLine2D(points); + } + + /// + /// Returns an enumerator for the vertices + /// + /// An enumerator for the vertices + [Obsolete("Use Vertices instead, obsolete since 6/12/2017")] + public IEnumerator GetEnumerator() + { + return this.points.GetEnumerator(); + } + + /// + /// Returns an enumerator for the vertices + /// + /// An enumerator for the vertices + [Obsolete("Use Vertices instead, obsolete since 6/12/2017")] + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Returns a value to indicate if a pair of polygons are equal + /// + /// The polygon to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the polygons are equal; otherwise false + [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; + } + + /// + [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; + } + + /// + [Pure] + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + return obj is Polygon2D d && this.Equals(d); + } + + /// + [Pure] + public override int GetHashCode() + { + return HashCode.CombineMany(this.points); + } + + /// + /// Populates the edge list + /// + private void PopulateEdgeList() + { + var localedges = new List(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); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean2D/Vector2D.cs b/src/Numerics/Spatial/Euclidean2D/Vector2D.cs new file mode 100644 index 00000000..394c56f6 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean2D/Vector2D.cs @@ -0,0 +1,612 @@ +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 +{ + /// + /// A struct representing a vector in 2D space + /// + [Serializable] + public struct Vector2D : IXmlSerializable, IEquatable, IFormattable + { + /// + /// The x component. + /// + public readonly double X; + + /// + /// The y component. + /// + public readonly double Y; + + /// + /// Initializes a new instance of the struct. + /// + /// The x component. + /// The y component. + public Vector2D(double x, double y) + { + this.X = x; + this.Y = y; + } + + /// + /// Initializes a new instance of the struct. + /// Creates a vector with length r rotated a counterclockwise from X-Axis + /// + /// The radius + /// The angle + [Obsolete("This constructor will be removed, use FromPolar. Made obsolete 2017-12-03.")] + //// ReSharper disable once UnusedMember.Global + public Vector2D(double r, Angle a) + : this(r * Math.Cos(a.Radians), r * Math.Sin(a.Radians)) + { + if (r < 0) + { + throw new ArgumentOutOfRangeException(nameof(r), r, "Expected a radius greater than or equal to zero."); + } + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 2 doubles + [Obsolete("This constructor will be removed. Made obsolete 2017-12-03.")] + //// ReSharper disable once UnusedMember.Global + public Vector2D(IEnumerable data) + : this(data.ToArray()) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 2 doubles + [Obsolete("This constructor will be removed. Made obsolete 2017-12-03.")] + public Vector2D(double[] data) + : this(data[0], data[1]) + { + if (data.Length != 2) + { + throw new ArgumentException("data.Length != 2!"); + } + } + + /// + /// Gets a vector representing the X Axis + /// + public static Vector2D XAxis { get; } = new Vector2D(1, 0); + + /// + /// Gets a vector representing the Y Axis + /// + public static Vector2D YAxis { get; } = new Vector2D(0, 1); + + /// + /// Gets the length of the vector + /// + [Pure] + public double Length => Math.Sqrt((this.X * this.X) + (this.Y * this.Y)); + + /// + /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are the same; otherwise false. + public static bool operator ==(Vector2D left, Vector2D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified vectors is not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are different; otherwise false. + public static bool operator !=(Vector2D left, Vector2D right) + { + return !left.Equals(right); + } + + /// + /// Adds two vectors + /// + /// The first vector + /// The second vector + /// A new summed vector + public static Vector2D operator +(Vector2D left, Vector2D right) + { + return left.Add(right); + } + + /// + /// Subtracts two vectors + /// + /// The first vector + /// The second vector + /// A new difference vector + public static Vector2D operator -(Vector2D left, Vector2D right) + { + return left.Subtract(right); + } + + /// + /// Negates the vector + /// + /// A vector to negate + /// A new negated vector + public static Vector2D operator -(Vector2D v) + { + return v.Negate(); + } + + /// + /// Multiplies a vector by a scalar + /// + /// A scalar + /// A vector + /// A scaled vector + public static Vector2D operator *(double d, Vector2D v) + { + return new Vector2D(d * v.X, d * v.Y); + } + + /// + /// Multiplies a vector by a scalar + /// + /// A vector + /// A scalar + /// A scaled vector + public static Vector2D operator *(Vector2D v, double d) + { + return d * v; + } + + /// + /// Divides a vector by a scalar + /// + /// A vector + /// A scalar + /// A scaled vector + public static Vector2D operator /(Vector2D v, double d) + { + return new Vector2D(v.X / d, v.Y / d); + } + + /// + /// Creates a Vector from Polar coordinates + /// + /// The distance of the point from the origin + /// The angle of the point as measured from the X Axis + /// A vector. + 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)); + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// A point at the coordinates specified + /// True if could be parsed. + public static bool TryParse(string text, out Vector2D result) + { + return TryParse(text, null, out result); + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string of the form x,y into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + 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}"); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static Vector2D ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + /// + /// Create a new from a Math.NET Numerics vector of length 2. + /// + /// A vector with length 2 to populate the created instance with. + /// A + [Pure] + public static Vector2D OfVector(Vector 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)); + } + + /// + /// Computes whether or not this vector is perpendicular to vector by: + /// 1. Normalizing both + /// 2. Computing the dot product. + /// 3. Comparing 1- Math.Abs(dot product) to + /// + /// The other + /// The tolerance for when vectors are said to be parallel + /// True if the vector dot product is within the given double tolerance of unity, false if not + [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; + } + + /// + /// Computes whether or not this vector is parallel to another vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when vectors are said to be parallel + /// True if the vectors are parallel within the angle tolerance, false if they are not + [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; + } + + /// + /// Computes whether or not this vector is perpendicular to vector by: + /// 1. Normalizing both + /// 2. Computing the dot product. + /// 3. Comparing Math.Abs(dot product) to + /// + /// The other + /// The tolerance for when vectors are said to be parallel + /// True if the vector dot product is within the given double tolerance of unity, false if not + [Pure] + public bool IsPerpendicularTo(Vector2D other, double tolerance = 1e-10) + { + return Math.Abs(this.Normalize().DotProduct(other.Normalize())) < tolerance; + } + + /// + /// Computes whether or not this vector is parallel to another vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when vectors are said to be parallel + /// True if the vectors are parallel within the angle tolerance, false if they are not + [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; + } + + /// + /// Compute the signed angle to another vector. + /// + /// The other + /// Positive in clockwise direction + /// When true and the result is > 180° a negative value is returned + /// The angle between the vectors. + [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); + } + + /// + /// Compute the angle between this vector and another using the arccosine of the dot product. + /// + /// The other + /// The angle between vectors, with a range between 0° and 180° + [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)))); + } + + /// + /// Rotates a Vector by an angle + /// + /// The angle. + /// A new rotated vector. + [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); + } + + /// + /// Perform the dot product on a pair of vectors + /// + /// The second vector + /// The result of the dot product. + [Pure] + public double DotProduct(Vector2D other) + { + return (this.X * other.X) + (this.Y * other.Y); + } + + /// + /// 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) + /// + /// The other + /// (this.X * other.Y) - (this.Y * other.X) + [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); + } + + /// + /// Projects this vector onto another vector + /// + /// The other + /// A representing this vector projected on + [Pure] + public Vector2D ProjectOn(Vector2D other) + { + return other * (this.DotProduct(other) / other.DotProduct(other)); + } + + /// + /// Creates a new unit vector from the existing vector. + /// + /// A new unit vector in the same direction as the original vector + [Pure] + public Vector2D Normalize() + { + var l = this.Length; + return new Vector2D(this.X / l, this.Y / l); + } + + /// + /// Scales the vector by the provided value + /// + /// a scaling factor + /// A new scale adjusted vector + [Pure] + public Vector2D ScaleBy(double d) + { + return new Vector2D(d * this.X, d * this.Y); + } + + /// + /// Returns the negative of the vector + /// + /// A new negated vector. + [Pure] + public Vector2D Negate() + { + return new Vector2D(-1 * this.X, -1 * this.Y); + } + + /// + /// Subtracts a vector from this vector. + /// + /// A vector to subtract + /// A new vector which is the difference of the current vector and the provided vector + [Pure] + public Vector2D Subtract(Vector2D v) + { + return new Vector2D(this.X - v.X, this.Y - v.Y); + } + + /// + /// Adds a vector to this vector + /// + /// A vector to add + /// A new vector which is the sum of the existing vector and the provided vector + [Pure] + public Vector2D Add(Vector2D v) + { + return new Vector2D(this.X + v.X, this.Y + v.Y); + } + + /// + /// Transforms a vector by multiplying it against a provided matrix + /// + /// The matrix to multiply + /// A new transformed vector + [Pure] + public Vector2D TransformBy(Matrix m) + { + var transformed = m.Multiply(this.ToVector()); + return new Vector2D(transformed.At(0), transformed.At(1)); + } + + /// + /// Convert to a Math.NET Numerics dense vector of length 2. + /// + /// A with the x and y values from this instance. + [Pure] + public Vector ToVector() + { + return Vector.Build.Dense(new[] { this.X, this.Y }); + } + + /// + /// Compare this instance with + /// + /// The other + /// The tolerance when comparing the x and y components + /// True if found to be equal. + [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; + } + + /// + [Pure] + public bool Equals(Vector2D other) => this.X.Equals(other.X) && this.Y.Equals(other.Y); + + /// + [Pure] + public override bool Equals(object obj) => obj is Vector2D v && this.Equals(v); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.X, this.Y); + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + [Pure] + public string ToString(IFormatProvider provider) + { + return this.ToString(null, provider); + } + + /// + [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)})"; + } + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("X", this.X); + writer.WriteAttribute("Y", this.Y); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Circle3D.cs b/src/Numerics/Spatial/Euclidean3D/Circle3D.cs new file mode 100644 index 00000000..f2eaa2e3 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/Circle3D.cs @@ -0,0 +1,193 @@ +using System; +using System.Diagnostics.Contracts; +using MathNet.Numerics.Spatial.Internal; + +namespace MathNet.Numerics.Spatial.Euclidean3D +{ + /// + /// Describes a 3 dimensional circle + /// + [Serializable] + public struct Circle3D : IEquatable + { + /// + /// The center of the circle + /// + public readonly Point3D CenterPoint; + + /// + /// the axis of the circle + /// + public readonly UnitVector3D Axis; + + /// + /// the radius of the circle + /// + public readonly double Radius; + + /// + /// Initializes a new instance of the struct. + /// Constructs a Circle3D with a given at a orientated to the + /// + /// The center of the circle + /// the axis of the circle + /// the radius of the circle + public Circle3D(Point3D centerPoint, UnitVector3D axis, double radius) + { + this.CenterPoint = centerPoint; + this.Axis = axis; + this.Radius = radius; + } + + /// + /// Initializes a new instance of the struct. + /// Create a circle from the midpoint between two points, in a direction along a specified axis + /// + /// First point on the circumference of the circle + /// Second point on the circumference of the circle + /// Direction of the plane in which the circle lies + [Obsolete("This constructor will be removed, use factory method FromPointsAndAxis. Made obsolete 2017-12-05.")] + public Circle3D(Point3D p1, Point3D p2, UnitVector3D axis) + { + this = FromPointsAndAxis(p1, p2, axis); + } + + /// + /// Initializes a new instance of the struct. + /// Create a circle from three points which lie along its circumference. + /// + /// The first point on the circle + /// The second point on the circle + /// The third point on the circle + [Obsolete("This constructor will be removed, use factory method FromPoints. Made obsolete 2017-12-05.")] + public Circle3D(Point3D p1, Point3D p2, Point3D p3) + { + this = FromPoints(p1, p2, p3); + } + + /// + /// Gets the diameter of the circle + /// + [Pure] + public double Diameter => 2 * this.Radius; + + /// + /// Gets the circumference of the circle + /// + [Pure] + public double Circumference => 2 * Math.PI * this.Radius; + + /// + /// Gets the area of the circle + /// + [Pure] + public double Area => this.Radius * this.Radius * Math.PI; + + /// + /// Returns a value that indicates whether each pair of elements in two specified circles is equal. + /// + /// The first circle to compare + /// The second circle to compare + /// True if the circles are the same; otherwise false. + public static bool operator ==(Circle3D left, Circle3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified circles is not equal. + /// + /// The first circle to compare + /// The second circle to compare + /// True if the circles are different; otherwise false. + public static bool operator !=(Circle3D left, Circle3D right) + { + return !left.Equals(right); + } + + /// + /// Initializes a new instance of the struct. + /// Create a circle from three points which lie along its circumference. + /// + /// The first point on the circle + /// The second point on the circle + /// The third point on the circle + /// A + 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)); + } + + /// + /// Initializes a new instance of the struct. + /// Create a circle from the midpoint between two points, in a direction along a specified axis + /// + /// First point on the circumference of the circle + /// Second point on the circumference of the circle + /// Direction of the plane in which the circle lies + /// A + public static Circle3D FromPointsAndAxis(Point3D p1, Point3D p2, UnitVector3D axis) + { + var cp = Point3D.MidPoint(p1, p2); + return new Circle3D(cp, axis, cp.DistanceTo(p1)); + } + + /// + /// Returns a value to indicate if a pair of circles are equal + /// + /// The circle to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the points are equal; otherwise false + [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); + } + + /// + [Pure] + public bool Equals(Circle3D c) + { + return this.CenterPoint.Equals(c.CenterPoint) + && this.Axis.Equals(c.Axis) + && this.Radius.Equals(c.Radius); + } + + /// + [Pure] + public override bool Equals(object obj) => obj is Circle3D c && this.Equals(c); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.CenterPoint, this.Axis, this.Radius); + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs b/src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs new file mode 100644 index 00000000..a18b1aaa --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/CoordinateSystem3D.cs @@ -0,0 +1,725 @@ +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 +{ + /// + /// A coordinate system + /// + [Serializable] + public class CoordinateSystem3D : LinearAlgebra.Double.DenseMatrix, IEquatable, IXmlSerializable + { + /// + /// A local regex pattern for 3D items + /// + private static readonly string Item3DPattern = Parser.Vector3DPattern.Trim('^', '$'); + + /// + /// A local regex pattern for a coordinate system + /// + private static readonly string CsPattern = string.Format(@"^ *o: *{{(?{0})}} *x: *{{(?{0})}} *y: *{{(?{0})}} *z: *{{(?{0})}} *$", Item3DPattern); + + /// + /// Initializes a new instance of the class. + /// + public CoordinateSystem3D() + : this(new Point3D(0, 0, 0), UnitVector3D.XAxis.ToVector3D(), UnitVector3D.YAxis.ToVector3D(), UnitVector3D.ZAxis.ToVector3D()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The x axis + /// The y axis + /// The z axis + /// The origin + public CoordinateSystem3D(Vector3D xAxis, Vector3D yAxis, Vector3D zAxis, Point3D origin) + : this(origin, xAxis, yAxis, zAxis) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The origin + /// The x axis + /// The y axis + /// The z axis + public CoordinateSystem3D(Point3D origin, UnitVector3D xAxis, UnitVector3D yAxis, UnitVector3D zAxis) + : this(origin, xAxis.ToVector3D(), yAxis.ToVector3D(), zAxis.ToVector3D()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The origin + /// The x axis + /// The y axis + /// The z axis + 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()) + ////{ + ////} + + /// + /// Initializes a new instance of the class. + /// + /// A matrix + public CoordinateSystem3D(Matrix 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"); + } + } + + /// + /// Gets the X Axis + /// + public Vector3D XAxis + { + get + { + var row = this.SubMatrix(0, 3, 0, 1).ToRowMajorArray(); + return new Vector3D(row[0], row[1], row[2]); + } + } + + /// + /// Gets the Y Axis + /// + public Vector3D YAxis + { + get + { + var row = this.SubMatrix(0, 3, 1, 1).ToRowMajorArray(); + return new Vector3D(row[0], row[1], row[2]); + } + } + + /// + /// Gets the z Axis + /// + public Vector3D ZAxis + { + get + { + var row = this.SubMatrix(0, 3, 2, 1).ToRowMajorArray(); + return new Vector3D(row[0], row[1], row[2]); + } + } + + /// + /// Gets the point of origin + /// + public Point3D Origin + { + get + { + var row = this.SubMatrix(0, 3, 3, 1).ToRowMajorArray(); + return new Point3D(row[0], row[1], row[2]); + } + } + + /// + /// Gets the offset to origin + /// + public Vector3D OffsetToBase + { + get { return this.Origin.ToVector3D(); } + } + + /// + /// Gets the base change matrix + /// + 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; + } + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified coordinate system is equal. + /// + /// The first coordinate system to compare + /// The second coordinate system to compare + /// True if the coordinate system are the same; otherwise false. + public static bool operator ==(CoordinateSystem3D left, CoordinateSystem3D right) + { + return CoordinateSystem3D.Equals(left, right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified coordinate system is not equal. + /// + /// The first coordinate system to compare + /// The second coordinate system to compare + /// True if the coordinate systems are different; otherwise false. + public static bool operator !=(CoordinateSystem3D left, CoordinateSystem3D right) + { + return !CoordinateSystem3D.Equals(left, right); + } + + /// + /// Creates a coordinate system from a string + /// + /// The string + /// A coordinate system + 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); + } + + /// + /// 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. + /// + /// Input Vector object to align from. + /// Input Vector object to align to. + /// Input Vector object. + /// A rotated coordinate system + 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; + } + + /// + /// Creates a coordinate system that rotates + /// + /// Angle to rotate + /// Vector to rotate about + /// A rotating coordinate system + 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); + } + + /// + /// Creates a coordinate system that rotates + /// + /// Angle to rotate + /// Vector to rotate about + /// A rotated coordinate system + public static CoordinateSystem3D Rotation(Angle angle, Vector3D v) + { + return Rotation(angle, v.Normalize()); + } + + /// + /// Rotation around Z (yaw) then around Y (pitch) and then around X (roll) + /// http://en.wikipedia.org/wiki/Aircraft_principal_axes + /// + /// Rotates around Z + /// Rotates around Y + /// Rotates around X + /// A rotated coordinate system + 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))); + } + + /// + /// Rotates around Z + /// + /// An angle + /// A rotated coordinate system + public static CoordinateSystem3D Yaw(Angle av) + { + return Rotation(av, UnitVector3D.ZAxis); + } + + /// + /// Rotates around Y + /// + /// An angle + /// A rotated coordinate system + public static CoordinateSystem3D Pitch(Angle av) + { + return Rotation(av, UnitVector3D.YAxis); + } + + /// + /// Rotates around X + /// + /// An angle + /// A rotated coordinate system + public static CoordinateSystem3D Roll(Angle av) + { + return Rotation(av, UnitVector3D.XAxis); + } + + /// + /// Creates a coordinate system that maps from the 'from' coordinate system to the 'to' coordinate system. + /// + /// The from coordinate system + /// The to coordinate system + /// A mapping coordinate system + public static CoordinateSystem3D CreateMappingCoordinateSystem(CoordinateSystem3D fromCs, CoordinateSystem3D toCs) + { + var m = toCs.Multiply(fromCs.Inverse()); + m[3, 3] = 1; + return new CoordinateSystem3D(m); + } + + /// + /// Sets this matrix to be the matrix that maps from the 'from' coordinate system to the 'to' coordinate system. + /// + /// Input Point3D that defines the origin to map the coordinate system from. + /// Input Vector3D object that defines the X-axis to map the coordinate system from. + /// Input Vector3D object that defines the Y-axis to map the coordinate system from. + /// Input Vector3D object that defines the Z-axis to map the coordinate system from. + /// Input Point3D object that defines the origin to map the coordinate system to. + /// Input Vector3D object that defines the X-axis to map the coordinate system to. + /// Input Vector3D object that defines the Y-axis to map the coordinate system to. + /// Input Vector3D object that defines the Z-axis to map the coordinate system to. + /// A mapping coordinate system + 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; + } + + /// + /// Creates a translation + /// + /// A translation vector + /// A translated coordinate system + public static CoordinateSystem3D Translation(Vector3D translation) + { + return new CoordinateSystem3D(translation.ToPoint3D(), UnitVector3D.XAxis, UnitVector3D.YAxis, UnitVector3D.ZAxis); + } + + /// + /// Creates a rotating coordinate system + /// + /// A 3×3 matrix with the rotation portion + /// A rotated coordinate system + /// A rotating coordinate system + public static CoordinateSystem3D SetRotationSubMatrix(Matrix 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; + } + + /// + /// Gets a rotation submatrix from a coordinate system + /// + /// a coordinate system + /// A rotation matrix + public static Matrix GetRotationSubMatrix(CoordinateSystem3D coordinateSystem) + { + return coordinateSystem.SubMatrix(0, 3, 0, 3); + } + + ////public CoordinateSystem SetCoordinateSystem(Matrix matrix) + ////{ + //// if (matrix.ColumnCount != 4 || matrix.RowCount != 4) + //// throw new ArgumentException("Not a 4x4 matrix!"); + //// return new CoordinateSystem(matrix); + ////} + + /// + /// Resets rotations preserves scales + /// + /// A coordinate system with reset rotation + 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); + } + + /// + /// Rotates a coordinate system around a vector + /// + /// The vector + /// An angle + /// A rotated coordinate system + public CoordinateSystem3D RotateCoordSysAroundVector(UnitVector3D about, Angle angle) + { + var rcs = Rotation(angle, about); + return rcs.Transform(this); + } + + /// + /// Rotate without Reset + /// + /// The yaw + /// The pitch + /// The roll + /// A rotated coordinate system + public CoordinateSystem3D RotateNoReset(Angle yaw, Angle pitch, Angle roll) + { + var rcs = Rotation(yaw, pitch, roll); + return rcs.Transform(this); + } + + /// + /// Translates a coordinate system + /// + /// a translation vector + /// A translated coordinate system + public CoordinateSystem3D OffsetBy(Vector3D v) + { + return new CoordinateSystem3D(this.Origin + v, this.XAxis, this.YAxis, this.ZAxis); + } + + /// + /// Translates a coordinate system + /// + /// a translation vector + /// A translated coordinate system + public CoordinateSystem3D OffsetBy(UnitVector3D v) + { + return new CoordinateSystem3D(this.Origin + v, this.XAxis, this.YAxis, this.ZAxis); + } + + /// + /// Transforms a ray according to this change matrix + /// + /// a ray + /// a transformed ray + 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)baseChangeMatrix); + return new Ray3D(point, direction); + } + + /// + /// Transforms a point according to this change matrix + /// + /// a point + /// a transformed point + public Point3D TransformToCoordSys(Point3D p) + { + var baseChangeMatrix = this.BaseChangeMatrix; + var point = baseChangeMatrix.Transform(p) + this.OffsetToBase; + return point; + } + + /// + /// Transforms a ray according to the inverse of this change matrix + /// + /// a ray + /// a transformed ray + 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); + } + + /// + /// Transforms a point according to the inverse of this change matrix + /// + /// a point + /// a transformed point + public Point3D TransformFromCoordSys(Point3D p) + { + var point = this.BaseChangeMatrix.Invert().Transform(p) + this.OffsetToBase; + return point; + } + + /// + /// Creates a rotation submatrix + /// + /// a matrix + /// a coordinate system + public CoordinateSystem3D SetRotationSubMatrix(Matrix r) + { + return SetRotationSubMatrix(r, this); + } + + /// + /// Returns a translation coordinate system + /// + /// a vector + /// a coordinate system + public CoordinateSystem3D SetTranslation(Vector3D v) + { + return new CoordinateSystem3D(v.ToPoint3D(), this.XAxis, this.YAxis, this.ZAxis); + } + + /// + /// Returns a rotation sub matrix + /// + /// a rotation sub matrix + public Matrix GetRotationSubMatrix() + { + return GetRotationSubMatrix(this); + } + + /// + /// Transforms a vector and returns the transformed vector + /// + /// A vector + /// A transformed vector + public Vector3D Transform(Vector3D v) + { + var v3 = Vector.Build.Dense(new[] { v.X, v.Y, v.Z }); + this.GetRotationSubMatrix().Multiply(v3, v3); + return new Vector3D(v3[0], v3[1], v3[2]); + } + + /// + /// Transforms a vector and returns the transformed vector + /// + /// a unit vector + /// A transformed vector + public Vector3D Transform(UnitVector3D v) + { + var v3 = Vector.Build.Dense(new[] { v.X, v.Y, v.Z }); + this.GetRotationSubMatrix().Multiply(v3, v3); + return new Vector3D(v3[0], v3[1], v3[2]); + } + + /// + /// Transforms a point and returns the transformed point + /// + /// a point + /// A transformed point + public Point3D Transform(Point3D p) + { + var v4 = Vector.Build.Dense(new[] { p.X, p.Y, p.Z, 1 }); + this.Multiply(v4, v4); + return new Point3D(v4[0], v4[1], v4[2]); + } + + /// + /// Transforms a coordinate system and returns the transformed + /// + /// a coordinate system + /// A transformed coordinate system + public CoordinateSystem3D Transform(CoordinateSystem3D cs) + { + return new CoordinateSystem3D(this.Multiply(cs)); + } + + /// + /// Transforms a line and returns the transformed. + /// + /// A line + /// A transformed line + [Obsolete("Use LineSegment3D, Obsolete from 2017-12-10")] + public Line3D Transform(Line3D l) + { + return new Line3D(this.Transform(l.StartPoint), this.Transform(l.EndPoint)); + } + + /// + /// Transforms a line segment. + /// + /// A line segment + /// The transformed line segment + public LineSegment3D Transform(LineSegment3D l) + { + return new LineSegment3D(this.Transform(l.StartPoint), this.Transform(l.EndPoint)); + } + + /// + /// Transforms a ray and returns the transformed. + /// + /// A ray + /// A transformed ray + public Ray3D Transform(Ray3D ray) + { + return new Ray3D(this.Transform(ray.ThroughPoint), this.Transform(ray.Direction)); + } + + /// + /// Transforms a coordinate system + /// + /// a matrix + /// A transformed coordinate system + public CoordinateSystem3D TransformBy(Matrix matrix) + { + return new CoordinateSystem3D(matrix.Multiply(this)); + } + + /// + /// Transforms this by the coordinate system and returns the transformed. + /// + /// a coordinate system + /// a transformed coordinate system + public CoordinateSystem3D TransformBy(CoordinateSystem3D cs) + { + return cs.Transform(this); + } + + /// + /// Inverts this coordinate system + /// + /// An inverted coordinate system + public CoordinateSystem3D Invert() + { + return new CoordinateSystem3D(this.Inverse()); + } + + /// + /// Returns a value to indicate if this CoordinateSystem is equivalent to a another CoordinateSystem + /// + /// The CoordinateSystem to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the CoordinateSystems are equal; otherwise false + [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; + } + + /// + [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; + } + + /// + [Pure] + public override bool Equals(object obj) + { + if (obj is null) + { + return false; + } + + return obj is CoordinateSystem3D cs && this.Equals(cs); + } + + /// + [Pure] + public override int GetHashCode() => HashCode.CombineMany(this.Values); + + /// + /// Returns a string representation of the coordinate system + /// + /// a string + public new string ToString() + { + return $"Origin: {this.Origin}, XAxis: {this.XAxis}, YAxis: {this.YAxis}, ZAxis: {this.ZAxis}"; + } + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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 }); + } + + /// + 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); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Line3D.cs b/src/Numerics/Spatial/Euclidean3D/Line3D.cs new file mode 100644 index 00000000..54b98e9b --- /dev/null +++ b/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 +{ + /// + /// A line between two points + /// + [Serializable] + public struct Line3D : IEquatable, IXmlSerializable + { + /// + /// The start point of the line + /// + public readonly Point3D StartPoint; + + /// + /// The end point of the line + /// + public readonly Point3D EndPoint; + + /// + /// Initializes a new instance of the struct. + /// Throws an ArgumentException if the is equal to the . + /// + /// The starting point of the line segment. + /// The ending point of the line segment. + public Line3D(Point3D startPoint, Point3D endPoint) + { + if (startPoint == endPoint) + { + throw new ArgumentException("StartPoint == EndPoint"); + } + + this.StartPoint = startPoint; + this.EndPoint = endPoint; + } + + /// + /// Gets distance from to , the length of the line + /// + [Pure] + public double Length => this.StartPoint.DistanceTo(this.EndPoint); + + /// + /// Gets the direction from the to + /// + [Pure] + public UnitVector3D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize(); + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(Line3D left, Line3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(Line3D left, Line3D right) + { + return !left.Equals(right); + } + + /// + /// Returns a new from a pair of strings which represent points. + /// See for details on acceptable formats. + /// + /// The string representation of the first point. + /// The string representation of the second point. + /// A line segment from the first point to the second point. + public static Line3D Parse(string startPoint, string endPoint) + { + return new Line3D(Point3D.Parse(startPoint), Point3D.Parse(endPoint)); + } + + /// + /// Returns the shortest line between this line and a point. + /// + /// the point to create a line to + /// If false the start point can extend beyond the start and endpoint of the line + /// The shortest line between the line and the point + [Pure] + public Line3D LineTo(Point3D p, bool mustStartBetweenStartAndEnd) + { + return new Line3D(this.ClosestPointTo(p, mustStartBetweenStartAndEnd), p); + } + + /// + /// Returns the closest point on the line to the given point. + /// + /// The point that the returned point is the closest point on the line to + /// If true the returned point is contained by the segment ends, otherwise it can be anywhere on the projected line + /// The closest point on the line to the provided point + [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; + } + + /// + /// The line projected on a plane + /// + /// The plane. + /// A projected line. + [Pure] + public Line3D ProjectOn(Plane3D plane) + { + return plane.Project(this); + } + + /// + /// Find the intersection between the line and a plane + /// + /// The plane. + /// A tolerance (epsilon) to compensate for floating point error + /// A point where the line and plane intersect; null if no such point exists + [Pure] + public Point3D? IntersectionWith(Plane3D plane, double tolerance = double.Epsilon) + { + return plane.IntersectionWith(this, tolerance); + } + + /// + /// 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. + /// + /// The other line to check this one against + /// True if the lines are parallel, false if they are not + [Pure] + public bool IsParallelTo(Line3D other) + { + return this.Direction.IsParallelTo(other.Direction, Precision.DoublePrecision * 2); + } + + /// + /// Checks to determine whether or not two lines are parallel to each other within a specified angle tolerance + /// + /// The other line to check this one against + /// If the angle between line directions is less than this value, the method returns true + /// True if the lines are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(Line3D other, Angle angleTolerance) + { + return this.Direction.IsParallelTo(other.Direction, angleTolerance); + } + + /// + /// 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. + /// + /// line to compute the closest points with + /// A tuple of two points representing the endpoints of the shortest distance between the two lines + [Pure] + public Tuple 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)); + } + + /// + /// 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. + /// + /// line to compute the closest points with + /// if true, the lines are treated as segments bounded by the start and end point + /// A tuple of two points representing the endpoints of the shortest distance between the two lines or segments + [Pure] + public Tuple 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; + } + + /// + [Pure] + public bool Equals(Line3D other) + { + return this.StartPoint.Equals(other.StartPoint) && this.EndPoint.Equals(other.EndPoint); + } + + /// + [Pure] + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + return obj is Line3D d && this.Equals(d); + } + + /// + [Pure] + public override int GetHashCode() + { + unchecked + { + var hashCode = this.StartPoint.GetHashCode(); + hashCode = (hashCode * 397) ^ this.EndPoint.GetHashCode(); + return hashCode; + } + } + + /// + [Pure] + public override string ToString() + { + return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}"; + } + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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())); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteElement("StartPoint", this.StartPoint); + writer.WriteElement("EndPoint", this.EndPoint); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/LineSegment3D.cs b/src/Numerics/Spatial/Euclidean3D/LineSegment3D.cs new file mode 100644 index 00000000..4fb7f8a2 --- /dev/null +++ b/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 +{ + /// + /// 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. + /// + public struct LineSegment3D : IEquatable + { + /// + /// The starting point of the line segment + /// + public readonly Point3D StartPoint; + + /// + /// The end point of the line segment + /// + public readonly Point3D EndPoint; + + /// + /// Initializes a new instance of the struct. + /// Throws an ArgumentException if the is equal to the . + /// + /// the starting point of the line segment. + /// the ending point of the line segment + 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; + } + + /// + /// Gets the distance from to + /// + [Pure] + public double Length => this.StartPoint.DistanceTo(this.EndPoint); + + /// + /// Gets a normalized vector in the direction from to + /// + [Pure] + public UnitVector3D Direction => this.StartPoint.VectorTo(this.EndPoint).Normalize(); + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(LineSegment3D left, LineSegment3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(LineSegment3D left, LineSegment3D right) + { + return !left.Equals(right); + } + + /// + /// Returns a new from a pair of strings which represent points. + /// See for details on acceptable formats. + /// + /// The string representation of the first point. + /// The string representation of the second point. + /// A line segment from the first point to the second point. + public static LineSegment3D Parse(string startPointString, string endPointString) + { + return new LineSegment3D(Point3D.Parse(startPointString), Point3D.Parse(endPointString)); + } + + /// + /// Translates a line according to a provided vector + /// + /// A vector to apply + /// A new translated line segment + public LineSegment3D TranslateBy(Vector3D vector) + { + return new LineSegment3D(this.StartPoint + vector, this.EndPoint + vector); + } + + /// + /// Returns the closest point on the line segment to the given point. + /// + /// The point that the returned point is the closest point on the line to + /// The closest point on the line to the provided point + [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; + } + + /// + /// Returns a new line segment between the closest point on this line segment and a point. + /// + /// the point to create a line to + /// A line segment between the nearest point on this segment and the provided point. + [Pure] + public LineSegment3D LineTo(Point3D p) + { + return new LineSegment3D(this.ClosestPointTo(p), p); + } + + /// + /// 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. + /// + /// line to compute the closest points with + /// A tolerance (epsilon) to adjust for floating point error + /// A line representing the endpoints of the shortest distance between the two segments + /// True if a line could be found, false if the lines intersect + [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; + } + + /// + /// Checks to determine whether or not two line segments are parallel to each other within a specified angle tolerance + /// + /// The other line to check this one against + /// If the angle between line directions is less than this value, the method returns true + /// True if the lines are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(LineSegment3D other, Angle tolerance) + { + return this.Direction.IsParallelTo(other.Direction, tolerance); + } + + /// + [Pure] + public override string ToString() + { + return $"StartPoint: {this.StartPoint}, EndPoint: {this.EndPoint}"; + } + + /// + /// Returns a value to indicate if a pair of line segments are equal + /// + /// The line segment to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the line segments are equal; otherwise false + [Pure] + public bool Equals(LineSegment3D other, double tolerance) + { + return this.StartPoint.Equals(other.StartPoint, tolerance) && this.EndPoint.Equals(other.EndPoint, tolerance); + } + + /// + [Pure] + public bool Equals(LineSegment3D l) => this.StartPoint.Equals(l.StartPoint) && this.EndPoint.Equals(l.EndPoint); + + /// + [Pure] + public override bool Equals(object obj) => obj is LineSegment3D l && this.Equals(l); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.StartPoint, this.EndPoint); + + /// + /// Extends the segment to a infinite line and finds the closest point on that line to the provided point. + /// + /// a point + /// A point on the infinite line which extends the segment + [Pure] + private Point3D ClosestLinePointTo(Point3D p) + { + var alongVector = this.StartPoint.VectorTo(p).DotProduct(this.Direction) * this.Direction; + return this.StartPoint + alongVector; + } + + /// + /// 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. + /// + /// line to compute the closest points with + /// A tolerance (epsilon) to adjust for floating point error + /// A tuple of two points representing the endpoints of the shortest distance between the two lines + [Pure] + private Tuple 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)); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Matrix3D.cs b/src/Numerics/Spatial/Euclidean3D/Matrix3D.cs new file mode 100644 index 00000000..537ca01b --- /dev/null +++ b/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 +{ + /// + /// Helper class for working with 3D matrixes + /// + public static class Matrix3D + { + /// + /// Creates a rotation matrix around the X axis + /// + /// The angle to rotate + /// A rotation matrix + 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; + } + + /// + /// Creates a rotation matrix around the Y axis + /// + /// The angle to rotate + /// A rotation matrix + 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; + } + + /// + /// Creates a rotation matrix around the Z axis + /// + /// The angle to rotate + /// A rotation matrix + public static Matrix 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; + } + + /// + /// 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. + /// + /// Input Vector object to align from. + /// Input Vector object to align to. + /// Input Vector object. + /// A transform matrix + public static Matrix RotationTo( + Vector3D fromVector, + Vector3D toVector, + UnitVector3D? axis = null) + { + return RotationTo(fromVector.Normalize(), toVector.Normalize(), axis); + } + + /// + /// 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. + /// + /// Input Vector object to align from. + /// Input Vector object to align to. + /// Input Vector object. + /// A transform matrix + public static Matrix 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); + } + + /// + /// Creates a rotation matrix around an arbitrary vector + /// + /// The vector + /// Angle in degrees + /// A transform matrix + public static Matrix 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; + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Plane3D.cs b/src/Numerics/Spatial/Euclidean3D/Plane3D.cs new file mode 100644 index 00000000..35db26a5 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/Plane3D.cs @@ -0,0 +1,526 @@ +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 +{ + /// + /// A geometric plane + /// + [Serializable] + public struct Plane3D : IEquatable, IXmlSerializable + { + /// + /// The normal vector of the Plane. + /// + public readonly UnitVector3D Normal; + + /// + /// The distance to the Plane along its normal from the origin. + /// + public readonly double D; + + /// + /// Initializes a new instance of the struct. + /// Constructs a Plane from the X, Y, and Z components of its normal, and its distance from the origin on that normal. + /// + /// The X-component of the normal. + /// The Y-component of the normal. + /// The Z-component of the normal. + /// The distance of the Plane along its normal from the origin. + public Plane3D(double x, double y, double z, double d) + : this(UnitVector3D.Create(x, y, z), -d) + { + } + + /// + /// Initializes a new instance of the struct. + /// Constructs a Plane from the given normal and distance along the normal from the origin. + /// + /// The Plane's normal vector. + /// The Plane's distance from the origin along its normal vector. + public Plane3D(UnitVector3D normal, double offset = 0) + { + this.Normal = normal; + this.D = -offset; + } + + /// + /// Initializes a new instance of the struct. + /// Constructs a Plane from the given normal and distance along the normal from the origin. + /// + /// The Plane's normal vector. + /// A point in the plane. + public Plane3D(UnitVector3D normal, Point3D rootPoint) + : this(normal, normal.DotProduct(rootPoint)) + { + } + + /// + /// Initializes a new instance of the struct. + /// Constructs a Plane from the given normal and distance along the normal from the origin. + /// + /// The Plane's normal vector. + /// A point in the plane. + public Plane3D(Point3D rootPoint, UnitVector3D normal) + : this(normal, normal.DotProduct(rootPoint)) + { + } + + /// + /// Initializes a new instance of the struct. + /// Creates a Plane that contains the three given points. + /// http://www.had2know.com/academics/equation-plane-through-3-points.html + /// + /// The first point on the Plane. + /// The second point on the Plane. + /// The third point on the Plane. + /// The Plane containing the three points. + [Obsolete("This constructor will be removed, use factory method Plane.FromPoints. Made obsolete 2017-12-05.")] + public Plane3D(Point3D p1, Point3D p2, Point3D p3) + { + this = FromPoints(p1, p2, p3); + } + + /// + /// Gets the x component. + /// + public double A => this.Normal.X; + + /// + /// Gets the y component. + /// + public double B => this.Normal.Y; + + /// + /// Gets the y component. + /// + public double C => this.Normal.Z; + + /// + /// Gets the point on the plane closest to origin. + /// + public Point3D RootPoint => (-this.D * this.Normal).ToPoint3D(); + + /// + /// Returns a value that indicates whether each pair of elements in two specified geometric planes is equal. + /// + /// The first plane to compare. + /// The second plane to compare. + /// True if the geometric planes are the same; otherwise false. + public static bool operator ==(Plane3D left, Plane3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified geometric planes is not equal. + /// + /// The first plane to compare. + /// The second plane to compare. + /// True if the geometric planes are different; otherwise false. + public static bool operator !=(Plane3D left, Plane3D right) + { + return !left.Equals(right); + } + + /// + /// Initializes a new instance of the struct. + /// Creates a plane that contains the three given points. + /// + /// The first point on the plane. + /// The second point on the plane. + /// The third point on the plane. + /// The plane containing the three points. + 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); + } + + /// + /// Returns a point of intersection between three planes + /// + /// The first plane + /// The second plane + /// The third plane + /// The intersection point + public static Point3D PointFromPlanes(Plane3D plane1, Plane3D plane2, Plane3D plane3) + { + return Point3D.IntersectionOf(plane1, plane2, plane3); + } + + /// + /// Creates a Plane from its string representation + /// + /// The string representation of the Plane + /// a new Plane + [Obsolete("Should not have been made public, removed on 9/12/2017")] + public static Plane3D Parse(string s) + { + return Parser.ParsePlane(s); + } + + /// + /// Get the distance to the point along the + /// + /// The + /// The distance. + [Pure] + public double SignedDistanceTo(Point3D point) + { + var p = this.Project(point); + var v = p.VectorTo(point); + return v.DotProduct(this.Normal); + } + + /// + /// Get the distance to the plane along the + /// This assumes the planes are parallel + /// + /// The + /// The distance. + [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); + } + + /// + /// Get the distance to the ThroughPoint of along the + /// This assumes the ray is parallel to the plane. + /// + /// The + /// The distance. + [Pure] + public double SignedDistanceTo(Ray3D ray) + { + if (Math.Abs(ray.Direction.DotProduct(this.Normal) - 0) < 1E-15) + { + return this.SignedDistanceTo(ray.ThroughPoint); + } + + return 0; + } + + /// + /// Get the distance to the point. + /// + /// The + /// The distance. + [Pure] + public double AbsoluteDistanceTo(Point3D point) + { + return Math.Abs(this.SignedDistanceTo(point)); + } + + /// + /// Projects a point onto the plane + /// + /// A point + /// The direction of projection + /// a projected point + [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; + } + + /// + /// Projects a line onto the plane + /// + /// The line to project + /// A projected line + [Obsolete("Use LineSegment3D instead, obsolete from 2017-12-10")] + public Line3D Project(Line3D line3DToProject) + { + var projectedStartPoint = this.Project(line3DToProject.StartPoint); + var projectedEndPoint = this.Project(line3DToProject.EndPoint); + return new Line3D(projectedStartPoint, projectedEndPoint); + } + + /// + /// Projects a line onto the plane + /// + /// The line to project + /// A projected line + [Pure] + public LineSegment3D Project(LineSegment3D line3DToProject) + { + var projectedStartPoint = this.Project(line3DToProject.StartPoint); + var projectedEndPoint = this.Project(line3DToProject.EndPoint); + return new LineSegment3D(projectedStartPoint, projectedEndPoint); + } + + /// + /// Projects a ray onto the plane + /// + /// The ray to project + /// A projected ray + [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); + } + + /// + /// Project Vector3D onto this plane + /// + /// The Vector3D to project + /// The projected Vector3D + [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()); + } + + /// + /// Project Vector3D onto this plane + /// + /// The Vector3D to project + /// The projected Vector3D + [Pure] + public Ray3D Project(UnitVector3D vector3DToProject) + { + return this.Project(vector3DToProject.ToVector3D()); + } + + /// + /// Finds the intersection of the two planes, throws if they are parallel + /// http://mathworld.wolfram.com/Plane-PlaneIntersection.html + /// + /// a plane which intersects + /// A tolerance (epsilon) to account for floating point error. + /// A ray at the intersection. + [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); + } + + /// + /// Find intersection between Line3D and Plane + /// http://geomalgorithms.com/a05-_intersect-1.html + /// + /// A line segment + /// A tolerance (epsilon) to account for floating point error. + /// Intersection Point or null + [Obsolete("Use LineSegment3D instead, Obsolete from 2017-12-10")] + 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); + } + + /// + /// Find intersection between LineSegment3D and Plane + /// http://geomalgorithms.com/a05-_intersect-1.html + /// + /// A line segment + /// A tolerance (epsilon) to account for floating point error. + /// Intersection Point or null + [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); + } + + /// + /// http://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/raycast/sld017.htm + /// + /// A ray + /// A tolerance (epsilon) to account for floating point error. + /// The point of intersection. + [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); + } + + /// + /// Returns mirrored about the plane. + /// + /// The + /// The mirrored point. + [Pure] + public Point3D MirrorAbout(Point3D p) + { + var p2 = this.Project(p); + var d = this.SignedDistanceTo(p); + return p2 - (1 * d * this.Normal); + } + + /// + /// Rotates a plane + /// + /// The vector about which to rotate + /// An angle to rotate + /// A rotated plane + [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); + } + + /// + /// Returns a value to indicate if a pair of geometric planes are equal + /// + /// The geometric plane to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the geometric planes are equal; otherwise false + [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); + } + + /// + [Pure] + public bool Equals(Plane3D p) => this.D.Equals(p.D) && this.Normal.Equals(p.Normal); + + /// + [Pure] + public override bool Equals(object obj) => obj is Plane3D p && this.Equals(p); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.Normal, this.D); + + /// + [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)}"; + } + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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())); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteElement("RootPoint", this.RootPoint); + writer.WriteElement("Normal", this.Normal); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Point3D.cs b/src/Numerics/Spatial/Euclidean3D/Point3D.cs new file mode 100644 index 00000000..08bd6d19 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/Point3D.cs @@ -0,0 +1,522 @@ +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 +{ + /// + /// Represents a point in 3 dimensional space + /// + [Serializable] + public struct Point3D : IXmlSerializable, IEquatable, IFormattable + { + /// + /// The x component. + /// + public readonly double X; + + /// + /// The y component. + /// + public readonly double Y; + + /// + /// The z component. + /// + public readonly double Z; + + /// + /// Initializes a new instance of the struct. + /// + /// The x component. + /// The y component. + /// The z component. + public Point3D(double x, double y, double z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point from a list of coordinates (x, y, z) + /// + /// a list of coordinates in the order x, y, z + /// Exception thrown if anything other than 3 coordinates are passed + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public Point3D(IEnumerable data) + : this(data.ToArray()) + { + } + + /// + /// Initializes a new instance of the struct. + /// Creates a point from a list of coordinates (x, y, z) + /// + /// a size 3 array of coordinates in the order x, y, z + /// Exception thrown if anything other than 3 coordinates are passed + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public Point3D(double[] data) + : this(data[0], data[1], data[2]) + { + if (data.Length != 3) + { + throw new ArgumentException("Size must be 3"); + } + } + + /// + /// Gets a point at the origin + /// + public static Point3D Origin { get; } = new Point3D(0, 0, 0); + + /// + /// Gets a point where all values are NAN + /// + public static Point3D NaN { get; } = new Point3D(double.NaN, double.NaN, double.NaN); + + /// + /// Multiplies a matrix and a vector representation of the point together + /// + /// A matrix + /// A point + /// A Mathnet.Numerics vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(Matrix left, Point3D right) + { + return left * right.ToVector(); + } + + /// + /// Multiplies a matrix and a vector representation of the point together + /// + /// A point + /// A matrix + /// A Mathnet.Numerics vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(Point3D left, Matrix right) + { + return left.ToVector() * right; + } + + /// + /// Adds a point and a vector together + /// + /// A point + /// A vector + /// A new point at the summed location + public static Point3D operator +(Point3D point, Vector3D vector) + { + return new Point3D(point.X + vector.X, point.Y + vector.Y, point.Z + vector.Z); + } + + /// + /// Adds a point and a vector together + /// + /// A point + /// A vector + /// A new point at the summed location + public static Point3D operator +(Point3D point, UnitVector3D vector) + { + return new Point3D(point.X + vector.X, point.Y + vector.Y, point.Z + vector.Z); + } + + /// + /// Subtracts a vector from a point + /// + /// A point + /// A vector + /// A new point at the difference + public static Point3D operator -(Point3D point, Vector3D vector) + { + return new Point3D(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z); + } + + /// + /// Subtracts a vector from a point + /// + /// A point + /// A vector + /// A new point at the difference + public static Point3D operator -(Point3D point, UnitVector3D vector) + { + return new Point3D(point.X - vector.X, point.Y - vector.Y, point.Z - vector.Z); + } + + /// + /// Subtracts the first point from the second point + /// + /// The first point + /// The second point + /// A vector pointing to the difference + public static Vector3D operator -(Point3D left, Point3D right) + { + return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified points is equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are the same; otherwise false. + public static bool operator ==(Point3D left, Point3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified points is not equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are different; otherwise false. + public static bool operator !=(Point3D left, Point3D right) + { + return !left.Equals(right); + } + + /// + /// Attempts to convert a string of the form x,y,z into a point + /// + /// The string to be converted + /// A point with the coordinates specified + /// True if could be parsed. + public static bool TryParse(string text, out Point3D result) + { + return TryParse(text, null, out result); + } + + /// + /// Attempts to convert a string of the form x,y,z into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string of the form x,y,z into a point + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + 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}"); + } + + /// + /// Create a new from a Math.NET Numerics vector of length 3. + /// + /// A vector with length 2 to populate the created instance with. + /// A + public static Point3D OfVector(Vector 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)); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static Point3D ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + /// + /// Returns the centroid of an arbitrary collection of points + /// + /// a list of points + /// The centroid of the points + public static Point3D Centroid(IEnumerable points) + { + return Centroid(points.ToArray()); + } + + /// + /// Returns the centroid of an arbitrary collection of points + /// + /// a list of points + /// The centroid of the points + 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)); + } + + /// + /// Returns the midpoint of two points + /// + /// The first point + /// The second point + /// The midpoint of the points + public static Point3D MidPoint(Point3D p1, Point3D p2) + { + return Centroid(p1, p2); + } + + /// + /// Returns the point at which three planes intersect + /// + /// The first plane + /// The second plane + /// The third plane + /// The point of intersection + public static Point3D IntersectionOf(Plane3D plane1, Plane3D plane2, Plane3D plane3) + { + var ray = plane1.IntersectionWith(plane2); + return plane3.IntersectionWith(ray); + } + + /// + /// Returns the point of intersection between a plane and a ray + /// + /// A geometric plane + /// a ray + /// The point of intersection + public static Point3D IntersectionOf(Plane3D plane, Ray3D ray) + { + return plane.IntersectionWith(ray); + } + + /// + /// Returns the mirror point of this point across a plane + /// + /// A plane + /// The mirrored point + [Pure] + public Point3D MirrorAbout(Plane3D plane) + { + return plane.MirrorAbout(this); + } + + /// + /// Projects a point onto a plane + /// + /// a plane + /// The projected point + [Pure] + public Point3D ProjectOn(Plane3D plane) + { + return plane.Project(this); + } + + /// + /// Rotates the point about a given vector + /// + /// A vector + /// The angle to rotate + /// The rotated point + [Pure] + public Point3D Rotate(Vector3D aboutVector, Angle angle) + { + return this.Rotate(aboutVector.Normalize(), angle); + } + + /// + /// Rotates the point about a given vector + /// + /// A vector + /// The angle to rotate + /// The rotated point + [Pure] + public Point3D Rotate(UnitVector3D aboutVector, Angle angle) + { + var cs = CoordinateSystem3D.Rotation(angle, aboutVector); + return cs.Transform(this); + } + + /// + /// Gets a vector from this point to another point + /// + /// The point to which the vector should go + /// A vector pointing to the other point. + [Pure] + public Vector3D VectorTo(Point3D p) + { + return p - this; + } + + /// + /// Finds the straight line distance to another point + /// + /// The other point + /// a distance measure + [Pure] + public double DistanceTo(Point3D p) + { + var vector = this.VectorTo(p); + return vector.Length; + } + + /// + /// Converts this point into a vector from the origin + /// + /// A vector equivalent to this point + [Pure] + public Vector3D ToVector3D() + { + return new Vector3D(this.X, this.Y, this.Z); + } + + /// + /// Applies a transform coordinate system to the point + /// + /// A coordinate system + /// A new 3D point + [Pure] + public Point3D TransformBy(CoordinateSystem3D cs) + { + return cs.Transform(this); + } + + /// + /// Applies a transform matrix to the point + /// + /// A transform matrix + /// A new point + [Pure] + public Point3D TransformBy(Matrix m) + { + return OfVector(m.Multiply(this.ToVector())); + } + + /// + /// Convert to a Math.NET Numerics dense vector of length 3. + /// + /// A Math.Net Numerics vector + [Pure] + public Vector ToVector() + { + return Vector.Build.Dense(new[] { this.X, this.Y, this.Z }); + } + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + [Pure] + public string ToString(IFormatProvider provider) + { + return this.ToString(null, provider); + } + + /// + [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)); + } + + /// + /// Returns a value to indicate if a pair of points are equal + /// + /// The point to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the points are equal; otherwise false + [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; + } + + /// + [Pure] + public bool Equals(Point3D other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z); + } + + /// + [Pure] + public override bool Equals(object obj) => obj is Point3D p && this.Equals(p); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + XmlSchema IXmlSerializable.GetSchema() => null; + + /// + 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()}"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("X", this.X); + writer.WriteAttribute("Y", this.Y); + writer.WriteAttribute("Z", this.Z); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs b/src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs new file mode 100644 index 00000000..8b0b3442 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/PolyLine3D.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using MathNet.Numerics.Spatial.Internal; + +namespace MathNet.Numerics.Spatial.Euclidean3D +{ + /// + /// A PolyLine is an ordered series of line segments in space represented as list of connected Point3Ds. + /// + public class PolyLine3D : IEnumerable, IEquatable + { + /// + /// An internal list of points + /// + private readonly List points; + + /// + /// Initializes a new instance of the class. + /// Creates a PolyLine3D from a pre-existing IEnumerable of Point3Ds + /// + /// A list of points. + public PolyLine3D(IEnumerable points) + { + this.points = new List(points); + } + + /// + /// Gets an integer representing the number of Point3D objects in the polyline + /// + [Obsolete("Use VertexCount instead, obsolete since 2018-01-12")] + public int Count => this.points.Count; + + /// + /// Gets the number of vertices in the polyline. + /// + public int VertexCount => this.points.Count; + + /// + /// Gets the length of the polyline, computed as the sum of the lengths of every segment + /// + public double Length => this.GetPolyLineLength(); + + /// + /// Gets a list of vertices + /// + public IEnumerable Vertices + { + get + { + foreach (var point in this.points) + { + yield return point; + } + } + } + + /// + /// Returns a point in the polyline by index number + /// + /// The index of a point + /// The indexed point + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + public Point3D this[int key] => this.points[key]; + + /// + /// Returns a value that indicates whether each pair of elements in two specified lines is equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are the same; otherwise false. + public static bool operator ==(PolyLine3D left, PolyLine3D right) + { + return left?.Equals(right) == true; + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified lines is not equal. + /// + /// The first line to compare + /// The second line to compare + /// True if the lines are different; otherwise false. + public static bool operator !=(PolyLine3D left, PolyLine3D right) + { + return left?.Equals(right) != true; + } + + /// + /// 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. + /// + /// The fractional length at which to compute the point + /// A point a fraction of the way along the line. + 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); + } + + /// + /// 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. + /// + /// The distance from the first point along the curve at which to return a point + /// A point which is the specified distance along the line + 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++; + } + } + + /// + /// Returns the closest point on the polyline to the given point. + /// + /// A point + /// A point which is the closest to the given point but still on the line. + 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; + } + + /// + /// Returns a value to indicate if a pair of polylines are equal + /// + /// The polyline to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the polylines are equal; otherwise false + [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; + } + + /// + [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; + } + + /// + [Pure] + public override bool Equals(object obj) + { + return obj is PolyLine3D polyLine3D && + this.Equals(polyLine3D); + } + + /// + [Pure] + public override int GetHashCode() + { + return HashCode.CombineMany(this.points); + } + + /// + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + public IEnumerator GetEnumerator() + { + return this.points.GetEnumerator(); + } + + /// + [Obsolete("Use Vertices instead, obsolete since 2018-01-12")] + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Returns the length of the polyline by summing the lengths of the individual segments + /// + /// The length of the line. + 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; + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Ray3D.cs b/src/Numerics/Spatial/Euclidean3D/Ray3D.cs new file mode 100644 index 00000000..dedb8d79 --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/Ray3D.cs @@ -0,0 +1,222 @@ +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 +{ + /// + /// A ray in 3D space + /// + [Serializable] + public struct Ray3D : IEquatable, IXmlSerializable, IFormattable + { + /// + /// The start point of the ray + /// + public readonly Point3D ThroughPoint; + + /// + /// The direction of the ray + /// + public readonly UnitVector3D Direction; + + /// + /// Initializes a new instance of the struct. + /// + /// The start point of the ray. + /// The direction of the ray. + public Ray3D(Point3D throughPoint, UnitVector3D direction) + { + this.ThroughPoint = throughPoint; + this.Direction = direction; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The start point of the ray. + /// A vector indicating the direction of the ray. + public Ray3D(Point3D throughPoint, Vector3D direction) + : this(throughPoint, direction.Normalize()) + { + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified rays is equal. + /// + /// The first ray to compare + /// The second ray to compare + /// True if the rays are the same; otherwise false. + public static bool operator ==(Ray3D left, Ray3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified rays is not equal. + /// + /// The first ray to compare + /// The second ray to compare + /// True if the rays are different; otherwise false. + public static bool operator !=(Ray3D left, Ray3D right) + { + return !left.Equals(right); + } + + /// + /// The intersection of the two planes + /// + /// The first plane + /// The second plane + /// A ray at the intersection of two planes + public static Ray3D IntersectionOf(Plane3D plane1, Plane3D plane2) + { + return plane1.IntersectionWith(plane2); + } + + /// + /// Parses string representation of throughpoint and direction + /// See and for details on acceptable formats. + /// This is mainly meant for tests + /// + /// a string representing a start point for the ray. + /// a string representing a direction for the ray. + /// A ray. + public static Ray3D Parse(string point, string direction) + { + return new Ray3D(Point3D.Parse(point), UnitVector3D.Parse(direction)); + } + + /// + /// Parses a string in the format: 'p:{1, 2, 3} v:{0, 0, 1}' to a Ray3D + /// This is mainly meant for tests + /// + /// a string representing the ray + /// a ray + [Obsolete("Should not have been made public. Mode Obsolete 2017-12-09")] + public static Ray3D Parse(string s) + { + return Parser.ParseRay3D(s); + } + + /// + /// Returns the shortest line from a point to the ray + /// + /// A point. + /// A line segment from the point to the closest point on the ray + [Pure] + [Obsolete("Use ShortestLineTo, Obsolete from 2017-12-11")] + public Line3D LineTo(Point3D point3D) + { + var v = this.ThroughPoint.VectorTo(point3D); + var alongVector = v.ProjectOn(this.Direction); + return new Line3D(this.ThroughPoint + alongVector, point3D); + } + + /// + /// Returns the shortest line from a point to the ray + /// + /// A point. + /// A line segment from the point to the closest point on the ray + [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); + } + + /// + /// Returns the point at which this ray intersects with the plane + /// + /// A geometric plane. + /// A point of intersection if such an intersection exists; otherwise null. + [Pure] + public Point3D? IntersectionWith(Plane3D plane) + { + return plane.IntersectionWith(this); + } + + /// + /// Returns a value to indicate if a pair of rays are collinear + /// + /// The ray to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the rays are collinear; otherwise false. + [Pure] + public bool IsCollinear(Ray3D otherRay, double tolerance = float.Epsilon) + { + return this.Direction.IsParallelTo(otherRay.Direction, tolerance); + } + + /// + /// Returns a value to indicate if a pair of rays are equal + /// + /// The ray to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the rays are equal; otherwise false + [Pure] + public bool Equals(Ray3D other, double tolerance) + { + return this.Direction.Equals(other.Direction, tolerance) && + this.ThroughPoint.Equals(other.ThroughPoint, tolerance); + } + + /// + [Pure] + public bool Equals(Ray3D r) => this.Direction.Equals(r.Direction) && this.ThroughPoint.Equals(r.ThroughPoint); + + /// + [Pure] + public override bool Equals(object obj) => obj is Ray3D r && this.Equals(r); + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.ThroughPoint, this.Direction); + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + [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)); + } + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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())); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteElement("ThroughPoint", this.ThroughPoint); + writer.WriteElement("Direction", this.Direction); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs b/src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs new file mode 100644 index 00000000..7b064f9c --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/UnitVector3D.cs @@ -0,0 +1,958 @@ +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 +{ + /// + /// A unit vector, this is used to describe a direction in 3D + /// + [Serializable] + public struct UnitVector3D : IXmlSerializable, IEquatable, IEquatable, IFormattable + { + /// + /// The x component. + /// + public readonly double X; + + /// + /// The y component. + /// + public readonly double Y; + + /// + /// The z component. + /// + public readonly double Z; + + /// + /// Initializes a new instance of the struct. + /// The provided values are scaled to L2 norm == 1 + /// + /// The x component. + /// The y component. + /// The z component. + [Obsolete("This constructor will be made private, prefer the factory method Create. Made obsolete 2017-12-05.")] + public 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; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 3 points + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public UnitVector3D(IEnumerable data) + : this(data.ToArray()) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 3 points + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public UnitVector3D(double[] data) + : this(data[0], data[1], data[2]) + { + if (data.Length != 3) + { + throw new ArgumentException("Size must be 3"); + } + } + + /// + /// Gets the X axis + /// + public static UnitVector3D XAxis { get; } = Create(1, 0, 0); + + /// + /// Gets the Y axis + /// + public static UnitVector3D YAxis { get; } = Create(0, 1, 0); + + /// + /// Gets the z Axis + /// + public static UnitVector3D ZAxis { get; } = Create(0, 0, 1); + + /// + /// Gets a vector orthogonal to this + /// + [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); + } + } + + /// + /// Gets the length of the vector not the count of elements + /// + [Pure] + public double Length => 1; + + /// + /// Gets the cross product matrix + /// + [Pure] + internal Matrix CrossProductMatrix => Matrix.Build.Dense(3, 3, new[] { 0d, this.Z, -this.Y, -this.Z, 0d, this.X, this.Y, -this.X, 0d }); + + /// + /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. + /// + /// The first vector to compare + /// The second vector to compare + /// True if the vectors are the same; otherwise false. + public static bool operator ==(UnitVector3D left, UnitVector3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. + /// + /// The first vector to compare + /// The second vector to compare + /// True if the vectors are the same; otherwise false. + public static bool operator ==(Vector3D left, UnitVector3D right) + { + return left.Equals((object)right); + } + + /// + /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. + /// + /// The first vector to compare + /// The second vector to compare + /// True if the vectors are the same; otherwise false. + public static bool operator ==(UnitVector3D left, Vector3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified vectors is not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are different; otherwise false. + public static bool operator !=(UnitVector3D left, UnitVector3D right) + { + return !left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified vectors is not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are different; otherwise false. + public static bool operator !=(Vector3D left, UnitVector3D right) + { + return !left.Equals((object)right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified vectors is not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are different; otherwise false. + public static bool operator !=(UnitVector3D left, Vector3D right) + { + return !left.Equals(right); + } + + /// + /// Adds two vectors + /// + /// The first vector + /// The second vector + /// A new summed vector + public static Vector3D operator +(UnitVector3D v1, UnitVector3D v2) + { + return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); + } + + /// + /// Adds two vectors + /// + /// The first vector + /// The second vector + /// A new summed vector + public static Vector3D operator +(Vector3D v1, UnitVector3D v2) + { + return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); + } + + /// + /// Adds two vectors + /// + /// The first vector + /// The second vector + /// A new summed vector + public static Vector3D operator +(UnitVector3D v1, Vector3D v2) + { + return new Vector3D(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); + } + + /// + /// Subtracts two vectors + /// + /// The first vector + /// The second vector + /// A new difference vector + public static Vector3D operator -(UnitVector3D v1, UnitVector3D v2) + { + return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); + } + + /// + /// Subtracts two vectors + /// + /// The first vector + /// The second vector + /// A new difference vector + public static Vector3D operator -(Vector3D v1, UnitVector3D v2) + { + return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); + } + + /// + /// Subtracts two vectors + /// + /// The first vector + /// The second vector + /// A new difference vector + public static Vector3D operator -(UnitVector3D v1, Vector3D v2) + { + return new Vector3D(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); + } + + /// + /// Negates the vector + /// + /// A vector to negate + /// A new negated vector + public static Vector3D operator -(UnitVector3D v) + { + return new Vector3D(-1 * v.X, -1 * v.Y, -1 * v.Z); + } + + /// + /// Multiplies a vector by a scalar + /// + /// A scalar + /// A vector + /// A scaled vector + public static Vector3D operator *(double d, UnitVector3D v) + { + return new Vector3D(d * v.X, d * v.Y, d * v.Z); + } + + /// + /// Divides a vector by a scalar + /// + /// A vector + /// A scalar + /// A scaled vector + public static Vector3D operator /(UnitVector3D v, double d) + { + return new Vector3D(v.X / d, v.Y / d, v.Z / d); + } + + /// + /// Multiplies a Matrix by a Vector + /// + /// A Matrix + /// A Vector + /// A new vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(Matrix left, UnitVector3D right) + { + return left * right.ToVector(); + } + + /// + /// Multiplies a vector by a matrix + /// + /// A Vector + /// A Matrix + /// A new vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(UnitVector3D left, Matrix right) + { + return left.ToVector() * right; + } + + /// + /// Returns the dot product of two vectors + /// + /// The first vector + /// The second vector + /// A scalar result + public static double operator *(UnitVector3D left, UnitVector3D right) + { + return left.DotProduct(right); + } + + /// + /// Initializes a new instance of the struct. + /// The provided values are scaled to L2 norm == 1 + /// + /// The x component. + /// The y component. + /// The z component. + /// The allowed deviation from 1 for the L2-norm of x,y,z + /// The + 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 + } + + /// + /// Create a new from a Math.NET Numerics vector of length 3. + /// + /// A vector with length 2 to populate the created instance with. + /// A + public static UnitVector3D OfVector(Vector 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)); + } + + /// + /// Attempts to convert a string of the form x,y,z into a vector + /// + /// The string to be converted + /// A vector with the coordinates specified + /// The tolerance for how big deviation from Length = 1 is accepted + /// True if could be parsed. + public static bool TryParse(string text, out UnitVector3D result, double tolerance = 0.1) + { + return TryParse(text, null, out result, tolerance); + } + + /// + /// 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. + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + /// The tolerance for how big deviation from Length = 1 is accepted + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string of the form x,y,z into a vector + /// + /// The string to be converted + /// The + /// The tolerance for how big deviation from Length = 1 is accepted + /// A point at the coordinates specified + 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}"); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static UnitVector3D ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + /// + /// Scale this instance by + /// + /// The plane to project on. + /// The projected + [Pure] + public Vector3D ScaleBy(double factor) + { + return factor * this; + } + + /// + /// Project this instance onto the plane + /// + /// The plane to project on. + /// The projected + [Pure] + public Ray3D ProjectOn(Plane3D plane) + { + return plane.Project(this.ToVector3D()); + } + + /// + /// Returns the Dot product of the current vector and a unit vector + /// + /// A unit vector + /// Returns a new vector + [Pure] + public Vector3D ProjectOn(UnitVector3D uv) + { + var pd = this.DotProduct(uv); + return pd * this; + } + + /// + /// Computes whether or not this vector is parallel to another vector using the dot product method and comparing it + /// to within a specified tolerance. + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of unity, false if it is not + [Pure] + public bool IsParallelTo(Vector3D othervector, double tolerance = 1e-10) + { + var other = othervector.Normalize(); + return this.IsParallelTo(other, tolerance); + } + + /// + /// 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. + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of unity, false if not + [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; + } + + /// + /// Determine whether or not this vector is parallel to another vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when the vectors are considered parallel. + /// true if the vectors are parallel within the angle tolerance, false if they are not + [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; + } + + /// + /// Determine whether or not this vector is parallel to a unit vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when the vectors are considered parallel. + /// true if the vectors are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(Vector3D othervector, Angle angleTolerance) + { + var other = othervector.Normalize(); + return this.IsParallelTo(other, angleTolerance); + } + + /// + /// Computes whether or not this vector is perpendicular to another vector using the dot product method and + /// comparing it to within a specified tolerance + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of zero, false if not + [Pure] + public bool IsPerpendicularTo(Vector3D othervector, double tolerance = 1e-10) + { + var other = othervector.Normalize(); + return Math.Abs(this.DotProduct(other)) < tolerance; + } + + /// + /// Computes whether or not this vector is perpendicular to another vector using the dot product method and + /// comparing it to within a specified tolerance + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of zero, false if not + [Pure] + public bool IsPerpendicularTo(UnitVector3D othervector, double tolerance = 1e-10) + { + return Math.Abs(this.DotProduct(othervector)) < tolerance; + } + + /// + /// Inverses the direction of the vector, equivalent to multiplying by -1 + /// + /// A pointing in the opposite direction. + [Pure] + public UnitVector3D Negate() + { + return UnitVector3D.Create(-1 * this.X, -1 * this.Y, -1 * this.Z); + } + + /// + /// Returns the dot product of two vectors. + /// + /// The second vector. + /// The dot product. + [Pure] + public double DotProduct(Vector3D v) + { + return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z); + } + + /// + /// Returns the dot product of two vectors. + /// + /// The second vector. + /// The dot product. + [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)); + } + + /// + /// Subtracts a vector from this + /// + /// a vector to subtract + /// A new vector + [Obsolete("Use - instead")] + public Vector3D Subtract(UnitVector3D v) + { + return new Vector3D(this.X - v.X, this.Y - v.Y, this.Z - v.Z); + } + + /// + /// Adds a vector to this + /// + /// a vector to add + /// A new vector + [Obsolete("Use + instead")] + public Vector3D Add(UnitVector3D v) + { + return new Vector3D(this.X + v.X, this.Y + v.Y, this.Z + v.Z); + } + + /// + /// Returns the cross product of this vector and a vector + /// + /// A vector + /// A new vector with the cross product result + [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; + } + + /// + /// Returns the cross product of this vector and a unit vector + /// + /// A vector + /// A new vector with the cross product result + [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; + } + + /// + /// Returns a dense Matrix with the unit tensor product + /// + /// a dense matrix + [Pure] + public Matrix 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.Build.Dense(3, 3, new[] { this.X * this.X, xy, xz, xy, this.Y * this.Y, yz, xz, yz, this.Z * this.Z }); + } + + /// + /// Returns signed angle + /// + /// The vector to calculate the signed angle to + /// The vector around which to rotate to get the correct sign + /// A signed Angle + [Pure] + public Angle SignedAngleTo(Vector3D v, UnitVector3D about) + { + return this.SignedAngleTo(v.Normalize(), about); + } + + /// + /// Returns signed angle + /// + /// The vector to calculate the signed angle to + /// The vector around which to rotate to get the correct sign + /// A signed Angle + [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); + } + + /// + /// The nearest angle between the vectors + /// + /// The other vector + /// The angle + [Pure] + public Angle AngleTo(Vector3D v) + { + return this.AngleTo(v.Normalize()); + } + + /// + /// Compute the angle between this vector and a unit vector using the arccosine of the dot product. + /// + /// The other vector + /// The angle between the vectors, with a range between 0° and 180° + [Pure] + public Angle AngleTo(UnitVector3D v) + { + var dp = this.DotProduct(v); + var angle = Math.Acos(dp); + return Angle.FromRadians(angle); + } + + /// + /// Returns a vector that is this vector rotated the signed angle around the about vector + /// + /// The vector to rotate around. + /// The angle positive according to right hand rule. + /// A rotated vector. + [Pure] + public UnitVector3D Rotate(UnitVector3D about, Angle angle) + { + var cs = CoordinateSystem3D.Rotation(angle, about); + return cs.Transform(this).Normalize(); + } + + /// + /// Returns a point equivalent to the vector + /// + /// A point + [Pure] + public Point3D ToPoint3D() + { + return new Point3D(this.X, this.Y, this.Z); + } + + /// + /// Returns a Vector3D equivalent to this unit vector + /// + /// A vector + [Pure] + public Vector3D ToVector3D() + { + return new Vector3D(this.X, this.Y, this.Z); + } + + /// + /// Transforms the vector by a coordinate system and returns the transformed. + /// + /// A coordinate system + /// A new transformed vector + [Pure] + public Vector3D TransformBy(CoordinateSystem3D coordinateSystem) + { + return coordinateSystem.Transform(this.ToVector3D()); + } + + /// + /// Transforms a vector by multiplying it against a provided matrix + /// + /// The matrix to multiply + /// A new transformed vector + [Pure] + public Vector3D TransformBy(Matrix m) + { + return Vector3D.OfVector(m.Multiply(this.ToVector())); + } + + /// + /// Convert to a Math.NET Numerics dense vector of length 3. + /// + /// A dense vector + [Pure] + public Vector ToVector() + { + return Vector.Build.Dense(new[] { this.X, this.Y, this.Z }); + } + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + [Pure] + public string ToString(IFormatProvider provider) + { + return this.ToString(null, provider); + } + + /// + [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)); + } + + /// + /// Returns a value to indicate if a pair of vectors are equal + /// + /// The vector to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the vectors are equal; otherwise false + [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; + } + + /// + /// Returns a value to indicate if a pair of vectors are equal + /// + /// The vector to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// True if the vectors are equal; otherwise false + [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; + } + + /// + [Pure] + public bool Equals(Vector3D other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z); + } + + /// + [Pure] + public bool Equals(UnitVector3D other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z); + } + + /// + [Pure] + public override bool Equals(object obj) + { + return (obj is UnitVector3D u && this.Equals(u)) || (obj is Vector3D v && this.Equals(v)); + } + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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()}"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("X", this.X); + writer.WriteAttribute("Y", this.Y); + writer.WriteAttribute("Z", this.Z); + } + + /// + /// Returns the dot product of this vector with a second + /// + /// a second vector + /// The dot product + [Pure] + internal double DotProduct(Point3D v) + { + return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z); + } + } +} diff --git a/src/Numerics/Spatial/Euclidean3D/Vector3D.cs b/src/Numerics/Spatial/Euclidean3D/Vector3D.cs new file mode 100644 index 00000000..ee4a301a --- /dev/null +++ b/src/Numerics/Spatial/Euclidean3D/Vector3D.cs @@ -0,0 +1,767 @@ +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 +{ + /// + /// A struct representing a vector in 3D space + /// + [Serializable] + public struct Vector3D : IXmlSerializable, IEquatable, IEquatable, IFormattable + { + /// + /// The x component. + /// + public readonly double X; + + /// + /// The y component. + /// + public readonly double Y; + + /// + /// The z component. + /// + public readonly double Z; + + /// + /// Initializes a new instance of the struct. + /// + /// The x component. + /// The y component. + /// The z component. + public Vector3D(double x, double y, double z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 3 doubles + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public Vector3D(IEnumerable data) + : this(data.ToArray()) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// A list of 3 doubles + [Obsolete("This constructor will be removed. Made obsolete 2017-12-05.")] + public Vector3D(double[] data) + : this(data[0], data[1], data[2]) + { + if (data.Length != 3) + { + throw new ArgumentException("Size must be 3"); + } + } + + /// + /// Gets an invalid vector with no values + /// + public static Vector3D NaN => new Vector3D(double.NaN, double.NaN, double.NaN); + + /// + /// Gets the Euclidean Norm. + /// + [Pure] + public double Length => Math.Sqrt((this.X * this.X) + (this.Y * this.Y) + (this.Z * this.Z)); + + /// + /// Gets a unit vector orthogonal to this + /// + [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); + } + } + + /// + /// Gets a dense matrix containing the cross product of this vector + /// + [Pure] + internal Matrix CrossProductMatrix => Matrix.Build.Dense(3, 3, new[] { 0d, this.Z, -this.Y, -this.Z, 0d, this.X, this.Y, -this.X, 0d }); + + /// + /// Returns a value that indicates whether each pair of elements in two specified vectors is equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are the same; otherwise false. + public static bool operator ==(Vector3D left, Vector3D right) + { + return left.Equals(right); + } + + /// + /// Returns a value that indicates whether any pair of elements in two specified vectors is not equal. + /// + /// The first vector to compare. + /// The second vector to compare. + /// True if the vectors are different; otherwise false. + public static bool operator !=(Vector3D left, Vector3D right) + { + return !left.Equals(right); + } + + /// + /// Multiplies a Matrix by a Vector + /// + /// A Matrix + /// A Vector + /// A new vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(Matrix left, Vector3D right) + { + return left * right.ToVector(); + } + + /// + /// Multiplies a vector by a matrix + /// + /// A Vector + /// A Matrix + /// A new vector + [Obsolete("Not sure this is nice")] + public static Vector operator *(Vector3D left, Matrix right) + { + return left.ToVector() * right; + } + + /// + /// Returns the dot product of two vectors + /// + /// The first vector + /// The second vector + /// A scalar result + public static double operator *(Vector3D left, Vector3D right) + { + return left.DotProduct(right); + } + + /// + /// Adds two vectors + /// + /// The first vector + /// The second vector + /// A new summed vector + public static Vector3D operator +(Vector3D left, Vector3D right) + { + return new Vector3D(left.X + right.X, left.Y + right.Y, left.Z + right.Z); + } + + /// + /// Subtracts two vectors + /// + /// The first vector + /// The second vector + /// A new difference vector + public static Vector3D operator -(Vector3D left, Vector3D right) + { + return new Vector3D(left.X - right.X, left.Y - right.Y, left.Z - right.Z); + } + + /// + /// Negates the vector + /// + /// A vector to negate + /// A new negated vector + public static Vector3D operator -(Vector3D v) + { + return v.Negate(); + } + + /// + /// Multiplies a vector by a scalar + /// + /// A scalar + /// A vector + /// A scaled vector + public static Vector3D operator *(double d, Vector3D v) + { + return new Vector3D(d * v.X, d * v.Y, d * v.Z); + } + + /// + /// Divides a vector by a scalar + /// + /// A vector + /// A scalar + /// A scaled vector + public static Vector3D operator /(Vector3D v, double d) + { + return new Vector3D(v.X / d, v.Y / d, v.Z / d); + } + + /// + /// Attempts to convert a string of the form x,y,z into a vector + /// + /// The string to be converted + /// A vector with the coordinates specified + /// True if could be parsed. + public static bool TryParse(string text, out Vector3D result) + { + return TryParse(text, null, out result); + } + + /// + /// Attempts to convert a string of the form x,y,z into a vector + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + /// True if could be parsed. + 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; + } + + /// + /// Attempts to convert a string of the form x,y,z into a vector + /// + /// The string to be converted + /// The + /// A point at the coordinates specified + 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}"); + } + + /// + /// Create a new from a Math.NET Numerics vector of length 3. + /// + /// A vector with length 2 to populate the created instance with. + /// A + public static Vector3D OfVector(Vector 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)); + } + + /// + /// Creates an from an . + /// + /// An positioned at the node to read into this . + /// An that contains the data read from the reader. + public static Vector3D ReadFrom(XmlReader reader) + { + return reader.ReadElementAs(); + } + + ////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); + ////} + + /// + /// Compute and return a unit vector from this vector + /// + /// a normalized unit vector + [Pure] + public UnitVector3D Normalize() + { + return UnitVector3D.Create(this.X, this.Y, this.Z); + } + + /// + /// Multiplies the current vector by a scalar + /// + /// a scalar + /// A new scaled vector + [Pure] + public Vector3D ScaleBy(double scaleFactor) + { + return scaleFactor * this; + } + + /// + /// Projects the vector onto a plane + /// + /// A geometric plane + /// A ray + [Pure] + public Ray3D ProjectOn(Plane3D planeToProjectOn) + { + return planeToProjectOn.Project(this); + } + + /// + /// Returns the Dot product of the current vector and a unit vector + /// + /// A unit vector + /// Returns a new vector + [Pure] + public Vector3D ProjectOn(UnitVector3D uv) + { + var pd = this.DotProduct(uv); + return pd * uv; + } + + /// + /// Computes whether or not this vector is parallel to another vector using the dot product method and comparing it + /// to within a specified tolerance. + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of unity, false if it is not + [Pure] + public bool IsParallelTo(Vector3D other, double tolerance = 1e-10) + { + var @this = this.Normalize(); + return @this.IsParallelTo(other, tolerance); + } + + /// + /// 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. + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of unity, false if not + [Pure] + public bool IsParallelTo(UnitVector3D other, double tolerance = 1e-10) + { + return this.Normalize().IsParallelTo(other, tolerance); + } + + /// + /// Determine whether or not this vector is parallel to another vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when the vectors are considered parallel. + /// true if the vectors are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(Vector3D other, Angle tolerance) + { + return this.Normalize().IsParallelTo(other, tolerance); + } + + /// + /// Determine whether or not this vector is parallel to a unit vector within a given angle tolerance. + /// + /// The other + /// The tolerance for when the vectors are considered parallel. + /// true if the vectors are parallel within the angle tolerance, false if they are not + [Pure] + public bool IsParallelTo(UnitVector3D other, Angle tolerance) + { + var @this = this.Normalize(); + return @this.IsParallelTo(other, tolerance); + } + + /// + /// Computes whether or not this vector is perpendicular to another vector using the dot product method and + /// comparing it to within a specified tolerance + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of zero, false if not + [Pure] + public bool IsPerpendicularTo(Vector3D other, double tolerance = 1e-6) + { + return Math.Abs(this.Normalize().DotProduct(other.Normalize())) < tolerance; + } + + /// + /// Computes whether or not this vector is perpendicular to another vector using the dot product method and + /// comparing it to within a specified tolerance + /// + /// The other + /// A tolerance value for the dot product method. Values below 2*Precision.DoublePrecision may cause issues. + /// true if the vector dot product is within the given tolerance of zero, false if not + [Pure] + public bool IsPerpendicularTo(UnitVector3D other, double tolerance = 1e-6) + { + return Math.Abs(this.Normalize().DotProduct(other)) < tolerance; + } + + /// + /// Inverses the direction of the vector, equivalent to multiplying by -1 + /// + /// A pointing in the opposite direction. + [Pure] + public Vector3D Negate() + { + return new Vector3D(-1 * this.X, -1 * this.Y, -1 * this.Z); + } + + /// + /// Returns the dot product of two vectors. + /// + /// The second vector. + /// The dot product. + [Pure] + public double DotProduct(Vector3D v) + { + return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z); + } + + /// + /// Returns the dot product of two vectors. + /// + /// The second vector. + /// The dot product. + [Pure] + public double DotProduct(UnitVector3D v) + { + return (this.X * v.X) + (this.Y * v.Y) + (this.Z * v.Z); + } + + /// + /// Subtracts a vector from this + /// + /// a vector to subtract + /// A new vector + [Obsolete("Use - instead")] + public Vector3D Subtract(Vector3D v) + { + return new Vector3D(this.X - v.X, this.Y - v.Y, this.Z - v.Z); + } + + /// + /// Adds a vector to this + /// + /// a vector to add + /// A new vector + [Obsolete("Use + instead")] + [Pure] + public Vector3D Add(Vector3D v) + { + return new Vector3D(this.X + v.X, this.Y + v.Y, this.Z + v.Z); + } + + /// + /// Returns the cross product of this vector and + /// + /// A vector + /// A new vector with the cross product result + [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; + } + + /// + /// Returns the cross product of this vector and + /// + /// A vector + /// A new vector with the cross product result + [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; + } + + /// + /// 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] + /// + /// a dense matrix + [Pure] + public Matrix 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.Build.Dense(3, 3, new[] { this.X * this.X, xy, xz, xy, this.Y * this.Y, yz, xz, yz, this.Z * this.Z }); + } + + /// + /// Returns signed angle + /// + /// The vector to calculate the signed angle to + /// The vector around which to rotate to get the correct sign + /// A signed Angle + [Pure] + public Angle SignedAngleTo(Vector3D v, UnitVector3D about) + { + return this.Normalize().SignedAngleTo(v.Normalize(), about); + } + + /// + /// Returns signed angle + /// + /// The vector to calculate the signed angle to + /// The vector around which to rotate to get the correct sign + /// A signed angle + [Pure] + public Angle SignedAngleTo(UnitVector3D v, UnitVector3D about) + { + return this.Normalize().SignedAngleTo(v, about); + } + + /// + /// Compute the angle between this vector and another using the arccosine of the dot product. + /// + /// The other vector + /// The angle between the vectors, with a range between 0° and 180° + [Pure] + public Angle AngleTo(Vector3D v) + { + var uv1 = this.Normalize(); + var uv2 = v.Normalize(); + return uv1.AngleTo(uv2); + } + + /// + /// Compute the angle between this vector and a unit vector using the arccosine of the dot product. + /// + /// The other vector + /// The angle between the vectors, with a range between 0° and 180° + [Pure] + public Angle AngleTo(UnitVector3D v) + { + var uv = this.Normalize(); + return uv.AngleTo(v); + } + + /// + /// Returns a vector that is this vector rotated the signed angle around the about vector + /// + /// A vector to rotate about + /// A signed angle + /// A rotated vector. + [Pure] + public Vector3D Rotate(Vector3D about, Angle angle) + { + return this.Rotate(about.Normalize(), angle); + } + + /// + /// Returns a vector that is this vector rotated the signed angle around the about vector + /// + /// A unit vector to rotate about + /// A signed angle + /// A rotated vector. + [Pure] + public Vector3D Rotate(UnitVector3D about, Angle angle) + { + var cs = CoordinateSystem3D.Rotation(angle, about); + return cs.Transform(this); + } + + /// + /// Returns a point equivalent to the vector + /// + /// A point + public Point3D ToPoint3D() + { + return new Point3D(this.X, this.Y, this.Z); + } + + /// + /// Transforms the vector by a coordinate system and returns the transformed. + /// + /// A coordinate system + /// A new transformed vector + [Pure] + public Vector3D TransformBy(CoordinateSystem3D coordinateSystem) + { + return coordinateSystem.Transform(this); + } + + /// + /// Transforms a vector by multiplying it against a provided matrix + /// + /// The matrix to multiply + /// A new transformed vector + [Pure] + public Vector3D TransformBy(Matrix m) + { + return Vector3D.OfVector(m.Multiply(this.ToVector())); + } + + /// + /// Convert to a Math.NET Numerics dense vector of length 3. + /// + /// A dense vector + [Pure] + public Vector ToVector() + { + return Vector.Build.Dense(new[] { this.X, this.Y, this.Z }); + } + + /// + [Pure] + public override string ToString() + { + return this.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Returns a string representation of this instance using the provided + /// + /// A + /// The string representation of this instance. + [Pure] + public string ToString(IFormatProvider provider) + { + return this.ToString(null, provider); + } + + /// + 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)); + } + + /// + /// Returns a value to indicate if a pair of vectors are equal + /// + /// The vector to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the vectors are equal; otherwise false + [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; + } + + /// + /// Returns a value to indicate if this vector is equivalent to a given unit vector + /// + /// The unit vector to compare against. + /// A tolerance (epsilon) to adjust for floating point error + /// true if the vectors are equal; otherwise false + [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; + } + + /// + [Pure] + public bool Equals(Vector3D other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z); + } + + /// + [Pure] + public bool Equals(UnitVector3D other) + { + return this.X.Equals(other.X) && this.Y.Equals(other.Y) && this.Z.Equals(other.Z); + } + + /// + [Pure] + public override bool Equals(object obj) + { + return (obj is UnitVector3D u && this.Equals(u)) || (obj is Vector3D v && this.Equals(v)); + } + + /// + [Pure] + public override int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z); + + /// + XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + /// + 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()}"); + } + + /// + void IXmlSerializable.WriteXml(XmlWriter writer) + { + writer.WriteAttribute("X", this.X); + writer.WriteAttribute("Y", this.Y); + writer.WriteAttribute("Z", this.Z); + } + } +} diff --git a/src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs b/src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs new file mode 100644 index 00000000..567aa301 --- /dev/null +++ b/src/Numerics/Spatial/Internal/AvlTreeSet/AvlNode.cs @@ -0,0 +1,109 @@ +using System.Diagnostics.CodeAnalysis; + +namespace MathNet.Numerics.Spatial.Internal.AvlTreeSet +{ + /// + /// A node of the Avl Tree + /// + /// Any type + [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "By design")] + internal sealed class AvlNode + { + /// + /// Gets or sets the parent node + /// + public AvlNode Parent; + + /// + /// Gets or sets the left node + /// + public AvlNode Left; + + /// + /// Gets or sets the right node + /// + public AvlNode Right; + + /// + /// Gets or sets the item + /// + public T Item; + + /// + /// Gets or sets the Avl balance + /// + public int Balance; + + /// + /// Non recursive function that return the next ordered node + /// + /// The next node + public AvlNode GetNextNode() + { + AvlNode 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; + } + + /// + /// Non recursive function that return the previous ordered node + /// + /// The previous node + public AvlNode GetPreviousNode() + { + AvlNode 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; + } + + /// + 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()},"; + } + } +} diff --git a/src/Numerics/Spatial/Internal/AvlTreeSet/AvlNodeItemEnumerator.cs b/src/Numerics/Spatial/Internal/AvlTreeSet/AvlNodeItemEnumerator.cs new file mode 100644 index 00000000..0c509f80 --- /dev/null +++ b/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 +{ + /// + /// An enumerator for AvlNodeItems + /// + /// Any type which is also a node type + internal class AvlNodeItemEnumerator : IEnumerator + { + /// + /// A reference to the tree + /// + private readonly AvlTreeSet avlTree; + + /// + /// The current node + /// + private AvlNode current = null; + + /// + /// Initializes a new instance of the class. + /// + /// The tree to enumerate + public AvlNodeItemEnumerator(AvlTreeSet avlTree) + { + this.avlTree = avlTree ?? throw new ArgumentNullException("avlTree can't be null"); + } + + /// + /// Gets the current node + /// + public T Current + { + get + { + if (this.current == null) + { + throw new InvalidOperationException("Current is invalid"); + } + + return this.current.Item; + } + } + + /// + /// Gets the current node + /// + object IEnumerator.Current => this.Current; + + /// + /// Dispose of the enumerator + /// + public void Dispose() + { + } + + /// + /// Moves to the next node + /// + /// True if the move to the next node was successful; otherwise false + 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; + } + + /// + /// Resets the enumerator + /// + public void Reset() + { + this.current = null; + } + } +} diff --git a/src/Numerics/Spatial/Internal/AvlTreeSet/AvlTreeSet.cs b/src/Numerics/Spatial/Internal/AvlTreeSet/AvlTreeSet.cs new file mode 100644 index 00000000..2f56ca06 --- /dev/null +++ b/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 +{ + /// + /// 2016-12-08, Eric Ouellet + /// The code is an adapted version of BitLush AvlTree: https://bitlush.com/blog/efficient-avl-tree-in-c-sharp + /// + /// Any type + internal class AvlTreeSet : IEnumerable, IEnumerable, ICollection, ICollection + { + /// + /// A comparer + /// + private readonly IComparer comparer; + + /// + /// The root node of the tree + /// + private AvlNode root; + + /// + /// a sync object + /// + private object syncRoot; + + /// + /// A count of nodes + /// + private int count = 0; + + /// + /// Initializes a new instance of the class. + /// + /// a comparer for nodes + public AvlTreeSet(IComparer comparer) + { + this.comparer = comparer; + } + + /// + /// Initializes a new instance of the class. + /// + public AvlTreeSet() + : this(Comparer.Default) + { + } + + /// + /// Gets the root node + /// + public AvlNode Root => this.root; + + /// + /// Gets the count of nodes + /// + public int Count => this.count; + + /// + /// Gets the sync object + /// + public object SyncRoot + { + get + { + if (this.syncRoot == null) + { + Interlocked.CompareExchange(ref this.syncRoot, new object(), (object)null); + } + + return this.syncRoot; + } + } + + /// + /// Gets a value indicating whether the tree is synchronized; + /// + public bool IsSynchronized => true; + + /// + /// Gets a value indicating whether the tree is read only; + /// + public bool IsReadOnly => false; + + /// + /// Gets an enumerator for the tree + /// + /// An enumerator + public IEnumerator GetEnumerator() + { + return new AvlNodeItemEnumerator(this); + } + + /// + /// Gets a value indicating whether the tree contains an item + /// + /// The item to find + /// True if the item is found; otherwise false; + public bool Contains(T item) + { + AvlNode 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; + } + + /// + /// Adds an item to the tree + /// + /// The item to add + /// True if adding was successful; otherwise false + public virtual bool Add(T item) + { + AvlNode node = this.root; + + while (node != null) + { + int compare = this.comparer.Compare(item, node.Item); + + if (compare < 0) + { + AvlNode left = node.Left; + + if (left == null) + { + node.Left = new AvlNode { Item = item, Parent = node }; + this.AddBalance(node, 1); + return true; + } + else + { + node = left; + } + } + else if (compare > 0) + { + AvlNode right = node.Right; + + if (right == null) + { + node.Right = new AvlNode { Item = item, Parent = node }; + this.AddBalance(node, -1); + return true; + } + else + { + node = right; + } + } + else + { + return false; + } + } + + this.root = new AvlNode { Item = item }; + this.count++; + + return true; + } + + /// + public void Clear() + { + this.root = null; + this.count = 0; + } + + /// + /// Gets an enumerator + /// + /// an enumerator + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Copies to an array + /// + /// The array to copy to + /// start point + /// number of items to copy + 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 current = this.GetFirstNode(); + while (current.GetNextNode() != null) + { + if (indexIter >= index) + { + array[indexArray] = current.Item; + indexArray++; + count--; + if (count == 0) + { + return; + } + } + + indexIter++; + } + + /* + foreach (AvlNode node in this.Nodes()) + { + if (indexIter >= index) + { + array[indexArray] = node.Item; + indexArray++; + count--; + if (count == 0) + { + return; + } + } + + indexIter++; + }*/ + } + + /// + public void CopyTo(T[] array, int arrayIndex) + { + this.CopyTo(array, arrayIndex, this.Count); + } + + /// + public void CopyTo(Array array, int index) + { + this.CopyTo(array as T[], index, this.Count); + } + + /// + void ICollection.Add(T item) + { + this.Add(item); + } + + /// + /// Gets the first item + /// + /// The first item + public T GetFirstItem() + { + AvlNode node = this.GetFirstNode(); + if (node != null) + { + return node.Item; + } + + return default(T); + } + + /// + /// Gets the first node + /// + /// The first node + public AvlNode GetFirstNode() + { + if (this.Root != null) + { + AvlNode current = this.Root; + while (current.Left != null) + { + current = current.Left; + } + + return current; + } + + return null; + } + + /// + /// gets the last item + /// + /// The last item + public T GetLastItem() + { + AvlNode node = this.GetLastNode(); + if (node != null) + { + return node.Item; + } + + return default(T); + } + + /// + /// Gets the last node + /// + /// returns the last node + public AvlNode GetLastNode() + { + if (this.Root != null) + { + AvlNode current = this.Root; + while (current.Right != null) + { + current = current.Right; + } + + return current; + } + + return null; + } + + /// + /// Removes a node + /// + /// item to remove + /// True if successful; otherwise false + public virtual bool Remove(T item) + { + AvlNode 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; + } + + /// + /// Gets a node with the provided item + /// + /// An item to find + /// The node with that item + protected AvlNode GetNode(T item) + { + AvlNode 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; + } + + /// + /// Should always be called for any inserted node + /// + /// A node + /// The balance measure + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void AddBalance(AvlNode 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 parent = node.Parent; + + if (parent != null) + { + balance = parent.Left == node ? 1 : -1; + } + + node = parent; + } + } + + /// + /// Rotate the tree node left + /// + /// A node + /// The rotated node + protected AvlNode RotateLeft(AvlNode node) + { + AvlNode right = node.Right; + AvlNode rightLeft = right.Left; + AvlNode 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; + } + + /// + /// Rotate a tree node right + /// + /// a node + /// The rotated tree node + protected AvlNode RotateRight(AvlNode node) + { + AvlNode left = node.Left; + AvlNode leftRight = left.Right; + AvlNode 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; + } + + /// + /// Rotate a tree node leftright + /// + /// a node + /// a rotated tree node + protected AvlNode RotateLeftRight(AvlNode node) + { + AvlNode left = node.Left; + AvlNode leftRight = left.Right; + AvlNode parent = node.Parent; + AvlNode leftRightRight = leftRight.Right; + AvlNode 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; + } + + /// + /// Rotate a tree node rightleft + /// + /// a node + /// a rotated tree node + protected AvlNode RotateRightLeft(AvlNode node) + { + AvlNode right = node.Right; + AvlNode rightLeft = right.Left; + AvlNode parent = node.Parent; + AvlNode rightLeftLeft = rightLeft.Left; + AvlNode 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; + } + + /// + /// Removes a node + /// + /// node to remove + protected void RemoveNode(AvlNode node) + { + this.count--; + + AvlNode left = node.Left; + AvlNode 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 successor = right; + + if (successor.Left == null) + { + AvlNode 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 parent = node.Parent; + AvlNode successorParent = successor.Parent; + AvlNode 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); + } + } + } + + /// + /// Should always be called for any removed node + /// + /// a node + /// the node balance + protected void RemoveBalance(AvlNode 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 parent = node.Parent; + + if (parent != null) + { + balance = parent.Left == node ? -1 : 1; + } + + node = parent; + } + } + + /// + /// Replace a node + /// + /// The node to replace + /// The replacing node + private static void Replace(AvlNode target, AvlNode source) + { + AvlNode left = source.Left; + AvlNode 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; + } + } + + /// + /// Counts the nodes recursively + /// + /// A node in the tree + /// A count of nodes + private int RecursiveCount(AvlNode node) + { + if (node == null) + { + return 0; + } + + return 1 + this.RecursiveCount(node.Left) + this.RecursiveCount(node.Right); + } + + /// + /// Find child max height + /// + /// A node in the tree + /// The height + private int RecursiveGetChildMaxHeight(AvlNode 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); + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs b/src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs new file mode 100644 index 00000000..5f6a0c23 --- /dev/null +++ b/src/Numerics/Spatial/Internal/ConvexHull/ConvexHull.cs @@ -0,0 +1,1522 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using MathNet.Numerics.Spatial.Euclidean2D; + +namespace MathNet.Numerics.Spatial.Internal.ConvexHull +{ + /// + /// An implementation of the work of Lui, Chen and Ouellet for solving the convex hull problem + /// https://www.codeproject.com/Articles/1210225/Fast-and-improved-D-Convex-Hull-algorithm-and-its + /// + /// Quadrant: Q2 | Q1 + /// ------- + /// Q3 | Q4 + /// + /// + internal class ConvexHull + { + /// + /// First quadrant + /// + private Quadrant q1; + + /// + /// Second quadrant + /// + private Quadrant q2; + + /// + /// Third quadrant + /// + private Quadrant q3; + + /// + /// Fourth quadrant + /// + private Quadrant q4; + + /// + /// Initial list of points + /// + private MutablePoint[] listOfPoint; + + /// + /// A value indicating if the graph needs closing + /// + private bool shouldCloseTheGraph; + + /// + /// A lock object + /// + private object findLimitFinalLock = new object(); + + /// + /// A limit + /// + private Limit limit = default(Limit); + + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// True if the graph should be closed; otherwise false + /// An estimate for the initial size of the result set + public ConvexHull(IEnumerable listOfPoint, bool shouldCloseTheGraph = true, int initialResultGuessSize = 0) + { + List l = new List(); + foreach (var point in listOfPoint) + { + MutablePoint p = new MutablePoint(point.X, point.Y); + l.Add(p); + } + + this.Init(l.ToArray(), shouldCloseTheGraph); + } + + /// + /// Returns the results as an array of points + /// + /// The results + public Point2D[] GetResultsAsArrayOfPoint() + { + if (this.listOfPoint == null || !this.listOfPoint.Any()) + { + return new Point2D[0]; + } + + int countOfPoints = this.q1.Count + this.q2.Count + this.q3.Count + this.q4.Count; + + if (this.q1.LastPoint == this.q2.FirstPoint) + { + countOfPoints--; + } + + if (this.q2.LastPoint == this.q3.FirstPoint) + { + countOfPoints--; + } + + if (this.q3.LastPoint == this.q4.FirstPoint) + { + countOfPoints--; + } + + if (this.q4.LastPoint == this.q1.FirstPoint) + { + countOfPoints--; + } + + // Case where there is only one point + if (countOfPoints == 0) + { + return new Point2D[] { new Point2D(this.q1.FirstPoint.X, this.q1.FirstPoint.Y) }; + } + + if (this.shouldCloseTheGraph) + { + countOfPoints++; + } + + Point2D[] results = new Point2D[countOfPoints]; + + int resultIndex = -1; + + if (this.q1.FirstPoint != this.q4.LastPoint) + { + foreach (MutablePoint pt in this.q1) + { + results[++resultIndex] = new Point2D(pt.X, pt.Y); + } + } + else + { + var enumerator = this.q1.GetEnumerator(); + enumerator.Reset(); + if (enumerator.MoveNext()) + { + // Skip first (same as the last one as quadrant 4 + while (enumerator.MoveNext()) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + } + } + + if (this.q2.Count == 1) + { + if (this.q2.FirstPoint != this.q1.LastPoint) + { + results[++resultIndex] = new Point2D(this.q2.FirstPoint.X, this.q2.FirstPoint.Y); + } + } + else + { + var enumerator = this.q2.GetEnumerator(); + enumerator.Reset(); + if (enumerator.MoveNext()) + { + if (enumerator.Current != this.q1.LastPoint) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + + while (enumerator.MoveNext()) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + } + } + + if (this.q3.Count == 1) + { + if (this.q3.FirstPoint != this.q2.LastPoint) + { + results[++resultIndex] = new Point2D(this.q3.FirstPoint.X, this.q3.FirstPoint.Y); + } + } + else + { + var enumerator = this.q3.GetEnumerator(); + enumerator.Reset(); + if (enumerator.MoveNext()) + { + if (enumerator.Current != this.q2.LastPoint) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + + while (enumerator.MoveNext()) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + } + } + + if (this.q4.Count == 1) + { + if (this.q4.FirstPoint != this.q3.LastPoint) + { + results[++resultIndex] = new Point2D(this.q4.FirstPoint.X, this.q4.FirstPoint.Y); + } + } + else + { + var enumerator = this.q4.GetEnumerator(); + enumerator.Reset(); + if (enumerator.MoveNext()) + { + if (enumerator.Current != this.q3.LastPoint) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + + while (enumerator.MoveNext()) + { + results[++resultIndex] = new Point2D(enumerator.Current.X, enumerator.Current.Y); + } + } + } + + if (this.shouldCloseTheGraph && results[resultIndex] != results[0]) + { + results[++resultIndex] = results[0]; + } + + return results; + } + + /// + /// Calculate the Convex Hull + /// + [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Simple struct clearly named")] + public void CalcConvexHull() + { + if (this.IsZeroData()) + { + return; + } + + this.SetQuadrantLimitsOneThread(); + + this.q1.Prepare(); + this.q2.Prepare(); + this.q3.Prepare(); + this.q4.Prepare(); + + MutablePoint q1Root = this.q1.RootPoint; + MutablePoint q2Root = this.q2.RootPoint; + MutablePoint q3Root = this.q3.RootPoint; + MutablePoint q4Root = this.q4.RootPoint; + + // Main Loop to extract ConvexHullPoints + MutablePoint[] points = this.listOfPoint; + int index = 0; + int pointCount = points.Length; + + if (points != null) + { + MutablePoint point; + + if (this.IsQuadrantAreDisjoint()) + { + Q1First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + goto Q1First; + } + else + { + goto End; + } + + Q2First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + goto Q2First; + } + else + { + goto End; + } + + Q3First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + goto Q3First; + } + else + { + goto End; + } + + Q4First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + goto Q4First; + } + else + { + goto End; + } + } + else + { + // Not disjoint + Q1First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + } + + goto Q3First; + } + + goto Q1First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + } + + goto Q4First; + } + + goto Q2First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + } + + goto Q3First; + } + else if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + } + + goto Q4First; + } + + goto Q1First; + } + else + { + goto End; + } + + Q2First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + } + + goto Q4First; + } + + goto Q2First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + } + + goto Q1First; + } + + goto Q3First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + } + + goto Q4First; + } + else if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + } + + goto Q1First; + } + + goto Q2First; + } + else + { + goto End; + } + + Q3First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + } + + goto Q1First; + } + + goto Q3First; + } + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + } + + goto Q2First; + } + + goto Q4First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + } + else if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + } + + goto Q3First; + } + else + { + goto End; + } + + Q4First: + if (index < pointCount) + { + point = points[index++]; + + if (point.X > q4Root.X && point.Y < q4Root.Y) + { + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, point)) + { + this.q4.ProcessPoint(ref point); + goto Q4First; + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + } + + goto Q2First; + } + + goto Q4First; + } + + if (point.X > q1Root.X && point.Y > q1Root.Y) + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, point)) + { + this.q1.ProcessPoint(ref point); + goto Q1First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + } + + goto Q3First; + } + + goto Q1First; + } + + if (point.X < q3Root.X && point.Y < q3Root.Y) + { + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, point)) + { + this.q3.ProcessPoint(ref point); + goto Q3First; + } + } + + if (point.X < q2Root.X && point.Y > q2Root.Y) + { + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, point)) + { + this.q2.ProcessPoint(ref point); + goto Q2First; + } + } + + goto Q4First; + } + else + { + goto End; + } + } + + End: + { + } + } + } + + /// + /// True if the point to check is to the right of the other provided points + /// + /// The first point + /// The second point + /// The point to check + /// True if the point is to rhe right of the other; Otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool IsPointToTheRightOfOthers(MutablePoint p1, MutablePoint p2, MutablePoint pointToCheck) + { + return ((p2.X - p1.X) * (pointToCheck.Y - p1.Y)) - ((p2.Y - p1.Y) * (pointToCheck.X - p1.X)) < 0; + } + + /// + /// Set quadrant limits + /// + private void SetQuadrantLimitsOneThread() + { + MutablePoint pointFirst = this.listOfPoint.First(); + + // Find the quadrant limits (maximum x and y) + double right, topLeft, topRight, left, bottomLeft, bottomRight; + right = topLeft = topRight = left = bottomLeft = bottomRight = pointFirst.X; + + double top, rightTop, rightBottom, bottom, leftTop, leftBottom; + top = rightTop = rightBottom = bottom = leftTop = leftBottom = pointFirst.Y; + + foreach (MutablePoint pt in this.listOfPoint) + { + if (pt.X >= right) + { + if (pt.X == right) + { + if (pt.Y > rightTop) + { + rightTop = pt.Y; + } + else + { + if (pt.Y < rightBottom) + { + rightBottom = pt.Y; + } + } + } + else + { + right = pt.X; + rightTop = rightBottom = pt.Y; + } + } + + if (pt.X <= left) + { + if (pt.X == left) + { + if (pt.Y > leftTop) + { + leftTop = pt.Y; + } + else + { + if (pt.Y < leftBottom) + { + leftBottom = pt.Y; + } + } + } + else + { + left = pt.X; + leftBottom = leftTop = pt.Y; + } + } + + if (pt.Y >= top) + { + if (pt.Y == top) + { + if (pt.X < topLeft) + { + topLeft = pt.X; + } + else + { + if (pt.X > topRight) + { + topRight = pt.X; + } + } + } + else + { + top = pt.Y; + topLeft = topRight = pt.X; + } + } + + if (pt.Y <= bottom) + { + if (pt.Y == bottom) + { + if (pt.X < bottomLeft) + { + bottomLeft = pt.X; + } + else + { + if (pt.X > bottomRight) + { + bottomRight = pt.X; + } + } + } + else + { + bottom = pt.Y; + bottomRight = bottomLeft = pt.X; + } + } + + this.q1.FirstPoint = new MutablePoint(right, rightTop); + this.q1.LastPoint = new MutablePoint(topRight, top); + this.q1.RootPoint = new MutablePoint(topRight, rightTop); + + this.q2.FirstPoint = new MutablePoint(topLeft, top); + this.q2.LastPoint = new MutablePoint(left, leftTop); + this.q2.RootPoint = new MutablePoint(topLeft, leftTop); + + this.q3.FirstPoint = new MutablePoint(left, leftBottom); + this.q3.LastPoint = new MutablePoint(bottomLeft, bottom); + this.q3.RootPoint = new MutablePoint(bottomLeft, leftBottom); + + this.q4.FirstPoint = new MutablePoint(bottomRight, bottom); + this.q4.LastPoint = new MutablePoint(right, rightBottom); + this.q4.RootPoint = new MutablePoint(bottomRight, rightBottom); + } + } + + /* + // ****************************************************************** + // For usage of Parallel func, I highly suggest: Stephen Toub: Patterns of parallel programming ==> Just Awsome !!! + // But its only my own fault if I'm not using it at its full potential... + private void SetQuadrantLimitsUsingAllThreads() + { + MutablePoint pt = this._listOfPoint.First(); + _limit = new Limit(pt); + + int coreCount = Environment.ProcessorCount; + + Task[] tasks = new Task[coreCount]; + for (int n = 0; n < tasks.Length; n++) + { + int nLocal = n; // Prevent Lambda internal closure error. + tasks[n] = Task.Factory.StartNew(() => + { + Limit limit = _limit.Copy(); + FindLimits(_listOfPoint, nLocal, coreCount, limit); + AggregateLimits(limit); + }); + } + Task.WaitAll(tasks); + + _q1.FirstPoint = _limit.Q1Right; + _q1.LastPoint = _limit.Q1Top; + _q2.FirstPoint = _limit.Q2Top; + _q2.LastPoint = _limit.Q2Left; + _q3.FirstPoint = _limit.Q3Left; + _q3.LastPoint = _limit.Q3Bottom; + _q4.FirstPoint = _limit.Q4Bottom; + _q4.LastPoint = _limit.Q4Right; + + _q1.RootPoint = new MutablePoint(_q1.LastPoint.X, _q1.FirstPoint.Y); + _q2.RootPoint = new MutablePoint(_q2.FirstPoint.X, _q2.LastPoint.Y); + _q3.RootPoint = new MutablePoint(_q3.LastPoint.X, _q3.FirstPoint.Y); + _q4.RootPoint = new MutablePoint(_q4.FirstPoint.X, _q4.LastPoint.Y); + } + */ + + /// + /// Find the limits + /// + /// a list of points + /// the start point + /// the offset + /// the limit + /// A limit + private Limit FindLimits(MutablePoint[] listOfPoint, int start, int offset, Limit limit) + { + for (int index = start; index < listOfPoint.Length; index += offset) + { + MutablePoint pt = listOfPoint[index]; + + double x = pt.X; + double y = pt.Y; + + // Top + if (y >= limit.Q2Top.Y) + { + if (y == limit.Q2Top.Y) + { + // Special + if (y == limit.Q1Top.Y) + { + if (x < limit.Q2Top.X) + { + limit.Q2Top.X = x; + } + else if (x > limit.Q1Top.X) + { + limit.Q1Top.X = x; + } + } + else + { + if (x < limit.Q2Top.X) + { + limit.Q1Top.X = limit.Q2Top.X; + limit.Q1Top.Y = limit.Q2Top.Y; + + limit.Q2Top.X = x; + } + else if (x > limit.Q1Top.X) + { + limit.Q1Top.X = x; + limit.Q1Top.Y = y; + } + } + } + else + { + limit.Q2Top.X = x; + limit.Q2Top.Y = y; + } + } + + // Bottom + if (y <= limit.Q3Bottom.Y) + { + if (y == limit.Q3Bottom.Y) + { + // Special + if (y == limit.Q4Bottom.Y) + { + if (x < limit.Q3Bottom.X) + { + limit.Q3Bottom.X = x; + } + else if (x > limit.Q4Bottom.X) + { + limit.Q4Bottom.X = x; + } + } + else + { + if (x < limit.Q3Bottom.X) + { + limit.Q4Bottom.X = limit.Q3Bottom.X; + limit.Q4Bottom.Y = limit.Q3Bottom.Y; + + limit.Q3Bottom.X = x; + } + else if (x > limit.Q3Bottom.X) + { + limit.Q4Bottom.X = x; + limit.Q4Bottom.Y = y; + } + } + } + else + { + limit.Q3Bottom.X = x; + limit.Q3Bottom.Y = y; + } + } + + // Right + if (x >= limit.Q4Right.X) + { + if (x == limit.Q4Right.X) + { + // Special + if (x == limit.Q1Right.X) + { + if (y < limit.Q4Right.Y) + { + limit.Q4Right.Y = y; + } + else if (y > limit.Q1Right.Y) + { + limit.Q1Right.Y = y; + } + } + else + { + if (y < limit.Q4Right.Y) + { + limit.Q1Right.X = limit.Q4Right.X; + limit.Q1Right.Y = limit.Q4Right.Y; + + limit.Q4Right.Y = y; + } + else if (y > limit.Q1Right.Y) + { + limit.Q1Right.X = x; + limit.Q1Right.Y = y; + } + } + } + else + { + limit.Q4Right.X = x; + limit.Q4Right.Y = y; + } + } + + // Left + if (x <= limit.Q3Left.X) + { + if (x == limit.Q3Left.X) + { + // Special + if (x == limit.Q2Left.X) + { + if (y < limit.Q3Left.Y) + { + limit.Q3Left.Y = y; + } + else if (y > limit.Q2Left.Y) + { + limit.Q2Left.Y = y; + } + } + else + { + if (y < limit.Q3Left.Y) + { + limit.Q2Left.X = limit.Q3Left.X; + limit.Q2Left.Y = limit.Q3Left.Y; + + limit.Q3Left.Y = y; + } + else if (y > limit.Q2Left.Y) + { + limit.Q2Left.X = x; + limit.Q2Left.Y = y; + } + } + } + else + { + limit.Q3Left.X = x; + limit.Q3Left.Y = y; + } + } + + if (limit.Q2Left.X != limit.Q3Left.X) + { + limit.Q2Left.X = limit.Q3Left.X; + limit.Q2Left.Y = limit.Q3Left.Y; + } + + if (limit.Q1Right.X != limit.Q4Right.X) + { + limit.Q1Right.X = limit.Q4Right.X; + limit.Q1Right.Y = limit.Q4Right.Y; + } + + if (limit.Q1Top.Y != limit.Q2Top.Y) + { + limit.Q1Top.X = limit.Q2Top.X; + limit.Q1Top.Y = limit.Q2Top.Y; + } + + if (limit.Q4Bottom.Y != limit.Q3Bottom.Y) + { + limit.Q4Bottom.X = limit.Q3Bottom.X; + limit.Q4Bottom.Y = limit.Q3Bottom.Y; + } + } + + return limit; + } + + /// + /// Find limits + /// + /// a point + /// a limit + /// The found limit + private Limit FindLimits(MutablePoint point, Limit limit) + { + double x = point.X; + double y = point.Y; + + // Top + if (y >= limit.Q2Top.Y) + { + if (y == limit.Q2Top.Y) + { + // Special + if (y == limit.Q1Top.Y) + { + if (x < limit.Q2Top.X) + { + limit.Q2Top.X = x; + } + else if (x > limit.Q1Top.X) + { + limit.Q1Top.X = x; + } + } + else + { + if (x < limit.Q2Top.X) + { + limit.Q1Top.X = limit.Q2Top.X; + limit.Q1Top.Y = limit.Q2Top.Y; + + limit.Q2Top.X = x; + } + else if (x > limit.Q1Top.X) + { + limit.Q1Top.X = x; + limit.Q1Top.Y = y; + } + } + } + else + { + limit.Q2Top.X = x; + limit.Q2Top.Y = y; + } + } + + // Bottom + if (y <= limit.Q3Bottom.Y) + { + if (y == limit.Q3Bottom.Y) + { + // Special + if (y == limit.Q4Bottom.Y) + { + if (x < limit.Q3Bottom.X) + { + limit.Q3Bottom.X = x; + } + else if (x > limit.Q4Bottom.X) + { + limit.Q4Bottom.X = x; + } + } + else + { + if (x < limit.Q3Bottom.X) + { + limit.Q4Bottom.X = limit.Q3Bottom.X; + limit.Q4Bottom.Y = limit.Q3Bottom.Y; + + limit.Q3Bottom.X = x; + } + else if (x > limit.Q3Bottom.X) + { + limit.Q4Bottom.X = x; + limit.Q4Bottom.Y = y; + } + } + } + else + { + limit.Q3Bottom.X = x; + limit.Q3Bottom.Y = y; + } + } + + // Right + if (x >= limit.Q4Right.X) + { + if (x == limit.Q4Right.X) + { + // Special + if (x == limit.Q1Right.X) + { + if (y < limit.Q4Right.Y) + { + limit.Q4Right.Y = y; + } + else if (y > limit.Q1Right.Y) + { + limit.Q1Right.Y = y; + } + } + else + { + if (y < limit.Q4Right.Y) + { + limit.Q1Right.X = limit.Q4Right.X; + limit.Q1Right.Y = limit.Q4Right.Y; + + limit.Q4Right.Y = y; + } + else if (y > limit.Q1Right.Y) + { + limit.Q1Right.X = x; + limit.Q1Right.Y = y; + } + } + } + else + { + limit.Q4Right.X = x; + limit.Q4Right.Y = y; + } + } + + // Left + if (x <= limit.Q3Left.X) + { + if (x == limit.Q3Left.X) + { + // Special + if (x == limit.Q2Left.X) + { + if (y < limit.Q3Left.Y) + { + limit.Q3Left.Y = y; + } + else if (y > limit.Q2Left.Y) + { + limit.Q2Left.Y = y; + } + } + else + { + if (y < limit.Q3Left.Y) + { + limit.Q2Left.X = limit.Q3Left.X; + limit.Q2Left.Y = limit.Q3Left.Y; + + limit.Q3Left.Y = y; + } + else if (y > limit.Q2Left.Y) + { + limit.Q2Left.X = x; + limit.Q2Left.Y = y; + } + } + } + else + { + limit.Q3Left.X = x; + limit.Q3Left.Y = y; + } + } + + if (limit.Q2Left.X != limit.Q3Left.X) + { + limit.Q2Left.X = limit.Q3Left.X; + limit.Q2Left.Y = limit.Q3Left.Y; + } + + if (limit.Q1Right.X != limit.Q4Right.X) + { + limit.Q1Right.X = limit.Q4Right.X; + limit.Q1Right.Y = limit.Q4Right.Y; + } + + if (limit.Q1Top.Y != limit.Q2Top.Y) + { + limit.Q1Top.X = limit.Q2Top.X; + limit.Q1Top.Y = limit.Q2Top.Y; + } + + if (limit.Q4Bottom.Y != limit.Q3Bottom.Y) + { + limit.Q4Bottom.X = limit.Q3Bottom.X; + limit.Q4Bottom.Y = limit.Q3Bottom.Y; + } + + return limit; + } + + /// + /// Set aggregate limits + /// + /// A limit + private void AggregateLimits(Limit limit) + { + lock (this.findLimitFinalLock) + { + if (limit.Q1Right.X >= this.limit.Q1Right.X) + { + if (limit.Q1Right.X == this.limit.Q1Right.X) + { + if (limit.Q1Right.Y > this.limit.Q1Right.Y) + { + this.limit.Q1Right = limit.Q1Right; + } + } + else + { + this.limit.Q1Right = limit.Q1Right; + } + } + + if (limit.Q4Right.X > this.limit.Q4Right.X) + { + if (limit.Q4Right.X == this.limit.Q4Right.X) + { + if (limit.Q4Right.Y < this.limit.Q4Right.Y) + { + this.limit.Q4Right = limit.Q4Right; + } + } + else + { + this.limit.Q4Right = limit.Q4Right; + } + } + + if (limit.Q2Left.X < this.limit.Q2Left.X) + { + if (limit.Q2Left.X == this.limit.Q2Left.X) + { + if (limit.Q2Left.Y > this.limit.Q2Left.Y) + { + this.limit.Q2Left = limit.Q2Left; + } + } + else + { + this.limit.Q2Left = limit.Q2Left; + } + } + + if (limit.Q3Left.X < this.limit.Q3Left.X) + { + if (limit.Q3Left.X == this.limit.Q3Left.X) + { + if (limit.Q3Left.Y > this.limit.Q3Left.Y) + { + this.limit.Q3Left = limit.Q3Left; + } + } + else + { + this.limit.Q3Left = limit.Q3Left; + } + } + + if (limit.Q1Top.Y > this.limit.Q1Top.Y) + { + if (limit.Q1Top.Y == this.limit.Q1Top.Y) + { + if (limit.Q1Top.X > this.limit.Q1Top.X) + { + this.limit.Q1Top = limit.Q1Top; + } + } + else + { + this.limit.Q1Top = limit.Q1Top; + } + } + + if (limit.Q2Top.Y > this.limit.Q2Top.Y) + { + if (limit.Q2Top.Y == this.limit.Q2Top.Y) + { + if (limit.Q2Top.X < this.limit.Q2Top.X) + { + this.limit.Q2Top = limit.Q2Top; + } + } + else + { + this.limit.Q2Top = limit.Q2Top; + } + } + + if (limit.Q3Bottom.Y < this.limit.Q3Bottom.Y) + { + if (limit.Q3Bottom.Y == this.limit.Q3Bottom.Y) + { + if (limit.Q3Bottom.X < this.limit.Q3Bottom.X) + { + this.limit.Q3Bottom = limit.Q3Bottom; + } + } + else + { + this.limit.Q3Bottom = limit.Q3Bottom; + } + } + + if (limit.Q4Bottom.Y < this.limit.Q4Bottom.Y) + { + if (limit.Q4Bottom.Y == this.limit.Q4Bottom.Y) + { + if (limit.Q4Bottom.X > this.limit.Q4Bottom.X) + { + this.limit.Q4Bottom = limit.Q4Bottom; + } + } + else + { + this.limit.Q4Bottom = limit.Q4Bottom; + } + } + } + } + + /// + /// Checks if the data is empty + /// + /// True if no data + private bool IsZeroData() + { + return this.listOfPoint == null || !this.listOfPoint.Any(); + } + + /// + /// Returns true if the quadrants are disjointed + /// + /// True if Disjoint; otherwise false + private bool IsQuadrantAreDisjoint() + { + if (IsPointToTheRightOfOthers(this.q1.FirstPoint, this.q1.LastPoint, this.q3.RootPoint)) + { + return false; + } + + if (IsPointToTheRightOfOthers(this.q2.FirstPoint, this.q2.LastPoint, this.q4.RootPoint)) + { + return false; + } + + if (IsPointToTheRightOfOthers(this.q3.FirstPoint, this.q3.LastPoint, this.q1.RootPoint)) + { + return false; + } + + if (IsPointToTheRightOfOthers(this.q4.FirstPoint, this.q4.LastPoint, this.q2.RootPoint)) + { + return false; + } + + return true; + } + + /// + /// Initializes the class + /// + /// a list of points + /// a bool indicating if the graph should be closed + private void Init(MutablePoint[] listOfPoint, bool shouldCloseTheGraph) + { + this.listOfPoint = listOfPoint; + this.shouldCloseTheGraph = shouldCloseTheGraph; + + this.q1 = new QuadrantSpecific1(this.listOfPoint, (a, b) => (a.X > b.X) ? -1 : (a.X < b.X) ? 1 : 0); + this.q2 = new QuadrantSpecific2(this.listOfPoint, (a, b) => (a.X > b.X) ? -1 : (a.X < b.X) ? 1 : 0); + this.q3 = new QuadrantSpecific3(this.listOfPoint, (a, b) => (a.X < b.X) ? -1 : (a.X > b.X) ? 1 : 0); + this.q4 = new QuadrantSpecific4(this.listOfPoint, (a, b) => (a.X < b.X) ? -1 : (a.X > b.X) ? 1 : 0); + } + + [SuppressMessage("Microsoft.StyleCop.CSharp.DocumentationRules", "SA1600:ElementsMustBeDocumented", Justification = "Simple struct clearly named")] + private struct Limit + { + internal MutablePoint Q1Top; + internal MutablePoint Q2Top; + internal MutablePoint Q2Left; + internal MutablePoint Q3Left; + internal MutablePoint Q3Bottom; + internal MutablePoint Q4Bottom; + internal MutablePoint Q4Right; + internal MutablePoint Q1Right; + + /// + /// Initializes a new instance of the struct. + /// + /// a point + internal Limit(MutablePoint point) + { + this.Q1Top = point; + this.Q2Top = point; + this.Q2Left = point; + this.Q3Left = point; + this.Q3Bottom = point; + this.Q4Bottom = point; + this.Q4Right = point; + this.Q1Right = point; + } + + /// + /// Copies a limit + /// + /// a new limit + internal Limit Copy() + { + Limit limit = new Limit + { + Q1Top = this.Q1Top, + Q2Top = this.Q2Top, + Q2Left = this.Q2Left, + Q3Left = this.Q3Left, + Q3Bottom = this.Q3Bottom, + Q4Bottom = this.Q4Bottom, + Q4Right = this.Q4Right, + Q1Right = this.Q1Right + }; + + return limit; + } + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs b/src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs new file mode 100644 index 00000000..b7f79c11 --- /dev/null +++ b/src/Numerics/Spatial/Internal/ConvexHull/MutablePoint.cs @@ -0,0 +1,65 @@ +using System; + +namespace MathNet.Numerics.Spatial.Internal.ConvexHull +{ + /// + /// Provides a working surface for points for ConvextHull - do not use otherwise + /// + internal struct MutablePoint : IEquatable + { + /// + /// Initializes a new instance of the struct. + /// + /// The x value + /// The y value + internal MutablePoint(double x, double y) + { + this.X = x; + this.Y = y; + } + + /// + /// Gets or sets the X value + /// + internal double X { get; set; } + + /// + /// Gets or sets the Y Value + /// + internal double Y { get; set; } + + /// + /// Returns a value that indicates whether each pair of elements in two specified points is equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are the same; otherwise false. + public static bool operator ==(MutablePoint p1, MutablePoint p2) => p1.Equals(p2); + + /// + /// Returns a value that indicates whether any pair of elements in two specified points is not equal. + /// + /// The first point to compare + /// The second point to compare + /// True if the points are different; otherwise false. + public static bool operator !=(MutablePoint p1, MutablePoint p2) => !p1.Equals(p2); + + /// + public bool Equals(MutablePoint other) + { + return this.X == other.X && this.Y == other.Y; + } + + /// + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + /// + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs b/src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs new file mode 100644 index 00000000..1eb06e05 --- /dev/null +++ b/src/Numerics/Spatial/Internal/ConvexHull/QComparer.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace MathNet.Numerics.Spatial.Internal.ConvexHull +{ + /// + /// A comparer for convex hull's use of an Avl tree + /// + internal class QComparer : IComparer + { + /// + /// A function to compare with + /// + private readonly Func comparer; + + /// + /// Initializes a new instance of the class. + /// + /// a function to use for comparing + public QComparer(Func comparer) + { + this.comparer = comparer; + } + + /// + /// Compares two points using the provided function + /// + /// the first point + /// the second point + /// A value of -1 if less than, a value of 1 is greater than; otherwise a value of 0 + public int Compare(MutablePoint pt1, MutablePoint pt2) + { + return this.comparer(pt1, pt2); + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/Quadrant.cs b/src/Numerics/Spatial/Internal/ConvexHull/Quadrant.cs new file mode 100644 index 00000000..bdaa5bf8 --- /dev/null +++ b/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 +{ + /// + /// An avl node for convex hull representing a quadrant + /// + [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "By design")] + [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1306:FieldNamesMustBeginWithLowerCaseLetter", Justification = "Reviewed.")] + internal abstract class Quadrant : AvlTreeSet + { + /// + /// The first point + /// + public MutablePoint FirstPoint; + + /// + /// The last point + /// + public MutablePoint LastPoint; + + /// + /// The root point + /// + public MutablePoint RootPoint; + + /// + /// The current node + /// + protected AvlNode CurrentNode = null; + + /// + /// A list of points + /// + protected MutablePoint[] ListOfPoint; + + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// Comparer is only used to add the second point (the last point, which is compared against the first one). + internal Quadrant(MutablePoint[] listOfPoint, IComparer comparer) + : base(comparer) + { + this.ListOfPoint = listOfPoint; + } + + /// + /// An enum for the side + /// + internal enum Side + { + /// + /// Unknown side + /// + Unknown = 0, + + /// + /// left side + /// + Left = 1, + + /// + /// right side + /// + Right = 2 + } + + /// + /// Prepares the node + /// + 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); + } + + /// + /// Tell if should try to add and where. -1 ==> Should not add. + /// + /// A point + internal abstract void ProcessPoint(ref MutablePoint point); + + /// + /// 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). + /// + protected abstract void SetQuadrantLimits(); + + /// + /// To know if to the right. It is meaningful when p1 is first and p2 is next. + /// + /// The first point + /// The second point + /// The point to check + /// 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. + //// [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; + } + + /// + /// Checks if point should be in quadrant + /// + /// a point + /// True if it is a good quadrant for the point; otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected abstract bool IsGoodQuadrantForPoint(MutablePoint pt); + + /// + /// 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. + /// + /// Previous point + /// New point + /// Next point + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1002:SemicolonsMustBeSpacedCorrectly", Justification = "Reviewed.")] + protected void InvalidateNeighbors(AvlNode pointPrevious, AvlNode pointNew, AvlNode pointNext) + { + bool invalidPoint; + + if (pointPrevious != null) + { + AvlNode 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 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(); + } + } + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific1.cs b/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific1.cs new file mode 100644 index 00000000..1f1af4f5 --- /dev/null +++ b/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 +{ + /// + /// Class to process quadrant 1 + /// + internal class QuadrantSpecific1 : Quadrant + { + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// a comparer + internal QuadrantSpecific1(MutablePoint[] listOfPoint, Func comparer) + : base(listOfPoint, new QComparer(comparer)) + { + } + + /// + /// Check if we can quickly reject a point + /// + /// a point + /// a point on the hull + /// True if can quickly reject; otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull) + { + return point.X <= pointHull.X && point.Y <= pointHull.Y; + } + + /// + /// Iterate over each points to see if we can add it has a ConvexHull point. + /// It is specific by Quadrant to improve efficiency. + /// + /// a point + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override void ProcessPoint(ref MutablePoint point) + { + this.CurrentNode = this.Root; + AvlNode currentPrevious = null; + AvlNode 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 newNode = new AvlNode(); + 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); + } + } + } + + /// + 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); + } + + /// + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool IsGoodQuadrantForPoint(MutablePoint pt) + { + if (pt.X > this.RootPoint.X && pt.Y > this.RootPoint.Y) + { + return true; + } + + return false; + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific2.cs b/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific2.cs new file mode 100644 index 00000000..fd705c2d --- /dev/null +++ b/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 +{ + /// + /// Class to process quadrant 2 + /// + internal class QuadrantSpecific2 : Quadrant + { + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// a comparer + internal QuadrantSpecific2(MutablePoint[] listOfPoint, Func comparer) + : base(listOfPoint, new QComparer(comparer)) + { + } + + /// + /// Check if we can quickly reject a point + /// + /// a point + /// a point on the hull + /// True if can quickly reject; otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull) + { + return point.X >= pointHull.X && point.Y <= pointHull.Y; + } + + /// + /// Iterate over each points to see if we can add it has a ConvexHull point. + /// It is specific by Quadrant to improve efficiency. + /// + /// a point + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override void ProcessPoint(ref MutablePoint point) + { + this.CurrentNode = this.Root; + AvlNode currentPrevious = null; + AvlNode 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 newNode = new AvlNode(); + 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; + } + } + + /// + 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); + } + + /// + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool IsGoodQuadrantForPoint(MutablePoint pt) + { + if (pt.X < this.RootPoint.X && pt.Y > this.RootPoint.Y) + { + return true; + } + + return false; + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific3.cs b/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific3.cs new file mode 100644 index 00000000..c8189e0f --- /dev/null +++ b/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 +{ + /// + /// Class to process quadrant 3 + /// + internal class QuadrantSpecific3 : Quadrant + { + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// a comparer + internal QuadrantSpecific3(MutablePoint[] listOfPoint, Func comparer) + : base(listOfPoint, new QComparer(comparer)) + { + } + + /// + /// Check if we can quickly reject a point + /// + /// a point + /// a point on the hull + /// True if can quickly reject; otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull) + { + return point.X >= pointHull.X && point.Y >= pointHull.Y; + } + + /// + /// Iterate over each points to see if we can add it has a ConvexHull point. + /// It is specific by Quadrant to improve efficiency. + /// + /// a point + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override void ProcessPoint(ref MutablePoint point) + { + this.CurrentNode = this.Root; + AvlNode currentPrevious = null; + AvlNode 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 newNode = new AvlNode(); + 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; + } + + /// + 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); + } + + /// + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool IsGoodQuadrantForPoint(MutablePoint pt) + { + if (pt.X < this.RootPoint.X && pt.Y < this.RootPoint.Y) + { + return true; + } + + return false; + } + } +} diff --git a/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific4.cs b/src/Numerics/Spatial/Internal/ConvexHull/QuadrantSpecific4.cs new file mode 100644 index 00000000..da05ee7a --- /dev/null +++ b/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 +{ + /// + /// Class to process quadrant 4 + /// + internal class QuadrantSpecific4 : Quadrant + { + /// + /// Initializes a new instance of the class. + /// + /// a list of points + /// a comparer + internal QuadrantSpecific4(MutablePoint[] listOfPoint, Func comparer) + : base(listOfPoint, new QComparer(comparer)) + { + } + + /// + /// Check if we can quickly reject a point + /// + /// a point + /// a point on the hull + /// True if can quickly reject; otherwise false + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool CanQuickReject(ref MutablePoint point, ref MutablePoint pointHull) + { + return point.X <= pointHull.X && point.Y >= pointHull.Y; + } + + /// + /// Iterate over each points to see if we can add it has a ConvexHull point. + /// It is specific by Quadrant to improve efficiency. + /// + /// a point + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal override void ProcessPoint(ref MutablePoint point) + { + this.CurrentNode = this.Root; + AvlNode currentPrevious = null; + AvlNode 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 newNode = new AvlNode(); + 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; + } + + /// + 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); + } + + /// + //// [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected override bool IsGoodQuadrantForPoint(MutablePoint pt) + { + if (pt.X > this.RootPoint.X && pt.Y < this.RootPoint.Y) + { + return true; + } + + return false; + } + } +} diff --git a/src/Numerics/Spatial/Internal/HashCode.cs b/src/Numerics/Spatial/Internal/HashCode.cs new file mode 100644 index 00000000..26fc67cc --- /dev/null +++ b/src/Numerics/Spatial/Internal/HashCode.cs @@ -0,0 +1,497 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +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 + + /// + /// Generates a hashcode + /// + internal struct HashCode + { + private static readonly uint s_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 uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static uint GenerateGlobalSeed() + { + // NOTE: Modified from original unsafe implementation. Might be better to use a more random seed. + System.Random r = new System.Random(); + int seed = r.Next(); + return unchecked((uint)seed); + } + + public static int Combine(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[] 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(List 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(IEnumerable 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 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 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 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 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 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 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 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; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Rol(uint value, int count) + => (value << count) | (value >> (32 - count)); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + hash += input * Prime2; + hash = Rol(hash, 13); + hash *= Prime1; + return hash; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + hash += queuedValue * Prime3; + return Rol(hash, 17) * Prime4; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + 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 s_seed + Prime5; + } + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer comparer) + { + Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0)); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + var val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint previousLength = _length++; + uint position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + _queue1 = val; + else if (position == 1) + _queue2 = val; + else if (position == 2) + _queue3 = val; + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + uint length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + uint position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable 0809 + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => throw new NotSupportedException("GetHasCode not supported"); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => throw new NotSupportedException("Equals is not supported"); +#pragma warning restore 0809 + } +} diff --git a/src/Numerics/Spatial/Internal/ImmutableList.cs b/src/Numerics/Spatial/Internal/ImmutableList.cs new file mode 100644 index 00000000..c0221fdd --- /dev/null +++ b/src/Numerics/Spatial/Internal/ImmutableList.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; + +namespace MathNet.Numerics.Spatial.Internal +{ + /// + /// Internal implementation of an immutable list + /// + internal static class ImmutableList + { + /// + /// Factory Construction + /// + /// The list type + /// A list of items to initialize with + /// An immutable list + internal static ImmutableList Create(IEnumerable data) + { + return ImmutableList.Empty.AddRange(data.AsCollection()); + } + + /// + /// Gets the passed source as a collection + /// + /// the list type + /// the list source + /// A collection + private static ICollection AsCollection(this IEnumerable source) + { + if (source is ICollection collection) + { + return collection; + } + + if (source is ImmutableList list) + { + return list.GetRawData(); + } + + return source.ToList(); + } + } +} diff --git a/src/Numerics/Spatial/Internal/ImmutableListOfT.cs b/src/Numerics/Spatial/Internal/ImmutableListOfT.cs new file mode 100644 index 00000000..8790330b --- /dev/null +++ b/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 +{ + /// + /// An internal implementation of ImmutableList + /// + /// A type for the list + internal sealed class ImmutableList : IEnumerable + { + /// + /// An empty list + /// + internal static readonly ImmutableList Empty = new ImmutableList(new T[0]); + + /// + /// The list data + /// + private readonly T[] data; + + /// + /// Initializes a new instance of the class. + /// + /// The data to initialize the list with + private ImmutableList(T[] data) + { + this.data = data; + } + + /// + /// Gets the number of items in the list + /// + internal int Count => this.data.Length; + + /// + /// An 0 based index into the list + /// + /// the index + /// A list item + internal T this[int index] => this.data[index]; + + /// + public IEnumerator GetEnumerator() => ((IList)this.data).GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => this.data.GetEnumerator(); + + /// + /// Adds an item to the list + /// + /// An item + /// A new list with the item added + [Pure] + internal ImmutableList 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(newData); + } + + /// + /// Adds a range of items to the list + /// + /// The items to add + /// A new list with the items added + [Pure] + internal ImmutableList AddRange(ICollection 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(newData); + } + + /// + /// Removes an item from the list + /// + /// The item to remove + /// A new list with the item removed + [Pure] + internal ImmutableList 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(newData); + } + + /// + /// An internal method to access the underlying data. To be used with care. + /// + /// The backing data array + internal T[] GetRawData() => this.data; + } +} diff --git a/src/Numerics/Spatial/Internal/Parser.cs b/src/Numerics/Spatial/Internal/Parser.cs new file mode 100644 index 00000000..bd744ea4 --- /dev/null +++ b/src/Numerics/Spatial/Internal/Parser.cs @@ -0,0 +1,63 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; +using MathNet.Numerics.Spatial.Euclidean3D; + +#pragma warning disable SA1600 // Elements must be documented +namespace MathNet.Numerics.Spatial.Internal +{ + [Obsolete("This should not have been public, will be removed in a future version. Made obsolete 2017-12-03")] + internal static class Parser + { + public const string SeparatorPattern = @" *[,;] *"; + public static readonly string DoublePattern = @"[+-]?\d*(?:[.,]\d+)?(?:[eE][+-]?\d+)?"; + public static readonly string Vector3DPattern = string.Format(@"^ *\(?(?{0}){1}(?{0}){1}(?{0})\)? *$", DoublePattern, SeparatorPattern); + public static readonly string Vector2DPattern = string.Format(@"^ *\(?(?{0}){1}(?{0})?\)? *$", DoublePattern, SeparatorPattern); + public static readonly string Item3DPattern = Vector3DPattern.Trim('^', '$'); + public static readonly string PlanePointVectorPattern = string.Format(@"^ *p: *{{(?

{0})}} *v: *{{(?{0})}} *$", Item3DPattern); + public static readonly string PlaneAbcdPattern = string.Format(@"^ *\(?(?{0}){1}(?{0}){1}(?{0}){1}(?{0})\)? *$", DoublePattern, SeparatorPattern); + + public static double ParseDouble(Group @group) + { + if (@group.Captures.Count != 1) + { + throw new ArgumentException("Expected single capture"); + } + + return ParseDouble(@group.Value); + } + + public static double ParseDouble(string s) + { + return double.Parse(s.Replace(',', '.'), CultureInfo.InvariantCulture); + } + + public static Plane3D ParsePlane(string s) + { + var match = Regex.Match(s, PlanePointVectorPattern); + if (match.Success) + { + var p = Point3D.Parse(match.Groups["p"].Value); + var uv = UnitVector3D.Parse(match.Groups["v"].Value); + return new Plane3D(p, uv); + } + + match = Regex.Match(s, PlaneAbcdPattern); + { + var a = ParseDouble(match.Groups["a"]); + var b = ParseDouble(match.Groups["b"]); + var c = ParseDouble(match.Groups["c"]); + var d = ParseDouble(match.Groups["d"]); + return new Plane3D(a, b, c, d); + } + } + + public static Ray3D ParseRay3D(string s) + { + var match = Regex.Match(s, PlanePointVectorPattern); + var p = Point3D.Parse(match.Groups["p"].Value); + var uv = UnitVector3D.Parse(match.Groups["v"].Value); + return new Ray3D(p, uv); + } + } +} diff --git a/src/Numerics/Spatial/Internal/Text.cs b/src/Numerics/Spatial/Internal/Text.cs new file mode 100644 index 00000000..8f5b3e15 --- /dev/null +++ b/src/Numerics/Spatial/Internal/Text.cs @@ -0,0 +1,429 @@ +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace MathNet.Numerics.Spatial.Internal +{ + ///

+ /// Internal text processing + /// + internal static class Text + { + /// + /// regex pattern with period + /// + private const string DoublePatternPointProvider = "[+-]?\\d*(?:[.]\\d+)?(?:[eE][+-]?\\d+)?"; + + /// + /// regex pattern with comma + /// + private const string DoublePatternCommaProvider = "[+-]?\\d*(?:[,]\\d+)?(?:[eE][+-]?\\d+)?"; + + /// + /// Separator with period + /// + private const string SeparatorPatternPointProvider = " ?[,;]?( |\u00A0)?"; + + /// + /// Separator with comma + /// + private const string SeparatorPatternCommaProvider = " ?[;]?( |\u00A0)?"; + + /// + /// Attempts to parse a string into x, y coordinates + /// + /// a string + /// a format provider + /// The x value + /// The y value + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to parse a string into x, y, z coordinates + /// + /// A string + /// A format provider + /// The x value + /// The y value + /// The z value + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to parse a string into an Angle + /// + /// A string + /// A format provider + /// An angle + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to parse a double + /// + /// A string + /// A format provider + /// A double + /// True if successful; otherwise false + 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); + } + + /// + /// A class providing regex matching for 2D values + /// + private static class Regex2D + { + /// + /// The pattern for this regex + /// + private const string Pattern2D = "^ *\\(?(?{0}){1}(?{0})\\)? *$"; + + /// + /// The standard options + /// + private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline; + + /// + /// A regex containing a . + /// + private static readonly Regex Point = new Regex( + string.Format(Pattern2D, DoublePatternPointProvider, SeparatorPatternPointProvider), + RegexOptions); + + /// + /// a regex containing a , + /// + private static readonly Regex Comma = new Regex( + string.Format(Pattern2D, DoublePatternCommaProvider, SeparatorPatternCommaProvider), + RegexOptions); + + /// + /// Attempts to match a string + /// + /// a string + /// a format provider + /// the match + /// True if successful; Otherwise false + 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; + } + } + + /// + /// A class providing regex matching for 3D values + /// + private static class Regex3D + { + /// + /// The pattern for this regex + /// + private const string Pattern3D = "^ *\\(?(?{0}){1}(?{0}){1}(?{0})\\)? *$"; + + /// + /// The standard options + /// + private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline; + + /// + /// A regex containing a . + /// + private static readonly Regex Point = new Regex( + string.Format(Pattern3D, DoublePatternPointProvider, SeparatorPatternPointProvider), + RegexOptions); + + /// + /// A regex containing a , + /// + private static readonly Regex Comma = new Regex( + string.Format(Pattern3D, DoublePatternCommaProvider, SeparatorPatternCommaProvider), + RegexOptions); + + /// + /// Attempts to match a string + /// + /// a string + /// a format provider + /// the match + /// True if successful; Otherwise false + 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; + } + } + + /// + /// A class providing regex matching for angle values + /// + private static class RegexAngle + { + /// + /// A regex for radians angles + /// + private const string RadiansPattern = "^(?{0})( |\u00A0)?(°|rad|radians) *$"; + + /// + /// A regex for degrees angles + /// + private const string DegreesPattern = "^(?{0})( |\u00A0)?(°|deg|degrees) *$"; + + /// + /// Standard regex options + /// + private const RegexOptions RegexOptions = System.Text.RegularExpressions.RegexOptions.ExplicitCapture | System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.Singleline | System.Text.RegularExpressions.RegexOptions.IgnoreCase; + + /// + /// Radians with a point + /// + private static readonly Regex RadiansPoint = new Regex( + string.Format(RadiansPattern, DoublePatternPointProvider), + RegexOptions); + + /// + /// Radians with a comma + /// + private static readonly Regex RadiansComma = new Regex( + string.Format(RadiansPattern, DoublePatternCommaProvider), + RegexOptions); + + /// + /// Degrees with a point + /// + private static readonly Regex DegreesPoint = new Regex( + string.Format(DegreesPattern, DoublePatternPointProvider), + RegexOptions); + + /// + /// Degrees with a comma + /// + private static readonly Regex DegreesComma = new Regex( + string.Format(DegreesPattern, DoublePatternCommaProvider), + RegexOptions); + + /// + /// Attempts to match Degrees + /// + /// a string + /// a format provider + /// a double + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to match Radians + /// + /// a string + /// a format provider + /// a double + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to match Radians with either . or , separators + /// + /// a string + /// a format provider + /// a list of matches + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to match Degrees with either . or , separators + /// + /// a string + /// a format provider + /// a list of matches + /// True if successful; otherwise false + 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; + } + } + } +} diff --git a/src/Numerics/Spatial/Internal/XmlReaderExt.cs b/src/Numerics/Spatial/Internal/XmlReaderExt.cs new file mode 100644 index 00000000..8592d90a --- /dev/null +++ b/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 +{ + /// + /// An extension class for XmlReader + /// + internal static class XmlReaderExt + { + /// + /// Creates a default(T) and calls ReadXml(reader) on it. + /// + /// The type of the instance to read from the current position of the reader. + /// A + /// A new instance of {T} with values from + internal static T ReadElementAs(this XmlReader reader) + where T : struct, IXmlSerializable + { + if (reader == null) + { + throw new ArgumentNullException(nameof(reader)); + } + + var instance = default(T); + instance.ReadXml(reader); + return instance; + } + + /// + /// Reads the attribute named if it exists on the current element. + /// This is not a proper try method as it checks if is null and throws. + /// + /// A + /// The name of the attribute to read the value of. + /// The value read from + /// True if the attribute was found. + 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; + } + + /// + /// Reads the values of the elements named and if they exist on the current element. + /// This is not a proper try method as it checks if is null and throws. + /// + /// + /// Calling this method has side effects as it changes the position of the reader. + /// + /// A + /// The local name of the element to read value from. + /// The value read from + /// True if both elements were found. + 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; + } + + /// + /// Reads the values of the elements named and if they exist on the current element. + /// This is not a proper try method as it checks if is null and throws. + /// + /// + /// Calling this method has side effects as it changes the position of the reader. + /// + /// A + /// The local name of the x element. + /// The local name of the y element. + /// The x value read from + /// The y value read from + /// True if both elements were found. + 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; + } + + /// + /// Reads the values of the elements named and if they exist on the current element. + /// This is not a proper try method as it checks if is null and throws. + /// + /// + /// Calling this method has side effects as it changes the position of the reader. + /// + /// A + /// The local name of the x element. + /// The local name of the y element. + /// The local name of the z element. + /// The x value read from + /// The y value read from + /// The z value read from + /// True if both elements were found. + 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; + } + + /// + /// Reads until first descendant + /// + /// An xml reader + /// True if successful; otherwise false + 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; + } + + /// + /// Attempts to read sibling elements as a pair of doubles + /// + /// an xml reader + /// The name of the x element + /// The name of the y element + /// The x value + /// The y value + /// True if successful; otherwise false + 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(); + } + } +} diff --git a/src/Numerics/Spatial/Internal/XmlWriterExt.cs b/src/Numerics/Spatial/Internal/XmlWriterExt.cs new file mode 100644 index 00000000..459224a2 --- /dev/null +++ b/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 +{ + /// + /// An extension class for XmlWriter + /// + internal static class XmlWriterExt + { + /// + /// Writes an element + /// + /// An Xml Writer + /// The element name + /// The value + internal static void WriteElement(this XmlWriter writer, string name, IXmlSerializable value) + { + writer.WriteStartElement(name); + value.WriteXml(writer); + writer.WriteEndElement(); + } + + /// + /// Writes an element + /// + /// An Xml Writer + /// The element name + /// The value + internal static void WriteElement(this XmlWriter writer, string name, double value) + { + writer.WriteStartElement(name); + writer.WriteValue(value); + writer.WriteEndElement(); + } + + /// + /// Writes an element + /// + /// An Xml Writer + /// The element name + /// The value + /// a format to apply to the value + 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(this XmlWriter writer, string name, T value) + { + writer.WriteStartAttribute(name); + writer.WriteValue(value); + writer.WriteEndAttribute(); + return writer; + } + } +} diff --git a/src/Numerics/paket.references b/src/Numerics/paket.references index 739f7d9a..9f996731 100644 --- a/src/Numerics/paket.references +++ b/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