七巧板拼接
程序员文章站
2022-03-03 11:13:05
...
知识点:
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
上一篇: 144. 二叉树的前序遍历
下一篇: canvas七巧板