Hi @rcmcdougle,
I’m late to the party, but if you are still interested in a Pythonic proximity2D script, here’s one that I wrote awhile back:
"""Search for three-dimensional proximity within a point list."""
__author__ = "p1r4t3b0y"
import Rhino.Geometry as rg
import rhinoscriptsyntax as rs
def point_cloud_closest_points(pt_cloud, max_radius):
"""Finds the points in a point cloud that are closest to each of
its items/points within a certain maximum radius with the
RTree algorithm.
Args:
pt_cloud (Rhino.Geometry.PointCloud): A point cloud.
max_radius (float): A maximum radius to search within.
Returns:
A nested list with a sublist of closest point indices
for each point in the point cloud.
"""
rtree = rg.RTree()
# Populate the RTree with point cloud points
for i in xrange(pt_cloud.Count):
rtree.Insert(pt_cloud.Item[i].Location, i) # i is custom id
closest_indices = []
for i in xrange(pt_cloud.Count):
# Construct the sphere as bounds used for searching
center_pt = pt_cloud.Item[i].Location
search_sphere = rg.Sphere(center_pt, max_radius)
# Perform the search within the spherical bounds
closest_ids = [] # list of custom ids per point
rtree_callback = lambda sender, args: closest_ids.append(args.Id) \
if i != args.Id else None
rtree.Search(search_sphere, rtree_callback)
# Sort the search result by closest distance
closest_dists = (pt_cloud.Item[j].Location.DistanceTo(center_pt) \
for j in closest_ids)
closest_ids = [x for _, x in sorted(zip(closest_dists, closest_ids))]
closest_indices.append(closest_ids)
return closest_indices
def proximity_3d(points, max_num, link=True, min_radius=None, max_radius=None):
"""Search for three-dimensional proximity within a point list.
Args:
points (list): A collection of three-dimensional points.
max_num (int): A maximum number of closest points to find.
link (bool): Optional True to output topology links.
min_radius (float): An optional minimum search radius.
max_radius (float): An optional maximum search radius.
Returns:
A nested list of proximity topology indices [0] and
a nested list of proximity links [1].
"""
# Initalise a point cloud from the points
pt_cloud = rg.PointCloud(points)
# Initalise the proximity topology tree (closest point cloud item indices)
prox_indices = [[j for j in range(len(points))] for i in points]
prox_lines = []
# Get the closest indices for each point cloud item by maximum radius
if max_radius != None and max_radius > 0.0:
max_rad_cl_idcs = point_cloud_closest_points(pt_cloud, max_radius)
# Replace the initial closesest indices
prox_indices = list(max_rad_cl_idcs)
# Get the closest indices for each point cloud item by minimum radius
if min_radius != None and min_radius > 0.0:
min_rad_cl_idcs = point_cloud_closest_points(pt_cloud, min_radius)
# Cull the closest indices of each point within the minimum radius
for i in xrange(len(prox_indices)):
if len(min_rad_cl_idcs[i]) > 0:
for idx in min_rad_cl_idcs[i]:
prox_indices[i].pop(prox_indices[i].index(idx))
# Cull indices by maximum number per point cloud item
for i in range(len(prox_indices)):
if len(prox_indices[i]) > max_num:
# Initalise a temporary current closest points point cloud
cl_cloud = rg.PointCloud()
idcs_map = {} # maps the inital to the new cloud item indices
idx_count = 0 # keeps track of current point cloud item index
for j, pt in enumerate(points):
if j in prox_indices[i] and j != i:
cl_cloud.Add(pt)
idcs_map[idx_count] = j
idx_count += 1
# Get the closest indices in the current point cloud by max. count
pt = [points[i]] # single point in a list
pcl_cl_idcs = rg.RTree.PointCloudKNeighbors(cl_cloud, pt, max_num)
# Remap the current point cloud closest to the inital indices
cl_indices = [idcs_map[idx] for lt in pcl_cl_idcs for idx in lt]
# Remove the superfluous from the current proximity 3d indices
merged_idcs = prox_indices[i] + cl_indices
cull_idcs = [x for x in merged_idcs if merged_idcs.count(x) == 1]
for idx in cull_idcs:
prox_indices[i].pop(prox_indices[i].index(idx))
if link:
for i in range(len(points)):
closest_pts = [points[j] for j in prox_indices[i]]
for pt in closest_pts:
prox_lines.append(rg.Line(points[i], pt))
return prox_indices, prox_lines
###############################################################################
if __name__ == "__main__":
pt_guids = rs.GetObjects(message="Select points", filter=1, preselect=True)
points = []
for guid in pt_guids:
pt = rs.coerce3dpoint(guid)
points.append(pt)
max_num = rs.GetInteger(message="Maximum number of closest points",
number=3,
minimum=1,
maximum=len(points)-1)
min_radius = rs.GetReal(message="Opitional minimum search radius",
number=0.0)
max_radius = rs.GetReal(message="Opitional maximum search radius",
number=0.0)
prox_indices, prox_links = proximity_3d(points, max_num, True, min_radius, max_radius)
for link in prox_links:
rs.AddLine(link.From, link.To)
I wrote it for GHPython, as part of another script, but added Rhino functionality to this version. You can simply run it as a script and provide the necessary data in Rhino.
The proximity links will be output to your current layer.