public FortuneArc SplitArc(FortuneArc splittingArc, FortuneArc splitter) { FortuneArc left = new FortuneArc(); left.Site = splittingArc.Site; FortuneArc right = new FortuneArc(); right.Site = splittingArc.Site; // create a new edge VoronoiEdge edge = new VoronoiEdge(splittingArc.Site.Cell, splitter.Site.Cell); // both nodes of the edge are initially points at infinity, // but can become final after the event range double x = splitter.Site.Cell.DataPoint.X; double y = splitter.Site.Cell.DataPoint.Y; double distance = getDistanceToArc(splittingArc.Site.Cell.DataPoint, y, x); edge.Node1 = new VoronoiNode(x, y - distance); edge.Node1.IsInfinit = true; edge.Node2 = new VoronoiNode(x, y - distance); edge.Node2.IsInfinit = true; // add this edge in both cell diagram splitter.Site.Cell.AddEdge(edge); splittingArc.Site.Cell.AddEdge(edge); //add nodes to the arcs of the ribs splitter.LeftNode = edge.Node1; splitter.RightNode = edge.Node2; left.LeftNode = splittingArc.LeftNode; left.RightNode = edge.Node1; right.LeftNode = edge.Node2; right.RightNode = splittingArc.RightNode; left.Left = splittingArc.Left; right.Right = splittingArc.Right; splitter.Left = left; splitter.Right = right; if (splittingArc.Parent != null) { if (splittingArc.Parent.Left == splittingArc) { splittingArc.Parent.Left = splitter; } else if (splittingArc.Parent.Right == splittingArc) { splittingArc.Parent.Right = splitter; } } else { Root = splitter; } return(splitter); }
/// <summary> /// Sets the directions of dangles for infinite edges. /// </summary> /// <param name="rectangle">Bounding rectangle of the tessellation nodes</param> /// <param name="startVerticalNodes">The initial nodes of the infinit vertical edges, /// formed by points with the maximum Y coordinate</param> internal void Finish(BoundingRectangle rectangle, List <VoronoiNode> startVerticalNodes) { double l = rectangle.MinY - rectangle.Width - rectangle.Height; FortuneArc arc = Root; while (arc.Left != null) { arc = arc.Left; } while (arc != null) { FortuneArc rn = arc.RightNeighbor; if (rn != null) { ICoordinate[] points = getArcIntersections(arc.Site.Cell.DataPoint, rn.Site.Cell.DataPoint, 2 * l); if (points.Length == 2) { ICoordinate p1 = points[0].X < points[1].X ? points[0] : points[1]; ICoordinate p2 = points[0].X < points[1].X ? points[1] : points[0]; if (arc.RightNode != null && arc.RightNode.IsInfinit) { if (arc.Site.Cell.DataPoint.Y > rn.Site.Cell.DataPoint.Y) { arc.RightNode.Point = p1; } else { arc.RightNode.Point = p2; } } } if (points.Length == 1) { if (arc.RightNode != null && arc.RightNode.IsInfinit) { arc.RightNode.Point = points[0]; } } } arc = rn; } foreach (VoronoiNode node in startVerticalNodes) { node.Point = PlanimetryEnvironment.NewCoordinate(node.Point.X, rectangle.MaxY + rectangle.Height); } }
private FortuneEvent getCircleEvent(FortuneArc arc1, FortuneArc arc2, FortuneArc arc3, double y) { ICoordinate a = arc1.Site.Cell.DataPoint; ICoordinate b = arc2.Site.Cell.DataPoint; ICoordinate c = arc3.Site.Cell.DataPoint; //bc should turn to the right with respect to ab if ((b.X - a.X) * (c.Y - a.Y) - (c.X - a.X) * (b.Y - a.Y) > 0) { return(null); } ICoordinate point = getCircleCenter(a, b, c); if (point != null) { FortuneCircleEvent newEvent = new FortuneCircleEvent(); newEvent.CircleCenter = point; double distance = PlanimetryAlgorithms.Distance(point, a); point = PlanimetryEnvironment.NewCoordinate(point.X, point.Y - distance); newEvent.Point = point; newEvent.Arc = arc2; if (_buildTriangles) { newEvent.Triangle = new Triangle(arc1.Site.Cell, arc2.Site.Cell, arc3.Site.Cell); } return(newEvent); } return(null); }
/// <summary> /// Removes an arc from the shoreline. /// </summary> /// <param name="arc">An arc to remove</param> public void RemoveArc(FortuneArc arc) { removeArcRecursive(Root, arc); }
private bool removeArcRecursive(FortuneArc root, FortuneArc arc) { if (root == null) { return(false); } if (root == arc) { // no children if (arc.Left == null && arc.Right == null) { if (arc.Parent != null) { if (arc.Parent.Left == arc) { arc.Parent.Left = null; } else if (arc.Parent.Right == arc) { arc.Parent.Right = null; } } if (Root == arc) { Root = null; } return(true); } // one child if (arc.Left != null ^ arc.Right != null) { FortuneArc child = arc.Left != null ? arc.Left : arc.Right; if (arc.Parent != null) { if (arc.Parent.Left == arc) { arc.Parent.Left = child; } else if (arc.Parent.Right == arc) { arc.Parent.Right = child; } } else { Root = child; Root.Parent = null; } return(true); } // have both child FortuneArc rightLeft = root.Right; while (rightLeft.Left != null) { rightLeft = rightLeft.Left; } rightLeft.Left = arc.Left; if (arc.Parent != null) { if (arc.Parent.Left == arc) { arc.Parent.Left = arc.Right; } else if (arc.Parent.Right == arc) { arc.Parent.Right = arc.Right; } } if (Root == arc) { Root = arc.Right; Root.Parent = null; } return(true); } else { if (removeArcRecursive(root.Left, arc)) { return(true); } return(removeArcRecursive(root.Right, arc)); } }
/// <summary> /// Finds the arc of the shoreline, under which arose a point event. /// </summary> /// <param name="eventX">An X coordinate of event</param> /// <param name="ly">A Y coordinate of the sweepline</param> /// <returns>The arc finded or null</returns> public FortuneArc FindArc(double eventX, double ly) { if (Root == null) { return(null); } FortuneArc currentArc = Root; while (true) { if (currentArc.Left == null && currentArc.Right == null) { return(currentArc); } if (currentArc.Site.Cell.DataPoint.Y == ly) { currentArc = currentArc.Site.Cell.DataPoint.X < eventX ? currentArc.Right : currentArc.Left; continue; } //find the boundaries of the left and right subtrees FortuneArc leftNeighbor = currentArc.LeftSubtreeBound; FortuneArc rightNeighbor = currentArc.RightSubtreeBound; ICoordinate[] pointsPrev = null; ICoordinate[] pointsNext = null; if (leftNeighbor != null) { pointsPrev = getArcIntersections(leftNeighbor.Site.Cell.DataPoint, currentArc.Site.Cell.DataPoint, ly); if (pointsPrev.Length == 2) { if ((pointsPrev[1].X < pointsPrev[0].X ^ leftNeighbor.Site.Cell.DataPoint.Y < currentArc.Site.Cell.DataPoint.Y)) { pointsPrev[0] = pointsPrev[1]; } } } if (rightNeighbor != null) { pointsNext = getArcIntersections(rightNeighbor.Site.Cell.DataPoint, currentArc.Site.Cell.DataPoint, ly); if (pointsNext.Length == 2) { if ((pointsNext[1].X <pointsNext[0].X ^ rightNeighbor.Site.Cell.DataPoint.Y> currentArc.Site.Cell.DataPoint.Y)) { pointsNext[0] = pointsNext[1]; } } } if ((currentArc.Left == null || pointsPrev[0].X <= eventX) && (currentArc.Right == null || pointsNext[0].X >= eventX)) { return(currentArc); } if (leftNeighbor != null && pointsPrev[0].X > eventX) { currentArc = currentArc.Left; continue; } if (rightNeighbor != null && pointsNext[0].X < eventX) { currentArc = currentArc.Right; continue; } } }
/// <summary> /// Initializes a new instance of MapAround.Geometry.Tessellations.FortuneArc /// </summary> internal FortuneArc(FortuneArc left, FortuneArc right) { _left = left; _right = right; }
private void init(List <VoronoiCell> cells, int lastMaxYIndex) { _rectangle = new BoundingRectangle(); _eventList = new LinkedList <FortuneEvent>(); _startVerticalNodes = new List <VoronoiNode>(); // Where several points have the minimum ordinate requires a separate pre-treatment int skipEventCount = 0; if (lastMaxYIndex > 0) { VoronoiNode previousNode = null; FortuneArc currentArc = null; skipEventCount = lastMaxYIndex + 1; for (int i = 0; i <= lastMaxYIndex; i++) { // add an arc of coastline FortuneArc arc = new FortuneArc(); FortuneSite site = new FortuneSite(); site.Cell = cells[i]; arc.Site = site; if (currentArc != null) { currentArc.Right = arc; } currentArc = arc; if (_shoreLine.Root == null) { _shoreLine.Root = arc; } if (previousNode != null) { arc.LeftNode = previousNode; } // add the vertical edges of the Voronoi diagram if (i < lastMaxYIndex) { VoronoiEdge edge = new VoronoiEdge(cells[i], cells[i + 1]); double middleX = (cells[i].DataPoint.X + cells[i + 1].DataPoint.X) / 2; double middleY = cells[i].DataPoint.Y; edge.Node1 = new VoronoiNode(middleX, middleY); edge.Node1.IsInfinit = true; _startVerticalNodes.Add(edge.Node1); edge.Node2 = new VoronoiNode(middleX, middleY); edge.Node2.IsInfinit = true; previousNode = edge.Node2; arc.RightNode = edge.Node2; cells[i].AddEdge(edge); cells[i + 1].AddEdge(edge); } } } // fill all point events int j = 0; foreach (VoronoiCell cell in cells) { if (skipEventCount > j++) { continue; } FortunePointEvent ev = new FortunePointEvent(); ev.Point = cell.DataPoint; ev.Cell = cell; _eventList.AddLast(ev); } }
private void handleCircleEvent(FortuneCircleEvent ev) { FortuneArc ln = ev.Arc.LeftNeighbor; FortuneArc rn = ev.Arc.RightNeighbor; // remove events range associated with this arc if (ln.CircleEvent != null) { _eventList.Remove(ln.CircleEvent); } if (rn.CircleEvent != null) { _eventList.Remove(rn.CircleEvent); } // remove the arc of coastline _shoreLine.RemoveArc(ev.Arc); //fix slave nodes arc ribs if (ev.Arc.LeftNode != null) { ev.Arc.LeftNode.Point = ev.CircleCenter; ev.Arc.LeftNode.IsInfinit = false; } if (ev.Arc.RightNode != null) { ev.Arc.RightNode.Point = ev.CircleCenter; ev.Arc.RightNode.IsInfinit = false; } // add a new edge VoronoiEdge edge = new VoronoiEdge(ln.Site.Cell, rn.Site.Cell); edge.Node1 = new VoronoiNode(ev.CircleCenter.X, ev.CircleCenter.Y); edge.Node2 = new VoronoiNode((ln.Site.Cell.DataPoint.X + rn.Site.Cell.DataPoint.X) / 2, (ln.Site.Cell.DataPoint.Y + rn.Site.Cell.DataPoint.Y) / 2); //expand the bounding rectangle of the chart _rectangle.Join(ev.CircleCenter); // one node of the new edge is fixed, the second - no edge.Node1.IsInfinit = false; edge.Node2.IsInfinit = true; // dobavleyaem edge to cells ln.Site.Cell.AddEdge(edge); rn.Site.Cell.AddEdge(edge); //add a triangle in the Delaunay triangulation, if necessary if (_buildTriangles) { _triangles.Add(ev.Triangle); } // not a fixed node is a new edge now vanished neighbors arc ln.RightNode = edge.Node2; rn.LeftNode = edge.Node2; FortuneArc lnln = ln.LeftNeighbor; FortuneArc lnrn = ln.RightNeighbor; FortuneArc rnln = rn.LeftNeighbor; FortuneArc rnrn = rn.RightNeighbor; // add events to the newly formed circle arcs triples if (lnln != null) { if (lnrn != null) { FortuneEvent eventToAdd = getCircleEvent(lnln, ln, lnrn, ev.Point.Y); if (eventToAdd != null) { addCircleEvent(eventToAdd); } ln.CircleEvent = eventToAdd; } } if (rnln != null) { if (rnrn != null) { FortuneEvent eventToAdd = getCircleEvent(rnln, rn, rnrn, ev.Point.Y); if (eventToAdd != null) { addCircleEvent(eventToAdd); } rn.CircleEvent = eventToAdd; } } }
private void handlePointEvent(FortunePointEvent ev) { if (_shoreLine.Root == null) { FortuneArc arc = new FortuneArc(); FortuneSite site = new FortuneSite(); site.Cell = ev.Cell; arc.Site = site; _shoreLine.Root = arc; return; } FortuneArc arcAbove = _shoreLine.FindArc(ev.Point.X, ev.Point.Y); // remove events range from the queue if (arcAbove.CircleEvent != null) { _eventList.Remove(arcAbove.CircleEvent); } // create an arc FortuneArc splitter = new FortuneArc(); splitter.Site = new FortuneSite(); splitter.Site.Cell = ev.Cell; FortuneArc newArc = _shoreLine.SplitArc(arcAbove, splitter); FortuneArc ln = newArc.LeftNeighbor; FortuneArc lnln = null; if (ln != null) { lnln = ln.LeftNeighbor; } FortuneArc rn = newArc.RightNeighbor; FortuneArc rnrn = null; if (rn != null) { rnrn = rn.RightNeighbor; } if (ln != null) { if (lnln != null) { FortuneEvent eventToAdd = getCircleEvent(lnln, ln, newArc, ev.Point.Y); if (eventToAdd != null) { addCircleEvent(eventToAdd); } ln.CircleEvent = eventToAdd; } } if (rn != null) { if (rnrn != null) { FortuneEvent eventToAdd = getCircleEvent(newArc, rn, rnrn, ev.Point.Y); if (eventToAdd != null) { addCircleEvent(eventToAdd); } rn.CircleEvent = eventToAdd; } } }