Workaround for rs.CullDuplicateNumbers (not yet implemented)


#1

Is there any simple Python method to cull near-duplicate numbers (within tolerance) within a list? I could for example create a list of 3dpoints with the x coordinate of the points as the value from the list (and the y, z as 0) and then use rs.CullDuplicatePoints, but that seems like a kind of kludge…

Also thought of sorting the list and then iterating over it and if the neighbor to the right is within tolerance, eliminate it… not that easy either… Is there a better/smarter way?

Thx, --Mitch


#2

At first I wasn’t sure whether you were talking about a list of scalars or a list of points, but after careful re-reading I concluded: scalars.

It seems to me that to accomplish your goal you will need to pair-wise compare each number with every other number to test for difference within tolerance, That’s pretty much what sorting does, but without the tolerance, and the library routines and functions to do it are pretty well optimized for speed. Once you have a sorted list, one pass through it testing for tolerance between one number and the next, throwing out those that don’t pass before moving to the next number in the list that isn’t within tolerance sounds to me like a pretty good method. So I think you are right. I guess that means I don’t know of a better/smarter way either.


(Dale Fugier) #3

You can try this (I have not tested this much…):

import rhinoscriptsyntax as rs

def PointLessThan(p0, p1):
    # Dictionary order
    if (p0[0] < p1[0]): return True
    if (p0[0] == p1[0] and p0[1] < p1[1]): return True
    if (p0[1] == p1[1] and p0[2] < p1[2]): return True
    return False

def PointGreaterThan(p0, p1):
    # Dictionary order
    if (p0[0] > p1[0]): return True
    if (p0[0] == p1[0] and p0[1] > p1[1]): return True
    if (p0[1] == p1[1] and p0[2] > p1[2]): return True
    return False

def ComparePoints(p0, p1):
    # Comparison function
    if PointLessThan(p0, p1):
        return -1
    elif PointGreaterThan(p0, p1):
        return 1
    else:
        return 0

def CullDuplicatePoints(points, tolerance):
    sorted(points, cmp=ComparePoints)
    p0 = points[-1]
    for i in range(len(points)-2, -1, -1):
        p1 = points[i]
        if (rs.Distance(p0, p1) < tolerance): points.pop(i)
        else: p0 = points[i]
    return points

RhinoScript differs in that it does not sort the points before comparing.

– Dale


#4

Hi Dale,

Thanks, I was actually looking for CullDuplicateNumbers as CullDuplicatePoints is already implemented in rhinoscriptsyntax in Python. What I came up with are two methods, one home-grown “sort-and-compare” method, the other using rs.CullDuplicatePoints as a cheat:

import rhinoscriptsyntax as rs
import Rhino

def CullDuplicateNumbers(nums,tol):
    nums.sort()
    unique=[nums[0]]
    for i in range(len(nums)-1):
        if abs(nums[i+1]-unique[-1])>=tol:
            unique.append(nums[i+1])
    return unique
    
def CullDuplicateNumbersRS(nums,tol):
    pts=[Rhino.Geometry.Point3d(0,0,num) for num in nums]
    pts=rs.CullDuplicatePoints(pts,tol)
    return [pt.Z for pt in pts]
    
    
numList=[1.0,1.11,1.0,2.0,4.0,1.2,3.0,5.0,7.0,1,1.1,1.2,1.09,1.3]
tolerance=0.1

print CullDuplicateNumbers(numList,tolerance)
>>>[1.0, 1.1, 1.3, 2.0, 3.0, 4.0, 5.0, 7.0]

print CullDuplicateNumbersRS(numList,tolerance)
>>>[1.0, 1.1, 1.3, 2.0, 3.0, 4.0, 5.0, 7.0]

–Mitch


(Steve Baer) #5

I addded CullDuplicateNumbers to rhinoscriptsyntax in SR8. It works pretty much the same as what you put together Mitch.
Thanks.


#6

Thanks Steve!

–Mitch