public static RegionRemesher QuickRemesh(DMesh3 mesh, int[] tris,
                                                 double targetEdgeLen, double smoothSpeed,
                                                 int rounds,
                                                 IProjectionTarget target,
                                                 QuickRemeshFlags flags = QuickRemeshFlags.PreventNormalFlips)
        {
            var remesh = new RegionRemesher(mesh, tris);

            if (target != null)
            {
                remesh.SetProjectionTarget(target);
            }

            remesh.SetTargetEdgeLength(targetEdgeLen);
            remesh.SmoothSpeedT = smoothSpeed;
            if ((flags & QuickRemeshFlags.PreventNormalFlips) != 0)
            {
                remesh.PreventNormalFlips = true;
            }

            for (int k = 0; k < rounds; ++k)
            {
                remesh.BasicRemeshPass();
            }
            remesh.BackPropropagate();
            return(remesh);
        }
        protected void project_vertex(int vID, IProjectionTarget targetIn)
        {
            Vector3d curpos    = mesh.GetVertex(vID);
            Vector3d projected = targetIn.Project(curpos, vID);

            mesh.SetVertex(vID, projected);
        }
Exemple #3
0
 public static void ConstrainVtxSpanTo(Remesher r, int[] spanV, IProjectionTarget target, int setID = -1)
 {
     if (r.Constraints == null)
     {
         r.SetExternalConstraints(new MeshConstraints());
     }
     ConstrainVtxSpanTo(r.Constraints, r.Mesh, spanV, target);
 }
Exemple #4
0
 public static void ConstrainVtxLoopTo(Remesher r, int[] loopV, IProjectionTarget target)
 {
     if (r.Constraints == null)
     {
         r.SetExternalConstraints(new MeshConstraints());
     }
     ConstrainVtxLoopTo(r.Constraints, r.Mesh, loopV, target);
 }
        public static RegionRemesher QuickRemesh(DMesh3 mesh, int[] tris,
                                                 double targetEdgeLen, double smoothSpeed,
                                                 int rounds,
                                                 IProjectionTarget target)
        {
            RegionRemesher remesh = new RegionRemesher(mesh, tris);

            if (target != null)
            {
                remesh.SetProjectionTarget(target);
            }
            remesh.SetTargetEdgeLength(targetEdgeLen);
            remesh.SmoothSpeedT = smoothSpeed;
            for (int k = 0; k < rounds; ++k)
            {
                remesh.BasicRemeshPass();
            }
            remesh.BackPropropagate();
            return(remesh);
        }
Exemple #6
0
        public static RegionRemesher QuickRemesh(NGonsCore.geometry3Sharp.mesh.DMesh3 mesh, int[] tris,
                                                 double minEdgeLen, double maxEdgeLen, double smoothSpeed,
                                                 int rounds,
                                                 IProjectionTarget target)
        {
            RegionRemesher remesh = new RegionRemesher(mesh, tris);

            if (target != null)
            {
                remesh.SetProjectionTarget(target);
            }
            remesh.MinEdgeLength = minEdgeLen;
            remesh.MaxEdgeLength = maxEdgeLen;
            remesh.SmoothSpeedT  = smoothSpeed;
            for (int k = 0; k < rounds; ++k)
            {
                remesh.BasicRemeshPass();
            }
            remesh.BackPropropagate();
            return(remesh);
        }
Exemple #7
0
        // for all vertices in loopV, constrain to target
        // for all edges in loopV, disable flips and constrain to target
        public static void ConstrainVtxSpanTo(MeshConstraints cons, DMesh3 mesh, IList <int> spanV, IProjectionTarget target, int setID = -1)
        {
            VertexConstraint vc = new VertexConstraint(target);
            int N = spanV.Count;

            for (int i = 0; i < N; ++i)
            {
                cons.SetOrUpdateVertexConstraint(spanV[i], vc);
            }

            EdgeConstraint ec = new EdgeConstraint(EdgeRefineFlags.NoFlip, target);

            ec.TrackingSetID = setID;
            for (int i = 0; i < N - 1; ++i)
            {
                int v0 = spanV[i];
                int v1 = spanV[i + 1];

                int eid = mesh.FindEdge(v0, v1);
                Debug.Assert(eid != DMesh3.InvalidID);
                if (eid != DMesh3.InvalidID)
                {
                    cons.SetOrUpdateEdgeConstraint(eid, ec);
                }
            }
        }
