-
Notifications
You must be signed in to change notification settings - Fork 8
/
Rect.py
129 lines (98 loc) · 4.27 KB
/
Rect.py
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from copy import deepcopy
from math import sqrt
from util import PointType
class Rect:
def __init__(self, origin: tuple, width:float, height:float, origin_type: PointType = PointType.BOTTOM_LEFT, rotated:bool = False) -> None:
"""
A container class and data structure for a rect representing a box to be packed.
Main functionality consists of wrapping and data and checking if rects overlap contain points etc.
Parameters
----------
origin, tuple
a tuple of (x,y) coordinates containing the ogigin point of the box. Can be either of corners depending on the case
width, float
the width of the rect
height, float
the height of the rect
origin_type, PointType
an enum value determining which of the four corners is the origin point
rotated, bool
boolean value indicating whether the rect is rotated
"""
assert(0 < width and 0 < height)
if rotated:
temp = height
height = width
width = temp
# Shift origin to bottom left corner depending on what type of point was given
if origin_type == PointType.BOTTOM_LEFT:
self.origin = origin
if origin_type == PointType.TOP_LEFT:
self.origin = (origin[0], origin[1] - height)
if origin_type == PointType.BOTTOM_RIGHT:
self.origin = (origin[0] - width, origin[1])
if origin_type == PointType.TOP_RIGHT:
self.origin = (origin[0] - width, origin[1] - height)
self.width = width
self.height = height
self.rotated = rotated
self.bottom = self.origin[1]
self.top = self.origin[1]+self.height
self.left = self.origin[0]
self.right = self.origin[0]+self.width
self.corner_bot_l = (self.left, self.bottom)
self.corner_top_l = (self.left, self.top)
self.corner_top_r = (self.right, self.top)
self.corner_bot_r = (self.right, self.bottom)
def __copy__(self):
cls = self.__class__
result = cls.__new__(cls)
result.__dict__.update(self.__dict__)
return result
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
setattr(result, k, deepcopy(v, memo))
return result
@property
def area(self) -> float:
return self.width * self.height
def contains(self, point: tuple) -> bool:
""" Return whether the rect contains a given point (x,y) """
return self.corner_bot_l[0] <= point[0] and self.corner_bot_l[1] <= point[1] and point[0] <= self.corner_top_r[0] and point[1] <= self.corner_top_r[1]
def min_distance(self, other) -> float:
"""
Returns the minumum distance between two Rects (AABBs), using an outer Rect method as described
in: https://gamedev.stackexchange.com/questions/154036/efficient-minimum-distance-between-two-axis-aligned-squares
"""
outer_left = min(self.left, other.left)
outer_right = max(self.right, other.right)
outer_bottom = min(self.bottom, other.bottom)
outer_top = max(self.top, other.top)
outer_width = outer_right - outer_left
outer_height = outer_top - outer_bottom
inner_width = max(0, outer_width - self.width - other.width)
inner_height = max(0, outer_height - self.height - other.height)
# TODO: Might be able to remove a sqrt here, not sure
return sqrt(inner_width**2 + inner_height**2)
def overlaps(self, other) -> bool:
"""
Returns wether two Rects overlap
"""
if self.right <= other.left or other.right <= self.left:
return False
if self.top <= other.bottom or other.top <= self.bottom:
return False
return True
def __iter__(self):
"""
Iterate through rectangle corners
"""
yield self.corner_bot_l
yield self.corner_top_l
yield self.corner_top_r
yield self.corner_bot_r
def __repr__(self):
return "R = (({}, {}), w={}, h={},r={})".format(self.origin[0], self.origin[1], self.width, self.height,self.rotated)