internal void AddBeachSection(FortuneSiteEvent siteEvent, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <VoronoiEdge> 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 Point(x, y); //create the two half edges corresponding to this intersection var leftEdge = new VoronoiEdge(intersection, site, leftSection.Data.Site); var rightEdge = new VoronoiEdge(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.Neighbours.Add(newSection.Data.Site); newSection.Data.Site.Neighbours.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 Point((leftSection.Data.Site.X + site.X) / 2, double.MinValue); var infEdge = new VoronoiEdge(start, leftSection.Data.Site, site); var newEdge = new VoronoiEdge(start, site, leftSection.Data.Site); newEdge.Neighbor = infEdge; edges.AddFirst(newEdge); leftSection.Data.Site.Neighbours.Add(newSection.Data.Site); newSection.Data.Site.Neighbours.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 Point( (cy * magnitudeB - by * magnitudeC) / (2 * d) + ax, (bx * magnitudeC - cx * magnitudeB) / (2 * d) + ay); rightSection.Data.Edge.EndPoint = vertex; //next we create a two new edges newSection.Data.Edge = new VoronoiEdge(vertex, site, leftSection.Data.Site); rightSection.Data.Edge = new VoronoiEdge(vertex, rightSection.Data.Site, site); edges.AddFirst(newSection.Data.Edge); edges.AddFirst(rightSection.Data.Edge); //add neighbors for delaunay newSection.Data.Site.Neighbours.Add(leftSection.Data.Site); leftSection.Data.Site.Neighbours.Add(newSection.Data.Site); newSection.Data.Site.Neighbours.Add(rightSection.Data.Site); rightSection.Data.Site.Neighbours.Add(newSection.Data.Site); CheckCircle(leftSection, eventQueue); CheckCircle(rightSection, eventQueue); } }
public void AddSection(FortuneSiteEvent siteEvent, MinHeap <FortuneEvent> eventQueue, HashSet <FortuneCircleEvent> deleted, LinkedList <Edge> edges) { FortuneSite site = siteEvent.site; double x = site.x; double directrix = site.y; RedBlackTreeNode <BeachSection> leftSection = null; RedBlackTreeNode <BeachSection> rightSection = null; RedBlackTreeNode <BeachSection> root = sections.root; while (root != null && leftSection != null && rightSection != null) { double distanceLeft = LeftBreakpoint(root, directrix) - x; if (distanceLeft > 0) { if (root.left == null) { rightSection = root; } else { root = root.left; } continue; } double distanceRight = x - RightBreakpoint(root, directrix); if (distanceRight > 0) { if (root.right == null) { leftSection = root; } root = root.right; continue; } if (distanceLeft.ApproxEqual(0)) { leftSection = root.previous; rightSection = root; continue; } if (distanceRight.ApproxEqual(0)) { leftSection = root; rightSection = root.next; continue; } leftSection = rightSection = root; } BeachSection section = new BeachSection(site); RedBlackTreeNode <BeachSection> newSectionNode = sections.Insert(leftSection, section); if (leftSection == null && rightSection == null) { return; } if (leftSection != null && leftSection == rightSection) { if (leftSection.data.circleEvent != null) { deleted.Add(leftSection.data.circleEvent); leftSection.data.circleEvent = null; } BeachSection copy = new BeachSection(leftSection.data.site); rightSection = sections.Insert(newSectionNode, copy); double y = ParabolaMath.EvalParabola(leftSection.data.site.x, leftSection.data.site.y, directrix, x); Point intersection = new Point(x, y); Edge leftEdge = new Edge(intersection, site, leftSection.data.site); Edge rightEdge = new Edge(intersection, leftSection.data.site, site); leftEdge.neighbour = rightEdge; edges.AddFirst(leftEdge); newSectionNode.data.edge = leftEdge; rightSection.data.edge = rightEdge; leftSection.data.site.neighbours.Add(newSectionNode.data.site); newSectionNode.data.site.neighbours.Add(leftSection.data.site); CheckCircle(leftSection, eventQueue); CheckCircle(rightSection, eventQueue); } else if (leftSection != null && rightSection != null) { Point start = new Point((leftSection.data.site.x + site.x) / 2, double.MinValue); Edge infiniteEdge = new Edge(start, leftSection.data.site, site); Edge newEdge = new Edge(start, site, leftSection.data.site); newEdge.neighbour = infiniteEdge; edges.AddFirst(newEdge); leftSection.data.site.neighbours.Add(newSectionNode.data.site); newSectionNode.data.site.neighbours.Add(leftSection.data.site); newSectionNode.data.edge = newEdge; } else if (leftSection != null && leftSection != rightSection) { 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; } FortuneSite leftSite = leftSection.data.site; double aX = leftSite.x; double aY = leftSite.y; double bX = site.x - aX; double bY = site.y - aY; FortuneSite rightSite = rightSection.data.site; double cX = rightSite.x - aX; double cY = rightSite.y - aY; double delta = bX * cY - bY * cX; double magnitudeB = bX * bX + bY * bY; double magnitudeC = cX * cX + cY * cY; Point vertex = new Point( (cY * magnitudeB - bY * magnitudeC) / (2 * delta) + aX, (bX * magnitudeC - cX * magnitudeB) / (2 * delta) + aY ); rightSection.data.edge.end = vertex; newSectionNode.data.edge = new Edge(vertex, site, leftSection.data.site); rightSection.data.edge = new Edge(vertex, rightSection.data.site, site); edges.AddFirst(newSectionNode.data.edge); edges.AddFirst(rightSection.data.edge); newSectionNode.data.site.neighbours.Add(leftSection.data.site); leftSection.data.site.neighbours.Add(newSectionNode.data.site); newSectionNode.data.site.neighbours.Add(rightSection.data.site); rightSection.data.site.neighbours.Add(newSectionNode.data.site); CheckCircle(leftSection, eventQueue); CheckCircle(rightSection, eventQueue); } }