private TriangleNet.Mesh createMesh() { TriangleNet.Geometry.Polygon polygon = createPolygon(); TriangleNet.Meshing.ConstraintOptions options = new TriangleNet.Meshing.ConstraintOptions() { ConformingDelaunay = true }; TriangleNet.Meshing.GenericMesher mesher = new TriangleNet.Meshing.GenericMesher(); TriangleNet.Mesh mesh = (TriangleNet.Mesh)mesher.Triangulate(polygon, options); TriangleNet.Smoothing.SimpleSmoother smoother = new TriangleNet.Smoothing.SimpleSmoother(); smoother.Smooth(mesh, smoothingIterations); return(mesh); }
public void Build(QuadTreeNode Mask, double compensateangle = 0.0) { // M = new Mesh(); Lines.Clear(); Mask.NodeWalker(GetAllCorners, true, true); var GM = new TriangleNet.Meshing.GenericMesher(); if (IG.Count < 3) { M = null; return; } else { M = GM.Triangulate(IG); } //M.Triangulate(IG); //M.Smooth(); //M.Refine(); //M.Refine(); double S = Math.Sin(compensateangle); double C = Math.Cos(compensateangle); foreach (var a in M.Triangles) { var V1 = M.Vertices.ElementAt(a.GetVertexID(0)); var V2 = M.Vertices.ElementAt(a.GetVertexID(1)); var V3 = M.Vertices.ElementAt(a.GetVertexID(2)); QuadTreeNode N = Mask.GetNode((V1.X + V2.X + V3.X) / 3.0, (V1.Y + V2.Y + V3.Y) / 3.0); if (N != null && N.Items.Count == 0) { var D1 = MathHelpers.Difference(new PointF((float)V1.X, (float)V1.Y), new PointF((float)V2.X, (float)V2.Y)); var D2 = MathHelpers.Difference(new PointF((float)V2.X, (float)V2.Y), new PointF((float)V3.X, (float)V3.Y)); var D3 = MathHelpers.Difference(new PointF((float)V3.X, (float)V3.Y), new PointF((float)V1.X, (float)V1.Y)); var D1L = MathHelpers.Length(D1); var D2L = MathHelpers.Length(D2); var D3L = MathHelpers.Length(D3); if (D1L < D2L && D1L < D3L) { // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V2.X, (float)V2.Y), new PointF((float)V3.X, (float)V3.Y))); // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V3.X, (float)V3.Y), new PointF((float)V1.X, (float)V1.Y))); Lines.Add(new Tuple <PointF, PointF>(new PointF((float)V1.X, (float)V1.Y), new PointF((float)V2.X, (float)V2.Y))); } if (D2L < D1L && D2L < D3L) { Lines.Add(new Tuple <PointF, PointF>(new PointF((float)V2.X, (float)V2.Y), new PointF((float)V3.X, (float)V3.Y))); // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V1.X, (float)V1.Y), new PointF((float)V2.X, (float)V2.Y))); // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V3.X, (float)V3.Y), new PointF((float)V1.X, (float)V1.Y))); } if (D3L < D2L && D3L < D1L) { Lines.Add(new Tuple <PointF, PointF>(new PointF((float)V3.X, (float)V3.Y), new PointF((float)V1.X, (float)V1.Y))); // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V1.X, (float)V1.Y), new PointF((float)V2.X, (float)V2.Y))); // Lines.Add(new Tuple<PointF, PointF>(new PointF((float)V2.X, (float)V2.Y), new PointF((float)V3.X, (float)V3.Y))); } } } Matrix mM = new Matrix(); mM.Rotate((float)compensateangle); List <Tuple <PointF, PointF> > l2 = new List <Tuple <PointF, PointF> >(); foreach (var a in Lines) { PointF[] A = new PointF[2] { a.Item1, a.Item2 }; mM.TransformPoints(A); l2.Add(new Tuple <PointF, PointF>(A[0], A[1])); } Lines = l2; }
// 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); }