Example #1
0
        public void Apply(DMesh3 mesh)
        {
            int N = ModifiedV.size;

            for (int i = 0; i < N; ++i)
            {
                int vid = ModifiedV[i];
                mesh.SetVertex(vid, NewPositions[i]);
                if (NewNormals != null)
                {
                    mesh.SetVertexNormal(vid, NewNormals[i]);
                }

                if (NewColors != null)
                {
                    mesh.SetVertexColor(vid, NewColors[i]);
                }

                if (NewUVs != null)
                {
                    mesh.SetVertexUV(vid, NewUVs[i]);
                }
            }
            if (OnApplyF != null)
            {
                OnApplyF(this);
            }
        }
Example #2
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);
        }
Example #3
0
        /// <summary>
        /// For each on-face vtx, we poke the face, and re-sort
        /// the remaining vertices on that face onto new faces/edges
        /// </summary>
        void insert_face_vertices()
        {
            while (FaceVertices.Count > 0)
            {
                var pair = FaceVertices.First();
                int tid  = pair.Key;
                List <SegmentVtx> triVerts = pair.Value;
                SegmentVtx        v        = triVerts[triVerts.Count - 1];
                triVerts.RemoveAt(triVerts.Count - 1);

                DMesh3.PokeTriangleInfo pokeInfo;
                MeshResult result = Target.PokeTriangle(tid, out pokeInfo);
                if (result != MeshResult.Ok)
                {
                    throw new Exception("shit");
                }

                int new_v = pokeInfo.new_vid;

                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
                FaceVertices.Remove(tid);

                // update remaining verts
                Index3i pokeEdges = pokeInfo.new_edges;
                var     pokeTris  = new Index3i(tid, pokeInfo.new_t1, pokeInfo.new_t2);
                foreach (SegmentVtx sv in triVerts)
                {
                    update_from_poke(sv, pokeEdges, pokeTris);
                    if (sv.type == 1)
                    {
                        add_edge_vtx(sv.elem_id, sv);
                    }
                    else if (sv.type == 2)
                    {
                        add_face_vtx(sv.elem_id, sv);
                    }
                }

                // track poke subfaces
                add_poke_subfaces(tid, ref pokeInfo);
            }
        }
 public static void Translate(DMesh3 mesh, double tx, double ty, double tz)
 {
     foreach (int vid in mesh.VertexIndices())
     {
         Vector3d v = mesh.GetVertex(vid);
         v.x += tx; v.y += ty; v.z += tz;
         mesh.SetVertex(vid, v);
     }
 }
 public static void Scale(DMesh3 mesh, double sx, double sy, double sz)
 {
     foreach (int vid in mesh.VertexIndices())
     {
         Vector3d v = mesh.GetVertex(vid);
         v.x *= sx; v.y *= sy; v.z *= sz;
         mesh.SetVertex(vid, v);
     }
 }
        public MeshInsertUVPolyCurve(DMesh3 mesh, PolyLine2d curve, bool isLoop = false)
        {
            Mesh   = mesh;
            Curve  = curve;
            IsLoop = isLoop;

            PointF    = (vid) => { return(Mesh.GetVertex(vid).xy); };
            SetPointF = (vid, pos) => { Mesh.SetVertex(vid, new Vector3d(pos.x, pos.y, 0)); };
        }
        public MeshInsertUVPolyCurve(DMesh3 mesh, PolyLine2d path)
        {
            Mesh   = mesh;
            Curve  = new PolyLine2d(path.Vertices);
            IsLoop = false;

            PointF    = (vid) => { return(Mesh.GetVertex(vid).xy); };
            SetPointF = (vid, pos) => { Mesh.SetVertex(vid, new Vector3d(pos.x, pos.y, 0)); };
        }
        public static void ScaleMesh(DMesh3 mesh, Frame3f f, Vector3f vScale)
        {
            foreach (int vid in mesh.VertexIndices())
            {
                Vector3f v          = (Vector3f)mesh.GetVertex(vid);
                Vector3f vScaledInF = f.ToFrameP(ref v) * vScale;
                Vector3d vNew       = f.FromFrameP(ref vScaledInF);
                mesh.SetVertex(vid, vNew);

                // TODO: normals
            }
        }
