/// <summary> /// Form a Delaunay triangulation by incrementally inserting vertices. /// </summary> /// <returns>Returns the number of edges on the convex hull of the /// triangulation.</returns> public IMesh Triangulate(IList <Vertex> points, Configuration config) { _TriangleNetMesh = new TriangleNetMesh(config); _TriangleNetMesh.TransferNodes(points); Otri starttri = new Otri(); // Create a triangular bounding box. GetBoundingBox(); foreach (var v in _TriangleNetMesh.vertices.Values) { starttri.tri = _TriangleNetMesh.dummytri; Osub tmp = default(Osub); if (_TriangleNetMesh.InsertVertex(v, ref starttri, ref tmp, false, false) == InsertVertexResult.Duplicate) { if (Log.Verbose) { Log.Instance.Warning("A duplicate vertex appeared and was ignored.", "Incremental.Triangulate()"); } v.type = VertexType.UndeadVertex; _TriangleNetMesh.undeads++; } } // Remove the bounding box. _TriangleNetMesh.hullsize = RemoveBox(); return(_TriangleNetMesh); }
/// <summary> /// Compute the Voronoi vertices (the circumcenters of the triangles). /// </summary> /// <returns>An empty map, which will map all vertices to a list of leaving edges.</returns> protected List <HalfEdge>[] ComputeVertices(TriangleNetMesh triangleNetMesh, Vertex[] vertices) { Otri tri = default(Otri); float xi = 0, eta = 0; Vertex vertex; Point pt; int id; // Maps all vertices to a list of leaving edges. var map = new List <HalfEdge> [triangleNetMesh.triangles.Count]; // Compue triangle circumcenters foreach (var t in triangleNetMesh.triangles) { id = t.id; tri.tri = t; pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta); vertex = factory.CreateVertex(pt.x, pt.y); vertex.id = id; vertices[id] = vertex; map[id] = new List <HalfEdge>(); } return(map); }
/// <summary> /// Gets the permutation vector for the Reverse Cuthill-McKee numbering. /// </summary> /// <param name="triangleNetMesh">The mesh.</param> /// <returns>Permutation vector.</returns> public int[] Renumber(TriangleNetMesh triangleNetMesh) { // Algorithm needs linear numbering of the nodes. triangleNetMesh.Renumber(NodeNumbering.Linear); return(Renumber(new AdjacencyMatrix(triangleNetMesh))); }
/// <summary> /// Number the vertices and write them to a .node file. /// </summary> /// <param name="triangleNetMesh"></param> /// <param name="filename"></param> public void WriteNodes(TriangleNetMesh triangleNetMesh, string filename) { using (var writer = new StreamWriter(filename)) { WriteNodes(writer, triangleNetMesh); } }
/// <summary> /// Write the triangle neighbors to a .neigh file. /// </summary> /// <param name="triangleNetMesh"></param> /// <param name="filename"></param> /// <remarks>WARNING: Be sure WriteElements has been called before, /// so the elements are numbered right!</remarks> public void WriteNeighbors(TriangleNetMesh triangleNetMesh, string filename) { Otri tri = default(Otri), trisym = default(Otri); int n1, n2, n3; int i = 0; using (StreamWriter writer = new StreamWriter(filename)) { // Number of triangles, three neighbors per triangle. writer.WriteLine("{0} 3", triangleNetMesh.triangles.Count); foreach (var item in triangleNetMesh.triangles) { tri.tri = item; tri.orient = 1; tri.Sym(ref trisym); n1 = trisym.tri.id; tri.orient = 2; tri.Sym(ref trisym); n2 = trisym.tri.id; tri.orient = 0; tri.Sym(ref trisym); n3 = trisym.tri.id; // Triangle number, neighboring triangle numbers. writer.WriteLine("{0} {1} {2} {3}", i++, n1, n2, n3); } } }
public TriangleLocator(TriangleNetMesh triangleNetMesh, IPredicates predicates) { _TriangleNetMesh = triangleNetMesh; this.predicates = predicates; sampler = new TriangleSampler(triangleNetMesh); }
/// <summary> /// Generate the Voronoi diagram from given triangle mesh.. /// </summary> /// <param name="triangleNetMesh"></param> /// <param name="bounded"></param> protected void Generate(TriangleNetMesh triangleNetMesh) { triangleNetMesh.Renumber(); base.edges = new List <HalfEdge>(); this.rays = new List <HalfEdge>(); // Allocate space for Voronoi diagram. var vertices = new Vertex[triangleNetMesh.triangles.Count + triangleNetMesh.hullsize]; var faces = new Face[triangleNetMesh.vertices.Count]; if (factory == null) { factory = new DefaultVoronoiFactory(); } factory.Initialize(vertices.Length, 2 * triangleNetMesh.NumberOfEdges, faces.Length); // Compute triangles circumcenters. var map = ComputeVertices(triangleNetMesh, vertices); // Create all Voronoi faces. foreach (var vertex in triangleNetMesh.vertices.Values) { faces[vertex.id] = factory.CreateFace(vertex); } ComputeEdges(triangleNetMesh, vertices, faces, map); // At this point all edges are computed, but the (edge.next) pointers aren't set. ConnectEdges(map); base.vertices = new List <Vertex>(vertices); base.faces = new List <Face>(faces); }
/// <summary> /// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class. /// </summary> /// <param name="triangleNetMesh">Mesh instance.</param> public BoundedVoronoiLegacy(TriangleNetMesh triangleNetMesh, bool includeBoundary) { _TriangleNetMesh = triangleNetMesh; this.includeBoundary = includeBoundary; Generate(); }
/// <summary> /// Initializes a new instance of the <see cref="EdgeIterator" /> class. /// </summary> public EdgeIterator(TriangleNetMesh triangleNetMesh) { triangles = triangleNetMesh.triangles.GetEnumerator(); triangles.MoveNext(); tri.tri = triangles.Current; tri.orient = 0; }
/// <summary> /// Form a Delaunay triangulation by the divide-and-conquer method. /// </summary> /// <returns></returns> /// <remarks> /// Sorts the vertices, calls a recursive procedure to triangulate them, and /// removes the bounding box, setting boundary markers as appropriate. /// </remarks> public IMesh Triangulate(IList <Vertex> points, Configuration config) { this.predicates = config.Predicates(); this._TriangleNetMesh = new TriangleNetMesh(config); this._TriangleNetMesh.TransferNodes(points); Otri hullleft = default(Otri), hullright = default(Otri); int i, j, n = points.Count; // Allocate an array of pointers to vertices for sorting. this.sortarray = new Vertex[n]; i = 0; foreach (var v in points) { sortarray[i++] = v; } // Sort the vertices. VertexSorter.Sort(sortarray); // Discard duplicate vertices, which can really mess up the algorithm. i = 0; for (j = 1; j < n; j++) { if ((sortarray[i].x == sortarray[j].x) && (sortarray[i].y == sortarray[j].y)) { if (Log.Verbose) { Log.Instance.Warning( String.Format("A duplicate vertex appeared and was ignored (ID {0}).", sortarray[j].id), "Dwyer.Triangulate()"); } sortarray[j].type = VertexType.UndeadVertex; _TriangleNetMesh.undeads++; } else { i++; sortarray[i] = sortarray[j]; } } i++; if (UseDwyer) { // Re-sort the array of vertices to accommodate alternating cuts. VertexSorter.Alternate(sortarray, i); } // Form the Delaunay triangulation. DivconqRecurse(0, i - 1, 0, ref hullleft, ref hullright); this._TriangleNetMesh.hullsize = RemoveGhosts(ref hullleft); return(this._TriangleNetMesh); }
public static void DrawGizmos(this TriangleNetMesh triangleNetMesh) { foreach (var triangle in triangleNetMesh.triangles) { var verts = triangle.vertices; Gizmos.DrawLine((Vector3)verts[0], (Vector3)verts[1]); Gizmos.DrawLine((Vector3)verts[1], (Vector3)verts[2]); Gizmos.DrawLine((Vector3)verts[2], (Vector3)verts[0]); } }
public StandardVoronoi(TriangleNetMesh triangleNetMesh, Rectangle box, IVoronoiFactory factory, IPredicates predicates) : base(triangleNetMesh, factory, predicates, true) { // We assume the box to be at least as large as the mesh. box.Expand(triangleNetMesh.bounds); // We explicitly told the base constructor to call the Generate method, so // at this point the basic Voronoi diagram is already created. PostProcess(box); }
public void Triangulate() { _Polygon = new Polygon(); foreach (var vector2 in _Contour) { _Polygon.Add(vector2); } _TriangleNetMesh = (TriangleNetMesh)_Polygon.Triangulate(); _Filter.mesh = _TriangleNetMesh.GenerateUnityMesh(); }
public void Update(TriangleNetMesh triangleNetMesh) { _TriangleNetMesh = triangleNetMesh; // Reset all measures. areaMeasure.Reset(); alphaMeasure.Reset(); qMeasure.Reset(); Compute(); }
/// <summary> /// Initializes a new instance of the <see cref="TriangleQuadTree" /> class. /// </summary> /// <param name="triangleNetMesh">Mesh containing triangles.</param> /// <param name="maxDepth">The maximum depth of the tree.</param> /// <param name="sizeBound">The maximum number of triangles contained in a leaf.</param> /// <remarks> /// The quadtree does not track changes of the mesh. If a mesh is refined or /// changed in any other way, a new quadtree has to be built to make the point /// location work. /// /// A node of the tree will be split, if its level if less than the max depth parameter /// AND the number of triangles in the node is greater than the size bound. /// </remarks> public TriangleQuadTree(TriangleNetMesh triangleNetMesh, int maxDepth = 10, int sizeBound = 10) { this.maxDepth = maxDepth; this.sizeBound = sizeBound; triangles = triangleNetMesh.Triangles.ToArray(); int currentDepth = 0; root = new QuadNode(triangleNetMesh.Bounds, this, true); root.CreateSubRegion(++currentDepth); }
/// <summary> /// Initializes a new instance of the <see cref="VoronoiBase" /> class. /// </summary> /// <param name="triangleNetMesh">Triangle mesh.</param> /// <param name="factory">Voronoi object factory.</param> /// <param name="predicates">Geometric predicates implementation.</param> /// <param name="generate">If set to true, the constuctor will call the Generate /// method, which builds the Voronoi diagram.</param> protected VoronoiBase(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates, bool generate) : base(false) { this.factory = factory; this.predicates = predicates; if (generate) { Generate(triangleNetMesh); } }
public AdjacencyMatrix(TriangleNetMesh triangleNetMesh) { N = triangleNetMesh.vertices.Count; // Set up the adj_row adjacency pointer array. pcol = AdjacencyCount(triangleNetMesh); nnz = pcol[N]; // Set up the adj adjacency array. irow = AdjacencySet(triangleNetMesh, pcol); SortIndices(); }
private void HashVertices(TriangleNetMesh triangleNetMesh) { if (vertices == null || triangleNetMesh.Vertices.Count != vertices.Length) { vertices = new int[triangleNetMesh.Vertices.Count]; } int i = 0; foreach (var v in triangleNetMesh.Vertices) { vertices[i++] = v.id; } }
/// <summary> /// Number the vertices and write them to a .node file. /// </summary> private void WriteNodes(StreamWriter writer, TriangleNetMesh triangleNetMesh) { int outvertices = triangleNetMesh.vertices.Count; int nextras = triangleNetMesh.nextras; Behavior behavior = triangleNetMesh.behavior; if (behavior.Jettison) { outvertices = triangleNetMesh.vertices.Count - triangleNetMesh.undeads; } if (writer != null) { // Number of vertices, number of dimensions, number of vertex attributes, // and number of boundary markers (zero or one). writer.WriteLine("{0} {1} {2} {3}", outvertices, triangleNetMesh.mesh_dim, nextras, behavior.UseBoundaryMarkers ? "1" : "0"); if (triangleNetMesh.numbering == NodeNumbering.None) { // If the mesh isn't numbered yet, use linear node numbering. triangleNetMesh.Renumber(); } if (triangleNetMesh.numbering == NodeNumbering.Linear) { // If numbering is linear, just use the dictionary values. WriteNodes(writer, triangleNetMesh.vertices.Values, behavior.UseBoundaryMarkers, nextras, behavior.Jettison); } else { // If numbering is not linear, a simple 'foreach' traversal of the dictionary // values doesn't reflect the actual numbering. Use an array instead. // TODO: Could use a custom sorting function on dictionary values instead. Vertex[] nodes = new Vertex[triangleNetMesh.vertices.Count]; foreach (var node in triangleNetMesh.vertices.Values) { nodes[node.id] = node; } WriteNodes(writer, nodes, behavior.UseBoundaryMarkers, nextras, behavior.Jettison); } } }
public BoundedVoronoi(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates) : base(triangleNetMesh, factory, predicates, true) { // We explicitly told the base constructor to call the Generate method, so // at this point the basic Voronoi diagram is already created. offset = vertices.Count; // Each vertex of the hull will be part of a Voronoi cell. vertices.Capacity = offset + triangleNetMesh.hullsize; // Create bounded Voronoi diagram. PostProcess(); ResolveBoundaryEdges(); }
public QualityMesher(TriangleNetMesh triangleNetMesh, Configuration config) { logger = Log.Instance; badsubsegs = new Queue <BadSubseg>(); queue = new BadTriQueue(); this._TriangleNetMesh = triangleNetMesh; this.predicates = config.Predicates(); this.behavior = triangleNetMesh.behavior; newLocation = new NewLocation(triangleNetMesh, predicates); newvertex_tri = new Triangle(); }
/// <summary> /// Reconstruct a triangulation from its raw data representation. /// </summary> public static TriangleNetMesh ToMesh(Polygon polygon, ITriangle[] triangles) { Otri tri = default(Otri); Osub subseg = default(Osub); int i = 0; int elements = triangles == null ? 0 : triangles.Length; int segments = polygon.Segments.Count; // TODO: Configuration should be a function argument. var mesh = new TriangleNetMesh(new Configuration()); mesh.TransferNodes(polygon.Points); mesh.regions.AddRange(polygon.Regions); mesh.behavior.useRegions = polygon.Regions.Count > 0; if (polygon.Segments.Count > 0) { mesh.behavior.Poly = true; mesh.holes.AddRange(polygon.Holes); } // Create the triangles. for (i = 0; i < elements; i++) { mesh.MakeTriangle(ref tri); } if (mesh.behavior.Poly) { mesh.insegments = segments; // Create the subsegments. for (i = 0; i < segments; i++) { mesh.MakeSegment(ref subseg); } } var vertexarray = SetNeighbors(mesh, triangles); SetSegments(mesh, polygon, vertexarray); return(mesh); }
private void Step(TriangleNetMesh triangleNetMesh, IVoronoiFactory factory, IPredicates predicates) { var voronoi = new BoundedVoronoi(triangleNetMesh, factory, predicates); float x, y; foreach (var face in voronoi.Faces) { if (face.generator.label == 0) { Centroid(face, out x, out y); face.generator.x = x; face.generator.y = y; } } }
private bool VerticesChanged(TriangleNetMesh triangleNetMesh) { if (vertices == null || triangleNetMesh.Vertices.Count != vertices.Length) { return(true); } int i = 0; foreach (var v in triangleNetMesh.Vertices) { if (v.id != vertices[i++]) { return(true); } } return(false); }
/// <summary> /// Rebuild the input geometry. /// </summary> private Polygon Rebuild(TriangleNetMesh triangleNetMesh) { var data = new Polygon(triangleNetMesh.vertices.Count); foreach (var v in triangleNetMesh.vertices.Values) { // Reset to input vertex. v.type = VertexType.InputVertex; data.Points.Add(v); } data.Segments.AddRange(triangleNetMesh.subsegs.Values.Cast <ISegment>()); data.Holes.AddRange(triangleNetMesh.holes); data.Regions.AddRange(triangleNetMesh.regions); return(data); }
/// <summary> /// Write the triangles to an .ele file. /// </summary> /// <param name="triangleNetMesh"></param> /// <param name="filename"></param> public void WriteElements(TriangleNetMesh triangleNetMesh, string filename) { Otri tri = default(Otri); Vertex p1, p2, p3; bool regions = triangleNetMesh.behavior.useRegions; int j = 0; tri.orient = 0; using (var writer = new StreamWriter(filename)) { // Number of triangles, vertices per triangle, attributes per triangle. writer.WriteLine("{0} 3 {1}", triangleNetMesh.triangles.Count, regions ? 1 : 0); foreach (var item in triangleNetMesh.triangles) { tri.tri = item; p1 = tri.Org(); p2 = tri.Dest(); p3 = tri.Apex(); // Triangle number, indices for three vertices. writer.Write("{0} {1} {2} {3}", j, p1.id, p2.id, p3.id); if (regions) { writer.Write(" {0}", tri.tri.label); } writer.WriteLine(); // Number elements item.id = j++; } } }
public static Mesh GenerateUnityMesh(this TriangleNetMesh triangleNetMesh, QualityOptions options = null) { if (options != null) { triangleNetMesh.Refine(options); } Mesh mesh = new Mesh(); var triangleNetVerts = triangleNetMesh.Vertices.ToList(); var triangles = triangleNetMesh.Triangles; Vector3[] verts = new Vector3[triangleNetVerts.Count]; int[] trisIndex = new int[triangles.Count * 3]; for (int i = 0; i < verts.Length; i++) { verts[i] = (Vector3)triangleNetVerts[i]; } int k = 0; foreach (var triangle in triangles) { for (int i = 2; i >= 0; i--) { trisIndex[k] = triangleNetVerts.IndexOf(triangle.GetVertex(i)); k++; } } mesh.vertices = verts; mesh.triangles = trisIndex; mesh.RecalculateBounds(); mesh.RecalculateNormals(); return(mesh); }
/// <summary> /// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class. /// </summary> /// <param name="triangleNetMesh">Mesh instance.</param> public BoundedVoronoiLegacy(TriangleNetMesh triangleNetMesh) : this(triangleNetMesh, true) { }
/// <summary> /// Sets adjacencies in a triangulation. /// </summary> /// <remarks> /// This routine can be used to create the compressed column storage /// for a linear triangle finite element discretization of Poisson's /// equation in two dimensions. /// </remarks> int[] AdjacencySet(TriangleNetMesh triangleNetMesh, int[] pcol) { int n = N; int[] col = new int[n]; // Copy of the adjacency rows input. Array.Copy(pcol, col, n); int i, nnz = pcol[n]; // Output list, stores the actual adjacency information. int[] list = new int[nnz]; // Set every node to be adjacent to itself. for (i = 0; i < n; i++) { list[col[i]] = i; col[i] += 1; } int n1, n2, n3; // Vertex numbers. int tid, nid; // Triangle and neighbor id. // Examine each triangle. foreach (var tri in triangleNetMesh.triangles) { tid = tri.id; n1 = tri.vertices[0].id; n2 = tri.vertices[1].id; n3 = tri.vertices[2].id; // Add edge (1,2) if this is the first occurrence, that is, if // the edge (1,2) is on a boundary (nid <= 0) or if this triangle // is the first of the pair in which the edge occurs (tid < nid). nid = tri.neighbors[2].tri.id; if (nid < 0 || tid < nid) { list[col[n1]++] = n2; list[col[n2]++] = n1; } // Add edge (2,3). nid = tri.neighbors[0].tri.id; if (nid < 0 || tid < nid) { list[col[n2]++] = n3; list[col[n3]++] = n2; } // Add edge (3,1). nid = tri.neighbors[1].tri.id; if (nid < 0 || tid < nid) { list[col[n1]++] = n3; list[col[n3]++] = n1; } } return(list); }
/// <summary> /// Counts adjacencies in a triangulation. /// </summary> /// <remarks> /// This routine is called to count the adjacencies, so that the /// appropriate amount of memory can be set aside for storage when /// the adjacency structure is created. /// /// The triangulation is assumed to involve 3-node triangles. /// /// Two nodes are "adjacent" if they are both nodes in some triangle. /// Also, a node is considered to be adjacent to itself. /// </remarks> int[] AdjacencyCount(TriangleNetMesh triangleNetMesh) { int n = N; int n1, n2, n3; int tid, nid; int[] pcol = new int[n + 1]; // Set every node to be adjacent to itself. for (int i = 0; i < n; i++) { pcol[i] = 1; } // Examine each triangle. foreach (var tri in triangleNetMesh.triangles) { tid = tri.id; n1 = tri.vertices[0].id; n2 = tri.vertices[1].id; n3 = tri.vertices[2].id; // Add edge (1,2) if this is the first occurrence, that is, if // the edge (1,2) is on a boundary (nid <= 0) or if this triangle // is the first of the pair in which the edge occurs (tid < nid). nid = tri.neighbors[2].tri.id; if (nid < 0 || tid < nid) { pcol[n1] += 1; pcol[n2] += 1; } // Add edge (2,3). nid = tri.neighbors[0].tri.id; if (nid < 0 || tid < nid) { pcol[n2] += 1; pcol[n3] += 1; } // Add edge (3,1). nid = tri.neighbors[1].tri.id; if (nid < 0 || tid < nid) { pcol[n3] += 1; pcol[n1] += 1; } } // We used PCOL to count the number of entries in each column. // Convert it to pointers into the ADJ array. for (int i = n; i > 0; i--) { pcol[i] = pcol[i - 1]; } pcol[0] = 0; for (int i = 1; i <= n; i++) { pcol[i] = pcol[i - 1] + pcol[i]; } return(pcol); }