public List <Vector2[]> TriangulateClipperSolution(ClipperLib.PolyTree solution) { var tess = new Tess(); tess.NoEmptyPolygons = true; // Add a contour for each part of the solution tree ClipperLib.PolyNode node = solution.GetFirst(); while (node != null) { // Only interested in closed paths if (!node.IsOpen) { // Add a new countor. Holes are automatically generated. var vertices = node.Contour.Select(pt => new ContourVertex { Position = new Vec3 { X = pt.X, Y = pt.Y, Z = 0 } }).ToArray(); tess.AddContour(vertices); } node = node.GetNext(); } return(TrianglesFromTessellator(tess)); }
private static void ClipperPolyTreeToPolygonListRecursively(ClipperLib.PolyNode node, HashSet <ClipperLib.IntPoint> sharpPoints, HashSet <ClipperLib.IntPoint> allPoints, List <PolygonClosedD2D> polygonList, IDictionary <ClipperLib.PolyNode, PolygonClosedD2D> dictClipperNodeToNode) { if (node.Contour != null && node.Contour.Count != 0) { var pointsInThisPolygon = node.Contour.Select(clipperPt => new PointD2D(clipperPt.X / 65536.0, clipperPt.Y / 65536.0)); var sharpPointsInThisPolygon = node.Contour.Where(clipperPt => sharpPoints.Contains(clipperPt)).Select(clipperPt => new PointD2D(clipperPt.X / 65536.0, clipperPt.Y / 65536.0)); var polygon = new PolygonClosedD2D(pointsInThisPolygon.ToArray(), new HashSet <PointD2D>(sharpPointsInThisPolygon)) { IsHole = node.IsHole }; polygonList.Add(polygon); if (node.IsHole) { polygon.Parent = dictClipperNodeToNode[node.Parent]; } dictClipperNodeToNode.Add(node, polygon); } if (0 != node.ChildCount) { foreach (var childNode in node.Childs) { ClipperPolyTreeToPolygonListRecursively(childNode, sharpPoints, allPoints, polygonList, dictClipperNodeToNode); } } }
private List <Poly2Tri.DelaunayTriangle> GetTriangleListFromClipperSolution(ClipperLib.PolyTree solution) { Func <ClipperLib.IntPoint, Poly2Tri.PolygonPoint> xfToPolygonPoint = (p) => new Poly2Tri.PolygonPoint(p.X, p.Y); Poly2Tri.PolygonSet polygonSet = new Poly2Tri.PolygonSet(); ClipperLib.PolyNode node = solution.GetFirst(); while (node != null) { // Only interested in closed paths if (!node.IsOpen) { if (node.IsHole) { if (polygonSet.Polygons.Count() > 0) { // Add hole to last polygon entered var polyPoints = node.Contour.Select(xfToPolygonPoint).ToArray(); Poly2Tri.Polygon hole = new Poly2Tri.Polygon(polyPoints); Poly2Tri.Polygon polygon = polygonSet.Polygons.Last(); polygon.AddHole(hole); } } else { // Add a new polygon to the set var polyPoints = node.Contour.Select(xfToPolygonPoint).ToList(); Poly2Tri.Polygon polygon = new Poly2Tri.Polygon(polyPoints); polygonSet.Add(polygon); } } node = node.GetNext(); } // Now triangulate the whole set Poly2Tri.P2T.Triangulate(polygonSet); // Combine all the triangles into one list List <Poly2Tri.DelaunayTriangle> triangles = new List <Poly2Tri.DelaunayTriangle>(); foreach (var polygon in polygonSet.Polygons) { triangles.AddRange(polygon.Triangles); } return(triangles); }
private void ExtractOutlines(ClipperLib.PolyNode tree, List <IShape> shapes) { if (tree.Contour.Any()) { // convert the Clipper Contour from scaled ints back down to the origional size (this is going to be lossy but not significantly) var pointCount = tree.Contour.Count; var vectors = new Vector2[pointCount]; for (var i = 0; i < pointCount; i++) { var p = tree.Contour[i]; vectors[i] = new Vector2(p.X, p.Y) / ClipperScaleFactor; } var polygon = new Polygon(new LinearLineSegment(vectors)); shapes.Add(polygon); } foreach (var c in tree.Childs) { this.ExtractOutlines(c, shapes); } }
public List <PointF[]> Triangulate(ClipperLib.PolyTree solution) { List <PointF[]> triangles = new List <PointF[]>(); var tess = new LibTessDotNet.Tess(); tess.NoEmptyPolygons = true; // Transformation function from ClipperLip Point to LibTess contour vertex Func <ClipperLib.IntPoint, LibTessDotNet.ContourVertex> xfToContourVertex = (p) => new LibTessDotNet.ContourVertex() { Position = new LibTessDotNet.Vec3 { X = p.X, Y = p.Y, Z = 0 } }; // Add a contour for each part of the solution tree ClipperLib.PolyNode node = solution.GetFirst(); while (node != null) { // Only interested in closed paths if (!node.IsOpen) { // Add a new countor. Holes are automatically generated. var vertices = node.Contour.Select(xfToContourVertex).ToArray(); tess.AddContour(vertices); } node = node.GetNext(); } // Do the tessellation tess.Tessellate(LibTessDotNet.WindingRule.EvenOdd, LibTessDotNet.ElementType.Polygons, 3); // Extract the triangles int numTriangles = tess.ElementCount; for (int i = 0; i < numTriangles; i++) { var v0 = tess.Vertices[tess.Elements[i * 3 + 0]].Position; var v1 = tess.Vertices[tess.Elements[i * 3 + 1]].Position; var v2 = tess.Vertices[tess.Elements[i * 3 + 2]].Position; List <PointF> triangle = new List <PointF>() { new PointF(v0.X, v0.Y), new PointF(v1.X, v1.Y), new PointF(v2.X, v2.Y), }; // Assre each triangle needs to be CCW float cross = Geometry.Math.Cross(triangle[0], triangle[1], triangle[2]); if (cross > 0) { triangle.Reverse(); } triangles.Add(triangle.ToArray()); } return(triangles); }