// Split a polygon by a plane, this is a bit faster than clipping twice with the flipped
        // plane, and will guarantee that points are exact
        public static SplitPolygon SplitPolygonByPlane(IEnumerable <IVertex> vertList, ClipPlane plane)
        {
            if (plane == null)
            {
                throw new ArgumentNullException("plane");
            }

            if (vertList == null)
            {
                throw new ArgumentNullException("vertList");
            }

            List <IVertex> front_list = new List <IVertex>();
            List <IVertex> back_list  = new List <IVertex>();

            ClipSide firstPointSide = ClipSide.CoPlanar;
            ClipSide lastPointSide  = ClipSide.CoPlanar;
            IVertex  lastVertex     = null;
            IVertex  firstVertex    = null;
            bool     isCoPlanar     = true;

            foreach (var v in vertList)
            {
                if (lastVertex == null)
                {
                    // This is the first vertex in the list, so it is specially handled
                    // because we have no prior state, and, we'll need to refer to it with
                    // regards to the final vertex
                    firstVertex    = lastVertex = v;
                    firstPointSide = lastPointSide = plane.ClassifyPoint(v.Position);
                    isCoPlanar     = isCoPlanar && firstPointSide == ClipSide.CoPlanar;

                    // Emit the point to both sides if coplanar, otherwise emit
                    // it to the particular side it is
                    if (firstPointSide == ClipSide.CoPlanar)
                    {
                        front_list.Add(v);
                        back_list.Add(v);
                    }
                    else if (firstPointSide == ClipSide.Back)
                    {
                        back_list.Add(v);
                    }
                    else
                    {
                        front_list.Add(v);
                    }

                    continue;
                }

                // Classify the current point to the plane
                ClipSide currPointSide = plane.ClassifyPoint(v.Position);
                isCoPlanar = isCoPlanar && currPointSide == ClipSide.CoPlanar;

                // One thing to be mindful of is to generate the intersection points in a consistent manner by
                // calling the intersection function with the verts in the same order.
                if (lastPointSide == ClipSide.CoPlanar)
                {
                    // The last point was on the split plane
                    if (currPointSide == ClipSide.CoPlanar)
                    {
                        // Still on the split plane
                        front_list.Add(v);
                        back_list.Add(v);
                    }
                    else if (currPointSide == ClipSide.Back)
                    {
                        // Going from the split plane to the backside
                        back_list.Add(v);
                    }
                    else
                    {
                        // Going from the split plane to the frontside
                        front_list.Add(v);
                    }
                }
                else if (lastPointSide == ClipSide.Front)
                {
                    // Last point was in the front
                    if (currPointSide == ClipSide.CoPlanar)
                    {
                        // Front -> coplanar
                        front_list.Add(v);
                        back_list.Add(v);
                    }
                    else if (currPointSide == ClipSide.Back)
                    {
                        // Front -> Back
                        var intersected_pt = plane.IntersectEdge(lastVertex, v);
                        front_list.Add(intersected_pt);
                        back_list.Add(intersected_pt);
                        back_list.Add(v);
                    }
                    else
                    {
                        // Front -> Front
                        front_list.Add(v);
                    }
                }
                else
                {
                    // Last point was in the back
                    if (currPointSide == ClipSide.CoPlanar)
                    {
                        // Back -> coplanar
                        front_list.Add(v);
                        back_list.Add(v);
                    }
                    else if (currPointSide == ClipSide.Back)
                    {
                        // Back -> Back
                        back_list.Add(v);
                    }
                    else
                    {
                        // Back -> Front
                        var intersected_pt = plane.IntersectEdge(v, lastVertex);
                        back_list.Add(intersected_pt);
                        front_list.Add(intersected_pt);
                        front_list.Add(v);
                    }
                }

                lastPointSide = currPointSide;
                lastVertex    = v;
            }

            // To finish up we need to handle the edge from the last vertex back to the first vertex
            // This is only necessary if the last point and the first point were on different sides of the plane
            if (firstPointSide != lastPointSide)
            {
                // We ended up in a different area then the first point
                if (firstPointSide == ClipSide.CoPlanar || lastPointSide == ClipSide.CoPlanar)
                {
                    // If we started or ended on a co-planar point, then we don't have to add more
                }
                else if (firstPointSide == ClipSide.Back)
                {
                    // We started in the back, we ended in the front
                    var intersection_pt = plane.IntersectEdge(lastVertex, firstVertex);
                    front_list.Add(intersection_pt);
                    back_list.Add(intersection_pt);
                }
                else if (firstPointSide == ClipSide.Front)
                {
                    // We started in the front, we ended in the back
                    var intersection_pt = plane.IntersectEdge(firstVertex, lastVertex);
                    front_list.Add(intersection_pt);
                    back_list.Add(intersection_pt);
                }
            }

            var res = new SplitPolygon();

            if (isCoPlanar)
            {
                front_list   = CleanDegenerates(front_list).ToList();
                res.coplaner = front_list.Count > 2 ? front_list.ToArray() : null;
                res.front    = null;
                res.back     = null;
            }
            else
            {
                // If there we some verts co-planar, but not all, and the remaining
                // verts are on one side, then the other side will have some verts, but not a full triangle
                front_list   = CleanDegenerates(front_list).ToList();
                back_list    = CleanDegenerates(back_list).ToList();
                res.front    = front_list.Count > 2 ? front_list.ToArray() : null;
                res.back     = back_list.Count > 2 ? back_list.ToArray() : null;
                res.coplaner = null;
            }
            return(res);
        }
        /// <summary>
        /// Generic implementation of Sutherland–Hodgman clipping algorithm
        /// </summary>
        /// <param name="vertList"></param>
        /// <returns></returns>
        public static IEnumerable <IVertex> ClipToPlaneRaw(IEnumerable <IVertex> vertList, ClipPlane plane)
        {
            if (plane == null)
            {
                throw new ArgumentNullException("plane");
            }

            if (vertList == null)
            {
                yield break;
            }

            ClipSide firstPointSide = ClipSide.CoPlanar;
            ClipSide lastPointSide  = ClipSide.CoPlanar;
            IVertex  lastVertex     = null;
            IVertex  firstVertex    = null;

            foreach (var v in vertList)
            {
                if (lastVertex == null)
                {
                    // This is the first vertex in the list, so it is specially handled
                    // because we have no prior state, and, we'll need to refer to it with
                    // regards to the final vertex
                    firstVertex    = lastVertex = v;
                    firstPointSide = lastPointSide = plane.ClassifyPoint(v.Position);

                    // If this point is in front of the plane, emit it
                    if (firstPointSide != ClipSide.Back)
                    {
                        yield return(v);
                    }
                    continue;
                }

                // Classify the current point to the plane
                ClipSide currPointSide = plane.ClassifyPoint(v.Position);

                // We have 4 possible situations:
                //   1) Last point in front of the plane, current point in front of the plane:
                //       In this situation emit the current point
                //   2) Last point in front of the plane, current point is behind the plane:
                //       In this situation emit the intersection point of the plane and the edge (last, curr)
                //   3) Last point in back of the plane, current point is behind the plane:
                //       In this situation do not emit anything
                //   4) Last point in back of the plane, current point in front of the plane:
                //       In this situation emit the intersection point of the plane and the edge (curr, last), and
                //       then emit the current point
                //
                // One thing to be mindful of is to generate the intersection points in a consistent manner by
                // calling the intersection function with the verts in the same order.
                if (lastPointSide != ClipSide.Back)
                {
                    if (currPointSide != ClipSide.Back)
                    {
                        // Situation 1
                        yield return(v);
                    }
                    else
                    {
                        // Situation 2
                        yield return(plane.IntersectEdge(lastVertex, v));
                    }
                }
                else
                {
                    if (currPointSide != ClipSide.Back)
                    {
                        // Situation 4
                        yield return(plane.IntersectEdge(v, lastVertex));

                        yield return(v);
                    }
                }

                lastPointSide = currPointSide;
                lastVertex    = v;
            }

            // To finish up we need to handle the edge from the last vertex back to the first vertex
            // This is only necessary if the last point and the first point were on different sides of the plane
            bool firstInFront = firstPointSide != ClipSide.Back;
            bool lastInFront  = lastPointSide != ClipSide.Back;

            if (firstInFront != lastInFront)
            {
                if (firstInFront)
                {
                    yield return(plane.IntersectEdge(firstVertex, lastVertex));
                }
                else
                {
                    yield return(plane.IntersectEdge(lastVertex, firstVertex));
                }
            }
        }