Example #9
0
        public virtual bool Smooth()
        {
            int NV = Vertices.Length;

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

            Func <DMesh3, int, double, Vector3d> smoothFunc = MeshUtil.UniformSmooth;

            if (SmoothType == SmoothTypes.MeanValue)
            {
                smoothFunc = MeshUtil.MeanValueSmooth;
            }
            else if (SmoothType == SmoothTypes.Cotan)
            {
                smoothFunc = MeshUtil.CotanSmooth;
            }

            Action <int> smooth = (i) =>
            {
                int vID = Vertices[i];
                SmoothedPostions[i] = smoothFunc(Mesh, vID, a);
            };
            Action <int> project = (i) =>
            {
                Vector3d pos = SmoothedPostions[i];
                SmoothedPostions[i] = ProjectF(pos, Vector3f.AxisY, Vertices[i]);
            };

            var indices = new IndexRangeEnumerator(0, NV);

            for (int round = 0; round < num_rounds; ++round)
            {
                gParallel.ForEach <int>(indices, smooth);
                if (ProjectF != null)
                {
                    gParallel.ForEach <int>(indices, project);
                }

                // bake
                for (int i = 0; i < NV; ++i)
                {
                    Mesh.SetVertex(Vertices[i], SmoothedPostions[i]);
                }
            }

            return(true);
        }
Example #10
0
        public bool SolveAndUpdateMesh()
        {
            int N = Mesh.MaxVertexID;

            Vector3d[] Result = new Vector3d[N];
            if (!Solve(Result))
            {
                return(false);
            }
            for (int i = 0; i < N; ++i)
            {
                if (Mesh.IsVertex(i))
                {
                    Mesh.SetVertex(i, Result[i]);
                }
            }
            return(true);
        }
        public virtual bool Extrude()
        {
            MeshEditor editor = new MeshEditor(Mesh);


            editor.SeparateTriangles(Triangles, true, out EdgePairs);

            MeshNormals normals      = null;
            bool        bHaveNormals = Mesh.HasVertexNormals;

            if (!bHaveNormals)
            {
                normals = new MeshNormals(Mesh);
                normals.Compute();
            }

            ExtrudeVertices = new MeshVertexSelection(Mesh);
            ExtrudeVertices.SelectTriangleVertices(Triangles);

            Vector3d[] NewVertices = new Vector3d[ExtrudeVertices.Count];
            int        k           = 0;

            foreach (int vid in ExtrudeVertices)
            {
                Vector3d v = Mesh.GetVertex(vid);
                Vector3f n = (bHaveNormals) ? Mesh.GetVertexNormal(vid) : (Vector3f)normals.Normals[vid];
                NewVertices[k++] = ExtrudedPositionF(v, n, vid);
            }
            k = 0;
            foreach (int vid in ExtrudeVertices)
            {
                Mesh.SetVertex(vid, NewVertices[k++]);
            }

            SetGroupID    = Group.GetGroupID(Mesh);
            JoinTriangles = editor.StitchUnorderedEdges(EdgePairs, SetGroupID);

            return(true);
        }
Example #12
0
        void split_missing(MeshMeshCut fromOp, MeshMeshCut toOp,
                           DMesh3 fromMesh, DMesh3 toMesh,
                           HashSet <int> fromVerts, HashSet <int> toVerts)
        {
            List <int> missing = new List <int>();

            foreach (int vid in fromVerts)
            {
                Vector3d v        = fromMesh.GetVertex(vid);
                int      near_vid = find_nearest_vertex(toMesh, v, toVerts);
                if (near_vid == DMesh3.InvalidID)
                {
                    missing.Add(vid);
                }
            }

            foreach (int vid in missing)
            {
                Vector3d v        = fromMesh.GetVertex(vid);
                int      near_eid = find_nearest_edge(toMesh, v, toVerts);
                if (near_eid == DMesh3.InvalidID)
                {
                    System.Console.WriteLine("could not find edge to split?");
                    continue;
                }

                DMesh3.EdgeSplitInfo splitInfo;
                MeshResult           result = toMesh.SplitEdge(near_eid, out splitInfo);
                if (result != MeshResult.Ok)
                {
                    System.Console.WriteLine("edge split failed");
                    continue;
                }

                toMesh.SetVertex(splitInfo.vNew, v);
                toVerts.Add(splitInfo.vNew);
            }
        }
Example #13
0
        public virtual bool Smooth()
        {
            int NV = Loop.Vertices.Length;

            double a          = MathUtil.Clamp(Alpha, 0, 1);
            double num_rounds = 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);
        }
