forked from AlanRynne/AR_Lib
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added 2D delaunay and voronoi in Delaunay class.
Added specific basic tests. Ignored coverage folder.
- Loading branch information
1 parent
926aa94
commit 40478bf
Showing
6 changed files
with
308 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -353,4 +353,6 @@ MigrationBackup/ | |
.vscode/settings.json | ||
|
||
coverage/reports/ | ||
docs/* | ||
docs/* | ||
|
||
coverage/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Paramdigma.Core.Geometry | ||
{ | ||
/// <summary> | ||
/// Class holding all the delaunay and vornoi classes in 2 dimensions. | ||
/// </summary> | ||
public static class Delaunay | ||
{ | ||
|
||
public static List<DelaunayTriangle> Compute(List<DelaunayPoint> points, List<DelaunayTriangle> border) | ||
{ | ||
List<DelaunayTriangle> triangulation = new List<DelaunayTriangle>(border); | ||
points.Reverse(); | ||
foreach (DelaunayPoint point in points) | ||
{ | ||
List<DelaunayTriangle> badTriangles = FindBadTriangles(point, triangulation); | ||
List<DelaunayEdge> polygon = FindHoleBoundaries(badTriangles); | ||
foreach (DelaunayTriangle triangle in badTriangles) | ||
{ | ||
foreach (DelaunayPoint vertex in triangle.Vertices) | ||
{ | ||
vertex.AdjacentTriangles.Remove(triangle); | ||
} | ||
if (triangulation.Contains(triangle)) triangulation.Remove(triangle); | ||
} | ||
foreach (DelaunayEdge edge in polygon) | ||
{ | ||
DelaunayTriangle triangle = new DelaunayTriangle(point, edge.StartPoint, edge.EndPoint); | ||
triangulation.Add(triangle); | ||
} | ||
} | ||
return triangulation; | ||
} | ||
|
||
public static List<DelaunayEdge> Voronoi(List<DelaunayTriangle> triangulation) | ||
{ | ||
List<DelaunayEdge> voronoiEdges = new List<DelaunayEdge>(); | ||
foreach (DelaunayTriangle triangle in triangulation) | ||
{ | ||
foreach (DelaunayTriangle neigbour in triangle.TrianglesWithSharedEdges()) | ||
{ | ||
DelaunayEdge edge = new DelaunayEdge(triangle.Circumcenter, neigbour.Circumcenter); | ||
voronoiEdges.Add(edge); | ||
|
||
} | ||
} | ||
return voronoiEdges; | ||
} | ||
|
||
private static List<DelaunayEdge> FindHoleBoundaries(List<DelaunayTriangle> badTriangles) | ||
{ | ||
List<DelaunayEdge> boundaryEdges = new List<DelaunayEdge>(); | ||
List<DelaunayEdge> duplicateEdges = new List<DelaunayEdge>(); | ||
foreach (DelaunayTriangle triangle in badTriangles) | ||
{ | ||
DelaunayEdge e = new DelaunayEdge(triangle.Vertices[0], triangle.Vertices[1]); | ||
if (!boundaryEdges.Contains(e)) | ||
boundaryEdges.Add(e); | ||
else | ||
duplicateEdges.Add(e); | ||
DelaunayEdge f = new DelaunayEdge(triangle.Vertices[1], triangle.Vertices[2]); | ||
if (!boundaryEdges.Contains(f)) | ||
boundaryEdges.Add(f); | ||
else | ||
duplicateEdges.Add(f); | ||
DelaunayEdge j = new DelaunayEdge(triangle.Vertices[2], triangle.Vertices[0]); | ||
if (!boundaryEdges.Contains(j)) | ||
boundaryEdges.Add(j); | ||
else | ||
duplicateEdges.Add(j); | ||
} | ||
|
||
for (int i = boundaryEdges.Count - 1; i >= 0; i--) | ||
{ | ||
DelaunayEdge e = boundaryEdges[i]; | ||
if (duplicateEdges.Contains(e)) | ||
boundaryEdges.Remove(e); | ||
} | ||
return boundaryEdges; | ||
} | ||
|
||
private static List<DelaunayTriangle> FindBadTriangles(DelaunayPoint point, List<DelaunayTriangle> triangles) | ||
{ | ||
List<DelaunayTriangle> badTriangles = new List<DelaunayTriangle>(); | ||
foreach (DelaunayTriangle triangle in triangles) | ||
{ | ||
if (triangle.IsPointInsideCircumcircle(point)) badTriangles.Add(triangle); | ||
} | ||
return badTriangles; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
namespace Paramdigma.Core.Geometry | ||
{ | ||
public class DelaunayEdge | ||
{ | ||
public DelaunayPoint StartPoint; | ||
public DelaunayPoint EndPoint; | ||
|
||
public DelaunayEdge(DelaunayPoint startPoint, DelaunayPoint endPoint) | ||
{ | ||
StartPoint = startPoint; | ||
EndPoint = endPoint; | ||
} | ||
|
||
public override bool Equals(object obj) | ||
{ | ||
if (obj == null) return false; | ||
if (obj.GetType() != GetType()) return false; | ||
var edge = obj as DelaunayEdge; | ||
var samePoints = StartPoint == edge.StartPoint && EndPoint == edge.EndPoint; | ||
var samePointsReversed = StartPoint == edge.EndPoint && EndPoint == edge.StartPoint; | ||
return samePoints || samePointsReversed; | ||
} | ||
|
||
public override int GetHashCode() | ||
{ | ||
int hCode = (int)StartPoint.X ^ (int)StartPoint.Y ^ (int)EndPoint.X ^ (int)EndPoint.Y; | ||
return hCode.GetHashCode(); | ||
} | ||
|
||
|
||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace Paramdigma.Core.Geometry | ||
{ | ||
public class DelaunayPoint : Point2d | ||
{ | ||
public List<DelaunayTriangle> AdjacentTriangles; | ||
|
||
public DelaunayPoint(double x, double y) : base(x, y) | ||
{ | ||
AdjacentTriangles = new List<DelaunayTriangle>(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Paramdigma.Core.Geometry | ||
{ | ||
public class DelaunayTriangle | ||
{ | ||
|
||
public List<DelaunayPoint> Vertices = new List<DelaunayPoint>(); | ||
|
||
public DelaunayPoint Circumcenter; | ||
public double RadiusSquared; | ||
|
||
public List<DelaunayTriangle> TrianglesWithSharedEdges() | ||
{ | ||
List<DelaunayTriangle> neighbours = new List<DelaunayTriangle>(); | ||
foreach (DelaunayPoint vertex in Vertices) | ||
{ | ||
foreach (DelaunayTriangle sharedTriangle in vertex.AdjacentTriangles) | ||
{ | ||
if (this.SharesEdgeWidth(sharedTriangle) && neighbours.Contains(sharedTriangle) == false && sharedTriangle != this) neighbours.Add(sharedTriangle); | ||
|
||
} | ||
} | ||
return neighbours; | ||
} | ||
public DelaunayTriangle(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoint point3) | ||
{ | ||
|
||
Vertices = new List<DelaunayPoint>(); | ||
if (!IsCounterClockwise(point1, point2, point3)) | ||
{ | ||
Vertices.Add(point1); | ||
Vertices.Add(point3); | ||
Vertices.Add(point2); | ||
} | ||
else | ||
{ | ||
Vertices.Add(point1); | ||
Vertices.Add(point2); | ||
Vertices.Add(point3); | ||
} | ||
Vertices[0].AdjacentTriangles.Add(this); | ||
Vertices[1].AdjacentTriangles.Add(this); | ||
Vertices[2].AdjacentTriangles.Add(this); | ||
UpdateCircumcircle(); | ||
|
||
} | ||
public void UpdateCircumcircle() | ||
{ | ||
DelaunayPoint p0 = Vertices[0]; | ||
DelaunayPoint p1 = Vertices[1]; | ||
DelaunayPoint p2 = Vertices[2]; | ||
double dA = p0.X * p0.X + p0.Y * p0.Y; | ||
double dB = p1.X * p1.X + p1.Y * p1.Y; | ||
double dC = p2.X * p2.X + p2.Y * p2.Y; | ||
|
||
double aux1 = (dA * (p2.Y - p1.Y) + dB * (p0.Y - p2.Y) + dC * (p1.Y - p0.Y)); | ||
double aux2 = -(dA * (p2.X - p1.X) + dB * (p0.X - p2.X) + dC * (p1.X - p0.X)); | ||
double div = (2 * (p0.X * (p2.Y - p1.Y) + p1.X * (p0.Y - p2.Y) + p2.X * (p1.Y - p0.Y))); | ||
|
||
if (div == 0) | ||
{ | ||
throw new System.Exception(); | ||
} | ||
|
||
DelaunayPoint center = new DelaunayPoint(aux1 / div, aux2 / div); | ||
Circumcenter = center; | ||
RadiusSquared = (center.X - p0.X) * (center.X - p0.X) + (center.Y - p0.Y) * (center.Y - p0.Y); | ||
} | ||
|
||
public bool IsCounterClockwise(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoint point3) | ||
{ | ||
double result = (point2.X - point1.X) * (point3.Y - point1.Y) - | ||
(point3.X - point1.X) * (point2.Y - point1.Y); | ||
return result > 0; | ||
} | ||
|
||
public bool SharesEdgeWidth(DelaunayTriangle triangle) | ||
{ | ||
int sharedCount = 0; | ||
foreach (DelaunayPoint vertex in this.Vertices) | ||
{ | ||
foreach (DelaunayPoint vertex2 in triangle.Vertices) | ||
{ | ||
if (vertex == vertex2) sharedCount++; | ||
} | ||
} | ||
return sharedCount == 2; | ||
|
||
throw new NotImplementedException(); | ||
} | ||
public bool IsPointInsideCircumcircle(DelaunayPoint point) | ||
{ | ||
double d_squared = (point.X - Circumcenter.X) * (point.X - Circumcenter.X) + | ||
(point.Y - Circumcenter.Y) * (point.Y - Circumcenter.Y); | ||
return d_squared < RadiusSquared; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using Paramdigma.Core.Geometry; | ||
using Xunit; | ||
|
||
namespace Paramdigma.Core.Tests.Geometry | ||
{ | ||
public class DelaunayTests | ||
{ | ||
[Fact] | ||
public void CanComputeDelaunay() | ||
{ | ||
var maxX = 100; | ||
var maxY = 100; | ||
var point0 = new DelaunayPoint(0, 0); | ||
var point1 = new DelaunayPoint(0, maxY); | ||
var point2 = new DelaunayPoint(maxX, maxY); | ||
var point3 = new DelaunayPoint(maxX, 0); | ||
var point4 = new DelaunayPoint(maxX/2, maxY/2); | ||
var points = new List<DelaunayPoint>() { point0, point1, point2, point3 }; | ||
var triangle1 = new DelaunayTriangle(point0, point1, point2); | ||
var triangle2 = new DelaunayTriangle(point0, point2, point3); | ||
var border = new List<DelaunayTriangle> { triangle1, triangle2 }; | ||
|
||
var delaunay = Delaunay.Compute( | ||
new List<DelaunayPoint>{point0,point1,point2,point3,point4}, | ||
border | ||
); | ||
Assert.True(delaunay.Count == 4); | ||
|
||
var voronoi = Delaunay.Voronoi(delaunay); | ||
// TODO: We expect 8 because it currently outputs the lines repeated twice. | ||
Assert.True(voronoi.Count == 8); | ||
} | ||
|
||
private List<DelaunayPoint> GeneratePoints(int amout, double maxX, double maxY, out List<DelaunayTriangle> border) | ||
{ | ||
|
||
DelaunayPoint point0 = new DelaunayPoint(0, 0); | ||
DelaunayPoint point1 = new DelaunayPoint(0, maxY); | ||
DelaunayPoint point2 = new DelaunayPoint(maxX, maxY); | ||
DelaunayPoint point3 = new DelaunayPoint(maxX, 0); | ||
List<DelaunayPoint> points = new List<DelaunayPoint>() { point0, point1, point2, point3 }; | ||
DelaunayTriangle triangle1 = new DelaunayTriangle(point0, point1, point2); | ||
DelaunayTriangle triangle2 = new DelaunayTriangle(point0, point2, point3); | ||
border = new List<DelaunayTriangle> { triangle1, triangle2 }; | ||
Random rnd = new Random(); | ||
List<DelaunayPoint> points2 = new List<DelaunayPoint>(); | ||
for (int i = 0; i < amout - 4; i++) | ||
{ | ||
points2.Add(RandomPoint(rnd, 0, maxX)); | ||
} | ||
return points2; | ||
} | ||
|
||
|
||
public static DelaunayPoint RandomPoint(Random RandGenerator, double MinValue, double MaxValue) | ||
{ | ||
double range = MaxValue - MinValue; | ||
DelaunayPoint randomPoint = new DelaunayPoint(RandGenerator.NextDouble() * range + MinValue, RandGenerator.NextDouble() * range + MinValue); | ||
return randomPoint; | ||
} | ||
} | ||
} |