e2D 1.4.11__py3-none-any.whl → 1.4.13__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 CHANGED
@@ -1,523 +1,105 @@
1
1
  from __future__ import annotations
2
2
 
3
- import numpy as _np
4
3
  import math as _mt
5
4
  import random as _rnd
5
+ from typing import Any, Generator, Literal
6
6
 
7
7
  PI = _mt.pi
8
- HALF_PI = PI/2
9
- QUARTER_PI = PI/4
10
- DOUBLE_PI = PI*2
8
+ PI_HALF = PI/2
9
+ PI_QUARTER = PI/4
10
+ PI_DOUBLE = PI*2
11
11
 
12
- # regular expression to remove comments:
13
- # """([\s\S]*?)"""
14
-
15
- #
12
+ sign = lambda val: -1 if val < 0 else (1 if val > 0 else 0)
16
13
 
17
14
  class Vector2D:
18
- round_values_on_print :int|float= 2
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
- """
15
+ round_values_on_print = 2
16
+ def __init__(self, x=.0, y=.0) -> None:
40
17
  self.x = x
41
18
  self.y = y
42
-
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
- """
67
- self.x = x
68
- self.y = y
69
-
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
19
 
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
- """
104
- other = self.__normalize__(other)
20
+ def distance_to(self, other, sqrd=True) -> int|float:
105
21
  d = (self.x - other.x)**2 + (self.y - other.y)**2
106
22
  return (d**(1/2) if sqrd else d)
107
23
 
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
- """
138
- other = self.__normalize__(other)
24
+ def angle_to(self, other) -> int|float:
139
25
  return _mt.atan2(other.y - self.y, other.x - self.x)
140
26
 
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)
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)
154
29
 
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
- """
174
- x = radius * _mt.cos(_mt.radians(degs)) + self.x
175
- y = radius * _mt.sin(_mt.radians(degs)) + self.y
176
- return Vector2D(x, y)
177
-
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
- """
211
- x = radius * _mt.cos(rad) + self.x
212
- y = radius * _mt.sin(rad) + self.y
213
- return Vector2D(x, y)
214
-
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).
30
+ @property
31
+ def angle(self) -> int|float:
32
+ return _mt.atan2(self.y, self.x)
230
33
 
231
- ## Explanation:
232
- The function creates a new Vector2D other with the same x and y coordinates as the current other.
34
+ @angle.setter
35
+ def angle(self, argv) -> None:
36
+ print(argv)
233
37
 
234
- The result is returned as a new Vector2D other, effectively making a copy of the original other.
235
- """
38
+ @property
39
+ def copy(self) -> "Vector2D":
236
40
  return Vector2D(self.x, self.y)
237
41
 
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
- """
277
- return self.no_zero_div_error(abs(self), "zero")
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
278
59
 
