manim 0.18.1__py3-none-any.whl → 0.19.0__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 (129) hide show
  1. manim/__main__.py +45 -12
  2. manim/_config/__init__.py +2 -2
  3. manim/_config/cli_colors.py +8 -4
  4. manim/_config/default.cfg +0 -2
  5. manim/_config/logger_utils.py +5 -0
  6. manim/_config/utils.py +29 -38
  7. manim/animation/animation.py +148 -8
  8. manim/animation/composition.py +16 -13
  9. manim/animation/creation.py +184 -8
  10. manim/animation/fading.py +5 -8
  11. manim/animation/indication.py +93 -26
  12. manim/animation/movement.py +21 -3
  13. manim/animation/rotation.py +2 -1
  14. manim/animation/specialized.py +3 -5
  15. manim/animation/speedmodifier.py +3 -3
  16. manim/animation/transform.py +4 -5
  17. manim/animation/updaters/mobject_update_utils.py +17 -14
  18. manim/camera/camera.py +2 -2
  19. manim/cli/__init__.py +17 -0
  20. manim/cli/cfg/group.py +52 -36
  21. manim/cli/checkhealth/checks.py +92 -76
  22. manim/cli/checkhealth/commands.py +12 -5
  23. manim/cli/default_group.py +148 -24
  24. manim/cli/init/commands.py +28 -23
  25. manim/cli/plugins/commands.py +13 -3
  26. manim/cli/render/commands.py +47 -42
  27. manim/cli/render/global_options.py +43 -9
  28. manim/cli/render/render_options.py +84 -19
  29. manim/constants.py +11 -4
  30. manim/mobject/frame.py +0 -1
  31. manim/mobject/geometry/arc.py +109 -75
  32. manim/mobject/geometry/boolean_ops.py +20 -17
  33. manim/mobject/geometry/labeled.py +300 -77
  34. manim/mobject/geometry/line.py +120 -60
  35. manim/mobject/geometry/polygram.py +109 -25
  36. manim/mobject/geometry/shape_matchers.py +35 -15
  37. manim/mobject/geometry/tips.py +36 -27
  38. manim/mobject/graph.py +48 -40
  39. manim/mobject/graphing/coordinate_systems.py +110 -45
  40. manim/mobject/graphing/functions.py +16 -10
  41. manim/mobject/graphing/number_line.py +23 -9
  42. manim/mobject/graphing/probability.py +2 -10
  43. manim/mobject/graphing/scale.py +6 -5
  44. manim/mobject/matrix.py +17 -19
  45. manim/mobject/mobject.py +149 -103
  46. manim/mobject/opengl/opengl_geometry.py +4 -8
  47. manim/mobject/opengl/opengl_mobject.py +506 -343
  48. manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
  49. manim/mobject/opengl/opengl_surface.py +1 -2
  50. manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
  51. manim/mobject/svg/brace.py +61 -13
  52. manim/mobject/svg/svg_mobject.py +2 -1
  53. manim/mobject/table.py +11 -12
  54. manim/mobject/text/code_mobject.py +186 -550
  55. manim/mobject/text/numbers.py +7 -7
  56. manim/mobject/text/tex_mobject.py +22 -13
  57. manim/mobject/text/text_mobject.py +29 -20
  58. manim/mobject/three_d/polyhedra.py +98 -1
  59. manim/mobject/three_d/three_dimensions.py +59 -31
  60. manim/mobject/types/image_mobject.py +37 -23
  61. manim/mobject/types/point_cloud_mobject.py +103 -67
  62. manim/mobject/types/vectorized_mobject.py +387 -214
  63. manim/mobject/value_tracker.py +2 -1
  64. manim/mobject/vector_field.py +2 -4
  65. manim/opengl/__init__.py +3 -3
  66. manim/plugins/__init__.py +2 -3
  67. manim/plugins/plugins_flags.py +3 -3
  68. manim/renderer/cairo_renderer.py +11 -11
  69. manim/renderer/opengl_renderer.py +19 -20
  70. manim/renderer/shader.py +2 -3
  71. manim/renderer/shader_wrapper.py +3 -2
  72. manim/scene/moving_camera_scene.py +23 -0
  73. manim/scene/scene.py +72 -41
  74. manim/scene/scene_file_writer.py +313 -164
  75. manim/scene/section.py +15 -15
  76. manim/scene/three_d_scene.py +8 -15
  77. manim/scene/vector_space_scene.py +3 -6
  78. manim/typing.py +326 -66
  79. manim/utils/bezier.py +1658 -381
  80. manim/utils/caching.py +11 -5
  81. manim/utils/color/AS2700.py +2 -0
  82. manim/utils/color/BS381.py +2 -0
  83. manim/utils/color/DVIPSNAMES.py +96 -0
  84. manim/utils/color/SVGNAMES.py +179 -0
  85. manim/utils/color/X11.py +3 -0
  86. manim/utils/color/XKCD.py +2 -0
  87. manim/utils/color/__init__.py +8 -5
  88. manim/utils/color/core.py +818 -301
  89. manim/utils/color/manim_colors.py +7 -9
  90. manim/utils/commands.py +40 -19
  91. manim/utils/config_ops.py +18 -13
  92. manim/utils/debug.py +8 -6
  93. manim/utils/deprecation.py +92 -43
  94. manim/utils/docbuild/autoaliasattr_directive.py +45 -8
  95. manim/utils/docbuild/autocolor_directive.py +12 -13
  96. manim/utils/docbuild/manim_directive.py +35 -29
  97. manim/utils/docbuild/module_parsing.py +74 -27
  98. manim/utils/family.py +3 -3
  99. manim/utils/family_ops.py +12 -4
  100. manim/utils/file_ops.py +22 -16
  101. manim/utils/hashing.py +7 -7
  102. manim/utils/images.py +10 -4
  103. manim/utils/ipython_magic.py +12 -8
  104. manim/utils/iterables.py +161 -119
  105. manim/utils/module_ops.py +55 -19
  106. manim/utils/opengl.py +68 -23
  107. manim/utils/parameter_parsing.py +3 -2
  108. manim/utils/paths.py +11 -5
  109. manim/utils/polylabel.py +168 -0
  110. manim/utils/qhull.py +218 -0
  111. manim/utils/rate_functions.py +69 -32
  112. manim/utils/simple_functions.py +24 -15
  113. manim/utils/sounds.py +7 -1
  114. manim/utils/space_ops.py +48 -37
  115. manim/utils/testing/_frames_testers.py +13 -8
  116. manim/utils/testing/_show_diff.py +5 -3
  117. manim/utils/testing/_test_class_makers.py +33 -18
  118. manim/utils/testing/frames_comparison.py +20 -14
  119. manim/utils/tex.py +4 -2
  120. manim/utils/tex_file_writing.py +45 -45
  121. manim/utils/tex_templates.py +1 -1
  122. manim/utils/unit.py +6 -5
  123. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
  124. manim-0.19.0.dist-info/RECORD +221 -0
  125. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  126. manim-0.18.1.dist-info/RECORD +0 -217
  127. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  128. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
  129. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -13,7 +13,8 @@ __all__ = [
13
13
 
14
14
  import fractions as fr
15
15
  import numbers
16
- from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence, TypeVar, overload
16
+ from collections.abc import Iterable, Sequence
17
+ from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
17
18
 
18
19
  import numpy as np
19
20
  from typing_extensions import Self
@@ -26,6 +27,7 @@ from manim.mobject.geometry.polygram import Polygon, Rectangle, RegularPolygon
26
27
  from manim.mobject.graphing.functions import ImplicitFunction, ParametricFunction
27
28
  from manim.mobject.graphing.number_line import NumberLine
28
29
  from manim.mobject.graphing.scale import LinearBase
30
+ from manim.mobject.mobject import Mobject
29
31
  from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
30
32
  from manim.mobject.opengl.opengl_surface import OpenGLSurface
31
33
  from manim.mobject.text.tex_mobject import MathTex
@@ -46,6 +48,7 @@ from manim.utils.color import (
46
48
  ManimColor,
47
49
  ParsableManimColor,
48
50
  color_gradient,
51
+ interpolate_color,
49
52
  invert_color,
50
53
  )
51
54
  from manim.utils.config_ops import merge_dicts_recursively, update_dict_recursively
@@ -54,7 +57,14 @@ from manim.utils.space_ops import angle_of_vector
54
57
 
55
58
  if TYPE_CHECKING:
56
59
  from manim.mobject.mobject import Mobject
57
- from manim.typing import ManimFloat, Point2D, Point3D, Vector3D
60
+ from manim.typing import (
61
+ ManimFloat,
62
+ Point2D,
63
+ Point2DLike,
64
+ Point3D,
65
+ Point3DLike,
66
+ Vector3D,
67
+ )
58
68
 
59
69
  LineType = TypeVar("LineType", bound=Line)
60
70
 
@@ -95,10 +105,10 @@ class CoordinateSystem:
95
105
  )
