├── README.md ├── UnityGeometryHelper ├── Test │ ├── App.config │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── Test.csproj ├── UnityGeometryHelper │ ├── QuaternionUtility.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── UnityGeometryHelper.csproj │ ├── ConvexAogrithm.cs │ ├── DuKeyMap.cs │ ├── Geometric3D.cs │ └── Geometric.cs └── UnityGeometryHelper.sln ├── .gitattributes └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # UnityGeometryHelper 2 | Unity几何综合运算 3 | 4 | #Doc 5 | https://www.showdoc.cc/web/#/109109980843671 6 | -------------------------------------------------------------------------------- /UnityGeometryHelper/Test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UnityGeometryHelper/Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityGeometryHelper; 5 | 6 | namespace Test 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | List points = new List() 13 | { 14 | new Vector3(-8623,0,-11289), 15 | new Vector3(11808,0,-11289), 16 | new Vector3(11808,0,5688), 17 | new Vector3(-8623,0,5688), 18 | new Vector3(-8623,0,5688), 19 | }; 20 | 21 | Vector3 checkP = new Vector3(-5836,600,1031); 22 | 23 | Console.WriteLine(Geometric.IsPointInArea(points.ToArray(), checkP)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/QuaternionUtility.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace UnityGeometryHelper 4 | { 5 | public class QuaternionUtility 6 | { 7 | public static Quaternion Euler(float x, float y, float z) 8 | { 9 | Quaternion res = new Quaternion() 10 | { 11 | w = cos(x / 2f) * cos(y / 2f) * cos(z / 2f) + sin(x / 2f) * sin(y / 2f) * sin(z / 2f), 12 | x = sin(x / 2f) * cos(y / 2f) * cos(z / 2f) - cos(x / 2f) * sin(y / 2f) * sin(z / 2f), 13 | y = cos(x / 2f) * sin(y / 2f) * cos(z / 2f) + sin(x / 2f) * cos(y / 2f) * sin(z / 2f), 14 | z = cos(x / 2f) * cos(y / 2f) * sin(z / 2f) - sin(x / 2f) * sin(y / 2f) * cos(z / 2f), 15 | }; 16 | 17 | return res; 18 | } 19 | 20 | public static Quaternion Axis(Vector3 axis, float angle) 21 | { 22 | var s = sin(angle / 2f); 23 | var c = cos(angle / 2f); 24 | 25 | Quaternion res = new Quaternion() 26 | { 27 | w = c, 28 | x = axis.x * s, 29 | y = axis.y * s, 30 | z = axis.z * s, 31 | }; 32 | 33 | return res; 34 | } 35 | 36 | private static float cos(float a) 37 | { 38 | return Mathf.Cos((a / 180) * Mathf.PI); 39 | } 40 | 41 | private static float sin(float a) 42 | { 43 | return Mathf.Sin((a / 180) * Mathf.PI); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UnityGeometryHelper/Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Test")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8ab2080f-9b8b-4181-ae3b-51e58a88bf3a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityGeometryHelper", "UnityGeometryHelper\UnityGeometryHelper.csproj", "{3C633A68-E6A0-4CA5-B423-9C616E34D93A}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{8AB2080F-9B8B-4181-AE3B-51E58A88BF3A}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {3C633A68-E6A0-4CA5-B423-9C616E34D93A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {3C633A68-E6A0-4CA5-B423-9C616E34D93A}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {3C633A68-E6A0-4CA5-B423-9C616E34D93A}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {3C633A68-E6A0-4CA5-B423-9C616E34D93A}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {8AB2080F-9B8B-4181-AE3B-51E58A88BF3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {8AB2080F-9B8B-4181-AE3B-51E58A88BF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {8AB2080F-9B8B-4181-AE3B-51E58A88BF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {8AB2080F-9B8B-4181-AE3B-51E58A88BF3A}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UnityGeometryHelper")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnityGeometryHelper")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3c633a68-e6a0-4ca5-b423-9c616e34d93a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/UnityGeometryHelper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3C633A68-E6A0-4CA5-B423-9C616E34D93A} 8 | Library 9 | Properties 10 | UnityGeometryHelper 11 | UnityGeometryHelper 12 | v3.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | ..\..\..\ZCN_WiringAlgo_1_0\WiringAlgo_Console\LitJson.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ..\..\..\ZCN_WiringAlgo_1_0\WiringAlgo_Console\UnityEngine.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 67 | -------------------------------------------------------------------------------- /UnityGeometryHelper/Test/Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {8AB2080F-9B8B-4181-AE3B-51E58A88BF3A} 8 | Exe 9 | Properties 10 | Test 11 | Test 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\..\..\ZCN_WiringAlgo_1_0\WiringAlgo_Console\LitJson.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | ..\..\..\ZCN_WiringAlgo_1_0\WiringAlgo_Console\UnityEngine.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {3c633a68-e6a0-4ca5-b423-9c616e34d93a} 61 | UnityGeometryHelper 62 | 63 | 64 | 65 | 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/ConvexAogrithm.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace UnityGeometryHelper 6 | { 7 | 8 | internal class ConvexAogrithm 9 | { 10 | private List nodes; 11 | private Stack sortedNodes; 12 | public Vector3[] sor_nodes; 13 | 14 | public ConvexAogrithm(List points) 15 | { 16 | nodes = points; 17 | } 18 | 19 | private double DistanceOfNodes(Vector3 p0, Vector3 p1) 20 | { 21 | return Vector3.Distance(p0, p1); 22 | } 23 | 24 | public void GetNodesByAngle() 25 | { 26 | Vector3 a; 27 | GetNodesByAngle(out a); 28 | } 29 | 30 | public void GetNodesByAngle(out Vector3 p0) 31 | { 32 | LinkedList list_node = new LinkedList(); 33 | p0 = GetMinYPoint(); 34 | LinkedListNode node = new LinkedListNode(nodes[0]); 35 | list_node.AddFirst(node); 36 | for (int i = 1; i < nodes.Count; i++) 37 | { 38 | int direct = IsClockDirection(p0, node.Value, nodes[i]); 39 | if (direct == 1) 40 | { 41 | list_node.AddLast(nodes[i]); 42 | node = list_node.Last; 43 | //node.Value = nodes[i]; 44 | 45 | } 46 | else if (direct == -10) 47 | { 48 | list_node.Last.Value = nodes[i]; 49 | //node = list_node.Last 50 | //node.Value = nodes[i]; 51 | } 52 | else if (direct == 10) 53 | continue; 54 | else if (direct == -1) 55 | { 56 | LinkedListNode temp = node.Previous; 57 | while (temp != null && IsClockDirection(p0, temp.Value, nodes[i]) == -1) 58 | { 59 | temp = temp.Previous; 60 | } 61 | if (temp == null) 62 | { 63 | list_node.AddFirst(nodes[i]); 64 | continue; 65 | } 66 | if (IsClockDirection(p0, temp.Value, nodes[i]) == -10) 67 | temp.Value = nodes[i]; 68 | else if (IsClockDirection(p0, temp.Value, nodes[i]) == 10) 69 | continue; 70 | else 71 | list_node.AddAfter(temp, nodes[i]); 72 | } 73 | } 74 | sor_nodes = list_node.ToArray(); 75 | sortedNodes = new Stack(); 76 | sortedNodes.Push(p0); 77 | sortedNodes.Push(sor_nodes[0]); 78 | sortedNodes.Push(sor_nodes[1]); 79 | for (int i = 2; i < sor_nodes.Length; i++) 80 | { 81 | 82 | Vector3 p2 = sor_nodes[i]; 83 | Vector3 p1 = sortedNodes.Pop(); 84 | Vector3 p0_sec = sortedNodes.Pop(); 85 | sortedNodes.Push(p0_sec); 86 | sortedNodes.Push(p1); 87 | 88 | if (IsClockDirection1(p0_sec, p1, p2) == 1) 89 | { 90 | sortedNodes.Push(p2); 91 | continue; 92 | } 93 | while (IsClockDirection1(p0_sec, p1, p2) != 1) 94 | { 95 | sortedNodes.Pop(); 96 | p1 = sortedNodes.Pop(); 97 | p0_sec = sortedNodes.Pop(); 98 | sortedNodes.Push(p0_sec); 99 | sortedNodes.Push(p1); 100 | } 101 | sortedNodes.Push(p2); 102 | } 103 | 104 | 105 | } 106 | 107 | private int IsClockDirection1(Vector3 p0, Vector3 p1, Vector3 p2) 108 | { 109 | //Vector3 p0_p1 = new Vector3(p1.x - p0.x, p1.z - p0.z); 110 | Vector3 p0_p1 = new Vector3(p1.x - p0.x, 0, p1.z - p0.z); 111 | Vector3 p0_p2 = new Vector3(p2.x - p0.x, 0, p2.z - p0.z); 112 | return (p0_p1.x * p0_p2.z - p0_p2.x * p0_p1.z) > 0 ? 1 : -1; 113 | } 114 | 115 | private Vector3 GetMinYPoint() 116 | { 117 | Vector3 succNode; 118 | float miny = nodes.Min(r => r.z); 119 | IEnumerable pminYs = nodes.Where(r => r.z == miny); 120 | Vector3[] ps = pminYs.ToArray(); 121 | if (pminYs.Count() > 1) 122 | { 123 | //succNode = pminYs.Single(r => r.x == pminYs.Min(t => t.x));//TODO:Linq换一下 124 | succNode = pminYs.First(r => r.x == pminYs.Min(t => t.x)); 125 | nodes.Remove(succNode); 126 | return succNode; 127 | } 128 | else 129 | { 130 | nodes.Remove(ps[0]); 131 | return ps[0]; 132 | } 133 | 134 | } 135 | 136 | private int IsClockDirection(Vector3 p0, Vector3 p1, Vector3 p2) 137 | { 138 | Vector3 p0_p1 = new Vector3(p1.x - p0.x, 0, p1.z - p0.z); 139 | Vector3 p0_p2 = new Vector3(p2.x - p0.x, 0, p2.z - p0.z); 140 | if ((p0_p1.x * p0_p2.z - p0_p2.x * p0_p1.z) != 0) 141 | return (p0_p1.x * p0_p2.z - p0_p2.x * p0_p1.z) > 0 ? 1 : -1; 142 | else 143 | return DistanceOfNodes(p0, p1) > DistanceOfNodes(p0, p2) ? 10 : -10; 144 | 145 | } 146 | 147 | public Stack SortedNodes 148 | { 149 | get { return sortedNodes; } 150 | } 151 | 152 | } 153 | } -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/DuKeyMap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace UnityGeometryHelper 6 | { 7 | public class Tuple 8 | { 9 | public TKey Key; 10 | public TValue Value; 11 | public Tuple() 12 | { 13 | 14 | } 15 | 16 | public Tuple(TKey key, TValue value) 17 | { 18 | this.Key = key; 19 | this.Value = value; 20 | } 21 | } 22 | 23 | public class DupKeyMap 24 | { 25 | public List> StorageList = 26 | new List>(); 27 | 28 | public TValue GetFirst(TKey key) 29 | { 30 | for (int i = 0; i < StorageList.Count; i++) 31 | { 32 | if (StorageList[i].Key.Equals(key)) 33 | { 34 | return StorageList[i].Value; 35 | } 36 | } 37 | 38 | throw new System.Exception(string.Format("{0} Not found in DupkeyMap", 39 | key.ToString())); 40 | } 41 | 42 | public List GetAll(TKey key) 43 | { 44 | List res = new List(); 45 | 46 | for (int i = 0; i < StorageList.Count; i++) 47 | { 48 | if (StorageList[i].Key.Equals(key)) 49 | { 50 | res.Add(StorageList[i].Value); 51 | } 52 | } 53 | 54 | return res; 55 | } 56 | 57 | public bool ContainKey(TKey key) 58 | { 59 | for (int i = 0; i < StorageList.Count; i++) 60 | { 61 | if (StorageList[i].Key.Equals(key)) 62 | { 63 | return true; 64 | } 65 | } 66 | 67 | return false; 68 | } 69 | 70 | public void Add(TKey key, TValue value) 71 | { 72 | StorageList.Add(new Tuple(key, value)); 73 | } 74 | 75 | public void RemoveFirst(TKey key) 76 | { 77 | for (int i = 0; i < StorageList.Count; i++) 78 | { 79 | if (StorageList[i].Key.Equals(key)) 80 | { 81 | StorageList.RemoveAt(i); 82 | return; 83 | } 84 | } 85 | } 86 | 87 | public void RemoveAll(TKey key) 88 | { 89 | for (int i = 0; i < StorageList.Count; i++) 90 | { 91 | if (StorageList[i].Key.Equals(key)) 92 | { 93 | StorageList.RemoveAt(i); 94 | i--; 95 | } 96 | } 97 | } 98 | 99 | public void ChangeFirstKeyTo(TKey oldKey, TKey newKey) 100 | { 101 | for (int i = 0; i < StorageList.Count; i++) 102 | { 103 | if (StorageList[i].Key.Equals(oldKey)) 104 | { 105 | StorageList[i].Key = newKey; 106 | return; 107 | } 108 | } 109 | } 110 | 111 | public void ChangeAllKeyTo(TKey oldKey, TKey newKey) 112 | { 113 | for (int i = 0; i < StorageList.Count; i++) 114 | { 115 | if (StorageList[i].Key.Equals(oldKey)) 116 | { 117 | StorageList[i].Key = newKey; 118 | } 119 | } 120 | } 121 | 122 | // a>b:true 123 | // 就会从小到大排列 124 | public delegate bool ISBIGGER(TKey a, TKey b); 125 | public void SortedByKey(ISBIGGER isBigger) 126 | { 127 | for (int i = 0; i < StorageList.Count; i++) 128 | { 129 | for (int j = i + 1; j < StorageList.Count; j++) 130 | { 131 | if (isBigger(StorageList[i].Key, StorageList[j].Key)) 132 | { 133 | Tuple swap = StorageList[i]; 134 | StorageList[i] = StorageList[j]; 135 | StorageList[j] = swap; 136 | } 137 | } 138 | } 139 | } 140 | 141 | public List FindByValues(TValue value) 142 | { 143 | List keys = new List(); 144 | 145 | for (int i = 0; i < StorageList.Count; i++) 146 | { 147 | if (StorageList[i].Value.Equals(value)) 148 | { 149 | keys.Add(StorageList[i].Key); 150 | } 151 | } 152 | 153 | return keys; 154 | } 155 | 156 | public List AllKey 157 | { 158 | get 159 | { 160 | List keys = new List(); 161 | 162 | for (int i = 0; i < StorageList.Count; i++) 163 | { 164 | keys.Add(StorageList[i].Key); 165 | } 166 | 167 | return keys; 168 | } 169 | } 170 | 171 | public List AllValue 172 | { 173 | get 174 | { 175 | List values = new List(); 176 | 177 | for (int i = 0; i < StorageList.Count; i++) 178 | { 179 | values.Add(StorageList[i].Value); 180 | } 181 | 182 | return values; 183 | } 184 | } 185 | 186 | public List GetNonDupKeys() 187 | { 188 | List keys = AllKey; 189 | List res = new List(); 190 | for (int i = 0; i < keys.Count; i++) 191 | { 192 | if (!res.Contains(keys[i])) 193 | { 194 | res.Add(keys[i]); 195 | } 196 | } 197 | 198 | return res; 199 | } 200 | 201 | public TValue this[TKey key] 202 | { 203 | get { return GetFirst(key); } 204 | set 205 | { 206 | if (this.ContainKey(key)) 207 | { 208 | for (int i = 0; i < StorageList.Count; i++) 209 | { 210 | if (StorageList[i].Key.Equals(key)) 211 | { 212 | StorageList[i].Value = value; 213 | return; 214 | } 215 | } 216 | } 217 | else 218 | { 219 | this.Add(key, value); 220 | } 221 | } 222 | } 223 | 224 | public int Count 225 | { 226 | get { return StorageList.Count; } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/Geometric3D.cs: -------------------------------------------------------------------------------- 1 | //using System.Collections.Generic; 2 | //using g3; 3 | //using UnityEngine; 4 | 5 | //namespace UnityGeometryHelper 6 | //{ 7 | // public static class Geometric3D 8 | // { 9 | // /// 10 | // /// 求过lp1,lp2与三角形pp1,pp2,pp3所在平面的交点 11 | // /// 12 | // /// 平面三角形1 13 | // /// 平面三角形2 14 | // /// 平面三角形3 15 | // /// 直线1 16 | // /// 直线2 17 | // /// 18 | // public static Vector3 GetCrossPointOfPlaneAndLine(Vector3 planeP1, Vector3 planeP2, Vector3 planeP3, 19 | // Vector3 lineP1, Vector3 lineP2) 20 | // { 21 | // Vector3 planeNor = (Vector3.Cross((planeP1 - planeP2), (planeP3 - planeP2))).normalized; 22 | 23 | // Vector3 planeP = planeP2; 24 | 25 | // float x1 = lineP1.x; 26 | // float x2 = lineP2.x; 27 | // float y1 = lineP1.y; 28 | // float y2 = lineP2.y; 29 | // float z1 = lineP1.z; 30 | // float z2 = lineP2.z; 31 | 32 | // float v1 = x2 - x1; 33 | // float m1 = x1; 34 | // float v2 = y2 - y1; 35 | // float m2 = y1; 36 | // float v3 = z2 - z1; 37 | // float m3 = z1; 38 | 39 | // float n1 = planeP.x; 40 | // float n2 = planeP.y; 41 | // float n3 = planeP.z; 42 | 43 | // float vp1 = planeNor.x; 44 | // float vp2 = planeNor.y; 45 | // float vp3 = planeNor.z; 46 | 47 | // float under = vp1 * v1 + vp2 * v2 + vp3 * v3; 48 | // if (under.Equals(0f)) 49 | // { 50 | // return default(Vector3); 51 | // } 52 | 53 | // float up = (n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3; 54 | 55 | // float resX = (((x2 - x1) * up) / under) + x1; 56 | // float resY = (((y2 - y1) * up) / under) + y1; 57 | // float resZ = (((z2 - z1) * up) / under) + z1; 58 | // return new Vector3(resX, resY, resZ); 59 | // } 60 | 61 | // /// 62 | // /// 直线是否与空间中的体相交 63 | // /// 64 | // /// 直线线点1 65 | // /// 直线线点2 66 | // /// mesh顶点 67 | // /// mesh三角 68 | // /// 69 | // public static bool IsLineCrossSpace(Vector3 lineP1, Vector3 lineP2, Vector3[] verts, int[] tris) 70 | // { 71 | // List vertfs = new List(); 72 | // for (int i = 0; i < verts.Length; i++) 73 | // { 74 | // vertfs.Add(new Vector3f(verts[i].x, verts[i].y, verts[i].z)); 75 | // } 76 | 77 | // DMesh3 mesh = DMesh3Builder.Build(vertfs, tris); 78 | 79 | // DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); 80 | // spatial.Build(); 81 | 82 | // Vector3 l12 = lineP2 - lineP1; 83 | 84 | // Vector3f origin1 = new Vector3f(lineP1.x, lineP1.y, lineP1.z); 85 | // Vector3f origin2 = new Vector3f(lineP2.x, lineP2.y, lineP2.z); 86 | // Vector3f direction = new Vector3f(l12.x, l12.y, l12.z); 87 | // Ray3d ray = new Ray3d(origin1, direction); 88 | 89 | // int hit_tid = spatial.FindNearestHitTriangle(ray); 90 | // bool isHit1 = hit_tid != DMesh3.InvalidID; 91 | 92 | // Ray3d ray2 = new Ray3d(origin2, -direction); 93 | // hit_tid = spatial.FindNearestHitTriangle(ray2); 94 | // bool isHit2 = hit_tid != DMesh3.InvalidID; 95 | 96 | // return isHit1 || isHit2; 97 | // } 98 | 99 | // /// 100 | // /// 射线是否和空间中的体相交 101 | // /// 102 | // /// 103 | // /// 104 | // /// 105 | // /// 106 | // /// 107 | // public static bool IsRayCrossSpace(Vector3 origin, Vector3 direction, Vector3[] verts, int[] tris) 108 | // { 109 | // List vertfs = new List(); 110 | // for (int i = 0; i < verts.Length; i++) 111 | // { 112 | // vertfs.Add(new Vector3f(verts[i].x, verts[i].y, verts[i].z)); 113 | // } 114 | 115 | // DMesh3 mesh = DMesh3Builder.Build(vertfs, tris); 116 | 117 | // DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); 118 | // spatial.Build(); 119 | 120 | // Vector3f originf = new Vector3f(origin.x, origin.y, origin.z); 121 | // Vector3f directionf = new Vector3f(direction.x, direction.y, direction.z); 122 | // Ray3d ray = new Ray3d(originf, directionf); 123 | 124 | // int hit_tid = spatial.FindNearestHitTriangle(ray); 125 | // bool isHit1 = hit_tid != DMesh3.InvalidID; 126 | 127 | // return isHit1; 128 | // } 129 | 130 | // public static bool IsSegmentCrossSpace(Vector3 segmentStart, Vector3 segmentEnd, 131 | // Vector3[] verts, int[] tris) 132 | // { 133 | // List vertfs = new List(); 134 | // for (int i = 0; i < verts.Length; i++) 135 | // { 136 | // vertfs.Add(new Vector3f(verts[i].x, verts[i].y, verts[i].z)); 137 | // } 138 | 139 | // DMesh3 mesh = DMesh3Builder.Build(vertfs, tris); 140 | 141 | // DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); 142 | // spatial.Build(); 143 | 144 | // Vector3 l12 = segmentStart - segmentEnd; 145 | 146 | // Vector3f origin1 = new Vector3f(segmentStart.x, segmentStart.y, segmentStart.z); 147 | // Vector3f origin2 = new Vector3f(segmentEnd.x, segmentEnd.y, segmentEnd.z); 148 | // Vector3f direction = new Vector3f(l12.x, l12.y, l12.z); 149 | // Ray3d ray = new Ray3d(origin1, direction); 150 | // Ray3d ray2 = new Ray3d(origin2, -direction); 151 | // int hit_tid1 = spatial.FindNearestHitTriangle(ray); 152 | // int hit_tid2 = spatial.FindNearestHitTriangle(ray2); 153 | 154 | // if (hit_tid1 == DMesh3.InvalidID && hit_tid1 == DMesh3.InvalidID) 155 | // { 156 | // return false; 157 | // } 158 | 159 | // if (hit_tid1 != DMesh3.InvalidID) 160 | // { 161 | // Vector3 hitPoint = GetCrossPointOfTri(verts, tris, hit_tid1, segmentStart, segmentEnd); 162 | // if (Vector3.Distance(hitPoint, segmentStart) + Vector3.Distance(hitPoint, segmentEnd) > 163 | // Vector3.Distance(segmentEnd, segmentStart)) 164 | // { 165 | // return false; 166 | // } 167 | // } 168 | 169 | // if (hit_tid2 != DMesh3.InvalidID) 170 | // { 171 | // Vector3 hitPoint = GetCrossPointOfTri(verts, tris, hit_tid2, segmentStart, segmentEnd); 172 | // if (Vector3.Distance(hitPoint, segmentStart) + Vector3.Distance(hitPoint, segmentEnd) > 173 | // Vector3.Distance(segmentEnd, segmentStart)) 174 | // { 175 | // return false; 176 | // } 177 | // } 178 | 179 | // return true; 180 | // } 181 | 182 | // public static Vector3 GetCrossPointOfTri(Vector3[] verts, int[] tris, int triId, Vector3 a, Vector3 b) 183 | // { 184 | // List triHit = new List() 185 | // { 186 | // verts[tris[triId]], 187 | // verts[tris[triId + 1]], 188 | // verts[tris[triId + 2]], 189 | // }; 190 | 191 | // return GetCrossPointOfPlaneAndLine(triHit[0], triHit[1], triHit[2], a, b); 192 | // } 193 | // } 194 | //} 195 | -------------------------------------------------------------------------------- /UnityGeometryHelper/UnityGeometryHelper/Geometric.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | 6 | namespace UnityGeometryHelper 7 | { 8 | public static class Geometric 9 | { 10 | #region 基础面积计算 11 | /// 12 | /// AB X AC 13 | /// 14 | /// 点A 15 | /// 点B 16 | /// 点C 17 | /// 18 | private static float cross(Vector3 A, Vector3 B, Vector3 C) 19 | { 20 | return (B.x - A.x) * (C.z - A.z) - (B.z - A.z) * (C.x - A.x); 21 | } 22 | 23 | /// 24 | /// 使用向量叉乘来计算任意多边形面积 25 | /// 26 | /// 顺序描述的点集 27 | /// 面积 28 | public static float GetArea(params Vector3[] points) 29 | { 30 | //去重 31 | List toCalcualte = new List(points); 32 | for (int i = 0; i < toCalcualte.Count; i++) 33 | { 34 | for (int j = 0; j < toCalcualte.Count; j++) 35 | { 36 | if (j != i && toCalcualte[i] == toCalcualte[j]) 37 | { 38 | toCalcualte.RemoveAt(j); 39 | j--; 40 | } 41 | } 42 | } 43 | 44 | int n = toCalcualte.Count; 45 | for (int i = 0; i < n; i++) 46 | { 47 | if (Geometric.Meet(toCalcualte[i], toCalcualte[(i + 1) % n], toCalcualte[(i + 2) % n], toCalcualte[(i + 3) % n])) 48 | { 49 | var temp = toCalcualte[(i + 2) % n]; 50 | toCalcualte[(i + 2) % n] = toCalcualte[(i + 1) % n]; 51 | toCalcualte[(i + 1) % n] = temp; 52 | } 53 | } 54 | 55 | points = toCalcualte.ToArray(); 56 | 57 | float area = 0f; 58 | Vector3 ANXI = toCalcualte[0] + Vector3.one * 100;//要尽量远来补平舍入误差 59 | for (int i = 0; i < points.Length; i++) 60 | { 61 | area += cross(ANXI, points[i], points[(i + 1) % points.Length]); 62 | } 63 | area /= 2f; 64 | return Mathf.Abs(area); 65 | } 66 | 67 | #endregion 68 | 69 | #region 点面关系 70 | 71 | /// 72 | /// 检测点是否在区域内 73 | /// 74 | /// 顺序描述的区域定点 75 | /// 检查点 76 | /// 77 | public static bool IsPointInArea(Vector3[] RegionVertexes, Vector3 toCheck) 78 | { 79 | float minX = Single.MaxValue; 80 | float minZ = Single.MaxValue; 81 | float maxX = Single.MinValue; 82 | float maxZ = Single.MinValue; 83 | foreach (var vertex in RegionVertexes) 84 | { 85 | if (vertex.x < minX) 86 | { 87 | minX = vertex.x; 88 | } 89 | 90 | if (vertex.x > maxX) 91 | { 92 | maxX = vertex.x; 93 | } 94 | 95 | if (vertex.z < minZ) 96 | { 97 | minZ = vertex.z; 98 | } 99 | 100 | if (vertex.z > maxZ) 101 | { 102 | maxZ = vertex.z; 103 | } 104 | } 105 | 106 | if (toCheck.x < minX || toCheck.x > maxX || toCheck.z < minZ || toCheck.z > maxZ) 107 | { 108 | //Console.WriteLine(minX+ " ! " + minZ); 109 | return false; 110 | }//长方形校验不通过直接可以返回flase的 111 | 112 | return Pnpoly(RegionVertexes.Length, RegionVertexes, toCheck.x, toCheck.z); 113 | } 114 | 115 | 116 | public static bool IsPointAlmostInArea(Vector3[] rvs, Vector3 toCheck, float offset = 0.1f) 117 | { 118 | if (IsPointInArea(rvs, toCheck)) 119 | { 120 | return true; 121 | } 122 | 123 | return IsPointOnAreaSide(rvs, toCheck, offset); 124 | } 125 | 126 | public static bool IsPointOnAreaSide(Vector3[] rvs, Vector3 toCheck, float offset = 0.1f) 127 | { 128 | for (int i = 0; i < rvs.Length; i++) 129 | { 130 | var thisStart = rvs[i]; 131 | var thisEnd = rvs[(i + 1) % rvs.Length]; 132 | if (IsPointAlmostOnSegment(thisStart, thisEnd, offset * 2, 133 | toCheck)) 134 | { 135 | return true; 136 | } 137 | } 138 | return false; 139 | } 140 | 141 | public static bool IsAreaFullyInAnotherArea(Vector3[] area1, Vector3[] area2) 142 | { 143 | int n1 = area1.Length; 144 | int n2 = area2.Length; 145 | for (int i = 0; i < n1; i++) 146 | { 147 | for (int j = 0; j < n2; j++) 148 | { 149 | Vector3 point11 = area1[i]; 150 | Vector3 point12 = area1[(i + 1) % n1]; 151 | Vector3 point21 = area2[j]; 152 | Vector3 point22 = area2[(j + 1) % n2]; 153 | 154 | if (Meet(point11, point12, point21, point22)) 155 | { 156 | return false; 157 | } 158 | } 159 | }//首先保证边界没有交点 160 | 161 | bool res1 = true; 162 | bool res2 = true; 163 | foreach (var point in area1) 164 | { 165 | if (!IsPointInArea(area2, point)) 166 | { 167 | res1 = false; 168 | break; 169 | } 170 | } 171 | 172 | foreach (var point in area2) 173 | { 174 | if (!IsPointInArea(area1, point)) 175 | { 176 | res2 = false; 177 | break; 178 | } 179 | } 180 | 181 | return res1 ^ res2;//必然有一个全部在内,另一个全部在外 182 | } 183 | 184 | public static bool IsAreaPartlyInAnotherArea(Vector3[] area1, Vector3[] area2) 185 | { 186 | int n1 = area1.Length; 187 | int n2 = area2.Length; 188 | for (int i = 0; i < n1; i++) 189 | { 190 | for (int j = 0; j < n2; j++) 191 | { 192 | Vector3 point11 = area1[i]; 193 | Vector3 point12 = area1[(i + 1) % n1]; 194 | Vector3 point21 = area2[j]; 195 | Vector3 point22 = area2[(j + 1) % n2]; 196 | 197 | if (Meet(point11, point12, point21, point22)) 198 | { 199 | return true; 200 | } 201 | } 202 | }//边界相交必然部分在内 203 | 204 | bool res1 = false; 205 | bool res2 = false; 206 | foreach (var point in area1) 207 | { 208 | if (IsPointInArea(area2, point)) 209 | { 210 | res1 = true; 211 | break; 212 | } 213 | } 214 | 215 | foreach (var point in area2) 216 | { 217 | if (IsPointInArea(area1, point)) 218 | { 219 | res2 = true; 220 | break; 221 | } 222 | } 223 | 224 | return res1 || res2;//边界没有相交的情况,除了完全分离的情况都是包含 225 | } 226 | 227 | /// 228 | /// Pnpoly内点检测算法 229 | /// 230 | /// 边树 231 | /// 顺序描述的区域点集 232 | /// x坐标 233 | /// z坐标 234 | /// 235 | private static bool Pnpoly(int polySides, Vector3[] RegionVertexes, float x, float z) 236 | { 237 | float maxX = float.MinValue; 238 | float minX = float.MaxValue; 239 | float maxz = float.MinValue; 240 | float minz = float.MaxValue; 241 | 242 | foreach (var thisP in RegionVertexes) 243 | { 244 | if (thisP.x > maxX) 245 | { 246 | maxX = thisP.x; 247 | } 248 | 249 | if (thisP.x < minX) 250 | { 251 | minX = thisP.x; 252 | } 253 | 254 | if (thisP.z > maxz) 255 | { 256 | maxz = thisP.z; 257 | } 258 | 259 | if (thisP.z < minz) 260 | { 261 | minz = thisP.z; 262 | } 263 | } 264 | 265 | float xOffset = maxX - minX; 266 | float zOffset = maxz - minz; 267 | 268 | return PNpoly_X(polySides, RegionVertexes, x, z, xOffset) || PNpoly_Y(polySides, RegionVertexes, x, z, zOffset); 269 | } 270 | 271 | private static bool PNpoly_Y(int polySides, Vector3[] RegionVertexes, float x, float z, float maxOffset) 272 | { 273 | Vector3 pointLeft = new Vector3(x, 0, z - maxOffset); 274 | Vector3 pointRight = new Vector3(x, 0, z + maxOffset); 275 | Vector3 pointSelf = new Vector3(x, 0, z); 276 | 277 | int left = 0; 278 | int right = 0; 279 | for (int i = 0; i < polySides; i++) 280 | { 281 | Vector3 point1 = RegionVertexes[i]; 282 | Vector3 point2 = RegionVertexes[(i + 1) % polySides]; 283 | if (Meet(pointLeft, pointSelf, point1, point2)/* && !Geometric.IsDirParallel(pointLeft - pointSelf, point2 - point1)*/) 284 | { 285 | left++; 286 | } 287 | else if (Meet(pointRight, pointSelf, point1, point2)/* && !Geometric.IsDirParallel(pointRight - pointSelf, point2 - point1)*/) 288 | { 289 | right++; 290 | } 291 | } 292 | 293 | if ((left % 2 == 1) && right % 2 == 1) 294 | { 295 | return true; 296 | } 297 | 298 | return false; 299 | } 300 | 301 | private static bool PNpoly_X(int polySides, Vector3[] RegionVertexes, float x, float z, float maxOffset) 302 | { 303 | Vector3 pointLeft = new Vector3(x - maxOffset, 0, z); 304 | Vector3 pointRight = new Vector3(x + maxOffset, 0, z); 305 | Vector3 pointSelf = new Vector3(x, 0, z); 306 | 307 | int left = 0; 308 | int right = 0; 309 | for (int i = 0; i < polySides; i++) 310 | { 311 | Vector3 point1 = RegionVertexes[i]; 312 | Vector3 point2 = RegionVertexes[(i + 1) % polySides]; 313 | if (Meet(pointLeft, pointSelf, point1, point2)/* && !Geometric.IsDirParallel(pointLeft-pointSelf, point2-point1)*/) 314 | { 315 | left++; 316 | } 317 | else if (Meet(pointRight, pointSelf, point1, point2)/* && !Geometric.IsDirParallel(pointRight - pointSelf, point2 - point1)*/) 318 | { 319 | right++; 320 | } 321 | } 322 | 323 | if ((left % 2 == 1) && right % 2 == 1) 324 | { 325 | return true; 326 | } 327 | 328 | return false; 329 | } 330 | 331 | #endregion 332 | 333 | #region 求线段交点 334 | //这一套没有问题! 335 | const float eps = (float)1e-6; 336 | 337 | const float Pi = Mathf.PI; 338 | 339 | public static bool IsDirParallel(Vector3 a, Vector3 b, float offset = 3f) 340 | { 341 | float angle = Vector3.Angle(a, b); 342 | return (angle < offset || angle > (180 - offset)); 343 | } 344 | public static bool IsDirSame(Vector3 a, Vector3 b, float offset = 3f) 345 | { 346 | float angle = Vector3.Angle(a, b); 347 | return (angle < offset); 348 | } 349 | /// 350 | /// 线段12-34的交点 351 | /// 352 | /// 353 | /// 354 | /// 355 | /// 356 | /// 357 | public static Vector3 GetCorssPointOfSegment(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4) 358 | { 359 | if (!Meet(point1, point2, point3, point4)) 360 | { 361 | return default(Vector3); 362 | } 363 | 364 | if (IsDirParallel(point1 - point2, point3 - point4)) 365 | { 366 | return default(Vector3); 367 | } 368 | 369 | return Inter(point1, point2, point3, point4); 370 | } 371 | 372 | public static Vector3 GetCrossPointOfLine(Vector3 point1, Vector3 point2, Vector3 point3, Vector3 point4) 373 | { 374 | Vector3 _dir12 = (point2 - point1).normalized; 375 | Vector3 _dir34 = (point4 - point3).normalized; 376 | 377 | float _angle = Vector3.Angle(_dir12, _dir34); 378 | if (_angle < 0.1f || _angle > 179.9f) 379 | { 380 | return default(Vector3); 381 | } 382 | 383 | var res = GetCorssPointOfSegment(point1 + _dir12 * 999, point2 - _dir12 * 999, point3 + _dir34 * 999, 384 | point4 - _dir34 * 999); 385 | return res; 386 | } 387 | 388 | /// 389 | /// 获取x的符号 390 | /// 391 | /// 392 | /// 393 | static int sgn(float x) 394 | { 395 | if (x < -eps) 396 | { 397 | return -1; 398 | } 399 | else 400 | { 401 | return x > eps ? 0 : 1; 402 | } 403 | } 404 | 405 | /// 406 | /// 向量叉乘 407 | /// 408 | /// 409 | /// 410 | /// 411 | /// 412 | /// 413 | static float Cross(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) 414 | { 415 | return (p2.x - p1.x) * (p4.z - p3.z) - (p2.z - p1.z) * (p4.x - p3.x); 416 | } 417 | 418 | static float Area(Vector3 p1, Vector3 p2, Vector3 p3) 419 | { 420 | return Cross(p1, p2, p1, p3); 421 | } 422 | 423 | static float fArea(Vector3 p1, Vector3 p2, Vector3 p3) 424 | { 425 | return Mathf.Abs((float)Area(p1, p2, p3)); 426 | } 427 | 428 | /// 429 | /// 线段是否相交 430 | /// 431 | /// 432 | /// 433 | /// 434 | /// 435 | /// 436 | public static bool Meet(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) 437 | { 438 | return Mathf.Max(Mathf.Min(p1.x, p2.x), Mathf.Min(p3.x, p4.x)) <= Mathf.Min(Mathf.Max(p1.x, p2.x), Mathf.Max(p3.x, p4.x)) 439 | && Mathf.Max(Mathf.Min(p1.z, p2.z), Mathf.Min(p3.z, p4.z)) <= Mathf.Min(Mathf.Max(p1.z, p2.z), Mathf.Max(p3.z, p4.z)) 440 | && sgn(Cross(p3, p2, p3, p4) * Cross(p3, p4, p3, p1)) >= 0 441 | && sgn(Cross(p1, p4, p1, p2) * Cross(p1, p2, p1, p3)) >= 0; 442 | } 443 | 444 | public static bool Meet_crossWay(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) 445 | { 446 | Vector3 crossP = GetCrossPointOfLine(p1, p2, p3, p4); 447 | return Geometric.IsPointOnSegment(p1, p2, crossP) && Geometric.IsPointOnSegment(p3, p4, crossP); 448 | } 449 | 450 | public static bool IsTwoLinePointPartCover(Vector3 a, Vector3 b, Vector3 c, Vector3 d, float offset = 0.03f) 451 | { 452 | return Geometric.IsPointAlmostOnSegment(a, b, offset, c) || Geometric.IsPointAlmostOnSegment(a, b, offset, d) || 453 | Geometric.IsPointAlmostOnSegment(c, d, offset, a) || Geometric.IsPointAlmostOnSegment(c, d, offset, b); 454 | } 455 | 456 | static Vector3 Inter(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4) 457 | { 458 | //特判:某顶点在另外一条直线上时 459 | if (IsPointOnLine(p1, p2, p3)) 460 | { 461 | return p3; 462 | } 463 | if (IsPointOnLine(p1, p2, p4)) 464 | { 465 | return p4; 466 | } 467 | if (IsPointOnLine(p3, p4, p1)) 468 | { 469 | return p1; 470 | } 471 | if (IsPointOnLine(p3, p4, p2)) 472 | { 473 | return p2; 474 | } 475 | float k = fArea(p1, p2, p3) / fArea(p1, p2, p4); 476 | return new Vector3((float)(p3.x + k * p4.x) / (float)(1 + k), 0, (float)(p3.z + k * p4.z) / (float)(1 + k)); 477 | } 478 | 479 | #endregion 480 | 481 | #region 点线关系 482 | 483 | public static bool IsPointAlmostOnSegment(Vector3 lineP1, Vector3 lineP2, float width, Vector3 toCheck) 484 | { 485 | Vector3 _lineDir = (lineP2 - lineP1).normalized; 486 | Quaternion q = QuaternionUtility.Euler(0, 90, 0); 487 | Vector3 _rightDir = (q * _lineDir).normalized; 488 | 489 | Vector3 diff = _rightDir * width - _lineDir * width; 490 | 491 | Vector3 lp1Right = lineP1 + diff; 492 | Vector3 lp1Left = lineP1 - diff; 493 | 494 | Vector3 lp2Right = lineP2 + diff; 495 | Vector3 lp2Left = lineP2 - diff; 496 | 497 | var res = Geometric.IsPointInArea(new[] { lp1Right, lp2Right, lp2Left, lp1Left }, toCheck); 498 | return res; 499 | } 500 | 501 | 502 | /// 503 | /// 点到线距离 504 | /// 505 | /// 点 506 | /// 线点1 507 | /// 线点2 508 | /// 509 | public static float GetDistanceFromPointToLine(Vector3 point, Vector3 a, Vector3 b) 510 | { 511 | if (Mathf.Abs(a.x - b.x) < 1e-6) 512 | { 513 | return Mathf.Abs(point.x - a.x); 514 | } 515 | if (Mathf.Abs(a.z - b.z) < 1e-6) 516 | { 517 | return Mathf.Abs(point.z - a.z); 518 | } 519 | 520 | float area = GetArea(point, a, b); 521 | return (2 * area) / Vector3.Distance(a, b); 522 | } 523 | 524 | /// 525 | /// 点是否在线上 526 | /// 527 | /// 线段点1 528 | /// 线段点2 529 | /// 检查点 530 | /// 531 | public static bool IsPointOnLine(Vector3 line1, Vector3 line2, Vector3 p) 532 | { 533 | float k = (line2.z - line1.z) / (line2.x - line1.x); 534 | float b = (line2.z * line1.x - line1.z * line2.x) / (line1.x - line2.x); 535 | 536 | if (Mathf.Abs(line1.x - line2.x) <= eps && Mathf.Abs(line1.x - p.x) <= eps) 537 | { 538 | return true; 539 | } 540 | 541 | if (Mathf.Abs((p.x * k + b) - p.z) < eps) 542 | { 543 | return true; 544 | } 545 | return false; 546 | } 547 | 548 | /// 549 | /// 是否在线段上 550 | /// 551 | /// 线段点1 552 | /// 线段点2 553 | /// 检查点 554 | /// 555 | public static bool IsPointOnSegment(Vector3 line1, Vector3 line2, Vector3 p) 556 | { 557 | 558 | if (Mathf.Abs(line1.x - line2.x) <= eps && Mathf.Abs(line1.x - p.x) <= eps) 559 | { 560 | float bigZ = line1.z > line2.z ? line1.z : line2.z; 561 | float smallZ = line1.z < line2.z ? line1.z : line2.z; 562 | if (p.z <= bigZ && p.z >= smallZ) 563 | { 564 | return true; 565 | } 566 | } 567 | 568 | float k = (line2.z - line1.z) / (line2.x - line1.x); 569 | float b = (line2.z * line1.x - line1.z * line2.x) / (line1.x - line2.x); 570 | if (Mathf.Abs((p.x * k + b) - p.z) < eps) 571 | { 572 | float maxX = line1.x > line2.x ? line1.x : line2.x; 573 | float minX = line1.x < line2.x ? line1.x : line2.x; 574 | float maxZ = line1.z > line2.z ? line1.z : line2.z; 575 | float minZ = line1.z < line2.z ? line1.z : line2.z; 576 | if (p.x <= maxX && p.x >= minX && p.z <= maxZ && p.z >= minZ) 577 | { 578 | return true; 579 | } 580 | } 581 | return false; 582 | } 583 | 584 | #endregion 585 | 586 | #region 点群 587 | 588 | public static List SortPointsByAngle(List points) 589 | { 590 | DupKeyMap map = new DupKeyMap(); 591 | Vector3 midP = new Vector3(); 592 | foreach (var p in points) 593 | { 594 | midP += p; 595 | } 596 | midP /= points.Count; 597 | foreach (var p in points) 598 | { 599 | map.Add(AbsYAngle(midP, p), p); 600 | } 601 | 602 | map.SortedByKey((a, b) => { return a > b; }); 603 | return map.AllValue; 604 | } 605 | 606 | /// 607 | /// 找出allP中离toCheck最近的点 608 | /// 609 | /// 610 | /// 611 | /// 612 | public static Vector3 GetCloestPoint(Vector3 toCheck, List allP) 613 | { 614 | if (allP == null || allP.Count == 0) 615 | { 616 | return default(Vector3); 617 | } 618 | 619 | Vector3 res = allP[0]; 620 | float minDis = Vector3.Distance(res, toCheck); 621 | for (int i = 0; i < allP.Count; i++) 622 | { 623 | float thisDis = Vector3.Distance(toCheck, allP[i]); 624 | if (thisDis < minDis) 625 | { 626 | minDis = thisDis; 627 | res = allP[i]; 628 | } 629 | } 630 | 631 | return res; 632 | } 633 | 634 | /// 635 | /// 获取点群的凸包 636 | /// 637 | /// 点群集合 638 | /// 凸包点群集合(不保证点序),最后一点重复第一点 639 | public static List GetBoundary(List allPoints) 640 | { 641 | //Drawer.Points(allPoints.ToArray(), Color.blue); 642 | ConvexAogrithm ca = new ConvexAogrithm(allPoints); 643 | ca.GetNodesByAngle(); 644 | 645 | var res = ca.SortedNodes.ToList(); 646 | res.Add(res[0]); 647 | return res; 648 | } 649 | 650 | /// 651 | /// 获取点群的最大闭包矩形 652 | /// 653 | /// 点群集合 654 | /// 最大闭包矩形五点(保证点序,最后一点重复第一点) 655 | public static List GetMaxClosureRect(List allPoints) 656 | { 657 | List res = new List(); 658 | 659 | float maxX = Single.MinValue; 660 | float maxZ = Single.MinValue; 661 | float minX = Single.MaxValue; 662 | float minZ = Single.MaxValue; 663 | 664 | for (int i = 0; i < allPoints.Count; i++) 665 | { 666 | if (allPoints[i].x > maxX) 667 | { 668 | maxX = allPoints[i].x; 669 | } 670 | 671 | if (allPoints[i].z > maxZ) 672 | { 673 | maxZ = allPoints[i].z; 674 | } 675 | 676 | if (allPoints[i].x < minX) 677 | { 678 | minX = allPoints[i].x; 679 | } 680 | 681 | if (allPoints[i].z < minZ) 682 | { 683 | minZ = allPoints[i].z; 684 | } 685 | } 686 | 687 | Vector3 leftUp = new Vector3(minX, 0, maxZ); 688 | Vector3 leftDown = new Vector3(minX, 0, minZ); 689 | Vector3 rightDown = new Vector3(maxX, 0, minZ); 690 | Vector3 rightUp = new Vector3(maxX, 0, maxZ); 691 | 692 | res.Add(leftUp); 693 | res.Add(leftDown); 694 | res.Add(rightDown); 695 | res.Add(rightUp); 696 | 697 | res.Add(leftUp); 698 | 699 | return res; 700 | } 701 | 702 | /// 703 | /// 点群线分 704 | /// 705 | /// 线点1 706 | /// 线点2 707 | /// 点群 708 | /// 709 | public static List> DividePointsByLine(Vector3 a, Vector3 b, 710 | List points) 711 | { 712 | Vector3 abDir = (b - a).normalized;// 线方向 713 | 714 | Dictionary> isOnRightMap = 715 | new Dictionary>(); 716 | isOnRightMap.Add(false, new List()); 717 | isOnRightMap.Add(true, new List()); 718 | for (int i = 0; i < points.Count; i++) 719 | { 720 | bool key = IsOnRight(a, b, points[i]); 721 | if (isOnRightMap.ContainsKey(key)) 722 | { 723 | isOnRightMap[key].Add(points[i]); 724 | } 725 | else 726 | { 727 | isOnRightMap.Add(key, new List() { points[i] }); 728 | } 729 | } 730 | 731 | return isOnRightMap.Values.ToList(); 732 | } 733 | 734 | public static bool IsOnRight(Vector3 a, Vector3 b, Vector3 p) 735 | { 736 | Vector3 abDir = (b - a).normalized;// 线方向 737 | Vector3 apDir = (p - a).normalized;// 点方向 738 | 739 | return ClockYAngle(abDir, apDir) < 180; 740 | } 741 | 742 | public delegate Vector3 GetVector3(T toGet); 743 | 744 | public static List> DividePointsByLine(Vector3 a, Vector3 b, 745 | List points, GetVector3 getter) 746 | { 747 | Dictionary> pointTMap = new Dictionary>(); 748 | List allP = new List(); 749 | for (int i = 0; i < points.Count; i++) 750 | { 751 | var key = getter(points[i]); 752 | if (pointTMap.ContainsKey(key)) 753 | { 754 | pointTMap[key].Add(points[i]); 755 | } 756 | else 757 | { 758 | pointTMap.Add(key, new List() { points[i] }); 759 | } 760 | allP.Add(getter(points[i])); 761 | } 762 | 763 | List> pRes = DividePointsByLine(a, b, allP); 764 | List> res = new List>(); 765 | for (int i = 0; i < pRes.Count; i++) 766 | { 767 | List row = new List(); 768 | for (int j = 0; j < pRes[i].Count; j++) 769 | { 770 | for (int k = 0; k < pointTMap[pRes[i][j]].Count; k++) 771 | { 772 | row.Add(pointTMap[pRes[i][j]][k]); 773 | } 774 | } 775 | res.Add(row); 776 | } 777 | 778 | return res; 779 | } 780 | 781 | #endregion 782 | 783 | #region V3操作 784 | 785 | 786 | public delegate float distanceCalc(Vector3 a, Vector3 b); 787 | /// 788 | /// 比较checker是不是比pin要离target更远 789 | /// 790 | /// 791 | /// 792 | /// 793 | /// 794 | public static bool IsFarFrom(Vector3 target, Vector3 check, Vector3 pin, distanceCalc calc) 795 | { 796 | return calc(target, check) > calc(target, pin); 797 | } 798 | 799 | public static bool IsOnLeft(Vector3 forward, Vector3 toCheck) 800 | { 801 | return Vector3.Cross(forward, toCheck).y < 0; 802 | } 803 | 804 | /// 805 | /// 求y平面A-B的顺时针角度, 806 | /// 807 | /// 808 | /// 809 | /// 810 | public static float ClockAngle(Vector3 a, Vector3 b) 811 | { 812 | float angle = Vector3.Angle(a, b); 813 | if (angle.Equals(0)) 814 | { 815 | return angle; 816 | } 817 | 818 | if (Vector3.Cross(a, b).y >= 0) 819 | { 820 | return angle; 821 | } 822 | else 823 | { 824 | return 360 - angle; 825 | } 826 | } 827 | 828 | /// 829 | /// 求y平面A-B的顺时针角度, 830 | /// 831 | /// 832 | /// 833 | /// 834 | public static float ClockYAngle(Vector3 dirA, Vector3 dirB) 835 | { 836 | float angle = Vector3.Angle(new Vector3(dirA.x, 0, dirA.z), new Vector3(dirB.x, 0, dirB.z)); 837 | if (angle.Equals(0)) 838 | { 839 | return angle; 840 | } 841 | 842 | if (Vector3.Cross(dirA, dirB).y >= 0) 843 | { 844 | return angle; 845 | } 846 | else 847 | { 848 | return 360 - angle; 849 | } 850 | } 851 | 852 | public static float AbsYAngle(Vector3 pointA, Vector3 pointB, Vector3 checkDir) 853 | { 854 | Vector3 ab = pointB - pointA; 855 | ab.y = 0; 856 | Vector3 anx = checkDir; 857 | if (Vector3.Cross(anx, ab).y <= 0) 858 | { 859 | return Vector3.Angle(anx, ab); 860 | } 861 | else 862 | { 863 | return 360 - Vector3.Angle(anx, ab); 864 | } 865 | } 866 | 867 | public static float AbsYAngle(Vector3 pointA, Vector3 pointB) 868 | { 869 | Vector3 ab = pointB - pointA; 870 | ab.y = 0; 871 | Vector3 anx = new Vector3(0, 0, 1); 872 | if (Vector3.Cross(anx, ab).y <= 0) 873 | { 874 | return Vector3.Angle(anx, ab); 875 | } 876 | else 877 | { 878 | return 360 - Vector3.Angle(anx, ab); 879 | } 880 | } 881 | 882 | /// 883 | /// 求y平面A-B的逆时针角度, 884 | /// 885 | /// 886 | /// 887 | /// 888 | public static float DeclockAngle(Vector3 a, Vector3 b) 889 | { 890 | float angle = Vector3.Angle(a, b); 891 | if (angle.Equals(0)) 892 | { 893 | return angle; 894 | } 895 | 896 | if (Vector3.Cross(a, b).y >= 0) 897 | { 898 | return 360 - angle; 899 | } 900 | else 901 | { 902 | return angle; 903 | } 904 | } 905 | 906 | public static bool IsClose(Vector3 a, Vector3 b, float offset = 0.1f) 907 | { 908 | return Vector3.Distance(a, b) < offset; 909 | } 910 | 911 | public static bool Contain(List list, Vector3 toCheck, float offset = 0.1f) 912 | { 913 | for (int i = 0; i < list.Count; i++) 914 | { 915 | if (IsClose(list[i], toCheck, offset)) 916 | { 917 | return true; 918 | } 919 | } 920 | 921 | return false; 922 | } 923 | 924 | #endregion 925 | 926 | #region LineRelated 927 | 928 | public static bool AlmostOnLine(Vector3 lineP1, Vector3 lineP2, float width, Vector3 toCheck) 929 | { 930 | Vector3 _lineDir = (lineP2 - lineP1).normalized; 931 | 932 | lineP1 += _lineDir*99; 933 | lineP2 -= _lineDir*99; 934 | 935 | Quaternion q = QuaternionUtility.Euler(0, 90, 0); 936 | Vector3 _rightDir = (q * _lineDir).normalized; 937 | 938 | Vector3 lp1Right = lineP1 + _rightDir * 0.5f * width - _lineDir * 0.5f * width; 939 | Vector3 lp1Left = lineP1 - _rightDir * 0.5f * width - _lineDir * 0.5f * width; 940 | 941 | Vector3 lp2Right = lineP2 + _rightDir * 0.5f * width + _lineDir * 0.5f * width; 942 | Vector3 lp2Left = lineP2 - _rightDir * 0.5f * width + _lineDir * 0.5f * width; 943 | 944 | return Geometric.IsPointInArea(new[] { lp1Right, lp2Right, lp2Left, lp1Left }, toCheck); 945 | } 946 | 947 | public static Vector3 GetHoverPoint(Vector3 lp1, Vector3 lp2, Vector3 modelP, float attachR) 948 | { 949 | var _area = Geometric.GetArea(lp1, lp2, modelP);//三角形面积 950 | var _lLength = Vector3.Distance(lp1, lp2);//lp1,lp2长度 951 | var dis = _lLength < 1e-3 ? attachR + 1 : 2 * _area / _lLength;//modelP到lp1,lp2的距离 952 | if (dis > attachR)//太远 953 | { 954 | return default(Vector3); 955 | } 956 | 957 | Vector3 lDir = lp1 - lp2;//直线方向 958 | Vector3 mDir = QuaternionUtility.Euler(0, 90, 0) * lDir;//xz平面上垂直直线方向 959 | Vector3 __S = modelP - mDir * 999; 960 | Vector3 __E = modelP + mDir * 999;//临时的两个线点 961 | 962 | if (!Geometric.Meet(__S, __E, lp1, lp2))//不相交 963 | { 964 | return default(Vector3); 965 | } 966 | 967 | //到这里还能执行的话是相交且接近, 就要计算点 968 | Vector3 res = Geometric.GetCorssPointOfSegment(__E, __S, lp1, lp2); 969 | return res; 970 | } 971 | 972 | public static float GetHoverDis(Vector3 lp1, Vector3 lp2, Vector3 modelP) 973 | { 974 | var _lp1 = new Vector3(lp1.x, 0, lp1.z); 975 | var _lp2 = new Vector3(lp2.x, 0, lp2.z); 976 | var _modelP = new Vector3(modelP.x, 0, modelP.z); 977 | 978 | var _area = Geometric.GetArea(_lp1, _lp2, _modelP);//三角形面积 979 | var _lLength = Vector3.Distance(_lp1, _lp2);//lp1,lp2长度 980 | var dis = 2 * _area / _lLength;//modelP到lp1,lp2的距离 981 | return dis; 982 | } 983 | 984 | public static bool IsHover(Vector3 lp1, Vector3 lp2, Vector3 modelP) 985 | { 986 | return !GetHoverPoint(lp1, lp2, modelP, 999).Equals(default(Vector3)); 987 | } 988 | 989 | public static Vector3 GetHoverPointOfLine(Vector3 lp1, Vector3 lp2, Vector3 modelP, float attachR) 990 | { 991 | var _area = Geometric.GetArea(lp1, lp2, modelP);//三角形面积 992 | var _lLength = Vector3.Distance(lp1, lp2);//lp1,lp2长度 993 | var dis = _lLength < 1e-3 ? attachR + 1 : 2 * _area / _lLength;//modelP到lp1,lp2的距离 994 | if (dis > attachR)//太远 995 | { 996 | return default(Vector3); 997 | } 998 | 999 | Vector3 lDir = lp1 - lp2;//直线方向 1000 | Vector3 mDir = QuaternionUtility.Euler(0, 90, 0) * lDir;//xz平面上垂直直线方向 1001 | Vector3 __S = modelP - mDir * 99; 1002 | Vector3 __E = modelP + mDir * 99;//临时的两个线点 1003 | 1004 | //if (!Geometric.Meet(__S, __E, lp1, lp2))//不相交 1005 | //{ 1006 | // return default(Vector3); 1007 | //} 1008 | 1009 | //到这里还能执行的话是相交且接近, 就要计算点 1010 | Vector3 res = Geometric.GetCrossPointOfLine(__E, __S, lp1, lp2); 1011 | 1012 | if ((Mathf.Abs(res.x - modelP.x) < 0.01f)) 1013 | { 1014 | res.x = modelP.x; 1015 | } 1016 | 1017 | if ((Mathf.Abs(res.z - modelP.z) < 0.01f)) 1018 | { 1019 | res.z = modelP.z; 1020 | } 1021 | 1022 | return res; 1023 | } 1024 | 1025 | public static Vector3 GetHoverPointOf3DLine(Vector3 lp1, Vector3 lp2, Vector3 modelP) 1026 | { 1027 | float y = Vector3.Distance(lp2, modelP); 1028 | float z = Vector3.Distance(lp1, modelP); 1029 | float x = Vector3.Distance(lp1, lp2); 1030 | float q = (x + y + z) * 0.5f; 1031 | float area = Mathf.Sqrt(q * (q - x) * (q - y) * (q - z)); 1032 | 1033 | float hoverDis = area / x; 1034 | float fromyP = Mathf.Sqrt(y * y - hoverDis * hoverDis); 1035 | 1036 | return lp2 + (lp1 - lp2).normalized * fromyP; 1037 | } 1038 | #endregion 1039 | 1040 | /// 1041 | /// 特殊的舍入进位算法 1042 | /// 1043 | /// 需要变化的数值 1044 | /// 保留到10^atIndex位 1045 | /// 位移步长,res=0+(i*step*2) 1046 | /// 1047 | public static double Round(double value, int atIndex = 0, float step = 0) 1048 | { 1049 | bool isNegative = false; 1050 | //如果是负数 1051 | if (value < 0) 1052 | { 1053 | isNegative = true; 1054 | value = -value; 1055 | } 1056 | double absValue = Math.Abs(value); 1057 | 1058 | double iValue = Math.Pow(10, atIndex);// 扩张值 1059 | 1060 | double thisBigInt = Math.Round(value * iValue, 0);// 取出扩张后的整数位 1061 | 1062 | if (thisBigInt % 2 != 0) 1063 | { 1064 | if (thisBigInt < absValue * iValue) 1065 | { 1066 | // 向下位移 1067 | thisBigInt -= step; 1068 | } 1069 | else 1070 | { 1071 | // 向上位移 1072 | thisBigInt += step; 1073 | } 1074 | } 1075 | 1076 | double res = thisBigInt / iValue; 1077 | 1078 | if (isNegative) 1079 | { 1080 | res = -res; 1081 | } 1082 | 1083 | return res; 1084 | } 1085 | } 1086 | } 1087 | --------------------------------------------------------------------------------