/// <summary> /// extract largest shell of meshIn /// </summary> public static DMesh3 LargestT(DMesh3 meshIn) { var c = new MeshConnectedComponents(meshIn); c.FindConnectedT(); c.SortByCount(false); var submesh = new DSubmesh3(meshIn, c.Components[0].Indices); return(submesh.SubMesh); }
/// <summary> /// Construct submesh set for an already-computed MeshConnectedComponents instance /// </summary> public DSubmesh3Set(DMesh3 mesh, MeshConnectedComponents components) { Mesh = mesh; TriangleSetF = (idx) => { return(components.Components[(int)idx].Indices); }; List <object> keys = new List <object>(); for (int k = 0; k < components.Count; ++k) { keys.Add(k); } TriangleSetKeys = keys; ComputeSubMeshes(); }
// assumes PathT is contains set of triangles // that are fully connected, ie a flood-fill cannot escape! void find_interior_from_tris() { var pathNbrs = new MeshFaceSelection(Mesh); pathNbrs.Select(PathT); pathNbrs.ExpandToOneRingNeighbours(); pathNbrs.Deselect(PathT); var connected = new MeshConnectedComponents(Mesh); connected.FilterSet = pathNbrs; connected.FindConnectedT(); int N = connected.Count; if (N < 2) { throw new Exception("MeshFacesFromLoop.find_interior: only found one connected component!"); } // only consider 2 largest. somehow we are sometimes getting additional // "outside" components, and if we do growing from there, it covers whole mesh?? connected.SortByCount(false); N = 2; var selections = new MeshFaceSelection[N]; bool[] done = new bool[N]; for (int i = 0; i < N; ++i) { selections[i] = new MeshFaceSelection(Mesh); selections[i].Select(connected.Components[i].Indices); done[i] = false; } var border_tris = new HashSet <int>(PathT); Func <int, bool> borderF = (tid) => { return(border_tris.Contains(tid) == false); }; // 'largest' flood fill is expensive...if we had a sense of tooth size we could reduce cost? for (int i = 0; i < N; ++i) { selections[i].FloodFill(connected.Components[i].Indices, borderF); } Array.Sort(selections, (a, b) => { return(a.Count.CompareTo(b.Count)); }); InteriorT = new List <int>(selections[0]); }
/// <summary> /// Separate input mesh into disconnected shells /// </summary> public static DMesh3[] Separate(DMesh3 meshIn) { MeshConnectedComponents c = new MeshConnectedComponents(meshIn); c.FindConnectedT(); DMesh3[] result = new DMesh3[c.Components.Count]; int ri = 0; foreach (Component comp in c.Components) { DSubmesh3 submesh = new DSubmesh3(meshIn, comp.Indices); result[ri++] = submesh.SubMesh; } return(result); }
public static GenusResult Genus(DMesh3 mesh) { GenusResult result = new GenusResult() { Valid = false, Genus = -1 }; if (!mesh.CachedIsClosed) { result.HasBoundary = true; return(result); } MeshConnectedComponents compT = new MeshConnectedComponents(mesh); compT.FindConnectedT(); if (compT.Count > 1) { result.MultipleConnectedComponents = true; return(result); } int isolated_verts = 0; foreach (int vid in mesh.VertexIndices()) { if (mesh.IsBowtieVertex(vid)) { result.HasBowtieVertices = true; return(result); } if (mesh.GetVtxTriangleCount(vid) == 0) { isolated_verts++; } } int V = mesh.VertexCount - isolated_verts; int F = mesh.TriangleCount; int E = mesh.EdgeCount; result.Genus = (2 - (V + F - E)) / 2; result.Valid = true; return(result); }
/// <summary> /// Remove any connected components with volume < min_volume area lt; min_area /// </summary> public int RemoveSmallComponents(double min_volume, double min_area) { MeshConnectedComponents C = new MeshConnectedComponents(Mesh); C.FindConnectedT(); if (C.Count == 1) { return(0); } int nRemoved = 0; foreach (var comp in C.Components) { Vector2d vol_area = MeshMeasurements.VolumeArea(Mesh, comp.Indices, Mesh.GetVertex); if (vol_area.x < min_volume || vol_area.y < min_area) { MeshEditor.RemoveTriangles(Mesh, comp.Indices); nRemoved++; } } return(nRemoved); }
public virtual bool Trim() { if (Spatial == null) { Spatial = new DMeshAABBTree3(new DMesh3(Mesh, false, MeshComponents.None)); Spatial.Build(); } if (seed_tri == -1) { seed_tri = Spatial.FindNearestTriangle(seed_pt); } var loop = new MeshFacesFromLoop(Mesh, TrimLine, Spatial, seed_tri); MeshFaceSelection selection = loop.ToSelection(); selection.LocalOptimize(true, true); var editor = new MeshEditor(Mesh); editor.RemoveTriangles(selection, true); var components = new MeshConnectedComponents(Mesh); components.FindConnectedT(); if (components.Count > 1) { int keep = components.LargestByCount; for (int i = 0; i < components.Count; ++i) { if (i != keep) { editor.RemoveTriangles(components[i].Indices, true); } } } editor.RemoveAllBowtieVertices(true); var loops = new MeshBoundaryLoops(Mesh); bool loopsOK = false; try { loopsOK = loops.Compute(); } catch (Exception) { return(false); } if (!loopsOK) { return(false); } // [TODO] to support trimming mesh w/ existing holes, we need to figure out which // loop we created in RemoveTriangles above! if (loops.Count > 1) { return(false); } int[] loopVerts = loops[0].Vertices; var borderTris = new MeshFaceSelection(Mesh); borderTris.SelectVertexOneRings(loopVerts); borderTris.ExpandToOneRingNeighbours(RemeshBorderRings); var remesh = new RegionRemesher(Mesh, borderTris.ToArray()); remesh.Region.MapVerticesToSubmesh(loopVerts); double target_len = TargetEdgeLength; if (target_len <= 0) { double mine, maxe, avge; MeshQueries.EdgeLengthStatsFromEdges(Mesh, loops[0].Edges, out mine, out maxe, out avge); target_len = avge; } var meshTarget = new MeshProjectionTarget(Spatial.Mesh, Spatial); remesh.SetProjectionTarget(meshTarget); remesh.SetTargetEdgeLength(target_len); remesh.SmoothSpeedT = SmoothingAlpha; var curveTarget = new DCurveProjectionTarget(TrimLine); var multiTarget = new SequentialProjectionTarget(curveTarget, meshTarget); int set_id = 3; MeshConstraintUtil.ConstrainVtxLoopTo(remesh, loopVerts, multiTarget, set_id); for (int i = 0; i < RemeshRounds; ++i) { remesh.BasicRemeshPass(); } remesh.BackPropropagate(); // [TODO] output loop somehow...use MeshConstraints.FindConstrainedEdgesBySetID(set_id)... return(true); } // Trim()