Exemple #8
0
 public static Action <List <Vector3d> > MakeLoopOnSurfaceProcessorF(FScene scene, IProjectionTarget surface,
                                                                     Func <float> SampleRateF, Func <bool> ClosedF,
                                                                     float fSmoothAlpha = 0.2f, int nSmoothIter = 15)
 {
     return((vertices) => {
         CurveResampler resampler = new CurveResampler();
         IWrappedCurve3d temp_curve = new IWrappedCurve3d()
         {
             VertexList = vertices, Closed = ClosedF()
         };
         float rate = SampleRateF();
         List <Vector3d> result = resampler.SplitCollapseResample(temp_curve, rate, rate * 0.6);
         if (result != null && result.Count > 3)
         {
             vertices.Clear();
             vertices.AddRange(result);
         }
         CurveUtils.InPlaceSmooth(vertices, fSmoothAlpha, nSmoothIter, ClosedF());
         gs.CurveDrawingUtil.ProjectToTarget_Scene(vertices, scene, surface);
     });
 }
Exemple #9
0
        public static void ProjectToTarget_Scene(IList <Vector3d> vertices, FScene scene, IProjectionTarget target)
        {
            int N = vertices.Count;

            for (int i = 0; i < N; ++i)
            {
                Vector3f v  = (Vector3f)vertices[i];
                Vector3f vW = SceneTransforms.SceneToWorldP(scene, v);
                vW          = (Vector3f)target.Project(vW);
                vertices[i] = SceneTransforms.WorldToSceneP(scene, vW);
            }
        }
Exemple #10
0
 public VertexConstraint(IProjectionTarget target)
 {
     Fixed      = false;
     FixedSetID = InvalidSetID;
     Target     = target;
 }
Exemple #11
0
        public IProjectionTarget                Target;     // vertex is constrained to lie on this projection Target.
                                                            // Fixed and Target are mutually exclusive

        public VertexConstraint(bool isFixed, int setID = InvalidSetID)
        {
            Fixed      = isFixed;
            FixedSetID = setID;
            Target     = null;
        }
Exemple #12
0
 public EdgeConstraint(EdgeRefineFlags rflags, IProjectionTarget target)
 {
     refineFlags = rflags;
     Target      = target;
 }
