internal void AddBeachSection(FortuneSiteEvent siteEvent, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <VEdge> edges) { var site = siteEvent.Site; var x = site.X; var directrix = site.Y; RBTreeNode <BeachSection> leftSection = null; RBTreeNode <BeachSection> rightSection = null; var node = beachLine.Root; //find the parabola(s) above this site while (node != null && leftSection == null && rightSection == null) { var distanceLeft = LeftBreakpoint(node, directrix) - x; if (distanceLeft > 0) { //the new site is before the left breakpoint if (node.Left == null) { rightSection = node; } else { node = node.Left; } continue; } var distanceRight = x - RightBreakpoint(node, directrix); if (distanceRight > 0) { //the new site is after the right breakpoint if (node.Right == null) { leftSection = node; } else { node = node.Right; } continue; } //the point lies below the left breakpoint if (distanceLeft.ApproxEqual(0)) { leftSection = node.Previous; rightSection = node; continue; } //the point lies below the right breakpoint if (distanceRight.ApproxEqual(0)) { leftSection = node; rightSection = node.Next; continue; } // distance Right < 0 and distance Left < 0 // this section is above the new site leftSection = rightSection = node; } //our goal is to insert the new node between the //left and right sections var section = new BeachSection(site); //left section could be null, in which case this node is the first //in the tree var newSection = beachLine.InsertSuccessor(leftSection, section); //new beach section is the first beach section to be added if (leftSection == null && rightSection == null) { return; } //main case: //if both left section and right section point to the same valid arc //we need to split the arc into a left arc and a right arc with our //new arc sitting in the middle if (leftSection != null && leftSection == rightSection) { //if the arc has a circle event, it was a false alarm. //remove it if (leftSection.Data.CircleEvent != null) { deleted.Add(leftSection.Data.CircleEvent); leftSection.Data.CircleEvent = null; } //we leave the existing arc as the left section in the tree //however we need to insert the right section defined by the arc var copy = new BeachSection(leftSection.Data.Site); rightSection = beachLine.InsertSuccessor(newSection, copy); //grab the projection of this site onto the parabola var y = ParabolaMath.EvalParabola(leftSection.Data.Site.X, leftSection.Data.Site.Y, directrix, x); var intersection = new VPoint(x, y); //create the two half edges corresponding to this intersection var leftEdge = new VEdge(intersection, site, leftSection.Data.Site); var rightEdge = new VEdge(intersection, leftSection.Data.Site, site); leftEdge.Neighbor = rightEdge; //put the edge in the list edges.AddFirst(leftEdge); //store the left edge on each arc section newSection.Data.Edge = leftEdge; rightSection.Data.Edge = rightEdge; //store neighbors for delaunay leftSection.Data.Site.Neighbors.Add(newSection.Data.Site); newSection.Data.Site.Neighbors.Add(leftSection.Data.Site); //create circle events CheckCircle(leftSection, eventQueue); CheckCircle(rightSection, eventQueue); } //site is the last beach section on the beach line //this can only happen if all previous sites //had the same y value else if (leftSection != null && rightSection == null) { var start = new VPoint((leftSection.Data.Site.X + site.X) / 2, double.MinValue); var infEdge = new VEdge(start, leftSection.Data.Site, site); var newEdge = new VEdge(start, site, leftSection.Data.Site); newEdge.Neighbor = infEdge; edges.AddFirst(newEdge); leftSection.Data.Site.Neighbors.Add(newSection.Data.Site); newSection.Data.Site.Neighbors.Add(leftSection.Data.Site); newSection.Data.Edge = newEdge; //cant check circles since they are colinear } //site is directly above a break point else if (leftSection != null && leftSection != rightSection) { //remove false alarms if (leftSection.Data.CircleEvent != null) { deleted.Add(leftSection.Data.CircleEvent); leftSection.Data.CircleEvent = null; } if (rightSection.Data.CircleEvent != null) { deleted.Add(rightSection.Data.CircleEvent); rightSection.Data.CircleEvent = null; } //the breakpoint will dissapear if we add this site //which means we will create an edge //we treat this very similar to a circle event since //an edge is finishing at the center of the circle //created by circumscribing the left center and right //sites //bring a to the origin var leftSite = leftSection.Data.Site; var ax = leftSite.X; var ay = leftSite.Y; var bx = site.X - ax; var by = site.Y - ay; var rightSite = rightSection.Data.Site; var cx = rightSite.X - ax; var cy = rightSite.Y - ay; var d = bx * cy - by * cx; var magnitudeB = bx * bx + by * by; var magnitudeC = cx * cx + cy * cy; var vertex = new VPoint( (cy * magnitudeB - by * magnitudeC) / (2 * d) + ax, (bx * magnitudeC - cx * magnitudeB) / (2 * d) + ay); rightSection.Data.Edge.End = vertex; //next we create a two new edges newSection.Data.Edge = new VEdge(vertex, site, leftSection.Data.Site); rightSection.Data.Edge = new VEdge(vertex, rightSection.Data.Site, site); edges.AddFirst(newSection.Data.Edge); edges.AddFirst(rightSection.Data.Edge); //add neighbors for delaunay newSection.Data.Site.Neighbors.Add(leftSection.Data.Site); leftSection.Data.Site.Neighbors.Add(newSection.Data.Site); newSection.Data.Site.Neighbors.Add(rightSection.Data.Site); rightSection.Data.Site.Neighbors.Add(newSection.Data.Site); CheckCircle(leftSection, eventQueue); CheckCircle(rightSection, eventQueue); } }
private static void CheckCircle(RBTreeNode <BeachSection> section, MinHeap <FortuneEvent> eventQueue) { //if (section == null) // return; var left = section.Previous; var right = section.Next; if (left == null || right == null) { return; } var leftSite = left.Data.Site; var centerSite = section.Data.Site; var rightSite = right.Data.Site; //if the left arc and right arc are defined by the same //focus, the two arcs cannot converge if (leftSite == rightSite) { return; } // http://mathforum.org/library/drmath/view/55002.html // because every piece of this program needs to be demoed in maple >.< //MATH HACKS: place center at origin and //draw vectors a and c to //left and right respectively double bx = centerSite.X, by = centerSite.Y, ax = leftSite.X - bx, ay = leftSite.Y - by, cx = rightSite.X - bx, cy = rightSite.Y - by; //The center beach section can only dissapear when //the angle between a and c is negative var d = ax * cy - ay * cx; if (d.ApproxGreaterThanOrEqualTo(0)) { return; } var magnitudeA = ax * ax + ay * ay; var magnitudeC = cx * cx + cy * cy; var x = (cy * magnitudeA - ay * magnitudeC) / (2 * d); var y = (ax * magnitudeC - cx * magnitudeA) / (2 * d); //add back offset var ycenter = y + by; //y center is off var circleEvent = new FortuneCircleEvent( new VPoint(x + bx, ycenter + Math.Sqrt(x * x + y * y)), ycenter, section ); section.Data.CircleEvent = circleEvent; eventQueue.Insert(circleEvent); }
internal void RemoveBeachSection(FortuneCircleEvent circle, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <VEdge> edges) { var section = circle.ToDelete; var x = circle.X; var y = circle.YCenter; var vertex = new VPoint(x, y); //multiple edges could end here var toBeRemoved = new List <RBTreeNode <BeachSection> >(); //look left var prev = section.Previous; while (prev.Data.CircleEvent != null && (x - prev.Data.CircleEvent.X).ApproxEqual(0) && (y - prev.Data.CircleEvent.Y).ApproxEqual(0)) { toBeRemoved.Add(prev); prev = prev.Previous; } var next = section.Next; while (next.Data.CircleEvent != null && (x - next.Data.CircleEvent.X).ApproxEqual(0) && (y - next.Data.CircleEvent.Y).ApproxEqual(0)) { toBeRemoved.Add(next); next = next.Next; } section.Data.Edge.End = vertex; section.Next.Data.Edge.End = vertex; section.Data.CircleEvent = null; //odds are this double writes a few edges but this is clean... foreach (var remove in toBeRemoved) { remove.Data.Edge.End = vertex; remove.Next.Data.Edge.End = vertex; deleted.Add(remove.Data.CircleEvent); remove.Data.CircleEvent = null; } //need to delete all upcoming circle events with this node if (prev.Data.CircleEvent != null) { deleted.Add(prev.Data.CircleEvent); prev.Data.CircleEvent = null; } if (next.Data.CircleEvent != null) { deleted.Add(next.Data.CircleEvent); next.Data.CircleEvent = null; } //create a new edge with start point at the vertex and assign it to next var newEdge = new VEdge(vertex, next.Data.Site, prev.Data.Site); next.Data.Edge = newEdge; edges.AddFirst(newEdge); //add neighbors for delaunay prev.Data.Site.Neighbors.Add(next.Data.Site); next.Data.Site.Neighbors.Add(prev.Data.Site); //remove the sectionfrom the tree beachLine.RemoveNode(section); foreach (var remove in toBeRemoved) { beachLine.RemoveNode(remove); } CheckCircle(prev, eventQueue); CheckCircle(next, eventQueue); }