/// <summary> /// Non-allocating version of CalculateDiagram. /// /// I guess it's not strictly true that it generates NO garbage, because /// it might if it has to resize internal buffers, but all buffers are /// reused from invocation to invocation. /// </summary> public void CalculateDiagram(IList <Vector2> inputVertices, ref VoronoiDiagram result) { // TODO: special case for 1 points // TODO: special case for 2 points // TODO: special case for 3 points // TODO: special case for collinear points if (inputVertices.Count < 3) { throw new NotImplementedException("Not implemented for < 3 vertices"); } if (result == null) { result = new VoronoiDiagram(); } var trig = result.Triangulation; result.Clear(); Profiler.BeginSample("Delaunay triangulation"); delCalc.CalculateTriangulation(inputVertices, ref trig); Profiler.EndSample(); pts.Clear(); var verts = trig.Vertices; var tris = trig.Triangles; var centers = result.Vertices; var edges = result.Edges; if (tris.Count > pts.Capacity) { { pts.Capacity = tris.Count; } } if (tris.Count > edges.Capacity) { edges.Capacity = tris.Count; } for (int ti = 0; ti < tris.Count; ti += 3) { var p0 = verts[tris[ti]]; var p1 = verts[tris[ti + 1]]; var p2 = verts[tris[ti + 2]]; // Triangle is in CCW order Debug.Assert(Geom.ToTheLeft(p2, p0, p1)); centers.Add(Geom.CircumcircleCenter(p0, p1, p2)); } for (int ti = 0; ti < tris.Count; ti += 3) { pts.Add(new PointTriangle(tris[ti], ti)); pts.Add(new PointTriangle(tris[ti + 1], ti)); pts.Add(new PointTriangle(tris[ti + 2], ti)); } cmp.tris = tris; cmp.verts = verts; Profiler.BeginSample("Sorting"); pts.Sort(cmp); Profiler.EndSample(); // The comparer lives on between runs of the algorithm, so clear the // reference to the arrays so that the reference is lost. It may be // the case that the calculator lives on much longer than the // results, and not clearing these would keep the results alive, // leaking memory. cmp.tris = null; cmp.verts = null; for (int i = 0; i < pts.Count; i++) { result.FirstEdgeBySite.Add(edges.Count); var start = i; var end = -1; for (int j = i + 1; j < pts.Count; j++) { if (pts[i].Point != pts[j].Point) { end = j - 1; break; } } if (end == -1) { end = pts.Count - 1; } i = end; var count = end - start; Debug.Assert(count >= 0); for (int ptiCurr = start; ptiCurr <= end; ptiCurr++) { bool isEdge; var ptiNext = ptiCurr + 1; if (ptiNext > end) { ptiNext = start; } var ptCurr = pts[ptiCurr]; var ptNext = pts[ptiNext]; var tiCurr = ptCurr.Triangle; var tiNext = ptNext.Triangle; var p0 = verts[ptCurr.Point]; var v2nan = new Vector2(float.NaN, float.NaN); if (count == 0) { isEdge = true; } else if (count == 1) { var cCurr = Geom.TriangleCentroid(verts[tris[tiCurr]], verts[tris[tiCurr + 1]], verts[tris[tiCurr + 2]]); var cNext = Geom.TriangleCentroid(verts[tris[tiNext]], verts[tris[tiNext + 1]], verts[tris[tiNext + 2]]); isEdge = Geom.ToTheLeft(cCurr, p0, cNext); } else { isEdge = !SharesEdge(tris, tiCurr, tiNext); } if (isEdge) { Vector2 v0, v1; if (ptCurr.Point == tris[tiCurr]) { v0 = verts[tris[tiCurr + 2]] - verts[tris[tiCurr + 0]]; } else if (ptCurr.Point == tris[tiCurr + 1]) { v0 = verts[tris[tiCurr + 0]] - verts[tris[tiCurr + 1]]; } else { Debug.Assert(ptCurr.Point == tris[tiCurr + 2]); v0 = verts[tris[tiCurr + 1]] - verts[tris[tiCurr + 2]]; } if (ptNext.Point == tris[tiNext]) { v1 = verts[tris[tiNext + 0]] - verts[tris[tiNext + 1]]; } else if (ptNext.Point == tris[tiNext + 1]) { v1 = verts[tris[tiNext + 1]] - verts[tris[tiNext + 2]]; } else { Debug.Assert(ptNext.Point == tris[tiNext + 2]); v1 = verts[tris[tiNext + 2]] - verts[tris[tiNext + 0]]; } edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.RayCCW, ptCurr.Point, tiCurr / 3, -1, Geom.RotateRightAngle(v0) )); edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.RayCW, ptCurr.Point, tiNext / 3, -1, Geom.RotateRightAngle(v1) )); } else { if (!Geom.AreCoincident(centers[tiCurr / 3], centers[tiNext / 3])) { edges.Add(new VoronoiDiagram.Edge( VoronoiDiagram.EdgeType.Segment, ptCurr.Point, tiCurr / 3, tiNext / 3, v2nan )); } } } } }
Vector2 Centroid(PointTriangle pt) { var ti = pt.Triangle; return(Geom.TriangleCentroid(verts[tris[ti]], verts[tris[ti + 1]], verts[tris[ti + 2]])); }