manim 0.17.3__py3-none-any.whl → 0.18.0.post0__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.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (84) hide show
  1. manim/__init__.py +1 -0
  2. manim/__main__.py +2 -0
  3. manim/_config/__init__.py +0 -1
  4. manim/_config/logger_utils.py +1 -0
  5. manim/_config/utils.py +14 -5
  6. manim/animation/changing.py +9 -5
  7. manim/animation/creation.py +8 -3
  8. manim/animation/indication.py +4 -4
  9. manim/animation/speedmodifier.py +2 -4
  10. manim/animation/updaters/mobject_update_utils.py +134 -16
  11. manim/camera/camera.py +31 -17
  12. manim/cli/checkhealth/__init__.py +0 -0
  13. manim/cli/checkhealth/checks.py +173 -0
  14. manim/cli/checkhealth/commands.py +81 -0
  15. manim/cli/render/global_options.py +6 -0
  16. manim/constants.py +58 -54
  17. manim/mobject/geometry/__init__.py +1 -0
  18. manim/mobject/geometry/arc.py +126 -91
  19. manim/mobject/geometry/boolean_ops.py +6 -10
  20. manim/mobject/geometry/labeled.py +155 -0
  21. manim/mobject/geometry/line.py +66 -50
  22. manim/mobject/geometry/polygram.py +23 -15
  23. manim/mobject/geometry/shape_matchers.py +24 -15
  24. manim/mobject/geometry/tips.py +62 -40
  25. manim/mobject/graph.py +3 -4
  26. manim/mobject/graphing/coordinate_systems.py +190 -139
  27. manim/mobject/graphing/number_line.py +5 -2
  28. manim/mobject/graphing/probability.py +4 -3
  29. manim/mobject/graphing/scale.py +7 -7
  30. manim/mobject/logo.py +108 -22
  31. manim/mobject/matrix.py +33 -37
  32. manim/mobject/mobject.py +327 -260
  33. manim/mobject/opengl/opengl_image_mobject.py +1 -1
  34. manim/mobject/opengl/opengl_mobject.py +18 -12
  35. manim/mobject/opengl/opengl_point_cloud_mobject.py +1 -1
  36. manim/mobject/opengl/opengl_surface.py +1 -1
  37. manim/mobject/opengl/opengl_vectorized_mobject.py +21 -17
  38. manim/mobject/svg/brace.py +3 -1
  39. manim/mobject/svg/svg_mobject.py +9 -11
  40. manim/mobject/table.py +50 -54
  41. manim/mobject/text/numbers.py +48 -6
  42. manim/mobject/text/tex_mobject.py +8 -12
  43. manim/mobject/text/text_mobject.py +32 -24
  44. manim/mobject/three_d/three_d_utils.py +13 -8
  45. manim/mobject/three_d/three_dimensions.py +61 -43
  46. manim/mobject/types/image_mobject.py +5 -4
  47. manim/mobject/types/point_cloud_mobject.py +8 -6
  48. manim/mobject/types/vectorized_mobject.py +385 -258
  49. manim/mobject/vector_field.py +19 -11
  50. manim/plugins/import_plugins.py +1 -1
  51. manim/plugins/plugins_flags.py +1 -6
  52. manim/renderer/shader.py +2 -2
  53. manim/scene/scene.py +15 -7
  54. manim/scene/scene_file_writer.py +1 -2
  55. manim/scene/three_d_scene.py +1 -1
  56. manim/scene/vector_space_scene.py +17 -7
  57. manim/typing.py +133 -0
  58. manim/utils/bezier.py +267 -83
  59. manim/utils/color/AS2700.py +234 -0
  60. manim/utils/color/BS381.py +315 -0
  61. manim/utils/color/X11.py +530 -0
  62. manim/utils/color/XKCD.py +949 -0
  63. manim/utils/color/__init__.py +58 -0
  64. manim/utils/color/core.py +1036 -0
  65. manim/utils/color/manim_colors.py +220 -0
  66. manim/utils/docbuild/autocolor_directive.py +92 -0
  67. manim/utils/docbuild/manim_directive.py +40 -6
  68. manim/utils/file_ops.py +1 -1
  69. manim/utils/hashing.py +1 -1
  70. manim/utils/iterables.py +1 -1
  71. manim/utils/rate_functions.py +33 -0
  72. manim/utils/simple_functions.py +0 -18
  73. manim/utils/space_ops.py +55 -42
  74. manim/utils/testing/frames_comparison.py +9 -0
  75. manim/utils/tex.py +2 -0
  76. manim/utils/tex_file_writing.py +29 -2
  77. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/METADATA +14 -14
  78. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/RECORD +82 -71
  79. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/WHEEL +1 -1
  80. manim/communitycolors.py +0 -9
  81. manim/utils/color.py +0 -552
  82. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/LICENSE +0 -0
  83. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/LICENSE.community +0 -0
  84. {manim-0.17.3.dist-info → manim-0.18.0.post0.dist-info}/entry_points.txt +0 -0
