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