public static VoronoiGraph ComputeVoronoiGraph(IEnumerable Datapoints) { BinaryPriorityQueue PQ = new BinaryPriorityQueue(); Hashtable CurrentCircles = new Hashtable(); VoronoiGraph VG = new VoronoiGraph(); VNode RootNode = null; foreach(Vector V in Datapoints) { PQ.Push(new VDataEvent(V)); } while(PQ.Count>0) { VEvent VE = PQ.Pop() as VEvent; VDataNode[] CircleCheckList; 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,VE.Y,out CircleCheckList); } else throw new Exception("Got event of type "+VE.GetType().ToString()+"!"); foreach(VDataNode VD in CircleCheckList) { if(CurrentCircles.ContainsKey(VD)) { ((VCircleEvent)CurrentCircles[VD]).Valid=false; CurrentCircles.Remove(VD); } VCircleEvent VCE = VNode.CircleCheckDataNode(VD,VE.Y); if(VCE!=null) { PQ.Push(VCE); CurrentCircles[VD]=VCE; } } if(VE is VDataEvent) { Vector DP = ((VDataEvent)VE).DataPoint; foreach(VCircleEvent VCE in CurrentCircles.Values) { if(MathTools.Dist(DP[0],DP[1],VCE.Center[0],VCE.Center[1])<VCE.Y-VCE.Center[1] && Math.Abs(MathTools.Dist(DP[0],DP[1],VCE.Center[0],VCE.Center[1])-(VCE.Y-VCE.Center[1]))>1e-10) VCE.Valid = false; } } } VNode.CleanUpTree(RootNode); foreach(VoronoiEdge VE in VG.Edges) { if(VE.Done) continue; if(VE.VVertexB == Fortune.VVUnkown) { VE.AddVertex(Fortune.VVInfinite); if(Math.Abs(VE.LeftData[1]-VE.RightData[1])<1e-10 && VE.LeftData[0]<VE.RightData[0]) { Vector 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)) { 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; }
public static VNode ProcessCircleEvent(VCircleEvent e, VNode Root, VoronoiGraph VG, double ys, out VDataNode[] CircleCheckList) { VDataNode a,b,c; VEdgeNode eu,eo; b = e.NodeN; a = VNode.LeftDataNode(b); c = VNode.RightDataNode(b); if(a==null || b.Parent==null || c==null || !a.DataPoint.Equals(e.NodeL.DataPoint) || !c.DataPoint.Equals(e.NodeR.DataPoint)) { CircleCheckList = new VDataNode[]{}; return Root; // Abbruch da sich der Graph verändert hat } eu = (VEdgeNode)b.Parent; CircleCheckList = new VDataNode[] {a,c}; //1. Create the new Vertex Vector VNew = new Vector(e.Center[0],e.Center[1]); // VNew[0] = Fortune.ParabolicCut(a.DataPoint[0],a.DataPoint[1],c.DataPoint[0],c.DataPoint[1],ys); // VNew[1] = (ys + a.DataPoint[1])/2 - 1/(2*(ys-a.DataPoint[1]))*(VNew[0]-a.DataPoint[0])*(VNew[0]-a.DataPoint[0]); VG.Vertizes.Add(VNew); //2. Find out if a or c are in a distand part of the tree (the other is then b's sibling) and assign the new vertex if(eu.Left==b) // c is sibling { eo = VNode.EdgeToRightDataNode(a); // replace eu by eu's Right eu.Parent.Replace(eu,eu.Right); } else // a is sibling { eo = VNode.EdgeToRightDataNode(b); // replace eu by eu's Left eu.Parent.Replace(eu,eu.Left); } eu.Edge.AddVertex(VNew); // ///////////////////// uncertain // if(eo==eu) // return Root; // ///////////////////// //complete & cleanup eo eo.Edge.AddVertex(VNew); //while(eo.Edge.VVertexB == Fortune.VVUnkown) //{ // eo.Flipped = !eo.Flipped; // eo.Edge.AddVertex(Fortune.VVInfinite); //} //if(eo.Flipped) //{ // Vector T = eo.Edge.LeftData; // eo.Edge.LeftData = eo.Edge.RightData; // eo.Edge.RightData = T; //} //2. Replace eo by new Edge VoronoiEdge VE = new VoronoiEdge(); VE.LeftData = a.DataPoint; VE.RightData = c.DataPoint; VE.AddVertex(VNew); VG.Edges.Add(VE); VEdgeNode VEN = new VEdgeNode(VE, false); VEN.Left = eo.Left; VEN.Right = eo.Right; if(eo.Parent == null) return VEN; eo.Parent.Replace(eo,VEN); return Root; }