// smooths embedded loop in mesh, by first smoothing edge loop and then // smoothing vertex neighbourhood // [TODO] geodesic nbrhoold instead of # of rings // [TODO] reprojection? public static void smooth_loop(DMesh3 mesh, EdgeLoop loop, int nRings) { MeshFaceSelection roi_t = new MeshFaceSelection(mesh); roi_t.SelectVertexOneRings(loop.Vertices); for (int i = 0; i < nRings; ++i) { roi_t.ExpandToOneRingNeighbours(); } roi_t.LocalOptimize(true, true); MeshVertexSelection roi_v = new MeshVertexSelection(mesh); roi_v.SelectTriangleVertices(roi_t.ToArray()); roi_v.Deselect(loop.Vertices); MeshLoopSmooth loop_smooth = new MeshLoopSmooth(mesh, loop); loop_smooth.Rounds = 1; MeshIterativeSmooth mesh_smooth = new MeshIterativeSmooth(mesh, roi_v.ToArray(), true); mesh_smooth.Rounds = 1; for (int i = 0; i < 10; ++i) { loop_smooth.Smooth(); mesh_smooth.Smooth(); } }
// local mesh smooth applied to all vertices in N-rings around input list public static void smooth_region(DMesh3 mesh, IEnumerable <int> vertices, int nRings) { MeshFaceSelection roi_t = new MeshFaceSelection(mesh); roi_t.SelectVertexOneRings(vertices); for (int i = 0; i < nRings; ++i) { roi_t.ExpandToOneRingNeighbours(); } roi_t.LocalOptimize(true, true); MeshVertexSelection roi_v = new MeshVertexSelection(mesh); roi_v.SelectTriangleVertices(roi_t.ToArray()); MeshIterativeSmooth mesh_smooth = new MeshIterativeSmooth(mesh, roi_v.ToArray(), true); mesh_smooth.Alpha = 0.2f; mesh_smooth.Rounds = 10; mesh_smooth.Smooth(); }
public virtual bool Extrude() { MeshEditor editor = new MeshEditor(Mesh); editor.SeparateTriangles(Triangles, true, out EdgePairs); MeshNormals normals = null; bool bHaveNormals = Mesh.HasVertexNormals; if (!bHaveNormals) { normals = new MeshNormals(Mesh); normals.Compute(); } ExtrudeVertices = new MeshVertexSelection(Mesh); ExtrudeVertices.SelectTriangleVertices(Triangles); Vector3d[] NewVertices = new Vector3d[ExtrudeVertices.Count]; int k = 0; foreach (int vid in ExtrudeVertices) { Vector3d v = Mesh.GetVertex(vid); Vector3f n = (bHaveNormals) ? Mesh.GetVertexNormal(vid) : (Vector3f)normals.Normals[vid]; NewVertices[k++] = ExtrudedPositionF(v, n, vid); } k = 0; foreach (int vid in ExtrudeVertices) { Mesh.SetVertex(vid, NewVertices[k++]); } SetGroupID = Group.GetGroupID(Mesh); JoinTriangles = editor.StitchUnorderedEdges(EdgePairs, SetGroupID); return(true); }
protected void compute_full(IEnumerable <int> Triangles, bool bIsFullMeshHint = false) { Graph = new DGraph3(); if (WantGraphEdgeInfo) { GraphEdges = new DVector <GraphEdgeInfo>(); } Vertices = new Dictionary <Vector3d, int>(); // multithreaded precomputation of per-vertex values double[] vertex_values = null; if (PrecomputeVertexValues) { vertex_values = new double[Mesh.MaxVertexID]; IEnumerable <int> verts = Mesh.VertexIndices(); if (bIsFullMeshHint == false) { MeshVertexSelection vertices = new MeshVertexSelection(Mesh); vertices.SelectTriangleVertices(Triangles); verts = vertices; } gParallel.ForEach(verts, (vid) => { vertex_values[vid] = ValueF(Mesh.GetVertex(vid)); }); VertexValueF = (vid) => { return(vertex_values[vid]); }; } foreach (int tid in Triangles) { Vector3dTuple3 tv = new Vector3dTuple3(); Mesh.GetTriVertices(tid, ref tv.V0, ref tv.V1, ref tv.V2); Index3i triVerts = Mesh.GetTriangle(tid); Vector3d f = (VertexValueF != null) ? new Vector3d(VertexValueF(triVerts.a), VertexValueF(triVerts.b), VertexValueF(triVerts.c)) : new Vector3d(ValueF(tv.V0), ValueF(tv.V1), ValueF(tv.V2)); // round f to 0 within epsilon? if (f.x < 0 && f.y < 0 && f.z < 0) { continue; } if (f.x > 0 && f.y > 0 && f.z > 0) { continue; } Index3i triEdges = Mesh.GetTriEdges(tid); if (f.x * f.y * f.z == 0) { int z0 = (f.x == 0) ? 0 : ((f.y == 0) ? 1 : 2); int i1 = (z0 + 1) % 3, i2 = (z0 + 2) % 3; if (f[i1] * f[i2] > 0) { continue; // single-vertex-crossing case, skip here and let other edges catch it } if (f[i1] == 0 || f[i2] == 0) { // on-edge case int z1 = f[i1] == 0 ? i1 : i2; if ((z0 + 1) % 3 != z1) { int tmp = z0; z0 = z1; z1 = tmp; // catch reverse-orientation cases } int e0 = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int e1 = add_or_append_vertex(Mesh.GetVertex(triVerts[z1])); int graph_eid = Graph.AppendEdge(e0, e1, (int)TriangleCase.OnEdge); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_on_edge(graph_eid, tid, triEdges[z0], new Index2i(e0, e1)); } } else { // edge/vertex case Util.gDevAssert(f[i1] * f[i2] < 0); int vert_vid = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int i = i1, j = i2; if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); int cross_vid = add_or_append_vertex(cross); add_edge_pos(triVerts[i], triVerts[j], cross); int graph_eid = Graph.AppendEdge(vert_vid, cross_vid, (int)TriangleCase.EdgeVertex); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_edge_vert(graph_eid, tid, triEdges[(z0 + 1) % 3], triVerts[z0], new Index2i(vert_vid, cross_vid)); } } } else { Index3i cross_verts = Index3i.Min; int less_than = 0; for (int tei = 0; tei < 3; ++tei) { int i = tei, j = (tei + 1) % 3; if (f[i] < 0) { less_than++; } if (f[i] * f[j] > 0) { continue; } if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); cross_verts[tei] = add_or_append_vertex(cross); add_edge_pos(triVerts[i], triVerts[j], cross); } int e0 = (cross_verts.a == int.MinValue) ? 1 : 0; int e1 = (cross_verts.c == int.MinValue) ? 1 : 2; if (e0 == 0 && e1 == 2) // preserve orientation order { e0 = 2; e1 = 0; } // preserving orientation does not mean we get a *consistent* orientation across faces. // To do that, we need to assign "sides". Either we have 1 less-than-0 or 1 greater-than-0 vtx. // Arbitrary decide that we want loops oriented like bdry loops would be if we discarded less-than side. // In that case, when we are "cutting off" one vertex, orientation would end up flipped if (less_than == 1) { int tmp = e0; e0 = e1; e1 = tmp; } int ev0 = cross_verts[e0]; int ev1 = cross_verts[e1]; // [RMS] if function is garbage, we can end up w/ case where both crossings // happen at same vertex, even though values are not the same (eg if // some values are double.MaxValue). We will just fail in these cases. if (ev0 != ev1) { Util.gDevAssert(ev0 != int.MinValue && ev1 != int.MinValue); int graph_eid = Graph.AppendEdge(ev0, ev1, (int)TriangleCase.EdgeEdge); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_edge_edge(graph_eid, tid, new Index2i(triEdges[e0], triEdges[e1]), new Index2i(ev0, ev1)); } } } } Vertices = null; }
/// <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); }