// After remeshing we may create an internal edge between two boundary vertices [a,b]. // Those vertices will be merged with vertices c and d in the base mesh. If the edge // [c,d] already exists in the base mesh, then after the merge we would have at least // 3 triangles at this edge. Dang. // // A common example is a 'fin' triangle that would duplicate a // 'fin' on the border of the base mesh after removing the submesh, but this situation can // arise anywhere (eg think about one-triangle-wide strips). // // This is very hard to remove, but we can at least avoid creating non-manifold edges (which // with the current DMesh3 will be prevented, hence leaving a hole) by splitting the // internal edge in the submesh (which presumably we were remeshing anyway, so changes are ok). public void RepairPossibleNonManifoldEdges() { // [TODO] do we need to repeat this more than once? I don't think so... // repair submesh int NE = Region.SubMesh.MaxEdgeID; var split_edges = new List <int>(); for (int eid = 0; eid < NE; ++eid) { if (Region.SubMesh.IsEdge(eid) == false) { continue; } if (Region.SubMesh.IsBoundaryEdge(eid)) { continue; } Index2i edgev = Region.SubMesh.GetEdgeV(eid); if (Region.SubMesh.IsBoundaryVertex(edgev.a) && Region.SubMesh.IsBoundaryVertex(edgev.b)) { // ok, we have an internal edge where both verts are on the boundary // now check if it is an edge in the base mesh int base_a = Region.MapVertexToBaseMesh(edgev.a); int base_b = Region.MapVertexToBaseMesh(edgev.b); if (base_a != DMesh3.InvalidID && base_b != DMesh3.InvalidID) { // both vertices in base mesh...right? Debug.Assert(Region.BaseMesh.IsVertex(base_a) && Region.BaseMesh.IsVertex(base_b)); int base_eid = Region.BaseMesh.FindEdge(base_a, base_b); if (base_eid != DMesh3.InvalidID) { split_edges.Add(eid); } } } } // split any problem edges we found and repeat this loop for (int i = 0; i < split_edges.Count; ++i) { DMesh3.EdgeSplitInfo split_info; Region.SubMesh.SplitEdge(split_edges[i], out split_info); } }
/// <summary> /// For given edge, return it's triangles and the triangles that would /// be created if it was flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipTris(DMesh3 mesh, int eID, out Index3i orig_t0, out Index3i orig_t1, out Index3i flip_t0, out Index3i flip_t1) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); orig_t0 = new Index3i(oa, ob, c); orig_t1 = new Index3i(ob, oa, d); flip_t0 = new Index3i(c, d, ob); flip_t1 = new Index3i(d, c, oa); }
/// <summary> /// DGraph3 edges are not oriented, which means they cannot inherit orientation from mesh. /// This function returns true if, for a given graph_eid, the vertex pair returned by /// Graph.GetEdgeV(graph_eid) should be reversed to be consistent with mesh orientation. /// Mainly inteded to be passed to DGraph3Util.ExtractCurves /// </summary> public bool ShouldReverseGraphEdge(int graph_eid) { if (GraphEdges == null) { throw new Exception("MeshIsoCurves.OrientEdge: must track edge graph info to orient edge"); } Index2i graph_ev = Graph.GetEdgeV(graph_eid); GraphEdgeInfo einfo = GraphEdges[graph_eid]; if (graph_ev.b == einfo.order.a && graph_ev.a == einfo.order.b) { return(true); } Util.gDevAssert(graph_ev.a == einfo.order.a && graph_ev.b == einfo.order.b); return(false); }
// convert vertex selection to edge selection. Require at least minCount verts of edge to be selected public MeshEdgeSelection(DMesh3 mesh, MeshVertexSelection convertV, int minCount = 2) : this(mesh) { minCount = MathUtil.Clamp(minCount, 1, 2); // [TODO] if minCount == 1, and convertV is small, it is faster to iterate over convertV!! foreach (int eid in mesh.EdgeIndices()) { Index2i ev = mesh.GetEdgeV(eid); int n = (convertV.IsSelected(ev.a) ? 1 : 0) + (convertV.IsSelected(ev.b) ? 1 : 0); if (n >= minCount) { add(eid); } } }
/// <summary> /// given list of edges of MeshA, and vertex map from A to B, map to list of edges on B /// </summary> public static List <int> MapEdgesViaVertexMap(IIndexMap AtoBV, DMesh3 MeshA, DMesh3 MeshB, List <int> edges) { int N = edges.Count; List <int> result = new List <int>(N); for (int i = 0; i < N; ++i) { int eid_a = edges[i]; Index2i aev = MeshA.GetEdgeV(eid_a); int bev0 = AtoBV[aev.a]; int bev1 = AtoBV[aev.b]; int eid_b = MeshB.FindEdge(bev0, bev1); Debug.Assert(eid_b != DMesh3.InvalidID); result.Add(eid_b); } return(result); }
// // [TODO] for internal vertices, there is no ambiguity in which is the left-turn edge, // we should be using 'closest' left-neighbour edge. // // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, IndexFlagSet used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } // [TODO] can do this more efficienty? int tid_in = DMesh3.InvalidID, tid_out = DMesh3.InvalidID; edge_is_boundary(bdry_eid, ref tid_in, ref tid_out); Index2i bdry_ev = get_oriented_edgev(bdry_eid, tid_in, tid_out); //Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } Debug.Assert(best_e != -1); return(best_e); }
public void debug_print_vertex(int v) { System.Console.WriteLine("Vertex " + v.ToString()); List <int> tris = new List <int>(); GetVtxTriangles(v, tris, false); System.Console.WriteLine(string.Format(" Tris {0} Edges {1} refcount {2}", tris.Count, GetVtxEdges(v).Count, vertices_refcount.refCount(v))); foreach (int t in tris) { Index3i tv = GetTriangle(t), te = GetTriEdges(t); System.Console.WriteLine(string.Format(" t{6} {0} {1} {2} te {3} {4} {5}", tv[0], tv[1], tv[2], te[0], te[1], te[2], t)); } foreach (int e in GetVtxEdges(v)) { Index2i ev = GetEdgeV(e), et = GetEdgeT(e); System.Console.WriteLine(string.Format(" e{4} {0} {1} / {2} {3}", ev[0], ev[1], et[0], et[1], e)); } }
public double this[int r, int c] { get { var v = new Index2i(Math.Min(r, c), Math.Max(r, c)); double value; if (d.TryGetValue(v, out value)) { return(value); } return(0); } set { Set(r, c, value); } }
/// <summary> /// for each on-edge vtx, we split the edge and then /// re-sort any of the vertices on that edge onto new edges /// </summary> void insert_edge_vertices() { while (EdgeVertices.Count > 0) { var pair = EdgeVertices.First(); int eid = pair.Key; List <SegmentVtx> edgeVerts = pair.Value; SegmentVtx v = edgeVerts[edgeVerts.Count - 1]; edgeVerts.RemoveAt(edgeVerts.Count - 1); Index2i splitTris = Target.GetEdgeT(eid); DMesh3.EdgeSplitInfo splitInfo; MeshResult result = Target.SplitEdge(eid, out splitInfo); if (result != MeshResult.Ok) { throw new Exception("insert_edge_vertices: split failed!"); } int new_v = splitInfo.vNew; var splitEdges = new Index2i(eid, splitInfo.eNewBN); Target.SetVertex(new_v, v.v); v.vtx_id = new_v; VIDToSegVtxMap[v.vtx_id] = v; PointHash.InsertPoint(v.vtx_id, v.v); // remove this triangles vtx list because it is no longer valid EdgeVertices.Remove(eid); // update remaining verts foreach (SegmentVtx sv in edgeVerts) { update_from_split(sv, splitEdges); if (sv.type == 1) { add_edge_vtx(sv.elem_id, sv); } } // track subfaces add_split_subfaces(splitTris, ref splitInfo); } }
public void ComputeBoundaryInfo(IEnumerable <int> triangles, int tri_count) { // set of base-mesh triangles that are in submesh IndexFlagSet sub_tris = new IndexFlagSet(BaseMesh.MaxTriangleID, tri_count); foreach (int ti in triangles) { sub_tris[ti] = true; } BaseBorderV = new IndexHashSet(); BaseBorderE = new IndexHashSet(); BaseBoundaryE = new IndexHashSet(); // Iterate through edges in submesh roi on base mesh. If // one of the tris of the edge is not in submesh roi, then this // is a boundary edge. // // (edge iteration via triangle iteration processes each internal edge twice...) foreach (int ti in triangles) { Index3i tedges = BaseMesh.GetTriEdges(ti); for (int j = 0; j < 3; ++j) { int eid = tedges[j]; Index2i tris = BaseMesh.GetEdgeT(eid); if (tris.b == DMesh3.InvalidID || sub_tris[tris.a] != sub_tris[tris.b]) { if (tris.b == DMesh3.InvalidID) { BaseBoundaryE[eid] = true; } else { BaseBorderE[eid] = true; } Index2i ve = BaseMesh.GetEdgeV(eid); BaseBorderV[ve.a] = true; BaseBorderV[ve.b] = true; } } } }
public virtual void DoReduce() { if (mesh.TriangleCount == 0) // badness if we don't catch this... { return; } begin_pass(); begin_setup(); InitializeVertexQuadrics(); InitializeQueue(); end_setup(); begin_ops(); begin_collapse(); while (EdgeQueue.Count > 0 && mesh.TriangleCount > TargetTriangleCount) { COUNT_ITERATIONS++; QEdge cur = EdgeQueue.Dequeue(); Nodes[cur.eid] = null; NodePool.Return(cur); if (!mesh.IsEdge(cur.eid)) { continue; } Index2i ev = mesh.GetEdgeV(cur.eid); int vKept; ProcessResult result = CollapseEdge(cur.eid, cur.collapse_pt, out vKept); if (result == ProcessResult.Ok_Collapsed) { vertQuadrics[vKept] = cur.q; UpdateNeighbours(vKept); } } end_collapse(); end_ops(); Reproject(); end_pass(); }
public void SplitToMaxEdgeLength(double fMaxLen) { List <int> queue = new List <int>(); int NE = Graph.MaxEdgeID; for (int eid = 0; eid < NE; ++eid) { if (!Graph.IsEdge(eid)) { continue; } Index2i ev = Graph.GetEdgeV(eid); double dist = Graph.GetVertex(ev.a).Distance(Graph.GetVertex(ev.b)); if (dist > fMaxLen) { DGraph2.EdgeSplitInfo splitInfo; if (Graph.SplitEdge(eid, out splitInfo) == MeshResult.Ok && dist > 2 * fMaxLen) { queue.Add(eid); queue.Add(splitInfo.eNewBN); } } } while (queue.Count > 0) { int eid = queue[queue.Count - 1]; queue.RemoveAt(queue.Count - 1); if (!Graph.IsEdge(eid)) { continue; } Index2i ev = Graph.GetEdgeV(eid); double dist = Graph.GetVertex(ev.a).Distance(Graph.GetVertex(ev.b)); if (dist > fMaxLen) { DGraph2.EdgeSplitInfo splitInfo; if (Graph.SplitEdge(eid, out splitInfo) == MeshResult.Ok && dist > 2 * fMaxLen) { queue.Add(eid); queue.Add(splitInfo.eNewBN); } } } }
// Support for ordering a set of unique indices into the vertex pool. On // output it is guaranteed that: v0 < v1 < v2. This is used to guarantee // consistent queries when the vertex ordering of a primitive is permuted, // a necessity when using floating-point arithmetic that suffers from // numerical round-off errors. The input indices are considered the // positive ordering. The output indices are either positively ordered // (an even number of transpositions occurs during sorting) or negatively // ordered (an odd number of transpositions occurs during sorting). The // functions return 'true' for a positive ordering and 'false' for a // negative ordering. public static bool Sort(ref int v0, ref int v1) { int j0, j1; bool positive; if (v0 < v1) { j0 = 0; j1 = 1; positive = true; } else { j0 = 1; j1 = 0; positive = false; } Index2i value = new Index2i(v0, v1); v0 = value[j0]; v1 = value[j1]; return(positive); }
// for all mesh boundary vertices, pin in current position, but allow splits public static void FixAllBoundaryEdges_AllowSplit(MeshConstraints cons, DMesh3 mesh, int setID) { EdgeConstraint edgeCons = new EdgeConstraint(EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse); VertexConstraint vertCons = new VertexConstraint(true, setID); int NE = mesh.MaxEdgeID; for (int ei = 0; ei < NE; ++ei) { if (mesh.IsEdge(ei) && mesh.IsBoundaryEdge(ei)) { cons.SetOrUpdateEdgeConstraint(ei, edgeCons); Index2i ev = mesh.GetEdgeV(ei); cons.SetOrUpdateVertexConstraint(ev.a, vertCons); cons.SetOrUpdateVertexConstraint(ev.b, vertCons); } } }
// update queue weight for each edge in vertex one-ring protected virtual void UpdateNeighbours(int vid) { foreach (int eid in mesh.VtxEdgesItr(vid)) { Index2i nev = mesh.GetEdgeV(eid); QuadricError Q = new QuadricError(ref vertQuadrics[nev.a], ref vertQuadrics[nev.b]); Vector3d opt = OptimalPoint(eid, ref Q, nev.a, nev.b); double err = Q.Evaluate(ref opt); EdgeQuadrics[eid] = new QEdge(eid, ref Q, ref opt); if (EdgeQueue.Contains(eid)) { EdgeQueue.Update(eid, (float)err); } else { EdgeQueue.Insert(eid, (float)err); } } }
// for all mesh boundary edges, disable flip/split/collapse // for all mesh boundary vertices, pin in current position public static void FixAllGroupBoundaryEdges(MeshConstraints cons, DMesh3 mesh, bool bPinVertices) { int NE = mesh.MaxEdgeID; for (int ei = 0; ei < NE; ++ei) { if (mesh.IsEdge(ei) && mesh.IsGroupBoundaryEdge(ei)) { cons.SetOrUpdateEdgeConstraint(ei, EdgeConstraint.FullyConstrained); if (bPinVertices) { Index2i ev = mesh.GetEdgeV(ei); cons.SetOrUpdateVertexConstraint(ev.a, VertexConstraint.Pinned); cons.SetOrUpdateVertexConstraint(ev.b, VertexConstraint.Pinned); } } } }
/// <summary> /// If we are at edge eid, which as one vertex prev_vid, find 'other' vertex, and other edge connected to that vertex, /// and return pair [next_edge, shared_vtx] /// Returns [int.MaxValue, shared_vtx] if shared_vtx is not valence=2 (ie stops at boundaries and complex junctions) /// </summary> public static Index2i NextEdgeAndVtx(int eid, int prev_vid, DGraph2 graph) { Index2i ev = graph.GetEdgeV(eid); int next_vid = (ev.a == prev_vid) ? ev.b : ev.a; if (graph.GetVtxEdgeCount(next_vid) != 2) { return(new Index2i(int.MaxValue, next_vid)); } foreach (int next_eid in graph.VtxEdgesItr(next_vid)) { if (next_eid != eid) { return(new Index2i(next_eid, next_vid)); } } return(Index2i.Max); }
// ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. // // [TODO] handle degenerate edges. what do we do then? Currently will only chose // degenerate edge if there are no other options (I think...) int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } // [RMS] w/ bowtie vertices and open spans, this does happen //Debug.Assert(best_e != -1); return(best_e); }
public virtual int[] AddTriangleFan_OrderedEdgeLoop(int center, int[] edge_loop, int group_id = -1) { int N = edge_loop.Length; int[] new_tris = new int[N]; int i = 0; for (i = 0; i < N; ++i) { if (Mesh.edge_is_boundary(edge_loop[i]) == false) { goto operation_failed; } Index2i ev = Mesh.GetOrientedBoundaryEdgeV(edge_loop[i]); int a = ev.a, b = ev.b; Index3i newT = new Index3i(center, b, a); int new_tid = Mesh.AppendTriangle(newT, group_id); if (new_tid == DMesh3.InvalidID) { goto operation_failed; } new_tris[i] = new_tid; } return(new_tris); operation_failed: // remove what we added so far if (i > 0) { if (remove_triangles(new_tris, i - 1) == false) { throw new Exception("MeshConstructor.AddTriangleFan_OrderedEdgeLoop: failed to add fan, and also failed to back out changes."); } } return(null); }
public void CollapseDegenerateEdges(double fDegenLenThresh = MathUtil.Epsilonf) { bool done = false; int max_passes = 100; int pass_count = 0; while (done == false && pass_count++ < max_passes) { done = true; int N = Graph.MaxEdgeID; for (int eid = 0; eid < N; eid++) { if (!Graph.IsEdge(eid)) { continue; } if (FixedEdgeFilterF(eid)) { continue; } Index2i ev = Graph.GetEdgeV(eid); Vector2d va = Graph.GetVertex(ev.a); Vector2d vb = Graph.GetVertex(ev.b); if (va.Distance(vb) < fDegenLenThresh) { int keep = ev.a; int remove = ev.b; DGraph2.EdgeCollapseInfo collapseInfo; if (Graph.CollapseEdge(keep, remove, out collapseInfo) == MeshResult.Ok) { done = false; } } } ; } }
/// <summary> /// For given edge, return normals of it's two triangles, and normals /// of the triangles created if edge is flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipNormals(DMesh3 mesh, int eID, out Vector3d n1, out Vector3d n2, out Vector3d on1, out Vector3d on2) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Vector3d vC = mesh.GetVertex(c), vD = mesh.GetVertex(d); Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); Vector3d vOA = mesh.GetVertex(oa), vOB = mesh.GetVertex(ob); n1 = MathUtil.Normal(ref vOA, ref vOB, ref vC); n2 = MathUtil.Normal(ref vOB, ref vOA, ref vD); on1 = MathUtil.Normal(ref vC, ref vD, ref vOB); on2 = MathUtil.Normal(ref vD, ref vC, ref vOA); }
/// <summary> /// construct EdgeLoop from a list of edges of mesh /// </summary> public static EdgeLoop FromEdges(DMesh3 mesh, IList <int> edges) { int[] Edges = new int[edges.Count]; for (int i = 0; i < Edges.Length; ++i) { Edges[i] = edges[i]; } int[] Vertices = new int[Edges.Length]; Index2i start_ev = mesh.GetEdgeV(Edges[0]); Index2i prev_ev = start_ev; for (int i = 1; i < Edges.Length; ++i) { Index2i next_ev = mesh.GetEdgeV(Edges[i % Edges.Length]); Vertices[i] = IndexUtil.find_shared_edge_v(ref prev_ev, ref next_ev); prev_ev = next_ev; } Vertices[0] = IndexUtil.find_edge_other_v(ref start_ev, Vertices[1]); return(new EdgeLoop(mesh, Vertices, Edges, false)); }
/// <summary> /// construct EdgeSpan from a list of edges of mesh /// </summary> public static EdgeSpan FromEdges(DMesh3 mesh, IList<int> edges) { int[] Edges = new int[edges.Count]; for (int i = 0; i < Edges.Length; ++i) Edges[i] = edges[i]; int[] Vertices = new int[Edges.Length+1]; Index2i start_ev = mesh.GetEdgeV(Edges[0]); Index2i prev_ev = start_ev; if (Edges.Length > 1) { for (int i = 1; i < Edges.Length; ++i) { Index2i next_ev = mesh.GetEdgeV(Edges[i]); Vertices[i] = IndexUtil.find_shared_edge_v(ref prev_ev, ref next_ev); prev_ev = next_ev; } Vertices[0] = IndexUtil.find_edge_other_v(ref start_ev, Vertices[1]); Vertices[Vertices.Length - 1] = IndexUtil.find_edge_other_v(prev_ev, Vertices[Vertices.Length - 2]); } else { Vertices[0] = start_ev[0]; Vertices[1] = start_ev[1]; } return new EdgeSpan(mesh, Vertices, Edges, false); }
void add_split_subfaces(Index2i origTris, ref DMesh3.EdgeSplitInfo splitInfo) { int parent_1 = get_parent(origTris.a); HashSet <int> subfaces_1 = get_subfaces(parent_1); if (origTris.a != parent_1) { add_subface(subfaces_1, parent_1, origTris.a); } add_subface(subfaces_1, parent_1, splitInfo.eNewT2); if (origTris.b != DMesh3.InvalidID) { int parent_2 = get_parent(origTris.b); HashSet <int> subfaces_2 = get_subfaces(parent_2); if (origTris.b != parent_2) { add_subface(subfaces_2, parent_2, origTris.b); } add_subface(subfaces_2, parent_2, splitInfo.eNewT3); } }
} // Cut() protected void collapse_degenerate_edges(HashSet <int> OnCutEdges, HashSet <int> ZeroEdges) { var sets = new HashSet <int>[2] { OnCutEdges, ZeroEdges }; double tol2 = DegenerateEdgeTol * DegenerateEdgeTol; Vector3d a = Vector3d.Zero, b = Vector3d.Zero; int collapsed = 0; do { collapsed = 0; foreach (var edge_set in sets) { foreach (int eid in edge_set) { if (Mesh.IsEdge(eid) == false) { continue; } Mesh.GetEdgeV(eid, ref a, ref b); if (a.DistanceSquared(b) > tol2) { continue; } Index2i ev = Mesh.GetEdgeV(eid); DMesh3.EdgeCollapseInfo collapseInfo; MeshResult result = Mesh.CollapseEdge(ev.a, ev.b, out collapseInfo); if (result == MeshResult.Ok) { collapsed++; } } } } while (collapsed != 0); }
public MeshRegionBoundaryLoops(DMesh3 mesh, int[] RegionTris, bool bAutoCompute = true) { this.Mesh = mesh; // make flag set for included triangles triangles = new IndexFlagSet(mesh.MaxTriangleID, RegionTris.Length); for (int i = 0; i < RegionTris.Length; ++i) { triangles[RegionTris[i]] = true; } // make flag set for included edges // NOTE: this currently processes non-boundary-edges twice. Could // avoid w/ another IndexFlagSet, but the check is inexpensive... edges = new IndexFlagSet(mesh.MaxEdgeID, RegionTris.Length); for (int i = 0; i < RegionTris.Length; ++i) { int tid = RegionTris[i]; Index3i te = Mesh.GetTriEdges(tid); for (int j = 0; j < 3; ++j) { int eid = te[j]; if (!edges.Contains(eid)) { Index2i et = mesh.GetEdgeT(eid); if (et.b == DMesh3.InvalidID || triangles[et.a] != triangles[et.b]) { edges.Add(eid); } } } } if (bAutoCompute) { Compute(); } }
int find_nearest_edge(DMesh3 mesh, Vector3d v, HashSet <int> vertices) { int near_eid = DMesh3.InvalidID; double nearSqr = VertexSnapTol * VertexSnapTol; foreach (int eid in mesh.BoundaryEdgeIndices()) { Index2i ev = mesh.GetEdgeV(eid); if (vertices.Contains(ev.a) == false || vertices.Contains(ev.b) == false) { continue; } Segment3d seg = new Segment3d(mesh.GetVertex(ev.a), mesh.GetVertex(ev.b)); double dSqr = seg.DistanceSquared(v); if (dSqr < nearSqr) { near_eid = eid; nearSqr = dSqr; } } return(near_eid); }
/// <summary> /// given EdgeLoop on MeshA, and vertex map from A to B, map to EdgeLoop on B /// </summary> public static EdgeLoop MapLoopViaVertexMap(IIndexMap AtoBV, DMesh3 MeshA, DMesh3 MeshB, EdgeLoop loopIn) { int NV = loopIn.VertexCount, NE = loopIn.EdgeCount; int[] newVerts = new int[NV]; for (int i = 0; i < NV; ++i) { newVerts[i] = AtoBV[loopIn.Vertices[i]]; } int[] newEdges = new int[NE]; for (int i = 0; i < NE; ++i) { int eid_a = loopIn.Edges[i]; Index2i aev = MeshA.GetEdgeV(eid_a); int bev0 = AtoBV[aev.a]; int bev1 = AtoBV[aev.b]; newEdges[i] = MeshB.FindEdge(bev0, bev1); Debug.Assert(newEdges[i] != DMesh3.InvalidID); } return(new EdgeLoop(MeshB, newVerts, newEdges, false)); }
// find the vtx that is the same in both ev0 and ev1 public static int find_shared_edge_v(ref Index2i ev0, ref Index2i ev1) { if (ev0.a == ev1.a) { return(ev0.a); } else if (ev0.a == ev1.b) { return(ev0.a); } else if (ev0.b == ev1.a) { return(ev0.b); } else if (ev0.b == ev1.b) { return(ev0.b); } else { return(DMesh3.InvalidID); } }
/// <summary> /// Exhaustively check that verts and edges of this EdgeSpan are consistent. Not for production use. /// </summary> public bool CheckValidity(FailMode eFailMode = FailMode.Throw) { bool is_ok = true; Action<bool> CheckOrFailF = (b) => { is_ok = is_ok && b; }; if (eFailMode == FailMode.DebugAssert) { CheckOrFailF = (b) => { Debug.Assert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.gDevAssert) { CheckOrFailF = (b) => { Util.gDevAssert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.Throw) { CheckOrFailF = (b) => { if (b == false) throw new Exception("EdgeSpan.CheckValidity: check failed"); }; } CheckOrFailF(Vertices.Length == Edges.Length + 1); for (int ei = 0; ei < Edges.Length; ++ei) { Index2i ev = Mesh.GetEdgeV(Edges[ei]); CheckOrFailF(Mesh.IsVertex(ev.a)); CheckOrFailF(Mesh.IsVertex(ev.b)); CheckOrFailF(Mesh.FindEdge(ev.a, ev.b) != DMesh3.InvalidID); CheckOrFailF(Vertices[ei] == ev.a || Vertices[ei] == ev.b); CheckOrFailF(Vertices[ei + 1] == ev.a || Vertices[ei + 1] == ev.b); } for (int vi = 0; vi < Vertices.Length-1; ++vi) { int a = Vertices[vi], b = Vertices[vi + 1]; CheckOrFailF(Mesh.IsVertex(a)); CheckOrFailF(Mesh.IsVertex(b)); CheckOrFailF(Mesh.FindEdge(a, b) != DMesh3.InvalidID); if (vi < Vertices.Length - 2) { int n = 0, edge_before_b = Edges[vi], edge_after_b = Edges[vi + 1]; foreach (int nbr_e in Mesh.VtxEdgesItr(b)) { if (nbr_e == edge_before_b || nbr_e == edge_after_b) n++; } CheckOrFailF(n == 2); } } return true; }