e2D 1.4.20__py3-none-any.whl → 1.4.23__py3-none-any.whl
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/__init__.py +71 -29
- e2D/__init__.pyi +244 -5
- e2D/colors.py +40 -20
- e2D/envs.py +56 -24
- e2D/utils.py +110 -74
- e2D/winrec.py +197 -12
- {e2d-1.4.20.dist-info → e2d-1.4.23.dist-info}/METADATA +1 -1
- e2d-1.4.23.dist-info/RECORD +13 -0
- {e2d-1.4.20.dist-info → e2d-1.4.23.dist-info}/WHEEL +1 -1
- e2d-1.4.20.dist-info/RECORD +0 -13
- {e2d-1.4.20.dist-info → e2d-1.4.23.dist-info}/licenses/LICENSE +0 -0
- {e2d-1.4.20.dist-info → e2d-1.4.23.dist-info}/top_level.txt +0 -0
e2D/__init__.py
CHANGED
|
@@ -35,8 +35,23 @@ class Vector2D:
|
|
|
35
35
|
@angle.setter
|
|
36
36
|
def angle(self, new_angle) -> None:
|
|
37
37
|
self.rotate(new_angle - self.angle)
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def aspect_x(self) -> float:
|
|
41
|
+
return self.x / self.y if self.y != 0 else 0
|
|
42
|
+
|
|
43
|
+
@aspect_x.setter
|
|
44
|
+
def aspect_x(self, new_aspect) -> None:
|
|
45
|
+
self.x = self.y * new_aspect
|
|
38
46
|
|
|
39
47
|
@property
|
|
48
|
+
def aspect_y(self) -> float:
|
|
49
|
+
return self.y / self.x if self.x != 0 else 0
|
|
50
|
+
|
|
51
|
+
@aspect_y.setter
|
|
52
|
+
def aspect_y(self, new_aspect) -> None:
|
|
53
|
+
self.y = self.x * new_aspect
|
|
54
|
+
|
|
40
55
|
def copy(self) -> "Vector2D":
|
|
41
56
|
return Vector2D(self.x, self.y)
|
|
42
57
|
|
|
@@ -52,19 +67,39 @@ class Vector2D:
|
|
|
52
67
|
self.y = clamp(self.y, min_val.y, max_val.y)
|
|
53
68
|
|
|
54
69
|
@property
|
|
55
|
-
def
|
|
70
|
+
def normalized(self) -> "Vector2D":
|
|
56
71
|
if (mag:=self.length) == 0:
|
|
57
|
-
return self.copy
|
|
72
|
+
return self.copy()
|
|
58
73
|
return Vector2D(self.x / mag, self.y / mag)
|
|
59
74
|
|
|
60
75
|
@property
|
|
61
76
|
def length(self) -> float:
|
|
62
77
|
return (self.x ** 2 + self.y ** 2) ** .5
|
|
63
|
-
|
|
78
|
+
|
|
79
|
+
@length.setter
|
|
80
|
+
def length(self, new_length: float) -> None:
|
|
81
|
+
current_length = self.length
|
|
82
|
+
if current_length == 0:
|
|
83
|
+
self.x = new_length
|
|
84
|
+
self.y = 0
|
|
85
|
+
else:
|
|
86
|
+
self.x *= new_length / current_length
|
|
87
|
+
self.y *= new_length / current_length
|
|
88
|
+
|
|
64
89
|
@property
|
|
65
90
|
def length_sqrd(self) -> float:
|
|
66
91
|
return self.x ** 2 + self.y ** 2
|
|
67
|
-
|
|
92
|
+
|
|
93
|
+
@length_sqrd.setter
|
|
94
|
+
def length_sqrd(self, new_length_sqrd: float) -> None:
|
|
95
|
+
current_length = self.length
|
|
96
|
+
if current_length == 0:
|
|
97
|
+
self.x = _mt.sqrt(new_length_sqrd)
|
|
98
|
+
self.y = 0
|
|
99
|
+
else:
|
|
100
|
+
self.x *= _mt.sqrt(new_length_sqrd) / current_length
|
|
101
|
+
self.y *= _mt.sqrt(new_length_sqrd) / current_length
|
|
102
|
+
|
|
68
103
|
@property
|
|
69
104
|
def inverse(self) -> "Vector2D":
|
|
70
105
|
return self.mult(-1)
|
|
@@ -119,6 +154,13 @@ class Vector2D:
|
|
|
119
154
|
def complex_to_cartesian(cls, complex_n) -> "Vector2D":
|
|
120
155
|
return cls(complex_n.real, complex_n.imag)
|
|
121
156
|
|
|
157
|
+
def cartesian_to_linear(self, size) -> int:
|
|
158
|
+
return int(self.x + self.y * size)
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
def linear_to_cartesian(cls, linear, size) -> "Vector2D":
|
|
162
|
+
return cls(linear % size, linear // size)
|
|
163
|
+
|
|
122
164
|
def lerp(self, other, t=.1) -> "Vector2D":
|
|
123
165
|
return Vector2D(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
|
|
124
166
|
|
|
@@ -428,47 +470,47 @@ class Vector2D:
|
|
|
428
470
|
def down_left_norm(cls) -> "Vector2D": return V2down_left_norm
|
|
429
471
|
|
|
430
472
|
@classmethod
|
|
431
|
-
def new_zero(cls) -> "Vector2D": return V2zero.copy
|
|
473
|
+
def new_zero(cls) -> "Vector2D": return V2zero.copy()
|
|
432
474
|
@classmethod
|
|
433
|
-
def new_one(cls) -> "Vector2D": return V2one.copy
|
|
475
|
+
def new_one(cls) -> "Vector2D": return V2one.copy()
|
|
434
476
|
@classmethod
|
|
435
|
-
def new_two(cls) -> "Vector2D": return V2two.copy
|
|
477
|
+
def new_two(cls) -> "Vector2D": return V2two.copy()
|
|
436
478
|
@classmethod
|
|
437
|
-
def new_pi(cls) -> "Vector2D": return V2pi.copy
|
|
479
|
+
def new_pi(cls) -> "Vector2D": return V2pi.copy()
|
|
438
480
|
@classmethod
|
|
439
|
-
def new_inf(cls) -> "Vector2D": return V2inf.copy
|
|
481
|
+
def new_inf(cls) -> "Vector2D": return V2inf.copy()
|
|
440
482
|
@classmethod
|
|
441
|
-
def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy
|
|
483
|
+
def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy()
|
|
442
484
|
@classmethod
|
|
443
|
-
def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy
|
|
485
|
+
def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy()
|
|
444
486
|
@classmethod
|
|
445
|
-
def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy
|
|
487
|
+
def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy()
|
|
446
488
|
@classmethod
|
|
447
|
-
def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy
|
|
489
|
+
def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy()
|
|
448
490
|
@classmethod
|
|
449
|
-
def new_up(cls) -> "Vector2D": return V2up.copy
|
|
491
|
+
def new_up(cls) -> "Vector2D": return V2up.copy()
|
|
450
492
|
@classmethod
|
|
451
|
-
def new_right(cls) -> "Vector2D": return V2right.copy
|
|
493
|
+
def new_right(cls) -> "Vector2D": return V2right.copy()
|
|
452
494
|
@classmethod
|
|
453
|
-
def new_down(cls) -> "Vector2D": return V2down.copy
|
|
495
|
+
def new_down(cls) -> "Vector2D": return V2down.copy()
|
|
454
496
|
@classmethod
|
|
455
|
-
def new_left(cls) -> "Vector2D": return V2left.copy
|
|
497
|
+
def new_left(cls) -> "Vector2D": return V2left.copy()
|
|
456
498
|
@classmethod
|
|
457
|
-
def new_up_right(cls) -> "Vector2D": return V2up_right.copy
|
|
499
|
+
def new_up_right(cls) -> "Vector2D": return V2up_right.copy()
|
|
458
500
|
@classmethod
|
|
459
|
-
def new_down_right(cls) -> "Vector2D": return V2down_right.copy
|
|
501
|
+
def new_down_right(cls) -> "Vector2D": return V2down_right.copy()
|
|
460
502
|
@classmethod
|
|
461
|
-
def new_up_left(cls) -> "Vector2D": return V2up_left.copy
|
|
503
|
+
def new_up_left(cls) -> "Vector2D": return V2up_left.copy()
|
|
462
504
|
@classmethod
|
|
463
|
-
def new_down_left(cls) -> "Vector2D": return V2down_left.copy
|
|
505
|
+
def new_down_left(cls) -> "Vector2D": return V2down_left.copy()
|
|
464
506
|
@classmethod
|
|
465
|
-
def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy
|
|
507
|
+
def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy()
|
|
466
508
|
@classmethod
|
|
467
|
-
def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy
|
|
509
|
+
def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy()
|
|
468
510
|
@classmethod
|
|
469
|
-
def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy
|
|
511
|
+
def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy()
|
|
470
512
|
@classmethod
|
|
471
|
-
def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy
|
|
513
|
+
def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy()
|
|
472
514
|
|
|
473
515
|
|
|
474
516
|
V2 = Vector2D
|
|
@@ -495,10 +537,10 @@ V2down_right = Vector2D(1, -1)
|
|
|
495
537
|
V2up_left = Vector2D(-1, 1)
|
|
496
538
|
V2down_left = Vector2D(-1, -1)
|
|
497
539
|
|
|
498
|
-
V2up_right_norm = V2up_right.
|
|
499
|
-
V2down_right_norm = V2down_right.
|
|
500
|
-
V2up_left_norm = V2up_left.
|
|
501
|
-
V2down_left_norm = V2down_left.
|
|
540
|
+
V2up_right_norm = V2up_right.normalized
|
|
541
|
+
V2down_right_norm = V2down_right.normalized
|
|
542
|
+
V2up_left_norm = V2up_left.normalized
|
|
543
|
+
V2down_left_norm = V2down_left.normalized
|
|
502
544
|
|
|
503
545
|
VECTORS_4_DIRECTIONS = (V2right, V2down, V2left, V2up)
|
|
504
546
|
VECTORS_4_SEMIDIRECTIONS = (V2down_right, V2down_left, V2up_left, V2up_right)
|
e2D/__init__.pyi
CHANGED
|
@@ -141,12 +141,60 @@ class Vector2D:
|
|
|
141
141
|
...
|
|
142
142
|
|
|
143
143
|
@property
|
|
144
|
-
def angle(self:"Vector2D") -> int|float:
|
|
144
|
+
def angle(self:"Vector2D") -> int|float:
|
|
145
|
+
"""
|
|
146
|
+
# Vector Angle
|
|
147
|
+
## Returns:
|
|
148
|
+
float: The angle (in radians) of the vector from the positive x-axis.
|
|
149
|
+
## Example:
|
|
150
|
+
v = Vector2D(1, 1)
|
|
151
|
+
angle = v.angle
|
|
152
|
+
print(angle) # Output: 0.7853981633974483
|
|
153
|
+
## Explanation:
|
|
154
|
+
This property calculates the angle of the vector from the positive x-axis using the `atan2` function.
|
|
155
|
+
"""
|
|
156
|
+
...
|
|
145
157
|
|
|
146
158
|
@angle.setter
|
|
147
|
-
def angle(self:"Vector2D", argv) -> None:
|
|
159
|
+
def angle(self:"Vector2D", argv) -> None:
|
|
160
|
+
...
|
|
148
161
|
|
|
149
162
|
@property
|
|
163
|
+
def aspect_x(self: "Vector2D") -> float:
|
|
164
|
+
"""
|
|
165
|
+
# Aspect Ratio (X over Y)
|
|
166
|
+
## Returns:
|
|
167
|
+
float: The aspect ratio of the vector, calculated as the x component divided by the y component. Returns 0 if the y component is zero.
|
|
168
|
+
## Example:
|
|
169
|
+
v = Vector2D(4, 2)
|
|
170
|
+
aspect = v.aspect_x
|
|
171
|
+
print(aspect) # Output: 2.0
|
|
172
|
+
## Explanation:
|
|
173
|
+
This property computes the ratio of the x component to the y component of the vector. If the y component is zero, it returns 0 to avoid division by zero.
|
|
174
|
+
"""
|
|
175
|
+
...
|
|
176
|
+
|
|
177
|
+
@aspect_x.setter
|
|
178
|
+
def aspect_x(self: "Vector2D", new_aspect) -> None: ...
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def aspect_y(self: "Vector2D") -> float:
|
|
182
|
+
"""
|
|
183
|
+
# Aspect Ratio (Y over X)
|
|
184
|
+
## Returns:
|
|
185
|
+
float: The aspect ratio of the vector, calculated as the y component divided by the x component. Returns 0 if the x component is zero.
|
|
186
|
+
## Example:
|
|
187
|
+
v = Vector2D(2, 4)
|
|
188
|
+
aspect = v.aspect_y
|
|
189
|
+
print(aspect) # Output: 2.0
|
|
190
|
+
## Explanation:
|
|
191
|
+
This property computes the ratio of the y component to the x component of the vector. If the x component is zero, it returns 0 to avoid division by zero.
|
|
192
|
+
"""
|
|
193
|
+
...
|
|
194
|
+
|
|
195
|
+
@aspect_y.setter
|
|
196
|
+
def aspect_y(self: "Vector2D", new_aspect) -> None: ...
|
|
197
|
+
|
|
150
198
|
def copy(self:"Vector2D") -> "Vector2D":
|
|
151
199
|
"""
|
|
152
200
|
# Create a copy of the current Vector2D other.
|
|
@@ -261,7 +309,7 @@ class Vector2D:
|
|
|
261
309
|
...
|
|
262
310
|
|
|
263
311
|
@property
|
|
264
|
-
def
|
|
312
|
+
def normalized(self:"Vector2D") -> "Vector2D":
|
|
265
313
|
"""
|
|
266
314
|
# Vector Normalization
|
|
267
315
|
|
|
@@ -273,7 +321,7 @@ class Vector2D:
|
|
|
273
321
|
|
|
274
322
|
## Example:
|
|
275
323
|
v = Vector2D(3, 4)
|
|
276
|
-
normalized_v = v.
|
|
324
|
+
normalized_v = v.normalized() # Normalize the vector (3, 4)
|
|
277
325
|
print(normalized_v) # Output: (0.6, 0.8)
|
|
278
326
|
|
|
279
327
|
## Explanation:
|
|
@@ -293,23 +341,123 @@ class Vector2D:
|
|
|
293
341
|
|
|
294
342
|
@property
|
|
295
343
|
def length(self:"Vector2D") -> float:
|
|
344
|
+
"""
|
|
345
|
+
# Vector Length
|
|
346
|
+
|
|
347
|
+
## Returns:
|
|
348
|
+
float: The length (magnitude) of the vector.
|
|
349
|
+
|
|
350
|
+
## Example:
|
|
351
|
+
v = Vector2D(3, 4)
|
|
352
|
+
length = v.length
|
|
353
|
+
print(length) # Output: 5.0
|
|
354
|
+
|
|
355
|
+
## Explanation:
|
|
356
|
+
This property calculates the length (magnitude) of the vector using the Pythagorean theorem.
|
|
357
|
+
"""
|
|
296
358
|
...
|
|
297
359
|
|
|
298
360
|
@property
|
|
299
361
|
def length_sqrd(self:"Vector2D") -> float:
|
|
362
|
+
"""
|
|
363
|
+
# Vector Length Squared
|
|
364
|
+
|
|
365
|
+
## Returns:
|
|
366
|
+
float: The squared length (magnitude) of the vector.
|
|
367
|
+
|
|
368
|
+
## Example:
|
|
369
|
+
v = Vector2D(3, 4)
|
|
370
|
+
length_sqrd = v.length_sqrd
|
|
371
|
+
print(length_sqrd) # Output: 25
|
|
372
|
+
|
|
373
|
+
## Explanation:
|
|
374
|
+
This property calculates the squared length (magnitude) of the vector.
|
|
375
|
+
It is more efficient than calculating the actual length, as it avoids the
|
|
376
|
+
square root calculation.
|
|
377
|
+
"""
|
|
300
378
|
...
|
|
301
379
|
|
|
302
380
|
@property
|
|
303
381
|
def inverse(self:"Vector2D") -> "Vector2D":
|
|
382
|
+
"""
|
|
383
|
+
# Vector Inversion
|
|
384
|
+
|
|
385
|
+
## Returns:
|
|
386
|
+
Vector2D: A new vector with inverted components.
|
|
387
|
+
|
|
388
|
+
## Example:
|
|
389
|
+
v = Vector2D(3, 4)
|
|
390
|
+
inverted_v = v.inverse()
|
|
391
|
+
print(inverted_v) # Output: (-3, -4)
|
|
392
|
+
|
|
393
|
+
## Explanation:
|
|
394
|
+
This method calculates the inverted version of the current vector, which means a new vector with the same magnitude
|
|
395
|
+
but opposite direction.
|
|
396
|
+
|
|
397
|
+
The inverted vector is obtained by negating each component of the current vector.
|
|
398
|
+
|
|
399
|
+
The resulting inverted vector is returned.
|
|
400
|
+
"""
|
|
304
401
|
...
|
|
305
402
|
|
|
306
403
|
def floor(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
404
|
+
"""
|
|
405
|
+
# Round the Vector2D components down to the nearest integer or specified decimal place.
|
|
406
|
+
|
|
407
|
+
## Parameters:
|
|
408
|
+
n (int|float|Vector2D, optional): The decimal place to round down to. Default is 1.
|
|
409
|
+
|
|
410
|
+
## Returns:
|
|
411
|
+
Vector2D: A new vector with rounded-down components.
|
|
412
|
+
|
|
413
|
+
## Example:
|
|
414
|
+
v = Vector2D(3.14159, 2.71828)
|
|
415
|
+
rounded_v = v.floor(2)
|
|
416
|
+
print(rounded_v) # Output: (3.14, 2.71)
|
|
417
|
+
|
|
418
|
+
## Explanation:
|
|
419
|
+
This method rounds each component of the vector down to the nearest integer or specified decimal place.
|
|
420
|
+
"""
|
|
307
421
|
...
|
|
308
422
|
|
|
309
423
|
def ceil(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
424
|
+
"""
|
|
425
|
+
# Round the Vector2D components up to the nearest integer or specified decimal place.
|
|
426
|
+
|
|
427
|
+
## Parameters:
|
|
428
|
+
n (int|float|Vector2D, optional): The decimal place to round up to. Default is 1.
|
|
429
|
+
|
|
430
|
+
## Returns:
|
|
431
|
+
Vector2D: A new vector with rounded-up components.
|
|
432
|
+
|
|
433
|
+
## Example:
|
|
434
|
+
v = Vector2D(3.14159, 2.71828)
|
|
435
|
+
rounded_v = v.ceil(2)
|
|
436
|
+
print(rounded_v) # Output: (3.15, 2.72)
|
|
437
|
+
|
|
438
|
+
## Explanation:
|
|
439
|
+
This method rounds each component of the vector up to the nearest integer or specified decimal place.
|
|
440
|
+
"""
|
|
310
441
|
...
|
|
311
442
|
|
|
312
443
|
def round(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
444
|
+
"""
|
|
445
|
+
# Round the Vector2D components to the nearest integer or specified decimal place.
|
|
446
|
+
|
|
447
|
+
## Parameters:
|
|
448
|
+
n (int|float|Vector2D, optional): The decimal place to round to. Default is 1.
|
|
449
|
+
|
|
450
|
+
## Returns:
|
|
451
|
+
Vector2D: A new vector with rounded components.
|
|
452
|
+
|
|
453
|
+
## Example:
|
|
454
|
+
v = Vector2D(3.14159, 2.71828)
|
|
455
|
+
rounded_v = v.round(2)
|
|
456
|
+
print(rounded_v) # Output: (3.14, 2.72)
|
|
457
|
+
|
|
458
|
+
## Explanation:
|
|
459
|
+
This method rounds each component of the vector to the nearest integer or specified decimal place.
|
|
460
|
+
"""
|
|
313
461
|
...
|
|
314
462
|
|
|
315
463
|
@classmethod
|
|
@@ -500,7 +648,65 @@ class Vector2D:
|
|
|
500
648
|
def cartesian_to_complex(self:"Vector2D") -> complex: ...
|
|
501
649
|
|
|
502
650
|
@classmethod
|
|
503
|
-
def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D":
|
|
651
|
+
def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D":
|
|
652
|
+
"""
|
|
653
|
+
# Convert a Complex Number to Cartesian Coordinates
|
|
654
|
+
|
|
655
|
+
## Parameters:
|
|
656
|
+
complex_n (complex): The complex number to convert.
|
|
657
|
+
|
|
658
|
+
## Returns:
|
|
659
|
+
Vector2D: The corresponding (x, y) coordinates as a Vector2D.
|
|
660
|
+
|
|
661
|
+
## Example:
|
|
662
|
+
v = Vector2D.complex_to_cartesian(3 + 4j) # v = Vector2D(3, 4)
|
|
663
|
+
|
|
664
|
+
## Explanation:
|
|
665
|
+
This class method converts a complex number (a + bi) to Cartesian coordinates (x, y).
|
|
666
|
+
The real part 'a' becomes the x-coordinate, and the imaginary part 'b' becomes the y-coordinate.
|
|
667
|
+
"""
|
|
668
|
+
...
|
|
669
|
+
|
|
670
|
+
def cartesian_to_linear(self:"Vector2D", size:int|float) -> int:
|
|
671
|
+
"""
|
|
672
|
+
# Convert 2D Cartesian coordinates to a linear index.
|
|
673
|
+
|
|
674
|
+
## Parameters:
|
|
675
|
+
size (int|float): The width of the grid (number of columns).
|
|
676
|
+
|
|
677
|
+
## Returns:
|
|
678
|
+
int: The linear index corresponding to the (x, y) coordinates.
|
|
679
|
+
|
|
680
|
+
## Example:
|
|
681
|
+
v = Vector2D(3, 2)
|
|
682
|
+
idx = v.cartesian_to_linear(5) # idx = 3 + 2*5 = 13
|
|
683
|
+
|
|
684
|
+
## Explanation:
|
|
685
|
+
This method converts the (x, y) coordinates of the vector to a single linear index,
|
|
686
|
+
assuming row-major order in a 2D grid of the given width.
|
|
687
|
+
"""
|
|
688
|
+
...
|
|
689
|
+
|
|
690
|
+
@classmethod
|
|
691
|
+
def linear_to_cartesian(cls, linear:int, size:int|float) -> "Vector2D":
|
|
692
|
+
"""
|
|
693
|
+
# Convert a linear index to 2D Cartesian coordinates.
|
|
694
|
+
|
|
695
|
+
## Parameters:
|
|
696
|
+
linear (int): The linear index.
|
|
697
|
+
size (int|float): The width of the grid (number of columns).
|
|
698
|
+
|
|
699
|
+
## Returns:
|
|
700
|
+
Vector2D: The corresponding (x, y) coordinates as a Vector2D.
|
|
701
|
+
|
|
702
|
+
## Example:
|
|
703
|
+
v = Vector2D.linear_to_cartesian(13, 5) # v = Vector2D(3, 2)
|
|
704
|
+
|
|
705
|
+
## Explanation:
|
|
706
|
+
This class method converts a linear index to (x, y) coordinates,
|
|
707
|
+
assuming row-major order in a 2D grid of the given width.
|
|
708
|
+
"""
|
|
709
|
+
...
|
|
504
710
|
|
|
505
711
|
def lerp(self:"Vector2D", other:"int|float|Vector2D|list|tuple", t:float=.1) -> "Vector2D":
|
|
506
712
|
"""
|
|
@@ -1095,4 +1301,37 @@ def distance_line_point(line_point_a:Vector2D, line_point_b:Vector2D, point_c:Ve
|
|
|
1095
1301
|
...
|
|
1096
1302
|
|
|
1097
1303
|
def optimize_value_string(value: int|float, precision: int) -> str:
|
|
1304
|
+
"""
|
|
1305
|
+
# Optimize the string representation of a numeric value based on its magnitude and the specified precision.
|
|
1306
|
+
|
|
1307
|
+
## Parameters:
|
|
1308
|
+
value (int | float): The numeric value to be formatted.
|
|
1309
|
+
precision (int): The number of decimal places or significant digits to use in the formatted string.
|
|
1310
|
+
|
|
1311
|
+
## Returns:
|
|
1312
|
+
str: The optimized string representation of the value.
|
|
1313
|
+
|
|
1314
|
+
## Example:
|
|
1315
|
+
value = 0.000012345
|
|
1316
|
+
precision = 3
|
|
1317
|
+
result = optimize_value_string(value, precision)
|
|
1318
|
+
print(result)
|
|
1319
|
+
# Output: '1.235e-05'
|
|
1320
|
+
|
|
1321
|
+
value = 123.456789
|
|
1322
|
+
precision = 2
|
|
1323
|
+
result = optimize_value_string(value, precision)
|
|
1324
|
+
print(result)
|
|
1325
|
+
# Output: '123.46'
|
|
1326
|
+
|
|
1327
|
+
## Explanation:
|
|
1328
|
+
The function formats the input value as a string, choosing between fixed-point and scientific notation
|
|
1329
|
+
based on the magnitude of the value relative to the specified precision:
|
|
1330
|
+
- If the absolute value is very small (less than 1 divided by 10 to the power of precision, but not zero),
|
|
1331
|
+
scientific notation is used.
|
|
1332
|
+
- If the absolute value is less than 10 to the power of precision, fixed-point notation is used,
|
|
1333
|
+
with trailing zeros and decimal points removed for brevity.
|
|
1334
|
+
- Otherwise, scientific notation is used.
|
|
1335
|
+
This ensures that the string representation is concise and readable, while maintaining the requested precision.
|
|
1336
|
+
"""
|
|
1098
1337
|
...
|
e2D/colors.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from colorsys import hsv_to_rgb as __hsv_to_rgb_def__, hls_to_rgb as __hls_to_rgb_def__, rgb_to_hls as __rgb_to_hls__, rgb_to_hsv as __rgb_to_hsv__
|
|
2
3
|
from typing import Any, Callable, Generator, Literal
|
|
3
4
|
from pygame.color import Color as __color_pygame__
|
|
4
|
-
from random import
|
|
5
|
+
from random import random as __rand__
|
|
5
6
|
|
|
6
7
|
RGB_COLOR_MODE = "rgb"
|
|
7
8
|
RGBA_COLOR_MODE = "rgba"
|
|
@@ -15,11 +16,11 @@ HLS_COLOR_MODE = "hls"
|
|
|
15
16
|
|
|
16
17
|
__LITERAL_COLOR_MODES__ = Literal["rgb","rgba","bgr","bgra","g","hsv","hls"] #,"cmyk","lab"]
|
|
17
18
|
|
|
18
|
-
def __hsv_to_rgb__(h:int|float, s:int|float, v:int|float) -> tuple[int|float, int|float, int|float]:
|
|
19
|
+
def __hsv_to_rgb__(h:"int|float", s:"int|float", v:"int|float") -> tuple["int|float", "int|float", "int|float"]:
|
|
19
20
|
r,g,b = __hsv_to_rgb_def__(h, s, v)
|
|
20
21
|
return r*255, g*255, b*255
|
|
21
22
|
|
|
22
|
-
def __hls_to_rgb__(h:int|float, s:int|float, v:int|float) -> tuple[int|float, int|float, int|float]:
|
|
23
|
+
def __hls_to_rgb__(h:"int|float", s:"int|float", v:"int|float") -> tuple["int|float", "int|float", "int|float"]:
|
|
23
24
|
r,g,b = __hls_to_rgb_def__(h, s, v)
|
|
24
25
|
return r*255, g*255, b*255
|
|
25
26
|
|
|
@@ -110,35 +111,35 @@ class Color:
|
|
|
110
111
|
return (d ** .5) if rooted else d
|
|
111
112
|
|
|
112
113
|
@classmethod
|
|
113
|
-
def new_rgb(cls, r:int|float, g:int|float, b:int|float) -> "Color":
|
|
114
|
+
def new_rgb(cls, r:"int|float", g:"int|float", b:"int|float") -> "Color":
|
|
114
115
|
return Color(r,g,b, mode=RGB_COLOR_MODE)
|
|
115
116
|
@classmethod
|
|
116
|
-
def new_rgba(cls, r:int|float, g:int|float, b:int|float, a:int|float) -> "Color":
|
|
117
|
+
def new_rgba(cls, r:"int|float", g:"int|float", b:"int|float", a:"int|float") -> "Color":
|
|
117
118
|
return Color(r,g,b,a, mode=RGBA_COLOR_MODE)
|
|
118
119
|
@classmethod
|
|
119
|
-
def new_bgr(cls, b:int|float, g:int|float, r:int|float) -> "Color":
|
|
120
|
+
def new_bgr(cls, b:"int|float", g:"int|float", r:"int|float") -> "Color":
|
|
120
121
|
return Color(b,g,r, mode=BGR_COLOR_MODE)
|
|
121
122
|
@classmethod
|
|
122
|
-
def new_bgra(cls, b:int|float, g:int|float, r:int|float, a:int|float) -> "Color":
|
|
123
|
+
def new_bgra(cls, b:"int|float", g:"int|float", r:"int|float", a:"int|float") -> "Color":
|
|
123
124
|
return Color(b,g,r,a, mode=BGRA_COLOR_MODE)
|
|
124
125
|
@classmethod
|
|
125
126
|
def new_g(cls, g) -> "Color":
|
|
126
127
|
return Color(g, mode=GRAY_COLOR_MODE)
|
|
127
128
|
@classmethod
|
|
128
|
-
def new_hsv(cls, h:int|float, s:int|float, v:int|float) -> "Color":
|
|
129
|
+
def new_hsv(cls, h:"int|float", s:"int|float", v:"int|float") -> "Color":
|
|
129
130
|
return Color(h,s,v, mode=HSV_COLOR_MODE)
|
|
130
131
|
@classmethod
|
|
131
|
-
def new_hls(cls, h:int|float, l:int|float, s:int|float) -> "Color":
|
|
132
|
+
def new_hls(cls, h:"int|float", l:"int|float", s:"int|float") -> "Color":
|
|
132
133
|
return Color(h,l,s, mode=HLS_COLOR_MODE)
|
|
133
134
|
# @classmethod
|
|
134
|
-
# def new_cmyk(cls, c:int|float, m:int|float, y:int|float, k:int|float) -> Color:
|
|
135
|
+
# def new_cmyk(cls, c:"int|float", m:"int|float", y:"int|float", k:"int|float") -> Color:
|
|
135
136
|
# return Color(c,m,y,k, mode=CMYK_COLOR_MODE)
|
|
136
137
|
# @classmethod
|
|
137
|
-
# def new_lab(cls, l:int|float, a:int|float, b:int|float) -> Color:
|
|
138
|
+
# def new_lab(cls, l:"int|float", a:"int|float", b:"int|float") -> Color:
|
|
138
139
|
# return Color(l,a,b, mode=LAB_COLOR_MODE)
|
|
139
140
|
|
|
140
141
|
@property
|
|
141
|
-
def values(self) -> tuple[int|float, ...]:
|
|
142
|
+
def values(self) -> tuple["int|float", ...]:
|
|
142
143
|
return tuple(self.__dict__.values())[:-1]
|
|
143
144
|
|
|
144
145
|
@property
|
|
@@ -146,7 +147,7 @@ class Color:
|
|
|
146
147
|
return tuple(self.__dict__.keys())[:-1]
|
|
147
148
|
|
|
148
149
|
@property
|
|
149
|
-
def items(self) -> tuple[tuple[str, int|float], ...]:
|
|
150
|
+
def items(self) -> tuple[tuple[str, "int|float"], ...]:
|
|
150
151
|
return tuple(self.__dict__.items())[:-1]
|
|
151
152
|
|
|
152
153
|
def copy(self) -> "Color":
|
|
@@ -392,7 +393,7 @@ class Color:
|
|
|
392
393
|
def __float__(self) -> "Color":
|
|
393
394
|
return Color(float(self.r), float(self.g), float(self.b))
|
|
394
395
|
|
|
395
|
-
def __getitem__(self, n) -> int|float:
|
|
396
|
+
def __getitem__(self, n) -> "int|float":
|
|
396
397
|
return self.values[n] if isinstance(n, int) else self.values[self.keys.index(n)]
|
|
397
398
|
|
|
398
399
|
def __iter__(self) -> Generator[float, Any, None]:
|
|
@@ -412,7 +413,7 @@ class Color:
|
|
|
412
413
|
try:
|
|
413
414
|
return cls(*other.values, mode=other.mode)
|
|
414
415
|
except:
|
|
415
|
-
raise TypeError(f"The value {other} of type {type(other)} is not a num type: [
|
|
416
|
+
raise TypeError(f"The value {other} of type {type(other)} is not a num type: [int|float] nor an array type: [list|tuple]")
|
|
416
417
|
|
|
417
418
|
@classmethod
|
|
418
419
|
def transparent(cls) -> "Color": return Color(0,0,0,0, mode=RGBA_COLOR_MODE)
|
|
@@ -426,13 +427,26 @@ class Color:
|
|
|
426
427
|
def green(cls) -> "Color": return Color(0,255,0)
|
|
427
428
|
@classmethod
|
|
428
429
|
def blue(cls) -> "Color": return Color(0,0,255)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
430
|
+
@classmethod
|
|
431
|
+
def cyan(cls) -> "Color": return Color(0,255,255)
|
|
432
|
+
@classmethod
|
|
433
|
+
def magenta(cls) -> "Color": return Color(255,0,255)
|
|
434
|
+
@classmethod
|
|
435
|
+
def yellow(cls) -> "Color": return Color(255,255,0)
|
|
432
436
|
|
|
433
437
|
@classmethod
|
|
434
|
-
def randomize(cls) -> "Color":
|
|
435
|
-
|
|
438
|
+
def randomize(cls, start=0, end=255, func=lambda val:val) -> "Color":
|
|
439
|
+
if not isinstance(start, Color):
|
|
440
|
+
if isinstance(start, (int, float)):
|
|
441
|
+
start = Color(start, start, start)
|
|
442
|
+
else:
|
|
443
|
+
raise Exception(f"\nArg start must be in [Color, int, float, tuple, list] not a [{type(start)}]\n")
|
|
444
|
+
if not isinstance(end, Color):
|
|
445
|
+
if isinstance(end, (int, float)):
|
|
446
|
+
end = Color(end, end, end)
|
|
447
|
+
else:
|
|
448
|
+
raise Exception(f"\nArg end must be in [Color, int, float, tuple, list] not a [{type(end)}]\n")
|
|
449
|
+
return start + Color(func(__rand__()), func(__rand__()), func(__rand__())) * (end - start)
|
|
436
450
|
|
|
437
451
|
|
|
438
452
|
TRANSPARENT_COLOR = Color.transparent()
|
|
@@ -441,6 +455,9 @@ BLACK_COLOR = Color.black()
|
|
|
441
455
|
RED_COLOR = Color.red()
|
|
442
456
|
GREEN_COLOR = Color.green()
|
|
443
457
|
BLUE_COLOR = Color.blue()
|
|
458
|
+
CYAN_COLOR = Color.cyan()
|
|
459
|
+
MAGENTA_COLOR = Color.magenta()
|
|
460
|
+
YELLOW_COLOR = Color.yellow()
|
|
444
461
|
|
|
445
462
|
TRANSPARENT_COLOR_PYG = TRANSPARENT_COLOR()
|
|
446
463
|
WHITE_COLOR_PYG = WHITE_COLOR()
|
|
@@ -448,3 +465,6 @@ BLACK_COLOR_PYG = BLACK_COLOR()
|
|
|
448
465
|
RED_COLOR_PYG = RED_COLOR()
|
|
449
466
|
GREEN_COLOR_PYG = GREEN_COLOR()
|
|
450
467
|
BLUE_COLOR_PYG = BLUE_COLOR()
|
|
468
|
+
CYAN_COLOR_PYG = CYAN_COLOR()
|
|
469
|
+
MAGENTA_COLOR_PYG = MAGENTA_COLOR()
|
|
470
|
+
YELLOW_COLOR_PYG = YELLOW_COLOR()
|