七巧板拼接

知识点:

1. 求凸包

2. 平面图形的旋转和翻转,矩阵运算

3.求向量夹角,判断拼接是否有重复

 

代码:

import copy
import numpy as np


# get colours and coordinates
def available_coloured_pieces(file_name):
    # get all coordinates from file
    coloured_pieces = []  # store coordinates
    line = file_name.readline()
    while line:
        line_tmp = copy.deepcopy(line)  # deepcopy for using split() function
        line_tmp_split = line_tmp.split()
        if len(line_tmp_split) > 0 and line_tmp_split[0] == "<path":
            shape = [int(s) for s in line_tmp_split if s.isdigit()]  # <class 'list'>: [30, 20, 110, 20, 110, 120, 30, 120]
            shape_color = []  # <class 'list'>: ["red", (30, 20), (110, 20), (110, 120), (30, 120)]
            shape_color.append(line_tmp.split('"')[-2])
            for i in range(len(shape))[::2]:
                point = (shape[i], shape[i+1])
                shape_color.append(point)
            coloured_pieces.append(shape_color)  # store each shape's coordinates
        line = file_name.readline()

    return coloured_pieces


# ------------------First Task-----------------------
# get base point index
def get_bottom_left_point(p):
    k = 0
    for i in range(1, len(p)):
        if p[i][1] < p[k][1] or (p[i][1] == p[k][1] and p[i][0] < p[k][0]):
            k = i
    return k


# get cross product value
def multiply(p1, p2, p0):
    x0 = p0[0]
    y0 = p0[1]
    x1 = p1[0]
    y1 = p1[1]
    x2 = p2[0]
    y2 = p2[1]
    return (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)


# get polar angle
def get_angle(p1, p0):
    x0 = p0[0]
    y0 = p0[1]
    x1 = p1[0]
    y1 = p1[1]
    if x1 - x0 == 0:
        if y1 - y0 == 0:
            return -1
        else:
            return np.pi / 2
    tan = float((y1 - y0) / (x1 - x0))
    arc = np.arctan(tan)
    if arc < 0:
        arc = np.pi + arc
    return arc


# sort points by polar angle, not include base point
def sort_points(p, base_point):
    p2 = []
    for i in range(len(p)):
        p2.append({"index": i, "arc": get_angle(p[i], base_point)})
    p2.sort(key=lambda k: (k.get("arc")))
    p_out = []
    for i in range(len(p2)):
        p_out.append(p[p2[i]["index"]])
    return p_out


def get_convex(p):
    base_index = get_bottom_left_point(p)
    base_point = p[base_index]
    p.remove(base_point)
    p_sort = sort_points(p, base_point)
    p_result = [base_point, p_sort[0]]
    for i in range(1, len(p_sort)):
        while multiply(p_result[-2], p_sort[i], p_result[-1]) > 0:
            p_result.pop()
        p_result.append(p_sort[i])
    return p_result


def check_convex(list_orig):
    list_cut = list_orig[1:]
    num = len(list_cut)
    if num < 3:
        return False
    list_unique = list(set(list_cut))
    if len(list_unique) != num:
        return False
    list_convex = get_convex(list_unique)
    if len(list_convex) != num:
        return False

    begin_idx = list_cut.index(list_convex[0])
    anti_clockwise = True
    for i in range(num):
        if list_convex[i] != list_cut[(begin_idx + i) % num]:
            anti_clockwise = False
            break

    clockwise = True
    for i in range(num):
        if list_convex[i] != list_cut[(begin_idx - i + num) % num]:
            clockwise = False
            break

    return clockwise or anti_clockwise


def are_valid(pieces):
    convex_list = []  # [True, False, False]
    for piece in pieces:  # iterate through every piece
        convex_list.append(check_convex(piece))

    for b in convex_list:  # [True, False, False]
        if not b:
            return False
    return True


# ------------------Second Task-----------------------
def normal(piece):  # [(50, 50), (50, 90), (90, 90)]
    num = len(piece)  # num of vertex
    piece_norm = []
    x_avg = sum(piece[i][0] for i in range(num))/len(piece)
    y_avg = sum(piece[i][1] for i in range(num))/len(piece)

    for i in range(num):
        piece_norm.append((piece[i][0]-x_avg, piece[i][1]-y_avg))

    return piece_norm


def is_rotation(ans):
    epsilon = 1e-5  # infinitely small
    if abs(ans[0][0] - ans[1][1]) < epsilon and abs(ans[1][0] + ans[0][1]) < epsilon and abs(ans[0][0]**2 + ans[0][1]**2 - 1) < epsilon:
    # if ans[0][0] == ans[1][1] and ans[1][0] == -ans[0][1] and ans[0][0] ** 2 + ans[0][1] ** 2 == 1:  # wrong for accuracy is not enough
        return True
    else:
        return False


