Exemple #1
0
        public virtual bool Extrude(int group_id = -1)
        {
            // duplicate loop vertices
            int NV = Loop.Vertices.Length;

            NewLoop          = new EdgeLoop(Mesh);
            NewLoop.Vertices = new int[NV];

            for (int i = 0; i < NV; ++i)
            {
                int vid = Loop.Vertices[i];
                NewLoop.Vertices[i] = Mesh.AppendVertex(Mesh, vid);
            }

            // move to offset positions
            for (int i = 0; i < NV; ++i)
            {
                Vector3D v     = Mesh.GetVertex(Loop.Vertices[i]);
                Vector3F n     = Mesh.GetVertexNormal(Loop.Vertices[i]);
                Vector3D new_v = PositionF(v, n, i);
                Mesh.SetVertex(NewLoop.Vertices[i], new_v);
            }

            // stitch interior
            MeshEditor edit = new MeshEditor(Mesh);

            NewTriangles = edit.StitchLoop(Loop.Vertices, NewLoop.Vertices, group_id);

            return(true);
        }
Exemple #2
0
        public virtual bool Fill(int group_id = -1)
        {
            if (Loop.Vertices.Length < 3)
            {
                return(false);
            }

            // this just needs one triangle
            if (Loop.Vertices.Length == 3)
            {
                Index3i tri     = new Index3i(Loop.Vertices[0], Loop.Vertices[2], Loop.Vertices[1]);
                int     new_tid = Mesh.AppendTriangle(tri, group_id);
                if (new_tid == NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID)
                {
                    return(false);
                }
                NewTriangles = new int[1] {
                    new_tid
                };
                NewVertex = NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID;
                return(true);
            }

            // [TODO] 4-case? could check nbr normals to figure out best internal edge...


            // compute centroid
            Vector3D c = Vector3D.Zero;

            for (int i = 0; i < Loop.Vertices.Length; ++i)
            {
                c += Mesh.GetVertex(Loop.Vertices[i]);
            }
            c *= 1.0 / Loop.Vertices.Length;

            // add centroid vtx
            NewVertex = Mesh.AppendVertex(c);

            // stitch triangles
            MeshEditor editor = new MeshEditor(Mesh);

            try {
                NewTriangles = editor.AddTriangleFan_OrderedVertexLoop(NewVertex, Loop.Vertices, group_id);
            } catch {
                NewTriangles = null;
            }

            // if fill failed, back out vertex-add
            if (NewTriangles == null)
            {
                Mesh.RemoveVertex(NewVertex);
                NewVertex = NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID;
            }

            return(true);
        }
Exemple #3
0
        public virtual bool Smooth()
        {
            int NV = Loop.Vertices.Length;

            double a          = math.MathUtil.Clamp(Alpha, 0, 1);
            double num_rounds = math.MathUtil.Clamp(Rounds, 0, 10000);

            for (int round = 0; round < num_rounds; ++round)
            {
                // compute
                gParallel.ForEach(Interval1i.Range(NV), (i) => {
                    int vid       = Loop.Vertices[(i + 1) % NV];
                    Vector3D prev = Mesh.GetVertex(Loop.Vertices[i]);
                    Vector3D cur  = Mesh.GetVertex(vid);
                    Vector3D next = Mesh.GetVertex(Loop.Vertices[(i + 2) % NV]);

                    Vector3D centroid   = (prev + next) * 0.5;
                    SmoothedPostions[i] = (1 - a) * cur + (a) * centroid;
                });

                // bake
                gParallel.ForEach(Interval1i.Range(NV), (i) => {
                    int vid      = Loop.Vertices[(i + 1) % NV];
                    Vector3D pos = SmoothedPostions[i];

                    if (ProjectF != null)
                    {
                        pos = ProjectF(pos, vid);
                    }

                    Mesh.SetVertex(vid, pos);
                });
            }

            return(true);
        }
