├── Captures ├── demo.gif ├── input.png ├── drawing.gif ├── mesh_refinement.png └── delaunay_triangulation.png ├── Triangulation2D.unitypackage ├── Assets ├── Packages │ ├── Triangulation2D │ │ ├── Demo │ │ │ ├── Scenes │ │ │ │ ├── Demo.unity │ │ │ │ └── Demo.unity.meta │ │ │ ├── Materials │ │ │ │ ├── DemoMesh.mat │ │ │ │ ├── CanvasLine.mat │ │ │ │ ├── DemoMeshLine.mat │ │ │ │ ├── CanvasLine.mat.meta │ │ │ │ ├── DemoMesh.mat.meta │ │ │ │ └── DemoMeshLine.mat.meta │ │ │ ├── Prefabs │ │ │ │ ├── DemoMesh.prefab │ │ │ │ └── DemoMesh.prefab.meta │ │ │ ├── Prefabs.meta │ │ │ ├── Scenes.meta │ │ │ ├── Scripts.meta │ │ │ ├── Shaders.meta │ │ │ ├── Materials.meta │ │ │ ├── Shaders │ │ │ │ ├── DemoLine.shader.meta │ │ │ │ ├── DemoMesh.shader.meta │ │ │ │ ├── SimplexNoise3D.cginc.meta │ │ │ │ ├── DemoLine.shader │ │ │ │ ├── DemoMesh.shader │ │ │ │ └── SimplexNoise3D.cginc │ │ │ └── Scripts │ │ │ │ ├── Demo.cs.meta │ │ │ │ ├── DemoMesh.cs.meta │ │ │ │ ├── LocalStorage.cs.meta │ │ │ │ ├── LocalStorage.cs │ │ │ │ ├── DemoMesh.cs │ │ │ │ └── Demo.cs │ │ ├── Demo.meta │ │ ├── Scripts.meta │ │ └── Scripts │ │ │ ├── Circle2D.cs.meta │ │ │ ├── HalfEdge2D.cs.meta │ │ │ ├── Polygon2D.cs.meta │ │ │ ├── Segment2D.cs.meta │ │ │ ├── Triangle2D.cs.meta │ │ │ ├── Utils2D.cs.meta │ │ │ ├── Vertex2D.cs.meta │ │ │ ├── Triangulation2D.cs.meta │ │ │ ├── HalfEdge2D.cs │ │ │ ├── Vertex2D.cs │ │ │ ├── Circle2D.cs │ │ │ ├── Segment2D.cs │ │ │ ├── Utils2D.cs │ │ │ ├── Triangle2D.cs │ │ │ ├── Polygon2D.cs │ │ │ └── Triangulation2D.cs │ └── Triangulation2D.meta ├── StreamingAssets │ ├── points.json.meta │ └── points.json ├── Packages.meta └── StreamingAssets.meta ├── .gitignore ├── LICENSE └── README.md /Captures/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Captures/demo.gif -------------------------------------------------------------------------------- /Captures/input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Captures/input.png -------------------------------------------------------------------------------- /Captures/drawing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Captures/drawing.gif -------------------------------------------------------------------------------- /Captures/mesh_refinement.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Captures/mesh_refinement.png -------------------------------------------------------------------------------- /Triangulation2D.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Triangulation2D.unitypackage -------------------------------------------------------------------------------- /Captures/delaunay_triangulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Captures/delaunay_triangulation.png -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scenes/Demo.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Assets/Packages/Triangulation2D/Demo/Scenes/Demo.unity -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/DemoMesh.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Assets/Packages/Triangulation2D/Demo/Materials/DemoMesh.mat -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/CanvasLine.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Assets/Packages/Triangulation2D/Demo/Materials/CanvasLine.mat -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Prefabs/DemoMesh.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Assets/Packages/Triangulation2D/Demo/Prefabs/DemoMesh.prefab -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/DemoMeshLine.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mattatz/unity-triangulation2D/HEAD/Assets/Packages/Triangulation2D/Demo/Materials/DemoMeshLine.mat -------------------------------------------------------------------------------- /Assets/StreamingAssets/points.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3dacea7ed195b41b1835a4e5ae1a28ba 3 | timeCreated: 1466595638 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 95a092943260541dbb5e61ee961f7dac 3 | folderAsset: yes 4 | timeCreated: 1465912480 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scenes/Demo.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 99428221878334a8683529a91c6d2b53 3 | timeCreated: 1466517357 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/StreamingAssets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d46e6b8408e7f4ac79ecf70f4ba4eba8 3 | folderAsset: yes 4 | timeCreated: 1466595611 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 701d6dbf589ea4376b6b9f97ec9764d2 3 | folderAsset: yes 4 | timeCreated: 1465912489 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/CanvasLine.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3cdb56c432dc643c99684f1e7daff9b8 3 | timeCreated: 1466516387 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/DemoMesh.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad59dae05c2214026b6e4097e9fe27d2 3 | timeCreated: 1466515189 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Prefabs/DemoMesh.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 01293f85e33724cffb1f6673f3588572 3 | timeCreated: 1466694480 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6d5eeb6844b10497092a5fac3e2ab776 3 | folderAsset: yes 4 | timeCreated: 1465912651 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials/DemoMeshLine.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0eacebf3e8c64a399242fae6d8dcbc8 3 | timeCreated: 1466694534 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3207a8a0bde6547718c98ca16d9b64ba 3 | folderAsset: yes 4 | timeCreated: 1466754477 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 02f1bde77e07549fe8c002581af4bf39 3 | folderAsset: yes 4 | timeCreated: 1466692500 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57b5bb180f19c43439bb92113c0685ec 3 | folderAsset: yes 4 | timeCreated: 1466692452 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 22ae8b0dfc64b479894a635234365124 3 | folderAsset: yes 4 | timeCreated: 1466692462 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4548c7205de644ae984fba97a98543a6 3 | folderAsset: yes 4 | timeCreated: 1466692475 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Materials.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7ee545cc6c30e4a3181546264346d7ab 3 | folderAsset: yes 4 | timeCreated: 1466692483 5 | licenseType: Free 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/DemoLine.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21e9e5ce29c1c4bf18deb2895e31bd70 3 | timeCreated: 1466516398 4 | licenseType: Free 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/DemoMesh.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5ea78bc862e0b4acc886e363496717b6 3 | timeCreated: 1466515194 4 | licenseType: Free 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/SimplexNoise3D.cginc.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27095d50a08104a8b8c1bd0cc39ee427 3 | timeCreated: 1466758276 4 | licenseType: Free 5 | ShaderImporter: 6 | defaultTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Ll]ibrary/ 2 | [Tt]emp/ 3 | [Oo]bj/ 4 | [Bb]uild/ 5 | [Pp]rojectSettings/ 6 | Papers/ 7 | 8 | # Autogenerated VS/MD solution and project files 9 | *.csproj 10 | *.unityproj 11 | *.sln 12 | *.suo 13 | *.user 14 | *.userprefs 15 | *.pidb 16 | *.booproj 17 | 18 | # Unity3D Generated File On Crash Reports 19 | sysinfo.txt -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/Demo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 77e8e35d5f324406abb67b63ec25336b 3 | timeCreated: 1466757756 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Circle2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4a27231bfb4241f59d009b3ef7a7f55 3 | timeCreated: 1466752903 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/HalfEdge2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd14051ec1efb42118dd17e727f7b467 3 | timeCreated: 1466753012 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Polygon2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a4073f796194c44e5b175af9d4b35f40 3 | timeCreated: 1466753084 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Segment2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1f518ac0328164dc79a8882211b12ece 3 | timeCreated: 1466753053 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Triangle2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9f67eb9a35bd43fbafca4352a90655d 3 | timeCreated: 1466753132 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Utils2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 55e48844b4a884e5286a8968e1dcb026 3 | timeCreated: 1466753182 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Vertex2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e02b555c78fb545a09fcf73891eb2d3e 3 | timeCreated: 1466752955 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/DemoMesh.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bcb5140256a2f492abf35ca80af1c53a 3 | timeCreated: 1466693882 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Triangulation2D.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a78fb95b758004930bc6358a8f554b36 3 | timeCreated: 1465912497 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/LocalStorage.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 179a565d3dde349a8ba57b9d77f383ae 3 | timeCreated: 1466755694 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/HalfEdge2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class HalfEdge2D { 11 | public Vector2 p; 12 | public HalfEdge2D from, to; 13 | public HalfEdge2D(Vector2 p) { 14 | this.p = p; 15 | } 16 | public void Invert () { 17 | var tmp = from; from = to; to = tmp; 18 | } 19 | public HalfEdge2D Split () { 20 | var m = (to.p + p) * 0.5f; 21 | var e = new HalfEdge2D(m); 22 | to.from = e; e.to = to; 23 | this.to = e; e.from = this; 24 | return e; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Vertex2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Vertex2D { 11 | public int ReferenceCount { get { return reference; } } 12 | public Vector2 Coordinate { get { return coordinate; } } 13 | 14 | Vector2 coordinate; 15 | int reference; 16 | 17 | public Vertex2D (Vector2 coord) { 18 | coordinate = coord; 19 | } 20 | 21 | public int Increment () { 22 | return ++reference; 23 | } 24 | 25 | public int Decrement () { 26 | return --reference; 27 | } 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/DemoLine.shader: -------------------------------------------------------------------------------- 1 | Shader "Triangulation2D/Demo/Line" { 2 | 3 | Properties { 4 | _Color ("Color", Color) = (0, 0, 1, 1) 5 | } 6 | 7 | SubShader { 8 | Tags { "RenderType"="Opaque" } 9 | LOD 100 10 | 11 | CGINCLUDE 12 | 13 | #include "UnityCG.cginc" 14 | 15 | #pragma target 3.0 16 | 17 | fixed4 _Color; 18 | 19 | struct appdata { 20 | float4 vertex : POSITION; 21 | float3 normal : NORMAL; 22 | float2 uv : TEXCOORD0; 23 | }; 24 | 25 | struct v2f { 26 | float4 vertex : SV_POSITION; 27 | float3 normal : NORMAL; 28 | float2 uv : TEXCOORD0; 29 | }; 30 | 31 | v2f vert (appdata v) { 32 | v2f o; 33 | o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 34 | o.normal = v.normal; 35 | o.uv = v.uv; 36 | return o; 37 | } 38 | 39 | ENDCG 40 | 41 | Pass { 42 | CGPROGRAM 43 | #pragma vertex vert 44 | #pragma fragment frag 45 | fixed4 frag (v2f i) : SV_Target { 46 | return _Color; 47 | } 48 | ENDCG 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 mattatz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/DemoMesh.shader: -------------------------------------------------------------------------------- 1 | Shader "Triangulation2D/Demo/Mesh" { 2 | 3 | Properties { 4 | } 5 | 6 | SubShader { 7 | Tags { "RenderType"="Opaque" } 8 | LOD 100 9 | 10 | CGINCLUDE 11 | 12 | #include "UnityCG.cginc" 13 | 14 | #pragma target 3.0 15 | 16 | struct appdata { 17 | float4 vertex : POSITION; 18 | float3 normal : NORMAL; 19 | float2 uv : TEXCOORD0; 20 | }; 21 | 22 | struct v2f { 23 | float4 vertex : SV_POSITION; 24 | float3 normal : NORMAL; 25 | float2 uv : TEXCOORD0; 26 | }; 27 | 28 | v2f vert (appdata v) { 29 | v2f o; 30 | o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 31 | o.normal = mul(unity_ObjectToWorld, float4(v.normal, 0)).xyz; 32 | o.uv = v.uv; 33 | return o; 34 | } 35 | 36 | fixed3 normal_color (fixed3 norm) { 37 | return (normalize(norm) + 1.0) * 0.5; 38 | } 39 | 40 | ENDCG 41 | 42 | Pass { 43 | Cull Front 44 | CGPROGRAM 45 | #pragma vertex vert 46 | #pragma fragment frag 47 | fixed4 frag (v2f i) : SV_Target { 48 | fixed4 col = fixed4(normal_color(i.normal), 1); 49 | return col; 50 | } 51 | ENDCG 52 | } 53 | 54 | Pass { 55 | Cull Back 56 | CGPROGRAM 57 | #pragma vertex vert 58 | #pragma fragment frag 59 | fixed4 frag (v2f i) : SV_Target { 60 | fixed4 col = fixed4(normal_color(-i.normal), 1); 61 | return col; 62 | } 63 | ENDCG 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/LocalStorage.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | 9 | namespace mattatz.Utils { 10 | 11 | [Serializable] 12 | public class JsonSerialization { 13 | [SerializeField] List target; 14 | public List ToList() { return target; } 15 | public JsonSerialization(List target) { 16 | this.target = target; 17 | } 18 | } 19 | 20 | public class LocalStorage { 21 | 22 | public static void SaveList (List target, string fileName) { 23 | var text = JsonUtility.ToJson(new JsonSerialization(target)); 24 | Save(text, fileName); 25 | } 26 | 27 | public static List LoadList (string fileName) { 28 | string json = Load(fileName); 29 | return JsonUtility.FromJson>(json).ToList(); 30 | } 31 | 32 | public static void Save (string text, string fileName) { 33 | string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileName); 34 | FileInfo fi = new FileInfo(path); 35 | StreamWriter sw = fi.CreateText(); 36 | sw.WriteLine(text); 37 | sw.Flush(); 38 | sw.Close(); 39 | } 40 | 41 | public static string Load (string fileName) { 42 | string path = System.IO.Path.Combine(Application.streamingAssetsPath, fileName); 43 | return System.IO.File.ReadAllText(path); 44 | } 45 | 46 | } 47 | 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/DemoMesh.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using Random = UnityEngine.Random; 3 | 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | 7 | namespace mattatz.Triangulation2DSystem.Example { 8 | 9 | [RequireComponent (typeof(MeshFilter))] 10 | [RequireComponent (typeof(Rigidbody))] 11 | public class DemoMesh : MonoBehaviour { 12 | 13 | [SerializeField] Material lineMat; 14 | 15 | Triangle2D[] triangles; 16 | 17 | void Start () { 18 | var body = GetComponent(); 19 | body.AddForce(Vector3.forward * Random.Range(150f, 160f)); 20 | body.AddTorque(Random.insideUnitSphere * Random.Range(10f, 20f)); 21 | } 22 | 23 | void Update () {} 24 | 25 | public void SetTriangulation (Triangulation2D triangulation) { 26 | var mesh = triangulation.Build(); 27 | GetComponent().sharedMesh = mesh; 28 | this.triangles = triangulation.Triangles; 29 | } 30 | 31 | void OnRenderObject () { 32 | if(triangles == null) return; 33 | 34 | GL.PushMatrix(); 35 | GL.MultMatrix (transform.localToWorldMatrix); 36 | 37 | lineMat.SetColor("_Color", Color.black); 38 | lineMat.SetPass(0); 39 | GL.Begin(GL.LINES); 40 | for(int i = 0, n = triangles.Length; i < n; i++) { 41 | var t = triangles[i]; 42 | GL.Vertex(t.s0.a.Coordinate); GL.Vertex(t.s0.b.Coordinate); 43 | GL.Vertex(t.s1.a.Coordinate); GL.Vertex(t.s1.b.Coordinate); 44 | GL.Vertex(t.s2.a.Coordinate); GL.Vertex(t.s2.b.Coordinate); 45 | } 46 | GL.End(); 47 | GL.PopMatrix(); 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Circle2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Circle2D { 11 | public Vector2 center; 12 | public float radius; 13 | 14 | public Circle2D (Vector2 c, float r) { 15 | this.center = c; 16 | this.radius = r; 17 | } 18 | 19 | public bool Contains (Vector2 p) { 20 | return (p - center).magnitude < radius; 21 | } 22 | 23 | public static Circle2D GetCircumscribedCircle(Triangle2D triangle) { 24 | var x1 = triangle.a.Coordinate.x; 25 | var y1 = triangle.a.Coordinate.y; 26 | var x2 = triangle.b.Coordinate.x; 27 | var y2 = triangle.b.Coordinate.y; 28 | var x3 = triangle.c.Coordinate.x; 29 | var y3 = triangle.c.Coordinate.y; 30 | 31 | float x1_2 = x1 * x1; 32 | float x2_2 = x2 * x2; 33 | float x3_2 = x3 * x3; 34 | float y1_2 = y1 * y1; 35 | float y2_2 = y2 * y2; 36 | float y3_2 = y3 * y3; 37 | 38 | // 外接円の中心座標を計算 39 | float c = 2f * ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)); 40 | float x = ((y3 - y1) * (x2_2 - x1_2 + y2_2 - y1_2) + (y1 - y2) * (x3_2 - x1_2 + y3_2 - y1_2)) / c; 41 | float y = ((x1 - x3) * (x2_2 - x1_2 + y2_2 - y1_2) + (x2 - x1) * (x3_2 - x1_2 + y3_2 - y1_2)) / c; 42 | float _x = (x1 - x); 43 | float _y = (y1 - y); 44 | 45 | float r = Mathf.Sqrt((_x * _x) + (_y * _y)); 46 | return new Circle2D(new Vector2(x, y), r); 47 | } 48 | 49 | public void DrawGizmos (float step = 0.02f) { 50 | 51 | var points = new List(); 52 | for(float t = 0f; t <= 1f; t += step) { 53 | var r = t * Mathf.PI * 2f; 54 | float x = Mathf.Cos(r) * radius; 55 | float y = Mathf.Sin(r) * radius; 56 | points.Add(center + new Vector2(x, y)); 57 | } 58 | 59 | for(int i = 0, n = points.Count; i < n; i++) { 60 | var p0 = points[i]; 61 | var p1 = points[(i + 1) % n]; 62 | Gizmos.DrawLine(p0, p1); 63 | } 64 | 65 | } 66 | 67 | } 68 | 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | unity-triangulation2D 2 | ===================== 3 | 4 | Delaunay Triangulation and Ruppert's Delaunay Refinement Algorithm in Unity. 5 | 6 | 7 | 8 | Input contour points for [planar straight-line graph](https://en.wikipedia.org/wiki/Planar_straight-line_graph) 9 | 10 | 11 | 12 | Delaunay Triangulation 13 | 14 | 15 | 16 | Mesh Refinement with minimum angle α(22.5) 17 | 18 | 19 | 20 | ## Usage 21 | 22 | ```cs 23 | // input points for a polygon2D contor 24 | List points = new List(); 25 | 26 | // Add Vector2 to points 27 | points.Add(new Vector2(-2.5f, -2.5f)); 28 | points.Add(new Vector2(2.5f, -2.5f)); 29 | points.Add(new Vector2(4.5f, 2.5f)); 30 | points.Add(new Vector2(0.5f, 4.5f)); 31 | points.Add(new Vector2(-3.5f, 2.5f)); 32 | 33 | // construct Polygon2D 34 | Polygon2D polygon = Polygon2D.Contour(points.ToArray()); 35 | 36 | // construct Triangulation2D with Polygon2D and threshold angle (18f ~ 27f recommended) 37 | Triangulation2D triangulation = new Triangulation2D(polygon, 22.5f); 38 | 39 | // build a mesh from triangles in a Triangulation2D instance 40 | Mesh mesh = triangulation.Build(); 41 | // GetComponent().sharedMesh = mesh; 42 | ``` 43 | 44 | ## Demo 45 | 46 | 47 | 48 | ## Sources 49 | 50 | - Jim Ruppert. A Delaunay Refinement Algorithm for Quality 2-Dimensional Mesh Generation - http://www.cis.upenn.edu/~cis610/ruppert.pdf 51 | 52 | - Ruppert's algorithm - https://en.wikipedia.org/wiki/Ruppert%27s_algorithm 53 | 54 | - Ruppert's Delaunay Refinement Algorithm - https://www.cs.cmu.edu/~quake/tripaper/triangle3.html 55 | 56 | - Chapter 7.pdf - http://www.ti.inf.ethz.ch/ew/Lehre/CG13/lecture/Chapter%207.pdf 57 | 58 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Segment2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Segment2D { 11 | 12 | public int ReferenceCount { get { return reference; } } 13 | public Vertex2D a, b; 14 | 15 | int reference; 16 | float length; 17 | 18 | public Segment2D (Vertex2D a, Vertex2D b) { 19 | this.a = a; 20 | this.b = b; 21 | } 22 | 23 | public Vector2 Midpoint () { 24 | return (a.Coordinate + b.Coordinate) * 0.5f; 25 | } 26 | 27 | public float Length () { 28 | if(length <= 0f) { 29 | length = (a.Coordinate - b.Coordinate).magnitude; 30 | } 31 | return length; 32 | } 33 | 34 | /* 35 | * check a given point "p" lies within diametral circle of segment(a, b) 36 | */ 37 | public bool EncroachedUpon (Vector2 p) { 38 | if(p == a.Coordinate || p == b.Coordinate) return false; 39 | var radius = (a.Coordinate - b.Coordinate).magnitude * 0.5f; 40 | return (Midpoint() - p).magnitude < radius; 41 | } 42 | 43 | const float epsilon = 0.0001f; 44 | public bool On (Vector2 p) { 45 | if(HasPoint(p)) return true; 46 | if(Distance(p) > epsilon) return false; 47 | 48 | Vector2 p0 = a.Coordinate, p1 = b.Coordinate; 49 | bool bx = (p0.x < p1.x) ? (p0.x <= p.x && p.x <= p1.x) : (p1.x <= p.x && p.x <= p0.x); 50 | bool by = (p0.y < p1.y) ? (p0.y <= p.y && p.y <= p1.y) : (p1.y <= p.y && p.y <= p0.y); 51 | return bx && by; 52 | } 53 | 54 | public bool On (Vertex2D v) { 55 | return On(v.Coordinate); 56 | } 57 | 58 | // https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line 59 | public float Distance (Vector2 p) { 60 | Vector2 p0 = a.Coordinate, p1 = b.Coordinate; 61 | float dx = (p1.x - p0.x), dy = (p1.y - p0.y); 62 | return Mathf.Abs((dy * p.x) - (dx * p.y) + (p1.x * p0.y) - (p1.y * p0.x)) / Mathf.Sqrt(dy * dy + dx * dx); 63 | } 64 | 65 | public float Distance (Vertex2D v) { 66 | return Distance(v.Coordinate); 67 | } 68 | 69 | public bool HasPoint (Vertex2D v) { 70 | return (a == v) || (b == v); 71 | } 72 | 73 | public bool HasPoint (Vector2 p) { 74 | return (a.Coordinate == p) || (b.Coordinate == p); 75 | } 76 | 77 | public int Increment () { 78 | a.Increment(); 79 | b.Increment(); 80 | return ++reference; 81 | } 82 | 83 | public int Decrement () { 84 | a.Decrement(); 85 | b.Decrement(); 86 | return --reference; 87 | } 88 | 89 | public void DrawGizmos () { 90 | Gizmos.DrawLine(a.Coordinate, b.Coordinate); 91 | } 92 | } 93 | 94 | } 95 | 96 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Scripts/Demo.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | #if UNITY_EDITOR 4 | using UnityEditor; 5 | #endif 6 | 7 | using System; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Collections; 11 | using System.Collections.Generic; 12 | 13 | using mattatz.Utils; 14 | 15 | namespace mattatz.Triangulation2DSystem.Example { 16 | 17 | public class Demo : MonoBehaviour { 18 | 19 | [SerializeField, Range(10f, 30f)] float angle = 20f; 20 | [SerializeField, Range(0.2f, 2f)] float threshold = 1.5f; 21 | [SerializeField] GameObject prefab; 22 | [SerializeField] Material lineMat; 23 | [SerializeField] bool debug; 24 | 25 | List points; 26 | 27 | Camera cam; 28 | float depth = 0f; 29 | bool dragging; 30 | 31 | void Start () { 32 | cam = Camera.main; 33 | depth = Mathf.Abs(cam.transform.position.z - transform.position.z); 34 | points = new List(); 35 | 36 | if(debug) { 37 | points = LocalStorage.LoadList("points.json"); 38 | Build(); 39 | } 40 | } 41 | 42 | void Update () { 43 | if(Input.GetMouseButtonDown(0)) { 44 | dragging = true; 45 | Clear(); 46 | } else if(Input.GetMouseButtonUp(0)) { 47 | // LocalStorage.SaveList(points, "points.json"); 48 | 49 | dragging = false; 50 | Build(); 51 | } 52 | 53 | if(dragging) { 54 | var screen = Input.mousePosition; 55 | screen.z = depth; 56 | var p = cam.ScreenToWorldPoint(screen); 57 | var p2D = new Vector2(p.x, p.y); 58 | if(points.Count <= 0 || Vector2.Distance(p2D, points.Last()) > threshold) { 59 | points.Add(p2D); 60 | } 61 | } 62 | } 63 | 64 | void Build () { 65 | points = Utils2D.Constrain(points, threshold); 66 | var polygon = Polygon2D.Contour(points.ToArray()); 67 | 68 | var vertices = polygon.Vertices; 69 | if(vertices.Length < 3) return; // error 70 | 71 | var triangulation = new Triangulation2D(polygon, angle); 72 | var go = Instantiate(prefab); 73 | go.transform.SetParent(transform, false); 74 | go.GetComponent().SetTriangulation(triangulation); 75 | 76 | Clear(); 77 | } 78 | 79 | void Clear () { 80 | points.Clear(); 81 | } 82 | 83 | void OnRenderObject () { 84 | if(points != null) { 85 | GL.PushMatrix(); 86 | GL.MultMatrix (transform.localToWorldMatrix); 87 | lineMat.SetColor("_Color", Color.white); 88 | lineMat.SetPass(0); 89 | GL.Begin(GL.LINES); 90 | for(int i = 0, n = points.Count - 1; i < n; i++) { 91 | GL.Vertex(points[i]); GL.Vertex(points[i + 1]); 92 | } 93 | GL.End(); 94 | GL.PopMatrix(); 95 | } 96 | } 97 | 98 | void OnDrawGizmos () { 99 | } 100 | 101 | } 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /Assets/StreamingAssets/points.json: -------------------------------------------------------------------------------- 1 | {"target":[{"x":-4.8453850746154789,"y":1.3892998695373536},{"x":-5.2785868644714359,"y":0.9979575276374817},{"x":-5.869033336639404,"y":0.6435996890068054},{"x":-6.459702014923096,"y":0.48613253235816958},{"x":-6.991237163543701,"y":0.48613253235816958},{"x":-7.050149917602539,"y":1.0176684856414796},{"x":-6.754924774169922,"y":1.4900699853897095},{"x":-6.164478778839111,"y":1.824715256690979},{"x":-5.5540995597839359,"y":1.9821840524673463},{"x":-5.80525016784668,"y":2.4869205951690676},{"x":-5.962717056274414,"y":3.0184550285339357},{"x":-6.021850109100342,"y":3.5894126892089845},{"x":-5.687204360961914,"y":4.042103290557861},{"x":-5.293425559997559,"y":4.416172027587891},{"x":-4.702756881713867,"y":4.790239334106445},{"x":-4.112310409545898,"y":4.969852924346924},{"x":-3.5610642433166506,"y":5.0878987312316898},{"x":-3.009817123413086,"y":5.0878987312316898},{"x":-2.4388606548309328,"y":4.927995204925537},{"x":-2.0255916118621828,"y":4.475305080413818},{"x":-1.927035927772522,"y":3.8060126304626467},{"x":-1.927035927772522,"y":3.077587842941284},{"x":-2.0453033447265627,"y":2.3097424507141115},{"x":-2.0453033447265627,"y":1.7192943096160889},{"x":-2.0453033447265627,"y":1.0694924592971802},{"x":-2.0453033447265627,"y":0.4987567365169525},{"x":-1.631812572479248,"y":-0.03277838975191116},{"x":-1.0805667638778687,"y":-0.1707558035850525},{"x":-0.4898983836174011,"y":-0.22966744005680085},{"x":0.45490604639053347,"y":-0.22966744005680085},{"x":1.6165307760238648,"y":-0.22966744005680085},{"x":2.6204683780670168,"y":0.12468960136175156},{"x":3.8607177734375,"y":0.7151363492012024},{"x":4.333118915557861,"y":0.9908708333969116},{"x":5.278143405914307,"y":1.758495569229126},{"x":6.045991897583008,"y":2.467209815979004},{"x":6.931883811950684,"y":3.175922155380249},{"x":7.364863395690918,"y":3.5499908924102785},{"x":7.837484836578369,"y":3.8454349040985109},{"x":8.526488304138184,"y":3.96347975730896},{"x":9.116934776306153,"y":3.786301612854004},{"x":8.998888969421387,"y":3.0995140075683595},{"x":8.841421127319336,"y":2.4696452617645265},{"x":8.782289505004883,"y":1.839776635169983},{"x":9.077733993530274,"y":1.3082408905029297},{"x":9.176068305969239,"y":0.48126113414764407},{"x":9.176068305969239,"y":-0.22745294868946076},{"x":9.176068305969239,"y":-0.7786989808082581},{"x":9.116934776306153,"y":-1.4282792806625367},{"x":8.723376274108887,"y":-1.8417692184448243},{"x":8.250755310058594,"y":-2.314171552658081},{"x":7.778351306915283,"y":-2.7865731716156008},{"x":6.951592922210693,"y":-3.554419755935669},{"x":5.691634178161621,"y":-4.440311908721924},{"x":4.451385021209717,"y":-5.149024963378906},{"x":3.624405860900879,"y":-5.503382682800293},{"x":2.206977367401123,"y":-6.015206813812256},{"x":1.3802188634872437,"y":-6.192384719848633},{"x":0.6123729944229126,"y":-6.310429573059082},{"x":-0.15525254607200623,"y":-6.310429573059082},{"x":-1.139699935913086,"y":-6.310429573059082},{"x":-1.9073253870010377,"y":-5.956072807312012},{"x":-2.4979937076568605,"y":-5.601716041564941},{"x":-2.8523502349853517,"y":-5.188447952270508},{"x":-3.2855517864227297,"y":-4.518934726715088},{"x":-3.580775022506714,"y":-4.007110595703125},{"x":-3.757953643798828,"y":-3.4755754470825197},{"x":-3.994044065475464,"y":-2.7079505920410158},{"x":-4.033466815948486,"y":-2.0189473628997804},{"x":-4.033466815948486,"y":-1.0147895812988282},{"x":-3.738241672515869,"y":-0.30607548356056216},{"x":-3.8957102298736574,"y":0.26488152146339419}]} 2 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Utils2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Utils2D { 11 | 12 | // constrain a distance between two points to "threshold" length 13 | public static List Constrain (List points, float threshold = 1f) { 14 | var result = new List(); 15 | 16 | var n = points.Count; 17 | for(int i = 0, j = 1; i < n && j < n; j++) { 18 | var from = points[i]; 19 | var to = points[j]; 20 | if(Vector2.Distance(from, to) > threshold) { 21 | result.Add(from); 22 | i = j; 23 | } 24 | } 25 | 26 | var p0 = result.Last(); 27 | var p1 = result.First(); 28 | if(Vector2.Distance(p0, p1) > threshold) { 29 | result.Add((p0 + p1) * 0.5f); 30 | } 31 | 32 | return result; 33 | } 34 | 35 | // http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect 36 | // check intersection segment (p0, p1) to segment (p2, p3) 37 | public static bool Intersect (Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) { 38 | var s1 = p1 - p0; 39 | var s2 = p3 - p2; 40 | var s = (-s1.y * (p0.x - p2.x) + s1.x * (p0.y - p2.y)) / (-s2.x * s1.y + s1.x * s2.y); 41 | var t = ( s2.x * (p0.y - p2.y) - s2.y * (p0.x - p2.x)) / (-s2.x * s1.y + s1.x * s2.y); 42 | return (s >= 0 && s <= 1 && t >= 0 && t <= 1); 43 | } 44 | 45 | // http://stackoverflow.com/questions/217578/how-can-i-determine-whether-a-2d-point-is-within-a-polygon 46 | public static bool Contains (Vector2 p, List vertices) { 47 | var n = vertices.Count; 48 | bool c = false; 49 | for(int i = 0, j = n - 1; i < n; j = i++) { 50 | if(vertices[i].Coordinate == p) return true; 51 | if ( 52 | ((vertices[i].Coordinate.y > p.y) != (vertices[j].Coordinate.y > p.y)) && 53 | (p.x < (vertices[j].Coordinate.x - vertices[i].Coordinate.x) * (p.y - vertices[i].Coordinate.y) / (vertices[j].Coordinate.y - vertices[i].Coordinate.y) + vertices[i].Coordinate.x) 54 | ) { 55 | c = !c; 56 | } 57 | } 58 | // c == true means odd, c == false means even 59 | return c; 60 | } 61 | 62 | // check p is left side of segment (from, to) 63 | public static bool LeftSide (Vector2 from, Vector2 to, Vector2 p) { 64 | float cross = ((to.x - from.x) * (p.y - from.y) - (to.y - from.y) * (p.x - from.x)); 65 | return (cross > 0f); 66 | } 67 | 68 | public static bool CheckEqual (Vertex2D v0, Vertex2D v1) { 69 | return (v0.Coordinate == v1.Coordinate); 70 | } 71 | 72 | public static bool CheckEqual (Segment2D s0, Segment2D s1) { 73 | return 74 | (CheckEqual(s0.a, s1.a) && CheckEqual(s0.b, s1.b)) || 75 | (CheckEqual(s0.a, s1.b) && CheckEqual(s0.b, s1.a)); 76 | } 77 | 78 | public static bool CheckEqual (Triangle2D t0, Triangle2D t1) { 79 | // 0,1,2 == 0,1,2 80 | // 0,1,2 == 0,2,1 81 | // 0,1,2 == 1,0,2 82 | // 0,1,2 == 1,2,0 83 | // 0,1,2 == 2,0,1 84 | // 0,1,2 == 2,1,0 85 | return 86 | (CheckEqual(t0.s0, t1.s0) && CheckEqual(t0.s1, t1.s1) && CheckEqual(t0.s2, t1.s2)) || 87 | (CheckEqual(t0.s0, t1.s0) && CheckEqual(t0.s1, t1.s2) && CheckEqual(t0.s2, t1.s1)) || 88 | 89 | (CheckEqual(t0.s0, t1.s1) && CheckEqual(t0.s1, t1.s0) && CheckEqual(t0.s2, t1.s2)) || 90 | (CheckEqual(t0.s0, t1.s1) && CheckEqual(t0.s1, t1.s2) && CheckEqual(t0.s2, t1.s0)) || 91 | 92 | (CheckEqual(t0.s0, t1.s2) && CheckEqual(t0.s1, t1.s0) && CheckEqual(t0.s2, t1.s1)) || 93 | (CheckEqual(t0.s0, t1.s2) && CheckEqual(t0.s1, t1.s1) && CheckEqual(t0.s2, t1.s0)); 94 | } 95 | 96 | } 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Demo/Shaders/SimplexNoise3D.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Noise Shader Library for Unity - https://github.com/keijiro/NoiseShader 3 | // 4 | // Original work (webgl-noise) Copyright (C) 2011 Ashima Arts. 5 | // Translation and modification was made by Keijiro Takahashi. 6 | // 7 | // This shader is based on the webgl-noise GLSL shader. For further details 8 | // of the original shader, please see the following description from the 9 | // original source code. 10 | // 11 | 12 | // 13 | // Description : Array and textureless GLSL 2D/3D/4D simplex 14 | // noise functions. 15 | // Author : Ian McEwan, Ashima Arts. 16 | // Maintainer : ijm 17 | // Lastmod : 20110822 (ijm) 18 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 19 | // Distributed under the MIT License. See LICENSE file. 20 | // https://github.com/ashima/webgl-noise 21 | // 22 | 23 | float3 mod289(float3 x) 24 | { 25 | return x - floor(x / 289.0) * 289.0; 26 | } 27 | 28 | float4 mod289(float4 x) 29 | { 30 | return x - floor(x / 289.0) * 289.0; 31 | } 32 | 33 | float4 permute(float4 x) 34 | { 35 | return mod289((x * 34.0 + 1.0) * x); 36 | } 37 | 38 | float4 taylorInvSqrt(float4 r) 39 | { 40 | return 1.79284291400159 - r * 0.85373472095314; 41 | } 42 | 43 | float snoise(float3 v) 44 | { 45 | const float2 C = float2(1.0 / 6.0, 1.0 / 3.0); 46 | 47 | // First corner 48 | float3 i = floor(v + dot(v, C.yyy)); 49 | float3 x0 = v - i + dot(i, C.xxx); 50 | 51 | // Other corners 52 | float3 g = step(x0.yzx, x0.xyz); 53 | float3 l = 1.0 - g; 54 | float3 i1 = min(g.xyz, l.zxy); 55 | float3 i2 = max(g.xyz, l.zxy); 56 | 57 | // x1 = x0 - i1 + 1.0 * C.xxx; 58 | // x2 = x0 - i2 + 2.0 * C.xxx; 59 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 60 | float3 x1 = x0 - i1 + C.xxx; 61 | float3 x2 = x0 - i2 + C.yyy; 62 | float3 x3 = x0 - 0.5; 63 | 64 | // Permutations 65 | i = mod289(i); // Avoid truncation effects in permutation 66 | float4 p = 67 | permute(permute(permute(i.z + float4(0.0, i1.z, i2.z, 1.0)) 68 | + i.y + float4(0.0, i1.y, i2.y, 1.0)) 69 | + i.x + float4(0.0, i1.x, i2.x, 1.0)); 70 | 71 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 72 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 73 | float4 j = p - 49.0 * floor(p / 49.0); // mod(p,7*7) 74 | 75 | float4 x_ = floor(j / 7.0); 76 | float4 y_ = floor(j - 7.0 * x_); // mod(j,N) 77 | 78 | float4 x = (x_ * 2.0 + 0.5) / 7.0 - 1.0; 79 | float4 y = (y_ * 2.0 + 0.5) / 7.0 - 1.0; 80 | 81 | float4 h = 1.0 - abs(x) - abs(y); 82 | 83 | float4 b0 = float4(x.xy, y.xy); 84 | float4 b1 = float4(x.zw, y.zw); 85 | 86 | //float4 s0 = float4(lessThan(b0, 0.0)) * 2.0 - 1.0; 87 | //float4 s1 = float4(lessThan(b1, 0.0)) * 2.0 - 1.0; 88 | float4 s0 = floor(b0) * 2.0 + 1.0; 89 | float4 s1 = floor(b1) * 2.0 + 1.0; 90 | float4 sh = -step(h, 0.0); 91 | 92 | float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; 93 | float4 a1 = b1.xzyw + s1.xzyw * sh.zzww; 94 | 95 | float3 g0 = float3(a0.xy, h.x); 96 | float3 g1 = float3(a0.zw, h.y); 97 | float3 g2 = float3(a1.xy, h.z); 98 | float3 g3 = float3(a1.zw, h.w); 99 | 100 | // Normalise gradients 101 | float4 norm = taylorInvSqrt(float4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3))); 102 | g0 *= norm.x; 103 | g1 *= norm.y; 104 | g2 *= norm.z; 105 | g3 *= norm.w; 106 | 107 | // Mix final noise value 108 | float4 m = max(0.6 - float4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); 109 | m = m * m; 110 | m = m * m; 111 | 112 | float4 px = float4(dot(x0, g0), dot(x1, g1), dot(x2, g2), dot(x3, g3)); 113 | return 42.0 * dot(m, px); 114 | } 115 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Triangle2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Triangle2D { 11 | 12 | public Vertex2D a, b, c; 13 | public Segment2D s0, s1, s2; 14 | private Circle2D circum; 15 | 16 | public Triangle2D (Segment2D s0, Segment2D s1, Segment2D s2) { 17 | this.s0 = s0; 18 | this.s1 = s1; 19 | this.s2 = s2; 20 | this.a = s0.a; 21 | this.b = s0.b; 22 | this.c = (s2.b == this.a || s2.b == this.b) ? s2.a : s2.b; 23 | } 24 | 25 | public bool HasPoint (Vertex2D p) { 26 | return (a == p) || (b == p) || (c == p); 27 | } 28 | 29 | public bool HasCommonPoint (Triangle2D t) { 30 | return HasPoint(t.a) || HasPoint(t.b) || HasPoint(t.c); 31 | } 32 | 33 | public bool HasSegment (Segment2D s) { 34 | return (s0 == s) || (s1 == s) || (s2 == s); 35 | } 36 | 37 | public bool HasSegment (Vector2 a, Vector2 b) { 38 | return (s0.HasPoint(a) && s0.HasPoint(b)) || (s1.HasPoint(a) && s1.HasPoint(b)) || (s2.HasPoint(a) && s2.HasPoint(b)); 39 | } 40 | 41 | public Vertex2D ExcludePoint (Segment2D s) { 42 | if(!s.HasPoint(a)) return a; 43 | else if(!s.HasPoint(b)) return b; 44 | return c; 45 | } 46 | 47 | public Vertex2D ExcludePoint (Vector2 p0, Vector2 p1) { 48 | if(p0 != a.Coordinate && p1 != a.Coordinate) return a; 49 | else if(p0 != b.Coordinate && p1 != b.Coordinate) return b; 50 | return c; 51 | } 52 | 53 | public Vertex2D[] ExcludePoint (Vector2 p) { 54 | if(p == a.Coordinate) return new Vertex2D[] { b, c }; 55 | else if(p == b.Coordinate) return new Vertex2D[] { a, c }; 56 | return new Vertex2D[] { a, b }; 57 | } 58 | 59 | public Segment2D[] ExcludeSegment (Segment2D s) { 60 | if(s0.Equals(s)) { 61 | return new Segment2D[] { s1, s2 }; 62 | } else if(s1.Equals(s)) { 63 | return new Segment2D[] { s0, s2 }; 64 | } 65 | return new Segment2D[] { s0, s1 }; 66 | } 67 | 68 | public Segment2D CommonSegment (Vertex2D v0, Vertex2D v1) { 69 | if(s0.HasPoint(v0) && s0.HasPoint(v1)) { 70 | return s0; 71 | } else if(s1.HasPoint(v0) && s1.HasPoint(v1)) { 72 | return s1; 73 | } 74 | return s2; 75 | } 76 | 77 | public Segment2D[] CommonSegments (Vertex2D v) { 78 | if(s0.HasPoint(v) && s1.HasPoint(v)) { 79 | return new [] {s0, s1}; 80 | } else if(s1.HasPoint(v) && s2.HasPoint(v)) { 81 | return new [] {s1, s2}; 82 | } 83 | return new [] {s0, s2}; 84 | } 85 | 86 | public Vector2 Circumcenter () { 87 | if(circum == null) { 88 | circum = Circle2D.GetCircumscribedCircle(this); 89 | } 90 | return circum.center; 91 | } 92 | 93 | public bool ContainsInExternalCircle (Vertex2D v) { 94 | if(circum == null) { 95 | circum = Circle2D.GetCircumscribedCircle(this); 96 | } 97 | return circum.Contains(v.Coordinate); 98 | } 99 | 100 | float Angle (Vector2 from, Vector2 to0, Vector2 to1) { 101 | var v0 = (to0 - from); 102 | var v1 = (to1 - from); 103 | 104 | // 0 ~ PI 105 | float acos = Mathf.Acos(Vector2.Dot(v0, v1) / Mathf.Sqrt(v0.sqrMagnitude * v1.sqrMagnitude)); 106 | return acos; 107 | } 108 | 109 | // angle must to RADIAN 110 | public bool Skinny (float angle, float threshold) { 111 | if(s0.Length() <= threshold && s1.Length() <= threshold && s2.Length() <= threshold) return false; 112 | 113 | if(Angle(a.Coordinate, b.Coordinate, c.Coordinate) < angle) return true; // angle bac 114 | else if(Angle(b.Coordinate, a.Coordinate, c.Coordinate) < angle) return true; // angle abc 115 | else if(Angle(c.Coordinate, a.Coordinate, b.Coordinate) < angle) return true; // angle acb 116 | return false; 117 | } 118 | 119 | public bool Equals (Triangle2D t) { 120 | return HasPoint(t.a) && HasPoint(t.b) && HasPoint(t.c); 121 | } 122 | 123 | public void DrawGizmos () { 124 | s0.DrawGizmos(); 125 | s1.DrawGizmos(); 126 | s2.DrawGizmos(); 127 | } 128 | 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Polygon2D.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | using System; 4 | using System.Linq; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | 8 | namespace mattatz.Triangulation2DSystem { 9 | 10 | public class Polygon2D { 11 | 12 | public Vertex2D[] Vertices { get { return vertices.ToArray(); } } 13 | public Segment2D[] Segments { get { return segments.ToArray(); } } 14 | 15 | List vertices; 16 | List segments; 17 | 18 | /* 19 | * slow incremental approach 20 | * O(n * log(n)) 21 | */ 22 | public static Polygon2D ConvexHull (Vector2[] points) { 23 | var ordered = points.ToList().OrderBy(p => p.x).ToList(); 24 | 25 | var upper = new List(); 26 | upper.Add(ordered[0]); 27 | upper.Add(ordered[1]); 28 | for(int i = 2, n = ordered.Count; i < n; i++) { 29 | upper.Add(ordered[i]); 30 | int l = upper.Count; 31 | if(l > 2) { 32 | var p = upper[l - 3]; 33 | var r = upper[l - 2]; 34 | var q = upper[l - 1]; 35 | if(Utils2D.LeftSide(p, q, r)) { // r is left side of pq 36 | upper.RemoveAt(l - 2); 37 | } 38 | } 39 | } 40 | 41 | var lower = new List(); 42 | lower.Add(ordered[ordered.Count - 1]); 43 | lower.Add(ordered[ordered.Count - 2]); 44 | for(int i = ordered.Count - 3; i >= 0; i--) { 45 | lower.Add(ordered[i]); 46 | int l = lower.Count; 47 | if(l > 2) { 48 | var p = lower[l - 3]; 49 | var r = lower[l - 2]; 50 | var q = lower[l - 1]; 51 | if(Utils2D.LeftSide(p, q, r)) { // r is left side of pq 52 | lower.RemoveAt(l - 2); 53 | } 54 | } 55 | } 56 | 57 | lower.RemoveAt(lower.Count - 1); 58 | lower.RemoveAt(0); 59 | 60 | upper.AddRange (lower); 61 | 62 | return new Polygon2D(upper.ToArray()); 63 | } 64 | 65 | public static Polygon2D Contour (Vector2[] points) { 66 | var n = points.Length; 67 | var edges = new List(); 68 | for(int i = 0; i < n; i++) { 69 | edges.Add(new HalfEdge2D(points[i])); 70 | } 71 | for(int i = 0; i < n; i++) { 72 | var e = edges[i]; 73 | e.from = edges[(i == 0) ? (n - 1) : (i - 1)]; 74 | e.to = edges[(i + 1) % n]; 75 | } 76 | edges = Polygon2D.SplitEdges(edges); 77 | 78 | var result = new List(); 79 | 80 | HalfEdge2D start = edges[0]; 81 | result.Add(start.p); 82 | 83 | HalfEdge2D current = start; 84 | 85 | while(true) { 86 | HalfEdge2D from = current, to = current.to; 87 | HalfEdge2D from2 = to.to, to2 = from2.to; 88 | 89 | bool flag = false; 90 | 91 | while(from2 != start && to2 != from) { 92 | if(flag = Utils2D.Intersect(from.p, to.p, from2.p, to2.p)) { 93 | break; 94 | } 95 | from2 = to2; 96 | to2 = to2.to; 97 | } 98 | 99 | if(!flag) { 100 | result.Add(to.p); 101 | current = to; // step to next 102 | } else { 103 | result.Add(from2.p); 104 | 105 | // reconnect 106 | from.to = from2; 107 | from2.to = from; // invert this edge later 108 | 109 | to.from = to2; 110 | to.Invert(); 111 | to2.from = to; 112 | 113 | HalfEdge2D e = from2; 114 | while(e != to) { 115 | e.Invert(); 116 | e = e.to; 117 | } 118 | 119 | current = from2; 120 | } 121 | 122 | if(current == start) break; 123 | } 124 | 125 | result.RemoveAt(result.Count - 1); // remove last 126 | 127 | return new Polygon2D(result.ToArray()); 128 | } 129 | 130 | // Disable to intersect more than two edges 131 | static List SplitEdges (List edges) { 132 | HalfEdge2D start = edges[0]; 133 | HalfEdge2D cur = start; 134 | 135 | while(true) { 136 | HalfEdge2D from = cur, to = from.to; 137 | HalfEdge2D from2 = to.to, to2 = from2.to; 138 | 139 | int intersections = 0; 140 | 141 | while(to2 != from.from) { 142 | if(Utils2D.Intersect(from.p, to.p, from2.p, to2.p)) { 143 | intersections++; 144 | if(intersections >= 2) { 145 | break; 146 | } 147 | } 148 | // next 149 | from2 = from2.to; 150 | to2 = to2.to; 151 | } 152 | 153 | if(intersections >= 2) { 154 | edges.Add(cur.Split()); 155 | } else { 156 | // next 157 | cur = cur.to; 158 | if(cur == start) break; 159 | } 160 | } 161 | 162 | return edges; 163 | } 164 | 165 | // contour must be inversed clockwise order. 166 | public Polygon2D (Vector2[] contour) { 167 | vertices = contour.Select(p => new Vertex2D(p)).ToList(); 168 | segments = new List(); 169 | for(int i = 0, n = vertices.Count; i < n; i++) { 170 | var v0 = vertices[i]; 171 | var v1 = vertices[(i + 1) % n]; 172 | segments.Add(new Segment2D(v0, v1)); 173 | } 174 | } 175 | 176 | public bool Contains (Vector2 p) { 177 | return Utils2D.Contains(p, vertices); 178 | } 179 | 180 | public void DrawGizmos () { 181 | segments.ForEach(s => s.DrawGizmos()); 182 | } 183 | 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /Assets/Packages/Triangulation2D/Scripts/Triangulation2D.cs: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mattatz / http://mattatz.github.io 3 | * 4 | * Ruppert's Delaunay Refinement Algorithm 5 | * Jim Ruppert. A Delaunay Refinement Algorithm for Quality 2-Dimensional Mesh Generation / http://www.cis.upenn.edu/~cis610/ruppert.pdf 6 | * The Quake Group at Carnegie Mellon University / https://www.cs.cmu.edu/~quake/tripaper/triangle3.html 7 | * Wikipedia / https://en.wikipedia.org/wiki/Ruppert%27s_algorithm 8 | * ETH zurich CG13 Chapter 7 / http://www.ti.inf.ethz.ch/ew/Lehre/CG13/lecture/Chapter%207.pdf 9 | */ 10 | 11 | using UnityEngine; 12 | 13 | using System; 14 | using System.Linq; 15 | using System.Collections; 16 | using System.Collections.Generic; 17 | 18 | namespace mattatz.Triangulation2DSystem { 19 | 20 | public class Triangulation2D { 21 | 22 | const float kAngleMax = 30f; 23 | 24 | public Polygon2D Polygon { get { return PSLG; } } 25 | public Triangle2D[] Triangles { get { return T.ToArray(); } } 26 | public List Edges { get { return E; } } 27 | public List Points { get { return P; } } 28 | 29 | List V = new List(); // vertices in PSLG 30 | List S = new List(); // segments in PSLG 31 | 32 | List P = new List(); // vertices in DT 33 | List E = new List(); // segments in DT 34 | 35 | Polygon2D PSLG; 36 | List T = new List(); 37 | 38 | public Triangulation2D (Polygon2D polygon, float angle = 20f, float threshold = 0.1f) { 39 | angle = Mathf.Min(angle, kAngleMax) * Mathf.Deg2Rad; 40 | 41 | PSLG = polygon; 42 | V = PSLG.Vertices.ToList(); 43 | S = PSLG.Segments.ToList(); 44 | Triangulate (polygon.Vertices.Select(v => v.Coordinate).ToArray(), angle, threshold); 45 | } 46 | 47 | public Mesh Build () { 48 | return Build((Vertex2D v) => { 49 | var xy = v.Coordinate; 50 | return new Vector3(xy.x, xy.y, 0f); 51 | }); 52 | } 53 | 54 | public Mesh Build (Func coord) { 55 | var mesh = new Mesh(); 56 | 57 | var vertices = P.Select(p => { 58 | return coord(p); 59 | }).ToArray(); 60 | 61 | var triangles = new List(); 62 | for(int i = 0, n = T.Count; i < n; i++) { 63 | var t = T[i]; 64 | int a = P.IndexOf(t.a), b = P.IndexOf(t.b), c = P.IndexOf(t.c); 65 | if(a < 0 || b < 0 || c < 0) { 66 | // Debug.Log(a + " : " + b + " : " + c); 67 | continue; 68 | } 69 | if(Utils2D.LeftSide(t.a.Coordinate, t.b.Coordinate, t.c.Coordinate)) { 70 | triangles.Add(a); triangles.Add(c); triangles.Add(b); 71 | } else { 72 | triangles.Add(a); triangles.Add(b); triangles.Add(c); 73 | } 74 | } 75 | 76 | mesh.vertices = vertices; 77 | mesh.SetTriangles(triangles.ToArray(), 0); 78 | mesh.RecalculateNormals(); 79 | 80 | return mesh; 81 | } 82 | 83 | int FindVertex (Vector2 p, List Vertices) { 84 | return Vertices.FindIndex (v => { 85 | return v.Coordinate == p; 86 | // return Mathf.Approximately(v.Coordinate.x, p.x) && Mathf.Approximately(v.Coordinate.y, p.y); 87 | }); 88 | } 89 | 90 | int FindSegment (Vertex2D a, Vertex2D b, List Segments) { 91 | return Segments.FindIndex (s => (s.a == a && s.b == b) || (s.a == b && s.b == a)); 92 | } 93 | 94 | public Vertex2D CheckAndAddVertex (Vector2 coord) { 95 | var idx = FindVertex(coord, P); 96 | if(idx < 0) { 97 | var v = new Vertex2D(coord); 98 | P.Add(v); 99 | return v; 100 | } 101 | return P[idx]; 102 | } 103 | 104 | public Segment2D CheckAndAddSegment (Vertex2D a, Vertex2D b) { 105 | var idx = FindSegment(a, b, E); 106 | Segment2D s; 107 | if(idx < 0) { 108 | s = new Segment2D(a, b); 109 | E.Add(s); 110 | } else { 111 | s = E[idx]; 112 | } 113 | s.Increment(); 114 | return s; 115 | } 116 | 117 | public Triangle2D AddTriangle (Vertex2D a, Vertex2D b, Vertex2D c) { 118 | var s0 = CheckAndAddSegment(a, b); 119 | var s1 = CheckAndAddSegment(b, c); 120 | var s2 = CheckAndAddSegment(c, a); 121 | var t = new Triangle2D(s0, s1, s2); 122 | T.Add(t); 123 | return t; 124 | } 125 | 126 | public void RemoveTriangle (Triangle2D t) { 127 | var idx = T.IndexOf(t); 128 | if(idx < 0) return; 129 | 130 | T.RemoveAt(idx); 131 | if(t.s0.Decrement() <= 0) RemoveSegment (t.s0); 132 | if(t.s1.Decrement() <= 0) RemoveSegment (t.s1); 133 | if(t.s2.Decrement() <= 0) RemoveSegment (t.s2); 134 | } 135 | 136 | public void RemoveTriangle (Segment2D s) { 137 | T.FindAll(t => t.HasSegment(s)).ForEach(t => RemoveTriangle(t)); 138 | } 139 | 140 | public void RemoveSegment (Segment2D s) { 141 | E.Remove(s); 142 | if(s.a.ReferenceCount <= 0) P.Remove(s.a); 143 | if(s.b.ReferenceCount <= 0) P.Remove(s.b); 144 | } 145 | 146 | void Bound (Vector2[] points, out Vector2 min, out Vector2 max) { 147 | min = Vector2.one * float.MaxValue; 148 | max = Vector2.one * float.MinValue; 149 | for(int i = 0, n = points.Length; i < n; i++) { 150 | var p = points[i]; 151 | min.x = Mathf.Min (min.x, p.x); 152 | min.y = Mathf.Min (min.y, p.y); 153 | max.x = Mathf.Max (max.x, p.x); 154 | max.y = Mathf.Max (max.y, p.y); 155 | } 156 | } 157 | 158 | public Triangle2D AddExternalTriangle (Vector2 min, Vector2 max) { 159 | var center = (max + min) * 0.5f; 160 | var diagonal = (max - min).magnitude; 161 | var dh = diagonal * 0.5f; 162 | var rdh = Mathf.Sqrt(3f) * dh; 163 | return AddTriangle( 164 | CheckAndAddVertex(center + new Vector2(-rdh, -dh) * 3f), 165 | CheckAndAddVertex(center + new Vector2(rdh, -dh) * 3f), 166 | CheckAndAddVertex(center + new Vector2(0f, diagonal) * 3f) 167 | ); 168 | } 169 | 170 | void Triangulate (Vector2[] points, float angle, float threshold) { 171 | Vector2 min, max; 172 | Bound(points, out min, out max); 173 | 174 | AddExternalTriangle(min, max); 175 | 176 | for(int i = 0, n = points.Length; i < n; i++) { 177 | var v = points[i]; 178 | UpdateTriangulation (v); 179 | } 180 | 181 | Refine (angle, threshold); 182 | RemoveExternalPSLG (); 183 | } 184 | 185 | void RemoveCommonTriangles (Triangle2D target) { 186 | for(int i = 0, n = T.Count; i < n; i++) { 187 | var t = T[i]; 188 | if(t.HasCommonPoint(target)) { 189 | RemoveTriangle (t); 190 | i--; 191 | n--; 192 | } 193 | } 194 | } 195 | 196 | void RemoveExternalPSLG () { 197 | for(int i = 0, n = T.Count; i < n; i++) { 198 | var t = T[i]; 199 | if(ExternalPSLG(t) || HasOuterSegments(t)) { 200 | // if(ExternalPSLG(t)) { 201 | RemoveTriangle (t); 202 | i--; 203 | n--; 204 | } 205 | } 206 | } 207 | 208 | bool ContainsSegments (Segment2D s, List segments) { 209 | return segments.FindIndex (s2 => 210 | (s2.a.Coordinate == s.a.Coordinate && s2.b.Coordinate == s.b.Coordinate) || 211 | (s2.a.Coordinate == s.b.Coordinate && s2.b.Coordinate == s.a.Coordinate) 212 | ) >= 0; 213 | } 214 | 215 | bool HasOuterSegments (Triangle2D t) { 216 | if(!ContainsSegments(t.s0, S)) { 217 | return ExternalPSLG(t.s0); 218 | } 219 | if(!ContainsSegments(t.s1, S)) { 220 | return ExternalPSLG(t.s1); 221 | } 222 | if(!ContainsSegments(t.s2, S)) { 223 | return ExternalPSLG(t.s2); 224 | } 225 | return false; 226 | } 227 | 228 | void UpdateTriangulation (Vector2 p) { 229 | var tmpT = new List(); 230 | var tmpS = new List(); 231 | 232 | var v = CheckAndAddVertex(p); 233 | tmpT = T.FindAll(t => t.ContainsInExternalCircle(v)); 234 | tmpT.ForEach(t => { 235 | tmpS.Add(t.s0); 236 | tmpS.Add(t.s1); 237 | tmpS.Add(t.s2); 238 | 239 | AddTriangle(t.a, t.b, v); 240 | AddTriangle(t.b, t.c, v); 241 | AddTriangle(t.c, t.a, v); 242 | RemoveTriangle (t); 243 | }); 244 | 245 | while(tmpS.Count != 0) { 246 | var s = tmpS.Last(); 247 | tmpS.RemoveAt(tmpS.Count - 1); 248 | 249 | var commonT = T.FindAll(t => t.HasSegment(s)); 250 | if(commonT.Count <= 1) continue; 251 | 252 | var abc = commonT[0]; 253 | var abd = commonT[1]; 254 | 255 | if(abc.Equals(abd)) { 256 | RemoveTriangle (abc); 257 | RemoveTriangle (abd); 258 | continue; 259 | } 260 | 261 | var a = s.a; 262 | var b = s.b; 263 | var c = abc.ExcludePoint(s); 264 | var d = abd.ExcludePoint(s); 265 | 266 | var ec = Circle2D.GetCircumscribedCircle(abc); 267 | if(ec.Contains(d.Coordinate)) { 268 | RemoveTriangle (abc); 269 | RemoveTriangle (abd); 270 | 271 | AddTriangle(a, c, d); // add acd 272 | AddTriangle(b, c, d); // add bcd 273 | 274 | var segments0 = abc.ExcludeSegment(s); 275 | tmpS.Add(segments0[0]); 276 | tmpS.Add(segments0[1]); 277 | 278 | var segments1 = abd.ExcludeSegment(s); 279 | tmpS.Add(segments1[0]); 280 | tmpS.Add(segments1[1]); 281 | } 282 | } 283 | 284 | } 285 | 286 | bool FindAndSplit (float threshold) { 287 | for(int i = 0, n = S.Count; i < n; i++) { 288 | var s = S[i]; 289 | if(s.Length() < threshold) continue; 290 | 291 | for(int j = 0, m = P.Count; j < m; j++) { 292 | if(s.EncroachedUpon(P[j].Coordinate)) { 293 | SplitSegment(s); 294 | return true; 295 | } 296 | } 297 | } 298 | return false; 299 | } 300 | 301 | bool ExternalPSLG (Vector2 p) { 302 | return !Utils2D.Contains(p, V); 303 | } 304 | 305 | bool ExternalPSLG (Segment2D s) { 306 | return ExternalPSLG(s.Midpoint()); 307 | } 308 | 309 | bool ExternalPSLG (Triangle2D t) { 310 | // return ExternalPSLG(t.s0) && ExternalPSLG(t.s1) && ExternalPSLG(t.s2); 311 | return 312 | ExternalPSLG(t.a.Coordinate) || 313 | ExternalPSLG(t.b.Coordinate) || 314 | ExternalPSLG(t.c.Coordinate) 315 | ; 316 | } 317 | 318 | void Refine (float angle, float threshold) { 319 | while(T.Any(t => !ExternalPSLG(t) && t.Skinny(angle, threshold))) { 320 | RefineSubRoutine(angle, threshold); 321 | } 322 | } 323 | 324 | void RefineSubRoutine (float angle, float threshold) { 325 | 326 | while(true) { 327 | if(!FindAndSplit(threshold)) break; 328 | } 329 | 330 | var skinny = T.Find (t => !ExternalPSLG(t) && t.Skinny(angle, threshold)); 331 | var p = skinny.Circumcenter(); 332 | 333 | var segments = S.FindAll(s => s.EncroachedUpon(p)); 334 | if(segments.Count > 0) { 335 | segments.ForEach(s => SplitSegment(s)); 336 | } else { 337 | SplitTriangle(skinny); 338 | } 339 | } 340 | 341 | void SplitTriangle (Triangle2D t) { 342 | var c = t.Circumcenter(); 343 | UpdateTriangulation(c); 344 | } 345 | 346 | void SplitSegment (Segment2D s) { 347 | Vertex2D a = s.a, b = s.b; 348 | var mv = new Vertex2D(s.Midpoint()); 349 | 350 | // add mv to V 351 | // the index is between a and b. 352 | var idxA = V.IndexOf(a); 353 | var idxB = V.IndexOf(b); 354 | if(Mathf.Abs(idxA - idxB) == 1) { 355 | var idx = (idxA > idxB) ? idxA : idxB; 356 | V.Insert(idx, mv); 357 | } else { 358 | V.Add(mv); 359 | } 360 | 361 | UpdateTriangulation(mv.Coordinate); 362 | 363 | // Add two halves to S 364 | var sidx = S.IndexOf(s); 365 | S.RemoveAt(sidx); 366 | 367 | S.Add(new Segment2D(s.a, mv)); 368 | S.Add(new Segment2D(mv, s.b)); 369 | } 370 | 371 | bool CheckUnique () { 372 | var flag = false; 373 | 374 | for(int i = 0, n = P.Count; i < n; i++) { 375 | var v0 = P[i]; 376 | for(int j = i + 1; j < n; j++) { 377 | var v1 = P[j]; 378 | if(Utils2D.CheckEqual(v0, v1)) { 379 | Debug.LogWarning("vertex " + i + " equals " + j); 380 | flag = true; 381 | } 382 | } 383 | } 384 | 385 | for(int i = 0, n = E.Count; i < n; i++) { 386 | var s0 = E[i]; 387 | for(int j = i + 1; j < n; j++) { 388 | var s1 = E[j]; 389 | if(Utils2D.CheckEqual(s0, s1)) { 390 | Debug.LogWarning("segment " + i + " equals " + j); 391 | flag = true; 392 | } 393 | } 394 | } 395 | 396 | for(int i = 0, n = T.Count; i < n; i++) { 397 | var t0 = T[i]; 398 | for(int j = i + 1; j < n; j++) { 399 | var t1 = T[j]; 400 | if(Utils2D.CheckEqual(t0, t1)) { 401 | Debug.LogWarning("triangle " + i + " equals " + j); 402 | flag = true; 403 | } 404 | } 405 | } 406 | 407 | for(int i = 0, n = T.Count; i < n; i++) { 408 | var t = T[i]; 409 | if(Utils2D.CheckEqual(t.s0, t.s1) || Utils2D.CheckEqual(t.s0, t.s2) || Utils2D.CheckEqual(t.s1, t.s2)) { 410 | Debug.LogWarning("triangle " + i + " has duplicated segments"); 411 | flag = true; 412 | } 413 | } 414 | 415 | return flag; 416 | } 417 | 418 | public void DrawGizmos () { 419 | T.ForEach(t => t.DrawGizmos()); 420 | } 421 | 422 | } 423 | 424 | } 425 | --------------------------------------------------------------------------------