e2D 1.4.19__tar.gz → 1.4.23__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Riccardo Mariani
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Riccardo Mariani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: e2D
3
- Version: 1.4.19
3
+ Version: 1.4.23
4
4
  Summary: Python library for 2D games. Streamlines dev with keyboard/mouse input, vector calculations, color manipulation, and collision detection. Simplify game creation and unleash creativity!
5
5
  Home-page: https://github.com/marick-py/e2D
6
6
  Author: Riccardo Mariani
@@ -13,6 +13,7 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: numpy
15
15
  Requires-Dist: pygame
16
+ Dynamic: license-file
16
17
 
17
18
  # e2D
18
19
  ## A Python Game Development Library
@@ -10,6 +10,7 @@ PI_QUARTER = PI/4
10
10
  PI_DOUBLE = PI*2
11
11
 
12
12
  sign = lambda val: -1 if val < 0 else (1 if val > 0 else 0)
13
+ clamp = lambda x, minn, maxx: x if x > minn and x < maxx else (minn if x < minn else maxx)
13
14
 
14
15
  class Vector2D:
15
16
  round_values_on_print = 2
@@ -34,29 +35,71 @@ class Vector2D:
34
35
  @angle.setter
35
36
  def angle(self, new_angle) -> None:
36
37
  self.rotate(new_angle - self.angle)
38
+
39
+ @property
40
+ def aspect_x(self) -> float:
41
+ return self.x / self.y if self.y != 0 else 0
42
+
43
+ @aspect_x.setter
44
+ def aspect_x(self, new_aspect) -> None:
45
+ self.x = self.y * new_aspect
37
46
 
38
47
  @property
48
+ def aspect_y(self) -> float:
49
+ return self.y / self.x if self.x != 0 else 0
50
+
51
+ @aspect_y.setter
52
+ def aspect_y(self, new_aspect) -> None:
53
+ self.y = self.x * new_aspect
54
+
39
55
  def copy(self) -> "Vector2D":
40
56
  return Vector2D(self.x, self.y)
41
57
 
42
58
  @property
43
59
  def sign(self) -> "Vector2D":
44
60
  return Vector2D(sign(self.x), sign(self.y))
61
+
62
+ def clamp(self, min_val: Vector2D, max_val: Vector2D) -> "Vector2D":
63
+ return Vector2D(clamp(self.x, min_val.x, max_val.x), clamp(self.y, min_val.y, max_val.y))
64
+
65
+ def iclamp(self, min_val: Vector2D, max_val: Vector2D) -> None:
66
+ self.x = clamp(self.x, min_val.x, max_val.x)
67
+ self.y = clamp(self.y, min_val.y, max_val.y)
45
68
 
46
69
  @property
47
- def normalize(self) -> "Vector2D":
70
+ def normalized(self) -> "Vector2D":
48
71
  if (mag:=self.length) == 0:
49
- return self.copy
72
+ return self.copy()
50
73
  return Vector2D(self.x / mag, self.y / mag)
51
74
 
52
75
  @property
53
76
  def length(self) -> float:
54
77
  return (self.x ** 2 + self.y ** 2) ** .5
55
-
78
+
79
+ @length.setter
80
+ def length(self, new_length: float) -> None:
81
+ current_length = self.length
82
+ if current_length == 0:
83
+ self.x = new_length
84
+ self.y = 0
85
+ else:
86
+ self.x *= new_length / current_length
87
+ self.y *= new_length / current_length
88
+
56
89
  @property
57
90
  def length_sqrd(self) -> float:
58
91
  return self.x ** 2 + self.y ** 2
59
-
92
+
93
+ @length_sqrd.setter
94
+ def length_sqrd(self, new_length_sqrd: float) -> None:
95
+ current_length = self.length
96
+ if current_length == 0:
97
+ self.x = _mt.sqrt(new_length_sqrd)
98
+ self.y = 0
99
+ else:
100
+ self.x *= _mt.sqrt(new_length_sqrd) / current_length
101
+ self.y *= _mt.sqrt(new_length_sqrd) / current_length
102
+
60
103
  @property
