// Sort of bad to introduce a dependency on the Path class, but this works.
        // The option to have this logic in QPExtractor is worse imo (does not promote code reuse and is hard to test,
        // because QPExtractor would not expose the method).
        public static Orientation GetOrientationOfCycle <TVertex>(this IReadOnlyUndirectedGraph <TVertex> graph, IEnumerable <TVertex> closedPath)
            where TVertex : IEquatable <TVertex>, IVertexInPlane
        {
            if (graph is null)
            {
                throw new ArgumentNullException(nameof(graph));
            }
            if (closedPath is null)
            {
                throw new ArgumentNullException(nameof(closedPath));
            }

            if (!closedPath.First().Equals(closedPath.Last()))
            {
                throw new ArgumentException($"The path is not closed.", nameof(closedPath));
            }
            if (closedPath.Count() == 1)
            {
                throw new ArgumentException($"The path is stationary.");
            }

            var pathAsCircularListOfVertices = new CircularList <TVertex>(closedPath.SkipLast(1));

            // Sum the external angle at every vertex
            double externalAngleSum = 0;

            for (int i = 0; i < pathAsCircularListOfVertices.Count; i++)
            {
                var vertex1 = pathAsCircularListOfVertices[i - 1];
                var vertex2 = pathAsCircularListOfVertices[i];
                var vertex3 = pathAsCircularListOfVertices[i + 1];

                var pos1 = vertex1.Position;
                var pos2 = vertex2.Position;
                var pos3 = vertex3.Position;

                externalAngleSum += PlaneUtility.GetExternalAngle(pos1, pos2, pos3);
            }

            const double Tolerance = 0.01;

            if (Math.Abs(externalAngleSum - 2 * Math.PI) < Tolerance)
            {
                return(Orientation.Counterclockwise);
            }
            else if (Math.Abs(externalAngleSum + 2 * Math.PI) < Tolerance)
            {
                return(Orientation.Clockwise);
            }
            else
            {
                throw new OrientationException($"Failed to determine the orientation of {closedPath}; external angle sum was {externalAngleSum}.");
            }
        }
        /// <summary>
        /// Determines whether two line segments intersect.
        /// </summary>
        /// <param name="lineSegment1">The first line segment.</param>
        /// <param name="lineSegment2">The second line segment.</param>
        /// <returns></returns>
        /// <remarks>
        /// <para>See <see href="https://www.cdn.geeksforgeeks.org/check-if-two-given-line-segments-intersect/"/>
        /// for the inner workings of this method (details which I have not worked out myself).</para></remarks>
        public static bool Intersect(OrientedLineSegment lineSegment1, OrientedLineSegment lineSegment2)
        {
            if (lineSegment1 is null)
            {
                throw new ArgumentNullException(nameof(lineSegment1));
            }
            if (lineSegment2 is null)
            {
                throw new ArgumentNullException(nameof(lineSegment2));
            }

            if (lineSegment1.Start == lineSegment1.End)
            {
                throw new NotImplementedException();                                         // Haven't checked that the method works in this case
            }
            if (lineSegment2.Start == lineSegment2.End)
            {
                throw new NotImplementedException();                                         // Haven't checked that the method works in this case
            }
            var ls1 = lineSegment1;
            var ls2 = lineSegment2;

            var o1 = PlaneUtility.GetOrientation(ls1.Start, ls1.End, ls2.Start);
            var o2 = PlaneUtility.GetOrientation(ls1.Start, ls1.End, ls2.End);
            var o3 = PlaneUtility.GetOrientation(ls2.Start, ls2.End, ls1.Start);
            var o4 = PlaneUtility.GetOrientation(ls2.Start, ls2.End, ls1.End);

            if (o1 != o2 && o3 != o4)
            {
                return(true);
            }

            if (o1 == TripletOrientation.Collinear && LineSegmentContainsPoint(ls1, ls2.Start))
            {
                return(true);
            }
            if (o2 == TripletOrientation.Collinear && LineSegmentContainsPoint(ls1, ls2.End))
            {
                return(true);
            }
            if (o3 == TripletOrientation.Collinear && LineSegmentContainsPoint(ls2, ls1.Start))
            {
                return(true);
            }
            if (o4 == TripletOrientation.Collinear && LineSegmentContainsPoint(ls2, ls1.End))
            {
                return(true);
            }

            return(false);

            // Idea (obsolete in favor of the above I guess):
            // Look at the common range of, say, the x-coordinates
            // If empty, then return false
            // If equal at either endpoint, return true (because they intersect in the endpoint)
            // If the first line segment is below/above the other in one endpoint and above/below the other in the other, return true
            // Else return false

            // The above doesn't work when one of the lines is vertical though

            // Assumes that p is on the line determined by ls (this assumes that ls is non-degenerate)
            bool LineSegmentContainsPoint(OrientedLineSegment ls, Point p)
            {
                return(Math.Min(ls.Start.X, ls.End.X) <= p.X &&
                       p.X <= Math.Max(ls.Start.X, ls.End.X) &&
                       Math.Min(ls.Start.Y, ls.End.Y) <= p.Y &&
                       p.Y <= Math.Max(ls.Start.Y, ls.End.Y));
            }
        }
        /// <summary>
        /// Determines whether two line segments intersect properly, in the sense that the
        /// interiors of the line segments intersect.
        /// </summary>
        /// <param name="lineSegment1">The first line segment.</param>
        /// <param name="lineSegment2">The second line segment.</param>
        /// <returns></returns>
        public static bool IntersectProperly(OrientedLineSegment lineSegment1, OrientedLineSegment lineSegment2)
        {
            if (lineSegment1 is null)
            {
                throw new ArgumentNullException(nameof(lineSegment1));
            }
            if (lineSegment2 is null)
            {
                throw new ArgumentNullException(nameof(lineSegment2));
            }

            if (lineSegment1.Start == lineSegment1.End)
            {
                return(false);                                        // The interior of a degenerate line segment is empty
            }
            if (lineSegment2.Start == lineSegment2.End)
            {
                return(false);                                        // The interior of a degenerate line segment is empty
            }
            var ls1 = lineSegment1;
            var ls2 = lineSegment2;

            var o1 = PlaneUtility.GetOrientation(ls1.Start, ls1.End, ls2.Start);
            var o2 = PlaneUtility.GetOrientation(ls1.Start, ls1.End, ls2.End);
            var o3 = PlaneUtility.GetOrientation(ls2.Start, ls2.End, ls1.Start);
            var o4 = PlaneUtility.GetOrientation(ls2.Start, ls2.End, ls1.End);

            // The line segments are collinear
            if (o1 == TripletOrientation.Collinear && o2 == TripletOrientation.Collinear)
            {
                return(ls1.IsEqualToAsUnorientedLineSegments(ls2) ||
                       LineSegmentInteriorContainsPoint(ls1, ls2.Start) ||
                       LineSegmentInteriorContainsPoint(ls1, ls2.End) ||
                       LineSegmentInteriorContainsPoint(ls2, ls1.Start) ||
                       LineSegmentInteriorContainsPoint(ls2, ls1.End));
            }

            // The line segments are not collinear, so if any three points are collinear, the line segments just "touch"
            // each other, which does not constitute a proper intersection
            if (o1 == TripletOrientation.Collinear || o2 == TripletOrientation.Collinear || o3 == TripletOrientation.Collinear || o4 == TripletOrientation.Collinear)
            {
                return(false);
            }

            // Then just do "the usual" check
            return(o1 != o2 && o3 != o4);

            // Assumes that p is on the line determined by ls (this assumes that ls is non-degenerate)
            bool LineSegmentContainsPoint(OrientedLineSegment ls, Point p)
            {
                return(Math.Min(ls.Start.X, ls.End.X) <= p.X &&
                       p.X <= Math.Max(ls.Start.X, ls.End.X) &&
                       Math.Min(ls.Start.Y, ls.End.Y) <= p.Y &&
                       p.Y <= Math.Max(ls.Start.Y, ls.End.Y));
            }

            // Assumes that p is on the line determined by ls (this assumes that ls is non-degenerate)
            bool LineSegmentInteriorContainsPoint(OrientedLineSegment ls, Point p)
            {
                return(LineSegmentContainsPoint(ls, p) && p != ls.Start && p != ls.End);
            }
        }