96
106
 
97
107
  # Extra lines and labels for point (1,1)
98
- graphs += grid.get_horizontal_line(grid.c2p(1, 1, 0), color=BLUE)
99
- graphs += grid.get_vertical_line(grid.c2p(1, 1, 0), color=BLUE)
100
- graphs += Dot(point=grid.c2p(1, 1, 0), color=YELLOW)
101
- graphs += Tex("(1,1)").scale(0.75).next_to(grid.c2p(1, 1, 0))
108
+ graphs += grid.get_horizontal_line(grid @ (1, 1, 0), color=BLUE)
109
+ graphs += grid.get_vertical_line(grid @ (1, 1, 0), color=BLUE)
110
+ graphs += Dot(point=grid @ (1, 1, 0), color=YELLOW)
111
+ graphs += Tex("(1,1)").scale(0.75).next_to(grid @ (1, 1, 0))
102
112
  title = Title(
103
113
  # spaces between braces to prevent SyntaxError
104
114
  r"Graphs of $y=x^{ {1}\over{n} }$ and $y=x^n (n=1,2,3,...,20)$",
@@ -144,10 +154,10 @@ class CoordinateSystem:
144
154
  self.y_length = y_length
145
155
  self.num_sampled_graph_points_per_tick = 10
146
156
 
147
- def coords_to_point(self, *coords: Sequence[ManimFloat]):
157
+ def coords_to_point(self, *coords: ManimFloat):
148
158
  raise NotImplementedError()
149
159
 
150
- def point_to_coords(self, point: Point3D):
160
+ def point_to_coords(self, point: Point3DLike):
151
161
  raise NotImplementedError()
152
162
 
153
163
  def polar_to_point(self, radius: float, azimuth: float) -> Point2D:
@@ -181,7 +191,7 @@ class CoordinateSystem:
181
191
  """
182
192
  return self.coords_to_point(radius * np.cos(azimuth), radius * np.sin(azimuth))
183
193
 
184
- def point_to_polar(self, point: np.ndarray) -> Point2D:
194
+ def point_to_polar(self, point: Point2DLike) -> Point2D:
185
195
  r"""Gets polar coordinates from a point.
186
196
 
187
197
  Parameters
@@ -203,7 +213,7 @@ class CoordinateSystem:
203
213
  """Abbreviation for :meth:`coords_to_point`"""
204
214
  return self.coords_to_point(*coords)
205
215
 
206
- def p2c(self, point: Point3D):
216
+ def p2c(self, point: Point3DLike):
207
217
  """Abbreviation for :meth:`point_to_coords`"""
208
218
  return self.point_to_coords(point)
209
219
 
@@ -331,7 +341,6 @@ class CoordinateSystem:
331
341
  )
