Skip to content

Commit

Permalink
Simplified some of the code.
Browse files Browse the repository at this point in the history
Added xml docs to all the classes.
  • Loading branch information
AlanRynne committed Jun 23, 2020
1 parent f460da6 commit 27ba0b7
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 132 deletions.
10 changes: 9 additions & 1 deletion Paramdigma.Core.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Binormal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Circumcenter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=circumcircle/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Delaunay/@EntryIndexedValue">True</s:Boolean>


<s:Boolean x:Key="/Default/UserDictionary/Words/=Paramdigma/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Perp/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Perp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Voronoi/@EntryIndexedValue">True</s:Boolean>

</wpf:ResourceDictionary>
85 changes: 39 additions & 46 deletions src/Geometry/2D/Delaunay.cs
Original file line number Diff line number Diff line change
@@ -1,73 +1,72 @@
using System.Collections.Generic;
using System.Linq;

namespace Paramdigma.Core.Geometry
{
/// <summary>
/// Class holding all the delaunay and vornoi classes in 2 dimensions.
/// Class holding all the delaunay and Voronoi classes in 2 dimensions.
/// </summary>
public static class Delaunay
{
public static List<DelaunayTriangle> Compute(List<DelaunayPoint> points, List<DelaunayTriangle> border)
/// <summary>
/// Compute the delaunay triangulation of a given list of points.
/// </summary>
/// <param name="points">Points to find delaunay tessellation.</param>
/// <param name="border">Border to start from.</param>
/// <returns>List of <see cref="DelaunayTriangle"/>.</returns>
public static IEnumerable<DelaunayTriangle> Compute(IEnumerable<DelaunayPoint> points, IEnumerable<DelaunayTriangle> border)
{
var triangulation = new List<DelaunayTriangle>(border);
points.Reverse();
foreach (var point in points)
{
var badTriangles = FindBadTriangles(point, triangulation);
var badTriangles = FindBadTriangles(point, triangulation).ToList();
var polygon = FindHoleBoundaries(badTriangles);
foreach (var triangle in badTriangles)
{
foreach (var vertex in triangle.Vertices)
{
vertex.AdjacentTriangles.Remove(triangle);
if (triangulation.Contains(triangle)) triangulation.Remove(triangle);
}
}

foreach (var edge in polygon)
{
var triangle = new DelaunayTriangle(point, edge.StartPoint, edge.EndPoint);
triangulation.Add(triangle);
if (triangulation.Contains(triangle))
triangulation.Remove(triangle);
}

triangulation.AddRange(polygon.Select(edge => new DelaunayTriangle(point, edge.StartPoint, edge.EndPoint)));
}

return triangulation;
}

public static List<DelaunayEdge> Voronoi(List<DelaunayTriangle> triangulation)
{
var voronoiEdges = new List<DelaunayEdge>();
foreach (var triangle in triangulation)
foreach (var neigbour in triangle.TrianglesWithSharedEdges())
{
var edge = new DelaunayEdge(triangle.Circumcenter, neigbour.Circumcenter);
voronoiEdges.Add(edge);
}
/// <summary>
/// Computes the Voronoi diagram of a given Delaunay triangulation as a list of <see cref="DelaunayTriangle"/> instances.
/// </summary>
/// <param name="triangulation">Delaunay triangulation.</param>
/// <returns>Collection of lines representing the Voronoi cells.</returns>
public static IEnumerable<Line2d> Voronoi(IEnumerable<DelaunayTriangle> triangulation)
=> from triangle in triangulation
from neighbour in triangle.TrianglesWithSharedEdges()
select new Line2d(triangle.Circumcenter, neighbour.Circumcenter);

return voronoiEdges;
}

private static List<DelaunayEdge> FindHoleBoundaries(List<DelaunayTriangle> badTriangles)
private static IEnumerable<DelaunayEdge> FindHoleBoundaries(IEnumerable<DelaunayTriangle> badTriangles)
{
var boundaryEdges = new List<DelaunayEdge>();
var duplicateEdges = new List<DelaunayEdge>();
foreach (var triangle in badTriangles)
{
var e = new DelaunayEdge(triangle.Vertices[0], triangle.Vertices[1]);
if (!boundaryEdges.Contains(e))
boundaryEdges.Add(e);
else
duplicateEdges.Add(e);
var f = new DelaunayEdge(triangle.Vertices[1], triangle.Vertices[2]);
if (!boundaryEdges.Contains(f))
boundaryEdges.Add(f);
else
duplicateEdges.Add(f);
var j = new DelaunayEdge(triangle.Vertices[2], triangle.Vertices[0]);
if (!boundaryEdges.Contains(j))
boundaryEdges.Add(j);
else
duplicateEdges.Add(j);
for (int i = 0; i < triangle.Vertices.Count; i++)
{
var e = new DelaunayEdge(
triangle.Vertices[i],
triangle.Vertices[(i + 1) % triangle.Vertices.Count]);
if (!boundaryEdges.Contains(e))
boundaryEdges.Add(e);
else
duplicateEdges.Add(e);
}
}


for (var i = boundaryEdges.Count - 1; i >= 0; i--)
{
var e = boundaryEdges[i];
Expand All @@ -78,13 +77,7 @@ private static List<DelaunayEdge> FindHoleBoundaries(List<DelaunayTriangle> badT
return boundaryEdges;
}

private static List<DelaunayTriangle> FindBadTriangles(DelaunayPoint point, List<DelaunayTriangle> triangles)
{
var badTriangles = new List<DelaunayTriangle>();
foreach (var triangle in triangles)
if (triangle.IsPointInsideCircumcircle(point))
badTriangles.Add(triangle);
return badTriangles;
}
private static IEnumerable<DelaunayTriangle> FindBadTriangles(Point2d point, IEnumerable<DelaunayTriangle> triangles)
=> triangles.Where(triangle => triangle.IsPointInsideCircumcircle(point));
}
}
34 changes: 25 additions & 9 deletions src/Geometry/2D/DelaunayEdge.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
namespace Paramdigma.Core.Geometry
{
/// <summary>
/// Represents a connection between two points in a Delaunay triangulation.
/// </summary>
public class DelaunayEdge
{
public DelaunayPoint EndPoint;
/// <summary>
/// The edge's start point.
/// </summary>
public DelaunayPoint StartPoint;

/// <summary>
/// The edge's end point.
/// </summary>
public DelaunayPoint EndPoint;

/// <summary>
/// Initializes a new instance of the <see cref="DelaunayEdge"/> class.
/// </summary>
/// <param name="startPoint">Start point.</param>
/// <param name="endPoint">End point.</param>
public DelaunayEdge(DelaunayPoint startPoint, DelaunayPoint endPoint)
{
this.StartPoint = startPoint;
this.EndPoint = endPoint;
StartPoint = startPoint;
EndPoint = endPoint;
}

/// <inheritdoc />
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj.GetType() != this.GetType()) return false;
var edge = obj as DelaunayEdge;
var samePoints = this.StartPoint == edge.StartPoint && this.EndPoint == edge.EndPoint;
var samePointsReversed = this.StartPoint == edge.EndPoint && this.EndPoint == edge.StartPoint;
if (!(obj is DelaunayEdge edge))
return false;
var samePoints = StartPoint == edge.StartPoint && EndPoint == edge.EndPoint;
var samePointsReversed = StartPoint == edge.EndPoint && EndPoint == edge.StartPoint;
return samePoints || samePointsReversed;
}

/// <inheritdoc />
public override int GetHashCode()
{
var hCode = (int)this.StartPoint.X ^ (int)this.StartPoint.Y ^ (int)this.EndPoint.X ^ (int)this.EndPoint.Y;
int hCode = (int)StartPoint.X ^ (int)StartPoint.Y ^ (int)EndPoint.X ^ (int)EndPoint.Y;
return hCode.GetHashCode();
}
}
Expand Down
22 changes: 21 additions & 1 deletion src/Geometry/2D/DelaunayPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@

namespace Paramdigma.Core.Geometry
{
/// <summary>
/// Represents a point in a delaunay triangulation with adjacency information.
/// </summary>
public class DelaunayPoint : Point2d
{
/// <summary>
/// List of adjacent triangles of this point.
/// </summary>
public List<DelaunayTriangle> AdjacentTriangles;

public DelaunayPoint(double x, double y) : base(x, y) => this.AdjacentTriangles = new List<DelaunayTriangle>();
/// <summary>
/// Initializes a new instance of the <see cref="DelaunayPoint"/> class from it's coordinates.
/// </summary>
/// <param name="x">X Coordinate.</param>
/// <param name="y">Y Coordinate.</param>
public DelaunayPoint(double x, double y) : base(x, y)
{
AdjacentTriangles = new List<DelaunayTriangle>();
}

/// <summary>
/// Initializes a new instance of the <see cref="DelaunayPoint"/> class from a <see cref="Point2d"/> instance.
/// </summary>
/// <param name="point">Point to create <see cref="DelaunayPoint"/> from.</param>
public DelaunayPoint(Point2d point) : this(point.X, point.Y) { }
}
}
113 changes: 75 additions & 38 deletions src/Geometry/2D/DelaunayTriangle.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Paramdigma.Core.Geometry
{
/// <summary>
/// Represents a triangle in a delaunay triangulation operation.
/// </summary>
public class DelaunayTriangle
{
public DelaunayPoint Circumcenter;
/// <summary>
/// Circumcenter of this triangle.
/// </summary>
public Point2d Circumcenter;

/// <summary>
/// Squared radius of the triangle's circumcircle.
/// </summary>
public double RadiusSquared;

public List<DelaunayPoint> Vertices = new List<DelaunayPoint>();
/// <summary>
/// List of vertices of this triangle.
/// </summary>
public List<DelaunayPoint> Vertices;

/// <summary>
/// Initializes a new instance of the <see cref="DelaunayTriangle" /> class.
/// </summary>
/// <param name="point1">Point A.</param>
/// <param name="point2">Point B.</param>
/// <param name="point3">Point C.</param>
public DelaunayTriangle(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoint point3)
{
this.Vertices = new List<DelaunayPoint>();
if (!this.IsCounterClockwise(point1, point2, point3))
if (!IsCounterClockwise(point1, point2, point3))
{
this.Vertices.Add(point1);
this.Vertices.Add(point3);
Expand All @@ -32,17 +52,44 @@ public DelaunayTriangle(DelaunayPoint point1, DelaunayPoint point2, DelaunayPoin
this.UpdateCircumcircle();
}

public List<DelaunayTriangle> TrianglesWithSharedEdges()
/// <summary>
/// Returns a collection of neighbouring triangles.
/// </summary>
/// <returns>Collection of triangles.</returns>
public IEnumerable<DelaunayTriangle> TrianglesWithSharedEdges()
{
var neighbours = new List<DelaunayTriangle>();
foreach (var vertex in this.Vertices)
foreach (var sharedTriangle in vertex.AdjacentTriangles)
if (this.SharesEdgeWidth(sharedTriangle) && neighbours.Contains(sharedTriangle) == false && sharedTriangle != this)
neighbours.Add(sharedTriangle);
foreach (var sharedTriangle in from vertex in this.Vertices
from sharedTriangle in vertex.AdjacentTriangles
where this.SharesEdgeWidth(sharedTriangle)
&& neighbours.Contains(sharedTriangle) == false
&& sharedTriangle != this
select sharedTriangle)
neighbours.Add(sharedTriangle);

return neighbours;
}

public void UpdateCircumcircle()
/// <summary>
/// Checks if a point is inside this triangle's circumcircle.
/// </summary>
/// <param name="point">Point2d to check.</param>
/// <returns>True if inside.</returns>
public bool IsPointInsideCircumcircle(Point2d point)
{
var dSquared = ((point.X - this.Circumcenter.X) * (point.X - this.Circumcenter.X))
+ ((point.Y - this.Circumcenter.Y) * (point.Y - this.Circumcenter.Y));
return dSquared < this.RadiusSquared;
}

private static bool IsCounterClockwise(Point2d point1, Point2d point2, Point2d point3)
{
var result = ((point2.X - point1.X) * (point3.Y - point1.Y))
- ((point3.X - point1.X) * (point2.Y - point1.Y));
return result > 0;
}

private void UpdateCircumcircle()
{
var p0 = this.Vertices[0];
var p1 = this.Vertices[1];
Expand All @@ -51,42 +98,32 @@ public void UpdateCircumcircle()
var dB = (p1.X * p1.X) + (p1.Y * p1.Y);
var dC = (p2.X * p2.X) + (p2.Y * p2.Y);

var aux1 = (dA * (p2.Y - p1.Y)) + (dB * (p0.Y - p2.Y)) + (dC * (p1.Y - p0.Y));
var aux2 = -((dA * (p2.X - p1.X)) + (dB * (p0.X - p2.X)) + (dC * (p1.X - p0.X)));
var div = 2 * ((p0.X * (p2.Y - p1.Y)) + (p1.X * (p0.Y - p2.Y)) + (p2.X * (p1.Y - p0.Y)));
var aux1 = (dA * (p2.Y - p1.Y))
+ (dB * (p0.Y - p2.Y))
+ (dC * (p1.Y - p0.Y));
var aux2 = -((dA * (p2.X - p1.X))
+ (dB * (p0.X - p2.X))
+ (dC * (p1.X - p0.X)));
var 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 Exception();
if (Math.Abs(div) < Settings.Tolerance)
throw new Exception("Divisor too small");

var center = new DelaunayPoint(aux1 / div, aux2 / div);
this.Circumcenter = center;
this.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)
{
var 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)
{
var sharedCount = 0;
foreach (var vertex in this.Vertices)
foreach (var vertex2 in triangle.Vertices)
if (vertex == vertex2)
sharedCount++;
return sharedCount == 2;

throw new NotImplementedException();
this.RadiusSquared = ((center.X - p0.X) * (center.X - p0.X))
+ ((center.Y - p0.Y) * (center.Y - p0.Y));
}

public bool IsPointInsideCircumcircle(DelaunayPoint point)
private bool SharesEdgeWidth(DelaunayTriangle triangle)
{
var d_squared = ((point.X - this.Circumcenter.X) * (point.X - this.Circumcenter.X)) +
((point.Y - this.Circumcenter.Y) * (point.Y - this.Circumcenter.Y));
return d_squared < this.RadiusSquared;
var shared = from pt1 in this.Vertices
from pt2 in triangle.Vertices
where pt1 == pt2
select pt1;
return shared.Count() == 2;
}
}
}
Loading

0 comments on commit 27ba0b7

Please sign in to comment.