Exemplo n.º 1
0
            public data(PointD[] sites, float width, float height, VoronoiDiagramFlags flags)
            {
                foreach (PointD p in sites)
                {
                    if (p.X > 0 && p.Y > 0 && p.X < width && p.Y < height)
                    {
                        SiteEvents.Add(new siteEvent(p));
                    }
                    else if ((flags & VoronoiDiagramFlags.RemoveOffboundsSites) == 0)
                    {
                        throw new Exception("The input contains a point outside the bounds or on the perimeter (coordinates " +
                                            p + "). This case is not handled by this algorithm. Use the RT.Util.VoronoiDiagramFlags.REMOVE_OFFBOUNDS_SITES " +
                                            "flag to automatically remove such off-bounds input points.");
                    }
                }
                SiteEvents.Sort();

                // Make sure there are no two equal points in the input
                for (int i = 1; i < SiteEvents.Count; i++)
                {
                    while (i < SiteEvents.Count && SiteEvents[i - 1].Position == SiteEvents[i].Position)
                    {
                        if ((flags & VoronoiDiagramFlags.RemoveDuplicates) == VoronoiDiagramFlags.RemoveDuplicates)
                        {
                            SiteEvents.RemoveAt(i);
                        }
                        else
                        {
                            throw new Exception("The input contains two points at the same coordinates " +
                                                SiteEvents[i].Position + ". Voronoi diagrams are undefined for such a situation. " +
                                                "Use the RT.Util.VoronoiDiagramFlags.REMOVE_DUPLICATES flag to automatically remove such duplicate input points.");
                        }
                    }
                }

                // Main loop
                while (SiteEvents.Count > 0 || CircleEvents.Count > 0)
                {
                    if (CircleEvents.Count > 0 && (SiteEvents.Count == 0 || CircleEvents[0].X <= SiteEvents[0].Position.X))
                    {
                        // Process a circle event
                        circleEvent evt = CircleEvents[0];
                        CircleEvents.RemoveAt(0);
                        int arcIndex = Arcs.IndexOf(evt.Arc);
                        if (arcIndex == -1)
                        {
                            continue;
                        }

                        // The two edges left and right of the disappearing arc end here
                        if (Arcs[arcIndex - 1].Edge != null)
                        {
                            Arcs[arcIndex - 1].Edge.SetEndPoint(evt.Center);
                        }
                        if (evt.Arc.Edge != null)
                        {
                            evt.Arc.Edge.SetEndPoint(evt.Center);
                        }

                        // Remove the arc from the beachline
                        Arcs.RemoveAt(arcIndex);
                        // ArcIndex now points to the arc after the one that disappeared

                        // Start a new edge at the point where the other two edges ended
                        Arcs[arcIndex - 1].Edge = new edge(Arcs[arcIndex - 1].Site, Arcs[arcIndex].Site);
                        Arcs[arcIndex - 1].Edge.SetEndPoint(evt.Center);
                        Edges.Add(Arcs[arcIndex - 1].Edge);

                        // Recheck circle events on either side of the disappearing arc
                        if (arcIndex > 0)
                        {
                            checkCircleEvent(CircleEvents, arcIndex - 1, evt.X);
                        }
                        if (arcIndex < Arcs.Count)
                        {
                            checkCircleEvent(CircleEvents, arcIndex, evt.X);
                        }
                    }
                    else
                    {
                        // Process a site event
                        siteEvent evt = SiteEvents[0];
                        SiteEvents.RemoveAt(0);

                        if (Arcs.Count == 0)
                        {
                            Arcs.Add(new arc(evt.Position));
                            continue;
                        }

                        // Find the current arc(s) at height e.Position.y (if there are any)
                        bool arcFound = false;
                        for (int i = 0; i < Arcs.Count; i++)
                        {
                            PointD intersect;
                            if (doesIntersect(evt.Position, i, out intersect))
                            {
                                // New parabola intersects Arc - duplicate Arc
                                Arcs.Insert(i + 1, new arc(Arcs[i].Site));
                                Arcs[i + 1].Edge = Arcs[i].Edge;

                                // Add a new Arc for Event.Position in the right place
                                Arcs.Insert(i + 1, new arc(evt.Position));

                                // Add new half-edges connected to Arc's endpoints
                                Arcs[i].Edge = Arcs[i + 1].Edge = new edge(Arcs[i + 1].Site, Arcs[i + 2].Site);
                                Edges.Add(Arcs[i].Edge);

                                // Check for new circle events around the new arc:
                                checkCircleEvent(CircleEvents, i, evt.Position.X);
                                checkCircleEvent(CircleEvents, i + 2, evt.Position.X);

                                arcFound = true;
                                break;
                            }
                        }

                        if (arcFound)
                        {
                            continue;
                        }

                        // Special case: If Event.Position never intersects an arc, append it to the list.
                        // This only happens if there is more than one site event with the lowest X co-ordinate.
                        arc lastArc = Arcs[Arcs.Count - 1];
                        arc newArc  = new arc(evt.Position);
                        lastArc.Edge = new edge(lastArc.Site, newArc.Site);
                        Edges.Add(lastArc.Edge);
                        lastArc.Edge.SetEndPoint(new PointD(0, (newArc.Site.Y + lastArc.Site.Y) / 2));
                        Arcs.Add(newArc);
                    }
                }

                // Advance the sweep line so no parabolas can cross the bounding box
                double var = 2 * width + height;

                // Extend each remaining edge to the new parabola intersections
                for (int i = 0; i < Arcs.Count - 1; i++)
                {
                    if (Arcs[i].Edge != null)
                    {
                        Arcs[i].Edge.SetEndPoint(getIntersection(Arcs[i].Site, Arcs[i + 1].Site, 2 * var));
                    }
                }

                // Clip all the edges with the bounding rectangle and remove edges that are entirely outside
                var newEdges      = new List <edge>();
                var boundingEdges = new[] { new EdgeD(0, 0, width, 0), new EdgeD(width, 0, width, height), new EdgeD(width, height, 0, height), new EdgeD(0, height, 0, 0) };

                foreach (edge e in Edges)
                {
                    if ((e.Start.Value.X < 0 || e.Start.Value.X > width || e.Start.Value.Y < 0 || e.Start.Value.Y > width) &&
                        (e.End.Value.X < 0 || e.End.Value.X > width || e.End.Value.Y < 0 || e.End.Value.Y > width) &&
                        !boundingEdges.Any(be => be.IntersectsWith(new EdgeD(e.Start.Value, e.End.Value))))
                    {
                        continue;
                    }

                    if (e.Start.Value.X < 0)
                    {
                        e.Start = new PointD(0, e.End.Value.X / (e.End.Value.X - e.Start.Value.X) * (e.Start.Value.Y - e.End.Value.Y) + e.End.Value.Y);
                    }
                    if (e.Start.Value.Y < 0)
                    {
                        e.Start = new PointD(e.End.Value.Y / (e.End.Value.Y - e.Start.Value.Y) * (e.Start.Value.X - e.End.Value.X) + e.End.Value.X, 0);
                    }
                    if (e.End.Value.X < 0)
                    {
                        e.End = new PointD(0, e.Start.Value.X / (e.Start.Value.X - e.End.Value.X) * (e.End.Value.Y - e.Start.Value.Y) + e.Start.Value.Y);
                    }
                    if (e.End.Value.Y < 0)
                    {
                        e.End = new PointD(e.Start.Value.Y / (e.Start.Value.Y - e.End.Value.Y) * (e.End.Value.X - e.Start.Value.X) + e.Start.Value.X, 0);
                    }

                    if (e.Start.Value.X > width)
                    {
                        e.Start = new PointD(width, (width - e.Start.Value.X) / (e.End.Value.X - e.Start.Value.X) * (e.End.Value.Y - e.Start.Value.Y) + e.Start.Value.Y);
                    }
                    if (e.Start.Value.Y > height)
                    {
                        e.Start = new PointD((height - e.Start.Value.Y) / (e.End.Value.Y - e.Start.Value.Y) * (e.End.Value.X - e.Start.Value.X) + e.Start.Value.X, height);
                    }
                    if (e.End.Value.X > width)
                    {
                        e.End = new PointD(width, (width - e.End.Value.X) / (e.Start.Value.X - e.End.Value.X) * (e.Start.Value.Y - e.End.Value.Y) + e.End.Value.Y);
                    }
                    if (e.End.Value.Y > height)
                    {
                        e.End = new PointD((height - e.End.Value.Y) / (e.Start.Value.Y - e.End.Value.Y) * (e.Start.Value.X - e.End.Value.X) + e.End.Value.X, height);
                    }
                    newEdges.Add(e);
                }
                Edges = newEdges;

                // Generate polygons from the edges
                foreach (edge e in Edges)
                {
                    if (!Polygons.ContainsKey(e.SiteA))
                    {
                        Polygons.Add(e.SiteA, new polygon(e.SiteA));
                    }
                    Polygons[e.SiteA].AddEdge(e);
                    if (!Polygons.ContainsKey(e.SiteB))
                    {
                        Polygons.Add(e.SiteB, new polygon(e.SiteB));
                    }
                    Polygons[e.SiteB].AddEdge(e);
                }
            }
Exemplo n.º 2
0
 /// <summary>
 ///     Generates a Voronoi diagram from a set of input points.</summary>
 /// <param name="sites">
 ///     Input points (sites) to generate diagram from.</param>
 /// <param name="size">
 ///     Size of the viewport. The origin of the viewport is assumed to be at (0, 0).</param>
 /// <param name="flags">
 ///     Set of <see cref="VoronoiDiagramFlags"/> values that specifies additional options.</param>
 /// <returns>
 ///     A list of line segments describing the Voronoi diagram.</returns>
 public static VoronoiDiagram GenerateVoronoiDiagram(PointD[] sites, SizeF size, VoronoiDiagramFlags flags = 0)
 {
     return(new data(sites, size.Width, size.Height, flags).Apply(data => new VoronoiDiagram
     {
         Edges = data.Edges.Select(edge => new EdgeD(edge.Start.Value, edge.End.Value)).ToList(),
         Polygons = data.Polygons
                    .Select(kvp => Ut.KeyValuePair(kvp.Key, kvp.Value.ToPolygonD(flags.HasFlag(VoronoiDiagramFlags.IncludeEdgePolygons), size.Width, size.Height)))
                    .Where(kvp => kvp.Value != null)
                    .ToDictionary()
     }));
 }