61
104
  def inverse(self) -> "Vector2D":
62
105
  return self.mult(-1)
@@ -111,6 +154,13 @@ class Vector2D:
111
154
  def complex_to_cartesian(cls, complex_n) -> "Vector2D":
112
155
  return cls(complex_n.real, complex_n.imag)
113
156
 
157
+ def cartesian_to_linear(self, size) -> int:
158
+ return int(self.x + self.y * size)
159
+
160
+ @classmethod
161
+ def linear_to_cartesian(cls, linear, size) -> "Vector2D":
162
+ return cls(linear % size, linear // size)
163
+
114
164
  def lerp(self, other, t=.1) -> "Vector2D":
115
165
  return Vector2D(self.x + (other.x - self.x) * t, self.y + (other.y - self.y) * t)
116
166
 
@@ -420,47 +470,47 @@ class Vector2D:
420
470
  def down_left_norm(cls) -> "Vector2D": return V2down_left_norm
421
471
 
422
472
  @classmethod
423
- def new_zero(cls) -> "Vector2D": return V2zero.copy
473
+ def new_zero(cls) -> "Vector2D": return V2zero.copy()
424
474
  @classmethod
425
- def new_one(cls) -> "Vector2D": return V2one.copy
475
+ def new_one(cls) -> "Vector2D": return V2one.copy()
426
476
  @classmethod
427
- def new_two(cls) -> "Vector2D": return V2two.copy
477
+ def new_two(cls) -> "Vector2D": return V2two.copy()
428
478
  @classmethod
429
- def new_pi(cls) -> "Vector2D": return V2pi.copy
479
+ def new_pi(cls) -> "Vector2D": return V2pi.copy()
430
480
  @classmethod
431
- def new_inf(cls) -> "Vector2D": return V2inf.copy
481
+ def new_inf(cls) -> "Vector2D": return V2inf.copy()
432
482
  @classmethod
433
- def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy
483
+ def new_neg_one(cls) -> "Vector2D": return V2neg_one.copy()
434
484
  @classmethod
435
- def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy
485
+ def new_neg_two(cls) -> "Vector2D": return V2neg_two.copy()
436
486
  @classmethod
437
- def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy
487
+ def new_neg_pi(cls) -> "Vector2D": return V2neg_pi.copy()
438
488
  @classmethod
439
- def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy
489
+ def new_neg_inf(cls) -> "Vector2D": return V2neg_inf.copy()
440
490
  @classmethod
441
- def new_up(cls) -> "Vector2D": return V2up.copy
491
+ def new_up(cls) -> "Vector2D": return V2up.copy()
442
492
  @classmethod
443
- def new_right(cls) -> "Vector2D": return V2right.copy
493
+ def new_right(cls) -> "Vector2D": return V2right.copy()
444
494
  @classmethod
445
- def new_down(cls) -> "Vector2D": return V2down.copy
495
+ def new_down(cls) -> "Vector2D": return V2down.copy()
446
496
  @classmethod
447
- def new_left(cls) -> "Vector2D": return V2left.copy
497
+ def new_left(cls) -> "Vector2D": return V2left.copy()
448
498
  @classmethod
449
- def new_up_right(cls) -> "Vector2D": return V2up_right.copy
499
+ def new_up_right(cls) -> "Vector2D": return V2up_right.copy()
450
500
  @classmethod
451
- def new_down_right(cls) -> "Vector2D": return V2down_right.copy
501
+ def new_down_right(cls) -> "Vector2D": return V2down_right.copy()
452
502
  @classmethod
453
- def new_up_left(cls) -> "Vector2D": return V2up_left.copy
503
+ def new_up_left(cls) -> "Vector2D": return V2up_left.copy()
454
504
  @classmethod
455
- def new_down_left(cls) -> "Vector2D": return V2down_left.copy
505
+ def new_down_left(cls) -> "Vector2D": return V2down_left.copy()
456
506
  @classmethod
457
- def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy
507
+ def new_up_right_norm(cls) -> "Vector2D": return V2up_right_norm.copy()
458
508
  @classmethod
459
- def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy
509
+ def new_down_right_norm(cls) -> "Vector2D": return V2down_right_norm.copy()
460
510
  @classmethod
461
- def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy
511
+ def new_up_left_norm(cls) -> "Vector2D": return V2up_left_norm.copy()
462
512
  @classmethod
463
- def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy
513
+ def new_down_left_norm(cls) -> "Vector2D": return V2down_left_norm.copy()
464
514
 
465
515
 
466
516
  V2 = Vector2D
@@ -487,10 +537,10 @@ V2down_right = Vector2D(1, -1)
487
537
  V2up_left = Vector2D(-1, 1)
488
538
  V2down_left = Vector2D(-1, -1)
489
539
 
490
- V2up_right_norm = V2up_right.normalize
491
- V2down_right_norm = V2down_right.normalize
492
- V2up_left_norm = V2up_left.normalize
493
- V2down_left_norm = V2down_left.normalize
540
+ V2up_right_norm = V2up_right.normalized
541
+ V2down_right_norm = V2down_right.normalized
542
+ V2up_left_norm = V2up_left.normalized
543
+ V2down_left_norm = V2down_left.normalized
494
544
 
495
545
  VECTORS_4_DIRECTIONS = (V2right, V2down, V2left, V2up)
496
546
  VECTORS_4_SEMIDIRECTIONS = (V2down_right, V2down_left, V2up_left, V2up_right)
@@ -12,6 +12,7 @@ PI_DOUBLE : float
12
12
  #
13
13
 
14
14
  sign : Callable[[int|float], Literal[-1,0,1]]
15
+ clamp: Callable[[int|float, int|float, int|float], int|float]
15
16
 
16
17
  class Vector2D:
17
18
  round_values_on_print : int|float
@@ -140,12 +141,60 @@ class Vector2D:
140
141
  ...
141
142
 
142
143
  @property
143
- def angle(self:"Vector2D") -> int|float: ...
144
+ def angle(self:"Vector2D") -> int|float:
145
+ """
146
+ # Vector Angle
147
+ ## Returns:
148
+ float: The angle (in radians) of the vector from the positive x-axis.
149
+ ## Example:
150
+ v = Vector2D(1, 1)
151
+ angle = v.angle
152
+ print(angle) # Output: 0.7853981633974483
153
+ ## Explanation:
154
+ This property calculates the angle of the vector from the positive x-axis using the `atan2` function.
155
+ """
156
+ ...
144
157
 
145
158
  @angle.setter
146
- def angle(self:"Vector2D", argv) -> None: ...
159
+ def angle(self:"Vector2D", argv) -> None:
160
+ ...
161
+
162
+ @property
163
+ def aspect_x(self: "Vector2D") -> float:
164
+ """
165
+ # Aspect Ratio (X over Y)
166
+ ## Returns:
167
+ float: The aspect ratio of the vector, calculated as the x component divided by the y component. Returns 0 if the y component is zero.
168
+ ## Example:
169
+ v = Vector2D(4, 2)
170
+ aspect = v.aspect_x
171
+ print(aspect) # Output: 2.0
172
+ ## Explanation:
173
+ This property computes the ratio of the x component to the y component of the vector. If the y component is zero, it returns 0 to avoid division by zero.
174
+ """
175
+ ...
176
+
177
+ @aspect_x.setter
178
+ def aspect_x(self: "Vector2D", new_aspect) -> None: ...
147
179
 
148
180
  @property
181
+ def aspect_y(self: "Vector2D") -> float:
182
+ """
183
+ # Aspect Ratio (Y over X)
184
+ ## Returns:
185
+ float: The aspect ratio of the vector, calculated as the y component divided by the x component. Returns 0 if the x component is zero.
186
+ ## Example:
187
+ v = Vector2D(2, 4)
188
+ aspect = v.aspect_y
189
+ print(aspect) # Output: 2.0
190
+ ## Explanation:
191
+ This property computes the ratio of the y component to the x component of the vector. If the x component is zero, it returns 0 to avoid division by zero.
192
+ """
193
+ ...
194
+
195
+ @aspect_y.setter
196
+ def aspect_y(self: "Vector2D", new_aspect) -> None: ...
197
+
149
198
  def copy(self:"Vector2D") -> "Vector2D":
150
199
  """
151
200
  # Create a copy of the current Vector2D other.
@@ -211,8 +260,56 @@ class Vector2D:
211
260
  """
212
261
  ...
213
262
 
263
+ def clamp(self, min_val: Vector2D, max_val: Vector2D) -> "Vector2D":
264
+ """
265
+ # Clamp the vector's components between the corresponding components of two other vectors.
266
+
267
+ ## Parameters:
268
+ min_val (Vector2D): The minimum vector for clamping.
269
+ max_val (Vector2D): The maximum vector for clamping.
270
+
271
+ ## Returns:
272
+ Vector2D: A new vector with its components clamped between the corresponding components of `min_val` and `max_val`.
273
+
274
+ ## Example:
275
+ v = Vector2D(5, 10)
276
+ min_val = Vector2D(0, 8)
277
+ max_val = Vector2D(6, 12)
278
+ clamped_v = v.clamp(min_val, max_val)
279
+ print(clamped_v) # Output: (5, 10)
280
+
281
+ ## Explanation:
282
+ This method clamps the x and y components of the current vector between the corresponding x and y components
283
+ of the `min_val` and `max_val` vectors. The resulting vector is returned as a new Vector2D instance.
284
+ """
285
+ ...
286
+
287
+ def iclamp(self, min_val: Vector2D, max_val: Vector2D) -> None:
288
+ """
289
+ # Clamp the vector's components in place between the corresponding components of two other vectors.
290
+
291
+ ## Parameters:
292
+ min_val (Vector2D): The minimum vector for clamping.
293
+ max_val (Vector2D): The maximum vector for clamping.
294
+
295
+ ## Returns:
296
+ None
297
+
298
+ ## Example:
299
+ v = Vector2D(5, 10)
300
+ min_val = Vector2D(0, 8)
301
+ max_val = Vector2D(6, 12)
302
+ v.iclamp(min_val, max_val)
303
+ print(v) # Output: (5, 10)
304
+
305
+ ## Explanation:
306
+ This method clamps the x and y components of the current vector in place between the corresponding x and y components
307
+ of the `min_val` and `max_val` vectors. The method modifies the current vector directly.
308
+ """
309
+ ...
310
+
214
311
  @property
215
- def normalize(self:"Vector2D") -> "Vector2D":
312
+ def normalized(self:"Vector2D") -> "Vector2D":
216
313
  """
217
314
  # Vector Normalization
218
315
 
@@ -224,7 +321,7 @@ class Vector2D:
224
321
 
225
322
  ## Example:
226
323
  v = Vector2D(3, 4)
227
- normalized_v = v.normalize() # Normalize the vector (3, 4)
324
+ normalized_v = v.normalized() # Normalize the vector (3, 4)
228
325
  print(normalized_v) # Output: (0.6, 0.8)
229
326
 
230
327
  ## Explanation:
@@ -244,23 +341,123 @@ class Vector2D:
244
341
 
245
342
  @property
246
343
  def length(self:"Vector2D") -> float:
344
+ """
345
+ # Vector Length
346
+
347
+ ## Returns:
348
+ float: The length (magnitude) of the vector.
349
+
350
+ ## Example:
351
+ v = Vector2D(3, 4)
352
+ length = v.length
353
+ print(length) # Output: 5.0
354
+
355
+ ## Explanation:
356
+ This property calculates the length (magnitude) of the vector using the Pythagorean theorem.
357
+ """
247
358
  ...
248
359
 
249
360
  @property
250
361
  def length_sqrd(self:"Vector2D") -> float:
362
+ """
363
+ # Vector Length Squared
364
+
365
+ ## Returns:
366
+ float: The squared length (magnitude) of the vector.
367
+
368
+ ## Example:
369
+ v = Vector2D(3, 4)
370
+ length_sqrd = v.length_sqrd
371
+ print(length_sqrd) # Output: 25
372
+
373
+ ## Explanation:
374
+ This property calculates the squared length (magnitude) of the vector.
375
+ It is more efficient than calculating the actual length, as it avoids the
376
+ square root calculation.
377
+ """
251
378
  ...
252
379
 
253
380
  @property
254
381
  def inverse(self:"Vector2D") -> "Vector2D":
382
+ """
383
+ # Vector Inversion
384
+
385
+ ## Returns:
386
+ Vector2D: A new vector with inverted components.
387
+
388
+ ## Example:
389
+ v = Vector2D(3, 4)
390
+ inverted_v = v.inverse()
391
+ print(inverted_v) # Output: (-3, -4)
392
+
393
+ ## Explanation:
394
+ This method calculates the inverted version of the current vector, which means a new vector with the same magnitude
395
+ but opposite direction.
396
+
397
+ The inverted vector is obtained by negating each component of the current vector.
398
+
399
+ The resulting inverted vector is returned.
400
+ """
255
401
  ...
256
402
 
257
403
  def floor(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
404
+ """
405
+ # Round the Vector2D components down to the nearest integer or specified decimal place.
406
+
407
+ ## Parameters:
408
+ n (int|float|Vector2D, optional): The decimal place to round down to. Default is 1.
409
+
410
+ ## Returns:
411
+ Vector2D: A new vector with rounded-down components.
412
+
413
+ ## Example:
414
+ v = Vector2D(3.14159, 2.71828)
415
+ rounded_v = v.floor(2)
416
+ print(rounded_v) # Output: (3.14, 2.71)
417
+
418
+ ## Explanation:
419
+ This method rounds each component of the vector down to the nearest integer or specified decimal place.
420
+ """
258
421
  ...
259
422
 
260
423
  def ceil(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
424
+ """
425
+ # Round the Vector2D components up to the nearest integer or specified decimal place.
426
+
427
+ ## Parameters:
428
+ n (int|float|Vector2D, optional): The decimal place to round up to. Default is 1.
429
+
430
+ ## Returns:
431
+ Vector2D: A new vector with rounded-up components.
432
+
433
+ ## Example:
434
+ v = Vector2D(3.14159, 2.71828)
435
+ rounded_v = v.ceil(2)
436
+ print(rounded_v) # Output: (3.15, 2.72)
437
+
438
+ ## Explanation:
439
+ This method rounds each component of the vector up to the nearest integer or specified decimal place.
440
+ """
261
441
  ...
262
442
 
263
443
  def round(self:"Vector2D", n:"int|float|Vector2D"=1) -> "Vector2D":
444
+ """
445
+ # Round the Vector2D components to the nearest integer or specified decimal place.
446
+
447
+ ## Parameters:
448
+ n (int|float|Vector2D, optional): The decimal place to round to. Default is 1.
449
+
450
+ ## Returns:
451
+ Vector2D: A new vector with rounded components.
452
+
453
+ ## Example:
454
+ v = Vector2D(3.14159, 2.71828)
455
+ rounded_v = v.round(2)
456
+ print(rounded_v) # Output: (3.14, 2.72)
457
+
458
+ ## Explanation:
459
+ This method rounds each component of the vector to the nearest integer or specified decimal place.
460
+ """
264
461
  ...
265
462
 
266
463
  @classmethod
@@ -451,7 +648,65 @@ class Vector2D:
451
648
  def cartesian_to_complex(self:"Vector2D") -> complex: ...
452
649
 
453
650
  @classmethod
454
- def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D": ...
651
+ def complex_to_cartesian(cls, complex_n: complex) -> "Vector2D":
652
+ """
653
+ # Convert a Complex Number to Cartesian Coordinates
654
+
655
+ ## Parameters:
656
+ complex_n (complex): The complex number to convert.
657
+
658
+ ## Returns:
659
+ Vector2D: The corresponding (x, y) coordinates as a Vector2D.
660
+
661
+ ## Example:
662
+ v = Vector2D.complex_to_cartesian(3 + 4j) # v = Vector2D(3, 4)
663
+
664
+ ## Explanation:
665
+ This class method converts a complex number (a + bi) to Cartesian coordinates (x, y).
666
+ The real part 'a' becomes the x-coordinate, and the imaginary part 'b' becomes the y-coordinate.
667
+ """
668
+ ...
669
+
670
+ def cartesian_to_linear(self:"Vector2D", size:int|float) -> int:
671
+ """
672
+ # Convert 2D Cartesian coordinates to a linear index.
673
+
674
+ ## Parameters:
675
+ size (int|float): The width of the grid (number of columns).
676
+
677
+ ## Returns:
678
+ int: The linear index corresponding to the (x, y) coordinates.
679
+
680
+ ## Example:
681
+ v = Vector2D(3, 2)
682
+ idx = v.cartesian_to_linear(5) # idx = 3 + 2*5 = 13
683
+
684
+ ## Explanation:
685
+ This method converts the (x, y) coordinates of the vector to a single linear index,
686
+ assuming row-major order in a 2D grid of the given width.
687
+ """
688
+ ...
689
+
690
+ @classmethod
691
+ def linear_to_cartesian(cls, linear:int, size:int|float) -> "Vector2D":
692
+ """
693
+ # Convert a linear index to 2D Cartesian coordinates.
694
+
695
+ ## Parameters:
696
+ linear (int): The linear index.
697
+ size (int|float): The width of the grid (number of columns).
698
+
699
+ ## Returns:
700
+ Vector2D: The corresponding (x, y) coordinates as a Vector2D.
701
+
702
+ ## Example:
703
+ v = Vector2D.linear_to_cartesian(13, 5) # v = Vector2D(3, 2)
704
+
705
+ ## Explanation:
706
+ This class method converts a linear index to (x, y) coordinates,
707
+ assuming row-major order in a 2D grid of the given width.
708
+ """
709
+ ...
455
710
 
456
711
  def lerp(self:"Vector2D", other:"int|float|Vector2D|list|tuple", t:float=.1) -> "Vector2D":
457
712
  """
@@ -1046,4 +1301,37 @@ def distance_line_point(line_point_a:Vector2D, line_point_b:Vector2D, point_c:Ve
1046
1301
  ...
1047
1302
 
1048
1303
  def optimize_value_string(value: int|float, precision: int) -> str:
1304
+ """
1305
+ # Optimize the string representation of a numeric value based on its magnitude and the specified precision.
1306
+
1307
+ ## Parameters:
1308
+ value (int | float): The numeric value to be formatted.
1309
+ precision (int): The number of decimal places or significant digits to use in the formatted string.
1310
+
1311
+ ## Returns:
1312
+ str: The optimized string representation of the value.
1313
+
1314
+ ## Example:
1315
+ value = 0.000012345
1316
+ precision = 3
1317
+ result = optimize_value_string(value, precision)
1318
+ print(result)
1319
+ # Output: '1.235e-05'
1320
+
1321
+ value = 123.456789
1322
+ precision = 2
1323
+ result = optimize_value_string(value, precision)
1324
+ print(result)
1325
+ # Output: '123.46'
1326
+
1327
+ ## Explanation:
1328
+ The function formats the input value as a string, choosing between fixed-point and scientific notation
1329
+ based on the magnitude of the value relative to the specified precision:
1330
+ - If the absolute value is very small (less than 1 divided by 10 to the power of precision, but not zero),
1331
+ scientific notation is used.
1332
+ - If the absolute value is less than 10 to the power of precision, fixed-point notation is used,
1333
+ with trailing zeros and decimal points removed for brevity.
1334
+ - Otherwise, scientific notation is used.
1335
+ This ensures that the string representation is concise and readable, while maintaining the requested precision.
1336
+ """
1049
1337
  ...