Example #14
0
        public virtual bool Cut()
        {
            double invalidDist = double.MinValue;

            MeshEdgeSelection   CutEdgeSet   = null;
            MeshVertexSelection CutVertexSet = null;

            if (CutFaceSet != null)
            {
                CutEdgeSet   = new MeshEdgeSelection(Mesh, CutFaceSet);
                CutVertexSet = new MeshVertexSelection(Mesh, CutEdgeSet);
            }

            // 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>();

            IEnumerable <int> edgeItr = Interval1i.Range(MaxEID);

            if (CutEdgeSet != null)
            {
                edgeItr = CutEdgeSet;
            }

            // cut existing edges with plane, using edge split
            foreach (int eid in edgeItr)
            {
                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) < MathUtil.Epsilon) ? 1 : 0;
                int n1 = (Math.Abs(f1) < 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;
                }

                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 != DMesh3.InvalidID)
                {
                    NewEdges.Add(splitInfo.eNewDN);
                    OnCutEdges.Add(splitInfo.eNewDN);
                }
            }

            // remove one-rings of all positive-side vertices.
            IEnumerable <int> vertexSet = Interval1i.Range(MaxVID);

            if (CutVertexSet != null)
            {
                vertexSet = CutVertexSet;
            }
            foreach (int vid in vertexSet)
            {
                if (signs[vid] > 0 && Mesh.IsVertex(vid))
                {
                    Mesh.RemoveVertex(vid, 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;
                CutSpans       = loops.Spans;
                CutLoopsFailed = false;
                FoundOpenSpans = CutSpans.Count > 0;
            } catch {
                CutLoops       = new List <EdgeLoop>();
                CutLoopsFailed = true;
            }

            return(true);
        }         // Cut()
Example #15
0
        public virtual bool Extrude()
        {
            MeshNormals normals      = null;
            bool        bHaveNormals = Mesh.HasVertexNormals;

            if (!bHaveNormals)
            {
                normals = new MeshNormals(Mesh);
                normals.Compute();
            }

            InitialLoops     = new MeshBoundaryLoops(Mesh);
            InitialTriangles = Mesh.TriangleIndices().ToArray();
            InitialVertices  = Mesh.VertexIndices().ToArray();

            // duplicate triangles of mesh
            InitialToOffsetMapV = new IndexMap(Mesh.MaxVertexID, Mesh.MaxVertexID);
            OffsetGroupID       = OffsetGroup.GetGroupID(Mesh);
            var editor = new MeshEditor(Mesh);

            OffsetTriangles = editor.DuplicateTriangles(InitialTriangles, ref InitialToOffsetMapV, OffsetGroupID);

            // set vertices to new positions
            foreach (int vid in InitialVertices)
            {
                int newvid = InitialToOffsetMapV[vid];
                if (!Mesh.IsVertex(newvid))
                {
                    continue;
                }

                Vector3d v    = Mesh.GetVertex(vid);
                Vector3f n    = (bHaveNormals) ? Mesh.GetVertexNormal(vid) : (Vector3f)normals.Normals[vid];
                Vector3d newv = ExtrudedPositionF(v, n, vid);

                Mesh.SetVertex(newvid, newv);
            }

            // we need to reverse one side
            if (IsPositiveOffset)
            {
                editor.ReverseTriangles(InitialTriangles);
            }
            else
            {
                editor.ReverseTriangles(OffsetTriangles);
            }

            // stitch each loop
            NewLoops        = new EdgeLoop[InitialLoops.Count];
            StitchTriangles = new int[InitialLoops.Count][];
            StitchGroupIDs  = new int[InitialLoops.Count];
            int li = 0;

            foreach (var loop in InitialLoops)
            {
                int[] loop2 = new int[loop.VertexCount];
                for (int k = 0; k < loop2.Length; ++k)
                {
                    loop2[k] = InitialToOffsetMapV[loop.Vertices[k]];
                }

                StitchGroupIDs[li] = StitchGroups.GetGroupID(Mesh);
                if (IsPositiveOffset)
                {
                    StitchTriangles[li] = editor.StitchLoop(loop2, loop.Vertices, StitchGroupIDs[li]);
                }
                else
                {
                    StitchTriangles[li] = editor.StitchLoop(loop.Vertices, loop2, StitchGroupIDs[li]);
                }
                NewLoops[li] = EdgeLoop.FromVertices(Mesh, loop2);
                li++;
            }

            return(true);
        }
        // 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));
        }
