public bool Intersects(OrientedLineSegment otherLineSegment)
        {
            if (otherLineSegment is null)
            {
                throw new ArgumentNullException(nameof(otherLineSegment));
            }

            return(Intersect(this, otherLineSegment));
        }
        /// <summary>
        /// Returns a boolean value indicating whether the quiver is plane, i.e., whether the
        /// arrows (drawn as straight lines) are pairwise non-intersecting.
        /// </summary>
        /// <returns>A boolean value indicating whether the quiver is plane.</returns>
        public static bool IsPlane <TVertex>(this IReadOnlyUndirectedGraph <TVertex> graph) where TVertex : IEquatable <TVertex>, IVertexInPlane
        {
            // Remark: Deconstructing lambda arguments does not seem to be possible as of this writing. In other words,
            // the following would not work:
            //     ... .Where( ((e1, e2)) => e1 != e2 )
            foreach (var(edge1, edge2) in Utility.CartesianProduct(graph.Edges, graph.Edges).Where(p => p.Item1 != p.Item2))
            {
                var lineSegment1 = new OrientedLineSegment(edge1.Vertex1.Position, edge1.Vertex2.Position);
                var lineSegment2 = new OrientedLineSegment(edge2.Vertex1.Position, edge2.Vertex2.Position);
                if (lineSegment1.IntersectsProperly(lineSegment2))
                {
                    return(false);
                }
            }

            return(true);
        }
        /// <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));
            }
        }
 public bool IsEqualToAsUnorientedLineSegments(OrientedLineSegment otherLineSegment)
 {
     return(this == otherLineSegment || this == otherLineSegment.Reverse());
 }
        /// <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);
            }
        }