-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathshape_2d.py
More file actions
125 lines (100 loc) · 4.12 KB
/
Copy pathshape_2d.py
File metadata and controls
125 lines (100 loc) · 4.12 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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import numpy as np
from extras import clamp
class Shape2D:
"""
All a shape requires to be defined are a position, and the points (relative
to that position that make up the shape. This should be in game coordinates).
You can do some maths to translate this into the respective shape.
"""
def __init__(self, position_2: np.ndarray, shape_points_2: np.ndarray):
self._position_2 = np.array([position_2[0], position_2[1], 1])
self._shape_points_2 = shape_points_2
def get_position(self):
"""Returns a shallow copy of the position"""
return np.array([self._position_2[0], self._position_2[1]])
def set_position(self, position_2: np.ndarray):
self._position_2 = np.array([position_2[0], position_2[1], 1])
def convert_to_screen_coordinates(self, camera_matrix_3_3: np.ndarray):
"""
This applies the correct order of matrix multiplications to return the
points projected to the camera/screen. i.e. The camera is fake. The
world moves!
"""
new_points = []
shape_t_matrix = np.array([
[1, 0, self._position_2[0]],
[0, 1, self._position_2[1]],
[0, 0, 1]
])
for x, y in self._shape_points_2:
# Homogenous point
point = np.array([x, y, 1])
# Matrix multiplication order matters
transformed = camera_matrix_3_3 @ shape_t_matrix @ point
new_points.append(transformed[:2])
return np.array(new_points)
class Rect2D(Shape2D):
def __init__(self, position_2: np.ndarray, size_2: np.ndarray):
super().__init__(position_2, np.array([
[0, 0 ],
[0, size_2[1]],
[size_2[0], size_2[1]],
[size_2[0], 0 ],
]))
class Circle2D(Shape2D):
def __init__(self, position_2: np.ndarray, radius: float):
super().__init__(position_2, np.array([
[0, 0],
]))
self._radius = radius
def get_radius(self):
return self._radius
def get_screen_radius(self, camera_scale: float):
return self._radius * camera_scale
def set_radius(self, radius: float):
self._radius = radius
class Camera2D:
def __init__(self, position_2: np.ndarray, scale: float):
self._position_2 = position_2
self._scale = scale
def get_scale(self):
"""Returns a shallow copy of the position"""
return self._scale
def set_scale(self, scale: float):
self._scale = scale
def get_position(self):
"""Returns a shallow copy of the position"""
return self._position_2.copy()
def set_position(self, position_2: np.ndarray):
self._position_2 = position_2
def update_matrices(self, screen_size_2: np.ndarray):
self._scale = clamp(self._scale, 0.05, 200)
self._scale_matrix_3_3 = np.array([
[self._scale, 0, 0],
[0, self._scale, 0],
[0, 0, 1]
])
# Moving the camera left, is effectively moving everything right. Hence
# this matrix is inverted.
self._camera_translation_3_3 = np.array([
[1, 0, -self._position_2[0]],
[0, 1, -self._position_2[1]],
[0, 0, 1]
])
# The center matrix ensures zooming is applied from the center of the
# camera, rather than from (0, 0). It also centres the Camera on (0, 0)
self._center_matrix_3_3 = np.array([
[1, 0, screen_size_2[0] / 2],
[0, 1, screen_size_2[1] / 2],
[0, 0, 1]
])
def get_camera_matrix_3_3(self):
"""
Returns the matrix to convert a coordinate from game coords to
camera/draw coords.
"""
return self._center_matrix_3_3 @ self._scale_matrix_3_3 @ self._camera_translation_3_3
def convert_screen_to_world_coord(self, screen_2: np.ndarray):
return (np.linalg.inv(
self._center_matrix_3_3 @ self._scale_matrix_3_3 @ self._camera_translation_3_3
) @ np.array([screen_2[0], screen_2[1], 1]))[:2]