Exemple #4
0
        // Modes: 0: centroids, 1: any vertex, 2: 2 vertices, 3: all vertices
        // ContainF should return true if 3D position is in set (eg inside box, etc)
        // If mode = 0, will be called with (centroid, tri_idx)
        // If mode = 1/2/3, will be called with (vtx_pos, vtx_idx)
        // AddF is called with triangle IDs that are in set
        public static void TrianglesContained(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, Func <Vector3D, int, bool> ContainF, Action <int> AddF, int nMode = 0)
        {
            BitArray inV = null;

            if (nMode != 0)
            {
                inV = new BitArray(mesh.MaxVertexID);
                foreach (int vid in mesh.VertexIndices())
                {
                    if (ContainF(mesh.GetVertex(vid), vid))
                    {
                        inV[vid] = true;
                    }
                }
            }

            foreach (int tid in mesh.TriangleIndices())
            {
                Index3i tri = mesh.GetTriangle(tid);

                bool bIn = false;
                if (nMode == 0)
                {
                    if (ContainF(mesh.GetTriCentroid(tid), tid))
                    {
                        bIn = true;
                    }
                }
                else
                {
                    int countIn = (inV[tri.a] ? 1 : 0) + (inV[tri.b] ? 1 : 0) + (inV[tri.c] ? 1 : 0);
                    bIn = (countIn >= nMode);
                }

                if (bIn)
                {
                    AddF(tid);
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            BitArray used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            List <int> loop_edges = new List <int>();     // [RMS] not sure we need this...
            List <int> loop_verts = new List <int>();
            List <int> bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }
                if (used_edge[eid] == true)
                {
                    continue;
                }
                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    loop_verts.Add(cure_a);

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }
                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)
                    {
                        throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found broken neighbourhood at vertex " + cure_b)
                              {
                                  UnclosedLoop = true
                              }
                    }
                    ;

                    int eNext = -1;
                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            Debug.Assert(num_be == bdry_nbrs);

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);

                            if (eNext == -1)
                            {
                                throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                      {
                                          BowtieFailure = true
                                      }
                            }
                            ;
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        eCur            = eNext;
                        used_edge[eCur] = true;
                    }
                }

                // if we saw a bowtie vertex, we might need to break up this loop,
                // so call extract_subloops
                if (bowties.Count > 0)
                {
                    List <EdgeLoop> subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    for (int i = 0; i < subloops.Count; ++i)
                    {
                        Loops.Add(subloops[i]);
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    EdgeLoop loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }

        // [TODO] cache this in a dictionary? we will not need very many, but we will
        //   need each multiple times!
        Vector3D get_vtx_normal(int vid)
        {
            Vector3D n = Vector3D.Zero;

            foreach (int ti in Mesh.VtxTrianglesItr(vid))
            {
                n += Mesh.GetTriNormal(ti);
            }
            n.Normalize();
            return(n);
        }

        // 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 = math.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);
        }

        // This is called when loopV contains one or more "bowtie" vertices.
        // These vertices *might* be duplicated in loopV (but not necessarily)
        // If they are, we have to break loopV into subloops that don't contain duplicates.
        //
        // The list bowties contains all the possible duplicates
        // (all v in bowties occur in loopV at least once)
        //
        // Currently loopE is not used, and the returned EdgeLoop objects do not have their Edges
        // arrays initialized. Perhaps to improve in future.
        List <EdgeLoop> extract_subloops(List <int> loopV, List <int> loopE, List <int> bowties)
        {
            List <EdgeLoop> subs = new List <EdgeLoop>();

            // figure out which bowties we saw are actually duplicated in loopV
            List <int> dupes = new List <int>();

            foreach (int bv in bowties)
            {
                if (count_in_list(loopV, bv) > 1)
                {
                    dupes.Add(bv);
                }
            }

            // we might not actually have any duplicates, if we got luck. Early out in that case
            if (dupes.Count == 0)
            {
                subs.Add(new EdgeLoop(Mesh)
                {
                    Vertices = loopV.ToArray(), Edges = loopE.ToArray(), BowtieVertices = bowties.ToArray()
                });
                return(subs);
            }

            // This loop extracts subloops until we have dealt with all the
            // duplicate vertices in loopV
            while (dupes.Count > 0)
            {
                // Find shortest "simple" loop, ie a loop from a bowtie to itself that
                // does not contain any other bowties. This is an independent loop.
                // We're doing a lot of extra work here if we only have one element in dupes...
                int bi = 0, bv = 0;
                int start_i = -1, end_i = -1;
                int bv_shortest = -1; int shortest = int.MaxValue;
                for ( ; bi < dupes.Count; ++bi)
                {
                    bv = dupes[bi];
                    if (is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i))
                    {
                        int len = count_span(loopV, start_i, end_i);
                        if (len < shortest)
                        {
                            bv_shortest = bv;
                            shortest    = len;
                        }
                    }
                }
                if (bv_shortest == -1)
                {
                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: Cannot find a valid simple loop");
                }
                if (bv != bv_shortest)
                {
                    bv = bv_shortest;
                    // running again just to get start_i and end_i...
                    is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i);
                }

                Debug.Assert(loopV[start_i] == bv && loopV[end_i] == bv);

                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices       = extract_span(loopV, start_i, end_i, true);
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);

                // If there are no more duplicates of this bowtie, we can treat
                // it like a regular vertex now
                if (count_in_list(loopV, bv) < 2)
                {
                    dupes.Remove(bv);
                }
            }

            // Should have one loop left that contains duplicates.
            // Extract this as a separate loop
            int nLeft = 0;

            for (int i = 0; i < loopV.Count; ++i)
            {
                if (loopV[i] != -1)
                {
                    nLeft++;
                }
            }
            if (nLeft > 0)
            {
                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices = new int[nLeft];
                int vi = 0;
                for (int i = 0; i < loopV.Count; ++i)
                {
                    if (loopV[i] != -1)
                    {
                        loop.Vertices[vi++] = loopV[i];
                    }
                }
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);
            }

            return(subs);
        }

        /*
         * In all the functions below, the list loopV is assumed to possibly
         * contain "removed" vertices indicated by -1. These are ignored.
         */


        // Check if the loop from bowtieV to bowtieV inside loopV contains any other bowtie verts.
        // Also returns start and end indices in loopV of "clean" loop
        // Note that start may be < end, if the "clean" loop wraps around the end
        bool is_simple_bowtie_loop(List <int> loopV, List <int> bowties, int bowtieV, out int start_i, out int end_i)
        {
            // find two indices of bowtie vert
            start_i = find_index(loopV, 0, bowtieV);
            end_i   = find_index(loopV, start_i + 1, bowtieV);

            if (is_simple_path(loopV, bowties, bowtieV, start_i, end_i))
            {
                return(true);
            }
            else if (is_simple_path(loopV, bowties, bowtieV, end_i, start_i))
            {
                int tmp = start_i; start_i = end_i; end_i = tmp;
                return(true);
            }
            else
            {
                return(false);       // not a simple bowtie loop!
            }
        }

        // check if forward path from loopV[i1] to loopV[i2] contains any bowtie verts other than bowtieV
        bool is_simple_path(List <int> loopV, List <int> bowties, int bowtieV, int i1, int i2)
        {
            int N = loopV.Count;

            for (int i = i1; i != i2; i = (i + 1) % N)
            {
                int vi = loopV[i];
                if (vi == -1)
                {
                    continue;       // skip removed vertices
                }
                if (vi != bowtieV && bowties.Contains(vi))
                {
                    return(false);
                }
            }
            return(true);
        }

        // Read out the span from loop[i0] to loop [i1-1] into an array.
        // If bMarkInvalid, then these values are set to -1 in loop
        int[] extract_span(List <int> loop, int i0, int i1, bool bMarkInvalid)
        {
            int num = count_span(loop, i0, i1);

            int[] a  = new int[num];
            int   ai = 0;
            int   N  = loop.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (loop[i] != -1)
                {
                    a[ai++] = loop[i];
                    if (bMarkInvalid)
                    {
                        loop[i] = -1;
                    }
                }
            }
            return(a);
        }

        // count number of valid vertices in l between loop[i0] and loop[i1-1]
        int count_span(List <int> l, int i0, int i1)
        {
            int c = 0;
            int N = l.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (l[i] != -1)
                {
                    c++;
                }
            }
            return(c);
        }

        // find the index of item in loop, starting at start index
        int find_index(List <int> loop, int start, int item)
        {
            for (int i = start; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    return(i);
                }
            }
            return(-1);
        }

        // count number of times item appears in loop
        int count_in_list(List <int> loop, int item)
        {
            int c = 0;

            for (int i = 0; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    c++;
                }
            }
            return(c);
        }
    }
