From 10a71a4be7128c45bb16b89ac2c653e5b3cdcdbc Mon Sep 17 00:00:00 2001 From: guigang xie Date: Mon, 13 Feb 2023 09:33:11 +0800 Subject: [PATCH] 3d math and model source files --- .../src/Language/StringHelpers.vb | 15 +- .../Language/Value/ByRefValueExtensions.vb | 14 +- gr/Landscape/3DBuilder/XML/models.vb | 3 + gr/Landscape/PLY/Header.vb | 185 ++--- gr/Landscape/PLY/PointCloud.vb | 16 + gr/Landscape/PLY/SimplePlyWriter.vb | 56 ++ gr/Landscape/Wavefront/FileName.vb | 10 + gr/Landscape/Wavefront/OBJ.vb | 49 ++ gr/Landscape/Wavefront/TextParser.vb | 103 +++ gr/Microsoft.VisualBasic.Imaging.sln | 603 +++++++++------- .../Drawing3D/Math3D/Polyhedron/.gitignore | 3 + .../Drawing3D/Math3D/Polyhedron/.travis.yml | 11 + .../Drawing3D/Math3D/Polyhedron/LICENSE | 29 + .../Drawing3D/Math3D/Polyhedron/README.rst | 17 + .../Drawing3D/Math3D/Polyhedron/polygon.py | 120 ++++ .../Drawing3D/Math3D/Polyhedron/polyhedron.py | 372 ++++++++++ .../Math3D/Polyhedron/test_polygon.py | 146 ++++ .../Math3D/Polyhedron/test_polyhedron.py | 666 ++++++++++++++++++ .../Drawing3D/Point3D.vb | 21 +- .../test/hqx_test.vb | 2 +- .../test/objReaderTest.vb | 13 + .../test/test.vbproj | 32 +- .../test/test5.vbproj | 126 ---- 23 files changed, 2084 insertions(+), 528 deletions(-) create mode 100644 gr/Landscape/PLY/PointCloud.vb create mode 100644 gr/Landscape/PLY/SimplePlyWriter.vb create mode 100644 gr/Landscape/Wavefront/FileName.vb create mode 100644 gr/Landscape/Wavefront/OBJ.vb create mode 100644 gr/Landscape/Wavefront/TextParser.vb create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.gitignore create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.travis.yml create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/LICENSE create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/README.rst create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polygon.py create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polyhedron.py create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polygon.py create mode 100644 gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polyhedron.py create mode 100644 gr/Microsoft.VisualBasic.Imaging/test/objReaderTest.vb delete mode 100644 gr/Microsoft.VisualBasic.Imaging/test/test5.vbproj diff --git a/Microsoft.VisualBasic.Core/src/Language/StringHelpers.vb b/Microsoft.VisualBasic.Core/src/Language/StringHelpers.vb index fd2a5f15b..cfa92d4c2 100644 --- a/Microsoft.VisualBasic.Core/src/Language/StringHelpers.vb +++ b/Microsoft.VisualBasic.Core/src/Language/StringHelpers.vb @@ -65,8 +65,21 @@ Namespace Language ''' Public Module FormatHelpers + ''' + ''' Removes all leading and trailing occurrences of a set of characters specified + ''' in an array from the current string. + ''' + ''' + ''' An array of Unicode characters to remove, or null. + ''' + ''' The string that remains after all occurrences of the characters in the trimChars + ''' parameter are removed from the start and end of the current string. If trimChars + ''' is null or an empty array, white-space characters are removed instead. If no + ''' characters can be trimmed from the current instance, the method returns the current + ''' instance unchanged. + ''' - Public Function Trim(str As Value(Of String), c As Char()) As String + Public Function Trim(str As Value(Of String), ParamArray c As Char()) As String If str Is Nothing OrElse str.Value Is Nothing Then Return "" Else diff --git a/Microsoft.VisualBasic.Core/src/Language/Value/ByRefValueExtensions.vb b/Microsoft.VisualBasic.Core/src/Language/Value/ByRefValueExtensions.vb index a23ef1f9f..ecfc0e642 100644 --- a/Microsoft.VisualBasic.Core/src/Language/Value/ByRefValueExtensions.vb +++ b/Microsoft.VisualBasic.Core/src/Language/Value/ByRefValueExtensions.vb @@ -89,10 +89,22 @@ Namespace Language.Values Return list.Value.First End Function + ''' + ''' get the first char + ''' + ''' + ''' Public Function First(str As ByRefString) As Char - Return str.Value.First + If str Is Nothing OrElse + str.Value Is Nothing OrElse + str.Value.Length = 0 Then + + Return Nothing + Else + Return str.Value.First + End If End Function diff --git a/gr/Landscape/3DBuilder/XML/models.vb b/gr/Landscape/3DBuilder/XML/models.vb index 9e193c8bb..c0820cf99 100644 --- a/gr/Landscape/3DBuilder/XML/models.vb +++ b/gr/Landscape/3DBuilder/XML/models.vb @@ -119,6 +119,9 @@ Namespace Vendor_3mf.XML End Function End Class + ''' + ''' 3 vertex index to create a triangle of the surface model data + ''' Public Class triangle Public Property v1 As Integer diff --git a/gr/Landscape/PLY/Header.vb b/gr/Landscape/PLY/Header.vb index d91986136..d293d5f4f 100644 --- a/gr/Landscape/PLY/Header.vb +++ b/gr/Landscape/PLY/Header.vb @@ -1,73 +1,69 @@ #Region "Microsoft.VisualBasic::050e16ba84ef1932bc43eda21a2e551e, sciBASIC#\gr\Landscape\PLY\Header.vb" - ' Author: - ' - ' asuka (amethyst.asuka@gcmodeller.org) - ' xie (genetics@smrucc.org) - ' xieguigang (xie.guigang@live.com) - ' - ' Copyright (c) 2018 GPL3 Licensed - ' - ' - ' GNU GENERAL PUBLIC LICENSE (GPL3) - ' - ' - ' This program is free software: you can redistribute it and/or modify - ' it under the terms of the GNU General Public License as published by - ' the Free Software Foundation, either version 3 of the License, or - ' (at your option) any later version. - ' - ' This program is distributed in the hope that it will be useful, - ' but WITHOUT ANY WARRANTY; without even the implied warranty of - ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ' GNU General Public License for more details. - ' - ' You should have received a copy of the GNU General Public License - ' along with this program. If not, see . - - - - ' /********************************************************************************/ - - ' Summaries: - - - ' Code Statistics: - - ' Total Lines: 94 - ' Code Lines: 76 - ' Comment Lines: 0 - ' Blank Lines: 18 - ' File Size: 3.52 KB - - - ' Class Header - ' - ' Properties: comment, element_face, element_vertex, properties - ' - ' Sub: WriteAsciiText - ' - ' Module SimplePlyWriter - ' - ' Function: WriteAsciiText - ' - ' Class PointCloud - ' - ' Properties: color, intensity, x, y, z - ' - ' Function: ToString - ' - ' - ' /********************************************************************************/ +' Author: +' +' asuka (amethyst.asuka@gcmodeller.org) +' xie (genetics@smrucc.org) +' xieguigang (xie.guigang@live.com) +' +' Copyright (c) 2018 GPL3 Licensed +' +' +' GNU GENERAL PUBLIC LICENSE (GPL3) +' +' +' This program is free software: you can redistribute it and/or modify +' it under the terms of the GNU General Public License as published by +' the Free Software Foundation, either version 3 of the License, or +' (at your option) any later version. +' +' This program is distributed in the hope that it will be useful, +' but WITHOUT ANY WARRANTY; without even the implied warranty of +' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +' GNU General Public License for more details. +' +' You should have received a copy of the GNU General Public License +' along with this program. If not, see . + + + +' /********************************************************************************/ + +' Summaries: + + +' Code Statistics: + +' Total Lines: 94 +' Code Lines: 76 +' Comment Lines: 0 +' Blank Lines: 18 +' File Size: 3.52 KB + + +' Class Header +' +' Properties: comment, element_face, element_vertex, properties +' +' Sub: WriteAsciiText +' +' Module SimplePlyWriter +' +' Function: WriteAsciiText +' +' Class PointCloud +' +' Properties: color, intensity, x, y, z +' +' Function: ToString +' +' +' /********************************************************************************/ #End Region -Imports System.Drawing Imports System.IO -Imports System.Text Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel -Imports Microsoft.VisualBasic.ComponentModel.Ranges.Model -Imports Microsoft.VisualBasic.Imaging.Drawing2D.Colors Namespace Ply @@ -91,68 +87,5 @@ Namespace Ply Call file.WriteLine($"end_header") End Sub - - End Class - - Public Module SimplePlyWriter - - Public Function WriteAsciiText(pointCloud As IEnumerable(Of PointCloud), - buffer As Stream, - Optional colors As ScalerPalette = ScalerPalette.turbo, - Optional levels As Integer = 200) As Boolean - - Using file As New StreamWriter(buffer, Encoding.ASCII) With { - .AutoFlush = True, - .NewLine = vbLf - } - Dim vertex As PointCloud() = pointCloud.ToArray - Dim header As New Header With { - .comment = "Point Cloud Model", - .element_face = -1, - .element_vertex = vertex.Length, - .properties = { - New NamedValue(Of String)("x", "float"), - New NamedValue(Of String)("y", "float"), - New NamedValue(Of String)("z", "float"), - New NamedValue(Of String)("intensity", "float"), - New NamedValue(Of String)("color", "string") - } - } - Dim colorSet As String() = Designer _ - .GetColors(colors.Description, levels) _ - .Select(Function(c) c.ToHtmlColor) _ - .ToArray - Dim scale As New DoubleRange(From v As PointCloud In vertex Select v.intensity) - Dim i As Integer - Dim index As New DoubleRange(0, levels - 1) - Dim clr As String - - Call header.WriteAsciiText(file) - Call file.Flush() - - For Each point As PointCloud In vertex - i = scale.ScaleMapping(point.intensity, index) - clr = colorSet(i) - - Call file.WriteLine($"{point.x.ToString("F3")} {point.y.ToString("F3")} {point.z.ToString("F3")} {point.intensity} {clr}") - Next - End Using - - Return True - End Function - End Module - - Public Class PointCloud - - Public Property x As Double - Public Property y As Double - Public Property z As Double - Public Property color As String - Public Property intensity As Double - - Public Overrides Function ToString() As String - Return $"[{x},{y},{z}] {intensity}" - End Function - End Class End Namespace diff --git a/gr/Landscape/PLY/PointCloud.vb b/gr/Landscape/PLY/PointCloud.vb new file mode 100644 index 000000000..833a8ad61 --- /dev/null +++ b/gr/Landscape/PLY/PointCloud.vb @@ -0,0 +1,16 @@ +Namespace Ply + + Public Class PointCloud + + Public Property x As Double + Public Property y As Double + Public Property z As Double + Public Property color As String + Public Property intensity As Double + + Public Overrides Function ToString() As String + Return $"[{x},{y},{z}] {intensity}" + End Function + + End Class +End Namespace \ No newline at end of file diff --git a/gr/Landscape/PLY/SimplePlyWriter.vb b/gr/Landscape/PLY/SimplePlyWriter.vb new file mode 100644 index 000000000..595e2bf3b --- /dev/null +++ b/gr/Landscape/PLY/SimplePlyWriter.vb @@ -0,0 +1,56 @@ +Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel +Imports Microsoft.VisualBasic.ComponentModel.Ranges.Model +Imports Microsoft.VisualBasic.Imaging.Drawing2D.Colors +Imports System.IO +Imports System.Text + +Namespace Ply + + Public Module SimplePlyWriter + + Public Function WriteAsciiText(pointCloud As IEnumerable(Of PointCloud), + buffer As Stream, + Optional colors As ScalerPalette = ScalerPalette.turbo, + Optional levels As Integer = 200) As Boolean + + Using file As New StreamWriter(buffer, Encoding.ASCII) With { + .AutoFlush = True, + .NewLine = vbLf + } + Dim vertex As PointCloud() = pointCloud.ToArray + Dim header As New Header With { + .comment = "Point Cloud Model", + .element_face = -1, + .element_vertex = vertex.Length, + .properties = { + New NamedValue(Of String)("x", "float"), + New NamedValue(Of String)("y", "float"), + New NamedValue(Of String)("z", "float"), + New NamedValue(Of String)("intensity", "float"), + New NamedValue(Of String)("color", "string") + } + } + Dim colorSet As String() = Designer _ + .GetColors(colors.Description, levels) _ + .Select(Function(c) c.ToHtmlColor) _ + .ToArray + Dim scale As New DoubleRange(From v As PointCloud In vertex Select v.intensity) + Dim i As Integer + Dim index As New DoubleRange(0, levels - 1) + Dim clr As String + + Call header.WriteAsciiText(file) + Call file.Flush() + + For Each point As PointCloud In vertex + i = scale.ScaleMapping(point.intensity, index) + clr = colorSet(i) + + Call file.WriteLine($"{point.x.ToString("F3")} {point.y.ToString("F3")} {point.z.ToString("F3")} {point.intensity} {clr}") + Next + End Using + + Return True + End Function + End Module +End Namespace \ No newline at end of file diff --git a/gr/Landscape/Wavefront/FileName.vb b/gr/Landscape/Wavefront/FileName.vb new file mode 100644 index 000000000..c64edbcba --- /dev/null +++ b/gr/Landscape/Wavefront/FileName.vb @@ -0,0 +1,10 @@ +Namespace Wavefront + + Public Class Triangle + + Public Property v3 As Integer() + Public Property vn3 As Integer() + Public Property comment As String + + End Class +End Namespace \ No newline at end of file diff --git a/gr/Landscape/Wavefront/OBJ.vb b/gr/Landscape/Wavefront/OBJ.vb new file mode 100644 index 000000000..8e8006a7d --- /dev/null +++ b/gr/Landscape/Wavefront/OBJ.vb @@ -0,0 +1,49 @@ +Imports System.IO +Imports System.Runtime.CompilerServices +Imports Microsoft.VisualBasic.Imaging.Drawing3D +Imports Microsoft.VisualBasic.Language + +Namespace Wavefront + + Public Class OBJ + + ''' + ''' lib file name of mtl data + ''' + ''' + Public Property mtllib As String + Public Property parts As ObjectPart() + Public Property comment As String + + + + Public Shared Function ReadFile(file As StreamReader) As OBJ + Return TextParser.ParseFile(file) + End Function + + End Class + + Public Class ObjectPart + + Public Property g As String + Public Property vertex As Point3D() + Public Property vn As Point3D() + Public Property usemtl As String + Public Property f As Triangle() + + Public ReadOnly Property IsEmpty As Boolean + Get + Return g.StringEmpty AndAlso + vertex.IsNullOrEmpty AndAlso + vn.IsNullOrEmpty AndAlso + usemtl.StringEmpty AndAlso + f.IsNullOrEmpty + End Get + End Property + + Public Overrides Function ToString() As String + Return $"{g Or "no_label".AsDefault}: {vertex.Length} vertexs and {f.Length} triangles" + End Function + + End Class +End Namespace \ No newline at end of file diff --git a/gr/Landscape/Wavefront/TextParser.vb b/gr/Landscape/Wavefront/TextParser.vb new file mode 100644 index 000000000..10109476f --- /dev/null +++ b/gr/Landscape/Wavefront/TextParser.vb @@ -0,0 +1,103 @@ +Imports System.IO +Imports System.Runtime.CompilerServices +Imports System.Text +Imports Microsoft.VisualBasic.Imaging.Drawing3D +Imports Microsoft.VisualBasic.Language +Imports Microsoft.VisualBasic.Language.Values +Imports Microsoft.VisualBasic.Linq + +Namespace Wavefront + + Public Module TextParser + + + Public Function ParseFile(buf As StreamReader) As OBJ + Dim line As New Value(Of String) + Dim comments As New StringBuilder + Dim mtllib As String = Nothing + Dim parts As New List(Of ObjectPart) + Dim vertex As New List(Of Point3D) + Dim vn As New List(Of Point3D) + Dim g As String = Nothing + Dim usemtl As String = Nothing + Dim f As New List(Of Triangle) + + Do While Not (line = buf.ReadLine) Is Nothing + If line.First = "#"c Then + comments.AppendLine(line.Trim("#"c, " "c)) + ElseIf Not line.Value.StringEmpty Then + If line.StartsWith("mtllib") Then + mtllib = Mid(line.Value, "mtllib".Length + 1).Trim + Else + Dim tokens As String() = line.Split + + Select Case tokens(Scan0) + Case "v" + Call vertex.Add(tokens.Skip(1).Select(AddressOf Val).ToArray.DoCall(Function(v) New Point3D(v))) + Case "vn" + Call vn.Add(tokens.Skip(1).Select(AddressOf Val).ToArray.DoCall(Function(v) New Point3D(v))) + Case "g" + g = tokens.Skip(1).JoinBy(" ") + Case "usemtl" + usemtl = tokens.Skip(1).JoinBy(" ") + Case "f" + Dim ints As Integer()() = tokens _ + .Skip(1) _ + .Take(3) _ + .Select(Function(str) + Return str.Split("/"c).Select(Function(c) CInt(Val(c))).ToArray + End Function) _ + .ToArray + + f.Add(New Triangle With { + .v3 = ints.Select(Function(v) v(Scan0)).ToArray, + .vn3 = ints.Select(Function(v) v(2)).ToArray, + .comment = tokens(4) + }) + Case Else + Throw New NotImplementedException(line) + End Select + End If + Else + Dim objPart As New ObjectPart With { + .vertex = vertex.ToArray, + .vn = vn.ToArray, + .g = g, + .usemtl = usemtl, + .f = f.ToArray + } + + If Not objPart.IsEmpty Then + ' create new part + Call parts.Add(objPart) + End If + + g = Nothing + usemtl = Nothing + f.Clear() + vertex.Clear() + vn.Clear() + End If + Loop + + If vertex.Count > 0 Then + Dim objPart As New ObjectPart With { + .vertex = vertex.ToArray, + .vn = vn.ToArray, + .g = g, + .usemtl = usemtl, + .f = f.ToArray + } + + ' create new part + parts.Add(objPart) + End If + + Return New OBJ With { + .comment = comments.ToString, + .mtllib = mtllib, + .parts = parts.ToArray + } + End Function + End Module +End Namespace \ No newline at end of file diff --git a/gr/Microsoft.VisualBasic.Imaging.sln b/gr/Microsoft.VisualBasic.Imaging.sln index 347249476..21a9f3bc3 100644 --- a/gr/Microsoft.VisualBasic.Imaging.sln +++ b/gr/Microsoft.VisualBasic.Imaging.sln @@ -1,294 +1,385 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33326.253 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Microsoft.VisualBasic.Imaging", "Microsoft.VisualBasic.Imaging\Microsoft.VisualBasic.Imaging.vbproj", "{85E71B4B-9276-4EC8-AADC-C849205F1EA8}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "framework", "framework", "{E0DFA7E4-1D86-4239-A8F6-C3ECAE31EC3D}" EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "47-dotnet_Microsoft.VisualBasic", "..\Microsoft.VisualBasic.Core\src\47-dotnet_Microsoft.VisualBasic.vbproj"", "{FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Core", "..\Microsoft.VisualBasic.Core\src\Core.vbproj", "{14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}" EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Test", "Microsoft.VisualBasic.Imaging\Test\Test.vbproj", "{1A566414-428F-403B-84A6-6B9F36FB1117}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Math.NET5", "..\Data_science\Mathematica\Math\Math\Math.NET5.vbproj", "{7B55A6EC-7F70-4A03-8A3F-3705F01658D6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{AFCC8068-49D2-4A75-80AB-22055703DD48}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "html_netcore5", "..\mime\text%html\html_netcore5.vbproj", "{A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}" EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "MIME-htmls", "..\mime\text%html\MIME-htmls.vbproj", "{2DD4BE80-DDC3-4767-94A7-10F1C3395716}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "imaging.NET5", "Microsoft.VisualBasic.Imaging\imaging.NET5.vbproj", "{2CCC6428-7934-4B1D-BE0D-757F6AF14692}" EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "math-core", "..\Data_science\Mathematica\Math\Math\math-core.vbproj", "{E3367B88-5D0C-495A-8273-331656D4B5AA}" +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Landscape", "Landscape\Landscape.vbproj", "{3E100C21-5196-485E-9546-BB0DB4138AF7}" EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Landscape", "Landscape\Landscape.vbproj", "{CCD693C3-EE09-473A-8344-26E05B373394}" -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "text.yaml", "..\mime\text%yaml\text.yaml.vbproj", "{FB637DE6-6BEB-4722-968A-F76DEB851DFC}" -EndProject -Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "physics", "physics\physics.vbproj", "{3291389C-EA87-4B33-A9A5-65A2D60BA3E2}" +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "test", "Microsoft.VisualBasic.Imaging\test\test.vbproj", "{1A566414-428F-403B-84A6-6B9F36FB1117}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - docs|Any CPU = docs|Any CPU - docs|x64 = docs|x64 - docs|x86 = docs|x86 - Publish|Any CPU = Publish|Any CPU - Publish|x64 = Publish|x64 - Publish|x86 = Publish|x86 + gcmodeller_desktop|Any CPU = gcmodeller_desktop|Any CPU + gcmodeller_desktop|x64 = gcmodeller_desktop|x64 + gcmodeller_desktop|x86 = gcmodeller_desktop|x86 + LipidSearch|Any CPU = LipidSearch|Any CPU + LipidSearch|x64 = LipidSearch|x64 + LipidSearch|x86 = LipidSearch|x86 + mzkit_win32|Any CPU = mzkit_win32|Any CPU + mzkit_win32|x64 = mzkit_win32|x64 + mzkit_win32|x86 = mzkit_win32|x86 + mzkit|Any CPU = mzkit|Any CPU + mzkit|x64 = mzkit|x64 + mzkit|x86 = mzkit|x86 + NPSearch|Any CPU = NPSearch|Any CPU + NPSearch|x64 = NPSearch|x64 + NPSearch|x86 = NPSearch|x86 + PlantMAT|Any CPU = PlantMAT|Any CPU + PlantMAT|x64 = PlantMAT|x64 + PlantMAT|x86 = PlantMAT|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 - Release2|Any CPU = Release2|Any CPU - Release2|x64 = Release2|x64 - Release2|x86 = Release2|x86 + Rsharp_app_release|Any CPU = Rsharp_app_release|Any CPU + Rsharp_app_release|x64 = Rsharp_app_release|x64 + Rsharp_app_release|x86 = Rsharp_app_release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|x64.ActiveCfg = Debug|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|x64.Build.0 = Debug|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|x86.ActiveCfg = Debug|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Debug|x86.Build.0 = Debug|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|Any CPU.ActiveCfg = docs|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|Any CPU.Build.0 = docs|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|x64.ActiveCfg = docs|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|x64.Build.0 = docs|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|x86.ActiveCfg = docs|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.docs|x86.Build.0 = docs|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|Any CPU.ActiveCfg = Publish|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|Any CPU.Build.0 = Publish|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|x64.ActiveCfg = Publish|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|x64.Build.0 = Publish|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|x86.ActiveCfg = Publish|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Publish|x86.Build.0 = Publish|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|Any CPU.Build.0 = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|x64.ActiveCfg = Release|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|x64.Build.0 = Release|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|x86.ActiveCfg = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release|x86.Build.0 = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|Any CPU.Build.0 = Release|Any CPU - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|x64.ActiveCfg = Release|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|x64.Build.0 = Release|x64 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|x86.ActiveCfg = Release|x86 - {85E71B4B-9276-4EC8-AADC-C849205F1EA8}.Release2|x86.Build.0 = Release|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|x64.ActiveCfg = Debug|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|x64.Build.0 = Debug|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|x86.ActiveCfg = Debug|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Debug|x86.Build.0 = Debug|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|Any CPU.ActiveCfg = docs|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|Any CPU.Build.0 = docs|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|x64.ActiveCfg = docs|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|x64.Build.0 = docs|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|x86.ActiveCfg = docs|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.docs|x86.Build.0 = docs|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|Any CPU.ActiveCfg = Publish|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|Any CPU.Build.0 = Publish|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|x64.ActiveCfg = Publish|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|x64.Build.0 = Publish|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|x86.ActiveCfg = Publish|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Publish|x86.Build.0 = Publish|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|Any CPU.Build.0 = Release|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|x64.ActiveCfg = Release|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|x64.Build.0 = Release|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|x86.ActiveCfg = Release|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release|x86.Build.0 = Release|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|Any CPU.Build.0 = Release|Any CPU - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|x64.ActiveCfg = Release|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|x64.Build.0 = Release|x64 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|x86.ActiveCfg = Release|x86 - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99}.Release2|x86.Build.0 = Release|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|x64.ActiveCfg = Debug|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|x64.Build.0 = Debug|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|x86.ActiveCfg = Debug|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Debug|x86.Build.0 = Debug|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|Any CPU.ActiveCfg = gcmodeller_desktop|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|Any CPU.Build.0 = gcmodeller_desktop|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|x64.ActiveCfg = gcmodeller_desktop|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|x64.Build.0 = gcmodeller_desktop|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|x86.ActiveCfg = gcmodeller_desktop|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.gcmodeller_desktop|x86.Build.0 = gcmodeller_desktop|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|Any CPU.ActiveCfg = LipidSearch|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|Any CPU.Build.0 = LipidSearch|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|x64.ActiveCfg = LipidSearch|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|x64.Build.0 = LipidSearch|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|x86.ActiveCfg = LipidSearch|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.LipidSearch|x86.Build.0 = LipidSearch|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|Any CPU.ActiveCfg = mzkit_win32|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|Any CPU.Build.0 = mzkit_win32|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|x64.ActiveCfg = mzkit_win32|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|x64.Build.0 = mzkit_win32|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|x86.ActiveCfg = mzkit_win32|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit_win32|x86.Build.0 = mzkit_win32|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|Any CPU.ActiveCfg = mzkit|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|Any CPU.Build.0 = mzkit|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|x64.ActiveCfg = mzkit|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|x64.Build.0 = mzkit|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|x86.ActiveCfg = mzkit|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.mzkit|x86.Build.0 = mzkit|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|Any CPU.ActiveCfg = NPSearch|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|Any CPU.Build.0 = NPSearch|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|x64.ActiveCfg = NPSearch|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|x64.Build.0 = NPSearch|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|x86.ActiveCfg = NPSearch|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.NPSearch|x86.Build.0 = NPSearch|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|Any CPU.ActiveCfg = PlantMAT|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|Any CPU.Build.0 = PlantMAT|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|x64.ActiveCfg = PlantMAT|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|x64.Build.0 = PlantMAT|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|x86.ActiveCfg = PlantMAT|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.PlantMAT|x86.Build.0 = PlantMAT|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|Any CPU.Build.0 = Release|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|x64.ActiveCfg = Release|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|x64.Build.0 = Release|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|x86.ActiveCfg = Release|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Release|x86.Build.0 = Release|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|Any CPU.ActiveCfg = Rsharp_app_release|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|Any CPU.Build.0 = Rsharp_app_release|Any CPU + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|x64.ActiveCfg = Rsharp_app_release|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|x64.Build.0 = Rsharp_app_release|x64 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|x86.ActiveCfg = Rsharp_app_release|x86 + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23}.Rsharp_app_release|x86.Build.0 = Rsharp_app_release|x86 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|x64.ActiveCfg = Debug|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|x64.Build.0 = Debug|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Debug|x86.Build.0 = Debug|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|Any CPU.ActiveCfg = gcmodeller_desktop|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|Any CPU.Build.0 = gcmodeller_desktop|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|x64.ActiveCfg = gcmodeller_desktop|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|x64.Build.0 = gcmodeller_desktop|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|x86.ActiveCfg = gcmodeller_desktop|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.gcmodeller_desktop|x86.Build.0 = gcmodeller_desktop|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|Any CPU.ActiveCfg = LipidSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|Any CPU.Build.0 = LipidSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|x64.ActiveCfg = LipidSearch|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|x64.Build.0 = LipidSearch|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|x86.ActiveCfg = LipidSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.LipidSearch|x86.Build.0 = LipidSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|Any CPU.ActiveCfg = mzkit_win32|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|Any CPU.Build.0 = mzkit_win32|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|x64.ActiveCfg = mzkit_win32|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|x64.Build.0 = mzkit_win32|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|x86.ActiveCfg = mzkit_win32|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit_win32|x86.Build.0 = mzkit_win32|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|Any CPU.ActiveCfg = mzkit|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|Any CPU.Build.0 = mzkit|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|x64.ActiveCfg = mzkit|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|x64.Build.0 = mzkit|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|x86.ActiveCfg = mzkit|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.mzkit|x86.Build.0 = mzkit|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|Any CPU.ActiveCfg = NPSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|Any CPU.Build.0 = NPSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|x64.ActiveCfg = NPSearch|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|x64.Build.0 = NPSearch|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|x86.ActiveCfg = NPSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.NPSearch|x86.Build.0 = NPSearch|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|Any CPU.ActiveCfg = PlantMAT|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|Any CPU.Build.0 = PlantMAT|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|x64.ActiveCfg = PlantMAT|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|x64.Build.0 = PlantMAT|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|x86.ActiveCfg = PlantMAT|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.PlantMAT|x86.Build.0 = PlantMAT|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|Any CPU.Build.0 = Release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|x64.ActiveCfg = Release|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|x64.Build.0 = Release|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|x86.ActiveCfg = Release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Release|x86.Build.0 = Release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|Any CPU.ActiveCfg = Rsharp_app_release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|Any CPU.Build.0 = Rsharp_app_release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|x64.ActiveCfg = Rsharp_app_release|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|x64.Build.0 = Rsharp_app_release|x64 + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|x86.ActiveCfg = Rsharp_app_release|Any CPU + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6}.Rsharp_app_release|x86.Build.0 = Rsharp_app_release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|x64.ActiveCfg = Debug|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|x64.Build.0 = Debug|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Debug|x86.Build.0 = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|Any CPU.ActiveCfg = gcmodeller_desktop|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|Any CPU.Build.0 = gcmodeller_desktop|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|x64.ActiveCfg = gcmodeller_desktop|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|x64.Build.0 = gcmodeller_desktop|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|x86.ActiveCfg = gcmodeller_desktop|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.gcmodeller_desktop|x86.Build.0 = gcmodeller_desktop|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|Any CPU.ActiveCfg = LipidSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|Any CPU.Build.0 = LipidSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|x64.ActiveCfg = LipidSearch|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|x64.Build.0 = LipidSearch|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|x86.ActiveCfg = LipidSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.LipidSearch|x86.Build.0 = LipidSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|Any CPU.ActiveCfg = mzkit_win32|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|Any CPU.Build.0 = mzkit_win32|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|x64.ActiveCfg = mzkit_win32|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|x64.Build.0 = mzkit_win32|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|x86.ActiveCfg = mzkit_win32|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit_win32|x86.Build.0 = mzkit_win32|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|Any CPU.ActiveCfg = mzkit|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|Any CPU.Build.0 = mzkit|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|x64.ActiveCfg = mzkit|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|x64.Build.0 = mzkit|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|x86.ActiveCfg = mzkit|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.mzkit|x86.Build.0 = mzkit|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|Any CPU.ActiveCfg = NPSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|Any CPU.Build.0 = NPSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|x64.ActiveCfg = NPSearch|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|x64.Build.0 = NPSearch|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|x86.ActiveCfg = NPSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.NPSearch|x86.Build.0 = NPSearch|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|Any CPU.ActiveCfg = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|Any CPU.Build.0 = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|x64.ActiveCfg = Debug|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|x64.Build.0 = Debug|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|x86.ActiveCfg = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.PlantMAT|x86.Build.0 = Debug|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|Any CPU.Build.0 = Release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|x64.ActiveCfg = Release|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|x64.Build.0 = Release|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|x86.ActiveCfg = Release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Release|x86.Build.0 = Release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|Any CPU.ActiveCfg = Rsharp_app_release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|Any CPU.Build.0 = Rsharp_app_release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|x64.ActiveCfg = Rsharp_app_release|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|x64.Build.0 = Rsharp_app_release|x64 + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|x86.ActiveCfg = Rsharp_app_release|Any CPU + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59}.Rsharp_app_release|x86.Build.0 = Rsharp_app_release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|x64.ActiveCfg = Debug|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|x64.Build.0 = Debug|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Debug|x86.Build.0 = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|Any CPU.ActiveCfg = gcmodeller_desktop|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|Any CPU.Build.0 = gcmodeller_desktop|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|x64.ActiveCfg = gcmodeller_desktop|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|x64.Build.0 = gcmodeller_desktop|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|x86.ActiveCfg = gcmodeller_desktop|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.gcmodeller_desktop|x86.Build.0 = gcmodeller_desktop|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|Any CPU.ActiveCfg = LipidSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|Any CPU.Build.0 = LipidSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|x64.ActiveCfg = LipidSearch|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|x64.Build.0 = LipidSearch|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|x86.ActiveCfg = LipidSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.LipidSearch|x86.Build.0 = LipidSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|Any CPU.ActiveCfg = mzkit_win32|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|Any CPU.Build.0 = mzkit_win32|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|x64.ActiveCfg = mzkit_win32|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|x64.Build.0 = mzkit_win32|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|x86.ActiveCfg = mzkit_win32|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit_win32|x86.Build.0 = mzkit_win32|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|Any CPU.ActiveCfg = mzkit|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|Any CPU.Build.0 = mzkit|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|x64.ActiveCfg = mzkit|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|x64.Build.0 = mzkit|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|x86.ActiveCfg = mzkit|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.mzkit|x86.Build.0 = mzkit|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|Any CPU.ActiveCfg = NPSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|Any CPU.Build.0 = NPSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|x64.ActiveCfg = NPSearch|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|x64.Build.0 = NPSearch|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|x86.ActiveCfg = NPSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.NPSearch|x86.Build.0 = NPSearch|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|Any CPU.ActiveCfg = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|Any CPU.Build.0 = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|x64.ActiveCfg = Debug|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|x64.Build.0 = Debug|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|x86.ActiveCfg = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.PlantMAT|x86.Build.0 = Debug|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|Any CPU.Build.0 = Release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|x64.ActiveCfg = Release|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|x64.Build.0 = Release|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|x86.ActiveCfg = Release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Release|x86.Build.0 = Release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|Any CPU.ActiveCfg = Rsharp_app_release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|Any CPU.Build.0 = Rsharp_app_release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|x64.ActiveCfg = Rsharp_app_release|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|x64.Build.0 = Rsharp_app_release|x64 + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|x86.ActiveCfg = Rsharp_app_release|Any CPU + {2CCC6428-7934-4B1D-BE0D-757F6AF14692}.Rsharp_app_release|x86.Build.0 = Rsharp_app_release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|x64.ActiveCfg = Debug|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|x64.Build.0 = Debug|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Debug|x86.Build.0 = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|Any CPU.ActiveCfg = gcmodeller_desktop|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|Any CPU.Build.0 = gcmodeller_desktop|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|x64.ActiveCfg = gcmodeller_desktop|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|x64.Build.0 = gcmodeller_desktop|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|x86.ActiveCfg = gcmodeller_desktop|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.gcmodeller_desktop|x86.Build.0 = gcmodeller_desktop|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|Any CPU.ActiveCfg = LipidSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|Any CPU.Build.0 = LipidSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|x64.ActiveCfg = LipidSearch|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|x64.Build.0 = LipidSearch|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|x86.ActiveCfg = LipidSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.LipidSearch|x86.Build.0 = LipidSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|Any CPU.ActiveCfg = mzkit_win32|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|Any CPU.Build.0 = mzkit_win32|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|x64.ActiveCfg = mzkit_win32|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|x64.Build.0 = mzkit_win32|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|x86.ActiveCfg = mzkit_win32|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit_win32|x86.Build.0 = mzkit_win32|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|Any CPU.ActiveCfg = mzkit|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|Any CPU.Build.0 = mzkit|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|x64.ActiveCfg = mzkit|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|x64.Build.0 = mzkit|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|x86.ActiveCfg = mzkit|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.mzkit|x86.Build.0 = mzkit|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|Any CPU.ActiveCfg = NPSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|Any CPU.Build.0 = NPSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|x64.ActiveCfg = NPSearch|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|x64.Build.0 = NPSearch|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|x86.ActiveCfg = NPSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.NPSearch|x86.Build.0 = NPSearch|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|Any CPU.ActiveCfg = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|Any CPU.Build.0 = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|x64.ActiveCfg = Debug|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|x64.Build.0 = Debug|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|x86.ActiveCfg = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.PlantMAT|x86.Build.0 = Debug|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|Any CPU.Build.0 = Release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|x64.ActiveCfg = Release|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|x64.Build.0 = Release|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|x86.ActiveCfg = Release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Release|x86.Build.0 = Release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|Any CPU.ActiveCfg = Rsharp_app_release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|Any CPU.Build.0 = Rsharp_app_release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|x64.ActiveCfg = Rsharp_app_release|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|x64.Build.0 = Rsharp_app_release|x64 + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|x86.ActiveCfg = Rsharp_app_release|Any CPU + {3E100C21-5196-485E-9546-BB0DB4138AF7}.Rsharp_app_release|x86.Build.0 = Rsharp_app_release|Any CPU {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|Any CPU.Build.0 = Debug|Any CPU {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|x64.ActiveCfg = Debug|x64 {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|x64.Build.0 = Debug|x64 {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|x86.ActiveCfg = Debug|x86 {1A566414-428F-403B-84A6-6B9F36FB1117}.Debug|x86.Build.0 = Debug|x86 - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|Any CPU.ActiveCfg = Debug|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|Any CPU.Build.0 = Debug|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|x64.ActiveCfg = Debug|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|x64.Build.0 = Debug|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|x86.ActiveCfg = Debug|x86 - {1A566414-428F-403B-84A6-6B9F36FB1117}.docs|x86.Build.0 = Debug|x86 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|Any CPU.ActiveCfg = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|Any CPU.Build.0 = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|x64.ActiveCfg = Release|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|x64.Build.0 = Release|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|x86.ActiveCfg = Release|x86 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Publish|x86.Build.0 = Release|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.gcmodeller_desktop|x86.Build.0 = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.LipidSearch|x86.Build.0 = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit_win32|x86.Build.0 = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.mzkit|x86.Build.0 = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.NPSearch|x86.Build.0 = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|Any CPU.ActiveCfg = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|Any CPU.Build.0 = Debug|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|x64.ActiveCfg = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|x64.Build.0 = Debug|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|x86.ActiveCfg = Debug|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.PlantMAT|x86.Build.0 = Debug|x86 {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|Any CPU.ActiveCfg = Release|Any CPU {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|Any CPU.Build.0 = Release|Any CPU {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x64.ActiveCfg = Release|x64 {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x64.Build.0 = Release|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x86.ActiveCfg = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x86.Build.0 = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|Any CPU.Build.0 = Release|Any CPU - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|x64.ActiveCfg = Release|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|x64.Build.0 = Release|x64 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|x86.ActiveCfg = Release|x86 - {1A566414-428F-403B-84A6-6B9F36FB1117}.Release2|x86.Build.0 = Release|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|x64.ActiveCfg = Debug|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|x64.Build.0 = Debug|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|x86.ActiveCfg = Debug|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Debug|x86.Build.0 = Debug|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|Any CPU.ActiveCfg = docs|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|Any CPU.Build.0 = docs|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|x64.ActiveCfg = docs|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|x64.Build.0 = docs|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|x86.ActiveCfg = docs|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.docs|x86.Build.0 = docs|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|Any CPU.ActiveCfg = Publish|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|Any CPU.Build.0 = Publish|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|x64.ActiveCfg = Publish|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|x64.Build.0 = Publish|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|x86.ActiveCfg = Publish|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Publish|x86.Build.0 = Publish|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|Any CPU.Build.0 = Release|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|x64.ActiveCfg = Release|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|x64.Build.0 = Release|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|x86.ActiveCfg = Release|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release|x86.Build.0 = Release|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|Any CPU.Build.0 = Release|Any CPU - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|x64.ActiveCfg = Release|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|x64.Build.0 = Release|x64 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|x86.ActiveCfg = Release|x86 - {2DD4BE80-DDC3-4767-94A7-10F1C3395716}.Release2|x86.Build.0 = Release|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|x64.ActiveCfg = Debug|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|x64.Build.0 = Debug|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|x86.ActiveCfg = Debug|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Debug|x86.Build.0 = Debug|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|Any CPU.ActiveCfg = docs|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|Any CPU.Build.0 = docs|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|x64.ActiveCfg = docs|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|x64.Build.0 = docs|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|x86.ActiveCfg = docs|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.docs|x86.Build.0 = docs|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|Any CPU.ActiveCfg = Publish|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|Any CPU.Build.0 = Publish|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|x64.ActiveCfg = Publish|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|x64.Build.0 = Publish|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|x86.ActiveCfg = Publish|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Publish|x86.Build.0 = Publish|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|Any CPU.Build.0 = Release|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|x64.ActiveCfg = Release|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|x64.Build.0 = Release|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|x86.ActiveCfg = Release|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release|x86.Build.0 = Release|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|Any CPU.Build.0 = Release|Any CPU - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|x64.ActiveCfg = Release|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|x64.Build.0 = Release|x64 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|x86.ActiveCfg = Release|x86 - {E3367B88-5D0C-495A-8273-331656D4B5AA}.Release2|x86.Build.0 = Release|x86 - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|x64.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|x64.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|x86.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Debug|x86.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|Any CPU.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|Any CPU.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|x64.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|x64.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|x86.ActiveCfg = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.docs|x86.Build.0 = Debug|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|Any CPU.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|Any CPU.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|x64.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|x64.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|x86.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Publish|x86.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|Any CPU.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|x64.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|x64.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|x86.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release|x86.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|Any CPU.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|x64.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|x64.Build.0 = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|x86.ActiveCfg = Release|Any CPU - {CCD693C3-EE09-473A-8344-26E05B373394}.Release2|x86.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|x64.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|x64.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|x86.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Debug|x86.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|Any CPU.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|Any CPU.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|x64.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|x64.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|x86.ActiveCfg = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.docs|x86.Build.0 = Debug|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|Any CPU.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|Any CPU.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|x64.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|x64.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|x86.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Publish|x86.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|Any CPU.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|x64.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|x64.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|x86.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release|x86.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|Any CPU.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|x64.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|x64.Build.0 = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|x86.ActiveCfg = Release|Any CPU - {FB637DE6-6BEB-4722-968A-F76DEB851DFC}.Release2|x86.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|x64.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|x64.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|x86.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Debug|x86.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|Any CPU.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|Any CPU.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|x64.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|x64.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|x86.ActiveCfg = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.docs|x86.Build.0 = Debug|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|Any CPU.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|Any CPU.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|x64.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|x64.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|x86.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Publish|x86.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|Any CPU.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|x64.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|x64.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|x86.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release|x86.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|Any CPU.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|Any CPU.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|x64.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|x64.Build.0 = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|x86.ActiveCfg = Release|Any CPU - {3291389C-EA87-4B33-A9A5-65A2D60BA3E2}.Release2|x86.Build.0 = Release|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x86.ActiveCfg = Release|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.Release|x86.Build.0 = Release|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|Any CPU.ActiveCfg = Release|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|Any CPU.Build.0 = Release|Any CPU + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|x64.ActiveCfg = Release|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|x64.Build.0 = Release|x64 + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|x86.ActiveCfg = Release|x86 + {1A566414-428F-403B-84A6-6B9F36FB1117}.Rsharp_app_release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {FECCE1FD-E1D4-49E3-A668-60BB5E7AED99} = {AFCC8068-49D2-4A75-80AB-22055703DD48} - {2DD4BE80-DDC3-4767-94A7-10F1C3395716} = {AFCC8068-49D2-4A75-80AB-22055703DD48} - {E3367B88-5D0C-495A-8273-331656D4B5AA} = {AFCC8068-49D2-4A75-80AB-22055703DD48} - {FB637DE6-6BEB-4722-968A-F76DEB851DFC} = {AFCC8068-49D2-4A75-80AB-22055703DD48} + {14589A8E-DEC0-4F3B-BBA3-F127B5A89B23} = {E0DFA7E4-1D86-4239-A8F6-C3ECAE31EC3D} + {7B55A6EC-7F70-4A03-8A3F-3705F01658D6} = {E0DFA7E4-1D86-4239-A8F6-C3ECAE31EC3D} + {A9CF5F42-84F0-4B92-922C-0F1CF08E0B59} = {E0DFA7E4-1D86-4239-A8F6-C3ECAE31EC3D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6398F1C0-7A2F-4ABF-A31F-268FC9F34B72} diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.gitignore b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.gitignore new file mode 100644 index 000000000..e776c8500 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.gitignore @@ -0,0 +1,3 @@ +# Byte-compiled files. +__pycache__/ +*.py[co] diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.travis.yml b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.travis.yml new file mode 100644 index 000000000..4a406a3c4 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - 2.7 + - 3.5 + - 3.6 + - pypy +script: + - python -m unittest discover -v +notifications: + email: + - dickinsm@gmail.com diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/LICENSE b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/LICENSE new file mode 100644 index 000000000..40cb3e8bf --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Mark Dickinson +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +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 HOLDER 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. diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/README.rst b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/README.rst new file mode 100644 index 000000000..eb7b26f47 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/README.rst @@ -0,0 +1,17 @@ +.. image:: https://zenodo.org/badge/20797542.svg + :target: https://zenodo.org/badge/latestdoi/20797542 + +Polyhedron +========== + +This repository contains a pure Python implementation of a robust algorithm for +point-in-polyhedron testing. + +See the docstring of the ``polyhedron`` module for details. + +License +------- + +The ``polyhedron`` code is Copyright (c) 2019, Mark Dickinson. +The code is released under the BSD 3-Clause License. See the ``LICENSE`` +file for details. diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polygon.py b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polygon.py new file mode 100644 index 000000000..7bcf0efe8 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polygon.py @@ -0,0 +1,120 @@ +"""Simple point-in-polygon algorithm based on winding number, with robustness +depending only on the underlying arithmetic. + +We've got a closed, possibly non-simple polygon described as a list of vertices +in R^2, and we're given a point that doesn't lie directly on the path of the +polygon. We'd like to compute the winding number of the polygon around the +point. + +There are two sources of difficulty: (1) dealing with numerical errors that +might result in an incorrect answer, and (2) dealing with degenerate cases. +We'll ignore the numerical issues for the moment. + +Strategy: without loss of generality, let's place the point at the origin. +Divide the remainder of the plane (i.e., R^2 minus the origin) into two +halves, L and R, defined as follows: + + L = {(x, y) | x < 0 or x == 0 and y < 0} + + R = {(x, y) | x > 0 or x == 0 and y > 0} + +That is, R contains all points with argument in the half-closed interval +(-pi/2, pi/2], and L contains all others. Note that with these definitions, L +and R are both convex: a line segment between two points in R lies entirely in +R, and similarly for L. In particular, a line segment between two points can +only pass through the origin if one of those points is in L and the other in R. + +Now the idea is that we follow the edges of the polygon, keeping track of how +many times we move between L and R. For each move from L to R (or vice versa), +we also need to compute whether the edge passes *above* or *below* the origin, +to compute its contribution to the total winding number. From the comment +above, we can safely ignore all edges that lie entirely within either L or R. + +""" + + +def sign(x): + """ + Return 1 if x is positive, -1 if it's negative, and 0 if it's zero. + + """ + if x > 0: + return 1 + elif x < 0: + return -1 + else: + return 0 + + +def vertex_sign(P, O): + result = sign(P[0] - O[0]) or sign(P[1] - O[1]) + if not result: + raise ValueError("vertex coincides with origin") + return result + + +def edge_sign(P, Q, O): + result = sign( + (P[0] - O[0]) * (Q[1] - O[1]) - (P[1] - O[1]) * (Q[0] - O[0])) + if not result: + raise ValueError("vertices collinear with origin") + return result + + +def half_turn(point1, point2, origin): + """ + Return the contribution to the total winding number about 'origin' from a + single edge, from point1 to point2. + + """ + edge_boundary = vertex_sign(point2, origin) - vertex_sign(point1, origin) + return 0 if not edge_boundary else edge_sign(point1, point2, origin) + + +class Polygon(object): + def __init__(self, vertex_positions): + """ + Initialize from list of vertex positions. + + """ + self.vertex_positions = vertex_positions + + def edge_positions(self): + """ + Pairs of vertex positions corresponding to the polygon edges. + + """ + points = self.vertex_positions + first_point = previous_point = points[0] + for point in points[1:]: + yield previous_point, point + previous_point = point + yield previous_point, first_point + + def area(self): + """ + Area enclosed by this polygon. + + For simple counterclockwise-oriented polygons, return the area + enclosed by the polygon. More generally, return the integral + of the winding number of the polygon over R^2. + + """ + acc = 0.0 + for p1, p2 in self.edge_positions(): + acc += (p1[0] - p2[0]) * (p1[1] + p2[1]) + return acc / 2.0 + + def winding_number(self, origin): + """ + Compute the (counterclockwise) winding number of a polygon around a + point. + + Raise ValueError if the point lies directly on the path + of the polygon. + + """ + return sum( + half_turn(point1, point2, origin) + for point1, point2 in self.edge_positions() + ) // 2 diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polyhedron.py b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polyhedron.py new file mode 100644 index 000000000..18ab4ab8c --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/polyhedron.py @@ -0,0 +1,372 @@ +"""Robust point-in-polyhedron test. + +Given an closed, oriented surface in R^3 described by a triangular mesh, the +code below gives a robust algorithm for determining whether a given point is +inside, on the boundary of, or outside, the surface. The algorithm should give +correct results even in degenerate cases, and applies to disconnected +polyhedra, non simply-connected surfaces, and so on. There are no requirements +for the surface to be convex, simple, connected or simply-connected. + +More precisely, we give a method for computing the *winding number* of a closed +oriented surface S around a point O that doesn't lie on S. Roughly speaking, +the winding number of the closed oriented surface S around a point O not on S +is the number of times that the surface encloses that point; for a simple +outward-oriented surface (like that of a convex polyhedron, for example), the +winding number will be 1 for points inside the surface and 0 for points +outside. + +For a precise definition of winding number, we can turn to algebraic topology: +our oriented surface is presented as a collection of combinatorial data +defining abstract vertices, edges and triangles, together with a mapping of +those vertices to R^3. The combinatorial data describe a simplicial complex C, +and assuming that O doesn't lie on the surface, the mapping of the vertices to +R^3 gives a continuous map from the geometric realization of C to R^3 - {O}. +This in turn induces a map on second homology groups: + + H^2(C, Z) -> H^2(R^3 - {O}, Z) + +and by taking the usual right-handed orientation in R^3 we identify H^2(R^3 - +{O}, Z) with Z. The image of [S] under this map gives the winding number. In +particular, the well-definedness of the winding number does not depend on +topological properties of the embedding: it doesn't matter if the surface is +self-intersecting, or has degenerate triangles. The only condition is that O +does not lie on the surface S. + +Algorithm +--------- +The algorithm is based around the usual method of ray-casting: we take a +vertical line L through O and count the intersections of this line with the +triangles of the surface, keeping track of orientations as we go. Let's ignore +corner cases for a moment and assume that: + +(1) O does not lie on the surface, and +(2) for each triangle T (thought of as a closed subset of R^3) touched by + our vertical line L, L meets the interior of T in exactly one point Q + +Then there are four possibilities for each such triangle T: + +1. T lies *above* O and is oriented *upwards* (*away* from O). +2. T lies *above* O and is oriented *downwards* (*towards* O). +3. T lies *below* O and is oriented *downwards* (*away* from O). +4. T lies *below* O and is oriented *upwards* (*towards* O). + +Let's write N1, N2, N3 and N4 for the counts of triangles satisfying conditions +1, 2, 3 and 4 respectively. Since we have a closed surface, these numbers +are not independent; they satisfy the relation: + + N1 + N4 == N2 + N3 + +That is, the number of upward-facing triangles must match the number of +downward-facing triangles. The winding number w is then given by: + + w = N1 - N2 == N3 - N4 + +In the code below, we simply compute 2*w = (N1 + N3) - (N2 + N4), so each +triangle oriented away from O contributes 1 to 2w, while each triangle oriented +towards O contributes -1. + + +Making the algorithm robust +--------------------------- +Now we describe how to augment the basic algorithm described above to include: + +- correct treatment of corner cases (vertical triangles, cases where L meets an + edge or vertex directly, etc.) + +- detection of the case where the point lies directly on the surface. + +It turns out that to make the algorithm robust, all we need to do is be careful +and consistent about classifying vertices, edges and triangles. We do this as +follows: + +- Each vertex of the surface that's not equal to O is considered *positive* if + its coordinates are lexicographically greater than O, and *negative* + otherwise. + +- For an edge PQ of the surface that's not collinear with O, we first describe + the classification in the case that P is negative and Q is positive, and + then extend to arbitrary PQ. + + For P negative and Q positive, there are two cases: + + 1. P and Q have distinct x coordinates. In that case we classify the edge + PQ by its intersection with the plane passing through O and parallel + to the yz-plane: the edge is *positive* if the intersection point is + positive, and *negative* otherwise. + + 2. P and Q have the same x coordinate, in which case they must have + distinct y coordinates. (If the x and the y coordinates both match + then PQ passes through O.) We classify by the intersection of PQ + with the line parallel to the y-axis through O. + + For P positive and Q negative, we classify as above but reverse the sign. + For like-signed P and Q, the classification isn't used. + + Computationally, in case 1 above, the y-coordinate of the intersection + point is: + + Py + (Qy - Py) * (Ox - Px) / (Qx - Px) + + and this is greater than Oy iff + + (Py - Oy) * (Qx - Ox) - (Px - Ox) * (Qy - Oy) + + is positive, so the sign of the edge is the sign of the above expression. + Similarly, if this quantity is zero then we need to look at the z-coordinate + of the intersection, and the sign of the edge is given by + + (Pz - Oz) * (Qx - Ox) - (Px - Ox) * (Qz - Oz) + + In case 2, both of the above quantities are zero, and the sign of the edge is + the sign of + + (Pz - Oz) * (Qy - Oy) - (Py - Oy) * (Qz - Oz) + + Another way to look at this: if P, Q and O are not collinear then the + matrix + + ( Px Qx Ox ) + ( Py Qy Ox ) + ( Pz Qz Ox ) + ( 1 1 1 ) + + has rank 3. It follows that at least one of the three 3x3 minors + + | Px Qx Ox | | Px Qx Ox | | Py Qy Oy | + | Py Qy Oy | | Pz Qz Oz | | Pz Qz Oz | + | 1 1 1 | | 1 1 1 | | 1 1 1 | + + is nonzero. We define the sign of PQ to be the *negative* of the sign of the + first nonzero minor in that list. + +- Each triangle PQR of the surface that's not coplanar with O is considered + *positive* if its normal points away from O, and *negative* if its normal + points towards O. + + Computationally, the sign of the triangle PQR is the sign of the 4x4 + determinant + + | Px Qx Rx Ox | + | Py Qy Ry Oy | + | Pz Qz Rz Oz | + | 1 1 1 1 | + + or equivalently of the 3x3 determinant + + | Px-Ox Qx-Ox Rx-Ox | + | Py-Oy Qy-Oy Ry-Oy | + | Pz-Oz Qz-Oz Rz-Oz | + + +Now to compute the contribution of any given triangle to the total winding +number: + +1. Classify the vertices of the triangle. At the same time, we can check that + none of the vertices is equal to O. If all vertices have the same sign, + then the winding number contribution is zero. + +2. Assuming that the vertices do not all have the same sign, two of the three + edges connect two differently-signed vertices. Classify both those edges + (and simultaneously check that they don't pass through O). If the edges + have opposite classification, then the winding number contribution is zero. + +3. Now two of the edges have the same sign: classify the triangle itself. If + the triangle is positive it contributes 1/2 to the winding number total; if + negative it contributes -1/2. In practice we count contributions of 1 and + -1, and halve the total at the end. + +Note that an edge between two like-signed vertices can never pass through O, so +there's no need to check the third edge in step 2. Similarly, a triangle whose +edge-cycle is trivial can't contain O in its interior. + +To understand what's going on above, it's helpful to step into the world of +homology again. The homology of R^3 - {O} can be identified with that of the +two-sphere S^2 by deformation retract, and we can decompose the two-sphere as a +CW complex consisting of six cells, as follows: + +* 0-cells B and F, where B = (-1, 0, 0) and F = (1, 0, 0) +* 1-cells L and R, where + L = {(cos t, sin t, 0) | -pi <= t <= 0 } + R = {(cos t, sin t, 0) | 0 <= t <= pi } +* 2-cells U and D, where U is the top half of the sphere (z >= 0) + and D is the bottom half (z <= 0), both oriented outwards. + +And the homology of the CW complex is now representable in terms of cellular +homology: + + d d + Z[U] + Z[D] --> Z[L] + Z[R] --> Z[B] + Z[F] + +with boundary maps given by: + + d[U] = [L] + [R]; d[D] = -[L] - [R] + d[R] = [B] - [F]; d[L] = [F] - [B] + +Now the original map C -> R^3 - {O} from the geometric realization of the +simplicial complex is homotopic to a map C -> S^2 that sends: + +* each positive vertex to F and each negative vertex to B +* each edge with boundary [F] - [B] to L if the edge is negative, and -R if the + edge is positive +* each edge with boundary [B] - [F] to R if the edge is positive, and -L if the + edge is negative +* all other edges to 0 +* each triangle whose boundary is [L] + [R] to either U or -D, + depending on whether the triangle is positive or negative +* each triangle whose boundary is -[L] - [R] to either D or -U, + depending on whether the triangle is positive or negative +* all other triangles to 0 + +Mapping all of the triangles in the surface this way, and summing the results +in second homology, we end up with (winding number)*([U] + [D]). + +""" + + +def sign(x): + """ + Return 1 if x is positive, -1 if it's negative, and 0 if it's zero. + + """ + if x > 0: + return 1 + elif x < 0: + return -1 + else: + return 0 + + +def vertex_sign(P, O): + """ + Sign of the vertex P with respect to O, as defined above. + + """ + result = sign(P[0] - O[0]) or sign(P[1] - O[1]) or sign(P[2] - O[2]) + if not result: + raise ValueError("vertex coincides with origin") + return result + + +def edge_sign(P, Q, O): + """ + Sign of the edge PQ with respect to O, as defined above. + + """ + result = ( + sign((P[1] - O[1]) * (Q[0] - O[0]) - (P[0] - O[0]) * (Q[1] - O[1])) or + sign((P[2] - O[2]) * (Q[0] - O[0]) - (P[0] - O[0]) * (Q[2] - O[2])) or + sign((P[2] - O[2]) * (Q[1] - O[1]) - (P[1] - O[1]) * (Q[2] - O[2])) + ) + if not result: + raise ValueError("vertices collinear with origin") + return result + + +def triangle_sign(P, Q, R, O): + """ + Sign of the triangle PQR with respect to O, as defined above. + + """ + m1_0 = P[0] - O[0] + m1_1 = P[1] - O[1] + m2_0 = Q[0] - O[0] + m2_1 = Q[1] - O[1] + m3_0 = R[0] - O[0] + m3_1 = R[1] - O[1] + result = sign( + (m1_0 * m2_1 - m1_1 * m2_0) * (R[2] - O[2]) + + (m2_0 * m3_1 - m2_1 * m3_0) * (P[2] - O[2]) + + (m3_0 * m1_1 - m3_1 * m1_0) * (Q[2] - O[2])) + if not result: + raise ValueError("vertices coplanar with origin") + return result + + +def triangle_chain(v1, v2, v3, origin): + """ + Return the contribution of this triangle to the winding number. + + Raise ValueError if the face contains the origin. + + """ + v1sign = vertex_sign(v1, origin) + v2sign = vertex_sign(v2, origin) + v3sign = vertex_sign(v3, origin) + + face_boundary = 0 + if v1sign != v2sign: + face_boundary += edge_sign(v1, v2, origin) + if v2sign != v3sign: + face_boundary += edge_sign(v2, v3, origin) + if v3sign != v1sign: + face_boundary += edge_sign(v3, v1, origin) + if not face_boundary: + return 0 + + return triangle_sign(v1, v2, v3, origin) + + +class Polyhedron(object): + def __init__(self, triangles, vertex_positions): + """ + Initialize from list of triangles and vertex positions. + + """ + # Validate: check the combinatorial data. + edges = set() + vertices = set() + for triangle in triangles: + vertices.update(triangle) + P, Q, R = triangle + for edge in ((P, Q), (Q, R), (R, P)): + if edge[0] == edge[1]: + raise ValueError("Self edge: {!r}".format(edge)) + if edge in edges: + raise ValueError("Duplicate edge: {!r}".format(edge)) + edges.add(edge) + + # For each edge that appears, the reverse edge should also appear. + for P, Q in edges: + if not (Q, P) in edges: + raise ValueError("Unmatched edge: {!r}".format((P, Q))) + + # Vertex set should match indices in vertex_positions. + if vertices != set(range(len(vertex_positions))): + raise ValueError("Vertex set doesn't match position indices.") + + # Vertex positions in R^3. + self.vertex_positions = vertex_positions + # Indices making up each triangle, counterclockwise + # around the outside of the face. + self.triangles = triangles + + def triangle_positions(self): + """ + Triples of vertex positions. + + """ + for triangle in self.triangles: + yield tuple(self.vertex_positions[vx] for vx in triangle) + + def volume(self): + """ + Return the volume of this polyhedron. + + """ + acc = 0 + for p1, p2, p3 in self.triangle_positions(): + # Twice the area of the projection onto the x-y plane. + det = ((p2[1] - p3[1]) * (p1[0] - p3[0]) - + (p2[0] - p3[0]) * (p1[1] - p3[1])) + # Three times the average height. + height = p1[2] + p2[2] + p3[2] + acc += det * height + return acc / 6.0 + + def winding_number(self, point): + """Determine the winding number of *self* around the given point. + + """ + return sum( + triangle_chain(v1, v2, v3, point) + for v1, v2, v3 in self.triangle_positions()) // 2 diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polygon.py b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polygon.py new file mode 100644 index 000000000..23cc982e4 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polygon.py @@ -0,0 +1,146 @@ +""" +Tests for Polygon.winding_number. + +""" +import unittest + +try: + import numpy +except ImportError: + NUMPY_AVAILABLE = False +else: + NUMPY_AVAILABLE = True + +from polygon import Polygon + + +class TestPolygon(unittest.TestCase): + def test_simple_square(self): + square = Polygon( + vertex_positions=[ + (1.0, -1.0), + (1.0, 1.0), + (-1.0, 1.0), + (-1.0, -1.0), + ] + ) + origin = (0.0, 0.0) + self.assertEqual(square.winding_number(origin), 1) + self.assertEqual(square.area(), 4.0) + + def test_double_square(self): + square = Polygon( + vertex_positions=[ + (1.0, -1.0), + (1.0, 1.0), + (-1.0, 1.0), + (-1.0, -1.0), + ] * 2 + ) + origin = (0.0, 0.0) + self.assertEqual(square.winding_number(origin), 2) + self.assertEqual(square.area(), 8.0) + + def test_clockwise_square(self): + square = Polygon( + vertex_positions=[ + (1.0, -1.0), + (1.0, 1.0), + (-1.0, 1.0), + (-1.0, -1.0), + ][::-1] + ) + origin = (0.0, 0.0) + self.assertEqual(square.winding_number(origin), -1) + self.assertEqual(square.area(), -4.0) + + def test_various_points_in_square(self): + square = Polygon( + vertex_positions=[ + (1.0, -1.0), + (1.0, 1.0), + (-1.0, 1.0), + (-1.0, -1.0), + ] + ) + test_points = [] + for x in range(-3, 4): + for y in range(-3, 4): + test_points.append((0.5*x, 0.5*y)) + + for point in test_points: + x, y = point + if -1 < x < 1 and -1 < y < 1: + # Point is inside. + self.assertEqual(square.winding_number(point), 1) + elif x < -1 or x > 1 or y < -1 or y > 1: + # Point outside. + self.assertEqual(square.winding_number(point), 0) + else: + with self.assertRaises(ValueError): + square.winding_number(point) + + def test_aitch(self): + aitch = Polygon( + vertex_positions=[ + (0, 0), + (1, 0), + (1, 1), + (2, 1), + (2, 0), + (3, 0), + (3, 3), + (2, 3), + (2, 2), + (1, 2), + (1, 3), + (0, 3), + ] + ) + + test_points = [ + (0.5*x, 0.5*y) + for y in range(-1, 8) + for x in range(-1, 8) + ] + + # * for boundary, '.' for outside, 'o' for inside. + template = """\ +......... +.***.***. +.*o*.*o*. +.*o***o*. +.*ooooo*. +.*o***o*. +.*o*.*o*. +.***.***. +......... +""" + expected = ''.join(template.strip().split()) + + assert len(expected) == len(test_points) + for point, point_type in zip(test_points, expected): + if point_type == '.': + self.assertEqual(aitch.winding_number(point), 0) + elif point_type == 'o': + self.assertEqual(aitch.winding_number(point), 1) + else: + with self.assertRaises(ValueError): + aitch.winding_number(point) + + @unittest.skipUnless(NUMPY_AVAILABLE, "Test requires NumPy") + def test_numpy_compatibility(self): + square = Polygon( + vertex_positions=numpy.array( + [ + [1.0, -1.0], + [1.0, 1.0], + [-1.0, 1.0], + [-1.0, -1.0], + ], + dtype=numpy.float64, + ) + ) + origin = numpy.array([0.0, 0.0], dtype=numpy.float64) + self.assertEqual(square.winding_number(origin), 1) + self.assertEqual(square.area(), 4.0) diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polyhedron.py b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polyhedron.py new file mode 100644 index 000000000..37ea97136 --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Math3D/Polyhedron/test_polyhedron.py @@ -0,0 +1,666 @@ +""" +Tests for Polyhedron winding number calculation. + +""" +import unittest + +try: + import numpy +except ImportError: + NUMPY_AVAILABLE = False +else: + NUMPY_AVAILABLE = True + +from polyhedron import Polyhedron + + +# Sample polyhedra ############################################################ + +# Regular tetrahedron. +tetrahedron = Polyhedron( + vertex_positions=[ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), + ], + triangles=[ + [0, 1, 3], + [0, 2, 1], + [0, 3, 2], + [1, 2, 3], + ], +) + + +def tetrahedron_classify(point): + x, y, z = point + if x + y + z < 2 and x + y > z and x + z > y and y + z > x: + return "inside" + if x + y + z <= 2 and x + y >= z and x + z >= y and y + z >= x: + return "boundary" + return "outside" + + +# Regular octahedron, with vertices on the axes. +octahedron = Polyhedron( + vertex_positions=[ + (-1, 0, 0), (0, -1, 0), (0, 0, -1), + (1, 0, 0), (0, 1, 0), (0, 0, 1), + ], + triangles=[ + [0, 2, 1], [0, 4, 2], [0, 1, 5], [0, 5, 4], + [3, 1, 2], [3, 5, 1], [3, 2, 4], [3, 4, 5], + ], +) + +# Cube with vertices at (+-1, +-1, +-1). +cube = Polyhedron( + vertex_positions=[ + (-1, -1, -1), (-1, -1, +1), (-1, +1, -1), (-1, +1, +1), + (+1, -1, -1), (+1, -1, +1), (+1, +1, -1), (+1, +1, +1), + ], + triangles=[ + [1, 3, 2], [1, 0, 4], [1, 5, 7], + [2, 0, 1], [2, 6, 4], [2, 3, 7], + [4, 5, 1], [4, 0, 2], [4, 6, 7], + [7, 3, 1], [7, 6, 2], [7, 5, 4], + ], +) + +# Pair of cubes, sharing a common vertex at the origin. +pair_of_cubes = Polyhedron( + vertex_positions=[ + (-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (-1, 0, 0), + (0, -1, -1), (0, -1, 0), (0, 0, -1), (0, 0, 0), + (0, 0, 1), (0, 1, 0), (0, 1, 1), + (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1), + ], + triangles=[ + [1, 3, 2], [1, 0, 4], [1, 5, 7], + [2, 0, 1], [2, 6, 4], [2, 3, 7], + [4, 5, 1], [4, 0, 2], [4, 6, 7], + [7, 3, 1], [7, 6, 2], [7, 5, 4], + [8, 10, 9], [8, 7, 11], [8, 12, 14], + [9, 7, 8], [9, 13, 11], [9, 10, 14], + [11, 12, 8], [11, 7, 9], [11, 13, 14], + [14, 10, 8], [14, 13, 9], [14, 12, 11], + ], +) + +# Two stacked cuboids, one directly above the other. +aligned_stacked_cuboids = Polyhedron( + vertex_positions=[ + (0, 0, 0), (0, 0, 1), (0, 3, 0), (0, 3, 1), + (3, 0, 0), (3, 0, 1), (3, 3, 0), (3, 3, 1), + (0, 0, 2), (0, 0, 3), (0, 3, 2), (0, 3, 3), + (3, 0, 2), (3, 0, 3), (3, 3, 2), (3, 3, 3), + ], + triangles=( + cube.triangles + + [[x+8, y+8, z+8] for x, y, z in cube.triangles] + ), +) + +# Similar, but with the cuboids misaligned. + +misaligned_stacked_cuboids = Polyhedron( + vertex_positions=[ + (0, 0, 0), (0, 0, 1), (0, 2, 0), (0, 2, 1), + (2, 0, 0), (2, 0, 1), (2, 2, 0), (2, 2, 1), + (1, 1, 2), (1, 1, 3), (1, 3, 2), (1, 3, 3), + (3, 1, 2), (3, 1, 3), (3, 3, 2), (3, 3, 3), + ], + triangles=( + cube.triangles + + [[x+8, y+8, z+8] for x, y, z in cube.triangles] + ), +) + +# Hollow cube: surface consists of two cubes, one facing outwards +# and one facing inwards. +hollow_cube = Polyhedron( + vertex_positions=[ + (0, 0, 0), (0, 0, 3), (0, 3, 0), (0, 3, 3), + (3, 0, 0), (3, 0, 3), (3, 3, 0), (3, 3, 3), + (1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), + (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2), + ], + triangles=( + # Outer cube. + cube.triangles + + # Inner cube: reverse orientation. + [[z+8, y+8, x+8] for x, y, z in cube.triangles] + ), +) + +# Nested cubes: surface consists of two cubes, both facing +# outwards. Points inside the inner cube should have a winding +# number of 2. + +nested_cube = Polyhedron( + vertex_positions=[ + (0, 0, 0), (0, 0, 3), (0, 3, 0), (0, 3, 3), + (3, 0, 0), (3, 0, 3), (3, 3, 0), (3, 3, 3), + (1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), + (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2), + ], + triangles=( + # Outer cube. + cube.triangles + + # Inner cube: same orientation as outer cube. + [[x+8, y+8, z+8] for x, y, z in cube.triangles] + ), +) + +# Torus. +torus = Polyhedron( + vertex_positions=[ + # Outer square, bottom face. + (0, 0, 0), (0, 3, 0), (3, 0, 0), (3, 3, 0), + # Inner square, bottom face. + (1, 1, 0), (1, 2, 0), (2, 1, 0), (2, 2, 0), + # Outer square, top face. + (0, 0, 1), (0, 3, 1), (3, 0, 1), (3, 3, 1), + # Inner square, top face. + (1, 1, 1), (1, 2, 1), (2, 1, 1), (2, 2, 1), + ], + triangles=[ + # Top face. + [8, 14, 12], [14, 8, 10], [10, 15, 14], [15, 10, 11], + [11, 13, 15], [13, 11, 9], [9, 12, 13], [12, 9, 8], + # Bottom face. + [4, 6, 0], [2, 0, 6], [6, 7, 2], [3, 2, 7], + [7, 5, 3], [1, 3, 5], [5, 4, 1], [0, 1, 4], + # Outer faces. + [0, 2, 10], [10, 8, 0], [2, 3, 11], [11, 10, 2], + [3, 1, 9], [9, 11, 3], [1, 0, 8], [8, 9, 1], + # Inner faces. + [4, 12, 14], [14, 6, 4], [6, 14, 15], [15, 7, 6], + [7, 15, 13], [13, 5, 7], [5, 13, 12], [12, 4, 5], + ], +) + + +# Empty surface +empty = Polyhedron( + vertex_positions=[], + triangles=[], +) + + +# Double-sided triangle. This is the simplest valid nontrivial +# surface. +triangle = Polyhedron( + triangles=[ + [0, 1, 2], + [2, 1, 0], + ], + vertex_positions=[ + [1.0, 0.0, 0.0], + [0.0, 1.0, 0.0], + [0.0, 0.0, 1.0], + ], +) + + +# A hexadecahedron that's topologically equivalent to two octagonal pyramids +# glued base-to-base, but with a mapping into R^3 that wraps the surface twice +# around the origin, so that its image in R^3 looks like the surface of an +# octahedron. All points inside the surface have winding number 2. +twice_wrapped_octahedron = Polyhedron( + triangles=[ + (0, 1, 2), + (0, 2, 3), + (0, 3, 4), + (0, 4, 5), + (0, 5, 6), + (0, 6, 7), + (0, 7, 8), + (0, 8, 1), + (9, 2, 1), + (9, 3, 2), + (9, 4, 3), + (9, 5, 4), + (9, 6, 5), + (9, 7, 6), + (9, 8, 7), + (9, 1, 8), + ], + vertex_positions=[ + (0.0, 0.0, 1.0), + (1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (-1.0, 0.0, 0.0), + (0.0, -1.0, 0.0), + (1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (-1.0, 0.0, 0.0), + (0.0, -1.0, 0.0), + (0.0, 0.0, -1.0), + ], +) + + +class TestPolyhedron(unittest.TestCase): + def test_invalid_polyhedra(self): + with self.assertRaises(ValueError): + # Tetrahedron with one face incorrectly oriented. + Polyhedron( + vertex_positions=[ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), + ], + triangles=[ + [0, 1, 3], + [0, 1, 2], + [0, 3, 2], + [1, 2, 3], + ], + ) + + with self.assertRaises(ValueError): + # Tetrahedron with a duplicated face. + Polyhedron( + vertex_positions=[ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), + ], + triangles=[ + [0, 1, 3], + [0, 2, 1], + [0, 3, 2], + [1, 2, 3], + [1, 2, 3], + ], + ) + + with self.assertRaises(ValueError): + # Tetrahedron with a missing face. + Polyhedron( + vertex_positions=[ + (0, 0, 0), + (0, 1, 1), + (1, 0, 1), + (1, 1, 0), + ], + triangles=[ + [0, 1, 3], + [0, 2, 1], + [0, 3, 2], + ], + ) + + def test_tetrahedron(self): + xs = ys = zs = [0.25 * v for v in range(-1, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = tetrahedron_classify(point) + if class_ == "inside": + self.assertEqual(tetrahedron.winding_number(point), 1) + elif class_ == "outside": + self.assertEqual(tetrahedron.winding_number(point), 0) + elif class_ == "boundary": + # Point is on the boundary. + with self.assertRaises(ValueError): + tetrahedron.winding_number(point) + else: + assert False, "should never get here" + + def test_cube(self): + # Check volume + self.assertEqual(cube.volume(), 8) + + def classify(point): + x, y, z = point + if -1 < x < 1 and -1 < y < 1 and -1 < z < 1: + return "inside" + if -1 <= x <= 1 and -1 <= y <= 1 and -1 <= z <= 1: + return "boundary" + return "outside" + + # Quarter-integer boundaries from -1.25 to 1.25 inclusive. + xs = ys = zs = [0.25 * v for v in range(-5, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(cube.winding_number(point), 1) + elif class_ == "outside": + self.assertEqual(cube.winding_number(point), 0) + elif class_ == "boundary": + # Point is on the boundary. + with self.assertRaises(ValueError): + cube.winding_number(point) + else: + assert False, "should never get here" + + def test_octahedron(self): + self.assertEqual(octahedron.volume(), 4.0 / 3.0) + + def classify(point): + x, y, z = point + s = abs(x) + abs(y) + abs(z) + if s < 1: + return "inside" + elif s == 1: + return "boundary" + else: + return "outside" + + # Quarter-integer boundaries from -1.25 to 1.25 inclusive. + xs = ys = zs = [0.25 * v for v in range(-5, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(octahedron.winding_number(point), 1) + elif class_ == "outside": + self.assertEqual(octahedron.winding_number(point), 0) + elif class_ == "boundary": + # Point is on the boundary. + with self.assertRaises(ValueError): + octahedron.winding_number(point) + else: + assert False, "never get here" + + def test_twice_wrapped_octahedron(self): + poly = twice_wrapped_octahedron + + self.assertEqual(poly.volume(), 8.0 / 3.0) + + def winding_number(point): + x, y, z = point + s = abs(x) + abs(y) + abs(z) + if s == 1: + raise ValueError("boundary") + return 2 if s < 1 else 0 + + xs = ys = zs = [0.25 * v for v in range(-5, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + try: + expected = winding_number(point) + except ValueError: + with self.assertRaises(ValueError): + poly.winding_number(point) + else: + self.assertEqual(poly.winding_number(point), expected) + + def test_pair_of_cubes(self): + self.assertEqual(pair_of_cubes.volume(), 2.0) + + def classify(point): + x, y, z = point + if -1 < x < 0 and -1 < y < 0 and -1 < z < 0: + return "inside" + if 0 < x < 1 and 0 < y < 1 and 0 < z < 1: + return "inside" + if -1 <= x <= 0 and -1 <= y <= 0 and -1 <= z <= 0: + return "boundary" + if 0 <= x <= 1 and 0 <= y <= 1 and 0 <= z <= 1: + return "boundary" + return "outside" + + # Quarter-integer boundaries from -1.25 to 1.25 inclusive. + xs = ys = zs = [0.25 * v for v in range(-5, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(pair_of_cubes.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + pair_of_cubes.winding_number(point) + elif class_ == "outside": + self.assertEqual(pair_of_cubes.winding_number(point), 0) + else: + assert False, "never get here" + + def test_hollow_cube(self): + self.assertEqual(hollow_cube.volume(), 26.0) + + def classify(point): + x, y, z = point + if 1 < x < 2 and 1 < y < 2 and 1 < z < 2: + return "outside" + if 1 <= x <= 2 and 1 <= y <= 2 and 1 <= z <= 2: + return "boundary" + if 0 < x < 3 and 0 < y < 3 and 0 < z < 3: + return "inside" + if 0 <= x <= 3 and 0 <= y <= 3 and 0 <= z <= 3: + return "boundary" + return "outside" + + xs = ys = zs = [0.25 * v for v in range(-1, 14)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(hollow_cube.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + hollow_cube.winding_number(point) + elif class_ == "outside": + self.assertEqual(hollow_cube.winding_number(point), 0) + else: + assert False, "never get here" + + def test_nested_cube(self): + # To make sense of the volume, think of it as the integral + # of the winding number: in effect, points inside the inner cube + # contribute to the volume *twice*. + self.assertEqual(nested_cube.volume(), 28.0) + + def classify(point): + x, y, z = point + if 1 < x < 2 and 1 < y < 2 and 1 < z < 2: + return "doubled" + if 1 <= x <= 2 and 1 <= y <= 2 and 1 <= z <= 2: + return "boundary" + if 0 < x < 3 and 0 < y < 3 and 0 < z < 3: + return "inside" + if 0 <= x <= 3 and 0 <= y <= 3 and 0 <= z <= 3: + return "boundary" + return "outside" + + xs = ys = zs = [0.25 * v for v in range(-1, 14)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(nested_cube.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + nested_cube.winding_number(point) + elif class_ == "outside": + self.assertEqual(nested_cube.winding_number(point), 0) + elif class_ == "doubled": + self.assertEqual(nested_cube.winding_number(point), 2) + else: + assert False, "never get here" + + def test_torus(self): + self.assertEqual(torus.volume(), 8.0) + + def classify(point): + x, y, z = point + if 0 < z < 1 and (0 < x < 1 or 2 < x < 3) and 0 < y < 3: + return "inside" + if 0 < z < 1 and (0 < y < 1 or 2 < y < 3) and 0 < x < 3: + return "inside" + if 0 <= z <= 1 and (0 <= x <= 1 or 2 <= x <= 3) and 0 <= y <= 3: + return "boundary" + if 0 <= z <= 1 and (0 <= y <= 1 or 2 <= y <= 3) and 0 <= x <= 3: + return "boundary" + return "outside" + + xs = ys = [0.25 * v for v in range(-1, 14)] + zs = [0.25 * v for v in range(-1, 6)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(torus.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + torus.winding_number(point) + elif class_ == "outside": + self.assertEqual(torus.winding_number(point), 0) + else: + assert False, "never get here" + + def test_aligned_stacked_cuboids(self): + self.assertEqual(aligned_stacked_cuboids.volume(), 18.0) + + def classify(point): + x, y, z = point + if 0 < x < 3 and 0 < y < 3 and 0 < z < 1: + return "inside" + if 0 < x < 3 and 0 < y < 3 and 2 < z < 3: + return "inside" + if 0 <= x <= 3 and 0 <= y <= 3 and 0 <= z <= 1: + return "boundary" + if 0 <= x <= 3 and 0 <= y <= 3 and 2 <= z <= 3: + return "boundary" + return "outside" + + xs = ys = zs = [0.25 * v for v in range(-1, 14)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual( + aligned_stacked_cuboids.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + aligned_stacked_cuboids.winding_number(point) + elif class_ == "outside": + self.assertEqual( + aligned_stacked_cuboids.winding_number(point), 0) + else: + assert False, "never get here" + + def test_misaligned_stacked_cuboids(self): + self.assertEqual(misaligned_stacked_cuboids.volume(), 8.0) + + def classify(point): + x, y, z = point + if 0 < x < 2 and 0 < y < 2 and 0 < z < 1: + return "inside" + if 1 < x < 3 and 1 < y < 3 and 2 < z < 3: + return "inside" + if 0 <= x <= 2 and 0 <= y <= 2 and 0 <= z <= 1: + return "boundary" + if 1 <= x <= 3 and 1 <= y <= 3 and 2 <= z <= 3: + return "boundary" + return "outside" + + xs = ys = zs = [0.25 * v for v in range(-1, 14)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual( + misaligned_stacked_cuboids.winding_number(point), 1) + elif class_ == "boundary": + with self.assertRaises(ValueError): + misaligned_stacked_cuboids.winding_number(point) + elif class_ == "outside": + self.assertEqual( + misaligned_stacked_cuboids.winding_number(point), 0) + else: + assert False, "never get here" + + def test_empty(self): + self.assertEqual(empty.volume(), 0.0) + xs = ys = zs = [0.25 * v for v in range(-1, 14)] + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + self.assertEqual(empty.winding_number(point), 0) + + @unittest.skipUnless(NUMPY_AVAILABLE, "Test requires NumPy") + def test_numpy_float64_compatibility(self): + # This is a repetition of test_cube, but using NumPy float64 + # and int64 values in place of Python ints for vertices and triangles + # (respectively), and ndarrays in place of lists or tuples. + numpy_cube = Polyhedron( + triangles=numpy.array(cube.triangles, dtype=numpy.int64), + vertex_positions=numpy.array( + cube.vertex_positions, dtype=numpy.float64), + ) + + # Check volume + self.assertEqual(numpy_cube.volume(), 8) + + def classify(point): + x, y, z = point + if -1 < x < 1 and -1 < y < 1 and -1 < z < 1: + return "inside" + if -1 <= x <= 1 and -1 <= y <= 1 and -1 <= z <= 1: + return "boundary" + return "outside" + + # Quarter-integer boundaries from -1.25 to 1.25 inclusive. + xs = ys = zs = numpy.linspace(-1.25, 1.25, 11) + + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(numpy_cube.winding_number(point), 1) + elif class_ == "outside": + self.assertEqual(numpy_cube.winding_number(point), 0) + elif class_ == "boundary": + # Point is on the boundary. + with self.assertRaises(ValueError): + numpy_cube.winding_number(point) + else: + assert False, "should never get here" + + @unittest.skipUnless(NUMPY_AVAILABLE, "Test requires NumPy") + def test_numpy_int64_compatibility(self): + # This is a repetition of test_cube, but using NumPy int64 + # values in place of Python ints, and ndarrays in place + # of lists or tuples. + numpy_cube = Polyhedron( + triangles=numpy.array(cube.triangles, dtype=numpy.int64), + vertex_positions=numpy.array( + cube.vertex_positions, dtype=numpy.int64), + ) + + # Check volume + self.assertEqual(numpy_cube.volume(), 8) + + def classify(point): + x, y, z = point + if -1 < x < 1 and -1 < y < 1 and -1 < z < 1: + return "inside" + if -1 <= x <= 1 and -1 <= y <= 1 and -1 <= z <= 1: + return "boundary" + return "outside" + + # Quarter-integer boundaries from -1.25 to 1.25 inclusive. + xs = ys = zs = numpy.linspace(-1.25, 1.25, 11) + + points = [(x, y, z) for x in xs for y in ys for z in zs] + for point in points: + class_ = classify(point) + if class_ == "inside": + self.assertEqual(numpy_cube.winding_number(point), 1) + elif class_ == "outside": + self.assertEqual(numpy_cube.winding_number(point), 0) + elif class_ == "boundary": + # Point is on the boundary. + with self.assertRaises(ValueError): + numpy_cube.winding_number(point) + else: + assert False, "should never get here" + + +if __name__ == '__main__': + unittest.main() diff --git a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Point3D.vb b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Point3D.vb index ba3e8c37e..d81d0e6e7 100644 --- a/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Point3D.vb +++ b/gr/Microsoft.VisualBasic.Imaging/Drawing3D/Point3D.vb @@ -97,16 +97,28 @@ Namespace Drawing3D Me.Z = z End Sub - Public Sub New(p As PointF, z!) + + Public Sub New(p As PointF, Optional z! = 0.0) Me.X = p.X Me.Y = p.Y Me.Z = z End Sub + Public Sub New(p As Point) Call Me.New(p.X, p.Y) End Sub + + Sub New(xyz As Double()) + Call Me.New(xyz(0), xyz(1), xyz.ElementAtOrDefault(2)) + End Sub + + + Sub New(xyz As Single()) + Call Me.New(xyz(0), xyz(1), xyz.ElementAtOrDefault(2)) + End Sub + Public Property X As Double Implements PointF3D.X Public Property Y As Double Implements PointF3D.Y Public Property Z As Double Implements PointF3D.Z @@ -195,9 +207,10 @@ Namespace Drawing3D Public Shared Operator -(p3D As Point3D, offset As Point3D) As Point3D Return New Point3D( - p3D.X - offset.X, - p3D.Y - offset.Y, - p3D.Z - offset.Z) + x:=p3D.X - offset.X, + y:=p3D.Y - offset.Y, + z:=p3D.Z - offset.Z + ) End Operator ''' diff --git a/gr/Microsoft.VisualBasic.Imaging/test/hqx_test.vb b/gr/Microsoft.VisualBasic.Imaging/test/hqx_test.vb index 1ff06e0f1..6c3d64627 100644 --- a/gr/Microsoft.VisualBasic.Imaging/test/hqx_test.vb +++ b/gr/Microsoft.VisualBasic.Imaging/test/hqx_test.vb @@ -50,7 +50,7 @@ #End Region Imports System.Drawing -Imports System.Drawing.Imaging +Imports Microsoft.VisualBasic.Imaging Imports Microsoft.VisualBasic.Imaging.BitmapImage Imports Microsoft.VisualBasic.Imaging.Drawing2D.HeatMap diff --git a/gr/Microsoft.VisualBasic.Imaging/test/objReaderTest.vb b/gr/Microsoft.VisualBasic.Imaging/test/objReaderTest.vb new file mode 100644 index 000000000..ec64fae7e --- /dev/null +++ b/gr/Microsoft.VisualBasic.Imaging/test/objReaderTest.vb @@ -0,0 +1,13 @@ +Imports System.IO +Imports Microsoft.VisualBasic.Imaging.Landscape.Wavefront + +Module objReaderTest + + Sub Main() + Using file As StreamReader = "E:\mzkit\Rscript\Library\MSI_app\data\Segmentation.obj".OpenReader + Dim obj As OBJ = OBJ.ReadFile(file) + + Pause() + End Using + End Sub +End Module diff --git a/gr/Microsoft.VisualBasic.Imaging/test/test.vbproj b/gr/Microsoft.VisualBasic.Imaging/test/test.vbproj index 72af95061..a54b9a48a 100644 --- a/gr/Microsoft.VisualBasic.Imaging/test/test.vbproj +++ b/gr/Microsoft.VisualBasic.Imaging/test/test.vbproj @@ -6,7 +6,7 @@ AnyCPU {1A566414-428F-403B-84A6-6B9F36FB1117} Exe - Test.Project.imageSmoothTest + Test.Project.objReaderTest Test.Project Test.Project 512 @@ -118,6 +118,7 @@ + @@ -128,6 +129,7 @@ + @@ -169,21 +171,25 @@ - - {e3367b88-5d0c-495a-8273-331656d4b5aa} - math-core + + {7b55a6ec-7f70-4a03-8a3f-3705f01658d6} + Math.NET5 - - {fecce1fd-e1d4-49e3-a668-60bb5e7aed99} - 47-dotnet_Microsoft.VisualBasic + + {14589a8e-dec0-4f3b-bba3-f127b5a89b23} + Core - - {2DD4BE80-DDC3-4767-94A7-10F1C3395716} - MIME-htmls + + {a9cf5f42-84f0-4b92-922c-0f1cf08e0b59} + html_netcore5 - - {85e71b4b-9276-4ec8-aadc-c849205f1ea8} - Microsoft.VisualBasic.Imaging + + {3e100c21-5196-485e-9546-bb0db4138af7} + Landscape + + + {2ccc6428-7934-4b1d-be0d-757f6af14692} + imaging.NET5 diff --git a/gr/Microsoft.VisualBasic.Imaging/test/test5.vbproj b/gr/Microsoft.VisualBasic.Imaging/test/test5.vbproj deleted file mode 100644 index 1fa93065b..000000000 --- a/gr/Microsoft.VisualBasic.Imaging/test/test5.vbproj +++ /dev/null @@ -1,126 +0,0 @@ - - - - Microsoft.VisualBasic.Imaging - net6.0 - test - AnyCPU;x64 - Debug;Release;LipidSearch;Rsharp_app_release;mzkit;NPSearch - Exe - - - - - netcore5=1,UNIX=1 - Microsoft.VisualBasic.Imaging.xml - true - true - full - - - - netcore5=1 - Microsoft.VisualBasic.Imaging.xml - - - - true - false - true - full - false - true - netcore5=1,UNIX=1 - Microsoft.VisualBasic.Imaging.xml - - - - true - false - true - full - false - true - netcore5=1,UNIX=1 - Microsoft.VisualBasic.Imaging.xml - D:\GCModeller\src\R-sharp\App\ - - - - true - false - true - full - false - true - netcore5=1,UNIX=1 - Microsoft.VisualBasic.Imaging.xml - D:\biodeep\biodeepdb_v3\metaCluster\NPSearch\assembly\ - - - - true - false - true - full - false - true - netcore5=1,UNIX=1 - Microsoft.VisualBasic.Imaging.xml - D:\GCModeller\src\R-sharp\App\ - - - - Microsoft.VisualBasic.Imaging.xml - - - - Microsoft.VisualBasic.Imaging.xml - - - - Microsoft.VisualBasic.Imaging.xml - - - - Microsoft.VisualBasic.Imaging.xml - - - - Microsoft.VisualBasic.Imaging.xml - - - - Microsoft.VisualBasic.Imaging.xml - true - false - true - full - false - netcore5=1,UNIX=1 - - - - - - - - - - - - - - - - - - - - - - - - - -