void find_crease_edges(double angle_tol) { CreaseEdges = new HashSet <int>(); BoundaryEdges = new HashSet <int>(); double dot_tol = Math.Cos(angle_tol * MathUtil.Deg2Rad); foreach (int eid in Mesh.EdgeIndices()) { Index2i et = Mesh.GetEdgeT(eid); if (et.b == DMesh3.InvalidID) { BoundaryEdges.Add(eid); continue; } Vector3d n0 = Mesh.GetTriNormal(et.a); Vector3d n1 = Mesh.GetTriNormal(et.b); if (Math.Abs(n0.Dot(n1)) < dot_tol) { CreaseEdges.Add(eid); } } AllEdges = new HashSet <int>(CreaseEdges);; foreach (int eid in BoundaryEdges) { AllEdges.Add(eid); } AllVertices = new HashSet <int>(); IndexUtil.EdgesToVertices(Mesh, AllEdges, AllVertices); }
void add_all_edges(int ei, HashSet <int> edge_set) { Index2i et = fillmesh.GetEdgeT(ei); Index3i te = fillmesh.GetTriEdges(et.a); edge_set.Add(te.a); edge_set.Add(te.b); edge_set.Add(te.c); te = fillmesh.GetTriEdges(et.b); edge_set.Add(te.a); edge_set.Add(te.b); edge_set.Add(te.c); }
List <int> get_edge_tris(DMesh3 mesh, int eid) { Index2i et = mesh.GetEdgeT(eid); List <int> tris = new List <int>() { et.a }; if (et.b != DMesh3.InvalidID) { tris.Add(et.b); } return(tris); }
UseFillType classify_hole() { return(UseFillType.MinimalFill); #if false int NV = FillLoop.VertexCount; int NE = FillLoop.EdgeCount; Vector3d size = FillLoop.ToCurve().GetBoundingBox().Diagonal; NormalHistogram hist = new NormalHistogram(4096, true); for (int k = 0; k < NE; ++k) { int eid = FillLoop.Edges[k]; Index2i et = Mesh.GetEdgeT(eid); Vector3d n = Mesh.GetTriNormal(et.a); hist.Count(n, 1.0, true); } if (hist.UsedBins.Count == 1) { return(UseFillType.PlanarFill); } //int nontrivial_bins = 0; //foreach ( int bin in hist.UsedBins ) { // if (hist.Counts[bin] > 8) // nontrivial_bins++; //} //if (nontrivial_bins > 0) // return UseFillType.PlanarSpansFill; return(UseFillType.SmoothFill); #endif }
// NO DOES NOT WORK. DOES NOT FIND EDGE SPANS THAT ARE IN PLANE BUT HAVE DIFFERENT NORMAL! // NEED TO COLLECT UP SPANS USING NORMAL HISTOGRAM NORMALS! // ALSO NEED TO ACTUALLY CHECK FOR COPLANARITY, NOT JUST SAME NORMAL!! Dictionary <Vector3d, List <EdgeSpan> > find_coplanar_span_sets(DMesh3 mesh, EdgeLoop loop) { double dot_thresh = 0.999; var span_sets = new Dictionary <Vector3d, List <EdgeSpan> >(); int NV = loop.Vertices.Length; int NE = loop.Edges.Length; var edge_normals = new Vector3d[NE]; for (int k = 0; k < NE; ++k) { edge_normals[k] = mesh.GetTriNormal(mesh.GetEdgeT(loop.Edges[k]).a); } // find coplanar verts // [RMS] this is wrong, if normals vary smoothly enough we will mark non-coplanar spans as coplanar bool[] vert_coplanar = new bool[NV]; int nc = 0; for (int k = 0; k < NV; ++k) { int prev = (k == 0) ? NV - 1 : k - 1; if (edge_normals[k].Dot(ref edge_normals[prev]) > dot_thresh) { vert_coplanar[k] = true; nc++; } } if (nc < 2) { return(null); } int iStart = 0; while (vert_coplanar[iStart]) { iStart++; } int iPrev = iStart; int iCur = iStart + 1; while (iCur != iStart) { if (vert_coplanar[iCur] == false) { iPrev = iCur; iCur = (iCur + 1) % NV; continue; } var edges = new List <int>() { loop.Edges[iPrev] }; int span_start_idx = iCur; while (vert_coplanar[iCur]) { edges.Add(loop.Edges[iCur]); iCur = (iCur + 1) % NV; } if (edges.Count > 1) { Vector3d span_n = edge_normals[span_start_idx]; var span = EdgeSpan.FromEdges(mesh, edges); span.CheckValidity(); foreach (var pair in span_sets) { if (pair.Key.Dot(ref span_n) > dot_thresh) { span_n = pair.Key; break; } } List <EdgeSpan> found; if (span_sets.TryGetValue(span_n, out found) == false) { span_sets[span_n] = new List <EdgeSpan>() { span }; } else { found.Add(span); } } } return(span_sets); }
List <Polygon2d> decompose_cluster_up(DMesh3 mesh) { optimize_mesh(mesh); mesh.CompactInPlace(); mesh.DiscardTriangleGroups(); mesh.EnableTriangleGroups(0); double minLength = Settings.MaxBridgeWidthMM * 0.75; double minArea = minLength * minLength; Dictionary <int, double> areas = new Dictionary <int, double>(); Dictionary <int, HashSet <int> > trisets = new Dictionary <int, HashSet <int> >(); HashSet <int> active_groups = new HashSet <int>(); Action <int, int> add_tri_to_group = (tid, gid) => { mesh.SetTriangleGroup(tid, gid); areas[gid] = areas[gid] + mesh.GetTriArea(tid); trisets[gid].Add(tid); }; Action <int, int> add_group_to_group = (gid, togid) => { var set = trisets[togid]; foreach (int tid in trisets[gid]) { mesh.SetTriangleGroup(tid, togid); set.Add(tid); } areas[togid] += areas[gid]; active_groups.Remove(gid); }; Func <IEnumerable <int>, int> find_min_area_group = (tri_itr) => { int min_gid = -1; double min_area = double.MaxValue; foreach (int tid in tri_itr) { int gid = mesh.GetTriangleGroup(tid); double a = areas[gid]; if (a < min_area) { min_area = a; min_gid = gid; } } return(min_gid); }; foreach (int eid in MeshIterators.InteriorEdges(mesh)) { Index2i et = mesh.GetEdgeT(eid); if (mesh.GetTriangleGroup(et.a) != 0 || mesh.GetTriangleGroup(et.b) != 0) { continue; } int gid = mesh.AllocateTriangleGroup(); areas[gid] = 0; trisets[gid] = new HashSet <int>(); active_groups.Add(gid); add_tri_to_group(et.a, gid); add_tri_to_group(et.b, gid); } foreach (int tid in mesh.TriangleIndices()) { if (mesh.GetTriangleGroup(tid) != 0) { continue; } int gid = find_min_area_group(mesh.TriTrianglesItr(tid)); add_tri_to_group(tid, gid); } IndexPriorityQueue pq = new IndexPriorityQueue(mesh.MaxGroupID); foreach (var pair in areas) { pq.Insert(pair.Key, (float)pair.Value); } while (pq.Count > 0) { int gid = pq.First; pq.Remove(gid); if (areas[gid] > minArea) // ?? { break; } List <int> nbr_groups = find_neighbour_groups(mesh, gid, trisets[gid]); int min_gid = -1; double min_area = double.MaxValue; foreach (int ngid in nbr_groups) { double a = areas[ngid]; if (a < min_area) { min_area = a; min_gid = ngid; } } if (min_gid != -1) { add_group_to_group(gid, min_gid); pq.Remove(min_gid); pq.Insert(min_gid, (float)areas[min_gid]); } } List <Polygon2d> result = new List <Polygon2d>(); int[][] sets = FaceGroupUtil.FindTriangleSetsByGroup(mesh); foreach (var set in sets) { result.Add(make_poly(mesh, set)); } return(result); }
public static void test_uv_insert_string() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_xy_25x25.obj"); mesh.EnableVertexUVs(Vector2f.Zero); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); PolygonFont2d font = PolygonFont2d.ReadFont("c:\\scratch\\font.bin"); //List<GeneralPolygon2d> letter = new List<GeneralPolygon2d>(font.Characters.First().Value.Polygons); //double targetWidth = 20.0f; List <GeneralPolygon2d> letter = font.GetCharacter('a'); double targetWidth = 10.0f; AxisAlignedBox2d bounds = font.MaxBounds; Vector2d center = bounds.Center; Vector2d scale2d = (targetWidth / font.MaxBounds.Width) * new Vector2d(1, 1); for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = new GeneralPolygon2d(letter[li]); gp.Scale(scale2d, center); gp.Translate(-center); letter[li] = gp; } List <MeshFaceSelection> letter_interiors = new List <MeshFaceSelection>(); bool bSimplify = true; for (int li = 0; li < letter.Count; ++li) { GeneralPolygon2d gp = letter[li]; MeshInsertUVPolyCurve outer = new MeshInsertUVPolyCurve(mesh, gp.Outer); Util.gDevAssert(outer.Validate() == ValidationStatus.Ok); outer.Apply(); if (bSimplify) { outer.Simplify(); } List <MeshInsertUVPolyCurve> holes = new List <MeshInsertUVPolyCurve>(gp.Holes.Count); for (int hi = 0; hi < gp.Holes.Count; ++hi) { MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, gp.Holes[hi]); Util.gDevAssert(insert.Validate() == ValidationStatus.Ok); insert.Apply(); if (bSimplify) { insert.Simplify(); } holes.Add(insert); } // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; EdgeLoop outer_loop = outer.Loops[0]; for (int i = 0; i < outer_loop.EdgeCount; ++i) { if (!mesh.IsEdge(outer_loop.Edges[i])) { continue; } Index2i et = mesh.GetEdgeT(outer_loop.Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = gp.Outer.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = gp.Outer.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // make list of all outer & hole edges HashSet <int> loopEdges = new HashSet <int>(outer_loop.Edges); foreach (var insertion in holes) { foreach (int eid in insertion.Loops[0].Edges) { loopEdges.Add(eid); } } // flood-fill inside loop from seed triangle MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); letter_interiors.Add(sel); } // extrude regions Func <Vector3d, Vector3f, int, Vector3d> OffsetF = (v, n, i) => { return(v + Vector3d.AxisZ); }; foreach (var interior in letter_interiors) { MeshExtrudeFaces extrude = new MeshExtrudeFaces(mesh, interior); extrude.ExtrudedPositionF = OffsetF; extrude.Extrude(); } TestUtil.WriteTestOutputMesh(mesh, "insert_uv_string.obj"); }
public static void test_uv_insert_segment() { DMesh3 mesh = TestUtil.LoadTestInputMesh("plane_250v.obj"); mesh.EnableVertexUVs(Vector2f.Zero); MeshTransforms.ConvertYUpToZUp(mesh); DMeshAABBTree3 spatial = new DMeshAABBTree3(mesh); spatial.Build(); int tid = spatial.FindNearestTriangle(Vector3d.Zero); //Polygon2d poly = Polygon2d.MakeRectangle(Vector2d.Zero, 5, 5); Polygon2d poly = Polygon2d.MakeCircle(5, 13); //PolyLine2d poly = new PolyLine2d( new Vector2d[] { -5 * Vector2d.One, 5 * Vector2d.One }); //int tri_edge0 = mesh.GetTriEdge(tid, 0); //Index2i edge0_tris = mesh.GetEdgeT(tri_edge0); //Index2i edge0_verts = mesh.GetEdgeV(tri_edge0); //Vector3d v0 = mesh.GetVertex(edge0_verts.a), v1 = mesh.GetVertex(edge0_verts.b); //Vector3d c = mesh.GetTriCentroid(tid); //Polygon2d poly = new Polygon2d(new Vector2d[] { // Vector2d.Lerp(v0.xy, v1.xy, -0.25), // Vector2d.Lerp(v0.xy, v1.xy, 1.5), // c.xy //}); MeshInsertUVPolyCurve insert = new MeshInsertUVPolyCurve(mesh, poly); insert.Apply(); Polygon2d test_poly = new Polygon2d(); List <double> distances = new List <double>(); List <int> nearests = new List <int>(); for (int i = 0; i < insert.Loops[0].VertexCount; ++i) { Vector2d v = mesh.GetVertex(insert.Loops[0].Vertices[i]).xy; test_poly.AppendVertex(v); int iNear; double fNear; distances.Add(poly.DistanceSquared(v, out iNear, out fNear)); nearests.Add(iNear); } System.Console.WriteLine("inserted loop poly has {0} edges", insert.Loops[0].EdgeCount); // find a triangle connected to loop that is inside the polygon // [TODO] maybe we could be a bit more robust about this? at least // check if triangle is too degenerate... int seed_tri = -1; for (int i = 0; i < insert.Loops[0].EdgeCount; ++i) { Index2i et = mesh.GetEdgeT(insert.Loops[0].Edges[i]); Vector3d ca = mesh.GetTriCentroid(et.a); bool in_a = poly.Contains(ca.xy); Vector3d cb = mesh.GetTriCentroid(et.b); bool in_b = poly.Contains(cb.xy); if (in_a && in_b == false) { seed_tri = et.a; break; } else if (in_b && in_a == false) { seed_tri = et.b; break; } } Util.gDevAssert(seed_tri != -1); // flood-fill inside loop HashSet <int> loopEdges = new HashSet <int>(insert.Loops[0].Edges); MeshFaceSelection sel = new MeshFaceSelection(mesh); sel.FloodFill(seed_tri, null, (eid) => { return(loopEdges.Contains(eid) == false); }); // delete inside loop MeshEditor editor = new MeshEditor(mesh); editor.RemoveTriangles(sel, true); MeshTransforms.ConvertZUpToYUp(mesh); TestUtil.WriteTestOutputMesh(mesh, "insert_uv_segment.obj"); //OBJWriter writer = new OBJWriter(); //var s = new System.IO.StreamWriter(Program.TEST_OUTPUT_PATH + "mesh_local_param.obj", false); //List<WriteMesh> wm = new List<WriteMesh>() { new WriteMesh(mesh) }; //WriteOptions opt = new WriteOptions() { // bCombineMeshes = false, bWriteGroups = false, bPerVertexColors = true, bPerVertexUVs = true, // AsciiHeaderFunc = () => { return "mttllib checkerboard.mtl\r\nusemtl checkerboard\r\n"; } //}; //writer.Write(s, wm, opt); //s.Close(); }