Exemple #13
0
        public IProjectionTarget              Target; // edge is associated with this projection Target.
                                                      // Currently only used as information, we do not explicitly
                                                      // project edges onto targets (must also set VertexConstraint)


        public EdgeConstraint(EdgeRefineFlags rflags)
        {
            refineFlags = rflags;
            Target      = null;
        }
        protected void merge_loops(DMesh3 mesh, EdgeLoop cutLoop, EdgeLoop connectorLoop, bool is_outer)
        {
            /*
             * To join the loops, we are going to first make a circle, then snap both
             * open loops to that circle. Then we sample a set of vertices on the circle
             * and remesh the loops while also snapping them to the circle vertices.
             * The result is two perfectly-matching edge loops.
             */

            //AxisAlignedBox3d cutLoopBounds =
            //    BoundsUtil.Bounds(cutLoop.Vertices, (vid) => { return mesh.GetVertex(vid); });

            AxisAlignedBox3d cutLoopBounds       = cutLoop.GetBounds();
            AxisAlignedBox3d connectorLoopBounds = connectorLoop.GetBounds();
            Vector3d         midPt = (cutLoopBounds.Center + connectorLoopBounds.Center) * 0.5;
            double           midY  = midPt.y;

            // this mess construcst the circle and the sampled version
            Frame3f           circFrame       = new Frame3f(midPt);
            Circle3d          circ            = new Circle3d(circFrame, connectorLoopBounds.Width * 0.5, 1);
            DistPoint3Circle3 dist            = new DistPoint3Circle3(Vector3d.Zero, circ);
            DCurve3           sampled         = new DCurve3();
            double            target_edge_len = TargetMeshEdgeLength;
            int N = (int)(circ.ArcLength / target_edge_len);

            for (int k = 0; k < N; ++k)
            {
                sampled.AppendVertex(circ.SampleT((double)k / (double)N));
            }

            MergeProjectionTarget circleTarget = new MergeProjectionTarget()
            {
                Mesh = mesh, CircleDist = dist, CircleLoop = sampled
            };

            EdgeLoop[] loops = new EdgeLoop[2] {
                cutLoop, connectorLoop
            };
            EdgeLoop[] outputLoops = new EdgeLoop[2];   // loops after this remeshing/etc (but might be missing some verts/edges!)
            for (int li = 0; li < 2; ++li)
            {
                EdgeLoop loop = loops[li];

                // snap the loop verts onto the analytic circle
                foreach (int vid in loop.Vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    dist.Point = new Vector3d(v.x, midY, v.z);
                    mesh.SetVertex(vid, dist.Compute().CircleClosest);
                }

                if (DebugStep <= 5)
                {
                    continue;
                }

                // remesh around the edge loop while we snap it to the sampled circle verts
                EdgeLoopRemesher loopRemesh = new EdgeLoopRemesher(mesh, loop)
                {
                    LocalSmoothingRings = 3
                };
                loopRemesh.EnableParallelProjection = false;
                loopRemesh.SetProjectionTarget(circleTarget);
                loopRemesh.SetTargetEdgeLength(TargetMeshEdgeLength);
                loopRemesh.SmoothSpeedT = 0.5f;
                for (int k = 0; k < 5; ++k)
                {
                    loopRemesh.BasicRemeshPass();
                }
                loopRemesh.SmoothSpeedT = 0;
                for (int k = 0; k < 2; ++k)
                {
                    loopRemesh.BasicRemeshPass();
                }
                EdgeLoop newLoop = loopRemesh.OutputLoop;
                outputLoops[li] = newLoop;

                if (DebugStep <= 6)
                {
                    continue;
                }

                // hard snap the loop vertices to the sampled circle verts
                foreach (int vid in newLoop.Vertices)
                {
                    Vector3d v = mesh.GetVertex(vid);
                    v = circleTarget.Project(v, vid);
                    mesh.SetVertex(vid, v);
                }

                // [TODO] we could re-order newLoop verts/edges to match the sampled verts order,
                // then the pair of loops would be consistently ordered (currently no guarantee)

                if (DebugStep <= 7)
                {
                    continue;
                }

                // collapse any degenerate edges on loop (just in case)
                // DANGER: if this actually happens, then outputLoops[li] has some invalid verts/edges!
                foreach (int eid in newLoop.Edges)
                {
                    if (mesh.IsEdge(eid))
                    {
                        Index2i  ev = mesh.GetEdgeV(eid);
                        Vector3d a = mesh.GetVertex(ev.a), b = mesh.GetVertex(ev.b);
                        if (a.Distance(b) < TargetMeshEdgeLength * 0.001)
                        {
                            DMesh3.EdgeCollapseInfo collapse;
                            mesh.CollapseEdge(ev.a, ev.b, out collapse);
                        }
                    }
                }
            }

            if (DebugStep <= 7)
            {
                return;
            }


            /*
             * Ok now we want to merge the loops and make them nice
             */

            // would be more efficient to find loops and stitch them...
            MergeCoincidentEdges merge = new MergeCoincidentEdges(mesh);

            merge.Apply();

            // fill any fail-holes??

            // remesh merge region
            MeshVertexSelection remesh_roi_v = new MeshVertexSelection(mesh);

            remesh_roi_v.Select(outputLoops[0].Vertices);
            remesh_roi_v.Select(outputLoops[1].Vertices);
            remesh_roi_v.ExpandToOneRingNeighbours(5);
            MeshFaceSelection remesh_roi = new MeshFaceSelection(mesh, remesh_roi_v, 1);

            remesh_roi.LocalOptimize(true, true);

            IProjectionTarget projTarget = null;

            if (is_outer)
            {
                projTarget = new NoPenetrationProjectionTarget()
                {
                    Spatial = this.OuterOffsetMeshSpatial
                };
            }
            else
            {
                projTarget = new NoPenetrationProjectionTarget()
                {
                    Spatial = this.InnerOffsetMeshSpatial
                };
            }

            RegionRemesher join_remesh =
                RegionRemesher.QuickRemesh(mesh, remesh_roi.ToArray(), TargetMeshEdgeLength, 0.5, 5, projTarget);

            if (DebugStep <= 8)
            {
                return;
            }


            if (false && is_outer)
            {
                Func <int, bool> filterF = (tid) => {
                    return(mesh.GetTriCentroid(tid).y > connectorLoopBounds.Max.y);
                };

                MeshFaceSelection tris = new MeshFaceSelection(mesh);
                foreach (int tid in join_remesh.CurrentBaseTriangles)
                {
                    if (filterF(tid))
                    {
                        tris.Select(tid);
                    }
                }
                tris.ExpandToOneRingNeighbours(5, filterF);

                MeshVertexSelection verts  = new MeshVertexSelection(mesh, tris);
                MeshIterativeSmooth smooth = new MeshIterativeSmooth(mesh, verts.ToArray(), true);
                smooth.Alpha    = 1.0f;
                smooth.Rounds   = 25;
                smooth.ProjectF = (v, n, vid) => { return(projTarget.Project(v)); };
                smooth.Smooth();
            }


            // [RMS] this smooths too far. we basically only want to smooth 'up' from top of socket...
            //LaplacianMeshSmoother.RegionSmooth(mesh, join_remesh.CurrentBaseTriangles, 1, 10);

            // need to post-enforce thickness, which we aren't doing above - we could though
        }
 public void SetProjectionTarget(IProjectionTarget target)
 {
     this.target = target;
 }
        // After we split an edge, we have created a new edge and a new vertex.
        // The edge needs to inherit the constraint on the other pre-existing edge that we kept.
        // In addition, if the edge vertices were both constrained, then we /might/
        // want to also constrain this new vertex, possibly project to constraint target.
        protected virtual void update_after_split(int edgeID, int va, int vb, ref DMesh3.EdgeSplitInfo splitInfo)
        {
            bool bPositionFixed = false;

            if (constraints != null && constraints.HasEdgeConstraint(edgeID))
            {
                // inherit edge constraint
                constraints.SetOrUpdateEdgeConstraint(splitInfo.eNewBN, constraints.GetEdgeConstraint(edgeID));

                // [RMS] update vertex constraints. Note that there is some ambiguity here.
                //   Both verts being constrained doesn't inherently mean that the edge is on
                //   a constraint, that's why these checks are only applied if edge is constrained.
                //   But constrained edge doesn't necessarily mean we want to inherit vert constraints!!
                //
                //   although, pretty safe to assume that we would at least disable flips
                //   if both vertices are constrained to same line/curve. So, maybe this makes sense...
                //
                //   (perhaps edge constraint should be explicitly tagged to resolve this ambiguity??)

                // vert inherits Fixed if both orig edge verts Fixed, and both tagged with same SetID
                VertexConstraint ca = constraints.GetVertexConstraint(va);
                VertexConstraint cb = constraints.GetVertexConstraint(vb);
                if (ca.Fixed && cb.Fixed)
                {
                    int nSetID = (ca.FixedSetID > 0 && ca.FixedSetID == cb.FixedSetID) ?
                                 ca.FixedSetID : VertexConstraint.InvalidSetID;
                    constraints.SetOrUpdateVertexConstraint(splitInfo.vNew,
                                                            new VertexConstraint(true, nSetID));
                    bPositionFixed = true;
                }

                // vert inherits Target if:
                //  1) both source verts and edge have same Target, and is same as edge target
                //  2) either vert has same target as edge, and other vert is fixed
                if (ca.Target != null || cb.Target != null)
                {
                    IProjectionTarget edge_target = constraints.GetEdgeConstraint(edgeID).Target;
                    IProjectionTarget set_target  = null;
                    if (ca.Target == cb.Target && ca.Target == edge_target)
                    {
                        set_target = edge_target;
                    }
                    else if (ca.Target == edge_target && cb.Fixed)
                    {
                        set_target = edge_target;
                    }
                    else if (cb.Target == edge_target && ca.Fixed)
                    {
                        set_target = edge_target;
                    }
                    if (set_target != null)
                    {
                        constraints.SetOrUpdateVertexConstraint(splitInfo.vNew,
                                                                new VertexConstraint(set_target));
                        project_vertex(splitInfo.vNew, set_target);
                        bPositionFixed = true;
                    }
                }
            }

            if (EnableInlineProjection && bPositionFixed == false && target != null)
            {
                project_vertex(splitInfo.vNew, target);
            }
        }
