Example #1
0
        /// <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);
        }
Example #2
0
        /// <summary>
        /// Check if edge flip might reverse normal direction.
        ///
        /// Not entirely clear on how to implement this test.
        /// Currently checking if any normal-pairs are reversed.
        /// </summary>
        protected bool flip_inverts_normals(int a, int b, int c, int d, int t0)
        {
            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 (n0.Dot(f0) < 0 || n1.Dot(f0) < 0)
            {
                return(true);
            }
            Vector3d f1 = MathUtil.FastNormalDirection(ref vD, ref vC, ref vOA);

            if (n0.Dot(f1) < 0 || n1.Dot(f1) < 0)
            {
                return(true);
            }

            // this only checks if output faces are pointing towards eachother, which seems
            // to still result in normal-flips in some cases
            //if (f0.Dot(f1) < 0)
            //    return true;

            return(false);
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
        /// <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);
        }
Example #7
0
        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);
        }