/// <summary> /// Will return the new root (unchanged except in start-up). /// </summary> /// <param name="e">The VDataEvent.</param> /// <param name="root">The root node.</param> /// <param name="vg">The VoronoiGraph.</param> /// <param name="ys">The ys.</param> /// <param name="circleCheckList">The circle check list.</param> /// <returns>The new root.</returns> public static VNode ProcessDataEvent(VDataEvent e, VNode root, VoronoiGraph vg, double ys, out VDataNode[] circleCheckList) { if (root == null) { root = new VDataNode(e.DataPoint); circleCheckList = new[] { (VDataNode)root }; return(root); } // 1. Find the node to be replaced VNode c = FindDataNode(root, ys, e.DataPoint.X); // 2. Create the subtree (ONE Edge, but two VEdgeNodes) VoronoiEdge ve = new VoronoiEdge { LeftData = ((VDataNode)c).DataPoint, RightData = e.DataPoint, VVertexA = Fortune.VVUnkown, VVertexB = Fortune.VVUnkown }; vg.Edges.Add(ve); VNode subRoot; if (Math.Abs(ve.LeftData.Y - ve.RightData.Y) < 1e-10) { if (ve.LeftData.X < ve.RightData.X) { subRoot = new VEdgeNode(ve, false) { Left = new VDataNode(ve.LeftData), Right = new VDataNode(ve.RightData) }; } else { subRoot = new VEdgeNode(ve, true) { Left = new VDataNode(ve.RightData), Right = new VDataNode(ve.LeftData) }; } circleCheckList = new[] { (VDataNode)subRoot.Left, (VDataNode)subRoot.Right }; } else { subRoot = new VEdgeNode(ve, false) { Left = new VDataNode(ve.LeftData), Right = new VEdgeNode(ve, true) { Left = new VDataNode(ve.RightData), Right = new VDataNode(ve.LeftData) } }; circleCheckList = new[] { (VDataNode)subRoot.Left, (VDataNode)subRoot.Right.Left, (VDataNode)subRoot.Right.Right }; } // 3. Apply subtree if (c.Parent == null) { return(subRoot); } c.Parent.Replace(c, subRoot); return(root); }
/// <summary> /// Calculates a list of edges and junction vertices by using the specified points. /// This defaults to not using any tolerance for determining if points are equal, /// and will not use the cleanup algorithm, which breaks the HandleBoundaries /// method in the Voronoi class. /// </summary> /// <param name="vertices">The original points to use during the calculation</param> /// <returns>A VoronoiGraph structure containing the output geometries</returns> public static VoronoiGraph ComputeVoronoiGraph(double[] vertices) { //BinaryPriorityQueue pq = new BinaryPriorityQueue(); SortedDictionary <VEvent, VEvent> pq = new SortedDictionary <VEvent, VEvent>(); Dictionary <VDataNode, VCircleEvent> currentCircles = new Dictionary <VDataNode, VCircleEvent>(); VoronoiGraph vg = new VoronoiGraph(); VNode rootNode = null; for (int i = 0; i < vertices.Length / 2; i++) { //pq.Push(new VDataEvent(new Vector(vertex))); VDataEvent e = new VDataEvent(new Vector2(vertices, i * 2)); if (pq.ContainsKey(e)) { continue; } pq.Add(e, e); } while (pq.Count > 0) { //VEvent ve = pq.Pop() as VEvent; VEvent ve = pq.First().Key; pq.Remove(ve); VDataNode[] circleCheckList = new VDataNode[] { }; if (ve is VDataEvent) { rootNode = VNode.ProcessDataEvent(ve as VDataEvent, rootNode, vg, ve.Y, out circleCheckList); } else if (ve is VCircleEvent) { currentCircles.Remove(((VCircleEvent)ve).NodeN); if (!((VCircleEvent)ve).Valid) { continue; } rootNode = VNode.ProcessCircleEvent(ve as VCircleEvent, rootNode, vg, out circleCheckList); } else if (ve != null) { throw new Exception("Got event of type " + ve.GetType() + "!"); } foreach (VDataNode vd in circleCheckList) { if (currentCircles.ContainsKey(vd)) { currentCircles[vd].Valid = false; currentCircles.Remove(vd); } if (ve == null) { continue; } VCircleEvent vce = VNode.CircleCheckDataNode(vd, ve.Y); if (vce == null) { continue; } //pq.Push(vce); pq.Add(vce, vce); currentCircles[vd] = vce; } if (!(ve is VDataEvent)) { continue; } Vector2 dp = ((VDataEvent)ve).DataPoint; foreach (VCircleEvent vce in currentCircles.Values) { if (MathTools.Dist(dp.X, dp.Y, vce.Center.X, vce.Center.Y) < vce.Y - vce.Center.Y && Math.Abs(MathTools.Dist(dp.X, dp.Y, vce.Center.X, vce.Center.Y) - (vce.Y - vce.Center.Y)) > 1e-10) { vce.Valid = false; } } } // This is where the MapWindow version should exit since it uses the HandleBoundaries // function instead. The following code is needed for Benjamin Ditter's original process to work. if (!DoCleanup) { return(vg); } VNode.CleanUpTree(rootNode); foreach (VoronoiEdge ve in vg.Edges) { if (ve.Done) { continue; } if (ve.VVertexB != VVUnkown) { continue; } ve.AddVertex(VVInfinite); if (Math.Abs(ve.LeftData.Y - ve.RightData.Y) < 1e-10 && ve.LeftData.X < ve.RightData.X) { Vector2 t = ve.LeftData; ve.LeftData = ve.RightData; ve.RightData = t; } } ArrayList minuteEdges = new ArrayList(); foreach (VoronoiEdge ve in vg.Edges) { if (ve.IsPartlyInfinite || !ve.VVertexA.Equals(ve.VVertexB)) { continue; } minuteEdges.Add(ve); // prevent rounding errors from expanding to holes foreach (VoronoiEdge ve2 in vg.Edges) { if (ve2.VVertexA.Equals(ve.VVertexA)) { ve2.VVertexA = ve.VVertexA; } if (ve2.VVertexB.Equals(ve.VVertexA)) { ve2.VVertexB = ve.VVertexA; } } } foreach (VoronoiEdge ve in minuteEdges) { vg.Edges.Remove(ve); } return(vg); }