Example #17
0
            internal bool RandomAdjust(g3.DMesh3 mesh, DMeshAABBTree3 tree, Random r, double max, double moveTries, double targetArea)
            {
                bool moved = false;

                if (this.locked)
                {
                    return(false);
                }

                for (int i = 0; i < moveTries; i++)
                {
                    var v0 = mesh.GetVertex(vertex_index.a);
                    var v1 = mesh.GetVertex(vertex_index.b);
                    var v2 = mesh.GetVertex(vertex_index.c);

                    var v0_old = mesh.GetVertex(vertex_index.a);
                    var v1_old = mesh.GetVertex(vertex_index.b);
                    var v2_old = mesh.GetVertex(vertex_index.c);

                    v0.x += (r.NextDouble() * max * 2 - max);
                    v0.y += (r.NextDouble() * max * 2 - max);
                    v0.z += (r.NextDouble() * max * 2 - max);

                    v1.x += (r.NextDouble() * max * 2 - max);
                    v1.y += (r.NextDouble() * max * 2 - max);
                    v1.z += (r.NextDouble() * max * 2 - max);

                    v2.x += (r.NextDouble() * max * 2 - max);
                    v2.y += (r.NextDouble() * max * 2 - max);
                    v2.z += (r.NextDouble() * max * 2 - max);

                    int tNearestID        = tree.FindNearestTriangle(v0);
                    DistPoint3Triangle3 q = MeshQueries.TriangleDistance(tree.Mesh, tNearestID, v0);
                    v0 = q.TriangleClosest;

                    tNearestID = tree.FindNearestTriangle(v1);
                    q          = MeshQueries.TriangleDistance(tree.Mesh, tNearestID, v1);
                    v1         = q.TriangleClosest;

                    tNearestID = tree.FindNearestTriangle(v2);
                    q          = MeshQueries.TriangleDistance(tree.Mesh, tNearestID, v2);
                    v2         = q.TriangleClosest;

                    double oldArea = (HowCloseToTargetArea(mesh, targetArea, 2) / targetArea) * 3;

                    double oldAngleQuality = GetTriangleTotalAnglesQualityHelper(mesh, 2);

                    var n = mesh.GetTriNormal(meshIndex);

                    double oldNormalQuality = GetNormalQuality(mesh, n, 2) * 6;

                    mesh.SetVertex(vertex_index.a, v0);
                    mesh.SetVertex(vertex_index.b, v1);
                    mesh.SetVertex(vertex_index.c, v2);

                    double newArea          = (HowCloseToTargetArea(mesh, targetArea, 2) / targetArea) * 3;
                    double newAngleQuality  = GetTriangleTotalAnglesQualityHelper(mesh, 2);
                    double newNormalQuality = GetNormalQuality(mesh, n, 2) * 6;

                    if ((oldArea + oldAngleQuality + oldNormalQuality) < (newArea + newAngleQuality + newNormalQuality))
                    {
                        mesh.SetVertex(vertex_index.a, v0_old);
                        mesh.SetVertex(vertex_index.b, v1_old);
                        mesh.SetVertex(vertex_index.c, v2_old);
                    }
                    else
                    {
                        moved = true;
                    }
                }

                return(moved);
            }
