public void Reset() { if (!this.beachLine) { this.beachLine = new RBTree <BeachSection>(); } // Move leftover beachsections to the beachsection junkyard. if (this.beachLine.Root) { BeachSection beachSection = this.beachLine.GetFirst(this.beachLine.Root); while (beachSection) { this.beachSectionJunkyard.Add(beachSection); beachSection = beachSection.Next; } } this.beachLine.Root = null; if (!this.circleEvents) { this.circleEvents = new RBTree <CircleEvent>(); } this.circleEvents.Root = this.firstCircleEvent = null; this.edges = new List <Edge>(); this.cells = new List <Cell>(); }
private void ProcessCircleEvent(CircleEvent circleEvent) { var leftHalfEdge = circleEvent.Left.PartialLine; if (leftHalfEdge.Face.Equals(circleEvent.Left.Point)) { leftHalfEdge = leftHalfEdge.Twin; } var centerHalfEdge = circleEvent.Center.PartialLine; if (centerHalfEdge.Face.Equals(circleEvent.Right.Point)) { centerHalfEdge = centerHalfEdge.Twin; } var rc = centerHalfEdge.Twin; rc.CircleEvent = circleEvent; leftHalfEdge.CircleEvent = circleEvent; circleEvent.PartialLine = centerHalfEdge; var prev = circleEvent.CenterLeaf.GetPreviousLeaf(); var next = circleEvent.CenterLeaf.GetNextLeaf(); if (prev?.DisappearEvent != null) { PriorityQueue.Delete(prev.DisappearEvent.Handle); prev.DisappearEvent = null; } if (next?.DisappearEvent != null) { PriorityQueue.Delete(next.DisappearEvent.Handle); next.DisappearEvent = null; } var newCircles = Events.RemoveNode(circleEvent, prev, circleEvent.CenterLeaf, next); if (newCircles == null) { return; } foreach (var ce in newCircles) { C5.IPriorityQueueHandle <IEvent> h = null; PriorityQueue.Add(ref h, ce); ce.Handle = h; } }
public void DetachCircleEvent(BeachSection arc) { CircleEvent circle = arc.circleEvent; if (circle) { if (!circle.Prev) { this.firstCircleEvent = circle.Next; } this.circleEvents.Remove(circle); // remove from RB-tree this.circleEventJunkyard.Add(circle); arc.circleEvent = null; } }
public VoronoiGraph Compute(List <Point> sites, BoundingRect bbox) { this.Reset(); // Initialize site event queue var siteEvents = sites.Take(sites.Count).ToList(); /*siteEvents.Sort((a, b) => * { * float r = b.y - a.y; * if (r != 0) { return Mathf.CeilToInt(r); } * return Mathf.CeilToInt(b.x - a.x); * });*/ siteEvents = siteEvents.OrderByDescending(s => s.y).ToList(); // process queue Point site = siteEvents.Last(); siteEvents.Remove(site); int siteid = 0; float xsitex = -Mathf.Infinity; float xsitey = -Mathf.Infinity; // main loop for (; ;) { // we need to figure whether we handle a site or circle event // for this we find out if there is a site event and it is // 'earlier' than the circle event CircleEvent circle = this.firstCircleEvent; // add beach section if (site && (!circle || site.y < circle.y || (site.y == circle.y && site.x < circle.x))) { // only if site is not a duplicate if (site.x != xsitex || site.y != xsitey) { // first create cell for new site //this.cells[siteid] = new Cell(site); this.cells.Insert(siteid, new Cell(site)); site.id = siteid++; // then create a beachsection for that site this.AddBeachSection(site); // remember last site coords to detect duplicate xsitey = site.y; xsitex = site.x; } site = siteEvents.Count > 0 ? siteEvents.Last() : null; if (siteEvents.Count > 0) { siteEvents.Remove(site); } } // remove beach section else if (circle) { this.RemoveBeachSection(circle.arc); } // all done, quit else { break; } } // wrapping-up this.ClipEdges(bbox); this.CloseCells(bbox); VoronoiGraph graph = new VoronoiGraph(); graph.sites = sites; graph.cells = this.cells; graph.edges = this.edges; this.Reset(); return(graph); }
public void AttachCircleEvent(BeachSection arc) { BeachSection lArc = arc.Prev; BeachSection rArc = arc.Next; if (!lArc || !rArc) { return; } // does that ever happen? Point lSite = lArc.site; Point cSite = arc.site; Point rSite = rArc.site; // If site of Left beachsection is same as site of // Right beachsection, there can't be convergence if (lSite == rSite) { return; } // Find the circumscribed circle for the three sites associated // with the beachsection triplet. // rhill 2011-05-26: It is more efficient to calculate in-place // rather than getting the resulting circumscribed circle from an // object returned by calling Voronoi.circumcircle() // http://mathforum.org/library/drmath/view/55002.html // Except that I bring the origin at cSite to simplify calculations. // The bottom-most part of the circumcircle is our Fortune 'circle // event', and its center is a vertex potentially part of the final // Voronoi diagram. float bx = cSite.x; float by = cSite.y; float ax = lSite.x - bx; float ay = lSite.y - by; float cx = rSite.x - bx; float cy = rSite.y - by; // If points l.c.r are clockwise, then center beach section does not // collapse, hence it can't end up as a vertex (we reuse 'd' here, which // sign is reverse of the orientation, hence we reverse the test. // http://en.wikipedia.org/wiki/Curveorientation#Orientationofasimplepolygon // rhill 2011-05-21: Nasty finite precision error which caused circumcircle() to // return infinites: 1e-12 seems to fix the problem. float d = 2 * (ax * cy - ay * cx); if (d >= -2e-12) { return; } float ha = ax * ax + ay * ay; float hc = cx * cx + cy * cy; float x = (cy * ha - ay * hc) / d; float y = (ax * hc - cx * ha) / d; float ycenter = y + by; // Important: ybottom should always be under or at sweep, so no need // to waste CPU cycles by checking // recycle circle event object if possible CircleEvent circleEvent = this.circleEventJunkyard.Count > 0 ? this.circleEventJunkyard.Last() : null; this.circleEventJunkyard.Remove(circleEvent); if (!circleEvent) { circleEvent = new CircleEvent(); } circleEvent.arc = arc; circleEvent.site = cSite; circleEvent.x = x + bx; circleEvent.y = ycenter + Mathf.Sqrt(x * x + y * y); // y bottom circleEvent.yCenter = ycenter; arc.circleEvent = circleEvent; // find insertion point in RB-tree: circle events are ordered from // smallest to largest CircleEvent predecessor = null; CircleEvent node = this.circleEvents.Root; while (node) { if (circleEvent.y < node.y || (circleEvent.y == node.y && circleEvent.x <= node.x)) { if (node.Left) { node = node.Left; } else { predecessor = node.Prev; break; } } else { if (node.Right) { node = node.Right; } else { predecessor = node; break; } } } this.circleEvents.Insert(predecessor, circleEvent); if (!predecessor) { this.firstCircleEvent = circleEvent; } }
public void RemoveBeachSection(BeachSection beachSection) { CircleEvent circle = beachSection.circleEvent; float x = circle.x; float y = circle.yCenter; Point vertex = new Point(x, y); BeachSection previous = beachSection.Prev; BeachSection next = beachSection.Next; LinkedList <BeachSection> disappearingTransitions = new LinkedList <BeachSection>(); disappearingTransitions.AddLast(beachSection); // remove collapsed beachsection from beachline this.DetachBeachSection(beachSection); // there could be more than one empty arc at the deletion point, this // happens when more than two edges are linked by the same vertex, // so we will collect all those edges by looking up both sides of // the deletion point. // by the way, there is *always* a predecessor/successor to any collapsed // beach section, it's just impossible to have a collapsing first/last // beach sections on the beachline, since they obviously are unconstrained // on their left/right side. // look left BeachSection lArc = previous; while (lArc.circleEvent && Mathf.Abs(x - lArc.circleEvent.x) < EPSILON && Mathf.Abs(y - lArc.circleEvent.yCenter) < EPSILON) { previous = lArc.Prev; disappearingTransitions.AddFirst(lArc); this.DetachBeachSection(lArc); // mark for reuse lArc = previous; } // even though it is not disappearing, I will also add the beach section // immediately to the left of the left-most collapsed beach section, for // convenience, since we need to refer to it later as this beach section // is the 'left' site of an edge for which a start point is set. disappearingTransitions.AddFirst(lArc); this.DetachCircleEvent(lArc); // look right BeachSection rArc = next; while (rArc.circleEvent && Mathf.Abs(x - rArc.circleEvent.x) < EPSILON && Mathf.Abs(y - rArc.circleEvent.yCenter) < EPSILON) { next = rArc.Next; disappearingTransitions.AddLast(rArc); this.DetachBeachSection(rArc); rArc = next; } // we also have to add the beach section immediately to the right of the // right-most collapsed beach section, since there is also a disappearing // transition representing an edge's start point on its left. disappearingTransitions.AddLast(rArc); this.DetachCircleEvent(rArc); // walk through all the disappearing transitions between beach sections and // set the start point of their (implied) edge. int nArcs = disappearingTransitions.Count; for (int iArc = 1; iArc < nArcs; iArc++) { rArc = disappearingTransitions.ElementAt(iArc); lArc = disappearingTransitions.ElementAt(iArc - 1); rArc.edge.SetStartPoint(lArc.site, rArc.site, vertex); } // create a new edge as we have now a new transition between // two beach sections which were previously not adjacent. // since this edge appears as a new vertex is defined, the vertex // actually define an end point of the edge (relative to the site // on the left) lArc = disappearingTransitions.ElementAt(0); rArc = disappearingTransitions.ElementAt(nArcs - 1); rArc.edge = this.CreateEdge(lArc.site, rArc.site, null, vertex); // create circle events if any for beach sections left in the beachline // adjacent to collapsed sections this.AttachCircleEvent(lArc); this.AttachCircleEvent(rArc); }
private void FinalizeDiagram(IBinaryNode node) { switch (node) { case Leaf leafNode when leafNode.BreakpointNode == null: return; case Leaf leafNode: { var breakpoint = leafNode.BreakpointNode.Breakpoint; var face = breakpoint.LeftListEvent.PartialLine.Face; var twinFace = breakpoint.LeftListEvent.PartialLine.Twin.Face; var centerX = 0.5f * (breakpoint.LeftListEvent.PartialLine.Face.X + breakpoint.LeftListEvent.PartialLine.Twin.Face.X); var centerY = 0.5f * (breakpoint.LeftListEvent.PartialLine.Face.Y + breakpoint.LeftListEvent.PartialLine.Twin.Face.Y); if (Math.Abs(face.Y - twinFace.Y) < EPSILON_DOUBLE) { var halfEdge = breakpoint.LeftListEvent.PartialLine; var circleEvent = new CircleEvent(centerX, -MaximumEdgeLimit); if (halfEdge.CircleEvent == null) { halfEdge.CircleEvent = circleEvent; circleEvent.PartialLine = halfEdge; } else { halfEdge.Twin.CircleEvent = circleEvent; circleEvent.PartialLine = halfEdge.Twin; } } else { var grad = -(twinFace.X - face.X) / (twinFace.Y - face.Y); var constant = centerY - grad * centerX; var bpx = breakpoint.X - centerX; var bpy = breakpoint.Y - centerY; var testX = centerX + 10000; var testY = testX * grad + constant; if (testX * bpx + testY * bpy <= 0) { testX = centerX - 10000; testY = (centerX - 10000) * grad + constant; } var circleEvent = new CircleEvent(testX, testY); var halfEdge = breakpoint.LeftListEvent.PartialLine; if (!halfEdge.Face.Equals(breakpoint.LeftListEvent.Point)) { halfEdge = halfEdge.Twin; } if (halfEdge.CircleEvent == null) { halfEdge.CircleEvent = circleEvent; } else if (halfEdge.Twin.CircleEvent == null) { halfEdge.Twin.CircleEvent = circleEvent; } } break; } case BreakpointNode breakpointNode: { breakpointNode.Breakpoint.InitializeBreakpoint(MaximumEdgeLimit); if (node.Left != null) { FinalizeDiagram(node.Left); } if (node.Right != null) { FinalizeDiagram(node.Right); } break; } } }
public IBinaryNode GetNearest(Builder builder, IBinaryNode current, SeedEvent seedEvent, double edgeLimit) { while (true) { var x = seedEvent.X; var y = seedEvent.Y; if ((int)y == (int)MaxY) { var le = new TreeItem(seedEvent.Vertex); var evt = new Leaf(le); var next = Root; while (next.GetNextLeaf() != null) { next = next.GetNextLeaf(); } if (!(next is Leaf nextLeaf)) { return(null); } var h1 = new PartialLine(nextLeaf.ListItem.Point); var h2 = new PartialLine(le.Point); h1.Twin = h2; h2.Twin = h1; PartialLines.Add(h1); PartialLines.Add(h2); if (nextLeaf.ListItem.Point.PartialLine == null) { nextLeaf.ListItem.Point.PartialLine = h1; } else { var n = nextLeaf.ListItem.Point.PartialLine; while (n.Next != null) { n = n.Next; } n.Next = h1; } nextLeaf.ListItem.PartialLine = h1; le.PartialLine = h1; le.Point.PartialLine = h2; var b0 = new Breakpoint(nextLeaf.ListItem, evt.ListItem); var bpNode0 = new BreakpointNode(b0); nextLeaf.BreakpointNode = bpNode0; if (next == Root) { next.Parent = bpNode0; Root = bpNode0; bpNode0.Left = next; bpNode0.Right = evt; evt.Parent = bpNode0; } else { var parent = next.Parent; parent.Right = bpNode0; bpNode0.Parent = parent; bpNode0.Left = next; next.Parent = bpNode0; bpNode0.Right = evt; evt.Parent = bpNode0; } b0.InitializeBreakpoint(y); b0.X = (x + next.X) / 2; b0.Y = y + edgeLimit; var ce = new CircleEvent(b0.X, b0.Y); h1.CircleEvent = ce; ce.PartialLine = h1; return(null); } current.CalculateInnerBreakpoint(y - 0.0001); if (Math.Abs(x - current.X) < EPSILON_DOUBLE) { return(current); } var child = x > current.X ? current.Right : current.Left; if (child is null) { return(current); } current = child; } }
public List <CircleEvent> RemoveNode(CircleEvent ce, Leaf prev, Leaf remove, Leaf next) { var circleEvents = new List <CircleEvent>(); var currentVector2 = prev.ListItem.Point; var newVector2 = next.ListItem.Point; var h1 = new PartialLine(currentVector2); var h2 = new PartialLine(newVector2); h1.Twin = h2; h2.Twin = h1; PartialLines.Add(h1); PartialLines.Add(h2); h1.CircleEvent = ce; var bp1 = new Breakpoint(prev.ListItem, next.ListItem); if (prev.BreakpointNode == remove.Parent) { prev.BreakpointNode = remove.BreakpointNode; } prev.BreakpointNode.Breakpoint = bp1; { var oldEdgeP = prev.ListItem.PartialLine; if (!oldEdgeP.Face.Equals(prev.ListItem.Point)) { oldEdgeP = oldEdgeP.Twin; } var n = oldEdgeP.Next; oldEdgeP.Next = h1; h1.Previous = oldEdgeP; if (n != null) { h1.Next = n; n.Previous = h1; } } { var oldEdgeN = next.ListItem.PartialLine; if (!oldEdgeN.Face.Equals(next.ListItem.Point)) { oldEdgeN = oldEdgeN.Twin; } var n = oldEdgeN.Next; oldEdgeN.Next = h2; h2.Previous = oldEdgeN; if (n != null) { h2.Next = n; n.Previous = h2; } } prev.ListItem.PartialLine = h1; if (remove.Parent.Parent == null) { Root = remove.Parent.Left == remove ? remove.Parent.Right : remove.Parent.Left; Root.Parent = null; } else { var grandparent = remove.Parent.Parent; var parent = remove.Parent; if (remove == remove.Parent.Left) { if (remove.Parent == grandparent.Left) { grandparent.Left = parent.Right; parent.Right.Parent = grandparent; } else { grandparent.Right = parent.Right; parent.Right.Parent = grandparent; } } else { if (remove.Parent == grandparent.Left) { grandparent.Left = parent.Left; parent.Left.Parent = grandparent; } else { grandparent.Right = parent.Left; parent.Left.Parent = grandparent; } } remove.Parent = null; } var nextNextTree = next.GetNextLeaf(); if (nextNextTree != null && nextNextTree is Leaf nextNext) { var circleEvent = new CircleEvent(prev, next, nextNext); var canCalculateCircle = circleEvent.CalculateCircle(); next.BreakpointNode.Breakpoint = new Breakpoint(next.ListItem, nextNext.ListItem); bp1.InitializeBreakpoint(circleEvent.DistanceFromLine); next.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); if (canCalculateCircle && circleEvent.CheckConvergence()) { next.DisappearEvent = circleEvent; circleEvents.Add(circleEvent); } } var prevPrevTree = prev.GetPreviousLeaf(); if (prevPrevTree != null && prevPrevTree is Leaf prevPrev) { var circleEvent = new CircleEvent(prevPrev, prev, next); var canCalculateCircle = circleEvent.CalculateCircle(); prevPrev.BreakpointNode.Breakpoint = new Breakpoint(prevPrev.ListItem, prev.ListItem); bp1.InitializeBreakpoint(circleEvent.DistanceFromLine); prevPrev.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); if (canCalculateCircle && circleEvent.CheckConvergence()) { prev.DisappearEvent = circleEvent; circleEvents.Add(circleEvent); } } return(circleEvents); }
public List <CircleEvent> AddSeedEvent(Leaf current, TreeItem listItem) { var circleEvents = new List <CircleEvent>(); var currentVector2 = current.ListItem.Point; var newVector2 = listItem.Point; var h1 = new PartialLine(currentVector2); var h2 = new PartialLine(newVector2); h1.Twin = h2; h2.Twin = h1; PartialLines.Add(h1); PartialLines.Add(h2); if (currentVector2.PartialLine == null) { currentVector2.PartialLine = h1; } newVector2.PartialLine = h2; var oldHe = current.ListItem.PartialLine; current.ListItem.PartialLine = h1; listItem.PartialLine = h1; var prev = current.GetPreviousLeaf(); var next = current.GetNextLeaf(); var newNode = new Leaf(listItem); var copy = current.ListItem.Copy(); var copyNode = new Leaf(copy); var bp1 = new Breakpoint(current.ListItem, listItem); bp1.InitializeBreakpoint(listItem.Point.Y - 1); var bp2 = new Breakpoint(listItem, copy); bp2.InitializeBreakpoint(listItem.Point.Y - 1); var bp1Node = new BreakpointNode(bp1); var bp2Node = new BreakpointNode(bp2); current.BreakpointNode = bp1Node; newNode.BreakpointNode = bp2Node; if (current.Parent != null) { if (current == current.Parent.Left) { current.Parent.Left = bp1Node; bp1Node.Parent = current.Parent; } else { current.Parent.Right = bp1Node; bp1Node.Parent = current.Parent; } } else { SetRoot(bp1Node); Root.Parent = null; } bp1Node.Left = current; current.Parent = bp1Node; bp1Node.Right = bp2Node; bp2Node.Parent = bp1Node; bp2Node.Left = newNode; newNode.Parent = bp2Node; bp2Node.Right = copyNode; copyNode.Parent = bp2Node; if (oldHe != null) { copyNode.ListItem.PartialLine = oldHe; var pre = copyNode.ListItem.PartialLine; if (!pre.Face.Equals(currentVector2)) { pre = pre.Twin; } var onext = pre.Next; pre.Next = h1; h1.Previous = pre; if (onext != null) { onext.Previous = h1; h1.Next = onext; } } if (prev != null && prev is Leaf prevLeaf) { var circleEvent = new CircleEvent(prevLeaf, current, newNode); var canCalculateCircle = circleEvent.CalculateCircle(); prevLeaf.BreakpointNode.Breakpoint = new Breakpoint(prevLeaf.ListItem, current.ListItem); bp1Node.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); prevLeaf.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); if (canCalculateCircle && circleEvent.CheckConvergence()) { current.DisappearEvent = circleEvent; circleEvents.Add(circleEvent); } } if (next != null && next is Leaf nextLeaf) { var circleEvent = new CircleEvent(newNode, copyNode, nextLeaf); var canCalculateCircle = circleEvent.CalculateCircle(); copyNode.BreakpointNode = (BreakpointNode)copyNode.GetNext(); copyNode.BreakpointNode.Breakpoint = new Breakpoint(copy, nextLeaf.ListItem); bp2Node.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); copyNode.BreakpointNode.Breakpoint.InitializeBreakpoint(circleEvent.DistanceFromLine); if (canCalculateCircle && circleEvent.CheckConvergence()) { copyNode.DisappearEvent = circleEvent; circleEvents.Add(circleEvent); } } else { copyNode.ListItem.PartialLine = h1; } return(circleEvents); }
void ProcessCircleEvent(FortunesAlgorithmState state, CircleEvent e) { var sweepY = e.Y; var swallowedParabolaNode = e.SwallowedParabolaNode; if (swallowedParabolaNode.Parent == null) { return; } Node leftAncestor, rightAncestor; swallowedParabolaNode.FindDirectionalAncestors(out leftAncestor, out rightAncestor); var leftAncestorEdgeRayData = (EdgeRayData)leftAncestor.Data; var rightAncestorEdgeRayData = (EdgeRayData)rightAncestor.Data; state.VoronoiEdges.Add(new VoronoiEdge( leftAncestorEdgeRayData.Origin, leftAncestorEdgeRayData.IsOriginBounded, e.Circumcenter, true)); state.VoronoiEdges.Add(new VoronoiEdge( rightAncestorEdgeRayData.Origin, leftAncestorEdgeRayData.IsOriginBounded, e.Circumcenter, true)); var leftParabolaNode = leftAncestor.Left.GetRightmost(); var leftParabolaFocus = ((ParabolaData)leftParabolaNode.Data).Focus; var rightParabolaNode = rightAncestor.Right.GetLeftmost(); var rightParabolaFocus = ((ParabolaData)rightParabolaNode.Data).Focus; state.DelanayEdges.Add(new VoronoiEdge(leftParabolaFocus, true, rightParabolaFocus, true)); // One ancestor will become the edge shared by the left/right parabolas, // the other will be deleted. It's guranteed our parent is one of these // ancestors and that the other ancestor is above it (that is, it has a // parent) so opt to delete our parent. var nodeParent = swallowedParabolaNode.Parent; var nodeSibling = nodeParent.Left == swallowedParabolaNode ? nodeParent.Right : nodeParent.Left; NodeOperations.ReplaceNode(nodeParent, nodeSibling, ref state.Root); nodeParent.Left = nodeParent.Right = null; var olderAncestor = nodeParent == leftAncestor ? rightAncestor : leftAncestor; var leftFocusRightFocus = rightParabolaFocus - leftParabolaFocus; // x is positive var edgeDirection = new TVector2(-leftFocusRightFocus.Y, leftFocusRightFocus.X); var leftRightFocusCenter = new TVector2( MathUtil.Average(leftParabolaFocus.X, rightParabolaFocus.X), MathUtil.Average(leftParabolaFocus.Y, rightParabolaFocus.Y)); var dy = sweepY - leftRightFocusCenter.Y; var edgeStart = e.Circumcenter; olderAncestor.Data = new EdgeRayData(true, edgeStart, edgeDirection); // add new potential circle events Node leftLeftLeaf; if (leftParabolaNode.TryGetLeftLeaf(out leftLeftLeaf)) { HandleAddCircleEvent(state, leftLeftLeaf, leftParabolaNode, rightParabolaNode, sweepY); } Node rightRightLeaf; if (rightParabolaNode.TryGetRightLeaf(out rightRightLeaf)) { HandleAddCircleEvent(state, leftParabolaNode, rightParabolaNode, rightRightLeaf, sweepY); } }