279
- def floor(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
60
+ def floor(self, n=1) -> "Vector2D":
280
61
  return self.__floor__(n)
281
62
 
282
- def ceil(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
63
+ def ceil(self, n=1) -> "Vector2D":
283
64
  return self.__ceil__(n)
284
65
 
285
- def round(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
66
+ def round(self, n=1) -> "Vector2D":
286
67
  return self.__round__(n)
287
68
 
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
- """
323
- if not any(isinstance(start, cls) for cls in {Vector2D, V2}):
324
- if type(start) in int|float: start = Vector2D(start, start) #type: ignore
325
- elif type(start) == None: start = Vector2D(0,0)
326
- else: raise Exception(f"\nArg start must be in [Vector2D, int, float, tuple, list] not a [{type(start)}]\n")
327
- if not any(isinstance(end, cls) for cls in {Vector2D, V2}):
328
- if type(end) in int|float: end = Vector2D(end, end) #type: ignore
329
- elif type(end) == None: end = Vector2D(1,1)
330
- else: raise Exception(f"\nArg end must be in [Vector2D, int, float, tuple, list] not a [{type(end)}]\n")
331
- return start + Vector2D(_rnd.random(), _rnd.random()) * (end - start) #type: ignore
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)
332
82
 
333
- def dot_product(self, other:"float|int|Vector2D|list|tuple") -> float:
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
- """
83
+ def dot_product(self, other) -> float:
357
84
  return self.x * other.x + self.y * other.y
358
85
 
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
- """
387
- mag = self.length()
388
- if mag == 0:
389
- return self
390
- return Vector2D(self.x / mag, self.y / mag)
391
-
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
- """
429
- other = self.__normalize__(other)
86
+ def projection(self, other) -> "Vector2D":
430
87
  dot_product = self.dot_product(other)
431
- magnitude_product = other.length() ** 2
88
+ magnitude_product = other.length ** 2
432
89
  if magnitude_product == 0:
433
90
  raise ValueError("Cannot calculate projection for zero vectors.")
434
91
  return other * (dot_product / magnitude_product)
435
92
 
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
- """
464
- normal = self.__normalize__(normal)
465
- projection = self.projection(normal)
466
- return self - projection * 2
93
+ def reflection(self, normal) -> "Vector2D":
94
+ return self - self.projection(normal) * 2
467
95
 
468
96
  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
- """
492
- r = self.length()
97
+ r = self.length
493
98
  theta = _mt.atan2(self.y, self.x)
494
99
  return r, theta
495
100
 
496
101
  @classmethod
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
+ def polar_to_cartesian(cls, r, theta) -> "Vector2D":
521
103
  x = r * _mt.cos(theta)
522
104
  y = r * _mt.sin(theta)
523
105
  return cls(x, y)
@@ -526,150 +108,40 @@ class Vector2D:
526
108
  return self.x + self.y * 1j
527
109
 
528
110
  @classmethod
529
- def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D":
111
+ def complex_to_cartesian(cls, complex_n) -> "Vector2D":
530
112
  return cls(complex_n.real, complex_n.imag)
531
113
 
532
- def length(self) -> float:
533
- return (self.x ** 2 + self.y ** 2) ** .5
534
-
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
- """
567
- other = self.__normalize__(other)
114
+ def lerp(self, other, t=.1) -> "Vector2D":
115
+ other = Vector2D.__normalize__(other)
568
116
  if not 0 <= t <= 1:
569
117
  raise ValueError("t must be between 0 and 1 for linear interpolation.")
570
118
  return Vector2D(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
571
119
 
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
- """
604
- if center is None: center = V2z
605
- else: center = self.__normalize__(center)
120
+ def rotate(self, angle, center=None) -> "Vector2D":
121
+ if center is None: center = Vector2D.zero()
606
122
  translated = self - center
607
123
  cos_angle = _mt.cos(angle)
608
124
  sin_angle = _mt.sin(angle)
609
125
  return Vector2D(translated.x * cos_angle - translated.y * sin_angle, translated.x * sin_angle + translated.y * cos_angle) + center
610
126
 
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
- """
654
- if any(isinstance(n, cls) for cls in {int, float}):
127
+ def no_zero_div_error(self, n, error_mode="zero") -> "Vector2D":
128
+ if isinstance(n, (int, float)):
655
129
  if n == 0:
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))
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))
657
131
  else:
658
132
  return self / n
659
- elif any(isinstance(n, cls) for cls in {Vector2D, V2}):
133
+ elif isinstance(n, Vector2D):
660
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
661
135
  else:
662
136
  raise Exception(f"\nArg n must be in [Vector2D, int, float, tuple, list] not a [{type(n)}]\n")
663
137
 
664
- def min(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
665
- other = self.__normalize__(other)
138
+ def min(self, other) -> "Vector2D":
666
139
  return Vector2D(min(self.x, other.x), min(self.y, other.y))
667
140
 
668
- def max(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
669
- other = self.__normalize__(other)
141
+ def max(self, other) -> "Vector2D":
670
142
  return Vector2D(max(self.x, other.x), max(self.y, other.y))
671
143
 
672
- def advanced_stringify(self:"Vector2D", precision:float|None=None, use_scientific_notation:bool=False, return_as_list=False) -> str:
144
+ def advanced_stringify(self, precision=None, use_scientific_notation=False, return_as_list=False) -> str|list[str]:
673
145
  precision = self.round_values_on_print if precision == None else precision
674
146
  def optimize(value) -> str:
675
147
  abs_value = abs(value)
@@ -680,433 +152,399 @@ class Vector2D:
680
152
  else:
681
153
  return f"{value:.{precision}e}"
682
154
  if return_as_list:
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
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}"]
684
156
  return f"{optimize(self.x)}, {optimize(self.y)}" if use_scientific_notation else f"{self.x:.{precision}f}, {self.y:.{precision}f}"
685
157
 
686
- def __str__(self:"Vector2D") -> str:
158
+ def __str__(self) -> str:
687
159
  return f"{self.x:.{self.round_values_on_print}f}, {self.y:.{self.round_values_on_print}f}"
688
160
 
689
- def __repr__(self:"Vector2D") -> str:
161
+ def __repr__(self) -> str:
690
162
  return f"x:{self.x:.{self.round_values_on_print}f}\ty:{self.y:.{self.round_values_on_print}f}"
691
163
 
692
- def __call__(self:"Vector2D", return_tuple=False) -> list|tuple:
693
- return (self.x, self.y) if return_tuple else [self.x, self.y]
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
694
229
 
695
230
  # normal operations Vector2D + a
696
- def __add__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
697
- other = self.__normalize__(other)
231
+ def __add__(self, other) -> "Vector2D":
232
+ other = Vector2D.__normalize__(other)
698
233
  return Vector2D(self.x + other.x, self.y + other.y)
699
234
 
700
- def __sub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
701
- other = self.__normalize__(other)
235
+ def __sub__(self, other) -> "Vector2D":
236
+ other = Vector2D.__normalize__(other)
702
237
  return Vector2D(self.x - other.x, self.y - other.y)
703
238
 
704
- def __mul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
705
- other = self.__normalize__(other)
239
+ def __mul__(self, other) -> "Vector2D":
240
+ other = Vector2D.__normalize__(other)
706
241
  return Vector2D(self.x * other.x, self.y * other.y)
707
242
 
708
- def __mod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
709
- other = self.__normalize__(other)
243
+ def __mod__(self, other) -> "Vector2D":
244
+ other = Vector2D.__normalize__(other)
710
245
  return Vector2D(self.x % other.x, self.y % other.y)
711
246
 
712
- def __pow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
713
- other = self.__normalize__(other)
247
+ def __pow__(self, other) -> "Vector2D":
248
+ other = Vector2D.__normalize__(other)
714
249
  return Vector2D(self.x ** other.x, self.y ** other.y)
715
250
 
716
- def __truediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
717
- other = self.__normalize__(other)
251
+ def __truediv__(self, other) -> "Vector2D":
252
+ other = Vector2D.__normalize__(other)
718
253
  return Vector2D(self.x / other.x, self.y / other.y)
719
254
 
720
- def __floordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
721
- other = self.__normalize__(other)
255
+ def __floordiv__(self, other) -> "Vector2D":
256
+ other = Vector2D.__normalize__(other)
722
257
  return Vector2D(self.x // other.x, self.y // other.y)
723
258
 
724
259
  # right operations a + Vector2D
725
- def __radd__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
260
+ def __radd__(self, other) -> "Vector2D":
726
261
  return self.__add__(other)
727
262
 
728
- def __rsub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
729
- other = self.__normalize__(other)
263
+ def __rsub__(self, other) -> "Vector2D":
264
+ other = Vector2D.__normalize__(other)
730
265
  return Vector2D(other.x - self.x, other.y - self.y)
731
266
 
732
- def __rmul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
267
+ def __rmul__(self, other) -> "Vector2D":
733
268
  return self.__mul__(other)
734
269
 
735
- def __rmod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
736
- other = self.__normalize__(other)
270
+ def __rmod__(self, other) -> "Vector2D":
271
+ other = Vector2D.__normalize__(other)
737
272
  return Vector2D(other.x % self.x, other.y % self.y)
738
273
 
739
- def __rpow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
740
- other = self.__normalize__(other)
274
+ def __rpow__(self, other) -> "Vector2D":
275
+ other = Vector2D.__normalize__(other)
741
276
  return Vector2D(other.x ** self.x, other.y ** self.y)
742
277
 
743
- def __rtruediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
744
- other = self.__normalize__(other)
278
+ def __rtruediv__(self, other) -> "Vector2D":
279
+ other = Vector2D.__normalize__(other)
745
280
  return Vector2D(other.x / self.x, other.y / self.y)
746
281
 
747
- def __rfloordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
748
- other = self.__normalize__(other)
282
+ def __rfloordiv__(self, other) -> "Vector2D":
283
+ other = Vector2D.__normalize__(other)
749
284
  return Vector2D(other.x // self.x, other.y // self.y)
750
285
 
751
286
  # in-place operations Vector2D += a
752
- def __iadd__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
753
- other = self.__normalize__(other)
287
+ def __iadd__(self, other) -> "Vector2D":
288
+ other = Vector2D.__normalize__(other)
754
289
  self.x += other.x
755
290
  self.y += other.y
756
291
  return self
757
292
 
758
- def __isub__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
759
- other = self.__normalize__(other)
293
+ def __isub__(self, other) -> "Vector2D":
294
+ other = Vector2D.__normalize__(other)
760
295
  self.x -= other.x
761
296
  self.y -= other.y
762
297
  return self
763
298
 
764
- def __imul__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
765
- other = self.__normalize__(other)
299
+ def __imul__(self, other) -> "Vector2D":
300
+ other = Vector2D.__normalize__(other)
766
301
  self.x *= other.x
767
302
  self.y *= other.y
768
303
  return self
769
304
 
770
- def __itruediv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
771
- other = self.__normalize__(other)
305
+ def __itruediv__(self, other) -> "Vector2D":
306
+ other = Vector2D.__normalize__(other)
772
307
  self.x /= other.x
773
308
  self.y /= other.y
774
309
  return self
775
310
 
776
- def __imod__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
777
- other = self.__normalize__(other)
311
+ def __imod__(self, other) -> "Vector2D":
312
+ other = Vector2D.__normalize__(other)
778
313
  self.x %= other.x
779
314
  self.y %= other.y
780
315
  return self
781
316
 
782
- def __ipow__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
783
- other = self.__normalize__(other)
317
+ def __ipow__(self, other) -> "Vector2D":
318
+ other = Vector2D.__normalize__(other)
784
319
  self.x **= other.x
785
320
  self.y **= other.y
786
321
  return self
787
322
 
788
- def __ifloordiv__(self:"Vector2D", other:"float|int|Vector2D|list|tuple") -> "Vector2D":
789
- other = self.__normalize__(other)
323
+ def __ifloordiv__(self, other) -> "Vector2D":
324
+ other = Vector2D.__normalize__(other)
790
325
  self.x //= other.x
791
326
  self.y //= other.y
792
327
  return self
793
328
 
794
329
  # comparasion
795
330
  def __eq__(self, other) -> bool:
796
- try: other = self.__normalize__(other)
331
+ try: other = Vector2D.__normalize__(other)
797
332
  except: return False
798
333
  return self.x == other.x and self.y == other.y
799
334
 
800
335
  def __ne__(self, other) -> bool:
801
336
  return not self.__eq__(other)
802
337
 
803
- def __abs__(self:"Vector2D") -> "Vector2D":
338
+ def __abs__(self) -> "Vector2D":
804
339
  return Vector2D(abs(self.x), abs(self.y))
805
340
 
806
- def __round__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
807
- n = self.__normalize__(n)
341
+ def __round__(self, n=1) -> "Vector2D":
342
+ n = Vector2D.__normalize__(n)
808
343
  return Vector2D(round(self.x / n.x) * n.x, round(self.y / n.y) * n.y)
809
344
 
810
- def __floor__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
811
- n = self.__normalize__(n)
812
- return Vector2D(_mt.floor(self.x / n.x) * n.x, _mt.floor(self.y / n.y) * n.y)
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)
813
348
 
814
- def __ceil__(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
815
- n = self.__normalize__(n)
816
- return Vector2D(_mt.ceil(self.x / n.x) * n.x, _mt.ceil(self.y / n.y) * n.y)
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)
817
352
 
818
- def __float__(self:"Vector2D") -> "Vector2D":
353
+ def __float__(self) -> "Vector2D":
819
354
  return Vector2D(float(self.x), float(self.y))
820
355
 
821
- def __getitem__(self:"Vector2D", n) -> int|float:
822
- if n in [0, "x"]:
356
+ def __getitem__(self, n) -> int|float:
357
+ if n == 0 or n == "x":
823
358
  return self.x
824
- elif n in [1, "y"]:
359
+ elif n == 1 or n == "y":
825
360
  return self.y
826
361
  else:
827
362
  raise IndexError("V2 has only x,y...")
828
363
 
829
- def __normalize__(self:"Vector2D", other) -> "Vector2D":
830
- if not isinstance(other, Vector2D):
831
- if any(isinstance(other, cls) for cls in {int, float}):
832
- return Vector2D(other, other)
833
- elif any(isinstance(other, cls) for cls in {list, tuple}):
834
- return Vector2D(*other[:2])
835
- else:
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}]")
840
- return other
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
841
466
 
842
467
  from .cvb import *
843
468
 
844
469
  V2 = Vector2D
845
470
 
846
- V2inf = Vector2D(float('inf'), float('inf'))
847
- V2z = VectorZero = Vector2D()
848
- V2one = Vector2D(1.0, 1.0)
471
+ V2zero = Vector2D(0, 0)
849
472
 
850
- def rgb(r:float, g:float, b:float) -> tuple[float, float, float]:
851
- return (r,g,b)
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"))
852
477
 
853
- def color_lerp(current_c:list|tuple, final_c:list|tuple, step:int|float=.1) -> tuple[float, float, float]:
854
- """
855
- # Linearly interpolate between two colors.
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"))
856
482
 
857
- ## Parameters:
858
- current_c (tuple or list): The RGB values of the current color as a tuple or list.
859
- final_c (tuple or list): The RGB values of the target color as a tuple or list.
860
- step (int or float): The interpolation step, ranging from 0.0 (current color) to 1.0 (target color).
483
+ V2up = Vector2D(0, 1)
484
+ V2right = Vector2D(1, 0)
485
+ V2down = Vector2D(0, -1)
486
+ V2left = Vector2D(-1, 0)
861
487
 
862
- ## Returns:
863
- tuple: The RGB values of the interpolated color as a tuple.
488
+ V2up_right = Vector2D(1, 1)
489
+ V2down_right = Vector2D(1, -1)
490
+ V2up_left = Vector2D(-1, 1)
491
+ V2down_left = Vector2D(-1, -1)
864
492
 
865
- ## Example:
866
- current_c = (255, 0, 0)
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
867
497
 
868
- final_c = (0, 0, 255)
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)
869
503
 
870
- step = 0.5
871
504
 
872
- interpolated_color = color_lerp(current_c, final_c, step)
505
+ def rgb(r:float, g:float, b:float) -> tuple[float, float, float]:
506
+ return (r,g,b)
873
507
 
874
- print(f"At step {step}: RGB {interpolated_color}")
875
-
876
- This will calculate the color at an interpolation step of 0.5 between (255, 0, 0) and (0, 0, 255).
877
- """
508
+ def color_lerp(current_c:list|tuple, final_c:list|tuple, step=.1) -> tuple[float, float, float]:
878
509
  return tuple(c + (final_c[i] - c) * step for i,c in enumerate(current_c)) #type: ignore
879
510
 
880
- def color_fade(starting_c:list|tuple, final_c:list|tuple, index:int|float, max_index:int|float) -> tuple[float, float, float]:
881
- """
882
- # Calculate the color at a specific index of a color fade between two given colors.
883
-
884
- ## Parameters:
885
- starting_c (tuple or list): The RGB values of the starting color as a tuple or list.
886
- final_c (tuple or list): The RGB values of the final color as a tuple or list.
887
- index (int or float): The current index of the color fade, representing a position
888
- between the starting and final colors.
889
- max_index (int or float): The maximum index of the color fade, indicating the endpoint
890
- position between the starting and final colors.
891
-
892
- ## Returns:
893
- tuple: The RGB values of the color at the specified index as a tuple.
894
-
895
- ## Example:
896
- starting_c = (255, 0, 0)
897
-
898
- final_c = (0, 0, 255)
899
-
900
- max_index = 100
901
-
902
- for i in range(max_index + 1):
903
-
904
- color_at_index = color_fade(starting_c, final_c, i, max_index)
905
-
906
- print(f"At index {i}: RGB {color_at_index}")
907
-
908
- This will print the colors transitioning from (255, 0, 0) to (0, 0, 255).
909
- """
511
+ def color_fade(starting_c:list|tuple, final_c:list|tuple, index, max_index) -> tuple[float, float, float]:
910
512
  return tuple((starting_c[i] - final_c[i]) / max_index * (max_index - index) + final_c[i] for i in range(3)) #type: ignore
911
513
 
912
514
  def weighted_color_fade(colors_dict:dict) -> tuple[float, float, float]:
913
- """
914
- # Calculate the weighted color based on a dictionary of colors and their corresponding weights.
915
-
916
- ## Parameters:
917
- colors_dict (dict): A dictionary where keys represent RGB color values as tuples,
918
- and values represent the weights (floats) for each color.
919
-
920
- ## Returns:
921
- tuple: The RGB values of the calculated weighted color as a tuple.
922
-
923
- ## Example:
924
- colors_dict = {
925
-
926
- (255, 255, 255): 0.1,
927
-
928
- (0, 0, 0): 0.9,
929
-
930
- }
931
-
932
- weighted_color = weighted_color_fade(colors_dict)
933
-
934
- print(f"Weighted color: RGB {weighted_color}")
935
-
936
- This will print the weighted color based on the provided dictionary.
937
- """
938
515
  colors = colors_dict.keys()
939
516
  weights = colors_dict.values()
940
517
 
941
518
  if float("inf") in weights: return list(colors)[list(weights).index(float("inf"))]
942
519
  return tuple(sum(n[i]*w for n,w in zip(colors, weights)) / sum(weights) for i in range(3)) #type: ignore
943
520
 
944
- def color_distance(starting_c:list|tuple, final_c:list|tuple, sqrd:bool=True) -> float:
945
- """
946
- # Calculate the distance between two colors in RGB space.
947
-
948
- ## Parameters:
949
- starting_c (list or tuple): The RGB values of the starting color.
950
- final_c (list or tuple): The RGB values of the final color.
951
- sqrd (bool, optional): If True, return the squared distance. If False, return
952
- the actual distance. Default is True.
953
-
954
- ## Returns:
955
- float: The squared distance between the two colors if `sqrd` is True, otherwise
956
- the actual distance.
957
-
958
- ## Example:
959
- starting_c = [255, 0, 0]
960
-
961
- final_c = [0, 255, 0]
962
-
963
- squared_distance = color_distance(starting_c, final_c)
964
-
965
- print(f"Squared Distance: {squared_distance}")
966
-
967
- distance = color_distance(starting_c, final_c, sqrd=False)
968
-
969
- print(f"Actual Distance: {distance}")
970
-
971
- This will calculate the squared and actual distances between the colors.
972
-
973
- ## Explanation:
974
- The function first calculates the squared distance between the two colors in RGB
975
- space. It does this by computing the sum of the squared differences of the RGB
976
- components for each color. The squared distance is obtained by taking the square
977
- root of this sum.
978
-
979
- The `sqrd` parameter allows the user to choose between returning the squared
980
- distance or the actual distance. If `sqrd` is True, the function returns the
981
- squared distance, and if `sqrd` is False, it returns the actual distance.
982
- """
521
+ def color_distance(starting_c:list|tuple, final_c:list|tuple, sqrd) -> float:
983
522
  distance = sum([(starting_c[i]-final_c[i])**2 for i in range(3)])
984
523
  return (distance ** .5) if sqrd else distance
985
524
 
986
- def angular_interpolation(starting_angle:int|float, final_angle:int|float, step:int|float=.1) -> float:
987
- """
988
- # Perform angular interpolation between two angles using the shortest distance.
989
-
990
- ## Parameters:
991
- starting_angle (int or float): The initial angle in radians.
992
- final_angle (int or float): The target angle in radians to interpolate towards.
993
- step (int or float, optional): The step size for interpolation in radians. Default is 0.1.
994
-
995
- ## Returns:
996
- float: The interpolated angle as a result of angular interpolation.
997
-
998
- ## Example:
999
- starting_angle = 1.0
1000
-
1001
- final_angle = 5.0
1002
-
1003
- interpolated_angle = angular_interpolation(starting_angle, final_angle)
1004
-
1005
- print(f"Interpolated angle: {interpolated_angle}")
1006
-
1007
- This will print the interpolated angle using angular interpolation.
1008
-
1009
- ## Explanation:
1010
- The function calculates three distances between the `starting_angle` and the
1011
- `final_angle`. These distances represent possible angular interpolations:
1012
- 1. The direct interpolation from `starting_angle` to `final_angle`.
1013
- 2. The interpolation by taking a full circle (2 * pi) and then proceeding from
1014
- `starting_angle` to `final_angle`.
1015
- 3. The interpolation by taking a full circle (2 * pi) in the opposite direction
1016
- and then proceeding from `starting_angle` to `final_angle`.
1017
-
1018
- The function then chooses the shortest distance from the three options and returns
1019
- the interpolated angle obtained by multiplying the shortest distance by the `step`
1020
- value.
525
+ def lerp(starting, ending, step=.1) -> float:
526
+ return starting + (ending - starting) * step
1021
527
 
1022
- The `step` parameter controls the granularity of interpolation. Smaller `step` values
1023
- provide more fine-grained interpolation but may require more iterations.
1024
- """
1025
- distances = (final_angle - starting_angle, final_angle - DOUBLE_PI - starting_angle, final_angle + DOUBLE_PI - starting_angle)
1026
- return min(distances, key=abs) * step
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
1027
536
 
1028
- def bezier_cubic_interpolation(t:float, p0:Vector2D, p1:Vector2D) -> float:
537
+ def bezier_cubic_interpolation(t, p0, p1) -> float:
1029
538
  return t*p0.y*3*(1 - t)**2 + p1.y*3*(1 - t) * t**2 + t**3
1030
539
 
1031
- def bezier_quadratic_interpolation(t:float, p0:Vector2D) -> float:
540
+ def bezier_quadratic_interpolation(t, p0) -> float:
1032
541
  return 2*(1-t)*t*p0.y+t**2
1033
542
 
1034
- def avg_position(*others:"Vector2D") -> Vector2D:
1035
- """
1036
- # Calculate the average position for a variable number of Vector2D others.
1037
-
1038
- ## Parameters:
1039
- *others (Vector2D): Variable number of Vector2D others representing positions.
1040
-
1041
- ## Returns:
1042
- Vector2D: The average position as a new Vector2D other.
1043
-
1044
- ## Example:
1045
- position1 = Vector2D(10, 20)
1046
-
1047
- position2 = Vector2D(30, 40)
1048
-
1049
- position3 = Vector2D(50, 60)
1050
-
1051
- average_pos = avg_position(position1, position2, position3)
1052
-
1053
- print(average_pos)
1054
-
1055
- This will print the average position of the three Vector2D others.
1056
-
1057
- ## Explanation:
1058
- The function takes a variable number of Vector2D others as input, representing positions.
1059
- It calculates the sum of all the Vector2D others using the `sum` function and then divides
1060
- it by the total number of others (length of `others`) to find the average position.
1061
-
1062
- The result is returned as a new Vector2D other representing the average position.
1063
- """
543
+ def avg_position(*others) -> Vector2D:
1064
544
  return sum(others) / len(others) #type: ignore
1065
545
 
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]:
1067
- """
1068
- # Find intersection points between a ray or line segment and multiple line segments.
1069
-
1070
- ## Parameters:
1071
- ray (list[Vector2D] | tuple[Vector2D, Vector2D]): The ray or line segment represented by two endpoints
1072
- (start and end points).
1073
- lines (list[tuple[Vector2D, Vector2D]]): A list of line segments represented by tuples of their endpoints.
1074
- return_inter_lines (bool, optional): If True, return a list of tuples containing the intersection points and the
1075
- corresponding intersecting line segment. Default is False.
1076
- sort (bool, optional): If True, sort the intersection points by their distance from the ray's start point.
1077
- Default is False.
1078
- return_empty (bool, optional): If True, include None for line segments with no intersection. Default is False.
1079
-
1080
- ## Returns:
1081
- list[tuple[Vector2D | None, tuple[Vector2D | V2, Vector2D | V2]]] | list[Vector2D | None]:
1082
- - If return_inter_lines is True, returns a list of tuples, each containing:
1083
- - The intersection point (Vector2D) if it exists, or None otherwise.
1084
- - The corresponding intersecting line segment (tuple[Vector2D | V2, Vector2D | V2]).
1085
- - If return_inter_lines is False, returns a list of intersection points (Vector2D) if they exist, or None otherwise.
1086
-
1087
- # Example:
1088
- ray = [Vector2D(1, 2), Vector2D(5, 3)]
1089
- lines = [(Vector2D(2, 2), Vector2D(4, 4)), (Vector2D(3, 3), Vector2D(6, 2))]
1090
- result = inter_points(ray, lines, return_inter_lines=True, sort=True)
1091
- print(result)
1092
-
1093
- ## Explanation:
1094
- The function finds the intersection points (if any) between the given ray (or line segment) and the provided list
1095
- of line segments. The intersection points are returned in a list.
1096
-
1097
- If return_inter_lines is True, the function returns a list of tuples, where each tuple contains the intersection point
1098
- (Vector2D) and the corresponding intersecting line segment (tuple[Vector2D | V2, Vector2D | V2]). If return_inter_lines
1099
- is False, the function returns only a list of intersection points (Vector2D) without the corresponding line segments.
1100
-
1101
- If sort is True, the intersection points are sorted by their distance from the ray's start point. If sort is False,
1102
- the intersection points are returned in the order they were found.
1103
-
1104
- If return_empty is True, the function includes None for line segments with no intersection. If return_empty is False,
1105
- line segments with no intersection are omitted from the result.
1106
-
1107
- Example usage is shown in the "Example" section above.
1108
- """
1109
- def lineLineIntersect(P0:"Vector2D", P1:"Vector2D", Q0:"Vector2D", Q1:"Vector2D") -> "Vector2D | None":
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":
1110
548
  d = (P1.x-P0.x) * (Q1.y-Q0.y) + (P1.y-P0.y) * (Q0.x-Q1.x)
1111
549
  if d == 0:
1112
550
  return None
@@ -1123,138 +561,34 @@ def inter_points(ray:list["Vector2D"]|tuple["Vector2D", "Vector2D"], lines:list[
1123
561
  collisions = [ip for line in lines if ((ip:=lineLineIntersect(line[1], line[0], ray[1], ray[0]))!=None or return_empty)]
1124
562
  return sorted(collisions, key=lambda x: ray[0].distance_to(x, False) if x != None else _mt.inf) if sort else collisions
1125
563
 
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]]:
1127
- """
1128
- # Generate points for a rectangle based on the given parameters.
1129
-
1130
- ## Parameters:
1131
- position (Vector2D): The center position of the rectangle.
1132
- size (Vector2D): The size of the rectangle (width and height).
1133
- rotation (int|float, optional): The rotation angle in degrees. Default is 0.
1134
- pos_in_middle (bool, optional): If True, the points represent corners of the rectangle.
1135
- If False, the points represent the rectangle's edges.
1136
- Default is True.
1137
- return_list (bool, optional): If True, return the points as lists instead of Vector2D others.
1138
- Default is False.
1139
- clockwise_return (bool, optional): If True, return the points in clockwise order (A, B, D, C).
1140
- If False, return the points in counterclockwise order (A, B, C, D).
1141
- Default is False.
1142
-
1143
- ## Returns:
1144
- tuple: A tuple containing the four points of the rectangle.
1145
-
1146
- ## Example:
1147
- position = Vector2D(100, 100)
1148
-
1149
- size = Vector2D(50, 30)
1150
-
1151
- rotation = 45
1152
-
1153
- points = get_points(position, size, rotation)
1154
-
1155
- print(points)
1156
-
1157
- This will print the four points of the rotated rectangle.
1158
-
1159
- ## Explanation:
1160
- The function calculates the four points (A, B, C, D) of the rectangle based on the center position,
1161
- size, rotation, and pos_in_middle parameters. The points represent the rectangle's corners if pos_in_middle
1162
- is True, and the edges if pos_in_middle is False.
1163
-
1164
- The points are returned as Vector2D others unless the return_list parameter is set to True. In that case,
1165
- the points will be returned as lists.
1166
-
1167
- The clockwise_return parameter determines the order of the points. If True, the points will be returned in
1168
- clockwise order (A, B, D, C), otherwise, they will be returned in counterclockwise order (A, B, C, D).
1169
- """
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]]:
1170
565
  if pos_in_middle:
1171
- d,a = size.length()/2, size.angle()
1172
- d1, d2 = V2z.point_from_rads(rotation+a, d), V2z.point_from_rads(rotation-a, d)
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)
1173
568
  A, B, C, D = position+d1, position+d2, position-d2, position-d1
1174
569
  else:
1175
- A, B, C, D = position.copy(),\
1176
- position.point_from_rads(rotation + V2z.angle_to(Vector2D(size.x, 0)), V2z.distance_to(Vector2D(size.x, 0))),\
1177
- position.point_from_rads(rotation + V2z.angle_to(Vector2D(0, size.y)), V2z.distance_to(Vector2D(0, size.y))),\
1178
- position.point_from_rads(rotation + V2z.angle_to(size), V2z.distance_to(size))
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))
1179
574
  points = (A, B, C, D) if not clockwise_return else (A, B, D, C)
1180
575
  return points if not return_list else tuple(x() for x in points)
1181
576
 
1182
- def get_lines(position:Vector2D, size:Vector2D, rotation:int|float=0, pos_in_middle:bool=True) -> list[list]:
1183
- """
1184
- # Generate lines representing the sides of a rectangle based on the given parameters.
1185
-
1186
- ## Parameters:
1187
- position (Vector2D): The center position of the rectangle.
1188
- size (Vector2D): The size of the rectangle (width and height).
1189
- rotation (int|float, optional): The rotation angle in degrees. Default is 0.
1190
- pos_in_middle (bool, optional): If True, the points represent corners of the rectangle.
1191
- If False, the points represent the rectangle's edges.
1192
- Default is True.
1193
-
1194
- ## Returns:
1195
- list[list[Vector2D]]: A list of lists, where each sublist contains two Vector2D others
1196
- representing the start and end points of a line segment.
1197
-
1198
- ## Example:
1199
- position = Vector2D(100, 100)
1200
-
1201
- size = Vector2D(50, 30)
1202
-
1203
- rotation = 45
1204
-
1205
- lines = get_lines(position, size, rotation)
1206
-
1207
- print(lines)
1208
-
1209
- This will print the four line segments representing the sides of the rotated rectangle.
1210
-
1211
- ## Explanation:
1212
- The function calculates the four points (A, B, C, D) of the rectangle using the `get_points` function
1213
- based on the center position, size, rotation, and pos_in_middle parameters.
1214
-
1215
- The function then returns a list of lists, where each sublist contains two Vector2D others representing
1216
- the start and end points of a line segment forming the sides of the rectangle.
1217
- """
577
+ def get_lines(position, size, rotation=0, pos_in_middle=True) -> list[list]:
1218
578
  A, B, C, D = get_points(position, size, rotation, pos_in_middle)
1219
579
  return [[A, B], [A, C], [C, D], [D, B]]
1220
580
 
1221
- def distance_line_point(line_point_a:Vector2D, line_point_b:Vector2D, point_c:Vector2D) -> float:
1222
- """
1223
- # Calculate the distance between a line segment and a point.
1224
-
1225
- ## Parameters:
1226
- line_point_a (Vector2D): The starting point of the line segment.
1227
- line_point_b (Vector2D): The ending point of the line segment.
1228
- point_c (Vector2D): The point to which the distance is calculated.
1229
-
1230
- ## Returns:
1231
- float: The distance between the line segment and the point.
1232
-
1233
- ## Example:
1234
- line_point_a = Vector2D(0, 0)
1235
-
1236
- line_point_b = Vector2D(10, 0)
1237
-
1238
- point_c = Vector2D(5, 5)
1239
-
1240
- distance = distance_line_point(line_point_a, line_point_b, point_c)
1241
-
1242
- print(distance)
1243
-
1244
- This will print the distance between the line segment and the point.
1245
-
1246
- ## Explanation:
1247
- The function calculates the distance between a line segment defined by two points (line_point_a and line_point_b)
1248
- and a third point (point_c).
1249
-
1250
- It does this by first computing the cross product of vectors (line_point_b - line_point_a) and (line_point_a - point_c).
1251
- The magnitude of the resulting vector is divided by the magnitude of (line_point_b - line_point_a) to obtain the distance.
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
1252
584
 
1253
- The result is returned as a float representing the distance between the line segment and the point.
1254
- """
1255
- 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
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
1256
590
 
1257
- def optimize_value_string(value:int|float, precision:int) -> str:
591
+ def optimize_value_string(value, precision) -> str:
1258
592
  abs_value = abs(value)
1259
593
  if abs_value < 1/10**precision and abs_value != 0:
1260
594
  return f"{value:.{precision}e}"