public static CompiledDrawing FromFace(FillFace face) { var fill = new CompiledDrawing(); // Simplify the face by subdividing overlapping curves var newFace = face.SubdivideOverlapping(); // Build the fill polygons and triangulate them fill.Triangles = Triangulator.YMonotone.Triangulate(newFace.Contours.Select(FillPolygon).ToArray()); // Collect all curve triangles var curves = newFace.Contours.SelectMany(v => v.Where(c => c.Type != CurveType.Line)).ToArray(); var curveTriangles = new List <CurveTriangle>(); var doubleCurveTriangles = new List <DoubleCurveTriangle>(); if (curves.Length > 0) { // Check first if the last and first curve aren't joinable bool lastFirstJoin = curves.Length > 1 && FillFace.AreCurvesFusable(curves[curves.Length - 1], curves[0]); if (lastFirstJoin) { doubleCurveTriangles.AddRange(FuseCurveTriangles(curves[curves.Length - 1], curves[0])); } else { curveTriangles.AddRange(CurveVertex.MakeTriangleFan(curves[0].CurveVertices)); } // Now, scan the curve list for pairs of scannable curves var k = lastFirstJoin ? 1 : 0; for (int i = k; i < curves.Length - k; i++) { if (i < curves.Length - 1 && FillFace.AreCurvesFusable(curves[i], curves[i + 1])) { doubleCurveTriangles.AddRange(FuseCurveTriangles(curves[i], curves[i + 1])); i++; } else { curveTriangles.AddRange(CurveVertex.MakeTriangleFan(curves[i].CurveVertices)); } } } fill.CurveTriangles = curveTriangles.ToArray(); fill.DoubleCurveTriangles = doubleCurveTriangles.ToArray(); return(fill); }
private static Double2[] FillPolygon(Curve[] contour) { // "Guess" a capacity for the list, add the first point var list = new List <Double2>((int)(1.4 * contour.Length)); // Check first if the last and first curve aren't joinable bool lastFirstJoin = FillFace.AreCurvesFusable(contour[contour.Length - 1], contour[0]); if (lastFirstJoin) { list.Add(contour[0].At(1)); } // Now, scan the curve list for pairs of scannable curves var k = lastFirstJoin ? 1 : 0; for (int i = k; i < contour.Length - k; i++) { if (i < contour.Length - 1 && FillFace.AreCurvesFusable(contour[i], contour[i + 1])) { // If they describe a positive winding on the plane, add only its endpoint var endp0 = contour[i].At(0); var endp1 = contour[i + 1].At(1); var pth = (endp0 + endp1) / 2; if (contour[i].WindingRelativeTo(pth) + contour[i + 1].WindingRelativeTo(pth) > 0) { list.Add(endp1); } else { // Else, compute the convex hull and add the correct point sequence var points = contour[i].EnclosingPolygon.Concat(contour[i + 1].EnclosingPolygon).ToArray(); var hull = GeometricUtils.ConvexHull(points); var hl = hull.Length; // We have to go through the hull clockwise. Find the first point int ik; for (ik = 0; ik < hl; ik++) { if (hull[ik] == endp0) { break; } } // And run through it for (int i0 = ik; i0 != (ik + 1) % hl; i0 = (i0 + hl - 1) % hl) { list.Add(hull[i0]); } } i++; } else if (contour[i].Type == CurveType.Line || contour[i].IsConvex) { list.Add(contour[i].At(1)); } else { list.AddRange(contour[i].EnclosingPolygon.Skip(1)); } } // Sanitize the list; identical vertices var indices = new List <int>(); for (int i = 0; i < list.Count; i++) { if (DoubleUtils.RoughlyEquals(list[i], list[(i + 1) % list.Count])) { indices.Add(i); } } list.ExtractIndices(indices.ToArray()); return(list.ToArray()); }