/// <summary> /// construct EdgeLoop from a list of vertices of mesh /// if loop is a boundary edge, we can correct orientation if requested /// </summary> public static EdgeLoop FromVertices(DMesh3 mesh, IList <int> vertices, bool bAutoOrient = true) { int[] Vertices = new int[vertices.Count]; for (int i = 0; i < Vertices.Length; i++) { Vertices[i] = vertices[i]; } if (bAutoOrient) { int a = Vertices[0], b = Vertices[1]; int eid = mesh.FindEdge(a, b); if (mesh.IsBoundaryEdge(eid)) { Index2i ev = mesh.GetOrientedBoundaryEdgeV(eid); if (ev.a == b && ev.b == a) { Array.Reverse(Vertices); } } } int[] Edges = new int[Vertices.Length]; for (int i = 0; i < Edges.Length; ++i) { int a = Vertices[i], b = Vertices[(i + 1) % Vertices.Length]; Edges[i] = mesh.FindEdge(a, b); if (Edges[i] == DMesh3.InvalidID) { throw new Exception("EdgeLoop.FromVertices: invalid edge [" + a + "," + b + "]"); } } return(new EdgeLoop(mesh, Vertices, Edges, false)); }
public bool IsInternalSpan() { int NV = Vertices.Length; for (int i = 0; i < NV-1; ++i ) { int eid = Mesh.FindEdge(Vertices[i], Vertices[i + 1]); Debug.Assert(eid != DMesh3.InvalidID); if (Mesh.IsBoundaryEdge(eid)) return false; } return true; }
public void AppendSegments(double r) { foreach (var seg in Segments) { Segment3d s = new Segment3d(seg.v0.v, seg.v1.v); if (Target.FindEdge(seg.v0.vtx_id, seg.v1.vtx_id) == DMesh3.InvalidID) { MeshEditor.AppendLine(Target, s, (float)r); } } }
public bool IsInternalLoop() { int NV = Vertices.Length; for (int i = 0; i < NV; ++i) { int eid = Mesh.FindEdge(Vertices[i], Vertices[(i + 1) % NV]); Debug.Assert(eid != DMesh3.InvalidID); if (Mesh.edge_is_boundary(eid)) { return(false); } } return(true); }
public int MapEdgeToBaseMesh(int sub_eid) { Index2i sub_ev = SubMesh.GetEdgeV(sub_eid); Index2i base_ev = MapVerticesToBaseMesh(sub_ev); return(BaseMesh.FindEdge(base_ev.a, base_ev.b)); }
// 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); } } }
/// <summary> /// if this is a border edge-loop, we can check that it is oriented correctly, and /// if not, reverse it. /// Returns true if we reversed orientation. /// </summary> public bool CorrectOrientation() { int a = Vertices[0], b = Vertices[1]; int eid = Mesh.FindEdge(a, b); if (Mesh.IsBoundaryEdge(eid)) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eid); if (ev.a == b && ev.b == a) { Reverse(); return(true); } } return(false); }
// 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); } } }
public int MapEdgeToSubmesh(int base_eid) { Index2i base_ev = BaseMesh.GetEdgeV(base_eid); Index2i sub_ev = MapVerticesToSubmesh(base_ev); return(SubMesh.FindEdge(sub_ev.a, sub_ev.b)); }
/// <summary> /// Convert vertex span to list of edges. This should be somewhere else. /// </summary> public static int[] VerticesToEdges(DMesh3 mesh, int[] vertex_span) { int NV = vertex_span.Length; int[] edges = new int[NV-1]; for ( int i = 0; i < NV-1; ++i ) { int v0 = vertex_span[i]; int v1 = vertex_span[(i + 1)]; edges[i] = mesh.FindEdge(v0, v1); } return edges; }
/// <summary> /// Convert a vertex loop to an edge loop. This should be somewhere else... /// </summary> public static int[] VertexLoopToEdgeLoop(DMesh3 mesh, int[] vertex_loop) { int NV = vertex_loop.Length; int[] edges = new int[NV]; for (int i = 0; i < NV; ++i) { int v0 = vertex_loop[i]; int v1 = vertex_loop[(i + 1) % NV]; edges[i] = mesh.FindEdge(v0, v1); } return(edges); }
void add_edge_pos(int a, int b, Vector3d crossing_pos) { int eid = Mesh.FindEdge(a, b); if (eid == DMesh3.InvalidID) { throw new Exception("MeshIsoCurves.add_edge_split: invalid edge?"); } if (EdgeLocations.ContainsKey(eid)) { return; } EdgeLocations[eid] = crossing_pos; }
/// <summary> /// construct EdgeLoop from a list of vertices of mesh /// </summary> public static EdgeLoop FromVertices(DMesh3 mesh, IList <int> vertices) { int[] Vertices = new int[vertices.Count]; for (int i = 0; i < Vertices.Length; i++) { Vertices[i] = vertices[i]; } int[] Edges = new int[Vertices.Length]; for (int i = 0; i < Edges.Length; ++i) { Edges[i] = mesh.FindEdge(Vertices[i], Vertices[(i + 1) % Vertices.Length]); } return(new EdgeLoop(mesh, Vertices, Edges, false)); }
/// <summary> /// given list of edges of MeshA, and vertex map from A to B, map to list of edges on B /// </summary> public static List <int> MapEdgesViaVertexMap(IIndexMap AtoBV, DMesh3 MeshA, DMesh3 MeshB, List <int> edges) { int N = edges.Count; List <int> result = new List <int>(N); for (int i = 0; i < N; ++i) { int eid_a = edges[i]; Index2i aev = MeshA.GetEdgeV(eid_a); int bev0 = AtoBV[aev.a]; int bev1 = AtoBV[aev.b]; int eid_b = MeshB.FindEdge(bev0, bev1); Debug.Assert(eid_b != DMesh3.InvalidID); result.Add(eid_b); } return(result); }
/// <summary> /// Check if all edges of this loop are boundary edges. /// If testMesh != null, will check that mesh instead of internal Mesh /// </summary> public bool IsBoundaryLoop(DMesh3 testMesh = null) { DMesh3 useMesh = (testMesh != null) ? testMesh : Mesh; int NV = Vertices.Length; for (int i = 0; i < NV; ++i) { int eid = useMesh.FindEdge(Vertices[i], Vertices[(i + 1) % NV]); Debug.Assert(eid != DMesh3.InvalidID); if (useMesh.IsBoundaryEdge(eid) == false) { return(false); } } return(true); }
/// <summary> /// construct EdgeSpan from a list of vertices of mesh /// </summary> public static EdgeSpan FromVertices(DMesh3 mesh, IList <int> vertices) { int NV = vertices.Count; int[] Vertices = new int[NV]; for (int i = 0; i < NV; ++i) { Vertices[i] = vertices[i]; } int NE = NV - 1; int[] Edges = new int[NE]; for (int i = 0; i < NE; ++i) { Edges[i] = mesh.FindEdge(Vertices[i], Vertices[i + 1]); if (Edges[i] == DMesh3.InvalidID) { throw new Exception("EdgeSpan.FromVertices: vertices are not connected by edge!"); } } return(new EdgeSpan(mesh, Vertices, Edges, false)); }
/// <summary> /// given EdgeLoop on MeshA, and vertex map from A to B, map to EdgeLoop on B /// </summary> public static EdgeLoop MapLoopViaVertexMap(IIndexMap AtoBV, DMesh3 MeshA, DMesh3 MeshB, EdgeLoop loopIn) { int NV = loopIn.VertexCount, NE = loopIn.EdgeCount; int[] newVerts = new int[NV]; for (int i = 0; i < NV; ++i) { newVerts[i] = AtoBV[loopIn.Vertices[i]]; } int[] newEdges = new int[NE]; for (int i = 0; i < NE; ++i) { int eid_a = loopIn.Edges[i]; Index2i aev = MeshA.GetEdgeV(eid_a); int bev0 = AtoBV[aev.a]; int bev1 = AtoBV[aev.b]; newEdges[i] = MeshB.FindEdge(bev0, bev1); Debug.Assert(newEdges[i] != DMesh3.InvalidID); } return(new EdgeLoop(MeshB, newVerts, newEdges, false)); }
public static ValidationStatus IsBoundaryLoop(DMesh3 mesh, EdgeLoop loop) { int N = loop.Vertices.Length; for (int i = 0; i < N; ++i) { if (!mesh.vertex_is_boundary(loop.Vertices[i])) { return(ValidationStatus.NotBoundaryVertex); } } 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); } if (mesh.edge_is_boundary(eid) == false) { return(ValidationStatus.NotBoundaryEdge); } Index2i ev = mesh.GetOrientedBoundaryEdgeV(eid); if (!(ev.a == a && ev.b == b)) { return(ValidationStatus.IncorrectLoopOrientation); } } return(ValidationStatus.Ok); }
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() { 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 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 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()
/// <summary> /// Disconnect the given triangles from their neighbours, by duplicating "boundary" vertices, ie /// vertices on edges for which one triangle is in-set and the other is not. /// If bComputeEdgePairs is true, we return list of old/new edge pairs (useful for stitching) /// [TODO] currently boundary-edge behaviour is to *not* duplicate boundary verts /// </summary> public bool SeparateTriangles(IEnumerable <int> triangles, bool bComputeEdgePairs, out List <Index2i> EdgePairs) { HashSet <int> in_set = new HashSet <int>(triangles); Dictionary <int, int> VertexMap = new Dictionary <int, int>(); EdgePairs = null; HashSet <int> edges = null; List <Index2i> OldEdgeVerts = null; if (bComputeEdgePairs) { EdgePairs = new List <Index2i>(); edges = new HashSet <int>(); OldEdgeVerts = new List <Index2i>(); } // duplicate vertices on edges that are on boundary of triangles roi foreach (int tid in triangles) { Index3i te = Mesh.GetTriEdges(tid); for (int j = 0; j < 3; ++j) { Index2i et = Mesh.GetEdgeT(te[j]); // [TODO] what about behavior where we want to also duplicate boundary verts?? if (et.b == DMesh3.InvalidID || (et.a == tid && in_set.Contains(et.b)) || (et.b == tid && in_set.Contains(et.a))) { te[j] = -1; } } for (int j = 0; j < 3; ++j) { if (te[j] == -1) { continue; } Index2i ev = Mesh.GetEdgeV(te[j]); if (VertexMap.ContainsKey(ev.a) == false) { VertexMap[ev.a] = Mesh.AppendVertex(Mesh, ev.a); } if (VertexMap.ContainsKey(ev.b) == false) { VertexMap[ev.b] = Mesh.AppendVertex(Mesh, ev.b); } if (bComputeEdgePairs && edges.Contains(te[j]) == false) { edges.Add(te[j]); OldEdgeVerts.Add(ev); EdgePairs.Add(new Index2i(te[j], -1)); } } } // update triangles foreach (int tid in triangles) { Index3i tv = Mesh.GetTriangle(tid); Index3i tv_new = tv; for (int j = 0; j < 3; ++j) { int newv; if (VertexMap.TryGetValue(tv[j], out newv)) { tv_new[j] = newv; } } if (tv_new != tv) { Mesh.SetTriangle(tid, tv_new); } } if (bComputeEdgePairs) { for (int k = 0; k < EdgePairs.Count; ++k) { Index2i old_ev = OldEdgeVerts[k]; int new_a = VertexMap[old_ev.a]; int new_b = VertexMap[old_ev.b]; int new_eid = Mesh.FindEdge(new_a, new_b); Util.gDevAssert(new_eid != DMesh3.InvalidID); EdgePairs[k] = new Index2i(EdgePairs[k].a, new_eid); } } return(true); }