e2D 1.4.11__tar.gz → 1.4.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {e2d-1.4.11 → e2d-1.4.12}/PKG-INFO +2 -2
- {e2d-1.4.11 → e2d-1.4.12}/README.md +1 -1
- e2d-1.4.12/e2D/__init__.py +589 -0
- e2d-1.4.11/e2D/__init__.py → e2d-1.4.12/e2D/__init__.pyi +397 -485
- e2d-1.4.12/e2D/envs.py +170 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D/plots.py +145 -101
- e2d-1.4.12/e2D/utils.py +186 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D/winrec.py +1 -5
- {e2d-1.4.11 → e2d-1.4.12}/e2D.egg-info/PKG-INFO +2 -2
- {e2d-1.4.11 → e2d-1.4.12}/e2D.egg-info/SOURCES.txt +1 -0
- {e2d-1.4.11 → e2d-1.4.12}/setup.cfg +1 -1
- e2d-1.4.11/e2D/envs.py +0 -221
- e2d-1.4.11/e2D/utils.py +0 -86
- {e2d-1.4.11 → e2d-1.4.12}/LICENSE +0 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D/cvb.py +0 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D.egg-info/dependency_links.txt +0 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D.egg-info/requires.txt +0 -0
- {e2d-1.4.11 → e2d-1.4.12}/e2D.egg-info/top_level.txt +0 -0
- {e2d-1.4.11 → e2d-1.4.12}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: e2D
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.12
|
|
4
4
|
Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
|
|
5
5
|
Home-page: https://github.com/marick-py/e2D
|
|
6
6
|
Author: Riccardo Mariani
|
|
@@ -263,7 +263,7 @@ e2D is open-source software licensed under the [MIT License](LICENSE).
|
|
|
263
263
|
|
|
264
264
|
## Contact
|
|
265
265
|
|
|
266
|
-
For inquiries, you can reach me at [
|
|
266
|
+
For inquiries, you can reach me at [emptyhead.dev@gmail.com](mailto:emptyhead.dev@gmail.com).
|
|
267
267
|
|
|
268
268
|
## Acknowledgements
|
|
269
269
|
The e2D is developed and maintained by [marick-py](https://github.com/marick-py) but is inspired by the Pygame library and various other game development resources. We would like to express our gratitude to the Pygame community and all the developers who have contributed to the open-source projects that made e2D possible.
|
|
@@ -247,7 +247,7 @@ e2D is open-source software licensed under the [MIT License](LICENSE).
|
|
|
247
247
|
|
|
248
248
|
## Contact
|
|
249
249
|
|
|
250
|
-
For inquiries, you can reach me at [
|
|
250
|
+
For inquiries, you can reach me at [emptyhead.dev@gmail.com](mailto:emptyhead.dev@gmail.com).
|
|
251
251
|
|
|
252
252
|
## Acknowledgements
|
|
253
253
|
The e2D is developed and maintained by [marick-py](https://github.com/marick-py) but is inspired by the Pygame library and various other game development resources. We would like to express our gratitude to the Pygame community and all the developers who have contributed to the open-source projects that made e2D possible.
|
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math as _mt
|
|
4
|
+
import random as _rnd
|
|
5
|
+
|
|
6
|
+
PI = _mt.pi
|
|
7
|
+
HALF_PI = PI/2
|
|
8
|
+
QUARTER_PI = PI/4
|
|
9
|
+
DOUBLE_PI = PI*2
|
|
10
|
+
|
|
11
|
+
sign = lambda val: -1 if val < 0 else (1 if val > 0 else 0)
|
|
12
|
+
|
|
13
|
+
class Vector2D:
|
|
14
|
+
round_values_on_print = 2
|
|
15
|
+
def __init__(self, x=.0, y=.0) -> None:
|
|
16
|
+
self.x = x
|
|
17
|
+
self.y = y
|
|
18
|
+
|
|
19
|
+
def distance_to(self, other, sqrd=True) -> int|float:
|
|
20
|
+
d = (self.x - other.x)**2 + (self.y - other.y)**2
|
|
21
|
+
return (d**(1/2) if sqrd else d)
|
|
22
|
+
|
|
23
|
+
def angle_to(self, other) -> int|float:
|
|
24
|
+
return _mt.atan2(other.y - self.y, other.x - self.x)
|
|
25
|
+
|
|
26
|
+
def point_from_angle_and_radius(self, rad, radius) -> "Vector2D":
|
|
27
|
+
return Vector2D(radius * _mt.cos(rad) + self.x, radius * _mt.sin(rad) + self.y)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def angle(self) -> int|float:
|
|
31
|
+
return _mt.atan2(self.y, self.x)
|
|
32
|
+
|
|
33
|
+
@angle.setter
|
|
34
|
+
def angle(self, argv) -> None:
|
|
35
|
+
print(argv)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def copy(self) -> "Vector2D":
|
|
39
|
+
return Vector2D(self.x, self.y)
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def sign(self) -> "Vector2D":
|
|
43
|
+
return Vector2D(sign(self.x), sign(self.y))
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def normalize(self) -> "Vector2D":
|
|
47
|
+
if (mag:=self.length) == 0:
|
|
48
|
+
return self.copy
|
|
49
|
+
return Vector2D(self.x / mag, self.y / mag)
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def length(self) -> float:
|
|
53
|
+
return (self.x ** 2 + self.y ** 2) ** .5
|
|
54
|
+
|
|
55
|
+
def floor(self, n=1) -> "Vector2D":
|
|
56
|
+
return self.__floor__(n)
|
|
57
|
+
|
|
58
|
+
def ceil(self, n=1) -> "Vector2D":
|
|
59
|
+
return self.__ceil__(n)
|
|
60
|
+
|
|
61
|
+
def round(self, n=1) -> "Vector2D":
|
|
62
|
+
return self.__round__(n)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def randomize(cls, start, end) -> "Vector2D":
|
|
66
|
+
if not isinstance(start, Vector2D):
|
|
67
|
+
if isinstance(start, (int, float)):
|
|
68
|
+
start = Vector2D(start, start)
|
|
69
|
+
else:
|
|
70
|
+
raise Exception(f"\nArg start must be in [Vector2D, int, float, tuple, list] not a [{type(start)}]\n")
|
|
71
|
+
if not isinstance(end, Vector2D):
|
|
72
|
+
if isinstance(end, (int, float)):
|
|
73
|
+
end = Vector2D(end, end)
|
|
74
|
+
else:
|
|
75
|
+
raise Exception(f"\nArg end must be in [Vector2D, int, float, tuple, list] not a [{type(end)}]\n")
|
|
76
|
+
return start + Vector2D(_rnd.random(), _rnd.random()) * (end - start)
|
|
77
|
+
|
|
78
|
+
def dot_product(self, other) -> float:
|
|
79
|
+
return self.x * other.x + self.y * other.y
|
|
80
|
+
|
|
81
|
+
def projection(self, other) -> "Vector2D":
|
|
82
|
+
dot_product = self.dot_product(other)
|
|
83
|
+
magnitude_product = other.length ** 2
|
|
84
|
+
if magnitude_product == 0:
|
|
85
|
+
raise ValueError("Cannot calculate projection for zero vectors.")
|
|
86
|
+
return other * (dot_product / magnitude_product)
|
|
87
|
+
|
|
88
|
+
def reflection(self, normal) -> "Vector2D":
|
|
89
|
+
return self - self.projection(normal) * 2
|
|
90
|
+
|
|
91
|
+
def cartesian_to_polar(self) -> tuple:
|
|
92
|
+
r = self.length
|
|
93
|
+
theta = _mt.atan2(self.y, self.x)
|
|
94
|
+
return r, theta
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def polar_to_cartesian(cls, r, theta) -> "Vector2D":
|
|
98
|
+
x = r * _mt.cos(theta)
|
|
99
|
+
y = r * _mt.sin(theta)
|
|
100
|
+
return cls(x, y)
|
|
101
|
+
|
|
102
|
+
def cartesian_to_complex(self) -> complex:
|
|
103
|
+
return self.x + self.y * 1j
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def complex_to_cartesian(cls, complex_n) -> "Vector2D":
|
|
107
|
+
return cls(complex_n.real, complex_n.imag)
|
|
108
|
+
|
|
109
|
+
def lerp(self, other, t=.1) -> "Vector2D":
|
|
110
|
+
other = Vector2D.__normalize__(other)
|
|
111
|
+
if not 0 <= t <= 1:
|
|
112
|
+
raise ValueError("t must be between 0 and 1 for linear interpolation.")
|
|
113
|
+
return Vector2D(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
|
|
114
|
+
|
|
115
|
+
def rotate(self, angle, center=None) -> "Vector2D":
|
|
116
|
+
if center is None: center = Vector2D.zero()
|
|
117
|
+
translated = self - center
|
|
118
|
+
cos_angle = _mt.cos(angle)
|
|
119
|
+
sin_angle = _mt.sin(angle)
|
|
120
|
+
return Vector2D(translated.x * cos_angle - translated.y * sin_angle, translated.x * sin_angle + translated.y * cos_angle) + center
|
|
121
|
+
|
|
122
|
+
def no_zero_div_error(self, n, error_mode="zero") -> "Vector2D":
|
|
123
|
+
if isinstance(n, (int, float)):
|
|
124
|
+
if n == 0:
|
|
125
|
+
return Vector2D(0 if error_mode == "zero" else (self.x if error_mode == "null" else _mt.nan), 0 if error_mode == "zero" else (self.y if error_mode == "null" else _mt.nan))
|
|
126
|
+
else:
|
|
127
|
+
return self / n
|
|
128
|
+
elif isinstance(n, Vector2D):
|
|
129
|
+
return Vector2D((0 if error_mode == "zero" else (self.x if error_mode == "null" else _mt.nan)) if n.x == 0 else self.x / n.x, (0 if error_mode == "zero" else (self.y if error_mode == "null" else _mt.nan)) if n.y == 0 else self.y / n.y) #type: ignore
|
|
130
|
+
else:
|
|
131
|
+
raise Exception(f"\nArg n must be in [Vector2D, int, float, tuple, list] not a [{type(n)}]\n")
|
|
132
|
+
|
|
133
|
+
def min(self, other) -> "Vector2D":
|
|
134
|
+
return Vector2D(min(self.x, other.x), min(self.y, other.y))
|
|
135
|
+
|
|
136
|
+
def max(self, other) -> "Vector2D":
|
|
137
|
+
return Vector2D(max(self.x, other.x), max(self.y, other.y))
|
|
138
|
+
|
|
139
|
+
def advanced_stringify(self, precision=None, use_scientific_notation=False, return_as_list=False) -> str|list[str]:
|
|
140
|
+
precision = self.round_values_on_print if precision == None else precision
|
|
141
|
+
def optimize(value) -> str:
|
|
142
|
+
abs_value = abs(value)
|
|
143
|
+
if abs_value < 1/10**precision and abs_value != 0:
|
|
144
|
+
return f"{value:.{precision}e}"
|
|
145
|
+
elif abs_value < 10**precision:
|
|
146
|
+
return f"{value:.{precision}f}".rstrip('0').rstrip('.')
|
|
147
|
+
else:
|
|
148
|
+
return f"{value:.{precision}e}"
|
|
149
|
+
if return_as_list:
|
|
150
|
+
return [f"{optimize(self.x)}", f"{optimize(self.y)}"] if use_scientific_notation else [f"{self.x:.{precision}f}", f"{self.y:.{precision}f}"]
|
|
151
|
+
return f"{optimize(self.x)}, {optimize(self.y)}" if use_scientific_notation else f"{self.x:.{precision}f}, {self.y:.{precision}f}"
|
|
152
|
+
|
|
153
|
+
def __str__(self) -> str:
|
|
154
|
+
return f"{self.x:.{self.round_values_on_print}f}, {self.y:.{self.round_values_on_print}f}"
|
|
155
|
+
|
|
156
|
+
def __repr__(self) -> str:
|
|
157
|
+
return f"x:{self.x:.{self.round_values_on_print}f}\ty:{self.y:.{self.round_values_on_print}f}"
|
|
158
|
+
|
|
159
|
+
def __call__(self) -> list:
|
|
160
|
+
return [self.x, self.y]
|
|
161
|
+
|
|
162
|
+
# fast operations Vector2D.operation(both,x,y)
|
|
163
|
+
def add(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
164
|
+
return Vector2D(self.x + (x + both), self.y + (y + both))
|
|
165
|
+
|
|
166
|
+
def sub(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
167
|
+
return Vector2D(self.x - (x + both), self.y - (y + both))
|
|
168
|
+
|
|
169
|
+
def mult(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
170
|
+
return Vector2D(self.x * (x + both), self.y * (y + both))
|
|
171
|
+
|
|
172
|
+
def pow(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
173
|
+
return Vector2D(self.x ** (x + both), self.y ** (y + both))
|
|
174
|
+
|
|
175
|
+
def mod(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
176
|
+
return Vector2D(self.x % (x + both), self.y % (y + both))
|
|
177
|
+
|
|
178
|
+
def div(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
179
|
+
return Vector2D(self.x / (x + both), self.y / (y + both))
|
|
180
|
+
|
|
181
|
+
def fdiv(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
182
|
+
return Vector2D(self.x // (x + both), self.y // (y + both))
|
|
183
|
+
|
|
184
|
+
# fast inplace operations Vector2D.ioperation(both,x,y)
|
|
185
|
+
def set(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
186
|
+
self.x = x + both
|
|
187
|
+
self.y = y + both
|
|
188
|
+
return self
|
|
189
|
+
|
|
190
|
+
def iadd(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
191
|
+
self.x += x + both
|
|
192
|
+
self.y += y + both
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
def isub(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
196
|
+
self.x -= x + both
|
|
197
|
+
self.y -= y + both
|
|
198
|
+
return self
|
|
199
|
+
|
|
200
|
+
def imult(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
201
|
+
self.x *= x + both
|
|
202
|
+
self.y *= y + both
|
|
203
|
+
return self
|
|
204
|
+
|
|
205
|
+
def ipow(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
206
|
+
self.x **= x + both
|
|
207
|
+
self.y **= y + both
|
|
208
|
+
return self
|
|
209
|
+
|
|
210
|
+
def imod(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
211
|
+
self.x %= x + both
|
|
212
|
+
self.y %= y + both
|
|
213
|
+
return self
|
|
214
|
+
|
|
215
|
+
def idiv(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
216
|
+
self.x /= x + both
|
|
217
|
+
self.y /= y + both
|
|
218
|
+
return self
|
|
219
|
+
|
|
220
|
+
def ifdiv(self, both=.0, x=.0, y=.0) -> Vector2D:
|
|
221
|
+
self.x //= x + both
|
|
222
|
+
self.y //= y + both
|
|
223
|
+
return self
|
|
224
|
+
|
|
225
|
+
# normal operations Vector2D + a
|
|
226
|
+
def __add__(self, other) -> "Vector2D":
|
|
227
|
+
other = Vector2D.__normalize__(other)
|
|
228
|
+
return Vector2D(self.x + other.x, self.y + other.y)
|
|
229
|
+
|
|
230
|
+
def __sub__(self, other) -> "Vector2D":
|
|
231
|
+
other = Vector2D.__normalize__(other)
|
|
232
|
+
return Vector2D(self.x - other.x, self.y - other.y)
|
|
233
|
+
|
|
234
|
+
def __mul__(self, other) -> "Vector2D":
|
|
235
|
+
other = Vector2D.__normalize__(other)
|
|
236
|
+
return Vector2D(self.x * other.x, self.y * other.y)
|
|
237
|
+
|
|
238
|
+
def __mod__(self, other) -> "Vector2D":
|
|
239
|
+
other = Vector2D.__normalize__(other)
|
|
240
|
+
return Vector2D(self.x % other.x, self.y % other.y)
|
|
241
|
+
|
|
242
|
+
def __pow__(self, other) -> "Vector2D":
|
|
243
|
+
other = Vector2D.__normalize__(other)
|
|
244
|
+
return Vector2D(self.x ** other.x, self.y ** other.y)
|
|
245
|
+
|
|
246
|
+
def __truediv__(self, other) -> "Vector2D":
|
|
247
|
+
other = Vector2D.__normalize__(other)
|
|
248
|
+
return Vector2D(self.x / other.x, self.y / other.y)
|
|
249
|
+
|
|
250
|
+
def __floordiv__(self, other) -> "Vector2D":
|
|
251
|
+
other = Vector2D.__normalize__(other)
|
|
252
|
+
return Vector2D(self.x // other.x, self.y // other.y)
|
|
253
|
+
|
|
254
|
+
# right operations a + Vector2D
|
|
255
|
+
def __radd__(self, other) -> "Vector2D":
|
|
256
|
+
return self.__add__(other)
|
|
257
|
+
|
|
258
|
+
def __rsub__(self, other) -> "Vector2D":
|
|
259
|
+
other = Vector2D.__normalize__(other)
|
|
260
|
+
return Vector2D(other.x - self.x, other.y - self.y)
|
|
261
|
+
|
|
262
|
+
def __rmul__(self, other) -> "Vector2D":
|
|
263
|
+
return self.__mul__(other)
|
|
264
|
+
|
|
265
|
+
def __rmod__(self, other) -> "Vector2D":
|
|
266
|
+
other = Vector2D.__normalize__(other)
|
|
267
|
+
return Vector2D(other.x % self.x, other.y % self.y)
|
|
268
|
+
|
|
269
|
+
def __rpow__(self, other) -> "Vector2D":
|
|
270
|
+
other = Vector2D.__normalize__(other)
|
|
271
|
+
return Vector2D(other.x ** self.x, other.y ** self.y)
|
|
272
|
+
|
|
273
|
+
def __rtruediv__(self, other) -> "Vector2D":
|
|
274
|
+
other = Vector2D.__normalize__(other)
|
|
275
|
+
return Vector2D(other.x / self.x, other.y / self.y)
|
|
276
|
+
|
|
277
|
+
def __rfloordiv__(self, other) -> "Vector2D":
|
|
278
|
+
other = Vector2D.__normalize__(other)
|
|
279
|
+
return Vector2D(other.x // self.x, other.y // self.y)
|
|
280
|
+
|
|
281
|
+
# in-place operations Vector2D += a
|
|
282
|
+
def __iadd__(self, other) -> "Vector2D":
|
|
283
|
+
other = Vector2D.__normalize__(other)
|
|
284
|
+
self.x += other.x
|
|
285
|
+
self.y += other.y
|
|
286
|
+
return self
|
|
287
|
+
|
|
288
|
+
def __isub__(self, other) -> "Vector2D":
|
|
289
|
+
other = Vector2D.__normalize__(other)
|
|
290
|
+
self.x -= other.x
|
|
291
|
+
self.y -= other.y
|
|
292
|
+
return self
|
|
293
|
+
|
|
294
|
+
def __imul__(self, other) -> "Vector2D":
|
|
295
|
+
other = Vector2D.__normalize__(other)
|
|
296
|
+
self.x *= other.x
|
|
297
|
+
self.y *= other.y
|
|
298
|
+
return self
|
|
299
|
+
|
|
300
|
+
def __itruediv__(self, other) -> "Vector2D":
|
|
301
|
+
other = Vector2D.__normalize__(other)
|
|
302
|
+
self.x /= other.x
|
|
303
|
+
self.y /= other.y
|
|
304
|
+
return self
|
|
305
|
+
|
|
306
|
+
def __imod__(self, other) -> "Vector2D":
|
|
307
|
+
other = Vector2D.__normalize__(other)
|
|
308
|
+
self.x %= other.x
|
|
309
|
+
self.y %= other.y
|
|
310
|
+
return self
|
|
311
|
+
|
|
312
|
+
def __ipow__(self, other) -> "Vector2D":
|
|
313
|
+
other = Vector2D.__normalize__(other)
|
|
314
|
+
self.x **= other.x
|
|
315
|
+
self.y **= other.y
|
|
316
|
+
return self
|
|
317
|
+
|
|
318
|
+
def __ifloordiv__(self, other) -> "Vector2D":
|
|
319
|
+
other = Vector2D.__normalize__(other)
|
|
320
|
+
self.x //= other.x
|
|
321
|
+
self.y //= other.y
|
|
322
|
+
return self
|
|
323
|
+
|
|
324
|
+
# comparasion
|
|
325
|
+
def __eq__(self, other) -> bool:
|
|
326
|
+
try: other = Vector2D.__normalize__(other)
|
|
327
|
+
except: return False
|
|
328
|
+
return self.x == other.x and self.y == other.y
|
|
329
|
+
|
|
330
|
+
def __ne__(self, other) -> bool:
|
|
331
|
+
return not self.__eq__(other)
|
|
332
|
+
|
|
333
|
+
def __abs__(self) -> "Vector2D":
|
|
334
|
+
return Vector2D(abs(self.x), abs(self.y))
|
|
335
|
+
|
|
336
|
+
def __round__(self, n=1) -> "Vector2D":
|
|
337
|
+
n = Vector2D.__normalize__(n)
|
|
338
|
+
return Vector2D(round(self.x / n.x) * n.x, round(self.y / n.y) * n.y)
|
|
339
|
+
|
|
340
|
+
def __floor__(self, n=1) -> "Vector2D":
|
|
341
|
+
n = Vector2D.__normalize__(n)
|
|
342
|
+
return Vector2D((self.x / n.x).__floor__() * n.x, (self.y / n.y).__floor__() * n.y)
|
|
343
|
+
|
|
344
|
+
def __ceil__(self, n=1) -> "Vector2D":
|
|
345
|
+
n = Vector2D.__normalize__(n)
|
|
346
|
+
return Vector2D((self.x / n.x).__ceil__() * n.x, (self.y / n.y).__ceil__() * n.y)
|
|
347
|
+
|
|
348
|
+
def __float__(self) -> "Vector2D":
|
|
349
|
+
return Vector2D(float(self.x), float(self.y))
|
|
350
|
+
|
|
351
|
+
def __getitem__(self, n) -> int|float:
|
|
352
|
+
if n == 0 or n == "x":
|
|
353
|
+
return self.x
|
|
354
|
+
elif n == 1 or n == "y":
|
|
355
|
+
return self.y
|
|
356
|
+
else:
|
|
357
|
+
raise IndexError("V2 has only x,y...")
|
|
358
|
+
|
|
359
|
+
@classmethod
|
|
360
|
+
def __normalize__(cls, other) -> "Vector2D":
|
|
361
|
+
if isinstance(other, Vector2D):
|
|
362
|
+
return other
|
|
363
|
+
if isinstance(other, (int, float)):
|
|
364
|
+
return cls(other, other)
|
|
365
|
+
if isinstance(other, (list, tuple)):
|
|
366
|
+
return cls(*other[:2])
|
|
367
|
+
try:
|
|
368
|
+
return cls(other.x, other.y)
|
|
369
|
+
except:
|
|
370
|
+
raise TypeError(f"The value {other} of type {type(other)} is not a num type: [{int|float}] nor an array type: [{list|tuple}]")
|
|
371
|
+
|
|
372
|
+
@classmethod
|
|
373
|
+
def zero(cls) -> "Vector2D": return V2zero
|
|
374
|
+
@classmethod
|
|
375
|
+
def one(cls) -> "Vector2D": return V2one
|
|
376
|
+
@classmethod
|
|
377
|
+
def two(cls) -> "Vector2D": return V2two
|
|
378
|
+
@classmethod
|
|
379
|
+
def pi(cls) -> "Vector2D": return V2pi
|
|
380
|
+
@classmethod
|
|
381
|
+
def inf(cls) -> "Vector2D": return V2inf
|
|
382
|
+
@classmethod
|
|
383
|
+
def neg_one(cls) -> "Vector2D": return V2neg_one
|
|
384
|
+
@classmethod
|
|
385
|
+
def neg_two(cls) -> "Vector2D": return V2neg_two
|
|
386
|
+
@classmethod
|
|
387
|
+
def neg_pi(cls) -> "Vector2D": return V2neg_pi
|
|
388
|
+
@classmethod
|
|
389
|
+
def neg_inf(cls) -> "Vector2D": return V2neg_inf
|
|
390
|
+
@classmethod
|
|
391
|
+
def up(cls) -> "Vector2D": return V2up
|
|
392
|
+
@classmethod
|
|
393
|
+
def right(cls) -> "Vector2D": return V2right
|
|
394
|
+
@classmethod
|
|
395
|
+
def down(cls) -> "Vector2D": return V2down
|
|
396
|
+
@classmethod
|
|
397
|
+
def left(cls) -> "Vector2D": return V2left
|
|
398
|
+
@classmethod
|
|
399
|
+
def up_right(cls) -> "Vector2D": return V2up_right
|
|
400
|
+
@classmethod
|
|
401
|
+
def down_right(cls) -> "Vector2D": return V2down_right
|
|
402
|
+
@classmethod
|
|
403
|
+
def up_left(cls) -> "Vector2D": return V2up_left
|
|
404
|
+
@classmethod
|
|
405
|
+
def down_left(cls) -> "Vector2D": return V2down_left
|
|
406
|
+
@classmethod
|
|
407
|
+
def up_right_norm(cls) -> "Vector2D": return V2up_right_norm
|
|
408
|
+
@classmethod
|
|
409
|
+
def down_right_norm(cls) -> "Vector2D": return V2down_right_norm
|
|
410
|
+
@classmethod
|
|
411
|
+
def up_left_norm(cls) -> "Vector2D": return V2up_left_norm
|
|
412
|
+
@classmethod
|
|
413
|
+
def down_left_norm(cls) -> "Vector2D": return V2down_left_norm
|
|
414
|
+
|
|
415
|
+
@classmethod
|
|
416
|
+
def new_zero(cls) -> "Vector2D": return V2zero.copy
|
|
417
|
+
@classmethod
|
|
418
|
+
def new_one(cls) -> "Vector2D": return V2one.copy
|
|
419
|
+
@classmethod
|
|
420
|
+
def new_two(cls) -> "Vector2D": return V2two.copy
|
|
421
|
+
@classmethod
|
|
422
|
+
def new_pi(cls) -> "Vector2D": return V2pi.copy
|
|
423
|
+
@classmethod
|
|
424
|
+
def new_inf(cls) -> "Vector2D": return V2inf.copy
|
|
425
|
+
@classmethod
|
|
426
|
+
def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy
|
|
427
|
+
@classmethod
|
|
428
|
+
def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy
|
|
429
|
+
@classmethod
|
|
430
|
+
def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy
|
|
431
|
+
@classmethod
|
|
432
|
+
def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy
|
|
433
|
+
@classmethod
|
|
434
|
+
def new_up(cls) -> "Vector2D": return V2up.copy
|
|
435
|
+
@classmethod
|
|
436
|
+
def new_right(cls) -> "Vector2D": return V2right.copy
|
|
437
|
+
@classmethod
|
|
438
|
+
def new_down(cls) -> "Vector2D": return V2down.copy
|
|
439
|
+
@classmethod
|
|
440
|
+
def new_left(cls) -> "Vector2D": return V2left.copy
|
|
441
|
+
@classmethod
|
|
442
|
+
def new_up_right(cls) -> "Vector2D": return V2up_right.copy
|
|
443
|
+
@classmethod
|
|
444
|
+
def new_down_right(cls) -> "Vector2D": return V2down_right.copy
|
|
445
|
+
@classmethod
|
|
446
|
+
def new_up_left(cls) -> "Vector2D": return V2up_left.copy
|
|
447
|
+
@classmethod
|
|
448
|
+
def new_down_left(cls) -> "Vector2D": return V2down_left.copy
|
|
449
|
+
@classmethod
|
|
450
|
+
def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy
|
|
451
|
+
@classmethod
|
|
452
|
+
def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy
|
|
453
|
+
@classmethod
|
|
454
|
+
def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy
|
|
455
|
+
@classmethod
|
|
456
|
+
def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy
|
|
457
|
+
|
|
458
|
+
from .cvb import *
|
|
459
|
+
|
|
460
|
+
V2 = Vector2D
|
|
461
|
+
|
|
462
|
+
V2zero = Vector2D(0, 0)
|
|
463
|
+
|
|
464
|
+
V2one = Vector2D(1.0, 1.0)
|
|
465
|
+
V2two = Vector2D(2.0, 2.0)
|
|
466
|
+
V2pi = Vector2D(PI, PI)
|
|
467
|
+
V2inf = Vector2D(float("inf"), float("inf"))
|
|
468
|
+
|
|
469
|
+
V2neg_one = Vector2D(1.0, 1.0)
|
|
470
|
+
V2neg_two = Vector2D(2.0, 2.0)
|
|
471
|
+
V2neg_pi = Vector2D(PI, PI)
|
|
472
|
+
V2neg_inf = Vector2D(float("inf"), float("inf"))
|
|
473
|
+
|
|
474
|
+
V2up = Vector2D(0, 1)
|
|
475
|
+
V2right = Vector2D(1, 0)
|
|
476
|
+
V2down = Vector2D(0, -1)
|
|
477
|
+
V2left = Vector2D(-1, 0)
|
|
478
|
+
|
|
479
|
+
V2up_right = Vector2D(1, 1)
|
|
480
|
+
V2down_right = Vector2D(1, -1)
|
|
481
|
+
V2up_left = Vector2D(-1, 1)
|
|
482
|
+
V2down_left = Vector2D(-1, -1)
|
|
483
|
+
|
|
484
|
+
V2up_right_norm = V2up_right.normalize
|
|
485
|
+
V2down_right_norm = V2down_right.normalize
|
|
486
|
+
V2up_left_norm = V2up_left.normalize
|
|
487
|
+
V2down_left_norm = V2down_left.normalize
|
|
488
|
+
|
|
489
|
+
VECTORS_4_DIRECTIONS = (V2right, V2down, V2left, V2up)
|
|
490
|
+
VECTORS_4_SEMIDIRECTIONS = (V2down_right, V2down_left, V2up_left, V2up_right)
|
|
491
|
+
VECTORS_4_SEMIDIRECTIONS_NORM = (V2down_right_norm, V2down_left_norm, V2up_left_norm, V2up_right_norm)
|
|
492
|
+
VECTORS_8_DIRECTIONS = (V2right, V2down_right, V2down, V2down_left, V2left, V2up_left, V2up, V2up_right)
|
|
493
|
+
VECTORS_8_DIRECTIONS_NORM = (V2right, V2down_right_norm, V2down, V2down_left_norm, V2left, V2up_left_norm, V2up, V2up_right_norm)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def rgb(r:float, g:float, b:float) -> tuple[float, float, float]:
|
|
497
|
+
return (r,g,b)
|
|
498
|
+
|
|
499
|
+
# def color_lerp(current_c:list|tuple, final_c:list|tuple, step=.1) -> tuple[float, float, float]:
|
|
500
|
+
# return tuple(c + (final_c[i] - c) * step for i,c in enumerate(current_c)) #type: ignore
|
|
501
|
+
|
|
502
|
+
# def color_fade(starting_c:list|tuple, final_c:list|tuple, index, max_index) -> tuple[float, float, float]:
|
|
503
|
+
# return tuple((starting_c[i] - final_c[i]) / max_index * (max_index - index) + final_c[i] for i in range(3)) #type: ignore
|
|
504
|
+
|
|
505
|
+
# def weighted_color_fade(colors_dict:dict) -> tuple[float, float, float]:
|
|
506
|
+
# colors = colors_dict.keys()
|
|
507
|
+
# weights = colors_dict.values()
|
|
508
|
+
|
|
509
|
+
# if float("inf") in weights: return list(colors)[list(weights).index(float("inf"))]
|
|
510
|
+
# return tuple(sum(n[i]*w for n,w in zip(colors, weights)) / sum(weights) for i in range(3)) #type: ignore
|
|
511
|
+
|
|
512
|
+
# def color_distance(starting_c:list|tuple, final_c:list|tuple, sqrd) -> float:
|
|
513
|
+
# distance = sum([(starting_c[i]-final_c[i])**2 for i in range(3)])
|
|
514
|
+
# return (distance ** .5) if sqrd else distance
|
|
515
|
+
|
|
516
|
+
def lerp(starting, ending, step=.1) -> float:
|
|
517
|
+
return starting + (ending - starting) * step
|
|
518
|
+
|
|
519
|
+
def angular_interpolation(starting_angle, final_angle, step=.1) -> float:
|
|
520
|
+
# my way
|
|
521
|
+
# delta = final_angle - starting_angle
|
|
522
|
+
# return starting_angle + min((delta, delta - DOUBLE_PI, delta + DOUBLE_PI), key=abs) * step
|
|
523
|
+
|
|
524
|
+
# math way
|
|
525
|
+
shortest_angle = ((((final_angle - starting_angle) % DOUBLE_PI) + DOUBLE_PI * 1.5) % DOUBLE_PI) - PI
|
|
526
|
+
return starting_angle + shortest_angle * step
|
|
527
|
+
|
|
528
|
+
def bezier_cubic_interpolation(t, p0, p1) -> float:
|
|
529
|
+
return t*p0.y*3*(1 - t)**2 + p1.y*3*(1 - t) * t**2 + t**3
|
|
530
|
+
|
|
531
|
+
def bezier_quadratic_interpolation(t, p0) -> float:
|
|
532
|
+
return 2*(1-t)*t*p0.y+t**2
|
|
533
|
+
|
|
534
|
+
def avg_position(*others) -> Vector2D:
|
|
535
|
+
return sum(others) / len(others) #type: ignore
|
|
536
|
+
|
|
537
|
+
def inter_points(ray, lines, return_inter_lines=False, sort=False, return_empty=False) -> list[tuple[Vector2D | None, tuple[Vector2D, Vector2D]]] | list[Vector2D | None] | list[Vector2D]:
|
|
538
|
+
def lineLineIntersect(P0, P1, Q0, Q1) -> "Vector2D | None":
|
|
539
|
+
d = (P1.x-P0.x) * (Q1.y-Q0.y) + (P1.y-P0.y) * (Q0.x-Q1.x)
|
|
540
|
+
if d == 0:
|
|
541
|
+
return None
|
|
542
|
+
t = ((Q0.x-P0.x) * (Q1.y-Q0.y) + (Q0.y-P0.y) * (Q0.x-Q1.x)) / d
|
|
543
|
+
u = ((Q0.x-P0.x) * (P1.y-P0.y) + (Q0.y-P0.y) * (P0.x-P1.x)) / d
|
|
544
|
+
if 0 <= t <= 1 and 0 <= u <= 1:
|
|
545
|
+
return Vector2D(P1.x * t + P0.x * (1-t), P1.y * t + P0.y * (1-t))
|
|
546
|
+
return None
|
|
547
|
+
|
|
548
|
+
if return_inter_lines:
|
|
549
|
+
collisions = [(ip, line) for line in lines if ((ip:=lineLineIntersect(line[1], line[0], ray[1], ray[0]))!=None or return_empty)]
|
|
550
|
+
return sorted(collisions, key=lambda x: ray[0].distance_to(x[0], False) if x[0] != None else _mt.inf) if sort else collisions
|
|
551
|
+
else:
|
|
552
|
+
collisions = [ip for line in lines if ((ip:=lineLineIntersect(line[1], line[0], ray[1], ray[0]))!=None or return_empty)]
|
|
553
|
+
return sorted(collisions, key=lambda x: ray[0].distance_to(x, False) if x != None else _mt.inf) if sort else collisions
|
|
554
|
+
|
|
555
|
+
def get_points(position, size, rotation=0, pos_in_middle=True, return_list=False, clockwise_return=False) -> tuple["Vector2D", "Vector2D", "Vector2D", "Vector2D"] | tuple[list[int|float]|tuple[int|float], list[int|float]|tuple[int|float], list[int|float]|tuple[int|float], list[int|float]|tuple[int|float]]:
|
|
556
|
+
if pos_in_middle:
|
|
557
|
+
d,a = size.length/2, size.angle
|
|
558
|
+
d1, d2 = Vector2D.zero().point_from_angle_and_radius(rotation+a, d), Vector2D.zero().point_from_angle_and_radius(rotation-a, d)
|
|
559
|
+
A, B, C, D = position+d1, position+d2, position-d2, position-d1
|
|
560
|
+
else:
|
|
561
|
+
A, B, C, D = position.copy,\
|
|
562
|
+
position.point_from_angle_and_radius(rotation + Vector2D.zero().angle_to(Vector2D(size.x, 0)), Vector2D.zero().distance_to(Vector2D(size.x, 0))),\
|
|
563
|
+
position.point_from_angle_and_radius(rotation + Vector2D.zero().angle_to(Vector2D(0, size.y)), Vector2D.zero().distance_to(Vector2D(0, size.y))),\
|
|
564
|
+
position.point_from_angle_and_radius(rotation + Vector2D.zero().angle_to(size), Vector2D.zero().distance_to(size))
|
|
565
|
+
points = (A, B, C, D) if not clockwise_return else (A, B, D, C)
|
|
566
|
+
return points if not return_list else tuple(x() for x in points)
|
|
567
|
+
|
|
568
|
+
def get_lines(position, size, rotation=0, pos_in_middle=True) -> list[list]:
|
|
569
|
+
A, B, C, D = get_points(position, size, rotation, pos_in_middle)
|
|
570
|
+
return [[A, B], [A, C], [C, D], [D, B]]
|
|
571
|
+
|
|
572
|
+
def distance_line_point(line_point_a, line_point_b, point_c) -> float:
|
|
573
|
+
# numpy way
|
|
574
|
+
# return float(_np.linalg.norm(_np.cross((line_point_b-line_point_a)(), (line_point_a-point_c)()))/_np.linalg.norm((line_point_b-line_point_a)())) #type: ignore
|
|
575
|
+
|
|
576
|
+
# math way
|
|
577
|
+
return abs((line_point_b.y - line_point_a.y) * point_c.x -\
|
|
578
|
+
(line_point_b.x - line_point_a.x) * point_c.y +\
|
|
579
|
+
line_point_b.x * line_point_a.y - line_point_b.y * line_point_a.x) /\
|
|
580
|
+
((line_point_b.y-line_point_a.y)**2 + (line_point_b.x-line_point_a.x)**2)**.5
|
|
581
|
+
|
|
582
|
+
def optimize_value_string(value, precision) -> str:
|
|
583
|
+
abs_value = abs(value)
|
|
584
|
+
if abs_value < 1/10**precision and abs_value != 0:
|
|
585
|
+
return f"{value:.{precision}e}"
|
|
586
|
+
elif abs_value < 10**precision:
|
|
587
|
+
return f"{value:.{precision}f}".rstrip('0').rstrip('.')
|
|
588
|
+
else:
|
|
589
|
+
return f"{value:.{precision}e}"
|