manim/utils/bezier.py CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from manim.typing import (
6
+ BezierPoints,
7
+ ColVector,
8
+ MatrixMN,
9
+ Point3D,
10
+ Point3D_Array,
11
+ PointDType,
12
+ QuadraticBezierPoints,
13
+ QuadraticBezierPoints_Array,
14
+ )
15
+
5
16
  __all__ = [
6
17
  "bezier",
7
18
  "partial_bezier_points",
@@ -20,11 +31,11 @@ __all__ = [
20
31
  ]
21
32
 
22
33
 
23
- import typing
24
34
  from functools import reduce
25
- from typing import Iterable
35
+ from typing import Any, Callable, Sequence, overload
26
36
 
27
37
  import numpy as np
38
+ import numpy.typing as npt
28
39
  from scipy import linalg
29
40
 
30
41
  from ..utils.simple_functions import choose
@@ -32,8 +43,8 @@ from ..utils.space_ops import cross2d, find_intersection
32
43
 
33
44
 
34
45
  def bezier(
35
- points: np.ndarray,
36
- ) -> typing.Callable[[float], int | typing.Iterable]:
46
+ points: Sequence[Point3D] | Point3D_Array,
47
+ ) -> Callable[[float], Point3D]:
37
48
  """Classic implementation of a bezier curve.
38
49
 
39
50
  Parameters
@@ -43,34 +54,39 @@ def bezier(
43
54
 
44
55
  Returns
45
56
  -------
46
- typing.Callable[[float], typing.Union[int, typing.Iterable]]
47
57
  function describing the bezier curve.
58
+ You can pass a t value between 0 and 1 to get the corresponding point on the curve.
48
59
  """
49
60
  n = len(points) - 1
50
-
51
61
  # Cubic Bezier curve
52
62
  if n == 3:
