/// <summary> /// if before a flip we have normals (n1,n2) and after we have (m1,m2), check if /// the dot between any of the 4 pairs changes sign after the flip, or is /// less than the dot-product tolerance (ie angle tolerance) /// </summary> public static bool CheckIfEdgeFlipCreatesFlip(DMesh3 mesh, int eID, double flip_dot_tol = 0.0) { Util.gDevAssert(mesh.IsBoundaryEdge(eID) == false); 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); Vector3d n0 = MathUtil.FastNormalDirection(ref vOA, ref vOB, ref vC); Vector3d n1 = MathUtil.FastNormalDirection(ref vOB, ref vOA, ref vD); Vector3d f0 = MathUtil.FastNormalDirection(ref vC, ref vD, ref vOB); if (edge_flip_metric(ref n0, ref f0, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f0, flip_dot_tol) <= flip_dot_tol) { return(true); } Vector3d f1 = MathUtil.FastNormalDirection(ref vD, ref vC, ref vOA); if (edge_flip_metric(ref n0, ref f1, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f1, flip_dot_tol) <= flip_dot_tol) { return(true); } return(false); }
public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> added_v, IEnumerable <int> added_t) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; if (added_v != null) { foreach (int vid in added_v) { Util.gDevAssert(mesh.IsVertex(vid)); append_vertex(mesh, vid); } } foreach (int tid in added_t) { Util.gDevAssert(mesh.IsTriangle(tid)); Index3i tv = mesh.GetTriangle(tid); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); AddedT.Add(tid); Triangles.Add(tri); } }
public void Initialize(DMesh3 mesh, IEnumerable <int> triangles) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; foreach (int tid in triangles) { if (!mesh.IsTriangle(tid)) { continue; } Index3i tv = mesh.GetTriangle(tid); bool va = save_vertex(mesh, tv.a); bool vb = save_vertex(mesh, tv.b); bool vc = save_vertex(mesh, tv.c); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); RemovedT.Add(tid); Triangles.Add(tri); MeshResult result = mesh.RemoveTriangle(tid, true, false); if (result != MeshResult.Ok) { throw new Exception("RemoveTrianglesMeshChange.Initialize: exception in RemoveTriangle(" + tid.ToString() + "): " + result.ToString()); } Util.gDevAssert(mesh.IsVertex(tv.a) == va && mesh.IsVertex(tv.b) == vb && mesh.IsVertex(tv.c) == vc); } }
public void Apply(DMesh3 mesh) { int NV = AddedV.size; if (NV > 0) { NewVertexInfo vinfo = new NewVertexInfo(Positions[0]); mesh.BeginUnsafeVerticesInsert(); for (int i = 0; i < NV; ++i) { int vid = AddedV[i]; vinfo.v = Positions[i]; if (Normals != null) { vinfo.bHaveN = true; vinfo.n = Normals[i]; } if (Colors != null) { vinfo.bHaveC = true; vinfo.c = Colors[i]; } if (UVs != null) { vinfo.bHaveUV = true; vinfo.uv = UVs[i]; } MeshResult result = mesh.InsertVertex(vid, ref vinfo, true); if (result != MeshResult.Ok) { throw new Exception("AddTrianglesMeshChange.Revert: error in InsertVertex(" + vid.ToString() + "): " + result.ToString()); } } mesh.EndUnsafeVerticesInsert(); } int NT = AddedT.size; if (NT > 0) { mesh.BeginUnsafeTrianglesInsert(); for (int i = 0; i < NT; ++i) { int tid = AddedT[i]; Index4i tdata = Triangles[i]; Index3i tri = new Index3i(tdata.a, tdata.b, tdata.c); MeshResult result = mesh.InsertTriangle(tid, tri, tdata.d, true); if (result != MeshResult.Ok) { throw new Exception("AddTrianglesMeshChange.Revert: error in InsertTriangle(" + tid.ToString() + "): " + result.ToString()); } } mesh.EndUnsafeTrianglesInsert(); } if (OnApplyF != null) { OnApplyF(AddedV, AddedT); } }
/// <summary> /// Check if collapsing edge edgeID to point newv will flip normal of any attached face /// </summary> public static bool CheckIfCollapseCreatesFlip(DMesh3 mesh, int edgeID, Vector3d newv) { Index4i edge_info = mesh.GetEdge(edgeID); int tc = edge_info.c, td = edge_info.d; for (int j = 0; j < 2; ++j) { int vid = edge_info[j]; int vother = edge_info[(j + 1) % 2]; foreach (int tid in mesh.VtxTrianglesItr(vid)) { if (tid == tc || tid == td) { continue; } Index3i curt = mesh.GetTriangle(tid); if (curt.a == vother || curt.b == vother || curt.c == vother) { return(true); // invalid nbrhood for collapse } Vector3d va = mesh.GetVertex(curt.a); Vector3d vb = mesh.GetVertex(curt.b); Vector3d vc = mesh.GetVertex(curt.c); Vector3d ncur = (vb - va).Cross(vc - va); double sign = 0; if (curt.a == vid) { Vector3d nnew = (vb - newv).Cross(vc - newv); sign = ncur.Dot(nnew); } else if (curt.b == vid) { Vector3d nnew = (newv - va).Cross(vc - va); sign = ncur.Dot(nnew); } else if (curt.c == vid) { Vector3d nnew = (vb - va).Cross(newv - va); sign = ncur.Dot(nnew); } else { throw new Exception("should never be here!"); } if (sign <= 0.0) { return(true); } } } return(false); }
/// <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); }
public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> remove_t) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; HashSet <int> triangles = new HashSet <int>(remove_t); HashSet <int> vertices = new HashSet <int>(); IndexUtil.TrianglesToVertices(mesh, remove_t, vertices); List <int> save_v = new List <int>(); foreach (int vid in vertices) { bool all_contained = true; foreach (int tid in mesh.VtxTrianglesItr(vid)) { if (triangles.Contains(tid) == false) { all_contained = false; break; } } if (all_contained) { save_v.Add(vid); } } foreach (int vid in save_v) { save_vertex(mesh, vid, true); } foreach (int tid in remove_t) { Util.gDevAssert(mesh.IsTriangle(tid)); Index3i tv = mesh.GetTriangle(tid); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); RemovedT.Add(tid); Triangles.Add(tri); } }
// actually write triangle line, in proper OBJ format static void write_quad(TextWriter writer, ref Index4i q, bool bNormals, bool bUVs, ref Index4i tuv) { if (bNormals == false && bUVs == false) { writer.WriteLine("f {0} {1} {2} {3}", q[0], q[1], q[2], q[3]); } else if (bNormals == true && bUVs == false) { writer.WriteLine("f {0}//{0} {1}//{1} {2}//{2} {3}//{3}", q[0], q[1], q[2], q[3]); } else if (bNormals == false && bUVs == true) { writer.WriteLine("f {0}/{4} {1}/{5} {2}/{6} {3}/{7}", q[0], q[1], q[2], q[3], tuv[0], tuv[1], tuv[2], tuv[3]); } else { writer.WriteLine("f {0}/{4}/{0} {1}/{5}/{1} {2}/{6}/{2} {3}/{7}/{3}", q[0], q[1], q[2], q[3], tuv[0], tuv[1], tuv[2], tuv[3]); } }
/// <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); }
public int CompareTo(Index4i other) { if (a != other.a) { return(a < other.a ? -1 : 1); } else if (b != other.b) { return(b < other.b ? -1 : 1); } else if (c != other.c) { return(c < other.c ? -1 : 1); } else if (d != other.d) { return(d < other.d ? -1 : 1); } return(0); }
public void Set(Index4i o) { a = o[0]; b = o[1]; c = o[2]; d = o[3]; }
public Index4i(Index4i copy) { a = copy.a; b = copy.b; c = copy.b; d = copy.d; }
public static bool Sort(ref int v0, ref int v1, ref int v2, ref int v3) { int j0, j1, j2, j3; bool positive; if (v0 < v1) { if (v2 < v3) { if (v1 < v2) { j0 = 0; j1 = 1; j2 = 2; j3 = 3; positive = true; } else if (v3 < v0) { j0 = 2; j1 = 3; j2 = 0; j3 = 1; positive = true; } else if (v2 < v0) { if (v3 < v1) { j0 = 2; j1 = 0; j2 = 3; j3 = 1; positive = false; } else { j0 = 2; j1 = 0; j2 = 1; j3 = 3; positive = true; } } else { if (v3 < v1) { j0 = 0; j1 = 2; j2 = 3; j3 = 1; positive = true; } else { j0 = 0; j1 = 2; j2 = 1; j3 = 3; positive = false; } } } else { if (v1 < v3) { j0 = 0; j1 = 1; j2 = 3; j3 = 2; positive = false; } else if (v2 < v0) { j0 = 3; j1 = 2; j2 = 0; j3 = 1; positive = false; } else if (v3 < v0) { if (v2 < v1) { j0 = 3; j1 = 0; j2 = 2; j3 = 1; positive = true; } else { j0 = 3; j1 = 0; j2 = 1; j3 = 2; positive = false; } } else { if (v2 < v1) { j0 = 0; j1 = 3; j2 = 2; j3 = 1; positive = false; } else { j0 = 0; j1 = 3; j2 = 1; j3 = 2; positive = true; } } } } else { if (v2 < v3) { if (v0 < v2) { j0 = 1; j1 = 0; j2 = 2; j3 = 3; positive = false; } else if (v3 < v1) { j0 = 2; j1 = 3; j2 = 1; j3 = 0; positive = false; } else if (v2 < v1) { if (v3 < v0) { j0 = 2; j1 = 1; j2 = 3; j3 = 0; positive = true; } else { j0 = 2; j1 = 1; j2 = 0; j3 = 3; positive = false; } } else { if (v3 < v0) { j0 = 1; j1 = 2; j2 = 3; j3 = 0; positive = false; } else { j0 = 1; j1 = 2; j2 = 0; j3 = 3; positive = true; } } } else { if (v0 < v3) { j0 = 1; j1 = 0; j2 = 3; j3 = 2; positive = true; } else if (v2 < v1) { j0 = 3; j1 = 2; j2 = 1; j3 = 0; positive = true; } else if (v3 < v1) { if (v2 < v0) { j0 = 3; j1 = 1; j2 = 2; j3 = 0; positive = false; } else { j0 = 3; j1 = 1; j2 = 0; j3 = 2; positive = true; } } else { if (v2 < v0) { j0 = 1; j1 = 3; j2 = 2; j3 = 0; positive = true; } else { j0 = 1; j1 = 3; j2 = 0; j3 = 2; positive = false; } } } } Index4i value = new Index4i(v0, v1, v2, v3); v0 = value[j0]; v1 = value[j1]; v2 = value[j2]; v3 = value[j3]; return(positive); }
public MeshResult MergeEdges(int eKeep, int eDiscard, out MergeEdgesInfo merge_info) { merge_info = new MergeEdgesInfo(); if (IsEdge(eKeep) == false || IsEdge(eDiscard) == false) { return(MeshResult.Failed_NotAnEdge); } Index4i edgeinfo_keep = GetEdge(eKeep); Index4i edgeinfo_discard = GetEdge(eDiscard); if (edgeinfo_keep.d != InvalidID || edgeinfo_discard.d != InvalidID) { return(MeshResult.Failed_NotABoundaryEdge); } int a = edgeinfo_keep.a, b = edgeinfo_keep.b; int tab = edgeinfo_keep.c; int eab = eKeep; int c = edgeinfo_discard.a, d = edgeinfo_discard.b; int tcd = edgeinfo_discard.c; int ecd = eDiscard; // Need to correctly orient a,b and c,d and then check that // we will not join triangles with incompatible winding order // I can't see how to do this purely topologically. // So relying on closest-pairs testing. IndexUtil.orient_tri_edge(ref a, ref b, GetTriangle(tab)); //int tcd_otherv = IndexUtil.orient_tri_edge_and_find_other_vtx(ref c, ref d, GetTriangle(tcd)); IndexUtil.orient_tri_edge(ref c, ref d, GetTriangle(tcd)); int x = c; c = d; d = x; // joinable bdry edges have opposing orientations, so flip to get ac and b/d correspondences Vector3d Va = GetVertex(a), Vb = GetVertex(b), Vc = GetVertex(c), Vd = GetVertex(d); if ((Va.DistanceSquared(Vc) + Vb.DistanceSquared(Vd)) > (Va.DistanceSquared(Vd) + Vb.DistanceSquared(Vc))) { return(MeshResult.Failed_SameOrientation); } // alternative that detects normal flip of triangle tcd. This is a more // robust geometric test, but fails if tri is degenerate...also more expensive //Vector3d otherv = GetVertex(tcd_otherv); //Vector3d Ncd = MathUtil.FastNormalDirection(GetVertex(c), GetVertex(d), otherv); //Vector3d Nab = MathUtil.FastNormalDirection(GetVertex(a), GetVertex(b), otherv); //if (Ncd.Dot(Nab) < 0) //return MeshResult.Failed_SameOrientation; merge_info.eKept = eab; merge_info.eRemoved = ecd; // [TODO] this acts on each interior tri twice. could avoid using vtx-tri iterator? List <int> edges_a = vertex_edges[a]; if (a != c) { // replace c w/ a in edges and tris connected to c, and move edges to a List <int> edges_c = vertex_edges[c]; for (int i = 0; i < edges_c.Count; ++i) { int eid = edges_c[i]; if (eid == eDiscard) { continue; } replace_edge_vertex(eid, c, a); short rc = 0; if (replace_tri_vertex(edges[4 * eid + 2], c, a) >= 0) { rc++; } if (edges[4 * eid + 3] != InvalidID) { if (replace_tri_vertex(edges[4 * eid + 3], c, a) >= 0) { rc++; } } edges_a.Add(eid); if (rc > 0) { vertices_refcount.increment(a, rc); vertices_refcount.decrement(c, rc); } } vertex_edges[c] = null; vertices_refcount.decrement(c); merge_info.vRemoved[0] = c; } else { edges_a.Remove(ecd); merge_info.vRemoved[0] = InvalidID; } merge_info.vKept[0] = a; List <int> edges_b = vertex_edges[b]; if (d != b) { // replace d w/ b in edges and tris connected to d, and move edges to b List <int> edges_d = vertex_edges[d]; for (int i = 0; i < edges_d.Count; ++i) { int eid = edges_d[i]; if (eid == eDiscard) { continue; } replace_edge_vertex(eid, d, b); short rc = 0; if (replace_tri_vertex(edges[4 * eid + 2], d, b) >= 0) { rc++; } if (edges[4 * eid + 3] != InvalidID) { if (replace_tri_vertex(edges[4 * eid + 3], d, b) >= 0) { rc++; } } edges_b.Add(eid); if (rc > 0) { vertices_refcount.increment(b, rc); vertices_refcount.decrement(d, rc); } } vertex_edges[d] = null; vertices_refcount.decrement(d); merge_info.vRemoved[1] = d; } else { edges_b.Remove(ecd); merge_info.vRemoved[1] = InvalidID; } merge_info.vKept[1] = b; // replace edge cd with edge ab in triangle tcd replace_triangle_edge(tcd, ecd, eab); edges_refcount.decrement(ecd); // update edge-tri adjacency set_edge_triangles(eab, tab, tcd); // Once we merge ab to cd, there may be additional edges (now) connected // to either a or b that are connected to the same vertex on their 'other' side. // So we now have two boundary edges connecting the same two vertices - disaster! // We need to find and merge these edges. // Q: I don't think it is possible to have multiple such edge-pairs at a or b // But I am not certain...is a bit tricky to handle because we modify edges_v... merge_info.eRemovedExtra = new Vector2i(InvalidID, InvalidID); merge_info.eKeptExtra = merge_info.eRemovedExtra; for (int vi = 0; vi < 2; ++vi) { int v1 = a, v2 = c; // vertices of merged edge if (vi == 1) { v1 = b; v2 = d; } if (v1 == v2) { continue; } List <int> edges_v = vertex_edges[v1]; int Nedges = edges_v.Count; bool found = false; // in this loop, we compare 'other' vert_1 and vert_2 of edges around v1. // problem case is when vert_1 == vert_2 (ie two edges w/ same other vtx). for (int i = 0; i < Nedges && found == false; ++i) { int edge_1 = edges_v[i]; if (edge_is_boundary(edge_1) == false) { continue; } int vert_1 = edge_other_v(edge_1, v1); for (int j = i + 1; j < Nedges; ++j) { int edge_2 = edges_v[j]; int vert_2 = edge_other_v(edge_2, v1); if (vert_1 == vert_2 && edge_is_boundary(edge_2)) // if ! boundary here, we are in deep trouble... // replace edge_2 w/ edge_1 in tri, update edge and vtx-edge-nbr lists { int tri_1 = edges[4 * edge_1 + 2]; int tri_2 = edges[4 * edge_2 + 2]; replace_triangle_edge(tri_2, edge_2, edge_1); set_edge_triangles(edge_1, tri_1, tri_2); vertex_edges[v1].Remove(edge_2); vertex_edges[vert_1].Remove(edge_2); edges_refcount.decrement(edge_2); merge_info.eRemovedExtra[vi] = edge_2; merge_info.eKeptExtra[vi] = edge_1; //Nedges = edges_v.Count; // this code allows us to continue checking, ie in case we had //i--; // multiple such edges. but I don't think it's possible. found = true; // exit outer i loop break; // exit inner j loop } } } } return(MeshResult.Ok); }
/// <summary> /// Check if this m2 is the same as this mesh. By default only checks /// vertices and triangles, turn on other parameters w/ flags /// </summary> public bool IsSameMesh(DMesh3 m2, bool bCheckConnectivity, bool bCheckEdgeIDs = false, bool bCheckNormals = false, bool bCheckColors = false, bool bCheckUVs = false, bool bCheckGroups = false, float Epsilon = MathUtil.Epsilonf) { if (VertexCount != m2.VertexCount) { return(false); } if (TriangleCount != m2.TriangleCount) { return(false); } foreach (int vid in VertexIndices()) { if (m2.IsVertex(vid) == false || GetVertex(vid).EpsilonEqual(m2.GetVertex(vid), Epsilon) == false) { return(false); } } foreach (int tid in TriangleIndices()) { if (m2.IsTriangle(tid) == false || GetTriangle(tid).Equals(m2.GetTriangle(tid)) == false) { return(false); } } if (bCheckConnectivity) { foreach (int eid in EdgeIndices()) { Index4i e = GetEdge(eid); int other_eid = m2.FindEdge(e.a, e.b); if (other_eid == InvalidID) { return(false); } Index4i oe = m2.GetEdge(other_eid); if (Math.Min(e.c, e.d) != Math.Min(oe.c, oe.d) || Math.Max(e.c, e.d) != Math.Max(oe.c, oe.d)) { return(false); } } } if (bCheckEdgeIDs) { if (EdgeCount != m2.EdgeCount) { return(false); } foreach (int eid in EdgeIndices()) { if (m2.IsEdge(eid) == false || GetEdge(eid).Equals(m2.GetEdge(eid)) == false) { return(false); } } } if (bCheckNormals) { if (HasVertexNormals != m2.HasVertexNormals) { return(false); } if (HasVertexNormals) { foreach (int vid in VertexIndices()) { if (GetVertexNormal(vid).EpsilonEqual(m2.GetVertexNormal(vid), Epsilon) == false) { return(false); } } } } if (bCheckColors) { if (HasVertexColors != m2.HasVertexColors) { return(false); } if (HasVertexColors) { foreach (int vid in VertexIndices()) { if (GetVertexColor(vid).EpsilonEqual(m2.GetVertexColor(vid), Epsilon) == false) { return(false); } } } } if (bCheckUVs) { if (HasVertexUVs != m2.HasVertexUVs) { return(false); } if (HasVertexUVs) { foreach (int vid in VertexIndices()) { if (GetVertexUV(vid).EpsilonEqual(m2.GetVertexUV(vid), Epsilon) == false) { return(false); } } } } if (bCheckGroups) { if (HasTriangleGroups != m2.HasTriangleGroups) { return(false); } if (HasTriangleGroups) { foreach (int tid in TriangleIndices()) { if (GetTriangleGroup(tid) != m2.GetTriangleGroup(tid)) { return(false); } } } } return(true); }
/// <summary> /// Stitch two sets of boundary edges that are provided as unordered pairs of edges, by /// adding triangulated quads between each edge pair. /// If bAbortOnFailure==true and a failure is encountered during stitching, the triangles added up to that point are removed. /// If bAbortOnFailure==false, failures are ignored and the returned triangle list may contain invalid values! /// </summary> public virtual int[] StitchUnorderedEdges(List <Index2i> EdgePairs, int group_id, bool bAbortOnFailure, out bool stitch_incomplete) { int N = EdgePairs.Count; int[] new_tris = new int[N * 2]; if (bAbortOnFailure == false) { for (int k = 0; k < new_tris.Length; ++k) { new_tris[k] = DMesh3.InvalidID; } } stitch_incomplete = false; int i = 0; for (; i < N; ++i) { Index2i edges = EdgePairs[i]; // look up and orient the first edge Index4i edge_a = Mesh.GetEdge(edges.a); if (edge_a.d != DMesh3.InvalidID) { if (bAbortOnFailure) { goto operation_failed; } else { stitch_incomplete = true; continue; } } Index3i edge_a_tri = Mesh.GetTriangle(edge_a.c); int a = edge_a.a, b = edge_a.b; IndexUtil.orient_tri_edge(ref a, ref b, edge_a_tri); // look up and orient the second edge Index4i edge_b = Mesh.GetEdge(edges.b); if (edge_b.d != DMesh3.InvalidID) { if (bAbortOnFailure) { goto operation_failed; } else { stitch_incomplete = true; continue; } } Index3i edge_b_tri = Mesh.GetTriangle(edge_b.c); int c = edge_b.a, d = edge_b.b; IndexUtil.orient_tri_edge(ref c, ref d, edge_b_tri); // swap second edge (right? should this be a parameter?) int tmp = c; c = d; d = tmp; var t1 = new Index3i(b, a, d); var t2 = new Index3i(a, c, d); int tid1 = Mesh.AppendTriangle(t1, group_id); int tid2 = Mesh.AppendTriangle(t2, group_id); if (tid1 < 0 || tid2 < 0) { if (bAbortOnFailure) { goto operation_failed; } else { stitch_incomplete = true; continue; } } new_tris[2 * i] = tid1; new_tris[2 * i + 1] = tid2; } return(new_tris); operation_failed: // remove what we added so far if (i > 0) { if (remove_triangles(new_tris, 2 * (i - 1)) == false) { throw new Exception("MeshEditor.StitchLoop: failed to add all triangles, and also failed to back out changes."); } } return(null); }
public bool Equals(Index4i other) { return(a == other.a && b == other.b && c == other.c && d == other.d); }
/// <summary> /// Stitch two sets of boundary edges that are provided as unordered pairs of edges, by /// adding triangulated quads between each edge pair. /// If a failure is encountered during stitching, the triangles added up to that point are removed. /// </summary> public virtual int[] StitchUnorderedEdges(List <Index2i> EdgePairs, int group_id = -1) { int N = EdgePairs.Count; int[] new_tris = new int[N * 2]; int i = 0; for (; i < N; ++i) { Index2i edges = EdgePairs[i]; // look up and orient the first edge Index4i edge_a = Mesh.GetEdge(edges.a); if (edge_a.d != DMesh3.InvalidID) { goto operation_failed; } Index3i edge_a_tri = Mesh.GetTriangle(edge_a.c); int a = edge_a.a, b = edge_a.b; IndexUtil.orient_tri_edge(ref a, ref b, edge_a_tri); // look up and orient the second edge Index4i edge_b = Mesh.GetEdge(edges.b); if (edge_b.d != DMesh3.InvalidID) { goto operation_failed; } Index3i edge_b_tri = Mesh.GetTriangle(edge_b.c); int c = edge_b.a, d = edge_b.b; IndexUtil.orient_tri_edge(ref c, ref d, edge_b_tri); // swap second edge (right? should this be a parameter?) int tmp = c; c = d; d = tmp; Index3i t1 = new Index3i(b, a, d); Index3i t2 = new Index3i(a, c, d); int tid1 = Mesh.AppendTriangle(t1, group_id); int tid2 = Mesh.AppendTriangle(t2, group_id); if (tid1 == DMesh3.InvalidID || tid2 == DMesh3.InvalidID) { goto operation_failed; } new_tris[2 * i] = tid1; new_tris[2 * i + 1] = tid2; } return(new_tris); operation_failed: // remove what we added so far if (i > 0) { if (remove_triangles(new_tris, 2 * (i - 1)) == false) { throw new Exception("MeshConstructor.StitchLoop: failed to add all triangles, and also failed to back out changes."); } } return(null); }
// Walk along edge loop and collapse to inserted curve vertices. EdgeLoop simplify(EdgeLoop loop) { HashSet <int> curve_verts = new HashSet <int>(CurveVertices); List <int> remaining_edges = new List <int>(); for (int li = 0; li < loop.EdgeCount; ++li) { int eid = loop.Edges[li]; Index2i ev = Mesh.GetEdgeV(eid); // cannot collapse edge between two "original" polygon verts (ie created by face pokes) if (curve_verts.Contains(ev.a) && curve_verts.Contains(ev.b)) { remaining_edges.Add(eid); continue; } // if we have an original vert, we need to keep it (and its position!) int keep = ev.a, discard = ev.b; Vector3d set_to = Vector3d.Zero; if (curve_verts.Contains(ev.b)) { keep = ev.b; discard = ev.a; set_to = Mesh.GetVertex(ev.b); } else if (curve_verts.Contains(ev.a)) { set_to = Mesh.GetVertex(ev.a); } else { set_to = 0.5 * (Mesh.GetVertex(ev.a) + Mesh.GetVertex(ev.b)); } // make sure we are not going to flip any normals // [OPTIMIZATION] May be possible to do this more efficiently because we know we are in // 2D and each tri should have same cw/ccw orientation. But we don't quite "know" we // are in 2D here, as CollapseEdge function is operating on the mesh coordinates... if (MeshUtil.CheckIfCollapseCreatesFlip(Mesh, eid, set_to)) { remaining_edges.Add(eid); continue; } // cannot collapse if the 'other' edges we would discard are OnCutEdges. This would // result in loop potentially being broken. bad! Index4i einfo = Mesh.GetEdge(eid); int c = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.c)); int d = IndexUtil.find_tri_other_vtx(keep, discard, Mesh.GetTriangle(einfo.d)); int ec = Mesh.FindEdge(discard, c); int ed = Mesh.FindEdge(discard, d); if (OnCutEdges.Contains(ec) || OnCutEdges.Contains(ed)) { remaining_edges.Add(eid); continue; } // do collapse and update internal data structures DMesh3.EdgeCollapseInfo collapse; MeshResult result = Mesh.CollapseEdge(keep, discard, out collapse); if (result == MeshResult.Ok) { Mesh.SetVertex(collapse.vKept, set_to); OnCutEdges.Remove(collapse.eCollapsed); } else { remaining_edges.Add(eid); } } return(EdgeLoop.FromEdges(Mesh, remaining_edges)); }
public static IOWriteResult WriteOBJ(SimpleQuadMesh mesh, string sPath, WriteOptions options) { StreamWriter writer = new StreamWriter(sPath); if (writer.BaseStream == null) { return(new IOWriteResult(IOCode.FileAccessError, "Could not open file " + sPath + " for writing")); } bool bVtxColors = options.bPerVertexColors && mesh.HasVertexColors; bool bNormals = options.bPerVertexNormals && mesh.HasVertexNormals; // use separate UV set if we have it, otherwise write per-vertex UVs if we have those bool bVtxUVs = options.bPerVertexUVs && mesh.HasVertexUVs; if (mesh.UVs != null) { bVtxUVs = false; } int[] mapV = new int[mesh.MaxVertexID]; int nAccumCountV = 1; // OBJ indices always start at 1 // write vertices for this mesh foreach (int vi in mesh.VertexIndices()) { mapV[vi] = nAccumCountV++; Vector3d v = mesh.GetVertex(vi); if (bVtxColors) { Vector3d c = mesh.GetVertexColor(vi); writer.WriteLine("v {0} {1} {2} {3:F8} {4:F8} {5:F8}", v[0], v[1], v[2], c[0], c[1], c[2]); } else { writer.WriteLine("v {0} {1} {2}", v[0], v[1], v[2]); } if (bNormals) { Vector3d n = mesh.GetVertexNormal(vi); writer.WriteLine("vn {0:F10} {1:F10} {2:F10}", n[0], n[1], n[2]); } if (bVtxUVs) { Vector2f uv = mesh.GetVertexUV(vi); writer.WriteLine("vt {0:F10} {1:F10}", uv.x, uv.y); } } foreach (int ti in mesh.QuadIndices()) { Index4i q = mesh.GetQuad(ti); q[0] = mapV[q[0]]; q[1] = mapV[q[1]]; q[2] = mapV[q[2]]; q[3] = mapV[q[3]]; write_quad(writer, ref q, bNormals, bVtxUVs, ref q); } writer.Close(); return(IOWriteResult.Ok); }