99from compas .topology import breadth_first_traverse
1010
1111
12- def _closest_faces (vertices , faces , nmax = 10 , radius = 10.0 ):
12+ def _closest_faces (vertices , faces , nmax , max_distance ):
1313 points = [centroid_points ([vertices [index ] for index in face ]) for face in faces ]
1414
15- k = min (len (faces ), nmax )
15+ k = len ( faces ) if nmax is None else min (len (faces ), nmax )
1616
1717 # determine the k closest faces for each face
1818 # each item in "closest" is
@@ -21,22 +21,26 @@ def _closest_faces(vertices, faces, nmax=10, radius=10.0):
2121 # [2] the distance between the test point and the face centroid
2222
2323 try :
24+ import numpy as np
2425 from scipy .spatial import cKDTree
2526
27+ tree = cKDTree (points )
28+ distances , closest = tree .query (points , k = k , workers = - 1 )
29+ if max_distance is None :
30+ return closest
31+
32+ closest_within_distance = []
33+ for i , closest_row in enumerate (closest ):
34+ idx = np .where (distances [i ] < max_distance )[0 ]
35+ closest_within_distance .append (closest_row [idx ].tolist ())
36+ return closest_within_distance
37+
2638 except Exception :
2739 try :
2840 from Rhino .Geometry import Point3d # type: ignore
2941 from Rhino .Geometry import RTree # type: ignore
3042 from Rhino .Geometry import Sphere # type: ignore
3143
32- except Exception :
33- from compas .geometry import KDTree
34-
35- tree = KDTree (points )
36- closest = [tree .nearest_neighbors (point , k ) for point in points ]
37- closest = [[index for xyz , index , d in nnbrs ] for nnbrs in closest ]
38-
39- else :
4044 tree = RTree ()
4145
4246 for i , point in enumerate (points ):
@@ -48,20 +52,26 @@ def callback(sender, e):
4852
4953 closest = []
5054 for i , point in enumerate (points ):
51- sphere = Sphere (Point3d (* point ), radius )
55+ sphere = Sphere (Point3d (* point ), max_distance )
5256 data = []
5357 tree .Search (sphere , callback , data )
5458 closest .append (data )
59+ return closest
5560
56- else :
57- tree = cKDTree (points )
58- _ , closest = tree .query (points , k = k , workers = - 1 )
61+ except Exception :
62+ from compas .geometry import KDTree
5963
60- return closest
64+ tree = KDTree (points )
65+ closest = [tree .nearest_neighbors (point , k ) for point in points ]
66+ if max_distance is None :
67+ return closest
68+ return [[index for xyz , index , d in nnbrs if d < max_distance ] for nnbrs in closest ]
6169
6270
63- def _face_adjacency (vertices , faces , nmax = 10 , radius = 10.0 ):
64- closest = _closest_faces (vertices , faces , nmax = nmax , radius = radius )
71+ def _face_adjacency (vertices , faces , nmax = None , max_distance = None ):
72+ if nmax is None and max_distance is None :
73+ raise ValueError ("Either nmax or max_distance should be specified." )
74+ closest = _closest_faces (vertices , faces , nmax = nmax , max_distance = max_distance )
6575
6676 adjacency = {}
6777
@@ -94,7 +104,7 @@ def _face_adjacency(vertices, faces, nmax=10, radius=10.0):
94104 return adjacency
95105
96106
97- def face_adjacency (points , faces ):
107+ def face_adjacency (points , faces , nmax = None , max_distance = None ):
98108 """Build a face adjacency dict.
99109
100110 Parameters
@@ -103,6 +113,10 @@ def face_adjacency(points, faces):
103113 The vertex locations of the faces.
104114 faces : list[list[int]]
105115 The faces defined as list of indices in the points list.
116+ nmax : int, optional
117+ The maximum number of neighboring faces to consider. If neither nmax nor max_distance is specified, all faces will be considered.
118+ max_distance : float, optional
119+ The max_distance of the search sphere for neighboring faces. If neither nmax nor max_distance is specified, all faces will be considered.
106120
107121 Returns
108122 -------
@@ -117,10 +131,8 @@ def face_adjacency(points, faces):
117131 purely geometrical, but uses a spatial indexing tree to speed up the search.
118132
119133 """
120- f = len (faces )
121-
122- if f > 100 :
123- return _face_adjacency (points , faces )
134+ if nmax or max_distance :
135+ return _face_adjacency (points , faces , nmax = nmax , max_distance = max_distance )
124136
125137 adjacency = {}
126138
@@ -152,7 +164,7 @@ def face_adjacency(points, faces):
152164 return adjacency
153165
154166
155- def unify_cycles (vertices , faces , root = None ):
167+ def unify_cycles (vertices , faces , root = None , nmax = None , max_distance = None ):
156168 """Unify the cycle directions of all faces.
157169
158170 Unified cycle directions is a necessary condition for the data structure to
@@ -164,8 +176,12 @@ def unify_cycles(vertices, faces, root=None):
164176 The vertex coordinates of the mesh.
165177 faces : list[list[int]]
166178 The faces of the mesh defined as lists of vertex indices.
167- root : str , optional
179+ root : int , optional
168180 The key of the root face.
181+ nmax : int, optional
182+ The maximum number of neighboring faces to consider. If neither nmax nor max_distance is specified, all faces will be considered.
183+ max_distance : float, optional
184+ The max_distance of the search sphere for neighboring faces. If neither nmax nor max_distance is specified, all faces will be considered.
169185
170186 Returns
171187 -------
@@ -196,7 +212,7 @@ def unify(node, nbr):
196212 if root is None :
197213 root = random .choice (list (range (len (faces ))))
198214
199- adj = face_adjacency (vertices , faces ) # this is the only place where the vertex coordinates are used
215+ adj = face_adjacency (vertices , faces , nmax = nmax , max_distance = max_distance ) # this is the only place where the vertex coordinates are used
200216
201217 visited = breadth_first_traverse (adj , root , unify )
202218
0 commit comments