查找给定点集中的矩形(无循环)#functools#compgeometry

2024-04-25 11:36:11 发布

您现在位置:Python中文网/ 问答频道 /正文

虽然我经常使用StackOverflow,但这是我在这里的第一篇帖子:)

我试图在一个给定的点集中找到矩形,只使用函数,避免任何循环。 为了使它更简单,只需要平行于反网格的矩形

我的问题是,有没有办法减少条件,以确定四个点是否是函数形式的矩形(参见函数get_rectangles())

itertools.composition()需要很多时间

这是我创建的,有一个main()要测试:

import itertools as it
from functools import *

###function to obtain the square distance given two points
def distancia2(point1, point2):
    return ((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2)

###function that returns the 6 possible square distances between four points sorted
def distancias(four_points):
    u1 = distancia2(four_points[0], four_points[1])
    u2 = distancia2(four_points[2], four_points[1])
    u3 = distancia2(four_points[2], four_points[3])
    u4 = distancia2(four_points[3], four_points[0])
    u5 = distancia2(four_points[0], four_points[2])
    u6 = distancia2(four_points[1], four_points[3])
    return sorted([u1, u2, u3, u4, u5, u6])

#condition for being a rectangle (grid oriented)
## lista is in the way of tupple of tupples
## i.e. lista = ((4,1),(5,1),(4,8),(5,8))
## return True if the four points can form a rectangle
def esrectangulo(lista):
    #CONDITION 1
    ## is parallel to the grid, so only two values of x and
    ## two values of y are required for define the four points
    ## dos_valoresX is the unic x values in the points == 2
    ##
    ## ( reduce(lambda ac, list: ac + [list[0]] if not list[0] in ac else ac,lista,[])  )
    ## ( here reduce is used to make a list of unic values in the list                  )
    dos_valoresX =  len(reduce(lambda ac, list: ac + [list[0]] if not list[0] in ac else ac,
                                         lista,
                                         [])) == 2
    dos_valoresY =  len(reduce(lambda ac, list: ac + [list[1]] if not list[1] in ac else ac,
                                         lista,
                                         [])) == 2
    cond_oriented = dos_valoresX and dos_valoresY

    #CONDITION 2
    ## There should be only three different distances in the six possible distances between points
    ## or 2 in square case
    seis_distancias = distancias(lista)
    dist_distintas = reduce(lambda xs , alist: xs + [alist] if not alist in xs else xs ,seis_distancias,[])
    solo_hay_tresodos = len(dist_distintas) == 3 or  len(dist_distintas) == 2

    #CONDITION 3
    ## The longest distance has to be equal to Pitagoras theorem using
    ## other distances
    pitagoras = (seis_distancias[5] == seis_distancias[1] + seis_distancias[3])

    return True if solo_hay_tresodos and pitagoras and cond_oriented else False

# Function returns the list of points to form rectangles in the list points
# point list is an aray of points tuples
# [(x1,y1),(x2,y2),(x3,y3)...]
def get_rectangles(pnts):
    # first, we filter the unic points
    points = list(reduce(lambda ac, p: ac + [p] if not p in ac else ac, pnts, []))

    # vertical and horizontal lines which contain more than one point
    # they are the possible rectangle points
    rectas_verticales = list(filter(lambda x: True if [p[0] for p in points].count(x) > 1 else False,
                                reduce(lambda ac, list: ac + [list[0]] if not list[0] in ac else ac,
                                       points,
                                       [])))
    rectas_horizontales = list(filter(lambda x: True if [p[1] for p in points].count(x) > 1 else False,
                                  reduce(lambda ac, list: ac + [list[1]] if not list[1] in ac else ac,
                                         points,
                                         [])))
    # filter all the points in these lines
    # puntos_inter = interested points
    def in_some_line(p):
        return True if p[0] in rectas_verticales and p[1] in rectas_horizontales else False

    puntos_inter = list(filter(in_some_line ,points))

    # rectangulos is an array of groups of four points which are rectangles on the grid
    # it.combinations(puntos_inter,4) creates all posible combinations of four points in puntos_iter
    # itertools library
    rectangulos = list(filter(esrectangulo,it.combinations(puntos_inter,4)))

    #return and ortogonal h. and v. lines
    return rectangulos, rectas_horizontales, rectas_verticales

# FOR TESTIING
def main():
    import matplotlib.pyplot as plt
    import matplotlib.patches as mpatches
    import operator
    import random

    #GENERATION OF RANDOM POINTS
    tam, n = 30, 100
    random_points = [(random.randint(1, tam), random.randint(1, tam)) for x in range(n)]
    print('points: ', random_points)

    [rectangles, hor_lines, ver_lines] = get_rectangles(random_points)
    print('rectangles: ', rectangles)
    #we can make a small plot with matplotlib for representation 
    def random_color():
        rgbl = [random.random() for x in range(3)]
        return tuple(rgbl)

    fig, ax = plt.subplots()
    for i in range(0, len(rectangles)):
        rectangle = sorted(rectangles[i],key= operator.itemgetter(1,0))
        rectangle[2], rectangle[3] = rectangle[3], rectangle[2]
        print(rectangle)
        ax.add_patch(mpatches.Polygon(rectangle, alpha=0.7, facecolor= 'None', edgecolor=random_color(),linewidth=2.))
    plt.scatter(x=list(map(lambda l: l[0],random_points)),y=list(map(lambda l: l[1],random_points)), s=10.,marker='o',c='k',alpha=0.3)
    plt.show()



if __name__ == "__main__":
    main()


Tags: ofthelambdainforreturnifrandom