/// <summary> /// Find point-normal frame at ray-intersection point on mesh, or return false if no hit. /// Returns interpolated vertex-normal frame if available, otherwise tri-normal frame. /// </summary> public static bool RayHitPointFrame(DMesh3 mesh, ISpatial spatial, Ray3d ray, out Frame3f hitPosFrame, bool bForceFaceNormal = false) { hitPosFrame = new Frame3f(); int tid = spatial.FindNearestHitTriangle(ray); if (tid == DMesh3.InvalidID) { return(false); } var isect = TriangleIntersection(mesh, tid, ray); if (isect.Result != IntersectionResult.Intersects) { return(false); } Vector3d surfPt = ray.PointAt(isect.RayParameter); if (mesh.HasVertexNormals && bForceFaceNormal == false) { hitPosFrame = SurfaceFrame(mesh, tid, surfPt); // TODO isect has bary-coords already!! } else { hitPosFrame = new Frame3f(surfPt, mesh.GetTriNormal(tid)); } return(true); }
public Vector3d FindNearestAndOffset(Vector3d pos) { int tNearestID = Spatial.FindNearestTriangle(pos); DistPoint3Triangle3 q = MeshQueries.TriangleDistance(Mesh, tNearestID, pos); Vector3d vHitNormal = (UseFaceNormal == false && Mesh.HasVertexNormals) ? Mesh.GetTriBaryNormal(tNearestID, q.TriangleBaryCoords.x, q.TriangleBaryCoords.y, q.TriangleBaryCoords.z) : Mesh.GetTriNormal(tNearestID); return(q.TriangleClosest + Distance * vHitNormal); }
// [TODO] cache this in a dictionary? we will not need very many, but we will // need each multiple times! Vector3d get_vtx_normal(int vid) { Vector3d n = Vector3d.Zero; foreach (int ti in Mesh.VtxTrianglesItr(vid)) { n += Mesh.GetTriNormal(ti); } n.Normalize(); return(n); }
/// <summary> /// Find point-normal(Z) frame at closest point to queryPoint on mesh. /// Returns interpolated vertex-normal frame if available, otherwise tri-normal frame. /// </summary> public static Frame3f NearestPointFrame(DMesh3 mesh, ISpatial spatial, Vector3d queryPoint, bool bForceFaceNormal = false) { int tid = spatial.FindNearestTriangle(queryPoint); Vector3d surfPt = TriangleDistance(mesh, tid, queryPoint).TriangleClosest; if (mesh.HasVertexNormals && bForceFaceNormal == false) { return(SurfaceFrame(mesh, tid, surfPt)); } else { return(new Frame3f(surfPt, mesh.GetTriNormal(tid))); } }
double GetNormalQuality(g3.DMesh3 mesh, g3.Vector3d target, int depth) { if (depth == 0) { return(1 - (mesh.GetTriNormal(meshIndex).Dot(target))); } double amount = GetNormalQuality(mesh, target, 0); foreach (var n in neighbors) { amount += GetNormalQuality(mesh, target, depth - 1); } return(amount); }
/// <summary> /// Count all input mesh face normals /// </summary> public void CountFaceNormals(DMesh3 mesh, bool bWeightByArea = true) { foreach (int tid in mesh.TriangleIndices()) { if (bWeightByArea) { Vector3d n, c; double area; mesh.GetTriInfo(tid, out n, out area, out c); Count(n, area, true); } else { Count(mesh.GetTriNormal(tid), 1.0, true); } } }
void build() { foreach (int tid in Mesh.TriangleIndices()) { double w = (UseAreaWeighting) ? Mesh.GetTriArea(tid) : 1.0; Vector3d n = Mesh.GetTriNormal(tid); Vector3i up = new Vector3i((int)(n.x * IntScale), (int)(n.y * IntScale), (int)(n.z * IntScale)); if (Histogram.ContainsKey(up)) { Histogram[up] += w; } else { Histogram[up] = w; } } }
/// <summary> /// Get point-normal frame on surface of mesh. Assumption is that point lies in tID. /// returns interpolated vertex-normal frame if available, otherwise tri-normal frame. /// </summary> public static Frame3f SurfaceFrame(DMesh3 mesh, int tID, Vector3d point) { if (!mesh.IsTriangle(tID)) { throw new Exception("MeshQueries.SurfaceFrame: triangle " + tID + " does not exist!"); } Triangle3d tri = new Triangle3d(); mesh.GetTriVertices(tID, ref tri.V0, ref tri.V1, ref tri.V2); Vector3d bary = tri.BarycentricCoords(point); point = tri.PointAt(bary); if (mesh.HasVertexNormals) { Vector3d normal = mesh.GetTriBaryNormal(tID, bary.x, bary.y, bary.z); return(new Frame3f(point, normal)); } else { return(new Frame3f(point, mesh.GetTriNormal(tID))); } }
/// <summary> /// Find the set of boundary EdgeLoops. Note that if we encounter topological /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible) /// </summary> public bool Compute() { // This algorithm assumes that triangles are oriented consistently, // so closed boundary-loop can be followed by walking edges in-order Loops = new List <EdgeLoop>(); Spans = new List <EdgeSpan>(); // early-out if we don't actually have boundaries if (Mesh.CachedIsClosed) { return(true); } int NE = Mesh.MaxEdgeID; // Temporary memory used to indicate when we have "used" an edge. BitArray used_edge = new BitArray(NE); used_edge.SetAll(false); // current loop is stored here, cleared after each loop extracted List <int> loop_edges = new List <int>(); // [RMS] not sure we need this... List <int> loop_verts = new List <int>(); List <int> bowties = new List <int>(); // Temp buffer for reading back all boundary edges of a vertex. // probably always small but in pathological cases it could be large... int[] all_e = new int[16]; // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx? // process all edges of mesh for (int eid = 0; eid < NE; ++eid) { if (!Mesh.IsEdge(eid)) { continue; } if (used_edge[eid] == true) { continue; } if (Mesh.IsBoundaryEdge(eid) == false) { continue; } if (EdgeFilterF != null && EdgeFilterF(eid) == false) { used_edge[eid] = true; continue; } // ok this is start of a boundary chain int eStart = eid; used_edge[eStart] = true; loop_edges.Add(eStart); int eCur = eid; // follow the chain in order of oriented edges bool bClosed = false; bool bIsOpenSpan = false; while (!bClosed) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur); int cure_a = ev.a, cure_b = ev.b; if (bIsOpenSpan) { cure_a = ev.b; cure_b = ev.a; } else { loop_verts.Add(cure_a); } int e0 = -1, e1 = 1; int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1); // have to filter this list, if we are filtering. this is ugly. if (EdgeFilterF != null) { if (bdry_nbrs > 2) { if (bdry_nbrs >= all_e.Length) { all_e = new int[bdry_nbrs]; } // we may repreat this below...irritating... int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be); } else { if (EdgeFilterF(e0) == false) { bdry_nbrs--; } if (EdgeFilterF(e1) == false) { bdry_nbrs--; } } } if (bdry_nbrs < 2) // hit an 'endpoint' vertex (should only happen when Filter is on...) { if (SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b) { UnclosedLoop = true } } ; if (bIsOpenSpan) { bClosed = true; continue; } else { bIsOpenSpan = true; // begin open span eCur = loop_edges[0]; // restart at other end of loop loop_edges.Reverse(); // do this so we can push to front continue; } } int eNext = -1; if (bdry_nbrs > 2) { // found "bowtie" vertex...things just got complicated! if (cure_b == loop_verts[0]) { // The "end" of the current edge is the same as the start vertex. // This means we can close the loop here. Might as well! eNext = -2; // sentinel value used below } else { // try to find an unused outgoing edge that is oriented properly. // This could create sub-loops, we will handle those later if (bdry_nbrs >= all_e.Length) { all_e = new int[2 * bdry_nbrs]; } int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); Debug.Assert(num_be == bdry_nbrs); if (EdgeFilterF != null) { num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be); } // Try to pick the best "turn left" vertex. eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge); if (eNext == -1) { if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b) { BowtieFailure = true } } ; // ok, we are stuck. all we can do now is terminate this loop and keep it as a span if (bIsOpenSpan) { bClosed = true; } else { bIsOpenSpan = true; bClosed = true; } continue; } } if (bowties.Contains(cure_b) == false) { bowties.Add(cure_b); } } else { // walk forward to next available edge Debug.Assert(e0 == eCur || e1 == eCur); eNext = (e0 == eCur) ? e1 : e0; } if (eNext == -2) { // found a bowtie vert that is the same as start-of-loop, so we // are just closing it off explicitly bClosed = true; } else if (eNext == eStart) { // found edge at start of loop, so loop is done. bClosed = true; } else if (used_edge[eNext] != false) { // disaster case - the next edge is already used, but it is not the start of our loop // All we can do is convert to open span and terminate if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext) { RepeatedEdge = true } } ; bIsOpenSpan = true; bClosed = true; } else { // push onto accumulated list Debug.Assert(used_edge[eNext] == false); loop_edges.Add(eNext); used_edge[eNext] = true; eCur = eNext; } } if (bIsOpenSpan) { SawOpenSpans = true; if (SpanBehavior == SpanBehaviors.Compute) { loop_edges.Reverse(); // orient properly EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges); Spans.Add(span); } } else if (bowties.Count > 0) { // if we saw a bowtie vertex, we might need to break up this loop, // so call extract_subloops Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties); foreach (var loop in subloops.Loops) { Loops.Add(loop); } if (subloops.Spans.Count > 0) { FellBackToSpansOnFailure = true; foreach (var span in subloops.Spans) { Spans.Add(span); } } } else { // clean simple loop, convert to EdgeLoop instance EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = loop_verts.ToArray(); loop.Edges = loop_edges.ToArray(); Loops.Add(loop); } // reset these lists loop_edges.Clear(); loop_verts.Clear(); bowties.Clear(); } return(true); } // [TODO] cache this in a dictionary? we will not need very many, but we will // need each multiple times! Vector3d get_vtx_normal(int vid) { Vector3d n = Vector3d.Zero; foreach (int ti in Mesh.VtxTrianglesItr(vid)) { n += Mesh.GetTriNormal(ti); } n.Normalize(); return(n); } // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. // // [TODO] handle degenerate edges. what do we do then? Currently will only chose // degenerate edge if there are no other options (I think...) int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } // [RMS] w/ bowtie vertices and open spans, this does happen //Debug.Assert(best_e != -1); return(best_e); }
/// <summary> /// Find the set of boundary EdgeLoops. Note that if we encounter topological /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible) /// </summary> public bool Compute() { // This algorithm assumes that triangles are oriented consistently, // so closed boundary-loop can be followed by walking edges in-order Loops = new List <EdgeLoop>(); Spans = new List <EdgeSpan>(); int NE = Mesh.MaxEdgeID; // Temporary memory used to indicate when we have "used" an edge. BitArray used_edge = new BitArray(NE); used_edge.SetAll(false); // current loop is stored here, cleared after each loop extracted List <int> loop_edges = new List <int>(); // [RMS] not sure we need this... List <int> loop_verts = new List <int>(); List <int> bowties = new List <int>(); // Temp buffer for reading back all boundary edges of a vertex. // probably always small but in pathological cases it could be large... int[] all_e = new int[16]; // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx? // process all edges of mesh for (int eid = 0; eid < NE; ++eid) { if (!Mesh.IsEdge(eid)) { continue; } if (used_edge[eid] == true) { continue; } if (Mesh.IsBoundaryEdge(eid) == false) { continue; } if (EdgeFilterF != null && EdgeFilterF(eid) == false) { used_edge[eid] = true; continue; } // ok this is start of a boundary chain int eStart = eid; used_edge[eStart] = true; loop_edges.Add(eStart); int eCur = eid; // follow the chain in order of oriented edges bool bClosed = false; bool bIsOpenSpan = false; while (!bClosed) { Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur); int cure_a = ev.a, cure_b = ev.b; if (bIsOpenSpan) { cure_a = ev.b; cure_b = ev.a; } else { loop_verts.Add(cure_a); } int e0 = -1, e1 = 1; int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1); // have to filter this list, if we are filtering. this is ugly. if (EdgeFilterF != null) { if (bdry_nbrs > 2) { if (bdry_nbrs >= all_e.Length) { all_e = new int[bdry_nbrs]; } // we may repreat this below...irritating... int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be); } else { if (EdgeFilterF(e0) == false) { bdry_nbrs--; } if (EdgeFilterF(e1) == false) { bdry_nbrs--; } } } if (bdry_nbrs < 2) // hit an 'endpoint' vertex (should only happen when Filter is on...) { if (SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b) { UnclosedLoop = true } } ; if (bIsOpenSpan) { bClosed = true; continue; } else { bIsOpenSpan = true; // begin open span eCur = loop_edges[0]; // restart at other end of loop loop_edges.Reverse(); // do this so we can push to front continue; } } int eNext = -1; if (bdry_nbrs > 2) { // found "bowtie" vertex...things just got complicated! if (cure_b == loop_verts[0]) { // The "end" of the current edge is the same as the start vertex. // This means we can close the loop here. Might as well! eNext = -2; // sentinel value used below } else { // try to find an unused outgoing edge that is oriented properly. // This could create sub-loops, we will handle those later if (bdry_nbrs >= all_e.Length) { all_e = new int[2 * bdry_nbrs]; } int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e); Debug.Assert(num_be == bdry_nbrs); if (EdgeFilterF != null) { num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be); } // Try to pick the best "turn left" vertex. eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge); if (eNext == -1) { if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b) { BowtieFailure = true } } ; // ok, we are stuck. all we can do now is terminate this loop and keep it as a span if (bIsOpenSpan) { bClosed = true; } else { bIsOpenSpan = true; bClosed = true; } continue; } } if (bowties.Contains(cure_b) == false) { bowties.Add(cure_b); } } else { // walk forward to next available edge Debug.Assert(e0 == eCur || e1 == eCur); eNext = (e0 == eCur) ? e1 : e0; } if (eNext == -2) { // found a bowtie vert that is the same as start-of-loop, so we // are just closing it off explicitly bClosed = true; } else if (eNext == eStart) { // found edge at start of loop, so loop is done. bClosed = true; } else if (used_edge[eNext] != false) { // disaster case - the next edge is already used, but it is not the start of our loop // All we can do is convert to open span and terminate if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext) { RepeatedEdge = true } } ; bIsOpenSpan = true; bClosed = true; } else { // push onto accumulated list Debug.Assert(used_edge[eNext] == false); loop_edges.Add(eNext); used_edge[eNext] = true; eCur = eNext; } } if (bIsOpenSpan) { SawOpenSpans = true; if (SpanBehavior == SpanBehaviors.Compute) { loop_edges.Reverse(); // orient properly EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges); Spans.Add(span); } } else if (bowties.Count > 0) { // if we saw a bowtie vertex, we might need to break up this loop, // so call extract_subloops List <EdgeLoop> subloops = extract_subloops(loop_verts, loop_edges, bowties); for (int i = 0; i < subloops.Count; ++i) { Loops.Add(subloops[i]); } } else { // clean simple loop, convert to EdgeLoop instance EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = loop_verts.ToArray(); loop.Edges = loop_edges.ToArray(); Loops.Add(loop); } // reset these lists loop_edges.Clear(); loop_verts.Clear(); bowties.Clear(); } return(true); } // [TODO] cache this in a dictionary? we will not need very many, but we will // need each multiple times! Vector3d get_vtx_normal(int vid) { Vector3d n = Vector3d.Zero; foreach (int ti in Mesh.VtxTrianglesItr(vid)) { n += Mesh.GetTriNormal(ti); } n.Normalize(); return(n); } // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v. // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e. // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v. // So, we compute the tangent plane at bowtie_v, and then the signed angle for each // viable edge in this plane. // // [TODO] handle degenerate edges. what do we do then? Currently will only chose // degenerate edge if there are no other options (I think...) int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges) { // compute normal and edge [a,bowtie] Vector3d n = get_vtx_normal(bowtie_v); int other_v = Mesh.edge_other_v(incoming_e, bowtie_v); Vector3d ab = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v); // our winner int best_e = -1; double best_angle = double.MaxValue; for (int i = 0; i < bdry_edges_count; ++i) { int bdry_eid = bdry_edges[i]; if (used_edges[bdry_eid] == true) { continue; // this edge is already used } Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid); if (bdry_ev.a != bowtie_v) { continue; // have to be able to chain to end of current edge, orientation-wise } // compute projected angle Vector3d bc = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v); float fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n); // turn left! if (best_angle == double.MaxValue || fAngleS < best_angle) { best_angle = fAngleS; best_e = bdry_eid; } } // [RMS] w/ bowtie vertices and open spans, this does happen //Debug.Assert(best_e != -1); return(best_e); } // This is called when loopV contains one or more "bowtie" vertices. // These vertices *might* be duplicated in loopV (but not necessarily) // If they are, we have to break loopV into subloops that don't contain duplicates. // // The list bowties contains all the possible duplicates // (all v in bowties occur in loopV at least once) // // Currently loopE is not used, and the returned EdgeLoop objects do not have their Edges // arrays initialized. Perhaps to improve in future. List <EdgeLoop> extract_subloops(List <int> loopV, List <int> loopE, List <int> bowties) { List <EdgeLoop> subs = new List <EdgeLoop>(); // figure out which bowties we saw are actually duplicated in loopV List <int> dupes = new List <int>(); foreach (int bv in bowties) { if (count_in_list(loopV, bv) > 1) { dupes.Add(bv); } } // we might not actually have any duplicates, if we got luck. Early out in that case if (dupes.Count == 0) { subs.Add(new EdgeLoop(Mesh) { Vertices = loopV.ToArray(), Edges = loopE.ToArray(), BowtieVertices = bowties.ToArray() }); return(subs); } // This loop extracts subloops until we have dealt with all the // duplicate vertices in loopV while (dupes.Count > 0) { // Find shortest "simple" loop, ie a loop from a bowtie to itself that // does not contain any other bowties. This is an independent loop. // We're doing a lot of extra work here if we only have one element in dupes... int bi = 0, bv = 0; int start_i = -1, end_i = -1; int bv_shortest = -1; int shortest = int.MaxValue; for ( ; bi < dupes.Count; ++bi) { bv = dupes[bi]; if (is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i)) { int len = count_span(loopV, start_i, end_i); if (len < shortest) { bv_shortest = bv; shortest = len; } } } if (bv_shortest == -1) { throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: Cannot find a valid simple loop"); } if (bv != bv_shortest) { bv = bv_shortest; // running again just to get start_i and end_i... is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i); } Debug.Assert(loopV[start_i] == bv && loopV[end_i] == bv); EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = extract_span(loopV, start_i, end_i, true); loop.Edges = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices); loop.BowtieVertices = bowties.ToArray(); subs.Add(loop); // If there are no more duplicates of this bowtie, we can treat // it like a regular vertex now if (count_in_list(loopV, bv) < 2) { dupes.Remove(bv); } } // Should have one loop left that contains duplicates. // Extract this as a separate loop int nLeft = 0; for (int i = 0; i < loopV.Count; ++i) { if (loopV[i] != -1) { nLeft++; } } if (nLeft > 0) { EdgeLoop loop = new EdgeLoop(Mesh); loop.Vertices = new int[nLeft]; int vi = 0; for (int i = 0; i < loopV.Count; ++i) { if (loopV[i] != -1) { loop.Vertices[vi++] = loopV[i]; } } loop.Edges = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices); loop.BowtieVertices = bowties.ToArray(); subs.Add(loop); } return(subs); } /* * In all the functions below, the list loopV is assumed to possibly * contain "removed" vertices indicated by -1. These are ignored. */ // Check if the loop from bowtieV to bowtieV inside loopV contains any other bowtie verts. // Also returns start and end indices in loopV of "clean" loop // Note that start may be < end, if the "clean" loop wraps around the end bool is_simple_bowtie_loop(List <int> loopV, List <int> bowties, int bowtieV, out int start_i, out int end_i) { // find two indices of bowtie vert start_i = find_index(loopV, 0, bowtieV); end_i = find_index(loopV, start_i + 1, bowtieV); if (is_simple_path(loopV, bowties, bowtieV, start_i, end_i)) { return(true); } else if (is_simple_path(loopV, bowties, bowtieV, end_i, start_i)) { int tmp = start_i; start_i = end_i; end_i = tmp; return(true); } else { return(false); // not a simple bowtie loop! } } // check if forward path from loopV[i1] to loopV[i2] contains any bowtie verts other than bowtieV bool is_simple_path(List <int> loopV, List <int> bowties, int bowtieV, int i1, int i2) { int N = loopV.Count; for (int i = i1; i != i2; i = (i + 1) % N) { int vi = loopV[i]; if (vi == -1) { continue; // skip removed vertices } if (vi != bowtieV && bowties.Contains(vi)) { return(false); } } return(true); } // Read out the span from loop[i0] to loop [i1-1] into an array. // If bMarkInvalid, then these values are set to -1 in loop int[] extract_span(List <int> loop, int i0, int i1, bool bMarkInvalid) { int num = count_span(loop, i0, i1); int[] a = new int[num]; int ai = 0; int N = loop.Count; for (int i = i0; i != i1; i = (i + 1) % N) { if (loop[i] != -1) { a[ai++] = loop[i]; if (bMarkInvalid) { loop[i] = -1; } } } return(a); } // count number of valid vertices in l between loop[i0] and loop[i1-1] int count_span(List <int> l, int i0, int i1) { int c = 0; int N = l.Count; for (int i = i0; i != i1; i = (i + 1) % N) { if (l[i] != -1) { c++; } } return(c); } // find the index of item in loop, starting at start index int find_index(List <int> loop, int start, int item) { for (int i = start; i < loop.Count; ++i) { if (loop[i] == item) { return(i); } } return(-1); } // count number of times item appears in loop int count_in_list(List <int> loop, int item) { int c = 0; for (int i = 0; i < loop.Count; ++i) { if (loop[i] == item) { c++; } } return(c); } } }
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); }
//public static void VoronoiMesh(List<g3.PolyLine3d> mesh, out List<g3.Line3d> listLines, out List<g3.PolyLine3d> listPolylines) //{ // System.Collections.Generic.SortedDictionary<int, MeshNode> faces = new System.Collections.Generic.SortedDictionary<int, MeshNode>(); // int index = 0; // foreach (var meshFaceIndex in mesh.TriangleIndices()) // { // var frame = mesh.GetTriFrame(meshFaceIndex); // g3.Index3i neighbors = mesh.GetTriNeighbourTris(meshFaceIndex); // g3.Index3i vertex_index = mesh.GetTriangle(meshFaceIndex); // faces.Add(meshFaceIndex, new MeshNode(index++, meshFaceIndex, frame, neighbors, vertex_index)); // } // foreach (var f in faces) // { // f.Value.neighbors.Clear(); // f.Value.neighbors.Capacity = 3; // for (int i = 0; i < 3; ++i) // { // int fn = f.Value.neighbors_index[i]; // if (fn >= 0) // f.Value.neighbors.Add(faces[fn]); // } // if (f.Value.neighbors.Count < 3) // { // f.Value.locked = true; // foreach (var n in f.Value.neighbors) // n.locked = true; // } // } // outputMesh = new g3.DMesh3(g3.MeshComponents.None); // listLines = new List<g3.Line3d>(); // listPolylines = new List<g3.PolyLine3d>(); // foreach (var f in faces) // { // outputMesh.AppendVertex(f.Value.frame.Origin); // } // HashSet<int> processedPoints = new HashSet<int>(); // foreach (var f in faces) // { // for (int i = 0; i < 3; i++) // { // List<int> outputLine = new List<int>(); // if (processedPoints.Contains(f.Value.vertex_index[i])) // continue; // int checkVertex = f.Value.vertex_index[i]; // MeshNode currentFaces = f.Value; // MeshNode prevFace = null; // bool fullLoop = false; // while (true) // { // for (int j = 0; j < currentFaces.neighbors.Count; j++) // { // var neighbor = currentFaces.neighbors[j]; // if (neighbor.UsesVertex(checkVertex)) // { // if (neighbor == prevFace) // continue; // if (neighbor == f.Value) // { // fullLoop = true; // break; // Found full loop // } // outputLine.Add(neighbor.index); // prevFace = currentFaces; // currentFaces = neighbor; // j = -1; // } // } // break; // } // if (fullLoop) // { // processedPoints.Add(checkVertex); // var polyline = new g3.PolyLine3d(); // if (outputLine.Count > 2) // { // g3.Vector3d centerPoint = f.Value.frame.Origin; // foreach (var p in outputLine) // centerPoint += outputMesh.GetVertex(p); // centerPoint /= (outputLine.Count + 1); // int center = outputMesh.AppendVertex(centerPoint); // var pS = outputMesh.GetVertex(f.Value.index); // var p0 = outputMesh.GetVertex(outputLine[0]); // var pE = outputMesh.GetVertex(outputLine[outputLine.Count - 1]); // var normal = mesh.GetTriNormal(f.Value.meshIndex); // polyline.AppendVertex(pS); // polyline.AppendVertex(p0); // listLines.Add(new g3.Line3d(pS, p0 - pS)); // var n = MathUtil.Normal(centerPoint, pS, p0); // bool reverseTri = n.Dot(normal) < 0; // if (!reverseTri) // outputMesh.AppendTriangle(center, f.Value.index, outputLine[0]); // else // outputMesh.AppendTriangle(center, outputLine[0], f.Value.index); // for (int j = 0; j < outputLine.Count - 1; j++) // { // var p1 = outputMesh.GetVertex(outputLine[j]); // var p2 = outputMesh.GetVertex(outputLine[j + 1]); // listLines.Add(new g3.Line3d(p1, p2 - p1)); // polyline.AppendVertex(p2); // if (!reverseTri) // outputMesh.AppendTriangle(center, outputLine[j], outputLine[j + 1]); // else // outputMesh.AppendTriangle(center, outputLine[j + 1], outputLine[j]); // } // polyline.AppendVertex(pS); // listLines.Add(new g3.Line3d(pE, pS - pE)); // listPolylines.Add(polyline); // if (!reverseTri) // outputMesh.AppendTriangle(center, outputLine[outputLine.Count - 1], f.Value.index); // else // outputMesh.AppendTriangle(center, f.Value.index, outputLine[outputLine.Count - 1]); // } // } // } // } //} public static void VoronoiMesh(g3.DMesh3 mesh, out g3.DMesh3 outputMesh, out List <g3.Line3d> listLines, out List <g3.PolyLine3d> listPolylines) { System.Collections.Generic.SortedDictionary <int, MeshNode> faces = new System.Collections.Generic.SortedDictionary <int, MeshNode>(); int index = 0; foreach (var meshFaceIndex in mesh.TriangleIndices()) { var frame = mesh.GetTriFrame(meshFaceIndex); g3.Index3i neighbors = mesh.GetTriNeighbourTris(meshFaceIndex); g3.Index3i vertex_index = mesh.GetTriangle(meshFaceIndex); faces.Add(meshFaceIndex, new MeshNode(index++, meshFaceIndex, frame, neighbors, vertex_index)); } foreach (var f in faces) { f.Value.neighbors.Clear(); f.Value.neighbors.Capacity = 3; for (int i = 0; i < 3; ++i) { int fn = f.Value.neighbors_index[i]; if (fn >= 0) { f.Value.neighbors.Add(faces[fn]); } } if (f.Value.neighbors.Count < 3) { f.Value.locked = true; foreach (var n in f.Value.neighbors) { n.locked = true; } } } outputMesh = new g3.DMesh3(g3.MeshComponents.None); listLines = new List <g3.Line3d>(); listPolylines = new List <g3.PolyLine3d>(); foreach (var f in faces) { outputMesh.AppendVertex(f.Value.frame.Origin); } HashSet <int> processedPoints = new HashSet <int>(); foreach (var f in faces) { for (int i = 0; i < 3; i++) { List <int> outputLine = new List <int>(); if (processedPoints.Contains(f.Value.vertex_index[i])) { continue; } int checkVertex = f.Value.vertex_index[i]; MeshNode currentFaces = f.Value; MeshNode prevFace = null; bool fullLoop = false; while (true) { for (int j = 0; j < currentFaces.neighbors.Count; j++) { var neighbor = currentFaces.neighbors[j]; if (neighbor.UsesVertex(checkVertex)) { if (neighbor == prevFace) { continue; } if (neighbor == f.Value) { fullLoop = true; break; // Found full loop } outputLine.Add(neighbor.index); prevFace = currentFaces; currentFaces = neighbor; j = -1; } } break; } if (fullLoop) { processedPoints.Add(checkVertex); var polyline = new g3.PolyLine3d(); if (outputLine.Count > 2) { g3.Vector3d centerPoint = f.Value.frame.Origin; foreach (var p in outputLine) { centerPoint += outputMesh.GetVertex(p); } centerPoint /= (outputLine.Count + 1); int center = outputMesh.AppendVertex(centerPoint); var pS = outputMesh.GetVertex(f.Value.index); var p0 = outputMesh.GetVertex(outputLine[0]); var pE = outputMesh.GetVertex(outputLine[outputLine.Count - 1]); var normal = mesh.GetTriNormal(f.Value.meshIndex); polyline.AppendVertex(pS); polyline.AppendVertex(p0); listLines.Add(new g3.Line3d(pS, p0 - pS)); var n = MathUtil.Normal(centerPoint, pS, p0); bool reverseTri = n.Dot(normal) < 0; if (!reverseTri) { outputMesh.AppendTriangle(center, f.Value.index, outputLine[0]); } else { outputMesh.AppendTriangle(center, outputLine[0], f.Value.index); } for (int j = 0; j < outputLine.Count - 1; j++) { var p1 = outputMesh.GetVertex(outputLine[j]); var p2 = outputMesh.GetVertex(outputLine[j + 1]); listLines.Add(new g3.Line3d(p1, p2 - p1)); polyline.AppendVertex(p2); if (!reverseTri) { outputMesh.AppendTriangle(center, outputLine[j], outputLine[j + 1]); } else { outputMesh.AppendTriangle(center, outputLine[j + 1], outputLine[j]); } } polyline.AppendVertex(pS); listLines.Add(new g3.Line3d(pE, pS - pE)); listPolylines.Add(polyline); if (!reverseTri) { outputMesh.AppendTriangle(center, outputLine[outputLine.Count - 1], f.Value.index); } else { outputMesh.AppendTriangle(center, f.Value.index, outputLine[outputLine.Count - 1]); } } } } } }
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); } } }