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