e2D 1.4.8__py3-none-any.whl → 1.4.10__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 +597 -79
- e2D/cvb.py +831 -0
- e2D/envs.py +124 -12
- e2D/plots.py +41 -24
- e2D/utils.py +6 -5
- {e2D-1.4.8.dist-info → e2D-1.4.10.dist-info}/METADATA +1 -1
- e2D-1.4.10.dist-info/RECORD +11 -0
- {e2D-1.4.8.dist-info → e2D-1.4.10.dist-info}/WHEEL +1 -1
- e2D-1.4.8.dist-info/RECORD +0 -10
- {e2D-1.4.8.dist-info → e2D-1.4.10.dist-info}/LICENSE +0 -0
- {e2D-1.4.8.dist-info → e2D-1.4.10.dist-info}/top_level.txt +0 -0
e2D/__init__.py
CHANGED
|
@@ -16,49 +16,310 @@ DOUBLE_PI = PI*2
|
|
|
16
16
|
|
|
17
17
|
class Vector2D:
|
|
18
18
|
round_values_on_print :int|float= 2
|
|
19
|
-
def __init__(self:"
|
|
19
|
+
def __init__(self:"Vector2D", x:int|float=0.0, y:int|float=0.0) -> None:
|
|
20
|
+
"""
|
|
21
|
+
# Initialize a 2D vector with the specified x and y components.
|
|
22
|
+
|
|
23
|
+
## Parameters:
|
|
24
|
+
x (int | float, optional): The x-component of the vector. Default is 0.
|
|
25
|
+
y (int | float, optional): The y-component of the vector. Default is 0.
|
|
26
|
+
|
|
27
|
+
## Example:
|
|
28
|
+
vector1 = Vector2D() # Creates a vector with x=0 and y=0
|
|
29
|
+
vector2 = Vector2D(3, -2.5) # Creates a vector with x=3 and y=-2.5
|
|
30
|
+
|
|
31
|
+
## Explanation:
|
|
32
|
+
This constructor initializes a 2D vector with the specified x and y components.
|
|
33
|
+
|
|
34
|
+
If no arguments are provided, the default values for x and y are both set to 0.
|
|
35
|
+
|
|
36
|
+
The x and y components can be integers or floating-point numbers.
|
|
37
|
+
|
|
38
|
+
Example usage is shown in the "Example" section above.
|
|
39
|
+
"""
|
|
20
40
|
self.x = x
|
|
21
41
|
self.y = y
|
|
22
42
|
|
|
23
|
-
def set(self:"
|
|
43
|
+
def set(self:"Vector2D", x:int|float=0, y:int|float=0) -> None:
|
|
44
|
+
"""
|
|
45
|
+
# Change the components of the Vector2D other without creating a new one.
|
|
46
|
+
|
|
47
|
+
## Parameters:
|
|
48
|
+
x (int | float, optional): The new x-component to set. Default is 0.
|
|
49
|
+
y (int | float, optional): The new y-component to set. Default is 0.
|
|
50
|
+
|
|
51
|
+
## Example:
|
|
52
|
+
vector = Vector2D(1, 2)
|
|
53
|
+
vector.set(3, -4)
|
|
54
|
+
print(vector.x, vector.y) # Output: 3, -4
|
|
55
|
+
|
|
56
|
+
## Explanation:
|
|
57
|
+
The method updates the x and y components of the Vector2D other to the specified values.
|
|
58
|
+
|
|
59
|
+
If no arguments are provided, the default values for x and y are both set to 0.
|
|
60
|
+
|
|
61
|
+
The x and y components can be integers or floating-point numbers.
|
|
62
|
+
|
|
63
|
+
The method does not return any value, but it modifies the Vector2D other in place.
|
|
64
|
+
|
|
65
|
+
Example usage is shown in the "Example" section above.
|
|
66
|
+
"""
|
|
24
67
|
self.x = x
|
|
25
68
|
self.y = y
|
|
26
69
|
|
|
27
|
-
def distance_to(self:"
|
|
70
|
+
def distance_to(self:"Vector2D", other:"float|int|Vector2D|list|tuple", sqrd:bool=True) -> int|float:
|
|
71
|
+
"""
|
|
72
|
+
# Calculate the distance between the current Vector2D other and another other.
|
|
73
|
+
|
|
74
|
+
## Parameters:
|
|
75
|
+
other (float or int or Vector2D or list|tuple): The other other to which the distance is calculated.
|
|
76
|
+
squared (bool, optional): If True, return the squared distance. If False, return the actual distance.
|
|
77
|
+
Default is True.
|
|
78
|
+
|
|
79
|
+
## Returns:
|
|
80
|
+
int|float: The squared distance between the current Vector2D other and the other other if `squared` is True,
|
|
81
|
+
otherwise the actual distance.
|
|
82
|
+
|
|
83
|
+
## Example:
|
|
84
|
+
point1 = Vector2D(0, 0)
|
|
85
|
+
|
|
86
|
+
point2 = Vector2D(3, 4)
|
|
87
|
+
|
|
88
|
+
squared_distance = point1.distance_to(point2)
|
|
89
|
+
|
|
90
|
+
print(f"Squared Distance: {squared_distance}")
|
|
91
|
+
|
|
92
|
+
distance = point1.distance_to(point2, squared=False)
|
|
93
|
+
|
|
94
|
+
print(f"Actual Distance: {distance}")
|
|
95
|
+
|
|
96
|
+
This will calculate the squared and actual distances between the two points.
|
|
97
|
+
|
|
98
|
+
## Explanation:
|
|
99
|
+
The function calculates the squared distance between the current Vector2D other (self) and another other
|
|
100
|
+
(other) using the formula: (self.x - other.x)**2 + (self.y - other.y)**2.
|
|
101
|
+
|
|
102
|
+
The result is returned as the squared distance if `squared` is True, or as the actual distance if `squared` is False.
|
|
103
|
+
"""
|
|
28
104
|
other = self.__normalize__(other)
|
|
29
105
|
d = (self.x - other.x)**2 + (self.y - other.y)**2
|
|
30
106
|
return (d**(1/2) if sqrd else d)
|
|
31
107
|
|
|
32
|
-
def
|
|
108
|
+
def angle(self:"Vector2D") -> int|float:
|
|
109
|
+
return _mt.atan2(self.y, self.x)
|
|
110
|
+
|
|
111
|
+
def angle_to(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> int|float:
|
|
112
|
+
"""
|
|
113
|
+
# Calculate the angle between the current Vector2D other and another other.
|
|
114
|
+
|
|
115
|
+
## Parameters:
|
|
116
|
+
other (float or int or Vector2D or list|tuple): The other other to which the angle is calculated.
|
|
117
|
+
|
|
118
|
+
## Returns:
|
|
119
|
+
int|float: The angle in radians between the current Vector2D other and the other other.
|
|
120
|
+
|
|
121
|
+
## Example:
|
|
122
|
+
point1 = Vector2D(0, 0)
|
|
123
|
+
|
|
124
|
+
point2 = Vector2D(1, 1)
|
|
125
|
+
|
|
126
|
+
angle = point1.angle_to(point2)
|
|
127
|
+
|
|
128
|
+
print(f"Angle in radians: {angle}")
|
|
129
|
+
|
|
130
|
+
This will calculate the angle in radians between the two points.
|
|
131
|
+
|
|
132
|
+
## Explanation:
|
|
133
|
+
The function calculates the angle in radians between the current Vector2D other (self) and another other
|
|
134
|
+
(other) using the `atan2` function from the `math` module.
|
|
135
|
+
|
|
136
|
+
The result is returned as the angle in radians.
|
|
137
|
+
"""
|
|
33
138
|
other = self.__normalize__(other)
|
|
34
139
|
return _mt.atan2(other.y - self.y, other.x - self.x)
|
|
35
140
|
|
|
36
|
-
def point_from_degs(self:"
|
|
141
|
+
def point_from_degs(self:"Vector2D", degs:int|float, radius:int|float) -> "Vector2D":
|
|
142
|
+
"""
|
|
143
|
+
# Calculate a new Vector2D point from the current point based on an angle in degs and a radius.
|
|
144
|
+
|
|
145
|
+
## Parameters:
|
|
146
|
+
rad (int|float): The angle in degs.
|
|
147
|
+
radius (int|float): The distance from the current point.
|
|
148
|
+
|
|
149
|
+
## Returns:
|
|
150
|
+
Vector2D: A new Vector2D point calculated from the current point.
|
|
151
|
+
|
|
152
|
+
## Example:
|
|
153
|
+
point1 = Vector2D(0, 0)
|
|
154
|
+
|
|
155
|
+
angle = 45
|
|
156
|
+
|
|
157
|
+
distance = 5
|
|
158
|
+
|
|
159
|
+
new_point = point1.point_from_degs(angle, distance)
|
|
160
|
+
|
|
161
|
+
print(new_point.x, new_point.y)
|
|
162
|
+
|
|
163
|
+
This will calculate a new point 5 units away from point1 at a 45-degree angle.
|
|
164
|
+
|
|
165
|
+
## Explanation:
|
|
166
|
+
The function calculates a new Vector2D point based on an angle in degs (degs) and a distance (radius)
|
|
167
|
+
from the current Vector2D point.
|
|
168
|
+
|
|
169
|
+
It computes the new x and y coordinates of the point using the trigonometric functions `cos` and `sin`
|
|
170
|
+
to determine the horizontal and vertical components of the new point.
|
|
171
|
+
|
|
172
|
+
The result is returned as a new Vector2D point with the calculated coordinates.
|
|
173
|
+
"""
|
|
37
174
|
x = radius * _mt.cos(_mt.radians(degs)) + self.x
|
|
38
175
|
y = radius * _mt.sin(_mt.radians(degs)) + self.y
|
|
39
176
|
return Vector2D(x, y)
|
|
40
177
|
|
|
41
|
-
def point_from_rads(self:"
|
|
178
|
+
def point_from_rads(self:"Vector2D", rad:int|float, radius:int|float) -> "Vector2D":
|
|
179
|
+
"""
|
|
180
|
+
# Calculate a new Vector2D point from the current point based on an angle in radians and a radius.
|
|
181
|
+
|
|
182
|
+
## Parameters:
|
|
183
|
+
rad (int|float): The angle in radians.
|
|
184
|
+
radius (int|float): The distance from the current point.
|
|
185
|
+
|
|
186
|
+
## Returns:
|
|
187
|
+
Vector2D: A new Vector2D point calculated from the current point.
|
|
188
|
+
|
|
189
|
+
## Example:
|
|
190
|
+
point1 = Vector2D(0, 0)
|
|
191
|
+
|
|
192
|
+
angle = 45
|
|
193
|
+
|
|
194
|
+
distance = 5
|
|
195
|
+
|
|
196
|
+
new_point = point1.point_from_degs(_mt.radians(angle), distance)
|
|
197
|
+
|
|
198
|
+
print(new_point.x, new_point.y)
|
|
199
|
+
|
|
200
|
+
This will calculate a new point 5 units away from point1 at a 45-degree angle.
|
|
201
|
+
|
|
202
|
+
## Explanation:
|
|
203
|
+
The function calculates a new Vector2D point based on an angle in radians (rad) and a distance (radius)
|
|
204
|
+
from the current Vector2D point.
|
|
205
|
+
|
|
206
|
+
It computes the new x and y coordinates of the point using the trigonometric functions `cos` and `sin`
|
|
207
|
+
to determine the horizontal and vertical components of the new point.
|
|
208
|
+
|
|
209
|
+
The result is returned as a new Vector2D point with the calculated coordinates.
|
|
210
|
+
"""
|
|
42
211
|
x = radius * _mt.cos(rad) + self.x
|
|
43
212
|
y = radius * _mt.sin(rad) + self.y
|
|
44
213
|
return Vector2D(x, y)
|
|
45
214
|
|
|
46
|
-
def copy(self:"
|
|
215
|
+
def copy(self:"Vector2D") -> "Vector2D":
|
|
216
|
+
"""
|
|
217
|
+
# Create a copy of the current Vector2D other.
|
|
218
|
+
|
|
219
|
+
## Returns:
|
|
220
|
+
Vector2D: A new Vector2D other with the same x and y coordinates as the current other.
|
|
221
|
+
|
|
222
|
+
## Example:
|
|
223
|
+
point1 = Vector2D(1, 2)
|
|
224
|
+
|
|
225
|
+
point2 = point1.copy()
|
|
226
|
+
|
|
227
|
+
print(point2.x, point2.y)
|
|
228
|
+
|
|
229
|
+
This will print the x and y coordinates of the copied Vector2D other (1, 2).
|
|
230
|
+
|
|
231
|
+
## Explanation:
|
|
232
|
+
The function creates a new Vector2D other with the same x and y coordinates as the current other.
|
|
233
|
+
|
|
234
|
+
The result is returned as a new Vector2D other, effectively making a copy of the original other.
|
|
235
|
+
"""
|
|
47
236
|
return Vector2D(self.x, self.y)
|
|
48
237
|
|
|
49
|
-
def sign(self:"
|
|
238
|
+
def sign(self:"Vector2D") -> "Vector2D":
|
|
239
|
+
"""
|
|
240
|
+
# Perform an "absolute round" operation on the Vector2D other.
|
|
241
|
+
|
|
242
|
+
## Parameters:
|
|
243
|
+
n (int|float, optional): The numeric value to scale the "absolute rounded" vector. Default is 1.
|
|
244
|
+
|
|
245
|
+
## Returns:
|
|
246
|
+
Vector2D: The "absolute rounded" Vector2D other scaled by the provided numeric value.
|
|
247
|
+
|
|
248
|
+
## Example:
|
|
249
|
+
vector1 = Vector2D(3.3, -4.7)
|
|
250
|
+
|
|
251
|
+
result1 = vector1.absolute_round(0.5)
|
|
252
|
+
|
|
253
|
+
print(result1.x, result1.y)
|
|
254
|
+
|
|
255
|
+
vector2 = Vector2D(-2.8, 1.1)
|
|
256
|
+
|
|
257
|
+
result2 = vector2.absolute_round()
|
|
258
|
+
|
|
259
|
+
print(result2.x, result2.y)
|
|
260
|
+
|
|
261
|
+
## Explanation:
|
|
262
|
+
The function performs an "absolute round" operation on the Vector2D other.
|
|
263
|
+
|
|
264
|
+
The "absolute round" operation involves taking the absolute values of both the x and y components of the Vector2D other,
|
|
265
|
+
and then scaling the resulting vector by the provided numeric value (n).
|
|
266
|
+
|
|
267
|
+
The default value of n is 1, which means the "absolute rounded" vector will have the same magnitude as the original vector.
|
|
268
|
+
|
|
269
|
+
If the provided numeric value (n) is 0, the function returns a Vector2D other with zeros for both components.
|
|
270
|
+
|
|
271
|
+
If the provided numeric value (n) is negative, the resulting "absolute rounded" vector will point in the opposite direction
|
|
272
|
+
as the original vector but will have the same magnitude.
|
|
273
|
+
|
|
274
|
+
Note: The "absolute round" operation does not perform standard mathematical rounding; instead, it ensures the resulting
|
|
275
|
+
vector points in the same direction as the original vector but has non-negative components.
|
|
276
|
+
"""
|
|
50
277
|
return self.no_zero_div_error(abs(self), "zero")
|
|
51
278
|
|
|
52
|
-
def floor(self:"
|
|
279
|
+
def floor(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
53
280
|
return self.__floor__(n)
|
|
54
281
|
|
|
55
|
-
def ceil(self:"
|
|
282
|
+
def ceil(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
56
283
|
return self.__ceil__(n)
|
|
57
284
|
|
|
58
|
-
def round(self:"
|
|
285
|
+
def round(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
59
286
|
return self.__round__(n)
|
|
60
287
|
|
|
61
|
-
def randomize(start:"int|float|Vector2D|
|
|
288
|
+
def randomize(start:"int|float|Vector2D|None"=None, end:"int|float|Vector2D|None"=None) -> "Vector2D": #type: ignore
|
|
289
|
+
"""
|
|
290
|
+
# Generate a random Vector2D point within the specified range.
|
|
291
|
+
|
|
292
|
+
## Parameters:
|
|
293
|
+
start (int|float or Vector2D or None, optional): The starting point of the range.
|
|
294
|
+
Default is None, which corresponds to (0, 0).
|
|
295
|
+
If numeric, both x and y will have the same value.
|
|
296
|
+
end (int|float or Vector2D or None, optional): The ending point of the range.
|
|
297
|
+
Default is None, which corresponds to (1, 1).
|
|
298
|
+
If numeric, both x and y will have the same value.
|
|
299
|
+
|
|
300
|
+
## Returns:
|
|
301
|
+
Vector2D: A new random Vector2D point within the specified range.
|
|
302
|
+
|
|
303
|
+
## Example:
|
|
304
|
+
random_point = randomize(Vector2D(10, 20), Vector2D(50, 70))
|
|
305
|
+
|
|
306
|
+
print(random_point.x, random_point.y)
|
|
307
|
+
|
|
308
|
+
This will print a random point between (10, 20) and (50, 70).
|
|
309
|
+
|
|
310
|
+
## Explanation:
|
|
311
|
+
The function generates a random Vector2D point within the specified range defined by `start` and `end`.
|
|
312
|
+
|
|
313
|
+
If `start` and `end` are numeric values (int or float), both x and y coordinates will have the same value.
|
|
314
|
+
|
|
315
|
+
If `start` and `end` are None, the default range is assumed to be (0, 0) to (1, 1).
|
|
316
|
+
|
|
317
|
+
The function first checks if `start` and `end` are Vector2D others. If not, it creates new Vector2D others
|
|
318
|
+
based on the numeric values provided or the default values.
|
|
319
|
+
|
|
320
|
+
It then generates random x and y coordinates in the range [0, 1) using the `random()` function from the `random` module.
|
|
321
|
+
These random values are then scaled by (end - start) and added to the start point to obtain the final random Vector2D point.
|
|
322
|
+
"""
|
|
62
323
|
if not any(isinstance(start, cls) for cls in {Vector2D, V2}):
|
|
63
324
|
if type(start) in int|float: start = Vector2D(start, start) #type: ignore
|
|
64
325
|
elif type(start) == None: start = Vector2D(0,0)
|
|
@@ -69,17 +330,102 @@ class Vector2D:
|
|
|
69
330
|
else: raise Exception(f"\nArg end must be in [Vector2D, int, float, tuple, list] not a [{type(end)}]\n")
|
|
70
331
|
return start + Vector2D(_rnd.random(), _rnd.random()) * (end - start) #type: ignore
|
|
71
332
|
|
|
72
|
-
def dot_product(self, other:"float|int|Vector2D|
|
|
333
|
+
def dot_product(self, other:"float|int|Vector2D|list|tuple") -> float:
|
|
73
334
|
other = self.__normalize__(other)
|
|
335
|
+
"""
|
|
336
|
+
# Calculate the dot product of the current vector with another vector.
|
|
337
|
+
|
|
338
|
+
## Parameters:
|
|
339
|
+
other (Vector2D): The other vector for the dot product calculation.
|
|
340
|
+
|
|
341
|
+
## Returns:
|
|
342
|
+
float: The dot product value.
|
|
343
|
+
|
|
344
|
+
## Example:
|
|
345
|
+
v1 = Vector2D(2, 3)
|
|
346
|
+
v2 = Vector2D(4, -1)
|
|
347
|
+
result = v1.dot_product(v2)
|
|
348
|
+
print(result) # Output: 5
|
|
349
|
+
|
|
350
|
+
## Explanation:
|
|
351
|
+
The dot product of two vectors (A and B) is given by the formula: dot_product = A.x * B.x + A.y * B.y
|
|
352
|
+
|
|
353
|
+
The method takes another vector (other) as input and returns the dot product value.
|
|
354
|
+
|
|
355
|
+
Example usage is shown in the "Example" section above.
|
|
356
|
+
"""
|
|
74
357
|
return self.x * other.x + self.y * other.y
|
|
75
358
|
|
|
76
359
|
def normalize(self) -> "Vector2D":
|
|
360
|
+
"""
|
|
361
|
+
# Vector Normalization
|
|
362
|
+
|
|
363
|
+
## Returns:
|
|
364
|
+
Vector2D: A new vector with the same direction as the current vector but with a magnitude of 1.
|
|
365
|
+
|
|
366
|
+
## Raises:
|
|
367
|
+
ValueError: If the magnitude of the current vector is zero (zero vector).
|
|
368
|
+
|
|
369
|
+
## Example:
|
|
370
|
+
v = Vector2D(3, 4)
|
|
371
|
+
normalized_v = v.normalize() # Normalize the vector (3, 4)
|
|
372
|
+
print(normalized_v) # Output: (0.6, 0.8)
|
|
373
|
+
|
|
374
|
+
## Explanation:
|
|
375
|
+
This method calculates the normalized version of the current vector, which means a new vector with the same direction as the original but with a magnitude of 1.
|
|
376
|
+
|
|
377
|
+
The method first calculates the magnitude of the current vector using the 'magnitude' method.
|
|
378
|
+
|
|
379
|
+
If the magnitude is zero (zero vector), a ValueError is raised, as normalization is not defined for zero vectors.
|
|
380
|
+
|
|
381
|
+
The normalized vector is obtained by dividing each component of the current vector by its magnitude.
|
|
382
|
+
|
|
383
|
+
The resulting normalized vector is returned.
|
|
384
|
+
|
|
385
|
+
Example usage is shown in the "Example" section above.
|
|
386
|
+
"""
|
|
77
387
|
mag = self.length()
|
|
78
388
|
if mag == 0:
|
|
79
389
|
return self
|
|
80
390
|
return Vector2D(self.x / mag, self.y / mag)
|
|
81
391
|
|
|
82
|
-
def projection(self, other:"float|int|Vector2D|
|
|
392
|
+
def projection(self, other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
393
|
+
"""
|
|
394
|
+
# Vector Projection
|
|
395
|
+
|
|
396
|
+
## Parameters:
|
|
397
|
+
other (float, int, Vector2D, V2, list, tuple): The vector onto which to project.
|
|
398
|
+
|
|
399
|
+
## Returns:
|
|
400
|
+
Vector2D or V2: The projection of the current vector onto the 'other' vector.
|
|
401
|
+
|
|
402
|
+
## Raises:
|
|
403
|
+
ValueError: If 'other' is a zero vector.
|
|
404
|
+
|
|
405
|
+
## Example:
|
|
406
|
+
v1 = Vector2D(3, 4)
|
|
407
|
+
v2 = Vector2D(1, 0)
|
|
408
|
+
projection_v = v1.projection(v2) # Calculate the projection of v1 onto v2
|
|
409
|
+
print(projection_v) # Output: (3.0, 0.0)
|
|
410
|
+
|
|
411
|
+
## Explanation:
|
|
412
|
+
This method calculates the projection of the current vector onto the 'other' vector.
|
|
413
|
+
The projection is a vector that represents the component of the current vector in the direction of the 'other' vector.
|
|
414
|
+
|
|
415
|
+
If 'other' is not a Vector2D instance, it will be converted to one using the '__normalize__' method.
|
|
416
|
+
The method first normalizes the 'other' vector using the '__normalize__' method of the vector.
|
|
417
|
+
|
|
418
|
+
Next, it calculates the dot product of the current vector and the normalized 'other' vector using the 'dot_product' method.
|
|
419
|
+
It also calculates the squared magnitude of the 'other' vector using the 'magnitude' method.
|
|
420
|
+
|
|
421
|
+
If the magnitude of 'other' is zero (a zero vector), a ValueError is raised, as projection is not defined for zero vectors.
|
|
422
|
+
|
|
423
|
+
The projection is then obtained by scaling the 'other' vector by the dot product divided by the squared magnitude.
|
|
424
|
+
|
|
425
|
+
The resulting projection vector is returned.
|
|
426
|
+
|
|
427
|
+
Example usage is shown in the "Example" section above.
|
|
428
|
+
"""
|
|
83
429
|
other = self.__normalize__(other)
|
|
84
430
|
dot_product = self.dot_product(other)
|
|
85
431
|
magnitude_product = other.length() ** 2
|
|
@@ -87,18 +433,91 @@ class Vector2D:
|
|
|
87
433
|
raise ValueError("Cannot calculate projection for zero vectors.")
|
|
88
434
|
return other * (dot_product / magnitude_product)
|
|
89
435
|
|
|
90
|
-
def reflection(self, normal:"float|int|Vector2D|
|
|
436
|
+
def reflection(self, normal:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
437
|
+
"""
|
|
438
|
+
# Vector Reflection
|
|
439
|
+
|
|
440
|
+
## Parameters:
|
|
441
|
+
normal (float, int, Vector2D, V2, list, tuple): The normal vector representing the surface of reflection.
|
|
442
|
+
|
|
443
|
+
## Returns:
|
|
444
|
+
Vector2D or V2: The reflected vector.
|
|
445
|
+
|
|
446
|
+
## Example:
|
|
447
|
+
incident_vector = Vector2D(3, 4)
|
|
448
|
+
normal_vector = Vector2D(1, 0)
|
|
449
|
+
reflected_vector = incident_vector.reflection(normal_vector) # Calculate the reflection of the incident vector over the given normal
|
|
450
|
+
print(reflected_vector) # Output: (-3.0, 4.0)
|
|
451
|
+
|
|
452
|
+
## Explanation:
|
|
453
|
+
This method calculates the reflection of the current vector over the given normal vector.
|
|
454
|
+
The normal vector represents the surface of reflection, and it should be normalized (unit vector).
|
|
455
|
+
|
|
456
|
+
The method first normalizes the 'normal' vector using the '__normalize__' method of the vector.
|
|
457
|
+
Next, it calculates the projection of the current vector onto the 'normal' vector using the 'projection' method.
|
|
458
|
+
The reflected vector is obtained by subtracting twice the projection from the current vector.
|
|
459
|
+
|
|
460
|
+
The resulting reflected vector is returned.
|
|
461
|
+
|
|
462
|
+
Example usage is shown in the "Example" section above.
|
|
463
|
+
"""
|
|
91
464
|
normal = self.__normalize__(normal)
|
|
92
465
|
projection = self.projection(normal)
|
|
93
466
|
return self - projection * 2
|
|
94
467
|
|
|
95
468
|
def cartesian_to_polar(self) -> tuple:
|
|
469
|
+
"""
|
|
470
|
+
# Convert Cartesian Coordinates to Polar Coordinates
|
|
471
|
+
|
|
472
|
+
## Returns:
|
|
473
|
+
tuple: A tuple containing the radial distance (magnitude) 'r' and the angle 'theta' in radians.
|
|
474
|
+
|
|
475
|
+
## Example:
|
|
476
|
+
v = Vector2D(3, 4)
|
|
477
|
+
r, theta = v.cartesian_to_polar() # Convert Cartesian coordinates (3, 4) to polar
|
|
478
|
+
print(r, theta) # Output: (5.0, 0.9272952180016122)
|
|
479
|
+
|
|
480
|
+
## Explanation:
|
|
481
|
+
This method converts Cartesian coordinates (x, y) to polar coordinates (r, theta).
|
|
482
|
+
'r' is the radial distance (magnitude) from the origin to the point, and 'theta' is the angle
|
|
483
|
+
(in radians) measured from the positive x-axis to the point.
|
|
484
|
+
|
|
485
|
+
The method calculates the radial distance 'r' using the 'magnitude' method of the vector.
|
|
486
|
+
The angle 'theta' is calculated using the arctan2 function, which takes the y and x components of the vector.
|
|
487
|
+
|
|
488
|
+
The resulting 'r' and 'theta' are returned as a tuple.
|
|
489
|
+
|
|
490
|
+
Example usage is shown in the "Example" section above.
|
|
491
|
+
"""
|
|
96
492
|
r = self.length()
|
|
97
493
|
theta = _mt.atan2(self.y, self.x)
|
|
98
494
|
return r, theta
|
|
99
495
|
|
|
100
496
|
@classmethod
|
|
101
|
-
def polar_to_cartesian(cls, r: float|int, theta: float|int) -> "Vector2D
|
|
497
|
+
def polar_to_cartesian(cls, r: float|int, theta: float|int) -> "Vector2D":
|
|
498
|
+
"""
|
|
499
|
+
# Convert Polar Coordinates to Cartesian Coordinates
|
|
500
|
+
|
|
501
|
+
## Parameters:
|
|
502
|
+
r (float or int): The radial distance (magnitude) from the origin to the point.
|
|
503
|
+
theta (float or int): The angle (in radians or degrees) measured from the positive x-axis to the point.
|
|
504
|
+
|
|
505
|
+
## Returns:
|
|
506
|
+
Vector2D or V2: A new vector representing the Cartesian coordinates (x, y) of the point.
|
|
507
|
+
|
|
508
|
+
## Example:
|
|
509
|
+
cartesian_point = Vector2D.polar_to_cartesian(5, math.pi/4) # Convert polar coordinates (r=5, theta=45 degrees) to Cartesian
|
|
510
|
+
print(cartesian_point) # Output: (3.5355339059327378, 3.5355339059327373)
|
|
511
|
+
|
|
512
|
+
## Explanation:
|
|
513
|
+
This class method converts polar coordinates (r, theta) to Cartesian coordinates (x, y).
|
|
514
|
+
'r' is the radial distance (magnitude) from the origin to the point, and 'theta' is the angle
|
|
515
|
+
(in radians or degrees) measured from the positive x-axis to the point.
|
|
516
|
+
|
|
517
|
+
The method calculates the x and y components using trigonometric functions (cosine and sine) based on 'r' and 'theta'.
|
|
518
|
+
|
|
519
|
+
Example usage is shown in the "Example" section above.
|
|
520
|
+
"""
|
|
102
521
|
x = r * _mt.cos(theta)
|
|
103
522
|
y = r * _mt.sin(theta)
|
|
104
523
|
return cls(x, y)
|
|
@@ -107,19 +526,81 @@ class Vector2D:
|
|
|
107
526
|
return self.x + self.y * 1j
|
|
108
527
|
|
|
109
528
|
@classmethod
|
|
110
|
-
def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D
|
|
529
|
+
def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D":
|
|
111
530
|
return cls(complex_n.real, complex_n.imag)
|
|
112
531
|
|
|
113
532
|
def length(self) -> float:
|
|
114
533
|
return (self.x ** 2 + self.y ** 2) ** .5
|
|
115
534
|
|
|
116
|
-
def lerp(self, other:"float|int|Vector2D|
|
|
535
|
+
def lerp(self, other:"float|int|Vector2D|list|tuple", t: float) -> "Vector2D":
|
|
536
|
+
"""
|
|
537
|
+
# Linear Interpolation (LERP)
|
|
538
|
+
|
|
539
|
+
## Parameters:
|
|
540
|
+
other (float, int, Vector2D, V2, list, tuple): The vector to interpolate towards.
|
|
541
|
+
t (float): The interpolation parameter. Must be between 0 and 1.
|
|
542
|
+
|
|
543
|
+
## Returns:
|
|
544
|
+
Vector2D or V2: The result of the linear interpolation.
|
|
545
|
+
|
|
546
|
+
## Raises:
|
|
547
|
+
ValueError: If t is not within the range [0, 1].
|
|
548
|
+
|
|
549
|
+
## Example:
|
|
550
|
+
v1 = Vector2D(1, 2)
|
|
551
|
+
v2 = Vector2D(5, 7)
|
|
552
|
+
interpolated_v = v1.lerp(v2, 0.5) # Linearly interpolate between v1 and v2 with t = 0.5
|
|
553
|
+
print(interpolated_v) # Output: (3.0, 4.5)
|
|
554
|
+
|
|
555
|
+
## Explanation:
|
|
556
|
+
This method performs linear interpolation between the current vector and the 'other' vector.
|
|
557
|
+
The 't' parameter represents the interpolation parameter, which controls how much the interpolation
|
|
558
|
+
leans towards the 'other' vector. When 't' is 0, the result will be equal to the current vector (self).
|
|
559
|
+
When 't' is 1, the result will be equal to the 'other' vector. For intermediate values of 't', the
|
|
560
|
+
result will be a linear combination of the two vectors, smoothly transitioning between them.
|
|
561
|
+
|
|
562
|
+
If 'other' is not a Vector2D instance, it will be converted to one using the '__normalize__' method.
|
|
563
|
+
If 't' is not within the range [0, 1], a ValueError is raised.
|
|
564
|
+
|
|
565
|
+
Example usage is shown in the "Example" section above.
|
|
566
|
+
"""
|
|
117
567
|
other = self.__normalize__(other)
|
|
118
568
|
if not 0 <= t <= 1:
|
|
119
569
|
raise ValueError("t must be between 0 and 1 for linear interpolation.")
|
|
120
570
|
return Vector2D(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
|
|
121
571
|
|
|
122
|
-
def rotate(self, angle: int|float, center:"float|int|Vector2D|
|
|
572
|
+
def rotate(self, angle: int|float, center:"float|int|Vector2D|list|tuple|None"=None) -> "Vector2D":
|
|
573
|
+
"""
|
|
574
|
+
# Rotate the vector by a given angle around the origin or a specified center.
|
|
575
|
+
|
|
576
|
+
## Parameters:
|
|
577
|
+
angle (int or float): The angle of rotation in radians or degrees, depending on the trigonometric functions used.
|
|
578
|
+
center (float, int, Vector2D, V2, list, tuple, or None): The center of rotation.
|
|
579
|
+
If None, the vector is rotated around the origin (0, 0).
|
|
580
|
+
|
|
581
|
+
## Returns:
|
|
582
|
+
Vector2D or V2: The rotated vector.
|
|
583
|
+
|
|
584
|
+
## Example:
|
|
585
|
+
v = Vector2D(3, 4)
|
|
586
|
+
rotated_v = v.rotate(math.pi / 4) # Rotate 45 degrees around the origin
|
|
587
|
+
print(rotated_v) # Output: (0.7071067811865476, 5.656854249492381)
|
|
588
|
+
|
|
589
|
+
center = Vector2D(1, 1)
|
|
590
|
+
rotated_v = v.rotate(math.pi / 4, center) # Rotate 45 degrees around the center (1, 1)
|
|
591
|
+
print(rotated_v) # Output: (1.7071067811865475, 2.656854249492381)
|
|
592
|
+
|
|
593
|
+
## Explanation:
|
|
594
|
+
This method rotates the vector by the specified angle around the given center.
|
|
595
|
+
If no center is provided, the vector is rotated around the origin (0, 0).
|
|
596
|
+
|
|
597
|
+
The method calculates the trigonometric functions (cosine and sine) of the angle to perform the rotation.
|
|
598
|
+
The translated vector is obtained by subtracting the center from the current vector.
|
|
599
|
+
The rotated vector is then obtained by applying the rotation transformation to the translated vector.
|
|
600
|
+
The center is added back to the rotated vector to obtain the final result.
|
|
601
|
+
|
|
602
|
+
Example usage is shown in the "Example" section above.
|
|
603
|
+
"""
|
|
123
604
|
if center is None: center = V2z
|
|
124
605
|
else: center = self.__normalize__(center)
|
|
125
606
|
translated = self - center
|
|
@@ -127,7 +608,49 @@ class Vector2D:
|
|
|
127
608
|
sin_angle = _mt.sin(angle)
|
|
128
609
|
return Vector2D(translated.x * cos_angle - translated.y * sin_angle, translated.x * sin_angle + translated.y * cos_angle) + center
|
|
129
610
|
|
|
130
|
-
def no_zero_div_error(self:"Vector2D
|
|
611
|
+
def no_zero_div_error(self:"Vector2D", n:"int|float|Vector2D", error_mode:str="zero") -> "Vector2D":
|
|
612
|
+
"""
|
|
613
|
+
# Handle division between the Vector2D other and a numeric value or another Vector2D other.
|
|
614
|
+
|
|
615
|
+
## Parameters:
|
|
616
|
+
n (int|float or Vector2D): The numeric value or Vector2D other for division.
|
|
617
|
+
error_mode (str, optional): The mode to handle division by zero scenarios.
|
|
618
|
+
- "zero" (default): Return a Vector2D other with zeros for both components.
|
|
619
|
+
- "null": Return a Vector2D other with the original x or y component if available,
|
|
620
|
+
otherwise, return NaN (Not a Number) for the component.
|
|
621
|
+
|
|
622
|
+
## Returns:
|
|
623
|
+
Vector2D: A new Vector2D other after division or handling division by zero scenarios.
|
|
624
|
+
|
|
625
|
+
## Example:
|
|
626
|
+
vector1 = Vector2D(3, 4)
|
|
627
|
+
|
|
628
|
+
result1 = vector1.no_zero_div_error(2)
|
|
629
|
+
|
|
630
|
+
print(result1.x, result1.y)
|
|
631
|
+
|
|
632
|
+
vector2 = Vector2D(5, 0)
|
|
633
|
+
|
|
634
|
+
result2 = vector1.no_zero_div_error(vector2, error_mode="null")
|
|
635
|
+
|
|
636
|
+
print(result2.x, result2.y)
|
|
637
|
+
|
|
638
|
+
## Explanation:
|
|
639
|
+
The function handles division between the Vector2D other and a numeric value or another Vector2D other.
|
|
640
|
+
|
|
641
|
+
If n is a numeric value (int or float):
|
|
642
|
+
- If n is zero, the function returns a Vector2D other with zeros for both components if error_mode is "zero".
|
|
643
|
+
- If error_mode is "null", the function returns a Vector2D other with the original x or y component if available,
|
|
644
|
+
otherwise, return NaN (Not a Number) for the component.
|
|
645
|
+
|
|
646
|
+
If n is a Vector2D other:
|
|
647
|
+
- If n's x or y component is zero, the function returns a Vector2D other with zeros for the corresponding component
|
|
648
|
+
if error_mode is "zero".
|
|
649
|
+
- If error_mode is "null", the function returns a Vector2D other with the original x or y component if available,
|
|
650
|
+
otherwise, return NaN (Not a Number) for the component.
|
|
651
|
+
|
|
652
|
+
If n is neither a numeric value nor a Vector2D other, the function raises an exception.
|
|
653
|
+
"""
|
|
131
654
|
if any(isinstance(n, cls) for cls in {int, float}):
|
|
132
655
|
if n == 0:
|
|
133
656
|
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))
|
|
@@ -138,15 +661,15 @@ class Vector2D:
|
|
|
138
661
|
else:
|
|
139
662
|
raise Exception(f"\nArg n must be in [Vector2D, int, float, tuple, list] not a [{type(n)}]\n")
|
|
140
663
|
|
|
141
|
-
def min(self:"
|
|
664
|
+
def min(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
142
665
|
other = self.__normalize__(other)
|
|
143
666
|
return Vector2D(min(self.x, other.x), min(self.y, other.y))
|
|
144
667
|
|
|
145
|
-
def max(self:"
|
|
668
|
+
def max(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
146
669
|
other = self.__normalize__(other)
|
|
147
670
|
return Vector2D(max(self.x, other.x), max(self.y, other.y))
|
|
148
671
|
|
|
149
|
-
def advanced_stringify(self:"
|
|
672
|
+
def advanced_stringify(self:"Vector2D", precision:float|None=None, use_scientific_notation:bool=False, return_as_list=False) -> str:
|
|
150
673
|
precision = self.round_values_on_print if precision == None else precision
|
|
151
674
|
def optimize(value) -> str:
|
|
152
675
|
abs_value = abs(value)
|
|
@@ -157,112 +680,112 @@ class Vector2D:
|
|
|
157
680
|
else:
|
|
158
681
|
return f"{value:.{precision}e}"
|
|
159
682
|
if return_as_list:
|
|
160
|
-
return [optimize(self.x), optimize(self.y)] if use_scientific_notation else [f"{self.x:.{precision}f}", f"{self.y:.{precision}f}"]
|
|
683
|
+
return [optimize(self.x), optimize(self.y)] if use_scientific_notation else [f"{self.x:.{precision}f}", f"{self.y:.{precision}f}"] #type: ignore
|
|
161
684
|
return f"{optimize(self.x)}, {optimize(self.y)}" if use_scientific_notation else f"{self.x:.{precision}f}, {self.y:.{precision}f}"
|
|
162
685
|
|
|
163
|
-
def __str__(self:"
|
|
686
|
+
def __str__(self:"Vector2D") -> str:
|
|
164
687
|
return f"{self.x:.{self.round_values_on_print}f}, {self.y:.{self.round_values_on_print}f}"
|
|
165
688
|
|
|
166
|
-
def __repr__(self:"
|
|
689
|
+
def __repr__(self:"Vector2D") -> str:
|
|
167
690
|
return f"x:{self.x:.{self.round_values_on_print}f}\ty:{self.y:.{self.round_values_on_print}f}"
|
|
168
691
|
|
|
169
|
-
def __call__(self:"
|
|
692
|
+
def __call__(self:"Vector2D", return_tuple=False) -> list|tuple:
|
|
170
693
|
return (self.x, self.y) if return_tuple else [self.x, self.y]
|
|
171
694
|
|
|
172
695
|
# normal operations Vector2D + a
|
|
173
|
-
def __add__(self:"
|
|
696
|
+
def __add__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
174
697
|
other = self.__normalize__(other)
|
|
175
698
|
return Vector2D(self.x + other.x, self.y + other.y)
|
|
176
699
|
|
|
177
|
-
def __sub__(self:"
|
|
700
|
+
def __sub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
178
701
|
other = self.__normalize__(other)
|
|
179
702
|
return Vector2D(self.x - other.x, self.y - other.y)
|
|
180
703
|
|
|
181
|
-
def __mul__(self:"
|
|
704
|
+
def __mul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
182
705
|
other = self.__normalize__(other)
|
|
183
706
|
return Vector2D(self.x * other.x, self.y * other.y)
|
|
184
707
|
|
|
185
|
-
def __mod__(self:"
|
|
708
|
+
def __mod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
186
709
|
other = self.__normalize__(other)
|
|
187
710
|
return Vector2D(self.x % other.x, self.y % other.y)
|
|
188
711
|
|
|
189
|
-
def __pow__(self:"
|
|
712
|
+
def __pow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
190
713
|
other = self.__normalize__(other)
|
|
191
714
|
return Vector2D(self.x ** other.x, self.y ** other.y)
|
|
192
715
|
|
|
193
|
-
def __truediv__(self:"
|
|
716
|
+
def __truediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
194
717
|
other = self.__normalize__(other)
|
|
195
718
|
return Vector2D(self.x / other.x, self.y / other.y)
|
|
196
719
|
|
|
197
|
-
def __floordiv__(self:"
|
|
720
|
+
def __floordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
198
721
|
other = self.__normalize__(other)
|
|
199
722
|
return Vector2D(self.x // other.x, self.y // other.y)
|
|
200
723
|
|
|
201
724
|
# right operations a + Vector2D
|
|
202
|
-
def __radd__(self:"
|
|
725
|
+
def __radd__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
203
726
|
return self.__add__(other)
|
|
204
727
|
|
|
205
|
-
def __rsub__(self:"
|
|
728
|
+
def __rsub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
206
729
|
other = self.__normalize__(other)
|
|
207
730
|
return Vector2D(other.x - self.x, other.y - self.y)
|
|
208
731
|
|
|
209
|
-
def __rmul__(self:"
|
|
732
|
+
def __rmul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
210
733
|
return self.__mul__(other)
|
|
211
734
|
|
|
212
|
-
def __rmod__(self:"
|
|
735
|
+
def __rmod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
213
736
|
other = self.__normalize__(other)
|
|
214
737
|
return Vector2D(other.x % self.x, other.y % self.y)
|
|
215
738
|
|
|
216
|
-
def __rpow__(self:"
|
|
739
|
+
def __rpow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
217
740
|
other = self.__normalize__(other)
|
|
218
741
|
return Vector2D(other.x ** self.x, other.y ** self.y)
|
|
219
742
|
|
|
220
|
-
def __rtruediv__(self:"
|
|
743
|
+
def __rtruediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
221
744
|
other = self.__normalize__(other)
|
|
222
745
|
return Vector2D(other.x / self.x, other.y / self.y)
|
|
223
746
|
|
|
224
|
-
def __rfloordiv__(self:"
|
|
747
|
+
def __rfloordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
225
748
|
other = self.__normalize__(other)
|
|
226
749
|
return Vector2D(other.x // self.x, other.y // self.y)
|
|
227
750
|
|
|
228
751
|
# in-place operations Vector2D += a
|
|
229
|
-
def __iadd__(self:"
|
|
752
|
+
def __iadd__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
230
753
|
other = self.__normalize__(other)
|
|
231
754
|
self.x += other.x
|
|
232
755
|
self.y += other.y
|
|
233
756
|
return self
|
|
234
757
|
|
|
235
|
-
def __isub__(self:"
|
|
758
|
+
def __isub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
236
759
|
other = self.__normalize__(other)
|
|
237
760
|
self.x -= other.x
|
|
238
761
|
self.y -= other.y
|
|
239
762
|
return self
|
|
240
763
|
|
|
241
|
-
def __imul__(self:"
|
|
764
|
+
def __imul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
242
765
|
other = self.__normalize__(other)
|
|
243
766
|
self.x *= other.x
|
|
244
767
|
self.y *= other.y
|
|
245
768
|
return self
|
|
246
769
|
|
|
247
|
-
def __itruediv__(self:"
|
|
770
|
+
def __itruediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
248
771
|
other = self.__normalize__(other)
|
|
249
772
|
self.x /= other.x
|
|
250
773
|
self.y /= other.y
|
|
251
774
|
return self
|
|
252
775
|
|
|
253
|
-
def __imod__(self:"
|
|
776
|
+
def __imod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
254
777
|
other = self.__normalize__(other)
|
|
255
778
|
self.x %= other.x
|
|
256
779
|
self.y %= other.y
|
|
257
780
|
return self
|
|
258
781
|
|
|
259
|
-
def __ipow__(self:"
|
|
782
|
+
def __ipow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
260
783
|
other = self.__normalize__(other)
|
|
261
784
|
self.x **= other.x
|
|
262
785
|
self.y **= other.y
|
|
263
786
|
return self
|
|
264
787
|
|
|
265
|
-
def __ifloordiv__(self:"
|
|
788
|
+
def __ifloordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
|
|
266
789
|
other = self.__normalize__(other)
|
|
267
790
|
self.x //= other.x
|
|
268
791
|
self.y //= other.y
|
|
@@ -277,25 +800,25 @@ class Vector2D:
|
|
|
277
800
|
def __ne__(self, other) -> bool:
|
|
278
801
|
return not self.__eq__(other)
|
|
279
802
|
|
|
280
|
-
def __abs__(self:"
|
|
803
|
+
def __abs__(self:"Vector2D") -> "Vector2D":
|
|
281
804
|
return Vector2D(abs(self.x), abs(self.y))
|
|
282
805
|
|
|
283
|
-
def __round__(self:"
|
|
806
|
+
def __round__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
284
807
|
n = self.__normalize__(n)
|
|
285
808
|
return Vector2D(round(self.x / n.x) * n.x, round(self.y / n.y) * n.y)
|
|
286
809
|
|
|
287
|
-
def __floor__(self:"
|
|
810
|
+
def __floor__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
288
811
|
n = self.__normalize__(n)
|
|
289
812
|
return Vector2D(_mt.floor(self.x / n.x) * n.x, _mt.floor(self.y / n.y) * n.y)
|
|
290
813
|
|
|
291
|
-
def __ceil__(self:"
|
|
814
|
+
def __ceil__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
|
|
292
815
|
n = self.__normalize__(n)
|
|
293
816
|
return Vector2D(_mt.ceil(self.x / n.x) * n.x, _mt.ceil(self.y / n.y) * n.y)
|
|
294
817
|
|
|
295
|
-
def __float__(self:"
|
|
818
|
+
def __float__(self:"Vector2D") -> "Vector2D":
|
|
296
819
|
return Vector2D(float(self.x), float(self.y))
|
|
297
820
|
|
|
298
|
-
def __getitem__(self:"
|
|
821
|
+
def __getitem__(self:"Vector2D", n) -> int|float:
|
|
299
822
|
if n in [0, "x"]:
|
|
300
823
|
return self.x
|
|
301
824
|
elif n in [1, "y"]:
|
|
@@ -303,27 +826,22 @@ class Vector2D:
|
|
|
303
826
|
else:
|
|
304
827
|
raise IndexError("V2 has only x,y...")
|
|
305
828
|
|
|
306
|
-
def __normalize__(self:"
|
|
829
|
+
def __normalize__(self:"Vector2D", other) -> "Vector2D":
|
|
307
830
|
if not isinstance(other, Vector2D):
|
|
308
831
|
if any(isinstance(other, cls) for cls in {int, float}):
|
|
309
832
|
return Vector2D(other, other)
|
|
310
833
|
elif any(isinstance(other, cls) for cls in {list, tuple}):
|
|
311
834
|
return Vector2D(*other[:2])
|
|
312
835
|
else:
|
|
313
|
-
|
|
836
|
+
try:
|
|
837
|
+
return Vector2D(other.x, other.y)
|
|
838
|
+
except:
|
|
839
|
+
raise TypeError(f"pThe value {other} of type {type(other)} is not a num type: [{int|float}] nor an array type: [{list|tuple}]")
|
|
314
840
|
return other
|
|
315
841
|
|
|
316
|
-
|
|
317
|
-
# try:
|
|
318
|
-
# from .Cmain import * #type: ignore
|
|
319
|
-
# __cvector2d__ = Vector2D
|
|
320
|
-
# except Exception as err:
|
|
321
|
-
# print(Warning(f"Unable to load the C-version on Vector2D: \n\t{err}"))
|
|
322
|
-
# __cvector2d__ = None
|
|
842
|
+
from .cvb import *
|
|
323
843
|
|
|
324
|
-
|
|
325
|
-
def __init__(self:"V2|Vector2D", x: int|float = 0, y: int|float = 0) -> None:
|
|
326
|
-
super().__init__(x, y)
|
|
844
|
+
V2 = Vector2D
|
|
327
845
|
|
|
328
846
|
V2inf = Vector2D(float('inf'), float('inf'))
|
|
329
847
|
V2z = VectorZero = Vector2D()
|
|
@@ -507,13 +1025,13 @@ def angular_interpolation(starting_angle:int|float, final_angle:int|float, step:
|
|
|
507
1025
|
distances = (final_angle - starting_angle, final_angle - DOUBLE_PI - starting_angle, final_angle + DOUBLE_PI - starting_angle)
|
|
508
1026
|
return min(distances, key=abs) * step
|
|
509
1027
|
|
|
510
|
-
def bezier_cubic_interpolation(t:float, p0:Vector2D
|
|
1028
|
+
def bezier_cubic_interpolation(t:float, p0:Vector2D, p1:Vector2D) -> float:
|
|
511
1029
|
return t*p0.y*3*(1 - t)**2 + p1.y*3*(1 - t) * t**2 + t**3
|
|
512
1030
|
|
|
513
|
-
def bezier_quadratic_interpolation(t:float, p0:Vector2D
|
|
1031
|
+
def bezier_quadratic_interpolation(t:float, p0:Vector2D) -> float:
|
|
514
1032
|
return 2*(1-t)*t*p0.y+t**2
|
|
515
1033
|
|
|
516
|
-
def avg_position(*others:"Vector2D
|
|
1034
|
+
def avg_position(*others:"Vector2D") -> Vector2D:
|
|
517
1035
|
"""
|
|
518
1036
|
# Calculate the average position for a variable number of Vector2D others.
|
|
519
1037
|
|
|
@@ -545,14 +1063,14 @@ def avg_position(*others:"Vector2D|V2") -> Vector2D|V2:
|
|
|
545
1063
|
"""
|
|
546
1064
|
return sum(others) / len(others) #type: ignore
|
|
547
1065
|
|
|
548
|
-
def inter_points(ray:list["Vector2D
|
|
1066
|
+
def inter_points(ray:list["Vector2D"]|tuple["Vector2D", "Vector2D"], lines:list[tuple["Vector2D", "Vector2D"]], return_inter_lines:bool=False, sort:bool=False, return_empty:bool=False) -> list[tuple[Vector2D | None, tuple[Vector2D | V2, Vector2D | V2]]] | list[Vector2D | None] | list[Vector2D]:
|
|
549
1067
|
"""
|
|
550
1068
|
# Find intersection points between a ray or line segment and multiple line segments.
|
|
551
1069
|
|
|
552
1070
|
## Parameters:
|
|
553
|
-
ray (list[Vector2D
|
|
1071
|
+
ray (list[Vector2D] | tuple[Vector2D, Vector2D]): The ray or line segment represented by two endpoints
|
|
554
1072
|
(start and end points).
|
|
555
|
-
lines (list[tuple[Vector2D
|
|
1073
|
+
lines (list[tuple[Vector2D, Vector2D]]): A list of line segments represented by tuples of their endpoints.
|
|
556
1074
|
return_inter_lines (bool, optional): If True, return a list of tuples containing the intersection points and the
|
|
557
1075
|
corresponding intersecting line segment. Default is False.
|
|
558
1076
|
sort (bool, optional): If True, sort the intersection points by their distance from the ray's start point.
|
|
@@ -588,7 +1106,7 @@ def inter_points(ray:list["Vector2D|V2"]|tuple["Vector2D|V2", "Vector2D|V2"], li
|
|
|
588
1106
|
|
|
589
1107
|
Example usage is shown in the "Example" section above.
|
|
590
1108
|
"""
|
|
591
|
-
def lineLineIntersect(P0:"
|
|
1109
|
+
def lineLineIntersect(P0:"Vector2D", P1:"Vector2D", Q0:"Vector2D", Q1:"Vector2D") -> "Vector2D | None":
|
|
592
1110
|
d = (P1.x-P0.x) * (Q1.y-Q0.y) + (P1.y-P0.y) * (Q0.x-Q1.x)
|
|
593
1111
|
if d == 0:
|
|
594
1112
|
return None
|
|
@@ -599,13 +1117,13 @@ def inter_points(ray:list["Vector2D|V2"]|tuple["Vector2D|V2", "Vector2D|V2"], li
|
|
|
599
1117
|
return None
|
|
600
1118
|
|
|
601
1119
|
if return_inter_lines:
|
|
602
|
-
collisions = [(lineLineIntersect(line[1], line[0], ray[1], ray[0])
|
|
1120
|
+
collisions = [(ip, line) for line in lines if ((ip:=lineLineIntersect(line[1], line[0], ray[1], ray[0]))!=None or return_empty)]
|
|
603
1121
|
return sorted(collisions, key=lambda x: ray[0].distance_to(x[0], False) if x[0] != None else _mt.inf) if sort else collisions
|
|
604
1122
|
else:
|
|
605
|
-
collisions = [lineLineIntersect(line[1], line[0], ray[1], ray[0])
|
|
1123
|
+
collisions = [ip for line in lines if ((ip:=lineLineIntersect(line[1], line[0], ray[1], ray[0]))!=None or return_empty)]
|
|
606
1124
|
return sorted(collisions, key=lambda x: ray[0].distance_to(x, False) if x != None else _mt.inf) if sort else collisions
|
|
607
1125
|
|
|
608
|
-
def get_points(position:Vector2D
|
|
1126
|
+
def get_points(position:Vector2D, size:Vector2D, rotation:int|float=0, pos_in_middle:bool=True, return_list:bool=False, clockwise_return:bool=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]]:
|
|
609
1127
|
"""
|
|
610
1128
|
# Generate points for a rectangle based on the given parameters.
|
|
611
1129
|
|
|
@@ -662,7 +1180,7 @@ def get_points(position:Vector2D|V2, size:Vector2D|V2, rotation:int|float=0, pos
|
|
|
662
1180
|
points = (A, B, C, D) if not clockwise_return else (A, B, D, C)
|
|
663
1181
|
return points if not return_list else tuple(x() for x in points)
|
|
664
1182
|
|
|
665
|
-
def get_lines(position:Vector2D
|
|
1183
|
+
def get_lines(position:Vector2D, size:Vector2D, rotation:int|float=0, pos_in_middle:bool=True) -> list[list]:
|
|
666
1184
|
"""
|
|
667
1185
|
# Generate lines representing the sides of a rectangle based on the given parameters.
|
|
668
1186
|
|
|
@@ -701,7 +1219,7 @@ def get_lines(position:Vector2D|V2, size:Vector2D|V2, rotation:int|float=0, pos_
|
|
|
701
1219
|
A, B, C, D = get_points(position, size, rotation, pos_in_middle)
|
|
702
1220
|
return [[A, B], [A, C], [C, D], [D, B]]
|
|
703
1221
|
|
|
704
|
-
def distance_line_point(line_point_a:Vector2D
|
|
1222
|
+
def distance_line_point(line_point_a:Vector2D, line_point_b:Vector2D, point_c:Vector2D) -> float:
|
|
705
1223
|
"""
|
|
706
1224
|
# Calculate the distance between a line segment and a point.
|
|
707
1225
|
|
|
@@ -735,7 +1253,7 @@ def distance_line_point(line_point_a:Vector2D|V2, line_point_b:Vector2D|V2, poin
|
|
|
735
1253
|
|
|
736
1254
|
The result is returned as a float representing the distance between the line segment and the point.
|
|
737
1255
|
"""
|
|
738
|
-
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)()))
|
|
1256
|
+
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
|
|
739
1257
|
|
|
740
1258
|
def optimize_value_string(value:int|float, precision:int) -> str:
|
|
741
1259
|
abs_value = abs(value)
|