Advertisement
Guest User

Untitled

a guest
May 26th, 2019
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.43 KB | None | 0 0
  1. import sys
  2. import math
  3. import numpy
  4. import random
  5. from PyQt5.QtCore import Qt, QPoint, QRectF
  6. from PyQt5.QtGui import QPainter, QPen, QBrush
  7. from PyQt5.QtWidgets import QMainWindow, QWidget, QApplication
  8.  
  9.  
  10. class MyMainWindow(QMainWindow):
  11. def __init__(self):
  12. super().__init__()
  13. self.setWindowTitle("Построение выпуклых многоугольников")
  14. self.setGeometry(180, 100, 600, 600) # начало в экр., ширина, высота
  15. palette = self.palette()
  16. palette.setColor(self.backgroundRole(), Qt.white)
  17. self.setPalette(palette)
  18. self.form_widget = MyFormWidget()
  19. self.setCentralWidget(self.form_widget)
  20.  
  21.  
  22. class MyFormWidget(QWidget):
  23. def __init__(self):
  24. super().__init__()
  25. self.poly_1_true = {'bottom1': [(200, 200, 0), (250, 150, 50), (100, 200, 0)], # bottom
  26. 'back1': [(250, 150, 50), (150, 100, 0), (100, 200, 0)], # back
  27. 'right1': [(200, 200, 0), (250, 150, 50), (150, 100, 0)], # right
  28. 'front1': [(100, 200, 0), (200, 200, 0), (150, 100, 0)]} # front
  29.  
  30. self.poly_2_true = {'left2': [(200, 150, 0), (200, 50, 0), (250, 20, 100), (250, 120, 100)], # left
  31. 'bottom2': [(200, 150, 0), (300, 150, 0), (350, 120, 100), (250, 120, 100)], # bottom
  32. 'back2': [(350, 120, 100), (350, 20, 100), (250, 20, 100), (250, 120, 100)], # back
  33. 'top2': [(200, 50, 0), (300, 50, 0), (350, 20, 100), (250, 20, 100)], # top
  34. 'right2': [(300, 50, 0), (300, 150, 0), (350, 120, 100), (350, 20, 100)], # right
  35. 'front2': [(200, 150, 0), (200, 50, 0), (300, 50, 0), (300, 150, 0)]} # front
  36.  
  37. def paintEvent(self, QPaintEvent):
  38. self.qp = QPainter()
  39. self.qp.begin(self)
  40. self.y_segments = {}
  41. self.side_color = {}
  42. self.min_y, self.max_y = self.get_min_max_y()
  43. self.find_segment_for_y()
  44. self.set_color()
  45. self.segments_painter()
  46. self.qp.end()
  47.  
  48. def get_vertex(self, poly):
  49. vertex = set()
  50. for side in poly.keys():
  51. for x in poly[side]:
  52. vertex.add(x)
  53. return [*vertex]
  54.  
  55. def local_min_max_y(self, vertex):
  56. return min(v[1] for v in vertex), max(v[1] for v in vertex)
  57.  
  58. def get_min_max_y(self):
  59. poly_vert_1 = self.get_vertex(self.poly_1_true)
  60. poly_vert_2 = self.get_vertex(self.poly_2_true)
  61. y_min_1, y_max_1 = self.local_min_max_y(poly_vert_1)
  62. y_min_2, y_max_2 = self.local_min_max_y(poly_vert_2)
  63. return min(y_min_1, y_min_2), max(y_max_1, y_max_2)
  64.  
  65. def find_bones(self, side):
  66. return [(side[i], side[i - 1]) for i in range(len(side))]
  67.  
  68. def get_bones_intersection(self, y, bones):
  69. bones_res = []
  70. for v1, v2 in bones:
  71. if v1[1] <= y <= v2[1] or v2[1] <= y <= v1[1]:
  72. bones_res.append((v1, v2))
  73. return bones_res
  74.  
  75. def get_equa_bones(self, bone):
  76. # представление ребра в виде уравнений
  77. x1, y1, z1 = bone[0]
  78. x2, y2, z2 = bone[1]
  79. p = (x2-x1, y2-y1, z2-z1) # == M направляющий вектор
  80. return bone[0], p
  81.  
  82. def find_inters_y_bone(self, y, bone):
  83. # ищем координаты x, z точек пересечения у и ребер
  84. coords, p = self.get_equa_bones(bone)
  85. if p[0] and p[1] and p[2]:
  86. x = round(p[0]/p[1]*(y-coords[1]) + coords[0])
  87. z = round(p[2]/p[1]*(y-coords[1]) + coords[2])
  88. return x, z
  89. elif not p[0] and not p[2]:
  90. return coords[0], coords[2]
  91. elif not p[0]:
  92. x = coords[0]
  93. z = round(p[2] / p[1] * (y - coords[1]) + coords[2])
  94. return x, z
  95. elif not p[2]:
  96. x = round(p[0] / p[1] * (y - coords[1]) + coords[0])
  97. z = coords[2]
  98. return x, z
  99.  
  100. def get_special_segment_inters(self, y, bone):
  101. end1, end2 = (), ()
  102. if bone[0][1] == y == bone[1][1]:
  103. x1 = min(bone[0][0], bone[1][0]) # первый конец по х
  104. x2 = max(bone[0][0], bone[1][0]) # второй
  105. z1 = min(bone[0][2], bone[1][2])
  106. z2 = max(bone[0][2], bone[1][2])
  107. return (x1, z1), (x2, z2)
  108. return end1, end2
  109.  
  110. def find_equal_y_x_z_coord(self, y, bones):
  111. for bone in bones:
  112. if y == bone[0][1]:
  113. return bone[0][0], bone[0][2]
  114. if y == bone[1][1]:
  115. return bone[1][0], bone[1][2]
  116. return -1, -1
  117.  
  118. def find_polygon_segment(self, y, polygon):
  119. # return dict of {((x1, z1), (x2, z2)): 'bottom1' ...
  120. segm_side = {}
  121. for name_side in polygon: # набираем точки на конкретной плоскости Оxz для у
  122. pre_inters_points = set()
  123. y_min, y_max = self.local_min_max_y(polygon[name_side])
  124. if y < y_min or y > y_max:
  125. continue
  126. if (y_min < y < y_max):
  127. # y точно внутри грани
  128. # нужно найти пересечения у с ребрами, которые задаются началом и концом и уравнением
  129. bones = self.find_bones(polygon[name_side])
  130. need_bones = self.get_bones_intersection(y, bones)
  131. for bone in need_bones:
  132. x, z = self.find_inters_y_bone(y, bone) # x, z
  133. pre_inters_points.add((x, z))
  134. else:
  135. # попали либо в ребро, либо в вершину
  136. # нужно определить, во что конкретно
  137. bones = self.find_bones(polygon[name_side])
  138. flag_found = False
  139. for bone in bones: # ищем отрезки
  140. end1, end2 = self.get_special_segment_inters(y, bone)
  141. if end1 and end2:
  142. pre_inters_points.add(end1)
  143. pre_inters_points.add(end2)
  144. flag_found = True
  145. if flag_found:
  146. break
  147. if not flag_found: # не нашли отрезки, ищем вершины
  148. x, z = self.find_equal_y_x_z_coord(y, bones)
  149. if x > -1 and z > -1:
  150. pre_inters_points.add((x, z))
  151. if pre_inters_points:
  152. if len(pre_inters_points) == 2:
  153. inters_points = [x for x in pre_inters_points]
  154. if inters_points[0] not in segm_side.keys():
  155. segm_side[inters_points[0]] = [name_side] # дб две точки пересечения с коорд x и z
  156. else:
  157. segm_side[inters_points[0]].append(name_side)
  158. if inters_points[1] not in segm_side.keys():
  159. segm_side[inters_points[1]] = [name_side] # дб две точки пересечения с коорд x и z
  160. else:
  161. segm_side[inters_points[1]].append(name_side)
  162. elif len(pre_inters_points) == 1:
  163. inters_points = [x for x in pre_inters_points]
  164. if inters_points[0] not in segm_side.keys():
  165. segm_side[inters_points[0]] = [name_side] # дб две точки пересечения с коорд x и z
  166. else:
  167. segm_side[inters_points[0]].append(name_side)
  168. return segm_side
  169.  
  170. def get_segments(self, segm_sides):
  171. # возвращает словарь отрезок ((), ()): имя грани
  172. founded_segms = {}
  173. for vertex in segm_sides.keys():
  174. sides1 = segm_sides[vertex]
  175. for side in sides1:
  176. for vertex_2 in segm_sides.keys():
  177. if vertex == vertex_2:
  178. continue
  179. sides2 = segm_sides[vertex_2]
  180. common = list(set(sides1) & set(sides2))
  181. if common:
  182. ends = (vertex, vertex_2) if vertex[0] < vertex_2[0] else (vertex_2, vertex)
  183. founded_segms[ends] = common[0]
  184. return founded_segms
  185.  
  186. def get_segm_eqv(self, segment):
  187. # return A, C, D for Ax + Cz + D = 0
  188. x1, y1 = segment[0]
  189. x2, y2 = segment[1]
  190. x_dis = x2-x1
  191. y_dis = y2-y1
  192. # A = y_dis, C = -x_dis, D = -x1*y_dis+y1*x_dis
  193. return (y_dis, -x_dis, -x1*y_dis+y1*x_dis)
  194.  
  195. def find_2_straights_inters(self, stra1, stra2):
  196. # point of intersection of 2 straight (use in next)
  197. main_det = round(numpy.linalg.det([[stra1[0], stra1[1]], [stra2[0], stra2[1]]]))
  198. if main_det:
  199. x_det = round(numpy.linalg.det([[-stra1[2], stra1[1]], [-stra2[2], stra2[1]]]))
  200. z_det = round(numpy.linalg.det([[stra1[0], -stra1[2]], [stra2[0], -stra2[2]]]))
  201. x_c = int(round(x_det / main_det))
  202. z_c = int(round(z_det / main_det))
  203. x_c_i = 0 if x_c == -0 else x_c
  204. z_c_i = 0 if z_c == -0 else z_c
  205. return x_c_i, z_c_i
  206. return None
  207.  
  208. def is_between(self, end_p_1, end_p_2, point):
  209. return (end_p_1[0] <= point[0] <= end_p_2[0] or end_p_2[0] <= point[0] <= end_p_1[0]) and \
  210. (end_p_1[1] <= point[1] <= end_p_2[1] or end_p_2[1] <= point[1] <= end_p_1[1])
  211.  
  212. def find_all_straights_intersections(self, straights, straights_with_ends, segm_on_side):
  213. # ищем все точки пересечения
  214. intersections = {}
  215. for i in range(len(straights)):
  216. for j in range(len(straights)):
  217. inter_v = self.find_2_straights_inters(straights[i], straights[j])
  218. if inter_v:
  219. a, b = straights_with_ends[straights[i]]
  220. if self.is_between(a, b, inter_v):
  221. c, d = straights_with_ends[straights[j]]
  222. if self.is_between(c, d, inter_v):
  223. if inter_v not in intersections.keys():
  224. intersections[inter_v] = [segm_on_side[(a, b)], segm_on_side[(c, d)]]
  225. return intersections
  226.  
  227. def get_segments_intersection(self, segm_on_side):
  228. # return dict {(140, 0): ['front1', 'back1']...
  229. segm_eqv = {} # ((140, 0), (190, 20)): (A, C, D) for Ax + Cz + D = 0
  230. straights = []
  231. straights_with_ends = {}
  232. straights_on_side = {}
  233. for segm in segm_on_side.keys():
  234. # segm == ((140, 0), (190, 20))
  235. eqv = self.get_segm_eqv(segm)
  236. segm_eqv[segm] = eqv
  237. straights.append(eqv)
  238. straights_with_ends[eqv] = segm
  239. straights_on_side[eqv] = segm_on_side[segm]
  240. all_plane_points = self.find_all_straights_intersections(straights, straights_with_ends, segm_on_side)
  241. print('str w ends: ' + str(straights_with_ends))
  242. print('segm on side: ' + str(segm_on_side))
  243. print('str on side: ' + str(straights_on_side))
  244. return all_plane_points, straights_with_ends, segm_on_side, straights_on_side
  245.  
  246. def collect_z_for_x(self, x, stra_w_ends, stra_on_side):
  247. z_set = set()
  248. # z_stra_dict = {}
  249. z_stra_dict_side = {}
  250. for stra in stra_w_ends.keys():
  251. if stra_w_ends[stra][0][0] <= x <= stra_w_ends[stra][1][0]:
  252. try:
  253. z_for_x = -round((stra[0] * x + stra[2]) / stra[1])
  254. z_set.add(z_for_x)
  255. if not z_for_x in z_stra_dict_side.keys():
  256. # z_stra_dict[z_for_x] = [stra]
  257. z_stra_dict_side[z_for_x] = [stra_on_side[stra]]
  258. else:
  259. # z_stra_dict[z_for_x].append(stra)
  260. z_stra_dict_side[z_for_x].append(stra_on_side[stra])
  261. except ZeroDivisionError:
  262. continue
  263. return z_set, z_stra_dict_side
  264.  
  265. def get_segment_for_paint_with_side(self, points, stra_w_ends, stra_on_side):
  266. segments_on_side = {}
  267. for i in range(len(points)-1):
  268. x1 = points[i][0]
  269. x2 = points[i+1][0]
  270. if x1 == x2:
  271. continue
  272. z1_set, z1_stra_dict_side = self.collect_z_for_x(x1, stra_w_ends, stra_on_side)
  273. z2_set, z2_stra_dict_side = self.collect_z_for_x(x2, stra_w_ends, stra_on_side)
  274. z1_list = [z for z in z1_set]
  275. z2_list = [z for z in z2_set]
  276. z1_list.sort()
  277. z2_list.sort()
  278. z1_p = -1
  279. z2_p = -1
  280. flag_end = False
  281. common_stra = []
  282. for tz1 in z1_list:
  283. for tz2 in z2_list:
  284. common_stra = list(set(z1_stra_dict_side[tz1]) & set(z2_stra_dict_side[tz2]))
  285. if common_stra:
  286. z1_p = tz1
  287. z2_p = tz2
  288. flag_end = True
  289. break
  290. if flag_end:
  291. break
  292. if z1_p > -1 and z2_p > -1:
  293. segments_on_side[(x1, x2)] = common_stra
  294. return segments_on_side
  295.  
  296. def get_segment_one_vertex(self, s_vertex, segm_side_vertex, segm_sides, s_ends):
  297. segments_res = {}
  298. x_min = min(p[0] for p in s_ends)
  299. x_max = max(p[0] for p in s_ends)
  300. if s_vertex[0][0] < x_min or s_vertex[0][0] > x_max:
  301. segments_res[(s_vertex[0][0], s_vertex[0][0])] = segm_side_vertex[s_vertex[0]][0]
  302. for p_end in [end for end in s_ends if end[0] == x_min or end[0] == x_max]:
  303. if s_vertex[0][0] == p_end[0]:
  304. if s_vertex[0][1] > p_end[1]:
  305. segments_res[(s_vertex[0][0], s_vertex[0][0])] = segm_side_vertex[s_vertex[0]][0]
  306. # критические случаи, когда точка совсем вне отрезков и на границе максимальных закончились
  307. # нужно достроить оставшийся кусок, где была больше чем 1 точка
  308. segmes_on_side = self.get_segments(segm_sides)
  309. vertex_on_side, str_w_ends, segm_on_side, str_on_side = self.get_segments_intersection(segmes_on_side) # ALL vertex on plane with side DICT!
  310. all_points = [p for p in vertex_on_side.keys()]
  311. all_points.sort()
  312. normal_segment_res = self.get_segment_for_paint_with_side(all_points, str_w_ends, str_on_side) # seems to dict {(x1, x2): side...
  313. for x1x2 in normal_segment_res.keys():
  314. segments_res[x1x2] = normal_segment_res[x1x2]
  315. return segments_res
  316.  
  317. def find_segments(self, segm_sides_1, segm_sides_2):
  318. # ищет отрезки для отрисовки
  319. segments = {} # dict: {(x1, x2): side_name...
  320. s_ends_1 = [point for point in segm_sides_1.keys()]
  321. s_ends_2 = [point for point in segm_sides_2.keys()]
  322. if len(s_ends_1) == 1 and len(s_ends_2) == 1: # пришли две вершины (напр. пирамиды)
  323. print('no')
  324. segments[(s_ends_1[0], s_ends_1[0])] = segm_sides_1[0]
  325. segments[(s_ends_2[0], s_ends_2[0])] = segm_sides_2[0]
  326. elif len(s_ends_1) == 1: # первые сегменты - вершина, вторые - норм
  327. print('yes')
  328. tmp_segm = self.get_segment_one_vertex(s_ends_1, segm_sides_1, segm_sides_2, s_ends_2)
  329. for xx in tmp_segm.keys():
  330. segments[xx] = tmp_segm[xx]
  331. elif len(s_ends_2) == 1: # первые сегменты - норм, вторые - вершина
  332. print('not again')
  333. tmp_segm = self.get_segment_one_vertex(s_ends_2, segm_sides_2, segm_sides_1, s_ends_1)
  334. for xx in tmp_segm.keys():
  335. segments[xx] = tmp_segm[xx]
  336. else: # оба сегмента - норм
  337. all_points_set = set([p for p in segm_sides_1.keys()] + [p for p in segm_sides_2.keys()])
  338. all_points = [p for p in all_points_set]
  339. all_points.sort()
  340. all_segm_sides = {}
  341. for p in segm_sides_1.keys():
  342. all_segm_sides[p] = segm_sides_1[p]
  343. for p in segm_sides_2.keys():
  344. all_segm_sides[p] = segm_sides_2[p]
  345. segmes_on_side = self.get_segments(all_segm_sides)
  346. _, str_w_ends, _, str_on_side = self.get_segments_intersection(segmes_on_side)
  347. tmp_segm = self.get_segment_for_paint_with_side(all_points, str_w_ends, str_on_side)
  348. for xx in tmp_segm.keys():
  349. segments[xx] = tmp_segm[xx]
  350. return segments
  351.  
  352. def find_segment_for_y(self):
  353. # segments_pro = {}
  354. for y in range(self.min_y, self.max_y):
  355. segm_sides_poly_1 = self.find_polygon_segment(y, self.poly_1_true)
  356. segm_sides_poly_2 = self.find_polygon_segment(y, self.poly_2_true)
  357. print('ssp_1' + str(segm_sides_poly_1))
  358. print('ssp_2' + str(segm_sides_poly_2))
  359. self.y_segments[y] = self.find_segments(segm_sides_poly_1, segm_sides_poly_2)
  360.  
  361. def set_color(self):
  362. d_color = {1: Qt.darkYellow, 2: Qt.darkRed, 3: Qt.darkCyan, 4: Qt.darkGreen,
  363. 5: Qt.darkBlue, 6: Qt.darkGray, 7: Qt.darkMagenta, 8: Qt.black, 9: Qt.blue,
  364. 10: Qt.red, 11: Qt.green}
  365. for side in self.poly_1_true:
  366. n_color = random.randrange(1, 11)
  367. self.side_color[side] = d_color[n_color]
  368. for side in self.poly_2_true:
  369. n_color = random.randrange(1, 11)
  370. self.side_color[side] = d_color[n_color]
  371.  
  372. def segments_painter(self):
  373. for y in self.y_segments.keys():
  374. for x1, x2 in self.y_segments[y]:
  375. self.qp.setPen(QPen(self.side_color[self.y_segments[y][(x1, x2)][0]], 2))
  376. p1 = QPoint(x1, y)
  377. p2 = QPoint(x2, y)
  378. self.qp.drawLine(p1, p2)
  379.  
  380.  
  381. if __name__ == '__main__':
  382. app = QApplication(sys.argv)
  383. window = MyMainWindow()
  384. window.show()
  385. sys.exit(app.exec_())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement