public virtual ValidationStatus Validate(double fDegenerateTol = MathUtil.ZeroTolerancef) { double dist_sqr_thresh = fDegenerateTol * fDegenerateTol; int nStop = IsLoop ? Curve.VertexCount - 1 : Curve.VertexCount; for (int k = 0; k < nStop; ++k) { Vector2d v0 = Curve[k]; Vector2d v1 = Curve[(k + 1) % Curve.VertexCount]; if (v0.DistanceSquared(v1) < dist_sqr_thresh) { return(ValidationStatus.NearDenegerateInputGeometry); } } foreach (int eid in Mesh.EdgeIndices()) { Index2i ev = Mesh.GetEdgeV(eid); if (PointF(ev.a).DistanceSquared(PointF(ev.b)) < dist_sqr_thresh) { return(ValidationStatus.NearDegenerateMeshEdges); } } return(ValidationStatus.Ok); }
protected virtual void InitializeQueue() { int NE = mesh.EdgeCount; int MaxEID = mesh.MaxEdgeID; EdgeQuadrics = new QEdge[MaxEID]; EdgeQueue = new IndexPriorityQueue(MaxEID); float[] edgeErrors = new float[MaxEID]; // vertex quadrics can be computed in parallel gParallel.ForEach(mesh.EdgeIndices(), (eid) => { Index2i ev = mesh.GetEdgeV(eid); QuadricError Q = new QuadricError(ref vertQuadrics[ev.a], ref vertQuadrics[ev.b]); Vector3d opt = OptimalPoint(eid, ref Q, ev.a, ev.b); edgeErrors[eid] = (float)Q.Evaluate(opt); EdgeQuadrics[eid] = new QEdge(eid, Q, opt); }); // sorted pq insert is faster, so sort edge errors array and index map int[] indices = new int[MaxEID]; for (int i = 0; i < MaxEID; ++i) { indices[i] = i; } Array.Sort(edgeErrors, indices); // now do inserts for (int i = 0; i < edgeErrors.Length; ++i) { int eid = indices[i]; if (mesh.IsEdge(eid)) { QEdge edge = EdgeQuadrics[eid]; EdgeQueue.Enqueue(edge.eid, edgeErrors[i]); } } /* * // previous code that does unsorted insert. This is marginally slower, but * // might get even slower on larger meshes? have only tried up to about 350k. * // (still, this function is not the bottleneck...) * int cur_eid = start_edges(); * bool done = false; * do { * if (mesh.IsEdge(cur_eid)) { * QEdge edge = EdgeQuadrics[cur_eid]; * double err = errList[cur_eid]; * EdgeQueue.Enqueue(cur_eid, (float)err); * } * cur_eid = next_edge(cur_eid, out done); * } while (done == false); */ }
/// <summary> /// we can vastly speed things up if we precompute some invariants. /// You need to re-run this if you are changing the mesh externally /// between remesh passes, otherwise you will get weird results. /// But you will probably still come out ahead, computation-time-wise /// </summary> public virtual void Precompute() { // if we know mesh is closed, we can skip is-boundary checks, which makes // the flip-valence tests much faster! MeshIsClosed = true; foreach (int eid in mesh.EdgeIndices()) { if (mesh.IsBoundaryEdge(eid)) { MeshIsClosed = false; break; } } }
// convert vertex selection to edge selection. Require at least minCount verts of edge to be selected public MeshEdgeSelection(DMesh3 mesh, MeshVertexSelection convertV, int minCount = 2) : this(mesh) { minCount = MathUtil.Clamp(minCount, 1, 2); // [TODO] if minCount == 1, and convertV is small, it is faster to iterate over convertV!! foreach (int eid in mesh.EdgeIndices()) { Index2i ev = mesh.GetEdgeV(eid); int n = (convertV.IsSelected(ev.a) ? 1 : 0) + (convertV.IsSelected(ev.b) ? 1 : 0); if (n >= minCount) { add(eid); } } }
// convert face selection to edge selection. Require at least minCount tris of edge to be selected public MeshEdgeSelection(DMesh3 mesh, MeshFaceSelection convertT, int minCount = 1) : this(mesh) { minCount = MathUtil.Clamp(minCount, 1, 2); if (minCount == 1) { foreach (int tid in convertT) { Index3i te = mesh.GetTriEdges(tid); add(te.a); add(te.b); add(te.c); } } else { foreach (int eid in mesh.EdgeIndices()) { Index2i et = mesh.GetEdgeT(eid); if (convertT.IsSelected(et.a) && convertT.IsSelected(et.b)) { add(eid); } } } }
// iterators allow us to work with gaps in index space public IEnumerable <int> VertexIndices() { return(Mesh.EdgeIndices()); }
public static DMesh3 RemeshMesh(g3.DMesh3 mesh, float minEdgeLength, float maxEdgeLength, float contraintAngle, float smoothSpeed, int smoothPasses, List <Line3d> constrainedLines) { // construct mesh projection target DMesh3 meshCopy = new DMesh3(mesh); DMeshAABBTree3 tree = new DMeshAABBTree3(meshCopy); tree.Build(); MeshProjectionTarget target = new MeshProjectionTarget() { Mesh = meshCopy, Spatial = tree }; MeshConstraints cons = new MeshConstraints(); EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); Index2i ev = mesh.GetEdgeV(eid); if (fAngle > contraintAngle) { cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags)); // TODO Ids based off of ?? What? int nSetID0 = (mesh.GetVertex(ev[0]).y > 1) ? 1 : 2; int nSetID1 = (mesh.GetVertex(ev[1]).y > 1) ? 1 : 2; cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true, nSetID0)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true, nSetID1)); } Vector3d p1 = mesh.GetVertex(ev.a); Vector3d p2 = mesh.GetVertex(ev.b); foreach (var v in constrainedLines) { if (p1.CompareTo(v.Origin) == 0) { Vector3d p = v.PointAt(1.0); if (p2.CompareTo(p) == 0) { cons.SetOrUpdateEdgeConstraint(eid, EdgeConstraint.FullyConstrained); break; } } } foreach (var v in constrainedLines) { if (p2.CompareTo(v.Origin) == 0) { Vector3d p = v.PointAt(1.0); if (p1.CompareTo(p) == 0) { cons.SetOrUpdateEdgeConstraint(eid, EdgeConstraint.FullyConstrained); break; } } } } Remesher r = new Remesher(mesh); r.SetExternalConstraints(cons); r.SetProjectionTarget(target); r.Precompute(); r.EnableFlips = r.EnableSplits = r.EnableCollapses = true; r.MinEdgeLength = minEdgeLength; //0.1f; r.MaxEdgeLength = maxEdgeLength; // 0.2f; r.EnableSmoothing = true; r.SmoothSpeedT = smoothSpeed; // .5; for (int k = 0; k < smoothPasses; ++k) // smoothPasses = 20 { r.BasicRemeshPass(); } return(mesh); }
// public static DMesh3 RemeshMeshNew(g3.DMesh3 mesh, float minEdgeLength, float maxEdgeLength, float contraintAngle, float smoothSpeed, int smoothPasses, g3.DMesh3 projectMeshInput = null, float projectAmount = 1.0f, float projectedDistance = float.MaxValue) { g3.DMesh3 projectMesh = projectMeshInput; if (projectMesh == null) { projectMesh = mesh; } DMesh3 projectMeshCopy = new DMesh3(projectMesh); DMeshAABBTree3 treeProject = new DMeshAABBTree3(projectMeshCopy); treeProject.Build(); GopherMeshProjectionTarget targetProject = new GopherMeshProjectionTarget() { Mesh = projectMeshCopy, Spatial = treeProject, amount = projectAmount, maxDistance = projectedDistance }; MeshConstraints cons = new MeshConstraints(); EdgeRefineFlags useFlags = EdgeRefineFlags.NoFlip; foreach (int eid in mesh.EdgeIndices()) { double fAngle = MeshUtil.OpeningAngleD(mesh, eid); if (fAngle > contraintAngle) { cons.SetOrUpdateEdgeConstraint(eid, new EdgeConstraint(useFlags)); Index2i ev = mesh.GetEdgeV(eid); //int nSetID0 = (mesh.GetVertex(ev[0]).y > 1) ? 1 : 2; //int nSetID1 = (mesh.GetVertex(ev[1]).y > 1) ? 1 : 2; cons.SetOrUpdateVertexConstraint(ev[0], new VertexConstraint(true)); cons.SetOrUpdateVertexConstraint(ev[1], new VertexConstraint(true)); } } // TODO Constrain Vertices too far away foreach (int vid in mesh.VertexIndices()) { var v = mesh.GetVertex(vid); //v.Distance() //targetProject.Project() } Remesher rProjected = new Remesher(mesh); rProjected.SetExternalConstraints(cons); rProjected.SetProjectionTarget(targetProject); rProjected.Precompute(); rProjected.EnableFlips = rProjected.EnableSplits = rProjected.EnableCollapses = true; rProjected.MinEdgeLength = minEdgeLength; //0.1f; rProjected.MaxEdgeLength = maxEdgeLength; // 0.2f; rProjected.EnableSmoothing = true; rProjected.SmoothSpeedT = smoothSpeed; // .5; if (projectMeshInput != null) { float bestSmoothPassProjectAmount = projectAmount / smoothPasses; float testbestSmoothPassProjectAmount = float.MaxValue; for (float smoothPassProjectAmount = -.1f; smoothPassProjectAmount < 1.1f; smoothPassProjectAmount += 0.005f) { double test = 0; for (int i = 0; i < smoothPasses; i++) { test = 1.0 * smoothPassProjectAmount + test * (1 - smoothPassProjectAmount); } if (Math.Abs(test - projectAmount) < Math.Abs(testbestSmoothPassProjectAmount - projectAmount)) { bestSmoothPassProjectAmount = (float)smoothPassProjectAmount; testbestSmoothPassProjectAmount = (float)test; } } targetProject.amount = bestSmoothPassProjectAmount; targetProject.maxDistance = projectedDistance; for (int k = 0; k < smoothPasses; ++k) // smoothPasses = 20 { rProjected.BasicRemeshPass(); } } else { for (int k = 0; k < smoothPasses; ++k) // smoothPasses = 20 { rProjected.BasicRemeshPass(); } } return(mesh); }