public static List <LineSegmentFr> Voronoi(List <PointFr> points)
        {
            var triangulation  = TriangulateIterative(points);
            var ch             = new PolygonFr(ConvexHullJarvis(points));
            var dictTriEdges   = new Dictionary <LineSegmentFr, PointFr>();
            var voronoiDiagram = new List <LineSegmentFr>();

            foreach (var triangle in triangulation)
            {
                var circumCenter = triangle.CircumCircle().Center;
                foreach (var edge in triangle.Edges(true))
                {
                    if (dictTriEdges.ContainsKey(edge))
                    {
                        voronoiDiagram.Add(new LineSegmentFr(circumCenter, dictTriEdges[edge], true));
                        dictTriEdges.Remove(edge);
                    }
                    else
                    {
                        dictTriEdges.Add(edge, circumCenter);
                    }
                }
            }

            voronoiDiagram.AddRange(
                dictTriEdges.Where(kvp => ch.Contains2(kvp.Value) && kvp.Key.Center() != kvp.Value)
                .Select(kvp => new LineSegmentFr(kvp.Key.Center(), kvp.Value)));

            for (var i = voronoiDiagram.Count - 1; i >= 0; i--)
            {
                var edge            = voronoiDiagram[i];
                var chIntersections = ch.Intersections(edge);
                if (chIntersections.Count > 2)
                {
                    throw new Exception("Błędne obliczenia");
                }
                if (!ch.Contains2(edge.StartPoint) && !ch.Contains2(edge.EndPoint)) // chIntersections.Count > 1 ||
                {
                    voronoiDiagram.Remove(edge);
                    continue;
                }
                if (!ch.Contains2(edge.StartPoint))
                {
                    edge.StartPoint = chIntersections.Except(edge.EndPoints()).Single();
                }
                else if (!ch.Contains2(edge.EndPoint))
                {
                    edge.EndPoint = chIntersections.Except(edge.EndPoints()).Single();
                }
                voronoiDiagram[i] = edge;
            }

            return(voronoiDiagram);
        }
        public static bool IsInsideConvexPolygon(PointFr point, PolygonFr polygon)
        {
            var vertices = polygon.Vertices.ToList(); // wierzchołki posortowane przeciwnie do wskazówek zegara względem wnętrza wielokąta
            var edges    = polygon.Edges().ToList();

            if (vertices.Count < 3)
            {
                throw new ArgumentException("Polygon can't have less than 3 vertices");
            }
            if (ConvexHullJarvis(vertices).Count != vertices.Count) // Powinno wystarczyć Count
            {
                throw new ArgumentException("Polygon must be convex");
            }

            if (vertices.Count == 3)
            {
                return(!edges.Any(point.IsRightOf));
            }

            var polyCenter = polygon.Center;

            if (polyCenter == null)
            {
                throw new Exception("Can't calculate center of the Polygon");
            }
            var mid = (PointFr)polyCenter;

            while (edges.Count > 1)
            {
                var testSegment = new LineSegmentFr(mid, edges[edges.Count / 2].StartPoint);
                if (point.IsRightOf(testSegment))
                {
                    edges = edges.Take(edges.Count / 2).ToList();
                }
                else if (point.IsLeftOf(testSegment))
                {
                    edges = edges.Skip(edges.Count / 2).ToList();
                }
                else if (testSegment.Contains(point))
                {
                    return(true);
                }
                else
                {
                    throw new Exception("Invalid calculations performed, it should never happen");
                }
            }

            return(!point.IsRightOf(edges.Single())); // czyli IsLeftOf + Contains, jeżeli jest we wierzchołku to będzie spełniony ostatni warunek z while'a
        }
        public static bool IsInsidePolygon(PointFr point, PolygonFr polygon)
        {
            if (polygon.Edges(true).Any(edge => edge.Contains(point)))
            {
                return(true);
            }

            var vertices = polygon.Vertices.ToList();
            int i, j = vertices.Count - 1;
            var contains = false;

            for (i = 0; i < vertices.Count; i++)
            {
                if ((vertices[i].Y < point.Y && vertices[j].Y >= point.Y || vertices[j].Y < point.Y && vertices[i].Y >= point.Y) && (vertices[i].X <= point.X || vertices[j].X <= point.X))
                {
                    contains ^= (vertices[i].X + (point.Y - vertices[i].Y) / (vertices[j].Y - vertices[i].Y) * (vertices[j].X - vertices[i].X) < point.X);
                }
                j = i;
            }

            return(contains);
        }
        public bool Contains(PolygonFr polygon)
        {
            var ps = polygon.Vertices.ToList();

            return(ps.Count != 0 && ps.All(Contains));
        }