public TriangleNet.Mesh BuildMesh() { var polygon = BuildPolygon(); var constraintOptions = new TriangleNet.Meshing.ConstraintOptions(); constraintOptions.ConformingDelaunay = true; var qualityOptions = new TriangleNet.Meshing.QualityOptions(); qualityOptions.MinimumAngle = 25; qualityOptions.MaximumAngle = 180; qualityOptions.MaximumArea = 2500; var mesh = (TriangleNet.Mesh)TriangleNet.Geometry.ExtensionMethods.Triangulate(polygon.polygon, constraintOptions, qualityOptions); return(mesh); }
public static List <Polyline> DelaunayTriangulation(this Polyline outerCurve, List <Polyline> innerCurves = null, double offsetDistance = -0.001, bool conformingDelaunay = true) { if (outerCurve == null) { BH.Engine.Base.Compute.RecordError("Cannot perform Delaunay Triangulation on an outer curve that is set to null."); return(new List <Polyline>()); } // Create a zero length list if no holes input if (innerCurves == null) { innerCurves = new List <Polyline>(); } double area = outerCurve.Area(); for (int x = 0; x < innerCurves.Count; x++) { Polyline pLine = outerCurve.BooleanDifference(new List <Polyline> { innerCurves[x] })[0]; if (pLine.Area() != area) { //The boolean difference returned a different polyline - offset this inner curve innerCurves[x] = innerCurves[x].Offset(offsetDistance); } } // Get the transformation matrix Plane plane = outerCurve.IFitPlane(); Vector normal = plane.Normal; List <Point> vertices = outerCurve.IDiscontinuityPoints(); Point refPoint = vertices.Min(); Point refPointP = BH.Engine.Geometry.Create.Point(refPoint.X, refPoint.Y, 0); Vector zVector = BH.Engine.Geometry.Create.Vector(0, 0, 1); Vector rotationVector = normal.CrossProduct(zVector).Normalise(); double rotationAngle = normal.Angle(zVector); TransformMatrix transformMatrix = BH.Engine.Geometry.Create.RotationMatrix(vertices.Min(), rotationVector, rotationAngle); // Get the translation vector Vector translateVector = refPointP - refPoint; // Transform the original input curve/s Polyline transformedCurve = Modify.Translate(outerCurve.Transform(transformMatrix), translateVector); List <Polyline> transformedHole = new List <Polyline>(); foreach (Polyline h in innerCurves) { if (h.IsCoplanar(outerCurve)) { transformedHole.Add(Modify.Translate(h.Transform(transformMatrix), translateVector)); } } // Convert geometry to Triangle inputs TriangleNet.Geometry.Polygon parentPolygon = new TriangleNet.Geometry.Polygon(); List <TriangleNet.Geometry.Vertex> parentVertices = new List <TriangleNet.Geometry.Vertex>(); foreach (Point point in transformedCurve.IDiscontinuityPoints()) { parentPolygon.Add(new TriangleNet.Geometry.Vertex(point.X, point.Y)); parentVertices.Add(new TriangleNet.Geometry.Vertex(point.X, point.Y)); } TriangleNet.Geometry.Contour parentContour = new TriangleNet.Geometry.Contour(parentVertices); parentPolygon.Add(parentContour); foreach (Polyline h in transformedHole) { List <TriangleNet.Geometry.Vertex> childVertices = new List <TriangleNet.Geometry.Vertex>(); foreach (Point point in h.IDiscontinuityPoints()) { childVertices.Add(new TriangleNet.Geometry.Vertex(point.X, point.Y)); } TriangleNet.Geometry.Contour childContour = new TriangleNet.Geometry.Contour(childVertices); Point childCentroid = h.PointInRegion(); parentPolygon.Add(childContour, new TriangleNet.Geometry.Point(childCentroid.X, childCentroid.Y)); } // Triangulate TriangleNet.Meshing.ConstraintOptions options = new TriangleNet.Meshing.ConstraintOptions() { ConformingDelaunay = conformingDelaunay, }; TriangleNet.Meshing.QualityOptions quality = new TriangleNet.Meshing.QualityOptions() { }; TriangleNet.Mesh mesh = (TriangleNet.Mesh)TriangleNet.Geometry.ExtensionMethods.Triangulate(parentPolygon, options, quality); // Convert triangulations back to BHoM geometry List <Polyline> translatedPolylines = new List <Polyline>(); foreach (var face in mesh.Triangles) { // List points defining the triangle List <Point> pts = new List <Point>(); pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(0).X, face.GetVertex(0).Y)); pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(1).X, face.GetVertex(1).Y)); pts.Add(BH.Engine.Geometry.Create.Point(face.GetVertex(2).X, face.GetVertex(2).Y)); pts.Add(pts.First()); translatedPolylines.Add(BH.Engine.Geometry.Create.Polyline(pts)); } // Translate back to original plane List <Polyline> meshPolylines = new List <Polyline>(); foreach (Polyline pl in translatedPolylines) { TransformMatrix matrixTransposed = transformMatrix.Invert(); Polyline meshPolyline = pl.Translate(-translateVector).Transform(transformMatrix.Invert()); meshPolylines.Add(meshPolyline); } return(meshPolylines); }
// TODO Generalize Slice to be more than just a plane (Maybe a parametric surface or NURB?) // Implement with Binary Space Partitioning private Triangle[] PatchSlice(Plane Slice) // this function triangulates the slicing plane so that the split parts are watertight { var PlanePts = new HashSet <double3>(); // these are the points in the plane var PlaneTris = new List <Triangle>(); // these will be holes or required triangles depending on the unit normal var PlaneLines = new HashSet <Line>(); // these are required edges in the surface triangulation for (int i = 0; i < Triangles.Length; i++) { var Tri = Triangles[i]; var LocA = Slice.AboveOrBelow(Tri.A); var LocB = Slice.AboveOrBelow(Tri.B); var LocC = Slice.AboveOrBelow(Tri.C); Tri.A = ToDouble3(ToFloat3(Tri.A)); Tri.B = ToDouble3(ToFloat3(Tri.B)); Tri.C = ToDouble3(ToFloat3(Tri.C)); if (LocA == Location.On) { PlanePts.Add(Tri.A); } if (LocB == Location.On) { PlanePts.Add(Tri.B); } if (LocC == Location.On) { PlanePts.Add(Tri.C); } if (LocA == Location.On && LocB == Location.On && LocC == Location.On) { PlaneTris.Add(Tri); } else if (LocA == Location.On && LocB == Location.On) { PlaneLines.Add(new Line(Tri.A, Tri.B)); } else if (LocB == Location.On && LocC == Location.On) { PlaneLines.Add(new Line(Tri.B, Tri.C)); } else if (LocA == Location.On && LocB == Location.On) { PlaneLines.Add(new Line(Tri.A, Tri.C)); } } var Pts3D = PlanePts.ToArray(); var Lines = PlaneLines.ToArray(); var x_new = Pts3D[1] - Pts3D[0]; x_new.Normalize(); // define the new x-axis as the vector between the first two // coplanar points var z_new = Slice.UnitNormal; var Poly = new Polygon(); double ZOffset = Slice.Transform(Pts3D[0], x_new).z; for (int i = 0; i < Pts3D.Length; i++) { var Pt_new = Slice.Transform(Pts3D[i], x_new); var Vertex_new = new Vertex(Pt_new.x, Pt_new.y); Poly.Add(Vertex_new); } var EdgeSet = new HashSet <Edge>(); for (int i = 0; i < Lines.Length; i++) { var PtA = Lines[i].A; var PtB = Lines[i].B; PtA = Slice.Transform(PtA, x_new); PtB = Slice.Transform(PtB, x_new); var P1 = new Vertex(PtA.x, PtA.y); var P2 = new Vertex(PtB.x, PtB.y); int I1 = Poly.Points.IndexOf(P1); int I2 = Poly.Points.IndexOf(P2); if (I1 == -1 || I2 == -1) { Console.WriteLine("Bad Segment Found!"); } var Edge = new Edge(Math.Min(I1, I2), Math.Max(I1, I2)); EdgeSet.Add(Edge); } var PolyEdges = EdgeSet.ToArray(); foreach (var item in PolyEdges) { Poly.Add(item); } //Poly = RemoveRedundantSegments(Poly.Segments, Poly.Points); bool Status = CheckWaterTightness(Poly.Segments, Poly.Points); TriangleNet.IO.TriangleWriter.WritePoly(Poly, "PolyTest.poly"); if (Status == false) { Console.WriteLine("Warning, Inconsistent cross section detected!"); //throw new Exception("STL File is not watertight!"); } var qualityOptions = new TriangleNet.Meshing.QualityOptions(); qualityOptions.MinimumAngle = 20; qualityOptions.MaximumAngle = 140; var myMesher = new TriangleNet.Meshing.GenericMesher(); var myMesh = (TriangleNet.Mesh)myMesher.Triangulate(Poly, qualityOptions); // Poly needs to be broken up into self contained singular regions // TODO: // How does this fair when the cross section is multiple separate regions adhering to Jordan Curve Theorem // Each region/ closed curve is a linked list. // FIXME: Write algorithm to break up seperate regions along the plane // Also account for holes being punched in the mesh (One side gets a hole, the mirror doesnt) when the cutting plane is along an interior surface // TODO Add back in unit normals to patch var Ans = new Triangle[myMesh.Triangles.Count]; if (Ans.Length == 0) { throw new Exception("Patch Meshing Failure Detected!"); } TriangleNet.IO.TriangleWriter.Write(myMesh, "MeshTest.ele"); for (int i = 0; i < Ans.Length; i++) { var myTri = myMesh.Triangles.ElementAt(i); var P0 = myMesh.Vertices.ElementAt(myTri.P0); var P1 = myMesh.Vertices.ElementAt(myTri.P1); var P2 = myMesh.Vertices.ElementAt(myTri.P2); var PtA = new double3(P0.X, P0.Y, ZOffset); var PtB = new double3(P1.X, P1.Y, ZOffset); var PtC = new double3(P2.X, P2.Y, ZOffset); PtA = Slice.UnTransform(PtA, x_new); // map back to 3d PtB = Slice.UnTransform(PtB, x_new); PtC = Slice.UnTransform(PtC, x_new); Ans[i] = new Triangle(PtA, PtB, PtC); } return(Ans); }