53
- return (
54
- lambda t: (1 - t) ** 3 * points[0]
63
+ return lambda t: np.asarray(
64
+ (1 - t) ** 3 * points[0]
55
65
  + 3 * t * (1 - t) ** 2 * points[1]
56
66
  + 3 * (1 - t) * t**2 * points[2]
57
- + t**3 * points[3]
67
+ + t**3 * points[3],
68
+ dtype=PointDType,
58
69
  )
59
70
  # Quadratic Bezier curve
60
71
  if n == 2:
61
- return (
62
- lambda t: (1 - t) ** 2 * points[0]
63
- + 2 * t * (1 - t) * points[1]
64
- + t**2 * points[2]
72
+ return lambda t: np.asarray(
73
+ (1 - t) ** 2 * points[0] + 2 * t * (1 - t) * points[1] + t**2 * points[2],
74
+ dtype=PointDType,
65
75
  )
66
76
 
67
- return lambda t: sum(
68
- ((1 - t) ** (n - k)) * (t**k) * choose(n, k) * point
69
- for k, point in enumerate(points)
77
+ return lambda t: np.asarray(
78
+ np.asarray(
79
+ [
80
+ (((1 - t) ** (n - k)) * (t**k) * choose(n, k) * point)
81
+ for k, point in enumerate(points)
82
+ ],
83
+ dtype=PointDType,
84
+ ).sum(axis=0)
70
85
  )
71
86
 
72
87
 
73
- def partial_bezier_points(points: np.ndarray, a: float, b: float) -> np.ndarray:
88
+ # !TODO: This function has still a weird implementation with the overlapping points
89
+ def partial_bezier_points(points: BezierPoints, a: float, b: float) -> BezierPoints:
74
90
  """Given an array of points which define bezier curve, and two numbers 0<=a<b<=1, return an array of the same size,
75
91
  which describes the portion of the original bezier curve on the interval [a, b].
76
92
 
@@ -90,23 +106,31 @@ def partial_bezier_points(points: np.ndarray, a: float, b: float) -> np.ndarray:
90
106
  np.ndarray
91
107
  Set of points defining the partial bezier curve.
92
108
  """
109
+ _len = len(points)
93
110
  if a == 1:
94
- return [points[-1]] * len(points)
111
+ return np.asarray([points[-1]] * _len, dtype=PointDType)
95
112
 
96
- a_to_1 = np.array([bezier(points[i:])(a) for i in range(len(points))])
113
+ a_to_1 = np.asarray(
114
+ [bezier(points[i:])(a) for i in range(_len)],
115
+ dtype=PointDType,
116
+ )
97
117
  end_prop = (b - a) / (1.0 - a)
98
- return np.array([bezier(a_to_1[: i + 1])(end_prop) for i in range(len(points))])
118
+ return np.asarray(
119
+ [bezier(a_to_1[: i + 1])(end_prop) for i in range(_len)],
120
+ dtype=PointDType,
121
+ )
99
122
 
100
123
 
101
124
  # Shortened version of partial_bezier_points just for quadratics,
102
125
  # since this is called a fair amount
103
- def partial_quadratic_bezier_points(points, a, b):
104
- points = np.asarray(points, dtype=np.float64)
126
+ def partial_quadratic_bezier_points(
127
+ points: QuadraticBezierPoints, a: float, b: float
128
+ ) -> QuadraticBezierPoints:
105
129
  if a == 1:
106
- return 3 * [points[-1]]
130
+ return np.asarray(3 * [points[-1]])
107
131
 
108
- def curve(t):
109
- return (
132
+ def curve(t: float) -> Point3D:
133
+ return np.asarray(
110
134
  points[0] * (1 - t) * (1 - t)
111
135
  + 2 * points[1] * t * (1 - t)
112
136
  + points[2] * t * t
@@ -118,10 +142,10 @@ def partial_quadratic_bezier_points(points, a, b):
118
142
  h1_prime = (1 - a) * points[1] + a * points[2]
119
143
  end_prop = (b - a) / (1.0 - a)
120
144
  h1 = (1 - end_prop) * h0 + end_prop * h1_prime
121
- return [h0, h1, h2]
145
+ return np.asarray((h0, h1, h2))
122
146
 
123
147
 
124
- def split_quadratic_bezier(points: np.ndarray, t: float) -> np.ndarray:
148
+ def split_quadratic_bezier(points: QuadraticBezierPoints, t: float) -> BezierPoints:
125
149
  """Split a quadratic Bézier curve at argument ``t`` into two quadratic curves.
126
150
 
127
151
  Parameters
@@ -143,10 +167,10 @@ def split_quadratic_bezier(points: np.ndarray, t: float) -> np.ndarray:
143
167
  s2 = interpolate(h1, a2, t)
144
168
  p = interpolate(s1, s2, t)
145
169
 
146
- return np.array([a1, s1, p, p, s2, a2])
170
+ return np.array((a1, s1, p, p, s2, a2))
147
171
 
148
172
 
149
- def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray:
173
+ def subdivide_quadratic_bezier(points: QuadraticBezierPoints, n: int) -> BezierPoints:
150
174
  """Subdivide a quadratic Bézier curve into ``n`` subcurves which have the same shape.
151
175
 
152
176
  The points at which the curve is split are located at the
@@ -178,8 +202,8 @@ def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray:
178
202
 
179
203
 
180
204
  def quadratic_bezier_remap(
181
- triplets: Iterable[Iterable[float]], new_number_of_curves: int
182
- ):
205
+ triplets: QuadraticBezierPoints_Array, new_number_of_curves: int
206
+ ) -> QuadraticBezierPoints_Array:
183
207
  """Remaps the number of curves to a higher amount by splitting bezier curves
184
208
 
185
209
  Parameters
@@ -234,7 +258,21 @@ def quadratic_bezier_remap(
234
258
 
235
259
 
236
260
  # Linear interpolation variants
237
- def interpolate(start: np.ndarray, end: np.ndarray, alpha: float) -> np.ndarray:
261
+
262
+
263
+ @overload
264
+ def interpolate(start: float, end: float, alpha: float) -> float:
265
+ ...
266
+
267
+
268
+ @overload
269
+ def interpolate(start: Point3D, end: Point3D, alpha: float) -> Point3D:
270
+ ...
271
+
272
+
273
+ def interpolate(
274
+ start: int | float | Point3D, end: int | float | Point3D, alpha: float | Point3D
275
+ ) -> float | Point3D:
238
276
  return (1 - alpha) * start + alpha * end
239
277
 
240
278
 
@@ -244,52 +282,192 @@ def integer_interpolate(
244
282
  alpha: float,
245
283
  ) -> tuple[int, float]:
246
284
  """
247
- Alpha is a float between 0 and 1. This returns
248
- an integer between start and end (inclusive) representing
249
- appropriate interpolation between them, along with a
250
- "residue" representing a new proportion between the
251
- returned integer and the next one of the
252
- list.
253
-
254
- For example, if start=0, end=10, alpha=0.46, This
255
- would return (4, 0.6).
285
+ This is a variant of interpolate that returns an integer and the residual
286
+
287
+ Parameters
288
+ ----------
289
+ start
290
+ The start of the range
291
+ end
292
+ The end of the range
293
+ alpha
294
+ a float between 0 and 1.
295
+
296
+ Returns
297
+ -------
298
+ tuple[int, float]
299
+ This returns an integer between start and end (inclusive) representing
300
+ appropriate interpolation between them, along with a
301
+ "residue" representing a new proportion between the
302
+ returned integer and the next one of the
303
+ list.
304
+
305
+ Example
306
+ -------
307
+
308
+ .. code-block:: pycon
309
+
310
+ >>> integer, residue = integer_interpolate(start=0, end=10, alpha=0.46)
311
+ >>> np.allclose((integer, residue), (4, 0.6))
312
+ True
256
313
  """
257
314
  if alpha >= 1:
258
- return (end - 1, 1.0)
315
+ return (int(end - 1), 1.0)
259
316
  if alpha <= 0:
260
- return (start, 0)
317
+ return (int(start), 0)
261
318
  value = int(interpolate(start, end, alpha))
262
319
  residue = ((end - start) * alpha) % 1
263
320
  return (value, residue)
264
321
 
265
322
 
323
+ @overload
266
324
  def mid(start: float, end: float) -> float:
325
+ ...
326
+
327
+
328
+ @overload
329
+ def mid(start: Point3D, end: Point3D) -> Point3D:
330
+ ...
331
+
332
+
333
+ def mid(start: float | Point3D, end: float | Point3D) -> float | Point3D:
334
+ """Returns the midpoint between two values.
335
+
336
+ Parameters
337
+ ----------
338
+ start
339
+ The first value
340
+ end
341
+ The second value
342
+
343
+ Returns
344
+ -------
345
+ The midpoint between the two values
346
+ """
267
347
  return (start + end) / 2.0
268
348
 
269
349
 
270
- def inverse_interpolate(start: float, end: float, value: float) -> np.ndarray:
350
+ @overload
351
+ def inverse_interpolate(start: float, end: float, value: float) -> float:
352
+ ...
353
+
354
+
355
+ @overload
356
+ def inverse_interpolate(start: float, end: float, value: Point3D) -> Point3D:
357
+ ...
358
+
359
+
360
+ @overload
361
+ def inverse_interpolate(start: Point3D, end: Point3D, value: Point3D) -> Point3D:
362
+ ...
363
+
364
+
365
+ def inverse_interpolate(
366
+ start: float | Point3D, end: float | Point3D, value: float | Point3D
367
+ ) -> float | Point3D:
368
+ """Perform inverse interpolation to determine the alpha
369
+ values that would produce the specified ``value``
370
+ given the ``start`` and ``end`` values or points.
371
+
372
+ Parameters
373
+ ----------
374
+ start
375
+ The start value or point of the interpolation.
376
+ end
377
+ The end value or point of the interpolation.
378
+ value
379
+ The value or point for which the alpha value
380
+ should be determined.
381
+
382
+ Returns
383
+ -------
384
+ The alpha values producing the given input
385
+ when interpolating between ``start`` and ``end``.
386
+
387
+ Example
388
+ -------
389
+
390
+ .. code-block:: pycon
391
+
392
+ >>> inverse_interpolate(start=2, end=6, value=4)
393
+ 0.5
394
+
395
+ >>> start = np.array([1, 2, 1])
396
+ >>> end = np.array([7, 8, 11])
397
+ >>> value = np.array([4, 5, 5])
398
+ >>> inverse_interpolate(start, end, value)
399
+ array([0.5, 0.5, 0.4])
400
+ """
271
401
  return np.true_divide(value - start, end - start)
272
402
 
273
403
 
404
+ @overload
274
405
  def match_interpolate(
275
406
  new_start: float,
276
407
  new_end: float,
277
408
  old_start: float,
278
409
  old_end: float,
279
410
  old_value: float,
280
- ) -> np.ndarray:
411
+ ) -> float:
412
+ ...
413
+
414
+
415
+ @overload
416
+ def match_interpolate(
417
+ new_start: float,
418
+ new_end: float,
419
+ old_start: float,
420
+ old_end: float,
421
+ old_value: Point3D,
422
+ ) -> Point3D:
423
+ ...
424
+
425
+
426
+ def match_interpolate(
427
+ new_start: float,
428
+ new_end: float,
429
+ old_start: float,
430
+ old_end: float,
431
+ old_value: float | Point3D,
432
+ ) -> float | Point3D:
433
+ """Interpolate a value from an old range to a new range.
434
+
435
+ Parameters
436
+ ----------
437
+ new_start
438
+ The start of the new range.
439
+ new_end
440
+ The end of the new range.
441
+ old_start
442
+ The start of the old range.
443
+ old_end
444
+ The end of the old range.
445
+ old_value
446
+ The value within the old range whose corresponding
447
+ value in the new range (with the same alpha value)
448
+ is desired.
449
+
450
+ Returns
451
+ -------
452
+ The interpolated value within the new range.
453
+
454
+ Examples
455
+ --------
456
+ >>> match_interpolate(0, 100, 10, 20, 15)
457
+ 50.0
458
+ """
459
+ old_alpha = inverse_interpolate(old_start, old_end, old_value)
281
460
  return interpolate(
282
461
  new_start,
283
462
  new_end,
284
- inverse_interpolate(old_start, old_end, old_value),
463
+ old_alpha, # type: ignore
285
464
  )
286
465
 
287
466
 
288
- # Figuring out which bezier curves most smoothly connect a sequence of points
289
-
290
-
291
- def get_smooth_cubic_bezier_handle_points(points):
292
- points = np.array(points)
467
+ def get_smooth_cubic_bezier_handle_points(
468
+ points: Point3D_Array,
469
+ ) -> tuple[BezierPoints, BezierPoints]:
470
+ points = np.asarray(points)
293
471
  num_handles = len(points) - 1
294
472
  dim = points.shape[1]
295
473
  if num_handles < 1:
@@ -301,7 +479,7 @@ def get_smooth_cubic_bezier_handle_points(points):
301
479
  # diag is a representation of the matrix in diagonal form
302
480
  # See https://www.particleincell.com/2012/bezier-splines/
303
481
  # for how to arrive at these equations
304
- diag = np.zeros((l + u + 1, 2 * num_handles))
482
+ diag: MatrixMN = np.zeros((l + u + 1, 2 * num_handles))
305
483
  diag[0, 1::2] = -1
306
484
  diag[0, 2::2] = 1
307
485
  diag[1, 0::2] = 2
@@ -314,13 +492,13 @@ def get_smooth_cubic_bezier_handle_points(points):
314
492
  # This is the b as in Ax = b, where we are solving for x,
315
493
  # and A is represented using diag. However, think of entries
316
494
  # to x and b as being points in space, not numbers
317
- b = np.zeros((2 * num_handles, dim))
495
+ b: Point3D_Array = np.zeros((2 * num_handles, dim))
318
496
  b[1::2] = 2 * points[1:]
319
497
  b[0] = points[0]
320
498
  b[-1] = points[-1]
321
499
 
322
- def solve_func(b):
323
- return linalg.solve_banded((l, u), diag, b)
500
+ def solve_func(b: ColVector) -> ColVector | MatrixMN:
501
+ return linalg.solve_banded((l, u), diag, b) # type: ignore
324
502
 
325
503
  use_closed_solve_function = is_closed(points)
326
504
  if use_closed_solve_function:
@@ -334,8 +512,8 @@ def get_smooth_cubic_bezier_handle_points(points):
334
512
  b[0] = 2 * points[0]
335
513
  b[-1] = np.zeros(dim)
336
514
 
337
- def closed_curve_solve_func(b):
338
- return linalg.solve(matrix, b)
515
+ def closed_curve_solve_func(b: ColVector) -> ColVector | MatrixMN:
516
+ return linalg.solve(matrix, b) # type: ignore
339
517
 
340
518
  handle_pairs = np.zeros((2 * num_handles, dim))
341
519
  for i in range(dim):
@@ -347,8 +525,8 @@ def get_smooth_cubic_bezier_handle_points(points):
347
525
 
348
526
 
349
527
  def get_smooth_handle_points(
350
- points: np.ndarray,
351
- ) -> tuple[np.ndarray, np.ndarray]:
528
+ points: BezierPoints,
529
+ ) -> tuple[BezierPoints, BezierPoints]:
352
530
  """Given some anchors (points), compute handles so the resulting bezier curve is smooth.
353
531
 
354
532
  Parameters
@@ -362,7 +540,7 @@ def get_smooth_handle_points(
362
540
  Computed handles.
363
541
  """
364
542
  # NOTE points here are anchors.
365
- points = np.array(points)
543
+ points = np.asarray(points)
366
544
  num_handles = len(points) - 1
367
545
  dim = points.shape[1]
368
546
  if num_handles < 1:
@@ -374,7 +552,7 @@ def get_smooth_handle_points(
374
552
  # diag is a representation of the matrix in diagonal form
375
553
  # See https://www.particleincell.com/2012/bezier-splines/
376
554
  # for how to arrive at these equations
377
- diag = np.zeros((l + u + 1, 2 * num_handles))
555
+ diag: MatrixMN = np.zeros((l + u + 1, 2 * num_handles))
378
556
  diag[0, 1::2] = -1
379
557
  diag[0, 2::2] = 1
380
558
  diag[1, 0::2] = 2
@@ -392,8 +570,8 @@ def get_smooth_handle_points(
392
570
  b[0] = points[0]
393
571
  b[-1] = points[-1]
394
572
 
395
- def solve_func(b: np.ndarray) -> np.ndarray:
396
- return linalg.solve_banded((l, u), diag, b)
573
+ def solve_func(b: ColVector) -> ColVector | MatrixMN:
574
+ return linalg.solve_banded((l, u), diag, b) # type: ignore
397
575
 
398
576
  use_closed_solve_function = is_closed(points)
399
577
  if use_closed_solve_function:
@@ -407,8 +585,8 @@ def get_smooth_handle_points(
407
585
  b[0] = 2 * points[0]
408
586
  b[-1] = np.zeros(dim)
409
587
 
410
- def closed_curve_solve_func(b: np.ndarray) -> np.ndarray:
411
- return linalg.solve(matrix, b)
588
+ def closed_curve_solve_func(b: ColVector) -> ColVector | MatrixMN:
589
+ return linalg.solve(matrix, b) # type: ignore
412
590
 
413
591
  handle_pairs = np.zeros((2 * num_handles, dim))
414
592
  for i in range(dim):
@@ -419,7 +597,9 @@ def get_smooth_handle_points(
419
597
  return handle_pairs[0::2], handle_pairs[1::2]
420
598
 
421
599
 
422
- def diag_to_matrix(l_and_u: tuple[int, int], diag: np.ndarray) -> np.ndarray:
600
+ def diag_to_matrix(
601
+ l_and_u: tuple[int, int], diag: npt.NDArray[Any]
602
+ ) -> npt.NDArray[Any]:
423
603
  """
424
604
  Converts array whose rows represent diagonal
425
605
  entries of a matrix into the matrix itself.
@@ -438,7 +618,9 @@ def diag_to_matrix(l_and_u: tuple[int, int], diag: np.ndarray) -> np.ndarray:
438
618
 
439
619
  # Given 4 control points for a cubic bezier curve (or arrays of such)
440
620
  # return control points for 2 quadratics (or 2n quadratics) approximating them.
441
- def get_quadratic_approximation_of_cubic(a0, h0, h1, a1):
621
+ def get_quadratic_approximation_of_cubic(
622
+ a0: Point3D, h0: Point3D, h1: Point3D, a1: Point3D
623
+ ) -> BezierPoints:
442
624
  a0 = np.array(a0, ndmin=2)
443
625
  h0 = np.array(h0, ndmin=2)
444
626
  h1 = np.array(h1, ndmin=2)
@@ -486,9 +668,9 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1):
486
668
  m, n = a0.shape
487
669
  t_mid = t_mid.repeat(n).reshape((m, n))
488
670
 
489
- # Compute bezier point and tangent at the chosen value of t
490
- mid = bezier([a0, h0, h1, a1])(t_mid)
491
- Tm = bezier([h0 - a0, h1 - h0, a1 - h1])(t_mid)
671
+ # Compute bezier point and tangent at the chosen value of t (these are vectorized)
672
+ mid = bezier([a0, h0, h1, a1])(t_mid) # type: ignore
673
+ Tm = bezier([h0 - a0, h1 - h0, a1 - h1])(t_mid) # type: ignore
492
674
 
493
675
  # Intersection between tangent lines at end points
494
676
  # and tangent in the middle
@@ -506,15 +688,15 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1):
506
688
  return result
507
689
 
508
690
 
509
- def is_closed(points: tuple[np.ndarray, np.ndarray]) -> bool:
510
- return np.allclose(points[0], points[-1])
691
+ def is_closed(points: Point3D_Array) -> bool:
692
+ return np.allclose(points[0], points[-1]) # type: ignore
511
693
 
512
694
 
513
695
  def proportions_along_bezier_curve_for_point(
514
- point: typing.Iterable[float | int],
515
- control_points: typing.Iterable[typing.Iterable[float | int]],
516
- round_to: float | int | None = 1e-6,
517
- ) -> np.ndarray:
696
+ point: Point3D,
697
+ control_points: BezierPoints,
698
+ round_to: float = 1e-6,
699
+ ) -> npt.NDArray[Any]:
518
700
  """Obtains the proportion along the bezier curve corresponding to a given point
519
701
  given the bezier curve's control points.
520
702
 
@@ -583,21 +765,23 @@ def proportions_along_bezier_curve_for_point(
583
765
  # Roots will be none, but in this specific instance, we don't need to consider that.
584
766
  continue
585
767
  bezier_polynom = np.polynomial.Polynomial(terms[::-1])
586
- polynom_roots = bezier_polynom.roots()
768
+ polynom_roots = bezier_polynom.roots() # type: ignore
587
769
  if len(polynom_roots) > 0:
588
770
  polynom_roots = np.around(polynom_roots, int(np.log10(1 / round_to)))
589
771
  roots.append(polynom_roots)
590
772
 
591
773
  roots = [[root for root in rootlist if root.imag == 0] for rootlist in roots]
592
- roots = reduce(np.intersect1d, roots) # Get common roots.
593
- roots = np.array([r.real for r in roots if 0 <= r.real <= 1])
594
- return roots
774
+ # Get common roots
775
+ # arg-type: ignore
776
+ roots = reduce(np.intersect1d, roots) # type: ignore
777
+ result = np.asarray([r.real for r in roots if 0 <= r.real <= 1])
778
+ return result
595
779
 
596
780
 
597
781
  def point_lies_on_bezier(
598
- point: typing.Iterable[float | int],
599
- control_points: typing.Iterable[typing.Iterable[float | int]],
600
- round_to: float | int | None = 1e-6,
782
+ point: Point3D,
783
+ control_points: BezierPoints,
784
+ round_to: float = 1e-6,
601
785
  ) -> bool:
602
786
  """Checks if a given point lies on the bezier curves with the given control points.
603
787