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); }
internal static CompiledDrawing CompileCurves(List <Curve> curves, SvgFillRule fillRule) { // Reunite all intersections to subdivide the curves var curveRootSets = new SortedDictionary <double, Double2> [curves.Count]; for (int i = 0; i < curveRootSets.Length; i++) { curveRootSets[i] = new SortedDictionary <double, Double2>() { [0] = curves[i].At(0), [1] = curves[i].At(1) } } ; // Get all intersections for (int i = 0; i < curves.Count; i++) { for (int j = i + 1; j < curves.Count; j++) { foreach (var pair in Curve.Intersections(curves[i], curves[j])) { if (!GeometricUtils.Inside01(pair.A) || !GeometricUtils.Inside01(pair.B)) { continue; } curveRootSets[i][pair.A] = curves[i].At(pair.A); curveRootSets[j][pair.B] = curves[j].At(pair.B); } } } // Cluster the intersections var curveRootClusters = DerivePointClustersFromRootSets(curveRootSets); // Finally, we can start building the DCEL var dcel = new DCEL.DCEL(); for (int i = 0; i < curves.Count; i++) { var prevPair = new KeyValuePair <double, int>(double.NaN, 0); foreach (var curPair in curveRootClusters[i]) { if (!double.IsNaN(prevPair.Key)) { Curve curve; if (prevPair.Key == 0 && curPair.Key == 1) { curve = curves[i]; } else { curve = curves[i].Subcurve(prevPair.Key, curPair.Key); } foreach (var c in curve.Simplify()) { // Skip degenerate curves if (c.IsDegenerate) { continue; } dcel.AddCurve(c, prevPair.Value, curPair.Value); //Console.WriteLine(dcel); //Console.ReadLine(); } } prevPair = curPair; } } //Console.WriteLine(dcel); //Console.ReadLine(); // Now, we remove wedges and assign the fill numbers dcel.RemoveWedges(); //Console.WriteLine(dcel); //Console.ReadLine(); dcel.AssignFillNumbers(); //Console.WriteLine(dcel); //Console.ReadLine(); // Pick the appropriate predicate for the fill rule Func <DCEL.Face, bool> facePredicate; if (fillRule == SvgFillRule.EvenOdd) { facePredicate = f => f.FillNumber % 2 != 0; } else { facePredicate = f => f.FillNumber != 0; } // Simplify the faces dcel.SimplifyFaces(facePredicate); //Console.WriteLine(dcel); //Console.ReadLine(); // Generate the filled faces var fills = dcel.Faces.Where(facePredicate).Select(face => new FillFace(face.Contours.Select(contour => contour.CyclicalSequence.Select(e => e.Curve).ToArray()).ToArray())); // Generace the filled faces return(CompiledDrawing.ConcatMany(fills.Select(CompiledDrawing.FromFace))); }
internal static CompiledDrawing CompileCurves(List <Curve> curves, FillRule fillRule) { // Reunite all intersections to subdivide the curves var curveRootSets = new SortedDictionary <double, Double2> [curves.Count]; for (int i = 0; i < curveRootSets.Length; i++) { curveRootSets[i] = new SortedDictionary <double, Double2>() { [0] = curves[i].At(0), [1] = curves[i].At(1) } } ; // Get all intersections for (int i = 0; i < curves.Count; i++) { for (int j = i + 1; j < curves.Count; j++) { foreach (var pair in Curve.Intersections(curves[i], curves[j])) { if (!GeometricUtils.Inside01(pair.A) || !GeometricUtils.Inside01(pair.B)) { continue; } var a = curves[i].At(pair.A); var b = curves[j].At(pair.B); if (!DoubleUtils.RoughlyEquals(a / 16, b / 16)) { //Console.WriteLine(string.Join(" ", Curve.Intersections(curves[i], curves[j]).Select(c => $"({c.A} {c.B})"))); Debug.Assert(false, "Problem here..."); } curveRootSets[i][pair.A] = curveRootSets[j][pair.B] = (a + b) / 2; } } } //for (int i = 0; i < curves.Count; i++) //Curve.RemoveSimilarRoots(curveRootSets[i]); // Finally, we can start building the DCEL var dcel = new DCEL.DCEL(); for (int i = 0; i < curves.Count; i++) { KeyValuePair <double, Double2> prevPair = new KeyValuePair <double, Double2>(double.NaN, new Double2()); foreach (var curPair in curveRootSets[i]) { if (!double.IsNaN(prevPair.Key)) { Curve curve; if (prevPair.Key == 0 && curPair.Key == 1) { curve = curves[i]; } else { curve = curves[i].Subcurve(prevPair.Key, curPair.Key); } foreach (var c in curve.Simplify()) { //if (c.IsDegenerate) continue; dcel.AddCurve(c, prevPair.Value, curPair.Value); //Console.WriteLine(dcel); //Console.ReadLine(); } } prevPair = curPair; } } //Console.WriteLine(dcel); //Console.ReadLine(); // Now, we remove wedges and assign the fill numbers dcel.RemoveWedges(); //Console.WriteLine(dcel); //Console.ReadLine(); dcel.AssignFillNumbers(); //Console.WriteLine(dcel); //Console.ReadLine(); // Pick the appropriate predicate for the fill rule Func <DCEL.Face, bool> facePredicate; if (fillRule == FillRule.Evenodd) { facePredicate = f => f.FillNumber % 2 != 0; } else { facePredicate = f => f.FillNumber != 0; } // Simplify the faces dcel.SimplifyFaces(facePredicate); //Console.WriteLine(dcel); //Console.ReadLine(); // Generate the filled faces var fills = dcel.Faces.Where(facePredicate).Select(face => new FillFace(face.Contours.Select(contour => contour.CyclicalSequence.Select(e => e.Curve).ToArray()).ToArray())); // Generace the filled faces return(CompiledDrawing.ConcatMany(fills.Select(CompiledDrawing.FromFace))); }