Sorting according to clockwise point coordinates

前端 未结 7 2141
别跟我提以往
别跟我提以往 2020-12-16 18:18

Given a list in Python containing 8 x, y coordinate values (all positive) of 4 points as [x1, x2, x3, x4, y1, y2, y3, y4] ((xi, yi) are x and y coo

相关标签:
7条回答
  • 2020-12-16 18:35

    Try this line of code

    def sort_clockwise(pts):
        rect = np.zeros((4, 2), dtype="float32")
        s = pts.sum(axis=1)
        rect[0] = pts[np.argmin(s)]
        rect[2] = pts[np.argmax(s)]
        diff = np.diff(pts, axis=1)
        rect[1] = pts[np.argmin(diff)]
        rect[3] = pts[np.argmax(diff)]
        return rect
    
    0 讨论(0)
  • 2020-12-16 18:37

    You should use a list of 2-item tuples as your data structure to represent a variable number of coordinates in a meaningful way.

    from functools import reduce
    import operator
    import math
    coords = [(0, 1), (1, 0), (1, 1), (0, 0)]
    center = tuple(map(operator.truediv, reduce(lambda x, y: map(operator.add, x, y), coords), [len(coords)] * 2))
    print(sorted(coords, key=lambda coord: (-135 - math.degrees(math.atan2(*tuple(map(operator.sub, coord, center))[::-1]))) % 360))
    

    This outputs:

    [(0, 0), (0, 1), (1, 1), (1, 0)]
    
    0 讨论(0)
  • 2020-12-16 18:42
    # P4=8,10 P1=3,5   P2=8,5   P3=3,10
    points=[8,3,8,3,10,5,5,10]
    k=0
    #we know these numbers are extreme and data won't be bigger than these
    xmin=1000
    xmax=-1000
    ymin=1000
    ymax=-1000
    #finding min and max values of x and y
    for i in points:
        if  k<4:
            if (xmin>i): xmin=i
            if (xmax<i): xmax=i        
        else:
            if (ymin>i): ymin=i
            if (ymax<i): ymax=i        
        k +=1
    
    sortedlist=[xmin,xmin,xmax,xmax,ymin,ymax,ymax,ymin]
    print(sortedlist)
    

    output:[3, 3, 8, 8, 5, 10, 10, 5] for other regions you need to change sortedlist line. if center is inside the box then it will require more condition controlling

    0 讨论(0)
  • 2020-12-16 18:43

    What we want to sort by is the angle from the start coordinate. I've used numpy here to interpret each vector from the starting coordinate as a complex number, for which there is an easy way of computing the angle (counterclockwise along the unit sphere)

    def angle_with_start(coord, start):
        vec = coord - start
        return np.angle(np.complex(vec[0], vec[1]))
    

    Full code:

    import itertools
    import numpy as np
    
    
    def angle_with_start(coord, start):
        vec = coord - start
        return np.angle(np.complex(vec[0], vec[1]))
    
    
    def sort_clockwise(points):
        # convert into a coordinate system
        # (1, 1, 1, 2) -> (1, 1), (1, 2)
        coords = [np.array([points[i], points[i+4]]) for i in range(len(points) // 2)]
    
        # find the point closest to the origin,
        # this becomes our starting point
        coords = sorted(coords, key=lambda coord: np.linalg.norm(coord))
        start = coords[0]
        rest = coords[1:]
    
        # sort the remaining coordinates by angle
        # with reverse=True because we want to sort by clockwise angle
        rest = sorted(rest, key=lambda coord: angle_with_start(coord, start), reverse=True)
    
        # our first coordinate should be our starting point
        rest.insert(0, start)
        # convert into the proper coordinate format
        # (1, 1), (1, 2) -> (1, 1, 1, 2)
        return list(itertools.chain.from_iterable(zip(*rest)))
    

    Behavior on some sample inputs:

    In [1]: a
    Out[1]: [1, 1, 2, 2, 1, 2, 1, 2]
    
    In [2]: sort_clockwise(a)
    Out[2]: [1, 1, 2, 2, 1, 2, 2, 1]
    
    In [3]: b
    Out[3]: [1, 2, 0, 2, 1, 2, 3, 1]
    
    In [4]: sort_clockwise(b)
    Out[4]: [1, 0, 2, 2, 1, 3, 2, 1]
    
    0 讨论(0)
  • 2020-12-16 18:47

    Based on BERA's answer but as a class:

    code

    import math
    
    def class Sorter:
        @staticmethod    
        def centerXY(xylist):
            x, y = zip(*xylist)
            l = len(x)
            return sum(x) / l, sum(y) / l  
    
        @staticmethod    
        def sortPoints(xylist):  
            cx, cy = Sorter.centerXY(xylist)
            xy_sorted = sorted(xylist, key = lambda x: math.atan2((x[1]-cy),(x[0]-cx)))
            return xy_sorted
    

    test

    def test_SortPoints():
        points=[(0,0),(0,1),(1,1),(1,0)]
        center=Sorter.centerXY(points)
        assert center==(0.5,0.5)
        sortedPoints=Sorter.sortPoints(points)
        assert sortedPoints==[(0, 0), (1, 0), (1, 1), (0, 1)]
    
    0 讨论(0)
  • 2020-12-16 18:48

    As suggested by IgnacioVazquez-Abrams, we can also do sorting according to atan2 angles:

    Code:

    import math
    import copy
    import matplotlib.pyplot as plt
    
    a = [2, 4, 5, 1, 0.5, 4, 0, 4]
    print(a)
    
    
    def clock(a):
        angles = []
        (x0, y0) = ((a[0]+a[1]+a[2]+a[3])/4, (a[4]+ a[5] + a[6] + a[7])/4)  # centroid
        for j in range(4):
            (dx, dy) = (a[j] - x0, a[j+4] - y0)
            angles.append(math.degrees(math.atan2(float(dy), float(dx))))
        for k in range(4):
            angles.append(angles[k] + 800)
        # print(angles)
    
        z = [copy.copy(x) for (y,x) in sorted(zip(angles,a), key=lambda pair: pair[0])]
        print("z is: ", z)
    
    plt.scatter(a[:4], a[4:8])
    plt.show()
    
    clock(a)
    

    Output is :

    [2, 4, 5, 1, 0.5, 4, 0, 4]
    [-121.60750224624891, 61.92751306414704, -46.73570458892839, 136.8476102659946, 678.3924977537511, 861.9275130641471, 753.2642954110717, 936.8476102659946]
    z is:  [2, 5, 4, 1, 0.5, 0, 4, 4]
    
    0 讨论(0)
提交回复
热议问题