def are_identical_sets_of_coloured_pieces(pieces_1, pieces_2):
    # pieces_1  [ ['red', (50, 50), (50, 90), (90, 90)],
    #             ['green', (160, 170), (160, 130), (120, 130)],
    #             ...
    #            ]
    if len(pieces_1) != len(pieces_2):  # num of pieces must be equal
        return False

    pieces_1.sort()
    pieces_2.sort()

    for i in range(len(pieces_1)):
        if pieces_1[i][0] != pieces_2[i][0]:
            return False
        piece1 = pieces_1[i][1:]  # [(50, 50), (50, 90), (90, 90)]
        piece2 = pieces_2[i][1:]  # [(200, 30), (180, 30), (180, 50), (220, 50)]

        if len(piece1) != len(piece2):
            return False

        piece1_norm = normal(piece1)  # move the center of gravity to the origin
        piece2_norm = normal(piece2)  # [(5.0, -10.0), (-15.0, -10.0), (-15.0, 10.0), (25.0, 10.0)]

        piece2_norm = piece2_norm + piece2_norm

        # no flip case
        x1 = []
        y1 = []
        for j in range(len(piece1_norm)):
            x1.append(piece1_norm[j][0])
            y1.append(piece1_norm[j][1])
        piece1_norm_cal = np.array([x1, y1])

        x2 = []
        y2 = []
        for k in range(len(piece2_norm)):
            x2.append(piece2_norm[k][0])
            y2.append(piece2_norm[k][1])
        piece2_norm_cal = np.array([x2, y2])

        piece2_norm_cal_anti_wise = np.array([x2[::-1], y2[::-1]])

        idx = False
        # flip case
        flip_mat = np.array([[1, 0],
                             [0, -1]])  # upside down
        piece1_norm_cal_flip = flip_mat.dot(piece1_norm_cal)
        num_vertex = len(piece1_norm_cal[0])  # num of vertex
        for n in range(num_vertex):
            clock_ans = piece2_norm_cal[:, n:n+num_vertex].dot(np.linalg.pinv(piece1_norm_cal))  # no flip case
            clock_ans_flip = piece2_norm_cal[:, n:n+num_vertex].dot(np.linalg.pinv(piece1_norm_cal_flip))  # flip case
            anti_clock_ans = piece2_norm_cal_anti_wise[:, n:n+num_vertex].dot(np.linalg.pinv(piece1_norm_cal))  # no flip case
            anti_clock_ans_flip = piece2_norm_cal_anti_wise[:, n:n+num_vertex].dot(np.linalg.pinv(piece1_norm_cal_flip))  # flip case
            is_rotation_clock = is_rotation(clock_ans)
            is_rotation_clock_flip = is_rotation(clock_ans_flip)
            is_rotation_anti_clock = is_rotation(anti_clock_ans)
            is_rotation_anti_clock_flip = is_rotation(anti_clock_ans_flip)
            if is_rotation_clock or is_rotation_clock_flip or is_rotation_anti_clock or is_rotation_anti_clock_flip:
                idx = True
                break

        if not idx:
            return False

    return True


# ------------------Third Task-----------------------
def is_alignment(tangram, shape):
    point_angle = {}
    point_vector = {}
    eps = 1e-5
    for piece in tangram:
        points = piece[1:]
        num = len(points)
        for i in range(num):
            if points[i] in shape:
                continue
            x0 = points[(i - 1 + num) % num][0]
            y0 = points[(i - 1 + num) % num][1]
            x1 = points[i][0]
            y1 = points[i][1]
            x2 = points[(i + 1) % num][0]
            y2 = points[(i + 1) % num][1]
            a = np.array([x0 - x1, y0 - y1])
            b = np.array([x2 - x1, y2 - y1])
            La = np.sqrt(a.dot(a))
            Lb = np.sqrt(b.dot(b))
            a_norm = tuple(a / La)
            b_norm = tuple(b / Lb)
            if La * Lb == 0:
                return False
            cos_angle = a.dot(b) / (La * Lb)
            angle = np.arccos(cos_angle)
            if points[i] not in point_angle:
                point_angle[points[i]] = angle
            else:
                point_angle[points[i]] += angle

            if points[i] not in point_vector:
                point_vector[points[i]] = {a_norm: 1, b_norm: 1}
            else:
                if a_norm not in point_vector[points[i]]:
                    point_vector[points[i]][a_norm] = 1
                else:
                    point_vector[points[i]][a_norm] += 1
                    if point_vector[points[i]][a_norm] > 2:
                        return False
                if b_norm not in point_vector[points[i]]:
                    point_vector[points[i]][b_norm] = 1
                else:
                    point_vector[points[i]][b_norm] += 1
                    if point_vector[points[i]][b_norm] > 2:
                        return False

    for k in point_angle:
        if not (abs(point_angle[k] - np.pi) < eps or abs(point_angle[k] - np.pi * 2) < eps):
            return False
    return True


def is_solution(tangram, shape):
    # tangram: [['green', (30, 60), (30, 20), (70, 20)],
    #           ['purple', (50, 120), (50, 80), (70, 60), (90, 80), (90, 120)],
    #           ['olive', (30, 100), (90, 40), (70, 20), (30, 60)],
    #           ['magenta', (70, 60), (110, 100), (110, 60), (90, 40)],
    #           ['blue', (50, 120), (30, 120), (30, 100), (50, 80)],
    #           ['red', (110, 20), (70, 20), (110, 60)],
    #           ['yellow', (110, 100), (110, 120), (90, 120), (90, 80)]]
    # shape:   [['grey', (30, 20), (110, 20), (110, 120), (30, 120)]]

    # no correct tangram and shape
    if len(shape) != 1:
        return False
    if len(shape[0]) < 4:  # colour and points
        return False
    for p in tangram:
        if len(p) < 4:
            return False

    # shape's points
    shape_points = shape[0][1:]

    # # tangram's points that not belong to shape's points
    # piece_set = set()  # remove duplicate
    # for piece in tangram:
    #     for piece_point in piece[1:]:
    #         if piece_point not in shape_points:
    #             piece_set.add(piece_point)
    #
    # # judge if the points are inside the shape
    # for point in piece_set:
    #     bool_inside = is_inside(point, shape_points)
    #     if not bool_inside:
    #         return False

    if is_alignment(tangram, shape_points):
        return True
    else:
        return False

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值