Example #18
0
        protected virtual ProcessResult CollapseEdge(int edgeID, Vector3d vNewPos, out int collapseToV)
        {
            collapseToV = DMesh3.InvalidID;
            RuntimeDebugCheck(edgeID);

            EdgeConstraint constraint =
                (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID);

            if (constraint.NoModifications)
            {
                return(ProcessResult.Ignored_EdgeIsFullyConstrained);
            }
            if (constraint.CanCollapse == false)
            {
                return(ProcessResult.Ignored_EdgeIsFullyConstrained);
            }


            // look up verts and tris for this edge
            int a = 0, b = 0, t0 = 0, t1 = 0;

            if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false)
            {
                return(ProcessResult.Failed_NotAnEdge);
            }
            bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID);

            // look up 'other' verts c (from t0) and d (from t1, if it exists)
            Index3i T0tv         = mesh.GetTriangle(t0);
            int     c            = IndexUtil.find_tri_other_vtx(a, b, T0tv);
            Index3i T1tv         = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1);
            int     d            = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv);

            Vector3d vA           = mesh.GetVertex(a);
            Vector3d vB           = mesh.GetVertex(b);
            double   edge_len_sqr = (vA - vB).LengthSquared;

            if (edge_len_sqr > MinEdgeLength * MinEdgeLength)
            {
                return(ProcessResult.Ignored_EdgeTooLong);
            }

            begin_collapse();

            // check if we should collapse, and also find which vertex we should collapse to,
            // in cases where we have constraints/etc
            int  collapse_to  = -1;
            bool bCanCollapse = can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to);

            if (bCanCollapse == false)
            {
                return(ProcessResult.Ignored_Constrained);
            }

            // if we have a boundary, we want to collapse to boundary
            if (PreserveBoundary && HaveBoundary)
            {
                if (collapse_to != -1)
                {
                    if ((IsBoundaryV(b) && collapse_to != b) ||
                        (IsBoundaryV(a) && collapse_to != a))
                    {
                        return(ProcessResult.Ignored_Constrained);
                    }
                }
                if (IsBoundaryV(b))
                {
                    collapse_to = b;
                }
                else if (IsBoundaryV(a))
                {
                    collapse_to = a;
                }
            }

            // optimization: if edge cd exists, we cannot collapse or flip. look that up here?
            //  funcs will do it internally...
            //  (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...)
            ProcessResult retVal = ProcessResult.Failed_OpNotSuccessful;

            int iKeep = b, iCollapse = a;

            // if either vtx is fixed, collapse to that position
            if (collapse_to == b)
            {
                vNewPos = vB;
            }
            else if (collapse_to == a)
            {
                iKeep   = a; iCollapse = b;
                vNewPos = vA;
            }
            else
            {
                vNewPos = get_projected_collapse_position(iKeep, vNewPos);
            }

            // check if this collapse will create a normal flip. Also checks
            // for invalid collapse nbrhood, since we are doing one-ring iter anyway.
            // [TODO] could we skip this one-ring check in CollapseEdge? pass in hints?
            if (creates_flip_or_invalid(a, b, ref vNewPos, t0, t1) || creates_flip_or_invalid(b, a, ref vNewPos, t0, t1))
            {
                retVal = ProcessResult.Ignored_CreatesFlip;
                goto skip_to_end;
            }

            // lots of cases where we cannot collapse, but we should just let
            // mesh sort that out, right?
            COUNT_COLLAPSES++;
            DMesh3.EdgeCollapseInfo collapseInfo;
            MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo);

            if (result == MeshResult.Ok)
            {
                collapseToV = iKeep;
                mesh.SetVertex(iKeep, vNewPos);
                if (constraints != null)
                {
                    constraints.ClearEdgeConstraint(edgeID);
                    constraints.ClearEdgeConstraint(collapseInfo.eRemoved0);
                    if (collapseInfo.eRemoved1 != DMesh3.InvalidID)
                    {
                        constraints.ClearEdgeConstraint(collapseInfo.eRemoved1);
                    }
                    constraints.ClearVertexConstraint(iCollapse);
                }
                OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo);
                DoDebugChecks();

                retVal = ProcessResult.Ok_Collapsed;
            }

skip_to_end:
            end_collapse();
            return(retVal);
        }
