/// <summary> /// precompute constant coefficients of triangle winding number approximation /// p: 'center' of expansion for triangles (area-weighted centroid avg) /// r: max distance from p to triangles /// order1: first-order vector coeff /// order2: second-order matrix coeff /// triCache: optional precomputed triangle centroid/normal/area /// </summary> public static void ComputeCoeffs(DMesh3 mesh, IEnumerable <int> triangles, ref Vector3d p, ref double r, ref Vector3d order1, ref Matrix3d order2, MeshTriInfoCache triCache = null) { p = Vector3d.Zero; order1 = Vector3d.Zero; order2 = Matrix3d.Zero; r = 0; // compute area-weighted centroid of triangles, we use this as the expansion point Vector3d P0 = Vector3d.Zero, P1 = Vector3d.Zero, P2 = Vector3d.Zero; double sum_area = 0; foreach (int tid in triangles) { if (triCache != null) { double area = triCache.Areas[tid]; sum_area += area; p += area * triCache.Centroids[tid]; } else { mesh.GetTriVertices(tid, ref P0, ref P1, ref P2); double area = MathUtil.Area(ref P0, ref P1, ref P2); sum_area += area; p += area * ((P0 + P1 + P2) / 3.0); } } p /= sum_area; // compute first and second-order coefficients of FWN taylor expansion, as well as // 'radius' value r, which is max dist from any tri vertex to p Vector3d n = Vector3d.Zero, c = Vector3d.Zero; double a = 0; foreach (int tid in triangles) { mesh.GetTriVertices(tid, ref P0, ref P1, ref P2); if (triCache == null) { c = (1.0 / 3.0) * (P0 + P1 + P2); n = MathUtil.FastNormalArea(ref P0, ref P1, ref P2, out a); } else { triCache.GetTriInfo(tid, ref n, ref a, ref c); } order1 += a * n; Vector3d dcp = c - p; order2 += a * new Matrix3d(ref dcp, ref n); // this is just for return value... double maxdist = MathUtil.Max(P0.DistanceSquared(ref p), P1.DistanceSquared(ref p), P2.DistanceSquared(ref p)); r = Math.Max(r, Math.Sqrt(maxdist)); } }
/// <summary> /// Brute-force search for closest pair of triangles on two meshes /// </summary> public static Index2i FindNearestTriangles_LinearSearch(DMesh3 mesh1, DMesh3 mesh2, out double fNearestSqr) { Index2i nearPair = Index2i.Max; fNearestSqr = double.MaxValue; foreach (int ti in mesh1.TriangleIndices()) { Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; mesh1.GetTriVertices(ti, ref a, ref b, ref c); foreach (int tj in mesh2.TriangleIndices()) { Vector3d e = Vector3d.Zero, f = Vector3d.Zero, g = Vector3d.Zero; mesh2.GetTriVertices(tj, ref e, ref f, ref g); var dist = new DistTriangle3Triangle3(new Triangle3d(a, b, c), new Triangle3d(e, f, g)); if (dist.GetSquared() < fNearestSqr) { fNearestSqr = dist.GetSquared(); nearPair = new Index2i(ti, tj); } } } fNearestSqr = Math.Sqrt(fNearestSqr); return(nearPair); }
internal double GetTriangleAnglesQuality(g3.DMesh3 mesh) { Vector3d v0 = Vector3d.Zero, v1 = Vector3d.Zero, v2 = Vector3d.Zero; mesh.GetTriVertices(meshIndex, ref v0, ref v1, ref v2); Vector3d anglesD = Vector3d.Zero; Vector3d e00 = (v1 - v0); e00.Normalize(); Vector3d e01 = (v2 - v0); e01.Normalize(); anglesD.x = Vector3d.AngleD(e00, e01); Vector3d e10 = (v0 - v1); e10.Normalize(); Vector3d e11 = (v2 - v1); e11.Normalize(); anglesD.y = Vector3d.AngleD(e10, e11); anglesD.z = 180 - anglesD.x - anglesD.y; double resultA = Math.Min(Math.Min(Math.Abs(anglesD.x - 90) / 10.0, Math.Abs(anglesD.y - 90) / 10.0), Math.Abs(anglesD.z - 90) / 10.0); double resultB = Math.Abs(anglesD.x - 60) / 30.0 + Math.Abs(anglesD.y - 60) / 30.0 + Math.Abs(anglesD.z - 60) / 30.0; double result = Math.Min(resultA, resultB); return(Math.Pow(result, 3)); }
/// <summary> /// Get barycentric coords of point in triangle /// </summary> public static Vector3d BaryCoords(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); return(tri.BarycentricCoords(point)); }
/// <summary> /// convenience function to construct a IntrRay3Triangle3 object for a mesh triangle /// </summary> public static IntrRay3Triangle3 TriangleIntersection(DMesh3 mesh, int ti, Ray3d ray) { if (!mesh.IsTriangle(ti)) { return(null); } Triangle3d tri = new Triangle3d(); mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2); IntrRay3Triangle3 q = new IntrRay3Triangle3(ray, tri); q.Find(); return(q); }
/// <summary> /// construct a DistPoint3Triangle3 object for a mesh triangle /// </summary> public static DistPoint3Triangle3 TriangleDistance(DMesh3 mesh, int ti, Vector3d point) { if (!mesh.IsTriangle(ti)) { return(null); } Triangle3d tri = new Triangle3d(); mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2); DistPoint3Triangle3 q = new DistPoint3Triangle3(point, tri); q.GetSquared(); return(q); }
/// <summary> /// Find distance from point to mesh /// Returns interpolated vertex-normal frame if available, otherwise tri-normal frame. /// </summary> public static double NearestPointDistance(DMesh3 mesh, ISpatial spatial, Vector3d queryPoint, double maxDist = double.MaxValue) { int tid = spatial.FindNearestTriangle(queryPoint, maxDist); if (tid == DMesh3.InvalidID) { return(double.MaxValue); } Triangle3d tri = new Triangle3d(); mesh.GetTriVertices(tid, ref tri.V0, ref tri.V1, ref tri.V2); Vector3d closest, bary; double dist_sqr = DistPoint3Triangle3.DistanceSqr(ref queryPoint, ref tri, out closest, out bary); return(Math.Sqrt(dist_sqr)); }
/// <summary> /// check if edge collapse will create a face-normal flip. /// Also checks if collapse would violate link condition, since /// we are iterating over one-ring anyway. /// /// This only checks one-ring of vid, so you have to call it twice, /// with vid and vother reversed, to check both one-rings /// </summary> protected bool collapse_creates_flip_or_invalid(int vid, int vother, ref Vector3d newv, int tc, int td) { Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in mesh.VtxTrianglesItr(vid)) { if (tid == tc || tid == td) { continue; } Index3i curt = mesh.GetTriangle(tid); if (curt.a == vother || curt.b == vother || curt.c == vother) { return(true); // invalid nbrhood for collapse } mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d ncur = (vb - va).Cross(vc - va); double sign = 0; if (curt.a == vid) { Vector3d nnew = (vb - newv).Cross(vc - newv); sign = edge_flip_metric(ref ncur, ref nnew); } else if (curt.b == vid) { Vector3d nnew = (newv - va).Cross(vc - va); sign = edge_flip_metric(ref ncur, ref nnew); } else if (curt.c == vid) { Vector3d nnew = (vb - va).Cross(newv - va); sign = edge_flip_metric(ref ncur, ref nnew); } else { throw new Exception("should never be here!"); } if (sign <= edge_flip_tol) { return(true); } } return(false); }
/// <summary> /// brute force search for any intersecting triangles on two meshes /// </summary> public static Index2i FindIntersectingTriangles_LinearSearch(DMesh3 mesh1, DMesh3 mesh2) { foreach (int ti in mesh1.TriangleIndices()) { Vector3d a = Vector3d.Zero, b = Vector3d.Zero, c = Vector3d.Zero; mesh1.GetTriVertices(ti, ref a, ref b, ref c); foreach (int tj in mesh2.TriangleIndices()) { Vector3d e = Vector3d.Zero, f = Vector3d.Zero, g = Vector3d.Zero; mesh2.GetTriVertices(tj, ref e, ref f, ref g); var intr = new IntrTriangle3Triangle3(new Triangle3d(a, b, c), new Triangle3d(e, f, g)); if (intr.Test()) { return(new Index2i(ti, tj)); } } } return(Index2i.Max); }
/// <summary> /// convenience function to construct a IntrTriangle3Triangle3 object for two mesh triangles /// </summary> public static IntrTriangle3Triangle3 TrianglesIntersection(DMesh3 mesh1, int ti, DMesh3 mesh2, int tj, Func <Vector3d, Vector3d> TransformF = null) { if (mesh1.IsTriangle(ti) == false || mesh2.IsTriangle(tj) == false) { return(null); } Triangle3d tri1 = new Triangle3d(), tri2 = new Triangle3d(); mesh1.GetTriVertices(ti, ref tri1.V0, ref tri1.V1, ref tri1.V2); mesh2.GetTriVertices(tj, ref tri2.V0, ref tri2.V1, ref tri2.V2); if (TransformF != null) { tri2.V0 = TransformF(tri2.V0); tri2.V1 = TransformF(tri2.V1); tri2.V2 = TransformF(tri2.V2); } IntrTriangle3Triangle3 intr = new IntrTriangle3Triangle3(tri1, tri2); intr.Find(); return(intr); }
/// <summary> /// convenience function to construct a DistTriangle3Triangle3 object for two mesh triangles /// </summary> public static DistTriangle3Triangle3 TrianglesDistance(DMesh3 mesh1, int ti, DMesh3 mesh2, int tj, Func <Vector3d, Vector3d> TransformF = null) { if (mesh1.IsTriangle(ti) == false || mesh2.IsTriangle(tj) == false) { return(null); } Triangle3d tri1 = new Triangle3d(), tri2 = new Triangle3d(); mesh1.GetTriVertices(ti, ref tri1.V0, ref tri1.V1, ref tri1.V2); mesh2.GetTriVertices(tj, ref tri2.V0, ref tri2.V1, ref tri2.V2); if (TransformF != null) { tri2.V0 = TransformF(tri2.V0); tri2.V1 = TransformF(tri2.V1); tri2.V2 = TransformF(tri2.V2); } DistTriangle3Triangle3 dist = new DistTriangle3Triangle3(tri1, tri2); dist.GetSquared(); return(dist); }
/// <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))); } }
public static int FindHitTriangle_LinearSearch(DMesh3 mesh, Ray3d ray) { int tNearestID = DMesh3.InvalidID; double fNearestT = double.MaxValue; var tri = new Triangle3d(); foreach (int ti in mesh.TriangleIndices()) { // [TODO] optimize this mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2); var ray_tri_hit = new IntrRay3Triangle3(ray, tri); if (ray_tri_hit.Find()) { if (ray_tri_hit.RayParameter < fNearestT) { fNearestT = ray_tri_hit.RayParameter; tNearestID = ti; } } } return(tNearestID); }
protected void compute_full(IEnumerable <int> Triangles, bool bIsFullMeshHint = false) { Graph = new DGraph3(); if (WantGraphEdgeInfo) { GraphEdges = new DVector <GraphEdgeInfo>(); } Vertices = new Dictionary <Vector3d, int>(); // multithreaded precomputation of per-vertex values double[] vertex_values = null; if (PrecomputeVertexValues) { vertex_values = new double[Mesh.MaxVertexID]; IEnumerable <int> verts = Mesh.VertexIndices(); if (bIsFullMeshHint == false) { MeshVertexSelection vertices = new MeshVertexSelection(Mesh); vertices.SelectTriangleVertices(Triangles); verts = vertices; } gParallel.ForEach(verts, (vid) => { vertex_values[vid] = ValueF(Mesh.GetVertex(vid)); }); VertexValueF = (vid) => { return(vertex_values[vid]); }; } foreach (int tid in Triangles) { Vector3dTuple3 tv = new Vector3dTuple3(); Mesh.GetTriVertices(tid, ref tv.V0, ref tv.V1, ref tv.V2); Index3i triVerts = Mesh.GetTriangle(tid); Vector3d f = (VertexValueF != null) ? new Vector3d(VertexValueF(triVerts.a), VertexValueF(triVerts.b), VertexValueF(triVerts.c)) : new Vector3d(ValueF(tv.V0), ValueF(tv.V1), ValueF(tv.V2)); // round f to 0 within epsilon? if (f.x < 0 && f.y < 0 && f.z < 0) { continue; } if (f.x > 0 && f.y > 0 && f.z > 0) { continue; } Index3i triEdges = Mesh.GetTriEdges(tid); if (f.x * f.y * f.z == 0) { int z0 = (f.x == 0) ? 0 : ((f.y == 0) ? 1 : 2); int i1 = (z0 + 1) % 3, i2 = (z0 + 2) % 3; if (f[i1] * f[i2] > 0) { continue; // single-vertex-crossing case, skip here and let other edges catch it } if (f[i1] == 0 || f[i2] == 0) { // on-edge case int z1 = f[i1] == 0 ? i1 : i2; if ((z0 + 1) % 3 != z1) { int tmp = z0; z0 = z1; z1 = tmp; // catch reverse-orientation cases } int e0 = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int e1 = add_or_append_vertex(Mesh.GetVertex(triVerts[z1])); int graph_eid = Graph.AppendEdge(e0, e1, (int)TriangleCase.OnEdge); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_on_edge(graph_eid, tid, triEdges[z0], new Index2i(e0, e1)); } } else { // edge/vertex case Util.gDevAssert(f[i1] * f[i2] < 0); int vert_vid = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int i = i1, j = i2; if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); int cross_vid = add_or_append_vertex(cross); add_edge_pos(triVerts[i], triVerts[j], cross); int graph_eid = Graph.AppendEdge(vert_vid, cross_vid, (int)TriangleCase.EdgeVertex); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_edge_vert(graph_eid, tid, triEdges[(z0 + 1) % 3], triVerts[z0], new Index2i(vert_vid, cross_vid)); } } } else { Index3i cross_verts = Index3i.Min; int less_than = 0; for (int tei = 0; tei < 3; ++tei) { int i = tei, j = (tei + 1) % 3; if (f[i] < 0) { less_than++; } if (f[i] * f[j] > 0) { continue; } if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); cross_verts[tei] = add_or_append_vertex(cross); add_edge_pos(triVerts[i], triVerts[j], cross); } int e0 = (cross_verts.a == int.MinValue) ? 1 : 0; int e1 = (cross_verts.c == int.MinValue) ? 1 : 2; if (e0 == 0 && e1 == 2) // preserve orientation order { e0 = 2; e1 = 0; } // preserving orientation does not mean we get a *consistent* orientation across faces. // To do that, we need to assign "sides". Either we have 1 less-than-0 or 1 greater-than-0 vtx. // Arbitrary decide that we want loops oriented like bdry loops would be if we discarded less-than side. // In that case, when we are "cutting off" one vertex, orientation would end up flipped if (less_than == 1) { int tmp = e0; e0 = e1; e1 = tmp; } int ev0 = cross_verts[e0]; int ev1 = cross_verts[e1]; // [RMS] if function is garbage, we can end up w/ case where both crossings // happen at same vertex, even though values are not the same (eg if // some values are double.MaxValue). We will just fail in these cases. if (ev0 != ev1) { Util.gDevAssert(ev0 != int.MinValue && ev1 != int.MinValue); int graph_eid = Graph.AppendEdge(ev0, ev1, (int)TriangleCase.EdgeEdge); if (graph_eid >= 0 && WantGraphEdgeInfo) { add_edge_edge(graph_eid, tid, new Index2i(triEdges[e0], triEdges[e1]), new Index2i(ev0, ev1)); } } } } Vertices = null; }
void find_hit_triangle(int iBox, ref Ray3d ray, ref double fNearestT, ref int tID) { int idx = box_to_index[iBox]; if (idx < triangles_end) // triange-list case, array is [N t1 t2 ... tN] { Triangle3d tri = new Triangle3d(); int num_tris = index_list[idx]; for (int i = 1; i <= num_tris; ++i) { int ti = index_list[idx + i]; // [TODO] optimize this mesh.GetTriVertices(ti, ref tri.V0, ref tri.V1, ref tri.V2); IntrRay3Triangle3 ray_tri_hit = new IntrRay3Triangle3(ray, tri); if (ray_tri_hit.Find()) { if (ray_tri_hit.RayParameter < fNearestT) { fNearestT = ray_tri_hit.RayParameter; tID = ti; } } } } else // internal node, either 1 or 2 child boxes { double e = MathUtil.ZeroTolerancef; int iChild1 = index_list[idx]; if (iChild1 < 0) // 1 child, descend if nearer than cur min-dist { iChild1 = (-iChild1) - 1; double fChild1T = box_ray_intersect_t(iChild1, ray); if (fChild1T <= fNearestT + e) { find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID); } } else // 2 children, descend closest first { iChild1 = iChild1 - 1; int iChild2 = index_list[idx + 1] - 1; double fChild1T = box_ray_intersect_t(iChild1, ray); double fChild2T = box_ray_intersect_t(iChild2, ray); if (fChild1T < fChild2T) { if (fChild1T <= fNearestT + e) { find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID); if (fChild2T <= fNearestT + e) { find_hit_triangle(iChild2, ref ray, ref fNearestT, ref tID); } } } else { if (fChild2T <= fNearestT + e) { find_hit_triangle(iChild2, ref ray, ref fNearestT, ref tID); if (fChild1T <= fNearestT + e) { find_hit_triangle(iChild1, ref ray, ref fNearestT, ref tID); } } } } } }
void make_level_set3(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f distances, int exact_band) { distances.resize(ni, nj, nk); distances.assign((ni + nj + nk) * dx); // upper bound on distance // closest triangle id for each grid cell DenseGrid3i closest_tri = new DenseGrid3i(ni, nj, nk, -1); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} DenseGrid3i intersection_count = new DenseGrid3i(ni, nj, nk, 0); if (DebugPrint) { System.Console.WriteLine("start"); } // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d xp = Vector3d.Zero, xq = Vector3d.Zero, xr = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && CancelF()) { break; } Mesh.GetTriVertices(tid, ref xp, ref xq, ref xr); // real ijk coordinates of xp/xq/xr double fip = (xp[0] - ox) / ddx, fjp = (xp[1] - oy) / ddx, fkp = (xp[2] - oz) / ddx; double fiq = (xq[0] - ox) / ddx, fjq = (xq[1] - oy) / ddx, fkq = (xq[2] - oz) / ddx; double fir = (xr[0] - ox) / ddx, fjr = (xr[1] - oy) / ddx, fkr = (xr[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - exact_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + exact_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - exact_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + exact_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - exact_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + exact_band + 1, 0, nk - 1); // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)point_triangle_distance(ref gx, ref xp, ref xq, ref xr); if (d < distances[i, j, k]) { distances[i, j, k] = d; closest_tri[i, j, k] = tid; } } } } } if (CancelF()) { return; } if (ComputeSigns == true) { if (DebugPrint) { System.Console.WriteLine("done narrow-band"); } compute_intersections(origin, dx, ni, nj, nk, intersection_count); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done intersections"); } if (ComputeMode == ComputeModes.FullGrid) { // and now we fill in the rest of the distances with fast sweeping for (int pass = 0; pass < 2; ++pass) { sweep_pass(origin, dx, distances, closest_tri); if (CancelF()) { return; } } if (DebugPrint) { System.Console.WriteLine("done sweeping"); } } else { // nothing! if (DebugPrint) { System.Console.WriteLine("skipped sweeping"); } } // then figure out signs (inside/outside) from intersection counts compute_signs(ni, nj, nk, distances, intersection_count); if (CancelF()) { return; } if (DebugPrint) { System.Console.WriteLine("done signs"); } if (WantIntersectionsGrid) { intersections_grid = intersection_count; } } if (WantClosestTriGrid) { closest_tri_grid = closest_tri; } } // end make_level_set_3
protected void compute_full(IEnumerable <int> Triangles) { Graph = new DGraph3(); if (WantGraphEdgeInfo) { GraphEdges = new DVector <GraphEdgeInfo>(); } Vertices = new Dictionary <Vector3d, int>(); foreach (int tid in Triangles) { Vector3dTuple3 tv = new Vector3dTuple3(); Mesh.GetTriVertices(tid, ref tv.V0, ref tv.V1, ref tv.V2); Vector3d f = new Vector3d(ValueF(tv.V0), ValueF(tv.V1), ValueF(tv.V2)); // round f to 0 within epsilon? if (f.x < 0 && f.y < 0 && f.z < 0) { continue; } if (f.x > 0 && f.y > 0 && f.z > 0) { continue; } Index3i triVerts = Mesh.GetTriangle(tid); Index3i triEdges = Mesh.GetTriEdges(tid); if (f.x * f.y * f.z == 0) { int z0 = (f.x == 0) ? 0 : ((f.y == 0) ? 1 : 2); int i1 = (z0 + 1) % 3, i2 = (z0 + 2) % 3; if (f[i1] * f[i2] > 0) { continue; // single-vertex-crossing case, skip here and let other edges catch it } if (f[i1] == 0 || f[i2] == 0) { // on-edge case int z1 = f[i1] == 0 ? i1 : i2; int e0 = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int e1 = add_or_append_vertex(Mesh.GetVertex(triVerts[z1])); int graph_eid = Graph.AppendEdge(e0, e1, (int)TriangleCase.OnEdge); if (WantGraphEdgeInfo) { add_on_edge(graph_eid, tid, triEdges[z0]); } } else { // edge/vertex case Util.gDevAssert(f[i1] * f[i2] < 0); int vert_vid = add_or_append_vertex(Mesh.GetVertex(triVerts[z0])); int i = i1, j = i2; if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); int cross_vid = add_or_append_vertex(cross); int graph_eid = Graph.AppendEdge(vert_vid, cross_vid, (int)TriangleCase.EdgeVertex); if (WantGraphEdgeInfo) { add_edge_edge(graph_eid, tid, new Index2i(triEdges[(z0 + 1) % 3], triVerts[z0])); } } } else { Index3i cross_verts = Index3i.Min; for (int ti = 0; ti < 3; ++ti) { int i = ti, j = (ti + 1) % 3; if (f[i] * f[j] > 0) { continue; } if (triVerts[j] < triVerts[i]) { int tmp = i; i = j; j = tmp; } Vector3d cross = find_crossing(tv[i], tv[j], f[i], f[j]); cross_verts[ti] = add_or_append_vertex(cross); } int e0 = (cross_verts.a == int.MinValue) ? 1 : 0; int e1 = (cross_verts.c == int.MinValue) ? 1 : 2; int ev0 = cross_verts[e0]; int ev1 = cross_verts[e1]; Util.gDevAssert(ev0 != int.MinValue && ev1 != int.MinValue); int graph_eid = Graph.AppendEdge(ev0, ev1, (int)TriangleCase.EdgeEdge); if (WantGraphEdgeInfo) { add_edge_edge(graph_eid, tid, new Index2i(triEdges[e0], triEdges[e1])); } } } Vertices = null; }
// fill the intersection grid w/ number of intersections in each cell void compute_intersections(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3i intersection_count) { double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; double invdx = 1.0 / dx; bool cancelled = false; // this is what we will do for each triangle. There are no grid-reads, only grid-writes, // since we use atomic_increment, it is always thread-safe Action <int> ProcessTriangleF = (tid) => { if (tid % 100 == 0 && CancelF() == true) { cancelled = true; } if (cancelled) { return; } Vector3d xp = Vector3d.Zero, xq = Vector3d.Zero, xr = Vector3d.Zero; Mesh.GetTriVertices(tid, ref xp, ref xq, ref xr); bool neg_x = false; if (InsideMode == InsideModes.ParityCount) { Vector3d n = MathUtil.FastNormalDirection(ref xp, ref xq, ref xr); neg_x = n.x > 0; } // real ijk coordinates of xp/xq/xr double fip = (xp[0] - ox) * invdx, fjp = (xp[1] - oy) * invdx, fkp = (xp[2] - oz) * invdx; double fiq = (xq[0] - ox) * invdx, fjq = (xq[1] - oy) * invdx, fkq = (xq[2] - oz) * invdx; double fir = (xr[0] - ox) * invdx, fjr = (xr[1] - oy) * invdx, fkr = (xr[2] - oz) * invdx; // recompute j/k integer bounds of triangle w/o exact band int j0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fjp, fjq, fjr)), 0, nj - 1); int j1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fjp, fjq, fjr)), 0, nj - 1); int k0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fkp, fkq, fkr)), 0, nk - 1); int k1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fkp, fkq, fkr)), 0, nk - 1); // and do intersection counts for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { double a, b, c; if (point_in_triangle_2d(j, k, fjp, fkp, fjq, fkq, fjr, fkr, out a, out b, out c)) { double fi = a * fip + b * fiq + c * fir; // intersection i coordinate int i_interval = (int)(Math.Ceiling(fi)); // intersection is in (i_interval-1,i_interval] if (i_interval < 0) { intersection_count.atomic_incdec(0, j, k, neg_x); } else if (i_interval < ni) { intersection_count.atomic_incdec(i_interval, j, k, neg_x); } else { // we ignore intersections that are beyond the +x side of the grid } } } } }; if (UseParallel) { gParallel.ForEach(Mesh.TriangleIndices(), ProcessTriangleF); } else { foreach (int tid in Mesh.TriangleIndices()) { ProcessTriangleF(tid); } } }
/// <summary> /// Compute distance from point to triangle in mesh, with minimal extra objects/etc /// </summary> public static double TriDistanceSqr(DMesh3 mesh, int ti, Vector3d point) { Vector3d V0 = Vector3d.Zero, V1 = Vector3d.Zero, V2 = Vector3d.Zero; mesh.GetTriVertices(ti, ref V0, ref V1, ref V2); Vector3d diff = V0 - point; Vector3d edge0 = V1 - V0; Vector3d edge1 = V2 - V0; double a00 = edge0.LengthSquared; double a01 = edge0.Dot(ref edge1); double a11 = edge1.LengthSquared; double b0 = diff.Dot(ref edge0); double b1 = diff.Dot(ref edge1); double c = diff.LengthSquared; double det = Math.Abs(a00 * a11 - a01 * a01); double s = a01 * b1 - a11 * b0; double t = a01 * b0 - a00 * b1; double sqrDistance; if (s + t <= det) { if (s < 0) { if (t < 0) { // region 4 if (b0 < 0) { t = 0; if (-b0 >= a00) { s = 1; sqrDistance = a00 + (2) * b0 + c; } else { s = -b0 / a00; sqrDistance = b0 * s + c; } } else { s = 0; if (b1 >= 0) { t = 0; sqrDistance = c; } else if (-b1 >= a11) { t = 1; sqrDistance = a11 + (2) * b1 + c; } else { t = -b1 / a11; sqrDistance = b1 * t + c; } } } else { // region 3 s = 0; if (b1 >= 0) { t = 0; sqrDistance = c; } else if (-b1 >= a11) { t = 1; sqrDistance = a11 + (2) * b1 + c; } else { t = -b1 / a11; sqrDistance = b1 * t + c; } } } else if (t < 0) { // region 5 t = 0; if (b0 >= 0) { s = 0; sqrDistance = c; } else if (-b0 >= a00) { s = 1; sqrDistance = a00 + (2) * b0 + c; } else { s = -b0 / a00; sqrDistance = b0 * s + c; } } else { // region 0 // minimum at interior point double invDet = (1) / det; s *= invDet; t *= invDet; sqrDistance = s * (a00 * s + a01 * t + (2) * b0) + t * (a01 * s + a11 * t + (2) * b1) + c; } } else { double tmp0, tmp1, numer, denom; if (s < 0) { // region 2 tmp0 = a01 + b0; tmp1 = a11 + b1; if (tmp1 > tmp0) { numer = tmp1 - tmp0; denom = a00 - (2) * a01 + a11; if (numer >= denom) { s = 1; t = 0; sqrDistance = a00 + (2) * b0 + c; } else { s = numer / denom; t = 1 - s; sqrDistance = s * (a00 * s + a01 * t + (2) * b0) + t * (a01 * s + a11 * t + (2) * b1) + c; } } else { s = 0; if (tmp1 <= 0) { t = 1; sqrDistance = a11 + (2) * b1 + c; } else if (b1 >= 0) { t = 0; sqrDistance = c; } else { t = -b1 / a11; sqrDistance = b1 * t + c; } } } else if (t < 0) { // region 6 tmp0 = a01 + b1; tmp1 = a00 + b0; if (tmp1 > tmp0) { numer = tmp1 - tmp0; denom = a00 - (2) * a01 + a11; if (numer >= denom) { t = 1; s = 0; sqrDistance = a11 + (2) * b1 + c; } else { t = numer / denom; s = 1 - t; sqrDistance = s * (a00 * s + a01 * t + (2) * b0) + t * (a01 * s + a11 * t + (2) * b1) + c; } } else { t = 0; if (tmp1 <= 0) { s = 1; sqrDistance = a00 + (2) * b0 + c; } else if (b0 >= 0) { s = 0; sqrDistance = c; } else { s = -b0 / a00; sqrDistance = b0 * s + c; } } } else { // region 1 numer = a11 + b1 - a01 - b0; if (numer <= 0) { s = 0; t = 1; sqrDistance = a11 + (2) * b1 + c; } else { denom = a00 - (2) * a01 + a11; if (numer >= denom) { s = 1; t = 0; sqrDistance = a00 + (2) * b0 + c; } else { s = numer / denom; t = 1 - s; sqrDistance = s * (a00 * s + a01 * t + (2) * b0) + t * (a01 * s + a11 * t + (2) * b1) + c; } } } } if (sqrDistance < 0) { sqrDistance = 0; } return(sqrDistance); }
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); } } }
/// <summary> /// 1) Find intersection segments /// 2) sort onto existing input mesh vtx/edge/face /// </summary> void find_segments() { Dictionary <Vector3d, SegmentVtx> SegVtxMap = new Dictionary <Vector3d, SegmentVtx>(); // find intersection segments // TODO: intersection polygons // TODO: do we need to care about intersection vertices? DMeshAABBTree3 targetSpatial = new DMeshAABBTree3(Target, true); DMeshAABBTree3 cutSpatial = new DMeshAABBTree3(CutMesh, true); var intersections = targetSpatial.FindAllIntersections(cutSpatial); // for each segment, for each vtx, determine if it is // at an existing vertex, on-edge, or in-face Segments = new IntersectSegment[intersections.Segments.Count]; for (int i = 0; i < Segments.Length; ++i) { var isect = intersections.Segments[i]; Vector3dTuple2 points = new Vector3dTuple2(isect.point0, isect.point1); IntersectSegment iseg = new IntersectSegment() { base_tid = isect.t0 }; Segments[i] = iseg; for (int j = 0; j < 2; ++j) { Vector3d v = points[j]; // if this exact vtx coord has been seen, use same vtx SegmentVtx sv; if (SegVtxMap.TryGetValue(v, out sv)) { iseg[j] = sv; continue; } sv = new SegmentVtx() { v = v }; SegVertices.Add(sv); SegVtxMap[v] = sv; iseg[j] = sv; // this vtx is tol-equal to input mesh vtx int existing_v = find_existing_vertex(isect.point0); if (existing_v >= 0) { sv.initial_type = sv.type = 0; sv.elem_id = existing_v; sv.vtx_id = existing_v; VIDToSegVtxMap[sv.vtx_id] = sv; continue; } Triangle3d tri = new Triangle3d(); Target.GetTriVertices(isect.t0, ref tri.V0, ref tri.V1, ref tri.V2); Index3i tv = Target.GetTriangle(isect.t0); // this vtx is tol-on input mesh edge int on_edge_i = on_edge(ref tri, ref v); if (on_edge_i >= 0) { sv.initial_type = sv.type = 1; sv.elem_id = Target.FindEdge(tv[on_edge_i], tv[(on_edge_i + 1) % 3]); Util.gDevAssert(sv.elem_id != DMesh3.InvalidID); add_edge_vtx(sv.elem_id, sv); continue; } // otherwise contained in input mesh face sv.initial_type = sv.type = 2; sv.elem_id = isect.t0; add_face_vtx(sv.elem_id, sv); } } }
void generate_support(Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f supportGrid) { supportGrid.resize(ni, nj, nk); supportGrid.assign(1); // sentinel bool CHECKERBOARD = false; // compute unsigned SDF int exact_band = 1; if (SubtractMesh && SubtractMeshOffset > 0) { int offset_band = (int)(SubtractMeshOffset / CellSize) + 1; exact_band = Math.Max(exact_band, offset_band); } sdf = new MeshSignedDistanceGrid(Mesh, CellSize) { ComputeSigns = true, ExactBandWidth = exact_band }; sdf.CancelF = this.CancelF; sdf.Compute(); if (CancelF()) { return; } var distanceField = new DenseGridTrilinearImplicit(sdf.Grid, sdf.GridOrigin, sdf.CellSize); double angle = MathUtil.Clamp(OverhangAngleDeg, 0.01, 89.99); double cos_thresh = Math.Cos(angle * MathUtil.Deg2Rad); // Compute narrow-band distances. For each triangle, we find its grid-coord-bbox, // and compute exact distances within that box. The intersection_count grid // is also filled in this computation double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; Vector3d va = Vector3d.Zero, vb = Vector3d.Zero, vc = Vector3d.Zero; foreach (int tid in Mesh.TriangleIndices()) { if (tid % 100 == 0 && CancelF()) { break; } Mesh.GetTriVertices(tid, ref va, ref vb, ref vc); Vector3d normal = MathUtil.Normal(ref va, ref vb, ref vc); if (normal.Dot(-Vector3d.AxisY) < cos_thresh) { continue; } // real ijk coordinates of va/vb/vc double fip = (va[0] - ox) / ddx, fjp = (va[1] - oy) / ddx, fkp = (va[2] - oz) / ddx; double fiq = (vb[0] - ox) / ddx, fjq = (vb[1] - oy) / ddx, fkq = (vb[2] - oz) / ddx; double fir = (vc[0] - ox) / ddx, fjr = (vc[1] - oy) / ddx, fkr = (vc[2] - oz) / ddx; // clamped integer bounding box of triangle plus exact-band int extra_band = 0; int i0 = MathUtil.Clamp(((int)MathUtil.Min(fip, fiq, fir)) - extra_band, 0, ni - 1); int i1 = MathUtil.Clamp(((int)MathUtil.Max(fip, fiq, fir)) + extra_band + 1, 0, ni - 1); int j0 = MathUtil.Clamp(((int)MathUtil.Min(fjp, fjq, fjr)) - extra_band, 0, nj - 1); int j1 = MathUtil.Clamp(((int)MathUtil.Max(fjp, fjq, fjr)) + extra_band + 1, 0, nj - 1); int k0 = MathUtil.Clamp(((int)MathUtil.Min(fkp, fkq, fkr)) - extra_band, 0, nk - 1); int k1 = MathUtil.Clamp(((int)MathUtil.Max(fkp, fkq, fkr)) + extra_band + 1, 0, nk - 1); // don't put into y=0 plane //if (j0 == 0) // j0 = 1; // compute distance for each tri inside this bounding box // note: this can be very conservative if the triangle is large and on diagonal to grid axes for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3d gx = new Vector3d((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = (float)MeshSignedDistanceGrid.point_triangle_distance(ref gx, ref va, ref vb, ref vc); // vertical checkerboard pattern (eg 'tips') if (CHECKERBOARD) { int zz = (k % 2 == 0) ? 1 : 0; if (i % 2 == zz) { continue; } } if (d < dx / 2) { supportGrid[i, j, k] = SUPPORT_TIP_TOP; } } } } } if (CancelF()) { return; } fill_vertical_spans(supportGrid, distanceField); generate_mesh(supportGrid, distanceField); }