public void InitializeFromApply(DMesh3 mesh, IEnumerable <int> triangles) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; foreach (int tid in triangles) { if (!mesh.IsTriangle(tid)) { continue; } Index3i tv = mesh.GetTriangle(tid); bool va = save_vertex(mesh, tv.a); bool vb = save_vertex(mesh, tv.b); bool vc = save_vertex(mesh, tv.c); var tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); RemovedT.Add(tid); Triangles.Add(tri); MeshResult result = mesh.RemoveTriangle(tid, true, false); if (result != MeshResult.Ok) { throw new Exception("RemoveTrianglesMeshChange.Initialize: exception in RemoveTriangle(" + tid.ToString() + "): " + result.ToString()); } Util.gDevAssert(mesh.IsVertex(tv.a) == va && mesh.IsVertex(tv.b) == vb && mesh.IsVertex(tv.c) == vc); } }
public void Select(int vID) { Debug.Assert(Mesh.IsVertex(vID)); if (Mesh.IsVertex(vID)) { add(vID); } }
/// <summary> /// Exhaustively check that verts and edges of this EdgeSpan are consistent. Not for production use. /// </summary> public bool CheckValidity(FailMode eFailMode = FailMode.Throw) { bool is_ok = true; Action <bool> CheckOrFailF = (b) => { is_ok = is_ok && b; }; if (eFailMode == FailMode.DebugAssert) { CheckOrFailF = (b) => { Debug.Assert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.gDevAssert) { CheckOrFailF = (b) => { Util.gDevAssert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.Throw) { CheckOrFailF = (b) => { if (b == false) { throw new Exception("EdgeSpan.CheckValidity: check failed"); } }; } CheckOrFailF(Vertices.Length == Edges.Length + 1); for (int ei = 0; ei < Edges.Length; ++ei) { Index2i ev = Mesh.GetEdgeV(Edges[ei]); CheckOrFailF(Mesh.IsVertex(ev.a)); CheckOrFailF(Mesh.IsVertex(ev.b)); CheckOrFailF(Mesh.FindEdge(ev.a, ev.b) != DMesh3.InvalidID); CheckOrFailF(Vertices[ei] == ev.a || Vertices[ei] == ev.b); CheckOrFailF(Vertices[ei + 1] == ev.a || Vertices[ei + 1] == ev.b); } for (int vi = 0; vi < Vertices.Length - 1; ++vi) { int a = Vertices[vi], b = Vertices[vi + 1]; CheckOrFailF(Mesh.IsVertex(a)); CheckOrFailF(Mesh.IsVertex(b)); CheckOrFailF(Mesh.FindEdge(a, b) != DMesh3.InvalidID); if (vi < Vertices.Length - 2) { int n = 0, edge_before_b = Edges[vi], edge_after_b = Edges[vi + 1]; foreach (int nbr_e in Mesh.VtxEdgesItr(b)) { if (nbr_e == edge_before_b || nbr_e == edge_after_b) { n++; } } CheckOrFailF(n == 2); } } return(true); }
public void RemoveContained() { DMeshAABBTree3 spatial = new DMeshAABBTree3(CutMesh, true); spatial.WindingNumber(Vector3d.Zero); SafeListBuilder <int> removeT = new SafeListBuilder <int>(); gParallel.ForEach(Target.TriangleIndices(), (tid) => { Vector3d v = Target.GetTriCentroid(tid); if (spatial.WindingNumber(v) > 0.9) { removeT.SafeAdd(tid); } }); MeshEditor.RemoveTriangles(Target, removeT.Result); // [RMS] construct set of on-cut vertices? This is not // necessarily all boundary vertices... CutVertices = new List <int>(); foreach (int vid in SegmentInsertVertices) { if (Target.IsVertex(vid)) { CutVertices.Add(vid); } } }
public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> added_v, IEnumerable <int> added_t) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; if (added_v != null) { foreach (int vid in added_v) { Util.gDevAssert(mesh.IsVertex(vid)); append_vertex(mesh, vid); } } foreach (int tid in added_t) { Util.gDevAssert(mesh.IsTriangle(tid)); Index3i tv = mesh.GetTriangle(tid); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); AddedT.Add(tid); Triangles.Add(tri); } }
/// <summary> /// verifies that there is a 1-1 correspondence between the fill and target loops. /// If so, adds to mergeMapV and returns true; /// </summary> bool build_merge_map_simple(DMesh3 fillMesh, int[] fillLoopV, DMesh3 targetMesh, int[] targetLoopV, double tol, IndexMap mergeMapV) { if (fillLoopV.Length != targetLoopV.Length) { return(false); } int NV = fillLoopV.Length; for (int k = 0; k < NV; ++k) { if (!fillMesh.IsVertex(fillLoopV[k])) { return(false); } Vector3d fillV = fillMesh.GetVertex(fillLoopV[k]); Vector3d sourceV = Mesh.GetVertex(targetLoopV[k]); if (fillV.Distance(sourceV) > tol) { return(false); } } for (int k = 0; k < NV; ++k) { mergeMapV[fillLoopV[k]] = targetLoopV[k]; } return(true); }
/// <summary> /// shortcut to construct graph for mesh vertices /// </summary> public static DijkstraGraphDistance MeshVertices(DMesh3 mesh, bool bSparse = false) { return((bSparse) ? new DijkstraGraphDistance(mesh.MaxVertexID, true, (id) => { return mesh.IsVertex(id); }, (a, b) => { return (float)mesh.GetVertex(a).Distance(mesh.GetVertex(b)); }, mesh.VtxVerticesItr, null) : new DijkstraGraphDistance(mesh.MaxVertexID, false, (id) => { return true; }, (a, b) => { return (float)mesh.GetVertex(a).Distance(mesh.GetVertex(b)); }, mesh.VtxVerticesItr, null)); }
public static IEnumerable <int> InteriorVertices(DMesh3 mesh) { int N = mesh.MaxVertexID; for (int i = 0; i < N; ++i) { if (mesh.IsVertex(i)) { if (mesh.IsBoundaryVertex(i) == false) { yield return(i); } } } }
public static IEnumerable <int> FilteredVertices(DMesh3 mesh, Func <DMesh3, int, bool> FilterF) { int N = mesh.MaxVertexID; for (int i = 0; i < N; ++i) { if (mesh.IsVertex(i)) { if (FilterF(mesh, i)) { yield return(i); } } } }
public static IEnumerable <int> BowtieVertices(DMesh3 mesh) { int N = mesh.MaxVertexID; for (int i = 0; i < N; ++i) { if (mesh.IsVertex(i)) { if (mesh.IsBowtieVertex(i)) { yield return(i); } } } }
public void CopyTo(DMesh3 SetMesh) { if (SetMesh.MaxVertexID < Mesh.MaxVertexID) { throw new Exception("MeshNormals.Set: SetMesh does not have enough vertices!"); } int NV = Mesh.MaxVertexID; for (int vi = 0; vi < NV; ++vi) { if (Mesh.IsVertex(vi) && SetMesh.IsVertex(vi)) { SetMesh.SetVertexNormal(vi, (Vector3f)Normals[vi]); } } }
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); }
protected virtual void InitializeVertexQuadrics() { int NT = mesh.MaxTriangleID; QuadricError[] triQuadrics = new QuadricError[NT]; double[] triAreas = new double[NT]; gParallel.BlockStartEnd(0, mesh.MaxTriangleID - 1, (start_tid, end_tid) => { Vector3d c, n; for (int tid = start_tid; tid <= end_tid; tid++) { if (mesh.IsTriangle(tid)) { mesh.GetTriInfo(tid, out n, out triAreas[tid], out c); triQuadrics[tid] = new QuadricError(ref n, ref c); } } }); int NV = mesh.MaxVertexID; vertQuadrics = new QuadricError[NV]; gParallel.BlockStartEnd(0, mesh.MaxVertexID - 1, (start_vid, end_vid) => { for (int vid = start_vid; vid <= end_vid; vid++) { vertQuadrics[vid] = QuadricError.Zero; if (mesh.IsVertex(vid)) { foreach (int tid in mesh.VtxTrianglesItr(vid)) { vertQuadrics[vid].Add(triAreas[tid], ref triQuadrics[tid]); } //Util.gDevAssert(MathUtil.EpsilonEqual(0, vertQuadrics[i].Evaluate(mesh.GetVertex(i)), MathUtil.Epsilon * 10)); } } }); }
public static ValidationStatus IsEdgeLoop(DMesh3 mesh, EdgeLoop loop) { int N = loop.Vertices.Length; for (int i = 0; i < N; ++i) { if (!mesh.IsVertex(loop.Vertices[i])) { return(ValidationStatus.NotAVertex); } } for (int i = 0; i < N; ++i) { int a = loop.Vertices[i]; int b = loop.Vertices[(i + 1) % N]; int eid = mesh.FindEdge(a, b); if (eid == DMesh3.InvalidID) { return(ValidationStatus.VerticesNotConnectedByEdge); } } return(ValidationStatus.Ok); }
public virtual bool Apply() { insert_corners(); // [RMS] not using this? //HashSet<int> corner_v = new HashSet<int>(CurveVertices); // not sure we need to track all of these HashSet <int> ZeroEdges = new HashSet <int>(); HashSet <int> ZeroVertices = new HashSet <int>(); OnCutEdges = new HashSet <int>(); // loop over segments, insert each one in sequence int N = (IsLoop) ? Curve.VertexCount : Curve.VertexCount - 1; for (int si = 0; si < N; ++si) { int i0 = si; int i1 = (si + 1) % Curve.VertexCount; Segment2d seg = new Segment2d(Curve[i0], Curve[i1]); int i0_vid = CurveVertices[i0]; int i1_vid = CurveVertices[i1]; // If these vertices are already connected by an edge, we can just continue. int existing_edge = Mesh.FindEdge(i0_vid, i1_vid); if (existing_edge != DMesh3.InvalidID) { OnCutEdges.Add(existing_edge); continue; } // compute edge-crossing signs // [TODO] could walk along mesh from a to b, rather than computing for entire mesh? int MaxVID = Mesh.MaxVertexID; int[] signs = new int[MaxVID]; gParallel.ForEach(Interval1i.Range(MaxVID), (vid) => { if (Mesh.IsVertex(vid)) { if (vid == i0_vid || vid == i1_vid) { signs[vid] = 0; } else { Vector2d v2 = PointF(vid); // tolerance defines band in which we will consider values to be zero signs[vid] = seg.WhichSide(v2, MathUtil.ZeroTolerance); } } else { signs[vid] = int.MaxValue; } }); // have to skip processing of new edges. If edge id // is > max at start, is new. Otherwise if in NewEdges list, also new. // (need both in case we re-use an old edge index) int MaxEID = Mesh.MaxEdgeID; HashSet <int> NewEdges = new HashSet <int>(); HashSet <int> NewCutVertices = new HashSet <int>(); NewCutVertices.Add(i0_vid); NewCutVertices.Add(i1_vid); // cut existing edges with segment for (int eid = 0; eid < MaxEID; ++eid) { if (Mesh.IsEdge(eid) == false) { continue; } if (eid >= MaxEID || NewEdges.Contains(eid)) { continue; } // cannot cut boundary edges? if (Mesh.IsBoundaryEdge(eid)) { continue; } Index2i ev = Mesh.GetEdgeV(eid); int eva_sign = signs[ev.a]; int evb_sign = signs[ev.b]; bool eva_in_segment = false; if (eva_sign == 0) { eva_in_segment = Math.Abs(seg.Project(PointF(ev.a))) < (seg.Extent + MathUtil.ZeroTolerance); } bool evb_in_segment = false; if (evb_sign == 0) { evb_in_segment = Math.Abs(seg.Project(PointF(ev.b))) < (seg.Extent + MathUtil.ZeroTolerance); } // If one or both vertices are on-segment, we have special case. // If just one vertex is on the segment, we can skip this edge. // If both vertices are on segment, then we can just re-use this edge. if (eva_in_segment || evb_in_segment) { if (eva_in_segment && evb_in_segment) { ZeroEdges.Add(eid); OnCutEdges.Add(eid); } else { ZeroVertices.Add(eva_in_segment ? ev.a : ev.b); } continue; } // no crossing if (eva_sign * evb_sign > 0) { continue; } // compute segment/segment intersection Vector2d va = PointF(ev.a); Vector2d vb = PointF(ev.b); Segment2d edge_seg = new Segment2d(va, vb); IntrSegment2Segment2 intr = new IntrSegment2Segment2(seg, edge_seg); intr.Compute(); if (intr.Type == IntersectionType.Segment) { // [RMS] we should have already caught this above, so if it happens here it is probably spurious? // we should have caught this case above, but numerics are different so it might occur again ZeroEdges.Add(eid); OnCutEdges.Add(eid); continue; } else if (intr.Type != IntersectionType.Point) { continue; // no intersection } Vector2d x = intr.Point0; // this case happens if we aren't "on-segment" but after we do the test the intersection pt // is within epsilon of one end of the edge. This is a spurious t-intersection and we // can ignore it. Some other edge should exist that picks up this vertex as part of it. // [TODO] what about if this edge is degenerate? bool x_in_segment = Math.Abs(edge_seg.Project(x)) < (edge_seg.Extent - MathUtil.ZeroTolerance); if (!x_in_segment) { continue; } // split edge at this segment DMesh3.EdgeSplitInfo splitInfo; MeshResult result = Mesh.SplitEdge(eid, out splitInfo); if (result != MeshResult.Ok) { throw new Exception("MeshInsertUVSegment.Cut: failed in SplitEdge"); //return false; } // move split point to intersection position SetPointF(splitInfo.vNew, x); NewCutVertices.Add(splitInfo.vNew); NewEdges.Add(splitInfo.eNewBN); NewEdges.Add(splitInfo.eNewCN); // some splits - but not all - result in new 'other' edges that are on // the polypath. We want to keep track of these edges so we can extract loop later. Index2i ecn = Mesh.GetEdgeV(splitInfo.eNewCN); if (NewCutVertices.Contains(ecn.a) && NewCutVertices.Contains(ecn.b)) { OnCutEdges.Add(splitInfo.eNewCN); } // since we don't handle bdry edges this should never be false, but maybe we will handle bdry later... if (splitInfo.eNewDN != DMesh3.InvalidID) { NewEdges.Add(splitInfo.eNewDN); Index2i edn = Mesh.GetEdgeV(splitInfo.eNewDN); if (NewCutVertices.Contains(edn.a) && NewCutVertices.Contains(edn.b)) { OnCutEdges.Add(splitInfo.eNewDN); } } } } //MeshEditor editor = new MeshEditor(Mesh); //foreach (int eid in OnCutEdges) // editor.AppendBox(new Frame3f(Mesh.GetEdgePoint(eid, 0.5)), 0.1f); //Util.WriteDebugMesh(Mesh, string.Format("C:\\git\\geometry3SharpDemos\\geometry3Test\\test_output\\after_inserted.obj")); // extract the cut paths if (EnableCutSpansAndLoops) { find_cut_paths(OnCutEdges); } return(true); } // Apply()
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); }
public virtual bool Apply() { HashSet <int> OnCurveVerts = new HashSet <int>(); // original vertices that were epsilon-coincident w/ curve vertices insert_corners(OnCurveVerts); // [RMS] not using this? //HashSet<int> corner_v = new HashSet<int>(CurveVertices); // not sure we need to track all of these HashSet <int> ZeroEdges = new HashSet <int>(); HashSet <int> ZeroVertices = new HashSet <int>(); OnCutEdges = new HashSet <int>(); HashSet <int> NewEdges = new HashSet <int>(); HashSet <int> NewCutVertices = new HashSet <int>(); sbyte[] signs = new sbyte[2 * Mesh.MaxVertexID + 2 * Curve.VertexCount]; HashSet <int> segTriangles = new HashSet <int>(); HashSet <int> segVertices = new HashSet <int>(); HashSet <int> segEdges = new HashSet <int>(); // loop over segments, insert each one in sequence int N = (IsLoop) ? Curve.VertexCount : Curve.VertexCount - 1; for (int si = 0; si < N; ++si) { int i0 = si; int i1 = (si + 1) % Curve.VertexCount; Segment2d seg = new Segment2d(Curve[i0], Curve[i1]); int i0_vid = CurveVertices[i0]; int i1_vid = CurveVertices[i1]; // If these vertices are already connected by an edge, we can just continue. int existing_edge = Mesh.FindEdge(i0_vid, i1_vid); if (existing_edge != DMesh3.InvalidID) { add_cut_edge(existing_edge); continue; } if (triSpatial != null) { segTriangles.Clear(); segVertices.Clear(); segEdges.Clear(); AxisAlignedBox2d segBounds = new AxisAlignedBox2d(seg.P0); segBounds.Contain(seg.P1); segBounds.Expand(MathUtil.ZeroTolerancef * 10); triSpatial.FindTrianglesInRange(segBounds, segTriangles); IndexUtil.TrianglesToVertices(Mesh, segTriangles, segVertices); IndexUtil.TrianglesToEdges(Mesh, segTriangles, segEdges); } int MaxVID = Mesh.MaxVertexID; IEnumerable <int> vertices = Interval1i.Range(MaxVID); if (triSpatial != null) { vertices = segVertices; } // compute edge-crossing signs // [TODO] could walk along mesh from a to b, rather than computing for entire mesh? if (signs.Length < MaxVID) { signs = new sbyte[2 * MaxVID]; } gParallel.ForEach(vertices, (vid) => { if (Mesh.IsVertex(vid)) { if (vid == i0_vid || vid == i1_vid) { signs[vid] = 0; } else { Vector2d v2 = PointF(vid); // tolerance defines band in which we will consider values to be zero signs[vid] = (sbyte)seg.WhichSide(v2, SpatialEpsilon); } } else { signs[vid] = sbyte.MaxValue; } }); // have to skip processing of new edges. If edge id // is > max at start, is new. Otherwise if in NewEdges list, also new. // (need both in case we re-use an old edge index) int MaxEID = Mesh.MaxEdgeID; NewEdges.Clear(); NewCutVertices.Clear(); NewCutVertices.Add(i0_vid); NewCutVertices.Add(i1_vid); // cut existing edges with segment IEnumerable <int> edges = Interval1i.Range(MaxEID); if (triSpatial != null) { edges = segEdges; } foreach (int eid in edges) { if (Mesh.IsEdge(eid) == false) { continue; } if (eid >= MaxEID || NewEdges.Contains(eid)) { continue; } // cannot cut boundary edges? if (Mesh.IsBoundaryEdge(eid)) { continue; } Index2i ev = Mesh.GetEdgeV(eid); int eva_sign = signs[ev.a]; int evb_sign = signs[ev.b]; // [RMS] should we be using larger epsilon here? If we don't track OnCurveVerts explicitly, we // need to at least use same epsilon we passed to insert_corner_from_bary...do we still also // need that to catch the edges we split in the poke? bool eva_in_segment = false; if (eva_sign == 0) { eva_in_segment = OnCurveVerts.Contains(ev.a) || Math.Abs(seg.Project(PointF(ev.a))) < (seg.Extent + SpatialEpsilon); } bool evb_in_segment = false; if (evb_sign == 0) { evb_in_segment = OnCurveVerts.Contains(ev.b) || Math.Abs(seg.Project(PointF(ev.b))) < (seg.Extent + SpatialEpsilon); } // If one or both vertices are on-segment, we have special case. // If just one vertex is on the segment, we can skip this edge. // If both vertices are on segment, then we can just re-use this edge. if (eva_in_segment || evb_in_segment) { if (eva_in_segment && evb_in_segment) { ZeroEdges.Add(eid); add_cut_edge(eid); NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b); } else { int zvid = eva_in_segment ? ev.a : ev.b; ZeroVertices.Add(zvid); NewCutVertices.Add(zvid); } continue; } // no crossing if (eva_sign * evb_sign > 0) { continue; } // compute segment/segment intersection Vector2d va = PointF(ev.a); Vector2d vb = PointF(ev.b); Segment2d edge_seg = new Segment2d(va, vb); IntrSegment2Segment2 intr = new IntrSegment2Segment2(seg, edge_seg); intr.Compute(); if (intr.Type == IntersectionType.Segment) { // [RMS] we should have already caught this above, so if it happens here it is probably spurious? // we should have caught this case above, but numerics are different so it might occur again ZeroEdges.Add(eid); NewCutVertices.Add(ev.a); NewCutVertices.Add(ev.b); add_cut_edge(eid); continue; } else if (intr.Type != IntersectionType.Point) { continue; // no intersection } Vector2d x = intr.Point0; double t = Math.Sqrt(x.DistanceSquared(va) / va.DistanceSquared(vb)); // this case happens if we aren't "on-segment" but after we do the test the intersection pt // is within epsilon of one end of the edge. This is a spurious t-intersection and we // can ignore it. Some other edge should exist that picks up this vertex as part of it. // [TODO] what about if this edge is degenerate? bool x_in_segment = Math.Abs(edge_seg.Project(x)) < (edge_seg.Extent - SpatialEpsilon); if (!x_in_segment) { continue; } Index2i et = Mesh.GetEdgeT(eid); spatial_remove_triangles(et.a, et.b); // split edge at this segment DMesh3.EdgeSplitInfo splitInfo; MeshResult result = Mesh.SplitEdge(eid, out splitInfo, t); if (result != MeshResult.Ok) { throw new Exception("MeshInsertUVSegment.Apply: SplitEdge failed - " + result.ToString()); //return false; } // move split point to intersection position SetPointF(splitInfo.vNew, x); NewCutVertices.Add(splitInfo.vNew); NewEdges.Add(splitInfo.eNewBN); NewEdges.Add(splitInfo.eNewCN); spatial_add_triangles(et.a, et.b); spatial_add_triangles(splitInfo.eNewT2, splitInfo.eNewT3); // some splits - but not all - result in new 'other' edges that are on // the polypath. We want to keep track of these edges so we can extract loop later. Index2i ecn = Mesh.GetEdgeV(splitInfo.eNewCN); if (NewCutVertices.Contains(ecn.a) && NewCutVertices.Contains(ecn.b)) { add_cut_edge(splitInfo.eNewCN); } // since we don't handle bdry edges this should never be false, but maybe we will handle bdry later... if (splitInfo.eNewDN != DMesh3.InvalidID) { NewEdges.Add(splitInfo.eNewDN); Index2i edn = Mesh.GetEdgeV(splitInfo.eNewDN); if (NewCutVertices.Contains(edn.a) && NewCutVertices.Contains(edn.b)) { add_cut_edge(splitInfo.eNewDN); } } } } // extract the cut paths if (EnableCutSpansAndLoops) { find_cut_paths(OnCutEdges); } return(true); } // Apply()
/// <summary> /// Check if this m2 is the same as this mesh. By default only checks /// vertices and triangles, turn on other parameters w/ flags /// </summary> public bool IsSameMesh(DMesh3 m2, bool bCheckEdges = false, bool bCheckNormals = false, bool bCheckColors = false, bool bCheckUVs = false, bool bCheckGroups = false, float Epsilon = MathUtil.Epsilonf) { if (VertexCount != m2.VertexCount) { return(false); } if (TriangleCount != m2.TriangleCount) { return(false); } foreach (int vid in VertexIndices()) { if (m2.IsVertex(vid) == false || GetVertex(vid).EpsilonEqual(m2.GetVertex(vid), Epsilon) == false) { return(false); } } foreach (int tid in TriangleIndices()) { if (m2.IsTriangle(tid) == false || GetTriangle(tid).Equals(m2.GetTriangle(tid)) == false) { return(false); } } if (bCheckEdges) { if (EdgeCount != m2.EdgeCount) { return(false); } foreach (int eid in EdgeIndices()) { if (m2.IsEdge(eid) == false || GetEdge(eid).Equals(m2.GetEdge(eid)) == false) { return(false); } } } if (bCheckNormals) { if (HasVertexNormals != m2.HasVertexNormals) { return(false); } if (HasVertexNormals) { foreach (int vid in VertexIndices()) { if (GetVertexNormal(vid).EpsilonEqual(m2.GetVertexNormal(vid), Epsilon) == false) { return(false); } } } } if (bCheckColors) { if (HasVertexColors != m2.HasVertexColors) { return(false); } if (HasVertexColors) { foreach (int vid in VertexIndices()) { if (GetVertexColor(vid).EpsilonEqual(m2.GetVertexColor(vid), Epsilon) == false) { return(false); } } } } if (bCheckUVs) { if (HasVertexUVs != m2.HasVertexUVs) { return(false); } if (HasVertexUVs) { foreach (int vid in VertexIndices()) { if (GetVertexUV(vid).EpsilonEqual(m2.GetVertexUV(vid), Epsilon) == false) { return(false); } } } } if (bCheckGroups) { if (HasTriangleGroups != m2.HasTriangleGroups) { return(false); } if (HasTriangleGroups) { foreach (int tid in TriangleIndices()) { if (GetTriangleGroup(tid) != m2.GetTriangleGroup(tid)) { return(false); } } } } return(true); }
private void Remove(TriangleRemoval rem = TriangleRemoval.contained) { #if ACAD var lastColor = 0; #endif DMeshAABBTree3 spatial = new DMeshAABBTree3(CutMesh, true); spatial.WindingNumber(Vector3d.Zero); SafeListBuilder <int> containedT = new SafeListBuilder <int>(); SafeListBuilder <int> removeAnywayT = new SafeListBuilder <int>(); // if the windinging number for the centroid point candidate triangles // is one or more (or close for safety), then it's inside the volume of cutMesh // gParallel.ForEach(Target.TriangleIndices(), (tid) => { if (Target.GetTriArea(tid) < VertexSnapTol) { removeAnywayT.SafeAdd(tid); return; // parallel: equivalent to continue. } Vector3d v = Target.GetTriCentroid(tid); if (AttemptPlanarRemoval) { // slightly offset the point to be evaluated. // var nrm = Target.GetTriNormal(tid); v -= nrm * 5 * VertexSnapTol; } var winding = spatial.WindingNumber(v); bool IsInternal = winding > 0.9; #if ACAD // temporarily here for debug purposes var wantColor = IsInternal ? 1 : 2; if (lastColor != wantColor) { Debug.WriteLine($"-LAYER set L{wantColor}"); Debug.WriteLine($""); lastColor = wantColor; } Triangle3d tri = new Triangle3d(); Target.GetTriVertices(tid, ref tri.V0, ref tri.V1, ref tri.V2); Debug.WriteLine($"3DPOLY {tri.V0.CommaDelimited} {tri.V1.CommaDelimited} {tri.V2.CommaDelimited} {tri.V0.CommaDelimited} {v.CommaDelimited} "); #endif if (IsInternal) { containedT.SafeAdd(tid); } }); if (rem == TriangleRemoval.contained) { MeshEditor.RemoveTriangles(Target, containedT.Result); } else if (rem == TriangleRemoval.external) { var ext = Target.TriangleIndices().Except(containedT.Result); MeshEditor.RemoveTriangles(Target, ext); } MeshEditor.RemoveTriangles(Target, removeAnywayT.Result); // [RMS] construct set of on-cut vertices? This is not // necessarily all boundary vertices... CutVertices = new List <int>(); foreach (int vid in SegmentInsertVertices) { if (Target.IsVertex(vid)) { CutVertices.Add(vid); } } }
// Assumption here is that Submesh has been modified, but boundary loop has // been preserved, and that old submesh has already been removed from this mesh. // So, we just have to append new vertices and then rewrite triangles // If new_tris or new_verts is non-null, we will return this info. // new_tris should be set to TriangleCount (ie it is not necessarily a map) // For new_verts, if we used an existing bdry vtx instead, we set the value to -(existing_index+1), // otherwise the value is new_index (+1 is to handle 0) // // Returns true if submesh successfully inserted, false if any triangles failed // (which happens if triangle would result in non-manifold mesh) public bool ReinsertSubmesh(DSubmesh3 sub, ref int[] new_tris, out IndexMap SubToNewV) { if (sub.BaseBorderV == null) { throw new Exception("MeshEditor.ReinsertSubmesh: Submesh does not have required boundary info. Call ComputeBoundaryInfo()!"); } DMesh3 submesh = sub.SubMesh; bool bAllOK = true; IndexFlagSet done_v = new IndexFlagSet(submesh.MaxVertexID, submesh.TriangleCount / 2); SubToNewV = new IndexMap(submesh.MaxVertexID, submesh.VertexCount); int nti = 0; int NT = submesh.MaxTriangleID; for (int ti = 0; ti < NT; ++ti) { if (submesh.IsTriangle(ti) == false) { continue; } Index3i sub_t = submesh.GetTriangle(ti); int gid = submesh.GetTriangleGroup(ti); Index3i new_t = Index3i.Zero; for (int j = 0; j < 3; ++j) { int sub_v = sub_t[j]; int new_v = -1; if (done_v[sub_v] == false) { // first check if this is a boundary vtx on submesh and maps to a bdry vtx on base mesh if (submesh.vertex_is_boundary(sub_v)) { int base_v = (sub_v < sub.SubToBaseV.size) ? sub.SubToBaseV[sub_v] : -1; if (base_v >= 0 && Mesh.IsVertex(base_v) && sub.BaseBorderV[base_v] == true) { // [RMS] this should always be true, but assert in tests to find out Debug.Assert(Mesh.vertex_is_boundary(base_v)); if (Mesh.vertex_is_boundary(base_v)) { new_v = base_v; } } } // if that didn't happen, append new vtx if (new_v == -1) { new_v = Mesh.AppendVertex(submesh, sub_v); } SubToNewV[sub_v] = new_v; done_v[sub_v] = true; } else { new_v = SubToNewV[sub_v]; } new_t[j] = new_v; } Debug.Assert(Mesh.FindTriangle(new_t.a, new_t.b, new_t.c) == DMesh3.InvalidID); int new_tid = Mesh.AppendTriangle(new_t, gid); Debug.Assert(new_tid != DMesh3.InvalidID && new_tid != DMesh3.NonManifoldID); if (!Mesh.IsTriangle(new_tid)) { bAllOK = false; } if (new_tris != null) { new_tris[nti++] = new_tid; } } return(bAllOK); }
/// <summary> /// Check if this m2 is the same as this mesh. By default only checks /// vertices and triangles, turn on other parameters w/ flags /// </summary> public bool IsSameMesh(DMesh3 m2, bool bCheckConnectivity, bool bCheckEdgeIDs = false, bool bCheckNormals = false, bool bCheckColors = false, bool bCheckUVs = false, bool bCheckGroups = false, float Epsilon = MathUtil.Epsilonf) { if (VertexCount != m2.VertexCount) { return(false); } if (TriangleCount != m2.TriangleCount) { return(false); } foreach (int vid in VertexIndices()) { if (m2.IsVertex(vid) == false || GetVertex(vid).EpsilonEqual(m2.GetVertex(vid), Epsilon) == false) { return(false); } } foreach (int tid in TriangleIndices()) { if (m2.IsTriangle(tid) == false || GetTriangle(tid).Equals(m2.GetTriangle(tid)) == false) { return(false); } } if (bCheckConnectivity) { foreach (int eid in EdgeIndices()) { Index4i e = GetEdge(eid); int other_eid = m2.FindEdge(e.a, e.b); if (other_eid == InvalidID) { return(false); } Index4i oe = m2.GetEdge(other_eid); if (Math.Min(e.c, e.d) != Math.Min(oe.c, oe.d) || Math.Max(e.c, e.d) != Math.Max(oe.c, oe.d)) { return(false); } } } if (bCheckEdgeIDs) { if (EdgeCount != m2.EdgeCount) { return(false); } foreach (int eid in EdgeIndices()) { if (m2.IsEdge(eid) == false || GetEdge(eid).Equals(m2.GetEdge(eid)) == false) { return(false); } } } if (bCheckNormals) { if (HasVertexNormals != m2.HasVertexNormals) { return(false); } if (HasVertexNormals) { foreach (int vid in VertexIndices()) { if (GetVertexNormal(vid).EpsilonEqual(m2.GetVertexNormal(vid), Epsilon) == false) { return(false); } } } } if (bCheckColors) { if (HasVertexColors != m2.HasVertexColors) { return(false); } if (HasVertexColors) { foreach (int vid in VertexIndices()) { if (GetVertexColor(vid).EpsilonEqual(m2.GetVertexColor(vid), Epsilon) == false) { return(false); } } } } if (bCheckUVs) { if (HasVertexUVs != m2.HasVertexUVs) { return(false); } if (HasVertexUVs) { foreach (int vid in VertexIndices()) { if (GetVertexUV(vid).EpsilonEqual(m2.GetVertexUV(vid), Epsilon) == false) { return(false); } } } } if (bCheckGroups) { if (HasTriangleGroups != m2.HasTriangleGroups) { return(false); } if (HasTriangleGroups) { foreach (int tid in TriangleIndices()) { if (GetTriangleGroup(tid) != m2.GetTriangleGroup(tid)) { return(false); } } } } 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()
/// <summary> /// Construct vertex correspondences between fill mesh boundary loop /// and input mesh boundary loop. In ideal case there is an easy 1-1 /// correspondence. If that is not true, then do a brute-force search /// to find the best correspondences we can. /// /// Currently only returns unique correspondences. If any vertex /// matches with multiple input vertices it is not merged. /// [TODO] we could do better in many cases... /// /// Return value is list of indices into fillLoopV that were not merged /// </summary> List <int> build_merge_map(DMesh3 fillMesh, int[] fillLoopV, DMesh3 targetMesh, int[] targetLoopV, double tol, IndexMap mergeMapV) { if (fillLoopV.Length == targetLoopV.Length) { if (build_merge_map_simple(fillMesh, fillLoopV, targetMesh, targetLoopV, tol, mergeMapV)) { return(null); } } int NF = fillLoopV.Length, NT = targetLoopV.Length; bool[] doneF = new bool[NF], doneT = new bool[NT]; int[] countF = new int[NF], countT = new int[NT]; var errorV = new List <int>(); var matchF = new SmallListSet(); matchF.Resize(NF); // find correspondences double tol_sqr = tol * tol; for (int i = 0; i < NF; ++i) { if (fillMesh.IsVertex(fillLoopV[i]) == false) { doneF[i] = true; errorV.Add(i); continue; } matchF.AllocateAt(i); Vector3d v = fillMesh.GetVertex(fillLoopV[i]); for (int j = 0; j < NT; ++j) { Vector3d v2 = targetMesh.GetVertex(targetLoopV[j]); if (v.DistanceSquared(ref v2) < tol_sqr) { matchF.Insert(i, j); } } } for (int i = 0; i < NF; ++i) { if (doneF[i]) { continue; } if (matchF.Count(i) == 1) { int j = matchF.First(i); mergeMapV[fillLoopV[i]] = targetLoopV[j]; doneF[i] = true; } } for (int i = 0; i < NF; ++i) { if (doneF[i] == false) { errorV.Add(i); } } return(errorV); }