private void CreateVoronoi(List <FlowStructure> flowStructures) { // Get the starts from everything var allStartPoints = flowStructures.Select(x => x.start).ToList(); // Make a 2d list of all the start points var nodes = new Node2List(allStartPoints); // Create a 2d list that forms the boundary var outline = new List <Node2>(); // Get sorted lists of coordinates to guestimate a boundary rectangle var sortedX = allStartPoints.OrderByDescending(item => item.X); var sortedY = allStartPoints.OrderByDescending(item => item.Y); outline.Add(new Node2(sortedX.First().X, sortedY.First().Y)); outline.Add(new Node2(sortedX.First().X, sortedY.Last().Y)); outline.Add(new Node2(sortedX.Last().X, sortedY.Last().Y)); outline.Add(new Node2(sortedX.Last().X, sortedY.First().Y)); // TODO: a delauney first so the brute force isn't needed, and the topology can be used to group var voronoiCells = Solver.Solve_BruteForce(nodes, outline); // Cells are order as the nodes were; so we can loop through both for (var i = 0; i < voronoiCells.Count; i++) { flowStructures[i].catchment = voronoiCells[i].ToPolyline(); } }
/* * /// <summary>Delaunay mesher.</summary> * /// <param name="nodes">Nodes to triangulate</param> * /// <param name="jitter_amount">Amount of random noise. Make sure there is at least some noise * /// if your input nodes are structured.</param> * /// <param name="faces">Face list output</param> * /// <returns>Mesh instance</returns> * public static Mesh Solve_Mesh(Node2List nodes, double jitter_amount, ref List<Face> faces) * { * faces = Solver.Solve_Faces(nodes, jitter_amount); * Mesh mesh1; * if (faces == null) * mesh1 = (Mesh)null; * else if (faces.Count == 0) * { * mesh1 = (Mesh)null; * } * else * { * Mesh mesh2 = new Mesh(); * int num1 = nodes.Count - 1; * for (int index = 0; index <= num1; ++index) * mesh2.Vertices.Add(nodes[index].x, nodes[index].y, 0.0); * int num2 = faces.Count - 1; * for (int index = 0; index <= num2; ++index) * mesh2.Faces.AddFace(faces[index].A, faces[index].B, faces[index].C); * mesh2.Normals.ComputeNormals(); * mesh1 = mesh2; * } * return mesh1; * } */ /// <summary>Connectivity solver. Returns a topological edge diagram of delaunay faces.</summary> /// <param name="nodes">Nodes to connect</param> /// <param name="jitter_amount">Amount of random motion.</param> /// <param name="include_convex_hull_edges">If true, the edges of the convex hull are included in the connectivity diagram.</param> /// <returns>Connectivity diagram for [nodes]</returns> public static Connectivity Solve_Connectivity( Node2List nodes, double jitter_amount, bool include_convex_hull_edges) { if (nodes == null) { throw new ArgumentNullException(nameof(nodes)); } if (nodes.Count < 2) { throw new InvalidOperationException("Insufficient nodes for a Connectivity diagram"); } List <Face> faces = (List <Face>)null; if (nodes.Count > 2) { faces = Solver.Solve_Faces(nodes, jitter_amount); } if (faces == null) { faces = new List <Face>(); } Connectivity connectivity = new Connectivity(); connectivity.SolveConnectivity(nodes, faces, include_convex_hull_edges); return(connectivity); }
public void AddFace(int A, int B, int C, Node2List Nodes) { FaceEx faceEx = new FaceEx(A, B, C); faceEx.ComputeBC(Nodes); AddFace(faceEx); }
/// <summary>Core Delaunay solver.</summary> /// <param name="nodes">Nodes to triangulate</param> /// <param name="jitter_amount">Amount of random noise. Make sure there is at least some noise /// if your input nodes are structured.</param> /// <returns>A list of triangular faces that connect indices in the [nodes] parameter.</returns> public static List <Face> Solve_Faces(Node2List nodes, double jitter_amount) { if (nodes == null) { throw new ArgumentNullException(nameof(nodes)); } if (nodes.Count < 3) { throw new InvalidOperationException("Insufficient nodes for a triangulation"); } Solver solver = new Solver(); solver.m_nodes = new Node2List(nodes); solver.m_nodes.RenumberNodes(); if (jitter_amount != 0.0) { solver.m_nodes.JitterNodes(jitter_amount); } List <Face> faceList; if (!solver.Triangulate()) { faceList = (List <Face>)null; } else { solver.RemapFaceIndices(); faceList = solver.m_faces; } return(faceList); }
/// <summary>This class cannot be constructed.</summary> private Solver() { this.m_nodes = new Node2List(); this.m_faces = new List <Face>(); this.m_box_corners = new int[4] { -1, -1, -1, -1 }; }
public void InsertFaces(Node2List nodes) { int num = m_F.Count - 1; for (int i = 0; i <= num; i++) { FaceEx faceEx = m_F[i]; if (faceEx != null) { Polyline polyline = new Polyline(); polyline.Add(nodes[faceEx.A].x, nodes[faceEx.A].y, 0.0); polyline.Add(nodes[faceEx.B].x, nodes[faceEx.B].y, 0.0); polyline.Add(nodes[faceEx.C].x, nodes[faceEx.C].y, 0.0); polyline.Add(nodes[faceEx.A].x, nodes[faceEx.A].y, 0.0); } } }
public static Polyline ComputeHull(Node2List pts) { List <int> list = new List <int>(); if (!Compute(pts, list)) { return(null); } Polyline polyline = new Polyline(list.Count); int num = list.Count - 1; for (int i = 0; i <= num; i++) { polyline.Add(pts[list[i]].x, pts[list[i]].y, 0.0); } polyline.Add(polyline[0]); return(polyline); }
public void SolveConnectivity(Node2List nodes, List <Face> faces, bool include_convex_hull_edges) { m_map = new List <List <int> >(nodes.Count); int num = nodes.Count - 1; for (int i = 0; i <= num; i++) { m_map.Add(new List <int>(6)); } int num2 = faces.Count - 1; for (int j = 0; j <= num2; j++) { Face face = faces[j]; m_map[face.A].Add(face.B); m_map[face.A].Add(face.C); m_map[face.B].Add(face.A); m_map[face.B].Add(face.C); m_map[face.C].Add(face.A); m_map[face.C].Add(face.B); } if (include_convex_hull_edges) { List <int> list = new List <int>(); if (Diagrams.ConvexHull.Solver.Compute(nodes, list)) { int num3 = list.Count - 1; for (int k = 0; k <= num3; k++) { int num4 = k + 1; if (num4 == list.Count) { num4 = 0; } m_map[list[k]].Add(list[num4]); m_map[list[num4]].Add(list[k]); } } } RemoveDuplicates(); }
/// <summary> /// This is the method that actually does the work. /// </summary> /// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param> protected override void SolveInstance(IGH_DataAccess DA) { string path = ""; int lineSkip = 1; if (!DA.GetData(0, ref path)) { return; } DA.GetData(1, ref recursive); DA.GetData(2, ref asMesh); DA.GetData(3, ref lineSkip); if (lineSkip < 1) { lineSkip = 1; } try { if (oldPath != path) { oldPath = path; geometry = GetGeometricData(path); } DataTree <IGH_GeometricGoo> geo = new DataTree <IGH_GeometricGoo>(); foreach (Tuple <int, ConcurrentQueue <IGH_GeometricGoo>, FileTypes> tuple in geometry) { switch (tuple.Item3) { case FileTypes.XYZ: if (asMesh) { ConcurrentQueue <Point3d> pp = new ConcurrentQueue <Point3d>(); Parallel.ForEach(tuple.Item2, (item, _, iNum) => { if (iNum % lineSkip == 0) { GH_Point p = new GH_Point(); if (GH_Convert.ToGHPoint(item, GH_Conversion.Both, ref p)) { pp.Enqueue(p.Value); } } }); Mesh mesh = new Mesh(); mesh.Vertices.AddVertices(pp); try { Node2List nodes = new Node2List(pp); List <Face> faces = Solver.Solve_Faces(nodes, 1); IEnumerable <MeshFace> meshFaces = faces.Select(x => new MeshFace(x.A, x.B, x.C)); mesh.Faces.AddFaces(meshFaces); ConcurrentQueue <IGH_GeometricGoo> goo = new ConcurrentQueue <IGH_GeometricGoo>(); goo.Enqueue(GH_Convert.ToGeometricGoo(mesh)); geo.AddRange(goo, new GH_Path(tuple.Item1)); } catch (Exception e) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message); } } else { ConcurrentQueue <IGH_GeometricGoo> goo = new ConcurrentQueue <IGH_GeometricGoo>(); Parallel.ForEach(tuple.Item2, (item, _, iNum) => { if (iNum % lineSkip == 0) { goo.Enqueue(item); } }); geo.AddRange(goo, new GH_Path(tuple.Item1)); } break; } } DA.SetDataTree(0, geo); } catch (Exception e) { this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message); } }
public void ComputeBC(Node2List Nodes) { ComputeBC(Nodes[A], Nodes[B], Nodes[C]); }
/// <summary> /// Compute the convex hull of list of nodes. /// </summary> /// <param name="nodes">Nodes to wrap. May not contain null references.</param> /// <param name="hull">Index list describing the convex hull (closing segment not included)</param> public static bool Compute(Node2List nodes, List <int> hull) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (hull == null) { throw new ArgumentNullException("hull"); } List <bool> list = new List <bool>(); hull.Clear(); list.Clear(); hull.Capacity = nodes.Count; list.Capacity = nodes.Count; if (nodes.Count == 0) { return(false); } if (nodes.Count == 1) { return(false); } if (nodes.Count == 2) { hull.Add(0); hull.Add(1); return(true); } int num = nodes.Count - 1; for (int i = 0; i <= num; i++) { list.Add(item: false); } int num2 = -1; int num3 = -1; int num4 = nodes.Count - 1; for (int j = 0; j <= num4; j++) { if (nodes[j] != null) { num2 = j; num3 = j; break; } } if (num2 < 0) { return(false); } int num5 = nodes.Count - 1; for (int k = 1; k <= num5; k++) { if (nodes[k] != null) { if (nodes[k].x < nodes[num2].x) { num2 = k; } else if (nodes[k].x == nodes[num2].x && nodes[k].y < nodes[num2].y) { num2 = k; } } } num3 = num2; do { int num6 = -1; int num7 = nodes.Count - 1; for (int l = 0; l <= num7; l++) { if (nodes[l] == null || list[l] || l == num3) { continue; } if (num6 == -1) { num6 = l; continue; } double num8 = CrossProduct(nodes[l], nodes[num3], nodes[num6]); if (num8 == 0.0) { if (DotProduct(nodes[num3], nodes[l], nodes[l]) > DotProduct(nodes[num3], nodes[num6], nodes[num6])) { num6 = l; } } else if (num8 < 0.0) { num6 = l; } } num3 = num6; list[num3] = true; hull.Add(num3); }while (num3 != num2); return(true); }
/// <summary> /// Solve the voronoi diagram using a Sorted Brute force approach. /// Works best with a collection of nodes which are spread along the x-direction. /// This function will renumber and sort the nodes. /// </summary> /// <param name="nodes">Nodes to solve for. This list will be renumbered and sorted.</param> /// <param name="outline">Initial boundary for every cell.</param> /// <returns>The voronoi cells. Order of cells is identical to the order of nodes.</returns> public static List <Cell2> Solve_BruteForce(Node2List nodes, IEnumerable <Node2> outline) { nodes = new Node2List(nodes); List <Node2> node2List; if (outline is List <Node2> ) { node2List = (List <Node2>)outline; } else { node2List = new List <Node2>(); node2List.AddRange(outline); } nodes.RenumberNodes(); nodes.Sort(Node2List.NodeListSort.X); List <Cell2> cell2List = new List <Cell2>(nodes.Count); int num1 = nodes.Count - 1; for (int index = 0; index <= num1; ++index) { cell2List.Add((Cell2)null); } int num2 = nodes.Count - 1; for (int index1 = 0; index1 <= num2; ++index1) { if (nodes[index1] != null) { Cell2 cell2 = new Cell2(nodes[index1], (IEnumerable <Node2>)node2List); double num3 = cell2.Radius(); for (int index2 = index1 - 1; index2 >= 0; index2 += -1) { if (nodes[index2] != null) { if (nodes[index2].x >= cell2.M.x - num3) { if (cell2.Slice(nodes[index2])) { if (cell2.C.Count != 0) { num3 = cell2.Radius(); } else { break; } } } else { break; } } } if (cell2.C.Count != 0) { int num4 = index1 + 1; int num5 = nodes.Count - 1; for (int index2 = num4; index2 <= num5; ++index2) { if (nodes[index2] != null) { if (nodes[index2].x <= cell2.M.x + num3) { if (cell2.Slice(nodes[index2])) { if (cell2.C.Count != 0) { num3 = cell2.Radius(); } else { break; } } } else { break; } } } cell2List[nodes[index1].tag] = cell2; } } } return(cell2List); }
/// <summary>Solve the voronoi diagram using a minimal connectivity map.</summary> /// <param name="nodes">Nodes to solve for.</param> /// <param name="diagram">Connectivity diagram. Can be obtained from a Delaunay mesh.</param> /// <param name="outline">Initial boundary for every cell.</param> public static List <Cell2> Solve_Connectivity( Node2List nodes, Connectivity diagram, IEnumerable <Node2> outline) { if (nodes == null) { throw new ArgumentNullException(nameof(nodes)); } if (diagram == null) { throw new ArgumentNullException(nameof(diagram)); } if (outline == null) { throw new ArgumentNullException("boundary"); } List <Node2> node2List; if (outline is List <Node2> ) { node2List = (List <Node2>)outline; } else { node2List = new List <Node2>(); node2List.AddRange(outline); } nodes = new Node2List(nodes); nodes.RenumberNodes(); List <Cell2> cell2List = new List <Cell2>(nodes.Count); int num1 = nodes.Count - 1; for (int index = 0; index <= num1; ++index) { cell2List.Add((Cell2)null); } int num2 = nodes.Count - 1; for (int node_index = 0; node_index <= num2; ++node_index) { if (nodes[node_index] != null) { Cell2 cell2 = new Cell2(nodes[node_index], (IEnumerable <Node2>)node2List); List <int> connections = diagram.GetConnections(node_index); if (connections != null) { int num3 = connections.Count - 1; for (int index1 = 0; index1 <= num3; ++index1) { int index2 = connections[index1]; if (index2 != node_index) { cell2.Slice(nodes[index2]); } } cell2List[nodes[node_index].tag] = cell2; } } } return(cell2List); }