/// <summary> /// Add all triangles in vertex one-rings of current selection to set. /// On a large mesh this is quite expensive as we don't know the boundary, /// so we have to iterate over all triangles. /// /// Return false from FilterF to prevent triangles from being included. /// </summary> public void ExpandToOneRingNeighbours(Func <int, bool> FilterF = null) { temp.Clear(); foreach (int tid in Selected) { Index3i tri_v = Mesh.GetTriangle(tid); for (int j = 0; j < 3; ++j) { int vid = tri_v[j]; foreach (int nbr_t in Mesh.VtxTrianglesItr(vid)) { if (FilterF != null && FilterF(nbr_t) == false) { continue; } if (IsSelected(nbr_t) == false) { temp.Add(nbr_t); } } } } for (int i = 0; i < temp.Count; ++i) { add(temp[i]); } }
public void SelectTriangleVertices(int[] triangles) { for (int i = 0; i < triangles.Length; ++i) { Index3i tri = Mesh.GetTriangle(triangles[i]); add(tri.a); add(tri.b); add(tri.c); } }
// accumulate triangle counts and track each box-parent index. // also checks that triangles are contained in boxes private void test_coverage(int[] tri_counts, int[] parent_indices, int iBox) { int idx = box_to_index[iBox]; debug_check_child_tris_in_box(iBox); if (idx < triangles_end) { // triange-list case, array is [N t1 t2 ... tN] int n = index_list[idx]; AxisAlignedBox3f box = get_box(iBox); for (int i = 1; i <= n; ++i) { int ti = index_list[idx + i]; tri_counts[ti]++; Index3i tv = mesh.GetTriangle(ti); for (int j = 0; j < 3; ++j) { Vector3f v = (Vector3f)mesh.GetVertex(tv[j]); if (!box.Contains(v)) { Util.gBreakToDebugger(); } } } } else { int i0 = index_list[idx]; if (i0 < 0) { // negative index means we only have one 'child' box to descend into i0 = (-i0) - 1; parent_indices[i0] = iBox; test_coverage(tri_counts, parent_indices, i0); } else { // positive index, two sequential child box indices to descend into i0 = i0 - 1; parent_indices[i0] = iBox; test_coverage(tri_counts, parent_indices, i0); int i1 = index_list[idx + 1]; i1 = i1 - 1; parent_indices[i1] = iBox; test_coverage(tri_counts, parent_indices, i1); } } }
// [RMS] estimate can be zero void compute(IEnumerable <int> triangles, int tri_count_est) { int est_verts = tri_count_est / 2; SubMesh = new DMesh3(BaseMesh.Components & WantComponents); BaseSubmeshV = new IndexFlagSet(BaseMesh.MaxVertexID, est_verts); BaseToSubV = new IndexMap(BaseMesh.MaxVertexID, est_verts); SubToBaseV = new DVector <int>(); if (ComputeTriMaps) { BaseToSubT = new IndexMap(BaseMesh.MaxTriangleID, tri_count_est); SubToBaseT = new DVector <int>(); } foreach (int tid in triangles) { if (!BaseMesh.IsTriangle(tid)) { throw new Exception("DSubmesh3.compute: triangle " + tid + " does not exist in BaseMesh!"); } Index3i base_t = BaseMesh.GetTriangle(tid); Index3i new_t = Index3i.Zero; int gid = BaseMesh.GetTriangleGroup(tid); for (int j = 0; j < 3; ++j) { int base_v = base_t[j]; int sub_v = -1; if (BaseSubmeshV[base_v] == false) { sub_v = SubMesh.AppendVertex(BaseMesh, base_v); BaseSubmeshV[base_v] = true; BaseToSubV[base_v] = sub_v; SubToBaseV.insert(base_v, sub_v); } else { sub_v = BaseToSubV[base_v]; } new_t[j] = sub_v; } if (OverrideGroupID >= 0) { gid = OverrideGroupID; } int sub_tid = SubMesh.AppendTriangle(new_t, gid); if (ComputeTriMaps) { BaseToSubT[tid] = sub_tid; SubToBaseT.insert(tid, sub_tid); } } }
/// <summary> /// Compute volume and surface area of triangles of mesh. /// Return value is (volume,area) /// Note that if triangles don't define closed region, volume is probably nonsense... /// </summary> public static Vector2d VolumeArea(DMesh3 mesh, IEnumerable <int> triangles, Func <int, Vector3d> getVertexF) { double mass_integral = 0.0; double area_sum = 0; foreach (int tid in triangles) { Index3i tri = mesh.GetTriangle(tid); // Get vertices of triangle i. Vector3d v0 = getVertexF(tri.a); Vector3d v1 = getVertexF(tri.b); Vector3d v2 = getVertexF(tri.c); // Get cross product of edges and (un-normalized) normal vector. Vector3d V1mV0 = v1 - v0; Vector3d V2mV0 = v2 - v0; Vector3d N = V1mV0.Cross(V2mV0); area_sum += 0.5 * N.Length; double tmp0 = v0.x + v1.x; double f1x = tmp0 + v2.x; mass_integral += N.x * f1x; } return(new Vector2d(mass_integral * (1.0 / 6.0), area_sum)); }
// convert vertex selection to face selection. Require at least minCount verts of // tri to be selected (valid values are 1,2,3) public MeshFaceSelection(DMesh3 mesh, MeshVertexSelection convertV, int minCount = 3) : this(mesh) { minCount = MathUtil.Clamp(minCount, 1, 3); foreach (int tid in mesh.TriangleIndices()) { Index3i tri = mesh.GetTriangle(tid); if (minCount == 1) { if (convertV.IsSelected(tri.a) || convertV.IsSelected(tri.b) || convertV.IsSelected(tri.c)) { add(tid); } } else if (minCount == 3) { if (convertV.IsSelected(tri.a) && convertV.IsSelected(tri.b) && convertV.IsSelected(tri.c)) { add(tid); } } else { int n = (convertV.IsSelected(tri.a) ? 1 : 0) + (convertV.IsSelected(tri.b) ? 1 : 0) + (convertV.IsSelected(tri.c) ? 1 : 0); if (n >= minCount) { add(tid); } } } }
/// <summary> /// find base-mesh interior vertices of region (ie does not include region boundary vertices) /// </summary> public HashSet <int> CurrentBaseInteriorVertices() { var verts = new HashSet <int>(); IndexHashSet borderv = Region.BaseBorderV; foreach (int tid in cur_base_tris) { Index3i tv = BaseMesh.GetTriangle(tid); if (borderv[tv.a] == false) { verts.Add(tv.a); } if (borderv[tv.b] == false) { verts.Add(tv.b); } if (borderv[tv.c] == false) { verts.Add(tv.c); } } return(verts); }
public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> added_v, IEnumerable <int> added_t) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; if (added_v != null) { foreach (int vid in added_v) { Util.gDevAssert(mesh.IsVertex(vid)); append_vertex(mesh, vid); } } foreach (int tid in added_t) { Util.gDevAssert(mesh.IsTriangle(tid)); Index3i tv = mesh.GetTriangle(tid); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); AddedT.Add(tid); Triangles.Add(tri); } }
override public MeshGenerator Generate() { MeshInsertPolygon insert; DMesh3 base_mesh = ComputeResult(out insert); DMesh3 compact = new DMesh3(base_mesh, true); int NV = compact.VertexCount; vertices = new VectorArray3d(NV); uv = new VectorArray2f(NV); normals = new VectorArray3f(NV); for (int vi = 0; vi < NV; ++vi) { vertices[vi] = compact.GetVertex(vi); uv[vi] = compact.GetVertexUV(vi); normals[vi] = FixedNormal; } int NT = compact.TriangleCount; triangles = new IndexArray3i(NT); for (int ti = 0; ti < NT; ++ti) { triangles[ti] = compact.GetTriangle(ti); } return(this); }
/// <summary> /// if before a flip we have normals (n1,n2) and after we have (m1,m2), check if /// the dot between any of the 4 pairs changes sign after the flip, or is /// less than the dot-product tolerance (ie angle tolerance) /// </summary> public static bool CheckIfEdgeFlipCreatesFlip(DMesh3 mesh, int eID, double flip_dot_tol = 0.0) { Util.gDevAssert(mesh.IsBoundaryEdge(eID) == false); Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Vector3d vC = mesh.GetVertex(c), vD = mesh.GetVertex(d); Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); Vector3d vOA = mesh.GetVertex(oa), vOB = mesh.GetVertex(ob); Vector3d n0 = MathUtil.FastNormalDirection(ref vOA, ref vOB, ref vC); Vector3d n1 = MathUtil.FastNormalDirection(ref vOB, ref vOA, ref vD); Vector3d f0 = MathUtil.FastNormalDirection(ref vC, ref vD, ref vOB); if (edge_flip_metric(ref n0, ref f0, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f0, flip_dot_tol) <= flip_dot_tol) { return(true); } Vector3d f1 = MathUtil.FastNormalDirection(ref vD, ref vC, ref vOA); if (edge_flip_metric(ref n0, ref f1, flip_dot_tol) <= flip_dot_tol || edge_flip_metric(ref n1, ref f1, flip_dot_tol) <= flip_dot_tol) { return(true); } return(false); }
public void Initialize(DMesh3 mesh, IEnumerable <int> triangles) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; foreach (int tid in triangles) { if (!mesh.IsTriangle(tid)) { continue; } Index3i tv = mesh.GetTriangle(tid); bool va = save_vertex(mesh, tv.a); bool vb = save_vertex(mesh, tv.b); bool vc = save_vertex(mesh, tv.c); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); RemovedT.Add(tid); Triangles.Add(tri); MeshResult result = mesh.RemoveTriangle(tid, true, false); if (result != MeshResult.Ok) { throw new Exception("RemoveTrianglesMeshChange.Initialize: exception in RemoveTriangle(" + tid.ToString() + "): " + result.ToString()); } Util.gDevAssert(mesh.IsVertex(tv.a) == va && mesh.IsVertex(tv.b) == vb && mesh.IsVertex(tv.c) == vc); } }
/// <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); }
public static void TrianglesToVertices(DMesh3 mesh, HashSet <int> triangles, HashSet <int> vertices) { foreach (int tid in triangles) { Index3i tv = mesh.GetTriangle(tid); vertices.Add(tv.a); vertices.Add(tv.b); vertices.Add(tv.c); } }
// convert face selection to vertex selection. public MeshVertexSelection(DMesh3 mesh, MeshFaceSelection convertT) : this(mesh) { foreach (int tid in convertT) { Index3i tv = mesh.GetTriangle(tid); add(tv.a); add(tv.b); add(tv.c); } }
// return same indices as GetEdgeV, but oriented based on attached triangle Index2i get_oriented_edgev(int eID, int tid_in, int tid_out) { Index2i edgev = Mesh.GetEdgeV(eID); int a = edgev.a, b = edgev.b; Index3i tri = Mesh.GetTriangle(tid_in); int ai = IndexUtil.find_edge_index_in_tri(a, b, ref tri); return(new Index2i(tri[ai], tri[(ai + 1) % 3])); }
void spatial_add_triangle(int tid) { if (triSpatial == null) { return; } Index3i tv = Mesh.GetTriangle(tid); Vector2d a = PointF(tv.a), b = PointF(tv.b), c = PointF(tv.c); triSpatial.InsertTriangleUnsafe(tid, ref a, ref b, ref c); }
/// <summary> /// Check if collapsing edge edgeID to point newv will flip normal of any attached face /// </summary> public static bool CheckIfCollapseCreatesFlip(DMesh3 mesh, int edgeID, Vector3d newv) { Index4i edge_info = mesh.GetEdge(edgeID); int tc = edge_info.c, td = edge_info.d; for (int j = 0; j < 2; ++j) { int vid = edge_info[j]; int vother = edge_info[(j + 1) % 2]; 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 } Vector3d va = mesh.GetVertex(curt.a); Vector3d vb = mesh.GetVertex(curt.b); Vector3d vc = mesh.GetVertex(curt.c); Vector3d ncur = (vb - va).Cross(vc - va); double sign = 0; if (curt.a == vid) { Vector3d nnew = (vb - newv).Cross(vc - newv); sign = ncur.Dot(nnew); } else if (curt.b == vid) { Vector3d nnew = (newv - va).Cross(vc - va); sign = ncur.Dot(nnew); } else if (curt.c == vid) { Vector3d nnew = (vb - va).Cross(newv - va); sign = ncur.Dot(nnew); } else { throw new Exception("should never be here!"); } if (sign <= 0.0) { return(true); } } } return(false); }
// (sequentially) find each triangle that path point lies in, and insert a vertex for // that point into mesh. void insert_corners() { PrimalQuery2d query = new PrimalQuery2d(PointF); // [TODO] can do everythnig up to PokeTriangle in parallel, // except if we are poking same tri w/ multiple points! CurveVertices = new int[Curve.VertexCount]; for (int i = 0; i < Curve.VertexCount; ++i) { Vector2d vInsert = Curve[i]; bool inserted = false; foreach (int tid in Mesh.TriangleIndices()) { Index3i tv = Mesh.GetTriangle(tid); // [RMS] using unsigned query here because we do not need to care about tri CW/CCW orientation // (right? otherwise we have to explicitly invert mesh. Nothing else we do depends on tri orientation) //int query_result = query.ToTriangle(vInsert, tv.a, tv.b, tv.c); int query_result = query.ToTriangleUnsigned(vInsert, tv.a, tv.b, tv.c); if (query_result == -1 || query_result == 0) { Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c)); int vid = insert_corner_from_bary(i, tid, bary); if (vid > 0) // this should be always happening.. { CurveVertices[i] = vid; inserted = true; //Util.WriteDebugMesh(Mesh, string.Format("C:\\git\\geometry3SharpDemos\\geometry3Test\\test_output\\after_insert_corner_{0}.obj", i)); break; } } } if (inserted == false) { throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex " + i.ToString() + " is not inside or on any mesh triangle!"); } } }
/// <summary> /// computes sum of opening-angles in triangles around vid, minus 2pi. /// This is zero on flat areas. /// </summary> public static double DiscreteGaussCurvature(DMesh3 mesh, int vid) { double angle_sum = 0; foreach (int tid in mesh.VtxTrianglesItr(vid)) { Index3i et = mesh.GetTriangle(tid); int idx = IndexUtil.find_tri_index(vid, ref et); angle_sum += mesh.GetTriInternalAngleR(tid, idx); } return(angle_sum - MathUtil.TwoPI); }
// create Component from triangles Component extract_submesh(int submesh_index, int[] subt, int Nt, int[] mapToCur, int[] subv, out Index2i mapRange, out int max_subv) { int subvi = 0; Component C = new Component(); C.id = submesh_index; C.triangles = new int[Nt * 3]; C.tri_count = Nt; mapRange = new Index2i(int.MaxValue, int.MinValue); // construct list of triangles and vertex map for (int ti = 0; ti < Nt; ++ti) { int tid = subt[ti]; Index3i tri = mesh.GetTriangle(tid); for (int j = 0; j < 3; ++j) { int vid = tri[j]; if (mapToCur[vid] == 0) { mapToCur[vid] = (subvi + 1); subv[subvi] = vid; if (vid < mapRange.a) { mapRange.a = vid; } else if (vid > mapRange.b) { mapRange.b = vid; } if (TrackVertexMapping) { add_submesh_mapv(vid, C.id, subvi); } subvi++; } C.triangles[3 * ti + j] = mapToCur[vid] - 1; } } C.source_vertices = new int[subvi]; Array.Copy(subv, C.source_vertices, subvi); max_subv = subvi; return(C); }
/// <summary> /// For given edge, return it's triangles and the triangles that would /// be created if it was flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipTris(DMesh3 mesh, int eID, out Index3i orig_t0, out Index3i orig_t1, out Index3i flip_t0, out Index3i flip_t1) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); orig_t0 = new Index3i(oa, ob, c); orig_t1 = new Index3i(ob, oa, d); flip_t0 = new Index3i(c, d, ob); flip_t1 = new Index3i(d, c, oa); }
// from http://www.geometry.caltech.edu/pubs/DMSB_III.pdf public static double VoronoiArea(DMesh3 mesh, int v_i) { double areaSum = 0; Vector3d Vi = mesh.GetVertex(v_i); foreach (int tid in mesh.VtxTrianglesItr(v_i)) { Index3i t = mesh.GetTriangle(tid); int ti = (t[0] == v_i) ? 0 : ((t[1] == v_i) ? 1 : 2); Vector3d Vj = mesh.GetVertex(t[(ti + 1) % 3]); Vector3d Vk = mesh.GetVertex(t[(ti + 2) % 3]); if (MathUtil.IsObtuse(Vi, Vj, Vk)) { Vector3d Vij = Vj - Vi; Vector3d Vik = Vk - Vi; Vij.Normalize(); Vik.Normalize(); double areaT = 0.5 * Vij.Cross(Vik).Length; if (Vector3d.AngleR(Vij, Vik) > MathUtil.HalfPI) { areaSum += (areaT * 0.5); // obtuse at v_i } else { areaSum += (areaT * 0.25); // not obtuse } } else { // voronoi area Vector3d Vji = Vi - Vj; double dist_ji = Vji.Normalize(); Vector3d Vki = Vi - Vk; double dist_ki = Vki.Normalize(); Vector3d Vkj = (Vj - Vk).Normalized; double cot_alpha_ij = MathUtil.VectorCot(Vki, Vkj); double cot_alpha_ik = MathUtil.VectorCot(Vji, -Vkj); areaSum += dist_ji * dist_ji * cot_alpha_ij * 0.125; areaSum += dist_ki * dist_ki * cot_alpha_ik * 0.125; } } return(areaSum); }
static void check_neighbour(DMesh3 mesh, DenseGrid3f phi, DenseGrid3i closest_tri, Vector3f gx, int i0, int j0, int k0, int i1, int j1, int k1) { if (closest_tri[i1, j1, k1] >= 0) { Index3i tri = mesh.GetTriangle(closest_tri[i1, j1, k1]); int p = tri.a, q = tri.b, r = tri.c; Vector3f xp = (Vector3f)mesh.GetVertex(p); Vector3f xq = (Vector3f)mesh.GetVertex(q); Vector3f xr = (Vector3f)mesh.GetVertex(r); float d = point_triangle_distance(gx, xp, xq, xr); if (d < phi[i0, j0, k0]) { phi[i0, j0, k0] = d; closest_tri[i0, j0, k0] = closest_tri[i1, j1, k1]; } } }
public void InitializeFromExisting(DMesh3 mesh, IEnumerable <int> remove_t) { initialize_buffers(mesh); bool has_groups = mesh.HasTriangleGroups; HashSet <int> triangles = new HashSet <int>(remove_t); HashSet <int> vertices = new HashSet <int>(); IndexUtil.TrianglesToVertices(mesh, remove_t, vertices); List <int> save_v = new List <int>(); foreach (int vid in vertices) { bool all_contained = true; foreach (int tid in mesh.VtxTrianglesItr(vid)) { if (triangles.Contains(tid) == false) { all_contained = false; break; } } if (all_contained) { save_v.Add(vid); } } foreach (int vid in save_v) { save_vertex(mesh, vid, true); } foreach (int tid in remove_t) { Util.gDevAssert(mesh.IsTriangle(tid)); Index3i tv = mesh.GetTriangle(tid); Index4i tri = new Index4i(tv.a, tv.b, tv.c, has_groups ? mesh.GetTriangleGroup(tid) : DMesh3.InvalidID); RemovedT.Add(tid); Triangles.Add(tri); } }
public void CompactCopy(DMesh3 copy, bool bNormals = true, bool bColors = true, bool bUVs = true) { if (copy.IsCompact) { Copy(copy, bNormals, bColors, bUVs); return; } vertices = new DVector <double>(); vertex_edges = new DVector <List <int> >(); vertices_refcount = new RefCountVector(); triangles = new DVector <int>(); triangle_edges = new DVector <int>(); triangles_refcount = new RefCountVector(); edges = new DVector <int>(); edges_refcount = new RefCountVector(); normals = (bNormals && copy.normals != null) ? new DVector <float>() : null; colors = (bColors && copy.colors != null) ? new DVector <float>() : null; uv = (bUVs && copy.uv != null) ? new DVector <float>() : null; // [TODO] if we knew some of these were dense we could copy directly... NewVertexInfo vinfo = new NewVertexInfo(); int[] mapV = new int[copy.MaxVertexID]; foreach (int vid in copy.vertices_refcount) { copy.GetVertex(vid, ref vinfo, bNormals, bColors, bUVs); mapV[vid] = AppendVertex(vinfo); } // [TODO] would be much faster to explicitly copy triangle & edge data structures!! foreach (int tid in copy.triangles_refcount) { Index3i t = copy.GetTriangle(tid); t.a = mapV[t.a]; t.b = mapV[t.b]; t.c = mapV[t.c]; int g = (copy.HasTriangleGroups) ? copy.GetTriangleGroup(tid) : InvalidID; AppendTriangle(t, g); } }
/// <summary> /// For given edge, return normals of it's two triangles, and normals /// of the triangles created if edge is flipped (used in edge-flip optimizers) /// </summary> public static void GetEdgeFlipNormals(DMesh3 mesh, int eID, out Vector3d n1, out Vector3d n2, out Vector3d on1, out Vector3d on2) { Index4i einfo = mesh.GetEdge(eID); Index2i ov = mesh.GetEdgeOpposingV(eID); int a = einfo.a, b = einfo.b, c = ov.a, d = ov.b; int t0 = einfo.c; Vector3d vC = mesh.GetVertex(c), vD = mesh.GetVertex(d); Index3i tri_v = mesh.GetTriangle(t0); int oa = a, ob = b; IndexUtil.orient_tri_edge(ref oa, ref ob, ref tri_v); Vector3d vOA = mesh.GetVertex(oa), vOB = mesh.GetVertex(ob); n1 = MathUtil.Normal(ref vOA, ref vOB, ref vC); n2 = MathUtil.Normal(ref vOB, ref vOA, ref vD); on1 = MathUtil.Normal(ref vC, ref vD, ref vOB); on2 = MathUtil.Normal(ref vD, ref vC, ref vOA); }
// Modes: 0: centroids, 1: any vertex, 2: 2 vertices, 3: all vertices // ContainF should return true if 3D position is in set (eg inside box, etc) // If mode = 0, will be called with (centroid, tri_idx) // If mode = 1/2/3, will be called with (vtx_pos, vtx_idx) // AddF is called with triangle IDs that are in set public static void TrianglesContained(DMesh3 mesh, Func <Vector3d, int, bool> ContainF, Action <int> AddF, int nMode = 0) { BitArray inV = null; if (nMode != 0) { inV = new BitArray(mesh.MaxVertexID); foreach (int vid in mesh.VertexIndices()) { if (ContainF(mesh.GetVertex(vid), vid)) { inV[vid] = true; } } } foreach (int tid in mesh.TriangleIndices()) { Index3i tri = mesh.GetTriangle(tid); bool bIn = false; if (nMode == 0) { if (ContainF(mesh.GetTriCentroid(tid), tid)) { bIn = true; } } else { int countIn = (inV[tri.a] ? 1 : 0) + (inV[tri.b] ? 1 : 0) + (inV[tri.c] ? 1 : 0); bIn = (countIn >= nMode); } if (bIn) { AddF(tid); } } }
void compute(IEnumerable <int> triangles, int tri_count) { int est_verts = tri_count / 2; SubMesh = new DMesh3(BaseMesh.Components & WantComponents); BaseSubmeshV = new IndexFlagSet(BaseMesh.MaxVertexID, est_verts); BaseToSubV = new IndexMap(BaseMesh.MaxVertexID, est_verts); SubToBaseV = new DVector <int>(); foreach (int ti in triangles) { Index3i base_t = BaseMesh.GetTriangle(ti); Index3i new_t = Index3i.Zero; int gid = BaseMesh.GetTriangleGroup(ti); for (int j = 0; j < 3; ++j) { int base_v = base_t[j]; int sub_v = -1; if (BaseSubmeshV[base_v] == false) { sub_v = SubMesh.AppendVertex(BaseMesh, base_v); BaseSubmeshV[base_v] = true; BaseToSubV[base_v] = sub_v; SubToBaseV.insert(base_v, sub_v); } else { sub_v = BaseToSubV[base_v]; } new_t[j] = sub_v; } SubMesh.AppendTriangle(new_t, gid); } }
/// <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); } } }
protected virtual ProcessResult CollapseEdge(int edgeID, Vector3d vNewPos, out int collapseToV) { collapseToV = DMesh3.InvalidID; RuntimeDebugCheck(edgeID); EdgeConstraint constraint = (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID); if (constraint.NoModifications) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } if (constraint.CanCollapse == false) { return(ProcessResult.Ignored_EdgeIsFullyConstrained); } // look up verts and tris for this edge int a = 0, b = 0, t0 = 0, t1 = 0; if (mesh.GetEdge(edgeID, ref a, ref b, ref t0, ref t1) == false) { return(ProcessResult.Failed_NotAnEdge); } bool bIsBoundaryEdge = (t1 == DMesh3.InvalidID); // look up 'other' verts c (from t0) and d (from t1, if it exists) Index3i T0tv = mesh.GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); Index3i T1tv = (bIsBoundaryEdge) ? DMesh3.InvalidTriangle : mesh.GetTriangle(t1); int d = (bIsBoundaryEdge) ? DMesh3.InvalidID : IndexUtil.find_tri_other_vtx(a, b, T1tv); Vector3d vA = mesh.GetVertex(a); Vector3d vB = mesh.GetVertex(b); double edge_len_sqr = (vA - vB).LengthSquared; if (edge_len_sqr > MinEdgeLength * MinEdgeLength) { return(ProcessResult.Ignored_EdgeTooLong); } begin_collapse(); // check if we should collapse, and also find which vertex we should collapse to, // in cases where we have constraints/etc int collapse_to = -1; bool bCanCollapse = can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to); if (bCanCollapse == false) { return(ProcessResult.Ignored_Constrained); } // if we have a boundary, we want to collapse to boundary if (PreserveBoundary && HaveBoundary) { if (collapse_to != -1) { if ((IsBoundaryV(b) && collapse_to != b) || (IsBoundaryV(a) && collapse_to != a)) { return(ProcessResult.Ignored_Constrained); } } if (IsBoundaryV(b)) { collapse_to = b; } else if (IsBoundaryV(a)) { collapse_to = a; } } // optimization: if edge cd exists, we cannot collapse or flip. look that up here? // funcs will do it internally... // (or maybe we can collapse if cd exists? edge-collapse doesn't check for it explicitly...) ProcessResult retVal = ProcessResult.Failed_OpNotSuccessful; int iKeep = b, iCollapse = a; // if either vtx is fixed, collapse to that position if (collapse_to == b) { vNewPos = vB; } else if (collapse_to == a) { iKeep = a; iCollapse = b; vNewPos = vA; } else { vNewPos = get_projected_collapse_position(iKeep, vNewPos); } // check if this collapse will create a normal flip. Also checks // for invalid collapse nbrhood, since we are doing one-ring iter anyway. // [TODO] could we skip this one-ring check in CollapseEdge? pass in hints? if (creates_flip_or_invalid(a, b, ref vNewPos, t0, t1) || creates_flip_or_invalid(b, a, ref vNewPos, t0, t1)) { retVal = ProcessResult.Ignored_CreatesFlip; goto skip_to_end; } // lots of cases where we cannot collapse, but we should just let // mesh sort that out, right? COUNT_COLLAPSES++; DMesh3.EdgeCollapseInfo collapseInfo; MeshResult result = mesh.CollapseEdge(iKeep, iCollapse, out collapseInfo); if (result == MeshResult.Ok) { collapseToV = iKeep; mesh.SetVertex(iKeep, vNewPos); if (constraints != null) { constraints.ClearEdgeConstraint(edgeID); constraints.ClearEdgeConstraint(collapseInfo.eRemoved0); if (collapseInfo.eRemoved1 != DMesh3.InvalidID) { constraints.ClearEdgeConstraint(collapseInfo.eRemoved1); } constraints.ClearVertexConstraint(iCollapse); } OnEdgeCollapse(edgeID, iKeep, iCollapse, collapseInfo); DoDebugChecks(); retVal = ProcessResult.Ok_Collapsed; } skip_to_end: end_collapse(); return(retVal); }