├── .gitignore
├── CHANGELOG.md.meta
├── DelaunatorSharp.Benchmark
├── DelaunatorBenchmark.cs
├── DelaunatorSharp.Benchmark.csproj
├── Distribution.cs
├── GetVoronoiCellsBenchmark.cs
└── Program.cs
├── DelaunatorSharp.Unity
├── CHANGELOG.md
├── CHANGELOG.md.meta
├── LICENSE
├── LICENSE.md
├── LICENSE.md.meta
├── LICENSE.meta
├── README.md
├── README.md.meta
├── Runtime.meta
├── Runtime
│ ├── DelaunatorSharp.Runtime.asmdef
│ ├── DelaunatorSharp.Runtime.asmdef.meta
│ ├── DelaunatorSharp.dll
│ ├── DelaunatorSharp.dll.meta
│ ├── Scripts.meta
│ └── Scripts
│ │ ├── DelaunatorExtensions.cs
│ │ ├── DelaunatorExtensions.cs.meta
│ │ ├── DelaunatorPreview.cs
│ │ ├── DelaunatorPreview.cs.meta
│ │ ├── UniformPoissionDiskSampler.cs
│ │ └── UniformPoissionDiskSampler.cs.meta
├── Samples~
│ ├── Materials.meta
│ ├── Materials
│ │ ├── LineMaterial.mat
│ │ ├── LineMaterial.mat.meta
│ │ ├── MeshMaterial.mat
│ │ └── MeshMaterial.mat.meta
│ ├── Prefabs.meta
│ ├── Prefabs
│ │ ├── DelaunatorPreview.prefab
│ │ ├── DelaunatorPreview.prefab.meta
│ │ ├── Triangle_PointPrefab.prefab
│ │ ├── Triangle_PointPrefab.prefab.meta
│ │ ├── Voronoi_PointPrefab.prefab
│ │ └── Voronoi_PointPrefab.prefab.meta
│ ├── Scenes.meta
│ └── Scenes
│ │ ├── SampleScene.unity
│ │ └── SampleScene.unity.meta
├── package.json
└── package.json.meta
├── DelaunatorSharp.WPF
├── App.config
├── App.xaml
├── App.xaml.cs
├── DelaunatorSharp.WPF.csproj
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── UniformPoissonDiskSampler.cs
├── packages.config
└── samples.json
├── DelaunatorSharp
├── Delaunator.cs
├── DelaunatorSharp.csproj
├── Interfaces
│ ├── IEdge.cs
│ ├── IPoint.cs
│ ├── ITriangle.cs
│ └── IVoronoiCell.cs
└── Models
│ ├── Edge.cs
│ ├── Point.cs
│ ├── Triangle.cs
│ └── VoronoiCell.cs
├── Images
├── Delaunator_Circle.PNG
├── Delaunator_Package Manager.png
├── Delaunator_Rectangle.png
├── Delaunator_Unity_Example.gif
└── Delaunator_Unity_Example_Mesh.gif
├── LICENSE
├── README.md
└── delaunator-sharp.sln
/.gitignore:
--------------------------------------------------------------------------------
1 | # This .gitignore file should be placed at the root of your Unity project directory
2 | #
3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore
4 | #
5 | /[Ll]ibrary/
6 | /[Tt]emp/
7 | /[Oo]bj/
8 | /[Bb]uild/
9 | /[Bb]uilds/
10 | /[Ll]ogs/
11 | /[Uu]ser[Ss]ettings/
12 |
13 | # MemoryCaptures can get excessive in size.
14 | # They also could contain extremely sensitive data
15 | /[Mm]emoryCaptures/
16 |
17 | # Asset meta data should only be ignored when the corresponding asset is also ignored
18 | !/[Aa]ssets/**/*.meta
19 |
20 | # Uncomment this line if you wish to ignore the asset store tools plugin
21 | # /[Aa]ssets/AssetStoreTools*
22 |
23 | # Autogenerated Jetbrains Rider plugin
24 | /[Aa]ssets/Plugins/Editor/JetBrains*
25 |
26 | # Visual Studio cache directory
27 | .vs/
28 |
29 | # Gradle cache directory
30 | .gradle/
31 |
32 | # Autogenerated VS/MD/Consulo solution and project files
33 | ExportedObj/
34 | .consulo/
35 | *.csproj
36 | *.unityproj
37 | *.sln
38 | *.suo
39 | *.tmp
40 | *.user
41 | *.userprefs
42 | *.pidb
43 | *.booproj
44 | *.svd
45 | *.pdb
46 | *.mdb
47 | *.opendb
48 | *.VC.db
49 |
50 | # Unity3D generated meta files
51 | *.pidb.meta
52 | *.pdb.meta
53 | *.mdb.meta
54 |
55 | # Unity3D generated file on crash reports
56 | sysinfo.txt
57 |
58 | # Builds
59 | *.apk
60 | *.aab
61 | *.unitypackage
62 |
63 | # Crashlytics generated file
64 | crashlytics-build.properties
65 |
66 | # Packed Addressables
67 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
68 |
69 | # Temporary auto-generated Android Assets
70 | /[Aa]ssets/[Ss]treamingAssets/aa.meta
71 | /[Aa]ssets/[Ss]treamingAssets/aa/*
72 |
73 | /[Pp]ackages/
74 | /DelaunatorSharp/bin
75 | /DelaunatorSharp/obj
76 | /DelaunatorSharp.WPF/bin
77 | /DelaunatorSharp.WPF/obj
78 | /DelaunatorSharp.Benchmark/obj
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4f2a0296c22b92f4b8bd817cc1cf7266
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Benchmark/DelaunatorBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using BenchmarkDotNet.Engines;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace DelaunatorSharp.Benchmark
7 | {
8 | [SimpleJob(RunStrategy.ColdStart, warmupCount:10, targetCount: 10)]
9 | [HtmlExporter]
10 | public class DelaunatorBenchmark
11 | {
12 | private Distribution distribution = new Distribution();
13 | private IPoint[] points;
14 |
15 | [Params(100000, 1000000)]
16 | public int Count;
17 |
18 | [ParamsAllValues]
19 | public Distribution.Type Type { get; set; }
20 |
21 | [GlobalSetup]
22 | public void GlobalSetup()
23 | {
24 | points = distribution.GetPoints(Type, Count).ToArray();
25 | }
26 |
27 | [Benchmark]
28 | public Delaunator Delaunator()
29 | {
30 | return new Delaunator(points);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Benchmark/DelaunatorSharp.Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Benchmark/Distribution.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace DelaunatorSharp.Benchmark
6 | {
7 | public class Distribution
8 | {
9 | private Random random = new Random();
10 | public enum Type { Uniform, Gaussian, Grid };
11 |
12 | public IEnumerable GetPoints(Type type, int count)
13 | {
14 | switch (type)
15 | {
16 | case Type.Uniform: return Uniform(count);
17 | case Type.Gaussian: return Gaussian(count);
18 | case Type.Grid: return Grid(count);
19 | }
20 |
21 | return Enumerable.Empty();
22 | }
23 |
24 | public IEnumerable Uniform(int count)
25 | {
26 | for (var i = 0; i < count; i++)
27 | yield return new Point(random.NextDouble() * Math.Pow(10, 3), random.NextDouble() * Math.Pow(10, 3));
28 | }
29 |
30 | public IEnumerable Grid(int count)
31 | {
32 | var size = Math.Sqrt(count);
33 | for (var i = 0; i < size; i++)
34 | for (var j = 0; j < size; j++)
35 | yield return new Point(i, j);
36 | }
37 | public IEnumerable Gaussian(int count)
38 | {
39 | for (var i = 0; i < count; i++)
40 | yield return new Point(PseudoNormal() * Math.Pow(10, 3), PseudoNormal() * Math.Pow(10, 3));
41 | }
42 |
43 | private double PseudoNormal()
44 | {
45 | var v = random.NextDouble() + random.NextDouble() + random.NextDouble() + random.NextDouble() + random.NextDouble() + random.NextDouble();
46 | return Math.Min(0.5 * (v - 3) / 3, 1);
47 | }
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Benchmark/GetVoronoiCellsBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using BenchmarkDotNet.Engines;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace DelaunatorSharp.Benchmark
7 | {
8 | [SimpleJob(RunStrategy.ColdStart, warmupCount:10, targetCount: 10)]
9 | [HtmlExporter]
10 | public class GetVoronoiCellsBenchmark
11 | {
12 | private Distribution distribution = new Distribution();
13 | private IPoint[] points;
14 |
15 | [Params(5000)]
16 | public int Count;
17 |
18 | [ParamsAllValues]
19 | public Distribution.Type Type { get; set; }
20 |
21 | private Delaunator delaunator;
22 |
23 | [GlobalSetup]
24 | public void GlobalSetup()
25 | {
26 | points = distribution.GetPoints(Type, Count).ToArray();
27 | delaunator = new Delaunator(points);
28 | }
29 |
30 | [Benchmark]
31 | public void GetVoronoiCells()
32 | {
33 | using var enumerator = delaunator.GetVoronoiCells().GetEnumerator();
34 | while (enumerator.MoveNext()) { }
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Running;
2 | using System.Linq;
3 |
4 | namespace DelaunatorSharp.Benchmark
5 | {
6 | class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | var summary = BenchmarkRunner.Run();
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | Initial Commit
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4f2a0296c22b92f4b8bd817cc1cf7266
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Patryk Grech
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 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Patryk Grech
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 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 662da728214b82d4bb48db9699cf19c7
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 67de90a526e8fa640ae8bdc4b4b6eafb
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/README.md:
--------------------------------------------------------------------------------
1 | UnityPackage for [Delaunator C#](https://github.com/nol1fe/delaunator-sharp)
2 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1cc4ee45e125ec34c865185d137ec022
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 15700a783632fbe429ecb52d9c41c939
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/DelaunatorSharp.Runtime.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "DelaunatorSharp.Runtime",
3 | "references": [],
4 | "includePlatforms": [],
5 | "excludePlatforms": [],
6 | "allowUnsafeCode": false,
7 | "overrideReferences": true,
8 | "precompiledReferences": [
9 | "DelaunatorSharp.dll"
10 | ],
11 | "autoReferenced": true,
12 | "defineConstraints": [],
13 | "versionDefines": [],
14 | "noEngineReferences": false
15 | }
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/DelaunatorSharp.Runtime.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f7c5b88a39394314e874750ef4655131
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/DelaunatorSharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/DelaunatorSharp.Unity/Runtime/DelaunatorSharp.dll
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/DelaunatorSharp.dll.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ee4147517c9ffee4aba0379153db94d2
3 | PluginImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | iconMap: {}
7 | executionOrder: {}
8 | defineConstraints: []
9 | isPreloaded: 0
10 | isOverridable: 1
11 | isExplicitlyReferenced: 0
12 | validateReferences: 1
13 | platformData:
14 | - first:
15 | Any:
16 | second:
17 | enabled: 1
18 | settings: {}
19 | - first:
20 | Editor: Editor
21 | second:
22 | enabled: 0
23 | settings:
24 | DefaultValueInitialized: true
25 | - first:
26 | Windows Store Apps: WindowsStoreApps
27 | second:
28 | enabled: 0
29 | settings:
30 | CPU: AnyCPU
31 | userData:
32 | assetBundleName:
33 | assetBundleVariant:
34 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0a4f2fedc36aea24ca0ccea230fe305c
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/DelaunatorExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 | using DelaunatorSharp;
5 | using System.Linq;
6 | using System;
7 |
8 |
9 | namespace DelaunatorSharp.Unity.Extensions
10 | {
11 | public static class DelaunatorExtensions
12 | {
13 | public static IPoint[] ToPoints(this IEnumerable vertices) => vertices.Select(vertex => new Point(vertex.x, vertex.y)).OfType().ToArray();
14 | public static IPoint[] ToPoints(this Transform[] vertices) => vertices.Select(x => x.transform.position).OfType().ToPoints();
15 |
16 | public static Vector2[] ToVectors2(this IEnumerable points) => points.Select(point => point.ToVector2()).ToArray();
17 | public static Vector3[] ToVectors3(this IEnumerable points) => points.Select(point => point.ToVector3()).ToArray();
18 |
19 | public static Vector2 ToVector2(this IPoint point) => new Vector2((float)point.X, (float)point.Y);
20 | public static Vector3 ToVector3(this IPoint point) => new Vector3((float)point.X, (float)point.Y);
21 | }
22 | }
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/DelaunatorExtensions.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f11db79632f9dc540808b5993a922766
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/DelaunatorPreview.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 | using UnityEngine;
4 | using DelaunatorSharp;
5 | using System.Linq;
6 | using DelaunatorSharp.Unity.Extensions;
7 | using System;
8 |
9 |
10 | namespace DelaunatorSharp.Unity.Extensions
11 | {
12 | public partial class DelaunatorPreview : MonoBehaviour
13 | {
14 | [SerializeField] GameObject trianglePointPrefab;
15 | [SerializeField] GameObject voronoiPointPrefab;
16 |
17 | private List points = new List();
18 | private GameObject meshObject;
19 |
20 | private Delaunator delaunator;
21 |
22 | private Transform PointsContainer;
23 | private Transform HullContainer;
24 | private Transform VoronoiContainer;
25 | private Transform TrianglesContainer;
26 |
27 | [SerializeField] float voronoiEdgeWidth = .01f;
28 | [SerializeField] float triangleEdgeWidth = .01f;
29 | [SerializeField] float hullEdgeWith = .01f;
30 |
31 | [SerializeField] Color triangleEdgeColor = Color.black;
32 | [SerializeField] Color hullColor = Color.magenta;
33 | [SerializeField] Color voronoiColor = Color.white;
34 |
35 | [SerializeField] Material meshMaterial;
36 | [SerializeField] Material lineMaterial;
37 |
38 |
39 | [SerializeField] float generationSize = 3;
40 | [SerializeField] float generationMinDistance = .2f;
41 |
42 | [SerializeField] bool drawTrianglePoints = true;
43 | [SerializeField] bool drawTriangleEdges = true;
44 | [SerializeField] bool drawVoronoiPoints = true;
45 | [SerializeField] bool drawVoronoiEdges = true;
46 | [SerializeField] bool drawHull = true;
47 | [SerializeField] bool createMesh = true;
48 |
49 |
50 | private void Start()
51 | {
52 | Clear();
53 | }
54 | void Update()
55 | {
56 | if (Input.GetKeyDown(KeyCode.Space))
57 | {
58 | points.Clear();
59 | Clear();
60 | }
61 |
62 | if (Input.GetKeyDown(KeyCode.Return))
63 | {
64 | Clear();
65 |
66 | var sampler = UniformPoissonDiskSampler.SampleCircle(Vector2.zero, generationSize, generationMinDistance);
67 | points = sampler.Select(point => new Vector2(point.x, point.y)).ToPoints().ToList();
68 | Debug.Log($"Generated Points Count {points.Count}");
69 | Create();
70 | return;
71 | }
72 |
73 | if (!Input.GetMouseButtonDown(0)) return;
74 |
75 | var target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
76 |
77 | points.Add(new Point(target.x, target.y));
78 | Create();
79 | }
80 |
81 | private void Create()
82 | {
83 | if (points.Count < 3) return;
84 |
85 | Clear();
86 |
87 | delaunator = new Delaunator(points.ToArray());
88 |
89 | CreateMesh();
90 | CreateTriangle();
91 | CreateHull();
92 | CreateVoronoi();
93 | }
94 |
95 | private void Clear()
96 | {
97 | CreateNewContainers();
98 |
99 | if (meshObject != null)
100 | {
101 | Destroy(meshObject);
102 | }
103 |
104 | delaunator = null;
105 | }
106 |
107 | private void CreateTriangle()
108 | {
109 | if (delaunator == null) return;
110 |
111 | delaunator.ForEachTriangleEdge(edge =>
112 | {
113 | if (drawTriangleEdges)
114 | {
115 | CreateLine(TrianglesContainer, $"TriangleEdge - {edge.Index}", new Vector3[] { edge.P.ToVector3(), edge.Q.ToVector3() }, triangleEdgeColor, triangleEdgeWidth, 0);
116 | }
117 |
118 | if (drawTrianglePoints)
119 | {
120 | var pointGameObject = Instantiate(trianglePointPrefab, PointsContainer);
121 | pointGameObject.transform.SetPositionAndRotation(edge.P.ToVector3(), Quaternion.identity);
122 | }
123 | });
124 | }
125 |
126 | private void CreateHull()
127 | {
128 | if (!drawHull) return;
129 | if (delaunator == null) return;
130 |
131 | CreateNewHullContainer();
132 |
133 | foreach (var edge in delaunator.GetHullEdges())
134 | {
135 | CreateLine(HullContainer, $"Hull Edge", new Vector3[] { edge.P.ToVector3(), edge.Q.ToVector3() }, hullColor, hullEdgeWith, 3);
136 | }
137 | }
138 |
139 | private void CreateVoronoi()
140 | {
141 | if (delaunator == null) return;
142 |
143 | delaunator.ForEachVoronoiEdge(edge =>
144 | {
145 | if (drawVoronoiEdges)
146 | {
147 | CreateLine(VoronoiContainer, $"Voronoi Edge", new Vector3[] { edge.P.ToVector3(), edge.Q.ToVector3() }, voronoiColor, voronoiEdgeWidth, 2);
148 | }
149 | if (drawVoronoiPoints)
150 | {
151 | var pointGameObject = Instantiate(voronoiPointPrefab, PointsContainer);
152 | pointGameObject.transform.SetPositionAndRotation(edge.P.ToVector3(), Quaternion.identity);
153 | }
154 | });
155 | }
156 |
157 | private void CreateLine(Transform container, string name, Vector3[] points, Color color, float width, int order = 1)
158 | {
159 | var lineGameObject = new GameObject(name);
160 | lineGameObject.transform.parent = container;
161 | var lineRenderer = lineGameObject.AddComponent();
162 |
163 | lineRenderer.SetPositions(points);
164 |
165 | lineRenderer.material = lineMaterial ?? new Material(Shader.Find("Standard"));
166 | lineRenderer.startColor = color;
167 | lineRenderer.endColor = color;
168 | lineRenderer.startWidth = width;
169 | lineRenderer.endWidth = width;
170 | lineRenderer.sortingOrder = order;
171 | }
172 |
173 | private void CreateMesh()
174 | {
175 | if (!createMesh) return;
176 |
177 | if (meshObject != null)
178 | {
179 | Destroy(meshObject);
180 | }
181 |
182 | var mesh = new Mesh
183 | {
184 | vertices = delaunator.Points.ToVectors3(),
185 | triangles = delaunator.Triangles
186 | };
187 |
188 | mesh.RecalculateNormals();
189 | mesh.RecalculateBounds();
190 |
191 | meshObject = new GameObject("DelaunatorMesh");
192 | var meshRenderer = meshObject.AddComponent();
193 | meshRenderer.sharedMaterial = meshMaterial ?? new Material(Shader.Find("Standard"));
194 | var meshFilter = meshObject.AddComponent();
195 | meshFilter.mesh = mesh;
196 | }
197 |
198 | private void CreateNewContainers()
199 | {
200 | CreateNewPointsContainer();
201 | CreateNewTrianglesContainer();
202 | CreateNewVoronoiContainer();
203 | CreateNewHullContainer();
204 | }
205 |
206 | private void CreateNewPointsContainer()
207 | {
208 | if (PointsContainer != null)
209 | {
210 | Destroy(PointsContainer.gameObject);
211 | }
212 |
213 | PointsContainer = new GameObject(nameof(PointsContainer)).transform;
214 | }
215 |
216 | private void CreateNewTrianglesContainer()
217 | {
218 | if (TrianglesContainer != null)
219 | {
220 | Destroy(TrianglesContainer.gameObject);
221 | }
222 |
223 | TrianglesContainer = new GameObject(nameof(TrianglesContainer)).transform;
224 | }
225 |
226 | private void CreateNewHullContainer()
227 | {
228 | if (HullContainer != null)
229 | {
230 | Destroy(HullContainer.gameObject);
231 | }
232 |
233 | HullContainer = new GameObject(nameof(HullContainer)).transform;
234 | }
235 |
236 | private void CreateNewVoronoiContainer()
237 | {
238 | if (VoronoiContainer != null)
239 | {
240 | Destroy(VoronoiContainer.gameObject);
241 | }
242 |
243 | VoronoiContainer = new GameObject(nameof(VoronoiContainer)).transform;
244 | }
245 | }
246 | }
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/DelaunatorPreview.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f1c88a4b9e28f8645b88feecfc0f6e08
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/UniformPoissionDiskSampler.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using UnityEngine;
6 |
7 | namespace DelaunatorSharp.Unity
8 | {
9 | // http://theinstructionlimit.com/fast-uniform-poisson-disk-sampling-in-c <- copied from Renaud Bédard
10 |
11 | // Adapated from java source by Herman Tulleken
12 | // http://www.luma.co.za/labs/2008/02/27/poisson-disk-sampling/
13 |
14 | // The algorithm is from the "Fast Poisson Disk Sampling in Arbitrary Dimensions" paper by Robert Bridson
15 | // http://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
16 |
17 | public static class UniformPoissonDiskSampler
18 | {
19 | public const int DefaultPointsPerIteration = 30;
20 |
21 | static readonly float SquareRootTwo = (float)Mathf.Sqrt(2);
22 |
23 | struct Settings
24 | {
25 | public Vector2 TopLeft, LowerRight, Center;
26 | public Vector2 Dimensions;
27 | public float? RejectionSqDistance;
28 | public float MinimumDistance;
29 | public float CellSize;
30 | public int GridWidth, GridHeight;
31 | }
32 |
33 | struct State
34 | {
35 | public Vector2?[,] Grid;
36 | public List ActivePoints, Points;
37 | }
38 |
39 | public static List SampleCircle(Vector2 center, float radius, float minimumDistance)
40 | {
41 | return SampleCircle(center, radius, minimumDistance, DefaultPointsPerIteration);
42 | }
43 | public static List SampleCircle(Vector2 center, float radius, float minimumDistance, int pointsPerIteration)
44 | {
45 | return Sample(center - new Vector2(radius, radius), center + new Vector2(radius, radius), radius, minimumDistance, pointsPerIteration);
46 | }
47 |
48 | public static List SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance)
49 | {
50 | return SampleRectangle(topLeft, lowerRight, minimumDistance, DefaultPointsPerIteration);
51 | }
52 | public static List SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance, int pointsPerIteration)
53 | {
54 | return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration);
55 | }
56 |
57 | static List Sample(Vector2 topLeft, Vector2 lowerRight, float? rejectionDistance, float minimumDistance, int pointsPerIteration)
58 | {
59 | var settings = new Settings
60 | {
61 | TopLeft = topLeft,
62 | LowerRight = lowerRight,
63 | Dimensions = lowerRight - topLeft,
64 | Center = (topLeft + lowerRight) / 2,
65 | CellSize = minimumDistance / SquareRootTwo,
66 | MinimumDistance = minimumDistance,
67 | RejectionSqDistance = rejectionDistance == null ? null : rejectionDistance * rejectionDistance
68 | };
69 | settings.GridWidth = (int)(settings.Dimensions.x / settings.CellSize) + 1;
70 | settings.GridHeight = (int)(settings.Dimensions.y / settings.CellSize) + 1;
71 |
72 | var state = new State
73 | {
74 | Grid = new Vector2?[settings.GridWidth, settings.GridHeight],
75 | ActivePoints = new List(),
76 | Points = new List()
77 | };
78 |
79 | AddFirstPoint(ref settings, ref state);
80 |
81 | while (state.ActivePoints.Count != 0)
82 | {
83 | var listIndex = Random.Range(0, state.ActivePoints.Count);
84 |
85 | var point = state.ActivePoints[listIndex];
86 | var found = false;
87 |
88 | for (var k = 0; k < pointsPerIteration; k++)
89 | found |= AddNextPoint(point, ref settings, ref state);
90 |
91 | if (!found)
92 | state.ActivePoints.RemoveAt(listIndex);
93 | }
94 |
95 | return state.Points;
96 | }
97 |
98 | static void AddFirstPoint(ref Settings settings, ref State state)
99 | {
100 | var added = false;
101 | while (!added)
102 | {
103 | var d = Random.value;
104 | var xr = settings.TopLeft.x + settings.Dimensions.x * d;
105 |
106 | d = Random.value;
107 | var yr = settings.TopLeft.y + settings.Dimensions.y * d;
108 |
109 | var p = new Vector2((float)xr, (float)yr);
110 | if (settings.RejectionSqDistance != null && DistanceSquared(settings.Center, p) > settings.RejectionSqDistance)
111 | continue;
112 | added = true;
113 |
114 | var index = Denormalize(p, settings.TopLeft, settings.CellSize);
115 |
116 | state.Grid[(int)index.x, (int)index.y] = p;
117 |
118 | state.ActivePoints.Add(p);
119 | state.Points.Add(p);
120 | }
121 | }
122 |
123 | static bool AddNextPoint(Vector2 point, ref Settings settings, ref State state)
124 | {
125 | var found = false;
126 | var q = GenerateRandomAround(point, settings.MinimumDistance);
127 |
128 | if (q.x >= settings.TopLeft.x && q.x < settings.LowerRight.x &&
129 | q.y > settings.TopLeft.y && q.y < settings.LowerRight.y &&
130 | (settings.RejectionSqDistance == null || DistanceSquared(settings.Center, q) <= settings.RejectionSqDistance))
131 | {
132 | var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize);
133 | var tooClose = false;
134 |
135 | for (var i = (int)Mathf.Max(0, qIndex.x - 2); i < Mathf.Min(settings.GridWidth, qIndex.x + 3) && !tooClose; i++)
136 | for (var j = (int)Mathf.Max(0, qIndex.y - 2); j < Mathf.Min(settings.GridHeight, qIndex.y + 3) && !tooClose; j++)
137 | if (state.Grid[i, j].HasValue && Vector2.Distance(state.Grid[i, j].Value, q) < settings.MinimumDistance)
138 | tooClose = true;
139 |
140 | if (!tooClose)
141 | {
142 | found = true;
143 | state.ActivePoints.Add(q);
144 | state.Points.Add(q);
145 | state.Grid[(int)qIndex.x, (int)qIndex.y] = q;
146 | }
147 | }
148 | return found;
149 | }
150 |
151 | static Vector2 GenerateRandomAround(Vector2 center, float minimumDistance)
152 | {
153 | var d = Random.value;
154 | var radius = minimumDistance + minimumDistance * d;
155 |
156 | d = Random.value;
157 | var angle = MathHelper.TwoPi * d;
158 |
159 | var newX = radius * Mathf.Sin(angle);
160 | var newY = radius * Mathf.Cos(angle);
161 |
162 | return new Vector2((float)(center.x + newX), (float)(center.y + newY));
163 | }
164 |
165 | static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize)
166 | {
167 | return new Vector2((int)((point.x - origin.x) / cellSize), (int)((point.y - origin.y) / cellSize));
168 | }
169 | static float DistanceSquared(Vector2 value1, Vector2 value2)
170 | {
171 | float x = value1.x - value2.x;
172 | float y = value1.y - value2.y;
173 |
174 | return (x * x) + (y * y);
175 | }
176 | }
177 |
178 | public static class MathHelper
179 | {
180 | public const float Pi = (float)Mathf.PI;
181 | public const float HalfPi = (float)(Mathf.PI / 2);
182 | public const float TwoPi = (float)(Mathf.PI * 2);
183 | }
184 | }
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Runtime/Scripts/UniformPoissionDiskSampler.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f3df112ae15bc2e49bf1e1ff349867fb
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Materials.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f290ef833da6192489c8f173cdff1145
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Materials/LineMaterial.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 6
6 | m_ObjectHideFlags: 0
7 | m_CorrespondingSourceObject: {fileID: 0}
8 | m_PrefabInstance: {fileID: 0}
9 | m_PrefabAsset: {fileID: 0}
10 | m_Name: LineMaterial
11 | m_Shader: {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0}
12 | m_ShaderKeywords:
13 | m_LightmapFlags: 4
14 | m_EnableInstancingVariants: 0
15 | m_DoubleSidedGI: 0
16 | m_CustomRenderQueue: -1
17 | stringTagMap: {}
18 | disabledShaderPasses: []
19 | m_SavedProperties:
20 | serializedVersion: 3
21 | m_TexEnvs:
22 | - _AlphaTex:
23 | m_Texture: {fileID: 0}
24 | m_Scale: {x: 1, y: 1}
25 | m_Offset: {x: 0, y: 0}
26 | - _BumpMap:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | - _DetailAlbedoMap:
31 | m_Texture: {fileID: 0}
32 | m_Scale: {x: 1, y: 1}
33 | m_Offset: {x: 0, y: 0}
34 | - _DetailMask:
35 | m_Texture: {fileID: 0}
36 | m_Scale: {x: 1, y: 1}
37 | m_Offset: {x: 0, y: 0}
38 | - _DetailNormalMap:
39 | m_Texture: {fileID: 0}
40 | m_Scale: {x: 1, y: 1}
41 | m_Offset: {x: 0, y: 0}
42 | - _EmissionMap:
43 | m_Texture: {fileID: 0}
44 | m_Scale: {x: 1, y: 1}
45 | m_Offset: {x: 0, y: 0}
46 | - _MainTex:
47 | m_Texture: {fileID: 0}
48 | m_Scale: {x: 1, y: 1}
49 | m_Offset: {x: 0, y: 0}
50 | - _MetallicGlossMap:
51 | m_Texture: {fileID: 0}
52 | m_Scale: {x: 1, y: 1}
53 | m_Offset: {x: 0, y: 0}
54 | - _OcclusionMap:
55 | m_Texture: {fileID: 0}
56 | m_Scale: {x: 1, y: 1}
57 | m_Offset: {x: 0, y: 0}
58 | - _ParallaxMap:
59 | m_Texture: {fileID: 0}
60 | m_Scale: {x: 1, y: 1}
61 | m_Offset: {x: 0, y: 0}
62 | m_Floats:
63 | - PixelSnap: 0
64 | - _BumpScale: 1
65 | - _Cutoff: 0.5
66 | - _DetailNormalMapScale: 1
67 | - _DstBlend: 0
68 | - _EnableExternalAlpha: 0
69 | - _GlossMapScale: 1
70 | - _Glossiness: 0.5
71 | - _GlossyReflections: 1
72 | - _Metallic: 0
73 | - _Mode: 0
74 | - _OcclusionStrength: 1
75 | - _Parallax: 0.02
76 | - _SmoothnessTextureChannel: 0
77 | - _SpecularHighlights: 1
78 | - _SrcBlend: 1
79 | - _UVSec: 0
80 | - _ZWrite: 1
81 | m_Colors:
82 | - _Color: {r: 1, g: 1, b: 1, a: 1}
83 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
84 | - _Flip: {r: 1, g: 1, b: 1, a: 1}
85 | - _RendererColor: {r: 1, g: 1, b: 1, a: 1}
86 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Materials/LineMaterial.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: fe60369f18361df418fb98a1ee9d6ea5
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Materials/MeshMaterial.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 6
6 | m_ObjectHideFlags: 0
7 | m_CorrespondingSourceObject: {fileID: 0}
8 | m_PrefabInstance: {fileID: 0}
9 | m_PrefabAsset: {fileID: 0}
10 | m_Name: MeshMaterial
11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
12 | m_ShaderKeywords:
13 | m_LightmapFlags: 4
14 | m_EnableInstancingVariants: 0
15 | m_DoubleSidedGI: 0
16 | m_CustomRenderQueue: -1
17 | stringTagMap: {}
18 | disabledShaderPasses: []
19 | m_SavedProperties:
20 | serializedVersion: 3
21 | m_TexEnvs:
22 | - _BumpMap:
23 | m_Texture: {fileID: 0}
24 | m_Scale: {x: 1, y: 1}
25 | m_Offset: {x: 0, y: 0}
26 | - _DetailAlbedoMap:
27 | m_Texture: {fileID: 0}
28 | m_Scale: {x: 1, y: 1}
29 | m_Offset: {x: 0, y: 0}
30 | - _DetailMask:
31 | m_Texture: {fileID: 0}
32 | m_Scale: {x: 1, y: 1}
33 | m_Offset: {x: 0, y: 0}
34 | - _DetailNormalMap:
35 | m_Texture: {fileID: 0}
36 | m_Scale: {x: 1, y: 1}
37 | m_Offset: {x: 0, y: 0}
38 | - _EmissionMap:
39 | m_Texture: {fileID: 0}
40 | m_Scale: {x: 1, y: 1}
41 | m_Offset: {x: 0, y: 0}
42 | - _MainTex:
43 | m_Texture: {fileID: 0}
44 | m_Scale: {x: 1, y: 1}
45 | m_Offset: {x: 0, y: 0}
46 | - _MetallicGlossMap:
47 | m_Texture: {fileID: 0}
48 | m_Scale: {x: 1, y: 1}
49 | m_Offset: {x: 0, y: 0}
50 | - _OcclusionMap:
51 | m_Texture: {fileID: 0}
52 | m_Scale: {x: 1, y: 1}
53 | m_Offset: {x: 0, y: 0}
54 | - _ParallaxMap:
55 | m_Texture: {fileID: 0}
56 | m_Scale: {x: 1, y: 1}
57 | m_Offset: {x: 0, y: 0}
58 | m_Floats:
59 | - _BumpScale: 1
60 | - _Cutoff: 0.5
61 | - _DetailNormalMapScale: 1
62 | - _DstBlend: 0
63 | - _GlossMapScale: 1
64 | - _Glossiness: 0.5
65 | - _GlossyReflections: 1
66 | - _Metallic: 0
67 | - _Mode: 0
68 | - _OcclusionStrength: 1
69 | - _Parallax: 0.02
70 | - _SmoothnessTextureChannel: 0
71 | - _SpecularHighlights: 1
72 | - _SrcBlend: 1
73 | - _UVSec: 0
74 | - _ZWrite: 1
75 | m_Colors:
76 | - _Color: {r: 0, g: 0, b: 0, a: 1}
77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
78 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Materials/MeshMaterial.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f6a42aa8c693b6c4ebf406ff7e09ef4a
3 | NativeFormatImporter:
4 | externalObjects: {}
5 | mainObjectFileID: 0
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e86fb5b042cadf3489d671da84d4a0a1
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/DelaunatorPreview.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &1292916145698231221
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | serializedVersion: 6
10 | m_Component:
11 | - component: {fileID: 1292916145698231222}
12 | - component: {fileID: 1292916145698231223}
13 | m_Layer: 0
14 | m_Name: DelaunatorPreview
15 | m_TagString: Untagged
16 | m_Icon: {fileID: 0}
17 | m_NavMeshLayer: 0
18 | m_StaticEditorFlags: 0
19 | m_IsActive: 1
20 | --- !u!4 &1292916145698231222
21 | Transform:
22 | m_ObjectHideFlags: 0
23 | m_CorrespondingSourceObject: {fileID: 0}
24 | m_PrefabInstance: {fileID: 0}
25 | m_PrefabAsset: {fileID: 0}
26 | m_GameObject: {fileID: 1292916145698231221}
27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
28 | m_LocalPosition: {x: 0, y: 0, z: 0}
29 | m_LocalScale: {x: 1, y: 1, z: 1}
30 | m_Children: []
31 | m_Father: {fileID: 0}
32 | m_RootOrder: 0
33 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
34 | --- !u!114 &1292916145698231223
35 | MonoBehaviour:
36 | m_ObjectHideFlags: 0
37 | m_CorrespondingSourceObject: {fileID: 0}
38 | m_PrefabInstance: {fileID: 0}
39 | m_PrefabAsset: {fileID: 0}
40 | m_GameObject: {fileID: 1292916145698231221}
41 | m_Enabled: 1
42 | m_EditorHideFlags: 0
43 | m_Script: {fileID: 11500000, guid: f1c88a4b9e28f8645b88feecfc0f6e08, type: 3}
44 | m_Name:
45 | m_EditorClassIdentifier:
46 | trianglePointPrefab: {fileID: 5961768956659207801, guid: c31800eeb430deb4b96b784ea16dd9cf,
47 | type: 3}
48 | voronoiPointPrefab: {fileID: 5961768956659207801, guid: 6c0e382d52a86f448ba06f8ff7c14a96,
49 | type: 3}
50 | voronoiEdgeWidth: 0.01
51 | triangleEdgeWidth: 0.01
52 | hullEdgeWith: 0.03
53 | triangleEdgeColor: {r: 0, g: 0, b: 0, a: 1}
54 | hullColor: {r: 1, g: 0.56366897, b: 0, a: 1}
55 | voronoiColor: {r: 1, g: 1, b: 1, a: 1}
56 | meshMaterial: {fileID: 2100000, guid: f6a42aa8c693b6c4ebf406ff7e09ef4a, type: 2}
57 | lineMaterial: {fileID: 2100000, guid: fe60369f18361df418fb98a1ee9d6ea5, type: 2}
58 | generationSize: 4.5
59 | generationMinDistance: 0.35
60 | drawTrianglePoints: 1
61 | drawTriangleEdges: 1
62 | drawVoronoiPoints: 1
63 | drawVoronoiEdges: 1
64 | drawHull: 1
65 | createMesh: 1
66 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/DelaunatorPreview.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c5d82fa185d78334199d0cfd6751110d
3 | PrefabImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/Triangle_PointPrefab.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &5961768956659207801
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | serializedVersion: 6
10 | m_Component:
11 | - component: {fileID: 5961768956659207807}
12 | - component: {fileID: 5961768956659207800}
13 | m_Layer: 0
14 | m_Name: Triangle_PointPrefab
15 | m_TagString: Untagged
16 | m_Icon: {fileID: 0}
17 | m_NavMeshLayer: 0
18 | m_StaticEditorFlags: 0
19 | m_IsActive: 1
20 | --- !u!4 &5961768956659207807
21 | Transform:
22 | m_ObjectHideFlags: 0
23 | m_CorrespondingSourceObject: {fileID: 0}
24 | m_PrefabInstance: {fileID: 0}
25 | m_PrefabAsset: {fileID: 0}
26 | m_GameObject: {fileID: 5961768956659207801}
27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
28 | m_LocalPosition: {x: 0, y: 0, z: 0}
29 | m_LocalScale: {x: 0.35, y: 0.35, z: 1}
30 | m_Children: []
31 | m_Father: {fileID: 0}
32 | m_RootOrder: 0
33 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
34 | --- !u!212 &5961768956659207800
35 | SpriteRenderer:
36 | m_ObjectHideFlags: 0
37 | m_CorrespondingSourceObject: {fileID: 0}
38 | m_PrefabInstance: {fileID: 0}
39 | m_PrefabAsset: {fileID: 0}
40 | m_GameObject: {fileID: 5961768956659207801}
41 | m_Enabled: 1
42 | m_CastShadows: 0
43 | m_ReceiveShadows: 0
44 | m_DynamicOccludee: 1
45 | m_MotionVectors: 1
46 | m_LightProbeUsage: 1
47 | m_ReflectionProbeUsage: 1
48 | m_RayTracingMode: 0
49 | m_RenderingLayerMask: 1
50 | m_RendererPriority: 0
51 | m_Materials:
52 | - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
53 | m_StaticBatchInfo:
54 | firstSubMesh: 0
55 | subMeshCount: 0
56 | m_StaticBatchRoot: {fileID: 0}
57 | m_ProbeAnchor: {fileID: 0}
58 | m_LightProbeVolumeOverride: {fileID: 0}
59 | m_ScaleInLightmap: 1
60 | m_ReceiveGI: 1
61 | m_PreserveUVs: 0
62 | m_IgnoreNormalsForChartDetection: 0
63 | m_ImportantGI: 0
64 | m_StitchLightmapSeams: 1
65 | m_SelectedEditorRenderState: 0
66 | m_MinimumChartSize: 4
67 | m_AutoUVMaxDistance: 0.5
68 | m_AutoUVMaxAngle: 89
69 | m_LightmapParameters: {fileID: 0}
70 | m_SortingLayerID: 0
71 | m_SortingLayer: 0
72 | m_SortingOrder: 0
73 | m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
74 | m_Color: {r: 0.9882353, g: 0.21568628, b: 0.25199825, a: 1}
75 | m_FlipX: 0
76 | m_FlipY: 0
77 | m_DrawMode: 0
78 | m_Size: {x: 0.16, y: 0.16}
79 | m_AdaptiveModeThreshold: 0.5
80 | m_SpriteTileMode: 0
81 | m_WasSpriteAssigned: 1
82 | m_MaskInteraction: 0
83 | m_SpriteSortPoint: 0
84 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/Triangle_PointPrefab.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c31800eeb430deb4b96b784ea16dd9cf
3 | PrefabImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/Voronoi_PointPrefab.prefab:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!1 &5961768956659207801
4 | GameObject:
5 | m_ObjectHideFlags: 0
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | serializedVersion: 6
10 | m_Component:
11 | - component: {fileID: 5961768956659207807}
12 | - component: {fileID: 5961768956659207800}
13 | m_Layer: 0
14 | m_Name: Voronoi_PointPrefab
15 | m_TagString: Untagged
16 | m_Icon: {fileID: 0}
17 | m_NavMeshLayer: 0
18 | m_StaticEditorFlags: 0
19 | m_IsActive: 1
20 | --- !u!4 &5961768956659207807
21 | Transform:
22 | m_ObjectHideFlags: 0
23 | m_CorrespondingSourceObject: {fileID: 0}
24 | m_PrefabInstance: {fileID: 0}
25 | m_PrefabAsset: {fileID: 0}
26 | m_GameObject: {fileID: 5961768956659207801}
27 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
28 | m_LocalPosition: {x: 0, y: 0, z: 0}
29 | m_LocalScale: {x: 0.35, y: 0.35, z: 1}
30 | m_Children: []
31 | m_Father: {fileID: 0}
32 | m_RootOrder: 0
33 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
34 | --- !u!212 &5961768956659207800
35 | SpriteRenderer:
36 | m_ObjectHideFlags: 0
37 | m_CorrespondingSourceObject: {fileID: 0}
38 | m_PrefabInstance: {fileID: 0}
39 | m_PrefabAsset: {fileID: 0}
40 | m_GameObject: {fileID: 5961768956659207801}
41 | m_Enabled: 1
42 | m_CastShadows: 0
43 | m_ReceiveShadows: 0
44 | m_DynamicOccludee: 1
45 | m_MotionVectors: 1
46 | m_LightProbeUsage: 1
47 | m_ReflectionProbeUsage: 1
48 | m_RayTracingMode: 0
49 | m_RenderingLayerMask: 1
50 | m_RendererPriority: 0
51 | m_Materials:
52 | - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
53 | m_StaticBatchInfo:
54 | firstSubMesh: 0
55 | subMeshCount: 0
56 | m_StaticBatchRoot: {fileID: 0}
57 | m_ProbeAnchor: {fileID: 0}
58 | m_LightProbeVolumeOverride: {fileID: 0}
59 | m_ScaleInLightmap: 1
60 | m_ReceiveGI: 1
61 | m_PreserveUVs: 0
62 | m_IgnoreNormalsForChartDetection: 0
63 | m_ImportantGI: 0
64 | m_StitchLightmapSeams: 1
65 | m_SelectedEditorRenderState: 0
66 | m_MinimumChartSize: 4
67 | m_AutoUVMaxDistance: 0.5
68 | m_AutoUVMaxAngle: 89
69 | m_LightmapParameters: {fileID: 0}
70 | m_SortingLayerID: 0
71 | m_SortingLayer: 0
72 | m_SortingOrder: 0
73 | m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0}
74 | m_Color: {r: 0.21568629, g: 0.21568629, b: 0.98823535, a: 1}
75 | m_FlipX: 0
76 | m_FlipY: 0
77 | m_DrawMode: 0
78 | m_Size: {x: 0.16, y: 0.16}
79 | m_AdaptiveModeThreshold: 0.5
80 | m_SpriteTileMode: 0
81 | m_WasSpriteAssigned: 1
82 | m_MaskInteraction: 0
83 | m_SpriteSortPoint: 0
84 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Prefabs/Voronoi_PointPrefab.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 6c0e382d52a86f448ba06f8ff7c14a96
3 | PrefabImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8a3f6913b68718041958ed7240d789ab
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Scenes/SampleScene.unity:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!29 &1
4 | OcclusionCullingSettings:
5 | m_ObjectHideFlags: 0
6 | serializedVersion: 2
7 | m_OcclusionBakeSettings:
8 | smallestOccluder: 5
9 | smallestHole: 0.25
10 | backfaceThreshold: 100
11 | m_SceneGUID: 00000000000000000000000000000000
12 | m_OcclusionCullingData: {fileID: 0}
13 | --- !u!104 &2
14 | RenderSettings:
15 | m_ObjectHideFlags: 0
16 | serializedVersion: 9
17 | m_Fog: 0
18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
19 | m_FogMode: 3
20 | m_FogDensity: 0.01
21 | m_LinearFogStart: 0
22 | m_LinearFogEnd: 300
23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
26 | m_AmbientIntensity: 1
27 | m_AmbientMode: 3
28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
29 | m_SkyboxMaterial: {fileID: 0}
30 | m_HaloStrength: 0.5
31 | m_FlareStrength: 1
32 | m_FlareFadeSpeed: 3
33 | m_HaloTexture: {fileID: 0}
34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
35 | m_DefaultReflectionMode: 0
36 | m_DefaultReflectionResolution: 128
37 | m_ReflectionBounces: 1
38 | m_ReflectionIntensity: 1
39 | m_CustomReflection: {fileID: 0}
40 | m_Sun: {fileID: 0}
41 | m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
42 | m_UseRadianceAmbientProbe: 0
43 | --- !u!157 &3
44 | LightmapSettings:
45 | m_ObjectHideFlags: 0
46 | serializedVersion: 11
47 | m_GIWorkflowMode: 1
48 | m_GISettings:
49 | serializedVersion: 2
50 | m_BounceScale: 1
51 | m_IndirectOutputScale: 1
52 | m_AlbedoBoost: 1
53 | m_EnvironmentLightingMode: 0
54 | m_EnableBakedLightmaps: 0
55 | m_EnableRealtimeLightmaps: 0
56 | m_LightmapEditorSettings:
57 | serializedVersion: 12
58 | m_Resolution: 2
59 | m_BakeResolution: 40
60 | m_AtlasSize: 1024
61 | m_AO: 0
62 | m_AOMaxDistance: 1
63 | m_CompAOExponent: 1
64 | m_CompAOExponentDirect: 0
65 | m_ExtractAmbientOcclusion: 0
66 | m_Padding: 2
67 | m_LightmapParameters: {fileID: 0}
68 | m_LightmapsBakeMode: 1
69 | m_TextureCompression: 1
70 | m_FinalGather: 0
71 | m_FinalGatherFiltering: 1
72 | m_FinalGatherRayCount: 256
73 | m_ReflectionCompression: 2
74 | m_MixedBakeMode: 2
75 | m_BakeBackend: 0
76 | m_PVRSampling: 1
77 | m_PVRDirectSampleCount: 32
78 | m_PVRSampleCount: 500
79 | m_PVRBounces: 2
80 | m_PVREnvironmentSampleCount: 500
81 | m_PVREnvironmentReferencePointCount: 2048
82 | m_PVRFilteringMode: 2
83 | m_PVRDenoiserTypeDirect: 0
84 | m_PVRDenoiserTypeIndirect: 0
85 | m_PVRDenoiserTypeAO: 0
86 | m_PVRFilterTypeDirect: 0
87 | m_PVRFilterTypeIndirect: 0
88 | m_PVRFilterTypeAO: 0
89 | m_PVREnvironmentMIS: 0
90 | m_PVRCulling: 1
91 | m_PVRFilteringGaussRadiusDirect: 1
92 | m_PVRFilteringGaussRadiusIndirect: 5
93 | m_PVRFilteringGaussRadiusAO: 2
94 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5
95 | m_PVRFilteringAtrousPositionSigmaIndirect: 2
96 | m_PVRFilteringAtrousPositionSigmaAO: 1
97 | m_ExportTrainingData: 0
98 | m_TrainingDataDestination: TrainingData
99 | m_LightProbeSampleCountMultiplier: 4
100 | m_LightingDataAsset: {fileID: 0}
101 | m_UseShadowmask: 1
102 | --- !u!196 &4
103 | NavMeshSettings:
104 | serializedVersion: 2
105 | m_ObjectHideFlags: 0
106 | m_BuildSettings:
107 | serializedVersion: 2
108 | agentTypeID: 0
109 | agentRadius: 0.5
110 | agentHeight: 2
111 | agentSlope: 45
112 | agentClimb: 0.4
113 | ledgeDropHeight: 0
114 | maxJumpAcrossDistance: 0
115 | minRegionArea: 2
116 | manualCellSize: 0
117 | cellSize: 0.16666667
118 | manualTileSize: 0
119 | tileSize: 256
120 | accuratePlacement: 0
121 | debug:
122 | m_Flags: 0
123 | m_NavMeshData: {fileID: 0}
124 | --- !u!1 &28093998
125 | GameObject:
126 | m_ObjectHideFlags: 0
127 | m_CorrespondingSourceObject: {fileID: 0}
128 | m_PrefabInstance: {fileID: 0}
129 | m_PrefabAsset: {fileID: 0}
130 | serializedVersion: 6
131 | m_Component:
132 | - component: {fileID: 28094002}
133 | - component: {fileID: 28094001}
134 | - component: {fileID: 28094000}
135 | - component: {fileID: 28093999}
136 | m_Layer: 5
137 | m_Name: Canvas
138 | m_TagString: Untagged
139 | m_Icon: {fileID: 0}
140 | m_NavMeshLayer: 0
141 | m_StaticEditorFlags: 0
142 | m_IsActive: 1
143 | --- !u!114 &28093999
144 | MonoBehaviour:
145 | m_ObjectHideFlags: 0
146 | m_CorrespondingSourceObject: {fileID: 0}
147 | m_PrefabInstance: {fileID: 0}
148 | m_PrefabAsset: {fileID: 0}
149 | m_GameObject: {fileID: 28093998}
150 | m_Enabled: 1
151 | m_EditorHideFlags: 0
152 | m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
153 | m_Name:
154 | m_EditorClassIdentifier:
155 | m_IgnoreReversedGraphics: 1
156 | m_BlockingObjects: 0
157 | m_BlockingMask:
158 | serializedVersion: 2
159 | m_Bits: 4294967295
160 | --- !u!114 &28094000
161 | MonoBehaviour:
162 | m_ObjectHideFlags: 0
163 | m_CorrespondingSourceObject: {fileID: 0}
164 | m_PrefabInstance: {fileID: 0}
165 | m_PrefabAsset: {fileID: 0}
166 | m_GameObject: {fileID: 28093998}
167 | m_Enabled: 1
168 | m_EditorHideFlags: 0
169 | m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
170 | m_Name:
171 | m_EditorClassIdentifier:
172 | m_UiScaleMode: 0
173 | m_ReferencePixelsPerUnit: 100
174 | m_ScaleFactor: 1
175 | m_ReferenceResolution: {x: 800, y: 600}
176 | m_ScreenMatchMode: 0
177 | m_MatchWidthOrHeight: 0
178 | m_PhysicalUnit: 3
179 | m_FallbackScreenDPI: 96
180 | m_DefaultSpriteDPI: 96
181 | m_DynamicPixelsPerUnit: 1
182 | --- !u!223 &28094001
183 | Canvas:
184 | m_ObjectHideFlags: 0
185 | m_CorrespondingSourceObject: {fileID: 0}
186 | m_PrefabInstance: {fileID: 0}
187 | m_PrefabAsset: {fileID: 0}
188 | m_GameObject: {fileID: 28093998}
189 | m_Enabled: 1
190 | serializedVersion: 3
191 | m_RenderMode: 0
192 | m_Camera: {fileID: 0}
193 | m_PlaneDistance: 100
194 | m_PixelPerfect: 0
195 | m_ReceivesEvents: 1
196 | m_OverrideSorting: 0
197 | m_OverridePixelPerfect: 0
198 | m_SortingBucketNormalizedSize: 0
199 | m_AdditionalShaderChannelsFlag: 0
200 | m_SortingLayerID: 0
201 | m_SortingOrder: 0
202 | m_TargetDisplay: 0
203 | --- !u!224 &28094002
204 | RectTransform:
205 | m_ObjectHideFlags: 0
206 | m_CorrespondingSourceObject: {fileID: 0}
207 | m_PrefabInstance: {fileID: 0}
208 | m_PrefabAsset: {fileID: 0}
209 | m_GameObject: {fileID: 28093998}
210 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
211 | m_LocalPosition: {x: 0, y: 0, z: 0}
212 | m_LocalScale: {x: 0, y: 0, z: 0}
213 | m_Children:
214 | - {fileID: 1200371491}
215 | - {fileID: 1187637874}
216 | m_Father: {fileID: 0}
217 | m_RootOrder: 1
218 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
219 | m_AnchorMin: {x: 0, y: 0}
220 | m_AnchorMax: {x: 0, y: 0}
221 | m_AnchoredPosition: {x: 0, y: 0}
222 | m_SizeDelta: {x: 0, y: 0}
223 | m_Pivot: {x: 0, y: 0}
224 | --- !u!1 &494305046
225 | GameObject:
226 | m_ObjectHideFlags: 0
227 | m_CorrespondingSourceObject: {fileID: 0}
228 | m_PrefabInstance: {fileID: 0}
229 | m_PrefabAsset: {fileID: 0}
230 | serializedVersion: 6
231 | m_Component:
232 | - component: {fileID: 494305048}
233 | - component: {fileID: 494305047}
234 | m_Layer: 0
235 | m_Name: Directional Light
236 | m_TagString: Untagged
237 | m_Icon: {fileID: 0}
238 | m_NavMeshLayer: 0
239 | m_StaticEditorFlags: 0
240 | m_IsActive: 1
241 | --- !u!108 &494305047
242 | Light:
243 | m_ObjectHideFlags: 0
244 | m_CorrespondingSourceObject: {fileID: 0}
245 | m_PrefabInstance: {fileID: 0}
246 | m_PrefabAsset: {fileID: 0}
247 | m_GameObject: {fileID: 494305046}
248 | m_Enabled: 1
249 | serializedVersion: 10
250 | m_Type: 1
251 | m_Shape: 0
252 | m_Color: {r: 1, g: 1, b: 1, a: 1}
253 | m_Intensity: 1
254 | m_Range: 10
255 | m_SpotAngle: 30
256 | m_InnerSpotAngle: 21.80208
257 | m_CookieSize: 10
258 | m_Shadows:
259 | m_Type: 0
260 | m_Resolution: -1
261 | m_CustomResolution: -1
262 | m_Strength: 1
263 | m_Bias: 0.05
264 | m_NormalBias: 0.4
265 | m_NearPlane: 0.2
266 | m_CullingMatrixOverride:
267 | e00: 1
268 | e01: 0
269 | e02: 0
270 | e03: 0
271 | e10: 0
272 | e11: 1
273 | e12: 0
274 | e13: 0
275 | e20: 0
276 | e21: 0
277 | e22: 1
278 | e23: 0
279 | e30: 0
280 | e31: 0
281 | e32: 0
282 | e33: 1
283 | m_UseCullingMatrixOverride: 0
284 | m_Cookie: {fileID: 0}
285 | m_DrawHalo: 0
286 | m_Flare: {fileID: 0}
287 | m_RenderMode: 0
288 | m_CullingMask:
289 | serializedVersion: 2
290 | m_Bits: 4294967295
291 | m_RenderingLayerMask: 1
292 | m_Lightmapping: 4
293 | m_LightShadowCasterMode: 0
294 | m_AreaSize: {x: 1, y: 1}
295 | m_BounceIntensity: 1
296 | m_ColorTemperature: 6570
297 | m_UseColorTemperature: 0
298 | m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
299 | m_UseBoundingSphereOverride: 0
300 | m_ShadowRadius: 0
301 | m_ShadowAngle: 0
302 | --- !u!4 &494305048
303 | Transform:
304 | m_ObjectHideFlags: 0
305 | m_CorrespondingSourceObject: {fileID: 0}
306 | m_PrefabInstance: {fileID: 0}
307 | m_PrefabAsset: {fileID: 0}
308 | m_GameObject: {fileID: 494305046}
309 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
310 | m_LocalPosition: {x: 0, y: 0, z: 0}
311 | m_LocalScale: {x: 1, y: 1, z: 1}
312 | m_Children: []
313 | m_Father: {fileID: 0}
314 | m_RootOrder: 3
315 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
316 | --- !u!1 &519420028
317 | GameObject:
318 | m_ObjectHideFlags: 0
319 | m_CorrespondingSourceObject: {fileID: 0}
320 | m_PrefabInstance: {fileID: 0}
321 | m_PrefabAsset: {fileID: 0}
322 | serializedVersion: 6
323 | m_Component:
324 | - component: {fileID: 519420032}
325 | - component: {fileID: 519420031}
326 | - component: {fileID: 519420029}
327 | m_Layer: 0
328 | m_Name: Main Camera
329 | m_TagString: MainCamera
330 | m_Icon: {fileID: 0}
331 | m_NavMeshLayer: 0
332 | m_StaticEditorFlags: 0
333 | m_IsActive: 1
334 | --- !u!81 &519420029
335 | AudioListener:
336 | m_ObjectHideFlags: 0
337 | m_CorrespondingSourceObject: {fileID: 0}
338 | m_PrefabInstance: {fileID: 0}
339 | m_PrefabAsset: {fileID: 0}
340 | m_GameObject: {fileID: 519420028}
341 | m_Enabled: 1
342 | --- !u!20 &519420031
343 | Camera:
344 | m_ObjectHideFlags: 0
345 | m_CorrespondingSourceObject: {fileID: 0}
346 | m_PrefabInstance: {fileID: 0}
347 | m_PrefabAsset: {fileID: 0}
348 | m_GameObject: {fileID: 519420028}
349 | m_Enabled: 1
350 | serializedVersion: 2
351 | m_ClearFlags: 2
352 | m_BackGroundColor: {r: 0.254717, g: 0.254717, b: 0.254717, a: 1}
353 | m_projectionMatrixMode: 1
354 | m_GateFitMode: 2
355 | m_FOVAxisMode: 0
356 | m_SensorSize: {x: 36, y: 24}
357 | m_LensShift: {x: 0, y: 0}
358 | m_FocalLength: 50
359 | m_NormalizedViewPortRect:
360 | serializedVersion: 2
361 | x: 0
362 | y: 0
363 | width: 1
364 | height: 1
365 | near clip plane: 0.3
366 | far clip plane: 1000
367 | field of view: 60
368 | orthographic: 1
369 | orthographic size: 5
370 | m_Depth: -1
371 | m_CullingMask:
372 | serializedVersion: 2
373 | m_Bits: 4294967295
374 | m_RenderingPath: -1
375 | m_TargetTexture: {fileID: 0}
376 | m_TargetDisplay: 0
377 | m_TargetEye: 0
378 | m_HDR: 1
379 | m_AllowMSAA: 0
380 | m_AllowDynamicResolution: 0
381 | m_ForceIntoRT: 0
382 | m_OcclusionCulling: 0
383 | m_StereoConvergence: 10
384 | m_StereoSeparation: 0.022
385 | --- !u!4 &519420032
386 | Transform:
387 | m_ObjectHideFlags: 0
388 | m_CorrespondingSourceObject: {fileID: 0}
389 | m_PrefabInstance: {fileID: 0}
390 | m_PrefabAsset: {fileID: 0}
391 | m_GameObject: {fileID: 519420028}
392 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
393 | m_LocalPosition: {x: 0, y: 0, z: -10}
394 | m_LocalScale: {x: 1, y: 1, z: 1}
395 | m_Children: []
396 | m_Father: {fileID: 0}
397 | m_RootOrder: 0
398 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
399 | --- !u!1 &763959694
400 | GameObject:
401 | m_ObjectHideFlags: 0
402 | m_CorrespondingSourceObject: {fileID: 0}
403 | m_PrefabInstance: {fileID: 0}
404 | m_PrefabAsset: {fileID: 0}
405 | serializedVersion: 6
406 | m_Component:
407 | - component: {fileID: 763959697}
408 | - component: {fileID: 763959696}
409 | - component: {fileID: 763959695}
410 | m_Layer: 0
411 | m_Name: EventSystem
412 | m_TagString: Untagged
413 | m_Icon: {fileID: 0}
414 | m_NavMeshLayer: 0
415 | m_StaticEditorFlags: 0
416 | m_IsActive: 1
417 | --- !u!114 &763959695
418 | MonoBehaviour:
419 | m_ObjectHideFlags: 0
420 | m_CorrespondingSourceObject: {fileID: 0}
421 | m_PrefabInstance: {fileID: 0}
422 | m_PrefabAsset: {fileID: 0}
423 | m_GameObject: {fileID: 763959694}
424 | m_Enabled: 1
425 | m_EditorHideFlags: 0
426 | m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3}
427 | m_Name:
428 | m_EditorClassIdentifier:
429 | m_HorizontalAxis: Horizontal
430 | m_VerticalAxis: Vertical
431 | m_SubmitButton: Submit
432 | m_CancelButton: Cancel
433 | m_InputActionsPerSecond: 10
434 | m_RepeatDelay: 0.5
435 | m_ForceModuleActive: 0
436 | --- !u!114 &763959696
437 | MonoBehaviour:
438 | m_ObjectHideFlags: 0
439 | m_CorrespondingSourceObject: {fileID: 0}
440 | m_PrefabInstance: {fileID: 0}
441 | m_PrefabAsset: {fileID: 0}
442 | m_GameObject: {fileID: 763959694}
443 | m_Enabled: 1
444 | m_EditorHideFlags: 0
445 | m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
446 | m_Name:
447 | m_EditorClassIdentifier:
448 | m_FirstSelected: {fileID: 0}
449 | m_sendNavigationEvents: 1
450 | m_DragThreshold: 10
451 | --- !u!4 &763959697
452 | Transform:
453 | m_ObjectHideFlags: 0
454 | m_CorrespondingSourceObject: {fileID: 0}
455 | m_PrefabInstance: {fileID: 0}
456 | m_PrefabAsset: {fileID: 0}
457 | m_GameObject: {fileID: 763959694}
458 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
459 | m_LocalPosition: {x: 0, y: 0, z: 0}
460 | m_LocalScale: {x: 1, y: 1, z: 1}
461 | m_Children: []
462 | m_Father: {fileID: 0}
463 | m_RootOrder: 2
464 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
465 | --- !u!1 &1187637873
466 | GameObject:
467 | m_ObjectHideFlags: 0
468 | m_CorrespondingSourceObject: {fileID: 0}
469 | m_PrefabInstance: {fileID: 0}
470 | m_PrefabAsset: {fileID: 0}
471 | serializedVersion: 6
472 | m_Component:
473 | - component: {fileID: 1187637874}
474 | - component: {fileID: 1187637876}
475 | - component: {fileID: 1187637875}
476 | m_Layer: 5
477 | m_Name: Text (1)
478 | m_TagString: Untagged
479 | m_Icon: {fileID: 0}
480 | m_NavMeshLayer: 0
481 | m_StaticEditorFlags: 0
482 | m_IsActive: 1
483 | --- !u!224 &1187637874
484 | RectTransform:
485 | m_ObjectHideFlags: 0
486 | m_CorrespondingSourceObject: {fileID: 0}
487 | m_PrefabInstance: {fileID: 0}
488 | m_PrefabAsset: {fileID: 0}
489 | m_GameObject: {fileID: 1187637873}
490 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
491 | m_LocalPosition: {x: 0, y: 0, z: 0}
492 | m_LocalScale: {x: 1, y: 1, z: 1}
493 | m_Children: []
494 | m_Father: {fileID: 28094002}
495 | m_RootOrder: 1
496 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
497 | m_AnchorMin: {x: 0, y: 1}
498 | m_AnchorMax: {x: 1, y: 1}
499 | m_AnchoredPosition: {x: 10, y: -50}
500 | m_SizeDelta: {x: -20, y: 50}
501 | m_Pivot: {x: 0.5, y: 1}
502 | --- !u!114 &1187637875
503 | MonoBehaviour:
504 | m_ObjectHideFlags: 0
505 | m_CorrespondingSourceObject: {fileID: 0}
506 | m_PrefabInstance: {fileID: 0}
507 | m_PrefabAsset: {fileID: 0}
508 | m_GameObject: {fileID: 1187637873}
509 | m_Enabled: 1
510 | m_EditorHideFlags: 0
511 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
512 | m_Name:
513 | m_EditorClassIdentifier:
514 | m_Material: {fileID: 0}
515 | m_Color: {r: 1, g: 1, b: 1, a: 1}
516 | m_RaycastTarget: 1
517 | m_OnCullStateChanged:
518 | m_PersistentCalls:
519 | m_Calls: []
520 | m_FontData:
521 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
522 | m_FontSize: 32
523 | m_FontStyle: 0
524 | m_BestFit: 0
525 | m_MinSize: 3
526 | m_MaxSize: 72
527 | m_Alignment: 3
528 | m_AlignByGeometry: 0
529 | m_RichText: 1
530 | m_HorizontalOverflow: 0
531 | m_VerticalOverflow: 0
532 | m_LineSpacing: 1
533 | m_Text: ENTER - Generate
534 | --- !u!222 &1187637876
535 | CanvasRenderer:
536 | m_ObjectHideFlags: 0
537 | m_CorrespondingSourceObject: {fileID: 0}
538 | m_PrefabInstance: {fileID: 0}
539 | m_PrefabAsset: {fileID: 0}
540 | m_GameObject: {fileID: 1187637873}
541 | m_CullTransparentMesh: 0
542 | --- !u!1 &1200371490
543 | GameObject:
544 | m_ObjectHideFlags: 0
545 | m_CorrespondingSourceObject: {fileID: 0}
546 | m_PrefabInstance: {fileID: 0}
547 | m_PrefabAsset: {fileID: 0}
548 | serializedVersion: 6
549 | m_Component:
550 | - component: {fileID: 1200371491}
551 | - component: {fileID: 1200371493}
552 | - component: {fileID: 1200371492}
553 | m_Layer: 5
554 | m_Name: Text
555 | m_TagString: Untagged
556 | m_Icon: {fileID: 0}
557 | m_NavMeshLayer: 0
558 | m_StaticEditorFlags: 0
559 | m_IsActive: 1
560 | --- !u!224 &1200371491
561 | RectTransform:
562 | m_ObjectHideFlags: 0
563 | m_CorrespondingSourceObject: {fileID: 0}
564 | m_PrefabInstance: {fileID: 0}
565 | m_PrefabAsset: {fileID: 0}
566 | m_GameObject: {fileID: 1200371490}
567 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
568 | m_LocalPosition: {x: 0, y: 0, z: 0}
569 | m_LocalScale: {x: 1, y: 1, z: 1}
570 | m_Children: []
571 | m_Father: {fileID: 28094002}
572 | m_RootOrder: 0
573 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
574 | m_AnchorMin: {x: 0, y: 1}
575 | m_AnchorMax: {x: 1, y: 1}
576 | m_AnchoredPosition: {x: 10, y: 0}
577 | m_SizeDelta: {x: -20, y: 50}
578 | m_Pivot: {x: 0.5, y: 1}
579 | --- !u!114 &1200371492
580 | MonoBehaviour:
581 | m_ObjectHideFlags: 0
582 | m_CorrespondingSourceObject: {fileID: 0}
583 | m_PrefabInstance: {fileID: 0}
584 | m_PrefabAsset: {fileID: 0}
585 | m_GameObject: {fileID: 1200371490}
586 | m_Enabled: 1
587 | m_EditorHideFlags: 0
588 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
589 | m_Name:
590 | m_EditorClassIdentifier:
591 | m_Material: {fileID: 0}
592 | m_Color: {r: 1, g: 1, b: 1, a: 1}
593 | m_RaycastTarget: 1
594 | m_OnCullStateChanged:
595 | m_PersistentCalls:
596 | m_Calls: []
597 | m_FontData:
598 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
599 | m_FontSize: 32
600 | m_FontStyle: 0
601 | m_BestFit: 0
602 | m_MinSize: 3
603 | m_MaxSize: 72
604 | m_Alignment: 3
605 | m_AlignByGeometry: 0
606 | m_RichText: 1
607 | m_HorizontalOverflow: 0
608 | m_VerticalOverflow: 0
609 | m_LineSpacing: 1
610 | m_Text: SPACE - Clear
611 | --- !u!222 &1200371493
612 | CanvasRenderer:
613 | m_ObjectHideFlags: 0
614 | m_CorrespondingSourceObject: {fileID: 0}
615 | m_PrefabInstance: {fileID: 0}
616 | m_PrefabAsset: {fileID: 0}
617 | m_GameObject: {fileID: 1200371490}
618 | m_CullTransparentMesh: 0
619 | --- !u!1001 &1257150516
620 | PrefabInstance:
621 | m_ObjectHideFlags: 0
622 | serializedVersion: 2
623 | m_Modification:
624 | m_TransformParent: {fileID: 0}
625 | m_Modifications:
626 | - target: {fileID: 1292916145698231221, guid: c5d82fa185d78334199d0cfd6751110d,
627 | type: 3}
628 | propertyPath: m_Name
629 | value: DelaunatorPreview
630 | objectReference: {fileID: 0}
631 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
632 | type: 3}
633 | propertyPath: m_LocalPosition.x
634 | value: 0
635 | objectReference: {fileID: 0}
636 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
637 | type: 3}
638 | propertyPath: m_LocalPosition.y
639 | value: 0
640 | objectReference: {fileID: 0}
641 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
642 | type: 3}
643 | propertyPath: m_LocalPosition.z
644 | value: 0
645 | objectReference: {fileID: 0}
646 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
647 | type: 3}
648 | propertyPath: m_LocalRotation.x
649 | value: 0
650 | objectReference: {fileID: 0}
651 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
652 | type: 3}
653 | propertyPath: m_LocalRotation.y
654 | value: 0
655 | objectReference: {fileID: 0}
656 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
657 | type: 3}
658 | propertyPath: m_LocalRotation.z
659 | value: 0
660 | objectReference: {fileID: 0}
661 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
662 | type: 3}
663 | propertyPath: m_LocalRotation.w
664 | value: 1
665 | objectReference: {fileID: 0}
666 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
667 | type: 3}
668 | propertyPath: m_RootOrder
669 | value: 4
670 | objectReference: {fileID: 0}
671 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
672 | type: 3}
673 | propertyPath: m_LocalEulerAnglesHint.x
674 | value: 0
675 | objectReference: {fileID: 0}
676 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
677 | type: 3}
678 | propertyPath: m_LocalEulerAnglesHint.y
679 | value: 0
680 | objectReference: {fileID: 0}
681 | - target: {fileID: 1292916145698231222, guid: c5d82fa185d78334199d0cfd6751110d,
682 | type: 3}
683 | propertyPath: m_LocalEulerAnglesHint.z
684 | value: 0
685 | objectReference: {fileID: 0}
686 | m_RemovedComponents: []
687 | m_SourcePrefab: {fileID: 100100000, guid: c5d82fa185d78334199d0cfd6751110d, type: 3}
688 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/Samples~/Scenes/SampleScene.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a0fc0652a76f61c4aa3085dc584fd6ba
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.nol1fe.delaunator",
3 | "displayName": "Delaunator",
4 | "description": "Fast Delaunay triangulation of 2D points\nPort from Mapbox's Delaunator project (JavaScript).",
5 | "license": "MIT",
6 | "version": "1.0.11",
7 | "references": [
8 | "DelaunatorSharp"
9 | ],
10 | "keywords": [
11 | "delaunator",
12 | "delaunay",
13 | "triangulation",
14 | "voronoi"
15 | ],
16 | "category": "triangulation",
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/nol1fe/delaunator-sharp.git"
20 | },
21 | "author": "nol1fe (https://github.com/nol1fe)",
22 | "samples": [
23 | {
24 | "displayName": "Examples",
25 | "description": "Example use of delaunator library",
26 | "path": "Samples~"
27 | }
28 | ],
29 | "type": "sample"
30 | }
31 |
--------------------------------------------------------------------------------
/DelaunatorSharp.Unity/package.json.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d5e288bfeacf77343abd3d52203db829
3 | PackageManifestImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace DelaunatorSharp.WPF
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/DelaunatorSharp.WPF.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A8C191DB-3C69-4847-B57A-1524D13C1A30}
8 | WinExe
9 | DelaunatorSharp.WPF
10 | DelaunatorSharp.WPF
11 | v4.7.2
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | latest
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 |
38 |
39 |
40 | ..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll
41 |
42 |
43 | ..\packages\SlimDX.4.0.13.44\lib\NET40\SlimDX.dll
44 |
45 |
46 |
47 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
48 |
49 |
50 |
51 | ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll
52 |
53 |
54 |
55 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
56 |
57 |
58 | ..\packages\System.Reactive.4.1.5\lib\net46\System.Reactive.dll
59 |
60 |
61 | ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
62 |
63 |
64 | ..\packages\System.Text.Encodings.Web.8.0.0\lib\net462\System.Text.Encodings.Web.dll
65 |
66 |
67 | ..\packages\System.Text.Json.8.0.5\lib\net462\System.Text.Json.dll
68 |
69 |
70 | ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
71 |
72 |
73 | ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 4.0
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | MSBuild:Compile
93 | Designer
94 |
95 |
96 |
97 | MSBuild:Compile
98 | Designer
99 |
100 |
101 | App.xaml
102 | Code
103 |
104 |
105 | MainWindow.xaml
106 | Code
107 |
108 |
109 |
110 |
111 | Code
112 |
113 |
114 | True
115 | True
116 | Resources.resx
117 |
118 |
119 | True
120 | Settings.settings
121 | True
122 |
123 |
124 | ResXFileCodeGenerator
125 | Resources.Designer.cs
126 |
127 |
128 |
129 |
130 | SettingsSingleFileGenerator
131 | Settings.Designer.cs
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | {75843efd-3227-4d27-93c5-f32d8a234e4d}
140 | DelaunatorSharp
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using SlimDX;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Reactive.Linq;
9 | using System.Text.Json;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Shapes;
15 |
16 | namespace DelaunatorSharp.WPF
17 | {
18 | ///
19 | /// Interaction logic for MainWindow.xaml
20 | ///
21 | public partial class MainWindow : System.Windows.Window
22 | {
23 | private Delaunator delaunator;
24 |
25 | private Brush TriangleBrush = Brushes.Black;
26 | private Brush VoronoiBrush = Brushes.White;
27 | private Brush VoronoiCircleBrush = Brushes.Blue;
28 | private Brush TriangleCircleBrush = Brushes.Red;
29 |
30 | private const string TimeFormat = @"hh\:mm\:ss";
31 | #region Observables
32 | private IObservable MouseMoveStream => Observable
33 | .FromEventPattern(this, nameof(MouseMove))
34 | .Select(x => x.EventArgs.GetPosition(this))
35 | .Select(point => new Point(point.X, point.Y));
36 | private IObservable MouseDownStream => Observable
37 | .FromEventPattern(this, nameof(MouseLeftButtonDown))
38 | .Select(evt => evt.EventArgs.GetPosition(this))
39 | .Select(point => new Point(point.X, point.Y));
40 | private IObservable Interval(double time = 1) => Observable
41 | .Interval(TimeSpan.FromSeconds(time))
42 | .TimeInterval()
43 | .Scan(TimeSpan.Zero, (result, item) => result += item.Interval)
44 | .ObserveOn(Dispatcher);
45 | #endregion Observables
46 | private bool IsLengthOfPointsValid => Points.Count > 2;
47 | private readonly ObservableCollection Points = new ObservableCollection();
48 |
49 | private bool displayPositionText;
50 |
51 | public MainWindow()
52 | {
53 | InitializeComponent();
54 | Interval(1).Subscribe(x => ApplicationTime.Content = x.ToString(TimeFormat));
55 |
56 | InitializeMouseStreams();
57 |
58 | var mainDirectory = System.IO.Path.GetFullPath(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
59 | var filePath = System.IO.Path.Combine(mainDirectory, "samples.json");
60 | LoadPoints(filePath);
61 |
62 | DrawDiagram();
63 | }
64 |
65 | private void InitializeMouseStreams()
66 | {
67 | MouseMoveStream.Subscribe(point => MousePosition.Content = point.ToString());
68 |
69 | MouseDownStream.Subscribe(x =>
70 | {
71 | Points.Add(x);
72 | if (IsLengthOfPointsValid)
73 | {
74 | DrawDiagram();
75 | }
76 | });
77 | }
78 |
79 | private void GenerateSamples()
80 | {
81 | var minimumDistance = 40;
82 | var width = (float)(ActualWidth != 0 ? ActualWidth : Width);
83 | var height = (float)(ActualHeight != 0 ? ActualHeight : Height);
84 | var samples = UniformPoissonDiskSampler.SampleCircle(new Vector2(width / 2, height / 3), 220, minimumDistance).Select(x => new Point(x.X, x.Y));
85 |
86 | foreach (var sample in samples)
87 | {
88 | Points.Add(sample);
89 | DrawCircle(sample);
90 | }
91 | }
92 | private void DrawDiagram()
93 | {
94 | if (!IsLengthOfPointsValid) return;
95 | ClearDiagram();
96 | Redraw();
97 | }
98 |
99 | private void Redraw()
100 | {
101 | ClearDiagram();
102 | delaunator = new Delaunator(Points.ToArray());
103 | DrawCircles(Points);
104 | DrawDelaunay();
105 | DrawVoronoi();
106 | DrawHull();
107 | }
108 |
109 | private void DrawVoronoi()
110 | {
111 | RefreshDelaunator();
112 |
113 | delaunator.ForEachVoronoiCell((cell) =>
114 | {
115 | var polygon = new Polygon
116 | {
117 | Stroke = VoronoiBrush,
118 | StrokeThickness = .2,
119 | Points = new PointCollection(cell.Points.Select(point => new System.Windows.Point(point.X, point.Y))),
120 | };
121 |
122 | DrawCircles(cell.Points, VoronoiCircleBrush);
123 | PlayGround.Children.Add(polygon);
124 |
125 | if (displayPositionText)
126 | {
127 | var centroidX = cell.Points.Average(point => point.X);
128 | var centroidY = cell.Points.Average(point => point.Y);
129 |
130 | var textBlock = new TextBlock
131 | {
132 | Text = cell.Index.ToString(),
133 | Foreground = Brushes.Black,
134 | FontSize = 12,
135 | };
136 |
137 | Canvas.SetLeft(textBlock, centroidX);
138 | Canvas.SetTop(textBlock, centroidY);
139 |
140 | PlayGround.Children.Add(textBlock);
141 | }
142 | });
143 | }
144 | private void DrawDelaunay()
145 | {
146 | RefreshDelaunator();
147 | delaunator.ForEachTriangleEdge(edge =>
148 | {
149 | DrawLine(edge.P, edge.Q, TriangleBrush);
150 | });
151 | }
152 | private void DrawHull()
153 | {
154 | RefreshDelaunator();
155 | foreach (var edge in delaunator.GetHullEdges())
156 | {
157 | DrawLine(edge.P, edge.Q, Brushes.BlueViolet, .5);
158 | }
159 | }
160 | private void ClearDiagram()
161 | {
162 | PlayGround.Children.Clear();
163 | }
164 | private void RefreshDelaunator()
165 | {
166 | if (!IsLengthOfPointsValid || Points.Count() == delaunator?.Points.Count()) return;
167 | delaunator = new Delaunator(Points.ToArray());
168 | }
169 |
170 | #region Canvas
171 | private void DrawCircles(IEnumerable points, Brush brush = null)
172 | {
173 | foreach (var point in points)
174 | {
175 | DrawCircle(point, brush);
176 | }
177 | }
178 | private void DrawCircle(IPoint point, Brush brush = null)
179 | {
180 | var ellipse = new Ellipse
181 | {
182 | Width = 4,
183 | Height = 4,
184 | Fill = brush ?? TriangleCircleBrush,
185 | Stroke = brush ?? TriangleCircleBrush,
186 | };
187 |
188 | ellipse.ToolTip = $"X: {point.X:F2}, Y: {point.Y:F2}";
189 |
190 | PlayGround.Children.Add(ellipse);
191 | Canvas.SetLeft(ellipse, point.X - ellipse.Width / 2);
192 | Canvas.SetTop(ellipse, point.Y - ellipse.Height / 2);
193 |
194 | if (displayPositionText)
195 | {
196 | var textBlock = new TextBlock
197 | {
198 | Text = ellipse.ToolTip.ToString(),
199 | Foreground = Brushes.Black,
200 | FontSize = 12,
201 | };
202 |
203 | Canvas.SetLeft(textBlock, point.X);
204 | Canvas.SetTop(textBlock, point.Y);
205 |
206 | PlayGround.Children.Add(textBlock);
207 | }
208 | }
209 |
210 | private void DrawLine(IPoint startPoint, IPoint endPoint, Brush stroke, double thickness = .3)
211 | {
212 | var line = new Line
213 | {
214 | X1 = startPoint.X,
215 | Y1 = startPoint.Y,
216 |
217 | X2 = endPoint.X,
218 | Y2 = endPoint.Y,
219 | Stroke = stroke,
220 | StrokeThickness = thickness
221 | };
222 |
223 | PlayGround.Children.Add(line);
224 | }
225 | #endregion Canvas
226 |
227 | #region ClickHandlers
228 | private void OnClearClick(object sender, System.Windows.RoutedEventArgs e)
229 | {
230 | Points.Clear();
231 | ClearDiagram();
232 | }
233 | private void OnGenerateSamplesClick(object sender, System.Windows.RoutedEventArgs e) => GenerateSamples();
234 | private void OnDrawVoronoiClick(object sender, System.Windows.RoutedEventArgs e) => DrawVoronoi();
235 | private void OnDrawDelaunayClick(object sender, System.Windows.RoutedEventArgs e) => DrawDelaunay();
236 | private void OnDrawDiagramClick(object sender, System.Windows.RoutedEventArgs e) => DrawDiagram();
237 | private void OnDrawHullClick(object sender, System.Windows.RoutedEventArgs e) => DrawHull();
238 | private void OnNewClick(object sender, System.Windows.RoutedEventArgs e)
239 | {
240 | Points.Clear();
241 | ClearDiagram();
242 | GenerateSamples();
243 | Redraw();
244 | }
245 |
246 | private void OnSavePoints(object sender, RoutedEventArgs e)
247 | {
248 | var saveFileDialog = new SaveFileDialog
249 | {
250 | Filter = "JSON Files (*.json)|*.json|All Files (*.*)|*.*",
251 | DefaultExt = "json",
252 | Title = "Save Points As JSON"
253 | };
254 |
255 | if (saveFileDialog.ShowDialog() == true)
256 | {
257 | var filePath = saveFileDialog.FileName;
258 | try
259 | {
260 | var json = JsonSerializer.Serialize(Points, new JsonSerializerOptions { WriteIndented = true });
261 | File.WriteAllText(filePath, json);
262 | MessageBox.Show("Points successfully saved.", "Save Successful", MessageBoxButton.OK, MessageBoxImage.Information);
263 | }
264 | catch (Exception ex)
265 | {
266 | MessageBox.Show($"An error occurred while saving the file: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
267 | }
268 | }
269 | }
270 |
271 | private void OnLoadPoints(object sender, RoutedEventArgs e)
272 | {
273 | var openFileDialog = new OpenFileDialog
274 | {
275 | Filter = "JSON Files (*.json)|*.json|All Files (*.*)|*.*",
276 | DefaultExt = "json",
277 | Title = "Open Points JSON File"
278 | };
279 |
280 | if (openFileDialog.ShowDialog() == true)
281 | {
282 | var filePath = openFileDialog.FileName;
283 | try
284 | {
285 | LoadPoints(filePath);
286 | }
287 | catch (Exception ex)
288 | {
289 | MessageBox.Show($"An error occurred while loading the file: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
290 | }
291 | }
292 | }
293 |
294 | private void LoadPoints(string filePath)
295 | {
296 | var json = File.ReadAllText(filePath);
297 | var loadedPoints = JsonSerializer.Deserialize>(json);
298 |
299 | if (loadedPoints != null)
300 | {
301 | Points.Clear();
302 | foreach (var point in loadedPoints)
303 | {
304 | Points.Add(point);
305 | }
306 |
307 | Redraw();
308 | }
309 | }
310 |
311 | #endregion ClickHandlers
312 | }
313 | }
314 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("Delaunator.WPF.Example")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Delaunator.WPF.Example")]
15 | [assembly: AssemblyCopyright("Copyright © 2019")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace DelaunatorSharp.WPF.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DelaunatorSharp.WPF.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace DelaunatorSharp.WPF.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/UniformPoissonDiskSampler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using SlimDX;
4 |
5 | namespace DelaunatorSharp.WPF
6 | {
7 | // http://theinstructionlimit.com/fast-uniform-poisson-disk-sampling-in-c <- copied from Renaud Bédard
8 |
9 | // Adapated from java source by Herman Tulleken
10 | // http://www.luma.co.za/labs/2008/02/27/poisson-disk-sampling/
11 |
12 | // The algorithm is from the "Fast Poisson Disk Sampling in Arbitrary Dimensions" paper by Robert Bridson
13 | // http://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
14 |
15 | public static class UniformPoissonDiskSampler
16 | {
17 | public const int DefaultPointsPerIteration = 30;
18 |
19 | static readonly float SquareRootTwo = (float)Math.Sqrt(2);
20 |
21 | struct Settings
22 | {
23 | public Vector2 TopLeft, LowerRight, Center;
24 | public Vector2 Dimensions;
25 | public float? RejectionSqDistance;
26 | public float MinimumDistance;
27 | public float CellSize;
28 | public int GridWidth, GridHeight;
29 | }
30 |
31 | struct State
32 | {
33 | public Vector2?[,] Grid;
34 | public List ActivePoints, Points;
35 | }
36 |
37 | public static List SampleCircle(Vector2 center, float radius, float minimumDistance)
38 | {
39 | return SampleCircle(center, radius, minimumDistance, DefaultPointsPerIteration);
40 | }
41 | public static List SampleCircle(Vector2 center, float radius, float minimumDistance, int pointsPerIteration)
42 | {
43 | return Sample(center - new Vector2(radius), center + new Vector2(radius), radius, minimumDistance, pointsPerIteration);
44 | }
45 |
46 | public static List SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance)
47 | {
48 | return SampleRectangle(topLeft, lowerRight, minimumDistance, DefaultPointsPerIteration);
49 | }
50 | public static List SampleRectangle(Vector2 topLeft, Vector2 lowerRight, float minimumDistance, int pointsPerIteration)
51 | {
52 | return Sample(topLeft, lowerRight, null, minimumDistance, pointsPerIteration);
53 | }
54 |
55 | static List Sample(Vector2 topLeft, Vector2 lowerRight, float? rejectionDistance, float minimumDistance, int pointsPerIteration)
56 | {
57 | var settings = new Settings
58 | {
59 | TopLeft = topLeft,
60 | LowerRight = lowerRight,
61 | Dimensions = lowerRight - topLeft,
62 | Center = (topLeft + lowerRight) / 2,
63 | CellSize = minimumDistance / SquareRootTwo,
64 | MinimumDistance = minimumDistance,
65 | RejectionSqDistance = rejectionDistance == null ? null : rejectionDistance * rejectionDistance
66 | };
67 | settings.GridWidth = (int)(settings.Dimensions.X / settings.CellSize) + 1;
68 | settings.GridHeight = (int)(settings.Dimensions.Y / settings.CellSize) + 1;
69 |
70 | var state = new State
71 | {
72 | Grid = new Vector2?[settings.GridWidth, settings.GridHeight],
73 | ActivePoints = new List(),
74 | Points = new List()
75 | };
76 |
77 | AddFirstPoint(ref settings, ref state);
78 |
79 | while (state.ActivePoints.Count != 0)
80 | {
81 | var listIndex = RandomHelper.Random.Next(state.ActivePoints.Count);
82 |
83 | var point = state.ActivePoints[listIndex];
84 | var found = false;
85 |
86 | for (var k = 0; k < pointsPerIteration; k++)
87 | found |= AddNextPoint(point, ref settings, ref state);
88 |
89 | if (!found)
90 | state.ActivePoints.RemoveAt(listIndex);
91 | }
92 |
93 | return state.Points;
94 | }
95 |
96 | static void AddFirstPoint(ref Settings settings, ref State state)
97 | {
98 | var added = false;
99 | while (!added)
100 | {
101 | var d = RandomHelper.Random.NextDouble();
102 | var xr = settings.TopLeft.X + settings.Dimensions.X * d;
103 |
104 | d = RandomHelper.Random.NextDouble();
105 | var yr = settings.TopLeft.Y + settings.Dimensions.Y * d;
106 |
107 | var p = new Vector2((float)xr, (float)yr);
108 | if (settings.RejectionSqDistance != null && Vector2.DistanceSquared(settings.Center, p) > settings.RejectionSqDistance)
109 | continue;
110 | added = true;
111 |
112 | var index = Denormalize(p, settings.TopLeft, settings.CellSize);
113 |
114 | state.Grid[(int)index.X, (int)index.Y] = p;
115 |
116 | state.ActivePoints.Add(p);
117 | state.Points.Add(p);
118 | }
119 | }
120 |
121 | static bool AddNextPoint(Vector2 point, ref Settings settings, ref State state)
122 | {
123 | var found = false;
124 | var q = GenerateRandomAround(point, settings.MinimumDistance);
125 |
126 | if (q.X >= settings.TopLeft.X && q.X < settings.LowerRight.X &&
127 | q.Y > settings.TopLeft.Y && q.Y < settings.LowerRight.Y &&
128 | (settings.RejectionSqDistance == null || Vector2.DistanceSquared(settings.Center, q) <= settings.RejectionSqDistance))
129 | {
130 | var qIndex = Denormalize(q, settings.TopLeft, settings.CellSize);
131 | var tooClose = false;
132 |
133 | for (var i = (int)Math.Max(0, qIndex.X - 2); i < Math.Min(settings.GridWidth, qIndex.X + 3) && !tooClose; i++)
134 | for (var j = (int)Math.Max(0, qIndex.Y - 2); j < Math.Min(settings.GridHeight, qIndex.Y + 3) && !tooClose; j++)
135 | if (state.Grid[i, j].HasValue && Vector2.Distance(state.Grid[i, j].Value, q) < settings.MinimumDistance)
136 | tooClose = true;
137 |
138 | if (!tooClose)
139 | {
140 | found = true;
141 | state.ActivePoints.Add(q);
142 | state.Points.Add(q);
143 | state.Grid[(int)qIndex.X, (int)qIndex.Y] = q;
144 | }
145 | }
146 | return found;
147 | }
148 |
149 | static Vector2 GenerateRandomAround(Vector2 center, float minimumDistance)
150 | {
151 | var d = RandomHelper.Random.NextDouble();
152 | var radius = minimumDistance + minimumDistance * d;
153 |
154 | d = RandomHelper.Random.NextDouble();
155 | var angle = MathHelper.TwoPi * d;
156 |
157 | var newX = radius * Math.Sin(angle);
158 | var newY = radius * Math.Cos(angle);
159 |
160 | return new Vector2((float)(center.X + newX), (float)(center.Y + newY));
161 | }
162 |
163 | static Vector2 Denormalize(Vector2 point, Vector2 origin, double cellSize)
164 | {
165 | return new Vector2((int)((point.X - origin.X) / cellSize), (int)((point.Y - origin.Y) / cellSize));
166 | }
167 | }
168 |
169 | public static class RandomHelper
170 | {
171 | public static readonly Random Random = new Random();
172 | }
173 |
174 | public static class MathHelper
175 | {
176 | public const float Pi = (float)Math.PI;
177 | public const float HalfPi = (float)(Math.PI / 2);
178 | public const float TwoPi = (float)(Math.PI * 2);
179 | }
180 | }
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/DelaunatorSharp.WPF/samples.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "X": 67,
4 | "Y": 67
5 | },
6 | {
7 | "X": 666,
8 | "Y": 63
9 | },
10 | {
11 | "X": 692,
12 | "Y": 417
13 | },
14 | {
15 | "X": 74,
16 | "Y": 431
17 | },
18 | {
19 | "X": 366,
20 | "Y": 248
21 | },
22 | {
23 | "X": 576,
24 | "Y": 242
25 | },
26 | {
27 | "X": 168,
28 | "Y": 250
29 | },
30 | {
31 | "X": 367,
32 | "Y": 128
33 | }
34 | ]
--------------------------------------------------------------------------------
/DelaunatorSharp/Delaunator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace DelaunatorSharp
6 | {
7 | public class Delaunator
8 | {
9 | private readonly double EPSILON = Math.Pow(2, -52);
10 | private readonly int[] EDGE_STACK = new int[512];
11 |
12 | ///
13 | /// One value per half-edge, containing the point index of where a given half edge starts.
14 | ///
15 | public int[] Triangles { get; private set; }
16 |
17 | ///
18 | /// One value per half-edge, containing the opposite half-edge in the adjacent triangle, or -1 if there is no adjacent triangle
19 | ///
20 | public int[] Halfedges { get; private set; }
21 |
22 | ///
23 | /// The initial points Delaunator was constructed with.
24 | ///
25 | public IPoint[] Points { get; private set; }
26 |
27 | ///
28 | /// A list of point indices that traverses the hull of the points.
29 | ///
30 | public int[] Hull { get; private set; }
31 |
32 | private readonly int hashSize;
33 | private readonly int[] hullPrev;
34 | private readonly int[] hullNext;
35 | private readonly int[] hullTri;
36 | private readonly int[] hullHash;
37 |
38 | private double cx;
39 | private double cy;
40 |
41 | private int trianglesLen;
42 | private readonly double[] coords;
43 | private readonly int hullStart;
44 | private readonly int hullSize;
45 |
46 | public Delaunator(IPoint[] points)
47 | {
48 | if (points.Length < 3)
49 | {
50 | throw new ArgumentOutOfRangeException("Need at least 3 points");
51 | }
52 |
53 | Points = points;
54 | coords = new double[Points.Length * 2];
55 |
56 | for (var i = 0; i < Points.Length; i++)
57 | {
58 | var p = Points[i];
59 | coords[2 * i] = p.X;
60 | coords[2 * i + 1] = p.Y;
61 | }
62 |
63 | var n = points.Length;
64 | var maxTriangles = 2 * n - 5;
65 |
66 | Triangles = new int[maxTriangles * 3];
67 |
68 | Halfedges = new int[maxTriangles * 3];
69 | hashSize = (int)Math.Ceiling(Math.Sqrt(n));
70 |
71 | hullPrev = new int[n];
72 | hullNext = new int[n];
73 | hullTri = new int[n];
74 | hullHash = new int[hashSize];
75 |
76 | var ids = new int[n];
77 |
78 | var minX = double.PositiveInfinity;
79 | var minY = double.PositiveInfinity;
80 | var maxX = double.NegativeInfinity;
81 | var maxY = double.NegativeInfinity;
82 |
83 | for (var i = 0; i < n; i++)
84 | {
85 | var x = coords[2 * i];
86 | var y = coords[2 * i + 1];
87 | if (x < minX) minX = x;
88 | if (y < minY) minY = y;
89 | if (x > maxX) maxX = x;
90 | if (y > maxY) maxY = y;
91 | ids[i] = i;
92 | }
93 |
94 | var cx = (minX + maxX) / 2;
95 | var cy = (minY + maxY) / 2;
96 |
97 | var minDist = double.PositiveInfinity;
98 | var i0 = 0;
99 | var i1 = 0;
100 | var i2 = 0;
101 |
102 | // pick a seed point close to the center
103 | for (int i = 0; i < n; i++)
104 | {
105 | var d = Dist(cx, cy, coords[2 * i], coords[2 * i + 1]);
106 | if (d < minDist)
107 | {
108 | i0 = i;
109 | minDist = d;
110 | }
111 | }
112 | var i0x = coords[2 * i0];
113 | var i0y = coords[2 * i0 + 1];
114 |
115 | minDist = double.PositiveInfinity;
116 |
117 | // find the point closest to the seed
118 | for (int i = 0; i < n; i++)
119 | {
120 | if (i == i0) continue;
121 | var d = Dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]);
122 | if (d < minDist && d > 0)
123 | {
124 | i1 = i;
125 | minDist = d;
126 | }
127 | }
128 |
129 | var i1x = coords[2 * i1];
130 | var i1y = coords[2 * i1 + 1];
131 |
132 | var minRadius = double.PositiveInfinity;
133 |
134 | // find the third point which forms the smallest circumcircle with the first two
135 | for (int i = 0; i < n; i++)
136 | {
137 | if (i == i0 || i == i1) continue;
138 | var r = Circumradius(i0x, i0y, i1x, i1y, coords[2 * i], coords[2 * i + 1]);
139 | if (r < minRadius)
140 | {
141 | i2 = i;
142 | minRadius = r;
143 | }
144 | }
145 | var i2x = coords[2 * i2];
146 | var i2y = coords[2 * i2 + 1];
147 |
148 | if (minRadius == double.PositiveInfinity)
149 | {
150 | throw new Exception("No Delaunay triangulation exists for this input.");
151 | }
152 |
153 | if (Orient(i0x, i0y, i1x, i1y, i2x, i2y))
154 | {
155 | var i = i1;
156 | var x = i1x;
157 | var y = i1y;
158 | i1 = i2;
159 | i1x = i2x;
160 | i1y = i2y;
161 | i2 = i;
162 | i2x = x;
163 | i2y = y;
164 | }
165 |
166 | var center = Circumcenter(i0x, i0y, i1x, i1y, i2x, i2y);
167 | this.cx = center.X;
168 | this.cy = center.Y;
169 |
170 | var dists = new double[n];
171 | for (var i = 0; i < n; i++)
172 | {
173 | dists[i] = Dist(coords[2 * i], coords[2 * i + 1], center.X, center.Y);
174 | }
175 |
176 | // sort the points by distance from the seed triangle circumcenter
177 | Quicksort(ids, dists, 0, n - 1);
178 |
179 | // set up the seed triangle as the starting hull
180 | hullStart = i0;
181 | hullSize = 3;
182 |
183 | hullNext[i0] = hullPrev[i2] = i1;
184 | hullNext[i1] = hullPrev[i0] = i2;
185 | hullNext[i2] = hullPrev[i1] = i0;
186 |
187 | hullTri[i0] = 0;
188 | hullTri[i1] = 1;
189 | hullTri[i2] = 2;
190 |
191 | hullHash[HashKey(i0x, i0y)] = i0;
192 | hullHash[HashKey(i1x, i1y)] = i1;
193 | hullHash[HashKey(i2x, i2y)] = i2;
194 |
195 | trianglesLen = 0;
196 | AddTriangle(i0, i1, i2, -1, -1, -1);
197 |
198 | double xp = 0;
199 | double yp = 0;
200 |
201 | for (var k = 0; k < ids.Length; k++)
202 | {
203 | var i = ids[k];
204 | var x = coords[2 * i];
205 | var y = coords[2 * i + 1];
206 |
207 | // skip near-duplicate points
208 | if (k > 0 && Math.Abs(x - xp) <= EPSILON && Math.Abs(y - yp) <= EPSILON) continue;
209 | xp = x;
210 | yp = y;
211 |
212 | // skip seed triangle points
213 | if (i == i0 || i == i1 || i == i2) continue;
214 |
215 | // find a visible edge on the convex hull using edge hash
216 | var start = 0;
217 | for (var j = 0; j < hashSize; j++)
218 | {
219 | var key = HashKey(x, y);
220 | start = hullHash[(key + j) % hashSize];
221 | if (start != -1 && start != hullNext[start]) break;
222 | }
223 |
224 |
225 | start = hullPrev[start];
226 | var e = start;
227 | var q = hullNext[e];
228 |
229 | while (!Orient(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1]))
230 | {
231 | e = q;
232 | if (e == start)
233 | {
234 | e = int.MaxValue;
235 | break;
236 | }
237 |
238 | q = hullNext[e];
239 | }
240 |
241 | if (e == int.MaxValue) continue; // likely a near-duplicate point; skip it
242 |
243 | // add the first triangle from the point
244 | var t = AddTriangle(e, i, hullNext[e], -1, -1, hullTri[e]);
245 |
246 | // recursively flip triangles from the point until they satisfy the Delaunay condition
247 | hullTri[i] = Legalize(t + 2);
248 | hullTri[e] = t; // keep track of boundary triangles on the hull
249 | hullSize++;
250 |
251 | // walk forward through the hull, adding more triangles and flipping recursively
252 | var next = hullNext[e];
253 | q = hullNext[next];
254 |
255 | while (Orient(x, y, coords[2 * next], coords[2 * next + 1], coords[2 * q], coords[2 * q + 1]))
256 | {
257 | t = AddTriangle(next, i, q, hullTri[i], -1, hullTri[next]);
258 | hullTri[i] = Legalize(t + 2);
259 | hullNext[next] = next; // mark as removed
260 | hullSize--;
261 | next = q;
262 |
263 | q = hullNext[next];
264 | }
265 |
266 | // walk backward from the other side, adding more triangles and flipping
267 | if (e == start)
268 | {
269 | q = hullPrev[e];
270 |
271 | while (Orient(x, y, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1]))
272 | {
273 | t = AddTriangle(q, i, e, -1, hullTri[e], hullTri[q]);
274 | Legalize(t + 2);
275 | hullTri[q] = t;
276 | hullNext[e] = e; // mark as removed
277 | hullSize--;
278 | e = q;
279 |
280 | q = hullPrev[e];
281 | }
282 | }
283 |
284 | // update the hull indices
285 | hullStart = hullPrev[i] = e;
286 | hullNext[e] = hullPrev[next] = i;
287 | hullNext[i] = next;
288 |
289 | // save the two new edges in the hash table
290 | hullHash[HashKey(x, y)] = i;
291 | hullHash[HashKey(coords[2 * e], coords[2 * e + 1])] = e;
292 | }
293 |
294 | Hull = new int[hullSize];
295 | var s = hullStart;
296 | for (var i = 0; i < hullSize; i++)
297 | {
298 | Hull[i] = s;
299 | s = hullNext[s];
300 | }
301 |
302 | hullPrev = hullNext = hullTri = null; // get rid of temporary arrays
303 |
304 | //// trim typed triangle mesh arrays
305 | Triangles = Triangles.Take(trianglesLen).ToArray();
306 | Halfedges = Halfedges.Take(trianglesLen).ToArray();
307 | }
308 |
309 | #region CreationLogic
310 | private int Legalize(int a)
311 | {
312 | var i = 0;
313 | int ar;
314 |
315 | // recursion eliminated with a fixed-size stack
316 | while (true)
317 | {
318 | var b = Halfedges[a];
319 |
320 | /* if the pair of triangles doesn't satisfy the Delaunay condition
321 | * (p1 is inside the circumcircle of [p0, pl, pr]), flip them,
322 | * then do the same check/flip recursively for the new pair of triangles
323 | *
324 | * pl pl
325 | * /||\ / \
326 | * al/ || \bl al/ \a
327 | * / || \ / \
328 | * / a||b \ flip /___ar___\
329 | * p0\ || /p1 => p0\---bl---/p1
330 | * \ || / \ /
331 | * ar\ || /br b\ /br
332 | * \||/ \ /
333 | * pr pr
334 | */
335 | int a0 = a - a % 3;
336 | ar = a0 + (a + 2) % 3;
337 |
338 | if (b == -1)
339 | { // convex hull edge
340 | if (i == 0) break;
341 | a = EDGE_STACK[--i];
342 | continue;
343 | }
344 |
345 | var b0 = b - b % 3;
346 | var al = a0 + (a + 1) % 3;
347 | var bl = b0 + (b + 2) % 3;
348 |
349 | var p0 = Triangles[ar];
350 | var pr = Triangles[a];
351 | var pl = Triangles[al];
352 | var p1 = Triangles[bl];
353 |
354 | var illegal = InCircle(
355 | coords[2 * p0], coords[2 * p0 + 1],
356 | coords[2 * pr], coords[2 * pr + 1],
357 | coords[2 * pl], coords[2 * pl + 1],
358 | coords[2 * p1], coords[2 * p1 + 1]);
359 |
360 | if (illegal)
361 | {
362 | Triangles[a] = p1;
363 | Triangles[b] = p0;
364 |
365 | var hbl = Halfedges[bl];
366 |
367 | // edge swapped on the other side of the hull (rare); fix the halfedge reference
368 | if (hbl == -1)
369 | {
370 | var e = hullStart;
371 | do
372 | {
373 | if (hullTri[e] == bl)
374 | {
375 | hullTri[e] = a;
376 | break;
377 | }
378 | e = hullPrev[e];
379 | } while (e != hullStart);
380 | }
381 | Link(a, hbl);
382 | Link(b, Halfedges[ar]);
383 | Link(ar, bl);
384 |
385 | var br = b0 + (b + 1) % 3;
386 |
387 | // don't worry about hitting the cap: it can only happen on extremely degenerate input
388 | if (i < EDGE_STACK.Length)
389 | {
390 | EDGE_STACK[i++] = br;
391 | }
392 | }
393 | else
394 | {
395 | if (i == 0) break;
396 | a = EDGE_STACK[--i];
397 | }
398 | }
399 |
400 | return ar;
401 | }
402 | private static bool InCircle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py)
403 | {
404 | var dx = ax - px;
405 | var dy = ay - py;
406 | var ex = bx - px;
407 | var ey = by - py;
408 | var fx = cx - px;
409 | var fy = cy - py;
410 |
411 | var ap = dx * dx + dy * dy;
412 | var bp = ex * ex + ey * ey;
413 | var cp = fx * fx + fy * fy;
414 |
415 | return dx * (ey * cp - bp * fy) -
416 | dy * (ex * cp - bp * fx) +
417 | ap * (ex * fy - ey * fx) < 0;
418 | }
419 | private int AddTriangle(int i0, int i1, int i2, int a, int b, int c)
420 | {
421 | var t = trianglesLen;
422 |
423 | Triangles[t] = i0;
424 | Triangles[t + 1] = i1;
425 | Triangles[t + 2] = i2;
426 |
427 | Link(t, a);
428 | Link(t + 1, b);
429 | Link(t + 2, c);
430 |
431 | trianglesLen += 3;
432 | return t;
433 | }
434 | private void Link(int a, int b)
435 | {
436 | Halfedges[a] = b;
437 | if (b != -1) Halfedges[b] = a;
438 | }
439 | private int HashKey(double x, double y) => (int)(Math.Floor(PseudoAngle(x - cx, y - cy) * hashSize) % hashSize);
440 | private static double PseudoAngle(double dx, double dy)
441 | {
442 | var p = dx / (Math.Abs(dx) + Math.Abs(dy));
443 | return (dy > 0 ? 3 - p : 1 + p) / 4; // [0..1]
444 | }
445 | private static void Quicksort(int[] ids, double[] dists, int left, int right)
446 | {
447 | if (right - left <= 20)
448 | {
449 | for (var i = left + 1; i <= right; i++)
450 | {
451 | var temp = ids[i];
452 | var tempDist = dists[temp];
453 | var j = i - 1;
454 | while (j >= left && dists[ids[j]] > tempDist) ids[j + 1] = ids[j--];
455 | ids[j + 1] = temp;
456 | }
457 | }
458 | else
459 | {
460 | var median = (left + right) >> 1;
461 | var i = left + 1;
462 | var j = right;
463 | Swap(ids, median, i);
464 | if (dists[ids[left]] > dists[ids[right]]) Swap(ids, left, right);
465 | if (dists[ids[i]] > dists[ids[right]]) Swap(ids, i, right);
466 | if (dists[ids[left]] > dists[ids[i]]) Swap(ids, left, i);
467 |
468 | var temp = ids[i];
469 | var tempDist = dists[temp];
470 | while (true)
471 | {
472 | do i++; while (dists[ids[i]] < tempDist);
473 | do j--; while (dists[ids[j]] > tempDist);
474 | if (j < i) break;
475 | Swap(ids, i, j);
476 | }
477 | ids[left + 1] = ids[j];
478 | ids[j] = temp;
479 |
480 | if (right - i + 1 >= j - left)
481 | {
482 | Quicksort(ids, dists, i, right);
483 | Quicksort(ids, dists, left, j - 1);
484 | }
485 | else
486 | {
487 | Quicksort(ids, dists, left, j - 1);
488 | Quicksort(ids, dists, i, right);
489 | }
490 | }
491 | }
492 | private static void Swap(int[] arr, int i, int j)
493 | {
494 | var tmp = arr[i];
495 | arr[i] = arr[j];
496 | arr[j] = tmp;
497 | }
498 | private static bool Orient(double px, double py, double qx, double qy, double rx, double ry) => (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0;
499 | private static double Circumradius(double ax, double ay, double bx, double by, double cx, double cy)
500 | {
501 | var dx = bx - ax;
502 | var dy = by - ay;
503 | var ex = cx - ax;
504 | var ey = cy - ay;
505 | var bl = dx * dx + dy * dy;
506 | var cl = ex * ex + ey * ey;
507 | var d = 0.5 / (dx * ey - dy * ex);
508 | var x = (ey * bl - dy * cl) * d;
509 | var y = (dx * cl - ex * bl) * d;
510 | return x * x + y * y;
511 | }
512 | private static Point Circumcenter(double ax, double ay, double bx, double by, double cx, double cy)
513 | {
514 | var dx = bx - ax;
515 | var dy = by - ay;
516 | var ex = cx - ax;
517 | var ey = cy - ay;
518 | var bl = dx * dx + dy * dy;
519 | var cl = ex * ex + ey * ey;
520 | var d = 0.5 / (dx * ey - dy * ex);
521 | var x = ax + (ey * bl - dy * cl) * d;
522 | var y = ay + (dx * cl - ex * bl) * d;
523 |
524 | return new Point(x, y);
525 | }
526 | private static double Dist(double ax, double ay, double bx, double by)
527 | {
528 | var dx = ax - bx;
529 | var dy = ay - by;
530 | return dx * dx + dy * dy;
531 | }
532 | #endregion CreationLogic
533 |
534 | #region GetMethods
535 | public IEnumerable GetTriangles()
536 | {
537 | for (var t = 0; t < Triangles.Length / 3; t++)
538 | {
539 | yield return new Triangle(t, GetTrianglePoints(t));
540 | }
541 | }
542 | public IEnumerable GetEdges()
543 | {
544 | for (var e = 0; e < Triangles.Length; e++)
545 | {
546 | if (e > Halfedges[e])
547 | {
548 | var p = Points[Triangles[e]];
549 | var q = Points[Triangles[NextHalfedge(e)]];
550 | yield return new Edge(e, p, q);
551 | }
552 | }
553 | }
554 | public IEnumerable GetVoronoiEdges(Func triangleVerticeSelector = null)
555 | {
556 | if (triangleVerticeSelector == null) triangleVerticeSelector = x => GetCentroid(x);
557 | for (var e = 0; e < Triangles.Length; e++)
558 | {
559 | if (e < Halfedges[e])
560 | {
561 | var p = triangleVerticeSelector(TriangleOfEdge(e));
562 | var q = triangleVerticeSelector(TriangleOfEdge(Halfedges[e]));
563 | yield return new Edge(e, p, q);
564 | }
565 | }
566 | }
567 |
568 | public IEnumerable GetVoronoiEdgesBasedOnCircumCenter() => GetVoronoiEdges(GetTriangleCircumcenter);
569 | public IEnumerable GetVoronoiEdgesBasedOnCentroids() => GetVoronoiEdges(GetCentroid);
570 |
571 | public IEnumerable GetVoronoiCells(Func triangleVerticeSelector = null)
572 | {
573 | if (triangleVerticeSelector == null) triangleVerticeSelector = x => GetCentroid(x);
574 |
575 | var seen = new HashSet();
576 | var vertices = new List(10); // Keep it outside the loop, reuse capacity, less resizes.
577 |
578 | for (var e = 0; e < Triangles.Length; e++)
579 | {
580 | var pointIndex = Triangles[NextHalfedge(e)];
581 | // True if element was added, If resize the set? O(n) : O(1)
582 | if (seen.Add(pointIndex))
583 | {
584 | foreach (var edge in EdgesAroundPoint(e))
585 | {
586 | // triangleVerticeSelector cant be null, no need to check before invoke (?.).
587 | vertices.Add(triangleVerticeSelector.Invoke(TriangleOfEdge(edge)));
588 | }
589 | yield return new VoronoiCell(pointIndex, vertices.ToArray());
590 | vertices.Clear(); // Clear elements, keep capacity
591 | }
592 | }
593 | }
594 |
595 | public IEnumerable GetVoronoiCellsBasedOnCircumcenters() => GetVoronoiCells(GetTriangleCircumcenter);
596 | public IEnumerable GetVoronoiCellsBasedOnCentroids() => GetVoronoiCells(GetCentroid);
597 |
598 | public IEnumerable GetHullEdges() => CreateHull(GetHullPoints());
599 |
600 | public IPoint[] GetHullPoints() => Array.ConvertAll(Hull, (x) => Points[x]);
601 |
602 | public IPoint[] GetTrianglePoints(int t)
603 | {
604 | var points = new List();
605 | foreach (var p in PointsOfTriangle(t))
606 | {
607 | points.Add(Points[p]);
608 | }
609 | return points.ToArray();
610 | }
611 |
612 | public IPoint[] GetRellaxedPoints()
613 | {
614 | var points = new List();
615 | foreach (var cell in GetVoronoiCellsBasedOnCircumcenters())
616 | {
617 | points.Add(GetCentroid(cell.Points));
618 | }
619 | return points.ToArray();
620 | }
621 |
622 | public IEnumerable GetEdgesOfTriangle(int t) => CreateHull(EdgesOfTriangle(t).Select(e => Points[Triangles[e]]));
623 | public static IEnumerable CreateHull(IEnumerable points) => points.Zip(points.Skip(1).Append(points.FirstOrDefault()), (a, b) => new Edge(0, a, b)).OfType();
624 | public IPoint GetTriangleCircumcenter(int t)
625 | {
626 | var vertices = GetTrianglePoints(t);
627 | return GetCircumcenter(vertices[0], vertices[1], vertices[2]);
628 | }
629 | public IPoint GetCentroid(int t)
630 | {
631 | var vertices = GetTrianglePoints(t);
632 | return GetCentroid(vertices);
633 | }
634 | public static IPoint GetCircumcenter(IPoint a, IPoint b, IPoint c) => Circumcenter(a.X, a.Y, b.X, b.Y, c.X, c.Y);
635 |
636 | public static IPoint GetCentroid(IPoint[] points)
637 | {
638 | double accumulatedArea = 0.0f;
639 | double centerX = 0.0f;
640 | double centerY = 0.0f;
641 |
642 | for (int i = 0, j = points.Length - 1; i < points.Length; j = i++)
643 | {
644 | var temp = points[i].X * points[j].Y - points[j].X * points[i].Y;
645 | accumulatedArea += temp;
646 | centerX += (points[i].X + points[j].X) * temp;
647 | centerY += (points[i].Y + points[j].Y) * temp;
648 | }
649 |
650 | if (Math.Abs(accumulatedArea) < 1E-7f)
651 | return new Point();
652 |
653 | accumulatedArea *= 3f;
654 | return new Point(centerX / accumulatedArea, centerY / accumulatedArea);
655 | }
656 |
657 | #endregion GetMethods
658 |
659 | #region ForEachMethods
660 | public void ForEachTriangle(Action callback)
661 | {
662 | foreach (var triangle in GetTriangles())
663 | {
664 | callback?.Invoke(triangle);
665 | }
666 | }
667 | public void ForEachTriangleEdge(Action callback)
668 | {
669 | foreach (var edge in GetEdges())
670 | {
671 | callback?.Invoke(edge);
672 | }
673 | }
674 | public void ForEachVoronoiEdge(Action callback)
675 | {
676 | foreach (var edge in GetVoronoiEdges())
677 | {
678 | callback?.Invoke(edge);
679 | }
680 | }
681 | public void ForEachVoronoiCellBasedOnCentroids(Action callback)
682 | {
683 | foreach (var cell in GetVoronoiCellsBasedOnCentroids())
684 | {
685 | callback?.Invoke(cell);
686 | }
687 | }
688 |
689 | public void ForEachVoronoiCellBasedOnCircumcenters(Action callback)
690 | {
691 | foreach (var cell in GetVoronoiCellsBasedOnCircumcenters())
692 | {
693 | callback?.Invoke(cell);
694 | }
695 | }
696 |
697 | public void ForEachVoronoiCell(Action callback, Func triangleVertexSelector = null)
698 | {
699 | foreach (var cell in GetVoronoiCells(triangleVertexSelector))
700 | {
701 | callback?.Invoke(cell);
702 | }
703 | }
704 |
705 | #endregion ForEachMethods
706 |
707 | #region Methods based on index
708 | ///
709 | /// Returns the half-edges that share a start point with the given half edge, in order.
710 | ///
711 | public IEnumerable EdgesAroundPoint(int start)
712 | {
713 | var incoming = start;
714 | do
715 | {
716 | yield return incoming;
717 | var outgoing = NextHalfedge(incoming);
718 | incoming = Halfedges[outgoing];
719 | } while (incoming != -1 && incoming != start);
720 | }
721 |
722 | ///
723 | /// Returns the three point indices of a given triangle id.
724 | ///
725 | public IEnumerable PointsOfTriangle(int t)
726 | {
727 | foreach (var edge in EdgesOfTriangle(t))
728 | {
729 | yield return Triangles[edge];
730 | }
731 | }
732 |
733 | ///
734 | /// Returns the triangle ids adjacent to the given triangle id.
735 | /// Will return up to three values.
736 | ///
737 | public IEnumerable TrianglesAdjacentToTriangle(int t)
738 | {
739 | var adjacentTriangles = new List();
740 | var triangleEdges = EdgesOfTriangle(t);
741 | foreach (var e in triangleEdges)
742 | {
743 | var opposite = Halfedges[e];
744 | if (opposite >= 0)
745 | {
746 | adjacentTriangles.Add(TriangleOfEdge(opposite));
747 | }
748 | }
749 | return adjacentTriangles;
750 | }
751 |
752 | public static int NextHalfedge(int e) => (e % 3 == 2) ? e - 2 : e + 1;
753 | public static int PreviousHalfedge(int e) => (e % 3 == 0) ? e + 2 : e - 1;
754 |
755 | ///
756 | /// Returns the three half-edges of a given triangle id.
757 | ///
758 | public static int[] EdgesOfTriangle(int t) => new int[] { 3 * t, 3 * t + 1, 3 * t + 2 };
759 |
760 | ///
761 | /// Returns the triangle id of a given half-edge.
762 | ///
763 | public static int TriangleOfEdge(int e) { return e / 3; }
764 | #endregion Methods based on index
765 | }
766 | }
--------------------------------------------------------------------------------
/DelaunatorSharp/DelaunatorSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net471
5 | Fast Delaunay triangulation of 2D points
6 | Port from Mapbox's Delaunator project (JavaScript).
7 | https://github.com/nol1fe/delaunator-sharp
8 | https://github.com/nol1fe/delaunator-sharp.git
9 | false
10 | nol1fe
11 | GBros
12 | delaunay triangulation voronoi
13 | MIT
14 | https://github.com/mapbox/delaunator
15 | 1.0.11
16 | Fix obtaining points in GetEdgesOfTriangle
17 | DelaunatorSharp
18 | DelaunatorSharp
19 | Delaunator
20 | Delaunator
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Interfaces/IEdge.cs:
--------------------------------------------------------------------------------
1 | namespace DelaunatorSharp
2 | {
3 | public interface IEdge
4 | {
5 | IPoint P { get; }
6 | IPoint Q { get; }
7 | int Index { get; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Interfaces/IPoint.cs:
--------------------------------------------------------------------------------
1 | namespace DelaunatorSharp
2 | {
3 | public interface IPoint
4 | {
5 | double X { get; set; }
6 | double Y { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Interfaces/ITriangle.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace DelaunatorSharp
4 | {
5 | public interface ITriangle
6 | {
7 | IEnumerable Points { get; }
8 | int Index { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Interfaces/IVoronoiCell.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace DelaunatorSharp
4 | {
5 | public interface IVoronoiCell
6 | {
7 | IPoint[] Points { get; }
8 | int Index { get; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Models/Edge.cs:
--------------------------------------------------------------------------------
1 | namespace DelaunatorSharp
2 | {
3 | public struct Edge : IEdge
4 | {
5 | public IPoint P { get; set; }
6 | public IPoint Q { get; set; }
7 | public int Index { get; set; }
8 |
9 | public Edge(int e, IPoint p, IPoint q)
10 | {
11 | Index = e;
12 | P = p;
13 | Q = q;
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Models/Point.cs:
--------------------------------------------------------------------------------
1 | namespace DelaunatorSharp
2 | {
3 | public struct Point : IPoint
4 | {
5 | public double X { get; set; }
6 | public double Y { get; set; }
7 |
8 | public Point(double x, double y)
9 | {
10 | X = x;
11 | Y = y;
12 | }
13 | public override string ToString() => $"{X},{Y}";
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Models/Triangle.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace DelaunatorSharp
4 | {
5 | public struct Triangle : ITriangle
6 | {
7 | public int Index { get; set; }
8 |
9 | public IEnumerable Points { get; set; }
10 |
11 | public Triangle(int t, IEnumerable points)
12 | {
13 | Points = points;
14 | Index = t;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/DelaunatorSharp/Models/VoronoiCell.cs:
--------------------------------------------------------------------------------
1 | namespace DelaunatorSharp
2 | {
3 | public struct VoronoiCell : IVoronoiCell
4 | {
5 | public IPoint[] Points { get; set; }
6 | public int Index { get; set; }
7 | public VoronoiCell(int triangleIndex, IPoint[] points)
8 | {
9 | Points = points;
10 | Index = triangleIndex;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Images/Delaunator_Circle.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/Images/Delaunator_Circle.PNG
--------------------------------------------------------------------------------
/Images/Delaunator_Package Manager.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/Images/Delaunator_Package Manager.png
--------------------------------------------------------------------------------
/Images/Delaunator_Rectangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/Images/Delaunator_Rectangle.png
--------------------------------------------------------------------------------
/Images/Delaunator_Unity_Example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/Images/Delaunator_Unity_Example.gif
--------------------------------------------------------------------------------
/Images/Delaunator_Unity_Example_Mesh.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nol1fe/delaunator-sharp/d259dae33d35cd7db0c46c20e7c2d3818dae4802/Images/Delaunator_Unity_Example_Mesh.gif
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Patryk Grech
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Delaunator C#
2 | [](https://openupm.com/packages/com.nol1fe.delaunator/)
3 |
4 | Fast [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) of 2D points implemented in C#.
5 |
6 | This code was ported from [Mapbox's Delaunator project](https://github.com/mapbox/delaunator) (JavaScript).
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## Documentation
14 |
15 | See https://mapbox.github.io/delaunator/ for more information about the `Triangles` and `Halfedges` data structures.
16 |
17 |
18 | ## Unity Installation
19 |
20 | Simply edit manifest.json file in your Unity Packages directory
21 | ```
22 | {
23 | "dependencies": {
24 | "com.nol1fe.delaunator": "https://github.com/nol1fe/delaunator-sharp.git?path=DelaunatorSharp.Unity",
25 | }
26 | ```
27 |
28 | ## Unity Example
29 |
30 | From UnityPackage Menager select Delaunator and press "Import into Project".
31 |
32 |
33 |
34 |
35 | ### Burst Triangulator (optional)
36 | For Unity developers seeking even greater performance and advanced triangulation capabilities, consider exploring [BurstTriangulator](https://github.com/andywiecko/BurstTriangulator).
37 |
38 | Built with Unity's Burst compiler, it offers lightning-fast triangulation, making it ideal for large datasets and real-time applications.
39 |
40 | ## WPF Example
41 |
42 | There is available playground in DelaunatorSharp.WPF.Example project that shows examples of drawing [Voronoi Diagram](https://en.wikipedia.org/wiki/Voronoi_diagram), [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) and [Convex Hull](https://en.wikipedia.org/wiki/Convex_hull)
43 |
44 |
45 |
46 |
47 |
48 |
49 | Points were generated with [Poisson Disc Sampling](https://www.jasondavies.com/poisson-disc)
50 | implemented by [UniformPoissonDiskSampler](http://theinstructionlimit.com/fast-uniform-poisson-disk-sampling-in-c)
51 |
52 |
53 | ## Performance
54 | ```
55 | BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19043
56 | 11th Gen Intel Core i7-11800H 2.30GHz, 1 CPU, 16 logical and 8 physical cores .NET Core SDK=5.0.401
57 | ```
58 |
59 | | Method | Count | Type | Mean | Error | StdDev |
60 | |----------- |-------- |--------- |----------:|---------:|---------:|
61 | | Delaunator | 100000 | Uniform | 63.66 ms | 21.68 ms | 14.34 ms |
62 | | Delaunator | 100000 | Gaussian | 62.07 ms | 21.60 ms | 14.29 ms |
63 | | Delaunator | 100000 | Grid | 46.88 ms | 18.53 ms | 12.25 ms |
64 | | Delaunator | 1000000 | Uniform | 658.91 ms | 41.11 ms | 27.19 ms |
65 | | Delaunator | 1000000 | Gaussian | 680.02 ms | 96.03 ms | 63.52 ms |
66 | | Delaunator | 1000000 | Grid | 516.89 ms | 60.64 ms | 40.11 ms |
67 |
68 | ## NuGet
69 |
70 | https://www.nuget.org/packages/Delaunator/
71 |
72 |
--------------------------------------------------------------------------------
/delaunator-sharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.28803.352
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelaunatorSharp", "DelaunatorSharp\DelaunatorSharp.csproj", "{75843EFD-3227-4D27-93C5-F32D8A234E4D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DelaunatorSharp.Benchmark", "DelaunatorSharp.Benchmark\DelaunatorSharp.Benchmark.csproj", "{3470C09A-3B6D-4D89-9F15-0DBA44370392}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DelaunatorSharp.WPF", "DelaunatorSharp.WPF\DelaunatorSharp.WPF.csproj", "{A8C191DB-3C69-4847-B57A-1524D13C1A30}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {75843EFD-3227-4D27-93C5-F32D8A234E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {75843EFD-3227-4D27-93C5-F32D8A234E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {75843EFD-3227-4D27-93C5-F32D8A234E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {75843EFD-3227-4D27-93C5-F32D8A234E4D}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {3470C09A-3B6D-4D89-9F15-0DBA44370392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {3470C09A-3B6D-4D89-9F15-0DBA44370392}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {3470C09A-3B6D-4D89-9F15-0DBA44370392}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {3470C09A-3B6D-4D89-9F15-0DBA44370392}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {A8C191DB-3C69-4847-B57A-1524D13C1A30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {A8C191DB-3C69-4847-B57A-1524D13C1A30}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {A8C191DB-3C69-4847-B57A-1524D13C1A30}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {A8C191DB-3C69-4847-B57A-1524D13C1A30}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {A6939564-FE8F-4987-A6F4-1E8082AFB38D}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------