// 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(); }