//  E = average of two neighboring face points and original endpoints
        //
        //  Compute P'             F = average F of all n face points for faces touching P
        //                         R = average R of all n edge midpoints for edges touching P
        //       F + 2R + (n-3)P   P = old point location
        //  P' = ----------------
        //             n           -> F weight is     1/n
        //                         -> R weight is     2/n
        //       F   2R   (n-3)P   -> P weight is (n-3)/n
        //  P' = - + -- + ------
        //       n    n      n
        //
        //  For each corner in the old polygon, add one quad
        //  (centroid, previous edge 'edge point', corner, next edge 'edge midpoint')
        public CatmullClarkGeometryOperation(Geometry src)
        {
            Source = src;

            //                        (n-3)P
            //  Make initial P's with ------
            //                           n
            foreach (Point oldPoint in Source.Points)
            {
                float n      = (float)oldPoint.Corners.Count;
                float weight = (n - 3.0f) / n;
                MakeNewPointFromPoint(weight, oldPoint);
            }

            //  Make edge points
            //  "average of two neighboring face points and original endpoints"
            //  Add midpoint (R) to each new end points
            //    R = average R of all n edge midpoints for edges touching P
            //   2R  we add both edge end points with weight 1 so total edge weight is 2
            //   --
            //    n
            foreach (var kvp in Source.Edges)
            {
                Edge  oldEdge  = kvp.Key;
                Point newPoint = MakeNewPointFromEdge(oldEdge);         //  these get weights 1 + 1
                foreach (Polygon oldPolygon in kvp.Value)
                {
                    float weight = 1.0f / (float)oldPolygon.Corners.Count;
                    AddPolygonCentroid(newPoint, weight, oldPolygon);
                }
                Point newPointA = pointOldToNew[oldEdge.A];
                Point newPointB = pointOldToNew[oldEdge.B];
                float nA        = (float)(oldEdge.A.Corners.Count);
                float nB        = (float)(oldEdge.B.Corners.Count);
                float weightA   = 1.0f / nA;
                float weightB   = 1.0f / nB;
                AddPointSource(newPointA, weightA, oldEdge.A);
                AddPointSource(newPointA, weightA, oldEdge.B);
                AddPointSource(newPointB, weightB, oldEdge.A);
                AddPointSource(newPointB, weightB, oldEdge.B);
            }

            foreach (Polygon oldPolygon in Source.Polygons)
            {
                //  Make centroid point
                MakeNewPointFromPolygonCentroid(oldPolygon);

                //  Add polygon centroids (F) to all corners' point sources
                //  F = average F of all n face points for faces touching P
                //   F    <- because F is average of all centroids, it adds extra /n
                //  ---
                //   n
                foreach (Corner oldCorner in oldPolygon.Corners)
                {
                    Point oldPoint     = oldCorner.Point;
                    Point newPoint     = pointOldToNew[oldPoint];
                    float pointWeight  = 1.0f / (float)(oldPoint.Corners.Count);
                    float cornerWeight = 1.0f / (float)(oldPolygon.Corners.Count);
                    AddPolygonCentroid(newPoint, pointWeight * pointWeight * cornerWeight, oldPolygon);
                }
            }

            //  Subdivide polygons, clone (and corners);
            for (uint polygonIndex = 0; polygonIndex < src.Polygons.Count; ++polygonIndex)
            {
                Polygon oldPolygon = Source.Polygons[(int)polygonIndex];

                for (int i = 0; i < oldPolygon.Corners.Count; ++i) // Corner oldCorner in oldPolygon.Corners)
                {
                    Corner  oldCorner      = oldPolygon.Corners[i];
                    Corner  previousCorner = oldPolygon.Corners[(oldPolygon.Corners.Count + i - 1) % oldPolygon.Corners.Count];
                    Corner  nextCorner     = oldPolygon.Corners[(i + 1) % oldPolygon.Corners.Count];
                    Edge    previousEdge   = new Edge(previousCorner.Point, oldCorner.Point);
                    Edge    nextEdge       = new Edge(oldCorner.Point, nextCorner.Point);
                    Polygon newPolygon     = MakeNewPolygonFromPolygon(oldPolygon);
                    MakeNewCornerFromPolygonCentroid(newPolygon, oldPolygon);
                    MakeNewCornerFromEdgePoint(newPolygon, previousEdge);
                    MakeNewCornerFromCorner(newPolygon, oldCorner);
                    MakeNewCornerFromEdgePoint(newPolygon, nextEdge);
                }
            }

            Destination.BuildEdges();
            //  TODO can we map some edges to old edges? yes. Do it.
            InterpolateAllAttributeMaps();
        }
        public TruncateGeometryOperation(Geometry src, bool close)
        {
            Source = src;

            //  Pass 1: Create truncated versions of old polygons
            foreach (Polygon oldPolygon in Source.Polygons)
            {
                Polygon newPolygon = MakeNewPolygonFromPolygon(oldPolygon);

                double n1    = (double)(oldPolygon.Corners.Count);
                double n2    = (double)(2 * oldPolygon.Corners.Count);
                float  ratio = (float)(System.Math.Tan(System.Math.PI / n2) / System.Math.Tan(System.Math.PI / n1));
                float  t     = 0.5f - 0.5f * ratio;

                //  Pass 1A: Create truncated corners
                for (int i = 0; i < oldPolygon.Corners.Count; ++i)
                {
                    Corner oldCorner  = oldPolygon.Corners[i];
                    Corner nextCorner = oldPolygon.Corners[(i + 1) % oldPolygon.Corners.Count];

                    UpdateTruncatedEdge(newPolygon, oldCorner, nextCorner, t);
                }

                //  Pass 1B: Collect open edges from new polygon to per point dictionary
                //  Note the order of points in new edge is important in this case.
                for (int i = 0; i < newPolygon.Corners.Count; i += 2)
                {
                    Corner newCorner      = newPolygon.Corners[i];
                    Corner previousCorner = newPolygon.Corners[(i + newPolygon.Corners.Count - 1) % newPolygon.Corners.Count];
                    Point  newPoint       = newCorner.Point;
                    Point  previousPoint  = previousCorner.Point;
                    Point  oldPoint       = newPointToOld[newPoint];
                    Edge   newEdge        = new Edge(newPoint, previousPoint);

                    if (oldPointToOpenEdges.ContainsKey(oldPoint) == false)
                    {
                        oldPointToOpenEdges[oldPoint] = new List <Edge>();
                    }
                    oldPointToOpenEdges[oldPoint].Add(newEdge);
                }
            }

            //  Pass 2: Connect open edges
            if (close)
            {
                foreach (Point oldPoint in src.Points)
                {
                    if (oldPointToOpenEdges.ContainsKey(oldPoint) == false)
                    {
                        continue;
                    }
                    if (oldPointToOpenEdges[oldPoint].Count < 3)
                    {
                        continue;
                    }
                    Polygon newPolygon = Destination.MakePolygon();
                    //  TODO This polygon has no sources.. inherit average of surrounding polygons?

                    Edge edge      = oldPointToOpenEdges[oldPoint].First();
                    Edge startEdge = edge;
                    //  TODO MUSTFIX    This loop does not terminate for some geometries
                    //                  such as tetrahemihexahedron
                    do
                    {
                        bool nextEdgeFound = false;
                        int  edgeIndex     = 0;
                        foreach (Edge nextEdge in oldPointToOpenEdges[oldPoint])
                        {
                            ++edgeIndex;
                            if (nextEdge.A == edge.B)
                            {
                                Point  newPoint  = edge.A;
                                Corner newCorner = newPolygon.MakeCorner(newPoint);
                                DistributeCornerSources(newCorner, 1.0f, newPoint);
                                edge          = nextEdge;
                                nextEdgeFound = true;
                                break;
                            }
                        }
                        /*  This can happen  */
                        if (nextEdgeFound == false)
                        {
                            break;
                        }
                    }while(edge.Equals(startEdge) == false);

                    if (newPolygon.Corners.Count < 3)
                    {
                        Destination.RemovePolygon(newPolygon);
                    }
                }
            }

            Destination.BuildEdges();
            InterpolateAllAttributeMaps();
        }