Is there a Python code equivalent to Proximity 2D?

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.

2 Likes