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