/// <summary> /// split input mesh into submeshes based on group ID /// **does not** separate disconnected components w/ same group ID /// </summary> public static DMesh3[] SeparateMeshByGroups(DMesh3 mesh, out int[] groupIDs) { Dictionary <int, List <int> > meshes = new Dictionary <int, List <int> >(); foreach (int tid in mesh.TriangleIndices()) { int gid = mesh.GetTriangleGroup(tid); List <int> tris; if (meshes.TryGetValue(gid, out tris) == false) { tris = new List <int>(); meshes[gid] = tris; } tris.Add(tid); } DMesh3[] result = new DMesh3[meshes.Count]; groupIDs = new int[meshes.Count]; int k = 0; foreach (var pair in meshes) { groupIDs[k] = pair.Key; List <int> tri_list = pair.Value; result[k++] = DSubmesh3.QuickSubmesh(mesh, tri_list); } return(result); }
/// <summary> /// Apply LaplacianMeshSmoother to subset of mesh triangles. /// border of subset always has soft constraint with borderWeight, /// but is then snapped back to original vtx pos after solve. /// nConstrainLoops inner loops are also soft-constrained, with weight falloff via square roots. /// interiorWeight is soft constraint added to all vertices /// </summary> public static void RegionSmooth(DMesh3 mesh, IEnumerable <int> triangles, int nConstrainLoops, double borderWeight = 10.0, double interiorWeight = 0.0) { RegionOperator region = new RegionOperator(mesh, triangles); DSubmesh3 submesh = region.Region; DMesh3 smoothMesh = submesh.SubMesh; LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(smoothMesh); // soft constraint on all interior vertices, if requested if (interiorWeight > 0) { foreach (int vid in smoothMesh.VertexIndices()) { smoother.SetConstraint(vid, smoothMesh.GetVertex(vid), interiorWeight, false); } } // now constrain borders double w = borderWeight; HashSet <int> constrained = (region.Region.BaseBorderV.Count > 0) ? new HashSet <int>() : null; foreach (int base_vid in region.Region.BaseBorderV) { int sub_vid = submesh.BaseToSubV[base_vid]; smoother.SetConstraint(sub_vid, smoothMesh.GetVertex(sub_vid), w, true); if (constrained != null) { constrained.Add(sub_vid); } } if (constrained.Count > 0) { w = Math.Sqrt(w); for (int k = 0; k < nConstrainLoops; ++k) { HashSet <int> next_layer = new HashSet <int>(); foreach (int sub_vid in constrained) { foreach (int nbr_vid in smoothMesh.VtxVerticesItr(sub_vid)) { if (constrained.Contains(nbr_vid) == false) { smoother.SetConstraint(nbr_vid, smoothMesh.GetVertex(nbr_vid), w, false); next_layer.Add(nbr_vid); } } } constrained.UnionWith(next_layer); w = Math.Sqrt(w); } } smoother.SolveAndUpdateMesh(); region.BackPropropagateVertices(true); }
public RegionOperator(DMesh3 mesh, int[] regionTris) { BaseMesh = mesh; Region = new DSubmesh3(mesh, regionTris); Region.ComputeBoundaryInfo(regionTris); cur_base_tris = (int[])regionTris.Clone(); }
/// <summary> /// Automatically construct fastest projection target for region of mesh /// </summary> public static MeshProjectionTarget Auto(DMesh3 mesh, IEnumerable <int> triangles, int nExpandRings = 5) { var targetRegion = new MeshFaceSelection(mesh); targetRegion.Select(triangles); targetRegion.ExpandToOneRingNeighbours(nExpandRings); var submesh = new DSubmesh3(mesh, targetRegion); return(new MeshProjectionTarget(submesh.SubMesh)); }
/// <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); }
public RegionOperator(DMesh3 mesh, IEnumerable <int> regionTris) { BaseMesh = mesh; Region = new DSubmesh3(mesh, regionTris); int count = regionTris.Count(); Region.ComputeBoundaryInfo(regionTris, count); cur_base_tris = regionTris.ToArray(); }
public RegionOperator(DMesh3 mesh, int[] regionTris, Action <DSubmesh3> submeshConfigF = null) { BaseMesh = mesh; Region = new DSubmesh3(mesh); if (submeshConfigF != null) { submeshConfigF(Region); } Region.Compute(regionTris); Region.ComputeBoundaryInfo(regionTris); cur_base_tris = (int[])regionTris.Clone(); }
public RegionRemesher(DMesh3 mesh, int[] regionTris) { BaseMesh = mesh; Region = new DSubmesh3(mesh, regionTris); Region.ComputeBoundaryInfo(regionTris); base.mesh = Region.SubMesh; cur_base_tris = (int[])regionTris.Clone(); // constrain region-boundary edges bdry_constraints = new MeshConstraints(); MeshConstraintUtil.FixSubmeshBoundaryEdges(bdry_constraints, Region); SetExternalConstraints(bdry_constraints); }
public RegionOperator(DMesh3 mesh, IEnumerable <int> regionTris, Action <DSubmesh3> submeshConfigF = null) { BaseMesh = mesh; Region = new DSubmesh3(mesh); if (submeshConfigF != null) { submeshConfigF(Region); } Region.Compute(regionTris); int count = regionTris.Count(); Region.ComputeBoundaryInfo(regionTris, count); cur_base_tris = regionTris.ToArray(); }
// loop through submesh border edges on basemesh, map to submesh, and // pin those edges / vertices public static void FixSubmeshBoundaryEdges(MeshConstraints cons, DSubmesh3 sub) { Debug.Assert(sub.BaseBorderE != null); foreach (int base_eid in sub.BaseBorderE) { Index2i base_ev = sub.BaseMesh.GetEdgeV(base_eid); Index2i sub_ev = sub.MapVerticesToSubmesh(base_ev); int sub_eid = sub.SubMesh.FindEdge(sub_ev.a, sub_ev.b); Debug.Assert(sub_eid != DMesh3.InvalidID); Debug.Assert(sub.SubMesh.IsBoundaryEdge(sub_eid)); cons.SetOrUpdateEdgeConstraint(sub_eid, EdgeConstraint.FullyConstrained); cons.SetOrUpdateVertexConstraint(sub_ev.a, VertexConstraint.Pinned); cons.SetOrUpdateVertexConstraint(sub_ev.b, VertexConstraint.Pinned); } }
public RegionRemesher(DMesh3 mesh, IEnumerable <int> regionTris) { BaseMesh = mesh; Region = new DSubmesh3(mesh, regionTris); int count = regionTris.Count(); Region.ComputeBoundaryInfo(regionTris, count); base.mesh = Region.SubMesh; cur_base_tris = regionTris.ToArray(); // constrain region-boundary edges bdry_constraints = new MeshConstraints(); MeshConstraintUtil.FixSubmeshBoundaryEdges(bdry_constraints, Region); SetExternalConstraints(bdry_constraints); }
/// <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 DMesh3 QuickSubmesh(DMesh3 mesh, int[] triangles) { DSubmesh3 submesh = new DSubmesh3(mesh, triangles); return(submesh.SubMesh); }
// Assumption here is that Submesh has been modified, but boundary loop has // been preserved, and that old submesh has already been removed from this mesh. // So, we just have to append new vertices and then rewrite triangles // If new_tris or new_verts is non-null, we will return this info. // new_tris should be set to TriangleCount (ie it is not necessarily a map) // For new_verts, if we used an existing bdry vtx instead, we set the value to -(existing_index+1), // otherwise the value is new_index (+1 is to handle 0) // // Returns true if submesh successfully inserted, false if any triangles failed // (which happens if triangle would result in non-manifold mesh) public bool ReinsertSubmesh(DSubmesh3 sub, ref int[] new_tris, out IndexMap SubToNewV) { if (sub.BaseBorderV == null) { throw new Exception("MeshEditor.ReinsertSubmesh: Submesh does not have required boundary info. Call ComputeBoundaryInfo()!"); } DMesh3 submesh = sub.SubMesh; bool bAllOK = true; IndexFlagSet done_v = new IndexFlagSet(submesh.MaxVertexID, submesh.TriangleCount / 2); SubToNewV = new IndexMap(submesh.MaxVertexID, submesh.VertexCount); int nti = 0; int NT = submesh.MaxTriangleID; for (int ti = 0; ti < NT; ++ti) { if (submesh.IsTriangle(ti) == false) { continue; } Index3i sub_t = submesh.GetTriangle(ti); int gid = submesh.GetTriangleGroup(ti); Index3i new_t = Index3i.Zero; for (int j = 0; j < 3; ++j) { int sub_v = sub_t[j]; int new_v = -1; if (done_v[sub_v] == false) { // first check if this is a boundary vtx on submesh and maps to a bdry vtx on base mesh if (submesh.vertex_is_boundary(sub_v)) { int base_v = (sub_v < sub.SubToBaseV.size) ? sub.SubToBaseV[sub_v] : -1; if (base_v >= 0 && Mesh.IsVertex(base_v) && sub.BaseBorderV[base_v] == true) { // [RMS] this should always be true, but assert in tests to find out Debug.Assert(Mesh.vertex_is_boundary(base_v)); if (Mesh.vertex_is_boundary(base_v)) { new_v = base_v; } } } // if that didn't happen, append new vtx if (new_v == -1) { new_v = Mesh.AppendVertex(submesh, sub_v); } SubToNewV[sub_v] = new_v; done_v[sub_v] = true; } else { new_v = SubToNewV[sub_v]; } new_t[j] = new_v; } Debug.Assert(Mesh.FindTriangle(new_t.a, new_t.b, new_t.c) == DMesh3.InvalidID); int new_tid = Mesh.AppendTriangle(new_t, gid); Debug.Assert(new_tid != DMesh3.InvalidID && new_tid != DMesh3.NonManifoldID); if (!Mesh.IsTriangle(new_tid)) { bAllOK = false; } if (new_tris != null) { new_tris[nti++] = new_tid; } } return(bAllOK); }
/// <summary> /// Apply LaplacianMeshSmoother to subset of mesh triangles. /// border of subset always has soft constraint with borderWeight, /// but is then snapped back to original vtx pos after solve. /// nConstrainLoops inner loops are also soft-constrained, with weight falloff via square roots (defines continuity) /// interiorWeight is soft constraint added to all vertices /// </summary> public static void RegionSmooth(DMesh3 mesh, IEnumerable <int> triangles, int nConstrainLoops, int nIncludeExteriorRings, bool bPreserveExteriorRings, double borderWeight = 10.0, double interiorWeight = 0.0) { HashSet <int> fixedVerts = new HashSet <int>(); if (nIncludeExteriorRings > 0) { MeshFaceSelection expandTris = new MeshFaceSelection(mesh); expandTris.Select(triangles); if (bPreserveExteriorRings) { MeshEdgeSelection bdryEdges = new MeshEdgeSelection(mesh); bdryEdges.SelectBoundaryTriEdges(expandTris); expandTris.ExpandToOneRingNeighbours(nIncludeExteriorRings); MeshVertexSelection startVerts = new MeshVertexSelection(mesh); startVerts.SelectTriangleVertices(triangles); startVerts.DeselectEdges(bdryEdges); MeshVertexSelection expandVerts = new MeshVertexSelection(mesh, expandTris); foreach (int vid in expandVerts) { if (startVerts.IsSelected(vid) == false) { fixedVerts.Add(vid); } } } else { expandTris.ExpandToOneRingNeighbours(nIncludeExteriorRings); } triangles = expandTris; } RegionOperator region = new RegionOperator(mesh, triangles); DSubmesh3 submesh = region.Region; DMesh3 smoothMesh = submesh.SubMesh; LaplacianMeshSmoother smoother = new LaplacianMeshSmoother(smoothMesh); // map fixed verts to submesh HashSet <int> subFixedVerts = new HashSet <int>(); foreach (int base_vid in fixedVerts) { subFixedVerts.Add(submesh.MapVertexToSubmesh(base_vid)); } // constrain borders double w = borderWeight; HashSet <int> constrained = (submesh.BaseBorderV.Count > 0) ? new HashSet <int>() : null; foreach (int base_vid in submesh.BaseBorderV) { int sub_vid = submesh.BaseToSubV[base_vid]; smoother.SetConstraint(sub_vid, smoothMesh.GetVertex(sub_vid), w, true); if (constrained != null) { constrained.Add(sub_vid); } } if (constrained.Count > 0) { w = Math.Sqrt(w); for (int k = 0; k < nConstrainLoops; ++k) { HashSet <int> next_layer = new HashSet <int>(); foreach (int sub_vid in constrained) { foreach (int nbr_vid in smoothMesh.VtxVerticesItr(sub_vid)) { if (constrained.Contains(nbr_vid) == false) { if (smoother.IsConstrained(nbr_vid) == false) { smoother.SetConstraint(nbr_vid, smoothMesh.GetVertex(nbr_vid), w, subFixedVerts.Contains(nbr_vid)); } next_layer.Add(nbr_vid); } } } constrained.UnionWith(next_layer); w = Math.Sqrt(w); } } // soft constraint on all interior vertices, if requested if (interiorWeight > 0) { foreach (int vid in smoothMesh.VertexIndices()) { if (smoother.IsConstrained(vid) == false) { smoother.SetConstraint(vid, smoothMesh.GetVertex(vid), interiorWeight, subFixedVerts.Contains(vid)); } } } else if (subFixedVerts.Count > 0) { foreach (int vid in subFixedVerts) { if (smoother.IsConstrained(vid) == false) { smoother.SetConstraint(vid, smoothMesh.GetVertex(vid), 0, true); } } } smoother.SolveAndUpdateMesh(); region.BackPropropagateVertices(true); }