internal MeshNode(int i, int fi, g3.Frame3f f, g3.Index3i neighbors_index, g3.Index3i vertex_index) { frame = f; this.neighbors_index = neighbors_index; this.vertex_index = vertex_index; meshIndex = fi; index = i; }
// (sequentially) find each triangle that path point lies in, and insert a vertex for // that point into mesh. void insert_corners(HashSet <int> MeshVertsOnCurve) { PrimalQuery2d query = new PrimalQuery2d(PointF); if (UseTriSpatial) { int count = Mesh.TriangleCount + Curve.VertexCount; int bins = 32; if (count < 25) { bins = 8; } else if (count < 100) { bins = 16; } AxisAlignedBox3d bounds3 = Mesh.CachedBounds; AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds3.Min.xy, bounds3.Max.xy); triSpatial = new TriangleBinsGrid2d(bounds2, bins); foreach (int tid in Mesh.TriangleIndices()) { spatial_add_triangle(tid); } } Func <int, Vector2d, bool> inTriangleF = (tid, pos) => { Index3i tv = Mesh.GetTriangle(tid); int query_result = query.ToTriangleUnsigned(pos, tv.a, tv.b, tv.c); return(query_result == -1 || query_result == 0); }; CurveVertices = new int[Curve.VertexCount]; for (int i = 0; i < Curve.VertexCount; ++i) { Vector2d vInsert = Curve[i]; bool inserted = false; // find the triangle that contains this curve point int contain_tid = DMesh3.InvalidID; if (triSpatial != null) { contain_tid = triSpatial.FindContainingTriangle(vInsert, inTriangleF); } else { 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) { contain_tid = tid; break; } } } // if we found one, insert the point via face-poke or edge-split, // unless it is exactly at existing vertex, in which case we can re-use it if (contain_tid != DMesh3.InvalidID) { Index3i tv = Mesh.GetTriangle(contain_tid); Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c)); // SpatialEpsilon is our zero-tolerance, so merge if we are closer than that bool is_existing_v; int vid = insert_corner_from_bary(i, contain_tid, bary, 0.01, 100 * SpatialEpsilon, out is_existing_v); if (vid > 0) { CurveVertices[i] = vid; if (is_existing_v) { MeshVertsOnCurve.Add(vid); } inserted = true; } } // if we did not find containing triangle, // try matching with any existing vertices. // This can happen if curve point is right on mesh border... if (inserted == false) { foreach (int vid in Mesh.VertexIndices()) { Vector2d v = PointF(vid); if (vInsert.Distance(v) < SpatialEpsilon) { CurveVertices[i] = vid; MeshVertsOnCurve.Add(vid); inserted = true; } } } // TODO: also case where curve point is right on mesh border edge, // and so it ends up being outside all triangles? if (inserted == false) { throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex " + i.ToString() + " is not inside or on any mesh triangle!"); } } }
public static bool RandomizeMesh(g3.DMesh3 mesh, out g3.DMesh3 outputMesh, double amount, double moveTries) { 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; } } } DMesh3 projectMeshCopy = new DMesh3(mesh); outputMesh = new DMesh3(mesh); if (faces.Count == 0) { return(false); } DMeshAABBTree3 treeProject = new DMeshAABBTree3(projectMeshCopy); treeProject.Build(); Random r = new Random(); bool result = false; //for (int i = 0; i < moveTries; i++) //{ double faceArea = 0; foreach (var f in faces) { faceArea += f.Value.TriangleArea(outputMesh); } faceArea /= faces.Count; foreach (var f in faces) { result |= f.Value.Randomize(outputMesh, treeProject, r, amount, moveTries, faceArea); } double newFaceArea = 0; foreach (var f in faces) { newFaceArea += f.Value.TriangleArea(outputMesh); } newFaceArea /= faces.Count; return(result); }
//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]); } } } } } }
/// <summary> /// Disconnect the given triangles from their neighbours, by duplicating "boundary" vertices, ie /// vertices on edges for which one triangle is in-set and the other is not. /// If bComputeEdgePairs is true, we return list of old/new edge pairs (useful for stitching) /// [TODO] currently boundary-edge behaviour is to *not* duplicate boundary verts /// </summary> public bool SeparateTriangles(IEnumerable <int> triangles, bool bComputeEdgePairs, out List <Index2i> EdgePairs) { HashSet <int> in_set = new HashSet <int>(triangles); Dictionary <int, int> VertexMap = new Dictionary <int, int>(); EdgePairs = null; HashSet <int> edges = null; List <Index2i> OldEdgeVerts = null; if (bComputeEdgePairs) { EdgePairs = new List <Index2i>(); edges = new HashSet <int>(); OldEdgeVerts = new List <Index2i>(); } // duplicate vertices on edges that are on boundary of triangles roi foreach (int tid in triangles) { Index3i te = Mesh.GetTriEdges(tid); for (int j = 0; j < 3; ++j) { Index2i et = Mesh.GetEdgeT(te[j]); // [TODO] what about behavior where we want to also duplicate boundary verts?? if (et.b == DMesh3.InvalidID || (et.a == tid && in_set.Contains(et.b)) || (et.b == tid && in_set.Contains(et.a))) { te[j] = -1; } } for (int j = 0; j < 3; ++j) { if (te[j] == -1) { continue; } Index2i ev = Mesh.GetEdgeV(te[j]); if (VertexMap.ContainsKey(ev.a) == false) { VertexMap[ev.a] = Mesh.AppendVertex(Mesh, ev.a); } if (VertexMap.ContainsKey(ev.b) == false) { VertexMap[ev.b] = Mesh.AppendVertex(Mesh, ev.b); } if (bComputeEdgePairs && edges.Contains(te[j]) == false) { edges.Add(te[j]); OldEdgeVerts.Add(ev); EdgePairs.Add(new Index2i(te[j], -1)); } } } // update triangles foreach (int tid in triangles) { Index3i tv = Mesh.GetTriangle(tid); Index3i tv_new = tv; for (int j = 0; j < 3; ++j) { int newv; if (VertexMap.TryGetValue(tv[j], out newv)) { tv_new[j] = newv; } } if (tv_new != tv) { Mesh.SetTriangle(tid, tv_new); } } if (bComputeEdgePairs) { for (int k = 0; k < EdgePairs.Count; ++k) { Index2i old_ev = OldEdgeVerts[k]; int new_a = VertexMap[old_ev.a]; int new_b = VertexMap[old_ev.b]; int new_eid = Mesh.FindEdge(new_a, new_b); Util.gDevAssert(new_eid != DMesh3.InvalidID); EdgePairs[k] = new Index2i(EdgePairs[k].a, new_eid); } } return(true); }
public void FindConnectedT() { Components = new List <Component>(); int NT = Mesh.MaxTriangleID; // [TODO] could use Euler formula to determine if mesh is closed genus-0... Func <int, bool> filter_func = (i) => { return(Mesh.IsTriangle(i)); }; if (FilterF != null) { filter_func = (i) => { return(Mesh.IsTriangle(i) && FilterF(i)); } } ; // initial active set contains all valid triangles byte[] active = new byte[Mesh.MaxTriangleID]; Interval1i activeRange = Interval1i.Empty; if (FilterSet != null) { for (int i = 0; i < NT; ++i) { active[i] = 255; } foreach (int tid in FilterSet) { bool bValid = filter_func(tid); if (bValid) { active[tid] = 0; activeRange.Contain(tid); } } } else { for (int i = 0; i < NT; ++i) { bool bValid = filter_func(i); if (bValid) { active[i] = 0; activeRange.Contain(i); } else { active[i] = 255; } } } // temporary buffers List <int> queue = new List <int>(NT / 10); List <int> cur_comp = new List <int>(NT / 10); // keep finding valid seed triangles and growing connected components // until we are done IEnumerable <int> range = (FilterSet != null) ? FilterSet : activeRange; foreach (int i in range) { //for ( int i = 0; i < NT; ++i ) { if (active[i] == 255) { continue; } int seed_t = i; if (SeedFilterF != null && SeedFilterF(seed_t) == false) { continue; } queue.Add(seed_t); active[seed_t] = 1; // in queue while (queue.Count > 0) { int cur_t = queue[queue.Count - 1]; queue.RemoveAt(queue.Count - 1); active[cur_t] = 2; // tri has been processed cur_comp.Add(cur_t); Index3i nbrs = Mesh.GetTriNeighbourTris(cur_t); for (int j = 0; j < 3; ++j) { int nbr_t = nbrs[j]; if (nbr_t != DMesh3.InvalidID && active[nbr_t] == 0) { queue.Add(nbr_t); active[nbr_t] = 1; // in queue } } } Component comp = new Component() { Indices = cur_comp.ToArray() }; Components.Add(comp); // remove tris in this component from active set for (int j = 0; j < comp.Indices.Length; ++j) { active[comp.Indices[j]] = 255; } cur_comp.Clear(); queue.Clear(); } }
public static bool is_ordered(int a, int b, ref Index3i tri_verts) { return((tri_verts.a == a && tri_verts.b == b) || (tri_verts.b == a && tri_verts.c == b) || (tri_verts.c == a && tri_verts.a == b)); }
public static int find_tri_ordered_edge(int a, int b, Index3i tri_verts) { return(find_tri_ordered_edge(a, b, ref tri_verts)); }
// Remove a tID from the mesh. Also removes any unreferenced edges after tri is removed. // If bRemoveIsolatedVertices is false, then if you remove all tris from a vert, that vert is also removed. // If bPreserveManifold, we check that you will not create a bowtie vertex (and return false). // If this check is not done, you have to make sure you don't create a bowtie, because other // code assumes we don't have bowties, and will not handle it properly public bool RemoveTriangle(int tID, bool bRemoveIsolatedVertices = true, bool bPreserveManifold = true) { if (!triangles_refcount.isValid(tID)) { Debug.Assert(false); return(false); } Index3i tv = GetTriangle(tID); Index3i te = GetTriEdges(tID); // if any tri vtx is a boundary vtx connected to two interior edges, then // we cannot remove this triangle because it would create a bowtie vertex! // (that vtx already has 2 boundary edges, and we would add two more) if (bPreserveManifold) { for (int j = 0; j < 3; ++j) { if (vertex_is_boundary(tv[j])) { if (edge_is_boundary(te[j]) == false && edge_is_boundary(te[(j + 2) % 3]) == false) { return(false); } } } } // Remove triangle from its edges. if edge has no triangles left, // then it is removed. for (int j = 0; j < 3; ++j) { int eid = te[j]; replace_edge_triangle(eid, tID, InvalidID); if (edges[4 * eid + 2] == InvalidID) { int a = edges[4 * eid]; List <int> edges_a = vertex_edges[a]; edges_a.Remove(eid); int b = edges[4 * eid + 1]; List <int> edges_b = vertex_edges[b]; edges_b.Remove(eid); edges_refcount.decrement(eid); } } // free this triangle triangles_refcount.decrement(tID); Debug.Assert(triangles_refcount.isValid(tID) == false); // Decrement vertex refcounts. If any hit 1 and we got remove-isolated flag, // we need to remove that vertex for (int j = 0; j < 3; ++j) { int vid = tv[j]; vertices_refcount.decrement(vid); if (bRemoveIsolatedVertices && vertices_refcount.refCount(vid) == 1) { vertices_refcount.decrement(vid); Debug.Assert(vertices_refcount.isValid(vid) == false); vertex_edges[vid] = null; } } updateTimeStamp(); return(true); }
public MeshResult CollapseEdge(int vKeep, int vRemove, out EdgeCollapseInfo collapse) { collapse = new EdgeCollapseInfo(); if (IsVertex(vKeep) == false || IsVertex(vRemove) == false) { return(MeshResult.Failed_NotAnEdge); } int b = vKeep; // renaming for sanity. We remove a and keep b int a = vRemove; List <int> edges_b = vertex_edges[b]; int eab = find_edge(a, b); if (eab == InvalidID) { return(MeshResult.Failed_NotAnEdge); } int t0 = edges[4 * eab + 2]; Index3i T0tv = GetTriangle(t0); int c = IndexUtil.find_tri_other_vtx(a, b, T0tv); // look up opposing triangle/vtx if we are not in boundary case bool bIsBoundaryEdge = false; int d = InvalidID; int t1 = edges[4 * eab + 3]; if (t1 != InvalidID) { Index3i T1tv = GetTriangle(t1); d = IndexUtil.find_tri_other_vtx(a, b, T1tv); if (c == d) { return(MeshResult.Failed_FoundDuplicateTriangle); } } else { bIsBoundaryEdge = true; } // We cannot collapse if edge lists of a and b share vertices other // than c and d (because then we will make a triangle [x b b]. // Unfortunately I cannot see a way to do this more efficiently than brute-force search // [TODO] if we had tri iterator for a, couldn't we check each tri for b (skipping t0 and t1) ? List <int> edges_a = vertex_edges[a]; int edges_a_count = 0; foreach (int eid_a in edges_a) { int vax = edge_other_v(eid_a, a); edges_a_count++; if (vax == b || vax == c || vax == d) { continue; } foreach (int eid_b in edges_b) { if (edge_other_v(eid_b, b) == vax) { return(MeshResult.Failed_InvalidNeighbourhood); } } } // We cannot collapse if we have a tetrahedron. In this case a has 3 nbr edges, // and edge cd exists. But that is not conclusive, we also have to check that // cd is an internal edge, and that each of its tris contain a or b if (edges_a_count == 3 && bIsBoundaryEdge == false) { int edc = find_edge(d, c); int edc_i = 4 * edc; if (edc != InvalidID && edges[edc_i + 3] != InvalidID) { int edc_t0 = edges[edc_i + 2]; int edc_t1 = edges[edc_i + 3]; if ((tri_has_v(edc_t0, a) && tri_has_v(edc_t1, b)) || (tri_has_v(edc_t0, b) && tri_has_v(edc_t1, a))) { return(MeshResult.Failed_CollapseTetrahedron); } } } else if (edges_a_count == 2 && bIsBoundaryEdge == true) { // cannot collapse edge if we are down to a single triangle if (edges_b.Count == 2 && vertex_edges[c].Count == 2) { return(MeshResult.Failed_CollapseTriangle); } } // [RMS] this was added from C++ version...seems like maybe I never hit // this case? Conceivably could be porting bug but looking at the // C++ code I cannot see how we could possibly have caught this case... // // cannot collapse an edge where both vertices are boundary vertices // because that would create a bowtie // // NOTE: potentially scanning all edges here...couldn't we // pick up eac/bc/ad/bd as we go? somehow? if (bIsBoundaryEdge == false && vertex_is_boundary(a) && vertex_is_boundary(b)) { return(MeshResult.Failed_InvalidNeighbourhood); } // 1) remove edge ab from vtx b // 2) find edges ad and ac, and tris tad, tac across those edges (will use later) // 3) for other edges, replace a with b, and add that edge to b // 4) replace a with b in all triangles connected to a int ead = InvalidID, eac = InvalidID; int tad = InvalidID, tac = InvalidID; foreach (int eid in edges_a) { int o = edge_other_v(eid, a); if (o == b) { if (edges_b.Remove(eid) != true) { debug_fail("remove case o == b"); } } else if (o == c) { eac = eid; if (vertex_edges[c].Remove(eid) != true) { debug_fail("remove case o == c"); } tac = edge_other_t(eid, t0); } else if (o == d) { ead = eid; if (vertex_edges[d].Remove(eid) != true) { debug_fail("remove case o == c, step 1"); } tad = edge_other_t(eid, t1); } else { if (replace_edge_vertex(eid, a, b) == -1) { debug_fail("remove case else"); } edges_b.Add(eid); } // [TODO] perhaps we can already have unique tri list because of the manifold-nbrhood check we need to do... for (int j = 0; j < 2; ++j) { int t_j = edges[4 * eid + 2 + j]; if (t_j != InvalidID && t_j != t0 && t_j != t1) { if (tri_has_v(t_j, a)) { if (replace_tri_vertex(t_j, a, b) == -1) { debug_fail("remove last check"); } vertices_refcount.increment(b); vertices_refcount.decrement(a); } } } } int ebc = InvalidID, ebd = InvalidID; if (bIsBoundaryEdge == false) { // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.refCount(a) == 3); // in t0,t1, and initial ref vertices_refcount.decrement(a, 3); Debug.Assert(vertices_refcount.isValid(a) == false); // remove triangles T0 and T1, and update b/c/d refcounts triangles_refcount.decrement(t0); triangles_refcount.decrement(t1); vertices_refcount.decrement(c); vertices_refcount.decrement(d); vertices_refcount.decrement(b, 2); Debug.Assert(triangles_refcount.isValid(t0) == false); Debug.Assert(triangles_refcount.isValid(t1) == false); // remove edges ead, eab, eac edges_refcount.decrement(ead); edges_refcount.decrement(eab); edges_refcount.decrement(eac); Debug.Assert(edges_refcount.isValid(ead) == false); Debug.Assert(edges_refcount.isValid(eab) == false); Debug.Assert(edges_refcount.isValid(eac) == false); // replace t0 and t1 in edges ebd and ebc that we kept ebd = find_edge(b, d); ebc = find_edge(b, c); if (replace_edge_triangle(ebd, t1, tad) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } if (replace_edge_triangle(ebc, t0, tac) == -1) { debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tad and tac if (tad != InvalidID) { if (replace_triangle_edge(tad, ead, ebd) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } } if (tac != InvalidID) { if (replace_triangle_edge(tac, eac, ebc) == -1) { debug_fail("isboundary=false branch, ebd replace triangle"); } } } else { // this is basically same code as above, just not referencing t1/d // remove all edges from vtx a, then remove vtx a edges_a.Clear(); Debug.Assert(vertices_refcount.refCount(a) == 2); // in t0 and initial ref vertices_refcount.decrement(a, 2); Debug.Assert(vertices_refcount.isValid(a) == false); // remove triangle T0 and update b/c refcounts triangles_refcount.decrement(t0); vertices_refcount.decrement(c); vertices_refcount.decrement(b); Debug.Assert(triangles_refcount.isValid(t0) == false); // remove edges eab and eac edges_refcount.decrement(eab); edges_refcount.decrement(eac); Debug.Assert(edges_refcount.isValid(eab) == false); Debug.Assert(edges_refcount.isValid(eac) == false); // replace t0 in edge ebc that we kept ebc = find_edge(b, c); if (replace_edge_triangle(ebc, t0, tac) == -1) { debug_fail("isboundary=false branch, ebc replace triangle"); } // update tri-edge-nbrs in tac if (tac != InvalidID) { if (replace_triangle_edge(tac, eac, ebc) == -1) { debug_fail("isboundary=true branch, ebd replace triangle"); } } } collapse.vKept = vKeep; collapse.vRemoved = vRemove; collapse.bIsBoundary = bIsBoundaryEdge; collapse.eCollapsed = eab; collapse.tRemoved0 = t0; collapse.tRemoved1 = t1; collapse.eRemoved0 = eac; collapse.eRemoved1 = ead; collapse.eKept0 = ebc; collapse.eKept1 = ebd; updateTimeStamp(); return(MeshResult.Ok); }
public MeshResult SplitEdge(int eab, out EdgeSplitInfo split) { split = new EdgeSplitInfo(); if (!IsEdge(eab)) { return(MeshResult.Failed_NotAnEdge); } // look up primary edge & triangle int eab_i = 4 * eab; int a = edges[eab_i], b = edges[eab_i + 1]; int t0 = edges[eab_i + 2]; Index3i T0tv = GetTriangle(t0); int[] T0tv_array = T0tv.array; int c = IndexUtil.orient_tri_edge_and_find_other_vtx(ref a, ref b, T0tv_array); // create new vertex Vector3d vNew = 0.5 * (GetVertex(a) + GetVertex(b)); int f = AppendVertex(vNew); // quite a bit of code is duplicated between boundary and non-boundary case, but it // is too hard to follow later if we factor it out... if (edge_is_boundary(eab)) { // look up edge bc, which needs to be modified Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; // rewrite existing triangle replace_tri_vertex(t0, b, f); // add new second triangle int t2 = add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.insert(triangle_groups[t0], t2); } // rewrite edge bc, create edge af replace_edge_triangle(ebc, t0, t2); int eaf = eab; replace_edge_vertex(eaf, b, f); vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges fb and fc int efb = add_edge(f, b, t2); int efc = add_edge(f, c, t0, t2); // update triangle edge-nbrs replace_triangle_edge(t0, ebc, efc); set_triangle_edges(t2, efb, ebc, efc); // update vertex refcounts vertices_refcount.increment(c); vertices_refcount.increment(f, 2); split.bIsBoundary = true; split.vNew = f; split.eNew = efb; updateTimeStamp(); return(MeshResult.Ok); } else // interior triangle branch // look up other triangle { int t1 = edges[eab_i + 3]; Index3i T1tv = GetTriangle(t1); int[] T1tv_array = T1tv.array; int d = IndexUtil.find_tri_other_vtx(a, b, T1tv_array); // look up edges that we are going to need to update // [TODO OPT] could use ordering to reduce # of compares here Index3i T0te = GetTriEdges(t0); int ebc = T0te[IndexUtil.find_edge_index_in_tri(b, c, T0tv_array)]; Index3i T1te = GetTriEdges(t1); int edb = T1te[IndexUtil.find_edge_index_in_tri(d, b, T1tv_array)]; // rewrite existing triangles replace_tri_vertex(t0, b, f); replace_tri_vertex(t1, b, f); // add two new triangles to close holes we just created int t2 = add_triangle_only(f, b, c, InvalidID, InvalidID, InvalidID); int t3 = add_triangle_only(f, d, b, InvalidID, InvalidID, InvalidID); if (triangle_groups != null) { triangle_groups.insert(triangle_groups[t0], t2); triangle_groups.insert(triangle_groups[t1], t3); } // update the edges we found above, to point to new triangles replace_edge_triangle(ebc, t0, t2); replace_edge_triangle(edb, t1, t3); // edge eab became eaf int eaf = eab; //Edge * eAF = eAB; replace_edge_vertex(eaf, b, f); // update a/b/f vertex-edges vertex_edges[b].Remove(eab); vertex_edges[f].Add(eaf); // create new edges connected to f (also updates vertex-edges) int efb = add_edge(f, b, t2, t3); int efc = add_edge(f, c, t0, t2); int edf = add_edge(d, f, t1, t3); // update triangle edge-nbrs replace_triangle_edge(t0, ebc, efc); replace_triangle_edge(t1, edb, edf); set_triangle_edges(t2, efb, ebc, efc); set_triangle_edges(t3, edf, edb, efb); // update vertex refcounts vertices_refcount.increment(c); vertices_refcount.increment(d); vertices_refcount.increment(f, 4); split.bIsBoundary = false; split.vNew = f; split.eNew = efb; updateTimeStamp(); return(MeshResult.Ok); } }
/// <summary> /// 1) Find intersection segments /// 2) sort onto existing input mesh vtx/edge/face /// </summary> void find_segments() { var SegVtxMap = new Dictionary <Vector3d, SegmentVtx>(); // find intersection segments // TODO: intersection polygons // TODO: do we need to care about intersection vertices? var targetSpatial = new DMeshAABBTree3(Target, true); var 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]; var points = new Vector3dTuple2(isect.point0, isect.point1); var 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; } var 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 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 (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 (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 (WantGraphEdgeInfo) { add_edge_edge(graph_eid, tid, new Index2i(triEdges[e0], triEdges[e1]), new Index2i(ev0, ev1)); } } } } Vertices = null; }
// This function checks that the mesh is well-formed, ie all internal data // structures are consistent public bool CheckValidity(bool bAllowNonManifoldVertices = false) { int[] triToVtxRefs = new int[this.MaxVertexID]; if (normals != null) { DMESH_CHECK_OR_FAIL(normals.size == vertices.size); } if (colors != null) { DMESH_CHECK_OR_FAIL(colors.size == vertices.size); } if (uv != null) { DMESH_CHECK_OR_FAIL(uv.size / 2 == vertices.size / 3); } if (triangle_groups != null) { DMESH_CHECK_OR_FAIL(triangle_groups.size == triangles.size / 3); } foreach (int tID in TriangleIndices()) { DMESH_CHECK_OR_FAIL(IsTriangle(tID)); DMESH_CHECK_OR_FAIL(triangles_refcount.refCount(tID) == 1); // vertices must exist Index3i tv = GetTriangle(tID); for (int j = 0; j < 3; ++j) { DMESH_CHECK_OR_FAIL(IsVertex(tv[j])); triToVtxRefs[tv[j]] += 1; } // edges must exist and reference this tri Index3i e = new Index3i(); for (int j = 0; j < 3; ++j) { int a = tv[j], b = tv[(j + 1) % 3]; e[j] = FindEdge(a, b); DMESH_CHECK_OR_FAIL(e[j] != InvalidID); DMESH_CHECK_OR_FAIL(edge_has_t(e[j], tID)); DMESH_CHECK_OR_FAIL(e[j] == FindEdgeFromTri(a, b, tID)); } DMESH_CHECK_OR_FAIL(e[0] != e[1] && e[0] != e[2] && e[1] != e[2]); // tri nbrs must exist and reference this tri, or same edge must be boundary edge Index3i te = GetTriEdges(tID); for (int j = 0; j < 3; ++j) { int eid = te[j]; DMESH_CHECK_OR_FAIL(IsEdge(eid)); int tOther = edge_other_t(eid, tID); if (tOther == InvalidID) { DMESH_CHECK_OR_FAIL(tri_is_boundary(tID)); continue; } DMESH_CHECK_OR_FAIL(tri_has_neighbour_t(tOther, tID) == true); // edge must have same two verts as tri for same index int a = tv[j], b = tv[(j + 1) % 3]; Index2i ev = GetEdgeV(te[j]); DMESH_CHECK_OR_FAIL(IndexUtil.same_pair_unordered(a, b, ev[0], ev[1])); // also check that nbr edge has opposite orientation Index3i othertv = GetTriangle(tOther); int found = IndexUtil.find_tri_ordered_edge(b, a, othertv.array); DMESH_CHECK_OR_FAIL(found != InvalidID); } } // edge verts/tris must exist foreach (int eID in EdgeIndices()) { DMESH_CHECK_OR_FAIL(IsEdge(eID)); DMESH_CHECK_OR_FAIL(edges_refcount.refCount(eID) == 1); Index2i ev = GetEdgeV(eID); Index2i et = GetEdgeT(eID); DMESH_CHECK_OR_FAIL(IsVertex(ev[0])); DMESH_CHECK_OR_FAIL(IsVertex(ev[1])); DMESH_CHECK_OR_FAIL(ev[0] < ev[1]); DMESH_CHECK_OR_FAIL(IsTriangle(et[0])); if (et[1] != InvalidID) { DMESH_CHECK_OR_FAIL(IsTriangle(et[1])); } } // verify compact check bool is_compact = vertices_refcount.is_dense; for (int vid = 0; vid < vertices.Length; ++vid) { DMESH_CHECK_OR_FAIL(vertices_refcount.isValid(vid)); } // vertex edges must exist and reference this vert foreach (int vID in VertexIndices()) { DMESH_CHECK_OR_FAIL(IsVertex(vID)); List <int> l = vertex_edges[vID]; foreach (int edgeid in l) { DMESH_CHECK_OR_FAIL(IsEdge(edgeid)); DMESH_CHECK_OR_FAIL(edge_has_v(edgeid, vID)); int otherV = edge_other_v(edgeid, vID); int e2 = find_edge(vID, otherV); DMESH_CHECK_OR_FAIL(e2 != InvalidID); DMESH_CHECK_OR_FAIL(e2 == edgeid); e2 = find_edge(otherV, vID); DMESH_CHECK_OR_FAIL(e2 != InvalidID); DMESH_CHECK_OR_FAIL(e2 == edgeid); } List <int> vTris = new List <int>(), vTris2 = new List <int>(); GetVtxTriangles(vID, vTris, false); GetVtxTriangles(vID, vTris2, true); DMESH_CHECK_OR_FAIL(vTris.Count == vTris2.Count); //System.Console.WriteLine(string.Format("{0} {1} {2}", vID, vTris.Count, GetVtxEdges(vID).Count)); if (bAllowNonManifoldVertices) { DMESH_CHECK_OR_FAIL(vTris.Count <= GetVtxEdges(vID).Count); } else { DMESH_CHECK_OR_FAIL(vTris.Count == GetVtxEdges(vID).Count || vTris.Count == GetVtxEdges(vID).Count - 1); } DMESH_CHECK_OR_FAIL(vertices_refcount.refCount(vID) == vTris.Count + 1); DMESH_CHECK_OR_FAIL(triToVtxRefs[vID] == vTris.Count); foreach (int tID in vTris) { DMESH_CHECK_OR_FAIL(tri_has_v(tID, vID)); } // check that edges around vert only references tris above, and reference all of them! List <int> vRemoveTris = new List <int>(vTris); foreach (int edgeid in l) { Index2i edget = GetEdgeT(edgeid); DMESH_CHECK_OR_FAIL(vTris.Contains(edget[0])); if (edget[1] != InvalidID) { DMESH_CHECK_OR_FAIL(vTris.Contains(edget[1])); } vRemoveTris.Remove(edget[0]); if (edget[1] != InvalidID) { vRemoveTris.Remove(edget[1]); } } DMESH_CHECK_OR_FAIL(vRemoveTris.Count == 0); } return(true); }
// insert point at bary_coords inside tid. If point is at vtx, just use that vtx. // If it is on an edge, do an edge split. Otherwise poke face. int insert_corner_from_bary(int iCorner, int tid, Vector3d bary_coords, double bary_tol, double spatial_tol, out bool is_existing_v) { is_existing_v = false; Vector2d vInsert = Curve[iCorner]; Index3i tv = Mesh.GetTriangle(tid); // handle cases where corner is on a vertex int cornerv = -1; if (bary_coords.x > 1 - bary_tol) { cornerv = tv.a; } else if (bary_coords.y > 1 - bary_tol) { cornerv = tv.b; } else if (bary_coords.z > 1 - bary_tol) { cornerv = tv.c; } if (cornerv != -1 && PointF(cornerv).Distance(vInsert) < spatial_tol) { is_existing_v = true; return(cornerv); } // handle cases where corner is on an edge int split_edge = -1; if (bary_coords.x < bary_tol) { split_edge = 1; } else if (bary_coords.y < bary_tol) { split_edge = 2; } else if (bary_coords.z < bary_tol) { split_edge = 0; } if (split_edge >= 0) { int eid = Mesh.GetTriEdge(tid, split_edge); Index2i ev = Mesh.GetEdgeV(eid); Segment2d seg = new Segment2d(PointF(ev.a), PointF(ev.b)); if (seg.DistanceSquared(vInsert) < spatial_tol * spatial_tol) { Index2i et = Mesh.GetEdgeT(eid); spatial_remove_triangles(et.a, et.b); DMesh3.EdgeSplitInfo split_info; MeshResult splitResult = Mesh.SplitEdge(eid, out split_info); if (splitResult != MeshResult.Ok) { throw new Exception("MeshInsertUVPolyCurve.insert_corner_from_bary: edge split failed in case sum==2 - " + splitResult.ToString()); } SetPointF(split_info.vNew, vInsert); spatial_add_triangles(et.a, et.b); spatial_add_triangles(split_info.eNewT2, split_info.eNewT3); return(split_info.vNew); } } spatial_remove_triangle(tid); // otherwise corner is inside triangle DMesh3.PokeTriangleInfo pokeinfo; MeshResult result = Mesh.PokeTriangle(tid, bary_coords, out pokeinfo); if (result != MeshResult.Ok) { throw new Exception("MeshInsertUVPolyCurve.insert_corner_from_bary: face poke failed - " + result.ToString()); } SetPointF(pokeinfo.new_vid, vInsert); spatial_add_triangle(tid); spatial_add_triangle(pokeinfo.new_t1); spatial_add_triangle(pokeinfo.new_t2); return(pokeinfo.new_vid); }
// (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); if (UseTriSpatial) { AxisAlignedBox3d bounds3 = Mesh.CachedBounds; AxisAlignedBox2d bounds2 = new AxisAlignedBox2d(bounds3.Min.xy, bounds3.Max.xy); triSpatial = new TriangleBinsGrid2d(bounds2, 32); foreach (int tid in Mesh.TriangleIndices()) { spatial_add_triangle(tid); } } Func <int, Vector2d, bool> inTriangleF = (tid, pos) => { Index3i tv = Mesh.GetTriangle(tid); int query_result = query.ToTriangleUnsigned(pos, tv.a, tv.b, tv.c); return(query_result == -1 || query_result == 0); }; CurveVertices = new int[Curve.VertexCount]; for (int i = 0; i < Curve.VertexCount; ++i) { Vector2d vInsert = Curve[i]; bool inserted = false; int contain_tid = DMesh3.InvalidID; if (triSpatial != null) { contain_tid = triSpatial.FindContainingTriangle(vInsert, inTriangleF); } else { 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) { contain_tid = tid; break; } } } if (contain_tid != DMesh3.InvalidID) { Index3i tv = Mesh.GetTriangle(contain_tid); Vector3d bary = MathUtil.BarycentricCoords(vInsert, PointF(tv.a), PointF(tv.b), PointF(tv.c)); int vid = insert_corner_from_bary(i, contain_tid, bary); if (vid > 0) // this should be always happening.. { CurveVertices[i] = vid; inserted = true; } else { throw new Exception("MeshInsertUVPolyCurve.insert_corners: failed to insert vertex " + i.ToString()); } } if (inserted == false) { throw new Exception("MeshInsertUVPolyCurve.insert_corners: curve vertex " + i.ToString() + " is not inside or on any mesh triangle!"); } } }
// insert point at bary_coords inside tid. If point is at vtx, just use that vtx. // If it is on an edge, do an edge split. Otherwise poke face. int insert_corner_from_bary(int iCorner, int tid, Vector3d bary_coords, double tol = MathUtil.ZeroTolerance) { Vector2d vInsert = Curve[iCorner]; Index3i tv = Mesh.GetTriangle(tid); // handle cases where corner is on a vertex if (bary_coords.x > 1 - tol) { return(tv.a); } else if (bary_coords.y > 1 - tol) { return(tv.b); } else if (bary_coords.z > 1 - tol) { return(tv.c); } // handle cases where corner is on an edge int split_edge = -1; if (bary_coords.x < tol) { split_edge = 1; } else if (bary_coords.y < tol) { split_edge = 2; } else if (bary_coords.z < tol) { split_edge = 0; } if (split_edge >= 0) { int eid = Mesh.GetTriEdge(tid, split_edge); Index2i ev = Mesh.GetEdgeT(eid); spatial_remove_triangles(ev.a, ev.b); DMesh3.EdgeSplitInfo split_info; MeshResult splitResult = Mesh.SplitEdge(eid, out split_info); if (splitResult != MeshResult.Ok) { throw new Exception("MeshInsertUVPolyCurve.insert_corner_from_bary: edge split failed in case sum==2 - " + splitResult.ToString()); } SetPointF(split_info.vNew, vInsert); spatial_add_triangles(ev.a, ev.b); spatial_add_triangles(split_info.eNewT2, split_info.eNewT3); return(split_info.vNew); } spatial_remove_triangle(tid); // otherwise corner is inside triangle DMesh3.PokeTriangleInfo pokeinfo; MeshResult result = Mesh.PokeTriangle(tid, bary_coords, out pokeinfo); if (result != MeshResult.Ok) { throw new Exception("MeshInsertUVPolyCurve.insert_corner_from_bary: face poke failed - " + result.ToString()); } spatial_add_triangle(tid); spatial_add_triangle(pokeinfo.new_t1); spatial_add_triangle(pokeinfo.new_t2); SetPointF(pokeinfo.new_vid, vInsert); return(pokeinfo.new_vid); }
/// <summary> // This function checks that the mesh is well-formed, ie all internal data // structures are consistent /// </summary> public bool CheckValidity(bool bAllowNonManifoldVertices = false, FailMode eFailMode = FailMode.Throw) { int[] triToVtxRefs = new int[this.MaxVertexID]; bool is_ok = true; Action <bool> CheckOrFailF = (b) => { is_ok = is_ok && b; }; if (eFailMode == FailMode.DebugAssert) { CheckOrFailF = (b) => { Debug.Assert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.gDevAssert) { CheckOrFailF = (b) => { Util.gDevAssert(b); is_ok = is_ok && b; }; } else if (eFailMode == FailMode.Throw) { CheckOrFailF = (b) => { if (b == false) { throw new Exception("DMesh3.CheckValidity: check failed"); } }; } if (normals != null) { CheckOrFailF(normals.size == vertices.size); } if (colors != null) { CheckOrFailF(colors.size == vertices.size); } if (uv != null) { CheckOrFailF(uv.size / 2 == vertices.size / 3); } if (triangle_groups != null) { CheckOrFailF(triangle_groups.size == triangles.size / 3); } foreach (int tID in TriangleIndices()) { CheckOrFailF(IsTriangle(tID)); CheckOrFailF(triangles_refcount.refCount(tID) == 1); // vertices must exist Index3i tv = GetTriangle(tID); for (int j = 0; j < 3; ++j) { CheckOrFailF(IsVertex(tv[j])); triToVtxRefs[tv[j]] += 1; } // edges must exist and reference this tri var e = new Index3i(); for (int j = 0; j < 3; ++j) { int a = tv[j], b = tv[(j + 1) % 3]; e[j] = FindEdge(a, b); CheckOrFailF(e[j] != InvalidID); CheckOrFailF(edge_has_t(e[j], tID)); CheckOrFailF(e[j] == FindEdgeFromTri(a, b, tID)); } CheckOrFailF(e[0] != e[1] && e[0] != e[2] && e[1] != e[2]); // tri nbrs must exist and reference this tri, or same edge must be boundary edge Index3i te = GetTriEdges(tID); for (int j = 0; j < 3; ++j) { int eid = te[j]; CheckOrFailF(IsEdge(eid)); int tOther = edge_other_t(eid, tID); if (tOther == InvalidID) { CheckOrFailF(tri_is_boundary(tID)); continue; } CheckOrFailF(tri_has_neighbour_t(tOther, tID) == true); // edge must have same two verts as tri for same index int a = tv[j], b = tv[(j + 1) % 3]; Index2i ev = GetEdgeV(te[j]); CheckOrFailF(IndexUtil.same_pair_unordered(a, b, ev[0], ev[1])); // also check that nbr edge has opposite orientation Index3i othertv = GetTriangle(tOther); int found = IndexUtil.find_tri_ordered_edge(b, a, othertv.array); CheckOrFailF(found != InvalidID); } } // edge verts/tris must exist foreach (int eID in EdgeIndices()) { CheckOrFailF(IsEdge(eID)); CheckOrFailF(edges_refcount.refCount(eID) == 1); Index2i ev = GetEdgeV(eID); Index2i et = GetEdgeT(eID); CheckOrFailF(IsVertex(ev[0])); CheckOrFailF(IsVertex(ev[1])); CheckOrFailF(et[0] != InvalidID); CheckOrFailF(ev[0] < ev[1]); CheckOrFailF(IsTriangle(et[0])); if (et[1] != InvalidID) { CheckOrFailF(IsTriangle(et[1])); } } // verify compact check bool is_compact = vertices_refcount.is_dense; if (is_compact) { for (int vid = 0; vid < vertices.Length / 3; ++vid) { CheckOrFailF(vertices_refcount.isValid(vid)); } } // vertex edges must exist and reference this vert foreach (int vID in VertexIndices()) { CheckOrFailF(IsVertex(vID)); Vector3d v = GetVertex(vID); CheckOrFailF(double.IsNaN(v.LengthSquared) == false); CheckOrFailF(double.IsInfinity(v.LengthSquared) == false); foreach (int edgeid in vertex_edges.ValueItr(vID)) { CheckOrFailF(IsEdge(edgeid)); CheckOrFailF(edge_has_v(edgeid, vID)); int otherV = edge_other_v(edgeid, vID); int e2 = find_edge(vID, otherV); CheckOrFailF(e2 != InvalidID); CheckOrFailF(e2 == edgeid); e2 = find_edge(otherV, vID); CheckOrFailF(e2 != InvalidID); CheckOrFailF(e2 == edgeid); } foreach (int nbr_vid in VtxVerticesItr(vID)) { CheckOrFailF(IsVertex(nbr_vid)); int edge = find_edge(vID, nbr_vid); CheckOrFailF(IsEdge(edge)); } List <int> vTris = new List <int>(), vTris2 = new List <int>(); GetVtxTriangles(vID, vTris, false); GetVtxTriangles(vID, vTris2, true); CheckOrFailF(vTris.Count == vTris2.Count); //System.Console.WriteLine(string.Format("{0} {1} {2}", vID, vTris.Count, GetVtxEdges(vID).Count)); if (bAllowNonManifoldVertices) { CheckOrFailF(vTris.Count <= GetVtxEdgeCount(vID)); } else { CheckOrFailF(vTris.Count == GetVtxEdgeCount(vID) || vTris.Count == GetVtxEdgeCount(vID) - 1); } CheckOrFailF(vertices_refcount.refCount(vID) == vTris.Count + 1); CheckOrFailF(triToVtxRefs[vID] == vTris.Count); foreach (int tID in vTris) { CheckOrFailF(tri_has_v(tID, vID)); } // check that edges around vert only references tris above, and reference all of them! var vRemoveTris = new List <int>(vTris); foreach (int edgeid in vertex_edges.ValueItr(vID)) { Index2i edget = GetEdgeT(edgeid); CheckOrFailF(vTris.Contains(edget[0])); if (edget[1] != InvalidID) { CheckOrFailF(vTris.Contains(edget[1])); } vRemoveTris.Remove(edget[0]); if (edget[1] != InvalidID) { vRemoveTris.Remove(edget[1]); } } CheckOrFailF(vRemoveTris.Count == 0); } return(is_ok); }
public static bool orient_tri_edge(ref int a, ref int b, Index3i tri_verts) { return(orient_tri_edge(ref a, ref b, ref tri_verts)); }
IOReadResult BuildMeshes_ByMaterial(ReadOptions options, IMeshBuilder builder) { if (vPositions.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No vertices in file")); } if (vTriangles.Length == 0) { return(new IOReadResult(IOCode.GarbageDataError, "No triangles in file")); } bool bHaveNormals = (vNormals.Length > 0); bool bHaveColors = (vColors.Length > 0); bool bHaveUVs = (vUVs.Length > 0); List <int> usedMaterialIDs = new List <int>(UsedMaterials.Keys); usedMaterialIDs.Add(Triangle.InvalidMaterialID); foreach (int material_id in usedMaterialIDs) { int matID = Triangle.InvalidMaterialID; if (material_id != Triangle.InvalidMaterialID) { string sMatName = UsedMaterials[material_id]; OBJMaterial useMat = Materials[sMatName]; matID = builder.BuildMaterial(useMat); } bool bMatHaveUVs = (material_id == Triangle.InvalidMaterialID) ? false : bHaveUVs; // don't append mesh until we actually see triangles int meshID = -1; Dictionary <Index3i, int> mapV = new Dictionary <Index3i, int>(); for (int k = 0; k < vTriangles.Length; ++k) { Triangle t = vTriangles[k]; if (t.nMaterialID == material_id) { if (meshID == -1) { meshID = builder.AppendNewMesh(bHaveNormals, bHaveColors, bMatHaveUVs, false); } Triangle t2 = new Triangle(); for (int j = 0; j < 3; ++j) { Index3i vk = new Index3i( t.vIndices[j] - 1, t.vNormals[j] - 1, t.vUVs[j] - 1); int use_vtx = -1; if (mapV.ContainsKey(vk) == false) { use_vtx = append_vertex(builder, vk, bHaveNormals, bHaveColors, bMatHaveUVs); mapV[vk] = use_vtx; } else { use_vtx = mapV[vk]; } t2.vIndices[j] = use_vtx; } append_triangle(builder, t2); } } if (matID != Triangle.InvalidMaterialID) { builder.AssignMaterial(matID, meshID); } } return(new IOReadResult(IOCode.Ok, "")); }
ProcessResult ProcessEdge(int edgeID) { EdgeConstraint constraint = (constraints == null) ? EdgeConstraint.Unconstrained : constraints.GetEdgeConstraint(edgeID); if (constraint.NoModifications) { 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; 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 = EnableCollapses && constraint.CanCollapse && edge_len_sqr < MinEdgeLength * MinEdgeLength && can_collapse_constraints(edgeID, a, b, c, d, t0, t1, out collapse_to); // 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...) // if edge length is too short, we want to collapse it bool bTriedCollapse = false; if (bCanCollapse) { int iKeep = b, iCollapse = a; Vector3d vNewPos = (vA + vB) * 0.5; // 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); } // TODO be smart about picking b (keep vtx). // - swap if one is bdry vtx, for example? // 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) { mesh.SetVertex(b, vNewPos); if (constraints != null) { constraints.ClearEdgeConstraint(edgeID); constraints.ClearEdgeConstraint(collapseInfo.eRemoved0); if (collapseInfo.eRemoved1 != DMesh3.InvalidID) { constraints.ClearEdgeConstraint(collapseInfo.eRemoved1); } constraints.ClearVertexConstraint(iCollapse); } DoDebugChecks(); return(ProcessResult.Ok_Collapsed); } else { bTriedCollapse = true; } } end_collapse(); begin_flip(); // if this is not a boundary edge, maybe we want to flip bool bTriedFlip = false; if (EnableFlips && constraint.CanFlip && bIsBoundaryEdge == false) { // don't want to flip if it will invert triangle...tetrahedron sign?? // can we do this more efficiently somehow? bool a_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(a)); bool b_is_boundary_vtx = (MeshIsClosed) ? false : (bIsBoundaryEdge || mesh.vertex_is_boundary(b)); bool c_is_boundary_vtx = (MeshIsClosed) ? false : mesh.vertex_is_boundary(c); bool d_is_boundary_vtx = (MeshIsClosed) ? false : mesh.vertex_is_boundary(d); int valence_a = mesh.GetVtxEdgeValence(a), valence_b = mesh.GetVtxEdgeValence(b); int valence_c = mesh.GetVtxEdgeValence(c), valence_d = mesh.GetVtxEdgeValence(d); int valence_a_target = (a_is_boundary_vtx) ? valence_a : 6; int valence_b_target = (b_is_boundary_vtx) ? valence_b : 6; int valence_c_target = (c_is_boundary_vtx) ? valence_c : 6; int valence_d_target = (d_is_boundary_vtx) ? valence_d : 6; // if total valence error improves by flip, we want to do it int curr_err = Math.Abs(valence_a - valence_a_target) + Math.Abs(valence_b - valence_b_target) + Math.Abs(valence_c - valence_c_target) + Math.Abs(valence_d - valence_d_target); int flip_err = Math.Abs((valence_a - 1) - valence_a_target) + Math.Abs((valence_b - 1) - valence_b_target) + Math.Abs((valence_c + 1) - valence_c_target) + Math.Abs((valence_d + 1) - valence_d_target); if (flip_err < curr_err) { // try flip DMesh3.EdgeFlipInfo flipInfo; COUNT_FLIPS++; MeshResult result = mesh.FlipEdge(edgeID, out flipInfo); if (result == MeshResult.Ok) { DoDebugChecks(); return(ProcessResult.Ok_Flipped); } else { bTriedFlip = true; } } } end_flip(); begin_split(); // if edge length is too long, we want to split it bool bTriedSplit = false; if (EnableSplits && constraint.CanSplit && edge_len_sqr > MaxEdgeLength * MaxEdgeLength) { DMesh3.EdgeSplitInfo splitInfo; COUNT_SPLITS++; MeshResult result = mesh.SplitEdge(edgeID, out splitInfo); if (result == MeshResult.Ok) { update_after_split(edgeID, a, b, splitInfo); DoDebugChecks(); return(ProcessResult.Ok_Split); } else { bTriedSplit = true; } } end_split(); if (bTriedFlip || bTriedSplit || bTriedCollapse) { return(ProcessResult.Failed_OpNotSuccessful); } else { return(ProcessResult.Ignored_EdgeIsFine); } }
public void clear() { nMaterialID = InvalidMaterialID; nGroupID = InvalidGroupID; vIndices = vNormals = vUVs = new Index3i(-1, -1, -1); }
/// <summary> /// Stitch two sets of boundary edges that are provided as unordered pairs of edges, by /// adding triangulated quads between each edge pair. /// If a failure is encountered during stitching, the triangles added up to that point are removed. /// </summary> public virtual int[] StitchUnorderedEdges(List <Index2i> EdgePairs, int group_id = -1) { int N = EdgePairs.Count; int[] new_tris = new int[N * 2]; int i = 0; for (; i < N; ++i) { Index2i edges = EdgePairs[i]; // look up and orient the first edge Index4i edge_a = Mesh.GetEdge(edges.a); if (edge_a.d != DMesh3.InvalidID) { goto operation_failed; } Index3i edge_a_tri = Mesh.GetTriangle(edge_a.c); int a = edge_a.a, b = edge_a.b; IndexUtil.orient_tri_edge(ref a, ref b, edge_a_tri); // look up and orient the second edge Index4i edge_b = Mesh.GetEdge(edges.b); if (edge_b.d != DMesh3.InvalidID) { goto operation_failed; } Index3i edge_b_tri = Mesh.GetTriangle(edge_b.c); int c = edge_b.a, d = edge_b.b; IndexUtil.orient_tri_edge(ref c, ref d, edge_b_tri); // swap second edge (right? should this be a parameter?) int tmp = c; c = d; d = tmp; Index3i t1 = new Index3i(b, a, d); Index3i t2 = new Index3i(a, c, d); int tid1 = Mesh.AppendTriangle(t1, group_id); int tid2 = Mesh.AppendTriangle(t2, group_id); if (tid1 == DMesh3.InvalidID || tid2 == DMesh3.InvalidID) { goto operation_failed; } new_tris[2 * i] = tid1; new_tris[2 * i + 1] = tid2; } return(new_tris); operation_failed: // remove what we added so far if (i > 0) { if (remove_triangles(new_tris, 2 * (i - 1)) == false) { throw new Exception("MeshEditor.StitchLoop: failed to add all triangles, and also failed to back out changes."); } } return(null); }
void make_level_set3(DMesh3 mesh, /*const std::vector<Vec3ui> &tri, const std::vector<Vec3f> &x*/ Vector3f origin, float dx, int ni, int nj, int nk, DenseGrid3f phi, int exact_band) { phi.resize(ni, nj, nk); phi.assign((ni + nj + nk) * dx); // upper bound on distance DenseGrid3i closest_tri = new DenseGrid3i(ni, nj, nk, -1); DenseGrid3i intersection_count = new DenseGrid3i(ni, nj, nk, 0); // intersection_count(i,j,k) is # of tri intersections in (i-1,i]x{j}x{k} // we begin by initializing distances near the mesh, and figuring out intersection counts System.Console.WriteLine("start"); //Vector3f ijkmin, ijkmax; // [RMS] unused in original code double ddx = (double)dx; double ox = (double)origin[0], oy = (double)origin[1], oz = (double)origin[2]; foreach (int t in mesh.TriangleIndices()) { Index3i triangle = mesh.GetTriangle(t); int p = triangle.a, q = triangle.b, r = triangle.c; Vector3d xp = mesh.GetVertex(p); Vector3d xq = mesh.GetVertex(q); Vector3d xr = mesh.GetVertex(r); // coordinates in grid to high precision 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; // do distances nearby 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); for (int k = k0; k <= k1; ++k) { for (int j = j0; j <= j1; ++j) { for (int i = i0; i <= i1; ++i) { Vector3f gx = new Vector3f((float)i * dx + origin[0], (float)j * dx + origin[1], (float)k * dx + origin[2]); float d = point_triangle_distance(gx, (Vector3f)xp, (Vector3f)xq, (Vector3f)xr); if (d < phi[i, j, k]) { phi[i, j, k] = d; closest_tri[i, j, k] = t; } } } } // and do intersection counts j0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fjp, fjq, fjr)), 0, nj - 1); j1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fjp, fjq, fjr)), 0, nj - 1); k0 = MathUtil.Clamp((int)Math.Ceiling(MathUtil.Min(fkp, fkq, fkr)), 0, nk - 1); k1 = MathUtil.Clamp((int)Math.Floor(MathUtil.Max(fkp, fkq, fkr)), 0, nk - 1); 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.increment(0, j, k); // we enlarge the first interval to include everything to the -x direction } else if (i_interval < ni) { intersection_count.increment(i_interval, j, k); } // we ignore intersections that are beyond the +x side of the grid } } } } System.Console.WriteLine("done narrow-band"); // and now we fill in the rest of the distances with fast sweeping for (int pass = 0; pass < 2; ++pass) { sweep(mesh, phi, closest_tri, origin, dx, +1, +1, +1); sweep(mesh, phi, closest_tri, origin, dx, -1, -1, -1); sweep(mesh, phi, closest_tri, origin, dx, +1, +1, -1); sweep(mesh, phi, closest_tri, origin, dx, -1, -1, +1); sweep(mesh, phi, closest_tri, origin, dx, +1, -1, +1); sweep(mesh, phi, closest_tri, origin, dx, -1, +1, -1); sweep(mesh, phi, closest_tri, origin, dx, +1, -1, -1); sweep(mesh, phi, closest_tri, origin, dx, -1, +1, +1); } System.Console.WriteLine("done sweeping"); // then figure out signs (inside/outside) from intersection counts for (int k = 0; k < nk; ++k) { for (int j = 0; j < nj; ++j) { int total_count = 0; for (int i = 0; i < ni; ++i) { total_count += intersection_count[i, j, k]; if (total_count % 2 == 1) // if parity of intersections so far is odd, { phi[i, j, k] = -phi[i, j, k]; // we are inside the mesh } } } } System.Console.WriteLine("done signs"); } // end make_level_set_3
// Assumption here is that Submesh has been modified, but boundary loop has // been preserved, and that old submesh has already been removed from this mesh. // So, we just have to append new vertices and then rewrite triangles // If new_tris or new_verts is non-null, we will return this info. // new_tris should be set to TriangleCount (ie it is not necessarily a map) // For new_verts, if we used an existing bdry vtx instead, we set the value to -(existing_index+1), // otherwise the value is new_index (+1 is to handle 0) // // Returns true if submesh successfully inserted, false if any triangles failed // (which happens if triangle would result in non-manifold mesh) public bool ReinsertSubmesh(DSubmesh3 sub, ref int[] new_tris, out IndexMap SubToNewV, DuplicateTriBehavior eDuplicateBehavior = DuplicateTriBehavior.AssertAbort) { if (sub.BaseBorderV == null) { throw new Exception("MeshEditor.ReinsertSubmesh: Submesh does not have required boundary info. Call ComputeBoundaryInfo()!"); } DMesh3 submesh = sub.SubMesh; bool bAllOK = true; IndexFlagSet done_v = new IndexFlagSet(submesh.MaxVertexID, submesh.TriangleCount / 2); SubToNewV = new IndexMap(submesh.MaxVertexID, submesh.VertexCount); int nti = 0; int NT = submesh.MaxTriangleID; for (int ti = 0; ti < NT; ++ti) { if (submesh.IsTriangle(ti) == false) { continue; } Index3i sub_t = submesh.GetTriangle(ti); int gid = submesh.GetTriangleGroup(ti); Index3i new_t = Index3i.Zero; for (int j = 0; j < 3; ++j) { int sub_v = sub_t[j]; int new_v = -1; if (done_v[sub_v] == false) { // first check if this is a boundary vtx on submesh and maps to a bdry vtx on base mesh if (submesh.IsBoundaryVertex(sub_v)) { int base_v = (sub_v < sub.SubToBaseV.size) ? sub.SubToBaseV[sub_v] : -1; if (base_v >= 0 && Mesh.IsVertex(base_v) && sub.BaseBorderV[base_v] == true) { // [RMS] this should always be true, but assert in tests to find out Debug.Assert(Mesh.IsBoundaryVertex(base_v)); if (Mesh.IsBoundaryVertex(base_v)) { new_v = base_v; } } } // if that didn't happen, append new vtx if (new_v == -1) { new_v = Mesh.AppendVertex(submesh, sub_v); } SubToNewV[sub_v] = new_v; done_v[sub_v] = true; } else { new_v = SubToNewV[sub_v]; } new_t[j] = new_v; } // try to handle duplicate-tri case if (eDuplicateBehavior == DuplicateTriBehavior.AssertContinue) { Debug.Assert(Mesh.FindTriangle(new_t.a, new_t.b, new_t.c) == DMesh3.InvalidID); } else { int existing_tid = Mesh.FindTriangle(new_t.a, new_t.b, new_t.c); if (existing_tid != DMesh3.InvalidID) { if (eDuplicateBehavior == DuplicateTriBehavior.AssertAbort) { Debug.Assert(existing_tid == DMesh3.InvalidID); return(false); } else if (eDuplicateBehavior == DuplicateTriBehavior.UseExisting) { if (new_tris != null) { new_tris[nti++] = existing_tid; } continue; } else if (eDuplicateBehavior == DuplicateTriBehavior.Replace) { Mesh.RemoveTriangle(existing_tid, false); } } } int new_tid = Mesh.AppendTriangle(new_t, gid); Debug.Assert(new_tid != DMesh3.InvalidID && new_tid != DMesh3.NonManifoldID); if (!Mesh.IsTriangle(new_tid)) { bAllOK = false; } if (new_tris != null) { new_tris[nti++] = new_tid; } } return(bAllOK); }
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); }
private int AppendTriangle(Index3i t) { return(AppendTriangle(t.a, t.b, t.c)); }
public IOWriteResult Write(TextWriter writer, List <WriteMesh> vMeshes, WriteOptions options) { int N = vMeshes.Count; writer.WriteLine("OFF"); string three_floats = Util.MakeVec3FormatString(0, 1, 2, options.RealPrecisionDigits); int nTotalV = 0, nTotalT = 0, nTotalE = 0; // OFF only supports one mesh, so have to collapse all input meshes // into a single list, with mapping for triangles // [TODO] can skip this if input is a single mesh! int[][] mapV = new int[N][]; for (int mi = 0; mi < N; ++mi) { nTotalV += vMeshes[mi].Mesh.VertexCount; nTotalT += vMeshes[mi].Mesh.TriangleCount; nTotalE += 0; mapV[mi] = new int[vMeshes[mi].Mesh.MaxVertexID]; } writer.WriteLine(string.Format("{0} {1} {2}", nTotalV, nTotalT, nTotalE)); // write all vertices, and construct vertex re-map int vi = 0; for (int mi = 0; mi < N; ++mi) { IMesh mesh = vMeshes[mi].Mesh; if (options.ProgressFunc != null) { options.ProgressFunc(mi, 2 * (N - 1)); } foreach (int vid in mesh.VertexIndices()) { Vector3d v = mesh.GetVertex(vid); writer.WriteLine(three_floats, v.x, v.y, v.z); mapV[mi][vid] = vi; vi++; } } // write all triangles for (int mi = 0; mi < N; ++mi) { IMesh mesh = vMeshes[mi].Mesh; if (options.ProgressFunc != null) { options.ProgressFunc(N + mi, 2 * (N - 1)); } foreach (int ti in mesh.TriangleIndices()) { Index3i t = mesh.GetTriangle(ti); t[0] = mapV[mi][t[0]]; t[1] = mapV[mi][t[1]]; t[2] = mapV[mi][t[2]]; writer.WriteLine(string.Format("3 {0} {1} {2}", t[0], t[1], t[2])); } } return(new IOWriteResult(IOCode.Ok, "")); }
/// <summary> /// Converts the provided integer vector from g3 into the OpenTK variant. /// </summary> /// <param name="vector">The vector to convert from its current integer variant. /// </param> /// <returns>An OpenTK vector to use in all calculations.</returns> public static Vector3 VectorConvert(g3.Index3i vector) { return(new Vector3(vector.a, vector.b, vector.c)); }
public void BuildLinear() { int NV = mesh.MaxVertexID; if (TrackVertexMapping) { mapTo = new Index2i[NV]; for (int i = 0; i < NV; ++i) { mapTo[i] = Index2i.Zero; } mapToMulti = new DVector <int>(); } // temporary map from orig vertices to submesh vertices int[] mapToCur = new int[NV]; Array.Clear(mapToCur, 0, mapToCur.Length); int nT = mesh.MaxTriangleID; // depending on the mesh, either the vertex count or triangle count // could hit the upper bound first. For connected meshes we have // NT =~ 2*NV by eulers formula, but eg for a pathological triangle // soup mesh, we might have NV = 3*NT int[] cur_subt = new int[MaxComponentSize]; // accumulated tris for this component int subti = 0; // index into cur_subt int subi = 1; // component index // also need to make sure we don't get too many verts. To do this // we have to keep count of how many unique verts we have accumulated. int[] cur_subv = new int[MaxComponentSize]; // temp buffer in add_component BitArray vert_bits = new BitArray(mesh.MaxVertexID); // which verts have we seen for this component int subvcount = 0; // accumulated vert count for this component Action add_component = () => { Index2i mapRange; int max_subv; Component new_comp = extract_submesh(subi++, cur_subt, subti, mapToCur, cur_subv, out mapRange, out max_subv); // [TODO] perhaps manager can request smaller chunks? Manager.AddComponent(new_comp); Array.Clear(cur_subt, 0, subti); subti = 0; Array.Clear(mapToCur, mapRange.a, mapRange.b - mapRange.a + 1); Array.Clear(cur_subv, 0, max_subv); subvcount = 0; vert_bits.SetAll(false); }; int[] tri_order = get_tri_order_by_axis_sort(); int tri_count = tri_order.Length; for (int ii = 0; ii < tri_count; ++ii) { int ti = tri_order[ii]; Index3i tri = mesh.GetTriangle(ti); if (vert_bits[tri.a] == false) { vert_bits[tri.a] = true; subvcount++; } if (vert_bits[tri.b] == false) { vert_bits[tri.b] = true; subvcount++; } if (vert_bits[tri.c] == false) { vert_bits[tri.c] = true; subvcount++; } cur_subt[subti++] = ti; if (subti == MaxComponentSize || subvcount > MaxComponentSize - 3) { add_component(); } } if (subti > 0) { add_component(); } }