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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: e2D
3
- Version: 1.4.11
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 [ricomari2006@gmail.com](mailto:ricomari2006@gmail.com).
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 [ricomari2006@gmail.com](mailto:ricomari2006@gmail.com).
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}"