// Split `polygon` by this plane if needed, then put the polygon or polygon
    // fragments in the appropriate lists. Coplanar polygons go into either
    // `coplanarFront` or `coplanarBack` depending on their orientation with
    // respect to this plane. Polygons in front or in back of this plane go into
    // either `front` or `back`.
    public void splitPolygon(CSGPolygon polygon, List <CSGPolygon> coplanarFront, List <CSGPolygon> coplanarBack, List <CSGPolygon> front, List <CSGPolygon> back)
    {
        // Classify each point as well as the entire polygon into one of the above
        // four classes.
        int polygonType = 0;

        int[] types = new int[polygon.vertices.Count];

        for (int i = 0; i < polygon.vertices.Count; i++)
        {
            float t    = this.normal.dot(polygon.vertices[i].pos) - this.w;
            int   type = (t < -EPSILON) ? BACK : (t > EPSILON) ? FRONT : COPLANAR;
            polygonType |= type;
            types[i]     = type;
        }

        // Put the polygon in the correct list, splitting it when necessary.
        switch (polygonType)
        {
        case COPLANAR:
            (this.normal.dot(polygon.plane.normal) > 0 ? coplanarFront : coplanarBack).Add(polygon);
            break;

        case FRONT:
            front.Add(polygon);
            break;

        case BACK:
            back.Add(polygon);
            break;

        case SPANNING:
        {
            List <CSGVertex> f = new List <CSGVertex>();
            List <CSGVertex> b = new List <CSGVertex>();
            for (int i = 0; i < polygon.vertices.Count; i++)
            {
                int       j  = (i + 1) % polygon.vertices.Count;
                int       ti = types[i];
                int       tj = types[j];
                CSGVertex vi = polygon.vertices[i];
                CSGVertex vj = polygon.vertices[j];

                if (ti != BACK)
                {
                    f.Add(vi);
                }

                if (ti != FRONT)
                {
                    b.Add(ti != BACK ? vi.clone() : vi);
                }

                if ((ti | tj) == SPANNING)
                {
                    float     t = (this.w - this.normal.dot(vi.pos)) / this.normal.dot(vj.pos.minus(vi.pos));
                    CSGVertex v = vi.interpolate(vj, t);
                    f.Add(v);
                    b.Add(v.clone());
                }
            }
            if (f.Count >= 3)
            {
                front.Add(new CSGPolygon(f, polygon.shared));
            }

            if (b.Count >= 3)
            {
                back.Add(new CSGPolygon(b, polygon.shared));
            }
            break;
        }
        }
    }