public static List <VoronoiCell> GenerateVoronoiDiagram(List <Vector3> sites) { //First generate the delaunay triangulation List <Triangle> triangles = TriangulateByFlippingEdges(sites); //Generate the voronoi diagram //Step 1. For every delaunay edge, compute a voronoi edge //The voronoi edge is the edge connecting the circumcenters of two neighboring delaunay triangles List <VoronoiEdge> voronoiEdges = new List <VoronoiEdge>(); for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; //Each triangle consists of these edges HalfEdge e1 = t.halfEdge; HalfEdge e2 = e1.nextEdge; HalfEdge e3 = e2.nextEdge; //Calculate the circumcenter for this triangle Vector3 v1 = e1.v.position; Vector3 v2 = e2.v.position; Vector3 v3 = e3.v.position; //The circumcenter is the center of a circle where the triangles corners is on the circumference of that circle //The .XZ() is an extension method that removes the y value of a vector3 so it becomes a vector2 Vector2 center2D = CalculateCircleCenter(new Vector2(v1.x, v1.z), new Vector2(v2.x, v2.z), new Vector2(v3.x, v3.z)); //The circumcenter is also known as a voronoi vertex, which is a position in the diagram where we are equally //close to the surrounding sites Vector3 voronoiVertex = new Vector3(center2D.x, 0f, center2D.y); TryAddVoronoiEdgeFromTriangleEdge(e1, voronoiVertex, voronoiEdges); TryAddVoronoiEdgeFromTriangleEdge(e2, voronoiVertex, voronoiEdges); TryAddVoronoiEdgeFromTriangleEdge(e3, voronoiVertex, voronoiEdges); } //Step 2. Find the voronoi cells where each cell is a list of all edges belonging to a site List <VoronoiCell> voronoiCells = new List <VoronoiCell>(); for (int i = 0; i < voronoiEdges.Count; i++) { VoronoiEdge e = voronoiEdges[i]; //Find the position in the list of all cells that includes this site int cellPos = TryFindCellPos(e, voronoiCells); //No cell was found so we need to create a new cell if (cellPos == -1) { VoronoiCell newCell = new VoronoiCell(e.sitePos); voronoiCells.Add(newCell); newCell.edges.Add(e); } else { voronoiCells[cellPos].edges.Add(e); } } return(voronoiCells); }
public static List <Triangle> TriangulatePoints(List <Vertex> points) { List <Triangle> triangles = new List <Triangle>(); //Sort the points along x-axis //OrderBy is always soring in ascending order - use OrderByDescending to get in the other order points = points.OrderBy(n => n.position.x).ToList(); //The first 3 vertices are always forming a triangle Triangle newTriangle = new Triangle(points[0].position, points[1].position, points[2].position); triangles.Add(newTriangle); //All edges that form the triangles, so we have something to test against List <Edge> edges = new List <Edge>(); edges.Add(new Edge(newTriangle.v1, newTriangle.v2)); edges.Add(new Edge(newTriangle.v2, newTriangle.v3)); edges.Add(new Edge(newTriangle.v3, newTriangle.v1)); //Add the other triangles one by one //Starts at 3 because we have already added 0,1,2 for (int i = 3; i < points.Count; i++) { Vector3 currentPoint = points[i].position; //The edges we add this loop or we will get stuck in an endless loop List <Edge> newEdges = new List <Edge>(); //Is this edge visible? We only need to check if the midpoint of the edge is visible for (int j = 0; j < edges.Count; j++) { Edge currentEdge = edges[j]; Vector3 midPoint = (currentEdge.v1.position + currentEdge.v2.position) / 2f; Edge edgeToMidpoint = new Edge(currentPoint, midPoint); //Check if this line is intersecting bool canSeeEdge = true; for (int k = 0; k < edges.Count; k++) { //Dont compare the edge with itself if (k == j) { continue; } if (AreEdgesIntersecting(edgeToMidpoint, edges[k])) { canSeeEdge = false; break; } } //This is a valid triangle if (canSeeEdge) { Edge edgeToPoint1 = new Edge(currentEdge.v1, new Vertex(currentPoint)); Edge edgeToPoint2 = new Edge(currentEdge.v2, new Vertex(currentPoint)); newEdges.Add(edgeToPoint1); newEdges.Add(edgeToPoint2); Triangle newTri = new Triangle(edgeToPoint1.v1, edgeToPoint1.v2, edgeToPoint2.v1); triangles.Add(newTri); } } for (int j = 0; j < newEdges.Count; j++) { edges.Add(newEdges[j]); } } return(triangles); }
private static void FlipEdge(HalfEdge one) { //The data we need //This edge's triangle HalfEdge two = one.nextEdge; HalfEdge three = one.prevEdge; //The opposite edge's triangle HalfEdge four = one.oppositeEdge; HalfEdge five = one.oppositeEdge.nextEdge; HalfEdge six = one.oppositeEdge.prevEdge; //The vertices Vertex a = one.v; Vertex b = one.nextEdge.v; Vertex c = one.prevEdge.v; Vertex d = one.oppositeEdge.nextEdge.v; //Flip //Change vertex a.halfEdge = one.nextEdge; c.halfEdge = one.oppositeEdge.nextEdge; //Change half-edge //Half-edge - half-edge connections one.nextEdge = three; one.prevEdge = five; two.nextEdge = four; two.prevEdge = six; three.nextEdge = five; three.prevEdge = one; four.nextEdge = six; four.prevEdge = two; five.nextEdge = one; five.prevEdge = three; six.nextEdge = two; six.prevEdge = four; //Half-edge - vertex connection one.v = b; two.v = b; three.v = c; four.v = d; five.v = d; six.v = a; //Half-edge - triangle connection Triangle t1 = one.t; Triangle t2 = four.t; one.t = t1; three.t = t1; five.t = t1; two.t = t2; four.t = t2; six.t = t2; //Opposite-edges are not changing! //Triangle connection t1.v1 = b; t1.v2 = c; t1.v3 = d; t2.v1 = b; t2.v2 = d; t2.v3 = a; t1.halfEdge = three; t2.halfEdge = four; }
public static List <HalfEdge> TransformFromTriangleToHalfEdge(List <Triangle> triangles) { //Make sure the triangles have the same orientation OrientTrianglesClockwise(triangles); //First create a list with all possible half-edges List <HalfEdge> halfEdges = new List <HalfEdge>(triangles.Count * 3); for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; HalfEdge he1 = new HalfEdge(t.v1); HalfEdge he2 = new HalfEdge(t.v2); HalfEdge he3 = new HalfEdge(t.v3); he1.nextEdge = he2; he2.nextEdge = he3; he3.nextEdge = he1; he1.prevEdge = he3; he2.prevEdge = he1; he3.prevEdge = he2; //The vertex needs to know of an edge going from it he1.v.halfEdge = he2; he2.v.halfEdge = he3; he3.v.halfEdge = he1; //The face the half-edge is connected to t.halfEdge = he1; he1.t = t; he2.t = t; he3.t = t; //Add the half-edges to the list halfEdges.Add(he1); halfEdges.Add(he2); halfEdges.Add(he3); } //Find the half-edges going in the opposite direction for (int i = 0; i < halfEdges.Count; i++) { HalfEdge he = halfEdges[i]; Vertex goingToVertex = he.v; Vertex goingFromVertex = he.prevEdge.v; for (int j = 0; j < halfEdges.Count; j++) { //Dont compare with itself if (i == j) { continue; } HalfEdge heOpposite = halfEdges[j]; //Is this edge going between the vertices in the opposite direction if (goingFromVertex.position == heOpposite.v.position && goingToVertex.position == heOpposite.prevEdge.v.position) { he.oppositeEdge = heOpposite; break; } } } return(halfEdges); }