-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathexercise3.py
More file actions
93 lines (72 loc) · 3.32 KB
/
exercise3.py
File metadata and controls
93 lines (72 loc) · 3.32 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
from exercise1 import Vector
from exercise2 import Point2D
class Rectangle:
def __init__(self, lower_left: Point2D, dx: float, dy: float) -> None:
self._lower_left = lower_left
self._dx = dx
self._dy = dy
def corner(self, i: int) -> Point2D:
assert i < 4
result = Point2D(self._lower_left.x, self._lower_left.y)
result += Vector([
self._dx if self._is_idx_on_right_edge(i) else 0.0,
self._dy if self._is_idx_on_upper_edge(i) else 0.0
])
return result
@property
def lower_left(self) -> Point2D:
return self._lower_left
@property
def upper_right(self) -> Point2D:
return self.corner(3)
# def contains(self, point: Point2D, tolerance: float = 0.0) -> bool: # Task B
def contains(self, point: Point2D, tolerance: float = 0.0) -> bool:
# Task A: remove duplication by defining a function
# that checks if a value is within an interval
# and reuse that here.
def is_in_interval(value: float, start: float, end: float) -> bool:
return start <= value <= end
ll_px = point.x - self._lower_left.x
# Combined Task A and Task B logic
return is_in_interval(ll_px, -tolerance, self._dx + tolerance) and is_in_interval(ll_py, -tolerance, self._dy + tolerance)
# Task B logic:
x_in = (ll_px >= -tolerance) and (ll_px <= self._dx + tolerance)
y_in = (ll_py >= -tolerance) and (ll_py <= self._dy + tolerance)
return x_in and y_in
def _is_idx_on_upper_edge(self, i: int) -> bool:
return i in [2, 3]
def _is_idx_on_right_edge(self, i: int) -> bool:
return i in [1, 3]
# def is_in_interval(...) -> bool: # Task A
def test_rectangle_contains_exact() -> None:
rectangle = Rectangle(lower_left=Point2D(1.0, 2.0), dx=2.5, dy=1.5)
for i in range(4):
assert rectangle.contains(rectangle.corner(i))
def test_rectangle_contains_tolerance() -> None:
rectangle = Rectangle(lower_left=Point2D(1.0, 2.0), dx=2.5, dy=1.5)
lower_left = rectangle.corner(0)
lower_right = rectangle.corner(1)
upper_left = rectangle.corner(2)
upper_right = rectangle.corner(3)
assert rectangle.contains(lower_left)
assert rectangle.contains(upper_left)
assert rectangle.contains(lower_right)
assert rectangle.contains(upper_right)
eps = 1e-10
lower_left -= Vector([eps, eps])
lower_right += Vector([eps, -eps])
upper_left += Vector([-eps, eps])
upper_right += Vector([eps, eps])
assert not rectangle.contains(lower_left)
assert not rectangle.contains(upper_left)
assert not rectangle.contains(lower_right)
assert not rectangle.contains(upper_right)
# Task B: make the tests below pass by adding optional tolerance argument to `contains`
assert not rectangle.contains(lower_left, tolerance=eps/2.0)
assert not rectangle.contains(upper_left, tolerance=eps/2.0)
assert not rectangle.contains(lower_right, tolerance=eps/2.0)
assert not rectangle.contains(upper_right, tolerance=eps/2.0)
assert rectangle.contains(lower_left, tolerance=eps*2.0)
assert rectangle.contains(upper_left, tolerance=eps*2.0)
assert rectangle.contains(lower_right, tolerance=eps*2.0)
assert rectangle.contains(upper_right, tolerance=eps*2.0)