332
342
  self.add(ax, y_label)
333
343
  """
334
-
335
344
  return self._get_axis_label(
336
345
  label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
337
346
  )
@@ -364,7 +373,6 @@ class CoordinateSystem:
364
373
  :class:`~.Mobject`
365
374
  The positioned label along the given axis.
366
375
  """
367
-
368
376
  label = self.x_axis._create_label_tex(label)
369
377
  label.next_to(axis.get_edge_center(edge), direction=direction, buff=buff)
370
378
  label.shift_onto_screen(buff=MED_SMALL_BUFF)
@@ -393,7 +401,9 @@ class CoordinateSystem:
393
401
  ax = ThreeDAxes()
394
402
  x_labels = range(-4, 5)
395
403
  z_labels = range(-4, 4, 2)
396
- ax.add_coordinates(x_labels, None, z_labels) # default y labels, custom x & z labels
404
+ ax.add_coordinates(
405
+ x_labels, None, z_labels
406
+ ) # default y labels, custom x & z labels
397
407
  ax.add_coordinates(x_labels) # only x labels
398
408
 
399
409
  You can also specifically control the position and value of the labels using a dict.
@@ -404,11 +414,18 @@ class CoordinateSystem:
404
414
  x_pos = [x for x in range(1, 8)]
405
415
 
406
416
  # strings are automatically converted into a Tex mobject.
407
- x_vals = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
417
+ x_vals = [
418
+ "Monday",
419
+ "Tuesday",
420
+ "Wednesday",
421
+ "Thursday",
422
+ "Friday",
423
+ "Saturday",
424
+ "Sunday",
425
+ ]
408
426
  x_dict = dict(zip(x_pos, x_vals))
409
427
  ax.add_coordinates(x_dict)
410
428
  """
411
-
412
429
  self.coordinate_labels = VGroup()
413
430
  # if nothing is passed to axes_numbers, produce axes with default labelling
414
431
  if not axes_numbers:
@@ -489,7 +506,6 @@ class CoordinateSystem:
489
506
  :meth:`~.CoordinateSystem.get_vertical_line`
490
507
  :meth:`~.CoordinateSystem.get_horizontal_line`
491
508
  """
492
-
493
509
  line_config = line_config if line_config is not None else {}
494
510
 
495
511
  if color is None:
@@ -559,14 +575,13 @@ class CoordinateSystem:
559
575
  class GetHorizontalLineExample(Scene):
560
576
  def construct(self):
561
577
  ax = Axes().add_coordinates()
562
- point = ax.c2p(-4, 1.5)
578
+ point = ax @ (-4, 1.5)
563
579
 
564
580
  dot = Dot(point)
565
581
  line = ax.get_horizontal_line(point, line_func=Line)
566
582
 
567
583
  self.add(ax, line, dot)
568
584
  """
569
-
570
585
  return self.get_line_from_axis_to_point(1, point, **kwargs)
571
586
 
572
587
  def get_lines_to_point(self, point: Sequence[float], **kwargs) -> VGroup:
@@ -603,7 +618,6 @@ class CoordinateSystem:
603
618
  lines_2 = ax.get_lines_to_point(circ.get_corner(DL), color=BLUE_B)
604
619
  self.add(ax, lines_1, lines_2, circ)
605
620
  """