Exemple #6
0
        public virtual bool Cut()
        {
            double invalidDist = double.MinValue;

            // compute signs
            int MaxVID = Mesh.MaxVertexID;

            double[] signs = new double[MaxVID];
            gParallel.ForEach(Interval1i.Range(MaxVID), (vid) => {
                if (Mesh.IsVertex(vid))
                {
                    Vector3D v = Mesh.GetVertex(vid);
                    signs[vid] = (v - PlaneOrigin).Dot(PlaneNormal);
                }
                else
                {
                    signs[vid] = invalidDist;
                }
            });

            HashSet <int> ZeroEdges    = new HashSet <int>();
            HashSet <int> ZeroVertices = new HashSet <int>();
            HashSet <int> OnCutEdges   = new HashSet <int>();

            // have to skip processing of new edges. If edge id
            // is > max at start, is new. Otherwise if in NewEdges list, also new.
            int           MaxEID   = Mesh.MaxEdgeID;
            HashSet <int> NewEdges = new HashSet <int>();

            // cut existing edges with plane, using edge split
            for (int eid = 0; eid < MaxEID; ++eid)
            {
                if (Mesh.IsEdge(eid) == false)
                {
                    continue;
                }
                if (eid >= MaxEID || NewEdges.Contains(eid))
                {
                    continue;
                }

                Index2i ev = Mesh.GetEdgeV(eid);
                double  f0 = signs[ev.a];
                double  f1 = signs[ev.b];

                // If both signs are 0, this edge is on-contour
                // If one sign is 0, that vertex is on-contour
                int n0 = (Math.Abs(f0) < math.MathUtil.Epsilon) ? 1 : 0;
                int n1 = (Math.Abs(f1) < math.MathUtil.Epsilon) ? 1 : 0;
                if (n0 + n1 > 0)
                {
                    if (n0 + n1 == 2)
                    {
                        ZeroEdges.Add(eid);
                    }
                    else
                    {
                        ZeroVertices.Add((n0 == 1) ? ev[0] : ev[1]);
                    }
                    continue;
                }

                // no crossing
                if (f0 * f1 > 0)
                {
                    continue;
                }

                NGonsCore.geometry3Sharp.mesh.DMesh3.EdgeSplitInfo splitInfo;
                MeshResult result = Mesh.SplitEdge(eid, out splitInfo);
                if (result != MeshResult.Ok)
                {
                    throw new Exception("MeshPlaneCut.Cut: failed in SplitEdge");
                    //return false;
                }

                // SplitEdge just bisects edge - use plane intersection instead
                double   t      = f0 / (f0 - f1);
                Vector3D newPos = (1 - t) * Mesh.GetVertex(ev.a) + (t) * Mesh.GetVertex(ev.b);
                Mesh.SetVertex(splitInfo.vNew, newPos);

                NewEdges.Add(splitInfo.eNewBN);
                NewEdges.Add(splitInfo.eNewCN);  OnCutEdges.Add(splitInfo.eNewCN);
                if (splitInfo.eNewDN != NGonsCore.geometry3Sharp.mesh.DMesh3.InvalidID)
                {
                    NewEdges.Add(splitInfo.eNewDN);
                    OnCutEdges.Add(splitInfo.eNewDN);
                }
            }

            // remove one-rings of all positive-side vertices.
            for (int i = 0; i < signs.Length; ++i)
            {
                if (signs[i] > 0 && Mesh.IsVertex(i))
                {
                    Mesh.RemoveVertex(i, true, false);
                }
            }

            // ok now we extract boundary loops, but restricted
            // to either the zero-edges we found, or the edges we created! bang!!
            Func <int, bool> CutEdgeFilterF = (eid) => {
                if (OnCutEdges.Contains(eid) || ZeroEdges.Contains(eid))
                {
                    return(true);
                }
                return(false);
            };

            try {
                MeshBoundaryLoops loops = new MeshBoundaryLoops(Mesh, false);
                loops.EdgeFilterF = CutEdgeFilterF;
                loops.Compute();

                CutLoops       = loops.Loops;
                CutLoopsFailed = false;
            } catch {
                CutLoops       = new List <EdgeLoop>();
                CutLoopsFailed = true;
            }

            return(true);
        }         // Cut()