Exemple #17
0
 public EdgeConstraint(EdgeRefineFlags rflags, IProjectionTarget target)
 {
     refineFlags   = rflags;
     Target        = target;
     TrackingSetID = -1;
 }
Exemple #18
0
        public int                            TrackingSetID; // not actually a constraint, but allows is to find descendents
                                                             // of an constraind input edge


        public EdgeConstraint(EdgeRefineFlags rflags)
        {
            refineFlags   = rflags;
            Target        = null;
            TrackingSetID = -1;
        }
        public static void test_remesh_constraints_vertcurves()
        {
            int    Slices = 16;
            DMesh3 mesh   = TestUtil.MakeCappedCylinder(false, Slices);

            MeshUtil.ScaleMesh(mesh, Frame3f.Identity, new Vector3f(1, 2, 1));
            //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(0.25);
            //DMesh3 mesh = TestUtil.MakeRemeshedCappedCylinder(1.0);
            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget mesh_target = new MeshProjectionTarget()
            {
                Mesh = meshCopy, Spatial = tree
            };

            // cylinder projection target
            CylinderProjectionTarget cyl_target = new CylinderProjectionTarget()
            {
                Cylinder = new Cylinder3d(new Vector3d(0, 1, 0), Vector3d.AxisY, 1, 2)
            };

            //IProjectionTarget target = mesh_target;
            IProjectionTarget target = cyl_target;

            // construct projection target circles
            CircleProjectionTarget bottomCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            bottomCons.Circle.Center.y = bounds.Min.y;
            CircleProjectionTarget topCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            topCons.Circle.Center.y = bounds.Max.y;


            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_before.obj");
            }

            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip;

            bool bConstrainVertices = true;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0f)
                {
                    Index2i  ev  = mesh.GetEdgeV(eid);
                    Vector3d ev0 = mesh.GetVertex(ev[0]);
                    Vector3d ev1 = mesh.GetVertex(ev[1]);
                    CircleProjectionTarget loopTarget = null;
                    if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y)
                    {
                        loopTarget = topCons;
                    }
                    else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y)
                    {
                        loopTarget = bottomCons;
                    }

                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget));
                    if (bConstrainVertices && loopTarget != null)
                    {
                        cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget));
                        cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget));
                    }
                }
            }


            Remesher r = new Remesher(mesh);

            //r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);
            r.Precompute();
            r.ENABLE_PROFILING = true;

            var stopwatch = Stopwatch.StartNew();

            //double fResScale = 1.0f;
            double fResScale = 0.5f;

            r.EnableFlips     = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength   = 0.1f * fResScale;
            r.MaxEdgeLength   = 0.2f * fResScale;
            r.EnableSmoothing = true;
            r.SmoothSpeedT    = 1.0f;

            try {
                for (int k = 0; k < 20; ++k)
                {
                    r.BasicRemeshPass();
                    mesh.CheckValidity();
                }
            } catch {
                // continue;
            }
            stopwatch.Stop();
            System.Console.WriteLine("Second Pass Timing: " + stopwatch.Elapsed);

            if (WriteDebugMeshes)
            {
                TestUtil.WriteDebugMesh(mesh, "remesh_analytic_constraints_test_after.obj");
            }
        }
        // for all vertices in loopV, constrain to target
        // for all edges in loopV, disable flips and constrain to target
        public static void ConstrainVtxLoopTo(MeshConstraints cons, DMesh3 mesh, int[] loopV, IProjectionTarget target, int setID = -1)
        {
            VertexConstraint vc = new VertexConstraint(target);

            for (int i = 0; i < loopV.Length; ++i)
            {
                cons.SetOrUpdateVertexConstraint(loopV[i], vc);
            }

            EdgeConstraint ec = new EdgeConstraint(EdgeRefineFlags.NoFlip, target);

            ec.TrackingSetID = setID;
            for (int i = 0; i < loopV.Length; ++i)
            {
                int v0 = loopV[i];
                int v1 = loopV[(i + 1) % loopV.Length];

                int eid = mesh.FindEdge(v0, v1);
                Debug.Assert(eid != DMesh3.InvalidID);
                if (eid != DMesh3.InvalidID)
                {
                    cons.SetOrUpdateEdgeConstraint(eid, ec);
                }
            }
        }
