-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathpolygon.py
More file actions
104 lines (73 loc) · 2.62 KB
/
polygon.py
File metadata and controls
104 lines (73 loc) · 2.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from enum import IntEnum
from math import atan2, degrees
from typing import TypeAlias
from xcoder.math.point import Point
from xcoder.math.rect import Rect
from xcoder.matrices import Matrix2x3
Polygon: TypeAlias = list[Point]
class PointOrder(IntEnum):
CLOCKWISE = 0
COUNTER_CLOCKWISE = 1
def get_polygon_sum_of_edges(polygon: Polygon) -> float:
"""
Mostly like signed area, but two times bigger and more accurate with signs.
https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1165943#1165943
:param polygon:
:return:
"""
points_sum = 0
for i in range(len(polygon)):
p1 = polygon[i]
p2 = polygon[(i + 1) % len(polygon)]
points_sum += (p2.x - p1.x) * (p1.y + p2.y)
return points_sum
def get_polygon_point_order(polygon: Polygon) -> PointOrder | None:
sum_of_edges = get_polygon_sum_of_edges(polygon)
if sum_of_edges > 0:
return PointOrder.CLOCKWISE
elif sum_of_edges < 0:
return PointOrder.COUNTER_CLOCKWISE
return None
def compare_polygons(
polygon1: Polygon,
polygon2: Polygon,
) -> tuple[float, bool]:
"""Calculates rotation and if polygon is mirrored.
:param polygon1: shape polygon
:param polygon2: sheet polygon
:return: rotation degrees, is polygon mirrored
"""
polygon1_order = get_polygon_point_order(polygon1)
polygon2_order = get_polygon_point_order(polygon2)
mirroring = polygon1_order != polygon2_order
dx = (polygon1[1].x - polygon1[0].x) * (-1 if mirroring else 1)
dy = polygon1[1].y - polygon1[0].y
du = polygon2[1].x - polygon2[0].x
dv = polygon2[1].y - polygon2[0].y
# Solution from https://stackoverflow.com/a/21484228/14915825
angle_radians = atan2(dy, dx) - atan2(dv, du)
angle = degrees(angle_radians)
return angle, mirroring
def get_rect(polygon: list[Point]) -> Rect:
"""Calculates polygon bounds and returns rect.
:param polygon: polygon points
:return: Rect object
"""
rect = Rect(left=100000, top=100000, right=-100000, bottom=-100000)
for point in polygon:
rect.add_point(point.x, point.y)
return rect
def apply_matrix(polygon: Polygon, matrix: Matrix2x3 | None = None) -> Polygon:
"""Applies affine matrix to the given polygon. If matrix is none, returns points.
:param polygon: polygon points
:param matrix: Affine matrix
"""
if matrix is None:
return polygon
return [
Point(
matrix.apply_x(point.x, point.y),
matrix.apply_y(point.x, point.y),
)
for point in polygon
]