606
-
607
621
  return VGroup(
608
622
  self.get_horizontal_line(point, **kwargs),
609
623
  self.get_vertical_line(point, **kwargs),
@@ -616,6 +630,8 @@ class CoordinateSystem:
616
630
  function: Callable[[float], float],
617
631
  x_range: Sequence[float] | None = None,
618
632
  use_vectorized: bool = False,
633
+ colorscale: Union[Iterable[Color], Iterable[Color, float]] | None = None,
634
+ colorscale_axis: int = 1,
619
635
  **kwargs: Any,
620
636
  ) -> ParametricFunction:
621
637
  """Generates a curve based on a function.
@@ -629,6 +645,12 @@ class CoordinateSystem:
629
645
  use_vectorized
630
646
  Whether to pass in the generated t value array to the function. Only use this if your function supports it.
631
647
  Output should be a numpy array of shape ``[y_0, y_1, ...]``
648
+ colorscale
649
+ Colors of the function. Optional parameter used when coloring a function by values. Passing a list of colors
650
+ and a colorscale_axis will color the function by y-value. Passing a list of tuples in the form ``(color, pivot)``
651
+ allows user-defined pivots where the color transitions.
652
+ colorscale_axis
653
+ Defines the axis on which the colorscale is applied (0 = x, 1 = y), default is y-axis (1).
632
654
  kwargs
633
655
  Additional parameters to be passed to :class:`~.ParametricFunction`.
634
656
 
@@ -688,7 +710,6 @@ class CoordinateSystem:
688
710
 
689
711
  self.add(axes, curves)
690
712
  """
691
-
692
713
  t_range = np.array(self.x_range, dtype=float)
693
714
  if x_range is not None:
694
715
  t_range[: len(x_range)] = x_range
@@ -707,7 +728,57 @@ class CoordinateSystem:
707
728
  use_vectorized=use_vectorized,
708
729
  **kwargs,
709
730
  )
731
+
710
732
  graph.underlying_function = function
733
+
734
+ if colorscale:
735
+ if type(colorscale[0]) in (list, tuple):
736
+ new_colors, pivots = [
737
+ [i for i, j in colorscale],
738
+ [j for i, j in colorscale],
739
+ ]
740
+ else:
741
+ new_colors = colorscale
742
+
743
+ ranges = [self.x_range, self.y_range]
744
+ pivot_min = ranges[colorscale_axis][0]
745
+ pivot_max = ranges[colorscale_axis][1]
746
+ pivot_frequency = (pivot_max - pivot_min) / (len(new_colors) - 1)
747
+ pivots = np.arange(
748
+ start=pivot_min,
749
+ stop=pivot_max + pivot_frequency,
750
+ step=pivot_frequency,
751
+ )
752
+
753
+ resolution = 0.01 if len(x_range) == 2 else x_range[2]
754
+ sample_points = np.arange(x_range[0], x_range[1] + resolution, resolution)
755
+ color_list = []
756
+ for samp_x in sample_points:
757
+ axis_value = (samp_x, function(samp_x))[colorscale_axis]
758
+ if axis_value <= pivots[0]:
759
+ color_list.append(new_colors[0])
760
+ elif axis_value >= pivots[-1]:
761
+ color_list.append(new_colors[-1])
762
+ else:
763
+ for i, pivot in enumerate(pivots):
764
+ if pivot > axis_value:
765
+ color_index = (axis_value - pivots[i - 1]) / (
766
+ pivots[i] - pivots[i - 1]
767
+ )
768
+ color_index = min(color_index, 1)
769
+ mob_color = interpolate_color(
770
+ new_colors[i - 1],
771
+ new_colors[i],
772
+ color_index,
773
+ )
774
+ color_list.append(mob_color)
775
+ break
776
+ if config.renderer == RendererType.OPENGL:
777
+ graph.set_color(color_list)
778
+ else:
779
+ graph.set_stroke(color_list)
780
+ graph.set_sheen_direction(RIGHT)
781
+
711
782
  return graph
712
783
 
713
784
  def plot_implicit_curve(
@@ -939,7 +1010,7 @@ class CoordinateSystem:
939
1010
  self,
940
1011
  x: float,
941
1012
  graph: ParametricFunction | VMobject,
942
- ) -> np.ndarray:
1013
+ ) -> Point3D:
943
1014
  """Returns the coordinates of the point on a ``graph`` corresponding to an ``x`` value.
944
1015
 
945
1016
  Parameters
@@ -975,7 +1046,6 @@ class CoordinateSystem:
975
1046
 
976
1047
  self.add(ax, curve, sq)
977
1048
  """
978
-
979
1049
  if hasattr(graph, "underlying_function"):
980
1050
  return graph.function(x)
981
1051
  else:
@@ -1031,7 +1101,7 @@ class CoordinateSystem:
1031
1101
  dot: bool = False,
1032
1102
  dot_config: dict[str, Any] | None = None,
1033
1103
  ) -> Mobject:
1034
- """Creates a properly positioned label for the passed graph, with an optional dot.
1104
+ r"""Creates a properly positioned label for the passed graph, with an optional dot.
1035
1105
 
1036
1106
  Parameters
1037
1107
  ----------
@@ -1068,7 +1138,7 @@ class CoordinateSystem:
1068
1138
  sin = ax.plot(lambda x: np.sin(x), color=PURPLE_B)
1069
1139
  label = ax.get_graph_label(
1070
1140
  graph=sin,
1071
- label= MathTex(r"\\frac{\\pi}{2}"),
1141
+ label= MathTex(r"\frac{\pi}{2}"),
1072
1142
  x_val=PI / 2,
1073
1143
  dot=True,
1074
1144
  direction=UR,
@@ -1076,7 +1146,6 @@ class CoordinateSystem:
1076
1146
 
1077
1147
  self.add(ax, sin, label)
1078
1148
  """
1079
-
1080
1149
  if dot_config is None:
1081
1150
  dot_config = {}
1082
1151
  if color is None:
@@ -1196,7 +1265,6 @@ class CoordinateSystem:
1196
1265
  ax, bounding_line, quadratic, rects_right, rects_left, bounded_rects
1197
1266
  )
1198
1267
  """
1199
-
1200
1268
  # setting up x_range, overwrite user's third input
1201
1269
  if x_range is None:
1202
1270
  if bounded_graph is None:
@@ -1386,7 +1454,6 @@ class CoordinateSystem:
1386
1454
  ax.angle_of_tangent(x=3, graph=curve)
1387
1455
  # 1.4056476493802699
1388
1456
  """
1389
-
1390
1457
  p0 = np.array([*self.input_to_graph_coords(x, graph)])
1391
1458
  p1 = np.array([*self.input_to_graph_coords(x + dx, graph)])
1392
1459
  return angle_of_vector(p1 - p0)
@@ -1418,7 +1485,6 @@ class CoordinateSystem:
1418
1485
  ax.slope_of_tangent(x=-2, graph=curve)
1419
1486
  # -3.5000000259052038
1420
1487
  """
1421
-
1422
1488
  return np.tan(self.angle_of_tangent(x, graph, **kwargs))
1423
1489
 
1424
1490
  def plot_derivative_graph(
@@ -1694,7 +1760,6 @@ class CoordinateSystem:
1694
1760
 
1695
1761
  self.add(ax, curve, lines)
1696
1762
  """
1697
-
1698
1763
  x_range = x_range if x_range is not None else self.x_range
1699
1764
 
1700
1765
  return VGroup(
@@ -1756,7 +1821,6 @@ class CoordinateSystem:
1756
1821
  t_label = axes.get_T_label(x_val=4, graph=func, label=Tex("x-value"))
1757
1822
  self.add(axes, func, t_label)
1758
1823
  """
1759
-
1760
1824
  T_label_group = VGroup()
1761
1825
  triangle = RegularPolygon(n=3, start_angle=np.pi / 2, stroke_width=0).set_fill(
1762
1826
  color=triangle_color,
@@ -1779,6 +1843,14 @@ class CoordinateSystem:
1779
1843
 
1780
1844
  return T_label_group
1781
1845
 
1846
+ def __matmul__(self, coord: Point3DLike | Mobject):
1847
+ if isinstance(coord, Mobject):
1848
+ coord = coord.get_center()
1849
+ return self.coords_to_point(*coord)
1850
+
1851
+ def __rmatmul__(self, point: Point3DLike):
1852
+ return self.point_to_coords(point)
1853
+
1782
1854
 
1783
1855
  class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1784
1856
  """Creates a set of axes.
@@ -1940,7 +2012,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1940
2012
  )
1941
2013
  )
1942
2014
  """
1943
-
1944
2015
  for default_config, passed_config in zip(default_configs, passed_configs):
1945
2016
  if passed_config is not None:
1946
2017
  update_dict_recursively(default_config, passed_config)
@@ -1979,6 +2050,7 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1979
2050
  self, *coords: float | Sequence[float] | Sequence[Sequence[float]] | np.ndarray
1980
2051
  ) -> np.ndarray:
1981
2052
  """Accepts coordinates from the axes and returns a point with respect to the scene.
2053
+ Equivalent to `ax @ (coord1)`
1982
2054
 
1983
2055
  Parameters
1984
2056
  ----------
@@ -2007,6 +2079,8 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
2007
2079
  >>> ax = Axes()
2008
2080
  >>> np.around(ax.coords_to_point(1, 0, 0), 2)
2009
2081
  array([0.86, 0. , 0. ])
2082
+ >>> np.around(ax @ (1, 0, 0), 2)
2083
+ array([0.86, 0. , 0. ])
2010
2084
  >>> np.around(ax.coords_to_point([[0, 1], [1, 1], [1, 0]]), 2)
2011
2085
  array([[0. , 0.75, 0. ],
2012
2086
  [0.86, 0.75, 0. ],
@@ -2182,7 +2256,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
2182
2256
  )
2183
2257
  self.add(ax, labels)
2184
2258
  """
2185
-
2186
2259
  self.axis_labels = VGroup(
2187
2260
  self.get_x_axis_label(x_label),
2188
2261
  self.get_y_axis_label(y_label),
@@ -2469,7 +2542,6 @@ class ThreeDAxes(Axes):
2469
2542
  self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
2470
2543
  self.add(ax, lab)
2471
2544
  """
2472
-
2473
2545
  positioned_label = self._get_axis_label(
2474
2546
  label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
2475
2547
  )
@@ -2520,7 +2592,6 @@ class ThreeDAxes(Axes):
2520
2592
  self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
2521
2593
  self.add(ax, lab)
2522
2594
  """
2523
-
2524
2595
  positioned_label = self._get_axis_label(
2525
2596
  label, self.get_z_axis(), edge, direction, buff=buff, **kwargs
2526
2597
  )
@@ -2574,7 +2645,6 @@ class ThreeDAxes(Axes):
2574
2645
  )
2575
2646
  self.add(axes, labels)
2576
2647
  """
2577
-
2578
2648
  self.axis_labels = VGroup(
2579
2649
  self.get_x_axis_label(x_label),
2580
2650
  self.get_y_axis_label(y_label),
@@ -2798,7 +2868,6 @@ class NumberPlane(Axes):
2798
2868
  The first (i.e the non-faded lines parallel to `axis_parallel_to`) and second
2799
2869
  (i.e the faded lines parallel to `axis_parallel_to`) sets of lines, respectively.
2800
2870
  """
2801
-
2802
2871
  line = Line(axis_parallel_to.get_start(), axis_parallel_to.get_end())
2803
2872
  if ratio_faded_lines == 0: # don't show faded lines
2804
2873
  ratio_faded_lines = 1 # i.e. set ratio to 1
@@ -3166,7 +3235,7 @@ class PolarPlane(Axes):
3166
3235
  elif self.azimuth_units == "degrees":
3167
3236
  a_tex = [
3168
3237
  MathTex(
3169
- f'{360 * i["label"]:g}' + r"^{\circ}",
3238
+ f"{360 * i['label']:g}" + r"^{\circ}",
3170
3239
  font_size=self.azimuth_label_font_size,
3171
3240
  ).next_to(
3172
3241
  i["point"],
@@ -3179,7 +3248,7 @@ class PolarPlane(Axes):
3179
3248
  elif self.azimuth_units == "gradians":
3180
3249
  a_tex = [
3181
3250
  MathTex(
3182
- f'{400 * i["label"]:g}' + r"^{g}",
3251
+ f"{400 * i['label']:g}" + r"^{g}",
3183
3252
  font_size=self.azimuth_label_font_size,
3184
3253
  ).next_to(
3185
3254
  i["point"],
@@ -3192,7 +3261,7 @@ class PolarPlane(Axes):
3192
3261
  elif self.azimuth_units is None:
3193
3262
  a_tex = [
3194
3263
  MathTex(
3195
- f'{i["label"]:g}',
3264
+ f"{i['label']:g}",
3196
3265
  font_size=self.azimuth_label_font_size,
3197
3266
  ).next_to(
3198
3267
  i["point"],
@@ -3310,7 +3379,6 @@ class ComplexPlane(NumberPlane):
3310
3379
  np.ndarray
3311
3380
  The point on the plane.
3312
3381
  """
3313
-
3314
3382
  number = complex(number)
3315
3383
  return self.coords_to_point(number.real, number.imag)
3316
3384
 
@@ -3318,7 +3386,7 @@ class ComplexPlane(NumberPlane):
3318
3386
  """Abbreviation for :meth:`number_to_point`."""
3319
3387
  return self.number_to_point(number)
3320
3388
 
3321
- def point_to_number(self, point: Point3D) -> complex:
3389
+ def point_to_number(self, point: Point3DLike) -> complex:
3322
3390
  """Accepts a point and returns a complex number equivalent to that point on the plane.
3323
3391
 
3324
3392
  Parameters
@@ -3331,11 +3399,10 @@ class ComplexPlane(NumberPlane):
3331
3399
  complex
3332
3400
  A complex number consisting of real and imaginary components.
3333
3401
  """
3334
-
3335
3402
  x, y = self.point_to_coords(point)
3336
3403
  return complex(x, y)
3337
3404
 
3338
- def p2n(self, point: Point3D) -> complex:
3405
+ def p2n(self, point: Point3DLike) -> complex:
3339
3406
  """Abbreviation for :meth:`point_to_number`."""
3340
3407
  return self.point_to_number(point)
3341
3408
 
@@ -3369,7 +3436,6 @@ class ComplexPlane(NumberPlane):
3369
3436
  :class:`~.VGroup`
3370
3437
  A :class:`~.VGroup` containing the positioned label mobjects.
3371
3438
  """
3372
-
3373
3439
  # TODO: Make this work the same as coord_sys.add_coordinates()
3374
3440
  if len(numbers) == 0:
3375
3441
  numbers = self._get_default_coordinate_values()
@@ -3400,6 +3466,5 @@ class ComplexPlane(NumberPlane):
3400
3466
  kwargs
3401
3467
  Additional arguments to be passed to :meth:`~.NumberLine.get_number_mobject`, i.e. :class:`~.DecimalNumber`.
3402
3468
  """
3403
-
3404
3469
  self.add(self.get_coordinate_labels(*numbers, **kwargs))
3405
3470
  return self
@@ -5,7 +5,8 @@ from __future__ import annotations
5
5
  __all__ = ["ParametricFunction", "FunctionGraph", "ImplicitFunction"]
6
6
 
7
7
 
8
- from typing import TYPE_CHECKING, Callable, Iterable, Sequence
8
+ from collections.abc import Iterable, Sequence
9
+ from typing import TYPE_CHECKING, Callable
9
10
 
10
11
  import numpy as np
11
12
  from isosurfaces import plot_isoline
@@ -16,7 +17,9 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
16
17
  from manim.mobject.types.vectorized_mobject import VMobject
17
18
 
18
19
  if TYPE_CHECKING:
19
- from manim.typing import Point2D, Point3D
20
+ from typing_extensions import Self
21
+
22
+ from manim.typing import Point3D, Point3DLike
20
23
 
21
24
  from manim.utils.color import YELLOW
22
25
 
@@ -101,8 +104,8 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
101
104
 
102
105
  def __init__(
103
106
  self,
104
- function: Callable[[float], Point3D],
105
- t_range: Point2D | Point3D = (0, 1),
107
+ function: Callable[[float], Point3DLike],
108
+ t_range: tuple[float, float] | tuple[float, float, float] = (0, 1),
106
109
  scaling: _ScaleBase = LinearBase(),
107
110
  dt: float = 1e-8,
108
111
  discontinuities: Iterable[float] | None = None,
@@ -110,10 +113,13 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
110
113
  use_vectorized: bool = False,
111
114
  **kwargs,
112
115
  ):
113
- self.function = function
114
- t_range = (0, 1, 0.01) if t_range is None else t_range
116
+ def internal_parametric_function(t: float) -> Point3D:
117
+ """Wrap ``function``'s output inside a NumPy array."""
118
+ return np.asarray(function(t))
119
+
120
+ self.function = internal_parametric_function
115
121
  if len(t_range) == 2:
116
- t_range = np.array([*t_range, 0.01])
122
+ t_range = (*t_range, 0.01)
117
123
 
118
124
  self.scaling = scaling
119
125
 
@@ -125,13 +131,13 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
125
131
 
126
132
  super().__init__(**kwargs)
127
133
 
128
- def get_function(self):
134
+ def get_function(self) -> Callable[[float], Point3D]:
129
135
  return self.function
130
136
 
131
- def get_point_from_function(self, t):
137
+ def get_point_from_function(self, t: float) -> Point3D:
132
138
  return self.function(t)
133
139
 
134
- def generate_points(self):
140
+ def generate_points(self) -> Self:
135
141
  if self.discontinuities is not None:
136
142
  discontinuities = filter(
137
143
  lambda t: self.t_min <= t <= self.t_max,
@@ -2,15 +2,18 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from manim.mobject.mobject import Mobject
5
6
  from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
6
7
 
7
8
  __all__ = ["NumberLine", "UnitInterval"]
8
9
 
9
10
 
10
- from typing import TYPE_CHECKING, Callable, Iterable, Sequence
11
+ from collections.abc import Iterable, Sequence
12
+ from typing import TYPE_CHECKING, Callable
11
13
 
12
14
  if TYPE_CHECKING:
13
15
  from manim.mobject.geometry.tips import ArrowTip
16
+ from manim.typing import Point3DLike
14
17
 
15
18
  import numpy as np
16
19
 
@@ -274,7 +277,8 @@ class NumberLine(Line):
274
277
 
275
278
  def add_ticks(self):
276
279
  """Adds ticks to the number line. Ticks can be accessed after creation
277
- via ``self.ticks``."""
280
+ via ``self.ticks``.
281
+ """
278
282
  ticks = VGroup()
279
283
  elongated_tick_size = self.tick_size * self.longer_tick_multiple
280
284
  elongated_tick_offsets = self.numbers_with_elongated_ticks - self.x_min
@@ -343,6 +347,7 @@ class NumberLine(Line):
343
347
  def number_to_point(self, number: float | np.ndarray) -> np.ndarray:
344
348
  """Accepts a value along the number line and returns a point with
345
349
  respect to the scene.
350
+ Equivalent to `NumberLine @ number`
346
351
 
347
352
  Parameters
348
353
  ----------
@@ -363,7 +368,9 @@ class NumberLine(Line):
363
368
  array([0., 0., 0.])
364
369
  >>> number_line.number_to_point(1)
365
370
  array([1., 0., 0.])
366
- >>> number_line.number_to_point([1,2,3])
371
+ >>> number_line @ 1
372
+ array([1., 0., 0.])
373
+ >>> number_line.number_to_point([1, 2, 3])
367
374
  array([[1., 0., 0.],
368
375
  [2., 0., 0.],
369
376
  [3., 0., 0.]])
@@ -395,11 +402,11 @@ class NumberLine(Line):
395
402
 
396
403
  >>> from manim import NumberLine
397
404
  >>> number_line = NumberLine()
398
- >>> number_line.point_to_number((0,0,0))
399
- 0.0
400
- >>> number_line.point_to_number((1,0,0))
401
- 1.0
402
- >>> number_line.point_to_number([[0.5,0,0],[1,0,0],[1.5,0,0]])
405
+ >>> number_line.point_to_number((0, 0, 0))
406
+ np.float64(0.0)
407
+ >>> number_line.point_to_number((1, 0, 0))
408
+ np.float64(1.0)
409
+ >>> number_line.point_to_number([[0.5, 0, 0], [1, 0, 0], [1.5, 0, 0]])
403
410
  array([0.5, 1. , 1.5])
404
411
 
405
412
  """
@@ -574,7 +581,6 @@ class NumberLine(Line):
574
581
  AttributeError
575
582
  If the label does not have a ``font_size`` attribute, an ``AttributeError`` is raised.
576
583
  """
577
-
578
584
  direction = self.label_direction if direction is None else direction
579
585
  buff = self.line_to_number_buff if buff is None else buff
580
586
  font_size = self.font_size if font_size is None else font_size
@@ -641,6 +647,14 @@ class NumberLine(Line):
641
647
  return 0
642
648
  return len(step.split(".")[-1])
643
649
 
650
+ def __matmul__(self, other: float):
651
+ return self.n2p(other)
652
+
653
+ def __rmatmul__(self, other: Point3DLike | Mobject):
654
+ if isinstance(other, Mobject):
655
+ other = other.get_center()
656
+ return self.p2n(other)
657
+
644
658
 
645
659
  class UnitInterval(NumberLine):
646
660
  def __init__(
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  __all__ = ["SampleSpace", "BarChart"]
6
6
 
7
7
 
8
- from typing import Iterable, MutableSequence, Sequence
8
+ from collections.abc import Iterable, MutableSequence, Sequence
9
9
 
10
10
  import numpy as np
11
11
 
@@ -319,7 +319,6 @@ class BarChart(Axes):
319
319
  Primarily used when the bars are initialized with ``self._add_bars``
320
320
  or updated via ``self.change_bar_values``.
321
321
  """
322
-
323
322
  self.bars.set_color_by_gradient(*self.bar_colors)
324
323
 
325
324
  def _add_x_axis_labels(self):
@@ -329,7 +328,6 @@ class BarChart(Axes):
329
328
 
330
329
  UP for negative values and DOWN for positive values.
331
330
  """
332
-
333
331
  val_range = np.arange(
334
332
  0.5, len(self.bar_names), 1
335
333
  ) # 0.5 shifted so that labels are centered, not on ticks
@@ -339,10 +337,7 @@ class BarChart(Axes):
339
337
  for i, (value, bar_name) in enumerate(zip(val_range, self.bar_names)):
340
338
  # to accommodate negative bars, the label may need to be
341
339
  # below or above the x_axis depending on the value of the bar
342
- if self.values[i] < 0:
343
- direction = UP
344
- else:
345
- direction = DOWN
340
+ direction = UP if self.values[i] < 0 else DOWN
346
341
  bar_name_label = self.x_axis.label_constructor(bar_name)
347
342
 
348
343
  bar_name_label.font_size = self.x_axis.font_size
@@ -372,7 +367,6 @@ class BarChart(Axes):
372
367
  Rectangle
373
368
  A positioned rectangle representing a bar on the chart.
374
369
  """
375
-
376
370
  # bar measurements relative to the axis
377
371
 
378
372
  # distance from between the y-axis and the top of the bar
@@ -435,7 +429,6 @@ class BarChart(Axes):
435
429
 
436
430
  self.add(chart, c_bar_lbls)
437
431
  """
438
-
439
432
  bar_labels = VGroup()
440
433
  for bar, value in zip(self.bars, self.values):
441
434
  bar_lbl = label_constructor(str(value))
@@ -483,7 +476,6 @@ class BarChart(Axes):
483
476
  chart.change_bar_values(list(reversed(values)))
484
477
  self.add(chart.get_bar_labels(font_size=24))
485
478
  """
486
-
487
479
  for i, (bar, value) in enumerate(zip(self.bars, values)):
488
480
  chart_val = self.values[i]
489
481