Exemple #21
0
        public DMesh3 remesh_constraints_vertcurves(int iterations, DMesh3 mesh, double min, double max, double angle)
        {
            mesh.CheckValidity();
            AxisAlignedBox3d bounds = mesh.CachedBounds;

            // construct mesh projection target
            DMesh3 meshCopy = new DMesh3(mesh);

            meshCopy.CheckValidity();
            DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy);

            tree.Build();
            MeshProjectionTarget mesh_target = new MeshProjectionTarget()
            {
                Mesh    = meshCopy,
                Spatial = tree
            };

            // cylinder projection target
            CylinderProjectionTarget cyl_target = new CylinderProjectionTarget()
            {
                Cylinder = new Cylinder3d(new Vector3D(0, 1, 0), Vector3D.AxisY, 1, 2)
            };

            //IProjectionTarget target = mesh_target;
            IProjectionTarget target = cyl_target;

            // construct projection target circles
            CircleProjectionTarget bottomCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            bottomCons.Circle.Center.y = bounds.Min.y;
            CircleProjectionTarget topCons = new CircleProjectionTarget()
            {
                Circle = new Circle3d(bounds.Center, 1.0)
            };

            topCons.Circle.Center.y = bounds.Max.y;


            // construct constraint set
            MeshConstraints cons = new MeshConstraints();

            //EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip | EdgeRefineFlags.NoCollapse;
            EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip;

            bool bConstrainVertices = true;

            foreach (int eid in mesh.EdgeIndices())
            {
                double fAngle = MeshUtil.OpeningAngleD(mesh, eid);
                if (fAngle > 30.0f)
                {
                    Index2i  ev  = mesh.GetEdgeV(eid);
                    Vector3D ev0 = mesh.GetVertex(ev[0]);
                    Vector3D ev1 = mesh.GetVertex(ev[1]);
                    CircleProjectionTarget loopTarget = null;
                    if (ev0.y > bounds.Center.y && ev1.y > bounds.Center.y)
                    {
                        loopTarget = topCons;
                    }
                    else if (ev0.y < bounds.Center.y && ev1.y < bounds.Center.y)
                    {
                        loopTarget = bottomCons;
                    }

                    cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags, loopTarget));
                    if (bConstrainVertices && loopTarget != null)
                    {
                        cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(loopTarget));
                        cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(loopTarget));
                    }
                }
            }


            Remesher r = new Remesher(mesh);

            //r.SetExternalConstraints(cons);
            r.SetProjectionTarget(target);
            r.Precompute();
            r.ENABLE_PROFILING = true;
            r.EnableFlips      = r.EnableSplits = r.EnableCollapses = true;
            r.MinEdgeLength    = min;
            r.MaxEdgeLength    = max;
            r.EnableSmoothing  = true;
            r.SmoothSpeedT     = 1.0f;

            for (int k = 0; k < iterations; ++k)
            {
                r.BasicRemeshPass();
                mesh.CheckValidity();
            }

            return(mesh);
        }