From 5e8cb736acb1e624248a28c70f2f4c5ad1c55f4b Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 28 Jul 2019 15:32:35 +0200 Subject: [PATCH] Graph structure for storing Voronoi and Delaunay graphs --- Assets/Map/Graph.cs | 13 +++++- Assets/Scripts/GraphGenerator.cs | 72 ++++++++++++++++++------------ Assets/Voronoi/BeachLine.cs | 28 +++++++++--- Assets/Voronoi/VoronoiGenerator.cs | 67 ++++++++++++++++----------- 4 files changed, 119 insertions(+), 61 deletions(-) diff --git a/Assets/Map/Graph.cs b/Assets/Map/Graph.cs index b050378..49b8239 100644 --- a/Assets/Map/Graph.cs +++ b/Assets/Map/Graph.cs @@ -1,10 +1,21 @@ using System.Collections.Generic; +using System.Linq; namespace Assets.Map { public class Graph { - public IList Vertices { get; internal set; } = new List(); + public List Vertices { get; internal set; } = new List(); public ISet<(int, int)> Edges { get; internal set; } = new HashSet<(int, int)>(); + + public void AddEdge(int a, int b) + { + Edges.Add(a < b ? (a, b) : (b, a)); + } + + public IEnumerable<(int, int)> EdgesOf(int a) + { + return Edges.Where(x => x.Item1 == a || x.Item2 == a); + } } } \ No newline at end of file diff --git a/Assets/Scripts/GraphGenerator.cs b/Assets/Scripts/GraphGenerator.cs index db09621..3301f49 100644 --- a/Assets/Scripts/GraphGenerator.cs +++ b/Assets/Scripts/GraphGenerator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Assets.Generators; +using Assets.Map; using Assets.Voronoi; using UnityEditor; using UnityEngine; @@ -18,7 +19,9 @@ namespace Assets public bool displayParabolas = false; public bool displayVertices = true; public bool displayNeighbours = false; - public bool displayLabels= false; + public bool displayLabels = false; + public bool displayHalfEdges = false; + public bool displayEdges = true; } [RequireComponent(typeof(MeshRenderer), typeof(MeshFilter)), ExecuteInEditMode] @@ -96,13 +99,10 @@ namespace Assets if (debug.displayPoints) { - foreach (var point in _points) { Gizmos.color = _colors.ContainsKey(point) ? _colors[point] : Color.white; Gizmos.DrawSphere(point, 1); - - if (debug.displayLabels) Handles.Label(point, $"({point.x:F1}, {point.y:F1})"); } } @@ -111,26 +111,31 @@ namespace Assets if (debug.displayNeighbours) { Gizmos.color = Color.yellow; + DisplayGraphEdges(generator.Delaunay); + } - foreach (var site in generator.Sites) + if (debug.displayVertices) + { + Gizmos.color = Color.blue; + DisplayGraphVertices(generator.Voronoi); + } + + if (debug.displayHalfEdges) + { + Gizmos.color = Color.green; + foreach (var edge in generator.HalfEdges) { - var vecStart = new Vector3((float)site.Point.x, (float)site.Point.y); - foreach (var neighbour in site.Neighbours) - { - var vecEnd = new Vector3((float)neighbour.Point.x, (float)neighbour.Point.y); - Gizmos.DrawLine(vecStart, vecEnd); - } + var vecStart = new Vector3((float)edge.Start.x, (float)edge.Start.y); + var vecEnd = new Vector3((float)edge.End.x, (float)edge.End.y); + + Gizmos.DrawLine(vecStart, vecEnd); } } - - Gizmos.color = Color.green; - foreach (var edge in generator.Edges) + if (debug.displayEdges) { - var vecStart = new Vector3((float)edge.start.x, (float)edge.start.y); - var vecEnd = new Vector3((float)edge.end.x, (float)edge.end.y); - - Gizmos.DrawLine(vecStart, vecEnd); + Gizmos.color = Color.white; + DisplayGraphEdges(generator.Voronoi); } Gizmos.color = Color.red; @@ -153,17 +158,6 @@ namespace Assets } } } - - if (debug.displayVertices) - { - foreach (var vertex in generator.Vertices) - { - var point = new Vector3((float)vertex.x, (float)vertex.y); - - Gizmos.DrawSphere(point, 1); - if (debug.displayLabels) Handles.Label(point, $"({point.x:F1}, {point.y:F1})"); - } - } if (debug.displayBeachLine) { @@ -186,5 +180,25 @@ namespace Assets generator.Step(); } } + + private void DisplayGraphVertices(Graph graph) + { + var vertices = graph.Vertices.Select(p => new Vector3((float) p.x, (float) p.y)); + + foreach (var v in vertices) + { + Gizmos.DrawSphere(v, 1); + if (debug.displayLabels) Handles.Label(v, $"({v.x:F2}, {v.y:F2})"); + } + } + + private void DisplayGraphEdges(Graph graph) + { + var edges = graph.Edges + .Select(edge => (graph.Vertices[edge.Item1], graph.Vertices[edge.Item2])) + .Select(edge => (new Vector3((float)edge.Item1.x, (float)edge.Item1.y), new Vector3((float)edge.Item2.x, (float)edge.Item2.y))); + + foreach (var (s, e) in edges) Gizmos.DrawLine(s, e); + } } } \ No newline at end of file diff --git a/Assets/Voronoi/BeachLine.cs b/Assets/Voronoi/BeachLine.cs index 017495d..9ee2fd8 100644 --- a/Assets/Voronoi/BeachLine.cs +++ b/Assets/Voronoi/BeachLine.cs @@ -6,10 +6,28 @@ using static System.Math; namespace Assets.Voronoi { - public class Edge + public class HalfEdge { - public Point start = null; - public Point end = null; + public Point Start = null; + public Point End = null; + + public int StartVertex = -1; + public int EndVertex = -1; + + private HalfEdge _twin; + + public HalfEdge Twin + { + get => _twin; + set + { + _twin = value; + value._twin = this; + } + } + + public bool IsComplete => (StartVertex != -1 && EndVertex != -1) || (_twin != null && EndVertex != -1 && _twin.EndVertex != -1); + public (int, int) Edge => _twin != null ? (this.EndVertex, _twin.EndVertex) : (StartVertex, EndVertex); } public class Parabola @@ -22,8 +40,8 @@ namespace Assets.Voronoi public Site Site { get; internal set; } public EdgeEvent Event { get; internal set; } - public Edge LeftEdge { get; set; } - public Edge RightEdge { get; set; } + public HalfEdge LeftEdge { get; set; } + public HalfEdge RightEdge { get; set; } public override string ToString() { diff --git a/Assets/Voronoi/VoronoiGenerator.cs b/Assets/Voronoi/VoronoiGenerator.cs index 1eda6fb..f9c8809 100644 --- a/Assets/Voronoi/VoronoiGenerator.cs +++ b/Assets/Voronoi/VoronoiGenerator.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Assets.Map; using Priority_Queue; namespace Assets.Voronoi @@ -29,12 +30,13 @@ namespace Assets.Voronoi public class Site { - public ISet Neighbours { get; internal set; } = new HashSet(); public Point Point { get; internal set; } + public int Index { get; internal set; } - public Site(Point point) + public Site(Point point, int index) { Point = point; + Index = index; } } @@ -43,8 +45,11 @@ namespace Assets.Voronoi public IList Sites { get; } public IPriorityQueue Queue { get; internal set; } - public IList Vertices { get; internal set; } - public IList Edges { get; internal set; } + + public Graph Voronoi; + public Graph Delaunay; + + public List HalfEdges; public BeachLine Line { get; private set; } @@ -52,7 +57,8 @@ namespace Assets.Voronoi public VoronoiGenerator(IList sites) { - Sites = sites.Select(x => new Site(x)).ToList(); + int i = 0; + Sites = sites.Select(x => new Site(x, i++)).ToList(); Reset(); } @@ -62,8 +68,11 @@ namespace Assets.Voronoi Queue = new SimplePriorityQueue(); Line = new BeachLine(); - Vertices = new List(); - Edges = new List(); + Voronoi = new Graph(); + Delaunay = new Graph(); + HalfEdges = new List(); + + Delaunay.Vertices.AddRange(from site in Sites select site.Point); foreach (var site in Sites) { @@ -110,10 +119,7 @@ namespace Assets.Voronoi private void HandleEdgeEvent(EdgeEvent @event) { - var queue = new Queue>(); - var node = @event.node; - var parabola = node.Value; var previous = node.Previous; var next = node.Next; @@ -121,15 +127,30 @@ namespace Assets.Voronoi if (previous?.Value.Event != null) previous.Value.Event.IsValid = false; if (next?.Value.Event != null) next.Value.Event.IsValid = false; - Vertices.Add(@event.vertex); + Voronoi.Vertices.Add(@event.vertex); - node.Value.LeftEdge.end = @event.vertex; - node.Value.RightEdge.end = @event.vertex; + node.Value.LeftEdge.End = @event.vertex; + node.Value.LeftEdge.EndVertex = Voronoi.Vertices.Count - 1; - Edges.Add(node.Value.LeftEdge); - Edges.Add(node.Value.RightEdge); + node.Value.RightEdge.End = @event.vertex; + node.Value.RightEdge.EndVertex = Voronoi.Vertices.Count - 1; - var newEdge = new Edge() { start = @event.vertex }; + HalfEdges.Add(node.Value.LeftEdge); + HalfEdges.Add(node.Value.RightEdge); + + if (node.Value.LeftEdge.IsComplete) + { + var (a, b) = node.Value.LeftEdge.Edge; + Voronoi.AddEdge(a, b); + } + + if (node.Value.RightEdge.IsComplete) + { + var (a, b) = node.Value.RightEdge.Edge; + Voronoi.AddEdge(a, b); + } + + var newEdge = new HalfEdge() { Start = @event.vertex, StartVertex = Voronoi.Vertices.Count - 1 }; node.Previous.Value.RightEdge = newEdge; node.Next.Value.LeftEdge = newEdge; @@ -143,10 +164,7 @@ namespace Assets.Voronoi Enqueue(n); if (previous != null && next != null) - { - previous.Value.Site.Neighbours.Add(next.Value.Site); - next.Value.Site.Neighbours.Add(previous.Value.Site); - } + Delaunay.AddEdge(next.Value.Site.Index, previous.Value.Site.Index); } private void HandleSiteEvent(SiteEvent @event) @@ -163,8 +181,8 @@ namespace Assets.Voronoi var node = Line.AddParabola(@event.Site, above); - var newLeft = new Edge() { start = start }; - var newRight = new Edge() { start = start }; + var newLeft = new HalfEdge() { Start = start }; + var newRight = new HalfEdge() { Start = start, Twin = newLeft }; node.Previous.Value.LeftEdge = left; node.Previous.Value.RightEdge = newLeft; @@ -182,10 +200,7 @@ namespace Assets.Voronoi Enqueue(n); if (node.Previous != null) - { - @event.Site.Neighbours.Add(node.Previous.Value.Site); - node.Previous.Value.Site.Neighbours.Add(@event.Site); - } + Delaunay.AddEdge(@event.Site.Index, node.Previous.Value.Site.Index); } else {