Example #19
0
        protected virtual ProcessResult ProcessEdge(int edgeID)
        {
            RuntimeDebugCheck(edgeID);

            EdgeConstraint constraint =
                (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID);

            if (constraint.NoModifications)
            {
                return(ProcessResult.Ignored_EdgeIsFullyConstrained);
            }

            // look up verts and tris for this edge
            int a = 0, b = 0, t0 = 0, t1 = 0;

            if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false)
            {
                return(ProcessResult.Failed_NotAnEdge);
            }
            bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID);

            // look up 'other' verts c (from t0) and d (from t1, if it exists)
            Index3i T0tv         = mesh.GetTriangle(t0);
            int     c            = IndexUtil.find_tri_other_vtx(a, b, T0tv);
            Index3i T1tv         = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1);
            int     d            = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv);

            Vector3d vA           = mesh.GetVertex(a);
            Vector3d vB           = mesh.GetVertex(b);
            double   edge_len_sqr = (vA - vB).LengthSquared;

            begin_collapse();

            // check if we should collapse, and also find which vertex we should collapse to,
            // in cases where we have constraints/etc
            int  collapse_to  = -1;
            bool bCanCollapse = EnableCollapses &&
                                constraint.CanCollapse &&
                                edge_len_sqr < MinEdgeLength * MinEdgeLength &&
                                can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to);

            // optimization: if edge cd exists, we cannot collapse or flip. look that up here?
            //  funcs will do it internally...
            //  (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...)

            // if edge length is too short, we want to collapse it
            bool bTriedCollapse = false;

            if (bCanCollapse)
            {
                int      iKeep = b, iCollapse = a;
                Vector3d vNewPos = (vA + vB) * 0.5;

                // if either vtx is fixed, collapse to that position
                if (collapse_to == b)
                {
                    vNewPos = vB;
                }
                else if (collapse_to == a)
                {
                    iKeep   = a; iCollapse = b;
                    vNewPos = vA;
                }
                else
                {
                    vNewPos = get_projected_collapse_position(iKeep, vNewPos);
                }

                // TODO be smart about picking b (keep vtx).
                //    - swap if one is bdry vtx, for example?
                // lots of cases where we cannot collapse, but we should just let
                // mesh sort that out, right?
                COUNT_COLLAPSES++;
                DMesh3.EdgeCollapseInfo collapseInfo;
                MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo);
                if (result == MeshResult.Ok)
                {
                    mesh.SetVertex(iKeep, vNewPos);
                    if (constraints != null)
                    {
                        constraints.ClearEdgeConstraint(edgeID);
                        constraints.ClearEdgeConstraint(collapseInfo.eRemoved0);
                        if (collapseInfo.eRemoved1 != DMesh3.InvalidID)
                        {
                            constraints.ClearEdgeConstraint(collapseInfo.eRemoved1);
                        }
                        constraints.ClearVertexConstraint(iCollapse);
                    }
                    OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo);
                    DoDebugChecks();

                    return(ProcessResult.Ok_Collapsed);
                }
                else
                {
                    bTriedCollapse = true;
                }
            }

            end_collapse();
            begin_flip();

            // if this is not a boundary edge, maybe we want to flip
            bool bTriedFlip = false;

            if (EnableFlips && constraint.CanFlip && bIsBoundaryEdge == false)
            {
                // don't want to flip if it will invert triangle...tetrahedron sign??

                // can we do this more efficiently somehow?
                bool a_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(a));
                bool b_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(b));
                bool c_is_boundary_vtx = (MeshIsClosed) ? false : mesh.vertex_is_boundary(c);
                bool d_is_boundary_vtx = (MeshIsClosed) ? false :  mesh.vertex_is_boundary(d);
                int  valence_a = mesh.GetVtxEdgeCount(a), valence_b = mesh.GetVtxEdgeCount(b);
                int  valence_c = mesh.GetVtxEdgeCount(c), valence_d = mesh.GetVtxEdgeCount(d);
                int  valence_a_target = (a_is_boundary_vtx) ? valence_a : 6;
                int  valence_b_target = (b_is_boundary_vtx) ? valence_b : 6;
                int  valence_c_target = (c_is_boundary_vtx) ? valence_c : 6;
                int  valence_d_target = (d_is_boundary_vtx) ? valence_d : 6;


                // if total valence error improves by flip, we want to do it
                int curr_err = Math.Abs(valence_a - valence_a_target) + Math.Abs(valence_b - valence_b_target)
                               + Math.Abs(valence_c - valence_c_target) + Math.Abs(valence_d - valence_d_target);
                int flip_err = Math.Abs((valence_a - 1) - valence_a_target) + Math.Abs((valence_b - 1) - valence_b_target)
                               + Math.Abs((valence_c + 1) - valence_c_target) + Math.Abs((valence_d + 1) - valence_d_target);

                if (flip_err < curr_err)
                {
                    // try flip
                    DMesh3.EdgeFlipInfo flipInfo;
                    COUNT_FLIPS++;
                    MeshResult result = mesh.FlipEdge(edgeID, out flipInfo);
                    if (result == MeshResult.Ok)
                    {
                        DoDebugChecks();
                        return(ProcessResult.Ok_Flipped);
                    }
                    else
                    {
                        bTriedFlip = true;
                    }
                }
            }

            end_flip();
            begin_split();

            // if edge length is too long, we want to split it
            bool bTriedSplit = false;

            if (EnableSplits && constraint.CanSplit && edge_len_sqr > MaxEdgeLength * MaxEdgeLength)
            {
                DMesh3.EdgeSplitInfo splitInfo;
                COUNT_SPLITS++;
                MeshResult result = mesh.SplitEdge(edgeID, out splitInfo);
                if (result == MeshResult.Ok)
                {
                    update_after_split(edgeID, a, b, splitInfo);
                    OnEdgeSplit(edgeID, a, b, splitInfo);
                    DoDebugChecks();
                    return(ProcessResult.Ok_Split);
                }
                else
                {
                    bTriedSplit = true;
                }
            }

            end_split();


            if (bTriedFlip || bTriedSplit || bTriedCollapse)
            {
                return(ProcessResult.Failed_OpNotSuccessful);
            }
            else
            {
                return(ProcessResult.Ignored_EdgeIsFine);
            }
        }